[committed] Fix a couple minor problems with string length range computations

Jeff Law law@redhat.com
Wed Jan 2 06:02:00 GMT 2019


So the first of probably 3 patches to fix issues with string length
computations now that the underlying infrastructure is in place.  Like
the prior patches, this is primarily Martin's work.  My contribution has
been to break it down into more manageable hunks.

In this patch we change the external API for get_range_strlen to pass in
the c_strlen_data pointer.  Hence the various changes in client code
such as builtins.c, calls.c, tree-ssa-strlen.c, etc.

Internally in the public get_range_strlen we also massage the result of
the internal call slightly.  For example, if the internal call fails, we
set the range info to impossible values that we can check for elsewhere.
 If the internal call didn't set maxbound, then we set it to an
appropriate value, etc.

With the change in how failures are reported we twiddle the sprintf call
site slightly more than the others since it cares.  Ultimately it's a
slight simplification.  This patch also fixes when we set the unlikely
range.

There's a few new tests and Martin made a couple tweaks to existing
tests that are included in this patch.

Bootstrapped and regression tested on x86_64.  My testers will iterate
on this overnight for the other targets.

Committing to the trunk momentarily.

Jeff
-------------- next part --------------
	* gimple-fold.h (get_range_strlen): Update prototype.
	* builtins.c (check_access): Update call to get_range_strlen to use
	c_strlen_data pointer.   Change various variable accesses to instead
	pull data from the c_strlen_data structure.
	(check_strncat_sizes, expand_builtin_strncat): Likewise.
	* calls.c (maybe_warn_nonstring_arg): Likewise.
	* tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Likewise.  Reset
	minimum length if maximum lengh is unknown.
	* gimple-ssa-sprintf.c (get_string_length): Likewise.  Drop code
	that used c_strlen, it's no longer needed.  Restructure slightly.
	(format_string): Set unlikely range appropriately.
	* gimple-fold.c (get_range_strlen): Update comments.  Fix minor
	formatting issues.
	(get_range_strlen):  Accept c_strlen_data pointer for external
	call sites as well.  Pass through to call to internal get_range_strlen.
	Adjust minlen, maxlen and maxbound as needed.
	(get_maxval_strlen): Update comments.
	(gimple_fold_builtin_strlen):  Update call to get_range_strlen
	to use c_strlen_data pointer.  Change variable accesses to instead
	use c_strlen_data data members.

	
	* gcc.dg/strlenopt-40.c: Disable a couple tests.
	* gcc.dg/strlenopt-48.c: Disable a couple tests.
	* gcc.dg/strlenopt-59.c: New test.
	* gcc.dg/tree-ssa/builtin-snprintf-5.c: New test.
	* g++.dg/init/strlen.C: New test.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 9fa11e158c3..28155b8c0aa 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3341,7 +3341,10 @@ check_access (tree exp, tree, tree, tree dstwrite,
 	     the upper bound given by MAXREAD add one to it for
 	     the terminating nul.  Otherwise, set it to one for
 	     the same reason, or to MAXREAD as appropriate.  */
-	  get_range_strlen (srcstr, range);
+	  c_strlen_data lendata = { };
+	  get_range_strlen (srcstr, &lendata, /* eltsize = */ 1);
+	  range[0] = lendata.minlen;
+	  range[1] = lendata.maxbound;
 	  if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
 	    {
 	      if (maxread && tree_int_cst_le (maxread, range[0]))
@@ -4209,8 +4212,8 @@ check_strncat_sizes (tree exp, tree objsize)
 
   /* Try to determine the range of lengths that the source expression
      refers to.  */
-  tree lenrange[2];
-  get_range_strlen (src, lenrange);
+  c_strlen_data lendata = { };
+  get_range_strlen (src, &lendata, /* eltsize = */ 1);
 
   /* Try to verify that the destination is big enough for the shortest
      string.  */
@@ -4224,8 +4227,8 @@ check_strncat_sizes (tree exp, tree objsize)
     }
 
   /* Add one for the terminating nul.  */
-  tree srclen = (lenrange[0]
-		 ? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
+  tree srclen = (lendata.minlen
+		 ? fold_build2 (PLUS_EXPR, size_type_node, lendata.minlen,
 				size_one_node)
 		 : NULL_TREE);
 
@@ -4277,12 +4280,15 @@ expand_builtin_strncat (tree exp, rtx)
   tree slen = c_strlen (src, 1);
 
   /* Try to determine the range of lengths that the source expression
-     refers to.  */
-  tree lenrange[2];
-  if (slen)
-    lenrange[0] = lenrange[1] = slen;
-  else
-    get_range_strlen (src, lenrange);
+     refers to.  Since the lengths are only used for warning and not
+     for code generation disable strict mode below.  */
+  tree maxlen = slen;
+  if (!maxlen)
+    {
+      c_strlen_data lendata = { };
+      get_range_strlen (src, &lendata, /* eltsize = */ 1);
+      maxlen = lendata.maxbound;
+    }
 
   /* Try to verify that the destination is big enough for the shortest
      string.  First try to determine the size of the destination object
@@ -4290,8 +4296,8 @@ expand_builtin_strncat (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   /* Add one for the terminating nul.  */
-  tree srclen = (lenrange[0]
-		 ? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
+  tree srclen = (maxlen
+		 ? fold_build2 (PLUS_EXPR, size_type_node, maxlen,
 				size_one_node)
 		 : NULL_TREE);
 
diff --git a/gcc/calls.c b/gcc/calls.c
index 867ae8197f2..04aafde99f6 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1569,9 +1569,11 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
   /* The bound argument to a bounded string function like strncpy.  */
   tree bound = NULL_TREE;
 
-  /* The range of lengths of a string argument to one of the comparison
-     functions.  If the length is less than the bound it is used instead.  */
-  tree lenrng[2] = { NULL_TREE, NULL_TREE };
+  /* The longest known or possible string argument to one of the comparison
+     functions.  If the length is less than the bound it is used instead.
+     Since the length is only used for warning and not for code generation
+     disable strict mode in the calls to get_range_strlen below.  */
+  tree maxlen = NULL_TREE;
 
   /* It's safe to call "bounded" string functions with a non-string
      argument since the functions provide an explicit bound for this
@@ -1591,11 +1593,15 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
 	   and to adjust the range of the bound of the bounded ones.  */
 	for (unsigned argno = 0;
 	     argno < MIN (nargs, 2)
-	     && !(lenrng[1] && TREE_CODE (lenrng[1]) == INTEGER_CST); argno++)
+	       && !(maxlen && TREE_CODE (maxlen) == INTEGER_CST); argno++)
 	  {
 	    tree arg = CALL_EXPR_ARG (exp, argno);
 	    if (!get_attr_nonstring_decl (arg))
-	      get_range_strlen (arg, lenrng);
+	      {
+		c_strlen_data lendata = { };
+		get_range_strlen (arg, &lendata, /* eltsize = */ 1);
+		maxlen = lendata.maxbound;
+	      }
 	  }
       }
       /* Fall through.  */
@@ -1616,8 +1622,11 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
       {
 	tree arg = CALL_EXPR_ARG (exp, 0);
 	if (!get_attr_nonstring_decl (arg))
-	  get_range_strlen (arg, lenrng);
-
+	  {
+	    c_strlen_data lendata = { };
+	    get_range_strlen (arg, &lendata, /* eltsize = */ 1);
+	    maxlen = lendata.maxbound;
+	  }
 	if (nargs > 1)
 	  bound = CALL_EXPR_ARG (exp, 1);
 	break;
@@ -1658,28 +1667,28 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
 	}
     }
 
