This is the last of the major pieces of Martin's work.
set_strlen_range is extracted out of maybe_set_strlen_range and used by
the strlen folder.

The remnants of maybe_set_strlen_range is made more conservative,
particularly for cases where we might cross a subobject boundary.

There's the usual testsuite tweaks as well as a few new tests.

Bootstrapped and regression tested on x86_64.  My testers will be
spinning on this later today.

There's one or two very minor cleanup patches still to wrap up and some
verification steps, but in terms of notable behavior changes I think
we're done.

Installing on the trunk,
jeff
commit 0ec4b03364a1893baa78b1ebe4006bfe6a03c134
Author: Jeff Law <l...@torsion.usersys.redhat.com>
Date:   Fri Dec 28 00:09:35 2018 -0500

            * gimple-fold.c (gimple_fold_builtin_strlen): Use set_strlen_range
            rather than set_range_info.
            * tree-ssa-strlen.c (set_strlen_range): Extracted from
            maybe_set_strlen_range.  Handle potentially boundary crossing
            cases more conservatively.
            (maybe_set_strlen_range): Parts refactored into set_strlen_range.
            Call set_strlen_range.
            * tree-ssa-strlen.h (set_strlen_range): Add prototype.
    
            * gcc.dg/strlenopt-36.c: Update.
            * gcc.dg/strlenopt-45.c: Update.
            * gcc.c-torture/execute/strlen-5.c: New test.
            * gcc.c-torture/execute/strlen-6.c: New test.
            * gcc.c-torture/execute/strlen-7.c: New test.

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index b344f6be61e..b4e2f25a2cc 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,6 +1,16 @@
 2019-01-02  Martin Sebor  <mse...@redhat.com>
             Jeff Law  <l...@redhat.com>
 
+
+       * gimple-fold.c (gimple_fold_builtin_strlen): Use set_strlen_range
+       rather than set_range_info.
+       * tree-ssa-strlen.c (set_strlen_range): Extracted from
+       maybe_set_strlen_range.  Handle potentially boundary crossing
+       cases more conservatively.
+       (maybe_set_strlen_range): Parts refactored into set_strlen_range.
+       Call set_strlen_range.
+       * tree-ssa-strlen.h (set_strlen_range): Add prototype.
+       
        PR middle-end/88663
        * gimple-fold.c (get_range_strlen): Update prototype to no longer
        need the flexp argument.
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 688daf92154..0bb4db5e160 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -3739,10 +3739,9 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
       return true;
     }
 
+  /* Set the strlen() range to [0, MAXLEN].  */
   if (tree lhs = gimple_call_lhs (stmt))
-    if (TREE_CODE (lhs) == SSA_NAME
-       && INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
-      set_range_info (lhs, VR_RANGE, minlen, maxlen);
+    set_strlen_range (lhs, maxlen);
 
   return false;
 }
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 40703c62017..1d5a8769fae 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,12 @@
+2019-01-02  Martin Sebor  <mse...@redhat.com>
+            Jeff Law  <l...@redhat.com>
+
+       * gcc.dg/strlenopt-36.c: Update.
+       * gcc.dg/strlenopt-45.c: Update.
+       * gcc.c-torture/execute/strlen-5.c: New test.
+       * gcc.c-torture/execute/strlen-6.c: New test.
+       * gcc.c-torture/execute/strlen-7.c: New test.
+
 2019-01-02  Jakub Jelinek  <ja...@redhat.com>
 
        PR testsuite/87304
