Skip to content

Commit 53a8a01

Browse files
AndyAyersMSjakobbotschjkotas
authored
Stack allocate unescaped boxes (#103361)
Enable object stack allocation for ref classes and extend the support to include boxed value classes. Use a specialized unbox helper for stack allocated boxes, both to avoid apparent escape of the box by the helper, and to ensure all box field accesses are visible to the JIT. Update the local address visitor to rewrite trees involving address of stack allocated boxes in some cases to avoid address exposure. Disable old promotion for stack allocated boxes (since we have no field handles) and allow physical promotion to enregister the box method table and/or payload as appropriate. In OSR methods handle the fact that the stack allocation may actually have been a heap allocation by the Tier0 method. The analysis TP cost is around 0.4-0.7% (notes below). Boxes are much less likely to escape than ref classes (roughly ~90% of boxes escape, ~99.8% of ref classes escape). Codegen impact is diminished somewhat because many of the boxes are dead and were already getting optimized away. Fixes #4584, #9118, #10195, #11192, #53585, #58554, #85570 --------- Co-authored-by: Jakob Botsch Nielsen <[email protected]> Co-authored-by: Jan Kotas <[email protected]>
1 parent 8366f6a commit 53a8a01

39 files changed

+832
-188
lines changed

src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@
197197
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\CastHelpers.cs" />
198198
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\ICastableHelpers.cs" />
199199
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeHelpers.CoreCLR.cs" />
200+
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\StackAllocatedBox.cs" />
200201
<Compile Include="$(BclSourcesRoot)\System\Runtime\ControlledExecution.CoreCLR.cs" />
201202
<Compile Include="$(BclSourcesRoot)\System\Runtime\DependentHandle.cs" />
202203
<Compile Include="$(MSBuildThisFileDirectory)..\nativeaot\Common\src\System\Runtime\RhFailFastReason.cs" />
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.InteropServices;
5+
6+
namespace System.Runtime.CompilerServices
7+
{
8+
[StructLayout(LayoutKind.Sequential)]
9+
internal unsafe struct StackAllocatedBox<T>
10+
{
11+
// These fields are only accessed from jitted code
12+
private MethodTable* _pMethodTable;
13+
private T _value;
14+
}
15+
}

src/coreclr/inc/corinfo.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ enum CorInfoHelpFunc
445445
CORINFO_HELP_BOX, // Fast box helper. Only possible exception is OutOfMemory
446446
CORINFO_HELP_BOX_NULLABLE, // special form of boxing for Nullable<T>
447447
CORINFO_HELP_UNBOX,
448+
CORINFO_HELP_UNBOX_TYPETEST, // Verify unbox type, throws if incompatible
448449
CORINFO_HELP_UNBOX_NULLABLE, // special form of unboxing for Nullable<T>
449450
CORINFO_HELP_GETREFANY, // Extract the byref from a TypedReference, checking that it is the expected type
450451

@@ -2552,6 +2553,14 @@ class ICorStaticInfo
25522553
CORINFO_CLASS_HANDLE cls
25532554
) = 0;
25542555

2556+
// Get a representation for a stack-allocated boxed value type.
2557+
//
2558+
// This differs from getTypeForBox in that it includes an explicit field
2559+
// for the method table pointer.
2560+
virtual CORINFO_CLASS_HANDLE getTypeForBoxOnStack(
2561+
CORINFO_CLASS_HANDLE cls
2562+
) = 0;
2563+
25552564
// returns the correct box helper for a particular class. Note
25562565
// that if this returns CORINFO_HELP_BOX, the JIT can assume
25572566
// 'standard' boxing (allocate object and copy), and optimize

