Skip to content

Commit a9808b9

Browse files
committed
Basic implementation of mdarray ctors and methods
1 parent 1afefeb commit a9808b9

File tree

4 files changed

+100
-32
lines changed

4 files changed

+100
-32
lines changed

src/coreclr/interpreter/compiler.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3008,7 +3008,7 @@ int InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
30083008
ctorClass = m_compHnd->getMethodClass(ctorMethod);
30093009
int32_t numArgs = ctorSignature.numArgs;
30103010

3011-
// TODO Special case array ctor / string ctor
3011+
// TODO Special case array ctor. We detect string ctor at execution time
30123012
m_pStackPointer -= numArgs;
30133013

30143014
// Allocate callArgs for the call, this + numArgs + terminator
@@ -3042,11 +3042,15 @@ int InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
30423042
{
30433043
AddIns(INTOP_NEWOBJ_VT);
30443044
m_pLastNewIns->data[1] = (int32_t)ALIGN_UP_TO(vtsize, INTERP_STACK_SLOT_SIZE);
3045+
m_pLastNewIns->data[2] = 0;
30453046
}
30463047
else
30473048
{
30483049
AddIns(INTOP_NEWOBJ);
30493050
m_pLastNewIns->data[1] = GetDataItemIndex(ctorClass);
3051+
m_pLastNewIns->data[2] = (m_compHnd->getArrayRank(ctorClass) > 0)
3052+
? GetDataItemIndexForHelperFtn(CORINFO_HELP_NEW_MDARR)
3053+
: 0;
30503054
}
30513055
m_pLastNewIns->data[0] = GetMethodDataItemIndex(ctorMethod);
30523056
m_pLastNewIns->SetSVar(CALL_ARGS_SVAR);

src/coreclr/interpreter/intops.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ OPDEF(INTOP_LDFLDA, "ldflda", 4, 1, 1, InterpOpInt)
279279
// Calls
280280
OPDEF(INTOP_CALL, "call", 4, 1, 1, InterpOpMethodHandle)
281281
OPDEF(INTOP_CALLVIRT, "callvirt", 4, 1, 1, InterpOpMethodHandle)
282-
OPDEF(INTOP_NEWOBJ, "newobj", 5, 1, 1, InterpOpMethodHandle)
282+
OPDEF(INTOP_NEWOBJ, "newobj", 6, 1, 1, InterpOpMethodHandle)
283283
OPDEF(INTOP_NEWOBJ_VT, "newobj.vt", 5, 1, 1, InterpOpMethodHandle)
284284

285285
OPDEF(INTOP_CALL_HELPER_PP, "call.helper.pp", 5, 1, 0, InterpOpThreeInts)

src/coreclr/vm/interpexec.cpp

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet, PCODE pC
4949
typedef void* (*HELPER_FTN_PP)(void*);
5050
typedef void* (*HELPER_FTN_BOX_UNBOX)(MethodTable*, void*);
5151
typedef Object* (*HELPER_FTN_NEWARR)(CORINFO_CLASS_HANDLE, intptr_t);
52+
typedef Object* (*HELPER_FTN_NEW_MDARR)(CORINFO_CLASS_HANDLE, int, void*);
5253

