Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/coreclr/interpreter/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,7 @@ int32_t InterpCompiler::GetDataItemIndexForHelperFtn(CorInfoHelpFunc ftn)
size_t data = !direct
? (size_t)indirect | INTERP_INDIRECT_HELPER_TAG
: (size_t)direct;
assert(data);
return GetDataItemIndex((void*)data);
}

Expand Down Expand Up @@ -3710,6 +3711,45 @@ int InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
break;
}

case CEE_LDTOKEN:
{
AddIns(INTOP_LDTOKEN);

CORINFO_RESOLVED_TOKEN resolvedToken;
ResolveToken(getU4LittleEndian(m_ip + 1), CORINFO_TOKENKIND_Ldtoken, &resolvedToken);

CORINFO_CLASS_HANDLE clsHnd = m_compHnd->getTokenTypeAsHandle(&resolvedToken);
PushStackType(StackTypeVT, clsHnd);
m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);

// see jit/importer.cpp CEE_LDTOKEN
CorInfoHelpFunc helper;
if (resolvedToken.hClass)
{
helper = CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE;
m_pLastNewIns->data[0] = GetDataItemIndex(resolvedToken.hClass);
}
else if (resolvedToken.hMethod)
{
helper = CORINFO_HELP_METHODDESC_TO_STUBRUNTIMEMETHOD;
m_pLastNewIns->data[0] = GetDataItemIndex(resolvedToken.hMethod);
}
else if (resolvedToken.hField)
{
helper = CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD;
m_pLastNewIns->data[0] = GetDataItemIndex(resolvedToken.hField);
}
else
{
helper = CORINFO_HELP_FAIL_FAST;
assert(!"Token not resolved or resolved to unexpected type");
}

m_pLastNewIns->data[1] = GetDataItemIndexForHelperFtn(helper);
m_ip += 5;
break;
}

default:
assert(0);
break;
Expand Down
15 changes: 15 additions & 0 deletions src/coreclr/interpreter/interpretershared.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,33 @@

struct InterpMethod
{
#if DEBUG
InterpMethod *self;
#endif
CORINFO_METHOD_HANDLE methodHnd;
int32_t allocaSize;
void** pDataItems;
bool initLocals;

InterpMethod(CORINFO_METHOD_HANDLE methodHnd, int32_t allocaSize, void** pDataItems, bool initLocals)
{
#if DEBUG
this->self = this;
#endif
this->methodHnd = methodHnd;
this->allocaSize = allocaSize;
this->pDataItems = pDataItems;
this->initLocals = initLocals;
}

bool CheckIntegrity()
{
#if DEBUG
return this->self == this;
#else
return true;
#endif
}
};

#endif
2 changes: 2 additions & 0 deletions src/coreclr/interpreter/intops.def
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ OPDEF(INTOP_STELEM_I8, "stelem.i8", 4, 0, 3, InterpOpNoArgs)
OPDEF(INTOP_STELEM_R4, "stelem.r4", 4, 0, 3, InterpOpNoArgs)
OPDEF(INTOP_STELEM_R8, "stelem.r8", 4, 0, 3, InterpOpNoArgs)

OPDEF(INTOP_LDTOKEN, "ldtoken", 4, 1, 0, InterpOpTwoInts) // [token data item] [conversion helper func]