src/coreclr/inc/icorjitinfoimpl_generated.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,9 @@ CorInfoHelpFunc getSharedCCtorHelper(
275275
CORINFO_CLASS_HANDLE getTypeForBox(
276276
CORINFO_CLASS_HANDLE cls) override;
277277

278+
CORINFO_CLASS_HANDLE getTypeForBoxOnStack(
279+
CORINFO_CLASS_HANDLE cls) override;
280+
278281
CorInfoHelpFunc getBoxHelper(
279282
CORINFO_CLASS_HANDLE cls) override;
280283

src/coreclr/inc/jiteeversionguid.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
4343
#define GUID_DEFINED
4444
#endif // !GUID_DEFINED
4545

46-
constexpr GUID JITEEVersionIdentifier = { /* e428e66d-5e0e-4320-ad8a-fa5a50f6da07 */
47-
0xe428e66d,
48-
0x5e0e,
49-
0x4320,
50-
{0xad, 0x8a, 0xfa, 0x5a, 0x50, 0xf6, 0xda, 0x07}
46+
constexpr GUID JITEEVersionIdentifier = { /* 748cd07e-c686-4085-8f5f-54c1b9cff44c */
47+
0x748cd07e,
48+
0xc686,
49+
0x4085,
50+
{0x8f, 0x5f, 0x54, 0xc1, 0xb9, 0xcf, 0xf4, 0x4c}
5151
};
5252

5353
//////////////////////////////////////////////////////////////////////////////////////////////////////////

src/coreclr/inc/jithelpers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
DYNAMICJITHELPER(CORINFO_HELP_BOX, JIT_Box, CORINFO_HELP_SIG_REG_ONLY)
107107
JITHELPER(CORINFO_HELP_BOX_NULLABLE, JIT_Box, CORINFO_HELP_SIG_REG_ONLY)
108108
DYNAMICJITHELPER(CORINFO_HELP_UNBOX, NULL, CORINFO_HELP_SIG_REG_ONLY)
109+
JITHELPER(CORINFO_HELP_UNBOX_TYPETEST, JIT_Unbox_TypeTest, CORINFO_HELP_SIG_REG_ONLY)
109110
JITHELPER(CORINFO_HELP_UNBOX_NULLABLE, JIT_Unbox_Nullable, CORINFO_HELP_SIG_4_STACK)
110111

111112
JITHELPER(CORINFO_HELP_GETREFANY, JIT_GetRefAny, CORINFO_HELP_SIG_8_STACK)

src/coreclr/jit/ICorJitInfo_names_generated.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ DEF_CLR_API(getNewArrHelper)
6868
DEF_CLR_API(getCastingHelper)
6969
DEF_CLR_API(getSharedCCtorHelper)
7070
DEF_CLR_API(getTypeForBox)
71+
DEF_CLR_API(getTypeForBoxOnStack)
7172
DEF_CLR_API(getBoxHelper)
7273
DEF_CLR_API(getUnBoxHelper)
7374
DEF_CLR_API(getRuntimeTypePointer)

src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,15 @@ CORINFO_CLASS_HANDLE WrapICorJitInfo::getTypeForBox(
635635
return temp;
636636
}
637637

638+
CORINFO_CLASS_HANDLE WrapICorJitInfo::getTypeForBoxOnStack(
639+
CORINFO_CLASS_HANDLE cls)
640+
{
641+
API_ENTER(getTypeForBoxOnStack);
642+
CORINFO_CLASS_HANDLE temp = wrapHnd->getTypeForBoxOnStack(cls);
643+
API_LEAVE(getTypeForBoxOnStack);
644+
return temp;
645+
}
646+
638647
CorInfoHelpFunc WrapICorJitInfo::getBoxHelper(
639648
CORINFO_CLASS_HANDLE cls)
640649
{

src/coreclr/jit/compiler.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,8 @@ class LclVarDsc
690690
unsigned char lvSingleDefDisqualifyReason = 'H';
691691
#endif
692692

693-
unsigned char lvAllDefsAreNoGc : 1; // For pinned locals: true if all defs of this local are no-gc
693+
unsigned char lvAllDefsAreNoGc : 1; // For pinned locals: true if all defs of this local are no-gc
694+
unsigned char lvStackAllocatedBox : 1; // Local is a stack allocated box
694695

695696
#if FEATURE_MULTIREG_ARGS
696697
regNumber lvRegNumForSlot(unsigned slotNum)
@@ -805,6 +806,11 @@ class LclVarDsc
805806
return lvIsMultiRegArg || lvIsMultiRegRet;
806807
}
807808

809+
bool IsStackAllocatedBox() const
810+
{
811+
return lvStackAllocatedBox;
812+
}
813+
808814
#if defined(DEBUG)
809815
private:
810816
DoNotEnregisterReason m_doNotEnregReason;
@@ -3523,7 +3529,7 @@ class Compiler
35233529

35243530
GenTree* gtNewRuntimeLookup(CORINFO_GENERIC_HANDLE hnd, CorInfoGenericHandleType hndTyp, GenTree* lookupTree);
35253531

3526-
GenTreeIndir* gtNewMethodTableLookup(GenTree* obj);
3532+
GenTreeIndir* gtNewMethodTableLookup(GenTree* obj, bool onStack = false);
35273533

35283534
//------------------------------------------------------------------------
35293535
// Other GenTree functions

src/coreclr/jit/compiler.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,9 +1872,9 @@ inline GenTreeCast* Compiler::gtNewCastNodeL(var_types typ, GenTree* op1, bool f
18721872
return cast;
18731873
}
18741874

1875-
inline GenTreeIndir* Compiler::gtNewMethodTableLookup(GenTree* object)
1875+
inline GenTreeIndir* Compiler::gtNewMethodTableLookup(GenTree* object, bool onStack)
18761876
{
1877-
assert(object->TypeIs(TYP_REF));
1877+
assert(onStack || object->TypeIs(TYP_REF));
18781878
GenTreeIndir* result = gtNewIndir(TYP_I_IMPL, object, GTF_IND_INVARIANT);
18791879
return result;
18801880
}

0 commit comments

Comments
 (0)