From patchwork Thu Sep 12 08:55:44 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 274469 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id BAE242C0113 for ; Thu, 12 Sep 2013 18:56:10 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:reply-to:references:mime-version :content-type:in-reply-to; q=dns; s=default; b=YeEc78d4R9vS8qYe5 dnlVEyD25Nsr0mNN+j3JSJxuxRQ7I9b4OAD3cTNHoEgIpnvHyKSygajsV8eLYpad lYNdNZnYNpI5iz/Hrfq6wNebeA2OHU14Cm+Z/sLzSyDM61oXZ4xlzK604yK5jjEb UanGvhnylKC1jj9MIe3/JU/6wU= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:reply-to:references:mime-version :content-type:in-reply-to; s=default; bh=3z5fCaEPwDJJpXFcZsg5bdw eEYU=; b=BekErBvWydUpMfoWZ+COFP8gBhFgNP8pwm/KQiwdu9foMv6qXp8fUU5 6oaE6gkVwbevs3UMy4l+8jqbH6MQ47Sf7Iw2YzZ4sqCJGZvUdTrpACmytZWfsUWf KV0VOMnv2klyZJdQoxsUsewk3IH9Zvp6P6fK7j0cFgH3hQPRhieo= Received: (qmail 13495 invoked by alias); 12 Sep 2013 08:55:58 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 13486 invoked by uid 89); 12 Sep 2013 08:55:58 -0000 Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 12 Sep 2013 08:55:58 +0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=AWL, BAYES_50, RP_MATCHES_RCVD, T_FILL_THIS_FORM_SHORT autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from int-mx12.intmail.prod.int.phx2.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id r8C8tnsi002708 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Thu, 12 Sep 2013 04:55:49 -0400 Received: from tucnak.zalov.cz (vpn1-5-29.ams2.redhat.com [10.36.5.29]) by int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id r8C8tjdE006806 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Thu, 12 Sep 2013 04:55:47 -0400 Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.14.7/8.14.7) with ESMTP id r8C8ti1f024274; Thu, 12 Sep 2013 10:55:44 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.14.7/8.14.7/Submit) id r8C8tiFg024273; Thu, 12 Sep 2013 10:55:44 +0200 Date: Thu, 12 Sep 2013 10:55:44 +0200 From: Jakub Jelinek To: Jason Merrill , Richard Henderson Cc: gcc-patches@gcc.gnu.org Subject: [gomp4] C++ OpenMP user defined reductions (take 2) Message-ID: <20130912085544.GS1817@tucnak.redhat.com> Reply-To: Jakub Jelinek References: <20130827165706.GK21876@tucnak.zalov.cz> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20130827165706.GK21876@tucnak.zalov.cz> User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes On Tue, Aug 27, 2013 at 06:57:06PM +0200, Jakub Jelinek wrote: > This is an attempt to implement OpenMP 4.0 user defined reductions. > I got initial 5 questions/issues with the standard answered, and have > another 13 questions/issues pending, this patch implements roughly what I > think should be the resolution, worst case we'll need to tweak it > afterwards. The only thing known to me left out from the implementation > is the handling of derived classes, if no UDR is found for the derived > class, the spec says: > "If the type is a derived class, then any reduction-identifier that matches > its base classes are also a match, if there is no specific match for the > type." > which I can understand how it would work without initializer clause on the > UDR or with initializer (fn_call (args)) form, but can't see how it would > work with initializer (omp_priv initializer) clause. > And, I haven't added tests for accessibility yet. Here is an updated patch, I got the remaining 13 questions answered too (though one has is still fuzzy) and posted 2 further questions for which I've just assumed a resolution. Changes since the last patch: 1) mangled type name is appended to the UDR name, because UDRs for different types are supposed to be independent, UDR with the same reduction-identifier, but for unrelated type is not supposed to hide UDR in some later scope 2) UDRs in classes are parsed before any other pending inline function bodies (so that those bodies can reference those UDRs) - we "inline" the UDRs during finish_omp_clauses already 3) diagnostics had been added for invalid reduction-identifier id-expressions in reduction clause, e.g. reduction (S::~S: something) 4) initializer (omp_priv) and initializer (omp_priv ()) are now rejected 5) base types handled (if lookup using the original type fails, we continue asking for base types) 6) fixed handling of is_invisiref_parm decls in reduction clauses 7) added handling of reference type decls in reduction clauses What is still missing is accessibility checking, in the attached udr5.C I'd assume we should error on the dg-error marked lines (because one of the UDRs is protected and another one is private). Not sure what I'm doing wrong that it doesn't complain. Jason, does this look ok for the branch? 2013-09-12 Jakub Jelinek gcc/ * tree.h (OMP_CLAUSE_REDUCTION_OMP_ORIG_REF): Define. * omp-low.c (lower_rec_simd_input_clauses): Don't set DECL_VALUE_EXPR on new_var if it is not a DECL_P. (lower_rec_input_clauses): Don't force max_vf = 1 if OMP_CLAUSE_REDUCTION_PLACEHOLDER. Add barrier also if any OMP_CLAUSE_REDUCTION_OMP_ORIG_REF is seen. For OMP_CLAUSE_PRIVATE in simd, fix last argument to omp_clause_default_ctor langhook. Handle OMP_CLAUSE_REDUCTION_PLACEHOLDER in simd loops, if OMP_CLAUSE_REDUCTION_GIMPLE_INIT is NULL, emit omp_clause_default_ctor if any and emit omp_clause_dtor if any. Handle C++ references in OMP_CLAUSE_REDUCTION clauses. (lower_reduction_clauses): Adjust comment for UDRs. Handle C++ references in OMP_CLAUSE_REDUCTION clauses. (lower_omp_taskreg): Emit reduction merges before destructors. * tree-pretty-print.c (dump_omp_clause): Don't emit any reduction operator name if OMP_CLAUSE_REDUCTION_CODE is ERROR_MARK. * gimplify.c (omp_add_variable): Ignore GOVD_LOCAL decls for which privatize_by_reference returns true. gcc/cp/ * cp-tree.h (lang_decl_fn): Add omp_declare_reduction_p bitfield. (DECL_OMP_DECLARE_REDUCTION_P): Define. (omp_reduction_id, cp_remove_omp_priv_cleanup_stmt, cp_check_omp_declare_reduction): New prototypes. (cxx_omp_create_clause_info): Add another bool argument. * decl.c (decls_match): For DECL_OMP_DECLARE_REDUCTION_P decls, ignore template and context mismatches. (duplicate_decls): Error out for redeclaration of UDRs. * parser.c (cp_parser_class_specifier_1): Handle UDRs before all other function bodies. (cp_parser_late_parsing_for_member): Handle UDRs. (cp_parser_omp_clause_reduction): Handle UDRs. (cp_parser_omp_declare_reduction_exprs, cp_parser_omp_declare_reduction): New functions. (cp_parser_omp_declare): Uncomment parsing of UDRs. * pt.c (instantiate_class_template_1): Call cp_check_omp_declare_reduction on UDRs. (tsubst_decl): Diagnose UDRs on reference types. (tsubst_omp_clauses): Subst OMP_CLAUSE_REDUCTION_PLACEHOLDER if needed. (tsubst_expr): Handle UDRs. (tsubst_omp_udr): New function. (instantiate_decl): Handle UDRs. * cp-gimplify.c (cp_genericize_r): Handle invisiref parm decls in OMP_CLAUSE_REDUCTION. (cxx_omp_privatize_by_reference): Return true also for decls with REFERENCE_TYPE. (cxx_omp_finish_clause): Adjust cxx_omp_create_clause_info caller. * semantics.c (cxx_omp_create_clause_info): Add need_dtor argument. Use it instead of need_default_ctor || need_copy_ctor for dtor info setup. (omp_reduction_id, omp_reduction_lookup, cp_remove_omp_priv_cleanup_stmt, cp_check_omp_declare_reduction_r, cp_check_omp_declare_reduction, clone_omp_udr, find_omp_placeholder_r): New functions. (struct cp_check_omp_declare_reduction_data): New type. (finish_omp_clauses): Adjust cxx_omp_create_clause_info caller. Handle UDRs. Handle decls with REFERENCE_TYPE type. gcc/fortran/ * trans-openmp.c (gfc_omp_clause_default_ctor, gfc_omp_clause_dtor): Return NULL for OMP_CLAUSE_REDUCTION. gcc/testsuite/ * g++.dg/gomp/clause-3.C: Adjust error messages. * g++.dg/gomp/udr-1.C: New test. * g++.dg/gomp/udr-2.C: New test. * g++.dg/gomp/udr-3.C: New test. * g++.dg/gomp/udr-4.C: New test. libgomp/ * testsuite/libgomp.c++/simd-4.C: New test. * testsuite/libgomp.c++/simd-5.C: New test. * testsuite/libgomp.c++/simd-6.C: New test. * testsuite/libgomp.c++/simd-7.C: New test. * testsuite/libgomp.c++/udr-1.C: New test. * testsuite/libgomp.c++/udr-2.C: New test. * testsuite/libgomp.c++/udr-3.C: New test. * testsuite/libgomp.c++/udr-4.C: New test. * testsuite/libgomp.c++/udr-5.C: New test. * testsuite/libgomp.c++/udr-6.C: New test. * testsuite/libgomp.c++/udr-7.C: New test. * testsuite/libgomp.c++/udr-8.C: New test. Jakub // { dg-do compile } struct S { int s; S () : s (0) {} private: #pragma omp declare reduction (+:S:omp_out.s += omp_in.s) protected: #pragma omp declare reduction (-:S:omp_out.s += omp_in.s) }; struct T : public S { void foo () { S s; #pragma omp parallel reduction (S::operator +:s) // { dg-error "" } s.s = 1; S t; #pragma omp parallel reduction (S::operator -:t) t.s = 1; S u; #pragma omp parallel reduction (+:u) // { dg-error "" } u.s = 1; S v; #pragma omp parallel reduction (-:v) v.s = 1; } }; void foo () { S s; #pragma omp parallel reduction (S::operator +:s) // { dg-error "" } s.s = 1; S t; #pragma omp parallel reduction (S::operator -:t) // { dg-error "" } t.s = 1; } --- gcc/omp-low.c.jj 2013-09-06 16:39:15.256197473 +0200 +++ gcc/omp-low.c 2013-09-11 18:19:39.099359153 +0200 @@ -2870,8 +2870,11 @@ lower_rec_simd_input_clauses (tree new_v NULL_TREE, NULL_TREE); lvar = build4 (ARRAY_REF, TREE_TYPE (new_var), avar, lane, NULL_TREE, NULL_TREE); - SET_DECL_VALUE_EXPR (new_var, lvar); - DECL_HAS_VALUE_EXPR_P (new_var) = 1; + if (DECL_P (new_var)) + { + SET_DECL_VALUE_EXPR (new_var, lvar); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + } return true; } @@ -2887,6 +2890,7 @@ lower_rec_input_clauses (tree clauses, g tree c, dtor, copyin_seq, x, ptr; bool copyin_by_ref = false; bool lastprivate_firstprivate = false; + bool reduction_omp_orig_ref = false; int pass; bool is_simd = (gimple_code (ctx->stmt) == GIMPLE_OMP_FOR && gimple_omp_for_kind (ctx->stmt) == GF_OMP_FOR_KIND_SIMD); @@ -2906,9 +2910,6 @@ lower_rec_input_clauses (tree clauses, g switch (OMP_CLAUSE_CODE (c)) { case OMP_CLAUSE_REDUCTION: - if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) - max_vf = 1; - /* FALLTHRU */ case OMP_CLAUSE_PRIVATE: case OMP_CLAUSE_FIRSTPRIVATE: case OMP_CLAUSE_LASTPRIVATE: @@ -2950,9 +2951,12 @@ lower_rec_input_clauses (tree clauses, g } case OMP_CLAUSE_FIRSTPRIVATE: case OMP_CLAUSE_COPYIN: - case OMP_CLAUSE_REDUCTION: case OMP_CLAUSE_LINEAR: break; + case OMP_CLAUSE_REDUCTION: + if (OMP_CLAUSE_REDUCTION_OMP_ORIG_REF (c)) + reduction_omp_orig_ref = true; + break; case OMP_CLAUSE__LOOPTEMP_: /* Handle _looptemp_ clauses only on parallel. */ if (fd) @@ -3053,10 +3057,7 @@ lower_rec_input_clauses (tree clauses, g allocate new backing storage for the new pointer variable. This allows us to avoid changing all the code that expects a pointer to something that expects - a direct variable. Note that this doesn't apply to - C++, since reference types are disallowed in data - sharing clauses there, except for NRV optimized - return values. */ + a direct variable. */ if (pass == 0) continue; @@ -3142,19 +3143,20 @@ lower_rec_input_clauses (tree clauses, g else x = NULL; do_private: - x = lang_hooks.decls.omp_clause_default_ctor (c, new_var, x); + tree nx; + nx = lang_hooks.decls.omp_clause_default_ctor (c, new_var, x); if (is_simd) { tree y = lang_hooks.decls.omp_clause_dtor (c, new_var); - if ((TREE_ADDRESSABLE (new_var) || x || y + if ((TREE_ADDRESSABLE (new_var) || nx || y || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE) && lower_rec_simd_input_clauses (new_var, ctx, max_vf, idx, lane, ivar, lvar)) { - if (x) + if (nx) x = lang_hooks.decls.omp_clause_default_ctor (c, unshare_expr (ivar), x); - if (x) + if (nx && x) gimplify_and_add (x, &llist[0]); if (y) { @@ -3171,8 +3173,8 @@ lower_rec_input_clauses (tree clauses, g break; } } - if (x) - gimplify_and_add (x, ilist); + if (nx) + gimplify_and_add (nx, ilist); /* FALLTHRU */ do_dtor: @@ -3319,19 +3321,89 @@ lower_rec_input_clauses (tree clauses, g if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) { tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c); + gimple tseq; x = build_outer_var_ref (var, ctx); - /* FIXME: Not handled yet. */ - gcc_assert (!is_simd); - if (is_reference (var)) + if (is_reference (var) + && !useless_type_conversion_p (TREE_TYPE (placeholder), + TREE_TYPE (x))) x = build_fold_addr_expr_loc (clause_loc, x); SET_DECL_VALUE_EXPR (placeholder, x); DECL_HAS_VALUE_EXPR_P (placeholder) = 1; - lower_omp (&OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c), ctx); - gimple_seq_add_seq (ilist, - OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c)); + tree new_vard = new_var; + if (is_reference (var)) + { + gcc_assert (TREE_CODE (new_var) == MEM_REF); + new_vard = TREE_OPERAND (new_var, 0); + gcc_assert (DECL_P (new_vard)); + } + if (is_simd + && lower_rec_simd_input_clauses (new_var, ctx, max_vf, + idx, lane, ivar, lvar)) + { + if (new_vard == new_var) + { + gcc_assert (DECL_VALUE_EXPR (new_var) == lvar); + SET_DECL_VALUE_EXPR (new_var, ivar); + } + else + { + SET_DECL_VALUE_EXPR (new_vard, + build_fold_addr_expr (ivar)); + DECL_HAS_VALUE_EXPR_P (new_vard) = 1; + } + x = lang_hooks.decls.omp_clause_default_ctor + (c, unshare_expr (ivar), + build_outer_var_ref (var, ctx)); + if (x) + gimplify_and_add (x, &llist[0]); + if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c)) + { + tseq = OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c); + lower_omp (&tseq, ctx); + gimple_seq_add_seq (&llist[0], tseq); + } + OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) = NULL; + tseq = OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c); + lower_omp (&tseq, ctx); + gimple_seq_add_seq (&llist[1], tseq); + OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c) = NULL; + DECL_HAS_VALUE_EXPR_P (placeholder) = 0; + if (new_vard == new_var) + SET_DECL_VALUE_EXPR (new_var, lvar); + else + SET_DECL_VALUE_EXPR (new_vard, + build_fold_addr_expr (lvar)); + x = lang_hooks.decls.omp_clause_dtor (c, ivar); + if (x) + { + tseq = NULL; + dtor = x; + gimplify_stmt (&dtor, &tseq); + gimple_seq_add_seq (&llist[1], tseq); + } + break; + } + x = lang_hooks.decls.omp_clause_default_ctor + (c, new_var, unshare_expr (x)); + if (x) + gimplify_and_add (x, ilist); + if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c)) + { + tseq = OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c); + lower_omp (&tseq, ctx); + gimple_seq_add_seq (ilist, tseq); + } OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) = NULL; + if (is_simd) + { + tseq = OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c); + lower_omp (&tseq, ctx); + gimple_seq_add_seq (dlist, tseq); + OMP_CLAUSE_REDUCTION_GIMPLE_MERGE (c) = NULL; + } DECL_HAS_VALUE_EXPR_P (placeholder) = 0; + goto do_dtor; } else { @@ -3431,8 +3503,9 @@ lower_rec_input_clauses (tree clauses, g master thread doesn't modify it before it is copied over in all threads. Similarly for variables in both firstprivate and lastprivate clauses we need to ensure the lastprivate copying - happens after firstprivate copying in all threads. */ - if (copyin_by_ref || lastprivate_firstprivate) + happens after firstprivate copying in all threads. And similarly + for UDRs if initializer expression refers to omp_orig. */ + if (copyin_by_ref || lastprivate_firstprivate || reduction_omp_orig_ref) { /* Don't add any barrier for #pragma omp simd or #pragma omp distribute. */ @@ -3621,7 +3694,7 @@ lower_reduction_clauses (tree clauses, g { if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) { - /* Never use OMP_ATOMIC for array reductions. */ + /* Never use OMP_ATOMIC for array reductions or UDRs. */ count = -1; break; } @@ -3668,7 +3741,9 @@ lower_reduction_clauses (tree clauses, g { tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c); - if (is_reference (var)) + if (is_reference (var) + && !useless_type_conversion_p (TREE_TYPE (placeholder), + TREE_TYPE (ref))) ref = build_fold_addr_expr_loc (clause_loc, ref); SET_DECL_VALUE_EXPR (placeholder, ref); DECL_HAS_VALUE_EXPR_P (placeholder) = 1; @@ -9097,7 +9172,7 @@ lower_omp_taskreg (gimple_stmt_iterator tree child_fn, t; gimple stmt = gsi_stmt (*gsi_p); gimple par_bind, bind; - gimple_seq par_body, olist, ilist, par_olist, par_ilist, new_body; + gimple_seq par_body, olist, ilist, par_olist, par_rlist, par_ilist, new_body; struct gimplify_ctx gctx; location_t loc = gimple_location (stmt); @@ -9125,10 +9200,11 @@ lower_omp_taskreg (gimple_stmt_iterator par_olist = NULL; par_ilist = NULL; + par_rlist = NULL; lower_rec_input_clauses (clauses, &par_ilist, &par_olist, ctx, NULL); lower_omp (&par_body, ctx); if (gimple_code (stmt) == GIMPLE_OMP_PARALLEL) - lower_reduction_clauses (clauses, &par_olist, ctx); + lower_reduction_clauses (clauses, &par_rlist, ctx); /* Declare all the variables created by mapping and the variables declared in the scope of the parallel body. */ @@ -9174,6 +9250,7 @@ lower_omp_taskreg (gimple_stmt_iterator gimple_seq_add_seq (&new_body, par_ilist); gimple_seq_add_seq (&new_body, par_body); + gimple_seq_add_seq (&new_body, par_rlist); gimple_seq_add_seq (&new_body, par_olist); new_body = maybe_catch_exception (new_body); if (ctx->cancellable) --- gcc/tree.h.jj 2013-09-05 09:19:03.309844499 +0200 +++ gcc/tree.h 2013-09-09 17:43:11.241759508 +0200 @@ -632,6 +632,9 @@ struct GTY(()) tree_base { OMP_CLAUSE_MAP_ZERO_BIAS_ARRAY_SECTION in OMP_CLAUSE_MAP + OMP_CLAUSE_REDUCTION_OMP_ORIG_REF in + OMP_CLAUSE_REDUCTION + TRANSACTION_EXPR_RELAXED in TRANSACTION_EXPR @@ -1974,6 +1977,11 @@ extern void protected_set_expr_location #define OMP_CLAUSE_REDUCTION_PLACEHOLDER(NODE) \ OMP_CLAUSE_OPERAND (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_REDUCTION), 3) +/* True if a REDUCTION clause may reference the original list item (omp_orig) + in its OMP_CLAUSE_REDUCTION_{,GIMPLE_}INIT. */ +#define OMP_CLAUSE_REDUCTION_OMP_ORIG_REF(NODE) \ + (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_REDUCTION)->base.public_flag) + /* True if a LINEAR clause doesn't need copy in. True for iterator vars which are always initialized inside of the loop construct, false otherwise. */ #define OMP_CLAUSE_LINEAR_NO_COPYIN(NODE) \ --- gcc/fortran/trans-openmp.c.jj 2013-06-26 12:15:38.000000000 +0200 +++ gcc/fortran/trans-openmp.c 2013-09-11 11:29:56.722018716 +0200 @@ -159,6 +159,9 @@ gfc_omp_clause_default_ctor (tree clause || GFC_TYPE_ARRAY_AKIND (type) != GFC_ARRAY_ALLOCATABLE) return NULL; + if (OMP_CLAUSE_CODE (clause) == OMP_CLAUSE_REDUCTION) + return NULL; + gcc_assert (outer != NULL); gcc_assert (OMP_CLAUSE_CODE (clause) == OMP_CLAUSE_PRIVATE || OMP_CLAUSE_CODE (clause) == OMP_CLAUSE_LASTPRIVATE); @@ -323,6 +326,9 @@ gfc_omp_clause_dtor (tree clause ATTRIBU || GFC_TYPE_ARRAY_AKIND (type) != GFC_ARRAY_ALLOCATABLE) return NULL; + if (OMP_CLAUSE_CODE (clause) == OMP_CLAUSE_REDUCTION) + return NULL; + /* Allocatable arrays in FIRSTPRIVATE/LASTPRIVATE etc. clauses need to be deallocated if they were allocated. */ return gfc_trans_dealloc_allocated (decl, false, NULL); --- gcc/cp/decl.c.jj 2013-08-27 20:50:53.458551851 +0200 +++ gcc/cp/decl.c 2013-09-09 17:43:11.302759290 +0200 @@ -978,12 +978,15 @@ decls_match (tree newdecl, tree olddecl) tree t2 = (DECL_USE_TEMPLATE (olddecl) ? DECL_TI_TEMPLATE (olddecl) : NULL_TREE); - if (t1 != t2) + if (t1 != t2 && !DECL_OMP_DECLARE_REDUCTION_P (newdecl)) return 0; if (CP_DECL_CONTEXT (newdecl) != CP_DECL_CONTEXT (olddecl) && ! (DECL_EXTERN_C_P (newdecl) - && DECL_EXTERN_C_P (olddecl))) + && DECL_EXTERN_C_P (olddecl)) + && ! (DECL_OMP_DECLARE_REDUCTION_P (newdecl) + && DECL_CONTEXT (newdecl) == NULL_TREE + && DECL_CONTEXT (olddecl) == current_function_decl)) return 0; /* A new declaration doesn't match a built-in one unless it @@ -1419,6 +1422,15 @@ duplicate_decls (tree newdecl, tree oldd type = cp_build_type_attribute_variant (type, attribs); TREE_TYPE (newdecl) = TREE_TYPE (olddecl) = type; } + else if (DECL_OMP_DECLARE_REDUCTION_P (olddecl)) + { + gcc_assert (DECL_OMP_DECLARE_REDUCTION_P (newdecl)); + error_at (DECL_SOURCE_LOCATION (newdecl), + "redeclaration of %"); + error_at (DECL_SOURCE_LOCATION (olddecl), + "previous % declaration"); + return error_mark_node; + } /* If a function is explicitly declared "throw ()", propagate that to the corresponding builtin. */ --- gcc/cp/parser.c.jj 2013-09-05 09:19:03.316844461 +0200 +++ gcc/cp/parser.c 2013-09-11 18:36:43.140063780 +0200 @@ -231,6 +231,9 @@ static void cp_parser_initial_pragma static tree cp_literal_operator_id (const char *); +static bool cp_parser_omp_declare_reduction_exprs + (tree, cp_parser *); + /* Manifest constants. */ #define CP_LEXER_BUFFER_SIZE ((256 * 1024) / sizeof (cp_token)) #define CP_SAVED_TOKEN_STACK 5 @@ -19058,8 +19061,19 @@ cp_parser_class_specifier_1 (cp_parser* if (pushed_scope) pop_scope (pushed_scope); /* Now parse the body of the functions. */ - FOR_EACH_VEC_SAFE_ELT (unparsed_funs_with_definitions, ix, decl) - cp_parser_late_parsing_for_member (parser, decl); + if (flag_openmp) + { + /* OpenMP UDRs need to be parsed before all other functions. */ + FOR_EACH_VEC_SAFE_ELT (unparsed_funs_with_definitions, ix, decl) + if (DECL_OMP_DECLARE_REDUCTION_P (decl)) + cp_parser_late_parsing_for_member (parser, decl); + FOR_EACH_VEC_SAFE_ELT (unparsed_funs_with_definitions, ix, decl) + if (!DECL_OMP_DECLARE_REDUCTION_P (decl)) + cp_parser_late_parsing_for_member (parser, decl); + } + else + FOR_EACH_VEC_SAFE_ELT (unparsed_funs_with_definitions, ix, decl) + cp_parser_late_parsing_for_member (parser, decl); vec_safe_truncate (unparsed_funs_with_definitions, 0); } @@ -22972,9 +22986,18 @@ cp_parser_late_parsing_for_member (cp_pa if (processing_template_decl) push_deferring_access_checks (dk_no_check); - /* Now, parse the body of the function. */ - cp_parser_function_definition_after_declarator (parser, - /*inline_p=*/true); + /* #pragma omp declare reduction needs special parsing. */ + if (DECL_OMP_DECLARE_REDUCTION_P (member_function)) + { + parser->lexer->in_pragma = true; + cp_parser_omp_declare_reduction_exprs (member_function, parser); + finish_function (0); + cp_check_omp_declare_reduction (member_function); + } + else + /* Now, parse the body of the function. */ + cp_parser_function_definition_after_declarator (parser, + /*inline_p=*/true); if (processing_template_decl) pop_deferring_access_checks (); @@ -26847,70 +26870,91 @@ cp_parser_omp_clause_ordered (cp_parser OpenMP 3.1: reduction-operator: - One of: + * - & ^ | && || min max */ + One of: + * - & ^ | && || min max + + OpenMP 4.0: + + reduction-operator: + One of: + * - & ^ | && || + id-expression */ static tree cp_parser_omp_clause_reduction (cp_parser *parser, tree list) { - enum tree_code code; - tree nlist, c; + enum tree_code code = ERROR_MARK; + tree nlist, c, id = NULL_TREE; if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) return list; switch (cp_lexer_peek_token (parser->lexer)->type) { - case CPP_PLUS: - code = PLUS_EXPR; - break; - case CPP_MULT: - code = MULT_EXPR; - break; - case CPP_MINUS: - code = MINUS_EXPR; - break; - case CPP_AND: - code = BIT_AND_EXPR; - break; - case CPP_XOR: - code = BIT_XOR_EXPR; - break; - case CPP_OR: - code = BIT_IOR_EXPR; - break; - case CPP_AND_AND: - code = TRUTH_ANDIF_EXPR; - break; - case CPP_OR_OR: - code = TRUTH_ORIF_EXPR; - break; - case CPP_NAME: - { - tree id = cp_lexer_peek_token (parser->lexer)->u.value; - const char *p = IDENTIFIER_POINTER (id); + case CPP_PLUS: code = PLUS_EXPR; break; + case CPP_MULT: code = MULT_EXPR; break; + case CPP_MINUS: code = MINUS_EXPR; break; + case CPP_AND: code = BIT_AND_EXPR; break; + case CPP_XOR: code = BIT_XOR_EXPR; break; + case CPP_OR: code = BIT_IOR_EXPR; break; + case CPP_AND_AND: code = TRUTH_ANDIF_EXPR; break; + case CPP_OR_OR: code = TRUTH_ORIF_EXPR; break; + default: break; + } - if (strcmp (p, "min") == 0) - { + if (code != ERROR_MARK) + cp_lexer_consume_token (parser->lexer); + else + { + bool saved_colon_corrects_to_scope_p; + saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; + parser->colon_corrects_to_scope_p = false; + id = cp_parser_id_expression (parser, /*template_p=*/false, + /*check_dependency_p=*/true, + /*template_p=*/NULL, + /*declarator_p=*/false, + /*optional_p=*/false); + parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; + if (identifier_p (id)) + { + const char *p = IDENTIFIER_POINTER (id); + + if (strcmp (p, "min") == 0) code = MIN_EXPR; - break; - } - if (strcmp (p, "max") == 0) - { + else if (strcmp (p, "max") == 0) code = MAX_EXPR; - break; - } - } - /* FALLTHROUGH */ - default: - cp_parser_error (parser, "expected %<+%>, %<*%>, %<-%>, %<&%>, %<^%>, " - "%<|%>, %<&&%>, %<||%>, % or %"); - resync_fail: - cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, - /*or_comma=*/false, - /*consume_paren=*/true); - return list; + else if (id == ansi_opname (PLUS_EXPR)) + code = PLUS_EXPR; + else if (id == ansi_opname (MULT_EXPR)) + code = MULT_EXPR; + else if (id == ansi_opname (MINUS_EXPR)) + code = MINUS_EXPR; + else if (id == ansi_opname (BIT_AND_EXPR)) + code = BIT_AND_EXPR; + else if (id == ansi_opname (BIT_IOR_EXPR)) + code = BIT_IOR_EXPR; + else if (id == ansi_opname (BIT_XOR_EXPR)) + code = BIT_XOR_EXPR; + else if (id == ansi_opname (TRUTH_ANDIF_EXPR)) + code = TRUTH_ANDIF_EXPR; + else if (id == ansi_opname (TRUTH_ORIF_EXPR)) + code = TRUTH_ORIF_EXPR; + id = omp_reduction_id (code, id, NULL_TREE); + tree scope = parser->scope; + if (scope) + id = build_qualified_name (NULL_TREE, scope, id, false); + parser->scope = NULL_TREE; + parser->qualifying_scope = NULL_TREE; + parser->object_scope = NULL_TREE; + } + else + { + error ("invalid reduction-identifier"); + resync_fail: + cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, + /*or_comma=*/false, + /*consume_paren=*/true); + return list; + } } - cp_lexer_consume_token (parser->lexer); if (!cp_parser_require (parser, CPP_COLON, RT_COLON)) goto resync_fail; @@ -26918,7 +26962,10 @@ cp_parser_omp_clause_reduction (cp_parse nlist = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_REDUCTION, list, NULL); for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c)) - OMP_CLAUSE_REDUCTION_CODE (c) = code; + { + OMP_CLAUSE_REDUCTION_CODE (c) = code; + OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = id; + } return nlist; } @@ -29737,10 +29784,378 @@ cp_parser_omp_end_declare_target (cp_par current_omp_declare_target_attribute--; } +/* Helper function of cp_parser_omp_declare_reduction. Parse the combiner + expression and optional initializer clause of + #pragma omp declare reduction. */ + +static bool +cp_parser_omp_declare_reduction_exprs (tree fndecl, cp_parser *parser) +{ + tree type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl))); + gcc_assert (TREE_CODE (type) == REFERENCE_TYPE); + type = TREE_TYPE (type); + tree omp_out = build_lang_decl (VAR_DECL, get_identifier ("omp_out"), type); + DECL_ARTIFICIAL (omp_out) = 1; + pushdecl (omp_out); + add_decl_expr (omp_out); + tree omp_in = build_lang_decl (VAR_DECL, get_identifier ("omp_in"), type); + DECL_ARTIFICIAL (omp_in) = 1; + pushdecl (omp_in); + add_decl_expr (omp_in); + tree combiner; + tree omp_priv = NULL_TREE, omp_orig = NULL_TREE, initializer = NULL_TREE; + + keep_next_level (true); + tree block = begin_omp_structured_block (); + combiner = cp_parser_expression (parser, false, NULL); + finish_expr_stmt (combiner); + block = finish_omp_structured_block (block); + add_stmt (block); + + if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)) + return false; + + const char *p = ""; + if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) + { + tree id = cp_lexer_peek_token (parser->lexer)->u.value; + p = IDENTIFIER_POINTER (id); + } + + if (strcmp (p, "initializer") == 0) + { + cp_lexer_consume_token (parser->lexer); + if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) + return false; + + p = ""; + if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) + { + tree id = cp_lexer_peek_token (parser->lexer)->u.value; + p = IDENTIFIER_POINTER (id); + } + + omp_priv = build_lang_decl (VAR_DECL, get_identifier ("omp_priv"), type); + DECL_ARTIFICIAL (omp_priv) = 1; + pushdecl (omp_priv); + add_decl_expr (omp_priv); + omp_orig = build_lang_decl (VAR_DECL, get_identifier ("omp_orig"), type); + DECL_ARTIFICIAL (omp_orig) = 1; + pushdecl (omp_orig); + add_decl_expr (omp_orig); + + keep_next_level (true); + block = begin_omp_structured_block (); + + bool ctor = false; + if (strcmp (p, "omp_priv") == 0) + { + bool is_direct_init, is_non_constant_init; + ctor = true; + cp_lexer_consume_token (parser->lexer); + /* Reject initializer (omp_priv) and initializer (omp_priv ()). */ + if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN) + || (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN) + && cp_lexer_peek_nth_token (parser->lexer, 2)->type + == CPP_CLOSE_PAREN + && cp_lexer_peek_nth_token (parser->lexer, 3)->type + == CPP_CLOSE_PAREN)) + { + finish_omp_structured_block (block); + error ("invalid initializer clause"); + return false; + } + initializer = cp_parser_initializer (parser, &is_direct_init, + &is_non_constant_init); + cp_finish_decl (omp_priv, initializer, !is_non_constant_init, + NULL_TREE, LOOKUP_ONLYCONVERTING); + } + else + { + cp_parser_parse_tentatively (parser); + tree fn_name = cp_parser_id_expression (parser, /*template_p=*/false, + /*check_dependency_p=*/true, + /*template_p=*/NULL, + /*declarator_p=*/false, + /*optional_p=*/false); + vec *args; + if (fn_name == error_mark_node + || cp_parser_error_occurred (parser) + || !cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN) + || ((args = cp_parser_parenthesized_expression_list + (parser, non_attr, /*cast_p=*/false, + /*allow_expansion_p=*/true, + /*non_constant_p=*/NULL)), + cp_parser_error_occurred (parser))) + { + finish_omp_structured_block (block); + cp_parser_abort_tentative_parse (parser); + cp_parser_error (parser, "expected id-expression (arguments)"); + return false; + } + unsigned int i; + tree arg; + FOR_EACH_VEC_SAFE_ELT (args, i, arg) + if (arg == omp_priv + || (TREE_CODE (arg) == ADDR_EXPR + && TREE_OPERAND (arg, 0) == omp_priv)) + break; + cp_parser_abort_tentative_parse (parser); + if (arg == NULL_TREE) + error ("one of the initializer call arguments should be %" + " or %<&omp_priv%>"); + initializer = cp_parser_postfix_expression (parser, false, false, false, + false, NULL); + finish_expr_stmt (initializer); + } + + block = finish_omp_structured_block (block); + cp_walk_tree (&block, cp_remove_omp_priv_cleanup_stmt, omp_priv, NULL); + finish_expr_stmt (block); + + if (ctor) + add_decl_expr (omp_orig); + + if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)) + return false; + } + + if (!cp_lexer_next_token_is (parser->lexer, CPP_PRAGMA_EOL)) + cp_parser_required_error (parser, RT_PRAGMA_EOL, /*keyword=*/false); + + return true; +} + +/* OpenMP 4.0 + #pragma omp declare reduction (reduction-id : typename-list : expression) \ + initializer-clause[opt] new-line + + initializer-clause: + initializer (omp_priv initializer) + initializer (function-name (argument-list)) */ + +static void +cp_parser_omp_declare_reduction (cp_parser *parser, cp_token *pragma_tok, + enum pragma_context) +{ + vec types = vNULL; + enum tree_code reduc_code = ERROR_MARK; + tree reduc_id = NULL_TREE, orig_reduc_id = NULL_TREE, type; + unsigned int i; + cp_token *first_token; + cp_token_cache *cp; + int errs; + + if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) + goto fail; + + switch (cp_lexer_peek_token (parser->lexer)->type) + { + case CPP_PLUS: + reduc_code = PLUS_EXPR; + break; + case CPP_MULT: + reduc_code = MULT_EXPR; + break; + case CPP_MINUS: + reduc_code = MINUS_EXPR; + break; + case CPP_AND: + reduc_code = BIT_AND_EXPR; + break; + case CPP_XOR: + reduc_code = BIT_XOR_EXPR; + break; + case CPP_OR: + reduc_code = BIT_IOR_EXPR; + break; + case CPP_AND_AND: + reduc_code = TRUTH_ANDIF_EXPR; + break; + case CPP_OR_OR: + reduc_code = TRUTH_ORIF_EXPR; + break; + case CPP_NAME: + reduc_id = orig_reduc_id = cp_parser_identifier (parser); + break; + default: + cp_parser_error (parser, "expected %<+%>, %<*%>, %<-%>, %<&%>, %<^%>, " + "%<|%>, %<&&%>, %<||%> or identifier"); + goto fail; + } + + if (reduc_code != ERROR_MARK) + cp_lexer_consume_token (parser->lexer); + + reduc_id = omp_reduction_id (reduc_code, reduc_id, NULL_TREE); + if (reduc_id == error_mark_node) + goto fail; + + if (!cp_parser_require (parser, CPP_COLON, RT_COLON)) + goto fail; + + /* Types may not be defined in declare reduction type list. */ + const char *saved_message; + saved_message = parser->type_definition_forbidden_message; + parser->type_definition_forbidden_message + = G_("types may not be defined in declare reduction type list"); + bool saved_colon_corrects_to_scope_p; + saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; + parser->colon_corrects_to_scope_p = false; + + while (true) + { + location_t loc = cp_lexer_peek_token (parser->lexer)->location; + type = cp_parser_type_id (parser); + if (type == error_mark_node) + ; + else if (ARITHMETIC_TYPE_P (type) + && (orig_reduc_id == NULL_TREE + || (TREE_CODE (type) != COMPLEX_TYPE + && (strcmp (IDENTIFIER_POINTER (orig_reduc_id), + "min") == 0 + || strcmp (IDENTIFIER_POINTER (orig_reduc_id), + "max") == 0)))) + error_at (loc, "predeclared arithmetic type in " + "%<#pragma omp declare reduction%>"); + else if (TREE_CODE (type) == FUNCTION_TYPE + || TREE_CODE (type) == METHOD_TYPE + || TREE_CODE (type) == ARRAY_TYPE + || TREE_CODE (type) == REFERENCE_TYPE) + error_at (loc, "function, array or reference type in " + "%<#pragma omp declare reduction%>"); + else if (TYPE_QUALS_NO_ADDR_SPACE (type)) + error_at (loc, "const, volatile or __restrict qualified type in " + "%<#pragma omp declare reduction%>"); + else + types.safe_push (type); + + if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) + cp_lexer_consume_token (parser->lexer); + else + break; + } + + /* Restore the saved message. */ + parser->type_definition_forbidden_message = saved_message; + parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; + + if (!cp_parser_require (parser, CPP_COLON, RT_COLON) + || types.is_empty ()) + { + fail: + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + types.release (); + return; + } + + first_token = cp_lexer_peek_token (parser->lexer); + cp = NULL; + errs = errorcount; + FOR_EACH_VEC_ELT (types, i, type) + { + tree fntype + = build_function_type_list (void_type_node, + cp_build_reference_type (type, false), + NULL_TREE); + tree this_reduc_id = reduc_id; + if (!dependent_type_p (type)) + this_reduc_id = omp_reduction_id (ERROR_MARK, reduc_id, type); + tree fndecl = build_lang_decl (FUNCTION_DECL, this_reduc_id, fntype); + DECL_SOURCE_LOCATION (fndecl) = pragma_tok->location; + DECL_ARTIFICIAL (fndecl) = 1; + DECL_EXTERNAL (fndecl) = 1; + DECL_DECLARED_INLINE_P (fndecl) = 1; + DECL_IGNORED_P (fndecl) = 1; + DECL_OMP_DECLARE_REDUCTION_P (fndecl) = 1; + DECL_ATTRIBUTES (fndecl) + = tree_cons (get_identifier ("gnu_inline"), NULL_TREE, + DECL_ATTRIBUTES (fndecl)); + if (processing_template_decl) + fndecl = push_template_decl (fndecl); + bool block_scope = false; + tree block = NULL_TREE; + if (current_function_decl) + { + block_scope = true; + if (!processing_template_decl) + pushdecl (fndecl); + } + else if (current_class_type) + { + if (cp == NULL) + { + while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL) + && cp_lexer_next_token_is_not (parser->lexer, CPP_EOF)) + cp_lexer_consume_token (parser->lexer); + if (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)) + goto fail; + cp = cp_token_cache_new (first_token, + cp_lexer_peek_nth_token (parser->lexer, + 2)); + } + DECL_STATIC_FUNCTION_P (fndecl) = 1; + finish_member_declaration (fndecl); + DECL_PENDING_INLINE_INFO (fndecl) = cp; + DECL_PENDING_INLINE_P (fndecl) = 1; + vec_safe_push (unparsed_funs_with_definitions, fndecl); + continue; + } + else + { + DECL_CONTEXT (fndecl) = current_namespace; + pushdecl (fndecl); + } + if (!block_scope) + start_preparsed_function (fndecl, NULL_TREE, SF_PRE_PARSED); + else + block = begin_omp_structured_block (); + if (cp) + { + cp_parser_push_lexer_for_tokens (parser, cp); + parser->lexer->in_pragma = true; + } + if (!cp_parser_omp_declare_reduction_exprs (fndecl, parser)) + { + if (!block_scope) + finish_function (0); + else + DECL_CONTEXT (fndecl) = current_function_decl; + if (cp) + cp_parser_pop_lexer (parser); + goto fail; + } + if (cp) + cp_parser_pop_lexer (parser); + if (!block_scope) + finish_function (0); + else + { + DECL_CONTEXT (fndecl) = current_function_decl; + block = finish_omp_structured_block (block); + if (TREE_CODE (block) == BIND_EXPR) + DECL_SAVED_TREE (fndecl) = BIND_EXPR_BODY (block); + else if (TREE_CODE (block) == STATEMENT_LIST) + DECL_SAVED_TREE (fndecl) = block; + if (processing_template_decl) + add_decl_expr (fndecl); + } + cp_check_omp_declare_reduction (fndecl); + if (cp == NULL && types.length () > 1) + cp = cp_token_cache_new (first_token, + cp_lexer_peek_nth_token (parser->lexer, 2)); + if (errs != errorcount) + break; + } + + cp_parser_require_pragma_eol (parser, pragma_tok); + types.release (); +} + /* OpenMP 4.0 #pragma omp declare simd declare-simd-clauses[optseq] new-line #pragma omp declare reduction (reduction-id : typename-list : expression) \ - identity-clause[opt] new-line + initializer-clause[opt] new-line #pragma omp declare target new-line */ static void @@ -29760,13 +30175,13 @@ cp_parser_omp_declare (cp_parser *parser return; } cp_ensure_no_omp_declare_simd (parser); -/* if (strcmp (p, "reduction") == 0) + if (strcmp (p, "reduction") == 0) { cp_lexer_consume_token (parser->lexer); cp_parser_omp_declare_reduction (parser, pragma_tok, context); return; - } */ + } if (strcmp (p, "target") == 0) { cp_lexer_consume_token (parser->lexer); --- gcc/cp/pt.c.jj 2013-08-27 20:50:55.833540151 +0200 +++ gcc/cp/pt.c 2013-09-11 10:18:40.342254675 +0200 @@ -8905,6 +8905,9 @@ instantiate_class_template_1 (tree type) /* Instantiate members marked with attribute used. */ if (r != error_mark_node && DECL_PRESERVE_P (r)) mark_used (r); + if (TREE_CODE (r) == FUNCTION_DECL + && DECL_OMP_DECLARE_REDUCTION_P (r)) + cp_check_omp_declare_reduction (r); } else { @@ -10352,6 +10355,24 @@ tsubst_decl (tree t, tree args, tsubst_f DECL_INITIAL (r) = NULL_TREE; DECL_CONTEXT (r) = ctx; + /* OpenMP UDRs have the only argument a reference to the declared + type. We want to diagnose if the declared type is a reference, + which is invalid, but as references to references are usually + quietly merged, diagnose it here. */ + if (DECL_OMP_DECLARE_REDUCTION_P (t)) + { + tree argtype + = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (t)))); + argtype = tsubst (argtype, args, complain, in_decl); + if (TREE_CODE (argtype) == REFERENCE_TYPE) + error_at (DECL_SOURCE_LOCATION (t), + "function, array or reference type in " + "%<#pragma omp declare reduction%>"); + if (strchr (IDENTIFIER_POINTER (DECL_NAME (t)), '~') == NULL) + DECL_NAME (r) = omp_reduction_id (ERROR_MARK, DECL_NAME (t), + argtype); + } + if (member && DECL_CONV_FN_P (r)) /* Type-conversion operator. Reconstruct the name, in case it's the name of one of the template's parameters. */ @@ -12747,7 +12768,6 @@ tsubst_omp_clauses (tree clauses, bool d case OMP_CLAUSE_PRIVATE: case OMP_CLAUSE_SHARED: case OMP_CLAUSE_FIRSTPRIVATE: - case OMP_CLAUSE_REDUCTION: case OMP_CLAUSE_COPYIN: case OMP_CLAUSE_COPYPRIVATE: case OMP_CLAUSE_IF: @@ -12770,6 +12790,26 @@ tsubst_omp_clauses (tree clauses, bool d = tsubst_expr (OMP_CLAUSE_OPERAND (oc, 0), args, complain, in_decl, /*integral_constant_expression_p=*/false); break; + case OMP_CLAUSE_REDUCTION: + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (oc)) + { + tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (oc); + if (TREE_CODE (placeholder) == SCOPE_REF) + { + tree scope = tsubst (TREE_OPERAND (placeholder, 0), args, + complain, in_decl); + OMP_CLAUSE_REDUCTION_PLACEHOLDER (nc) + = build_qualified_name (NULL_TREE, scope, + TREE_OPERAND (placeholder, 1), + false); + } + else + gcc_assert (identifier_p (placeholder)); + } + OMP_CLAUSE_OPERAND (nc, 0) + = tsubst_expr (OMP_CLAUSE_OPERAND (oc, 0), args, complain, + in_decl, /*integral_constant_expression_p=*/false); + break; case OMP_CLAUSE_LINEAR: case OMP_CLAUSE_ALIGNED: OMP_CLAUSE_OPERAND (nc, 0) @@ -13100,6 +13140,17 @@ tsubst_expr (tree t, tree args, tsubst_f } else if (DECL_IMPLICIT_TYPEDEF_P (t)) /* We already did a pushtag. */; + else if (TREE_CODE (decl) == FUNCTION_DECL + && DECL_OMP_DECLARE_REDUCTION_P (decl) + && DECL_CONTEXT (pattern_decl) + && TREE_CODE (DECL_CONTEXT (pattern_decl)) + == FUNCTION_DECL) + { + DECL_CONTEXT (decl) = NULL_TREE; + pushdecl (decl); + DECL_CONTEXT (decl) = current_function_decl; + cp_check_omp_declare_reduction (decl); + } else { int const_init = false; @@ -13604,6 +13655,72 @@ tsubst_expr (tree t, tree args, tsubst_f #undef RETURN } +/* Instantiate the special body of the artificial DECL_OMP_DECLARE_REDUCTION + function. */ + +static void +tsubst_omp_udr (tree t, tree args, tsubst_flags_t complain, tree in_decl) +{ + if (t == NULL_TREE || t == error_mark_node) + return; + + gcc_assert (TREE_CODE (t) == STATEMENT_LIST); + + tree_stmt_iterator tsi; + int i; + tree stmts[7]; + memset (stmts, 0, sizeof stmts); + for (i = 0, tsi = tsi_start (t); + i < 7 && !tsi_end_p (tsi); + i++, tsi_next (&tsi)) + stmts[i] = tsi_stmt (tsi); + gcc_assert (tsi_end_p (tsi)); + + if (i >= 3) + { + gcc_assert (TREE_CODE (stmts[0]) == DECL_EXPR + && TREE_CODE (stmts[1]) == DECL_EXPR); + tree omp_out = tsubst (DECL_EXPR_DECL (stmts[0]), + args, complain, in_decl); + tree omp_in = tsubst (DECL_EXPR_DECL (stmts[1]), + args, complain, in_decl); + DECL_CONTEXT (omp_out) = current_function_decl; + DECL_CONTEXT (omp_in) = current_function_decl; + keep_next_level (true); + tree block = begin_omp_structured_block (); + tsubst_expr (stmts[2], args, complain, in_decl, false); + block = finish_omp_structured_block (block); + block = maybe_cleanup_point_expr_void (block); + add_decl_expr (omp_out); + if (TREE_NO_WARNING (DECL_EXPR_DECL (stmts[0]))) + TREE_NO_WARNING (omp_out) = 1; + add_decl_expr (omp_in); + finish_expr_stmt (block); + } + if (i >= 6) + { + gcc_assert (TREE_CODE (stmts[3]) == DECL_EXPR + && TREE_CODE (stmts[4]) == DECL_EXPR); + tree omp_priv = tsubst (DECL_EXPR_DECL (stmts[3]), + args, complain, in_decl); + tree omp_orig = tsubst (DECL_EXPR_DECL (stmts[4]), + args, complain, in_decl); + DECL_CONTEXT (omp_priv) = current_function_decl; + DECL_CONTEXT (omp_orig) = current_function_decl; + keep_next_level (true); + tree block = begin_omp_structured_block (); + tsubst_expr (stmts[5], args, complain, in_decl, false); + block = finish_omp_structured_block (block); + block = maybe_cleanup_point_expr_void (block); + cp_walk_tree (&block, cp_remove_omp_priv_cleanup_stmt, omp_priv, NULL); + add_decl_expr (omp_priv); + add_decl_expr (omp_orig); + finish_expr_stmt (block); + if (i == 7) + add_decl_expr (omp_orig); + } +} + /* T is a postfix-expression that is not being used in a function call. Return the substituted version of T. */ @@ -19288,6 +19405,7 @@ instantiate_decl (tree d, int defer_ok, tree subst_decl; tree tmpl_parm; tree spec_parm; + tree block = NULL_TREE; /* Save away the current list, in case we are instantiating one template from within the body of another. */ @@ -19297,7 +19415,11 @@ instantiate_decl (tree d, int defer_ok, local_specializations = pointer_map_create (); /* Set up context. */ - start_preparsed_function (d, NULL_TREE, SF_PRE_PARSED); + if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern) + && TREE_CODE (DECL_CONTEXT (code_pattern)) == FUNCTION_DECL) + block = push_stmt_list (); + else + start_preparsed_function (d, NULL_TREE, SF_PRE_PARSED); /* Some typedefs referenced from within the template code need to be access checked at template instantiation time, i.e now. These @@ -19334,21 +19456,37 @@ instantiate_decl (tree d, int defer_ok, gcc_assert (!spec_parm); /* Substitute into the body of the function. */ - tsubst_expr (DECL_SAVED_TREE (code_pattern), args, - tf_warning_or_error, tmpl, - /*integral_constant_expression_p=*/false); - - /* Set the current input_location to the end of the function - so that finish_function knows where we are. */ - input_location = DECL_STRUCT_FUNCTION (code_pattern)->function_end_locus; + if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern)) + tsubst_omp_udr (DECL_SAVED_TREE (code_pattern), args, + tf_warning_or_error, tmpl); + else + { + tsubst_expr (DECL_SAVED_TREE (code_pattern), args, + tf_warning_or_error, tmpl, + /*integral_constant_expression_p=*/false); + + /* Set the current input_location to the end of the function + so that finish_function knows where we are. */ + input_location + = DECL_STRUCT_FUNCTION (code_pattern)->function_end_locus; + } /* We don't need the local specializations any more. */ pointer_map_destroy (local_specializations); local_specializations = saved_local_specializations; /* Finish the function. */ - d = finish_function (0); - expand_or_defer_fn (d); + if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern) + && TREE_CODE (DECL_CONTEXT (code_pattern)) == FUNCTION_DECL) + DECL_SAVED_TREE (d) = pop_stmt_list (block); + else + { + d = finish_function (0); + expand_or_defer_fn (d); + } + + if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern)) + cp_check_omp_declare_reduction (d); } /* We're not deferring instantiation any more. */ --- gcc/cp/cp-tree.h.jj 2013-08-27 21:01:21.864303729 +0200 +++ gcc/cp/cp-tree.h 2013-09-09 18:11:39.656840266 +0200 @@ -1980,7 +1980,8 @@ struct GTY(()) lang_decl_fn { unsigned thunk_p : 1; unsigned this_thunk_p : 1; unsigned hidden_friend_p : 1; - /* 1 spare bit. */ + unsigned omp_declare_reduction_p : 1; + /* No spare bits on 32-bit hosts, 32 on 64-bit hosts. */ /* For a non-thunk function decl, this is a tree list of friendly classes. For a thunk function decl, it is the @@ -3183,6 +3184,11 @@ more_aggr_init_expr_args_p (const aggr_i #define DECL_HIDDEN_FRIEND_P(NODE) \ (LANG_DECL_FN_CHECK (DECL_COMMON_CHECK (NODE))->hidden_friend_p) +/* Nonzero if NODE is an artificial FUNCTION_DECL for + #pragma omp declare reduction. */ +#define DECL_OMP_DECLARE_REDUCTION_P(NODE) \ + (LANG_DECL_FN_CHECK (DECL_COMMON_CHECK (NODE))->omp_declare_reduction_p) + /* Nonzero if DECL has been declared threadprivate by #pragma omp threadprivate. */ #define CP_DECL_THREADPRIVATE_P(DECL) \ @@ -5777,6 +5783,9 @@ extern tree finish_qualified_id_expr (t extern void simplify_aggr_init_expr (tree *); extern void finalize_nrv (tree *, tree, tree); extern void note_decl_for_pch (tree); +extern tree omp_reduction_id (enum tree_code, tree, tree); +extern tree cp_remove_omp_priv_cleanup_stmt (tree *, int *, void *); +extern void cp_check_omp_declare_reduction (tree); extern tree finish_omp_clauses (tree); extern void finish_omp_threadprivate (tree); extern tree begin_omp_structured_block (void); @@ -5801,7 +5810,8 @@ extern void finish_omp_cancellation_poin extern tree begin_transaction_stmt (location_t, tree *, int); extern void finish_transaction_stmt (tree, tree, int, tree); extern tree build_transaction_expr (location_t, tree, int, tree); -extern bool cxx_omp_create_clause_info (tree, tree, bool, bool, bool); +extern bool cxx_omp_create_clause_info (tree, tree, bool, bool, + bool, bool); extern tree baselink_for_fns (tree); extern void finish_static_assert (tree, tree, location_t, bool); --- gcc/cp/cp-gimplify.c.jj 2013-08-27 20:50:54.802547089 +0200 +++ gcc/cp/cp-gimplify.c 2013-09-11 17:20:57.264555222 +0200 @@ -936,7 +936,16 @@ cp_genericize_r (tree *stmt_p, int *walk *walk_subtrees = 0; break; case OMP_CLAUSE_REDUCTION: - gcc_assert (!is_invisiref_parm (OMP_CLAUSE_DECL (stmt))); + if (is_invisiref_parm (OMP_CLAUSE_DECL (stmt))) + { + *walk_subtrees = 0; + if (OMP_CLAUSE_REDUCTION_INIT (stmt)) + cp_walk_tree (&OMP_CLAUSE_REDUCTION_INIT (stmt), + cp_genericize_r, data, NULL); + if (OMP_CLAUSE_REDUCTION_MERGE (stmt)) + cp_walk_tree (&OMP_CLAUSE_REDUCTION_MERGE (stmt), + cp_genericize_r, data, NULL); + } break; default: break; @@ -1406,7 +1415,8 @@ cxx_omp_clause_dtor (tree clause, tree d bool cxx_omp_privatize_by_reference (const_tree decl) { - return is_invisiref_parm (decl); + return TREE_CODE (TREE_TYPE (decl)) == REFERENCE_TYPE + || is_invisiref_parm (decl); } /* Return true if DECL is const qualified var having no mutable member. */ @@ -1509,7 +1519,7 @@ cxx_omp_finish_clause (tree c) for making these queries. */ if (!make_shared && CLASS_TYPE_P (inner_type) - && cxx_omp_create_clause_info (c, inner_type, false, true, false)) + && cxx_omp_create_clause_info (c, inner_type, false, true, false, true)) make_shared = true; if (make_shared) --- gcc/cp/semantics.c.jj 2013-09-05 15:57:01.106488431 +0200 +++ gcc/cp/semantics.c 2013-09-11 13:02:49.970923223 +0200 @@ -4040,7 +4040,8 @@ finalize_nrv (tree *tp, tree var, tree r bool cxx_omp_create_clause_info (tree c, tree type, bool need_default_ctor, - bool need_copy_ctor, bool need_copy_assignment) + bool need_copy_ctor, bool need_copy_assignment, + bool need_dtor) { int save_errorcount = errorcount; tree info, t; @@ -4064,8 +4065,7 @@ cxx_omp_create_clause_info (tree c, tree TREE_VEC_ELT (info, 0) = t; } - if ((need_default_ctor || need_copy_ctor) - && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)) + if (need_dtor && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)) TREE_VEC_ELT (info, 1) = get_dtor (type, tf_warning_or_error); if (need_copy_assignment) @@ -4534,6 +4534,339 @@ handle_omp_array_sections (tree c) return false; } +/* Return identifier to look up for omp declare reduction. */ + +tree +omp_reduction_id (enum tree_code reduction_code, tree reduction_id, tree type) +{ + const char *p = NULL; + const char *m = NULL; + switch (reduction_code) + { + case PLUS_EXPR: + case MULT_EXPR: + case MINUS_EXPR: + case BIT_AND_EXPR: + case BIT_XOR_EXPR: + case BIT_IOR_EXPR: + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + reduction_id = ansi_opname (reduction_code); + break; + case MIN_EXPR: + p = "min"; + break; + case MAX_EXPR: + p = "max"; + break; + default: + break; + } + + if (p == NULL) + { + if (TREE_CODE (reduction_id) != IDENTIFIER_NODE) + return error_mark_node; + p = IDENTIFIER_POINTER (reduction_id); + } + + if (type != NULL_TREE) + m = mangle_type_string (TYPE_MAIN_VARIANT (type)); + + const char prefix[] = "omp declare reduction "; + size_t lenp = sizeof (prefix); + if (strncmp (p, prefix, lenp - 1) == 0) + lenp = 1; + size_t len = strlen (p); + size_t lenm = m ? strlen (m) + 1 : 0; + char *name = XALLOCAVEC (char, lenp + len + lenm); + if (lenp > 1) + memcpy (name, prefix, lenp - 1); + memcpy (name + lenp - 1, p, len + 1); + if (m) + { + name[lenp + len - 1] = '~'; + memcpy (name + lenp + len, m, lenm); + } + return get_identifier (name); +} + +/* Lookup OpenMP UDR ID for TYPE, return the corresponding artificial + FUNCTION_DECL or NULL_TREE if not found. */ + +static tree +omp_reduction_lookup (location_t loc, tree id, tree type) +{ + tree orig_id = id; + if (identifier_p (id)) + { + cp_id_kind idk; + bool nonint_cst_expression_p; + const char *error_msg; + id = omp_reduction_id (ERROR_MARK, id, type); + tree decl = lookup_name (id); + if (decl == NULL_TREE) + decl = error_mark_node; + id = finish_id_expression (id, decl, NULL_TREE, &idk, false, true, + &nonint_cst_expression_p, false, true, false, + false, &error_msg, loc); + if (idk == CP_ID_KIND_UNQUALIFIED + && identifier_p (id)) + { + vec *args = NULL; + vec_safe_push (args, build_reference_type (type)); + id = perform_koenig_lookup (id, args, false, tf_none); + } + } + else if (TREE_CODE (id) == SCOPE_REF) + id = lookup_qualified_name (TREE_OPERAND (id, 0), + omp_reduction_id (ERROR_MARK, + TREE_OPERAND (id, 1), + type), + false, false); + if (id && is_overloaded_fn (id)) + id = get_fns (id); + for (; id; id = OVL_NEXT (id)) + { + tree fndecl = OVL_CURRENT (id); + if (TREE_CODE (fndecl) == FUNCTION_DECL) + { + tree argtype = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl))); + if (same_type_p (TREE_TYPE (argtype), type)) + break; + } + } + if (id == NULL_TREE && CLASS_TYPE_P (type) && TYPE_BINFO (type)) + { + tree binfo = TYPE_BINFO (type), base_binfo; + unsigned int ix; + for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++) + { + id = omp_reduction_lookup (loc, orig_id, BINFO_TYPE (base_binfo)); + if (id != NULL_TREE) + return id; + } + } + return id; +} + +/* Helper function for cp_parser_omp_declare_reduction_exprs + and tsubst_omp_udr. + Remove CLEANUP_STMT for data (omp_priv variable). + Also append INIT_EXPR for DECL_INITIAL of omp_priv after its + DECL_EXPR. */ + +tree +cp_remove_omp_priv_cleanup_stmt (tree *tp, int *walk_subtrees, void *data) +{ + if (TYPE_P (*tp)) + *walk_subtrees = 0; + else if (TREE_CODE (*tp) == CLEANUP_STMT && CLEANUP_DECL (*tp) == (tree) data) + *tp = CLEANUP_BODY (*tp); + else if (TREE_CODE (*tp) == DECL_EXPR) + { + tree decl = DECL_EXPR_DECL (*tp); + if (!processing_template_decl + && decl == (tree) data + && DECL_INITIAL (decl) + && DECL_INITIAL (decl) != error_mark_node) + { + tree list = NULL_TREE; + append_to_statement_list_force (*tp, &list); + tree init_expr = build2 (INIT_EXPR, void_type_node, + decl, DECL_INITIAL (decl)); + DECL_INITIAL (decl) = NULL_TREE; + append_to_statement_list_force (init_expr, &list); + *tp = list; + } + } + return NULL_TREE; +} + +/* Data passed from cp_check_omp_declare_reduction to + cp_check_omp_declare_reduction_r. */ + +struct cp_check_omp_declare_reduction_data +{ + location_t loc; + tree stmts[7]; + bool combiner_p; +}; + +/* Helper function for cp_check_omp_declare_reduction, called via + cp_walk_tree. */ + +static tree +cp_check_omp_declare_reduction_r (tree *tp, int *, void *data) +{ + struct cp_check_omp_declare_reduction_data *udr_data + = (struct cp_check_omp_declare_reduction_data *) data; + if (SSA_VAR_P (*tp) + && !DECL_ARTIFICIAL (*tp) + && *tp != DECL_EXPR_DECL (udr_data->stmts[udr_data->combiner_p ? 0 : 3]) + && *tp != DECL_EXPR_DECL (udr_data->stmts[udr_data->combiner_p ? 1 : 4])) + { + location_t loc = udr_data->loc; + if (udr_data->combiner_p) + error_at (loc, "%<#pragma omp declare reduction%> combiner refers to " + "variable %qD which is not % nor %", + *tp); + else + error_at (loc, "%<#pragma omp declare reduction%> initializer refers " + "to variable %qD which is not % nor " + "%", + *tp); + return *tp; + } + return NULL_TREE; +} + +/* Diagnose violation of OpenMP #pragma omp declare reduction restrictions. */ + +void +cp_check_omp_declare_reduction (tree udr) +{ + tree type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (udr))); + gcc_assert (TREE_CODE (type) == REFERENCE_TYPE); + type = TREE_TYPE (type); + int i; + location_t loc = DECL_SOURCE_LOCATION (udr); + + if (type == error_mark_node) + return; + if (ARITHMETIC_TYPE_P (type)) + { + static enum tree_code predef_codes[] + = { PLUS_EXPR, MULT_EXPR, MINUS_EXPR, BIT_AND_EXPR, BIT_XOR_EXPR, + BIT_IOR_EXPR, TRUTH_ANDIF_EXPR, TRUTH_ORIF_EXPR }; + for (i = 0; i < 8; i++) + { + tree id = omp_reduction_id (predef_codes[i], NULL_TREE, NULL_TREE); + const char *n1 = IDENTIFIER_POINTER (DECL_NAME (udr)); + const char *n2 = IDENTIFIER_POINTER (id); + if (strncmp (n1, n2, IDENTIFIER_LENGTH (id)) == 0 + && (n1[IDENTIFIER_LENGTH (id)] == '~' + || n1[IDENTIFIER_LENGTH (id)] == '\0')) + break; + } + + if (i == 8 + && TREE_CODE (type) != COMPLEX_EXPR) + { + const char prefix_minmax[] = "omp declare reduction m"; + size_t prefix_size = sizeof (prefix_minmax) - 1; + const char *n = IDENTIFIER_POINTER (DECL_NAME (udr)); + if (strncmp (IDENTIFIER_POINTER (DECL_NAME (udr)), + prefix_minmax, prefix_size) == 0 + && ((n[prefix_size] == 'i' && n[prefix_size + 1] == 'n') + || (n[prefix_size] == 'a' && n[prefix_size + 1] == 'x')) + && (n[prefix_size + 2] == '~' || n[prefix_size + 2] == '\0')) + i = 0; + } + if (i < 8) + { + error_at (loc, "predeclared arithmetic type in " + "%<#pragma omp declare reduction%>"); + return; + } + } + else if (TREE_CODE (type) == FUNCTION_TYPE + || TREE_CODE (type) == METHOD_TYPE + || TREE_CODE (type) == ARRAY_TYPE + || TREE_CODE (type) == REFERENCE_TYPE) + { + error_at (loc, "function, array or reference type in " + "%<#pragma omp declare reduction%>"); + return; + } + else if (TYPE_QUALS_NO_ADDR_SPACE (type)) + { + error_at (loc, "const, volatile or __restrict qualified type in " + "%<#pragma omp declare reduction%>"); + return; + } + + tree body = DECL_SAVED_TREE (udr); + if (body == NULL_TREE || TREE_CODE (body) != STATEMENT_LIST) + return; + + tree_stmt_iterator tsi; + struct cp_check_omp_declare_reduction_data data; + memset (data.stmts, 0, sizeof data.stmts); + for (i = 0, tsi = tsi_start (body); + i < 7 && !tsi_end_p (tsi); + i++, tsi_next (&tsi)) + data.stmts[i] = tsi_stmt (tsi); + data.loc = loc; + gcc_assert (tsi_end_p (tsi)); + if (i >= 3) + { + gcc_assert (TREE_CODE (data.stmts[0]) == DECL_EXPR + && TREE_CODE (data.stmts[1]) == DECL_EXPR); + if (TREE_NO_WARNING (DECL_EXPR_DECL (data.stmts[0]))) + return; + data.combiner_p = true; + if (cp_walk_tree (&data.stmts[2], cp_check_omp_declare_reduction_r, + &data, NULL)) + TREE_NO_WARNING (DECL_EXPR_DECL (data.stmts[0])) = 1; + } + if (i >= 6) + { + gcc_assert (TREE_CODE (data.stmts[3]) == DECL_EXPR + && TREE_CODE (data.stmts[4]) == DECL_EXPR); + data.combiner_p = false; + if (cp_walk_tree (&data.stmts[5], cp_check_omp_declare_reduction_r, + &data, NULL) + || cp_walk_tree (&DECL_INITIAL (DECL_EXPR_DECL (data.stmts[3])), + cp_check_omp_declare_reduction_r, &data, NULL)) + TREE_NO_WARNING (DECL_EXPR_DECL (data.stmts[0])) = 1; + if (i == 7) + gcc_assert (TREE_CODE (data.stmts[6]) == DECL_EXPR); + } +} + +/* Helper function of finish_omp_clauses. Clone STMT as if we were making + an inline call. But, remap + the OMP_DECL1 VAR_DECL (omp_out resp. omp_orig) to PLACEHOLDER + and OMP_DECL2 VAR_DECL (omp_in resp. omp_priv) to DECL. */ + +static tree +clone_omp_udr (tree stmt, tree omp_decl1, tree omp_decl2, + tree decl, tree placeholder) +{ + copy_body_data id; + struct pointer_map_t *decl_map = pointer_map_create (); + + *pointer_map_insert (decl_map, omp_decl1) = placeholder; + *pointer_map_insert (decl_map, omp_decl2) = decl; + memset (&id, 0, sizeof (id)); + id.src_fn = DECL_CONTEXT (omp_decl1); + id.dst_fn = current_function_decl; + id.src_cfun = DECL_STRUCT_FUNCTION (id.src_fn); + id.decl_map = decl_map; + + id.copy_decl = copy_decl_no_change; + id.transform_call_graph_edges = CB_CGE_DUPLICATE; + id.transform_new_cfg = true; + id.transform_return_to_modify = false; + id.transform_lang_insert_block = NULL; + id.eh_lp_nr = 0; + walk_tree (&stmt, copy_tree_body_r, &id, NULL); + pointer_map_destroy (decl_map); + return stmt; +} + +/* Helper function of finish_omp_clauses, called via cp_walk_tree. + Find OMP_CLAUSE_PLACEHOLDER (passed in DATA) in *TP. */ + +static tree +find_omp_placeholder_r (tree *tp, int *, void *data) +{ + if (*tp == (tree) data) + return *tp; + return NULL_TREE; +} + /* For all elements of CLAUSES, validate them vs OpenMP constraints. Remove any elements from the list that are invalid. */ @@ -5059,6 +5392,7 @@ finish_omp_clauses (tree clauses) bool need_copy_ctor = false; bool need_copy_assignment = false; bool need_implicitly_determined = false; + bool need_dtor = false; tree type, inner_type; switch (c_kind) @@ -5071,12 +5405,14 @@ finish_omp_clauses (tree clauses) name = "private"; need_complete_non_reference = true; need_default_ctor = true; + need_dtor = true; need_implicitly_determined = true; break; case OMP_CLAUSE_FIRSTPRIVATE: name = "firstprivate"; need_complete_non_reference = true; need_copy_ctor = true; + need_dtor = true; need_implicitly_determined = true; break; case OMP_CLAUSE_LASTPRIVATE: @@ -5124,34 +5460,228 @@ finish_omp_clauses (tree clauses) { case OMP_CLAUSE_LASTPRIVATE: if (!bitmap_bit_p (&firstprivate_head, DECL_UID (t))) - need_default_ctor = true; + { + need_default_ctor = true; + need_dtor = true; + } break; case OMP_CLAUSE_REDUCTION: - if (AGGREGATE_TYPE_P (TREE_TYPE (t)) - || POINTER_TYPE_P (TREE_TYPE (t))) - { - error ("%qE has invalid type for %", t); - remove = true; - } - else if (FLOAT_TYPE_P (TREE_TYPE (t))) - { - enum tree_code r_code = OMP_CLAUSE_REDUCTION_CODE (c); - switch (r_code) + { + bool predefined = false; + tree type = TREE_TYPE (t); + if (TREE_CODE (type) == REFERENCE_TYPE) + type = TREE_TYPE (type); + if (ARITHMETIC_TYPE_P (type)) + switch (OMP_CLAUSE_REDUCTION_CODE (c)) { case PLUS_EXPR: case MULT_EXPR: case MINUS_EXPR: + predefined = true; + break; case MIN_EXPR: case MAX_EXPR: + if (TREE_CODE (type) == COMPLEX_TYPE) + break; + predefined = true; + break; + case BIT_AND_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + if (FLOAT_TYPE_P (type)) + break; + predefined = true; break; default: - error ("%qE has invalid type for %", - t, operator_name_info[r_code].name); - remove = true; + break; } - } - break; + else if (TREE_CODE (type) == ARRAY_TYPE + || TYPE_READONLY (type)) + { + error ("%qE has invalid type for %", t); + remove = true; + break; + } + else if (!processing_template_decl) + { + t = require_complete_type (t); + if (t == error_mark_node) + { + remove = true; + break; + } + } + if (predefined) + { + OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = NULL_TREE; + } + else if (!processing_template_decl) + { + tree id = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c); + + type = TYPE_MAIN_VARIANT (TREE_TYPE (t)); + if (TREE_CODE (type) == REFERENCE_TYPE) + type = TREE_TYPE (type); + OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = NULL_TREE; + if (id == NULL_TREE) + id = omp_reduction_id (OMP_CLAUSE_REDUCTION_CODE (c), + NULL_TREE, NULL_TREE); + id = omp_reduction_lookup (OMP_CLAUSE_LOCATION (c), id, type); + if (id) + { + id = OVL_CURRENT (id); + if (DECL_TEMPLATE_INFO (id)) + id = instantiate_decl (id, /*defer_ok*/0, true); + tree body = DECL_SAVED_TREE (id); + if (TREE_CODE (body) == STATEMENT_LIST) + { + tree_stmt_iterator tsi; + tree placeholder = NULL_TREE; + int i; + tree stmts[7]; + tree atype + = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (id))); + atype = TREE_TYPE (atype); + bool need_static_cast = !same_type_p (type, atype); + memset (stmts, 0, sizeof stmts); + for (i = 0, tsi = tsi_start (body); + i < 7 && !tsi_end_p (tsi); + i++, tsi_next (&tsi)) + stmts[i] = tsi_stmt (tsi); + gcc_assert (tsi_end_p (tsi)); + if (i >= 3) + { + gcc_assert (TREE_CODE (stmts[0]) == DECL_EXPR + && TREE_CODE (stmts[1]) == DECL_EXPR); + placeholder + = build_lang_decl (VAR_DECL, NULL_TREE, type); + DECL_ARTIFICIAL (placeholder) = 1; + DECL_IGNORED_P (placeholder) = 1; + OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = placeholder; + if (TREE_ADDRESSABLE (DECL_EXPR_DECL (stmts[0]))) + cxx_mark_addressable (placeholder); + if (TREE_ADDRESSABLE (DECL_EXPR_DECL (stmts[1])) + && TREE_CODE (TREE_TYPE (OMP_CLAUSE_DECL (c))) + != REFERENCE_TYPE) + cxx_mark_addressable (OMP_CLAUSE_DECL (c)); + tree omp_out = placeholder; + tree omp_in + = convert_from_reference (OMP_CLAUSE_DECL (c)); + if (need_static_cast) + { + tree ptype = build_pointer_type (atype); + omp_out = build_fold_addr_expr (omp_out); + omp_out + = build_static_cast (ptype, omp_out, + tf_warning_or_error); + omp_in = build_fold_addr_expr (omp_in); + omp_in + = build_static_cast (ptype, omp_in, + tf_warning_or_error); + if (omp_out == error_mark_node + || omp_in == error_mark_node) + { + remove = true; + break; + } + omp_out + = build1 (INDIRECT_REF, atype, omp_out); + omp_in + = build1 (INDIRECT_REF, atype, omp_in); + } + OMP_CLAUSE_REDUCTION_MERGE (c) + = clone_omp_udr (stmts[2], + DECL_EXPR_DECL (stmts[0]), + DECL_EXPR_DECL (stmts[1]), + omp_in, omp_out); + } + if (i >= 6) + { + gcc_assert (TREE_CODE (stmts[3]) == DECL_EXPR + && TREE_CODE (stmts[4]) == DECL_EXPR); + if (TREE_ADDRESSABLE (DECL_EXPR_DECL (stmts[3]))) + cxx_mark_addressable (OMP_CLAUSE_DECL (c)); + if (TREE_ADDRESSABLE (DECL_EXPR_DECL (stmts[4]))) + cxx_mark_addressable (placeholder); + tree omp_priv + = convert_from_reference (OMP_CLAUSE_DECL (c)); + tree omp_orig = placeholder; + if (need_static_cast) + { + if (i == 7) + { + error_at (OMP_CLAUSE_LOCATION (c), + "user defined reduction with " + "constructor initializer for " + "base class %qT", atype); + remove = true; + break; + } + tree ptype = build_pointer_type (atype); + omp_priv = build_fold_addr_expr (omp_priv); + omp_priv + = build_static_cast (ptype, omp_priv, + tf_warning_or_error); + omp_orig = build_fold_addr_expr (omp_orig); + omp_orig + = build_static_cast (ptype, omp_orig, + tf_warning_or_error); + if (omp_priv == error_mark_node + || omp_orig == error_mark_node) + { + remove = true; + break; + } + omp_priv + = build1 (INDIRECT_REF, atype, omp_priv); + omp_orig + = build1 (INDIRECT_REF, atype, omp_orig); + } + if (i == 6) + need_default_ctor = true; + OMP_CLAUSE_REDUCTION_INIT (c) + = clone_omp_udr (stmts[5], + DECL_EXPR_DECL (stmts[4]), + DECL_EXPR_DECL (stmts[3]), + omp_priv, omp_orig); + if (cp_walk_tree (&OMP_CLAUSE_REDUCTION_INIT (c), + find_omp_placeholder_r, + placeholder, NULL)) + OMP_CLAUSE_REDUCTION_OMP_ORIG_REF (c) = 1; + } + else if (i >= 3) + { + if (CLASS_TYPE_P (type) && !pod_type_p (type)) + need_default_ctor = true; + else + { + tree init; + tree v = convert_from_reference (t); + if (AGGREGATE_TYPE_P (TREE_TYPE (v))) + init = build_constructor (TREE_TYPE (v), + NULL); + else + init = fold_convert (TREE_TYPE (v), + integer_zero_node); + OMP_CLAUSE_REDUCTION_INIT (c) + = build2 (INIT_EXPR, TREE_TYPE (v), v, init); + } + } + } + } + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + need_dtor = true; + else + { + error ("user defined reduction not found for %qD", t); + remove = true; + } + } + break; + } case OMP_CLAUSE_COPYIN: if (!VAR_P (t) || !DECL_THREAD_LOCAL_P (t)) @@ -5213,15 +5743,21 @@ finish_omp_clauses (tree clauses) while (TREE_CODE (inner_type) == ARRAY_TYPE) inner_type = TREE_TYPE (inner_type); + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION + && TREE_CODE (inner_type) == REFERENCE_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 for making these queries. */ if (CLASS_TYPE_P (inner_type) && COMPLETE_TYPE_P (inner_type) - && (need_default_ctor || need_copy_ctor || need_copy_assignment) + && (need_default_ctor || need_copy_ctor + || need_copy_assignment || need_dtor) && !type_dependent_expression_p (t) && cxx_omp_create_clause_info (c, inner_type, need_default_ctor, - need_copy_ctor, need_copy_assignment)) + need_copy_ctor, need_copy_assignment, + need_dtor)) remove = true; if (remove) --- gcc/tree-pretty-print.c.jj 2013-09-09 17:43:11.278759395 +0200 +++ gcc/tree-pretty-print.c 2013-09-09 17:44:27.323364451 +0200 @@ -332,8 +332,12 @@ dump_omp_clause (pretty_printer *buffer, case OMP_CLAUSE_REDUCTION: pp_string (buffer, "reduction("); - pp_string (buffer, op_symbol_code (OMP_CLAUSE_REDUCTION_CODE (clause))); - pp_colon (buffer); + if (OMP_CLAUSE_REDUCTION_CODE (clause) != ERROR_MARK) + { + pp_string (buffer, + op_symbol_code (OMP_CLAUSE_REDUCTION_CODE (clause))); + pp_colon (buffer); + } dump_generic_node (buffer, OMP_CLAUSE_DECL (clause), spc, flags, false); pp_right_paren (buffer); --- gcc/gimplify.c.jj 2013-09-06 14:47:13.000000000 +0200 +++ gcc/gimplify.c 2013-09-11 17:18:39.944280480 +0200 @@ -5894,10 +5894,9 @@ omp_add_variable (struct gimplify_omp_ct && DECL_P (TYPE_SIZE_UNIT (TREE_TYPE (decl)))) omp_notice_variable (ctx, TYPE_SIZE_UNIT (TREE_TYPE (decl)), true); } - else if ((flags & GOVD_MAP) == 0 + else if ((flags & (GOVD_MAP | GOVD_LOCAL)) == 0 && lang_hooks.decls.omp_privatize_by_reference (decl)) { - gcc_assert ((flags & GOVD_LOCAL) == 0); omp_firstprivatize_type_sizes (ctx, TREE_TYPE (decl)); /* Similar to the direct variable sized case above, we'll need the --- gcc/testsuite/g++.dg/gomp/udr-1.C.jj 2013-09-09 17:43:11.367758947 +0200 +++ gcc/testsuite/g++.dg/gomp/udr-1.C 2013-09-09 17:43:11.367758947 +0200 @@ -0,0 +1,119 @@ +// { dg-do compile } +// { dg-options "-fopenmp" } + +namespace N1 +{ + #pragma omp declare reduction (| : long int : omp_out |= omp_in) // { dg-error "predeclared arithmetic type" } + #pragma omp declare reduction (+ : char : omp_out += omp_in) // { dg-error "predeclared arithmetic type" } + typedef short T; + #pragma omp declare reduction (min : T : omp_out += omp_in) // { dg-error "predeclared arithmetic type" } + #pragma omp declare reduction (* : _Complex double : omp_out *= omp_in)// { dg-error "predeclared arithmetic type" } +} +namespace N2 +{ + template + struct S + { + #pragma omp declare reduction (| : T1 : omp_out |= omp_in) // { dg-error "predeclared arithmetic type" } + #pragma omp declare reduction (+ : T2 : omp_out += omp_in) // { dg-error "predeclared arithmetic type" } + typedef T3 T; + #pragma omp declare reduction (min : T : omp_out += omp_in) // { dg-error "predeclared arithmetic type" } + #pragma omp declare reduction (* : T4 : omp_out *= omp_in) // { dg-error "predeclared arithmetic type" } + }; + S s; + template + int foo () + { + #pragma omp declare reduction (| : T1 : omp_out |= omp_in) // { dg-error "predeclared arithmetic type" } + #pragma omp declare reduction (+ : T2 : omp_out += omp_in) // { dg-error "predeclared arithmetic type" } + typedef T3 T; + #pragma omp declare reduction (min : T : omp_out += omp_in) // { dg-error "predeclared arithmetic type" } + #pragma omp declare reduction (* : T4 : omp_out *= omp_in) // { dg-error "predeclared arithmetic type" } + return 0; + } + int x = foo (); +} +namespace N3 +{ + void bar (); + #pragma omp declare reduction (| : __typeof (bar) : omp_out |= omp_in)// { dg-error "function, array or reference" } + #pragma omp declare reduction (+ : char () : omp_out += omp_in) // { dg-error "function, array or reference" } + typedef short T; + #pragma omp declare reduction (min : T[2] : omp_out += omp_in) // { dg-error "function, array or reference" } + #pragma omp declare reduction (baz : char & : omp_out *= omp_in) // { dg-error "function, array or reference" } +} +namespace N4 +{ + void bar (); + template + struct S + { + #pragma omp declare reduction (| : T1 : omp_out |= omp_in) // { dg-error "function, array or reference" } + #pragma omp declare reduction (+ : T2 : omp_out += omp_in) // { dg-error "function, array or reference" } + typedef T3 T; + #pragma omp declare reduction (min : T : omp_out += omp_in) // { dg-error "function, array or reference" } + #pragma omp declare reduction (baz : T4 : omp_out *= omp_in) // { dg-error "function, array or reference" } + }; + S<__typeof (bar), char (), short [3], char []> s; + template + int foo () + { + #pragma omp declare reduction (| : T1 : omp_out |= omp_in) // { dg-error "function, array or reference" } + #pragma omp declare reduction (+ : T2 : omp_out += omp_in) // { dg-error "function, array or reference" } + typedef T3 T; + #pragma omp declare reduction (min : T : omp_out += omp_in) // { dg-error "function, array or reference" } + #pragma omp declare reduction (baz : T4 : omp_out *= omp_in) // { dg-error "function, array or reference" } + return 0; + } + int x = foo <__typeof (bar), char (), short[], char [2]> (); +} +namespace N5 +{ + template + struct S + { + #pragma omp declare reduction (baz : T : omp_out *= omp_in) // { dg-error "function, array or reference" } + }; + S s; + template + int foo () + { + #pragma omp declare reduction (baz : T : omp_out *= omp_in) // { dg-error "function, array or reference" } + return 0; + } + int x = foo (); +} +namespace N6 +{ + struct A { int a; A () : a (0) {} }; + #pragma omp declare reduction (| : const A : omp_out.a |= omp_in.a) // { dg-error "const, volatile or __restrict" } + #pragma omp declare reduction (+ : __const A : omp_out.a += omp_in.a) // { dg-error "const, volatile or __restrict" } + typedef volatile A T; + #pragma omp declare reduction (min : T : omp_out.a += omp_in.a) // { dg-error "const, volatile or __restrict" } + #pragma omp declare reduction (* : A *__restrict : omp_out->a *= omp_in->a)// { dg-error "const, volatile or __restrict" } +} +namespace N7 +{ + struct A { int a; A () : a (0) {} }; + template + struct S + { + #pragma omp declare reduction (| : T1 : omp_out |= omp_in) // { dg-error "const, volatile or __restrict" } + #pragma omp declare reduction (+ : T2 : omp_out += omp_in) // { dg-error "const, volatile or __restrict" } + typedef T3 T; + #pragma omp declare reduction (min : T : omp_out += omp_in) // { dg-error "const, volatile or __restrict" } + #pragma omp declare reduction (* : T4 : omp_out *= omp_in) // { dg-error "const, volatile or __restrict" } + }; + S s; + template + int foo () + { + #pragma omp declare reduction (| : T1 : omp_out |= omp_in) // { dg-error "const, volatile or __restrict" } + #pragma omp declare reduction (+ : T2 : omp_out += omp_in) // { dg-error "const, volatile or __restrict" } + typedef T3 T; + #pragma omp declare reduction (min : T : omp_out += omp_in) // { dg-error "const, volatile or __restrict" } + #pragma omp declare reduction (* : T4 : omp_out *= omp_in) // { dg-error "const, volatile or __restrict" } + return 0; + } + int x = foo (); +} --- gcc/testsuite/g++.dg/gomp/udr-3.C.jj 2013-09-09 17:43:11.367758947 +0200 +++ gcc/testsuite/g++.dg/gomp/udr-3.C 2013-09-11 12:25:26.000000000 +0200 @@ -0,0 +1,191 @@ +// { dg-do compile } +// { dg-options "-fopenmp" } + +struct S { int s; S () : s (0) {} S (int x) : s (x) {} ~S () {} }; +struct T { int t; T () : t (0) {} T (int x) : t (x) {} ~T () {} }; + +#pragma omp declare reduction (+: ::S: omp_out.s += omp_in.s) +#pragma omp declare reduction (*: S: omp_out.s *= omp_in.s) \ + initializer (omp_priv (1)) +#pragma omp declare reduction (foo: S: omp_out.s += omp_in.s) + +void +f1 () +{ + S s, s2; + T t; + #pragma omp declare reduction (+: T: omp_out.t += omp_in.t) + #pragma omp parallel reduction (+: t) reduction (foo: s) reduction (*: s2) + s.s = 1, t.t = 1, s2.s = 2; + #pragma omp parallel reduction (::operator +: s) + s.s = 1; + #pragma omp parallel reduction (+: s) + s.s = 1; +} + +template +int +f2 () +{ + S s, s2; + T t; + #pragma omp declare reduction (+: T: omp_out.t += omp_in.t) + #pragma omp parallel reduction (+: t) reduction (foo: s) reduction (*: s2) + s.s = 1, t.t = 1, s2.s = 2; + #pragma omp parallel reduction (::operator +: s) + s.s = 1; + #pragma omp parallel reduction (+: s) + s.s = 1; + return 0; +} + +int x = f2<0> (); + +void bar (S &); + +void +f3 () +{ + #pragma omp declare reduction (foo: S: omp_out.s += omp_in.s) initializer (bar (omp_priv)) + #pragma omp declare reduction (bar: S: omp_out.s += omp_in.s) initializer (bar (omp_orig)) // { dg-error "one of the initializer call arguments should be" } +} + +template +int +f4 () +{ + #pragma omp declare reduction (foo: T: omp_out.s += omp_in.s) initializer (bar (omp_priv)) + #pragma omp declare reduction (bar: T: omp_out.s += omp_in.s) initializer (bar (omp_orig)) // { dg-error "one of the initializer call arguments should be" } + return 0; +} + +int y = f4 (); + +namespace N1 +{ + #pragma omp declare reduction (+: ::S: omp_out.s *= omp_in.s) // { dg-error "previous" } + #pragma omp declare reduction (+: S: omp_out.s += omp_in.s) // { dg-error "redeclaration of" } + void + f5 () + { + #pragma omp declare reduction (f5: S: omp_out.s *= omp_in.s) // { dg-error "previous" } + #pragma omp declare reduction (f5: ::S: omp_out.s += omp_in.s) // { dg-error "redeclaration of" } + } +} + +namespace N2 +{ + struct U + { + #pragma omp declare reduction (bar: S: omp_out.s *= omp_in.s) // { dg-error "with" } + #pragma omp declare reduction (bar: S: omp_out.s += omp_in.s) // { dg-error "cannot be overloaded" } + }; +} + +namespace N3 +{ + #pragma omp declare reduction (+: ::S: omp_out.s *= omp_in.s) // { dg-error "previous" } + #pragma omp declare reduction (+: T: omp_out.t += omp_in.t) + #pragma omp declare reduction (+: S: omp_out.s += omp_in.s) // { dg-error "redeclaration of" } + #pragma omp declare reduction (n3: long: omp_out += omp_in) // { dg-error "previous" } + #pragma omp declare reduction (n3: long int: omp_out += omp_in) // { dg-error "redeclaration of" } + #pragma omp declare reduction (n3: short unsigned: omp_out += omp_in) + #pragma omp declare reduction (n3: short int: omp_out += omp_in) + void + f6 () + { + #pragma omp declare reduction (f6: T: omp_out.t += omp_in.t) + #pragma omp declare reduction (f6: S: omp_out.s *= omp_in.s) // { dg-error "previous" } + #pragma omp declare reduction (f6: ::S: omp_out.s += omp_in.s) // { dg-error "redeclaration of" } + #pragma omp declare reduction (f6: long: omp_out += omp_in) // { dg-error "previous" } + #pragma omp declare reduction (f6: long int: omp_out += omp_in) // { dg-error "redeclaration of" } + #pragma omp declare reduction (f6: short unsigned: omp_out += omp_in) + #pragma omp declare reduction (f6: short int: omp_out += omp_in) + } +} + +namespace N4 +{ + struct U + { + #pragma omp declare reduction (bar: T: omp_out.t += omp_in.t) + #pragma omp declare reduction (bar: S: omp_out.s *= omp_in.s) // { dg-error "with" } + #pragma omp declare reduction (bar: S: omp_out.s += omp_in.s) // { dg-error "cannot be overloaded" } + #pragma omp declare reduction (bar: long: omp_out += omp_in) // { dg-error "with" } + #pragma omp declare reduction (bar: long int: omp_out += omp_in) // { dg-error "cannot be overloaded" } + #pragma omp declare reduction (bar: short unsigned: omp_out += omp_in) + #pragma omp declare reduction (bar: short int: omp_out += omp_in) + }; +} + +namespace N5 +{ + template + int + f7 () + { + #pragma omp declare reduction (f7: T: omp_out.s *= omp_in.s) // { dg-error "previous" } + #pragma omp declare reduction (f7: T: omp_out.s += omp_in.s) // { dg-error "redeclaration of" } + return 0; + } + int x = f7 (); + template + struct U + { + #pragma omp declare reduction (bar: T: omp_out.s *= omp_in.s) // { dg-error "with" } + #pragma omp declare reduction (bar: T: omp_out.s += omp_in.s) // { dg-error "cannot be overloaded" } + }; + U u; +} + +namespace N6 +{ + template + int + f8 () + { + #pragma omp declare reduction (f8: T: omp_out.t += omp_in.t) + #pragma omp declare reduction (f8: U: omp_out.s *= omp_in.s) // { dg-error "previous" } + #pragma omp declare reduction (f8: ::S: omp_out.s += omp_in.s) // { dg-error "redeclaration of" } + #pragma omp declare reduction (f8: long: omp_out += omp_in) // { dg-error "previous" } + #pragma omp declare reduction (f8: long int: omp_out += omp_in) // { dg-error "redeclaration of" } + #pragma omp declare reduction (f8: short unsigned: omp_out += omp_in) + #pragma omp declare reduction (f8: short int: omp_out += omp_in) + return 0; + } + int x = f8 (); + template + struct U + { + typedef V V2; + #pragma omp declare reduction (bar: T: omp_out.t += omp_in.t) + #pragma omp declare reduction (bar: V: omp_out.s *= omp_in.s) // { dg-error "with" } + #pragma omp declare reduction (bar: V2: omp_out.s += omp_in.s) // { dg-error "cannot be overloaded" } + #pragma omp declare reduction (bar: long: omp_out += omp_in) // { dg-error "with" } + #pragma omp declare reduction (bar: long int: omp_out += omp_in) // { dg-error "cannot be overloaded" } + #pragma omp declare reduction (bar: short unsigned: omp_out += omp_in) + #pragma omp declare reduction (bar: short int: omp_out += omp_in) + }; + U u; +} + +namespace N7 +{ + #pragma omp declare reduction (+: S: omp_out.s += omp_in.s) initializer (omp_priv) // { dg-error "invalid initializer clause" } + #pragma omp declare reduction (+: T: omp_out.t += omp_in.t) initializer (omp_priv ()) // { dg-error "invalid initializer clause" } +} + +namespace N8 +{ + struct A { int a; A (); ~A (); }; + struct B { int b; B (); ~B (); B (int); }; + struct C : public A, B { int c; C (); ~C (); }; + #pragma omp declare reduction (+:B:omp_out.b += omp_in.b) initializer (omp_priv (4)) + void bar (C &); + void baz () + { + C a; + #pragma omp parallel reduction (+:a) // { dg-error "user defined reduction with constructor initializer for base class" } + bar (a); + } +} --- gcc/testsuite/g++.dg/gomp/clause-3.C.jj 2013-09-09 17:43:11.367758947 +0200 +++ gcc/testsuite/g++.dg/gomp/clause-3.C 2013-09-09 17:58:17.781026154 +0200 @@ -11,7 +11,7 @@ int t; void foo (int x) { - char *p; + char *pp; struct S { int i; int j; } s; char a[32]; double d; @@ -42,18 +42,18 @@ foo (int x) ; #pragma omp p firstprivate (bar) // { dg-error "is not a variable" } ; -#pragma omp p reduction (+:p) // { dg-error "has invalid type for" } +#pragma omp p reduction (+:pp) // { dg-error "user defined reduction not found for" } ; -#pragma omp p reduction (*:s) // { dg-error "has invalid type for" } +#pragma omp p reduction (*:s) // { dg-error "user defined reduction not found for" } ; #pragma omp p reduction (-:a) // { dg-error "has invalid type for" } ; d = 0; #pragma omp p reduction (*:d) ; -#pragma omp p reduction (|:d) // { dg-error "has invalid type for" } +#pragma omp p reduction (|:d) // { dg-error "user defined reduction not found for" } ; -#pragma omp p reduction (&&:d) // { dg-error "has invalid type for" } +#pragma omp p reduction (&&:d) // { dg-error "user defined reduction not found for" } ; #pragma omp p copyin (d) // { dg-error "must be 'threadprivate'" } ; --- gcc/testsuite/g++.dg/gomp/udr-2.C.jj 2013-09-09 17:43:11.367758947 +0200 +++ gcc/testsuite/g++.dg/gomp/udr-2.C 2013-09-09 17:43:11.367758947 +0200 @@ -0,0 +1,119 @@ +// { dg-do compile } +// { dg-options "-fopenmp" } + +struct W { int w; W () : w (0) {} W (int x) : w (x) {} }; +namespace N1 +{ + int v; + #pragma omp declare reduction (foo : long int : omp_out |= v) // { dg-error "combiner refers to variable" } + #pragma omp declare reduction (foo : char : omp_out = v) // { dg-error "combiner refers to variable" } + typedef short T; + #pragma omp declare reduction (foo : T : omp_out += N1::v) // { dg-error "combiner refers to variable" } + #pragma omp declare reduction (foo : int : v *= omp_in) // { dg-error "combiner refers to variable" } + #pragma omp declare reduction (foo : W : omp_out.w *= omp_in.w + v) // { dg-error "combiner refers to variable" } +} +namespace N2 +{ + int v; + template + struct S + { + #pragma omp declare reduction (foo : T1 : omp_out |= v) // { dg-error "combiner refers to variable" } + #pragma omp declare reduction (foo : T2 : omp_out = v) // { dg-error "combiner refers to variable" } + typedef T3 T; + #pragma omp declare reduction (foo : T : omp_out += N1::v) // { dg-error "combiner refers to variable" } + #pragma omp declare reduction (foo : T4 : v *= omp_in) // { dg-error "combiner refers to variable" } + #pragma omp declare reduction (foo : T5 : omp_out.w *= omp_in.w + v) // { dg-error "combiner refers to variable" } + }; + S s; + template + int foo () + { + #pragma omp declare reduction (foo : T1 : omp_out |= v) // { dg-error "combiner refers to variable" } + #pragma omp declare reduction (foo : T2 : omp_out = v) // { dg-error "combiner refers to variable" } + typedef T3 T; + #pragma omp declare reduction (foo : T : omp_out += N1::v) // { dg-error "combiner refers to variable" } + #pragma omp declare reduction (foo : T4 : v *= omp_in) // { dg-error "combiner refers to variable" } + #pragma omp declare reduction (foo : T5 : omp_out.w *= omp_in.w + v) // { dg-error "combiner refers to variable" } + return 0; + } + int x = foo (); +} +namespace N3 +{ + int v; + #pragma omp declare reduction (foo : long int : omp_out |= omp_in) initializer (omp_priv = v) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : char : omp_out += omp_in) initializer (omp_priv ((char) N3::v)) // { dg-error "initializer refers to variable" } + typedef short T; + #pragma omp declare reduction (foo : T : omp_out += omp_in) initializer (omp_priv = (short) v) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : _Complex double : omp_out *= omp_in) initializer (omp_priv (v)) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : W : omp_out.w *= omp_in.w) initializer (omp_priv (N3::v)) // { dg-error "initializer refers to variable" } +} +namespace N4 +{ + int v; + template + struct S + { + #pragma omp declare reduction (foo : T1 : omp_out |= omp_in) initializer (omp_priv = v) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : T2 : omp_out += omp_in) initializer (omp_priv ((char) N3::v)) // { dg-error "initializer refers to variable" } + typedef T3 T; + #pragma omp declare reduction (foo : T : omp_out += omp_in) initializer (omp_priv = (short) v) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : T4 : omp_out *= omp_in) initializer (omp_priv (v)) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : T5 : omp_out.w *= omp_in.w) initializer (omp_priv (N3::v)) // { dg-error "initializer refers to variable" } + }; + S s; + template + int foo () + { + #pragma omp declare reduction (foo : T1 : omp_out |= omp_in) initializer (omp_priv = v) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : T2 : omp_out += omp_in) initializer (omp_priv ((char) N3::v)) // { dg-error "initializer refers to variable" } + typedef T3 T; + #pragma omp declare reduction (foo : T : omp_out += omp_in) initializer (omp_priv = (short) v) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : T4 : omp_out *= omp_in) initializer (omp_priv (v)) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : T5 : omp_out.w *= omp_in.w) initializer (omp_priv (N3::v)) // { dg-error "initializer refers to variable" } + return 0; + } + int x = foo (); +} +template +void init (T &, int &); +template +void initializer (T, int &); +namespace N5 +{ + int v; + #pragma omp declare reduction (foo : long int : omp_out |= omp_in) initializer (init (omp_priv, v)) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : char : omp_out += omp_in) initializer (initializer (&omp_priv, N3::v)) // { dg-error "initializer refers to variable" } + typedef short T; + #pragma omp declare reduction (foo : T : omp_out += omp_in) initializer (init (omp_priv, v)) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : _Complex double : omp_out *= omp_in) initializer (initializer (&omp_priv, v)) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : W : omp_out.w *= omp_in.w) initializer (init (omp_priv, N3::v)) // { dg-error "initializer refers to variable" } +} +namespace N6 +{ + int v; + template + struct S + { + #pragma omp declare reduction (foo : T1 : omp_out |= omp_in) initializer (initializer (&omp_priv, v)) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : T2 : omp_out += omp_in) initializer (init (omp_priv, N3::v)) // { dg-error "initializer refers to variable" } + typedef T3 T; + #pragma omp declare reduction (foo : T : omp_out += omp_in) initializer (init (omp_priv, v)) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : T4 : omp_out *= omp_in) initializer (init (omp_priv, v)) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : T5 : omp_out.w *= omp_in.w) initializer (initializer (&omp_priv, N3::v)) // { dg-error "initializer refers to variable" } + }; + S s; + template + int foo () + { + #pragma omp declare reduction (foo : T1 : omp_out |= omp_in) initializer (init (omp_priv, v)) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : T2 : omp_out += omp_in) initializer (init (omp_priv, N3::v)) // { dg-error "initializer refers to variable" } + typedef T3 T; + #pragma omp declare reduction (foo : T : omp_out += omp_in) initializer (initializer (&omp_priv, v)) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : T4 : omp_out *= omp_in) initializer (init (omp_priv, v)) // { dg-error "initializer refers to variable" } + #pragma omp declare reduction (foo : T5 : omp_out.w *= omp_in.w) initializer (initializer (omp_priv, N3::v)) // { dg-error "initializer refers to variable" } + return 0; + } + int x = foo (); +} --- gcc/testsuite/g++.dg/gomp/udr-4.C.jj 2013-09-11 18:42:04.963355505 +0200 +++ gcc/testsuite/g++.dg/gomp/udr-4.C 2013-09-11 18:41:54.000000000 +0200 @@ -0,0 +1,14 @@ +// { dg-do compile } + +struct S; // { dg-error "forward declaration" } +#pragma omp declare reduction (+:S:omp_out.s += omp_in.s) // { dg-error "invalid use of incomplete type" } +struct S { int s; S () : s (1) {} }; +#pragma omp declare reduction (*:S:omp_out.s *= omp_in.s) + +void +foo () +{ + S s; + #pragma omp parallel reduction (S::~S:s) // { dg-error "invalid reduction-identifier" } + s.s = 1; +} --- libgomp/testsuite/libgomp.c++/udr-1.C.jj 2013-09-09 17:43:11.368758942 +0200 +++ libgomp/testsuite/libgomp.c++/udr-1.C 2013-09-09 17:43:11.368758942 +0200 @@ -0,0 +1,82 @@ +// { dg-do run } + +extern "C" void abort (); + +struct S +{ + int s; + void foo (S &x) { s += x.s; } + void foo (S &x, bool y) { s += x.s; if (y) abort (); } + S (const S &x) { s = x.s + 1; } + S (const S &x, bool y) { s = x.s + 2; if (y) abort (); } + S () { s = 6; } + ~S (); +}; + +S::~S () +{ + if (s < 6) abort (); + s = -1; + /* Ensure the above store is not DSEd. */ + asm volatile ("" : : "r" (&s) : "memory"); +} + +void +bar (S &x) +{ + if (x.s != 6) abort (); + x.s = 15; +} + +#pragma omp declare reduction (foo: S: omp_out.foo (omp_in)) \ + initializer (omp_priv (omp_orig, false)) +#pragma omp declare reduction (foo: char, int, short: omp_out += omp_in - 4) \ + initializer (omp_priv (4)) +#pragma omp declare reduction (+: S: omp_out.foo (omp_in, false)) \ + initializer (omp_priv (omp_orig)) + +namespace N +{ + #pragma omp declare reduction (foo: S: omp_out.foo (omp_in)) \ + initializer (::bar (omp_priv)) + namespace M {} +} + +int +main () +{ + S a, b, c, s, t, u; + if (a.s != 6 || b.s != 6 || c.s != 6 + || s.s != 6 || t.s != 6 || u.s != 6) abort (); + s.s = 9; t.s = 10; u.s = 11; + int d = 0, e = 0, f = 0, g = 0, h = 30, v = 2, q = 0; + #pragma omp declare reduction (foo: S: omp_out.foo (omp_in, true)) \ + initializer (omp_priv = omp_orig) + { + #pragma omp declare reduction (foo: S: omp_out.foo (omp_in, false)) \ + initializer (omp_priv = omp_orig) + #pragma omp parallel num_threads (4) reduction (N::operator +: q) \ + reduction (operator +: a, d) reduction (::operator +: b, e) \ + reduction (+: c, f) reduction (::N::M::operator +: g) \ + reduction (::N::min: h) reduction (foo: s) reduction (N::foo: t) \ + reduction (::foo: u) reduction (::foo: v) + { + if (a.s != 7 || b.s != 7 || c.s != 7 + || s.s != 10 || t.s != 15 || u.s != 13 + || v != 4 || d || e || f || g || h != __INT_MAX__) abort (); + asm volatile ("" : "+m" (a.s), "+m" (b.s)); + asm volatile ("" : "+m" (c.s), "+r" (d)); + asm volatile ("" : "+r" (e), "+r" (f)); + asm volatile ("" : "+r" (g), "+r" (h)); + asm volatile ("" : "+m" (s.s), "+m" (t.s)); + asm volatile ("" : "+m" (u.s), "+r" (v)); + a.s++; b.s++; c.s++; d++; e++; f++; g++; h = t.s; + s.s++; t.s++; u.s++; v++; q++; + } + } + if (a.s != 6 + q * 8 || b.s != 6 + q * 8 || c.s != 6 + q * 8 + || d != q || e != q || f != q || g != q || h != 15 + || s.s != 9 + q * 11 || t.s != 10 + q * 16 || u.s != 11 + q * 14 + || v != 2 + q) + abort (); +} --- libgomp/testsuite/libgomp.c++/udr-3.C.jj 2013-09-09 17:43:11.369758937 +0200 +++ libgomp/testsuite/libgomp.c++/udr-3.C 2013-09-11 12:06:42.960002990 +0200 @@ -0,0 +1,149 @@ +// { dg-do run } + +extern "C" void abort (); + +void +dblinit (double *p) +{ + *p = 2.0; +} + +namespace NS +{ + template + struct U + { + void foo (U &, bool); + U (); + }; + template + struct S + { + int s; + #pragma omp declare reduction (foo : U<0>, S : omp_out.foo (omp_in, false)) + #pragma omp declare reduction (foo : int : omp_out += omp_in) \ + initializer (omp_priv = N + 2) + #pragma omp declare reduction (foo : double : omp_out += omp_in) \ + initializer (dblinit (&omp_priv)) + void baz (int v) + { + S s; + int q = 0; + if (s.s != 6 || v != 0) abort (); + s.s = 20; + double d = 4.0; + #pragma omp parallel num_threads (4) reduction (foo : s, v, d) \ + reduction (::NS::U::operator + : q) + { + if (s.s != 6 || q != 0 || v != N + 2 || d != 2.0) abort (); + asm volatile ("" : "+m" (s.s), "+r" (q), "+r" (v)); + s.s++; q++; v++; + } + if (s.s != 20 + q * 7 || (N + 3) * q != v || d != 4.0 + 2.0 * q) + abort (); + } + void foo (S &x) { s += x.s; } + void foo (S &x, bool y) { s += x.s; if (y) abort (); } + S (const S &x) { s = x.s + 1; } + S (const S &x, bool y) { s = x.s + 2; if (y) abort (); } + S () { s = 6; } + S (int x) { s = x; } + ~S (); + }; + #pragma omp declare reduction (bar : S<1> : omp_out.foo (omp_in)) \ + initializer (omp_priv (8)) +} + +template +NS::S::~S () +{ + if (s < 6) abort (); + s = -1; + /* Ensure the above store is not DSEd. */ + asm volatile ("" : : "r" (&s) : "memory"); +} + +template +struct T : public NS::S +{ + void baz () + { + NS::S s; + int q = 0; + if (s.s != 6) abort (); + #pragma omp parallel num_threads (4) reduction (foo:s) \ + reduction (+: q) + { + if (s.s != 6 || q != 0) abort (); + asm volatile ("" : "+m" (s.s), "+r" (q)); + s.s += 2; q++; + } + if (s.s != 6 + q * 8) abort (); + } +}; + +struct W +{ + int v; + W () : v (6) {} + ~W () {} +}; + +template +struct V +{ + #pragma omp declare reduction (baz: T: omp_out.s += omp_in.s) \ + initializer (omp_priv (11)) + #pragma omp declare reduction (baz: D: omp_out += omp_in) \ + initializer (dblinit (&omp_priv)) + static void dblinit (D *x) { *x = 3.0; } + void baz () + { + T t; + V v; + int q = 0; + D d = 4.0; + if (t.s != 6 || v.v != 4) abort (); + #pragma omp declare reduction (+ : V, W : omp_out.v -= omp_in.v) \ + initializer (omp_priv (12)) + { + #pragma omp declare reduction (+ : W, V : omp_out.v += omp_in.v) \ + initializer (omp_priv (9)) + #pragma omp parallel num_threads (4) reduction (+: v, q) \ + reduction (baz: t, d) + { + if (t.s != 11 || v.v != 9 || q != 0 || d != 3.0) abort (); + asm volatile ("" : "+m" (t.s), "+m" (v.v), "+r" (q)); + t.s += 2; v.v += 3; q++; + } + if (t.s != 6 + 13 * q || v.v != 4 + 12 * q || d != 4.0 + 3.0 * q) + abort (); + } + } + int v; + V () : v (4) {} + V (int x) : v (x) {} + ~V () {} +}; + +int +main () +{ + NS::S<0> u; + u.baz (0); + T<2> t; + t.baz (); + NS::S<1> s; + int q = 0; + if (s.s != 6) abort (); + // Test ADL + #pragma omp parallel num_threads (4) reduction (bar:s) reduction (+:q) + { + if (s.s != 8 || q != 0) abort (); + asm volatile ("" : "+m" (s.s), "+r" (q)); + s.s += 4; q++; + } + if (s.s != 6 + q * 12) abort (); + V , double> v; + v.baz (); +} --- libgomp/testsuite/libgomp.c++/simd-4.C.jj 2013-09-09 17:43:11.368758942 +0200 +++ libgomp/testsuite/libgomp.c++/simd-4.C 2013-09-11 12:16:57.052674813 +0200 @@ -0,0 +1,45 @@ +// { dg-do run } +// { dg-options "-O2" } +// { dg-additional-options "-msse2" { target sse2_runtime } } +// { dg-additional-options "-mavx" { target avx_runtime } } + +extern "C" void abort (); +int a[1024] __attribute__((aligned (32))) = { 1 }; +struct S +{ + int s; + S () : s (0) {} + ~S () {} +}; +#pragma omp declare reduction (+:S:omp_out.s += omp_in.s) +#pragma omp declare reduction (foo:S:omp_out.s += omp_in.s) +#pragma omp declare reduction (foo:int:omp_out += omp_in) + +__attribute__((noinline, noclone)) int +foo () +{ + int i, u = 0; + S s, t; + #pragma omp simd aligned(a : 32) reduction(+:s) reduction(foo:t, u) + for (i = 0; i < 1024; i++) + { + int x = a[i]; + s.s += x; + t.s += x; + u += x; + } + if (t.s != s.s || u != s.s) + abort (); + return s.s; +} + +int +main () +{ + int i; + for (i = 0; i < 1024; i++) + a[i] = (i & 31) + (i / 128); + int s = foo (); + if (s != 19456) + abort (); +} --- libgomp/testsuite/libgomp.c++/udr-7.C.jj 2013-09-11 17:37:36.341363569 +0200 +++ libgomp/testsuite/libgomp.c++/udr-7.C 2013-09-11 17:33:10.000000000 +0200 @@ -0,0 +1,72 @@ +// { dg-do run } + +extern "C" void abort (); + +struct S +{ + int s; + void foo (S &x) { s += x.s; } + S (const S &x) { s = x.s + 1; } + S () { s = 6; } + ~S () {} +}; + +void +bar (S &x, S &y) +{ + if (x.s != 6 || y.s != 6) + abort (); + x.s = 8; +} + +#pragma omp declare reduction (foo: S: omp_out.foo (omp_in)) \ + initializer (omp_priv (omp_orig)) +#pragma omp declare reduction (bar : S: omp_out.foo (omp_in)) \ + initializer (bar (omp_priv, omp_orig)) + +S +baz (S x) +{ + S r; + int i = 0; + if (x.s != 7 || r.s != 6) + abort (); + #pragma omp parallel reduction (foo: x) reduction (bar: r) \ + reduction (+: i) + { + if (x.s != 8 || r.s != 8) + abort (); + x.s = 12; + r.s = 14; + i = 1; + } + if (x.s != 7 + 12 * i || r.s != 6 + 14 * i) + abort (); + return r; +} + +void +baz (S &x, S &y) +{ + int i = 0, &j = i; + #pragma omp parallel reduction (foo: x) reduction (bar: y) \ + reduction (+: i) + { + if (x.s != 7 || y.s != 8) + abort (); + x.s = 12; + y.s = 14; + i = 1; + } + if (x.s != 6 + 12 * j || y.s != 6 + 14 * j) + abort (); +} + +int +main () +{ + S s; + baz (s); + S t, u; + baz (t, u); +} --- libgomp/testsuite/libgomp.c++/simd-7.C.jj 2013-09-11 18:22:16.180549560 +0200 +++ libgomp/testsuite/libgomp.c++/simd-7.C 2013-09-11 18:25:58.836399590 +0200 @@ -0,0 +1,72 @@ +// { dg-do run } +// { dg-options "-O2" } +// { dg-additional-options "-msse2" { target sse2_runtime } } +// { dg-additional-options "-mavx" { target avx_runtime } } + +extern "C" void abort (); +int a[1024] __attribute__((aligned (32))) = { 1 }; +struct S +{ + int s; + S () : s (0) {} + S (int x) : s (x) {} + ~S () {} +}; +#pragma omp declare reduction (+:S:omp_out.s += omp_in.s) \ + initializer (omp_priv (0)) +#pragma omp declare reduction (foo:S:omp_out.s += omp_in.s) \ + initializer (omp_priv (0)) +#pragma omp declare reduction (foo:int:omp_out += omp_in) \ + initializer (omp_priv = 0) + +__attribute__((noinline, noclone)) S +foo (S s) +{ + int i, v = 0, &u = v; + S t; + #pragma omp simd aligned(a : 32) reduction(+:s) reduction(foo:t, u) \ + safelen(1) + for (i = 0; i < 1024; i++) + { + int x = a[i]; + s.s += x; + t.s += x; + u += x; + } + if (t.s != s.s || u != s.s) + abort (); + return t; +} + +__attribute__((noinline, noclone)) int +bar (S &s, S &t) +{ + int i, v = 0, &u = v; + #pragma omp simd aligned(a : 32) reduction(+:s) reduction(foo:t, u) \ + safelen(1) + for (i = 0; i < 1024; i++) + { + int x = a[i]; + s.s += x; + t.s += x; + u += x; + } + if (t.s != s.s || u != s.s) + abort (); + return s.s; +} + +int +main () +{ + int i; + for (i = 0; i < 1024; i++) + a[i] = (i & 31) + (i / 128); + S q; + int s = foo (q).s; + if (s != 19456) + abort (); + S r, v; + if (bar (r, v) != s) + abort (); +} --- libgomp/testsuite/libgomp.c++/udr-2.C.jj 2013-09-09 17:43:11.369758937 +0200 +++ libgomp/testsuite/libgomp.c++/udr-2.C 2013-09-11 12:06:35.261046352 +0200 @@ -0,0 +1,88 @@ +// { dg-do run } + +extern "C" void abort (); + +namespace NS +{ + struct U + { + void foo (U &, bool); + U (); + }; + struct S + { + int s; + #pragma omp declare reduction (foo : U, S : omp_out.foo (omp_in, false)) + #pragma omp declare reduction (foo : int : omp_out += omp_in) \ + initializer (omp_priv = int ()) + void baz (int v) + { + S s; + int q = 0; + if (s.s != 6 || v != 0) abort (); + s.s = 20; + #pragma omp parallel num_threads (4) reduction (foo : s, v) \ + reduction (::NS::U::operator + : q) + { + if (s.s != 6 || q != 0 || v != 0) abort (); + asm volatile ("" : "+m" (s.s), "+r" (q), "+r" (v)); + s.s++; q++; v++; + } + if (s.s != 20 + q * 7 || q != v) abort (); + } + void foo (S &x) { s += x.s; } + void foo (S &x, bool y) { s += x.s; if (y) abort (); } + S (const S &x) { s = x.s + 1; } + S (const S &x, bool y) { s = x.s + 2; if (y) abort (); } + S () { s = 6; } + S (int x) { s = x; } + ~S (); + }; + #pragma omp declare reduction (bar : S : omp_out.foo (omp_in)) \ + initializer (omp_priv (8)) +} + +NS::S::~S () +{ + if (s < 6) abort (); + s = -1; + /* Ensure the above store is not DSEd. */ + asm volatile ("" : : "r" (&s) : "memory"); +} + +struct T : public NS::S +{ + void baz () + { + S s; + int q = 0; + if (s.s != 6) abort (); + #pragma omp parallel num_threads (4) reduction (foo:s) \ + reduction (+: q) + { + if (s.s != 6 || q != 0) abort (); + asm volatile ("" : "+m" (s.s), "+r" (q)); + s.s += 2; q++; + } + if (s.s != 6 + q * 8) abort (); + } +}; + +int +main () +{ + NS::S s; + s.baz (0); + T t; + t.baz (); + int q = 0; + if (s.s != 6) abort (); + // Test ADL + #pragma omp parallel num_threads (4) reduction (bar:s) reduction (+:q) + { + if (s.s != 8 || q != 0) abort (); + asm volatile ("" : "+m" (s.s), "+r" (q)); + s.s += 4; q++; + } + if (s.s != 6 + q * 12) abort (); +} --- libgomp/testsuite/libgomp.c++/udr-8.C.jj 2013-09-11 17:53:23.295568197 +0200 +++ libgomp/testsuite/libgomp.c++/udr-8.C 2013-09-11 17:53:11.000000000 +0200 @@ -0,0 +1,39 @@ +// { dg-do run } + +extern "C" void abort (); + +struct S; +void foo (S *, S *); +void bar (S &, S &); +#pragma omp declare reduction (+:S:foo (&omp_out, &omp_in)) +#pragma omp declare reduction (*:S:bar (omp_out, omp_in)) +struct S { int s; S () : s (0) {} }; + +void +foo (S *x, S *y) +{ + x->s += y->s; +} + +void +bar (S &x, S &y) +{ + x.s += y.s; +} + +int +main () +{ + S s, t; + int i; + #pragma omp parallel reduction (+:s, i) reduction (*:t) + { + if (s.s != 0 || t.s != 0) + abort (); + s.s = 2; + t.s = 3; + i = 1; + } + if (s.s != 2 * i || t.s != 3 * i) + abort (); +} --- libgomp/testsuite/libgomp.c++/simd-5.C.jj 2013-09-09 17:43:11.368758942 +0200 +++ libgomp/testsuite/libgomp.c++/simd-5.C 2013-09-11 12:17:06.042629927 +0200 @@ -0,0 +1,47 @@ +// { dg-do run } +// { dg-options "-O2" } +// { dg-additional-options "-msse2" { target sse2_runtime } } +// { dg-additional-options "-mavx" { target avx_runtime } } + +extern "C" void abort (); +int a[1024] __attribute__((aligned (32))) = { 1 }; +struct S +{ + int s; + S () : s (0) {} + ~S () {} +}; +#pragma omp declare reduction (+:S:omp_out.s += omp_in.s) +#pragma omp declare reduction (foo:S:omp_out.s += omp_in.s) +#pragma omp declare reduction (foo:int:omp_out += omp_in) + +__attribute__((noinline, noclone)) int +foo () +{ + int i, u = 0, q = 0; + S s, t; + #pragma omp simd aligned(a : 32) reduction(+:s, q) reduction(foo:t, u) \ + safelen(1) + for (i = 0; i < 1024; i++) + { + int x = a[i]; + s.s += x; + t.s += x; + u += x; + q++; + } + if (t.s != s.s || u != s.s || q != 1024) + abort (); + return s.s; +} + +int +main () +{ + int i; + for (i = 0; i < 1024; i++) + a[i] = (i & 31) + (i / 128); + int s = foo (); + if (s != 19456) + abort (); +} --- libgomp/testsuite/libgomp.c++/udr-4.C.jj 2013-09-09 17:43:11.369758937 +0200 +++ libgomp/testsuite/libgomp.c++/udr-4.C 2013-09-11 12:07:02.964893591 +0200 @@ -0,0 +1,32 @@ +// { dg-do run } + +extern "C" void abort (); + +struct S +{ + int s; + S () : s (0) {} + ~S () {} +}; + +#pragma omp declare reduction (+:S:omp_out.s += omp_in.s) +#pragma omp declare reduction (foo:S:omp_out.s += omp_in.s) +#pragma omp declare reduction (foo:int:omp_out += omp_in) + +int +main () +{ + int i, u = 0, q = 0; + S s, t; + if (s.s != 0 || t.s != 0) abort (); + #pragma omp parallel reduction(+:s, q) reduction(foo:t, u) + { + if (s.s != 0 || t.s != 0 || u != 0 || q != 0) abort (); + s.s = 6; + t.s = 8; + u = 9; + q++; + } + if (s.s != 6 * q || t.s != 8 * q || u != 9 * q) abort (); + return 0; +} --- libgomp/testsuite/libgomp.c++/simd-6.C.jj 2013-09-11 17:54:49.046125920 +0200 +++ libgomp/testsuite/libgomp.c++/simd-6.C 2013-09-11 18:00:37.414316598 +0200 @@ -0,0 +1,70 @@ +// { dg-do run } +// { dg-options "-O2" } +// { dg-additional-options "-msse2" { target sse2_runtime } } +// { dg-additional-options "-mavx" { target avx_runtime } } + +extern "C" void abort (); +int a[1024] __attribute__((aligned (32))) = { 1 }; +struct S +{ + int s; + S () : s (0) {} + S (int x) : s (x) {} + ~S () {} +}; +#pragma omp declare reduction (+:S:omp_out.s += omp_in.s) \ + initializer (omp_priv (0)) +#pragma omp declare reduction (foo:S:omp_out.s += omp_in.s) \ + initializer (omp_priv (0)) +#pragma omp declare reduction (foo:int:omp_out += omp_in) \ + initializer (omp_priv = 0) + +__attribute__((noinline, noclone)) S +foo (S s) +{ + int i, v = 0, &u = v; + S t; + #pragma omp simd aligned(a : 32) reduction(+:s) reduction(foo:t, u) + for (i = 0; i < 1024; i++) + { + int x = a[i]; + s.s += x; + t.s += x; + u += x; + } + if (t.s != s.s || u != s.s) + abort (); + return t; +} + +__attribute__((noinline, noclone)) int +bar (S &s, S &t) +{ + int i, v = 0, &u = v; + #pragma omp simd aligned(a : 32) reduction(+:s) reduction(foo:t, u) + for (i = 0; i < 1024; i++) + { + int x = a[i]; + s.s += x; + t.s += x; + u += x; + } + if (t.s != s.s || u != s.s) + abort (); + return s.s; +} + +int +main () +{ + int i; + for (i = 0; i < 1024; i++) + a[i] = (i & 31) + (i / 128); + S q; + int s = foo (q).s; + if (s != 19456) + abort (); + S r, v; + if (bar (r, v) != s) + abort (); +} --- libgomp/testsuite/libgomp.c++/udr-6.C.jj 2013-09-11 17:37:33.174380830 +0200 +++ libgomp/testsuite/libgomp.c++/udr-6.C 2013-09-11 12:05:38.000000000 +0200 @@ -0,0 +1,70 @@ +// { dg-do run } + +extern "C" void abort (); + +struct A { int a; A () : a (6) {} }; +struct B { int b; B () : b (5) {} }; +struct C { int c; C () : c (4) {} }; +struct D { int d; D () : d (3) {} }; +struct E : A, B {}; +struct F : C, D {}; +struct G : E, F {}; +void foo (B &); +void foo (F &); +#pragma omp declare reduction (+:F:omp_out.c += omp_in.c) \ + initializer(foo (omp_priv)) +#pragma omp declare reduction (+:B:omp_out.b += omp_in.b) \ + initializer(foo (omp_priv)) + +void +foo (B &x) +{ + if (x.b != 5) + abort (); + x.b = 9; +} + +template +void bar (T &x, T &y, int z) +{ + if (z) + abort (); + x.a += y.a; +} + +namespace N1 +{ + struct A { int a; A () : a (0) {} }; + #pragma omp declare reduction (+:A:bar (omp_out, omp_in, 0)) +}; +namespace N2 +{ + struct B : N1::A { }; + #pragma omp declare reduction (+:N1::A:bar (omp_out, omp_in, 1)) +}; + +int +main () +{ + G g; + int i = 0; + #pragma omp parallel reduction(+:g, i) + { + if (g.a != 6 || g.b != 9 || g.c != 4 || g.d != 3) + abort (); + g.a = 1, g.b = 2, g.c = 3, g.d = 4, i = 1; + } + if (g.a != 6 || g.b != 5 + 2 * i || g.c != 4 || g.d != 3) + abort (); + N2::B b; + i = 0; + #pragma omp parallel reduction (+:b, i) + { + if (b.a != 0) + abort (); + b.a = 4; + i = 1; + } + if (b.a != 4 * i) + abort (); +} --- libgomp/testsuite/libgomp.c++/udr-5.C.jj 2013-09-10 18:46:05.235622414 +0200 +++ libgomp/testsuite/libgomp.c++/udr-5.C 2013-09-10 18:43:35.000000000 +0200 @@ -0,0 +1,49 @@ +// { dg-do run } + +extern "C" void abort (); + +struct S +{ + void foo () + { + S s; + int j = 0; + #pragma omp declare reduction (bar : int : omp_out += omp_in) + #pragma omp parallel reduction (bar : s) reduction(S::operator+ : j) + s.a = 4, j = 1; + if (s.a != 4 * j) abort (); + } + #pragma omp declare reduction (bar : S : baz (omp_out, omp_in)) + static void baz (S &x, S &y) { x.a += y.a; } + S () : a (0) {} + int a; +}; + +template +struct T +{ + void foo () + { + S s; + T t; + int j = 0; + #pragma omp declare reduction (bar : int : omp_out += omp_in) + #pragma omp parallel reduction (bar : t) reduction (S::bar : s) \ + reduction(T::operator+ : j) + s.a = 4, t.a = 5, j = 1; + if (s.a != 4 * j || t.a != 5 * j) abort (); + } + #pragma omp declare reduction (bar : T : baz (omp_out, omp_in)) + static void baz (T &x, T &y) { x.a += y.a; } + T () : a (N) {} + int a; +}; + +int +main () +{ + S s; + s.foo (); + T<0> t; + t.foo (); +}