Skip to content

Conversation

@EgorBo
Copy link
Member

@EgorBo EgorBo commented Sep 17, 2025

Fixes #119654

For a minimal repro:

int Test(long availableSigned)
{
    if (availableSigned == 0L)
        return 0;
    return (uint)(ulong)availableSigned != 0 ? 111 : 1;
}

We have two BBJ_TRUE blocks

[000003] -----------                         *  JTRUE     void  
[000002] -----------                         \--*  NE        int   
[000000] -----------                            +--*  LCL_VAR   long   V00 arg0         
[000001] -----------                            \--*  CNS_INT   long   0


[000008] -----------                         *  JTRUE     void  
[000007] -----------                         \--*  NE        int   
[000005] -----------                            +--*  CAST      int <- uint <- long
[000004] -----------                            |  \--*  LCL_VAR   long   V00 arg0          (last use)
[000006] -----------                            \--*  CNS_INT   int    0

an optimization in morph (optNarrowTree to be precise) optimizes the CAST node into a re-typed local (LONG->INT) and we end up with:

[000003] -----------                         *  JTRUE     void  
[000002] -----------                         \--*  NE        int   
[000000] -----------                            +--*  LCL_VAR   long   V00 arg0         
[000001] -----------                            \--*  CNS_INT   long   0


[000008] -----+-----                         *  JTRUE     void  
[000007] J----+-N---                         \--*  NE        int   
[000004] -----+-----                            +--*  LCL_VAR   int    V00 arg0          (last use)
[000006] -----+-----                            \--*  CNS_INT   int    0

and then Local Assertion Prop thinks the second NE can be folded to true given the assertion created by the first JTRUE - but it's not correct since the locals are the same but their types are different (INT vs LONG).

Looks like such re-typed locals are a bug farm, unfortunately, it's a massive size regression if I disable that in optNarrowTree

Copilot AI review requested due to automatic review settings September 17, 2025 04:20
Copy link
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 fixes a bug in the JIT compiler's local assertion propagation where incorrect type handling led to faulty optimizations. The issue occurred when the compiler incorrectly folded a comparison to true based on an assertion from a different type comparison of the same local variable.

  • Adds a type check in optAssertionPropLocal_RelOp to prevent assertion propagation when local variable types don't match
  • Includes a regression test to verify the fix for the specific scenario involving long-to-int type conversions

Reviewed Changes

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

File Description
src/coreclr/jit/assertionprop.cpp Adds type validation to prevent incorrect assertion propagation across different types of the same local variable
src/tests/JIT/Regression/JitBlue/Runtime_119654/Runtime_119654.cs Test case demonstrating the bug scenario with long/int type conversions and conditional logic
src/tests/JIT/Regression/JitBlue/Runtime_119654/Runtime_119654.csproj Project file for the regression test

@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 Sep 17, 2025
@dotnet-policy-service
Copy link
Contributor

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

@EgorBo
Copy link
Member Author

EgorBo commented Sep 17, 2025

I presume it's similar to #112506 but opposite - there we stop creating assertions for truncated locals and here we stop consuming them for truncated locals

@jakobbotsch
Copy link
Member

Looks like such re-typed locals are a bug farm, unfortunately, it's a massive size regression if I disable that in optNarrowTree

I think we can move the transformation to lowering for x64 and arm64, and then keep the CAST<int>(LCL_VAR<long>) shape until then.
Note that IV widening also produces this shape of IR, so it would need to be updated too.

@EgorBo
Copy link
Member Author

EgorBo commented Sep 17, 2025

Looks like such re-typed locals are a bug farm, unfortunately, it's a massive size regression if I disable that in optNarrowTree

I think we can move the transformation to lowering for x64 and arm64, and then keep the CAST<int>(LCL_VAR<long>) shape until then. Note that IV widening also produces this shape of IR, so it would need to be updated too.

Good point, I fear a change like that might not be safe to backport to .net 10, so let's land this and then I'll implement your suggestion, will see how the diffs look like

@jakobbotsch
Copy link
Member

Good point, I fear a change like that might not be safe to backport to .net 10, so let's land this and then I'll implement your suggestion, will see how the diffs look like

I definitely agree, we shouldn't backport a change like that. I just think that would be the better long term design.

@EgorBo EgorBo merged commit 55d09c7 into dotnet:main Sep 17, 2025
118 checks passed
@EgorBo
Copy link
Member Author

EgorBo commented Sep 17, 2025

/backport to release/10.0

@github-actions
Copy link
Contributor

@github-actions github-actions bot locked and limited conversation to collaborators Oct 18, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

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.

Unexpected IndexOutOfRangeException

3 participants