diff options
author | waffl3x <waffl3x@baylibre.com> | 2025-05-05 20:20:00 +0000 |
---|---|---|
committer | Sandra Loosemore <sloosemore@baylibre.com> | 2025-05-15 20:25:54 +0000 |
commit | ac1372b107c14dc7d8b1321391a403f5aab33de2 (patch) | |
tree | cff798d96658b243064c0aa20b3753e4546a6790 /gcc | |
parent | 983c4ffae6391caa9e2ac16ed1e3495fff2b95bb (diff) | |
download | gcc-ac1372b107c14dc7d8b1321391a403f5aab33de2.zip gcc-ac1372b107c14dc7d8b1321391a403f5aab33de2.tar.gz gcc-ac1372b107c14dc7d8b1321391a403f5aab33de2.tar.bz2 |
OpenMP: C/C++ adjust-args numeric ranges
Add support for OpenMP parameter-lists in an adjust_args clause, more
specifically, numeric ranges and parameter indices. Many bugs are also
fixed along the way. Most of the fixes rely on the changes to handling of
clause arguments and can't reasonably be split out, while most of the
changes that fix PR119602 came as a side effect of properly handling numeric
ranges with dependent bounds so it makes sense to include it here.
Variadic arguments with an incorrect type are not currently diagnosed, but
handling for them is otherwise functional. It is unclear how references to
pointers are supposed to be handled, so for now we sorry for that case.
PR c++/119659
PR c++/118859
PR c++/119601
PR c++/119602
PR c++/119775
gcc/c/ChangeLog:
* c-parser.cc (c_omp_numeric_ranges_always_overlap): New function.
(c_parser_omp_parm_list): New function.
(c_finish_omp_declare_variant): Use c_parser_omp_parm_list instead
of c_parser_omp_variable_list. Refactor, change format of
"omp declare variant variant args" attribute.
gcc/cp/ChangeLog:
PR c++/119659
PR c++/118859
PR c++/119601
PR c++/119602
PR c++/119775
* cp-tree.h (finish_omp_parm_list): New declaration.
(finish_omp_adjust_args): New declaration.
* decl.cc (omp_declare_variant_finalize_one): Refactor and change
attribute unpacking, use finish_omp_parm_list and
finish_omp_adjust_args, refactor append_args diagnostics, add
nbase_parms to append_args attribute, remove special handling for
member functions.
* parser.cc (cp_parser_direct_declarator): Don't pass parms.
(cp_parser_late_return_type_opt): Remove parms parameter.
(cp_parser_omp_parm_list): New function.
(cp_finish_omp_declare_variant): Remove parms parameter.
Add NULL_TREE instead of nbase_args to append_args_tree. Refactor,
use cp_parser_omp_parm_list not cp_parser_omp_var_list_no_open,
handle "need_device_addr" and remove handling and diagnostics of
parm list arguments that are done too early. Change format of
unnamed variant attribute.
(cp_parser_late_parsing_omp_declare_simd): Remove parms parameter.
* pt.cc (tsubst_attribute): Copy "omp declare variant base" nodes,
substitute parm list numeric range bounds.
* semantics.cc (finish_omp_parm_list): New function.
(finish_omp_adjust_args): New function.
gcc/fortran/ChangeLog:
* trans-openmp.cc (gfc_trans_omp_declare_variant): Change format of
"omp declare variant variant args" attribute.
gcc/ChangeLog:
* gimplify.cc (modify_call_for_omp_dispatch): Refactor and change
attribute unpacking. For adjust_args variadic functions, expand
numeric ranges with relative bounds. Refactor argument adjustment.
libgomp/ChangeLog:
* libgomp.texi: Set 'adjust args' variadic arguments support to Y.
gcc/testsuite/ChangeLog:
* c-c++-common/gomp/pr118579.c: Change error text.
* g++.dg/gomp/adjust-args-1.C: Fix error text, add dg-* directives.
* g++.dg/gomp/adjust-args-2.C: Add dg-* directives.
* g++.dg/gomp/append-args-1.C: Add dg-* directives.
* gcc.dg/gomp/adjust-args-1.c: Fix error text, add dg-* directives.
* gcc.dg/gomp/append-args-1.c: Fix error text, add dg-* directives.
* c-c++-common/gomp/adjust-args-7.c: New test.
* c-c++-common/gomp/adjust-args-8.c: New test.
* c-c++-common/gomp/adjust-args-9.c: New test.
* c-c++-common/gomp/adjust-args-10.c: New test.
* c-c++-common/gomp/adjust-args-11.c: New test.
* c-c++-common/gomp/adjust-args-12.c: New test.
* c-c++-common/gomp/adjust-args-13.c: New test.
* c-c++-common/gomp/adjust-args-14.c: New test.
* c-c++-common/gomp/adjust-args-15.c: New test.
* g++.dg/gomp/adjust-args-5.C: New test.
* g++.dg/gomp/adjust-args-6.C: New test.
* g++.dg/gomp/adjust-args-7.C: New test.
* g++.dg/gomp/adjust-args-8.C: New test.
* g++.dg/gomp/adjust-args-9.C: New test.
* g++.dg/gomp/adjust-args-10.C: New test.
* g++.dg/gomp/adjust-args-11.C: New test.
* g++.dg/gomp/adjust-args-12.C: New test.
* g++.dg/gomp/adjust-args-13.C: New test.
* g++.dg/gomp/adjust-args-14.C: New test.
* g++.dg/gomp/adjust-args-15.C: New test.
* g++.dg/gomp/adjust-args-16.C: New test.
* g++.dg/gomp/append-args-9.C: New test.
* g++.dg/gomp/append-args-10.C: New test.
* g++.dg/gomp/append-args-11.C: New test.
* g++.dg/gomp/append-args-omp-interop-t.h: New header.
Signed-off-by: Waffl3x <waffl3x@baylibre.com>
Diffstat (limited to 'gcc')
39 files changed, 4914 insertions, 434 deletions
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 31ab7d0..9af7440 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -17116,6 +17116,722 @@ c_parser_omp_var_list_parens (c_parser *parser, enum omp_clause_code kind, return list; } +/* Helper for c_parser_omp_parm_list and c_finish_omp_declare_variant. + Compare two OpenMP parameter-list-item numeric ranges with a relative bound. + Returns true if they always overlap for any value of omp_num_args, + returns false otherwise. + + Literal bounds are never compared with each other here, + c_parser_omp_parm_list already handles that case. + + In hindsight, this was never really worth doing. If there is ever a case + found that this function gets wrong the best course of action is probably + to just disable the section that causes a problem. This function only + serves to diagnose overlapping numeric ranges in variadic functions early, + gimplify.cc:modify_call_for_omp_dispatch will always catch these problems + when the numeric range is expanded even if this function misses any cases. + + If I could go back in time, I would stop myself from writing this, but it's + already done now. It technically does serve its purpose of providing better + diagnostics for niche scenarios, so until it breaks, here it is. */ + +static bool +c_omp_numeric_ranges_always_overlap (tree first, tree second) +{ + gcc_assert (first && TREE_CODE (first) == TREE_LIST + && second && TREE_CODE (second) == TREE_LIST); + + auto bound_is_relative = [] (tree bound) -> bool + { + gcc_assert (!TREE_PURPOSE (bound) + || TREE_PURPOSE (bound) + == get_identifier ("omp relative bound")); + /* NULL_TREE means literal, the only other possible value is + get_identifier ("omp relative bound"), I hate this design though. */ + return TREE_PURPOSE (bound); + }; + + tree lb1 = TREE_PURPOSE (first); + tree ub1 = TREE_VALUE (first); + gcc_assert (lb1 && ub1); + const bool lb1_relative = bound_is_relative (lb1); + const bool ub1_relative = bound_is_relative (ub1); + const bool first_mixed = !(lb1_relative && ub1_relative); + + tree lb2 = TREE_PURPOSE (second); + tree ub2 = TREE_VALUE (second); + gcc_assert (lb2 && ub2); + const bool lb2_relative = bound_is_relative (lb2); + const bool ub2_relative = bound_is_relative (ub2); + const bool second_mixed = !(lb2_relative && ub2_relative); + + /* Both ranges must have a relative bound. */ + gcc_assert ((lb1_relative || ub1_relative) + && (lb2_relative || ub2_relative)); + + /* Both fully relative. */ + if (!first_mixed && !second_mixed) + { + /* (relative : relative), (relative : relative) */ + wi::tree_to_widest_ref lb1_v = wi::to_widest (TREE_VALUE (lb1)); + wi::tree_to_widest_ref ub1_v = wi::to_widest (TREE_VALUE (ub1)); + wi::tree_to_widest_ref lb2_v = wi::to_widest (TREE_VALUE (lb2)); + wi::tree_to_widest_ref ub2_v = wi::to_widest (TREE_VALUE (ub2)); + /* We compare lower bound to upper bound including equality because + upper bounds are stored as one past the end of the range. */ + return (lb1_v >= lb2_v && lb1_v < ub2_v) + || (ub1_v > lb2_v && ub1_v <= ub2_v); + } + else if (first_mixed && second_mixed) + { + /* Note that this is a comparison, not logical and/or. */ + if (lb1_relative == lb2_relative) + { + /* FIRST SECOND + LB1 UB1 LB2 UB2 + (literal : relative), (literal : relative) + (relative : literal), (relative : literal) */ + + /* Simply compare the relative bounds, if they match the two ranges + will always overlap. + There is some other static analysis that can be done, but it isn't + worth the time to implement. */ + gcc_assert (ub1_relative == ub2_relative); + if (lb1_relative) + { + /* (relative : literal), (relative : literal) */ + return wi::to_widest (TREE_VALUE (lb1)) + == wi::to_widest (TREE_VALUE (lb2)); + } + else + { + /* (literal : relative), (literal : relative) */ + return wi::to_widest (TREE_VALUE (ub1)) + == wi::to_widest (TREE_VALUE (ub2)); + } + } + else + { + /* FIRST SECOND + LB1 UB1 LB2 UB2 + (literal : relative), (relative : literal) + (relative : literal), (literal : relative) */ + gcc_assert (lb1_relative != lb2_relative + && ub2_relative != ub2_relative + && (lb1_relative == ub2_relative + || lb2_relative == ub1_relative)); + /* There is definitely more interesting static analysis that can + be done here but it would probably be a waste of time. */ + tree relative_lb = lb1_relative ? lb1 : lb2; + tree relative_ub = ub1_relative ? ub1 : ub2; + return wi::to_widest (TREE_VALUE (relative_lb)) + >= wi::to_widest (TREE_VALUE (relative_ub)); + } + } + else + { + /* FIRST SECOND + LB1 UB1 LB2 UB2 + (literal : relative), (relative : relative) + (relative : relative), (literal : relative) + + (relative : relative), (relative : literal) + (relative : literal), (relative : relative) */ + gcc_assert ((first_mixed && !second_mixed) + || (!first_mixed && second_mixed)); + tree lb_mixed = first_mixed ? lb1 : lb2; + tree ub_mixed = first_mixed ? ub1 : ub2; + + tree lb_full_relative = !first_mixed ? lb1 : lb2; + tree ub_full_relative = !first_mixed ? ub1 : ub2; + + if (bound_is_relative (lb_mixed)) + { + return wi::to_widest (TREE_VALUE (lb_mixed)) + >= wi::to_widest (TREE_VALUE (lb_full_relative)) + && wi::to_widest (TREE_VALUE (lb_mixed)) + < wi::to_widest (TREE_VALUE (ub_full_relative)); + } + else + { + gcc_assert (bound_is_relative (ub_mixed)); + return wi::to_widest (TREE_VALUE (ub_mixed)) + > wi::to_widest (TREE_VALUE (lb_full_relative)) + && wi::to_widest (TREE_VALUE (ub_mixed)) + <= wi::to_widest (TREE_VALUE (ub_full_relative)); + } + } + gcc_unreachable (); +} + + +/* Parse an OpenMP parameter-list. + parameter-list: + parameter-list-item[, parameter-list-item [, ...]] + + parameter-list-item: + named parameter list item + parameter index (1 based) + numeric-range + + + numeric-range: + [bound]:[bound] + + bound: + index-expr + omp_num_args[±logical_offset] + + A named parameter list item is the name of a parameter. A parameter index + is a positive integer literal that is the 1 based index of a parameter. + A numeric-range is a pair of bounds of the form lb:ub, the values of each + bound form a closed interval of parameter indices. Bounds can be literal or + relative. An index-expr is a non-negative integer constant-expression that + is the value of a literal bound. The special identifier omp_num_args is + equal to the number of arguments passed to the function at the call site, + including the number of varargs. Optionally, a plus or minus with a + logical_offset may follow omp_num_args, logical_offset is a non-negative + integer constant-expression. A bound formed with omp_num_args is a relative + bound. If a bound is omitted, a default value is used. The default value + of lb is as if 1 were specified, the default value of ub is as if + omp_num_args were specified. + + Each parameter-list-item is stored in a TREE_LIST. The PURPOSE is for + general use and left NULL_TREE here, and the item is stored in the VALUE. + An item is a TREE_LIST, the PURPOSE is an expression with the location of + the list item, and the VALUE is a representation of the item. + Each parameter-list-item is stored in a TREE_LIST node VALUE. The PURPOSE + is unused, and the VALUE is the item-repr. + + Node - PUPOSE: NULL_TREE + - VALUE: item-with-location + item-with-location - PURPOSE: expr-with-location + - VALUE: item-repr + + An item-repr is a INTEGER_CST or a TREE_LIST. An INTEGER_CST is the 0 based + index of a specified parameter, derived from a named parameter list item or + a parameter index. A TREE_LIST is a numeric-range where its PURPOSE is a + TREE_LIST representing the lb, and its VALUE is a TREE_LIST representing the + ub. + + item-repr + INTEGER_CST - parameter index (0 based) + TREE_LIST - PURPOSE: TREE_LIST (lb) + - VALUE: TREE_LIST (ub) + + lb and ub are a TREE_LIST of the following form; + TREE_LIST - PURPOSE: relative bound marker (NULL_TREE if literal) + - VALUE: expr-value + + In non-variadic functions numeric ranges are immediately expanded into + INTEGER_CST nodes corresponding to each index specified by the interval. + + The expr-value is an INTEGER_CST node of type integer_type_node, the value + corresponding to the expr. The value of lb is adjusted to be 0 based, while + the value of ub is adjusted to be 0 based, and one past the end to support + empty ranges. In other words, lb is adjusted by -1, and ub remains the + same. + + Parameters that are specified but are not defined, out of range indices and + duplicate specifications are diagnosed. Additionally, numeric ranges that + can be proven to always overlap for any value of omp_num_args even before + expansion are also diagnosed. This provides diagnostics that occur before + the function is used. In hindsight, I wish I didn't waste my time on that + last one. + If a diagnostic is issued for a list item, it is not appened to the list and + parsing continues. Returns NULL_TREE if no valid list items are parsed. + + This function strictly handles a parameter-list, it does not parse clause + modifiers, or parenthesis other than in the expr of a numeric range. */ + +static tree +c_parser_omp_parm_list (c_parser *parser, tree decl, const int parm_count) +{ + /* TODO: C++ front end was enhanced a little, gotta make changes in here + to match it. */ + tree list = NULL_TREE; + /* Even though an adjust_args clause on a non-variadic function with 0 + parameters is silly, we should still probably handle it gracefully. */ + const bool variadic_p = TYPE_ARG_TYPES (TREE_TYPE (decl)) != void_list_node + && parm_count == 0; + const int omp_num_args_value = parm_count; + + auto unique_append_to_list = [&list, &variadic_p] (int idx, location_t loc) + { + gcc_assert (idx >= 0); + /* Keep track of the last chain to append to the list. */ + tree *chain = &list; + for (tree node = list; node; node = TREE_CHAIN (node)) + { + chain = &TREE_CHAIN (node); + tree item = TREE_VALUE (node); + /* Skip numeric range nodes, only valid for variadic functions. */ + if (variadic_p && TREE_CODE (TREE_VALUE (item)) != INTEGER_CST) + /* Early exit. */; + else if (wi::to_widest (TREE_VALUE (item)) == idx) + /* Return the item for diagnostic purposes. */ + return item; + } + gcc_assert (*chain == NULL_TREE); + /* Store the location in PURPOSE for use in diagnostics. */ + tree item = build_tree_list (build_empty_stmt (loc), + build_int_cst (integer_type_node, idx)); + /* Leave PURPOSE unused for use by the caller of + c_parser_omp_parm_list. */ + *chain = build_tree_list (NULL_TREE, item); + return NULL_TREE; + }; + + auto tok_terminates_item_p = [] (c_token *tok) + { + return tok->type == CPP_COMMA + || tok->type == CPP_CLOSE_PAREN; + }; + /* The first list item is (obviously) not preceded by a comma. */ + goto first_element; + do + { + /* Consume the comma. */ + c_parser_consume_token (parser); + first_element: + c_token *const tok = c_parser_peek_token (parser); + + /* OpenMP 6.0 (162:29-34) + A parameter list item can be one of the following: + • A named parameter list item; + • The position of a parameter in a parameter specification specified + by a positive integer, where 1 represents the first parameter; or + • A parameter range specified by lb : ub where both lb and ub must + be an expression of integer OpenMP type with the constant property + and the positive property. + + The spec does not support arbitrary expression outside of a numeric + range. In theory they could be supported as a parameter index, but + for now we do not support that case. */ + + /* If we don't see a comma or close paren this can't be a named parameter + list item or a parameter index, it can only be a numeric range. + As far as I can tell, there is no well-formed code that could break + this assumption. */ + if (!tok_terminates_item_p (c_parser_peek_2nd_token (parser)) + /* Or this edge case, there is a default lower bound. */ + || tok->type == CPP_COLON) + /* Early exit, numeric range case handled below. */; + else if (tok->type == CPP_NAME + && tok->id_kind == C_ID_ID) + { + if (strcmp (IDENTIFIER_POINTER (tok->value), "omp_num_args") == 0) + { + error_at (tok->location, "%<omp_num_args%> may only be used at " + "the start of a numeric range bound"); + c_parser_consume_token (parser); + continue; + } + tree parm_decl = lookup_name (tok->value); + + if (parm_decl && TREE_CODE (parm_decl) == PARM_DECL) + { + tree parm = DECL_ARGUMENTS (decl); + /* We store indices in 0 based form internally. */ + int idx = 0; + while (parm != parm_decl) + { + gcc_assert (parm != NULL_TREE && parm != void_list_node); + ++idx; + parm = DECL_CHAIN (parm); + } + if (tree dupe = unique_append_to_list (idx, tok->location)) + { + error_at (tok->location, + "OpenMP parameter list items must specify a " + "unique parameter"); + inform (EXPR_LOCATION (TREE_PURPOSE (dupe)), + "parameter previously specified here"); + } + } + else + { + /* It feels like the only reasonable solution is to cook our own + solution for this, undeclared_variable doesn't give us what + we wan't for more than a few reasons. */ + error_at (tok->location, + "%qs is not a function parameter", + IDENTIFIER_POINTER (tok->value)); + /* FIXME: Something like this is a good idea. */ + /* if (parm_decl && TREE_CONSTANT (parm_decl)) + inform (tok->location, + "an expression is only allowed in a " + "numeric range"); */ + /* Don't use undeclared_variable if we are parsing a decl + instead of a declaration, it breaks subsequent lookups in + later functions. */ + } + c_parser_consume_token (parser); + continue; + } + else if (tok->type == CPP_NUMBER) + { + if (wi::to_widest (tok->value) <= 0) + error_at (tok->location, "parameter indices in an OpenMP " + "parameter list must be positive"); + else if (wi::to_widest (tok->value) > INT_MAX) + error_at (tok->location, "parameter index is too big"); + else + { + /* We store indices 0 based internally, OpenMP specifies + 1 based indices, modify it. */ + const int idx = tree_to_shwi (tok->value) - 1; + if (!variadic_p && idx >= parm_count) + error_at (tok->location, + "parameter list item index is out of range"); + else + { + if (tree dupe = unique_append_to_list (idx, tok->location)) + { + error_at (tok->location, + "OpenMP parameter list items must specify a " + "unique parameter"); + inform (EXPR_LOCATION (TREE_PURPOSE (dupe)), + "parameter previously specified here"); + } + } + } + c_parser_consume_token (parser); + continue; + } + else + { + gcc_checking_assert (tok_terminates_item_p + (c_parser_peek_2nd_token (parser))); + error_at (tok->location, "expected parameter or integer"); + c_parser_consume_token (parser); + continue; + } + /* We have a numeric range or something ill formed now, this can be + an arbitrary expression. */ + + /* Empty bounds are delimited differently for lower and upper bounds, + handle them before calling parse_bound. */ + auto parse_bound = [&] () + { + enum omp_num_args + { + num_args_none, + num_args_plus, + num_args_minus, + num_args_no_offset + }; + /* (OpenMP 6.0, 162:35-37) + In both lb and ub, an expression using omp_num_args, that enables + identification of parameters relative to the last argument of the + call, can be used with the form: + omp_num_args [± logical_offset] */ + + const omp_num_args parsed_omp_num_args = [&] () + { + c_token *tok = c_parser_peek_token (parser); + if (tok->type == CPP_NAME + && tok->id_kind == C_ID_ID + && strcmp (IDENTIFIER_POINTER (tok->value), "omp_num_args") + == 0) + { + /* Consume omp_num_args. */ + c_parser_consume_token (parser); + c_token *op_tok = c_parser_peek_token (parser); + if (op_tok->type == CPP_PLUS) + { + c_parser_consume_token (parser); + return num_args_plus; + } + else if (op_tok->type == CPP_MINUS) + { + c_parser_consume_token (parser); + return num_args_minus; + } + return num_args_no_offset; + } + else + return num_args_none; + } (); /* IILE. */ + + /* If there was omp_num_args but no operator an expr is not + permitted, we are finished with this bound. */ + if (parsed_omp_num_args == num_args_no_offset) + return build_int_cst (integer_type_node, omp_num_args_value); + gcc_assert (parsed_omp_num_args < num_args_no_offset); + + c_expr expr = c_parser_expr_no_commas (parser, NULL); + /* I don't know if this location is correct. */ + const location_t expr_loc = expr.get_location (); + /* I don't think read_p true is correct. */ + expr = convert_lvalue_to_rvalue (expr_loc, expr, false, true); + if (expr.value == error_mark_node) + return error_mark_node; + tree folded = c_fully_fold (expr.value, false, NULL); + if (!TREE_CONSTANT (folded)) + { + error_at (expr_loc, "expression of a bound must be a " + "constant expression"); + return error_mark_node; + } + /* This seems wrong... */ + gcc_assert (TREE_CODE (folded) == INTEGER_CST); + /* If we have omp_num_args, expr can be 0, + if we don't, expr must be positive. */ + const int sgn = tree_int_cst_sgn (folded); + /* I'm sure this is wrong but I dunno a better way right now. */ + const ptrdiff_t value = tree_to_shwi (folded); + switch (parsed_omp_num_args) + { + case num_args_none: + { + if (sgn != 1) + { + error_at (expr_loc, "expression of bound must be " + "positive"); + return error_mark_node; + } + if (!variadic_p && value > omp_num_args_value) + { + error_at (expr_loc, "expression of bound is out " + "of range"); + return error_mark_node; + } + return build_int_cst (integer_type_node, value); + } + case num_args_plus: + { + if (sgn != 0) + { + error_at (expr_loc, + "logical offset must be equal to 0 in a bound " + "of the form %<omp_num_args+logical-offset%>"); + return error_mark_node; + } + return build_int_cst (integer_type_node, omp_num_args_value); + } + case num_args_minus: + { + if (sgn == -1) + { + error_at (expr_loc, + "logical offset must be non-negative"); + return error_mark_node; + } + if (variadic_p) + return build_int_cst (integer_type_node, -value); + const ptrdiff_t parm_index = omp_num_args_value - value; + if (parm_index <= 0) + { + error_at (expr_loc, + "bound with logical offset evaluates to an " + "out of range index"); + return error_mark_node; + } + return build_int_cst (integer_type_node, parm_index); + } + case num_args_no_offset: + /* Handled above. */ + default: + gcc_unreachable (); + } + gcc_unreachable (); + }; + const location_t num_range_loc_begin = tok->location; + + /* As stated above, empty bounds are handled here. */ + tree lb = c_parser_next_token_is (parser, CPP_COLON) ? NULL_TREE + : parse_bound (); + /* I wish we could error here saying that we expect an unqualified-id, + an integer, or an expression. Parsing the expression emits the error + right away though. */ + if (lb && error_operand_p (lb)) + { + c_parser_skip_to_end_of_parameter (parser); + continue; + } + /* Tokens get consumed by parse_bound. */ + if (c_parser_next_token_is_not (parser, CPP_COLON)) + { + /* lower_bound can only be null if the next token was a colon. */ + gcc_assert (lb != NULL_TREE); + c_parser_error (parser, "expected %<:%>"); + if (tok_terminates_item_p (c_parser_peek_token (parser))) + { + const location_t loc = make_location (num_range_loc_begin, + num_range_loc_begin, + input_location); + inform (loc, "an expression is only allowed in a numeric range"); + } + c_parser_skip_to_end_of_parameter (parser); + continue; + } + const location_t colon_loc = c_parser_peek_token (parser)->location; + c_parser_consume_token (parser); + + tree ub = tok_terminates_item_p (c_parser_peek_token (parser)) + ? NULL_TREE : parse_bound (); + if (!ub || ub == error_mark_node) + c_parser_skip_to_end_of_parameter (parser); + + location_t num_range_loc_end = ub != NULL_TREE ? input_location + : colon_loc; + location_t num_range_loc = make_location (num_range_loc_begin, + num_range_loc_begin, + num_range_loc_end); + /* I think we are supposed to have some sort of diagnostic here, I'm just + not sure what it should be. */ + if (lb == error_mark_node || ub == error_mark_node) + continue; + /* Handle default bounds. */ + const ptrdiff_t lb_val = lb ? tree_to_shwi (lb) + : 1; + const ptrdiff_t ub_val = ub ? tree_to_shwi (ub) + : omp_num_args_value; + + gcc_assert (variadic_p || (lb_val > 0 && ub_val > 0)); + /* We only know this at this point if they are both negative/zero or both + positive, so basically if both or neither use omp_num_args. */ + /* FIXME: need a test for this case, I think we are missing this case + in the C++ front end, so add it. */ + if (((lb_val <= 0) == (ub_val <= 0)) && lb_val > ub_val) + { + error_at (num_range_loc, + "numeric range lower bound must be less than " + "or equal to upper bound"); + continue; + } + + auto add_range_known = [&] (const int lb, const int ub) + { + gcc_assert (lb > 0 && ub > 0 && lb <= ub); + + for (int idx = lb; idx <= ub; ++idx) + { + gcc_assert (variadic_p || idx <= parm_count); + if (tree dupe = unique_append_to_list (idx - 1, num_range_loc)) + { + error_at (num_range_loc, + "expansion of numeric range specifies " + "non-unique index %d", idx); + inform (EXPR_LOCATION (TREE_PURPOSE (dupe)), + "parameter previously specified here"); + } + } + }; + /* Store ub as exclusive (one past the end) so we can differentiate an + empty range from a range of one index without ever encoding lb as + greater than ub. + Semantically, OpenMP does not allow this as numeric range bounds are + specified to be inclusive, but we utilize it for diagnostic purposes. + This is explained in detail below. */ + auto add_range_unknown = [&] (const int lb_in, + const bool lb_relative_p, + const int ub_in, + const bool ub_relative_p) + { + /* If both bounds are relative, then lb should be <= ub. */ + gcc_assert ((!(lb_relative_p && ub_relative_p) || lb_in <= ub_in) + /* We only deal with ranges that aren't known here, so + at least one bound should be relative to num args. */ + && (lb_relative_p || ub_relative_p)); + /* Adjust to be 0 based, -1 now corresponds to the last arg. */ + const int lb = lb_in - 1; + /* Adjust to be 0 based, but add 1 to make it one past the end. */ + const int ub = ub_in - 1 + 1; + /* We don't check against the non-range indices, we already check + that by adding any indices we can be sure of WAY below. */ + auto build_bound = [] (int val, bool add_num_args) + { + return build_tree_list (add_num_args + ? get_identifier ("omp relative bound") + : NULL_TREE, + build_int_cst (integer_type_node, val)); + }; + tree lb_node = build_bound (lb, lb_relative_p); + tree ub_node = build_bound (ub, ub_relative_p); + tree new_range = build_tree_list (lb_node, ub_node); + /* Keep track of the last chain to append to the list. */ + tree *chain = &list; + for (tree node = list; node; node = TREE_CHAIN (node)) + { + chain = &TREE_CHAIN (node); + tree item = TREE_VALUE (node); + gcc_assert (TREE_PURPOSE (item)); + if (TREE_CODE (TREE_VALUE (item)) == INTEGER_CST) + continue; + + tree range = TREE_VALUE (item); + if (c_omp_numeric_ranges_always_overlap (range, new_range)) + { + error_at (num_range_loc, + "numeric range always overlaps with another " + "range"); + inform (EXPR_LOCATION (TREE_PURPOSE (item)), + "overlaps with this range"); + /* Do not add this range. */ + return; + } + } + tree item = build_tree_list (build_empty_stmt (num_range_loc), + new_range); + /* Leave PURPOSE unused for use by the caller of + c_parser_omp_parm_list. */ + *chain = build_tree_list (NULL_TREE, item); + }; + + if (lb_val > 0 && ub_val > 0) + { + gcc_assert (variadic_p + || (lb_val <= parm_count && ub_val <= parm_count)); + add_range_known (lb_val, ub_val); + } + else if (lb_val <= 0 && ub_val <= 0) + { + gcc_assert (variadic_p); + add_range_unknown (lb_val, true, ub_val, true); + } + /* Add the indices that will be specified for all well-formed calls to + the function. This lets us diagnose indices that were specified + (or rather, will be when the numeric range is expanded) multiple times + before the function is even called. We must adjust the literal bound + of the numeric range accordingly depending on how many indices we + add to prevent them from being specified again erroneously once the + range is expanded at the call site. + We can do this because we support expansion of unknown ranges + evaluating to an empty interval, as mentioned above in + add_range_unknown. */ + else if (lb_val > 0) + { + gcc_assert (variadic_p); + /* FIXME: Make sure to add a test where lb > parm_count, that + originally could break this realized that would break this + optimization. */ + /* In the case that UB refers to the last argument, we can assume all + non-variadic arguments between LB and the last non-variadic arg, + if any, will always be specified. */ + const int known_upper_bound = ub_val == 0 && lb_val <= parm_count + ? parm_count : lb_val; + add_range_known (lb_val, known_upper_bound); + add_range_unknown (known_upper_bound + 1, false, ub_val, true); + } + else if (ub_val > 0) + { + gcc_assert (variadic_p); + /* We can do this because numeric ranges are inclusive, any + well-formed call to this function will cause the range to evaluate + to include the literal index. */ + add_range_known (ub_val, ub_val); + add_range_unknown (lb_val, true, ub_val - 1, false); + } + else + gcc_unreachable (); + + } while (c_parser_next_token_is (parser, CPP_COMMA)); + return list; +} + + /* OpenACC 2.0: copy ( variable-list ) copyin ( variable-list ) @@ -27831,16 +28547,56 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) parens.require_close (parser); + const int parm_count = [&] () + { + tree parm = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); + int parm_count = 0; + while (parm != NULL_TREE && parm != void_list_node) + { + ++parm_count; + parm = TREE_CHAIN (parm); + } + gcc_assert (!parm || parm == void_list_node); + return parm == void_list_node ? parm_count : 0; + } (); /* IILE. */ + /* Do we care about non-variadic functions with 0 parameters? I don't think + we do, but lets handle for that case anyway, at least as long as we aren't + diagnosing for it. */ + const bool variadic_p = TYPE_ARG_TYPES (TREE_TYPE (fndecl)) == void_list_node + ? false : parm_count == 0; + tree append_args_tree = NULL_TREE; tree append_args_last; - vec<tree> adjust_args_list = vNULL; + hash_map<int_hash<int, -1, -2>, tree> adjust_args_idxs; bool has_match = false, has_adjust_args = false; location_t adjust_args_loc = UNKNOWN_LOCATION; location_t append_args_loc = UNKNOWN_LOCATION; location_t match_loc = UNKNOWN_LOCATION; - tree need_device_ptr_list = NULL_TREE; tree ctx = error_mark_node; + tree adjust_args_list = NULL_TREE; + auto append_adjust_args = [chain = &adjust_args_list] (tree node) mutable + { + gcc_assert (chain && *chain == NULL_TREE); + *chain = node; + chain = &TREE_CHAIN (node); + }; + + auto compare_ranges = [&] (tree item) + { + for (tree n2 = adjust_args_list; n2; n2 = TREE_CHAIN (n2)) + { + tree item2 = TREE_VALUE (n2); + if (TREE_CODE (TREE_VALUE (item2)) == INTEGER_CST) + continue; + else if (c_omp_numeric_ranges_always_overlap (TREE_VALUE (item2), + TREE_VALUE (item))) + /* Return the location. */ + return TREE_PURPOSE (item2); + } + return NULL_TREE; + }; + do { if (c_parser_next_token_is (parser, CPP_COMMA) @@ -27885,7 +28641,8 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) if (!parens.require_open (parser)) goto fail; - + /* This almost certainly causes problems with technically correct, but + insane functions that are variadic with no params. */ if (parms == NULL_TREE) parms = error_mark_node; @@ -27944,62 +28701,119 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) if (c_parser_next_token_is (parser, CPP_NAME) && c_parser_peek_2nd_token (parser)->type == CPP_COLON) { - const char *p - = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); + tree modifier_id = c_parser_peek_token (parser)->value; + const char *p = IDENTIFIER_POINTER (modifier_id); if (strcmp (p, "need_device_ptr") == 0 || strcmp (p, "nothing") == 0) { - c_parser_consume_token (parser); // need_device_ptr + c_parser_consume_token (parser); // need_device_ptr / nothing c_parser_consume_token (parser); // : loc = c_parser_peek_token (parser)->location; - tree list - = c_parser_omp_variable_list (parser, loc, OMP_CLAUSE_ERROR, - NULL_TREE); + const tree parm_list + = c_parser_omp_parm_list (parser, fndecl, parm_count); - tree arg; if (variant != error_mark_node) - for (tree c = list; c != NULL_TREE; c = TREE_CHAIN (c)) + for (tree next, n = parm_list; n != NULL_TREE; n = next) { - tree decl = TREE_PURPOSE (c); - location_t arg_loc = EXPR_LOCATION (TREE_VALUE (c)); - int idx; - for (arg = parms, idx = 0; arg != NULL; - arg = TREE_CHAIN (arg), idx++) - if (arg == decl) - break; - if (arg == NULL_TREE) + next = TREE_CHAIN (n); + TREE_CHAIN (n) = NULL_TREE; + TREE_PURPOSE (n) = modifier_id; + + tree item = TREE_VALUE (n); + const location_t item_loc + = EXPR_LOCATION (TREE_PURPOSE (item)); + if (TREE_CODE (TREE_VALUE (item)) == TREE_LIST) { - error_at (arg_loc, - "%qD is not a function argument", - decl); - goto fail; + /* Ranges are expanded by c_parser_omp_parm_list + in non-variadic functions. */ + gcc_assert (variadic_p); + if (tree dupe = compare_ranges (item)) + { + const location_t dupe_item_loc + = EXPR_LOCATION (dupe); + + error_at (item_loc, + "numeric range always overlaps with " + "previously specified numeric " + "range"); + inform (dupe_item_loc, + "previously specified here"); + } + else + append_adjust_args (n); + continue; } - if (adjust_args_list.contains (arg)) + gcc_assert (TREE_CODE (TREE_VALUE (item)) + == INTEGER_CST); + const int idx = tree_to_shwi (TREE_VALUE (item)); + /* Indices are 0 based, c_parser_omp_parm_list is + supposed to handle out of range indices. */ + gcc_assert (idx >= 0 + && (variadic_p || idx < parm_count)); + + if (tree *dupe = adjust_args_idxs.get (idx)) { - error_at (arg_loc, - "%qD is specified more than once", - decl); + const location_t prev_item_loc + = EXPR_LOCATION (TREE_PURPOSE (*dupe)); + /* Ensure the wording matches that in + c_parser_omp_parm_list. */ + error_at (item_loc, + "parameter list item specified more " + "than once"); + inform (prev_item_loc, + "previously specified here"); + /* FIXME: Don't fail, keep going. */ goto fail; } - if (strcmp (p, "need_device_ptr") == 0 - && TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE) + /* Unconditionally push idx so we don't emit the + following errors multiple times. */ + if (adjust_args_idxs.put (idx, item)) + gcc_unreachable (); + + if (strcmp (p, "need_device_ptr") == 0) { - error_at (loc, "%qD is not of pointer type", decl); - goto fail; + const tree parm = [&] () + { + if (idx >= parm_count) + return NULL_TREE; + int curr_idx = 0; + tree parm = parms; + while (parm != NULL_TREE) + { + if (curr_idx == idx) + return parm; + ++curr_idx; + parm = TREE_CHAIN (parm); + } + /* We already confirmed a parm exists in + c_parser_omp_parm_list. */ + gcc_unreachable (); + } (); /* IILE. */ + /* If we don't have an argument (because the index + is to a variadic arg) we can't check this. */ + if (parm + && TREE_CODE (TREE_TYPE (parm)) + != POINTER_TYPE) + { + error_at (DECL_SOURCE_LOCATION (parm), + "%qD is not of pointer type", parm); + inform (item_loc, "specified here"); + /* FIXME: Don't fail, keep going. */ + goto fail; + } + append_adjust_args (n); } - adjust_args_list.safe_push (arg); - if (strcmp (p, "need_device_ptr") == 0) + else if (strcmp (p, "nothing") == 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 + /* We only need to save parameter list items from a + clause with the nothing modifier if the function + is variadic. */ + if (variadic_p) + append_adjust_args (n); } + else + gcc_unreachable (); } } else @@ -28228,11 +29042,13 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) } if ((ctx != error_mark_node && variant != error_mark_node) - && (need_device_ptr_list || append_args_tree)) + && (adjust_args_list || append_args_tree)) { tree variant_decl = tree_strip_nop_conversions (variant); - tree t = build_tree_list (need_device_ptr_list, - NULL_TREE /* need_device_addr */); + tree t = build_tree_list (CHECKING_P + ? get_identifier ("omp adjust args idxs") + : NULL_TREE, + adjust_args_list); TREE_CHAIN (t) = append_args_tree; DECL_ATTRIBUTES (variant_decl) = tree_cons (get_identifier ("omp declare variant variant args"), t, diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index b40f791..f984940 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8105,6 +8105,8 @@ extern bool cp_check_omp_declare_reduction (tree); extern bool cp_check_omp_declare_mapper (tree); extern void finish_omp_declare_simd_methods (tree); extern tree cp_finish_omp_init_prefer_type (tree); +extern tree finish_omp_parm_list (tree, const_tree, int); +extern tree finish_omp_adjust_args (tree, const_tree, int); extern tree finish_omp_clauses (tree, enum c_omp_region_type); extern tree omp_instantiate_mappers (tree); extern tree push_omp_privatization_clauses (bool); diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 2b385d5..9db508f 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -8482,15 +8482,29 @@ omp_declare_variant_finalize_one (tree decl, tree attr) parm = DECL_CHAIN (parm); for (; parm; parm = DECL_CHAIN (parm)) vec_safe_push (args, forward_parm (parm)); - + /* The layout of these nodes are a mess, this function is generally very hard + to reason about because of it, this needs to be fixed. */ + const tree adjust_args_idxs = [&] () + { + const tree omp_variant_clauses_temp = TREE_CHAIN (TREE_CHAIN (chain)); + gcc_checking_assert (!omp_variant_clauses_temp + || TREE_PURPOSE (omp_variant_clauses_temp) + == get_identifier ("omp variant clauses temp")); + const tree adjust_args_idxs = omp_variant_clauses_temp + ? TREE_VALUE (omp_variant_clauses_temp) + : NULL_TREE; + gcc_checking_assert (!adjust_args_idxs + || TREE_PURPOSE (adjust_args_idxs) + == get_identifier ("omp adjust args idxs")); + return adjust_args_idxs; + } (); /* IILE. */ unsigned nappend_args = 0; - tree append_args_list = TREE_CHAIN (TREE_CHAIN (chain)); + const tree append_args_list + = adjust_args_idxs && TREE_CHAIN (adjust_args_idxs) + ? TREE_VALUE (TREE_CHAIN (adjust_args_idxs)) + : NULL_TREE;; if (append_args_list) { - append_args_list = TREE_VALUE (append_args_list); - append_args_list = (append_args_list && TREE_CHAIN (append_args_list) - ? TREE_VALUE (TREE_CHAIN (append_args_list)) - : NULL_TREE); for (tree t = append_args_list; t; t = TREE_CHAIN (t)) nappend_args++; if (nappend_args) @@ -8521,6 +8535,55 @@ omp_declare_variant_finalize_one (tree decl, tree attr) vec_safe_push (args, build_stub_object (TREE_TYPE (type))); } } + /* We assume the this parameter is included in the declaration, if it isn't + our parm count will be wrong. */ + gcc_assert (!DECL_IOBJ_MEMBER_FUNCTION_P (decl) + || is_this_parameter (DECL_ARGUMENTS (decl))); + struct bundled_parm_info + { + int count; + bool unexpanded_pack; + bool variadic; + }; + const bundled_parm_info parm_info = [&] () + { + bool pack = false; + int cnt = 0; + tree parm_t = TYPE_ARG_TYPES (TREE_TYPE (decl)); + while (parm_t && parm_t != void_list_node) + { + /* We can't tell how many parameters there are until all parameter + packs have been expanded. */ + if (PACK_EXPANSION_P (TREE_VALUE (parm_t))) + pack = true; + parm_t = TREE_CHAIN (parm_t); + ++cnt; + } + return bundled_parm_info{!pack ? cnt : 0, pack, parm_t == NULL_TREE}; + } (); /* IILE. */ + tree adjust_args_list = adjust_args_idxs ? TREE_VALUE (adjust_args_idxs) + : NULL_TREE; + if (adjust_args_list) + { + /* We currently treat a parm count of 0 as variadic. */ + adjust_args_list + = finish_omp_parm_list (adjust_args_list, + decl, + parm_info.variadic ? 0 : parm_info.count); + if (adjust_args_list != error_mark_node) + adjust_args_list + = finish_omp_adjust_args (adjust_args_list, + decl, + parm_info.variadic ? 0 : parm_info.count); + if (adjust_args_list == error_mark_node) + adjust_args_list = NULL_TREE; + } + /* This will also clear the adjust_args_list if there was an error. */ + if (adjust_args_idxs) + TREE_VALUE (adjust_args_idxs) = adjust_args_list; + + /* Maybe we should just return at this point if an unexpanded pack was + encountered, there isn't much else that we can do if there is. */ bool koenig_p = false; if (idk == CP_ID_KIND_UNQUALIFIED || idk == CP_ID_KIND_TEMPLATE_ID) @@ -8587,120 +8650,119 @@ omp_declare_variant_finalize_one (tree decl, tree attr) variant = cp_get_callee_fndecl_nofold (STRIP_REFERENCE_REF (variant)); input_location = save_loc; - if (variant) + if (!variant) { - bool fail; - const char *varname = IDENTIFIER_POINTER (DECL_NAME (variant)); - if (!nappend_args) - fail = !comptypes (TREE_TYPE (decl), TREE_TYPE (variant), - COMPARE_STRICT); - else - { - unsigned nbase_args = 0; - for (tree t = TYPE_ARG_TYPES (TREE_TYPE (decl)); - t && TREE_VALUE (t) != void_type_node; t = TREE_CHAIN (t)) - nbase_args++; - tree vargs, varg; - vargs = varg = TYPE_ARG_TYPES (TREE_TYPE (variant)); - for (unsigned i = 0; i < nbase_args && varg; - i++, varg = TREE_CHAIN (varg)) - vargs = varg; - for (unsigned i = 0; i < nappend_args && varg; i++) - varg = TREE_CHAIN (varg); - tree saved_vargs; - if (nbase_args) - { - saved_vargs = TREE_CHAIN (vargs); - TREE_CHAIN (vargs) = varg; - } - else - { - saved_vargs = vargs; - TYPE_ARG_TYPES (TREE_TYPE (variant)) = varg; - } - /* Skip assert check that TYPE_CANONICAL is the same. */ - fail = !comptypes (TREE_TYPE (decl), TREE_TYPE (variant), - COMPARE_STRUCTURAL); - if (nbase_args) - TREE_CHAIN (vargs) = saved_vargs; - else - TYPE_ARG_TYPES (TREE_TYPE (variant)) = saved_vargs; - varg = saved_vargs; - if (!fail && !processing_template_decl) - for (unsigned i = 0; i < nappend_args; - i++, varg = TREE_CHAIN (varg)) - if (!varg || !c_omp_interop_t_p (TREE_VALUE (varg))) - { - error_at (DECL_SOURCE_LOCATION (variant), - "argument %d of %qD must be of %<omp_interop_t%>", - nbase_args + i + 1, variant); - inform (EXPR_LOCATION (TREE_PURPOSE (append_args_list)), - "%<append_args%> specified here"); - break; - } - } + /* Don't error unless we are fully instantiated. */ + if (processing_template_decl) + return false; + error_at (varid_loc, "could not find variant declaration"); + return true; + } + + /* We should probably just error and return here if the two functions are not + both member functions or both free functions, but I don't want to move + the later error that checks for builtins ip right now. */ + auto emit_variant_type_error = [&] () + { + error_at (varid_loc, "variant %qD and base %qD have incompatible " + "types", variant, decl); + }; + /* Unlike below, COMPARE_STRICT is fine here. */ + if (!nappend_args + && !comptypes (TREE_TYPE (decl), TREE_TYPE (variant), COMPARE_STRICT)) + { + error_at (varid_loc, "variant %qD and base %qD have incompatible " + "types", variant, decl); + return true; + } + if (nappend_args) + { + const unsigned nbase_parms = parm_info.count; + { + tree t = TREE_CHAIN (TREE_CHAIN (chain)); + tree append_args_node = TREE_CHAIN (TREE_VALUE (t)); + /* Add the number of parameters once we know how many there are, for + now just wait until we are fully instantiated to keep it simple. */ + if (append_args_node && !processing_template_decl) + TREE_PURPOSE (append_args_node) + = build_int_cst (integer_type_node, nbase_parms); + } + /* This is where appended interop parms start, we need to remove them + temporarily to compare the function types. */ + tree *const last_regular_parm_chain = [&] () + { + if (!nbase_parms) + return &TYPE_ARG_TYPES (TREE_TYPE (variant)); + /* Return a pointer to the last regular parm's chain, subtract 1 from + nbase_parms so we don't iterate past it. */ + tree last_regular_parm + = chain_index (nbase_parms - 1, + TYPE_ARG_TYPES (TREE_TYPE (variant))); + return &TREE_CHAIN (last_regular_parm); + } (); /* IILE. */ + + /* Go past the added interop parms to find the first hidden parm, or the + end of the list of parms, this can be NULL_TREE or void_list_node. */ + tree first_hidden_parm = chain_index (nappend_args, + *last_regular_parm_chain); + tree interop_parms_start = *last_regular_parm_chain; + *last_regular_parm_chain = first_hidden_parm; + /* Skip assert check that TYPE_CANONICAL is the same, use + COMPARE_STRUCTURAL, not COMPARE_STRICT. */ + const bool fail = !comptypes (TREE_TYPE (decl), TREE_TYPE (variant), + COMPARE_STRUCTURAL); + /* Return the variant back to normal, even if the comparison failed. */ + *last_regular_parm_chain = interop_parms_start; + if (fail) { - error_at (varid_loc, "variant %qD and base %qD have incompatible " - "types", variant, decl); + emit_variant_type_error (); return true; } - if (fndecl_built_in_p (variant) - && (startswith (varname, "__builtin_") - || startswith (varname, "__sync_") - || startswith (varname, "__atomic_"))) - { - error_at (varid_loc, "variant %qD is a built-in", variant); - return true; - } - else - { - tree construct - = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT); - omp_mark_declare_variant (match_loc, variant, construct); - 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) - { - if (DECL_NONSTATIC_MEMBER_P (variant) - && TREE_VALUE (adjust_args_list)) - { - /* Shift arg position for the added 'this' pointer. */ - /* Handle need_device_ptr */ - for (tree t = TREE_PURPOSE (TREE_VALUE (adjust_args_list)); - t; t = TREE_CHAIN (t)) - TREE_VALUE (t) - = build_int_cst (TREE_TYPE (t), - tree_to_uhwi (TREE_VALUE (t)) + 1); - } - if (DECL_NONSTATIC_MEMBER_P (variant) && append_args_list) - { - /* Shift likewise the number of args after which the - interop object should be added. */ - tree nargs = TREE_CHAIN (TREE_VALUE (adjust_args_list)); - TREE_PURPOSE (nargs) - = build_int_cst (TREE_TYPE (nargs), - tree_to_uhwi (TREE_PURPOSE (nargs)) + 1); - } - for (tree t = append_args_list; t; t = TREE_CHAIN (t)) - TREE_VALUE (t) - = cp_finish_omp_init_prefer_type (TREE_VALUE (t)); - DECL_ATTRIBUTES (variant) = tree_cons ( - get_identifier ("omp declare variant variant args"), - TREE_VALUE (adjust_args_list), DECL_ATTRIBUTES (variant)); - } - } + tree interop_parm = interop_parms_start; + if (!processing_template_decl) + for (unsigned i = 0; i < nappend_args; i++) + { + if (!interop_parm + || !c_omp_interop_t_p (TREE_VALUE (interop_parm))) + { + error_at (DECL_SOURCE_LOCATION (variant), + "argument %d of %qD must be of %<omp_interop_t%>", + nbase_parms + i + 1, variant); + inform (EXPR_LOCATION (TREE_PURPOSE (append_args_list)), + "%<append_args%> specified here"); + break; + } + interop_parm = TREE_CHAIN (interop_parm); + } } - else if (!processing_template_decl) + const char *varname = IDENTIFIER_POINTER (DECL_NAME (variant)); + if (fndecl_built_in_p (variant) + && (startswith (varname, "__builtin_") + || startswith (varname, "__sync_") + || startswith (varname, "__atomic_"))) { - error_at (varid_loc, "could not find variant declaration"); + error_at (varid_loc, "variant %qD is a built-in", variant); return true; } + tree construct + = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT); + omp_mark_declare_variant (match_loc, variant, construct); + if (!omp_context_selector_matches (ctx, NULL_TREE, false)) + return true; + TREE_PURPOSE (TREE_VALUE (attr)) = variant; + + // Prepend adjust_args list to variant attributes + if (adjust_args_idxs != NULL_TREE) + { + for (tree t = append_args_list; t; t = TREE_CHAIN (t)) + TREE_VALUE (t) = cp_finish_omp_init_prefer_type (TREE_VALUE (t)); + DECL_ATTRIBUTES (variant) + = tree_cons (get_identifier ("omp declare variant variant args"), + adjust_args_idxs, DECL_ATTRIBUTES (variant)); + } + return false; } diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index f38a327..eb0133b 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -2705,7 +2705,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 &, tree); + (cp_parser *, cp_declarator *, tree &); static tree cp_parser_declarator_id (cp_parser *, bool); static tree cp_parser_type_id @@ -2740,7 +2740,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, tree); + (cp_parser *, tree); static tree cp_parser_late_parsing_oacc_routine (cp_parser *, tree); @@ -25066,7 +25066,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, params); + requires_clause); cp_finalize_omp_declare_simd (parser, &odsd); @@ -25932,7 +25932,7 @@ parsing_function_declarator () static tree cp_parser_late_return_type_opt (cp_parser *parser, cp_declarator *declarator, - tree &requires_clause, tree parms) + tree &requires_clause) { cp_token *token; tree type = NULL_TREE; @@ -25988,8 +25988,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, - parms); + = cp_parser_late_parsing_omp_declare_simd (parser, + declarator->attributes); if (oacc_routine_p) declarator->attributes = cp_parser_late_parsing_oacc_routine (parser, @@ -39975,6 +39975,417 @@ cp_parser_omp_var_list (cp_parser *parser, enum omp_clause_code kind, tree list, return list; } +/* Parse an OpenMP parameter-list. + parameter-list: + parameter-list-item[, parameter-list-item [, ...]] + + parameter-list-item: + named parameter list item + parameter index (1 based) + numeric-range + + numeric-range: + [bound]:[bound] + + bound: + index-expr + omp_num_args[±logical_offset] + + A named parameter list item is the name of a parameter. A parameter index + is a positive integer literal that is the 1 based index of a parameter. + A numeric-range is a pair of bounds of the form lb:ub, the values of each + bound form a closed interval of parameter indices. Bounds can be literal or + relative. An index-expr is a non-negative integer constant-expression that + is the value of a literal bound. The special identifier omp_num_args is + equal to the number of arguments passed to the function at the call site, + including the number of varargs. Optionally, a plus or minus with a + logical_offset may follow omp_num_args, logical_offset is a non-negative + integer constant-expression. A bound formed with omp_num_args is a relative + bound. If a bound is omitted, a default value is used. The default value + of lb is as if 1 were specified, the default value of ub is as if + omp_num_args were specified. + + Each parameter-list-item is stored in a TREE_LIST. The PURPOSE is for + general use and left NULL_TREE here, and the item is stored in the VALUE. + An item is a TREE_LIST, the PURPOSE is an expression with the location of + the list item, and the VALUE is a representation of the item. + Each parameter-list-item is stored in a TREE_LIST node VALUE. The PURPOSE + is unused, and the VALUE is the item-repr. + + Node - PUPOSE: NULL_TREE + - VALUE: item-with-location + item-with-location - PURPOSE: expr-with-location + - VALUE: item-repr + + An item-repr is a PARM_DECL, a NOP_EXPR, or a TREE_LIST. A PARM_DECL is a + named parameter list item. A NOP_EXPR is the unadjusted 1-based parameter + index. A TREE_LIST is a numeric-range where its PURPOSE is a TREE_LIST + representing the lb, and its VALUE is a TREE_LIST representing the ub. + + item-repr + PARM_DECL - parameter name + NOP_EXPR - parameter index (1 based) + TREE_LIST - PURPOSE: TREE_LIST (lb) + - VALUE: TREE_LIST (ub) + + lb and ub are a TREE_LIST of the following form; + TREE_LIST - PURPOSE: relative bound marker (NULL_TREE if literal) + - VALUE: expr-value + + This function strictly handles a parameter-list, it does not parse clause + modifiers, or parenthesis other than in the expr of a numeric range. + + If a diagnostic is issued for a list item, it is not appened to the list and + parsing continues. Returns NULL_TREE if no valid list items are parsed. */ + +static tree +cp_parser_omp_parm_list (cp_parser *parser) +{ + tree list = NULL_TREE; + auto append_to_list = [chain = &list] (tree arg, location_t loc) mutable + { + gcc_assert (*chain == NULL_TREE); + *chain = build_tree_list (NULL_TREE, + build_tree_list (build_empty_stmt (loc), arg)); + chain = &TREE_CHAIN (*chain); + }; + + auto tok_terminates_item_p = [] (const cp_token *tok) + { + return tok->type == CPP_COMMA + || tok->type == CPP_CLOSE_PAREN; + }; + /* The first list item is (obviously) not preceded by a comma. */ + goto first_element; + do + { + /* Consume the comma. */ + cp_lexer_consume_token (parser->lexer); + first_element: + + cp_token *const tok = cp_lexer_peek_token (parser->lexer); + + /* OpenMP 6.0 (162:29-34) + A parameter list item can be one of the following: + • A named parameter list item; + • The position of a parameter in a parameter specification specified + by a positive integer, where 1 represents the first parameter; or + • A parameter range specified by lb : ub where both lb and ub must + be an expression of integer OpenMP type with the constant property + and the positive property. + + The spec does not support arbitrary expression outside of a numeric + range. In theory they could be supported as a parameter index, but + for now we do not support that case. */ + + /* If we don't see a comma or close paren this can't be a named parameter + list item or a parameter index, it can only be a numeric range. */ + if (!tok_terminates_item_p (cp_lexer_peek_nth_token (parser->lexer, 2)) + /* Or this edge case, there is a default lower bound. */ + || tok->type == CPP_COLON) + /* Early exit, numeric range case handled below. */; + else if (tok->type == CPP_NAME) + { + if (strcmp (IDENTIFIER_POINTER (tok->u.value), "omp_num_args") == 0) + { + error_at (tok->location, "%<omp_num_args%> may only be used at " + "the start of a numeric range bound"); + cp_lexer_consume_token (parser->lexer); + continue; + } + /* This might not be the right way to do this, we might want to use + cp_parser_lookup_name_simple instead. */ + tree parm = lookup_name (tok->u.value, + LOOK_where::BLOCK, + LOOK_want::NORMAL); + if (parm && TREE_CODE (parm) == PARM_DECL) + { + if (DECL_PACK_P (parm)) + { + /* In theory we could just consider every element of the pack + as being specified, the spec does not say what to do + though. */ + sorry_at (tok->location, + "parameter packs are not supported as an OpenMP " + "named parameter list item"); + inform (DECL_SOURCE_LOCATION (parm), + "declared as a pack here"); + } + else + append_to_list (parm, tok->location); + } + else + { + /* FIXME: Nice diagnostic, potentially using + cp_parser_name_lookup_error. */ + error_at (tok->location, + "%qs is not a function parameter", + IDENTIFIER_POINTER (tok->u.value)); + } + cp_lexer_consume_token (parser->lexer); + continue; + } + else if (tok->type == CPP_NUMBER) + { + if (wi::to_widest (tok->u.value) <= 0) + { + error_at (tok->location, + "parameter indices in an OpenMP " + "parameter list must be positive"); + } + else if (wi::to_widest (tok->u.value) > INT_MAX) + error_at (tok->location, "parameter index is too big"); + else + { + /* Don't adjust here, we can't finalize these until we know if we + are in a member function or not. We can probably hack this to + find out in here, but it belongs in finish_omp_parm_list, not + here. + FIXME: We have to come up with a better way of transporting + these and marking them as unfinalized. Wrapping in a NOP is + really quite bad. */ + tree cst = build_int_cst (integer_type_node, + tree_to_shwi (tok->u.value)); + append_to_list (build_nop (integer_type_node, cst), + tok->location); + } + cp_lexer_consume_token (parser->lexer); + continue; + } + else + { + gcc_checking_assert (tok_terminates_item_p + (cp_lexer_peek_nth_token (parser->lexer, 2))); + cp_parser_error (parser, "expected unqualified-id, " + "integer, or expression"); + cp_lexer_consume_token (parser->lexer); + continue; + } + /* We have a numeric range or something ill formed now, this can be + an arbitrary expression. */ + + /* Empty bounds are delimited differently for lower and upper bounds, + handle them without calling parse_bound. */ + auto parse_bound = [&] () -> tree + { + location_t bound_start + = cp_lexer_peek_token (parser->lexer)->location; + enum omp_num_args + { + num_args_none, + num_args_plus, + num_args_minus, + num_args_no_offset + }; + /* (OpenMP 6.0, 162:35-37) + In both lb and ub, an expression using omp_num_args, that enables + identification of parameters relative to the last argument of the + call, can be used with the form: + omp_num_args [± logical_offset] */ + const omp_num_args parsed_omp_num_args = [&] () + { + cp_token *tok = cp_lexer_peek_token (parser->lexer); + if (tok->type == CPP_NAME + && strcmp (IDENTIFIER_POINTER (tok->u.value), "omp_num_args") + == 0) + { + /* Consume omp_num_args. */ + cp_lexer_consume_token (parser->lexer); + cp_token *op_tok = cp_lexer_peek_token (parser->lexer); + if (op_tok->type == CPP_PLUS) + { + cp_lexer_consume_token (parser->lexer); + return num_args_plus; + } + else if (op_tok->type == CPP_MINUS) + { + cp_lexer_consume_token (parser->lexer); + return num_args_minus; + } + return num_args_no_offset; + } + else + return num_args_none; + } (); /* IILE. */ + /* If there was omp_num_args but no operator an expr is not + permitted, we are finished with this bound. */ + if (parsed_omp_num_args == num_args_no_offset) + { + tree cst = build_zero_cst (integer_type_node); + /* I hate this hack. We don't know if we are parsing a lb or ub, + so even though we know it's value we have to wait until later + to finalize it. */ + return build_tree_list (get_identifier ("omp num args plus"), + build1_loc (bound_start, + NOP_EXPR, + integer_type_node, + cst)); + } + const bool saved_flag = parser->colon_corrects_to_scope_p; + /* Disable this diagnostic to parse id:id cases such as + 'V:omp_num_args' where V is a constant expression variable. */ + parser->colon_corrects_to_scope_p = false; + /* Function arguments are considered an assignment-expression by the + C++ standard, it seems to me that those semantics match what we + want from an expr in lb or ub. */ + cp_expr expr = cp_parser_assignment_expression (parser); + parser->colon_corrects_to_scope_p = saved_flag; + + if (!expr || expr == error_mark_node) + return error_mark_node; + + auto finish_bound_expr = [&parsed_omp_num_args] (cp_expr expr_in) + { + const location_t loc = expr_in.get_location (); + tree expr = expr_in.get_value (); + /* Try to fold early if expr is not dependent. I'm pretty sure + this should be manifestly constant-evaluated. We require a + constant here, let fold_non_dependent_expr complain, but + handle everything else in finish_omp_parm_list. */ + if (!value_dependent_expression_p (expr)) + { + expr = fold_non_dependent_expr (expr, + tf_warning_or_error, + true); + if (!expr || error_operand_p (expr)) + { + if (parsed_omp_num_args != num_args_none) + error_at (loc, "logical offset of a bound must " + "be a constant expression"); + else + error_at (loc, "expression of a bound must be a " + "constant expression"); + return error_mark_node; + } + } + /* We need a way to signal that an expr has not been adjusted, + the best way I came up with is checking if it is an + INTEGER_CST, but if we already have an INTEGER_CST at this + point, what now? Wrap it in a nop, that's what. */ + if (TREE_CODE (expr) == INTEGER_CST) + return build1_loc (loc, NOP_EXPR, TREE_TYPE (expr), expr); + /* We still need this for things like template parameters. */ + auto maybe_force_wrap_with_location = [&] () + { + if (!expr + || error_operand_p (expr) + || CAN_HAVE_LOCATION_P (expr)) + return expr; + /* Pulled from maybe_wrap_with_location. */ + const tree_code code + = ((CONSTANT_CLASS_P (expr) + && TREE_CODE (expr) != STRING_CST) + || (TREE_CODE (expr) == CONST_DECL + && !TREE_STATIC (expr))) + ? NON_LVALUE_EXPR : VIEW_CONVERT_EXPR; + tree wrap = build1_loc (loc, code, TREE_TYPE (expr), expr); + EXPR_LOCATION_WRAPPER_P (wrap) = 1; + return wrap; + }; + return maybe_force_wrap_with_location (); + }; + + gcc_assert (parsed_omp_num_args < num_args_no_offset); + switch (parsed_omp_num_args) + { + case num_args_none: + /* NULL_TREE represents literal. */ + return build_tree_list (NULL_TREE, + finish_bound_expr (expr)); + case num_args_plus: + return build_tree_list (get_identifier ("omp num args plus"), + finish_bound_expr (expr)); + case num_args_minus: + return build_tree_list (get_identifier ("omp num args minus"), + finish_bound_expr (expr)); + case num_args_no_offset: + /* Handled above. */ + default: + gcc_unreachable (); + } + gcc_unreachable (); + }; + /* I'm not happy with the state of diagnostics here, but I'm not sure how + to fix it so it's best to wait to see which cases end up giving really + unclear errors. */ + location_t num_range_loc_begin + = cp_lexer_peek_token (parser->lexer)->location; + /* As stated above, empty bounds are handled here. */ + tree lower_bound = cp_lexer_next_token_is (parser->lexer, CPP_COLON) + ? NULL_TREE : parse_bound (); + /* I wish we could error here saying that we expect an unqualified-id, + an integer, or an expression. Parsing the expression emits the error + right away though. Maybe we can do some tentative parsing? */ + if (lower_bound && error_operand_p (lower_bound)) + { + cp_parser_skip_to_closing_parenthesis (parser, + /*recovering=*/true, + /*or_comma=*/true, + /*consume_paren=*/false); + continue; + } + /* Tokens get consumed by parse_bound. */ + if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON)) + { + /* lower_bound can only be null if the next token was a colon. */ + gcc_assert (lower_bound && !error_operand_p (lower_bound)); + const cp_token *const next_tok = cp_lexer_peek_token (parser->lexer); + + cp_parser_error (parser, "expected %<:%>"); + if (tok_terminates_item_p (next_tok)) + { + const location_t loc = make_location (num_range_loc_begin, + num_range_loc_begin, + input_location); + inform (loc, "an expression is only allowed in a numeric range"); + } + /* Do not consume the close paren, this function does not handle + that part of the clause. */ + cp_parser_skip_to_closing_parenthesis (parser, + /*recovering=*/true, + /*or_comma=*/true, + /*consume_paren=*/false); + continue; + } + location_t colon_loc = cp_lexer_consume_token (parser->lexer)->location; + tree upper_bound = tok_terminates_item_p + (cp_lexer_peek_token (parser->lexer)) + ? NULL_TREE : parse_bound (); + + /* I think we are supposed to have some sort of diagnostic here, I'm just + not sure what it should be. */ + if (error_operand_p (lower_bound) || error_operand_p (upper_bound)) + continue; + + location_t num_range_loc_end + = upper_bound ? EXPR_LOCATION (TREE_VALUE (upper_bound)) : colon_loc; + + auto build_default_bound = [] (tree num_args_marker, int val) + { + /* Unfortunately, we can't assume what the final value will be + because we don't know if we are in a member function or not. */ + tree value = build_nop (integer_type_node, + build_int_cst (integer_type_node, val)); + return build_tree_list (num_args_marker, value); + }; + static constexpr int lb_default = 1; + /* Internally, 0 + omp_num_args refers to the last arg. */ + static constexpr int ub_default = 0; + if (!lower_bound) + lower_bound = build_default_bound (NULL_TREE, lb_default); + if (!upper_bound) + upper_bound + = build_default_bound (get_identifier ("omp num args plus"), + ub_default); + + append_to_list (build_tree_list (lower_bound, upper_bound), + make_location (num_range_loc_begin, + num_range_loc_begin, + num_range_loc_end)); + } while (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)); + return list; +} + /* OpenACC 2.0: copy ( variable-list ) copyin ( variable-list ) @@ -51803,7 +52214,7 @@ cp_parser_omp_dispatch (cp_parser *parser, cp_token *pragma_tok) static tree cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, - tree attrs, tree parms) + tree attrs) { matching_parens parens; if (!parens.require_open (parser)) @@ -51863,13 +52274,27 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, tree append_args_tree = NULL_TREE; tree append_args_last = NULL_TREE; - vec<tree> adjust_args_list = vNULL; bool has_match = false, has_adjust_args = false; location_t adjust_args_loc = UNKNOWN_LOCATION; location_t append_args_loc = UNKNOWN_LOCATION; - tree need_device_ptr_list = NULL_TREE; + tree ctx = NULL_TREE; + tree adjust_args_list = NULL_TREE; + auto append_adjust_args + = [chain = &adjust_args_list] (tree list, tree clause_modifier) mutable + { + gcc_assert (chain && *chain == NULL_TREE); + *chain = list; + /* Just stick them all together in one list and process them all + at once later. */ + for (tree node = list; node; node = TREE_CHAIN (node)) + { + TREE_PURPOSE (node) = clause_modifier; + chain = &TREE_CHAIN (node); + } + }; + do { if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA) @@ -51964,78 +52389,48 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, { const char *p = IDENTIFIER_POINTER (adjust_op_tok->u.value); if (strcmp (p, "need_device_ptr") == 0 + || strcmp (p, "need_device_addr") == 0 || strcmp (p, "nothing") == 0) { cp_lexer_consume_token (parser->lexer); // need_device_ptr cp_lexer_consume_token (parser->lexer); // : - 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 list = cp_parser_omp_parm_list (parser); + if (list && list != error_mark_node) + /* It should be fine to just use the identifier node. */ + append_adjust_args (list, adjust_op_tok->u.value); + else { - tree decl = TREE_PURPOSE (c); - location_t arg_loc = EXPR_LOCATION (TREE_VALUE (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)) - { - 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 - } + /* Do we need a specific diagnostic here? + I don't like failing here, we should be skipping to + a close paren and continuing. */ + goto fail; } } else { error_at (adjust_op_tok->location, "expected %<nothing%> or %<need_device_ptr%>"); + /* We should be trying to recover here instead of immediately + failing, skipping to close paren and continuing. */ goto fail; } } else { + /* We should be trying to recover here instead of immediately + failing, skipping to close paren and continuing. */ error_at (adjust_op_tok->location, "expected %<nothing%> or %<need_device_ptr%> followed " "by %<:%>"); goto fail; } + /* cp_parser_omp_var_list_no_open used to handle this, we don't use + it anymore though. */ + if (!parens.require_close (parser)) + /* We should be trying to recover here instead of immediately + failing, I'm not sure what we skip to though. */ + goto fail; } else if (ccode == append_args) { @@ -52099,14 +52494,12 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, cp_lexer_consume_token (parser->lexer); // ',' } while (true); - int nbase_args = 0; - for (tree t = parms; - t && TREE_VALUE (t) != void_type_node; t = TREE_CHAIN (t)) - nbase_args++; - /* Store as purpose = arg number after which to append - and value = list of interop items. */ - append_args_tree = build_tree_list (build_int_cst (integer_type_node, - nbase_args), + /* This is where the number of args used to be inserted, it still + gets put here by omp_declare_variant_finalize_one once we know how + many parameters there are. Ideally we should refactor the way we + pass this data around, once we do that we can remove this bit from + here. Until then, leave it be. */ + append_args_tree = build_tree_list (NULL_TREE, append_args_tree); } } while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)); @@ -52136,11 +52529,17 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, // 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. - tree t = build_tree_list (need_device_ptr_list, - NULL_TREE /* need_device_addr */); + tree debug_idxs_node + = CHECKING_P ? get_identifier ("omp adjust args idxs") + : NULL_TREE; + tree t = build_tree_list (debug_idxs_node, + adjust_args_list); TREE_CHAIN (t) = append_args_tree; + tree debug_tail_node + = CHECKING_P ? get_identifier ("omp variant clauses temp") + : NULL_TREE; TREE_VALUE (attrs) = chainon (TREE_VALUE (attrs), - build_tree_list ( NULL_TREE, t)); + build_tree_list (debug_tail_node, t)); } } @@ -52153,8 +52552,7 @@ 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, - tree parms) +cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs) { struct cp_token_cache *ce; cp_omp_declare_simd_data *data = parser->omp_declare_simd; @@ -52198,7 +52596,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, parms); + = cp_finish_omp_declare_variant (parser, pragma_tok, attrs); } cp_parser_pop_lexer (parser); } @@ -52330,7 +52728,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, parms); + attrs); } gcc_assert (parser->lexer != lexer); vec_safe_truncate (lexer->buffer, 0); diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 54f1b3f..d794083 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -12151,36 +12151,159 @@ tsubst_attribute (tree t, tree *decl_p, tree args, ++cp_unevaluated_operand; tree varid = tsubst_expr (TREE_PURPOSE (val), args, complain, in_decl); --cp_unevaluated_operand; - tree chain = TREE_CHAIN (val); + tree chain = copy_list (TREE_CHAIN (val)); location_t match_loc = cp_expr_loc_or_input_loc (TREE_PURPOSE (chain)); tree ctx = copy_list (TREE_VALUE (val)); - tree append_args_list = TREE_CHAIN (TREE_CHAIN (chain)); - if (append_args_list - && TREE_VALUE (append_args_list) - && TREE_CHAIN (TREE_VALUE (append_args_list))) - { - append_args_list = TREE_VALUE (append_args_list); - append_args_list = TREE_VALUE (TREE_CHAIN (append_args_list)); - for (; append_args_list; - append_args_list = TREE_CHAIN (append_args_list)) - { - tree pref_list = TREE_VALUE (append_args_list); - if (pref_list == NULL_TREE || TREE_CODE (pref_list) != TREE_LIST) + /* These asserts may seem strange but the layout of this attribute is + really difficult to grok and remember. They should be left in until + we refactor the layout of the stored nodes. */ + gcc_assert (TREE_CHAIN (chain)); + /* These nodes were copied by copy_list above, don't copy it again. */ + tree omp_variant_clauses = TREE_CHAIN (TREE_CHAIN (chain)); + gcc_checking_assert (!omp_variant_clauses + || TREE_PURPOSE (omp_variant_clauses) + == get_identifier ("omp variant clauses temp")); + tree adjust_args_idxs = NULL_TREE; + if (omp_variant_clauses) + { + gcc_assert (TREE_VALUE (omp_variant_clauses)); + adjust_args_idxs = copy_node (TREE_VALUE (omp_variant_clauses)); + gcc_assert (adjust_args_idxs); + gcc_checking_assert (TREE_PURPOSE (adjust_args_idxs) + == get_identifier ("omp adjust args idxs")); + /* copy_node doesn't copy the CHAIN. */ + if (adjust_args_idxs) + { + if (TREE_CHAIN (TREE_VALUE (omp_variant_clauses))) + TREE_CHAIN (adjust_args_idxs) + = copy_node (TREE_CHAIN (TREE_VALUE (omp_variant_clauses))); + TREE_VALUE (omp_variant_clauses) = adjust_args_idxs; + } + } + if (adjust_args_idxs + && TREE_CHAIN (adjust_args_idxs)) + { + /* This only needs to be copied if node PURPOSE is NULL_TREE, or if + there is a dependent prefer type node. It's hard to determine + this though, so don't try to handle it conditionally for now. */ + tree append_args_node = TREE_CHAIN (adjust_args_idxs); + /* PURPOSE holds the count of real args, it doesn't need to be copied + because it will just be replaced if it needs to be changed. + VALUE holds the list of append_args. */ + append_args_node = copy_node (append_args_node); + /* It's too hard to figure out if we have anything dependent, + unconditionally copy this list. */ + tree append_args_head = copy_list (TREE_VALUE (append_args_node)); + for (tree n = append_args_head; n != NULL_TREE; n = TREE_CHAIN (n)) + { + tree pref_list = TREE_VALUE (n); + if (pref_list == NULL_TREE + || TREE_CODE (pref_list) != TREE_LIST) + /* Not a pref_list. */ continue; - tree fr_list = TREE_VALUE (pref_list); + tree fr_list = copy_node (TREE_VALUE (pref_list)); int len = TREE_VEC_LENGTH (fr_list); + /* Track if substitution occurs. + I'm not really sure that this even works the way I hope it + does, really I'm pretty sure tsubst_expr will basically always + return some sort of copy. The proper solution is probably + marking the list as dependent during parsing. */ + bool substituted = false; for (int i = 0; i < len; i++) { - tree *fr_expr = &TREE_VEC_ELT (fr_list, i); - /* Preserve NOP_EXPR to have a location. */ - if (*fr_expr && TREE_CODE (*fr_expr) == NOP_EXPR) - TREE_OPERAND (*fr_expr, 0) - = tsubst_expr (TREE_OPERAND (*fr_expr, 0), args, complain, - in_decl); - else - *fr_expr = tsubst_expr (*fr_expr, args, complain, in_decl); + tree elt = TREE_VEC_ELT (fr_list, i); + if (!elt) + continue; + tree expr = TREE_CODE (elt) == NOP_EXPR + ? TREE_OPERAND (elt, 0) + : elt; + tree new_expr = tsubst_expr (expr, args, complain, in_decl); + if (new_expr != expr) + { + substituted = true; + if (TREE_CODE (elt) == NOP_EXPR) + { + tree copied_nop = copy_node (elt); + TREE_OPERAND (copied_nop, 0) = new_expr; + TREE_VEC_ELT (fr_list, i) = copied_nop; + } + else + TREE_VEC_ELT (fr_list, i) = new_expr; + } + } + if (substituted) + { + pref_list = copy_node (pref_list); + TREE_VALUE (pref_list) = fr_list; + /* This node gets mutated in cp_finish_omp_init_prefer_type + if fr_list is dependent, it needs to be copied. */ + TREE_PURPOSE (pref_list) + = copy_node (TREE_PURPOSE (pref_list)); + TREE_VALUE (n) = pref_list; + } + } + TREE_VALUE (append_args_node) = append_args_head; + TREE_CHAIN (adjust_args_idxs) = append_args_node; + } + gcc_assert (TREE_CHAIN (chain)); + + tree adjust_args_list = adjust_args_idxs + ? TREE_VALUE (adjust_args_idxs) + : NULL_TREE; + + if (adjust_args_list && ATTR_IS_DEPENDENT (adjust_args_list)) + { + tree copied = copy_list (adjust_args_list); + /* Substitute numeric ranges, we also need to copy ranges with + relative bounds, in theory it's possible for them to get expanded + while one bound is still dependent. */ + for (tree n = copied; n; n = TREE_CHAIN (n)) + { + tree item = TREE_VALUE (n); + const tree_code code = TREE_CODE (TREE_VALUE (item)); + gcc_assert (code == PARM_DECL + || code == INTEGER_CST + || code == TREE_LIST); + if (code == TREE_LIST) + { + tree range = TREE_VALUE (item); + gcc_assert (TREE_CODE (range) == TREE_LIST); + auto tsubst_bound = [&] (tree bound) + { + gcc_assert (bound != NULL_TREE); + if (bound == error_mark_node + || !ATTR_IS_DEPENDENT (bound)) + { + /* As above, copy the bound if it is relative. */ + if (TREE_PURPOSE (bound) != NULL_TREE) + return copy_node (bound); + else + return bound; + } + tree expr = TREE_VALUE (bound); + tree subst_expr + = tsubst_expr (expr, args, complain, in_decl); + gcc_assert (subst_expr != expr); + tree new_bound = build_tree_list (TREE_PURPOSE (bound), + subst_expr); + return new_bound; + }; + tree lb = tsubst_bound (TREE_PURPOSE (range)); + tree ub = tsubst_bound (TREE_VALUE (range)); + /* Only build a new range if substitution occured. */ + if (lb != TREE_PURPOSE (range) + || ub != TREE_VALUE (range)) + { + tree new_range = build_tree_list (lb, ub); + /* We don't need to copy the purpose, it just holds + a location. */ + TREE_VALUE (n) = build_tree_list (TREE_PURPOSE (item), + new_range); + } } } + TREE_VALUE (adjust_args_idxs) = copied; + TREE_VALUE (omp_variant_clauses) = adjust_args_idxs; } for (tree tss = ctx; tss; tss = TREE_CHAIN (tss)) { diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 770872a..32d4511 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -7922,6 +7922,731 @@ cp_finish_omp_init_prefer_type (tree pref_type) } return t; } +/* LIST is a TREE_LIST, its TREE_PURPOSE is not used here, but its (possibly + NULL_TREE) value is propegated to any new nodes derived from the node. + Its TREE_VALUE is a TREE_LIST representing an OpenMP parameter-list-item. + Its TREE_PURPOSE contains an expr storing the location of the item and + TREE_VALUE contains the representation of the item. It is a NOP_EXPR, + INTEGER_CST, PARM_DECL, or TREE_LIST, this function possibly mutates it, it + is not preserved. DECL is the function the clause/clauses that LIST is + specified in is applied to. PARM_COUNT is the number of parameters, unless + the function has a parameter pack, or if the function is variadic, then + PARM_COUNT is 0. Functions with an empty parameter list are not handled + here. + + Finalize each element in LIST, diagnose non-unique elements, and mutate + the original element appending them to a new list. The finalized parameter + indices are 0 based in contrast to OpenMP specifying 1 based parameter + indices, that adjustment is done here. NOP_EXPR elements require adjustment + from a 1 based index to a 0 based index. INTEGER_CST elements are finalized + parameter indices, but are still used for diagnosing duplicate elements. + PARM_DECL elements are switched out for their corresponding 0 based index, + provided it can be determined. TREE_LIST represents a numeric range. If + the number of parameters is known and DECL is non-variadic, relative bounds + are folded into literal bounds. If both bounds are non-relative the numeric + range is expanded, replacing the TREE_LIST with N INTEGER_CST nodes for each + index in the numeric range. If DECL is variadic, numeric ranges with + a relative bound are represented the same as in c_parser_omp_parm_list + so gimplify.cc:modify_call_for_omp_dispatch can handle them the same way. + + Returns TREE_LIST or error_mark_node if each elt was invalid. */ + +tree +finish_omp_parm_list (tree list, const_tree decl, const int parm_count) +{ + gcc_assert (list && decl); + const tree parms = DECL_ARGUMENTS (decl); + /* We assume that the this parameter is included in parms, make sure this is + still true. */ + gcc_assert (!DECL_IOBJ_MEMBER_FUNCTION_P (decl) + || is_this_parameter (parms)); + /* We expect at least one argument, unless the function is variadic, in which + case parm_count will be 0. */ + gcc_assert (parm_count >= 0 && (parms || parm_count == 0)); + + hash_map<int_hash<int, -1, -2>, tree> specified_idxs; + /* If there are any nodes that were not able to be finalized and/or expanded + this gets set to true, this can occur even if we are fully instantiated + if the function is a variadic function. */ + bool dependent = false; + tree new_list = NULL_TREE; + auto append_to_list = [chain = &new_list] (tree node) mutable + { + gcc_assert (*chain == NULL_TREE); + *chain = node; + chain = &TREE_CHAIN (*chain); + }; + + const int iobj_parm_adjustment = DECL_IOBJ_MEMBER_FUNCTION_P (decl) ? 1 : 0; + + for (tree next, node = list; node; node = next) + { + /* Nodes are mutated and appended to new_list, retrieve its chain early + and remember it. */ + next = TREE_CHAIN (node); + TREE_CHAIN (node) = NULL_TREE; + tree parm_list_item = TREE_VALUE (node); + switch (TREE_CODE (TREE_VALUE (parm_list_item))) + { + case NOP_EXPR: + { + /* cp_parser_omp_parm_list stores parameter index items in a + NOP so we know to modify them here. This solution is + imperfect, but there isn't time to do it differently. */ + tree cst = TREE_OPERAND (TREE_VALUE (parm_list_item), 0); + /* Adjust the 1 based index to 0 based, potentially adjust for + the 'this' parameter. */ + const int idx = tree_to_shwi (cst) - 1 + iobj_parm_adjustment; + TREE_VALUE (parm_list_item) + = build_int_cst (integer_type_node, idx); + gcc_fallthrough (); + } + case INTEGER_CST: + { + /* These are finished, just check for errors and append + them to the list. + Check everything, we might have new errors if we didn't know + how many parameters we had the first time around. */ + const int idx = tree_to_shwi (TREE_VALUE (parm_list_item)); + tree *first = specified_idxs.get (idx); + if (first) + { + error_at (EXPR_LOCATION (TREE_PURPOSE (parm_list_item)), + "OpenMP parameter list items must specify a " + "unique parameter"); + inform (EXPR_LOCATION (TREE_PURPOSE (*first)), + "parameter previously specified here"); + } + else if (parm_count != 0 && idx >= parm_count) + { + error_at (EXPR_LOCATION (TREE_PURPOSE (parm_list_item)), + "parameter list item index is out of range"); + } + else + { + append_to_list (node); + if (specified_idxs.put (idx, parm_list_item)) + gcc_unreachable (); + } + break; + } + case PARM_DECL: + { + const const_tree parm_to_find = TREE_VALUE (parm_list_item); + /* Indices are stored as 0 based, don't adjust for iobj func, + the this parameter is present in parms. */ + int idx = 0; + const_tree parm = parms; + while (true) + { + /* We already confirmed that the parameter exists + in cp_parser_omp_parm_list, we should never be reaching + the end of this list. */ + gcc_assert (parm); + /* Expansion of a parameter pack will change the index of + parameters that come after it, we will have to defer this + lookup until the fndecl has been substituted. */ + if (DECL_PACK_P (parm)) + { + gcc_assert (processing_template_decl); + /* As explained above, this only happens with a parameter + pack in the middle of a function, this slower lookup + is fine for such an edge case. */ + const tree first = [&] () + { + for (tree n = new_list; n; n = TREE_CHAIN (n)) + { + tree item = TREE_VALUE (n); + if (TREE_CODE (TREE_VALUE (item)) == PARM_DECL + /* This comparison works because we make sure + to store the original node. */ + && TREE_VALUE (item) == parm_to_find) + return item; + } + return NULL_TREE; + } (); /* IILE. */ + if (first) + { + location_t loc + = EXPR_LOCATION (TREE_PURPOSE (parm_list_item)); + error_at (loc, + "OpenMP parameter list items must specify " + "a unique parameter"); + inform (EXPR_LOCATION (TREE_PURPOSE (first)), + "parameter previously specified here"); + } + else + { + /* We need to process this again once the pack + blocking us has been expanded. */ + dependent = true; + /* Make sure we use the original so the above + comparison works when we return here later. + This may no longer be required since we are + comparing the DECL_NAME of each below, but + regardless, use the original. */ + append_to_list (node); + } + break; + } + /* Compare the identifier nodes to so the comparison works + even after the node has been substituted. */ + if (DECL_NAME (parm) == DECL_NAME (parm_to_find)) + { + tree *first = specified_idxs.get (idx); + if (first) + { + location_t loc + = EXPR_LOCATION (TREE_PURPOSE (parm_list_item)); + error_at (loc, + "OpenMP parameter list items must specify " + "a unique parameter"); + inform (EXPR_LOCATION (TREE_PURPOSE (*first)), + "parameter previously specified here"); + } + else + { + TREE_VALUE (parm_list_item) + = build_int_cst (integer_type_node, idx); + append_to_list (node); + if (specified_idxs.put (idx, parm_list_item)) + gcc_unreachable (); + } + break; + } + ++idx; + parm = DECL_CHAIN (parm); + } + break; + } + case TREE_LIST: + { + /* Mutates bound. + This is the final point where indices and ranges are adjusted + from OpenMP spec representation (1 based indices, inclusive + intervals [lb, ub]) to GCC's internal representation (0 based + indices, inclusive lb, exclusive ub [lb, ub)), this is + intended to match C++ semantics. + Care must be taken to ensure we do not make these adjustments + multiple times. */ + auto do_bound = [&] (tree bound, const int correction) + { + gcc_assert (-1 <= correction && correction <= 1); + if (bound == error_mark_node) + return bound; + + tree expr = TREE_VALUE (bound); + /* If we already have an integer_cst, the bound has already + been converted to a 0 based index. Do not strip location + wrappers, they might be a template parameter that got + substituted to an INTEGER_CST, but not been finalized + yet. */ + if (TREE_CODE (expr) == INTEGER_CST) + return bound; + + const location_t expr_loc = EXPR_LOCATION (expr); + + if (type_dependent_expression_p (expr)) + { + ATTR_IS_DEPENDENT (bound) = true; + return bound; + } + else if (!INTEGRAL_TYPE_P (TREE_TYPE (expr))) + { + if (TREE_PURPOSE (bound)) + error_at (expr_loc, "logical offset of a bound must " + "be of type %<int%>"); + else + error_at (expr_loc, "expression of a bound must be " + "of type %<int%>"); + return error_mark_node; + } + else if (value_dependent_expression_p (expr)) + { + ATTR_IS_DEPENDENT (bound) = true; + return bound; + } + /* EXPR is not dependent, get rid of any leftover location + wrappers. */ + expr = tree_strip_any_location_wrapper (expr); + /* Unless we want some really good diagnostics, we don't need + to wrap expr with a location anymore. Additionally, if we + do that we need a new way of differentiating adjusted and + unadjusted expressions. */ + + /* Do we need to mark this as an rvalue use with + mark_rvalue_use as well? + We either need to strictly only accept expressions of type + int, or warn for conversions. + I'm pretty sure this should be manifestly + constant-evaluated. We require a constant here, + let fold_non_dependent_expr complain. */ + expr = fold_non_dependent_expr (expr, + tf_warning_or_error, + true); + if (!TREE_CONSTANT (expr)) + { + if (TREE_PURPOSE (bound)) + error_at (expr_loc, "logical offset of a bound must " + "be a constant expression"); + else + error_at (expr_loc, "expression of a bound must be a " + "constant expression"); + return error_mark_node; + } + + const int sgn = tree_int_cst_sgn (expr); + const int val = tree_to_shwi (expr); + /* Technically this can work with omp_num_args+expr but the + spec forbids it, we can support it later if we like. */ + if (sgn < 0) + { + if (TREE_PURPOSE (bound)) + error_at (expr_loc, "logical offset of a bound must " + "be non negative"); + else + error_at (expr_loc, "expression of a bound must be " + "positive"); + return error_mark_node; + } + + const const_tree num_args_marker = TREE_PURPOSE (bound); + if (num_args_marker == NULL_TREE) + { + if (sgn != 1) + { + error_at (expr_loc, "expression of bound must be " + "positive"); + return error_mark_node; + } + if (parm_count > 0 && val > parm_count) + { + /* FIXME: output omp_num_args and parm_count. */ + error_at (expr_loc, "expression of bound is out " + "of range"); + return error_mark_node; + } + TREE_VALUE (bound) = build_int_cst (integer_type_node, + val + correction); + return bound; + } + else if (num_args_marker + == get_identifier ("omp num args plus")) + { + if (sgn != 0) + { + error_at (expr_loc, + "logical offset must be equal to 0 in a " + "bound of the form " + "%<omp_num_args+logical-offset%>"); + return error_mark_node; + } + TREE_PURPOSE (bound) + = get_identifier ("omp relative bound"); + /* This expresses + omp_num_args + correction + logical offset, + the only valid value for logical offset is 0. */ + TREE_VALUE (bound) = build_int_cst (integer_type_node, + correction + 0); + return bound; + } + else if (num_args_marker + == get_identifier ("omp num args minus")) + { + gcc_assert (sgn != -1); + TREE_PURPOSE (bound) + = get_identifier ("omp relative bound"); + /* Don't invert correction, we are expressing + omp_num_args + correction - logical offset. */ + TREE_VALUE (bound) = build_int_cst (integer_type_node, + correction + (-val)); + return bound; + } + gcc_unreachable (); + }; + /* Convert both to 0 based indices, upper bound + is stored one past the end. */ + static constexpr int lb_adjustment = -1; + static constexpr int ub_adjustment = -1 + 1; + + tree range = TREE_VALUE (parm_list_item); + tree lb = do_bound (TREE_PURPOSE (range), + lb_adjustment + iobj_parm_adjustment); + tree ub = do_bound (TREE_VALUE (range), + ub_adjustment + iobj_parm_adjustment); + gcc_assert (lb && ub); + /* If we know how many params there are for sure we can + change this bound to a literal. */ + auto maybe_fold_relative_bound = [&] (tree bound) + { + if (bound == error_mark_node + || parm_count == 0 + || !TREE_PURPOSE (bound)) + return bound; + gcc_assert (TREE_PURPOSE (bound) + == get_identifier ("omp relative bound")); + const int value = tree_to_shwi (TREE_VALUE (bound)); + gcc_assert (value <= 0 && parm_count >= 1); + /* Overflow is impossible. */ + const int diff = parm_count + value; + if (diff < 0) + { + /* FIXME: output value of omp_num_args. */ + error_at (EXPR_LOCATION (TREE_CHAIN (bound)), + "bound with logical offset evaluates to an " + "out of range index"); + return error_mark_node; + } + gcc_assert (diff < INT_MAX); + TREE_PURPOSE (bound) = NULL_TREE; + TREE_VALUE (bound) = build_int_cst (integer_type_node, diff); + return bound; + }; + lb = maybe_fold_relative_bound (lb); + ub = maybe_fold_relative_bound (ub); + + gcc_assert (lb && ub); + const tree range_loc_wrapped = TREE_PURPOSE (parm_list_item); + + auto append_one_idx = [&] (tree purpose, tree loc_expr, int idx) + { + tree *dupe = specified_idxs.get (idx); + gcc_assert (!dupe || *dupe); + if (dupe) + return *dupe; + tree cst = build_int_cst (integer_type_node, idx); + tree new_item = build_tree_list (loc_expr, cst); + append_to_list (build_tree_list (purpose, new_item)); + if (specified_idxs.put (idx, parm_list_item)) + gcc_unreachable (); + return NULL_TREE; + }; + + /* TODO: handle better lol. */ + if (lb == error_mark_node || ub == error_mark_node) + continue; + /* Wait until both lb and ub are substituted before trying to + process any further, we are also done if both bounds are + relative. */ + if ((TREE_PURPOSE (lb) && TREE_PURPOSE (ub)) + || value_dependent_expression_p (TREE_VALUE (lb)) + || value_dependent_expression_p (TREE_VALUE (ub))) + { + /* If we are instantiating and have unexpanded numeric ranges + then this function must be variadic, and thus it doesn't + make this parm list dependent. + This doesn't really matter since we are high-jacking this + flag but it doesn't hurt to be technically correct. */ + /* Early escape...? */ + } + /* If both bounds are non relative, we can fully expand them. */ + else if (!TREE_PURPOSE (lb) && !TREE_PURPOSE (ub)) + { + const int lb_val = tree_to_shwi (TREE_VALUE (lb)); + const int ub_val = tree_to_shwi (TREE_VALUE (ub)); + /* Empty ranges are not allowed at this point. */ + if (lb_val >= ub_val) + { + /* Note that the error message does not match the + condition as we altered ub to be one past the end. */ + error_at (EXPR_LOCATION (range_loc_wrapped), + "numeric range lower bound must be less than " + "or equal to upper bound"); + continue; + } + gcc_assert (lb_val >= 0 && ub_val > 0 && lb_val < ub_val); + + for (int idx = lb_val; idx < ub_val; ++idx) + { + /* There might be something in PURPOSE that we want to + propogate when expanding. */ + tree dupe = append_one_idx (TREE_PURPOSE (node), + range_loc_wrapped, + idx); + if (dupe) + { + const int omp_idx = idx + 1; + error_at (EXPR_LOCATION (range_loc_wrapped), + "expansion of numeric range specifies " + "non-unique index %d", + omp_idx); + inform (EXPR_LOCATION (TREE_PURPOSE (dupe)), + "parameter previously specified here"); + } + } + /* The range is fully expanded, do not add it back to the + list. */ + TREE_CHAIN (node) = NULL_TREE; + continue; + } + else if (!processing_template_decl) + { + /* Wait until we are fully instantiated to make this + transformation, expanding a bound with omp_num_args after + doing this will cause bugs. + We also potentially cause bugs if one gets expanded, gets + a partial expansion here, and then the other bound gets + expanded later. That case is probably fine but we should + avoid it anyway. */ + gcc_assert (!TREE_PURPOSE (lb) + || TREE_PURPOSE (lb) + == get_identifier ("omp relative bound")); + gcc_assert (!TREE_PURPOSE (ub) + || TREE_PURPOSE (ub) + == get_identifier ("omp relative bound")); + /* At least one of lb and ub are NULL_TREE, and the other + is omp relative bound. */ + gcc_assert (TREE_PURPOSE (lb) != TREE_PURPOSE (ub)); + /* This only adds slight quality of life to diagnostics, it + isn't really worth it, but we need parity with the C front + end. Alternatively, handling empty numeric ranges could + have been removed from modify_call_for_omp_dispatch but + it's already there and it isn't that hard to add support + here. */ + if (TREE_PURPOSE (ub)) + { + /* The C front end goes a little further adding all + indices between lb and the last real parameter, + we aren't going to those efforts here though. */ + gcc_assert (!TREE_PURPOSE (lb)); + const int val = tree_to_shwi (TREE_VALUE (lb)); + gcc_assert (val < INT_MAX); + /* We know the index in lb will always be specified. */ + tree dupe = append_one_idx (TREE_PURPOSE (node), + range_loc_wrapped, + val); + if (dupe) + { + error_at (EXPR_LOCATION (range_loc_wrapped), + "lower bound of numeric range specifies " + "non-unique index %d", + val); + inform (EXPR_LOCATION (TREE_PURPOSE (dupe)), + "parameter previously specified here"); + } + /* The value was added above, adjust lb to be ahead by + one so it's not added again in + modify_call_for_omp_dispatch. */ + TREE_VALUE (lb) = build_int_cst (integer_type_node, + val + 1); + } + else + { + gcc_assert (TREE_PURPOSE (lb)); + const int val = tree_to_shwi (TREE_VALUE (ub)); + gcc_assert (val > 0); + /* We know the index in ub will always be specified. */ + tree dupe = append_one_idx (TREE_PURPOSE (node), + range_loc_wrapped, + val); + if (dupe) + { + error_at (EXPR_LOCATION (range_loc_wrapped), + "upper bound of numeric range specifies " + "non-unique index %d", val); + inform (EXPR_LOCATION (TREE_PURPOSE (dupe)), + "parameter previously specified here"); + } + /* The value was added above, adjust ub to be behind by + one so it's not added again in + modify_call_for_omp_dispatch. */ + TREE_VALUE (ub) = build_int_cst (integer_type_node, + val - 1); + } + /* This is not a full expansion, just a partial, we still + to add the numeric range to the final list. */ + } + dependent = processing_template_decl; + TREE_PURPOSE (range) = lb; + TREE_VALUE (range) = ub; + TREE_VALUE (parm_list_item) = range; + append_to_list (node); + break; + } + default: + gcc_unreachable (); + } + } + if (!new_list) + return error_mark_node; + /* Kinda a hack, hopefully temporary. */ + ATTR_IS_DEPENDENT (new_list) = dependent; + return new_list; +} + +/* LIST is a TREE_LIST representing an OpenMP parameter-list specified in an + adjust_args clause, or multiple concatanated parameter-lists each specified + in an adjust_args clause, each of which may have the same clause modifier, + or different clause modifiers. The clause modifier of the adjust_args + clause the parameter-list-item was specified in is stored in the + TREE_PURPOSE of each elt of LIST. DECL is the function decl the clauses + are applied to, PARM_COUNT is 0 if the number of parameters is unknown + or because the function is variadic, otherwise PARM_COUNT is the number of + parameters. + + Check for and diagnose invalid parameter types for each item, remove them + from the list so errors are not diagnosed multiple times. Remove items with + the "nothing" modifier once everything is done. + + Returns TREE_LIST or NULL_TREE if no items have errors, returns TREE_LIST + or error_mark_node if there were errors diagnosed. NULL_TREE is never + returned if an error was diagnosed. */ + +tree +finish_omp_adjust_args (tree list, const_tree decl, const int parm_count) +{ + gcc_assert (list && decl); + /* We need to keep track of this so we know whether we can remove items with + the "nothing" modifier. */ + bool has_dependent_list_items = false; + + const const_tree need_device_ptr_id = get_identifier ("need_device_ptr"); + const const_tree need_device_addr_id = get_identifier ("need_device_addr"); + const const_tree nothing_id = get_identifier ("nothing"); + tree *prev_chain = &list; + auto keep_node = [&] (tree n) { prev_chain = &TREE_CHAIN (n); }; + auto remove_node = [&] (tree n) { *prev_chain = TREE_CHAIN (n); }; + for (tree n = list; n != NULL_TREE; n = TREE_CHAIN (n)) + { + tree parm_list_item = TREE_VALUE (n); + if (TREE_CODE (TREE_VALUE (parm_list_item)) == PARM_DECL) + { + /* We only encounter a PARM_DECL here if a parameter pack comes + before it, it will have been replaced by an index by + finish_omp_parm_list otherwise. */ + gcc_assert (processing_template_decl); + keep_node (n); + has_dependent_list_items = true; + continue; + } + /* Numeric range case. */ + if (TREE_CODE (TREE_VALUE (parm_list_item)) == TREE_LIST) + { + /* These will have been expanded by finish_omp_parm_list unless we + can't determine the number of parameters. */ + gcc_assert (processing_template_decl || parm_count == 0); + keep_node (n); + has_dependent_list_items = true; + continue; + } + gcc_assert (TREE_CODE (TREE_VALUE (parm_list_item)) == INTEGER_CST); + + const const_tree n_modifier = TREE_PURPOSE (n); + gcc_assert (n_modifier == nothing_id + || n_modifier == need_device_ptr_id + || n_modifier == need_device_addr_id); + if (n_modifier == nothing_id) + { + keep_node (n); + continue; + } + const int idx = tree_to_shwi (TREE_VALUE (parm_list_item)); + + gcc_assert (idx >= 0 && (parm_count == 0 || idx < parm_count)); + const const_tree parm_decl = [&] () -> const_tree + { + const const_tree parms = DECL_ARGUMENTS (decl); + gcc_assert (parms != NULL_TREE || parm_count == 0); + if (parms == NULL_TREE + || (parm_count != 0 && idx >= parm_count)) + return NULL_TREE; + + int cur_idx = 0; + for (const_tree p = parms; p != NULL_TREE; p = DECL_CHAIN (p)) + { + /* This kind of sucks, we really should be building a vec instead + of traversing the list of parms each time. */ + gcc_assert (parm_count == 0 || cur_idx < parm_count); + if (DECL_PACK_P (p)) + return NULL_TREE; + if (cur_idx == idx) + return p; + ++cur_idx; + } + return NULL_TREE; + } (); /* IILE. */ + /* cp_parser_omp_parm_list handles out of range indices. */ + gcc_assert (parm_count == 0 || parm_decl); + + if (!parm_decl) + has_dependent_list_items = true; + else if (n_modifier == need_device_ptr_id) + { + /* OpenMP 6.0 (332:28-30) + If the need_device_ptr adjust-op modifier is present, each list + item that appears in the clause that refers to a specific named + argument in the declaration of the function variant must be of + pointer type or reference to pointer type. */ + tree parm_type = TREE_TYPE (parm_decl); + if (WILDCARD_TYPE_P (parm_type)) + /* Do nothing for now, it might become a pointer. */; + else if (TYPE_REF_P (parm_type) + && WILDCARD_TYPE_P (TREE_TYPE (parm_type))) + /* It might become a reference to a pointer. */; + else if (!TYPE_PTR_P (parm_type)) + { + if (TYPE_REF_P (parm_type) + && TYPE_PTR_P (TREE_TYPE (parm_type))) + /* The semantics for this are unclear, instead of supporting + it incorrectly, just sorry. */ + sorry_at (DECL_SOURCE_LOCATION (parm_decl), + "parameter with type reference to pointer in an " + "%<adjust_args%> with the %<need_device_ptr%> " + "modifier is not currently supported"); + else + error_at (DECL_SOURCE_LOCATION (parm_decl), + "parameter specified in an %<adjust_args%> clause " + "with the %<need_device_ptr%> modifier must be of " + "pointer type"); + inform (EXPR_LOCATION (TREE_PURPOSE (parm_list_item)), + "parameter specified here"); + remove_node (n); + continue; + } + } + else if (n_modifier == need_device_addr_id) + { + /* OpenMP 6.0 (332:31-33) + If the need_device_addr adjust-op modifier is present, each list + item that appears in the clause must refer to an argument in the + declaration of the function variant that has a reference type. */ + tree parm_type = TREE_TYPE (parm_decl); + if (WILDCARD_TYPE_P (parm_type)) + /* Do nothing for now, it might become a ref. */; + else if (!TYPE_REF_P (parm_type)) + { + error_at (DECL_SOURCE_LOCATION (parm_decl), + "parameter specified in an %<adjust_args%> clause " + "with the %<need_device_addr%> modifier must be of " + "reference type"); + inform (EXPR_LOCATION (TREE_PURPOSE (parm_list_item)), + "parameter specified here"); + remove_node (n); + continue; + } + } + /* If we get here there were no errors. */ + keep_node (n); + } + + /* All items were removed due to errors. */ + if (!list) + return error_mark_node; + if (has_dependent_list_items) + return list; + /* We no longer need to keep items with the "nothing" modifier. */ + prev_chain = &list; + for (tree n = list; n != NULL_TREE; n = TREE_CHAIN (n)) + { + if (TREE_PURPOSE (n) == nothing_id) + remove_node (n); + else + keep_node (n); + } + /* If all items had the "nothing" modifier, we might have NULL_TREE here, + but that isn't a problem. */ + return list; +} /* For all elements of CLAUSES, validate them vs OpenMP constraints. Remove any elements from the list that are invalid. */ diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc index debca53..edc1adc 100644 --- a/gcc/fortran/trans-openmp.cc +++ b/gcc/fortran/trans-openmp.cc @@ -11156,8 +11156,14 @@ gfc_trans_omp_declare_variant (gfc_namespace *ns, gfc_namespace *parent_ns) if (omp_context_selector_matches (set_selectors, NULL_TREE, false)) { - tree need_device_ptr_list = NULL_TREE; - tree need_device_addr_list = NULL_TREE; + tree adjust_args_tree_list = NULL_TREE; + auto add_adjust_args + = [chain = &adjust_args_tree_list] (tree n) mutable + { + gcc_assert (chain && *chain == NULL_TREE); + *chain = n; + chain = &TREE_CHAIN (n); + }; tree append_args_tree = NULL_TREE; tree id = get_identifier ("omp declare variant base"); tree variant = gfc_get_symbol_decl (variant_proc_sym); @@ -11376,29 +11382,30 @@ gfc_trans_omp_declare_variant (gfc_namespace *ns, gfc_namespace *parent_ns) if (arg_list->u.adj_args.need_ptr || arg_list->u.adj_args.need_addr) { + tree modifier + = arg_list->u.adj_args.need_ptr + ? get_identifier ("need_device_ptr") + : get_identifier ("need_device_addr"); + gcc_checking_assert + (!arg_list->u.adj_args.need_addr + || modifier + == get_identifier ("need_device_addr")); // Store 0-based argument index, // as in gimplify_call_expr - tree t + tree item = build_tree_list ( NULL_TREE, build_int_cst (integer_type_node, idx + arg_idx_offset)); - if (arg_list->u.adj_args.need_ptr) - need_device_ptr_list - = chainon (need_device_ptr_list, t); - else - need_device_addr_list - = chainon (need_device_addr_list, t); + add_adjust_args (build_tree_list (modifier, + item)); } } } tree t = NULL_TREE; - if (need_device_ptr_list - || need_device_addr_list - || append_args_tree) + if (adjust_args_tree_list || append_args_tree) { - t = build_tree_list (need_device_ptr_list, - need_device_addr_list), + t = build_tree_list (NULL_TREE, adjust_args_tree_list), TREE_CHAIN (t) = append_args_tree; DECL_ATTRIBUTES (variant) = tree_cons ( get_identifier ("omp declare variant variant args"), t, diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc index d2f1a97..1e354d0 100644 --- a/gcc/gimplify.cc +++ b/gcc/gimplify.cc @@ -3981,7 +3981,7 @@ modify_call_for_omp_dispatch (tree expr, tree dispatch_clauses, bool want_value, bool pointerize) { location_t loc = EXPR_LOCATION (expr); - tree fndecl = get_callee_fndecl (expr); + const tree fndecl = get_callee_fndecl (expr); /* Skip processing if we don't get the expected call form. */ if (!fndecl) @@ -3990,23 +3990,180 @@ modify_call_for_omp_dispatch (tree expr, tree dispatch_clauses, tree init_code = NULL_TREE; tree cleanup = NULL_TREE; tree clobbers = NULL_TREE; - int nargs = call_expr_nargs (expr); + const int nargs = call_expr_nargs (expr); tree dispatch_device_num = NULL_TREE; tree dispatch_interop = NULL_TREE; tree dispatch_append_args = NULL_TREE; + /* Equal to the number of parameters. */ int nfirst_args = 0; - tree dispatch_adjust_args_list - = lookup_attribute ("omp declare variant variant args", - DECL_ATTRIBUTES (fndecl)); - if (dispatch_adjust_args_list) + const const_tree nothing_id = get_identifier ("nothing"); + const const_tree need_ptr_id = get_identifier ("need_device_ptr"); + const const_tree need_addr_id = get_identifier ("need_device_addr"); + + vec<tree> dispatch_adjust_args_specifiers = vNULL; + + if (tree declare_variant_variant_args_attr + = lookup_attribute ("omp declare variant variant args", + DECL_ATTRIBUTES (fndecl))) { + /* Due to how the nodes are layed out, unpacking them is pretty + incomprehensible. */ + gcc_assert (TREE_VALUE (declare_variant_variant_args_attr)); + dispatch_append_args + = TREE_CHAIN (TREE_VALUE (declare_variant_variant_args_attr)); + tree dispatch_adjust_args_list + = TREE_VALUE (declare_variant_variant_args_attr); + gcc_assert (dispatch_adjust_args_list); dispatch_adjust_args_list = TREE_VALUE (dispatch_adjust_args_list); - dispatch_append_args = TREE_CHAIN (dispatch_adjust_args_list); - if (TREE_PURPOSE (dispatch_adjust_args_list) == NULL_TREE - && TREE_VALUE (dispatch_adjust_args_list) == NULL_TREE) - dispatch_adjust_args_list = NULL_TREE; + + if (dispatch_adjust_args_list) + { + dispatch_adjust_args_specifiers.create (nargs); + for (int arg_idx = 0; arg_idx < nargs; ++arg_idx) + dispatch_adjust_args_specifiers.quick_push (NULL_TREE); + + for (tree n = dispatch_adjust_args_list; n; n = TREE_CHAIN (n)) + { + gcc_assert (TREE_VALUE (n) + && (TREE_PURPOSE (n) == nothing_id + || TREE_PURPOSE (n) == need_ptr_id + || TREE_PURPOSE (n) == need_addr_id)); + tree item = TREE_VALUE (n); + /* Diagnostics make more sense if we defer these. */ + if (TREE_CODE (TREE_VALUE (item)) == TREE_LIST) + continue; + gcc_assert (TREE_CODE (TREE_VALUE (item)) == INTEGER_CST); + const int idx = tree_to_shwi (TREE_VALUE (item)); + if (idx >= nargs) + { + /* Adjust to a 1 based index for output. */ + const int adjusted = idx + 1; + error_at (EXPR_LOCATION (TREE_PURPOSE (item)), + "parameter index %d is out of range with %d " + "arguments", + adjusted, nargs); + continue; + } + tree& spec_at_idx = dispatch_adjust_args_specifiers[idx]; + gcc_assert (spec_at_idx == NULL_TREE); + spec_at_idx = n; + } + /* There might be a better place to put this. */ + const bool variadic_func_p = [&] () + { + tree parm_type = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); + while (parm_type && parm_type != void_list_node) + parm_type = TREE_CHAIN (parm_type); + return parm_type != void_list_node; + } (); /* IILE. */ + auto expand_range = [&] (tree modifier_id, tree loc, tree range) + { + /* We only encounter numeric ranges here if fn is variadic. */ + gcc_assert (variadic_func_p); + const location_t range_loc = EXPR_LOCATION (loc); + const tree lb_node = TREE_PURPOSE (range); + const tree ub_node = TREE_VALUE (range); + const bool relative_lb = TREE_PURPOSE (lb_node) != NULL_TREE; + const bool relative_ub = TREE_PURPOSE (ub_node) != NULL_TREE; + const ptrdiff_t lb_raw = tree_to_shwi (TREE_VALUE (lb_node)); + const ptrdiff_t ub_raw = tree_to_shwi (TREE_VALUE (ub_node)); + /* relative_lb implies lb_raw <= -1, + relative_ub implies ub_raw <= 0. */ + gcc_assert ((relative_lb || relative_ub) + && (!relative_lb || lb_raw <= -1) + && (!relative_ub || ub_raw <= 0)); + /* (relative_lb && relative_ub) implies lb_raw < ub_raw. */ + gcc_assert (!(relative_lb && relative_ub) || lb_raw < ub_raw); + const ptrdiff_t lb = relative_lb ? lb_raw + nargs : lb_raw; + const ptrdiff_t ub = relative_ub ? ub_raw + nargs : ub_raw; + /* This will never happen, still gotta diagnose it. */ + if (lb > INT_MAX || ub > INT_MAX) + { + if (lb > INT_MAX) + error_at (range_loc, "lb overflow"); + else if (ub > INT_MAX) + error_at (range_loc, "ub overflow"); + return; + } + /* Internally, ub is stored as one-past-the-end. */ + if (lb < 0 || ub < 1) + { + if (lb < 0) + /* FIXME: Use location of lb specifically. */ + error_at (range_loc, + "lower bound with logical offset is negative " + "with %d arguments", + nargs); + if (ub < 1) + /* FIXME: Use location of ub specifically. */ + error_at (range_loc, + "upper bound with logical offset is negative " + "with %d arguments", + nargs); + return; + } + /* It's okay for lb and ub to be equal, we allow empty ranges + at this point. Don't bother diagnosing this if either bound + is out of range. */ + if (lb > ub) + { + if (relative_lb) + error_at (range_loc, + "lower bound with logical offset is greater " + "than upper bound with %d arguments", + nargs); + else + error_at (range_loc, + "upper bound with logical offset is less than " + "lower bound with %d arguments", + nargs); + return; + } + + for (int idx = lb; idx < ub; ++idx) + { + tree& spec_at_idx = dispatch_adjust_args_specifiers[idx]; + if (spec_at_idx != NULL_TREE) + { + tree item = TREE_VALUE (spec_at_idx); + location_t dupe_loc + = EXPR_LOCATION (TREE_PURPOSE (item)); + /* FIXME: Use nfirst_args to determine whether an index + refers to a variadic argument to enhance the + diagnostic. */ + error_at (range_loc, + "expansion of numeric range with %d " + "arguments specifies an already specified " + "parameter", + nargs); + inform (dupe_loc, "parameter previously specified here"); + /* Give up after the first collision to avoid spamming + errors. Alternatively, we could also remember which + ones we diagnosed, but it doesn't seem worth it. */ + return; + } + else + { + /* We don't need to create an index node anymore, + it is represented by the position in vec. */ + tree new_item = build_tree_list (loc, NULL_TREE); + spec_at_idx = build_tree_list (modifier_id, new_item); + } + } + }; + for (tree n = dispatch_adjust_args_list; n; n = TREE_CHAIN (n)) + { + tree item = TREE_VALUE (n); + if (TREE_CODE (TREE_VALUE (item)) != TREE_LIST) + continue; + expand_range (TREE_PURPOSE (n), + TREE_PURPOSE (item), + TREE_VALUE (item)); + } + } } + if (dispatch_append_args) { nfirst_args = tree_to_shwi (TREE_PURPOSE (dispatch_append_args)); @@ -4016,9 +4173,8 @@ modify_call_for_omp_dispatch (tree expr, tree dispatch_clauses, if (dispatch_device_num) dispatch_device_num = OMP_CLAUSE_DEVICE_ID (dispatch_device_num); dispatch_interop = omp_find_clause (dispatch_clauses, OMP_CLAUSE_INTEROP); - int nappend = 0, ninterop = 0; - for (tree t = dispatch_append_args; t; t = TREE_CHAIN (t)) - nappend++; + const int nappend = list_length (dispatch_append_args); + int ninterop = 0; /* FIXME: error checking should be taken out of this function and handled before any attempt at filtering or resolution happens. @@ -4246,10 +4402,14 @@ modify_call_for_omp_dispatch (tree expr, tree dispatch_clauses, i += nappend; for (j = nfirst_args; j < nargs; j++) buffer[i++] = CALL_EXPR_ARG (expr, j); - nargs += nappend; + /* Leave nargs alone so we don't need to account for changes of varargs + indices when adjusting the arguments below. + We also don't want any surprises if we move the above append_args + handling down, as it depends on nargs. */ + const int new_nargs = nargs + nappend; tree call = expr; expr = build_call_array_loc (EXPR_LOCATION (expr), TREE_TYPE (call), - CALL_EXPR_FN (call), nargs, buffer); + CALL_EXPR_FN (call), new_nargs, buffer); /* Copy all CALL_EXPR flags. */ CALL_EXPR_STATIC_CHAIN (expr) = CALL_EXPR_STATIC_CHAIN (call); @@ -4261,139 +4421,164 @@ modify_call_for_omp_dispatch (tree expr, tree dispatch_clauses, CALL_EXPR_VA_ARG_PACK (expr) = CALL_EXPR_VA_ARG_PACK (call); } - /* Nothing to do for adjust_args? */ - if (!dispatch_adjust_args_list || !TYPE_ARG_TYPES (TREE_TYPE (fndecl))) - goto add_cleanup; - - /* Handle adjust_args. */ - for (int i = 0; i < nargs; i++) + auto adjust_the_arg = [&] (tree arg, tree aa_spec) { - tree *arg_p = &CALL_EXPR_ARG (expr, i); - - /* Nothing to do if arg is constant null pointer. */ - if (integer_zerop (*arg_p)) - continue; + if (integer_zerop (arg) || !aa_spec) + return arg; + const bool need_device_ptr = TREE_PURPOSE (aa_spec) == need_ptr_id; + const bool need_device_addr = TREE_PURPOSE (aa_spec) == need_addr_id; + if (!need_device_ptr && !need_device_addr) + return arg; - bool need_device_ptr = false; - bool need_device_addr = false; - for (int need_addr = 0; need_addr <= 1; need_addr++) - for (tree arg = (need_addr - ? TREE_VALUE (dispatch_adjust_args_list) - : TREE_PURPOSE (dispatch_adjust_args_list)); - arg != NULL; arg = TREE_CHAIN (arg)) - { - if (TREE_VALUE (arg) - && TREE_CODE (TREE_VALUE (arg)) == INTEGER_CST - && wi::eq_p (i, wi::to_wide (TREE_VALUE (arg)))) - { - if (need_addr) - need_device_addr = true; - else - need_device_ptr = true; - break; - } - } - - if (need_device_ptr || need_device_addr) + auto find_arg_in_clause = [&] (const_tree clauses) -> const_tree { - bool is_device_ptr = false; - bool has_device_addr = false; - - for (tree c = dispatch_clauses; c; c = TREE_CHAIN (c)) + const const_tree arg_decl = [&] () { - if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR - || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR) - { - tree decl1 = DECL_NAME (OMP_CLAUSE_DECL (c)); - tree decl2 = tree_strip_nop_conversions (*arg_p); - if (TREE_CODE (decl2) == ADDR_EXPR) - decl2 = TREE_OPERAND (decl2, 0); - if (VAR_P (decl2) || TREE_CODE (decl2) == PARM_DECL) - { - decl2 = DECL_NAME (decl2); - if (decl1 == decl2 - && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR) - { - if (need_device_addr) - warning_at (OMP_CLAUSE_LOCATION (c), - OPT_Wopenmp, - "%<is_device_ptr%> for %qD does" - " not imply %<has_device_addr%> " - "required for %<need_device_addr%>", - OMP_CLAUSE_DECL (c)); - is_device_ptr = true; - break; - } - else if (decl1 == decl2) - { - if (need_device_ptr) - warning_at (OMP_CLAUSE_LOCATION (c), - OPT_Wopenmp, - "%<has_device_addr%> for %qD does" - " not imply %<is_device_ptr%> " - "required for %<need_device_ptr%>", - OMP_CLAUSE_DECL (c)); - has_device_addr = true; - break; - } - } - } - } - - if ((need_device_ptr && !is_device_ptr) - || (need_device_addr && !has_device_addr)) + tree arg_decl = tree_strip_nop_conversions (arg); + if (TREE_CODE (arg_decl) == ADDR_EXPR) + arg_decl = TREE_OPERAND (arg_decl, 0); + return arg_decl; + } (); /* IILE. */ + for (const_tree c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) { - if (dispatch_device_num == NULL_TREE) - { - // device_num = omp_get_default_device () - tree fn - = builtin_decl_explicit (BUILT_IN_OMP_GET_DEFAULT_DEVICE); - tree call = build_call_expr (fn, 0); - dispatch_device_num = create_tmp_var_raw (TREE_TYPE (call)); - tree init - = build4 (TARGET_EXPR, TREE_TYPE (call), - dispatch_device_num, call, NULL_TREE, NULL_TREE); - if (init_code) - init_code = build2 (COMPOUND_EXPR, TREE_TYPE (init), - init_code, init); - else - init_code = init; - } - - // We want to emit the following statement: - // mapped_arg = omp_get_mapped_ptr (arg, - // device_num) - // but arg has to be the actual pointer, not a - // reference or a conversion expression. - tree actual_ptr - = ((TREE_CODE (*arg_p) == ADDR_EXPR) - ? TREE_OPERAND (*arg_p, 0) - : *arg_p); - if (TREE_CODE (actual_ptr) == NOP_EXPR - && (TREE_CODE (TREE_TYPE (TREE_OPERAND (actual_ptr, 0))) - == REFERENCE_TYPE)) - { - actual_ptr = TREE_OPERAND (actual_ptr, 0); - actual_ptr = build1 (INDIRECT_REF, - TREE_TYPE (actual_ptr), - actual_ptr); - } - tree fn = builtin_decl_explicit (BUILT_IN_OMP_GET_MAPPED_PTR); - tree mapped_arg = build_call_expr_loc (loc, fn, 2, actual_ptr, - dispatch_device_num); - - if (TREE_CODE (*arg_p) == ADDR_EXPR - || (TREE_CODE (TREE_TYPE (actual_ptr)) == REFERENCE_TYPE)) - mapped_arg = build_fold_addr_expr (mapped_arg); - else if (TREE_CODE (*arg_p) == NOP_EXPR) - mapped_arg = build1 (NOP_EXPR, TREE_TYPE (*arg_p), - mapped_arg); - *arg_p = mapped_arg; + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR + && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR) + continue; + const tree name_in_clause = DECL_NAME (OMP_CLAUSE_DECL (c)); + if ((VAR_P (arg_decl) || TREE_CODE (arg_decl) == PARM_DECL) + && name_in_clause == DECL_NAME (arg_decl)) + return c; } + return NULL_TREE; + }; + /* The code this was refactored from stops on the first clause with a + matching var/parm specified in it. */ + const_tree clause_with_arg = find_arg_in_clause (dispatch_clauses); + /* I assume if a var/parm is used in multiple clauses it gets diagnosed + before we get here, make sure that is true. */ + gcc_checking_assert (!clause_with_arg + || !find_arg_in_clause + (OMP_CLAUSE_CHAIN (clause_with_arg))); + + const bool is_device_ptr = clause_with_arg + && OMP_CLAUSE_CODE (clause_with_arg) + == OMP_CLAUSE_IS_DEVICE_PTR; + const bool has_device_addr = clause_with_arg + && OMP_CLAUSE_CODE (clause_with_arg) + == OMP_CLAUSE_HAS_DEVICE_ADDR; + /* Obviously impossible with how things are currently implemented. */ + gcc_assert (!(is_device_ptr && has_device_addr)); + + if (need_device_addr && is_device_ptr) + warning_at (OMP_CLAUSE_LOCATION (clause_with_arg), + OPT_Wopenmp, + "%<is_device_ptr%> for %qD does not imply " + "%<has_device_addr%> required for %<need_device_addr%>", + OMP_CLAUSE_DECL (clause_with_arg)); + if (need_device_ptr && has_device_addr) + warning_at (OMP_CLAUSE_LOCATION (clause_with_arg), + OPT_Wopenmp, + "%<has_device_addr%> for %qD does not imply " + "%<is_device_ptr%> required for %<need_device_ptr%>", + OMP_CLAUSE_DECL (clause_with_arg)); + /* ARG does not need to be adjusted. */ + if ((need_device_ptr && is_device_ptr) + || (need_device_addr && has_device_addr)) + return arg; + + if (dispatch_device_num == NULL_TREE) + { + // device_num = omp_get_default_device () + tree fn = builtin_decl_explicit (BUILT_IN_OMP_GET_DEFAULT_DEVICE); + tree call = build_call_expr (fn, 0); + dispatch_device_num = create_tmp_var_raw (TREE_TYPE (call)); + tree init = build4 (TARGET_EXPR, TREE_TYPE (call), + dispatch_device_num, call, NULL_TREE, NULL_TREE); + if (init_code) + init_code = build2 (COMPOUND_EXPR, TREE_TYPE (init), + init_code, init); + else + init_code = init; + } + + // We want to emit the following statement: + // mapped_arg = omp_get_mapped_ptr (arg, + // device_num) + // but arg has to be the actual pointer, not a + // reference or a conversion expression. + tree actual_ptr = TREE_CODE (arg) == ADDR_EXPR ? TREE_OPERAND (arg, 0) + : arg; + if (TREE_CODE (actual_ptr) == NOP_EXPR + && (TREE_CODE (TREE_TYPE (TREE_OPERAND (actual_ptr, 0))) + == REFERENCE_TYPE)) + { + actual_ptr = TREE_OPERAND (actual_ptr, 0); + actual_ptr + = build1 (INDIRECT_REF, TREE_TYPE (actual_ptr), actual_ptr); + } + tree fn = builtin_decl_explicit (BUILT_IN_OMP_GET_MAPPED_PTR); + tree mapped_arg + = build_call_expr_loc (loc, fn, 2, actual_ptr, dispatch_device_num); + + if (TREE_CODE (arg) == ADDR_EXPR + || (TREE_CODE (TREE_TYPE (actual_ptr)) == REFERENCE_TYPE)) + mapped_arg = build_fold_addr_expr (mapped_arg); + else if (TREE_CODE (arg) == NOP_EXPR) + mapped_arg = build1 (NOP_EXPR, TREE_TYPE (arg), mapped_arg); + return mapped_arg; + }; + + /* Nothing to do for adjust_args? */ + const bool adjust_args_needed = [&] () + { + if (!dispatch_adjust_args_specifiers.exists ()) + return false; + for (auto const& aa_spec : dispatch_adjust_args_specifiers) + { + if (aa_spec + && (TREE_PURPOSE (aa_spec) == need_ptr_id + || TREE_PURPOSE (aa_spec) == need_addr_id)) + return true; + } + return false; + } (); /* IILE. */ + + if (adjust_args_needed) + { + /* FIXME: We need to check argument types. */ + const int num_parms = nfirst_args ? nfirst_args : nargs; + /* adjust_the_arg returns arg unchanged if no adjustments are needed. */ + for (int idx = 0; idx < num_parms; ++idx) + { + gcc_assert (dispatch_adjust_args_specifiers.length () + > static_cast<size_t>(idx)); + const tree aa_spec = dispatch_adjust_args_specifiers[idx]; + tree *const arg = &CALL_EXPR_ARG (expr, idx); + *arg = adjust_the_arg (*arg, aa_spec); + } + /* Variadic args come after append_args args, we can't do adjust_args + until after append_args is done though because append_args needs to + push into init_code first. We can probably fix this, but until then + we just need to adjust our index into CALL_EXPR_ARG by the number of + appended args. + It would just be simpler if we could handle adjust_args first, but I + don't know if there is a trivial way of handling the init_code + ordering. + This only handles varargs in functions that have an append_args + clause, varargs are handled in the above loop otherwise and this loop + is skipped. */ + const int varargs_start = num_parms; + for (int idx = varargs_start; idx < nargs; ++idx) + { + gcc_assert (dispatch_adjust_args_specifiers.length () + > static_cast<size_t>(idx)); + const tree aa_spec = dispatch_adjust_args_specifiers[idx]; + const int call_expr_arg_idx = idx + nappend; + tree *const arg = &CALL_EXPR_ARG (expr, call_expr_arg_idx); + *arg = adjust_the_arg (*arg, aa_spec); } } - add_cleanup: if (cleanup) { tree result = NULL_TREE; diff --git a/gcc/testsuite/c-c++-common/gomp/adjust-args-10.c b/gcc/testsuite/c-c++-common/gomp/adjust-args-10.c new file mode 100644 index 0000000..5cda21e --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/adjust-args-10.c @@ -0,0 +1,13 @@ +/* Diagnose invalid type in variadic arguments. */ + +void v0(int *, ...) {} + +#pragma omp declare variant(v0) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:omp_num_args) +void b0(int *, ...) {} + +void f0(int *p0, int *p1, int *p2, int *p3, int *p4) +{ + #pragma omp dispatch + b0(p0, p1, p2, p3, p4, 42); /* { dg-error "variadic argument 5 specified in an 'append_args' clause with the 'need_device_ptr' modifier must be of pointer type" "" { xfail *-*-* } } */ +} diff --git a/gcc/testsuite/c-c++-common/gomp/adjust-args-11.c b/gcc/testsuite/c-c++-common/gomp/adjust-args-11.c new file mode 100644 index 0000000..eabf124 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/adjust-args-11.c @@ -0,0 +1,58 @@ +/* Reject expressions outside of a numeric range. */ + +void v_0(int, int, int) {} +void v_1(int, int, int) {} +void v_2(int, int, int) {} +void v_3(int, int, int) {} + +enum {constant_expression = 1}; + +/* { dg-error "expected ':' before '\\)' token" "" { target *-*-* } .+2 } */ +/* { dg-note "an expression is only allowed in a numeric range" "" { target *-*-* } .+1 } */ +#pragma omp declare variant (v_0) match (construct={dispatch}) adjust_args (nothing: 0+1) +void b0 (int, int, int) {} + +/* { dg-error "'constant_expression' is not a function parameter" "" { target *-*-* } .+2 } */ +/* { dg-note "an expression is only allowed in a numeric range" "" { xfail *-*-* } .+1 } */ +#pragma omp declare variant (v_1) match (construct={dispatch}) adjust_args (nothing: constant_expression) +void b1 (int, int, int) {} + +/* { dg-error "expected ':' before '\\)' token" "" { target *-*-* } .+2 } */ +/* { dg-note "an expression is only allowed in a numeric range" "" { target *-*-* } .+1 } */ +#pragma omp declare variant (v_2) match (construct={dispatch}) adjust_args (nothing: constant_expression + 0) +void b2 (int, int, int) {} + +/* { dg-error "expected ':' before '\\)' token" "" { target *-*-* } .+2 } */ +/* { dg-note "an expression is only allowed in a numeric range" "" { target *-*-* } .+1 } */ +#pragma omp declare variant (v_3) match (construct={dispatch}) adjust_args (nothing: 0 + constant_expression) +void b3 (int, int, int) {} + + +/* Invalid uses of omp_num_args. */ + +void ona_v0 (int, int, int) {} +void ona_v1 (int, int, int) {} +void ona_v2 (int, int, int) {} +void ona_v3 (int, int, int) {} + +/* { dg-error "'omp_num_args' may only be used at the start of a numeric range bound" "" { target *-*-* } .+2 } */ +#pragma omp declare variant(ona_v0) match(construct={dispatch}) \ + adjust_args(nothing: omp_num_args) +void ona_b0 (int, int, int) {} + +/* { dg-error "'omp_num_args' may only be used at the start of a numeric range bound" "" { target *-*-* } .+3 } */ +/* { dg-error "'omp_num_args' may only be used at the start of a numeric range bound" "" { target *-*-* } .+3 } */ +#pragma omp declare variant(ona_v1) match(construct={dispatch}) \ + adjust_args(nothing: omp_num_args, \ + omp_num_args) +void ona_b1 (int, int, int) {} + +/* { dg-error "'omp_num_args' may only be used at the start of a numeric range bound" "" { target *-*-* } .+2 } */ +#pragma omp declare variant(ona_v2) match(construct={dispatch}) \ + adjust_args(nothing: omp_num_args, 1) +void ona_b2 (int, int, int) {} + +/* { dg-error "'omp_num_args' may only be used at the start of a numeric range bound" "" { target *-*-* } .+2 } */ +#pragma omp declare variant(ona_v3) match(construct={dispatch}) \ + adjust_args(nothing: 1, omp_num_args) +void ona_b3 (int, int, int) {} diff --git a/gcc/testsuite/c-c++-common/gomp/adjust-args-12.c b/gcc/testsuite/c-c++-common/gomp/adjust-args-12.c new file mode 100644 index 0000000..3a283ac --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/adjust-args-12.c @@ -0,0 +1,20 @@ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +/* Valid constant-expressions in numeric ranges. */ + +void v (int *, int *, int *, int *, int *) {} + +#pragma omp declare variant(v) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 5-4:6-4, 4) +void b (int *, int *, int *, int *, int *) {} + +void f (int *p0, int *p1, int *p2, int *p3, int *p4) +{ + #pragma omp dispatch + b (p0, p1, p2, p3, p4); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p0, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p1, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p3, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v \\(D\.\[0-9\]+, D\.\[0-9\]+, p2, D\.\[0-9\]+, p4\\);" "gimple" } } */ +} +/* { dg-final { scan-tree-dump-times "D\.\[0-9\]+ = __builtin_omp_get_default_device \\(\\);" 1 "gimple" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/adjust-args-13.c b/gcc/testsuite/c-c++-common/gomp/adjust-args-13.c new file mode 100644 index 0000000..dd18594 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/adjust-args-13.c @@ -0,0 +1,53 @@ +/* Depending on how the variable is looked up, such as by using undeclared_variable + to emit a diagnostic in the c front end, we might accidently prevent diagnostics + later in the file. + Note, the names specified in the adjust_args clause are important for this test. */ + +/* No parameters. */ + +void v00(); +#pragma omp declare variant(v00) match(construct={dispatch}) \ + adjust_args(need_device_ptr: w) /* { dg-error "'w' is not a function parameter" } */ +void b00(); + +void v01() {} +#pragma omp declare variant(v01) match(construct={dispatch}) \ + adjust_args(need_device_ptr: w) /* { dg-error "'w' is not a function parameter" } */ +void b01() {} + +/* No parameters, specified with void. */ + +void v10(void); +#pragma omp declare variant(v10) match(construct={dispatch}) \ + adjust_args(need_device_ptr: x) /* { dg-error "'x' is not a function parameter" } */ +void b10(void); + +void v11(void) {} +#pragma omp declare variant(v11) match(construct={dispatch}) \ + adjust_args(need_device_ptr: x) /* { dg-error "'x' is not a function parameter" } */ +void b11(void) {} + +/* Variadic. */ + +void v20(...); +#pragma omp declare variant(v20) match(construct={dispatch}) \ + adjust_args(need_device_ptr: y) /* { dg-error "'y' is not a function parameter" } */ +void b20(...); + +void v21(...) {} +#pragma omp declare variant(v21) match(construct={dispatch}) \ + adjust_args(need_device_ptr: y) /* { dg-error "'y' is not a function parameter" } */ +void b21(...) {} + +/* With non-empty parameter list. */ + +void v30(int a); +#pragma omp declare variant(v30) match(construct={dispatch}) \ + adjust_args(need_device_ptr: z) /* { dg-error "'z' is not a function parameter" } */ +void b30(int a); + +void v31(int a) { (void)a; } +#pragma omp declare variant(v31) match(construct={dispatch}) \ + adjust_args(need_device_ptr: z) /* { dg-error "'z' is not a function parameter" } */ +void b31(int a) { (void)a; } + diff --git a/gcc/testsuite/c-c++-common/gomp/adjust-args-14.c b/gcc/testsuite/c-c++-common/gomp/adjust-args-14.c new file mode 100644 index 0000000..039f439 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/adjust-args-14.c @@ -0,0 +1,57 @@ +/* Multiple uses of the same name should not be diagnosed multiple times. */ + +/* No parameters. */ + +void v00(); +#pragma omp declare variant(v00) match(construct={dispatch}) \ + adjust_args(need_device_ptr: w, /* { dg-error "'w' is not a function parameter" } */ \ + w) /* { dg-bogus "'w' is not a function parameter" "" { xfail *-*-* } } */ +void b00(); + +void v01() {} +#pragma omp declare variant(v01) match(construct={dispatch}) \ + adjust_args(need_device_ptr: ww, /* { dg-error "'ww' is not a function parameter" } */ \ + ww) /* { dg-bogus "'ww' is not a function parameter" "" { xfail *-*-* } } */ +void b01() {} + +/* No parameters, specified with void. */ + +void v10(void); +#pragma omp declare variant(v10) match(construct={dispatch}) \ + adjust_args(need_device_ptr: x, /* { dg-error "'x' is not a function parameter" } */ \ + x) /* { dg-bogus "'x' is not a function parameter" "" { xfail *-*-* } } */ +void b10(void); + +void v11(void) {} +#pragma omp declare variant(v11) match(construct={dispatch}) \ + adjust_args(need_device_ptr: xx, /* { dg-error "'xx' is not a function parameter" } */ \ + xx) /* { dg-bogus "'xx' is not a function parameter" "" { xfail *-*-* } } */ +void b11(void) {} + +/* Variadic. */ + +void v20(...); +#pragma omp declare variant(v20) match(construct={dispatch}) \ + adjust_args(need_device_ptr: y, /* { dg-error "'y' is not a function parameter" } */ \ + y) /* { dg-bogus "'y' is not a function parameter" "" { xfail *-*-* } } */ +void b20(...); + +void v21(...) {} +#pragma omp declare variant(v21) match(construct={dispatch}) \ + adjust_args(need_device_ptr: yy, /* { dg-error "'yy' is not a function parameter" } */ \ + yy) /* { dg-bogus "'yy' is not a function parameter" "" { xfail *-*-* } } */ +void b21(...) {} + +/* With non-empty parameter list. */ + +void v30(int a); +#pragma omp declare variant(v30) match(construct={dispatch}) \ + adjust_args(need_device_ptr: z, /* { dg-error "'z' is not a function parameter" } */ \ + z) /* { dg-bogus "'z' is not a function parameter" "" { xfail *-*-* } } */ +void b30(int a); + +void v31(int a) { (void)a; } +#pragma omp declare variant(v31) match(construct={dispatch}) \ + adjust_args(need_device_ptr: zz, /* { dg-error "'zz' is not a function parameter" } */ \ + zz) /* { dg-bogus "'zz' is not a function parameter" "" { xfail *-*-* } } */ +void b31(int a) { (void)a; }
\ No newline at end of file diff --git a/gcc/testsuite/c-c++-common/gomp/adjust-args-15.c b/gcc/testsuite/c-c++-common/gomp/adjust-args-15.c new file mode 100644 index 0000000..6347891 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/adjust-args-15.c @@ -0,0 +1,15 @@ +/* Technically a bug, won't fix, probably. + + OpenMP 6.0 (158:18) + In all cases, white space in clause-argument-list is optional. + + The lexer obviously doesn't like this very much, but technically it is + correct OpenMP syntax, the first colon is a part of the + modifier-specification-list, the second is a numeric range with both + lb and ub not specified. */ + +void v (int) {} + +#pragma omp declare variant(v) match(construct={dispatch}) \ + adjust_args(nothing::) /* { dg-bogus "" "" { xfail *-*-* } } */ +void b (int) {} diff --git a/gcc/testsuite/c-c++-common/gomp/adjust-args-7.c b/gcc/testsuite/c-c++-common/gomp/adjust-args-7.c new file mode 100644 index 0000000..e4b0930 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/adjust-args-7.c @@ -0,0 +1,529 @@ +/* Test uses of omp_num_args. */ + +void v_1_arg_0(int) {} +void v_1_arg_1(int) {} +void v_1_arg_2(int) {} +void v_1_arg_3(int) {} +void v_1_arg_4(int) {} +void v_1_arg_5(int) {} +void v_1_arg_6(int) {} +void v_1_arg_7(int) {} +void v_1_arg_8(int) {} +void v_1_arg_9(int) {} +void v_1_arg_10(int) {} +void v_1_arg_11(int) {} +void v_1_arg_12(int) {} +void v_1_arg_13(int) {} +void v_1_arg_14(int) {} +void v_1_arg_15(int) {} +void v_1_arg_16(int) {} +void v_1_arg_17(int) {} +void v_1_arg_18(int) {} +void v_1_arg_19(int) {} +void v_1_arg_20(int) {} +void v_1_arg_21(int) {} +void v_1_arg_22(int) {} +void v_1_arg_23(int) {} +void v_1_arg_24(int) {} + + +// literal + +#pragma omp declare variant (v_1_arg_0) match (construct={dispatch}) adjust_args (nothing: 1:1) +void b_1_arg_literal_literal_0 (int) {} + +// defaults (lb default is 1, ub default is omp_num_args) + +#pragma omp declare variant (v_1_arg_1) match (construct={dispatch}) adjust_args (nothing: :) +void b_1_arg_default_default (int) {} + +#pragma omp declare variant (v_1_arg_2) match (construct={dispatch}) adjust_args (nothing: :1) +void b_1_arg_default_literal_0 (int) {} + +#pragma omp declare variant (v_1_arg_3) match (construct={dispatch}) adjust_args (nothing: :omp_num_args) +void b_1_arg_default_numargs_0 (int) {} + +#pragma omp declare variant (v_1_arg_4) match (construct={dispatch}) adjust_args (nothing: :omp_num_args+0) +void b_1_arg_default_numargs_1 (int) {} + +#pragma omp declare variant (v_1_arg_5) match (construct={dispatch}) adjust_args (nothing: :omp_num_args-0) +void b_1_arg_default_numargs_2 (int) {} + +#pragma omp declare variant (v_1_arg_6) match (construct={dispatch}) adjust_args (nothing: 1:) +void b_1_arg_literal_default_0 (int) {} + +#pragma omp declare variant (v_1_arg_7) match (construct={dispatch}) adjust_args (nothing: omp_num_args:) +void b_1_arg_numargs_default_0 (int) {} + +#pragma omp declare variant (v_1_arg_8) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:) +void b_1_arg_numargs_default_1 (int) {} + +#pragma omp declare variant (v_1_arg_9) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:) +void b_1_arg_numargs_default_2 (int) {} + + +// literal : omp_num_args+/- + +#pragma omp declare variant (v_1_arg_10) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args) +void b_1_arg_literal_numargs_0 (int) {} + +#pragma omp declare variant (v_1_arg_11) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args+0) +void b_1_arg_literal_numargs_1 (int) {} + +#pragma omp declare variant (v_1_arg_12) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-0) +void b_1_arg_literal_numargs_2 (int) {} + +// omp_num_args+/- : literal + +#pragma omp declare variant (v_1_arg_13) match (construct={dispatch}) adjust_args (nothing: omp_num_args:1) +void b_1_arg_numargs_literal_0 (int) {} + +#pragma omp declare variant (v_1_arg_14) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:1) +void b_1_arg_numargs_literal_1 (int) {} + +#pragma omp declare variant (v_1_arg_15) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:1) +void b_1_arg_numargs_literal_2 (int) {} + +// omp_num_args+/- : omp_num_args+/- +// we need to avoid combinatorial explosion here... + +#pragma omp declare variant (v_1_arg_16) match (construct={dispatch}) adjust_args (nothing: omp_num_args:omp_num_args) +void b_1_arg_numargs_numargs_0_0 (int) {} + +#pragma omp declare variant (v_1_arg_17) match (construct={dispatch}) adjust_args (nothing: omp_num_args:omp_num_args+0) +void b_1_arg_numargs_numargs_0_1 (int) {} + +#pragma omp declare variant (v_1_arg_18) match (construct={dispatch}) adjust_args (nothing: omp_num_args:omp_num_args-0) +void b_1_arg_numargs_numargs_0_2 (int) {} + +#pragma omp declare variant (v_1_arg_19) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:omp_num_args) +void b_1_arg_numargs_numargs_1_0 (int) {} + +#pragma omp declare variant (v_1_arg_20) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:omp_num_args+0) +void b_1_arg_numargs_numargs_1_1 (int) {} + +#pragma omp declare variant (v_1_arg_21) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:omp_num_args-0) +void b_1_arg_numargs_numargs_1_2 (int) {} + +#pragma omp declare variant (v_1_arg_22) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:omp_num_args) +void b_1_arg_numargs_numargs_2_0 (int) {} + +#pragma omp declare variant (v_1_arg_23) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:omp_num_args+0) +void b_1_arg_numargs_numargs_2_1 (int) {} + +#pragma omp declare variant (v_1_arg_24) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:omp_num_args-0) +void b_1_arg_numargs_numargs_2_2 (int) {} + + + +void v_2_arg(int, int) {} + +// literal + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: 1:1) +void b_2_arg_literal_literal_0_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: 1:2) +void b_2_arg_literal_literal_0_1 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: 2:2) +void b_2_arg_literal_literal_1_1 (int, int) {} + +// defaults (lb default is 1, ub default is omp_num_args) + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: :) +void b_2_arg_default_default (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: :1) +void b_2_arg_default_literal_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: :2) +void b_2_arg_default_literal_2 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: :omp_num_args) +void b_2_arg_default_numargs_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: :omp_num_args+0) +void b_2_arg_default_numargs_1 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: :omp_num_args-0) +void b_2_arg_default_numargs_2 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: :omp_num_args-1) +void b_2_arg_default_numargs_3 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: 1:) +void b_2_arg_literal_default_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: 2:) +void b_2_arg_literal_default_1 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:) +void b_2_arg_numargs_default_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:) +void b_2_arg_numargs_default_1 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:) +void b_2_arg_numargs_default_2 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:) +void b_2_arg_numargs_default_3 (int, int) {} + +// literal : omp_num_args+/- + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args) +void b_2_arg_literal_numargs_0_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args+0) +void b_2_arg_literal_numargs_0_1 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-0) +void b_2_arg_literal_numargs_0_2 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) +void b_2_arg_literal_numargs_0_3 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: 2:omp_num_args) +void b_2_arg_literal_numargs_1_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: 2:omp_num_args+0) +void b_2_arg_literal_numargs_1_1 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: 2:omp_num_args-0) +void b_2_arg_literal_numargs_1_2 (int, int) {} + +/* Out of range +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: 2:omp_num_args-1) +void b_2_arg_literal_numargs_1_3 (int, int) {} */ + + +// omp_num_args+/- : literal + +/* Out of range +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:1) +void b_2_arg_numargs_literal_0_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:1) +void b_2_arg_numargs_literal_1_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:1) +void b_2_arg_numargs_literal_2_0 (int, int) {} */ + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:1) +void b_2_arg_numargs_literal_3_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:2) +void b_2_arg_numargs_literal_0_1 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:2) +void b_2_arg_numargs_literal_1_1 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:2) +void b_2_arg_numargs_literal_2_1 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:2) +void b_2_arg_numargs_literal_3_1 (int, int) {} + +// omp_num_args+/- : omp_num_args+/- + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:omp_num_args) +void b_2_arg_numargs_numargs_0_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:omp_num_args+0) +void b_2_arg_numargs_numargs_0_1 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:omp_num_args-0) +void b_2_arg_numargs_numargs_0_2 (int, int) {} +/* Out of range +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:omp_num_args-1) +void b_2_arg_numargs_numargs_0_3 (int, int) {} */ + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:omp_num_args) +void b_2_arg_numargs_numargs_1_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:omp_num_args+0) +void b_2_arg_numargs_numargs_1_1 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:omp_num_args-0) +void b_2_arg_numargs_numargs_1_2 (int, int) {} +/* Out of range +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:omp_num_args-1) +void b_2_arg_numargs_numargs_1_3 (int, int) {} */ + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:omp_num_args) +void b_2_arg_numargs_numargs_2_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:omp_num_args+0) +void b_2_arg_numargs_numargs_2_1 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:omp_num_args-0) +void b_2_arg_numargs_numargs_2_2 (int, int) {} +/* Out of range +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:omp_num_args-1) +void b_2_arg_numargs_numargs_2_3 (int, int) {} */ + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:omp_num_args) +void b_2_arg_numargs_numargs_3_0 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:omp_num_args+0) +void b_2_arg_numargs_numargs_3_1 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:omp_num_args-0) +void b_2_arg_numargs_numargs_3_2 (int, int) {} + +#pragma omp declare variant (v_2_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:omp_num_args-1) +void b_2_arg_numargs_numargs_3_3 (int, int) {} + + + +void v_3_arg(int, int, int) {} + +// literal + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 1:1) +void b_3_arg_literal_literal_0_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 1:2) +void b_3_arg_literal_literal_0_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 1:3) +void b_3_arg_literal_literal_0_2 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 2:2) +void b_3_arg_literal_literal_1_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 2:3) +void b_3_arg_literal_literal_1_2 (int, int, int) {} + +// defaults (lb default is 1, ub default is omp_num_args) + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: :) +void b_3_arg_default_default (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: :1) +void b_3_arg_default_literal_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: :2) +void b_3_arg_default_literal_2 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: :3) +void b_3_arg_default_literal_3 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: :omp_num_args) +void b_3_arg_default_numargs_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: :omp_num_args+0) +void b_3_arg_default_numargs_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: :omp_num_args-0) +void b_3_arg_default_numargs_2 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: :omp_num_args-1) +void b_3_arg_default_numargs_3 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: :omp_num_args-2) +void b_3_arg_default_numargs_4 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 1:) +void b_3_arg_literal_default_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 2:) +void b_3_arg_literal_default_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 3:) +void b_3_arg_literal_default_2 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:) +void b_3_arg_numargs_default_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:) +void b_3_arg_numargs_default_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:) +void b_3_arg_numargs_default_2 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:) +void b_3_arg_numargs_default_3 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-2:) +void b_3_arg_numargs_default_4 (int, int, int) {} + +// literal : omp_num_args+/- + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args) +void b_3_arg_literal_numargs_0_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args+0) +void b_3_arg_literal_numargs_0_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-0) +void b_3_arg_literal_numargs_0_2 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) +void b_3_arg_literal_numargs_0_3 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-2) +void b_3_arg_literal_numargs_0_4 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 2:omp_num_args) +void b_3_arg_literal_numargs_1_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 2:omp_num_args+0) +void b_3_arg_literal_numargs_1_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 2:omp_num_args-0) +void b_3_arg_literal_numargs_1_2 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 2:omp_num_args-1) +void b_3_arg_literal_numargs_1_3 (int, int, int) {} +/* Out of range +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 2:omp_num_args-2) +void b_3_arg_literal_numargs_1_4 (int, int, int) {} */ + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 3:omp_num_args) +void b_3_arg_literal_numargs_2_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 3:omp_num_args+0) +void b_3_arg_literal_numargs_2_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 3:omp_num_args-0) +void b_3_arg_literal_numargs_2_2 (int, int, int) {} +/* Out of range +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 3:omp_num_args-1) +void b_3_arg_literal_numargs_2_3 (int, int, int) {} +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: 3:omp_num_args-2) +void b_3_arg_literal_numargs_2_4 (int, int, int) {} */ + + +// omp_num_args+/- : literal + +/* Out of range +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:1) +void b_3_arg_numargs_literal_0_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:1) +void b_3_arg_numargs_literal_1_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:1) +void b_3_arg_numargs_literal_2_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:1) +void b_3_arg_numargs_literal_3_0 (int, int, int) {} */ + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-2:1) +void b_3_arg_numargs_literal_4_0 (int, int, int) {} + +/* Out of range +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:2) +void b_3_arg_numargs_literal_0_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:2) +void b_3_arg_numargs_literal_1_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:2) +void b_3_arg_numargs_literal_2_1 (int, int, int) {} */ + + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:2) +void b_3_arg_numargs_literal_3_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-2:2) +void b_3_arg_numargs_literal_4_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:3) +void b_3_arg_numargs_literal_0_2 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:3) +void b_3_arg_numargs_literal_1_2 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:3) +void b_3_arg_numargs_literal_2_2 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:3) +void b_3_arg_numargs_literal_3_2 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-2:3) +void b_3_arg_numargs_literal_4_2 (int, int, int) {} + +// omp_num_args+/- : omp_num_args+/- + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:omp_num_args) +void b_3_arg_numargs_numargs_0_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:omp_num_args+0) +void b_3_arg_numargs_numargs_0_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:omp_num_args-0) +void b_3_arg_numargs_numargs_0_2 (int, int, int) {} +/* Out of range +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:omp_num_args-1) +void b_3_arg_numargs_numargs_0_3 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args:omp_num_args-2) +void b_3_arg_numargs_numargs_0_4 (int, int, int) {} */ + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:omp_num_args) +void b_3_arg_numargs_numargs_1_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:omp_num_args+0) +void b_3_arg_numargs_numargs_1_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:omp_num_args-0) +void b_3_arg_numargs_numargs_1_2 (int, int, int) {} +/* Out of range +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:omp_num_args-1) +void b_3_arg_numargs_numargs_1_3 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args+0:omp_num_args-2) +void b_3_arg_numargs_numargs_1_4 (int, int, int) {} */ + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:omp_num_args) +void b_3_arg_numargs_numargs_2_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:omp_num_args+0) +void b_3_arg_numargs_numargs_2_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:omp_num_args-0) +void b_3_arg_numargs_numargs_2_2 (int, int, int) {} +/* Out of range +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:omp_num_args-1) +void b_3_arg_numargs_numargs_2_3 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-0:omp_num_args-2) +void b_3_arg_numargs_numargs_2_4 (int, int, int) {} */ + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:omp_num_args) +void b_3_arg_numargs_numargs_3_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:omp_num_args+0) +void b_3_arg_numargs_numargs_3_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:omp_num_args-0) +void b_3_arg_numargs_numargs_3_2 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:omp_num_args-1) +void b_3_arg_numargs_numargs_3_3 (int, int, int) {} +/* Out of range +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-1:omp_num_args-2) +void b_3_arg_numargs_numargs_3_4 (int, int, int) {} */ + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-2:omp_num_args) +void b_3_arg_numargs_numargs_4_0 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-2:omp_num_args+0) +void b_3_arg_numargs_numargs_4_1 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-2:omp_num_args-0) +void b_3_arg_numargs_numargs_4_2 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-2:omp_num_args-1) +void b_3_arg_numargs_numargs_4_3 (int, int, int) {} + +#pragma omp declare variant (v_3_arg) match (construct={dispatch}) adjust_args (nothing: omp_num_args-2:omp_num_args-2) +void b_3_arg_numargs_numargs_4_4 (int, int, int) {} + + +/* 1-3 args should be fine for now. +void v_4_arg(int, int, int, int) {} +void v_5_arg(int, int, int, int, int) {} */ diff --git a/gcc/testsuite/c-c++-common/gomp/adjust-args-8.c b/gcc/testsuite/c-c++-common/gomp/adjust-args-8.c new file mode 100644 index 0000000..a791ed3 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/adjust-args-8.c @@ -0,0 +1,405 @@ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +/* Test uses of omp_num_args in a variadic function. */ + +/* NOTE: Make sure the arguments passed to the functions have unique names to + not interfere with the dg-final checks. */ + +void v0_0_arg_vari_0(...) {} +void v0_0_arg_vari_1(...) {} +void v0_0_arg_vari_2(...) {} +void v0_0_arg_vari_3(...) {} +void v0_0_arg_vari_4(...) {} +void v0_0_arg_vari_5(...) {} +void v0_0_arg_vari_6(...) {} +void v0_0_arg_vari_7(...) {} + +/* All args adjusted. */ + +// defaults + +#pragma omp declare variant (v0_0_arg_vari_0) match (construct={dispatch}) adjust_args (need_device_ptr: :) +void b_default_default (...) {} + +#pragma omp declare variant (v0_0_arg_vari_1) match (construct={dispatch}) adjust_args (need_device_ptr: :omp_num_args) +void b_default_numargs_0 (...) {} + +#pragma omp declare variant (v0_0_arg_vari_2) match (construct={dispatch}) adjust_args (need_device_ptr: :omp_num_args+0) +void b_default_numargs_1 (...) {} + +#pragma omp declare variant (v0_0_arg_vari_3) match (construct={dispatch}) adjust_args (need_device_ptr: :omp_num_args-0) +void b_default_numargs_2 (...) {} + +#pragma omp declare variant (v0_0_arg_vari_4) match (construct={dispatch}) adjust_args (need_device_ptr: 1:) +void b_literal_default_0 (...) {} + +// literal : omp_num_args+/- + +#pragma omp declare variant (v0_0_arg_vari_5) match (construct={dispatch}) adjust_args (need_device_ptr: 1:omp_num_args) +void b_literal_numargs_0 (...) {} + +#pragma omp declare variant (v0_0_arg_vari_6) match (construct={dispatch}) adjust_args (need_device_ptr: 1:omp_num_args+0) +void b_literal_numargs_1 (...) {} + +#pragma omp declare variant (v0_0_arg_vari_7) match (construct={dispatch}) adjust_args (need_device_ptr: 1:omp_num_args-0) +void b_literal_numargs_2 (...) {} + +/* 8 function calls. */ +#define PASS_ARGS_TO_ALL(...) \ + do { \ + _Pragma("omp dispatch") \ + b_default_default(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_default_numargs_0(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_default_numargs_1(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_default_numargs_2(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_literal_default_0(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_literal_numargs_0(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_literal_numargs_1(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_literal_numargs_2(__VA_ARGS__); \ + } while (0); + +void do_all_args(int *p0) +{ + /* 6 uses of p0, times 8, 6*8=48. */ + /* 3 expansions, times 8, 3*8=24 uses of omp dispatch. */ +/* { dg-final { scan-tree-dump-times "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p0, D\.\[0-9\]+\\);" 48 "gimple" } } */ + PASS_ARGS_TO_ALL (p0); +/* { dg-final { scan-tree-dump-times "v0_0_arg_vari_\[0-7\] \\(D\.\[0-9\]+\\);" 8 "gimple" } } */ + PASS_ARGS_TO_ALL (p0, p0); +/* { dg-final { scan-tree-dump-times "v0_0_arg_vari_\[0-7\] \\(D\.\[0-9\]+, D\.\[0-9\]+\\);" 8 "gimple" } } */ + PASS_ARGS_TO_ALL (p0, p0, p0); +/* { dg-final { scan-tree-dump-times "v0_0_arg_vari_\[0-7\] \\(D\.\[0-9\]+, D\.\[0-9\]+, D\.\[0-9\]+\\);" 8 "gimple" } } */ +} +#undef PASS_ARGS_TO_ALL + +void v1_0_arg_vari_8(...) {} +void v1_0_arg_vari_9(...) {} +void v1_0_arg_vari_10(...) {} + +/* First arg adjusted. */ + +#pragma omp declare variant (v1_0_arg_vari_8) match (construct={dispatch}) adjust_args (need_device_ptr: 1:1) +void b_firstarg_needptr_literal_literal(...) {} + +#pragma omp declare variant (v1_0_arg_vari_9) match (construct={dispatch}) adjust_args (need_device_ptr: 1:1) adjust_args (nothing: 2:omp_num_args) +void b_firstarg_needptr_literal_literal_rest_nothing_literal_numargs(...) {} + +#pragma omp declare variant (v1_0_arg_vari_10) match (construct={dispatch}) adjust_args (nothing: 2:omp_num_args) adjust_args (need_device_ptr: 1:1) +void b_rest_nothing_literal_numargs_firstarg_needptr_literal_literal(...) {} + +void do_first_arg(int *p1) +{ + int a = 42; + /* 7 uses of p1. */ + /* 7 uses of omp dispatch. */ +/* { dg-final { scan-tree-dump-times "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p1, D\.\[0-9\]+\\);" 7 "gimple" } } */ + #pragma omp dispatch + b_firstarg_needptr_literal_literal(p1); + #pragma omp dispatch + b_firstarg_needptr_literal_literal(p1, a); + #pragma omp dispatch + b_firstarg_needptr_literal_literal(p1, a, a); + + #pragma omp dispatch + b_firstarg_needptr_literal_literal_rest_nothing_literal_numargs(p1, a); + #pragma omp dispatch + b_firstarg_needptr_literal_literal_rest_nothing_literal_numargs(p1, a, a); + + #pragma omp dispatch + b_rest_nothing_literal_numargs_firstarg_needptr_literal_literal(p1, a); + #pragma omp dispatch + b_rest_nothing_literal_numargs_firstarg_needptr_literal_literal(p1, a, a); +/* { dg-final { scan-tree-dump-times "v1_0_arg_vari_1?\[089\] \\(D\.\[0-9\]+(?:, a){0,2}\\);" 7 "gimple" } } */ +} + +/* Last arg adjusted. */ + +void v2_0_arg_vari_11(...) {} +void v2_0_arg_vari_12(...) {} +void v2_0_arg_vari_13(...) {} +void v2_0_arg_vari_14(...) {} +void v2_0_arg_vari_15(...) {} +void v2_0_arg_vari_16(...) {} +void v2_0_arg_vari_17(...) {} +void v2_0_arg_vari_18(...) {} +void v2_0_arg_vari_19(...) {} +void v2_0_arg_vari_20(...) {} +void v2_0_arg_vari_21(...) {} +void v2_0_arg_vari_22(...) {} + +#pragma omp declare variant (v2_0_arg_vari_11) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args:) +void b_lastarg_needptr_numargs_default_0 (...) {} + +#pragma omp declare variant (v2_0_arg_vari_12) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args+0:) +void b_lastarg_needptr_numargs_default_1 (...) {} + +#pragma omp declare variant (v2_0_arg_vari_13) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args-0:) +void b_lastarg_needptr_numargs_default_2 (...) {} + +// omp_num_args+/- : omp_num_args+/- + +#pragma omp declare variant (v2_0_arg_vari_14) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args:omp_num_args) +void b_lastarg_needptr_numargs_numargs_0_0 (...) {} + +#pragma omp declare variant (v2_0_arg_vari_15) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args:omp_num_args+0) +void b_lastarg_needptr_numargs_numargs_0_1 (...) {} + +#pragma omp declare variant (v2_0_arg_vari_16) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args:omp_num_args-0) +void b_lastarg_needptr_numargs_numargs_0_2 (...) {} + +#pragma omp declare variant (v2_0_arg_vari_17) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args+0:omp_num_args) +void b_lastarg_needptr_numargs_numargs_1_0 (...) {} + +#pragma omp declare variant (v2_0_arg_vari_18) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args+0:omp_num_args+0) +void b_lastarg_needptr_numargs_numargs_1_1 (...) {} + +#pragma omp declare variant (v2_0_arg_vari_19) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args+0:omp_num_args-0) +void b_lastarg_needptr_numargs_numargs_1_2 (...) {} + +#pragma omp declare variant (v2_0_arg_vari_20) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args-0:omp_num_args) +void b_lastarg_needptr_numargs_numargs_2_0 (...) {} + +#pragma omp declare variant (v2_0_arg_vari_21) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args-0:omp_num_args+0) +void b_lastarg_needptr_numargs_numargs_2_1 (...) {} + +#pragma omp declare variant (v2_0_arg_vari_22) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args-0:omp_num_args-0) +void b_lastarg_needptr_numargs_numargs_2_2 (...) {} + +/* 12 function calls. */ +#define PASS_ARGS_TO_ALL(...) \ + do { \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_default_0(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_default_1(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_default_2(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_0_0(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_0_1(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_0_2(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_1_0(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_1_1(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_1_2(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_2_0(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_2_1(__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_2_2(__VA_ARGS__); \ + } while (0) + +void do_lastarg_0(int *p2) +{ + int a = 42; + /* 3 uses of p2, times 12, 3*12=36. */ + /* 3 expansions, times 12, 3*12=36 uses of omp dispatch. */ +/* { dg-final { scan-tree-dump-times "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p2, D\.\[0-9\]+\\);" 36 "gimple" } } */ + PASS_ARGS_TO_ALL (p2); + PASS_ARGS_TO_ALL (a, p2); + PASS_ARGS_TO_ALL (a, a, p2); +/* { dg-final { scan-tree-dump-times "v2_0_arg_vari_\[12\]\[0-9\] \\((?:a, ){0,2}D\.\[0-9\]+\\);" 36 "gimple" } } */ +} + +#undef PASS_ARGS_TO_ALL + +void v3_0_arg_vari_23(...) {} +void v3_0_arg_vari_24(...) {} +void v3_0_arg_vari_25(...) {} +void v3_0_arg_vari_26(...) {} +void v3_0_arg_vari_27(...) {} +void v3_0_arg_vari_28(...) {} +void v3_0_arg_vari_29(...) {} +void v3_0_arg_vari_30(...) {} +void v3_0_arg_vari_31(...) {} +void v3_0_arg_vari_32(...) {} +void v3_0_arg_vari_33(...) {} +void v3_0_arg_vari_34(...) {} +void v3_0_arg_vari_35(...) {} +void v3_0_arg_vari_36(...) {} +void v3_0_arg_vari_37(...) {} +void v3_0_arg_vari_38(...) {} +void v3_0_arg_vari_39(...) {} +void v3_0_arg_vari_40(...) {} +void v3_0_arg_vari_41(...) {} +void v3_0_arg_vari_42(...) {} +void v3_0_arg_vari_43(...) {} +void v3_0_arg_vari_44(...) {} +void v3_0_arg_vari_45(...) {} +void v3_0_arg_vari_46(...) {} + +#pragma omp declare variant (v3_0_arg_vari_23) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args:) adjust_args (nothing: 1:omp_num_args-1) +void b_lastarg_needptr_numargs_default_rest_nothing_literal_numargs_0 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_24) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args+0:) adjust_args (nothing: 1:omp_num_args-1) +void b_lastarg_needptr_numargs_default_rest_nothing_literal_numargs_1 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_25) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args-0:) adjust_args (nothing: 1:omp_num_args-1) +void b_lastarg_needptr_numargs_default_rest_nothing_literal_numargs_2 (...) {} + +// omp_num_args+/- : omp_num_args+/- + +#pragma omp declare variant (v3_0_arg_vari_26) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args:omp_num_args) adjust_args (nothing: 1:omp_num_args-1) +void b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_0_0 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_27) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args:omp_num_args+0) adjust_args (nothing: 1:omp_num_args-1) +void b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_0_1 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_28) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args:omp_num_args-0) adjust_args (nothing: 1:omp_num_args-1) +void b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_0_2 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_29) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args+0:omp_num_args) adjust_args (nothing: 1:omp_num_args-1) +void b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_1_0 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_30) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args+0:omp_num_args+0) adjust_args (nothing: 1:omp_num_args-1) +void b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_1_1 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_31) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args+0:omp_num_args-0) adjust_args (nothing: 1:omp_num_args-1) +void b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_1_2 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_32) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args-0:omp_num_args) adjust_args (nothing: 1:omp_num_args-1) +void b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_2_0 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_33) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args-0:omp_num_args+0) adjust_args (nothing: 1:omp_num_args-1) +void b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_2_1 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_34) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args-0:omp_num_args-0) adjust_args (nothing: 1:omp_num_args-1) +void b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_2_2 (...) {} + + +/* same as above section with clauses reversed */ + +#pragma omp declare variant (v3_0_arg_vari_35) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) adjust_args (need_device_ptr: omp_num_args:) +void b_rest_nothing_literal_numargs_lastarg_needptr_numargs_default_0 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_36) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) adjust_args (need_device_ptr: omp_num_args+0:) +void b_rest_nothing_literal_numargs_lastarg_needptr_numargs_default_1 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_37) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) adjust_args (need_device_ptr: omp_num_args-0:) +void b_rest_nothing_literal_numargs_lastarg_needptr_numargs_default_2 (...) {} + +// omp_num_args+/- : omp_num_args+/- + +#pragma omp declare variant (v3_0_arg_vari_38) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) adjust_args (need_device_ptr: omp_num_args:omp_num_args) +void b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_0_0 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_39) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) adjust_args (need_device_ptr: omp_num_args:omp_num_args+0) +void b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_0_1 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_40) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) adjust_args (need_device_ptr: omp_num_args:omp_num_args-0) +void b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_0_2 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_41) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) adjust_args (need_device_ptr: omp_num_args+0:omp_num_args) +void b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_1_0 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_42) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) adjust_args (need_device_ptr: omp_num_args+0:omp_num_args+0) +void b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_1_1 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_43) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) adjust_args (need_device_ptr: omp_num_args+0:omp_num_args-0) +void b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_1_2 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_44) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) adjust_args (need_device_ptr: omp_num_args-0:omp_num_args) +void b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_2_0 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_45) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) adjust_args (need_device_ptr: omp_num_args-0:omp_num_args+0) +void b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_2_1 (...) {} + +#pragma omp declare variant (v3_0_arg_vari_46) match (construct={dispatch}) adjust_args (nothing: 1:omp_num_args-1) adjust_args (need_device_ptr: omp_num_args-0:omp_num_args-0) +void b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_2_2 (...) {} + +/* 24 function calls. */ +#define PASS_ARGS_TO_ALL(...) \ + do { \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_default_rest_nothing_literal_numargs_0 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_default_rest_nothing_literal_numargs_1 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_default_rest_nothing_literal_numargs_2 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_0_0 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_0_1 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_0_2 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_1_0 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_1_1 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_1_2 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_2_0 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_2_1 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_lastarg_needptr_numargs_numargs_rest_nothing_literal_numargs_2_2 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_rest_nothing_literal_numargs_lastarg_needptr_numargs_default_0 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_rest_nothing_literal_numargs_lastarg_needptr_numargs_default_1 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_rest_nothing_literal_numargs_lastarg_needptr_numargs_default_2 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_0_0 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_0_1 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_0_2 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_1_0 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_1_1 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_1_2 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_2_0 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_2_1 (__VA_ARGS__); \ + _Pragma("omp dispatch") \ + b_rest_nothing_literal_numargs_lastarg_needptr_numargs_numargs_2_2 (__VA_ARGS__); \ + } while (0) + +void do_lastarg_1(int *p3) +{ + int a = 42; + /* 3 uses of p3, times 24, 3*24=72. */ + /* 3 expansions, times 24, 3*24=72 uses of omp dispatch. */ + /* Can't pass a single arg to this one. */ + PASS_ARGS_TO_ALL (a, p3); + PASS_ARGS_TO_ALL (a, a, p3); + PASS_ARGS_TO_ALL (a, a, a, p3); +/* { dg-final { scan-tree-dump-times "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p3, D\.\[0-9\]+\\);" 72 "gimple" } } */ +/* { dg-final { scan-tree-dump-times "v3_0_arg_vari_\[2-4\]\[0-9\] \\((?:a, ){1,3}D\.\[0-9\]+\\);" 72 "gimple" } } */ +} +#undef PASS_ARGS_TO_ALL + +/* 24 + 7 + 36 + 72 = 139. */ +/* { dg-final { scan-tree-dump-times "D\.\[0-9\]+ = __builtin_omp_get_default_device \\(\\);" 139 "gimple" } } */ + + +/* Lots of these cases are invalid (depending on the value of the literal), + thus they go somewhere else. */ + +// omp_num_args+/- : literal + +// #pragma omp declare variant (N/A) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args:1) +// void b_numargs_literal_0 (...) {} + +// #pragma omp declare variant (N/A) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args+0:1) +// void b_numargs_literal_1 (...) {} + +// #pragma omp declare variant (N/A) match (construct={dispatch}) adjust_args (need_device_ptr: omp_num_args-0:1) +// void b_numargs_literal_2 (...) {} diff --git a/gcc/testsuite/c-c++-common/gomp/adjust-args-9.c b/gcc/testsuite/c-c++-common/gomp/adjust-args-9.c new file mode 100644 index 0000000..8b3aa24 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/adjust-args-9.c @@ -0,0 +1,125 @@ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +/* Test uses of parameter index in a function. + TODO: cases with variadic functions, + cases referring to variadic arguments. */ + +#define NUMBER_ONE 1 +#define NUMBER_TWO 2 +#define NUMBER_THREE 3 + +void v_ptr(int *) {} +void macro_v_ptr(int *) {} + +#pragma omp declare variant (v_ptr) match (construct={dispatch}) adjust_args (need_device_ptr: 1) +void b_ptr (int *) {} + +#pragma omp declare variant (macro_v_ptr) match (construct={dispatch}) adjust_args (need_device_ptr: NUMBER_ONE) +void macro_b_ptr (int *) {} + + +void v_ptr_val(int *, int) {} +void macro_v_ptr_val(int *, int) {} + +#pragma omp declare variant (v_ptr_val) match (construct={dispatch}) adjust_args (need_device_ptr: 1) +void b_ptr_val (int *, int) {} + +#pragma omp declare variant (macro_v_ptr_val) match (construct={dispatch}) adjust_args (need_device_ptr: NUMBER_ONE) +void macro_b_ptr_val (int *, int) {} + + +void v_val_ptr(int, int *) {} +void macro_v_val_ptr(int, int *) {} + +#pragma omp declare variant (v_val_ptr) match (construct={dispatch}) adjust_args (need_device_ptr: 2) +void b_val_ptr (int, int *) {} + +#pragma omp declare variant (macro_v_val_ptr) match (construct={dispatch}) adjust_args (need_device_ptr: NUMBER_TWO) +void macro_b_val_ptr (int, int *) {} + + +void v_ptr_val_val(int *, int, int) {} +void macro_v_ptr_val_val(int *, int, int) {} + +#pragma omp declare variant (v_ptr_val_val) match (construct={dispatch}) adjust_args (need_device_ptr: 1) +void b_ptr_val_val (int *, int, int) {} + +#pragma omp declare variant (macro_v_ptr_val_val) match (construct={dispatch}) adjust_args (need_device_ptr: NUMBER_ONE) +void macro_b_ptr_val_val (int *, int, int) {} + + +void v_val_ptr_val(int, int *, int) {} +void macro_v_val_ptr_val(int, int *, int) {} + +#pragma omp declare variant (v_val_ptr_val) match (construct={dispatch}) adjust_args (need_device_ptr: 2) +void b_val_ptr_val (int, int *, int) {} + +#pragma omp declare variant (macro_v_val_ptr_val) match (construct={dispatch}) adjust_args (need_device_ptr: NUMBER_TWO) +void macro_b_val_ptr_val (int, int *, int) {} + + +void v_val_val_ptr(int, int, int *) {} +void macro_v_val_val_ptr(int, int, int *) {} + +#pragma omp declare variant (v_val_val_ptr) match (construct={dispatch}) adjust_args (need_device_ptr: 3) +void b_val_val_ptr (int, int, int *) {} + +#pragma omp declare variant (macro_v_val_val_ptr) match (construct={dispatch}) adjust_args (need_device_ptr: NUMBER_THREE) +void macro_b_val_val_ptr (int, int, int *) {} + + +void f(int *p0, int *p1, int *p2, int *p3, int *p4, + int *p5, int *p6, int *p7, int *p8, int *p9, + int *pA, int *pB) +{ + #pragma omp dispatch + b_ptr (p0); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p0, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v_ptr \\(D\.\[0-9\]+\\);" "gimple" } } */ + #pragma omp dispatch + b_ptr_val (p1, 4); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p1, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v_ptr_val \\(D\.\[0-9\]+, 4\\);" "gimple" } } */ + #pragma omp dispatch + b_val_ptr (4, p2); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p2, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v_val_ptr \\(4, D\.\[0-9\]+\\);" "gimple" } } */ + #pragma omp dispatch + b_ptr_val_val (p3, 4, 4); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p3, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v_ptr_val_val \\(D\.\[0-9\]+, 4, 4\\);" "gimple" } } */ + #pragma omp dispatch + b_val_ptr_val (4, p4, 4); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p4, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v_val_ptr_val \\(4, D\.\[0-9\]+, 4\\);" "gimple" } } */ + #pragma omp dispatch + b_val_val_ptr (4, 4, p5); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p5, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v_val_val_ptr \\(4, 4, D\.\[0-9\]+\\);" "gimple" } } */ + + #pragma omp dispatch + macro_b_ptr (p6); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p6, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "macro_v_ptr \\(D\.\[0-9\]+\\);" "gimple" } } */ + #pragma omp dispatch + macro_b_ptr_val (p7, 4); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p7, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "macro_v_ptr_val \\(D\.\[0-9\]+, 4\\);" "gimple" } } */ + #pragma omp dispatch + macro_b_val_ptr (4, p8); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p8, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "macro_v_val_ptr \\(4, D\.\[0-9\]+\\);" "gimple" } } */ + #pragma omp dispatch + macro_b_ptr_val_val (p9, 4, 4); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p9, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "macro_v_ptr_val_val \\(D\.\[0-9\]+, 4, 4\\);" "gimple" } } */ + #pragma omp dispatch + macro_b_val_ptr_val (4, pA, 4); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(pA, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "macro_v_val_ptr_val \\(4, D\.\[0-9\]+, 4\\);" "gimple" } } */ + #pragma omp dispatch + macro_b_val_val_ptr (4, 4, pB); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(pB, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "macro_v_val_val_ptr \\(4, 4, D\.\[0-9\]+\\);" "gimple" } } */ +} +/* { dg-final { scan-tree-dump-times "D\.\[0-9\]+ = __builtin_omp_get_default_device \\(\\);" 12 "gimple" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/pr118579.c b/gcc/testsuite/c-c++-common/gomp/pr118579.c index 2a96085..5d2de9a 100644 --- a/gcc/testsuite/c-c++-common/gomp/pr118579.c +++ b/gcc/testsuite/c-c++-common/gomp/pr118579.c @@ -6,7 +6,8 @@ void fvar(int *, int *); #pragma omp declare variant(fvar) \ match(construct={dispatch}) \ adjust_args(need_device_ptr: yyy, xxx, xxx) -/* { dg-error "37: .xxx. is specified more than once" "" { target *-*-* } .-1 } */ +/* { dg-error "42: OpenMP parameter list items must specify a unique parameter" "" { target *-*-* } .-1 } */ +/* { dg-note "37: parameter previously specified here" "" { target *-*-* } .-2 } */ void f(int *xxx, int*yyy); diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-1.C b/gcc/testsuite/g++.dg/gomp/adjust-args-1.C index 29fde14..3aee78e 100644 --- a/gcc/testsuite/g++.dg/gomp/adjust-args-1.C +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-1.C @@ -1,8 +1,6 @@ /* Test parsing of OMP clause adjust_args */ /* { dg-do compile } */ -int b; - int f0 (void *a); int g (void *a); int f1 (int); @@ -23,17 +21,28 @@ int f4 (void *a); 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" } */ +#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (nothing:) /* { dg-error "expected primary-expression 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" } */ +#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (nothing: z) /* { dg-error "'z' is not a function parameter" } */ 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" } */ +#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (need_device_ptr: a) /* { dg-note "parameter specified here" } */ +int f9 (int a); /* { dg-error "parameter specified in an 'adjust_args' clause with the 'need_device_ptr' modifier must be of pointer type" } */ +#pragma omp declare variant (f1) match (construct={dispatch}) \ + adjust_args (nothing: a) \ + adjust_args (nothing: a) 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" } */ +/* { dg-note "parameter previously specified here" "" { target *-*-* } .-3 } */ +/* { dg-error "OpenMP parameter list items must specify a unique parameter" "" { target *-*-* } .-3 } */ +#pragma omp declare variant (g) match (construct={dispatch}) \ + adjust_args (nothing: a) \ + adjust_args (need_device_ptr: a) 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" } */ +/* { dg-note "parameter previously specified here" "" { target *-*-* } .-3 } */ +/* { dg-error "OpenMP parameter list items must specify a unique parameter" "" { target *-*-* } .-3 } */ + +int b; + +#pragma omp declare variant (g) match (construct={dispatch}) adjust_args (need_device_ptr: b) /* { dg-error "'b' is not a function parameter" } */ 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'" } */ +#pragma omp declare variant (g) match (construct={dispatch}) adjust_args (need_device_ptr: this) /* { dg-error "expected unqualified-id, integer, or expression before 'this'" } */ int f13 (void *a); diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-10.C b/gcc/testsuite/g++.dg/gomp/adjust-args-10.C new file mode 100644 index 0000000..b2ea5f8 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-10.C @@ -0,0 +1,56 @@ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +/* Valid constexpr variable/NTTP in numeric range. + TODO: add tests including multiple instantiations of the same function template. */ + +const int G = 2; + +template<int> +struct S1 {}; + +template<int, int> +struct S2 {}; + + +void v0(int *, int *) {} + +#pragma omp declare variant(v0) match(construct={dispatch}) \ + adjust_args(need_device_ptr: G:omp_num_args) +void b0(int *, int *) {} + + +template<typename T> +void v1(T, int *, int *) {} + +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: V:omp_num_args) +template<int V> +void b1(S1<V>, int *, int *) {} + + +template<typename T> +void v2(T, int *, int *, int *) {} + +#pragma omp declare variant(v2) match(construct={dispatch}) \ + adjust_args(need_device_ptr: LB:UB) +template<int LB, int UB> +void b2(S2<LB, UB>, int *, int *, int *) {} + + +void f(int *p0, int *p1, int *p2, int *p3, int *p4, int *p5, int *p6) +{ + #pragma omp dispatch + b0(p0, p1); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p1, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v0 \\(p0, D\.\[0-9\]+\\);" "gimple" } } */ + #pragma omp dispatch + b1(S1<3>(), p2, p3); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p3, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v1<S1<3> > \\(D\.\[0-9\]+, p2, D\.\[0-9\]+\\);" "gimple" } } */ + #pragma omp dispatch + b2(S2<2, 3>(), p4, p5, p6); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p4, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p5, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v2<S2<2, 3> > \\(D\.\[0-9\]+, D\.\[0-9\]+, D\.\[0-9\]+, p6\\);" "gimple" } } */ +} +/* { dg-final { scan-tree-dump-times "D\.\[0-9\]+ = __builtin_omp_get_default_device \\(\\);" 3 "gimple" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-11.C b/gcc/testsuite/g++.dg/gomp/adjust-args-11.C new file mode 100644 index 0000000..2b91759 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-11.C @@ -0,0 +1,112 @@ +/* { dg-do compile { target c++11 } } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +/* Valid constexpr variable/NTTP in numeric range in functions + with a parameter pack. */ + +template<int, int> +struct S2 {}; + +template<typename T, typename... Ts> +void v0(T, Ts...) {} + +#pragma omp declare variant(v0) match(construct={dispatch}) \ + adjust_args(need_device_ptr: LB:UB) +template<int LB, int UB, typename... Ts> +void b0(S2<LB, UB>, Ts...) {} + + +void f0(int *p0, int *p1, int *p2) +{ + #pragma omp dispatch + b0(S2<2, 3>{}, p0, p1, p2); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p0, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p1, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v0<S2<2, 3>, int\\*, int\\*, int\\*> \\(D\.\[0-9\]+, D\.\[0-9\]+, D\.\[0-9\]+, p2\\);" "gimple" } } */ +} + + +/* With multiple instantiations. */ + +template<typename T, typename... Ts> +void v1(T, Ts...) {} + +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: V0+0:V1+0) +template<int V0, int V1, typename... Ts> +void b1(S2<V0, V1>, Ts...) {} + +void f1(int *f1_p0, int *f1_p1, int *f1_p2, int *f1_p3, int *f1_p4) +{ + #pragma omp dispatch + b1(S2<3, 3>{}, f1_p0, f1_p1); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f1_p1, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v1<S2<3, 3>, int\\*, int\\*> \\(D\.\[0-9\]+, f1_p0, D\.\[0-9\]+\\);" "gimple" } } */ + #pragma omp dispatch + b1(S2<4, 4>{}, f1_p2, f1_p3, f1_p4); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f1_p4, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v1<S2<4, 4>, int\\*, int\\*, int\\*> \\(D\.\[0-9\]+, f1_p2, f1_p3, D\.\[0-9\]+\\);" "gimple" } } */ +} + + + +/* Multiple instantiations of function with parameter packs + with an adjust_args clause with a relative numeric range. */ + +template<typename... Ts> +void v2(Ts...) {} + +#pragma omp declare variant(v2) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:omp_num_args) +template<typename... Ts> +void b2(Ts...) {} + +void f2(int *f2_p0, int *f2_p1, int *f2_p2, int *f2_p3, int *f2_p4, int *f2_p5) +{ + #pragma omp dispatch + b2(f2_p0); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f2_p0, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v2<int\\*> \\(D\.\[0-9\]+\\);" "gimple" } } */ + #pragma omp dispatch + b2(f2_p1, f2_p2); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f2_p1, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f2_p2, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v2<int\\*, int\\*> \\(D\.\[0-9\]+, D\.\[0-9\]+\\);" "gimple" } } */ + #pragma omp dispatch + b2(f2_p3, f2_p4, f2_p5); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f2_p3, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f2_p4, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f2_p5, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v2<int\\*, int\\*, int\\*> \\(D\.\[0-9\]+, D\.\[0-9\]+, D\.\[0-9\]+\\);" "gimple" } } */ +} + + +template<typename... Ts> +void v3(Ts...) {} + +#pragma omp declare variant(v3) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:omp_num_args) +template<typename... Ts> +void b3(Ts...) {} + +void f3(int *f3_p0, int *f3_p1, int *f3_p2, int *f3_p3, int *f3_p4, int *f3_p5) +{ + #pragma omp dispatch + b3(f3_p0, f3_p1, f3_p2); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f3_p0, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f3_p1, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f3_p2, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v3<int\\*, int\\*, int\\*> \\(D\.\[0-9\]+, D\.\[0-9\]+, D\.\[0-9\]+\\);" "gimple" } } */ + #pragma omp dispatch + b3(f3_p3, f3_p4); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f3_p3, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f3_p4, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v3<int\\*, int\\*> \\(D\.\[0-9\]+, D\.\[0-9\]+\\);" "gimple" } } */ + #pragma omp dispatch + b3(f3_p5); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(f3_p5, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "v3<int\\*> \\(D\.\[0-9\]+\\);" "gimple" } } */ +} + + +/* { dg-final { scan-tree-dump-times "D\.\[0-9\]+ = __builtin_omp_get_default_device \\(\\);" 9 "gimple" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-12.C b/gcc/testsuite/g++.dg/gomp/adjust-args-12.C new file mode 100644 index 0000000..1c7baf7 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-12.C @@ -0,0 +1,62 @@ +/* Invalid value constexpr var/NTTP in numeric range in functions. + TODO: Add more cases. */ + +template<int> +struct S1 {}; + +const int G0 = 0; +const int G1 = 2; + + +void v_g0(int *) {} + +/* { dg-error "expression of bound must be positive" "" { target *-*-* } .+2 } */ +#pragma omp declare variant(v_g0) match(construct={dispatch}) \ + adjust_args(need_device_ptr: G0+0:omp_num_args) +void b_g0(int *) {} + +void v_g1(int *) {} + +/* { dg-error "expression of bound is out of range" "" { target *-*-* } .+2 } */ +#pragma omp declare variant(v_g1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: G1+0:omp_num_args) +void b_g1(int *) {} + + +template<typename T> +void v0(T, int *) {} + +#pragma omp declare variant(v0) match(construct={dispatch}) \ + adjust_args(need_device_ptr: V+0:omp_num_args) +template<int V> +void b0(S1<V>, int *) {} + +template<typename T> +void v1(T, int *) {} + +/* { dg-error "expression of bound must be positive" "" { target *-*-* } .+2 } */ +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: V+0:omp_num_args) +template<int V> +void b1(S1<V>, int *) {} + +template<typename T> +void v2(T, int *) {} + +/* { dg-error "expression of bound is out of range" "" { target *-*-* } .+2 } */ +#pragma omp declare variant(v2) match(construct={dispatch}) \ + adjust_args(need_device_ptr: V+0:omp_num_args) +template<int V> +void b2(S1<V>, int *) {} + +void f0(int *p0) +{ + /* Not out of range. */ + #pragma omp dispatch + b0(S1<2>(), p0); /* { dg-bogus "required from here" } */ + /* Out of range. */ + #pragma omp dispatch + b1(S1<0>(), p0); /* { dg-message "required from here" } */ + #pragma omp dispatch + b2(S1<3>(), p0); /* { dg-message "required from here" } */ +} diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-13.C b/gcc/testsuite/g++.dg/gomp/adjust-args-13.C new file mode 100644 index 0000000..5e183e0 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-13.C @@ -0,0 +1,95 @@ +/* { dg-do compile { target c++11 } } */ + +/* Invalid value constexpr var/NTTP in numeric range in functions + with a parameter pack. + TODO: Add more cases. */ + +template<int> +struct S1 {}; + +const int G0 = 0; +const int G1 = 2; + + +template<typename... Ts> +void v_g0(int *, Ts...) {} +template<typename... Ts> +void v_g1(int *, Ts...) {} +template<typename... Ts> +void v_g2(int *, Ts...) {} + + +/* Always invalid, should diagnose without instantiation. */ +/* { dg-error "expression of bound must be positive" "" { target *-*-* } .+2 } */ +#pragma omp declare variant(v_g0) match(construct={dispatch}) \ + adjust_args(need_device_ptr: G0+0:omp_num_args) +template<typename... Ts> +void b_g0(int *, Ts...) {} + +#pragma omp declare variant(v_g1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: G1+0:omp_num_args) +template<typename... Ts> +void b_g1(int *, Ts...) {} + +/* { dg-error "numeric range lower bound must be less than or equal to upper bound" "" { target *-*-* } .+2 } */ +#pragma omp declare variant(v_g2) match(construct={dispatch}) \ + adjust_args(need_device_ptr: G1+0:omp_num_args) +template<typename... Ts> +void b_g2(int *, Ts...) {} + + +void f0 (int *p0) +{ + /* All 3 of these have a fixed lb, equal to G1. */ + /* In range, no error. */ + #pragma omp dispatch + b_g1(p0, p0, p0); + #pragma omp dispatch + b_g2(p0, p0, p0); + /* Out of range. */ + #pragma omp dispatch + b_g2(p0); /* { dg-message "required from here" } */ +} + + +template<typename T, typename... Ts> +void v0(T, Ts...) {} + +/* { dg-error "expression of bound must be positive" "" { target *-*-* } .+2 } */ +#pragma omp declare variant(v0) match(construct={dispatch}) \ + adjust_args(need_device_ptr: V+0:omp_num_args) +template<int V, typename... Ts> +void b0(S1<V>, Ts...) {} + +template<typename T, typename... Ts> +void v1(T, Ts...) {} + +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: V+0:omp_num_args) +template<int V, typename... Ts> +void b1(S1<V>, Ts...) {} + +template<typename T, typename... Ts> +void v2(T, Ts...) {} + +/* { dg-error "expression of bound is out of range" "" { target *-*-* } .+2 } */ +#pragma omp declare variant(v2) match(construct={dispatch}) \ + adjust_args(need_device_ptr: V+0:omp_num_args) +template<int V, typename... Ts> +void b2(S1<V>, Ts...) {} + +void f1(int *p0, int *p1, int *p2) +{ + #pragma omp dispatch + b0(S1<0>{}, p0, p1, p2); /* { dg-message "required from here" } */ + /* In range, no error. */ + #pragma omp dispatch + b1(S1<2>{}, p0); /* { dg-bogus "required from here" } */ + #pragma omp dispatch + b1(S1<4>{}, p0, p1, p2); /* { dg-bogus "required from here" } */ + /* Out of range. */ + #pragma omp dispatch + b2(S1<3>{}, p0); /* { dg-message "required from here" } */ + #pragma omp dispatch + b2(S1<5>{}, p0, p1, p2); /* { dg-message "required from here" } */ +} diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-14.C b/gcc/testsuite/g++.dg/gomp/adjust-args-14.C new file mode 100644 index 0000000..e5a6322 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-14.C @@ -0,0 +1,24 @@ +/* Make sure non-dependent nothing list items are not removed too early. + The order of the adjust_args clauses is important for this test, there was a + bug where we were removing nothing list-items before we encountered a + dependent item. */ + +template<int> +struct S {}; + +template<typename T> +void v0 (T) {} + +/* { dg-note "parameter previously specified here" "" { target *-*-* } .+3 } */ +/* { dg-error "expansion of numeric range specifies non-unique index 1" "" { target *-*-* } .+3 } */ +#pragma omp declare variant(v0) match(construct={dispatch}) \ + adjust_args(nothing: 1) \ + adjust_args(nothing: V+0:V+0) +template<int V> +void b0 (S<V>) {} + +void f () +{ + #pragma omp dispatch + b0 (S<1>()); +} diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-15.C b/gcc/testsuite/g++.dg/gomp/adjust-args-15.C new file mode 100644 index 0000000..63c954d --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-15.C @@ -0,0 +1,23 @@ +/* { dg-do compile { target c++11 } } */ + +/* Make sure non-dependent nothing list items are not removed too early. + The order of the adjust_args clauses is important for this test, there was a + bug where we were removing nothing list-items before we encountered a + dependent item. */ + +template<typename... Ts> +void v1 (int, Ts...) {} + +/* { dg-note "parameter previously specified here" "" { target *-*-* } .+3 } */ +/* { dg-error "expansion of numeric range specifies non-unique index 1" "" { target *-*-* } .+3 } */ +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(nothing: 1) \ + adjust_args(nothing: 1:omp_num_args) +template<typename... Ts> +void b1 (int, Ts...) {} + +void f () +{ + #pragma omp dispatch + b1 (42, 42); +} diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-16.C b/gcc/testsuite/g++.dg/gomp/adjust-args-16.C new file mode 100644 index 0000000..c46870c --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-16.C @@ -0,0 +1,30 @@ +/* Reject expressions outside of a numeric range. */ + +void v_0(int, int, int) {} +void v_1(int, int, int) {} +void v_2(int, int, int) {} +void v_3(int, int, int) {} + +const int constant_expression = 42; + +/* { dg-error "expected ':' before '\\)' token" "" { target *-*-* } .+2 } */ +/* { dg-note "an expression is only allowed in a numeric range" "" { target *-*-* } .+1 } */ +#pragma omp declare variant (v_0) match (construct={dispatch}) adjust_args (nothing: 0+1) +void b0 (int, int, int) {} + +/* { dg-error "'constant_expression' is not a function parameter" "" { target *-*-* } .+2 } */ +/* { dg-note "an expression is only allowed in a numeric range" "" { xfail *-*-* } .+1 } */ +#pragma omp declare variant (v_1) match (construct={dispatch}) adjust_args (nothing: constant_expression) +void b1 (int, int, int) {} + +/* { dg-error "expected ':' before '\\)' token" "" { target *-*-* } .+2 } */ +/* { dg-note "an expression is only allowed in a numeric range" "" { target *-*-* } .+1 } */ +#pragma omp declare variant (v_2) match (construct={dispatch}) adjust_args (nothing: constant_expression + 0) +void b2 (int, int, int) {} + +/* { dg-error "expected ':' before '\\)' token" "" { target *-*-* } .+2 } */ +/* { dg-note "an expression is only allowed in a numeric range" "" { target *-*-* } .+1 } */ +#pragma omp declare variant (v_3) match (construct={dispatch}) adjust_args (nothing: 0 + constant_expression) +void b3 (int, int, int) {} + +/* We could use some tests to make sure non constant-expressions get diagnosed nicely. */ diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-2.C b/gcc/testsuite/g++.dg/gomp/adjust-args-2.C index a78f06e..73d6548 100644 --- a/gcc/testsuite/g++.dg/gomp/adjust-args-2.C +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-2.C @@ -7,12 +7,13 @@ struct S { 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) +#pragma omp declare variant (f0) match (construct={dispatch}) adjust_args (need_device_ptr: a, b) /* { dg-note "parameter specified here" } */ template <typename T> -T f1(T a, T *b); +T f1(T a, T *b); /* { dg-error "parameter specified in an 'adjust_args' clause with the 'need_device_ptr' modifier must be of pointer type" } */ namespace N { class C{ @@ -29,8 +30,8 @@ namespace N { #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); +#pragma omp declare variant (f4) match (construct={dispatch}) adjust_args (need_device_ptr: s) /* { dg-note "parameter specified here" } */ +void f5(S *&s); /* { dg-message "parameter with type reference to pointer in an 'adjust_args' with the 'need_device_ptr' modifier is not currently supported" } */ void test() { S s, *sp; @@ -39,7 +40,7 @@ void test() { #pragma omp dispatch s.f0(a); #pragma omp dispatch - f1(b, a); + f1(b, a); /* { dg-message "required from here" } */ #pragma omp dispatch c.f0(&c); #pragma omp dispatch diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-5.C b/gcc/testsuite/g++.dg/gomp/adjust-args-5.C new file mode 100644 index 0000000..04bb17f --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-5.C @@ -0,0 +1,42 @@ +/* PR c++/119659 */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +/* Test correct argument gets adjusted. */ + +struct S { + void v0(int *, int *) {} + + #pragma omp declare variant(v0) match(construct={dispatch}) adjust_args(need_device_ptr: a) + void b0(int *a, int *b) {} + + void v1(int *, int *) {} + + #pragma omp declare variant(v1) match(construct={dispatch}) adjust_args(need_device_ptr: 1) + void b1(int *a, int *b) {} + + void v2(int *, int *) {} + + #pragma omp declare variant(v2) match(construct={dispatch}) adjust_args(need_device_ptr: 1:1) + void b2(int *a, int *b) {} +}; + + +void f(int *p0, int *p1, int *p2, int *p3, int *p4, int *p5) +{ + S s; +/* { dg-final { scan-tree-dump-times "D\.\[0-9\]+ = __builtin_omp_get_default_device \\(\\);" 3 "gimple" } } */ + #pragma omp dispatch + s.b0(p0, p1); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p0, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "S::v0 \\(&s, D\.\[0-9\]+, p1\\);" "gimple" } } */ + + #pragma omp dispatch + s.b1(p2, p3); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p2, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "S::v1 \\(&s, D\.\[0-9\]+, p3\\);" "gimple" } } */ + + #pragma omp dispatch + s.b2(p4, p5); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p4, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "S::v2 \\(&s, D\.\[0-9\]+, p5\\);" "gimple" } } */ +} diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-6.C b/gcc/testsuite/g++.dg/gomp/adjust-args-6.C new file mode 100644 index 0000000..77d5d1c --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-6.C @@ -0,0 +1,97 @@ +/* PR c++/119659 */ + +/* { dg-do compile { target c++11 } } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +/* Test correct argument gets adjusted. */ + +struct S { + template<typename... Ts> + void v0_pack(int *, Ts...) {} + + #pragma omp declare variant(v0_pack) match(construct={dispatch}) \ + adjust_args(need_device_ptr: a) + template<typename... Ts> + void b0_pack(int *a, Ts...) { static_cast<void>(a); } + + + template<typename... Ts> + void v1_pack(int *, Ts...) {} + + #pragma omp declare variant(v1_pack) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1) + template<typename... Ts> + void b1_pack(int *a, Ts...) { static_cast<void>(a); } + + + template<typename... Ts> + void v2_pack(int *, Ts...) {} + + #pragma omp declare variant(v2_pack) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:1) + template<typename... Ts> + void b2_pack(int *a, Ts...) { static_cast<void>(a); } + + + template<typename... Ts> + void v3_pack(int *, Ts...) {} + + #pragma omp declare variant(v3_pack) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 2) + template<typename... Ts> + void b3_pack(int *a, Ts...) { static_cast<void>(a); } + + + template<typename... Ts> + void v4_pack(int *, Ts...) {} + + #pragma omp declare variant(v4_pack) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 2:2) + template<typename... Ts> + void b4_pack(int *a, Ts...) { static_cast<void>(a); } +}; + + +void f(int *p0, int *p1, int *p2, int *p3, + int *p4, int *p5, int *p6, int *p7, + int *p8, int *p9, int *pA, int *pB, + int *pC, int *pD, int *pE, int *pF) +{ + S s; + #pragma omp dispatch + s.b0_pack(p0, p1); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p0, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "S::v0_pack<int\\*> \\(&s, D\.\[0-9\]+, p1\\);" "gimple" } } */ + + #pragma omp dispatch + s.b1_pack(p2, p3); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p2, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "S::v1_pack<int\\*> \\(&s, D\.\[0-9\]+, p3\\);" "gimple" } } */ + + #pragma omp dispatch + s.b2_pack(p4, p5); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p4, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "S::v2_pack<int\\*> \\(&s, D\.\[0-9\]+, p5\\);" "gimple" } } */ + + #pragma omp dispatch + s.b3_pack(p6, p7); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p7, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "S::v3_pack<int\\*> \\(&s, p6, D\.\[0-9\]+\\);" "gimple" } } */ + + #pragma omp dispatch + s.b3_pack(p8, p9, pA); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(p9, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "S::v3_pack<int\\*, int\\*> \\(&s, p8, D\.\[0-9\]+, pA\\);" "gimple" } } */ + + #pragma omp dispatch + s.b4_pack(pB, pC); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(pC, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "S::v4_pack<int\\*> \\(&s, pB, D\.\[0-9\]+\\);" "gimple" } } */ + + #pragma omp dispatch + s.b4_pack(pD, pE, pF); +/* { dg-final { scan-tree-dump "D\.\[0-9\]+ = __builtin_omp_get_mapped_ptr \\(pC, D\.\[0-9\]+\\);" "gimple" } } */ +/* { dg-final { scan-tree-dump "S::v4_pack<int\\*, int\\*> \\(&s, pD, D\.\[0-9\]+, pF\\);" "gimple" } } */ +} + +/* { dg-final { scan-tree-dump-times "D\.\[0-9\]+ = __builtin_omp_get_default_device \\(\\);" 7 "gimple" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-7.C b/gcc/testsuite/g++.dg/gomp/adjust-args-7.C new file mode 100644 index 0000000..4b526fc --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-7.C @@ -0,0 +1,100 @@ +/* PR c++/118859 */ + +/* Diagnose invalid substituted types of depdendent parameters specified + in a need_device_ptr/need_device_addr modified adjust_args clause. + TODO: Need more cases with varying reference specifiers in the + variant/base function. */ + +template<typename T> +void v0(T) {} + +#pragma omp declare variant(v0) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1) /* { dg-note "parameter specified here" } */ +template<typename T> +void b0(T) {} /* { dg-error "parameter specified in an 'adjust_args' clause with the 'need_device_ptr' modifier must be of pointer type" } */ + + +template<typename T> +void v1(T) {} + +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1) +template<typename T> +void b1(T) {} + +void f0(int *p0, int *p1) +{ + #pragma omp dispatch + b0(42); /* { dg-message "required from here" } */ + #pragma omp dispatch + b0(p0); /* { dg-bogus "required from here" } */ + #pragma omp dispatch + b1(p1); /* { dg-bogus "required from here" } */ +} + +template<typename T> +struct Type { typedef T type; }; + +template<typename T> +void v2(Type<T>, typename Type<T>::type) {} + +#pragma omp declare variant(v2) match(construct={dispatch}) \ + adjust_args(need_device_addr: 2) /* { dg-note "parameter specified here" } */ +template<typename T> +void b2(Type<T>, typename Type<T>::type) {} /* { dg-error "parameter specified in an 'adjust_args' clause with the 'need_device_addr' modifier must be of reference type" } */ + + +template<typename T> +void v3(Type<T>, typename Type<T>::type) {} + +#pragma omp declare variant(v3) match(construct={dispatch}) \ + adjust_args(need_device_addr: 2) +template<typename T> +void b3(Type<T>, typename Type<T>::type) {} + + +void f2(int &r0, int &r1) +{ + #pragma omp dispatch + b2(Type<int>(), 42); /* { dg-message "required from here" } */ + #pragma omp dispatch + b2(Type<int&>(), r0); /* { dg-bogus "required from here" } */ + #pragma omp dispatch + b3(Type<int&>(), r1); /* { dg-bogus "required from here" } */ +} + +template<typename T> +void vX(T) {} + +#pragma omp declare variant(vX) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1) +template<typename T> +void bX(T) {} + +template<typename T> +void vY(T&) {} + +#pragma omp declare variant(vY) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1) /* { dg-note "parameter specified here" } */ +template<typename T> +void bY(T&) {} /* { dg-message "parameter with type reference to pointer in an 'adjust_args' with the 'need_device_ptr' modifier is not currently supported" } */ + + +template<typename T> +void v4(Type<T>, typename Type<T>::type) {} + +#pragma omp declare variant(v4) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 2) /* { dg-note "parameter specified here" } */ +template<typename T> +void b4(Type<T>, typename Type<T>::type) {} /* { dg-message "parameter with type reference to pointer in an 'adjust_args' with the 'need_device_ptr' modifier is not currently supported" } */ + + +void f3(int *p, int *&rp0, int *&rp1) +{ + #pragma omp dispatch + bX(rp0); /* { dg-bogus "required from here" } */ + #pragma omp dispatch + bY(p); /* { dg-message "required from here" } */ + #pragma omp dispatch + b4(Type<int *&>(), rp1); /* { dg-message "required from here" } */ +} diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-8.C b/gcc/testsuite/g++.dg/gomp/adjust-args-8.C new file mode 100644 index 0000000..a03f438 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-8.C @@ -0,0 +1,23 @@ +/* PR c++/118859 */ + +/* { dg-do compile { target c++11 } } */ + +/* Diagnose invalid types in a parameter pack that corresponds to an index + specified in a need_device_ptr/need_device_addr modified adjust_args clause. + TODO: Needs more cases, ideally matching the preceding (adjust-args-5.C) test cases. */ + +template<typename... Ts> +void v0(Ts...) {} + +#pragma omp declare variant(v0) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1) /* { dg-note "parameter specified here" } */ +template<typename... Ts> +void b0(Ts...) {} /* { dg-error "parameter specified in an 'adjust_args' clause with the 'need_device_ptr' modifier must be of pointer type" } */ + +void f0(int p0, int p1) +{ + #pragma omp dispatch + b0(42, p0); /* { dg-message "required from here" } */ + #pragma omp dispatch + b0(p1, 42); /* { dg-bogus "required from here" } */ +} diff --git a/gcc/testsuite/g++.dg/gomp/adjust-args-9.C b/gcc/testsuite/g++.dg/gomp/adjust-args-9.C new file mode 100644 index 0000000..3492f19 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/adjust-args-9.C @@ -0,0 +1,39 @@ +/* { dg-do compile { target c++11 } } */ + +/* Literal numeric range in function template with a parameter pack. + + This case may seem to be unremarkable, but it's a non-dependent numeric range + in a function template in which we don't know the amount of parameters. + The way it's handled (at the time of writing) causes some pretty low quality + diagnostics, hence the seperate test case. + + The numeric range is expanded before the function is instantiated, so the + fact that it was a numeric range is forgotten by the time the size is known + and a diagnostic can be issued. */ + +template<typename... Ts> +void v0(Ts...) {} +template<typename... Ts> +void v1(Ts...) {} + +#pragma omp declare variant(v0) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:2) +template<typename... Ts> +void b0(Ts...) {} + +#pragma omp declare variant(v1) match(construct={dispatch}) \ + adjust_args(need_device_ptr: 1:2) /* { dg-error "parameter list item index is out of range" } */ +template<typename... Ts> +void b1(Ts...) {} + +void f(int *p) +{ + /* Not out of range. */ + #pragma omp dispatch + b0(p, p); /* { dg-bogus "required from here" } */ + #pragma omp dispatch + b1(p, p); /* { dg-bogus "required from here" } */ + /* Out of range. */ + #pragma omp dispatch + b1(p); /* { dg-message "required from here" } */ +} diff --git a/gcc/testsuite/g++.dg/gomp/append-args-1.C b/gcc/testsuite/g++.dg/gomp/append-args-1.C index 70952e0..b0ff66a 100644 --- a/gcc/testsuite/g++.dg/gomp/append-args-1.C +++ b/gcc/testsuite/g++.dg/gomp/append-args-1.C @@ -51,9 +51,8 @@ void repl2(T, T2, T3, T3); append_args(interop(target, targetsync, prefer_type(1)), \ interop(target, prefer_type({fr(3), attr("ompx_nop")},{fr(2)},{attr("ompx_all")}))) template<typename T, typename T2> -void base2(T x, T2 y); - - +void base2(T x, T2 y); /* { dg-error "parameter specified in an 'adjust_args' clause with the 'need_device_ptr' modifier must be of pointer type" } */ +/* { dg-note "parameter specified here" "" { target *-*-* } .-5 } */ template<typename T,typename T3> void tooFewRepl(T, T, T3); #pragma omp declare variant(tooFewRepl) match(construct={dispatch}) \ diff --git a/gcc/testsuite/g++.dg/gomp/append-args-10.C b/gcc/testsuite/g++.dg/gomp/append-args-10.C new file mode 100644 index 0000000..9d07628 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/append-args-10.C @@ -0,0 +1,36 @@ +/* PR c++/119601 */ +/* { dg-do compile { target c++11 } } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +/* Parameter pack cases, check that interop arguments are passed correctly. */ + +#include "append-args-omp-interop-t.h" + +template<typename... Args> +void v0(int*, Args...) {} + +#pragma omp declare variant(v0) match(construct={dispatch}) \ + append_args(interop(target)) +template<typename... Args> +void b0(int *, Args...) {} + + +template<typename... Args> +void v1(int*, Args...) {} + +#pragma omp declare variant(v1) match(construct={dispatch}) \ + append_args(interop(target), \ + interop(targetsync)) +template<typename... Args> +void b1(int *, Args...) {} + + +void f1(int *p0, int *p1) +{ + #pragma omp dispatch + b0(p0); +/* { dg-final { scan-tree-dump "v0<\[^>\]*> \\(p0, interop\.\[0-9\]\\);" "gimple" } } */ + #pragma omp dispatch + b1(p1, 1, 2, 3); +/* { dg-final { scan-tree-dump "v1<int, int, int, omp_interop_t, omp_interop_t> \\(p1, 1, 2, 3, interop\.\[0-9\], interop\.\[0-9\]\\);" "gimple" } } */ +}
\ No newline at end of file diff --git a/gcc/testsuite/g++.dg/gomp/append-args-11.C b/gcc/testsuite/g++.dg/gomp/append-args-11.C new file mode 100644 index 0000000..fbec59e --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/append-args-11.C @@ -0,0 +1,96 @@ +/* PR c++/119602 */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +#include "append-args-omp-interop-t.h" + +/* Check multiple instantiations with a dependent prefer_type value + have the correct value. */ + +typedef enum omp_interop_fr_t +{ + omp_ifr_cuda = 1, + omp_ifr_cuda_driver = 2, + omp_ifr_opencl = 3, + omp_ifr_sycl = 4, + omp_ifr_hip = 5, + omp_ifr_level_zero = 6, + omp_ifr_hsa = 7, + omp_ifr_last = omp_ifr_hsa +} omp_interop_fr_t; + +template<omp_interop_fr_t V> +struct FR {}; + +template<omp_interop_fr_t V, typename T2> +void v_dependent_fr(FR<V>, T2) { } + +#pragma omp declare variant(v_dependent_fr) match(construct={dispatch}) \ + append_args(interop(target, \ + prefer_type(V))) +template<omp_interop_fr_t V> +void b_dependent_fr(FR<V>) { } + + +template<typename T, typename T2> +void v_cuda(T, T2) { } + +#pragma omp declare variant(v_cuda) match(construct={dispatch}) \ + append_args(interop(target, \ + prefer_type(omp_ifr_cuda_driver))) +template<typename T> +void b_cuda(T) { } + + +template<typename T, typename T2> +void v_hip(T, T2) { } + +#pragma omp declare variant(v_hip) match(construct={dispatch}) \ + append_args(interop(target, \ + prefer_type(omp_ifr_hip))) +template<typename T> +void b_hip(T) { } + + +template<typename T, typename T2> +void v_hsa(T, T2) { } + +#pragma omp declare variant(v_hsa) match(construct={dispatch}) \ + append_args(interop(target, \ + prefer_type(omp_ifr_hsa))) +template<typename T> +void b_hsa(T) { } + +void f () +{ + #pragma omp dispatch + b_dependent_fr (FR<omp_ifr_cuda_driver>()); + + #pragma omp dispatch + b_dependent_fr (FR<omp_ifr_hip>()); + + #pragma omp dispatch + b_dependent_fr (FR<omp_ifr_level_zero>()); + + #pragma omp dispatch + b_dependent_fr (FR<omp_ifr_hsa>()); + + #pragma omp dispatch + b_cuda (0); + + #pragma omp dispatch + b_hip (0); + + #pragma omp dispatch + b_hsa (0); +} + +/* "\\\[" and "\\\]" matches a literal '[' and ']', + "\\\\" matches a literal '\' in case anyone is wondering. */ +/* omp_ifr_cuda_driver */ +/* { dg-final { scan-tree-dump-times "pref_type\.\[0-9\]+\\\[0\\\] = \"\\\\x80\\\\x02\\\\x80\\\\x00\";" 2 "gimple" } } */ +/* omp_ifr_hip */ +/* { dg-final { scan-tree-dump-times "pref_type\.\[0-9\]+\\\[0\\\] = \"\\\\x80\\\\x05\\\\x80\\\\x00\";" 2 "gimple" } } */ +/* omp_ifr_level_zero */ +/* { dg-final { scan-tree-dump-times "pref_type\.\[0-9\]+\\\[0\\\] = \"\\\\x80\\\\x06\\\\x80\\\\x00\";" 1 "gimple" } } */ +/* omp_ifr_hsa */ +/* { dg-final { scan-tree-dump-times "pref_type\.\[0-9\]+\\\[0\\\] = \"\\\\x80\\\\x07\\\\x80\\\\x00\";" 2 "gimple" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/append-args-9.C b/gcc/testsuite/g++.dg/gomp/append-args-9.C new file mode 100644 index 0000000..95c8c3f --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/append-args-9.C @@ -0,0 +1,21 @@ +/* PR c++/119775 */ + +/* { dg-additional-options "-fdump-tree-gimple" } */ + +#include "append-args-omp-interop-t.h" + +struct S { + void v(int *, omp_interop_t) {} + + #pragma omp declare variant(v) match(construct={dispatch}) \ + append_args(interop(target)) + void b(int *) {} +}; + +void f(int *p) +{ + S s = S(); + #pragma omp dispatch + s.b(p); +/* { dg-final { scan-tree-dump "S::v \\(&s, p,\ interop\.\[0-9\]+\\);" "gimple" } } */ +} diff --git a/gcc/testsuite/g++.dg/gomp/append-args-omp-interop-t.h b/gcc/testsuite/g++.dg/gomp/append-args-omp-interop-t.h new file mode 100644 index 0000000..0a3eb91 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/append-args-omp-interop-t.h @@ -0,0 +1,11 @@ +#if __cplusplus >= 201103L +# define __GOMP_UINTPTR_T_ENUM : __UINTPTR_TYPE__ +#else +# define __GOMP_UINTPTR_T_ENUM +#endif + +typedef enum omp_interop_t __GOMP_UINTPTR_T_ENUM +{ + omp_interop_none = 0, + __omp_interop_t_max__ = __UINTPTR_MAX__ +} omp_interop_t; diff --git a/gcc/testsuite/gcc.dg/gomp/adjust-args-1.c b/gcc/testsuite/gcc.dg/gomp/adjust-args-1.c index 90787ef..a91e7ab 100644 --- a/gcc/testsuite/gcc.dg/gomp/adjust-args-1.c +++ b/gcc/testsuite/gcc.dg/gomp/adjust-args-1.c @@ -1,8 +1,6 @@ /* Test parsing of OMP clause adjust_args */ /* { dg-do compile } */ -int b; - int f0 (void *a); int g (void *a); int f1 (int); @@ -17,16 +15,27 @@ int f4 (void *a); 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 identifier before '\\)' token" } */ +#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (nothing:) /* { dg-error "expected expression before '\\)' token" } */ int f7 (int a); -#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (nothing: z) /* { dg-error "'z' undeclared here \\(not in a function\\)" } */ +#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (nothing: z) /* { dg-error "'z' is not a function parameter" } */ int f8 (int a); -#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (need_device_ptr: a) /* { dg-error "'a' is not of pointer type" } */ -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" } */ +#pragma omp declare variant (f1) match (construct={dispatch}) adjust_args (need_device_ptr: a) /* { dg-note "specified here" } */ +int f9 (int a); /* { dg-error "'a' is not of pointer type" } */ +#pragma omp declare variant (f1) match (construct={dispatch}) \ + adjust_args (nothing: a) \ + adjust_args (nothing: a) 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" } */ +/* { dg-note "previously specified here" "" { target *-*-* } .-3 } */ +/* { dg-error "parameter list item specified more than once" "" { target *-*-* } .-3 } */ +#pragma omp declare variant (g) match (construct={dispatch}) \ + adjust_args (nothing: a) \ + adjust_args (need_device_ptr: a) 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" } */ +/* { dg-note "previously specified here" "" { target *-*-* } .-3 } */ +/* { dg-error "parameter list item specified more than once" "" { target *-*-* } .-3 } */ + +int b; + +#pragma omp declare variant (g) match (construct={dispatch}) adjust_args (need_device_ptr: b) /* { dg-error "'b' is not a function parameter" } */ int f12 (void *a); diff --git a/gcc/testsuite/gcc.dg/gomp/append-args-1.c b/gcc/testsuite/gcc.dg/gomp/append-args-1.c index 81dd106..743d042 100644 --- a/gcc/testsuite/gcc.dg/gomp/append-args-1.c +++ b/gcc/testsuite/gcc.dg/gomp/append-args-1.c @@ -37,8 +37,8 @@ void variant_fn3(); /* { dg-error "argument 1 of 'variant_fn3' must be of 'omp_ #pragma omp declare variant(variant_fn3) match(construct={dispatch}) append_args(interop(target)) \ adjust_args(need_device_ptr: x,y) void bar3(); -/* { dg-error "'x' undeclared here \\(not in a function\\)" "" { target *-*-* } .-2 } */ -/* { dg-error "'y' undeclared here \\(not in a function\\)" "" { target *-*-* } .-3 } */ +/* { dg-error "'x' is not a function parameter" "" { target *-*-* } .-2 } */ +/* { dg-error "'y' is not a function parameter" "" { target *-*-* } .-3 } */ /* { dg-note "'append_args' specified here" "" { target *-*-* } .-5 } */ @@ -56,7 +56,8 @@ void variant_fn5(omp_interop_t, omp_interop_t); adjust_args(need_device_ptr: x,y) void bar5(); /* { dg-error "variant 'variant_fn5' and base 'bar5' have incompatible types" "" { target *-*-* } .-3 } */ - +/* { dg-error "'x' is not a function parameter" "" { target *-*-* } .-3 } */ +/* { dg-error "'y' is not a function parameter" "" { target *-*-* } .-4 } */ void variant_fn6(omp_interop_t, omp_interop_t); #pragma omp declare variant(variant_fn6) match(construct={dispatch}) append_args(interop(target)) |