diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index cad4ecb5a864e6..f40de4429071a7 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -10945,10 +10945,6 @@ class Compiler unsigned typGetBlkLayoutNum(unsigned blockSize); // Get the layout for the specified array of known length ClassLayout* typGetArrayLayout(CORINFO_CLASS_HANDLE classHandle, unsigned length); - // Get a layout like an existing layout, with all gc refs removed - ClassLayout* typGetNonGCLayout(ClassLayout* existingLayout); - // Get a layout like an existing layout, with all gc refs changed to byrefs - ClassLayout* typGetByrefLayout(ClassLayout* existingLayout); 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 13c5f8774da9e4..1160300ad6698c 100644 --- a/src/coreclr/jit/layout.cpp +++ b/src/coreclr/jit/layout.cpp @@ -420,33 +420,6 @@ ClassLayout* Compiler::typGetArrayLayout(CORINFO_CLASS_HANDLE classHandle, unsig return typGetCustomLayout(b); } -ClassLayout* Compiler::typGetNonGCLayout(ClassLayout* layout) -{ - assert(layout->HasGCPtr()); - ClassLayoutBuilder b(this, layout->GetSize()); - b.CopyPaddingFrom(0, layout); - -#ifdef DEBUG - b.CopyNameFrom(layout, "[nongc] "); -#endif - - return typGetCustomLayout(b); -} - -ClassLayout* Compiler::typGetByrefLayout(ClassLayout* layout) -{ - assert(layout->HasGCPtr()); - ClassLayoutBuilder b(this, layout->GetSize()); - b.CopyPaddingFrom(0, layout); - b.CopyGCInfoFromMakeByref(0, layout); - -#ifdef DEBUG - b.CopyNameFrom(layout, "[byref] "); -#endif - - return typGetCustomLayout(b); -} - #ifdef DEBUG //------------------------------------------------------------------------ // CopyNameFrom: Copy layout names, with optional prefix. @@ -462,21 +435,17 @@ void ClassLayoutBuilder::CopyNameFrom(ClassLayout* layout, const char* prefix) if (prefix != nullptr) { - char* newName = nullptr; - char* newShortName = nullptr; + const char* newName = nullptr; + const char* newShortName = nullptr; if (layoutName != nullptr) { - size_t len = strlen(prefix) + strlen(layoutName) + 1; - newName = m_compiler->getAllocator(CMK_DebugOnly).allocate(len); - sprintf_s(newName, len, "%s%s", prefix, layoutShortName); + newName = m_compiler->printfAlloc("%s%.100s", prefix, layoutName); } if (layoutShortName != nullptr) { - size_t len = strlen(prefix) + strlen(layoutName) + 1; - newShortName = m_compiler->getAllocator(CMK_DebugOnly).allocate(len); - sprintf_s(newShortName, len, "%s%s", prefix, layoutShortName); + newShortName = m_compiler->printfAlloc("%s%.100s", prefix, layoutShortName); } SetName(newName, newShortName); @@ -708,8 +677,8 @@ const SegmentList& ClassLayout::GetNonPadding(Compiler* comp) // AreCompatible: check if 2 layouts are the same for copying. // // Arguments: -// layout1 - the first layout (copy destination) -// layout2 - the second layout (copy source) +// layout1 - the first layout +// layout2 - the second layout // // Return value: // true if compatible, false otherwise. @@ -773,8 +742,6 @@ bool ClassLayout::AreCompatible(const ClassLayout* layout1, const ClassLayout* l return true; } - assert(clsHnd1 != NO_CLASS_HANDLE); - assert(clsHnd2 != NO_CLASS_HANDLE); assert(layout1->HasGCPtr() && layout2->HasGCPtr()); if (layout1->GetGCPtrCount() != layout2->GetGCPtrCount()) @@ -815,16 +782,13 @@ bool ClassLayout::CanAssignFrom(const ClassLayout* layout) return true; } - // Do the normal compatibility check first, when possible to do so. + // Do the normal compatibility check first // - if ((IsCustomLayout() == layout->IsCustomLayout()) || (!HasGCPtr() && !layout->HasGCPtr())) - { - const bool areCompatible = AreCompatible(this, layout); + const bool areCompatible = AreCompatible(this, layout); - if (areCompatible) - { - return true; - } + if (areCompatible) + { + return true; } // Must be same size @@ -1087,34 +1051,6 @@ void ClassLayoutBuilder::CopyGCInfoFrom(unsigned offset, ClassLayout* layout) } } -//------------------------------------------------------------------------ -// CopyInfoGCFromMakeByref: Copy GC pointers from another layout,and change -// all gc references to be TYP_BYREF (TYPE_GC_BYREF) -// -// Arguments: -// offset - Offset in this builder to start copy information into. -// layout - Layout to get information from. -// -void ClassLayoutBuilder::CopyGCInfoFromMakeByref(unsigned offset, ClassLayout* layout) -{ - assert(offset + layout->GetSize() <= m_size); - - if (layout->GetGCPtrCount() > 0) - { - assert(offset % TARGET_POINTER_SIZE == 0); - unsigned startSlot = offset / TARGET_POINTER_SIZE; - for (unsigned slot = 0; slot < layout->GetSlotCount(); slot++) - { - CorInfoGCType gcType = layout->GetGCPtr(slot); - if (gcType == TYPE_GC_REF) - { - gcType = TYPE_GC_BYREF; - } - SetGCPtr(startSlot + slot, gcType); - } - } -} - //------------------------------------------------------------------------ // CopyInfoPaddingFrom: Copy padding from another layout. // diff --git a/src/coreclr/jit/layout.h b/src/coreclr/jit/layout.h index dee61727e115e5..1f5918840057a4 100644 --- a/src/coreclr/jit/layout.h +++ b/src/coreclr/jit/layout.h @@ -35,7 +35,6 @@ class ClassLayoutBuilder void SetGCPtrType(unsigned slot, var_types type); void CopyGCInfoFrom(unsigned offset, ClassLayout* layout); - void CopyGCInfoFromMakeByref(unsigned offset, ClassLayout* layout); void CopyPaddingFrom(unsigned offset, ClassLayout* layout); void AddPadding(const SegmentList::Segment& padding); void RemovePadding(const SegmentList::Segment& nonPadding); diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index e1f538aea8d18d..c45af95d17e86a 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -960,12 +960,6 @@ bool ObjectAllocator::CanAllocateLclVarOnStack(unsigned int lclNum, return false; } - if (comp->info.compCompHnd->getTypeForBoxOnStack(clsHnd) == NO_CLASS_HANDLE) - { - *reason = "[no boxed type available]"; - return false; - } - classSize = comp->info.compCompHnd->getClassSize(clsHnd); } else @@ -1061,6 +1055,7 @@ ObjectAllocator::ObjectAllocationType ObjectAllocator::AllocationKind(GenTree* t return allocType; } +//------------------------------------------------------------------------ // MorphAllocObjNodes: Morph each GT_ALLOCOBJ node either into an // allocation helper call or stack allocation. // @@ -1203,13 +1198,11 @@ bool ObjectAllocator::MorphAllocObjNodes() //------------------------------------------------------------------------ CORINFO_CLASS_HANDLE clsHnd = data->AsAllocObj()->gtAllocObjClsHnd; - CORINFO_CLASS_HANDLE stackClsHnd = clsHnd; const bool isValueClass = comp->info.compCompHnd->isValueClass(clsHnd); if (isValueClass) { comp->Metrics.NewBoxedValueClassHelperCalls++; - stackClsHnd = comp->info.compCompHnd->getTypeForBoxOnStack(clsHnd); } else { @@ -1221,29 +1214,31 @@ bool ObjectAllocator::MorphAllocObjNodes() // reason set by the call canStack = false; } - else if (stackClsHnd == NO_CLASS_HANDLE) - { - assert(isValueClass); - onHeapReason = "[no class handle for this boxed value class]"; - canStack = false; - } else { JITDUMP("Allocating V%02u on the stack\n", lclNum); canStack = true; - const unsigned int stackLclNum = - MorphAllocObjNodeIntoStackAlloc(data->AsAllocObj(), stackClsHnd, isValueClass, block, stmt); - m_HeapLocalToStackLocalMap.AddOrUpdate(lclNum, stackLclNum); + + ClassLayout* layout = nullptr; if (isValueClass) { + CORINFO_CLASS_HANDLE boxedClsHnd = comp->info.compCompHnd->getTypeForBox(clsHnd); + assert(boxedClsHnd != NO_CLASS_HANDLE); + ClassLayout* structLayout = comp->typGetObjLayout(boxedClsHnd); + layout = GetBoxedLayout(structLayout); comp->Metrics.StackAllocatedBoxedValueClasses++; } else { + layout = comp->typGetObjLayout(clsHnd); comp->Metrics.StackAllocatedRefClasses++; } + const unsigned int stackLclNum = + MorphAllocObjNodeIntoStackAlloc(data->AsAllocObj(), layout, block, stmt); + m_HeapLocalToStackLocalMap.AddOrUpdate(lclNum, stackLclNum); + bashCall = true; } } @@ -1449,8 +1444,7 @@ unsigned int ObjectAllocator::MorphNewArrNodeIntoStackAlloc(GenTreeCall* // allocation. // Arguments: // allocObj - GT_ALLOCOBJ that will be replaced by a stack allocation -// clsHnd - class representing the stack allocated object -// isValueClass - we are stack allocating a boxed value class +// layout - layout for the stack allocated objectd // block - a basic block where allocObj is // stmt - a statement where allocObj is // @@ -1460,24 +1454,29 @@ unsigned int ObjectAllocator::MorphNewArrNodeIntoStackAlloc(GenTreeCall* // Notes: // This function can insert additional statements before stmt. // -unsigned int ObjectAllocator::MorphAllocObjNodeIntoStackAlloc( - GenTreeAllocObj* allocObj, CORINFO_CLASS_HANDLE clsHnd, bool isValueClass, BasicBlock* block, Statement* stmt) +unsigned int ObjectAllocator::MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* allocObj, + ClassLayout* layout, + BasicBlock* block, + Statement* stmt) { assert(allocObj != nullptr); assert(m_AnalysisDone); - assert(clsHnd != NO_CLASS_HANDLE); - const bool shortLifetime = false; - const unsigned int lclNum = comp->lvaGrabTemp(shortLifetime DEBUGARG( - isValueClass ? "stack allocated boxed value class temp" : "stack allocated ref class temp")); +#ifdef DEBUG + const char* lclName = comp->printfAlloc("stack allocated %.110s", layout->GetShortClassName()); +#endif - comp->lvaSetStruct(lclNum, clsHnd, /* unsafeValueClsCheck */ false); + const bool shortLifetime = false; + const unsigned int lclNum = comp->lvaGrabTemp(shortLifetime DEBUGARG(lclName)); + comp->lvaSetStruct(lclNum, layout, /* unsafeValueClsCheck */ false); - // Initialize the object memory if necessary. - bool bbInALoop = block->HasFlag(BBF_BACKWARD_JUMP); - bool bbIsReturn = block->KindIs(BBJ_RETURN); LclVarDsc* const lclDsc = comp->lvaGetDesc(lclNum); lclDsc->lvStackAllocatedObject = true; + + // Initialize the object memory if necessary. + bool bbInALoop = block->HasFlag(BBF_BACKWARD_JUMP); + bool bbIsReturn = block->KindIs(BBJ_RETURN); + if (comp->fgVarNeedsExplicitZeroInit(lclNum, bbInALoop, bbIsReturn)) { //------------------------------------------------------------------------ @@ -2317,7 +2316,7 @@ void ObjectAllocator::RewriteUses() if (newType == TYP_I_IMPL) { // New layout with no gc refs + padding - newLayout = comp->typGetNonGCLayout(layout); + newLayout = GetNonGCLayout(layout); JITDUMP("Changing layout of struct V%02u to block\n", lclNum); lclVarDsc->ChangeLayout(newLayout); } @@ -2325,7 +2324,7 @@ void ObjectAllocator::RewriteUses() { // New layout with all gc refs as byrefs + padding // (todo, perhaps: see if old layout was already all byrefs) - newLayout = comp->typGetByrefLayout(layout); + newLayout = GetByrefLayout(layout); JITDUMP("Changing layout of struct V%02u to byref\n", lclNum); lclVarDsc->ChangeLayout(newLayout); } @@ -4077,3 +4076,80 @@ void ObjectAllocator::CloneAndSpecialize() assert(numberOfClonedRegions == m_regionsToClone); } + +//------------------------------------------------------------------------------ +// GetBoxedLayout: get a layout for a boxed version of a struct +// +// Arguments: +// layout - layout of the struct +// +// Notes: +// For Nullable, layout class should be T +// +ClassLayout* ObjectAllocator::GetBoxedLayout(ClassLayout* layout) +{ + assert(layout->IsValueClass()); + + ClassLayoutBuilder b(comp, TARGET_POINTER_SIZE + layout->GetSize()); + b.CopyPaddingFrom(TARGET_POINTER_SIZE, layout); + b.CopyGCInfoFrom(TARGET_POINTER_SIZE, layout); + +#ifdef DEBUG + b.CopyNameFrom(layout, "[boxed] "); +#endif + + return comp->typGetCustomLayout(b); +} + +//------------------------------------------------------------------------------ +// GetNonGCLayout: get a layout with the same size and padding as an existing +// layout, but with no GC fields. +// +// Arguments: +// layout - existing layout to use as template +// +ClassLayout* ObjectAllocator::GetNonGCLayout(ClassLayout* layout) +{ + assert(layout->HasGCPtr()); + ClassLayoutBuilder b(comp, layout->GetSize()); + b.CopyPaddingFrom(0, layout); + +#ifdef DEBUG + b.CopyNameFrom(layout, "[nongc] "); +#endif + + return comp->typGetCustomLayout(b); +} + +//------------------------------------------------------------------------------ +// GetByrefLayout: get a layout with the same size and padding as an existing +// layout, but with all GC fields retyped to byref. +// +// Arguments: +// layout - existing layout to use as template +// +ClassLayout* ObjectAllocator::GetByrefLayout(ClassLayout* layout) +{ + assert(layout->HasGCPtr()); + ClassLayoutBuilder b(comp, layout->GetSize()); + b.CopyPaddingFrom(0, layout); + + if (layout->GetGCPtrCount() > 0) + { + for (unsigned slot = 0; slot < layout->GetSlotCount(); slot++) + { + var_types gcType = layout->GetGCPtrType(slot); + if (gcType == TYP_REF) + { + gcType = TYP_BYREF; + } + b.SetGCPtrType(slot, gcType); + } + } + +#ifdef DEBUG + b.CopyNameFrom(layout, "[byref] "); +#endif + + return comp->typGetCustomLayout(b); +} diff --git a/src/coreclr/jit/objectalloc.h b/src/coreclr/jit/objectalloc.h index 624368ca37ed65..b3d07d7eb060cc 100644 --- a/src/coreclr/jit/objectalloc.h +++ b/src/coreclr/jit/objectalloc.h @@ -190,8 +190,10 @@ class ObjectAllocator final : public Phase bool MorphAllocObjNodes(); void RewriteUses(); GenTree* MorphAllocObjNodeIntoHelperCall(GenTreeAllocObj* allocObj); - unsigned int MorphAllocObjNodeIntoStackAlloc( - GenTreeAllocObj* allocObj, CORINFO_CLASS_HANDLE clsHnd, bool isValueClass, BasicBlock* block, Statement* stmt); + unsigned int MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* allocObj, + ClassLayout* layout, + BasicBlock* block, + Statement* stmt); unsigned int MorphNewArrNodeIntoStackAlloc(GenTreeCall* newArr, CORINFO_CLASS_HANDLE clsHnd, unsigned int length, @@ -230,6 +232,10 @@ class ObjectAllocator final : public Phase static const unsigned int s_StackAllocMaxSize = 0x2000U; + ClassLayout* GetBoxedLayout(ClassLayout* structLayout); + ClassLayout* GetNonGCLayout(ClassLayout* existingLayout); + ClassLayout* GetByrefLayout(ClassLayout* existingLayout); + #ifdef DEBUG void DumpIndex(unsigned bvIndex); #endif