gcc/c/ChangeLog * c-decl.cc (current_omp_declare_variant_attribute): Define. * c-lang.h (struct c_omp_declare_variant_attr): Declare. (current_omp_declare_variant_attribute): Declare. * c-parser.cc (c_parser_skip_to_pragma_omp_end_declare_variant): New. (c_parser_translation_unit): Check for "omp begin declare variant" with no matching "end". (c_parser_declaration_or_fndef): Handle functions in "omp begin declare variant" block. (c_finish_omp_declare_variant): Merge context selectors with surrounding "omp begin declare variant". (JOIN_STR): Define. (omp_start_variant_function): New. (omp_finish_variant_function): New. (c_parser_omp_begin): Handle "omp begin declare variant". (c_parser_omp_end): Likewise.
Co-Authored-By: Julian Brown <jul...@codesourcery.com> --- gcc/c/c-decl.cc | 3 + gcc/c/c-lang.h | 8 ++ gcc/c/c-parser.cc | 347 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 320 insertions(+), 38 deletions(-) diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 0dcbae9b26f..8b778c16e2d 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -163,6 +163,9 @@ vec<c_omp_declare_target_attr, va_gc> *current_omp_declare_target_attribute; we are in. */ vec<c_omp_begin_assumes_data, va_gc> *current_omp_begin_assumes; +/* Vector of "omp begin/end declare variant" blocks we are in. */ +vec<c_omp_declare_variant_attr, va_gc> *current_omp_declare_variant_attribute; + /* Vector of loop names with C_DECL_LOOP_NAME or C_DECL_SWITCH_NAME marked LABEL_DECL as the last and canonical for each loop or switch. */ static vec<tree> loop_names; diff --git a/gcc/c/c-lang.h b/gcc/c/c-lang.h index 4b93d184dbc..cd68fc00e45 100644 --- a/gcc/c/c-lang.h +++ b/gcc/c/c-lang.h @@ -72,6 +72,11 @@ struct GTY(()) c_omp_begin_assumes_data { bool attr_syntax; }; +struct GTY(()) c_omp_declare_variant_attr { + bool attr_syntax; + tree selector; +}; + /* If non-empty, implicit "omp declare target" attribute is added into the attribute lists. */ extern GTY(()) vec<c_omp_declare_target_attr, va_gc> @@ -80,5 +85,8 @@ extern GTY(()) vec<c_omp_declare_target_attr, va_gc> #pragma omp end assumes (and how many times when nested). */ extern GTY(()) vec<c_omp_begin_assumes_data, va_gc> *current_omp_begin_assumes; +/* And similarly for #pragma omp begin/end declare variant. */ +extern GTY(()) vec<c_omp_declare_variant_attr, va_gc> + *current_omp_declare_variant_attribute; #endif /* ! GCC_C_LANG_H */ diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 62c6bc031d6..97f7da4a2ed 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -1456,6 +1456,55 @@ c_parser_skip_to_pragma_eol (c_parser *parser, bool error_if_not_eol = true) parser->error = false; } +/* Skip tokens up to and including "#pragma omp end declare variant". + Properly handle nested "#pragma omp begin declare variant" pragmas. */ +static void +c_parser_skip_to_pragma_omp_end_declare_variant (c_parser *parser) +{ + for (int depth = 0; depth >= 0; ) + { + c_token *token = c_parser_peek_token (parser); + + switch (token->type) + { + case CPP_PRAGMA_EOL: + if (!parser->in_pragma) + break; + /* FALLTHRU */ + case CPP_EOF: + /* If we've run out of tokens, stop. */ + return; + + case CPP_PRAGMA: + if ((token->pragma_kind == PRAGMA_OMP_BEGIN + || token->pragma_kind == PRAGMA_OMP_END) + && c_parser_peek_nth_token (parser, 2)->type == CPP_NAME + && c_parser_peek_nth_token (parser, 3)->type == CPP_NAME) + { + tree id1 = c_parser_peek_nth_token (parser, 2)->value; + tree id2 = c_parser_peek_nth_token (parser, 3)->value; + if (strcmp (IDENTIFIER_POINTER (id1), "declare") == 0 + && strcmp (IDENTIFIER_POINTER (id2), "variant") == 0) + { + if (token->pragma_kind == PRAGMA_OMP_BEGIN) + depth++; + else + depth--; + } + } + c_parser_consume_pragma (parser); + c_parser_skip_to_pragma_eol (parser, false); + continue; + + default: + break; + } + + /* Consume the token. */ + c_parser_consume_token (parser); + } +} + /* Skip tokens until we have consumed an entire block, or until we have consumed a non-nested ';'. */ @@ -1978,6 +2027,13 @@ c_parser_translation_unit (c_parser *parser) "#pragma omp end declare target"); vec_safe_truncate (current_omp_declare_target_attribute, 0); } + if (vec_safe_length (current_omp_declare_variant_attribute)) + { + if (!errorcount) + error ("%<omp begin declare variant%> without corresponding " + "%<omp end declare variant%>"); + vec_safe_truncate (current_omp_declare_variant_attribute, 0); + } if (vec_safe_length (current_omp_begin_assumes)) { if (!errorcount) @@ -2111,6 +2167,8 @@ static void c_parser_handle_directive_omp_attributes (tree &, vec<c_token> *&, vec<c_token> *); static void c_finish_omp_declare_simd (c_parser *, tree, tree, vec<c_token> *); static void c_finish_oacc_routine (struct oacc_routine_data *, tree, bool); +static tree omp_start_variant_function (c_declarator *, tree); +static void omp_finish_variant_function (tree, tree, tree); /* Build and add a DEBUG_BEGIN_STMT statement with location LOC. */ @@ -3037,6 +3095,21 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, pedwarn (here, OPT_Wpedantic, "ISO C forbids nested functions"); c_push_function_context (); } + + /* 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; + if (!vec_safe_is_empty (current_omp_declare_variant_attribute)) + { + c_omp_declare_variant_attr a + = current_omp_declare_variant_attribute->last (); + dv_ctx = copy_list (a.selector); + dv_base = omp_start_variant_function (declarator, dv_ctx); + } + if (!start_function (specs, declarator, all_prefix_attrs)) { /* At this point we've consumed: @@ -3114,6 +3187,11 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, DECL_STRUCT_FUNCTION (current_function_decl)->function_start_locus = startloc; location_t endloc = startloc; + /* 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. */ + if (dv_base && current_function_decl != error_mark_node) + omp_finish_variant_function (current_function_decl, dv_base, dv_ctx); /* If the definition was marked with __RTL, use the RTL parser now, consuming the function body. */ @@ -26984,6 +27062,20 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) 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 (current_omp_declare_variant_attribute)) + { + c_omp_declare_variant_attr a + = current_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) { if (TREE_CODE (variant) != FUNCTION_DECL) @@ -27384,6 +27476,87 @@ c_finish_omp_declare_simd (c_parser *parser, tree fndecl, tree parms, clauses[0].type = CPP_PRAGMA; } +/* This is consistent with the C++ front end. */ + +#if !defined (NO_DOT_IN_LABEL) +#define JOIN_STR "." +#elif !defined (NO_DOLLAR_IN_LABEL) +#define JOIN_STR "$" +#else +#define JOIN_STR "_" +#endif + +/* 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 (c_declarator *declarator, tree ctx) +{ + c_declarator *id = declarator; + while (id->kind != cdk_id) + { + id = id->declarator; + gcc_assert (id); + } + tree name = id->u.id.id; + id->u.id.id = omp_mangle_variant_name (name, ctx, JOIN_STR); + 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, 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. */ +static void +omp_finish_variant_function (tree decl, tree base_name, tree ctx) +{ + /* First look up BASE_NAME and ensure it matches DECL. */ + tree base_decl = lookup_name (base_name); + if (base_decl == error_mark_node) + base_decl = NULL_TREE; + if (!base_decl) + { + error_at (DECL_SOURCE_LOCATION (decl), + "no previous declaration of base function"); + return; + } + + if (!comptypes (TREE_TYPE (decl), TREE_TYPE (base_decl))) + { + error_at (DECL_SOURCE_LOCATION (decl), + "variant function definition does not match previous " + "declaration of %qE", base_decl); + return; + } + + /* Now set the attributes on the base and variant decls for the middle + end. */ + omp_check_for_duplicate_variant (DECL_SOURCE_LOCATION (decl), + base_decl, 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 (base_decl); + tree match_loc_node + = maybe_wrap_with_location (integer_zero_node, + DECL_SOURCE_LOCATION (base_decl)); + tree loc_node = tree_cons (match_loc_node, integer_zero_node, + build_tree_list (match_loc_node, + integer_zero_node)); + attrs = tree_cons (get_identifier ("omp declare variant base"), + tree_cons (decl, ctx, loc_node), attrs); + DECL_ATTRIBUTES (base_decl) = attrs; + + /* Variant functions are essentially anonymous and cannot be referenced + outside the compilation unit. */ + TREE_PUBLIC (decl) = 0; + DECL_COMDAT (decl) = 0; +} + + /* D should be C_TOKEN_VEC from omp::decl attribute. If it contains a threadprivate, groupprivate, allocate or declare target directive, return true and parse it for DECL. */ @@ -27616,7 +27789,9 @@ c_parser_omp_declare_target (c_parser *parser) /* 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) \ @@ -27656,10 +27831,74 @@ c_parser_omp_begin (c_parser *parser) indirect }; vec_safe_push (current_omp_declare_target_attribute, attr); } + else if (strcmp (p, "variant") == 0) + { + c_parser_consume_token (parser); + const char *clause = ""; + matching_parens parens; + location_t match_loc = c_parser_peek_token (parser)->location; + if (c_parser_next_token_is (parser, CPP_NAME)) + { + tree id = c_parser_peek_token (parser)->value; + clause = IDENTIFIER_POINTER (id); + } + if (strcmp (clause, "match") != 0) + { + c_parser_error (parser, "expected %<match%>"); + c_parser_skip_to_pragma_eol (parser); + return; + } + + c_parser_consume_token (parser); + + if (!parens.require_open (parser)) + { + c_parser_skip_to_pragma_eol (parser, false); + return; + } + + tree ctx = + c_parser_omp_context_selector_specification (parser, NULL_TREE); + 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 (current_omp_declare_variant_attribute)) + { + c_omp_declare_variant_attr a + = current_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. */ + c_parser_skip_to_pragma_eol (parser, false); + c_parser_skip_to_pragma_omp_end_declare_variant (parser); + return; + } + else + { + bool attr_syntax = parser->in_omp_attribute_pragma != NULL; + c_omp_declare_variant_attr a = { attr_syntax, ctx }; + vec_safe_push (current_omp_declare_variant_attribute, a); + } + + parens.require_close (parser); + c_parser_skip_to_pragma_eol (parser); + } else { - c_parser_error (parser, "expected %<target%>"); - c_parser_skip_to_pragma_eol (parser); + c_parser_error (parser, "expected %<target%> or %<variant%>"); + c_parser_skip_to_pragma_eol (parser, false); } } else if (strcmp (p, "assumes") == 0) @@ -27672,7 +27911,8 @@ c_parser_omp_begin (c_parser *parser) } else { - c_parser_error (parser, "expected %<declare target%> or %<assumes%>"); + c_parser_error (parser, "expected %<declare target%>, " + "%<declare variant%>, or %<assumes%>"); c_parser_skip_to_pragma_eol (parser); } } @@ -27681,7 +27921,8 @@ c_parser_omp_begin (c_parser *parser) #pragma omp end declare target OpenMP 5.1 - #pragma omp end assumes */ + #pragma omp end assumes + #pragma omp end declare variant new-line */ static void c_parser_omp_end (c_parser *parser) @@ -27694,44 +27935,74 @@ c_parser_omp_end (c_parser *parser) if (strcmp (p, "declare") == 0) { c_parser_consume_token (parser); - if (c_parser_next_token_is (parser, CPP_NAME) - && strcmp (IDENTIFIER_POINTER (c_parser_peek_token (parser)->value), - "target") == 0) - c_parser_consume_token (parser); + p = ""; + if (c_parser_next_token_is (parser, CPP_NAME)) + p = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); + if (strcmp (p, "target") == 0) + { + c_parser_consume_token (parser); + bool attr_syntax = parser->in_omp_attribute_pragma != NULL; + c_parser_skip_to_pragma_eol (parser); + if (!vec_safe_length (current_omp_declare_target_attribute)) + error_at (loc, "%<#pragma omp end declare target%> without " + "corresponding %<#pragma omp declare target%> or " + "%<#pragma omp begin declare target%>"); + else + { + c_omp_declare_target_attr + a = current_omp_declare_target_attribute->pop (); + if (a.attr_syntax != attr_syntax) + { + if (a.attr_syntax) + error_at (loc, + "%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 (loc, + "%qs in pragma syntax terminated " + "with %qs in attribute syntax", + a.device_type >= 0 ? "begin declare target" + : "declare target", + "end declare target"); + } + } + } + else if (strcmp (p, "variant") == 0) + { + c_parser_consume_token (parser); + bool attr_syntax = parser->in_omp_attribute_pragma != NULL; + c_parser_skip_to_pragma_eol (parser); + if (!vec_safe_length (current_omp_declare_variant_attribute)) + error_at (loc, "%<#pragma omp end declare variant%> without " + "corresponding %<#pragma omp begin declare variant%>"); + else + { + c_omp_declare_variant_attr + a = current_omp_declare_variant_attribute->pop (); + if (a.attr_syntax != attr_syntax) + { + if (a.attr_syntax) + error_at (loc, + "%<begin declare variant%> in attribute syntax " + "terminated with " + "%<end declare variant%> in pragma syntax"); + else + error_at (loc, + "%<begin declare variant%> in pragma syntax " + "terminated with " + "%<end declare variant%> in attribute syntax"); + } + } + } else { - c_parser_error (parser, "expected %<target%>"); + c_parser_error (parser, "expected %<target%> or %<variant%>"); c_parser_skip_to_pragma_eol (parser); return; } - bool attr_syntax = parser->in_omp_attribute_pragma != NULL; - c_parser_skip_to_pragma_eol (parser); - if (!vec_safe_length (current_omp_declare_target_attribute)) - error_at (loc, "%<#pragma omp end declare target%> without " - "corresponding %<#pragma omp declare target%> or " - "%<#pragma omp begin declare target%>"); - else - { - c_omp_declare_target_attr - a = current_omp_declare_target_attribute->pop (); - if (a.attr_syntax != attr_syntax) - { - if (a.attr_syntax) - error_at (loc, - "%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 (loc, - "%qs in pragma syntax terminated " - "with %qs in attribute syntax", - a.device_type >= 0 ? "begin declare target" - : "declare target", - "end declare target"); - } - } } else if (strcmp (p, "assumes") == 0) { -- 2.34.1