Skip to content

Commit e3467fc

Browse files
authored
[interp] Reverse p/invoke (#119133)
* Implements GetStubContext intrinsic in the interpreter * For interpreted reverse p/invoke stubs, we ensure that TheUMEntryPrestubWorker runs for every invocation so that it can capture the hidden argument in a thread local. The interpreter can then recover the hidden argument from the thread local in order to offer it via GetStubContext later. * Upon entry to interpreted methods that accept a hidden argument, we store the hidden argument (that was saved by the um entry prestub worker) into a special local that can be read from later by calls to GetStubContext.
1 parent af1a89f commit e3467fc

File tree

8 files changed

+104
-6
lines changed

8 files changed

+104
-6
lines changed

src/coreclr/interpreter/compiler.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,7 @@ InterpCompiler::InterpCompiler(COMP_HANDLE compHnd,
12881288
CORINFO_METHOD_INFO* methodInfo)
12891289
: m_stackmapsByClass(FreeInterpreterStackMap)
12901290
, m_pInitLocalsIns(nullptr)
1291+
, m_hiddenArgumentVar(-1)
12911292
, m_globalVarsWithRefsStackTop(0)
12921293
{
12931294
m_genericLookupToDataItemIndex.Init(&m_dataItems, this);
@@ -2306,6 +2307,16 @@ bool InterpCompiler::EmitNamedIntrinsicCall(NamedIntrinsic ni, CORINFO_CLASS_HAN
23062307
AddIns(INTOP_THROW_PNSE);
23072308
return true;
23082309

2310+
case NI_System_StubHelpers_GetStubContext:
2311+
{
2312+
assert(m_hiddenArgumentVar >= 0);
2313+
AddIns(INTOP_MOV_P);
2314+
PushStackType(StackTypeI, NULL);
2315+
m_pLastNewIns->SetSVar(m_hiddenArgumentVar);
2316+
m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);
2317+
return true;
2318+
}
2319+
23092320
case NI_System_Runtime_CompilerServices_StaticsHelpers_VolatileReadAsByref:
23102321
{
23112322
CHECK_STACK(1);
@@ -2852,7 +2863,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
28522863
// intrinsics for the recursive call. Otherwise we will just recurse infinitely and overflow stack.
28532864
// This expansion can produce value that is inconsistent with the value seen by JIT/R2R code that can
28542865
// cause user code to misbehave. This is by design. One-off method Interpretation is for internal use only.
2855-
bool isMustExpand = (callInfo.hMethod == m_methodHnd);
2866+
bool isMustExpand = (callInfo.hMethod == m_methodHnd) || (ni == NI_System_StubHelpers_GetStubContext);
28562867
if ((InterpConfig.InterpMode() == 3) || isMustExpand)
28572868
{
28582869
if (EmitNamedIntrinsicCall(ni, resolvedCallToken.hClass, callInfo.hMethod, callInfo.sig))
@@ -3697,6 +3708,17 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
36973708
// adding an opcode.
36983709
AddIns(INTOP_SAFEPOINT);
36993710

3711+
CORJIT_FLAGS corJitFlags;
3712+
DWORD jitFlagsSize = m_compHnd->getJitFlags(&corJitFlags, sizeof(corJitFlags));
3713+
assert(jitFlagsSize == sizeof(corJitFlags));
3714+
3715+
if (corJitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_PUBLISH_SECRET_PARAM))
3716+
{
3717+
m_hiddenArgumentVar = CreateVarExplicit(InterpTypeI, NULL, sizeof(void *));
3718+
AddIns(INTOP_STORESTUBCONTEXT);
3719+
m_pLastNewIns->SetDVar(m_hiddenArgumentVar);
3720+
}
3721+
37003722
CorInfoInitClassResult initOnFunctionStart = m_compHnd->initClass(NULL, NULL, METHOD_BEING_COMPILED_CONTEXT());
37013723
if ((initOnFunctionStart & CORINFO_INITCLASS_USE_HELPER) == CORINFO_INITCLASS_USE_HELPER)
37023724
{

src/coreclr/interpreter/compiler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,10 @@ class InterpCompiler
514514
int32_t m_currentILOffset;
515515
InterpInst* m_pInitLocalsIns;
516516

517+
// If the method has a hidden argument, GenerateCode allocates a var to store it and
518+
// populates the var at method entry
519+
int32_t m_hiddenArgumentVar;
520+
517521
// Table of mappings of leave instructions to the first finally call island the leave
518522
// needs to execute.
519523
TArray<LeavesTableEntry> m_leavesTable;

src/coreclr/interpreter/interpretershared.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ struct InterpMethod
3838
bool initLocals;
3939
bool unmanagedCallersOnly;
4040

41-
InterpMethod(CORINFO_METHOD_HANDLE methodHnd, int32_t argsSize, int32_t allocaSize, void** pDataItems, bool initLocals, bool unmanagedCallersOnly)
41+
InterpMethod(
42+
CORINFO_METHOD_HANDLE methodHnd, int32_t argsSize, int32_t allocaSize,
43+
void** pDataItems, bool initLocals, bool unmanagedCallersOnly
44+
)
4245
{
4346
#if DEBUG
4447
this->self = this;

src/coreclr/interpreter/intops.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ OPDEF(INTOP_COMPARE_EXCHANGE_I4, "compare.exchange.i4", 5, 1, 3, InterpOpNoArgs)
417417
OPDEF(INTOP_COMPARE_EXCHANGE_I8, "compare.exchange.i8", 5, 1, 3, InterpOpNoArgs)
418418
OPDEF(INTOP_EXCHANGE_I4, "exchange.i4", 4, 1, 2, InterpOpNoArgs)
419419
OPDEF(INTOP_EXCHANGE_I8, "exchange.i8", 4, 1, 2, InterpOpNoArgs)
420+
OPDEF(INTOP_STORESTUBCONTEXT, "storestubcontext", 2, 1, 0, InterpOpNoArgs)
420421

421422
// All instructions after this point are IROPS, instructions that are not emitted/executed
422423
OPDEF(INTOP_NOP, "nop", 1, 0, 0, InterpOpNoArgs)

src/coreclr/interpreter/intrinsics.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ NamedIntrinsic GetNamedIntrinsic(COMP_HANDLE compHnd, CORINFO_METHOD_HANDLE comp
4444
{
4545
if (!strcmp(methodName, "NextCallReturnAddress"))
4646
return NI_System_StubHelpers_NextCallReturnAddress;
47+
else if (!strcmp(methodName, "GetStubContext"))
48+
return NI_System_StubHelpers_GetStubContext;
4749
}
4850
}
4951
else if (!strcmp(namespaceName, "System.Numerics"))

src/coreclr/vm/dllimportcallback.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,19 @@ VOID CallbackOnCollectedDelegate(UMEntryThunkData* pEntryThunkData)
210210
EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_FAILFAST, message.GetUnicode());
211211
}
212212

213+
#ifdef FEATURE_INTERPRETER
214+
PLATFORM_THREAD_LOCAL UMEntryThunkData * t_MostRecentUMEntryThunkData;
215+
216+
UMEntryThunkData * GetMostRecentUMEntryThunkData()
217+
{
218+
LIMITED_METHOD_CONTRACT;
219+
220+
UMEntryThunkData * result = t_MostRecentUMEntryThunkData;
221+
t_MostRecentUMEntryThunkData = nullptr;
222+
return result;
223+
}
224+
#endif
225+
213226
PCODE TheUMEntryPrestubWorker(UMEntryThunkData * pUMEntryThunkData)
214227
{
215228
STATIC_CONTRACT_THROWS;
@@ -222,6 +235,15 @@ PCODE TheUMEntryPrestubWorker(UMEntryThunkData * pUMEntryThunkData)
222235
CREATETHREAD_IF_NULL_FAILFAST(pThread, W("Failed to setup new thread during reverse P/Invoke"));
223236
}
224237

238+
#ifdef FEATURE_INTERPRETER
239+
PCODE pInterpreterTarget = pUMEntryThunkData->GetInterpreterTarget();
240+
if (pInterpreterTarget != (PCODE)0)
241+
{
242+
t_MostRecentUMEntryThunkData = pUMEntryThunkData;
243+
return pInterpreterTarget;
244+
}
245+
#endif // FEATURE_INTERPRETER
246+
225247
// Verify the current thread isn't in COOP mode.
226248
if (pThread->PreemptiveGCDisabled())
227249
ReversePInvokeBadTransition();
@@ -263,7 +285,7 @@ UMEntryThunkData* UMEntryThunkData::CreateUMEntryThunk()
263285
LoaderAllocator *pLoaderAllocator = SystemDomain::GetGlobalLoaderAllocator();
264286
AllocMemTracker amTracker;
265287
AllocMemTracker *pamTracker = &amTracker;
266-
288+
267289
pData = (UMEntryThunkData *)pamTracker->Track(pLoaderAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(UMEntryThunkData))));
268290
UMEntryThunk* pThunk = (UMEntryThunk*)pamTracker->Track(pLoaderAllocator->GetNewStubPrecodeHeap()->AllocStub());
269291
#ifdef FEATURE_PERFMAP

src/coreclr/vm/dllimportcallback.h

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ class UMEntryThunk : private StubPrecode
134134
PTR_UMEntryThunkData GetData() const
135135
{
136136
LIMITED_METHOD_CONTRACT;
137-
137+
138138
return dac_cast<PTR_UMEntryThunkData>(GetSecretParam());
139139
}
140140
};
@@ -149,6 +149,13 @@ class UMEntryThunkData
149149
// but it may not have the correct value.
150150
PCODE m_pManagedTarget;
151151

