-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Don't allow the hoisting of GT_CLS_VARs that were assigned a constant value. #26551
Conversation
@dotnet/jit-contrib PTAL |
27cef35
to
49380f9
Compare
Updated fix. @dotnet/jit-contrib PTAL |
src/jit/optimizer.cpp
Outdated
if (m_compiler->vnStore->IsVNConstant(vn)) | ||
{ | ||
// Don't allow the hoisting of GT_CLS_VAR an GT_LCL_VAR that were assigned a constant value. | ||
// But allow constant GT_ARGPLACE nodes which occur when we have constant args. |
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 might just request a bit more lengthy comment here. As I understand it, the reason we can't do this thing that seems safe, though perhaps not all that useful, is that we're not checking safety that something that's known to evaluate to a constant is not being modified within the loop. If I have that right, it would be nice to add that here so that people (like me) won't be confused in future when they see this.
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.
Is this the right place for this fix? optVNIsLoopInvariant has logic for determining whether the definition is inside the loop. That logic is not executed in this case because of this code:
Lines 7197 to 7201 in ef585a4
// We'll always short-circuit constants. | |
if (vnStore->IsVNConstant(vn) || vn == vnStore->VNForVoid()) | |
{ | |
return true; | |
} |
Perhaps that short-circuit should be removed so that we try to determine if the definition is inside the loop even for const vn's.
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.
That function examines the vn
passed in and sees it it is a VNF_PhiDef or a VNF_PhiMemoryDef.
So it can only identify the GT_LCL_VARS that have definitions that are tracked by Phi nodes.
When we have a constant vn
we can't trace back to the definition.
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.
optVNIsLoopInvariant also has logic for when the vn is not VNF_PhiDef or a VNF_PhiMemoryDef: it checks function args if vn is a function and it checks vn loop annotations if vn is not a function.
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.
That handles things like binary operators such as Add, Sub, etc..
Constant values are terminal node with no children, only a 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.
That handles things like binary operators such as Add, Sub, etc..
That part of VN invariance is actually dubious since it's redundant to what the rest of loop hoisting does.
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.
That part of VN invariance is actually dubious
One important case is handled here:
We have several "pure" Jit helper functions that take constants as arguments.
It is important that such calls get hoisted into the loop pre-header and then turned into CSE's:
Examples are CORINFO_HELP_GETSHARED_GCSTATIC_BASE
and CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE
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.
Those are already handled in HoistVisitor::PostOrderVisit
- it already checks for pure helpers with invariant args.
As far as I can tell the only good reason to check VN invariance is to detect memory loads that are invariant. Apart from that the VN invariance check is effectively redundant and confusing:
PostOrderVisit
determines thata
andb
are invariant and then trivially concludes thata + b
is also invariant.- At that point it checks VN invariance - it goes "down" the VN "tree" to check if
a + b
is invariant
Basically as PostOrderVisit
goes up the expression tree it keeps going down the VN tree again and again attempting to check what has already been checked. If it weren't for loopVnInvariantCache
this would probably rather slow.
To make things worse, VN invariance could handle the case of a variable defined inside the loop by an invariant expression. Sadly this doesn't happen because PostOrderVisit
specifically requires that the definition be outside the loop and doesn't check VN invariance if the def is inside the loop.
What are the diffs with this fix? |
d0fdc25
to
b5dd942
Compare
In System.Private.CoreLib no diffs |
No diffs in --frameworks |
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.
LGTM
Perhaps we should open an issue to address cleanup and improvement suggestions by @mikedn (#26551 (comment)).
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.
A couple of minor things.
b5dd942
to
d6d39d5
Compare
With the current fix we will see a regression in a couple of the benchmarks:
The old implementation will hoist expressions such as this one found in
|
I will implement a new fix that replaces nodes in the hoisted expressions that have constant value number, but are not true constants such as GT_CNS_INT or GT_CNS_DBL |
I am going to limit this to just GT_CLS_VARS. so that we don't regress the cases that improve the CodeQuality tests. Running the Asm diffs |
a1412cd
to
39820ca
Compare
With this fix we still have a bug here since we'll hoist any non-GT_CLS_VAR node with a constant value number, which is unsafe. I like your previous suggestion
better. Was there a problem with that approach? |
It is a much bigger change and I don't think it is required to fix this issue. |
The issue can potentially happen with assignments of constants to non-faulting indirections (e.g., fields or array elements). I couldn't get a repro with an indirection but only because we recognize the indirections as non-faulting too late. |
@briansull This PR was merged with failing test in |
…constant value. (dotnet#26551)" This reverts commit 2342c82.
Revert "Don't allow the hoisting of GT_CLS_VARs that were assigned a constant value. (#26551)"
New proposed fix: #26952 |
Fixes Issue #26417