Skip to content

Commit a216015

Browse files
authored
Implement interpreter PInvoke Calli (#119356)
* Implement interpreter PInvoke Calli The Calli implementation was missing support for the case when the targer is a pinvoke. That resulted in errors due to incorrect GC mode. This change fixes that. * Add handling of pInvokeMarshallingRequired and suppressGCTransition * Fix argument name * PR feedback * Disable calli with marshalled pinvokes * Refactor the target invocation methods * PR feedback and some unification of arguments * Fix calling convention check - mask IMAGE_CEE_CS_CALLCONV_MASK
1 parent 6ff294e commit a216015

File tree

6 files changed

+116
-22
lines changed

6 files changed

+116
-22
lines changed

src/coreclr/interpreter/compiler.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2854,6 +2854,27 @@ void InterpCompiler::EmitPushLdvirtftn(int thisVar, CORINFO_RESOLVED_TOKEN* pRes
28542854
m_pLastNewIns->info.pCallInfo->pCallArgs = callArgs;
28552855
}
28562856

2857+
void InterpCompiler::EmitCalli(bool isTailCall, void* calliCookie, int callIFunctionPointerVar, CORINFO_SIG_INFO* callSiteSig)
2858+
{
2859+
AddIns(isTailCall ? INTOP_CALLI_TAIL : INTOP_CALLI);
2860+
m_pLastNewIns->data[0] = GetDataItemIndex(calliCookie);
2861+
// data[1] is set to 1 if the calli is calling a pinvoke, 0 otherwise
2862+
bool suppressGCTransition = false;
2863+
CorInfoCallConv callConv = (CorInfoCallConv)(callSiteSig->callConv & IMAGE_CEE_CS_CALLCONV_MASK);
2864+
bool isPInvoke = (callConv != CORINFO_CALLCONV_DEFAULT && callConv != CORINFO_CALLCONV_VARARG);
2865+
if (isPInvoke)
2866+
{
2867+
if (m_compHnd->pInvokeMarshalingRequired(NULL, callSiteSig))
2868+
{
2869+
BADCODE("PInvoke marshalling for calli is not supported in interpreted code");
2870+
}
2871+
m_compHnd->getUnmanagedCallConv(nullptr, callSiteSig, &suppressGCTransition);
2872+
}
2873+
m_pLastNewIns->data[1] = (suppressGCTransition ? (int32_t)CalliFlags::SuppressGCTransition : 0) |
2874+
(isPInvoke ? (int32_t)CalliFlags::PInvoke : 0);
2875+
m_pLastNewIns->SetSVars2(CALL_ARGS_SVAR, callIFunctionPointerVar);
2876+
}
2877+
28572878
void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool readonly, bool tailcall, bool newObj, bool isCalli)
28582879
{
28592880
uint32_t token = getU4LittleEndian(m_ip + 1);
@@ -3259,9 +3280,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
32593280
}
32603281
else if (isCalli)
32613282
{
3262-
AddIns(tailcall ? INTOP_CALLI_TAIL : INTOP_CALLI);
3263-
m_pLastNewIns->data[0] = GetDataItemIndex(calliCookie);
3264-
m_pLastNewIns->SetSVars2(CALL_ARGS_SVAR, callIFunctionPointerVar);
3283+
EmitCalli(tailcall, calliCookie, callIFunctionPointerVar, &callInfo.sig);
32653284
}
32663285
else
32673286
{
@@ -3318,9 +3337,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
33183337

33193338
calliCookie = m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig);
33203339

3321-
AddIns(tailcall ? INTOP_CALLI_TAIL : INTOP_CALLI);
3322-
m_pLastNewIns->data[0] = GetDataItemIndex(calliCookie);
3323-
m_pLastNewIns->SetSVars2(CALL_ARGS_SVAR, codePointerLookupResult);
3340+
EmitCalli(tailcall, calliCookie, codePointerLookupResult, &callInfo.sig);
33243341
break;
33253342
}
33263343
case CORINFO_VIRTUALCALL_VTABLE:
@@ -3353,9 +3370,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
33533370

33543371
calliCookie = m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig);
33553372