OPDEF(INTOP_MOV_I4_I1, "mov.i4.i1", 3, 1, 1, InterpOpNoArgs)
OPDEF(INTOP_MOV_I4_U1, "mov.i4.u1", 3, 1, 1, InterpOpNoArgs)
OPDEF(INTOP_MOV_I4_I2, "mov.i4.i2", 3, 1, 1, InterpOpNoArgs)
Expand Down
80 changes: 45 additions & 35 deletions src/coreclr/vm/callstubgenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ PCODE GPRegsRoutines[] =
(PCODE)Load_RCX_RDX, // 01
(PCODE)Load_RCX_RDX_R8, // 02
(PCODE)Load_RCX_RDX_R8_R9, // 03
(PCODE)0, // 10
(PCODE)0, // 10
(PCODE)Load_RDX, // 11
(PCODE)Load_RDX_R8, // 12
(PCODE)Load_RDX_R8_R9, // 13
Expand Down Expand Up @@ -75,7 +75,7 @@ PCODE FPRegsRoutines[] =
(PCODE)Load_XMM0_XMM1, // 01
(PCODE)Load_XMM0_XMM1_XMM2, // 02
(PCODE)Load_XMM0_XMM1_XMM2_XMM3, // 03
(PCODE)0, // 10
(PCODE)0, // 10
(PCODE)Load_XMM1, // 11
(PCODE)Load_XMM1_XMM2, // 12
(PCODE)Load_XMM1_XMM2_XMM3, // 13
Expand Down Expand Up @@ -121,7 +121,7 @@ PCODE GPRegsRoutines[] =
(PCODE)Load_RDI_RSI_RDX_RCX, // 03
(PCODE)Load_RDI_RSI_RDX_RCX_R8, // 04
(PCODE)Load_RDI_RSI_RDX_RCX_R8_R9, // 05
(PCODE)0, // 10
(PCODE)0, // 10
(PCODE)Load_RSI, // 11
(PCODE)Load_RSI_RDX, // 12
(PCODE)Load_RSI_RDX_RCX, // 13
Expand Down Expand Up @@ -200,7 +200,7 @@ PCODE FPRegsRoutines[] =
(PCODE)Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5, // 05
(PCODE)Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6, // 06
(PCODE)Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7,// 07
(PCODE)0, // 10
(PCODE)0, // 10
(PCODE)Load_XMM1, // 11
(PCODE)Load_XMM1_XMM2, // 12
(PCODE)Load_XMM1_XMM2_XMM3, // 13
Expand Down Expand Up @@ -321,7 +321,7 @@ PCODE GPRegsRoutines[] =
(PCODE)Load_X0_X1_X2_X3_X4_X5, // 05
(PCODE)Load_X0_X1_X2_X3_X4_X5_X6, // 06
(PCODE)Load_X0_X1_X2_X3_X4_X5_X6_X7, // 07
(PCODE)0, // 10
(PCODE)0, // 10
(PCODE)Load_X1, // 11
(PCODE)Load_X1_X2, // 12
(PCODE)Load_X1_X2_X3, // 13
Expand All @@ -338,7 +338,7 @@ PCODE GPRegsRoutines[] =
(PCODE)Load_X2_X3_X4_X5_X6, // 26
(PCODE)Load_X2_X3_X4_X5_X6_X7, // 27
(PCODE)0, // 30
(PCODE)0, // 31
(PCODE)0, // 31
(PCODE)0, // 32
(PCODE)Load_X3, // 33
(PCODE)Load_X3_X4, // 34
Expand Down Expand Up @@ -438,7 +438,7 @@ PCODE FPRegsRoutines[] =
(PCODE)Load_D0_D1_D2_D3_D4_D5, // 05
(PCODE)Load_D0_D1_D2_D3_D4_D5_D6, // 06
(PCODE)Load_D0_D1_D2_D3_D4_D5_D6_D7, // 07
(PCODE)0, // 10
(PCODE)0, // 10
(PCODE)Load_D1, // 11
(PCODE)Load_D1_D2, // 12
(PCODE)Load_D1_D2_D3, // 13
Expand All @@ -455,7 +455,7 @@ PCODE FPRegsRoutines[] =
(PCODE)Load_D2_D3_D4_D5_D6, // 26
(PCODE)Load_D2_D3_D4_D5_D6_D7, // 27
(PCODE)0, // 30
(PCODE)0, // 31
(PCODE)0, // 31
(PCODE)0, // 32
(PCODE)Load_D3, // 33
(PCODE)Load_D3_D4, // 34
Expand Down Expand Up @@ -590,7 +590,7 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra
ArgLocDesc argLocDesc;
argIt.GetArgLoc(ofs, &argLocDesc);

