Skip to content

Commit 4ea3cb1

Browse files
authored
JIT: Escape analysis cleanup (#114716)
* use `printfAlloc` in a few places * move escape-analysis specific layout code to objectalloc.cpp * use custom layout for stack allocated boxes * use layout a bit more broadly in objectalloc.cpp
1 parent 40f209c commit 4ea3cb1

File tree

5 files changed

+126
-113
lines changed

5 files changed

+126
-113
lines changed

src/coreclr/jit/compiler.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10945,10 +10945,6 @@ class Compiler
1094510945
unsigned typGetBlkLayoutNum(unsigned blockSize);
1094610946
// Get the layout for the specified array of known length
1094710947
ClassLayout* typGetArrayLayout(CORINFO_CLASS_HANDLE classHandle, unsigned length);
10948-
// Get a layout like an existing layout, with all gc refs removed
10949-
ClassLayout* typGetNonGCLayout(ClassLayout* existingLayout);
10950-
// Get a layout like an existing layout, with all gc refs changed to byrefs
10951-
ClassLayout* typGetByrefLayout(ClassLayout* existingLayout);
1095210948

1095310949
var_types TypeHandleToVarType(CORINFO_CLASS_HANDLE handle, ClassLayout** pLayout = nullptr);
1095410950
var_types TypeHandleToVarType(CorInfoType jitType, CORINFO_CLASS_HANDLE handle, ClassLayout** pLayout = nullptr);

src/coreclr/jit/layout.cpp

Lines changed: 11 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -420,33 +420,6 @@ ClassLayout* Compiler::typGetArrayLayout(CORINFO_CLASS_HANDLE classHandle, unsig
420420
return typGetCustomLayout(b);
421421
}
422422

423-
ClassLayout* Compiler::typGetNonGCLayout(ClassLayout* layout)
424-
{
425-
assert(layout->HasGCPtr());
426-
ClassLayoutBuilder b(this, layout->GetSize());
427-
b.CopyPaddingFrom(0, layout);
428-
429-
#ifdef DEBUG
430-
b.CopyNameFrom(layout, "[nongc] ");
431-
#endif
432-
433-
return typGetCustomLayout(b);
434-
}
435-
436-
ClassLayout* Compiler::typGetByrefLayout(ClassLayout* layout)
437-
{
438-
assert(layout->HasGCPtr());
439-
ClassLayoutBuilder b(this, layout->GetSize());
440-
b.CopyPaddingFrom(0, layout);
441-
b.CopyGCInfoFromMakeByref(0, layout);
442-
443-
#ifdef DEBUG
444-
b.CopyNameFrom(layout, "[byref] ");
445-
#endif
446-
447-
return typGetCustomLayout(b);
448-
}
449-
450423
#ifdef DEBUG
451424
//------------------------------------------------------------------------
452425
// CopyNameFrom: Copy layout names, with optional prefix.
@@ -462,21 +435,17 @@ void ClassLayoutBuilder::CopyNameFrom(ClassLayout* layout, const char* prefix)
462435

463436
if (prefix != nullptr)
464437
{
465-
char* newName = nullptr;
466-
char* newShortName = nullptr;
438+
const char* newName = nullptr;
439+
const char* newShortName = nullptr;
467440

468441
if (layoutName != nullptr)
469442
{
470-
size_t len = strlen(prefix) + strlen(layoutName) + 1;
471-
newName = m_compiler->getAllocator(CMK_DebugOnly).allocate<char>(len);
472-
sprintf_s(newName, len, "%s%s", prefix, layoutShortName);
443+
newName = m_compiler->printfAlloc("%s%.100s", prefix, layoutName);
473444
}
474445

475446
if (layoutShortName != nullptr)
476447
{
477-
size_t len = strlen(prefix) + strlen(layoutName) + 1;
478-
newShortName = m_compiler->getAllocator(CMK_DebugOnly).allocate<char>(len);
479-
sprintf_s(newShortName, len, "%s%s", prefix, layoutShortName);
448+
newShortName = m_compiler->printfAlloc("%s%.100s", prefix, layoutShortName);
480449
}
481450

482451
SetName(newName, newShortName);
@@ -708,8 +677,8 @@ const SegmentList& ClassLayout::GetNonPadding(Compiler* comp)
708677
// AreCompatible: check if 2 layouts are the same for copying.
709678
//
710679
// Arguments:
711-
// layout1 - the first layout (copy destination)
712-
// layout2 - the second layout (copy source)
680+
// layout1 - the first layout
681+
// layout2 - the second layout
713682
//
714683
// Return value:
715684
// true if compatible, false otherwise.
@@ -773,8 +742,6 @@ bool ClassLayout::AreCompatible(const ClassLayout* layout1, const ClassLayout* l
773742
return true;
774743
}
775744

776-
assert(clsHnd1 != NO_CLASS_HANDLE);
777-
assert(clsHnd2 != NO_CLASS_HANDLE);
778745
assert(layout1->HasGCPtr() && layout2->HasGCPtr());
779746

780747
if (layout1->GetGCPtrCount() != layout2->GetGCPtrCount())
@@ -815,16 +782,13 @@ bool ClassLayout::CanAssignFrom(const ClassLayout* layout)
815782
return true;
816783
}
817784

818-
// Do the normal compatibility check first, when possible to do so.
785+
// Do the normal compatibility check first
819786
//
820-
if ((IsCustomLayout() == layout->IsCustomLayout()) || (!HasGCPtr() && !layout->HasGCPtr()))
821-
{
822-
const bool areCompatible = AreCompatible(this, layout);
787+
const bool areCompatible = AreCompatible(this, layout);
823788

824-
if (areCompatible)
825-
{
826-
return true;
827-
}
789+
if (areCompatible)
790+
{
791+
return true;
828792
}
829793

830794
// Must be same size
@@ -1087,34 +1051,6 @@ void ClassLayoutBuilder::CopyGCInfoFrom(unsigned offset, ClassLayout* layout)
10871051
}
10881052
}
10891053

