This adds a few more minmax cmp operand simplifications which were missed before.
`MIN(a,b) < a` -> `a > b`
`MIN(a,b) >= a` -> `a <= b`
`MAX(a,b) > a` -> `a < b`
`MAX(a,b) <= a` -> `a >= b`
OK? Bootstrapped and tested on x86_64-linux-gnu.
Note gcc.dg/pr96708-negative.c needed to updated to remove the
check for MIN/MAX as they have been optimized (correctly) away.
PR tree-optimization/111364
gcc/ChangeLog:
* match.pd (`MIN (X, Y) == X`): Extend
to min/lt, min/ge, max/gt, max/le.
gcc/testsuite/ChangeLog:
* gcc.c-torture/execute/minmaxcmp-1.c: New test.
* gcc.dg/tree-ssa/minmaxcmp-2.c: New test.
* gcc.dg/pr96708-negative.c: Update testcase.
* gcc.dg/pr96708-positive.c: Add comment about `return 0`.
(maxmin @0 (bit_not @1))))
/* MIN (X, Y) == X -> X <= Y */
-(for minmax (min min max max)
- cmp (eq ne eq ne )
- out (le gt ge lt )
+/* MIN (X, Y) < X -> X > Y */
+/* MIN (X, Y) >= X -> X <= Y */
+(for minmax (min min min min max max max max)
+ cmp (eq ne lt ge eq ne gt le )
+ out (le gt gt le ge lt lt ge )
(simplify
(cmp:c (minmax:c @0 @1) @0)
(if (ANY_INTEGRAL_TYPE_P (TREE_TYPE (@0)))
--- /dev/null
+#define func(vol, op1, op2) \
+_Bool op1##_##op2##_##vol (int a, int b) \
+{ \
+ vol int x = op_##op1(a, b); \
+ return op_##op2(x, a); \
+}
+
+#define op_lt(a, b) ((a) < (b))
+#define op_le(a, b) ((a) <= (b))
+#define op_eq(a, b) ((a) == (b))
+#define op_ne(a, b) ((a) != (b))
+#define op_gt(a, b) ((a) > (b))
+#define op_ge(a, b) ((a) >= (b))
+#define op_min(a, b) ((a) < (b) ? (a) : (b))
+#define op_max(a, b) ((a) > (b) ? (a) : (b))
+
+
+#define funcs(a) \
+ a(min,lt) \
+ a(max,lt) \
+ a(min,gt) \
+ a(max,gt) \
+ a(min,le) \
+ a(max,le) \
+ a(min,ge) \
+ a(max,ge) \
+ a(min,ne) \
+ a(max,ne) \
+ a(min,eq) \
+ a(max,eq)
+
+#define funcs1(a,b) \
+func(,a,b) \
+func(volatile,a,b)
+
+funcs(funcs1)
+
+#define test(op1,op2) \
+do { \
+ if (op1##_##op2##_(x,y) != op1##_##op2##_volatile(x,y)) \
+ __builtin_abort(); \
+} while(0);
+
+int main()
+{
+ for(int x = -10; x < 10; x++)
+ for(int y = -10; y < 10; y++)
+ {
+ funcs(test)
+ }
+}
return 0;
}
-/* { dg-final { scan-tree-dump-times "MAX_EXPR" 2 "optimized" } } */
-/* { dg-final { scan-tree-dump-times "MIN_EXPR" 2 "optimized" } } */
+/* Even though test[1-4] originally has MIN/MAX, those can be optimized away
+ into just comparing a and b arguments. */
/* { dg-final { scan-tree-dump-times "return 0;" 1 "optimized" } } */
/* { dg-final { scan-tree-dump-not { "return 1;" } "optimized" } } */
return 0;
}
+/* Note main has one `return 0`. */
/* { dg-final { scan-tree-dump-times "return 0;" 3 "optimized" } } */
/* { dg-final { scan-tree-dump-times "return 1;" 2 "optimized" } } */
/* { dg-final { scan-tree-dump-not { "MAX_EXPR" } "optimized" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-original" } */
+/* PR tree-optimization/111364 */
+
+#define min1(a, b) ((a) < (b) ? (a) : (b))
+#define max1(a, b) ((a) > (b) ? (a) : (b))
+
+int minlt(int a, int b)
+{
+ return min1(a, b) < a; // b < a or a > b
+}
+/* { dg-final { scan-tree-dump "return a > b;|return b < a;" "original" } } */
+
+int minge(int c, int d)
+{
+ return min1(c, d) >= c; // d >= c or c <= d
+}
+/* { dg-final { scan-tree-dump "return c <= d;|return d <= c;" "original" } } */
+
+int maxgt(int e, int f)
+{
+ return max1(e, f) > e; // e > f or f < e
+}
+/* { dg-final { scan-tree-dump "return e < f;|return f > e;" "original" } } */
+
+int maxle(int x, int y)
+{
+ return max1(x, y) <= x; // y <= x or x >= y
+}
+/* { dg-final { scan-tree-dump "return x >= y;|return y <= x;" "original" } } */