diff options
Diffstat (limited to 'gcc/tree-ssa-math-opts.c')
-rw-r--r-- | gcc/tree-ssa-math-opts.c | 186 |
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:; |