Hi! In OpenMP 4.1, C++ references are allowed in {,first,last}private and linear clauses (previously it has been only allowed in reduction), but most of the support code in the middle-end has long been there for invisible references and fortran already.
2015-05-27 Jakub Jelinek <ja...@redhat.com> * omp-low.c (lower_rec_input_clauses): Unshare new_var before passing it to omp_clause_{default,copy}_ctor. gcc/cp/ * cp-gimplify.c (cxx_omp_finish_clause): Don't complain about reference type here. * semantics.c (finish_omp_clauses): Allow references in {{,first,last}private,linear} clauses. gcc/testsuite/ * g++.dg/gomp/task-1.C: Remove both dg-error directives. * g++.dg/gomp/reference-1.C: New test. libgomp/ * testsuite/libgomp.c++/ctor-13.C: New test. * testsuite/libgomp.c++/simd14.C: New test. * testsuite/libgomp.c++/reference-1.C: New test. --- gcc/omp-low.c.jj 2015-05-21 11:12:09.000000000 +0200 +++ gcc/omp-low.c 2015-05-27 13:25:24.934324632 +0200 @@ -4038,7 +4038,8 @@ lower_rec_input_clauses (tree clauses, g x = NULL; do_private: tree nx; - nx = lang_hooks.decls.omp_clause_default_ctor (c, new_var, x); + nx = lang_hooks.decls.omp_clause_default_ctor + (c, unshare_expr (new_var), x); if (is_simd) { tree y = lang_hooks.decls.omp_clause_dtor (c, new_var); @@ -4192,7 +4193,8 @@ lower_rec_input_clauses (tree clauses, g break; } } - x = lang_hooks.decls.omp_clause_copy_ctor (c, new_var, x); + x = lang_hooks.decls.omp_clause_copy_ctor + (c, unshare_expr (new_var), x); gimplify_and_add (x, ilist); goto do_dtor; --- gcc/cp/cp-gimplify.c.jj 2015-05-26 20:36:41.000000000 +0200 +++ gcc/cp/cp-gimplify.c 2015-05-27 14:33:01.690834022 +0200 @@ -1716,16 +1716,7 @@ cxx_omp_finish_clause (tree c, gimple_se if (decl == error_mark_node) make_shared = true; else if (TREE_CODE (TREE_TYPE (decl)) == REFERENCE_TYPE) - { - if (is_invisiref_parm (decl)) - inner_type = TREE_TYPE (inner_type); - else - { - error ("%qE implicitly determined as %<firstprivate%> has reference type", - decl); - make_shared = true; - } - } + inner_type = TREE_TYPE (inner_type); /* We're interested in the base element, not arrays. */ while (TREE_CODE (inner_type) == ARRAY_TYPE) --- gcc/cp/semantics.c.jj 2015-05-26 17:07:46.000000000 +0200 +++ gcc/cp/semantics.c 2015-05-27 16:33:16.153576552 +0200 @@ -5330,14 +5330,19 @@ finish_omp_clauses (tree clauses) goto check_dup_generic; case OMP_CLAUSE_LINEAR: t = OMP_CLAUSE_DECL (c); - if (!type_dependent_expression_p (t) - && !INTEGRAL_TYPE_P (TREE_TYPE (t)) - && TREE_CODE (TREE_TYPE (t)) != POINTER_TYPE) + if (!type_dependent_expression_p (t)) { - error ("linear clause applied to non-integral non-pointer " - "variable with %qT type", TREE_TYPE (t)); - remove = true; - break; + tree type = TREE_TYPE (t); + if (TREE_CODE (type) == REFERENCE_TYPE) + type = TREE_TYPE (type); + if (!INTEGRAL_TYPE_P (type) + && TREE_CODE (type) != POINTER_TYPE) + { + error ("linear clause applied to non-integral non-pointer " + "variable with %qT type", TREE_TYPE (t)); + remove = true; + break; + } } t = OMP_CLAUSE_LINEAR_STEP (c); if (t == NULL_TREE) @@ -5362,14 +5367,16 @@ finish_omp_clauses (tree clauses) if (TREE_CODE (OMP_CLAUSE_DECL (c)) == PARM_DECL) t = maybe_constant_value (t); t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); - if (TREE_CODE (TREE_TYPE (OMP_CLAUSE_DECL (c))) - == POINTER_TYPE) + tree type = TREE_TYPE (OMP_CLAUSE_DECL (c)); + if (TREE_CODE (type) == REFERENCE_TYPE) + type = TREE_TYPE (type); + if (TREE_CODE (type) == POINTER_TYPE) { + tree d = convert_from_reference (OMP_CLAUSE_DECL (c)); t = pointer_int_sum (OMP_CLAUSE_LOCATION (c), PLUS_EXPR, - OMP_CLAUSE_DECL (c), t); + d, t); t = fold_build2_loc (OMP_CLAUSE_LOCATION (c), - MINUS_EXPR, sizetype, t, - OMP_CLAUSE_DECL (c)); + MINUS_EXPR, sizetype, t, d); if (t == error_mark_node) { remove = true; @@ -5377,7 +5384,7 @@ finish_omp_clauses (tree clauses) } } else - t = fold_convert (TREE_TYPE (OMP_CLAUSE_DECL (c)), t); + t = fold_convert (type, t); } OMP_CLAUSE_LINEAR_STEP (c) = t; } @@ -6019,7 +6026,7 @@ finish_omp_clauses (tree clauses) { enum omp_clause_code c_kind = OMP_CLAUSE_CODE (c); bool remove = false; - bool need_complete_non_reference = false; + bool need_complete_type = false; bool need_default_ctor = false; bool need_copy_ctor = false; bool need_copy_assignment = false; @@ -6033,19 +6040,19 @@ finish_omp_clauses (tree clauses) need_implicitly_determined = true; break; case OMP_CLAUSE_PRIVATE: - need_complete_non_reference = true; + need_complete_type = true; need_default_ctor = true; need_dtor = true; need_implicitly_determined = true; break; case OMP_CLAUSE_FIRSTPRIVATE: - need_complete_non_reference = true; + need_complete_type = true; need_copy_ctor = true; need_dtor = true; need_implicitly_determined = true; break; case OMP_CLAUSE_LASTPRIVATE: - need_complete_non_reference = true; + need_complete_type = true; need_copy_assignment = true; need_implicitly_determined = true; break; @@ -6125,18 +6132,14 @@ finish_omp_clauses (tree clauses) break; } - if (need_complete_non_reference || need_copy_assignment) + if (need_complete_type || need_copy_assignment) { t = require_complete_type (t); if (t == error_mark_node) remove = true; else if (TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE - && need_complete_non_reference) - { - error ("%qE has reference type for %qs", t, - omp_clause_code_name[OMP_CLAUSE_CODE (c)]); - remove = true; - } + && !complete_type_or_else (TREE_TYPE (TREE_TYPE (t)), t)) + remove = true; } if (need_implicitly_determined) { @@ -6171,12 +6174,13 @@ finish_omp_clauses (tree clauses) /* We're interested in the base element, not arrays. */ inner_type = type = TREE_TYPE (t); - while (TREE_CODE (inner_type) == ARRAY_TYPE) - inner_type = TREE_TYPE (inner_type); - - if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + if ((need_complete_type + || need_copy_assignment + || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION) && TREE_CODE (inner_type) == REFERENCE_TYPE) inner_type = TREE_TYPE (inner_type); + while (TREE_CODE (inner_type) == ARRAY_TYPE) + inner_type = TREE_TYPE (inner_type); /* Check for special function availability by building a call to one. Save the results, because later we won't be in the right context --- gcc/testsuite/g++.dg/gomp/task-1.C.jj 2015-04-24 12:31:59.000000000 +0200 +++ gcc/testsuite/g++.dg/gomp/task-1.C 2015-05-27 15:27:12.060612616 +0200 @@ -10,8 +10,8 @@ const A a; void foo (A &p) { const A &q = a; -#pragma omp task // { dg-error "has reference type" } +#pragma omp task bar (p); -#pragma omp task // { dg-error "has reference type" } +#pragma omp task bar (q); } --- gcc/testsuite/g++.dg/gomp/reference-1.C.jj 2015-05-27 16:40:39.208545177 +0200 +++ gcc/testsuite/g++.dg/gomp/reference-1.C 2015-05-27 16:40:06.000000000 +0200 @@ -0,0 +1,26 @@ +// { dg-do compile } + +struct S; // { dg-message "forward declaration" } +void foo (S &); + +void +f1 (S &x) // { dg-error "has incomplete type" } +{ +#pragma omp parallel private (x) + foo (x); +} + +void +f2 (S &x) // { dg-error "has incomplete type" } +{ +#pragma omp parallel firstprivate (x) + foo (x); +} + +void +f3 (S &x) // { dg-error "has incomplete type" } +{ +#pragma omp parallel for lastprivate (x) + for (int i = 0; i < 10; i++) + foo (x); +} --- libgomp/testsuite/libgomp.c++/ctor-13.C.jj 2015-05-27 13:26:38.375175340 +0200 +++ libgomp/testsuite/libgomp.c++/ctor-13.C 2015-05-27 13:34:05.000000000 +0200 @@ -0,0 +1,242 @@ +// { dg-do run } + +#include <omp.h> +#include <assert.h> + +struct B +{ + static int ic, dc, xc, ac, cc; + + B(); + B(const B &); + ~B(); + B& operator=(const B &); + void doit(); + static void clear(); +}; + +int B::ic; +int B::dc; +int B::xc; +int B::cc; +int B::ac; + +B::B() +{ + #pragma omp atomic + ic++; +} + +B::~B() +{ + #pragma omp atomic + dc++; +} + +B::B(const B &) +{ + #pragma omp atomic + cc++; +} + +B& B::operator=(const B &) +{ + #pragma omp atomic + ac++; + return *this; +} + +void B::doit() +{ + #pragma omp atomic + xc++; +} + +void B::clear() +{ + ic = 0; + dc = 0; + cc = 0; + ac = 0; + xc = 0; +} + +static int n; + +void f1(B &a) +{ + B b; + B &c = b; + #pragma omp parallel default(none) private(a, c) shared (n) + { + #pragma omp master + n = omp_get_num_threads (); + a.doit(); + c.doit(); + } +} + +void f2(B &a) +{ + B b; + B &c = b; + #pragma omp parallel default(none) firstprivate(a, c) shared(n) + { + #pragma omp master + n = omp_get_num_threads (); + a.doit(); + c.doit(); + } +} + +void f3(B &a) +{ + B b; + B &c = b; + #pragma omp parallel default(none) shared(n, a, c) + { + #pragma omp master + n = omp_get_num_threads (); + #pragma omp for lastprivate (a, c) + for (int i = 0; i < omp_get_num_threads (); i++) + { + a.doit(); + c.doit(); + } + } +} + +void f4() +{ + B b; + B &c = b; + #pragma omp parallel default(none) private (c) shared (n) + { + B d; + B &e = d; + #pragma omp single copyprivate (c, e) + { + c.doit(); + e.doit(); + } + c.doit(); + e.doit(); + } +} + +void f5(B (&a)[2]) +{ + B b[2]; + B (&c)[2] = b; + #pragma omp parallel default(none) private(a, c) shared (n) + { + #pragma omp master + n = omp_get_num_threads (); + a[0].doit(); + a[1].doit(); + c[0].doit(); + c[1].doit(); + } +} + +void f6(B (&a)[2]) +{ + B b[2]; + B (&c)[2] = b; + #pragma omp parallel default(none) firstprivate(a, c) shared (n) + { + #pragma omp master + n = omp_get_num_threads (); + a[0].doit(); + a[1].doit(); + c[0].doit(); + c[1].doit(); + } +} + +void f7(B (&a)[2]) +{ + B b[2]; + B (&c)[2] = b; + #pragma omp parallel default(none) shared(n, a, c) + { + #pragma omp master + n = omp_get_num_threads (); + #pragma omp for lastprivate (a, c) + for (int i = 0; i < omp_get_num_threads (); i++) + { + a[0].doit(); + a[1].doit(); + c[0].doit(); + c[1].doit(); + } + } +} + +void f8() +{ + B b[2]; + B (&c)[2] = b; + #pragma omp parallel default(none) private (c) shared (n) + { + B d[2]; + B (&e)[2] = d; + #pragma omp single copyprivate (c, e) + { + c[0].doit(); + c[1].doit(); + e[0].doit(); + e[1].doit(); + } + c[0].doit(); + c[1].doit(); + e[0].doit(); + e[1].doit(); + } +} + +int main() +{ + { + B a; + f1(a); + } + assert (B::xc == 2*n && B::ic == 2*n+2 && B::dc == 2*n+2 && B::ac == 0 && B::cc == 0); + B::clear(); + { + B a; + f2(a); + } + assert (B::xc == 2*n && B::ic == 2 && B::dc == 2*n+2 && B::ac == 0 && B::cc == 2*n); + B::clear(); + { + B a; + f3(a); + } + assert (B::xc == 2*n && B::ic == 2*n+2 && B::dc == 2*n+2 && B::ac == 2 && B::cc == 0); + B::clear(); + f4(); + assert (B::xc == 2*n+2 && B::ic == 2*n+1 && B::dc == 2*n+1 && B::ac == 2*n-2 && B::cc == 0); + B::clear(); + { + B a[2]; + f5(a); + } + assert (B::xc == 4*n && B::ic == 4*n+4 && B::dc == 4*n+4 && B::ac == 0 && B::cc == 0); + B::clear(); + { + B a[2]; + f6(a); + } + assert (B::xc == 4*n && B::ic == 4 && B::dc == 4*n+4 && B::ac == 0 && B::cc == 4*n); + B::clear(); + { + B a[2]; + f7(a); + } + assert (B::xc == 4*n && B::ic == 4*n+4 && B::dc == 4*n+4 && B::ac == 4 && B::cc == 0); + B::clear(); + f8(); + assert (B::xc == 4*n+4 && B::ic == 4*n+2 && B::dc == 4*n+2 && B::ac == 4*n-4 && B::cc == 0); + return 0; +} --- libgomp/testsuite/libgomp.c++/simd14.C.jj 2015-05-27 14:57:53.683724066 +0200 +++ libgomp/testsuite/libgomp.c++/simd14.C 2015-05-27 14:58:53.270765657 +0200 @@ -0,0 +1,43 @@ +// { dg-do run } +// { dg-options "-O2" } +// { dg-additional-options "-msse2" { target sse2_runtime } } +// { dg-additional-options "-mavx" { target avx_runtime } } + +int a[1024]; +short b[2048]; + +static inline void +bar (int &x, unsigned long long &y, short *&z) +{ + a[x] = x + y + *z; + x++; + y += 17; + z += 2; +} + +__attribute__((noinline, noclone)) int +foo (unsigned long long &s, short *&t) +{ + int i, j = 0; + int &r = j; +#pragma omp parallel for simd linear(r) linear(s:17ULL) linear(t:2) + for (i = 0; i < 1024; i++) + bar (r, s, t); + return j; +} + +int +main () +{ + int i; + for (i = 0; i < 2048; i++) + b[i] = 3 * i; + unsigned long long s = 12; + short *t = b; + int j = foo (s, t); + for (i = 0; i < 1024; i++) + if (a[i] != 12 + 24 * i) + __builtin_abort (); + if (j != 1024 || s != 12 + 1024 * 17ULL || t != &b[2048]) + __builtin_abort (); +} --- libgomp/testsuite/libgomp.c++/reference-1.C.jj 2015-05-27 14:59:53.711797319 +0200 +++ libgomp/testsuite/libgomp.c++/reference-1.C 2015-05-27 15:11:27.286685406 +0200 @@ -0,0 +1,57 @@ +// { dg-do run } + +#include <omp.h> + +__attribute__((noinline, noclone)) void +foo (int &a, short &d, char &g) +{ + unsigned long b = 12; + unsigned long &c = b; + long long e = 21; + long long &f = e; + unsigned int h = 12; + unsigned int &k = h; + #pragma omp parallel default(none) private(a, c) firstprivate(d, f) shared(g, k) + { + int i = omp_get_thread_num (); + a = i; + c = 2 * i; + if (d != 27 || f != 21) + __builtin_abort (); + d = 3 * (i & 0xfff); + f = 4 * i; + #pragma omp barrier + if (a != i || c != 2 * i || d != 3 * (i & 0xfff) || f != 4 * i) + __builtin_abort (); + #pragma omp for lastprivate(g, k) + for (int j = 0; j < 32; j++) + { + g = j; + k = 3 * j; + } + } + if (g != 31 || k != 31 * 3) + __builtin_abort (); + #pragma omp parallel for firstprivate (g, k) lastprivate (g, k) + for (int j = 0; j < 32; j++) + { + if (g != 31 || k != 31 * 3) + __builtin_abort (); + if (j == 31) + { + g = 29; + k = 138; + } + } + if (g != 29 || k != 138) + __builtin_abort (); +} + +int +main () +{ + int a = 5; + short d = 27; + char g = ' '; + foo (a, d, g); +} Jakub