3356-
AddIns(tailcall ? INTOP_CALLI_TAIL : INTOP_CALLI);
3357-
m_pLastNewIns->data[0] = GetDataItemIndex(calliCookie);
3358-
m_pLastNewIns->SetSVars2(CALL_ARGS_SVAR, synthesizedLdvirtftnPtrVar);
3373+
EmitCalli(tailcall, calliCookie, synthesizedLdvirtftnPtrVar, &callInfo.sig);
33593374
}
33603375
else
33613376
{

src/coreclr/interpreter/compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,7 @@ class InterpCompiler
711711
void EmitShiftOp(int32_t opBase);
712712
void EmitCompareOp(int32_t opBase);
713713
void EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool readonly, bool tailcall, bool newObj, bool isCalli);
714+
void EmitCalli(bool isTailCall, void* calliCookie, int callIFunctionPointerVar, CORINFO_SIG_INFO* callSiteSig);
714715
bool EmitNamedIntrinsicCall(NamedIntrinsic ni, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO sig);
715716
void EmitLdind(InterpType type, CORINFO_CLASS_HANDLE clsHnd, int32_t offset);
716717
void EmitStind(InterpType type, CORINFO_CLASS_HANDLE clsHnd, int32_t offset, bool reverseSVarOrder);

src/coreclr/interpreter/inc/interpretershared.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,11 @@ enum class PInvokeCallFlags : int32_t
171171
SuppressGCTransition = 1 << 1, // The pinvoke is marked by the SuppressGCTransition attribute
172172
};
173173

174+
enum class CalliFlags : int32_t
175+
{
176+
None = 0,
177+
SuppressGCTransition = 1 << 1, // The call is marked by the SuppressGCTransition attribute
178+
PInvoke = 1 << 2, // The call is a PInvoke call
179+
};
180+
174181
#endif

src/coreclr/interpreter/inc/intops.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ OPDEF(INTOP_LDFLDA, "ldflda", 4, 1, 1, InterpOpInt)
358358
// Calls
359359
OPDEF(INTOP_CALL, "call", 4, 1, 1, InterpOpMethodHandle)
360360
OPDEF(INTOP_CALLDELEGATE, "call.delegate", 4, 1, 1, InterpOpMethodHandle)
361-
OPDEF(INTOP_CALLI, "calli", 5, 1, 2, InterpOpLdPtr)
361+
OPDEF(INTOP_CALLI, "calli", 6, 1, 2, InterpOpLdPtr)
362362
OPDEF(INTOP_CALLVIRT, "callvirt", 4, 1, 1, InterpOpMethodHandle)
363363
OPDEF(INTOP_CALL_PINVOKE, "call.pinvoke", 6, 1, 1, InterpOpMethodHandle) // inlined (no marshaling wrapper) pinvokes only
364364
OPDEF(INTOP_NEWOBJ, "newobj", 5, 1, 1, InterpOpMethodHandle)
@@ -367,7 +367,7 @@ OPDEF(INTOP_NEWOBJ_VT, "newobj.vt", 5, 1, 1, InterpOpMethodHandle)
367367

368368
// Tail calls
369369
OPDEF(INTOP_CALL_TAIL, "call.tail", 4, 1, 1, InterpOpMethodHandle)
370-
OPDEF(INTOP_CALLI_TAIL, "calli", 5, 1, 2, InterpOpLdPtr)
370+
OPDEF(INTOP_CALLI_TAIL, "calli.tail", 6, 1, 2, InterpOpLdPtr)
371371
OPDEF(INTOP_CALLVIRT_TAIL, "callvirt.tail", 4, 1, 1, InterpOpMethodHandle)
372372

373373
// The following helper call instructions exist in 2 variants, one for normal methods, and one for cases where a shared generic lookup is needed.

src/coreclr/vm/interpexec.cpp

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313

1414
// Call invoker helpers provided by platform.
1515
void InvokeManagedMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet, PCODE target);
16-
void InvokeUnmanagedMethod(MethodDesc *targetMethod, int8_t *stack, InterpMethodContextFrame *pFrame, int32_t callArgsOffset, int32_t returnOffset, PCODE callTarget);
17-
void InvokeCalliStub(PCODE ftn, void* stubHeaderTemplate, int8_t *pArgs, int8_t *pRet);
16+
void InvokeUnmanagedMethod(MethodDesc *targetMethod, int8_t *pArgs, int8_t *pRet, PCODE callTarget);
17+
void InvokeCalliStub(PCODE ftn, void* cookie, int8_t *pArgs, int8_t *pRet);
18+
void InvokeUnmanagedCalli(PCODE ftn, void *cookie, int8_t *pArgs, int8_t *pRet);
1819
void InvokeDelegateInvokeMethod(MethodDesc *pMDDelegateInvoke, int8_t *pArgs, int8_t *pRet, PCODE target);
1920

