Skip to content
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

Merged
merged 10 commits into from
Nov 28, 2022

Conversation

jakobbotsch
Copy link
Member

@jakobbotsch jakobbotsch commented Nov 22, 2022

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

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
@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Nov 22, 2022
@ghost ghost assigned jakobbotsch Nov 22, 2022
@ghost
Copy link

ghost commented Nov 22, 2022

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Issue Details

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 #78554

Author: jakobbotsch
Assignees: -
Labels:

area-CodeGen-coreclr

Milestone: -

@@ -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;
Copy link
Member Author

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.

@a74nh
Copy link
Contributor

a74nh commented Nov 22, 2022

Is it worth adding the test case from #78561 ?

@jakobbotsch
Copy link
Member Author

Is it worth adding the test case from #78561 ?

Yep, will do that

@jakobbotsch
Copy link
Member Author

Diffs were not as bad as I would've feared. The change to check GTF_ORDER_SIDEEFF in if-conversion was the most impactful and we can special case it for invariant nodes/locals, which avoids most of those regressions.

Other than that regressions fell into these categories:

  • The introduced GTF_ORDER_SIDEEFF on indirections sometimes now disallows RBO from duplicating nodes. It is questionable that we are doing this in the first place, I have opened JIT: RBO still introduces data races #78713 about it.
  • Setting GTF_ORDER_SIDEEFF on an indir that was the source of a block store would block containment of the source address. Submitted JIT: Ignore source indir when checking interference for block store source address #78763 for this, will push other changes to this PR after this is merged.
  • Rarely, the new GTF_ORDER_SIDEEFF is propagated to some node that ends up pure later (e.g. due to CSE). This may block address mode creation and other containment that was previously allowed.

@jakobbotsch
Copy link
Member Author

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

@jakobbotsch jakobbotsch marked this pull request as ready for review November 23, 2022 19:07
@@ -9696,6 +9696,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
}
if (helperNode != nullptr)
{
helperNode->gtFlags |= GTF_ORDER_SIDEEFF;
Copy link
Contributor

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.

Copy link
Member Author

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.

Comment on lines 10467 to 10468
nullcheck->gtFlags |= GTF_ORDER_SIDEEFF;
GenTree* result = gtNewOperNode(GT_COMMA, TYP_BYREF, nullcheck, boxPayloadAddress);
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Contributor

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?

Copy link
Contributor

@SingleAccretion SingleAccretion Nov 23, 2022

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.

Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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.

@jakobbotsch
Copy link
Member Author

Diffs after setting GTF_ORDER_SIDEEFF on both operands of the commas are still pretty minimal, with the larger diffs still being test only.

@EgorBo Can you take a look and approve if it looks ok to you?

@jakobbotsch
Copy link
Member Author

/azp run runtime-coreclr jitstress, runtime-coreclr libraries-jitstress, Fuzzlyn

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@jakobbotsch
Copy link
Member Author

Failures are #78912 #78909

@jakobbotsch jakobbotsch merged commit f35444a into dotnet:main Nov 28, 2022
@jakobbotsch jakobbotsch deleted the fix-78554 branch November 28, 2022 13:53
@ghost ghost locked as resolved and limited conversation to collaborators Dec 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Arm64: If Conversion can bypass overflow checks
4 participants