@@ -228,15 +228,26 @@ const BYTE InterpreterThunkEmitter::HeaderSize = sizeof(InterpreterThunk);
228228const BYTE InterpreterThunkEmitter::ThunkSize = sizeof (Call);
229229const uint InterpreterThunkEmitter::ThunksPerBlock = (BlockSize - HeaderSize) / ThunkSize;
230230
231- InterpreterThunkEmitter::InterpreterThunkEmitter (ArenaAllocator* allocator, CustomHeap::CodePageAllocators * codePageAllocators, bool isAsmInterpreterThunk) :
232- // TODO: michhol oop JIT move interpreter thunk emitter out of process
233- emitBufferManager(allocator, codePageAllocators, /* scriptContext*/ nullptr , _u(" Interpreter thunk buffer" ), GetCurrentProcess()),
234- allocation(nullptr ),
231+ InterpreterThunkEmitter::InterpreterThunkEmitter (Js::ScriptContext* context, ArenaAllocator* allocator, CustomHeap::CodePageAllocators * codePageAllocators, bool isAsmInterpreterThunk) :
232+ emitBufferManager(nullptr ),
233+ scriptContext(context),
235234 allocator(allocator),
236235 thunkCount(0 ),
237236 thunkBuffer(nullptr ),
238237 isAsmInterpreterThunk(isAsmInterpreterThunk)
239238{
239+ if (!JITManager::GetJITManager ()->IsOOPJITEnabled ())
240+ {
241+ emitBufferManager = HeapNew (EmitBufferManager<>, allocator, codePageAllocators, /* scriptContext*/ nullptr , _u (" Interpreter thunk buffer" ), GetCurrentProcess ());
242+ }
243+ }
244+
245+ InterpreterThunkEmitter::~InterpreterThunkEmitter ()
246+ {
247+ if (emitBufferManager != nullptr )
248+ {
249+ HeapDelete (emitBufferManager);
250+ }
240251}
241252
242253//
@@ -287,64 +298,154 @@ void* InterpreterThunkEmitter::ConvertToEntryPoint(PVOID dynamicInterpreterThunk
287298
288299void InterpreterThunkEmitter::NewThunkBlock ()
289300{
290- Assert (this ->thunkCount == 0 );
291- BYTE* buffer;
292- BYTE* currentBuffer;
293- DWORD bufferSize = BlockSize;
294- DWORD thunkCount = 0 ;
295-
296- void * interpreterThunk = nullptr ;
297-
298- // the static interpreter thunk invoked by the dynamic emitted thunk
299- #ifdef ASMJS_PLAT
300- if (isAsmInterpreterThunk)
301+ #ifdef ENABLE_OOP_NATIVE_CODEGEN
302+ if (JITManager::GetJITManager ()->IsOOPJITEnabled ())
301303 {
302- interpreterThunk = (void *)Js::InterpreterStackFrame::InterpreterAsmThunk;
304+ NewOOPJITThunkBlock ();
305+ return ;
303306 }
304- else
305307#endif
306- {
307- interpreterThunk = (void *)Js::InterpreterStackFrame::InterpreterThunk;
308- }
309308
310- allocation = emitBufferManager.AllocateBuffer (bufferSize, &buffer);
309+ Assert (this ->thunkCount == 0 );
310+ BYTE* buffer;
311+
312+ EmitBufferAllocation * allocation = emitBufferManager->AllocateBuffer (BlockSize, &buffer);
311313 if (allocation == nullptr )
312314 {
313315 Js::Throw::OutOfMemory ();
314316 }
315- if (!emitBufferManager.ProtectBufferWithExecuteReadWriteForInterpreter (allocation))
317+ if (!emitBufferManager->ProtectBufferWithExecuteReadWriteForInterpreter (allocation))
318+ {
319+ Js::Throw::OutOfMemory ();
320+ }
321+
322+ #if PDATA_ENABLED
323+ PRUNTIME_FUNCTION pdataStart;
324+ intptr_t epilogEnd;
325+ #endif
326+
327+ FillBuffer (
328+ this ->allocator ,
329+ this ->scriptContext ->GetThreadContext (),
330+ this ->isAsmInterpreterThunk ,
331+ (intptr_t )buffer,
332+ BlockSize,
333+ buffer,
334+ #if PDATA_ENABLED
335+ &pdataStart,
336+ &epilogEnd,
337+ #endif
338+ &this ->thunkCount
339+ );
340+
341+ if (!emitBufferManager->CommitReadWriteBufferForInterpreter (allocation, buffer, BlockSize))
316342 {
317343 Js::Throw::OutOfMemory ();
318344 }
319345
320- currentBuffer = buffer;
346+ // Call to set VALID flag for CFG check
347+ ThreadContext::GetContextForCurrentThread ()->SetValidCallTargetForCFG (buffer);
348+
349+ // Update object state only at the end when everything has succeeded - and no exceptions can be thrown.
350+ auto block = this ->thunkBlocks .PrependNode (allocator, buffer);
351+ #if PDATA_ENABLED
352+ void * pdataTable;
353+ PDataManager::RegisterPdata ((PRUNTIME_FUNCTION)pdataStart, (ULONG_PTR)buffer, (ULONG_PTR)epilogEnd, &pdataTable);
354+ block->SetPdata (pdataTable);
355+ #else
356+ Unused (block);
357+ #endif
358+ this ->thunkBuffer = buffer;
359+ }
360+
361+ #ifdef ENABLE_OOP_NATIVE_CODEGEN
362+ void InterpreterThunkEmitter::NewOOPJITThunkBlock ()
363+ {
364+ InterpreterThunkInfoIDL thunkInfo;
365+ HRESULT hr = JITManager::GetJITManager ()->NewInterpreterThunkBlock (
366+ this ->scriptContext ->GetRemoteScriptAddr (),
367+ this ->isAsmInterpreterThunk ,
368+ &thunkInfo
369+ );
370+ JITManager::HandleServerCallResult (hr);
321371
372+ this ->thunkBuffer = (BYTE*)thunkInfo.thunkBlockAddr ;
373+
374+ // Update object state only at the end when everything has succeeded - and no exceptions can be thrown.
375+ auto block = this ->thunkBlocks .PrependNode (allocator, this ->thunkBuffer );
376+ #if PDATA_ENABLED
377+ void * pdataTable;
378+ PDataManager::RegisterPdata ((PRUNTIME_FUNCTION)thunkInfo.pdataTableStart , (ULONG_PTR)this ->thunkBuffer , (ULONG_PTR)thunkInfo.epilogEndAddr , &pdataTable);
379+ block->SetPdata (pdataTable);
380+ #else
381+ Unused (block);
382+ #endif
383+
384+ this ->thunkCount = thunkInfo.thunkCount ;
385+ }
386+ #endif
387+
388+ /* static */
389+ void InterpreterThunkEmitter::FillBuffer (
390+ _In_ ArenaAllocator * arena,
391+ _In_ ThreadContextInfo * threadContext,
392+ _In_ bool asmJsThunk,
393+ _In_ intptr_t finalAddr,
394+ _In_ size_t bufferSize,
395+ _Out_writes_bytes_all_ (bufferSize) BYTE* buffer,
396+ #if PDATA_ENABLED
397+ _Out_ PRUNTIME_FUNCTION * pdataTableStart,
398+ _Out_ intptr_t * epilogEndAddr,
399+ #endif
400+ _Out_ DWORD * thunkCount
401+ )
402+ {
322403#ifdef _M_X64
323- PrologEncoder prologEncoder (allocator );
404+ PrologEncoder prologEncoder (arena );
324405 prologEncoder.EncodeSmallProlog (PrologSize, StackAllocSize);
325406 DWORD pdataSize = prologEncoder.SizeOfPData ();
326407#elif defined(_M_ARM32_OR_ARM64)
327408 DWORD pdataSize = sizeof (RUNTIME_FUNCTION);
328409#else
329410 DWORD pdataSize = 0 ;
330411#endif
331- DWORD bytesRemaining = bufferSize ;
412+ DWORD bytesRemaining = BlockSize ;
332413 DWORD bytesWritten = 0 ;
333414 DWORD epilogSize = sizeof (Epilog);
415+ DWORD thunks = 0 ;
416+
417+ intptr_t interpreterThunk;
418+
419+ // the static interpreter thunk invoked by the dynamic emitted thunk
420+ #ifdef ASMJS_PLAT
421+ if (asmJsThunk)
422+ {
423+ interpreterThunk = SHIFT_ADDR (threadContext, &Js::InterpreterStackFrame::InterpreterAsmThunk);
424+ }
425+ else
426+ #endif
427+ {
428+ interpreterThunk = SHIFT_ADDR (threadContext, &Js::InterpreterStackFrame::InterpreterThunk);
429+ }
334430
431+ BYTE * currentBuffer = buffer;
335432 // Ensure there is space for PDATA at the end
336- BYTE* pdataStart = currentBuffer + (bufferSize - Math::Align (pdataSize, EMIT_BUFFER_ALIGNMENT));
433+ BYTE* pdataStart = currentBuffer + (BlockSize - Math::Align (pdataSize, EMIT_BUFFER_ALIGNMENT));
337434 BYTE* epilogStart = pdataStart - Math::Align (epilogSize, EMIT_BUFFER_ALIGNMENT);
338435
436+ // Ensure there is space for PDATA at the end
437+ intptr_t finalPdataStart = finalAddr + (BlockSize - Math::Align (pdataSize, EMIT_BUFFER_ALIGNMENT));
438+ intptr_t finalEpilogStart = finalPdataStart - Math::Align (epilogSize, EMIT_BUFFER_ALIGNMENT);
439+
339440 // Copy the thunk buffer and modify it.
340441 js_memcpy_s (currentBuffer, bytesRemaining, InterpreterThunk, HeaderSize);
341- EncodeInterpreterThunk (currentBuffer, buffer , HeaderSize, epilogStart , epilogSize, interpreterThunk);
442+ EncodeInterpreterThunk (currentBuffer, finalAddr , HeaderSize, finalEpilogStart , epilogSize, interpreterThunk);
342443 currentBuffer += HeaderSize;
343444 bytesRemaining -= HeaderSize;
344445
345446 // Copy call buffer
346447 DWORD callSize = sizeof (Call);
347- while (currentBuffer < epilogStart - callSize)
448+ while (currentBuffer < epilogStart - callSize)
348449 {
349450 js_memcpy_s (currentBuffer, bytesRemaining, Call, callSize);
350451#if _M_ARM
@@ -367,7 +468,7 @@ void InterpreterThunkEmitter::NewThunkBlock()
367468#endif
368469 currentBuffer += callSize;
369470 bytesRemaining -= callSize;
370- thunkCount ++;
471+ thunks ++;
371472 }
372473
373474 // Fill any gap till start of epilog
@@ -394,30 +495,20 @@ void InterpreterThunkEmitter::NewThunkBlock()
394495 GeneratePdata (buffer, functionSize, &pdata);
395496 bytesWritten = CopyWithAlignment (pdataStart, bytesRemaining, (const BYTE*)&pdata, pdataSize, EMIT_BUFFER_ALIGNMENT);
396497#endif
397- void * pdataTable ;
398- PDataManager::RegisterPdata ((PRUNTIME_FUNCTION) pdataStart, (ULONG_PTR) buffer, (ULONG_PTR) epilogEnd, &pdataTable) ;
498+ *pdataTableStart = (PRUNTIME_FUNCTION)finalPdataStart ;
499+ *epilogEndAddr = finalEpilogStart ;
399500#endif
400- if (!emitBufferManager.CommitReadWriteBufferForInterpreter (allocation, buffer, bufferSize))
401- {
402- Js::Throw::OutOfMemory ();
403- }
404-
405- // Call to set VALID flag for CFG check
406- ThreadContext::GetContextForCurrentThread ()->SetValidCallTargetForCFG (buffer);
407-
408- // Update object state only at the end when everything has succeeded - and no exceptions can be thrown.
409- ThunkBlock* block = this ->thunkBlocks .PrependNode (allocator, buffer);
410- UNREFERENCED_PARAMETER (block);
411- #if PDATA_ENABLED
412- block->SetPdata (pdataTable);
413- #endif
414- this ->thunkCount = thunkCount;
415- this ->thunkBuffer = buffer;
501+ *thunkCount = thunks;
416502}
417503
418-
419504#if _M_ARM
420- void InterpreterThunkEmitter::EncodeInterpreterThunk (__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize, __in void * const interpreterThunk)
505+ void InterpreterThunkEmitter::EncodeInterpreterThunk (
506+ __in_bcount (thunkSize) BYTE* thunkBuffer,
507+ __in const intptr_t thunkBufferStartAddress,
508+ __in const DWORD thunkSize,
509+ __in const intptr_t epilogStart,
510+ __in const DWORD epilogSize,
511+ __in const intptr_t interpreterThunk)
421512{
422513 _Analysis_assume_ (thunkSize == HeaderSize);
423514 // Encode MOVW
@@ -482,7 +573,13 @@ void InterpreterThunkEmitter::GeneratePdata(_In_ const BYTE* entryPoint, _In_ co
482573}
483574
484575#elif _M_ARM64
485- void InterpreterThunkEmitter::EncodeInterpreterThunk (__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize, __in void * const interpreterThunk)
576+ void InterpreterThunkEmitter::EncodeInterpreterThunk (
577+ __in_bcount (thunkSize) BYTE* thunkBuffer,
578+ __in const intptr_t thunkBufferStartAddress,
579+ __in const DWORD thunkSize,
580+ __in const intptr_t epilogStart,
581+ __in const DWORD epilogSize,
582+ __in const intptr_t interpreterThunk)
486583{
487584 int addrOffset = ThunkAddressOffset;
488585
@@ -556,7 +653,13 @@ void InterpreterThunkEmitter::GeneratePdata(_In_ const BYTE* entryPoint, _In_ co
556653 function->FrameSize = 5 ; // the number of bytes of stack that is allocated for this function divided by 16
557654}
558655#else
559- void InterpreterThunkEmitter::EncodeInterpreterThunk (__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize, __in void * const interpreterThunk)
656+ void InterpreterThunkEmitter::EncodeInterpreterThunk (
657+ __in_bcount (thunkSize) BYTE* thunkBuffer,
658+ __in const intptr_t thunkBufferStartAddress,
659+ __in const DWORD thunkSize,
660+ __in const intptr_t epilogStart,
661+ __in const DWORD epilogSize,
662+ __in const intptr_t interpreterThunk)
560663{
561664 _Analysis_assume_ (thunkSize == HeaderSize);
562665 Emit (thunkBuffer, ThunkAddressOffset, (uintptr_t )interpreterThunk);
@@ -579,7 +682,6 @@ DWORD InterpreterThunkEmitter::FillDebugBreak(_In_ BYTE* dest, _In_ DWORD count)
579682#elif defined(_M_ARM64)
580683 Assert (count % 4 == 0 );
581684#endif
582- // TODO: michhol OOP JIT. after mving OOP, change to runtime process handle
583685 CustomHeap::FillDebugBreak (dest, count, GetCurrentProcess ());
584686 return count;
585687}
@@ -607,6 +709,30 @@ DWORD InterpreterThunkEmitter::CopyWithAlignment(
607709 return srcSize;
608710}
609711
712+ #if DBG
713+ bool
714+ InterpreterThunkEmitter::IsInHeap (void * address)
715+ {
716+ #ifdef ENABLE_OOP_NATIVE_CODEGEN
717+ if (JITManager::GetJITManager ()->IsOOPJITEnabled ())
718+ {
719+ intptr_t remoteScript = this ->scriptContext ->GetRemoteScriptAddr (false );
720+ if (!remoteScript)
721+ {
722+ return false ;
723+ }
724+ boolean result;
725+ JITManager::GetJITManager ()->IsInterpreterThunkAddr (remoteScript, (intptr_t )address, this ->isAsmInterpreterThunk , &result);
726+ return result != FALSE ;
727+ }
728+ else
729+ #endif
730+ {
731+ return emitBufferManager->IsInHeap (address);
732+ }
733+ }
734+ #endif
735+
610736// We only decommit at close because there might still be some
611737// code running here.
612738// The destructor of emitBufferManager will cause the eventual release.
@@ -622,7 +748,20 @@ void InterpreterThunkEmitter::Close()
622748#endif
623749 this ->thunkBlocks .Clear (allocator);
624750 this ->freeListedThunkBlocks .Clear (allocator);
625- emitBufferManager.Decommit ();
751+ #ifdef ENABLE_OOP_NATIVE_CODEGEN
752+ if (JITManager::GetJITManager ()->IsOOPJITEnabled ())
753+ {
754+ intptr_t remoteScript = this ->scriptContext ->GetRemoteScriptAddr (false );
755+ if (remoteScript)
756+ {
757+ JITManager::GetJITManager ()->DecommitInterpreterBufferManager (remoteScript, this ->isAsmInterpreterThunk );
758+ }
759+ }
760+ else
761+ #endif
762+ {
763+ emitBufferManager->Decommit ();
764+ }
626765 this ->thunkBuffer = nullptr ;
627766 this ->thunkCount = 0 ;
628767}
0 commit comments