@@ -10620,6 +10620,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case OMP_ATOMIC_CAPTURE_OLD:
case OMP_ATOMIC_CAPTURE_NEW:
case OMP_DEPOBJ:
+ case OMP_ALLOCATE:
case OACC_PARALLEL:
case OACC_KERNELS:
case OACC_SERIAL:
@@ -12491,6 +12492,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
case OMP_ATOMIC_CAPTURE_OLD:
case OMP_ATOMIC_CAPTURE_NEW:
case OMP_DEPOBJ:
+ case OMP_ALLOCATE:
case OACC_PARALLEL:
case OACC_KERNELS:
case OACC_SERIAL:
@@ -495,6 +495,17 @@ DEFTREECODE (TEMPLATE_INFO, "template_info", tcc_exceptional, 0)
Operand 1: OMP_DEPOBJ_CLAUSES: List of clauses. */
DEFTREECODE (OMP_DEPOBJ, "omp_depobj", tcc_statement, 2)
+/* OpenMP - #pragma omp allocate
+ Underlying node type is tree_exp, used to represent the directive as a
+ statement in a function. Only used for template instantiation.
+ Operand 0: OMP_ALLOCATE_VARS: tree_list containing each var_decl passed to
+ the directive as an args, purpose contains the
+ var_decl, value contains a expr that holds the
+ location where the var was passed in.
+ Operand 1: OMP_ALLOCATE_ALLOCATOR: Expr of the allocator clause.
+ Operand 2: OMP_ALLOCATE_ALIGN: Expr of the align clause. */
+DEFTREECODE (OMP_ALLOCATE, "omp_allocate", tcc_statement, 3)
+
/* Extensions for Concepts. */
/* Used to represent information associated with constrained declarations. */
@@ -6005,6 +6005,21 @@ target_expr_needs_replace (tree t)
#define OMP_DEPOBJ_DEPOBJ(NODE) TREE_OPERAND (OMP_DEPOBJ_CHECK (NODE), 0)
#define OMP_DEPOBJ_CLAUSES(NODE) TREE_OPERAND (OMP_DEPOBJ_CHECK (NODE), 1)
+/* OMP_ALLOCATE accessors.
+ #pragma omp allocate(var1, var2) allocator(Expr) align(Expr) */
+/* Contains a tree_list containing each variable passed as an argument to the
+ allocate directive. The purpose holds the var_decl, the value holds an expr
+ containing the location the var was passed as an argument. */
+#define OMP_ALLOCATE_VARS(NODE) (TREE_OPERAND (OMP_ALLOCATE_CHECK (NODE), 0))
+/* Contains the expression passed in the allocator clause, may be NULL_TREE if
+ no clause was provided, or error_mark_node if errors occurred. */
+#define OMP_ALLOCATE_ALLOCATOR(NODE) \
+ (TREE_OPERAND (OMP_ALLOCATE_CHECK (NODE), 1))
+/* Contains the expression passed in the align clause, may be NULL_TREE if no
+ clause was provided, or error_mark_node if errors occurred. */
+#define OMP_ALLOCATE_ALIGN(NODE) \
+ (TREE_OPERAND (OMP_ALLOCATE_CHECK (NODE), 2))
+
/* An enumeration of the kind of tags that C++ accepts. */
enum tag_types {
none_type = 0, /* Not a tag type. */
@@ -8562,6 +8577,8 @@ extern tree finish_omp_for (location_t, enum tree_code,
tree, tree, tree, tree, tree,
tree, tree, vec<tree> *, tree);
extern tree finish_omp_for_block (tree, tree);
+extern void finish_omp_allocate (location_t, tree, tree, tree,
+ tree);
extern void finish_omp_atomic (location_t, enum tree_code,
enum tree_code, tree, tree,
tree, tree, tree, tree, tree,
@@ -47184,11 +47184,85 @@ cp_parser_omp_structured_block (cp_parser *parser, bool *if_p)
static void
cp_parser_omp_allocate (cp_parser *parser, cp_token *pragma_tok)
{
- tree allocator = NULL_TREE;
- tree alignment = NULL_TREE;
- location_t loc = pragma_tok->location;
- tree nl = cp_parser_omp_var_list (parser, OMP_CLAUSE_ALLOCATE, NULL_TREE);
+ tree nl = cp_parser_omp_var_list (parser, OMP_CLAUSE_ERROR, NULL_TREE);
+
+ {
+ /* The head might have an error and need to be removed. */
+ tree *chain = &nl;
+ for (tree node = nl; node != NULL_TREE; node = TREE_CHAIN (node))
+ {
+ const tree var = TREE_PURPOSE (node);
+ const tree arg_loc_wrapper = TREE_VALUE (node);
+ const location_t arg_loc = EXPR_LOCATION (arg_loc_wrapper);
+
+ tree attr = lookup_attribute ("omp allocate",
+ DECL_ATTRIBUTES (var));
+ if (attr)
+ {
+ auto_diagnostic_group d;
+ error_at (arg_loc,
+ "%qD already appeared as list item in an "
+ "%<allocate%> directive", var);
+ const location_t old_arg_loc = [&] ()
+ {
+ tree attr_value = TREE_VALUE (attr);
+ if (TREE_CODE (attr_value) == NOP_EXPR)
+ return EXPR_LOCATION (attr_value);
+ /* If the previous directive that has this var as an arg was
+ already finished by finish_omp_allocate, attr_value is a
+ tree_list instead. */
+ gcc_assert (TREE_CODE (attr_value) == TREE_LIST);
+ /* In this case the location wrapper is stored differently. */
+ return EXPR_LOCATION (TREE_CHAIN (attr_value));
+ } (); /* IILE. */
+ inform (old_arg_loc,
+ "%qD previously appeared here", var);
+ /* Remove the node. */
+ *chain = TREE_CHAIN (node);
+ }
+ else
+ {
+ /* Mark the variable as having appeared in an allocate directive.
+ Do this even if any of the clauses are dependent, we need to
+ know this variable appeared in a directive before instantiation
+ to emit correct diagnostics. Stash the arg's location here for
+ better diagnostics.
+
+ There is a lot of subtle complexity here because the directive
+ syntactically appears after the declarations of its arguments,
+ so processing this directive only starts after the arguments
+ have been processed. This much is obvious, the issue is adding
+ an attribute to each VAR_DECL after they have been processed is
+ fairly intrusive, we need to take care to not break invariants
+ that those processes set up. In particular, attributes are
+ ordered by dependency, dependent first, non-dependent after.
+ There are parts of the compiler that expect this ordering. If
+ we naively chain the "omp allocate" attr to the front it will
+ effectively shadow dependent attributes, and if we chain it to
+ the end we will overwrite data in non-dependent attributes.
+ The solution is to chain it after the last dependent attribute,
+ this is still fairly brittle but that is the consequence of this
+ feature. */
+ {
+ tree *const attr_chain = [&] ()
+ {
+ tree *p = &DECL_ATTRIBUTES (var);
+ while (*p != NULL_TREE && ATTR_IS_DEPENDENT (*p))
+ p = &TREE_CHAIN (*p);
+ return p;
+ } (); /* IILE. */
+ *attr_chain = tree_cons (get_identifier ("omp allocate"),
+ arg_loc_wrapper,
+ *attr_chain);
+ }
+ /* Keep the node. */
+ chain = &TREE_CHAIN (node);
+ }
+ }
+ }
+ cp_expr allocator = NULL_TREE;
+ cp_expr alignment = NULL_TREE;
do
{
if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)
@@ -47209,70 +47283,30 @@ cp_parser_omp_allocate (cp_parser *parser, cp_token *pragma_tok)
}
if (!parens.require_open (parser))
break;
- tree expr = cp_parser_assignment_expression (parser);
+ cp_expr expr = cp_parser_assignment_expression (parser);
if (p[2] == 'i' && alignment)
{
error_at (cloc, "too many %qs clauses", "align");
break;
}
else if (p[2] == 'i')
- {
- if (expr != error_mark_node)
- alignment = expr;
- /* FIXME: Remove when adding check to semantics.cc; cf FIXME below. */
- if (alignment
- && !type_dependent_expression_p (alignment)
- && !INTEGRAL_TYPE_P (TREE_TYPE (alignment)))
- {
- error_at (cloc, "%<align%> clause argument needs to be "
- "positive constant power of two integer "
- "expression");
- alignment = NULL_TREE;
- }
- else if (alignment)
- {
- alignment = mark_rvalue_use (alignment);
- if (!processing_template_decl)
- {
- alignment = maybe_constant_value (alignment);
- if (TREE_CODE (alignment) != INTEGER_CST
- || !tree_fits_uhwi_p (alignment)
- || !integer_pow2p (alignment))
- {
- error_at (cloc, "%<align%> clause argument needs to be "
- "positive constant power of two integer "
- "expression");
- alignment = NULL_TREE;
- }
- }
- }
- }
+ alignment = expr;
else if (allocator)
{
error_at (cloc, "too many %qs clauses", "allocator");
break;
}
else
- {
- if (expr != error_mark_node)
- allocator = expr;
- }
+ allocator = expr;
parens.require_close (parser);
} while (true);
cp_parser_require_pragma_eol (parser, pragma_tok);
- if (allocator || alignment)
- for (tree c = nl; c != NULL_TREE; c = OMP_CLAUSE_CHAIN (c))
- {
- OMP_CLAUSE_ALLOCATE_ALLOCATOR (c) = allocator;
- OMP_CLAUSE_ALLOCATE_ALIGN (c) = alignment;
- }
-
- /* FIXME: When implementing properly, delete the align/allocate expr error
- check above and add one in semantics.cc (to properly handle templates).
- Base this on the allocator/align modifiers check for the 'allocate' clause
- in semantics.cc's finish_omp_clauses. */
- sorry_at (loc, "%<#pragma omp allocate%> not yet supported");
+ finish_omp_allocate (pragma_tok->location,
+ nl,
+ allocator,
+ alignment,
+ current_scope ());
}
/* OpenMP 2.5:
@@ -20466,6 +20466,32 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
OMP_DEPOBJ_CLAUSES (t));
break;
+ case OMP_ALLOCATE:
+ {
+ gcc_assert (flag_openmp);
+
+ tree alloc
+ = tsubst_expr (OMP_ALLOCATE_ALLOCATOR (t), args, complain, in_decl);
+ tree align
+ = tsubst_expr (OMP_ALLOCATE_ALIGN (t), args, complain, in_decl);
+ tree vars = copy_list (OMP_ALLOCATE_VARS (t));
+ for (tree node = vars; node != NULL_TREE; node = TREE_CHAIN (node))
+ {
+ if (TREE_PURPOSE (node) == error_mark_node)
+ continue;
+ /* The var was already substituted, just look up the new node. */
+ tree var = lookup_name (DECL_NAME (TREE_PURPOSE (node)),
+ LOOK_where::BLOCK, LOOK_want::NORMAL);
+ /* There's a weird edge case where lookup_name returns NULL_TREE,
+ but only for incorrect code. Even so, handle NULL_TREE to avoid
+ segfaulting in those cases. */
+ TREE_PURPOSE (node) = var ? var : error_mark_node;
+ /* Don't copy the attr here, let finish_omp_allocate handle it. */
+ }
+ finish_omp_allocate (EXPR_LOCATION (t), vars, alloc, align,
+ current_scope ());
+ break;
+ }
case OACC_DATA:
case OMP_TARGET_DATA:
case OMP_TARGET:
@@ -12354,6 +12354,332 @@ finish_omp_for_block (tree bind, tree omp_for)
return bind;
}
+/* Validate an OpenMP allocate directive, then add the ALLOC and ALIGN exprs to
+ the "omp allocate" attr of each decl found in VARS. The value of attr is
+ a TREE_LIST with ALLOC stored in its purpose member and ALIGN stored in its
+ value member. ALLOC and ALIGN are exprs passed as arguments to the
+ allocator and align clauses of the directive. VARS may be NULL_TREE if
+ there were errors during parsing.
+ #pragma omp allocate(VARS) allocator(ALLOC) align(ALIGN)
+
+ If processing_template_decl, a stmt of tree_code OMP_ALLOCATE is added to
+ the function instead. LOC is used to initialize EXPR_LOCATION of the stmt.
+
+ If any errors occur, the "omp allocate" attr is marked so the middle end
+ knows to skip it during gimplification. */
+
+void
+finish_omp_allocate (const location_t loc, const tree var_list,
+ const tree alloc_in, const tree align_in,
+ const tree context)
+{
+ /* Takes a VAR_DECL. */
+ using var_predicate = bool (*)(const_tree);
+ const auto any_of_vars = [&var_list] (var_predicate predicate)
+ {
+ for (tree vn = var_list; vn != NULL_TREE; vn = TREE_CHAIN (vn))
+ if (predicate (TREE_PURPOSE (vn)))
+ return true;
+ return false;
+ };
+ const bool global = !DECL_DECLARES_FUNCTION_P (context);
+ const var_predicate tree_static_p = [&] () -> var_predicate
+ {
+ /* If this directive is not in a function, all the vars are static. */
+ if (global)
+ return [] (const_tree) -> bool { return true; };
+ else
+ return [] (const_tree t) -> bool { return TREE_STATIC (t); };
+ } (); /* IILE. */
+ const bool any_static_vars = any_of_vars (tree_static_p);
+
+ /* (OpenMP 5.2, 174:15) Type: expression of integer type
+ Properties: constant, positive */
+ const tree align = [&align_in] ()
+ {
+ if (!align_in || align_in == error_mark_node)
+ return align_in;
+
+ auto emit_align_err = [&align_in] ()
+ {
+ error_at (EXPR_LOCATION (align_in),
+ "%<align%> clause argument needs to be positive constant "
+ "power of two integer expression");
+ };
+ if (processing_template_decl)
+ return align_in;
+
+ /* Identity conversions are okay. */
+ const tree converted_align
+ = build_converted_constant_expr (size_type_node, align_in, tf_error);
+ gcc_assert (converted_align != NULL_TREE);
+ if (converted_align == error_mark_node)
+ {
+ emit_align_err ();
+ return error_mark_node;
+ }
+
+ const tree folded_align = cxx_constant_value (converted_align);
+ if (folded_align == error_mark_node)
+ {
+ emit_align_err ();
+ return error_mark_node;
+ }
+ gcc_assert (TREE_CONSTANT (folded_align));
+ /* (OpenMP 5.2, 175:10) alignment must evaluate to a power of two. */
+ if (tree_int_cst_sgn (folded_align) != 1
+ || !integer_pow2p (folded_align))
+ {
+ emit_align_err ();
+ return error_mark_node;
+ }
+ return folded_align;
+ } (); /* IILE. */
+
+ /* (OpenMP 5.2, 175:17) Type: expression of allocator_handle type
+ Properties: default */
+ const tree alloc = [&] ()
+ {
+ if (alloc_in == error_mark_node)
+ return error_mark_node;
+ /* (OpenMP 5.2 176:26-28)
+ If a list item has static storage duration, the allocator clause must
+ be specified and the allocator expression in the clause must be a
+ constant expression that evaluates to one of the predefined memory
+ allocator values. */
+ const auto emit_diag_for_static_vars = [&] ()
+ {
+ inform (UNKNOWN_LOCATION,
+ "because one or more variables with static storage duration "
+ "appear in the %<allocate%> directive");
+ };
+ if (alloc_in == NULL_TREE)
+ {
+ if (!any_static_vars)
+ return NULL_TREE;
+ else
+ {
+ auto_diagnostic_group d;
+ error_at (loc, "%<allocator%> clause must be specified");
+ emit_diag_for_static_vars ();
+ return error_mark_node;
+ }
+ gcc_unreachable ();
+ }
+ if (processing_template_decl)
+ return alloc_in;
+
+ const tree alloc_type = [&] ()
+ {
+ static tree cached_alloc_type = NULL_TREE;
+ if (cached_alloc_type != NULL_TREE)
+ return cached_alloc_type;
+
+ const tree alloc_type_decl
+ = lookup_qualified_name (global_namespace,
+ get_identifier ("omp_allocator_handle_t"),
+ LOOK_want::TYPE);
+ /* User didn't include omp.h... */
+ if (alloc_type_decl == error_mark_node)
+ {
+ /* We need to wait until instantiation to complain about it if
+ alloc_in could possibly be well-formed. */
+ if (processing_template_decl
+ && (type_dependent_expression_p (alloc_in)
+ /* No one sane would have a conversion template and
+ use it this way, but it would still technically be
+ correct so we have to account for it. */
+ || (CLASS_TYPE_P (TREE_TYPE (alloc_in))
+ && TYPE_HAS_CONVERSION (TREE_TYPE (alloc_in)))))
+ {
+ gcc_assert (cached_alloc_type == NULL_TREE);
+ return cached_alloc_type;
+ }
+ auto_diagnostic_group d;
+ error_at (EXPR_LOCATION (alloc_in),
+ "%<allocator%> clause requires a valid declaration "
+ "of %<omp_allocator_handle_t%>");
+ inform (EXPR_LOCATION (alloc_in),
+ "%<omp_allocator_handle_t%> is defined in header "
+ "%<<omp.h>%>; this is probably fixable by adding "
+ "%<#include <omp.h>%>");
+ cached_alloc_type = error_mark_node;
+ return cached_alloc_type;
+ }
+ const auto underlying_type_is_valid = [] (const const_tree type)
+ {
+ const const_tree canonical_type = TYPE_CANONICAL (type);
+ if (cxx_dialect >= cxx11)
+ return canonical_type == uintptr_type_node;
+ gcc_assert (cxx_dialect < cxx11);
+ /* Pre c++11 we can't just assume the underlying type. */
+ return TYPE_UNSIGNED (canonical_type) == true
+ && wi::to_widest (TYPE_SIZE (canonical_type))
+ == wi::to_widest (TYPE_SIZE (uintptr_type_node))
+ && wi::to_widest (TYPE_MIN_VALUE (canonical_type))
+ == wi::to_widest (TYPE_MIN_VALUE (uintptr_type_node))
+ && wi::to_widest (TYPE_MAX_VALUE (canonical_type))
+ == wi::to_widest (TYPE_MAX_VALUE (uintptr_type_node));
+ };
+ const tree alloc_type = TREE_TYPE (alloc_type_decl);
+ if (TREE_CODE (alloc_type) != ENUMERAL_TYPE
+ || !underlying_type_is_valid (TREE_TYPE (alloc_type)))
+ {
+ auto_diagnostic_group d;
+ error_at (EXPR_LOCATION (alloc_in),
+ "%<allocator%> clause requires a valid declaration "
+ "of %<omp_allocator_handle_t%>");
+ /* This diagnostic is not very good, especially if it somehow
+ is the declaration in omp.h. */
+ inform (DECL_SOURCE_LOCATION (alloc_type_decl),
+ "invalid type declared here");
+ cached_alloc_type = error_mark_node;
+ }
+ else
+ cached_alloc_type = alloc_type;
+ gcc_assert (cached_alloc_type != NULL_TREE);
+ return cached_alloc_type;
+ } (); /* IILE. */
+ /* Any sane user will include omp.h before we get here, unfortunately
+ insane doesn't mean incorrect. */
+ if (alloc_type == NULL_TREE)
+ return alloc_in;
+ /* We can't do anything meaningful with alloc_in if we lack a valid
+ omp_allocator_handle_t type. */
+ if (alloc_type == error_mark_node)
+ return error_mark_node;
+
+ /* Identity conversion is okay. */
+ const tree converted_alloc
+ = any_static_vars
+ ? build_converted_constant_expr (alloc_type, alloc_in, tf_error)
+ : perform_implicit_conversion (alloc_type, alloc_in, tf_error);
+
+ /* The expr is not manifestly constant-evaluated in this case, only do
+ simple folding even if it is potentially a constant expression. */
+ if (!any_static_vars)
+ return maybe_fold_non_dependent_expr (converted_alloc, tf_none);
+
+ /* Per OpenMP 5.2 176:26-28 quoted above, the allocator clause is
+ manifestly constant-evaluated in this case. */
+ const tree ret = cxx_constant_value (converted_alloc);
+ if (ret == error_mark_node)
+ {
+ auto_diagnostic_group d;
+ error_at (EXPR_LOCATION (alloc_in),
+ "%<allocator%> clause requires a constant "
+ "predefined allocator");
+ emit_diag_for_static_vars ();
+ return error_mark_node;
+ }
+ gcc_assert (TREE_CONSTANT (ret));
+ wi::tree_to_widest_ref alloc_value = wi::to_widest (ret);
+ /* MAX is inclusive. */
+ const bool predefined_allocator_p
+ = (alloc_value >= 1
+ && alloc_value <= GOMP_OMP_PREDEF_ALLOC_MAX)
+ || (alloc_value >= GOMP_OMPX_PREDEF_ALLOC_MIN
+ && alloc_value <= GOMP_OMPX_PREDEF_ALLOC_MAX);
+ if (!predefined_allocator_p)
+ {
+ auto_diagnostic_group d;
+ error_at (EXPR_LOCATION (alloc_in),
+ "%<allocator%> clause requires a constant "
+ "predefined allocator");
+ emit_diag_for_static_vars ();
+ inform (EXPR_LOCATION (alloc_in),
+ "expression evaluates to %qwu", tree_to_uhwi (ret));
+ return error_mark_node;
+ }
+ return ret;
+ } (); /* IILE. */
+
+ auto finalize_allocate_attr = [] (tree var, tree alloc, tree align)
+ {
+ /* This should only be called once for each var, either after a
+ diagnostic, or when we are finished with the directive. */
+ gcc_assert (var != NULL_TREE && var != error_mark_node);
+
+ /* Replace the old attr in place to maintain the invariants of
+ DECL_ATTRIBUTES as described in cp_parser_omp_allocate.
+ Unfortunately lookup_attribute does not return an "iterator" to the
+ attr so we have to write the lookup ourselves here. */
+ tree *const attr_it = [&] ()
+ {
+ tree *it = &DECL_ATTRIBUTES (var);
+ for (; *it != NULL_TREE; it = &TREE_CHAIN (*it))
+ if (is_attribute_p ("omp allocate", get_attribute_name (*it)))
+ return it;
+ gcc_unreachable ();
+ } (); /* IILE. */
+ /* Don't modify the old attr, for various reasons it isn't marked
+ dependent so substitution does not copy it. */
+ const_tree old_attr = *attr_it;
+ /* There should only be one attr. */
+ gcc_checking_assert (!lookup_attribute ("omp allocate",
+ TREE_CHAIN (old_attr)));
+ /* This location is where the var was specified in the directive. */
+ const tree arg_loc = TREE_VALUE (old_attr);
+ gcc_assert (arg_loc != NULL_TREE && TREE_CODE (arg_loc) == NOP_EXPR);
+ /* Smuggle the location, we might still need it for diagnostics, such as
+ if we are still parsing a function. */
+ const tree attr_value = tree_cons (alloc, align, arg_loc);
+
+ /* As noted above, we create the final attr ourselves instead of having
+ substitution handle it. */
+ *attr_it = tree_cons (TREE_PURPOSE (old_attr),
+ attr_value,
+ TREE_CHAIN (old_attr));
+ };
+ auto finalize_var_node_with_error = [&] (tree node)
+ {
+ /* We can't remove the attribute, the variable still needs to be marked
+ in case that we are still parsing a function for the first time.
+ We avoid handling error_mark_node in varpool_node::finalize_decl by
+ setting align to NULL_TREE. */
+ finalize_allocate_attr (TREE_PURPOSE (node), error_mark_node, NULL_TREE);
+ /* Prevent further diagnostics for this var. */
+ TREE_PURPOSE (node) = error_mark_node;
+ };
+
+ /* Even if there have been errors, save the current state, there might be
+ more to diagnose on a later instantiation. */
+ if (processing_template_decl)
+ {
+ tree allocate_stmt = make_node (OMP_ALLOCATE);
+ /* This shouldn't matter, but better to set it anyway. */
+ TREE_SIDE_EFFECTS (allocate_stmt) = 0;
+ SET_EXPR_LOCATION (allocate_stmt, loc);
+ OMP_ALLOCATE_VARS (allocate_stmt) = var_list;
+ OMP_ALLOCATE_ALLOCATOR (allocate_stmt) = alloc;
+ OMP_ALLOCATE_ALIGN (allocate_stmt) = align;
+ add_stmt (allocate_stmt);
+ return;
+ }
+ else if (alloc == error_mark_node || align == error_mark_node || !var_list
+ || any_of_vars (&error_operand_p))
+ {
+ /* The directive is fully instantiated, but there were errors. */
+ for (tree vn = var_list; vn != NULL_TREE; vn = TREE_CHAIN (vn))
+ /* Don't mark vars a second time. */
+ if (TREE_PURPOSE (vn) != error_mark_node)
+ finalize_var_node_with_error (vn);
+ return;
+ }
+
+ gcc_assert (!processing_template_decl
+ && alloc != error_mark_node
+ && align != error_mark_node
+ && var_list != NULL_TREE
+ && !any_of_vars (&error_operand_p));
+
+ /* We can technically finalize earlier if everything (vars, alloc, align) is
+ substituted and the alloc expr doesn't contain any local variables, this
+ isn't worth the added complexity for now though. */
+ for (tree vn = var_list; vn != NULL_TREE; vn = TREE_CHAIN (vn))
+ finalize_allocate_attr (TREE_PURPOSE (vn), alloc, align);
+}
+
void
finish_omp_atomic (location_t loc, enum tree_code code, enum tree_code opcode,
tree lhs, tree rhs, tree v, tree lhs1, tree rhs1, tree r,
@@ -1440,11 +1440,19 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
&& DECL_CONTEXT (t) == current_function_decl
&& TREE_USED (t)
&& (attr = lookup_attribute ("omp allocate", DECL_ATTRIBUTES (t)))
- != NULL_TREE)
+ != NULL_TREE
+ /* In C++, bad attributes are marked instead of removed. */
+ && TREE_PURPOSE (TREE_VALUE (attr)) != error_mark_node)
{
gcc_assert (!DECL_HAS_VALUE_EXPR_P (t));
tree alloc = TREE_PURPOSE (TREE_VALUE (attr));
tree align = TREE_VALUE (TREE_VALUE (attr));
+ /* The C++ front end smuggles a location through the chain field,
+ this conflicts with Fortran handling, clear it. */
+ if (TREE_CHAIN (TREE_VALUE (attr)) != NULL_TREE
+ && TREE_CODE (TREE_CHAIN (TREE_VALUE (attr))) == NOP_EXPR)
+ TREE_CHAIN (TREE_VALUE (attr)) = NULL_TREE;
+
/* Allocate directives that appear in a target region must specify
an allocator clause unless a requires directive with the
dynamic_allocators clause is present in the same compilation
@@ -1,5 +1,3 @@
-/* TODO: enable for C++ once implemented. */
-/* { dg-do compile { target c } } */
/* { dg-additional-options "-Wall -fdump-tree-gimple" } */
typedef enum omp_allocator_handle_t
@@ -23,6 +21,7 @@ void
h1()
{
omp_allocator_handle_t my_handle;
+ /* { dg-bogus "variable 'my_handle' set but not used" "" { target *-*-* } .-1 } */
int B1[3]; /* { dg-warning "'my_handle' is used uninitialized" } */
/* { dg-warning "variable 'B1' set but not used" "" { target *-*-* } .-1 } */
#pragma omp allocate(B1) allocator(my_handle)
@@ -36,6 +35,7 @@ void
h2()
{
omp_allocator_handle_t my_handle;
+ /* { dg-bogus "variable 'my_handle' set but not used" "" { target *-*-* } .-1 } */
int B2[3]; /* { dg-warning "unused variable 'B2'" } */
#pragma omp allocate(B2) allocator(my_handle) /* No warning as 'B2' is unused */
}
@@ -44,6 +44,7 @@ void
h3()
{
omp_allocator_handle_t my_handle;
+ /* { dg-bogus "variable 'my_handle' set but not used" "" { target *-*-* } .-1 } */
int B3[3] = {1,2,3}; /* { dg-warning "unused variable 'B3'" } */
#pragma omp allocate(B3) allocator(my_handle) /* No warning as 'B3' is unused */
}
@@ -1,5 +1,9 @@
-/* TODO: enable for C++ once implemented. */
-/* { dg-do compile { target c } } */
+/* This warning is effectively bogus, it appears to only trigger after UB(?)
+ triggers an optimization that removes the case labels. We don't actually
+ want to check for this behavior. If anything we might want to ensure it
+ doesn't trigger when jumping over a variable found in an allocate directive,
+ as we are supposed to already diagnose an error for that case. */
+/* { dg-additional-options -Wno-switch-unreachable } */
void bar();
void use (int*);
@@ -7,15 +11,14 @@ void use (int*);
void
f (int i)
{
- switch (i) /* { dg-note "switch starts here" } */
+ switch (i) /* { dg-note "switch starts here" "" { xfail c++ } } */
{
- int j; /* { dg-note "'j' declared here" } */
+ int j; /* { dg-note "'j' declared here" "" { xfail c++ } } */
#pragma omp allocate(j)
- case 42: /* { dg-error "switch jumps over OpenMP 'allocate' allocation" } */
+ case 42: /* { dg-error "switch jumps over OpenMP 'allocate' allocation" "" { xfail c++ } } */
bar ();
- /* { dg-warning "statement will never be executed \\\[-Wswitch-unreachable\\\]" "" { target *-*-* } .-1 } */
break;
- case 51: /* { dg-error "switch jumps over OpenMP 'allocate' allocation" } */
+ case 51: /* { dg-error "switch jumps over OpenMP 'allocate' allocation" "" { xfail c++ } } */
use (&j);
break;
}
@@ -25,13 +28,17 @@ int
h (int i2)
{
if (i2 == 5)
- goto label; /* { dg-error "jump skips OpenMP 'allocate' allocation" } */
+ goto label; /* { dg-error "jump skips OpenMP 'allocate' allocation" "" { xfail c++ } } */
+ /* { dg-note "from here" "" { target c++ } .-1 } */
return 5;
- int k2; /* { dg-note "'k2' declared here" } */
- int j2 = 4; /* { dg-note "'j2' declared here" } */
+ int k2; /* { dg-note "'k2' declared here" "" { xfail c++ } } */
+ int j2 = 4; /* { dg-note "'j2' declared here" "" { xfail c++ } } */
+ /* { dg-note "crosses initialization of 'int j2'" "" { target c++ } .-1 } */
#pragma omp allocate(k2, j2)
-label: /* { dg-note "label 'label' defined here" } */
+label: /* { dg-note "label 'label' defined here" "" { xfail c++ } } */
+// It might make sense to make this bogus, as semantically it's assigning to the pointed at value.
+/* { dg-error "jump to label 'label'" "" { target c++ } .-2 } */
k2 = 4;
return j2 + k2;
}
@@ -1,6 +1,3 @@
-/* TODO: enable for C++ once implemented. */
-/* { dg-do compile { target c } } */
-
typedef enum omp_allocator_handle_t
#if __cplusplus >= 201103L
: __UINTPTR_TYPE__
@@ -15,9 +12,33 @@ int
f ()
{
omp_allocator_handle_t my_allocator;
- int n = 5; /* { dg-note "to be allocated variable declared here" } */
- my_allocator = omp_default_mem_alloc; /* { dg-note "modified here" } */
- #pragma omp allocate(n) allocator(my_allocator) /* { dg-error "variable 'my_allocator' used in the 'allocator' clause must not be modified between declaration of 'n' and its 'allocate' directive" } */
+ int n = 5; /* { dg-note "to be allocated variable declared here" "" { xfail c++ } } */
+ my_allocator = omp_default_mem_alloc; /* { dg-note "modified here" "" { xfail c++ } } */
+ #pragma omp allocate(n) allocator(my_allocator) /* { dg-error "variable 'my_allocator' used in the 'allocator' clause must not be modified between declaration of 'n' and its 'allocate' directive" "" { xfail c++ } } */
+ n = 7;
+ return n;
+}
+
+int
+f1 ()
+{
+ omp_allocator_handle_t alloc;
+ {
+ int n = 42; /* { dg-note "to be allocated variable declared here" "" { xfail *-*-* } } */
+ alloc = omp_default_mem_alloc; /* { dg-note "modified here" "" { xfail *-*-* } } */
+ #pragma omp allocate(n) allocator(alloc) /* { dg-error "variable 'alloc' used in the 'allocator' clause must not be modified between declaration of 'n' and its 'allocate' directive" "" { xfail *-*-* } } */
+ n = 7;
+ return n;
+ }
+}
+
+int
+f2 ()
+{
+ omp_allocator_handle_t my_allocator;
+ int n = 5; /* { dg-note "to be allocated variable declared here" "" { xfail *-*-* } } */
+ int hide_mutation = my_allocator = omp_default_mem_alloc; /* { dg-note "modified here" "" { xfail *-*-* } } */
+ #pragma omp allocate(n) allocator(my_allocator) /* { dg-error "variable 'my_allocator' used in the 'allocator' clause must not be modified between declaration of 'n' and its 'allocate' directive" "" { xfail *-*-* } } */
n = 7;
return n;
}
@@ -26,9 +47,9 @@ f ()
int
g ()
{
- int n = 5; /* { dg-note "to be allocated variable declared here" } */
- omp_allocator_handle_t my_allocator = omp_low_lat_mem_alloc; /* { dg-note "declared here" } */
- #pragma omp allocate(n) allocator(my_allocator) /* { dg-error "variable 'my_allocator' used in the 'allocator' clause must be declared before 'n'" } */
+ int n = 5; /* { dg-note "to be allocated variable declared here" "" { xfail c++ } } */
+ omp_allocator_handle_t my_allocator = omp_low_lat_mem_alloc; /* { dg-note "declared here" "" { xfail c++ } } */
+ #pragma omp allocate(n) allocator(my_allocator) /* { dg-error "variable 'my_allocator' used in the 'allocator' clause must be declared before 'n'" "" { xfail c++ } } */
n = 7;
return n;
}
@@ -1,6 +1,3 @@
-/* TODO: enable for C++ once implemented. */
-/* { dg-do compile { target c } } */
-
#pragma omp begin declare target
void
f ()
@@ -1,6 +1,3 @@
-/* TODO: enable for C++ once implemented. */
-/* { dg-do compile { target c } } */
-
#pragma omp requires dynamic_allocators
#pragma omp begin declare target
@@ -1,6 +1,3 @@
-/* TODO: enable for C++ once implemented. */
-/* { dg-do compile { target c } } */
-
typedef enum omp_allocator_handle_t
#if __cplusplus >= 201103L
: __UINTPTR_TYPE__
@@ -17,12 +14,15 @@ omp_allocator_handle_t foo(int, int *);
void
f ()
{
- int v; /* { dg-note "to be allocated variable declared here" } */
- int n = 5;
- int a = 1; /* { dg-note "declared here" } */
+ int v; /* { dg-note "to be allocated variable declared here" "" { xfail c++ } } */
+ static const int n = 5;
+ int a = 1;
+ /* { dg-note "declared here" "" { xfail c++ } .-1 } */
int b[n];
+ /* { dg-note "declared here" "" { target c++ xfail c++ } .-1 } */
b[a] = 5;
- #pragma omp allocate (v) allocator (foo (a, &b[a])) /* { dg-error "variable 'a' used in the 'allocator' clause must be declared before 'v'" } */
+ #pragma omp allocate (v) allocator (foo (a, &b[a]))
+ /* { dg-error "variable 'a' used in the 'allocator' clause must be declared before 'v'" "" { xfail c++ } .-1 } */
}
void
@@ -32,7 +32,8 @@ g ()
int a = 1;
int b[n];
b[a] = 5;
- int v; /* { dg-note "to be allocated variable declared here" } */
- a = 2; /* { dg-note "modified here" } */
- #pragma omp allocate (v) allocator (foo (a, &b[a])) /* { dg-error "variable 'a' used in the 'allocator' clause must not be modified between declaration of 'v' and its 'allocate' directive" } */
+ int v; /* { dg-note "to be allocated variable declared here" "" { xfail c++ } } */
+ a = 2; /* { dg-note "modified here" "" { xfail c++ } } */
+ #pragma omp allocate (v) allocator (foo (a, &b[a]))
+ /* { dg-error "variable 'a' used in the 'allocator' clause must not be modified between declaration of 'v' and its 'allocate' directive" "" { xfail c++ } .-1 } */
}
@@ -20,7 +20,7 @@ one ()
#pragma omp target map(tofrom: result) firstprivate(n)
{
int var = 5; //, var2[n];
- #pragma omp allocate(var) align(128) allocator(omp_low_lat_mem_alloc) /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } } */
+ #pragma omp allocate(var) align(128) allocator(omp_low_lat_mem_alloc)
var = 7;
}
@@ -1,26 +1,10 @@
// { dg-additional-options "-Wno-deprecated-openmp" }
-typedef enum omp_allocator_handle_t
-#if __cplusplus >= 201103L
-: __UINTPTR_TYPE__
-#endif
-{
- omp_null_allocator = 0,
- omp_default_mem_alloc = 1,
- omp_large_cap_mem_alloc = 2,
- omp_const_mem_alloc = 3,
- omp_high_bw_mem_alloc = 4,
- omp_low_lat_mem_alloc = 5,
- omp_cgroup_mem_alloc = 6,
- omp_pteam_mem_alloc = 7,
- omp_thread_mem_alloc = 8,
- __omp_allocator_handle_t_max__ = __UINTPTR_MAX__
-} omp_allocator_handle_t;
+#include "allocate-allocator-handle.h"
void test0 ()
{
int A1[5], B1[5];
#pragma omp allocate(A1) align(128) allocator(omp_default_mem_alloc)
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
#ifndef __cplusplus
_Static_assert (_Alignof(A1) == _Alignof(B1), "wrong alignment");
@@ -1,3 +1,4 @@
+/* Does not include allocate-allocator-handle.h due to extra values being added. */
typedef enum omp_allocator_handle_t
#if __cplusplus >= 201103L
: __UINTPTR_TYPE__
@@ -21,7 +22,6 @@ typedef enum omp_allocator_handle_t
static int A1[5] = {1,2,3,4,5}, B1[5];
#pragma omp allocate(A1) align(128) allocator(omp_default_mem_alloc)
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
#ifndef __cplusplus
_Static_assert (_Alignof(A1) == _Alignof(B1), "wrong alignment");
@@ -32,7 +32,6 @@ static_assert (alignof(A1) == alignof(B1), "wrong alignment");
static int *ptr;
#pragma omp allocate(ptr) align(2) allocator(omp_default_mem_alloc)
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
#ifndef __cplusplus
_Static_assert (_Alignof(ptr) == _Alignof(int*), "wrong alignment");
@@ -46,7 +45,6 @@ get ()
{
static int q = 0;
#pragma omp allocate(q) align(1024) allocator(omp_default_mem_alloc)
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
#ifndef __cplusplus
_Static_assert (_Alignof(q) == _Alignof(int), "wrong alignment");
@@ -58,12 +56,19 @@ get ()
return &A1[q];
}
-static int invalid1, okay1, invalid2, invalid3;
-#pragma omp allocate(invalid1) align(128) allocator(ompx_gnu_pinned_bogus_1) /* { dg-error "'allocator' clause requires a predefined allocator as 'invalid1' is static" "" { xfail c++ } } */
+static int invalid1, okay1, invalid2, invalid3; /* { dg-note "'invalid\[123\]' declared here" "" { target c++ xfail c++ } } */
+#pragma omp allocate(invalid1) align(128) allocator(ompx_gnu_pinned_bogus_1) /* { dg-error "'allocator' clause requires a predefined allocator as 'invalid1' is static" "" { target c } } */
+/* { dg-error "'allocator' clause requires a constant predefined allocator" "" { target c++ } .-1 } */
+/* { dg-note "because one or more variables with static storage duration appear in the 'allocate' directive" "" { target c++ xfail c++ } .-2 } */
+/* { dg-note "expression evaluates to '9'" "" { target c++ } .-3 } */
#pragma omp allocate(okay1) align(128) allocator(ompx_gnu_pinned_mem_alloc) /* Okay */
-#pragma omp allocate(invalid2) align(128) allocator(ompx_gnu_pinned_bogus_2) /* { dg-error "'allocator' clause requires a predefined allocator as 'invalid2' is static" "" { xfail c++ } } */
-#pragma omp allocate(invalid3) align(128) allocator(ompx_gnu_pinned_bogus_3) /* { dg-error "'allocator' clause requires a predefined allocator as 'invalid3' is static" "" { xfail c++ } } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-4 } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-4 } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-4 } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-4 } */
+#pragma omp allocate(invalid2) align(128) allocator(ompx_gnu_pinned_bogus_2) /* { dg-error "'allocator' clause requires a predefined allocator as 'invalid2' is static" "" { target c } } */
+/* { dg-error "'allocator' clause requires a constant predefined allocator" "" { target c++ } .-1 } */
+/* { dg-note "because one or more variables with static storage duration appear in the 'allocate' directive" "" { target c++ xfail c++ } .-2 } */
+/* { dg-note "expression evaluates to '199'" "" { target c++ } .-3 } */
+#pragma omp allocate(invalid3) align(128) allocator(ompx_gnu_pinned_bogus_3) /* { dg-error "'allocator' clause requires a predefined allocator as 'invalid3' is static" "" { target c } } */
+/* { dg-error "'allocator' clause requires a constant predefined allocator" "" { target c++ } .-1 } */
+/* { dg-note "because one or more variables with static storage duration appear in the 'allocate' directive" "" { target c++ xfail c++ } .-2 } */
+/* { dg-note "expression evaluates to '2001'" "" { target c++ } .-3 } */
+
+/* { dg-note "because one or more variables with static storage duration appear in the 'allocate' directive" "" { target c++ } 0 } */
@@ -21,11 +21,11 @@ foo ()
omp_allocator_handle_t my_allocator = omp_default_mem_alloc;
int a, b;
static int c;
-#pragma omp allocate (a) /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } } */
-#pragma omp allocate (b) allocator(my_allocator) /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } } */
+#pragma omp allocate (a)
+#pragma omp allocate (b) allocator(my_allocator)
#pragma omp allocate(c) align(32)
/* { dg-message "'allocator' clause required for static variable 'c'" "" { target c } .-1 } */
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-2 } */
+ /* { dg-error "'allocator' clause must be specified" "" { target c++ } .-2 } */
}
void
@@ -34,14 +34,14 @@ bar ()
int a, a2, b;
omp_allocator_handle_t my_allocator;
#pragma omp allocate /* { dg-error "expected '\\(' before end of line" } */
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
#pragma omp allocate allocator(my_allocator) /* { dg-error "expected '\\(' before 'allocator'" } */
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
#pragma omp allocate(a) foo(my_allocator) /* { dg-error "expected 'allocator'" } */
/* { dg-error "expected end of line before '\\(' token" "" { target *-*-* } .-1 } */
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-2 } */
-#pragma omp allocate(a2) allocator(b) /* { dg-error "'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'" "todo: cp/semantics.c" { xfail c++ } } */
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
+#pragma omp allocate(a2) allocator(b)
+ /* { dg-error "'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'" "" { target c } .-1 } */
+ /* { dg-error "invalid conversion from 'int' to 'omp_allocator_handle_t'" "" { target c++ } .-2 } */
+ /* We have diverging behavior here between c and c++ due to a difference in
+ order of diagnostics, as well as diverging semantics, this should probably be unified. */
}
@@ -50,32 +50,27 @@ align_test ()
{
int i1,i2,i3,i4,i5,i6;
#pragma omp allocate(i1) allocator(omp_default_mem_alloc), align(32)
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
#pragma omp allocate(i2) align ( 32 ),allocator(omp_default_mem_alloc)
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
#pragma omp allocate(i3),allocator(omp_default_mem_alloc) align(32)
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
#pragma omp allocate(i4) align ( 32 ) allocator(omp_default_mem_alloc)
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
#pragma omp allocate(i5) allocator ( omp_high_bw_mem_alloc ), align ( 32 ) allocator(omp_default_mem_alloc)
/* { dg-error "too many 'allocator' clauses" "" { target *-*-* } .-1 } */
/* { dg-error "expected end of line before '\\)' token" "" { target *-*-* } .-2 } */
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-3 } */
#pragma omp allocate(i6) align ( 32 ), align(32) allocator(omp_default_mem_alloc)
/* { dg-error "too many 'align' clauses" "" { target *-*-* } .-1 } */
/* { dg-error "expected end of line before '\\)' token" "" { target *-*-* } .-2 } */
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-3 } */
}
void
align_test2 ()
{
int i, i2,i3;
- #pragma omp allocate(i) align (32.0) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
+ #pragma omp allocate(i) align (32.0)
+ /* { dg-error {could not convert '(?:\(double\))?3\.2e\+1l?' from 'double' to '(?:long )?unsigned int'} "" { target c++ } .-1 } */
+ /* { dg-error {conversion from 'double' to '(?:long )?unsigned int' in a converted constant expression} "" { target c++ } .-2 } */
+ /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-3 } */
#pragma omp allocate(i2) align ( 31 ) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
#pragma omp allocate(i3) align ( -32 ) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */
- /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
+ /* { dg-bogus "narrowing conversion of '-32' from" "" { target c++ xfail c++11 } .-1 } */
}
@@ -1,93 +1,78 @@
-typedef enum omp_allocator_handle_t
-#if __cplusplus >= 201103L
-: __UINTPTR_TYPE__
-#endif
-{
- omp_null_allocator = 0,
- omp_default_mem_alloc = 1,
- omp_large_cap_mem_alloc = 2,
- omp_const_mem_alloc = 3,
- omp_high_bw_mem_alloc = 4,
- omp_low_lat_mem_alloc = 5,
- omp_cgroup_mem_alloc = 6,
- omp_pteam_mem_alloc = 7,
- omp_thread_mem_alloc = 8,
- __ompx_last_mem_alloc = omp_thread_mem_alloc,
- __omp_allocator_handle_t_max__ = __UINTPTR_MAX__
-} omp_allocator_handle_t;
-
-
-static int A[5] = {1,2,3,4,5};
+#include "allocate-allocator-handle.h"
+
+static int A1[5] = {1,2,3,4,5}; /* { dg-line A_decl } */
static int A2[5] = {1,2,3,4,5};
static int A3[5] = {1,2,3,4,5};
-static int A4[5] = {1,2,3,4,5};
-static int A5[5] = {1,2,3,4,5};
-int B, C, C2, D;
+static int A4[5] = {1,2,3,4,5}; /* { dg-line A4_decl } */
+static int A5[5] = {1,2,3,4,5}; /* { dg-line A5_decl } */
+int B, C, C2, D; /* { dg-note "declared here" "" { xfail c++ } } */
/* If the following fails because of added predefined allocators, please update
+ - include/gomp-constants.h's GOMP_OMP_PREDEF_ALLOC_MAX or GOMP_OMPX_PREDEF_ALLOC_MAX
- c/c-parser.c's c_parser_omp_allocate
- fortran/openmp.cc's is_predefined_allocator
- libgomp/env.c's parse_allocator
- libgomp/libgomp.texi (document the new values - multiple locations)
+ ensure that the memory-spaces are also up to date. */
-#pragma omp allocate(A) align(32) allocator((omp_allocator_handle_t) 9) /* { dg-error "'allocator' clause requires a predefined allocator as 'A' is static" "" { xfail c++ } } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
-
+#pragma omp allocate(A1) align(32) allocator((omp_allocator_handle_t) 9)
+/* { dg-error "'allocator' clause requires a predefined allocator as 'A1' is static" "" { target c } .-1 } */
+/* { dg-error "'allocator' clause requires a constant predefined allocator" "" { target c++ } .-2 } */
+/* { dg-message "because one or more variables with static storage duration appear in the 'allocate' directive" "" { target c++ xfail c++ } .-3 } */
+/* { dg-message "expression evaluates to '9'" "" { target c++ } .-4 } */
+/* { dg-note "'A1' declared here" "" { target c++ xfail c++ } A_decl } */
// typo in allocator name:
#pragma omp allocate(A2) allocator(omp_low_latency_mem_alloc)
/* { dg-error "'omp_low_latency_mem_alloc' undeclared here \\(not in a function\\); did you mean 'omp_low_lat_mem_alloc'\\?" "" { target c } .-1 } */
/* { dg-error "'omp_low_latency_mem_alloc' was not declared in this scope; did you mean 'omp_low_lat_mem_alloc'\\?" "" { target c++ } .-2 } */
/* { dg-error "'allocator' clause required for static variable 'A2'" "" { target c } .-3 } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-4 } */
/* align be const multiple of 2 */
#pragma omp allocate(A3) align(31) allocator(omp_default_mem_alloc) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
-/* allocator missing (required as A is static) */
-#pragma omp allocate(A4) align(32) /* { dg-error "'allocator' clause required for static variable 'A4'" "" { xfail c++ } } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
+/* allocator missing (required as A4 is static) */
+#pragma omp allocate(A4) align(32)
+/* { dg-error "'allocator' clause required for static variable 'A4'" "" { target c } .-1 } */
+/* { dg-error "'allocator' clause must be specified" "" { target c++ } .-2 } */
+/* { dg-message "because one or more variables with static storage duration appear in the 'allocate' directive" "" { target c++ xfail c++ } .-3 } */
+/* { dg-note "'A4' declared here" "" { target c++ xfail c++ } A4_decl } */
/* "expression in the clause must be a constant expression that evaluates to one of the
predefined memory allocator values -> omp_low_lat_mem_alloc" */
#pragma omp allocate(B) allocator((omp_allocator_handle_t) (omp_high_bw_mem_alloc+1)) align(32) /* OK: omp_low_lat_mem_alloc */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
#pragma omp allocate(C) allocator((omp_allocator_handle_t) 2) /* OK: omp_large_cap_mem_alloc */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
-#pragma omp allocate(A5) align(32) allocator(omp_null_allocator) /* { dg-error "'allocator' clause requires a predefined allocator as 'A5' is static" "" { xfail c++ } } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
+#pragma omp allocate(A5) align(32) allocator(omp_null_allocator)
+/* { dg-error "'allocator' clause requires a predefined allocator as 'A5' is static" "" { target c } .-1 } */
+/* { dg-error "'allocator' clause requires a constant predefined allocator" "" { target c++ } .-2 } */
+/* { dg-message "because one or more variables with static storage duration appear in the 'allocate' directive" "" { target c++ xfail c++ } .-3 } */
+/* { dg-message "expression evaluates to '0'" "" { target c++ } .-4 } */
+/* { dg-note "'A5' declared here" "" { target c++ xfail c++ } A5_decl } */
#pragma omp allocate(C2) align(32) allocator(omp_large_cap_mem_alloc)
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
// allocate directive in same TU
int f()
{
#pragma omp allocate(D) align(32) allocator(omp_large_cap_mem_alloc) /* { dg-error "'allocate' directive must be in the same scope as 'D'" "" { xfail c++ } } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
-/* { dg-note "declared here" "" { target c } 25 } */
- return A[0];
+ return A1[0];
}
int g()
{
- int a2=1, b2=2;
+ int a2=1, b2=2; /* { dg-line g_a2_b2_decl } */
#pragma omp allocate(a2)
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
- #pragma omp allocate(a2) /* { dg-error "'a2' already appeared as list item in an 'allocate' directive" "" { xfail c++ } } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
+ #pragma omp allocate(a2) /* { dg-error "'a2' already appeared as list item in an 'allocate' directive" } */
+/* { dg-note "'a2' previously appeared here" "" { target c++ } .-2 } */
{
int c2=3;
#pragma omp allocate(c2, b2) /* { dg-error "'allocate' directive must be in the same scope as 'b2'" "" { xfail c++ } } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
-/* { dg-note "declared here" "" { target c } .-9 } */
+/* { dg-note "declared here" "" { target *-*-* xfail c++ } g_a2_b2_decl } */
return c2+a2+b2;
}
}
@@ -95,15 +80,20 @@ int g()
int h(int q)
{
#pragma omp allocate(q) /* { dg-error "function parameter 'q' may not appear as list item in an 'allocate' directive" "" { xfail c++ } } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
+/* { dg-note "parameter 'q' declared here" "" { target c++ xfail c++ } .-3 } */
return q;
}
int
k ()
{
- static int var3 = 8;
- #pragma omp allocate(var3) allocator((omp_allocator_handle_t)-1L) /* { dg-error "'allocator' clause requires a predefined allocator as 'var3' is static" "" { target c } } */
-/* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } .-1 } */
+ static int var3 = 8; /* { dg-note "'var3' declared here" "" { target c++ xfail c++ } } */
+ #pragma omp allocate(var3) allocator((omp_allocator_handle_t)-1L)
+ /* { dg-error "'allocator' clause requires a predefined allocator as 'var3' is static" "" { target c } .-1 } */
+ /* { dg-error "'allocator' clause requires a constant predefined allocator" "" { target c++ } .-2 } */
+ /* { dg-message "because one or more variables with static storage duration appear in the 'allocate' directive" "" { target c++ xfail c++ } .-3 } */
+ /* { dg-message "expression evaluates to '\\d+'" "" { target c++ } .-4 } */
return var3;
}
+
+/* { dg-bogus "because one or more variables with static storage duration appear in the 'allocate' directive" "" { xfail c++ } 0 } */
new file mode 100644
@@ -0,0 +1,19 @@
+typedef __UINTPTR_TYPE__ omp_uintptr_t;
+
+typedef enum omp_allocator_handle_t
+#if __cplusplus >= 201103L
+: omp_uintptr_t
+#endif
+{
+ omp_null_allocator = 0,
+ omp_default_mem_alloc = 1,
+ omp_large_cap_mem_alloc = 2,
+ omp_const_mem_alloc = 3,
+ omp_high_bw_mem_alloc = 4,
+ omp_low_lat_mem_alloc = 5,
+ omp_cgroup_mem_alloc = 6,
+ omp_pteam_mem_alloc = 7,
+ omp_thread_mem_alloc = 8,
+ __ompx_last_mem_alloc = omp_thread_mem_alloc,
+ __omp_allocator_handle_t_max__ = __UINTPTR_MAX__
+} omp_allocator_handle_t;
@@ -20,7 +20,6 @@ foo (void)
int i, k = 0, l = 0;
#pragma omp allocate, (i) /* { dg-error "expected '\\\(' before ',' token" } */
/* { dg-error "expected end of line before ',' token" "" { target c++ } .-1 } */
- /* { dg-message "not yet supported" "" { target c++ } .-2 } */
#pragma omp critical, (bar) /* { dg-error "expected an OpenMP clause before '\\\(' token" } */
;
#pragma omp flush, (k, l) /* { dg-error "expected '\\\(' or end of line before ',' token" "" { target c } } */
@@ -22,7 +22,7 @@ f (omp_allocator_handle_t my_alloc)
#pragma omp target
{
int a; /* { dg-error "'my_alloc' in 'allocator' clause inside a target region must be specified in an 'uses_allocators' clause on the 'target' directive" "not yet implemented" { xfail *-*-* } } */
- #pragma omp allocate(a) allocator(my_alloc) /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } } */
+ #pragma omp allocate(a) allocator(my_alloc)
a = 5;
void *prt = omp_alloc(32, my_alloc);
#pragma omp parallel allocate(allocator(my_alloc) : a) firstprivate(a) /* { dg-error "allocator 'my_alloc' in 'allocate' clause inside a target region must be specified in an 'uses_allocators' clause on the 'target' directive" "not yet implemented" { xfail *-*-* } } */
@@ -37,7 +37,7 @@ g (omp_allocator_handle_t my_alloc)
#pragma omp target uses_allocators(my_alloc)
{
int a;
- #pragma omp allocate(a) allocator(my_alloc) /* { dg-message "sorry, unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } } */
+ #pragma omp allocate(a) allocator(my_alloc)
a = 5;
void *prt = omp_alloc(32, my_alloc);
#pragma omp parallel allocate(allocator(my_alloc) : a) firstprivate(a)
new file mode 100644
@@ -0,0 +1,115 @@
+/* { dg-do compile { target c++17 } } */
+#include "allocate-allocator-handle.h"
+
+/* Invalid allocator clause. */
+
+template<auto Alloc>
+void auto_nttp_allocator()
+{
+ int a;
+ #pragma omp allocate(a) allocator(Alloc) /* { dg-error "invalid conversion from 'int' to 'omp_allocator_handle_t'" } */
+}
+
+template<auto Alloc>
+void auto_nttp_allocator_uninstantiated()
+{
+ int a;
+ #pragma omp allocate(a) allocator(Alloc)
+}
+
+#define DEFINITELY_NOT_PREDEFINED static_cast<omp_allocator_handle_t>(1024)
+
+void instantiate_auto_nttp_allocator()
+{
+ auto_nttp_allocator<omp_default_mem_alloc>(); /* { dg-bogus "required from here" } */
+ auto_nttp_allocator<DEFINITELY_NOT_PREDEFINED>(); /* { dg-bogus "required from here" } */
+ auto_nttp_allocator<1>(); /* { dg-message "required from here" } */
+}
+
+#undef DEFINITELY_NOT_PREDEFINED
+
+template<auto Alloc>
+void auto_nttp_allocator_static_0()
+{
+ static int a;
+ #pragma omp allocate(a) allocator(Alloc)
+ /* { dg-error "invalid conversion from 'int' to 'omp_allocator_handle_t'" "" { target *-*-* } .-1 } */
+ /* { dg-error "'allocator' clause requires a constant predefined allocator" "" { xfail *-*-* } .-2 } */
+}
+
+template<auto Alloc>
+void auto_nttp_allocator_static_1()
+{
+ static int a;
+ #pragma omp allocate(a) allocator(Alloc) /* { dg-error "invalid conversion from 'int' to 'omp_allocator_handle_t'" } */
+}
+
+template<auto Alloc>
+void auto_nttp_allocator_static_2()
+{
+ static int a;
+ #pragma omp allocate(a) allocator(Alloc)
+ /* { dg-error "'allocator' clause requires a constant predefined allocator" "" { xfail *-*-* } .-1 } */
+}
+
+template<auto Alloc>
+void auto_nttp_allocator_static_uninstantiated()
+{
+ static int a;
+ #pragma omp allocate(a) allocator(Alloc)
+}
+
+#define DEFINITELY_NOT_PREDEFINED static_cast<omp_allocator_handle_t>(1024)
+
+void instantiate_auto_nttp_allocator_static()
+{
+ auto_nttp_allocator_static_0<omp_default_mem_alloc>(); /* { dg-bogus "required from here" } */
+ auto_nttp_allocator_static_0<1>(); /* { dg-message "required from here" } */
+ auto_nttp_allocator_static_0<DEFINITELY_NOT_PREDEFINED>(); /* { dg-message "required from here" } */
+
+ auto_nttp_allocator_static_1<omp_default_mem_alloc>(); /* { dg-bogus "required from here" } */
+ auto_nttp_allocator_static_1<1>(); /* { dg-message "required from here" } */
+
+ auto_nttp_allocator_static_2<omp_default_mem_alloc>(); /* { dg-bogus "required from here" } */
+ auto_nttp_allocator_static_2<DEFINITELY_NOT_PREDEFINED>(); /* { dg-message "required from here" } */
+}
+
+#undef DEFINITELY_NOT_PREDEFINED
+
+/* { dg-bogus "'allocator' clause requires a constant predefined allocator" "" { xfail *-*-* } 0 } */
+
+/* Invalid align clause */
+
+template<auto Align>
+void auto_nttp_align_uninstantiated()
+{
+ int a;
+ #pragma omp allocate(a) align(Align)
+}
+
+template<auto Align>
+void auto_nttp_align_0()
+{
+ int a;
+ #pragma omp allocate(a) align(Align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { xfail *-*-* } } */
+}
+
+template<auto Align>
+void auto_nttp_align_1()
+{
+ int a;
+ #pragma omp allocate(a) align(Align)
+ /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { xfail *-*-* } .-1 } */
+ /* { dg-error {could not convert 'nullptr' from 'std::nullptr_t' to '(?:long )?unsigned int'} "" { target *-*-* } .-2 } */
+}
+
+void instantiate_auto_nttp_align()
+{
+ auto_nttp_align_0<32>(); /* { dg-bogus "required from here" } */
+ auto_nttp_align_0<42>(); /* { dg-message "required from here" } */
+
+ auto_nttp_align_1<32>(); /* { dg-bogus "required from here" } */
+ auto_nttp_align_1<nullptr>(); /* { dg-message "required from here" } */
+}
+
+/* { dg-bogus "'align' clause argument needs to be positive constant power of two integer expression" "" { xfail *-*-* } 0 } */
new file mode 100644
@@ -0,0 +1,18 @@
+/* Just a silly ICE I came across by accident, easy fix, might be a problem
+ with lookup_name but I'm not certain.
+
+ For some reason, the floating 'a;' breaks lookup_name in tsubst_stmt during
+ substitution of the allocate directive, despite it being found no problem
+ during parsing of the allocate directive's var list.
+ I don't have time to investigate it further so I'm just going to fix it
+ by checking for NULL_TREE on the return of lookup_name. */
+
+template<typename>
+void f()
+{
+ a; /* { dg-error "'a' was not declared in this scope" } */
+ int a = 42;
+ #pragma omp allocate(a)
+}
+template void f<void>();
+
new file mode 100644
@@ -0,0 +1,38 @@
+/* { dg-do compile { target c++14 } } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+/* Ensure allocate directive does not break attributes on a decl (and vice-versa). */
+
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\(64," 2 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\(32," 2 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\(16," 2 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\(8," 2 "gimple" } } */
+
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_free" 8 "gimple" } } */
+
+template<unsigned Align>
+int test0 ()
+{
+ int a [[deprecated]];
+ #pragma omp allocate(a) align(Align)
+ return a; /* { dg-warning "'a' is deprecated" } */
+}
+
+template int test0<64u>();
+template int test0<32u>();
+template int test0<16u>();
+template int test0<8u>();
+
+
+template<unsigned Align>
+int test1 ()
+{
+ int a [[deprecated]];
+ #pragma omp allocate(a) align(Align)
+ return a; /* { dg-warning "'a' is deprecated" } */
+}
+
+template int test1<8u>();
+template int test1<16u>();
+template int test1<32u>();
+template int test1<64u>();
new file mode 100644
@@ -0,0 +1,28 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+/* Misc valid constexpr expressions in a clause.
+ TODO: Really needs more cases. */
+
+/* Not constexpr, invalid value, not called. */
+template<typename T>
+int get_align(T) { return 42; }
+
+template<typename T>
+int dependent_call_adl()
+{
+ int a = 42;
+ #pragma omp allocate(a) align(get_align(T{}))
+ return a;
+}
+
+namespace foo
+{
+ struct S {};
+
+ constexpr int get_align(S) { return 32; }
+}
+
+template int dependent_call_adl<foo::S>();
+/* { dg-final { scan-tree-dump "__builtin_GOMP_alloc \\(32," "gimple" } } */
+/* { dg-final { scan-tree-dump "__builtin_GOMP_free" "gimple" } } */
new file mode 100644
@@ -0,0 +1,411 @@
+/* { dg-additional-options "-fdump-tree-gimple" } */
+/* { dg-skip-if "" { ilp32 } } */
+
+#include "allocate-allocator-handle.h"
+
+/* All cases are valid and should work. */
+
+/* Note: the OpenMP allocate directive is not supposed to change the alignof
+ of expr a, it was decided to have too many edge cases. The static asserts
+ in these cases correctly test that it remains untouched.
+
+ Note: cases that take omp_default_mem_alloc and have no align clause elide
+ the GOMP_alloc/GOMP_free transformations. */
+
+/* 86 total cases. */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc" 86 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_free" 86 "gimple" } } */
+
+/* FIXME: This almost certainly needs tweaks for systems where alignof(int)
+ is not 4. */
+
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(32," 31 "gimple" } } */
+
+/* 9 * 3 = 27 cases scanned here. */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(8," 3 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(16," 3 "gimple" } } */
+/* Align==32 scanned above. */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(64," 3 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(128," 3 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(256," 3 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(512," 3 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(1024," 3 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(2048," 3 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(4096," 3 "gimple" } } */
+
+/* The align argument is at least alignof(int) (4) in all cases, thus we can subtract
+ the cases where align >= 8 from the total cases to determine how many cases
+ there are where align == 4.
+ 86 - (27 + 31) = 28.
+ Please update this math if changes are made to this test so everything
+ is (somewhat) coherent. */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(4," 28 "gimple" } } */
+
+
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(\\d+, \\d+, 0B\\\)" 29 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(\\d+, \\d+, 1\\\)" 22 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(\\d+, \\d+, 2\\\)" 31 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(\\d+, \\d+, 3\\\)" 2 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(\\d+, \\d+, 4\\\)" 2 "gimple" } } */
+
+struct S0 {
+ int _v;
+ S0(int v) : _v(v) {}
+ operator int() const { return 42; }
+};
+
+struct S1 {
+ int _v[2];
+ S1(int v) : _v() {
+ int *end = _v + sizeof(_v) / sizeof(*_v);
+ for (int *it = _v; it != end; ++it)
+ *it = v;
+ }
+ operator int() const { return 42; }
+};
+
+/**********************
+ * dependent variable *
+ **********************/
+
+template<typename T>
+T dep_local()
+{
+ T a = 42;
+ #pragma omp allocate(a)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(T));
+ #endif
+ return a;
+}
+
+template<typename T>
+T dep_local_align()
+{
+ T a = 42;
+ #pragma omp allocate(a) align(32)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(T));
+ #endif
+ return a;
+}
+
+template<typename T>
+T dep_local_alloc_0()
+{
+ T a = 42;
+ #pragma omp allocate(a) allocator(omp_default_mem_alloc)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(T));
+ #endif
+ return a;
+}
+
+template<typename T>
+T dep_local_alloc_1()
+{
+ T a = 42;
+ #pragma omp allocate(a) allocator(omp_large_cap_mem_alloc)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(T));
+ #endif
+ return a;
+}
+
+template<typename T>
+T dep_local_align_alloc_0()
+{
+ T a = 42;
+ #pragma omp allocate(a) align(32) allocator(omp_default_mem_alloc)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(T));
+ #endif
+ return a;
+}
+
+template<typename T>
+T dep_local_align_alloc_1()
+{
+ T a = 42;
+ #pragma omp allocate(a) align(32) allocator(omp_large_cap_mem_alloc)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(T));
+ #endif
+ return a;
+}
+
+/* 5 cases per expansion, dep_local_alloc_0 uses omp_default_mem_alloc
+ without an align clause.
+ 2 allocator==0B,
+ 1 allocator==1 (because 1 case elided),
+ 2 allocator==2 cases per expansion.
+
+ 3 align==32 cases per expansion. */
+#define INSTANTIATE_ALL_WITH_T(type) \
+ do { \
+ type v0 = dep_local<type>(); \
+ type v1 = dep_local_align<type>(); \
+ type v2 = dep_local_alloc_0<type>(); \
+ type v3 = dep_local_alloc_1<type>(); \
+ type v4 = dep_local_align_alloc_0<type>(); \
+ type v5 = dep_local_align_alloc_1<type>(); \
+ static_cast<void>(v0); \
+ static_cast<void>(v1); \
+ static_cast<void>(v2); \
+ static_cast<void>(v3); \
+ static_cast<void>(v4); \
+ static_cast<void>(v5); \
+ } while (false)
+
+/* 4 * 5 cases, 20.
+ 4 * 2 allocator==0B cases, 8.
+ 4 * 1 allocator==1 cases, 4.
+ 4 * 2 allocator==2 cases, 8.
+ 4 * 3 align==32 cases, 12. */
+void instantiate_dep_tests()
+{
+ INSTANTIATE_ALL_WITH_T(int);
+ INSTANTIATE_ALL_WITH_T(float);
+ INSTANTIATE_ALL_WITH_T(S0);
+ INSTANTIATE_ALL_WITH_T(S1);
+}
+
+#undef INSTANTIATE_ALL_WITH_T
+
+/**********************
+ * template parameter *
+ **********************/
+
+template<typename T>
+int template_parm(T)
+{
+ int a = 42;
+ #pragma omp allocate(a)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(int));
+ #endif
+ return a;
+}
+
+template<typename T>
+int template_parm_align(T)
+{
+ int a = 42;
+ #pragma omp allocate(a) align(32)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(int));
+ #endif
+ return a;
+}
+
+template<typename T>
+int template_parm_alloc_0(T)
+{
+ int a = 42;
+ #pragma omp allocate(a) allocator(omp_default_mem_alloc)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(int));
+ #endif
+ return a;
+}
+
+template<typename T>
+int template_parm_alloc_1(T)
+{
+ int a = 42;
+ #pragma omp allocate(a) allocator(omp_large_cap_mem_alloc)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(int));
+ #endif
+ return a;
+}
+
+template<typename T>
+int template_parm_align_alloc_0(T)
+{
+ int a = 42;
+ #pragma omp allocate(a) align(32) allocator(omp_default_mem_alloc)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(int));
+ #endif
+ return a;
+}
+
+template<typename T>
+int template_parm_align_alloc_1(T)
+{
+ int a = 42;
+ #pragma omp allocate(a) align(32) allocator(omp_large_cap_mem_alloc)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(int));
+ #endif
+ return a;
+}
+
+/* 5 cases per expansion, template_parm_alloc_0 uses omp_default_mem_alloc
+ without an align clause.
+ 2 allocator==0B,
+ 1 allocator==1 (because 1 case elided),
+ 2 allocator==2 cases per expansion.
+
+ 3 align==32 cases per expansion. */
+#define INSTANTIATE_ALL_WITH_T(type) \
+ do { \
+ type a = 42; \
+ int v0 = template_parm(a); \
+ int v1 = template_parm_align(a); \
+ int v2 = template_parm_alloc_0(a); \
+ int v3 = template_parm_alloc_1(a); \
+ int v4 = template_parm_align_alloc_0(a); \
+ int v5 = template_parm_align_alloc_1(a); \
+ static_cast<void>(v0); \
+ static_cast<void>(v1); \
+ static_cast<void>(v2); \
+ static_cast<void>(v3); \
+ static_cast<void>(v4); \
+ static_cast<void>(v5); \
+ } while (false)
+
+/* 4 * 5 cases, 20.
+ 4 * 2 allocator==0B cases, 8.
+ 4 * 1 allocator==1 cases, 4.
+ 4 * 2 allocator==2 cases, 8.
+ 4 * 3 align==32 cases, 12. */
+void instantiate_template_parm_tests()
+{
+ INSTANTIATE_ALL_WITH_T(int);
+ INSTANTIATE_ALL_WITH_T(float);
+ INSTANTIATE_ALL_WITH_T(S0);
+ INSTANTIATE_ALL_WITH_T(S1);
+}
+
+#undef INSTANTIATE_ALL_WITH_T
+
+/*************************************
+ * non-type template parameter align *
+ *************************************/
+
+template<int Align>
+int nttp_align()
+{
+ int a = 42;
+ #pragma omp allocate(a) align(Align)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(int));
+ #endif
+ return a;
+}
+
+template<int Align>
+int nttp_align_alloc_0()
+{
+ int a = 42;
+ #pragma omp allocate(a) align(Align) allocator(omp_default_mem_alloc)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(int));
+ #endif
+ return a;
+}
+
+template<int Align>
+int nttp_align_alloc_1()
+{
+ int a = 42;
+ #pragma omp allocate(a) align(Align) allocator(omp_large_cap_mem_alloc)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(int));
+ #endif
+ return a;
+}
+
+/* 3 cases per expansion.
+ 1 allocator==0B, 1 allocator==1, 1 allocator==2 cases per expansion.
+ 3 align==value cases per expansion. */
+#define INSTANTIATE_ALL_WITH_V(value) \
+ do { \
+ int v0 = nttp_align<value>(); \
+ int v1 = nttp_align_alloc_0<value>(); \
+ int v2 = nttp_align_alloc_1<value>(); \
+ static_cast<void>(v0); \
+ static_cast<void>(v1); \
+ static_cast<void>(v2); \
+ } while (false)
+
+/* 13 * 3 cases, 39.
+ 13 * allocator==0B cases.
+ 13 * allocator==1 cases.
+ 13 * allocator==2 cases. */
+void instantiate_nttp_align_tests()
+{
+ /* Minimum align for int is 4, 3 * 3 alloc=4, 9 alloc=4. */
+ INSTANTIATE_ALL_WITH_V(1);
+ INSTANTIATE_ALL_WITH_V(2);
+ INSTANTIATE_ALL_WITH_V(4);
+ /* 3 alloc=N for the rest. */
+ INSTANTIATE_ALL_WITH_V(8);
+ INSTANTIATE_ALL_WITH_V(16);
+ INSTANTIATE_ALL_WITH_V(32);
+ INSTANTIATE_ALL_WITH_V(64);
+ INSTANTIATE_ALL_WITH_V(128);
+ INSTANTIATE_ALL_WITH_V(256);
+ INSTANTIATE_ALL_WITH_V(512);
+ INSTANTIATE_ALL_WITH_V(1024);
+ INSTANTIATE_ALL_WITH_V(2048);
+ INSTANTIATE_ALL_WITH_V(4096);
+}
+
+#undef INSTANTIATE_ALL_WITH_V
+
+
+template<omp_allocator_handle_t Alloc>
+int nttp_alloc()
+{
+ int a = 42;
+ #pragma omp allocate(a) allocator(Alloc)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(int));
+ #endif
+ return a;
+}
+
+template<omp_allocator_handle_t Alloc>
+int nttp_alloc_align()
+{
+ int a = 42;
+ #pragma omp allocate(a) align(32) allocator(Alloc)
+ #if __cplusplus >= 201103L
+ static_assert(alignof(a) == alignof(int));
+ #endif
+ return a;
+}
+
+/* 2 cases per expansion.
+ 2 allocator=value per expansion.
+ 1 alloc=32 per expansion. */
+#define INSTANTIATE_ALL_WITH_V(value) \
+ do { \
+ int v0 = nttp_alloc<value>(); \
+ int v1 = nttp_alloc_align<value>(); \
+ static_cast<void>(v0); \
+ static_cast<void>(v1); \
+ } while (false)
+
+/* 4 * 2 cases, 8,
+ -1 omp_default_mem_alloc without align, 7 cases.
+ 1 * allocator==1 (1 elided),
+ 2 * allocator==2,
+ 2 * allocator==3,
+ 2 * allocator==4 cases.
+ 4 * 1 alloc=32 cases, 4. */
+void instantiate_nttp_alloc_tests()
+{
+ INSTANTIATE_ALL_WITH_V(omp_default_mem_alloc);
+ INSTANTIATE_ALL_WITH_V(omp_large_cap_mem_alloc);
+ INSTANTIATE_ALL_WITH_V(omp_const_mem_alloc);
+ INSTANTIATE_ALL_WITH_V(omp_high_bw_mem_alloc);
+}
+
+#undef INSTANTIATE_ALL_WITH_V
+
+/* We are probably missing quite a few cases here.
+ Missing cases with a param in allocator clause. */
new file mode 100644
@@ -0,0 +1,392 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-skip-if "" { ilp32 } } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+#include "allocate-allocator-handle.h"
+
+/* Valid uses of lambda captures in an allocator clause. */
+
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc" 200 "gimple" } } */
+
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_free" 200 "gimple" } } */
+
+template<typename, typename>
+struct is_same { static constexpr bool value = false; };
+
+template<typename T>
+struct is_same<T, T> { static constexpr bool value = true; };
+
+struct S0 {
+ int _v;
+ S0(int v) : _v(v) {}
+ operator int() const { return 42; }
+};
+
+struct S1 {
+ int _v[2];
+ S1(int v) : _v() {
+ int *end = _v + sizeof(_v) / sizeof(*_v);
+ for (int *it = _v; it != end; ++it)
+ *it = v;
+ }
+ operator int() const { return 42; }
+};
+
+/* Suppresses int/float cases from being optimized out, I'm not sure if this is
+ going to be sufficient for all cases though. */
+int (*prevent_optimization)() = nullptr;
+
+#define BLANK_ARGUMENT
+
+/* Capturing with an initiaizer was added in C++14 */
+#if __cplusplus >= 201402L
+
+#define CAPTURE_LITERAL_INIT_LAMBDA(type) \
+do { \
+ auto capture_literal_init = [alloc = omp_default_mem_alloc](){ \
+ type a = prevent_optimization(); \
+ _Pragma("omp allocate(a) allocator(alloc)") \
+ return a; \
+ }; \
+ auto result = capture_literal_init(); \
+ static_assert(is_same<type, decltype(result)>::value); \
+} while (false)
+
+void test_capture_literal_init_nondep_lambdas()
+{
+ /* 4 cases */
+ CAPTURE_LITERAL_INIT_LAMBDA(int);
+ CAPTURE_LITERAL_INIT_LAMBDA(float);
+ CAPTURE_LITERAL_INIT_LAMBDA(S0);
+ CAPTURE_LITERAL_INIT_LAMBDA(S1);
+}
+
+/* 1 case per instantiation */
+template<typename T>
+void test_capture_literal_init_dependent_lambdas()
+{
+ CAPTURE_LITERAL_INIT_LAMBDA(T);
+}
+/* 4 cases */
+template void test_capture_literal_init_dependent_lambdas<int>();
+template void test_capture_literal_init_dependent_lambdas<float>();
+template void test_capture_literal_init_dependent_lambdas<S0>();
+template void test_capture_literal_init_dependent_lambdas<S1>();
+
+#undef CAPTURE_LITERAL_INIT_LAMBDA
+
+
+
+#define EXPLICIT_CAPTURE_WITH_INIT_LAMBDA(type, opt_tok, capture) \
+do { \
+ auto explicit_capture_with_init = [opt_tok alloc = capture](){ \
+ type a = prevent_optimization(); \
+ _Pragma("omp allocate(a) allocator(alloc)") \
+ return a; \
+ }; \
+ auto result = explicit_capture_with_init(); \
+ static_assert(is_same<type, decltype(result)>::value); \
+} while (false)
+
+/* 4 cases per expansion */
+#define TEST_LAMBDAS_WITH_TYPE(type, opt_tok) \
+do { \
+ EXPLICIT_CAPTURE_WITH_INIT_LAMBDA(type, opt_tok, alloc); \
+ EXPLICIT_CAPTURE_WITH_INIT_LAMBDA(type, opt_tok, alloc_ref); \
+ EXPLICIT_CAPTURE_WITH_INIT_LAMBDA(type, opt_tok, alloc_param); \
+ EXPLICIT_CAPTURE_WITH_INIT_LAMBDA(type, opt_tok, alloc_ref_param); \
+} while (false)
+
+/* 4*4 = 16 cases */
+void test_capture_by_value_with_init_nondep_lambdas(omp_allocator_handle_t alloc_param,
+ omp_allocator_handle_t& alloc_ref_param)
+{
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ omp_allocator_handle_t& alloc_ref = alloc;
+ TEST_LAMBDAS_WITH_TYPE(int, BLANK_ARGUMENT);
+ TEST_LAMBDAS_WITH_TYPE(float, BLANK_ARGUMENT);
+ TEST_LAMBDAS_WITH_TYPE(S0, BLANK_ARGUMENT);
+ TEST_LAMBDAS_WITH_TYPE(S1, BLANK_ARGUMENT);
+}
+
+/* 4*4 = 16 cases */
+void test_capture_by_ref_with_init_nondep_lambdas(omp_allocator_handle_t alloc_param,
+ omp_allocator_handle_t& alloc_ref_param)
+{
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ omp_allocator_handle_t& alloc_ref = alloc;
+ TEST_LAMBDAS_WITH_TYPE(int, &);
+ TEST_LAMBDAS_WITH_TYPE(float, &);
+ TEST_LAMBDAS_WITH_TYPE(S0, &);
+ TEST_LAMBDAS_WITH_TYPE(S1, &);
+}
+
+/* 4 cases per instantiation */
+template<typename T>
+void test_capture_by_value_with_init_dependent_lambdas(omp_allocator_handle_t alloc_param,
+ omp_allocator_handle_t& alloc_ref_param)
+{
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ omp_allocator_handle_t& alloc_ref = alloc;
+ TEST_LAMBDAS_WITH_TYPE(T, BLANK_ARGUMENT);
+}
+/* 4*4 = 16 cases */
+template void test_capture_by_value_with_init_dependent_lambdas<int>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_capture_by_value_with_init_dependent_lambdas<float>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_capture_by_value_with_init_dependent_lambdas<S0>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_capture_by_value_with_init_dependent_lambdas<S1>(omp_allocator_handle_t, omp_allocator_handle_t&);
+
+/* 4 cases per instantiation */
+template<typename T>
+void test_capture_by_ref_with_init_dependent_lambdas(omp_allocator_handle_t alloc_param,
+ omp_allocator_handle_t& alloc_ref_param)
+{
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ omp_allocator_handle_t& alloc_ref = alloc;
+ TEST_LAMBDAS_WITH_TYPE(T, &);
+}
+/* 4*4 = 16 cases */
+template void test_capture_by_ref_with_init_dependent_lambdas<int>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_capture_by_ref_with_init_dependent_lambdas<float>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_capture_by_ref_with_init_dependent_lambdas<S0>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_capture_by_ref_with_init_dependent_lambdas<S1>(omp_allocator_handle_t, omp_allocator_handle_t&);
+
+#undef EXPLICIT_CAPTURE_WITH_INIT_LAMBDA
+#undef TEST_LAMBDAS_WITH_TYPE
+
+#else
+/* There are 4 + 4 + 16 + 16 + 16 + 16 cases in the above, we replicate that
+ many so our count in dg-final isn't wrong in C++11 mode.
+ The counter isn't required but it doesn't hurt and might make it easier
+ to inspect the asm if we need to, that's the goal anyway. */
+
+#define DUMMY_ALLOC(counter) \
+do { \
+ auto dummy_ ##counter = [alloc](){ \
+ int a = prevent_optimization(); \
+ _Pragma("omp allocate(a) allocator(alloc)") \
+ return a; \
+ }; \
+ auto result = dummy_ ##counter(); \
+} while (false)
+
+#define DUMMY_X4(counter) \
+do { \
+ DUMMY_ALLOC(counter ##A); \
+ DUMMY_ALLOC(counter ##B); \
+ DUMMY_ALLOC(counter ##C); \
+ DUMMY_ALLOC(counter ##D); \
+} while (false)
+
+void dummy_cases(omp_allocator_handle_t alloc)
+{
+ /* 4x1 for test_capture_literal_init_nondep_lambdas */
+ DUMMY_X4(0);
+ /* 4x1 for test_capture_literal_init_dependent_lambdas */
+ DUMMY_X4(1);
+ /* 4x4 for test_capture_by_value_with_init_nondep_lambdas */
+ DUMMY_X4(2);
+ DUMMY_X4(3);
+ DUMMY_X4(4);
+ DUMMY_X4(5);
+ /* 4x4 for test_capture_by_value_with_init_dependent_lambdas */
+ DUMMY_X4(6);
+ DUMMY_X4(7);
+ DUMMY_X4(8);
+ DUMMY_X4(9);
+ /* 4x4 for test_capture_by_ref_with_init_dependent_lambdas */
+ DUMMY_X4(9);
+ DUMMY_X4(10);
+ DUMMY_X4(11);
+ DUMMY_X4(12);
+ /* 4x4 for test_capture_by_ref_with_init_nondep_lambdas */
+ DUMMY_X4(13);
+ DUMMY_X4(14);
+ DUMMY_X4(15);
+ DUMMY_X4(16);
+}
+
+#undef DUMMY_ALLOC
+#undef DUMMY_X4
+
+#endif
+
+
+#define EXPLICIT_CAPTURE_LAMBDAS(type, opt_tok) \
+do { \
+ auto explicit_capture0 = [opt_tok alloc](){ \
+ type a = prevent_optimization(); \
+ _Pragma("omp allocate(a) allocator(alloc)") \
+ return a; \
+ }; \
+ auto result0 = explicit_capture0(); \
+ static_assert(is_same<type, decltype(result0)>::value); \
+ \
+ auto explicit_capture1 = [opt_tok alloc_ref](){ \
+ type a = prevent_optimization(); \
+ _Pragma("omp allocate(a) allocator(alloc_ref)") \
+ return a; \
+ }; \
+ auto result1 = explicit_capture1(); \
+ static_assert(is_same<type, decltype(result1)>::value); \
+ \
+ auto explicit_capture2 = [opt_tok alloc_param](){ \
+ type a = prevent_optimization(); \
+ _Pragma("omp allocate(a) allocator(alloc_param)") \
+ return a; \
+ }; \
+ auto result2 = explicit_capture2(); \
+ static_assert(is_same<type, decltype(result2)>::value); \
+ \
+ auto explicit_capture3 = [opt_tok alloc_ref_param](){ \
+ type a = prevent_optimization(); \
+ _Pragma("omp allocate(a) allocator(alloc_ref_param)") \
+ return a; \
+ }; \
+ auto result3 = explicit_capture3(); \
+ static_assert(is_same<type, decltype(result3)>::value); \
+} while (false)
+
+void test_explicit_capture_by_value_nondep_lambdas(omp_allocator_handle_t alloc_param,
+ omp_allocator_handle_t& alloc_ref_param)
+{
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ omp_allocator_handle_t& alloc_ref = alloc;
+ EXPLICIT_CAPTURE_LAMBDAS(int, BLANK_ARGUMENT);
+ EXPLICIT_CAPTURE_LAMBDAS(float, BLANK_ARGUMENT);
+ EXPLICIT_CAPTURE_LAMBDAS(S0, BLANK_ARGUMENT);
+ EXPLICIT_CAPTURE_LAMBDAS(S1, BLANK_ARGUMENT);
+}
+
+void test_explicit_capture_by_ref_nondep_lambdas(omp_allocator_handle_t alloc_param,
+ omp_allocator_handle_t& alloc_ref_param)
+{
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ omp_allocator_handle_t& alloc_ref = alloc;
+ EXPLICIT_CAPTURE_LAMBDAS(int, &);
+ EXPLICIT_CAPTURE_LAMBDAS(float, &);
+ EXPLICIT_CAPTURE_LAMBDAS(S0, &);
+ EXPLICIT_CAPTURE_LAMBDAS(S1, &);
+}
+
+template<typename T>
+void test_explicit_capture_by_value_dependent_lambdas(omp_allocator_handle_t alloc_param,
+ omp_allocator_handle_t& alloc_ref_param)
+{
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ omp_allocator_handle_t& alloc_ref = alloc;
+ EXPLICIT_CAPTURE_LAMBDAS(T, BLANK_ARGUMENT);
+}
+template void test_explicit_capture_by_value_dependent_lambdas<int>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_explicit_capture_by_value_dependent_lambdas<float>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_explicit_capture_by_value_dependent_lambdas<S0>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_explicit_capture_by_value_dependent_lambdas<S1>(omp_allocator_handle_t, omp_allocator_handle_t&);
+
+
+template<typename T>
+void test_explicit_capture_by_ref_dependent_lambdas(omp_allocator_handle_t alloc_param,
+ omp_allocator_handle_t& alloc_ref_param)
+{
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ omp_allocator_handle_t& alloc_ref = alloc;
+ EXPLICIT_CAPTURE_LAMBDAS(T, &);
+}
+template void test_explicit_capture_by_ref_dependent_lambdas<int>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_explicit_capture_by_ref_dependent_lambdas<float>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_explicit_capture_by_ref_dependent_lambdas<S0>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_explicit_capture_by_ref_dependent_lambdas<S1>(omp_allocator_handle_t, omp_allocator_handle_t&);
+
+#undef EXPLICIT_CAPTURE_LAMBDA
+#undef TEST_LAMBDAS_WITH_TYPE
+
+
+#define DEFAULT_CAPTURE_LAMBDAS(type, capture) \
+do { \
+ auto default_capture0 = [capture](){ \
+ type a = prevent_optimization(); \
+ _Pragma("omp allocate(a) allocator(alloc)") \
+ return a; \
+ }; \
+ auto result0 = default_capture0(); \
+ static_assert(is_same<type, decltype(result0)>::value); \
+ \
+ auto default_capture1 = [capture](){ \
+ type a = prevent_optimization(); \
+ _Pragma("omp allocate(a) allocator(alloc_ref)") \
+ return a; \
+ }; \
+ auto result1 = default_capture1(); \
+ static_assert(is_same<type, decltype(result1)>::value); \
+ \
+ auto default_capture2 = [capture](){ \
+ type a = prevent_optimization(); \
+ _Pragma("omp allocate(a) allocator(alloc_param)") \
+ return a; \
+ }; \
+ auto result2 = default_capture2(); \
+ static_assert(is_same<type, decltype(result2)>::value); \
+ \
+ auto default_capture3 = [capture](){ \
+ type a = prevent_optimization(); \
+ _Pragma("omp allocate(a) allocator(alloc_ref_param)") \
+ return a; \
+ }; \
+ auto result3 = default_capture3(); \
+ static_assert(is_same<type, decltype(result3)>::value); \
+} while (false)
+
+
+
+void test_default_capture_by_value_nondep_lambdas(omp_allocator_handle_t alloc_param,
+ omp_allocator_handle_t& alloc_ref_param)
+{
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ omp_allocator_handle_t& alloc_ref = alloc;
+ DEFAULT_CAPTURE_LAMBDAS(int, =);
+ DEFAULT_CAPTURE_LAMBDAS(float, =);
+ DEFAULT_CAPTURE_LAMBDAS(S0, =);
+ DEFAULT_CAPTURE_LAMBDAS(S1, =);
+}
+
+void test_default_capture_by_ref_nondep_lambdas(omp_allocator_handle_t alloc_param,
+ omp_allocator_handle_t& alloc_ref_param)
+{
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ omp_allocator_handle_t& alloc_ref = alloc;
+ DEFAULT_CAPTURE_LAMBDAS(int, &);
+ DEFAULT_CAPTURE_LAMBDAS(float, &);
+ DEFAULT_CAPTURE_LAMBDAS(S0, &);
+ DEFAULT_CAPTURE_LAMBDAS(S1, &);
+}
+
+template<typename T>
+void test_default_capture_by_value_dependent_lambdas(omp_allocator_handle_t alloc_param,
+ omp_allocator_handle_t& alloc_ref_param)
+{
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ omp_allocator_handle_t& alloc_ref = alloc;
+ DEFAULT_CAPTURE_LAMBDAS(T, =);
+}
+template void test_default_capture_by_value_dependent_lambdas<int>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_default_capture_by_value_dependent_lambdas<float>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_default_capture_by_value_dependent_lambdas<S0>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_default_capture_by_value_dependent_lambdas<S1>(omp_allocator_handle_t, omp_allocator_handle_t&);
+
+template<typename T>
+void test_default_capture_by_ref_dependent_lambdas(omp_allocator_handle_t alloc_param,
+ omp_allocator_handle_t& alloc_ref_param)
+{
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ omp_allocator_handle_t& alloc_ref = alloc;
+ DEFAULT_CAPTURE_LAMBDAS(T, &);
+}
+template void test_default_capture_by_ref_dependent_lambdas<int>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_default_capture_by_ref_dependent_lambdas<float>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_default_capture_by_ref_dependent_lambdas<S0>(omp_allocator_handle_t, omp_allocator_handle_t&);
+template void test_default_capture_by_ref_dependent_lambdas<S1>(omp_allocator_handle_t, omp_allocator_handle_t&);
+
+#undef DEFAULT_CAPTURE_LAMBDA
+#undef TEST_LAMBDAS_WITH_TYPE
+
+/* Potential missing cases: captures that are type dependent, mutable lambdas. */
+
new file mode 100644
@@ -0,0 +1,222 @@
+/* { dg-do compile { target c++20 } } */
+/* { dg-skip-if "" { ilp32 } } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+#include "allocate-allocator-handle.h"
+
+
+/* Valid C++20 cases.
+
+ Note: cases that take omp_default_mem_alloc and have no align clause elide
+ the GOMP_alloc/GOMP_free transformations. */
+
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc" 40 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_free" 40 "gimple" } } */
+
+/* FIXME: This almost certainly needs tweaks for systems where alignof(int)
+ is not 4. */
+
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(32," 24 "gimple" } } */
+
+/* The align argument is at least alignof(int) (4) in all cases, thus we can subtract
+ the cases where align >= 8 from the total cases to determine how many cases
+ there are where align == 4.
+ 40 - 24 = 16.
+ Please update this math if changes are made to this test so everything
+ is (somewhat) coherent. */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(4," 16 "gimple" } } */
+
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(\\d+, \\d+, 0B\\\)" 16 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(\\d+, \\d+, 1\\\)" 8 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\\(\\d+, \\d+, 2\\\)" 16 "gimple" } } */
+
+struct S0 {
+ int _v;
+ S0(int v) : _v(v) {}
+ operator int() const { return 42; }
+};
+
+struct S1 {
+ int _v[2];
+ S1(int v) : _v{v, v} {}
+ operator int() const { return 42; }
+};
+
+/*****************************
+ * template parameter (auto) *
+ *****************************/
+
+int auto_parm(auto)
+{
+ int a = 42;
+ #pragma omp allocate(a)
+ static_assert(alignof(a) == alignof(int));
+ return a;
+}
+
+int auto_parm_align(auto)
+{
+ int a = 42;
+ #pragma omp allocate(a) align(32)
+ static_assert(alignof(a) == alignof(int));
+ return a;
+}
+
+int auto_parm_alloc_0(auto)
+{
+ int a = 42;
+ #pragma omp allocate(a) allocator(omp_default_mem_alloc)
+ static_assert(alignof(a) == alignof(int));
+ return a;
+}
+
+int auto_parm_alloc_1(auto)
+{
+ int a = 42;
+ #pragma omp allocate(a) allocator(omp_large_cap_mem_alloc)
+ static_assert(alignof(a) == alignof(int));
+ return a;
+}
+
+int auto_parm_align_alloc_0(auto)
+{
+ int a = 42;
+ #pragma omp allocate(a) align(32) allocator(omp_default_mem_alloc)
+ static_assert(alignof(a) == alignof(int));
+ return a;
+}
+
+int auto_parm_align_alloc_1(auto)
+{
+ int a = 42;
+ #pragma omp allocate(a) align(32) allocator(omp_large_cap_mem_alloc)
+ static_assert(alignof(a) == alignof(int));
+ return a;
+}
+
+/* 5 cases per expansion, auto_parm_alloc_0 uses omp_default_mem_alloc
+ without an align clause.
+ 2 allocator==0B,
+ 1 allocator==1 (because 1 case elided),
+ 2 allocator==2 cases per expansion.
+
+ 3 align==32 cases per expansion. */
+#define INSTANTIATE_ALL_WITH_T(type) \
+ do { \
+ type a = 42; \
+ int v0 = auto_parm(a); \
+ int v1 = auto_parm_align(a); \
+ int v2 = auto_parm_alloc_0(a); \
+ int v3 = auto_parm_alloc_1(a); \
+ int v4 = auto_parm_align_alloc_0(a); \
+ int v5 = auto_parm_align_alloc_1(a); \
+ static_cast<void>(v0); \
+ static_cast<void>(v1); \
+ static_cast<void>(v2); \
+ static_cast<void>(v3); \
+ static_cast<void>(v4); \
+ static_cast<void>(v5); \
+ } while (false)
+
+/* 4 * 5 cases, 20.
+ 4 * 2 allocator==0B cases, 8.
+ 4 * 1 allocator==1 cases, 4.
+ 4 * 2 allocator==2 cases, 8.
+ 4 * 3 align==32 cases, 12. */
+void instantiate_auto_parm_tests()
+{
+ INSTANTIATE_ALL_WITH_T(int);
+ INSTANTIATE_ALL_WITH_T(float);
+ INSTANTIATE_ALL_WITH_T(S0);
+ INSTANTIATE_ALL_WITH_T(S1);
+}
+
+#undef INSTANTIATE_ALL_WITH_T
+
+
+auto dep_local_auto_parm(auto p)
+{
+ auto a = p;
+ #pragma omp allocate(a)
+ static_assert(alignof(a) == alignof(decltype(p)));
+ return a;
+}
+
+auto dep_local_auto_parm_align(auto p)
+{
+ auto a = p;
+ #pragma omp allocate(a) align(32)
+ static_assert(alignof(a) == alignof(decltype(p)));
+ return a;
+}
+
+auto dep_local_auto_parm_alloc_0(auto p)
+{
+ auto a = p;
+ #pragma omp allocate(a) allocator(omp_default_mem_alloc)
+ static_assert(alignof(a) == alignof(decltype(p)));
+ return a;
+}
+
+auto dep_local_auto_parm_alloc_1(auto p)
+{
+ auto a = p;
+ #pragma omp allocate(a) allocator(omp_large_cap_mem_alloc)
+ static_assert(alignof(a) == alignof(decltype(p)));
+ return a;
+}
+
+auto dep_local_auto_parm_align_alloc_0(auto p)
+{
+ auto a = p;
+ #pragma omp allocate(a) align(32) allocator(omp_default_mem_alloc)
+ static_assert(alignof(a) == alignof(decltype(p)));
+ return a;
+}
+
+auto dep_local_auto_parm_align_alloc_1(auto p)
+{
+ auto a = p;
+ #pragma omp allocate(a) align(32) allocator(omp_large_cap_mem_alloc)
+ static_assert(alignof(a) == alignof(decltype(p)));
+ return a;
+}
+
+/* 5 cases per expansion, dep_local_auto_parm_alloc_0 uses omp_default_mem_alloc
+ without an align clause.
+ 2 allocator==0B,
+ 1 allocator==1 (because 1 case elided),
+ 2 allocator==2 cases per expansion.
+
+ 3 align==32 cases per expansion. */
+#define INSTANTIATE_ALL_WITH_T(type) \
+ do { \
+ type a = 42; \
+ type v0 = dep_local_auto_parm(a); \
+ type v1 = dep_local_auto_parm_align(a); \
+ type v2 = dep_local_auto_parm_alloc_0(a); \
+ type v3 = dep_local_auto_parm_alloc_1(a); \
+ type v4 = dep_local_auto_parm_align_alloc_0(a); \
+ type v5 = dep_local_auto_parm_align_alloc_1(a); \
+ static_cast<void>(v0); \
+ static_cast<void>(v1); \
+ static_cast<void>(v2); \
+ static_cast<void>(v3); \
+ static_cast<void>(v4); \
+ static_cast<void>(v5); \
+ } while (false)
+
+/* 4 * 5 cases, 20.
+ 4 * 2 allocator==0B cases, 8.
+ 4 * 1 allocator==1 cases, 4.
+ 4 * 2 allocator==2 cases, 8.
+ 4 * 3 align==32 cases, 12. */
+void instantiate_dep_local_auto_parm_tests()
+{
+ INSTANTIATE_ALL_WITH_T(int);
+ INSTANTIATE_ALL_WITH_T(float);
+ INSTANTIATE_ALL_WITH_T(S0);
+ INSTANTIATE_ALL_WITH_T(S1);
+}
+
+#undef INSTANTIATE_ALL_WITH_T
new file mode 100644
@@ -0,0 +1,48 @@
+/* { dg-additional-options "-fdump-tree-gimple" } */
+#include "allocate-allocator-handle.h"
+
+/* Check simple cases that do not have an align clause and the allocator clause
+ folds to omp_default_mem_alloc. These cases are supposed to elide
+ the usual transformations (calling __builtin_GOMP_alloc/__builtin_GOMP_free)
+ that are usually applied to vars in an allocate directive.
+
+ It is not entirely clear if this is correct behavior, and due to differences
+ in how C++ works we can't support this for all cases, such as for exprs with
+ calls to constexpr functions.
+ At the very least we should make sure the obvious cases still work as
+ expected. */
+
+/* { dg-final { scan-tree-dump-not "__builtin_GOMP_alloc" "gimple" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_GOMP_free" "gimple" } } */
+
+int hardcoded_alloc(int p)
+{
+ int a = p;
+ #pragma omp allocate(a) allocator(omp_default_mem_alloc)
+ return a;
+}
+
+int converted_expr_alloc(int p)
+{
+ int a = p;
+ #pragma omp allocate(a) allocator(omp_allocator_handle_t(1))
+ return a;
+}
+
+int simple_expr_alloc(int p)
+{
+ int a = p;
+ #pragma omp allocate(a) allocator(omp_allocator_handle_t(0+1))
+ return a;
+}
+
+template<omp_allocator_handle_t Alloc>
+int nttp_alloc(int p)
+{
+ int a = p;
+ #pragma omp allocate(a) allocator(Alloc)
+ return a;
+}
+template int nttp_alloc<omp_default_mem_alloc>(int);
+
+
new file mode 100644
@@ -0,0 +1,78 @@
+/* { dg-do compile { target c++23 } } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+#include "allocate-allocator-handle.h"
+
+/* Test allocator clause is conditionally manifestly constant evaluated.
+
+ This test needs some work to actually detect if it is doing the right thing
+ in the static cases, but we barely implement allocation for those cases so
+ there isn't really much to detect.
+
+ For the non-static cases, it would be ideal if we checked whether it
+ optimizes properly, currently that is not being done. */
+
+
+/* Note, in practice this isn't actually useful as user handles are (AFAIK)
+ assigned by the runtime, this is just for testing. */
+constexpr omp_allocator_handle_t get_alloc_fn_foldable ()
+{
+ if consteval {
+ return omp_large_cap_mem_alloc;
+ }
+ else {
+ return omp_allocator_handle_t{128};
+ }
+}
+
+omp_allocator_handle_t my_global_user_handle = omp_allocator_handle_t{256};
+
+constexpr omp_allocator_handle_t get_alloc_fn_non_foldable ()
+{
+ if consteval {
+ return omp_large_cap_mem_alloc;
+ }
+ else {
+ return my_global_user_handle;
+ }
+}
+
+
+/* This could optimize to 128 but is not required to. */
+int non_manifest_cx_foldable (int p)
+{
+ int non_static_a = p;
+ #pragma omp allocate(non_static_a) allocator(get_alloc_fn_foldable ())
+ return non_static_a;
+}
+/* { dg-final { scan-tree-dump-times "= get_alloc_fn_foldable" 1 "gimple" } } */
+
+/* At best this can optimize to 'my_global_user_handle'. */
+int non_manifest_cx_non_foldable (int p)
+{
+ int non_static_b = p;
+ #pragma omp allocate(non_static_b) allocator(get_alloc_fn_non_foldable ())
+ return non_static_b;
+}
+/* { dg-final { scan-tree-dump-times "= get_alloc_fn_non_foldable" 1 "gimple" } } */
+
+/* In both of these cases, the allocator clause's expr is
+ manifestly constant evaluated. */
+
+/* This MUST fold to omp_large_cap_mem_alloc. */
+int manifest_cx_foldable (int p)
+{
+ static int static_a = p;
+ #pragma omp allocate(static_a) allocator(get_alloc_fn_foldable ())
+ return static_a;
+}
+
+/* This MUST fold to omp_large_cap_mem_alloc, calling get_alloc_fn_non_foldable
+ is intentional, the function is only non-foldable if the allocator clause
+ is not manifestly constant evaluated. */
+int manifest_cx_non_foldable (int p)
+{
+ static int static_b = p;
+ #pragma omp allocate(static_b) allocator(get_alloc_fn_non_foldable ())
+ return static_b;
+}
new file mode 100644
@@ -0,0 +1,18 @@
+typedef __UINTPTR_TYPE__ omp_uintptr_t;
+
+typedef enum omp_allocator_handle_t
+#if __cplusplus >= 201103L
+: omp_uintptr_t
+#endif
+{
+ omp_null_allocator = 0,
+ omp_default_mem_alloc = 1,
+ omp_large_cap_mem_alloc = 2,
+ omp_const_mem_alloc = 3,
+ omp_high_bw_mem_alloc = 4,
+ omp_low_lat_mem_alloc = 5,
+ omp_cgroup_mem_alloc = 6,
+ omp_pteam_mem_alloc = 7,
+ omp_thread_mem_alloc = 8,
+ __omp_allocator_handle_t_max__ = __UINTPTR_MAX__
+} omp_allocator_handle_t;
new file mode 100644
@@ -0,0 +1,63 @@
+#include "allocate-allocator-handle.h"
+
+/* If the following fails because of added predefined allocators, please update
+ - include/gomp-constants.h's GOMP_OMP_PREDEF_ALLOC_MAX or GOMP_OMPX_PREDEF_ALLOC_MAX
+ - libgomp/env.c's parse_allocator
+ - libgomp/libgomp.texi (document the new values - multiple locations)
+ - gcc/testsuite/c-c++-common/gomp/allocate-9.c (fix the hardcoded values)
+ - gcc/testsuite/g++.dg/gomp/allocate-handles-2.C (update GOMP_OMP_PREDEF_ALLOC_MAX and/or GOMP_OMPX_PREDEF_ALLOC_MAX)
+ + ensure that the memory-spaces are also up to date. */
+
+/* I had wanted to simply include /include/gomp-constants.h to ensure
+ synchronization, while also having hardcoded values as a canary, but
+ including files from that directory does not seem to be supported. */
+#define GOMP_OMP_PREDEF_ALLOC_MAX 8
+#define GOMP_OMPX_PREDEF_ALLOC_MIN 200
+#define GOMP_OMPX_PREDEF_ALLOC_MAX 201
+
+int g0 = 42; /* { dg-note "'g0' declared here" "" { xfail *-*-* } } */
+#pragma omp allocate(g0) allocator(omp_null_allocator)
+/* { dg-error "'allocator' clause requires a constant predefined allocator" "" { target *-*-* } .-1 } */
+/* { dg-note "because one or more variables with static storage duration appear in the 'allocate' directive" "" { xfail *-*-* } .-2 } */
+/* { dg-note "expression evaluates to '0'" "" { target *-*-* } .-3 } */
+int g1 = 42; /* { dg-note "'g1' declared here" "" { xfail *-*-* } }*/
+#pragma omp allocate(g1) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMP_PREDEF_ALLOC_MAX + 1))
+/* { dg-error "'allocator' clause requires a constant predefined allocator" "If this test fails because of added predefined allocators please ensure everything is updated accordingly, see this test case for more information" { target *-*-* } .-1 } */
+/* { dg-note "because one or more variables with static storage duration appear in the 'allocate' directive" "" { xfail *-*-* } .-2 } */
+/* { dg-note "expression evaluates to '9'" "" { target *-*-* } .-3 } */
+int g2 = 42; /* { dg-note "'g2' declared here" "" { xfail *-*-* } }*/
+#pragma omp allocate(g2) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMPX_PREDEF_ALLOC_MIN - 1))
+/* { dg-error "'allocator' clause requires a constant predefined allocator" "" { target *-*-* } .-1 } */
+/* { dg-note "because one or more variables with static storage duration appear in the 'allocate' directive" "" { xfail *-*-* } .-2 } */
+/* { dg-note "expression evaluates to '199'" "" { target *-*-* } .-3 } */
+int g3 = 42; /* { dg-note "'g3' declared here" "" { xfail *-*-* } }*/
+#pragma omp allocate(g3) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMPX_PREDEF_ALLOC_MAX + 1))
+/* { dg-error "'allocator' clause requires a constant predefined allocator" "If this test fails because of added predefined allocators please ensure everything is updated accordingly, see this test case for more information" { target *-*-* } .-1 } */
+/* { dg-note "because one or more variables with static storage duration appear in the 'allocate' directive" "" { xfail *-*-* } .-2 } */
+/* { dg-note "expression evaluates to '202'" "" { target *-*-* } .-3 } */
+
+void test_predefined_allocs()
+{
+ static int a0 = 42; /* { dg-note "'a0' declared here" "" { xfail *-*-* } }*/
+ #pragma omp allocate(a0) allocator(omp_null_allocator)
+ /* { dg-error "'allocator' clause requires a constant predefined allocator" "" { target *-*-* } .-1 } */
+ /* { dg-note "because one or more variables with static storage duration appear in the 'allocate' directive" "" { xfail *-*-* } .-2 } */
+ /* { dg-note "expression evaluates to '0'" "" { target *-*-* } .-3 } */
+ static int a1 = 42; /* { dg-note "'a1' declared here" "" { xfail *-*-* } }*/
+ #pragma omp allocate(a1) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMP_PREDEF_ALLOC_MAX + 1))
+ /* { dg-error "'allocator' clause requires a constant predefined allocator" "If this test fails because of added predefined allocators please ensure everything is updated accordingly, see this test case for more information" { target *-*-* } .-1 } */
+ /* { dg-note "because one or more variables with static storage duration appear in the 'allocate' directive" "" { xfail *-*-* } .-2 } */
+ /* { dg-note "expression evaluates to '9'" "" { target *-*-* } .-3 } */
+ static int a2 = 42; /* { dg-note "'a2' declared here" "" { xfail *-*-* } }*/
+ #pragma omp allocate(a2) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMPX_PREDEF_ALLOC_MIN - 1))
+ /* { dg-error "'allocator' clause requires a constant predefined allocator" "" { target *-*-* } .-1 } */
+ /* { dg-note "because one or more variables with static storage duration appear in the 'allocate' directive" "" { xfail *-*-* } .-2 } */
+ /* { dg-note "expression evaluates to '199'" "" { target *-*-* } .-3 } */
+ static int a3 = 42; /* { dg-note "'a3' declared here" "" { xfail *-*-* } }*/
+ #pragma omp allocate(a3) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMPX_PREDEF_ALLOC_MAX + 1))
+ /* { dg-error "'allocator' clause requires a constant predefined allocator" "If this test fails because of added predefined allocators please ensure everything is updated accordingly, see this test case for more information" { target *-*-* } .-1 } */
+ /* { dg-note "because one or more variables with static storage duration appear in the 'allocate' directive" "" { xfail *-*-* } .-2 } */
+ /* { dg-note "expression evaluates to '202'" "" { target *-*-* } .-3 } */
+}
+
+/* { dg-bogus "because one or more variables with static storage duration appear in the 'allocate' directive" "" { xfail c++ } 0 }*/
new file mode 100644
@@ -0,0 +1,45 @@
+/* { dg-do compile { target c++11 } } */
+#include "allocate-allocator-handle.h"
+
+/* I had wanted to simply include /include/gomp-constants.h to ensure
+ synchronization, but including files from that directory does not seem
+ to be supported. */
+#define GOMP_OMP_PREDEF_ALLOC_MAX 8
+#define GOMP_OMPX_PREDEF_ALLOC_MIN 200
+#define GOMP_OMPX_PREDEF_ALLOC_MAX 201
+
+/* Test that all predefined allocators are correctly treated as predefined. */
+
+template<omp_allocator_handle_t Alloc>
+void test_predefined_alloc()
+{
+ static int a = 42;
+ #pragma omp allocate(a) allocator(Alloc)
+}
+
+/* Because this is written to work as far back as c++11 it is a little bit
+ crusty. It is metaprogrammed to automatically test the full ranges
+ specified above. */
+
+template<omp_allocator_handle_t...>
+struct sequence {};
+
+template<__UINTPTR_TYPE__ Offset, __UINTPTR_TYPE__... Is>
+using modified = sequence<static_cast<omp_allocator_handle_t>(Is + Offset)...>;
+
+template<__UINTPTR_TYPE__ Start, __UINTPTR_TYPE__ End>
+using make_offset_sequence = modified<Start, __integer_pack(End - Start)...>;
+
+template<omp_allocator_handle_t... Allocs>
+void unpack(sequence<Allocs...>)
+{
+ int helper[] = {(test_predefined_alloc<Allocs>(), 0)...};
+}
+
+void do_tests()
+{
+ /* make_sequence creates a sequence [Start, End) while the *_MAX values are
+ inclusive, add 1 to the End arg to create an exclusive range. */
+ unpack(make_offset_sequence<1, GOMP_OMP_PREDEF_ALLOC_MAX + 1>{});
+ unpack(make_offset_sequence<GOMP_OMPX_PREDEF_ALLOC_MIN, GOMP_OMPX_PREDEF_ALLOC_MAX + 1>{});
+}
new file mode 100644
@@ -0,0 +1,329 @@
+/* { dg-do run } */
+/* { dg-additional-options "-fdump-tree-omplower" } */
+
+/* For the 4 vars in omp_parallel, 4 in omp_target and 1 of 2 in each of no_alloc{,2}_func. */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc \\(" 10 "omplower" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_free \\(" 10 "omplower" } } */
+
+#include <omp.h>
+
+
+void
+check_int (int *x, int y)
+{
+ if (*x != y)
+ __builtin_abort ();
+}
+
+void
+check_ptr (int **x, int *y)
+{
+ if (*x != y)
+ __builtin_abort ();
+}
+
+
+template<typename t>
+t
+no_alloc_func ()
+{
+ /* There is no __builtin_GOMP_alloc / __builtin_GOMP_free as
+ allocator == omp_default_mem_alloc (known at compile time. */
+ t no_alloc, alloc_has_align = 3;
+ #pragma omp allocate(no_alloc) allocator(omp_default_mem_alloc)
+ /* But this one is allocated because of align. */
+ #pragma omp allocate(alloc_has_align) allocator(omp_default_mem_alloc) align(sizeof(t))
+ no_alloc = 7;
+ return no_alloc + alloc_has_align;
+}
+
+template<typename t>
+t
+no_alloc2_func()
+{
+ /* There is no __builtin_GOMP_alloc / __builtin_GOMP_free as
+ no_alloc2 is TREE_UNUSED. But there is for is_alloc2. */
+ t no_alloc2, is_alloc2;
+ #pragma omp allocate(no_alloc2, is_alloc2)
+ is_alloc2 = 7;
+ return is_alloc2;
+}
+
+
+template<typename t>
+void
+omp_parallel ()
+{
+ int n = 6;
+ t iii = 5, jjj[5], kkk[n];
+ t *ptr = (t *) 0x1234;
+ #pragma omp allocate(iii, jjj, kkk, ptr)
+
+ for (int i = 0; i < 5; i++)
+ jjj[i] = 3*i;
+ for (int i = 0; i < 6; i++)
+ kkk[i] = 7*i;
+
+ #pragma omp parallel default(none) firstprivate(iii, jjj, kkk, ptr) if(0)
+ {
+ if (iii != 5)
+ __builtin_abort();
+ iii = 7;
+ check_int (&iii, 7);
+ for (int i = 0; i < 5; i++)
+ if (jjj[i] != 3*i)
+ __builtin_abort ();
+ for (int i = 0; i < 6; i++)
+ if (kkk[i] != 7*i)
+ __builtin_abort ();
+ for (int i = 0; i < 5; i++)
+ jjj[i] = 4*i;
+ for (int i = 0; i < 6; i++)
+ kkk[i] = 8*i;
+ for (int i = 0; i < 5; i++)
+ check_int (&jjj[i], 4*i);
+ for (int i = 0; i < 6; i++)
+ check_int (&kkk[i], 8*i);
+ if (ptr != (int *) 0x1234)
+ __builtin_abort ();
+ ptr = (int *) 0xabcd;
+ if (ptr != (int *) 0xabcd)
+ __builtin_abort ();
+ check_ptr (&ptr, (int *) 0xabcd);
+ }
+ if (iii != 5)
+ __builtin_abort ();
+ check_int (&iii, 5);
+ for (int i = 0; i < 5; i++)
+ {
+ if (jjj[i] != 3*i)
+ __builtin_abort ();
+ check_int (&jjj[i], 3*i);
+ }
+ for (int i = 0; i < 6; i++)
+ {
+ if (kkk[i] != 7*i)
+ __builtin_abort ();
+ check_int (&kkk[i], 7*i);
+ }
+ if (ptr != (int *) 0x1234)
+ __builtin_abort ();
+ check_ptr (&ptr, (int *) 0x1234);
+
+ #pragma omp parallel default(firstprivate) if(0)
+ {
+ if (iii != 5)
+ __builtin_abort();
+ iii = 7;
+ check_int (&iii, 7);
+ for (int i = 0; i < 5; i++)
+ if (jjj[i] != 3*i)
+ __builtin_abort ();
+ for (int i = 0; i < 6; i++)
+ if (kkk[i] != 7*i)
+ __builtin_abort ();
+ for (int i = 0; i < 5; i++)
+ jjj[i] = 4*i;
+ for (int i = 0; i < 6; i++)
+ kkk[i] = 8*i;
+ for (int i = 0; i < 5; i++)
+ check_int (&jjj[i], 4*i);
+ for (int i = 0; i < 6; i++)
+ check_int (&kkk[i], 8*i);
+ if (ptr != (int *) 0x1234)
+ __builtin_abort ();
+ ptr = (int *) 0xabcd;
+ if (ptr != (int *) 0xabcd)
+ __builtin_abort ();
+ check_ptr (&ptr, (int *) 0xabcd);
+ }
+ if (iii != 5)
+ __builtin_abort ();
+ check_int (&iii, 5);
+ for (int i = 0; i < 5; i++)
+ {
+ if (jjj[i] != 3*i)
+ __builtin_abort ();
+ check_int (&jjj[i], 3*i);
+ }
+ for (int i = 0; i < 6; i++)
+ {
+ if (kkk[i] != 7*i)
+ __builtin_abort ();
+ check_int (&kkk[i], 7*i);
+ }
+ if (ptr != (int *) 0x1234)
+ __builtin_abort ();
+ check_ptr (&ptr, (int *) 0x1234);
+}
+
+
+template<typename t>
+void
+omp_target ()
+{
+ int n = 6;
+ t iii = 5, jjj[5], kkk[n];
+ t *ptr = (int *) 0x1234;
+ #pragma omp allocate(iii, jjj, kkk, ptr)
+
+ for (int i = 0; i < 5; i++)
+ jjj[i] = 3*i;
+ for (int i = 0; i < 6; i++)
+ kkk[i] = 7*i;
+
+ #pragma omp target defaultmap(none) firstprivate(iii, jjj, kkk, ptr)
+ {
+ if (iii != 5)
+ __builtin_abort();
+ iii = 7;
+ check_int (&iii, 7);
+ for (int i = 0; i < 5; i++)
+ if (jjj[i] != 3*i)
+ __builtin_abort ();
+ for (int i = 0; i < 6; i++)
+ if (kkk[i] != 7*i)
+ __builtin_abort ();
+ for (int i = 0; i < 5; i++)
+ jjj[i] = 4*i;
+ for (int i = 0; i < 6; i++)
+ kkk[i] = 8*i;
+ for (int i = 0; i < 5; i++)
+ check_int (&jjj[i], 4*i);
+ for (int i = 0; i < 6; i++)
+ check_int (&kkk[i], 8*i);
+ if (ptr != (int *) 0x1234)
+ __builtin_abort ();
+ ptr = (int *) 0xabcd;
+ if (ptr != (int *) 0xabcd)
+ __builtin_abort ();
+ check_ptr (&ptr, (int *) 0xabcd);
+ }
+ if (iii != 5)
+ __builtin_abort ();
+ check_int (&iii, 5);
+ for (int i = 0; i < 5; i++)
+ {
+ if (jjj[i] != 3*i)
+ __builtin_abort ();
+ check_int (&jjj[i], 3*i);
+ }
+ for (int i = 0; i < 6; i++)
+ {
+ if (kkk[i] != 7*i)
+ __builtin_abort ();
+ check_int (&kkk[i], 7*i);
+ }
+ if (ptr != (int *) 0x1234)
+ __builtin_abort ();
+ check_ptr (&ptr, (int *) 0x1234);
+
+ #pragma omp target defaultmap(firstprivate)
+ {
+ if (iii != 5)
+ __builtin_abort();
+ iii = 7;
+ check_int (&iii, 7);
+ for (int i = 0; i < 5; i++)
+ if (jjj[i] != 3*i)
+ __builtin_abort ();
+ for (int i = 0; i < 6; i++)
+ if (kkk[i] != 7*i)
+ __builtin_abort ();
+ for (int i = 0; i < 5; i++)
+ jjj[i] = 4*i;
+ for (int i = 0; i < 6; i++)
+ kkk[i] = 8*i;
+ for (int i = 0; i < 5; i++)
+ check_int (&jjj[i], 4*i);
+ for (int i = 0; i < 6; i++)
+ check_int (&kkk[i], 8*i);
+ if (ptr != (int *) 0x1234)
+ __builtin_abort ();
+ ptr = (int *) 0xabcd;
+ if (ptr != (int *) 0xabcd)
+ __builtin_abort ();
+ check_ptr (&ptr, (int *) 0xabcd);
+ }
+ if (iii != 5)
+ __builtin_abort ();
+ check_int (&iii, 5);
+ for (int i = 0; i < 5; i++)
+ {
+ if (jjj[i] != 3*i)
+ __builtin_abort ();
+ check_int (&jjj[i], 3*i);
+ }
+ for (int i = 0; i < 6; i++)
+ {
+ if (kkk[i] != 7*i)
+ __builtin_abort ();
+ check_int (&kkk[i], 7*i);
+ }
+ if (ptr != (int *) 0x1234)
+ __builtin_abort ();
+ check_ptr (&ptr, (int *) 0x1234);
+
+ #pragma omp target defaultmap(tofrom)
+ {
+ if (iii != 5)
+ __builtin_abort();
+ iii = 7;
+ check_int (&iii, 7);
+ for (int i = 0; i < 5; i++)
+ if (jjj[i] != 3*i)
+ __builtin_abort ();
+ for (int i = 0; i < 6; i++)
+ if (kkk[i] != 7*i)
+ __builtin_abort ();
+ for (int i = 0; i < 5; i++)
+ jjj[i] = 4*i;
+ for (int i = 0; i < 6; i++)
+ kkk[i] = 8*i;
+ for (int i = 0; i < 5; i++)
+ check_int (&jjj[i], 4*i);
+ for (int i = 0; i < 6; i++)
+ check_int (&kkk[i], 8*i);
+ if (ptr != (int *) 0x1234)
+ __builtin_abort ();
+ ptr = (int *) 0xabcd;
+ if (ptr != (int *) 0xabcd)
+ __builtin_abort ();
+ check_ptr (&ptr, (int *) 0xabcd);
+ }
+
+ if (iii != 7)
+ __builtin_abort ();
+ check_int (&iii, 7);
+ for (int i = 0; i < 5; i++)
+ {
+ if (jjj[i] != 4*i)
+ __builtin_abort ();
+ check_int (&jjj[i], 4*i);
+ }
+ for (int i = 0; i < 6; i++)
+ {
+ if (kkk[i] != 8*i)
+ __builtin_abort ();
+ check_int (&kkk[i], 8*i);
+ }
+ if (ptr != (int *) 0xabcd)
+ __builtin_abort ();
+ check_ptr (&ptr, (int *) 0xabcd);
+}
+
+int
+foo()
+{
+ return no_alloc_func<int>() + no_alloc2_func<int>();
+}
+
+int
+main ()
+{
+ omp_parallel<int> ();
+ omp_target<int> ();
+ if (foo() != 10 + 7)
+ __builtin_abort ();
+ return 0;
+}
similarity index 95%
rename from libgomp/testsuite/libgomp.c/allocate-4.c
rename to libgomp/testsuite/libgomp.c-c++-common/allocate-4.c
@@ -1,6 +1,3 @@
-/* TODO: move to ../libgomp.c-c++-common once C++ is implemented. */
-/* NOTE: { target c } is unsupported with with the C compiler. */
-
/* { dg-do run } */
/* { dg-additional-options "-fdump-tree-gimple" } */
similarity index 96%
rename from libgomp/testsuite/libgomp.c/allocate-5.c
rename to libgomp/testsuite/libgomp.c-c++-common/allocate-5.c
@@ -1,6 +1,3 @@
-/* TODO: move to ../libgomp.c-c++-common once C++ is implemented. */
-/* NOTE: { target c } is unsupported with with the C compiler. */
-
/* { dg-do run } */
/* { dg-additional-options "-fdump-tree-gimple" } */
similarity index 98%
rename from libgomp/testsuite/libgomp.c/allocate-6.c
rename to libgomp/testsuite/libgomp.c-c++-common/allocate-6.c
@@ -1,6 +1,3 @@
-/* TODO: move to ../libgomp.c-c++-common once C++ is implemented. */
-/* NOTE: { target c } is unsupported with with the C compiler. */
-
/* { dg-do run } */
/* { dg-additional-options "-fdump-tree-omplower" } */
similarity index 90%
rename from libgomp/testsuite/libgomp.c/allocate-7.c
rename to libgomp/testsuite/libgomp.c-c++-common/allocate-7.c
@@ -1,6 +1,3 @@
-/* TODO: move to ../libgomp.c-c++-common once C++ is implemented. */
-/* NOTE: { target c } is unsupported with with the C compiler. */
-
/* { dg-do run } */
#include <omp.h>