-
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
JIT: Set GTF_ORDER_SIDEEFF for some nodes with invisible dependencies #78698
Conversation
We have a few places where we create the pattern "COMMA(some check, some value)". In some of these cases there may not be any visible dependency (e.g. use of a defined value) which makes the dependency invisible to the JIT. If the value is safe to compute only because of the check (for example, a bounds check + indexing operation), and if the value otherwise has no side effects, then nothing prevented the backend or optimizations from reordering these nodes. Fix dotnet#78554
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch Issue DetailsWe have a few places where we create the pattern "COMMA(some check, some value)". In some of these cases there may not be any visible dependency (e.g. use of a defined value) which makes the dependency invisible to the JIT. If the value is safe to compute only because of the check (for example, a bounds check + indexing operation), and if the value otherwise has no side effects, then nothing prevented the backend or optimizations from reordering these nodes. Fix #78554
|
@@ -5190,6 +5194,8 @@ GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* m | |||
GenTree* lclVar = gtNewLclvNode(lclNum, objRefType); | |||
GenTree* nullchk = gtNewNullCheck(lclVar, compCurBB); | |||
|
|||
nullchk->gtFlags |= GTF_ORDER_SIDEEFF; |
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 probably makes #71435 possible again.
Is it worth adding the test case from #78561 ? |
Yep, will do that |
Diffs were not as bad as I would've feared. The change to check Other than that regressions fell into these categories:
|
Diffs are pretty minimal now, the larger xarch ones are in some of the massive cse tests. This should unblock #77728. cc @dotnet/jit-contrib @a74nh PTAL @EgorBo @SingleAccretion |
src/tests/JIT/Regression/JitBlue/Runtime_78554/Runtime_78554.cs
Outdated
Show resolved
Hide resolved
src/coreclr/jit/importer.cpp
Outdated
@@ -9696,6 +9696,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) | |||
} | |||
if (helperNode != nullptr) | |||
{ | |||
helperNode->gtFlags |= GTF_ORDER_SIDEEFF; |
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.
Why do we need to set the ordering effect on these?
The calls should not be reorderable with GTF_GLOB_REF
-tagged field static access by themselves.
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.
Hmm yes, I can revert this one. I just looked for commas and applied this on the op1
in any place there was no directly visible dependency between the operands.
src/coreclr/jit/importer.cpp
Outdated
nullcheck->gtFlags |= GTF_ORDER_SIDEEFF; | ||
GenTree* result = gtNewOperNode(GT_COMMA, TYP_BYREF, nullcheck, boxPayloadAddress); |
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 is not obvious what the ordering effect here protects against -- what would a theoretical example of bad transformation look like?
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 think I can revert this one too given that boxPayloadAddress
is known to be very small when cloneOperand
is null.
If it wasn't then early prop would probably be able to do something illegal here if it removed this null check due to an upcoming null check, causing us to form some random byref.
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.
Hmm, I'm not sure after all. Without setting this then if we did the optimization in #71435 we could potentially remove these null checks and turn a NRE into an AV when accessing a field right around the explicit null check threshold. Consider something like:
ASG
LCL_VAR byref V00
COMMA byref // added here
NULLCHECK // node A
LCL_VAR ref V01
ADD byref
LCL_VAR ref V01
CNS_INT long 8
ASG
LCL_VAR int V02
IND int // node B
ADD
LCL_VAR byref V00
CNS_INT long 0xFFFC
ASG
LCL_VAR int V03
IND int // node C
LCL_VAR ref V01
The optimization would reason as follows: the node A null check is unnecessary because node B only has the same side effects as the null check (throwing NRE), and node C subsumes node A. However, node B will actually AV without node A.
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 would classify this as the optimization being wrong, since we can only consider NRE to be "true" NRE and not "NRE but maybe AV" with small offsets (we do the checks against the max null check offset in other places to differentiate these two cases, though not everywhere).
More fundamentally, fixing this with an ordering effect seems... arbitrary? What is the ordering dependency?
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.
We've talked with @jakobbotsch on Discord and concluded the ordering here is needed because "almost null" byrefs must never occur on dynamically reachable paths, so swapping the ADD
with the null check would effectively introduce UB into the program.
It has also been determined that our handling around GTF_ORDER_SIDEEFF
is a bit lacking, and it's meaning a bit unclear. We've settled on the "only fully side-effect-less nodes can be reordered with ordered nodes". This then implies both the ADD
and the null check need to be tagged as ordered.
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 has also been determined that our handling around
GTF_ORDER_SIDEEFF
is a bit lacking, and it's meaning a bit unclear. We've settled on the "only fully side-effect-less nodes can be reordered with ordered nodes"
It might be useful to add the meaning as a comment. Maybe next to the definition in gentree.h? The current "sub-expression has a re-ordering side effect" is a little lacking.
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 agree we should have some more detailed docs on what the effect flags mean and how they are assumed to be set, but I think it would be better to add a section in https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/jit/ryujit-tutorial.md and refer to that. I will aim to do a write-up of that soon.
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.
ok, I'm happy with that.
/azp run runtime-coreclr jitstress, runtime-coreclr libraries-jitstress, Fuzzlyn |
Azure Pipelines successfully started running 3 pipeline(s). |
We have a few places where we create the pattern "COMMA(some check, some value)". In some of these cases there may not be any visible dependency (e.g. use of a defined value) which makes the dependency invisible to the JIT.
If the value is safe to compute only because of the check (for example, a bounds check + indexing operation), and if the value otherwise has no side effects, then nothing prevented the backend or optimizations from reordering these nodes.
A particular problem we may have is around array indexing and bounds checks. Creating an arbitrary illegal byref is not allowed, but to the JIT this node is just a normal node that is completely free of any side effects. Before this change nothing was preventing us from reordering the bounds checks with the computation of the array element.
Fix #78554