2021
// Use the NOINLINE to ensure that the InlinedCallFrame in this method is a lower stack address than any InterpMethodContextFrame values.
2122
NOINLINE
22-
void InvokeUnmanagedMethodWithTransition(MethodDesc *targetMethod, int8_t *stack, InterpMethodContextFrame *pFrame, int32_t callArgsOffset, int32_t returnOffset, PCODE callTarget)
23+
void InvokeUnmanagedMethodWithTransition(MethodDesc *targetMethod, int8_t *stack, InterpMethodContextFrame *pFrame, int8_t *pArgs, int8_t *pRet, PCODE callTarget)
2324
{
2425
InlinedCallFrame inlinedCallFrame;
2526
inlinedCallFrame.m_pCallerReturnAddress = (TADDR)pFrame->ip;
@@ -32,12 +33,38 @@ void InvokeUnmanagedMethodWithTransition(MethodDesc *targetMethod, int8_t *stack
3233
{
3334
GCX_PREEMP();
3435
// WASM-TODO: Handle unmanaged calling conventions
35-
InvokeManagedMethod(targetMethod, stack + callArgsOffset, stack + returnOffset, callTarget);
36+
InvokeManagedMethod(targetMethod, pArgs, pRet, callTarget);
3637
}
3738

3839
inlinedCallFrame.Pop();
3940
}
4041

42+
NOINLINE
43+
void InvokeUnmanagedCalliWithTransition(PCODE ftn, void *cookie, int8_t *stack, InterpMethodContextFrame *pFrame, int8_t *pArgs, int8_t *pRet)
44+
{
45+
CONTRACTL
46+
{
47+
THROWS;
48+
MODE_COOPERATIVE;
49+
PRECONDITION(CheckPointer((void*)ftn));
50+
PRECONDITION(CheckPointer(cookie));
51+
}
52+
CONTRACTL_END
53+
54+
InlinedCallFrame inlinedCallFrame;
55+
inlinedCallFrame.m_pCallerReturnAddress = (TADDR)pFrame->ip;
56+
inlinedCallFrame.m_pCallSiteSP = pFrame;
57+
inlinedCallFrame.m_pCalleeSavedFP = (TADDR)stack;
58+
inlinedCallFrame.m_pThread = GetThread();
59+
inlinedCallFrame.m_Datum = NULL;
60+
inlinedCallFrame.Push();
61+
{
62+
GCX_PREEMP();
63+
InvokeUnmanagedCalli(ftn, cookie, pArgs, pRet);
64+
}
65+
inlinedCallFrame.Pop();
66+
}
67+
4168
#ifndef TARGET_WASM
4269
#include "callstubgenerator.h"
4370

@@ -96,9 +123,9 @@ void InvokeManagedMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet, PCODE tar
96123
pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize);
97124
}
98125

99-
void InvokeUnmanagedMethod(MethodDesc *targetMethod, int8_t *stack, InterpMethodContextFrame *pFrame, int32_t callArgsOffset, int32_t returnOffset, PCODE callTarget)
126+
void InvokeUnmanagedMethod(MethodDesc *targetMethod, int8_t *pArgs, int8_t *pRet, PCODE callTarget)
100127
{
101-
InvokeManagedMethod(targetMethod, stack + callArgsOffset, stack + returnOffset, callTarget);
128+
InvokeManagedMethod(targetMethod, pArgs, pRet, callTarget);
102129
}
103130

104131
void InvokeDelegateInvokeMethod(MethodDesc *pMDDelegateInvoke, int8_t *pArgs, int8_t *pRet, PCODE target)
@@ -129,6 +156,29 @@ void InvokeDelegateInvokeMethod(MethodDesc *pMDDelegateInvoke, int8_t *pArgs, in
129156
pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize);
130157
}
131158

