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 <k...@codesourcery.com> > 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 <k...@codesourcery.com> > > 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