-
Notifications
You must be signed in to change notification settings - Fork 5k
JIT: enhance escape analysis to understand local struct fields #113141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -414,18 +414,80 @@ ClassLayout* Compiler::typGetBlkLayout(unsigned blockSize) | |
return typGetCustomLayout(ClassLayoutBuilder(this, blockSize)); | ||
} | ||
|
||
unsigned Compiler::typGetArrayLayoutNum(CORINFO_CLASS_HANDLE classHandle, unsigned length) | ||
ClassLayout* Compiler::typGetArrayLayout(CORINFO_CLASS_HANDLE classHandle, unsigned length) | ||
{ | ||
ClassLayoutBuilder b = ClassLayoutBuilder::BuildArray(this, classHandle, length); | ||
return typGetCustomLayoutNum(b); | ||
return typGetCustomLayout(b); | ||
} | ||
|
||
ClassLayout* Compiler::typGetArrayLayout(CORINFO_CLASS_HANDLE classHandle, unsigned length) | ||
ClassLayout* Compiler::typGetNonGCLayout(ClassLayout* layout) | ||
{ | ||
ClassLayoutBuilder b = ClassLayoutBuilder::BuildArray(this, classHandle, length); | ||
assert(layout->HasGCPtr()); | ||
ClassLayoutBuilder b(this, layout->GetSize()); | ||
b.CopyPaddingFrom(0, layout); | ||
|
||
#ifdef DEBUG | ||
b.CopyNameFrom(layout, "[nongc] "); | ||
#endif | ||
|
||
return typGetCustomLayout(b); | ||
} | ||
|
||
ClassLayout* Compiler::typGetByrefLayout(ClassLayout* layout) | ||
{ | ||
assert(layout->HasGCPtr()); | ||
ClassLayoutBuilder b(this, layout->GetSize()); | ||
b.CopyPaddingFrom(0, layout); | ||
b.CopyGCInfoFromMakeByref(0, layout); | ||
|
||
#ifdef DEBUG | ||
b.CopyNameFrom(layout, "[byref] "); | ||
#endif | ||
|
||
return typGetCustomLayout(b); | ||
} | ||
|
||
#ifdef DEBUG | ||
//------------------------------------------------------------------------ | ||
// CopyNameFrom: Copy layout names, with optional prefix. | ||
// | ||
// Parameters: | ||
// layout - layout to copy from | ||
// prefix - prefix to add (or nullptr) | ||
// | ||
void ClassLayoutBuilder::CopyNameFrom(ClassLayout* layout, const char* prefix) | ||
{ | ||
const char* layoutName = layout->GetClassName(); | ||
const char* layoutShortName = layout->GetShortClassName(); | ||
|
||
if (prefix != nullptr) | ||
{ | ||
char* newName = nullptr; | ||
char* newShortName = nullptr; | ||
|
||
if (layoutName != nullptr) | ||
{ | ||
size_t len = strlen(prefix) + strlen(layoutName) + 1; | ||
newName = m_compiler->getAllocator(CMK_DebugOnly).allocate<char>(len); | ||
sprintf_s(newName, len, "%s%s", prefix, layoutShortName); | ||
} | ||
|
||
if (layoutShortName != nullptr) | ||
{ | ||
size_t len = strlen(prefix) + strlen(layoutName) + 1; | ||
newShortName = m_compiler->getAllocator(CMK_DebugOnly).allocate<char>(len); | ||
sprintf_s(newShortName, len, "%s%s", prefix, layoutShortName); | ||
} | ||
|
||
SetName(newName, newShortName); | ||
} | ||
else | ||
{ | ||
SetName(layoutName, layoutShortName); | ||
} | ||
Comment on lines
+463
to
+487
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can use |
||
} | ||
#endif // DEBUG | ||
|
||
//------------------------------------------------------------------------ | ||
// Create: Create a ClassLayout from an EE side class handle. | ||
// | ||
|
@@ -646,8 +708,8 @@ const SegmentList& ClassLayout::GetNonPadding(Compiler* comp) | |
// AreCompatible: check if 2 layouts are the same for copying. | ||
// | ||
// Arguments: | ||
// layout1 - the first layout; | ||
// layout2 - the second layout. | ||
// layout1 - the first layout (copy destination) | ||
// layout2 - the second layout (copy source) | ||
// | ||
// Return value: | ||
// true if compatible, false otherwise. | ||
|
@@ -706,8 +768,8 @@ bool ClassLayout::AreCompatible(const ClassLayout* layout1, const ClassLayout* l | |
return true; | ||
} | ||
|
||
assert(clsHnd1 != NO_CLASS_HANDLE); | ||
assert(clsHnd2 != NO_CLASS_HANDLE); | ||
// assert(clsHnd1 != NO_CLASS_HANDLE); | ||
// assert(clsHnd2 != NO_CLASS_HANDLE); | ||
assert(layout1->HasGCPtr() && layout2->HasGCPtr()); | ||
|
||
if (layout1->GetGCPtrCount() != layout2->GetGCPtrCount()) | ||
|
@@ -722,6 +784,13 @@ bool ClassLayout::AreCompatible(const ClassLayout* layout1, const ClassLayout* l | |
{ | ||
if (layout1->GetGCPtrType(i) != layout2->GetGCPtrType(i)) | ||
{ | ||
// Allow a source GC_REF to match a custom GC_BYREF | ||
// | ||
if ((layout2->GetGCPtrType(i) == TYP_REF) && (layout1->GetGCPtrType(i) == TYP_BYREF) && | ||
layout1->IsCustomLayout()) | ||
{ | ||
continue; | ||
} | ||
Comment on lines
+787
to
+793
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changing the definition of layout compatibility seems quite questionable to me. Why is it necessary? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was tempted to retain the class handle in new layout, but didn't. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note allowing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So are you saying that for the object aObj = ...; // doesn't escape
object bObj = ...; // escapes
aObj = bObj; case, today we will produce a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would just suggest to rename this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another idea here is to leverage the fact that these new layouts are based on existing layouts, and remember the "inspiring" layout. Then if two layouts have the same inspiring layout, they are compatible, provided we're not assigning a gc layout to a non-gc layout (which should indicate an error in escape analysis). Also tempted to make this an instance method so it's easier to remember which layout is which, eg There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it would be odd to me if a layout inspired by another layout would compare differently than another, equal layout that was not inspired. That would break transitivity properties you would expect to hold. I am fine with introducing a notion of assignment compatibility given that we allow the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, let me split out the existing uses in a separate PR. I need to look at what escape analysis will do for struct retyping that preserves GCness to see if we ever end up with a case like
and make sure we know to retype |
||
return false; | ||
} | ||
} | ||
|
@@ -791,7 +860,7 @@ ClassLayoutBuilder ClassLayoutBuilder::BuildArray(Compiler* compiler, CORINFO_CL | |
unsigned offset = OFFSETOF__CORINFO_Array__data; | ||
for (unsigned i = 0; i < length; i++) | ||
{ | ||
builder.CopyInfoFrom(offset, elementLayout, /* copy padding */ false); | ||
builder.CopyGCInfoFrom(offset, elementLayout); | ||
offset += elementSize; | ||
} | ||
} | ||
|
@@ -896,14 +965,13 @@ void ClassLayoutBuilder::SetGCPtrType(unsigned slot, var_types type) | |
} | ||
|
||
//------------------------------------------------------------------------ | ||
// CopyInfoFrom: Copy GC pointers and padding information from another layout. | ||
// CopyInfoGCFrom: Copy GC pointers from another layout. | ||
// | ||
// Arguments: | ||
// offset - Offset in this builder to start copy information into. | ||
// layout - Layout to get information from. | ||
// copyPadding - Whether padding info should also be copied from the layout. | ||
// | ||
void ClassLayoutBuilder::CopyInfoFrom(unsigned offset, ClassLayout* layout, bool copyPadding) | ||
void ClassLayoutBuilder::CopyGCInfoFrom(unsigned offset, ClassLayout* layout) | ||
{ | ||
assert(offset + layout->GetSize() <= m_size); | ||
|
||
|
@@ -916,18 +984,53 @@ void ClassLayoutBuilder::CopyInfoFrom(unsigned offset, ClassLayout* layout, bool | |
SetGCPtr(startSlot + slot, layout->GetGCPtr(slot)); | ||
} | ||
} | ||
} | ||
|
||
if (copyPadding) | ||
{ | ||
AddPadding(SegmentList::Segment(offset, offset + layout->GetSize())); | ||
//------------------------------------------------------------------------ | ||
// CopyInfoGCFromMakeByref: Copy GC pointers from another layout,and change | ||
// all gc references to be TYP_BYREF (TYPE_GC_BYREF) | ||
// | ||
// Arguments: | ||
// offset - Offset in this builder to start copy information into. | ||
// layout - Layout to get information from. | ||
// | ||
void ClassLayoutBuilder::CopyGCInfoFromMakeByref(unsigned offset, ClassLayout* layout) | ||
{ | ||
assert(offset + layout->GetSize() <= m_size); | ||
|
||
for (const SegmentList::Segment& nonPadding : layout->GetNonPadding(m_compiler)) | ||
if (layout->GetGCPtrCount() > 0) | ||
{ | ||
assert(offset % TARGET_POINTER_SIZE == 0); | ||
unsigned startSlot = offset / TARGET_POINTER_SIZE; | ||
for (unsigned slot = 0; slot < layout->GetSlotCount(); slot++) | ||
{ | ||
RemovePadding(SegmentList::Segment(offset + nonPadding.Start, offset + nonPadding.End)); | ||
CorInfoGCType gcType = layout->GetGCPtr(slot); | ||
if (gcType == TYPE_GC_REF) | ||
{ | ||
gcType = TYPE_GC_BYREF; | ||
} | ||
SetGCPtr(startSlot + slot, gcType); | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code feels very object stack allocation specific. Put it in objectalloc.cpp? |
||
|
||
//------------------------------------------------------------------------ | ||
// CopyInfoPaddingFrom: Copy padding from another layout. | ||
// | ||
// Arguments: | ||
// offset - Offset in this builder to start copy information into. | ||
// layout - Layout to get information from. | ||
// | ||
void ClassLayoutBuilder::CopyPaddingFrom(unsigned offset, ClassLayout* layout) | ||
{ | ||
AddPadding(SegmentList::Segment(offset, offset + layout->GetSize())); | ||
|
||
for (const SegmentList::Segment& nonPadding : layout->GetNonPadding(m_compiler)) | ||
{ | ||
RemovePadding(SegmentList::Segment(offset + nonPadding.Start, offset + nonPadding.End)); | ||
} | ||
} | ||
|
||
//------------------------------------------------------------------------ | ||
// GetOrCreateNonPadding: Get the non padding segment list, or create it if it | ||
// does not exist. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like something that only stack allocation is ever going to use, so it would make more sense to me to keep it in objectalloc.h/cpp.