diff mbox series

[PATCH improve early strlen range folding (PR 83671)

Message ID 9ab7f1fb-2407-7f59-244a-709fd5305c3e@gmail.com
State New
Headers show
Series [PATCH improve early strlen range folding (PR 83671) | expand

Commit Message

Martin Sebor Jan. 6, 2018, 10:04 p.m. UTC
Bug 83671 - Fix for false positive reported by -Wstringop-overflow
does not work at -O1, points out that the string length range
optimization implemented as a solution for bug 83373 doesn't help
at -O1.  The root cause is that the fix was added to the strlen
pass that doesn't run at -O1.

The string length range computation doesn't depend on the strlen
pass, and so the range can be set earlier, in gimple-fold, and
its results made available even at -O1.  The attached patch
changes the gimple_fold_builtin_strlen() function to do that.

While testing the change I came across a number of other simple
strlen cases that currently aren't handled, some at -O1, others
at all.  I added code to handle some of the simplest of them
and opened bugs to remind us/myself to get back to the rest in
the future (pr83693 and pr83702).  The significant enhancement
is handling arrays of arrays with non-constant indices and
pointers to such things, such as in:

   char a[2][7];

   void f (int i)
   {
     if (strlen (a[i]) > 6)   // eliminated with the patch
       abort ();
   }

Attached is a near-minimal patch to handle PR 83671.

Martin

Comments

Richard Biener Jan. 8, 2018, 11:28 a.m. UTC | #1
On Sat, Jan 6, 2018 at 11:04 PM, Martin Sebor <msebor@gmail.com> wrote:
> Bug 83671 - Fix for false positive reported by -Wstringop-overflow
> does not work at -O1, points out that the string length range
> optimization implemented as a solution for bug 83373 doesn't help
> at -O1.  The root cause is that the fix was added to the strlen
> pass that doesn't run at -O1.
>
> The string length range computation doesn't depend on the strlen
> pass, and so the range can be set earlier, in gimple-fold, and
> its results made available even at -O1.  The attached patch
> changes the gimple_fold_builtin_strlen() function to do that.
>
> While testing the change I came across a number of other simple
> strlen cases that currently aren't handled, some at -O1, others
> at all.  I added code to handle some of the simplest of them
> and opened bugs to remind us/myself to get back to the rest in
> the future (pr83693 and pr83702).  The significant enhancement
> is handling arrays of arrays with non-constant indices and
> pointers to such things, such as in:
>
>   char a[2][7];
>
>   void f (int i)
>   {
>     if (strlen (a[i]) > 6)   // eliminated with the patch
>       abort ();
>   }
>
> Attached is a near-minimal patch to handle PR 83671.

Please don't use set_range_info form insinde fold_stmt (), this is
IMHO a layering violation.

Why not restrict -Wstrinop-overflow to -O2+?

Richard.

> Martin
Martin Sebor Jan. 9, 2018, 3:05 a.m. UTC | #2
On 01/08/2018 04:28 AM, Richard Biener wrote:
> On Sat, Jan 6, 2018 at 11:04 PM, Martin Sebor <msebor@gmail.com> wrote:
>> Bug 83671 - Fix for false positive reported by -Wstringop-overflow
>> does not work at -O1, points out that the string length range
>> optimization implemented as a solution for bug 83373 doesn't help
>> at -O1.  The root cause is that the fix was added to the strlen
>> pass that doesn't run at -O1.
>>
>> The string length range computation doesn't depend on the strlen
>> pass, and so the range can be set earlier, in gimple-fold, and
>> its results made available even at -O1.  The attached patch
>> changes the gimple_fold_builtin_strlen() function to do that.
>>
>> While testing the change I came across a number of other simple
>> strlen cases that currently aren't handled, some at -O1, others
>> at all.  I added code to handle some of the simplest of them
>> and opened bugs to remind us/myself to get back to the rest in
>> the future (pr83693 and pr83702).  The significant enhancement
>> is handling arrays of arrays with non-constant indices and
>> pointers to such things, such as in:
>>
>>   char a[2][7];
>>
>>   void f (int i)
>>   {
>>     if (strlen (a[i]) > 6)   // eliminated with the patch
>>       abort ();
>>   }
>>
>> Attached is a near-minimal patch to handle PR 83671.
>
> Please don't use set_range_info form insinde fold_stmt (), this is
> IMHO a layering violation.
>
> Why not restrict -Wstrinop-overflow to -O2+?

For simple cases, -Wstrinop-overflow works even without
optimization.  Given the notoriety of buffer overflows and
because not all software is or can always be compiled with
-O2 or even with -O1, I think it's important to keep it working.

I initially did consider resolving PR 83671 as won't fix because
at -O1 neither the strlen pass not VRP runs.  But as I explained
above, because the range doesn't depend on the strlen pass and
can be determined and set anywhere, regardless of the optimization 
level, doing it outside the pass seems like a win-win.  Exposing
the range early improves downstream transformations before strlen
runs or even when it doesn't, and as a bonus, the warning also
disappears.

But I don't understand the concern.  The range is only set when
the expression is an SSA_NAME, and setting in this function here
seems no different than setting it in one if its callers (which
may be, indirectly. in a pass like strlen).  If you have
a suggestion for a better place to set it I'd be happy to move
it, I just don't see the problem.

In case it wasn't clear from my description (and got obscured
by the example above), this change is not about just arrays of
arrays (that's a minor improvement I included in it).  It's
bout letting GCC optimize all strlen(array) expressions at -O1,
such as the following:

   char a[32];

   void f (void)
   {
     if (strlen (a) > 31)
       abort ();
   }

Martin
Jeff Law Jan. 10, 2018, 12:06 a.m. UTC | #3
On 01/08/2018 08:05 PM, Martin Sebor wrote:
> On 01/08/2018 04:28 AM, Richard Biener wrote:
>> On Sat, Jan 6, 2018 at 11:04 PM, Martin Sebor <msebor@gmail.com> wrote:
>>> Bug 83671 - Fix for false positive reported by -Wstringop-overflow
>>> does not work at -O1, points out that the string length range
>>> optimization implemented as a solution for bug 83373 doesn't help
>>> at -O1.  The root cause is that the fix was added to the strlen
>>> pass that doesn't run at -O1.
>>>
>>> The string length range computation doesn't depend on the strlen
>>> pass, and so the range can be set earlier, in gimple-fold, and
>>> its results made available even at -O1.  The attached patch
>>> changes the gimple_fold_builtin_strlen() function to do that.
>>>
>>> While testing the change I came across a number of other simple
>>> strlen cases that currently aren't handled, some at -O1, others
>>> at all.  I added code to handle some of the simplest of them
>>> and opened bugs to remind us/myself to get back to the rest in
>>> the future (pr83693 and pr83702).  The significant enhancement
>>> is handling arrays of arrays with non-constant indices and
>>> pointers to such things, such as in:
>>>
>>>   char a[2][7];
>>>
>>>   void f (int i)
>>>   {
>>>     if (strlen (a[i]) > 6)   // eliminated with the patch
>>>       abort ();
>>>   }
>>>
>>> Attached is a near-minimal patch to handle PR 83671.
>>
>> Please don't use set_range_info form insinde fold_stmt (), this is
>> IMHO a layering violation.
>>
>> Why not restrict -Wstrinop-overflow to -O2+?
> 
> For simple cases, -Wstrinop-overflow works even without
> optimization.  Given the notoriety of buffer overflows and
> because not all software is or can always be compiled with
> -O2 or even with -O1, I think it's important to keep it working.
If something can't be compiled with -O2, then it can't really be
compiled with -O1.  In that kind of scenario someone is papering over a bug.

While I am sympathetic to the desire to provide as many and as accurate
of warnings at -O1 as -O2, we have to balance that against the cost,
both in terms of compile-time and in terms of maintenance if we end up
having to duplicate code.  We have to evaluate each case individually.




> 
> I initially did consider resolving PR 83671 as won't fix because
> at -O1 neither the strlen pass not VRP runs.  But as I explained
> above, because the range doesn't depend on the strlen pass and
> can be determined and set anywhere, regardless of the optimization
> level, doing it outside the pass seems like a win-win.  Exposing
> the range early improves downstream transformations before strlen
> runs or even when it doesn't, and as a bonus, the warning also
> disappears.
Outside is a lose if we have to duplicate the analysis or significant
chunks of code, or even small amounts of code if they are complex.


> 
> But I don't understand the concern.  The range is only set when
> the expression is an SSA_NAME, and setting in this function here
> seems no different than setting it in one if its callers (which
> may be, indirectly. in a pass like strlen).  If you have
> a suggestion for a better place to set it I'd be happy to move
> it, I just don't see the problem.So identifying and setting the range is analysis.  Folding the statement
is an optimization.  Setting the range from inside a folding routine is
a layering violation in general.  Though frankly we have not been
particularly good at separation of analysis from optimization.

But I suspect this is a truly trivial part of the patch, so let's just
drop it as controversial.

I'll try to look at the actual patch in detail tomorrow.

Jeff
Jeff Law Jan. 10, 2018, 7:30 p.m. UTC | #4
On 01/06/2018 03:04 PM, Martin Sebor wrote:
> Bug 83671 - Fix for false positive reported by -Wstringop-overflow
> does not work at -O1, points out that the string length range
> optimization implemented as a solution for bug 83373 doesn't help
> at -O1.  The root cause is that the fix was added to the strlen
> pass that doesn't run at -O1.
> 
> The string length range computation doesn't depend on the strlen
> pass, and so the range can be set earlier, in gimple-fold, and
> its results made available even at -O1.  The attached patch
> changes the gimple_fold_builtin_strlen() function to do that.
> 
> While testing the change I came across a number of other simple
> strlen cases that currently aren't handled, some at -O1, others
> at all.  I added code to handle some of the simplest of them
> and opened bugs to remind us/myself to get back to the rest in
> the future (pr83693 and pr83702).  The significant enhancement
> is handling arrays of arrays with non-constant indices and
> pointers to such things, such as in:
> 
>   char a[2][7];
> 
>   void f (int i)
>   {
>     if (strlen (a[i]) > 6)   // eliminated with the patch
>       abort ();
>   }
> 
> Attached is a near-minimal patch to handle PR 83671.
> 
> Martin
> 
> gcc-83671.diff
> 
> 
> PR tree-optimization/83671 - Fix for false positive reported by -Wstringop-overflow does not work with inlining
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR tree-optimization/83671
> 	* gcc.dg/strlenopt-40.c: New test.
> 	* gcc.dg/strlenopt-41.c: New test.
> 
> gcc/ChangeLog:
> 
> 	PR tree-optimization/83671
> 	* builtins.c (c_strlen): Unconditionally return zero for the empty
> 	string.
> 	Use -Warray-bounds for warnings.
> 	* gimple-fold.c (get_range_strlen): Handle non-constant lengths
> 	for non-constant array indices with COMPONENT_REF, arrays of
> 	arrays, and pointers to arrays.
> 	(gimple_fold_builtin_strlen): Determine and set length range for
> 	non-constant character arrays.
> 
> @@ -1311,14 +1311,30 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
[ ... ]
> +	  else if (TREE_CODE (TREE_OPERAND (op, 0)) == COMPONENT_REF && fuzzy)
> +	    {
> +	      tree idx = TREE_OPERAND (op, 1);
> +
> +	      arg = TREE_OPERAND (op, 0);
> +	      tree optype = TREE_TYPE (arg);
> +	      if (tree dom = TYPE_DOMAIN (optype))
> +		if (tree bound = TYPE_MAX_VALUE (dom))
> +		  if (TREE_CODE (bound) == INTEGER_CST
> +		      && TREE_CODE (idx) == INTEGER_CST
> +		      && tree_int_cst_lt (bound, idx))
> +		    return false;
This deserves a comment what are you looking for and why do you return
false when you find it.  I think I know the answers, but it'd be clearer
to future readers to spell it out in a comment.

With that comment and removal of the controversial set_range_info, I
think this is OK.

Jeff
H.J. Lu Jan. 11, 2018, 1:30 a.m. UTC | #5
On Sat, Jan 6, 2018 at 2:04 PM, Martin Sebor <msebor@gmail.com> wrote:
> Bug 83671 - Fix for false positive reported by -Wstringop-overflow
> does not work at -O1, points out that the string length range
> optimization implemented as a solution for bug 83373 doesn't help
> at -O1.  The root cause is that the fix was added to the strlen
> pass that doesn't run at -O1.
>
> The string length range computation doesn't depend on the strlen
> pass, and so the range can be set earlier, in gimple-fold, and
> its results made available even at -O1.  The attached patch
> changes the gimple_fold_builtin_strlen() function to do that.
>
> While testing the change I came across a number of other simple
> strlen cases that currently aren't handled, some at -O1, others
> at all.  I added code to handle some of the simplest of them
> and opened bugs to remind us/myself to get back to the rest in
> the future (pr83693 and pr83702).  The significant enhancement
> is handling arrays of arrays with non-constant indices and
> pointers to such things, such as in:
>
>   char a[2][7];
>
>   void f (int i)
>   {
>     if (strlen (a[i]) > 6)   // eliminated with the patch
>       abort ();
>   }
>
> Attached is a near-minimal patch to handle PR 83671.
>

This may have caused:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83781.
Martin Sebor Jan. 11, 2018, 5:18 a.m. UTC | #6
On 01/10/2018 06:30 PM, H.J. Lu wrote:
> On Sat, Jan 6, 2018 at 2:04 PM, Martin Sebor <msebor@gmail.com> wrote:
>> Bug 83671 - Fix for false positive reported by -Wstringop-overflow
>> does not work at -O1, points out that the string length range
>> optimization implemented as a solution for bug 83373 doesn't help
>> at -O1.  The root cause is that the fix was added to the strlen
>> pass that doesn't run at -O1.
>>
>> The string length range computation doesn't depend on the strlen
>> pass, and so the range can be set earlier, in gimple-fold, and
>> its results made available even at -O1.  The attached patch
>> changes the gimple_fold_builtin_strlen() function to do that.
>>
>> While testing the change I came across a number of other simple
>> strlen cases that currently aren't handled, some at -O1, others
>> at all.  I added code to handle some of the simplest of them
>> and opened bugs to remind us/myself to get back to the rest in
>> the future (pr83693 and pr83702).  The significant enhancement
>> is handling arrays of arrays with non-constant indices and
>> pointers to such things, such as in:
>>
>>   char a[2][7];
>>
>>   void f (int i)
>>   {
>>     if (strlen (a[i]) > 6)   // eliminated with the patch
>>       abort ();
>>   }
>>
>> Attached is a near-minimal patch to handle PR 83671.
>>
>
> This may have caused:
>
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83781.

Yes, it did.  I committed r256477 to fix the problem.  With
it, plain x86_64 bootstrap as well as with --with-arch=corei7
--with-cpu=corei7 succeed.

Sorry for the breakage.

Martin
diff mbox series

Patch

PR tree-optimization/83671 - Fix for false positive reported by -Wstringop-overflow does not work with inlining

gcc/testsuite/ChangeLog:

	PR tree-optimization/83671
	* gcc.dg/strlenopt-40.c: New test.
	* gcc.dg/strlenopt-41.c: New test.

gcc/ChangeLog:

	PR tree-optimization/83671
	* builtins.c (c_strlen): Unconditionally return zero for the empty
	string.
	Use -Warray-bounds for warnings.
	* gimple-fold.c (get_range_strlen): Handle non-constant lengths
	for non-constant array indices with COMPONENT_REF, arrays of
	arrays, and pointers to arrays.
	(gimple_fold_builtin_strlen): Determine and set length range for
	non-constant character arrays.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 1d6e69d..fa07a48 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -621,6 +621,9 @@  c_strlen (tree src, int only_value)
 	  return NULL_TREE;
 	}
 
+      if (!maxelts)
+	return ssize_int (0);
+
       /* We don't know the starting offset, but we do know that the string
 	 has no internal zero bytes.  We can assume that the offset falls
 	 within the bounds of the string; otherwise, the programmer deserves
@@ -651,7 +654,8 @@  c_strlen (tree src, int only_value)
       if (only_value != 2
 	  && !TREE_NO_WARNING (src))
         {
-	  warning_at (loc, 0, "offset %qwi outside bounds of constant string",
+	  warning_at (loc, OPT_Warray_bounds,
+		      "offset %qwi outside bounds of constant string",
 		      eltoff);
           TREE_NO_WARNING (src) = 1;
         }
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 7e4cb74..2dead65 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -1299,7 +1299,7 @@  static bool
 get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
 		  bool fuzzy, bool *flexp)
 {
-  tree var, val;
+  tree var, val = NULL_TREE;
   gimple *def_stmt;
 
   /* The minimum and maximum length.  The MAXLEN pointer stays unchanged
@@ -1311,14 +1311,30 @@  get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
     {
       /* We can end up with &(*iftmp_1)[0] here as well, so handle it.  */
       if (TREE_CODE (arg) == ADDR_EXPR
-	  && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF
-	  && integer_zerop (TREE_OPERAND (TREE_OPERAND (arg, 0), 1)))
+	  && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF)
 	{
-	  tree aop0 = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
-	  if (TREE_CODE (aop0) == INDIRECT_REF
-	      && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
-	    return get_range_strlen (TREE_OPERAND (aop0, 0),
-				     length, visited, type, fuzzy, flexp);
+	  tree op = TREE_OPERAND (arg, 0);
+	  if (integer_zerop (TREE_OPERAND (op, 1)))
+	    {
+	      tree aop0 = TREE_OPERAND (op, 0);
+	      if (TREE_CODE (aop0) == INDIRECT_REF
+		  && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
+		return get_range_strlen (TREE_OPERAND (aop0, 0),
+					 length, visited, type, fuzzy, flexp);
+	    }
+	  else if (TREE_CODE (TREE_OPERAND (op, 0)) == COMPONENT_REF && fuzzy)
+	    {
+	      tree idx = TREE_OPERAND (op, 1);
+
+	      arg = TREE_OPERAND (op, 0);
+	      tree optype = TREE_TYPE (arg);
+	      if (tree dom = TYPE_DOMAIN (optype))
+		if (tree bound = TYPE_MAX_VALUE (dom))
+		  if (TREE_CODE (bound) == INTEGER_CST
+		      && TREE_CODE (idx) == INTEGER_CST
+		      && tree_int_cst_lt (bound, idx))
+		    return false;
+	    }
 	}
 
       if (type == 2)
@@ -1337,21 +1353,48 @@  get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
 	    return get_range_strlen (TREE_OPERAND (arg, 0), length,
 				     visited, type, fuzzy, flexp);
 
-	  if (TREE_CODE (arg) == COMPONENT_REF
+	  if (TREE_CODE (arg) == ARRAY_REF)
+	    {
+	      tree type = TREE_TYPE (TREE_OPERAND (arg, 0));
+
+	      while (TREE_CODE (type) == ARRAY_TYPE
+		     && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
+		type = TREE_TYPE (type);
+
+	      val = TYPE_SIZE_UNIT (type);
+	      if (!val || integer_zerop (val))
+		return false;
+
+	      val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
+				 integer_one_node);
+	      /* Set the minimum size to zero since the string in
+		 the array could have zero length.  */
+	      *minlen = ssize_int (0);	
+	    }
+	  else if (TREE_CODE (arg) == COMPONENT_REF
 	      && TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1))) == ARRAY_TYPE)
 	    {
 	      /* Use the type of the member array to determine the upper
 		 bound on the length of the array.  This may be overly
 		 optimistic if the array itself isn't NUL-terminated and
 		 the caller relies on the subsequent member to contain
-		 the NUL.
+		 the NUL but that would only be considered valid if
+		 the array were the last member of a struct.
 		 Set *FLEXP to true if the array whose bound is being
 		 used is at the end of a struct.  */
 	      if (array_at_struct_end_p (arg))
 		*flexp = true;
 
 	      arg = TREE_OPERAND (arg, 1);
-	      val = TYPE_SIZE_UNIT (TREE_TYPE (arg));
+
+	      tree type = TREE_TYPE (arg);
+
+	      while (TREE_CODE (type) == ARRAY_TYPE
+		     && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
+		type = TREE_TYPE (type);
+
+	      /* Fail when the array bound is unknown or zero.  */
+	      val = TYPE_SIZE_UNIT (type);
 	      if (!val || integer_zerop (val))
 		return false;
 	      val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
@@ -1361,17 +1404,25 @@  get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
 	      *minlen = ssize_int (0);
 	    }
 
-	  if (VAR_P (arg) 
-	      && TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE)
+	  if (VAR_P (arg))
 	    {
-	      val = TYPE_SIZE_UNIT (TREE_TYPE (arg));
-	      if (!val || TREE_CODE (val) != INTEGER_CST || integer_zerop (val))
-		return false;
-	      val = wide_int_to_tree (TREE_TYPE (val), 
-				      wi::sub(wi::to_wide (val), 1));
-	      /* Set the minimum size to zero since the string in
-		 the array could have zero length.  */
-	      *minlen = ssize_int (0);
+	      tree type = TREE_TYPE (arg);
+	      if (POINTER_TYPE_P (type))
+		type = TREE_TYPE (type);
+
+	      if (TREE_CODE (type) == ARRAY_TYPE)
+		{
+		  val = TYPE_SIZE_UNIT (type);
+		  if (!val
+		      || TREE_CODE (val) != INTEGER_CST
+		      || integer_zerop (val))
+		    return false;
+		  val = wide_int_to_tree (TREE_TYPE (val),
+					  wi::sub(wi::to_wide (val), 1));
+		  /* Set the minimum size to zero since the string in
+		     the array could have zero length.  */
+		  *minlen = ssize_int (0);
+		}
 	    }
 	}
 
@@ -3462,12 +3513,44 @@  static bool
 gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
 {
   gimple *stmt = gsi_stmt (*gsi);
-  tree len = get_maxval_strlen (gimple_call_arg (stmt, 0), 0);
-  if (!len)
-    return false;
-  len = force_gimple_operand_gsi (gsi, len, true, NULL, true, GSI_SAME_STMT);
-  replace_call_with_value (gsi, len);
-  return true;
+
+  wide_int minlen;
+  wide_int maxlen;
+
+  tree lenrange[2];
+  if (!get_range_strlen (gimple_call_arg (stmt, 0), lenrange)
+      && lenrange[0] && TREE_CODE (lenrange[0]) == INTEGER_CST
+      && lenrange[1] && TREE_CODE (lenrange[1]) == 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]);
+    }
+  else
+    {
+      unsigned prec = TYPE_PRECISION (sizetype);
+
+      minlen = wi::shwi (0, prec);
+      maxlen = wi::to_wide (max_object_size (), prec) - 2;
+    }
+
+  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]);
+      return true;
+    }
+
+  tree lhs = gimple_call_lhs (stmt);
+  if (lhs && TREE_CODE (lhs) == SSA_NAME)
+    set_range_info (lhs, VR_RANGE, minlen, maxlen);
+
+  return false;
 }
 
 /* Fold a call to __builtin_acc_on_device.  */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-40.c b/gcc/testsuite/gcc.dg/strlenopt-40.c
