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",
