The wide_int APIs expect operands to have the same precision and
abort when they don't.  This is especially insidious in code where
the operands normally do have the same precision but where mixed
precision arguments can come up as a result of unusual combinations
optimization options.  That is also what precipitated pr93986.

The attached patch adjusts the code to extend all wide_int operands
to the same precision to avoid the ICE.

Besides the usual bootstrap/testing I also compiled all string tests
in gcc.dg with the same options as in the test case in pr93986 in
an effort to weed out any lingering bugs like it (found none).

Martin
PR tree-optimization/93986 - ICE on mixed-precision wide_int arguments

gcc/testsuite/ChangeLog:

	PR tree-optimization/93986
	* gcc.dg/pr93986.c: New test.

gcc/ChangeLog:

	PR tree-optimization/93986
	* tree-ssa-strlen.c (maybe_warn_overflow): Convert all wide_int
	operands to the same precision to avoid ICEs.

diff --git a/gcc/testsuite/gcc.dg/pr93986.c b/gcc/testsuite/gcc.dg/pr93986.c
new file mode 100644
index 00000000000..bdbc192a01d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr93986.c
@@ -0,0 +1,16 @@
+/* PR tree-optimization/93986 - ICE in decompose, at wide-int.h:984
+   { dg-do compile }
+   { dg-options "-O1 -foptimize-strlen -ftree-slp-vectorize" } */
+
+int dd (void);
+
+void ya (int cm)
+{
+  char s2[cm];
+
+  s2[cm-12] = s2[cm-11] = s2[cm-10] = s2[cm-9]
+    = s2[cm-8] = s2[cm-7] = s2[cm-6] = s2[cm-5] = ' ';
+
+  if (dd ())
+    __builtin_exit (0);
+}
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index b76b54efbd8..136a72700d9 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -1924,11 +1924,22 @@ maybe_warn_overflow (gimple *stmt, tree len,
   if (TREE_NO_WARNING (dest))
     return;
 
+  /* Use maximum precision to avoid overflow in the addition below.
+     Make sure all operands have the same precision to keep wide_int
+     from ICE'ing.  */
+  const int prec = ADDR_MAX_PRECISION;
+  /* Convenience constants.  */
+  const wide_int diff_min
+    = wi::to_wide (TYPE_MIN_VALUE (ptrdiff_type_node), prec);
+  const wide_int diff_max
+    = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node), prec);
+  const wide_int size_max
+    = wi::to_wide (TYPE_MAX_VALUE (size_type_node), prec);
+
   /* The offset into the destination object computed below and not
      reflected in DESTSIZE.  */
   wide_int offrng[2];
