
OpenMP 5.0 removed the restriction that multiple collapsed loops must be perfectly nested, allowing "intervening code" (including nested BLOCKs) before or after each nested loop. In GCC this code is moved into the inner loop body by the respective front ends. This patch changes the C++ front end to use recursive descent parsing on nested loops within an "omp for" construct, rather than an iterative approach, in order to preserve proper nesting of compound statements. Preserving cleanups (destructors) for class objects declared in intervening code and loop initializers complicates moving the former into the body of the loop; this is handled by parsing the entire construct before reassembling any of it. gcc/cp/ChangeLog * cp-tree.h (cp_convert_omp_range_for): Adjust declaration. * parser.cc (struct omp_for_parse_data): New. (cp_parser_postfix_expression): Diagnose calls to OpenMP runtime in intervening code. (check_omp_intervening_code): New. (cp_parser_statement_seq_opt): Special-case nested loops, blocks, and other constructs for OpenMP loops. (cp_parser_iteration_statement): Reject loops in intervening code. (cp_parser_omp_for_loop_init): Expand comments and tweak the interface slightly to better distinguish input/output parameters. (cp_convert_omp_range_for): Likewise. (cp_parser_omp_loop_nest): New, split from cp_parser_omp_for_loop and largely rewritten. Add more comments. (insert_structured_blocks): New. (find_structured_blocks): New. (struct sit_data, substitute_in_tree_walker, substitute_in_tree): New. (fixup_blocks_walker): New. (cp_parser_omp_for_loop): Rewrite to use recursive descent instead of a loop. Add logic to reshuffle the bits of code collected during parsing so intervening code gets moved to the loop body. (cp_parser_omp_loop): Remove call to finish_omp_for_block, which is now redundant. (cp_parser_omp_simd): Likewise. (cp_parser_omp_for): Likewise. (cp_parser_omp_distribute): Likewise. (cp_parser_oacc_loop): Likewise. (cp_parser_omp_taskloop): Likewise. (cp_parser_pragma): Reject OpenMP pragmas in intervening code. * parser.h (struct cp_parser): Add omp_for_parse_state field. * pt.cc (tsubst_omp_for_iterator): Adjust call to cp_convert_omp_range_for. * semantics.cc (finish_omp_for): Try harder to preserve location of loop variable init expression for use in diagnostics. (struct fofb_data, finish_omp_for_block_walker): New. (finish_omp_for_block): Allow variables to be bound in a BIND_EXPR nested inside BIND instead of directly in BIND itself. gcc/testsuite/ChangeLog * c-c++-common/goacc/tile-2.c: Adjust expected error patterns. * g++.dg/gomp/attrs-imperfect1.C: New test. * g++.dg/gomp/attrs-imperfect2.C: New test. * g++.dg/gomp/attrs-imperfect3.C: New test. * g++.dg/gomp/attrs-imperfect4.C: New test. * g++.dg/gomp/attrs-imperfect5.C: New test. * g++.dg/gomp/pr41967.C: Adjust expected error patterns. * g++.dg/gomp/tpl-imperfect-gotos.C: New test. * g++.dg/gomp/tpl-imperfect-invalid-scope.C: New test. libgomp/ChangeLog * testsuite/libgomp.c++/attrs-imperfect1.C: New test. * testsuite/libgomp.c++/attrs-imperfect2.C: New test. * testsuite/libgomp.c++/attrs-imperfect3.C: New test. * testsuite/libgomp.c++/attrs-imperfect4.C: New test. * testsuite/libgomp.c++/attrs-imperfect5.C: New test. * testsuite/libgomp.c++/attrs-imperfect6.C: New test. * testsuite/libgomp.c++/imperfect-class-1.C: New test. * testsuite/libgomp.c++/imperfect-class-2.C: New test. * testsuite/libgomp.c++/imperfect-class-3.C: New test. * testsuite/libgomp.c++/imperfect-destructor.C: New test. * testsuite/libgomp.c++/imperfect-template-1.C: New test. * testsuite/libgomp.c++/imperfect-template-2.C: New test. * testsuite/libgomp.c++/imperfect-template-3.C: New test.
167 lines
4.7 KiB
C
167 lines
4.7 KiB
C
// { dg-do run }
|
|
// Test that class iterators and imperfectly-nested loops work together.
|
|
// This variant tests loop initialization by declaration.
|
|
|
|
typedef __PTRDIFF_TYPE__ ptrdiff_t;
|
|
typedef int T;
|
|
typedef int S;
|
|
|
|
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;
|
|
friend bool operator == (I &, I &);
|
|
friend bool operator == (const I &, const I &);
|
|
friend bool operator < (I &, I &);
|
|
friend bool operator < (const I &, const I &);
|
|
friend bool operator <= (I &, I &);
|
|
friend bool operator <= (const I &, const I &);
|
|
friend bool operator > (I &, I &);
|
|
friend bool operator > (const I &, const I &);
|
|
friend bool operator >= (I &, I &);
|
|
friend bool operator >= (const I &, const I &);
|
|
friend typename I::difference_type operator - (I &, I &);
|
|
friend typename I::difference_type operator - (const I &, const I &);
|
|
friend I operator + (typename I::difference_type , const I &);
|
|
private:
|
|
T *p;
|
|
};
|
|
I::I () : p (0) {}
|
|
I::~I () { p = (T *) 0; }
|
|
I::I (T *x) : p (x) {}
|
|
I::I (const I &x) : p (x.p) {}
|
|
T &I::operator * () { return *p; }
|
|
T *I::operator -> () { return p; }
|
|
T &I::operator [] (const difference_type &x) const { return p[x]; }
|
|
I &I::operator = (const I &x) { p = x.p; return *this; }
|
|
I &I::operator ++ () { ++p; return *this; }
|
|
I I::operator ++ (int) { return I (p++); }
|
|
I &I::operator -- () { --p; return *this; }
|
|
I I::operator -- (int) { return I (p--); }
|
|
I &I::operator += (const difference_type &x) { p += x; return *this; }
|
|
I &I::operator -= (const difference_type &x) { p -= x; return *this; }
|
|
I I::operator + (const difference_type &x) const { return I (p + x); }
|
|
I I::operator - (const difference_type &x) const { return I (p - x); }
|
|
bool operator == (I &x, I &y) { return x.p == y.p; }
|
|
bool operator == (const I &x, const I &y) { return x.p == y.p; }
|
|
bool operator != (I &x, I &y) { return !(x == y); }
|
|
bool operator != (const I &x, const I &y) { return !(x == y); }
|
|
bool operator < (I &x, I &y) { return x.p < y.p; }
|
|
bool operator < (const I &x, const I &y) { return x.p < y.p; }
|
|
bool operator <= (I &x, I &y) { return x.p <= y.p; }
|
|
bool operator <= (const I &x, const I &y) { return x.p <= y.p; }
|
|
bool operator > (I &x, I &y) { return x.p > y.p; }
|
|
bool operator > (const I &x, const I &y) { return x.p > y.p; }
|
|
bool operator >= (I &x, I &y) { return x.p >= y.p; }
|
|
bool operator >= (const I &x, const I &y) { return x.p >= y.p; }
|
|
typename I::difference_type operator - (I &x, I &y) { return x.p - y.p; }
|
|
typename I::difference_type operator - (const I &x, const I &y) { return x.p - y.p; }
|
|
I operator + (typename I::difference_type x, const I &y) { return I (x + y.p); }
|
|
|
|
class J
|
|
{
|
|
public:
|
|
J(const I &x, const I &y) : b (x), e (y) {}
|
|
const I &begin ();
|
|
const I &end ();
|
|
private:
|
|
I b, e;
|
|
};
|
|
|
|
const I &J::begin () { return b; }
|
|
const I &J::end () { return e; }
|
|
|
|
static int f1count[3], f2count[3];
|
|
|
|
#ifndef __cplusplus
|
|
extern void abort (void);
|
|
#else
|
|
extern "C" void abort (void);
|
|
#endif
|
|
|
|
void f1 (int depth)
|
|
{
|
|
f1count[depth]++;
|
|
}
|
|
|
|
void f2 (int depth)
|
|
{
|
|
f2count[depth]++;
|
|
}
|
|
|
|
void s1 (J a1, J a2, J a3)
|
|
{
|
|
#pragma omp for collapse(3)
|
|
for (I i = a1.begin (); i < a1.end (); i++)
|
|
{
|
|
f1 (0);
|
|
for (I j = a2.begin (); j < a2.end (); j++)
|
|
{
|
|
f1 (1);
|
|
for (I k = a3.begin (); k < a3.end (); k++)
|
|
{
|
|
f1 (2);
|
|
f2 (2);
|
|
}
|
|
f2 (1);
|
|
}
|
|
f2 (0);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
main (void)
|
|
{
|
|
|
|
int index[] = {0, 1, 2, 3, 4, 5};
|
|
|
|
J x (&index[0], &index[3]);
|
|
J y (&index[0], &index[4]);
|
|
J z (&index[0], &index[5]);
|
|
|
|
f1count[0] = 0;
|
|
f1count[1] = 0;
|
|
f1count[2] = 0;
|
|
f2count[0] = 0;
|
|
f2count[1] = 0;
|
|
f2count[2] = 0;
|
|
|
|
s1 (x, y, z);
|
|
|
|
/* All intervening code at the same depth must be executed the same
|
|
number of times. */
|
|
if (f1count[0] != f2count[0]) abort ();
|
|
if (f1count[1] != f2count[1]) abort ();
|
|
if (f1count[2] != f2count[2]) abort ();
|
|
|
|
/* Intervening code must be executed at least as many times as the loop
|
|
that encloses it. */
|
|
if (f1count[0] < 3) abort ();
|
|
if (f1count[1] < 3 * 4) abort ();
|
|
|
|
/* Intervening code must not be executed more times than the number
|
|
of logical iterations. */
|
|
if (f1count[0] > 3 * 4 * 5) abort ();
|
|
if (f1count[1] > 3 * 4 * 5) abort ();
|
|
|
|
/* Check that the innermost loop body is executed exactly the number
|
|
of logical iterations expected. */
|
|
if (f1count[2] != 3 * 4 * 5) abort ();
|
|
}
|