#ifdef UNIX_AMD64_ABI
#ifdef UNIX_AMD64_ABI
if (argIt.GetArgLocDescForStructInRegs() != NULL)
{
TypeHandle argTypeHandle;
Expand All @@ -604,33 +604,43 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra
{
ArgLocDesc argLocDescEightByte = {};
SystemVClassificationType eightByteType = pEEClass->GetEightByteClassification(i);
if (eightByteType == SystemVClassificationTypeInteger)
{
if (argLocDesc.m_cGenReg != 0)
{
argLocDescEightByte.m_cGenReg = 1;
argLocDescEightByte.m_idxGenReg = argLocDesc.m_idxGenReg++;
}
else
{
argLocDescEightByte.m_byteStackSize = 8;
argLocDescEightByte.m_byteStackIndex = argLocDesc.m_byteStackIndex;
argLocDesc.m_byteStackIndex += 8;
}
}
else if (eightByteType == SystemVClassificationTypeSSE)
switch (eightByteType)
{
if (argLocDesc.m_cFloatReg != 0)
case SystemVClassificationTypeInteger:
case SystemVClassificationTypeIntegerReference:
case SystemVClassificationTypeIntegerByRef:
{
argLocDescEightByte.m_cFloatReg = 1;
argLocDescEightByte.m_idxFloatReg = argLocDesc.m_idxFloatReg++;
if (argLocDesc.m_cGenReg != 0)
{
argLocDescEightByte.m_cGenReg = 1;
argLocDescEightByte.m_idxGenReg = argLocDesc.m_idxGenReg++;
}
else
{
argLocDescEightByte.m_byteStackSize = 8;
argLocDescEightByte.m_byteStackIndex = argLocDesc.m_byteStackIndex;
argLocDesc.m_byteStackIndex += 8;
}
break;
}
else
case SystemVClassificationTypeSSE:
{
argLocDescEightByte.m_byteStackSize = 8;
argLocDescEightByte.m_byteStackIndex = argLocDesc.m_byteStackIndex;
argLocDesc.m_byteStackIndex += 8;
if (argLocDesc.m_cFloatReg != 0)
{
argLocDescEightByte.m_cFloatReg = 1;
argLocDescEightByte.m_idxFloatReg = argLocDesc.m_idxFloatReg++;
}
else
{
argLocDescEightByte.m_byteStackSize = 8;
argLocDescEightByte.m_byteStackIndex = argLocDesc.m_byteStackIndex;
argLocDesc.m_byteStackIndex += 8;
}
break;
}
default:
assert(!"Unhandled systemv classification for argument in GenerateCallStub");
break;
}
ProcessArgument(argIt, argLocDescEightByte, pRoutines);
}
Expand Down Expand Up @@ -878,7 +888,7 @@ void CallStubGenerator::ProcessArgument(ArgIterator& argIt, ArgLocDesc& argLocDe
}

if (argLocDesc.m_cGenReg != 0)
{
{
if (m_r1 == NoRange) // No active range yet
{
// Start a new range
Expand Down Expand Up @@ -907,7 +917,7 @@ void CallStubGenerator::ProcessArgument(ArgIterator& argIt, ArgLocDesc& argLocDe
// Start a new range
m_x1 = argLocDesc.m_idxFloatReg;
m_x2 = m_x1 + argLocDesc.m_cFloatReg - 1;
}
}
else if (argLocDesc.m_idxFloatReg == m_x2 + 1)
{
// Extend an existing range
Expand All @@ -929,7 +939,7 @@ void CallStubGenerator::ProcessArgument(ArgIterator& argIt, ArgLocDesc& argLocDe
// Start a new range
m_s1 = argLocDesc.m_byteStackIndex;
m_s2 = m_s1 + argLocDesc.m_byteStackSize - 1;
}
}
else if ((argLocDesc.m_byteStackIndex == m_s2 + 1) && (argLocDesc.m_byteStackSize >= 8))
{
// Extend an existing range, but only if the argument is at least pointer size large.
Expand Down Expand Up @@ -987,4 +997,4 @@ void CallStubGenerator::ProcessArgument(ArgIterator& argIt, ArgLocDesc& argLocDe
#endif // UNIX_AMD64_ABI
}

#endif // FEATURE_INTERPRETER
#endif // FEATURE_INTERPRETER
20 changes: 20 additions & 0 deletions src/coreclr/vm/interpexec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
int8_t *stack;

InterpMethod *pMethod = *(InterpMethod**)pFrame->startIp;
assert(pMethod->CheckIntegrity());

pThreadContext->pStackPointer = pFrame->pStack + pMethod->allocaSize;
ip = pFrame->startIp + sizeof(InterpMethod*) / sizeof(int32_t);
stack = pFrame->pStack;
Expand Down Expand Up @@ -1172,6 +1174,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr

// Set execution state for the new frame
pMethod = *(InterpMethod**)pFrame->startIp;
assert(pMethod->CheckIntegrity());
stack = pFrame->pStack;
ip = pFrame->startIp + sizeof(InterpMethod*) / sizeof(int32_t);
pThreadContext->pStackPointer = stack + pMethod->allocaSize;
Expand Down Expand Up @@ -1423,6 +1426,21 @@ do { \
STELEM(double, double);
break;
}
case INTOP_LDTOKEN:
Copy link

Copilot AI May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review the ldtoken opcode implementation to ensure that the helper function resolution using the INTERP_INDIRECT_HELPER_TAG flag works correctly for all token types.

Copilot uses AI. Check for mistakes.
{
int dreg = ip[1];
void *nativeHandle = pMethod->pDataItems[ip[2]];
size_t helperDirectOrIndirect = (size_t)pMethod->pDataItems[ip[3]];
HELPER_FTN_PP helper = nullptr;
if (helperDirectOrIndirect & INTERP_INDIRECT_HELPER_TAG)
helper = *(HELPER_FTN_PP *)(helperDirectOrIndirect & ~INTERP_INDIRECT_HELPER_TAG);
else
helper = (HELPER_FTN_PP)helperDirectOrIndirect;
void *managedHandle = helper(nativeHandle);
LOCAL_VAR(dreg, void*) = managedHandle;
ip += 4;
break;
}
case INTOP_FAILFAST:
assert(0);
break;
Expand Down Expand Up @@ -1456,6 +1474,7 @@ do { \

stack = pFrame->pStack;
pMethod = *(InterpMethod**)pFrame->startIp;
assert(pMethod->CheckIntegrity());
pThreadContext->pStackPointer = pFrame->pStack + pMethod->allocaSize;
goto MAIN_LOOP;
}
Expand All @@ -1472,6 +1491,7 @@ do { \
ip = pFrame->ip;
stack = pFrame->pStack;
pMethod = *(InterpMethod**)pFrame->startIp;
assert(pMethod->CheckIntegrity());
pFrame->ip = NULL;

pThreadContext->pStackPointer = pFrame->pStack + pMethod->allocaSize;
Expand Down
34 changes: 34 additions & 0 deletions src/tests/JIT/interpreter/Interpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,13 @@ public static void RunInterpreterTests()
if (!TestSizeof())
Environment.FailFast(null);

if (!TestLdtoken())
Environment.FailFast(null);
/*
if (!TestMdArray())
Environment.FailFast(null);
*/

System.GC.Collect();
}

Expand Down Expand Up @@ -959,4 +966,31 @@ public static unsafe bool TestSizeof()
return false;
return true;
}

public static int LdtokenField = 7;

public static bool TestLdtoken()
{
Type t = typeof(int);
int i = 42;
if (!ReferenceEquals(t, i.GetType()))
return false;
// These generate field and method ldtoken opcodes, but the test fails because we are missing castclass and possibly also generics
/*
System.Linq.Expressions.Expression<Func<int>> f = () => LdtokenField;
System.Linq.Expressions.Expression<Action> a = () => TestLdtoken();
*/
return true;
}

public static bool TestMdArray()
{
// FIXME: This generates roughly:
// newobj int[,].ctor
// ldtoken int[,]
// call System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray
// The newobj currently fails because int[,].ctor isn't a real method, the interp needs to use getCallInfo to determine how to invoke it
int[,] a = {{1, 2}, {3, 4}};
return a[0, 1] == 2;
}
}
Loading