From e333c8f164f1193d713f39ffe7f7f9ea47d23d40 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 20 May 2021 16:01:24 -0700 Subject: [PATCH 01/45] wip --- src/coreclr/jit/jitconfigvalues.h | 2 + src/coreclr/jit/layout.cpp | 84 +++++++++++++++++++++++-------- src/coreclr/jit/layout.h | 31 ++++++++++-- src/coreclr/jit/lclvars.cpp | 15 +++++- src/coreclr/jit/objectalloc.cpp | 55 ++++++++++++++------ src/coreclr/jit/objectalloc.h | 63 ++++++++++++++++++----- src/coreclr/vm/jitinterface.cpp | 16 ++++-- 7 files changed, 208 insertions(+), 58 deletions(-) diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index dbd56791d88d8..65b626aece925 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -652,6 +652,8 @@ RELEASE_CONFIG_INTEGER(JitInlinePolicyModel, W("JitInlinePolicyModel"), 0) RELEASE_CONFIG_INTEGER(JitInlinePolicyProfile, W("JitInlinePolicyProfile"), 0) RELEASE_CONFIG_INTEGER(JitInlinePolicyProfileThreshold, W("JitInlinePolicyProfileThreshold"), 40) RELEASE_CONFIG_INTEGER(JitObjectStackAllocation, W("JitObjectStackAllocation"), 0) +RELEASE_CONFIG_INTEGER(JitObjectStackAllocationRefClass, W("JitObjectStackAllocationRefClass"), 0) +RELEASE_CONFIG_INTEGER(JitObjectStackAllocationBoxedValueClass, W("JitObjectStackAllocationBoxedValueClass"), 0) RELEASE_CONFIG_INTEGER(JitEECallTimingInfo, W("JitEECallTimingInfo"), 0) diff --git a/src/coreclr/jit/layout.cpp b/src/coreclr/jit/layout.cpp index 8de93f838971f..e0785139f7a14 100644 --- a/src/coreclr/jit/layout.cpp +++ b/src/coreclr/jit/layout.cpp @@ -20,23 +20,25 @@ class ClassLayoutTable typedef JitHashTable, unsigned> BlkLayoutIndexMap; typedef JitHashTable, unsigned> ObjLayoutIndexMap; + typedef JitHashTable, unsigned> BoxLayoutIndexMap; union { - // Up to 3 layouts can be stored "inline" and finding a layout by handle/size can be done using linear search. + // Up to 4 layouts can be stored "inline" and finding a layout by handle/size can be done using linear search. // Most methods need no more than 2 layouts. - ClassLayout* m_layoutArray[3]; + ClassLayout* m_layoutArray[4]; // Otherwise a dynamic array is allocated and hashtables are used to map from handle/size to layout array index. struct { ClassLayout** m_layoutLargeArray; BlkLayoutIndexMap* m_blkLayoutMap; ObjLayoutIndexMap* m_objLayoutMap; + BoxLayoutIndexMap* m_boxLayoutMap; }; }; // The number of layout objects stored in this table. unsigned m_layoutCount; - // The capacity of m_layoutLargeArray (when more than 3 layouts are stored). + // The capacity of m_layoutLargeArray (when more than 4 layouts are stored). unsigned m_layoutLargeCapacity; // We furthermore fast-path the 0-sized block layout which is used for // block locals that may grow (e.g. the outgoing arg area in every non-x86 @@ -98,9 +100,9 @@ class ClassLayoutTable } // Get the layout for the specified class handle. - ClassLayout* GetObjLayout(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle) + ClassLayout* GetObjLayout(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) { - return GetLayoutByIndex(GetObjLayoutIndex(compiler, classHandle)); + return GetLayoutByIndex(GetObjLayoutIndex(compiler, classHandle, isBoxedValueClass)); } // Get a number that uniquely identifies a layout for the specified class handle. @@ -147,7 +149,9 @@ class ClassLayoutTable else { unsigned index = 0; + if ((layout->IsBlockLayout() && m_blkLayoutMap->Lookup(layout->GetSize(), &index)) || + (layout->IsBoxedValueClass() && m_boxLayoutMap->Lookup(layout->GetClassHandle(), &index)) || m_objLayoutMap->Lookup(layout->GetClassHandle(), &index)) { return index; @@ -202,7 +206,7 @@ class ClassLayoutTable return index; } - unsigned GetObjLayoutIndex(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle) + unsigned GetObjLayoutIndex(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) { assert(classHandle != NO_CLASS_HANDLE); @@ -210,12 +214,21 @@ class ClassLayoutTable { for (unsigned i = 0; i < m_layoutCount; i++) { - if (m_layoutArray[i]->GetClassHandle() == classHandle) + if ((m_layoutArray[i]->GetClassHandle() == classHandle) && + (isBoxedValueClass == m_layoutArray[i]->IsBoxedValueClass())) { return i; } } } + else if (isBoxedValueClass) + { + unsigned index; + if (m_boxLayoutMap->Lookup(classHandle, &index)) + { + return index; + } + } else { unsigned index; @@ -225,12 +238,12 @@ class ClassLayoutTable } } - return AddObjLayout(compiler, CreateObjLayout(compiler, classHandle)); + return AddObjLayout(compiler, CreateObjLayout(compiler, classHandle, isBoxedValueClass)); } - ClassLayout* CreateObjLayout(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle) + ClassLayout* CreateObjLayout(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) { - return ClassLayout::Create(compiler, classHandle); + return ClassLayout::Create(compiler, classHandle, isBoxedValueClass); } unsigned AddObjLayout(Compiler* compiler, ClassLayout* layout) @@ -242,7 +255,15 @@ class ClassLayoutTable } unsigned index = AddLayoutLarge(compiler, layout); - m_objLayoutMap->Set(layout->GetClassHandle(), index); + + if (layout->IsBoxedValueClass()) + { + m_boxLayoutMap->Set(layout->GetClassHandle(), index); + } + else + { + m_objLayoutMap->Set(layout->GetClassHandle(), index); + } return index; } @@ -258,6 +279,7 @@ class ClassLayoutTable { BlkLayoutIndexMap* blkLayoutMap = new (alloc) BlkLayoutIndexMap(alloc); ObjLayoutIndexMap* objLayoutMap = new (alloc) ObjLayoutIndexMap(alloc); + BoxLayoutIndexMap* boxLayoutMap = new (alloc) BoxLayoutIndexMap(alloc); for (unsigned i = 0; i < m_layoutCount; i++) { @@ -268,6 +290,10 @@ class ClassLayoutTable { blkLayoutMap->Set(l->GetSize(), i); } + else if (l->IsBoxedValueClass()) + { + boxLayoutMap->Set(l->GetClassHandle(), i); + } else { objLayoutMap->Set(l->GetClassHandle(), i); @@ -276,6 +302,7 @@ class ClassLayoutTable m_blkLayoutMap = blkLayoutMap; m_objLayoutMap = objLayoutMap; + m_boxLayoutMap = boxLayoutMap; } else { @@ -344,27 +371,35 @@ ClassLayout* Compiler::typGetBlkLayout(unsigned blockSize) return typGetClassLayoutTable()->GetBlkLayout(this, blockSize); } -unsigned Compiler::typGetObjLayoutNum(CORINFO_CLASS_HANDLE classHandle) +unsigned Compiler::typGetObjLayoutNum(CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) { - return typGetClassLayoutTable()->GetObjLayoutNum(this, classHandle); + return typGetClassLayoutTable()->GetObjLayoutNum(this, classHandle, isBoxedValueClass); } -ClassLayout* Compiler::typGetObjLayout(CORINFO_CLASS_HANDLE classHandle) +ClassLayout* Compiler::typGetObjLayout(CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) { - return typGetClassLayoutTable()->GetObjLayout(this, classHandle); + ClassLayout* const result = typGetClassLayoutTable()->GetObjLayout(this, classHandle, isBoxedValueClass); + assert(result->IsBoxedValueClass() == isBoxedValueClass); + return result; } -ClassLayout* ClassLayout::Create(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle) +ClassLayout* ClassLayout::Create(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) { - bool isValueClass = compiler->eeIsValueClass(classHandle); + bool isValueClass = compiler->info.compCompHnd->isValueClass(classHandle); unsigned size; if (isValueClass) { size = compiler->info.compCompHnd->getClassSize(classHandle); + + if (isBoxedValueClass) + { + size += TARGET_POINTER_SIZE; + } } else { + assert(!isBoxedValueClass); size = compiler->info.compCompHnd->getHeapClassSize(classHandle); } @@ -374,9 +409,8 @@ ClassLayout* ClassLayout::Create(Compiler* compiler, CORINFO_CLASS_HANDLE classH INDEBUG(const char* shortClassName = compiler->eeGetShortClassName(classHandle);) ClassLayout* layout = new (compiler, CMK_ClassLayout) - ClassLayout(classHandle, isValueClass, size, type DEBUGARG(className) DEBUGARG(shortClassName)); + ClassLayout(classHandle, isValueClass, isBoxedValueClass, size DEBUGARG(className)); layout->InitializeGCPtrs(compiler); - return layout; } @@ -405,14 +439,15 @@ void ClassLayout::InitializeGCPtrs(Compiler* compiler) gcPtrs = m_gcPtrsArray; } + // The gcPtrs array will have the right size for boxed value classes, but all + // entries will be shifted down one slot to match the unboxed rep. + // unsigned gcPtrCount = compiler->info.compCompHnd->getClassGClayout(m_classHandle, gcPtrs); assert((gcPtrCount == 0) || ((compiler->info.compCompHnd->getClassAttribs(m_classHandle) & (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_BYREF_LIKE)) != 0)); - // Since class size is unsigned there's no way we could have more than 2^30 slots - // so it should be safe to fit this into a 30 bits bit field. - assert(gcPtrCount < (1 << 30)); + assert(gcPtrCount < (1 << 24)); m_gcPtrCount = gcPtrCount; } @@ -503,6 +538,11 @@ bool ClassLayout::AreCompatible(const ClassLayout* layout1, const ClassLayout* l return true; } + if (layout1->IsBoxedValueClass() != layout2->IsBoxedValueClass()) + { + return false; + } + if (layout1->GetSize() != layout2->GetSize()) { return false; diff --git a/src/coreclr/jit/layout.h b/src/coreclr/jit/layout.h index c2c901d1f33c9..4515db9f1501f 100644 --- a/src/coreclr/jit/layout.h +++ b/src/coreclr/jit/layout.h @@ -22,10 +22,12 @@ class ClassLayout const unsigned m_size; const unsigned m_isValueClass : 1; + const unsigned m_isBoxedValueClass : 1; + INDEBUG(unsigned m_gcPtrsInitialized : 1;) - // The number of GC pointers in this layout. Since the maximum size is 2^32-1 the count - // can fit in at most 30 bits. - unsigned m_gcPtrCount : 30; + // The number of GC pointers in this layout. Since the the maximum size is 2^32-1 the count + // TODO: show 24 bits suffices + unsigned m_gcPtrCount : 24; // Array of CorInfoGCType (as BYTE) that describes the GC layout of the class. // For small classes the array is stored inline, avoiding an extra allocation @@ -52,6 +54,7 @@ class ClassLayout : m_classHandle(NO_CLASS_HANDLE) , m_size(size) , m_isValueClass(false) + , m_isBoxedValueClass(false) #ifdef DEBUG , m_gcPtrsInitialized(true) #endif @@ -65,15 +68,19 @@ class ClassLayout { } - static ClassLayout* Create(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle); + static ClassLayout* Create(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass = false); ClassLayout(CORINFO_CLASS_HANDLE classHandle, bool isValueClass, unsigned size, - var_types type DEBUGARG(const char* className) DEBUGARG(const char* shortClassName)) + ClassLayout(CORINFO_CLASS_HANDLE classHandle, bool isValueClass, unsigned size DEBUGARG(const char* className)) + bool isValueClass, + bool isBoxedValueClass, + unsigned size DEBUGARG(const char* className)) : m_classHandle(classHandle) , m_size(size) , m_isValueClass(isValueClass) + , m_isBoxedValueClass(isBoxedValueClass) #ifdef DEBUG , m_gcPtrsInitialized(false) #endif @@ -120,6 +127,11 @@ class ClassLayout return m_isValueClass; } + bool IsBoxedValueClass() const + { + return m_isBoxedValueClass; + } + unsigned GetSize() const { return m_size; @@ -240,6 +252,15 @@ class ClassLayout return TYPE_GC_NONE; } + if (m_isBoxedValueClass) + { + if (slot == 0) + { + return TYPE_GC_NONE; + } + return static_cast(GetGCPtrs()[slot - 1]); + } + return static_cast(GetGCPtrs()[slot]); } }; diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 889eacefd29f1..d42dd5ef16c26 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -3334,6 +3334,8 @@ bool Compiler::lvaIsLocalImplicitlyAccessedByRef(unsigned lclNum) const // this information is already available on the CallArgABIInformation, and shouldn't need to be // recomputed. // +// Also seems like this info could be cached in the layout. +// bool Compiler::lvaIsMultiregStruct(LclVarDsc* varDsc, bool isVarArg) { if (varTypeIsStruct(varDsc->TypeGet())) @@ -3377,11 +3379,21 @@ void Compiler::lvaSetStruct(unsigned varNum, ClassLayout* layout, bool unsafeVal { varDsc->lvType = TYP_STRUCT; } + if (varDsc->GetLayout() == nullptr) { + ClassLayout* layout = typGetObjLayout(typeHnd); + + if (isBoxedValueClass) + { + assert(layout->GetSize() > TARGET_POINTER_SIZE); + } + varDsc->SetLayout(layout); - if (layout->IsValueClass()) + // Boxed value classes are always passed explicitly by ref, and are never simd/hfa. + // + if (layout->IsValueClass() && !isBoxedValueClass) { varDsc->lvType = layout->GetType(); @@ -3422,6 +3434,7 @@ void Compiler::lvaSetStruct(unsigned varNum, ClassLayout* layout, bool unsafeVal } else { + assert(!isBoxedValueClass); assert(ClassLayout::AreCompatible(varDsc->GetLayout(), layout)); // Inlining could replace a canon struct type with an exact one. varDsc->SetLayout(layout); diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 3d6206e0f723c..a1417a76a6f2b 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -389,12 +389,32 @@ bool ObjectAllocator::MorphAllocObjNodes() GenTreeAllocObj* asAllocObj = data->AsAllocObj(); unsigned int lclNum = stmtExpr->AsLclVar()->GetLclNum(); CORINFO_CLASS_HANDLE clsHnd = data->AsAllocObj()->gtAllocObjClsHnd; + const char* onHeapReason = nullptr; + bool canStack = false; // Don't attempt to do stack allocations inside basic blocks that may be in a loop. - if (IsObjectStackAllocationEnabled() && !basicBlockHasBackwardJump && - CanAllocateLclVarOnStack(lclNum, clsHnd)) + // + if (!IsObjectStackAllocationEnabled()) { - JITDUMP("Allocating local variable V%02u on the stack\n", lclNum); + onHeapReason = "[object stack allocation disabled]"; + canStack = false; + } + else if (basicBlockHasBackwardJump) + { + onHeapReason = "[alloc in loop]"; + canStack = false; + } + else if (!CanAllocateLclVarOnStack(lclNum, clsHnd, &onHeapReason)) + { + // reason set by the call + canStack = false; + } + else + { + JITDUMP("Allocating V%02u on the stack\n", lclNum); + canStack = true; + + printf("@@@ SA V%02u (%s) in %s\n", lclNum, comp->eeGetClassName(clsHnd), comp->info.compFullName); const unsigned int stackLclNum = MorphAllocObjNodeIntoStackAlloc(asAllocObj, block, stmt); m_HeapLocalToStackLocalMap.AddOrUpdate(lclNum, stackLclNum); @@ -406,13 +426,11 @@ bool ObjectAllocator::MorphAllocObjNodes() comp->optMethodFlags |= OMF_HAS_OBJSTACKALLOC; didStackAllocate = true; } - else - { - if (IsObjectStackAllocationEnabled()) - { - JITDUMP("Allocating local variable V%02u on the heap\n", lclNum); - } + if (!canStack) + { + assert(onHeapReason != nullptr); + JITDUMP("Allocating V%02u on the heap: %s\n", lclNum, onHeapReason); data = MorphAllocObjNodeIntoHelperCall(asAllocObj); stmtExpr->AsLclVar()->Data() = data; stmtExpr->AddAllEffectsFlags(data); @@ -504,10 +522,12 @@ unsigned int ObjectAllocator::MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* a assert(allocObj != nullptr); assert(m_AnalysisDone); - const bool shortLifetime = false; - const unsigned int lclNum = comp->lvaGrabTemp(shortLifetime DEBUGARG("MorphAllocObjNodeIntoStackAlloc temp")); - const int unsafeValueClsCheck = true; - comp->lvaSetStruct(lclNum, allocObj->gtAllocObjClsHnd, unsafeValueClsCheck); + const bool isValueClass = comp->info.compCompHnd->isValueClass(allocObj->gtAllocObjClsHnd); + const bool shortLifetime = false; + + const unsigned int lclNum = comp->lvaGrabTemp(shortLifetime DEBUGARG( + isValueClass ? "stack allocated boxed value class temp" : "stack allocated ref class temp")); + comp->lvaSetStruct(lclNum, allocObj->gtAllocObjClsHnd, true, false, isValueClass); // Initialize the object memory if necessary. bool bbInALoop = block->HasFlag(BBF_BACKWARD_JUMP); @@ -533,6 +553,8 @@ unsigned int ObjectAllocator::MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* a comp->compSuppressedZeroInit = true; } + // Initialize the vtable slot. + // //------------------------------------------------------------------------ // STMTx (IL 0x... ???) // * STORE_LCL_FLD long @@ -584,6 +606,8 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack* parent GenTree* parent = parentStack->Top(parentIndex); keepChecking = false; + JITDUMP("... L%02u ... checking [%06u]\n", lclNum, comp->dspTreeID(parent)); + switch (parent->OperGet()) { // Update the connection graph if we are storing to a local. @@ -601,6 +625,7 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack* parent case GT_EQ: case GT_NE: + case GT_NULLCHECK: canLclVarEscapeViaParentStack = false; break; @@ -615,7 +640,7 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack* parent case GT_COLON: case GT_QMARK: case GT_ADD: - case GT_FIELD_ADDR: + case GT_BOX: // Check whether the local escapes via its grandparent. ++parentIndex; keepChecking = true; @@ -696,6 +721,8 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p case GT_EQ: case GT_NE: + case GT_NULLCHECK: + case GT_BOX: break; case GT_COMMA: diff --git a/src/coreclr/jit/objectalloc.h b/src/coreclr/jit/objectalloc.h index 07307161da002..d5a57044fed2b 100644 --- a/src/coreclr/jit/objectalloc.h +++ b/src/coreclr/jit/objectalloc.h @@ -47,7 +47,7 @@ class ObjectAllocator final : public Phase virtual PhaseStatus DoPhase() override; private: - bool CanAllocateLclVarOnStack(unsigned int lclNum, CORINFO_CLASS_HANDLE clsHnd); + bool CanAllocateLclVarOnStack(unsigned int lclNum, CORINFO_CLASS_HANDLE clsHnd, const char** reason); bool CanLclVarEscape(unsigned int lclNum); void MarkLclVarAsPossiblyStackPointing(unsigned int lclNum); void MarkLclVarAsDefinitelyStackPointing(unsigned int lclNum); @@ -110,34 +110,71 @@ inline void ObjectAllocator::EnableObjectStackAllocation() // // Arguments: // lclNum - Local variable number -// clsHnd - Class handle of the variable class +// clsHnd - Class/struct handle of the variable class +// reason - [out, required] if result is false, reason why // // Return Value: // Returns true iff local variable can be allocated on the stack. // -// Notes: -// Stack allocation of objects with gc fields and boxed objects is currently disabled. - -inline bool ObjectAllocator::CanAllocateLclVarOnStack(unsigned int lclNum, CORINFO_CLASS_HANDLE clsHnd) +inline bool ObjectAllocator::CanAllocateLclVarOnStack(unsigned int lclNum, + CORINFO_CLASS_HANDLE clsHnd, + const char** reason) { assert(m_AnalysisDone); - DWORD classAttribs = comp->info.compCompHnd->getClassAttribs(clsHnd); + bool enableBoxedValueClasses = true; + bool enableRefClasses = true; + *reason = "[ok]"; + +#ifdef DEBUG + enableBoxedValueClasses = (JitConfig.JitObjectStackAllocationBoxedValueClass() != 0); + enableRefClasses = (JitConfig.JitObjectStackAllocationRefClass() != 0); +#endif + + unsigned int classSize = 0; - if ((classAttribs & CORINFO_FLG_VALUECLASS) != 0) + if (comp->info.compCompHnd->isValueClass(clsHnd)) { - // TODO-ObjectStackAllocation: enable stack allocation of boxed structs - return false; + if (!enableBoxedValueClasses) + { + *reason = "[disabled by config]"; + return false; + } + + classSize = comp->info.compCompHnd->getClassSize(clsHnd); + } + else + { + if (!enableRefClasses) + { + *reason = "[disabled by config]"; + return false; + } + + if (!comp->info.compCompHnd->canAllocateOnStack(clsHnd)) + { + *reason = "[runtime disallows]"; + return false; + } + + classSize = comp->info.compCompHnd->getHeapClassSize(clsHnd); } - if (!comp->info.compCompHnd->canAllocateOnStack(clsHnd)) + if (classSize > s_StackAllocMaxSize) { + *reason = "[too large]"; return false; } - const unsigned int classSize = comp->info.compCompHnd->getHeapClassSize(clsHnd); + const bool escapes = CanLclVarEscape(lclNum); + + if (escapes) + { + *reason = "[escapes]"; + return false; + } - return !CanLclVarEscape(lclNum) && (classSize <= s_StackAllocMaxSize); + return true; } //------------------------------------------------------------------------ diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 8022543639ea5..e8034ec46e194 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1995,11 +1995,22 @@ CEEInfo::getHeapClassSize( TypeHandle VMClsHnd(clsHnd); MethodTable* pMT = VMClsHnd.GetMethodTable(); _ASSERTE(pMT); - _ASSERTE(!pMT->IsValueType()); _ASSERTE(!pMT->HasComponentSize()); +#ifdef FEATURE_READYTORUN_COMPILER + _ASSERTE(!IsReadyToRunCompilation() || pMT->IsInheritanceChainLayoutFixedInCurrentVersionBubble()); +#endif + // Add OBJECT_SIZE to account for method table pointer. - result = pMT->GetNumInstanceFieldBytes() + OBJECT_SIZE; + // + if (pMT->IsValueType()) + { + result = VMClsHnd.GetSize() + OBJECT_SIZE; + } + else + { + result = pMT->GetNumInstanceFieldBytes() + OBJECT_SIZE; + } EE_TO_JIT_TRANSITION_LEAF(); return result; @@ -2023,7 +2034,6 @@ bool CEEInfo::canAllocateOnStack(CORINFO_CLASS_HANDLE clsHnd) TypeHandle VMClsHnd(clsHnd); MethodTable* pMT = VMClsHnd.GetMethodTable(); _ASSERTE(pMT); - _ASSERTE(!pMT->IsValueType()); result = !pMT->HasFinalizer(); From 5deb676f3c82bdfff65e58e4b3bf305186f2678a Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 10 Jun 2024 17:40:05 -0700 Subject: [PATCH 02/45] post rebase fixes --- src/coreclr/jit/compiler.h | 4 ++-- src/coreclr/jit/layout.cpp | 22 ++++++++++++++-------- src/coreclr/jit/layout.h | 8 +++----- src/coreclr/jit/lclvars.cpp | 13 +------------ src/coreclr/jit/objectalloc.cpp | 18 ++++++++++-------- src/coreclr/jit/objectalloc.h | 8 ++++---- 6 files changed, 34 insertions(+), 39 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 75fa2d9e8e2a4..40bc3f5278905 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -10679,9 +10679,9 @@ class Compiler // Get the number of a layout having the specified size but no class handle. unsigned typGetBlkLayoutNum(unsigned blockSize); // Get the layout for the specified class handle. - ClassLayout* typGetObjLayout(CORINFO_CLASS_HANDLE classHandle); + ClassLayout* typGetObjLayout(CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass = false); // Get the number of a layout for the specified class handle. - unsigned typGetObjLayoutNum(CORINFO_CLASS_HANDLE classHandle); + unsigned typGetObjLayoutNum(CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass = false); 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 e0785139f7a14..bf8f34f128fc0 100644 --- a/src/coreclr/jit/layout.cpp +++ b/src/coreclr/jit/layout.cpp @@ -106,9 +106,9 @@ class ClassLayoutTable } // Get a number that uniquely identifies a layout for the specified class handle. - unsigned GetObjLayoutNum(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle) + unsigned GetObjLayoutNum(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) { - return GetObjLayoutIndex(compiler, classHandle) + FirstLayoutNum; + return GetObjLayoutIndex(compiler, classHandle, isBoxedValueClass) + FirstLayoutNum; } private: @@ -385,8 +385,9 @@ ClassLayout* Compiler::typGetObjLayout(CORINFO_CLASS_HANDLE classHandle, bool is ClassLayout* ClassLayout::Create(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) { - bool isValueClass = compiler->info.compCompHnd->isValueClass(classHandle); - unsigned size; + bool isValueClass = compiler->info.compCompHnd->isValueClass(classHandle); + unsigned size; + var_types type; if (isValueClass) { @@ -395,21 +396,26 @@ ClassLayout* ClassLayout::Create(Compiler* compiler, CORINFO_CLASS_HANDLE classH if (isBoxedValueClass) { size += TARGET_POINTER_SIZE; + type = TYP_REF; + } + else + { + type = compiler->impNormStructType(classHandle); } } else { assert(!isBoxedValueClass); size = compiler->info.compCompHnd->getHeapClassSize(classHandle); + type = TYP_REF; } - var_types type = compiler->impNormStructType(classHandle); - INDEBUG(const char* className = compiler->eeGetClassName(classHandle);) INDEBUG(const char* shortClassName = compiler->eeGetShortClassName(classHandle);) - ClassLayout* layout = new (compiler, CMK_ClassLayout) - ClassLayout(classHandle, isValueClass, isBoxedValueClass, size DEBUGARG(className)); + ClassLayout* layout = + new (compiler, CMK_ClassLayout) ClassLayout(classHandle, isValueClass, isBoxedValueClass, size, + type DEBUGARG(className) DEBUGARG(shortClassName)); layout->InitializeGCPtrs(compiler); return layout; } diff --git a/src/coreclr/jit/layout.h b/src/coreclr/jit/layout.h index 4515db9f1501f..da04d3e2211fa 100644 --- a/src/coreclr/jit/layout.h +++ b/src/coreclr/jit/layout.h @@ -21,7 +21,7 @@ class ClassLayout // for cpblk/initblk. const unsigned m_size; - const unsigned m_isValueClass : 1; + const unsigned m_isValueClass : 1; const unsigned m_isBoxedValueClass : 1; INDEBUG(unsigned m_gcPtrsInitialized : 1;) @@ -71,12 +71,10 @@ class ClassLayout static ClassLayout* Create(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass = false); ClassLayout(CORINFO_CLASS_HANDLE classHandle, - bool isValueClass, - unsigned size, - ClassLayout(CORINFO_CLASS_HANDLE classHandle, bool isValueClass, unsigned size DEBUGARG(const char* className)) bool isValueClass, bool isBoxedValueClass, - unsigned size DEBUGARG(const char* className)) + unsigned size, + var_types type DEBUGARG(const char* className) DEBUGARG(const char* shortClassName)) : m_classHandle(classHandle) , m_size(size) , m_isValueClass(isValueClass) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index d42dd5ef16c26..8b690a352f737 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -3379,21 +3379,11 @@ void Compiler::lvaSetStruct(unsigned varNum, ClassLayout* layout, bool unsafeVal { varDsc->lvType = TYP_STRUCT; } - if (varDsc->GetLayout() == nullptr) { - ClassLayout* layout = typGetObjLayout(typeHnd); - - if (isBoxedValueClass) - { - assert(layout->GetSize() > TARGET_POINTER_SIZE); - } - varDsc->SetLayout(layout); - // Boxed value classes are always passed explicitly by ref, and are never simd/hfa. - // - if (layout->IsValueClass() && !isBoxedValueClass) + if (layout->IsValueClass() && !layout->IsBoxedValueClass()) { varDsc->lvType = layout->GetType(); @@ -3434,7 +3424,6 @@ void Compiler::lvaSetStruct(unsigned varNum, ClassLayout* layout, bool unsafeVal } else { - assert(!isBoxedValueClass); assert(ClassLayout::AreCompatible(varDsc->GetLayout(), layout)); // Inlining could replace a canon struct type with an exact one. varDsc->SetLayout(layout); diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index a1417a76a6f2b..46d8dbe1959ba 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -386,9 +386,9 @@ bool ObjectAllocator::MorphAllocObjNodes() // \--* CNS_INT(h) long //------------------------------------------------------------------------ - GenTreeAllocObj* asAllocObj = data->AsAllocObj(); - unsigned int lclNum = stmtExpr->AsLclVar()->GetLclNum(); - CORINFO_CLASS_HANDLE clsHnd = data->AsAllocObj()->gtAllocObjClsHnd; + GenTreeAllocObj* asAllocObj = data->AsAllocObj(); + unsigned int lclNum = stmtExpr->AsLclVar()->GetLclNum(); + CORINFO_CLASS_HANDLE clsHnd = data->AsAllocObj()->gtAllocObjClsHnd; const char* onHeapReason = nullptr; bool canStack = false; @@ -522,12 +522,14 @@ unsigned int ObjectAllocator::MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* a assert(allocObj != nullptr); assert(m_AnalysisDone); - const bool isValueClass = comp->info.compCompHnd->isValueClass(allocObj->gtAllocObjClsHnd); - const bool shortLifetime = false; - - const unsigned int lclNum = comp->lvaGrabTemp(shortLifetime DEBUGARG( + CORINFO_CLASS_HANDLE clsHnd = allocObj->gtAllocObjClsHnd; + const bool isValueClass = comp->info.compCompHnd->isValueClass(clsHnd); + const bool shortLifetime = false; + const unsigned int lclNum = comp->lvaGrabTemp(shortLifetime DEBUGARG( isValueClass ? "stack allocated boxed value class temp" : "stack allocated ref class temp")); - comp->lvaSetStruct(lclNum, allocObj->gtAllocObjClsHnd, true, false, isValueClass); + ClassLayout* const layout = comp->typGetObjLayout(clsHnd, isValueClass); + + comp->lvaSetStruct(lclNum, layout, /* unsafeValueClsCheck */ false); // Initialize the object memory if necessary. bool bbInALoop = block->HasFlag(BBF_BACKWARD_JUMP); diff --git a/src/coreclr/jit/objectalloc.h b/src/coreclr/jit/objectalloc.h index d5a57044fed2b..7413950e181f8 100644 --- a/src/coreclr/jit/objectalloc.h +++ b/src/coreclr/jit/objectalloc.h @@ -47,7 +47,7 @@ class ObjectAllocator final : public Phase virtual PhaseStatus DoPhase() override; private: - bool CanAllocateLclVarOnStack(unsigned int lclNum, CORINFO_CLASS_HANDLE clsHnd, const char** reason); + bool CanAllocateLclVarOnStack(unsigned int lclNum, CORINFO_CLASS_HANDLE clsHnd, const char** reason); bool CanLclVarEscape(unsigned int lclNum); void MarkLclVarAsPossiblyStackPointing(unsigned int lclNum); void MarkLclVarAsDefinitelyStackPointing(unsigned int lclNum); @@ -123,12 +123,12 @@ inline bool ObjectAllocator::CanAllocateLclVarOnStack(unsigned int lclNu assert(m_AnalysisDone); bool enableBoxedValueClasses = true; - bool enableRefClasses = true; - *reason = "[ok]"; + bool enableRefClasses = true; + *reason = "[ok]"; #ifdef DEBUG enableBoxedValueClasses = (JitConfig.JitObjectStackAllocationBoxedValueClass() != 0); - enableRefClasses = (JitConfig.JitObjectStackAllocationRefClass() != 0); + enableRefClasses = (JitConfig.JitObjectStackAllocationRefClass() != 0); #endif unsigned int classSize = 0; From 877927a72ba5c34f170f6e326ea316f8ad01f40b Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Tue, 11 Jun 2024 13:45:58 -0700 Subject: [PATCH 03/45] wip --- src/coreclr/inc/corinfo.h | 8 + src/coreclr/inc/icorjitinfoimpl_generated.h | 3 + src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/jit/ICorJitInfo_names_generated.h | 1 + .../jit/ICorJitInfo_wrapper_generated.hpp | 9 + src/coreclr/jit/jitconfigvalues.h | 4 +- src/coreclr/jit/lclmorph.cpp | 1 - src/coreclr/jit/objectalloc.cpp | 43 +++- .../tools/Common/JitInterface/CorInfoImpl.cs | 7 + .../JitInterface/CorInfoImpl_generated.cs | 242 ++++++++++-------- .../ThunkGenerator/ThunkInput.txt | 1 + .../aot/jitinterface/jitinterface_generated.h | 10 + .../tools/superpmi/superpmi-shared/lwmlist.h | 1 + .../superpmi-shared/methodcontext.cpp | 24 ++ .../superpmi/superpmi-shared/methodcontext.h | 6 +- .../superpmi-shim-collector/icorjitinfo.cpp | 9 + .../icorjitinfo_generated.cpp | 7 + .../icorjitinfo_generated.cpp | 6 + .../tools/superpmi/superpmi/icorjitinfo.cpp | 7 + src/coreclr/vm/jitinterface.cpp | 43 ++++ 20 files changed, 316 insertions(+), 126 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index db5499b81fd33..c1237c8e61bbb 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -2547,6 +2547,14 @@ class ICorStaticInfo CORINFO_CLASS_HANDLE cls ) = 0; + // Get a representation for a stack-allocated boxed value type. + // + // This differs from getTypeForBox in that it includes an explicit field + // for the method table pointer. + virtual CORINFO_CLASS_HANDLE getTypeForBoxOnStack( + CORINFO_CLASS_HANDLE cls + ) = 0; + // returns the correct box helper for a particular class. Note // that if this returns CORINFO_HELP_BOX, the JIT can assume // 'standard' boxing (allocate object and copy), and optimize diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 5572a044b9b0a..86ed580bca656 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -274,6 +274,9 @@ CorInfoHelpFunc getSharedCCtorHelper( CORINFO_CLASS_HANDLE getTypeForBox( CORINFO_CLASS_HANDLE cls) override; +CORINFO_CLASS_HANDLE getTypeForBoxOnStack( + CORINFO_CLASS_HANDLE cls) override; + CorInfoHelpFunc getBoxHelper( CORINFO_CLASS_HANDLE cls) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 35a7e623b9a9b..ce15e123c31a9 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 6e0b439f-0d18-4836-a486-4962af0cc948 */ - 0x6e0b439f, - 0x0d18, - 0x4836, - {0xa4, 0x86, 0x49, 0x62, 0xaf, 0x0c, 0xc9, 0x48} +constexpr GUID JITEEVersionIdentifier = { /* 51839df0-d82f-4bb6-8bdb-66bd2813082c */ + 0x51839df0, + 0xd82f, + 0x4bb6, + {0x8b, 0xdb, 0x66, 0xbd, 0x28, 0x13, 0x08, 0x2c} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index 2e2573a911993..d4675441dc17f 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -67,6 +67,7 @@ DEF_CLR_API(getNewArrHelper) DEF_CLR_API(getCastingHelper) DEF_CLR_API(getSharedCCtorHelper) DEF_CLR_API(getTypeForBox) +DEF_CLR_API(getTypeForBoxOnStack) DEF_CLR_API(getBoxHelper) DEF_CLR_API(getUnBoxHelper) DEF_CLR_API(getRuntimeTypePointer) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index e001c56c26dcb..424e0d4176af6 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -628,6 +628,15 @@ CORINFO_CLASS_HANDLE WrapICorJitInfo::getTypeForBox( return temp; } +CORINFO_CLASS_HANDLE WrapICorJitInfo::getTypeForBoxOnStack( + CORINFO_CLASS_HANDLE cls) +{ + API_ENTER(getTypeForBoxOnStack); + CORINFO_CLASS_HANDLE temp = wrapHnd->getTypeForBoxOnStack(cls); + API_LEAVE(getTypeForBoxOnStack); + return temp; +} + CorInfoHelpFunc WrapICorJitInfo::getBoxHelper( CORINFO_CLASS_HANDLE cls) { diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 65b626aece925..8666c4eb1a83e 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -651,9 +651,9 @@ RELEASE_CONFIG_INTEGER(JitExtDefaultPolicyProfScale, W("JitExtDefaultPolicyProfS RELEASE_CONFIG_INTEGER(JitInlinePolicyModel, W("JitInlinePolicyModel"), 0) RELEASE_CONFIG_INTEGER(JitInlinePolicyProfile, W("JitInlinePolicyProfile"), 0) RELEASE_CONFIG_INTEGER(JitInlinePolicyProfileThreshold, W("JitInlinePolicyProfileThreshold"), 40) -RELEASE_CONFIG_INTEGER(JitObjectStackAllocation, W("JitObjectStackAllocation"), 0) +RELEASE_CONFIG_INTEGER(JitObjectStackAllocation, W("JitObjectStackAllocation"), 1) RELEASE_CONFIG_INTEGER(JitObjectStackAllocationRefClass, W("JitObjectStackAllocationRefClass"), 0) -RELEASE_CONFIG_INTEGER(JitObjectStackAllocationBoxedValueClass, W("JitObjectStackAllocationBoxedValueClass"), 0) +RELEASE_CONFIG_INTEGER(JitObjectStackAllocationBoxedValueClass, W("JitObjectStackAllocationBoxedValueClass"), 1) RELEASE_CONFIG_INTEGER(JitEECallTimingInfo, W("JitEECallTimingInfo"), 0) diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index b8ecc8b1f736f..b8b6a4c4ef316 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -802,7 +802,6 @@ class LocalAddressVisitor final : public GenTreeVisitor } PushValue(use); - return Compiler::WALK_CONTINUE; } diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 46d8dbe1959ba..5b946b69fa2b5 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -527,8 +527,15 @@ unsigned int ObjectAllocator::MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* a const bool shortLifetime = false; const unsigned int lclNum = comp->lvaGrabTemp(shortLifetime DEBUGARG( isValueClass ? "stack allocated boxed value class temp" : "stack allocated ref class temp")); - ClassLayout* const layout = comp->typGetObjLayout(clsHnd, isValueClass); + if (isValueClass) + { + clsHnd = comp->info.compCompHnd->getTypeForBoxOnStack(clsHnd); + // must pre-check for this + assert(clsHnd != NO_CLASS_HANDLE); + } + + ClassLayout* const layout = comp->typGetObjLayout(clsHnd) comp->lvaSetStruct(lclNum, layout, /* unsafeValueClsCheck */ false); // Initialize the object memory if necessary. @@ -649,6 +656,8 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack* parent break; case GT_STOREIND: + case GT_STORE_BLK: + case GT_BLK: if (tree != parent->AsIndir()->Addr()) { // TODO-ObjectStackAllocation: track stores to fields. @@ -715,6 +724,7 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p switch (parent->OperGet()) { case GT_STORE_LCL_VAR: + case GT_BOX: if (parent->TypeGet() == TYP_REF) { parent->ChangeType(newType); @@ -724,7 +734,6 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p case GT_EQ: case GT_NE: case GT_NULLCHECK: - case GT_BOX: break; case GT_COMMA: @@ -747,6 +756,8 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p break; case GT_STOREIND: + case GT_STORE_BLK: + case GT_BLK: assert(tree == parent->AsIndir()->Addr()); // The new target could be *not* on the heap. @@ -791,7 +802,7 @@ void ObjectAllocator::RewriteUses() enum { DoPreOrder = true, - DoLclVarsOnly = true, + DoPostOrder = true, ComputeStack = true, }; @@ -804,7 +815,11 @@ void ObjectAllocator::RewriteUses() Compiler::fgWalkResult PreOrderVisit(GenTree** use, GenTree* user) { GenTree* tree = *use; - assert(tree->OperIsAnyLocal()); + + if (!tree->OperIsAnyLocal()) + { + return Compiler::fgWalkResult::WALK_CONTINUE; + } const unsigned int lclNum = tree->AsLclVarCommon()->GetLclNum(); unsigned int newLclNum = BAD_VAR_NUM; @@ -844,6 +859,26 @@ void ObjectAllocator::RewriteUses() return Compiler::fgWalkResult::WALK_CONTINUE; } + + Compiler::fgWalkResult PostOrderVisit(GenTree** use, GenTree* user) + { + GenTree* const tree = *use; + + // Remove GT_BOX, if stack allocated + // + if (tree->OperIs(GT_BOX)) + { + GenTree* const boxLcl = tree->AsOp()->gtGetOp1(); + assert(boxLcl->OperIs(GT_LCL_VAR, GT_LCL_ADDR)); + if (boxLcl->OperIs(GT_LCL_ADDR)) + { + JITDUMP("Removing BOX wrapper [%06u]\n", m_compiler->dspTreeID(tree)); + *use = boxLcl; + } + } + + return Compiler::fgWalkResult::WALK_CONTINUE; + } }; for (BasicBlock* const block : comp->Blocks()) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 85d64b161f7cb..0f6efeaf30e0d 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2607,6 +2607,13 @@ private CorInfoHelpFunc getSharedCCtorHelper(CORINFO_CLASS_STRUCT_* clsHnd) return ObjectToHandle(typeForBox); } + private CORINFO_CLASS_STRUCT_* getTypeForBoxOnStack(CORINFO_CLASS_STRUCT_* cls) + { + // Todo: implement... + _ = HandleToObject(cls); + return null; + } + private CorInfoHelpFunc getBoxHelper(CORINFO_CLASS_STRUCT_* cls) { var type = HandleToObject(cls); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index e4ef2264da255..837012277f517 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -942,6 +942,21 @@ private static CorInfoHelpFunc _getSharedCCtorHelper(IntPtr thisHandle, IntPtr* } } + [UnmanagedCallersOnly] + private static CORINFO_CLASS_STRUCT_* _getTypeForBoxOnStack(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* cls) + { + var _this = GetThis(thisHandle); + try + { + return _this.getTypeForBoxOnStack(cls); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] private static CorInfoHelpFunc _getBoxHelper(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* cls) { @@ -2594,7 +2609,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 175); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 176); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_notifyMethodInfoUsage; @@ -2659,118 +2674,119 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[60] = (delegate* unmanaged)&_getCastingHelper; callbacks[61] = (delegate* unmanaged)&_getSharedCCtorHelper; callbacks[62] = (delegate* unmanaged)&_getTypeForBox; - callbacks[63] = (delegate* unmanaged)&_getBoxHelper; - callbacks[64] = (delegate* unmanaged)&_getUnBoxHelper; - callbacks[65] = (delegate* unmanaged)&_getRuntimeTypePointer; - callbacks[66] = (delegate* unmanaged)&_isObjectImmutable; - callbacks[67] = (delegate* unmanaged)&_getStringChar; - callbacks[68] = (delegate* unmanaged)&_getObjectType; - callbacks[69] = (delegate* unmanaged)&_getReadyToRunHelper; - callbacks[70] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; - callbacks[71] = (delegate* unmanaged)&_initClass; - callbacks[72] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; - callbacks[73] = (delegate* unmanaged)&_getBuiltinClass; - callbacks[74] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; - callbacks[75] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; - callbacks[76] = (delegate* unmanaged)&_canCast; - callbacks[77] = (delegate* unmanaged)&_compareTypesForCast; - callbacks[78] = (delegate* unmanaged)&_compareTypesForEquality; - callbacks[79] = (delegate* unmanaged)&_isMoreSpecificType; - callbacks[80] = (delegate* unmanaged)&_isExactType; - callbacks[81] = (delegate* unmanaged)&_isNullableType; - callbacks[82] = (delegate* unmanaged)&_isEnum; - callbacks[83] = (delegate* unmanaged)&_getParentType; - callbacks[84] = (delegate* unmanaged)&_getChildType; - callbacks[85] = (delegate* unmanaged)&_isSDArray; - callbacks[86] = (delegate* unmanaged)&_getArrayRank; - callbacks[87] = (delegate* unmanaged)&_getArrayIntrinsicID; - callbacks[88] = (delegate* unmanaged)&_getArrayInitializationData; - callbacks[89] = (delegate* unmanaged)&_canAccessClass; - callbacks[90] = (delegate* unmanaged)&_printFieldName; - callbacks[91] = (delegate* unmanaged)&_getFieldClass; - callbacks[92] = (delegate* unmanaged)&_getFieldType; - callbacks[93] = (delegate* unmanaged)&_getFieldOffset; - callbacks[94] = (delegate* unmanaged)&_getFieldInfo; - callbacks[95] = (delegate* unmanaged)&_getThreadLocalFieldInfo; - callbacks[96] = (delegate* unmanaged)&_getThreadLocalStaticBlocksInfo; - callbacks[97] = (delegate* unmanaged)&_getThreadLocalStaticInfo_NativeAOT; - callbacks[98] = (delegate* unmanaged)&_isFieldStatic; - callbacks[99] = (delegate* unmanaged)&_getArrayOrStringLength; - callbacks[100] = (delegate* unmanaged)&_getBoundaries; - callbacks[101] = (delegate* unmanaged)&_setBoundaries; - callbacks[102] = (delegate* unmanaged)&_getVars; - callbacks[103] = (delegate* unmanaged)&_setVars; - callbacks[104] = (delegate* unmanaged)&_reportRichMappings; - callbacks[105] = (delegate* unmanaged)&_reportMetadata; - callbacks[106] = (delegate* unmanaged)&_allocateArray; - callbacks[107] = (delegate* unmanaged)&_freeArray; - callbacks[108] = (delegate* unmanaged)&_getArgNext; - callbacks[109] = (delegate* unmanaged)&_getArgType; - callbacks[110] = (delegate* unmanaged)&_getExactClasses; - callbacks[111] = (delegate* unmanaged)&_getArgClass; - callbacks[112] = (delegate* unmanaged)&_getHFAType; - callbacks[113] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[114] = (delegate* unmanaged)&_runWithSPMIErrorTrap; - callbacks[115] = (delegate* unmanaged)&_getEEInfo; - callbacks[116] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[117] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[118] = (delegate* unmanaged)&_printMethodName; - callbacks[119] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[120] = (delegate* unmanaged)&_getMethodHash; - callbacks[121] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[122] = (delegate* unmanaged)&_getSwiftLowering; - callbacks[123] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[124] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; - callbacks[125] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[126] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[127] = (delegate* unmanaged)&_getHelperFtn; - callbacks[128] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[129] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[130] = (delegate* unmanaged)&_getMethodSync; - callbacks[131] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[132] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[133] = (delegate* unmanaged)&_embedClassHandle; - callbacks[134] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[135] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[136] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[137] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[138] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[139] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[140] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[141] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[142] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[143] = (delegate* unmanaged)&_getCallInfo; - callbacks[144] = (delegate* unmanaged)&_getClassDomainID; - callbacks[145] = (delegate* unmanaged)&_getStaticFieldContent; - callbacks[146] = (delegate* unmanaged)&_getObjectContent; - callbacks[147] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[148] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[149] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[150] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[151] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[152] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[153] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[154] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[155] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[156] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[157] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[158] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[159] = (delegate* unmanaged)&_allocMem; - callbacks[160] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[161] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[162] = (delegate* unmanaged)&_allocGCInfo; - callbacks[163] = (delegate* unmanaged)&_setEHcount; - callbacks[164] = (delegate* unmanaged)&_setEHinfo; - callbacks[165] = (delegate* unmanaged)&_logMsg; - callbacks[166] = (delegate* unmanaged)&_doAssert; - callbacks[167] = (delegate* unmanaged)&_reportFatalError; - callbacks[168] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[169] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[170] = (delegate* unmanaged)&_recordCallSite; - callbacks[171] = (delegate* unmanaged)&_recordRelocation; - callbacks[172] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[173] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[174] = (delegate* unmanaged)&_getJitFlags; + callbacks[63] = (delegate* unmanaged)&_getTypeForBoxOnStack; + callbacks[64] = (delegate* unmanaged)&_getBoxHelper; + callbacks[65] = (delegate* unmanaged)&_getUnBoxHelper; + callbacks[66] = (delegate* unmanaged)&_getRuntimeTypePointer; + callbacks[67] = (delegate* unmanaged)&_isObjectImmutable; + callbacks[68] = (delegate* unmanaged)&_getStringChar; + callbacks[69] = (delegate* unmanaged)&_getObjectType; + callbacks[70] = (delegate* unmanaged)&_getReadyToRunHelper; + callbacks[71] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; + callbacks[72] = (delegate* unmanaged)&_initClass; + callbacks[73] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; + callbacks[74] = (delegate* unmanaged)&_getBuiltinClass; + callbacks[75] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; + callbacks[76] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; + callbacks[77] = (delegate* unmanaged)&_canCast; + callbacks[78] = (delegate* unmanaged)&_compareTypesForCast; + callbacks[79] = (delegate* unmanaged)&_compareTypesForEquality; + callbacks[80] = (delegate* unmanaged)&_isMoreSpecificType; + callbacks[81] = (delegate* unmanaged)&_isExactType; + callbacks[82] = (delegate* unmanaged)&_isNullableType; + callbacks[83] = (delegate* unmanaged)&_isEnum; + callbacks[84] = (delegate* unmanaged)&_getParentType; + callbacks[85] = (delegate* unmanaged)&_getChildType; + callbacks[86] = (delegate* unmanaged)&_isSDArray; + callbacks[87] = (delegate* unmanaged)&_getArrayRank; + callbacks[88] = (delegate* unmanaged)&_getArrayIntrinsicID; + callbacks[89] = (delegate* unmanaged)&_getArrayInitializationData; + callbacks[90] = (delegate* unmanaged)&_canAccessClass; + callbacks[91] = (delegate* unmanaged)&_printFieldName; + callbacks[92] = (delegate* unmanaged)&_getFieldClass; + callbacks[93] = (delegate* unmanaged)&_getFieldType; + callbacks[94] = (delegate* unmanaged)&_getFieldOffset; + callbacks[95] = (delegate* unmanaged)&_getFieldInfo; + callbacks[96] = (delegate* unmanaged)&_getThreadLocalFieldInfo; + callbacks[97] = (delegate* unmanaged)&_getThreadLocalStaticBlocksInfo; + callbacks[98] = (delegate* unmanaged)&_getThreadLocalStaticInfo_NativeAOT; + callbacks[99] = (delegate* unmanaged)&_isFieldStatic; + callbacks[100] = (delegate* unmanaged)&_getArrayOrStringLength; + callbacks[101] = (delegate* unmanaged)&_getBoundaries; + callbacks[102] = (delegate* unmanaged)&_setBoundaries; + callbacks[103] = (delegate* unmanaged)&_getVars; + callbacks[104] = (delegate* unmanaged)&_setVars; + callbacks[105] = (delegate* unmanaged)&_reportRichMappings; + callbacks[106] = (delegate* unmanaged)&_reportMetadata; + callbacks[107] = (delegate* unmanaged)&_allocateArray; + callbacks[108] = (delegate* unmanaged)&_freeArray; + callbacks[109] = (delegate* unmanaged)&_getArgNext; + callbacks[110] = (delegate* unmanaged)&_getArgType; + callbacks[111] = (delegate* unmanaged)&_getExactClasses; + callbacks[112] = (delegate* unmanaged)&_getArgClass; + callbacks[113] = (delegate* unmanaged)&_getHFAType; + callbacks[114] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[115] = (delegate* unmanaged)&_runWithSPMIErrorTrap; + callbacks[116] = (delegate* unmanaged)&_getEEInfo; + callbacks[117] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[118] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[119] = (delegate* unmanaged)&_printMethodName; + callbacks[120] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[121] = (delegate* unmanaged)&_getMethodHash; + callbacks[122] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[123] = (delegate* unmanaged)&_getSwiftLowering; + callbacks[124] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; + callbacks[125] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; + callbacks[126] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[127] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[128] = (delegate* unmanaged)&_getHelperFtn; + callbacks[129] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[130] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[131] = (delegate* unmanaged)&_getMethodSync; + callbacks[132] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[133] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[134] = (delegate* unmanaged)&_embedClassHandle; + callbacks[135] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[136] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[137] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[138] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[139] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[140] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[141] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[142] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[143] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[144] = (delegate* unmanaged)&_getCallInfo; + callbacks[145] = (delegate* unmanaged)&_getClassDomainID; + callbacks[146] = (delegate* unmanaged)&_getStaticFieldContent; + callbacks[147] = (delegate* unmanaged)&_getObjectContent; + callbacks[148] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[149] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[150] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[151] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[152] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[153] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[154] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[155] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[156] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[157] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[158] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[159] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[160] = (delegate* unmanaged)&_allocMem; + callbacks[161] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[162] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[163] = (delegate* unmanaged)&_allocGCInfo; + callbacks[164] = (delegate* unmanaged)&_setEHcount; + callbacks[165] = (delegate* unmanaged)&_setEHinfo; + callbacks[166] = (delegate* unmanaged)&_logMsg; + callbacks[167] = (delegate* unmanaged)&_doAssert; + callbacks[168] = (delegate* unmanaged)&_reportFatalError; + callbacks[169] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[170] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[171] = (delegate* unmanaged)&_recordCallSite; + callbacks[172] = (delegate* unmanaged)&_recordRelocation; + callbacks[173] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[174] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[175] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 2b78b23547432..efc780b126bde 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -226,6 +226,7 @@ FUNCTIONS CorInfoHelpFunc getCastingHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fThrowing) CorInfoHelpFunc getSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd) CORINFO_CLASS_HANDLE getTypeForBox(CORINFO_CLASS_HANDLE cls) + CORINFO_CLASS_HANDLE getTypeForBoxOnStack(CORINFO_CLASS_HANDLE cls) CorInfoHelpFunc getBoxHelper(CORINFO_CLASS_HANDLE cls) CorInfoHelpFunc getUnBoxHelper(CORINFO_CLASS_HANDLE cls) CORINFO_OBJECT_HANDLE getRuntimeTypePointer(CORINFO_CLASS_HANDLE cls) diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 905ddd41d82b5..736159c0ee3d4 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -74,6 +74,7 @@ struct JitInterfaceCallbacks CorInfoHelpFunc (* getCastingHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fThrowing); CorInfoHelpFunc (* getSharedCCtorHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE clsHnd); CORINFO_CLASS_HANDLE (* getTypeForBox)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); + CORINFO_CLASS_HANDLE (* getTypeForBoxOnStack)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); CorInfoHelpFunc (* getBoxHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); CorInfoHelpFunc (* getUnBoxHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); CORINFO_OBJECT_HANDLE (* getRuntimeTypePointer)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); @@ -817,6 +818,15 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } + virtual CORINFO_CLASS_HANDLE getTypeForBoxOnStack( + CORINFO_CLASS_HANDLE cls) +{ + CorInfoExceptionClass* pException = nullptr; + CORINFO_CLASS_HANDLE temp = _callbacks->getTypeForBoxOnStack(_thisHandle, &pException, cls); + if (pException != nullptr) throw pException; + return temp; +} + virtual CorInfoHelpFunc getBoxHelper( CORINFO_CLASS_HANDLE cls) { diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index 75c1ac1b6f52a..0134ce4d0c0b1 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -133,6 +133,7 @@ LWM(UpdateEntryPointForTailCall, Agnostic_CORINFO_CONST_LOOKUP, Agnostic_CORINFO LWM(GetThreadTLSIndex, DWORD, DLD) LWM(GetTokenTypeAsHandle, GetTokenTypeAsHandleValue, DWORDLONG) LWM(GetTypeForBox, DWORDLONG, DWORDLONG) +LWM(GetTypeForBoxOnStack, DWORDLONG, DWORDLONG) LWM(GetTypeForPrimitiveValueClass, DWORDLONG, DWORD) LWM(GetTypeForPrimitiveNumericClass, DWORDLONG, DWORD) LWM(GetUnboxedEntry, DWORDLONG, DLD); diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 0500096cef9df..87c52f7b0f0ec 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -1941,6 +1941,30 @@ CORINFO_CLASS_HANDLE MethodContext::repGetTypeForBox(CORINFO_CLASS_HANDLE cls) return result; } +void MethodContext::recGetTypeForBoxOnStack(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result) +{ + if (GetTypeForBoxOnStack == nullptr) + GetTypeForBoxOnStack = new LightWeightMap(); + + DWORDLONG key = CastHandle(cls); + DWORDLONG value = CastHandle(result); + GetTypeForBoxOnStack->Add(key, value); + DEBUG_REC(dmpGetTypeForBoxOnStack(key, value)); +} +void MethodContext::dmpGetTypeForBoxOnStack(DWORDLONG key, DWORDLONG value) +{ + printf("GetTypeForBoxOnStack key cls-%016" PRIX64 ", value res-%016" PRIX64 "", key, value); +} + +CORINFO_CLASS_HANDLE MethodContext::repGetTypeForBoxOnStack(CORINFO_CLASS_HANDLE cls) +{ + DWORDLONG key = CastHandle(cls); + DWORDLONG value = LookupByKeyOrMiss(GetTypeForBoxOnStack, key, ": key %016" PRIX64 "", key); + DEBUG_REP(dmpGetTypeForBoxOnStack(key, value)); + CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)value; + return result; +} + void MethodContext::recGetBoxHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc result) { if (GetBoxHelper == nullptr) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 2c85988aa32e8..c4b7db6796fb9 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -248,6 +248,10 @@ class MethodContext void dmpGetTypeForBox(DWORDLONG key, DWORDLONG value); CORINFO_CLASS_HANDLE repGetTypeForBox(CORINFO_CLASS_HANDLE cls); + void recGetTypeForBoxOnStack(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result); + void dmpGetTypeForBoxOnStack(DWORDLONG key, DWORDLONG value); + CORINFO_CLASS_HANDLE repGetTypeForBoxOnStack(CORINFO_CLASS_HANDLE cls); + void recGetBoxHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc result); void dmpGetBoxHelper(DWORDLONG key, DWORD value); CorInfoHelpFunc repGetBoxHelper(CORINFO_CLASS_HANDLE cls); @@ -1040,7 +1044,7 @@ enum mcPackets Packet_GetRelocTypeHint = 84, //Packet_GetSecurityPrologHelper = 85, Packet_GetSharedCCtorHelper = 86, - //Packet_GetTailCallCopyArgsThunk = 87, + Packet_GetTypeForBoxOnStack = 87, Packet_GetThreadTLSIndex = 88, Packet_GetTokenTypeAsHandle = 89, Packet_GetTypeForBox = 90, diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index ac8efbddcfeec..54de5d200f1b5 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -716,6 +716,15 @@ CORINFO_CLASS_HANDLE interceptor_ICJI::getTypeForBox(CORINFO_CLASS_HANDLE cls) return temp; } +// Class handle for a boxed value type, on the stack. +CORINFO_CLASS_HANDLE interceptor_ICJI::getTypeForBoxOnStack(CORINFO_CLASS_HANDLE cls) +{ + mc->cr->AddCall("getTypeForBoxOnStack"); + CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getTypeForBoxOnStack(cls); + mc->recGetTypeForBoxOnStack(cls, temp); + return temp; +} + // returns the correct box helper for a particular class. Note // that if this returns CORINFO_HELP_BOX, the JIT can assume // 'standard' boxing (allocate object and copy), and optimize diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index a365cdb111e22..faa87bb49a55d 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -517,6 +517,13 @@ CORINFO_CLASS_HANDLE interceptor_ICJI::getTypeForBox( return original_ICorJitInfo->getTypeForBox(cls); } +CORINFO_CLASS_HANDLE interceptor_ICJI::getTypeForBoxOnStack( + CORINFO_CLASS_HANDLE cls) +{ + mcs->AddCall("getTypeForBoxOnStack"); + return original_ICorJitInfo->getTypeForBoxOnStack(cls); +} + CorInfoHelpFunc interceptor_ICJI::getBoxHelper( CORINFO_CLASS_HANDLE cls) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index 0d80993f52b67..d1464c810d144 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -454,6 +454,12 @@ CORINFO_CLASS_HANDLE interceptor_ICJI::getTypeForBox( return original_ICorJitInfo->getTypeForBox(cls); } +CORINFO_CLASS_HANDLE interceptor_ICJI::getTypeForBoxOnStack( + CORINFO_CLASS_HANDLE cls) +{ + return original_ICorJitInfo->getTypeForBoxOnStack(cls); +} + CorInfoHelpFunc interceptor_ICJI::getBoxHelper( CORINFO_CLASS_HANDLE cls) { diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index b263c1cafdebc..8b965a0968b59 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -594,6 +594,13 @@ CORINFO_CLASS_HANDLE MyICJI::getTypeForBox(CORINFO_CLASS_HANDLE cls) return jitInstance->mc->repGetTypeForBox(cls); } +// Class handle for a boxed value type, on the stack. +CORINFO_CLASS_HANDLE MyICJI::getTypeForBoxOnStack(CORINFO_CLASS_HANDLE cls) +{ + jitInstance->mc->cr->AddCall("getTypeForBoxOnStack"); + return jitInstance->mc->repGetTypeForBoxOnStack(cls); +} + // returns the correct box helper for a particular class. Note // that if this returns CORINFO_HELP_BOX, the JIT can assume // 'standard' boxing (allocate object and copy), and optimize diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index e8034ec46e194..d227eb331d99f 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -6362,6 +6362,49 @@ CORINFO_CLASS_HANDLE CEEInfo::getTypeForBox(CORINFO_CLASS_HANDLE cls) return static_cast(VMClsHnd.AsPtr()); } +/***********************************************************************/ +// Get a representation for a stack-allocated boxed value type +// +CORINFO_CLASS_HANDLE CEEInfo::getTypeForBoxOnStack(CORINFO_CLASS_HANDLE cls) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + CORINFO_CLASS_HANDLE result = NULL; + + JIT_TO_EE_TRANSITION(); + + TypeHandle VMClsHnd(cls); + if (Nullable::IsNullableType(VMClsHnd)) + { + VMClsHnd = VMClsHnd.AsMethodTable()->GetInstantiation()[0]; + } + +#ifdef FEATURE_64BIT_ALIGNMENT + if (VMClsHnd.RequiresAlign8()) + { + // TODO: Maybe handle 32 bit platforms with 8 byte alignments + result = NULL; + } + else +#endif + { + TypeHandle mt(CoreLibBinder::GetElementType(ELEMENT_TYPE_I)); + TypeHandle boxedFields[2] = {mt, VMClsHnd}; + Instantiation boxedFieldsInst((TypeHandle*)&boxedFields, 2); + TypeHandle genericKvp = CoreLibBinder::GetClass(CLASS__KEYVALUEPAIRGENERIC); + TypeHandle boxedKvp = genericKvp.Instantiate(boxedFieldsInst); + result = static_cast(boxedKvp.AsPtr()); + } + + EE_TO_JIT_TRANSITION(); + + return result; +} + /***********************************************************************/ // see code:Nullable#NullableVerification CorInfoHelpFunc CEEInfo::getBoxHelper(CORINFO_CLASS_HANDLE clsHnd) From 0a7f3d4afca26615659573c77b1715724db5ed3c Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Tue, 11 Jun 2024 14:38:13 -0700 Subject: [PATCH 04/45] revert layout changes, rely on KVP instead --- src/coreclr/jit/compiler.h | 4 +- src/coreclr/jit/layout.cpp | 95 +++++++++------------------------ src/coreclr/jit/layout.h | 29 ++-------- src/coreclr/jit/lclvars.cpp | 2 +- src/coreclr/jit/objectalloc.cpp | 10 ++-- src/coreclr/jit/objectalloc.h | 6 +++ 6 files changed, 44 insertions(+), 102 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 40bc3f5278905..75fa2d9e8e2a4 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -10679,9 +10679,9 @@ class Compiler // Get the number of a layout having the specified size but no class handle. unsigned typGetBlkLayoutNum(unsigned blockSize); // Get the layout for the specified class handle. - ClassLayout* typGetObjLayout(CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass = false); + ClassLayout* typGetObjLayout(CORINFO_CLASS_HANDLE classHandle); // Get the number of a layout for the specified class handle. - unsigned typGetObjLayoutNum(CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass = false); + unsigned typGetObjLayoutNum(CORINFO_CLASS_HANDLE classHandle); 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 bf8f34f128fc0..6a30b66acbc4f 100644 --- a/src/coreclr/jit/layout.cpp +++ b/src/coreclr/jit/layout.cpp @@ -20,25 +20,23 @@ class ClassLayoutTable typedef JitHashTable, unsigned> BlkLayoutIndexMap; typedef JitHashTable, unsigned> ObjLayoutIndexMap; - typedef JitHashTable, unsigned> BoxLayoutIndexMap; union { - // Up to 4 layouts can be stored "inline" and finding a layout by handle/size can be done using linear search. + // Up to 3 layouts can be stored "inline" and finding a layout by handle/size can be done using linear search. // Most methods need no more than 2 layouts. - ClassLayout* m_layoutArray[4]; + ClassLayout* m_layoutArray[3]; // Otherwise a dynamic array is allocated and hashtables are used to map from handle/size to layout array index. struct { ClassLayout** m_layoutLargeArray; BlkLayoutIndexMap* m_blkLayoutMap; ObjLayoutIndexMap* m_objLayoutMap; - BoxLayoutIndexMap* m_boxLayoutMap; }; }; // The number of layout objects stored in this table. unsigned m_layoutCount; - // The capacity of m_layoutLargeArray (when more than 4 layouts are stored). + // The capacity of m_layoutLargeArray (when more than 3 layouts are stored). unsigned m_layoutLargeCapacity; // We furthermore fast-path the 0-sized block layout which is used for // block locals that may grow (e.g. the outgoing arg area in every non-x86 @@ -100,15 +98,15 @@ class ClassLayoutTable } // Get the layout for the specified class handle. - ClassLayout* GetObjLayout(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) + ClassLayout* GetObjLayout(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle) { - return GetLayoutByIndex(GetObjLayoutIndex(compiler, classHandle, isBoxedValueClass)); + return GetLayoutByIndex(GetObjLayoutIndex(compiler, classHandle)); } // Get a number that uniquely identifies a layout for the specified class handle. - unsigned GetObjLayoutNum(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) + unsigned GetObjLayoutNum(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle) { - return GetObjLayoutIndex(compiler, classHandle, isBoxedValueClass) + FirstLayoutNum; + return GetObjLayoutIndex(compiler, classHandle) + FirstLayoutNum; } private: @@ -149,9 +147,7 @@ class ClassLayoutTable else { unsigned index = 0; - if ((layout->IsBlockLayout() && m_blkLayoutMap->Lookup(layout->GetSize(), &index)) || - (layout->IsBoxedValueClass() && m_boxLayoutMap->Lookup(layout->GetClassHandle(), &index)) || m_objLayoutMap->Lookup(layout->GetClassHandle(), &index)) { return index; @@ -206,7 +202,7 @@ class ClassLayoutTable return index; } - unsigned GetObjLayoutIndex(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) + unsigned GetObjLayoutIndex(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle) { assert(classHandle != NO_CLASS_HANDLE); @@ -214,21 +210,12 @@ class ClassLayoutTable { for (unsigned i = 0; i < m_layoutCount; i++) { - if ((m_layoutArray[i]->GetClassHandle() == classHandle) && - (isBoxedValueClass == m_layoutArray[i]->IsBoxedValueClass())) + if (m_layoutArray[i]->GetClassHandle() == classHandle) { return i; } } } - else if (isBoxedValueClass) - { - unsigned index; - if (m_boxLayoutMap->Lookup(classHandle, &index)) - { - return index; - } - } else { unsigned index; @@ -238,12 +225,12 @@ class ClassLayoutTable } } - return AddObjLayout(compiler, CreateObjLayout(compiler, classHandle, isBoxedValueClass)); + return AddObjLayout(compiler, CreateObjLayout(compiler, classHandle)); } - ClassLayout* CreateObjLayout(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) + ClassLayout* CreateObjLayout(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle) { - return ClassLayout::Create(compiler, classHandle, isBoxedValueClass); + return ClassLayout::Create(compiler, classHandle); } unsigned AddObjLayout(Compiler* compiler, ClassLayout* layout) @@ -255,15 +242,7 @@ class ClassLayoutTable } unsigned index = AddLayoutLarge(compiler, layout); - - if (layout->IsBoxedValueClass()) - { - m_boxLayoutMap->Set(layout->GetClassHandle(), index); - } - else - { - m_objLayoutMap->Set(layout->GetClassHandle(), index); - } + m_objLayoutMap->Set(layout->GetClassHandle(), index); return index; } @@ -279,7 +258,6 @@ class ClassLayoutTable { BlkLayoutIndexMap* blkLayoutMap = new (alloc) BlkLayoutIndexMap(alloc); ObjLayoutIndexMap* objLayoutMap = new (alloc) ObjLayoutIndexMap(alloc); - BoxLayoutIndexMap* boxLayoutMap = new (alloc) BoxLayoutIndexMap(alloc); for (unsigned i = 0; i < m_layoutCount; i++) { @@ -290,10 +268,6 @@ class ClassLayoutTable { blkLayoutMap->Set(l->GetSize(), i); } - else if (l->IsBoxedValueClass()) - { - boxLayoutMap->Set(l->GetClassHandle(), i); - } else { objLayoutMap->Set(l->GetClassHandle(), i); @@ -302,7 +276,6 @@ class ClassLayoutTable m_blkLayoutMap = blkLayoutMap; m_objLayoutMap = objLayoutMap; - m_boxLayoutMap = boxLayoutMap; } else { @@ -371,52 +344,39 @@ ClassLayout* Compiler::typGetBlkLayout(unsigned blockSize) return typGetClassLayoutTable()->GetBlkLayout(this, blockSize); } -unsigned Compiler::typGetObjLayoutNum(CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) +unsigned Compiler::typGetObjLayoutNum(CORINFO_CLASS_HANDLE classHandle) { - return typGetClassLayoutTable()->GetObjLayoutNum(this, classHandle, isBoxedValueClass); + return typGetClassLayoutTable()->GetObjLayoutNum(this, classHandle); } -ClassLayout* Compiler::typGetObjLayout(CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) +ClassLayout* Compiler::typGetObjLayout(CORINFO_CLASS_HANDLE classHandle) { - ClassLayout* const result = typGetClassLayoutTable()->GetObjLayout(this, classHandle, isBoxedValueClass); - assert(result->IsBoxedValueClass() == isBoxedValueClass); - return result; + return typGetClassLayoutTable()->GetObjLayout(this, classHandle); } -ClassLayout* ClassLayout::Create(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass) +ClassLayout* ClassLayout::Create(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle) { - bool isValueClass = compiler->info.compCompHnd->isValueClass(classHandle); - unsigned size; - var_types type; + bool isValueClass = compiler->eeIsValueClass(classHandle); + unsigned size; if (isValueClass) { size = compiler->info.compCompHnd->getClassSize(classHandle); - - if (isBoxedValueClass) - { - size += TARGET_POINTER_SIZE; - type = TYP_REF; - } - else - { - type = compiler->impNormStructType(classHandle); - } } else { - assert(!isBoxedValueClass); size = compiler->info.compCompHnd->getHeapClassSize(classHandle); - type = TYP_REF; } + var_types type = compiler->impNormStructType(classHandle); + INDEBUG(const char* className = compiler->eeGetClassName(classHandle);) INDEBUG(const char* shortClassName = compiler->eeGetShortClassName(classHandle);) - ClassLayout* layout = - new (compiler, CMK_ClassLayout) ClassLayout(classHandle, isValueClass, isBoxedValueClass, size, - type DEBUGARG(className) DEBUGARG(shortClassName)); + ClassLayout* layout = new (compiler, CMK_ClassLayout) + ClassLayout(classHandle, isValueClass, size, type DEBUGARG(className) DEBUGARG(shortClassName)); layout->InitializeGCPtrs(compiler); + return layout; } @@ -544,11 +504,6 @@ bool ClassLayout::AreCompatible(const ClassLayout* layout1, const ClassLayout* l return true; } - if (layout1->IsBoxedValueClass() != layout2->IsBoxedValueClass()) - { - return false; - } - if (layout1->GetSize() != layout2->GetSize()) { return false; diff --git a/src/coreclr/jit/layout.h b/src/coreclr/jit/layout.h index da04d3e2211fa..c2c901d1f33c9 100644 --- a/src/coreclr/jit/layout.h +++ b/src/coreclr/jit/layout.h @@ -21,13 +21,11 @@ class ClassLayout // for cpblk/initblk. const unsigned m_size; - const unsigned m_isValueClass : 1; - const unsigned m_isBoxedValueClass : 1; - + const unsigned m_isValueClass : 1; INDEBUG(unsigned m_gcPtrsInitialized : 1;) - // The number of GC pointers in this layout. Since the the maximum size is 2^32-1 the count - // TODO: show 24 bits suffices - unsigned m_gcPtrCount : 24; + // The number of GC pointers in this layout. Since the maximum size is 2^32-1 the count + // can fit in at most 30 bits. + unsigned m_gcPtrCount : 30; // Array of CorInfoGCType (as BYTE) that describes the GC layout of the class. // For small classes the array is stored inline, avoiding an extra allocation @@ -54,7 +52,6 @@ class ClassLayout : m_classHandle(NO_CLASS_HANDLE) , m_size(size) , m_isValueClass(false) - , m_isBoxedValueClass(false) #ifdef DEBUG , m_gcPtrsInitialized(true) #endif @@ -68,17 +65,15 @@ class ClassLayout { } - static ClassLayout* Create(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle, bool isBoxedValueClass = false); + static ClassLayout* Create(Compiler* compiler, CORINFO_CLASS_HANDLE classHandle); ClassLayout(CORINFO_CLASS_HANDLE classHandle, bool isValueClass, - bool isBoxedValueClass, unsigned size, var_types type DEBUGARG(const char* className) DEBUGARG(const char* shortClassName)) : m_classHandle(classHandle) , m_size(size) , m_isValueClass(isValueClass) - , m_isBoxedValueClass(isBoxedValueClass) #ifdef DEBUG , m_gcPtrsInitialized(false) #endif @@ -125,11 +120,6 @@ class ClassLayout return m_isValueClass; } - bool IsBoxedValueClass() const - { - return m_isBoxedValueClass; - } - unsigned GetSize() const { return m_size; @@ -250,15 +240,6 @@ class ClassLayout return TYPE_GC_NONE; } - if (m_isBoxedValueClass) - { - if (slot == 0) - { - return TYPE_GC_NONE; - } - return static_cast(GetGCPtrs()[slot - 1]); - } - return static_cast(GetGCPtrs()[slot]); } }; diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 8b690a352f737..cb53f7ff061d4 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -3383,7 +3383,7 @@ void Compiler::lvaSetStruct(unsigned varNum, ClassLayout* layout, bool unsafeVal { varDsc->SetLayout(layout); - if (layout->IsValueClass() && !layout->IsBoxedValueClass()) + if (layout->IsValueClass()) { varDsc->lvType = layout->GetType(); diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 5b946b69fa2b5..867912fd8e3fa 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -531,11 +531,11 @@ unsigned int ObjectAllocator::MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* a if (isValueClass) { clsHnd = comp->info.compCompHnd->getTypeForBoxOnStack(clsHnd); - // must pre-check for this + // must pre-check for this assert(clsHnd != NO_CLASS_HANDLE); } - ClassLayout* const layout = comp->typGetObjLayout(clsHnd) + ClassLayout* const layout = comp->typGetObjLayout(clsHnd); comp->lvaSetStruct(lclNum, layout, /* unsafeValueClsCheck */ false); // Initialize the object memory if necessary. @@ -801,9 +801,9 @@ void ObjectAllocator::RewriteUses() public: enum { - DoPreOrder = true, - DoPostOrder = true, - ComputeStack = true, + DoPreOrder = true, + DoPostOrder = true, + ComputeStack = true, }; RewriteUsesVisitor(ObjectAllocator* allocator) diff --git a/src/coreclr/jit/objectalloc.h b/src/coreclr/jit/objectalloc.h index 7413950e181f8..c384faba7f169 100644 --- a/src/coreclr/jit/objectalloc.h +++ b/src/coreclr/jit/objectalloc.h @@ -141,6 +141,12 @@ inline bool ObjectAllocator::CanAllocateLclVarOnStack(unsigned int lclNu 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 From 56500c31a4a709f4c8a044880c56ab23aa79d339 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Tue, 11 Jun 2024 20:01:34 -0700 Subject: [PATCH 05/45] trying to enable promotion --- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/lclmorph.cpp | 41 +++++++++++++++++++++++++++++++++ src/coreclr/jit/lclvars.cpp | 6 +++++ src/coreclr/jit/objectalloc.cpp | 39 +++++++++++++++++++++++++++++-- 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 75fa2d9e8e2a4..9d23ded058f71 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -692,6 +692,7 @@ class LclVarDsc #endif unsigned char lvAllDefsAreNoGc : 1; // For pinned locals: true if all defs of this local are no-gc + unsigned char lvStackAllocatedBox : 1; // Local is a stack allocated box #if FEATURE_MULTIREG_ARGS regNumber lvRegNumForSlot(unsigned slotNum) diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index b8b6a4c4ef316..cb3d7e5ef27af 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -1023,6 +1023,47 @@ class LocalAddressVisitor final : public GenTreeVisitor SequenceCall(node->AsCall()); break; + case GT_EQ: + case GT_NE: + { + // If we see &lcl EQ/NE null, rewrite to 0/1 comparison + // to reduce overall address exposure. + // + assert(TopValue(2).Node() == node); + assert(TopValue(1).Node() == node->AsOp()->gtOp1); + assert(TopValue(0).Node() == node->AsOp()->gtOp2); + + GenTree* op1 = node->AsOp()->gtOp1; + GenTree* op2 = node->AsOp()->gtOp2; + bool rewrite = false; + + if (op1->OperIs(GT_LCL_ADDR) && op2->IsIntegralConst(0)) + { + op1 = m_compiler->gtNewIconNode(1); + op2->ChangeType(TYP_INT); + rewrite = true; + } + else if (op2->OperIs(GT_LCL_ADDR) && op1->IsIntegralConst(0)) + { + op2 = m_compiler->gtNewIconNode(1); + op1->ChangeType(TYP_INT); + rewrite = true; + } + + if (rewrite) + { + JITDUMP("Rewriting known address-of comparison [%06u]\n", m_compiler->dspTreeID(node)); + node->AsOp()->gtOp1 = op1; + node->AsOp()->gtOp2 = op2; + } + + INDEBUG(TopValue(0).Consume()); + INDEBUG(TopValue(1).Consume()); + PopValue(); + PopValue(); + break; + } + default: while (TopValue(0).Node() != node) { diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index cb53f7ff061d4..8609fb96ecccd 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -2543,9 +2543,15 @@ bool Compiler::StructPromotionHelper::CanPromoteStructVar(unsigned lclNum) if (varDsc->GetLayout()->IsBlockLayout()) { + JITDUMP(" struct promotion of V%02u is disabled because it has block layout\n", lclNum); return false; } + if (varDsc->lvStackAllocatedBox) + { + JITDUMP(" struct promotion of V%02u is disabled because it is a stack allocated box\n", lclNum); + } + #ifdef SWIFT_SUPPORT // Swift structs are not passed in a way that match their layout and // require reassembling on the local stack frame. Skip promotion for these diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 867912fd8e3fa..b722859d93657 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -535,13 +535,13 @@ unsigned int ObjectAllocator::MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* a assert(clsHnd != NO_CLASS_HANDLE); } - ClassLayout* const layout = comp->typGetObjLayout(clsHnd); - comp->lvaSetStruct(lclNum, layout, /* unsafeValueClsCheck */ false); + comp->lvaSetStruct(lclNum, clsHnd, /* 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->lvStackAllocatedBox = isValueClass; if (comp->fgVarNeedsExplicitZeroInit(lclNum, bbInALoop, bbIsReturn)) { //------------------------------------------------------------------------ @@ -877,6 +877,41 @@ void ObjectAllocator::RewriteUses() } } +#if 0 + // Translate accesses to box payloads to have field seqs + // + if (tree->OperIs(GT_ADD)) + { + GenTree* op1 = tree->AsOp()->gtGetOp1(); + GenTree* op2 = tree->AsOp()->gtGetOp2(); + bool tryToTransform = false; + + if (op1->OperIs(GT_LCL_ADDR) && op2->IsIntegralConst()) + { + tryToTransform = true; + } + else if (op2->OperIs(GT_LCL_ADDR) && op1->IsIntegralConst()) + { + std::swap(op1, op2); + tryToTransform = true; + } + + if (tryToTransform) + { + LclVarDsc* const varDsc = m_compiler->lvaGetDesc(op1->AsLclVarCommon()); + if (varDsc->lvStackAllocatedBox && (op2->AsIntConCommon()->IntegralValue() == TARGET_POINTER_SIZE)) + { + JITDUMP("Rewriting box payload ADD [%06u]\n", m_compiler->dspTreeID(tree)); + CORINFO_FIELD_HANDLE fieldHnd = m_compiler->info.compCompHnd->getFieldInClass(varDsc->lvClassHnd, 1); + FieldSeq* const fieldSeq = m_compiler->GetFieldSeqStore()->Create(fieldHnd, TARGET_POINTER_SIZE, FieldSeq::FieldKind::Instance); + op2 = m_compiler->gtNewIconNode(TARGET_POINTER_SIZE, fieldSeq); + tree->AsOp()->gtOp1 = op1; + tree->AsOp()->gtOp2 = op2; + } + } + } +#endif + return Compiler::fgWalkResult::WALK_CONTINUE; } }; From f9e6e470d45a8ebd1bbd456f6d17c1c19f76ee71 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 12 Jun 2024 06:04:16 +0200 Subject: [PATCH 06/45] Small fixes --- src/coreclr/jit/lclmorph.cpp | 40 ++++++++++++++++-------------------- src/coreclr/jit/lclvars.cpp | 1 + 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index cb3d7e5ef27af..3cf645b02a5fc 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -1033,34 +1033,30 @@ class LocalAddressVisitor final : public GenTreeVisitor assert(TopValue(1).Node() == node->AsOp()->gtOp1); assert(TopValue(0).Node() == node->AsOp()->gtOp2); - GenTree* op1 = node->AsOp()->gtOp1; - GenTree* op2 = node->AsOp()->gtOp2; - bool rewrite = false; + Value& lhs = TopValue(1); + Value& rhs = TopValue(0); - if (op1->OperIs(GT_LCL_ADDR) && op2->IsIntegralConst(0)) + if ((lhs.IsAddress() && rhs.Node()->IsIntegralConst(0)) || + (rhs.IsAddress() && lhs.Node()->IsIntegralConst(0))) { - op1 = m_compiler->gtNewIconNode(1); - op2->ChangeType(TYP_INT); - rewrite = true; - } - else if (op2->OperIs(GT_LCL_ADDR) && op1->IsIntegralConst(0)) - { - op2 = m_compiler->gtNewIconNode(1); - op1->ChangeType(TYP_INT); - rewrite = true; - } + JITDUMP("Rewriting known address-of comparison [%06u]\n", m_compiler->dspTreeID(node)); + *lhs.Use() = m_compiler->gtNewIconNode(0); + *rhs.Use() = m_compiler->gtNewIconNode(node->OperIs(GT_EQ) ? 0 : 1); + m_stmtModified = true; - if (rewrite) + INDEBUG(TopValue(0).Consume()); + INDEBUG(TopValue(1).Consume()); + PopValue(); + PopValue(); + } + else { - JITDUMP("Rewriting known address-of comparison [%06u]\n", m_compiler->dspTreeID(node)); - node->AsOp()->gtOp1 = op1; - node->AsOp()->gtOp2 = op2; + EscapeValue(TopValue(0), node); + PopValue(); + EscapeValue(TopValue(0), node); + PopValue(); } - INDEBUG(TopValue(0).Consume()); - INDEBUG(TopValue(1).Consume()); - PopValue(); - PopValue(); break; } diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 8609fb96ecccd..4bcb16d502171 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -2550,6 +2550,7 @@ bool Compiler::StructPromotionHelper::CanPromoteStructVar(unsigned lclNum) if (varDsc->lvStackAllocatedBox) { JITDUMP(" struct promotion of V%02u is disabled because it is a stack allocated box\n", lclNum); + return false; } #ifdef SWIFT_SUPPORT From 67402d491cd9d058aa54d68bd01aff23e48edefe Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 12 Jun 2024 06:15:13 +0200 Subject: [PATCH 07/45] Update src/coreclr/jit/lclmorph.cpp --- src/coreclr/jit/lclmorph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 3cf645b02a5fc..03482ba1d8b0f 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -1041,7 +1041,7 @@ class LocalAddressVisitor final : public GenTreeVisitor { JITDUMP("Rewriting known address-of comparison [%06u]\n", m_compiler->dspTreeID(node)); *lhs.Use() = m_compiler->gtNewIconNode(0); - *rhs.Use() = m_compiler->gtNewIconNode(node->OperIs(GT_EQ) ? 0 : 1); + *rhs.Use() = m_compiler->gtNewIconNode(1); m_stmtModified = true; INDEBUG(TopValue(0).Consume()); From 350d7ca3a0e4c7b38b90464fe3332876263a14f8 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 12 Jun 2024 11:13:14 -0700 Subject: [PATCH 08/45] format --- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/lclmorph.cpp | 4 ++-- src/coreclr/jit/objectalloc.cpp | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 9d23ded058f71..807e9d7baf698 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -691,7 +691,7 @@ class LclVarDsc unsigned char lvSingleDefDisqualifyReason = 'H'; #endif - unsigned char lvAllDefsAreNoGc : 1; // For pinned locals: true if all defs of this local are no-gc + unsigned char lvAllDefsAreNoGc : 1; // For pinned locals: true if all defs of this local are no-gc unsigned char lvStackAllocatedBox : 1; // Local is a stack allocated box #if FEATURE_MULTIREG_ARGS diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 03482ba1d8b0f..35f266b9432cb 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -1040,8 +1040,8 @@ class LocalAddressVisitor final : public GenTreeVisitor (rhs.IsAddress() && lhs.Node()->IsIntegralConst(0))) { JITDUMP("Rewriting known address-of comparison [%06u]\n", m_compiler->dspTreeID(node)); - *lhs.Use() = m_compiler->gtNewIconNode(0); - *rhs.Use() = m_compiler->gtNewIconNode(1); + *lhs.Use() = m_compiler->gtNewIconNode(0); + *rhs.Use() = m_compiler->gtNewIconNode(1); m_stmtModified = true; INDEBUG(TopValue(0).Consume()); diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index b722859d93657..b2f655f0e42c3 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -414,7 +414,8 @@ bool ObjectAllocator::MorphAllocObjNodes() JITDUMP("Allocating V%02u on the stack\n", lclNum); canStack = true; - printf("@@@ SA V%02u (%s) in %s\n", lclNum, comp->eeGetClassName(clsHnd), comp->info.compFullName); + // printf("@@@ SA V%02u (%s) in %s\n", lclNum, comp->eeGetClassName(clsHnd), + // comp->info.compFullName); const unsigned int stackLclNum = MorphAllocObjNodeIntoStackAlloc(asAllocObj, block, stmt); m_HeapLocalToStackLocalMap.AddOrUpdate(lclNum, stackLclNum); From 0328665d97c4f7412cb0ad55426b848e107d1d65 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 12 Jun 2024 12:30:16 -0700 Subject: [PATCH 09/45] avoid escaping nullcheck addrs --- src/coreclr/jit/lclmorph.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 35f266b9432cb..0024b192b20d7 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -339,6 +339,7 @@ class LocalEqualsLocalAddrAssertions // void OnExposed(unsigned lclNum) { + JITDUMP("On exposed: V%02u\n", lclNum); BitVecTraits localsTraits(m_comp->lvaCount, m_comp); BitVecOps::AddElemD(&localsTraits, m_localsToExpose, lclNum); } @@ -1060,6 +1061,22 @@ class LocalAddressVisitor final : public GenTreeVisitor break; } + case GT_NULLCHECK: + { + assert(TopValue(1).Node() == node); + assert(TopValue(0).Node() == node->AsOp()->gtOp1); + Value& op = TopValue(0); + if (op.IsAddress()) + { + JITDUMP("Bashing nullcheck of local [%06u] to NOP\n", m_compiler->dspTreeID(node)); + node->gtBashToNOP(); + m_stmtModified = true; + } + INDEBUG(TopValue(0).Consume()); + PopValue(); + break; + } + default: while (TopValue(0).Node() != node) { From 315916da694cb2f70ea1f18887c2b7e498b91a7d Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 12 Jun 2024 19:39:14 -0700 Subject: [PATCH 10/45] fix nullcheck handling --- src/coreclr/jit/lclmorph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 0024b192b20d7..7df1fa77333f2 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -1072,7 +1072,7 @@ class LocalAddressVisitor final : public GenTreeVisitor node->gtBashToNOP(); m_stmtModified = true; } - INDEBUG(TopValue(0).Consume()); + EscapeValue(TopValue(0), node); PopValue(); break; } From 91b83d30688dc73a5b948979eff5cb64599ecb9b Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 13 Jun 2024 15:58:40 -0700 Subject: [PATCH 11/45] fix colon retyping --- src/coreclr/jit/objectalloc.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index b2f655f0e42c3..1c1283c0dd050 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -414,8 +414,7 @@ bool ObjectAllocator::MorphAllocObjNodes() JITDUMP("Allocating V%02u on the stack\n", lclNum); canStack = true; - // printf("@@@ SA V%02u (%s) in %s\n", lclNum, comp->eeGetClassName(clsHnd), - // comp->info.compFullName); + // printf("@@@ SA V%02u (%s) in %s\n", lclNum, comp->eeGetClassName(clsHnd), comp->info.compFullName); const unsigned int stackLclNum = MorphAllocObjNodeIntoStackAlloc(asAllocObj, block, stmt); m_HeapLocalToStackLocalMap.AddOrUpdate(lclNum, stackLclNum); @@ -751,6 +750,22 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p if (parent->TypeGet() == TYP_REF) { parent->ChangeType(newType); + + if (parent->OperIs(GT_COLON)) + { + GenTree* const lhs = parent->AsOp()->gtGetOp1(); + GenTree* const rhs = parent->AsOp()->gtGetOp2(); + + if (lhs->TypeIs(TYP_REF)) + { + lhs->ChangeType(newType); + } + + if (rhs->TypeIs(TYP_REF)) + { + rhs->ChangeType(newType); + } + } } ++parentIndex; keepChecking = true; From bcc7c3a59fdc0d271d911eb4434b81b1e2c53215 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 13 Jun 2024 17:35:41 -0700 Subject: [PATCH 12/45] metrics, enable for ref classes too --- src/coreclr/jit/jitconfigvalues.h | 2 +- src/coreclr/jit/jitmetadatalist.h | 4 ++++ src/coreclr/jit/objectalloc.cpp | 26 ++++++++++++++++++++++++-- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 8666c4eb1a83e..fc963ee0db233 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -652,7 +652,7 @@ RELEASE_CONFIG_INTEGER(JitInlinePolicyModel, W("JitInlinePolicyModel"), 0) RELEASE_CONFIG_INTEGER(JitInlinePolicyProfile, W("JitInlinePolicyProfile"), 0) RELEASE_CONFIG_INTEGER(JitInlinePolicyProfileThreshold, W("JitInlinePolicyProfileThreshold"), 40) RELEASE_CONFIG_INTEGER(JitObjectStackAllocation, W("JitObjectStackAllocation"), 1) -RELEASE_CONFIG_INTEGER(JitObjectStackAllocationRefClass, W("JitObjectStackAllocationRefClass"), 0) +RELEASE_CONFIG_INTEGER(JitObjectStackAllocationRefClass, W("JitObjectStackAllocationRefClass"), 1) RELEASE_CONFIG_INTEGER(JitObjectStackAllocationBoxedValueClass, W("JitObjectStackAllocationBoxedValueClass"), 1) RELEASE_CONFIG_INTEGER(JitEECallTimingInfo, W("JitEECallTimingInfo"), 0) diff --git a/src/coreclr/jit/jitmetadatalist.h b/src/coreclr/jit/jitmetadatalist.h index 209133f4324b0..10da7249177f8 100644 --- a/src/coreclr/jit/jitmetadatalist.h +++ b/src/coreclr/jit/jitmetadatalist.h @@ -71,6 +71,10 @@ JITMETADATAMETRIC(ProfileInconsistentInlineeScale, int, 0) JITMETADATAMETRIC(ProfileInconsistentInlinee, int, 0) JITMETADATAMETRIC(ProfileInconsistentNoReturnInlinee, int, 0) JITMETADATAMETRIC(ProfileInconsistentMayThrowInlinee, int, 0) +JITMETADATAMETRIC(NewRefClassHelperCalls, int, 0) +JITMETADATAMETRIC(StackAllocatedRefClasses, int, 0) +JITMETADATAMETRIC(NewBoxedValueClassHelperCalls, int, 0) +JITMETADATAMETRIC(StackAllocatedBoxedValueClasses, int, 0) #undef JITMETADATA #undef JITMETADATAINFO diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 1c1283c0dd050..57d6c2bc8b72a 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -389,9 +389,19 @@ bool ObjectAllocator::MorphAllocObjNodes() GenTreeAllocObj* asAllocObj = data->AsAllocObj(); unsigned int lclNum = stmtExpr->AsLclVar()->GetLclNum(); CORINFO_CLASS_HANDLE clsHnd = data->AsAllocObj()->gtAllocObjClsHnd; + const bool isValueClass = comp->info.compCompHnd->isValueClass(clsHnd); const char* onHeapReason = nullptr; bool canStack = false; + if (isValueClass) + { + comp->Metrics.NewBoxedValueClassHelperCalls++; + } + else + { + comp->Metrics.NewRefClassHelperCalls++; + } + // Don't attempt to do stack allocations inside basic blocks that may be in a loop. // if (!IsObjectStackAllocationEnabled()) @@ -414,7 +424,8 @@ bool ObjectAllocator::MorphAllocObjNodes() JITDUMP("Allocating V%02u on the stack\n", lclNum); canStack = true; - // printf("@@@ SA V%02u (%s) in %s\n", lclNum, comp->eeGetClassName(clsHnd), comp->info.compFullName); + // printf("@@@ SA V%02u (%s) in %s\n", lclNum, comp->eeGetClassName(clsHnd), + // comp->info.compFullName); const unsigned int stackLclNum = MorphAllocObjNodeIntoStackAlloc(asAllocObj, block, stmt); m_HeapLocalToStackLocalMap.AddOrUpdate(lclNum, stackLclNum); @@ -427,7 +438,18 @@ bool ObjectAllocator::MorphAllocObjNodes() didStackAllocate = true; } - if (!canStack) + if (canStack) + { + if (isValueClass) + { + comp->Metrics.StackAllocatedBoxedValueClasses++; + } + else + { + comp->Metrics.StackAllocatedRefClasses++; + } + } + else { assert(onHeapReason != nullptr); JITDUMP("Allocating V%02u on the heap: %s\n", lclNum, onHeapReason); From 20a83b4a32f10c797e8f93ff5a93666ff2be7719 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 13 Jun 2024 18:01:33 -0700 Subject: [PATCH 13/45] fix object stack allocation test --- src/coreclr/jit/objectalloc.cpp | 1 + .../opt/ObjectStackAllocation/ObjectStackAllocationTests.cs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 57d6c2bc8b72a..9d50d10d5a3c1 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -672,6 +672,7 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack* parent case GT_QMARK: case GT_ADD: case GT_BOX: + case GT_FIELD_ADDR: // Check whether the local escapes via its grandparent. ++parentIndex; keepChecking = true; diff --git a/src/tests/JIT/opt/ObjectStackAllocation/ObjectStackAllocationTests.cs b/src/tests/JIT/opt/ObjectStackAllocation/ObjectStackAllocationTests.cs index 9faf9b4cce4f0..5a45f43dcc7ca 100644 --- a/src/tests/JIT/opt/ObjectStackAllocation/ObjectStackAllocationTests.cs +++ b/src/tests/JIT/opt/ObjectStackAllocation/ObjectStackAllocationTests.cs @@ -152,6 +152,9 @@ public static int TestEntryPoint() CallTestAndVerifyAllocation(AllocateClassWithGcFieldAndInt, 5, expectedAllocationKind); + // Stack allocation of boxed structs is now enabled + CallTestAndVerifyAllocation(BoxSimpleStructAndAddFields, 12, expectedAllocationKind); + // The remaining tests currently never allocate on the stack if (expectedAllocationKind == AllocationKind.Stack) { expectedAllocationKind = AllocationKind.Heap; @@ -163,9 +166,6 @@ public static int TestEntryPoint() // This test calls CORINFO_HELP_CHKCASTCLASS_SPECIAL CallTestAndVerifyAllocation(AllocateSimpleClassAndCast, 7, expectedAllocationKind); - // Stack allocation of boxed structs is currently disabled - CallTestAndVerifyAllocation(BoxSimpleStructAndAddFields, 12, expectedAllocationKind); - return methodResult; } From d9d299c137aa95e3f75cdd3f29ff863ae6c7fef5 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 13 Jun 2024 19:59:14 -0700 Subject: [PATCH 14/45] handle not getting a class handle for a box --- src/coreclr/jit/objectalloc.cpp | 40 +++++++++++++++++---------------- src/coreclr/jit/objectalloc.h | 3 ++- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 9d50d10d5a3c1..20a5f1d0541f4 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -389,6 +389,7 @@ bool ObjectAllocator::MorphAllocObjNodes() GenTreeAllocObj* asAllocObj = data->AsAllocObj(); unsigned int lclNum = stmtExpr->AsLclVar()->GetLclNum(); CORINFO_CLASS_HANDLE clsHnd = data->AsAllocObj()->gtAllocObjClsHnd; + CORINFO_CLASS_HANDLE stackClsHnd = clsHnd; const bool isValueClass = comp->info.compCompHnd->isValueClass(clsHnd); const char* onHeapReason = nullptr; bool canStack = false; @@ -396,6 +397,7 @@ bool ObjectAllocator::MorphAllocObjNodes() if (isValueClass) { comp->Metrics.NewBoxedValueClassHelperCalls++; + stackClsHnd = comp->info.compCompHnd->getTypeForBoxOnStack(clsHnd); } else { @@ -419,6 +421,12 @@ 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); @@ -427,7 +435,8 @@ bool ObjectAllocator::MorphAllocObjNodes() // printf("@@@ SA V%02u (%s) in %s\n", lclNum, comp->eeGetClassName(clsHnd), // comp->info.compFullName); - const unsigned int stackLclNum = MorphAllocObjNodeIntoStackAlloc(asAllocObj, block, stmt); + const unsigned int stackLclNum = + MorphAllocObjNodeIntoStackAlloc(asAllocObj, stackClsHnd, isValueClass, block, stmt); m_HeapLocalToStackLocalMap.AddOrUpdate(lclNum, stackLclNum); // We keep the set of possibly-stack-pointing pointers as a superset of the set of // definitely-stack-pointing pointers. All definitely-stack-pointing pointers are in both sets. @@ -527,36 +536,29 @@ GenTree* ObjectAllocator::MorphAllocObjNodeIntoHelperCall(GenTreeAllocObj* alloc // MorphAllocObjNodeIntoStackAlloc: Morph a GT_ALLOCOBJ node into stack // allocation. // Arguments: -// allocObj - GT_ALLOCOBJ that will be replaced by a stack allocation -// block - a basic block where allocObj is -// stmt - a statement where allocObj is +// 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 +// block - a basic block where allocObj is +// stmt - a statement where allocObj is // // Return Value: // local num for the new stack allocated local // // Notes: // This function can insert additional statements before stmt. - -unsigned int ObjectAllocator::MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* allocObj, - BasicBlock* block, - Statement* stmt) +// +unsigned int ObjectAllocator::MorphAllocObjNodeIntoStackAlloc( + GenTreeAllocObj* allocObj, CORINFO_CLASS_HANDLE clsHnd, bool isValueClass, BasicBlock* block, Statement* stmt) { assert(allocObj != nullptr); assert(m_AnalysisDone); + assert(clsHnd != NO_CLASS_HANDLE); - CORINFO_CLASS_HANDLE clsHnd = allocObj->gtAllocObjClsHnd; - const bool isValueClass = comp->info.compCompHnd->isValueClass(clsHnd); - const bool shortLifetime = false; - const unsigned int lclNum = comp->lvaGrabTemp(shortLifetime DEBUGARG( + const bool shortLifetime = false; + const unsigned int lclNum = comp->lvaGrabTemp(shortLifetime DEBUGARG( isValueClass ? "stack allocated boxed value class temp" : "stack allocated ref class temp")); - if (isValueClass) - { - clsHnd = comp->info.compCompHnd->getTypeForBoxOnStack(clsHnd); - // must pre-check for this - assert(clsHnd != NO_CLASS_HANDLE); - } - comp->lvaSetStruct(lclNum, clsHnd, /* unsafeValueClsCheck */ false); // Initialize the object memory if necessary. diff --git a/src/coreclr/jit/objectalloc.h b/src/coreclr/jit/objectalloc.h index c384faba7f169..8a873be8b34ad 100644 --- a/src/coreclr/jit/objectalloc.h +++ b/src/coreclr/jit/objectalloc.h @@ -62,7 +62,8 @@ class ObjectAllocator final : public Phase bool MorphAllocObjNodes(); void RewriteUses(); GenTree* MorphAllocObjNodeIntoHelperCall(GenTreeAllocObj* allocObj); - unsigned int MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* allocObj, BasicBlock* block, Statement* stmt); + unsigned int MorphAllocObjNodeIntoStackAlloc( + GenTreeAllocObj* allocObj, CORINFO_CLASS_HANDLE clsHnd, bool isValueClass, BasicBlock* block, Statement* stmt); struct BuildConnGraphVisitorCallbackData; bool CanLclVarEscapeViaParentStack(ArrayStack* parentStack, unsigned int lclNum); void UpdateAncestorTypes(GenTree* tree, ArrayStack* parentStack, var_types newType); From cc1037efbc6312874838fb5735f12af1be4ae5df Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 14 Jun 2024 18:05:38 -0700 Subject: [PATCH 15/45] don't recycle packet number --- src/coreclr/inc/jiteeversionguid.h | 10 +++++----- .../tools/superpmi/superpmi-shared/methodcontext.h | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 32f271fe494ed..1176e7f7b793e 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 70f467a0-782b-48c2-9484-68239488e64a */ - 0x70f467a0, - 0x782b, - 0x48c2, - {0x94, 0x84, 0x68, 0x23, 0x94, 0x88, 0xe6, 0x4a} +constexpr GUID JITEEVersionIdentifier = { /* 476dc9ca-35a9-45a1-b0d3-67fc3137559f */ + 0x476dc9ca, + 0x35a9, + 0x45a1, + {0xb0, 0xd3, 0x67, 0xfc, 0x31, 0x37, 0x55, 0x9f} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 16977e0375fce..e65ea52c85d37 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -1048,7 +1048,7 @@ enum mcPackets Packet_GetRelocTypeHint = 84, //Packet_GetSecurityPrologHelper = 85, Packet_GetSharedCCtorHelper = 86, - Packet_GetTypeForBoxOnStack = 87, + //Packet_GetTailCallCopyArgsThunk = 87, Packet_GetThreadTLSIndex = 88, Packet_GetTokenTypeAsHandle = 89, Packet_GetTypeForBox = 90, @@ -1180,6 +1180,7 @@ enum mcPackets Packet_IsNullableType = 217, Packet_GetClassStaticDynamicInfo = 218, Packet_GetClassThreadStaticDynamicInfo = 219, + Packet_GetTypeForBoxOnStack = 220, }; void SetDebugDumpVariables(); From 3964a099c48de7991303274c86915baa23b61612 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Sun, 16 Jun 2024 08:41:39 -0700 Subject: [PATCH 16/45] fix nullcheck exposure in local morph --- src/coreclr/jit/lclmorph.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 7df1fa77333f2..b99eb7c3e9b6d 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -1070,10 +1070,15 @@ class LocalAddressVisitor final : public GenTreeVisitor { JITDUMP("Bashing nullcheck of local [%06u] to NOP\n", m_compiler->dspTreeID(node)); node->gtBashToNOP(); + INDEBUG(TopValue(0).Consume()); + PopValue(); m_stmtModified = true; } - EscapeValue(TopValue(0), node); - PopValue(); + else + { + EscapeValue(TopValue(0), node); + PopValue(); + } break; } From b1c986d70fa45ed876367a3e58ab94fc028afc94 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 17 Jun 2024 14:44:21 -0700 Subject: [PATCH 17/45] allow promotion of address-exposed stack allocated boxes --- src/coreclr/jit/compiler.h | 5 +++++ src/coreclr/jit/promotion.cpp | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 608b245ece752..6ce124c901bbc 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -807,6 +807,11 @@ class LclVarDsc return lvIsMultiRegArg || lvIsMultiRegRet; } + bool IsStackAllocatedBox() const + { + return lvStackAllocatedBox; + } + #if defined(DEBUG) private: DoNotEnregisterReason m_doNotEnregReason; diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index b656f1576d0d9..047359757790e 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -1020,7 +1020,7 @@ class LocalsUseVisitor : public GenTreeVisitor ClassLayout* accessLayout; AccessKindFlags accessFlags; - if (lcl->OperIs(GT_LCL_ADDR)) + if (lcl->OperIs(GT_LCL_ADDR) && !dsc->IsStackAllocatedBox()) { assert(user->OperIs(GT_CALL) && dsc->IsHiddenBufferStructArg() && (user->AsCall()->gtArgs.GetRetBufferArg()->GetNode() == lcl)); @@ -1410,7 +1410,7 @@ class LocalsUseVisitor : public GenTreeVisitor // AccessKindFlags ClassifyLocalAccess(GenTreeLclVarCommon* lcl, GenTree* user) { - assert(lcl->OperIsLocalRead() || lcl->OperIsLocalStore()); + // assert(lcl->OperIsLocalRead() || lcl->OperIsLocalStore()); AccessKindFlags flags = AccessKindFlags::None; if (lcl->OperIsLocalStore()) @@ -2937,7 +2937,7 @@ bool Promotion::HaveCandidateLocals() // bool Promotion::IsCandidateForPhysicalPromotion(LclVarDsc* dsc) { - return (dsc->TypeGet() == TYP_STRUCT) && !dsc->lvPromoted && !dsc->IsAddressExposed(); + return (dsc->TypeGet() == TYP_STRUCT) && !dsc->lvPromoted && (!dsc->IsAddressExposed()|| dsc->IsStackAllocatedBox()); } //------------------------------------------------------------------------ From ed41d78594e39550d6bb306db3fd5993365cacb1 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 17 Jun 2024 17:33:13 -0700 Subject: [PATCH 18/45] unbox helper only captures type --- src/coreclr/jit/objectalloc.cpp | 4 +++- src/coreclr/jit/promotion.cpp | 3 ++- src/coreclr/jit/utils.cpp | 11 +++++++---- src/coreclr/jit/utils.h | 8 ++++++++ src/coreclr/vm/jitinterface.cpp | 4 ++++ 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 20a5f1d0541f4..c16b4bb90593d 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -705,7 +705,8 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack* parent // 2. Protect objects as interior (GCPROTECT_BEGININTERIOR() instead of GCPROTECT_BEGIN()). // 3. Don't check that the object is in the heap in ValidateInner. - canLclVarEscapeViaParentStack = true; + canLclVarEscapeViaParentStack = + !Compiler::s_helperCallProperties.IsNoEscape(comp->eeGetHelperNum(asCall->gtCallMethHnd)); } break; } @@ -814,6 +815,7 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p break; case GT_IND: + case GT_CALL: break; default: diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index 047359757790e..12064c18fe6a7 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -2937,7 +2937,8 @@ bool Promotion::HaveCandidateLocals() // bool Promotion::IsCandidateForPhysicalPromotion(LclVarDsc* dsc) { - return (dsc->TypeGet() == TYP_STRUCT) && !dsc->lvPromoted && (!dsc->IsAddressExposed()|| dsc->IsStackAllocatedBox()); + return (dsc->TypeGet() == TYP_STRUCT) && !dsc->lvPromoted && + (!dsc->IsAddressExposed() || dsc->IsStackAllocatedBox()); } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 0bdd731f09c88..0db69f0a1a820 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1519,6 +1519,7 @@ void HelperCallProperties::init() bool isAllocator = false; // true if the result is usually a newly created heap item, or may throw OutOfMemory bool mutatesHeap = false; // true if any previous heap objects [are|can be] modified bool mayRunCctor = false; // true if the helper call may cause a static constructor to be run. + bool isNoEscape = false; // true if none of the GC ref arguments can escape switch (helper) { @@ -1643,8 +1644,9 @@ void HelperCallProperties::init() case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE: case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE: - isPure = true; - noThrow = true; // These return null for a failing cast + isNoEscape = true; + isPure = true; + noThrow = true; // These return null for a failing cast break; case CORINFO_HELP_GETCURRENTMANAGEDTHREADID: @@ -1668,8 +1670,8 @@ void HelperCallProperties::init() // helpers returning addresses, these can also throw case CORINFO_HELP_UNBOX: case CORINFO_HELP_LDELEMA_REF: - - isPure = true; + isNoEscape = true; + isPure = true; break; // GETREFANY is pure up to the value of the struct argument. We @@ -1830,6 +1832,7 @@ void HelperCallProperties::init() m_isAllocator[helper] = isAllocator; m_mutatesHeap[helper] = mutatesHeap; m_mayRunCctor[helper] = mayRunCctor; + m_isNoEscape[helper] = isNoEscape; } } diff --git a/src/coreclr/jit/utils.h b/src/coreclr/jit/utils.h index f665a4e813eff..29a1d7d29c933 100644 --- a/src/coreclr/jit/utils.h +++ b/src/coreclr/jit/utils.h @@ -590,6 +590,7 @@ class HelperCallProperties bool m_isAllocator[CORINFO_HELP_COUNT]; bool m_mutatesHeap[CORINFO_HELP_COUNT]; bool m_mayRunCctor[CORINFO_HELP_COUNT]; + bool m_isNoEscape[CORINFO_HELP_COUNT]; void init(); @@ -647,6 +648,13 @@ class HelperCallProperties assert(helperId < CORINFO_HELP_COUNT); return m_mayRunCctor[helperId]; } + + bool IsNoEscape(CorInfoHelpFunc helperId) + { + assert(helperId > CORINFO_HELP_UNDEF); + assert(helperId < CORINFO_HELP_COUNT); + return m_isNoEscape[helperId]; + } }; //***************************************************************************** diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index d9519e8e8b7bf..21237c80b6ea2 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1948,6 +1948,10 @@ bool CEEInfo::canAllocateOnStack(CORINFO_CLASS_HANDLE clsHnd) result = !pMT->HasFinalizer(); +#ifdef FEATURE_COMINTEROP + result &= !pMT->IsComObjectType(); +#endif // FEATURE_COMINTEROP + EE_TO_JIT_TRANSITION_LEAF(); return result; } From 536b2ae6d84b6d3c14d4c6d51ad54de5d50516ff Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 17 Jun 2024 22:44:55 -0700 Subject: [PATCH 19/45] restore lost guid update and related --- src/coreclr/inc/jiteeversionguid.h | 10 +- .../JitInterface/CorInfoImpl_generated.cs | 227 +++++++++--------- 2 files changed, 119 insertions(+), 118 deletions(-) diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 76a1bbc466d79..0377eda05b68a 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* e428e66d-5e0e-4320-ad8a-fa5a50f6da07 */ - 0xe428e66d, - 0x5e0e, - 0x4320, - {0xad, 0x8a, 0xfa, 0x5a, 0x50, 0xf6, 0xda, 0x07} +constexpr GUID JITEEVersionIdentifier = { /* d2316b2e-4571-45c2-b186-b5d46474a09c */ + 0xd2316b2e, + 0x4571, + 0x45c2, + {0xb1, 0x86, 0xb5, 0xd4, 0x64, 0x74, 0xa0, 0x9c} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index c51f7c2ab5203..16840f73581a1 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -2624,7 +2624,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 176); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 177); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_notifyMethodInfoUsage; @@ -2690,118 +2690,119 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[61] = (delegate* unmanaged)&_getCastingHelper; callbacks[62] = (delegate* unmanaged)&_getSharedCCtorHelper; callbacks[63] = (delegate* unmanaged)&_getTypeForBox; - callbacks[64] = (delegate* unmanaged)&_getBoxHelper; - callbacks[65] = (delegate* unmanaged)&_getUnBoxHelper; - callbacks[66] = (delegate* unmanaged)&_getRuntimeTypePointer; - callbacks[67] = (delegate* unmanaged)&_isObjectImmutable; - callbacks[68] = (delegate* unmanaged)&_getStringChar; - callbacks[69] = (delegate* unmanaged)&_getObjectType; - callbacks[70] = (delegate* unmanaged)&_getReadyToRunHelper; - callbacks[71] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; - callbacks[72] = (delegate* unmanaged)&_initClass; - callbacks[73] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; - callbacks[74] = (delegate* unmanaged)&_getBuiltinClass; - callbacks[75] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; - callbacks[76] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; - callbacks[77] = (delegate* unmanaged)&_canCast; - callbacks[78] = (delegate* unmanaged)&_compareTypesForCast; - callbacks[79] = (delegate* unmanaged)&_compareTypesForEquality; - callbacks[80] = (delegate* unmanaged)&_isMoreSpecificType; - callbacks[81] = (delegate* unmanaged)&_isExactType; - callbacks[82] = (delegate* unmanaged)&_isGenericType; - callbacks[83] = (delegate* unmanaged)&_isNullableType; - callbacks[84] = (delegate* unmanaged)&_isEnum; - callbacks[85] = (delegate* unmanaged)&_getParentType; - callbacks[86] = (delegate* unmanaged)&_getChildType; - callbacks[87] = (delegate* unmanaged)&_isSDArray; - callbacks[88] = (delegate* unmanaged)&_getArrayRank; - callbacks[89] = (delegate* unmanaged)&_getArrayIntrinsicID; - callbacks[90] = (delegate* unmanaged)&_getArrayInitializationData; - callbacks[91] = (delegate* unmanaged)&_canAccessClass; - callbacks[92] = (delegate* unmanaged)&_printFieldName; - callbacks[93] = (delegate* unmanaged)&_getFieldClass; - callbacks[94] = (delegate* unmanaged)&_getFieldType; - callbacks[95] = (delegate* unmanaged)&_getFieldOffset; - callbacks[96] = (delegate* unmanaged)&_getFieldInfo; - callbacks[97] = (delegate* unmanaged)&_getThreadLocalFieldInfo; - callbacks[98] = (delegate* unmanaged)&_getThreadLocalStaticBlocksInfo; - callbacks[99] = (delegate* unmanaged)&_getThreadLocalStaticInfo_NativeAOT; - callbacks[100] = (delegate* unmanaged)&_isFieldStatic; - callbacks[101] = (delegate* unmanaged)&_getArrayOrStringLength; - callbacks[102] = (delegate* unmanaged)&_getBoundaries; - callbacks[103] = (delegate* unmanaged)&_setBoundaries; - callbacks[104] = (delegate* unmanaged)&_getVars; - callbacks[105] = (delegate* unmanaged)&_setVars; - callbacks[106] = (delegate* unmanaged)&_reportRichMappings; - callbacks[107] = (delegate* unmanaged)&_reportMetadata; - callbacks[108] = (delegate* unmanaged)&_allocateArray; - callbacks[109] = (delegate* unmanaged)&_freeArray; - callbacks[110] = (delegate* unmanaged)&_getArgNext; - callbacks[111] = (delegate* unmanaged)&_getArgType; - callbacks[112] = (delegate* unmanaged)&_getExactClasses; - callbacks[113] = (delegate* unmanaged)&_getArgClass; - callbacks[114] = (delegate* unmanaged)&_getHFAType; - callbacks[115] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[116] = (delegate* unmanaged)&_runWithSPMIErrorTrap; - callbacks[117] = (delegate* unmanaged)&_getEEInfo; - callbacks[118] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[119] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[120] = (delegate* unmanaged)&_printMethodName; - callbacks[121] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[122] = (delegate* unmanaged)&_getMethodHash; - callbacks[123] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[124] = (delegate* unmanaged)&_getSwiftLowering; - callbacks[125] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[126] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; - callbacks[127] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[128] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[129] = (delegate* unmanaged)&_getHelperFtn; - callbacks[130] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[131] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[132] = (delegate* unmanaged)&_getMethodSync; - callbacks[133] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[134] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[135] = (delegate* unmanaged)&_embedClassHandle; - callbacks[136] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[137] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[138] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[139] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[140] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[141] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[142] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[143] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[144] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[145] = (delegate* unmanaged)&_getCallInfo; - callbacks[146] = (delegate* unmanaged)&_getStaticFieldContent; - callbacks[147] = (delegate* unmanaged)&_getObjectContent; - callbacks[148] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[149] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[150] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[151] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[152] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[153] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[154] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[155] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[156] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[157] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[158] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[159] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[160] = (delegate* unmanaged)&_allocMem; - callbacks[161] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[162] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[163] = (delegate* unmanaged)&_allocGCInfo; - callbacks[164] = (delegate* unmanaged)&_setEHcount; - callbacks[165] = (delegate* unmanaged)&_setEHinfo; - callbacks[166] = (delegate* unmanaged)&_logMsg; - callbacks[167] = (delegate* unmanaged)&_doAssert; - callbacks[168] = (delegate* unmanaged)&_reportFatalError; - callbacks[169] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[170] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[171] = (delegate* unmanaged)&_recordCallSite; - callbacks[172] = (delegate* unmanaged)&_recordRelocation; - callbacks[173] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[174] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[175] = (delegate* unmanaged)&_getJitFlags; + callbacks[64] = (delegate* unmanaged)&_getTypeForBoxOnStack; + callbacks[65] = (delegate* unmanaged)&_getBoxHelper; + callbacks[66] = (delegate* unmanaged)&_getUnBoxHelper; + callbacks[67] = (delegate* unmanaged)&_getRuntimeTypePointer; + callbacks[68] = (delegate* unmanaged)&_isObjectImmutable; + callbacks[69] = (delegate* unmanaged)&_getStringChar; + callbacks[70] = (delegate* unmanaged)&_getObjectType; + callbacks[71] = (delegate* unmanaged)&_getReadyToRunHelper; + callbacks[72] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; + callbacks[73] = (delegate* unmanaged)&_initClass; + callbacks[74] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; + callbacks[75] = (delegate* unmanaged)&_getBuiltinClass; + callbacks[76] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; + callbacks[77] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; + callbacks[78] = (delegate* unmanaged)&_canCast; + callbacks[79] = (delegate* unmanaged)&_compareTypesForCast; + callbacks[80] = (delegate* unmanaged)&_compareTypesForEquality; + callbacks[81] = (delegate* unmanaged)&_isMoreSpecificType; + callbacks[82] = (delegate* unmanaged)&_isExactType; + callbacks[83] = (delegate* unmanaged)&_isGenericType; + callbacks[84] = (delegate* unmanaged)&_isNullableType; + callbacks[85] = (delegate* unmanaged)&_isEnum; + callbacks[86] = (delegate* unmanaged)&_getParentType; + callbacks[87] = (delegate* unmanaged)&_getChildType; + callbacks[88] = (delegate* unmanaged)&_isSDArray; + callbacks[89] = (delegate* unmanaged)&_getArrayRank; + callbacks[90] = (delegate* unmanaged)&_getArrayIntrinsicID; + callbacks[91] = (delegate* unmanaged)&_getArrayInitializationData; + callbacks[92] = (delegate* unmanaged)&_canAccessClass; + callbacks[93] = (delegate* unmanaged)&_printFieldName; + callbacks[94] = (delegate* unmanaged)&_getFieldClass; + callbacks[95] = (delegate* unmanaged)&_getFieldType; + callbacks[96] = (delegate* unmanaged)&_getFieldOffset; + callbacks[97] = (delegate* unmanaged)&_getFieldInfo; + callbacks[98] = (delegate* unmanaged)&_getThreadLocalFieldInfo; + callbacks[99] = (delegate* unmanaged)&_getThreadLocalStaticBlocksInfo; + callbacks[100] = (delegate* unmanaged)&_getThreadLocalStaticInfo_NativeAOT; + callbacks[101] = (delegate* unmanaged)&_isFieldStatic; + callbacks[102] = (delegate* unmanaged)&_getArrayOrStringLength; + callbacks[103] = (delegate* unmanaged)&_getBoundaries; + callbacks[104] = (delegate* unmanaged)&_setBoundaries; + callbacks[105] = (delegate* unmanaged)&_getVars; + callbacks[106] = (delegate* unmanaged)&_setVars; + callbacks[107] = (delegate* unmanaged)&_reportRichMappings; + callbacks[108] = (delegate* unmanaged)&_reportMetadata; + callbacks[109] = (delegate* unmanaged)&_allocateArray; + callbacks[110] = (delegate* unmanaged)&_freeArray; + callbacks[111] = (delegate* unmanaged)&_getArgNext; + callbacks[112] = (delegate* unmanaged)&_getArgType; + callbacks[113] = (delegate* unmanaged)&_getExactClasses; + callbacks[114] = (delegate* unmanaged)&_getArgClass; + callbacks[115] = (delegate* unmanaged)&_getHFAType; + callbacks[116] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[117] = (delegate* unmanaged)&_runWithSPMIErrorTrap; + callbacks[118] = (delegate* unmanaged)&_getEEInfo; + callbacks[119] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[120] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[121] = (delegate* unmanaged)&_printMethodName; + callbacks[122] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[123] = (delegate* unmanaged)&_getMethodHash; + callbacks[124] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[125] = (delegate* unmanaged)&_getSwiftLowering; + callbacks[126] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; + callbacks[127] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; + callbacks[128] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[129] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[130] = (delegate* unmanaged)&_getHelperFtn; + callbacks[131] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[132] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[133] = (delegate* unmanaged)&_getMethodSync; + callbacks[134] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[135] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[136] = (delegate* unmanaged)&_embedClassHandle; + callbacks[137] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[138] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[139] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[140] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[141] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[142] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[143] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[144] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[145] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[146] = (delegate* unmanaged)&_getCallInfo; + callbacks[147] = (delegate* unmanaged)&_getStaticFieldContent; + callbacks[148] = (delegate* unmanaged)&_getObjectContent; + callbacks[149] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[150] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[151] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[152] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[153] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[154] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[155] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[156] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[157] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[158] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[159] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[160] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[161] = (delegate* unmanaged)&_allocMem; + callbacks[162] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[163] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[164] = (delegate* unmanaged)&_allocGCInfo; + callbacks[165] = (delegate* unmanaged)&_setEHcount; + callbacks[166] = (delegate* unmanaged)&_setEHinfo; + callbacks[167] = (delegate* unmanaged)&_logMsg; + callbacks[168] = (delegate* unmanaged)&_doAssert; + callbacks[169] = (delegate* unmanaged)&_reportFatalError; + callbacks[170] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[171] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[172] = (delegate* unmanaged)&_recordCallSite; + callbacks[173] = (delegate* unmanaged)&_recordRelocation; + callbacks[174] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[175] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[176] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } From 5e9e3fb6c12d58e7ecbd7ff3067dbc670b285da4 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Tue, 18 Jun 2024 11:31:14 -0700 Subject: [PATCH 20/45] boost inlining more if we pass a box as an arg --- src/coreclr/jit/inlinepolicy.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/inlinepolicy.cpp b/src/coreclr/jit/inlinepolicy.cpp index 3b771f291607b..74581756c1490 100644 --- a/src/coreclr/jit/inlinepolicy.cpp +++ b/src/coreclr/jit/inlinepolicy.cpp @@ -1628,7 +1628,16 @@ double ExtendedDefaultPolicy::DetermineMultiplier() // // void Caller() => DoNothing(42); // 42 is going to be boxed at the call site. // - multiplier += 0.5; + if (JitConfig.JitObjectStackAllocation() > 0) + { + // We might also be able to stack allocate the box + // + multiplier += 3.0 + m_ArgIsBoxedAtCallsite; + } + else + { + multiplier += 0.5; + } JITDUMP("\nCallsite is going to box %d arguments. Multiplier increased to %g.", m_ArgIsBoxedAtCallsite, multiplier); } From 0685c5849a1e6a8661ea0c5c3edeee6e65607c2d Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Tue, 18 Jun 2024 14:41:18 -0700 Subject: [PATCH 21/45] try and fix promotion to write back if there are helper call struct uses --- src/coreclr/jit/objectalloc.cpp | 6 +++--- src/coreclr/jit/promotion.cpp | 35 +++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index c16b4bb90593d..8cacb31f493f4 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -431,9 +431,9 @@ bool ObjectAllocator::MorphAllocObjNodes() { JITDUMP("Allocating V%02u on the stack\n", lclNum); canStack = true; - - // printf("@@@ SA V%02u (%s) in %s\n", lclNum, comp->eeGetClassName(clsHnd), - // comp->info.compFullName); +// #ifdef DEBUG +// printf("@@@ SA V%02u (%s) in %s\n", lclNum, comp->eeGetClassName(clsHnd), comp->info.compFullName); +// #endif const unsigned int stackLclNum = MorphAllocObjNodeIntoStackAlloc(asAllocObj, stackClsHnd, isValueClass, block, stmt); diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index 12064c18fe6a7..ca32c0f3ababe 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -1020,14 +1020,24 @@ class LocalsUseVisitor : public GenTreeVisitor ClassLayout* accessLayout; AccessKindFlags accessFlags; - if (lcl->OperIs(GT_LCL_ADDR) && !dsc->IsStackAllocatedBox()) + if (lcl->OperIs(GT_LCL_ADDR) && user->OperIs(GT_CALL)) { - assert(user->OperIs(GT_CALL) && dsc->IsHiddenBufferStructArg() && - (user->AsCall()->gtArgs.GetRetBufferArg()->GetNode() == lcl)); + if (dsc->IsStackAllocatedBox()) + { + // TODO: assert this is a call to a "no-escape helper" + accessType = TYP_STRUCT; + accessLayout = dsc->GetLayout(); + accessFlags = AccessKindFlags::IsCallArg; + } + else + { + assert(dsc->IsHiddenBufferStructArg() && + (user->AsCall()->gtArgs.GetRetBufferArg()->GetNode() == lcl)); - accessType = TYP_STRUCT; - accessLayout = m_compiler->typGetObjLayout(user->AsCall()->gtRetClsHnd); - accessFlags = AccessKindFlags::IsCallRetBuf; + accessType = TYP_STRUCT; + accessLayout = m_compiler->typGetObjLayout(user->AsCall()->gtRetClsHnd); + accessFlags = AccessKindFlags::IsCallRetBuf; + } } else { @@ -2294,14 +2304,19 @@ void ReplaceVisitor::InsertPreStatementWriteBacks() for (CallArg& arg : call->gtArgs.Args()) { GenTree* node = arg.GetNode()->gtEffectiveVal(); - if (!node->TypeIs(TYP_STRUCT) || !node->OperIsLocalRead()) + + bool writeBackHelperArg = + node->OperIs(GT_LCL_ADDR) && (arg.GetWellKnownArg() != WellKnownArg::RetBuffer); + bool writeBackStructArg = node->TypeIs(TYP_STRUCT) && node->OperIsLocalRead(); + + if (!writeBackHelperArg && !writeBackStructArg) { continue; } - GenTreeLclVarCommon* lcl = node->AsLclVarCommon(); - m_replacer->WriteBackBeforeCurrentStatement(lcl->GetLclNum(), lcl->GetLclOffs(), - lcl->GetLayout(m_compiler)->GetSize()); + GenTreeLclVarCommon* lcl = node->AsLclVarCommon(); + ClassLayout* layout = m_compiler->lvaGetDesc(lcl)->GetLayout(); + m_replacer->WriteBackBeforeCurrentStatement(lcl->GetLclNum(), lcl->GetLclOffs(), layout->GetSize()); } } From 07f68eea70a6e6cd0ae27b37d7f8a2ac43f65c65 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 19 Jun 2024 08:49:06 -0700 Subject: [PATCH 22/45] fix physical promotion writeback logic --- src/coreclr/jit/objectalloc.cpp | 7 ++++--- src/coreclr/jit/promotion.cpp | 8 +++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 8cacb31f493f4..d3b3300d94b44 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -431,9 +431,10 @@ bool ObjectAllocator::MorphAllocObjNodes() { JITDUMP("Allocating V%02u on the stack\n", lclNum); canStack = true; -// #ifdef DEBUG -// printf("@@@ SA V%02u (%s) in %s\n", lclNum, comp->eeGetClassName(clsHnd), comp->info.compFullName); -// #endif + // #ifdef DEBUG + // printf("@@@ SA V%02u (%s) in %s\n", lclNum, comp->eeGetClassName(clsHnd), + // comp->info.compFullName); + // #endif const unsigned int stackLclNum = MorphAllocObjNodeIntoStackAlloc(asAllocObj, stackClsHnd, isValueClass, block, stmt); diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index ca32c0f3ababe..1eb3e1f2d91e7 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -2314,9 +2314,11 @@ void ReplaceVisitor::InsertPreStatementWriteBacks() continue; } - GenTreeLclVarCommon* lcl = node->AsLclVarCommon(); - ClassLayout* layout = m_compiler->lvaGetDesc(lcl)->GetLayout(); - m_replacer->WriteBackBeforeCurrentStatement(lcl->GetLclNum(), lcl->GetLclOffs(), layout->GetSize()); + GenTreeLclVarCommon* lcl = node->AsLclVarCommon(); + LclVarDsc* dsc = m_compiler->lvaGetDesc(lcl); + unsigned size = writeBackHelperArg ? dsc->lvExactSize() : dsc->GetLayout()->GetSize(); + + m_replacer->WriteBackBeforeCurrentStatement(lcl->GetLclNum(), lcl->GetLclOffs(), size); } } From 2ae83e6d025e2dcc9bba288eda2baeed412814fa Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 19 Jun 2024 09:18:14 -0700 Subject: [PATCH 23/45] fix layout access for regular case --- src/coreclr/jit/promotion.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index 1eb3e1f2d91e7..a51b91505b3ae 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -2314,9 +2314,9 @@ void ReplaceVisitor::InsertPreStatementWriteBacks() continue; } - GenTreeLclVarCommon* lcl = node->AsLclVarCommon(); - LclVarDsc* dsc = m_compiler->lvaGetDesc(lcl); - unsigned size = writeBackHelperArg ? dsc->lvExactSize() : dsc->GetLayout()->GetSize(); + GenTreeLclVarCommon* lcl = node->AsLclVarCommon(); + LclVarDsc* dsc = m_compiler->lvaGetDesc(lcl); + unsigned size = writeBackHelperArg ? dsc->lvExactSize() : lcl->GetLayout(m_compiler)->GetSize(); m_replacer->WriteBackBeforeCurrentStatement(lcl->GetLclNum(), lcl->GetLclOffs(), size); } From 1cd10228fe867db2e4342969af2edec0357888e8 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 19 Jun 2024 10:51:47 -0700 Subject: [PATCH 24/45] handle more address comparisons in local morph --- src/coreclr/jit/lclmorph.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index a010ca6939279..aeed8fcc5610d 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -515,6 +515,13 @@ class LocalAddressVisitor final : public GenTreeVisitor return m_offset; } + bool IsSameAddress(const Value& other) const + { + assert(IsAddress() && other.IsAddress()); + + return ((LclNum() == other.LclNum()) && (Offset() == other.Offset())); + } + //------------------------------------------------------------------------ // Address: Produce an address value from a LCL_ADDR node. // @@ -1040,7 +1047,7 @@ class LocalAddressVisitor final : public GenTreeVisitor if ((lhs.IsAddress() && rhs.Node()->IsIntegralConst(0)) || (rhs.IsAddress() && lhs.Node()->IsIntegralConst(0))) { - JITDUMP("Rewriting known address-of comparison [%06u]\n", m_compiler->dspTreeID(node)); + JITDUMP("Rewriting known address vs null comparison [%06u]\n", m_compiler->dspTreeID(node)); *lhs.Use() = m_compiler->gtNewIconNode(0); *rhs.Use() = m_compiler->gtNewIconNode(1); m_stmtModified = true; @@ -1050,6 +1057,19 @@ class LocalAddressVisitor final : public GenTreeVisitor PopValue(); PopValue(); } + else if (lhs.IsAddress() && rhs.IsAddress()) + { + JITDUMP("Rewriting known address vs address comparison [%06u]\n", m_compiler->dspTreeID(node)); + bool isSameAddress = lhs.IsSameAddress(rhs); + *lhs.Use() = m_compiler->gtNewIconNode(0); + *rhs.Use() = m_compiler->gtNewIconNode(isSameAddress ? 0 : 1); + m_stmtModified = true; + + INDEBUG(TopValue(0).Consume()); + INDEBUG(TopValue(1).Consume()); + PopValue(); + PopValue(); + } else { EscapeValue(TopValue(0), node); From 40bb5602921ff5be40d0c2485dbd47cae0044f34 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 19 Jun 2024 15:28:02 -0700 Subject: [PATCH 25/45] unbox type test helper --- src/coreclr/inc/corinfo.h | 1 + src/coreclr/inc/jiteeversionguid.h | 10 ++-- src/coreclr/inc/jithelpers.h | 1 + src/coreclr/jit/utils.cpp | 8 ++- .../Common/JitInterface/CorInfoHelpFunc.cs | 1 + src/coreclr/vm/ecalllist.h | 1 + src/coreclr/vm/jithelpers.cpp | 59 +++++++++++++++++++ src/coreclr/vm/jitinterface.h | 1 + 8 files changed, 75 insertions(+), 7 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index d20b1722a8af0..cc1ca62f47485 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -445,6 +445,7 @@ enum CorInfoHelpFunc CORINFO_HELP_BOX, // Fast box helper. Only possible exception is OutOfMemory CORINFO_HELP_BOX_NULLABLE, // special form of boxing for Nullable CORINFO_HELP_UNBOX, + CORINFO_HELP_UNBOX_TYPETEST, // Verify unbox type, throws if incompatible CORINFO_HELP_UNBOX_NULLABLE, // special form of unboxing for Nullable CORINFO_HELP_GETREFANY, // Extract the byref from a TypedReference, checking that it is the expected type diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 0377eda05b68a..ff7129df56b56 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* d2316b2e-4571-45c2-b186-b5d46474a09c */ - 0xd2316b2e, - 0x4571, - 0x45c2, - {0xb1, 0x86, 0xb5, 0xd4, 0x64, 0x74, 0xa0, 0x9c} +constexpr GUID JITEEVersionIdentifier = { /* 748cd07e-c686-4085-8f5f-54c1b9cff44c */ + 0x748cd07e, + 0xc686, + 0x4085, + {0x8f, 0x5f, 0x54, 0xc1, 0xb9, 0xcf, 0xf4, 0x4c} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index 50beaabe66e90..4273bebca40b7 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -106,6 +106,7 @@ DYNAMICJITHELPER(CORINFO_HELP_BOX, JIT_Box, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_BOX_NULLABLE, JIT_Box, CORINFO_HELP_SIG_REG_ONLY) DYNAMICJITHELPER(CORINFO_HELP_UNBOX, NULL, CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_UNBOX_TYPETEST, JIT_Unbox_TypeTest, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_UNBOX_NULLABLE, JIT_Unbox_Nullable, CORINFO_HELP_SIG_4_STACK) JITHELPER(CORINFO_HELP_GETREFANY, JIT_GetRefAny, CORINFO_HELP_SIG_8_STACK) diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 0172066e34b23..dd8c8eeedaae5 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1644,7 +1644,7 @@ void HelperCallProperties::init() case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE: case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE: - isNoEscape = true; + // isNoEscape = true; isPure = true; noThrow = true; // These return null for a failing cast break; @@ -1661,6 +1661,7 @@ void HelperCallProperties::init() case CORINFO_HELP_CHKCASTANY: case CORINFO_HELP_CHKCASTCLASS_SPECIAL: case CORINFO_HELP_READYTORUN_CHKCAST: + case CORINFO_HELP_UNBOX_TYPETEST: // These throw for a failing cast // But if given a null input arg will return null @@ -1669,11 +1670,14 @@ void HelperCallProperties::init() // helpers returning addresses, these can also throw case CORINFO_HELP_UNBOX: - case CORINFO_HELP_LDELEMA_REF: isNoEscape = true; isPure = true; break; + case CORINFO_HELP_LDELEMA_REF: + isPure = true; + break; + // GETREFANY is pure up to the value of the struct argument. We // only support that when it is not an implicit byref. case CORINFO_HELP_GETREFANY: diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs index 47d202909bce3..0ba59363c8f9c 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs @@ -87,6 +87,7 @@ which is the right helper to use to allocate an object of a given type. */ CORINFO_HELP_BOX, // Fast box helper. Only possible exception is OutOfMemory CORINFO_HELP_BOX_NULLABLE, // special form of boxing for Nullable CORINFO_HELP_UNBOX, + CORINFO_HELP_UNBOX_TYPETEST, CORINFO_HELP_UNBOX_NULLABLE, // special form of unboxing for Nullable CORINFO_HELP_GETREFANY, // Extract the byref from a TypedReference, checking that it is the expected type diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 4a9f2355f395c..734fa19ad5729 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -356,6 +356,7 @@ FCFuncStart(gCastHelpers) FCFuncElement("IsInstanceOfAny_NoCacheLookup", ::IsInstanceOfAny_NoCacheLookup) FCFuncElement("ChkCastAny_NoCacheLookup", ::ChkCastAny_NoCacheLookup) FCFuncElement("Unbox_Helper", ::Unbox_Helper) + FCFuncElement("JIT_Unbox_TypeTest", ::JIT_Unbox_TypeTest) FCFuncElement("WriteBarrier", ::WriteBarrier_Helper) FCFuncEnd() diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index efdd2de796232..935199ec679a4 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -2257,6 +2257,65 @@ HCIMPL2(LPVOID, Unbox_Helper, CORINFO_CLASS_HANDLE type, Object* obj) } HCIMPLEND +/* framed Unbox type test helper that handles enums and full-blown type equivalence */ +NOINLINE HCIMPL2(void, JIT_Unbox_TypeTest_Framed, MethodTable* pMT1, MethodTable* pMT2) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_0(); + HELPER_METHOD_POLL(); + + if (pMT1->GetInternalCorElementType() == pMT2->GetInternalCorElementType() && + (pMT1->IsEnum() || pMT1->IsTruePrimitive()) && + (pMT2->IsEnum() || pMT2->IsTruePrimitive())) + { + // type test test passes + } + else if (pMT1->IsEquivalentTo(pMT2)) + { + // the structures are equivalent + } + else + { + COMPlusThrowInvalidCastException(TypeHandle(pMT2), TypeHandle(pMT1)); + } + HELPER_METHOD_FRAME_END(); +} +HCIMPLEND + +/*************************************************************/ +/* Unbox type test that handles enums */ +HCIMPL2(void, JIT_Unbox_TypeTest, CORINFO_CLASS_HANDLE type, CORINFO_CLASS_HANDLE boxType) +{ + FCALL_CONTRACT; + + TypeHandle typeHnd(type); + // boxable types have method tables + _ASSERTE(!typeHnd.IsTypeDesc()); + + MethodTable* pMT1 = typeHnd.AsMethodTable(); + // must be a value type + _ASSERTE(pMT1->IsValueType()); + + TypeHandle boxTypeHnd(boxType); + MethodTable* pMT2 = boxTypeHnd.AsMethodTable(); + + // we allow enums and their primitive type to be interchangeable. + // if suspension is requested, defer to the framed helper. + if (pMT1->GetInternalCorElementType() == pMT2->GetInternalCorElementType() && + (pMT1->IsEnum() || pMT1->IsTruePrimitive()) && + (pMT2->IsEnum() || pMT2->IsTruePrimitive()) && + g_TrapReturningThreads == 0) + { + return; + } + + // Fall back to a framed helper that can also handle GC suspension and type equivalence. + ENDFORBIDGC(); + HCCALL2(JIT_Unbox_TypeTest_Framed, pMT1, pMT1); +} +HCIMPLEND + /*************************************************************/ HCIMPL2_IV(LPVOID, JIT_GetRefAny, CORINFO_CLASS_HANDLE type, TypedByRef typedByRef) { diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index f34786e2e994f..68617d39305bd 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -243,6 +243,7 @@ extern "C" FCDECL2(VOID, JIT_WriteBarrierEnsureNonHeapTarget, Object **dst, Obje extern "C" FCDECL2(Object*, ChkCastAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj); extern "C" FCDECL2(Object*, IsInstanceOfAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj); extern "C" FCDECL2(LPVOID, Unbox_Helper, CORINFO_CLASS_HANDLE type, Object* obj); +extern "C" FCDECL2(void, JIT_Unbox_TypeTest, CORINFO_CLASS_HANDLE type, CORINFO_CLASS_HANDLE boxType); extern "C" FCDECL3(void, JIT_Unbox_Nullable, void * destPtr, CORINFO_CLASS_HANDLE type, Object* obj); // ARM64 JIT_WriteBarrier uses speciall ABI and thus is not callable directly From 2beb6b355043bb3aa3507ba1b16b1938cccb47e8 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 19 Jun 2024 16:14:02 -0700 Subject: [PATCH 26/45] rewrite unbox helper calls if we stack allocate the box --- src/coreclr/jit/objectalloc.cpp | 68 ++++++++++++++++++++------------- src/coreclr/jit/utils.cpp | 6 +-- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index d3b3300d94b44..7e952b3501e1d 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -921,40 +921,56 @@ void ObjectAllocator::RewriteUses() } } -#if 0 - // Translate accesses to box payloads to have field seqs + // Make box accesses explicit for UNBOX_HELPER // - if (tree->OperIs(GT_ADD)) + if (tree->IsCall()) { - GenTree* op1 = tree->AsOp()->gtGetOp1(); - GenTree* op2 = tree->AsOp()->gtGetOp2(); - bool tryToTransform = false; - - if (op1->OperIs(GT_LCL_ADDR) && op2->IsIntegralConst()) - { - tryToTransform = true; - } - else if (op2->OperIs(GT_LCL_ADDR) && op1->IsIntegralConst()) + GenTreeCall* const call = tree->AsCall(); + assert(call->IsHelperCall()); + if (call->IsHelperCall(m_compiler, CORINFO_HELP_BOX)) { - std::swap(op1, op2); - tryToTransform = true; - } + // See if first arg is our stack allocated box. + // + GenTree* const firstArg = call->gtArgs.GetArgByIndex(0)->GetNode(); - if (tryToTransform) - { - LclVarDsc* const varDsc = m_compiler->lvaGetDesc(op1->AsLclVarCommon()); - if (varDsc->lvStackAllocatedBox && (op2->AsIntConCommon()->IntegralValue() == TARGET_POINTER_SIZE)) + if (firstArg->OperIs(GT_LCL_ADDR)) { - JITDUMP("Rewriting box payload ADD [%06u]\n", m_compiler->dspTreeID(tree)); - CORINFO_FIELD_HANDLE fieldHnd = m_compiler->info.compCompHnd->getFieldInClass(varDsc->lvClassHnd, 1); - FieldSeq* const fieldSeq = m_compiler->GetFieldSeqStore()->Create(fieldHnd, TARGET_POINTER_SIZE, FieldSeq::FieldKind::Instance); - op2 = m_compiler->gtNewIconNode(TARGET_POINTER_SIZE, fieldSeq); - tree->AsOp()->gtOp1 = op1; - tree->AsOp()->gtOp2 = op2; + GenTreeLclVarCommon* const lcl = firstArg->AsLclVarCommon(); + unsigned const lclNum = lcl->GetLclNum(); + LclVarDsc* dsc = m_compiler->lvaGetDesc(lclNum); + + // assert this? + if (dsc->IsStackAllocatedBox()) + { + // Yes -- rewrite the call to make the box accesses explicit in jitted code. + // user = COMMA(CALL(UNBOX_HELPER_TYPETEST, obj->MethodTable, type), &obj + + // TARGET_POINTER_SIZE) + // + JITDUMP("Rewriting [%06u] to invoke box type test helper\n", m_compiler->dspTreeID(tree)); + + call->gtCallMethHnd = m_compiler->eeFindHelper(CORINFO_HELP_UNBOX_TYPETEST); + lcl->ChangeOper(GT_LCL_VAR); + GenTree* const mt = m_compiler->gtNewMethodTableLookup(lcl); + + if (user == nullptr) + { + // call was just for effect, we're done. + } + else + { + GenTree* const boxAddr = m_compiler->gtNewLclVarAddrNode(lclNum); + GenTree* const payloadAddr = + m_compiler->gtNewOperNode(GT_ADD, TYP_BYREF, boxAddr, + m_compiler->gtNewIconNode(TARGET_POINTER_SIZE, + TYP_I_IMPL)); + GenTree* const comma = + m_compiler->gtNewOperNode(GT_COMMA, TYP_BYREF, call, payloadAddr); + *use = comma; + } + } } } } -#endif return Compiler::fgWalkResult::WALK_CONTINUE; } diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index dd8c8eeedaae5..255a43dc80b38 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1645,8 +1645,8 @@ void HelperCallProperties::init() case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE: // isNoEscape = true; - isPure = true; - noThrow = true; // These return null for a failing cast + isPure = true; + noThrow = true; // These return null for a failing cast break; case CORINFO_HELP_GETCURRENTMANAGEDTHREADID: @@ -1675,7 +1675,7 @@ void HelperCallProperties::init() break; case CORINFO_HELP_LDELEMA_REF: - isPure = true; + isPure = true; break; // GETREFANY is pure up to the value of the struct argument. We From 3522738634479a6409922eb556b1fffab80494aa Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 19 Jun 2024 16:18:38 -0700 Subject: [PATCH 27/45] undo hacky changes to promotion --- src/coreclr/jit/layout.cpp | 4 +++- src/coreclr/jit/promotion.cpp | 37 ++++++++++------------------------- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/src/coreclr/jit/layout.cpp b/src/coreclr/jit/layout.cpp index 6a30b66acbc4f..58024233eb6e7 100644 --- a/src/coreclr/jit/layout.cpp +++ b/src/coreclr/jit/layout.cpp @@ -413,7 +413,9 @@ void ClassLayout::InitializeGCPtrs(Compiler* compiler) assert((gcPtrCount == 0) || ((compiler->info.compCompHnd->getClassAttribs(m_classHandle) & (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_BYREF_LIKE)) != 0)); - assert(gcPtrCount < (1 << 24)); + // Since class size is unsigned there's no way we could have more than 2^30 slots + // so it should be safe to fit this into a 30 bits bit field. + assert(gcPtrCount < (1 << 30)); m_gcPtrCount = gcPtrCount; } diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index a51b91505b3ae..68c00321c0b59 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -1020,24 +1020,14 @@ class LocalsUseVisitor : public GenTreeVisitor ClassLayout* accessLayout; AccessKindFlags accessFlags; - if (lcl->OperIs(GT_LCL_ADDR) && user->OperIs(GT_CALL)) + if (lcl->OperIs(GT_LCL_ADDR)) { - if (dsc->IsStackAllocatedBox()) - { - // TODO: assert this is a call to a "no-escape helper" - accessType = TYP_STRUCT; - accessLayout = dsc->GetLayout(); - accessFlags = AccessKindFlags::IsCallArg; - } - else - { - assert(dsc->IsHiddenBufferStructArg() && - (user->AsCall()->gtArgs.GetRetBufferArg()->GetNode() == lcl)); + assert(user->OperIs(GT_CALL) && dsc->IsHiddenBufferStructArg() && + (user->AsCall()->gtArgs.GetRetBufferArg()->GetNode() == lcl)); - accessType = TYP_STRUCT; - accessLayout = m_compiler->typGetObjLayout(user->AsCall()->gtRetClsHnd); - accessFlags = AccessKindFlags::IsCallRetBuf; - } + accessType = TYP_STRUCT; + accessLayout = m_compiler->typGetObjLayout(user->AsCall()->gtRetClsHnd); + accessFlags = AccessKindFlags::IsCallRetBuf; } else { @@ -1420,7 +1410,7 @@ class LocalsUseVisitor : public GenTreeVisitor // AccessKindFlags ClassifyLocalAccess(GenTreeLclVarCommon* lcl, GenTree* user) { - // assert(lcl->OperIsLocalRead() || lcl->OperIsLocalStore()); + assert(lcl->OperIsLocalRead() || lcl->OperIsLocalStore()); AccessKindFlags flags = AccessKindFlags::None; if (lcl->OperIsLocalStore()) @@ -2304,21 +2294,14 @@ void ReplaceVisitor::InsertPreStatementWriteBacks() for (CallArg& arg : call->gtArgs.Args()) { GenTree* node = arg.GetNode()->gtEffectiveVal(); - - bool writeBackHelperArg = - node->OperIs(GT_LCL_ADDR) && (arg.GetWellKnownArg() != WellKnownArg::RetBuffer); - bool writeBackStructArg = node->TypeIs(TYP_STRUCT) && node->OperIsLocalRead(); - - if (!writeBackHelperArg && !writeBackStructArg) + if (!node->TypeIs(TYP_STRUCT) || !node->OperIsLocalRead()) { continue; } GenTreeLclVarCommon* lcl = node->AsLclVarCommon(); - LclVarDsc* dsc = m_compiler->lvaGetDesc(lcl); - unsigned size = writeBackHelperArg ? dsc->lvExactSize() : lcl->GetLayout(m_compiler)->GetSize(); - - m_replacer->WriteBackBeforeCurrentStatement(lcl->GetLclNum(), lcl->GetLclOffs(), size); + m_replacer->WriteBackBeforeCurrentStatement(lcl->GetLclNum(), lcl->GetLclOffs(), + lcl->GetLayout(m_compiler)->GetSize()); } } From d88b4a908507e88dfff5f769fa30212189e66987 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 19 Jun 2024 18:29:19 -0700 Subject: [PATCH 28/45] more extensive rewriting -- anything that might be a stack allocated box --- src/coreclr/jit/objectalloc.cpp | 74 ++++++++++++++++----------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 7e952b3501e1d..a7f9a2a6ff981 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -920,53 +920,51 @@ void ObjectAllocator::RewriteUses() *use = boxLcl; } } - // Make box accesses explicit for UNBOX_HELPER // - if (tree->IsCall()) + else if (tree->IsCall()) { GenTreeCall* const call = tree->AsCall(); - assert(call->IsHelperCall()); - if (call->IsHelperCall(m_compiler, CORINFO_HELP_BOX)) + + if (call->IsHelperCall(m_compiler, CORINFO_HELP_UNBOX)) { - // See if first arg is our stack allocated box. + JITDUMP("Found unbox helper call [%06u]\n", m_compiler->dspTreeID(call)); + + // See if first arg is possibly a stack allocated box. + // (arg will have been retyped local or local address) // - GenTree* const firstArg = call->gtArgs.GetArgByIndex(0)->GetNode(); + CallArg* secondArg = call->gtArgs.GetArgByIndex(1); + GenTree* const secondArgNode = secondArg->GetNode(); - if (firstArg->OperIs(GT_LCL_ADDR)) + if (secondArgNode->OperIsLocal() && !secondArgNode->TypeIs(TYP_REF)) { - GenTreeLclVarCommon* const lcl = firstArg->AsLclVarCommon(); - unsigned const lclNum = lcl->GetLclNum(); - LclVarDsc* dsc = m_compiler->lvaGetDesc(lclNum); - - // assert this? - if (dsc->IsStackAllocatedBox()) + GenTreeLclVarCommon* const lcl = secondArgNode->AsLclVarCommon(); + GenTree* const lclCopy = (user == nullptr) ? nullptr : m_compiler->gtClone(lcl); + + // Rewrite the call to make the box accesses explicit in jitted code. + // user = COMMA( + // CALL(UNBOX_HELPER_TYPETEST, obj->MethodTable, type), + // ADD(&obj, TARGET_POINTER_SIZE)) + // + JITDUMP("Rewriting to invoke box type test helper\n"); + + call->gtCallMethHnd = m_compiler->eeFindHelper(CORINFO_HELP_UNBOX_TYPETEST); + lcl->ChangeOper(GT_LCL_VAR); + GenTree* const mt = m_compiler->gtNewMethodTableLookup(lcl); + call->gtArgs.Remove(secondArg); + call->gtArgs.PushBack(m_compiler, NewCallArg::Primitive(mt)); + + if (user == nullptr) { - // Yes -- rewrite the call to make the box accesses explicit in jitted code. - // user = COMMA(CALL(UNBOX_HELPER_TYPETEST, obj->MethodTable, type), &obj + - // TARGET_POINTER_SIZE) - // - JITDUMP("Rewriting [%06u] to invoke box type test helper\n", m_compiler->dspTreeID(tree)); - - call->gtCallMethHnd = m_compiler->eeFindHelper(CORINFO_HELP_UNBOX_TYPETEST); - lcl->ChangeOper(GT_LCL_VAR); - GenTree* const mt = m_compiler->gtNewMethodTableLookup(lcl); - - if (user == nullptr) - { - // call was just for effect, we're done. - } - else - { - GenTree* const boxAddr = m_compiler->gtNewLclVarAddrNode(lclNum); - GenTree* const payloadAddr = - m_compiler->gtNewOperNode(GT_ADD, TYP_BYREF, boxAddr, - m_compiler->gtNewIconNode(TARGET_POINTER_SIZE, - TYP_I_IMPL)); - GenTree* const comma = - m_compiler->gtNewOperNode(GT_COMMA, TYP_BYREF, call, payloadAddr); - *use = comma; - } + // call was just for effect, we're done. + } + else + { + GenTree* const payloadAddr = + m_compiler->gtNewOperNode(GT_ADD, TYP_BYREF, lclCopy, + m_compiler->gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL)); + GenTree* const comma = m_compiler->gtNewOperNode(GT_COMMA, TYP_BYREF, call, payloadAddr); + *use = comma; } } } From 9c7885de7ad9957c07d672df08f5a8b7841bb41a Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 19 Jun 2024 19:03:15 -0700 Subject: [PATCH 29/45] allow method table access for stack allocated objects --- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/compiler.hpp | 4 ++-- src/coreclr/jit/objectalloc.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index f0fad517abed1..a5071d22fa818 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3528,7 +3528,7 @@ class Compiler GenTree* gtNewRuntimeLookup(CORINFO_GENERIC_HANDLE hnd, CorInfoGenericHandleType hndTyp, GenTree* lookupTree); - GenTreeIndir* gtNewMethodTableLookup(GenTree* obj); + GenTreeIndir* gtNewMethodTableLookup(GenTree* obj, bool onStack = false); //------------------------------------------------------------------------ // Other GenTree functions diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 21bd4fc5a1efd..69e95d60d58ae 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -1872,9 +1872,9 @@ inline GenTreeCast* Compiler::gtNewCastNodeL(var_types typ, GenTree* op1, bool f return cast; } -inline GenTreeIndir* Compiler::gtNewMethodTableLookup(GenTree* object) +inline GenTreeIndir* Compiler::gtNewMethodTableLookup(GenTree* object, bool onStack) { - assert(object->TypeIs(TYP_REF)); + assert(onStack || object->TypeIs(TYP_REF)); GenTreeIndir* result = gtNewIndir(TYP_I_IMPL, object, GTF_IND_INVARIANT); return result; } diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index a7f9a2a6ff981..b31aed8f8faa4 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -950,7 +950,7 @@ void ObjectAllocator::RewriteUses() call->gtCallMethHnd = m_compiler->eeFindHelper(CORINFO_HELP_UNBOX_TYPETEST); lcl->ChangeOper(GT_LCL_VAR); - GenTree* const mt = m_compiler->gtNewMethodTableLookup(lcl); + GenTree* const mt = m_compiler->gtNewMethodTableLookup(lcl, /* onStack */ true); call->gtArgs.Remove(secondArg); call->gtArgs.PushBack(m_compiler, NewCallArg::Primitive(mt)); From 06cbf8b695332f71dfb3c442ca44fd1790838b67 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 20 Jun 2024 14:40:26 -0700 Subject: [PATCH 30/45] do indir/comma swapping in object allocator --- src/coreclr/jit/objectalloc.cpp | 20 ++++++++++++++++++++ src/coreclr/jit/valuenum.cpp | 4 ++++ src/coreclr/jit/valuenumfuncs.h | 1 + 3 files changed, 25 insertions(+) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index b31aed8f8faa4..4e679186e0b9c 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -965,10 +965,30 @@ void ObjectAllocator::RewriteUses() m_compiler->gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL)); GenTree* const comma = m_compiler->gtNewOperNode(GT_COMMA, TYP_BYREF, call, payloadAddr); *use = comma; + + // flag this comma so we can find it later + comma->gtFlags |= GTF_MAKE_CSE; } } } } + else if (tree->OperIsIndir()) + { + GenTreeIndir* const indir = tree->AsIndir(); + GenTree* const addr = indir->Addr(); + + if (addr->OperIs(GT_COMMA) && ((addr->gtFlags & GTF_MAKE_CSE) != 0)) + { + GenTree* const actualAddr = addr->gtEffectiveVal(); + GenTree* sideEffects = nullptr; + m_compiler->gtExtractSideEffList(indir, &sideEffects, GTF_SIDE_EFFECT, /* ignore root */ true); + + indir->Addr() = actualAddr; + indir->gtFlags &= ~GTF_SIDE_EFFECT; + GenTree* const newComma = m_compiler->gtNewOperNode(GT_COMMA, indir->TypeGet(), sideEffects, indir); + *use = newComma; + } + } return Compiler::fgWalkResult::WALK_CONTINUE; } diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 17b45702776c8..0273f1bf43c34 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -13333,6 +13333,10 @@ VNFunc Compiler::fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc) vnf = VNF_Unbox; break; + case CORINFO_HELP_UNBOX_TYPETEST: + vnf = VNF_Unbox_TypeTest; + break; + // A constant within any method. case CORINFO_HELP_GETCURRENTMANAGEDTHREADID: vnf = VNF_ManagedThreadId; diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h index 869cce00c9c3c..70c950fccfc7e 100644 --- a/src/coreclr/jit/valuenumfuncs.h +++ b/src/coreclr/jit/valuenumfuncs.h @@ -161,6 +161,7 @@ ValueNumFuncDef(LazyStrCns, 2, false, true, false, false) // Lazy-ini ValueNumFuncDef(InvariantLoad, 1, false, false, false, false) // Args: 0: (VN of) the address. ValueNumFuncDef(InvariantNonNullLoad, 1, false, true, false, false) // Args: 0: (VN of) the address. ValueNumFuncDef(Unbox, 2, false, false, false, false) +ValueNumFuncDef(Unbox_TypeTest, 2, false, false, false, false) ValueNumFuncDef(LT_UN, 2, false, false, false, false) // unsigned or unordered comparisons ValueNumFuncDef(LE_UN, 2, false, false, false, false) From 946c780c0082dabb9f6ff4be93137c276e121132 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 20 Jun 2024 19:08:25 -0700 Subject: [PATCH 31/45] fall back to relying on (lack of) exposure --- src/coreclr/jit/promotion.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index 68c00321c0b59..b656f1576d0d9 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -2937,8 +2937,7 @@ bool Promotion::HaveCandidateLocals() // bool Promotion::IsCandidateForPhysicalPromotion(LclVarDsc* dsc) { - return (dsc->TypeGet() == TYP_STRUCT) && !dsc->lvPromoted && - (!dsc->IsAddressExposed() || dsc->IsStackAllocatedBox()); + return (dsc->TypeGet() == TYP_STRUCT) && !dsc->lvPromoted && !dsc->IsAddressExposed(); } //------------------------------------------------------------------------ From e63a883ef83aa4c0b212c64efe1f421ebd361ea9 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 21 Jun 2024 14:03:25 -0700 Subject: [PATCH 32/45] fix type test helper, add range control --- src/coreclr/jit/jitconfigvalues.h | 1 + src/coreclr/jit/objectalloc.cpp | 22 ++++++++++++++++------ src/coreclr/vm/jithelpers.cpp | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 8463044aeb9cf..b0f0e90621c8d 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -654,6 +654,7 @@ RELEASE_CONFIG_INTEGER(JitExtDefaultPolicyProfScale, W("JitExtDefaultPolicyProfS RELEASE_CONFIG_INTEGER(JitInlinePolicyModel, W("JitInlinePolicyModel"), 0) RELEASE_CONFIG_INTEGER(JitInlinePolicyProfile, W("JitInlinePolicyProfile"), 0) RELEASE_CONFIG_INTEGER(JitInlinePolicyProfileThreshold, W("JitInlinePolicyProfileThreshold"), 40) +CONFIG_STRING(JitObjectStackAllocationRange, W("JitObjectStackAllocationRange")) RELEASE_CONFIG_INTEGER(JitObjectStackAllocation, W("JitObjectStackAllocation"), 1) RELEASE_CONFIG_INTEGER(JitObjectStackAllocationRefClass, W("JitObjectStackAllocationRefClass"), 1) RELEASE_CONFIG_INTEGER(JitObjectStackAllocationBoxedValueClass, W("JitObjectStackAllocationBoxedValueClass"), 1) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 4e679186e0b9c..4f8f350b5f4bd 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -36,14 +36,24 @@ PhaseStatus ObjectAllocator::DoPhase() return PhaseStatus::MODIFIED_NOTHING; } - if (IsObjectStackAllocationEnabled()) + bool enabled = IsObjectStackAllocationEnabled(); + +#ifdef DEBUG + static ConfigMethodRange JitObjectStackAllocationRange; + JitObjectStackAllocationRange.EnsureInit(JitConfig.JitObjectStackAllocationRange()); + const unsigned hash = comp->info.compMethodHash(); + enabled &= JitObjectStackAllocationRange.Contains(hash); +#endif + + if (enabled) { JITDUMP("enabled, analyzing...\n"); DoAnalysis(); } else { - JITDUMP("disabled, punting\n"); + JITDUMP("disabled%s, punting\n", IsObjectStackAllocationEnabled() ? " by range config" : ""); + m_IsObjectStackAllocationEnabled = false; } const bool didStackAllocate = MorphAllocObjNodes(); @@ -431,10 +441,10 @@ bool ObjectAllocator::MorphAllocObjNodes() { JITDUMP("Allocating V%02u on the stack\n", lclNum); canStack = true; - // #ifdef DEBUG - // printf("@@@ SA V%02u (%s) in %s\n", lclNum, comp->eeGetClassName(clsHnd), - // comp->info.compFullName); - // #endif +#ifdef DEBUG + // printf("@@@ SA V%02u (%s) in %s (0x%08x)\n", lclNum, comp->eeGetClassName(clsHnd), + // comp->info.compFullName, comp->info.compMethodHash()); +#endif const unsigned int stackLclNum = MorphAllocObjNodeIntoStackAlloc(asAllocObj, stackClsHnd, isValueClass, block, stmt); diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 4f0a6ede8e94d..e1e490c44d2bd 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -2360,7 +2360,7 @@ HCIMPL2(void, JIT_Unbox_TypeTest, CORINFO_CLASS_HANDLE type, CORINFO_CLASS_HANDL // Fall back to a framed helper that can also handle GC suspension and type equivalence. ENDFORBIDGC(); - HCCALL2(JIT_Unbox_TypeTest_Framed, pMT1, pMT1); + HCCALL2(JIT_Unbox_TypeTest_Framed, pMT1, pMT2); } HCIMPLEND From 41ea7bde0af18ef43a919899be1f1ff9025d5913 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 21 Jun 2024 17:53:46 -0700 Subject: [PATCH 33/45] fix unbox helper re-write --- src/coreclr/jit/objectalloc.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 4f8f350b5f4bd..0e3c8ff757aa8 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -940,36 +940,37 @@ void ObjectAllocator::RewriteUses() { JITDUMP("Found unbox helper call [%06u]\n", m_compiler->dspTreeID(call)); - // See if first arg is possibly a stack allocated box. + // See if first arg is possibly a stack allocated box or ref class // (arg will have been retyped local or local address) // CallArg* secondArg = call->gtArgs.GetArgByIndex(1); GenTree* const secondArgNode = secondArg->GetNode(); - if (secondArgNode->OperIsLocal() && !secondArgNode->TypeIs(TYP_REF)) + if ((secondArgNode->OperIsLocal() || secondArgNode->OperIs(GT_LCL_ADDR)) && + !secondArgNode->TypeIs(TYP_REF)) { - GenTreeLclVarCommon* const lcl = secondArgNode->AsLclVarCommon(); - GenTree* const lclCopy = (user == nullptr) ? nullptr : m_compiler->gtClone(lcl); + const bool isForEffect = (user == nullptr) || call->TypeIs(TYP_VOID); + GenTreeLclVarCommon* const lcl = secondArgNode->AsLclVarCommon(); // Rewrite the call to make the box accesses explicit in jitted code. // user = COMMA( // CALL(UNBOX_HELPER_TYPETEST, obj->MethodTable, type), - // ADD(&obj, TARGET_POINTER_SIZE)) + // ADD(obj, TARGET_POINTER_SIZE)) // - JITDUMP("Rewriting to invoke box type test helper\n"); + JITDUMP("Rewriting to invoke box type test helper%s\n", isForEffect ? " for side effect" : ""); call->gtCallMethHnd = m_compiler->eeFindHelper(CORINFO_HELP_UNBOX_TYPETEST); - lcl->ChangeOper(GT_LCL_VAR); - GenTree* const mt = m_compiler->gtNewMethodTableLookup(lcl, /* onStack */ true); + GenTree* const mt = m_compiler->gtNewMethodTableLookup(lcl, /* onStack */ true); call->gtArgs.Remove(secondArg); call->gtArgs.PushBack(m_compiler, NewCallArg::Primitive(mt)); - if (user == nullptr) + if (isForEffect) { // call was just for effect, we're done. } else { + GenTree* const lclCopy = m_compiler->gtClone(lcl); GenTree* const payloadAddr = m_compiler->gtNewOperNode(GT_ADD, TYP_BYREF, lclCopy, m_compiler->gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL)); From 337fc7ea7da9a455732b7ca44e1579ac6513a0b4 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 21 Jun 2024 18:23:07 -0700 Subject: [PATCH 34/45] need more capable clone --- src/coreclr/jit/objectalloc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 0e3c8ff757aa8..819a07ce64de9 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -970,7 +970,7 @@ void ObjectAllocator::RewriteUses() } else { - GenTree* const lclCopy = m_compiler->gtClone(lcl); + GenTree* const lclCopy = m_compiler->gtClone(lcl, /* complexOk */ true); GenTree* const payloadAddr = m_compiler->gtNewOperNode(GT_ADD, TYP_BYREF, lclCopy, m_compiler->gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL)); From 6cdefdf9817adc249f61191429a15d4bb176e0ba Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Sun, 23 Jun 2024 08:45:15 -0700 Subject: [PATCH 35/45] report all object refs as byrefs for OSR (no retyping to nint) --- src/coreclr/jit/objectalloc.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 819a07ce64de9..8e4a23acd423c 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -36,13 +36,20 @@ PhaseStatus ObjectAllocator::DoPhase() return PhaseStatus::MODIFIED_NOTHING; } - bool enabled = IsObjectStackAllocationEnabled(); + bool enabled = IsObjectStackAllocationEnabled(); + const char* disableReason = ": global config"; #ifdef DEBUG - static ConfigMethodRange JitObjectStackAllocationRange; - JitObjectStackAllocationRange.EnsureInit(JitConfig.JitObjectStackAllocationRange()); - const unsigned hash = comp->info.compMethodHash(); - enabled &= JitObjectStackAllocationRange.Contains(hash); + // Allow disabling based on method hash + // + if (enabled) + { + static ConfigMethodRange JitObjectStackAllocationRange; + JitObjectStackAllocationRange.EnsureInit(JitConfig.JitObjectStackAllocationRange()); + const unsigned hash = comp->info.compMethodHash(); + enabled &= JitObjectStackAllocationRange.Contains(hash); + disableReason = ": range config"; + } #endif if (enabled) @@ -52,7 +59,7 @@ PhaseStatus ObjectAllocator::DoPhase() } else { - JITDUMP("disabled%s, punting\n", IsObjectStackAllocationEnabled() ? " by range config" : ""); + JITDUMP("disabled%s, punting\n", IsObjectStackAllocationEnabled() ? disableReason : ""); m_IsObjectStackAllocationEnabled = false; } @@ -315,7 +322,9 @@ void ObjectAllocator::ComputeStackObjectPointers(BitVecTraits* bitVecTraits) MarkLclVarAsPossiblyStackPointing(lclNum); // Check if this pointer always points to the stack. - if (lclVarDsc->lvSingleDef == 1) + // For OSR the reference may be pointing at the heap-allocated Tier0 version. + // + if ((lclVarDsc->lvSingleDef == 1) && !comp->opts.IsOSR()) { // Check if we know what is assigned to this pointer. unsigned bitCount = BitVecOps::Count(bitVecTraits, m_ConnGraphAdjacencyMatrix[lclNum]); From 2c3785e18212f0de12d0e3910b15244dfa118b45 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Sun, 23 Jun 2024 08:53:44 -0700 Subject: [PATCH 36/45] disable ref class stack allocation for r2r/naot for now --- src/coreclr/jit/objectalloc.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/coreclr/jit/objectalloc.h b/src/coreclr/jit/objectalloc.h index 8a873be8b34ad..283216d79482b 100644 --- a/src/coreclr/jit/objectalloc.h +++ b/src/coreclr/jit/objectalloc.h @@ -164,6 +164,15 @@ inline bool ObjectAllocator::CanAllocateLclVarOnStack(unsigned int lclNu return false; } +#ifdef FEATURE_READYTORUN + if (comp->opts.IsReadyToRun()) + { + // Need to fix getClassGClayout and maybe more + *reason = "[R2R/NAOT support NYI]"; + return false; + } +#endif + classSize = comp->info.compCompHnd->getHeapClassSize(clsHnd); } From 7ca7027aada234122eaff13e7469f7389bc409b6 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 24 Jun 2024 11:13:56 -0700 Subject: [PATCH 37/45] minor TP improvement --- src/coreclr/jit/objectalloc.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 8e4a23acd423c..d7a811acf2917 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -187,9 +187,17 @@ void ObjectAllocator::MarkEscapingVarsAndBuildConnGraph() Compiler::fgWalkResult PreOrderVisit(GenTree** use, GenTree* user) { - GenTree* tree = *use; - unsigned lclNum = tree->AsLclVarCommon()->GetLclNum(); - bool lclEscapes = true; + GenTree* const tree = *use; + unsigned const lclNum = tree->AsLclVarCommon()->GetLclNum(); + + // If this local already escapes, no need to look further. + // + if (m_allocator->CanLclVarEscape(lclNum)) + { + return Compiler::fgWalkResult::WALK_CONTINUE; + } + + bool lclEscapes = true; if (tree->OperIsLocalStore()) { From 351d484a893b6733f32f8098183c9bbc3958f8d7 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 24 Jun 2024 11:45:50 -0700 Subject: [PATCH 38/45] dial back inliner --- src/coreclr/jit/inlinepolicy.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/coreclr/jit/inlinepolicy.cpp b/src/coreclr/jit/inlinepolicy.cpp index 74581756c1490..0c6ac9241fca6 100644 --- a/src/coreclr/jit/inlinepolicy.cpp +++ b/src/coreclr/jit/inlinepolicy.cpp @@ -1628,16 +1628,7 @@ double ExtendedDefaultPolicy::DetermineMultiplier() // // void Caller() => DoNothing(42); // 42 is going to be boxed at the call site. // - if (JitConfig.JitObjectStackAllocation() > 0) - { - // We might also be able to stack allocate the box - // - multiplier += 3.0 + m_ArgIsBoxedAtCallsite; - } - else - { - multiplier += 0.5; - } + multiplier += 0.5 * m_ArgIsBoxedAtCallsite; JITDUMP("\nCallsite is going to box %d arguments. Multiplier increased to %g.", m_ArgIsBoxedAtCallsite, multiplier); } From ccad461e1c696b29f09e2ec5393dd9895d4848d4 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 26 Jun 2024 13:53:48 -0700 Subject: [PATCH 39/45] fix spmi capture of ref class gclayout --- .../superpmi/superpmi-shim-collector/icorjitinfo.cpp | 12 ++++++++++-- src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index fe4235e0b2d7e..611edfaf034e5 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -585,7 +585,7 @@ unsigned interceptor_ICJI::getClassAlignmentRequirement(CORINFO_CLASS_HANDLE cls return temp; } -// This is only called for Value classes. It returns a boolean array +// This is called for ref and value classes. It returns a boolean array // in representing of 'cls' from a GC perspective. The class is // assumed to be an array of machine words // (of length // getClassSize(cls) / sizeof(void*)), @@ -600,7 +600,15 @@ unsigned interceptor_ICJI::getClassGClayout(CORINFO_CLASS_HANDLE cls, /* IN */ { mc->cr->AddCall("getClassGClayout"); unsigned temp = original_ICorJitInfo->getClassGClayout(cls, gcPtrs); - unsigned len = (getClassSize(cls) + sizeof(void*) - 1) / sizeof(void*); + unsigned len = 0; + if (isValueClass(cls)) + { + len = (getClassSize(cls) + sizeof(void*) - 1) / sizeof(void*); + } + else + { + len = (getHeapClassSize(cls) + sizeof(void*) - 1) / sizeof(void*); + } mc->recGetClassGClayout(cls, gcPtrs, len, temp); return temp; } diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index ca9c4cb3dbb2a..e42b41ee8365d 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -509,7 +509,7 @@ unsigned MyICJI::getClassAlignmentRequirement(CORINFO_CLASS_HANDLE cls, bool fDo return jitInstance->mc->repGetClassAlignmentRequirement(cls, fDoubleAlignHint); } -// This is only called for Value classes. It returns a boolean array +// This called for ref and value classes. It returns a boolean array // in representing of 'cls' from a GC perspective. The class is // assumed to be an array of machine words // (of length // getClassSize(cls) / sizeof(void*)), From 14d2455389c098b01181061295fe0a685f35b188 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 26 Jun 2024 14:54:17 -0700 Subject: [PATCH 40/45] Use StackAllocatedBox --- .../System.Private.CoreLib.csproj | 1 + .../CompilerServices/StackAllocatedBox.cs | 17 +++++++++++++++++ src/coreclr/vm/corelib.h | 2 ++ src/coreclr/vm/jitinterface.cpp | 10 ++++------ 4 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StackAllocatedBox.cs diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 1586ba4132120..f1282ff6768c4 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -197,6 +197,7 @@ + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StackAllocatedBox.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StackAllocatedBox.cs new file mode 100644 index 0000000000000..ef5301da924fe --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StackAllocatedBox.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices; + +#pragma warning disable CA1823 +#pragma warning disable CS0169 + +internal unsafe struct StackAllocatedBox +{ + // These fields are only accessed from jitted code + private MethodTable* _pMethodTable; + private T _value; +} + +#pragma warning restore CS0169 +#pragma warning restore CA1823 diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 2e1a7e970563f..9fcfbd8cf216a 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1115,6 +1115,8 @@ DEFINE_METHOD(ICASTABLEHELPERS, GETIMPLTYPE, GetImplType, SM_ICast #endif // FEATURE_ICASTABLE +DEFINE_CLASS(STACKALLOCATEDBOX, CompilerServices, StackAllocatedBox`1) + DEFINE_CLASS(UTF8STRINGMARSHALLER, Marshalling, Utf8StringMarshaller) DEFINE_METHOD(UTF8STRINGMARSHALLER, CONVERT_TO_MANAGED, ConvertToManaged, SM_PtrByte_RetStr) DEFINE_METHOD(UTF8STRINGMARSHALLER, CONVERT_TO_UNMANAGED, ConvertToUnmanaged, SM_Str_RetPtrByte) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 3c726f9ccafc0..9dfb3daffd4d1 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -6270,12 +6270,10 @@ CORINFO_CLASS_HANDLE CEEInfo::getTypeForBoxOnStack(CORINFO_CLASS_HANDLE cls) else #endif { - TypeHandle mt(CoreLibBinder::GetElementType(ELEMENT_TYPE_I)); - TypeHandle boxedFields[2] = {mt, VMClsHnd}; - Instantiation boxedFieldsInst((TypeHandle*)&boxedFields, 2); - TypeHandle genericKvp = CoreLibBinder::GetClass(CLASS__KEYVALUEPAIRGENERIC); - TypeHandle boxedKvp = genericKvp.Instantiate(boxedFieldsInst); - result = static_cast(boxedKvp.AsPtr()); + Instantiation boxedFieldsInst(&VMClsHnd, 1); + TypeHandle stackAllocatedBox = CoreLibBinder::GetClass(CLASS__STACKALLOCATEDBOX); + TypeHandle stackAllocatedBoxInst = stackAllocatedBox.Instantiate(boxedFieldsInst); + result = static_cast(stackAllocatedBoxInst.AsPtr()); } EE_TO_JIT_TRANSITION(); From 8a077ea5efafe8750d649265cc9c0bbc712cc191 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 27 Jun 2024 13:13:05 -0700 Subject: [PATCH 41/45] Apply suggestions from code review Co-authored-by: Jan Kotas Co-authored-by: Jakob Botsch Nielsen --- .../CompilerServices/StackAllocatedBox.cs | 20 +++++++++---------- src/coreclr/jit/objectalloc.cpp | 4 ++-- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StackAllocatedBox.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StackAllocatedBox.cs index ef5301da924fe..885a2d93c6242 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StackAllocatedBox.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StackAllocatedBox.cs @@ -1,17 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace System.Runtime.CompilerServices; +using System.Runtime.InteropServices; -#pragma warning disable CA1823 -#pragma warning disable CS0169 - -internal unsafe struct StackAllocatedBox +namespace System.Runtime.CompilerServices { - // These fields are only accessed from jitted code - private MethodTable* _pMethodTable; - private T _value; + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct StackAllocatedBox + { + // These fields are only accessed from jitted code + private MethodTable* _pMethodTable; + private T _value; + } } - -#pragma warning restore CS0169 -#pragma warning restore CA1823 diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index d7a811acf2917..a568bb10a58f4 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -957,7 +957,7 @@ void ObjectAllocator::RewriteUses() { JITDUMP("Found unbox helper call [%06u]\n", m_compiler->dspTreeID(call)); - // See if first arg is possibly a stack allocated box or ref class + // See if second arg is possibly a stack allocated box or ref class // (arg will have been retyped local or local address) // CallArg* secondArg = call->gtArgs.GetArgByIndex(1); @@ -987,7 +987,7 @@ void ObjectAllocator::RewriteUses() } else { - GenTree* const lclCopy = m_compiler->gtClone(lcl, /* complexOk */ true); + GenTree* const lclCopy = m_compiler->gtCloneExpr(lcl); GenTree* const payloadAddr = m_compiler->gtNewOperNode(GT_ADD, TYP_BYREF, lclCopy, m_compiler->gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL)); From dc873e50d725c373e83dcd98f1fc88308d6c94b6 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 27 Jun 2024 18:09:18 -0700 Subject: [PATCH 42/45] more review feedback --- src/coreclr/jit/layout.cpp | 3 - src/coreclr/jit/objectalloc.cpp | 98 +++++++++++++++++++++------------ src/coreclr/jit/utils.cpp | 1 - 3 files changed, 62 insertions(+), 40 deletions(-) diff --git a/src/coreclr/jit/layout.cpp b/src/coreclr/jit/layout.cpp index 58024233eb6e7..8de93f838971f 100644 --- a/src/coreclr/jit/layout.cpp +++ b/src/coreclr/jit/layout.cpp @@ -405,9 +405,6 @@ void ClassLayout::InitializeGCPtrs(Compiler* compiler) gcPtrs = m_gcPtrsArray; } - // The gcPtrs array will have the right size for boxed value classes, but all - // entries will be shifted down one slot to match the unboxed rep. - // unsigned gcPtrCount = compiler->info.compCompHnd->getClassGClayout(m_classHandle, gcPtrs); assert((gcPtrCount == 0) || ((compiler->info.compCompHnd->getClassAttribs(m_classHandle) & diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index a568bb10a58f4..4e5476003dba6 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -458,11 +458,6 @@ bool ObjectAllocator::MorphAllocObjNodes() { JITDUMP("Allocating V%02u on the stack\n", lclNum); canStack = true; -#ifdef DEBUG - // printf("@@@ SA V%02u (%s) in %s (0x%08x)\n", lclNum, comp->eeGetClassName(clsHnd), - // comp->info.compFullName, comp->info.compMethodHash()); -#endif - const unsigned int stackLclNum = MorphAllocObjNodeIntoStackAlloc(asAllocObj, stackClsHnd, isValueClass, block, stmt); m_HeapLocalToStackLocalMap.AddOrUpdate(lclNum, stackLclNum); @@ -728,11 +723,6 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack* parent if (asCall->IsHelperCall()) { - // TODO-ObjectStackAllocation: Special-case helpers here that - // 1. Don't make objects escape. - // 2. Protect objects as interior (GCPROTECT_BEGININTERIOR() instead of GCPROTECT_BEGIN()). - // 3. Don't check that the object is in the heap in ValidateInner. - canLclVarEscapeViaParentStack = !Compiler::s_helperCallProperties.IsNoEscape(comp->eeGetHelperNum(asCall->gtCallMethHnd)); } @@ -797,30 +787,57 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p break; } FALLTHROUGH; - case GT_COLON: case GT_QMARK: case GT_ADD: case GT_FIELD_ADDR: if (parent->TypeGet() == TYP_REF) { parent->ChangeType(newType); + } + ++parentIndex; + keepChecking = true; + break; - if (parent->OperIs(GT_COLON)) - { - GenTree* const lhs = parent->AsOp()->gtGetOp1(); - GenTree* const rhs = parent->AsOp()->gtGetOp2(); + case GT_COLON: + { + GenTree* const lhs = tree->AsOp()->gtGetOp1(); + GenTree* const rhs = tree->AsOp()->gtGetOp2(); - if (lhs->TypeIs(TYP_REF)) - { - lhs->ChangeType(newType); - } + // One or both children may have been retyped. + // Ensure we don't lose the fact that the joint result + // may be a GC type. + // + var_types lhsType = lhs->TypeGet(); + var_types rhsType = rhs->TypeGet(); - if (rhs->TypeIs(TYP_REF)) - { - rhs->ChangeType(newType); - } - } + if (lhsType == TYP_REF) + { + assert(rhsType != TYP_REF); + assert(rhsType == tree->TypeGet()); + newType = TYP_BYREF; } + + if (rhsType == TYP_REF) + { + assert(lhsType != TYP_REF); + assert(lhsType == tree->TypeGet()); + newType = TYP_BYREF; + } + + tree->ChangeType(newType); + + if (lhsType != newType) + { + lhs->ChangeType(newType); + } + + if (rhsType != newType) + { + rhs->ChangeType(newType); + } + + parent->ChangeType(newType); + } ++parentIndex; keepChecking = true; break; @@ -993,28 +1010,37 @@ void ObjectAllocator::RewriteUses() m_compiler->gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL)); GenTree* const comma = m_compiler->gtNewOperNode(GT_COMMA, TYP_BYREF, call, payloadAddr); *use = comma; - - // flag this comma so we can find it later - comma->gtFlags |= GTF_MAKE_CSE; } } } } else if (tree->OperIsIndir()) { + // Look for cases where the addr is a comma created above, and + // sink the indir into the comma so later phases can see the access more cleanly. + // GenTreeIndir* const indir = tree->AsIndir(); GenTree* const addr = indir->Addr(); - if (addr->OperIs(GT_COMMA) && ((addr->gtFlags & GTF_MAKE_CSE) != 0)) + if (addr->OperIs(GT_COMMA)) { - GenTree* const actualAddr = addr->gtEffectiveVal(); - GenTree* sideEffects = nullptr; - m_compiler->gtExtractSideEffList(indir, &sideEffects, GTF_SIDE_EFFECT, /* ignore root */ true); - - indir->Addr() = actualAddr; - indir->gtFlags &= ~GTF_SIDE_EFFECT; - GenTree* const newComma = m_compiler->gtNewOperNode(GT_COMMA, indir->TypeGet(), sideEffects, indir); - *use = newComma; + GenTree* const lastEffect = addr->AsOp()->gtGetOp1(); + + if (lastEffect->IsCall() && + lastEffect->AsCall()->IsHelperCall(m_compiler, CORINFO_HELP_UNBOX_TYPETEST)) + { + GenTree* const actualAddr = addr->gtEffectiveVal(); + GenTree* sideEffects = nullptr; + m_compiler->gtExtractSideEffList(indir, &sideEffects, GTF_SIDE_EFFECT, /* ignore root */ true); + + // indir is based on a local address, no side effectg possible. + // + indir->Addr() = actualAddr; + indir->gtFlags &= ~GTF_SIDE_EFFECT; + GenTree* const newComma = + m_compiler->gtNewOperNode(GT_COMMA, indir->TypeGet(), sideEffects, indir); + *use = newComma; + } } } diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 255a43dc80b38..81d426939d3df 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1644,7 +1644,6 @@ void HelperCallProperties::init() case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE: case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE: - // isNoEscape = true; isPure = true; noThrow = true; // These return null for a failing cast break; From 2597a44e919d16633ce24ba3719eb363b08af969 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 27 Jun 2024 19:45:05 -0700 Subject: [PATCH 43/45] fix --- src/coreclr/jit/objectalloc.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 4e5476003dba6..230e195bb4665 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -800,8 +800,8 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p case GT_COLON: { - GenTree* const lhs = tree->AsOp()->gtGetOp1(); - GenTree* const rhs = tree->AsOp()->gtGetOp2(); + GenTree* const lhs = parent->AsOp()->gtGetOp1(); + GenTree* const rhs = parent->AsOp()->gtGetOp2(); // One or both children may have been retyped. // Ensure we don't lose the fact that the joint result @@ -837,10 +837,11 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p } parent->ChangeType(newType); - } + ++parentIndex; keepChecking = true; - break; + } + break; case GT_STOREIND: case GT_STORE_BLK: From 504012d20b1ae54bfb23c89304cbb2bbf4a92a3d Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 27 Jun 2024 19:48:06 -0700 Subject: [PATCH 44/45] fix typo in comment --- src/coreclr/jit/objectalloc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index 230e195bb4665..acfc4d33ba4a3 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -1034,7 +1034,7 @@ void ObjectAllocator::RewriteUses() GenTree* sideEffects = nullptr; m_compiler->gtExtractSideEffList(indir, &sideEffects, GTF_SIDE_EFFECT, /* ignore root */ true); - // indir is based on a local address, no side effectg possible. + // indir is based on a local address, no side effect possible. // indir->Addr() = actualAddr; indir->gtFlags &= ~GTF_SIDE_EFFECT; From 7af3d982d7fbf67b3c3d3bb6d6e629a8d9b4ad11 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 28 Jun 2024 10:14:31 -0700 Subject: [PATCH 45/45] more surgical sibling type update for qmark/colon --- src/coreclr/jit/objectalloc.cpp | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index acfc4d33ba4a3..89e28c5978c6d 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -803,39 +803,20 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p GenTree* const lhs = parent->AsOp()->gtGetOp1(); GenTree* const rhs = parent->AsOp()->gtGetOp2(); - // One or both children may have been retyped. - // Ensure we don't lose the fact that the joint result - // may be a GC type. + // We may see sibling null refs. Retype them as appropriate. // - var_types lhsType = lhs->TypeGet(); - var_types rhsType = rhs->TypeGet(); - - if (lhsType == TYP_REF) - { - assert(rhsType != TYP_REF); - assert(rhsType == tree->TypeGet()); - newType = TYP_BYREF; - } - - if (rhsType == TYP_REF) + if (lhs == tree) { - assert(lhsType != TYP_REF); - assert(lhsType == tree->TypeGet()); - newType = TYP_BYREF; + assert(rhs->IsIntegralConst(0)); + rhs->ChangeType(newType); } - - tree->ChangeType(newType); - - if (lhsType != newType) + else { + assert(rhs == tree); + assert(lhs->IsIntegralConst(0)); lhs->ChangeType(newType); } - if (rhsType != newType) - { - rhs->ChangeType(newType); - } - parent->ChangeType(newType); ++parentIndex;