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 5 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
1 change: 1 addition & 0 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,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
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
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
1 change: 1 addition & 0 deletions src/coreclr/inc/jithelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,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
2 changes: 2 additions & 0 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2158,6 +2158,7 @@ GenTree* Compiler::getArrayLengthFromAllocation(GenTree* tree DEBUGARG(BasicBloc
switch (eeGetHelperNum(call->gtCallMethHnd))
{
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 Expand Up @@ -18300,6 +18301,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
50 changes: 46 additions & 4 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9810,11 +9810,13 @@ 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);

CorInfoHelpFunc helper = CORINFO_HELP_UNDEF;

#ifdef FEATURE_READYTORUN
if (opts.IsReadyToRun())
{
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 @@ -9839,11 +9841,51 @@ void Compiler::impImportBlockCode(BasicBlock* block)
#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);
}

// If we're jitting a static constructor and detect the following code pattern:
//
// newarr
// stsfld
// ret
//
// We replace default heap allocator for newarr with the one that prefers frozen segments.
// This is a very simple and conservative implementation targeting Array.Empty<T>(), ideally
// we want to be able to use frozen allocators more broadly, but that analysis is not trivial.
//
if (((info.compFlags & FLG_CCTOR) == FLG_CCTOR) &&
// Does VM allow us to use frozen allocators? (e.g. are we in a non-collectible assembly)
opts.jitFlags->IsSet(JitFlags::JIT_FLAG_FROZEN_ALLOC_ALLOWED))
{
// Check next two opcodes
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)
{
// TODO in this PR: R2R
if (helper == CORINFO_HELP_NEWARR_1_OBJ || helper == CORINFO_HELP_NEWARR_1_VC)
{
// Check that the field is "static readonly"
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)
{
// Replace the helper with the frozen version
op1->AsCall()->gtCallMethHnd = eeFindHelper(CORINFO_HELP_NEWARR_1_FROZEN);
}
}
}
}
}

op1->AsCall()->compileTimeHelperArgumentHandle = (CORINFO_GENERIC_HANDLE)resolvedToken.hClass;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1872,6 +1872,7 @@ GenTree* Compiler::impInitializeArrayIntrinsic(CORINFO_SIG_INFO* sig)
bool isMDArray = false;

