@@ -629,6 +629,9 @@ struct GTY(()) tree_base {
OMP_CLAUSE_LINEAR_NO_COPYIN in
OMP_CLAUSE_LINEAR
+ OMP_CLAUSE_REDUCTION_OMP_ORIG_REF in
+ OMP_CLAUSE_REDUCTION
+
TRANSACTION_EXPR_RELAXED in
TRANSACTION_EXPR
@@ -1968,6 +1971,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) \
@@ -2679,6 +2679,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);
@@ -2697,9 +2698,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:
@@ -2738,9 +2736,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)
@@ -2927,19 +2928,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)
{
@@ -2956,8 +2958,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:
@@ -3104,19 +3106,73 @@ 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))
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));
+ if (is_simd
+ && lower_rec_simd_input_clauses (new_var, ctx, max_vf,
+ idx, lane, ivar, lvar))
+ {
+ gcc_assert (DECL_VALUE_EXPR (new_var) == lvar);
+ SET_DECL_VALUE_EXPR (new_var, ivar);
+ if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) == NULL)
+ {
+ 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]);
+ }
+ else
+ {
+ 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;
+ SET_DECL_VALUE_EXPR (new_var, 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;
+ }
+ if (OMP_CLAUSE_REDUCTION_GIMPLE_INIT (c) == NULL)
+ {
+ x = lang_hooks.decls.omp_clause_default_ctor (c, new_var,
+ x);
+ if (x)
+ gimplify_and_add (x, ilist);
+ }
+ else
+ {
+ 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
{
@@ -3216,8 +3272,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. */
@@ -3406,7 +3463,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;
}
@@ -8503,7 +8560,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);
@@ -8531,10 +8588,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. */
@@ -8572,6 +8630,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)
@@ -330,8 +330,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_character (buffer, ':');
+ if (OMP_CLAUSE_REDUCTION_CODE (clause) != ERROR_MARK)
+ {
+ pp_string (buffer,
+ op_symbol_code (OMP_CLAUSE_REDUCTION_CODE (clause)));
+ pp_character (buffer, ':');
+ }
dump_generic_node (buffer, OMP_CLAUSE_DECL (clause),
spc, flags, false);
pp_character (buffer, ')');
@@ -1982,7 +1982,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
@@ -3187,6 +3188,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) \
@@ -5773,6 +5779,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);
+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);
@@ -5797,7 +5806,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);
@@ -976,12 +976,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
@@ -1416,6 +1419,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 %<pragma omp declare reduction%>");
+ error_at (DECL_SOURCE_LOCATION (olddecl),
+ "previous %<pragma omp declare reduction%> declaration");
+ return error_mark_node;
+ }
/* If a function is explicitly declared "throw ()", propagate that to
the corresponding builtin. */
@@ -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
@@ -23083,9 +23086,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 ();
@@ -26952,70 +26964,89 @@ 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;
+ location_t loc;
+ saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
+ parser->colon_corrects_to_scope_p = false;
+ loc = cp_lexer_peek_token (parser->lexer)->location;
+ 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 %<+%>, %<*%>, %<-%>, %<&%>, %<^%>, "
- "%<|%>, %<&&%>, %<||%>, %<min%> or %<max%>");
- 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);
+ tree scope = parser->scope;
+ if (scope)
+ id = cp_parser_lookup_name_simple (parser, id, loc);
+ }
+ else
+ {
+ 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;
@@ -27023,7 +27054,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;
}
@@ -29839,10 +29873,361 @@ 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 ();
+
+ if (strcmp (p, "omp_priv") == 0)
+ {
+ bool is_direct_init, is_non_constant_init;
+ cp_lexer_consume_token (parser->lexer);
+ 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);
+ initializer = NULL_TREE;
+ }
+ else
+ {
+ cp_finish_decl (omp_priv, NULL_TREE, false, NULL_TREE,
+ LOOKUP_ONLYCONVERTING);
+ 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<tree, va_gc> *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 %<omp_priv%>"
+ " 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 (!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<tree> 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);
+ 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 fndecl = build_lang_decl (FUNCTION_DECL, 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
@@ -29862,13 +30247,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);
@@ -8826,6 +8826,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
{
@@ -10256,6 +10259,21 @@ tsubst_decl (tree t, tree args, tsubst_f
if (excessive_deduction_depth)
RETURN (error_mark_node);
+ /* 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%>");
+ }
+
/* We do NOT check for matching decls pushed separately at this
point, as they may not represent instantiations of this
template, and in any case are considered separate under the
@@ -12663,7 +12681,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:
@@ -12686,6 +12703,19 @@ 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))
+ {
+ if (!identifier_p (OMP_CLAUSE_REDUCTION_PLACEHOLDER (oc)))
+ OMP_CLAUSE_REDUCTION_PLACEHOLDER (nc)
+ = tsubst_expr (OMP_CLAUSE_REDUCTION_PLACEHOLDER (oc), args,
+ complain, in_decl,
+ /*integral_constant_expression_p=*/false);
+ }
+ 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)
@@ -13016,6 +13046,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;
@@ -13520,6 +13561,70 @@ 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[6];
+ memset (stmts, 0, sizeof stmts);
+ for (i = 0, tsi = tsi_start (t);
+ i < 6 && !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);
+ }
+}
+
/* T is a postfix-expression that is not being used in a function
call. Return the substituted version of T. */
@@ -19193,6 +19298,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. */
@@ -19202,7 +19308,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
@@ -19239,26 +19349,44 @@ 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;
+ if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern)
+ && TREE_CODE (DECL_CONTEXT (code_pattern)) == FUNCTION_DECL)
+ DECL_SAVED_TREE (d) = pop_stmt_list (block);
+
/* We expand all the array notation expressions here. */
if (flag_enable_cilkplus
&& contains_array_notation_expr (DECL_SAVED_TREE (d)))
DECL_SAVED_TREE (d) = expand_array_notation_exprs (DECL_SAVED_TREE (d));
/* 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)
+ {
+ 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. */
@@ -1503,7 +1503,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)
@@ -4053,7 +4053,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;
@@ -4077,8 +4078,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)
@@ -4544,6 +4544,250 @@ 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)
+{
+ const char *p = 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);
+ }
+
+ size_t lenp = sizeof "omp declare reduction ";
+ size_t len = strlen (p);
+ char *name = XALLOCAVEC (char, lenp + len);
+ memcpy (name, "omp declare reduction ", lenp - 1);
+ memcpy (name + lenp - 1, p, len + 1);
+ return get_identifier (name);
+}
+
+/* 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[6];
+ 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 %<omp_out%> nor %<omp_in%>",
+ *tp);
+ else
+ error_at (loc, "%<#pragma omp declare reduction%> initializer refers "
+ "to variable %qD which is not %<omp_priv%> nor "
+ "%<omp_orig%>",
+ *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++)
+ if (DECL_NAME (udr) == omp_reduction_id (predef_codes[i], NULL_TREE))
+ break;
+ if (i == 8
+ && TREE_CODE (type) != COMPLEX_EXPR
+ && (strcmp (IDENTIFIER_POINTER (DECL_NAME (udr)),
+ "omp declare reduction min") == 0
+ || strcmp (IDENTIFIER_POINTER (DECL_NAME (udr)),
+ "omp declare reduction max") == 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 < 6 && !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;
+ }
+}
+
+/* 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 OMP_CLAUSE_DECL (C). */
+
+static tree
+clone_omp_udr (tree c, tree stmt, tree omp_decl1, tree omp_decl2,
+ 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) = OMP_CLAUSE_DECL (c);
+ 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. */
@@ -5063,6 +5307,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)
@@ -5075,12 +5320,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:
@@ -5128,34 +5375,189 @@ 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 %<reduction%>", 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;
+ if (ARITHMETIC_TYPE_P (TREE_TYPE (t)))
+ 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 (TREE_TYPE (t)) == 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 (TREE_TYPE (t)))
+ break;
+ predefined = true;
break;
default:
- error ("%qE has invalid type for %<reduction(%s)%>",
- t, operator_name_info[r_code].name);
- remove = true;
+ break;
}
- }
- break;
+ else if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
+ || TYPE_READONLY (TREE_TYPE (t)))
+ {
+ error ("%qE has invalid type for %<reduction%>", 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 type = TYPE_MAIN_VARIANT (TREE_TYPE (t));
+ tree id = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c);
+
+ OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = NULL_TREE;
+ if (id == NULL_TREE)
+ id = omp_reduction_id (OMP_CLAUSE_REDUCTION_CODE (c),
+ NULL_TREE);
+ if (identifier_p (id))
+ {
+ cp_id_kind idk;
+ bool nonint_cst_expression_p;
+ const char *error_msg;
+ 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,
+ OMP_CLAUSE_LOCATION (c));
+ if (idk == CP_ID_KIND_UNQUALIFIED
+ && identifier_p (id))
+ {
+ vec<tree, va_gc> *args = NULL;
+ vec_safe_push (args, build_reference_type (type));
+ id = perform_koenig_lookup (id, args, false, tf_none);
+ }
+ }
+ 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)
+ {
+ 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[6];
+ memset (stmts, 0, sizeof stmts);
+ for (i = 0, tsi = tsi_start (body);
+ i < 6 && !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])))
+ cxx_mark_addressable (OMP_CLAUSE_DECL (c));
+ OMP_CLAUSE_REDUCTION_MERGE (c)
+ = clone_omp_udr (c, stmts[2],
+ DECL_EXPR_DECL (stmts[0]),
+ DECL_EXPR_DECL (stmts[1]),
+ placeholder);
+ }
+ 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);
+ OMP_CLAUSE_REDUCTION_INIT (c)
+ = clone_omp_udr (c, stmts[5],
+ DECL_EXPR_DECL (stmts[4]),
+ DECL_EXPR_DECL (stmts[3]),
+ placeholder);
+ 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 (TYPE_HAS_USER_CONSTRUCTOR (TREE_TYPE (t)))
+ need_default_ctor = true;
+ else
+ {
+ tree init;
+ if (AGGREGATE_TYPE_P (TREE_TYPE (t)))
+ init = build_constructor (TREE_TYPE (t),
+ NULL);
+ else
+ init = fold_convert (TREE_TYPE (t),
+ integer_zero_node);
+ OMP_CLAUSE_REDUCTION_INIT (c)
+ = build2 (INIT_EXPR, TREE_TYPE (t), t, 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))
@@ -5222,10 +5624,12 @@ finish_omp_clauses (tree clauses)
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)
@@ -42,18 +42,18 @@ foo (int x)
;
#pragma omp p firstprivate (bar) // { dg-error "is not a variable" }
;
-#pragma omp p reduction (+:pp) // { 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'" }
;
@@ -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 <typename T1, typename T2, typename T3, typename T4>
+ 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<long int, char, short, _Complex double> s;
+ template <typename T1, typename T2, typename T3, typename T4>
+ 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 <long int, char, short, _Complex double> ();
+}
+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 <typename T1, typename T2, typename T3, typename T4>
+ 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 <typename T1, typename T2, typename T3, typename T4>
+ 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 <typename T>
+ struct S
+ {
+ #pragma omp declare reduction (baz : T : omp_out *= omp_in) // { dg-error "function, array or reference" }
+ };
+ S<char &> s;
+ template <typename T>
+ int foo ()
+ {
+ #pragma omp declare reduction (baz : T : omp_out *= omp_in) // { dg-error "function, array or reference" }
+ return 0;
+ }
+ int x = foo <char &> ();
+}
+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 <typename T1, typename T2, typename T3, typename T4>
+ 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<const A, __const A, volatile A, A *__restrict> s;
+ template <typename T1, typename T2, typename T3, typename T4>
+ 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 <const A, __const A, volatile A, A *__restrict> ();
+}
@@ -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 <typename T1, typename T2, typename T3, typename T4, typename T5>
+ 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<long int, char, short, _Complex double, W> s;
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ 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 <long int, char, short, _Complex double, W> ();
+}
+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 <typename T1, typename T2, typename T3, typename T4, typename T5>
+ 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<long int, char, short, _Complex double, W> s;
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ 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 <long int, char, short, _Complex double, W> ();
+}
+template <typename T>
+void init (T &, int &);
+template <typename T>
+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 <typename T1, typename T2, typename T3, typename T4, typename T5>
+ 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<long int, char, short, _Complex double, W> s;
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ 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 <long int, char, short, _Complex double, W> ();
+}
@@ -0,0 +1,170 @@
+// { 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) // { dg-error "user defined reduction not found" }
+ s.s = 1;
+}
+
+template <int N>
+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) // { dg-error "user defined reduction not found" }
+ 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 <typename T>
+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 <S> ();
+
+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 <typename T>
+ 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 <S> ();
+ template <typename T>
+ 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<S> u;
+}
+
+namespace N6
+{
+ template <typename U>
+ 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 <S> ();
+ template <typename V>
+ 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<S> u;
+}
@@ -0,0 +1,46 @@
+// { 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) \
+ initializer (omp_priv ())
+#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 ();
+}
@@ -0,0 +1,48 @@
+// { 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) \
+ initializer (omp_priv ())
+#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 ();
+}
@@ -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 ();
+}
@@ -0,0 +1,89 @@
+// { 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)) \
+ initializer (omp_priv ())
+ #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 ();
+}
@@ -0,0 +1,150 @@
+// { dg-do run }
+
+extern "C" void abort ();
+
+void
+dblinit (double *p)
+{
+ *p = 2.0;
+}
+
+namespace NS
+{
+ template <int N>
+ struct U
+ {
+ void foo (U &, bool);
+ U ();
+ };
+ template <int N>
+ struct S
+ {
+ int s;
+ #pragma omp declare reduction (foo : U<0>, S : omp_out.foo (omp_in, false)) \
+ initializer (omp_priv ())
+ #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<N>::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 <int N>
+NS::S<N>::~S ()
+{
+ if (s < 6) abort ();
+ s = -1;
+ /* Ensure the above store is not DSEd. */
+ asm volatile ("" : : "r" (&s) : "memory");
+}
+
+template <int N>
+struct T : public NS::S<N>
+{
+ void baz ()
+ {
+ NS::S<N> 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 <typename T, typename D>
+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 <NS::S <0>, double> v;
+ v.baz ();
+}
@@ -0,0 +1,33 @@
+// { 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) \
+ initializer (omp_priv ())
+#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;
+}