aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorRichard Earnshaw <rearnsha@arm.com>2021-05-13 11:42:58 +0100
committerRichard Earnshaw <rearnsha@arm.com>2021-05-13 11:44:45 +0100
commita451598b2c02e1ca3c62fea272d73a9f31922252 (patch)
treeb63c39fbeeb19ad8816211179bfb777643c7ce4e /gcc
parent829c4bea06600ea4201462f91ce6d76ca21fdb35 (diff)
downloadgcc-a451598b2c02e1ca3c62fea272d73a9f31922252.zip
gcc-a451598b2c02e1ca3c62fea272d73a9f31922252.tar.gz
gcc-a451598b2c02e1ca3c62fea272d73a9f31922252.tar.bz2
arm: correctly handle inequality comparisons against max constants [PR100563]
Normally we expect the gimple optimizers to fold away comparisons that are always true, but at some lower optimization levels this is not always the case, so the back-end has to be able to generate correct code in these cases. In this example, we have a comparison of the form (unsigned long long) op <= ~0ULL which, of course is always true. Normally, in the arm back-end we handle these expansions where the immediate cannot be handled directly by adding 1 to the constant and then adjusting the comparison operator: (unsigned long long) op < CONST + 1 but we cannot do that when the constant is already the largest value. Fortunately, we observe that the comparisons we need to handle this way are either always true or always false, so instead of forming a comparison against the maximum value, we can replace it with a comparison against the minimum value (which just happens to also be a constant we can handle. So op1 <= ~0ULL -> op1 >= 0U op1 > ~0ULL -> op1 < 0U op1 <= LONG_LONG_INT_MAX -> op1 >= (-LONG_LONG_INT_MAX - 1) op1 > LONG_LONG_INT_MAX -> op1 < (-LONG_LONG_INT_MAX - 1) gcc: PR target/100563 * config/arm/arm.c (arm_canonicalize_comparison): Correctly canonicalize DImode inequality comparisons against the maximum integral value. gcc/testsuite: * gcc.dg/pr100563.c: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/config/arm/arm.c29
-rw-r--r--gcc/testsuite/gcc.dg/pr100563.c9
2 files changed, 34 insertions, 4 deletions
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 2962071..d0c0c50 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -5563,9 +5563,20 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
return;
*op1 = GEN_INT (i + 1);
*code = *code == GT ? GE : LT;
- return;
}
- break;
+ else
+ {
+ /* GT maxval is always false, LE maxval is always true.
+ We can't fold that away here as we must make a
+ comparison, but we can fold them to comparisons
+ with the same result that can be handled:
+ op0 GT maxval -> op0 LT minval
+ op0 LE maxval -> op0 GE minval
+ where minval = (-maxval - 1). */
+ *op1 = GEN_INT (-maxval - 1);
+ *code = *code == GT ? LT : GE;
+ }
+ return;
case GTU:
case LEU:
@@ -5578,9 +5589,19 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
return;
*op1 = GEN_INT (i + 1);
*code = *code == GTU ? GEU : LTU;
- return;
}
- break;
+ else
+ {
+ /* GTU ~0 is always false, LEU ~0 is always true.
+ We can't fold that away here as we must make a
+ comparison, but we can fold them to comparisons
+ with the same result that can be handled:
+ op0 GTU ~0 -> op0 LTU 0
+ op0 LEU ~0 -> op0 GEU 0. */
+ *op1 = const0_rtx;
+ *code = *code == GTU ? LTU : GEU;
+ }
+ return;
default:
gcc_unreachable ();
diff --git a/gcc/testsuite/gcc.dg/pr100563.c b/gcc/testsuite/gcc.dg/pr100563.c
new file mode 100644
index 0000000..812eb9e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr100563.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-options "-Og" } */
+unsigned long long e(void);
+void f(int);
+void a() {
+ short b = -1, c = (int)&b;
+ unsigned long long d = e();
+ f(b >= d);
+}