if (newArrayCall->AsCall()->gtCallMethHnd != eeFindHelper(CORINFO_HELP_NEWARR_1_DIRECT) &&
newArrayCall->AsCall()->gtCallMethHnd != eeFindHelper(CORINFO_HELP_NEWARR_1_FROZEN) &&
newArrayCall->AsCall()->gtCallMethHnd != eeFindHelper(CORINFO_HELP_NEWARR_1_OBJ) &&
newArrayCall->AsCall()->gtCallMethHnd != eeFindHelper(CORINFO_HELP_NEWARR_1_VC) &&
newArrayCall->AsCall()->gtCallMethHnd != eeFindHelper(CORINFO_HELP_NEWARR_1_ALIGN8)
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/jitee.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class JitFlags
JIT_FLAG_OSR = 13, // Generate alternate version for On Stack Replacement

JIT_FLAG_ALT_JIT = 14, // JIT should consider itself an ALT_JIT
JIT_FLAG_UNUSED8 = 15,
JIT_FLAG_FROZEN_ALLOC_ALLOWED = 15, // JIT is allowed to use *_FROZEN allocators
JIT_FLAG_UNUSED9 = 16,

#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM64)
Expand Down Expand Up @@ -187,6 +187,7 @@ class JitFlags
#endif

FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_ALT_JIT, JIT_FLAG_ALT_JIT);
FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_FROZEN_ALLOC_ALLOWED, JIT_FLAG_FROZEN_ALLOC_ALLOWED);
FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_MAKEFINALCODE, JIT_FLAG_MAKEFINALCODE);
FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_READYTORUN, JIT_FLAG_READYTORUN);
FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_PROF_ENTERLEAVE, JIT_FLAG_PROF_ENTERLEAVE);
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1335,6 +1335,7 @@ void HelperCallProperties::init()
case CORINFO_HELP_NEW_MDARR:
case CORINFO_HELP_NEW_MDARR_RARE:
case CORINFO_HELP_NEWARR_1_DIRECT:
case CORINFO_HELP_NEWARR_1_FROZEN:
case CORINFO_HELP_NEWARR_1_OBJ:
case CORINFO_HELP_READYTORUN_NEWARR_1:

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/valuenum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12172,6 +12172,7 @@ VNFunc Compiler::fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc)
break;

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
1 change: 1 addition & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ which is the right helper to use to allocate an object of a given type. */
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
CORINFO_HELP_NEWARR_1_ALIGN8, // like VC, but aligns the array start
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1411,6 +1411,7 @@ public enum CorJitFlag : uint
CORJIT_FLAG_UNUSED6 = 12,
CORJIT_FLAG_OSR = 13, // Generate alternate version for On Stack Replacement
CORJIT_FLAG_ALT_JIT = 14, // JIT should consider itself an ALT_JIT
CORJIT_FLAG_FROZEN_ALLOC_ALLOWED = 15, // JIT is allowed to use *_FROZEN allocators
CORJIT_FLAG_UNUSED10 = 17,
CORJIT_FLAG_MAKEFINALCODE = 18, // Use the final code generator, i.e., not the interpreter.
CORJIT_FLAG_READYTORUN = 19, // Use version-resilient code generation
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,8 @@ const char* CorJitFlagToString(CORJIT_FLAGS::CorJitFlag flag)
return "CORJIT_FLAG_OSR";
case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_ALT_JIT:
return "CORJIT_FLAG_ALT_JIT";
case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_FROZEN_ALLOC_ALLOWED:
return "CORJIT_FLAG_FROZEN_ALLOC_ALLOWED";
case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_MAKEFINALCODE:
return "CORJIT_FLAG_MAKEFINALCODE";
case CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_READYTORUN:
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/frozenobjectheap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ Object* FrozenObjectSegment::TryAllocateObject(PTR_MethodTable type, size_t obje
{
_ASSERT(m_pStart != nullptr && m_Size > 0 && m_SegmentHandle != nullptr); // Expected to be inited
_ASSERT(IS_ALIGNED(m_pCurrent, DATA_ALIGNMENT));
_ASSERT(IS_ALIGNED(objectSize, DATA_ALIGNMENT));
_ASSERT(objectSize <= FOH_COMMIT_SIZE);
_ASSERT(m_pCurrent >= m_pStart + sizeof(ObjHeader));

Expand Down
85 changes: 85 additions & 0 deletions src/coreclr/vm/gchelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,91 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS
return ObjectToOBJECTREF((Object*)orArray);
}

// Same as AllocateSzArray but attempts to allocate the array on a frozen segment.
// It fallbacks to AllocateSzArray if it fails to allocate on a frozen segment.
OBJECTREF AllocateFrozenSzArray(MethodTable* pArrayMT, INT32 cElements)
{
CONTRACTL{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
} CONTRACTL_END;

SetTypeHandleOnThreadForAlloc(TypeHandle(pArrayMT));

_ASSERTE(pArrayMT->CheckInstanceActivated());
_ASSERTE(pArrayMT->GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY);

// The initial validation is copied from AllocateSzArray impl

CorElementType elemType = pArrayMT->GetArrayElementType();

if (pArrayMT->ContainsPointers() && cElements > 0)
{
// For arrays with GC pointers we can only work with empty arrays
return AllocateSzArray(pArrayMT, cElements);
}

if (pArrayMT->Collectible())
{
// Ignore arrays of collectible types
return AllocateSzArray(pArrayMT, cElements);
}

// Disallow the creation of void[] (an array of System.Void)
if (elemType == ELEMENT_TYPE_VOID)
COMPlusThrow(kArgumentException);

if (cElements < 0)
COMPlusThrow(kOverflowException);

if ((SIZE_T)cElements > MaxArrayLength())
ThrowOutOfMemoryDimensionsExceeded();

SIZE_T componentSize = pArrayMT->GetComponentSize();
#ifdef TARGET_64BIT
// POSITIVE_INT32 * UINT16 + SMALL_CONST
// this cannot overflow on 64bit
size_t totalSize = cElements * componentSize + pArrayMT->GetBaseSize();

#else
S_SIZE_T safeTotalSize = S_SIZE_T((DWORD)cElements) * S_SIZE_T((DWORD)componentSize) + S_SIZE_T((DWORD)pArrayMT->GetBaseSize());
if (safeTotalSize.IsOverflow())
ThrowOutOfMemoryDimensionsExceeded();

size_t totalSize = safeTotalSize.Value();
#endif

// FrozenObjectHeapManager doesn't yet support objects with a custom alignment,
// so we give up on arrays of value types requiring 8 byte alignment on 32bit platforms.
if ((DATA_ALIGNMENT < sizeof(double)) && (elemType == ELEMENT_TYPE_R8))
{
return AllocateSzArray(pArrayMT, cElements);
}
#ifdef FEATURE_64BIT_ALIGNMENT
MethodTable* pElementMT = pArrayMT->GetArrayElementTypeHandle().GetMethodTable();
if (pElementMT->RequiresAlign8() && pElementMT->IsValueType())
{
return AllocateSzArray(pArrayMT, cElements);
}
#endif

FrozenObjectHeapManager* foh = SystemDomain::GetFrozenObjectHeapManager();
ArrayBase* orArray = static_cast<ArrayBase*>(foh->TryAllocateObject(pArrayMT, PtrAlign(totalSize), /*publish*/ false));
if (orArray == nullptr)
{
// We failed to allocate on a frozen segment, fallback to AllocateSzArray
// E.g. if the array is too big to fit on a frozen segment
return AllocateSzArray(pArrayMT, cElements);
}
orArray->m_NumComponents = cElements;

// Publish needs to be postponed in this case because we need to specify array length
PublishObjectAndNotify(orArray, GC_ALLOC_NO_FLAGS);

return ObjectToOBJECTREF(orArray);
}

void ThrowOutOfMemoryDimensionsExceeded()
{
CONTRACTL {
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/gchelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS);
OBJECTREF AllocateSzArray(TypeHandle arrayType, INT32 length, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS);

// Allocate single-dimensional array with a hint to prefer frozen segments
OBJECTREF AllocateFrozenSzArray(MethodTable* pArrayMT, INT32 length);

// The main Array allocation routine, can do multi-dimensional
OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS);
OBJECTREF AllocateArrayEx(TypeHandle arrayType, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS);
Expand Down
40 changes: 40 additions & 0 deletions src/coreclr/vm/jithelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2719,6 +2719,46 @@ HCIMPL2(Object*, JIT_NewArr1, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size)
}
HCIMPLEND


