-
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
Fix VN incorrect optimizations with a new JitEEInterface function. #57282
Conversation
0f34021
to
397825b
Compare
/azp run runtime-coreclr libraries-jitstress, runtime-coreclr jitstress, runtime-coreclr outerloop |
Azure Pipelines successfully started running 3 pipeline(s). |
src/coreclr/jit/valuenum.cpp
Outdated
@@ -8006,10 +8006,11 @@ void Compiler::fgValueNumberTree(GenTree* tree) | |||
rhsVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lclVarTree->TypeGet())); | |||
} | |||
|
|||
lclDefSsaNum = GetSsaNumForLocalVarDef(lclVarTree); |
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.
For a tree like:
N005 ( 11, 10) [001400] -A------R---- * ASG long
N004 ( 6, 5) [001401] *------N----- +--* IND long
N003 ( 3, 3) [001402] ------------- | \--* ADDR byref Zero Fseq[_00]
N002 ( 3, 2) [001403] U------N----- | \--* LCL_VAR struct<System.Runtime.Intrinsics.Vector128`1[Byte], 16> V45 tmp38 ud:3->4
N001 ( 1, 1) [001404] ------------- \--* LCL_VAR long V69 tmp62 u:2 (last use)
SSA was working correctly and printing that 001403
is using SSA-3 and defining SSA-4. However, this code, for some reason, was writing the result as a define for SSA-3.
So when at the next tree we were asking for GetPerSsaData(4)
we were not getting an initialized value.
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.
Maybe this comes up now because we no longer set lvOverlappingFields
in the importer?
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.
It looks like this could be moved to come before line 8051, as that is the first use of this value,
src/coreclr/jit/valuenum.cpp
Outdated
@@ -7273,6 +7269,29 @@ void Compiler::fgValueNumberBlockAssignment(GenTree* tree) | |||
isNewUniq = true; | |||
} | |||
|
|||
if (!isNewUniq && (rhsVarDsc != nullptr) && (lhsVarDsc != nullptr)) |
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.
the check isNewUniq
is weak here because it is possible that we have already generated a unique VN for rhs but did not report it here.
Because of that we can't narrow the cases that go inside this block to assert(ClassLayout::AreCompatible(rhsVarDsc->GetLayout(), lhsVarDsc->GetLayout()))
.
However, all cases that pass all these "if" should have unique VN, we just ask for it twice for some trees.
PTAL @AndyAyersMS @briansull @dotnet/jit-contrib |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
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.
Overall this looks to be in good shape.
Left you a few notes.
src/coreclr/jit/valuenum.cpp
Outdated
@@ -8006,10 +8006,11 @@ void Compiler::fgValueNumberTree(GenTree* tree) | |||
rhsVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lclVarTree->TypeGet())); | |||
} | |||
|
|||
lclDefSsaNum = GetSsaNumForLocalVarDef(lclVarTree); |
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.
Maybe this comes up now because we no longer set lvOverlappingFields
in the importer?
src/coreclr/jit/valuenum.cpp
Outdated
} | ||
else | ||
{ | ||
CORINFO_FIELD_HANDLE fldHnd = rhsFldSeq->GetTail()->GetFieldHandle(); |
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.
Presumably we've weeded out the NotAField
cases above?
Maybe assert for it here, as calling getFieldClass
on a null handle will cause an AV.
src/coreclr/jit/valuenum.cpp
Outdated
} | ||
else | ||
{ | ||
CORINFO_FIELD_HANDLE fldHnd = lhsFldSeq->GetTail()->GetFieldHandle(); |
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.
ditto here
src/coreclr/jit/valuenum.cpp
Outdated
else | ||
{ | ||
CORINFO_FIELD_HANDLE fldHnd = rhsFldSeq->GetTail()->GetFieldHandle(); | ||
rhsStructHnd = info.compCompHnd->getFieldClass(fldHnd); |
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.
There's a bit of a divergence between doesFieldBelongToClass
and getFieldClass
-- otherwise you would not have needed this new interface method. Can that cause problems here?
// and logical field pair then return true. This is needed as the field handle here | ||
// is used as a key into a hashtable mapping writes to fields to value numbers. | ||
// | ||
// In this implmentation this is made more complex as the JIT is exposed to CORINFO_FIELD_STRUCT |
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.
Maybe make it clearer that the crossgen2 behavior is different than the runtime behavior?
Is this something we should fix down the road? Seems like it would make us more conservative in places when doing R2R codegen.
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.
Maybe make it clearer that the crossgen2 behavior is different than the runtime behavior?
I will add a comment.
Is this something we should fix down the road? Seems like it would make us more conservative in places when doing R2R codegen.
I hope we will target to get rid of FldSeq and its dependency on FIELD_HANDLE and STRUCT_HANDLE.
If not we can do:
if (crossgen2)
{
canonFieldHandle = JitEEInterface::getCanonField(fieldHandle);
bool doesBelong = JitEEInterface::doesFieldBelongToClass(canonFieldHandle , clsHnd);
}
but I don't like such a solution, it hammers JIT to VM behavior, we can do it always, not only for crossgen2, but then it is an unnecessary VM call.
src/coreclr/jit/lclmorph.cpp
Outdated
|
||
haveCorrectFieldForVN = | ||
compiler->info.compCompHnd->doesFieldBelongToClass(field->gtFldHnd, clsHnd); | ||
assert(haveCorrectFieldForVN); |
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.
If we're going to assert like this (as opposed to say noway
which might be appropriate here, if we think the only ways we can get confused about fields is when optimizing) then you might as well only call the interface method under DEBUG.
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.
I thought that when using UnsafeAs
to change the struct type to a different struct type, we would get a false back from doesFieldBelongToClass
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.
changed to a noway assert.
I thought that when using UnsafeAs to change the struct type to a different struct type, we would get a false back from doesFieldBelongToClass
yes, but they have to stay in the same tree without spilling, so something like:
struct1 s1 = new S1();
Unsafe.As<s1, s2>(ref s1).fieldOfS2 = 10;
if this is imported as one field:
FIELD fieldOfS2
\-- OBJ(struct2)
\-- ADDR byref
\--LCL_VAR struct1
here we should catch it and transform as
LCL_FLD struct1 (NotAField)
instead of
LCL_FLD struct1 (fieldOfS2)
but I could not create a test where we keep the source as 1 tree so I added an assert that it is unreachable for now.
src/coreclr/jit/lclmorph.cpp
Outdated
|
||
haveCorrectFieldForVN = | ||
compiler->info.compCompHnd->doesFieldBelongToClass(field->gtFldHnd, clsHnd); | ||
assert(haveCorrectFieldForVN); |
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.
I thought that when using UnsafeAs
to change the struct type to a different struct type, we would get a false back from doesFieldBelongToClass
src/coreclr/jit/valuenum.cpp
Outdated
@@ -8006,10 +8006,11 @@ void Compiler::fgValueNumberTree(GenTree* tree) | |||
rhsVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lclVarTree->TypeGet())); | |||
} | |||
|
|||
lclDefSsaNum = GetSsaNumForLocalVarDef(lclVarTree); |
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.
It looks like this could be moved to come before line 8051, as that is the first use of this value,
1a8c70a
to
cf38484
Compare
/azp run runtime-coreclr libraries-jitstress, runtime-coreclr jitstress, runtime-coreclr outerloop |
Azure Pipelines successfully started running 3 pipeline(s). |
/azp run runtime-coreclr libraries-jitstress, runtime-coreclr jitstress, runtime-coreclr outerloop |
Azure Pipelines successfully started running 3 pipeline(s). |
with a naive implementation so far.
… type case in the managed compiler
It appeared that we have many trees where we generate unique VN for rhs and don't inform `fgValueNumberBlockAssignment` about it. For example, for ``` N005 ( 10, 8) [000033] -A--G---R--- * ASG struct (copy) N004 ( 3, 2) [000031] D------N---- +--* LCL_VAR struct<eightByteStruct, 8> V01 loc1 d:2 N003 ( 6, 5) [000030] n---G------- \--* IND struct N002 ( 3, 3) [000043] ------------ \--* ADDR byref N001 ( 3, 2) [000044] -------N---- \--* LCL_VAR struct<largeStruct, 32> V00 loc0 u:6 (last use) ``` we will generate unique rhs but then `fgValueNumberBlockAssignment` will still try to work it as with a non-unique one.
For a tree like: ``` N005 ( 11, 10) [001400] -A------R---- * ASG long N004 ( 6, 5) [001401] *------N----- +--* IND long N003 ( 3, 3) [001402] ------------- | \--* ADDR byref Zero Fseq[_00] N002 ( 3, 2) [001403] U------N----- | \--* LCL_VAR struct<System.Runtime.Intrinsics.Vector128`1[Byte], 16> V45 tmp38 ud:3->4 N001 ( 1, 1) [001404] ------------- \--* LCL_VAR long V69 tmp62 u:2 (last use) ``` SSA was working correctly and printing that `001403` is using SSA-3 and defining SSA-4. However, this code, for some reason, was writing result as a define for SSA-3.
0cffe6a
to
e438e3d
Compare
/azp run runtime-coreclr libraries-jitstress, runtime-coreclr jitstress, runtime-coreclr outerloop |
Azure Pipelines successfully started running 3 pipeline(s). |
The only failure is |
/backport to release/6.0-rc1 |
Started backporting to release/6.0-rc1: https://github.com/dotnet/runtime/actions/runs/1162100281 |
This is an alternative for #57076 that gets VM help to avoid non-unique fieldSeq in VN maps.
The main advantage of this approach is that we keep ValueNumbering changes small (and I personally can't be confident in any VN changes) and we don't change other phases to carry any information that was not there. Instead, we ask JitEEInterface where we need to restore field->struct information.
Thanks to @davidwrighton for the VM part of this change.
No pmi diffs, can't do SPMI because the GUID has changed. Will post crossgen2 diffs later, we expect small regressions there.
Fixes #42517, fixes #49954, fixes #54102, fixes #56980.
#57076 (comment) has a detailed explanation of the issue, but it is probably faster to look at the test cases added in this PR.