Skip to content

Commit

Permalink
Allocate Array.Empty<> on a frozen segment (NonGC heap) (dotnet#85559)
Browse files Browse the repository at this point in the history
Co-authored-by: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
  • Loading branch information
EgorBo and jakobbotsch committed May 3, 2023
1 parent 7afd85d commit 0be256e
Show file tree
Hide file tree
Showing 44 changed files with 451 additions and 354 deletions.
2 changes: 2 additions & 0 deletions docs/design/coreclr/botr/readytorun-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,8 @@ enum ReadyToRunHelper
READYTORUN_HELPER_GenericNonGcTlsBase = 0x67,
READYTORUN_HELPER_VirtualFuncPtr = 0x68,
READYTORUN_HELPER_IsInstanceOfException = 0x69,
READYTORUN_HELPER_NewMaybeFrozenArray = 0x6A,
READYTORUN_HELPER_NewMaybeFrozenObject = 0x6B,

// Long mul/div/shift ops
READYTORUN_HELPER_LMul = 0xC0,
Expand Down
10 changes: 2 additions & 8 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ enum CorInfoHelpFunc
which is the right helper to use to allocate an object of a given type. */

CORINFO_HELP_NEWFAST,
CORINFO_HELP_NEWFAST_MAYBEFROZEN, // allocator for objects that *might* allocate them on a frozen segment
CORINFO_HELP_NEWSFAST, // allocator for small, non-finalizer, non-array object
CORINFO_HELP_NEWSFAST_FINALIZE, // allocator for small, finalizable, non-array object
CORINFO_HELP_NEWSFAST_ALIGN8, // allocator for small, non-finalizer, non-array object, 8 byte aligned
Expand All @@ -412,6 +413,7 @@ enum CorInfoHelpFunc
CORINFO_HELP_NEW_MDARR,// multi-dim array helper for arrays Rank != 1 (with or without lower bounds - dimensions passed in as unmanaged array)
CORINFO_HELP_NEW_MDARR_RARE,// rare multi-dim array helper (Rank == 1)
CORINFO_HELP_NEWARR_1_DIRECT, // helper for any one dimensional array creation
CORINFO_HELP_NEWARR_1_MAYBEFROZEN, // allocator for arrays that *might* allocate them on a frozen segment
CORINFO_HELP_NEWARR_1_OBJ, // optimized 1-D object arrays
CORINFO_HELP_NEWARR_1_VC, // optimized 1-D value class arrays
CORINFO_HELP_NEWARR_1_ALIGN8, // like VC, but aligns the array start
Expand Down Expand Up @@ -583,8 +585,6 @@ enum CorInfoHelpFunc
CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE, // Convert from a TypeHandle (native structure pointer) to RuntimeTypeHandle at run-time
CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL, // Convert from a TypeHandle (native structure pointer) to RuntimeTypeHandle at run-time, handle might point to a null type

CORINFO_HELP_ARE_TYPES_EQUIVALENT, // Check whether two TypeHandles (native structure pointers) are equivalent

CORINFO_HELP_VIRTUAL_FUNC_PTR, // look up a virtual method at run-time

// Not a real helpers. Instead of taking handle arguments, these helpers point to a small stub that loads the handle argument and calls the static helper.
Expand Down Expand Up @@ -2619,12 +2619,6 @@ class ICorStaticInfo
CORINFO_CLASS_HANDLE parent // base type
) = 0;

// TRUE if cls1 and cls2 are considered equivalent types.
virtual bool areTypesEquivalent(
CORINFO_CLASS_HANDLE cls1,
CORINFO_CLASS_HANDLE cls2
) = 0;