-  if (lenrng[1] && TREE_CODE (lenrng[1]) == INTEGER_CST)
+  if (maxlen && !integer_all_onesp (maxlen))
     {
       /* Add one for the nul.  */
-      lenrng[1] = const_binop (PLUS_EXPR, TREE_TYPE (lenrng[1]),
-			       lenrng[1], size_one_node);
+      maxlen = const_binop (PLUS_EXPR, TREE_TYPE (maxlen), maxlen,
+			    size_one_node);
 
       if (!bndrng[0])
 	{
 	  /* Conservatively use the upper bound of the lengths for
 	     both the lower and the upper bound of the operation.  */
-	  bndrng[0] = lenrng[1];
-	  bndrng[1] = lenrng[1];
+	  bndrng[0] = maxlen;
+	  bndrng[1] = maxlen;
 	  bound = void_type_node;
 	}
       else
 	{
 	  /* Replace the bound on the operation with the upper bound
 	     of the length of the string if the latter is smaller.  */
-	  if (tree_int_cst_lt (lenrng[1], bndrng[0]))
-	    bndrng[0] = lenrng[1];
-	  else if (tree_int_cst_lt (lenrng[1], bndrng[1]))
-	    bndrng[1] = lenrng[1];
+	  if (tree_int_cst_lt (maxlen, bndrng[0]))
+	    bndrng[0] = maxlen;
+	  else if (tree_int_cst_lt (maxlen, bndrng[1]))
+	    bndrng[1] = maxlen;
 	}
     }
 
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 5e29f79ee09..6185f98e6bd 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -1502,11 +1502,14 @@ get_range_strlen_tree (tree arg, bitmap *visited,
   return true;
 }
 
