re PR c++/86443 (ICEs on #pragma omp distribute parallel for with class iterators)
authorJakub Jelinek <jakub@redhat.com>
Tue, 10 Jul 2018 07:12:37 +0000 (09:12 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Tue, 10 Jul 2018 07:12:37 +0000 (09:12 +0200)
PR c++/86443
* gimplify.c (find_combined_omp_for): Add DATA argument, in addition
to finding the inner OMP_FOR/OMP_SIMD stmt find non-trivial wrappers,
BLOCKs with BLOCK_VARs, OMP_PARALLEL in between, OMP_FOR in between.
(gimplify_omp_for): For composite loops, move outer
OMP_{DISTRIBUTE,TASKLOOP,FOR,PARALLEL} right around innermost
OMP_FOR/OMP_SIMD if there are any non-trivial wrappers.  For class
iterators add any needed clauses.  Allow OMP_FOR_ORIG_DECLS to contain
TREE_LIST for both the original class iterator and the "last" helper
var.  Gimplify OMP_FOR_PRE_BODY before the outermost composite
loop, remember has_decl_expr from outer composite loops for the
innermost OMP_SIMD in TREE_PRIVATE bit on OMP_FOR_INIT.
gcc/c-family/
* c-omp.c (c_omp_check_loop_iv_r, c_omp_check_loop_iv): Allow declv
to contain TREE_LIST for both the original class iterator and the
"last" helper var.
gcc/cp/
* semantics.c (handle_omp_for_class_iterator): Remove lastp argument,
instead of setting *lastp turn orig_declv elt into a TREE_LIST.
(finish_omp_for): Adjust handle_omp_for_class_iterator caller.
* pt.c (tsubst_omp_for_iterator): Allow OMP_FOR_ORIG_DECLS to contain
TREE_LIST for both the original class iterator and the "last" helper
var.
libgomp/
* testsuite/libgomp.c++/for-15.C: New test.

From-SVN: r262534

gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-omp.c
gcc/cp/ChangeLog
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/gimplify.c
libgomp/ChangeLog
libgomp/testsuite/libgomp.c++/for-15.C [new file with mode: 0644]

index dda22d8e180ace281c24687ce3f158519a4fd791..3230cf3aa82d4077ca4fcc6e1934865e5c2bacb4 100644 (file)
@@ -1,3 +1,18 @@
+2018-07-10  Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/86443
+       * gimplify.c (find_combined_omp_for): Add DATA argument, in addition
+       to finding the inner OMP_FOR/OMP_SIMD stmt find non-trivial wrappers,
+       BLOCKs with BLOCK_VARs, OMP_PARALLEL in between, OMP_FOR in between.
+       (gimplify_omp_for): For composite loops, move outer
+       OMP_{DISTRIBUTE,TASKLOOP,FOR,PARALLEL} right around innermost
+       OMP_FOR/OMP_SIMD if there are any non-trivial wrappers.  For class
+       iterators add any needed clauses.  Allow OMP_FOR_ORIG_DECLS to contain
+       TREE_LIST for both the original class iterator and the "last" helper
+       var.  Gimplify OMP_FOR_PRE_BODY before the outermost composite
+       loop, remember has_decl_expr from outer composite loops for the
+       innermost OMP_SIMD in TREE_PRIVATE bit on OMP_FOR_INIT.
+
 2018-07-09  Martin Sebor  <msebor@redhat.com>
 
        PR middle-end/77357
index bf4e7e9bd89a54fb146b13c838c5a2cdb768f957..a459f0613265c20435a3b9fa5a907b16d27d51d1 100644 (file)
@@ -1,3 +1,10 @@
+2018-07-10  Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/86443
+       * c-omp.c (c_omp_check_loop_iv_r, c_omp_check_loop_iv): Allow declv
+       to contain TREE_LIST for both the original class iterator and the
+       "last" helper var.
+
 2018-07-07  Eric Botcazou  <ebotcazou@adacore.com>
 
        * c-ada-spec.c (to_ada_name): Remove index parameter.
index d8695f517af078201aecbc6e04db27edec5bcb3c..89849379ca7691e91d9ac1ac8cfa2a4672bbea76 100644 (file)
@@ -827,7 +827,9 @@ c_omp_check_loop_iv_r (tree *tp, int *walk_subtrees, void *data)
     {
       int i;
       for (i = 0; i < TREE_VEC_LENGTH (d->declv); i++)
-       if (*tp == TREE_VEC_ELT (d->declv, i))
+       if (*tp == TREE_VEC_ELT (d->declv, i)
+           || (TREE_CODE (TREE_VEC_ELT (d->declv, i)) == TREE_LIST
+               && *tp == TREE_PURPOSE (TREE_VEC_ELT (d->declv, i))))
          {
            location_t loc = d->expr_loc;
            if (loc == UNKNOWN_LOCATION)
@@ -894,7 +896,9 @@ c_omp_check_loop_iv (tree stmt, tree declv, walk_tree_lh lh)
         expression then involves the subtraction and always refers
         to the original value.  The C++ FE needs to warn on those
         earlier.  */
-      if (decl == TREE_VEC_ELT (declv, i))
+      if (decl == TREE_VEC_ELT (declv, i)
+         || (TREE_CODE (TREE_VEC_ELT (declv, i)) == TREE_LIST
+             && decl == TREE_PURPOSE (TREE_VEC_ELT (declv, i))))
        {
          data.expr_loc = EXPR_LOCATION (cond);
          data.kind = 1;
index 82cb481ff779339d68dc575631e834daac0bc71d..86d4a349905975de544b1cfbe52bcb9045c48aa8 100644 (file)
@@ -1,3 +1,13 @@
+2018-07-10  Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/86443
+       * semantics.c (handle_omp_for_class_iterator): Remove lastp argument,
+       instead of setting *lastp turn orig_declv elt into a TREE_LIST.
+       (finish_omp_for): Adjust handle_omp_for_class_iterator caller.
+       * pt.c (tsubst_omp_for_iterator): Allow OMP_FOR_ORIG_DECLS to contain
+       TREE_LIST for both the original class iterator and the "last" helper
+       var.
+
 2018-07-09  Paolo Carlini  <paolo.carlini@oracle.com>
 
        * decl.c (grokdeclarator): Use rich_location::add_range in three
index 3780f3492aa1b7fec32e3152210a140e62eb61db..eae9e14d37d0bf302d60142025d9d01dbda3c7bd 100644 (file)
@@ -16267,7 +16267,12 @@ tsubst_omp_for_iterator (tree t, int i, tree declv, tree orig_declv,
   if (orig_declv && OMP_FOR_ORIG_DECLS (t))
     {
       tree o = TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (t), i);
-      TREE_VEC_ELT (orig_declv, i) = RECUR (o);
+      if (TREE_CODE (o) == TREE_LIST)
+       TREE_VEC_ELT (orig_declv, i)
+         = tree_cons (RECUR (TREE_PURPOSE (o)),
+                      RECUR (TREE_VALUE (o)), NULL_TREE);
+      else
+       TREE_VEC_ELT (orig_declv, i) = RECUR (o);
     }
 
   decl = TREE_OPERAND (init, 0);
index c779137da456f07ad5cdd6498b098b6f9fae9f62..ef73d07161fb3ea5e69e91aefb2f25d24f14442f 100644 (file)
@@ -7679,7 +7679,7 @@ static bool
 handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code,
                               tree declv, tree orig_declv, tree initv,
                               tree condv, tree incrv, tree *body,
-                              tree *pre_body, tree &clauses, tree *lastp,
+                              tree *pre_body, tree &clauses,
                               int collapse, int ordered)
 {
   tree diff, iter_init, iter_incr = NULL, last;
@@ -7983,7 +7983,8 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code,
   TREE_VEC_ELT (initv, i) = init;
   TREE_VEC_ELT (condv, i) = cond;
   TREE_VEC_ELT (incrv, i) = incr;
-  *lastp = last;
+  TREE_VEC_ELT (orig_declv, i)
+    = tree_cons (TREE_VEC_ELT (orig_declv, i), last, NULL_TREE);
 
   return false;
 }
@@ -8002,7 +8003,6 @@ finish_omp_for (location_t locus, enum tree_code code, tree declv,
 {
   tree omp_for = NULL, orig_incr = NULL;
   tree decl = NULL, init, cond, incr;
-  tree last = NULL_TREE;
   location_t elocus;
   int i;
   int collapse = 1;
@@ -8169,7 +8169,7 @@ finish_omp_for (location_t locus, enum tree_code code, tree declv,
            }
          if (handle_omp_for_class_iterator (i, locus, code, declv, orig_declv,
                                             initv, condv, incrv, &body,
-                                            &pre_body, clauses, &last,
+                                            &pre_body, clauses,
                                             collapse, ordered))
            return NULL;
          continue;
index c86ad1ad96279ab7e99839090ac3fea341026071..6b76d17dbaf6d32fe07cdef537377fa0e0cc8d0a 100644 (file)
@@ -9532,24 +9532,53 @@ gimplify_omp_task (tree *expr_p, gimple_seq *pre_p)
 }
 
 /* Helper function of gimplify_omp_for, find OMP_FOR resp. OMP_SIMD
-   with non-NULL OMP_FOR_INIT.  */
+   with non-NULL OMP_FOR_INIT.  Also, fill in pdata array,
+   pdata[0] non-NULL if there is anything non-trivial in between, pdata[1]
+   is address of OMP_PARALLEL in between if any, pdata[2] is address of
+   OMP_FOR in between if any and pdata[3] is address of the inner
+   OMP_FOR/OMP_SIMD.  */
 
 static tree
-find_combined_omp_for (tree *tp, int *walk_subtrees, void *)
+find_combined_omp_for (tree *tp, int *walk_subtrees, void *data)
 {
+  tree **pdata = (tree **) data;
   *walk_subtrees = 0;
   switch (TREE_CODE (*tp))
     {
     case OMP_FOR:
+      if (OMP_FOR_INIT (*tp) != NULL_TREE)
+       {
+         pdata[3] = tp;
+         return *tp;
+       }
+      pdata[2] = tp;
       *walk_subtrees = 1;
-      /* FALLTHRU */
+      break;
     case OMP_SIMD:
       if (OMP_FOR_INIT (*tp) != NULL_TREE)
-       return *tp;
+       {
+         pdata[3] = tp;
+         return *tp;
+       }
       break;
     case BIND_EXPR:
+      if (BIND_EXPR_VARS (*tp)
+         || (BIND_EXPR_BLOCK (*tp)
+             && BLOCK_VARS (BIND_EXPR_BLOCK (*tp))))
+       pdata[0] = tp;
+      *walk_subtrees = 1;
+      break;
     case STATEMENT_LIST:
+      if (!tsi_one_before_end_p (tsi_start (*tp)))
+       pdata[0] = tp;
+      *walk_subtrees = 1;
+      break;
+    case TRY_FINALLY_EXPR:
+      pdata[0] = tp;
+      *walk_subtrees = 1;
+      break;
     case OMP_PARALLEL:
+      pdata[1] = tp;
       *walk_subtrees = 1;
       break;
     default:
@@ -9574,6 +9603,115 @@ gimplify_omp_for (tree *expr_p, gimple_seq *pre_p)
 
   orig_for_stmt = for_stmt = *expr_p;
 
+  if (OMP_FOR_INIT (for_stmt) == NULL_TREE)
+    {
+      tree *data[4] = { NULL, NULL, NULL, NULL };
+      gcc_assert (TREE_CODE (for_stmt) != OACC_LOOP);
+      inner_for_stmt = walk_tree (&OMP_FOR_BODY (for_stmt),
+                                 find_combined_omp_for, data, NULL);
+      if (inner_for_stmt == NULL_TREE)
+       {
+         gcc_assert (seen_error ());
+         *expr_p = NULL_TREE;
+         return GS_ERROR;
+       }
+      if (data[2] && OMP_FOR_PRE_BODY (*data[2]))
+       {
+         append_to_statement_list_force (OMP_FOR_PRE_BODY (*data[2]),
+                                         &OMP_FOR_PRE_BODY (for_stmt));
+         OMP_FOR_PRE_BODY (*data[2]) = NULL_TREE;
+       }
+      if (OMP_FOR_PRE_BODY (inner_for_stmt))
+       {
+         append_to_statement_list_force (OMP_FOR_PRE_BODY (inner_for_stmt),
+                                         &OMP_FOR_PRE_BODY (for_stmt));
+         OMP_FOR_PRE_BODY (inner_for_stmt) = NULL_TREE;
+       }
+
+      if (data[0])
+       {
+         /* We have some statements or variable declarations in between
+            the composite construct directives.  Move them around the
+            inner_for_stmt.  */
+         data[0] = expr_p;
+         for (i = 0; i < 3; i++)
+           if (data[i])
+             {
+               tree t = *data[i];
+               if (i < 2 && data[i + 1] == &OMP_BODY (t))
+                 data[i + 1] = data[i];
+               *data[i] = OMP_BODY (t);
+               tree body = build3 (BIND_EXPR, void_type_node, NULL_TREE,
+                                   NULL_TREE, make_node (BLOCK));
+               OMP_BODY (t) = body;
+               append_to_statement_list_force (inner_for_stmt,
+                                               &BIND_EXPR_BODY (body));
+               *data[3] = t;
+               data[3] = tsi_stmt_ptr (tsi_start (BIND_EXPR_BODY (body)));
+               gcc_assert (*data[3] == inner_for_stmt);
+             }
+         return GS_OK;
+       }
+
+      for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (inner_for_stmt)); i++)
+       if (OMP_FOR_ORIG_DECLS (inner_for_stmt)
+           && TREE_CODE (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (inner_for_stmt),
+                                       i)) == TREE_LIST)
+         {
+           tree orig = TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (inner_for_stmt), i);
+           /* Class iterators aren't allowed on OMP_SIMD, so the only
+              case we need to solve is distribute parallel for.  */
+           gcc_assert (TREE_CODE (inner_for_stmt) == OMP_FOR
+                       && TREE_CODE (for_stmt) == OMP_DISTRIBUTE
+                       && data[1]);
+           tree orig_decl = TREE_PURPOSE (orig);
+           tree last = TREE_VALUE (orig);
+           tree *pc;
+           for (pc = &OMP_FOR_CLAUSES (inner_for_stmt);
+                *pc; pc = &OMP_CLAUSE_CHAIN (*pc))
+             if ((OMP_CLAUSE_CODE (*pc) == OMP_CLAUSE_PRIVATE
+                  || OMP_CLAUSE_CODE (*pc) == OMP_CLAUSE_LASTPRIVATE)
+                 && OMP_CLAUSE_DECL (*pc) == orig_decl)
+               break;
+           if (*pc == NULL_TREE)
+             ;
+           else if (OMP_CLAUSE_CODE (*pc) == OMP_CLAUSE_PRIVATE)
+             {
+               /* private clause will appear only on inner_for_stmt.
+                  Change it into firstprivate, and add private clause
+                  on for_stmt.  */
+               tree c = copy_node (*pc);
+               OMP_CLAUSE_CHAIN (c) = OMP_FOR_CLAUSES (for_stmt);
+               OMP_FOR_CLAUSES (for_stmt) = c;
+               OMP_CLAUSE_CODE (*pc) = OMP_CLAUSE_FIRSTPRIVATE;
+               lang_hooks.decls.omp_finish_clause (*pc, pre_p);
+             }
+           else
+             {
+               /* lastprivate clause will appear on both inner_for_stmt
+                  and for_stmt.  Add firstprivate clause to
+                  inner_for_stmt.  */
+               tree c = build_omp_clause (OMP_CLAUSE_LOCATION (*pc),
+                                          OMP_CLAUSE_FIRSTPRIVATE);
+               OMP_CLAUSE_DECL (c) = OMP_CLAUSE_DECL (*pc);
+               OMP_CLAUSE_CHAIN (c) = *pc;
+               *pc = c;
+               lang_hooks.decls.omp_finish_clause (*pc, pre_p);
+             }
+           tree c = build_omp_clause (UNKNOWN_LOCATION,
+                                      OMP_CLAUSE_FIRSTPRIVATE);
+           OMP_CLAUSE_DECL (c) = last;
+           OMP_CLAUSE_CHAIN (c) = OMP_PARALLEL_CLAUSES (*data[1]);
+           OMP_PARALLEL_CLAUSES (*data[1]) = c;
+           c = build_omp_clause (UNKNOWN_LOCATION,
+                                 *pc ? OMP_CLAUSE_SHARED
+                                     : OMP_CLAUSE_FIRSTPRIVATE);
+           OMP_CLAUSE_DECL (c) = orig_decl;
+           OMP_CLAUSE_CHAIN (c) = OMP_PARALLEL_CLAUSES (*data[1]);
+           OMP_PARALLEL_CLAUSES (*data[1]) = c;
+         }
+    }
+
   switch (TREE_CODE (for_stmt))
     {
     case OMP_FOR:
@@ -9611,19 +9749,6 @@ gimplify_omp_for (tree *expr_p, gimple_seq *pre_p)
          }
     }
 
-  if (OMP_FOR_INIT (for_stmt) == NULL_TREE)
-    {
-      gcc_assert (TREE_CODE (for_stmt) != OACC_LOOP);
-      inner_for_stmt = walk_tree (&OMP_FOR_BODY (for_stmt),
-                                 find_combined_omp_for, NULL, NULL);
-      if (inner_for_stmt == NULL_TREE)
-       {
-         gcc_assert (seen_error ());
-         *expr_p = NULL_TREE;
-         return GS_ERROR;
-       }
-    }
-
   if (TREE_CODE (for_stmt) != OMP_TASKLOOP)
     gimplify_scan_omp_clauses (&OMP_FOR_CLAUSES (for_stmt), pre_p, ort,
                               TREE_CODE (for_stmt));
@@ -9633,7 +9758,9 @@ gimplify_omp_for (tree *expr_p, gimple_seq *pre_p)
 
   /* Handle OMP_FOR_INIT.  */
   for_pre_body = NULL;
-  if (ort == ORT_SIMD && OMP_FOR_PRE_BODY (for_stmt))
+  if ((ort == ORT_SIMD
+       || (inner_for_stmt && TREE_CODE (inner_for_stmt) == OMP_SIMD))
+      && OMP_FOR_PRE_BODY (for_stmt))
     {
       has_decl_expr = BITMAP_ALLOC (NULL);
       if (TREE_CODE (OMP_FOR_PRE_BODY (for_stmt)) == DECL_EXPR
@@ -9774,8 +9901,12 @@ gimplify_omp_for (tree *expr_p, gimple_seq *pre_p)
       if (is_doacross)
        {
          if (TREE_CODE (for_stmt) == OMP_FOR && OMP_FOR_ORIG_DECLS (for_stmt))
-           gimplify_omp_ctxp->loop_iter_var.quick_push
-             (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (for_stmt), i));
+           {
+             tree orig_decl = TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (for_stmt), i);
+             if (TREE_CODE (orig_decl) == TREE_LIST)
+               orig_decl = TREE_PURPOSE (orig_decl);
+             gimplify_omp_ctxp->loop_iter_var.quick_push (orig_decl);
+           }
          else
            gimplify_omp_ctxp->loop_iter_var.quick_push (decl);
          gimplify_omp_ctxp->loop_iter_var.quick_push (decl);
@@ -9785,7 +9916,12 @@ gimplify_omp_for (tree *expr_p, gimple_seq *pre_p)
       tree c = NULL_TREE;
       tree c2 = NULL_TREE;
       if (orig_for_stmt != for_stmt)
-       /* Do this only on innermost construct for combined ones.  */;
+       {
+         /* Preserve this information until we gimplify the inner simd.  */
+         if (has_decl_expr
+             && bitmap_bit_p (has_decl_expr, DECL_UID (decl)))
+           TREE_PRIVATE (t) = 1;
+       }
       else if (ort == ORT_SIMD)
        {
          splay_tree_node n = splay_tree_lookup (gimplify_omp_ctxp->variables,
@@ -9800,8 +9936,9 @@ gimplify_omp_for (tree *expr_p, gimple_seq *pre_p)
              c = build_omp_clause (input_location, OMP_CLAUSE_LINEAR);
              OMP_CLAUSE_LINEAR_NO_COPYIN (c) = 1;
              unsigned int flags = GOVD_LINEAR | GOVD_EXPLICIT | GOVD_SEEN;
-             if (has_decl_expr
-                 && bitmap_bit_p (has_decl_expr, DECL_UID (decl)))
+             if ((has_decl_expr
+                  && bitmap_bit_p (has_decl_expr, DECL_UID (decl)))
+                 || TREE_PRIVATE (t))
                {
                  OMP_CLAUSE_LINEAR_NO_COPYOUT (c) = 1;
                  flags |= GOVD_LINEAR_LASTPRIVATE_NO_OUTER;
@@ -9923,6 +10060,8 @@ gimplify_omp_for (tree *expr_p, gimple_seq *pre_p)
              bool lastprivate
                = (!has_decl_expr
                   || !bitmap_bit_p (has_decl_expr, DECL_UID (decl)));
+             if (TREE_PRIVATE (t))
+               lastprivate = false;
              struct gimplify_omp_ctx *outer
                = gimplify_omp_ctxp->outer_context;
              if (outer && lastprivate)
index ef0cdd06422195cfdd8cb8babc4793f177e4cf30..1423598d1892a60eadb081a08273de83e2539376 100644 (file)
@@ -1,3 +1,8 @@
+2018-07-10  Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/86443
+       * testsuite/libgomp.c++/for-15.C: New test.
+
 2018-06-26  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/86291
diff --git a/libgomp/testsuite/libgomp.c++/for-15.C b/libgomp/testsuite/libgomp.c++/for-15.C
new file mode 100644 (file)
index 0000000..5e922ac
--- /dev/null
@@ -0,0 +1,232 @@
+// PR c++/86443
+// { dg-do run }
+// { dg-additional-options "-std=c++17" }
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+extern "C" void abort ();
+
+#pragma omp declare target
+template <typename T>
+class I
+{
+public:
+  typedef ptrdiff_t difference_type;
+  I ();
+  ~I ();
+  I (T *);
+  I (const I &);
+  T &operator * ();
+  T *operator -> ();
+  T &operator [] (const difference_type &) const;
+  I &operator = (const I &);
+  I &operator ++ ();
+  I operator ++ (int);
+  I &operator -- ();
+  I operator -- (int);
+  I &operator += (const difference_type &);
+  I &operator -= (const difference_type &);
+  I operator + (const difference_type &) const;
+  I operator - (const difference_type &) const;
+  template <typename S> friend bool operator == (I<S> &, I<S> &);
+  template <typename S> friend bool operator == (const I<S> &, const I<S> &);
+  template <typename S> friend bool operator < (I<S> &, I<S> &);
+  template <typename S> friend bool operator < (const I<S> &, const I<S> &);
+  template <typename S> friend bool operator <= (I<S> &, I<S> &);
+  template <typename S> friend bool operator <= (const I<S> &, const I<S> &);
+  template <typename S> friend bool operator > (I<S> &, I<S> &);
+  template <typename S> friend bool operator > (const I<S> &, const I<S> &);
+  template <typename S> friend bool operator >= (I<S> &, I<S> &);
+  template <typename S> friend bool operator >= (const I<S> &, const I<S> &);
+  template <typename S> friend typename I<S>::difference_type operator - (I<S> &, I<S> &);
+  template <typename S> friend typename I<S>::difference_type operator - (const I<S> &, const I<S> &);
+  template <typename S> friend I<S> operator + (typename I<S>::difference_type , const I<S> &);
+private:
+  T *p;
+};
+template <typename T> I<T>::I () : p (0) {}
+template <typename T> I<T>::~I () {}
+template <typename T> I<T>::I (T *x) : p (x) {}
+template <typename T> I<T>::I (const I &x) : p (x.p) {}
+template <typename T> T &I<T>::operator * () { return *p; }
+template <typename T> T *I<T>::operator -> () { return p; }
+template <typename T> T &I<T>::operator [] (const difference_type &x) const { return p[x]; }
+template <typename T> I<T> &I<T>::operator = (const I &x) { p = x.p; return *this; }
+template <typename T> I<T> &I<T>::operator ++ () { ++p; return *this; }
+template <typename T> I<T> I<T>::operator ++ (int) { return I (p++); }
+template <typename T> I<T> &I<T>::operator -- () { --p; return *this; }
+template <typename T> I<T> I<T>::operator -- (int) { return I (p--); }
+template <typename T> I<T> &I<T>::operator += (const difference_type &x) { p += x; return *this; }
+template <typename T> I<T> &I<T>::operator -= (const difference_type &x) { p -= x; return *this; }
+template <typename T> I<T> I<T>::operator + (const difference_type &x) const { return I (p + x); }
+template <typename T> I<T> I<T>::operator - (const difference_type &x) const { return I (p - x); }
+template <typename T> bool operator == (I<T> &x, I<T> &y) { return x.p == y.p; }
+template <typename T> bool operator == (const I<T> &x, const I<T> &y) { return x.p == y.p; }
+template <typename T> bool operator != (I<T> &x, I<T> &y) { return !(x == y); }
+template <typename T> bool operator != (const I<T> &x, const I<T> &y) { return !(x == y); }
+template <typename T> bool operator < (I<T> &x, I<T> &y) { return x.p < y.p; }
+template <typename T> bool operator < (const I<T> &x, const I<T> &y) { return x.p < y.p; }
+template <typename T> bool operator <= (I<T> &x, I<T> &y) { return x.p <= y.p; }
+template <typename T> bool operator <= (const I<T> &x, const I<T> &y) { return x.p <= y.p; }
+template <typename T> bool operator > (I<T> &x, I<T> &y) { return x.p > y.p; }
+template <typename T> bool operator > (const I<T> &x, const I<T> &y) { return x.p > y.p; }
+template <typename T> bool operator >= (I<T> &x, I<T> &y) { return x.p >= y.p; }
+template <typename T> bool operator >= (const I<T> &x, const I<T> &y) { return x.p >= y.p; }
+template <typename T> typename I<T>::difference_type operator - (I<T> &x, I<T> &y) { return x.p - y.p; }
+template <typename T> typename I<T>::difference_type operator - (const I<T> &x, const I<T> &y) { return x.p - y.p; }
+template <typename T> I<T> operator + (typename I<T>::difference_type x, const I<T> &y) { return I<T> (x + y.p); }
+
+template <typename T>
+class J
+{
+public:
+  J(const I<T> &x, const I<T> &y) : b (x), e (y) {}
+  const I<T> &begin ();
+  const I<T> &end ();
+private:
+  I<T> b, e;
+};
+
+template <typename T> const I<T> &J<T>::begin () { return b; }
+template <typename T> const I<T> &J<T>::end () { return e; }
+
+int a[2000];
+int results[2000];
+
+template <typename T>
+void
+baz (I<T> &i)
+{
+  if (*i < 0 || *i >= 2000)
+    abort ();
+  results[*i]++;
+}
+
+void
+baz (int i)
+{
+  if (i < 0 || i >= 2000)
+    abort ();
+  results[i]++;
+}
+
+void
+qux (I<int> &i)
+{
+  if (*i != 1931)
+    abort ();
+}
+
+void
+f1 (J<int> j)
+{
+#pragma omp distribute parallel for default(none)
+  for (I<int> i = j.begin (); i < j.end (); i += 3)
+    baz (*i);
+}
+
+void
+f2 (J<int> j)
+{
+  I<int> i;
+#pragma omp distribute parallel for default(none)
+  for (i = j.begin (); i < j.end (); ++i)
+    baz (*i);
+}
+
+template <int N>
+void
+f3 (J<int> j)
+{
+#pragma omp distribute parallel for default(none)
+  for (I<int> i = j.begin (); i < j.end (); i += 6)
+    baz (*i);
+}
+
+template <int N>
+void
+f4 (J<int> j)
+{
+  I<int> i;
+#pragma omp distribute parallel for default(none)
+  for (i = j.begin (); i < j.end (); i += 9)
+    baz (*i);
+}
+
+template <typename T>
+void
+f5 (J<T> j)
+{
+#pragma omp distribute parallel for default(none)
+  for (I<T> i = j.begin (); i < j.end (); i += 4)
+    baz (*i);
+}
+
+template <typename T>
+void
+f6 (J<T> j)
+{
+  I<T> i;
+#pragma omp distribute parallel for default(none)
+  for (i = j.begin (); i < j.end (); i += 7)
+    baz (*i);
+}
+
+#pragma omp end declare target
+
+#define check(expr) \
+  for (int i = 0; i < 2000; i++)                       \
+    if (expr)                                          \
+      {                                                        \
+       if (results[i] != 1)                            \
+         abort ();                                     \
+       results[i] = 0;                                 \
+      }                                                        \
+    else if (results[i])                               \
+      abort ()
+
+int
+main ()
+{
+  int a[2000];
+  for (int i = 0; i < 2000; i++)
+    a[i] = i;
+  #pragma omp target data map (to: a)
+  {
+    #pragma omp target teams map (tofrom: results)
+    {
+      J<int> j (&a[75], &a[1945]);
+      f1 (j);
+    }
+    check (i >= 75 && i < 1945 && (i - 75) % 3 == 0);
+    #pragma omp target teams map (tofrom: results)
+    {
+      J<int> j (&a[63], &a[1949]);
+      f2 (j);
+    }
+    check (i >= 63 && i < 1949);
+    #pragma omp target teams map (tofrom: results)
+    {
+      J<int> j (&a[58], &a[1979]);
+      f3 <2> (j);
+    }
+    check (i >= 58 && i < 1979 && (i - 58) % 6 == 0);
+    #pragma omp target teams map (tofrom: results)
+    {
+      J<int> j (&a[59], &a[1981]);
+      f4 <9> (j);
+    }
+    check (i >= 59 && i < 1981 && (i - 59) % 9 == 0);
+    #pragma omp target teams map (tofrom: results)
+    {
+      J<int> j (&a[52], &a[1972]);
+      f5 (j);
+    }
+    check (i >= 52 && i < 1972 && (i - 52) % 4 == 0);
+    #pragma omp target teams map (tofrom: results)
+    {
+      J<int> j (&a[31], &a[1827]);
+      f6 (j);
+    }
+    check (i >= 31 && i < 1827 && (i - 31) % 7 == 0);
+  }
+}