-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Avoid VN bugs with struct reinterpretation. #57076
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 |
---|---|---|
|
@@ -39,6 +39,10 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor> | |
unsigned m_lclNum; | ||
unsigned m_offset; | ||
bool m_address; | ||
bool m_excludeFromVN; // This value should not participate in VN optimizations, | ||
// for example, because it represents a reinterpreted struct. | ||
// When we transform it we should set `DontVN()` for `LclVar` | ||
// and `NotAField` for `LclFld`. | ||
INDEBUG(bool m_consumed;) | ||
|
||
public: | ||
|
@@ -49,6 +53,7 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor> | |
, m_lclNum(BAD_VAR_NUM) | ||
, m_offset(0) | ||
, m_address(false) | ||
, m_excludeFromVN(false) | ||
#ifdef DEBUG | ||
, m_consumed(false) | ||
#endif // DEBUG | ||
|
@@ -112,6 +117,10 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor> | |
assert(!IsLocation() && !IsAddress()); | ||
|
||
m_lclNum = lclVar->GetLclNum(); | ||
if (lclVar->DontVN()) | ||
{ | ||
m_excludeFromVN = true; | ||
} | ||
|
||
assert(m_offset == 0); | ||
assert(m_fieldSeq == nullptr); | ||
|
@@ -195,10 +204,11 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor> | |
|
||
if (val.IsLocation()) | ||
{ | ||
m_address = true; | ||
m_lclNum = val.m_lclNum; | ||
m_offset = val.m_offset; | ||
m_fieldSeq = val.m_fieldSeq; | ||
m_address = true; | ||
m_lclNum = val.m_lclNum; | ||
m_offset = val.m_offset; | ||
m_fieldSeq = val.m_fieldSeq; | ||
m_excludeFromVN = val.m_excludeFromVN; | ||
} | ||
|
||
INDEBUG(val.Consume();) | ||
|
@@ -246,7 +256,7 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor> | |
m_lclNum = val.m_lclNum; | ||
m_offset = newOffset.Value(); | ||
|
||
if (field->gtFldMayOverlap) | ||
if (field->gtFldMayOverlap || val.m_excludeFromVN) | ||
{ | ||
m_fieldSeq = FieldSeqStore::NotAField(); | ||
} | ||
|
@@ -287,9 +297,10 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor> | |
|
||
if (val.IsAddress()) | ||
{ | ||
m_lclNum = val.m_lclNum; | ||
m_offset = val.m_offset; | ||
m_fieldSeq = val.m_fieldSeq; | ||
m_lclNum = val.m_lclNum; | ||
m_offset = val.m_offset; | ||
m_fieldSeq = val.m_fieldSeq; | ||
m_excludeFromVN = val.m_excludeFromVN; | ||
} | ||
|
||
INDEBUG(val.Consume();) | ||
|
@@ -942,6 +953,8 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor> | |
ClassLayout* structLayout = nullptr; | ||
FieldSeqNode* fieldSeq = val.FieldSeq(); | ||
|
||
bool cantDoVNOnTHisTree = false; | ||
|
||
if ((fieldSeq != nullptr) && (fieldSeq != FieldSeqStore::NotAField())) | ||
{ | ||
// TODO-ADDR: ObjectAllocator produces FIELD nodes with FirstElemPseudoField as field | ||
|
@@ -1013,19 +1026,28 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor> | |
structLayout = indir->AsBlk()->GetLayout(); | ||
} | ||
|
||
// We're not going to produce a TYP_STRUCT LCL_FLD so we don't need the field sequence. | ||
fieldSeq = nullptr; | ||
if (fieldSeq != nullptr) | ||
{ | ||
// We're not going to produce a TYP_STRUCT LCL_FLD so we don't need the field sequence. | ||
fieldSeq = nullptr; | ||
JITDUMP("Dropped field seq from [%06u]\n", m_compiler->dspTreeID(indir)); | ||
} | ||
} | ||
|
||
// We're only processing TYP_STRUCT variables now so the layout should never be null, | ||
// otherwise the below layout equality check would be insufficient. | ||
assert(varDsc->GetLayout() != nullptr); | ||
|
||
if ((val.Offset() == 0) && (structLayout != nullptr) && | ||
if ((val.Offset() == 0) && (structLayout != nullptr) && (fieldSeq == nullptr) && | ||
ClassLayout::AreCompatible(structLayout, varDsc->GetLayout())) | ||
{ | ||
indir->ChangeOper(GT_LCL_VAR); | ||
indir->AsLclVar()->SetLclNum(val.LclNum()); | ||
|
||
if (structLayout->GetClassHandle() != varDsc->GetLayout()->GetClassHandle()) | ||
{ | ||
cantDoVNOnTHisTree = true; | ||
} | ||
} | ||
else if (!varTypeIsStruct(indir->TypeGet())) | ||
{ | ||
|
@@ -1065,6 +1087,14 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor> | |
|
||
indir->gtFlags = flags; | ||
|
||
if (cantDoVNOnTHisTree) | ||
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. Add a comment here as well |
||
{ | ||
assert(indir->OperIs(GT_LCL_VAR)); | ||
indir->AsLclVar()->SetDontVN(); | ||
JITDUMP("Exclude local var tree [%06u] from VN because because of struct reinterpretation\n", | ||
m_compiler->dspTreeID(indir)); | ||
} | ||
|
||
INDEBUG(m_stmtModified = true;) | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -7172,7 +7172,10 @@ void Compiler::fgValueNumberBlockAssignment(GenTree* tree) | |||
rhsVarDsc = &lvaTable[rhsLclNum]; | ||||
if (!lvaInSsa(rhsLclNum) || rhsFldSeq == FieldSeqStore::NotAField()) | ||||
{ | ||||
rhsVNPair.SetBoth(vnStore->VNForExpr(compCurBB, rhsLclVarTree->TypeGet())); | ||||
isNewUniq = true; | ||||
} | ||||
else if (rhsLclVarTree->OperIs(GT_LCL_VAR) && rhsLclVarTree->AsLclVar()->DontVN()) | ||||
{ | ||||
isNewUniq = true; | ||||
} | ||||
else | ||||
|
@@ -7187,7 +7190,6 @@ void Compiler::fgValueNumberBlockAssignment(GenTree* tree) | |||
} | ||||
else | ||||
{ | ||||
rhsVNPair.SetBoth(vnStore->VNForExpr(compCurBB, rhs->TypeGet())); | ||||
isNewUniq = true; | ||||
} | ||||
} | ||||
|
@@ -7272,6 +7274,11 @@ void Compiler::fgValueNumberBlockAssignment(GenTree* tree) | |||
JITDUMP(" *** Missing field sequence info for Dst/LHS of COPYBLK\n"); | ||||
isNewUniq = true; | ||||
} | ||||
else if (lclVarTree->OperIs(GT_LCL_VAR) && lclVarTree->AsLclVar()->DontVN()) | ||||
{ | ||||
JITDUMP(" ***Struct reinterpretation on rhs of COPYBLK\n"); | ||||
isNewUniq = true; | ||||
} | ||||
|
||||
if (isNewUniq) | ||||
{ | ||||
|
@@ -7399,9 +7406,9 @@ void Compiler::fgValueNumberTree(GenTree* tree) | |||
|
||||
case GT_LCL_VAR: | ||||
{ | ||||
GenTreeLclVarCommon* lcl = tree->AsLclVarCommon(); | ||||
unsigned lclNum = lcl->GetLclNum(); | ||||
LclVarDsc* varDsc = &lvaTable[lclNum]; | ||||
GenTreeLclVar* lcl = tree->AsLclVar(); | ||||
unsigned lclNum = lcl->GetLclNum(); | ||||
LclVarDsc* varDsc = &lvaTable[lclNum]; | ||||
|
||||
if (varDsc->CanBeReplacedWithItsField(this)) | ||||
{ | ||||
|
@@ -7457,7 +7464,11 @@ void Compiler::fgValueNumberTree(GenTree* tree) | |||
unsigned typSize = genTypeSize(genActualType(typ)); | ||||
unsigned varSize = genTypeSize(genActualType(varDsc->TypeGet())); | ||||
|
||||
if (typSize == varSize) | ||||
if (lcl->DontVN()) | ||||
{ | ||||
generateUniqueVN = true; | ||||
} | ||||
else if (typSize == varSize) | ||||
{ | ||||
lcl->gtVNPair = wholeLclVarVNP; | ||||
} | ||||
|
@@ -7585,7 +7596,16 @@ void Compiler::fgValueNumberTree(GenTree* tree) | |||
else | ||||
{ | ||||
ValueNumPair lclVNPair = varDsc->GetPerSsaData(ssaNum)->m_vnPair; | ||||
tree->gtVNPair = vnStore->VNPairApplySelectors(lclVNPair, lclFld->GetFieldSeq(), indType); | ||||
if (lclVNPair.GetLiberal() == ValueNumStore::NoVN) | ||||
{ | ||||
// We didn't not assign a correct VN to the local, probably it was written using a different | ||||
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. @briansull I am trying to pass the ci, could you please help me with it? The failures look like https://dev.azure.com/dnceng/public/_build/results?buildId=1289712&view=ms.vss-test-web.build-test-results-tab&runId=38073760&paneView=dotnet-dnceng.dnceng-build-release-tasks.helix-test-information-tab&resultId=107788
if both are true why do we hit these asserts and what is the best way to fix it? Should I add similar checks 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 am trying to understand your issue. 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. Generally it is an error to have read with a ValueNumber of NoVN Check if line 7500 (above) applies here:
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. Yes, but I don't understand where here do we update 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. @sandreenko 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. Perhaps you need to mark the local variable as unsafe to ValueNumber 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 fix would be to set
when processing
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. If I understand correctly VN that we print on LHS of an assignment does not matter at all, the real value is saved in runtime/src/coreclr/jit/valuenum.cpp Line 7549 in 7b0a27e
but in our case it is a Def, because it is a use:
but, obviously, we don't care about its value, we won't read it anywhere. What we care about is value of 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. What you are saying above does sound correct to me. I haven't made many (or any) changes in this area SSA and VN, so it is also my understanding from reading the code. 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. But with this approach you will have to check for this value for every read (use) site, because the mismatch can occur either at the def or the use. In the above case we have a mismatch on the def site, which I guess already does write a NoVN, but later at the use site we pull out the NoVN and try to use it in VNPairApplySelectors and the use site won't have the |
||||
// type. | ||||
tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, indType)); | ||||
} | ||||
else | ||||
{ | ||||
tree->gtVNPair = vnStore->VNPairApplySelectors(lclVNPair, lclFld->GetFieldSeq(), indType); | ||||
} | ||||
} | ||||
} | ||||
} | ||||
|
@@ -7782,8 +7802,8 @@ void Compiler::fgValueNumberTree(GenTree* tree) | |||
{ | ||||
case GT_LCL_VAR: | ||||
{ | ||||
GenTreeLclVarCommon* lcl = lhs->AsLclVarCommon(); | ||||
unsigned lclDefSsaNum = GetSsaNumForLocalVarDef(lcl); | ||||
GenTreeLclVar* lcl = lhs->AsLclVar(); | ||||
unsigned lclDefSsaNum = GetSsaNumForLocalVarDef(lcl); | ||||
|
||||
// Should not have been recorded as updating the GC heap. | ||||
assert(!GetMemorySsaMap(GcHeap)->Lookup(tree, &memorySsaNum)); | ||||
|
@@ -7795,6 +7815,18 @@ void Compiler::fgValueNumberTree(GenTree* tree) | |||
|
||||
assert(rhsVNPair.GetLiberal() != ValueNumStore::NoVN); | ||||
|
||||
ValueNumPair lhsVNPair; | ||||
|
||||
if (!lcl->DontVN()) | ||||
{ | ||||
lhsVNPair = rhsVNPair; | ||||
} | ||||
else | ||||
{ | ||||
ValueNum uniqVN = vnStore->VNForExpr(compCurBB, lcl->TypeGet()); | ||||
lhsVNPair.SetBoth(uniqVN); | ||||
} | ||||
|
||||
Comment on lines
+7818
to
+7829
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 appears 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. interesting, thanks for the catch. I need to investigate why it fixes one of the test cases that I was targeting with this change. |
||||
lhs->gtVNPair = rhsVNPair; | ||||
lvaTable[lcl->GetLclNum()].GetPerSsaData(lclDefSsaNum)->m_vnPair = rhsVNPair; | ||||
|
||||
|
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.
Can you add a comment here, about the Class handle mismatch, and how/why we handle it