diff options
| author | Sandra Loosemore <sloosemore@baylibre.com> | 2025-11-20 21:45:09 +0000 |
|---|---|---|
| committer | Sandra Loosemore <sloosemore@baylibre.com> | 2025-11-22 17:05:22 +0000 |
| commit | a469cf3df2ac97944a44948891e9fe22ba2f1d10 (patch) | |
| tree | 735f89ddef0e2968b4f87611fc31724a68b9a210 /gcc/cp | |
| parent | 31107ba466715f3f662e9ddcefd5c33c483d0a31 (diff) | |
OpenMP: C++ front end support for "begin declare variant"
This patch implements C++ support for the "begin declare variant"
construct. The OpenMP specification is hazy on interaction of this
feature with C++ language features. Variant functions in classes are
supported but must be defined as members in the class definition,
using an unqualified name for the base function which also must be
present in that class. Similarly variant functions in a namespace can
only be defined in that namespace using an unqualified name for a base
function already declared in that namespace. Variants for template
functions or inside template classes seem to (mostly) work.
gcc/c-family/ChangeLog
* c-omp.cc (c_omp_directives): Uncomment "begin declare variant"
and "end declare variant".
gcc/cp/ChangeLog
* cp-tree.h (struct cp_omp_declare_variant_attr): New.
(struct saved_scope): Add omp_declare_variant_attribute field.
* decl.cc (omp_declare_variant_finalize_one): Add logic to inject
"this" parameter for method calls.
* parser.cc (cp_parser_skip_to_pragma_omp_end_declare_variant): New.
(cp_parser_translation_unit): Handle leftover "begin declare variant"
functions.
(omp_start_variant_function): New.
(omp_finish_variant_function): New.
(omp_maybe_record_variant_base): New.
(cp_parser_init_declarator): Handle variant functions.
(cp_parser_class_specifier): Handle deferred lookup of base functions
when the entire class has been seen.
(cp_parser_member_declaration): Handle variant functions.
(cp_finish_omp_declare_variant): Merge context selectors if in
a "begin declare variant" block.
(cp_parser_omp_begin): Match "omp begin declare variant". Adjust
error messages.
(cp_parser_omp_end): Match "omp end declare variant".
* parser.h (struct omp_begin_declare_variant_map_entry): New.
(struct cp_parser): Add omp_begin_declare_variant_map field.
* semantics.cc (finish_translation_unit): Detect unmatched
"omp begin declare variant".
gcc/testsuite/ChangeLog
* g++.dg/gomp/delim-declare-variant-1.C: New.
* g++.dg/gomp/delim-declare-variant-2.C: New.
* g++.dg/gomp/delim-declare-variant-3.C: New.
* g++.dg/gomp/delim-declare-variant-4.C: New.
* g++.dg/gomp/delim-declare-variant-5.C: New.
* g++.dg/gomp/delim-declare-variant-6.C: New.
* g++.dg/gomp/delim-declare-variant-7.C: New.
* g++.dg/gomp/delim-declare-variant-40.C: New.
* g++.dg/gomp/delim-declare-variant-41.C: New.
* g++.dg/gomp/delim-declare-variant-50.C: New.
* g++.dg/gomp/delim-declare-variant-51.C: New.
* g++.dg/gomp/delim-declare-variant-52.C: New.
* g++.dg/gomp/delim-declare-variant-70.C: New.
* g++.dg/gomp/delim-declare-variant-71.C: New.
libgomp/
* testsuite/libgomp.c++/bdv_module1.C: New.
* testsuite/libgomp.c++/bdv_module1_main.C: New.
* testsuite/libgomp.c++/bdv_module2.C: New.
* testsuite/libgomp.c++/bdv_module2_impl.C: New.
* testsuite/libgomp.c++/bdv_module2_main.C: New.
* testsuite/libgomp.c++/bdv_module3.C: New.
* testsuite/libgomp.c++/bdv_module3_impl.C: New.
* testsuite/libgomp.c++/bdv_module3_main.C: New.
* testsuite/libgomp.c++/delim-declare-variant-1.C: New.
* testsuite/libgomp.c++/delim-declare-variant-2.C: New.
* testsuite/libgomp.c++/delim-declare-variant-7.C: New.
Co-Authored-By: Julian Brown <julian@codesourcery.com>
Co-Authored-By: waffl3x <waffl3x@baylibre.com>
Diffstat (limited to 'gcc/cp')
| -rw-r--r-- | gcc/cp/cp-tree.h | 6 | ||||
| -rw-r--r-- | gcc/cp/decl.cc | 15 | ||||
| -rw-r--r-- | gcc/cp/parser.cc | 642 | ||||
| -rw-r--r-- | gcc/cp/parser.h | 12 | ||||
| -rw-r--r-- | gcc/cp/semantics.cc | 7 |
5 files changed, 649 insertions, 33 deletions
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index c6e284d060c..9b679fcf16a 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1946,6 +1946,11 @@ struct GTY(()) cp_omp_begin_assumes_data { bool attr_syntax; }; +struct GTY(()) cp_omp_declare_variant_attr { + bool attr_syntax; + tree selector; +}; + /* Global state. */ struct GTY(()) saved_scope { @@ -1998,6 +2003,7 @@ struct GTY(()) saved_scope { hash_map<tree, tree> *GTY((skip)) x_local_specializations; vec<cp_omp_declare_target_attr, va_gc> *omp_declare_target_attribute; vec<cp_omp_begin_assumes_data, va_gc> *omp_begin_assumes; + vec<cp_omp_declare_variant_attr, va_gc> *omp_declare_variant_attribute; struct saved_scope *prev; }; diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index c62f0777f5b..c5066dfc60b 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -9075,6 +9075,21 @@ omp_declare_variant_finalize_one (tree decl, tree attr) if (idk == CP_ID_KIND_QUALIFIED) variant = finish_call_expr (variant, &args, /*disallow_virtual=*/true, koenig_p, tf_warning_or_error); + else if (idk == CP_ID_KIND_NONE + && TREE_CODE (variant) == FUNCTION_DECL + && DECL_IOBJ_MEMBER_FUNCTION_P (variant) + && CLASS_TYPE_P (DECL_CONTEXT (decl))) + { + tree saved_ccp = current_class_ptr; + tree saved_ccr = current_class_ref; + current_class_ptr = NULL_TREE; + current_class_ref = NULL_TREE; + inject_this_parameter (DECL_CONTEXT (decl), TYPE_UNQUALIFIED); + variant = finish_call_expr (variant, &args, /*disallow_virtual=*/false, + koenig_p, tf_warning_or_error); + current_class_ptr = saved_ccp; + current_class_ref = saved_ccr; + } else variant = finish_call_expr (variant, &args, /*disallow_virtual=*/false, koenig_p, tf_warning_or_error); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index dc26b10f39b..e0c8e0ec8ad 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -273,6 +273,10 @@ static void cp_finalize_oacc_routine static void check_omp_intervening_code (cp_parser *); +static tree omp_start_variant_function + (cp_declarator *, tree); +static int omp_finish_variant_function + (cp_parser *, tree, tree, tree, bool, bool); /* Manifest constants. */ #define CP_LEXER_BUFFER_SIZE ((256 * 1024) / sizeof (cp_token)) @@ -4561,6 +4565,54 @@ cp_parser_require_pragma_eol (cp_parser *parser, cp_token *pragma_tok) } } +/* Skip tokens up to and including "#pragma omp end declare variant". + Properly handle nested "#pragma omp begin declare variant" pragmas. */ +static void +cp_parser_skip_to_pragma_omp_end_declare_variant (cp_parser *parser) +{ + for (int depth = 0; depth >= 0; ) + { + cp_token *token = cp_lexer_peek_token (parser->lexer); + + switch (token->type) + { + case CPP_PRAGMA_EOL: + if (!parser->lexer->in_pragma) + break; + /* FALLTHRU */ + case CPP_EOF: + /* If we've run out of tokens, stop. */ + return; + + case CPP_PRAGMA: + if ((cp_parser_pragma_kind (token) == PRAGMA_OMP_BEGIN + || cp_parser_pragma_kind (token) == PRAGMA_OMP_END) + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME) + && cp_lexer_nth_token_is (parser->lexer, 3, CPP_NAME)) + { + tree id1 = cp_lexer_peek_nth_token (parser->lexer, 2)->u.value; + tree id2 = cp_lexer_peek_nth_token (parser->lexer, 3)->u.value; + if (strcmp (IDENTIFIER_POINTER (id1), "declare") == 0 + && strcmp (IDENTIFIER_POINTER (id2), "variant") == 0) + { + if (cp_parser_pragma_kind (token) == PRAGMA_OMP_BEGIN) + depth++; + else + depth--; + } + } + cp_parser_skip_to_pragma_eol (parser, token); + continue; + + default: + break; + } + + /* Consume the token. */ + cp_lexer_consume_token (parser->lexer); + } +} + /* This is a simple wrapper around make_typename_type. When the id is an unresolved identifier node, we can provide a superior diagnostic using cp_parser_diagnose_invalid_type_name. */ @@ -5506,6 +5558,22 @@ cp_parser_translation_unit (cp_parser* parser) cp_parser_toplevel_declaration (parser); } + /* Register any functions found in "begin declare variant" blocks, whose + base function declarations may be found "elsewhere" in the translation + unit. */ + /* FIXME: we could probably just diagnose any leftover functions as an + error directly here, all valid cases were supposed to have been + caught already. */ + if (vec_safe_length (parser->omp_begin_declare_variant_map)) + { + unsigned int i; + omp_begin_declare_variant_map_entry *e; + FOR_EACH_VEC_ELT (*parser->omp_begin_declare_variant_map, i, e) + omp_finish_variant_function (parser, e->variant, e->id, e->ctx, + false, true); + parser->omp_begin_declare_variant_map = NULL; + } + #if ENABLE_ANALYZER if (flag_analyzer) { @@ -24515,6 +24583,278 @@ cp_parser_maybe_adjust_declarator_for_dguide (cp_parser *parser, } } +/* Helper function for OpenMP "begin declare variant" directives. + Function definitions inside the construct need to have their names + mangled according to the context selector CTX. The DECLARATOR is + modified in place to point to a new identifier; the original name of + the function is returned. */ +static tree +omp_start_variant_function (cp_declarator *declarator, tree ctx) +{ + cp_declarator *id = get_id_declarator (declarator); + tree name = id->u.id.unqualified_name; + tree scope = id->u.id.qualifying_scope; + enum special_function_kind sfk = id->u.id.sfk; + + /* There seems to be no reasonable interpretation of what the behavior + should be if the name is qualified. You cannot add the variant function + to a class or namespace from outside of that scope. */ + if (scope) + { + sorry_at (id->id_loc, + "cannot handle qualified name for variant function"); + return NULL_TREE; + } + + /* Catch disallowed constructors and destructors now. We can't mangle + destructor names (which are not IDENTIFIER_NODEs) in any case. */ + if (sfk == sfk_constructor) + { + error_at (id->id_loc, + "declare variant directives are not allowed on constructors"); + return NULL_TREE; + } + if (sfk == sfk_destructor) + { + error_at (id->id_loc, + "declare variant directives are not allowed on destructors"); + return NULL_TREE; + } + if (TREE_CODE (name) != IDENTIFIER_NODE) + { + sorry_at (id->id_loc, + "cannot handle %s identifier name", + get_tree_code_name (TREE_CODE (name))); + return NULL_TREE; + } + + /* Mangle the name in the declarator. If we're in a module interface, + add an additional prefix on the name ("mi" == "module interface"), + so that it will not collide with names in the module purview that + are defined locally within module interface TUs. Note that the + module name mangling of this name happens after this step. */ + const char *sep + = (module_interface_p () ? JOIN_STR "mi" : JOIN_STR); + id->u.id.unqualified_name = omp_mangle_variant_name (name, ctx, sep); + + return name; +} + +/* Helper function for OpenMP "begin declare variant" directives. Now + that we have a DECL for the variant function, and BASE_NAME for the + base function, look up the decl for BASE_NAME in the same scope as + DECL, add an "omp declare variant base" attribute pointing at CTX + to the base decl, and an "omp declare variant variant" attribute to + the variant DECL. IN_CLASS_P is true for DECLs appearing in a class + scope, and if DIAGNOSE_ERROR_P is false do not diagnose errors about + failure to match at this time (although other semantic errors when a + match is found are always diagnosed). Returns 1 on success, 0 for no + match, and -1 for a match with semantic errors. */ +static int +omp_finish_variant_function (cp_parser *parser, tree decl, tree base_name, + tree ctx, bool in_class_p, bool diagnose_error_p) +{ + tree match = NULL_TREE; + bool is_template = false; + tree decl_context = CP_DECL_CONTEXT (decl); + tree base_decl; + + /* First find the base_decl in the same scope as decl. If we are in + a class scope the parser still points at the scope, otherwise + look up base_name in the same namespace as decl appeared in. */ + if (in_class_p) + base_decl = cp_parser_lookup_name_simple (parser, base_name, + DECL_SOURCE_LOCATION (decl)); + else + base_decl = lookup_qualified_name (decl_context, base_name); + + if (base_decl == error_mark_node) + base_decl = NULL_TREE; + if (!base_decl) + { + if (!diagnose_error_p) + return 0; + /* We previously rejected non-identifier base function names. */ + error_at (DECL_SOURCE_LOCATION (decl), + "no declaration of base function %qs in this scope", + IDENTIFIER_POINTER (base_name)); + return -1; + } + + /* Find the right overloaded function. */ + if (TREE_CODE (base_decl) == OVERLOAD) + { + for (ovl_iterator iter (base_decl); iter; ++iter) + { + tree bb = *iter; + if (decls_match (decl, bb)) + { + match = bb; + break; + } + else if (TREE_CODE (bb) == TEMPLATE_DECL + && TREE_CODE (decl) == FUNCTION_DECL + && DECL_TEMPLATE_INFO (decl)) + { + tree decl_template = DECL_TI_TEMPLATE (decl); + if (decl_template + && PRIMARY_TEMPLATE_P (decl_template) + && decls_match (bb, decl_template)) + { + /* We want to put the attributes on the function rather + than on the TEMPLATE_DECL that points to it. */ + match = DECL_TEMPLATE_RESULT (bb); + is_template = true; + break; + } + } + } + } + else if (decls_match (decl, base_decl)) + match = base_decl; + else if (TREE_CODE (base_decl) == TEMPLATE_DECL) + /* Per comment in cp-tree.h, TEMPLATE_DECLs are always wrapped in an + OVERLOAD, so we should never see them here. */ + gcc_unreachable (); + else if (TREE_CODE (base_decl) == TREE_LIST) + { + error_at (DECL_SOURCE_LOCATION (decl), "base function is ambiguous"); + return -1; + } + else if (BASELINK_P (base_decl)) + { + /* Baselink tree nodes do not have a DECL_NAME so we can't use it + in the error message. */ + error_at (DECL_SOURCE_LOCATION (decl), + "variant function must be in the same scope as the " + "base function %qs", IDENTIFIER_POINTER (base_name)); + return -1; + } + if (!match) + { + /* We get here if base_decl doesn't match DECL or have an + overload matching DECL. */ + if (!diagnose_error_p) + return 0; + error_at (DECL_SOURCE_LOCATION (decl), + "variant function definition does not match " + "declaration of %qE", base_decl); + if (TREE_CODE (base_decl) != OVERLOAD) + inform (DECL_SOURCE_LOCATION (base_decl), "declared here"); + return -1; + } + else if (CP_DECL_CONTEXT (match) != decl_context) + { + /* Reject inherited or using decls. */ + error_at (DECL_SOURCE_LOCATION (decl), + "variant function must be in the same scope as the " + "base function %qE", base_decl); + return -1; + } + else if (DECL_VIRTUAL_P (decl) || DECL_VIRTUAL_P (match)) + { + error_at (DECL_SOURCE_LOCATION (decl), + "declare variant directives are not allowed on " + "virtual functions"); + return -1; + } + else if (DECL_DEFAULTED_FN (decl) || DECL_DEFAULTED_FN (match)) + { + error_at (DECL_SOURCE_LOCATION (decl), + "declare variant directives are not allowed on " + "defaulted functions"); + return -1; + } + else if (DECL_DELETED_FN (decl) || DECL_DELETED_FN (match)) + { + error_at (DECL_SOURCE_LOCATION (decl), + "declare variant directives are not allowed on " + "deleted functions"); + return -1; + } + else if (DECL_IMMEDIATE_FUNCTION_P (decl) + || DECL_IMMEDIATE_FUNCTION_P (match)) + { + error_at (DECL_SOURCE_LOCATION (decl), + "declare variant directives are not allowed on " + "immediate functions"); + return -1; + } + + /* Inside a template, make the "omp declare variant base" attribute + point to the name of DECL rather than DECL itself. During template + instantiation, omp_declare_variant_finalize_one will handle this + using the same logic as for the non-delimited form of "declare variant", + causing template instantiation as needed. For the non-template case, + there is nothing that will trigger omp_declare_variant_finalize_one; + so we create the final form of the attribute here, which points + directly to DECL rather than its name. */ + tree decl_or_name = decl; + cp_id_kind idk = CP_ID_KIND_NONE; + if (processing_template_decl && is_template) + { + decl_or_name = DECL_NAME (decl); + idk = CP_ID_KIND_TEMPLATE_ID; + } + + omp_check_for_duplicate_variant (DECL_SOURCE_LOCATION (decl), + match, ctx); + tree construct + = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT); + omp_mark_declare_variant (DECL_SOURCE_LOCATION (decl), decl, construct); + + tree attrs = DECL_ATTRIBUTES (match); + tree match_loc_node = build_empty_stmt (DECL_SOURCE_LOCATION (match)); + tree loc_node = tree_cons (match_loc_node, + build_int_cst (integer_type_node, idk), + build_tree_list (match_loc_node, + integer_zero_node)); + attrs = tree_cons (get_identifier ("omp declare variant base"), + tree_cons (decl_or_name, ctx, loc_node), attrs); + if (processing_template_decl) + ATTR_IS_DEPENDENT (attrs) = 1; + DECL_ATTRIBUTES (match) = attrs; + + /* Except for variants defined in a module interface, variant functions + are essentially anonymous and cannot be referenced by name, so make + them have internal linkage. Note that class methods in C++ normally + have external linkage with weak/comdat semantics; this prevents that. */ + if (!module_interface_p ()) + { + TREE_PUBLIC (decl) = 0; + DECL_COMDAT (decl) = 0; + DECL_INTERFACE_KNOWN (decl) = 1; + DECL_NOT_REALLY_EXTERN (decl) = 1; + } + return 1; +} + +/* Called when we have seen a function declaration or definition DECL. + OpenMP "begin declare variant" allows variant definitions to precede + the declaration of the base function. If DECL is a newly-declared + function matching some variant that hasn't been recorded on its base + function yet, do that now. */ + +static void +omp_maybe_record_variant_base (cp_parser* parser, tree decl) +{ + if (vec_safe_length (parser->omp_begin_declare_variant_map)) + { + unsigned int i; + omp_begin_declare_variant_map_entry *e; + FOR_EACH_VEC_ELT (*parser->omp_begin_declare_variant_map, i, e) + { + if (DECL_NAME (decl) == e->id + && omp_finish_variant_function (parser, e->variant, e->id, + e->ctx, false, false)) + { + parser->omp_begin_declare_variant_map->unordered_remove (i); + break; + } + } + } +} + /* Declarators [gram.dcl.decl] */ /* Parse an init-declarator. @@ -24731,6 +25071,27 @@ cp_parser_init_declarator (cp_parser* parser, /* This is a function-definition. */ *function_definition_p = true; + /* If we're in an OpenMP "begin declare variant" block, the + name in the declarator refers to the base function. We need + to save that and modify the declarator to have the mangled + name for the variant function instead. */ + tree dv_base = NULL_TREE; + tree dv_ctx = NULL_TREE; + vec<cp_omp_declare_variant_attr, va_gc> *dv_state + = scope_chain->omp_declare_variant_attribute; + + if (!vec_safe_is_empty (dv_state)) + { + cp_omp_declare_variant_attr a = dv_state->last (); + dv_ctx = copy_list (a.selector); + dv_base = omp_start_variant_function (declarator, dv_ctx); + if (dv_base == NULL_TREE) + { + cp_parser_skip_to_end_of_statement (parser); + return error_mark_node; + } + } + /* Parse the function definition. */ if (member_p) decl = cp_parser_save_member_function_body (parser, @@ -24749,6 +25110,20 @@ cp_parser_init_declarator (cp_parser* parser, = func_brace_location; } + /* If this function was in a "begin declare variant" block, + store the pointer back to the base function and fix up + the attributes for the middle end. Do it now if the base + function has already been declared, otherwise save the info. */ + if (dv_base && decl != error_mark_node + && !omp_finish_variant_function (parser, decl, dv_base, dv_ctx, + false, false)) + { + omp_begin_declare_variant_map_entry e = { decl, dv_base, dv_ctx }; + vec_safe_push (parser->omp_begin_declare_variant_map, e); + } + else if (flag_openmp && !member_p) + omp_maybe_record_variant_base (parser, decl); + return decl; } } @@ -24826,6 +25201,27 @@ cp_parser_init_declarator (cp_parser* parser, is_initialized = SD_DEFAULTED; else if (t2->keyword == RID_DELETE) is_initialized = SD_DELETED; + if (!vec_safe_is_empty (scope_chain->omp_declare_variant_attribute)) + { + /* We're in a "begin declare variant" construct. The parser + doesn't go through the normal function definition path for + these and hence doesn't invoke omp_finish_variant_function + where these errors would otherwise be caught. */ + if (is_initialized == SD_DEFAULTED) + { + error_at (declarator->init_loc, + "declare variant directives are not allowed on " + "defaulted functions"); + return error_mark_node; + } + else if (is_initialized == SD_DELETED) + { + error_at (declarator->init_loc, + "declare variant directives are not allowed on " + "deleted functions"); + return error_mark_node; + } + } } } else @@ -25075,6 +25471,19 @@ cp_parser_init_declarator (cp_parser* parser, && type_uses_auto (decl_specifiers->type)) *auto_result = strip_declarator_types (TREE_TYPE (decl), declarator); + /* Check whether a function declaration might be a base function for a + variant previously declared in an OpenMP "begin declare variant" block + that has not been recorded yet. Class members are ignored here because + they are all processed after parsing the whole class. + FIXME: We store these variants in a vector and use a linear search here. + We don't expect gazillions of these variant functions to be provided + without an earlier declaration, but if it's problematical we could + use a hash table instead. */ + if (flag_openmp + && !member_p + && function_declarator_p (declarator)) + omp_maybe_record_variant_base (parser, decl); + return decl; } @@ -28326,6 +28735,11 @@ cp_parser_class_specifier (cp_parser* parser) tree saved_ccr = current_class_ref; current_class_ptr = NULL_TREE; current_class_ref = NULL_TREE; + /* Set up for deferred lookup of "omp begin declare variant" base functions + in the class. */ + vec<omp_begin_declare_variant_map_entry, va_gc> *save_variant_map + = parser->omp_begin_declare_variant_map; + parser->omp_begin_declare_variant_map = NULL; /* Start the class. */ if (nested_name_specifier_p) @@ -28347,6 +28761,19 @@ cp_parser_class_specifier (cp_parser* parser) /* Parse the member-specification. */ cp_parser_member_specification_opt (parser); + /* Register any "begin declare variant" functions in this class, since + references to the base function can only be resolved after the + entire class is seen. */ + if (vec_safe_length (parser->omp_begin_declare_variant_map)) + { + unsigned int i; + omp_begin_declare_variant_map_entry *e; + FOR_EACH_VEC_ELT (*parser->omp_begin_declare_variant_map, i, e) + omp_finish_variant_function (parser, e->variant, e->id, e->ctx, + true, true); + } + parser->omp_begin_declare_variant_map = save_variant_map; + /* Look for the trailing `}'. */ closing_brace = braces.require_close (parser); /* Look for trailing attributes to apply to this class. */ @@ -30070,6 +30497,28 @@ cp_parser_member_declaration (cp_parser* parser) if (initializer && initializer_token_start) error_at (initializer_token_start->location, "pure-specifier on function-definition"); + + /* If we're in an OpenMP "begin declare variant" block, + the name in the declarator refers to the base function. + We need to save that and modify the declarator to have + the mangled name for the variant function instead. */ + tree dv_base = NULL_TREE; + tree dv_ctx = NULL_TREE; + vec<cp_omp_declare_variant_attr, va_gc> *dv_state + = scope_chain->omp_declare_variant_attribute; + if (!vec_safe_is_empty (dv_state)) + { + cp_omp_declare_variant_attr a = dv_state->last (); + dv_ctx = copy_list (a.selector); + dv_base = omp_start_variant_function (declarator, + dv_ctx); + if (dv_base == NULL_TREE) + { + cp_parser_skip_to_end_of_statement (parser); + goto out; + } + } + decl = cp_parser_save_member_function_body (parser, &decl_specifiers, declarator, @@ -30080,6 +30529,20 @@ cp_parser_member_declaration (cp_parser* parser) /* If the member was not a friend, declare it here. */ if (!friend_p) finish_member_declaration (decl); + + /* If this function was in a "begin declare variant" + block, record the information we need to find the + base function and fix it up later. At this point in + parsing, we may not have seen the base function yet + so we defer looking it up and registering the variant + until the class is complete. */ + if (dv_base && decl != error_mark_node) + { + omp_begin_declare_variant_map_entry e + = { decl, dv_base, dv_ctx }; + vec_safe_push (parser->omp_begin_declare_variant_map, e); + } + /* Peek at the next token. */ token = cp_lexer_peek_token (parser->lexer); /* If the next token is a semicolon, consume it. */ @@ -51566,6 +52029,20 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, goto fail; ctx = omp_check_context_selector (match_loc, ctx, OMP_CTX_DECLARE_VARIANT); + + /* The OpenMP spec says the merging rules for enclosing + "begin declare variant" contexts apply to "declare variant + directives" -- the term it uses to refer to both directive + forms. */ + if (ctx != error_mark_node + && !vec_safe_is_empty (scope_chain->omp_declare_variant_attribute)) + { + cp_omp_declare_variant_attr a + = scope_chain->omp_declare_variant_attribute->last (); + tree outer_ctx = a.selector; + ctx = omp_merge_context_selectors (match_loc, outer_ctx, ctx, + OMP_CTX_DECLARE_VARIANT); + } if (ctx != error_mark_node && variant != error_mark_node) { tree match_loc_node @@ -52227,7 +52704,9 @@ cp_parser_omp_declare_target (cp_parser *parser, cp_token *pragma_tok) /* OpenMP 5.1 # pragma omp begin assumes clauses[optseq] new-line - # pragma omp begin declare target clauses[optseq] new-line */ + # pragma omp begin declare target clauses[optseq] new-line + + # pragma omp begin declare variant match (context-selector) new-line */ #define OMP_BEGIN_DECLARE_TARGET_CLAUSE_MASK \ ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE_TYPE) \ @@ -52273,9 +52752,75 @@ cp_parser_omp_begin (cp_parser *parser, cp_token *pragma_tok) = { in_omp_attribute_pragma, device_type, indirect }; vec_safe_push (scope_chain->omp_declare_target_attribute, a); } + else if (strcmp (p, "variant") == 0) + { + cp_lexer_consume_token (parser->lexer); + const char *clause = ""; + matching_parens parens; + location_t match_loc = cp_lexer_peek_token (parser->lexer)->location; + if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) + cp_lexer_consume_token (parser->lexer); + if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) + { + tree id = cp_lexer_peek_token (parser->lexer)->u.value; + clause = IDENTIFIER_POINTER (id); + } + if (strcmp (clause, "match") != 0) + { + cp_parser_error (parser, "expected %<match%>"); + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + return; + } + + cp_lexer_consume_token (parser->lexer); + + if (!parens.require_open (parser)) + { + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + return; + } + + tree ctx = cp_parser_omp_context_selector_specification (parser, + true); + if (ctx != error_mark_node) + ctx = omp_check_context_selector (match_loc, ctx, + OMP_CTX_BEGIN_DECLARE_VARIANT); + + if (ctx != error_mark_node + && !vec_safe_is_empty (scope_chain->omp_declare_variant_attribute)) + { + cp_omp_declare_variant_attr a + = scope_chain->omp_declare_variant_attribute->last (); + tree outer_ctx = a.selector; + ctx = omp_merge_context_selectors (match_loc, outer_ctx, ctx, + OMP_CTX_BEGIN_DECLARE_VARIANT); + } + + if (ctx == error_mark_node + || !omp_context_selector_matches (ctx, NULL_TREE, false, true)) + { + /* The context is either invalid or cannot possibly match. + In the latter case the spec says all code in the begin/end + sequence will be elided. In the former case we'll get bogus + errors from trying to parse it without a valid context to + use for name-mangling, so elide that too. */ + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + cp_parser_skip_to_pragma_omp_end_declare_variant (parser); + return; + } + else + { + cp_omp_declare_variant_attr a + = { parser->lexer->in_omp_attribute_pragma, ctx }; + vec_safe_push (scope_chain->omp_declare_variant_attribute, a); + } + + parens.require_close (parser); + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + } else { - cp_parser_error (parser, "expected %<target%>"); + cp_parser_error (parser, "expected %<target%> or %<variant%>"); cp_parser_skip_to_pragma_eol (parser, pragma_tok); } } @@ -52288,7 +52833,8 @@ cp_parser_omp_begin (cp_parser *parser, cp_token *pragma_tok) } else { - cp_parser_error (parser, "expected %<declare target%> or %<assumes%>"); + cp_parser_error (parser, "expected %<declare target%>, " + "%<declare variant%>, or %<assumes%>"); cp_parser_skip_to_pragma_eol (parser, pragma_tok); } } @@ -52297,7 +52843,8 @@ cp_parser_omp_begin (cp_parser *parser, cp_token *pragma_tok) # pragma omp end declare target new-line OpenMP 5.1: - # pragma omp end assumes new-line */ + # pragma omp end assumes new-line + # pragma omp end declare variant new-line */ static void cp_parser_omp_end (cp_parser *parser, cp_token *pragma_tok) @@ -52319,41 +52866,70 @@ cp_parser_omp_end (cp_parser *parser, cp_token *pragma_tok) p = IDENTIFIER_POINTER (id); } if (strcmp (p, "target") == 0) - cp_lexer_consume_token (parser->lexer); - else { - cp_parser_error (parser, "expected %<target%>"); - cp_parser_skip_to_pragma_eol (parser, pragma_tok); - return; + cp_lexer_consume_token (parser->lexer); + cp_parser_require_pragma_eol (parser, pragma_tok); + if (!vec_safe_length (scope_chain->omp_declare_target_attribute)) + error_at (pragma_tok->location, + "%<#pragma omp end declare target%> without " + "corresponding %<#pragma omp declare target%> or " + "%<#pragma omp begin declare target%>"); + else + { + cp_omp_declare_target_attr + a = scope_chain->omp_declare_target_attribute->pop (); + if (a.attr_syntax != in_omp_attribute_pragma) + { + if (a.attr_syntax) + error_at (pragma_tok->location, + "%qs in attribute syntax terminated " + "with %qs in pragma syntax", + a.device_type >= 0 ? "begin declare target" + : "declare target", + "end declare target"); + else + error_at (pragma_tok->location, + "%qs in pragma syntax terminated " + "with %qs in attribute syntax", + a.device_type >= 0 ? "begin declare target" + : "declare target", + "end declare target"); + } + } } - cp_parser_require_pragma_eol (parser, pragma_tok); - if (!vec_safe_length (scope_chain->omp_declare_target_attribute)) - error_at (pragma_tok->location, - "%<#pragma omp end declare target%> without corresponding " - "%<#pragma omp declare target%> or " - "%<#pragma omp begin declare target%>"); - else + else if (strcmp (p, "variant") == 0) { - cp_omp_declare_target_attr - a = scope_chain->omp_declare_target_attribute->pop (); - if (a.attr_syntax != in_omp_attribute_pragma) + cp_lexer_consume_token (parser->lexer); + cp_parser_require_pragma_eol (parser, pragma_tok); + if (!vec_safe_length (scope_chain->omp_declare_variant_attribute)) + error_at (pragma_tok->location, + "%<#pragma omp end declare variant%> without " + "corresponding %<#pragma omp begin declare variant%>"); + else { - if (a.attr_syntax) - error_at (pragma_tok->location, - "%qs in attribute syntax terminated " - "with %qs in pragma syntax", - a.device_type >= 0 ? "begin declare target" - : "declare target", - "end declare target"); - else - error_at (pragma_tok->location, - "%qs in pragma syntax terminated " - "with %qs in attribute syntax", - a.device_type >= 0 ? "begin declare target" - : "declare target", - "end declare target"); + cp_omp_declare_variant_attr + a = scope_chain->omp_declare_variant_attribute->pop (); + if (a.attr_syntax != in_omp_attribute_pragma) + { + if (a.attr_syntax) + error_at (pragma_tok->location, + "%<begin declare variant%> in attribute syntax " + "terminated with %<end declare variant%> in " + "pragma syntax"); + else + error_at (pragma_tok->location, + "%<begin declare variant%> in pragma syntax " + "terminated with %<end declare variant%> in " + "attribute syntax"); + } } } + else + { + cp_parser_error (parser, "expected %<target%>"); + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + return; + } } else if (strcmp (p, "assumes") == 0) { diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index 3a17be993f1..3e7251c38a9 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -233,6 +233,13 @@ struct cp_oacc_routine_data : cp_omp_declare_simd_data { tree clauses; }; +/* Helper data structure for parsing #pragma omp begin declare variant. */ +struct GTY(()) omp_begin_declare_variant_map_entry { + tree variant; /* The variant decl. */ + tree id; /* Name of base function. */ + tree ctx; /* The context selector associated with the variant. */ +}; + /* The cp_parser structure represents the C++ parser. */ struct GTY(()) cp_parser { @@ -458,6 +465,11 @@ struct GTY(()) cp_parser { outside that file. */ struct omp_metadirective_parse_data * GTY((skip)) omp_metadirective_state; + + /* Recorded information about functions in OpenMP "begin declare variant" + constructs that still need to be registered with their base functions. */ + vec<omp_begin_declare_variant_map_entry, va_gc> * + omp_begin_declare_variant_map; }; /* In parser.cc */ diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 3e19a56f51e..0d8981ecabf 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -4051,6 +4051,13 @@ finish_translation_unit (void) "#pragma omp end declare target"); vec_safe_truncate (scope_chain->omp_declare_target_attribute, 0); } + if (vec_safe_length (scope_chain->omp_declare_variant_attribute)) + { + if (!errorcount) + error ("%<omp begin declare variant%> without corresponding " + "%<omp end declare variant%>"); + vec_safe_truncate (scope_chain->omp_declare_variant_attribute, 0); + } if (vec_safe_length (scope_chain->omp_begin_assumes)) { if (!errorcount) |
