Hi,
This is a simple patch exploiting more undefined pointer overflow behavior in
loop niter analysis.  Originally, it only supports POINTER_PLUS_EXPR if the
offset part is IV.  This patch also handles the case if pointer is IV.  With
this patch, the while(true) loop in test can be removed by cddce pass now.

Bootstrap and test on x86_64 and AArch64.  This patch introduces two failures:
FAIL: g++.dg/pr79095-1.C  -std=gnu++98 (test for excess errors)
FAIL: g++.dg/pr79095-2.C  -std=gnu++11 (test for excess errors)
I believe this exposes inaccurate value range information issue.  For below 
code:
/* { dg-do compile } */
/* { dg-options "-Wall -O3" } */

typedef long unsigned int size_t;

inline void
fill (int *p, size_t n, int)
{
  while (n--)
    *p++ = 0;
}

struct B
{
  int* p0, *p1, *p2;

  size_t size () const {
    return size_t (p1 - p0);
  }

  void resize (size_t n) {
    if (n > size())
      append (n - size());
  }

  void append (size_t n)
  {
    if (size_t (p2 - p1) >= n)   {
      fill (p1, n, 0);
    }
  }
};

void foo (B &b)
{
  if (b.size () != 0)
    b.resize (b.size () - 1);
}

GCC gives below warning with this patch:
pr79095-1.C: In function ‘void foo(B&)’:
pr79095-1.C:10:7: warning: iteration 4611686018427387903 invokes undefined 
behavior [-Waggressive-loop-optimizations]
     *p++ = 0;
      ~^~
pr79095-1.C:9:11: note: within this loop
   while (n--)
           ^~

Problem is VRP should understand that it's never the case with condition:
  (size_t (p2 - p1) >= n) 
in function B::append.

So, any comment?

Thanks,
bin
2017-11-02  Bin Cheng  <bin.ch...@arm.com>

        PR tree-optimization/82776
        * tree-ssa-loop-niter.c (infer_loop_bounds_from_pointer_arith): Handle
        POINTER_PLUS_EXPR in which the pointer is an IV.
        (infer_loop_bounds_from_signedness): Refine comment.

gcc/testsuite
2017-11-02  Bin Cheng  <bin.ch...@arm.com>

        PR tree-optimization/82776
        * g++.dg/pr82776.C: New test.
        * gcc.dg/tree-ssa/split-path-6.c: Refine test.
diff --git a/gcc/testsuite/g++.dg/pr82776.C b/gcc/testsuite/g++.dg/pr82776.C
new file mode 100644
index 0000000..2a66817
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr82776.C
@@ -0,0 +1,78 @@
+// PR tree-optimization/82776
+// { dg-do compile }
+// { dg-options "-O2 -std=c++14 -fdump-tree-cddce2-details" }
+
+#include <type_traits>
+#include <cstdint>
+#include <vector>
+#include <cstdlib>
+#include <array>
+#include <memory>
+
+
+unsigned baz (unsigned);
+
+struct Chunk {
+  std::array<uint8_t,14> tags_;
+  uint8_t control_;
+
+  bool eof() const {
+    return (control_ & 1) != 0;
+  }
+
+  static constexpr unsigned kFullMask = (1 << 14) - 1;
+
+  unsigned occupiedMask() const {
+    return baz (kFullMask);
+  }
+};
+
+#define LIKELY(x) __builtin_expect((x), true)
+#define UNLIKELY(x) __builtin_expect((x), false)
+
+struct Iter {
+  Chunk* chunk_;
+  std::size_t index_;
+
+  void advance() {
+    // common case is packed entries
+    while (index_ > 0) {
+      --index_;
+      if (LIKELY(chunk_->tags_[index_] != 0)) {
+        return;
+      }
+    }
+
+    // bar only skips the work of advance() if this loop can
+    // be guaranteed to terminate
+#ifdef ENABLE_FORLOOP
+    for (std::size_t i = 1; i != 0; ++i) {
+#else
+    while (true) {
+#endif
+      // exhausted the current chunk
+      if (chunk_->eof()) {
+        chunk_ = nullptr;
+        break;
+      }
+      ++chunk_;
+      auto m = chunk_->occupiedMask();
+      if (m != 0) {
+        index_ = 31 - __builtin_clz(m);
+        break;
+      }
+    }
+  }
+};
+
+static Iter foo(Iter iter) {
+  puts("hello");
+  iter.advance();
+  return iter;
+}
+
+void bar(Iter iter) {
+  foo(iter);
+}
+
+// { dg-final { scan-tree-dump-not "can not prove finiteness of loop" "cddce2" 
} }
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/split-path-6.c 
b/gcc/testsuite/gcc.dg/tree-ssa/split-path-6.c
index 682166f..2206d05 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/split-path-6.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/split-path-6.c
@@ -53,10 +53,11 @@ oof ()
 }
 
 void
-lookharder (string)
+lookharder (string, res)
      char *string;
+     char *res;
 {
-  register char *g;
+  register char *g = res;
   register char *s;
   for (s = string; *s != '\0'; s++)
     {
diff --git a/gcc/tree-ssa-loop-niter.c b/gcc/tree-ssa-loop-niter.c
index 6efe67a..7c1ac61 100644
--- a/gcc/tree-ssa-loop-niter.c
+++ b/gcc/tree-ssa-loop-niter.c
@@ -3422,7 +3422,7 @@ static void
 infer_loop_bounds_from_pointer_arith (struct loop *loop, gimple *stmt)
 {
   tree def, base, step, scev, type, low, high;
-  tree var, ptr;
+  tree rhs2, rhs1;
 
   if (!is_gimple_assign (stmt)
       || gimple_assign_rhs_code (stmt) != POINTER_PLUS_EXPR)
@@ -3436,12 +3436,13 @@ infer_loop_bounds_from_pointer_arith (struct loop 
*loop, gimple *stmt)
   if (!nowrap_type_p (type))
     return;
 
-  ptr = gimple_assign_rhs1 (stmt);
-  if (!expr_invariant_in_loop_p (loop, ptr))
+  rhs2 = gimple_assign_rhs2 (stmt);
+  if (TYPE_PRECISION (type) != TYPE_PRECISION (TREE_TYPE (rhs2)))
     return;
 
-  var = gimple_assign_rhs2 (stmt);
-  if (TYPE_PRECISION (type) != TYPE_PRECISION (TREE_TYPE (var)))
+  rhs1 = gimple_assign_rhs1 (stmt);
+  if (!expr_invariant_in_loop_p (loop, rhs1)
+      && !expr_invariant_in_loop_p (loop, rhs2))
     return;
 
   struct loop *uloop = loop_containing_stmt (stmt);
@@ -3521,6 +3522,8 @@ infer_loop_bounds_from_signedness (struct loop *loop, 
gimple *stmt)
      allocated size,
 
    - signed variables should not overflow when flag_wrapv is not set.
+
+   - pointer variables should not overflow when flag_wrapv is not set.
 */
 
 static void

Reply via email to