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 )
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)
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;
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
}
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,
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);
else
vec_safe_push (args, build_zero_cst (TREE_TYPE (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)
vec_safe_push (args, build_zero_cst (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)
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;
}
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
(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);
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);
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;
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,
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 )
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))
tree append_args_tree = NULL_TREE;
tree append_args_last;
- 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)
{
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)
{
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));
// 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));
}
}
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;
{
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);
}
{
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);
++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))
{
}
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. */
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);
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,
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)
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));
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.
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);
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;
--- /dev/null
+/* 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 *-*-* } } */
+}
--- /dev/null
+/* 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) {}
--- /dev/null
+/* { 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" } } */
--- /dev/null
+/* 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; }
+
--- /dev/null
+/* 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
--- /dev/null
+/* 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) {}
--- /dev/null
+/* 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) {} */
--- /dev/null
+/* { 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 (...) {}
--- /dev/null
+/* { 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" } } */
#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);
/* Test parsing of OMP clause adjust_args */
/* { dg-do compile } */
-int b;
-
int f0 (void *a);
int g (void *a);
int f1 (int);
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);
--- /dev/null
+/* { 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" } } */
--- /dev/null
+/* { 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" } } */
--- /dev/null
+/* 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" } */
+}
--- /dev/null
+/* { 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" } */
+}
--- /dev/null
+/* 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>());
+}
--- /dev/null
+/* { 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);
+}
--- /dev/null
+/* 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. */
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{
#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;
#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
--- /dev/null
+/* 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" } } */
+}
--- /dev/null
+/* 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" } } */
--- /dev/null
+{ dg-require-effective-target c++11 }
+/* 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" } */
+}
--- /dev/null
+/* 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" } */
+}
--- /dev/null
+/* { 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" } */
+}
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}) \
--- /dev/null
+/* 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
--- /dev/null
+/* 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" } } */
--- /dev/null
+/* 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" } } */
+}
--- /dev/null
+#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;
/* Test parsing of OMP clause adjust_args */
/* { dg-do compile } */
-int b;
-
int f0 (void *a);
int g (void *a);
int f1 (int);
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);
#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 } */
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))
@item The OpenMP directive syntax was extended to include C 23 attribute
specifiers @tab Y @tab
@item All inarguable clauses take now an optional Boolean argument @tab N @tab
+@item The @code{adjust_args} clause was extended to specify the argument by position
+ and supports variadic arguments @tab Y @tab
@item For Fortran, @emph{locator list} can be also function reference with
data pointer result @tab N @tab
@item Concept of @emph{assumed-size arrays} in C and C++