The following makes niter analysis recognize a loop with an exit
condition scanning over a STRING_CST.  This is done via enhancing
the force evaluation code rather than recognizing for example
strlen (s) as number of iterations because it allows to handle
some more cases.

STRING_CSTs are easy to handle since nothing can write to them, also
processing those should be cheap.  I'd appreciate another eye on
the constraints I put in.

Note to avoid the -Warray-bound dianostic we have to early unroll
the loop (there's no final value replacement done, there's a PR
for doing this as part of CD-DCE when possibly eliding a loop).
This works for strings up to 8 chars (including the '\0') only
(rather than 16, the unroll niter limit) because unroll estimation
will not see that the load from the string constant goes away.

Final value replacement doesn't work since ivcanon is now after it,
it's not the time to move the pass though.  The pass is in theory
supposed to add a canonical IV for the _by_eval cases, but we
didn't "fix" this when we added cunrolli (we probably should have
moved ivcanon very early, or made cunroll add such IV if we
used _by_eval but did not unroll).

Bootstrapped on x86_64-unknown-linux-gnu, testing in progress.

I'm not sure it's worth handling this special case in the imperfect
way at this point.  I have a followup to also handle POINTER_PLUS
of "Hello World" and 1 which appears when doing a C test and that
shows IVCANON then eventually adds a simple counting IV, we DCE
the load and late unroll it for larger strings.

        PR tree-optimization/92539
        * tree-ssa-loop-ivcanon.cc (tree_unroll_loops_completely_1):
        Also try force-evaluation if ivcanon did not yet run.
        * tree-ssa-loop-niter.cc (loop_niter_by_eval): When we
        don't find a proper PHI try if the exit condition scans
        over a STRING_CST and simulate that.

        * g++.dg/warn/Warray-bounds-pr92539.C: New testcase.
---
 .../g++.dg/warn/Warray-bounds-pr92539.C       | 51 +++++++++++++++++++
 gcc/tree-ssa-loop-ivcanon.cc                  |  2 +-
 gcc/tree-ssa-loop-niter.cc                    | 39 +++++++++++++-
 3 files changed, 90 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/warn/Warray-bounds-pr92539.C

diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-pr92539.C 
b/gcc/testsuite/g++.dg/warn/Warray-bounds-pr92539.C
new file mode 100644
index 00000000000..ea506ed1450
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-pr92539.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-O2 -Warray-bounds" }
+
+static bool
+ischar(int ch)
+{
+    return (0 == (ch & ~0xff) || ~0 == (ch | 0xff)) != 0;
+}
+
+static bool eat(char const*& first, char const* last)
+{
+    if (first != last && ischar(*first)) { // { dg-bogus "bounds" }
+        ++first;
+        return true;
+    }
+    return false;
+}
+
+static bool eat_two(char const*& first, char const* last)
+{
+    auto save = first;
+    if (eat(first, last) && eat(first, last))
+        return true;
+    first = save;
+    return false;
+}
+
+static bool foo(char const*& first, char const* last)
+{
+    auto local_iterator = first;
+    int i = 0;
+    for (; i < 3; ++i)
+        if (!eat_two(local_iterator, last))
+            return false;
+    first = local_iterator;
+    return true;
+}
+
+static bool test(char const* in, bool full_match = true)
+{
+    auto last = in;
+    while (*last)
+        ++last;
+    return foo(in, last) && (!full_match || (in == last)); // { dg-bogus 
"bounds" }
+}
+
+int main()
+{
+    return test("aa");
+}
+
diff --git a/gcc/tree-ssa-loop-ivcanon.cc b/gcc/tree-ssa-loop-ivcanon.cc
index 6e6569e5713..7cc340b23c5 100644
--- a/gcc/tree-ssa-loop-ivcanon.cc
+++ b/gcc/tree-ssa-loop-ivcanon.cc
@@ -1492,7 +1492,7 @@ tree_unroll_loops_completely_1 (bool may_increase_size, 
bool unroll_outer,
     ul = UL_NO_GROWTH;
 
   if (canonicalize_loop_induction_variables
-      (loop, false, ul, !flag_tree_loop_ivcanon, unroll_outer,
+      (loop, false, ul, !flag_tree_loop_ivcanon || cunrolli, unroll_outer,
        innermost, cunrolli))
     {
       /* If we'll continue unrolling, we need to propagate constants
diff --git a/gcc/tree-ssa-loop-niter.cc b/gcc/tree-ssa-loop-niter.cc
index 9e966266ca3..7d9b7eb7594 100644
--- a/gcc/tree-ssa-loop-niter.cc
+++ b/gcc/tree-ssa-loop-niter.cc
@@ -3595,7 +3595,44 @@ loop_niter_by_eval (class loop *loop, edge exit)
        {
          phi = get_base_for (loop, op[j]);
          if (!phi)
-           return chrec_dont_know;
+           {
+             gassign *def;
+             if (j == 0
+                 && (cmp == NE_EXPR || cmp == EQ_EXPR)
+                 && TREE_CODE (op[0]) == SSA_NAME
+                 && TREE_CODE (op[1]) == INTEGER_CST
+                 && (def = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (op[0])))
+                 && gimple_assign_rhs_code (def) == MEM_REF)
+               {
+                 tree mem = gimple_assign_rhs1 (def);
+                 affine_iv iv;
+                 if (TYPE_MODE (TREE_TYPE (mem)) == TYPE_MODE (char_type_node)
+                     && simple_iv (loop, loop,
+                                   TREE_OPERAND (mem, 0), &iv, false)
+                     && TREE_CODE (iv.base) == ADDR_EXPR
+                     && TREE_CODE (TREE_OPERAND (iv.base, 0)) == STRING_CST
+                     && tree_fits_uhwi_p (TREE_OPERAND (mem, 1))
+                     && tree_fits_uhwi_p (iv.step))
+                   {
+                     tree str = TREE_OPERAND (iv.base, 0);
+                     unsigned i = 0;
+                     for (unsigned HOST_WIDE_INT idx
+                            = tree_to_uhwi (TREE_OPERAND (mem, 1));
+                          idx < (unsigned)TREE_STRING_LENGTH (str)
+                          && i < MAX_ITERATIONS_TO_TRACK;
+                          idx += tree_to_uhwi (iv.step), ++i)
+                       {
+                         int res
+                           = compare_tree_int (op[1],
+                                               TREE_STRING_POINTER (str)[idx]);
+                         if ((cmp == NE_EXPR && res == 0)
+                             || (cmp == EQ_EXPR && res != 0))
+                           return build_int_cst (unsigned_type_node, i);
+                       }
+                   }
+               }
+             return chrec_dont_know;
+           }
          val[j] = PHI_ARG_DEF_FROM_EDGE (phi, loop_preheader_edge (loop));
          next[j] = PHI_ARG_DEF_FROM_EDGE (phi, loop_latch_edge (loop));
        }
-- 
2.43.0

Reply via email to