aboutsummaryrefslogtreecommitdiff
path: root/gcc/c/c-parser.cc
diff options
context:
space:
mode:
authorSandra Loosemore <sloosemore@baylibre.com>2025-01-14 23:27:53 +0000
committerSandra Loosemore <sloosemore@baylibre.com>2025-01-16 18:12:12 +0000
commit4e20914d3306d8898ce586313a40fb92ef0b8964 (patch)
tree1f738d5e735bfb7b2fe46920790b91088b216502 /gcc/c/c-parser.cc
parent0696af74b3392e2178215607337b116d1bb53e34 (diff)
downloadgcc-4e20914d3306d8898ce586313a40fb92ef0b8964.zip
gcc-4e20914d3306d8898ce586313a40fb92ef0b8964.tar.gz
gcc-4e20914d3306d8898ce586313a40fb92ef0b8964.tar.bz2
OpenMP: Add C support for metadirectives and dynamic selectors.
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. (c_parser_omp_assumption_clauses): Handle C_OMP_DIR_META. (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 <kcy@codesourcery.com> Co-Authored-By: Sandra Loosemore <sandra@codesourcery.com>
Diffstat (limited to 'gcc/c/c-parser.cc')
-rw-r--r--gcc/c/c-parser.cc489
1 files changed, 478 insertions, 11 deletions
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 36c8f79..b45c7ef 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -270,6 +270,11 @@ struct GTY(()) c_parser {
/* Set for omp::decl attribute parsing to the decl to which it
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
@@ -1455,9 +1460,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)
@@ -1480,7 +1487,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);
@@ -1491,7 +1498,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;
@@ -1504,6 +1512,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.
@@ -1752,6 +1773,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 };
@@ -7785,6 +7807,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:
@@ -7856,6 +7903,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)
{
@@ -8102,8 +8151,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))
@@ -15717,6 +15768,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);
@@ -26702,12 +26757,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);
@@ -28797,6 +28849,7 @@ c_parser_omp_assumption_clauses (c_parser *parser, bool is_assume)
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]))
@@ -28881,6 +28934,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