@@ -9015,7 +9015,7 @@ tree_expr_nonzero_warnv_p (tree t, bool *strict_overflow_p)
/* Return true when T is an address and is known to be nonzero.
Handle warnings about undefined signed overflow. */
-static bool
+bool
tree_expr_nonzero_p (tree t)
{
bool ret, strict_overflow_p;
@@ -169,6 +169,7 @@ extern tree size_diffop_loc (location_t, tree, tree);
#define non_lvalue(T) non_lvalue_loc (UNKNOWN_LOCATION, T)
extern tree non_lvalue_loc (location_t, tree);
+extern bool tree_expr_nonzero_p (tree);
extern bool tree_expr_nonnegative_p (tree);
extern bool tree_expr_nonnegative_warnv_p (tree, bool *, int = 0);
extern tree make_range (tree, int *, tree *, tree *, bool *);
@@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see
zerop
CONSTANT_CLASS_P
tree_expr_nonnegative_p
+ tree_expr_nonzero_p
integer_valued_real_p
integer_pow2p
HONOR_NANS)
@@ -1017,7 +1018,31 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
&& tree_nop_conversion_p (type, TREE_TYPE (@1)))
(convert (bit_and (bit_not @1) @0))))
+/* For integral types with undefined overflow and C != 0 fold
+ x * C EQ/NE y * C into x EQ/NE y. */
+(for cmp (eq ne)
+ (simplify
+ (cmp (mult:c @0 @1) (mult:c @2 @1))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
+ && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0))
+ && tree_expr_nonzero_p (@1))
+ (cmp @0 @2))))
+
+/* For integral types with undefined overflow and C != 0 fold
+ x * C RELOP y * C into:
+ x RELOP y for nonnegative C
+ y RELOP x for negative C */
+(for cmp (lt gt le ge)
+ (simplify
+ (cmp (mult:c @0 @1) (mult:c @2 @1))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
+ && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0)))
+ (if (tree_expr_nonnegative_p (@1) && tree_expr_nonzero_p (@1))
+ (cmp @0 @2)
+ (if (TREE_CODE (@1) == INTEGER_CST
+ && wi::lt_p (@1, 0, TYPE_SIGN (TREE_TYPE (@1))))
+ (cmp @2 @0))))))
/* ((X inner_op C0) outer_op C1)
With X being a tree where value_range has reasoned certain bits to always be
new file mode 100644
@@ -0,0 +1,51 @@
+/* PR middle-end/31096 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+#define zero(name, op) \
+int name (int a, int b) \
+{ return a * 0 op b * 0; }
+
+zero(zeq, ==) zero(zne, !=) zero(zlt, <)
+zero(zgt, >) zero(zge, >=) zero(zle, <=)
+
+#define unsign_pos(name, op) \
+int name (unsigned a, unsigned b) \
+{ return a * 4 op b * 4; }
+
+unsign_pos(upeq, ==) unsign_pos(upne, !=) unsign_pos(uplt, <)
+unsign_pos(upgt, >) unsign_pos(upge, >=) unsign_pos(uple, <=)
+
+#define unsign_neg(name, op) \
+int name (unsigned a, unsigned b) \
+{ return a * -2 op b * -2; }
+
+unsign_neg(uneq, ==) unsign_neg(unne, !=) unsign_neg(unlt, <)
+unsign_neg(ungt, >) unsign_neg(unge, >=) unsign_neg(unle, <=)
+
+#define float(name, op) \
+int name (float a, float b) \
+{ return a * 5 op b * 5; }
+
+float(feq, ==) float(fne, !=) float(flt, <)
+float(fgt, >) float(fge, >=) float(fle, <=)
+
+#define float_val(name, op) \
+int name (int a, int b) \
+{ return a * 54.0 op b * 54.0; }
+
+float_val(fveq, ==) float_val(fvne, !=) float_val(fvlt, <)
+float_val(fvgt, >) float_val(fvge, >=) float_val(fvle, <=)
+
+#define vec(name, op) \
+int name (int a, int b) \
+{ int c[10]; return a * c[1] op b * c[1]; }
+
+vec(veq, ==) vec(vne, !=) vec(vlt, <)
+vec(vgt, >) vec(vge, >=) vec(vle, <=)
+
+/* { dg-final { scan-tree-dump-times "\\(D\\) \\* 4" 24 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "\\(D\\) \\* 4294967294" 12 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "\\(D\\) \\* 5\\.0e\\+0" 12 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "\\* 5\\.4e\\+1" 12 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "\\(D\\) \\* c\\\$1_8\\(D\\)" 12 "optimized" } } */
new file mode 100644
@@ -0,0 +1,36 @@
+/* PR middle-end/31096 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+#define signval_pos(name, op) \
+int name (int a, int b) \
+{ return a * 4 op b * 4; }
+
+signval_pos(peq, ==) signval_pos(pne, !=) signval_pos(plt, <)
+signval_pos(pgt, >) signval_pos(pge, >=) signval_pos(ple, <=)
+
+#define signval_neg(name, op) \
+int name (int a, int b) \
+{ return a * -23 op b * -23; }
+
+signval_neg(neq, ==) signval_neg(nne, !=) signval_neg(nlt, <)
+signval_neg(ngt, >) signval_neg(nge, >=) signval_neg(nle, <=)
+
+#define vec_pos(name, op) \
+int name (int a[10], int b[10]) \
+{ return a[3] * 4 op b[8] * 4; }
+
+vec_pos(vpeq, ==) vec_pos(vpne, !=) vec_pos(vplt, <)
+vec_pos(vpgt, >) vec_pos(vpge, >=) vec_pos(vple, <=)
+
+#define vec_neg(name, op) \
+int name (int a[10], int b[10]) \
+{ return a[3] * -23 op b[8] * -23; }
+
+vec_neg(vneq, ==) vec_neg(vnne, !=) vec_neg(vnlt, <)
+vec_neg(vngt, >) vec_neg(vnge, >=) vec_neg(vnle, <=)
+
+/* { dg-final { scan-tree-dump-not "\\(D\\) \\* 4" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "\\(D\\) \\* -23" "optimized" } } */
+/* { dg-final { scan-tree-dump-times "_1 = b_2\\(D\\)" 4 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "_1 = MEM\\\[\\(int \\*\\)b" 4 "optimized" } } */