new file mode 100644
index 0000000..f4577d6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-40.c
@@ -0,0 +1,393 @@ 
+/* PR tree-optimization/83671 - fix for false positive reported by
+   -Wstringop-overflow does not work with inlining
+   { dg-do compile }
+   { dg-options "-O1 -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+#define DIFF_MAX __PTRDIFF_MAX__
+
+#define CAT(x, y) x ## y
+#define CONCAT(x, y) CAT (x, y)
+#define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do {				\
+    extern void FAILNAME (name) (void);		\
+    FAILNAME (name)();				\
+  } while (0)
+
+/* Macros to emit a call to funcation named
+     call_in_{true,false}_branch_not_eliminated_on_line_NNN()
+   for each call that's expected to be eliminated.  The dg-final
+   scan-tree-dump-time directive at the bottom of the test verifies
+   that no such call appears in output.  */
+#define ELIM_TRUE(expr) \
+  if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
+
+#define ELIM_FALSE(expr)					\
+  if (!!(expr)) FAIL (in_false_branch_not_eliminated); else (void)0
+
+/* Macro to emit a call to a function named
+     call_made_in_{true,false}_branch_on_line_NNN()
+   for each call that's expected to be retained.  The dg-final
+   scan-tree-dump-time directive at the bottom of the test verifies
+   that the expected number of both kinds of calls appears in output
+   (a pair for each line with the invocation of the KEEP() macro.  */
+#define KEEP(expr)				\
+  if (expr)					\
+    FAIL (made_in_true_branch);			\
+  else						\
+    FAIL (made_in_false_branch)
+
+typedef char A3[3], A5[5], A7[7], AX[];
+
+typedef A3 A7_3[7];
+typedef A3 AX_3[];
+typedef A5 A7_5[7];
+typedef A7 A5_7[5];
+
+extern A7_3 a7_3;
+extern A5_7 a5_7;
+extern AX_3 ax_3;
+
+extern A3 a3;
+extern A7 a5;
+extern A7 a7;
+extern AX ax;
+
+extern A3 *pa3;
+extern A5 *pa5;
+extern A7 *pa7;
+
+extern A7_3 *pa7_3;
+extern AX_3 *pax_3;
+extern A5_7 *pa5_7;
+extern A7_5 *pa7_5;
+
+extern char *ptr;
+
+struct MemArrays0 {
+  A7_3 a7_3;
+  A5_7 a5_7;
+  char a3[3], a5[5], a0[0];
+};
+struct MemArraysX { char a3[3], a5[5], ax[]; };
+struct MemArrays7 { char a3[3], a5[5], a7[7]; };
+
+struct MemArrays0 ma0_3_5_7[3][5][7];
+
+void elim_strings (int i)
+{
+  ELIM_TRUE (strlen (i < 0 ? "123" : "321") == 3);
+  ELIM_FALSE (strlen (i < 0 ? "123" : "321") > 3);
+  ELIM_FALSE (strlen (i < 0 ? "123" : "321") < 3);
+
+  ELIM_TRUE (strlen (i < 0 ? "123" : "4321") >= 3);
+  ELIM_FALSE (strlen (i < 0 ? "123" : "4321") > 4);
+  ELIM_FALSE (strlen (i < 0 ? "123" : "4321") < 3);
+
+  ELIM_TRUE (strlen (i < 0 ? "1234" : "321") >= 3);
+  ELIM_FALSE (strlen (i < 0 ? "1234" : "321") < 3);
+  ELIM_FALSE (strlen (i < 0 ? "1234" : "321") > 4);
+
+  ELIM_TRUE (strlen (i < 0 ? "123" : "4321") <= 4);
+  ELIM_TRUE (strlen (i < 0 ? "1234" : "321") <= 4);
+
+  ELIM_TRUE (strlen (i < 0 ? "1" : "123456789") <= 9);
+  ELIM_TRUE (strlen (i < 0 ? "1" : "123456789") >= 1);
+}
+
+/* Verify that strlen calls involving uninitialized global arrays
+   of known size are eliminated when they appear in expressions
+   that test for results that must be true.  */
+void elim_global_arrays (int i)
+{
+  /* Verify that the expression involving the strlen call as well
+     as whatever depends on it is eliminated  from the test output.
+     All these expressions must be trivially true.  */
+  ELIM_TRUE (strlen (a7_3[0]) < sizeof a7_3[0]);
+  ELIM_TRUE (strlen (a7_3[1]) < sizeof a7_3[1]);
+  ELIM_TRUE (strlen (a7_3[6]) < sizeof a7_3[6]);
+  ELIM_TRUE (strlen (a7_3[i]) < sizeof a7_3[i]);
+
+  ELIM_TRUE (strlen (a5_7[0]) < sizeof a5_7[0]);
+  ELIM_TRUE (strlen (a5_7[1]) < sizeof a5_7[1]);
+  ELIM_TRUE (strlen (a5_7[4]) < sizeof a5_7[4]);
+  ELIM_TRUE (strlen (a5_7[i]) < sizeof a5_7[0]);
+
+  ELIM_TRUE (strlen (ax_3[0]) < sizeof ax_3[0]);
+  ELIM_TRUE (strlen (ax_3[1]) < sizeof ax_3[1]);
+  ELIM_TRUE (strlen (ax_3[9]) < sizeof ax_3[9]);
+  ELIM_TRUE (strlen (ax_3[i]) < sizeof ax_3[i]);
+
+  ELIM_TRUE (strlen (a3) < sizeof a3);
+  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);
+}
+
+void elim_pointer_to_arrays (void)
+{
+  ELIM_TRUE (strlen (*pa7) < 7);
+  ELIM_TRUE (strlen (*pa5) < 5);
+  ELIM_TRUE (strlen (*pa3) < 3);
+
+  ELIM_TRUE (strlen ((*pa7_3)[0]) < 3);
+  ELIM_TRUE (strlen ((*pa7_3)[1]) < 3);
+  ELIM_TRUE (strlen ((*pa7_3)[6]) < 3);
+
+  ELIM_TRUE (strlen ((*pax_3)[0]) < 3);
+  ELIM_TRUE (strlen ((*pax_3)[1]) < 3);
+  ELIM_TRUE (strlen ((*pax_3)[9]) < 3);
+
+  ELIM_TRUE (strlen ((*pa5_7)[0]) < 7);
+  ELIM_TRUE (strlen ((*pa5_7)[1]) < 7);
+  ELIM_TRUE (strlen ((*pa5_7)[4]) < 7);
+}
+
+void elim_global_arrays_and_strings (int i)
+{
+  ELIM_TRUE (strlen (i < 0 ? a3 : "") < 3);
+  ELIM_TRUE (strlen (i < 0 ? a3 : "1") < 3);
+  ELIM_TRUE (strlen (i < 0 ? a3 : "12") < 3);
+  ELIM_TRUE (strlen (i < 0 ? a3 : "123") < 4);
+
+  ELIM_FALSE (strlen (i < 0 ? a3 : "") > 3);
+  ELIM_FALSE (strlen (i < 0 ? a3 : "1") > 3);
+  ELIM_FALSE (strlen (i < 0 ? a3 : "12") > 3);
+  ELIM_FALSE (strlen (i < 0 ? a3 : "123") > 4);
+
+  ELIM_TRUE (strlen (i < 0 ? a7 : "") < 7);
+  ELIM_TRUE (strlen (i < 0 ? a7 : "1") < 7);
+  ELIM_TRUE (strlen (i < 0 ? a7 : "12") < 7);
+  ELIM_TRUE (strlen (i < 0 ? a7 : "123") < 7);
+  ELIM_TRUE (strlen (i < 0 ? a7 : "123456") < 7);
+  ELIM_TRUE (strlen (i < 0 ? a7 : "1234567") < 8);
+
+  ELIM_FALSE (strlen (i < 0 ? a7 : "") > 6);
+  ELIM_FALSE (strlen (i < 0 ? a7 : "1") > 6);
+  ELIM_FALSE (strlen (i < 0 ? a7 : "12") > 6);
+  ELIM_FALSE (strlen (i < 0 ? a7 : "123") > 6);
+  ELIM_FALSE (strlen (i < 0 ? a7 : "123456") > 7);
+  ELIM_FALSE (strlen (i < 0 ? a7 : "1234567") > 8);
+}
+
+void elim_member_arrays_obj (int i)
+{
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a3) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a3) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a3) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a3) < 3);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a3) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a3) < 3);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a3) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a3) < 3);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5) < 5);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a5) < 5);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a5) < 5);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a5) < 5);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a5) < 5);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a5) < 5);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a5) < 5);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5) < 5);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a7_3[0]) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a7_3[2]) < 3);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < 7);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < 7);
+}
+
+void elim_member_arrays_ptr (struct MemArrays0 *ma0,
+			     struct MemArraysX *max,
+			     struct MemArrays7 *ma7,
+			     int i)
+{
+  ELIM_TRUE (strlen (ma0->a7_3[0]) < 3);
+  ELIM_TRUE (strlen (ma0->a7_3[1]) < 3);
+  ELIM_TRUE (strlen (ma0->a7_3[6]) < 3);
+  ELIM_TRUE (strlen (ma0->a7_3[6]) < 3);
+  ELIM_TRUE (strlen (ma0->a7_3[i]) < 3);
+  ELIM_TRUE (strlen (ma0->a7_3[i]) < 3);
+
+  ELIM_TRUE (strlen (ma0->a5_7[0]) < 7);
+  ELIM_TRUE (strlen (ma0[0].a5_7[0]) < 7);
+  ELIM_TRUE (strlen (ma0[1].a5_7[0]) < 7);
+  ELIM_TRUE (strlen (ma0[1].a5_7[4]) < 7);
+  ELIM_TRUE (strlen (ma0[9].a5_7[0]) < 7);
+  ELIM_TRUE (strlen (ma0[9].a5_7[4]) < 7);
+
+  ELIM_TRUE (strlen (ma0->a3) < sizeof ma0->a3);
+  ELIM_TRUE (strlen (ma0->a5) < sizeof ma0->a5);
+  ELIM_TRUE (strlen (ma0->a0) < DIFF_MAX - 1);
+
+  ELIM_TRUE (strlen (max->a3) < sizeof max->a3);
+  ELIM_TRUE (strlen (max->a5) < sizeof max->a5);
+  ELIM_TRUE (strlen (max->ax) < DIFF_MAX - 1);
+
+  ELIM_TRUE (strlen (ma7->a3) < sizeof max->a3);
+  ELIM_TRUE (strlen (ma7->a5) < sizeof max->a5);
+  ELIM_TRUE (strlen (ma7->a7) < DIFF_MAX - 1);
+}
+
+
+#line 1000
+
+/* Verify that strlen calls involving uninitialized global arrays
+   of unknown size are not eliminated when they appear in expressions
+   that test for results that need not be true.  */
+void keep_global_arrays (int i)
+{
+  KEEP (strlen (a7_3[0]) < 2);
+  KEEP (strlen (a7_3[1]) < 2);
+  KEEP (strlen (a7_3[6]) < 2);
+  KEEP (strlen (a7_3[i]) < 2);
+
+  KEEP (strlen (a5_7[0]) < 6);
+  KEEP (strlen (a5_7[1]) < 6);
+  KEEP (strlen (a5_7[4]) < 6);
+  KEEP (strlen (a5_7[i]) < 6);
+
+  KEEP (strlen (ax_3[0]) < 2);
+  KEEP (strlen (ax_3[1]) < 2);
+  KEEP (strlen (ax_3[2]) < 2);
+  KEEP (strlen (ax_3[i]) < 2);
+
+  KEEP (strlen (a3) < 2);
+  KEEP (strlen (a7) < 6);
+
+  KEEP (strlen (a3 + i) < 2);
+  KEEP (strlen (a7 + i) < 2);
+
+  /* The length of an array of unknown size may be as large as
+     DIFF_MAX - 2.  */
+  KEEP (strlen (ax) != DIFF_MAX - 2);
+  KEEP (strlen (ax) < DIFF_MAX - 2);
+  KEEP (strlen (ax) < 999);
+  KEEP (strlen (ax) < 1);
+}
+
+void keep_pointer_to_arrays (void)
+{
+  KEEP (strlen (*pa7) < 6);
+  KEEP (strlen (*pa5) < 4);
+  KEEP (strlen (*pa3) < 2);
+
+  KEEP (strlen ((*pa7_3)[0]) < 2);
+  KEEP (strlen ((*pa7_3)[1]) < 2);
+  KEEP (strlen ((*pa7_3)[6]) < 2);
+
+  KEEP (strlen ((*pax_3)[0]) < 2);
+  KEEP (strlen ((*pax_3)[1]) < 2);
+  KEEP (strlen ((*pax_3)[9]) < 2);
+
+  KEEP (strlen ((*pa5_7)[0]) < 6);
+  KEEP (strlen ((*pa5_7)[1]) < 6);
+  KEEP (strlen ((*pa5_7)[4]) < 6);
+}
+
+void keep_global_arrays_and_strings (int i)
+{
+  KEEP (strlen (i < 0 ? a3 : "") < 2);
+  KEEP (strlen (i < 0 ? a3 : "1") < 2);
+  KEEP (strlen (i < 0 ? a3 : "12") < 2);
+  KEEP (strlen (i < 0 ? a3 : "123") < 3);
+
+  KEEP (strlen (i < 0 ? a7 : "") < 5);
+  KEEP (strlen (i < 0 ? a7 : "1") < 5);
+  KEEP (strlen (i < 0 ? a7 : "12") < 5);
+  KEEP (strlen (i < 0 ? a7 : "123") < 5);
+  KEEP (strlen (i < 0 ? a7 : "123456") < 6);
+  KEEP (strlen (i < 0 ? a7 : "1234567") < 6);
+}
+
+void keep_member_arrays_obj (int i)
+{
+  KEEP (strlen (ma0_3_5_7[0][0][0].a3) < 2);
+  KEEP (strlen (ma0_3_5_7[0][0][1].a3) < 2);
+  KEEP (strlen (ma0_3_5_7[0][0][2].a3) < 2);
+  KEEP (strlen (ma0_3_5_7[0][0][6].a3) < 2);
+
+  KEEP (strlen (ma0_3_5_7[1][0][0].a3) < 2);
+  KEEP (strlen (ma0_3_5_7[2][0][1].a3) < 2);
+
+  KEEP (strlen (ma0_3_5_7[1][1][0].a3) < 2);
+  KEEP (strlen (ma0_3_5_7[2][4][6].a3) < 2);
+
+  KEEP (strlen (ma0_3_5_7[0][0][0].a5) < 4);
+  KEEP (strlen (ma0_3_5_7[0][0][1].a5) < 4);
+  KEEP (strlen (ma0_3_5_7[0][0][2].a5) < 4);
+  KEEP (strlen (ma0_3_5_7[0][0][6].a5) < 4);
+
+  KEEP (strlen (ma0_3_5_7[1][0][0].a5) < 4);
+  KEEP (strlen (ma0_3_5_7[2][0][1].a5) < 4);
+
+  KEEP (strlen (ma0_3_5_7[1][1][0].a5) < 4);
+  KEEP (strlen (ma0_3_5_7[2][4][6].a5) < 4);
+
+  KEEP (strlen (ma0_3_5_7[0][0][0].a7_3[0]) < 2);
+  KEEP (strlen (ma0_3_5_7[2][4][6].a7_3[2]) < 2);
+
+  KEEP (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < 6);
+  KEEP (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < 6);
+}
+
+void keep_member_arrays_ptr (struct MemArrays0 *ma0,
+			     struct MemArraysX *max,
+			     struct MemArrays7 *ma7,
+			     int i)
+{
+  KEEP (strlen (ma0->a7_3[0]) > 0);
+  KEEP (strlen (ma0->a7_3[0]) < 2);
+  KEEP (strlen (ma0->a7_3[1]) < 2);
+  KEEP (strlen (ma0->a7_3[6]) < 2);
+  KEEP (strlen (ma0->a7_3[6]) < 2);
+  KEEP (strlen (ma0->a7_3[i]) > 0);
+  KEEP (strlen (ma0->a7_3[i]) < 2);
+  KEEP (strlen (ma0->a7_3[i]) < 2);
+
+  KEEP (strlen (ma0->a5_7[0]) < 5);
+  KEEP (strlen (ma0[0].a5_7[0]) < 5);
+  KEEP (strlen (ma0[1].a5_7[0]) < 5);
+  KEEP (strlen (ma0[9].a5_7[0]) < 5);
+  KEEP (strlen (ma0[9].a5_7[4]) < 5);
+  KEEP (strlen (ma0[i].a5_7[4]) < 5);
+  KEEP (strlen (ma0[i].a5_7[i]) < 5);
+
+  KEEP (strlen (ma0->a0) < DIFF_MAX - 2);
+  KEEP (strlen (ma0->a0) < 999);
+  KEEP (strlen (ma0->a0) < 1);
+
+  KEEP (strlen (max->ax) < DIFF_MAX - 2);
+  KEEP (strlen (max->ax) < 999);
+  KEEP (strlen (max->ax) < 1);
+
+  KEEP (strlen (ma7->a7) < DIFF_MAX - 2);
+  KEEP (strlen (ma7->a7) < 999);
+  KEEP (strlen (ma7->a7) < 1);
+}
+
+void keep_pointers (const char *s)
+{
+  KEEP (strlen (ptr) < DIFF_MAX - 2);
+  KEEP (strlen (ptr) < 999);
+  KEEP (strlen (ptr) < 1);
+
+  KEEP (strlen (s) < DIFF_MAX - 2);
+  KEEP (strlen (s) < 999);
+  KEEP (strlen (s) < 1);
+}
+
+
+/* { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated_" 0 "optimized" } }
+   { dg-final { scan-tree-dump-times "call_in_false_branch_not_eliminated_" 0 "optimized" } }
+
+   { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 92 "optimized" } }
+   { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 92 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-41.c b/gcc/testsuite/gcc.dg/strlenopt-41.c
new file mode 100644
index 0000000..c5e8eb6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-41.c
@@ -0,0 +1,34 @@ 
+/* PR tree-optimization/83671 - fix for false positive reported by
+   -Wstringop-overflow does not work with inlining
+   Verify that the length the empty string is folded to zero even at -O1
+   regardless of offset into it.
+   Also verify that the length of a non-empty string isn't folded given
+   a variable offset.
+   { dg-do compile }
+   { dg-options "-O1 -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+inline unsigned length (const char *s)
+{
+  return __builtin_strlen (s);
+}
+
+void check_length_cst (int i)
+{
+  unsigned len = length (&""[i]);
+
+  if (len)
+    __builtin_abort ();
+}
+
+void check_length_var (int i)
+{
+  unsigned len = length (&"1"[i]);
+
+  if (len != 1)
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-tree-dump-times "abort" 1 "optimized" } }
+   { dg-final { scan-tree-dump-times "strlen" 1 "optimized" } } */