Skip to content

Commit b2585c6

Browse files
committed
JIT: Support for devirtualizing array interface methods
Update JIT and runtime to devirtualize interface calls on arrays over non-shared element types. Shared types are not (yet) handled. Add intrinsic and inlining attributes to key methods in the BCL. This allows the JIT to devirtualize and inline enumerator creation and devirtualize and inline all methods that access the enumerator. And this in turn allows the enumerator to be stack allocated. However, the enumerator fields are not (yet) physically promoted, because of an optimization in the BCL to return a static empty array enumerator. So the object being accessed later is ambiguous. Progress towards dotnet#62457.
1 parent b14e2f5 commit b2585c6

File tree

23 files changed

+458
-170
lines changed

23 files changed

+458
-170
lines changed

src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,8 @@ private SZArrayHelper()
740740
Debug.Fail("Hey! How'd I get here?");
741741
}
742742

743+
[Intrinsic]
744+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
743745
internal IEnumerator<T> GetEnumerator<T>()
744746
{
745747
// ! Warning: "this" is an array, not an SZArrayHelper. See comments above

src/coreclr/inc/corinfo.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2139,6 +2139,12 @@ class ICorStaticInfo
21392139
CORINFO_CLASS_HANDLE elemType
21402140
) = 0;
21412141

2142+
// Given T, return the type of the SZArrayHelper enumerator
2143+
// Returns null if the type can't be determined exactly.
2144+
virtual CORINFO_CLASS_HANDLE getSZArrayHelperEnumeratorClass(
2145+
CORINFO_CLASS_HANDLE elemType
2146+
) = 0;
2147+
21422148
// Given resolved token that corresponds to an intrinsic classified to
21432149
// get a raw handle (NI_System_Activator_AllocatorOf etc.), fetch the
21442150
// handle associated with the token. If this is not possible at

