https://gcc.gnu.org/g:a9164dde46c41794c97d40a1a0271db16512f2c0
commit a9164dde46c41794c97d40a1a0271db16512f2c0 Author: Paul-Antoine Arras <par...@baylibre.com> Date: Wed Nov 20 15:28:58 2024 +0100 OpenMP: C++ front-end support for dispatch + adjust_args This patch adds C++ support for the `dispatch` construct and the `adjust_args` clause. It relies on the c-family bits comprised in the corresponding C front end patch for pragmas and attributes. Additional C/C++ common testcases are provided in a subsequent patch in the series. gcc/cp/ChangeLog: * decl.cc (omp_declare_variant_finalize_one): Set adjust_args need_device_ptr attribute. * parser.cc (cp_parser_direct_declarator): Update call to cp_parser_late_return_type_opt. (cp_parser_late_return_type_opt): Add 'tree parms' parameter. Update call to cp_parser_late_parsing_omp_declare_simd. (cp_parser_omp_clause_name): Handle nocontext and novariants clauses. (cp_parser_omp_clause_novariants): New function. (cp_parser_omp_clause_nocontext): Likewise. (cp_parser_omp_all_clauses): Handle PRAGMA_OMP_CLAUSE_NOVARIANTS and PRAGMA_OMP_CLAUSE_NOCONTEXT. (cp_parser_omp_dispatch_body): New function, inspired from cp_parser_assignment_expression and cp_parser_postfix_expression. (OMP_DISPATCH_CLAUSE_MASK): Define. (cp_parser_omp_dispatch): New function. (cp_finish_omp_declare_variant): Add parameter. Handle adjust_args clause. (cp_parser_late_parsing_omp_declare_simd): Add parameter. Update calls to cp_finish_omp_declare_variant and cp_finish_omp_declare_variant. (cp_parser_omp_construct): Handle PRAGMA_OMP_DISPATCH. (cp_parser_pragma): Likewise. * semantics.cc (finish_omp_clauses): Handle OMP_CLAUSE_NOCONTEXT and OMP_CLAUSE_NOVARIANTS. * pt.cc (tsubst_omp_clauses): Handle OMP_CLAUSE_NOCONTEXT and OMP_CLAUSE_NOVARIANTS. (tsubst_stmt): Handle OMP_DISPATCH. (tsubst_expr): Handle IFN_GOMP_DISPATCH. gcc/testsuite/ChangeLog: * g++.dg/gomp/adjust-args-1.C: New test. * g++.dg/gomp/adjust-args-2.C: New test. * g++.dg/gomp/adjust-args-3.C: New test. * g++.dg/gomp/dispatch-1.C: New test. * g++.dg/gomp/dispatch-2.C: New test. * g++.dg/gomp/dispatch-3.C: New test. * g++.dg/gomp/dispatch-4.C: New test. * g++.dg/gomp/dispatch-5.C: New test. * g++.dg/gomp/dispatch-6.C: New test. * g++.dg/gomp/dispatch-7.C: New test. (cherry picked from commit ed49709acda4938f71ef2dfac68450be5429db3d) Diff: --- gcc/cp/ChangeLog.omp | 33 +++ gcc/cp/decl.cc | 7 + gcc/cp/parser.cc | 466 +++++++++++++++++++++++++++--- gcc/cp/pt.cc | 28 ++ gcc/cp/semantics.cc | 20 ++ gcc/testsuite/ChangeLog.omp | 16 + gcc/testsuite/g++.dg/gomp/adjust-args-1.C | 39 +++ gcc/testsuite/g++.dg/gomp/adjust-args-2.C | 51 ++++ gcc/testsuite/g++.dg/gomp/adjust-args-3.C | 6 + gcc/testsuite/g++.dg/gomp/dispatch-1.C | 53 ++++ gcc/testsuite/g++.dg/gomp/dispatch-2.C | 62 ++++ gcc/testsuite/g++.dg/gomp/dispatch-3.C | 17 ++ gcc/testsuite/g++.dg/gomp/dispatch-4.C | 22 ++ gcc/testsuite/g++.dg/gomp/dispatch-5.C | 17 ++ gcc/testsuite/g++.dg/gomp/dispatch-6.C | 29 ++ gcc/testsuite/g++.dg/gomp/dispatch-7.C | 43 +++ 16 files changed, 864 insertions(+), 45 deletions(-) diff --git a/gcc/cp/ChangeLog.omp b/gcc/cp/ChangeLog.omp index b68cd46caf42..01483816e018 100644 --- a/gcc/cp/ChangeLog.omp +++ b/gcc/cp/ChangeLog.omp @@ -1,3 +1,36 @@ +2025-01-27 Paul-Antoine Arras <par...@baylibre.com> + + Backported from master: + 2024-11-20 Paul-Antoine Arras <par...@baylibre.com> + + * decl.cc (omp_declare_variant_finalize_one): Set adjust_args + need_device_ptr attribute. + * parser.cc (cp_parser_direct_declarator): Update call to + cp_parser_late_return_type_opt. + (cp_parser_late_return_type_opt): Add 'tree parms' parameter. Update + call to cp_parser_late_parsing_omp_declare_simd. + (cp_parser_omp_clause_name): Handle nocontext and novariants clauses. + (cp_parser_omp_clause_novariants): New function. + (cp_parser_omp_clause_nocontext): Likewise. + (cp_parser_omp_all_clauses): Handle PRAGMA_OMP_CLAUSE_NOVARIANTS and + PRAGMA_OMP_CLAUSE_NOCONTEXT. + (cp_parser_omp_dispatch_body): New function, inspired from + cp_parser_assignment_expression and cp_parser_postfix_expression. + (OMP_DISPATCH_CLAUSE_MASK): Define. + (cp_parser_omp_dispatch): New function. + (cp_finish_omp_declare_variant): Add parameter. Handle adjust_args + clause. + (cp_parser_late_parsing_omp_declare_simd): Add parameter. Update calls + to cp_finish_omp_declare_variant and cp_finish_omp_declare_variant. + (cp_parser_omp_construct): Handle PRAGMA_OMP_DISPATCH. + (cp_parser_pragma): Likewise. + * semantics.cc (finish_omp_clauses): Handle OMP_CLAUSE_NOCONTEXT and + OMP_CLAUSE_NOVARIANTS. + * pt.cc (tsubst_omp_clauses): Handle OMP_CLAUSE_NOCONTEXT and + OMP_CLAUSE_NOVARIANTS. + (tsubst_stmt): Handle OMP_DISPATCH. + (tsubst_expr): Handle IFN_GOMP_DISPATCH. + 2025-01-23 Tobias Burnus <tbur...@baylibre.com> Backported from master: diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 097e958b1ec1..b1f8759cee89 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -8346,6 +8346,13 @@ omp_declare_variant_finalize_one (tree decl, tree attr) if (!omp_context_selector_matches (ctx, NULL_TREE, false)) return true; TREE_PURPOSE (TREE_VALUE (attr)) = variant; + + // Prepend adjust_args list to variant attributes + tree adjust_args_list = TREE_CHAIN (TREE_CHAIN (chain)); + if (adjust_args_list != NULL_TREE) + DECL_ATTRIBUTES (variant) = tree_cons ( + get_identifier ("omp declare variant variant adjust_args"), + TREE_VALUE (adjust_args_list), DECL_ATTRIBUTES (variant)); } } else if (!processing_template_decl) diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 5a05fb18ef87..b1c17a451d79 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#include "omp-selectors.h" #define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" @@ -2585,7 +2586,7 @@ static cp_ref_qualifier cp_parser_ref_qualifier_opt static tree cp_parser_tx_qualifier_opt (cp_parser *); static tree cp_parser_late_return_type_opt - (cp_parser *, cp_declarator *, tree &); + (cp_parser *, cp_declarator *, tree &, tree); static tree cp_parser_declarator_id (cp_parser *, bool); static tree cp_parser_type_id @@ -2620,7 +2621,7 @@ static void cp_parser_ctor_initializer_opt_and_function_body (cp_parser *, bool); static tree cp_parser_late_parsing_omp_declare_simd - (cp_parser *, tree); + (cp_parser *, tree, tree); static tree cp_parser_late_parsing_oacc_routine (cp_parser *, tree); @@ -24136,7 +24137,7 @@ cp_parser_direct_declarator (cp_parser* parser, tree requires_clause = NULL_TREE; late_return = cp_parser_late_return_type_opt (parser, declarator, - requires_clause); + requires_clause, params); cp_finalize_omp_declare_simd (parser, &odsd); @@ -25000,8 +25001,8 @@ parsing_function_declarator () function. */ static tree -cp_parser_late_return_type_opt (cp_parser* parser, cp_declarator *declarator, - tree& requires_clause) +cp_parser_late_return_type_opt (cp_parser *parser, cp_declarator *declarator, + tree &requires_clause, tree parms) { cp_token *token; tree type = NULL_TREE; @@ -25037,8 +25038,8 @@ cp_parser_late_return_type_opt (cp_parser* parser, cp_declarator *declarator, if (declare_simd_p) declarator->attributes - = cp_parser_late_parsing_omp_declare_simd (parser, - declarator->attributes); + = cp_parser_late_parsing_omp_declare_simd (parser, declarator->attributes, + parms); if (oacc_routine_p) declarator->attributes = cp_parser_late_parsing_oacc_routine (parser, @@ -38141,6 +38142,8 @@ cp_parser_omp_clause_name (cp_parser *parser) case 'n': if (!strcmp ("no_create", p)) result = PRAGMA_OACC_CLAUSE_NO_CREATE; + else if (!strcmp ("nocontext", p)) + result = PRAGMA_OMP_CLAUSE_NOCONTEXT; else if (!strcmp ("nogroup", p)) result = PRAGMA_OMP_CLAUSE_NOGROUP; else if (!strcmp ("nohost", p)) @@ -38149,6 +38152,8 @@ cp_parser_omp_clause_name (cp_parser *parser) result = PRAGMA_OMP_CLAUSE_NONTEMPORAL; else if (!strcmp ("notinbranch", p)) result = PRAGMA_OMP_CLAUSE_NOTINBRANCH; + else if (!strcmp ("novariants", p)) + result = PRAGMA_OMP_CLAUSE_NOVARIANTS; else if (!strcmp ("nowait", p)) result = PRAGMA_OMP_CLAUSE_NOWAIT; else if (!strcmp ("num_gangs", p)) @@ -40735,6 +40740,56 @@ cp_parser_omp_clause_partial (cp_parser *parser, tree list, location_t loc) return c; } +/* OpenMP 5.1 + novariants ( scalar-expression ) */ + +static tree +cp_parser_omp_clause_novariants (cp_parser *parser, tree list, location_t loc) +{ + matching_parens parens; + if (!parens.require_open (parser)) + return list; + + tree t = cp_parser_assignment_expression (parser); + if (t == error_mark_node || !parens.require_close (parser)) + cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, + /*or_comma=*/false, + /*consume_paren=*/true); + + check_no_duplicate_clause (list, OMP_CLAUSE_NOVARIANTS, "novariants", loc); + + tree c = build_omp_clause (loc, OMP_CLAUSE_NOVARIANTS); + OMP_CLAUSE_NOVARIANTS_EXPR (c) = t; + OMP_CLAUSE_CHAIN (c) = list; + + return c; +} + +/* OpenMP 5.1 + nocontext ( scalar-expression ) */ + +static tree +cp_parser_omp_clause_nocontext (cp_parser *parser, tree list, location_t loc) +{ + matching_parens parens; + if (!parens.require_open (parser)) + return list; + + tree t = cp_parser_assignment_expression (parser); + if (t == error_mark_node || !parens.require_close (parser)) + cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, + /*or_comma=*/false, + /*consume_paren=*/true); + + check_no_duplicate_clause (list, OMP_CLAUSE_NOCONTEXT, "nocontext", loc); + + tree c = build_omp_clause (loc, OMP_CLAUSE_NOCONTEXT); + OMP_CLAUSE_NOCONTEXT_EXPR (c) = t; + OMP_CLAUSE_CHAIN (c) = list; + + return c; +} + /* OpenMP 4.0: aligned ( variable-list ) aligned ( variable-list : constant-expression ) */ @@ -43298,6 +43353,16 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask, clauses = cp_parser_omp_clause_full (clauses, token->location); c_name = "full"; break; + case PRAGMA_OMP_CLAUSE_NOVARIANTS: + clauses = cp_parser_omp_clause_novariants (parser, clauses, + token->location); + c_name = "novariants"; + break; + case PRAGMA_OMP_CLAUSE_NOCONTEXT: + clauses + = cp_parser_omp_clause_nocontext (parser, clauses, token->location); + c_name = "nocontext"; + break; default: cp_parser_error (parser, "expected an OpenMP clause"); goto saw_error; @@ -49605,12 +49670,160 @@ cp_parser_omp_assumes (cp_parser *parser, cp_token *pragma_tok) return false; } +/* Parse a function dispatch structured block: + + lvalue-expression = target-call ( [expression-list] ); + or + target-call ( [expression-list] ); + + Inspired from cp_parser_assignment_expression and + cp_parser_postfix_expression. +*/ + +static tree +cp_parser_omp_dispatch_body (cp_parser *parser) +{ + /* Parse the binary expressions (lvalue-expression or target-call). */ + cp_expr expr = cp_parser_binary_expression (parser, false, false, false, + PREC_NOT_OPERATOR, NULL); + if (TREE_CODE (STRIP_REFERENCE_REF (expr.get_value ())) == CALL_EXPR + || TREE_CODE (expr) == ERROR_MARK) + return expr; + + /* We have the lvalue, now deal with the assignment. */ + + if (!cp_parser_require (parser, CPP_EQ, RT_EQ)) + return error_mark_node; + + /* Peek at the next token. */ + cp_token *token = cp_lexer_peek_token (parser->lexer); + location_t loc = token->location; + + /* Parse function call. */ + cp_expr rhs = cp_parser_postfix_expression (parser, false, false, false, + false, nullptr); + if (rhs == error_mark_node) + return rhs; + + if (TREE_CODE (STRIP_REFERENCE_REF (rhs.get_value ())) != CALL_EXPR) + { + error_at (EXPR_LOC_OR_LOC (rhs, rhs.get_location ()), + "expected target-function call"); + return error_mark_node; + } + + /* Build the assignment expression. Its default + location: + LHS = RHS + ~~~~^~~~~ + is the location of the '=' token as the + caret, ranging from the start of the lhs to the + end of the rhs. */ + loc = make_location (loc, expr.get_start (), rhs.get_finish ()); + expr = build_x_modify_expr (loc, expr, NOP_EXPR, rhs, NULL_TREE, + complain_flags (false)); + /* TODO: build_x_modify_expr doesn't honor the location, + so we must set it here. */ + expr.set_location (loc); + + return expr; +} + +/* OpenMP 5.1: + # pragma omp dispatch dispatch-clause[optseq] new-line + expression-stmt + + LOC is the location of the #pragma. +*/ + +#define OMP_DISPATCH_CLAUSE_MASK \ + ((OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE) \ + | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEPEND) \ + | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOVARIANTS) \ + | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOCONTEXT) \ + | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR) \ + | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOWAIT)) + +static tree +cp_parser_omp_dispatch (cp_parser *parser, cp_token *pragma_tok) +{ + location_t loc = cp_lexer_peek_token (parser->lexer)->location; + tree stmt = make_node (OMP_DISPATCH); + SET_EXPR_LOCATION (stmt, loc); + TREE_TYPE (stmt) = void_type_node; + + OMP_DISPATCH_CLAUSES (stmt) + = cp_parser_omp_all_clauses (parser, OMP_DISPATCH_CLAUSE_MASK, + "#pragma omp dispatch", pragma_tok); + + // Extract depend clauses and create taskwait + tree depend_clauses = NULL_TREE; + tree *depend_clauses_ptr = &depend_clauses; + for (tree c = OMP_DISPATCH_CLAUSES (stmt); c; c = OMP_CLAUSE_CHAIN (c)) + { + if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND) + { + *depend_clauses_ptr = c; + depend_clauses_ptr = &OMP_CLAUSE_CHAIN (c); + } + } + if (depend_clauses != NULL_TREE) + { + tree stmt = make_node (OMP_TASK); + TREE_TYPE (stmt) = void_node; + OMP_TASK_CLAUSES (stmt) = depend_clauses; + OMP_TASK_BODY (stmt) = NULL_TREE; + SET_EXPR_LOCATION (stmt, loc); + add_stmt (stmt); + } + + // Parse expression statement + loc = cp_lexer_peek_token (parser->lexer)->location; + tree dispatch_body = cp_parser_omp_dispatch_body (parser); + if (dispatch_body == error_mark_node) + { + inform (loc, + "%<#pragma omp dispatch%> must be followed by a direct function " + "call with optional assignment"); + cp_parser_skip_to_end_of_block_or_statement (parser); + return NULL_TREE; + } + + // Walk the tree to find the dispatch function call and wrap it into an IFN + tree *dispatch_call; + switch (TREE_CODE (STRIP_REFERENCE_REF (dispatch_body))) + { + case MODIFY_EXPR: + dispatch_call = &TREE_OPERAND (dispatch_body, 1); + break; + case MODOP_EXPR: + dispatch_call = &TREE_OPERAND (dispatch_body, 2); + break; + case CALL_EXPR: + dispatch_call = &dispatch_body; + break; + default: + gcc_unreachable (); + } + if (TREE_CODE (*dispatch_call) == FLOAT_EXPR + || TREE_CODE (*dispatch_call) == CONVERT_EXPR) + dispatch_call = &TREE_OPERAND (*dispatch_call, 0); + *dispatch_call = build_call_expr_internal_loc (loc, IFN_GOMP_DISPATCH, + TREE_TYPE (*dispatch_call), 1, + *dispatch_call); + + cp_parser_consume_semicolon_at_end_of_statement (parser); + OMP_DISPATCH_BODY (stmt) = dispatch_body; + + return add_stmt (stmt); +} + /* Finalize #pragma omp declare variant after a fndecl has been parsed, and put that into "omp declare variant base" attribute. */ static tree cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, - tree attrs) + tree attrs, tree parms) { matching_parens parens; if (!parens.require_open (parser)) @@ -49668,44 +49881,196 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, location_t finish_loc = get_finish (varid.get_location ()); location_t varid_loc = make_location (caret_loc, start_loc, finish_loc); - if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA) - && cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME)) - cp_lexer_consume_token (parser->lexer); + vec<tree> adjust_args_list = vNULL; + bool has_match = false, has_adjust_args = false; + location_t adjust_args_loc = UNKNOWN_LOCATION; + tree need_device_ptr_list = make_node (TREE_LIST); - const char *clause = ""; - location_t match_loc = cp_lexer_peek_token (parser->lexer)->location; - if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) - clause = IDENTIFIER_POINTER (cp_lexer_peek_token (parser->lexer)->u.value); - if (strcmp (clause, "match")) + do { - cp_parser_error (parser, "expected %<match%>"); - goto fail; - } + if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA) + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME)) + cp_lexer_consume_token (parser->lexer); - cp_lexer_consume_token (parser->lexer); + const char *clause = ""; + location_t match_loc = cp_lexer_peek_token (parser->lexer)->location; + if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) + clause + = IDENTIFIER_POINTER (cp_lexer_peek_token (parser->lexer)->u.value); - if (!parens.require_open (parser)) - goto fail; + enum clause + { + match, + adjust_args + } ccode; - tree ctx = cp_parser_omp_context_selector_specification (parser, true); - if (ctx == error_mark_node) - goto fail; - ctx = omp_check_context_selector (match_loc, ctx, false); - if (ctx != error_mark_node && variant != error_mark_node) - { - tree match_loc_node = maybe_wrap_with_location (integer_zero_node, - match_loc); - tree loc_node = maybe_wrap_with_location (integer_zero_node, varid_loc); - loc_node = tree_cons (match_loc_node, - build_int_cst (integer_type_node, idk), - build_tree_list (loc_node, integer_zero_node)); - attrs = tree_cons (get_identifier ("omp declare variant base"), - tree_cons (variant, ctx, loc_node), attrs); - if (processing_template_decl) - ATTR_IS_DEPENDENT (attrs) = 1; + if (strcmp (clause, "match") == 0) + ccode = match; + else if (strcmp (clause, "adjust_args") == 0) + { + ccode = adjust_args; + adjust_args_loc = match_loc; + } + else + { + cp_parser_error (parser, "expected %<match%> clause"); + goto fail; + } + + cp_lexer_consume_token (parser->lexer); + + if (!parens.require_open (parser)) + goto fail; + + if (ccode == match) + { + if (has_match) + error_at (match_loc, "too many %<match%> clauses"); + has_match = true; + tree ctx + = cp_parser_omp_context_selector_specification (parser, true); + if (ctx == error_mark_node) + goto fail; + ctx = omp_check_context_selector (match_loc, ctx, false); + if (ctx != error_mark_node && variant != error_mark_node) + { + tree match_loc_node + = maybe_wrap_with_location (integer_zero_node, match_loc); + tree loc_node + = maybe_wrap_with_location (integer_zero_node, varid_loc); + loc_node + = tree_cons (match_loc_node, + build_int_cst (integer_type_node, idk), + build_tree_list (loc_node, integer_zero_node)); + attrs = tree_cons (get_identifier ("omp declare variant base"), + tree_cons (variant, ctx, loc_node), attrs); + if (processing_template_decl) + ATTR_IS_DEPENDENT (attrs) = 1; + } + if (!parens.require_close (parser)) + goto fail; + } + else if (ccode == adjust_args) + { + has_adjust_args = true; + cp_token *adjust_op_tok = cp_lexer_peek_token (parser->lexer); + if (cp_lexer_next_token_is (parser->lexer, CPP_NAME) + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON)) + { + const char *p = IDENTIFIER_POINTER (adjust_op_tok->u.value); + if (strcmp (p, "need_device_ptr") == 0 + || strcmp (p, "nothing") == 0) + { + cp_lexer_consume_token (parser->lexer); // need_device_ptr + cp_lexer_consume_token (parser->lexer); // : + location_t arg_loc + = cp_lexer_peek_token (parser->lexer)->location; + + tree arg; + tree list + = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_ERROR, + NULL_TREE, NULL); + + for (tree c = list; c != NULL_TREE; c = TREE_CHAIN (c)) + { + tree decl = TREE_PURPOSE (c); + int idx; + for (arg = parms, idx = 0; arg != NULL; + arg = TREE_CHAIN (arg), idx++) + if (TREE_VALUE (arg) == decl) + break; + if (arg == NULL_TREE) + { + error_at (arg_loc, "%qD is not a function argument", + decl); + continue; + } + arg = TREE_VALUE (arg); + if (adjust_args_list.contains (arg)) + { + // TODO fix location + error_at (arg_loc, "%qD is specified more than once", + decl); + continue; + } + if (strcmp (p, "need_device_ptr") == 0) + { + bool is_ptr_or_template + = TEMPLATE_PARM_P (TREE_TYPE (arg)) + || POINTER_TYPE_P (TREE_TYPE (arg)); + if (!is_ptr_or_template) + { + error_at (arg_loc, "%qD is not a C pointer", + decl); + continue; + } + } + adjust_args_list.safe_push (arg); + if (strcmp (p, "need_device_ptr") == 0) + { + need_device_ptr_list = chainon ( + need_device_ptr_list, + build_tree_list ( + NULL_TREE, + build_int_cst ( + integer_type_node, + idx))); // Store 0-based argument index, + // as in gimplify_call_expr + } + } + } + else + { + error_at (adjust_op_tok->location, + "expected %<nothing%> or %<need_device_ptr%>"); + goto fail; + } + } + else + { + error_at (adjust_op_tok->location, + "expected %<nothing%> or %<need_device_ptr%> followed " + "by %<:%>"); + goto fail; + } + } + } while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)); + + if (has_adjust_args) + { + if (!has_match) + { + error_at (adjust_args_loc, + "an %<adjust_args%> clause requires a %<match%> clause"); + } + else + { + tree ctx = TREE_VALUE (TREE_VALUE (attrs)); + if (!omp_get_context_selector (ctx, OMP_TRAIT_SET_CONSTRUCT, + OMP_TRAIT_CONSTRUCT_DISPATCH)) + error_at ( + adjust_args_loc, + "an %<adjust_args%> clause can only be specified if the " + "%<dispatch%> selector of the construct selector set appears " + "in the %<match%> clause"); + else if (TREE_CHAIN (need_device_ptr_list) != NULL_TREE) + { + // We might not have a DECL for the variant yet. So we store the + // need_device_ptr list in the base function attribute, after loc + // nodes. + gcc_assert (TREE_PURPOSE (attrs) + == get_identifier ("omp declare variant base")); + gcc_assert (TREE_PURPOSE (TREE_VALUE (attrs)) == variant); + TREE_VALUE (attrs) = chainon ( + TREE_VALUE (attrs), + build_tree_list ( + NULL_TREE, + build_tree_list (need_device_ptr_list, + NULL_TREE /*need_device_addr */))); + } + } } - parens.require_close (parser); cp_parser_skip_to_pragma_eol (parser, pragma_tok); return attrs; } @@ -49715,7 +50080,8 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, been parsed, and put that into "omp declare simd" attribute. */ static tree -cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs) +cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs, + tree parms) { struct cp_token_cache *ce; cp_omp_declare_simd_data *data = parser->omp_declare_simd; @@ -49759,7 +50125,7 @@ cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs) { gcc_assert (strcmp (kind, "variant") == 0); attrs - = cp_finish_omp_declare_variant (parser, pragma_tok, attrs); + = cp_finish_omp_declare_variant (parser, pragma_tok, attrs, parms); } cp_parser_pop_lexer (parser); } @@ -49890,9 +50256,8 @@ cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs) else { gcc_assert (strcmp (kind, "variant") == 0); - attrs - = cp_finish_omp_declare_variant (parser, pragma_tok, - attrs); + attrs = cp_finish_omp_declare_variant (parser, pragma_tok, + attrs, parms); } gcc_assert (parser->lexer != lexer); vec_safe_truncate (lexer->buffer, 0); @@ -51352,7 +51717,11 @@ fail: #pragma omp declare target new-line OpenMP 5.0 - #pragma omp declare variant (identifier) match (context-selector) */ + #pragma omp declare variant (identifier) match (context-selector) + + OpenMP 5.1 + #pragma omp declare variant (identifier) match (context-selector) \ + adjust_args (adjust-op:argument-list) */ static bool cp_parser_omp_declare (cp_parser *parser, cp_token *pragma_tok, @@ -52219,6 +52588,9 @@ cp_parser_omp_construct (cp_parser *parser, cp_token *pragma_tok, bool *if_p) case PRAGMA_OMP_UNROLL: stmt = cp_parser_omp_unroll (parser, pragma_tok, if_p); break; + case PRAGMA_OMP_DISPATCH: + stmt = cp_parser_omp_dispatch (parser, pragma_tok); + break; default: gcc_unreachable (); } @@ -52919,6 +53291,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p) "%<#pragma omp sections%> construct"); break; + case PRAGMA_OMP_DISPATCH: + cp_parser_omp_dispatch (parser, pragma_tok); + return true; + case PRAGMA_IVDEP: case PRAGMA_UNROLL: case PRAGMA_NOVECTOR: diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 47ee7cd3ed6e..d3e6a76fd81c 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -17769,6 +17769,16 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort, = tsubst_expr (OMP_CLAUSE_SIZES_LIST (oc), args, complain, in_decl); break; + case OMP_CLAUSE_NOCONTEXT: + OMP_CLAUSE_NOCONTEXT_EXPR (nc) + = tsubst_expr (OMP_CLAUSE_NOCONTEXT_EXPR (oc), args, complain, + in_decl); + break; + case OMP_CLAUSE_NOVARIANTS: + OMP_CLAUSE_NOVARIANTS_EXPR (nc) + = tsubst_expr (OMP_CLAUSE_NOVARIANTS_EXPR (oc), args, complain, + in_decl); + break; case OMP_CLAUSE_REDUCTION: case OMP_CLAUSE_IN_REDUCTION: case OMP_CLAUSE_TASK_REDUCTION: @@ -19481,6 +19491,16 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl) add_stmt (t); break; + case OMP_DISPATCH: + tmp = tsubst_omp_clauses (OMP_DISPATCH_CLAUSES (t), C_ORT_OMP, args, + complain, in_decl); + stmt = RECUR (OMP_DISPATCH_BODY (t)); + t = copy_node (t); + OMP_DISPATCH_BODY (t) = stmt; + OMP_DISPATCH_CLAUSES (t) = tmp; + add_stmt (t); + break; + case OMP_ATOMIC: gcc_assert (OMP_ATOMIC_DEPENDENT_P (t)); tmp = NULL_TREE; @@ -21337,6 +21357,14 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) } break; + case IFN_GOMP_DISPATCH: + ret = build_call_expr_internal_loc (EXPR_LOCATION (t), + IFN_GOMP_DISPATCH, + TREE_TYPE (call_args[0]), 1, + call_args[0]); + RETURN (ret); + break; + default: /* Unsupported internal function with arguments. */ gcc_unreachable (); diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 8c481fa625b2..d0c415f4692f 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -7924,6 +7924,26 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort) OMP_CLAUSE_FINAL_EXPR (c) = t; break; + case OMP_CLAUSE_NOCONTEXT: + t = OMP_CLAUSE_NOCONTEXT_EXPR (c); + t = maybe_convert_cond (t); + if (t == error_mark_node) + remove = true; + else if (!processing_template_decl) + t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); + OMP_CLAUSE_NOCONTEXT_EXPR (c) = t; + break; + + case OMP_CLAUSE_NOVARIANTS: + t = OMP_CLAUSE_NOVARIANTS_EXPR (c); + t = maybe_convert_cond (t); + if (t == error_mark_node) + remove = true; + else if (!processing_template_decl) + t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); + OMP_CLAUSE_NOVARIANTS_EXPR (c) = t; + break; + case OMP_CLAUSE_GANG: /* Operand 1 is the gang static: argument. */ t = OMP_CLAUSE_OPERAND (c, 1); diff --git a/gcc/testsuite/ChangeLog.omp b/gcc/testsuite/ChangeLog.omp index 6959c9b8854d..2162805e5708 100644 --- a/gcc/testsuite/ChangeLog.omp +++ b/gcc/testsuite/ChangeLog.omp @@ -1,3 +1,19 @@ +2025-01-27 Paul-Antoine Arras <par...@baylibre.com> + + Backported from master: + 2024-11-20 Paul-Antoine Arras <par...@baylibre.com> + + * g++.dg/gomp/adjust-args-1.C: New test. + * g++.dg/gomp/adjust-args-2.C: New test. + * g++.dg/gomp/adjust-args-3.C: New test. + * g++.dg/gomp/dispatch-1.C: New test. + * g++.dg/gomp/dispatch-2.C: New test. + * g++.dg/gomp/dispatch-3.C: New test. + * g++.dg/gomp/dispatch-4.C: New test. + * g++.dg/gomp/dispatch-5.C: New test. + * g++.dg/gomp/dispatch-6.C: New test. + * g++.dg/gomp/dispatch-7.C: New test. + 2025-01-27 Paul-Antoine Arras <par...@baylibre.com> Backported from master: diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-1.C b/gcc/testsuite/g++.dg/gomp/adjust-args-1.C new file mode 100644 index 000000000000..29fde1464f44 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-1.C @@ -0,0 +1,39 @@ +/* Test parsing of OMP clause adjust_args */ +/* { dg-do compile } */ + +int b; + +int f0 (void *a); +int g (void *a); +int f1 (int); + +#pragma omp declare variant (f0) match (construct={target}) adjust_args (nothing: a) /* { dg-error "an 'adjust_args' clause can only be specified if the 'dispatch' selector of the construct selector set appears in the 'match' clause" } */ +int f2 (void *a); +#pragma omp declare variant (f0) match (construct={dispatch,target}) adjust_args (need_device_ptr: a) /* { dg-error "'int f0.void..' used as a variant with incompatible 'construct' selector sets" } */ +int f2a (void *a); +#pragma omp declare variant (f0) match (construct={target,dispatch}) adjust_args (need_device_ptr: a) /* { dg-error "'int f0.void..' used as a variant with incompatible 'construct' selector sets" } */ +int f2b (void *a); +#pragma omp declare variant (f0) match (construct={dispatch},device={arch(gcn)}) adjust_args (need_device_ptr: a) /* { dg-error "'int f0.void..' used as a variant with incompatible 'construct' selector sets" } */ +int f2c (void *a); +#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (other: a) /* { dg-error "expected 'nothing' or 'need_device_ptr'" } */ +int f3 (int a); +#pragma omp declare variant (f0) adjust_args (nothing: a) /* { dg-error "an 'adjust_args' clause requires a 'match' clause" } */ +int f4 (void *a); +#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args () /* { dg-error "expected 'nothing' or 'need_device_ptr' followed by ':'" } */ +int f5 (int a); +#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (nothing) /* { dg-error "expected 'nothing' or 'need_device_ptr' followed by ':'" } */ +int f6 (int a); +#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (nothing:) /* { dg-error "expected unqualified-id before '\\)' token" } */ +int f7 (int a); +#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (nothing: z) /* { dg-error "'z' has not been declared" } */ +int f8 (int a); +#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (need_device_ptr: a) /* { dg-error "'a' is not a C pointer" } */ +int f9 (int a); +#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (nothing: a) adjust_args (nothing: a) /* { dg-error "'a' is specified more than once" } */ +int f10 (int a); +#pragma omp declare variant (g) match (construct={dispatch}) adjust_args (nothing: a) adjust_args (need_device_ptr: a) /* { dg-error "'a' is specified more than once" } */ +int f11 (void *a); +#pragma omp declare variant (g) match (construct={dispatch}) adjust_args (need_device_ptr: b) /* { dg-error "'b' is not a function argument" } */ +int f12 (void *a); +#pragma omp declare variant (g) match (construct={dispatch}) adjust_args (need_device_ptr: this) /* { dg-error "expected unqualified-id before 'this'" } */ +int f13 (void *a); diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-2.C b/gcc/testsuite/g++.dg/gomp/adjust-args-2.C new file mode 100644 index 000000000000..a78f06ec1937 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-2.C @@ -0,0 +1,51 @@ +struct S { + int a; + int g (const void *b); + #pragma omp declare variant (g) match (construct={dispatch}) adjust_args (need_device_ptr: b) + int f0(const void *b); + int operator()() { return a; } + bool operator!() { return !a; } +}; + +template <typename T> +T f0(T a, T *b); + +#pragma omp declare variant (f0) match (construct={dispatch}) adjust_args (need_device_ptr: a, b) +template <typename T> +T f1(T a, T *b); + +namespace N { + class C{ + public: + void g(C *c); + #pragma omp declare variant (g) match (construct={dispatch}) adjust_args (need_device_ptr: c) + void f0(C *c); + }; + void g(C *c); + #pragma omp declare variant (g) match (construct={dispatch}) adjust_args (need_device_ptr: c) + void f0(C *c); +} + +#pragma omp declare variant (g) match (construct={dispatch}) adjust_args (need_device_ptr: c) +void f3(N::C *c); +void f4(S *&s); +#pragma omp declare variant (f4) match (construct={dispatch}) adjust_args (need_device_ptr: s) +void f5(S *&s); + +void test() { + S s, *sp; + N::C c; + int *a, b; + #pragma omp dispatch + s.f0(a); + #pragma omp dispatch + f1(b, a); + #pragma omp dispatch + c.f0(&c); + #pragma omp dispatch + N::f0(&c); + #pragma omp dispatch + f3(&c); + #pragma omp dispatch + f5(sp); +} diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-3.C b/gcc/testsuite/g++.dg/gomp/adjust-args-3.C new file mode 100644 index 000000000000..3f9a0cd00713 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-3.C @@ -0,0 +1,6 @@ +// Check that an adjust_args clause does not lead to an ICE when the match +// clause is missing. + +void f(int *, int *, int *); +#pragma omp declare variant(f) adjust_args(need_device_ptr: xxx) /* { dg-error "an 'adjust_args' clause requires a 'match' clause" } */ +void g(int *xxx, int *yyy, int *zzz); diff --git a/gcc/testsuite/g++.dg/gomp/dispatch-1.C b/gcc/testsuite/g++.dg/gomp/dispatch-1.C new file mode 100644 index 000000000000..fb467afcd85b --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/dispatch-1.C @@ -0,0 +1,53 @@ +struct S { + int a; + void f0(double); + int operator()() { return a; } + bool operator!() { return !a; } +}; + +int f0(int); +template <typename T> +T f1(T a, T b); +void (*f2)(void); + +namespace N { + class C{}; + void f0(C); + int a; +} + +int test() { + int result; + double d = 5.0; + N::C c; + S s; + S* sp = &s; + int &r = result; + #pragma omp dispatch + result = f0(5); + #pragma omp dispatch + r = f0(5); + #pragma omp dispatch + N::a = ::f0(5); + #pragma omp dispatch + sp->a = f1<int>(5, 10); + #pragma omp dispatch + s.a = f1(5, 10); + #pragma omp dispatch + f2(); + #pragma omp dispatch + N::f0(c); + #pragma omp dispatch + f0(c); + #pragma omp dispatch + s.f0(d); + #pragma omp dispatch + sp->f0(d); + #pragma omp dispatch + sp->f0(d); + #pragma omp dispatch + s(); + #pragma omp dispatch + !s; + return result; +} diff --git a/gcc/testsuite/g++.dg/gomp/dispatch-2.C b/gcc/testsuite/g++.dg/gomp/dispatch-2.C new file mode 100644 index 000000000000..c987a371b970 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/dispatch-2.C @@ -0,0 +1,62 @@ +/* Test parsing of #pragma omp dispatch */ +/* { dg-do compile } */ + +struct S { + int a; + int b; + virtual int f (double); +}; + +int f0 (int); + +void f1 (void) +{ + int a, b; + double x; + int arr[1]; + S s; + +#pragma omp dispatch + int c = f0 (a); /* { dg-error "expected primary-expression before 'int'" } */ +#pragma omp dispatch + int f2 (int d); /* { dg-error "expected primary-expression before 'int'" } */ +#pragma omp dispatch + a = b; /* { dg-error "expected target-function call" } */ +#pragma omp dispatch + s.a = f0(a) + b; /* { dg-error "expected ';' before '\\+' token" } */ +#pragma omp dispatch + b = !f0(a); /* { dg-error "expected primary-expression before '!' token" } */ +#pragma omp dispatch + s.b += f0(s.a); /* { dg-error "expected '=' before '\\+=' token" } */ +#pragma omp dispatch +#pragma omp threadprivate(a) /* { dg-error "'#pragma' is not allowed here" } */ + a = f0(b); +#pragma omp dispatch + a = s.f(x); /* { dg-error "'f' is a virtual function but only a direct call is allowed in a dispatch construct" } */ + +#pragma omp dispatch nocontext(s) /* { dg-error "could not convert 's' from 'S' to 'bool'" } */ + f0(a); +#pragma omp dispatch nocontext(a, b) /* { dg-error "expected '\\)' before ','" } */ + f0(a); +#pragma omp dispatch nocontext(a) nocontext(b) /* { dg-error "too many 'nocontext' clauses" } */ + f0(a); +#pragma omp dispatch novariants(s) /* { dg-error "could not convert 's' from 'S' to 'bool'" } */ + f0(a); +#pragma omp dispatch novariants(a, b) /* { dg-error "expected '\\)' before ','" } */ + f0(a); +#pragma omp dispatch novariants(a) novariants(b) /* { dg-error "too many 'novariants' clauses" } */ + f0(a); +#pragma omp dispatch nowait nowait /* { dg-error "too many 'nowait' clauses" } */ + f0(a); +#pragma omp dispatch device(x) /* { dg-error "'device' id must be integral" } */ + f0(a); +#pragma omp dispatch device(arr) /* { dg-error "'device' id must be integral" } */ + f0(a); +#pragma omp dispatch is_device_ptr(x) /* { dg-error "'is_device_ptr' variable is neither a pointer, nor an array nor reference to pointer" } */ + f0(a); +#pragma omp dispatch is_device_ptr(&x) /* { dg-error "expected unqualified-id before '&' token" } */ + f0(a); +#pragma omp dispatch depend(inout: s.f) /* { dg-error "'s.S::f' is not lvalue expression nor array section in 'depend' clause" } */ + f0(a); + +} diff --git a/gcc/testsuite/g++.dg/gomp/dispatch-3.C b/gcc/testsuite/g++.dg/gomp/dispatch-3.C new file mode 100644 index 000000000000..03fd7dc6f6ce --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/dispatch-3.C @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-original -fdump-tree-gimple" } */ + +/* Check that the right call to f is wrapped in a GOMP_DISPATCH internal function + before translation and that it is stripped during gimplification. */ + +int &f(int); +void g(int *x) +{ + #pragma omp dispatch + x[f(1)] = f(f(2)); + // ^ only this call to f is a dispatch call +} + +/* { dg-final { scan-tree-dump "\.GOMP_DISPATCH \\(\\*f \\(\\*f \\(2\\)\\)\\)" "original" } } */ +/* { dg-final { scan-tree-dump-times "\.GOMP_DISPATCH" 1 "original" } } */ +/* { dg-final { scan-tree-dump-not "\.GOMP_DISPATCH" "gimple" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/dispatch-4.C b/gcc/testsuite/g++.dg/gomp/dispatch-4.C new file mode 100644 index 000000000000..a0fe6c746c4b --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/dispatch-4.C @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +/* Check that no host-to-device pointer conversion happens for a nullptr + argument. */ + +#include <cstddef> + +int variant_fn(int *, int * = NULL); + +#pragma omp declare variant(variant_fn) match(construct={dispatch}) adjust_args(need_device_ptr : x, y) +int bar(int *x, int *y = NULL); + +void sub(int *a, int *b) +{ + int x; + #pragma omp dispatch + x = bar(a); +} + +/* { dg-final { scan-tree-dump-times "__builtin_omp_get_mapped_ptr" 1 "gimple" } } */ +/* { dg-final { scan-tree-dump-not "__builtin_omp_get_mapped_ptr \\(OB" "gimple" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/dispatch-5.C b/gcc/testsuite/g++.dg/gomp/dispatch-5.C new file mode 100644 index 000000000000..268bbac5d766 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/dispatch-5.C @@ -0,0 +1,17 @@ +/* { dg-do compile } */ + +/* Check that the parser does not issue an error when a variant returns a + reference. */ + +int& variant_fn(); + +#pragma omp declare variant(variant_fn) match(construct={target}) +int& bar(); + +void sub(int a) +{ + #pragma omp dispatch + bar(); + #pragma omp dispatch + a = bar(); +} diff --git a/gcc/testsuite/g++.dg/gomp/dispatch-6.C b/gcc/testsuite/g++.dg/gomp/dispatch-6.C new file mode 100644 index 000000000000..675b1ec8d9ae --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/dispatch-6.C @@ -0,0 +1,29 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-original" } */ + +/* Check that templates are properly handled. */ + +template<typename T> +T templ_var_fn(T x); + +#pragma omp declare variant(templ_var_fn) match(construct={dispatch}) adjust_args(need_device_ptr: x) +template<typename T> +T templ_base_fn(T x); + +template<typename T, typename TB> +void f(int *y, TB x) +{ +#pragma omp dispatch nocontext (x) + y = templ_base_fn<T> (y); +#pragma omp dispatch novariants (x) + y = templ_base_fn<T> (y); +} + +void bar() +{ + int a; + bool b = true; + f<int*,bool> (&a, b); +} + +/* { dg-final { scan-tree-dump-times "y = \.GOMP_DISPATCH \\(templ_base_fn<int\\*> \\(y\\)\\)" 2 "original" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/dispatch-7.C b/gcc/testsuite/g++.dg/gomp/dispatch-7.C new file mode 100644 index 000000000000..193cca860150 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/dispatch-7.C @@ -0,0 +1,43 @@ +// { dg-do compile { target c++11 } } + + +template<typename T> +T templ_var_fn(T x); + + +template<typename T> [[omp::directive (declare variant(templ_var_fn) match(construct={dispatch}) adjust_args(need_device_ptr: x))]] +T templ_base_fn(T x); + +template<typename T, typename TB> +void f(int *y, TB x) +{ +[[omp::directive (dispatch nocontext (x))]] + y = templ_base_fn<T> (y); +[[omp::directive (dispatch novariants (x))]] + y = templ_base_fn<T> (y); +} + +void bar() +{ + int a; + bool b = true; + f<int*,bool> (&a, b); +} + + +void variant_fn(int *x, int *y, int *z); + +[[omp::directive (declare variant(variant_fn) match(construct={dispatch}) adjust_args(need_device_ptr: x,y) adjust_args(nothing: z))]] +void bar(int *x, int *y, int *z); + +void sub(int *x, int *y, int *z) +{ + [[omp::directive (dispatch is_device_ptr(y))]] + bar(x, y, z); + [[omp::directive (dispatch device(0))]] + bar(x, y, z); + [[omp::directive (dispatch nocontext(1) novariants(1))]] + bar(x, y, z); + [[omp::directive (dispatch depend(inout: x) nowait)]] + bar(x, y, z); +}