1090-
//------------------------------------------------------------------------
1091-
// CopyInfoGCFromMakeByref: Copy GC pointers from another layout,and change
1092-
// all gc references to be TYP_BYREF (TYPE_GC_BYREF)
1093-
//
1094-
// Arguments:
1095-
// offset - Offset in this builder to start copy information into.
1096-
// layout - Layout to get information from.
1097-
//
1098-
void ClassLayoutBuilder::CopyGCInfoFromMakeByref(unsigned offset, ClassLayout* layout)
1099-
{
1100-
assert(offset + layout->GetSize() <= m_size);
1101-
1102-
if (layout->GetGCPtrCount() > 0)
1103-
{
1104-
assert(offset % TARGET_POINTER_SIZE == 0);
1105-
unsigned startSlot = offset / TARGET_POINTER_SIZE;
1106-
for (unsigned slot = 0; slot < layout->GetSlotCount(); slot++)
1107-
{
1108-
CorInfoGCType gcType = layout->GetGCPtr(slot);
1109-
if (gcType == TYPE_GC_REF)
1110-
{
1111-
gcType = TYPE_GC_BYREF;
1112-
}
1113-
SetGCPtr(startSlot + slot, gcType);
1114-
}
1115-
}
1116-
}
1117-
11181054
//------------------------------------------------------------------------
11191055
// CopyInfoPaddingFrom: Copy padding from another layout.
11201056
//

src/coreclr/jit/layout.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ class ClassLayoutBuilder
3535

3636
void SetGCPtrType(unsigned slot, var_types type);
3737
void CopyGCInfoFrom(unsigned offset, ClassLayout* layout);
38-
void CopyGCInfoFromMakeByref(unsigned offset, ClassLayout* layout);
3938
void CopyPaddingFrom(unsigned offset, ClassLayout* layout);
4039
void AddPadding(const SegmentList::Segment& padding);
4140
void RemovePadding(const SegmentList::Segment& nonPadding);

src/coreclr/jit/objectalloc.cpp

Lines changed: 107 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -960,12 +960,6 @@ bool ObjectAllocator::CanAllocateLclVarOnStack(unsigned int lclNum,
960960
return false;
961961
}
962962

963-
if (comp->info.compCompHnd->getTypeForBoxOnStack(clsHnd) == NO_CLASS_HANDLE)
964-
{
965-
*reason = "[no boxed type available]";
966-
return false;
967-
}
968-
969963
classSize = comp->info.compCompHnd->getClassSize(clsHnd);
970964
}
971965
else
@@ -1061,6 +1055,7 @@ ObjectAllocator::ObjectAllocationType ObjectAllocator::AllocationKind(GenTree* t
10611055
return allocType;
10621056
}
10631057

1058+
//------------------------------------------------------------------------
10641059
// MorphAllocObjNodes: Morph each GT_ALLOCOBJ node either into an
10651060
// allocation helper call or stack allocation.
10661061
//
@@ -1203,13 +1198,11 @@ bool ObjectAllocator::MorphAllocObjNodes()
12031198
//------------------------------------------------------------------------
12041199

12051200
CORINFO_CLASS_HANDLE clsHnd = data->AsAllocObj()->gtAllocObjClsHnd;
1206-
CORINFO_CLASS_HANDLE stackClsHnd = clsHnd;
12071201
const bool isValueClass = comp->info.compCompHnd->isValueClass(clsHnd);
12081202