/*************************************************************/
HCIMPL2(Object*, JIT_NewArr1Frozen, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size)
{
FCALL_CONTRACT;

OBJECTREF newArray = NULL;

HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame

MethodTable* pArrayMT = (MethodTable*)arrayMT;

_ASSERTE(pArrayMT->IsFullyLoaded());
_ASSERTE(pArrayMT->IsArray());
_ASSERTE(!pArrayMT->IsMultiDimArray());

if (size < 0)
COMPlusThrow(kOverflowException);

#ifdef HOST_64BIT
// Even though ECMA allows using a native int as the argument to newarr instruction
// (therefore size is INT_PTR), ArrayBase::m_NumComponents is 32-bit, so even on 64-bit
// platforms we can't create an array whose size exceeds 32 bits.
if (size > INT_MAX)
EX_THROW(EEMessageException, (kOverflowException, IDS_EE_ARRAY_DIMENSIONS_EXCEEDED));
#endif

#ifdef _DEBUG
if (g_pConfig->FastGCStressLevel()) {
GetThread()->DisableStressHeap();
}
#endif // _DEBUG

newArray = AllocateFrozenSzArray(pArrayMT, (INT32)size);
HELPER_METHOD_FRAME_END();

return(OBJECTREFToObject(newArray));
}
HCIMPLEND

/*************************************************************/
HCIMPL3(Object*, JIT_NewMDArr, CORINFO_CLASS_HANDLE classHnd, unsigned dwNumArgs, INT32 * pArgList)
{
Expand Down
Loading