From e89057c4828623b13cfb502876cc5c87efd0fd96 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Mon, 24 Feb 2025 14:49:20 +0000 Subject: [PATCH 01/10] arm64: Set zero flag for more comparisons --- src/coreclr/jit/lower.cpp | 6 +-- src/tests/JIT/opt/InstructionCombining/Add.cs | 48 +++++++++++++++++ src/tests/JIT/opt/InstructionCombining/And.cs | 48 +++++++++++++++++ .../{BitwiseClearShift.cs => Bic.cs} | 52 ++++++++++++++++++- src/tests/JIT/opt/InstructionCombining/Neg.cs | 48 +++++++++++++++++ src/tests/JIT/opt/InstructionCombining/Sub.cs | 48 +++++++++++++++++ 6 files changed, 245 insertions(+), 5 deletions(-) rename src/tests/JIT/opt/InstructionCombining/{BitwiseClearShift.cs => Bic.cs} (84%) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 56882a27d32b30..d98cc57e072aa4 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4171,10 +4171,10 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) } } - // Optimize EQ/NE(op_that_sets_zf, 0) into op_that_sets_zf with GTF_SET_FLAGS + SETCC. + // Optimize EQ/NE/GT/GE/LT/LE(op_that_sets_zf, 0) into op_that_sets_zf with GTF_SET_FLAGS + SETCC. LIR::Use use; - if (cmp->OperIs(GT_EQ, GT_NE) && op2->IsIntegralConst(0) && op1->SupportsSettingZeroFlag() && - BlockRange().TryGetUse(cmp, &use)) + if (cmp->OperIs(GT_EQ, GT_NE, GT_GT, GT_GE, GT_LT, GT_LE) && op2->IsIntegralConst(0) && + op1->SupportsSettingZeroFlag() && BlockRange().TryGetUse(cmp, &use)) { op1->gtFlags |= GTF_SET_FLAGS; op1->SetUnusedValue(); diff --git a/src/tests/JIT/opt/InstructionCombining/Add.cs b/src/tests/JIT/opt/InstructionCombining/Add.cs index 8838bd2fe187ae..5954c535a47661 100644 --- a/src/tests/JIT/opt/InstructionCombining/Add.cs +++ b/src/tests/JIT/opt/InstructionCombining/Add.cs @@ -135,6 +135,26 @@ public static int CheckAdd() fail = true; } + if (!AddsGreaterThan(1, 2)) + { + fail = true; + } + + if (!AddsGreaterThanEq(-2, 2)) + { + fail = true; + } + + if (!AddsLessThan(4, -5)) + { + fail = true; + } + + if (!AddsLessThanEq(-6, 6)) + { + fail = true; + } + if (fail) { return 101; @@ -336,5 +356,33 @@ static bool AddsBinOpSingleLine(int a, int b, int c, int d) //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} return (a + b == 0) | (c + d == 0); } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool AddsGreaterThan(int a, int b) + { + //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return a + b > 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool AddsGreaterThanEq(int a, int b) + { + //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return a + b >= 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool AddsLessThan(int a, int b) + { + //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return a + b < 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool AddsLessThanEq(int a, int b) + { + //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return a + b <= 0; + } } } diff --git a/src/tests/JIT/opt/InstructionCombining/And.cs b/src/tests/JIT/opt/InstructionCombining/And.cs index 483b008e3cf11f..f794bc20624a92 100644 --- a/src/tests/JIT/opt/InstructionCombining/And.cs +++ b/src/tests/JIT/opt/InstructionCombining/And.cs @@ -105,6 +105,26 @@ public static int CheckAnd() fail = true; } + if (!AndsGreaterThan(3, 2)) + { + fail = true; + } + + if (!AndsGreaterThanEq(5, 8)) + { + fail = true; + } + + if (!AndsLessThan(-8, -4)) + { + fail = true; + } + + if (!AndsLessThanEq(5, 2)) + { + fail = true; + } + if (fail) { return 101; @@ -264,5 +284,33 @@ static bool AndsBinOpSingleLine(uint a, uint b, uint c, uint d) //ARM64-FULL-LINE: tst {{w[0-9]+}}, {{w[0-9]+}} return ((a & b) == 0) | ((c & d) == 0); } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool AndsGreaterThan(int a, int b) + { + //ARM64-FULL-LINE: ands w0, {{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]+}} + 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]+}} + 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]+}} + return (a & b) <= 0; + } } } diff --git a/src/tests/JIT/opt/InstructionCombining/BitwiseClearShift.cs b/src/tests/JIT/opt/InstructionCombining/Bic.cs similarity index 84% rename from src/tests/JIT/opt/InstructionCombining/BitwiseClearShift.cs rename to src/tests/JIT/opt/InstructionCombining/Bic.cs index 15dca88683a76a..adb7eb7816ee4b 100644 --- a/src/tests/JIT/opt/InstructionCombining/BitwiseClearShift.cs +++ b/src/tests/JIT/opt/InstructionCombining/Bic.cs @@ -5,13 +5,13 @@ using System.Runtime.CompilerServices; using Xunit; -namespace TestBitwiseClearShift +namespace TestBic { public class Program { [MethodImpl(MethodImplOptions.NoInlining)] [Fact] - public static int CheckBitwiseClearShift() + public static int CheckBic() { bool fail = false; @@ -95,6 +95,26 @@ public static int CheckBitwiseClearShift() fail = true; } + if (!BicsGreaterThan(1, 2)) + { + fail = true; + } + + if (!BicsGreaterThanEq(-2, -2)) + { + fail = true; + } + + if (!BicsLessThan(-2, 15)) + { + fail = true; + } + + if (!BicsLessThanEq(-6, 6)) + { + fail = true; + } + if (fail) { return 101; @@ -266,5 +286,33 @@ static bool BicsBinOpSingleLine(uint a, uint b, uint c, uint d) //ARM64-FULL-LINE: bics {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} return ((~a & b) != 0) & ((c & ~d) != 0); } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool BicsGreaterThan(int a, int b) + { + //ARM64-FULL-LINE: bics {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return (a & ~b) > 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool BicsGreaterThanEq(int a, int b) + { + //ARM64-FULL-LINE: bics {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return (a & ~b) >= 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool BicsLessThan(int a, int b) + { + //ARM64-FULL-LINE: bics {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return (a & ~b) < 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool BicsLessThanEq(int a, int b) + { + //ARM64-FULL-LINE: bics {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return (a & ~b) <= 0; + } } } diff --git a/src/tests/JIT/opt/InstructionCombining/Neg.cs b/src/tests/JIT/opt/InstructionCombining/Neg.cs index 2db9a7b70404fd..d994936687894e 100644 --- a/src/tests/JIT/opt/InstructionCombining/Neg.cs +++ b/src/tests/JIT/opt/InstructionCombining/Neg.cs @@ -95,6 +95,26 @@ public static int CheckNeg() fail = true; } + if (!NegsGreaterThan(-1)) + { + fail = true; + } + + if (!NegsGreaterThanEq(0)) + { + fail = true; + } + + if (!NegsLessThan(5)) + { + fail = true; + } + + if (!NegsLessThanEq(20)) + { + fail = true; + } + if (fail) { return 101; @@ -239,5 +259,33 @@ static bool NegsBinOpSingleLine(int a, int b) //ARM64-FULL-LINE: negs {{w[0-9]+}}, {{w[0-9]+}}, LSL #1 return (-(a>>1) != 0) | (-(b<<1) != 0); } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool NegsGreaterThan(int a) + { + //ARM64-FULL-LINE: negs {{w[0-9]+}}, {{w[0-9]+}} + return -a > 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool NegsGreaterThanEq(int a) + { + //ARM64-FULL-LINE: negs {{w[0-9]+}}, {{w[0-9]+}} + return -a >= 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool NegsLessThan(int a) + { + //ARM64-FULL-LINE: negs {{w[0-9]+}}, {{w[0-9]+}} + return -a < 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool NegsLessThanEq(int a) + { + //ARM64-FULL-LINE: negs {{w[0-9]+}}, {{w[0-9]+}} + return -a <= 0; + } } } diff --git a/src/tests/JIT/opt/InstructionCombining/Sub.cs b/src/tests/JIT/opt/InstructionCombining/Sub.cs index e01e15a1655d0d..761745434db57c 100644 --- a/src/tests/JIT/opt/InstructionCombining/Sub.cs +++ b/src/tests/JIT/opt/InstructionCombining/Sub.cs @@ -121,6 +121,26 @@ public static int CheckSub() fail = true; } + if (!SubsGreaterThan(5, 3)) + { + fail = true; + } + + if (!SubsGreaterThanEq(4, 4)) + { + fail = true; + } + + if (!SubsLessThan(7, 8)) + { + fail = true; + } + + if (!SubsLessThanEq(10, 10)) + { + fail = true; + } + if (fail) { return 101; @@ -304,5 +324,33 @@ static bool SubsBinOpSingleLine(int a, int b, int c, int d) //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} return (a - b == 0) | (c - d == 0); } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool SubsGreaterThan(int a, int b) + { + //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return a - b > 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool SubsGreaterThanEq(int a, int b) + { + //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return a - b >= 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool SubsLessThan(int a, int b) + { + //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return a - b < 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool SubsLessThanEq(int a, int b) + { + //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return a - b <= 0; + } } } From 077d8cdb8f4346a3605a0f7626c22b67642ace4f Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Tue, 25 Feb 2025 10:52:32 +0000 Subject: [PATCH 02/10] Rename SupportsSettingZeroFlag to SupportsSettingFlags --- src/coreclr/jit/gentree.cpp | 9 +++++---- src/coreclr/jit/gentree.h | 2 +- src/coreclr/jit/lower.cpp | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 95087e29be2304..fbec2add9f8451 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -19938,12 +19938,13 @@ bool GenTree::IsArrayAddr(GenTreeArrAddr** pArrAddr) } //------------------------------------------------------------------------ -// SupportsSettingZeroFlag: Returns true if this is an arithmetic operation -// whose codegen supports setting the "zero flag" as part of its operation. +// SupportsSettingFlags: Returns true if this is an arithmetic operation +// whose codegen supports setting flags as part of its operation. +// This includes the "zero, negative, carry & overflow flags" // // Return Value: // True if so. A false return does not imply that codegen for the node will -// not trash the zero flag. +// not trash the flags. // // Remarks: // For example, for EQ (AND x y) 0, both xarch and arm64 can emit @@ -19953,7 +19954,7 @@ bool GenTree::IsArrayAddr(GenTreeArrAddr** pArrAddr) // The backend expects any node for which the flags will be consumed to be // marked with GTF_SET_FLAGS. // -bool GenTree::SupportsSettingZeroFlag() +bool GenTree::SupportsSettingFlags() { #if defined(TARGET_XARCH) if (OperIs(GT_AND, GT_OR, GT_XOR, GT_ADD, GT_SUB, GT_NEG)) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index e01d9705eb0d4a..e274faa169da3e 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -2031,7 +2031,7 @@ struct GenTree bool IsArrayAddr(GenTreeArrAddr** pArrAddr); - bool SupportsSettingZeroFlag(); + bool SupportsSettingFlags(); // These are only used for dumping. // The GetRegNum() is only valid in LIR, but the dumping methods are not easily diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index d98cc57e072aa4..c5d1d96640f706 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4174,7 +4174,7 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) // Optimize EQ/NE/GT/GE/LT/LE(op_that_sets_zf, 0) into op_that_sets_zf with GTF_SET_FLAGS + SETCC. LIR::Use use; if (cmp->OperIs(GT_EQ, GT_NE, GT_GT, GT_GE, GT_LT, GT_LE) && op2->IsIntegralConst(0) && - op1->SupportsSettingZeroFlag() && BlockRange().TryGetUse(cmp, &use)) + op1->SupportsSettingFlags() && BlockRange().TryGetUse(cmp, &use)) { op1->gtFlags |= GTF_SET_FLAGS; op1->SetUnusedValue(); From f573c26440947b1342945dbc9ae3c868b1f52654 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Fri, 28 Feb 2025 09:18:35 +0000 Subject: [PATCH 03/10] Fix Bic csproj --- .../{BitwiseClearShift.csproj => Bic.csproj} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/tests/JIT/opt/InstructionCombining/{BitwiseClearShift.csproj => Bic.csproj} (92%) diff --git a/src/tests/JIT/opt/InstructionCombining/BitwiseClearShift.csproj b/src/tests/JIT/opt/InstructionCombining/Bic.csproj similarity index 92% rename from src/tests/JIT/opt/InstructionCombining/BitwiseClearShift.csproj rename to src/tests/JIT/opt/InstructionCombining/Bic.csproj index fa7abf5d8a8d0b..6427e11abbe7dd 100644 --- a/src/tests/JIT/opt/InstructionCombining/BitwiseClearShift.csproj +++ b/src/tests/JIT/opt/InstructionCombining/Bic.csproj @@ -8,7 +8,7 @@ True - + true From 1f488a63e398f23a6e2442a4949b1483a5cfbcb0 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Tue, 4 Mar 2025 13:09:36 +0000 Subject: [PATCH 04/10] Only support other comparisons on ARM64 --- src/coreclr/jit/lower.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 7214e229e2b75b..5850cb247051fb 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4150,8 +4150,11 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) // Optimize EQ/NE/GT/GE/LT/LE(op_that_sets_zf, 0) into op_that_sets_zf with GTF_SET_FLAGS + SETCC. LIR::Use use; - if (cmp->OperIs(GT_EQ, GT_NE, GT_GT, GT_GE, GT_LT, GT_LE) && op2->IsIntegralConst(0) && - op1->SupportsSettingFlags() && BlockRange().TryGetUse(cmp, &use)) + bool supportedCmp = cmp->OperIs(GT_EQ, GT_NE); +#ifdef TARGET_ARM64 + supportedCmp |= cmp->OperIs(GT_GT, GT_GE, GT_LT, GT_LE); +#endif + if (supportedCmp && op2->IsIntegralConst(0) && op1->SupportsSettingFlags() && BlockRange().TryGetUse(cmp, &use)) { op1->gtFlags |= GTF_SET_FLAGS; op1->SetUnusedValue(); From d507f31050a02243becb7738a8f99ca20efe45fa Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Wed, 5 Mar 2025 09:54:50 +0000 Subject: [PATCH 05/10] Add SupportsSettingResultFlags() and restore SupportsSettingZeroFlag() Change-Id: I0dbd2de796a11badde063684761cec93fc1c8355 --- src/coreclr/jit/gentree.cpp | 43 ++++++++++++++++++++++++++++++++----- src/coreclr/jit/gentree.h | 4 +++- src/coreclr/jit/lower.cpp | 8 +++---- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 2f076e9bf8c422..6f7a0b4b155c55 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -19938,13 +19938,12 @@ bool GenTree::IsArrayAddr(GenTreeArrAddr** pArrAddr) } //------------------------------------------------------------------------ -// SupportsSettingFlags: Returns true if this is an arithmetic operation -// whose codegen supports setting flags as part of its operation. -// This includes the "zero, negative, carry & overflow flags" +// SupportsSettingZeroFlag: Returns true if this is an arithmetic operation +// whose codegen supports setting the "zero flag" as part of its operation. // // Return Value: // True if so. A false return does not imply that codegen for the node will -// not trash the flags. +// not trash the zero flag. // // Remarks: // For example, for EQ (AND x y) 0, both xarch and arm64 can emit @@ -19954,7 +19953,7 @@ bool GenTree::IsArrayAddr(GenTreeArrAddr** pArrAddr) // The backend expects any node for which the flags will be consumed to be // marked with GTF_SET_FLAGS. // -bool GenTree::SupportsSettingFlags() +bool GenTree::SupportsSettingZeroFlag() { #if defined(TARGET_XARCH) if (OperIs(GT_AND, GT_OR, GT_XOR, GT_ADD, GT_SUB, GT_NEG)) @@ -19984,6 +19983,40 @@ bool GenTree::SupportsSettingFlags() return false; } +//------------------------------------------------------------------------ +// SupportsSettingResultFlags: Returns true if this is an arithmetic operation +// whose codegen supports setting "result flags" as part of its operation +// other than the "zero flag" +// +// Return Value: +// True if so. A false return does not imply that codegen for the node will +// not trash the result flags. +// +// Remarks: +// For example, for GT (AND x y) 0, both arm64 can emit instructions that +// directly set the flags after the 'AND' and thus no comparison is needed. +// +// The backend expects any node for which the flags will be consumed to be +// marked with GTF_SET_FLAGS. +// +bool GenTree::SupportsSettingResultFlags() +{ +#if defined(TARGET_ARM64) + if (OperIs(GT_AND, GT_AND_NOT, GT_NEG)) + { + return true; + } + + // We do not support setting result flags for madd/msub. + if (OperIs(GT_ADD, GT_SUB) && (!gtGetOp2()->OperIs(GT_MUL) || !gtGetOp2()->isContained())) + { + return true; + } +#endif + + return false; +} + //------------------------------------------------------------------------ // Create: Create or retrieve a field sequence for the given field handle. // diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index e274faa169da3e..6a5d4c075a1ba1 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -2031,7 +2031,9 @@ struct GenTree bool IsArrayAddr(GenTreeArrAddr** pArrAddr); - bool SupportsSettingFlags(); + bool SupportsSettingZeroFlag(); + + bool SupportsSettingResultFlags(); // These are only used for dumping. // The GetRegNum() is only valid in LIR, but the dumping methods are not easily diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 5850cb247051fb..834ed1954dd9bf 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4150,11 +4150,9 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) // Optimize EQ/NE/GT/GE/LT/LE(op_that_sets_zf, 0) into op_that_sets_zf with GTF_SET_FLAGS + SETCC. LIR::Use use; - bool supportedCmp = cmp->OperIs(GT_EQ, GT_NE); -#ifdef TARGET_ARM64 - supportedCmp |= cmp->OperIs(GT_GT, GT_GE, GT_LT, GT_LE); -#endif - if (supportedCmp && op2->IsIntegralConst(0) && op1->SupportsSettingFlags() && BlockRange().TryGetUse(cmp, &use)) + if (((cmp->OperIs(GT_EQ, GT_NE) && op1->SupportsSettingZeroFlag()) || + (cmp->OperIs(GT_GT, GT_GE, GT_LT, GT_LE) && op1->SupportsSettingResultFlags())) && + op2->IsIntegralConst(0) && BlockRange().TryGetUse(cmp, &use)) { op1->gtFlags |= GTF_SET_FLAGS; op1->SetUnusedValue(); From 24aa3403c41082ff6814fbef4d78392c105c405e Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Wed, 5 Mar 2025 10:35:17 +0000 Subject: [PATCH 06/10] * Call SupportsSettingResultFlags() from SupportsSettingZeroFlag() * Update SupportsSettingResultFlags() comments Change-Id: I6c1f9ad86933329575442ce212b322650ac71b34 --- src/coreclr/jit/gentree.cpp | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 6f7a0b4b155c55..78b04dc9197740 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -19955,6 +19955,10 @@ bool GenTree::IsArrayAddr(GenTreeArrAddr** pArrAddr) // bool GenTree::SupportsSettingZeroFlag() { + if (SupportsSettingResultFlags()) { + return true; + } + #if defined(TARGET_XARCH) if (OperIs(GT_AND, GT_OR, GT_XOR, GT_ADD, GT_SUB, GT_NEG)) { @@ -19967,13 +19971,36 @@ bool GenTree::SupportsSettingZeroFlag() return true; } #endif -#elif defined(TARGET_ARM64) +#endif + + return false; +} + +//------------------------------------------------------------------------ +// SupportsSettingResultFlags: Returns true if this is an arithmetic operation +// whose codegen supports setting the carry, overflow, zero and sign flags based +// on the result of the operation. +// +// Return Value: +// True if so. A false return does not imply that codegen for the node will +// not trash the result flags. +// +// Remarks: +// For example, for GT (AND x y) 0, arm64 can emit instructions that +// directly set the flags after the 'AND' and thus no comparison is needed. +// +// The backend expects any node for which the flags will be consumed to be +// marked with GTF_SET_FLAGS. +// +bool GenTree::SupportsSettingResultFlags() +{ +#if defined(TARGET_ARM64) if (OperIs(GT_AND, GT_AND_NOT, GT_NEG)) { return true; } - // We do not support setting zero flag for madd/msub. + // We do not support setting result flags for madd/msub. if (OperIs(GT_ADD, GT_SUB) && (!gtGetOp2()->OperIs(GT_MUL) || !gtGetOp2()->isContained())) { return true; From d42e5988fe899522dae6d22321f915ba848e7229 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Wed, 5 Mar 2025 10:46:37 +0000 Subject: [PATCH 07/10] Fix formatting Change-Id: I76753a9b034934aa0f1c765854421ce9e7b970d3 --- src/coreclr/jit/gentree.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 78b04dc9197740..7bd783e3bbbbdd 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -19955,7 +19955,8 @@ bool GenTree::IsArrayAddr(GenTreeArrAddr** pArrAddr) // bool GenTree::SupportsSettingZeroFlag() { - if (SupportsSettingResultFlags()) { + if (SupportsSettingResultFlags()) + { return true; } From 61c1144fb9fd317253f08c284afe092c6d844d39 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Thu, 6 Mar 2025 12:43:26 +0000 Subject: [PATCH 08/10] Remove duplicated SupportsSettingResultFlags() definition Change-Id: I18402694c3a8b63f71b9cb2b810a0d4db05f4aaf --- src/coreclr/jit/gentree.cpp | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 6d00a46c1c584c..3bd9d10f833857 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -20021,40 +20021,6 @@ bool GenTree::SupportsSettingResultFlags() return false; } -//------------------------------------------------------------------------ -// SupportsSettingResultFlags: Returns true if this is an arithmetic operation -// whose codegen supports setting "result flags" as part of its operation -// other than the "zero flag" -// -// Return Value: -// True if so. A false return does not imply that codegen for the node will -// not trash the result flags. -// -// Remarks: -// For example, for GT (AND x y) 0, both arm64 can emit instructions that -// directly set the flags after the 'AND' and thus no comparison is needed. -// -// The backend expects any node for which the flags will be consumed to be -// marked with GTF_SET_FLAGS. -// -bool GenTree::SupportsSettingResultFlags() -{ -#if defined(TARGET_ARM64) - if (OperIs(GT_AND, GT_AND_NOT, GT_NEG)) - { - return true; - } - - // We do not support setting result flags for madd/msub. - if (OperIs(GT_ADD, GT_SUB) && (!gtGetOp2()->OperIs(GT_MUL) || !gtGetOp2()->isContained())) - { - return true; - } -#endif - - return false; -} - //------------------------------------------------------------------------ // Create: Create or retrieve a field sequence for the given field handle. // From a17decef0f8f0bea2c97c2cc66a74073483810d5 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Thu, 24 Apr 2025 09:36:27 +0000 Subject: [PATCH 09/10] Only build stack frame if skipFrames is greater than zero --- .../src/System/Diagnostics/StackFrame.CoreCLR.cs | 2 +- src/tests/JIT/opt/InstructionCombining/Add.cs | 10 ++++++++++ src/tests/JIT/opt/InstructionCombining/Sub.cs | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreCLR.cs index 8c1850d31e840b..28fe1e0f69b883 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreCLR.cs @@ -38,7 +38,7 @@ private void BuildStackFrame(int skipFrames, bool needFileInfo) skipFrames += StackTrace.CalculateFramesToSkip(StackF, iNumOfFrames); - if ((iNumOfFrames - skipFrames) > 0) + if (((iNumOfFrames - skipFrames) > 0) && (skipFrames >= 0)) { _method = StackF.GetMethodBase(skipFrames); _nativeOffset = StackF.GetOffset(skipFrames); diff --git a/src/tests/JIT/opt/InstructionCombining/Add.cs b/src/tests/JIT/opt/InstructionCombining/Add.cs index 5954c535a47661..69c5c00d061cb6 100644 --- a/src/tests/JIT/opt/InstructionCombining/Add.cs +++ b/src/tests/JIT/opt/InstructionCombining/Add.cs @@ -139,6 +139,11 @@ public static int CheckAdd() { fail = true; } + + if (!AddsGreaterThan(1, int.MaxValue)) + { + fail = true; + } if (!AddsGreaterThanEq(-2, 2)) { @@ -150,6 +155,11 @@ public static int CheckAdd() fail = true; } + if (!AddsLessThan(-2, int.MinValue)) + { + fail = true; + } + if (!AddsLessThanEq(-6, 6)) { fail = true; diff --git a/src/tests/JIT/opt/InstructionCombining/Sub.cs b/src/tests/JIT/opt/InstructionCombining/Sub.cs index 761745434db57c..28992a730edbaa 100644 --- a/src/tests/JIT/opt/InstructionCombining/Sub.cs +++ b/src/tests/JIT/opt/InstructionCombining/Sub.cs @@ -126,6 +126,11 @@ public static int CheckSub() fail = true; } + if (!SubsGreaterThan(1, int.MinValue)) + { + fail = true; + } + if (!SubsGreaterThanEq(4, 4)) { fail = true; @@ -136,6 +141,11 @@ public static int CheckSub() fail = true; } + if (!SubsLessThan(-5, int.MaxValue)) + { + fail = true; + } + if (!SubsLessThanEq(10, 10)) { fail = true; From 00f0b60fadcc3d812b0246b91e4118ea368601b0 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Tue, 6 May 2025 10:16:57 +0000 Subject: [PATCH 10/10] Don't allow ADD/SUB because they don't work with the overflow --- src/coreclr/jit/gentree.cpp | 34 +++++ src/coreclr/jit/lower.cpp | 4 +- src/tests/JIT/opt/InstructionCombining/Add.cs | 131 ++++++++++++++--- .../JIT/opt/InstructionCombining/CmpZero.cs | 139 ++++++++++++++++++ .../opt/InstructionCombining/CmpZero.csproj | 17 +++ src/tests/JIT/opt/InstructionCombining/Sub.cs | 129 +++++++++++++--- 6 files changed, 408 insertions(+), 46 deletions(-) create mode 100644 src/tests/JIT/opt/InstructionCombining/CmpZero.cs create mode 100644 src/tests/JIT/opt/InstructionCombining/CmpZero.csproj diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 5f97f265d89ef2..cc7aaa718c8369 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -20086,6 +20086,40 @@ bool GenTree::SupportsSettingResultFlags() return false; } +//------------------------------------------------------------------------ +// SupportsSettingResultFlags: Returns true if this is an arithmetic operation +// whose codegen supports setting "result flags" as part of its operation +// other than the "zero flag" +// +// Return Value: +// True if so. A false return does not imply that codegen for the node will +// not trash the result flags. +// +// Remarks: +// For example, for GT (AND x y) 0, both arm64 can emit instructions that +// directly set the flags after the 'AND' and thus no comparison is needed. +// +// The backend expects any node for which the flags will be consumed to be +// marked with GTF_SET_FLAGS. +// +bool GenTree::SupportsSettingResultFlags() +{ +#if defined(TARGET_ARM64) + if (OperIs(GT_AND, GT_AND_NOT, GT_NEG)) + { + return true; + } + + // We do not support setting result flags for madd/msub. + if (OperIs(GT_ADD, GT_SUB) && (!gtGetOp2()->OperIs(GT_MUL) || !gtGetOp2()->isContained())) + { + return true; + } +#endif + + return false; +} + //------------------------------------------------------------------------ // Create: Create or retrieve a field sequence for the given field handle. // diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 41d7fa74cbd4fa..d754f7322d0f7a 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4202,9 +4202,11 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) } // Optimize EQ/NE/GT/GE/LT/LE(op_that_sets_zf, 0) into op_that_sets_zf with GTF_SET_FLAGS + SETCC. + // For GT/GE/LT/LE don't allow ADD/SUB, C# has to check for overflow. LIR::Use use; if (((cmp->OperIs(GT_EQ, GT_NE) && op1->SupportsSettingZeroFlag()) || - (cmp->OperIs(GT_GT, GT_GE, GT_LT, GT_LE) && op1->SupportsSettingResultFlags())) && + (cmp->OperIs(GT_GT, GT_GE, GT_LT, GT_LE) && !op1->OperIs(GT_ADD, GT_SUB) && + op1->SupportsSettingResultFlags())) && op2->IsIntegralConst(0) && BlockRange().TryGetUse(cmp, &use)) { op1->gtFlags |= GTF_SET_FLAGS; diff --git a/src/tests/JIT/opt/InstructionCombining/Add.cs b/src/tests/JIT/opt/InstructionCombining/Add.cs index 69c5c00d061cb6..e7932d731b99d9 100644 --- a/src/tests/JIT/opt/InstructionCombining/Add.cs +++ b/src/tests/JIT/opt/InstructionCombining/Add.cs @@ -135,32 +135,102 @@ public static int CheckAdd() fail = true; } - if (!AddsGreaterThan(1, 2)) + if (AddGtZero(-3, 4) != 1) { fail = true; } - - if (!AddsGreaterThan(1, int.MaxValue)) + + if (AddGtZero(3, -3) != 0) + { + fail = true; + } + + if (AddGtZero(-5, -10) != 0) + { + fail = true; + } + + if (AddGtZero(int.MaxValue, 1) != 0) + { + fail = true; + } + + if (AddGtZero(int.MinValue, -1) != 1) + { + fail = true; + } + + if (AddGeZero(1, 1) != 1) + { + fail = true; + } + + if (AddGeZero(0, 0) != 1) + { + fail = true; + } + + if (AddGeZero(-1, -1) != 0) + { + fail = true; + } + + if (AddGeZero(int.MaxValue, 1) != 0) + { + fail = true; + } + + if (AddGeZero(int.MinValue, -1) != 1) + { + fail = true; + } + + if (AddLtZero(1, 1) != 0) + { + fail = true; + } + + if (AddLtZero(0, 0) != 0) + { + fail = true; + } + + if (AddLtZero(-1, -1) != 1) + { + fail = true; + } + + if (AddLtZero(int.MaxValue, 1) != 1) + { + fail = true; + } + + if (AddLtZero(int.MinValue, -1) != 0) { fail = true; } - if (!AddsGreaterThanEq(-2, 2)) + if (AddLeZero(1, 1) != 0) { fail = true; } - if (!AddsLessThan(4, -5)) + if (AddLeZero(0, 0) != 1) { fail = true; } - if (!AddsLessThan(-2, int.MinValue)) + if (AddLeZero(-1, -1) != 1) { fail = true; } - if (!AddsLessThanEq(-6, 6)) + if (AddLeZero(int.MaxValue, 1) != 1) + { + fail = true; + } + + if (AddLeZero(int.MinValue, -1) != 0) { fail = true; } @@ -368,31 +438,46 @@ static bool AddsBinOpSingleLine(int a, int b, int c, int d) } [MethodImpl(MethodImplOptions.NoInlining)] - static bool AddsGreaterThan(int a, int b) - { - //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} - return a + b > 0; + static int AddGtZero(int a, int b) { + //ARM64-FULL-LINE: add {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #0 + //ARM64-FULL-LINE: cset {{x[0-9]+}}, gt + if (a + b > 0) { + return 1; + } + return 0; } [MethodImpl(MethodImplOptions.NoInlining)] - static bool AddsGreaterThanEq(int a, int b) - { - //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} - return a + b >= 0; + static int AddGeZero(int a, int b) { + //ARM64-FULL-LINE: add {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #0 + //ARM64-FULL-LINE: cset {{x[0-9]+}}, ge + if (a + b >= 0) { + return 1; + } + return 0; } [MethodImpl(MethodImplOptions.NoInlining)] - static bool AddsLessThan(int a, int b) - { - //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} - return a + b < 0; + static int AddLtZero(int a, int b) { + //ARM64-FULL-LINE: add {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: lsr {{w[0-9]+}}, {{w[0-9]+}}, #31 + if (a + b < 0) { + return 1; + } + return 0; } [MethodImpl(MethodImplOptions.NoInlining)] - static bool AddsLessThanEq(int a, int b) - { - //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} - return a + b <= 0; + static int AddLeZero(int a, int b) { + //ARM64-FULL-LINE: add {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #0 + //ARM64-FULL-LINE: cset {{x[0-9]+}}, le + if (a + b <= 0) { + return 1; + } + return 0; } } } diff --git a/src/tests/JIT/opt/InstructionCombining/CmpZero.cs b/src/tests/JIT/opt/InstructionCombining/CmpZero.cs new file mode 100644 index 00000000000000..f6e89bce9faa38 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/CmpZero.cs @@ -0,0 +1,139 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +namespace TestCompareZero +{ + public class Program + { + [MethodImpl(MethodImplOptions.NoInlining)] + [Fact] + public static int CheckCompareZero() + { + bool fail = false; + + if (AddGtZero(-3, 4) != 1) fail = true; + if (AddGtZero(3, -3) != 0) fail = true; + if (AddGtZero(-5, -10) != 0) fail = true; + if (AddGtZero(int.MaxValue, 1) != 0) fail = true; + if (AddGtZero(int.MinValue, -1) != 1) fail = true; + + if (AddGeZero(1, 1) != 1) fail = true; + if (AddGeZero(0, 0) != 1) fail = true; + if (AddGeZero(-1, -1) != 0) fail = true; + if (AddGeZero(int.MaxValue, 1) != 0) fail = true; + if (AddGeZero(int.MinValue, -1) != 1) fail = true; + + if (AddLtZero(1, 1) != 0) fail = true; + if (AddLtZero(0, 0) != 0) fail = true; + if (AddLtZero(-1, -1) != 1) fail = true; + if (AddLtZero(int.MaxValue, 1) != 1) fail = true; + if (AddLtZero(int.MinValue, -1) != 0) fail = true; + + if (AddLeZero(1, 1) != 0) fail = true; + if (AddLeZero(0, 0) != 1) fail = true; + if (AddLeZero(-1, -1) != 1) fail = true; + if (AddLeZero(int.MaxValue, 1) != 1) fail = true; + if (AddLeZero(int.MinValue, -1) != 0) fail = true; + + if (SubGtZero(5, 3) != 1) fail = true; + if (SubGtZero(3, 3) != 0) fail = true; + if (SubGtZero(2, 4) != 0) fail = true; + if (SubGtZero(int.MinValue, 1) != 1) fail = true; + if (SubGtZero(int.MaxValue, -1) != 0) fail = true; + + if (SubGeZero(5, 3) != 1) fail = true; + if (SubGeZero(3, 3) != 1) fail = true; + if (SubGeZero(2, 4) != 0) fail = true; + if (SubGeZero(int.MinValue, 1) != 1) fail = true; + if (SubGeZero(int.MaxValue, -1) != 0) fail = true; + + if (SubLtZero(5, 3) != 0) fail = true; + if (SubLtZero(3, 3) != 0) fail = true; + if (SubLtZero(2, 4) != 1) fail = true; + if (SubLtZero(int.MinValue, 1) != 0) fail = true; + if (SubLtZero(int.MaxValue, -1) != 1) fail = true; + + if (SubLeZero(5, 3) != 0) fail = true; + if (SubLeZero(3, 3) != 1) fail = true; + if (SubLeZero(2, 4) != 1) fail = true; + if (SubLeZero(int.MinValue, 1) != 0) fail = true; + if (SubLeZero(int.MaxValue, -1) != 1) fail = true; + + if (fail) + { + return 101; + } + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AddGtZero(int a, int b) { + if (a + b > 0) { + return 1; + } + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AddGeZero(int a, int b) { + if (a + b >= 0) { + return 1; + } + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AddLtZero(int a, int b) { + if (a + b < 0) { + return 1; + } + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AddLeZero(int a, int b) { + if (a + b <= 0) { + return 1; + } + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int SubGtZero(int a, int b) { + if (a - b > 0) { + return 1; + } + return 0; + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + static int SubGeZero(int a, int b) { + if (a - b >= 0) { + return 1; + } + return 0; + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + static int SubLtZero(int a, int b) { + if (a - b < 0) { + return 1; + } + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int SubLeZero(int a, int b) { + if (a - b <= 0) { + return 1; + } + return 0; + } + } +} diff --git a/src/tests/JIT/opt/InstructionCombining/CmpZero.csproj b/src/tests/JIT/opt/InstructionCombining/CmpZero.csproj new file mode 100644 index 00000000000000..5487a7ec88d8ff --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/CmpZero.csproj @@ -0,0 +1,17 @@ + + + + true + + + None + True + + + + true + + + + + diff --git a/src/tests/JIT/opt/InstructionCombining/Sub.cs b/src/tests/JIT/opt/InstructionCombining/Sub.cs index 28992a730edbaa..d635c537beeb51 100644 --- a/src/tests/JIT/opt/InstructionCombining/Sub.cs +++ b/src/tests/JIT/opt/InstructionCombining/Sub.cs @@ -121,32 +121,102 @@ public static int CheckSub() fail = true; } - if (!SubsGreaterThan(5, 3)) + if (SubGtZero(5, 3) != 1) { fail = true; } - if (!SubsGreaterThan(1, int.MinValue)) + if (SubGtZero(3, 3) != 0) { fail = true; } - if (!SubsGreaterThanEq(4, 4)) + if (SubGtZero(2, 4) != 0) { fail = true; } - if (!SubsLessThan(7, 8)) + if (SubGtZero(int.MinValue, 1) != 1) { fail = true; } - if (!SubsLessThan(-5, int.MaxValue)) + if (SubGtZero(int.MaxValue, -1) != 0) { fail = true; } - if (!SubsLessThanEq(10, 10)) + if (SubGeZero(5, 3) != 1) + { + fail = true; + } + + if (SubGeZero(3, 3) != 1) + { + fail = true; + } + + if (SubGeZero(2, 4) != 0) + { + fail = true; + } + + if (SubGeZero(int.MinValue, 1) != 1) + { + fail = true; + } + + if (SubGeZero(int.MaxValue, -1) != 0) + { + fail = true; + } + + if (SubLtZero(5, 3) != 0) + { + fail = true; + } + + if (SubLtZero(3, 3) != 0) + { + fail = true; + } + + if (SubLtZero(2, 4) != 1) + { + fail = true; + } + + if (SubLtZero(int.MinValue, 1) != 0) + { + fail = true; + } + + if (SubLtZero(int.MaxValue, -1) != 1) + { + fail = true; + } + + if (SubLeZero(5, 3) != 0) + { + fail = true; + } + + if (SubLeZero(3, 3) != 1) + { + fail = true; + } + + if (SubLeZero(2, 4) != 1) + { + fail = true; + } + + if (SubLeZero(int.MinValue, 1) != 0) + { + fail = true; + } + + if (SubLeZero(int.MaxValue, -1) != 1) { fail = true; } @@ -336,31 +406,46 @@ static bool SubsBinOpSingleLine(int a, int b, int c, int d) } [MethodImpl(MethodImplOptions.NoInlining)] - static bool SubsGreaterThan(int a, int b) - { - //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} - return a - b > 0; + static int SubGtZero(int a, int b) { + //ARM64-FULL-LINE: sub {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #0 + //ARM64-FULL-LINE: cset {{x[0-9]+}}, gt + if (a - b > 0) { + return 1; + } + return 0; } [MethodImpl(MethodImplOptions.NoInlining)] - static bool SubsGreaterThanEq(int a, int b) - { - //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} - return a - b >= 0; + static int SubGeZero(int a, int b) { + //ARM64-FULL-LINE: sub {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #0 + //ARM64-FULL-LINE: cset {{x[0-9]+}}, ge + if (a - b >= 0) { + return 1; + } + return 0; } [MethodImpl(MethodImplOptions.NoInlining)] - static bool SubsLessThan(int a, int b) - { - //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} - return a - b < 0; + static int SubLtZero(int a, int b) { + //ARM64-FULL-LINE: sub {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: lsr {{w[0-9]+}}, {{w[0-9]+}}, #31 + if (a - b < 0) { + return 1; + } + return 0; } [MethodImpl(MethodImplOptions.NoInlining)] - static bool SubsLessThanEq(int a, int b) - { - //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} - return a - b <= 0; + static int SubLeZero(int a, int b) { + //ARM64-FULL-LINE: sub {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, #0 + //ARM64-FULL-LINE: cset {{x[0-9]+}}, le + if (a - b <= 0) { + return 1; + } + return 0; } } }