Skip to content
24 changes: 23 additions & 1 deletion src/coreclr/interpreter/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
{
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/interpreter/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<LeavesTableEntry> m_leavesTable;
Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/interpreter/interpretershared.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/interpreter/intops.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/interpreter/intrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down
24 changes: 23 additions & 1 deletion src/coreclr/vm/dllimportcallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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
Expand Down
44 changes: 41 additions & 3 deletions src/coreclr/vm/dllimportcallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ class UMEntryThunk : private StubPrecode
PTR_UMEntryThunkData GetData() const
{
LIMITED_METHOD_CONTRACT;

return dac_cast<PTR_UMEntryThunkData>(GetSecretParam());
}
};
Expand All @@ -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<PCODE> m_pInterpretedTarget;
#endif

// This is used for debugging and profiling.
PTR_MethodDesc m_pMD;

Expand Down Expand Up @@ -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;

Expand All @@ -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;

Expand All @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/vm/interpexec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,8 @@ void PrepareInitialCode(MethodDesc *pMD)
PAL_ENDTRY
}

UMEntryThunkData * GetMostRecentUMEntryThunkData();

void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFrame *pFrame, InterpThreadContext *pThreadContext, ExceptionClauseArgs *pExceptionClauseArgs)
{
CONTRACTL
Expand Down Expand Up @@ -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;
Expand Down
Loading