5354
InterpThreadContext::InterpThreadContext()
5455
{
@@ -1118,22 +1119,35 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
11181119
// First execution of this call. Ensure target method is compiled and
11191120
// patch the data item slot with the actual method code.
11201121
MethodDesc *pMD = (MethodDesc*)(targetMethod & ~INTERP_METHOD_HANDLE_TAG);
1121-
PCODE code = pMD->GetNativeCode();
1122-
if (!code) {
1123-
// This is an optimization to ensure that the stack walk will not have to search
1124-
// for the topmost frame in the current InterpExecMethod. It is not required
1125-
// for correctness, as the stack walk will find the topmost frame anyway. But it
1126-
// would need to seek through the frames to find it.
1127-
// An alternative approach would be to update the topmost frame during stack walk
1128-
// to make the probability that the next stack walk will need to search only a
1129-
// small subset of frames high.
1130-
pInterpreterFrame->SetTopInterpMethodContextFrame(pFrame);
1131-
GCX_PREEMP();
1132-
pMD->PrepareInitialCode(CallerGCMode::Coop);
1133-
code = pMD->GetNativeCode();
1122+
if (pMD->IsArray() || pMD->IsFCall())
1123+
{
1124+
// For FCalls and array special methods we can't go through the regular caching path
1125+
// because it will try to do pMD->GetNativeCode() and then crash.
1126+
PCODE code = pMD->TryGetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY);
1127+
assert(code);
1128+
InvokeCompiledMethod(pMD, stack + callArgsOffset, stack + returnOffset, code);
1129+
// FIXME: Caching in dataItems somehow
1130+
break;
1131+
}
1132+
else
1133+
{
1134+
PCODE code = pMD->GetNativeCode();
1135+
if (!code) {
1136+
// This is an optimization to ensure that the stack walk will not have to search
1137+
// for the topmost frame in the current InterpExecMethod. It is not required
1138+
// for correctness, as the stack walk will find the topmost frame anyway. But it
1139+
// would need to seek through the frames to find it.
1140+
// An alternative approach would be to update the topmost frame during stack walk
1141+
// to make the probability that the next stack walk will need to search only a
1142+
// small subset of frames high.
1143+
pInterpreterFrame->SetTopInterpMethodContextFrame(pFrame);
1144+
GCX_PREEMP();
1145+
pMD->PrepareInitialCode(CallerGCMode::Coop);
1146+
code = pMD->GetNativeCode();
1147+
}
1148+
pMethod->pDataItems[methodSlot] = (void*)code;
1149+
targetIp = (const int32_t*)code;
11341150
}
1135-
pMethod->pDataItems[methodSlot] = (void*)code;
1136-
targetIp = (const int32_t*)code;
11371151
}
11381152
else
11391153
{
@@ -1204,14 +1218,39 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
12041218
LOCAL_VAR(callArgsOffset, void*) = nullptr; // and zero it too! it might get scanned by the GC.
12051219
callArgsOffset += sizeof(StackVal);
12061220

1207-
// Get the address of the fcall that implements the ctor
1208-
PCODE code = pMD->TryGetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY);
1209-
assert(code);
1221+
if (pClass->IsArray())
1222+
{
1223+
// mdarray ctors are implemented via a helper ftn we grabbed at compile time
1224+
size_t helperDirectOrIndirect = (size_t)pMethod->pDataItems[ip[5]];
1225+
HELPER_FTN_NEW_MDARR helper = nullptr;
1226+
if (helperDirectOrIndirect & INTERP_INDIRECT_HELPER_TAG)
1227+
helper = *(HELPER_FTN_NEW_MDARR *)(helperDirectOrIndirect & ~INTERP_INDIRECT_HELPER_TAG);
1228+
else
1229+
helper = (HELPER_FTN_NEW_MDARR)helperDirectOrIndirect;
1230+
assert(helper);
1231+
1232+
// mdarray ctor wants an array of ints, we can't pass the callArgsOffset directly since
1233+
// it's technically a stream of intptrs even if each value is an int
1234+
int rank = pClass->GetRank();
1235+
int dims[8] = { 0 };
1236+
assert(rank < 8);
1237+
for (int i = 0; i < rank; i++)
1238+
dims[i] = LOCAL_VAR(callArgsOffset + (i * sizeof(StackVal)), int);
1239+
1240+
LOCAL_VAR(returnOffset, Object*) = helper((CORINFO_CLASS_HANDLE)pClass, rank, dims);
1241+
}
1242+
else
1243+
{
1244+
// Get the address of the fcall that implements the ctor
1245+
PCODE code = pMD->TryGetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY);
1246+
assert(code);
1247+
1248+
// callArgsOffset now only points to the ctor arguments, which are what the fcall expects.
1249+
// returnOffset still points to where the new instance goes, and the fcall will write it there.
1250+
InvokeCompiledMethod(pMD, stack + callArgsOffset, stack + returnOffset, code);
1251+
}
12101252

