Skip to content

JIT: Forward comparisons with live-out boolean results - #135984

Open
dhartglassMSFT wants to merge 5 commits into
dotnet:mainfrom
dhartglassMSFT:m1_t5
Open

dhartglassMSFT wants to merge 5 commits into
dotnet:mainfrom
dhartglassMSFT:m1_t5

Conversation

@dhartglassMSFT

@dhartglassMSFT dhartglassMSFT commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Method:
bool M1(object o) => o != null && (o is int or uint or long);

Intro block has code with an extra compare like cmp+cset+cbz (null check, write result of null check to local, compare and branch on local). The first cmp is needed because the local which receives the null check is live in later blocks.

To fix, JIT will look for shapes like this during forwardsub, replace the branch with the null check itself, and store a constant to the null-check-local along the path its used.

TPDiff reports +0.01%.

Diffs from motivating example:

x64

        test     rcx, rcx
-       setne    al
-       movzx    rax, al
-       test     eax, eax
-       je       RETURN
+       jne      TYPE_CHECKS
+       xor      eax, eax
+       jmp      RETURN

 TYPE_CHECKS:
        rest_of_function

ARM64

-       cmp      x0, #0
-       cset     x1, ne
-       cbz      w1, RETURN
+       cbnz     x0, TYPE_CHECKS
+       mov      w0, wzr
+       b        RETURN

 TYPE_CHECKS:
       rest_of_function
...
 RETURN:
-       mov      w0, w1
        ldp      fp, lr, [sp], #0x10
        ret      lr

Full arch diff results. I'm still looking through regressions. Some are cases of loop cloning being enabled, or changes in lsra.

Target Contexts with diffs Smaller Larger Same size Net code-size delta
Windows x64 279 248 25 6 −3,621 bytes
Linux x64 299 266 28 5 −3,707 bytes
Windows x86 248 205 37 6 −2,776 bytes
Windows ARM64 285 224 7 54 −2,196 bytes
Linux ARM64 317 242 15 60 −2,448 bytes
macOS ARM64 26 22 1 3 −244 bytes
Linux ARM 235 222 7 6 −4,156 bytes
Browser WASM 1 1 0 0 −2 bytes

@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 Oct 16, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 5 pipeline(s).
11 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@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.

@dhartglassMSFT dhartglassMSFT changed the title JIT sink comparison results along a constant edge JIT: Forward comparisons with live-out boolean results Oct 22, 2026
@dhartglassMSFT
dhartglassMSFT marked this pull request as ready for review October 22, 2026 23:49
@dhartglassMSFT

Copy link
Copy Markdown
Contributor Author

@dotnet/jit-contrib @EgorBo
If there's a better place than forward sub for this let me know. I wanted an early phase that had liveness information

unsigned const lclNum = def->AsLclVar()->GetLclNum();
LclVarDsc* const varDsc = lvaGetDesc(lclNum);
if (!varDsc->lvTracked || varDsc->IsAddressExposed() || varDsc->lvPinned || varDsc->lvIsStructField ||
varDsc->IsLiveInOutOfHandler() || (genActualType(varDsc->TypeGet()) != TYP_INT))

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.

Not sure I understand the limitation on varDsc->lvIsStructField and genActualType(varDsc->TypeGet()) != TYP_INT). Also, IsLiveInOutOfHandler feels redudant - Liveness already adds what exception handlers need to a block's live-in set


if (cond->OperIs(GT_EQ))
{
bool const reversed = gtTryReverseCond(compare);

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.

am I missing something - why do we reverse only EQ here?

@EgorBo

EgorBo commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

This feels like it's doing what optRedundantRelop supposed to do? Do you mind if I leave this to @AndyAyersMS or @jakobbotsch to take a look, perhaps we can generalize it into some strength reduction / code sink phase or something, because current diffs looks quite small.

This branch has not been deployed

No deployments
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.

2 participants