+2016-11-23 Bin Cheng <bin.cheng@arm.com>
+
+ * fold-const.c (fold_cond_expr_with_comparison): Move simplification
+ for A cmp C1 ? A : C2 to below, also simplify remaining code.
+ * match.pd: Move and extend simplification from above to here:
+ (cond (cmp (convert1? x) c1) (convert2? x) c2) -> (minmax (x c)).
+ * tree-if-conv.c (ifcvt_follow_ssa_use_edges): New func.
+ (predicate_scalar_phi): Call fold_stmt using the new valueize func.
+
2016-11-23 Martin Liska <mliska@suse.cz>
Martin Jambor <mjambor@suse.cz>
}
}
- /* If this is A op C1 ? A : C2 with C1 and C2 constant integers,
- we might still be able to simplify this. For example,
- if C1 is one less or one more than C2, this might have started
- out as a MIN or MAX and been transformed by this function.
- Only good for INTEGER_TYPEs, because we need TYPE_MAX_VALUE. */
+ /* If this is A == C1 ? A : C2 with C1 and C2 constant integers,
+ we simplify it into A == C1 ? C1 : C2. */
- if (INTEGRAL_TYPE_P (type)
+ if (comp_code == EQ_EXPR
+ && INTEGRAL_TYPE_P (type)
&& TREE_CODE (arg01) == INTEGER_CST
+ && TREE_CODE (arg1) != INTEGER_CST
&& TREE_CODE (arg2) == INTEGER_CST)
- switch (comp_code)
- {
- case EQ_EXPR:
- if (TREE_CODE (arg1) == INTEGER_CST)
- break;
- /* We can replace A with C1 in this case. */
- arg1 = fold_convert_loc (loc, type, arg01);
- return fold_build3_loc (loc, COND_EXPR, type, arg0, arg1, arg2);
-
- case LT_EXPR:
- /* If C1 is C2 + 1, this is min(A, C2), but use ARG00's type for
- MIN_EXPR, to preserve the signedness of the comparison. */
- if (! operand_equal_p (arg2, TYPE_MAX_VALUE (type),
- OEP_ONLY_CONST)
- && operand_equal_p (arg01,
- const_binop (PLUS_EXPR, arg2,
- build_int_cst (type, 1)),
- OEP_ONLY_CONST))
- {
- tem = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (arg00), arg00,
- fold_convert_loc (loc, TREE_TYPE (arg00),
- arg2));
- return fold_convert_loc (loc, type, tem);
- }
- break;
-
- case LE_EXPR:
- /* If C1 is C2 - 1, this is min(A, C2), with the same care
- as above. */
- if (! operand_equal_p (arg2, TYPE_MIN_VALUE (type),
- OEP_ONLY_CONST)
- && operand_equal_p (arg01,
- const_binop (MINUS_EXPR, arg2,
- build_int_cst (type, 1)),
- OEP_ONLY_CONST))
- {
- tem = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (arg00), arg00,
- fold_convert_loc (loc, TREE_TYPE (arg00),
- arg2));
- return fold_convert_loc (loc, type, tem);
- }
- break;
-
- case GT_EXPR:
- /* If C1 is C2 - 1, this is max(A, C2), but use ARG00's type for
- MAX_EXPR, to preserve the signedness of the comparison. */
- if (! operand_equal_p (arg2, TYPE_MIN_VALUE (type),
- OEP_ONLY_CONST)
- && operand_equal_p (arg01,
- const_binop (MINUS_EXPR, arg2,
- build_int_cst (type, 1)),
- OEP_ONLY_CONST))
- {
- tem = fold_build2_loc (loc, MAX_EXPR, TREE_TYPE (arg00), arg00,
- fold_convert_loc (loc, TREE_TYPE (arg00),
- arg2));
- return fold_convert_loc (loc, type, tem);
- }
- break;
-
- case GE_EXPR:
- /* If C1 is C2 + 1, this is max(A, C2), with the same care as above. */
- if (! operand_equal_p (arg2, TYPE_MAX_VALUE (type),
- OEP_ONLY_CONST)
- && operand_equal_p (arg01,
- const_binop (PLUS_EXPR, arg2,
- build_int_cst (type, 1)),
- OEP_ONLY_CONST))
- {
- tem = fold_build2_loc (loc, MAX_EXPR, TREE_TYPE (arg00), arg00,
- fold_convert_loc (loc, TREE_TYPE (arg00),
- arg2));
- return fold_convert_loc (loc, type, tem);
- }
- break;
- case NE_EXPR:
- break;
- default:
- gcc_unreachable ();
- }
+ {
+ arg1 = fold_convert_loc (loc, type, arg01);
+ return fold_build3_loc (loc, COND_EXPR, type, arg0, arg1, arg2);
+ }
return NULL_TREE;
}
(if (integer_zerop (@0))
@2)))
+/* Simplification moved from fold_cond_expr_with_comparison. It may also
+ be extended. */
+/* (cond (cmp (convert1? x) c1) (convert2? x) c2) -> (minmax (x c)) if:
+ 1) Conversions are type widening from smaller type.
+ 2) Const c1 equals to c2 after canonicalizing comparison.
+ 3) Comparison has tree code LT, LE, GT or GE.
+ This specific pattern is needed when (cmp (convert x) c) may not
+ be simplified by comparison patterns because of multiple uses of
+ x. It also makes sense here because simplifying across multiple
+ referred var is always benefitial for complicated cases. */
+(for cmp (lt le gt ge)
+ (simplify
+ (cond (cmp@0 (convert1? @1) INTEGER_CST@3) (convert2? @1) INTEGER_CST@2)
+ (with
+ {
+ tree from_type = TREE_TYPE (@1);
+ tree c1_type = TREE_TYPE (@3), c2_type = TREE_TYPE (@2);
+ enum tree_code code = TREE_CODE (@0), cmp_code = TREE_CODE (@0);
+
+ if (int_fits_type_p (@2, from_type)
+ && (types_match (c1_type, from_type)
+ || (TYPE_PRECISION (c1_type) > TYPE_PRECISION (from_type)
+ && (TYPE_UNSIGNED (from_type)
+ || TYPE_SIGN (c1_type) == TYPE_SIGN (from_type))))
+ && (types_match (c2_type, from_type)
+ || (TYPE_PRECISION (c2_type) > TYPE_PRECISION (from_type)
+ && (TYPE_UNSIGNED (from_type)
+ || TYPE_SIGN (c2_type) == TYPE_SIGN (from_type)))))
+ {
+ if (wi::to_widest (@3) == (wi::to_widest (@2) - 1))
+ {
+ /* X <= Y - 1 equals to X < Y. */
+ if (cmp_code == LE_EXPR)
+ code = LT_EXPR;
+ /* X > Y - 1 equals to X >= Y. */
+ if (cmp_code == GT_EXPR)
+ code = GE_EXPR;
+ }
+ if (wi::to_widest (@3) == (wi::to_widest (@2) + 1))
+ {
+ /* X < Y + 1 equals to X <= Y. */
+ if (cmp_code == LT_EXPR)
+ code = LE_EXPR;
+ /* X >= Y + 1 equals to X > Y. */
+ if (cmp_code == GE_EXPR)
+ code = GT_EXPR;
+ }
+ if (code != cmp_code || wi::to_widest (@2) == wi::to_widest (@3))
+ {
+ if (cmp_code == LT_EXPR || cmp_code == LE_EXPR)
+ code = MIN_EXPR;
+ if (cmp_code == GT_EXPR || cmp_code == GE_EXPR)
+ code = MAX_EXPR;
+ }
+ }
+ }
+ (if (code == MAX_EXPR)
+ (convert (max @1 (convert:from_type @2)))
+ (if (code == MIN_EXPR)
+ (convert (min @1 (convert:from_type @2))))))))
+
(for cnd (cond vec_cond)
/* A ? B : (A ? X : C) -> A ? B : C. */
(simplify
+2016-11-23 Bin Cheng <bin.cheng@arm.com>
+
+ * gcc.dg/fold-cond_expr-1.c: New test.
+ * gcc.dg/fold-condcmpconv-1.c: New test.
+ * gcc.dg/fold-condcmpconv-2.c: New test.
+
2016-11-23 Richard Biener <rguenther@suse.de>
PR middle-end/71762
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int
+min1 (signed char op1, signed char op2)
+{
+ return (op1 < 25) ? (int)op1 : 24;
+}
+int
+min2 (signed char op1, signed char op2)
+{
+ return (op1 <= 24) ? (int)op1 : 25;
+}
+int
+min3 (unsigned char op1, unsigned char op2)
+{
+ return (op1 < 25) ? (unsigned int)op1 : 24;
+}
+int
+min4 (unsigned char op1, unsigned char op2)
+{
+ return (op1 <= 24) ? (unsigned int)op1 : 25;
+}
+int
+max1 (signed char op1, signed char op2)
+{
+ return (op1 > 24) ? (int)op1 : 25;
+}
+int
+max2 (signed char op1, signed char op2)
+{
+ return (op1 >= 25) ? (int)op1 : 24;
+}
+int
+max3 (unsigned char op1, unsigned char op2)
+{
+ return (op1 > 24) ? (unsigned int)op1 : 25;
+}
+int
+max4 (unsigned char op1, unsigned char op2)
+{
+ return (op1 >= 25) ? (unsigned int)op1 : 24;
+}
+
+/* { dg-final { scan-tree-dump-times "MIN_EXPR" 4 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "MAX_EXPR" 4 "optimized" } } */
+
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-ifcvt" } */
+
+int foo (unsigned short a[], unsigned int x)
+{
+ unsigned int i;
+ for (i = 0; i < 1000; i++)
+ {
+ x = a[i];
+ a[i] = (unsigned short)(x >= 255 ? 255 : x);
+ } return x;
+}
+
+/* { dg-final { scan-tree-dump " = MIN_EXPR <" "ifcvt" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-ifcvt" } */
+
+int foo (short a[], int x)
+{
+ unsigned int i;
+ for (i = 0; i < 1000; i++)
+ {
+ x = a[i];
+ a[i] = (short)(x <= 0 ? 0 : x);
+ } return x;
+}
+
+
+/* { dg-final { scan-tree-dump " = MAX_EXPR <" "ifcvt" } } */
-
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-phiopt1-details" } */
+/* { dg-options "-O2 -fdump-tree-gimple" } */
extern unsigned short mode_size[];
return (64 < mode_size[mode] ? 64 : mode_size[mode]);
}
-/* { dg-final { scan-tree-dump-times "factor conversion out" 1 "phiopt1" } } */
-/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "phiopt1" } } */
+/* With simplifications transforming cond_expr int min/max_expr
+ supported by match.pd patterns, we can optimize this at early
+ stage of compilation, rather than relying on phiopt for that. */
+/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "gimple" } } */
return cond;
}
+/* Local valueization callback that follows all-use SSA edges. */
+
+static tree
+ifcvt_follow_ssa_use_edges (tree val)
+{
+ return val;
+}
+
/* Replace a scalar PHI node with a COND_EXPR using COND as condition.
This routine can handle PHI nodes with more than two arguments.
arg0, arg1);
new_stmt = gimple_build_assign (res, rhs);
gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+ gimple_stmt_iterator new_gsi = gsi_for_stmt (new_stmt);
+ fold_stmt (&new_gsi, ifcvt_follow_ssa_use_edges);
update_stmt (new_stmt);
if (dump_file && (dump_flags & TDF_DETAILS))