(plus (convert @0) (op @2 (convert @1))))))
#endif
-/* Inverse of the above: (T)(A) +- CST -> (T)(A +- CST') when T is a
- widening conversion from a type with undefined overflow and the outer
- type wraps. This allows VN to discover that (T)A + (T)C == (T)(A + C)
- regardless of which form appears first in program order. PR124545.
- The rewrite is unsound for unsigned inner types: the narrow op wraps
- mod 2^prec (defined) while the widened op does not, changing the
- observed value. Cover the unsigned case separately once ranger can
- prove no wrap. */
-#if GIMPLE
- (for op (plus minus)
- (simplify
- (op (convert @0) INTEGER_CST@1)
- (if (TREE_CODE (TREE_TYPE (@0)) == INTEGER_TYPE
- && TREE_CODE (type) == INTEGER_TYPE
- && TYPE_PRECISION (type) > TYPE_PRECISION (TREE_TYPE (@0))
- && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0))
- && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@0))
- && TYPE_OVERFLOW_WRAPS (type)
- /* CST must be the sign-extension of its low inner-precision bits,
- otherwise narrowing changes the value. Use min_precision (..,
- SIGNED) rather than int_fits_type_p so that small negative offsets
- encoded as large unsigned constants (e.g. -1 as sizetype) still
- qualify. */
- && wi::min_precision (wi::to_wide (@1), SIGNED)
- <= TYPE_PRECISION (TREE_TYPE (@0)))
- (with {
- wide_int c1 = wi::to_wide (@1);
- tree inner_cst = wide_int_to_tree (TREE_TYPE (@0),
- wi::sext (c1, TYPE_PRECISION (TREE_TYPE (@0)))); }
- (convert (op! @0 { inner_cst; }))))))
-#endif
-
/* (T)(A) +- (T)(B) -> (T)(A +- B) only when (A +- B) could be simplified
to a simple value. */
(for op (plus minus)
--- /dev/null
+/* PR tree-optimization/126415 */
+/* Wrong code from the inverse widening rewrite (T)A +- CST -> (T)(A +- CST'):
+ the introduced narrow signed operation may overflow even though the
+ original widened operation is fully defined. The narrow op then collapses
+ through defined-wrap identities (mod 2^narrow-prec), changing the value. */
+/* { dg-do run } */
+
+int printf(const char *, ...);
+
+long d(short p1, unsigned e, char f) {
+ if (246 >= 149u - f)
+ return 0;
+ return f;
+}
+int g(char p1) { return d(0, 0, p1); }
+int fn3(char p1) {
+ long i = g(p1 + 159);
+ return i;
+}
+
+/* Minimal variants: signed char/short/int inner, PLUS and MINUS, and a
+ negative CST encoded as a large unsigned constant. Use signed char
+ explicitly: the checks encode sign-extension results and plain char
+ is unsigned on some targets. */
+volatile signed char vc1 = -84;
+volatile signed char vc2 = 50;
+volatile short vs = -21000;
+volatile int vi = -2000000000;
+volatile signed char vc3 = 100;
+
+int main() {
+ if (fn3(-84) != 0)
+ __builtin_abort ();
+
+ signed char p1 = vc1;
+ signed char f1 = (signed char)((unsigned char)p1 + 159);
+ if ((unsigned)f1 + 97 != 172u)
+ __builtin_abort ();
+
+ signed char p2 = vc2;
+ signed char f2 = (signed char)((unsigned char)p2 + 97);
+ if ((unsigned)f2 - 97 != 4294967090u)
+ __builtin_abort ();
+
+ short p3 = vs;
+ short f3 = (short)((unsigned short)p3 + 40000);
+ if ((unsigned)f3 + 25536 != 44536u)
+ __builtin_abort ();
+
+ int p4 = vi;
+ int f4 = (int)((unsigned)p4 + 3000000000u);
+ if ((unsigned long long)f4 + 1294967296ull != 2294967296ull)
+ __builtin_abort ();
+
+ signed char p5 = vc3;
+ signed char f5 = (signed char)((unsigned char)p5 + 97);
+ if ((unsigned)f5 + 0xFFFFFF9Fu != 4294967140u)
+ __builtin_abort ();
+
+ return 0;
+}
return NULL_TREE;
}
+/* Return true if RESULT, the result of a value-number lookup, may be
+ used at the statement being visited. A result of wrapping type can
+ be inserted for code hoisting without introducing undefined
+ overflow; anything else has to be available. See PR86554. */
+
+static bool
+vn_nary_result_avail_or_insertable_p (tree result)
+{
+ return (TYPE_OVERFLOW_WRAPS (TREE_TYPE (result))
+ || (rpo_avail && vn_context_bb
+ && rpo_avail->eliminate_avail (vn_context_bb, result)));
+}
+
+/* If OP is an SSA name defined by a conversion from an integral type,
+ return the valueized source of the conversion, otherwise return
+ NULL_TREE. */
+
+static tree
+ssa_integral_conversion_op (tree op)
+{
+ if (TREE_CODE (op) != SSA_NAME)
+ return NULL_TREE;
+ gassign *def = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (op));
+ if (!def || !CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def)))
+ return NULL_TREE;
+ const tree src = gimple_assign_rhs1 (def);
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (src)))
+ return NULL_TREE;
+ return vn_valueize (src);
+}
+
/* Visit a nary operator RHS, value number it, and return true if the
value number of LHS has changed as a result. */
ops[0] = vn_nary_op_lookup_pieces
(2, gimple_assign_rhs_code (def), type, ops, NULL);
/* We have wider operation available. */
- if (ops[0]
- /* If the leader is a wrapping operation we can
- insert it for code hoisting w/o introducing
- undefined overflow. If it is not it has to
- be available. See PR86554. */
- && (TYPE_OVERFLOW_WRAPS (TREE_TYPE (ops[0]))
- || (rpo_avail && vn_context_bb
- && rpo_avail->eliminate_avail (vn_context_bb,
- ops[0]))))
+ if (ops[0] && vn_nary_result_avail_or_insertable_p (ops[0]))
{
unsigned lhs_prec = TYPE_PRECISION (type);
unsigned rhs_prec = TYPE_PRECISION (TREE_TYPE (rhs1));
}
}
break;
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ {
+ /* Match (T)A +- B against an existing (T)(A +- B'), the inverse
+ of the conversion case above, so the redundancy is detected
+ regardless of the order the two forms appear in the IL.
+ See PR124545. The narrow operation is only ever looked up,
+ never created: assuming no overflow is only valid for
+ operations the program actually executes, so the narrow
+ leader has to be available. Creating the narrow operation
+ instead is wrong-code, see PR126415. */
+ const tree narrow1 = ssa_integral_conversion_op (vn_valueize (rhs1));
+ if (!INTEGRAL_TYPE_P (type) || !narrow1)
+ break;
+ const tree ntype = TREE_TYPE (narrow1);
+ /* A sign-change keeps the value bit-identical; a widening is
+ only handled when the narrow operation cannot wrap. */
+ const bool sign_change_p
+ = TYPE_PRECISION (ntype) == TYPE_PRECISION (type);
+ const bool nowrap_widening_p
+ = (TYPE_PRECISION (ntype) < TYPE_PRECISION (type)
+ && TYPE_OVERFLOW_UNDEFINED (ntype));
+ if (!sign_change_p && !nowrap_widening_p)
+ break;
+ /* Determine the narrow variant of the second operand: a
+ constant that narrows and extends back unchanged, or a
+ conversion from the same narrow type. */
+ const tree rhs2 = gimple_assign_rhs2 (stmt);
+ tree narrow2 = NULL_TREE;
+ if (TREE_CODE (rhs2) == INTEGER_CST)
+ {
+ const widest_int cst = wi::to_widest (rhs2);
+ const widest_int narrowed
+ = wi::ext (cst, TYPE_PRECISION (ntype), TYPE_SIGN (ntype));
+ const widest_int extended
+ = wi::ext (narrowed, TYPE_PRECISION (type), TYPE_SIGN (type));
+ if (cst == extended)
+ narrow2 = fold_convert (ntype, rhs2);
+ }
+ else if (TREE_CODE (rhs2) == SSA_NAME)
+ {
+ const tree op = ssa_integral_conversion_op (vn_valueize (rhs2));
+ if (op && types_compatible_p (TREE_TYPE (op), ntype))
+ narrow2 = op;
+ }
+ if (!narrow2)
+ break;
+ tree ops[3] = { narrow1, narrow2 };
+ const tree narrow_val
+ = vn_nary_op_lookup_pieces (2, code, ntype, ops, NULL);
+ /* We have a narrower or sign-changed operation available. */
+ if (narrow_val && vn_nary_result_avail_or_insertable_p (narrow_val))
+ {
+ gimple_match_op match_op (gimple_match_cond::UNCOND,
+ NOP_EXPR, type, narrow_val);
+ result = vn_nary_build_or_lookup (&match_op);
+ if (result)
+ {
+ const bool changed = set_ssa_val_to (lhs, result);
+ if (TREE_CODE (result) == SSA_NAME)
+ vn_nary_op_insert_stmt (stmt, result);
+ return changed;
+ }
+ }
+ }
+ break;
case BIT_AND_EXPR:
if (INTEGRAL_TYPE_P (type)
&& TREE_CODE (rhs1) == SSA_NAME