src/coreclr/inc/icorjitinfoimpl_generated.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ CORINFO_CLASS_HANDLE getDefaultComparerClass(
105105
CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(
106106
CORINFO_CLASS_HANDLE elemType) override;
107107

108+
CORINFO_CLASS_HANDLE getSZArrayHelperEnumeratorClass(
109+
CORINFO_CLASS_HANDLE elemType) override;
110+
108111
void expandRawHandleIntrinsic(
109112
CORINFO_RESOLVED_TOKEN* pResolvedToken,
110113
CORINFO_METHOD_HANDLE callerHandle,

src/coreclr/inc/jiteeversionguid.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
4343
#define GUID_DEFINED
4444
#endif // !GUID_DEFINED
4545

46-
constexpr GUID JITEEVersionIdentifier = { /* b75a5475-ff22-4078-9551-2024ce03d383 */
47-
0xb75a5475,
48-
0xff22,
49-
0x4078,
50-
{0x95, 0x51, 0x20, 0x24, 0xce, 0x03, 0xd3, 0x83}
46+
constexpr GUID JITEEVersionIdentifier = { /* 61da137a-bc95-4a59-859c-1725c88e678e */
47+
0x61da137a,
48+
0xbc95,
49+
0x4a59,
50+
{0x85, 0x9c, 0x17, 0x25, 0xc8, 0x8e, 0x67, 0x8e}
5151
};
5252

5353
//////////////////////////////////////////////////////////////////////////////////////////////////////////

src/coreclr/jit/ICorJitInfo_names_generated.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ DEF_CLR_API(resolveVirtualMethod)
2424
DEF_CLR_API(getUnboxedEntry)
2525
DEF_CLR_API(getDefaultComparerClass)
2626
DEF_CLR_API(getDefaultEqualityComparerClass)
27+
DEF_CLR_API(getSZArrayHelperEnumeratorClass)
2728
DEF_CLR_API(expandRawHandleIntrinsic)
2829
DEF_CLR_API(isIntrinsicType)
2930
DEF_CLR_API(getUnmanagedCallConv)

src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,15 @@ CORINFO_CLASS_HANDLE WrapICorJitInfo::getDefaultEqualityComparerClass(
209209
return temp;
210210
}
211211

212+
CORINFO_CLASS_HANDLE WrapICorJitInfo::getSZArrayHelperEnumeratorClass(
213+
CORINFO_CLASS_HANDLE elemType)
214+
{
215+
API_ENTER(getSZArrayHelperEnumeratorClass);
216+
CORINFO_CLASS_HANDLE temp = wrapHnd->getSZArrayHelperEnumeratorClass(elemType);
217+
API_LEAVE(getSZArrayHelperEnumeratorClass);
218+
return temp;
219+
}
220+
212221
void WrapICorJitInfo::expandRawHandleIntrinsic(
213222
CORINFO_RESOLVED_TOKEN* pResolvedToken,
214223
CORINFO_METHOD_HANDLE callerHandle,

src/coreclr/jit/importercalls.cpp

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,13 @@ var_types Compiler::impImportCall(OPCODE opcode,
969969
// Devirtualization may change which method gets invoked. Update our local cache.
970970
//
971971
methHnd = callInfo->hMethod;
972+
973+
// If we devirtualized to an intrinsic, assume this is one of the special cases.
974+
//
975+
if ((callInfo->methodFlags & CORINFO_FLG_INTRINSIC) != 0)
976+
{
977+
call->AsCall()->gtCallMoreFlags |= GTF_CALL_M_SPECIAL_INTRINSIC;
978+
}
972979
}
973980
else if (call->AsCall()->IsDelegateInvoke())
974981
{
@@ -1346,12 +1353,26 @@ var_types Compiler::impImportCall(OPCODE opcode,
13461353
eeGetCallSiteSig(pResolvedToken->token, pResolvedToken->tokenScope, pResolvedToken->tokenContext, sig);
13471354
}
13481355

1356+
CORINFO_CLASS_HANDLE retTypeClass = sig->retTypeClass;
1357+
13491358
// Sometimes "call" is not a GT_CALL (if we imported an intrinsic that didn't turn into a call)
13501359
if (!bIntrinsicImported)
13511360
{
13521361
assert(call->IsCall());
1362+
GenTreeCall* const origCall = call->AsCall();
13531363

1354-
GenTreeCall* origCall = call->AsCall();
1364+
// If the call is a special intrisic, we may know a more exact return type.
1365+
//
1366+
if (origCall->IsSpecialIntrinsic())
1367+
{
1368+
CORINFO_CLASS_HANDLE updatedRetTypeClass = impGetSpecialIntrinsicExactReturnType(origCall);
1369+
1370+
if (updatedRetTypeClass != NO_CLASS_HANDLE)
1371+
{
1372+
JITDUMP("Updating method return type to %s\n", eeGetClassName(updatedRetTypeClass));
1373+
retTypeClass = updatedRetTypeClass;
1374+
}
1375+
}
13551376

13561377
const bool isFatPointerCandidate = origCall->IsFatPointerCandidate();
13571378
const bool isInlineCandidate = origCall->IsInlineCandidate();
@@ -1360,7 +1381,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
13601381
if (varTypeIsStruct(callRetTyp))
13611382
{
13621383
// Need to treat all "split tree" cases here, not just inline candidates
1363-
call = impFixupCallStructReturn(call->AsCall(), sig->retTypeClass);
1384+
call = impFixupCallStructReturn(call->AsCall(), retTypeClass);
13641385
callRetTyp = call->TypeGet();
13651386
}
13661387

@@ -1513,7 +1534,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
15131534
}
15141535
}
15151536

1516-
typeInfo tiRetVal = verMakeTypeInfo(sig->retType, sig->retTypeClass);
1537+
typeInfo tiRetVal = verMakeTypeInfo(sig->retType, retTypeClass);
15171538
impPushOnStack(call, tiRetVal);
15181539
}
15191540

@@ -4671,6 +4692,13 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd,
46714692
break;
46724693
}
46734694

4695+
case NI_System_SZArrayHelper_GetEnumerator:
4696+
{
4697+
// We may know the exact type this returns
4698+
isSpecial = true;
4699+
break;
4700+
}
4701+
46744702
case NI_System_BitConverter_DoubleToInt64Bits:
46754703
{
46764704
GenTree* op1 = impStackTop().val;
@@ -8089,8 +8117,18 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
80898117
if (derivedMethod != nullptr)
80908118
{
80918119
assert(exactContext != nullptr);
8092-
assert(((size_t)exactContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS);
8093-
derivedClass = (CORINFO_CLASS_HANDLE)((size_t)exactContext & ~CORINFO_CONTEXTFLAGS_MASK);
8120+
8121+
if (((size_t)exactContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS)
8122+
{
8123+
derivedClass = (CORINFO_CLASS_HANDLE)((size_t)exactContext & ~CORINFO_CONTEXTFLAGS_MASK);
8124+
}
8125+
else
8126+
{
8127+
// Array interface devirt can return a generic method of the non-generic SZArrayHelper class.
8128+
//
8129+
assert(((size_t)exactContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD);
8130+
derivedClass = info.compCompHnd->getMethodClass(derivedMethod);
8131+
}
80948132
}
80958133

80968134
DWORD derivedMethodAttribs = 0;
@@ -8524,7 +8562,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
85248562
//
85258563
if (pExactContextHandle != nullptr)
85268564
{
8527-
*pExactContextHandle = MAKE_CLASSCONTEXT(derivedClass);
8565+
*pExactContextHandle = exactContext;
85288566
}
85298567

85308568
// We might have created a new recursive tail call candidate.
@@ -8729,6 +8767,43 @@ CORINFO_CLASS_HANDLE Compiler::impGetSpecialIntrinsicExactReturnType(GenTreeCall
87298767
break;
87308768
}
87318769

8770+
case NI_System_SZArrayHelper_GetEnumerator:
8771+
{
8772+
// Expect one method generic parameter; figure out which it is.
8773+
CORINFO_SIG_INFO sig;
8774+
info.compCompHnd->getMethodSig(methodHnd, &sig);
8775+
assert(sig.sigInst.methInstCount == 1);
8776+
assert(sig.sigInst.classInstCount == 0);
8777+
8778+
CORINFO_CLASS_HANDLE typeHnd = sig.sigInst.methInst[0];
8779+
assert(typeHnd != nullptr);
8780+
8781+
CallArg* instParam = call->gtArgs.FindWellKnownArg(WellKnownArg::InstParam);
8782+
if (instParam != nullptr)
8783+
{
8784+
assert(instParam->GetNext() == nullptr);
8785+
CORINFO_CLASS_HANDLE hClass = gtGetHelperArgClassHandle(instParam->GetNode());
8786+
if (hClass != NO_CLASS_HANDLE)
8787+
{
8788+
typeHnd = getTypeInstantiationArgument(hClass, 0);
8789+
}
8790+
}
8791+
8792+
result = info.compCompHnd->getSZArrayHelperEnumeratorClass(typeHnd);
8793+
8794+
if (result != NO_CLASS_HANDLE)
8795+
{
8796+
JITDUMP("Special intrinsic for type %s: return type is %s\n", eeGetClassName(typeHnd),
8797+
result != nullptr ? eeGetClassName(result) : "unknown");
8798+
}
8799+
else
8800+
{
8801+
JITDUMP("Special intrinsic for type %s: type undetermined, so deferring opt\n",
8802+
eeGetClassName(typeHnd));
8803+
}
8804+
break;
8805+
}
8806+
87328807
default:
87338808
{
87348809
JITDUMP("This special intrinsic not handled, sorry...\n");
@@ -10212,6 +10287,14 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
1021210287
result = NI_System_String_EndsWith;
1021310288
}
1021410289
}
10290+
else if (strcmp(className, "SZArrayHelper") == 0)
10291+
{
10292+
if (strcmp(methodName, "GetEnumerator") == 0)
10293+
{
10294+
result = NI_System_SZArrayHelper_GetEnumerator;
10295+
}
10296+
}
10297+
1021510298
break;
1021610299
}
1021710300

src/coreclr/jit/namedintrinsiclist.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,11 @@ enum NamedIntrinsic : unsigned short
251251
NI_PRIMITIVE_TrailingZeroCount,
252252

253253
NI_PRIMITIVE_END,
254+
255+
//
256+
// Array Intrinsics
257+
//
258+
NI_System_SZArrayHelper_GetEnumerator,
254259
};
255260

256261
#endif // _NAMEDINTRINSICLIST_H_

src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,13 @@ static CORINFO_RESOLVED_TOKEN CreateResolvedTokenFromMethod(CorInfoImpl jitInter
15211521
return comparer != null ? ObjectToHandle(comparer) : null;
15221522
}
15231523

1524+
private CORINFO_CLASS_STRUCT_* getSZArrayHelperEnumeratorClass(CORINFO_CLASS_STRUCT_* elemType)
1525+
{
1526+
// TBD
1527+
_ = HandleToObject(elemType);
1528+
return null;
1529+
}
1530+
15241531
private bool isIntrinsicType(CORINFO_CLASS_STRUCT_* classHnd)
15251532
{
15261533
TypeDesc type = HandleToObject(classHnd);

0 commit comments

Comments
 (0)