Skip to content

Commit 812df7b

Browse files
committed
JIT: enhance escape analysis to understand local struct fields
Continuation of dotnet#113141 Implement a very simplistic "field sensitive" analysis for gc structs where we model each struct as simply its gc field(s). That is, given a gc struct `G` with GC field `f`, we model ``` G.f = ... ``` as an assignment to `G` and ``` = G.f ``` as a read from `G`. Since we know `G` itself cannot escape, "escaping" of `G` means `G.f` can escape. Note this conflates the fields of `G`: either they all escape or none of them do. We could make this more granular but it's not clear that doing so is worthwhile, and it requires more up-front work to determine how to track each field's status. If the struct or struct fields are only consumed locally, we may be able to prove the gc referents don't escape. This is a subset/elaboration of dotnet#112543 that does not try and reason interprocedurally. Contributes to dotnet#104936 / dotnet#108913 Fixes dotnet#113236 (once the right inlines happen)
1 parent 68db36f commit 812df7b

File tree

7 files changed

+715
-66
lines changed

7 files changed

+715
-66
lines changed

src/coreclr/jit/compiler.h

+11-2
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,13 @@ class LclVarDsc
10361036
m_layout = layout;
10371037
}
10381038

1039+
// Change the layout to one that may not be compatible.
1040+
void ChangeLayout(ClassLayout* layout)
1041+
{
1042+
assert(varTypeIsStruct(lvType));
1043+
m_layout = layout;
1044+
}
1045+
10391046
// Grow the size of a block layout local.
10401047
void GrowBlockLayout(ClassLayout* layout)
10411048
{
@@ -10928,8 +10935,10 @@ class Compiler
1092810935
unsigned typGetBlkLayoutNum(unsigned blockSize);
1092910936
// Get the layout for the specified array of known length
1093010937
ClassLayout* typGetArrayLayout(CORINFO_CLASS_HANDLE classHandle, unsigned length);
10931-
// Get the number of a layout for the specified array of known length
10932-
unsigned typGetArrayLayoutNum(CORINFO_CLASS_HANDLE classHandle, unsigned length);
10938+
// Get a layout like an existing layout, with all gc refs removed
10939+
ClassLayout* typGetNonGCLayout(ClassLayout* existingLayout);
10940+
// Get a layout like an existing layout, with all gc refs changed to byrefs
10941+
ClassLayout* typGetByrefLayout(ClassLayout* existingLayout);
1093310942

1093410943
var_types TypeHandleToVarType(CORINFO_CLASS_HANDLE handle, ClassLayout** pLayout = nullptr);
1093510944
var_types TypeHandleToVarType(CorInfoType jitType, CORINFO_CLASS_HANDLE handle, ClassLayout** pLayout = nullptr);

src/coreclr/jit/jitconfigvalues.h

+1
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,7 @@ RELEASE_CONFIG_INTEGER(JitObjectStackAllocationConditionalEscape, "JitObjectStac
678678
CONFIG_STRING(JitObjectStackAllocationConditionalEscapeRange, "JitObjectStackAllocationConditionalEscapeRange")
679679
RELEASE_CONFIG_INTEGER(JitObjectStackAllocationArray, "JitObjectStackAllocationArray", 1)
680680
RELEASE_CONFIG_INTEGER(JitObjectStackAllocationSize, "JitObjectStackAllocationSize", 528)
681+
RELEASE_CONFIG_INTEGER(JitObjectStackAllocationTrackFields, "JitObjectStackAllocationTrackFields", 1)
681682

682683
RELEASE_CONFIG_INTEGER(JitEECallTimingInfo, "JitEECallTimingInfo", 0)
683684

src/coreclr/jit/layout.cpp

+198-17
Original file line numberDiff line numberDiff line change
@@ -414,18 +414,80 @@ ClassLayout* Compiler::typGetBlkLayout(unsigned blockSize)
414414
return typGetCustomLayout(ClassLayoutBuilder(this, blockSize));
415415
}
416416

417-
unsigned Compiler::typGetArrayLayoutNum(CORINFO_CLASS_HANDLE classHandle, unsigned length)
417+
ClassLayout* Compiler::typGetArrayLayout(CORINFO_CLASS_HANDLE classHandle, unsigned length)
418418
{
419419
ClassLayoutBuilder b = ClassLayoutBuilder::BuildArray(this, classHandle, length);
420-
return typGetCustomLayoutNum(b);
420+
return typGetCustomLayout(b);
421421
}
422422

423-
ClassLayout* Compiler::typGetArrayLayout(CORINFO_CLASS_HANDLE classHandle, unsigned length)
423+
ClassLayout* Compiler::typGetNonGCLayout(ClassLayout* layout)
424424
{
425-
ClassLayoutBuilder b = ClassLayoutBuilder::BuildArray(this, classHandle, length);
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+
426447
return typGetCustomLayout(b);
427448
}
428449

450+
#ifdef DEBUG
451+
//------------------------------------------------------------------------
452+
// CopyNameFrom: Copy layout names, with optional prefix.
453+
//
454+
// Parameters:
455+
// layout - layout to copy from
456+
// prefix - prefix to add (or nullptr)
457+
//
458+
void ClassLayoutBuilder::CopyNameFrom(ClassLayout* layout, const char* prefix)
459+
{
460+
const char* layoutName = layout->GetClassName();
461+
const char* layoutShortName = layout->GetShortClassName();
462+
463+
if (prefix != nullptr)
464+
{
465+
char* newName = nullptr;
466+
char* newShortName = nullptr;
467+
468+
if (layoutName != nullptr)
469+
{
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);
473+
}
474+
475+
if (layoutShortName != nullptr)
476+
{
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);
480+
}
481+
482+
SetName(newName, newShortName);
483+
}
484+
else
485+
{
486+
SetName(layoutName, layoutShortName);
487+
}
488+
}
489+
#endif // DEBUG
490+
429491
//------------------------------------------------------------------------
430492
// Create: Create a ClassLayout from an EE side class handle.
431493
//
@@ -646,8 +708,8 @@ const SegmentList& ClassLayout::GetNonPadding(Compiler* comp)
646708
// AreCompatible: check if 2 layouts are the same for copying.
647709
//
648710
// Arguments:
649-
// layout1 - the first layout;
650-
// layout2 - the second layout.
711+
// layout1 - the first layout (copy destination)
712+
// layout2 - the second layout (copy source)
651713
//
652714
// Return value:
653715
// true if compatible, false otherwise.
@@ -658,6 +720,8 @@ const SegmentList& ClassLayout::GetNonPadding(Compiler* comp)
658720
//
659721
// This is an equivalence relation:
660722
// AreCompatible(a, b) == AreCompatible(b, a)
723+
// AreCompatible(a, a) == true
724+
// AreCompatible(a, b) && AreCompatible(b, c) ==> AreCompatible(a, c)
661725
//
662726
// static
663727
bool ClassLayout::AreCompatible(const ClassLayout* layout1, const ClassLayout* layout2)
@@ -746,9 +810,92 @@ bool ClassLayout::AreCompatible(const ClassLayout* layout1, const ClassLayout* l
746810
//
747811
bool ClassLayout::CanAssignFrom(const ClassLayout* layout)
748812
{
749-
// Currently this is the same as compatability
813+
if (this == layout)
814+
{
815+
return true;
816+
}
817+
818+
// Do the normal compatibility check first, when possible to do so.
819+
//
820+
if ((IsCustomLayout() == layout->IsCustomLayout()) || (!HasGCPtr() && !layout->HasGCPtr()))
821+
{
822+
const bool areCompatible = AreCompatible(this, layout);
823+
824+
if (areCompatible)
825+
{
826+
return true;
827+
}
828+
}
829+
830+
// Must be same size
831+
//
832+
if (GetSize() != layout->GetSize())
833+
{
834+
return false;
835+
}
836+
837+
// Must be same IR type
838+
//
839+
if (GetType() != layout->GetType())
840+
{
841+
return false;
842+
}
843+
844+
// Dest is GC, source is GC. Allow, slotwise:
750845
//
751-
return AreCompatible(this, layout);
846+
// byref <- ref, byref, nint
847+
// ref <- ref
848+
// nint <- nint
849+
//
850+
if (HasGCPtr() && layout->HasGCPtr())
851+
{
852+
const unsigned slotsCount = GetSlotCount();
853+
assert(slotsCount == layout->GetSlotCount());
854+
855+
for (unsigned i = 0; i < slotsCount; ++i)
856+
{
857+
var_types slotType = GetGCPtrType(i);
858+
var_types layoutSlotType = layout->GetGCPtrType(i);
859+
860+
if ((slotType != TYP_BYREF) && (slotType != layoutSlotType))
861+
{
862+
return false;
863+
}
864+
}
865+
return true;
866+
}
867+
868+
// Dest is GC, source is noGC. Allow, slotwise:
869+
//
870+
// byref <- nint
871+
// nint <- nint
872+
//
873+
if (HasGCPtr() && !layout->HasGCPtr())
874+
{
875+
const unsigned slotsCount = GetSlotCount();
876+
877+
for (unsigned i = 0; i < slotsCount; ++i)
878+
{
879+
var_types slotType = GetGCPtrType(i);
880+
if (slotType == TYP_REF)
881+
{
882+
return false;
883+
}
884+
}
885+
return true;
886+
}
887+
888+
// Dest is noGC, source is GC. Disallow.
889+
//
890+
if (!HasGCPtr() && layout->HasGCPtr())
891+
{
892+
assert(!HasGCPtr());
893+
return false;
894+
}
895+
896+
// Dest is noGC, source is noGC, and they're not compatible.
897+
//
898+
return false;
752899
}
753900

754901
//------------------------------------------------------------------------
@@ -814,7 +961,7 @@ ClassLayoutBuilder ClassLayoutBuilder::BuildArray(Compiler* compiler, CORINFO_CL
814961
unsigned offset = OFFSETOF__CORINFO_Array__data;
815962
for (unsigned i = 0; i < length; i++)
816963
{
817-
builder.CopyInfoFrom(offset, elementLayout, /* copy padding */ false);
964+
builder.CopyGCInfoFrom(offset, elementLayout);
818965
offset += elementSize;
819966
}
820967
}
@@ -919,14 +1066,13 @@ void ClassLayoutBuilder::SetGCPtrType(unsigned slot, var_types type)
9191066
}
9201067