1211-
// callArgsOffset now only points to the ctor arguments, which are what the fcall expects.
1212-
// returnOffset still points to where the new instance goes, and the fcall will write it there.
1213-
InvokeCompiledMethod(pMD, stack + callArgsOffset, stack + returnOffset, code);
1214-
ip += 5;
1253+
ip += 6;
12151254
break;
12161255
}
12171256
else
@@ -1222,7 +1261,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
12221261
LOCAL_VAR(returnOffset, OBJECTREF) = objRef;
12231262
// Set `this` arg for ctor call
12241263
LOCAL_VAR(callArgsOffset, OBJECTREF) = objRef;
1225-
ip += 5;
1264+
ip += 6;
12261265

12271266
goto CALL_INTERP_SLOT;
12281267
}

src/tests/JIT/interpreter/Interpreter.cs

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -424,10 +424,9 @@ public static void RunInterpreterTests()
424424

425425
if (!TestLdtoken())
426426
Environment.FailFast(null);
427-
/*
427+
428428
if (!TestMdArray())
429429
Environment.FailFast(null);
430-
*/
431430

432431
System.GC.Collect();
433432
}
@@ -1000,12 +999,38 @@ public static bool TestLdtoken()
1000999

10011000
public static bool TestMdArray()
10021001
{
1003-
// FIXME: This generates roughly:
1004-
// newobj int[,].ctor
1005-
// ldtoken int[,]
1006-
// call System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray
1007-
// The newobj currently fails because int[,].ctor isn't a real method, the interp needs to use getCallInfo to determine how to invoke it
1002+
/* FIXME: This generates roughly:
1003+
newobj int[,].ctor
1004+
ldtoken int[,]
1005+
call System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray
1006+
While the newobj now works, the InitializeArray call fails:
1007+
Assert failure(PID 61692 [0x0000f0fc], Thread: 40872 [0x9fa8]): Consistency check failed:
1008+
FAILED: (!implSlot.IsNull() || pMT->IsInterface()) && "Valid method implementation was not found."
1009+
1010+
CORECLR! CHECK::Trigger + 0x20F (0x00007ffa`807af47f)
1011+
CORECLR! VirtualCallStubManager::Resolver + 0xFAA (0x00007ffa`80e387fa)
1012+
CORECLR! VirtualCallStubManager::ResolveWorker + 0xB3C (0x00007ffa`80e36d2c)
1013+
CORECLR! ExternalMethodFixupWorker + 0xDCC (0x00007ffa`80d5d49c)
1014+
CORECLR! DelayLoad_MethodCall + 0x71 (0x00007ffa`813a0601)
1015+
SYSTEM.PRIVATE.CORELIB! <no symbol> + 0x0 (0x00007ffa`77af0098)
1016+
CORECLR! CallJittedMethodRetVoid + 0x14 (0x00007ffa`8139e7e4)
1017+
*/
1018+
/*
10081019
int[,] a = {{1, 2}, {3, 4}};
10091020
return a[0, 1] == 2;
1021+
*/
1022+
1023+
int[,] a = new int[2,2];
1024+
if (a[0, 1] != 0)
1025+
return false;
1026+
1027+
a[0, 0] = 1;
1028+
a[0, 1] = 2;
1029+
a[1, 0] = 3;
1030+
a[1, 1] = 4;
1031+
if (a[0, 1] != 2)
1032+
return false;
1033+
1034+
return true;
10101035
}
10111036
}

0 commit comments

Comments
 (0)