From: Philipp Tomsich <p...@gnu.org> While most shifts wider than the bitwidth of a type will be caught by other passes, it is possible that these show up for VRP. Consider the following example: int func (int a, int b, int c) { return (a << ((b && c) - 1)); }
This adds simplify_using_ranges::simplify_lshift_using_ranges to detect and rewrite such cases. If the intersection of meaningful shift amounts for the underlying type and the value-range computed for the shift-amount (whether an integer constant or a variable) is empty, the statement is replaced with the zero-constant of the same precision as the result. gcc/ChangeLog: * vr-values.h (simplify_using_ranges): Declare. * vr-values.c (simplify_lshift_using_ranges): New function. (simplify): Use simplify_lshift_using_ranges for LSHIFT_EXPR. --- gcc/ChangeLog | 6 ++++++ gcc/vr-values.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gcc/vr-values.h | 1 + 3 files changed, 66 insertions(+) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 89317d4..b8b9beb 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2020-11-16 Philipp Tomsich <p...@gnu.org> + + * vr-values.h (simplify_using_ranges): Declare. + * vr-values.c (simplify_lshift_using_ranges): New function. + (simplify): Use simplify_lshift_using_ranges for LSHIFT_EXPR. + 2020-11-13 Jan Hubicka <j...@suse.cz> * tree-ssa-alias.c (ao_ref_base_alias_ptr_type): Remove accidental diff --git a/gcc/vr-values.c b/gcc/vr-values.c index 9f5943a..da7208e 100644 --- a/gcc/vr-values.c +++ b/gcc/vr-values.c @@ -3318,6 +3318,58 @@ simplify_using_ranges::simplify_div_or_mod_using_ranges return false; } +/* Simplify a lshift, if the shift-amount is larger than the + bit-width of the type. Return true if we do simplify. */ +bool +simplify_using_ranges::simplify_lshift_using_ranges + (gimple_stmt_iterator *gsi, + gimple *stmt) +{ + tree op0 = gimple_assign_rhs1 (stmt); + tree op1 = gimple_assign_rhs2 (stmt); + value_range vr1; + + /* We only support integral types. */ + if (INTEGRAL_TYPE_P (op0)) + return false; + + if (TREE_CODE (op1) == INTEGER_CST) + vr1.set (op1); + else if (TREE_CODE (op1) == SSA_NAME) + vr1 = *(query->get_value_range (op1, stmt)); + else + return false; + + if (vr1.varying_p () || vr1.undefined_p ()) + return false; + + /* Shift amounts are valid up to the type precision. Any shift that + is outside of the range [0, type precision - 1] can be rewritten + to a constant result. */ + const unsigned prec = TYPE_PRECISION (TREE_TYPE (op0)); + value_range valid (build_zero_cst (TREE_TYPE (op1)), + build_int_cst (TREE_TYPE (op1), prec - 1), + VR_RANGE); + + valid.intersect (vr1); + if (valid.undefined_p ()) + { + /* If the intersection is empty (i.e. undefined), then we can replace the + shift with the zero-constant. */ + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nReplacing shift beyond precision in stmt: "); + print_gimple_stmt (dump_file, stmt, 0); + } + gimple_assign_set_rhs_from_tree (gsi, build_zero_cst (TREE_TYPE (op0))); + update_stmt (gsi_stmt (*gsi)); + return true; + } + + return false; +} + /* Simplify a min or max if the ranges of the two operands are disjoint. Return true if we do simplify. */ @@ -4422,6 +4474,13 @@ simplify_using_ranges::simplify (gimple_stmt_iterator *gsi) case MAX_EXPR: return simplify_min_or_max_using_ranges (gsi, stmt); + case LSHIFT_EXPR: + if ((TREE_CODE (rhs1) == SSA_NAME + || TREE_CODE (rhs1) == INTEGER_CST) + && INTEGRAL_TYPE_P (TREE_TYPE (rhs1))) + return simplify_lshift_using_ranges (gsi, stmt); + break; + default: break; } diff --git a/gcc/vr-values.h b/gcc/vr-values.h index 59fac0c..18fd5c1 100644 --- a/gcc/vr-values.h +++ b/gcc/vr-values.h @@ -48,6 +48,7 @@ private: bool simplify_div_or_mod_using_ranges (gimple_stmt_iterator *, gimple *); bool simplify_abs_using_ranges (gimple_stmt_iterator *, gimple *); bool simplify_bit_ops_using_ranges (gimple_stmt_iterator *, gimple *); + bool simplify_lshift_using_ranges (gimple_stmt_iterator *, gimple *); bool simplify_min_or_max_using_ranges (gimple_stmt_iterator *, gimple *); bool simplify_cond_using_ranges_1 (gcond *); bool fold_cond (gcond *); -- 1.8.3.1