// See if a cast from fromClass to toClass will succeed, fail, or needs
// to be resolved at runtime.
virtual TypeCompareState compareTypesForCast(
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/inc/corjitflags.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class CORJIT_FLAGS
CORJIT_FLAG_OSR = 13, // Generate alternate method for On Stack Replacement

CORJIT_FLAG_ALT_JIT = 14, // JIT should consider itself an ALT_JIT
CORJIT_FLAG_UNUSED8 = 15,
CORJIT_FLAG_FROZEN_ALLOC_ALLOWED = 15, // JIT is allowed to use *_MAYBEFROZEN allocators
CORJIT_FLAG_UNUSED9 = 16,
CORJIT_FLAG_UNUSED10 = 17,

Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/inc/icorjitinfoimpl_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,10 +339,6 @@ bool canCast(
CORINFO_CLASS_HANDLE child,
CORINFO_CLASS_HANDLE parent) override;

bool areTypesEquivalent(
CORINFO_CLASS_HANDLE cls1,
CORINFO_CLASS_HANDLE cls2) override;

TypeCompareState compareTypesForCast(
CORINFO_CLASS_HANDLE fromClass,
CORINFO_CLASS_HANDLE toClass) override;
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jiteeversionguid.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
#define GUID_DEFINED
#endif // !GUID_DEFINED

constexpr GUID JITEEVersionIdentifier = { /* 387bcec3-9a71-4422-a11c-e7ce3b73c592 */
0x387bcec3,
0x9a71,
0x4422,
{0xa1, 0x1c, 0xe7, 0xce, 0x3b, 0x73, 0xc5, 0x92}
constexpr GUID JITEEVersionIdentifier = { /* 4e6355a0-3844-45e2-8cef-082c18217e14 */
0x4e6355a0,
0x3844,
0x45e2,
{0x8c, 0xef, 0x8, 0x2c, 0x18, 0x21, 0x7e, 0x14}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/inc/jithelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@

// Allocating a new object
JITHELPER(CORINFO_HELP_NEWFAST, JIT_New, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_NEWFAST_MAYBEFROZEN, JIT_NewMaybeFrozen,CORINFO_HELP_SIG_REG_ONLY)
DYNAMICJITHELPER(CORINFO_HELP_NEWSFAST, JIT_New, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_NEWSFAST_FINALIZE, NULL, CORINFO_HELP_SIG_REG_ONLY)
DYNAMICJITHELPER(CORINFO_HELP_NEWSFAST_ALIGN8, JIT_New, CORINFO_HELP_SIG_REG_ONLY)
Expand All @@ -78,6 +79,7 @@
JITHELPER(CORINFO_HELP_NEW_MDARR, JIT_NewMDArr,CORINFO_HELP_SIG_4_STACK)
JITHELPER(CORINFO_HELP_NEW_MDARR_RARE, JIT_NewMDArr,CORINFO_HELP_SIG_4_STACK)
JITHELPER(CORINFO_HELP_NEWARR_1_DIRECT, JIT_NewArr1,CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_NEWARR_1_MAYBEFROZEN, JIT_NewArr1MaybeFrozen,CORINFO_HELP_SIG_REG_ONLY)
DYNAMICJITHELPER(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1,CORINFO_HELP_SIG_REG_ONLY)
DYNAMICJITHELPER(CORINFO_HELP_NEWARR_1_VC, JIT_NewArr1,CORINFO_HELP_SIG_REG_ONLY)
DYNAMICJITHELPER(CORINFO_HELP_NEWARR_1_ALIGN8, JIT_NewArr1,CORINFO_HELP_SIG_REG_ONLY)
Expand Down Expand Up @@ -252,8 +254,6 @@
JITHELPER(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE, JIT_GetRuntimeType, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL, JIT_GetRuntimeType_MaybeNull, CORINFO_HELP_SIG_REG_ONLY)

JITHELPER(CORINFO_HELP_ARE_TYPES_EQUIVALENT, NULL, CORINFO_HELP_SIG_REG_ONLY)

JITHELPER(CORINFO_HELP_VIRTUAL_FUNC_PTR, JIT_VirtualFunctionPointer, CORINFO_HELP_SIG_4_STACK)

JITHELPER(CORINFO_HELP_READYTORUN_NEW, NULL, CORINFO_HELP_SIG_NO_ALIGN_STUB)
Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/inc/readytorun.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
// src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h
#define READYTORUN_MAJOR_VERSION 0x0009
#define READYTORUN_MINOR_VERSION 0x0000
#define READYTORUN_MINOR_VERSION 0x0001

#define MINIMUM_READYTORUN_MAJOR_VERSION 0x009

Expand All @@ -30,6 +30,7 @@
// R2R 6.0 is not backward compatible with 5.x or earlier.
// R2R Version 8.0 Changes the alignment of the Int128 type
// R2R Version 9.0 adds support for the Vector512 type
// R2R Version 9.1 adds new helpers to allocate objects on frozen segments

struct READYTORUN_CORE_HEADER
{
Expand Down Expand Up @@ -335,6 +336,8 @@ enum ReadyToRunHelper
READYTORUN_HELPER_GenericNonGcTlsBase = 0x67,
READYTORUN_HELPER_VirtualFuncPtr = 0x68,
READYTORUN_HELPER_IsInstanceOfException = 0x69,
READYTORUN_HELPER_NewMaybeFrozenArray = 0x6A,
READYTORUN_HELPER_NewMaybeFrozenObject = 0x6B,

// Long mul/div/shift ops
READYTORUN_HELPER_LMul = 0xC0,
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/inc/readytorunhelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ HELPER(READYTORUN_HELPER_GenericNonGcTlsBase, CORINFO_HELP_GETGENERICS_NON

HELPER(READYTORUN_HELPER_VirtualFuncPtr, CORINFO_HELP_VIRTUAL_FUNC_PTR, )
HELPER(READYTORUN_HELPER_IsInstanceOfException, CORINFO_HELP_ISINSTANCEOF_EXCEPTION, )
HELPER(READYTORUN_HELPER_NewMaybeFrozenArray, CORINFO_HELP_NEWARR_1_MAYBEFROZEN, )
HELPER(READYTORUN_HELPER_NewMaybeFrozenObject, CORINFO_HELP_NEWFAST_MAYBEFROZEN, )

HELPER(READYTORUN_HELPER_LMul, CORINFO_HELP_LMUL, )
HELPER(READYTORUN_HELPER_LMulOfv, CORINFO_HELP_LMUL_OVF, )
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/jit/ICorJitInfo_names_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ DEF_CLR_API(getBuiltinClass)
DEF_CLR_API(getTypeForPrimitiveValueClass)
DEF_CLR_API(getTypeForPrimitiveNumericClass)
DEF_CLR_API(canCast)
DEF_CLR_API(areTypesEquivalent)
DEF_CLR_API(compareTypesForCast)
DEF_CLR_API(compareTypesForEquality)
DEF_CLR_API(mergeClasses)
Expand Down
10 changes: 0 additions & 10 deletions src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -793,16 +793,6 @@ bool WrapICorJitInfo::canCast(
return temp;
}

bool WrapICorJitInfo::areTypesEquivalent(
CORINFO_CLASS_HANDLE cls1,
CORINFO_CLASS_HANDLE cls2)
{
API_ENTER(areTypesEquivalent);
bool temp = wrapHnd->areTypesEquivalent(cls1, cls2);
API_LEAVE(areTypesEquivalent);
return temp;
}

TypeCompareState WrapICorJitInfo::compareTypesForCast(
CORINFO_CLASS_HANDLE fromClass,
CORINFO_CLASS_HANDLE toClass)
Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3038,10 +3038,6 @@ class Compiler
GenTree* gtFoldBoxNullable(GenTree* tree);
GenTree* gtFoldExprCompare(GenTree* tree);
GenTree* gtFoldExprConditional(GenTree* tree);
GenTree* gtCreateHandleCompare(genTreeOps oper,
GenTree* op1,
GenTree* op2,
CorInfoInlineTypeCheck typeCheckInliningResult);
GenTree* gtFoldExprCall(GenTreeCall* call);
GenTree* gtFoldTypeCompare(GenTree* tree);
GenTree* gtFoldTypeEqualityCall(bool isEq, GenTree* op1, GenTree* op2);
Expand Down
77 changes: 12 additions & 65 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2158,30 +2158,17 @@ GenTree* Compiler::getArrayLengthFromAllocation(GenTree* tree DEBUGARG(BasicBloc

if (call->gtCallType == CT_HELPER)
{
switch (eeGetHelperNum(call->gtCallMethHnd))
CorInfoHelpFunc helper = eeGetHelperNum(call->gtCallMethHnd);
switch (helper)
{
case CORINFO_HELP_NEWARR_1_MAYBEFROZEN:
case CORINFO_HELP_NEWARR_1_DIRECT:
case CORINFO_HELP_NEWARR_1_OBJ:
case CORINFO_HELP_NEWARR_1_VC:
case CORINFO_HELP_NEWARR_1_ALIGN8:
{
// This is an array allocation site. Grab the array length node.
arrayLength = call->gtArgs.GetArgByIndex(1)->GetNode();
break;
}

case CORINFO_HELP_READYTORUN_NEWARR_1:
{
// On arm when compiling on certain platforms for ready to
// run, a handle will be inserted before the length. To
// handle this case, we will grab the last argument as
// that's always the length. See
// CallArgs::AddFinalArgsAndDetermineABIInfo for where the
// handle is inserted.
for (CallArg& arg : call->gtArgs.Args())
{
arrayLength = arg.GetNode();
}
arrayLength = call->gtArgs.GetUserArgByIndex(1)->GetNode();
break;
}

Expand Down Expand Up @@ -13614,47 +13601,6 @@ GenTree* Compiler::gtFoldExprConditional(GenTree* tree)
return replacement;
}

//------------------------------------------------------------------------
// gtCreateHandleCompare: generate a type handle comparison
//
// Arguments:
// oper -- comparison operation (equal/not equal)
// op1 -- first operand
// op2 -- second operand
// typeCheckInliningResult -- indicates how the comparison should happen
//
// Returns:
// Type comparison tree
//

GenTree* Compiler::gtCreateHandleCompare(genTreeOps oper,
GenTree* op1,
GenTree* op2,
CorInfoInlineTypeCheck typeCheckInliningResult)
{
// If we can compare pointers directly, just emit the binary operation
if (typeCheckInliningResult == CORINFO_INLINE_TYPECHECK_PASS)
{
return gtNewOperNode(oper, TYP_INT, op1, op2);
}

assert(typeCheckInliningResult == CORINFO_INLINE_TYPECHECK_USE_HELPER);

// Emit a call to a runtime helper
GenTree* ret = gtNewHelperCallNode(CORINFO_HELP_ARE_TYPES_EQUIVALENT, TYP_INT, op1, op2);
if (oper == GT_EQ)
{
ret = gtNewOperNode(GT_NE, TYP_INT, ret, gtNewIconNode(0, TYP_INT));
}
else
{
assert(oper == GT_NE);
ret = gtNewOperNode(GT_EQ, TYP_INT, ret, gtNewIconNode(0, TYP_INT));
}

return ret;
}

//------------------------------------------------------------------------
// gtFoldTypeCompare: see if a type comparison can be further simplified
//
Expand Down Expand Up @@ -13767,9 +13713,9 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree)
inliningKind = info.compCompHnd->canInlineTypeCheck(cls2Hnd, CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN);
}

assert(inliningKind == CORINFO_INLINE_TYPECHECK_PASS || inliningKind == CORINFO_INLINE_TYPECHECK_USE_HELPER);
assert(inliningKind == CORINFO_INLINE_TYPECHECK_PASS);

GenTree* compare = gtCreateHandleCompare(oper, op1ClassFromHandle, op2ClassFromHandle, inliningKind);
GenTree* compare = gtNewOperNode(oper, TYP_INT, op1ClassFromHandle, op2ClassFromHandle);

// Drop any now-irrelevant flags
compare->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_DONT_CSE);
Expand Down Expand Up @@ -13805,11 +13751,10 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree)

arg2 = gtNewMethodTableLookup(arg2);

CorInfoInlineTypeCheck inliningKind =
info.compCompHnd->canInlineTypeCheck(nullptr, CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE);
assert(inliningKind == CORINFO_INLINE_TYPECHECK_PASS || inliningKind == CORINFO_INLINE_TYPECHECK_USE_HELPER);
assert(info.compCompHnd->canInlineTypeCheck(nullptr, CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE) ==
CORINFO_INLINE_TYPECHECK_PASS);

GenTree* compare = gtCreateHandleCompare(oper, arg1, arg2, inliningKind);
GenTree* compare = gtNewOperNode(oper, TYP_INT, arg1, arg2);

// Drop any now-irrelevant flags
compare->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_DONT_CSE);
Expand Down Expand Up @@ -13907,7 +13852,8 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree)
GenTree* const objMT = gtNewMethodTableLookup(objOp);

// Compare the two method tables
GenTree* const compare = gtCreateHandleCompare(oper, objMT, knownMT, typeCheckInliningResult);
assert(typeCheckInliningResult == CORINFO_INLINE_TYPECHECK_PASS);
GenTree* const compare = gtNewOperNode(oper, TYP_INT, objMT, knownMT);

// Drop any now irrelevant flags
compare->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_DONT_CSE);
Expand Down Expand Up @@ -18576,6 +18522,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetHelperCallClassHandle(GenTreeCall* call, boo
}

case CORINFO_HELP_NEWARR_1_DIRECT:
case CORINFO_HELP_NEWARR_1_MAYBEFROZEN:
case CORINFO_HELP_NEWARR_1_OBJ:
case CORINFO_HELP_NEWARR_1_VC:
case CORINFO_HELP_NEWARR_1_ALIGN8:
Expand Down
59 changes: 52 additions & 7 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9734,11 +9734,57 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// So if we have an int, explicitly extend it to be a native int.
op2 = impImplicitIorI4Cast(op2, TYP_I_IMPL);

bool isFrozenAllocator = false;
// If we're jitting a static constructor and detect the following code pattern:
//
// newarr
// stsfld
// ret
//
// we emit a "frozen" allocator for newarr to, hopefully, allocate that array on a frozen segment.
// This is a very simple and conservative implementation targeting Array.Empty<T>()'s shape
// Ideally, we want to be able to use frozen allocators more broadly, but such an analysis is
// not trivial.
//
if (((info.compFlags & FLG_CCTOR) == FLG_CCTOR) &&
// Does VM allow us to use frozen allocators?
opts.jitFlags->IsSet(JitFlags::JIT_FLAG_FROZEN_ALLOC_ALLOWED))
{
// Check next two opcodes (have to be STSFLD and RET)
const BYTE* nextOpcode1 = codeAddr + sizeof(mdToken);
const BYTE* nextOpcode2 = nextOpcode1 + sizeof(mdToken) + 1;
if ((nextOpcode2 < codeEndp) && (getU1LittleEndian(nextOpcode1) == CEE_STSFLD))
{
if (getU1LittleEndian(nextOpcode2) == CEE_RET)
{
// Check that the field is "static readonly", we don't want to waste memory
// for potentially mutable fields.
CORINFO_RESOLVED_TOKEN fldToken;
impResolveToken(nextOpcode1 + 1, &fldToken, CORINFO_TOKENKIND_Field);
CORINFO_FIELD_INFO fi;
eeGetFieldInfo(&fldToken, CORINFO_ACCESS_SET, &fi);
unsigned flagsToCheck = CORINFO_FLG_FIELD_STATIC | CORINFO_FLG_FIELD_FINAL;
if ((fi.fieldFlags & flagsToCheck) == flagsToCheck)
{
#ifdef FEATURE_READYTORUN
if (opts.IsReadyToRun())
if (opts.IsReadyToRun())
{
// Need to restore array classes before creating array objects on the heap
op1 = impTokenToHandle(&resolvedToken, nullptr, true /*mustRestoreHandle*/);
}
#endif
op1 = gtNewHelperCallNode(CORINFO_HELP_NEWARR_1_MAYBEFROZEN, TYP_REF, op1, op2);
isFrozenAllocator = true;
}
}
}
}

#ifdef FEATURE_READYTORUN
if (opts.IsReadyToRun() && !isFrozenAllocator)
{
op1 = impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_NEWARR_1, TYP_REF, nullptr,
op2);
helper = CORINFO_HELP_READYTORUN_NEWARR_1;
op1 = impReadyToRunHelperToTree(&resolvedToken, helper, TYP_REF, nullptr, op2);
usingReadyToRunHelper = (op1 != nullptr);

if (!usingReadyToRunHelper)
Expand All @@ -9750,7 +9796,6 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// 3) Allocate the new array
// Reason: performance (today, we'll always use the slow helper for the R2R generics case)

// Need to restore array classes before creating array objects on the heap
op1 = impTokenToHandle(&resolvedToken, nullptr, true /*mustRestoreHandle*/);
if (op1 == nullptr)
{ // compDonotInline()
Expand All @@ -9759,15 +9804,15 @@ void Compiler::impImportBlockCode(BasicBlock* block)
}
}

if (!usingReadyToRunHelper)
if (!usingReadyToRunHelper && !isFrozenAllocator)
#endif
{
/* Create a call to 'new' */
helper = info.compCompHnd->getNewArrHelper(resolvedToken.hClass);

// Note that this only works for shared generic code because the same helper is used for all
// reference array types
op1 =
gtNewHelperCallNode(info.compCompHnd->getNewArrHelper(resolvedToken.hClass), TYP_REF, op1, op2);
op1 = gtNewHelperCallNode(helper, TYP_REF, op1, op2);
}

op1->AsCall()->compileTimeHelperArgumentHandle = (CORINFO_GENERIC_HANDLE)resolvedToken.hClass;
Expand Down
Loading

0 comments on commit 0be256e

Please sign in to comment.