12091203
if (isValueClass)
12101204
{
12111205
comp->Metrics.NewBoxedValueClassHelperCalls++;
1212-
stackClsHnd = comp->info.compCompHnd->getTypeForBoxOnStack(clsHnd);
12131206
}
12141207
else
12151208
{
@@ -1221,29 +1214,31 @@ bool ObjectAllocator::MorphAllocObjNodes()
12211214
// reason set by the call
12221215
canStack = false;
12231216
}
1224-
else if (stackClsHnd == NO_CLASS_HANDLE)
1225-
{
1226-
assert(isValueClass);
1227-
onHeapReason = "[no class handle for this boxed value class]";
1228-
canStack = false;
1229-
}
12301217
else
12311218
{
12321219
JITDUMP("Allocating V%02u on the stack\n", lclNum);
12331220
canStack = true;
1234-
const unsigned int stackLclNum =
1235-
MorphAllocObjNodeIntoStackAlloc(data->AsAllocObj(), stackClsHnd, isValueClass, block, stmt);
1236-
m_HeapLocalToStackLocalMap.AddOrUpdate(lclNum, stackLclNum);
1221+
1222+
ClassLayout* layout = nullptr;
12371223

12381224
if (isValueClass)
12391225
{
1226+
CORINFO_CLASS_HANDLE boxedClsHnd = comp->info.compCompHnd->getTypeForBox(clsHnd);
1227+
assert(boxedClsHnd != NO_CLASS_HANDLE);
1228+
ClassLayout* structLayout = comp->typGetObjLayout(boxedClsHnd);
1229+
layout = GetBoxedLayout(structLayout);
12401230
comp->Metrics.StackAllocatedBoxedValueClasses++;
12411231
}
12421232
else
12431233
{
1234+
layout = comp->typGetObjLayout(clsHnd);
12441235
comp->Metrics.StackAllocatedRefClasses++;
12451236
}
12461237

1238+
const unsigned int stackLclNum =
1239+
MorphAllocObjNodeIntoStackAlloc(data->AsAllocObj(), layout, block, stmt);
1240+
m_HeapLocalToStackLocalMap.AddOrUpdate(lclNum, stackLclNum);
1241+
12471242
bashCall = true;
12481243
}
12491244
}
@@ -1449,8 +1444,7 @@ unsigned int ObjectAllocator::MorphNewArrNodeIntoStackAlloc(GenTreeCall*
14491444
// allocation.
14501445
// Arguments:
14511446
// allocObj - GT_ALLOCOBJ that will be replaced by a stack allocation
1452-
// clsHnd - class representing the stack allocated object
1453-
// isValueClass - we are stack allocating a boxed value class
1447+
// layout - layout for the stack allocated objectd
14541448
// block - a basic block where allocObj is
14551449
// stmt - a statement where allocObj is
14561450
//
@@ -1460,24 +1454,29 @@ unsigned int ObjectAllocator::MorphNewArrNodeIntoStackAlloc(GenTreeCall*
14601454
// Notes:
14611455
// This function can insert additional statements before stmt.
14621456
//
1463-
unsigned int ObjectAllocator::MorphAllocObjNodeIntoStackAlloc(
1464-
GenTreeAllocObj* allocObj, CORINFO_CLASS_HANDLE clsHnd, bool isValueClass, BasicBlock* block, Statement* stmt)
1457+
unsigned int ObjectAllocator::MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* allocObj,
1458+
ClassLayout* layout,
1459+
BasicBlock* block,
1460+
Statement* stmt)
14651461
{
14661462
assert(allocObj != nullptr);
14671463
assert(m_AnalysisDone);
1468-
assert(clsHnd != NO_CLASS_HANDLE);
14691464

1470-
const bool shortLifetime = false;
1471-
const unsigned int lclNum = comp->lvaGrabTemp(shortLifetime DEBUGARG(
1472-
isValueClass ? "stack allocated boxed value class temp" : "stack allocated ref class temp"));
1465+
#ifdef DEBUG
1466+
const char* lclName = comp->printfAlloc("stack allocated %.110s", layout->GetShortClassName());
1467+
#endif
14731468

1474-
comp->lvaSetStruct(lclNum, clsHnd, /* unsafeValueClsCheck */ false);
1469+
const bool shortLifetime = false;
1470+
const unsigned int lclNum = comp->lvaGrabTemp(shortLifetime DEBUGARG(lclName));
1471+
comp->lvaSetStruct(lclNum, layout, /* unsafeValueClsCheck */ false);
14751472

1476-
// Initialize the object memory if necessary.
1477-
bool bbInALoop = block->HasFlag(BBF_BACKWARD_JUMP);
1478-
bool bbIsReturn = block->KindIs(BBJ_RETURN);
14791473
LclVarDsc* const lclDsc = comp->lvaGetDesc(lclNum);
14801474
lclDsc->lvStackAllocatedObject = true;
1475+
1476+
// Initialize the object memory if necessary.
1477+
bool bbInALoop = block->HasFlag(BBF_BACKWARD_JUMP);
1478+
bool bbIsReturn = block->KindIs(BBJ_RETURN);
1479+
14811480
if (comp->fgVarNeedsExplicitZeroInit(lclNum, bbInALoop, bbIsReturn))
14821481
{
14831482
//------------------------------------------------------------------------
@@ -2317,15 +2316,15 @@ void ObjectAllocator::RewriteUses()
23172316
if (newType == TYP_I_IMPL)
23182317
{
23192318
// New layout with no gc refs + padding
2320-
newLayout = comp->typGetNonGCLayout(layout);
2319+
newLayout = GetNonGCLayout(layout);
23212320
JITDUMP("Changing layout of struct V%02u to block\n", lclNum);
23222321
lclVarDsc->ChangeLayout(newLayout);
23232322
}
23242323
else
23252324
{
23262325
// New layout with all gc refs as byrefs + padding
23272326
// (todo, perhaps: see if old layout was already all byrefs)
2328-
newLayout = comp->typGetByrefLayout(layout);
2327+
newLayout = GetByrefLayout(layout);
23292328
JITDUMP("Changing layout of struct V%02u to byref\n", lclNum);
23302329
lclVarDsc->ChangeLayout(newLayout);
23312330
}
@@ -4077,3 +4076,80 @@ void ObjectAllocator::CloneAndSpecialize()
40774076

40784077
assert(numberOfClonedRegions == m_regionsToClone);
40794078
}
4079+
4080+
//------------------------------------------------------------------------------
4081+
// GetBoxedLayout: get a layout for a boxed version of a struct
4082+
//
4083+
// Arguments:
4084+
// layout - layout of the struct
4085+
//
4086+
// Notes:
4087+
// For Nullable<T>, layout class should be T
4088+
//
4089+
ClassLayout* ObjectAllocator::GetBoxedLayout(ClassLayout* layout)
4090+
{
4091+
assert(layout->IsValueClass());
4092+
4093+
ClassLayoutBuilder b(comp, TARGET_POINTER_SIZE + layout->GetSize());
4094+
b.CopyPaddingFrom(TARGET_POINTER_SIZE, layout);
4095+
b.CopyGCInfoFrom(TARGET_POINTER_SIZE, layout);
4096+
4097+
#ifdef DEBUG
4098+
b.CopyNameFrom(layout, "[boxed] ");
4099+
#endif
4100+
4101+
return comp->typGetCustomLayout(b);
4102+
}
4103+
4104+
//------------------------------------------------------------------------------
4105+
// GetNonGCLayout: get a layout with the same size and padding as an existing
4106+
// layout, but with no GC fields.
4107+
//
4108+
// Arguments:
4109+
// layout - existing layout to use as template
4110+
//
4111+
ClassLayout* ObjectAllocator::GetNonGCLayout(ClassLayout* layout)
4112+
{
4113+
assert(layout->HasGCPtr());
4114+
ClassLayoutBuilder b(comp, layout->GetSize());
4115+
b.CopyPaddingFrom(0, layout);
4116+
4117+
#ifdef DEBUG
4118+
b.CopyNameFrom(layout, "[nongc] ");
4119+
#endif
4120+
4121+
return comp->typGetCustomLayout(b);
4122+
}
4123+
4124+
//------------------------------------------------------------------------------
4125+
// GetByrefLayout: get a layout with the same size and padding as an existing
4126+
// layout, but with all GC fields retyped to byref.
4127+
//
4128+
// Arguments:
4129+
// layout - existing layout to use as template
4130+
//
4131+
ClassLayout* ObjectAllocator::GetByrefLayout(ClassLayout* layout)
4132+
{
4133+
assert(layout->HasGCPtr());
4134+
ClassLayoutBuilder b(comp, layout->GetSize());
4135+
b.CopyPaddingFrom(0, layout);
4136+
4137+
if (layout->GetGCPtrCount() > 0)
4138+
{
4139+
for (unsigned slot = 0; slot < layout->GetSlotCount(); slot++)
4140+
{
4141+
var_types gcType = layout->GetGCPtrType(slot);
4142+
if (gcType == TYP_REF)
4143+
{
4144+
gcType = TYP_BYREF;
4145+
}
4146+
b.SetGCPtrType(slot, gcType);
4147+
}
4148+
}
4149+
4150+
#ifdef DEBUG
4151+
b.CopyNameFrom(layout, "[byref] ");
4152+
#endif
4153+
4154+
return comp->typGetCustomLayout(b);
4155+
}

0 commit comments

Comments
 (0)