> use INTEGRAL_TYPE_P.
Done.
> but you do not actually _use_ vr_outer. Do you think that if
> vr_outer is a VR_RANGE then the outer operation may not
> possibly have wrapped? That's a false conclusion.
These were remains of a previous version. vr_outer is indeed not needed
anymore; removed.
> wi::add overload with the overflow flag? ISTR you want to handle "negative"
> unsigned constants somehow, but then I don't see how the above works.
> I'd say if wmin/wmax interpreted as signed are positive and then using
> a signed op to add w1 results in a still positive number you're fine
> (you don't seem
> to restrict the widening cast to either zero- or sign-extending).
Changed to using wi:add overload now.
In essence, three cases are being handled:
- wrapped_range --> do not simplify
- !wrapped_range && ovf ("negative" unsigned) --> simplify and combine
with sign extension in the outer type
- !wrapped_range && !ovf ("positive" unsigned) --> simplify and combine
with zero extension in the outer type.
Regards
Robin
diff --git a/gcc/match.pd b/gcc/match.pd
index 80a17ba..ec1af69 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -1290,6 +1290,116 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
(if (cst && !TREE_OVERFLOW (cst))
(plus { cst; } @0))))
+/* ((T)(A +- CST)) +- CST -> (T)(A) +- CST) */
+#if GIMPLE
+ (for outer_op (plus minus)
+ (for inner_op (plus minus)
+ (simplify
+ (outer_op (convert (inner_op@3 @0 INTEGER_CST@1)) INTEGER_CST@2)
+ (if (INTEGRAL_TYPE_P (type)
+ && TYPE_PRECISION (type) > TYPE_PRECISION (TREE_TYPE (@3)))
+ (with
+ {
+ tree cst;
+ tree inner_type = TREE_TYPE (@3);
+ wide_int wmin0, wmax0;
+
+ bool ovf = true;
+ bool ovf_undef = TYPE_OVERFLOW_UNDEFINED (inner_type);
+
+ enum value_range_type vr0 =
+ get_range_info (@0, &wmin0, &wmax0);
+
+ bool wrapped_range = true;
+
+ /* Convert combined constant to tree of outer type if
+ there was no overflow in the original inner operation. */
+ if (ovf_undef || vr0 == VR_RANGE)
+ {
+ wide_int w1 = @1;
+ wide_int w2 = @2;
+
+ if (inner_op == MINUS_EXPR)
+ w1 = wi::neg (w1);
+
+ if (outer_op == MINUS_EXPR)
+ w2 = wi::neg (w2);
+
+ bool ovf;
+
+ if (!ovf_undef && vr0 == VR_RANGE)
+ {
+ bool max_ovf;
+ bool min_ovf;
+
+ signop sgn = TYPE_SIGN (inner_type);
+ wi::add (wmin0, w1, sgn, &min_ovf);
+ wi::add (wmax0, w1, sgn, &max_ovf);
+
+ ovf = min_ovf || max_ovf;
+ wrapped_range = ((min_ovf && !max_ovf)
+ || (!min_ovf && max_ovf));
+ }
+
+ /* Extend @1 to TYPE. */
+ w1 = w1.from (w1, TYPE_PRECISION (type),
+ ovf ? SIGNED : TYPE_SIGN (inner_type));
+
+ /* Combine in outer, larger type. */
+ wide_int combined_cst;
+ combined_cst = wi::add (w1, w2);
+
+ cst = wide_int_to_tree (type, combined_cst);
+ }
+ }
+ (if (ovf_undef || !wrapped_range)
+ (outer_op (convert @0) { cst; }))
+ )))))
+#endif
+
+/* ((T)(A)) +- CST -> (T)(A +- CST) */
+#if GIMPLE
+ (for outer_op (plus minus)
+ (simplify
+ (outer_op (convert SSA_NAME@0) INTEGER_CST@2)
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
+ && INTEGRAL_TYPE_P (type)
+ && TYPE_PRECISION (type) > TYPE_PRECISION (TREE_TYPE (@0)))
+ /* Perform binary operation inside the cast if the constant fits
+ and there is no overflow. */
+ (with
+ {
+ bool wrapped_range = true;
+ tree cst_inner = NULL_TREE;
+ enum value_range_type vr = VR_VARYING;
+ tree inner_type = TREE_TYPE (@0);
+
+ if (int_fits_type_p (@2, inner_type))
+ {
+ cst_inner = fold_convert (inner_type, @2);
+
+ wide_int wmin0, wmax0;
+ wide_int w1 = cst_inner;
+ signop sgn = TYPE_SIGN (inner_type);
+ vr = get_range_info (@0, &wmin0, &wmax0);
+
+ if (vr == VR_RANGE)
+ {
+ bool min_ovf;
+ wi::add (wmin0, w1, sgn, &min_ovf);
+
+ bool max_ovf;
+ wi::add (wmax0, w1, sgn, &max_ovf);
+
+ wrapped_range = (min_ovf && !max_ovf) || (!min_ovf && max_ovf);
+ }
+ }
+ }
+ (if (cst_inner && !wrapped_range)
+ (convert (outer_op @0 { cst_inner; })))
+ ))))
+#endif
+
/* ~A + A -> -1 */
(simplify
(plus:c (bit_not @0) @0)