Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3220,8 +3220,12 @@ private void getAddressOfPInvokeTarget(CORINFO_METHOD_STRUCT_* method, ref CORIN
ModuleToken moduleToken = new ModuleToken(ecmaMethod.Module, ecmaMethod.Handle);
MethodWithToken methodWithToken = new MethodWithToken(ecmaMethod, moduleToken, constrainedType: null, unboxing: false, genericContextObject: null);

if ((ecmaMethod.GetPInvokeMethodCallingConventions() & UnmanagedCallingConventions.IsSuppressGcTransition) != 0)
if (((ecmaMethod.GetPInvokeMethodCallingConventions() & UnmanagedCallingConventions.IsSuppressGcTransition) != 0)
|| _compilation.NodeFactory.Target.IsWasm)
{
// Suppress GC transition P/Invokes are called directly, since we can't do a GC transition at this point.
// On Wasm, we also call directly because the runtime doesn't generate P/Invoke import precodes/stubs; instead,
// errors are reported when we fix up the method.
pLookup.addr = (void*)ObjectToHandle(_compilation.SymbolNodeFactory.GetPInvokeTargetNode(methodWithToken));
pLookup.accessType = InfoAccessType.IAT_PVALUE;
}
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/jithelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1114,7 +1114,7 @@ HRESULT EEToProfInterfaceImpl::SetEnterLeaveFunctionHooksForJit(FunctionEnter3 *
// tailored to the post-pinvoke operations.
extern "C" VOID JIT_PInvokeEndRarePath();

void JIT_PInvokeEndRarePath()
NOINLINE void JIT_PInvokeEndRarePath()
{
PreserveLastErrorHolder preserveLastError;

Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14334,6 +14334,7 @@ BOOL LoadDynamicInfoEntry(Module *currentModule,
}
break;

#ifdef HAS_PINVOKE_IMPORT_PRECODE
case READYTORUN_FIXUP_IndirectPInvokeTarget:
{
MethodDesc *pMethod = ZapSig::DecodeMethod(currentModule, pInfoModule, pBlob);
Expand All @@ -14343,6 +14344,7 @@ BOOL LoadDynamicInfoEntry(Module *currentModule,
result = (size_t)(LPVOID)&(pMD->m_pPInvokeTarget);
}
break;
#endif // HAS_PINVOKE_IMPORT_PRECODE

case READYTORUN_FIXUP_PInvokeTarget:
{
Expand Down
101 changes: 92 additions & 9 deletions src/coreclr/vm/wasm/helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#define WASM_STRINGIFY_HELPER(value) #value
#define WASM_STRINGIFY(value) WASM_STRINGIFY_HELPER(value)
#define INLINED_PINVOKE_FROM_R2R 1

void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, TransitionBlock* block, size_t argsSize, int8_t* retBuff);

Expand Down Expand Up @@ -491,13 +492,22 @@ void InlinedCallFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateF
return;
}

pRD->pCurrentContext->InterpreterIP = *(DWORD *)&m_pCallerReturnAddress;

pRD->IsCallerContextValid = FALSE;

pRD->pCurrentContext->InterpreterSP = *(DWORD *)&m_pCallSiteSP;
pRD->pCurrentContext->InterpreterFP = *(DWORD *)&m_pCalleeSavedFP;

if (m_pCallerReturnAddress == INLINED_PINVOKE_FROM_R2R)
{
pRD->pCurrentContext->InterpreterSP = (TADDR)m_pCallSiteSP;
pRD->pCurrentContext->InterpreterIP = GetWasmVirtualIPFromStackPointer((TADDR)m_pCallSiteSP);
_ASSERTE(pRD->pCurrentContext->InterpreterIP != 0); // We should be in RyuJit compiled code here
pRD->pCurrentContext->InterpreterFP = GetWasmFramePointerFromStackPointer((TADDR)m_pCallSiteSP, (PCODE)pRD->pCurrentContext->InterpreterIP);
}
else
{
pRD->pCurrentContext->InterpreterIP = *(DWORD *)&m_pCallerReturnAddress;
pRD->pCurrentContext->InterpreterSP = *(DWORD *)&m_pCallSiteSP;
pRD->pCurrentContext->InterpreterFP = *(DWORD *)&m_pCalleeSavedFP;
}

SyncRegDisplayToCurrentContext(pRD);

#ifdef FEATURE_INTERPRETER
Expand Down Expand Up @@ -681,15 +691,88 @@ extern "C" void STDCALL GenericPInvokeCalliHelper(void)
PORTABILITY_ASSERT("GenericPInvokeCalliHelper is not implemented on wasm");
}

EXTERN_C void JIT_PInvokeBegin(InlinedCallFrame* pFrame)
// Does the pinvoke frame transition; the naked wrappers below have already set the wasm
// __stack_pointer global to sp so it is safe to run native code here.
EXTERN_C void JIT_PInvokeBeginImpl(void* sp, InlinedCallFrame* pFrame)
{
Thread* pThread = GetThread();

// Initialize the JIT-provided frame storage, deriving its state from sp/pep since wasm
// has no machine registers to read the caller SP / return address from.
Comment thread
davidwrighton marked this conversation as resolved.
::new ((void*)pFrame) InlinedCallFrame();
pFrame->m_pCallSiteSP = sp;
pFrame->m_pCallerReturnAddress = INLINED_PINVOKE_FROM_R2R; // When this is true, UpdateRegDisplay_Impl derives state from m_pCallSiteSP.
pFrame->m_pCalleeSavedFP = 0;
pFrame->m_pThread = pThread;

// Link the frame and transition to preemptive GC mode for the native call.
pFrame->Push();
pThread->EnablePreemptiveGC();
}

// R2R keeps its shadow SP in a local and leaves the __stack_pointer global stale, so publish
// the incoming sp to __stack_pointer before any native code runs and leave it there so the
// subsequent native pinvoke target is also safe.
extern "C" __attribute__((naked)) void JIT_PInvokeBegin(void* sp, InlinedCallFrame* pFrame, PCODE pep)
{
Comment thread
davidwrighton marked this conversation as resolved.
asm("local.get 0\n" /* sp */
"global.set __stack_pointer\n" /* __stack_pointer = sp before any native code runs */
"local.get 0\n" /* sp */
"local.get 1\n" /* pFrame */
"call %0\n"
"return" ::"i"(JIT_PInvokeBeginImpl));
}

extern "C" VOID JIT_PInvokeEndRarePath();

#ifdef DEBUG
// Debug variant of these apis tests that sp and __stack_pointer are in sync
EXTERN_C void JIT_PInvokeEndImpl(TADDR sp, TADDR stack_pointer_global_value, InlinedCallFrame* pFrame)
{
PORTABILITY_ASSERT("JIT_PInvokeBegin is not implemented on wasm");
_ASSERTE(sp == stack_pointer_global_value);
Thread* pThread = (Thread*)pFrame->m_pThread;

pThread->m_fPreemptiveGCDisabled.StoreWithoutBarrier(1);
if (g_TrapReturningThreads)
{
JIT_PInvokeEndRarePath();
}
else
{
pFrame->Pop();
}
}

EXTERN_C void JIT_PInvokeEnd(InlinedCallFrame* pFrame)
extern "C" __attribute__((naked)) void JIT_PInvokeEnd(void* sp, InlinedCallFrame* pFrame, PCODE pep)
{
asm(
"local.get 0\n" /* sp */
"global.get __stack_pointer\n" /* __stack_pointer */
"local.get 1\n" /* pFrame */
"local.get 0\n" /* sp */
"global.set __stack_pointer\n" /* __stack_pointer = sp before any native code runs, set this here, so that if the assumption around sp == __stack_pointer is wrong the assert logic will work correctly. */
"call %0\n"
"return" ::"i"(JIT_PInvokeEndImpl));
}
#else
extern "C" void JIT_PInvokeEnd(void* sp, InlinedCallFrame* pFrame, PCODE pep)
{
PORTABILITY_ASSERT("JIT_PInvokeEnd is not implemented on wasm");
UNREFERENCED_PARAMETER(sp);
UNREFERENCED_PARAMETER(pep);

Comment thread
davidwrighton marked this conversation as resolved.
Thread* pThread = (Thread*)pFrame->m_pThread;

pThread->m_fPreemptiveGCDisabled.StoreWithoutBarrier(1);
if (g_TrapReturningThreads)
{
JIT_PInvokeEndRarePath();
}
else
{
pFrame->Pop();
}
}
#endif

extern "C" void STDCALL JIT_StackProbe()
{
Expand Down
Loading