Skip to content

Commit 5c5ed27

Browse files
davidwrightonCopilotjkotas
authored
[clr-interp] Intrinic handling for Delegate Invoke methods (#118406)
* [Interpreter] Intrinic handling for Delegate Invoke methods - Add a new opcode - Add a new invocation method. Note that this needs to have the same copy the CallStubHeader logic as calli, as the target is not consistent from use to use Co-authored-by: Copilot <[email protected]> Co-authored-by: Jan Kotas <[email protected]>
1 parent de93e0a commit 5c5ed27

File tree

4 files changed

+106
-18
lines changed

4 files changed

+106
-18
lines changed

src/coreclr/interpreter/compiler.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2712,6 +2712,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
27122712
{
27132713
uint32_t token = getU4LittleEndian(m_ip + 1);
27142714
bool isVirtual = (*m_ip == CEE_CALLVIRT);
2715+
bool isDelegateInvoke = false;
27152716

27162717
CORINFO_RESOLVED_TOKEN resolvedCallToken;
27172718
CORINFO_CALL_INFO callInfo;
@@ -2763,6 +2764,11 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
27632764
}
27642765
}
27652766

2767+
if (callInfo.methodFlags & CORINFO_FLG_DELEGATE_INVOKE)
2768+
{
2769+
isDelegateInvoke = true;
2770+
}
2771+
27662772
if (callInfo.thisTransform != CORINFO_NO_THIS_TRANSFORM)
27672773
{
27682774
assert(pConstrainedToken != NULL);
@@ -3036,7 +3042,15 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
30363042
// before the call.
30373043
// TODO: Add null checking behavior somewhere here!
30383044
}
3039-
AddIns((isPInvoke && !isMarshaledPInvoke) ? INTOP_CALL_PINVOKE : INTOP_CALL);
3045+
if (isDelegateInvoke)
3046+
{
3047+
assert(!isPInvoke && !isMarshaledPInvoke);
3048+
AddIns(INTOP_CALLDELEGATE);
3049+
}
3050+
else
3051+
{
3052+
AddIns((isPInvoke && !isMarshaledPInvoke) ? INTOP_CALL_PINVOKE : INTOP_CALL);
3053+
}
30403054
m_pLastNewIns->data[0] = GetMethodDataItemIndex(callInfo.hMethod);
30413055
if (isPInvoke && !isMarshaledPInvoke)
30423056
{

src/coreclr/interpreter/intops.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ OPDEF(INTOP_LDFLDA, "ldflda", 4, 1, 1, InterpOpInt)
356356

357357
// Calls
358358
OPDEF(INTOP_CALL, "call", 4, 1, 1, InterpOpMethodHandle)
359+
OPDEF(INTOP_CALLDELEGATE, "call.delegate", 4, 1, 1, InterpOpMethodHandle)
359360
OPDEF(INTOP_CALLI, "calli", 5, 1, 2, InterpOpLdPtr)
360361
OPDEF(INTOP_CALLVIRT, "callvirt", 4, 1, 1, InterpOpMethodHandle)
361362
OPDEF(INTOP_CALL_PINVOKE, "call.pinvoke", 6, 1, 1, InterpOpMethodHandle) // inlined (no marshaling wrapper) pinvokes only

src/coreclr/vm/interpexec.cpp

Lines changed: 85 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,41 @@
1414
#ifdef TARGET_WASM
1515
void InvokeCalliStub(PCODE ftn, CallStubHeader *stubHeaderTemplate, int8_t *pArgs, int8_t *pRet);
1616
void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet, PCODE target);
17+
void InvokeDelegateInvokeMethod(MethodDesc *pMDDelegateInvoke, int8_t *pArgs, int8_t *pRet, PCODE target);
1718
#else
1819
#include "callstubgenerator.h"
1920

21+
CallStubHeader *UpdateCallStubForMethod(MethodDesc *pMD)
22+
{
23+
CONTRACTL
24+
{
25+
THROWS;
26+
MODE_ANY;
27+
PRECONDITION(CheckPointer(pMD));
28+
}
29+
CONTRACTL_END
30+
31+
GCX_PREEMP();
32+
33+
CallStubGenerator callStubGenerator;
34+
35+
AllocMemTracker amTracker;
36+
CallStubHeader *header = callStubGenerator.GenerateCallStub(pMD, &amTracker, true /* interpreterToNative */);
37+
38+
if (pMD->SetCallStub(header))
39+
{
40+
amTracker.SuppressRelease();
41+
}
42+
else
43+
{
44+
// We have lost the race for generating the header, use the one that was generated by another thread
45+
// and let the amTracker release the memory of the one we generated.
46+
header = pMD->GetCallStub();
47+
}
48+
49+
return header;
50+
}
51+
2052
void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet, PCODE target)
2153
{
2254
CONTRACTL
@@ -32,22 +64,7 @@ void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet, PCODE ta
3264
CallStubHeader *pHeader = pMD->GetCallStub();
3365
if (pHeader == NULL)
3466
{
35-
CallStubGenerator callStubGenerator;
36-
GCX_PREEMP();
37-
38-
AllocMemTracker amTracker;
39-
pHeader = callStubGenerator.GenerateCallStub(pMD, &amTracker, true /* interpreterToNative */);
40-
41-
if (pMD->SetCallStub(pHeader))
42-
{
43-
amTracker.SuppressRelease();
44-
}
45-
else
46-
{
47-
// We have lost the race for generating the header, use the one that was generated by another thread
48-
// and let the amTracker release the memory of the one we generated.
49-
pHeader = pMD->GetCallStub();
50-
}
67+
pHeader = UpdateCallStubForMethod(pMD);
5168
}
5269

5370
// Interpreter-FIXME: Potential race condition if a single CallStubHeader is reused for multiple targets.
@@ -56,6 +73,34 @@ void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet, PCODE ta
5673
pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize);
5774
}
5875

