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/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index 54af242fa6cd28..cc7aaa718c8369 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -20024,6 +20024,11 @@ 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))
{
@@ -20036,18 +20041,76 @@ 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))
{
return true;
}
- // We do not support setting zero flag for madd/msub.
+ // We do not support setting result flags if neg has a contained mul
if (OperIs(GT_NEG) && (!gtGetOp1()->OperIs(GT_MUL) || !gtGetOp1()->isContained()))
{
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;
+}
+
+//------------------------------------------------------------------------
+// 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;
diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h
index e57d334a2805b5..6be0887bf29359 100644
--- a/src/coreclr/jit/gentree.h
+++ b/src/coreclr/jit/gentree.h
@@ -2034,6 +2034,8 @@ struct GenTree
bool SupportsSettingZeroFlag();
+ bool SupportsSettingResultFlags();
+
// These are only used for dumping.
// The GetRegNum() is only valid in LIR, but the dumping methods are not easily
// modified to check this.
diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp
index cc3cf97d919336..d754f7322d0f7a 100644
--- a/src/coreclr/jit/lower.cpp
+++ b/src/coreclr/jit/lower.cpp
@@ -4201,10 +4201,13 @@ 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.
+ // 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) && op2->IsIntegralConst(0) && op1->SupportsSettingZeroFlag() &&
- BlockRange().TryGetUse(cmp, &use))
+ if (((cmp->OperIs(GT_EQ, GT_NE) && op1->SupportsSettingZeroFlag()) ||
+ (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;
op1->SetUnusedValue();
diff --git a/src/tests/JIT/opt/InstructionCombining/Add.cs b/src/tests/JIT/opt/InstructionCombining/Add.cs
index 8838bd2fe187ae..e7932d731b99d9 100644
--- a/src/tests/JIT/opt/InstructionCombining/Add.cs
+++ b/src/tests/JIT/opt/InstructionCombining/Add.cs
@@ -135,6 +135,106 @@ public static int CheckAdd()
fail = true;
}
+ 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 (fail)
{
return 101;
@@ -336,5 +436,48 @@ 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 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 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 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 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/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/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
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/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..d635c537beeb51 100644
--- a/src/tests/JIT/opt/InstructionCombining/Sub.cs
+++ b/src/tests/JIT/opt/InstructionCombining/Sub.cs
@@ -121,6 +121,106 @@ public static int CheckSub()
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;
@@ -304,5 +404,48 @@ 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 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 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 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 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;
+ }
}
}