Skip to content

JIT: (bug) assertion prop retargets a small-typed LCL_VAR to an int local, emitting a truncated compare (wrong result) #133859

Closed

Description

@EgorBo

The VN relop based copy assertion prop path in optAssertionProp_RelOp retargets op1's lclNum/ssaNum to op2's local but leaves op1's node type alone, producing NE(LCL_VAR ubyte V03, LCL_VAR int V03). The mismatched types stop the self-compare from folding, and codegen emits a narrow load for one side, so the compare yields the wrong answer.

Minimal Repro

using System;
using System.Runtime.CompilerServices;

public static class Program
{
    [MethodImpl(MethodImplOptions.NoInlining)]
    static void Consume(int x) { }

    [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
    public static int Test(byte b, int[] arr, int x)
    {
        b = (byte)x;
        int len = arr.Length;
        int r = 0;
        try
        {
            if (b != len)
            {
                Consume(b);
                Consume(len);
                r = (b == len) ? 1 : 0;
            }
            else
            {
                r = 3;
            }
            if (x == 12345)
            {
                len = 7;
            }
        }
        catch
        {
            r = len;
        }
        return r;
    }

    public static int Main()
    {
        Console.WriteLine(Test(0, new int[300], 44));
        return 100;
    }
}

b is 44, len is 300; the b != len branch is taken, so (b == len) is statically false and r must be 0.

Expected

0

Actual

1

Disasm (DOTNET_JitDisasm=Test), V03 is int loc0:

       movzx    rax, byte  ptr [rbp-0x0C]      ; <=== narrow load of the TYP_INT local V03 (300 -> 44)
       mov      ecx, dword ptr [rbp-0x0C]      ; <=== full load of the same local (300)
       cmp      eax, ecx
       setne    al

Notes

  • Malformed tree after assertion prop: NE(LCL_VAR ubyte V03, LCL_VAR int V03) — GenTree::Compare won't fold identical operands whose types differ, so the node reaches codegen.
  • Fix direction: in the local/local copy path also fix up op1's type (e.g. op1->ChangeType(op2->TypeGet())), or bail out when the types differ.
  • The try/catch and second store to len only force V03 to be do-not-enreg so the narrow load becomes a visible memory access; the malformed IR is produced without them too.
  • Reproduces in Release and Checked corerun, with and without tiering; no assert fires. Does not reproduce on .NET 10.0.12, so this looks main-only (the optAssertionProp_RelOp code is old, so some upstream change now lets the small-typed LCL_VAR reach assertion prop uncast).

Activity

  1. added this to the 12.0.0 milestone on Sep 14, 2026
  2. added
    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
    on Sep 14, 2026
  3. dotnet-policy-service commented on Sep 14, 2026

    @dotnet-policy-service
    Contributor

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

  4. EgorBo commented on Sep 18, 2026

    @EgorBo
    MemberAuthor

    Affected SDK versions:

    • 11.0.100-rc.2.26467.112
  5. added a commit that references this issue on Oct 2, 2026
    779bd79
  6. self-assigned this
    on Oct 8, 2026
  7. locked and limited conversation to collaborators on Nov 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions