Skip to content

Clear some stale GTF_ORDER_SIDEEFF flags#130007

Merged
dhartglassMSFT merged 7 commits into
dotnet:mainfrom
dhartglassMSFT:129055_p2_method3
Jul 23, 2026
Merged

Clear some stale GTF_ORDER_SIDEEFF flags#130007
dhartglassMSFT merged 7 commits into
dotnet:mainfrom
dhartglassMSFT:129055_p2_method3

Conversation

@dhartglassMSFT

@dhartglassMSFT dhartglassMSFT commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Clear stale GTF_ORDER_SIDEEFF flags to enable more if-conversion and optimize bools.

Motivating example was o.GetType() == typeof(int) || o.GetType() == typeof(uint) || o.GetType() == typeof(long) - we couldn't combine into OR because leftover side effect bit in one of the blocks.

If an op doesn't itself support side effects, and none of node's children have the bit, then the node may have the bit cleared.

Diffs show more optimize bools+if conversion taking place.
One of the bad diffs was block layout changing and causing LSRA to spill slightly different.
Another bad diff exposed an issue where edge-likelihoods weren't recalculated properly after optimize bools, and caused perfscore to balloon. I'm working on a fix for that seperately.

Copilot AI review requested due to automatic review settings June 29, 2026 22:37
@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jun 29, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts JIT tree side-effect flag maintenance by attempting to clear stale GTF_ORDER_SIDEEFF bits during Compiler::gtUpdateStmtSideEffects, to unblock downstream optimizations that are inhibited by incorrectly-propagated ordering constraints.

Changes:

  • Add logic in gtUpdateStmtSideEffects to clear GTF_ORDER_SIDEEFF on nodes that don’t support ordering side effects when none of their operands still carry the flag.

Comment thread src/coreclr/jit/gentree.cpp Outdated
@dhartglassMSFT
dhartglassMSFT marked this pull request as ready for review July 2, 2026 21:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

@dhartglassMSFT

Copy link
Copy Markdown
Contributor Author

link to diffs https://dev.azure.com/dnceng-public/public/_build/results?buildId=1486826&view=ms.vss-build-web.run-extensions-tab

Example (more ccmp in a method similar to the motivating example)

-4 (-7.14%) : AggregateSymbol:IsRefType():bool:this (FullOpts)
@@ -22,30 +22,27 @@ G_M9025_IG02:        ; bbWeight=1, gcrefRegs=0001 {x0}, byrefRegs=0000 {}, byref
             ; gcrRegs +[x0]
             ldr     w1, [x0, #0x90]
             cmp     w1, #1
-            beq     G_M9025_IG04
+            bne     G_M9025_IG05
                         ;; size=12 bbWeight=1 PerfScore 4.50
-G_M9025_IG03:        ; bbWeight=0.50, gcrefRegs=0000 {}, byrefRegs=0000 {}, byref, isz
+G_M9025_IG03:        ; bbWeight=0.50, gcrefRegs=0000 {}, byrefRegs=0000 {}, byref
             ; gcrRegs -[x0]
-            cmp     w1, #3
-            bne     G_M9025_IG06
-                        ;; size=8 bbWeight=0.50 PerfScore 0.75
-G_M9025_IG04:        ; bbWeight=0.50, gcrefRegs=0000 {}, byrefRegs=0000 {}, byref
             mov     w0, #1
                         ;; size=4 bbWeight=0.50 PerfScore 0.25
-G_M9025_IG05:        ; bbWeight=0.50, epilog, nogc, extend
+G_M9025_IG04:        ; bbWeight=0.50, epilog, nogc, extend
             ldp     fp, lr, [sp], #0x10
             ret     lr
                         ;; size=8 bbWeight=0.50 PerfScore 1.00
-G_M9025_IG06:        ; bbWeight=0.50, gcVars=0000000000000000 {}, gcrefRegs=0000 {}, byrefRegs=0000 {}, gcvars, byref
-            cmp     w1, #2
+G_M9025_IG05:        ; bbWeight=0.50, gcVars=0000000000000000 {}, gcrefRegs=0000 {}, byrefRegs=0000 {}, gcvars, byref
+            cmp     w1, #3
+            ccmp    w1, #2, z, ne
             cset    x0, eq
-                        ;; size=8 bbWeight=0.50 PerfScore 0.50
-G_M9025_IG07:        ; bbWeight=0.50, epilog, nogc, extend
+                        ;; size=12 bbWeight=0.50 PerfScore 0.75
+G_M9025_IG06:        ; bbWeight=0.50, epilog, nogc, extend
             ldp     fp, lr, [sp], #0x10
             ret     lr
                         ;; size=8 bbWeight=0.50 PerfScore 1.00
 
-; Total bytes of code 56, prolog size 8, PerfScore 9.50, instruction count 14, allocated bytes for code 56 (MethodHash=4337dcbe) for method Microsoft.CSharp.RuntimeBinder.Semantics.AggregateSymbol:IsRefType():bool:this (FullOpts)
+; Total bytes of code 52, prolog size 8, PerfScore 9.00, instruction count 13, allocated bytes for code 52 (MethodHash=4337dcbe) for method Microsoft.CSharp.RuntimeBinder.Semantics.AggregateSymbol:IsRefType():bool:this (FullOpts)
 ; ============================================================

@AndyAyersMS AndyAyersMS left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume we end up in this state because we fail to clear the flag when we change the oper?

Should we add an IR check that we never see GTF_ORDER_SIDEEFF on a node where it isn't expected?

Comment thread src/coreclr/jit/gentree.cpp Outdated
@jakobbotsch

Copy link
Copy Markdown
Member

I assume we end up in this state because we fail to clear the flag when we change the oper?

Should we add an IR check that we never see GTF_ORDER_SIDEEFF on a node where it isn't expected?

I agree it would be nice to make fgDebugCheckFlags check this flag more thoroughly. There will likely be some fallout if you do that -- for one you will probably need to change gtUpdateNodeOperSideEffects to remove this flag. Morph uses that to do a similar update of flags as gtUpdateStmtSideEffects inline during its walk.

@dhartglassMSFT

Copy link
Copy Markdown
Contributor Author

I assume we end up in this state because we fail to clear the flag when we change the oper?
Should we add an IR check that we never see GTF_ORDER_SIDEEFF on a node where it isn't expected?

I agree it would be nice to make fgDebugCheckFlags check this flag more thoroughly. There will likely be some fallout if you do that -- for one you will probably need to change gtUpdateNodeOperSideEffects to remove this flag. Morph uses that to do a similar update of flags as gtUpdateStmtSideEffects inline during its walk.

@jakobbotsch + @AndyAyersMS sequence of steps here was, null check gets removed on an IND so it + the parent EQ is marked SIDEEFFECT:

 [000019] -----O-----   JTRUE
 [000018] J----O-N---     EQ
 [000017] #----O-----        IND    long
 [000011] -----+-----           LCL_VAR ref V00
 [000013] H----+-----        CNS_INT(h) ...

IND gets CSE'd with a local, but EQ retains the bit (the gtUpdateStmtSideEffects method im changing here is called immediately after the CSE)

 [000018] J----O-N---   EQ         int 
 [000037] -----------      LCL_VAR long V02 cse0 
 [000013] H----+-----      CNS_INT(h) ...

@dhartglassMSFT

Copy link
Copy Markdown
Contributor Author

Opened #130150 to track the bad diff with the ballooned perfscore

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
  "version": 5,
  "last_dispatched_commit": "a715ad358df9038ffe2e22d2ebc83cd4358993ef",
  "last_dispatched_base_ref": "main",
  "last_dispatched_base_sha": "fe5e47348f86013bbe8f3041e56f5208cd632e53",
  "last_reviewed_commit": "a715ad358df9038ffe2e22d2ebc83cd4358993ef",
  "last_reviewed_base_ref": "main",
  "last_reviewed_base_sha": "fe5e47348f86013bbe8f3041e56f5208cd632e53",
  "last_recorded_worker_run_id": "29679159636",
  "review_attempt_commit": "",
  "review_attempt_base_ref": "",
  "review_attempt_count": 0,
  "max_review_attempts": 5,
  "review_history_format": "holistic-review-disclosure-v1",
  "review_history": [
    {
      "commit": "a715ad358df9038ffe2e22d2ebc83cd4358993ef",
      "review_id": 4730522596
    }
  ]
}

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holistic Review

Motivation: The problem is real: stale GTF_ORDER_SIDEEFF bits left on nodes whose oper does not support an ordering side effect block downstream optimizations (optimize-bools / if-conversion), as shown by the o.GetType() == typeof(...) OR-combining example. Clearing conservatively-set ordering bits is a legitimate cleanup.