76+
void InvokeDelegateInvokeMethod(MethodDesc *pMDDelegateInvoke, int8_t *pArgs, int8_t *pRet, PCODE target)
77+
{
78+
CONTRACTL
79+
{
80+
THROWS;
81+
MODE_ANY;
82+
PRECONDITION(CheckPointer(pMDDelegateInvoke));
83+
PRECONDITION(CheckPointer(pArgs));
84+
PRECONDITION(CheckPointer(pRet));
85+
}
86+
CONTRACTL_END
87+
88+
CallStubHeader *stubHeaderTemplate = pMDDelegateInvoke->GetCallStub();
89+
if (stubHeaderTemplate == NULL)
90+
{
91+
stubHeaderTemplate = UpdateCallStubForMethod(pMDDelegateInvoke);
92+
}
93+
94+
// CallStubHeaders encode their destination addresses in the Routines array, so they need to be
95+
// copied to a local buffer before we can actually set their target address.
96+
size_t templateSize = stubHeaderTemplate->GetSize();
97+
uint8_t* actualCallStub = (uint8_t*)alloca(templateSize);
98+
memcpy(actualCallStub, stubHeaderTemplate, templateSize);
99+
CallStubHeader *pHeader = (CallStubHeader*)actualCallStub;
100+
pHeader->SetTarget(target); // The method to call
101+
pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize);
102+
}
103+
59104
void InvokeCalliStub(PCODE ftn, CallStubHeader *stubHeaderTemplate, int8_t *pArgs, int8_t *pRet)
60105
{
61106
CONTRACTL
@@ -84,7 +129,6 @@ CallStubHeader *CreateNativeToInterpreterCallStub(InterpMethod* pInterpMethod)
84129
CallStubHeader *pHeader = VolatileLoadWithoutBarrier(&pInterpMethod->pCallStub);
85130
GCX_PREEMP();
86131

87-
AllocMemTracker amTracker;
88132
if (pHeader == NULL)
89133
{
90134
// Ensure that there is an interpreter thread context instance and thus an interpreter stack
@@ -1881,6 +1925,30 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
18811925
break;
18821926
}
18831927

1928+
case INTOP_CALLDELEGATE:
1929+
{
1930+
returnOffset = ip[1];
1931+
callArgsOffset = ip[2];
1932+
methodSlot = ip[3];
1933+
1934+
// targetMethod holds a pointer to the Invoke method of the delegate, not the final actual target.
1935+
targetMethod = (MethodDesc*)pMethod->pDataItems[methodSlot];
1936+
1937+
ip += 4;
1938+
1939+
DELEGATEREF delegateObj = LOCAL_VAR(callArgsOffset, DELEGATEREF);
1940+
NULL_CHECK(delegateObj);
1941+
PCODE targetAddress = delegateObj->GetMethodPtr();
1942+
OBJECTREF targetMethodObj = delegateObj->GetTarget();
1943+
LOCAL_VAR(callArgsOffset, OBJECTREF) = targetMethodObj;
1944+
1945+
// TODO! Once we are investigating performance here, we may want to optimize this so that
1946+
// delegate calls to interpeted methods don't have to go through the native invoke here, but for
1947+
// now this should work well.
1948+
InvokeDelegateInvokeMethod(targetMethod, stack + callArgsOffset, stack + returnOffset, targetAddress);
1949+
break;
1950+
}
1951+
18841952
case INTOP_CALL:
18851953
{
18861954
returnOffset = ip[1];

src/coreclr/vm/wasm/helpers.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,3 +487,8 @@ void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet, PCODE ta
487487
{
488488
PORTABILITY_ASSERT("Attempted to execute non-interpreter code from interpreter on wasm, this is not yet implemented");
489489
}
490+
491+
void InvokeDelegateInvokeMethod(MethodDesc *pMDDelegateInvoke, int8_t *pArgs, int8_t *pRet, PCODE target)
492+
{
493+
PORTABILITY_ASSERT("Attempted to execute non-interpreter code from interpreter on wasm, this is not yet implemented");
494+
}

0 commit comments

Comments
 (0)