Additional shared C/C++ testcases are included in a subsequent patch in this series.
gcc/cp/ChangeLog PR middle-end/112779 PR middle-end/113904 * cp-tree.h (struct saved_scope): Add new field x_processing_omp_trait_property_expr. (processing_omp_trait_property_expr): New. (cp_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. (cp_parser_label_for_labeled_statement): Mangle label names in a metadirective body. (cp_parser_jump_statement): Likewise. (cp_parser_omp_context_selector): Allow arbitrary expressions in device_num and condition properties. (analyze_metadirective_body): New. (cp_parser_omp_metadirective): New. (cp_parser_pragma): Handle PRAGMA_OMP_METADIRECTIVE. * parser.h (struct cp_parser): Add omp_metadirective_state field. * pt.cc (tsubst_omp_context_selector): New. (tsubst_stmt): Handle OMP_METADIRECTIVE. * semantics.cc (finish_id_expression_1): Don't diagnose use of parameter outside function body in dynamic selector expressions here. gcc/testsuite/ PR middle-end/112779 PR middle-end/113904 * c-c++-common/gomp/declare-variant-2.c: Adjust output for C++. * g++.dg/gomp/declare-variant-class-1.C: New. * g++.dg/gomp/declare-variant-class-2.C: New. * g++.dg/gomp/metadirective-template-1.C: New. libgomp/ PR middle-end/112779 PR middle-end/113904 * testsuite/libgomp.c++/metadirective-template-1.C: New. * testsuite/libgomp.c++/metadirective-template-2.C: New. * testsuite/libgomp.c++/metadirective-template-3.C: New. Co-Authored-By: Kwok Cheung Yeung <k...@codesourcery.com> Co-Authored-By: Sandra Loosemore <san...@codesourcery.com> --- gcc/cp/cp-tree.h | 2 + gcc/cp/parser.cc | 540 +++++++++++++++++- gcc/cp/parser.h | 6 + gcc/cp/pt.cc | 126 ++++ gcc/cp/semantics.cc | 3 +- .../c-c++-common/gomp/declare-variant-2.c | 4 +- .../g++.dg/gomp/declare-variant-class-1.C | 32 ++ .../g++.dg/gomp/declare-variant-class-2.C | 37 ++ .../g++.dg/gomp/metadirective-template-1.C | 74 +++ .../libgomp.c++/metadirective-template-1.C | 39 ++ .../libgomp.c++/metadirective-template-2.C | 43 ++ .../libgomp.c++/metadirective-template-3.C | 43 ++ 12 files changed, 926 insertions(+), 23 deletions(-) create mode 100644 gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C create mode 100644 gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C create mode 100644 gcc/testsuite/g++.dg/gomp/metadirective-template-1.C create mode 100644 libgomp/testsuite/libgomp.c++/metadirective-template-1.C create mode 100644 libgomp/testsuite/libgomp.c++/metadirective-template-2.C create mode 100644 libgomp/testsuite/libgomp.c++/metadirective-template-3.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 5f0529530b9..7875c5d0a57 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1929,6 +1929,7 @@ struct GTY(()) saved_scope { int suppress_location_wrappers; BOOL_BITFIELD x_processing_explicit_instantiation : 1; BOOL_BITFIELD need_pop_function_context : 1; + BOOL_BITFIELD x_processing_omp_trait_property_expr : 1; /* Nonzero if we are parsing the discarded statement of a constexpr if-statement. */ @@ -2000,6 +2001,7 @@ extern GTY(()) struct saved_scope *scope_chain; #define processing_template_decl scope_chain->x_processing_template_decl #define processing_specialization scope_chain->x_processing_specialization #define processing_explicit_instantiation scope_chain->x_processing_explicit_instantiation +#define processing_omp_trait_property_expr scope_chain->x_processing_omp_trait_property_expr /* Nonzero if we are parsing the conditional expression of a contract condition. These expressions appear outside the paramter list (like a diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 666e034dd5b..c9b0401de60 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -3020,7 +3020,7 @@ static void cp_parser_skip_to_end_of_statement static void cp_parser_consume_semicolon_at_end_of_statement (cp_parser *); static void cp_parser_skip_to_end_of_block_or_statement - (cp_parser *); + (cp_parser *, bool = false); static bool cp_parser_skip_to_closing_brace (cp_parser *); static bool cp_parser_skip_entire_template_parameter_list @@ -4245,9 +4245,11 @@ cp_parser_consume_semicolon_at_end_of_statement (cp_parser *parser) have consumed a non-nested `;'. */ static void -cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser) +cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser, + bool metadirective_p) { int nesting_depth = 0; + int bracket_depth = 0; /* Unwind generic function template scope if necessary. */ if (parser->fully_implicit_function_template_p) @@ -4269,7 +4271,7 @@ cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser) case CPP_SEMICOLON: /* Stop if this is an unnested ';'. */ - if (!nesting_depth) + if (!nesting_depth && (!metadirective_p || bracket_depth <= 0)) nesting_depth = -1; break; @@ -4288,6 +4290,19 @@ 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. */ + if (metadirective_p && nesting_depth == 0) + bracket_depth++; + break; + + case CPP_CLOSE_PAREN: + if (metadirective_p && nesting_depth == 0) + bracket_depth--; + break; + case CPP_KEYWORD: if (!cp_token_is_module_directive (token)) break; @@ -13269,6 +13284,33 @@ attr_chainon (tree attrs, tree attr) return chainon (attrs, attr); } + +/* State object for mangling labels in a metadirective region. */ + +/* 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 cp_parser_label: mangle a metadirective region + label NAME if necessary. */ +static tree +mangle_metadirective_region_label (cp_parser *parser, tree name) +{ + if (parser->omp_metadirective_state->body_labels->contains (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->omp_metadirective_state->region_num); + return get_identifier (new_name); + } + return name; +} + /* Parse the label for a labeled-statement, i.e. label: @@ -13371,7 +13413,11 @@ cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes) default: /* Anything else must be an ordinary label. */ - label = finish_label_stmt (cp_parser_identifier (parser)); + cp_expr identifier = cp_parser_identifier (parser); + if (identifier != error_mark_node + && parser->omp_metadirective_state) + *identifier = mangle_metadirective_region_label (parser, *identifier); + label = finish_label_stmt (identifier); if (label && TREE_CODE (label) == LABEL_DECL) { FALLTHROUGH_LABEL_P (label) = fallthrough_p; @@ -15246,7 +15292,13 @@ cp_parser_jump_statement (cp_parser* parser, tree &std_attrs) finish_goto_stmt (cp_parser_expression (parser)); } else - finish_goto_stmt (cp_parser_identifier (parser)); + { + cp_expr identifier = cp_parser_identifier (parser); + if (identifier != error_mark_node && parser->omp_metadirective_state) + *identifier = mangle_metadirective_region_label (parser, + *identifier); + finish_goto_stmt (identifier); + } /* Look for the final `;'. */ cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); break; @@ -49509,23 +49561,25 @@ cp_parser_omp_context_selector (cp_parser *parser, enum omp_tss_code set, break; case OMP_TRAIT_PROPERTY_DEV_NUM_EXPR: case OMP_TRAIT_PROPERTY_BOOL_EXPR: - /* FIXME: this is bogus, the expression need - not be constant. */ - t = cp_parser_constant_expression (parser); - if (t != error_mark_node) + processing_omp_trait_property_expr = true; + /* This actually parses a not-necessarily-constant + conditional-expression. */ + t = cp_parser_constant_expression (parser, true, NULL, false); + processing_omp_trait_property_expr = false; + if (t == error_mark_node) + return error_mark_node; + if (!type_dependent_expression_p (t) + && !value_dependent_expression_p (t)) { t = fold_non_dependent_expr (t); - if (!value_dependent_expression_p (t) - && (!INTEGRAL_TYPE_P (TREE_TYPE (t)) - || !tree_fits_shwi_p (t))) - error_at (token->location, "property must be " - "constant integer expression"); - else - properties = make_trait_property (NULL_TREE, t, - properties); + if (!INTEGRAL_TYPE_P (TREE_TYPE (t))) + { + error_at (token->location, + "property must be integer expression"); + return error_mark_node; + } } - else - return error_mark_node; + properties = make_trait_property (NULL_TREE, t, properties); break; case OMP_TRAIT_PROPERTY_CLAUSE_LIST: if (sel == OMP_TRAIT_CONSTRUCT_SIMD) @@ -50872,6 +50926,450 @@ cp_parser_omp_end (cp_parser *parser, cp_token *pragma_tok) } } + +/* Helper function for cp_parser_omp_metadirective. */ + +static void +analyze_metadirective_body (cp_parser *parser, + vec<cp_token> &tokens, + vec<tree> &labels) +{ + int nesting_depth = 0; + int bracket_depth = 0; + bool in_case = false; + bool in_label_decl = false; + cp_token *pragma_tok = NULL; + + while (1) + { + 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); + 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: + 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; + case CPP_PRAGMA: + parser->lexer->in_pragma = true; + pragma_tok = token; + goto add; + case CPP_PRAGMA_EOL: + /* C++ attribute syntax for OMP directives lexes as a pragma, + but we must reset the associated lexer state when we reach + the end in order to get the tokens for the statement that + come after it. */ + tokens.safe_push (*token); + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + pragma_tok = NULL; + continue; + 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 void +cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok, + bool *if_p) +{ + static unsigned int metadirective_region_count = 0; + + 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 pragma_loc = pragma_tok->location; + tree standalone_body = NULL_TREE; + vec<struct omp_variant> candidates; + bool requires_body = false; + + tree ret = make_node (OMP_METADIRECTIVE); + SET_EXPR_LOCATION (ret, pragma_loc); + TREE_TYPE (ret) = void_type_node; + OMP_METADIRECTIVE_VARIANTS (ret) = NULL_TREE; + + while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)) + { + if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) + cp_lexer_consume_token (parser->lexer); + 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%>, " + "%<otherwise%>, or %<default%> clause"); + 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 || strcmp (p, "otherwise") == 0; + if (default_p) + { + if (default_seen) + { + error_at (match_loc, "too many %<otherwise%> or %<default%> " + "clauses in %<metadirective%>"); + cp_parser_skip_to_end_of_block_or_statement (parser, true); + goto fail; + } + else + default_seen = true; + } + else if (default_seen) + { + error_at (match_loc, "%<otherwise%> or %<default%> clause " + "must appear last in %<metadirective%>"); + cp_parser_skip_to_end_of_block_or_statement (parser, true); + goto fail; + } + if (!strcmp (p, "when") == 0 && !default_p) + { + error_at (match_loc, "%qs is not valid for %qs", + p, "metadirective"); + cp_parser_skip_to_end_of_block_or_statement (parser, true); + 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); + if (ctx == error_mark_node) + goto fail; + ctx = omp_check_context_selector (match_loc, ctx, true); + if (ctx == error_mark_node) + goto fail; + + /* Remove the selector from further consideration if it can be + evaluated as a non-match at this point. */ + /* FIXME: we could still do this if the context selector + doesn't have any dependent subexpressions. */ + skip = (!processing_template_decl + && !omp_context_selector_matches (ctx, NULL_TREE, false)); + if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON)) + { + cp_parser_require (parser, CPP_COLON, RT_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; + + const char *directive[3] = {}; + int i; + for (i = 0; i < 3; i++) + { + tree id; + if (cp_lexer_peek_nth_token (parser->lexer, i + 1)->type + == CPP_CLOSE_PAREN) + { + if (i == 0) + directive[i++] = "nothing"; + break; + } + else if (cp_lexer_peek_nth_token (parser->lexer, i + 1)->type + == CPP_NAME) + id = cp_lexer_peek_nth_token (parser->lexer, i + 1)->u.value; + else if (cp_lexer_peek_nth_token (parser->lexer, i + 1)->keyword + != RID_MAX) + { + enum rid rid + = cp_lexer_peek_nth_token (parser->lexer, i + 1)->keyword; + id = ridpointers[rid]; + } + else + break; + + directive[i] = IDENTIFIER_POINTER (id); + } + if (i == 0) + { + error_at (loc, "expected directive name"); + cp_parser_skip_to_end_of_block_or_statement (parser, true); + goto fail; + } + + 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++) + cp_lexer_consume_token (parser->lexer); + cp_parser_error (parser, "unknown directive name"); + goto fail; + } + 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++) + cp_lexer_consume_token (parser->lexer); + } + if (p == NULL) + { + cp_parser_error (parser, "expected directive name"); + goto fail; + } + if (omp_directive->id == PRAGMA_OMP_METADIRECTIVE) + { + cp_parser_error (parser, + "metadirectives cannot be used as variants of a " + "%<metadirective%>"); + goto fail; + } + if (omp_directive->kind == C_OMP_DIR_DECLARATIVE) + { + sorry_at (loc, "declarative directive variants of a " + "%<metadirective%> are not supported"); + goto fail; + } + if (omp_directive->kind == C_OMP_DIR_CONSTRUCT) + requires_body = true; + + 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); + } + + if (requires_body) + 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. */ + gcc_assert (requires_body || body_tokens.is_empty ()); + 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; + struct omp_metadirective_parse_data *old_state + = parser->omp_metadirective_state; + parser->lexer = lexer; + cp_lexer_set_source_position_from_token (lexer->next_token); + struct omp_metadirective_parse_data new_state; + new_state.body_labels = &body_labels; + new_state.region_num = ++metadirective_region_count; + parser->omp_metadirective_state = &new_state; + + int prev_errorcount = errorcount; + tree directive = push_stmt_list (); + tree directive_stmt = begin_compound_stmt (0); + + 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 && 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 (); + 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, 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. */ + gcc_assert (errorcount != prev_errorcount + || cp_lexer_next_token_is (parser->lexer, CPP_EOF)); + + parser->lexer = old_lexer; + cp_lexer_set_source_position_from_token (old_lexer->next_token); + parser->omp_metadirective_state = old_state; + } + + /* Try to resolve the metadirective early. */ + if (!processing_template_decl) + { + candidates = omp_early_resolve_metadirective (ret); + if (!candidates.is_empty ()) + ret = c_omp_expand_variant_construct (candidates); + } + + add_stmt (ret); + return; + +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, true); +} + + /* 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 @@ -52862,6 +53360,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p) cp_parser_omp_nothing (parser, pragma_tok); return false; + case PRAGMA_OMP_METADIRECTIVE: + cp_parser_omp_metadirective (parser, pragma_tok, if_p); + return true; + case PRAGMA_OMP_ERROR: return cp_parser_omp_error (parser, pragma_tok, context); diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index 09b356e5e73..09ff57a7f1b 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -450,6 +450,12 @@ struct GTY(()) cp_parser { /* Pointer to state for parsing omp_loops. Managed by cp_parser_omp_for_loop in parser.cc and not used outside that file. */ struct omp_for_parse_data * GTY((skip)) omp_for_parse_state; + + /* Non-null only when parsing the body of an OpenMP metadirective. + Managed by cp_parser_omp_metadirective in parser.cc and not used + outside that file. */ + struct omp_metadirective_parse_data * GTY((skip)) + omp_metadirective_state; }; /* In parser.cc */ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 8a91c9ce9f6..2c6027c7879 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -18056,6 +18056,86 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort, return new_clauses; } +/* Like tsubst_copy, but specifically for OpenMP context selectors. */ +static tree +tsubst_omp_context_selector (tree ctx, tree args, tsubst_flags_t complain, + tree in_decl) +{ + tree new_ctx = NULL_TREE; + for (tree set = ctx; set; set = TREE_CHAIN (set)) + { + tree selectors = NULL_TREE; + for (tree sel = OMP_TSS_TRAIT_SELECTORS (set); sel; + sel = TREE_CHAIN (sel)) + { + enum omp_ts_code code = OMP_TS_CODE (sel); + tree properties = NULL_TREE; + tree score = OMP_TS_SCORE (sel); + tree t; + + if (score) + { + score = tsubst_expr (score, args, complain, in_decl); + score = fold_non_dependent_expr (score); + if (!value_dependent_expression_p (score) + && !type_dependent_expression_p (score)) + { + if (!INTEGRAL_TYPE_P (TREE_TYPE (score)) + || TREE_CODE (score) != INTEGER_CST) + { + error_at (cp_expr_loc_or_input_loc (score), + "%<score%> argument must " + "be constant integer expression"); + score = NULL_TREE; + } + else if (tree_int_cst_sgn (score) < 0) + { + error_at (cp_expr_loc_or_input_loc (score), + "%<score%> argument must be non-negative"); + score = NULL_TREE; + } + } + } + + switch (omp_ts_map[OMP_TS_CODE (sel)].tp_type) + { + case OMP_TRAIT_PROPERTY_DEV_NUM_EXPR: + case OMP_TRAIT_PROPERTY_BOOL_EXPR: + t = tsubst_expr (OMP_TP_VALUE (OMP_TS_PROPERTIES (sel)), + args, complain, in_decl); + t = fold_non_dependent_expr (t); + if (!value_dependent_expression_p (t) + && !type_dependent_expression_p (t) + && !INTEGRAL_TYPE_P (TREE_TYPE (t))) + error_at (cp_expr_loc_or_input_loc (t), + "property must be integer expression"); + else + properties = make_trait_property (NULL_TREE, t, NULL_TREE); + break; + case OMP_TRAIT_PROPERTY_CLAUSE_LIST: + if (OMP_TS_CODE (sel) == OMP_TRAIT_CONSTRUCT_SIMD) + properties = tsubst_omp_clauses (OMP_TS_PROPERTIES (sel), + C_ORT_OMP_DECLARE_SIMD, + args, complain, in_decl); + break; + default: + /* Nothing to do here, just copy. */ + for (tree prop = OMP_TS_PROPERTIES (sel); + prop; prop = TREE_CHAIN (prop)) + properties = make_trait_property (OMP_TP_NAME (prop), + OMP_TP_VALUE (prop), + properties); + } + selectors = make_trait_selector (code, score, properties, selectors); + } + new_ctx = make_trait_set_selector (OMP_TSS_CODE (set), + nreverse (selectors), + new_ctx); + } + return nreverse (new_ctx); +} + + /* Like tsubst_expr, but unshare TREE_LIST nodes. */ static tree @@ -19661,6 +19741,52 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl) } break; + case OMP_METADIRECTIVE: + { + tree variants = NULL_TREE; + for (tree v = OMP_METADIRECTIVE_VARIANTS (t); v; v = TREE_CHAIN (v)) + { + tree ctx = OMP_METADIRECTIVE_VARIANT_SELECTOR (v); + tree directive = OMP_METADIRECTIVE_VARIANT_DIRECTIVE (v); + tree body = OMP_METADIRECTIVE_VARIANT_BODY (v); + tree s; + + /* CTX is null if this is the default variant. */ + if (ctx) + { + ctx = tsubst_omp_context_selector (ctx, args, complain, + in_decl); + /* Remove the selector from further consideration if it can be + evaluated as a non-match at this point. */ + if (omp_context_selector_matches (ctx, NULL_TREE, false) == 0) + continue; + } + s = push_stmt_list (); + RECUR (directive); + directive = pop_stmt_list (s); + if (body) + { + s = push_stmt_list (); + RECUR (body); + body = pop_stmt_list (s); + } + variants + = chainon (variants, + make_omp_metadirective_variant (ctx, directive, + body)); + } + t = copy_node (t); + OMP_METADIRECTIVE_VARIANTS (t) = variants; + + /* Try to resolve the metadirective early. */ + vec<struct omp_variant> candidates + = omp_early_resolve_metadirective (t); + if (!candidates.is_empty ()) + t = c_omp_expand_variant_construct (candidates); + add_stmt (t); + break; + } + case TRANSACTION_EXPR: { int flags = 0; diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 8dc687f1001..36f348aff00 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -4606,7 +4606,8 @@ finish_id_expression_1 (tree id_expression, if (TREE_CODE (decl) == PARM_DECL && DECL_CONTEXT (decl) == NULL_TREE && !cp_unevaluated_operand - && !processing_contract_condition) + && !processing_contract_condition + && !processing_omp_trait_property_expr) { *error_msg = G_("use of parameter outside function body"); return error_mark_node; 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 13b0614cac4..27c1cdff044 100644 --- a/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c +++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c @@ -38,9 +38,7 @@ 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 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 } */ +#pragma omp declare variant (f1) match(user={condition(f1)}) /* { dg-error "property must be integer expression" } */ void f21 (void); #pragma omp declare variant (f1) match(user={condition(1, 2, 3)}) /* { dg-error "expected '\\)' before ',' token" } */ void f22 (void); diff --git a/gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C b/gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C new file mode 100644 index 00000000000..35b25de860f --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-foffload=disable" } */ +/* { dg-additional-options "-mavx512bw -mavx512vl" { target { i?86-*-* x86_64-*-* } } } */ + +/* References to function parameters in dynamic selector expressions for + "declare variant" isn't supported yet; see PR 113904. Check to see that + a proper error is diagnosed meanwhile and GCC doesn't just wander off + into the weeds and ICE. */ + +extern int frob (int); + +class junk +{ + public: + int data; + static void f01 (int, int); + static void f02 (int, int); + static void f03 (int, int); +#pragma omp declare variant (f01) match (target_device={device_num (devnum), isa("avx512f","avx512vl")}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ +#pragma omp declare variant (f02) match (implementation={vendor(score(15):gnu)}) +#pragma omp declare variant (f03) match (user={condition(score(11):frob (ok + 42))}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ + static void f04 (int devnum, int ok); +}; + +void +test1 (void) +{ + int i; + #pragma omp parallel for + for (i = 0; i < 1; i++) + junk::f04 (17, 1); +} diff --git a/gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C b/gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C new file mode 100644 index 00000000000..b30243f70c6 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C @@ -0,0 +1,37 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-foffload=disable" } */ +/* { dg-additional-options "-mavx512bw -mavx512vl" { target { i?86-*-* x86_64-*-* } } } */ + +/* References to function parameters in dynamic selector expressions for + "declare variant" isn't supported yet; see PR 113904. Check to see that + a proper error is diagnosed meanwhile and GCC doesn't just wander off + into the weeds and ICE. */ + +extern int frob (int); +extern int frobmore (class junk *); + +class junk +{ + public: + int data; + void f01 (int, int); + void f02 (int, int); + void f03 (int, int); + void f04 (int, int); + void f05 (int, int); +#pragma omp declare variant (f01) match (target_device={device_num (devnum), isa("avx512f","avx512vl")}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ +#pragma omp declare variant (f02) match (implementation={vendor(score(15):gnu)}) +#pragma omp declare variant (f03) match (user={condition(score(11):frob (ok + 42))}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ +#pragma omp declare variant (f04) match (user={condition(score(11):data)}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ +#pragma omp declare variant (f05) match (user={condition(score(11):frobmore (this))}) /* { dg-message "sorry, unimplemented: reference to function parameter" } */ + void f06 (int devnum, int ok); +}; + +void +test1 (junk *j) +{ + int i; + #pragma omp parallel for + for (i = 0; i < 1; i++) + j->f06 (17, 1); +} diff --git a/gcc/testsuite/g++.dg/gomp/metadirective-template-1.C b/gcc/testsuite/g++.dg/gomp/metadirective-template-1.C new file mode 100644 index 00000000000..92e72baf1c2 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/metadirective-template-1.C @@ -0,0 +1,74 @@ +// { dg-do compile } +// { dg-additional-options "-fdump-tree-gimple" } + +// Check that expressions in metadirectives are handled correctly in template +// functions when they are value-dependent and/or type-dependent. + +int x; + +// Type-dependent expression. +template <typename T> +void +f1 (void) +{ +#pragma omp metadirective \ + when (user={condition(static_cast<T> (1.0))}: target) + x = 1; +} + +// Value-dependent expression. +template <int N> +void +f2 (void) +{ +#pragma omp metadirective \ + when (user={condition(N)}: target) + x = 2; +} + +// Both type- and value-dependent. +template <typename T, T N> +void +f3 (void) +{ +#pragma omp metadirective \ + when (user={condition(N)}: target) + x = 3; +} + +// Expression is itself a template instantiation. +template <typename T, T N> +bool +test (void) +{ + return N != 0; +} + +template <typename T, T N> +void +f4 (void) +{ +#pragma omp metadirective \ + when (user={condition(test<T, N> ())}: teams) + x = 4; +} + +int +main (void) +{ + f1 <int> (); + f2 <1> (); + f2 <0> (); + f3 <int, 1> (); + f3 <int, 0> (); + f4 <int, 1> (); + f4 <int, 0> (); +} + +// Each of f1..f3 should be instantiated once with a condition expression +// that is a constant 1, producing a target construct. The metadirective in +// f4 has a non-constant condition expression, so both instantiations will +// produce a conditional including an alternative including a teams construct. + +// { dg-final { scan-tree-dump-times "pragma omp target" 3 "gimple" } } +// { dg-final { scan-tree-dump-times "pragma omp teams" 2 "gimple" } } diff --git a/libgomp/testsuite/libgomp.c++/metadirective-template-1.C b/libgomp/testsuite/libgomp.c++/metadirective-template-1.C new file mode 100644 index 00000000000..45115366522 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/metadirective-template-1.C @@ -0,0 +1,39 @@ +#include <stdio.h> + +template <bool tasking> +int +fib (int n) +{ + int i, j; + if (n < 2) + return n; + else if ( tasking && n < 8 ) // serial/taskless cutoff for n<8 + return fib<false> (n); + else + { +#pragma omp metadirective \ + when (user = {condition (tasking)}: task shared(i)) + i = fib<tasking> (n - 1); +#pragma omp metadirective \ + when (user = {condition (score(10): tasking)}: task shared(j)) + j = fib<tasking> (n - 2); +#pragma omp metadirective \ + when (user = {condition (tasking)}: taskwait) + return i + j; + } +} + +int +main () +{ + int n = 15, o = 610; +#pragma omp parallel +#pragma omp single + { + if (fib<true> (n) != o) + __builtin_abort (); + if (fib<false> (n) != o) + __builtin_abort (); + } + return 0; +} diff --git a/libgomp/testsuite/libgomp.c++/metadirective-template-2.C b/libgomp/testsuite/libgomp.c++/metadirective-template-2.C new file mode 100644 index 00000000000..a116bfd6f61 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/metadirective-template-2.C @@ -0,0 +1,43 @@ +# include <stdio.h> + +template <bool tasking> +int +fib (int n, bool flag) +{ + int i, j; + if (n < 2) + return n; + else if ( tasking && flag && n < 8 ) // serial/taskless cutoff for n<8 + return fib<false> (n, false); + else + { +#pragma omp metadirective \ + when (user = {condition (tasking && flag)}: task shared(i)) + i = fib<tasking> (n - 1, flag); +#pragma omp metadirective \ + when (user = {condition (score(10): tasking && flag)}: task shared(j)) + j = fib<tasking> (n - 2, flag); +#pragma omp metadirective \ + when (user = {condition (tasking && flag)}: taskwait) + return i + j; + } +} + +int +main () +{ + int n = 15, o = 610; +#pragma omp parallel +#pragma omp single + { + if (fib<true> (n, true) != o) + __builtin_abort (); + if (fib<true> (n, false) != o) + __builtin_abort (); + if (fib<false> (n, false) != o) + __builtin_abort (); + if (fib<false> (n, true) != o) + __builtin_abort (); + } + return 0; +} diff --git a/libgomp/testsuite/libgomp.c++/metadirective-template-3.C b/libgomp/testsuite/libgomp.c++/metadirective-template-3.C new file mode 100644 index 00000000000..934ae499c00 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/metadirective-template-3.C @@ -0,0 +1,43 @@ +# include <stdio.h> + +template <bool tasking> +int +fib (int n, bool flag) +{ + int i, j; + if (n < 2) + return n; + else if ( tasking && flag && n < 8 ) // serial/taskless cutoff for n<8 + return fib<false> (n, false); + else + { +#pragma omp metadirective \ + when (user = {condition (tasking && flag)}: task shared(i)) \ + when (user = {condition (!tasking && !flag)}: nothing) \ + otherwise (error at(execution) message("oops 1")) + i = fib<tasking> (n - 1, flag); +#pragma omp metadirective \ + when (user = {condition (score(10): tasking && flag)}: task shared(j)) \ + when (user = {condition (tasking || flag)} : \ + error at(execution) message ("oops 2")) + j = fib<tasking> (n - 2, flag); +#pragma omp metadirective \ + when (user = {condition (tasking && flag)}: taskwait) + return i + j; + } +} + +int +main () +{ + int n = 15, o = 610; +#pragma omp parallel +#pragma omp single + { + if (fib<true> (n, true) != o) + __builtin_abort (); + if (fib<false> (n, false) != o) + __builtin_abort (); + } + return 0; +} -- 2.25.1