9211068
//------------------------------------------------------------------------
922-
// CopyInfoFrom: Copy GC pointers and padding information from another layout.
1069+
// CopyInfoGCFrom: Copy GC pointers from another layout.
9231070
//
9241071
// Arguments:
9251072
// offset - Offset in this builder to start copy information into.
9261073
// layout - Layout to get information from.
927-
// copyPadding - Whether padding info should also be copied from the layout.
9281074
//
929-
void ClassLayoutBuilder::CopyInfoFrom(unsigned offset, ClassLayout* layout, bool copyPadding)
1075+
void ClassLayoutBuilder::CopyGCInfoFrom(unsigned offset, ClassLayout* layout)
9301076
{
9311077
assert(offset + layout->GetSize() <= m_size);
9321078

@@ -939,18 +1085,53 @@ void ClassLayoutBuilder::CopyInfoFrom(unsigned offset, ClassLayout* layout, bool
9391085
SetGCPtr(startSlot + slot, layout->GetGCPtr(slot));
9401086
}
9411087
}
1088+
}
9421089

943-
if (copyPadding)
944-
{
945-
AddPadding(SegmentList::Segment(offset, offset + layout->GetSize()));
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);
9461101

947-
for (const SegmentList::Segment& nonPadding : layout->GetNonPadding(m_compiler))
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++)
9481107
{
949-
RemovePadding(SegmentList::Segment(offset + nonPadding.Start, offset + nonPadding.End));
1108+
CorInfoGCType gcType = layout->GetGCPtr(slot);
1109+
if (gcType == TYPE_GC_REF)
1110+
{
1111+
gcType = TYPE_GC_BYREF;
1112+
}
1113+
SetGCPtr(startSlot + slot, gcType);
9501114
}
9511115
}
9521116
}
9531117

