diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 5445bd5fd1fd0d1bf9b47333ef008dfe997445f5..cf75376b851b9cb78e63aca55ffe16a41a248905 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,8 @@
+2017-05-25  Marc Glisse  <marc.glisse@inria.fr>
+
+	* match.pd ((A +- CST1) +- CST2): Allow some conversions.
+	* tree.c (drop_tree_overflow): Handle COMPLEX_CST and VECTOR_CST.
+
 2017-05-25  Marc Glisse  <marc.glisse@inria.fr>
 
 	* fold-const.c (fold_binary_loc) [(A & C) == D]: Remove transformation.
diff --git a/gcc/match.pd b/gcc/match.pd
index 618b2ec3da83c6a9a2bcbc91710825144482b8c3..77271a016198a58fc5411f2e056207166d96a92b 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -1299,15 +1299,39 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   /* (A +- CST1) +- CST2 -> A + CST3  */
   (for outer_op (plus minus)
    (for inner_op (plus minus)
+	neg_inner_op (minus plus)
     (simplify
-     (outer_op (inner_op @0 CONSTANT_CLASS_P@1) CONSTANT_CLASS_P@2)
-     /* If the constant operation overflows we cannot do the transform
-	as we would introduce undefined overflow, for example
-	with (a - 1) + INT_MIN.  */
-     (with { tree cst = const_binop (outer_op == inner_op
-				     ? PLUS_EXPR : MINUS_EXPR, type, @1, @2); }
-      (if (cst && !TREE_OVERFLOW (cst))
-       (inner_op @0 { cst; } ))))))
+     (outer_op (convert? (inner_op @0 CONSTANT_CLASS_P@1)) CONSTANT_CLASS_P@2)
+     (if (tree_nop_conversion_p (type, TREE_TYPE (@0)))
+      /* If one of the types wraps, use that one.  */
+      (if (!ANY_INTEGRAL_TYPE_P (type) || TYPE_OVERFLOW_WRAPS (type))
+       (if (outer_op == PLUS_EXPR)
+	(plus (convert @0) (inner_op @2 (convert @1)))
+	(minus (convert @0) (neg_inner_op @2 (convert @1))))
+       (if (!ANY_INTEGRAL_TYPE_P (TREE_TYPE (@0))
+	    || TYPE_OVERFLOW_WRAPS (TREE_TYPE (@0)))
+	(if (outer_op == PLUS_EXPR)
+	 (convert (plus @0 (inner_op (convert @2) @1)))
+	 (convert (minus @0 (neg_inner_op (convert @2) @1))))
+	/* If the constant operation overflows we cannot do the transform
+	   directly as we would introduce undefined overflow, for example
+	   with (a - 1) + INT_MIN.  */
+	(if (types_match (type, @0))
+	 (with { tree cst = const_binop (outer_op == inner_op
+					 ? PLUS_EXPR : MINUS_EXPR,
+					 type, @1, @2); }
+	  (if (cst && !TREE_OVERFLOW (cst))
+	   (inner_op @0 { cst; } )
+	   /* X+INT_MAX+1 is X-INT_MIN.  */
+	   (if (INTEGRAL_TYPE_P (type) && cst
+		&& wi::eq_p (cst, wi::min_value (type)))
+	    (neg_inner_op @0 { wide_int_to_tree (type, cst); })
+	    /* Last resort, use some unsigned type.  */
+	    (with { tree utype = unsigned_type_for (type); }
+	     (convert (inner_op
+		       (convert:utype @0)
+		       (convert:utype
+			{ drop_tree_overflow (cst); }))))))))))))))
 
   /* (CST1 - A) +- CST2 -> CST3 - A  */
   (for outer_op (plus minus)
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 84026e31153439cc928631e4979b65fed81cfc81..efe1dcb14f84ebd531ebdd81718a1d22358887a3 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2017-05-25  Marc Glisse  <marc.glisse@inria.fr>
+
+	* gcc.dg/tree-ssa/addadd.c: New file.
+
 2017-05-24  Nathan Sidwell  <nathan@acm.org>
 
 	* g++.dg/lookup/friend12.C: Adjust diagnostics.
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/addadd.c b/gcc/testsuite/gcc.dg/tree-ssa/addadd.c
new file mode 100644
index 0000000000000000000000000000000000000000..16474db65658a5e3e88b141b24f991c80936a9e9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/addadd.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdump-tree-optimized" } */
+
+int f(unsigned x){
+  x += 123;
+  int y = x;
+  y -= 99;
+  return y;
+}
+unsigned g(int x){
+  x += 123;
+  unsigned y = x;
+  y -= 99;
+  return y;
+}
+int h(int x){
+  x += __INT_MAX__;
+  x += 1;
+  return x;
+}
+int i(int x){
+  x += __INT_MAX__;
+  x += __INT_MAX__;
+  return x;
+}
+typedef int S __attribute__((vector_size(16)));
+void j(S*x){
+  *x += __INT_MAX__;
+  *x += __INT_MAX__;
+}
+
+/* { dg-final { scan-tree-dump-times " \\+ 24;" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "\\(unsigned int\\)" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "2147483647" "optimized" } } */
diff --git a/gcc/tree.c b/gcc/tree.c
index db31620736982f6b3c5f0884fc6ab9e9cec4cda9..a58f9aaa69ec4b0b5ad22d92a41935ceae36b1c4 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -13138,6 +13138,25 @@ drop_tree_overflow (tree t)
      and drop the flag.  */
   t = copy_node (t);
   TREE_OVERFLOW (t) = 0;
+
+  /* For constants that contain nested constants, drop the flag
+     from those as well.  */
+  if (TREE_CODE (t) == COMPLEX_CST)
+    {
+      if (TREE_OVERFLOW (TREE_REALPART (t)))
+	TREE_REALPART (t) = drop_tree_overflow (TREE_REALPART (t));
+      if (TREE_OVERFLOW (TREE_IMAGPART (t)))
+	TREE_IMAGPART (t) = drop_tree_overflow (TREE_IMAGPART (t));
+    }
+  if (TREE_CODE (t) == VECTOR_CST)
+    {
+      for (unsigned i = 0; i < VECTOR_CST_NELTS (t); ++i)
+	{
+	  tree& elt = VECTOR_CST_ELT (t, i);
+	  if (TREE_OVERFLOW (elt))
+	    elt = drop_tree_overflow (elt);
+	}
+    }
   return t;
 }