summaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-math-opts.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-ssa-math-opts.c')
-rw-r--r--gcc/tree-ssa-math-opts.c186
1 files changed, 185 insertions, 1 deletions
diff --git a/gcc/tree-ssa-math-opts.c b/gcc/tree-ssa-math-opts.c
index 1e7cf7ed283..8376eab746d 100644
--- a/gcc/tree-ssa-math-opts.c
+++ b/gcc/tree-ssa-math-opts.c
@@ -3491,6 +3491,189 @@ convert_mult_to_fma (gimple *mul_stmt, tree op1, tree op2)
return true;
}
+
+/* Helper function of match_uaddsub_overflow. Return 1
+ if USE_STMT is unsigned overflow check ovf != 0 for
+ STMT, -1 if USE_STMT is unsigned overflow check ovf == 0
+ and 0 otherwise. */
+
+static int
+uaddsub_overflow_check_p (gimple *stmt, gimple *use_stmt)
+{
+ enum tree_code ccode = ERROR_MARK;
+ tree crhs1 = NULL_TREE, crhs2 = NULL_TREE;
+ if (gimple_code (use_stmt) == GIMPLE_COND)
+ {
+ ccode = gimple_cond_code (use_stmt);
+ crhs1 = gimple_cond_lhs (use_stmt);
+ crhs2 = gimple_cond_rhs (use_stmt);
+ }
+ else if (is_gimple_assign (use_stmt))
+ {
+ if (gimple_assign_rhs_class (use_stmt) == GIMPLE_BINARY_RHS)
+ {
+ ccode = gimple_assign_rhs_code (use_stmt);
+ crhs1 = gimple_assign_rhs1 (use_stmt);
+ crhs2 = gimple_assign_rhs2 (use_stmt);
+ }
+ else if (gimple_assign_rhs_code (use_stmt) == COND_EXPR)
+ {
+ tree cond = gimple_assign_rhs1 (use_stmt);
+ if (COMPARISON_CLASS_P (cond))
+ {
+ ccode = TREE_CODE (cond);
+ crhs1 = TREE_OPERAND (cond, 0);
+ crhs2 = TREE_OPERAND (cond, 1);
+ }
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+ else
+ return 0;
+
+ if (TREE_CODE_CLASS (ccode) != tcc_comparison)
+ return 0;
+
+ enum tree_code code = gimple_assign_rhs_code (stmt);
+ tree lhs = gimple_assign_lhs (stmt);
+ tree rhs1 = gimple_assign_rhs1 (stmt);
+ tree rhs2 = gimple_assign_rhs2 (stmt);
+
+ switch (ccode)
+ {
+ case GT_EXPR:
+ case LE_EXPR:
+ /* r = a - b; r > a or r <= a
+ r = a + b; a > r or a <= r or b > r or b <= r. */
+ if ((code == MINUS_EXPR && crhs1 == lhs && crhs2 == rhs1)
+ || (code == PLUS_EXPR && (crhs1 == rhs1 || crhs1 == rhs2)
+ && crhs2 == lhs))
+ return ccode == GT_EXPR ? 1 : -1;
+ break;
+ case LT_EXPR:
+ case GE_EXPR:
+ /* r = a - b; a < r or a >= r
+ r = a + b; r < a or r >= a or r < b or r >= b. */
+ if ((code == MINUS_EXPR && crhs1 == rhs1 && crhs2 == lhs)
+ || (code == PLUS_EXPR && crhs1 == lhs
+ && (crhs2 == rhs1 || crhs2 == rhs2)))
+ return ccode == LT_EXPR ? 1 : -1;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* Recognize for unsigned x
+ x = y - z;
+ if (x > y)
+ where there are other uses of x and replace it with
+ _7 = SUB_OVERFLOW (y, z);
+ x = REALPART_EXPR <_7>;
+ _8 = IMAGPART_EXPR <_7>;
+ if (_8)
+ and similarly for addition. */
+
+static bool
+match_uaddsub_overflow (gimple_stmt_iterator *gsi, gimple *stmt,
+ enum tree_code code)
+{
+ tree lhs = gimple_assign_lhs (stmt);
+ tree type = TREE_TYPE (lhs);
+ use_operand_p use_p;
+ imm_use_iterator iter;
+ bool use_seen = false;
+ bool ovf_use_seen = false;
+ gimple *use_stmt;
+
+ gcc_checking_assert (code == PLUS_EXPR || code == MINUS_EXPR);
+ if (!INTEGRAL_TYPE_P (type)
+ || !TYPE_UNSIGNED (type)
+ || has_zero_uses (lhs)
+ || has_single_use (lhs)
+ || optab_handler (code == PLUS_EXPR ? uaddv4_optab : usubv4_optab,
+ TYPE_MODE (type)) == CODE_FOR_nothing)
+ return false;
+
+ FOR_EACH_IMM_USE_FAST (use_p, iter, lhs)
+ {
+ use_stmt = USE_STMT (use_p);
+ if (is_gimple_debug (use_stmt))
+ continue;
+
+ if (uaddsub_overflow_check_p (stmt, use_stmt))
+ ovf_use_seen = true;
+ else
+ use_seen = true;
+ if (ovf_use_seen && use_seen)
+ break;
+ }
+
+ if (!ovf_use_seen || !use_seen)
+ return false;
+
+ tree ctype = build_complex_type (type);
+ tree rhs1 = gimple_assign_rhs1 (stmt);
+ tree rhs2 = gimple_assign_rhs2 (stmt);
+ gcall *g = gimple_build_call_internal (code == PLUS_EXPR
+ ? IFN_ADD_OVERFLOW : IFN_SUB_OVERFLOW,
+ 2, rhs1, rhs2);
+ tree ctmp = make_ssa_name (ctype);
+ gimple_call_set_lhs (g, ctmp);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ gassign *g2 = gimple_build_assign (lhs, REALPART_EXPR,
+ build1 (REALPART_EXPR, type, ctmp));
+ gsi_replace (gsi, g2, true);
+ tree ovf = make_ssa_name (type);
+ g2 = gimple_build_assign (ovf, IMAGPART_EXPR,
+ build1 (IMAGPART_EXPR, type, ctmp));
+ gsi_insert_after (gsi, g2, GSI_NEW_STMT);
+
+ FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
+ {
+ if (is_gimple_debug (use_stmt))
+ continue;
+
+ int ovf_use = uaddsub_overflow_check_p (stmt, use_stmt);
+ if (ovf_use == 0)
+ continue;
+ if (gimple_code (use_stmt) == GIMPLE_COND)
+ {
+ gcond *cond_stmt = as_a <gcond *> (use_stmt);
+ gimple_cond_set_lhs (cond_stmt, ovf);
+ gimple_cond_set_rhs (cond_stmt, build_int_cst (type, 0));
+ gimple_cond_set_code (cond_stmt, ovf_use == 1 ? NE_EXPR : EQ_EXPR);
+ }
+ else
+ {
+ gcc_checking_assert (is_gimple_assign (use_stmt));
+ if (gimple_assign_rhs_class (use_stmt) == GIMPLE_BINARY_RHS)
+ {
+ gimple_assign_set_rhs1 (use_stmt, ovf);
+ gimple_assign_set_rhs2 (use_stmt, build_int_cst (type, 0));
+ gimple_assign_set_rhs_code (use_stmt,
+ ovf_use == 1 ? NE_EXPR : EQ_EXPR);
+ }
+ else
+ {
+ gcc_checking_assert (gimple_assign_rhs_code (use_stmt)
+ == COND_EXPR);
+ tree cond = build2 (ovf_use == 1 ? NE_EXPR : EQ_EXPR,
+ boolean_type_node, ovf,
+ build_int_cst (type, 0));
+ gimple_assign_set_rhs1 (use_stmt, cond);
+ }
+ }
+ update_stmt (use_stmt);
+ }
+ return true;
+}
+
+
/* Find integer multiplications where the operands are extended from
smaller types, and replace the MULT_EXPR with a WIDEN_MULT_EXPR
where appropriate. */
@@ -3563,7 +3746,8 @@ pass_optimize_widening_mul::execute (function *fun)
case PLUS_EXPR:
case MINUS_EXPR:
- convert_plusminus_to_widen (&gsi, stmt, code);
+ if (!convert_plusminus_to_widen (&gsi, stmt, code))
+ match_uaddsub_overflow (&gsi, stmt, code);
break;
default:;