Skip to content

More precise ranges for casts in GetRangeFromAssertions#124415

Merged
EgorBo merged 12 commits into
dotnet:mainfrom
EgorBo:precise-cast-ranges
May 12, 2026
Merged

More precise ranges for casts in GetRangeFromAssertions#124415
EgorBo merged 12 commits into
dotnet:mainfrom
EgorBo:precise-cast-ranges

Conversation

@EgorBo
Copy link
Copy Markdown
Member

@EgorBo EgorBo commented Feb 14, 2026

Copilot AI review requested due to automatic review settings February 14, 2026 01:24
@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 Feb 14, 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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 improves range analysis in the JIT's range check optimization pass by enabling more precise range propagation through cast operations and adding support for TYP_INT in GetRangeFromType.

Changes:

  • Add early range tightening based on VN type for small types in GetRangeFromAssertions (though this appears to be dead code due to VN normalization)
  • Change cast source type check from exact TYP_INT match to using genActualType, making the code more defensive
  • Add TYP_INT case to GetRangeFromType to return the full int32 range instead of Unknown

Comment thread src/coreclr/jit/rangecheck.cpp Outdated
Copilot AI review requested due to automatic review settings February 14, 2026 03:05
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 2 comments.

Comment thread src/coreclr/jit/rangecheck.cpp
Comment thread src/coreclr/jit/rangecheck.cpp
Copilot AI review requested due to automatic review settings February 14, 2026 15:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread src/coreclr/jit/rangecheck.cpp
@EgorBo EgorBo force-pushed the precise-cast-ranges branch from 2704ba3 to e492c87 Compare February 14, 2026 18:54
Copilot AI review requested due to automatic review settings February 14, 2026 18:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 2 out of 2 changed files in this pull request and generated no new comments.

@EgorBo EgorBo marked this pull request as ready for review February 14, 2026 21:17
Copilot AI review requested due to automatic review settings February 14, 2026 21:19
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 2 out of 2 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings May 10, 2026 21:12
@EgorBo EgorBo force-pushed the precise-cast-ranges branch from 6546d64 to a0cbca9 Compare May 10, 2026 21:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 2 out of 2 changed files in this pull request and generated no new comments.

The varTypeIsGC(vnType) branch was added defensively for 32-bit, where
TYP_I_IMPL == TYP_INT could in theory let GC-typed VNs reach this code.
In practice, all callers gate on TYP_INT and recursive sites either gate
on TYP_INT or operate on results that cannot produce GC operands of an
INT VN. SPMI replay (x64 aspnet/libraries/benchmarks and x86 benchmarks)
shows no behavior change after removal — asmdiffs are identical at
-4,708 bytes net for aspnet.

If a GC VN ever does leak in, the existing assert(genActualType(vnType)
== TYP_INT) will surface it cleanly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/coreclr/jit/assertionprop.cpp Outdated
Per @jakobbotsch review feedback: NOL locals can live in registers where
the JIT does not insert any normalization without the cast. Retyping the
LCL_VAR to its small type and dropping the cast lets codegen read the
register without a normalizing load (e.g., movzx), exposing potentially
undefined upper bits for register-resident NOL params.

The reason morph.cpp:3230 can perform a similar retype-and-drop is that
it gates on local subrange assertions, which only exist after a
JIT-controlled store that normalized the bits. VN-based subrange proofs
have no such guarantee — they can come from comparisons, casts, joins,
or phi merges that say nothing about the physical register state.

Restore the original PR behavior of skipping NOL locals entirely in the
VN-based path. The local-prop path below is unchanged and still
performs the safe retyping based on local assertions.

Cost on benchmarks.run: +4,482 bytes vs the (unsafe) prior commit, but
still -3,670 bytes net vs origin/main. SPMI replay clean on aspnet,
libraries (incl. tests), benchmarks. JIT/opt/Or/IntOr disasm pattern
preserved by the small-cast-on-non-LCL_VAR guard introduced earlier.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 10, 2026 22:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 2 out of 2 changed files in this pull request and generated no new comments.

Reverts 0a3a858 ("Remove unreachable GC early return ...") which
broke 32-bit crossgen2 of System.Private.CoreLib (Memmove and others
asserting `genActualType(vnType) == TYP_INT` at rangecheck.cpp:698).

Why the GC return is actually needed (and my prior analysis missed):

On 32-bit targets TYP_INT/TYP_BYREF/TYP_REF are all 4 bytes, so the
JIT can legally write a byref-valued expression (e.g. LCL_ADDR) into
an int-typed local. The local is TYP_INT but its VN is a TYP_BYREF
function such as PtrToLoc.

Repro context (libraries.crossgen2 MC 146148, F# SubstHelperRaw):

    STORE_LCL_VAR int V06 <- LCL_ADDR int V15      # V06 has VN PtrToLoc(...) (BYREF)
    ...PHI int V06 across blocks $340 = PhiDef(...)
    NE int (LCL_VAR int V06, CNS_INT int 0)        # asks for range of $340

PR dotnet#127117 (Apr 23) added PhiDef recursion in
GetRangeFromAssertionsWorker; the visitor walks reaching VNs of the
phi, and on 32-bit one of them can be the BYREF PtrToLoc VN even
though the public caller passed an int-typed tree. The pre-existing
GC early-return guard catches exactly this case; the explanatory
comment is updated so the next reader doesn't repeat my mistake.

x64 SPMI didn't catch it because TYP_BYREF (8 bytes) and TYP_INT (4
bytes) cannot alias in the same local, so `LCL_ADDR int` doesn't
appear and the phi recursion never reaches a BYREF VN.

Validation: x86 libraries.crossgen2 SPMI replay clean (279,521 ctx).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 11, 2026 11:39
@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented May 11, 2026

@EgorBot -arm -amd

using BenchmarkDotNet.Attributes;

public class Benchmarks
{
    private byte b = 42;

    [Benchmark]
    public string ByteToString() => b.ToString();
}

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 2 out of 2 changed files in this pull request and generated no new comments.

@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented May 12, 2026

I think this is ready, PTAL @jakobbotsch @dotnet/jit-contrib
Some nice diffs

An example of what it folds:

internal static string UInt32ToDecStr(uint value)
{
// For small numbers, consult a lazily-populated cache.
if (value < SmallNumberCacheLength)
{
return UInt32ToDecStrForKnownSmallNumber(value);
}

When Byte.cs pass itself on its ToString() we can fold the value < 300 check

@EgorBo EgorBo requested a review from jakobbotsch May 12, 2026 09:58
Comment thread src/coreclr/jit/rangecheck.cpp
Comment thread src/coreclr/jit/assertionprop.cpp
Comment thread src/coreclr/jit/assertionprop.cpp
@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented May 12, 2026

/ba-g timeouts

@EgorBo EgorBo merged commit ccc1b27 into dotnet:main May 12, 2026
129 of 139 checks passed
@EgorBo EgorBo deleted the precise-cast-ranges branch May 12, 2026 12:33
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.

3 participants