Approach: The fix is placed correctly in the gtUpdateStmtSideEffects pre/post-order walker. Clearing the bit in PreOrderVisit when !OperSupportsOrderingSideEffect() is safe because PostOrderVisit re-propagates GTF_ALL_EFFECT (which includes GTF_ORDER_SIDEEFF) from children to the parent, so any node that legitimately inherits the flag from an operand gets it re-set. OperSupportsOrderingSideEffect() returns true for TYP_BYREF, so byref-forming nodes are not incorrectly stripped. This mirrors the existing canonicalization in OperEffects (gentree.cpp ~L8844), keeping the two paths consistent.

Summary: ✅ LGTM. The change is minimal, correct, and internally consistent with existing GTF_ORDER_SIDEEFF handling. Per the JIT folder guidance, a dedicated regression test is not required for this codegen-quality change validated by differential (SuperPMI) diffs; the author already notes reviewing the resulting good/bad diffs. Worth tracking in the PR discussion (as AndyAyersMS raised): the ideal long-term fix is to clear the flag at the point the oper is changed, and/or add an IR-consistency check asserting GTF_ORDER_SIDEEFF never appears on an unsupported node without a child carrying it. Those are reasonable follow-ups rather than blockers.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 66.2 AIC · ⌖ 14.5 AIC · ⊞ 10K

Copilot AI review requested due to automatic review settings July 22, 2026 05:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread src/coreclr/jit/gentree.cpp Outdated
Comment thread src/coreclr/jit/fgdiagnostic.cpp Outdated
Copilot AI review requested due to automatic review settings July 22, 2026 17:00
@dhartglassMSFT

Copy link
Copy Markdown
Contributor Author

@AndyAyersMS + @jakobbotsch I added a Debug check for this.

I also needed flag updates in fgMorphArgs and gtUpdateNodeOperSideEffects

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

src/coreclr/jit/gentree.cpp:11855

  • The gtUpdateNodeSideEffects doc comment is now out of date: it claims only EXCEPT/ASG/CALL are updated, but gtUpdateNodeOperSideEffects now also clears stale GTF_ORDER_SIDEEFF, and gtUpdateNodeSideEffects propagates GTF_ALL_EFFECT from children (including GTF_ORDER_SIDEEFF). Consider updating the comment (and fix the "side efects" typo) so it reflects current behavior.
    src/coreclr/jit/fgdiagnostic.cpp:3652
  • Minor typo/clarity issue in the comment describing when GTF_ORDER_SIDEEFF is treated as "extra": the current text says "set set" and implies an explicit child check here, but the child condition is already implied by actualFlags & ~expectedFlags in this branch. Consider tightening the wording.
        // GTF_ORDER_SIDEEFF is stale if set set on a node whose oper does not support it,
        // and whose children do not have it set
        if (tree->OperSupportsOrderingSideEffect())

Copilot AI review requested due to automatic review settings July 22, 2026 17:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

src/coreclr/jit/fgdiagnostic.cpp:3652

  • The comment describing when GTF_ORDER_SIDEEFF is considered stale is misleading: this helper doesn't directly inspect children. Instead, because expectedFlags already unions in child effects, an extra GTF_ORDER_SIDEEFF here indicates staleness only when the oper doesn't support it and it's not coming from children (i.e., not in expectedFlags). Updating the comment will help future maintainers reason about the mask logic.
        // GTF_ORDER_SIDEEFF is stale if set on a node whose oper does not support it,
        // and whose children do not have it set
        if (tree->OperSupportsOrderingSideEffect())

@JulieLeeMSFT JulieLeeMSFT left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@dhartglassMSFT
dhartglassMSFT merged commit 54549c8 into dotnet:main Jul 23, 2026
132 of 136 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-rc1 milestone Jul 24, 2026
hez2010 pushed a commit to hez2010/runtime that referenced this pull request Jul 26, 2026
Clear stale `GTF_ORDER_SIDEEFF` flags to enable more if-conversion and
optimize bools.

Motivating example was `o.GetType() == typeof(int) || o.GetType() ==
typeof(uint) || o.GetType() == typeof(long)` - we couldn't combine into
`OR` because leftover side effect bit in one of the blocks.

If an op doesn't itself support side effects, and none of node's
children have the bit, then the node may have the bit cleared.

Diffs show more optimize bools+if conversion taking place.
One of the bad diffs was block layout changing and causing LSRA to spill
slightly different.
Another bad diff exposed an issue where edge-likelihoods weren't
recalculated properly after optimize bools, and caused perfscore to
balloon. I'm working on a fix for that seperately.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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.

5 participants