diff options
author | waffl3x <waffl3x@baylibre.com> | 2025-01-10 01:13:37 -0700 |
---|---|---|
committer | Sandra Loosemore <sloosemore@baylibre.com> | 2025-05-15 20:25:50 +0000 |
commit | 6e7487f5f6fa84f8ca6870f012a81d0d7a222c54 (patch) | |
tree | 7369891638da4a0626f7576b34d9f6c63e4c42ef /gcc | |
parent | 5f7678f015bb31a5aef0ea4f801a3fcff2e85050 (diff) | |
download | gcc-6e7487f5f6fa84f8ca6870f012a81d0d7a222c54.zip gcc-6e7487f5f6fa84f8ca6870f012a81d0d7a222c54.tar.gz gcc-6e7487f5f6fa84f8ca6870f012a81d0d7a222c54.tar.bz2 |
OpenMP: Add C++ support for 'omp allocate'
This patch handles local variables, global variables, as well as static
local variables where it is currently practical to do so. For now we sorry
on static local variables inside implicit constexpr functions, this includes
lambdas in c++17 and up, and inline functions with -fimplicit-constexpr in
c++14 and up. I have another patch that fixes most cases, unfortunately
there are a few cases that are not fixable without additional redesigns.
For function templates Instead of storing the directive directly in a
variables 'omp allocate' attribute and substituting into it, this patch
adds the OMP_ALLOCATE tree code that gets substituted. This makes it much
easier to prevent duplicate diagnostics with an invalid allocator/align
clause. This is added to a function template's stmt list and is only used
for substitution. It is not added to instantiations of a function template,
nor to regular functions.
Location information is included in the 'omp allocate' attribute to enhance
diagnostics of variables used in multiple allocate directives. While it is
possible that this could be added to the c front end, it would require some
reworking so it is not included in this patch. It was easy to support this
in the c++ front end because it is not practical to wait for a finalized
directive to add the 'omp allocate' attribute to vars, nor is it practical
to remove it in error cases. Consequentially some extra handling for this
needed to be added to gimplify.cc to avoid problems in error cases and
prevent conflicts with Fortran specific implementation details.
There is a left over band-aid fix in make_rtl_for_nonlocal_decl that only
worked for template cases, it probably has no effect at the moment.
The problem is make_rtl_for_nonlocal_decl never defers static locals in any
kind of constexpr function, including lambdas in c++17, regardless of
whether they can be used in a constant expression. I have a lengthy write
up of the history of why this is the case and the implications of it all,
but it is not directly relevant to this patch. In short, the original
reason static locals are not deferred was to fix PR70353 and was added in
r6-7642-ge0bffbbb5936be, however in r9-3788-gddd0d18c9c0702 the handling of
that case was changed and no longer goes through make_rtl_for_nonlocal_decl.
Unfortunately, we can't merely undo what was added, as c++23 static
constexpr local variables rely on it, as well as cases with c++17 lambdas
that should be disallowed, but aren't.
This should never be relevant, As OpenMP directives are not currently
allowed in constexpr functions, but as stated above the early processing of
static locals happens regardless of whether the function is actually usable
in a constant expression. In non templates, this early processing occurs
before we have even parsed the allocate directive, causing alignment
specified in an align clause to be skipped over entirely. In templates we
at least get to add the attribute to mark a var before this happens, so we
can use the presence of it to make sure they get deferred. This is the
band-aid that is currently present in make_rtl_for_nonlocal_decl, however
we currently reject these cases as it is fairly difficult to differentiate
whether we are in a regular function or not. We can't just rely on
processing_template_decl as it would just error upon instantiation. In
hindsight it would probably have worked fine in cp_parser_omp_allocate, but
this is supposed to be a temporary measure anyway as I have a follow up
patch.
gcc/c/ChangeLog:
* c-parser.cc (c_parser_omp_allocate): Fix typo in diagnostic.
gcc/ChangeLog:
* cgraphunit.cc (varpool_node::finalize_decl): Add assert.
* gimplify.cc (gimplify_bind_expr): Handle C++ specific
implementation details.
gcc/cp/ChangeLog:
* constexpr.cc (potential_constant_expression_1): Handle
OMP_ALLOCATE.
* cp-tree.def (OMP_ALLOCATE): New tree code.
* cp-tree.h (OMP_ALLOCATE_LOCATION): Define.
(OMP_ALLOCATE_VARS): Define.
(OMP_ALLOCATE_ALLOCATOR): Define.
(OMP_ALLOCATE_ALIGN): Define.
(finish_omp_allocate): New function declaration.
* decl.cc (make_rtl_for_nonlocal_decl): Work around ICE with
implicit constexpr functions.
* parser.cc (cp_parser_omp_allocate): Use OMP_CLAUSE_ERROR,
add diagnostics for args, call finish_omp_allocate.
(cp_parser_omp_construct): Don't handle PRAGMA_OMP_ALLOCATE.
(cp_parser_pragma): Comment.
* pt.cc (tsubst_stmt): Handle OMP_ALLOCATE, call
finish_omp_allocate.
* semantics.cc (finish_omp_allocate): New function.
* typeck.cc (can_do_nrvo_p): Don't do NRVO for omp allocate vars.
libgomp/ChangeLog:
* libgomp.texi: Document C++ support.
* testsuite/libgomp.c/allocate-4.c: Move to...
* testsuite/libgomp.c-c++-common/allocate-4.c: ...here.
* testsuite/libgomp.c/allocate-5.c: Move to...
* testsuite/libgomp.c-c++-common/allocate-5.c: ...here.
* testsuite/libgomp.c/allocate-6.c: Move to...
* testsuite/libgomp.c-c++-common/allocate-6.c: ...here.
* testsuite/libgomp.c++/allocate-2.C: New test.
gcc/testsuite/ChangeLog:
* c-c++-common/gomp/allocate-allocator-handle.h: New header.
* c-c++-common/gomp/allocate-5.c: Remove dg-messages for 'sorry',
add dg-error for c++.
* c-c++-common/gomp/allocate-9.c: Include header, remove dg-messages
for 'sorry', add dg-notes for c++, minor refactoring.
* c-c++-common/gomp/allocate-10.c: Enable for c++.
* c-c++-common/gomp/allocate-11.c: Enable for c++, disable warning.
* c-c++-common/gomp/allocate-12.c: Enable for c++, add cases.
* c-c++-common/gomp/allocate-14.c: Enable for c++.
* c-c++-common/gomp/allocate-15.c: Enable for c++.
* c-c++-common/gomp/allocate-16.c: Enable for c++.
* c-c++-common/gomp/allocate-17.c: Remove dg-message for 'sorry'.
* c-c++-common/gomp/allocate-18.c: Include header, remove dg-message
for 'sorry'.
* c-c++-common/gomp/allocate-19.c: Remove xfails for c++, remove
dg-messages for 'sorry'.
* c-c++-common/gomp/allocate-20.c: New test.
* c-c++-common/gomp/directive-1.c: Remove dg-message for 'sorry'.
* g++.dg/gomp/allocate-allocator-handle.h: New header.
* g++.dg/gomp/allocate-5.C: New test.
* g++.dg/gomp/allocate-6.C: New test.
* g++.dg/gomp/allocate-7.C: New test.
* g++.dg/gomp/allocate-8.C: New test.
* g++.dg/gomp/allocate-9.C: New test.
* g++.dg/gomp/allocate-10.C: New test.
* g++.dg/gomp/allocate-11.C: New test.
* g++.dg/gomp/allocate-12.C: New test.
* g++.dg/gomp/allocate-13.C: New test.
* g++.dg/gomp/allocate-14.C: New test.
* g++.dg/gomp/allocate-15.C: New test.
* g++.dg/gomp/allocate-16.C: New test.
* g++.dg/gomp/allocate-17.C: New test.
* g++.dg/gomp/allocate-18.C: New test.
* g++.dg/gomp/allocate-19.C: New test.
* g++.dg/gomp/allocate-20.C: New test.
* g++.dg/gomp/allocate-21.C: New test.
Signed-off-by: waffl3x <waffl3x@baylibre.com>
Co-authored-by: Tobias Burnus <tobias@codesourcery.com>
Diffstat (limited to 'gcc')
43 files changed, 4019 insertions, 188 deletions
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 44b00ae..00ad861 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -23136,7 +23136,7 @@ c_parser_omp_allocate (c_parser *parser) != get_identifier ("omp_allocator_handle_t")) { error_at (expr_loc, - "%<allocator%> clause allocator expression has type " + "%<allocator%> clause expression has type " "%qT rather than %<omp_allocator_handle_t%>", TREE_TYPE (allocator)); allocator = NULL_TREE; diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc index 20b03b6..33ee89b 100644 --- a/gcc/cgraphunit.cc +++ b/gcc/cgraphunit.cc @@ -990,6 +990,10 @@ varpool_node::finalize_decl (tree decl) tree attr = lookup_attribute ("omp allocate", DECL_ATTRIBUTES (decl)); if (attr) { + /* The var loc, wrapped in a nop_expr, is stored here by + cp_parser_omp_allocate, finish_omp_allocate finalizes it, + we should never reach here before that happens. */ + gcc_assert (TREE_CODE (TREE_VALUE (attr)) != NOP_EXPR); tree align = TREE_VALUE (TREE_VALUE (attr)); if (align) SET_DECL_ALIGN (decl, MAX (tree_to_uhwi (align) * BITS_PER_UNIT, diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index ae76dc2..48327fb 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -11042,6 +11042,22 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, return true; } + /* We technically should never encounter this, but handling a generic + lambda checks the function body before instantiation to see if it can be + declared constexpr. This is currently fairly buggy and not respected + by other parts of the code though. */ + case OMP_ALLOCATE: + /* This is the only case I observed this, we want to know if other cases + suddenly manifest. */ + gcc_assert (cxx_dialect >= cxx17 + && processing_template_decl + && LAMBDA_FUNCTION_P (current_function_decl)); + /* OpenMP does not currently allow directives in constexpr functions. + However as hinted at above, returning false here doesn't actually stop + lambdas from being called in constant expressions. + We still return false in case that changes in the future. */ + return false; + default: if (objc_non_constant_expr_p (t)) return false; diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def index bf79a52..2182141 100644 --- a/gcc/cp/cp-tree.def +++ b/gcc/cp/cp-tree.def @@ -491,6 +491,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. */ diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 5d7b381..d1e2a28 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5681,6 +5681,23 @@ 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) */ +/* Location of the OMP_ALLOCATE directive. */ +#define OMP_ALLOCATE_LOCATION(NODE) (OMP_ALLOCATE_CHECK (NODE)->exp.locus) +/* 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 + 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 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. */ @@ -8102,6 +8119,7 @@ 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); extern void finish_omp_atomic (location_t, enum tree_code, enum tree_code, tree, tree, tree, tree, tree, tree, tree, diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 132558f..44e6130 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -8171,6 +8171,17 @@ make_rtl_for_nonlocal_decl (tree decl, tree init, const char* asmspec) && !var_in_maybe_constexpr_fn (decl)) || DECL_VIRTUAL_P (decl)); + /* See testsuite/g++.dg/gomp/allocate-15.C + This is a band-aid to fix an ICE with implicit constexpr functions, it + does not fix the non-template case in allocate-15.C though. We currently + sorry on all cases where this is relevant so it shouldn't be necessary. + The above needs to be changed but it makes more sense to defer it to + another patch as it relates to some other bugs too and has slightly wider + implications than just within the scope of OpenMP. */ + if (!defer_p && flag_openmp + && lookup_attribute ("omp allocate", DECL_ATTRIBUTES (decl))) + defer_p = 1; + /* Defer template instantiations. */ if (DECL_LANG_SPECIFIC (decl) && DECL_IMPLICIT_INSTANTIATION (decl)) diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index eaa58b7..db3ef70 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -44695,11 +44695,149 @@ 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); + /* If there were errors nl might be NULL_TREE, we need to handle this case + where necessary to diagnose as much as possible. */ + tree nl = cp_parser_omp_var_list (parser, OMP_CLAUSE_ERROR, NULL_TREE); + /* Reverse the chain so diagnostics are in forward order. */ + nl = nreverse (nl); + + /* The var declared first in this function of the variables passed into the + allocate directive, this gets assigned in the following loop. We can't + assign nl's decl to it, because it might have an error, and nl itself + might be NULL_TREE. + + We use this to simplify checking the allocator clause's expr later. */ + tree f_var = NULL_TREE; + { + /* The head might have an error and need to be replaced. */ + tree *chain = &nl; + for (tree node = nl; node != NULL_TREE; node = TREE_CHAIN (node)) + { + tree var = TREE_PURPOSE (node); + /* Do this before duplicates are diagnosed, parms are never valid so we + don't want to diagnose duplicate uses of them. */ + if (TREE_CODE (var) == PARM_DECL) + { + auto_diagnostic_group d; + error_at (EXPR_LOCATION (TREE_VALUE (node)), + "function parameter %qD may not appear as list item in " + "an %<allocate%> directive", var); + inform (DECL_SOURCE_LOCATION (var), + "parameter %qD declared here", var); + /* Remove the node. */ + *chain = TREE_CHAIN (node); + /* There is nothing else to diagnose for a parm. */ + continue; + } + /* Diagnose duplicate vars passed to the allocate directive. + We could generate fixits for a number of these, but adding the + comma token to be removed to the fixit seems difficult. + This is O(n^2), we'll have to use a hash table if it ever becomes + a problem. */ + { + auto_diagnostic_group d; + bool duplicate_entry = false; + /* Frontmost non duplicate node. */ + tree node_prev = node; + /* The node we are checking as a potential duplicate. */ + tree node_current = TREE_CHAIN (node); + while (node_current != NULL_TREE) + { + if (TREE_PURPOSE (node) == TREE_PURPOSE (node_current)) + { + /* If we could just get the location of the comma token a + fixit hint would be viable here. */ + error_at (EXPR_LOCATION (TREE_VALUE (node_current)), + "%qD already appeared as list item in this " + "directive", + TREE_PURPOSE (node)); + duplicate_entry = true; + /* The current node is a duplicate of node, remove it. */ + TREE_CHAIN (node_prev) = TREE_CHAIN (node_current); + } + else + node_prev = node_current; + node_current = TREE_CHAIN (node_current); + } + if (duplicate_entry) + inform (EXPR_LOCATION (TREE_VALUE (node)), "appeared first here"); + /* Dupes of node doesn't mean we remove it, keep going. */ + } + auto var_is_in_current_scope = [] (tree var) + { + tree v = current_binding_level->names; + for (; v != NULL_TREE; v = DECL_CHAIN (v)) + if (v == var) + return true; + return false; + }; + /* If we do this before checking if the var was used in another + allocate directive the diagnostic should be more clear if the user + intended to shadow another variable. */ + if (!var_is_in_current_scope (var)) + { + auto_diagnostic_group d; + error_at (EXPR_LOCATION (TREE_VALUE (node)), + "%<allocate%> directive must be in the same scope as " + "%qD", var); + inform (DECL_SOURCE_LOCATION (var), "declared here"); + /* Remove the node. */ + *chain = TREE_CHAIN (node); + continue; + } + tree attr = lookup_attribute ("omp allocate", + DECL_ATTRIBUTES (var)); + if (attr) + { + auto_diagnostic_group d; + error_at (EXPR_LOCATION (TREE_VALUE (node)), + "%qD already appeared as list item in an " + "%<allocate%> directive", var); + /* The loc is stored in the chain member of the tree_list when the + directive is complete. Currently, it is only possible for a + directive to already be finished if we are parsing a + non-template. */ + tree var_loc = TREE_CODE (TREE_VALUE (attr)) == NOP_EXPR + ? TREE_VALUE (attr) + : TREE_CHAIN (TREE_VALUE (attr)); + inform (EXPR_LOCATION (var_loc), + "%qD previously appeared here", var); + /* Remove the node. */ + *chain = TREE_CHAIN (node); + } + else + { + /* Mark the variable so we can diagnose this during parsing, + including before a template is instantiated. + This is necessary even if we diagnose errors for this directive + so we don't miss diagnosing secondary uses of a variable in + later allocate directives. Additionally, we take advantage of + this to store the loc the variable was used for diagnostics. */ + DECL_ATTRIBUTES (var) = tree_cons (get_identifier ("omp allocate"), + TREE_VALUE (node), + DECL_ATTRIBUTES (var)); + /* Everything is good, we are keeping this node now, + update the current chain pointer. */ + chain = &TREE_CHAIN (node); + + /* Nodes that contain a parm or a var that is not in this scope + were don't make it this far, we can to assign f_var now. */ + if (f_var == NULL_TREE) + f_var = var; + /* I am not sure of a better way to do this. Maybe it would be + better to walk the declarations in the current scope? The + chain is backwards so it would probably be worse. */ + else if (linemap_location_before_p (line_table, + DECL_SOURCE_LOCATION (var), + DECL_SOURCE_LOCATION (f_var))) + f_var = var; + } + } + } + /* cp_parser_assignment_expression wraps in a location by default. */ + cp_expr allocator = NULL_TREE; + cp_expr alignment = NULL_TREE; do { if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA) @@ -44720,70 +44858,202 @@ 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; - } + /* Used to carry information into the below lambda through cp_walk_tree. */ + struct cp_omp_loc_tree + { + location_t loc; + tree first_arg; + tree arg_list; + }; + /* Check whether the expression used in the allocator clause is declared or + modified between the variable declaration and its allocate directive. + + We could consider moving everything here to finish_omp_allocate but it's + convenient to keep it here until we opt to tackle the issues noted in the + comments below. */ + auto check_omp_allocate_allocator_r = [] (tree *tp, int *, void *data) + { + /* We bail on the first error we find. Alternatively we could diagnose + as much as we can, keeping track of vars we have already diagnosed to + prevent duplicates errors, but it doesn't seem worth doing. */ + tree var_in_expr = *tp; + if (!VAR_P (var_in_expr)) + return NULL_TREE; - /* 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"); + location_t alloc_expr_loc = ((cp_omp_loc_tree *) data)->loc; + tree first_declared_arg = ((cp_omp_loc_tree *) data)->first_arg; + tree all_args = ((cp_omp_loc_tree *) data)->arg_list; + + /* For obvious reasons, you can't use a var used in an allocate directive + as part of the allocator clause's expression. We don't have to check + this specifically for each var because usage of a var declared after + the first declared var will also be rejected, but this would result in + a misleading diagnostic. It doesn't cost use much to do this check + explicitly for each arg so do it this way. */ + for (tree arg = all_args; arg; arg = TREE_CHAIN (arg)) + if (var_in_expr == TREE_PURPOSE (arg)) + { + tree arg_decl = TREE_PURPOSE (arg); + /* It would be nice if there were an easy way to put the caret on + the use of the variable, but there doesn't appear to be. */ + auto_diagnostic_group d; + error_at (alloc_expr_loc, + "variable %qD used in this %<allocate%> directive must " + "not be used in its %<allocator%> clause", arg_decl); + inform (DECL_SOURCE_LOCATION (arg_decl), "declared here"); + inform (EXPR_LOCATION (TREE_VALUE (arg)), + "used in allocate directive here"); + return error_mark_node; + } + /* We can't rely on locations to determine declaration order because var + decls that are implicit lambda captures have their location set to + their first use. This is probably a bug but relying on source + location for this seems incorrect anyway. */ + for (tree v = current_binding_level->names; v; v = DECL_CHAIN (v)) + { + /* If we find first_declared_arg before var_in_expr it must have + been declared before it. */ + if (v == first_declared_arg) + break; + if (v == var_in_expr) + { + /* Captures don't make it here so we should be able to rely on + the DECL_SOURCE_LOCATION for var_in_expr. */ + auto_diagnostic_group d; + error_at (alloc_expr_loc, + "variable %qD used in the %<allocator%> clause " + "must be declared before %qD", + var_in_expr, first_declared_arg); + inform (DECL_SOURCE_LOCATION (var_in_expr), "declared here"); + inform (DECL_SOURCE_LOCATION (first_declared_arg), + "to be allocated variable declared here"); + return error_mark_node; + } + } + + /* It's super easy to hide mutations to variables used in the alloc + clause with our current error checking, the following is not currently + diagnosed. + + void f() { + omp_allocator_handle_t alloc = omp_default_mem_alloc; + int a; + int hide_mutation = alloc = omp_large_cap_mem_alloc; + #pragma omp allocate(a) allocator(alloc) + } + + But this is fairly representative of what we currently have in the C + front end, which has the same problem, so a problem for the future. + + Alongside incomplete error diagnostics, there are also cases that + can't be diagnosed or warned until template instantiation. + + template<typename T> + void f(T arg) { + omp_allocator_handle_t alloc = omp_default_mem_alloc; + int a; + foobar(arg, alloc); + #pragma omp allocate(a) allocator(alloc) + } + It's impossible to know what the signature of foobar is until overload + resolution completes. To diagnose (or at least warn) for these edge + cases, this section should be moved to finish_omp_allocate instead of + here. We can add full diagnostics to mutations of variables in the + allocator clause once we do that. */ + gcc_assert (cur_stmt_list + && TREE_CODE (cur_stmt_list) == STATEMENT_LIST); + for (tree_stmt_iterator stmt_it = tsi_last (cur_stmt_list); + !tsi_end_p (stmt_it); + --stmt_it) + { + tree stmt = *stmt_it; + /* Don't check anything preceding first_declared_arg's decl. */ + if (TREE_CODE (stmt) == DECL_EXPR + && DECL_EXPR_DECL (stmt) == first_declared_arg) + break; + /* Due to differences in the C++ AST, I don't believe this catches + any cases right now.*/ + if (TREE_CODE (stmt) == MODIFY_EXPR + && TREE_OPERAND (stmt, 0) == var_in_expr) + { + auto_diagnostic_group d; + error_at (alloc_expr_loc, + "variable %qD used in the %<allocator%> clause must " + "not be modified between declaration of %qD and its " + "%<allocate%> directive", + var_in_expr, first_declared_arg); + inform (EXPR_LOCATION (stmt), "modified here"); + inform (DECL_SOURCE_LOCATION (first_declared_arg), + "to be allocated variable declared here"); + return error_mark_node; + } + } + return NULL_TREE; + }; + /* This diagnostic is meaningless if we have no valid args. */ + if (allocator != NULL_TREE && nl != NULL_TREE) + { + gcc_assert (f_var != NULL_TREE); + /* Declarations and mutations must happen before any of the vars are + declared. If this is satisfied for the first declaration, it will be + satisfied for all of them, so just check the first. */ + cp_omp_loc_tree data = {allocator.get_location (), f_var, nl}; + /* We can't take the address of an rvalue, so we need to do this. */ + tree a = allocator.get_value (); + if (cp_walk_tree (&a, check_omp_allocate_allocator_r, &data, NULL)) + allocator = cp_expr (error_mark_node, UNKNOWN_LOCATION); + } + /* I couldn't find a function that already does this, might be best to + add it instead of having it here. + Some codes, such as template parameters, don't get wrapped despite not + being able to carry a location. We need a location to issue correct + diagnostics in finish_omp_allocate. */ + auto maybe_force_wrap_with_location = [](cp_expr expr_with_loc) -> tree + { + tree expr = expr_with_loc.get_value (); + if (!expr || error_operand_p (expr)) + return expr; + /* In most situations, expr will already have been wrapped, + we don't need to do anything if that's the case. */ + if (CAN_HAVE_LOCATION_P (expr)) + return expr; + + location_t expr_loc = expr_with_loc.get_location (); + /* Copied from tree.cc:maybe_wrap_with_location. */ + tree_code code + = (((CONSTANT_CLASS_P (expr) && TREE_CODE (expr) != STRING_CST) + || (TREE_CODE (expr) == CONST_DECL && !TREE_STATIC (expr))) + ? NON_LVALUE_EXPR : VIEW_CONVERT_EXPR); + tree wrapper = build1_loc (expr_loc, code, TREE_TYPE (expr), expr); + /* Mark this node as being a wrapper. */ + EXPR_LOCATION_WRAPPER_P (wrapper) = 1; + return wrapper; + }; + /* We can still diagnose some things about allocator/alignment even if nl + is empty. */ + finish_omp_allocate (pragma_tok->location, + nl, + maybe_force_wrap_with_location (allocator), + maybe_force_wrap_with_location (alignment)); } /* OpenMP 2.5: @@ -53870,8 +54140,8 @@ cp_parser_omp_construct (cp_parser *parser, cp_token *pragma_tok, bool *if_p) stmt = cp_parser_oacc_wait (parser, pragma_tok); break; case PRAGMA_OMP_ALLOCATE: - cp_parser_omp_allocate (parser, pragma_tok); - return; + /* This is a declarative directive, not a construct. */ + gcc_unreachable (); case PRAGMA_OMP_ATOMIC: cp_parser_omp_atomic (parser, pragma_tok, false); return; @@ -54573,7 +54843,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p) cp_parser_omp_construct (parser, pragma_tok, if_p); return true; case PRAGMA_OMP_ALLOCATE: + /* Don't go through cp_parser_omp_construct as this pragma is a + declarative directive, not a construct. */ cp_parser_omp_allocate (parser, pragma_tok); + /* EOL is handled in cp_parser_omp_allocate. */ return false; case PRAGMA_OACC_ATOMIC: case PRAGMA_OACC_CACHE: diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index ea06740..e2041b8 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -19687,6 +19687,31 @@ 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 (OMP_ALLOCATE_LOCATION (t), vars, alloc, align); + break; + } case OACC_DATA: case OMP_TARGET_DATA: case OMP_TARGET: diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 562736e..fe7d335 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -12326,6 +12326,299 @@ 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 the nodes location member, + this information is currently unused. */ + +void +finish_omp_allocate (location_t loc, tree var_list, tree alloc, tree align) +{ + /* Common routine for modifying the "omp allocate" attribute. This should + only be called once for each var, either after a diagnostic, or when we + are finished with the directive. */ + auto finalize_allocate_attr = [] (tree var, tree alloc, tree align) + { + gcc_assert (var != NULL_TREE && var != error_mark_node); + + /* The attr was added in cp_parser_omp_allocate. */ + tree attr = lookup_attribute ("omp allocate", DECL_ATTRIBUTES (var)); + gcc_assert (attr != NULL_TREE); + + /* cp_parser_omp_allocate adds the location where the var was used as an + arg for diagnostics, it should still be untouched at this point. */ + tree arg_loc = TREE_VALUE (attr); + gcc_assert (arg_loc != NULL_TREE && TREE_CODE (arg_loc) == NOP_EXPR); + + /* We still need the location in case parsing hasn't finished yet, we + simply smuggle it through the chain member. */ + tree attr_value = tree_cons (alloc, align, arg_loc); + /* We can't modify the old "omp allocate" attr, substitution doesn't know + the attr is dependent so it isn't copied when substituting the var. + We avoid making unnecessary copies creating the final node here. */ + DECL_ATTRIBUTES (var) + = tree_cons (get_identifier ("omp allocate"), + attr_value, + remove_attribute ("omp allocate", + DECL_ATTRIBUTES (var))); + }; + /* The alloc/align clauses get marked with error_mark_node after an error is + reported to prevent duplicate diagnosis. The same is done for the var + (TREE_PURPOSE) of any node that has an error, additionally the + "omp allocate" attr is marked so the middle end knows to skip it during + gimplification. */ + + + + for (tree vn = var_list; vn != NULL_TREE; vn = TREE_CHAIN (vn)) + { + tree var = TREE_PURPOSE (vn); + bool var_has_error = false; + if (var == error_mark_node) + /* Early escape. */; + else if (TYPE_REF_P (TREE_TYPE (var))) + { + auto_diagnostic_group d; + error_at (EXPR_LOCATION (TREE_VALUE (vn)), + "variable %qD with reference type may not appear as a " + "list item in an %<allocate%> directive", var); + inform (DECL_SOURCE_LOCATION (var), "%qD declared here", var); + var_has_error = true; + } + else if (TREE_STATIC (var) && var_in_maybe_constexpr_fn (var)) + { + /* Unfortunately, until the first round of band-aids is applied to + make_rtl_for_nonlocal_decl we can't support static vars in + implicit constexpr functions in non-template contexts at all. + Technically, we could support cases inside templates, but it's + difficult to differentiate them here, and it would be confusing to + only allow the cases in templates. */ + auto_diagnostic_group d; + sorry_at (EXPR_LOCATION (TREE_VALUE (vn)), + "static variable %qD is not supported in an %<allocate%> " + "directive in an implicit constexpr function", var); + inform (DECL_SOURCE_LOCATION (var), "%qD declared here", var); + var_has_error = true; + } + + if (var_has_error) + { + /* Mark the node so we don't need to lookup the attribute every + time we check if we need to skip a diagnostic. */ + TREE_PURPOSE (vn) = error_mark_node; + /* We won't have access to the var after it's cleared from the node, + finalize it early. + We avoid needing to handle error_mark_node in + varpool_node::finalize_decl if we make align a NULL_TREE. */ + finalize_allocate_attr (var, error_mark_node, NULL_TREE); + } + } + /* Unfortunately, we can't easily diagnose use of a parameter in the alloc or + align expr before instantiation. For a type dependent expr + potential_constant_expression must return true even if the expr contains + a parameter. The align and alloc clause's exprs must be of type + integer/omp_allocator_handle_t respectively, in theory these extra + constraints would let us diagnose some cases during parsing of a template + declaration. The following case is invalid. + void f0(auto p) { + int a; + #pragma omp allocate(a) align(p) + } + We know that this can't be valid because expr p must be an integer type, + not an empty class type. On the other hand... + constexpr int g(auto) { return 32; } + void f1(auto p) { + int a; + #pragma omp allocate(a) align(g (p)) + } + This is valid code if p is an empty class type, so we can't just + disqualify an expression because it contains a local var or parameter. + + In short, we don't jump through hoops to try to diagnose cases that are + possible to be proven ill-formed, such as with f1 above, we just diagnose + it upon instantiation. Perhaps this can be revisited, but it doesn't + seem to be worth it. It will complicate the error handling code here, + and has a risk of breaking valid code like the f1 case above. + + See PR91953 and r10-6416-g8fda2c274ac66d60c1dfc1349e9efb4e8c2a3580 for + more information. + + There are also funny cases like const int that are considered constant + expressions which we have to accept for correctness, but that only applies + to variables, not parameters. */ + if (align && align != error_mark_node) + { + /* (OpenMP 5.1, 181:17-18) alignment is a constant positive integer + expression with a value that is a power of two. */ + location_t align_loc = EXPR_LOCATION (align); + if (!type_dependent_expression_p (align)) + { + /* Might we want to support implicitly convertible to int? Is that + forbidden by the spec? */ + if (!INTEGRAL_TYPE_P (TREE_TYPE (align))) + { + /* Just use the same error as the value checked error, there is + little value in fragmenting the wording. */ + error_at (align_loc, + "%<align%> clause argument needs to be positive " + "constant power of two integer expression"); + /* Don't repeat the error again. */ + align = error_mark_node; + } + } + if (align != error_mark_node && !value_dependent_expression_p (align)) + { + align = fold_non_dependent_expr (align); + if (!TREE_CONSTANT (align) + || tree_int_cst_sgn (align) != 1 + || !integer_pow2p (align)) + { + error_at (align_loc, + "%<align%> clause argument needs to be positive " + "constant power of two integer expression"); + align = error_mark_node; + } + } + } + + if (alloc == NULL_TREE) + { + for (tree node = var_list; node != NULL_TREE; node = TREE_CHAIN (node)) + { + tree var = TREE_PURPOSE (node); + if (var != error_mark_node && TREE_STATIC (var)) + { + auto_diagnostic_group d; + error_at (EXPR_LOCATION (TREE_VALUE (node)), + "%<allocator%> clause required for " + "static variable %qD", var); + inform (DECL_SOURCE_LOCATION (var), "%qD declared here", var); + alloc = error_mark_node; + } + } + } + else if (alloc != error_mark_node) + { + location_t alloc_loc = EXPR_LOCATION (alloc); + if (!type_dependent_expression_p (alloc)) + { + tree orig_type = TYPE_MAIN_VARIANT (TREE_TYPE (alloc)); + if (!INTEGRAL_TYPE_P (TREE_TYPE (alloc)) + || TREE_CODE (orig_type) != ENUMERAL_TYPE + || TYPE_NAME (orig_type) == NULL_TREE + || (DECL_NAME (TYPE_NAME (orig_type)) + != get_identifier ("omp_allocator_handle_t"))) + { + error_at (alloc_loc, + "%<allocator%> clause expression has type " + "%qT rather than %<omp_allocator_handle_t%>", + TREE_TYPE (alloc)); + alloc = error_mark_node; + } + } + if (alloc != error_mark_node && !value_dependent_expression_p (alloc)) + { + /* It is unclear if this is required as fold_non_dependent_expr + appears to correctly return the original expr if it can't be + folded. Additionally, should we be passing tf_none? */ + alloc = maybe_fold_non_dependent_expr (alloc); + const bool constant_predefined_allocator = [&] () + { + if (!TREE_CONSTANT (alloc)) + return false; + wi::tree_to_widest_ref alloc_value = wi::to_widest (alloc); + /* MAX is inclusive. */ + return (alloc_value >= 1 + && alloc_value <= GOMP_OMP_PREDEF_ALLOC_MAX) + || (alloc_value >= GOMP_OMPX_PREDEF_ALLOC_MIN + && alloc_value <= GOMP_OMPX_PREDEF_ALLOC_MAX); + } (); /* IILE. */ + if (!constant_predefined_allocator) + { + for (tree vn = var_list; vn != NULL_TREE; vn = TREE_CHAIN (vn)) + { + tree var = TREE_PURPOSE (vn); + if (var != error_mark_node && TREE_STATIC (var)) + { + auto_diagnostic_group d; + /* Perhaps we should only report a single error and + inform for each static var? */ + error_at (alloc_loc, + "%<allocator%> clause requires a predefined " + "allocator as %qD is static", var); + inform (DECL_SOURCE_LOCATION (var), + "%qD declared here", var); + alloc = error_mark_node; + } + } + } + } + } + /* Helper algorithm. */ + auto any_of_vars = [&var_list] (bool (*predicate)(tree)) + { + for (tree vn = var_list; vn != NULL_TREE; vn = TREE_CHAIN (vn)) + if (predicate (TREE_PURPOSE (vn))) + return true; + return false; + }; + + /* Even if there have been errors we still want to save our progress so we + don't miss any potential diagnostics. + Technically we don't have to do this if there were errors and alloc, + align, and all the vars are substituted, but it's more work to check for + that than to just add the stmt. If it were viable to finalize everything + before instantiation is complete it might be worth it, but we can't do + that because substitution has to eagerly copy nodes. */ + if (processing_template_decl) + { + tree allocate_stmt = make_node (OMP_ALLOCATE); + /* Pretty sure we don't want side effects on this, it also probably + doesn't matter but lets avoid unnecessary noise. */ + TREE_SIDE_EFFECTS (allocate_stmt) = 0; + OMP_ALLOCATE_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 ([] (tree var) { return var == error_mark_node; })) + { + /* The directive is fully instantiated, however, errors were diagnosed. + We can't remove the "omp allocate" attr just in case we are still + parsing a function, instead, we mark it. */ + for (tree vn = var_list; vn != NULL_TREE; vn = TREE_CHAIN (vn)) + { + tree var = TREE_PURPOSE (vn); + /* If the var decl is marked, it has already been finalized. */ + if (var != error_mark_node) + /* We avoid needing to handle error_mark_node in + varpool_node::finalize_decl if we make align a NULL_TREE. */ + finalize_allocate_attr (var, error_mark_node, NULL_TREE); + } + return; + } + + /* We have no errors and everything is fully instantiated, we can finally + finish the attribute on each var_decl. */ + gcc_assert (!processing_template_decl + && alloc != error_mark_node + && align != error_mark_node + && var_list != NULL_TREE); + + 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, diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index 1ab0664..46418f9 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -10884,7 +10884,10 @@ can_do_nrvo_p (tree retval, tree functype) && same_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (retval)), TYPE_MAIN_VARIANT (functype)) /* And the returned value must be non-volatile. */ - && !TYPE_VOLATILE (TREE_TYPE (retval))); + && !TYPE_VOLATILE (TREE_TYPE (retval)) + /* And the variable must not be used in an allocate directive. */ + && (!flag_openmp || !lookup_attribute ("omp allocate", + DECL_ATTRIBUTES (retval)))); } /* True if we would like to perform NRVO, i.e. can_do_nrvo_p is true and we diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc index aa1dc91..b48d114 100644 --- a/gcc/gimplify.cc +++ b/gcc/gimplify.cc @@ -1489,11 +1489,17 @@ 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 + && 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, + clear it to avoid conflicts with Fortran specific code. */ + 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 diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-10.c b/gcc/testsuite/c-c++-common/gomp/allocate-10.c index 7e8f579..2d93ea0 100644 --- a/gcc/testsuite/c-c++-common/gomp/allocate-10.c +++ b/gcc/testsuite/c-c++-common/gomp/allocate-10.c @@ -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 @@ -22,9 +20,10 @@ f() void h1() { - omp_allocator_handle_t my_handle; + omp_allocator_handle_t my_handle; /* { dg-line h1_my_handle_decl } */ int B1[3]; /* { dg-warning "'my_handle' is used uninitialized" } */ /* { dg-warning "variable 'B1' set but not used" "" { target *-*-* } .-1 } */ + /* { dg-bogus "variable 'my_handle' set but not used" "" { xfail c++ } h1_my_handle_decl } */ #pragma omp allocate(B1) allocator(my_handle) B1[0] = 5; /* { dg-final { scan-tree-dump-times "__builtin_GOMP_alloc" 1 "gimple" } } */ @@ -35,15 +34,17 @@ h1() void h2() { - omp_allocator_handle_t my_handle; + omp_allocator_handle_t my_handle; /* { dg-line h2_my_handle_decl } */ int B2[3]; /* { dg-warning "unused variable 'B2'" } */ + /* { dg-bogus "variable 'my_handle' set but not used" "" { xfail c++ } h2_my_handle_decl } */ #pragma omp allocate(B2) allocator(my_handle) /* No warning as 'B2' is unused */ } void h3() { - omp_allocator_handle_t my_handle; + omp_allocator_handle_t my_handle; /* { dg-line h3_my_handle_decl } */ int B3[3] = {1,2,3}; /* { dg-warning "unused variable 'B3'" } */ + /* { dg-bogus "variable 'my_handle' set but not used" "" { xfail c++ } h3_my_handle_decl } */ #pragma omp allocate(B3) allocator(my_handle) /* No warning as 'B3' is unused */ } diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-11.c b/gcc/testsuite/c-c++-common/gomp/allocate-11.c index dceb97f..dd1bcd9 100644 --- a/gcc/testsuite/c-c++-common/gomp/allocate-11.c +++ b/gcc/testsuite/c-c++-common/gomp/allocate-11.c @@ -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; } diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-12.c b/gcc/testsuite/c-c++-common/gomp/allocate-12.c index 1b77db9..6f3ddd3 100644 --- a/gcc/testsuite/c-c++-common/gomp/allocate-12.c +++ b/gcc/testsuite/c-c++-common/gomp/allocate-12.c @@ -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; } diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-14.c b/gcc/testsuite/c-c++-common/gomp/allocate-14.c index 894921a..55f3739 100644 --- a/gcc/testsuite/c-c++-common/gomp/allocate-14.c +++ b/gcc/testsuite/c-c++-common/gomp/allocate-14.c @@ -1,6 +1,3 @@ -/* TODO: enable for C++ once implemented. */ -/* { dg-do compile { target c } } */ - #pragma omp begin declare target void f () diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-15.c b/gcc/testsuite/c-c++-common/gomp/allocate-15.c index 52cb768..b448714 100644 --- a/gcc/testsuite/c-c++-common/gomp/allocate-15.c +++ b/gcc/testsuite/c-c++-common/gomp/allocate-15.c @@ -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 diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-16.c b/gcc/testsuite/c-c++-common/gomp/allocate-16.c index 0873803..198fe32 100644 --- a/gcc/testsuite/c-c++-common/gomp/allocate-16.c +++ b/gcc/testsuite/c-c++-common/gomp/allocate-16.c @@ -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__ @@ -32,7 +29,7 @@ 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++ } } */ } diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-17.c b/gcc/testsuite/c-c++-common/gomp/allocate-17.c index f75af0c..2a896cc 100644 --- a/gcc/testsuite/c-c++-common/gomp/allocate-17.c +++ b/gcc/testsuite/c-c++-common/gomp/allocate-17.c @@ -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; } diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-18.c b/gcc/testsuite/c-c++-common/gomp/allocate-18.c index 49dc60f..e6ae69f 100644 --- a/gcc/testsuite/c-c++-common/gomp/allocate-18.c +++ b/gcc/testsuite/c-c++-common/gomp/allocate-18.c @@ -1,25 +1,9 @@ -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"); diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-19.c b/gcc/testsuite/c-c++-common/gomp/allocate-19.c index 5c5fc00..8bf8e27 100644 --- a/gcc/testsuite/c-c++-common/gomp/allocate-19.c +++ b/gcc/testsuite/c-c++-common/gomp/allocate-19.c @@ -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"); @@ -59,11 +57,8 @@ get () } 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++ } } */ +#pragma omp allocate(invalid1) align(128) allocator(ompx_gnu_pinned_bogus_1) /* { dg-error "'allocator' clause requires a predefined allocator as 'invalid1' is static" } */ #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" } */ +#pragma omp allocate(invalid3) align(128) allocator(ompx_gnu_pinned_bogus_3) /* { dg-error "'allocator' clause requires a predefined allocator as 'invalid3' is static" } */ + diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-20.c b/gcc/testsuite/c-c++-common/gomp/allocate-20.c new file mode 100644 index 0000000..f65af70 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/allocate-20.c @@ -0,0 +1,337 @@ +#include "allocate-allocator-handle.h" + +#pragma omp allocate() allocator(omp_default_mem_alloc) +/* { dg-error "expected identifier before '\\\)' token" "" { target c } .-1 } */ +/* { dg-error "expected unqualified-id before '\\\)' token" "" { target c++ } .-2 } */ + +/* The following tests are broken up into multiple lines to verify they refer + to the correct use of the variable, this seems to be the easiest way. + C does not currently refer to the correct line. */ +int g; +/* { dg-error "'g' already appeared as list item in an 'allocate' directive" "" { target c } .+1 } */ +#pragma omp allocate(\ +g,\ +g,\ +g,\ +g,\ +g) allocator(omp_default_mem_alloc) +/* { dg-note "appeared first here" "" { target c++ } .-5 } */ +/* { dg-error "'g' already appeared as list item in this directive" "" { target c++ } .-5 } */ +/* { dg-error "'g' already appeared as list item in this directive" "" { target c++ } .-5 } */ +/* { dg-error "'g' already appeared as list item in this directive" "" { target c++ } .-5 } */ +/* { dg-error "'g' already appeared as list item in this directive" "" { target c++ } .-5 } */ + +int g0_0; +int g0_1; +/* { dg-error "'g0_0' already appeared as list item in an 'allocate' directive" "" { target c } .+1 } */ +#pragma omp allocate(\ +g0_0,\ +g0_1,\ +g0_0,\ +g0_0) allocator(omp_default_mem_alloc) +/* { dg-note "appeared first here" "" { target c++ } .-4 } */ +/* { dg-error "'g0_0' already appeared as list item in this directive" "" { target c++ } .-3 } */ +/* { dg-error "'g0_0' already appeared as list item in this directive" "" { target c++ } .-3 } */ + +int g1_0; +int g1_1; +/* { dg-error "'g1_0' already appeared as list item in an 'allocate' directive" "" { target c } .+2 } */ +/* { dg-error "'g1_1' already appeared as list item in an 'allocate' directive" "" { target c } .+1 } */ +#pragma omp allocate(\ +g1_1,\ +g1_0,\ +g1_1,\ +g1_0,\ +g1_0,\ +g1_1) allocator(omp_default_mem_alloc) +/* { dg-note "appeared first here" "" { target c++ } .-6 } */ +/* { dg-note "appeared first here" "" { target c++ } .-6 } */ +/* { dg-error "'g1_1' already appeared as list item in this directive" "" { target c++ } .-6 } */ +/* { dg-error "'g1_0' already appeared as list item in this directive" "" { target c++ } .-6 } */ +/* { dg-error "'g1_0' already appeared as list item in this directive" "" { target c++ } .-6 } */ +/* { dg-error "'g1_1' already appeared as list item in this directive" "" { target c++ } .-6 } */ + +void f() +{ + int v; + /* { dg-error "'v' already appeared as list item in an 'allocate' directive" "" { target c} .+1 } */ + #pragma omp allocate(\ + v,\ + v,\ + v,\ + v,\ + v) + /* { dg-note "appeared first here" "" { target c++ } .-5 } */ + /* { dg-error "'v' already appeared as list item in this directive" "" { target c++ } .-5 } */ + /* { dg-error "'v' already appeared as list item in this directive" "" { target c++ } .-5 } */ + /* { dg-error "'v' already appeared as list item in this directive" "" { target c++ } .-5 } */ + /* { dg-error "'v' already appeared as list item in this directive" "" { target c++ } .-5 } */ + + int v0_0; + int v0_1; + /* { dg-error "'v0_0' already appeared as list item in an 'allocate' directive" "" { target c } .+1 } */ + #pragma omp allocate(\ + v0_0,\ + v0_1,\ + v0_0,\ + v0_0) + /* { dg-note "appeared first here" "" { target c++ } .-4 } */ + /* { dg-error "'v0_0' already appeared as list item in this directive" "" { target c++ } .-3 } */ + /* { dg-error "'v0_0' already appeared as list item in this directive" "" { target c++ } .-3 } */ + + int v1_0; + int v1_1; + /* { dg-error "'v1_0' already appeared as list item in an 'allocate' directive" "" { target c } .+2 } */ + /* { dg-error "'v1_1' already appeared as list item in an 'allocate' directive" "" { target c } .+1 } */ + #pragma omp allocate(\ + v1_1,\ + v1_0,\ + v1_1,\ + v1_0,\ + v1_0,\ + v1_1) + /* { dg-note "appeared first here" "" { target c++ } .-6 } */ + /* { dg-note "appeared first here" "" { target c++ } .-6 } */ + /* { dg-error "'v1_1' already appeared as list item in this directive" "" { target c++ } .-6 } */ + /* { dg-error "'v1_0' already appeared as list item in this directive" "" { target c++ } .-6 } */ + /* { dg-error "'v1_0' already appeared as list item in this directive" "" { target c++ } .-6 } */ + /* { dg-error "'v1_1' already appeared as list item in this directive" "" { target c++ } .-6 } */ +} + +void f_with_parm(int p) /* { dg-note "parameter 'p' declared here" "" { target c++ } } */ +{ + #pragma omp allocate(p) + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c } .+1 } */ + #pragma omp allocate(\ + p,\ + p,\ + p) + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-3 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-3 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-3 } */ + + int v; + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c } .+2 } */ + /* { dg-error "'v' already appeared as list item in an 'allocate' directive" "" { target c } .+1 } */ + #pragma omp allocate(\ + v,\ + p,\ + v,\ + v,\ + p,\ + v,\ + v) + /* { dg-note "appeared first here" "" { target c++ } .-7 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-7 } */ + /* { dg-error "'v' already appeared as list item in this directive" "" { target c++ } .-7 } */ + /* { dg-error "'v' already appeared as list item in this directive" "" { target c++ } .-7 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-7 } */ + /* { dg-error "'v' already appeared as list item in this directive" "" { target c++ } .-7 } */ + /* { dg-error "'v' already appeared as list item in this directive" "" { target c++ } .-7 } */ + + int v0_0; + int v0_1; + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c } .+2 } */ + /* { dg-error "'v0_0' already appeared as list item in an 'allocate' directive" "" { target c } .+1 } */ + #pragma omp allocate(\ + p,\ + v0_0,\ + v0_1,\ + v0_0,\ + v0_0) + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-5 } */ + /* { dg-note "appeared first here" "" { target c++ } .-5 } */ + /* { dg-error "'v0_0' already appeared as list item in this directive" "" { target c++ } .-4 } */ + /* { dg-error "'v0_0' already appeared as list item in this directive" "" { target c++ } .-4 } */ + + int v1_0; + int v1_1; + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c } .+3 } */ + /* { dg-error "'v1_0' already appeared as list item in an 'allocate' directive" "" { target c } .+2 } */ + /* { dg-error "'v1_1' already appeared as list item in an 'allocate' directive" "" { target c } .+1 } */ + #pragma omp allocate(\ + v1_1,\ + p,\ + v1_0,\ + v1_1,\ + v1_0,\ + p,\ + v1_0,\ + v1_1,\ + p) + /* { dg-note "appeared first here" "" { target c++ } .-9 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-9 } */ + /* { dg-note "appeared first here" "" { target c++ } .-9 } */ + /* { dg-error "'v1_1' already appeared as list item in this directive" "" { target c++ } .-9 } */ + /* { dg-error "'v1_0' already appeared as list item in this directive" "" { target c++ } .-9 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-9 } */ + /* { dg-error "'v1_0' already appeared as list item in this directive" "" { target c++ } .-9 } */ + /* { dg-error "'v1_1' already appeared as list item in this directive" "" { target c++ } .-9 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-9 } */ +} + +/* Valid var used in allocator clause diagnostics. + (No diagnostic should be emitted related to 'alloc') */ + +void f_with_parm_and_allocator0(int p) /* { dg-note "parameter 'p' declared here" "" { target c++ } } */ +{ + omp_allocator_handle_t alloc = omp_default_mem_alloc; + #pragma omp allocate(p) allocator(alloc) + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c } .+1 } */ + #pragma omp allocate(\ + p,\ + p,\ + p) allocator(alloc) + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-3 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-3 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-3 } */ + + int v; + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c } .+2 } */ + /* { dg-error "'v' already appeared as list item in an 'allocate' directive" "" { target c } .+1 } */ + #pragma omp allocate(\ + v,\ + p,\ + v,\ + v,\ + p,\ + v,\ + v) allocator(alloc) + /* { dg-note "appeared first here" "" { target c++ } .-7 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-7 } */ + /* { dg-error "'v' already appeared as list item in this directive" "" { target c++ } .-7 } */ + /* { dg-error "'v' already appeared as list item in this directive" "" { target c++ } .-7 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-7 } */ + /* { dg-error "'v' already appeared as list item in this directive" "" { target c++ } .-7 } */ + /* { dg-error "'v' already appeared as list item in this directive" "" { target c++ } .-7 } */ + + int v0_0; + int v0_1; + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c } .+2 } */ + /* { dg-error "'v0_0' already appeared as list item in an 'allocate' directive" "" { target c } .+1 } */ + #pragma omp allocate(\ + p,\ + v0_0,\ + v0_1,\ + v0_0,\ + v0_0) allocator(alloc) + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-5 } */ + /* { dg-note "appeared first here" "" { target c++ } .-5 } */ + /* { dg-error "'v0_0' already appeared as list item in this directive" "" { target c++ } .-4 } */ + /* { dg-error "'v0_0' already appeared as list item in this directive" "" { target c++ } .-4 } */ + + int v1_0; + int v1_1; + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c } .+3 } */ + /* { dg-error "'v1_0' already appeared as list item in an 'allocate' directive" "" { target c } .+2 } */ + /* { dg-error "'v1_1' already appeared as list item in an 'allocate' directive" "" { target c } .+1 } */ + #pragma omp allocate(\ + v1_1,\ + p,\ + v1_0,\ + v1_1,\ + v1_0,\ + p,\ + v1_0,\ + v1_1,\ + p) allocator(alloc) + /* { dg-note "appeared first here" "" { target c++ } .-9 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-9 } */ + /* { dg-note "appeared first here" "" { target c++ } .-9 } */ + /* { dg-error "'v1_1' already appeared as list item in this directive" "" { target c++ } .-9 } */ + /* { dg-error "'v1_0' already appeared as list item in this directive" "" { target c++ } .-9 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-9 } */ + /* { dg-error "'v1_0' already appeared as list item in this directive" "" { target c++ } .-9 } */ + /* { dg-error "'v1_1' already appeared as list item in this directive" "" { target c++ } .-9 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-9 } */ +} + +/* Var used in allocator clause diagnostics. Tests that invalid vars passed + into the allocate directive are not considered and bogus/repeat diagnostics + are not emitted. */ + +void f_with_parm_and_allocator1(int p) /* { dg-note "parameter 'p' declared here" "" { target c++ } } */ +{ + int v0; /* { dg-note "to be allocated variable declared here" } */ + omp_allocator_handle_t alloc0 = omp_default_mem_alloc; /* { dg-note "declared here" } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c } .+2 } */ + /* { dg-error "variable 'alloc0' used in the 'allocator' clause must be declared before 'v0'" "" { target c } .+1 } */ + #pragma omp allocate(\ + p,\ + v0)\ + allocator(alloc0) + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-3 } */ + /* { dg-error "variable 'alloc0' used in the 'allocator' clause must be declared before 'v0'" "" { target c++ } .-2 } */ + + int v1; /* { dg-note "declared here" } */ + { + omp_allocator_handle_t alloc1 = omp_default_mem_alloc; + int v2; + /* { dg-error "'allocate' directive must be in the same scope as 'v1'" "" { target c } .+1 } */ + #pragma omp allocate(\ + v1,\ + v2\ + ) allocator(alloc1) + /* { dg-error "'allocate' directive must be in the same scope as 'v1'" "" { target c++ } .-3 } */ + } + { + int v3; /* { dg-note "to be allocated variable declared here" } */ + omp_allocator_handle_t alloc2 = omp_default_mem_alloc; /* { dg-note "declared here" } */ + /* { dg-error "variable 'alloc2' used in the 'allocator' clause must be declared before 'v3'" "" { target c } .+2 } */ + /* { dg-error "'allocate' directive must be in the same scope as 'v1'" "" { target c } .+1 } */ + #pragma omp allocate(\ + v1,\ + v3\ + ) allocator(alloc2) + /* { dg-error "'allocate' directive must be in the same scope as 'v1'" "" { target c++ } .-3 } */ + /* { dg-error "variable 'alloc2' used in the 'allocator' clause must be declared before 'v3'" "" { target c++ } .-2 } */ + } +} + +/* First argument valid. + These cases could still be fleshed out a bit more, there was original a typo + that caused diagnostics to always refer to the first argument of the + directive in the C++ front end, these tests are for that case. */ + +void first_valid0() +{ + int a; /* { dg-note "declared here" } */ + { + int v; + /* { dg-error "'allocate' directive must be in the same scope as 'a'" "" { target c } .+1 } */ + #pragma omp allocate(\ + v,\ + a) /* { dg-error "'allocate' directive must be in the same scope as 'a'" "" { target c++ } } */ + } +} + +void first_valid1(int p) /* { dg-note "parameter 'p' declared here" "" { target c++ } } */ +{ + int v; + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c } .+1 } */ + #pragma omp allocate(\ + v,\ + p) /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } } */ +} + +void first_valid2(int p) /* { dg-note "parameter 'p' declared here" "" { target c++ } } */ +{ + int a; /* { dg-note "declared here" } */ + { + int v; + /* { dg-error "'allocate' directive must be in the same scope as 'a'" "" { target c } .+2 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c } .+1 } */ + #pragma omp allocate(\ + v,\ + a,\ + p) + /* { dg-error "'allocate' directive must be in the same scope as 'a'" "" { target c++ } .-2 } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target c++ } .-2 } */ + } +} + +/* Missing cases that contain undeclared variables. */ diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-5.c b/gcc/testsuite/c-c++-common/gomp/allocate-5.c index 2ca4786..bd10b6f 100644 --- a/gcc/testsuite/c-c++-common/gomp/allocate-5.c +++ b/gcc/testsuite/c-c++-common/gomp/allocate-5.c @@ -21,11 +21,10 @@ 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-message "'allocator' clause required for static variable 'c'" "" { target *-*-* } .-1 } */ } void @@ -34,14 +33,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 allocator 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 "variable 'b' used in the 'allocator' clause must be declared before 'a2'" "" { target c++ } .-2 } */ + /* We have diverging behavior here between c and c++ due to a difference in + order of diagnostics, this should probably be unified. */ } @@ -50,22 +49,16 @@ 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 @@ -73,9 +66,6 @@ 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(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 } */ } diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-9.c b/gcc/testsuite/c-c++-common/gomp/allocate-9.c index f37a111..feab8c7 100644 --- a/gcc/testsuite/c-c++-common/gomp/allocate-9.c +++ b/gcc/testsuite/c-c++-common/gomp/allocate-9.c @@ -1,101 +1,75 @@ -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" } */ /* 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" } */ +/* { dg-note "'A1' declared here" "" { target 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'" } */ +/* { dg-note "'A4' declared here" "" { target 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" } */ +/* { dg-note "'A5' declared here" "" { target 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]; + #pragma omp allocate(D) align(32) allocator(omp_large_cap_mem_alloc) /* { dg-error "'allocate' directive must be in the same scope as 'D'" } */ + 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 } */ + #pragma omp allocate(c2, b2) /* { dg-error "'allocate' directive must be in the same scope as 'b2'" } */ +/* { dg-note "declared here" "" { target *-*-* } g_a2_b2_decl } */ return c2+a2+b2; } } 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 } */ + #pragma omp allocate(q) /* { dg-error "function parameter 'q' may not appear as list item in an 'allocate' directive" } */ +/* { dg-note "parameter 'q' declared here" "" { target c++ } .-3 } */ return q; } @@ -103,7 +77,7 @@ 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 } */ + #pragma omp allocate(var3) allocator((omp_allocator_handle_t)-1L) /* { dg-error "'allocator' clause requires a predefined allocator as 'var3' is static" } */ +/* { dg-note "'var3' declared here" "" { target c++ } .-2 } */ return var3; } diff --git a/gcc/testsuite/c-c++-common/gomp/allocate-allocator-handle.h b/gcc/testsuite/c-c++-common/gomp/allocate-allocator-handle.h new file mode 100644 index 0000000..ecb2836 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/allocate-allocator-handle.h @@ -0,0 +1,17 @@ +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; diff --git a/gcc/testsuite/c-c++-common/gomp/directive-1.c b/gcc/testsuite/c-c++-common/gomp/directive-1.c index 21ca319..e3ede6e 100644 --- a/gcc/testsuite/c-c++-common/gomp/directive-1.c +++ b/gcc/testsuite/c-c++-common/gomp/directive-1.c @@ -19,7 +19,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 } } */ diff --git a/gcc/testsuite/g++.dg/gomp/allocate-10.C b/gcc/testsuite/g++.dg/gomp/allocate-10.C new file mode 100644 index 0000000..d5ff28a --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-10.C @@ -0,0 +1,1019 @@ +#include "allocate-allocator-handle.h" + +/* Diagnostics for invalid cases. There are a few valid cases peppered in here + but that's not what is being tested for in here. */ + + +/**************************************************** + * Reference variable used in an allocate directive * + ****************************************************/ + +void ref_var() +{ + int a = 42; + int& ref = a; /* { dg-note "'ref' declared here" } */ + #pragma omp allocate(ref) /* { dg-error "variable 'ref' with reference type may not appear as a list item in an 'allocate' directive" } */ +} + +template<typename> +void ref_var_templ_not_instantiated() +{ + int a = 42; + int& ref = a; /* { dg-note "'ref' declared here" } */ + #pragma omp allocate(ref) /* { dg-error "variable 'ref' with reference type may not appear as a list item in an 'allocate' directive" } */ +} + +template<typename T> +void dependent_ref_var_templ_not_instantiated() +{ + T a = 42; + T& t = a; /* { dg-note "'t' declared here" } */ + #pragma omp allocate(t) /* { dg-error "variable 't' with reference type may not appear as a list item in an 'allocate' directive" } */ +} + +template<typename T> +void dependent_var_templ_not_instantiated() +{ + T t = 42; + #pragma omp allocate(t) +} + +template<typename T> +void dependent_var_templ_0() +{ + T t = 42; + #pragma omp allocate(t) +} + +template<typename T> +void dependent_var_templ_1() +{ + T t = 42; /* { dg-note "'t' declared here" } */ + #pragma omp allocate(t) /* { dg-error "variable 't' with reference type may not appear as a list item in an 'allocate' directive" } */ +} + +template<typename T> +void dependent_var_templ_2() +{ + int a; + T t = a; /* { dg-note "'t' declared here" } */ + #pragma omp allocate(t) /* { dg-error "variable 't' with reference type may not appear as a list item in an 'allocate' directive" } */ +} + +void instantiate_var_templ() +{ + dependent_var_templ_0<int>(); /* { dg-bogus "required from here" } */ + dependent_var_templ_1<int>(); /* { dg-bogus "required from here" } */ + dependent_var_templ_1<int const&>(); /* { dg-message "required from here" } */ + dependent_var_templ_2<int>(); /* { dg-bogus "required from here" } */ + dependent_var_templ_2<int&>(); /* { dg-message "required from here" } */ + dependent_var_templ_2<int const&>(); /* { dg-message "required from here" } */ +} + + +/**************************** + * Invalid allocator clause * + ****************************/ + +template<omp_allocator_handle_t Alloc> +void nttp_allocator() +{ + int a; + #pragma omp allocate(a) allocator(Alloc) +} + +template<omp_allocator_handle_t Alloc> +void nttp_allocator_uninstantiated() +{ + int a; + #pragma omp allocate(a) allocator(Alloc) +} + +template<int Alloc> +void nttp_wrong_type_allocator_uninstantiated() +{ + int a; + #pragma omp allocate(a) allocator(Alloc) /* { dg-error "'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'" } */ +} + +template<typename AllocT, AllocT Alloc> +void nttp_dependent_type_allocator() +{ + int a; + #pragma omp allocate(a) allocator(Alloc) /* { dg-error "'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'" } */ +} + +template<typename AllocT, AllocT Alloc> +void nttp_dependent_type_allocator_uninstantiated() +{ + int a; + #pragma omp allocate(a) allocator(Alloc) +} + +void instantiate_nttp_allocator() +{ + nttp_allocator<omp_default_mem_alloc>(); /* { dg-bogus "required from here" } */ + nttp_dependent_type_allocator<omp_allocator_handle_t, omp_default_mem_alloc>(); /* { dg-bogus "required from here" } */ + nttp_dependent_type_allocator<int, 5>(); /* { dg-message "required from here" } */ +} + +template<omp_allocator_handle_t Alloc> +void nttp_allocator_static() +{ + static int a; /* { dg-note "'a' declared here" } */ + #pragma omp allocate(a) allocator(Alloc) /* { dg-error "'allocator' clause requires a predefined allocator as 'a' is static" } */ +} + +template<omp_allocator_handle_t Alloc> +void nttp_allocator_uninstantiated_static() +{ + static int a; + #pragma omp allocate(a) allocator(Alloc) +} + +template<int Alloc> +void nttp_wrong_type_allocator_uninstantiated_static() +{ + static int a; + #pragma omp allocate(a) allocator(Alloc) /* { dg-error "'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'" } */ +} + +template<typename AllocT, AllocT Alloc> +void nttp_dependent_type_allocator_static_0() +{ + static int a; /* { dg-note "'a' declared here" } */ + #pragma omp allocate(a) allocator(Alloc) /* { dg-error "'allocator' clause requires a predefined allocator as 'a' is static" } */ +} + +template<typename AllocT, AllocT Alloc> +void nttp_dependent_type_allocator_static_1() +{ + static int a; + #pragma omp allocate(a) allocator(Alloc) /* { dg-error "'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'" } */ +} + +template<typename AllocT, AllocT Alloc> +void nttp_dependent_type_allocator_uninstantiated_static() +{ + static int a; + #pragma omp allocate(a) allocator(Alloc) +} + +#define DEFINITELY_NOT_PREDEFINED static_cast<omp_allocator_handle_t>(1024) + +void instantiate_nttp_allocator_static() +{ + nttp_allocator_static<omp_default_mem_alloc>(); /* { dg-bogus "required from here" } */ + nttp_allocator_static<DEFINITELY_NOT_PREDEFINED>(); /* { dg-message "required from here" } */ + nttp_dependent_type_allocator_static_0<omp_allocator_handle_t, omp_default_mem_alloc>(); /* { dg-bogus "required from here" } */ + nttp_dependent_type_allocator_static_0<omp_allocator_handle_t, DEFINITELY_NOT_PREDEFINED>(); /* { dg-message "required from here" } */ + nttp_dependent_type_allocator_static_1<int, 1>(); /* { dg-message "required from here" } */ +} + +#undef DEFINITELY_NOT_PREDEFINED + + +template<typename AllocT> +void templ_allocator_param_0(AllocT alloc) +{ + int a; + #pragma omp allocate(a) allocator(alloc) +} + +template<typename AllocT> +void templ_allocator_param_1(AllocT alloc) +{ + int a; + #pragma omp allocate(a) allocator(alloc) /* { dg-error "'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'" } */ +} + +template<typename AllocT> +void templ_allocator_param_uninstantiated(AllocT alloc) +{ + int a; + #pragma omp allocate(a) allocator(alloc) +} + +void instantiate_templ_allocator_param() +{ + templ_allocator_param_0(omp_default_mem_alloc); /* { dg-bogus "required from here" } */ + templ_allocator_param_1(omp_default_mem_alloc); /* { dg-bogus "required from here" } */ + templ_allocator_param_1(0); /* { dg-message "required from here" } */ +} + + +template<typename> +void missing_allocator_clause_uninstantiated() +{ + static int a; /* { dg-note "'a' declared here" } */ + #pragma omp allocate(a) /* { dg-error "'allocator' clause required for static variable 'a'" } */ +} + +/* Cases that are never constant omp_allocator_handle_t expressions (and are required to be) */ + +template<typename> +void allocator_param_static_uninstantiated(omp_allocator_handle_t alloc) +{ + static int a; /* { dg-note "'a' declared here" } */ + #pragma omp allocate(a) allocator(alloc) /* { dg-error "'allocator' clause requires a predefined allocator as 'a' is static" } */ +} + +template<typename> +void allocator_var_static_uninstantiated() +{ + omp_allocator_handle_t alloc = omp_default_mem_alloc; + static int a; /* { dg-note "'a' declared here" } */ + #pragma omp allocate(a) allocator(alloc) /* { dg-error "'allocator' clause requires a predefined allocator as 'a' is static" } */ +} + +/* See cp/semantics.cc:finish_omp_allocate + These cases will always be invalid but diagnosing type dependent cases + before instantiation is too difficult. */ + +template<typename AllocT> +void templ_allocator_param_static_uninstantiated(AllocT alloc) +{ + static int a; + #pragma omp allocate(a) allocator(alloc) +} + +template<typename AllocT> +void templ_allocator_var_static_uninstantiated() +{ + AllocT alloc = omp_default_mem_alloc; + static int a; + #pragma omp allocate(a) allocator(alloc) +} + + +/************************ + * Invalid align clause * + ************************/ + +template<int Align> +void nttp_align() +{ + int a; + #pragma omp allocate(a) align(Align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ +} + +template<int Align> +void nttp_align_uninstantiated() +{ + int a; + #pragma omp allocate(a) align(Align) +} + +template<int* Align> +void nttp_wrong_type_align_uninstantiated() +{ + int a; + #pragma omp allocate(a) align(Align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ +} + +template<typename AlignT, AlignT Align> +void nttp_dependent_type_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" } */ +} + +template<typename AlignT, AlignT Align> +void nttp_dependent_type_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 *-*-* } } */ +} + +template<typename AlignT, AlignT Align> +void nttp_dependent_type_align_uninstantiated() +{ + int a; + #pragma omp allocate(a) align(Align) +} + +void instantiate_nttp_align() +{ + nttp_align<32>(); + nttp_align<42>(); /* { dg-message "required from here" } */ + nttp_dependent_type_align_0<int, 32>(); /* { dg-bogus "required from here" } */ + nttp_dependent_type_align_0<int, 42>(); /* { dg-message "required from here" } */ + nttp_dependent_type_align_1<int, 32>(); /* { dg-bogus "required from here" } */ + /* We just need any non integer NTTP that is valid in c++98, this fits the bill. */ + nttp_dependent_type_align_1<void(*)(), instantiate_nttp_align>(); /* { dg-message "required from here" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "Bugged diagnostic, see comment" { target *-*-* } .-1 } */ + /* I believe this diagnostic is bugged, it should refer to where the + expression is used, not where it originated from. This isn't a bug for + this feature though so I'm making the test case work around it, + when this bug is fixed this test case, and the xfail in the test case in + nttp_dependent_type_align_1 can be remove. */ +} + +/* Cases that are never constant integer expressions (always required for the align clause) */ + +template<typename> +void align_param_uninstantiated(int align) +{ + int a; + #pragma omp allocate(a) align(align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ +} + +template<typename> +void align_var_uninstantiated() +{ + int align = 32; + int a; + #pragma omp allocate(a) align(align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ +} + +/* See cp/semantics.cc:finish_omp_allocate + These cases will always be invalid but diagnosing type dependent cases + before instantiation is too difficult. */ + +template<typename AlignT> +void templ_align_param_uninstantiated(AlignT align) +{ + int a; + #pragma omp allocate(a) align(align) +} + +template<typename AlignT> +void templ_align_var_uninstantiated() +{ + AlignT align = 32; + int a; + #pragma omp allocate(a) align(align) +} + + + +/*************** + * Mixed cases * + ***************/ + +template<typename Var, + typename AllocT, AllocT Alloc, + typename AlignT, AlignT Align> +void all_dependent_uninstantiated() +{ + int b = 42; + Var a = b; + #pragma omp allocate(a) allocator(Alloc) align(Align) +} + +template<typename Var, + typename AllocT, AllocT Alloc, + typename AlignT, AlignT Align> +void all_dependent_valid() +{ + int b = 42; + Var a = b; + #pragma omp allocate(a) allocator(Alloc) align(Align) +} + +template<typename Var, + typename AllocT, AllocT Alloc, + typename AlignT, AlignT Align> +void all_dependent_0() +{ + int b = 42; + Var a = b; /* { dg-note "'a' declared here" } */ + #pragma omp allocate(a) allocator(Alloc) align(Align) + /* { dg-error "variable 'a' with reference type may not appear as a list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ +} + +template<typename Var, + typename AllocT, AllocT Alloc, + typename AlignT, AlignT Align> +void all_dependent_1() +{ + int b = 42; + Var a = b; + #pragma omp allocate(a) allocator(Alloc) align(Align) + /* { dg-error "'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'" "" { target *-*-* } .-1 } */ +} + +template<typename Var, + typename AllocT, AllocT Alloc, + typename AlignT, AlignT Align> +void all_dependent_2() +{ + int b = 42; + Var a = b; + #pragma omp allocate(a) allocator(Alloc) align(Align) + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ +} + +template<typename Var, + typename AllocT, AllocT Alloc, + typename AlignT, AlignT Align> +void all_dependent_3() +{ + int b = 42; + Var a = b; /* { dg-note "'a' declared here" } */ + #pragma omp allocate(a) allocator(Alloc) align(Align) + /* { dg-error "variable 'a' with reference type may not appear as a list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + /* { dg-error "'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'" "" { target *-*-* } .-2 } */ +} + +template<typename Var, + typename AllocT, AllocT Alloc, + typename AlignT, AlignT Align> +void all_dependent_4() +{ + int b = 42; + Var a = b; /* { dg-note "'a' declared here" } */ + #pragma omp allocate(a) allocator(Alloc) align(Align) + /* { dg-error "variable 'a' with reference type may not appear as a list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-2 } */ +} + +template<typename Var, + typename AllocT, AllocT Alloc, + typename AlignT, AlignT Align> +void all_dependent_5() +{ + int b = 42; + Var a = b; + #pragma omp allocate(a) allocator(Alloc) align(Align) + /* { dg-error "'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'" "" { target *-*-* } .-1 } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-2 } */ +} + +template<typename Var, + typename AllocT, AllocT Alloc, + typename AlignT, AlignT Align> +void all_dependent_6() +{ + int b = 42; + Var a = b; /* { dg-note "'a' declared here" } */ + #pragma omp allocate(a) allocator(Alloc) align(Align) + /* { dg-error "variable 'a' with reference type may not appear as a list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + /* { dg-error "'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'" "" { target *-*-* } .-2 } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-3 } */ +} + +void instantiate_all_dependent() +{ + all_dependent_valid<int, omp_allocator_handle_t, omp_default_mem_alloc, int, 32>(); + /* Don't test the type mismatch for the align clause here, it's diagnostic + location is buggy, and the error message is the same. We just really want + to test that we aren't emitting bogus errors when multiple things are + dependent, so it's unnecessary to test that case again. */ + all_dependent_0<int, omp_allocator_handle_t, omp_default_mem_alloc, int, 32>(); /* { dg-bogus "required from here" } */ + all_dependent_0<int&, omp_allocator_handle_t, omp_default_mem_alloc, int, 32>(); /* { dg-message "required from here" } */ + + all_dependent_1<int, omp_allocator_handle_t, omp_default_mem_alloc, int, 32>(); /* { dg-bogus "required from here" } */ + all_dependent_1<int, int, 1, int, 32>(); /* { dg-message "required from here" } */ + + all_dependent_2<int, omp_allocator_handle_t, omp_default_mem_alloc, int, 32>(); /* { dg-bogus "required from here" } */ + all_dependent_2<int, omp_allocator_handle_t, omp_default_mem_alloc, int, 42>(); /* { dg-message "required from here" } */ + + all_dependent_3<int, omp_allocator_handle_t, omp_default_mem_alloc, int, 32>(); /* { dg-bogus "required from here" } */ + all_dependent_3<int&, int, 1, int, 32>(); /* { dg-message "required from here" } */ + + all_dependent_4<int, omp_allocator_handle_t, omp_default_mem_alloc, int, 32>(); /* { dg-bogus "required from here" } */ + all_dependent_4<int&, omp_allocator_handle_t, omp_default_mem_alloc, int, 42>(); /* { dg-message "required from here" } */ + + all_dependent_5<int, omp_allocator_handle_t, omp_default_mem_alloc, int, 32>(); /* { dg-bogus "required from here" } */ + all_dependent_5<int, int, 1, int, 42>(); /* { dg-message "required from here" } */ + + all_dependent_6<int, omp_allocator_handle_t, omp_default_mem_alloc, int, 32>(); /* { dg-bogus "required from here" } */ + all_dependent_6<int&, int, 1, int, 42>(); /* { dg-message "required from here" } */ +} + +/* We are missing combined cases for static var used in the allocate directive, + but it should be fine, the combined cases immediately above are probably + overkill as it is. */ + + +/****************************** + * Invalid allocate directive * + ******************************/ + +/* We are only testing that we gracefully handle an empty list of vars. */ + +void no_parens() +{ + #pragma omp allocate /* { dg-error "expected '\\\(' before end of line" } */ +} + +template<typename> +void templ_no_parens() +{ + #pragma omp allocate /* { dg-error "expected '\\\(' before end of line" } */ +} +template void templ_no_parens<void>(); + +template<typename> +void templ_no_parens_uninstantiated() +{ + #pragma omp allocate /* { dg-error "expected '\\\(' before end of line" } */ +} + +void no_vars() +{ + #pragma omp allocate() /* { dg-error "expected unqualified-id before '\\\)' token" } */ +} + +template<typename> +void templ_no_vars() +{ + #pragma omp allocate() /* { dg-error "expected unqualified-id before '\\\)' token" } */ +} +template void templ_no_vars<void>(); + +template<typename> +void templ_no_vars_uninstantiated() +{ + #pragma omp allocate() /* { dg-error "expected unqualified-id before '\\\)' token" } */ +} + +/* We can't diagnose anything about the allocator clause if we have no + variables, but we do need to make sure we don't crash. */ + +void no_vars_allocator() +{ + #pragma omp allocate() allocator(omp_default_mem_alloc) /* { dg-error "expected unqualified-id before '\\\)' token" } */ +} + +template<typename> +void templ_no_vars_allocator() +{ + #pragma omp allocate() allocator(omp_default_mem_alloc) /* { dg-error "expected unqualified-id before '\\\)' token" } */ +} +template void templ_no_vars_allocator<void>(); + +template<typename> +void templ_no_vars_allocator_uninstantiated() +{ + #pragma omp allocate() allocator(omp_default_mem_alloc) /* { dg-error "expected unqualified-id before '\\\)' token" } */ +} + +/* We can still diagnose errors about the align clause without any vars. */ + +void no_vars_invalid_align() +{ + #pragma omp allocate() align(42) /* { dg-error "expected unqualified-id before '\\\)' token" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ +} + +template<typename> +void templ_no_vars_invalid_align() +{ + #pragma omp allocate() align(42) /* { dg-error "expected unqualified-id before '\\\)' token" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ +} +template void templ_no_vars_invalid_align<void>(); + +template<typename> +void templ_no_vars_invalid_align_uninstantiated() +{ + #pragma omp allocate() align(42) /* { dg-error "expected unqualified-id before '\\\)' token" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ +} + +template<int Align> +void templ_no_vars_dep_align() +{ + #pragma omp allocate() align(Align) /* { dg-error "expected unqualified-id before '\\\)' token" } */ +} +template void templ_no_vars_dep_align<32>(); + +template<int Align> +void templ_no_vars_dep_align_invalid() +{ + #pragma omp allocate() align(Align) /* { dg-error "expected unqualified-id before '\\\)' token" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ +} +template void templ_no_vars_dep_align_invalid<42>(); + +template<int Align> +void templ_no_vars_dep_align_uninstantiated() +{ + #pragma omp allocate() align(Align) /* { dg-error "expected unqualified-id before '\\\)' token" } */ +} + +/********************************* + * All vars in directive invalid * + *********************************/ + +void invalid_vars_param(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + #pragma omp allocate(p) /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" } */ +} + +template<typename> +void templ_invalid_vars_param(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + #pragma omp allocate(p) /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" } */ +} +template void templ_invalid_vars_param<void>(int); + +template<typename> +void templ_invalid_vars_param_uninstantiated(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + #pragma omp allocate(p) /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" } */ +} + +void invalid_vars_out_of_scope() +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + } +} + +template<typename> +void templ_invalid_vars_out_of_scope() +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + } +} +template void templ_invalid_vars_out_of_scope<void>(); + +template<typename> +void templ_invalid_vars_out_of_scope_uninstantiated() +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + } +} + +void invalid_vars_out_of_scope_and_param(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a, p) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + } +} + +template<typename> +void templ_invalid_vars_out_of_scope_and_param(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a, p) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + } +} +template void templ_invalid_vars_out_of_scope_and_param<void>(int); + +template<typename> +void templ_invalid_vars_out_of_scope_and_param_uninstantiated(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a, p) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + } +} + +/* Same as above, we can't diagnose anything about the allocator clause if we + have no variables, but we do need to make sure we don't crash. */ + +void invalid_vars_param_allocator(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + #pragma omp allocate(p) allocator(omp_default_mem_alloc) /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" } */ +} + +template<typename> +void templ_invalid_vars_param_allocator(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + #pragma omp allocate(p) allocator(omp_default_mem_alloc) /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" } */ +} +template void templ_invalid_vars_param_allocator<void>(int); + +template<typename> +void templ_invalid_vars_param_allocator_uninstantiated(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + #pragma omp allocate(p) allocator(omp_default_mem_alloc) /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" } */ +} + +void invalid_vars_out_of_scope_allocator() +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a) allocator(omp_default_mem_alloc) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + } +} + +template<typename> +void templ_invalid_vars_out_of_scope_allocator() +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a) allocator(omp_default_mem_alloc) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + } +} +template void templ_invalid_vars_out_of_scope_allocator<void>(); + +template<typename> +void templ_invalid_vars_out_of_scope_allocator_uninstantiated() +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a) allocator(omp_default_mem_alloc) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + } +} + +void invalid_vars_out_of_scope_and_param_allocator(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a, p) allocator(omp_default_mem_alloc) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + } +} + +template<typename> +void templ_invalid_vars_out_of_scope_and_param_allocator(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a, p) allocator(omp_default_mem_alloc) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + } +} +template void templ_invalid_vars_out_of_scope_and_param_allocator<void>(int); + +template<typename> +void templ_invalid_vars_out_of_scope_and_param_allocator_uninstantiated(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a, p) allocator(omp_default_mem_alloc) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + } +} + +/* Invalid vars with non-dependent invalid align */ + +void invalid_vars_param_align_invalid(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + #pragma omp allocate(p) align(42) /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ +} + +template<typename> +void templ_invalid_vars_param_align_invalid(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + #pragma omp allocate(p) align(42) /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ +} +template void templ_invalid_vars_param_align_invalid<void>(int); + +template<typename> +void templ_invalid_vars_param_align_invalid_uninstantiated(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + #pragma omp allocate(p) align(42) /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ +} + +void invalid_vars_out_of_scope_align_invalid() +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a) align(42) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ + } +} + +template<typename> +void templ_invalid_vars_out_of_scope_align_invalid() +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a) align(42) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ + } +} +template void templ_invalid_vars_out_of_scope_align_invalid<void>(); + +template<typename> +void templ_invalid_vars_out_of_scope_align_invalid_uninstantiated() +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a) align(42) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ + } +} + +void invalid_vars_out_of_scope_and_param_align_invalid(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a, p) align(42) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-2 } */ + } +} + +template<typename> +void templ_invalid_vars_out_of_scope_and_param_align_invalid(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a, p) align(42) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-2 } */ + } +} +template void templ_invalid_vars_out_of_scope_and_param_align_invalid<void>(int); + +template<typename> +void templ_invalid_vars_out_of_scope_and_param_align_invalid_uninstantiated(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a, p) align(42) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-2 } */ + } +} + + +/* Param (dependent align) */ + +template<int Align> +void templ_invalid_vars_param_dependent_align_uninstantiated(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + #pragma omp allocate(p) align(Align) /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" } */ +} + +template<int Align> +void templ_invalid_vars_param_dependent_align(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + #pragma omp allocate(p) align(Align) /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" } */ +} +template void templ_invalid_vars_param_dependent_align<32>(int); + +template<int Align> +void templ_invalid_vars_param_dependent_align_invalid(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + #pragma omp allocate(p) align(Align) /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ +} +template void templ_invalid_vars_param_dependent_align_invalid<42>(int); + + +/* Out of scope (dependent align) */ + +template<int Align> +void templ_invalid_vars_out_of_scope_dependent_align_uninstantiated() +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a) align(Align) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + } +} + +template<int Align> +void templ_invalid_vars_out_of_scope_dependent_align() +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a) align(Align) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + } +} +template void templ_invalid_vars_out_of_scope_dependent_align<32>(); + +template<int Align> +void templ_invalid_vars_out_of_scope_dependent_align_invalid() +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a) align(Align) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ + } +} +template void templ_invalid_vars_out_of_scope_dependent_align_invalid<42>(); + + +/* Param and out of scope (dependent align) */ + +template<int Align> +void templ_invalid_vars_out_of_scope_and_param_dependent_align_uninstantiated(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a, p) align(Align) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + } +} + +template<int Align> +void templ_invalid_vars_out_of_scope_and_param_dependent_align(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a, p) align(Align) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + } +} +template void templ_invalid_vars_out_of_scope_and_param_dependent_align<32>(int); + +template<int Align> +void templ_invalid_vars_out_of_scope_and_param_dependent_align_invalid(int p) /* { dg-note "parameter 'p' declared here" } */ +{ + int a; /* { dg-note "declared here" } */ + { + #pragma omp allocate(a, p) align(Align) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + /* { dg-error "function parameter 'p' may not appear as list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-2 } */ + } +} +template void templ_invalid_vars_out_of_scope_and_param_dependent_align_invalid<42>(int); + + + +/**************************************************** + * uses of var in multiple directives in a template * + ****************************************************/ + +/* We are missing a lot of cases here but testing all of them shouldn't be + necessary. Uses of variables in multiple directives are diagnosed during + parsing so templates shouldn't change anything. This is of course as long + as we don't change that, and these cases should be enough to deter anyone + from doing so. */ + +template<typename> +void multiple_uses_non_dependent_directive_uninstantiated() +{ + int a; + #pragma omp allocate(a) /* { dg-note "'a' previously appeared here" } */ + #pragma omp allocate(a) /* { dg-error "'a' already appeared as list item in an 'allocate' directive" } */ +} + +template<typename> +void multiple_uses_non_dependent_directive() +{ + int a; + #pragma omp allocate(a) /* { dg-note "'a' previously appeared here" } */ + #pragma omp allocate(a) /* { dg-error "'a' already appeared as list item in an 'allocate' directive" } */ +} +template void multiple_uses_non_dependent_directive<void>(); + + +template<int Align> +void multiple_uses_dep_directive_before_align_uninstantiated() +{ + int a; + #pragma omp allocate(a) align(Align) /* { dg-note "'a' previously appeared here" } */ + #pragma omp allocate(a) /* { dg-error "'a' already appeared as list item in an 'allocate' directive" } */ +} + +template<int Align> +void multiple_uses_dep_directive_before_valid_align() +{ + int a; + #pragma omp allocate(a) align(Align) /* { dg-note "'a' previously appeared here" } */ + #pragma omp allocate(a) /* { dg-error "'a' already appeared as list item in an 'allocate' directive" } */ +} +template void multiple_uses_dep_directive_before_valid_align<32>(); + +template<int Align> +void multiple_uses_dep_directive_before_invalid_align() +{ + int a; + #pragma omp allocate(a) align(Align) /* { dg-note "'a' previously appeared here" } */ + #pragma omp allocate(a) /* { dg-error "'a' already appeared as list item in an 'allocate' directive" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-2 } */ +} +template void multiple_uses_dep_directive_before_invalid_align<42>(); + + +/* Dependent directive after the independent one. */ + +template<int Align> +void multiple_uses_dep_directive_after_align_uninstantiated() +{ + int a; + #pragma omp allocate(a) /* { dg-note "'a' previously appeared here" } */ + #pragma omp allocate(a) align(Align) /* { dg-error "'a' already appeared as list item in an 'allocate' directive" } */ +} + +template<int Align> +void multiple_uses_dep_directive_after_valid_align() +{ + int a; + #pragma omp allocate(a) /* { dg-note "'a' previously appeared here" } */ + #pragma omp allocate(a) align(Align) /* { dg-error "'a' already appeared as list item in an 'allocate' directive" } */ +} +template void multiple_uses_dep_directive_after_valid_align<32>(); + +template<int Align> +void multiple_uses_dep_directive_after_invalid_align() +{ + int a; + #pragma omp allocate(a) /* { dg-note "'a' previously appeared here" } */ + #pragma omp allocate(a) align(Align) /* { dg-error "'a' already appeared as list item in an 'allocate' directive" } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ +} +template void multiple_uses_dep_directive_after_invalid_align<42>(); + diff --git a/gcc/testsuite/g++.dg/gomp/allocate-11.C b/gcc/testsuite/g++.dg/gomp/allocate-11.C new file mode 100644 index 0000000..38638d7 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-11.C @@ -0,0 +1,50 @@ +/* { dg-do compile { target c++11 } } */ + +/* Diagnostics for rvalue reference vars used in an allocate directive. */ + +void rref_var() +{ + int&& ref = 42; /* { dg-note "'ref' declared here" } */ + #pragma omp allocate(ref) /* { dg-error "variable 'ref' with reference type may not appear as a list item in an 'allocate' directive" } */ +} + +void const_rref_var() +{ + int const&& ref = 42; /* { dg-note "'ref' declared here" } */ + #pragma omp allocate(ref) /* { dg-error "variable 'ref' with reference type may not appear as a list item in an 'allocate' directive" } */ +} + +template<typename> +void rref_var_templ_not_instantiated() +{ + int&& ref = 42; /* { dg-note "'ref' declared here" } */ + #pragma omp allocate(ref) /* { dg-error "variable 'ref' with reference type may not appear as a list item in an 'allocate' directive" } */ +} + +template<typename> +void const_rref_var_templ_not_instantiated() +{ + int const&& ref = 42; /* { dg-note "'ref' declared here" } */ + #pragma omp allocate(ref) /* { dg-error "variable 'ref' with reference type may not appear as a list item in an 'allocate' directive" } */ +} + +template<typename T> +void dependent_rref_var_templ_not_instantiated() +{ + T&& t = 42; /* { dg-note "'t' declared here" } */ + #pragma omp allocate(t) /* { dg-error "variable 't' with reference type may not appear as a list item in an 'allocate' directive" } */ +} + +template<typename T> +void dependent_var_templ() +{ + T t = 42; /* { dg-note "'t' declared here" } */ + #pragma omp allocate(t) /* { dg-error "variable 't' with reference type may not appear as a list item in an 'allocate' directive" } */ +} +void instantiate_var_templ() +{ + dependent_var_templ<int>(); /* { dg-bogus "required from here" } */ + dependent_var_templ<int&&>(); /* { dg-message "required from here" } */ + dependent_var_templ<int const&&>(); /* { dg-message "required from here" } */ +} + diff --git a/gcc/testsuite/g++.dg/gomp/allocate-12.C b/gcc/testsuite/g++.dg/gomp/allocate-12.C new file mode 100644 index 0000000..678c398 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-12.C @@ -0,0 +1,108 @@ +/* { 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 "'allocator' clause expression has type 'int' rather than '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 "'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'" "" { target *-*-* } .-1 } */ + /* { dg-error "'allocator' clause requires a predefined allocator as 'a' is static" "" { target *-*-* } .-2 } */ +} + +template<auto Alloc> +void auto_nttp_allocator_static_1() +{ + static int a; + #pragma omp allocate(a) allocator(Alloc) /* { dg-error "'allocator' clause expression has type 'int' rather than '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 predefined allocator as 'a' is static" } */ +} + +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 + +/* 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" } */ +} + +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" } */ +} + +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" } */ +} diff --git a/gcc/testsuite/g++.dg/gomp/allocate-13.C b/gcc/testsuite/g++.dg/gomp/allocate-13.C new file mode 100644 index 0000000..feec331 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-13.C @@ -0,0 +1,172 @@ +/* { dg-do compile { target c++11 } } */ +#include "allocate-allocator-handle.h" + +/* Incorrect use of lambda captures in a directive or clause. + There are a few cases in here that are impacted by the bug with implicit + constexpr functions detailed in allocate-15.C and allocate-16.C. */ + +void capture_used_in_directive() +{ + int a = 42; + auto cl = [a](){ + #pragma omp allocate(a) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + }; +} + +template<typename> +void capture_used_in_directive_templ_uninstantiated() +{ + int a = 42; + auto cl = [a](){ + #pragma omp allocate(a) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + }; +} + +template<typename> +void capture_used_in_directive_templ() +{ + int a = 42; + auto cl = [a](){ + #pragma omp allocate(a) /* { dg-error "'allocate' directive must be in the same scope as 'a'" } */ + }; +} + +void instantiate_capture_used_in_directive() +{ + capture_used_in_directive_templ<void>(); +} + + + +void capture_used_in_allocator_clause_static_var() +{ + omp_allocator_handle_t alloc = omp_default_mem_alloc; + auto cl = [alloc](){ + static int a = 42; + #pragma omp allocate(a) allocator(alloc) /* { dg-error "'allocator' clause requires a predefined allocator as 'a' is static" "" { xfail c++17 } } */ + /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" "" { target c++17 } .-1 } */ + }; +} + +/* This is similar to capture_used_in_align_clause_templ_uninstantiated below, + see below for more info. */ +template<typename> +void capture_used_in_allocator_clause_static_var_templ_uninstantiated() +{ + omp_allocator_handle_t alloc = omp_default_mem_alloc; + auto cl = [alloc](){ + static int a = 42; + #pragma omp allocate(a) allocator(alloc) /* { dg-error "'allocator' clause requires a predefined allocator as 'a' is static" "" { xfail *-*-* } } */ + /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" "" { target c++17 } .-1 } */ + }; +} + +/* It's not viable to diagnose this case, see the comment on + dependent_capture_used_in_align_clause_templ_uninstantiated below. */ +template<typename T> +void dependent_capture_used_in_allocator_clause_static_var_templ_uninstantiated() +{ + T alloc = omp_default_mem_alloc; + auto cl = [alloc](){ + static int a = 42; + #pragma omp allocate(a) allocator(alloc) /* { dg-error "'allocator' clause requires a predefined allocator as 'a' is static" "" { xfail *-*-* } } */ + /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" "" { target c++17 } .-1 } */ + }; +} + +template<typename> +void capture_used_in_allocator_clause_static_var_templ() +{ + omp_allocator_handle_t alloc = omp_default_mem_alloc; + auto cl = [alloc](){ + static int a = 42; + #pragma omp allocate(a) allocator(alloc) /* { dg-error "'allocator' clause requires a predefined allocator as 'a' is static" "" { xfail c++17 } } */ + /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" "" { target c++17 } .-1 } */ + }; +} + +template<typename T> +void dependent_capture_used_in_allocator_clause_static_var_templ() +{ + T alloc = omp_default_mem_alloc; + auto cl = [alloc](){ + static int a = 42; + #pragma omp allocate(a) allocator(alloc) /* { dg-error "'allocator' clause requires a predefined allocator as 'a' is static" "" { xfail c++17 } } */ + /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" "" { target c++17 } .-1 } */ + }; +} + +void instantiate_capture_used_in_allocator_clause_static_var() +{ + capture_used_in_allocator_clause_static_var_templ<void>(); + dependent_capture_used_in_allocator_clause_static_var_templ<omp_allocator_handle_t>(); +} + + + +void capture_used_in_align_clause() +{ + int align = 32; + auto cl = [align](){ + int a; + #pragma omp allocate(a) align(align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ + }; +} + +/* This case should be diagnosable, but we don't even try right now. Even if + we did try by checking if an expr is potential_constant_expression, it + appears to incorrectly returns true for the align clause's expression here. + We know the type is int so we know it's not an empty type, so we should be + able to know that it can't possibly be a constant expression even before + the template is instantiated. Unfortunately, as stated above, + potential_constant_expression does not agree. It's also possible this is + not a bug in potential_constant_expression and there is something I am + overlooking. */ +template<typename> +void capture_used_in_align_clause_templ_uninstantiated() +{ + int align = 32; + auto cl = [align](){ + int a; + #pragma omp allocate(a) align(align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { xfail *-*-* } } */ + }; +} + +/* Unlike the above case this case is very hard to diagnose, + potential_constant_expression correctly returns true for the align clause's + expr here. See cp/semantics.cc:finish_omp_allocate for more information. */ +template<typename T> +void dependent_capture_used_in_align_clause_templ_uninstantiated() +{ + T align = 32; + auto cl = [align](){ + int a; + #pragma omp allocate(a) align(align) /* { eg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { xfail *-*-* } } */ + }; +} + +template<typename> +void capture_used_in_align_clause_templ() +{ + int align = 32; + auto cl = [align](){ + int a; + #pragma omp allocate(a) align(align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ + }; +} + +template<typename T> +void dependent_capture_used_in_align_clause_templ() +{ + T align = 32; + auto cl = [align](){ + int a; + #pragma omp allocate(a) align(align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ + }; +} + +void instantiate_capture_used_in_align() +{ + capture_used_in_align_clause_templ<void>(); + dependent_capture_used_in_align_clause_templ<int>(); +} diff --git a/gcc/testsuite/g++.dg/gomp/allocate-14.C b/gcc/testsuite/g++.dg/gomp/allocate-14.C new file mode 100644 index 0000000..a959758 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-14.C @@ -0,0 +1,172 @@ +/* { dg-do compile { target c++17 } } */ + +/* Nested lambdas */ + +#include "allocate-allocator-handle.h" + +template<int Align> +auto lambda_00() +{ + return [](auto p0){ + return [](auto p1){ + return [](auto p2){ + return [p2](auto p3){ + int b = 42; + decltype(p3) a = b; + #pragma omp allocate(a) align(Align) allocator(p2) + return a; + }; + }; + }; + }; +} + +template<int Align> +auto lambda_01() +{ + return [](auto p0){ + return [](auto p1){ + return [](auto p2){ + return [p2](auto p3){ + int b = 42; + decltype(p3) a = b; /* { dg-message "'a' declared here" } */ + #pragma omp allocate(a) align(Align) allocator(p2) + /* { dg-error "variable 'a' with reference type may not appear as a list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-2 } */ + /* { dg-error "'allocator' clause expression has type 'const int' rather than 'omp_allocator_handle_t'" "" { target *-*-* } .-3 } */ + return a; + }; + }; + }; + }; +} + +template<int Align> +auto lambda_02() +{ + return [](auto p0){ + return [](auto p1){ + return [](auto p2){ + return [p2](auto p3){ + int b = 42; + decltype(p3) a = b; + #pragma omp allocate(a) align(Align) allocator(p2) + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-1 } */ + return a; + }; + }; + }; + }; +} + +void instantiate_lambdas_0() +{ + /* valid */ + auto c00 = lambda_00<32>(); + auto c01 = c00(0); + auto c02 = c01(0); + auto c03 = c02(omp_default_mem_alloc); + c03(0); + /* invalid */ + auto c10 = lambda_01<30>(); /* { dg-message "required from here" } */ + auto c11 = c10(0); /* { dg-bogus "required from here" } */ + auto c12 = c11(0); /* { dg-bogus "required from here" } */ + auto c13 = c12(0); /* { dg-message "required from here" } */ + int a = 0; + c13.operator()<int&>(a); /* { dg-message "required from here" } */ + /* partially instantiated (invalid) */ + auto c20 = lambda_02<30>(); /* { dg-message "required from here" } */ +} + + + +template<int Align> +auto lambda_10() +{ + return [](auto p0){ + int a = 42; + #pragma omp allocate(a) align(Align) + return [](auto p1){ + int a = 42; + #pragma omp allocate(a) align(Align) + return [](auto p2){ + int a = 42; + #pragma omp allocate(a) align(Align) + return [p2](auto p3){ + int b = 42; + decltype(p3) a = b; + #pragma omp allocate(a) align(Align) allocator(p2) + return a; + }; + }; + }; + }; +} + +template<int Align> +auto lambda_11() +{ + return [](auto p0){ + int a = 42; + #pragma omp allocate(a) align(Align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ + return [](auto p1){ + int a = 42; + #pragma omp allocate(a) align(Align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ + return [](auto p2){ + int a = 42; + #pragma omp allocate(a) align(Align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ + return [p2](auto p3){ + int b = 42; + decltype(p3) a = b; /* { dg-message "'a' declared here" } */ + #pragma omp allocate(a) align(Align) allocator(p2) + /* { dg-error "variable 'a' with reference type may not appear as a list item in an 'allocate' directive" "" { target *-*-* } .-1 } */ + /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" "" { target *-*-* } .-2 } */ + /* { dg-error "'allocator' clause expression has type 'const int' rather than 'omp_allocator_handle_t'" "" { target *-*-* } .-3 } */ + return a; + }; + }; + }; + }; +} + +template<int Align> +auto lambda_12() +{ + return [](auto p0){ + int a = 42; + #pragma omp allocate(a) align(Align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ + return [](auto p1){ + int a = 42; + #pragma omp allocate(a) align(Align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ + return [](auto p2){ + int a = 42; + #pragma omp allocate(a) align(Align) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ + return [p2](auto p3){ + int b = 42; + decltype(p3) a = b; + #pragma omp allocate(a) align(Align) allocator(p2) /* { dg-error "'align' clause argument needs to be positive constant power of two integer expression" } */ + return a; + }; + }; + }; + }; +} + +void instantiate_lambdas_1() +{ + /* valid */ + auto c00 = lambda_10<32>(); + auto c01 = c00(0); + auto c02 = c01(0); + auto c03 = c02(omp_default_mem_alloc); + c03(0); + /* invalid */ + auto c10 = lambda_11<30>(); /* { dg-message "required from here" } */ + auto c11 = c10(0); /* { dg-bogus "required from here" } */ + auto c12 = c11(0); /* { dg-bogus "required from here" } */ + auto c13 = c12(0); /* { dg-message "required from here" } */ + int a = 0; + c13.operator()<int&>(a); /* { dg-message "required from here" } */ + /* partially instantiated (invalid) */ + auto c20 = lambda_12<30>(); /* { dg-message "required from here" } */ +} diff --git a/gcc/testsuite/g++.dg/gomp/allocate-15.C b/gcc/testsuite/g++.dg/gomp/allocate-15.C new file mode 100644 index 0000000..1f86bc42 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-15.C @@ -0,0 +1,142 @@ +/* { dg-do compile { target c++17 } } */ +#include "allocate-allocator-handle.h" + +/* Check application of align clause to static variables used in an OpenMP + allocate directive in functions that are implicitly constexpr. + Lambdas, lambdas in function templates. + Missing cases for generics lambdas, see below. + See allocate-16.C for cases in inline functions/function templates with + -fimplicit-constexpr. + + This test case is valid in c++11 but the bug we are testing for does not + manifest until c++17, which makes lambdas implicit constexpr functions. + + For now, we simply do not support these cases. */ + + + +/* These cases had problems in c++17 and up because of dubious handling of + constexpr functions in cp/decl.cc:make_rtl_for_nonlocal_decl. In c++17 and + up lambdas are implicitly constexpr, and are always considered potentially + constexpr functions. I think this is dubious as the constexpr flag does get + set for a lambdas call operator, it also causes odd bugs where a lambda + can be used in a constant expression, even when it should not be able to. + But I digress, there is another test case for that and that oddity is not + the root of this problem. + + Usually, make_rtl_for_nonlocal_decl defers compilation of local static + variables until later, except when a function is declared constexpr. From + looking at why this code was originally added, I feel like it was a heavy + handed fix for the original problem as it should have just made sure + __func__, __PRETTY_FUNC__, and other static variables of that kind processed + differently. I would imagine that the thought process behind it went + something like "well, static local variables aren't allowed in constexpr + functions anyway, so this isn't a big deal" and I can understand this + rationale. With that said, __func__ and it's counterparts do not seem to be + processed in make_rtl_for_nonlocal_decl anymore, so the added handling seems + to be irrelevant now at best. + + Unfortunately, as stated in the first paragraph, because lambdas are + unconditionally considered constexpr in c++17 and up, local static variables + in lambdas are never deferred. As far as I can tell, this has the + consequence that they are always emitted regardless of whether they are used + or not. This really isn't that big a deal in the grand scheme of things, + and it may very well be that they get pruned during LTO so the impact of + this is probably slim to none. + + However, as soon as OpenMP allocate directives come into play, the problems + are much more pronounced. In non templates, early finalization occurs + before we even parse the allocate directive, before the "omp allocate" + attribute is added to the var decl. Consequentially, the specified + alignment in the align clause of the directive does not get applied to the + variable. In function templates the consequences are much worse, parsing + the template decl adds an incomplete "omp allocate" attribute to the var + decl as a marker in an incomplete state. During instantiation of the + template, early finalization of the decl occurs, before we have substituted + into the allocate directive and finalized the attribute on the var decl. + In this case, varpool_node::finalize_decl finds the "omp allocate" attribute + and tries to read an alignment from it, which has not been stored yet. + At best we crash here, at worst it reads garbage as an OMP_CLAUSE is stored + there for diagnostic purposes. + + This also occurs for inline functions with -fimplicit-constexpr, but as + noted above those tests are split out to allocate-16.C. */ + + + +/* Making a regex for demangled identifiers is actually way harder than making + a regex for mangled ones, too many escapes are needed. */ + +/* { dg-final { scan-assembler "\.align 256\\s*\.type\\s*_ZZZ6f0_256vENKUlvE_clEvE1a" } } */ +int* f0_256() +{ + auto cl = [](){ + static int a = 42; + #pragma omp allocate(a) align(256) allocator(omp_default_mem_alloc) /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" } */ + return &a; + }; + return cl(); +} +/* { dg-final { scan-assembler "\.align 512\\s*\.type\\s*_ZZZ6f0_512vENKUlvE_clEvE1a" } } */ +int* f0_512() +{ + auto cl = [](){ + static int a = 42; + #pragma omp allocate(a) align(512) allocator(omp_default_mem_alloc) /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" } */ + return &a; + }; + return cl(); +} +/* { dg-final { scan-assembler "\.align 1024\\s*\.type\\s*_ZZZ7f0_1024vENKUlvE_clEvE1a" } } */ +int* f0_1024() +{ + auto cl = [](){ + static int a = 42; + #pragma omp allocate(a) align(1024) allocator(omp_default_mem_alloc) /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" } */ + return &a; + }; + return cl(); +} + +/* { dg-final { scan-assembler "\.align 256\\s*\.type\\s*_ZZZ6f1_256IvEPivENKUlvE_clEvE1a" } } */ +template<typename> +int* f1_256() +{ + auto cl = [](){ + static int a = 42; + #pragma omp allocate(a) align(256) allocator(omp_default_mem_alloc) /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" } */ + return &a; + }; + return cl(); +} +template int* f1_256<void>(); + +/* { dg-final { scan-assembler "\.align 512\\s*\.type\\s*_ZZZ6f1_512IvEPivENKUlvE_clEvE1a" } } */ +template<typename> +int* f1_512() +{ + auto cl = [](){ + static int a = 42; + #pragma omp allocate(a) align(512) allocator(omp_default_mem_alloc) /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" } */ + return &a; + }; + return cl(); +} +template int* f1_512<void>(); + +/* { dg-final { scan-assembler "\.align 1024\\s*\.type\\s*_ZZZ7f1_1024IvEPivENKUlvE_clEvE1a" } } */ +template<typename> +int* f1_1024() +{ + auto cl = [](){ + static int a = 42; + #pragma omp allocate(a) align(1024) allocator(omp_default_mem_alloc) /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" } */ + return &a; + }; + return cl(); +} +template int* f1_1024<void>(); + +/* Missing cases for generic lambda, and generic lambda in function template. + They shouldn't behave differently, but for completeness they should be + added, I'm just not going to spend any more time on this right now. */ diff --git a/gcc/testsuite/g++.dg/gomp/allocate-16.C b/gcc/testsuite/g++.dg/gomp/allocate-16.C new file mode 100644 index 0000000..869b0e5 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-16.C @@ -0,0 +1,75 @@ +/* { dg-do compile { target c++14 } } */ +/* { dg-additional-options "-fimplicit-constexpr" } */ +#include "allocate-allocator-handle.h" + +/* Check application of align clause to static variables used in an OpenMP + allocate directive in functions that are implicitly constexpr. + Inline functions and function templates with -fimplicit-constexpr. + + See allocate-15.C for more information, and cases using lambdas. + + For now, we simply do not support these cases. */ + +/* Making a regex for demangled identifiers is actually way harder than making + a regex for mangled ones, too many escapes are needed. + + We need to ODR-use the regular functions to force them to be emitted. */ + +/* { dg-final { scan-assembler "\.align 256\\s*\.type\\s*_ZZ6f0_256vE1a" } } */ +inline int* f0_256() +{ + static int a = 42; + #pragma omp allocate(a) align(256) allocator(omp_default_mem_alloc) /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" } */ + return &a; +} +constexpr int*(*odr_use_f0_256)() = &f0_256; + +/* { dg-final { scan-assembler "\.align 512\\s*\.type\\s*_ZZ6f0_512vE1a" } } */ +inline int* f0_512() +{ + static int a = 42; + #pragma omp allocate(a) align(512) allocator(omp_default_mem_alloc) /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" } */ + return &a; +} +constexpr int*(*odr_use_f0_512)() = &f0_512; + +/* { dg-final { scan-assembler "\.align 1024\\s*\.type\\s*_ZZ7f0_1024vE1a" } } */ +inline int* f0_1024() +{ + static int a = 42; + #pragma omp allocate(a) align(1024) allocator(omp_default_mem_alloc) /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" } */ + return &a; +} +constexpr int*(*odr_use_f0_1024)() = &f0_1024; + + + +/* { dg-final { scan-assembler "\.align 256\\s*\.type\\s*_ZZ6f1_256IvEPivE1a" } } */ +template<typename> +inline int* f1_256() +{ + static int a = 42; + #pragma omp allocate(a) align(256) allocator(omp_default_mem_alloc) /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" } */ + return &a; +} +template int* f1_256<void>(); + +/* { dg-final { scan-assembler "\.align 512\\s*\.type\\s*_ZZ6f1_512IvEPivE1a" } } */ +template<typename> +inline int* f1_512() +{ + static int a = 42; + #pragma omp allocate(a) align(512) allocator(omp_default_mem_alloc) /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" } */ + return &a; +} +template int* f1_512<void>(); + +/* { dg-final { scan-assembler "\.align 1024\\s*\.type\\s*_ZZ7f1_1024IvEPivE1a" } } */ +template<typename> +inline int* f1_1024() +{ + static int a = 42; + #pragma omp allocate(a) align(1024) allocator(omp_default_mem_alloc) /* { dg-message "static variable 'a' is not supported in an 'allocate' directive in an implicit constexpr function" } */ + return &a; +} +template int* f1_1024<void>(); diff --git a/gcc/testsuite/g++.dg/gomp/allocate-17.C b/gcc/testsuite/g++.dg/gomp/allocate-17.C new file mode 100644 index 0000000..ca13845 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-17.C @@ -0,0 +1,69 @@ +/* { dg-do compile { target c++17 } } */ + +/* OpenMP allocate directive in constant expressions where execution does not + pass through the allocation of the variable in the directive. + Lambdas, both implicit and explicit constexpr, + in a function and in a function template. + + These cases will be valid if/when OpenMP relaxes restrictions on directives + in constexpr functions. It might make sense to only allow this behavior in + c++23 though. + + Constexpr lambdas are only permitted in c++17, it doesn't make sense to test + anything prior than that. */ + +void do_constexpr_lambda() +{ + auto cl = [](bool b) constexpr { + if (b) + return 42; + int a = 42; + #pragma omp allocate(a) /* { dg-error "OpenMP directives may not appear in 'constexpr' functions" } */ + return a; + }; + constexpr int v = cl(true); /* { dg-error "'do_constexpr_lambda\\\(\\\)::<lambda\\\(bool\\\)>' called in a constant expression" "" { xfail *-*-* } } */ +} + +void do_lambda() +{ + auto cl = [](bool b){ + if (b) + return 42; + int a = 42; + #pragma omp allocate(a) /* { dg-error "OpenMP directives may not appear in 'constexpr' functions" "" { xfail *-*-* } } */ + return a; + }; + constexpr int v = cl(true); /* { dg-error "'do_lambda\\\(\\\)::<lambda\\\(bool\\\)>' called in a constant expression" "" { xfail *-*-* } } */ +} + +template<typename> +void templ_do_constexpr_lambda() +{ + auto cl = [](bool b) constexpr { + if (b) + return 42; + int a = 42; + #pragma omp allocate(a) /* { dg-error "OpenMP directives may not appear in 'constexpr' functions" } */ + return a; + }; + constexpr int v = cl(true); /* { dg-error "'templ_do_constexpr_lambda<void>\\\(\\\)::<lambda\\\(bool\\\)>' called in a constant expression" "" { xfail *-*-* } } */ +} +template void templ_do_constexpr_lambda<void>(); + +template<typename> +void templ_do_lambda() +{ + auto cl = [](bool b){ + if (b) + return 42; + int a = 42; + #pragma omp allocate(a) /* { dg-error "OpenMP directives may not appear in 'constexpr' functions" "" { xfail *-*-* } } */ + return a; + }; + constexpr int v = cl(true); /* { dg-error "'templ_do_lambda<void>\\\(\\\)::<lambda\\\(bool\\\)>' called in a constant expression" "" { xfail *-*-* } } */ +} +template void templ_do_lambda<void>(); + +/* Missing cases for generic lambda, and generic lambda in function template. + They shouldn't behave differently, but for completeness they should be + added, I'm just not going to spend any more time on this right now. */ diff --git a/gcc/testsuite/g++.dg/gomp/allocate-18.C b/gcc/testsuite/g++.dg/gomp/allocate-18.C new file mode 100644 index 0000000..3c8ca20 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-18.C @@ -0,0 +1,60 @@ +/* { dg-do compile { target c++14 } } */ +/* { dg-additional-options "-fimplicit-constexpr" } */ + +/* OpenMP allocate directive in constant expressions where execution does not + pass through the allocation of the variable in the directive. + Regular functions and function templates, + constexpr and inline with -fimplicit-constexpr. + + These cases will be valid if/when OpenMP relaxes restrictions on directives + in constexpr functions. It might make sense to only allow this behavior in + c++23 though. + + It doesn't make sense to test these cases in c++11 as constexpr functions + are far more limited, and are diagnosed completely differently. + + Even though -fimplicit-constexpr is an extension, its behavior is similar to + lambdas in c++17, so I am including tests for it. + See allocate-17.C for test cases involving lambdas. */ + +constexpr int f_constexpr(bool b) +{ + if (b) + return 42; + int a = 42; + #pragma omp allocate(a) /* { dg-error "OpenMP directives may not appear in 'constexpr' functions" } */ + return a; +} +constexpr int g0 = f_constexpr(true); /* { dg-error "'constexpr int f_constexpr\\\(bool\\\)' called in a constant expression" "" { xfail *-*-* } } */ + +template<typename> +constexpr int f_constexpr_templ(bool b) +{ + if (b) + return 42; + int a = 42; + #pragma omp allocate(a) /* { dg-error "OpenMP directives may not appear in 'constexpr' functions" } */ + return a; +} +constexpr int g1 = f_constexpr_templ<void>(true); /* { dg-error "'constexpr int f_constexpr_templ\\\(bool\\\) \\\[with <template-parameter-1-1> = void\\\]' called in a constant expression" "" { xfail *-*-* } } */ + +inline int f_inline(bool b) +{ + if (b) + return 42; + int a = 42; + #pragma omp allocate(a) /* { dg-error "OpenMP directives may not appear in 'constexpr' functions" "" { xfail *-*-* } } */ + return a; +} +constexpr int g2 = f_inline(true); /* { dg-error "'int f_inline\\\(bool\\\)' called in a constant expression" "" { xfail *-*-* } } */ + +template<typename> +inline int f_inline_templ(bool b) +{ + if (b) + return 42; + int a = 42; + #pragma omp allocate(a) /* { dg-error "OpenMP directives may not appear in 'constexpr' functions" "" { xfail *-*-* } } */ + return a; +} +constexpr int g3 = f_inline_templ<void>(true); /* { dg-error "'int f_inline_templ\\\(bool\\\) \\\[with <template-parameter-1-1> = void\\\]' called in a constant expression" "" { xfail *-*-* } } */ diff --git a/gcc/testsuite/g++.dg/gomp/allocate-19.C b/gcc/testsuite/g++.dg/gomp/allocate-19.C new file mode 100644 index 0000000..de726ec --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-19.C @@ -0,0 +1,27 @@ +/* This used to ICE due to NRVO being attempted on 's', NRVO can not be + done on a variable used in an allocate directive. */ + +/* NRVO probably kicks in when sizeof(T) is greater than 16. */ +struct S { + char _v[17]; +}; + +S f0() +{ + S s; + #pragma omp allocate(s) + return s; +} + +/* Also test with a VERY LARGE type just in case the above isn't big enough + to trigger NRVO in all cases. */ +struct Big { + char _v[4096]; +}; + +Big f1() +{ + Big b; + #pragma omp allocate(b) + return b; +} diff --git a/gcc/testsuite/g++.dg/gomp/allocate-20.C b/gcc/testsuite/g++.dg/gomp/allocate-20.C new file mode 100644 index 0000000..d9bc1de --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-20.C @@ -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>(); + diff --git a/gcc/testsuite/g++.dg/gomp/allocate-21.C b/gcc/testsuite/g++.dg/gomp/allocate-21.C new file mode 100644 index 0000000..576d516 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-21.C @@ -0,0 +1,26 @@ +/* { dg-do compile { target c++11 } } */ + +/* Dependent function calls in a clause. */ + +/* Not constexpr, invalid value. */ +template<typename T> +int get_align(T) { return 42; } + +template<typename T> +void f() +{ + int a = 42; + #pragma omp allocate(a) align(get_align(T{})) +} + +namespace foo +{ + struct S {}; + + constexpr int get_align(S) { return 32; } +} + +void instantiate() +{ + f<foo::S>(); +} diff --git a/gcc/testsuite/g++.dg/gomp/allocate-5.C b/gcc/testsuite/g++.dg/gomp/allocate-5.C new file mode 100644 index 0000000..af28388 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-5.C @@ -0,0 +1,321 @@ +#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. */ + +struct S0 { + int _v; + S0(int v) : _v(v) {} + operator int() const { return 42; } +}; + +struct S1 { + int _v[32]; + 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; +} + +#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) + +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; +} + +#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) + +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; +} + +#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>(); \ + } while (false) + +void instantiate_nttp_align_tests() +{ + INSTANTIATE_ALL_WITH_V(1); + INSTANTIATE_ALL_WITH_V(2); + INSTANTIATE_ALL_WITH_V(4); + 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; +} + +#define INSTANTIATE_ALL_WITH_V(value) \ + do { \ + int v0 = nttp_alloc<value>(); \ + int v1 = nttp_alloc_align<value>(); \ + } while (false) + +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 for alloc param */ diff --git a/gcc/testsuite/g++.dg/gomp/allocate-6.C b/gcc/testsuite/g++.dg/gomp/allocate-6.C new file mode 100644 index 0000000..d33b7d5 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-6.C @@ -0,0 +1,391 @@ +/* { dg-do compile { target c++11 } } */ +/* { 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[32]; + 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. */ + diff --git a/gcc/testsuite/g++.dg/gomp/allocate-7.C b/gcc/testsuite/g++.dg/gomp/allocate-7.C new file mode 100644 index 0000000..012b52b --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-7.C @@ -0,0 +1,99 @@ +/* { dg-do compile { target c++20 } } */ + +#include "allocate-allocator-handle.h" + +/* C++20 tests */ + +struct S0 { + int _v; + S0(int v) : _v(v) {} + operator int() const { return 42; } +}; + +struct S1 { + int _v[32]; + S1(int v) : _v{v, v, v, v, v, v, v, v, + v, v, v, v, v, v, v, v, + v, v, v, v, v, v, v, v, + v, v, v, v, 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; +} + +#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) + +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 diff --git a/gcc/testsuite/g++.dg/gomp/allocate-8.C b/gcc/testsuite/g++.dg/gomp/allocate-8.C new file mode 100644 index 0000000..b5d06b0 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-8.C @@ -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 200 + +/* 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>{}); +} diff --git a/gcc/testsuite/g++.dg/gomp/allocate-9.C b/gcc/testsuite/g++.dg/gomp/allocate-9.C new file mode 100644 index 0000000..a2b7fb0 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-9.C @@ -0,0 +1,45 @@ +#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-8.C (update GOMP_OMP_PREDEF_ALLOC_MAX 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 200 + +int g0 = 42; /* { dg-note "'g0' declared here" }*/ +#pragma omp allocate(g0) allocator(omp_null_allocator) +/* { dg-error "'allocator' clause requires a predefined allocator as 'g0' is static" "" { target *-*-* } .-1 } */ +int g1 = 42; /* { dg-note "'g1' declared here" }*/ +#pragma omp allocate(g1) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMP_PREDEF_ALLOC_MAX + 1)) +/* { dg-error "'allocator' clause requires a predefined allocator as 'g1' is static" "If this test fails because of added predefined allocators please ensure everything is updated accordingly, see this test case for more information" { target *-*-* } .-1 } */ +int g2 = 42; /* { dg-note "'g2' declared here" }*/ +#pragma omp allocate(g2) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMPX_PREDEF_ALLOC_MIN - 1)) +/* { dg-error "'allocator' clause requires a predefined allocator as 'g2' is static" "" { target *-*-* } .-1 } */ +int g3 = 42; /* { dg-note "'g3' declared here" }*/ +#pragma omp allocate(g3) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMPX_PREDEF_ALLOC_MAX + 1)) +/* { dg-error "'allocator' clause requires a predefined allocator as 'g3' is static" "If this test fails because of added predefined allocators please ensure everything is updated accordingly, see this test case for more information" { target *-*-* } .-1 } */ + +void test_predefined_allocs() +{ + static int a0 = 42; /* { dg-note "'a0' declared here" }*/ + #pragma omp allocate(a0) allocator(omp_null_allocator) + /* { dg-error "'allocator' clause requires a predefined allocator as 'a0' is static" "" { target *-*-* } .-1 } */ + static int a1 = 42; /* { dg-note "'a1' declared here" }*/ + #pragma omp allocate(a1) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMP_PREDEF_ALLOC_MAX + 1)) + /* { dg-error "'allocator' clause requires a predefined allocator as 'a1' is static" "If this test fails because of added predefined allocators please ensure everything is updated accordingly, see this test case for more information" { target *-*-* } .-1 } */ + static int a2 = 42; /* { dg-note "'a2' declared here" }*/ + #pragma omp allocate(a2) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMPX_PREDEF_ALLOC_MIN - 1)) + /* { dg-error "'allocator' clause requires a predefined allocator as 'a2' is static" "" { target *-*-* } .-1 } */ + static int a3 = 42; /* { dg-note "'a3' declared here" }*/ + #pragma omp allocate(a3) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMPX_PREDEF_ALLOC_MAX + 1)) + /* { dg-error "'allocator' clause requires a predefined allocator as 'a3' is static" "If this test fails because of added predefined allocators please ensure everything is updated accordingly, see this test case for more information" { target *-*-* } .-1 } */ +} diff --git a/gcc/testsuite/g++.dg/gomp/allocate-allocator-handle.h b/gcc/testsuite/g++.dg/gomp/allocate-allocator-handle.h new file mode 100644 index 0000000..3576867 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/allocate-allocator-handle.h @@ -0,0 +1,16 @@ +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; |