Skip to content

Commit e163124

Browse files
authored
Implement ldtoken in the interpreter (#115723)
* Implements a new LDTOKEN interpreter opcode with support for method/field/type tokens. Only type tokens have been tested, it's not currently possible to test the other two. * Adds an integrity check header to InterpMethod because the behavior if we jump to something that isn't a valid InterpMethod is very messy and harder to debug. * Fixes a bug in the CallStub generator for sysv (we weren't handling certain argument types.)
1 parent 77de6e6 commit e163124

File tree

6 files changed

+156
-35
lines changed

6 files changed

+156
-35
lines changed

src/coreclr/interpreter/compiler.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,6 +1736,7 @@ int32_t InterpCompiler::GetDataItemIndexForHelperFtn(CorInfoHelpFunc ftn)
17361736
size_t data = !direct
17371737
? (size_t)indirect | INTERP_INDIRECT_HELPER_TAG
17381738
: (size_t)direct;
1739+
assert(data);
17391740
return GetDataItemIndex((void*)data);
17401741
}
17411742

@@ -3710,6 +3711,45 @@ int InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
37103711
break;
37113712
}
37123713

3714+
case CEE_LDTOKEN:
3715+
{
3716+
AddIns(INTOP_LDTOKEN);
3717+
3718+
CORINFO_RESOLVED_TOKEN resolvedToken;
3719+
ResolveToken(getU4LittleEndian(m_ip + 1), CORINFO_TOKENKIND_Ldtoken, &resolvedToken);
3720+
3721+
CORINFO_CLASS_HANDLE clsHnd = m_compHnd->getTokenTypeAsHandle(&resolvedToken);
3722+
PushStackType(StackTypeVT, clsHnd);
3723+
m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);
3724+
3725+
// see jit/importer.cpp CEE_LDTOKEN
3726+
CorInfoHelpFunc helper;
3727+
if (resolvedToken.hClass)
3728+
{
3729+
helper = CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE;
3730+
m_pLastNewIns->data[0] = GetDataItemIndex(resolvedToken.hClass);
3731+
}
3732+
else if (resolvedToken.hMethod)
3733+
{
3734+
helper = CORINFO_HELP_METHODDESC_TO_STUBRUNTIMEMETHOD;
3735+
m_pLastNewIns->data[0] = GetDataItemIndex(resolvedToken.hMethod);
3736+
}
3737+
else if (resolvedToken.hField)
3738+
{
3739+
helper = CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD;
3740+
m_pLastNewIns->data[0] = GetDataItemIndex(resolvedToken.hField);
3741+
}
3742+
else
3743+
{
3744+
helper = CORINFO_HELP_FAIL_FAST;
3745+
assert(!"Token not resolved or resolved to unexpected type");
3746+
}
3747+
3748+
m_pLastNewIns->data[1] = GetDataItemIndexForHelperFtn(helper);
3749+
m_ip += 5;
3750+
break;
3751+
}
3752+
37133753
default:
37143754
assert(0);
37153755
break;

src/coreclr/interpreter/interpretershared.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,33 @@
1616

1717
struct InterpMethod
1818
{
19+
#if DEBUG
20+
InterpMethod *self;
21+
#endif
1922
CORINFO_METHOD_HANDLE methodHnd;
2023
int32_t allocaSize;
2124
void** pDataItems;
2225
bool initLocals;
2326

2427
InterpMethod(CORINFO_METHOD_HANDLE methodHnd, int32_t allocaSize, void** pDataItems, bool initLocals)
2528
{
29+
#if DEBUG
30+
this->self = this;
31+
#endif
2632
this->methodHnd = methodHnd;
2733
this->allocaSize = allocaSize;
2834
this->pDataItems = pDataItems;
2935
this->initLocals = initLocals;
3036
}
37+
38+
bool CheckIntegrity()
39+
{
40+
#if DEBUG
41+
return this->self == this;
42+
#else
43+
return true;
44+
#endif
45+
}
3146
};
3247

3348
#endif

src/coreclr/interpreter/intops.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ OPDEF(INTOP_STELEM_I8, "stelem.i8", 4, 0, 3, InterpOpNoArgs)
4242
OPDEF(INTOP_STELEM_R4, "stelem.r4", 4, 0, 3, InterpOpNoArgs)
4343
OPDEF(INTOP_STELEM_R8, "stelem.r8", 4, 0, 3, InterpOpNoArgs)
4444

45+
OPDEF(INTOP_LDTOKEN, "ldtoken", 4, 1, 0, InterpOpTwoInts) // [token data item] [conversion helper func]
46+
4547
OPDEF(INTOP_MOV_I4_I1, "mov.i4.i1", 3, 1, 1, InterpOpNoArgs)
4648
OPDEF(INTOP_MOV_I4_U1, "mov.i4.u1", 3, 1, 1, InterpOpNoArgs)
4749
OPDEF(INTOP_MOV_I4_I2, "mov.i4.i2", 3, 1, 1, InterpOpNoArgs)

