Skip to content

JIT: extend ccmp combining and fold redundant null-check for isinst / GetType chains #129055

Open

Description

@AndyAyersMS

Note

AI-assisted (Copilot CLI).

Follow-up to #47920 (closed as resolved). The original "ineffective codegen" complaint is fixed — o is int or uint or long (M0) now produces the smallest, fastest code, using arm64 ccmp to combine the second and third type-id compares. Two related codegen-quality gaps remain:

1. Redundant null-check materialization when o != null && (o is X) appears

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

The leading o != null is semantically redundant (the isinst already returns false for null), but the JIT emits:

cmp  x1, #0          ; <-- could be folded away
cset x0, ne
cbz  w0, IG06
ldr  x0, [x1]        ; ...isinst chain identical to M0...

Equivalent C# without the leading null check (M0) emits just cbz x1, IG05 instead. ~8 bytes / 3 instructions wasted per call. The IL pattern is cgt.un(o, null); stloc V02; ldloc V02; brfalse. optOptimizeBools could simplify STORE V02 = (X != null); JTRUE V02 == 0 into JTRUE X == null (and forward-substitute to eliminate V02's first definition), but it doesn't, likely because V02 is reassigned later in flow with the isinst result.

2. ccmp combining only fires for the or-pattern, not for ||-chains or GetType()-equality chains

Form Code size (arm64 .NET 11) Uses ccmp
o is int or uint or long 92 B yes (combines uint+long)
o is int || o is uint || o is long 108 B no — three separate cmp/beq
o.GetType() == typeof(int) || o.GetType() == typeof(uint) || o.GetType() == typeof(long) 92 B (without null check) / 108 B (with) no — three separate cmp/beq

The ||-chain pattern is the obvious hand-written form; users with existing code should already get the same ccmp combining the or-pattern enjoys. Likewise the explicit GetType() == typeof(X) || ... chain (a common pattern from before is-patterns existed) should benefit.

Suggested next steps

  • Investigate generalizing the ccmp combine in arm64 lowering to handle short ||-chains of methodtable-equality comparisons, regardless of whether the source pattern came from is X or Y or Z, is X || is Y || is Z, or GetType() == typeof(X) || ....
  • Fix optOptimizeBools / value-numbering to fold (X != null) && IsInst(X, T) → IsInst(X, T) (or equivalently fold the redundant null-materialization).

Related: #47920 (closed).

cc @dotnet/jit-contrib

Activity

  1. dotnet-policy-service commented on Jun 5, 2026

    @dotnet-policy-service
    Contributor

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

  2. AndyAyersMS commented on Jun 5, 2026

    @AndyAyersMS
    MemberAuthor

    FYI @dhartglassMSFT some other smallish ARM64 CQ issues.

  3. removed
    untriagedNew issue has not been triaged by the area owner
    on Jun 5, 2026
  4. added this to the 11.0.0 milestone on Jun 5, 2026
  5. dhartglassMSFT commented on Jun 12, 2026

    @dhartglassMSFT
    Contributor

    Taking a look

  6. dhartglassMSFT commented on Jun 12, 2026

    @dhartglassMSFT
    Contributor

    Yeah problem here is with the extra stloc/ldloc, that local is used in the return block. Other IL (for something like o != null && (something_else) doesn't have the extra local, it just branches on the argument

  7. dhartglassMSFT commented on Jul 6, 2026

    @dhartglassMSFT
    Contributor

    To update, issue 1 is because an extra local that contains the result of the first null comparison that's live until the shared return. It is possible to remove the first CMP if we know precisely where its uses are though.

    Issue 2-2 is oddly enough the opposite of issue 1 - that method has multiple returns that look similar, so optimize bools can't do the block-combining-into-or because the two blocks don't have the same branch target like in 2-1.

    2-3 have a fix for in pr.

  8. dhartglassMSFT commented on Aug 14, 2026

    @dhartglassMSFT
    Contributor

    ongoing, but won't reach 11 deadline

  9. modified the milestones: 11.0.0, 12.0.0 on Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

arch-arm64area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMItenet-performancePerformance related issue

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions