From eae34b499470c8849937dce100d4769853cbfbd7 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 6 Feb 2025 14:03:42 -0800 Subject: [PATCH] JIT: initial support for stack allocating arrays of GC type Add the ability to create a ClassLayout for arrays, and use this to unblock stack allocation for arrays with GC types. Replaces #111686. --- src/coreclr/jit/compiler.h | 4 ++ src/coreclr/jit/layout.cpp | 90 ++++++++++++++++++++++++++++++++- src/coreclr/jit/layout.h | 2 + src/coreclr/jit/objectalloc.cpp | 2 +- src/coreclr/jit/objectalloc.h | 26 +--------- 5 files changed, 97 insertions(+), 27 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index ba794464a07a28..29a6e602a86c3d 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -11118,6 +11118,10 @@ class Compiler ClassLayout* typGetBlkLayout(unsigned blockSize); // Get the number of a layout having the specified size but no class handle. unsigned typGetBlkLayoutNum(unsigned blockSize); + // Get the layout for the specified array of known length + ClassLayout* typGetArrayLayout(CORINFO_CLASS_HANDLE classHandle, unsigned length); + // Get the number of a layout for the specified array of known length + unsigned typGetArrayLayoutNum(CORINFO_CLASS_HANDLE classHandle, unsigned length); var_types TypeHandleToVarType(CORINFO_CLASS_HANDLE handle, ClassLayout** pLayout = nullptr); var_types TypeHandleToVarType(CorInfoType jitType, CORINFO_CLASS_HANDLE handle, ClassLayout** pLayout = nullptr); diff --git a/src/coreclr/jit/layout.cpp b/src/coreclr/jit/layout.cpp index e8b852dff5fb03..e84f55781ce17e 100644 --- a/src/coreclr/jit/layout.cpp +++ b/src/coreclr/jit/layout.cpp @@ -414,6 +414,18 @@ ClassLayout* Compiler::typGetBlkLayout(unsigned blockSize) return typGetCustomLayout(ClassLayoutBuilder(this, blockSize)); } +unsigned Compiler::typGetArrayLayoutNum(CORINFO_CLASS_HANDLE classHandle, unsigned length) +{ + ClassLayoutBuilder b = ClassLayoutBuilder::BuildArray(this, classHandle, length); + return typGetCustomLayoutNum(b); +} + +ClassLayout* Compiler::typGetArrayLayout(CORINFO_CLASS_HANDLE classHandle, unsigned length) +{ + ClassLayoutBuilder b = ClassLayoutBuilder::BuildArray(this, classHandle, length); + return typGetCustomLayout(b); +} + //------------------------------------------------------------------------ // Create: Create a ClassLayout from an EE side class handle. // @@ -717,8 +729,8 @@ bool ClassLayout::AreCompatible(const ClassLayout* layout1, const ClassLayout* l } //------------------------------------------------------------------------ -// ClassLayoutbuilder: Construct a new builder for a class layout of the -// specified size, with all data being considered as non-padding. +// ClassLayoutBuilder: Construct a new builder for a class layout of the +// specified size. // // Arguments: // compiler - Compiler instance @@ -730,6 +742,80 @@ ClassLayoutBuilder::ClassLayoutBuilder(Compiler* compiler, unsigned size) { } +//------------------------------------------------------------------------ +// BuildArray: Construct a builder for an array layout +// +// Arguments: +// compiler - Compiler instance +// arrayHandle - class handle for array +// length - array length (in elements) +// +// Note: +// For arrays of structs we currently do not copy any struct padding, +// with the presumption that it is unlikely we will ever promote array elements. +// +ClassLayoutBuilder ClassLayoutBuilder::BuildArray(Compiler* compiler, CORINFO_CLASS_HANDLE arrayHandle, unsigned length) +{ + assert(length <= CORINFO_Array_MaxLength); + assert(arrayHandle != NO_CLASS_HANDLE); + + CORINFO_CLASS_HANDLE elemClsHnd = NO_CLASS_HANDLE; + CorInfoType corType = compiler->info.compCompHnd->getChildType(arrayHandle, &elemClsHnd); + var_types type = JITtype2varType(corType); + + ClassLayout* elementLayout = nullptr; + unsigned elementSize = 0; + + if (type == TYP_STRUCT) + { + elementLayout = compiler->typGetObjLayout(elemClsHnd); + elementSize = elementLayout->GetSize(); + } + else + { + elementSize = genTypeSize(type); + } + + ClrSafeInt totalSize(elementSize); + totalSize *= static_cast(length); + totalSize += static_cast(OFFSETOF__CORINFO_Array__data); + assert(!totalSize.IsOverflow()); + + ClassLayoutBuilder builder(compiler, totalSize.Value()); + + if (elementLayout != nullptr) + { + if (elementLayout->HasGCPtr()) + { + unsigned offset = OFFSETOF__CORINFO_Array__data; + for (unsigned i = 0; i < length; i++) + { + builder.CopyInfoFrom(offset, elementLayout, /* copy padding */ false); + offset += elementSize; + } + } + } + else if (varTypeIsGC(type)) + { + unsigned offset = OFFSETOF__CORINFO_Array__data; + for (unsigned i = 0; i < length; i++) + { + assert((offset % TARGET_POINTER_SIZE) == 0); + unsigned const slot = offset / TARGET_POINTER_SIZE; + builder.SetGCPtrType(slot, type); + offset += elementSize; + } + } + +#ifdef DEBUG + const char* className = compiler->eeGetClassName(arrayHandle); + const char* shortClassName = compiler->eeGetShortClassName(arrayHandle); + builder.SetName(className, shortClassName); +#endif + + return builder; +} + //------------------------------------------------------------------------ // GetOrCreateGCPtrs: Get or create the array indicating GC pointer types. // diff --git a/src/coreclr/jit/layout.h b/src/coreclr/jit/layout.h index abe3d642ef8f1a..03708a007052d4 100644 --- a/src/coreclr/jit/layout.h +++ b/src/coreclr/jit/layout.h @@ -41,6 +41,8 @@ class ClassLayoutBuilder #ifdef DEBUG void SetName(const char* name, const char* shortName); #endif + + static ClassLayoutBuilder BuildArray(Compiler* compiler, CORINFO_CLASS_HANDLE arrayType, unsigned length); }; // Encapsulates layout information about a class (typically a value class but this can also be diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index ec3b37eb15901e..fce4152d0a0620 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -828,7 +828,7 @@ unsigned int ObjectAllocator::MorphNewArrNodeIntoStackAlloc(GenTreeCall* blockSize = AlignUp(blockSize, 8); } - comp->lvaSetStruct(lclNum, comp->typGetBlkLayout(blockSize), /* unsafeValueClsCheck */ false); + comp->lvaSetStruct(lclNum, comp->typGetArrayLayout(clsHnd, length), /* unsafe */ false); lclDsc->lvStackAllocatedObject = true; // Initialize the object memory if necessary. diff --git a/src/coreclr/jit/objectalloc.h b/src/coreclr/jit/objectalloc.h index 593e7b7915335c..99dd8965924669 100644 --- a/src/coreclr/jit/objectalloc.h +++ b/src/coreclr/jit/objectalloc.h @@ -359,30 +359,8 @@ inline bool ObjectAllocator::CanAllocateLclVarOnStack(unsigned int lclNu return false; } - CORINFO_CLASS_HANDLE elemClsHnd = NO_CLASS_HANDLE; - CorInfoType corType = comp->info.compCompHnd->getChildType(clsHnd, &elemClsHnd); - var_types type = JITtype2varType(corType); - ClassLayout* elemLayout = type == TYP_STRUCT ? comp->typGetObjLayout(elemClsHnd) : nullptr; - - if (varTypeIsGC(type) || ((elemLayout != nullptr) && elemLayout->HasGCPtr())) - { - *reason = "[array contains gc refs]"; - return false; - } - - const unsigned elemSize = elemLayout != nullptr ? elemLayout->GetSize() : genTypeSize(type); - - ClrSafeInt totalSize(elemSize); - totalSize *= static_cast(length); - totalSize += static_cast(OFFSETOF__CORINFO_Array__data); - - if (totalSize.IsOverflow()) - { - *reason = "[overflow array length]"; - return false; - } - - classSize = totalSize.Value(); + ClassLayout* const layout = comp->typGetArrayLayout(clsHnd, (unsigned)length); + classSize = layout->GetSize(); } else if (allocType == OAT_NEWOBJ) {