src/coreclr/vm/callstubgenerator.cpp

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ PCODE GPRegsRoutines[] =
4747
(PCODE)Load_RCX_RDX, // 01
4848
(PCODE)Load_RCX_RDX_R8, // 02
4949
(PCODE)Load_RCX_RDX_R8_R9, // 03
50-
(PCODE)0, // 10
50+
(PCODE)0, // 10
5151
(PCODE)Load_RDX, // 11
5252
(PCODE)Load_RDX_R8, // 12
5353
(PCODE)Load_RDX_R8_R9, // 13
@@ -75,7 +75,7 @@ PCODE FPRegsRoutines[] =
7575
(PCODE)Load_XMM0_XMM1, // 01
7676
(PCODE)Load_XMM0_XMM1_XMM2, // 02
7777
(PCODE)Load_XMM0_XMM1_XMM2_XMM3, // 03
78-
(PCODE)0, // 10
78+
(PCODE)0, // 10
7979
(PCODE)Load_XMM1, // 11
8080
(PCODE)Load_XMM1_XMM2, // 12
8181
(PCODE)Load_XMM1_XMM2_XMM3, // 13
@@ -121,7 +121,7 @@ PCODE GPRegsRoutines[] =
121121
(PCODE)Load_RDI_RSI_RDX_RCX, // 03
122122
(PCODE)Load_RDI_RSI_RDX_RCX_R8, // 04
123123
(PCODE)Load_RDI_RSI_RDX_RCX_R8_R9, // 05
124-
(PCODE)0, // 10
124+
(PCODE)0, // 10
125125
(PCODE)Load_RSI, // 11
126126
(PCODE)Load_RSI_RDX, // 12
127127
(PCODE)Load_RSI_RDX_RCX, // 13
@@ -200,7 +200,7 @@ PCODE FPRegsRoutines[] =
200200
(PCODE)Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5, // 05
201201
(PCODE)Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6, // 06
202202
(PCODE)Load_XMM0_XMM1_XMM2_XMM3_XMM4_XMM5_XMM6_XMM7,// 07
203-
(PCODE)0, // 10
203+
(PCODE)0, // 10
204204
(PCODE)Load_XMM1, // 11
205205
(PCODE)Load_XMM1_XMM2, // 12
206206
(PCODE)Load_XMM1_XMM2_XMM3, // 13
@@ -321,7 +321,7 @@ PCODE GPRegsRoutines[] =
321321
(PCODE)Load_X0_X1_X2_X3_X4_X5, // 05
322322
(PCODE)Load_X0_X1_X2_X3_X4_X5_X6, // 06
323323
(PCODE)Load_X0_X1_X2_X3_X4_X5_X6_X7, // 07
324-
(PCODE)0, // 10
324+
(PCODE)0, // 10
325325
(PCODE)Load_X1, // 11
326326
(PCODE)Load_X1_X2, // 12
327327
(PCODE)Load_X1_X2_X3, // 13
@@ -338,7 +338,7 @@ PCODE GPRegsRoutines[] =
338338
(PCODE)Load_X2_X3_X4_X5_X6, // 26
339339
(PCODE)Load_X2_X3_X4_X5_X6_X7, // 27
340340
(PCODE)0, // 30
341-
(PCODE)0, // 31
341+
(PCODE)0, // 31
342342
(PCODE)0, // 32
343343
(PCODE)Load_X3, // 33
344344
(PCODE)Load_X3_X4, // 34
@@ -438,7 +438,7 @@ PCODE FPRegsRoutines[] =
438438
(PCODE)Load_D0_D1_D2_D3_D4_D5, // 05
439439
(PCODE)Load_D0_D1_D2_D3_D4_D5_D6, // 06
440440
(PCODE)Load_D0_D1_D2_D3_D4_D5_D6_D7, // 07
441-
(PCODE)0, // 10
441+
(PCODE)0, // 10
442442
(PCODE)Load_D1, // 11
443443
(PCODE)Load_D1_D2, // 12
444444
(PCODE)Load_D1_D2_D3, // 13
@@ -455,7 +455,7 @@ PCODE FPRegsRoutines[] =
455455
(PCODE)Load_D2_D3_D4_D5_D6, // 26
456456
(PCODE)Load_D2_D3_D4_D5_D6_D7, // 27
457457
(PCODE)0, // 30
458-
(PCODE)0, // 31
458+
(PCODE)0, // 31
459459
(PCODE)0, // 32
460460
(PCODE)Load_D3, // 33
461461
(PCODE)Load_D3_D4, // 34
@@ -590,7 +590,7 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra
590590
ArgLocDesc argLocDesc;
591591
argIt.GetArgLoc(ofs, &argLocDesc);
592592

