diff options
Diffstat (limited to 'gcc/tree-data-ref.c')
-rw-r--r-- | gcc/tree-data-ref.c | 39 |
1 files changed, 37 insertions, 2 deletions
diff --git a/gcc/tree-data-ref.c b/gcc/tree-data-ref.c index b5c0b7f..f3070d3 100644 --- a/gcc/tree-data-ref.c +++ b/gcc/tree-data-ref.c @@ -705,11 +705,46 @@ split_constant_offset_1 (tree type, tree op0, enum tree_code code, tree op1, and the outer precision is at least as large as the inner. */ tree itype = TREE_TYPE (op0); if ((POINTER_TYPE_P (itype) - || (INTEGRAL_TYPE_P (itype) && TYPE_OVERFLOW_UNDEFINED (itype))) + || (INTEGRAL_TYPE_P (itype) && !TYPE_OVERFLOW_TRAPS (itype))) && TYPE_PRECISION (type) >= TYPE_PRECISION (itype) && (POINTER_TYPE_P (type) || INTEGRAL_TYPE_P (type))) { - split_constant_offset (op0, &var0, off); + if (INTEGRAL_TYPE_P (itype) && TYPE_OVERFLOW_WRAPS (itype)) + { + /* Split the unconverted operand and try to prove that + wrapping isn't a problem. */ + tree tmp_var, tmp_off; + split_constant_offset (op0, &tmp_var, &tmp_off); + + /* See whether we have an SSA_NAME whose range is known + to be [A, B]. */ + if (TREE_CODE (tmp_var) != SSA_NAME) + return false; + wide_int var_min, var_max; + if (get_range_info (tmp_var, &var_min, &var_max) != VR_RANGE) + return false; + + /* See whether the range of OP0 (i.e. TMP_VAR + TMP_OFF) + is known to be [A + TMP_OFF, B + TMP_OFF], with all + operations done in ITYPE. The addition must overflow + at both ends of the range or at neither. */ + bool overflow[2]; + signop sgn = TYPE_SIGN (itype); + unsigned int prec = TYPE_PRECISION (itype); + wide_int woff = wi::to_wide (tmp_off, prec); + wide_int op0_min = wi::add (var_min, woff, sgn, &overflow[0]); + wi::add (var_max, woff, sgn, &overflow[1]); + if (overflow[0] != overflow[1]) + return false; + + /* Calculate (ssizetype) OP0 - (ssizetype) TMP_VAR. */ + widest_int diff = (widest_int::from (op0_min, sgn) + - widest_int::from (var_min, sgn)); + var0 = tmp_var; + *off = wide_int_to_tree (ssizetype, diff); + } + else + split_constant_offset (op0, &var0, off); *var = fold_convert (type, var0); return true; } |