aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2018-01-10 21:40:14 +0000
committerMartin Sebor <msebor@gcc.gnu.org>2018-01-10 14:40:14 -0700
commitc42d0aa0893cab444366c80fdd5b23bb45de6276 (patch)
treefafafff32b2c7dda509c5af6251cf14765cc834f /gcc
parente7c6abad7f0bcbf0e60d27bc8cff1087c5195f76 (diff)
downloadgcc-c42d0aa0893cab444366c80fdd5b23bb45de6276.zip
gcc-c42d0aa0893cab444366c80fdd5b23bb45de6276.tar.gz
gcc-c42d0aa0893cab444366c80fdd5b23bb45de6276.tar.bz2
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. From-SVN: r256457
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog12
-rw-r--r--gcc/builtins.c6
-rw-r--r--gcc/gimple-fold.c140
-rw-r--r--gcc/testsuite/ChangeLog6
-rw-r--r--gcc/testsuite/g++.dg/warn/string1.C2
-rw-r--r--gcc/testsuite/gcc.dg/strlenopt-40.c393
-rw-r--r--gcc/testsuite/gcc.dg/strlenopt-41.c34
7 files changed, 564 insertions, 29 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index ba1a844..081c04a 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,15 @@
+2018-01-10 Martin Sebor <msebor@redhat.com>
+
+ 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.
+
2018-01-10 Aldy Hernandez <aldyh@redhat.com>
PR middle-end/81897
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 1d6e69d..a0d0a10 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..504a85d 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,33 @@ 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)
+ {
+ /* Fail if an array is the last member of a struct object
+ since it could be treated as a (fake) flexible array
+ member. */
+ 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 +1356,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 +1407,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 +3516,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/ChangeLog b/gcc/testsuite/ChangeLog
index 825224d..10273a9 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2018-01-10 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/83671
+ * gcc.dg/strlenopt-40.c: New test.
+ * gcc.dg/strlenopt-41.c: New test.
+
2018-01-10 Steven G. Kargl <kargl@gcc.gnu.org>
PR fortran/83093
diff --git a/gcc/testsuite/g++.dg/warn/string1.C b/gcc/testsuite/g++.dg/warn/string1.C
index 8f24a78..a0b0d47 100644
--- a/gcc/testsuite/g++.dg/warn/string1.C
+++ b/gcc/testsuite/g++.dg/warn/string1.C
@@ -1,5 +1,5 @@
// PR c++/35652
-// { dg-options "-O" }
+// { dg-options "-O -Wall" }
#include <string>
int test() {
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" } } */