From 5cb0803b6a2f1e7061cd36dd51579f67d54159ce Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Thu, 19 Feb 2026 15:35:25 +0000 Subject: [PATCH 1/2] arm64: Fixed using ands/bics to compare against static unsigned constant 0. - Fixes #124510 --- src/coreclr/jit/lower.cpp | 25 ++++++++- .../JitBlue/Runtime_124510/Runtime_124510.cs | 51 +++++++++++++++++++ .../Runtime_124510/Runtime_124510.csproj | 12 +++++ src/tests/JIT/opt/InstructionCombining/And.cs | 51 +++++++++++++++++-- src/tests/JIT/opt/InstructionCombining/Bic.cs | 43 ++++++++++++++++ 5 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_124510/Runtime_124510.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_124510/Runtime_124510.csproj 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/JitBlue/Runtime_124510/Runtime_124510.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_124510/Runtime_124510.csproj new file mode 100644 index 00000000000000..34ee804273d2c5 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_124510/Runtime_124510.csproj @@ -0,0 +1,12 @@ + + + + true + None + True + + + + + + 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; + } } } From c789232f4e5f582ba5787e080ecfd5dc63ab26a5 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Fri, 20 Feb 2026 08:54:23 +0000 Subject: [PATCH 2/2] Remove separate csproj --- .../JitBlue/Runtime_124510/Runtime_124510.csproj | 12 ------------ src/tests/JIT/Regression/Regression_ro_1.csproj | 1 + 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_124510/Runtime_124510.csproj diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_124510/Runtime_124510.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_124510/Runtime_124510.csproj deleted file mode 100644 index 34ee804273d2c5..00000000000000 --- a/src/tests/JIT/Regression/JitBlue/Runtime_124510/Runtime_124510.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - true - None - True - - - - - - 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 @@ +