Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allocate Array.Empty<> on a frozen segment (NonGC heap) #85559

Merged
merged 23 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from 16 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
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_NewFrozenArray = 0x6A,
READYTORUN_HELPER_NewFrozenObject = 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_FROZEN, // allocator for objects on a frozen segment
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
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_FROZEN, // helper for any one dimensional array creation 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
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
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 *_FROZEN 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_FROZEN, JIT_NewFrozen,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_FROZEN, JIT_NewArr1Frozen,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_NewFrozenArray = 0x6A,
READYTORUN_HELPER_NewFrozenObject = 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_NewFrozenArray, CORINFO_HELP_NEWARR_1_FROZEN, )
HELPER(READYTORUN_HELPER_NewFrozenObject, CORINFO_HELP_NEWFAST_FROZEN, )

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 @@ -3018,10 +3018,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
75 changes: 23 additions & 52 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2155,16 +2155,27 @@ 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_FROZEN:
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;
if (opts.IsReadyToRun() && helper == CORINFO_HELP_NEWARR_1_FROZEN)
{
// CORINFO_HELP_NEWARR_1_FROZEN is used in both JIT and R2R,
// for R2R take the same path as CORINFO_HELP_READYTORUN_NEWARR_1
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
FALLTHROUGH;
}
else
{
// This is an array allocation site. Grab the array length node.
arrayLength = call->gtArgs.GetArgByIndex(1)->GetNode();
break;
}
}

case CORINFO_HELP_READYTORUN_NEWARR_1:
Expand Down Expand Up @@ -13308,47 +13319,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 @@ -13461,9 +13431,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 @@ -13499,11 +13469,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 @@ -13601,7 +13570,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 @@ -18300,6 +18270,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetHelperCallClassHandle(GenTreeCall* call, boo
}

case CORINFO_HELP_NEWARR_1_DIRECT:
case CORINFO_HELP_NEWARR_1_FROZEN:
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 @@ -9730,11 +9730,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)
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
{
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_FROZEN, TYP_REF, op1, op2);
isFrozenAllocator = true;
}
}
}
}
EgorBo marked this conversation as resolved.
Show resolved Hide resolved

#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 @@ -9746,7 +9792,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 @@ -9755,15 +9800,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