@@ -1399,7 +1399,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 {
@@ -1413,6 +1414,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_metadirective (vec<struct omp_variant> &);
/* Return next tree in the chain for chain_next walking of tree nodes. */
inline tree
@@ -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
@@ -485,6 +486,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
@@ -535,6 +557,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))
{
@@ -4280,7 +4280,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,
@@ -4310,7 +4310,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 },
@@ -4328,8 +4328,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. */
@@ -4412,3 +4412,55 @@ c_omp_categorize_directive (const char *first, const char *second,
}
return NULL;
}
+
+/* Auxilliary helper function for c_omp_expand_metadirective. */
+
+static tree
+c_omp_expand_metadirective_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 = candidate.dynamic_selector;
+ gcc_assert (cond != NULL_TREE);
+
+ tree else_block = c_omp_expand_metadirective_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_metadirective (vec<struct omp_variant> &candidates)
+{
+ hash_map<tree, tree> body_labels;
+ return c_omp_expand_metadirective_r (candidates, body_labels, 0);
+}
@@ -1529,6 +1529,7 @@ static const struct omp_pragma_def omp_pragmas[] = {
{ "error", PRAGMA_OMP_ERROR },
{ "end", PRAGMA_OMP_END },
{ "flush", PRAGMA_OMP_FLUSH },
+ { "metadirective", PRAGMA_OMP_METADIRECTIVE },
{ "nothing", PRAGMA_OMP_NOTHING },
{ "requires", PRAGMA_OMP_REQUIRES },
{ "scope", PRAGMA_OMP_SCOPE },
@@ -64,6 +64,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,
@@ -263,9 +263,24 @@ struct GTY(()) c_parser {
otherwise NULL. */
vec<c_token, va_gc> *in_omp_attribute_pragma;
+ /* When in_omp_attribute_pragma is non-null, these fields save the values
+ of the tokens and tokens_avail fields, so that they can be restored
+ after parsing the attribute. Note that parsing the body of a
+ metadirective uses its own save/restore mechanism as those can be
+ nested with or without the attribute pragmas in the body. */
+ c_token * GTY((skip)) save_tokens;
+ unsigned int save_tokens_avail;
+
/* Set for omp::decl attribute parsing to the decl to which it
appertains. */
tree in_omp_decl_attribute;
+
+ /* Set if we are processing a statement body associated with a
+ metadirective variant. */
+ BOOL_BITFIELD in_metadirective_body : 1;
+
+ vec<tree> * GTY((skip)) metadirective_body_labels;
+ unsigned int metadirective_region_num;
};
/* Return a pointer to the Nth token in PARSERs tokens_buf. */
@@ -1307,8 +1322,10 @@ c_parser_skip_until_found (c_parser *parser,
c_token *token = c_parser_peek_token (parser);
if (token->type == CPP_EOF)
{
- parser->tokens = &parser->tokens_buf[0];
- parser->tokens_avail = token->flags;
+ parser->tokens = parser->save_tokens;
+ parser->save_tokens = NULL;
+ parser->tokens_avail = parser->save_tokens_avail;
+ parser->save_tokens_avail = 0;
parser->in_omp_attribute_pragma = NULL;
}
}
@@ -1330,8 +1347,10 @@ c_parser_skip_until_found (c_parser *parser,
c_token *token = c_parser_peek_token (parser);
if (token->type == CPP_EOF)
{
- parser->tokens = &parser->tokens_buf[0];
- parser->tokens_avail = token->flags;
+ parser->tokens = parser->save_tokens;
+ parser->save_tokens = NULL;
+ parser->tokens_avail = parser->save_tokens_avail;
+ parser->save_tokens_avail = 0;
parser->in_omp_attribute_pragma = NULL;
}
}
@@ -1424,8 +1443,10 @@ c_parser_skip_to_pragma_eol (c_parser *parser, bool error_if_not_eol = true)
c_token *token = c_parser_peek_token (parser);
if (token->type == CPP_EOF)
{
- parser->tokens = &parser->tokens_buf[0];
- parser->tokens_avail = token->flags;
+ parser->tokens = parser->save_tokens;
+ parser->save_tokens = NULL;
+ parser->tokens_avail = parser->save_tokens_avail;
+ parser->save_tokens_avail = 0;
parser->in_omp_attribute_pragma = NULL;
}
}
@@ -1437,9 +1458,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)
@@ -1462,7 +1485,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);
@@ -1473,7 +1496,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;
@@ -1486,6 +1510,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.
@@ -1725,6 +1762,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 };
@@ -6839,7 +6877,6 @@ c_parser_handle_statement_omp_attributes (c_parser *parser, tree &attrs,
return false;
unsigned int tokens_avail = parser->tokens_avail;
- gcc_assert (parser->tokens == &parser->tokens_buf[0]);
tokens++;
vec<c_token, va_gc> *toks = NULL;
@@ -6874,7 +6911,10 @@ c_parser_handle_statement_omp_attributes (c_parser *parser, tree &attrs,
tok.flags = tokens_avail;
toks->quick_push (tok);
+ gcc_assert (!parser->save_tokens);
+ parser->save_tokens = parser->tokens;
parser->tokens = toks->address ();
+ parser->save_tokens_avail = tokens_avail;
parser->tokens_avail = tokens;
parser->in_omp_attribute_pragma = toks;
return true;
@@ -7360,6 +7400,18 @@ c_parser_all_labels (c_parser *parser)
c_parser_error (parser, "expected statement");
}
+
+/* Helper function for c_parser_label: mangle a metadirective region
+ label NAME. */
+static tree
+mangle_metadirective_region_label (c_parser *parser, tree 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->metadirective_region_num);
+ return get_identifier (new_name);
+}
+
/* Parse a label (C90 6.6.1, C99 6.8.1, C11 6.8.1).
label:
@@ -7431,6 +7483,9 @@ 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->in_metadirective_body
+ && parser->metadirective_body_labels->contains (name))
+ name = mangle_metadirective_region_label (parser, name);
tlab = define_label (loc2, name);
if (tlab)
{
@@ -7658,8 +7713,11 @@ 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->in_metadirective_body
+ && parser->metadirective_body_labels->contains (name))
+ 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))
@@ -14736,6 +14794,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);
@@ -24879,7 +24941,7 @@ c_parser_omp_declare_simd (c_parser *parser, enum pragma_context context)
static tree
c_parser_omp_context_selector (c_parser *parser, enum omp_tss_code set,
- tree parms)
+ tree parms, bool metadirective_p)
{
tree ret = NULL_TREE;
do
@@ -25026,12 +25088,18 @@ 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");
+ /* FIXME: I believe it is an unimplemented feature rather
+ than a user error to have non-constant expressions
+ inside "declare variant". */
+ if (!metadirective_p
+ && (!INTEGRAL_TYPE_P (TREE_TYPE (t))
+ || !tree_fits_shwi_p (t)))
+ error_at (token->location,
+ "property must be constant integer expression");
+ else if (metadirective_p
+ && !INTEGRAL_TYPE_P (TREE_TYPE (t)))
+ error_at (token->location,
+ "property must be integer expression");
else
properties = make_trait_property (NULL_TREE, t,
properties);
@@ -25112,7 +25180,8 @@ c_parser_omp_context_selector (c_parser *parser, enum omp_tss_code set,
user */
static tree
-c_parser_omp_context_selector_specification (c_parser *parser, tree parms)
+c_parser_omp_context_selector_specification (c_parser *parser, tree parms,
+ bool metadirective_p)
{
tree ret = NULL_TREE;
do
@@ -25137,7 +25206,8 @@ c_parser_omp_context_selector_specification (c_parser *parser, tree parms)
if (!braces.require_open (parser))
return error_mark_node;
- tree selectors = c_parser_omp_context_selector (parser, set, parms);
+ tree selectors = c_parser_omp_context_selector (parser, set, parms,
+ metadirective_p);
if (selectors == error_mark_node)
ret = error_mark_node;
else if (ret != error_mark_node)
@@ -25213,7 +25283,8 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms)
if (parms == NULL_TREE)
parms = error_mark_node;
- tree ctx = c_parser_omp_context_selector_specification (parser, parms);
+ tree ctx = c_parser_omp_context_selector_specification (parser,
+ parms, false);
if (ctx == error_mark_node)
goto fail;
ctx = omp_check_context_selector (match_loc, ctx, false);
@@ -25414,7 +25485,10 @@ c_maybe_parse_omp_decl (tree decl, tree d)
tok.flags = tokens_avail;
toks->quick_push (tok);
parser->in_omp_decl_attribute = decl;
+ gcc_assert (!parser->save_tokens);
+ parser->save_tokens = parser->tokens;
parser->tokens = toks->address ();
+ parser->save_tokens_avail = parser->tokens_avail;
parser->tokens_avail = toks->length ();
parser->in_omp_attribute_pragma = toks;
c_parser_pragma (parser, pragma_external, NULL);
@@ -26890,6 +26964,412 @@ 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;
+ }
+ 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, true);
+ 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 can be
+ evaluated as a non-match at this point. */
+ skip = omp_context_selector_matches (ctx, true, true) == 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;
+ bool old_in_metadirective_body = parser->in_metadirective_body;
+ vec<tree> *old_metadirective_body_labels
+ = parser->metadirective_body_labels;
+ unsigned int old_metadirective_region_num
+ = parser->metadirective_region_num;
+
+ parser->tokens = tokens.address ();
+ parser->tokens_avail = tokens.length ();
+ parser->in_metadirective_body = true;
+ parser->metadirective_body_labels = &body_labels;
+ parser->metadirective_region_num = ++metadirective_region_count;
+
+ int prev_errorcount = errorcount;
+ tree directive = c_begin_compound_stmt (true);
+
+ c_parser_pragma (parser, pragma_compound, if_p);
+ 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_metadirective_body = old_in_metadirective_body;
+ parser->metadirective_body_labels = old_metadirective_body_labels;
+ parser->metadirective_region_num = old_metadirective_region_num;
+ }
+
+ /* Try to resolve the metadirective early. */
+ candidates = omp_early_resolve_metadirective (ret);
+ if (!candidates.is_empty ())
+ ret = c_omp_expand_metadirective (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
new file mode 100644
@@ -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);
+ }
+}