-  const int off_prec = TYPE_PRECISION (ptrdiff_type_node);
-  offrng[0] = offrng[1] = wi::zero (off_prec);
+  offrng[0] = offrng[1] = wi::zero (prec);
 
   if (!si)
     {
@@ -1943,13 +1954,14 @@ maybe_warn_overflow (gimple *stmt, tree len,
 	  ref = TREE_OPERAND (ref, 0);
 	  if (get_range (off, offrng, rvals))
 	    {
-	      offrng[0] = offrng[0].from (offrng[0], off_prec, SIGNED);
-	      offrng[1] = offrng[1].from (offrng[1], off_prec, SIGNED);
+	      /* Convert offsets to the expected precision.  */
+	      offrng[0] = wide_int::from (offrng[0], prec, SIGNED);
+	      offrng[1] = wide_int::from (offrng[1], prec, SIGNED);
 	    }
 	  else
 	    {
-	      offrng[0] = wi::to_wide (TYPE_MIN_VALUE (ptrdiff_type_node));
-	      offrng[1] = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
+	      offrng[0] = diff_min;
+	      offrng[1] = diff_max;
 	    }
 	}
 
@@ -1960,13 +1972,13 @@ maybe_warn_overflow (gimple *stmt, tree len,
 	  wide_int memoffrng[2];
 	  if (get_range (mem_off, memoffrng, rvals))
 	    {
-	      offrng[0] += memoffrng[0];
-	      offrng[1] += memoffrng[1];
+	      offrng[0] += wide_int::from (memoffrng[0], prec, SIGNED);
+	      offrng[1] += wide_int::from (memoffrng[1], prec, SIGNED);
 	    }
 	  else
 	    {
-	      offrng[0] = wi::to_wide (TYPE_MIN_VALUE (ptrdiff_type_node));
-	      offrng[1] = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
+	      offrng[0] = diff_min;
+	      offrng[1] = diff_max;
 	    }
 	}
 
@@ -1974,8 +1986,8 @@ maybe_warn_overflow (gimple *stmt, tree len,
       if (int idx = get_stridx (ref, stroffrng, rvals))
 	{
 	  si = get_strinfo (idx);
-	  offrng[0] += stroffrng[0];
-	  offrng[1] += stroffrng[1];
+	  offrng[0] += wide_int::from (stroffrng[0], prec, SIGNED);
+	  offrng[1] += wide_int::from (stroffrng[1], prec, SIGNED);
 	}
     }
 
@@ -1995,7 +2007,6 @@ maybe_warn_overflow (gimple *stmt, tree len,
   /* Compute the range of sizes of the destination object.  The range
      is constant for declared objects but may be a range for allocated
      objects.  */
-  const int siz_prec = TYPE_PRECISION (size_type_node);
   wide_int sizrng[2];
   if (si)
     {
@@ -2003,7 +2014,7 @@ maybe_warn_overflow (gimple *stmt, tree len,
       alloc_call = si->alloc;
     }
   else
-    offrng[0] = offrng[1] = wi::zero (off_prec);
+    offrng[0] = offrng[1] = wi::zero (prec);
 
   if (!destsize)
     {
@@ -2014,7 +2025,7 @@ maybe_warn_overflow (gimple *stmt, tree len,
 	{
 	  /* Remember OFF but clear OFFRNG that may have been set above.  */
 	  destoff = off;
-	  offrng[0] = offrng[1] = wi::zero (off_prec);
+	  offrng[0] = offrng[1] = wi::zero (prec);
 
 	  if (destdecl && TREE_CODE (destdecl) == SSA_NAME)
 	    {
@@ -2030,20 +2041,20 @@ maybe_warn_overflow (gimple *stmt, tree len,
 		 so that overflow in allocated objects whose size depends
 		 on the strlen of the source can still be diagnosed
 		 below.  */
-	      sizrng[0] = wi::zero (siz_prec);
-	      sizrng[1] = wi::to_wide (TYPE_MAX_VALUE (sizetype));
+	      sizrng[0] = wi::zero (prec);
+	      sizrng[1] = size_max;
 	    }
 	}
     }
 
   if (!destsize)
     {
-      sizrng[0] = wi::zero (siz_prec);
-      sizrng[1] = wi::to_wide (TYPE_MAX_VALUE (sizetype));
+      sizrng[0] = wi::zero (prec);
+      sizrng[1] = size_max;
     };
 
-  sizrng[0] = sizrng[0].from (sizrng[0], siz_prec, UNSIGNED);
-  sizrng[1] = sizrng[1].from (sizrng[1], siz_prec, UNSIGNED);
+  sizrng[0] = wide_int::from (sizrng[0], prec, UNSIGNED);
+  sizrng[1] = wide_int::from (sizrng[1], prec, UNSIGNED);
 
   /* Return early if the DESTSIZE size expression is the same as LEN
      and the offset into the destination is zero.  This might happen
@@ -2056,6 +2067,9 @@ maybe_warn_overflow (gimple *stmt, tree len,
   if (!get_range (len, lenrng, rvals))
     return;
 
+  lenrng[0] = wide_int::from (lenrng[0], prec, SIGNED);
+  lenrng[1] = wide_int::from (lenrng[1], prec, SIGNED);
+
   if (plus_one)
     {
       lenrng[0] += 1;
@@ -2259,8 +2273,7 @@ maybe_warn_overflow (gimple *stmt, tree len,
       else if (sizrng[0] == 0)
 	{
 	  /* Avoid printing impossible sizes.  */
-	  if (wi::ltu_p (sizrng[1],
-			 wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2))
+	  if (wi::ltu_p (sizrng[1], diff_max - 2))
 	    inform (gimple_location (alloc_call),
 		    "at offset %s to an object with size at most %wu "
 		    "declared here",
@@ -2284,8 +2297,7 @@ maybe_warn_overflow (gimple *stmt, tree len,
   else if (sizrng[0] == 0)
     {
       /* Avoid printing impossible sizes.  */
-      if (wi::ltu_p (sizrng[1],
-		     wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2))
+      if (wi::ltu_p (sizrng[1], diff_max - 2))
 	inform (gimple_location (alloc_call),
 		"at offset %s to an object with size at most %wu allocated "
 		"by %qD here",

Reply via email to