diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
index 1586ba41321204..f1282ff6768c41 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 00000000000000..885a2d93c62428
--- /dev/null
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StackAllocatedBox.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+
+namespace System.Runtime.CompilerServices
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct StackAllocatedBox
+ {
+ // These fields are only accessed from jitted code
+ private MethodTable* _pMethodTable;
+ private T _value;
+ }
+}
diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index 89c61ebeac690e..cc1ca62f47485e 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
@@ -2552,6 +2553,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 38f9dac0e2e4d1..ee537e0c3be540 100644
--- a/src/coreclr/inc/icorjitinfoimpl_generated.h
+++ b/src/coreclr/inc/icorjitinfoimpl_generated.h
@@ -275,6 +275,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 76a1bbc466d790..ff7129df56b563 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 = { /* 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 50beaabe66e903..4273bebca40b74 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/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h
index a2bfe1de9c80cc..27ed43df0dcd35 100644
--- a/src/coreclr/jit/ICorJitInfo_names_generated.h
+++ b/src/coreclr/jit/ICorJitInfo_names_generated.h
@@ -68,6 +68,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 ff54b778f2a2cd..997405624d2cd1 100644
--- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
+++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
@@ -635,6 +635,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/compiler.h b/src/coreclr/jit/compiler.h
index 6136c97d5d47b8..35d64261f120a1 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -691,7 +691,8 @@ 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
regNumber lvRegNumForSlot(unsigned slotNum)
@@ -806,6 +807,11 @@ class LclVarDsc
return lvIsMultiRegArg || lvIsMultiRegRet;
}
+ bool IsStackAllocatedBox() const
+ {
+ return lvStackAllocatedBox;
+ }
+
#if defined(DEBUG)
private:
DoNotEnregisterReason m_doNotEnregReason;
@@ -3524,7 +3530,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 21bd4fc5a1efd2..69e95d60d58ae2 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/inlinepolicy.cpp b/src/coreclr/jit/inlinepolicy.cpp
index 3b771f291607bb..0c6ac9241fca6a 100644
--- a/src/coreclr/jit/inlinepolicy.cpp
+++ b/src/coreclr/jit/inlinepolicy.cpp
@@ -1628,7 +1628,7 @@ double ExtendedDefaultPolicy::DetermineMultiplier()
//
// void Caller() => DoNothing(42); // 42 is going to be boxed at the call site.
//
- multiplier += 0.5;
+ multiplier += 0.5 * m_ArgIsBoxedAtCallsite;
JITDUMP("\nCallsite is going to box %d arguments. Multiplier increased to %g.", m_ArgIsBoxedAtCallsite,
multiplier);
}
diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h
index 97322e200a3fe7..b0f0e90621c8d3 100644
--- a/src/coreclr/jit/jitconfigvalues.h
+++ b/src/coreclr/jit/jitconfigvalues.h
@@ -654,7 +654,10 @@ 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)
+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)
RELEASE_CONFIG_INTEGER(JitEECallTimingInfo, W("JitEECallTimingInfo"), 0)
diff --git a/src/coreclr/jit/jitmetadatalist.h b/src/coreclr/jit/jitmetadatalist.h
index 209133f4324b0f..10da7249177f88 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/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp
index 1774aea55c8314..aeed8fcc5610d4 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);
}
@@ -514,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.
//
@@ -802,7 +810,6 @@ class LocalAddressVisitor final : public GenTreeVisitor
}
PushValue(use);
-
return Compiler::WALK_CONTINUE;
}
@@ -1024,6 +1031,77 @@ 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);
+
+ Value& lhs = TopValue(1);
+ Value& rhs = TopValue(0);
+
+ if ((lhs.IsAddress() && rhs.Node()->IsIntegralConst(0)) ||
+ (rhs.IsAddress() && lhs.Node()->IsIntegralConst(0)))
+ {
+ 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;
+
+ INDEBUG(TopValue(0).Consume());
+ INDEBUG(TopValue(1).Consume());
+ 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);
+ PopValue();
+ EscapeValue(TopValue(0), node);
+ PopValue();
+ }
+
+ 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();
+ INDEBUG(TopValue(0).Consume());
+ PopValue();
+ m_stmtModified = true;
+ }
+ else
+ {
+ EscapeValue(TopValue(0), node);
+ PopValue();
+ }
+ break;
+ }
+
default:
while (TopValue(0).Node() != node)
{
diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index 687ac114e329f0..21b5d3cc79a8b3 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -2576,6 +2576,13 @@ 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);
return false;
}
@@ -3367,6 +3374,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()))
diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp
index 3d6206e0f723cf..89e28c5978c6d4 100644
--- a/src/coreclr/jit/objectalloc.cpp
+++ b/src/coreclr/jit/objectalloc.cpp
@@ -36,14 +36,31 @@ PhaseStatus ObjectAllocator::DoPhase()
return PhaseStatus::MODIFIED_NOTHING;
}
- if (IsObjectStackAllocationEnabled())
+ bool enabled = IsObjectStackAllocationEnabled();
+ const char* disableReason = ": global config";
+
+#ifdef DEBUG
+ // 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)
{
JITDUMP("enabled, analyzing...\n");
DoAnalysis();
}
else
{
- JITDUMP("disabled, punting\n");
+ JITDUMP("disabled%s, punting\n", IsObjectStackAllocationEnabled() ? disableReason : "");
+ m_IsObjectStackAllocationEnabled = false;
}
const bool didStackAllocate = MorphAllocObjNodes();
@@ -170,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())
{
@@ -305,7 +330,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]);
@@ -386,17 +413,53 @@ 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;
+ CORINFO_CLASS_HANDLE stackClsHnd = clsHnd;
+ const bool isValueClass = comp->info.compCompHnd->isValueClass(clsHnd);
+ 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 (isValueClass)
+ {
+ comp->Metrics.NewBoxedValueClassHelperCalls++;
+ stackClsHnd = comp->info.compCompHnd->getTypeForBoxOnStack(clsHnd);
+ }
+ else
{
- JITDUMP("Allocating local variable V%02u on the stack\n", lclNum);
+ comp->Metrics.NewRefClassHelperCalls++;
+ }
- const unsigned int stackLclNum = MorphAllocObjNodeIntoStackAlloc(asAllocObj, block, stmt);
+ // Don't attempt to do stack allocations inside basic blocks that may be in a loop.
+ //
+ if (!IsObjectStackAllocationEnabled())
+ {
+ 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 if (stackClsHnd == NO_CLASS_HANDLE)
+ {
+ assert(isValueClass);
+ onHeapReason = "[no class handle for this boxed value class]";
+ canStack = false;
+ }
+ else
+ {
+ JITDUMP("Allocating V%02u on the stack\n", lclNum);
+ canStack = true;
+ const unsigned int stackLclNum =
+ MorphAllocObjNodeIntoStackAlloc(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.
@@ -406,13 +469,22 @@ bool ObjectAllocator::MorphAllocObjNodes()
comp->optMethodFlags |= OMF_HAS_OBJSTACKALLOC;
didStackAllocate = true;
}
- else
+
+ if (canStack)
{
- if (IsObjectStackAllocationEnabled())
+ if (isValueClass)
{
- JITDUMP("Allocating local variable V%02u on the heap\n", lclNum);
+ comp->Metrics.StackAllocatedBoxedValueClasses++;
}
-
+ else
+ {
+ comp->Metrics.StackAllocatedRefClasses++;
+ }
+ }
+ else
+ {
+ assert(onHeapReason != nullptr);
+ JITDUMP("Allocating V%02u on the heap: %s\n", lclNum, onHeapReason);
data = MorphAllocObjNodeIntoHelperCall(asAllocObj);
stmtExpr->AsLclVar()->Data() = data;
stmtExpr->AddAllEffectsFlags(data);
@@ -487,32 +559,36 @@ 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);
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 unsigned int lclNum = comp->lvaGrabTemp(shortLifetime DEBUGARG(
+ isValueClass ? "stack allocated boxed value class temp" : "stack allocated ref class temp"));
+
+ 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))
{
//------------------------------------------------------------------------
@@ -533,6 +609,8 @@ unsigned int ObjectAllocator::MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* a
comp->compSuppressedZeroInit = true;
}
+ // Initialize the vtable slot.
+ //
//------------------------------------------------------------------------
// STMTx (IL 0x... ???)
// * STORE_LCL_FLD long
@@ -584,6 +662,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 +681,7 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack* parent
case GT_EQ:
case GT_NE:
+ case GT_NULLCHECK:
canLclVarEscapeViaParentStack = false;
break;
@@ -615,6 +696,7 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack* parent
case GT_COLON:
case GT_QMARK:
case GT_ADD:
+ case GT_BOX:
case GT_FIELD_ADDR:
// Check whether the local escapes via its grandparent.
++parentIndex;
@@ -622,6 +704,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.
@@ -639,12 +723,8 @@ 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 = true;
+ canLclVarEscapeViaParentStack =
+ !Compiler::s_helperCallProperties.IsNoEscape(comp->eeGetHelperNum(asCall->gtCallMethHnd));
}
break;
}
@@ -688,6 +768,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);
@@ -696,6 +777,7 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p
case GT_EQ:
case GT_NE:
+ case GT_NULLCHECK:
break;
case GT_COMMA:
@@ -705,7 +787,6 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p
break;
}
FALLTHROUGH;
- case GT_COLON:
case GT_QMARK:
case GT_ADD:
case GT_FIELD_ADDR:
@@ -717,7 +798,35 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p
keepChecking = true;
break;
+ case GT_COLON:
+ {
+ GenTree* const lhs = parent->AsOp()->gtGetOp1();
+ GenTree* const rhs = parent->AsOp()->gtGetOp2();
+
+ // We may see sibling null refs. Retype them as appropriate.
+ //
+ if (lhs == tree)
+ {
+ assert(rhs->IsIntegralConst(0));
+ rhs->ChangeType(newType);
+ }
+ else
+ {
+ assert(rhs == tree);
+ assert(lhs->IsIntegralConst(0));
+ lhs->ChangeType(newType);
+ }
+
+ parent->ChangeType(newType);
+
+ ++parentIndex;
+ keepChecking = true;
+ }
+ 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.
@@ -733,6 +842,7 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p
break;
case GT_IND:
+ case GT_CALL:
break;
default:
@@ -761,9 +871,9 @@ void ObjectAllocator::RewriteUses()
public:
enum
{
- DoPreOrder = true,
- DoLclVarsOnly = true,
- ComputeStack = true,
+ DoPreOrder = true,
+ DoPostOrder = true,
+ ComputeStack = true,
};
RewriteUsesVisitor(ObjectAllocator* allocator)
@@ -775,7 +885,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;
@@ -815,6 +929,105 @@ 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;
+ }
+ }
+ // Make box accesses explicit for UNBOX_HELPER
+ //
+ else if (tree->IsCall())
+ {
+ GenTreeCall* const call = tree->AsCall();
+
+ if (call->IsHelperCall(m_compiler, CORINFO_HELP_UNBOX))
+ {
+ JITDUMP("Found unbox helper call [%06u]\n", m_compiler->dspTreeID(call));
+
+ // 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);
+ GenTree* const secondArgNode = secondArg->GetNode();
+
+ if ((secondArgNode->OperIsLocal() || secondArgNode->OperIs(GT_LCL_ADDR)) &&
+ !secondArgNode->TypeIs(TYP_REF))
+ {
+ 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))
+ //
+ JITDUMP("Rewriting to invoke box type test helper%s\n", isForEffect ? " for side effect" : "");
+
+ call->gtCallMethHnd = m_compiler->eeFindHelper(CORINFO_HELP_UNBOX_TYPETEST);
+ GenTree* const mt = m_compiler->gtNewMethodTableLookup(lcl, /* onStack */ true);
+ call->gtArgs.Remove(secondArg);
+ call->gtArgs.PushBack(m_compiler, NewCallArg::Primitive(mt));
+
+ if (isForEffect)
+ {
+ // call was just for effect, we're done.
+ }
+ else
+ {
+ 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));
+ GenTree* const comma = m_compiler->gtNewOperNode(GT_COMMA, TYP_BYREF, call, payloadAddr);
+ *use = comma;
+ }
+ }
+ }
+ }
+ 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))
+ {
+ 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 effect possible.
+ //
+ 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;
+ }
};
for (BasicBlock* const block : comp->Blocks())
diff --git a/src/coreclr/jit/objectalloc.h b/src/coreclr/jit/objectalloc.h
index 07307161da002b..283216d79482be 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);
@@ -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);
@@ -110,34 +111,86 @@ 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;
+ }
+
+ if (comp->info.compCompHnd->getTypeForBoxOnStack(clsHnd) == NO_CLASS_HANDLE)
+ {
+ *reason = "[no boxed type available]";
+ 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;
+ }
+
+#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);
}
- 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/jit/utils.cpp b/src/coreclr/jit/utils.cpp
index 50429877708608..81d426939d3dfa 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)
{
@@ -1659,6 +1660,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
@@ -1667,8 +1669,11 @@ 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;
@@ -1830,6 +1835,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 f665a4e813eff6..29a1d7d29c933c 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/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp
index 17b45702776c80..0273f1bf43c347 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 869cce00c9c3c2..70c950fccfc7e0 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)
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
index 47d202909bce34..0ba59363c8f9c8 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/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
index a695d07be6fcf1..22cb30e58a6abd 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
@@ -2604,6 +2604,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 950ce7f4a12232..16840f73581a1b 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
@@ -957,6 +957,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)
{
@@ -2609,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;
@@ -2675,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;
}
diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt
index 9fc314fe56a370..e2157fb43e4d4e 100644
--- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt
+++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt
@@ -227,6 +227,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 75722274d9e9b6..a8e95b43bc38e3 100644
--- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h
+++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h
@@ -75,6 +75,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);
@@ -825,6 +826,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 fd85fdd97e892f..eb4968fbb44880 100644
--- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h
+++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h
@@ -134,6 +134,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 0e0fa438e727a0..53bb5a2f00c426 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 c970dd9a7b226c..679fa2e0355e17 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);
@@ -1181,6 +1185,7 @@ enum mcPackets
Packet_GetClassStaticDynamicInfo = 218,
Packet_GetClassThreadStaticDynamicInfo = 219,
Packet_IsGenericType = 220,
+ Packet_GetTypeForBoxOnStack = 221,
};
void SetDebugDumpVariables();
diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp
index 84ad616c28eb9c..611edfaf034e5b 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;
}
@@ -707,6 +715,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 2d6faf2c657283..db3573b30e418a 100644
--- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp
+++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp
@@ -522,6 +522,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 210485e727c9bf..152a3d9ba7cf14 100644
--- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp
+++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp
@@ -458,6 +458,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 9e29f43e03ec38..e42b41ee8365d4 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*)),
@@ -598,6 +598,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/corelib.h b/src/coreclr/vm/corelib.h
index 2e1a7e970563f7..9fcfbd8cf216ac 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/ecalllist.h b/src/coreclr/vm/ecalllist.h
index 4a9f2355f395c2..734fa19ad5729a 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 4d6127241bddbc..e1e490c44d2bd0 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -2305,6 +2305,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, pMT2);
+}
+HCIMPLEND
+
/*************************************************************/
HCIMPL2_IV(LPVOID, JIT_GetRefAny, CORINFO_CLASS_HANDLE type, TypedByRef typedByRef)
{
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 54ce75255eeaf6..9dfb3daffd4d13 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -1906,11 +1906,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;
@@ -1934,10 +1945,13 @@ bool CEEInfo::canAllocateOnStack(CORINFO_CLASS_HANDLE clsHnd)
TypeHandle VMClsHnd(clsHnd);
MethodTable* pMT = VMClsHnd.GetMethodTable();
_ASSERTE(pMT);
- _ASSERTE(!pMT->IsValueType());
result = !pMT->HasFinalizer();
+#ifdef FEATURE_COMINTEROP
+ result &= !pMT->IsComObjectType();
+#endif // FEATURE_COMINTEROP
+
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
@@ -6226,6 +6240,47 @@ 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
+ {
+ Instantiation boxedFieldsInst(&VMClsHnd, 1);
+ TypeHandle stackAllocatedBox = CoreLibBinder::GetClass(CLASS__STACKALLOCATEDBOX);
+ TypeHandle stackAllocatedBoxInst = stackAllocatedBox.Instantiate(boxedFieldsInst);
+ result = static_cast(stackAllocatedBoxInst.AsPtr());
+ }
+
+ EE_TO_JIT_TRANSITION();
+
+ return result;
+}
+
/***********************************************************************/
// see code:Nullable#NullableVerification
CorInfoHelpFunc CEEInfo::getBoxHelper(CORINFO_CLASS_HANDLE clsHnd)
diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h
index f34786e2e994fd..68617d39305bd2 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
diff --git a/src/tests/JIT/opt/ObjectStackAllocation/ObjectStackAllocationTests.cs b/src/tests/JIT/opt/ObjectStackAllocation/ObjectStackAllocationTests.cs
index 9faf9b4cce4f09..5a45f43dcc7cae 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;
}