152+
#ifdef FEATURE_INTERPRETER
153+
// InterpreterPrecode to tailcall if the target is interpreted. This allows TheUMEntryPrestubWorker
154+
// stash the hidden argument in a thread static and avoid collision with the hidden argument
155+
// used by InterpreterPrecode.
156+
Volatile<PCODE> m_pInterpretedTarget;
157+
#endif
158+
152159
// This is used for debugging and profiling.
153160
PTR_MethodDesc m_pMD;
154161

@@ -200,6 +207,9 @@ class UMEntryThunkData
200207
m_pManagedTarget = pManagedTarget;
201208
m_pObjectHandle = pObjectHandle;
202209
m_pUMThunkMarshInfo = pUMThunkMarshInfo;
210+
#ifdef FEATURE_INTERPRETER
211+
m_pInterpretedTarget = (PCODE)0;
212+
#endif
203213

204214
m_pMD = pMD;
205215

@@ -214,7 +224,15 @@ class UMEntryThunkData
214224

215225
void Terminate();
216226

217-
VOID RunTimeInit()
227+
#ifdef FEATURE_INTERPRETER
228+
PCODE GetInterpreterTarget()
229+
{
230+
LIMITED_METHOD_CONTRACT;
231+
return m_pInterpretedTarget;
232+
}
233+
#endif
234+
235+
void RunTimeInit()
218236
{
219237
STANDARD_VM_CONTRACT;
220238

@@ -227,7 +245,27 @@ class UMEntryThunkData
227245
if (m_pObjectHandle == NULL && m_pManagedTarget == (TADDR)0)
228246
m_pManagedTarget = m_pMD->GetMultiCallableAddrOfCode();
229247

230-
m_pUMEntryThunk->SetTargetUnconditional(m_pUMThunkMarshInfo->GetExecStubEntryPoint());
248+
PCODE entryPoint = m_pUMThunkMarshInfo->GetExecStubEntryPoint();
249+
250+
#ifdef FEATURE_INTERPRETER
251+
// For interpreted stubs we need to ensure that TheUMEntryPrestubWorker runs for every
252+
// unmanaged-to-managed invocation in order to populate the TLS variable every time.
253+
auto stubKind = RangeSectionStubManager::GetStubKind(entryPoint);
254+
if (stubKind == STUB_CODE_BLOCK_STUBPRECODE)
255+
{
256+
StubPrecode* pPrecode = Precode::GetPrecodeFromEntryPoint(entryPoint)->AsStubPrecode();
257+
if (pPrecode->GetType() == PRECODE_INTERPRETER)
258+
{
259+
m_pInterpretedTarget = entryPoint;
260+
entryPoint = (PCODE)0;
261+
}
262+
}
263+
264+
if (entryPoint != (PCODE)0)
265+
#endif // FEATURE_INTERPRETER
266+
{
267+
m_pUMEntryThunk->SetTargetUnconditional(entryPoint);
268+
}
231269

232270
#ifdef _DEBUG
233271
m_state = kRunTimeInited;

src/coreclr/vm/interpexec.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,8 @@ void PrepareInitialCode(MethodDesc *pMD)
530530
PAL_ENDTRY
531531
}
532532

533+
UMEntryThunkData * GetMostRecentUMEntryThunkData();
534+
533535
void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFrame *pFrame, InterpThreadContext *pThreadContext, ExceptionClauseArgs *pExceptionClauseArgs)
534536
{
535537
CONTRACTL
@@ -610,6 +612,10 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
610612
MemoryBarrier();
611613
ip++;
612614
break;
615+
case INTOP_STORESTUBCONTEXT:
616+
LOCAL_VAR(ip[1], void*) = GetMostRecentUMEntryThunkData();
617+
ip += 2;
618+
break;
613619
case INTOP_LDC_I4:
614620
LOCAL_VAR(ip[1], int32_t) = ip[2];
615621
ip += 3;

0 commit comments

Comments
 (0)