-/* Obtain the minimum and maximum string length or minimum and maximum
-   value of ARG in LENGTH[0] and LENGTH[1], respectively.
-   If ARG is an SSA name variable, follow its use-def chains.  When
-   TYPE == 0, if LENGTH[1] is not equal to the length we determine or
-   if we are unable to determine the length or value, return false.
+/* For an ARG referencing one or more strings, try to obtain the range
+   of their lengths, or the size of the largest array ARG referes to if
+   the range of lengths cannot be determined, and store all in *PDATA.
+   For an integer ARG (when RKIND == SRK_INT_VALUE), try to determine
+   the maximum constant value.
+   If ARG is an SSA_NAME, follow its use-def chains.  When RKIND ==
+   SRK_STRLEN, then if PDATA->MAXLEN is not equal to the determined
+   length or if we are unable to determine the length, return false.
    VISITED is a bitmap of visited variables.
    RKIND determines the kind of value or range to obtain (see
    strlen_range_kind).
@@ -1516,8 +1519,7 @@ get_range_strlen_tree (tree arg, bitmap *visited,
    Return true if *PDATA was successfully populated and false otherwise.  */
 
 static bool
-get_range_strlen (tree arg, bitmap *visited,
-		  strlen_range_kind rkind,
+get_range_strlen (tree arg, bitmap *visited, strlen_range_kind rkind,
 		  c_strlen_data *pdata, bool *flexp, unsigned eltsize)
 {
 
@@ -1612,6 +1614,7 @@ get_range_strlen (tree arg, bitmap *visited,
         return false;
     }
 }
+
 /* Determine the minimum and maximum value or string length that ARG
    refers to and store each in the first two elements of MINMAXLEN.
    For expressions that point to strings of unknown lengths that are
@@ -1638,47 +1641,39 @@ get_range_strlen (tree arg, bitmap *visited,
    4 for wide characer strings.  ELTSIZE is by default 1.  */
 
 bool
-get_range_strlen (tree arg, tree minmaxlen[2], unsigned eltsize,
-		  bool strict, tree *nonstr /* = NULL */)
+get_range_strlen (tree arg, c_strlen_data *pdata, unsigned eltsize, bool strict)
 {
   bitmap visited = NULL;
 
-  minmaxlen[0] = NULL_TREE;
-  minmaxlen[1] = NULL_TREE;
-
-  tree nonstrbuf;
-  if (!nonstr)
-    nonstr = &nonstrbuf;
-  *nonstr = NULL_TREE;
-
   bool flexarray = false;
-  c_strlen_data lendata = { };
-  if (!get_range_strlen (arg, &visited,
-			 strict ? SRK_LENRANGE : SRK_LENRANGE_2,
-			 &lendata, &flexarray, eltsize))
-    {
-      minmaxlen[0] = NULL_TREE;
-      minmaxlen[1] = NULL_TREE;
-    }
-  else
+  if (!get_range_strlen (arg, &visited, strict ? SRK_LENRANGE : SRK_LENRANGE_2, pdata, &flexarray, eltsize))
     {
-      minmaxlen[0] = lendata.minlen;
-      minmaxlen[1] = lendata.maxlen;
+      /* On failure extend the length range to an impossible maximum
+	 (a valid MAXLEN must be less than PTRDIFF_MAX - 1).  Other
+	 members can stay unchanged regardless.  */
+      pdata->minlen = ssize_int (0);
+      pdata->maxlen = build_all_ones_cst (size_type_node);
     }
+  else if (!pdata->minlen)
+    pdata->minlen = ssize_int (0);
+
+  /* Unless its null, leave the more conservative MAXBOUND unchanged.  */
+  if (!pdata->maxbound)
+    pdata->maxbound = pdata->maxlen;
 
-  *nonstr = lendata.decl;
   if (visited)
     BITMAP_FREE (visited);
 
   return flexarray;
 }
 
-/* Return the maximum string length for ARG, counting by TYPE
-   (1, 2 or 4 for normal or wide chars).  NONSTR indicates
-   if the caller is prepared to handle unterminated strings.
+/* Return the maximum value for ARG given RKIND (see strlen_range_kind).
+   For ARG of pointer types, NONSTR indicates if the caller is prepared
+   to handle unterminated strings.   For integer ARG and when RKIND ==
+   SRK_INT_VALUE, NONSTR must be null.
 
-   If an unterminated string is discovered and our caller handles
-   unterminated strings, then bubble up the offending DECL and
+   If an unterminated array is discovered and our caller handles
+   unterminated arrays, then bubble up the offending DECL and
    return the maximum size.  Otherwise return NULL.  */
 
 static tree
@@ -1692,10 +1687,15 @@ get_maxval_strlen (tree arg, strlen_range_kind rkind, tree *nonstr = NULL)
 
   bitmap visited = NULL;
 
-  bool dummy;
+  /* Reset DATA.MAXLEN if the call fails or when DATA.MAXLEN
+     is unbounded.  */
   c_strlen_data lendata = { };
+  bool dummy;
   if (!get_range_strlen (arg, &visited, rkind, &lendata, &dummy, 1))
     lendata.maxlen = NULL_TREE;
+  else if (lendata.maxlen && integer_all_onesp (lendata.maxlen))
+    lendata.maxlen = NULL_TREE;
+
   if (visited)
     BITMAP_FREE (visited);
 
@@ -3668,21 +3668,19 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
   wide_int minlen;
   wide_int maxlen;
 
-  /* Set to non-null if ARG refers to an unterminated array.  */
-  tree nonstr;
-  tree lenrange[2];
-  if (!get_range_strlen (arg, lenrange, 1, true, &nonstr)
-      && !nonstr
-      && lenrange[0] && TREE_CODE (lenrange[0]) == INTEGER_CST
-      && lenrange[1] && TREE_CODE (lenrange[1]) == INTEGER_CST)
+  c_strlen_data lendata = { };
+  if (!get_range_strlen (arg, &lendata, /* eltsize = */ 1)
+      && !lendata.decl
+      && lendata.minlen && TREE_CODE (lendata.minlen) == INTEGER_CST
+      && lendata.maxlen && TREE_CODE (lendata.maxlen) == INTEGER_CST)
     {
       /* The range of lengths refers to either a single constant
 	 string or to the longest and shortest constant string
 	 referenced by the argument of the strlen() call, or to
 	 the strings that can possibly be stored in the arrays
 	 the argument refers to.  */
-      minlen = wi::to_wide (lenrange[0]);
-      maxlen = wi::to_wide (lenrange[1]);
+      minlen = wi::to_wide (lendata.minlen);
+      maxlen = wi::to_wide (lendata.maxlen);
     }
   else
     {
@@ -3694,9 +3692,12 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
 
   if (minlen == maxlen)
     {
-      lenrange[0] = force_gimple_operand_gsi (gsi, lenrange[0], true, NULL,
-					      true, GSI_SAME_STMT);
-      replace_call_with_value (gsi, lenrange[0]);
+      /* Fold the strlen call to a constant.  */
+      tree type = TREE_TYPE (lendata.minlen);
+      tree len = force_gimple_operand_gsi (gsi,
+					   wide_int_to_tree (type, minlen),
+					   true, NULL, true, GSI_SAME_STMT);
+      replace_call_with_value (gsi, len);
       return true;
     }
 
diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h
index 76a65818437..35f1ba26953 100644
--- a/gcc/gimple-fold.h
+++ b/gcc/gimple-fold.h
@@ -25,8 +25,8 @@ along with GCC; see the file COPYING3.  If not see
 extern tree create_tmp_reg_or_ssa_name (tree, gimple *stmt = NULL);
 extern tree canonicalize_constructor_val (tree, tree);
 extern tree get_symbol_constant_value (tree);
-extern bool get_range_strlen (tree, tree[2], unsigned = 1,
-			      bool = false, tree * = NULL);
+struct c_strlen_data;
+extern bool get_range_strlen (tree, c_strlen_data *, unsigned eltsize, bool = false);
 extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree);
 extern bool fold_stmt (gimple_stmt_iterator *);
 extern bool fold_stmt (gimple_stmt_iterator *, tree (*) (tree));
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index fe50c89bc89..65aab5ba092 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -2003,90 +2003,73 @@ get_string_length (tree str, unsigned eltsize)
   if (!str)
     return fmtresult ();
 
-  c_strlen_data data = { };
-  tree slen = c_strlen (str, 1, &data, eltsize);
-  if (slen && TREE_CODE (slen) == INTEGER_CST)
-    {
-      /* The string is properly terminated and
-	 we know its length.  */
-      fmtresult res (tree_to_shwi (slen));
-      res.nonstr = NULL_TREE;
-      return res;
-    }
-  else if (!slen
-	   && data.decl
-	   && data.minlen
-	   && TREE_CODE (data.minlen) == INTEGER_CST)
-    {
-      /* STR was not properly NUL terminated, but we have
-	 length information about the unterminated string.  */
-      fmtresult res (tree_to_shwi (data.minlen));
-      res.nonstr = data.decl;
-      return res;
-    }
-
   /* Determine the length of the shortest and longest string referenced
      by STR.  Strings of unknown lengths are bounded by the sizes of
      arrays that subexpressions of STR may refer to.  Pointers that
-     aren't known to point any such arrays result in LENRANGE[1] set
-     to SIZE_MAX.  NONSTR is set to the declaration of the constant
-     array that is known not to be nul-terminated.  */
-  tree lenrange[2];
-  tree nonstr;
-  bool flexarray = get_range_strlen (str, lenrange, eltsize, false, &nonstr);
+     aren't known to point any such arrays result in LENDATA.MAXLEN
+     set to SIZE_MAX.  */
+  c_strlen_data lendata = { };
+  bool flexarray = get_range_strlen (str, &lendata, eltsize);
+
+  /* Return the default result when nothing is known about the string. */
+  if (integer_all_onesp (lendata.maxbound)
+      && integer_all_onesp (lendata.maxlen))
+    return fmtresult ();
 
-  if (lenrange [0] || lenrange [1])
+  HOST_WIDE_INT min
+    = (tree_fits_uhwi_p (lendata.minlen)
+       ? tree_to_uhwi (lendata.minlen)
+       : 0);
+
+  HOST_WIDE_INT max
+    = (tree_fits_uhwi_p (lendata.maxbound)
+       ? tree_to_uhwi (lendata.maxbound)
+       : HOST_WIDE_INT_M1U);
+
+  const bool unbounded = flexarray || integer_all_onesp (lendata.maxlen);
+
+  /* Set the max/likely counters to unbounded when a minimum is known
+     but the maximum length isn't bounded.  This implies that STR is
+     a conditional expression involving a string of known length and
+     and an expression of unknown/unbounded length.  */
+  if (min
+      && (unsigned HOST_WIDE_INT)min < HOST_WIDE_INT_M1U
+      && unbounded)
+    max = HOST_WIDE_INT_M1U;
+
+  /* get_range_strlen() returns the target value of SIZE_MAX for
+     strings of unknown length.  Bump it up to HOST_WIDE_INT_M1U
+     which may be bigger.  */
+  if ((unsigned HOST_WIDE_INT)min == target_size_max ())
+    min = HOST_WIDE_INT_M1U;
+  if ((unsigned HOST_WIDE_INT)max == target_size_max ())
+    max = HOST_WIDE_INT_M1U;
+
+  fmtresult res (min, max);
+  res.nonstr = lendata.decl;
+
+  /* Set RES.KNOWNRANGE to true if and only if all strings referenced
+     by STR are known to be bounded (though not necessarily by their
+     actual length but perhaps by their maximum possible length).  */
+  if (res.range.max < target_int_max ())
     {
-      HOST_WIDE_INT min
-	= (tree_fits_uhwi_p (lenrange[0])
-	   ? tree_to_uhwi (lenrange[0])
-	   : 0);
-
-      HOST_WIDE_INT max
-	= (tree_fits_uhwi_p (lenrange[1])
-	   ? tree_to_uhwi (lenrange[1])
-	   : HOST_WIDE_INT_M1U);
-
-      /* get_range_strlen() returns the target value of SIZE_MAX for
-	 strings of unknown length.  Bump it up to HOST_WIDE_INT_M1U
-	 which may be bigger.  */
-      if ((unsigned HOST_WIDE_INT)min == target_size_max ())
-	min = HOST_WIDE_INT_M1U;
-      if ((unsigned HOST_WIDE_INT)max == target_size_max ())
-	max = HOST_WIDE_INT_M1U;
-
-      fmtresult res (min, max);
-      res.nonstr = nonstr;
-
-      /* Set RES.KNOWNRANGE to true if and only if all strings referenced
-	 by STR are known to be bounded (though not necessarily by their
-	 actual length but perhaps by their maximum possible length).  */
-      if (res.range.max < target_int_max ())
-	{
-	  res.knownrange = true;
-	  /* When the the length of the longest string is known and not
-	     excessive use it as the likely length of the string(s).  */
-	  res.range.likely = res.range.max;
-	}
-      else
-	{
-	  /* When the upper bound is unknown (it can be zero or excessive)
-	     set the likely length to the greater of 1 and the length of
-	     the shortest string and reset the lower bound to zero.  */
-	  res.range.likely = res.range.min ? res.range.min : warn_level > 1;
-	  res.range.min = 0;
-	}
-
-      /* If the range of string length has been estimated from the size
-	 of an array at the end of a struct assume that it's longer than
-	 the array bound says it is in case it's used as a poor man's
-	 flexible array member, such as in struct S { char a[4]; };  */
-      res.range.unlikely = flexarray ? HOST_WIDE_INT_MAX : res.range.max;
-
-      return res;
+      res.knownrange = true;
+      /* When the the length of the longest string is known and not
+	 excessive use it as the likely length of the string(s).  */
+      res.range.likely = res.range.max;
+    }
+  else
+    {
+      /* When the upper bound is unknown (it can be zero or excessive)
+	 set the likely length to the greater of 1 and the length of
+	 the shortest string and reset the lower bound to zero.  */
+      res.range.likely = res.range.min ? res.range.min : warn_level > 1;
+      res.range.min = 0;
     }
 
-  return fmtresult ();
+  res.range.unlikely = unbounded ? HOST_WIDE_INT_MAX : res.range.max;
+
+  return res;
 }
 
 /* Return the minimum and maximum number of characters formatted
@@ -2326,6 +2309,8 @@ format_string (const directive &dir, tree arg, vr_values *)
 	  if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max)
 	    res.range.max = dir.prec[1];
 	  res.range.likely = dir.prec[1] ? warn_level > 1 : 0;
+	  if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.unlikely)
+	    res.range.unlikely = dir.prec[1];
 	}
       else if (slen.range.min >= target_int_max ())
 	{
@@ -2335,6 +2320,7 @@ format_string (const directive &dir, tree arg, vr_values *)
 	     empty, while at level 1 they are assumed to be one byte
 	     long.  */
 	  res.range.likely = warn_level > 1;
