Skip to content

Commit 872864b

Browse files
committed
[MERGE #1707 @MikeHolman] move dynamic interpreter thunk generation OOP
Merge pull request #1707 from MikeHolman:oopite
2 parents 3c82d73 + 5e5099f commit 872864b

16 files changed

+605
-109
lines changed

lib/Backend/InterpreterThunkEmitter.cpp

Lines changed: 192 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -228,15 +228,26 @@ const BYTE InterpreterThunkEmitter::HeaderSize = sizeof(InterpreterThunk);
228228
const BYTE InterpreterThunkEmitter::ThunkSize = sizeof(Call);
229229
const 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

288299
void 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

Comments
 (0)