159+
void InvokeUnmanagedCalli(PCODE ftn, void *cookie, int8_t *pArgs, int8_t *pRet)
160+
{
161+
CONTRACTL
162+
{
163+
THROWS;
164+
MODE_ANY;
165+
PRECONDITION(CheckPointer((void*)ftn));
166+
PRECONDITION(CheckPointer(cookie));
167+
}
168+
CONTRACTL_END
169+
170+
// CallStubHeaders encode their destination addresses in the Routines array, so they need to be
171+
// copied to a local buffer before we can actually set their target address.
172+
CallStubHeader* stubHeaderTemplate = (CallStubHeader*)cookie;
173+
size_t templateSize = stubHeaderTemplate->GetSize();
174+
uint8_t* actualCallStub = (uint8_t*)alloca(templateSize);
175+
memcpy(actualCallStub, stubHeaderTemplate, templateSize);
176+
CallStubHeader *pHeader = (CallStubHeader*)actualCallStub;
177+
pHeader->SetTarget(ftn); // The method to call
178+
179+
pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize);
180+
}
181+
132182
void InvokeCalliStub(PCODE ftn, void *cookie, int8_t *pArgs, int8_t *pRet)
133183
{
134184
CONTRACTL
@@ -2117,15 +2167,31 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
21172167
callArgsOffset = ip[2];
21182168
int32_t calliFunctionPointerVar = ip[3];
21192169
int32_t calliCookie = ip[4];
2170+
int32_t flags = ip[5];
21202171

21212172
void* cookie = pMethod->pDataItems[calliCookie];
2122-
ip += 5;
2173+
ip += 6;
21232174

21242175
// Save current execution state for when we return from called method
21252176
pFrame->ip = ip;
21262177

21272178
// Interpreter-FIXME: isTailcall
2128-
InvokeCalliStub(LOCAL_VAR(calliFunctionPointerVar, PCODE), cookie, stack + callArgsOffset, stack + returnOffset);
2179+
if (flags & (int32_t)CalliFlags::PInvoke)
2180+
{
2181+
if (flags & (int32_t)CalliFlags::SuppressGCTransition)
2182+
{
2183+
InvokeUnmanagedCalli(LOCAL_VAR(calliFunctionPointerVar, PCODE), cookie, stack + callArgsOffset, stack + returnOffset);
2184+
}
2185+
else
2186+
{
2187+
InvokeUnmanagedCalliWithTransition(LOCAL_VAR(calliFunctionPointerVar, PCODE), cookie, stack, pFrame, stack + callArgsOffset, stack + returnOffset);
2188+
}
2189+
}
2190+
else
2191+
{
2192+
InvokeCalliStub(LOCAL_VAR(calliFunctionPointerVar, PCODE), cookie, stack + callArgsOffset, stack + returnOffset);
2193+
}
2194+
21292195
break;
21302196
}
21312197

@@ -2152,11 +2218,11 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
21522218

21532219
if (flags & (int32_t)PInvokeCallFlags::SuppressGCTransition)
21542220
{
2155-
InvokeUnmanagedMethod(targetMethod, stack, pFrame, callArgsOffset, returnOffset, callTarget);
2221+
InvokeUnmanagedMethod(targetMethod, stack + callArgsOffset, stack + returnOffset, callTarget);
21562222
}
21572223
else
21582224
{
2159-
InvokeUnmanagedMethodWithTransition(targetMethod, stack, pFrame, callArgsOffset, returnOffset, callTarget);
2225+
InvokeUnmanagedMethodWithTransition(targetMethod, stack, pFrame, stack + callArgsOffset, stack + returnOffset, callTarget);
21602226
}
21612227

21622228
break;

src/coreclr/vm/wasm/helpers.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ void InvokeManagedMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet, PCODE tar
390390
PORTABILITY_ASSERT("Attempted to execute non-interpreter code from interpreter on wasm, this is not yet implemented");
391391
}
392392

393-
void InvokeUnmanagedMethod(MethodDesc *targetMethod, int8_t *stack, InterpMethodContextFrame *pFrame, int32_t callArgsOffset, int32_t returnOffset, PCODE callTarget)
393+
void InvokeUnmanagedMethod(MethodDesc *targetMethod, int8_t *pArgs, int8_t *pRet, PCODE callTarget)
394394
{
395395
PORTABILITY_ASSERT("Attempted to execute unmanaged code from interpreter on wasm, this is not yet implemented");
396396
}
@@ -403,6 +403,11 @@ void InvokeCalliStub(PCODE ftn, void* cookie, int8_t *pArgs, int8_t *pRet)
403403
((void(*)(PCODE, int8_t*, int8_t*))cookie)(ftn, pArgs, pRet);
404404
}
405405

406+
void InvokeUnmanagedCalli(PCODE ftn, void *cookie, int8_t *pArgs, int8_t *pRet)
407+
{
408+
PORTABILITY_ASSERT("Attempted to execute unmanaged calli from interpreter on wasm, this is not yet implemented");
409+
}
410+
406411
void InvokeDelegateInvokeMethod(MethodDesc *pMDDelegateInvoke, int8_t *pArgs, int8_t *pRet, PCODE target)
407412
{
408413
PORTABILITY_ASSERT("Attempted to execute non-interpreter code from interpreter on wasm, this is not yet implemented");

0 commit comments

Comments
 (0)