Skip to content

Commit dc643f2

Browse files
Status and incomplete fat Precode pointer. (#2780)
* Update status. Incomplete fat pointer implementation using Precode.
1 parent a8b3ea5 commit dc643f2

12 files changed

+286
-52
lines changed

experiment_status.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
# Status 15-NOV-2024
2+
3+
## Aaron's CoreCLR learnings
4+
5+
- `PCODE` manifests itself from a myriad of places.
6+
- The `MethodDesc::DoPrestub()` entry point should be referenced for many of the APIs that can produce `PCODE`. (for example, `GetStubForInteropMethod()`).
7+
- The ILStubResolver deletes the associated IL post JIT - `ILStubResolver::ClearCompileTimeState`.
8+
- This means that when an IL stub will be the interpreted, clean-up of generated IL needs to be avoided.
9+
- If a future CoreCLR interpreter uses a converted byte code approach, then getting rid of the IL may not be an issue.
10+
- See `CEEInfo::getMethodInfo()` for how IL stubs are impacted, see `MethodDesc::IsWrapperStub()`.
11+
- Created a new `Precode` type to represent the interpreter entry point.
12+
- See `MethodDescCallSite` for the common ways CoreCLR calls from C++ into managed code. Specifically, `CallTargetWorker()` is of note.
13+
- The `Precode` approach seems to be a valid way to loop into the execution engine without too much disruption.
14+
- Passing around stub context to P/Invokes needs to be considered in the interpreter case.
15+
- Disabling P/Invoke stub sharing would avoid this need. ReadyToRun is prior art for disabling stub sharing.
16+
117
# Status 01-NOV-2024
218

319
Audit of W^X locations in CoreCLR has yielded locations that need investigation. The section below links to all locations where generated code is manipulated at run-time and indicates a scenario that needs consideration. Jan Vorlicek did a quick check on the list and added a high level thought on relevance to the CoreCLR interpreter track.

src/coreclr/vm/amd64/CallDescrWorkerAMD64.asm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ StackCopyLoop: ; copy the arguments to stack top-down t
4242

4343
mov rax, [rbx + CallDescrData__dwRegTypeMap] ; save the reg (arg) type map
4444

45+
; Store stub context
46+
cmp QWORD PTR [rbx + CallDescrData__pStubContextMD], 0
47+
jz NoStubContext
48+
mov r10, [rbx + CallDescrData__pStubContextMD]
49+
NoStubContext:
4550
mov rcx, 0[rsp] ; load first four argument registers
4651
movss xmm0, real4 ptr 0[rsp] ;
4752
cmp al, ASM_ELEMENT_TYPE_R8 ;

src/coreclr/vm/amd64/asmconstants.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,12 +477,14 @@ ASMCONSTANTS_C_ASSERT(OFFSETOF__InlinedCallFrame__m_pThread
477477
#define CallDescrData__pFloatArgumentRegisters 0x18
478478
#define CallDescrData__fpReturnSize 0x20
479479
#define CallDescrData__pTarget 0x28
480-
#define CallDescrData__returnValue 0x30
480+
#define CallDescrData__pStubContextMD 0x30
481+
#define CallDescrData__returnValue 0x38
481482
#else
482483
#define CallDescrData__dwRegTypeMap 0x10
483484
#define CallDescrData__fpReturnSize 0x18
484485
#define CallDescrData__pTarget 0x20
485-
#define CallDescrData__returnValue 0x28
486+
#define CallDescrData__pStubContextMD 0x28
487+
#define CallDescrData__returnValue 0x30
486488
#endif
487489

488490
ASMCONSTANTS_C_ASSERT(CallDescrData__pSrc == offsetof(CallDescrData, pSrc))
@@ -495,6 +497,7 @@ ASMCONSTANTS_C_ASSERT(CallDescrData__dwRegTypeMap == offsetof(CallDescrD
495497
#endif
496498
ASMCONSTANTS_C_ASSERT(CallDescrData__fpReturnSize == offsetof(CallDescrData, fpReturnSize))
497499
ASMCONSTANTS_C_ASSERT(CallDescrData__pTarget == offsetof(CallDescrData, pTarget))
500+
ASMCONSTANTS_C_ASSERT(CallDescrData__pStubContextMD == offsetof(CallDescrData, pStubContextMD))
498501
ASMCONSTANTS_C_ASSERT(CallDescrData__returnValue == offsetof(CallDescrData, returnValue))
499502

500503
#ifdef UNIX_AMD64_ABI

src/coreclr/vm/callhelpers.cpp

Lines changed: 65 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ void AssertMulticoreJitAllowedModule(PCODE pTarget)
3333

3434
#endif
3535

36+
#ifdef FEATURE_INTERPRETER
37+
extern void InterpretCallTarget(PCODE pCallTarget, const ARG_SLOT* pArguments, ARG_SLOT* pReturnValue, int cbReturnValue);
38+
#endif // FEATURE_INTERPRETER
39+
3640
// For X86, INSTALL_COMPLUS_EXCEPTION_HANDLER grants us sufficient protection to call into
3741
// managed code.
3842
//
@@ -209,7 +213,7 @@ void * DispatchCallSimple(
209213
g_pDebugInterface->TraceCall((const BYTE *)pTargetAddress);
210214
#endif // DEBUGGING_SUPPORTED
211215

212-
CallDescrData callDescrData;
216+
CallDescrData callDescrData{};
213217

214218
#ifdef CALLDESCR_ARGREGS
215219
callDescrData.pSrc = pSrc + NUM_ARGUMENT_REGISTERS;
@@ -234,18 +238,22 @@ void * DispatchCallSimple(
234238
callDescrData.fpReturnSize = 0;
235239
callDescrData.pTarget = pTargetAddress;
236240

237-
if ((dwDispatchCallSimpleFlags & DispatchCallSimple_CatchHandlerFoundNotification) != 0)
238-
{
239-
DispatchCallDebuggerWrapper(
240-
&callDescrData,
241-
dwDispatchCallSimpleFlags & DispatchCallSimple_CriticalCall);
242-
}
243-
else
244-
{
245-
CallDescrWorkerWithHandler(&callDescrData, dwDispatchCallSimpleFlags & DispatchCallSimple_CriticalCall);
246-
}
247-
248-
return *(void **)(&callDescrData.returnValue);
241+
ARG_SLOT result;
242+
InterpretCallTarget(pTargetAddress, pSrc, &result, sizeof(result));
243+
return (void*)result;
244+
245+
//if ((dwDispatchCallSimpleFlags & DispatchCallSimple_CatchHandlerFoundNotification) != 0)
246+
//{
247+
// DispatchCallDebuggerWrapper(
248+
// &callDescrData,
249+
// dwDispatchCallSimpleFlags & DispatchCallSimple_CriticalCall);
250+
//}
251+
//else
252+
//{
253+
// CallDescrWorkerWithHandler(&callDescrData, dwDispatchCallSimpleFlags & DispatchCallSimple_CriticalCall);
254+
//}
255+
256+
//return *(void **)(&callDescrData.returnValue);
249257
}
250258

251259
#ifdef CALLDESCR_REGTYPEMAP
@@ -553,7 +561,7 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT *
553561

554562
} // END GCX_FORBID & ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE
555563

556-
CallDescrData callDescrData;
564+
CallDescrData callDescrData{};
557565

558566
callDescrData.pSrc = pTransitionBlock + sizeof(TransitionBlock);
559567
_ASSERTE((nStackBytes % TARGET_POINTER_SIZE) == 0);
@@ -574,49 +582,65 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT *
574582
callDescrData.pTarget = m_pCallTarget;
575583

576584
#ifdef FEATURE_INTERPRETER
577-
if (transitionToPreemptive)
585+
callDescrData.pStubContextMD = m_pMD;
586+
if (InterpreterPrecode::IsInstance(m_pCallTarget))
578587
{
579-
GCPreemp transitionIfILStub(transitionToPreemptive);
580-
CallDescrWorkerInternal(&callDescrData);
588+
if (transitionToPreemptive)
589+
{
590+
GCPreemp transitionIfILStub(transitionToPreemptive);
591+
InterpretCallTarget(m_pCallTarget, pArguments, pReturnValue, cbReturnValue);
592+
}
593+
else
594+
{
595+
InterpretCallTarget(m_pCallTarget, pArguments, pReturnValue, cbReturnValue);
596+
}
581597
}
582598
else
583599
#endif // FEATURE_INTERPRETER
584600
{
585-
CallDescrWorkerWithHandler(&callDescrData);
586-
}
587-
588-
#ifdef FEATURE_HFA
589-
if (pvRetBuff != NULL)
590-
{
591-
memcpyNoGCRefs(pvRetBuff, &callDescrData.returnValue, sizeof(callDescrData.returnValue));
592-
}
593-
#endif // FEATURE_HFA
594-
595-
if (pReturnValue != NULL)
596-
{
597-
_ASSERTE((DWORD)cbReturnValue <= sizeof(callDescrData.returnValue));
598-
#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64)
599-
if (callDescrData.fpReturnSize != FpStruct::UseIntCallConv)
601+
if (transitionToPreemptive)
600602
{
601-
FpStructInRegistersInfo info = m_argIt.GetReturnFpStructInRegistersInfo();
602-
CopyReturnedFpStructFromRegisters(pReturnValue, callDescrData.returnValue, info, false);
603+
GCPreemp transitionIfILStub(transitionToPreemptive);
604+
CallDescrWorkerInternal(&callDescrData);
603605
}
604606
else
605-
#endif // defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64)
606607
{
607-
memcpyNoGCRefs(pReturnValue, &callDescrData.returnValue, cbReturnValue);
608+
CallDescrWorkerWithHandler(&callDescrData);
608609
}
609610

610-
#if !defined(HOST_64BIT) && BIGENDIAN
611+
#ifdef FEATURE_HFA
612+
if (pvRetBuff != NULL)
611613
{
612-
GCX_FORBID();
614+
memcpyNoGCRefs(pvRetBuff, &callDescrData.returnValue, sizeof(callDescrData.returnValue));
615+
}
616+
#endif // FEATURE_HFA
613617

614-
if (!m_methodSig.Is64BitReturn())
618+
if (pReturnValue != NULL)
619+
{
620+
_ASSERTE((DWORD)cbReturnValue <= sizeof(callDescrData.returnValue));
621+
#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64)
622+
if (callDescrData.fpReturnSize != FpStruct::UseIntCallConv)
615623
{
616-
pReturnValue[0] >>= 32;
624+
FpStructInRegistersInfo info = m_argIt.GetReturnFpStructInRegistersInfo();
625+
CopyReturnedFpStructFromRegisters(pReturnValue, callDescrData.returnValue, info, false);
626+
}
627+
else
628+
#endif // defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64)
629+
{
630+
memcpyNoGCRefs(pReturnValue, &callDescrData.returnValue, cbReturnValue);
631+
}
632+
633+
#if !defined(HOST_64BIT) && BIGENDIAN
634+
{
635+
GCX_FORBID();
636+
637+
if (!m_methodSig.Is64BitReturn())
638+
{
639+
pReturnValue[0] >>= 32;
640+
}
617641
}
618-
}
619642
#endif // !defined(HOST_64BIT) && BIGENDIAN
643+
}
620644
}
621645
}
622646

src/coreclr/vm/callhelpers.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ struct CallDescrData
2929
UINT32 fpReturnSize;
3030
PCODE pTarget;
3131

32+
#ifdef FEATURE_INTERPRETER
33+
LPVOID pStubContextMD;
34+
#endif // FEATURE_INTERPRETER
35+
3236
#ifdef CALLDESCR_RETBUFFARGREG
3337
// Pointer to return buffer arg location
3438
UINT64* pRetBuffArg;

src/coreclr/vm/interpreter.cpp

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "openum.h"
1414
#include "fcall.h"
1515
#include "frames.h"
16+
#include "dllimport.h"
1617
#include "gcheaputilities.h"
1718
#include <float.h>
1819
#include "jitinterface.h"
@@ -10050,7 +10051,12 @@ void Interpreter::DoCallWork(bool virtualCall, void* thisArg, CORINFO_RESOLVED_T
1005010051
}
1005110052

1005210053
// Compile the target in advance of calling.
10053-
if (exactMethToCall->IsPointingToPrestub())
10054+
if (exactMethToCall->IsNDirect())
10055+
{
10056+
GCX_PREEMP();
10057+
target = GetStubForInteropMethod(exactMethToCall);
10058+
}
10059+
else if (exactMethToCall->IsPointingToPrestub())
1005410060
{
1005510061
MethodTable* dispatchingMT = NULL;
1005610062
if (exactMethToCall->IsVtableMethod())
@@ -10087,10 +10093,19 @@ void Interpreter::DoCallWork(bool virtualCall, void* thisArg, CORINFO_RESOLVED_T
1008710093
bool b = CycleTimer::GetThreadCyclesS(&startCycles); _ASSERTE(b);
1008810094
#endif // INTERP_ILCYCLE_PROFILE
1008910095

10096+
// If the current method being interpreted is an IL stub, we're calling native code, so
10097+
// change the GC mode. (We'll only do this at the call if the calling convention turns out
10098+
// to be a managed calling convention.)
10099+
bool transitionToPreemptive = false;
10100+
{
10101+
//GCX_PREEMP();
10102+
//transitionToPreemptive = exactMethToCall != NULL && exactMethToCall->IsNDirect();
10103+
}
10104+
1009010105
#if defined(UNIX_AMD64_ABI) || defined(TARGET_RISCV64)
10091-
mdcs.CallTargetWorker(args, retVals, HasTwoSlotBuf ? 16: 8);
10106+
mdcs.CallTargetWorker(args, retVals, HasTwoSlotBuf ? 16: 8, transitionToPreemptive);
1009210107
#else
10093-
mdcs.CallTargetWorker(args, retVals, 8);
10108+
mdcs.CallTargetWorker(args, retVals, 8, transitionToPreemptive);
1009410109
#endif
1009510110

1009610111
if (pCscd != NULL)
@@ -10534,12 +10549,30 @@ void Interpreter::CallI()
1053410549
PCODE ftnPtr = OpStackGet<PCODE>(ftnInd);
1053510550

1053610551
{
10537-
MethodDesc* methToCall;
10552+
MethodDesc* methToCall = NULL;
1053810553
// If we're interpreting the target, simply call it directly.
10539-
if ((methToCall = InterpretationStubToMethodInfo((PCODE)ftnPtr)) != NULL)
10554+
if (InterpreterPrecode::IsInstance(ftnPtr)
10555+
|| (methToCall = InterpretationStubToMethodInfo((PCODE)ftnPtr)) != NULL)
1054010556
{
10541-
InterpreterMethodInfo* methInfo = MethodHandleToInterpreterMethInfoPtr(CORINFO_METHOD_HANDLE(methToCall));
10557+
InterpreterMethodInfo* methInfo = methToCall != NULL
10558+
? MethodHandleToInterpreterMethInfoPtr(CORINFO_METHOD_HANDLE(methToCall))
10559+
: NULL;
10560+
10561+
// Allocate a new jitInfo and also a new InterpreterMethodInfo.
10562+
if (methInfo == NULL)
10563+
{
10564+
methToCall = InterpreterPrecode::Get(ftnPtr)->GetMethodDesc();
10565+
CEEInfo* jitInfo = new CEEInfo(methToCall, true);
10566+
10567+
CORINFO_METHOD_INFO methInfoLocal;
10568+
10569+
GCX_PREEMP();
10570+
jitInfo->getMethodInfo(CORINFO_METHOD_HANDLE(methToCall), &methInfoLocal, NULL);
10571+
GenerateInterpreterStub(jitInfo, &methInfoLocal, NULL, 0, &methInfo, true);
10572+
delete jitInfo;
10573+
}
1054210574
_ASSERTE(methInfo != NULL);
10575+
1054310576
#if INTERP_ILCYCLE_PROFILE
1054410577
bool b = CycleTimer::GetThreadCyclesS(&startCycles); _ASSERTE(b);
1054510578
#endif // INTERP_ILCYCLE_PROFILE
@@ -12869,4 +12902,32 @@ void Interpreter::PrintILProfile(Interpreter::InstrExecRecord *recs, unsigned in
1286912902
}
1287012903
#endif // INTERP_ILINSTR_PROFILE
1287112904

12905+
void InterpretCallTarget(PCODE pCallTarget, const ARG_SLOT* pArguments, ARG_SLOT* pReturnValue, int cbReturnValue)
12906+
{
12907+
_ASSERTE(InterpreterPrecode::IsInstance(pCallTarget));
12908+
12909+
InterpreterPrecode* interpPrecode = InterpreterPrecode::Get(pCallTarget);
12910+
12911+
MethodDesc* pMD = interpPrecode->GetMethodDesc();
12912+
12913+
InterpreterMethodInfo* methInfo;
12914+
CORINFO_METHOD_INFO jitMethInfo;
12915+
{
12916+
CEEInfo* jitInfo = new CEEInfo(pMD, true);
12917+
12918+
GCX_PREEMP();
12919+
if (!jitInfo->getMethodInfo(CORINFO_METHOD_HANDLE(pMD), &jitMethInfo, NULL))
12920+
{
12921+
_ASSERTE(false && "getMethodInfo failure");
12922+
}
12923+
12924+
Interpreter::GenerateInterpreterStub(jitInfo, &jitMethInfo, NULL, 0, &methInfo, true);
12925+
delete jitInfo;
12926+
}
12927+
12928+
ARG_SLOT retVal = Interpreter::InterpretMethodBody(methInfo, true, reinterpret_cast<BYTE*>(const_cast<ARG_SLOT*>(pArguments)), NULL);
12929+
if (pReturnValue != NULL)
12930+
*pReturnValue = retVal;
12931+
}
12932+
1287212933
#endif // FEATURE_INTERPRETER

src/coreclr/vm/interpreter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,9 +746,11 @@ class Interpreter
746746
friend float F_CALL_CONV InterpretMethodFloat(InterpreterMethodInfo* methInfo, BYTE* ilArgs, void* stubContext);
747747
friend double F_CALL_CONV InterpretMethodDouble(InterpreterMethodInfo* methInfo, BYTE* ilArgs, void* stubContext);
748748

749+
public:
749750
// This will be inlined into the bodies of the methods above
750751
static inline ARG_SLOT InterpretMethodBody(InterpreterMethodInfo* interpMethInfo, bool directCall, BYTE* ilArgs, void* stubContext);
751752

753+
private:
752754
// The local frame size of the method being interpreted.
753755
static size_t GetFrameSize(InterpreterMethodInfo* interpMethInfo);
754756

src/coreclr/vm/jitinterface.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7697,7 +7697,7 @@ CEEInfo::getMethodInfo(
76977697
getMethodInfoHelper(cxt, methInfo, context);
76987698
result = true;
76997699
}
7700-
else if (!ftn->IsWrapperStub() && ftn->HasILHeader())
7700+
else if (ftn->HasILHeader()) // !ftn->IsWrapperStub() &&
77017701
{
77027702
COR_ILMETHOD_DECODER header(ftn->GetILHeader(), ftn->GetMDImport(), NULL);
77037703
cxt.Header = &header;

src/coreclr/vm/method.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2658,6 +2658,14 @@ MethodDesc* MethodDesc::GetMethodDescFromStubAddr(PCODE addr, BOOL fSpeculative
26582658

26592659
MethodDesc* pMD = NULL;
26602660

2661+
#ifdef FEATURE_INTERPRETER
2662+
if (InterpreterPrecode::IsInstance(addr))
2663+
{
2664+
pMD = InterpreterPrecode::Get(addr)->GetMethodDesc();
2665+
RETURN(pMD);
2666+
}
2667+
#endif // FEATURE_INTERPRETER
2668+
26612669
// Otherwise this must be some kind of precode
26622670
//
26632671
PTR_Precode pPrecode = Precode::GetPrecodeFromEntryPoint(addr, fSpeculative);
@@ -3879,7 +3887,9 @@ PrecodeType MethodDesc::GetPrecodeType()
38793887
LIMITED_METHOD_CONTRACT;
38803888

38813889
PrecodeType precodeType = PRECODE_INVALID;
3882-
3890+
#ifdef FEATURE_INTERPRETER
3891+
precodeType = PRECODE_INTERPRETER;
3892+
#else // !FEATURE_INTERPRETER
38833893
#ifdef HAS_FIXUP_PRECODE
38843894
if (!RequiresMethodDescCallingConvention())
38853895
{
@@ -3891,6 +3901,7 @@ PrecodeType MethodDesc::GetPrecodeType()
38913901
{
38923902
precodeType = PRECODE_STUB;
38933903
}
3904+
#endif // FEATURE_INTERPRETER
38943905

38953906
return precodeType;
38963907
}

0 commit comments

Comments
 (0)