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.
This commit is contained in:
Paul-Antoine Arras 2024-11-20 15:28:58 +01:00
parent d7d8d9dae9
commit ed49709acd
14 changed files with 818 additions and 48 deletions

View file

@ -8401,6 +8401,13 @@ omp_declare_variant_finalize_one (tree decl, tree attr)
if (!omp_context_selector_matches (ctx))
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)

View file

@ -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"
@ -2603,7 +2604,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
@ -2638,7 +2639,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);
@ -24327,7 +24328,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);
@ -25192,8 +25193,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;
@ -25229,8 +25230,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,
@ -38349,6 +38350,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))
@ -38357,6 +38360,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))
@ -40803,6 +40808,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 ) */
@ -42910,6 +42965,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;
@ -49151,12 +49216,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))
@ -49214,44 +49427,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);
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);
enum clause
{
match,
adjust_args
} ccode;
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);
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 */)));
}
}
}
cp_lexer_consume_token (parser->lexer);
if (!parens.require_open (parser))
goto fail;
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);
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;
}
parens.require_close (parser);
cp_parser_skip_to_pragma_eol (parser, pragma_tok);
return attrs;
}
@ -49261,7 +49626,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;
@ -49305,7 +49671,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);
}
@ -49436,9 +49802,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);
@ -50291,7 +50656,11 @@ cp_parser_omp_declare_reduction (cp_parser *parser, cp_token *pragma_tok,
#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,
@ -51155,6 +51524,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 ();
}
@ -51851,6 +52223,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:

View file

@ -17799,6 +17799,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:
@ -19462,6 +19472,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;
@ -21205,6 +21225,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 ();

View file

@ -7753,6 +7753,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);

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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" } } */

View file

@ -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" } } */

View file

@ -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();
}

View file

@ -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" } } */

View file

@ -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);
}