On Fri, Dec 10, 2021 at 05:37:34PM +0000, Kwok Cheung Yeung wrote:
> From e9bb138d4c3f560e48e408facce2361533685a98 Mon Sep 17 00:00:00 2001
> From: Kwok Cheung Yeung <[email protected]>
> Date: Mon, 6 Dec 2021 22:58:01 +0000
> Subject: [PATCH 5/7] openmp: Add C++ support for parsing metadirectives
>
> This adds support for parsing OpenMP metadirectives in the C++ front end.
>
> 2021-12-10 Kwok Cheung Yeung <[email protected]>
>
> gcc/cp/
> * parser.c (cp_parser_skip_to_end_of_statement): Handle parentheses.
> (cp_parser_skip_to_end_of_block_or_statement): Likewise.
> (cp_parser_omp_context_selector): Add extra argument. Allow
> non-constant expressions.
> (cp_parser_omp_context_selector_specification): Add extra argument and
> propagate to cp_parser_omp_context_selector.
> (analyze_metadirective_body): New.
> (cp_parser_omp_metadirective): New.
> (cp_parser_omp_construct): Handle PRAGMA_OMP_METADIRECTIVE.
> (cp_parser_pragma): Handle PRAGMA_OMP_METADIRECTIVE.
> ---
> gcc/cp/parser.c | 425 +++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 417 insertions(+), 8 deletions(-)
>
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 6f273bfe21f..afbfe148949 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -3907,6 +3907,17 @@ cp_parser_skip_to_end_of_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. */
> + ++nesting_depth;
> + break;
> +
> + case CPP_CLOSE_PAREN:
> + --nesting_depth;
> + break;
> +
> case CPP_KEYWORD:
> if (token->keyword != RID__EXPORT
> && token->keyword != RID__MODULE
> @@ -3996,6 +4007,17 @@ 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. */
> + nesting_depth++;
> + break;
> +
> + case CPP_CLOSE_PAREN:
> + nesting_depth--;
> + break;
> +
Like for C FE, I think this is too risky.
> case CTX_PROPERTY_EXPR:
> - t = cp_parser_constant_expression (parser);
> + /* Allow non-constant expressions in metadirectives. */
> + t = metadirective_p
> + ? cp_parser_expression (parser)
> + : cp_parser_constant_expression (parser);
> if (t != error_mark_node)
> {
> t = fold_non_dependent_expr (t);
> - if (!value_dependent_expression_p (t)
> - && (!INTEGRAL_TYPE_P (TREE_TYPE (t))
> - || !tree_fits_shwi_p (t)))
> + if (metadirective_p && !INTEGRAL_TYPE_P (TREE_TYPE (t)))
Like in the other patch, but more importantly, if t is
type_dependent_expression_p, you shouldn't diagnose it, it might be
integral after instantiation. But it needs to be diagnosed later during
instantiation if it isn't integral then...
> + 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);
Similar thing as for C, identifier : can appear in various spots even when
it isn't a label, in C++ even in many more cases. Say
nested struct definition, struct S : T {}; or (that is for both C and C++)
e.g. bitfields struct V { int v : 13; };
> + goto add;
> + case CPP_OPEN_BRACE:
> + ++nesting_depth;
> + goto add;
> + case CPP_CLOSE_BRACE:
> + if (--nesting_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;
> + 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 tree
> +cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok,
> + char *p_name, omp_clause_mask, tree *,
> + bool *if_p)
> +{
> + tree ret;
> + 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 loc = cp_lexer_peek_token (parser->lexer)->location;
> + tree standalone_body = NULL_TREE;
> + vec<struct omp_metadirective_variant> candidates;
> +
> + ret = make_node (OMP_METADIRECTIVE);
Better write tree ret = make_node ...
i.e. at least where easily possible declare vars on first use rather than
at the start of function.
Also, same comments I wrote in the C FE patch.
> + SET_EXPR_LOCATION (ret, loc);
> + TREE_TYPE (ret) = void_type_node;
> + OMP_METADIRECTIVE_CLAUSES (ret) = NULL_TREE;
> + strcat (p_name, " metadirective");
> +
> + while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL))
> + {
> + 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%> or %<default%>");
> + 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;
> + if (default_p)
> + {
> + if (default_seen)
> + {
> + cp_parser_error (parser, "there can only be one default clause "
> + "in a metadirective");
> + goto fail;
> + }
> + else
> + default_seen = true;
> + }
> + if (!strcmp (p, "when") == 0 && !default_p)
> + {
> + cp_parser_error (parser, "expected %<when%> or %<default%>");
> + 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,
> + true);
> + if (ctx == error_mark_node)
> + goto fail;
> + ctx = omp_check_context_selector (match_loc, ctx);
> + if (ctx == error_mark_node)
> + goto fail;
> +
> + /* Remove the selector from further consideration if can be
> + evaluated as a non-match at this point. */
> + skip = (omp_context_selector_matches (ctx, true) == 0);
> +
> + if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
> + {
> + cp_parser_error (parser, "expected 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;
> +
> + p = NULL;
> + if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
> + p = "nothing";
> + else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR))
> + {
> + p = "for";
> + cp_lexer_consume_token (parser->lexer);
> + }
> + else if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
> + {
> + cp_token *token = cp_lexer_consume_token (parser->lexer);
> + p = IDENTIFIER_POINTER (token->u.value);
> + }
> +
> + if (p == NULL)
> + {
> + cp_parser_error (parser, "expected directive name");
> + goto fail;
> + }
> +
> + const struct c_omp_directive *omp_directive
> + = c_omp_categorize_directive (p, NULL, NULL);
> +
> + if (omp_directive == NULL)
> + {
> + cp_parser_error (parser, "unknown directive name");
> + goto fail;
> + }
> + if (omp_directive->id == PRAGMA_OMP_METADIRECTIVE)
> + {
> + cp_parser_error (parser,
> + "metadirectives cannot be used as directive "
> + "variants");
> + goto fail;
> + }
> + if (omp_directive->kind == C_OMP_DIR_DECLARATIVE)
> + {
> + sorry_at (loc, "declarative directive variants are not supported");
> + goto fail;
> + }
> +
> + 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);
> + }
> +
> + 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. */
> + 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;
> + parser->lexer = lexer;
> + cp_lexer_set_source_position_from_token (lexer->next_token);
> +
> + tree directive = push_stmt_list ();
> + tree directive_stmt = begin_compound_stmt (0);
> +
> + /* Declare all labels that occur within the directive body as
> + local. */
> + for (unsigned j = 0; j < body_labels.length (); j++)
> + finish_label_decl (body_labels[j]);
> + 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)
> + {
> + /* 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);
> + }
> +
> + tree body = standalone_p ? standalone_body : NULL_TREE;
> + tree variant = build_tree_list (ctx, build_tree_list (directive,
> body));
> + OMP_METADIRECTIVE_CLAUSES (ret)
> + = chainon (OMP_METADIRECTIVE_CLAUSES (ret), variant);
> +
> + /* Check that all valid tokens have been consumed. */
> + gcc_assert (cp_lexer_next_token_is (parser->lexer, CPP_EOF));
> +
> + parser->lexer = old_lexer;
> + cp_lexer_set_source_position_from_token (old_lexer->next_token);
> + }
> +
> + /* Try to resolve the metadirective early. */
> + candidates = omp_resolve_metadirective (ret);
> + if (!candidates.is_empty ())
> + ret = c_omp_expand_metadirective (candidates);
> +
> + add_stmt (ret);
> +
> + return ret;
> +
> +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);
> + return error_mark_node;
> +}
> +
> +
> /* 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
> @@ -47077,6 +47480,11 @@ cp_parser_omp_construct (cp_parser *parser, cp_token
> *pragma_tok, bool *if_p)
> stmt = cp_parser_omp_master (parser, pragma_tok, p_name, mask, NULL,
> if_p);
> break;
> + case PRAGMA_OMP_METADIRECTIVE:
> + strcpy (p_name, "#pragma omp");
> + stmt = cp_parser_omp_metadirective (parser, pragma_tok, p_name, mask,
> + NULL, if_p);
> + break;
> case PRAGMA_OMP_PARALLEL:
> strcpy (p_name, "#pragma omp");
> stmt = cp_parser_omp_parallel (parser, pragma_tok, p_name, mask, NULL,
> @@ -47727,6 +48135,7 @@ cp_parser_pragma (cp_parser *parser, enum
> pragma_context context, bool *if_p)
> case PRAGMA_OMP_LOOP:
> case PRAGMA_OMP_MASKED:
> case PRAGMA_OMP_MASTER:
> + case PRAGMA_OMP_METADIRECTIVE:
> case PRAGMA_OMP_PARALLEL:
> case PRAGMA_OMP_SCOPE:
> case PRAGMA_OMP_SECTIONS:
> --
I miss handling of OMP_METADIRECTIVE in pt.c and testsuite coverage
of metadirectives in templates (both function templates and class templates
in whose methods metadirectives are used).
And something I forgot to note in the C FE patch, there is the
"The context selector that appears in a when clause must not specify any
properties
for the simd selector."
restriction I haven't seen being checked for (and tested in the testsuite).
Jakub