593-
#ifdef UNIX_AMD64_ABI
593+
#ifdef UNIX_AMD64_ABI
594594
if (argIt.GetArgLocDescForStructInRegs() != NULL)
595595
{
596596
TypeHandle argTypeHandle;
@@ -604,33 +604,43 @@ CallStubHeader *CallStubGenerator::GenerateCallStub(MethodDesc *pMD, AllocMemTra
604604
{
605605
ArgLocDesc argLocDescEightByte = {};
606606
SystemVClassificationType eightByteType = pEEClass->GetEightByteClassification(i);
607-
if (eightByteType == SystemVClassificationTypeInteger)
608-
{
609-
if (argLocDesc.m_cGenReg != 0)
610-
{
611-
argLocDescEightByte.m_cGenReg = 1;
612-
argLocDescEightByte.m_idxGenReg = argLocDesc.m_idxGenReg++;
613-
}
614-
else
615-
{
616-
argLocDescEightByte.m_byteStackSize = 8;
617-
argLocDescEightByte.m_byteStackIndex = argLocDesc.m_byteStackIndex;
618-
argLocDesc.m_byteStackIndex += 8;
619-
}
620-
}
621-
else if (eightByteType == SystemVClassificationTypeSSE)
607+
switch (eightByteType)
622608
{
623-
if (argLocDesc.m_cFloatReg != 0)
609+
case SystemVClassificationTypeInteger:
610+
case SystemVClassificationTypeIntegerReference:
611+
case SystemVClassificationTypeIntegerByRef:
624612
{
625-
argLocDescEightByte.m_cFloatReg = 1;
626-
argLocDescEightByte.m_idxFloatReg = argLocDesc.m_idxFloatReg++;
613+
if (argLocDesc.m_cGenReg != 0)
614+
{
615+
argLocDescEightByte.m_cGenReg = 1;
616+
argLocDescEightByte.m_idxGenReg = argLocDesc.m_idxGenReg++;
617+
}
618+
else
619+
{
620+
argLocDescEightByte.m_byteStackSize = 8;
621+
argLocDescEightByte.m_byteStackIndex = argLocDesc.m_byteStackIndex;
622+
argLocDesc.m_byteStackIndex += 8;
623+
}
624+
break;
627625
}
628-
else
626+
case SystemVClassificationTypeSSE:
629627
{
630-
argLocDescEightByte.m_byteStackSize = 8;
631-
argLocDescEightByte.m_byteStackIndex = argLocDesc.m_byteStackIndex;
632-
argLocDesc.m_byteStackIndex += 8;
628+
if (argLocDesc.m_cFloatReg != 0)
629+
{
630+
argLocDescEightByte.m_cFloatReg = 1;
631+
argLocDescEightByte.m_idxFloatReg = argLocDesc.m_idxFloatReg++;
632+
}
633+
else
634+
{
635+
argLocDescEightByte.m_byteStackSize = 8;
636+
argLocDescEightByte.m_byteStackIndex = argLocDesc.m_byteStackIndex;
637+
argLocDesc.m_byteStackIndex += 8;
638+
}
639+
break;
633640
}
641+
default:
642+
assert(!"Unhandled systemv classification for argument in GenerateCallStub");
643+
break;
634644
}
635645
ProcessArgument(argIt, argLocDescEightByte, pRoutines);
636646
}
@@ -878,7 +888,7 @@ void CallStubGenerator::ProcessArgument(ArgIterator& argIt, ArgLocDesc& argLocDe
878888
}
879889

880890
if (argLocDesc.m_cGenReg != 0)
881-
{
891+
{
882892
if (m_r1 == NoRange) // No active range yet
883893
{
884894
// Start a new range
@@ -907,7 +917,7 @@ void CallStubGenerator::ProcessArgument(ArgIterator& argIt, ArgLocDesc& argLocDe
907917
// Start a new range
908918
m_x1 = argLocDesc.m_idxFloatReg;
909919
m_x2 = m_x1 + argLocDesc.m_cFloatReg - 1;
910-
}
920+
}
911921
else if (argLocDesc.m_idxFloatReg == m_x2 + 1)
912922
{
913923
// Extend an existing range
@@ -929,7 +939,7 @@ void CallStubGenerator::ProcessArgument(ArgIterator& argIt, ArgLocDesc& argLocDe
929939
// Start a new range
930940
m_s1 = argLocDesc.m_byteStackIndex;
931941
m_s2 = m_s1 + argLocDesc.m_byteStackSize - 1;
932-
}
942+
}
933943
else if ((argLocDesc.m_byteStackIndex == m_s2 + 1) && (argLocDesc.m_byteStackSize >= 8))
934944
{
935945
// Extend an existing range, but only if the argument is at least pointer size large.
@@ -987,4 +997,4 @@ void CallStubGenerator::ProcessArgument(ArgIterator& argIt, ArgLocDesc& argLocDe
987997
#endif // UNIX_AMD64_ABI
988998
}
989999

990-
#endif // FEATURE_INTERPRETER
1000+
#endif // FEATURE_INTERPRETER

src/coreclr/vm/interpexec.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
9191
int8_t *stack;
9292

9393
InterpMethod *pMethod = *(InterpMethod**)pFrame->startIp;
94+
assert(pMethod->CheckIntegrity());
95+
9496
pThreadContext->pStackPointer = pFrame->pStack + pMethod->allocaSize;
9597
ip = pFrame->startIp + sizeof(InterpMethod*) / sizeof(int32_t);
9698
stack = pFrame->pStack;
@@ -1172,6 +1174,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
11721174

11731175
// Set execution state for the new frame
11741176
pMethod = *(InterpMethod**)pFrame->startIp;
1177+
assert(pMethod->CheckIntegrity());
11751178
stack = pFrame->pStack;
11761179
ip = pFrame->startIp + sizeof(InterpMethod*) / sizeof(int32_t);
11771180
pThreadContext->pStackPointer = stack + pMethod->allocaSize;
@@ -1423,6 +1426,21 @@ do { \
14231426
STELEM(double, double);
14241427
break;
14251428
}
1429+
case INTOP_LDTOKEN:
1430+
{
1431+
int dreg = ip[1];
1432+
void *nativeHandle = pMethod->pDataItems[ip[2]];
1433+
size_t helperDirectOrIndirect = (size_t)pMethod->pDataItems[ip[3]];
1434+
HELPER_FTN_PP helper = nullptr;
1435+
if (helperDirectOrIndirect & INTERP_INDIRECT_HELPER_TAG)
1436+
helper = *(HELPER_FTN_PP *)(helperDirectOrIndirect & ~INTERP_INDIRECT_HELPER_TAG);
1437+
else
1438+
helper = (HELPER_FTN_PP)helperDirectOrIndirect;
1439+
void *managedHandle = helper(nativeHandle);
1440+
LOCAL_VAR(dreg, void*) = managedHandle;
1441+
ip += 4;
1442+
break;
1443+
}
14261444
case INTOP_FAILFAST:
14271445
assert(0);
14281446
break;
@@ -1456,6 +1474,7 @@ do { \
14561474

14571475
stack = pFrame->pStack;
14581476
pMethod = *(InterpMethod**)pFrame->startIp;
1477+
assert(pMethod->CheckIntegrity());
14591478
pThreadContext->pStackPointer = pFrame->pStack + pMethod->allocaSize;
14601479
goto MAIN_LOOP;
14611480
}
@@ -1472,6 +1491,7 @@ do { \
14721491
ip = pFrame->ip;
14731492
stack = pFrame->pStack;
14741493
pMethod = *(InterpMethod**)pFrame->startIp;
1494+
assert(pMethod->CheckIntegrity());
14751495
pFrame->ip = NULL;
14761496

14771497
pThreadContext->pStackPointer = pFrame->pStack + pMethod->allocaSize;

src/tests/JIT/interpreter/Interpreter.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,13 @@ public static void RunInterpreterTests()
419419
if (!TestSizeof())
420420
Environment.FailFast(null);
421421

422+
if (!TestLdtoken())
423+
Environment.FailFast(null);
424+
/*
425+
if (!TestMdArray())
426+
Environment.FailFast(null);
427+
*/
428+
422429
System.GC.Collect();
423430
}
424431

@@ -959,4 +966,31 @@ public static unsafe bool TestSizeof()
959966
return false;
960967
return true;
961968
}
969+
970+
public static int LdtokenField = 7;
971+
972+
public static bool TestLdtoken()
973+
{
974+
Type t = typeof(int);
975+
int i = 42;
976+
if (!ReferenceEquals(t, i.GetType()))
977+
return false;
978+
// These generate field and method ldtoken opcodes, but the test fails because we are missing castclass and possibly also generics
979+
/*
980+
System.Linq.Expressions.Expression<Func<int>> f = () => LdtokenField;
981+
System.Linq.Expressions.Expression<Action> a = () => TestLdtoken();
982+
*/
983+
return true;
984+
}
985+
986+
public static bool TestMdArray()
987+
{
988+
// FIXME: This generates roughly:
989+
// newobj int[,].ctor
990+
// ldtoken int[,]
991+
// call System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray
992+
// The newobj currently fails because int[,].ctor isn't a real method, the interp needs to use getCallInfo to determine how to invoke it
993+
int[,] a = {{1, 2}, {3, 4}};
994+
return a[0, 1] == 2;
995+
}
962996
}

0 commit comments

Comments
 (0)