1118+
//------------------------------------------------------------------------
1119+
// CopyInfoPaddingFrom: Copy padding from another layout.
1120+
//
1121+
// Arguments:
1122+
// offset - Offset in this builder to start copy information into.
1123+
// layout - Layout to get information from.
1124+
//
1125+
void ClassLayoutBuilder::CopyPaddingFrom(unsigned offset, ClassLayout* layout)
1126+
{
1127+
AddPadding(SegmentList::Segment(offset, offset + layout->GetSize()));
1128+
1129+
for (const SegmentList::Segment& nonPadding : layout->GetNonPadding(m_compiler))
1130+
{
1131+
RemovePadding(SegmentList::Segment(offset + nonPadding.Start, offset + nonPadding.End));
1132+
}
1133+
}
1134+
9541135
//------------------------------------------------------------------------
9551136
// GetOrCreateNonPadding: Get the non padding segment list, or create it if it
9561137
// does not exist.

src/coreclr/jit/layout.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,15 @@ class ClassLayoutBuilder
3434
ClassLayoutBuilder(Compiler* compiler, unsigned size);
3535

3636
void SetGCPtrType(unsigned slot, var_types type);
37-
void CopyInfoFrom(unsigned offset, ClassLayout* layout, bool copyPadding);
37+
void CopyGCInfoFrom(unsigned offset, ClassLayout* layout);
38+
void CopyGCInfoFromMakeByref(unsigned offset, ClassLayout* layout);
39+
void CopyPaddingFrom(unsigned offset, ClassLayout* layout);
3840
void AddPadding(const SegmentList::Segment& padding);
3941
void RemovePadding(const SegmentList::Segment& nonPadding);
4042

4143
#ifdef DEBUG
4244
void SetName(const char* name, const char* shortName);
45+
void CopyNameFrom(ClassLayout* layout, const char* prefix);
4346
#endif
4447

4548
static ClassLayoutBuilder BuildArray(Compiler* compiler, CORINFO_CLASS_HANDLE arrayType, unsigned length);

0 commit comments

Comments
 (0)