aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSandra Loosemore <sloosemore@baylibre.com>2025-01-14 23:28:02 +0000
committerSandra Loosemore <sloosemore@baylibre.com>2025-01-16 18:12:17 +0000
commit677e452e55e5a91e699d4f01cc9b88297cc41a0d (patch)
tree0679252841b0aa1379e9ac5435d4ebba0180ebfc
parent4e20914d3306d8898ce586313a40fb92ef0b8964 (diff)
downloadgcc-677e452e55e5a91e699d4f01cc9b88297cc41a0d.zip
gcc-677e452e55e5a91e699d4f01cc9b88297cc41a0d.tar.gz
gcc-677e452e55e5a91e699d4f01cc9b88297cc41a0d.tar.bz2
OpenMP: C++ support for metadirectives and dynamic selectors.
Additional shared C/C++ testcases are included in a subsequent patch in this series. gcc/cp/ChangeLog PR middle-end/112779 PR middle-end/113904 * cp-tree.h (struct saved_scope): Add new field x_processing_omp_trait_property_expr. (processing_omp_trait_property_expr): New. * parser.cc (cp_parser_skip_to_end_of_block_or_statement): Add metadirective_p parameter and handle skipping over the parentheses in a "for" statement. (struct omp_metadirective_parse_data): New. (mangle_metadirective_region_label): New. (cp_parser_label_for_labeled_statement): Mangle label names in a metadirective body. (cp_parser_jump_statement): Likewise. (cp_parser_omp_context_selector): Allow arbitrary expressions in device_num and condition properties. (cp_parser_omp_assumption_clauses): Handle C_OMP_DIR_META. (analyze_metadirective_body): New. (cp_parser_omp_metadirective): New. (cp_parser_pragma): Handle PRAGMA_OMP_METADIRECTIVE. * parser.h (struct cp_parser): Add omp_metadirective_state field. * pt.cc (tsubst_omp_context_selector): New. (tsubst_stmt): Handle OMP_METADIRECTIVE. * semantics.cc (finish_id_expression_1): Don't diagnose use of parameter outside function body in dynamic selector expressions here. gcc/testsuite/ PR middle-end/112779 PR middle-end/113904 * c-c++-common/gomp/declare-variant-2.c: Adjust output for C++. * g++.dg/gomp/declare-variant-class-1.C: New. * g++.dg/gomp/declare-variant-class-2.C: New. * g++.dg/gomp/metadirective-template-1.C: New. libgomp/ PR middle-end/112779 PR middle-end/113904 * testsuite/libgomp.c++/metadirective-template-1.C: New. * testsuite/libgomp.c++/metadirective-template-2.C: New. * testsuite/libgomp.c++/metadirective-template-3.C: New. Co-Authored-By: Kwok Cheung Yeung <kcy@codesourcery.com> Co-Authored-By: Sandra Loosemore <sandra@codesourcery.com>
-rw-r--r--gcc/cp/cp-tree.h2
-rw-r--r--gcc/cp/parser.cc541
-rw-r--r--gcc/cp/parser.h6
-rw-r--r--gcc/cp/pt.cc126
-rw-r--r--gcc/cp/semantics.cc3
-rw-r--r--gcc/testsuite/c-c++-common/gomp/declare-variant-2.c4
-rw-r--r--gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C32
-rw-r--r--gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C37
-rw-r--r--gcc/testsuite/g++.dg/gomp/metadirective-template-1.C74
-rw-r--r--libgomp/testsuite/libgomp.c++/metadirective-template-1.C39
-rw-r--r--libgomp/testsuite/libgomp.c++/metadirective-template-2.C43
-rw-r--r--libgomp/testsuite/libgomp.c++/metadirective-template-3.C43
12 files changed, 927 insertions, 23 deletions
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index bb10c7c..269aca5 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1950,6 +1950,7 @@ struct GTY(()) saved_scope {
int suppress_location_wrappers;
BOOL_BITFIELD x_processing_explicit_instantiation : 1;
BOOL_BITFIELD need_pop_function_context : 1;
+ BOOL_BITFIELD x_processing_omp_trait_property_expr : 1;
/* Nonzero if we are parsing the discarded statement of a constexpr
if-statement. */
@@ -2021,6 +2022,7 @@ extern GTY(()) struct saved_scope *scope_chain;
#define processing_template_decl scope_chain->x_processing_template_decl
#define processing_specialization scope_chain->x_processing_specialization
#define processing_explicit_instantiation scope_chain->x_processing_explicit_instantiation
+#define processing_omp_trait_property_expr scope_chain->x_processing_omp_trait_property_expr
/* Nonzero if we are parsing the conditional expression of a contract
condition. These expressions appear outside the paramter list (like a
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 2d7ea9f..74f4f7c 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -3020,7 +3020,7 @@ static void cp_parser_skip_to_end_of_statement
static void cp_parser_consume_semicolon_at_end_of_statement
(cp_parser *);
static void cp_parser_skip_to_end_of_block_or_statement
- (cp_parser *);
+ (cp_parser *, bool = false);
static bool cp_parser_skip_to_closing_brace
(cp_parser *);
static bool cp_parser_skip_entire_template_parameter_list
@@ -4245,9 +4245,11 @@ cp_parser_consume_semicolon_at_end_of_statement (cp_parser *parser)
have consumed a non-nested `;'. */
static void
-cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser)
+cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser,
+ bool metadirective_p)
{
int nesting_depth = 0;
+ int bracket_depth = 0;
/* Unwind generic function template scope if necessary. */
if (parser->fully_implicit_function_template_p)
@@ -4269,7 +4271,7 @@ cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser)
case CPP_SEMICOLON:
/* Stop if this is an unnested ';'. */
- if (!nesting_depth)
+ if (!nesting_depth && (!metadirective_p || bracket_depth <= 0))
nesting_depth = -1;
break;
@@ -4288,6 +4290,19 @@ cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser)
nesting_depth++;
break;
+ case CPP_OPEN_PAREN:
+ /* Track parentheses in case the statement is a standalone 'for'
+ statement - we want to skip over the semicolons separating the
+ operands. */
+ if (metadirective_p && nesting_depth == 0)
+ bracket_depth++;
+ break;
+
+ case CPP_CLOSE_PAREN:
+ if (metadirective_p && nesting_depth == 0)
+ bracket_depth--;
+ break;
+
case CPP_KEYWORD:
if (!cp_token_is_module_directive (token))
break;
@@ -13263,6 +13278,33 @@ attr_chainon (tree attrs, tree attr)
return chainon (attrs, attr);
}
+
+/* State object for mangling labels in a metadirective region. */
+
+/* Information used while parsing an OpenMP metadirective. */
+struct omp_metadirective_parse_data {
+ /* These fields are used to unique-ify labels when reparsing the
+ code in a metadirective alternative. */
+ vec<tree> * GTY((skip)) body_labels;
+ unsigned int region_num;
+};
+
+/* Helper function for cp_parser_label: mangle a metadirective region
+ label NAME if necessary. */
+static tree
+mangle_metadirective_region_label (cp_parser *parser, tree name)
+{
+ if (parser->omp_metadirective_state->body_labels->contains (name))
+ {
+ const char *old_name = IDENTIFIER_POINTER (name);
+ char *new_name = (char *) alloca (strlen (old_name) + 32);
+ sprintf (new_name, "%s_MDR%u", old_name,
+ parser->omp_metadirective_state->region_num);
+ return get_identifier (new_name);
+ }
+ return name;
+}
+
/* Parse the label for a labeled-statement, i.e.
label:
@@ -13365,7 +13407,11 @@ cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes)
default:
/* Anything else must be an ordinary label. */
- label = finish_label_stmt (cp_parser_identifier (parser));
+ cp_expr identifier = cp_parser_identifier (parser);
+ if (identifier != error_mark_node
+ && parser->omp_metadirective_state)
+ *identifier = mangle_metadirective_region_label (parser, *identifier);
+ label = finish_label_stmt (identifier);
if (label && TREE_CODE (label) == LABEL_DECL)
{
FALLTHROUGH_LABEL_P (label) = fallthrough_p;
@@ -15240,7 +15286,13 @@ cp_parser_jump_statement (cp_parser* parser, tree &std_attrs)
finish_goto_stmt (cp_parser_expression (parser));
}
else
- finish_goto_stmt (cp_parser_identifier (parser));
+ {
+ cp_expr identifier = cp_parser_identifier (parser);
+ if (identifier != error_mark_node && parser->omp_metadirective_state)
+ *identifier = mangle_metadirective_region_label (parser,
+ *identifier);
+ finish_goto_stmt (identifier);
+ }
/* Look for the final `;'. */
cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
break;
@@ -49577,23 +49629,25 @@ cp_parser_omp_context_selector (cp_parser *parser, enum omp_tss_code set,
break;
case OMP_TRAIT_PROPERTY_DEV_NUM_EXPR:
case OMP_TRAIT_PROPERTY_BOOL_EXPR:
- /* FIXME: this is bogus, the expression need
- not be constant. */
- t = cp_parser_constant_expression (parser);
- if (t != error_mark_node)
+ processing_omp_trait_property_expr = true;
+ /* This actually parses a not-necessarily-constant
+ conditional-expression. */
+ t = cp_parser_constant_expression (parser, true, NULL, false);
+ processing_omp_trait_property_expr = false;
+ if (t == error_mark_node)
+ return error_mark_node;
+ if (!type_dependent_expression_p (t)
+ && !value_dependent_expression_p (t))
{
t = fold_non_dependent_expr (t);
- if (!value_dependent_expression_p (t)
- && (!INTEGRAL_TYPE_P (TREE_TYPE (t))
- || !tree_fits_shwi_p (t)))
- error_at (token->location, "property must be "
- "constant integer expression");
- else
- properties = make_trait_property (NULL_TREE, t,
- properties);
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (t)))
+ {
+ error_at (token->location,
+ "property must be integer expression");
+ return error_mark_node;
+ }
}
- else
- return error_mark_node;
+ properties = make_trait_property (NULL_TREE, t, properties);
break;
case OMP_TRAIT_PROPERTY_CLAUSE_LIST:
if (sel == OMP_TRAIT_CONSTRUCT_SIMD)
@@ -49836,6 +49890,7 @@ cp_parser_omp_assumption_clauses (cp_parser *parser, cp_token *pragma_tok,
if (dir == NULL
|| dir->kind == C_OMP_DIR_DECLARATIVE
|| dir->kind == C_OMP_DIR_INFORMATIONAL
+ || dir->kind == C_OMP_DIR_META
|| dir->id == PRAGMA_OMP_END
|| (!dir->second && directive[1])
|| (!dir->third && directive[2]))
@@ -51005,6 +51060,450 @@ cp_parser_omp_end (cp_parser *parser, cp_token *pragma_tok)
}
}
+
+/* Helper function for cp_parser_omp_metadirective. */
+
+static void
+analyze_metadirective_body (cp_parser *parser,
+ vec<cp_token> &tokens,
+ vec<tree> &labels)
+{
+ int nesting_depth = 0;
+ int bracket_depth = 0;
+ bool in_case = false;
+ bool in_label_decl = false;
+ cp_token *pragma_tok = NULL;
+
+ while (1)
+ {
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+ bool stop = false;
+
+ if (cp_lexer_next_token_is_keyword (parser->lexer, RID_CASE))
+ in_case = true;
+ else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL))
+ in_label_decl = true;
+
+ switch (token->type)
+ {
+ case CPP_EOF:
+ break;
+ case CPP_NAME:
+ if ((!in_case
+ && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON))
+ || in_label_decl)
+ labels.safe_push (token->u.value);
+ goto add;
+ case CPP_OPEN_BRACE:
+ ++nesting_depth;
+ goto add;
+ case CPP_CLOSE_BRACE:
+ if (--nesting_depth == 0 && bracket_depth == 0)
+ stop = true;
+ goto add;
+ case CPP_OPEN_PAREN:
+ ++bracket_depth;
+ goto add;
+ case CPP_CLOSE_PAREN:
+ --bracket_depth;
+ goto add;
+ case CPP_COLON:
+ in_case = false;
+ goto add;
+ case CPP_SEMICOLON:
+ if (nesting_depth == 0 && bracket_depth == 0)
+ stop = true;
+ /* Local label declarations are terminated by a semicolon. */
+ in_label_decl = false;
+ goto add;
+ case CPP_PRAGMA:
+ parser->lexer->in_pragma = true;
+ pragma_tok = token;
+ goto add;
+ case CPP_PRAGMA_EOL:
+ /* C++ attribute syntax for OMP directives lexes as a pragma,
+ but we must reset the associated lexer state when we reach
+ the end in order to get the tokens for the statement that
+ come after it. */
+ tokens.safe_push (*token);
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+ pragma_tok = NULL;
+ continue;
+ default:
+ add:
+ tokens.safe_push (*token);
+ cp_lexer_consume_token (parser->lexer);
+ if (stop)
+ break;
+ continue;
+ }
+ break;
+ }
+}
+
+/* OpenMP 5.0:
+
+ # pragma omp metadirective [clause[, clause]]
+*/
+
+static void
+cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok,
+ bool *if_p)
+{
+ static unsigned int metadirective_region_count = 0;
+
+ auto_vec<cp_token> directive_tokens;
+ auto_vec<cp_token> body_tokens;
+ auto_vec<tree> body_labels;
+ auto_vec<const struct c_omp_directive *> directives;
+ auto_vec<tree> ctxs;
+ bool default_seen = false;
+ int directive_token_idx = 0;
+ location_t pragma_loc = pragma_tok->location;
+ tree standalone_body = NULL_TREE;
+ vec<struct omp_variant> candidates;
+ bool requires_body = false;
+
+ tree ret = make_node (OMP_METADIRECTIVE);
+ SET_EXPR_LOCATION (ret, pragma_loc);
+ TREE_TYPE (ret) = void_type_node;
+ OMP_METADIRECTIVE_VARIANTS (ret) = NULL_TREE;
+
+ while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL))
+ {
+ if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+ cp_lexer_consume_token (parser->lexer);
+ if (cp_lexer_next_token_is_not (parser->lexer, CPP_NAME)
+ && cp_lexer_next_token_is_not (parser->lexer, CPP_KEYWORD))
+ {
+ cp_parser_error (parser, "expected %<when%>, "
+ "%<otherwise%>, or %<default%> clause");
+ goto fail;
+ }
+
+ location_t match_loc = cp_lexer_peek_token (parser->lexer)->location;
+ const char *p
+ = IDENTIFIER_POINTER (cp_lexer_peek_token (parser->lexer)->u.value);
+ cp_lexer_consume_token (parser->lexer);
+ bool default_p
+ = strcmp (p, "default") == 0 || strcmp (p, "otherwise") == 0;
+ if (default_p)
+ {
+ if (default_seen)
+ {
+ error_at (match_loc, "too many %<otherwise%> or %<default%> "
+ "clauses in %<metadirective%>");
+ cp_parser_skip_to_end_of_block_or_statement (parser, true);
+ goto fail;
+ }
+ else
+ default_seen = true;
+ }
+ else if (default_seen)
+ {
+ error_at (match_loc, "%<otherwise%> or %<default%> clause "
+ "must appear last in %<metadirective%>");
+ cp_parser_skip_to_end_of_block_or_statement (parser, true);
+ goto fail;
+ }
+ if (!strcmp (p, "when") == 0 && !default_p)
+ {
+ error_at (match_loc, "%qs is not valid for %qs",
+ p, "metadirective");
+ cp_parser_skip_to_end_of_block_or_statement (parser, true);
+ goto fail;
+ }
+
+ matching_parens parens;
+ tree ctx = NULL_TREE;
+ bool skip = false;
+
+ if (!parens.require_open (parser))
+ goto fail;
+
+ if (!default_p)
+ {
+ ctx = cp_parser_omp_context_selector_specification (parser, false);
+ if (ctx == error_mark_node)
+ goto fail;
+ ctx = omp_check_context_selector (match_loc, ctx, true);
+ if (ctx == error_mark_node)
+ goto fail;
+
+ /* Remove the selector from further consideration if it can be
+ evaluated as a non-match at this point. */
+ /* FIXME: we could still do this if the context selector
+ doesn't have any dependent subexpressions. */
+ skip = (!processing_template_decl
+ && !omp_context_selector_matches (ctx, NULL_TREE, false));
+ if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
+ {
+ cp_parser_require (parser, CPP_COLON, RT_COLON);
+ goto fail;
+ }
+ cp_lexer_consume_token (parser->lexer);
+ }
+
+ /* Read in the directive type and create a dummy pragma token for
+ it. */
+ location_t loc = cp_lexer_peek_token (parser->lexer)->location;
+
+ const char *directive[3] = {};
+ int i;
+ for (i = 0; i < 3; i++)
+ {
+ tree id;
+ if (cp_lexer_peek_nth_token (parser->lexer, i + 1)->type
+ == CPP_CLOSE_PAREN)
+ {
+ if (i == 0)
+ directive[i++] = "nothing";
+ break;
+ }
+ else if (cp_lexer_peek_nth_token (parser->lexer, i + 1)->type
+ == CPP_NAME)
+ id = cp_lexer_peek_nth_token (parser->lexer, i + 1)->u.value;
+ else if (cp_lexer_peek_nth_token (parser->lexer, i + 1)->keyword
+ != RID_MAX)
+ {
+ enum rid rid
+ = cp_lexer_peek_nth_token (parser->lexer, i + 1)->keyword;
+ id = ridpointers[rid];
+ }
+ else
+ break;
+
+ directive[i] = IDENTIFIER_POINTER (id);
+ }
+ if (i == 0)
+ {
+ error_at (loc, "expected directive name");
+ cp_parser_skip_to_end_of_block_or_statement (parser, true);
+ goto fail;
+ }
+
+ const struct c_omp_directive *omp_directive
+ = c_omp_categorize_directive (directive[0],
+ directive[1],
+ directive[2]);
+
+ if (omp_directive == NULL)
+ {
+ for (int j = 0; j < i; j++)
+ cp_lexer_consume_token (parser->lexer);
+ cp_parser_error (parser, "unknown directive name");
+ goto fail;
+ }
+ else
+ {
+ int token_count = 0;
+ if (omp_directive->first) token_count++;
+ if (omp_directive->second) token_count++;
+ if (omp_directive->third) token_count++;
+ for (int j = 0; j < token_count; j++)
+ cp_lexer_consume_token (parser->lexer);
+ }
+ if (p == NULL)
+ {
+ cp_parser_error (parser, "expected directive name");
+ goto fail;
+ }
+ if (omp_directive->id == PRAGMA_OMP_METADIRECTIVE)
+ {
+ cp_parser_error (parser,
+ "metadirectives cannot be used as variants of a "
+ "%<metadirective%>");
+ goto fail;
+ }
+ if (omp_directive->kind == C_OMP_DIR_DECLARATIVE)
+ {
+ sorry_at (loc, "declarative directive variants of a "
+ "%<metadirective%> are not supported");
+ goto fail;
+ }
+ if (omp_directive->kind == C_OMP_DIR_CONSTRUCT)
+ requires_body = true;
+
+ if (!skip)
+ {
+ cp_token pragma_token;
+ pragma_token.type = CPP_PRAGMA;
+ pragma_token.location = loc;
+ pragma_token.u.value = build_int_cst (NULL, omp_directive->id);
+
+ directives.safe_push (omp_directive);
+ directive_tokens.safe_push (pragma_token);
+ ctxs.safe_push (ctx);
+ }
+
+ /* Read in tokens for the directive clauses. */
+ int nesting_depth = 0;
+ while (1)
+ {
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+ switch (token->type)
+ {
+ case CPP_EOF:
+ case CPP_PRAGMA_EOL:
+ break;
+ case CPP_OPEN_PAREN:
+ ++nesting_depth;
+ goto add;
+ case CPP_CLOSE_PAREN:
+ if (nesting_depth-- == 0)
+ break;
+ goto add;
+ default:
+ add:
+ if (!skip)
+ directive_tokens.safe_push (*token);
+ cp_lexer_consume_token (parser->lexer);
+ continue;
+ }
+ break;
+ }
+
+ cp_lexer_consume_token (parser->lexer);
+
+ if (!skip)
+ {
+ cp_token eol_token = {};
+ eol_token.type = CPP_PRAGMA_EOL;
+ eol_token.keyword = RID_MAX;
+ directive_tokens.safe_push (eol_token);
+ }
+ }
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+
+ if (!default_seen)
+ {
+ /* Add a default clause that evaluates to 'omp nothing'. */
+ const struct c_omp_directive *omp_directive
+ = c_omp_categorize_directive ("nothing", NULL, NULL);
+
+ cp_token pragma_token = {};
+ pragma_token.type = CPP_PRAGMA;
+ pragma_token.keyword = RID_MAX;
+ pragma_token.location = UNKNOWN_LOCATION;
+ pragma_token.u.value = build_int_cst (NULL, PRAGMA_OMP_NOTHING);
+
+ directives.safe_push (omp_directive);
+ directive_tokens.safe_push (pragma_token);
+ ctxs.safe_push (NULL_TREE);
+
+ cp_token eol_token = {};
+ eol_token.type = CPP_PRAGMA_EOL;
+ eol_token.keyword = RID_MAX;
+ directive_tokens.safe_push (eol_token);
+ }
+
+ if (requires_body)
+ analyze_metadirective_body (parser, body_tokens, body_labels);
+
+ /* Process each candidate directive. */
+ unsigned i;
+ tree ctx;
+ cp_lexer *lexer;
+
+ lexer = cp_lexer_alloc ();
+ lexer->debugging_p = parser->lexer->debugging_p;
+ vec_safe_reserve (lexer->buffer,
+ directive_tokens.length () + body_tokens.length () + 2);
+
+ FOR_EACH_VEC_ELT (ctxs, i, ctx)
+ {
+ lexer->buffer->truncate (0);
+
+ /* Add the directive tokens. */
+ do
+ lexer->buffer->quick_push (directive_tokens [directive_token_idx++]);
+ while (lexer->buffer->last ().type != CPP_PRAGMA_EOL);
+
+ /* Add the body tokens. */
+ gcc_assert (requires_body || body_tokens.is_empty ());
+ for (unsigned j = 0; j < body_tokens.length (); j++)
+ lexer->buffer->quick_push (body_tokens[j]);
+
+ /* Make sure nothing tries to read past the end of the tokens. */
+ cp_token eof_token = {};
+ eof_token.type = CPP_EOF;
+ eof_token.keyword = RID_MAX;
+ lexer->buffer->quick_push (eof_token);
+ lexer->buffer->quick_push (eof_token);
+
+ lexer->next_token = lexer->buffer->address();
+ lexer->last_token = lexer->next_token + lexer->buffer->length () - 1;
+
+ cp_lexer *old_lexer = parser->lexer;
+ struct omp_metadirective_parse_data *old_state
+ = parser->omp_metadirective_state;
+ parser->lexer = lexer;
+ cp_lexer_set_source_position_from_token (lexer->next_token);
+ struct omp_metadirective_parse_data new_state;
+ new_state.body_labels = &body_labels;
+ new_state.region_num = ++metadirective_region_count;
+ parser->omp_metadirective_state = &new_state;
+
+ int prev_errorcount = errorcount;
+ tree directive = push_stmt_list ();
+ tree directive_stmt = begin_compound_stmt (0);
+
+ cp_parser_pragma (parser, pragma_compound, if_p);
+ finish_compound_stmt (directive_stmt);
+ directive = pop_stmt_list (directive);
+
+ bool standalone_p
+ = directives[i]->kind == C_OMP_DIR_STANDALONE
+ || directives[i]->kind == C_OMP_DIR_UTILITY;
+ if (standalone_p && requires_body)
+ {
+ /* Parsing standalone directives will not consume the body
+ tokens, so do that here. */
+ if (standalone_body == NULL_TREE)
+ {
+ standalone_body = push_stmt_list ();
+ cp_parser_statement (parser, NULL_TREE, false, if_p);
+ standalone_body = pop_stmt_list (standalone_body);
+ }
+ else
+ cp_parser_skip_to_end_of_block_or_statement (parser, true);
+ }
+
+ tree body = standalone_p ? standalone_body : NULL_TREE;
+ tree variant = make_omp_metadirective_variant (ctx, directive, body);
+ OMP_METADIRECTIVE_VARIANTS (ret)
+ = chainon (OMP_METADIRECTIVE_VARIANTS (ret), variant);
+
+ /* Check that all valid tokens have been consumed if no parse errors
+ encountered. */
+ gcc_assert (errorcount != prev_errorcount
+ || cp_lexer_next_token_is (parser->lexer, CPP_EOF));
+
+ parser->lexer = old_lexer;
+ cp_lexer_set_source_position_from_token (old_lexer->next_token);
+ parser->omp_metadirective_state = old_state;
+ }
+
+ /* Try to resolve the metadirective early. */
+ if (!processing_template_decl)
+ {
+ candidates = omp_early_resolve_metadirective (ret);
+ if (!candidates.is_empty ())
+ ret = c_omp_expand_variant_construct (candidates);
+ }
+
+ add_stmt (ret);
+ return;
+
+fail:
+ /* Skip the metadirective pragma. */
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+
+ /* Skip the metadirective body. */
+ cp_parser_skip_to_end_of_block_or_statement (parser, true);
+}
+
+
/* Helper function of cp_parser_omp_declare_reduction. Parse the combiner
expression and optional initializer clause of
#pragma omp declare reduction. We store the expression(s) as
@@ -53017,6 +53516,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p)
cp_parser_omp_nothing (parser, pragma_tok);
return false;
+ case PRAGMA_OMP_METADIRECTIVE:
+ cp_parser_omp_metadirective (parser, pragma_tok, if_p);
+ return true;
+
case PRAGMA_OMP_ERROR:
return cp_parser_omp_error (parser, pragma_tok, context);
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 1d28743..f9ed801 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -450,6 +450,12 @@ struct GTY(()) cp_parser {
/* Pointer to state for parsing omp_loops. Managed by
cp_parser_omp_for_loop in parser.cc and not used outside that file. */
struct omp_for_parse_data * GTY((skip)) omp_for_parse_state;
+
+ /* Non-null only when parsing the body of an OpenMP metadirective.
+ Managed by cp_parser_omp_metadirective in parser.cc and not used
+ outside that file. */
+ struct omp_metadirective_parse_data * GTY((skip))
+ omp_metadirective_state;
};
/* In parser.cc */
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 3b6743f..961696f 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -18152,6 +18152,86 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
return new_clauses;
}
+/* Like tsubst_copy, but specifically for OpenMP context selectors. */
+static tree
+tsubst_omp_context_selector (tree ctx, tree args, tsubst_flags_t complain,
+ tree in_decl)
+{
+ tree new_ctx = NULL_TREE;
+ for (tree set = ctx; set; set = TREE_CHAIN (set))
+ {
+ tree selectors = NULL_TREE;
+ for (tree sel = OMP_TSS_TRAIT_SELECTORS (set); sel;
+ sel = TREE_CHAIN (sel))
+ {
+ enum omp_ts_code code = OMP_TS_CODE (sel);
+ tree properties = NULL_TREE;
+ tree score = OMP_TS_SCORE (sel);
+ tree t;
+
+ if (score)
+ {
+ score = tsubst_expr (score, args, complain, in_decl);
+ score = fold_non_dependent_expr (score);
+ if (!value_dependent_expression_p (score)
+ && !type_dependent_expression_p (score))
+ {
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (score))
+ || TREE_CODE (score) != INTEGER_CST)
+ {
+ error_at (cp_expr_loc_or_input_loc (score),
+ "%<score%> argument must "
+ "be constant integer expression");
+ score = NULL_TREE;
+ }
+ else if (tree_int_cst_sgn (score) < 0)
+ {
+ error_at (cp_expr_loc_or_input_loc (score),
+ "%<score%> argument must be non-negative");
+ score = NULL_TREE;
+ }
+ }
+ }
+
+ switch (omp_ts_map[OMP_TS_CODE (sel)].tp_type)
+ {
+ case OMP_TRAIT_PROPERTY_DEV_NUM_EXPR:
+ case OMP_TRAIT_PROPERTY_BOOL_EXPR:
+ t = tsubst_expr (OMP_TP_VALUE (OMP_TS_PROPERTIES (sel)),
+ args, complain, in_decl);
+ t = fold_non_dependent_expr (t);
+ if (!value_dependent_expression_p (t)
+ && !type_dependent_expression_p (t)
+ && !INTEGRAL_TYPE_P (TREE_TYPE (t)))
+ error_at (cp_expr_loc_or_input_loc (t),
+ "property must be integer expression");
+ else
+ properties = make_trait_property (NULL_TREE, t, NULL_TREE);
+ break;
+ case OMP_TRAIT_PROPERTY_CLAUSE_LIST:
+ if (OMP_TS_CODE (sel) == OMP_TRAIT_CONSTRUCT_SIMD)
+ properties = tsubst_omp_clauses (OMP_TS_PROPERTIES (sel),
+ C_ORT_OMP_DECLARE_SIMD,
+ args, complain, in_decl);
+ break;
+ default:
+ /* Nothing to do here, just copy. */
+ for (tree prop = OMP_TS_PROPERTIES (sel);
+ prop; prop = TREE_CHAIN (prop))
+ properties = make_trait_property (OMP_TP_NAME (prop),
+ OMP_TP_VALUE (prop),
+ properties);
+ }
+ selectors = make_trait_selector (code, score, properties, selectors);
+ }
+ new_ctx = make_trait_set_selector (OMP_TSS_CODE (set),
+ nreverse (selectors),
+ new_ctx);
+ }
+ return nreverse (new_ctx);
+}
+
+
/* Like tsubst_expr, but unshare TREE_LIST nodes. */
static tree
@@ -19766,6 +19846,52 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
}
break;
+ case OMP_METADIRECTIVE:
+ {
+ tree variants = NULL_TREE;
+ for (tree v = OMP_METADIRECTIVE_VARIANTS (t); v; v = TREE_CHAIN (v))
+ {
+ tree ctx = OMP_METADIRECTIVE_VARIANT_SELECTOR (v);
+ tree directive = OMP_METADIRECTIVE_VARIANT_DIRECTIVE (v);
+ tree body = OMP_METADIRECTIVE_VARIANT_BODY (v);
+ tree s;
+
+ /* CTX is null if this is the default variant. */
+ if (ctx)
+ {
+ ctx = tsubst_omp_context_selector (ctx, args, complain,
+ in_decl);
+ /* Remove the selector from further consideration if it can be
+ evaluated as a non-match at this point. */
+ if (omp_context_selector_matches (ctx, NULL_TREE, false) == 0)
+ continue;
+ }
+ s = push_stmt_list ();
+ RECUR (directive);
+ directive = pop_stmt_list (s);
+ if (body)
+ {
+ s = push_stmt_list ();
+ RECUR (body);
+ body = pop_stmt_list (s);
+ }
+ variants
+ = chainon (variants,
+ make_omp_metadirective_variant (ctx, directive,
+ body));
+ }
+ t = copy_node (t);
+ OMP_METADIRECTIVE_VARIANTS (t) = variants;
+
+ /* Try to resolve the metadirective early. */
+ vec<struct omp_variant> candidates
+ = omp_early_resolve_metadirective (t);
+ if (!candidates.is_empty ())
+ t = c_omp_expand_variant_construct (candidates);
+ add_stmt (t);
+ break;
+ }
+
case TRANSACTION_EXPR:
{
int flags = 0;
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index ce9ef1f..2daad2d 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -4673,7 +4673,8 @@ finish_id_expression_1 (tree id_expression,
if (TREE_CODE (decl) == PARM_DECL
&& DECL_CONTEXT (decl) == NULL_TREE
&& !cp_unevaluated_operand
- && !processing_contract_condition)
+ && !processing_contract_condition
+ && !processing_omp_trait_property_expr)
{
*error_msg = G_("use of parameter outside function body");
return error_mark_node;
diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c b/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c
index 19e40f6..7711dbc 100644
--- a/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c
+++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c
@@ -38,9 +38,7 @@ void f18 (void);
void f19 (void);
#pragma omp declare variant (f1) match(user={condition()}) /* { dg-error "expected \[^\n\r]*expression before '\\)' token" } */
void f20 (void);
-#pragma omp declare variant (f1) match(user={condition(f1)}) /* { dg-error "property must be integer expression" "" { target c } } */
-/* { dg-error "cannot appear in a constant-expression" "" { target c++98_only } .-1 } */
-/* { dg-error "property must be constant integer expression" "" { target { c++11 } } .-2 } */
+#pragma omp declare variant (f1) match(user={condition(f1)}) /* { dg-error "property must be integer expression" } */
void f21 (void);
#pragma omp declare variant (f1) match(user={condition(1, 2, 3)}) /* { dg-error "expected '\\)' before ',' token" } */
void f22 (void);
diff --git a/gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C b/gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C
new file mode 100644
index 0000000..35b25de
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-foffload=disable" } */
+/* { dg-additional-options "-mavx512bw -mavx512vl" { target { i?86-*-* x86_64-*-* } } } */
+
+/* References to function parameters in dynamic selector expressions for
+ "declare variant" isn't supported yet; see PR 113904. Check to see that
+ a proper error is diagnosed meanwhile and GCC doesn't just wander off
+ into the weeds and ICE. */
+
+extern int frob (int);
+
+class junk
+{
+ public:
+ int data;
+ static void f01 (int, int);
+ static void f02 (int, int);
+ static void f03 (int, int);
+#pragma omp declare variant (f01) match (target_device={device_num (devnum), isa("avx512f","avx512vl")}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */
+#pragma omp declare variant (f02) match (implementation={vendor(score(15):gnu)})
+#pragma omp declare variant (f03) match (user={condition(score(11):frob (ok + 42))}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */
+ static void f04 (int devnum, int ok);
+};
+
+void
+test1 (void)
+{
+ int i;
+ #pragma omp parallel for
+ for (i = 0; i < 1; i++)
+ junk::f04 (17, 1);
+}
diff --git a/gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C b/gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C
new file mode 100644
index 0000000..b30243f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-foffload=disable" } */
+/* { dg-additional-options "-mavx512bw -mavx512vl" { target { i?86-*-* x86_64-*-* } } } */
+
+/* References to function parameters in dynamic selector expressions for
+ "declare variant" isn't supported yet; see PR 113904. Check to see that
+ a proper error is diagnosed meanwhile and GCC doesn't just wander off
+ into the weeds and ICE. */
+
+extern int frob (int);
+extern int frobmore (class junk *);
+
+class junk
+{
+ public:
+ int data;
+ void f01 (int, int);
+ void f02 (int, int);
+ void f03 (int, int);
+ void f04 (int, int);
+ void f05 (int, int);
+#pragma omp declare variant (f01) match (target_device={device_num (devnum), isa("avx512f","avx512vl")}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */
+#pragma omp declare variant (f02) match (implementation={vendor(score(15):gnu)})
+#pragma omp declare variant (f03) match (user={condition(score(11):frob (ok + 42))}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */
+#pragma omp declare variant (f04) match (user={condition(score(11):data)}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */
+#pragma omp declare variant (f05) match (user={condition(score(11):frobmore (this))}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */
+ void f06 (int devnum, int ok);
+};
+
+void
+test1 (junk *j)
+{
+ int i;
+ #pragma omp parallel for
+ for (i = 0; i < 1; i++)
+ j->f06 (17, 1);
+}
diff --git a/gcc/testsuite/g++.dg/gomp/metadirective-template-1.C b/gcc/testsuite/g++.dg/gomp/metadirective-template-1.C
new file mode 100644
index 0000000..92e72ba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/metadirective-template-1.C
@@ -0,0 +1,74 @@
+// { dg-do compile }
+// { dg-additional-options "-fdump-tree-gimple" }
+
+// Check that expressions in metadirectives are handled correctly in template
+// functions when they are value-dependent and/or type-dependent.
+
+int x;
+
+// Type-dependent expression.
+template <typename T>
+void
+f1 (void)
+{
+#pragma omp metadirective \
+ when (user={condition(static_cast<T> (1.0))}: target)
+ x = 1;
+}
+
+// Value-dependent expression.
+template <int N>
+void
+f2 (void)
+{
+#pragma omp metadirective \
+ when (user={condition(N)}: target)
+ x = 2;
+}
+
+// Both type- and value-dependent.
+template <typename T, T N>
+void
+f3 (void)
+{
+#pragma omp metadirective \
+ when (user={condition(N)}: target)
+ x = 3;
+}
+
+// Expression is itself a template instantiation.
+template <typename T, T N>
+bool
+test (void)
+{
+ return N != 0;
+}
+
+template <typename T, T N>
+void
+f4 (void)
+{
+#pragma omp metadirective \
+ when (user={condition(test<T, N> ())}: teams)
+ x = 4;
+}
+
+int
+main (void)
+{
+ f1 <int> ();
+ f2 <1> ();
+ f2 <0> ();
+ f3 <int, 1> ();
+ f3 <int, 0> ();
+ f4 <int, 1> ();
+ f4 <int, 0> ();
+}
+
+// Each of f1..f3 should be instantiated once with a condition expression
+// that is a constant 1, producing a target construct. The metadirective in
+// f4 has a non-constant condition expression, so both instantiations will
+// produce a conditional including an alternative including a teams construct.
+
+// { dg-final { scan-tree-dump-times "pragma omp target" 3 "gimple" } }
+// { dg-final { scan-tree-dump-times "pragma omp teams" 2 "gimple" } }
diff --git a/libgomp/testsuite/libgomp.c++/metadirective-template-1.C b/libgomp/testsuite/libgomp.c++/metadirective-template-1.C
new file mode 100644
index 0000000..4511536
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/metadirective-template-1.C
@@ -0,0 +1,39 @@
+#include <stdio.h>
+
+template <bool tasking>
+int
+fib (int n)
+{
+ int i, j;
+ if (n < 2)
+ return n;
+ else if ( tasking && n < 8 ) // serial/taskless cutoff for n<8
+ return fib<false> (n);
+ else
+ {
+#pragma omp metadirective \
+ when (user = {condition (tasking)}: task shared(i))
+ i = fib<tasking> (n - 1);
+#pragma omp metadirective \
+ when (user = {condition (score(10): tasking)}: task shared(j))
+ j = fib<tasking> (n - 2);
+#pragma omp metadirective \
+ when (user = {condition (tasking)}: taskwait)
+ return i + j;
+ }
+}
+
+int
+main ()
+{
+ int n = 15, o = 610;
+#pragma omp parallel
+#pragma omp single
+ {
+ if (fib<true> (n) != o)
+ __builtin_abort ();
+ if (fib<false> (n) != o)
+ __builtin_abort ();
+ }
+ return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/metadirective-template-2.C b/libgomp/testsuite/libgomp.c++/metadirective-template-2.C
new file mode 100644
index 0000000..a116bfd
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/metadirective-template-2.C
@@ -0,0 +1,43 @@
+# include <stdio.h>
+
+template <bool tasking>
+int
+fib (int n, bool flag)
+{
+ int i, j;
+ if (n < 2)
+ return n;
+ else if ( tasking && flag && n < 8 ) // serial/taskless cutoff for n<8
+ return fib<false> (n, false);
+ else
+ {
+#pragma omp metadirective \
+ when (user = {condition (tasking && flag)}: task shared(i))
+ i = fib<tasking> (n - 1, flag);
+#pragma omp metadirective \
+ when (user = {condition (score(10): tasking && flag)}: task shared(j))
+ j = fib<tasking> (n - 2, flag);
+#pragma omp metadirective \
+ when (user = {condition (tasking && flag)}: taskwait)
+ return i + j;
+ }
+}
+
+int
+main ()
+{
+ int n = 15, o = 610;
+#pragma omp parallel
+#pragma omp single
+ {
+ if (fib<true> (n, true) != o)
+ __builtin_abort ();
+ if (fib<true> (n, false) != o)
+ __builtin_abort ();
+ if (fib<false> (n, false) != o)
+ __builtin_abort ();
+ if (fib<false> (n, true) != o)
+ __builtin_abort ();
+ }
+ return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/metadirective-template-3.C b/libgomp/testsuite/libgomp.c++/metadirective-template-3.C
new file mode 100644
index 0000000..934ae49
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/metadirective-template-3.C
@@ -0,0 +1,43 @@
+# include <stdio.h>
+
+template <bool tasking>
+int
+fib (int n, bool flag)
+{
+ int i, j;
+ if (n < 2)
+ return n;
+ else if ( tasking && flag && n < 8 ) // serial/taskless cutoff for n<8
+ return fib<false> (n, false);
+ else
+ {
+#pragma omp metadirective \
+ when (user = {condition (tasking && flag)}: task shared(i)) \
+ when (user = {condition (!tasking && !flag)}: nothing) \
+ otherwise (error at(execution) message("oops 1"))
+ i = fib<tasking> (n - 1, flag);
+#pragma omp metadirective \
+ when (user = {condition (score(10): tasking && flag)}: task shared(j)) \
+ when (user = {condition (tasking || flag)} : \
+ error at(execution) message ("oops 2"))
+ j = fib<tasking> (n - 2, flag);
+#pragma omp metadirective \
+ when (user = {condition (tasking && flag)}: taskwait)
+ return i + j;
+ }
+}
+
+int
+main ()
+{
+ int n = 15, o = 610;
+#pragma omp parallel
+#pragma omp single
+ {
+ if (fib<true> (n, true) != o)
+ __builtin_abort ();
+ if (fib<false> (n, false) != o)
+ __builtin_abort ();
+ }
+ return 0;
+}