+	  res.range.unlikely = HOST_WIDE_INT_MAX;
 	}
       else
 	{
@@ -2344,8 +2330,6 @@ format_string (const directive &dir, tree arg, vr_values *)
 	  if (res.range.likely >= target_int_max ())
 	    res.range.likely = warn_level > 1;
 	}
-
-      res.range.unlikely = res.range.max;
     }
 
   /* If the argument isn't a nul-terminated string and the number
diff --git a/gcc/testsuite/gcc.dg/strlenopt-40.c b/gcc/testsuite/gcc.dg/strlenopt-40.c
index f4577d69e1a..e24b510deeb 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-40.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-40.c
@@ -124,8 +124,8 @@ void elim_global_arrays (int i)
   ELIM_TRUE (strlen (a7) < sizeof a7);
 
   ELIM_TRUE (strlen (ax) != DIFF_MAX);
-  ELIM_TRUE (strlen (ax) != DIFF_MAX - 1);
-  ELIM_TRUE (strlen (ax) < DIFF_MAX - 1);
+  /* ELIM_TRUE (strlen (ax) != DIFF_MAX - 1); */
+  /* ELIM_TRUE (strlen (ax) < DIFF_MAX - 1); */
 }
 
 void elim_pointer_to_arrays (void)
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 80309e43998..f64bc9bea79 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -1989,15 +1989,18 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
     lenrange[0] = lenrange[1] = wi::shwi (~sidx, prec);
   else
     {
-      tree range[2];
-      get_range_strlen (src, range);
-      if (range[0] != NULL_TREE
-	  && TREE_CODE (range[0]) == INTEGER_CST
-	  && range[1] != NULL_TREE
-	  && TREE_CODE (range[1]) == INTEGER_CST)
-	{
-	  lenrange[0] = wi::to_wide (range[0], prec);
-	  lenrange[1] = wi::to_wide (range[1], prec);
+      c_strlen_data lendata = { };
+      get_range_strlen (src, &lendata, /* eltsize = */1);
+      if (TREE_CODE (lendata.minlen) == INTEGER_CST
+	  && TREE_CODE (lendata.maxbound) == INTEGER_CST)
+	{
+	  /* When LENDATA.MAXLEN is unknown, reset LENDATA.MINLEN
+	     which stores the length of the shortest known string.  */
+	  if (integer_all_onesp (lendata.maxlen))
+	    lenrange[0] = wi::shwi (0, prec);
+	  else
+	    lenrange[0] = wi::to_wide (lendata.minlen, prec);
+	  lenrange[1] = wi::to_wide (lendata.maxbound, prec);
 	}
       else
 	{


More information about the Gcc-patches mailing list