diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 656b58ee84fbf7..c0558f14da54b2 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4599,7 +4599,30 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) BlockRange().Remove(op2); GenCondition cmpCondition = GenCondition::FromRelop(cmp); - GenTreeCC* setcc = m_compiler->gtNewCC(GT_SETCC, cmp->TypeGet(), cmpCondition); + + // For unsigned compares against zero that rely on flags-as-compare-to-zero, + // we cannot use UGT/UGE/ULT/ULE directly because op1 may set only NZ flags + // (e.g. ANDS on ARM64 clears C/V). UGT/ULT depend on carry, which would be wrong. + // Rewrite the condition to use Z/N where possible, or bail out. + if (cmp->IsUnsigned() && op2->IsIntegralConst(0) && cmp->OperIs(GT_GT, GT_GE, GT_LT, GT_LE)) + { + if (cmp->OperIs(GT_GT)) + { + // x > 0U <=> x != 0 + cmpCondition = GenCondition::NE; + } + else if (cmp->OperIs(GT_LE)) + { + // x <= 0U <=> x == 0 + cmpCondition = GenCondition::EQ; + } + else + { + // x >= 0U is always true and x < 0U is always false; keep the compare for correctness. + return cmp; + } + } + GenTreeCC* setcc = m_compiler->gtNewCC(GT_SETCC, cmp->TypeGet(), cmpCondition); BlockRange().InsertAfter(op1, setcc); use.ReplaceWith(setcc); diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_124510/Runtime_124510.cs b/src/tests/JIT/Regression/JitBlue/Runtime_124510/Runtime_124510.cs new file mode 100644 index 00000000000000..0986417aafb80b --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_124510/Runtime_124510.cs @@ -0,0 +1,51 @@ +// Generated by Fuzzlyn v3.3 on 2026-02-15 16:52:57 +// Run on Arm64 Linux +// Seed: 14599529654677333173-vectort,vector64,vector128,armadvsimd,armadvsimdarm64,armaes,armarmbase,armarmbasearm64,armcrc32,armcrc32arm64,armdp,armrdm,armrdmarm64,armsha1,armsha256,armsve,armsve2 +// Reduced from 73.3 KiB to 0.7 KiB in 00:00:52 +// Debug: Outputs 0 +// Release: Outputs 1 +public class C0 +{ + public ulong F3; + public C0(ulong f3) + { + F3 = f3; + } +} + +public class C1 +{ + public byte F8; +} + +public class C2 +{ + public C0 F1; + public C1 F2; + public C2(C0 f1, C1 f2) + { + F1 = f1; + F2 = f2; + } +} + +public struct S0 +{ + public byte F0; +} + +public class Program +{ + public static S0 s_1; + public static C2 s_2 = new C2(new C0(8013948595597981922UL), new C1()); + public static void Main() + { + var vr1 = s_2.F1.F3; + if (((uint)(vr1 & 3080599622U) <= (ushort)(s_2.F2.F8 % 1))) + { + byte vr3 = s_1.F0++; + } + + System.Console.WriteLine(s_1.F0); + } +} diff --git a/src/tests/JIT/Regression/Regression_ro_1.csproj b/src/tests/JIT/Regression/Regression_ro_1.csproj index 7d7788d931a8d6..62bf725b3642ad 100644 --- a/src/tests/JIT/Regression/Regression_ro_1.csproj +++ b/src/tests/JIT/Regression/Regression_ro_1.csproj @@ -78,6 +78,7 @@ + diff --git a/src/tests/JIT/opt/InstructionCombining/And.cs b/src/tests/JIT/opt/InstructionCombining/And.cs index f794bc20624a92..e4f2742d7fc88e 100644 --- a/src/tests/JIT/opt/InstructionCombining/And.cs +++ b/src/tests/JIT/opt/InstructionCombining/And.cs @@ -125,6 +125,16 @@ public static int CheckAnd() fail = true; } + if (AndsUnsignedStaticBranchLe() != 0) + { + fail = true; + } + + if (AndsUnsignedStaticBranchGt() == 0) + { + fail = true; + } + if (fail) { return 101; @@ -288,29 +298,62 @@ static bool AndsBinOpSingleLine(uint a, uint b, uint c, uint d) [MethodImpl(MethodImplOptions.NoInlining)] static bool AndsGreaterThan(int a, int b) { - //ARM64-FULL-LINE: ands w0, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: ands {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} return (a & b) > 0; } [MethodImpl(MethodImplOptions.NoInlining)] static bool AndsGreaterThanEq(int a, int b) { - //ARM64-FULL-LINE: ands w0, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: ands {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} return (a & b) >= 0; } [MethodImpl(MethodImplOptions.NoInlining)] static bool AndsLessThan(int a, int b) { - //ARM64-FULL-LINE: ands w0, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: ands {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} return (a & b) < 0; } [MethodImpl(MethodImplOptions.NoInlining)] static bool AndsLessThanEq(int a, int b) { - //ARM64-FULL-LINE: ands w0, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: ands {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} return (a & b) <= 0; } + + static ulong s_a = 8013948595597981922UL; + static byte s_b = 0; + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AndsUnsignedStaticBranchLe() + { + //ARM64-FULL-LINE: ands {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: cset {{x[0-9]+}}, eq + uint left = (uint)(s_a & 0xB79E3846u); + uint right = (ushort)(s_b % 1); + + if (left <= right) + { + return 1; + } + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AndsUnsignedStaticBranchGt() + { + //ARM64-FULL-LINE: tst {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: cset {{x[0-9]+}}, ne + uint left = (uint)(s_a & 0xB79E3846u); + uint right = (ushort)(s_b % 1); + + if (left > right) + { + return 1; + } + return 0; + } } } diff --git a/src/tests/JIT/opt/InstructionCombining/Bic.cs b/src/tests/JIT/opt/InstructionCombining/Bic.cs index 419981104094aa..4e817c73652e73 100644 --- a/src/tests/JIT/opt/InstructionCombining/Bic.cs +++ b/src/tests/JIT/opt/InstructionCombining/Bic.cs @@ -115,6 +115,16 @@ public static int CheckBic() fail = true; } + if (BicsUnsignedStaticBranchLe() != 0) + { + fail = true; + } + + if (BicsUnsignedStaticBranchGt() == 0) + { + fail = true; + } + if (fail) { return 101; @@ -316,5 +326,38 @@ static bool BicsLessThanEq(int a, int b) //ARM64-FULL-LINE: bics {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} return (a & ~b) <= 0; } + + static uint s_a = 0xFFFFFFFFu; + static uint s_b = 0x12345678u; + + [MethodImpl(MethodImplOptions.NoInlining)] + static int BicsUnsignedStaticBranchLe() + { + //ARM64-FULL-LINE: bics {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: cset {{x[0-9]+}}, eq + uint left = s_a & ~s_b; + uint right = (ushort)(s_b % 1); + + if (left <= right) + { + return 1; + } + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int BicsUnsignedStaticBranchGt() + { + //ARM64-FULL-LINE: bics {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: cset {{x[0-9]+}}, ne + uint left = s_a & ~s_b; + uint right = (ushort)(s_b % 1); + + if (left > right) + { + return 1; + } + return 0; + } } }