Additional shared C/C++ testcases are included in a subsequent patch in this series.
gcc/c-family/ChangeLog PR middle-end/112779 PR middle-end/113904 * c-common.h (enum c_omp_directive_kind): Add C_OMP_DIR_META. (c_omp_expand_variant_construct): Declare. * c-gimplify.cc: Include omp-general.h. (genericize_omp_metadirective_stmt): New. (c_genericize_control_stmt): Add case for OMP_METADIRECTIVE. * c-omp.cc (c_omp_directives): Fix entries for metadirective. (c_omp_expand_variant_construct_r): New. (c_omp_expand_variant_construct): New. * c-pragma.cc (omp_pragmas): Add metadirective. * c-pragma.h (enum pragma_kind): Add PRAGMA_OMP_METADIRECTIVE. gcc/c/ChangeLog PR middle-end/112779 PR middle-end/113904 * c-parser.cc (struct c_parser): Add omp_metadirective_state field. (c_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. (c_parser_label): Mangle label names in a metadirective body. (c_parser_statement_after_labels): Likewise. (c_parser_pragma): Handle PRAGMA_OMP_METADIRECTIVE. (c_parser_omp_context_selector): Allow arbitrary expressions in device_num and condition properties. (analyze_metadirective_body): New. (c_parser_omp_metadirective): New. gcc/testsuite/ PR middle-end/112779 * c-c++-common/gomp/declare-variant-2.c: Adjust expected output for C. * gcc.dg/gomp/metadirective-1.c: New. Co-Authored-By: Kwok Cheung Yeung <k...@codesourcery.com> Co-Authored-By: Sandra Loosemore <san...@codesourcery.com> --- gcc/c-family/c-common.h | 4 +- gcc/c-family/c-gimplify.cc | 27 + gcc/c-family/c-omp.cc | 60 ++- gcc/c-family/c-pragma.cc | 1 + gcc/c-family/c-pragma.h | 1 + gcc/c/c-parser.cc | 488 +++++++++++++++++- .../c-c++-common/gomp/declare-variant-2.c | 6 +- gcc/testsuite/gcc.dg/gomp/metadirective-1.c | 15 + 8 files changed, 584 insertions(+), 18 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/gomp/metadirective-1.c diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index ac574e922e3..6c4a7a3efa0 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1413,7 +1413,8 @@ enum c_omp_directive_kind { C_OMP_DIR_CONSTRUCT, C_OMP_DIR_DECLARATIVE, C_OMP_DIR_UTILITY, - C_OMP_DIR_INFORMATIONAL + C_OMP_DIR_INFORMATIONAL, + C_OMP_DIR_META }; struct c_omp_directive { @@ -1427,6 +1428,7 @@ extern const struct c_omp_directive c_omp_directives[]; extern const struct c_omp_directive *c_omp_categorize_directive (const char *, const char *, const char *); +extern tree c_omp_expand_variant_construct (vec<struct omp_variant> &); /* Return next tree in the chain for chain_next walking of tree nodes. */ inline tree diff --git a/gcc/c-family/c-gimplify.cc b/gcc/c-family/c-gimplify.cc index d4b97b4a972..b1f6ea2a1a9 100644 --- a/gcc/c-family/c-gimplify.cc +++ b/gcc/c-family/c-gimplify.cc @@ -43,6 +43,7 @@ along with GCC; see the file COPYING3. If not see #include "context.h" #include "tree-pass.h" #include "internal-fn.h" +#include "omp-general.h" /* The gimplification pass converts the language-dependent trees (ld-trees) emitted by the parser into language-independent trees @@ -543,6 +544,27 @@ genericize_omp_for_stmt (tree *stmt_p, int *walk_subtrees, void *data, finish_bc_block (&OMP_FOR_BODY (stmt), bc_continue, clab); } +/* Genericize a OMP_METADIRECTIVE node *STMT_P. */ + +static void +genericize_omp_metadirective_stmt (tree *stmt_p, int *walk_subtrees, + void *data, walk_tree_fn func, + walk_tree_lh lh) +{ + tree stmt = *stmt_p; + + for (tree variant = OMP_METADIRECTIVE_VARIANTS (stmt); + variant != NULL_TREE; + variant = TREE_CHAIN (variant)) + { + walk_tree_1 (&OMP_METADIRECTIVE_VARIANT_DIRECTIVE (variant), + func, data, NULL, lh); + walk_tree_1 (&OMP_METADIRECTIVE_VARIANT_BODY (variant), + func, data, NULL, lh); + } + + *walk_subtrees = 0; +} /* Lower structured control flow tree nodes, such as loops. The STMT_P, WALK_SUBTREES, and DATA arguments are as for the walk_tree_fn @@ -593,6 +615,11 @@ c_genericize_control_stmt (tree *stmt_p, int *walk_subtrees, void *data, genericize_omp_for_stmt (stmt_p, walk_subtrees, data, func, lh); break; + case OMP_METADIRECTIVE: + genericize_omp_metadirective_stmt (stmt_p, walk_subtrees, data, func, + lh); + break; + case STATEMENT_LIST: if (TREE_SIDE_EFFECTS (stmt)) { diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc index 7e20e5a5082..b6981020acd 100644 --- a/gcc/c-family/c-omp.cc +++ b/gcc/c-family/c-omp.cc @@ -4299,7 +4299,7 @@ const struct c_omp_directive c_omp_directives[] = { /* { "begin", "declare", "variant", PRAGMA_OMP_BEGIN, C_OMP_DIR_DECLARATIVE, false }, */ /* { "begin", "metadirective", nullptr, PRAGMA_OMP_BEGIN, - C_OMP_DIR_???, ??? }, */ + C_OMP_DIR_META, false }, */ { "cancel", nullptr, nullptr, PRAGMA_OMP_CANCEL, C_OMP_DIR_STANDALONE, false }, { "cancellation", "point", nullptr, PRAGMA_OMP_CANCELLATION_POINT, @@ -4329,7 +4329,7 @@ const struct c_omp_directive c_omp_directives[] = { /* { "end", "declare", "variant", PRAGMA_OMP_END, C_OMP_DIR_DECLARATIVE, false }, */ /* { "end", "metadirective", nullptr, PRAGMA_OMP_END, - C_OMP_DIR_???, ??? }, */ + C_OMP_DIR_META, false }, */ /* error with at(execution) is C_OMP_DIR_STANDALONE. */ { "error", nullptr, nullptr, PRAGMA_OMP_ERROR, C_OMP_DIR_UTILITY, false }, @@ -4347,8 +4347,8 @@ const struct c_omp_directive c_omp_directives[] = { C_OMP_DIR_CONSTRUCT, true }, { "master", nullptr, nullptr, PRAGMA_OMP_MASTER, C_OMP_DIR_CONSTRUCT, true }, - /* { "metadirective", nullptr, nullptr, PRAGMA_OMP_METADIRECTIVE, - C_OMP_DIR_???, ??? }, */ + { "metadirective", nullptr, nullptr, PRAGMA_OMP_METADIRECTIVE, + C_OMP_DIR_META, false }, { "nothing", nullptr, nullptr, PRAGMA_OMP_NOTHING, C_OMP_DIR_UTILITY, false }, /* ordered with depend clause is C_OMP_DIR_STANDALONE. */ @@ -4431,3 +4431,55 @@ c_omp_categorize_directive (const char *first, const char *second, } return NULL; } + +/* Auxilliary helper function for c_omp_expand_variant_construct. */ + +static tree +c_omp_expand_variant_construct_r (vec<struct omp_variant> &candidates, + hash_map<tree, tree> &body_labels, + unsigned index) +{ + struct omp_variant &candidate = candidates[index]; + tree if_block = push_stmt_list (); + if (candidate.alternative != NULL_TREE) + add_stmt (candidate.alternative); + if (candidate.body != NULL_TREE) + { + tree *label = body_labels.get (candidate.body); + if (label != NULL) + add_stmt (build1 (GOTO_EXPR, void_type_node, *label)); + else + { + tree body_label = create_artificial_label (UNKNOWN_LOCATION); + add_stmt (build1 (LABEL_EXPR, void_type_node, body_label)); + add_stmt (candidate.body); + body_labels.put (candidate.body, body_label); + } + } + if_block = pop_stmt_list (if_block); + + if (index == candidates.length () - 1) + return if_block; + + tree cond = omp_dynamic_cond (candidate.selector, NULL_TREE); + gcc_assert (cond != NULL_TREE); + + tree else_block = c_omp_expand_variant_construct_r (candidates, body_labels, + index + 1); + tree ret = push_stmt_list (); + tree stmt = build3 (COND_EXPR, void_type_node, cond, if_block, else_block); + add_stmt (stmt); + ret = pop_stmt_list (ret); + + return ret; +} + +/* Resolve the vector of metadirective variant CANDIDATES to a parse tree + structure. */ + +tree +c_omp_expand_variant_construct (vec<struct omp_variant> &candidates) +{ + hash_map<tree, tree> body_labels; + return c_omp_expand_variant_construct_r (candidates, body_labels, 0); +} diff --git a/gcc/c-family/c-pragma.cc b/gcc/c-family/c-pragma.cc index 52997eb5bdc..857c7d87e33 100644 --- a/gcc/c-family/c-pragma.cc +++ b/gcc/c-family/c-pragma.cc @@ -1529,6 +1529,7 @@ static const struct omp_pragma_def omp_pragmas[] = { { "end", PRAGMA_OMP_END }, { "flush", PRAGMA_OMP_FLUSH }, { "interop", PRAGMA_OMP_INTEROP }, + { "metadirective", PRAGMA_OMP_METADIRECTIVE }, { "nothing", PRAGMA_OMP_NOTHING }, { "requires", PRAGMA_OMP_REQUIRES }, { "scope", PRAGMA_OMP_SCOPE }, diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h index df5625d5f4f..3e257f8370d 100644 --- a/gcc/c-family/c-pragma.h +++ b/gcc/c-family/c-pragma.h @@ -66,6 +66,7 @@ enum pragma_kind { PRAGMA_OMP_NOTHING, PRAGMA_OMP_MASKED, PRAGMA_OMP_MASTER, + PRAGMA_OMP_METADIRECTIVE, PRAGMA_OMP_ORDERED, PRAGMA_OMP_PARALLEL, PRAGMA_OMP_REQUIRES, diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 7c318f5d17f..8bb442eb637 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -271,6 +271,11 @@ struct GTY(()) c_parser { appertains. */ tree in_omp_decl_attribute; + /* Non-null only when parsing the body of an OpenMP metadirective. + Managed by c_parser_omp_metadirective. */ + struct omp_metadirective_parse_data * GTY((skip)) + omp_metadirective_state; + }; /* Holds data needed to restore the token stream to its previous state @@ -1456,9 +1461,11 @@ c_parser_skip_to_pragma_eol (c_parser *parser, bool error_if_not_eol = true) have consumed a non-nested ';'. */ static void -c_parser_skip_to_end_of_block_or_statement (c_parser *parser) +c_parser_skip_to_end_of_block_or_statement (c_parser *parser, + bool metadirective_p = false) { unsigned nesting_depth = 0; + int bracket_depth = 0; bool save_error = parser->error; while (true) @@ -1481,7 +1488,7 @@ c_parser_skip_to_end_of_block_or_statement (c_parser *parser) case CPP_SEMICOLON: /* If the next token is a ';', we have reached the end of the statement. */ - if (!nesting_depth) + if (!nesting_depth && (!metadirective_p || bracket_depth <= 0)) { /* Consume the ';'. */ c_parser_consume_token (parser); @@ -1492,7 +1499,8 @@ c_parser_skip_to_end_of_block_or_statement (c_parser *parser) case CPP_CLOSE_BRACE: /* If the next token is a non-nested '}', then we have reached the end of the current block. */ - if (nesting_depth == 0 || --nesting_depth == 0) + if ((nesting_depth == 0 || --nesting_depth == 0) + && (!metadirective_p || bracket_depth <= 0)) { c_parser_consume_token (parser); goto finished; @@ -1505,6 +1513,19 @@ c_parser_skip_to_end_of_block_or_statement (c_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_PRAGMA: /* If we see a pragma, consume the whole thing at once. We have some safeguards against consuming pragmas willy-nilly. @@ -1753,6 +1774,7 @@ static void c_parser_omp_taskwait (c_parser *); static void c_parser_omp_taskyield (c_parser *); static void c_parser_omp_cancel (c_parser *); static void c_parser_omp_nothing (c_parser *); +static void c_parser_omp_metadirective (c_parser *, bool *); enum pragma_context { pragma_external, pragma_struct, pragma_param, pragma_stmt, pragma_compound }; @@ -7755,6 +7777,31 @@ c_parser_all_labels (c_parser *parser) return attr; } + +/* 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 c_parser_label: mangle a metadirective region + label NAME. */ +static tree +mangle_metadirective_region_label (c_parser *parser, tree name) +{ + if (parser->omp_metadirective_state->body_labels->contains (name)) + { + const char *old_name = IDENTIFIER_POINTER (name); + char *new_name = (char *) XALLOCAVEC (char, 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 a label (C90 6.6.1, C99 6.8.1, C11 6.8.1). label: @@ -7826,6 +7873,8 @@ c_parser_label (c_parser *parser, tree std_attrs) gcc_assert (c_parser_next_token_is (parser, CPP_COLON)); c_parser_consume_token (parser); attrs = c_parser_gnu_attributes (parser); + if (parser->omp_metadirective_state) + name = mangle_metadirective_region_label (parser, name); tlab = define_label (loc2, name); if (tlab) { @@ -8072,8 +8121,10 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, c_parser_consume_token (parser); if (c_parser_next_token_is (parser, CPP_NAME)) { - stmt = c_finish_goto_label (loc, - c_parser_peek_token (parser)->value); + tree name = c_parser_peek_token (parser)->value; + if (parser->omp_metadirective_state) + name = mangle_metadirective_region_label (parser, name); + stmt = c_finish_goto_label (loc, name); c_parser_consume_token (parser); } else if (c_parser_next_token_is (parser, CPP_MULT)) @@ -15686,6 +15737,10 @@ c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p, c_parser_omp_nothing (parser); return false; + case PRAGMA_OMP_METADIRECTIVE: + c_parser_omp_metadirective (parser, if_p); + return true; + case PRAGMA_OMP_ERROR: return c_parser_omp_error (parser, context); @@ -26637,12 +26692,9 @@ c_parser_omp_context_selector (c_parser *parser, enum omp_tss_code set, { mark_exp_read (t); t = c_fully_fold (t, false, NULL); - /* FIXME: this is bogus, both device_num and - condition selectors allow arbitrary expressions. */ - if (!INTEGRAL_TYPE_P (TREE_TYPE (t)) - || !tree_fits_shwi_p (t)) - error_at (token->location, "property must be " - "constant integer expression"); + if (!INTEGRAL_TYPE_P (TREE_TYPE (t))) + error_at (token->location, + "property must be integer expression"); else properties = make_trait_property (NULL_TREE, t, properties); @@ -28672,6 +28724,420 @@ c_parser_omp_assumes (c_parser *parser) c_parser_omp_assumption_clauses (parser, false); } +/* Helper function for c_parser_omp_metadirective. */ + +static void +analyze_metadirective_body (c_parser *parser, + vec<c_token> &tokens, + vec<tree> &labels) +{ + int nesting_depth = 0; + int bracket_depth = 0; + bool ignore_label = false; + + /* Read in the body tokens to the tokens for each candidate directive. */ + while (1) + { + c_token *token = c_parser_peek_token (parser); + bool stop = false; + + if (c_parser_next_token_is_keyword (parser, RID_CASE)) + ignore_label = true; + + switch (token->type) + { + case CPP_EOF: + break; + case CPP_NAME: + if (!ignore_label + && c_parser_peek_2nd_token (parser)->type == CPP_COLON) + labels.safe_push (token->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: + ignore_label = false; + goto add; + case CPP_SEMICOLON: + if (nesting_depth == 0 && bracket_depth == 0) + stop = true; + goto add; + default: + add: + tokens.safe_push (*token); + if (token->type == CPP_PRAGMA) + c_parser_consume_pragma (parser); + else if (token->type == CPP_PRAGMA_EOL) + c_parser_skip_to_pragma_eol (parser); + else + c_parser_consume_token (parser); + if (stop) + break; + continue; + } + break; + } +} + +/* OpenMP 5.0: + + # pragma omp metadirective [clause[, clause]] +*/ + +static void +c_parser_omp_metadirective (c_parser *parser, bool *if_p) +{ + static unsigned int metadirective_region_count = 0; + + tree ret; + auto_vec<c_token> directive_tokens; + auto_vec<c_token> body_tokens; + auto_vec<tree> body_labels; + auto_vec<const struct c_omp_directive *> directives; + auto_vec<tree> ctxs; + vec<struct omp_variant> candidates; + bool default_seen = false; + int directive_token_idx = 0; + tree standalone_body = NULL_TREE; + location_t pragma_loc = c_parser_peek_token (parser)->location; + bool requires_body = false; + + ret = make_node (OMP_METADIRECTIVE); + SET_EXPR_LOCATION (ret, pragma_loc); + TREE_TYPE (ret) = void_type_node; + OMP_METADIRECTIVE_VARIANTS (ret) = NULL_TREE; + + c_parser_consume_pragma (parser); + while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL)) + { + if (c_parser_next_token_is (parser, CPP_COMMA)) + c_parser_consume_token (parser); + if (c_parser_next_token_is_not (parser, CPP_NAME) + && c_parser_next_token_is_not (parser, CPP_KEYWORD)) + { + c_parser_error (parser, "expected %<when%>, " + "%<otherwise%>, or %<default%> clause"); + goto error; + } + + location_t match_loc = c_parser_peek_token (parser)->location; + const char *p = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); + c_parser_consume_token (parser); + 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%>"); + c_parser_skip_to_end_of_block_or_statement (parser, true); + goto error; + } + default_seen = true; + } + else if (default_seen) + { + error_at (match_loc, "%<otherwise%> or %<default%> clause " + "must appear last in %<metadirective%>"); + c_parser_skip_to_end_of_block_or_statement (parser, true); + goto error; + } + if (!(strcmp (p, "when") == 0 || default_p)) + { + error_at (match_loc, "%qs is not valid for %qs", + p, "metadirective"); + c_parser_skip_to_end_of_block_or_statement (parser, true); + goto error; + } + + matching_parens parens; + tree ctx = NULL_TREE; + bool skip = false; + + if (!parens.require_open (parser)) + goto error; + + if (!default_p) + { + ctx = c_parser_omp_context_selector_specification (parser, + NULL_TREE); + if (ctx == error_mark_node) + goto error; + ctx = omp_check_context_selector (match_loc, ctx, true); + if (ctx == error_mark_node) + goto error; + + /* Remove the selector from further consideration if it can be + evaluated as a non-match at this point. */ + skip = (omp_context_selector_matches (ctx, NULL_TREE, false) == 0); + + if (c_parser_next_token_is_not (parser, CPP_COLON)) + { + c_parser_require (parser, CPP_COLON, "expected %<:%>"); + goto error; + } + c_parser_consume_token (parser); + } + + /* Read in the directive type and create a dummy pragma token for + it. */ + location_t loc = c_parser_peek_token (parser)->location; + + const char *directive[3] = {}; + int i; + for (i = 0; i < 3; i++) + { + tree id; + if (c_parser_peek_nth_token (parser, i + 1)->type + == CPP_CLOSE_PAREN) + { + if (i == 0) + directive[i++] = "nothing"; + break; + } + else if (c_parser_peek_nth_token (parser, i + 1)->type + == CPP_NAME) + id = c_parser_peek_nth_token (parser, i + 1)->value; + else if (c_parser_peek_nth_token (parser, i + 1)->keyword + != RID_MAX) + { + enum rid rid + = c_parser_peek_nth_token (parser, i + 1)->keyword; + id = ridpointers[rid]; + } + else + break; + + directive[i] = IDENTIFIER_POINTER (id); + } + if (i == 0) + { + error_at (loc, "expected directive name"); + c_parser_skip_to_end_of_block_or_statement (parser, true); + goto error; + } + + 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++) + c_parser_consume_token (parser); + c_parser_error (parser, "unknown directive name"); + goto error; + } + 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++) + c_parser_consume_token (parser); + } + if (omp_directive->id == PRAGMA_OMP_METADIRECTIVE) + { + c_parser_error (parser, + "metadirectives cannot be used as variants of a " + "%<metadirective%>"); + goto error; + } + if (omp_directive->kind == C_OMP_DIR_DECLARATIVE) + { + sorry_at (loc, "declarative directive variants of a " + "%<metadirective%> are not supported"); + goto error; + } + if (omp_directive->kind == C_OMP_DIR_CONSTRUCT) + requires_body = true; + + if (!skip) + { + c_token pragma_token; + pragma_token.type = CPP_PRAGMA; + pragma_token.location = loc; + pragma_token.pragma_kind = (enum pragma_kind) 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) + { + c_token *token = c_parser_peek_token (parser); + 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); + c_parser_consume_token (parser); + continue; + } + break; + } + + c_parser_consume_token (parser); + + if (!skip) + { + c_token eol_token; + memset (&eol_token, 0, sizeof (eol_token)); + eol_token.type = CPP_PRAGMA_EOL; + directive_tokens.safe_push (eol_token); + } + } + c_parser_skip_to_pragma_eol (parser); + + 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); + + c_token pragma_token; + pragma_token.type = CPP_PRAGMA; + pragma_token.location = UNKNOWN_LOCATION; + pragma_token.pragma_kind = PRAGMA_OMP_NOTHING; + + directives.safe_push (omp_directive); + directive_tokens.safe_push (pragma_token); + ctxs.safe_push (NULL_TREE); + + c_token eol_token; + memset (&eol_token, 0, sizeof (eol_token)); + eol_token.type = CPP_PRAGMA_EOL; + 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; + + FOR_EACH_VEC_ELT (ctxs, i, ctx) + { + auto_vec<c_token> tokens; + + /* Add the directive tokens. */ + do + tokens.safe_push (directive_tokens [directive_token_idx++]); + while (tokens.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++) + tokens.safe_push (body_tokens[j]); + + /* Make sure nothing tries to read past the end of the tokens. */ + c_token eof_token; + memset (&eof_token, 0, sizeof (eof_token)); + eof_token.type = CPP_EOF; + tokens.safe_push (eof_token); + tokens.safe_push (eof_token); + + unsigned int old_tokens_avail = parser->tokens_avail; + c_token *old_tokens = parser->tokens; + struct omp_attribute_pragma_state *old_in_omp_attribute_pragma + = parser->in_omp_attribute_pragma; + struct omp_metadirective_parse_data *old_state + = parser->omp_metadirective_state; + + struct omp_metadirective_parse_data new_state; + new_state.body_labels = &body_labels; + new_state.region_num = ++metadirective_region_count; + + parser->tokens = tokens.address (); + parser->tokens_avail = tokens.length (); + parser->in_omp_attribute_pragma = NULL; + parser->omp_metadirective_state = &new_state; + + int prev_errorcount = errorcount; + tree directive = c_begin_compound_stmt (true); + + c_parser_pragma (parser, pragma_compound, if_p, NULL_TREE); + directive = c_end_compound_stmt (pragma_loc, directive, true); + 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 (); + c_parser_statement (parser, if_p); + standalone_body = pop_stmt_list (standalone_body); + } + else + c_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. */ + if (errorcount == prev_errorcount) + { + gcc_assert (parser->tokens_avail == 2); + gcc_assert (c_parser_next_token_is (parser, CPP_EOF)); + gcc_assert (c_parser_peek_2nd_token (parser)->type == CPP_EOF); + } + + parser->tokens = old_tokens; + parser->tokens_avail = old_tokens_avail; + parser->in_omp_attribute_pragma = old_in_omp_attribute_pragma; + parser->omp_metadirective_state = old_state; + } + + /* Try to resolve the metadirective early. */ + candidates = omp_early_resolve_metadirective (ret); + if (!candidates.is_empty ()) + ret = c_omp_expand_variant_construct (candidates); + + add_stmt (ret); + return; + +error: + if (parser->in_pragma) + c_parser_skip_to_pragma_eol (parser); + c_parser_skip_to_end_of_block_or_statement (parser, true); +} + /* Main entry point to parsing most OpenMP pragmas. */ static void 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 9f209f4ea66..13b0614cac4 100644 --- a/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c +++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c @@ -38,8 +38,10 @@ 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 constant integer expression" "" { target { c || c++11 } } } */ -void f21 (void); /* { dg-error "cannot appear in a constant-expression" "" { target c++98_only } .-1 } */ +#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 } */ +void f21 (void); #pragma omp declare variant (f1) match(user={condition(1, 2, 3)}) /* { dg-error "expected '\\)' before ',' token" } */ void f22 (void); #pragma omp declare variant (f1) match(construct={master}) /* { dg-warning "unknown selector 'master' for context selector set 'construct'" } */ diff --git a/gcc/testsuite/gcc.dg/gomp/metadirective-1.c b/gcc/testsuite/gcc.dg/gomp/metadirective-1.c new file mode 100644 index 00000000000..2ac81bfde75 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/metadirective-1.c @@ -0,0 +1,15 @@ +int main (void) +{ + int x, y; + + /* Test nested functions inside statement body. */ + #pragma omp metadirective \ + when (device={arch("nvptx")}: teams num_teams(512)) \ + when (device={arch("gcn")}: teams num_teams(256)) \ + default (teams num_teams(4)) + { + int f (int x) { return x * 3; } + + y = f (x); + } +} -- 2.25.1