diff --git a/gcc/testsuite/gcc.c-torture/execute/strlen-5.c 
b/gcc/testsuite/gcc.c-torture/execute/strlen-5.c
new file mode 100644
index 00000000000..9af57d5ee39
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/strlen-5.c
@@ -0,0 +1,653 @@
+/* Test to verify that even strictly undefined strlen() calls with
+   unterminated character arrays yield the "expected" results when
+   the terminating nul is present in a subsequent suobobject.  */
+
+extern __SIZE_TYPE__ strlen (const char *);
+
+unsigned nfails;
+
+#define A(expr, N)                                             \
+  do {                                                         \
+    const char *s = (expr);                                    \
+    unsigned n = strlen (s);                                   \
+    ((n == N)                                                  \
+     ? 0                                                       \
+     : (__builtin_printf ("line %i: strlen (%s = \"%s\")"      \
+                         " == %u failed\n",                    \
+                         __LINE__, #expr, s, N),               \
+       ++nfails));                                             \
+  } while (0)
+
+
+int idx;
+
+
+const char ca[][4] = {
+  { '1', '2', '3', '4' }, { '5' },
+  { '1', '2', '3', '4' }, { '5', '6' },
+  { '1', '2', '3', '4' }, { '5', '6', '7' },
+  { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+  { '9' }
+};
+
+static void test_const_global_arrays (void)
+{
+  A (ca[0], 5);
+  A (&ca[0][0], 5);
+  A (&ca[0][1], 4);
+  A (&ca[0][3], 2);
+
+  int i = 0;
+  A (ca[i], 5);
+  A (&ca[i][0], 5);
+  A (&ca[i][1], 4);
+  A (&ca[i][3], 2);
+
+  int j = i;
+  A (&ca[i][i], 5);
+  A (&ca[i][j + 1], 4);
+  A (&ca[i][j + 2], 3);
+
+  A (&ca[idx][i], 5);
+  A (&ca[idx][j + 1], 4);
+  A (&ca[idx][j + 2], 3);
+
+  A (&ca[idx][idx], 5);
+  A (&ca[idx][idx + 1], 4);
+  A (&ca[idx][idx + 2], 3);
+
+  A (&ca[0][++j], 4);
+  A (&ca[0][++j], 3);
+  A (&ca[0][++j], 2);
+
+  if (j != 3)
+    ++nfails;
+}
+
+
+static void test_const_local_arrays (void)
+{
+  const char a[][4] = {
+    { '1', '2', '3', '4' }, { '5' },
+    { '1', '2', '3', '4' }, { '5', '6' },
+    { '1', '2', '3', '4' }, { '5', '6', '7' },
+    { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+    { '9' }
+  };
+
+  A (a[0], 5);
+  A (&a[0][0], 5);
+  A (&a[0][1], 4);
+  A (&a[0][3], 2);
+
+  int i = 0;
+  A (a[i], 5);
+  A (&a[i][0], 5);
+  A (&a[i][1], 4);
+  A (&a[i][3], 2);
+
+  int j = i;
+  A (&a[i][i], 5);
+  A (&a[i][j + 1], 4);
+  A (&a[i][j + 2], 3);
+
+  A (&a[idx][i], 5);
+  A (&a[idx][j + 1], 4);
+  A (&a[idx][j + 2], 3);
+
+  A (&a[idx][idx], 5);
+  A (&a[idx][idx + 1], 4);
+  A (&a[idx][idx + 2], 3);
+
+  A (&a[0][++j], 4);
+  A (&a[0][++j], 3);
+  A (&a[0][++j], 2);
+
+  if (j != 3)
+    ++nfails;
+}
+
+
+char va[][4] = {
+  { '1', '2', '3', '4' }, { '5' },
+  { '1', '2', '3', '4' }, { '5', '6' },
+  { '1', '2', '3', '4' }, { '5', '6', '7' },
+  { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+  { '9' }
+};
+
+static void test_nonconst_global_arrays (void)
+{
+  {
+    A (va[0], 5);
+    A (&va[0][0], 5);
+    A (&va[0][1], 4);
+    A (&va[0][3], 2);
+
+    int i = 0;
+    A (va[i], 5);
+    A (&va[i][0], 5);
+    A (&va[i][1], 4);
+    A (&va[i][3], 2);
+
+    int j = i;
+    A (&va[i][i], 5);
+    A (&va[i][j + 1], 4);
+    A (&va[i][j + 2], 3);
+
+    A (&va[idx][i], 5);
+    A (&va[idx][j + 1], 4);
+    A (&va[idx][j + 2], 3);
+
+    A (&va[idx][idx], 5);
+    A (&va[idx][idx + 1], 4);
+    A (&va[idx][idx + 2], 3);
+  }
+
+  {
+    A (va[2], 6);
+    A (&va[2][0], 6);
+    A (&va[2][1], 5);
+    A (&va[2][3], 3);
+
+    int i = 2;
+    A (va[i], 6);
+    A (&va[i][0], 6);
+    A (&va[i][1], 5);
+    A (&va[i][3], 3);
+
+    int j = i - 1;
+    A (&va[i][j - 1], 6);
+    A (&va[i][j], 5);
+    A (&va[i][j + 1], 4);
+
+    A (&va[idx + 2][i - 1], 5);
+    A (&va[idx + 2][j], 5);
+    A (&va[idx + 2][j + 1], 4);
+  }
+
+  int j = 0;
+
+  A (&va[0][++j], 4);
+  A (&va[0][++j], 3);
+  A (&va[0][++j], 2);
+
+  if (j != 3)
+    ++nfails;
+}
+
+
+static void test_nonconst_local_arrays (void)
+{
+  char a[][4] = {
+    { '1', '2', '3', '4' }, { '5' },
+    { '1', '2', '3', '4' }, { '5', '6' },
+    { '1', '2', '3', '4' }, { '5', '6', '7' },
+    { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+    { '9' }
+  };
+
+  A (a[0], 5);
+  A (&a[0][0], 5);
+  A (&a[0][1], 4);
+  A (&a[0][3], 2);
+
+  int i = 0;
+  A (a[i], 5);
+  A (&a[i][0], 5);
+  A (&a[i][1], 4);
+  A (&a[i][3], 2);
+
+  int j = i;
+  A (&a[i][i], 5);
+  A (&a[i][j + 1], 4);
+  A (&a[i][j + 2], 3);
+
+  A (&a[idx][i], 5);
+  A (&a[idx][j + 1], 4);
+  A (&a[idx][j + 2], 3);
+
+  A (&a[idx][idx], 5);
+  A (&a[idx][idx + 1], 4);
+  A (&a[idx][idx + 2], 3);
+
+  A (&a[0][++j], 4);
+  A (&a[0][++j], 3);
+  A (&a[0][++j], 2);
+
+  if (j != 3)
+    ++nfails;
+}
+
+
+struct MemArrays { char a[4], b[4]; };
+
+const struct MemArrays cma[] = {
+  { { '1', '2', '3', '4' }, { '5' } },
+  { { '1', '2', '3', '4' }, { '5', '6' } },
+  { { '1', '2', '3', '4' }, { '5', '6' } },
+  { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+  { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+  { { '9' }, { '\0' } }
+};
+
+static void test_const_global_member_arrays (void)
+{
+  {
+    A (cma[0].a, 5);
+    A (&cma[0].a[0], 5);
+    A (&cma[0].a[1], 4);
+    A (&cma[0].a[2], 3);
+
+    int i = 0;
+    A (cma[i].a, 5);
+    A (&cma[i].a[0], 5);
+    A (&cma[i].a[1], 4);
+    A (&cma[i].a[2], 3);
+
+    int j = i;
+    A (&cma[i].a[j], 5);
+    A (&cma[i].a[j + 1], 4);
+    A (&cma[i].a[j + 2], 3);
+
+    A (&cma[idx].a[i], 5);
+    A (&cma[idx].a[j + 1], 4);
+    A (&cma[idx].a[j + 2], 3);
+
+    A (&cma[idx].a[idx], 5);
+    A (&cma[idx].a[idx + 1], 4);
+    A (&cma[idx].a[idx + 2], 3);
+  }
+
+  {
+    A (cma[1].a, 6);
+    A (&cma[1].a[0], 6);
+    A (&cma[1].a[1], 5);
+    A (&cma[1].a[2], 4);
+
+    int i = 1;
+    A (cma[i].a, 6);
+    A (&cma[i].a[0], 6);
+    A (&cma[i].a[1], 5);
+    A (&cma[i].a[2], 4);
+
+    int j = i - 1;
+    A (&cma[i].a[j], 6);
+    A (&cma[i].a[j + 1], 5);
+    A (&cma[i].a[j + 2], 4);
+
+    A (&cma[idx + 1].a[j], 6);
+    A (&cma[idx + 1].a[j + 1], 5);
+    A (&cma[idx + 1].a[j + 2], 4);
+
+    A (&cma[idx + 1].a[idx], 6);
+    A (&cma[idx + 1].a[idx + 1], 5);
+    A (&cma[idx + 1].a[idx + 2], 4);
+  }
+
+  {
+    A (cma[4].a, 9);
+    A (&cma[4].a[0], 9);
+    A (&cma[4].a[1], 8);
+    A (&cma[4].b[0], 5);
+
+    int i = 4;
+    A (cma[i].a, 9);
+    A (&cma[i].a[0], 9);
+    A (&cma[i].a[1], 8);
+    A (&cma[i].b[0], 5);
+
+    int j = i - 1;
+    A (&cma[i].a[j], 6);
+    A (&cma[i].a[j + 1], 5);
+    A (&cma[i].b[j - 2], 4);
+
+    A (&cma[idx + 4].a[j], 6);
+    A (&cma[idx + 4].a[j + 1], 5);
+    A (&cma[idx + 4].b[j - 2], 4);
+
+    A (&cma[idx + 4].a[idx], 9);
+    A (&cma[idx + 4].a[idx + 1], 8);
+    A (&cma[idx + 4].b[idx + 1], 4);
+  }
+}
+
+
+static void test_const_local_member_arrays (void)
+{
+  const struct MemArrays ma[] = {
+    { { '1', '2', '3', '4' }, { '5' } },
+    { { '1', '2', '3', '4' }, { '5', '6' } },
+    { { '1', '2', '3', '4' }, { '5', '6' } },
+    { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+    { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+    { { '9' }, { '\0' } }
+  };
+
+  {
+    A (ma[0].a, 5);
+    A (&ma[0].a[0], 5);
+    A (&ma[0].a[1], 4);
+    A (&ma[0].a[2], 3);
+
+    int i = 0;
+    A (ma[i].a, 5);
+    A (&ma[i].a[0], 5);
+    A (&ma[i].a[1], 4);
+    A (&ma[i].a[2], 3);
+
+    int j = i;
+    A (&ma[i].a[j], 5);
+    A (&ma[i].a[j + 1], 4);
+    A (&ma[i].a[j + 2], 3);
+
+    A (&ma[idx].a[i], 5);
+    A (&ma[idx].a[j + 1], 4);
+    A (&ma[idx].a[j + 2], 3);
+
+    A (&ma[idx].a[idx], 5);
+    A (&ma[idx].a[idx + 1], 4);
+    A (&ma[idx].a[idx + 2], 3);
+  }
+
+  {
+    A (ma[1].a, 6);
+    A (&ma[1].a[0], 6);
+    A (&ma[1].a[1], 5);
+    A (&ma[1].a[2], 4);
+
+    int i = 1;
+    A (ma[i].a, 6);
+    A (&ma[i].a[0], 6);
+    A (&ma[i].a[1], 5);
+    A (&ma[i].a[2], 4);
+
+    int j = i - 1;
+    A (&ma[i].a[j], 6);
+    A (&ma[i].a[j + 1], 5);
+    A (&ma[i].a[j + 2], 4);
+
+    A (&ma[idx + 1].a[j], 6);
+    A (&ma[idx + 1].a[j + 1], 5);
+    A (&ma[idx + 1].a[j + 2], 4);
+
+    A (&ma[idx + 1].a[idx], 6);
+    A (&ma[idx + 1].a[idx + 1], 5);
+    A (&ma[idx + 1].a[idx + 2], 4);
+  }
+
+  {
+    A (ma[4].a, 9);
+    A (&ma[4].a[0], 9);
+    A (&ma[4].a[1], 8);
+    A (&ma[4].b[0], 5);
+
+    int i = 4;
+    A (ma[i].a, 9);
+    A (&ma[i].a[0], 9);
+    A (&ma[i].a[1], 8);
+    A (&ma[i].b[0], 5);
+
+    int j = i - 1;
+    A (&ma[i].a[j], 6);
+    A (&ma[i].a[j + 1], 5);
+    A (&ma[i].b[j - 2], 4);
+
+    A (&ma[idx + 4].a[j], 6);
+    A (&ma[idx + 4].a[j + 1], 5);
+    A (&ma[idx + 4].b[j - 2], 4);
+
+    A (&ma[idx + 4].a[idx], 9);
+    A (&ma[idx + 4].a[idx + 1], 8);
+    A (&ma[idx + 4].b[idx + 1], 4);
+  }
+}
+
+struct MemArrays vma[] = {
+  { { '1', '2', '3', '4' }, { '5' } },
+  { { '1', '2', '3', '4' }, { '5', '6' } },
+  { { '1', '2', '3', '4' }, { '5', '6' } },
+  { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+  { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+  { { '9' }, { '\0' } }
+};
+
+static void test_nonconst_global_member_arrays (void)
+{
+  {
+    A (vma[0].a, 5);
+    A (&vma[0].a[0], 5);
+    A (&vma[0].a[1], 4);
+    A (&vma[0].a[2], 3);
+
+    int i = 0;
+    A (vma[i].a, 5);
+    A (&vma[i].a[0], 5);
+    A (&vma[i].a[1], 4);
+    A (&vma[i].a[2], 3);
+
+    int j = i;
+    A (&vma[i].a[j], 5);
+    A (&vma[i].a[j + 1], 4);
+    A (&vma[i].a[j + 2], 3);
+
+    A (&vma[idx].a[i], 5);
+    A (&vma[idx].a[j + 1], 4);
+    A (&vma[idx].a[j + 2], 3);
+
+    A (&vma[idx].a[idx], 5);
+    A (&vma[idx].a[idx + 1], 4);
+    A (&vma[idx].a[idx + 2], 3);
+  }
+
+  {
+    A (vma[1].a, 6);
+    A (&vma[1].a[0], 6);
+    A (&vma[1].a[1], 5);
+    A (&vma[1].a[2], 4);
+
+    int i = 1;
+    A (vma[i].a, 6);
+    A (&vma[i].a[0], 6);
+    A (&vma[i].a[1], 5);
+    A (&vma[i].a[2], 4);
+
+    int j = i - 1;
+    A (&vma[i].a[j], 6);
+    A (&vma[i].a[j + 1], 5);
+    A (&vma[i].a[j + 2], 4);
+
+    A (&vma[idx + 1].a[j], 6);
+    A (&vma[idx + 1].a[j + 1], 5);
+    A (&vma[idx + 1].a[j + 2], 4);
+
+    A (&vma[idx + 1].a[idx], 6);
+    A (&vma[idx + 1].a[idx + 1], 5);
+    A (&vma[idx + 1].a[idx + 2], 4);
+  }
+
+  {
+    A (vma[4].a, 9);
+    A (&vma[4].a[0], 9);
+    A (&vma[4].a[1], 8);
+    A (&vma[4].b[0], 5);
+
+    int i = 4;
+    A (vma[i].a, 9);
+    A (&vma[i].a[0], 9);
+    A (&vma[i].a[1], 8);
+    A (&vma[i].b[0], 5);
+
+    int j = i - 1;
+    A (&vma[i].a[j], 6);
+    A (&vma[i].a[j + 1], 5);
+    A (&vma[i].b[j - 2], 4);
+
+    A (&vma[idx + 4].a[j], 6);
+    A (&vma[idx + 4].a[j + 1], 5);
+    A (&vma[idx + 4].b[j - 2], 4);
+
+    A (&vma[idx + 4].a[idx], 9);
+    A (&vma[idx + 4].a[idx + 1], 8);
+    A (&vma[idx + 4].b[idx + 1], 4);
+  }
+}
+
+
+static void test_nonconst_local_member_arrays (void)
+{
+  struct MemArrays ma[] = {
+    { { '1', '2', '3', '4' }, { '5' } },
+    { { '1', '2', '3', '4' }, { '5', '6' } },
+    { { '1', '2', '3', '4' }, { '5', '6' } },
+    { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+    { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+    { { '9' }, { '\0' } }
+  };
+
+  {
+    A (ma[0].a, 5);
+    A (&ma[0].a[0], 5);
+    A (&ma[0].a[1], 4);
+    A (&ma[0].a[2], 3);
+
+    int i = 0;
+    A (ma[i].a, 5);
+    A (&ma[i].a[0], 5);
+    A (&ma[i].a[1], 4);
+    A (&ma[i].a[2], 3);
+
+    int j = i;
+    A (&ma[i].a[j], 5);
+    A (&ma[i].a[j + 1], 4);
+    A (&ma[i].a[j + 2], 3);
+
+    A (&ma[idx].a[i], 5);
+    A (&ma[idx].a[j + 1], 4);
+    A (&ma[idx].a[j + 2], 3);
+
+    A (&ma[idx].a[idx], 5);
+    A (&ma[idx].a[idx + 1], 4);
+    A (&ma[idx].a[idx + 2], 3);
+  }
+
+  {
+    A (ma[1].a, 6);
+    A (&ma[1].a[0], 6);
+    A (&ma[1].a[1], 5);
+    A (&ma[1].a[2], 4);
+
+    int i = 1;
+    A (ma[i].a, 6);
+    A (&ma[i].a[0], 6);
+    A (&ma[i].a[1], 5);
+    A (&ma[i].a[2], 4);
+
+    int j = i - 1;
+    A (&ma[i].a[j], 6);
+    A (&ma[i].a[j + 1], 5);
+    A (&ma[i].a[j + 2], 4);
+
+    A (&ma[idx + 1].a[j], 6);
+    A (&ma[idx + 1].a[j + 1], 5);
+    A (&ma[idx + 1].a[j + 2], 4);
+
+    A (&ma[idx + 1].a[idx], 6);
+    A (&ma[idx + 1].a[idx + 1], 5);
+    A (&ma[idx + 1].a[idx + 2], 4);
+  }
+
+  {
+    A (ma[4].a, 9);
+    A (&ma[4].a[0], 9);
+    A (&ma[4].a[1], 8);
+    A (&ma[4].b[0], 5);
+
+    int i = 4;
+    A (ma[i].a, 9);
+    A (&ma[i].a[0], 9);
+    A (&ma[i].a[1], 8);
+    A (&ma[i].b[0], 5);
+
+    int j = i - 1;
+    A (&ma[i].a[j], 6);
+    A (&ma[i].a[j + 1], 5);
+    A (&ma[i].b[j - 2], 4);
+
+    A (&ma[idx + 4].a[j], 6);
+    A (&ma[idx + 4].a[j + 1], 5);
+    A (&ma[idx + 4].b[j - 2], 4);
+
+    A (&ma[idx + 4].a[idx], 9);
+    A (&ma[idx + 4].a[idx + 1], 8);
+    A (&ma[idx + 4].b[idx + 1], 4);
+  }
+}
+
+
+union UnionMemberArrays
+{
+  struct { char a[4], b[4]; } a;
+  struct { char a[8]; } c;
+};
+
+const union UnionMemberArrays cu = {
+  { { '1', '2', '3', '4' }, { '5', } }
+};
+
+static void test_const_union_member_arrays (void)
+{
+  A (cu.a.a, 5);
+  A (cu.a.b, 1);
+  A (cu.c.a, 5);
+
+  const union UnionMemberArrays clu = {
+    { { '1', '2', '3', '4' }, { '5', '6' } }
+  };
+
+  A (clu.a.a, 6);
+  A (clu.a.b, 2);
+  A (clu.c.a, 6);
+}
+
+
+union UnionMemberArrays vu = {
+  { { '1', '2', '3', '4' }, { '5', '6' } }
+};
+
+static void test_nonconst_union_member_arrays (void)
+{
+  A (vu.a.a, 6);
+  A (vu.a.b, 2);
+  A (vu.c.a, 6);
+
+  union UnionMemberArrays lvu = {
+    { { '1', '2', '3', '4' }, { '5', '6', '7' } }
+  };
+
+  A (lvu.a.a, 7);
+  A (lvu.a.b, 3);
+  A (lvu.c.a, 7);
+}
+
+
+int main (void)
+{
+  test_const_global_arrays ();
+  test_const_local_arrays ();
+
+  test_nonconst_global_arrays ();
+  test_nonconst_local_arrays ();
+
+  test_const_global_member_arrays ();
+  test_const_local_member_arrays ();
+
+  test_nonconst_global_member_arrays ();
+  test_nonconst_local_member_arrays ();
+
+  test_const_union_member_arrays ();
+  test_nonconst_union_member_arrays ();
+
+  if (nfails)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/strlen-6.c 
b/gcc/testsuite/gcc.c-torture/execute/strlen-6.c
new file mode 100644
index 00000000000..1df5b21f616
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/strlen-6.c
@@ -0,0 +1,113 @@
+/* Test to verify that strlen() calls with conditional expressions
+   and unterminated arrays or pointers to such things as arguments
+   are evaluated without making assumptions about array sizes.  */
+
+extern __SIZE_TYPE__ strlen (const char *);
+
+unsigned nfails;
+
+#define A(expr, N)                                             \
+  do {                                                         \
+    const char *_s = (expr);                                   \
+    unsigned _n = strlen (_s);                                 \
+    ((_n == N)                                                 \
+     ? 0                                                       \
+     : (__builtin_printf ("line %i: strlen ((%s) = (\"%s\"))"  \
+                         " == %u failed\n",                    \
+                         __LINE__, #expr, _s, N),              \
+       ++nfails));                                             \
+  } while (0)
+
+
+volatile int i0 = 0;
+
+const char ca[2][3] = { "12" };
+const char cb[2][3] = { { '1', '2', '3', }, { '4' } };
+
+char va[2][3] = { "123" };
+char vb[2][3] = { { '1', '2', '3', }, { '4', '5' } };
+
+const char *s = "123456";
+
+
+static void test_binary_cond_expr_global (void)
+{
+  A (i0 ? "1" : ca[0], 2);
+  A (i0 ? ca[0] : "123", 3);
+
+  /* The call to strlen (cb[0]) is strictly undefined because the array
+     isn't nul-terminated.  This test verifies that the strlen range
+     optimization doesn't assume that the argument is necessarily nul
+     terminated.
+     Ditto for strlen (vb[0]).  */
+  A (i0 ? "1" : cb[0], 4);              /* GCC 8.2 failure */
+  A (i0 ? cb[0] : "12", 2);
+
+  A (i0 ? "1" : va[0], 3);              /* GCC 8.2 failure */
+  A (i0 ? va[0] : "1234", 4);
+
+  A (i0 ? "1" : vb[0], 5);              /* GCC 8.2 failure */
+  A (i0 ? vb[0] : "12", 2);
+}
+
+
+static void test_binary_cond_expr_local (void)
+{
+  const char lca[2][3] = { "12" };
+  const char lcb[2][3] = { { '1', '2', '3', }, { '4' } };
+
+  char lva[2][3] = { "123" };
+  char lvb[2][3] = { { '1', '2', '3', }, { '4', '5' } };
+
+  /* Also undefined as above.  */
+  A (i0 ? "1" : lca[0], 2);
+  A (i0 ? lca[0] : "123", 3);
+
+  A (i0 ? "1" : lcb[0], 4);             /* GCC 8.2 failure */
+  A (i0 ? lcb[0] : "12", 2);
+
+  A (i0 ? "1" : lva[0], 3);             /* GCC 8.2 failure */
+  A (i0 ? lva[0] : "1234", 4);
+
+  A (i0 ? "1" : lvb[0], 5);             /* GCC 8.2 failure */
+  A (i0 ? lvb[0] : "12", 2);
+}
+
+
+static void test_ternary_cond_expr (void)
+{
+  /* Also undefined.  */
+  A (i0 == 0 ? s : i0 == 1 ? vb[0] : "123", 6);
+  A (i0 == 0 ? vb[0] : i0 == 1 ? s : "123", 5);
+  A (i0 == 0 ? "123" : i0 == 1 ? s : vb[0], 3);
+}
+
+
+const char (*pca)[3] = &ca[0];
+const char (*pcb)[3] = &cb[0];
+
+char (*pva)[3] = &va[0];
+char (*pvb)[3] = &vb[0];
+
+static void test_binary_cond_expr_arrayptr (void)
+{
+  /* Also undefined.  */
+  A (i0 ? *pca : *pcb, 4);              /* GCC 8.2 failure */
+  A (i0 ? *pcb : *pca, 2);
+
+  A (i0 ? *pva : *pvb, 5);              /* GCC 8.2 failure */
+  A (i0 ? *pvb : *pva, 3);
+}
+
+
+int main (void)
+{
+  test_binary_cond_expr_global ();
+  test_binary_cond_expr_local ();
+
+  test_ternary_cond_expr ();
+  test_binary_cond_expr_arrayptr ();
+
+  if (nfails)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/strlen-7.c 
b/gcc/testsuite/gcc.c-torture/execute/strlen-7.c
new file mode 100644
index 00000000000..884db6547ad
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/strlen-7.c
@@ -0,0 +1,37 @@
+/* Test to verify that a strlen() call with a pointer to a dynamic type
+   doesn't make assumptions based on the static type of the original
+   pointer.  See g++.dg/init/strlen.C for the corresponding C++ test.  */
+
+struct A { int i; char a[1]; void (*p)(); };
+struct B { char a[sizeof (struct A) - __builtin_offsetof (struct A, a)]; };
+
+__attribute__ ((noipa)) void
+init (char *d, const char *s)
+{
+  __builtin_strcpy (d, s);
+}
+
+struct B b;
+
+__attribute__ ((noipa)) void
+test_dynamic_type (struct A *p)
+{
+  /* The following call is undefined because it writes past the end
+     of the p->a subobject, but the corresponding GIMPLE considers
+     it valid and there's apparently no way to distinguish invalid
+     cases from ones like it that might be valid.  If/when GIMPLE
+     changes to make this possible this test can be removed.  */
+  char *q = (char*)__builtin_memcpy (p->a, &b, sizeof b);
+
+  init (q, "foobar");
+
+  if (6 != __builtin_strlen (q))
+    __builtin_abort();
+}
+
+int main (void)
+{
+  struct A *p = (struct A*)__builtin_malloc (sizeof *p);
+  test_dynamic_type (p);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/strlenopt-36.c 
b/gcc/testsuite/gcc.dg/strlenopt-36.c
index d6fcca26b97..56e59a431a4 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-36.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-36.c
@@ -9,23 +9,6 @@ extern char a7[7], a6[6], a5[5], a4[4], a3[3], a2[2], a1[1];
 extern char a0[0];   /* Intentionally not tested here.  */
 extern char ax[];    /* Same.  */
 
-struct MemArrays {
-  char a7[7], a6[6], a5[5], a4[4], a3[3], a2[2], a1[1];
-  char a0[0];   /* Not tested here.  */
-};
-
-struct NestedMemArrays {
-  struct { char a7[7]; } ma7;
-  struct { char a6[6]; } ma6;
-  struct { char a5[5]; } ma5;
-  struct { char a4[4]; } ma4;
-  struct { char a3[3]; } ma3;
-  struct { char a2[2]; } ma2;
-  struct { char a1[1]; } ma1;
-  struct { char a0[0]; } ma0;
-  char last;
-};
-
 extern void failure_on_line (int);
 
 #define TEST_FAIL(line)                                        \
@@ -51,36 +34,4 @@ void test_array (void)
     T (strlen (a1) == 0);  */
 }
 
-void test_memarray (struct MemArrays *ma)
-{
-  T (strlen (ma->a7) < sizeof ma->a7);
-  T (strlen (ma->a6) < sizeof ma->a6);
-  T (strlen (ma->a5) < sizeof ma->a5);
-  T (strlen (ma->a4) < sizeof ma->a4);
-  T (strlen (ma->a3) < sizeof ma->a3);
-
-  /* The following two calls are folded too early which defeats
-     the strlen() optimization.
-  T (strlen (ma->a2) == 1);
-  T (strlen (ma->a1) == 0);  */
-}
-
-/* Verify that the range of strlen(A) of a last struct member is
-   set even when the array is the sole member of a struct as long
-   as the struct itself is a member of another struct.  The converse
-   is tested in stlenopt-37.c.  */
-void test_nested_memarray (struct NestedMemArrays *ma)
-{
-  T (strlen (ma->ma7.a7) < sizeof ma->ma7.a7);
-  T (strlen (ma->ma6.a6) < sizeof ma->ma6.a6);
-  T (strlen (ma->ma5.a5) < sizeof ma->ma5.a5);
-  T (strlen (ma->ma4.a4) < sizeof ma->ma4.a4);
-  T (strlen (ma->ma3.a3) < sizeof ma->ma3.a3);
-
-  /* The following two calls are folded too early which defeats
-     the strlen() optimization.
-  T (strlen (ma->ma2.a2) == 1);
-  T (strlen (ma->ma1.a1) == 0);  */
-}
-
 /* { dg-final { scan-tree-dump-not "failure_on_line" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-45.c 
b/gcc/testsuite/gcc.dg/strlenopt-45.c
index bd9b197a75d..31c1e538f6f 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-45.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-45.c
@@ -2,7 +2,7 @@
    Test to verify that strnlen built-in expansion works correctly
    in the absence of tree strlen optimization.
    { dg-do compile }
-   { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+   { dg-options "-O2 -Wall -Wno-stringop-overflow -fdump-tree-optimized" } */
 
 #include "strlenopt.h"
 
@@ -85,19 +85,19 @@ void elim_strnlen_arr_cst (void)
   ELIM (strnlen (a3_7[0], 1) < 2);
   ELIM (strnlen (a3_7[0], 2) < 3);
   ELIM (strnlen (a3_7[0], 3) < 4);
-  ELIM (strnlen (a3_7[0], 9) < 8);
-  ELIM (strnlen (a3_7[0], PTRDIFF_MAX) < 8);
-  ELIM (strnlen (a3_7[0], SIZE_MAX) < 8);
-  ELIM (strnlen (a3_7[0], -1) < 8);
+  ELIM (strnlen (a3_7[0], 9) <= 9);
+  ELIM (strnlen (a3_7[0], PTRDIFF_MAX) <= sizeof a3_7);
+  ELIM (strnlen (a3_7[0], SIZE_MAX) <= sizeof a3_7);
+  ELIM (strnlen (a3_7[0], -1) <= sizeof a3_7);
 
   ELIM (strnlen (a3_7[2], 0) == 0);
   ELIM (strnlen (a3_7[2], 1) < 2);
   ELIM (strnlen (a3_7[2], 2) < 3);
   ELIM (strnlen (a3_7[2], 3) < 4);
-  ELIM (strnlen (a3_7[2], 9) < 8);
-  ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < 8);
-  ELIM (strnlen (a3_7[2], SIZE_MAX) < 8);
-  ELIM (strnlen (a3_7[2], -1) < 8);
+  ELIM (strnlen (a3_7[2], 9) <= 9);
+  ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < sizeof a3_7);
+  ELIM (strnlen (a3_7[2], SIZE_MAX) < sizeof a3_7);
+  ELIM (strnlen (a3_7[2], -1) < sizeof a3_7);
 
   ELIM (strnlen ((char*)a3_7, 0) == 0);
   ELIM (strnlen ((char*)a3_7, 1) < 2);
@@ -105,123 +105,19 @@ void elim_strnlen_arr_cst (void)
   ELIM (strnlen ((char*)a3_7, 3) < 4);
   ELIM (strnlen ((char*)a3_7, 9) < 10);
   ELIM (strnlen ((char*)a3_7, 19) < 20);
-  ELIM (strnlen ((char*)a3_7, 21) < 22);
-  ELIM (strnlen ((char*)a3_7, 23) < 22);
-  ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) < 22);
-  ELIM (strnlen ((char*)a3_7, SIZE_MAX) < 22);
-  ELIM (strnlen ((char*)a3_7, -1) < 22);
+  ELIM (strnlen ((char*)a3_7, 21) <= sizeof a3_7);
+  ELIM (strnlen ((char*)a3_7, 23) <= sizeof a3_7);
+  ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) <= sizeof a3_7);
+  ELIM (strnlen ((char*)a3_7, SIZE_MAX) <= sizeof a3_7);
+  ELIM (strnlen ((char*)a3_7, -1) <= sizeof a3_7);
 
   ELIM (strnlen (ax, 0) == 0);
   ELIM (strnlen (ax, 1) < 2);
   ELIM (strnlen (ax, 2) < 3);
   ELIM (strnlen (ax, 9) < 10);
-  ELIM (strnlen (a3, PTRDIFF_MAX) <= PTRDIFF_MAX);
-  ELIM (strnlen (a3, SIZE_MAX) < PTRDIFF_MAX);
-  ELIM (strnlen (a3, -1) < PTRDIFF_MAX);
-}
-
-struct MemArrays
-{
-  char c;
-  char a0[0];
-  char a1[1];
-  char a3[3];
-  char a5[5];
-  char a3_7[3][7];
-  char ax[1];
-};
-
-void elim_strnlen_memarr_cst (struct MemArrays *p, int i)
-{
-  ELIM (strnlen (&p->c, 0) == 0);
-  ELIM (strnlen (&p->c, 1) < 2);
-  ELIM (strnlen (&p->c, 9) == 0);
-  ELIM (strnlen (&p->c, PTRDIFF_MAX) == 0);
-  ELIM (strnlen (&p->c, SIZE_MAX) == 0);
-  ELIM (strnlen (&p->c, -1) == 0);
-
-  /* Other accesses to internal zero-length arrays are undefined.  */
-  ELIM (strnlen (p->a0, 0) == 0);
-
-  ELIM (strnlen (p->a1, 0) == 0);
-  ELIM (strnlen (p->a1, 1) < 2);
-  ELIM (strnlen (p->a1, 9) == 0);
-  ELIM (strnlen (p->a1, PTRDIFF_MAX) == 0);
-  ELIM (strnlen (p->a1, SIZE_MAX) == 0);
-  ELIM (strnlen (p->a1, -1) == 0);
-
-  ELIM (strnlen (p->a3, 0) == 0);
-  ELIM (strnlen (p->a3, 1) < 2);
-  ELIM (strnlen (p->a3, 2) < 3);
-  ELIM (strnlen (p->a3, 3) < 4);
-  ELIM (strnlen (p->a3, 9) < 4);
-  ELIM (strnlen (p->a3, PTRDIFF_MAX) < 4);
-  ELIM (strnlen (p->a3, SIZE_MAX) < 4);
-  ELIM (strnlen (p->a3, -1) < 4);
-
-  ELIM (strnlen (p[i].a3, 0) == 0);
-  ELIM (strnlen (p[i].a3, 1) < 2);
-  ELIM (strnlen (p[i].a3, 2) < 3);
-  ELIM (strnlen (p[i].a3, 3) < 4);
-  ELIM (strnlen (p[i].a3, 9) < 4);
-  ELIM (strnlen (p[i].a3, PTRDIFF_MAX) < 4);
-  ELIM (strnlen (p[i].a3, SIZE_MAX) < 4);
-  ELIM (strnlen (p[i].a3, -1) < 4);
-
-  ELIM (strnlen (p->a3_7[0], 0) == 0);
-  ELIM (strnlen (p->a3_7[0], 1) < 2);
-  ELIM (strnlen (p->a3_7[0], 2) < 3);
-  ELIM (strnlen (p->a3_7[0], 3) < 4);
-  ELIM (strnlen (p->a3_7[0], 9) < 8);
-  ELIM (strnlen (p->a3_7[0], PTRDIFF_MAX) < 8);
-  ELIM (strnlen (p->a3_7[0], SIZE_MAX) < 8);
-  ELIM (strnlen (p->a3_7[0], -1) < 8);
-
-  ELIM (strnlen (p->a3_7[2], 0) == 0);
-  ELIM (strnlen (p->a3_7[2], 1) < 2);
-  ELIM (strnlen (p->a3_7[2], 2) < 3);
-  ELIM (strnlen (p->a3_7[2], 3) < 4);
-  ELIM (strnlen (p->a3_7[2], 9) < 8);
-  ELIM (strnlen (p->a3_7[2], PTRDIFF_MAX) < 8);
-  ELIM (strnlen (p->a3_7[2], SIZE_MAX) < 8);
-  ELIM (strnlen (p->a3_7[2], -1) < 8);
-
-  ELIM (strnlen (p->a3_7[i], 0) == 0);
-  ELIM (strnlen (p->a3_7[i], 1) < 2);
-  ELIM (strnlen (p->a3_7[i], 2) < 3);
-  ELIM (strnlen (p->a3_7[i], 3) < 4);
-
-#if 0
-  /* This is tranformed into strnlen ((char*)p + offsetof (a3_7[i]), N)
-     which makes it impssible to determine the size of the array.  */
-  ELIM (strnlen (p->a3_7[i], 9) < 8);
-  ELIM (strnlen (p->a3_7[i], PTRDIFF_MAX) < 8);
-  ELIM (strnlen (p->a3_7[i], SIZE_MAX) < 8);
-  ELIM (strnlen (p->a3_7[i], -1) < 8);
-#else
-  ELIM (strnlen (p->a3_7[i], 9) < 10);
-  ELIM (strnlen (p->a3_7[i], 19) < 20);
-#endif
-
-  ELIM (strnlen ((char*)p->a3_7, 0) == 0);
-  ELIM (strnlen ((char*)p->a3_7, 1) < 2);
-  ELIM (strnlen ((char*)p->a3_7, 2) < 3);
-  ELIM (strnlen ((char*)p->a3_7, 3) < 4);
-  ELIM (strnlen ((char*)p->a3_7, 9) < 10);
-  ELIM (strnlen ((char*)p->a3_7, 19) < 20);
-  ELIM (strnlen ((char*)p->a3_7, 21) < 22);
-  ELIM (strnlen ((char*)p->a3_7, 23) < 22);
-  ELIM (strnlen ((char*)p->a3_7, PTRDIFF_MAX) < 22);
-  ELIM (strnlen ((char*)p->a3_7, SIZE_MAX) < 22);
-  ELIM (strnlen ((char*)p->a3_7, -1) < 22);
-
-  ELIM (strnlen (p->ax, 0) == 0);
-  ELIM (strnlen (p->ax, 1) < 2);
-  ELIM (strnlen (p->ax, 2) < 3);
-  ELIM (strnlen (p->ax, 9) < 10);
-  ELIM (strnlen (p->a3, PTRDIFF_MAX) <= PTRDIFF_MAX);
-  ELIM (strnlen (p->a3, SIZE_MAX) < PTRDIFF_MAX);
-  ELIM (strnlen (p->a3, -1) < PTRDIFF_MAX);
+  ELIM (strnlen (ax, PTRDIFF_MAX) < PTRDIFF_MAX);
+  ELIM (strnlen (ax, SIZE_MAX) < PTRDIFF_MAX);
+  ELIM (strnlen (ax, -1) < PTRDIFF_MAX);
 }
 
 
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index f64bc9bea79..55fba88e0f4 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -1121,67 +1121,23 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool 
is_strcat)
   update_stmt (last.stmt);
 }
 
-/* For an LHS that is an SSA_NAME and for strlen() or strnlen() argument
-   SRC, set LHS range info to [0, min (N, BOUND)] if SRC refers to
-   a character array A[N] with unknown length bounded by N, and for
-   strnlen(), by min (N, BOUND).  */
-
-static tree
-maybe_set_strlen_range (tree lhs, tree src, tree bound)
+/* For an LHS that is an SSA_NAME that is the result of a strlen()
+   call, or when BOUND is non-null, of a strnlen() call, set LHS
+   range info to [0, min (MAX, BOUND)] when the range includes more
+   than one value and return LHS.  Otherwise, when the range
+   [MIN, MAX] is such that MIN == MAX, return the tree representation
+   of (MIN). The latter allows callers to fold suitable strnlen() calls
+   to constants.  */
+
+tree
+set_strlen_range (tree lhs, wide_int max, tree bound /* = NULL_TREE */)
 {
   if (TREE_CODE (lhs) != SSA_NAME
       || !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
     return NULL_TREE;
 
-  if (TREE_CODE (src) == SSA_NAME)
-    {
-      gimple *def = SSA_NAME_DEF_STMT (src);
-      if (is_gimple_assign (def)
-         && gimple_assign_rhs_code (def) == ADDR_EXPR)
-       src = gimple_assign_rhs1 (def);
-    }
-
-  wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
   wide_int min = wi::zero (max.get_precision ());
 
-  if (TREE_CODE (src) == ADDR_EXPR)
-    {
-      /* The last array member of a struct can be bigger than its size
-        suggests if it's treated as a poor-man's flexible array member.  */
-      src = TREE_OPERAND (src, 0);
-      bool src_is_array = TREE_CODE (TREE_TYPE (src)) == ARRAY_TYPE;
-      if (src_is_array
-         && TREE_CODE (src) != MEM_REF
-         && !array_at_struct_end_p (src))
-       {
-         tree type = TREE_TYPE (src);
-         if (tree size = TYPE_SIZE_UNIT (type))
-           if (size && TREE_CODE (size) == INTEGER_CST)
-             max = wi::to_wide (size);
-
-         /* For strlen() the upper bound above is equal to
-            the longest string that can be stored in the array
-            (i.e., it accounts for the terminating nul.  For
-            strnlen() bump up the maximum by one since the array
-            need not be nul-terminated.  */
-         if (!bound && max != 0)
-           --max;
-       }
-      else
-       {
-         if (TREE_CODE (src) == COMPONENT_REF && !src_is_array)
-           src = TREE_OPERAND (src, 1);
-         if (DECL_P (src))
-           {
-             /* Handle the unlikely case of strlen (&c) where c is some
-                variable.  */
-             if (tree size = DECL_SIZE_UNIT (src))
-               if (TREE_CODE (size) == INTEGER_CST)
-                 max = wi::to_wide (size);
-           }
-       }
-    }
-
   if (bound)
     {
       /* For strnlen, adjust MIN and MAX as necessary.  If the bound
@@ -1205,7 +1161,7 @@ maybe_set_strlen_range (tree lhs, tree src, tree bound)
            {
              /* For a bound in a known range, adjust the range determined
                 above as necessary.  For a bound in some anti-range or
-                in an unknown range, use the range determined above.  */
+                in an unknown range, use the range determined by callers.  */
              if (wi::ltu_p (minbound, min))
                min = minbound;
              if (wi::ltu_p (maxbound, max))
@@ -1221,6 +1177,79 @@ maybe_set_strlen_range (tree lhs, tree src, tree bound)
   return lhs;
 }
 
+/* For an LHS that is an SSA_NAME and for strlen() or strnlen() argument
+   SRC, set LHS range info to [0, min (N, BOUND)] if SRC refers to
+   a character array A[N] with unknown length bounded by N, and for
+   strnlen(), by min (N, BOUND).  */
+
+static tree
+maybe_set_strlen_range (tree lhs, tree src, tree bound)
+{
+  if (TREE_CODE (lhs) != SSA_NAME
+      || !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+    return NULL_TREE;
+
+  if (TREE_CODE (src) == SSA_NAME)
+    {
+      gimple *def = SSA_NAME_DEF_STMT (src);
+      if (is_gimple_assign (def)
+         && gimple_assign_rhs_code (def) == ADDR_EXPR)
+       src = gimple_assign_rhs1 (def);
+    }
+
+  /* The longest string is PTRDIFF_MAX - 1 bytes including the final
+     NUL so that the difference between a pointer to just past it and
+     one to its beginning is positive.  */
+  wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2;
+
+  if (TREE_CODE (src) == ADDR_EXPR)
+    {
+      /* The last array member of a struct can be bigger than its size
+        suggests if it's treated as a poor-man's flexible array member.  */
+      src = TREE_OPERAND (src, 0);
+      if (TREE_CODE (src) != MEM_REF
+         && !array_at_struct_end_p (src))
+       {
+         tree type = TREE_TYPE (src);
+         tree size = TYPE_SIZE_UNIT (type);
+         if (size
+             && TREE_CODE (size) == INTEGER_CST
+             && !integer_zerop (size))
+           {
+             /* Even though such uses of strlen would be undefined,
+                avoid relying on arrays of arrays in case some genius
+                decides to call strlen on an unterminated array element
+                that's followed by a terminated one.  Likewise, avoid
+                assuming that a struct array member is necessarily
+                nul-terminated (the nul may be in the member that
+                follows).  In those cases, assume that the length
+                of the string stored in such an array is bounded
+                by the size of the enclosing object if one can be
+                determined.  */
+             tree base = get_base_address (src);
+             if (VAR_P (base))
+               {
+                 if (tree size = DECL_SIZE_UNIT (base))
+                   if (size
+                       && TREE_CODE (size) == INTEGER_CST
+                       && TREE_CODE (TREE_TYPE (base)) != POINTER_TYPE)
+                     max = wi::to_wide (size);
+               }
+           }
+
+         /* For strlen() the upper bound above is equal to
+            the longest string that can be stored in the array
+            (i.e., it accounts for the terminating nul.  For
+            strnlen() bump up the maximum by one since the array
+            need not be nul-terminated.  */
+         if (!bound && max != 0)
+           --max;
+       }
+    }
+
+  return set_strlen_range (lhs, max, bound);
+}
+
 /* Handle a strlen call.  If strlen of the argument is known, replace
    the strlen call with the known value, otherwise remember that strlen
    of the argument is stored in the lhs SSA_NAME.  */
diff --git a/gcc/tree-ssa-strlen.h b/gcc/tree-ssa-strlen.h
index 6b1b819fffd..0b68465eb2b 100644
--- a/gcc/tree-ssa-strlen.h
+++ b/gcc/tree-ssa-strlen.h
@@ -23,5 +23,6 @@
 
 extern bool is_strlen_related_p (tree, tree);
 extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree);
+extern tree set_strlen_range (tree, wide_int, tree = NULL_TREE);
 
 #endif   // GCC_TREE_SSA_STRLEN_H

Reply via email to