diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index dbb26066c6a351..ad2df68abd405e 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -1288,6 +1288,7 @@ InterpCompiler::InterpCompiler(COMP_HANDLE compHnd, CORINFO_METHOD_INFO* methodInfo) : m_stackmapsByClass(FreeInterpreterStackMap) , m_pInitLocalsIns(nullptr) + , m_hiddenArgumentVar(-1) , m_globalVarsWithRefsStackTop(0) { m_genericLookupToDataItemIndex.Init(&m_dataItems, this); @@ -2306,6 +2307,16 @@ bool InterpCompiler::EmitNamedIntrinsicCall(NamedIntrinsic ni, CORINFO_CLASS_HAN AddIns(INTOP_THROW_PNSE); return true; + case NI_System_StubHelpers_GetStubContext: + { + assert(m_hiddenArgumentVar >= 0); + AddIns(INTOP_MOV_P); + PushStackType(StackTypeI, NULL); + m_pLastNewIns->SetSVar(m_hiddenArgumentVar); + m_pLastNewIns->SetDVar(m_pStackPointer[-1].var); + return true; + } + case NI_System_Runtime_CompilerServices_StaticsHelpers_VolatileReadAsByref: { CHECK_STACK(1); @@ -2852,7 +2863,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re // intrinsics for the recursive call. Otherwise we will just recurse infinitely and overflow stack. // This expansion can produce value that is inconsistent with the value seen by JIT/R2R code that can // cause user code to misbehave. This is by design. One-off method Interpretation is for internal use only. - bool isMustExpand = (callInfo.hMethod == m_methodHnd); + bool isMustExpand = (callInfo.hMethod == m_methodHnd) || (ni == NI_System_StubHelpers_GetStubContext); if ((InterpConfig.InterpMode() == 3) || isMustExpand) { if (EmitNamedIntrinsicCall(ni, resolvedCallToken.hClass, callInfo.hMethod, callInfo.sig)) @@ -3616,6 +3627,17 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) // adding an opcode. AddIns(INTOP_SAFEPOINT); + CORJIT_FLAGS corJitFlags; + DWORD jitFlagsSize = m_compHnd->getJitFlags(&corJitFlags, sizeof(corJitFlags)); + assert(jitFlagsSize == sizeof(corJitFlags)); + + if (corJitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_PUBLISH_SECRET_PARAM)) + { + m_hiddenArgumentVar = CreateVarExplicit(InterpTypeI, NULL, sizeof(void *)); + AddIns(INTOP_STORESTUBCONTEXT); + m_pLastNewIns->SetDVar(m_hiddenArgumentVar); + } + CorInfoInitClassResult initOnFunctionStart = m_compHnd->initClass(NULL, NULL, METHOD_BEING_COMPILED_CONTEXT()); if ((initOnFunctionStart & CORINFO_INITCLASS_USE_HELPER) == CORINFO_INITCLASS_USE_HELPER) { diff --git a/src/coreclr/interpreter/compiler.h b/src/coreclr/interpreter/compiler.h index f880589bc6c653..79845ea7cb5d7b 100644 --- a/src/coreclr/interpreter/compiler.h +++ b/src/coreclr/interpreter/compiler.h @@ -515,6 +515,10 @@ class InterpCompiler int32_t m_currentILOffset; InterpInst* m_pInitLocalsIns; + // If the method has a hidden argument, GenerateCode allocates a var to store it and + // populates the var at method entry + int32_t m_hiddenArgumentVar; + // Table of mappings of leave instructions to the first finally call island the leave // needs to execute. TArray m_leavesTable; diff --git a/src/coreclr/interpreter/interpretershared.h b/src/coreclr/interpreter/interpretershared.h index 137077d6add124..74af7e8ab0ee3a 100644 --- a/src/coreclr/interpreter/interpretershared.h +++ b/src/coreclr/interpreter/interpretershared.h @@ -38,7 +38,10 @@ struct InterpMethod bool initLocals; bool unmanagedCallersOnly; - InterpMethod(CORINFO_METHOD_HANDLE methodHnd, int32_t argsSize, int32_t allocaSize, void** pDataItems, bool initLocals, bool unmanagedCallersOnly) + InterpMethod( + CORINFO_METHOD_HANDLE methodHnd, int32_t argsSize, int32_t allocaSize, + void** pDataItems, bool initLocals, bool unmanagedCallersOnly + ) { #if DEBUG this->self = this; diff --git a/src/coreclr/interpreter/intops.def b/src/coreclr/interpreter/intops.def index 839e6057bf0c49..3a6b9754d8b46e 100644 --- a/src/coreclr/interpreter/intops.def +++ b/src/coreclr/interpreter/intops.def @@ -417,6 +417,7 @@ OPDEF(INTOP_COMPARE_EXCHANGE_I4, "compare.exchange.i4", 5, 1, 3, InterpOpNoArgs) OPDEF(INTOP_COMPARE_EXCHANGE_I8, "compare.exchange.i8", 5, 1, 3, InterpOpNoArgs) OPDEF(INTOP_EXCHANGE_I4, "exchange.i4", 4, 1, 2, InterpOpNoArgs) OPDEF(INTOP_EXCHANGE_I8, "exchange.i8", 4, 1, 2, InterpOpNoArgs) +OPDEF(INTOP_STORESTUBCONTEXT, "storestubcontext", 2, 1, 0, InterpOpNoArgs) // All instructions after this point are IROPS, instructions that are not emitted/executed OPDEF(INTOP_NOP, "nop", 1, 0, 0, InterpOpNoArgs) diff --git a/src/coreclr/interpreter/intrinsics.cpp b/src/coreclr/interpreter/intrinsics.cpp index 2d2ba35cccb772..e6ebabd2236670 100644 --- a/src/coreclr/interpreter/intrinsics.cpp +++ b/src/coreclr/interpreter/intrinsics.cpp @@ -44,6 +44,8 @@ NamedIntrinsic GetNamedIntrinsic(COMP_HANDLE compHnd, CORINFO_METHOD_HANDLE comp { if (!strcmp(methodName, "NextCallReturnAddress")) return NI_System_StubHelpers_NextCallReturnAddress; + else if (!strcmp(methodName, "GetStubContext")) + return NI_System_StubHelpers_GetStubContext; } } else if (!strcmp(namespaceName, "System.Numerics")) diff --git a/src/coreclr/vm/dllimportcallback.cpp b/src/coreclr/vm/dllimportcallback.cpp index 58cd290172cbc3..94fe95775d418b 100644 --- a/src/coreclr/vm/dllimportcallback.cpp +++ b/src/coreclr/vm/dllimportcallback.cpp @@ -210,6 +210,19 @@ VOID CallbackOnCollectedDelegate(UMEntryThunkData* pEntryThunkData) EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_FAILFAST, message.GetUnicode()); } +#ifdef FEATURE_INTERPRETER +PLATFORM_THREAD_LOCAL UMEntryThunkData * t_MostRecentUMEntryThunkData; + +UMEntryThunkData * GetMostRecentUMEntryThunkData() +{ + LIMITED_METHOD_CONTRACT; + + UMEntryThunkData * result = t_MostRecentUMEntryThunkData; + t_MostRecentUMEntryThunkData = nullptr; + return result; +} +#endif + PCODE TheUMEntryPrestubWorker(UMEntryThunkData * pUMEntryThunkData) { STATIC_CONTRACT_THROWS; @@ -222,6 +235,15 @@ PCODE TheUMEntryPrestubWorker(UMEntryThunkData * pUMEntryThunkData) CREATETHREAD_IF_NULL_FAILFAST(pThread, W("Failed to setup new thread during reverse P/Invoke")); } +#ifdef FEATURE_INTERPRETER + PCODE pInterpreterTarget = pUMEntryThunkData->GetInterpreterTarget(); + if (pInterpreterTarget != (PCODE)0) + { + t_MostRecentUMEntryThunkData = pUMEntryThunkData; + return pInterpreterTarget; + } +#endif // FEATURE_INTERPRETER + // Verify the current thread isn't in COOP mode. if (pThread->PreemptiveGCDisabled()) ReversePInvokeBadTransition(); @@ -263,7 +285,7 @@ UMEntryThunkData* UMEntryThunkData::CreateUMEntryThunk() LoaderAllocator *pLoaderAllocator = SystemDomain::GetGlobalLoaderAllocator(); AllocMemTracker amTracker; AllocMemTracker *pamTracker = &amTracker; - + pData = (UMEntryThunkData *)pamTracker->Track(pLoaderAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(UMEntryThunkData)))); UMEntryThunk* pThunk = (UMEntryThunk*)pamTracker->Track(pLoaderAllocator->GetNewStubPrecodeHeap()->AllocStub()); #ifdef FEATURE_PERFMAP diff --git a/src/coreclr/vm/dllimportcallback.h b/src/coreclr/vm/dllimportcallback.h index 5341c1420e4d61..eedf953b0ff394 100644 --- a/src/coreclr/vm/dllimportcallback.h +++ b/src/coreclr/vm/dllimportcallback.h @@ -134,7 +134,7 @@ class UMEntryThunk : private StubPrecode PTR_UMEntryThunkData GetData() const { LIMITED_METHOD_CONTRACT; - + return dac_cast(GetSecretParam()); } }; @@ -149,6 +149,13 @@ class UMEntryThunkData // but it may not have the correct value. PCODE m_pManagedTarget; +#ifdef FEATURE_INTERPRETER + // InterpreterPrecode to tailcall if the target is interpreted. This allows TheUMEntryPrestubWorker + // stash the hidden argument in a thread static and avoid collision with the hidden argument + // used by InterpreterPrecode. + Volatile m_pInterpretedTarget; +#endif + // This is used for debugging and profiling. PTR_MethodDesc m_pMD; @@ -200,6 +207,9 @@ class UMEntryThunkData m_pManagedTarget = pManagedTarget; m_pObjectHandle = pObjectHandle; m_pUMThunkMarshInfo = pUMThunkMarshInfo; +#ifdef FEATURE_INTERPRETER + m_pInterpretedTarget = (PCODE)0; +#endif m_pMD = pMD; @@ -214,7 +224,15 @@ class UMEntryThunkData void Terminate(); - VOID RunTimeInit() +#ifdef FEATURE_INTERPRETER + PCODE GetInterpreterTarget() + { + LIMITED_METHOD_CONTRACT; + return m_pInterpretedTarget; + } +#endif + + void RunTimeInit() { STANDARD_VM_CONTRACT; @@ -227,7 +245,27 @@ class UMEntryThunkData if (m_pObjectHandle == NULL && m_pManagedTarget == (TADDR)0) m_pManagedTarget = m_pMD->GetMultiCallableAddrOfCode(); - m_pUMEntryThunk->SetTargetUnconditional(m_pUMThunkMarshInfo->GetExecStubEntryPoint()); + PCODE entryPoint = m_pUMThunkMarshInfo->GetExecStubEntryPoint(); + +#ifdef FEATURE_INTERPRETER + // For interpreted stubs we need to ensure that TheUMEntryPrestubWorker runs for every + // unmanaged-to-managed invocation in order to populate the TLS variable every time. + auto stubKind = RangeSectionStubManager::GetStubKind(entryPoint); + if (stubKind == STUB_CODE_BLOCK_STUBPRECODE) + { + StubPrecode* pPrecode = Precode::GetPrecodeFromEntryPoint(entryPoint)->AsStubPrecode(); + if (pPrecode->GetType() == PRECODE_INTERPRETER) + { + m_pInterpretedTarget = entryPoint; + entryPoint = (PCODE)0; + } + } + + if (entryPoint != (PCODE)0) +#endif // FEATURE_INTERPRETER + { + m_pUMEntryThunk->SetTargetUnconditional(entryPoint); + } #ifdef _DEBUG m_state = kRunTimeInited; diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index bc504bab76272e..883b5327751085 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -530,6 +530,8 @@ void PrepareInitialCode(MethodDesc *pMD) PAL_ENDTRY } +UMEntryThunkData * GetMostRecentUMEntryThunkData(); + void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFrame *pFrame, InterpThreadContext *pThreadContext, ExceptionClauseArgs *pExceptionClauseArgs) { CONTRACTL @@ -610,6 +612,10 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr MemoryBarrier(); ip++; break; + case INTOP_STORESTUBCONTEXT: + LOCAL_VAR(ip[1], void*) = GetMostRecentUMEntryThunkData(); + ip += 2; + break; case INTOP_LDC_I4: LOCAL_VAR(ip[1], int32_t) = ip[2]; ip += 3;