aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/ChangeLog26
-rw-r--r--gcc/cp/ChangeLog.omp332
-rw-r--r--gcc/cp/constexpr.cc25
-rw-r--r--gcc/cp/cp-gimplify.cc6
-rw-r--r--gcc/cp/cp-objcp-common.cc1
-rw-r--r--gcc/cp/cp-objcp-common.h9
-rw-r--r--gcc/cp/cp-tree.def12
-rw-r--r--gcc/cp/cp-tree.h56
-rw-r--r--gcc/cp/decl.cc410
-rw-r--r--gcc/cp/decl2.cc26
-rw-r--r--gcc/cp/error.cc30
-rw-r--r--gcc/cp/mangle.cc1
-rw-r--r--gcc/cp/operators.def1
-rw-r--r--gcc/cp/parser.cc2646
-rw-r--r--gcc/cp/parser.h12
-rw-r--r--gcc/cp/pt.cc285
-rw-r--r--gcc/cp/semantics.cc1800
-rw-r--r--gcc/cp/typeck.cc21
18 files changed, 5207 insertions, 492 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 59bc179..8983abf 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,29 @@
+2025-05-30 Sandra Loosemore <sloosemore@baylibre.com>
+
+ Backported from master:
+ 2025-05-29 Sandra Loosemore <sloosemore@baylibre.com>
+
+ * parser.cc (cp_parser_omp_metadirective): Do not call
+ cp_parser_skip_to_end_of_block_or_statement after an error.
+
+2025-05-30 Sandra Loosemore <sloosemore@baylibre.com>
+
+ Backported from master:
+ 2025-05-29 Sandra Loosemore <sloosemore@baylibre.com>
+
+ PR c/120180
+ * parser.cc (cp_parser_omp_metadirective): Only consume the
+ token if it is the expected close paren.
+
+2025-05-26 Tobias Burnus <tburnus@baylibre.com>
+
+ Backported from master:
+ 2025-05-26 Tobias Burnus <tburnus@baylibre.com>
+
+ PR c++/120413
+ * semantics.cc (finish_omp_target_clauses_r): Handle
+ BIND_EXPR with empty BIND_EXPR_BLOCK.
+
2025-05-23 Nathaniel Shead <nathanieloshead@gmail.com>
Backported from master:
diff --git a/gcc/cp/ChangeLog.omp b/gcc/cp/ChangeLog.omp
new file mode 100644
index 0000000..3f2574a
--- /dev/null
+++ b/gcc/cp/ChangeLog.omp
@@ -0,0 +1,332 @@
+2025-05-15 Sandra Loosemore <sloosemore@baylibre.com>
+ Tobias Burnus <tburnus@baylibre.com>
+
+ * parser.cc (cp_finish_omp_declare_variant): Adjust error messages.
+
+2025-05-15 waffl3x <waffl3x@baylibre.com>
+
+ PR c++/119659
+ PR c++/118859
+ PR c++/119601
+ PR c++/119602
+ PR c++/119775
+ PR c++/119659
+ PR c++/118859
+ PR c++/119601
+ PR c++/119602
+ PR c++/119775
+ * cp-tree.h (finish_omp_parm_list): New declaration.
+ (finish_omp_adjust_args): New declaration.
+ * decl.cc (omp_declare_variant_finalize_one): Refactor and change
+ attribute unpacking, use finish_omp_parm_list and
+ finish_omp_adjust_args, refactor append_args diagnostics, add
+ nbase_parms to append_args attribute, remove special handling for
+ member functions.
+ * parser.cc (cp_parser_direct_declarator): Don't pass parms.
+ (cp_parser_late_return_type_opt): Remove parms parameter.
+ (cp_parser_omp_parm_list): New function.
+ (cp_finish_omp_declare_variant): Remove parms parameter.
+ Add NULL_TREE instead of nbase_args to append_args_tree. Refactor,
+ use cp_parser_omp_parm_list not cp_parser_omp_var_list_no_open,
+ handle "need_device_addr" and remove handling and diagnostics of
+ parm list arguments that are done too early. Change format of
+ unnamed variant attribute.
+ (cp_parser_late_parsing_omp_declare_simd): Remove parms parameter.
+ * pt.cc (tsubst_attribute): Copy "omp declare variant base" nodes,
+ substitute parm list numeric range bounds.
+ * semantics.cc (finish_omp_parm_list): New function.
+ (finish_omp_adjust_args): New function.
+
+2025-05-15 Kwok Cheung Yeung <kcyeung@baylibre.com>
+ Andrew Stubbs <ams@baylibre.com>
+
+ * parser.cc (cp_parser_omp_clause_map): Apply iterator to push and
+ pop mapper clauses.
+ * semantics.cc (cxx_omp_map_array_section): Allow array types for
+ base type of array sections.
+
+2025-05-15 Kwok Cheung Yeung <kcyeung@baylibre.com>
+
+ * parser.cc (cp_parser_omp_iterators): Use macros for accessing
+ iterator elements.
+ (cp_parser_omp_clause_affinity): Likewise.
+ (cp_parser_omp_clause_depend): Likewise.
+ (cp_parser_omp_clause_from_to): Likewise.
+ (cp_parser_omp_clause_map): Likewise.
+ * semantics.cc (cp_omp_finish_iterators): Likewise.
+
+2025-05-15 Kwok Cheung Yeung <kcyeung@baylibre.com>
+
+ * semantics.cc (handle_omp_array_sections): Add extra argument. Set
+ argument to true if array section has a stride that is not one.
+ (finish_omp_clauses): Disable strided updates when iterators are
+ used in the clause. Emit sorry if strided.
+
+2025-05-15 Kwok Cheung Yeung <kcyeung@baylibre.com>
+
+ * parser.cc (cp_parser_omp_clause_from_to): Parse 'iterator' modifier.
+ * semantics.cc (finish_omp_clauses): Finish iterators for to/from
+ clauses.
+
+2025-05-15 Kwok Cheung Yeung <kcyeung@baylibre.com>
+ Andrew Stubbs <ams@baylibre.com>
+
+ * parser.cc (cp_parser_omp_clause_map): Parse 'iterator' modifier.
+ * semantics.cc (finish_omp_clauses): Finish iterators. Apply
+ iterators to generated clauses.
+
+2025-05-15 Kwok Cheung Yeung <kcyeung@baylibre.com>
+
+ * pt.cc (tsubst_omp_clause_decl): Use OMP_ITERATOR_DECL_P.
+ * semantics.cc (handle_omp_array_sections): Likewise.
+ (finish_omp_clauses): Likewise.
+
+2025-05-15 Chung-Lin Tang <cltang@baylibre.com>
+
+ * parser.cc (cp_parser_omp_var_list_no_open): Adjust parsing of opening
+ square bracket.
+ (cp_parser_omp_clause_reduction): Adjustments for
+ OpenACC-specific cases.
+ * semantics.cc (cp_oacc_reduction_defined_type_p): New function.
+ (cp_oacc_reduction_code_name): Likewise.
+ (finish_omp_reduction_clause): Handle OpenACC cases using new
+ functions.
+
+2025-05-15 Sandra Loosemore <sloosemore@baylibre.com>
+ Julian Brown <julian@codesourcery.com>
+ waffl3x <waffl3x@baylibre.com>
+
+ * cp-tree.h (struct cp_omp_declare_variant_attr): New.
+ (struct saved_scope): Add omp_declare_variant_attribute field.
+ * decl.cc (omp_declare_variant_finalize_one): Add logic to inject
+ "this" parameter for method calls.
+ * parser.cc (cp_parser_skip_to_pragma_omp_end_declare_variant): New.
+ (omp_start_variant_function): New.
+ (omp_finish_variant_function): New.
+ (cp_parser_init_declarator): Handle variant functions.
+ (cp_parser_class_specifier): Handle deferred lookup of base functions
+ when the entire class has been seen.
+ (cp_parser_member_declaration): Handle variant functions.
+ (cp_finish_omp_declare_variant): Merge context selectors if in
+ a "begin declare variant" block.
+ (cp_parser_omp_begin): Match "omp begin declare variant". Adjust
+ error messages.
+ (cp_parser_omp_end): Match "omp end declare variant".
+ * parser.h (struct cp_parser): Add omp_unregistered_variants field.
+ * semantics.cc (finish_translation_unit): Detect unmatched
+ "omp begin declare variant".
+
+2025-05-15 waffl3x <waffl3x@baylibre.com>
+ Tobias Burnus <tobias@codesourcery.com>
+
+ * 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.
+
+2025-05-15 Julian Brown <julian@codesourcery.com>
+ Andrew Stubbs <ams@baylibre.com>
+ Kwok Cheung Yeung <kcyeung@baylibre.com>
+ Sandra Loosemore <sloosemore@baylibre.com>
+
+ * parser.cc (cp_parser_omp_var_list_no_open): Support array-shaping
+ operator in 'declare mapper' definitions.
+ (cp_parser_omp_clause_from_to): Add parsing for mapper modifier.
+ (cp_parser_omp_clause_map): Pass C_ORT_OMP_DECLARE_MAPPER to
+ cp_parser_omp_var_list_no_open in mapper definitions.
+ (cp_parser_omp_target_update): Instantiate mappers.
+
+2025-05-15 Julian Brown <julian@codesourcery.com>
+
+ * cp-objcp-common.cc (cp_common_init_ts): Add array-shape cast
+ support.
+ * cp-tree.def (OMP_ARRAYSHAPE_CAST_EXPR): Add tree code.
+ * cp-tree.h (DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST): Add flag.
+ (cp_omp_create_arrayshape_type, cp_build_omp_arrayshape_cast): Add
+ prototypes.
+ (grok_omp_array_section, build_omp_array_section): Add stride
+ parameters.
+ * decl.cc (create_anon_array_type): New function.
+ (cp_omp_create_arrayshape_type): New function.
+ * decl2.cc (grok_omp_array_section): Add stride parameter.
+ (min_vis_expr_r): Add OMP_ARRAYSHAPE_CAST_EXPR support.
+ * error.cc (dump_expr): Add stride support for OMP_ARRAY_SECTION.
+ * mangle.cc (write_expression): Add OMP_ARRAYSHAPE_CAST_EXPR support.
+ * operators.def (OMP_ARRAYSHAPE_CAST_EXPR): Add.
+ * parser.cc (cp_parser_new): Initialise omp_array_shaping_op_p and
+ omp_has_array_shape_p fields.
+ (cp_parser_statement_expr): Don't allow array shaping op in statement
+ exprs.
+ (cp_parser_postfix_open_square_expression): Add stride parsing for
+ array sections. Use array section code to represent array refs if we
+ have an array-shaping operator.
+ (cp_parser_parenthesized_expression_list): Don't allow array-shaping
+ op here.
+ (cp_parser_cast_expression): Add array-shaping operator parsing.
+ (cp_parser_lambda_expression): Don't allow array-shaping op in lambda
+ body.
+ (cp_parser_braced_list): Don't allow array-shaping op in braced list.
+ (struct omp_dim): Add stride field.
+ (cp_parser_var_list_no_open): Add stride/array shape support.
+ (cp_parser_omp_target_update): Handle noncontiguous updates.
+ * parser.h (cp_parser): Add omp_array_shaping_op_p and
+ omp_has_array_shape_p fields.
+ * pt.cc (tsubst): Add array-shape cast support.
+ (tsubst_copy, tsubst_copy_and_build): Likewise. Add stride support for
+ OMP_ARRAY_SECTION.
+ (tsubst_omp_clause_decl): Add stride support for OMP_ARRAY_SECTION.
+ * semantics.cc (handle_omp_array_sections_1): Add DISCONTIGUOUS
+ parameter and stride support.
+ (omp_array_section_low_bound): New function.
+ (handle_omp_array_sections): Add DISCONTIGUOUS parameter and stride
+ support.
+ (finish_omp_clauses): Update calls to handle_omp_array_sections, and
+ add noncontiguous array update support.
+ (cp_build_omp_arrayshape_cast): New function.
+ * typeck.cc (structural_comptypes): Add array-shape cast support.
+ (build_omp_array_section): Add stride parameter.
+ (check_for_casting_away_constness): Add OMP_ARRAYSHAPE_CAST_EXPR
+ support.
+
+2025-05-15 Julian Brown <julian@codesourcery.com>
+
+ * semantics.cc (handle_omp_array_sections): Pass pointer to clause
+ instead of clause. Add PNEXT return parameter for next clause in list
+ to process.
+ (finish_omp_clauses): Update calls to handle_omp_array_sections.
+ Handle cases where initial clause might be replaced.
+
+2025-05-15 Julian Brown <julian@codesourcery.com>
+
+ * parser.cc (cp_parser_omp_target_data): Instantiate mappers for 'omp
+ target data'.
+ (cp_parser_omp_target_enter_data): Instantiate mappers for 'omp target
+ enter data'.
+ (cp_parser_omp_target_exit_data): Instantiate mappers for 'omp target
+ exit data'.
+ (cp_parser_omp_target): Add c_omp_region_type argument to
+ c_omp_instantiate_mappers call.
+ * pt.cc (tsubst_omp_clauses): Instantiate mappers for OMP regions other
+ than just C_ORT_OMP_TARGET.
+ (tsubst_expr): Update call to tsubst_omp_clauses for OMP_TARGET_UPDATE,
+ OMP_TARGET_ENTER_DATA, OMP_TARGET_EXIT_DATA stanza.
+ * semantics.cc (cxx_omp_map_array_section): Avoid calling
+ build_array_ref for non-array/non-pointer bases (error reported
+ already).
+
+2025-05-15 Julian Brown <julian@codesourcery.com>
+
+ * constexpr.cc (reduced_constant_expression_p): Add OMP_DECLARE_MAPPER
+ case.
+ (cxx_eval_constant_expression, potential_constant_expression_1):
+ Likewise.
+ * cp-gimplify.cc (cxx_omp_finish_mapper_clauses): New function.
+ * cp-objcp-common.h (LANG_HOOKS_OMP_FINISH_MAPPER_CLAUSES,
+ LANG_HOOKS_OMP_MAPPER_LOOKUP, LANG_HOOKS_OMP_EXTRACT_MAPPER_DIRECTIVE,
+ LANG_HOOKS_OMP_MAP_ARRAY_SECTION): Define langhooks.
+ * cp-tree.h (lang_decl_base): Add omp_declare_mapper_p field. Recount
+ spare bits comment.
+ (DECL_OMP_DECLARE_MAPPER_P): New macro.
+ (omp_mapper_id): Add prototype.
+ (cp_check_omp_declare_mapper): Add prototype.
+ (omp_instantiate_mappers): Add prototype.
+ (cxx_omp_finish_mapper_clauses): Add prototype.
+ (cxx_omp_mapper_lookup): Add prototype.
+ (cxx_omp_extract_mapper_directive): Add prototype.
+ (cxx_omp_map_array_section): Add prototype.
+ * decl.cc (check_initializer): Add OpenMP declare mapper support.
+ (cp_finish_decl): Set DECL_INITIAL for OpenMP declare mapper var decls
+ as appropriate.
+ * decl2.cc (mark_used): Instantiate OpenMP "declare mapper" magic var
+ decls.
+ * error.cc (dump_omp_declare_mapper): New function.
+ (dump_simple_decl): Use above.
+ * parser.cc (cp_parser_omp_clause_map): Add KIND parameter. Support
+ "mapper" modifier.
+ (cp_parser_omp_all_clauses): Add KIND argument to
+ cp_parser_omp_clause_map call.
+ (cp_parser_omp_target): Call omp_instantiate_mappers before
+ finish_omp_clauses.
+ (cp_parser_omp_declare_mapper): New function.
+ (cp_parser_omp_declare): Add "declare mapper" support.
+ * pt.cc (tsubst_decl): Adjust name of "declare mapper" magic var decls
+ once we know their type.
+ (tsubst_omp_clauses): Call omp_instantiate_mappers before
+ finish_omp_clauses, for target regions.
+ (tsubst_expr): Support OMP_DECLARE_MAPPER nodes.
+ (instantiate_decl): Instantiate initialiser (i.e definition) for OpenMP
+ declare mappers.
+ * semantics.cc (gimplify.h): Include.
+ (omp_mapper_id, omp_mapper_lookup, omp_extract_mapper_directive,
+ cxx_omp_map_array_section, cp_check_omp_declare_mapper): New functions.
+ (finish_omp_clauses): Delete GOMP_MAP_PUSH_MAPPER_NAME and
+ GOMP_MAP_POP_MAPPER_NAME artificial clauses.
+ (omp_target_walk_data): Add MAPPERS field.
+ (finish_omp_target_clauses_r): Scan for uses of struct/union/class type
+ variables.
+ (finish_omp_target_clauses): Create artificial mapper binding clauses
+ for used structs/unions/classes in offload region.
+
+2025-05-15 Tobias Burnus <tobias@codesourcery.com>
+
+ * parser.cc (cp_parser_omp_clause_uses_allocators): New.
+ (cp_parser_omp_clause_name, cp_parser_omp_all_clauses,
+ OMP_TARGET_CLAUSE_MASK): Handle uses_allocators.
+ * semantics.cc (finish_omp_clauses): Likewise.
+
+2025-05-15 Chung-Lin Tang <cltang@codesourcery.com>
+ Paul-Antoine Arras <parras@baylibre.com>
+
+ * semantics.cc (finish_omp_clauses): Adjust to allow duplicate
+ mapped variables for OpenMP.
+
+2025-05-15 Julian Brown <julian@codesourcery.com>
+ Cesar Philippidis <cesar@codesourcery.com>
+ Nathan Sidwell <nathan@acm.org>
+ Kwok Cheung Yeung <kcy@codesourcery.com>
+
+ * parser.cc (cp_parser_omp_var_list_no_open): New c_omp_region_type
+ argument. Use it to specialize handling of OMP_CLAUSE_REDUCTION for
+ OpenACC.
+ (cp_parser_omp_var_list): Add c_omp_region_type argument. Update call
+ to cp_parser_omp_var_list_no_open.
+ (cp_parser_oacc_data_clause): Update call to
+ cp_parser_omp_var_list_no_open.
+ (cp_parser_omp_clause_reduction): Change is_omp boolean parameter to
+ c_omp_region_type. Update call to cp_parser_omp_var_list_no_open.
+ (cp_parser_omp_clause_from_to): Update call to
+ cp_parser_omp_clause_var_list_no_open.
+ (cp_parser_omp_clause_map): Likewise.
+ (cp_parser_omp_clause_init): Likewise.
+ (cp_parser_oacc_all_clauses): Update call to
+ cp_parser_omp_clause_reduction.
+ (cp_parser_omp_all_clauses): Likewise.
+ * semantics.cc (finish_omp_reduction_clause): Add c_omp_region_type
+ argument. Suppress user-defined reduction error for OpenACC.
+ (finish_omp_clauses): Update call to finish_omp_reduction_clause.
+
+2025-05-15 Chung-Lin Tang <cltang@codesourcery.com>
+ Kwok Cheung Yeung <kcy@codesourcery.com>
+ Paul-Antoine Arras <parras@baylibre.com>
+
+ PR other/76739
+ * semantics.cc (handle_omp_array_sections_1): Add 'bool &non_contiguous'
+ parameter, adjust recursive call site, add cases for allowing
+ pointer based multi-dimensional arrays for OpenACC. Reject
+ non-DECL base-pointer cases as unsupported.
+ (handle_omp_array_sections): Adjust handle_omp_array_sections_1 call,
+ handle non-contiguous case to create dynamic array map. \ No newline at end of file
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index d647a09..48327fb 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -3540,6 +3540,9 @@ reduced_constant_expression_p (tree t)
/* Even if we can't lower this yet, it's constant. */
return true;
+ case OMP_DECLARE_MAPPER:
+ return true;
+
case CONSTRUCTOR:
/* And we need to handle PTRMEM_CST wrapped in a CONSTRUCTOR. */
tree field;
@@ -7849,6 +7852,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case LABEL_EXPR:
case CASE_LABEL_EXPR:
case PREDICT_EXPR:
+ case OMP_DECLARE_MAPPER:
return t;
case PARM_DECL:
@@ -10536,6 +10540,11 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
"expression", t);
return false;
+ case OMP_DECLARE_MAPPER:
+ /* This can be used to initialize VAR_DECLs: it's treated as a magic
+ constant. */
+ return true;
+
case ASM_EXPR:
if (flags & tf_error)
inline_asm_in_constexpr_error (loc, fundef_p);
@@ -11033,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-gimplify.cc b/gcc/cp/cp-gimplify.cc
index d2423fd..a4f3eaa 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -2809,6 +2809,12 @@ cxx_omp_finish_clause (tree c, gimple_seq *, bool /* openacc */)
}
}
+tree
+cxx_omp_finish_mapper_clauses (tree clauses)
+{
+ return finish_omp_clauses (clauses, C_ORT_OMP);
+}
+
/* Return true if DECL's DECL_VALUE_EXPR (if any) should be
disregarded in OpenMP construct, because it is going to be
remapped during OpenMP lowering. SHARED is true if DECL
diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
index 8336d0b..f56541e 100644
--- a/gcc/cp/cp-objcp-common.cc
+++ b/gcc/cp/cp-objcp-common.cc
@@ -685,6 +685,7 @@ cp_common_init_ts (void)
MARK_TS_EXP (OFFSET_REF);
MARK_TS_EXP (PSEUDO_DTOR_EXPR);
MARK_TS_EXP (REINTERPRET_CAST_EXPR);
+ MARK_TS_EXP (OMP_ARRAYSHAPE_CAST_EXPR);
MARK_TS_EXP (SCOPE_REF);
MARK_TS_EXP (STATIC_CAST_EXPR);
MARK_TS_EXP (STMT_EXPR);
diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
index 13fb80c..ff35428 100644
--- a/gcc/cp/cp-objcp-common.h
+++ b/gcc/cp/cp-objcp-common.h
@@ -190,6 +190,15 @@ static const scoped_attribute_specs *const cp_objcp_attribute_table[] =
#define LANG_HOOKS_OMP_CLAUSE_DTOR cxx_omp_clause_dtor
#undef LANG_HOOKS_OMP_FINISH_CLAUSE
#define LANG_HOOKS_OMP_FINISH_CLAUSE cxx_omp_finish_clause
+#undef LANG_HOOKS_OMP_FINISH_MAPPER_CLAUSES
+#define LANG_HOOKS_OMP_FINISH_MAPPER_CLAUSES cxx_omp_finish_mapper_clauses
+#undef LANG_HOOKS_OMP_MAPPER_LOOKUP
+#define LANG_HOOKS_OMP_MAPPER_LOOKUP cxx_omp_mapper_lookup
+#undef LANG_HOOKS_OMP_EXTRACT_MAPPER_DIRECTIVE
+#define LANG_HOOKS_OMP_EXTRACT_MAPPER_DIRECTIVE \
+ cxx_omp_extract_mapper_directive
+#undef LANG_HOOKS_OMP_MAP_ARRAY_SECTION
+#define LANG_HOOKS_OMP_MAP_ARRAY_SECTION cxx_omp_map_array_section
#undef LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE
#define LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE cxx_omp_privatize_by_reference
#undef LANG_HOOKS_OMP_DISREGARD_VALUE_EXPR
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index bb5aaf9..2182141 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -257,6 +257,7 @@ DEFTREECODE (REINTERPRET_CAST_EXPR, "reinterpret_cast_expr", tcc_unary, 1)
DEFTREECODE (CONST_CAST_EXPR, "const_cast_expr", tcc_unary, 1)
DEFTREECODE (STATIC_CAST_EXPR, "static_cast_expr", tcc_unary, 1)
DEFTREECODE (DYNAMIC_CAST_EXPR, "dynamic_cast_expr", tcc_unary, 1)
+DEFTREECODE (OMP_ARRAYSHAPE_CAST_EXPR, "omp_arrayshape_cast_expr", tcc_unary, 1)
DEFTREECODE (IMPLICIT_CONV_EXPR, "implicit_conv_expr", tcc_unary, 1)
DEFTREECODE (DOTSTAR_EXPR, "dotstar_expr", tcc_expression, 2)
DEFTREECODE (TYPEID_EXPR, "typeid_expr", tcc_expression, 1)
@@ -490,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 eb32ec0..f984940 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -515,6 +515,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
LOOKUP_FOUND_P (in RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE, NAMESPACE_DECL)
FNDECL_MANIFESTLY_CONST_EVALUATED (in FUNCTION_DECL)
TARGET_EXPR_INTERNAL_P (in TARGET_EXPR)
+ DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (in DECLTYPE_TYPE)
5: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
@@ -1923,6 +1924,11 @@ struct GTY(()) cp_omp_begin_assumes_data {
bool attr_syntax;
};
+struct GTY(()) cp_omp_declare_variant_attr {
+ bool attr_syntax;
+ tree selector;
+};
+
/* Global state. */
struct GTY(()) saved_scope {
@@ -1973,6 +1979,7 @@ struct GTY(()) saved_scope {
hash_map<tree, tree> *GTY((skip)) x_local_specializations;
vec<cp_omp_declare_target_attr, va_gc> *omp_declare_target_attribute;
vec<cp_omp_begin_assumes_data, va_gc> *omp_begin_assumes;
+ vec<cp_omp_declare_variant_attr, va_gc> *omp_declare_variant_attribute;
struct saved_scope *prev;
};
@@ -2975,7 +2982,10 @@ struct GTY(()) lang_decl_base {
unsigned module_keyed_decls_p : 1; /* has keys, applies to all decls */
- /* 11 spare bits. */
+ /* VAR_DECL being used to represent an OpenMP declared mapper. */
+ unsigned omp_declare_mapper_p : 1;
+
+ /* 10 spare bits. */
};
/* True for DECL codes which have template info and access. */
@@ -4530,6 +4540,11 @@ get_vec_init_expr (tree t)
#define DECL_OMP_DECLARE_REDUCTION_P(NODE) \
(LANG_DECL_FN_CHECK (DECL_COMMON_CHECK (NODE))->omp_declare_reduction_p)
+/* Nonzero if NODE is an artificial FUNCTION_DECL for
+ #pragma omp declare mapper. */
+#define DECL_OMP_DECLARE_MAPPER_P(NODE) \
+ (DECL_LANG_SPECIFIC (VAR_DECL_CHECK (NODE))->u.base.omp_declare_mapper_p)
+
/* Nonzero if DECL has been declared threadprivate by
#pragma omp threadprivate. */
#define CP_DECL_THREADPRIVATE_P(DECL) \
@@ -5050,6 +5065,8 @@ get_vec_init_expr (tree t)
TREE_LANG_FLAG_2 (DECLTYPE_TYPE_CHECK (NODE))
#define DECLTYPE_FOR_REF_CAPTURE(NODE) \
TREE_LANG_FLAG_3 (DECLTYPE_TYPE_CHECK (NODE))
+#define DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST(NODE) \
+ TREE_LANG_FLAG_4 (DECLTYPE_TYPE_CHECK (NODE))
/* Nonzero for VAR_DECL and FUNCTION_DECL node means that `extern' was
specified in its declaration. This can also be set for an
@@ -5670,6 +5687,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. */
@@ -7221,6 +7255,8 @@ extern tree cxx_comdat_group (tree);
extern bool cp_missing_noreturn_ok_p (tree);
extern bool is_direct_enum_init (tree, tree);
extern void initialize_artificial_var (tree, vec<constructor_elt, va_gc> *);
+extern tree cp_omp_create_arrayshape_type (location_t, tree,
+ vec<cp_expr> *);
extern tree check_var_type (tree, tree, location_t);
extern tree reshape_init (tree, tree, tsubst_flags_t);
extern tree next_aggregate_field (tree);
@@ -7255,7 +7291,8 @@ extern void grokclassfn (tree, tree,
enum overload_flags);
extern tree grok_array_decl (location_t, tree, tree,
vec<tree, va_gc> **, tsubst_flags_t);
-extern tree grok_omp_array_section (location_t, tree, tree, tree);
+extern tree grok_omp_array_section (location_t, tree, tree, tree,
+ tree);
extern tree delete_sanity (location_t, tree, tree, bool,
int, tsubst_flags_t);
extern tree check_classfn (tree, tree, tree);
@@ -8062,11 +8099,16 @@ extern tree finish_qualified_id_expr (tree, tree, bool, bool,
extern void simplify_aggr_init_expr (tree *);
extern void finalize_nrv (tree, tree);
extern tree omp_reduction_id (enum tree_code, tree, tree);
+extern tree omp_mapper_id (tree, tree);
extern tree cp_remove_omp_priv_cleanup_stmt (tree *, int *, void *);
extern bool cp_check_omp_declare_reduction (tree);
+extern bool cp_check_omp_declare_mapper (tree);
extern void finish_omp_declare_simd_methods (tree);
extern tree cp_finish_omp_init_prefer_type (tree);
+extern tree finish_omp_parm_list (tree, const_tree, int);
+extern tree finish_omp_adjust_args (tree, const_tree, int);
extern tree finish_omp_clauses (tree, enum c_omp_region_type);
+extern tree omp_instantiate_mappers (tree);
extern tree push_omp_privatization_clauses (bool);
extern void pop_omp_privatization_clauses (tree);
extern void save_omp_privatization_clauses (vec<tree> &);
@@ -8085,6 +8127,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,
@@ -8148,6 +8191,8 @@ extern tree cp_build_vec_convert (tree, location_t, tree,
tsubst_flags_t);
extern tree cp_build_bit_cast (location_t, tree, tree,
tsubst_flags_t);
+extern tree cp_build_omp_arrayshape_cast (location_t, tree, tree,
+ tsubst_flags_t);
extern void start_lambda_scope (tree decl);
extern void finish_lambda_scope (void);
extern void record_lambda_scope (tree lambda);
@@ -8406,7 +8451,8 @@ inline tree build_x_binary_op (const op_location_t &loc,
}
extern tree build_x_array_ref (location_t, tree, tree,
tsubst_flags_t);
-extern tree build_omp_array_section (location_t, tree, tree, tree);
+extern tree build_omp_array_section (location_t, tree, tree, tree,
+ tree);
extern tree build_x_unary_op (location_t,
enum tree_code, cp_expr,
tree, tsubst_flags_t);
@@ -8662,6 +8708,10 @@ extern tree cxx_omp_clause_copy_ctor (tree, tree, tree);
extern tree cxx_omp_clause_assign_op (tree, tree, tree);
extern tree cxx_omp_clause_dtor (tree, tree);
extern void cxx_omp_finish_clause (tree, gimple_seq *, bool);
+extern tree cxx_omp_finish_mapper_clauses (tree);
+extern tree cxx_omp_mapper_lookup (tree, tree);
+extern tree cxx_omp_extract_mapper_directive (tree);
+extern tree cxx_omp_map_array_section (location_t, tree);
extern bool cxx_omp_privatize_by_reference (const_tree);
extern bool cxx_omp_disregard_value_expr (tree, bool);
extern void cp_fold_function (tree);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4e97093..9db508f 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -7871,6 +7871,12 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
}
else if (!init && DECL_REALLY_EXTERN (decl))
;
+ else if (flag_openmp
+ && VAR_P (decl)
+ && DECL_LANG_SPECIFIC (decl)
+ && DECL_OMP_DECLARE_MAPPER_P (decl)
+ && TREE_CODE (init) == OMP_DECLARE_MAPPER)
+ return NULL_TREE;
else if (init || type_build_ctor_call (type)
|| TYPE_REF_P (type))
{
@@ -8165,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))
@@ -8465,15 +8482,29 @@ omp_declare_variant_finalize_one (tree decl, tree attr)
parm = DECL_CHAIN (parm);
for (; parm; parm = DECL_CHAIN (parm))
vec_safe_push (args, forward_parm (parm));
-
+ /* The layout of these nodes are a mess, this function is generally very hard
+ to reason about because of it, this needs to be fixed. */
+ const tree adjust_args_idxs = [&] ()
+ {
+ const tree omp_variant_clauses_temp = TREE_CHAIN (TREE_CHAIN (chain));
+ gcc_checking_assert (!omp_variant_clauses_temp
+ || TREE_PURPOSE (omp_variant_clauses_temp)
+ == get_identifier ("omp variant clauses temp"));
+ const tree adjust_args_idxs = omp_variant_clauses_temp
+ ? TREE_VALUE (omp_variant_clauses_temp)
+ : NULL_TREE;
+ gcc_checking_assert (!adjust_args_idxs
+ || TREE_PURPOSE (adjust_args_idxs)
+ == get_identifier ("omp adjust args idxs"));
+ return adjust_args_idxs;
+ } (); /* IILE. */
unsigned nappend_args = 0;
- tree append_args_list = TREE_CHAIN (TREE_CHAIN (chain));
+ const tree append_args_list
+ = adjust_args_idxs && TREE_CHAIN (adjust_args_idxs)
+ ? TREE_VALUE (TREE_CHAIN (adjust_args_idxs))
+ : NULL_TREE;;
if (append_args_list)
{
- append_args_list = TREE_VALUE (append_args_list);
- append_args_list = (append_args_list && TREE_CHAIN (append_args_list)
- ? TREE_VALUE (TREE_CHAIN (append_args_list))
- : NULL_TREE);
for (tree t = append_args_list; t; t = TREE_CHAIN (t))
nappend_args++;
if (nappend_args)
@@ -8504,6 +8535,55 @@ omp_declare_variant_finalize_one (tree decl, tree attr)
vec_safe_push (args, build_stub_object (TREE_TYPE (type)));
}
}
+ /* We assume the this parameter is included in the declaration, if it isn't
+ our parm count will be wrong. */
+ gcc_assert (!DECL_IOBJ_MEMBER_FUNCTION_P (decl)
+ || is_this_parameter (DECL_ARGUMENTS (decl)));
+ struct bundled_parm_info
+ {
+ int count;
+ bool unexpanded_pack;
+ bool variadic;
+ };
+ const bundled_parm_info parm_info = [&] ()
+ {
+ bool pack = false;
+ int cnt = 0;
+ tree parm_t = TYPE_ARG_TYPES (TREE_TYPE (decl));
+ while (parm_t && parm_t != void_list_node)
+ {
+ /* We can't tell how many parameters there are until all parameter
+ packs have been expanded. */
+ if (PACK_EXPANSION_P (TREE_VALUE (parm_t)))
+ pack = true;
+ parm_t = TREE_CHAIN (parm_t);
+ ++cnt;
+ }
+ return bundled_parm_info{!pack ? cnt : 0, pack, parm_t == NULL_TREE};
+ } (); /* IILE. */
+ tree adjust_args_list = adjust_args_idxs ? TREE_VALUE (adjust_args_idxs)
+ : NULL_TREE;
+ if (adjust_args_list)
+ {
+ /* We currently treat a parm count of 0 as variadic. */
+ adjust_args_list
+ = finish_omp_parm_list (adjust_args_list,
+ decl,
+ parm_info.variadic ? 0 : parm_info.count);
+ if (adjust_args_list != error_mark_node)
+ adjust_args_list
+ = finish_omp_adjust_args (adjust_args_list,
+ decl,
+ parm_info.variadic ? 0 : parm_info.count);
+ if (adjust_args_list == error_mark_node)
+ adjust_args_list = NULL_TREE;
+ }
+ /* This will also clear the adjust_args_list if there was an error. */
+ if (adjust_args_idxs)
+ TREE_VALUE (adjust_args_idxs) = adjust_args_list;
+
+ /* Maybe we should just return at this point if an unexpanded pack was
+ encountered, there isn't much else that we can do if there is. */
bool koenig_p = false;
if (idk == CP_ID_KIND_UNQUALIFIED || idk == CP_ID_KIND_TEMPLATE_ID)
@@ -8543,6 +8623,21 @@ omp_declare_variant_finalize_one (tree decl, tree attr)
if (idk == CP_ID_KIND_QUALIFIED)
variant = finish_call_expr (variant, &args, /*disallow_virtual=*/true,
koenig_p, tf_warning_or_error);
+ else if (idk == CP_ID_KIND_NONE
+ && TREE_CODE (variant) == FUNCTION_DECL
+ && DECL_IOBJ_MEMBER_FUNCTION_P (variant)
+ && CLASS_TYPE_P (DECL_CONTEXT (decl)))
+ {
+ tree saved_ccp = current_class_ptr;
+ tree saved_ccr = current_class_ref;
+ current_class_ptr = NULL_TREE;
+ current_class_ref = NULL_TREE;
+ inject_this_parameter (DECL_CONTEXT (decl), TYPE_UNQUALIFIED);
+ variant = finish_call_expr (variant, &args, /*disallow_virtual=*/false,
+ koenig_p, tf_warning_or_error);
+ current_class_ptr = saved_ccp;
+ current_class_ref = saved_ccr;
+ }
else
variant = finish_call_expr (variant, &args, /*disallow_virtual=*/false,
koenig_p, tf_warning_or_error);
@@ -8555,120 +8650,119 @@ omp_declare_variant_finalize_one (tree decl, tree attr)
variant = cp_get_callee_fndecl_nofold (STRIP_REFERENCE_REF (variant));
input_location = save_loc;
- if (variant)
+ if (!variant)
{
- bool fail;
- const char *varname = IDENTIFIER_POINTER (DECL_NAME (variant));
- if (!nappend_args)
- fail = !comptypes (TREE_TYPE (decl), TREE_TYPE (variant),
- COMPARE_STRICT);
- else
- {
- unsigned nbase_args = 0;
- for (tree t = TYPE_ARG_TYPES (TREE_TYPE (decl));
- t && TREE_VALUE (t) != void_type_node; t = TREE_CHAIN (t))
- nbase_args++;
- tree vargs, varg;
- vargs = varg = TYPE_ARG_TYPES (TREE_TYPE (variant));
- for (unsigned i = 0; i < nbase_args && varg;
- i++, varg = TREE_CHAIN (varg))
- vargs = varg;
- for (unsigned i = 0; i < nappend_args && varg; i++)
- varg = TREE_CHAIN (varg);
- tree saved_vargs;
- if (nbase_args)
- {
- saved_vargs = TREE_CHAIN (vargs);
- TREE_CHAIN (vargs) = varg;
- }
- else
- {
- saved_vargs = vargs;
- TYPE_ARG_TYPES (TREE_TYPE (variant)) = varg;
- }
- /* Skip assert check that TYPE_CANONICAL is the same. */
- fail = !comptypes (TREE_TYPE (decl), TREE_TYPE (variant),
- COMPARE_STRUCTURAL);
- if (nbase_args)
- TREE_CHAIN (vargs) = saved_vargs;
- else
- TYPE_ARG_TYPES (TREE_TYPE (variant)) = saved_vargs;
- varg = saved_vargs;
- if (!fail && !processing_template_decl)
- for (unsigned i = 0; i < nappend_args;
- i++, varg = TREE_CHAIN (varg))
- if (!varg || !c_omp_interop_t_p (TREE_VALUE (varg)))
- {
- error_at (DECL_SOURCE_LOCATION (variant),
- "argument %d of %qD must be of %<omp_interop_t%>",
- nbase_args + i + 1, variant);
- inform (EXPR_LOCATION (TREE_PURPOSE (append_args_list)),
- "%<append_args%> specified here");
- break;
- }
- }
+ /* Don't error unless we are fully instantiated. */
+ if (processing_template_decl)
+ return false;
+ error_at (varid_loc, "could not find variant declaration");
+ return true;
+ }
+
+ /* We should probably just error and return here if the two functions are not
+ both member functions or both free functions, but I don't want to move
+ the later error that checks for builtins ip right now. */
+ auto emit_variant_type_error = [&] ()
+ {
+ error_at (varid_loc, "variant %qD and base %qD have incompatible "
+ "types", variant, decl);
+ };
+ /* Unlike below, COMPARE_STRICT is fine here. */
+ if (!nappend_args
+ && !comptypes (TREE_TYPE (decl), TREE_TYPE (variant), COMPARE_STRICT))
+ {
+ error_at (varid_loc, "variant %qD and base %qD have incompatible "
+ "types", variant, decl);
+ return true;
+ }
+ if (nappend_args)
+ {
+ const unsigned nbase_parms = parm_info.count;
+ {
+ tree t = TREE_CHAIN (TREE_CHAIN (chain));
+ tree append_args_node = TREE_CHAIN (TREE_VALUE (t));
+ /* Add the number of parameters once we know how many there are, for
+ now just wait until we are fully instantiated to keep it simple. */
+ if (append_args_node && !processing_template_decl)
+ TREE_PURPOSE (append_args_node)
+ = build_int_cst (integer_type_node, nbase_parms);
+ }
+ /* This is where appended interop parms start, we need to remove them
+ temporarily to compare the function types. */
+ tree *const last_regular_parm_chain = [&] ()
+ {
+ if (!nbase_parms)
+ return &TYPE_ARG_TYPES (TREE_TYPE (variant));
+ /* Return a pointer to the last regular parm's chain, subtract 1 from
+ nbase_parms so we don't iterate past it. */
+ tree last_regular_parm
+ = chain_index (nbase_parms - 1,
+ TYPE_ARG_TYPES (TREE_TYPE (variant)));
+ return &TREE_CHAIN (last_regular_parm);
+ } (); /* IILE. */
+
+ /* Go past the added interop parms to find the first hidden parm, or the
+ end of the list of parms, this can be NULL_TREE or void_list_node. */
+ tree first_hidden_parm = chain_index (nappend_args,
+ *last_regular_parm_chain);
+ tree interop_parms_start = *last_regular_parm_chain;
+ *last_regular_parm_chain = first_hidden_parm;
+ /* Skip assert check that TYPE_CANONICAL is the same, use
+ COMPARE_STRUCTURAL, not COMPARE_STRICT. */
+ const bool fail = !comptypes (TREE_TYPE (decl), TREE_TYPE (variant),
+ COMPARE_STRUCTURAL);
+ /* Return the variant back to normal, even if the comparison failed. */
+ *last_regular_parm_chain = interop_parms_start;
+
if (fail)
{
- error_at (varid_loc, "variant %qD and base %qD have incompatible "
- "types", variant, decl);
- return true;
- }
- if (fndecl_built_in_p (variant)
- && (startswith (varname, "__builtin_")
- || startswith (varname, "__sync_")
- || startswith (varname, "__atomic_")))
- {
- error_at (varid_loc, "variant %qD is a built-in", variant);
+ emit_variant_type_error ();
return true;
}
- else
- {
- tree construct
- = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT);
- omp_mark_declare_variant (match_loc, variant, construct);
- if (!omp_context_selector_matches (ctx, NULL_TREE, false))
- return true;
- TREE_PURPOSE (TREE_VALUE (attr)) = variant;
-
- // Prepend adjust_args list to variant attributes
- tree adjust_args_list = TREE_CHAIN (TREE_CHAIN (chain));
- if (adjust_args_list != NULL_TREE)
- {
- if (DECL_NONSTATIC_MEMBER_P (variant)
- && TREE_VALUE (adjust_args_list))
- {
- /* Shift arg position for the added 'this' pointer. */
- /* Handle need_device_ptr */
- for (tree t = TREE_PURPOSE (TREE_VALUE (adjust_args_list));
- t; t = TREE_CHAIN (t))
- TREE_VALUE (t)
- = build_int_cst (TREE_TYPE (t),
- tree_to_uhwi (TREE_VALUE (t)) + 1);
- }
- if (DECL_NONSTATIC_MEMBER_P (variant) && append_args_list)
- {
- /* Shift likewise the number of args after which the
- interop object should be added. */
- tree nargs = TREE_CHAIN (TREE_VALUE (adjust_args_list));
- TREE_PURPOSE (nargs)
- = build_int_cst (TREE_TYPE (nargs),
- tree_to_uhwi (TREE_PURPOSE (nargs)) + 1);
- }
- for (tree t = append_args_list; t; t = TREE_CHAIN (t))
- TREE_VALUE (t)
- = cp_finish_omp_init_prefer_type (TREE_VALUE (t));
- DECL_ATTRIBUTES (variant) = tree_cons (
- get_identifier ("omp declare variant variant args"),
- TREE_VALUE (adjust_args_list), DECL_ATTRIBUTES (variant));
- }
- }
+ tree interop_parm = interop_parms_start;
+ if (!processing_template_decl)
+ for (unsigned i = 0; i < nappend_args; i++)
+ {
+ if (!interop_parm
+ || !c_omp_interop_t_p (TREE_VALUE (interop_parm)))
+ {
+ error_at (DECL_SOURCE_LOCATION (variant),
+ "argument %d of %qD must be of %<omp_interop_t%>",
+ nbase_parms + i + 1, variant);
+ inform (EXPR_LOCATION (TREE_PURPOSE (append_args_list)),
+ "%<append_args%> specified here");
+ break;
+ }
+ interop_parm = TREE_CHAIN (interop_parm);
+ }
}
- else if (!processing_template_decl)
+ const char *varname = IDENTIFIER_POINTER (DECL_NAME (variant));
+ if (fndecl_built_in_p (variant)
+ && (startswith (varname, "__builtin_")
+ || startswith (varname, "__sync_")
+ || startswith (varname, "__atomic_")))
{
- error_at (varid_loc, "could not find variant declaration");
+ error_at (varid_loc, "variant %qD is a built-in", variant);
return true;
}
+ tree construct
+ = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT);
+ omp_mark_declare_variant (match_loc, variant, construct);
+ if (!omp_context_selector_matches (ctx, NULL_TREE, false))
+ return true;
+ TREE_PURPOSE (TREE_VALUE (attr)) = variant;
+
+ // Prepend adjust_args list to variant attributes
+ if (adjust_args_idxs != NULL_TREE)
+ {
+ for (tree t = append_args_list; t; t = TREE_CHAIN (t))
+ TREE_VALUE (t) = cp_finish_omp_init_prefer_type (TREE_VALUE (t));
+ DECL_ATTRIBUTES (variant)
+ = tree_cons (get_identifier ("omp declare variant variant args"),
+ adjust_args_idxs, DECL_ATTRIBUTES (variant));
+ }
+
return false;
}
@@ -9188,14 +9282,23 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
varpool_node::get_create (decl);
}
+ if (flag_openmp
+ && VAR_P (decl)
+ && DECL_LANG_SPECIFIC (decl)
+ && DECL_OMP_DECLARE_MAPPER_P (decl)
+ && init)
+ {
+ gcc_assert (TREE_CODE (init) == OMP_DECLARE_MAPPER);
+ DECL_INITIAL (decl) = init;
+ }
/* Convert the initializer to the type of DECL, if we have not
already initialized DECL. */
- if (!DECL_INITIALIZED_P (decl)
- /* If !DECL_EXTERNAL then DECL is being defined. In the
- case of a static data member initialized inside the
- class-specifier, there can be an initializer even if DECL
- is *not* defined. */
- && (!DECL_EXTERNAL (decl) || init))
+ else if (!DECL_INITIALIZED_P (decl)
+ /* If !DECL_EXTERNAL then DECL is being defined. In the
+ case of a static data member initialized inside the
+ class-specifier, there can be an initializer even if DECL
+ is *not* defined. */
+ && (!DECL_EXTERNAL (decl) || init))
{
cleanups = make_tree_vector ();
init = check_initializer (decl, init, flags, &cleanups);
@@ -12400,6 +12503,81 @@ create_array_type_for_decl (tree name, tree type, tree size, location_t loc)
return build_cplus_array_type (type, itype);
}
+/* Build an anonymous array of SIZE elements of ELTYPE. */
+
+static tree
+create_anon_array_type (location_t loc, tree eltype, tree size)
+{
+ if (eltype == error_mark_node || size == error_mark_node)
+ return error_mark_node;
+
+ tree itype = compute_array_index_type_loc (loc, NULL_TREE, size,
+ tf_warning_or_error);
+
+ if (type_uses_auto (eltype)
+ && variably_modified_type_p (itype, /*fn=*/NULL_TREE))
+ {
+ sorry_at (loc, "variable-length array of %<auto%>");
+ return error_mark_node;
+ }
+
+ return build_cplus_array_type (eltype, itype);
+}
+
+/* Derive an array type for an OpenMP array-shaping operator given EXPR, which
+ is an expression that might have array refs or array sections postfixed
+ (e.g. "ptr[0:3:2][3:4]"), and OMP_SHAPE_DIMS, a vector of dimensions. */
+
+tree
+cp_omp_create_arrayshape_type (location_t loc, tree expr,
+ vec<cp_expr> *omp_shape_dims)
+{
+ tree type, strip_sections = expr;
+
+ while (TREE_CODE (strip_sections) == OMP_ARRAY_SECTION
+ || TREE_CODE (strip_sections) == ARRAY_REF)
+ strip_sections = TREE_OPERAND (strip_sections, 0);
+
+ /* Determine the element type, either directly or by using
+ "decltype" of an expression representing an element to
+ figure it out later during template instantiation. */
+ if (type_dependent_expression_p (expr))
+ {
+ type = cxx_make_type (DECLTYPE_TYPE);
+
+ DECLTYPE_TYPE_EXPR (type)
+ = build_min_nt_loc (loc, INDIRECT_REF, strip_sections);
+ DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (type) = true;
+ SET_TYPE_STRUCTURAL_EQUALITY (type);
+ }
+ else
+ {
+ type = TREE_TYPE (strip_sections);
+
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+
+ if (TREE_CODE (type) != POINTER_TYPE)
+ {
+ error ("OpenMP array shaping operator with non-pointer argument");
+ return error_mark_node;
+ }
+
+ type = TREE_TYPE (type);
+ }
+
+ int i;
+ cp_expr dim;
+ FOR_EACH_VEC_ELT_REVERSE (*omp_shape_dims, i, dim)
+ {
+ if (!type_dependent_expression_p (dim))
+ dim = fold_convert (sizetype, dim);
+ type = create_anon_array_type (loc, type, dim);
+ }
+
+ return type;
+}
+
/* Returns the smallest location that is not UNKNOWN_LOCATION. */
static location_t
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index bceaf78..4195c08 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -622,35 +622,39 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp,
tree
grok_omp_array_section (location_t loc, tree array_expr, tree index,
- tree length)
+ tree length, tree stride)
{
tree orig_array_expr = array_expr;
tree orig_index = index;
tree orig_length = length;
+ tree orig_stride = stride;
if (error_operand_p (array_expr)
|| error_operand_p (index)
- || error_operand_p (length))
+ || error_operand_p (length)
+ || error_operand_p (stride))
return error_mark_node;
if (processing_template_decl
&& (type_dependent_expression_p (array_expr)
|| type_dependent_expression_p (index)
- || type_dependent_expression_p (length)))
- return build_min_nt_loc (loc, OMP_ARRAY_SECTION, array_expr, index, length);
+ || type_dependent_expression_p (length)
+ || type_dependent_expression_p (stride)))
+ return build_min_nt_loc (loc, OMP_ARRAY_SECTION, array_expr, index, length, stride);
index = fold_non_dependent_expr (index);
length = fold_non_dependent_expr (length);
+ stride = fold_non_dependent_expr (stride);
/* NOTE: We can pass through invalidly-typed index/length fields
here (e.g. if the user tries to use a floating-point index/length).
This is diagnosed later in semantics.cc:handle_omp_array_sections_1. */
- tree expr = build_omp_array_section (loc, array_expr, index, length);
+ tree expr = build_omp_array_section (loc, array_expr, index, length, stride);
if (processing_template_decl)
expr = build_min_non_dep (OMP_ARRAY_SECTION, expr, orig_array_expr,
- orig_index, orig_length);
+ orig_index, orig_length, orig_stride);
return expr;
}
@@ -2834,6 +2838,7 @@ min_vis_expr_r (tree *tp, int */*walk_subtrees*/, void *data)
case REINTERPRET_CAST_EXPR:
case CONST_CAST_EXPR:
case DYNAMIC_CAST_EXPR:
+ case OMP_ARRAYSHAPE_CAST_EXPR:
case NEW_EXPR:
case CONSTRUCTOR:
case LAMBDA_EXPR:
@@ -6406,12 +6411,17 @@ mark_used (tree decl, tsubst_flags_t complain /* = tf_warning_or_error */)
/* If DECL has a deduced return type, we need to instantiate it now to
find out its type. For OpenMP user defined reductions, we need them
- instantiated for reduction clauses which inline them by hand directly. */
+ instantiated for reduction clauses which inline them by hand directly.
+ OpenMP declared mappers are used implicitly so must be instantiated
+ before they can be detected. */
if (undeduced_auto_decl (decl)
|| (VAR_P (decl)
&& VAR_HAD_UNKNOWN_BOUND (decl))
|| (TREE_CODE (decl) == FUNCTION_DECL
- && DECL_OMP_DECLARE_REDUCTION_P (decl)))
+ && DECL_OMP_DECLARE_REDUCTION_P (decl))
+ || (TREE_CODE (decl) == VAR_DECL
+ && DECL_LANG_SPECIFIC (decl)
+ && DECL_OMP_DECLARE_MAPPER_P (decl)))
maybe_instantiate_decl (decl);
if (!decl_dependent_p (decl)
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 499eb1b..f9af2be 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -1255,12 +1255,37 @@ dump_global_iord (cxx_pretty_printer *pp, tree t)
pp_printf (pp, p, DECL_SOURCE_FILE (t));
}
+/* Write a representation of OpenMP "declare mapper" T to PP in a manner
+ suitable for error messages. */
+
+static void
+dump_omp_declare_mapper (cxx_pretty_printer *pp, tree t, int flags)
+{
+ pp_string (pp, "#pragma omp declare mapper");
+ if (t == NULL_TREE || t == error_mark_node)
+ return;
+ pp_space (pp);
+ pp_cxx_left_paren (pp);
+ if (OMP_DECLARE_MAPPER_ID (t))
+ {
+ pp_cxx_tree_identifier (pp, OMP_DECLARE_MAPPER_ID (t));
+ pp_colon (pp);
+ }
+ dump_type (pp, TREE_TYPE (t), flags);
+ pp_cxx_right_paren (pp);
+}
+
static void
dump_simple_decl (cxx_pretty_printer *pp, tree t, tree type, int flags)
{
if (VAR_P (t) && DECL_NTTP_OBJECT_P (t))
return dump_expr (pp, DECL_INITIAL (t), flags);
+ if (TREE_CODE (t) == VAR_DECL
+ && DECL_LANG_SPECIFIC (t)
+ && DECL_OMP_DECLARE_MAPPER_P (t))
+ return dump_omp_declare_mapper (pp, DECL_INITIAL (t), flags);
+
if (flags & TFF_DECL_SPECIFIERS)
{
if (concept_definition_p (t))
@@ -2594,6 +2619,11 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
dump_expr (pp, TREE_OPERAND (t, 1), flags);
pp_colon (pp);
dump_expr (pp, TREE_OPERAND (t, 2), flags);
+ if (TREE_OPERAND (t, 3))
+ {
+ pp_colon (pp);
+ dump_expr (pp, TREE_OPERAND (t, 3), flags);
+ }
pp_cxx_right_bracket (pp);
break;
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index 3d5e96b..6d0ce22 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -3944,6 +3944,7 @@ write_expression (tree expr)
case REINTERPRET_CAST_EXPR:
case STATIC_CAST_EXPR:
case CONST_CAST_EXPR:
+ case OMP_ARRAYSHAPE_CAST_EXPR:
write_type (TREE_TYPE (expr));
write_expression (TREE_OPERAND (expr, 0));
break;
diff --git a/gcc/cp/operators.def b/gcc/cp/operators.def
index 17601d3..e24714b 100644
--- a/gcc/cp/operators.def
+++ b/gcc/cp/operators.def
@@ -134,6 +134,7 @@ DEF_OPERATOR (NULL, DYNAMIC_CAST_EXPR, "dc", OVL_OP_FLAG_UNARY)
DEF_OPERATOR (NULL, REINTERPRET_CAST_EXPR, "rc", OVL_OP_FLAG_UNARY)
DEF_OPERATOR (NULL, CONST_CAST_EXPR, "cc", OVL_OP_FLAG_UNARY)
DEF_OPERATOR (NULL, STATIC_CAST_EXPR, "sc", OVL_OP_FLAG_UNARY)
+DEF_OPERATOR (NULL, OMP_ARRAYSHAPE_CAST_EXPR, "oc", OVL_OP_FLAG_UNARY)
DEF_OPERATOR (NULL, SCOPE_REF, "sr", OVL_OP_FLAG_NONE)
DEF_OPERATOR (NULL, EXPR_PACK_EXPANSION, "sp", OVL_OP_FLAG_NONE)
DEF_OPERATOR (NULL, UNARY_LEFT_FOLD_EXPR, "fl", OVL_OP_FLAG_NONE)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 3628cfe..9c1d976 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -2705,7 +2705,7 @@ static cp_ref_qualifier cp_parser_ref_qualifier_opt
static tree cp_parser_tx_qualifier_opt
(cp_parser *);
static tree cp_parser_late_return_type_opt
- (cp_parser *, cp_declarator *, tree &, tree);
+ (cp_parser *, cp_declarator *, tree &);
static tree cp_parser_declarator_id
(cp_parser *, bool);
static tree cp_parser_type_id
@@ -2740,7 +2740,7 @@ static void cp_parser_ctor_initializer_opt_and_function_body
(cp_parser *, bool);
static tree cp_parser_late_parsing_omp_declare_simd
- (cp_parser *, tree, tree);
+ (cp_parser *, tree);
static tree cp_parser_late_parsing_oacc_routine
(cp_parser *, tree);
@@ -4524,6 +4524,54 @@ cp_parser_require_pragma_eol (cp_parser *parser, cp_token *pragma_tok)
}
}
+/* Skip tokens up to and including "#pragma omp end declare variant".
+ Properly handle nested "#pragma omp begin declare variant" pragmas. */
+static void
+cp_parser_skip_to_pragma_omp_end_declare_variant (cp_parser *parser)
+{
+ for (int depth = 0; depth >= 0; )
+ {
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+
+ switch (token->type)
+ {
+ case CPP_PRAGMA_EOL:
+ if (!parser->lexer->in_pragma)
+ break;
+ /* FALLTHRU */
+ case CPP_EOF:
+ /* If we've run out of tokens, stop. */
+ return;
+
+ case CPP_PRAGMA:
+ if ((cp_parser_pragma_kind (token) == PRAGMA_OMP_BEGIN
+ || cp_parser_pragma_kind (token) == PRAGMA_OMP_END)
+ && cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME)
+ && cp_lexer_nth_token_is (parser->lexer, 3, CPP_NAME))
+ {
+ tree id1 = cp_lexer_peek_nth_token (parser->lexer, 2)->u.value;
+ tree id2 = cp_lexer_peek_nth_token (parser->lexer, 3)->u.value;
+ if (strcmp (IDENTIFIER_POINTER (id1), "declare") == 0
+ && strcmp (IDENTIFIER_POINTER (id2), "variant") == 0)
+ {
+ if (cp_parser_pragma_kind (token) == PRAGMA_OMP_BEGIN)
+ depth++;
+ else
+ depth--;
+ }
+ }
+ cp_parser_skip_to_pragma_eol (parser, token);
+ continue;
+
+ default:
+ break;
+ }
+
+ /* Consume the token. */
+ cp_lexer_consume_token (parser->lexer);
+ }
+}
+
/* This is a simple wrapper around make_typename_type. When the id is
an unresolved identifier node, we can provide a superior diagnostic
using cp_parser_diagnose_invalid_type_name. */
@@ -4652,6 +4700,12 @@ cp_parser_new (cp_lexer *lexer)
/* Disallow OpenMP array sections in expressions. */
parser->omp_array_section_p = false;
+ /* Disallow OpenMP array-shaping operator in expressions. */
+ parser->omp_array_shaping_op_p = false;
+
+ /* We don't have an OpenMP array shape here. */
+ parser->omp_has_array_shape_p = false;
+
/* Not declaring an implicit function template. */
parser->auto_is_implicit_function_template_parm_p = false;
parser->fully_implicit_function_template_p = false;
@@ -5659,6 +5713,7 @@ cp_parser_statement_expr (cp_parser *parser)
{
cp_token_position start = cp_parser_start_tentative_firewall (parser);
auto oas = make_temp_override (parser->omp_array_section_p, false);
+ auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
/* Consume the '('. */
location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
@@ -8722,7 +8777,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
&& cp_lexer_next_token_is (parser->lexer, CPP_COLON))
{
cp_lexer_consume_token (parser->lexer);
- tree length = NULL_TREE;
+ tree length = NULL_TREE, stride = NULL_TREE;
if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE))
{
if (cxx_dialect >= cxx23)
@@ -8755,9 +8810,23 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
/*warn_comma_p=*/warn_comma_subscript);
}
+ if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+ {
+ cp_lexer_consume_token (parser->lexer);
+ /* We could check for C++-23 multidimensional/comma-separated
+ subscripts here, or not bother. */
+ if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE))
+ stride
+ = cp_parser_expression (parser, NULL, /*cast_p=*/false,
+ /*decltype_p=*/false,
+ /*warn_comma_p=*/warn_comma_subscript);
+ }
+
parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
- if (index == error_mark_node || length == error_mark_node)
+ if (index == error_mark_node
+ || length == error_mark_node
+ || stride == error_mark_node)
{
cp_parser_skip_to_closing_square_bracket (parser);
return error_mark_node;
@@ -8766,7 +8835,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
return grok_omp_array_section (input_location, postfix_expression, index,
- length);
+ length, stride);
}
parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
@@ -8774,11 +8843,23 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
/* Look for the closing `]'. */
cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
- /* Build the ARRAY_REF. */
- postfix_expression = grok_array_decl (loc, postfix_expression,
- index, &expression_list,
- tf_warning_or_error
- | (decltype_p ? tf_decltype : 0));
+ if (parser->omp_has_array_shape_p
+ && (expression_list.get () == NULL
+ || vec_safe_length (expression_list) == 1))
+ /* If we have an array-shaping operator, we may not be able to represent
+ a well-formed ARRAY_REF here, because we are coercing the type of the
+ innermost array base and the original type may not be compatible. Use
+ the OMP_ARRAY_SECTION code instead. We also want to explicitly avoid
+ creating INDIRECT_REFs for pointer bases, because that can lead to
+ parsing ambiguities (see cp_parser_omp_var_list_no_open). */
+ return grok_omp_array_section (loc, postfix_expression, index,
+ size_one_node, NULL_TREE);
+ else
+ /* Build the ARRAY_REF. */
+ postfix_expression = grok_array_decl (loc, postfix_expression,
+ index, &expression_list,
+ tf_warning_or_error
+ | (decltype_p ? tf_decltype : 0));
/* When not doing offsetof, array references are not permitted in
constant-expressions. */
@@ -9101,6 +9182,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
vec<tree, va_gc> *expression_list;
bool saved_greater_than_is_operator_p;
bool saved_omp_array_section_p;
+ bool saved_omp_array_shaping_op_p;
/* Assume all the expressions will be constant. */
if (non_constant_p)
@@ -9119,7 +9201,9 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
parser->greater_than_is_operator_p = true;
saved_omp_array_section_p = parser->omp_array_section_p;
+ saved_omp_array_shaping_op_p = parser->omp_array_shaping_op_p;
parser->omp_array_section_p = false;
+ parser->omp_array_shaping_op_p = false;
cp_expr expr (NULL_TREE);
@@ -9203,6 +9287,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
parser->greater_than_is_operator_p
= saved_greater_than_is_operator_p;
parser->omp_array_section_p = saved_omp_array_section_p;
+ parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
return NULL;
}
}
@@ -9210,6 +9295,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
parser->greater_than_is_operator_p
= saved_greater_than_is_operator_p;
parser->omp_array_section_p = saved_omp_array_section_p;
+ parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
return expression_list;
}
@@ -10505,6 +10591,8 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
cp_expr expr (NULL_TREE);
int cast_expression = 0;
const char *saved_message;
+ auto_vec<cp_expr, 4> omp_shape_dims;
+ bool omp_array_shape_p = false;
/* There's no way to know yet whether or not this is a cast.
For example, `(int (3))' is a unary-expression, while `(int)
@@ -10574,6 +10662,28 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
that the call to cp_parser_error_occurred below returns true. */
if (!cast_expression)
cp_parser_simulate_error (parser);
+ else if (parser->omp_array_shaping_op_p
+ && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+ {
+ auto oas = make_temp_override (parser->omp_array_section_p, false);
+ auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
+
+ while (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+ {
+ cp_lexer_consume_token (parser->lexer);
+ cp_expr e = cp_parser_expression (parser);
+ if (e.get_value () == error_mark_node)
+ break;
+ omp_shape_dims.safe_push (e);
+ if (!cp_parser_require (parser, CPP_CLOSE_SQUARE,
+ RT_CLOSE_SQUARE))
+ break;
+ }
+ cp_token *close_paren = parens.require_close (parser);
+ if (close_paren)
+ close_paren_loc = close_paren->location;
+ omp_array_shape_p = true;
+ }
else
{
type_id_in_expr_sentinel s (parser);
@@ -10593,6 +10703,10 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
function returning T. */
if (!cp_parser_error_occurred (parser))
{
+ auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
+ auto as = make_temp_override (parser->omp_has_array_shape_p,
+ omp_array_shape_p);
+
/* Only commit if the cast-expression doesn't start with
'++', '--', or '[' in C++11. */
if (cast_expression > 0)
@@ -10606,6 +10720,24 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
if (cp_parser_parse_definitely (parser))
{
+ if (omp_array_shape_p)
+ {
+ location_t cast_loc = make_location (open_paren_loc,
+ open_paren_loc,
+ expr.get_finish ());
+
+ type = cp_omp_create_arrayshape_type (cast_loc, expr,
+ &omp_shape_dims);
+
+ /* Things rapidly get worse below if we carry on from here
+ with an erroneous type... */
+ if (error_operand_p (type))
+ return error_mark_node;
+
+ return cp_build_omp_arrayshape_cast (cast_loc, type, expr,
+ tf_warning_or_error);
+ }
+
/* Warn about old-style casts, if so requested. */
if (warn_old_style_cast
&& !in_system_header_at (input_location)
@@ -11776,6 +11908,7 @@ cp_parser_lambda_expression (cp_parser* parser)
bool auto_is_implicit_function_template_parm_p
= parser->auto_is_implicit_function_template_parm_p;
bool saved_omp_array_section_p = parser->omp_array_section_p;
+ bool saved_omp_array_shaping_op_p = parser->omp_array_shaping_op_p;
parser->num_template_parameter_lists = 0;
parser->in_statement = 0;
@@ -11785,6 +11918,7 @@ cp_parser_lambda_expression (cp_parser* parser)
parser->implicit_template_scope = 0;
parser->auto_is_implicit_function_template_parm_p = false;
parser->omp_array_section_p = false;
+ parser->omp_array_shaping_op_p = false;
/* Inside the lambda, outside unevaluated context do not apply. */
cp_evaluated ev;
@@ -11839,6 +11973,7 @@ cp_parser_lambda_expression (cp_parser* parser)
parser->auto_is_implicit_function_template_parm_p
= auto_is_implicit_function_template_parm_p;
parser->omp_array_section_p = saved_omp_array_section_p;
+ parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
}
/* This lambda shouldn't have any proxies left at this point. */
@@ -23781,6 +23916,226 @@ cp_parser_maybe_adjust_declarator_for_dguide (cp_parser *parser,
}
}
+/* Helper function for OpenMP "begin declare variant" directives.
+ Function definitions inside the construct need to have their names
+ mangled according to the context selector CTX. The DECLARATOR is
+ modified in place to point to a new identifier; the original name of
+ the function is returned. */
+static tree
+omp_start_variant_function (cp_declarator *declarator, tree ctx)
+{
+ cp_declarator *id = get_id_declarator (declarator);
+ tree name = id->u.id.unqualified_name;
+ tree scope = id->u.id.qualifying_scope;
+ enum special_function_kind sfk = id->u.id.sfk;
+
+ /* There seems to be no reasonable interpretation of what the behavior
+ should be if the name is qualified. You cannot add the variant function
+ to a class or namespace from outside of that scope. */
+ if (scope)
+ {
+ sorry_at (id->id_loc,
+ "cannot handle qualified name for variant function");
+ return NULL_TREE;
+ }
+
+ /* Catch disallowed constructors and destructors now. We can't mangle
+ destructor names (which are not IDENTIFIER_NODEs) in any case. */
+ if (sfk == sfk_constructor)
+ {
+ error_at (id->id_loc,
+ "declare variant directives are not allowed on constructors");
+ return NULL_TREE;
+ }
+ if (sfk == sfk_destructor)
+ {
+ error_at (id->id_loc,
+ "declare variant directives are not allowed on destructors");
+ return NULL_TREE;
+ }
+ if (TREE_CODE (name) != IDENTIFIER_NODE)
+ {
+ sorry_at (id->id_loc,
+ "cannot handle %s identifier name",
+ get_tree_code_name (TREE_CODE (name)));
+ return NULL_TREE;
+ }
+
+ /* Mangle the name in the declarator. */
+ id->u.id.unqualified_name
+ = omp_mangle_variant_name (name, ctx, JOIN_STR);
+
+ return name;
+}
+
+/* Helper function for OpenMP "begin declare variant" directives. Now
+ that we have a DECL for the variant function, and BASE_NAME for the
+ base function, look up the decl for BASE_NAME in the same scope as
+ DECL, add an "omp declare variant base" attribute pointing at CTX
+ to the base decl, and an "omp declare variant variant" attribute to
+ the variant DECL. */
+static void
+omp_finish_variant_function (cp_parser *parser, tree decl, tree base_name,
+ tree ctx)
+{
+ tree match = NULL_TREE;
+ bool is_template = false;
+ tree decl_context = CP_DECL_CONTEXT (decl);
+
+ /* First find the base_decl. */
+ tree base_decl = cp_parser_lookup_name_simple (parser, base_name,
+ DECL_SOURCE_LOCATION (decl));
+
+ if (base_decl == error_mark_node)
+ base_decl = NULL_TREE;
+ if (!base_decl)
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "no previous declaration of base function in this scope");
+ return;
+ }
+
+ /* Find the right overloaded function. */
+ if (TREE_CODE (base_decl) == OVERLOAD)
+ {
+ for (ovl_iterator iter (base_decl); iter; ++iter)
+ {
+ tree bb = *iter;
+ if (decls_match (decl, bb))
+ {
+ match = bb;
+ break;
+ }
+ else if (TREE_CODE (bb) == TEMPLATE_DECL
+ && TREE_CODE (decl) == FUNCTION_DECL
+ && DECL_TEMPLATE_INFO (decl))
+ {
+ tree decl_template = DECL_TI_TEMPLATE (decl);
+ if (decl_template
+ && PRIMARY_TEMPLATE_P (decl_template)
+ && decls_match (bb, decl_template))
+ {
+ /* We want to put the attributes on the function rather
+ than on the TEMPLATE_DECL that points to it. */
+ match = DECL_TEMPLATE_RESULT (bb);
+ is_template = true;
+ break;
+ }
+ }
+ }
+ }
+ else if (decls_match (decl, base_decl))
+ match = base_decl;
+ else if (TREE_CODE (base_decl) == TEMPLATE_DECL)
+ /* Per comment in cp-tree.h, TEMPLATE_DECLs are always wrapped in an
+ OVERLOAD, so we should never see them here. */
+ gcc_unreachable ();
+ else if (TREE_CODE (base_decl) == TREE_LIST)
+ {
+ error_at (DECL_SOURCE_LOCATION (decl), "base function is ambiguous");
+ return;
+ }
+ else if (TREE_CODE (base_decl) == SCOPE_REF)
+ {
+ /* This shows up in some cases involving templates; it's apparently a
+ placeholder for names that can't be matched to a declaration
+ until template instantiation. */
+ sorry_at (DECL_SOURCE_LOCATION (decl),
+ "base function cannot be resolved");
+ return;
+ }
+
+ if (!match)
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "variant function definition does not match previous "
+ "declaration of %qE", base_decl);
+ return;
+ }
+ else if (CP_DECL_CONTEXT (match) != decl_context)
+ {
+ /* Reject inherited or using decls. */
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "variant function must be in the same scope as the "
+ "base function %qE", match);
+ return;
+ }
+ else if (DECL_VIRTUAL_P (decl) || DECL_VIRTUAL_P (match))
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "declare variant directives are not allowed on "
+ "virtual functions");
+ return;
+ }
+ else if (DECL_DEFAULTED_FN (decl) || DECL_DEFAULTED_FN (match))
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "declare variant directives are not allowed on "
+ "defaulted functions");
+ return;
+ }
+ else if (DECL_DELETED_FN (decl) || DECL_DELETED_FN (match))
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "declare variant directives are not allowed on "
+ "deleted functions");
+ return;
+ }
+ else if (DECL_IMMEDIATE_FUNCTION_P (decl)
+ || DECL_IMMEDIATE_FUNCTION_P (match))
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "declare variant directives are not allowed on "
+ "immediate functions");
+ return;
+ }
+
+ /* Inside a template, make the "omp declare variant base" attribute
+ point to the name of DECL rather than DECL itself. During template
+ instantiation, omp_declare_variant_finalize_one will handle this
+ using the same logic as for the non-delimited form of "declare variant",
+ causing template instantiation as needed. For the non-template case,
+ there is nothing that will trigger omp_declare_variant_finalize_one;
+ so we create the final form of the attribute here, which points
+ directly to DECL rather than its name. */
+ tree decl_or_name = decl;
+ cp_id_kind idk = CP_ID_KIND_NONE;
+ if (processing_template_decl && is_template)
+ {
+ decl_or_name = DECL_NAME (decl);
+ idk = CP_ID_KIND_TEMPLATE_ID;
+ }
+
+ omp_check_for_duplicate_variant (DECL_SOURCE_LOCATION (decl),
+ match, ctx);
+ tree construct
+ = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT);
+ omp_mark_declare_variant (DECL_SOURCE_LOCATION (decl), decl, construct);
+
+ tree attrs = DECL_ATTRIBUTES (match);
+ tree match_loc_node
+ = maybe_wrap_with_location (integer_zero_node,
+ DECL_SOURCE_LOCATION (match));
+ tree loc_node = tree_cons (match_loc_node,
+ build_int_cst (integer_type_node, idk),
+ build_tree_list (match_loc_node,
+ integer_zero_node));
+ attrs = tree_cons (get_identifier ("omp declare variant base"),
+ tree_cons (decl_or_name, ctx, loc_node), attrs);
+ if (processing_template_decl)
+ ATTR_IS_DEPENDENT (attrs) = 1;
+ DECL_ATTRIBUTES (match) = attrs;
+
+ /* Variant functions are essentially anonymous and cannot be
+ referenced by name, so make them have internal linkage. Note
+ that class methods in C++ normally have external linkage with
+ weak/comdat semantics; this prevents that. */
+ TREE_PUBLIC (decl) = 0;
+ DECL_COMDAT (decl) = 0;
+ DECL_INTERFACE_KNOWN (decl) = 1;
+ DECL_NOT_REALLY_EXTERN (decl) = 1;
+}
+
/* Declarators [gram.dcl.decl] */
/* Parse an init-declarator.
@@ -23997,6 +24352,27 @@ cp_parser_init_declarator (cp_parser* parser,
/* This is a function-definition. */
*function_definition_p = true;
+ /* If we're in an OpenMP "begin declare variant" block, the
+ name in the declarator refers to the base function. We need
+ to save that and modify the declarator to have the mangled
+ name for the variant function instead. */
+ tree dv_base = NULL_TREE;
+ tree dv_ctx = NULL_TREE;
+ vec<cp_omp_declare_variant_attr, va_gc> *dv_state
+ = scope_chain->omp_declare_variant_attribute;
+
+ if (!vec_safe_is_empty (dv_state))
+ {
+ cp_omp_declare_variant_attr a = dv_state->last ();
+ dv_ctx = copy_list (a.selector);
+ dv_base = omp_start_variant_function (declarator, dv_ctx);
+ if (dv_base == NULL_TREE)
+ {
+ cp_parser_skip_to_end_of_statement (parser);
+ return error_mark_node;
+ }
+ }
+
/* Parse the function definition. */
if (member_p)
decl = cp_parser_save_member_function_body (parser,
@@ -24015,6 +24391,11 @@ cp_parser_init_declarator (cp_parser* parser,
= func_brace_location;
}
+ /* If this function was in a "begin declare variant" block,
+ store the pointer back to the base function and fix up
+ the attributes for the middle end. */
+ if (dv_base && decl != error_mark_node)
+ omp_finish_variant_function (parser, decl, dv_base, dv_ctx);
return decl;
}
}
@@ -24092,6 +24473,27 @@ cp_parser_init_declarator (cp_parser* parser,
is_initialized = SD_DEFAULTED;
else if (t2->keyword == RID_DELETE)
is_initialized = SD_DELETED;
+ if (!vec_safe_is_empty (scope_chain->omp_declare_variant_attribute))
+ {
+ /* We're in a "begin declare variant" construct. The parser
+ doesn't go through the normal function definition path for
+ these and hence doesn't invoke omp_finish_variant_function
+ where these errors would otherwise be caught. */
+ if (is_initialized == SD_DEFAULTED)
+ {
+ error_at (declarator->init_loc,
+ "declare variant directives are not allowed on "
+ "defaulted functions");
+ return error_mark_node;
+ }
+ else if (is_initialized == SD_DELETED)
+ {
+ error_at (declarator->init_loc,
+ "declare variant directives are not allowed on "
+ "deleted functions");
+ return error_mark_node;
+ }
+ }
}
}
else
@@ -24664,7 +25066,7 @@ cp_parser_direct_declarator (cp_parser* parser,
tree requires_clause = NULL_TREE;
late_return
= cp_parser_late_return_type_opt (parser, declarator,
- requires_clause, params);
+ requires_clause);
cp_finalize_omp_declare_simd (parser, &odsd);
@@ -25530,7 +25932,7 @@ parsing_function_declarator ()
static tree
cp_parser_late_return_type_opt (cp_parser *parser, cp_declarator *declarator,
- tree &requires_clause, tree parms)
+ tree &requires_clause)
{
cp_token *token;
tree type = NULL_TREE;
@@ -25586,8 +25988,8 @@ cp_parser_late_return_type_opt (cp_parser *parser, cp_declarator *declarator,
if (declare_simd_p)
declarator->attributes
- = cp_parser_late_parsing_omp_declare_simd (parser, declarator->attributes,
- parms);
+ = cp_parser_late_parsing_omp_declare_simd (parser,
+ declarator->attributes);
if (oacc_routine_p)
declarator->attributes
= cp_parser_late_parsing_oacc_routine (parser,
@@ -26892,6 +27294,7 @@ cp_parser_braced_list (cp_parser *parser, bool *non_constant_p /*=nullptr*/)
tree initializer;
location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
auto oas = make_temp_override (parser->omp_array_section_p, false);
+ auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
/* Consume the `{' token. */
matching_braces braces;
@@ -27565,6 +27968,10 @@ cp_parser_class_specifier (cp_parser* parser)
tree saved_ccr = current_class_ref;
current_class_ptr = NULL_TREE;
current_class_ref = NULL_TREE;
+ /* Set up for deferred lookup of "omp begin declare variant" base functions
+ in the class. */
+ tree save_unregistered_variants = parser->omp_unregistered_variants;
+ parser->omp_unregistered_variants = NULL_TREE;
/* Start the class. */
if (nested_name_specifier_p)
@@ -27586,6 +27993,19 @@ cp_parser_class_specifier (cp_parser* parser)
/* Parse the member-specification. */
cp_parser_member_specification_opt (parser);
+ /* Register any "begin declare variant" functions in this class, since
+ references to the base function can only be resolved after the
+ entire class is seen. */
+ for (tree bdv = parser->omp_unregistered_variants; bdv;
+ bdv = TREE_CHAIN (bdv))
+ {
+ tree dv_base = TREE_PURPOSE (TREE_PURPOSE (bdv));
+ tree dv_ctx = TREE_VALUE (TREE_PURPOSE (bdv));
+ tree dv_decl = TREE_VALUE (bdv);
+ omp_finish_variant_function (parser, dv_decl, dv_base, dv_ctx);
+ }
+ parser->omp_unregistered_variants = save_unregistered_variants;
+
/* Look for the trailing `}'. */
closing_brace = braces.require_close (parser);
/* Look for trailing attributes to apply to this class. */
@@ -29248,6 +29668,28 @@ cp_parser_member_declaration (cp_parser* parser)
if (initializer && initializer_token_start)
error_at (initializer_token_start->location,
"pure-specifier on function-definition");
+
+ /* If we're in an OpenMP "begin declare variant" block,
+ the name in the declarator refers to the base function.
+ We need to save that and modify the declarator to have
+ the mangled name for the variant function instead. */
+ tree dv_base = NULL_TREE;
+ tree dv_ctx = NULL_TREE;
+ vec<cp_omp_declare_variant_attr, va_gc> *dv_state
+ = scope_chain->omp_declare_variant_attribute;
+ if (!vec_safe_is_empty (dv_state))
+ {
+ cp_omp_declare_variant_attr a = dv_state->last ();
+ dv_ctx = copy_list (a.selector);
+ dv_base = omp_start_variant_function (declarator,
+ dv_ctx);
+ if (dv_base == NULL_TREE)
+ {
+ cp_parser_skip_to_end_of_statement (parser);
+ goto out;
+ }
+ }
+
decl = cp_parser_save_member_function_body (parser,
&decl_specifiers,
declarator,
@@ -29258,6 +29700,19 @@ cp_parser_member_declaration (cp_parser* parser)
/* If the member was not a friend, declare it here. */
if (!friend_p)
finish_member_declaration (decl);
+
+ /* If this function was in a "begin declare variant"
+ block, record the information we need to find the
+ base function and fix it up later. At this point in
+ parsing, we may not have seen the base function yet
+ so we defer looking it up and registering the variant
+ until the class is complete. */
+ if (dv_base && decl != error_mark_node)
+ parser->omp_unregistered_variants
+ = tree_cons (tree_cons (dv_base, dv_ctx, NULL_TREE),
+ decl,
+ parser->omp_unregistered_variants);
+
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
/* If the next token is a semicolon, consume it. */
@@ -38927,6 +39382,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
result = PRAGMA_OMP_CLAUSE_USE_DEVICE_ADDR;
else if (!strcmp ("use_device_ptr", p))
result = PRAGMA_OMP_CLAUSE_USE_DEVICE_PTR;
+ else if (!strcmp ("uses_allocators", p))
+ result = PRAGMA_OMP_CLAUSE_USES_ALLOCATORS;
break;
case 'v':
if (!strcmp ("vector", p))
@@ -38983,16 +39440,17 @@ check_no_duplicate_clause (tree clauses, enum omp_clause_code code,
struct omp_dim
{
- tree low_bound, length;
+ tree low_bound, length, stride;
location_t loc;
bool no_colon;
- omp_dim (tree lb, tree len, location_t lo, bool nc)
- : low_bound (lb), length (len), loc (lo), no_colon (nc) {}
+ omp_dim (tree lb, tree len, tree str, location_t lo, bool nc)
+ : low_bound (lb), length (len), stride (str), loc (lo), no_colon (nc) {}
};
static tree
cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
tree list, bool *colon,
+ enum c_omp_region_type ort = C_ORT_OMP,
bool map_lvalue = false)
{
auto_vec<omp_dim> dims;
@@ -39019,10 +39477,23 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
|| kind == OMP_CLAUSE_FROM))
{
auto s = make_temp_override (parser->omp_array_section_p, true);
+ auto o = make_temp_override (parser->omp_array_shaping_op_p,
+ (kind == OMP_CLAUSE_TO
+ || kind == OMP_CLAUSE_FROM
+ || ort == C_ORT_OMP_DECLARE_MAPPER));
+ tree reshaped_to = NULL_TREE;
token = cp_lexer_peek_token (parser->lexer);
location_t loc = token->location;
decl = cp_parser_assignment_expression (parser);
+ if ((TREE_CODE (decl) == VIEW_CONVERT_EXPR
+ && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+ || TREE_CODE (decl) == OMP_ARRAYSHAPE_CAST_EXPR)
+ {
+ reshaped_to = TREE_TYPE (decl);
+ decl = TREE_OPERAND (decl, 0);
+ }
+
/* This code rewrites a parsed expression containing various tree
codes used to represent array accesses into a more uniform nest of
OMP_ARRAY_SECTION nodes before it is processed by
@@ -39033,49 +39504,159 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
dims.truncate (0);
if (TREE_CODE (decl) == OMP_ARRAY_SECTION)
{
+ size_t sections = 0;
+ tree orig_decl = decl;
+ bool update_p = (kind == OMP_CLAUSE_TO
+ || kind == OMP_CLAUSE_FROM);
+ bool maybe_ptr_based_noncontig_update = false;
+
+ while (update_p
+ && !reshaped_to
+ && (TREE_CODE (decl) == OMP_ARRAY_SECTION
+ || TREE_CODE (decl) == ARRAY_REF
+ || TREE_CODE (decl) == COMPOUND_EXPR))
+ {
+ if (TREE_CODE (decl) == COMPOUND_EXPR)
+ decl = TREE_OPERAND (decl, 1);
+ else
+ {
+ if (TREE_CODE (decl) == OMP_ARRAY_SECTION)
+ maybe_ptr_based_noncontig_update = true;
+ decl = TREE_OPERAND (decl, 0);
+ sections++;
+ }
+ }
+
+ decl = orig_decl;
+
while (TREE_CODE (decl) == OMP_ARRAY_SECTION)
{
tree low_bound = TREE_OPERAND (decl, 1);
tree length = TREE_OPERAND (decl, 2);
- dims.safe_push (omp_dim (low_bound, length, loc, false));
+ tree stride = TREE_OPERAND (decl, 3);
+ dims.safe_push (omp_dim (low_bound, length, stride, loc,
+ false));
decl = TREE_OPERAND (decl, 0);
+ if (sections > 0)
+ sections--;
}
+ /* The handling of INDIRECT_REF here in the presence of
+ array-shaping operations is a little tricky. We need to
+ avoid treating a pointer dereference as a unit-sized array
+ section when we have an array shaping operation, because we
+ don't want an indirection to consume one of the user's
+ requested array dimensions. E.g. if we have a
+ double-indirect pointer like:
+
+ int **foopp;
+ #pragma omp target update from(([N][N]) (*foopp)[0:X][0:Y])
+
+ We don't want to interpret this as:
+
+ foopp[0:1][0:X][0:Y]
+
+ else the array shape [N][N] won't match. Also we can't match
+ the array sections right-to-left instead, else this:
+
+ #pragma omp target update from(([N][N]) (*foopp)[0:X])
+
+ would not copy the dimensions:
+
+ (*foopp)[0:X][0:N]
+
+ as required. So, avoid descending through INDIRECT_REFs if
+ we have an array-shaping op.
+
+ If we *don't* have an array-shaping op, but we have a
+ multiply-indirected pointer and an array section like this:
+
+ int ***fooppp;
+ #pragma omp target update from((**fooppp)[0:X:S]
+
+ also avoid descending through more indirections than we have
+ array sections, since the noncontiguous update processing code
+ won't understand them (and doesn't need to traverse them
+ anyway). */
+
while (TREE_CODE (decl) == ARRAY_REF
- || TREE_CODE (decl) == INDIRECT_REF
+ || (TREE_CODE (decl) == INDIRECT_REF
+ && !reshaped_to)
|| TREE_CODE (decl) == COMPOUND_EXPR)
{
if (REFERENCE_REF_P (decl))
break;
+ if (maybe_ptr_based_noncontig_update && sections == 0)
+ break;
+
if (TREE_CODE (decl) == COMPOUND_EXPR)
{
decl = TREE_OPERAND (decl, 1);
STRIP_NOPS (decl);
+ continue;
}
- else if (TREE_CODE (decl) == INDIRECT_REF)
+ else if (TREE_CODE (decl) == INDIRECT_REF
+ && !reshaped_to)
{
dims.safe_push (omp_dim (integer_zero_node,
- integer_one_node, loc, true));
+ integer_one_node, NULL_TREE, loc,
+ true));
decl = TREE_OPERAND (decl, 0);
}
else /* ARRAY_REF. */
{
tree index = TREE_OPERAND (decl, 1);
- dims.safe_push (omp_dim (index, integer_one_node, loc,
- true));
+ dims.safe_push (omp_dim (index, integer_one_node,
+ NULL_TREE, loc, true));
decl = TREE_OPERAND (decl, 0);
+ if (sections > 0)
+ sections--;
}
}
+ if (reshaped_to)
+ {
+ unsigned reshaped_dims = 0;
+
+ for (tree t = reshaped_to;
+ TREE_CODE (t) == ARRAY_TYPE;
+ t = TREE_TYPE (t))
+ reshaped_dims++;
+
+ if (dims.length () > reshaped_dims)
+ {
+ error_at (loc, "too many array section specifiers "
+ "for %qT", reshaped_to);
+ decl = error_mark_node;
+ }
+ else
+ {
+ /* We have a pointer DECL whose target should be
+ interpreted as an array with particular dimensions,
+ not "the pointer itself". So, add an indirection
+ here. */
+ if (type_dependent_expression_p (decl))
+ decl = build_min_nt_loc (loc, INDIRECT_REF, decl);
+ else
+ {
+ /* We're interested in the reference target. */
+ decl = convert_from_reference (decl);
+ decl = cp_build_fold_indirect_ref (decl);
+ }
+ decl
+ = cp_build_omp_arrayshape_cast (loc, reshaped_to, decl,
+ tf_warning_or_error);
+ }
+ }
/* Bare references have their own special handling, so remove
the explicit dereference added by convert_from_reference. */
- if (REFERENCE_REF_P (decl))
+ else if (REFERENCE_REF_P (decl))
decl = TREE_OPERAND (decl, 0);
for (int i = dims.length () - 1; i >= 0; i--)
decl = grok_omp_array_section (loc, decl, dims[i].low_bound,
- dims[i].length);
+ dims[i].length, dims[i].stride);
}
else if (TREE_CODE (decl) == INDIRECT_REF)
{
@@ -39091,7 +39672,7 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
if (!ref_p)
decl = grok_omp_array_section (loc, decl, integer_zero_node,
- integer_one_node);
+ integer_one_node, NULL_TREE);
}
else if (TREE_CODE (decl) == ARRAY_REF)
{
@@ -39100,7 +39681,16 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
decl = TREE_OPERAND (decl, 0);
STRIP_NOPS (decl);
- decl = grok_omp_array_section (loc, decl, idx, integer_one_node);
+ decl = grok_omp_array_section (loc, decl, idx, integer_one_node,
+ NULL_TREE);
+ }
+ else if (reshaped_to)
+ {
+ /* We're copying the whole of a reshaped array, originally a
+ base pointer. Rewrite as an array section. */
+ tree elems = array_type_nelts_total (reshaped_to);
+ decl = grok_omp_array_section (loc, decl, size_zero_node, elems,
+ NULL_TREE);
}
else if (TREE_CODE (decl) == NON_LVALUE_EXPR
|| CONVERT_EXPR_P (decl))
@@ -39264,7 +39854,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
goto skip_comma;
}
- dims.safe_push (omp_dim (low_bound, length, loc, no_colon));
+ dims.safe_push (omp_dim (low_bound, length, NULL_TREE, loc,
+ no_colon));
}
if ((kind == OMP_CLAUSE_MAP
@@ -39286,7 +39877,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
for (unsigned i = 0; i < dims.length (); i++)
decl = build_omp_array_section (input_location, decl,
dims[i].low_bound,
- dims[i].length);
+ dims[i].length,
+ dims[i].stride);
break;
default:
break;
@@ -39299,6 +39891,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
&& cp_parser_simulate_error (parser))
{
depend_lvalue:
+ auto o = make_temp_override (parser->omp_array_shaping_op_p,
+ true);
cp_parser_abort_tentative_parse (parser);
decl = cp_parser_assignment_expression (parser, NULL,
false, false);
@@ -39357,6 +39951,7 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
static tree
cp_parser_omp_var_list (cp_parser *parser, enum omp_clause_code kind, tree list,
+ enum c_omp_region_type ort = C_ORT_OMP,
bool map_lvalue = false)
{
if (parser->lexer->in_omp_decl_attribute)
@@ -39375,11 +39970,422 @@ cp_parser_omp_var_list (cp_parser *parser, enum omp_clause_code kind, tree list,
}
if (cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
- return cp_parser_omp_var_list_no_open (parser, kind, list, NULL,
+ return cp_parser_omp_var_list_no_open (parser, kind, list, NULL, ort,
map_lvalue);
return list;
}
+/* Parse an OpenMP parameter-list.
+ parameter-list:
+ parameter-list-item[, parameter-list-item [, ...]]
+
+ parameter-list-item:
+ named parameter list item
+ parameter index (1 based)
+ numeric-range
+
+ numeric-range:
+ [bound]:[bound]
+
+ bound:
+ index-expr
+ omp_num_args[±logical_offset]
+
+ A named parameter list item is the name of a parameter. A parameter index
+ is a positive integer literal that is the 1 based index of a parameter.
+ A numeric-range is a pair of bounds of the form lb:ub, the values of each
+ bound form a closed interval of parameter indices. Bounds can be literal or
+ relative. An index-expr is a non-negative integer constant-expression that
+ is the value of a literal bound. The special identifier omp_num_args is
+ equal to the number of arguments passed to the function at the call site,
+ including the number of varargs. Optionally, a plus or minus with a
+ logical_offset may follow omp_num_args, logical_offset is a non-negative
+ integer constant-expression. A bound formed with omp_num_args is a relative
+ bound. If a bound is omitted, a default value is used. The default value
+ of lb is as if 1 were specified, the default value of ub is as if
+ omp_num_args were specified.
+
+ Each parameter-list-item is stored in a TREE_LIST. The PURPOSE is for
+ general use and left NULL_TREE here, and the item is stored in the VALUE.
+ An item is a TREE_LIST, the PURPOSE is an expression with the location of
+ the list item, and the VALUE is a representation of the item.
+ Each parameter-list-item is stored in a TREE_LIST node VALUE. The PURPOSE
+ is unused, and the VALUE is the item-repr.
+
+ Node - PUPOSE: NULL_TREE
+ - VALUE: item-with-location
+ item-with-location - PURPOSE: expr-with-location
+ - VALUE: item-repr
+
+ An item-repr is a PARM_DECL, a NOP_EXPR, or a TREE_LIST. A PARM_DECL is a
+ named parameter list item. A NOP_EXPR is the unadjusted 1-based parameter
+ index. A TREE_LIST is a numeric-range where its PURPOSE is a TREE_LIST
+ representing the lb, and its VALUE is a TREE_LIST representing the ub.
+
+ item-repr
+ PARM_DECL - parameter name
+ NOP_EXPR - parameter index (1 based)
+ TREE_LIST - PURPOSE: TREE_LIST (lb)
+ - VALUE: TREE_LIST (ub)
+
+ lb and ub are a TREE_LIST of the following form;
+ TREE_LIST - PURPOSE: relative bound marker (NULL_TREE if literal)
+ - VALUE: expr-value
+
+ This function strictly handles a parameter-list, it does not parse clause
+ modifiers, or parenthesis other than in the expr of a numeric range.
+
+ If a diagnostic is issued for a list item, it is not appened to the list and
+ parsing continues. Returns NULL_TREE if no valid list items are parsed. */
+
+static tree
+cp_parser_omp_parm_list (cp_parser *parser)
+{
+ tree list = NULL_TREE;
+ auto append_to_list = [chain = &list] (tree arg, location_t loc) mutable
+ {
+ gcc_assert (*chain == NULL_TREE);
+ *chain = build_tree_list (NULL_TREE,
+ build_tree_list (build_empty_stmt (loc), arg));
+ chain = &TREE_CHAIN (*chain);
+ };
+
+ auto tok_terminates_item_p = [] (const cp_token *tok)
+ {
+ return tok->type == CPP_COMMA
+ || tok->type == CPP_CLOSE_PAREN;
+ };
+ /* The first list item is (obviously) not preceded by a comma. */
+ goto first_element;
+ do
+ {
+ /* Consume the comma. */
+ cp_lexer_consume_token (parser->lexer);
+ first_element:
+
+ cp_token *const tok = cp_lexer_peek_token (parser->lexer);
+
+ /* OpenMP 6.0 (162:29-34)
+ A parameter list item can be one of the following:
+ • A named parameter list item;
+ • The position of a parameter in a parameter specification specified
+ by a positive integer, where 1 represents the first parameter; or
+ • A parameter range specified by lb : ub where both lb and ub must
+ be an expression of integer OpenMP type with the constant property
+ and the positive property.
+
+ The spec does not support arbitrary expression outside of a numeric
+ range. In theory they could be supported as a parameter index, but
+ for now we do not support that case. */
+
+ /* If we don't see a comma or close paren this can't be a named parameter
+ list item or a parameter index, it can only be a numeric range. */
+ if (!tok_terminates_item_p (cp_lexer_peek_nth_token (parser->lexer, 2))
+ /* Or this edge case, there is a default lower bound. */
+ || tok->type == CPP_COLON)
+ /* Early exit, numeric range case handled below. */;
+ else if (tok->type == CPP_NAME)
+ {
+ if (strcmp (IDENTIFIER_POINTER (tok->u.value), "omp_num_args") == 0)
+ {
+ error_at (tok->location, "%<omp_num_args%> may only be used at "
+ "the start of a numeric range bound");
+ cp_lexer_consume_token (parser->lexer);
+ continue;
+ }
+ /* This might not be the right way to do this, we might want to use
+ cp_parser_lookup_name_simple instead. */
+ tree parm = lookup_name (tok->u.value,
+ LOOK_where::BLOCK,
+ LOOK_want::NORMAL);
+ if (parm && TREE_CODE (parm) == PARM_DECL)
+ {
+ if (DECL_PACK_P (parm))
+ {
+ /* In theory we could just consider every element of the pack
+ as being specified, the spec does not say what to do
+ though. */
+ sorry_at (tok->location,
+ "parameter packs are not supported as an OpenMP "
+ "named parameter list item");
+ inform (DECL_SOURCE_LOCATION (parm),
+ "declared as a pack here");
+ }
+ else
+ append_to_list (parm, tok->location);
+ }
+ else
+ {
+ /* FIXME: Nice diagnostic, potentially using
+ cp_parser_name_lookup_error. */
+ error_at (tok->location,
+ "%qs is not a function parameter",
+ IDENTIFIER_POINTER (tok->u.value));
+ }
+ cp_lexer_consume_token (parser->lexer);
+ continue;
+ }
+ else if (tok->type == CPP_NUMBER)
+ {
+ if (wi::to_widest (tok->u.value) <= 0)
+ {
+ error_at (tok->location,
+ "parameter indices in an OpenMP "
+ "parameter list must be positive");
+ }
+ else if (wi::to_widest (tok->u.value) > INT_MAX)
+ error_at (tok->location, "parameter index is too big");
+ else
+ {
+ /* Don't adjust here, we can't finalize these until we know if we
+ are in a member function or not. We can probably hack this to
+ find out in here, but it belongs in finish_omp_parm_list, not
+ here.
+ FIXME: We have to come up with a better way of transporting
+ these and marking them as unfinalized. Wrapping in a NOP is
+ really quite bad. */
+ tree cst = build_int_cst (integer_type_node,
+ tree_to_shwi (tok->u.value));
+ append_to_list (build_nop (integer_type_node, cst),
+ tok->location);
+ }
+ cp_lexer_consume_token (parser->lexer);
+ continue;
+ }
+ else
+ {
+ gcc_checking_assert (tok_terminates_item_p
+ (cp_lexer_peek_nth_token (parser->lexer, 2)));
+ cp_parser_error (parser, "expected unqualified-id, "
+ "integer, or expression");
+ cp_lexer_consume_token (parser->lexer);
+ continue;
+ }
+ /* We have a numeric range or something ill formed now, this can be
+ an arbitrary expression. */
+
+ /* Empty bounds are delimited differently for lower and upper bounds,
+ handle them without calling parse_bound. */
+ auto parse_bound = [&] () -> tree
+ {
+ location_t bound_start
+ = cp_lexer_peek_token (parser->lexer)->location;
+ enum omp_num_args
+ {
+ num_args_none,
+ num_args_plus,
+ num_args_minus,
+ num_args_no_offset
+ };
+ /* (OpenMP 6.0, 162:35-37)
+ In both lb and ub, an expression using omp_num_args, that enables
+ identification of parameters relative to the last argument of the
+ call, can be used with the form:
+ omp_num_args [± logical_offset] */
+ const omp_num_args parsed_omp_num_args = [&] ()
+ {
+ cp_token *tok = cp_lexer_peek_token (parser->lexer);
+ if (tok->type == CPP_NAME
+ && strcmp (IDENTIFIER_POINTER (tok->u.value), "omp_num_args")
+ == 0)
+ {
+ /* Consume omp_num_args. */
+ cp_lexer_consume_token (parser->lexer);
+ cp_token *op_tok = cp_lexer_peek_token (parser->lexer);
+ if (op_tok->type == CPP_PLUS)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ return num_args_plus;
+ }
+ else if (op_tok->type == CPP_MINUS)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ return num_args_minus;
+ }
+ return num_args_no_offset;
+ }
+ else
+ return num_args_none;
+ } (); /* IILE. */
+ /* If there was omp_num_args but no operator an expr is not
+ permitted, we are finished with this bound. */
+ if (parsed_omp_num_args == num_args_no_offset)
+ {
+ tree cst = build_zero_cst (integer_type_node);
+ /* I hate this hack. We don't know if we are parsing a lb or ub,
+ so even though we know it's value we have to wait until later
+ to finalize it. */
+ return build_tree_list (get_identifier ("omp num args plus"),
+ build1_loc (bound_start,
+ NOP_EXPR,
+ integer_type_node,
+ cst));
+ }
+ const bool saved_flag = parser->colon_corrects_to_scope_p;
+ /* Disable this diagnostic to parse id:id cases such as
+ 'V:omp_num_args' where V is a constant expression variable. */
+ parser->colon_corrects_to_scope_p = false;
+ /* Function arguments are considered an assignment-expression by the
+ C++ standard, it seems to me that those semantics match what we
+ want from an expr in lb or ub. */
+ cp_expr expr = cp_parser_assignment_expression (parser);
+ parser->colon_corrects_to_scope_p = saved_flag;
+
+ if (!expr || expr == error_mark_node)
+ return error_mark_node;
+
+ auto finish_bound_expr = [&parsed_omp_num_args] (cp_expr expr_in)
+ {
+ const location_t loc = expr_in.get_location ();
+ tree expr = expr_in.get_value ();
+ /* Try to fold early if expr is not dependent. I'm pretty sure
+ this should be manifestly constant-evaluated. We require a
+ constant here, let fold_non_dependent_expr complain, but
+ handle everything else in finish_omp_parm_list. */
+ if (!value_dependent_expression_p (expr))
+ {
+ expr = fold_non_dependent_expr (expr,
+ tf_warning_or_error,
+ true);
+ if (!expr || error_operand_p (expr))
+ {
+ if (parsed_omp_num_args != num_args_none)
+ error_at (loc, "logical offset of a bound must "
+ "be a constant expression");
+ else
+ error_at (loc, "expression of a bound must be a "
+ "constant expression");
+ return error_mark_node;
+ }
+ }
+ /* We need a way to signal that an expr has not been adjusted,
+ the best way I came up with is checking if it is an
+ INTEGER_CST, but if we already have an INTEGER_CST at this
+ point, what now? Wrap it in a nop, that's what. */
+ if (TREE_CODE (expr) == INTEGER_CST)
+ return build1_loc (loc, NOP_EXPR, TREE_TYPE (expr), expr);
+ /* We still need this for things like template parameters. */
+ auto maybe_force_wrap_with_location = [&] ()
+ {
+ if (!expr
+ || error_operand_p (expr)
+ || CAN_HAVE_LOCATION_P (expr))
+ return expr;
+ /* Pulled from maybe_wrap_with_location. */
+ const tree_code code
+ = ((CONSTANT_CLASS_P (expr)
+ && TREE_CODE (expr) != STRING_CST)
+ || (TREE_CODE (expr) == CONST_DECL
+ && !TREE_STATIC (expr)))
+ ? NON_LVALUE_EXPR : VIEW_CONVERT_EXPR;
+ tree wrap = build1_loc (loc, code, TREE_TYPE (expr), expr);
+ EXPR_LOCATION_WRAPPER_P (wrap) = 1;
+ return wrap;
+ };
+ return maybe_force_wrap_with_location ();
+ };
+
+ gcc_assert (parsed_omp_num_args < num_args_no_offset);
+ switch (parsed_omp_num_args)
+ {
+ case num_args_none:
+ /* NULL_TREE represents literal. */
+ return build_tree_list (NULL_TREE,
+ finish_bound_expr (expr));
+ case num_args_plus:
+ return build_tree_list (get_identifier ("omp num args plus"),
+ finish_bound_expr (expr));
+ case num_args_minus:
+ return build_tree_list (get_identifier ("omp num args minus"),
+ finish_bound_expr (expr));
+ case num_args_no_offset:
+ /* Handled above. */
+ default:
+ gcc_unreachable ();
+ }
+ gcc_unreachable ();
+ };
+ /* I'm not happy with the state of diagnostics here, but I'm not sure how
+ to fix it so it's best to wait to see which cases end up giving really
+ unclear errors. */
+ location_t num_range_loc_begin
+ = cp_lexer_peek_token (parser->lexer)->location;
+ /* As stated above, empty bounds are handled here. */
+ tree lower_bound = cp_lexer_next_token_is (parser->lexer, CPP_COLON)
+ ? NULL_TREE : parse_bound ();
+ /* I wish we could error here saying that we expect an unqualified-id,
+ an integer, or an expression. Parsing the expression emits the error
+ right away though. Maybe we can do some tentative parsing? */
+ if (lower_bound && error_operand_p (lower_bound))
+ {
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/true,
+ /*consume_paren=*/false);
+ continue;
+ }
+ /* Tokens get consumed by parse_bound. */
+ if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
+ {
+ /* lower_bound can only be null if the next token was a colon. */
+ gcc_assert (lower_bound && !error_operand_p (lower_bound));
+ const cp_token *const next_tok = cp_lexer_peek_token (parser->lexer);
+
+ cp_parser_error (parser, "expected %<:%>");
+ if (tok_terminates_item_p (next_tok))
+ {
+ const location_t loc = make_location (num_range_loc_begin,
+ num_range_loc_begin,
+ input_location);
+ inform (loc, "an expression is only allowed in a numeric range");
+ }
+ /* Do not consume the close paren, this function does not handle
+ that part of the clause. */
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/true,
+ /*consume_paren=*/false);
+ continue;
+ }
+ location_t colon_loc = cp_lexer_consume_token (parser->lexer)->location;
+ tree upper_bound = tok_terminates_item_p
+ (cp_lexer_peek_token (parser->lexer))
+ ? NULL_TREE : parse_bound ();
+
+ /* I think we are supposed to have some sort of diagnostic here, I'm just
+ not sure what it should be. */
+ if (error_operand_p (lower_bound) || error_operand_p (upper_bound))
+ continue;
+
+ location_t num_range_loc_end
+ = upper_bound ? EXPR_LOCATION (TREE_VALUE (upper_bound)) : colon_loc;
+
+ auto build_default_bound = [] (tree num_args_marker, int val)
+ {
+ /* Unfortunately, we can't assume what the final value will be
+ because we don't know if we are in a member function or not. */
+ tree value = build_nop (integer_type_node,
+ build_int_cst (integer_type_node, val));
+ return build_tree_list (num_args_marker, value);
+ };
+ static constexpr int lb_default = 1;
+ /* Internally, 0 + omp_num_args refers to the last arg. */
+ static constexpr int ub_default = 0;
+ if (!lower_bound)
+ lower_bound = build_default_bound (NULL_TREE, lb_default);
+ if (!upper_bound)
+ upper_bound
+ = build_default_bound (get_identifier ("omp num args plus"),
+ ub_default);
+
+ append_to_list (build_tree_list (lower_bound, upper_bound),
+ make_location (num_range_loc_begin,
+ num_range_loc_begin,
+ num_range_loc_end));
+ } while (cp_lexer_next_token_is (parser->lexer, CPP_COMMA));
+ return list;
+}
+
/* OpenACC 2.0:
copy ( variable-list )
copyin ( variable-list )
@@ -39467,7 +40473,7 @@ cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind,
}
}
nl = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_MAP, list, NULL,
- false);
+ C_ORT_ACC, false);
}
for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
@@ -40831,7 +41837,7 @@ cp_parser_omp_clause_ordered (cp_parser *parser,
static tree
cp_parser_omp_clause_reduction (cp_parser *parser, enum omp_clause_code kind,
- bool is_omp, tree list)
+ enum c_omp_region_type ort, tree list)
{
enum tree_code code = ERROR_MARK;
tree nlist, c, id = NULL_TREE;
@@ -40841,7 +41847,7 @@ cp_parser_omp_clause_reduction (cp_parser *parser, enum omp_clause_code kind,
if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
return list;
- if (kind == OMP_CLAUSE_REDUCTION && is_omp)
+ if (kind == OMP_CLAUSE_REDUCTION && ort == C_ORT_OMP)
{
if (cp_lexer_next_token_is_keyword (parser->lexer, RID_DEFAULT)
&& cp_lexer_nth_token_is (parser->lexer, 2, CPP_COMMA))
@@ -40916,6 +41922,12 @@ cp_parser_omp_clause_reduction (cp_parser *parser, enum omp_clause_code kind,
code = TRUTH_ANDIF_EXPR;
else if (id == ovl_op_identifier (false, TRUTH_ORIF_EXPR))
code = TRUTH_ORIF_EXPR;
+ if (code == ERROR_MARK && ort == C_ORT_ACC)
+ {
+ cp_parser_error (parser, "expected %<+%>, %<*%>, %<-%>, %<&%>, "
+ "%<^%>, %<|%>, %<&&%>, %<||%>, %<min%> or %<max%>");
+ goto resync_fail;
+ }
id = omp_reduction_id (code, id, NULL_TREE);
tree scope = parser->scope;
if (scope)
@@ -40938,11 +41950,14 @@ cp_parser_omp_clause_reduction (cp_parser *parser, enum omp_clause_code kind,
if (!cp_parser_require (parser, CPP_COLON, RT_COLON))
goto resync_fail;
- nlist = cp_parser_omp_var_list_no_open (parser, kind, list,
- NULL);
+ nlist = cp_parser_omp_var_list_no_open (parser, kind, list, NULL, ort);
for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c))
{
OMP_CLAUSE_REDUCTION_CODE (c) = code;
+ /* OpenACC does not require anything below. */
+ if (ort == C_ORT_ACC)
+ continue;
+
if (task)
OMP_CLAUSE_REDUCTION_TASK (c) = 1;
else if (inscan)
@@ -41479,6 +42494,234 @@ cp_parser_omp_clause_allocate (cp_parser *parser, tree list)
return nlist;
}
+/* OpenMP 5.0:
+ uses_allocators ( allocator-list )
+
+ allocator-list:
+ allocator
+ allocator , allocator-list
+ allocator ( traits-array )
+ allocator ( traits-array ) , allocator-list
+
+ OpenMP 5.2:
+
+ uses_allocators ( modifier : allocator-list )
+ uses_allocators ( modifier , modifier : allocator-list )
+
+ modifier:
+ traits ( traits-array )
+ memspace ( mem-space-handle ) */
+
+static tree
+cp_parser_omp_clause_uses_allocators (cp_parser *parser, tree list)
+{
+ location_t clause_loc
+ = cp_lexer_peek_token (parser->lexer)->location;
+ tree t = NULL_TREE, nl = list;
+ matching_parens parens;
+ if (!parens.require_open (parser))
+ return list;
+
+ tree memspace_expr = NULL_TREE;
+ tree traits_var = NULL_TREE;
+
+ struct item_tok
+ {
+ location_t loc;
+ tree id;
+ item_tok (void) : loc (UNKNOWN_LOCATION), id (NULL_TREE) {}
+ };
+ struct item { item_tok name, arg; };
+ auto_vec<item> *modifiers = NULL, *allocators = NULL;
+ auto_vec<item> *cur_list = new auto_vec<item> (4);
+
+ while (true)
+ {
+ item it;
+
+ if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+ {
+ cp_token *tok = cp_lexer_peek_token (parser->lexer);
+ it.name.id = tok->u.value;
+ it.name.loc = tok->location;
+ cp_lexer_consume_token (parser->lexer);
+
+ if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+ {
+ matching_parens parens2;
+ parens2.consume_open (parser);
+
+ if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+ {
+ tok = cp_lexer_peek_token (parser->lexer);
+ it.arg.id = tok->u.value;
+ it.arg.loc = tok->location;
+ cp_lexer_consume_token (parser->lexer);
+ }
+ else
+ {
+ cp_parser_error (parser, "expected identifier");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ goto end;
+ }
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/false,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ }
+ }
+
+ cur_list->safe_push (it);
+
+ if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+ cp_lexer_consume_token (parser->lexer);
+ else if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+ {
+ if (modifiers)
+ {
+ cp_parser_error (parser, "expected %<)%>");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ goto end;
+ }
+ else
+ {
+ cp_lexer_consume_token (parser->lexer);
+ modifiers = cur_list;
+ cur_list = new auto_vec<item> (4);
+ }
+ }
+ else if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+ {
+ gcc_assert (allocators == NULL);
+ allocators = cur_list;
+ cur_list = NULL;
+ break;
+ }
+ else
+ {
+ cp_parser_error (parser, "expected %<)%>");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ goto end;
+ }
+ }
+
+ if (modifiers)
+ for (unsigned i = 0; i < modifiers->length (); i++)
+ {
+ item& it = (*modifiers)[i];
+ const char *p = IDENTIFIER_POINTER (it.name.id);
+ int strcmp_traits = 1, strcmp_memspace = 1;
+
+ if ((strcmp_traits = strcmp ("traits", p)) == 0
+ || (strcmp_memspace = strcmp ("memspace", p)) == 0)
+ {
+ if ((strcmp_traits == 0 && traits_var != NULL_TREE)
+ || (strcmp_memspace == 0 && memspace_expr != NULL_TREE))
+ {
+ error_at (it.name.loc, "duplicate %qs modifier", p);
+ goto end;
+ }
+ t = cp_parser_lookup_name_simple (parser, it.arg.id, it.arg.loc);
+ if (t == error_mark_node)
+ {
+ cp_parser_name_lookup_error (parser, it.arg.id, t, NLE_NULL,
+ it.arg.loc);
+ }
+ else if (strcmp_memspace == 0)
+ memspace_expr = t;
+ else if (strcmp_traits == 0)
+ traits_var = t;
+ else
+ gcc_unreachable ();
+ }
+ else
+ {
+ error_at (it.name.loc, "unknown modifier %qE", it.name.id);
+ goto end;
+ }
+ }
+
+ if (allocators)
+ {
+ if (modifiers)
+ {
+ if (allocators->length () > 1)
+ {
+ error_at ((*allocators)[1].name.loc,
+ "%<uses_allocators%> clause only accepts a single "
+ "allocator when using modifiers");
+ goto end;
+ }
+ else if ((*allocators)[0].arg.id)
+ {
+ error_at ((*allocators)[0].arg.loc,
+ "legacy %<%E(%E)%> traits syntax not allowed in "
+ "%<uses_allocators%> clause when using modifiers",
+ (*allocators)[0].name.id, (*allocators)[0].arg.id);
+ goto end;
+ }
+ }
+
+ for (unsigned i = 0; i < allocators->length (); i++)
+ {
+ item& it = (*allocators)[i];
+ t = cp_parser_lookup_name_simple (parser, it.name.id, it.name.loc);
+ if (t == error_mark_node)
+ {
+ cp_parser_name_lookup_error (parser, it.name.id, t, NLE_NULL,
+ it.name.loc);
+ goto end;
+ }
+ else if (t != error_mark_node)
+ {
+ tree t2 = NULL_TREE;
+ if (it.arg.id)
+ {
+ t2 = cp_parser_lookup_name_simple (parser, it.arg.id,
+ it.arg.loc);
+ if (t2 == error_mark_node)
+ {
+ cp_parser_name_lookup_error (parser, it.arg.id, t2,
+ NLE_NULL, it.arg.loc);
+ goto end;
+ }
+ }
+ else
+ t2 = traits_var;
+
+ tree c = build_omp_clause (clause_loc,
+ OMP_CLAUSE_USES_ALLOCATORS);
+ OMP_CLAUSE_USES_ALLOCATORS_ALLOCATOR (c) = t;
+ OMP_CLAUSE_USES_ALLOCATORS_MEMSPACE (c) = memspace_expr;
+ OMP_CLAUSE_USES_ALLOCATORS_TRAITS (c) = t2;
+ OMP_CLAUSE_CHAIN (c) = nl;
+ nl = c;
+ }
+ }
+ }
+ end:
+ if (cur_list)
+ delete cur_list;
+ if (modifiers)
+ delete modifiers;
+ if (allocators)
+ delete allocators;
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/false,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ return nl;
+}
+
/* OpenMP 2.5:
lastprivate ( variable-list )
@@ -42030,10 +43273,10 @@ cp_parser_omp_iterators (cp_parser *parser)
pushdecl (iter_var);
*last = make_tree_vec (6);
- TREE_VEC_ELT (*last, 0) = iter_var;
- TREE_VEC_ELT (*last, 1) = begin;
- TREE_VEC_ELT (*last, 2) = end;
- TREE_VEC_ELT (*last, 3) = step;
+ OMP_ITERATORS_VAR (*last) = iter_var;
+ OMP_ITERATORS_BEGIN (*last) = begin;
+ OMP_ITERATORS_END (*last) = end;
+ OMP_ITERATORS_STEP (*last) = step;
last = &TREE_CHAIN (*last);
if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
@@ -42107,7 +43350,7 @@ cp_parser_omp_clause_affinity (cp_parser *parser, tree list)
tree block = poplevel (1, 1, 0);
if (iterators != error_mark_node)
{
- TREE_VEC_ELT (iterators, 5) = block;
+ OMP_ITERATORS_BLOCK (iterators) = block;
for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c))
OMP_CLAUSE_DECL (c) = build_tree_list (iterators,
OMP_CLAUSE_DECL (c));
@@ -42232,7 +43475,7 @@ cp_parser_omp_clause_depend (cp_parser *parser, tree list, location_t loc)
if (iterators == error_mark_node)
iterators = NULL_TREE;
else
- TREE_VEC_ELT (iterators, 5) = block;
+ OMP_ITERATORS_BLOCK (iterators) = block;
}
for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c))
@@ -42328,8 +43571,11 @@ cp_parser_omp_clause_doacross (cp_parser *parser, tree list, location_t loc)
to ( variable-list )
OpenMP 5.1:
- from ( [present :] variable-list )
- to ( [present :] variable-list ) */
+ from ( [motion-modifier[,] [motion-modifier[,]...]:] variable-list )
+ to ( [motion-modifier[,] [motion-modifier[,]...]:] variable-list )
+
+ motion-modifier:
+ present | iterator (iterators-definition) */
static tree
cp_parser_omp_clause_from_to (cp_parser *parser, enum omp_clause_code kind,
@@ -42338,23 +43584,196 @@ cp_parser_omp_clause_from_to (cp_parser *parser, enum omp_clause_code kind,
if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
return list;
- bool present = false;
- cp_token *token = cp_lexer_peek_token (parser->lexer);
+ int pos = 1;
+ int colon_pos = 0;
+ int iterator_length = 0;
- if (token->type == CPP_NAME
- && strcmp (IDENTIFIER_POINTER (token->u.value), "present") == 0
- && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON))
+ while (cp_lexer_peek_nth_token (parser->lexer, pos)->type == CPP_NAME)
{
- present = true;
- cp_lexer_consume_token (parser->lexer);
- cp_lexer_consume_token (parser->lexer);
+ const char *identifier =
+ IDENTIFIER_POINTER (cp_lexer_peek_nth_token (parser->lexer,
+ pos)->u.value);
+ if (cp_lexer_nth_token_is (parser->lexer, pos + 1, CPP_OPEN_PAREN))
+ {
+ int n = cp_parser_skip_balanced_tokens (parser, pos + 1);
+ if (n != pos + 1)
+ {
+ if (strcmp (identifier, "iterator") == 0)
+ iterator_length = n - pos;
+ pos = n - 1;
+ }
+ }
+ if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type == CPP_COMMA)
+ pos += 2;
+ else
+ pos++;
+ if (cp_lexer_peek_nth_token (parser->lexer, pos)->type == CPP_COLON)
+ {
+ colon_pos = pos;
+ break;
+ }
+ }
+
+ bool present_modifier = false;
+ bool mapper_modifier = false;
+ tree mapper_name = NULL_TREE;
+ tree iterators = NULL_TREE;
+
+ for (int pos = 1; pos < colon_pos; ++pos)
+ {
+ cp_token *tok = cp_lexer_peek_token (parser->lexer);
+ if (tok->type == CPP_COMMA)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ continue;
+ }
+ const char *p = IDENTIFIER_POINTER (tok->u.value);
+ if (strcmp ("present", p) == 0)
+ {
+ if (present_modifier)
+ {
+ cp_parser_error (parser, "too many %<present%> modifiers");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ return list;
+ }
+ present_modifier = true;
+ cp_lexer_consume_token (parser->lexer);
+ }
+ else if (strcmp ("iterator", p) == 0)
+ {
+ if (iterators)
+ {
+ cp_parser_error (parser, "too many %<iterator%> modifiers");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ return list;
+ }
+ begin_scope (sk_omp, NULL);
+ iterators = cp_parser_omp_iterators (parser);
+ pos += iterator_length - 1;
+ }
+ else if (strcmp ("mapper", p) == 0)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ matching_parens parens;
+ if (parens.require_open (parser))
+ {
+ if (mapper_modifier)
+ {
+ cp_parser_error (parser, "too many %<mapper%> modifiers");
+ /* Assume it's a well-formed mapper modifier, even if it
+ seems to be in the wrong place. */
+ cp_lexer_consume_token (parser->lexer);
+ parens.require_close (parser);
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/
+ true);
+ return list;
+ }
+ tok = cp_lexer_peek_token (parser->lexer);
+ switch (tok->type)
+ {
+ case CPP_NAME:
+ {
+ cp_expr e = cp_parser_identifier (parser);
+ if (e != error_mark_node)
+ mapper_name = e;
+ else
+ goto err;
+ }
+ break;
+ case CPP_KEYWORD:
+ if (tok->keyword == RID_DEFAULT)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ break;
+ }
+ /* Fallthrough. */
+ default:
+ err:
+ cp_parser_error (parser,
+ "expected identifier or %<default%>");
+ return list;
+ }
+
+ if (!parens.require_close (parser))
+ {
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/
+ true);
+ return list;
+ }
+ mapper_modifier = true;
+ pos += 3;
+ }
+ }
+ else
+ {
+ cp_parser_error (parser, "%<to%> or %<from%> clause with "
+ "modifier other than %<iterator%>, "
+ "%<mapper%> or %<present%>");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ return list;
+ }
}
- tree nl = cp_parser_omp_var_list_no_open (parser, kind, list, NULL, true);
- if (present)
+ if (colon_pos)
+ cp_parser_require (parser, CPP_COLON, RT_COLON);
+
+ tree nl = cp_parser_omp_var_list_no_open (parser, kind, list, NULL, C_ORT_OMP,
+ true);
+ if (present_modifier)
for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
OMP_CLAUSE_MOTION_PRESENT (c) = 1;
+ if (mapper_name)
+ {
+ tree last_new = NULL_TREE;
+ for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
+ last_new = c;
+
+ tree name = build_omp_clause (input_location, OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (name, GOMP_MAP_PUSH_MAPPER_NAME);
+ OMP_CLAUSE_DECL (name) = mapper_name;
+ OMP_CLAUSE_CHAIN (name) = nl;
+ nl = name;
+
+ gcc_assert (last_new);
+
+ name = build_omp_clause (input_location, OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (name, GOMP_MAP_POP_MAPPER_NAME);
+ OMP_CLAUSE_DECL (name) = null_pointer_node;
+ if (iterators)
+ OMP_CLAUSE_ITERATORS (name) = iterators;
+ OMP_CLAUSE_CHAIN (name) = OMP_CLAUSE_CHAIN (last_new);
+ OMP_CLAUSE_CHAIN (last_new) = name;
+ }
+
+ if (iterators)
+ {
+ tree block = poplevel (1, 1, 0);
+ if (iterators == error_mark_node)
+ iterators = NULL_TREE;
+ else
+ OMP_ITERATORS_BLOCK (iterators) = block;
+ }
+
+ if (iterators)
+ for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
+ OMP_CLAUSE_ITERATORS (c) = iterators;
+
return nl;
}
@@ -42375,36 +43794,59 @@ cp_parser_omp_clause_from_to (cp_parser *parser, enum omp_clause_code kind,
map ( [map-type-modifier[,] ...] map-kind: variable-list )
map-type-modifier:
- always | close */
+ always | close | mapper ( mapper-name ) */
static tree
-cp_parser_omp_clause_map (cp_parser *parser, tree list)
+cp_parser_omp_clause_map (cp_parser *parser, tree list, enum gomp_map_kind kind)
{
tree nlist, c;
- enum gomp_map_kind kind = GOMP_MAP_TOFROM;
if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
return list;
int pos = 1;
int map_kind_pos = 0;
- while (cp_lexer_peek_nth_token (parser->lexer, pos)->type == CPP_NAME
- || cp_lexer_peek_nth_token (parser->lexer, pos)->keyword == RID_DELETE)
+ int iterator_length = 0;
+ for (;;)
{
- if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type == CPP_COLON)
+ cp_token *tok = cp_lexer_peek_nth_token (parser->lexer, pos);
+ if (!(tok->type == CPP_NAME || tok->keyword == RID_DELETE))
+ break;
+
+ cp_token *next_tok = cp_lexer_peek_nth_token (parser->lexer, pos + 1);
+ if (tok->type == CPP_NAME
+ && strcmp (IDENTIFIER_POINTER (tok->u.value), "iterator") == 0
+ && next_tok->type == CPP_OPEN_PAREN)
+ {
+ int n = cp_parser_skip_balanced_tokens (parser, pos + 1);
+ if (n != pos + 1)
+ {
+ iterator_length = n - pos;
+ pos = n - 1;
+ next_tok = cp_lexer_peek_nth_token (parser->lexer, n);
+ }
+ }
+
+ if (next_tok->type == CPP_COLON)
{
map_kind_pos = pos;
break;
}
- if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type == CPP_COMMA)
+ if (next_tok->type == CPP_COMMA)
pos++;
+ else if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type
+ == CPP_OPEN_PAREN)
+ pos = cp_parser_skip_balanced_tokens (parser, pos + 1);
pos++;
}
bool always_modifier = false;
bool close_modifier = false;
bool present_modifier = false;
+ bool mapper_modifier = false;
+ tree mapper_name = NULL_TREE;
+ tree iterators = NULL_TREE;
for (int pos = 1; pos < map_kind_pos; ++pos)
{
cp_token *tok = cp_lexer_peek_token (parser->lexer);
@@ -42427,6 +43869,7 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list)
return list;
}
always_modifier = true;
+ cp_lexer_consume_token (parser->lexer);
}
else if (strcmp ("close", p) == 0)
{
@@ -42440,6 +43883,71 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list)
return list;
}
close_modifier = true;
+ cp_lexer_consume_token (parser->lexer);
+ }
+ else if (strcmp ("mapper", p) == 0)
+ {
+ cp_lexer_consume_token (parser->lexer);
+
+ matching_parens parens;
+ if (parens.require_open (parser))
+ {
+ if (mapper_modifier)
+ {
+ cp_parser_error (parser, "too many %<mapper%> modifiers");
+ /* Assume it's a well-formed mapper modifier, even if it
+ seems to be in the wrong place. */
+ cp_lexer_consume_token (parser->lexer);
+ parens.require_close (parser);
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/
+ true);
+ return list;
+ }
+
+ tok = cp_lexer_peek_token (parser->lexer);
+ switch (tok->type)
+ {
+ case CPP_NAME:
+ {
+ cp_expr e = cp_parser_identifier (parser);
+ if (e != error_mark_node)
+ mapper_name = e;
+ else
+ goto err;
+ }
+ break;
+
+ case CPP_KEYWORD:
+ if (tok->keyword == RID_DEFAULT)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ break;
+ }
+ /* Fallthrough. */
+
+ default:
+ err:
+ cp_parser_error (parser,
+ "expected identifier or %<default%>");
+ return list;
+ }
+
+ if (!parens.require_close (parser))
+ {
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/
+ true);
+ return list;
+ }
+
+ mapper_modifier = true;
+ pos += 3;
+ }
}
else if (strcmp ("present", p) == 0)
{
@@ -42453,19 +43961,37 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list)
return list;
}
present_modifier = true;
- }
+ cp_lexer_consume_token (parser->lexer);
+ }
+ else if (strcmp ("iterator", p) == 0
+ && cp_lexer_peek_nth_token (parser->lexer, 2)->type
+ == CPP_OPEN_PAREN)
+ {
+ if (iterators)
+ {
+ cp_parser_error (parser, "too many %<iterator%> modifiers");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ return list;
+ }
+ begin_scope (sk_omp, NULL);
+ iterators = cp_parser_omp_iterators (parser);
+ pos += iterator_length - 1;
+ continue;
+ }
else
{
- cp_parser_error (parser, "%<map%> clause with map-type modifier other"
- " than %<always%>, %<close%> or %<present%>");
+ cp_parser_error (parser, "%<map%> clause with map-type modifier "
+ "other than %<always%>, %<close%>, "
+ "%<iterator%>, %<mapper%> or %<present%>");
cp_parser_skip_to_closing_parenthesis (parser,
/*recovering=*/true,
/*or_comma=*/false,
/*consume_paren=*/true);
return list;
}
-
- cp_lexer_consume_token (parser->lexer);
}
if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)
@@ -42518,11 +44044,47 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list)
legally. */
begin_scope (sk_omp, NULL);
nlist = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_MAP, list,
- NULL, true);
+ NULL, (kind == GOMP_MAP_UNSET
+ ? C_ORT_OMP_DECLARE_MAPPER
+ : C_ORT_OMP), true);
finish_scope ();
+ tree last_new = NULL_TREE;
+
+ if (iterators)
+ {
+ tree block = poplevel (1, 1, 0);
+ if (iterators == error_mark_node)
+ iterators = NULL_TREE;
+ else
+ OMP_ITERATORS_BLOCK (iterators) = block;
+ }
+
for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c))
- OMP_CLAUSE_SET_MAP_KIND (c, kind);
+ {
+ OMP_CLAUSE_SET_MAP_KIND (c, kind);
+ OMP_CLAUSE_ITERATORS (c) = iterators;
+ last_new = c;
+ }
+
+ if (mapper_name)
+ {
+ tree name = build_omp_clause (input_location, OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (name, GOMP_MAP_PUSH_MAPPER_NAME);
+ OMP_CLAUSE_DECL (name) = mapper_name;
+ if (iterators)
+ OMP_CLAUSE_ITERATORS (name) = iterators;
+ OMP_CLAUSE_CHAIN (name) = nlist;
+ nlist = name;
+
+ gcc_assert (last_new);
+
+ name = build_omp_clause (input_location, OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (name, GOMP_MAP_POP_MAPPER_NAME);
+ OMP_CLAUSE_DECL (name) = null_pointer_node;
+ OMP_CLAUSE_CHAIN (name) = OMP_CLAUSE_CHAIN (last_new);
+ OMP_CLAUSE_CHAIN (last_new) = name;
+ }
return nlist;
}
@@ -43180,7 +44742,7 @@ cp_parser_omp_clause_init (cp_parser *parser, tree list)
"missing required %<target%> and/or %<targetsync%> modifier");
tree nl = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_INIT, list,
- NULL, false);
+ NULL);
for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
{
TREE_ADDRESSABLE (OMP_CLAUSE_DECL (c)) = 1;
@@ -43423,7 +44985,7 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
case PRAGMA_OACC_CLAUSE_REDUCTION:
clauses
= cp_parser_omp_clause_reduction (parser, OMP_CLAUSE_REDUCTION,
- false, clauses);
+ C_ORT_ACC, clauses);
c_name = "reduction";
break;
case PRAGMA_OACC_CLAUSE_SELF:
@@ -43621,7 +45183,7 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
case PRAGMA_OMP_CLAUSE_IN_REDUCTION:
clauses
= cp_parser_omp_clause_reduction (parser, OMP_CLAUSE_IN_REDUCTION,
- true, clauses);
+ C_ORT_OMP, clauses);
c_name = "in_reduction";
break;
case PRAGMA_OMP_CLAUSE_INDIRECT:
@@ -43676,7 +45238,7 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
case PRAGMA_OMP_CLAUSE_REDUCTION:
clauses
= cp_parser_omp_clause_reduction (parser, OMP_CLAUSE_REDUCTION,
- true, clauses);
+ C_ORT_OMP, clauses);
c_name = "reduction";
break;
case PRAGMA_OMP_CLAUSE_SCHEDULE:
@@ -43693,7 +45255,7 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
clauses
= cp_parser_omp_clause_reduction (parser,
OMP_CLAUSE_TASK_REDUCTION,
- true, clauses);
+ C_ORT_OMP, clauses);
c_name = "task_reduction";
break;
case PRAGMA_OMP_CLAUSE_UNTIED:
@@ -43796,6 +45358,10 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
clauses = cp_parser_omp_clause_allocate (parser, clauses);
c_name = "allocate";
break;
+ case PRAGMA_OMP_CLAUSE_USES_ALLOCATORS:
+ clauses = cp_parser_omp_clause_uses_allocators (parser, clauses);
+ c_name = "uses_allocators";
+ break;
case PRAGMA_OMP_CLAUSE_LINEAR:
{
bool declare_simd = false;
@@ -43850,7 +45416,7 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
c_name = "detach";
break;
case PRAGMA_OMP_CLAUSE_MAP:
- clauses = cp_parser_omp_clause_map (parser, clauses);
+ clauses = cp_parser_omp_clause_map (parser, clauses, GOMP_MAP_TOFROM);
c_name = "map";
break;
case PRAGMA_OMP_CLAUSE_DEVICE:
@@ -44011,11 +45577,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)
@@ -44036,70 +45740,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;
+
+ 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;
- /* 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 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:
@@ -48332,7 +50168,10 @@ cp_parser_omp_target_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
tree clauses
= cp_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
- "#pragma omp target data", pragma_tok);
+ "#pragma omp target data", pragma_tok, false);
+ if (!processing_template_decl)
+ clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP);
+ clauses = finish_omp_clauses (clauses, C_ORT_OMP);
c_omp_adjust_map_clauses (clauses, false);
int map_seen = 0;
for (tree *pc = &clauses; *pc;)
@@ -48447,7 +50286,11 @@ cp_parser_omp_target_enter_data (cp_parser *parser, cp_token *pragma_tok,
tree clauses
= cp_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
- "#pragma omp target enter data", pragma_tok);
+ "#pragma omp target enter data", pragma_tok,
+ false);
+ if (!processing_template_decl)
+ clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP);
+ clauses = finish_omp_clauses (clauses, C_ORT_OMP);
c_omp_adjust_map_clauses (clauses, false);
int map_seen = 0;
for (tree *pc = &clauses; *pc;)
@@ -48564,6 +50407,8 @@ cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok,
= cp_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
"#pragma omp target exit data", pragma_tok,
false);
+ if (!processing_template_decl)
+ clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP_EXIT_DATA);
clauses = finish_omp_clauses (clauses, C_ORT_OMP_EXIT_DATA);
c_omp_adjust_map_clauses (clauses, false);
int map_seen = 0;
@@ -48657,9 +50502,43 @@ cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
tree clauses
= cp_parser_omp_all_clauses (parser, OMP_TARGET_UPDATE_CLAUSE_MASK,
- "#pragma omp target update", pragma_tok);
- if (omp_find_clause (clauses, OMP_CLAUSE_TO) == NULL_TREE
- && omp_find_clause (clauses, OMP_CLAUSE_FROM) == NULL_TREE)
+ "#pragma omp target update", pragma_tok,
+ false);
+ if (!processing_template_decl)
+ clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP_UPDATE);
+ clauses = finish_omp_clauses (clauses, C_ORT_OMP_UPDATE);
+ bool to_clause = false, from_clause = false;
+ for (tree c = clauses;
+ c && !to_clause && !from_clause;
+ c = OMP_CLAUSE_CHAIN (c))
+ {
+ switch (OMP_CLAUSE_CODE (c))
+ {
+ case OMP_CLAUSE_TO:
+ to_clause = true;
+ break;
+ case OMP_CLAUSE_FROM:
+ from_clause = true;
+ break;
+ case OMP_CLAUSE_MAP:
+ switch (OMP_CLAUSE_MAP_KIND (c))
+ {
+ case GOMP_MAP_TO_GRID:
+ to_clause = true;
+ break;
+ case GOMP_MAP_FROM_GRID:
+ from_clause = true;
+ break;
+ default:
+ ;
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ if (!to_clause && !from_clause)
{
error_at (pragma_tok->location,
"%<#pragma omp target update%> must contain at least one "
@@ -48696,7 +50575,8 @@ cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION) \
| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_THREAD_LIMIT) \
| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
- | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
+ | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR)\
+ | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_USES_ALLOCATORS))
static bool
cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok,
@@ -48856,6 +50736,8 @@ cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok,
OMP_CLAUSE_CHAIN (nc) = OMP_CLAUSE_CHAIN (c);
OMP_CLAUSE_CHAIN (c) = nc;
}
+ if (!processing_template_decl)
+ clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP_TARGET);
clauses = finish_omp_clauses (clauses, C_ORT_OMP_TARGET);
c_omp_adjust_map_clauses (clauses, true);
@@ -50332,7 +52214,7 @@ cp_parser_omp_dispatch (cp_parser *parser, cp_token *pragma_tok)
static tree
cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
- tree attrs, tree parms)
+ tree attrs)
{
matching_parens parens;
if (!parens.require_open (parser))
@@ -50392,13 +52274,27 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
tree append_args_tree = NULL_TREE;
tree append_args_last = NULL_TREE;
- vec<tree> adjust_args_list = vNULL;
bool has_match = false, has_adjust_args = false;
location_t adjust_args_loc = UNKNOWN_LOCATION;
location_t append_args_loc = UNKNOWN_LOCATION;
- tree need_device_ptr_list = NULL_TREE;
+
tree ctx = NULL_TREE;
+ tree adjust_args_list = NULL_TREE;
+ auto append_adjust_args
+ = [chain = &adjust_args_list] (tree list, tree clause_modifier) mutable
+ {
+ gcc_assert (chain && *chain == NULL_TREE);
+ *chain = list;
+ /* Just stick them all together in one list and process them all
+ at once later. */
+ for (tree node = list; node; node = TREE_CHAIN (node))
+ {
+ TREE_PURPOSE (node) = clause_modifier;
+ chain = &TREE_CHAIN (node);
+ }
+ };
+
do
{
if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)
@@ -50452,6 +52348,20 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
goto fail;
ctx = omp_check_context_selector (match_loc, ctx,
OMP_CTX_DECLARE_VARIANT);
+
+ /* The OpenMP spec says the merging rules for enclosing
+ "begin declare variant" contexts apply to "declare variant
+ directives" -- the term it uses to refer to both directive
+ forms. */
+ if (ctx != error_mark_node
+ && !vec_safe_is_empty (scope_chain->omp_declare_variant_attribute))
+ {
+ cp_omp_declare_variant_attr a
+ = scope_chain->omp_declare_variant_attribute->last ();
+ tree outer_ctx = a.selector;
+ ctx = omp_merge_context_selectors (match_loc, outer_ctx, ctx,
+ OMP_CTX_DECLARE_VARIANT);
+ }
if (ctx != error_mark_node && variant != error_mark_node)
{
tree match_loc_node
@@ -50479,78 +52389,49 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
{
const char *p = IDENTIFIER_POINTER (adjust_op_tok->u.value);
if (strcmp (p, "need_device_ptr") == 0
+ || strcmp (p, "need_device_addr") == 0
|| strcmp (p, "nothing") == 0)
{
cp_lexer_consume_token (parser->lexer); // need_device_ptr
cp_lexer_consume_token (parser->lexer); // :
- tree arg;
- tree list
- = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_ERROR,
- NULL_TREE, NULL);
-
- for (tree c = list; c != NULL_TREE; c = TREE_CHAIN (c))
+ tree list = cp_parser_omp_parm_list (parser);
+ if (list && list != error_mark_node)
+ /* It should be fine to just use the identifier node. */
+ append_adjust_args (list, adjust_op_tok->u.value);
+ else
{
- tree decl = TREE_PURPOSE (c);
- location_t arg_loc = EXPR_LOCATION (TREE_VALUE (c));
- int idx;
- for (arg = parms, idx = 0; arg != NULL;
- arg = TREE_CHAIN (arg), idx++)
- if (TREE_VALUE (arg) == decl)
- break;
- if (arg == NULL_TREE)
- {
- error_at (arg_loc, "%qD is not a function argument",
- decl);
- continue;
- }
- arg = TREE_VALUE (arg);
- if (adjust_args_list.contains (arg))
- {
- error_at (arg_loc, "%qD is specified more than once",
- decl);
- continue;
- }
- if (strcmp (p, "need_device_ptr") == 0)
- {
- bool is_ptr_or_template
- = TEMPLATE_PARM_P (TREE_TYPE (arg))
- || POINTER_TYPE_P (TREE_TYPE (arg));
- if (!is_ptr_or_template)
- {
- error_at (arg_loc, "%qD is not a C pointer",
- decl);
- continue;
- }
- }
- adjust_args_list.safe_push (arg);
- if (strcmp (p, "need_device_ptr") == 0)
- {
- need_device_ptr_list = chainon (
- need_device_ptr_list,
- build_tree_list (
- NULL_TREE,
- build_int_cst (
- integer_type_node,
- idx))); // Store 0-based argument index,
- // as in gimplify_call_expr
- }
+ /* Do we need a specific diagnostic here?
+ I don't like failing here, we should be skipping to
+ a close paren and continuing. */
+ goto fail;
}
}
else
{
error_at (adjust_op_tok->location,
- "expected %<nothing%> or %<need_device_ptr%>");
+ "expected %<nothing%>, %<need_device_ptr%> or "
+ "%<need_device_addr%>");
+ /* We should be trying to recover here instead of immediately
+ failing, skipping to close paren and continuing. */
goto fail;
}
}
else
{
+ /* We should be trying to recover here instead of immediately
+ failing, skipping to close paren and continuing. */
error_at (adjust_op_tok->location,
- "expected %<nothing%> or %<need_device_ptr%> followed "
- "by %<:%>");
+ "expected %<nothing%>, %<need_device_ptr%> or "
+ "%<need_device_addr%> followed by %<:%>");
goto fail;
}
+ /* cp_parser_omp_var_list_no_open used to handle this, we don't use
+ it anymore though. */
+ if (!parens.require_close (parser))
+ /* We should be trying to recover here instead of immediately
+ failing, I'm not sure what we skip to though. */
+ goto fail;
}
else if (ccode == append_args)
{
@@ -50614,14 +52495,12 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
cp_lexer_consume_token (parser->lexer); // ','
}
while (true);
- int nbase_args = 0;
- for (tree t = parms;
- t && TREE_VALUE (t) != void_type_node; t = TREE_CHAIN (t))
- nbase_args++;
- /* Store as purpose = arg number after which to append
- and value = list of interop items. */
- append_args_tree = build_tree_list (build_int_cst (integer_type_node,
- nbase_args),
+ /* This is where the number of args used to be inserted, it still
+ gets put here by omp_declare_variant_finalize_one once we know how
+ many parameters there are. Ideally we should refactor the way we
+ pass this data around, once we do that we can remove this bit from
+ here. Until then, leave it be. */
+ append_args_tree = build_tree_list (NULL_TREE,
append_args_tree);
}
} while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL));
@@ -50651,11 +52530,17 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
// We might not have a DECL for the variant yet. So we store the
// need_device_ptr list in the base function attribute, after loc
// nodes.
- tree t = build_tree_list (need_device_ptr_list,
- NULL_TREE /* need_device_addr */);
+ tree debug_idxs_node
+ = CHECKING_P ? get_identifier ("omp adjust args idxs")
+ : NULL_TREE;
+ tree t = build_tree_list (debug_idxs_node,
+ adjust_args_list);
TREE_CHAIN (t) = append_args_tree;
+ tree debug_tail_node
+ = CHECKING_P ? get_identifier ("omp variant clauses temp")
+ : NULL_TREE;
TREE_VALUE (attrs) = chainon (TREE_VALUE (attrs),
- build_tree_list ( NULL_TREE, t));
+ build_tree_list (debug_tail_node, t));
}
}
@@ -50668,8 +52553,7 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
been parsed, and put that into "omp declare simd" attribute. */
static tree
-cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs,
- tree parms)
+cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs)
{
struct cp_token_cache *ce;
cp_omp_declare_simd_data *data = parser->omp_declare_simd;
@@ -50713,7 +52597,7 @@ cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs,
{
gcc_assert (strcmp (kind, "variant") == 0);
attrs
- = cp_finish_omp_declare_variant (parser, pragma_tok, attrs, parms);
+ = cp_finish_omp_declare_variant (parser, pragma_tok, attrs);
}
cp_parser_pop_lexer (parser);
}
@@ -50845,7 +52729,7 @@ cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs,
{
gcc_assert (strcmp (kind, "variant") == 0);
attrs = cp_finish_omp_declare_variant (parser, pragma_tok,
- attrs, parms);
+ attrs);
}
gcc_assert (parser->lexer != lexer);
vec_safe_truncate (lexer->buffer, 0);
@@ -51113,7 +52997,9 @@ cp_parser_omp_declare_target (cp_parser *parser, cp_token *pragma_tok)
/* OpenMP 5.1
# pragma omp begin assumes clauses[optseq] new-line
- # pragma omp begin declare target clauses[optseq] new-line */
+ # pragma omp begin declare target clauses[optseq] new-line
+
+ # pragma omp begin declare variant (match context-selector) new-line */
#define OMP_BEGIN_DECLARE_TARGET_CLAUSE_MASK \
( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE_TYPE) \
@@ -51159,9 +53045,73 @@ cp_parser_omp_begin (cp_parser *parser, cp_token *pragma_tok)
= { in_omp_attribute_pragma, device_type, indirect };
vec_safe_push (scope_chain->omp_declare_target_attribute, a);
}
+ else if (strcmp (p, "variant") == 0)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ const char *clause = "";
+ matching_parens parens;
+ location_t match_loc = cp_lexer_peek_token (parser->lexer)->location;
+ if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+ {
+ tree id = cp_lexer_peek_token (parser->lexer)->u.value;
+ clause = IDENTIFIER_POINTER (id);
+ }
+ if (strcmp (clause, "match") != 0)
+ {
+ cp_parser_error (parser, "expected %<match%>");
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+ return;
+ }
+
+ cp_lexer_consume_token (parser->lexer);
+
+ if (!parens.require_open (parser))
+ {
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+ return;
+ }
+
+ tree ctx = cp_parser_omp_context_selector_specification (parser,
+ true);
+ if (ctx != error_mark_node)
+ ctx = omp_check_context_selector (match_loc, ctx,
+ OMP_CTX_BEGIN_DECLARE_VARIANT);
+
+ if (ctx != error_mark_node
+ && !vec_safe_is_empty (scope_chain->omp_declare_variant_attribute))
+ {
+ cp_omp_declare_variant_attr a
+ = scope_chain->omp_declare_variant_attribute->last ();
+ tree outer_ctx = a.selector;
+ ctx = omp_merge_context_selectors (match_loc, outer_ctx, ctx,
+ OMP_CTX_BEGIN_DECLARE_VARIANT);
+ }
+
+ if (ctx == error_mark_node
+ || !omp_context_selector_matches (ctx, NULL_TREE, false, true))
+ {
+ /* The context is either invalid or cannot possibly match.
+ In the latter case the spec says all code in the begin/end
+ sequence will be elided. In the former case we'll get bogus
+ errors from trying to parse it without a valid context to
+ use for name-mangling, so elide that too. */
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+ cp_parser_skip_to_pragma_omp_end_declare_variant (parser);
+ return;
+ }
+ else
+ {
+ cp_omp_declare_variant_attr a
+ = { parser->lexer->in_omp_attribute_pragma, ctx };
+ vec_safe_push (scope_chain->omp_declare_variant_attribute, a);
+ }
+
+ parens.require_close (parser);
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+ }
else
{
- cp_parser_error (parser, "expected %<target%>");
+ cp_parser_error (parser, "expected %<target%> or %<variant%>");
cp_parser_skip_to_pragma_eol (parser, pragma_tok);
}
}
@@ -51174,7 +53124,8 @@ cp_parser_omp_begin (cp_parser *parser, cp_token *pragma_tok)
}
else
{
- cp_parser_error (parser, "expected %<declare target%> or %<assumes%>");
+ cp_parser_error (parser, "expected %<declare target%>, "
+ "%<declare variant%>, or %<assumes%>");
cp_parser_skip_to_pragma_eol (parser, pragma_tok);
}
}
@@ -51183,7 +53134,8 @@ cp_parser_omp_begin (cp_parser *parser, cp_token *pragma_tok)
# pragma omp end declare target new-line
OpenMP 5.1:
- # pragma omp end assumes new-line */
+ # pragma omp end assumes new-line
+ # pragma omp end declare variant new-line */
static void
cp_parser_omp_end (cp_parser *parser, cp_token *pragma_tok)
@@ -51205,41 +53157,70 @@ cp_parser_omp_end (cp_parser *parser, cp_token *pragma_tok)
p = IDENTIFIER_POINTER (id);
}
if (strcmp (p, "target") == 0)
- cp_lexer_consume_token (parser->lexer);
- else
{
- cp_parser_error (parser, "expected %<target%>");
- cp_parser_skip_to_pragma_eol (parser, pragma_tok);
- return;
+ cp_lexer_consume_token (parser->lexer);
+ cp_parser_require_pragma_eol (parser, pragma_tok);
+ if (!vec_safe_length (scope_chain->omp_declare_target_attribute))
+ error_at (pragma_tok->location,
+ "%<#pragma omp end declare target%> without "
+ "corresponding %<#pragma omp declare target%> or "
+ "%<#pragma omp begin declare target%>");
+ else
+ {
+ cp_omp_declare_target_attr
+ a = scope_chain->omp_declare_target_attribute->pop ();
+ if (a.attr_syntax != in_omp_attribute_pragma)
+ {
+ if (a.attr_syntax)
+ error_at (pragma_tok->location,
+ "%qs in attribute syntax terminated "
+ "with %qs in pragma syntax",
+ a.device_type >= 0 ? "begin declare target"
+ : "declare target",
+ "end declare target");
+ else
+ error_at (pragma_tok->location,
+ "%qs in pragma syntax terminated "
+ "with %qs in attribute syntax",
+ a.device_type >= 0 ? "begin declare target"
+ : "declare target",
+ "end declare target");
+ }
+ }
}
- cp_parser_require_pragma_eol (parser, pragma_tok);
- if (!vec_safe_length (scope_chain->omp_declare_target_attribute))
- error_at (pragma_tok->location,
- "%<#pragma omp end declare target%> without corresponding "
- "%<#pragma omp declare target%> or "
- "%<#pragma omp begin declare target%>");
- else
+ else if (strcmp (p, "variant") == 0)
{
- cp_omp_declare_target_attr
- a = scope_chain->omp_declare_target_attribute->pop ();
- if (a.attr_syntax != in_omp_attribute_pragma)
+ cp_lexer_consume_token (parser->lexer);
+ cp_parser_require_pragma_eol (parser, pragma_tok);
+ if (!vec_safe_length (scope_chain->omp_declare_variant_attribute))
+ error_at (pragma_tok->location,
+ "%<#pragma omp end declare variant%> without "
+ "corresponding %<#pragma omp begin declare variant%>");
+ else
{
- if (a.attr_syntax)
- error_at (pragma_tok->location,
- "%qs in attribute syntax terminated "
- "with %qs in pragma syntax",
- a.device_type >= 0 ? "begin declare target"
- : "declare target",
- "end declare target");
- else
- error_at (pragma_tok->location,
- "%qs in pragma syntax terminated "
- "with %qs in attribute syntax",
- a.device_type >= 0 ? "begin declare target"
- : "declare target",
- "end declare target");
+ cp_omp_declare_variant_attr
+ a = scope_chain->omp_declare_variant_attribute->pop ();
+ if (a.attr_syntax != in_omp_attribute_pragma)
+ {
+ if (a.attr_syntax)
+ error_at (pragma_tok->location,
+ "%<begin declare variant%> in attribute syntax "
+ "terminated with %<end declare variant%> in "
+ "pragma syntax");
+ else
+ error_at (pragma_tok->location,
+ "%<begin declare variant%> in pragma syntax "
+ "terminated with %<end declare variant%> in "
+ "attribute syntax");
+ }
}
}
+ else
+ {
+ cp_parser_error (parser, "expected %<target%>");
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+ return;
+ }
}
else if (strcmp (p, "assumes") == 0)
{
@@ -51409,7 +53390,6 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok,
{
error_at (match_loc, "too many %<otherwise%> or %<default%> "
"clauses in %<metadirective%>");
- cp_parser_skip_to_end_of_block_or_statement (parser, true);
goto fail;
}
else
@@ -51419,14 +53399,12 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok,
{
error_at (match_loc, "%<otherwise%> or %<default%> clause "
"must appear last in %<metadirective%>");
- cp_parser_skip_to_end_of_block_or_statement (parser, true);
goto fail;
}
if (!default_p && strcmp (p, "when") != 0)
{
error_at (match_loc, "%qs is not valid for %qs",
p, "metadirective");
- cp_parser_skip_to_end_of_block_or_statement (parser, true);
goto fail;
}
@@ -51495,7 +53473,6 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok,
if (i == 0)
{
error_at (loc, "expected directive name");
- cp_parser_skip_to_end_of_block_or_statement (parser, true);
goto fail;
}
@@ -51568,7 +53545,10 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok,
goto add;
case CPP_CLOSE_PAREN:
if (nesting_depth-- == 0)
- break;
+ {
+ cp_lexer_consume_token (parser->lexer);
+ break;
+ }
goto add;
default:
add:
@@ -51580,8 +53560,6 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok,
break;
}
- cp_lexer_consume_token (parser->lexer);
-
if (!skip)
{
cp_token eol_token = {};
@@ -51713,11 +53691,8 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok,
return;
fail:
- /* Skip the metadirective pragma. */
+ /* Skip the metadirective pragma. Do not skip the metadirective body. */
cp_parser_skip_to_pragma_eol (parser, pragma_tok);
-
- /* Skip the metadirective body. */
- cp_parser_skip_to_end_of_block_or_statement (parser, true);
}
@@ -52136,6 +54111,172 @@ cp_parser_omp_declare_reduction (cp_parser *parser, cp_token *pragma_tok,
obstack_free (&declarator_obstack, p);
}
+/* OpenMP 5.0
+ #pragma omp declare mapper([mapper-identifier:]type var) \
+ [clause[[,] clause] ... ] new-line */
+
+static void
+cp_parser_omp_declare_mapper (cp_parser *parser, cp_token *pragma_tok,
+ enum pragma_context)
+{
+ cp_token *token = NULL;
+ tree type = NULL_TREE, vardecl = NULL_TREE, block = NULL_TREE;
+ bool block_scope = false;
+ /* Don't create location wrapper nodes within "declare mapper"
+ directives. */
+ auto_suppress_location_wrappers sentinel;
+ tree mapper_name = NULL_TREE;
+ tree mapper_id, id, placeholder, mapper, maplist = NULL_TREE;
+
+ if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+ goto fail;
+
+ if (current_function_decl)
+ block_scope = true;
+
+ token = cp_lexer_peek_token (parser->lexer);
+
+ if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON))
+ {
+ switch (token->type)
+ {
+ case CPP_NAME:
+ {
+ cp_expr e = cp_parser_identifier (parser);
+ if (e != error_mark_node)
+ mapper_name = e;
+ else
+ goto fail;
+ }
+ break;
+
+ case CPP_KEYWORD:
+ if (token->keyword == RID_DEFAULT)
+ {
+ mapper_name = NULL_TREE;
+ cp_lexer_consume_token (parser->lexer);
+ break;
+ }
+ /* Fallthrough. */
+
+ default:
+ cp_parser_error (parser, "expected identifier or %<default%>");
+ }
+
+ if (!cp_parser_require (parser, CPP_COLON, RT_COLON))
+ goto fail;
+ }
+
+ {
+ const char *saved_message = parser->type_definition_forbidden_message;
+ parser->type_definition_forbidden_message
+ = G_("types may not be defined within %<declare mapper%>");
+ type_id_in_expr_sentinel s (parser);
+ type = cp_parser_type_id (parser);
+ parser->type_definition_forbidden_message = saved_message;
+ }
+
+ if (dependent_type_p (type))
+ mapper_id = omp_mapper_id (mapper_name, NULL_TREE);
+ else
+ mapper_id = omp_mapper_id (mapper_name, type);
+
+ vardecl = build_lang_decl (VAR_DECL, mapper_id, type);
+ DECL_ARTIFICIAL (vardecl) = 1;
+ TREE_STATIC (vardecl) = 1;
+ TREE_PUBLIC (vardecl) = 0;
+ DECL_EXTERNAL (vardecl) = 0;
+ DECL_DECLARED_CONSTEXPR_P (vardecl) = 1;
+ DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (vardecl) = 1;
+ DECL_OMP_DECLARE_MAPPER_P (vardecl) = 1;
+
+ keep_next_level (true);
+ block = begin_omp_structured_block ();
+
+ if (block_scope)
+ DECL_CONTEXT (vardecl) = current_function_decl;
+ else if (current_class_type)
+ DECL_CONTEXT (vardecl) = current_class_type;
+ else
+ DECL_CONTEXT (vardecl) = current_namespace;
+
+ if (processing_template_decl)
+ vardecl = push_template_decl (vardecl);
+
+ id = cp_parser_declarator_id (parser, false);
+
+ if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+ {
+ finish_omp_structured_block (block);
+ goto fail;
+ }
+
+ placeholder = build_lang_decl (VAR_DECL, id, type);
+ DECL_CONTEXT (placeholder) = DECL_CONTEXT (vardecl);
+ if (processing_template_decl)
+ placeholder = push_template_decl (placeholder);
+ pushdecl (placeholder);
+ cp_finish_decl (placeholder, NULL_TREE, 0, NULL_TREE, 0);
+ DECL_ARTIFICIAL (placeholder) = 1;
+ TREE_USED (placeholder) = 1;
+
+ while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL))
+ {
+ pragma_omp_clause c_kind = cp_parser_omp_clause_name (parser);
+ if (c_kind != PRAGMA_OMP_CLAUSE_MAP)
+ {
+ if (c_kind != PRAGMA_OMP_CLAUSE_NONE)
+ cp_parser_error (parser, "unexpected clause");
+ finish_omp_structured_block (block);
+ goto fail;
+ }
+ maplist = cp_parser_omp_clause_map (parser, maplist, GOMP_MAP_UNSET);
+ if (maplist == NULL_TREE)
+ break;
+ }
+
+ if (maplist == NULL_TREE)
+ {
+ cp_parser_error (parser, "missing %<map%> clause");
+ finish_omp_structured_block (block);
+ goto fail;
+ }
+
+ mapper = make_node (OMP_DECLARE_MAPPER);
+ TREE_TYPE (mapper) = type;
+ OMP_DECLARE_MAPPER_ID (mapper) = mapper_name;
+ OMP_DECLARE_MAPPER_DECL (mapper) = placeholder;
+ OMP_DECLARE_MAPPER_CLAUSES (mapper) = maplist;
+
+ finish_omp_structured_block (block);
+
+ DECL_INITIAL (vardecl) = mapper;
+
+ if (current_class_type)
+ {
+ if (processing_template_decl)
+ {
+ retrofit_lang_decl (vardecl);
+ SET_DECL_VAR_DECLARED_INLINE_P (vardecl);
+ }
+ finish_static_data_member_decl (vardecl, mapper,
+ /*init_const_expr_p=*/true, NULL_TREE, 0);
+ finish_member_declaration (vardecl);
+ }
+ else if (processing_template_decl && block_scope)
+ add_decl_expr (vardecl);
+ else
+ pushdecl (vardecl);
+
+ cp_check_omp_declare_mapper (vardecl);
+
+ cp_parser_require_pragma_eol (parser, pragma_tok);
+ return;
+
+fail:
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+}
+
/* OpenMP 4.0
#pragma omp declare simd declare-simd-clauses[optseq] new-line
#pragma omp declare reduction (reduction-id : typename-list : expression) \
@@ -52180,6 +54321,12 @@ cp_parser_omp_declare (cp_parser *parser, cp_token *pragma_tok,
context);
return false;
}
+ if (strcmp (p, "mapper") == 0)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ cp_parser_omp_declare_mapper (parser, pragma_tok, context);
+ return false;
+ }
if (!flag_openmp) /* flag_openmp_simd */
{
cp_parser_skip_to_pragma_eol (parser, pragma_tok);
@@ -52193,7 +54340,7 @@ cp_parser_omp_declare (cp_parser *parser, cp_token *pragma_tok,
}
}
cp_parser_error (parser, "expected %<simd%>, %<reduction%>, "
- "%<target%> or %<variant%>");
+ "%<target%>, %<mapper%> or %<variant%>");
cp_parser_require_pragma_eol (parser, pragma_tok);
return false;
}
@@ -52968,8 +55115,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;
@@ -53671,7 +55818,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/parser.h b/gcc/cp/parser.h
index f9ed801..b8cf763 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -419,6 +419,13 @@ struct GTY(()) cp_parser {
/* TRUE if an OpenMP array section is allowed. */
bool omp_array_section_p;
+ /* TRUE if an OpenMP array-shaping operator is allowed. */
+ bool omp_array_shaping_op_p;
+
+ /* TRUE if we are parsing an expression with an OpenMP array-shaping
+ operator. */
+ bool omp_has_array_shape_p;
+
/* Tracks the function's template parameter list when declaring a function
using generic type parameters. This is either a new chain in the case of a
fully implicit function template or an extension of the function's existing
@@ -456,6 +463,11 @@ struct GTY(()) cp_parser {
outside that file. */
struct omp_metadirective_parse_data * GTY((skip))
omp_metadirective_state;
+
+ /* TREE_LIST of "omp begin declare variant" functions when the base
+ function has not been seen "elsewhere" (per the OpenMP spec) yet;
+ used only when these functions are seen in a class definition. */
+ tree omp_unregistered_variants;
};
/* In parser.cc */
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index be9af50..fc2b31f 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -12151,36 +12151,159 @@ tsubst_attribute (tree t, tree *decl_p, tree args,
++cp_unevaluated_operand;
tree varid = tsubst_expr (TREE_PURPOSE (val), args, complain, in_decl);
--cp_unevaluated_operand;
- tree chain = TREE_CHAIN (val);
+ tree chain = copy_list (TREE_CHAIN (val));
location_t match_loc = cp_expr_loc_or_input_loc (TREE_PURPOSE (chain));
tree ctx = copy_list (TREE_VALUE (val));
- tree append_args_list = TREE_CHAIN (TREE_CHAIN (chain));
- if (append_args_list
- && TREE_VALUE (append_args_list)
- && TREE_CHAIN (TREE_VALUE (append_args_list)))
- {
- append_args_list = TREE_VALUE (append_args_list);
- append_args_list = TREE_VALUE (TREE_CHAIN (append_args_list));
- for (; append_args_list;
- append_args_list = TREE_CHAIN (append_args_list))
- {
- tree pref_list = TREE_VALUE (append_args_list);
- if (pref_list == NULL_TREE || TREE_CODE (pref_list) != TREE_LIST)
+ /* These asserts may seem strange but the layout of this attribute is
+ really difficult to grok and remember. They should be left in until
+ we refactor the layout of the stored nodes. */
+ gcc_assert (TREE_CHAIN (chain));
+ /* These nodes were copied by copy_list above, don't copy it again. */
+ tree omp_variant_clauses = TREE_CHAIN (TREE_CHAIN (chain));
+ gcc_checking_assert (!omp_variant_clauses
+ || TREE_PURPOSE (omp_variant_clauses)
+ == get_identifier ("omp variant clauses temp"));
+ tree adjust_args_idxs = NULL_TREE;
+ if (omp_variant_clauses)
+ {
+ gcc_assert (TREE_VALUE (omp_variant_clauses));
+ adjust_args_idxs = copy_node (TREE_VALUE (omp_variant_clauses));
+ gcc_assert (adjust_args_idxs);
+ gcc_checking_assert (TREE_PURPOSE (adjust_args_idxs)
+ == get_identifier ("omp adjust args idxs"));
+ /* copy_node doesn't copy the CHAIN. */
+ if (adjust_args_idxs)
+ {
+ if (TREE_CHAIN (TREE_VALUE (omp_variant_clauses)))
+ TREE_CHAIN (adjust_args_idxs)
+ = copy_node (TREE_CHAIN (TREE_VALUE (omp_variant_clauses)));
+ TREE_VALUE (omp_variant_clauses) = adjust_args_idxs;
+ }
+ }
+ if (adjust_args_idxs
+ && TREE_CHAIN (adjust_args_idxs))
+ {
+ /* This only needs to be copied if node PURPOSE is NULL_TREE, or if
+ there is a dependent prefer type node. It's hard to determine
+ this though, so don't try to handle it conditionally for now. */
+ tree append_args_node = TREE_CHAIN (adjust_args_idxs);
+ /* PURPOSE holds the count of real args, it doesn't need to be copied
+ because it will just be replaced if it needs to be changed.
+ VALUE holds the list of append_args. */
+ append_args_node = copy_node (append_args_node);
+ /* It's too hard to figure out if we have anything dependent,
+ unconditionally copy this list. */
+ tree append_args_head = copy_list (TREE_VALUE (append_args_node));
+ for (tree n = append_args_head; n != NULL_TREE; n = TREE_CHAIN (n))
+ {
+ tree pref_list = TREE_VALUE (n);
+ if (pref_list == NULL_TREE
+ || TREE_CODE (pref_list) != TREE_LIST)
+ /* Not a pref_list. */
continue;
- tree fr_list = TREE_VALUE (pref_list);
+ tree fr_list = copy_node (TREE_VALUE (pref_list));
int len = TREE_VEC_LENGTH (fr_list);
+ /* Track if substitution occurs.
+ I'm not really sure that this even works the way I hope it
+ does, really I'm pretty sure tsubst_expr will basically always
+ return some sort of copy. The proper solution is probably
+ marking the list as dependent during parsing. */
+ bool substituted = false;
for (int i = 0; i < len; i++)
{
- tree *fr_expr = &TREE_VEC_ELT (fr_list, i);
- /* Preserve NOP_EXPR to have a location. */
- if (*fr_expr && TREE_CODE (*fr_expr) == NOP_EXPR)
- TREE_OPERAND (*fr_expr, 0)
- = tsubst_expr (TREE_OPERAND (*fr_expr, 0), args, complain,
- in_decl);
- else
- *fr_expr = tsubst_expr (*fr_expr, args, complain, in_decl);
+ tree elt = TREE_VEC_ELT (fr_list, i);
+ if (!elt)
+ continue;
+ tree expr = TREE_CODE (elt) == NOP_EXPR
+ ? TREE_OPERAND (elt, 0)
+ : elt;
+ tree new_expr = tsubst_expr (expr, args, complain, in_decl);
+ if (new_expr != expr)
+ {
+ substituted = true;
+ if (TREE_CODE (elt) == NOP_EXPR)
+ {
+ tree copied_nop = copy_node (elt);
+ TREE_OPERAND (copied_nop, 0) = new_expr;
+ TREE_VEC_ELT (fr_list, i) = copied_nop;
+ }
+ else
+ TREE_VEC_ELT (fr_list, i) = new_expr;
+ }
+ }
+ if (substituted)
+ {
+ pref_list = copy_node (pref_list);
+ TREE_VALUE (pref_list) = fr_list;
+ /* This node gets mutated in cp_finish_omp_init_prefer_type
+ if fr_list is dependent, it needs to be copied. */
+ TREE_PURPOSE (pref_list)
+ = copy_node (TREE_PURPOSE (pref_list));
+ TREE_VALUE (n) = pref_list;
}
}
+ TREE_VALUE (append_args_node) = append_args_head;
+ TREE_CHAIN (adjust_args_idxs) = append_args_node;
+ }
+ gcc_assert (TREE_CHAIN (chain));
+
+ tree adjust_args_list = adjust_args_idxs
+ ? TREE_VALUE (adjust_args_idxs)
+ : NULL_TREE;
+
+ if (adjust_args_list && ATTR_IS_DEPENDENT (adjust_args_list))
+ {
+ tree copied = copy_list (adjust_args_list);
+ /* Substitute numeric ranges, we also need to copy ranges with
+ relative bounds, in theory it's possible for them to get expanded
+ while one bound is still dependent. */
+ for (tree n = copied; n; n = TREE_CHAIN (n))
+ {
+ tree item = TREE_VALUE (n);
+ const tree_code code = TREE_CODE (TREE_VALUE (item));
+ gcc_assert (code == PARM_DECL
+ || code == INTEGER_CST
+ || code == TREE_LIST);
+ if (code == TREE_LIST)
+ {
+ tree range = TREE_VALUE (item);
+ gcc_assert (TREE_CODE (range) == TREE_LIST);
+ auto tsubst_bound = [&] (tree bound)
+ {
+ gcc_assert (bound != NULL_TREE);
+ if (bound == error_mark_node
+ || !ATTR_IS_DEPENDENT (bound))
+ {
+ /* As above, copy the bound if it is relative. */
+ if (TREE_PURPOSE (bound) != NULL_TREE)
+ return copy_node (bound);
+ else
+ return bound;
+ }
+ tree expr = TREE_VALUE (bound);
+ tree subst_expr
+ = tsubst_expr (expr, args, complain, in_decl);
+ gcc_assert (subst_expr != expr);
+ tree new_bound = build_tree_list (TREE_PURPOSE (bound),
+ subst_expr);
+ return new_bound;
+ };
+ tree lb = tsubst_bound (TREE_PURPOSE (range));
+ tree ub = tsubst_bound (TREE_VALUE (range));
+ /* Only build a new range if substitution occured. */
+ if (lb != TREE_PURPOSE (range)
+ || ub != TREE_VALUE (range))
+ {
+ tree new_range = build_tree_list (lb, ub);
+ /* We don't need to copy the purpose, it just holds
+ a location. */
+ TREE_VALUE (n) = build_tree_list (TREE_PURPOSE (item),
+ new_range);
+ }
+ }
+ }
+ TREE_VALUE (adjust_args_idxs) = copied;
+ TREE_VALUE (omp_variant_clauses) = adjust_args_idxs;
}
for (tree tss = ctx; tss; tss = TREE_CHAIN (tss))
{
@@ -15904,7 +16027,10 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain,
= remove_attribute ("visibility", DECL_ATTRIBUTES (r));
}
determine_visibility (r);
- if ((!local_p || TREE_STATIC (t)) && DECL_SECTION_NAME (t))
+ if ((!local_p || TREE_STATIC (t))
+ && !(flag_openmp && DECL_LANG_SPECIFIC (t)
+ && DECL_OMP_DECLARE_MAPPER_P (t))
+ && DECL_SECTION_NAME (t))
set_decl_section_name (r, t);
}
@@ -15956,6 +16082,13 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain,
SET_TYPE_STRUCTURAL_EQUALITY (TREE_TYPE (r));
}
+ if (flag_openmp
+ && VAR_P (t)
+ && DECL_LANG_SPECIFIC (t)
+ && DECL_OMP_DECLARE_MAPPER_P (t)
+ && strchr (IDENTIFIER_POINTER (DECL_NAME (t)), '~') == NULL)
+ DECL_NAME (r) = omp_mapper_id (DECL_NAME (t), TREE_TYPE (r));
+
layout_decl (r, 0);
}
break;
@@ -17252,6 +17385,10 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
member access. */
id = false;
type = finish_decltype_type (type, id, complain);
+
+ if (DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (t)
+ && TYPE_REF_P (type))
+ type = TREE_TYPE (type);
}
return cp_build_qualified_type (type,
cp_type_quals (t)
@@ -17847,9 +17984,7 @@ tsubst_omp_clause_decl (tree decl, tree args, tsubst_flags_t complain,
return decl;
/* Handle OpenMP iterators. */
- if (TREE_CODE (decl) == TREE_LIST
- && TREE_PURPOSE (decl)
- && TREE_CODE (TREE_PURPOSE (decl)) == TREE_VEC)
+ if (OMP_ITERATOR_DECL_P (decl))
{
tree ret;
if (iterator_cache[0] == TREE_PURPOSE (decl))
@@ -17911,14 +18046,17 @@ tsubst_omp_clause_decl (tree decl, tree args, tsubst_flags_t complain,
= tsubst_stmt (TREE_OPERAND (decl, 1), args, complain, in_decl);
tree length = tsubst_stmt (TREE_OPERAND (decl, 2), args, complain,
in_decl);
+ tree stride = tsubst_stmt (TREE_OPERAND (decl, 3), args, complain,
+ in_decl);
tree base = tsubst_omp_clause_decl (TREE_OPERAND (decl, 0), args,
complain, in_decl, NULL);
if (TREE_OPERAND (decl, 0) == base
&& TREE_OPERAND (decl, 1) == low_bound
- && TREE_OPERAND (decl, 2) == length)
+ && TREE_OPERAND (decl, 2) == length
+ && TREE_OPERAND (decl, 3) == stride)
return decl;
- return build3 (OMP_ARRAY_SECTION, TREE_TYPE (base), base, low_bound,
- length);
+ return build4 (OMP_ARRAY_SECTION, TREE_TYPE (base), base, low_bound,
+ length, stride);
}
tree ret = tsubst_stmt (decl, args, complain, in_decl);
/* Undo convert_from_reference tsubst_expr could have called. */
@@ -18220,8 +18358,10 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
}
new_clauses = nreverse (new_clauses);
- if (ort != C_ORT_OMP_DECLARE_SIMD)
+ if (ort != C_ORT_OMP_DECLARE_SIMD && ort != C_ORT_OMP_DECLARE_MAPPER)
{
+ if (ort & C_ORT_OMP)
+ new_clauses = c_omp_instantiate_mappers (new_clauses, ort);
new_clauses = finish_omp_clauses (new_clauses, ort);
if (linear_no_step)
for (nc = new_clauses; nc; nc = OMP_CLAUSE_CHAIN (nc))
@@ -19668,6 +19808,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:
@@ -19736,7 +19901,9 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
case OMP_TARGET_UPDATE:
case OMP_TARGET_ENTER_DATA:
case OMP_TARGET_EXIT_DATA:
- tmp = tsubst_omp_clauses (OMP_STANDALONE_CLAUSES (t), C_ORT_OMP, args,
+ tmp = tsubst_omp_clauses (OMP_STANDALONE_CLAUSES (t),
+ (TREE_CODE (t) == OMP_TARGET_EXIT_DATA
+ ? C_ORT_OMP_EXIT_DATA : C_ORT_OMP), args,
complain, in_decl);
t = copy_node (t);
OMP_STANDALONE_CLAUSES (t) = tmp;
@@ -19941,6 +20108,22 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
break;
}
+ case OMP_DECLARE_MAPPER:
+ {
+ t = copy_node (t);
+
+ tree decl = OMP_DECLARE_MAPPER_DECL (t);
+ decl = tsubst (decl, args, complain, in_decl);
+ tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
+ tree clauses = OMP_DECLARE_MAPPER_CLAUSES (t);
+ clauses = tsubst_omp_clauses (clauses, C_ORT_OMP_DECLARE_MAPPER, args,
+ complain, in_decl);
+ TREE_TYPE (t) = type;
+ OMP_DECLARE_MAPPER_DECL (t) = decl;
+ OMP_DECLARE_MAPPER_CLAUSES (t) = clauses;
+ RETURN (t);
+ }
+
case TRANSACTION_EXPR:
{
int flags = 0;
@@ -20797,6 +20980,14 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
RETURN (cp_build_bit_cast (EXPR_LOCATION (t), type, op0, complain));
}
+ case OMP_ARRAYSHAPE_CAST_EXPR:
+ {
+ tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
+ tree op0 = RECUR (TREE_OPERAND (t, 0));
+ RETURN (cp_build_omp_arrayshape_cast (EXPR_LOCATION (t), type, op0,
+ complain));
+ }
+
case POSTDECREMENT_EXPR:
case POSTINCREMENT_EXPR:
op1 = tsubst_non_call_postfix_expression (TREE_OPERAND (t, 0),
@@ -20982,7 +21173,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
case OMP_ARRAY_SECTION:
{
tree op0 = RECUR (TREE_OPERAND (t, 0));
- tree op1 = NULL_TREE, op2 = NULL_TREE;
+ tree op1 = NULL_TREE, op2 = NULL_TREE, op3 = NULL_TREE;
if (op0 == error_mark_node)
RETURN (error_mark_node);
if (TREE_OPERAND (t, 1))
@@ -20997,7 +21188,31 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
if (op2 == error_mark_node)
RETURN (error_mark_node);
}
- RETURN (build_omp_array_section (EXPR_LOCATION (t), op0, op1, op2));
+ if (TREE_OPERAND (t, 3))
+ {
+ op3 = RECUR (TREE_OPERAND (t, 3));
+ if (op3 == error_mark_node)
+ RETURN (error_mark_node);
+ }
+ RETURN (build_omp_array_section (EXPR_LOCATION (t), op0, op1, op2,
+ op3));
+ }
+
+ case OMP_DECLARE_MAPPER:
+ {
+ t = copy_node (t);
+
+ tree decl = OMP_DECLARE_MAPPER_DECL (t);
+ DECL_OMP_DECLARE_MAPPER_P (decl) = 1;
+ decl = tsubst (decl, args, complain, in_decl);
+ tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
+ tree clauses = OMP_DECLARE_MAPPER_CLAUSES (t);
+ clauses = tsubst_omp_clauses (clauses, C_ORT_OMP_DECLARE_MAPPER, args,
+ complain, in_decl);
+ TREE_TYPE (t) = type;
+ OMP_DECLARE_MAPPER_DECL (t) = decl;
+ OMP_DECLARE_MAPPER_CLAUSES (t) = clauses;
+ RETURN (t);
}
case SIZEOF_EXPR:
@@ -27967,7 +28182,9 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p)
|| (external_p && VAR_P (d))
/* Handle here a deleted function too, avoid generating
its body (c++/61080). */
- || deleted_p)
+ || deleted_p
+ /* We need the initializer for an OpenMP declare mapper. */
+ || (VAR_P (d) && DECL_LANG_SPECIFIC (d) && DECL_OMP_DECLARE_MAPPER_P (d)))
{
/* The definition of the static data member is now required so
we must substitute the initializer. */
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 804c22d..f1523ed 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3. If not see
#include "gomp-constants.h"
#include "predict.h"
#include "memmodel.h"
+#include "gimplify.h"
/* There routines provide a modular interface to perform many parsing
operations. They may therefore be used during actual parsing, or
@@ -4010,6 +4011,13 @@ finish_translation_unit (void)
"#pragma omp end declare target");
vec_safe_truncate (scope_chain->omp_declare_target_attribute, 0);
}
+ if (vec_safe_length (scope_chain->omp_declare_variant_attribute))
+ {
+ if (!errorcount)
+ error ("%<omp begin declare variant%> without corresponding "
+ "%<omp end declare variant%>");
+ vec_safe_truncate (scope_chain->omp_declare_variant_attribute, 0);
+ }
if (vec_safe_length (scope_chain->omp_begin_assumes))
{
if (!errorcount)
@@ -5906,14 +5914,17 @@ public:
<= FIRST_NON_ONE we diagnose non-contiguous arrays if low bound isn't
0 or length isn't the array domain max + 1, for > FIRST_NON_ONE we
can if MAYBE_ZERO_LEN is false. MAYBE_ZERO_LEN will be true in the above
- case though, as some lengths could be zero. */
+ case though, as some lengths could be zero.
+ NON_CONTIGUOUS will be true if this is an OpenACC non-contiguous array
+ section. */
static tree
handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
bool &maybe_zero_len, unsigned int &first_non_one,
- enum c_omp_region_type ort)
+ bool &non_contiguous, enum c_omp_region_type ort,
+ int *discontiguous)
{
- tree ret, low_bound, length, type;
+ tree ret, low_bound, length, stride, type;
bool openacc = (ort & C_ORT_ACC) != 0;
if (TREE_CODE (t) != OMP_ARRAY_SECTION)
{
@@ -5975,18 +5986,26 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
&& TREE_CODE (TREE_OPERAND (t, 0)) == FIELD_DECL)
TREE_OPERAND (t, 0) = omp_privatize_field (TREE_OPERAND (t, 0), false);
ret = handle_omp_array_sections_1 (c, TREE_OPERAND (t, 0), types,
- maybe_zero_len, first_non_one, ort);
+ maybe_zero_len, first_non_one,
+ non_contiguous, ort, discontiguous);
if (ret == error_mark_node || ret == NULL_TREE)
return ret;
- type = TREE_TYPE (ret);
+ if (TREE_CODE (ret) == OMP_ARRAY_SECTION)
+ type = TREE_TYPE (TREE_TYPE (TREE_OPERAND (ret, 0)));
+ else
+ type = TREE_TYPE (ret);
low_bound = TREE_OPERAND (t, 1);
length = TREE_OPERAND (t, 2);
+ stride = TREE_OPERAND (t, 3);
if ((low_bound && type_dependent_expression_p (low_bound))
- || (length && type_dependent_expression_p (length)))
+ || (length && type_dependent_expression_p (length))
+ || (stride && type_dependent_expression_p (stride)))
return NULL_TREE;
- if (low_bound == error_mark_node || length == error_mark_node)
+ if (low_bound == error_mark_node
+ || length == error_mark_node
+ || stride == error_mark_node)
return error_mark_node;
if (low_bound && !INTEGRAL_TYPE_P (TREE_TYPE (low_bound)))
@@ -6003,15 +6022,26 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
length);
return error_mark_node;
}
+ if (stride && !INTEGRAL_TYPE_P (TREE_TYPE (stride)))
+ {
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "stride %qE of array section does not have integral type",
+ stride);
+ return error_mark_node;
+ }
if (low_bound)
low_bound = mark_rvalue_use (low_bound);
if (length)
length = mark_rvalue_use (length);
+ if (stride)
+ stride = mark_rvalue_use (stride);
/* We need to reduce to real constant-values for checks below. */
if (length)
length = fold_simple (length);
if (low_bound)
low_bound = fold_simple (low_bound);
+ if (stride)
+ stride = fold_simple (stride);
if (low_bound
&& TREE_CODE (low_bound) == INTEGER_CST
&& TYPE_PRECISION (TREE_TYPE (low_bound))
@@ -6022,9 +6052,15 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
&& TYPE_PRECISION (TREE_TYPE (length))
> TYPE_PRECISION (sizetype))
length = fold_convert (sizetype, length);
+ if (stride
+ && TREE_CODE (stride) == INTEGER_CST
+ && TYPE_PRECISION (TREE_TYPE (stride))
+ > TYPE_PRECISION (sizetype))
+ stride = fold_convert (sizetype, stride);
if (low_bound == NULL_TREE)
low_bound = integer_zero_node;
-
+ if (stride == NULL_TREE)
+ stride = size_one_node;
if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
&& (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
|| OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
@@ -6143,12 +6179,29 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
}
if (length && TREE_CODE (length) == INTEGER_CST)
{
- if (tree_int_cst_lt (size, length))
+ tree slength = length;
+ if (stride && TREE_CODE (stride) == INTEGER_CST)
{
- error_at (OMP_CLAUSE_LOCATION (c),
- "length %qE above array section size "
- "in %qs clause", length,
- omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+ slength = size_binop (MULT_EXPR,
+ fold_convert (sizetype, length),
+ fold_convert (sizetype, stride));
+ slength = size_binop (MINUS_EXPR,
+ slength,
+ fold_convert (sizetype, stride));
+ slength = size_binop (PLUS_EXPR, slength, size_one_node);
+ }
+ if (tree_int_cst_lt (size, slength))
+ {
+ if (stride)
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "length %qE with stride %qE above array "
+ "section size in %qs clause", length, stride,
+ omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+ else
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "length %qE above array section size "
+ "in %qs clause", length,
+ omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
return error_mark_node;
}
if (TREE_CODE (low_bound) == INTEGER_CST)
@@ -6156,7 +6209,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
tree lbpluslen
= size_binop (PLUS_EXPR,
fold_convert (sizetype, low_bound),
- fold_convert (sizetype, length));
+ fold_convert (sizetype, slength));
if (TREE_CODE (lbpluslen) == INTEGER_CST
&& tree_int_cst_lt (size, lbpluslen))
{
@@ -6226,12 +6279,38 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
d = TREE_OPERAND (d, 0))
{
tree d_length = TREE_OPERAND (d, 2);
- if (d_length == NULL_TREE || !integer_onep (d_length))
+ tree d_stride = TREE_OPERAND (d, 3);
+ if (d_length == NULL_TREE
+ || !integer_onep (d_length)
+ || (d_stride && !integer_onep (d_stride)))
{
- error_at (OMP_CLAUSE_LOCATION (c),
- "array section is not contiguous in %qs clause",
- omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
- return error_mark_node;
+ if (openacc && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP)
+ {
+ while (TREE_CODE (d) == OMP_ARRAY_SECTION)
+ d = TREE_OPERAND (d, 0);
+ if (DECL_P (d))
+ {
+ /* Note that OpenACC does accept these kinds of
+ non-contiguous pointer based arrays. */
+ non_contiguous = true;
+ break;
+ }
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "base-pointer expression in %qs clause not "
+ "supported for non-contiguous arrays",
+ omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+ return error_mark_node;
+ }
+
+ if (discontiguous && *discontiguous)
+ *discontiguous = 2;
+ else
+ {
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "array section is not contiguous in %qs clause",
+ omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+ return error_mark_node;
+ }
}
}
}
@@ -6243,7 +6322,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
return error_mark_node;
}
if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND)
- types.safe_push (TREE_TYPE (ret));
+ types.safe_push (type);
/* We will need to evaluate lb more than once. */
tree lb = cp_save_expr (low_bound);
if (lb != low_bound)
@@ -6262,29 +6341,59 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION
|| OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION
|| OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TASK_REDUCTION);
- ret = grok_array_decl (OMP_CLAUSE_LOCATION (c), ret, low_bound, NULL,
- tf_warning_or_error);
+ /* NOTE: Stride/length are discarded for affinity/depend here. */
+ if (discontiguous
+ && *discontiguous
+ && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_AFFINITY
+ && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND)
+ ret = grok_omp_array_section (OMP_CLAUSE_LOCATION (c), ret, low_bound,
+ length, stride);
+ else
+ ret = grok_array_decl (OMP_CLAUSE_LOCATION (c), ret, low_bound, NULL,
+ tf_warning_or_error);
return ret;
}
-/* Handle array sections for clause C. */
+/* We built a reference to an array section, but it turns out we only need a
+ set of ARRAY_REFs to the lower bound. Rewrite the node. */
+
+static tree
+omp_array_section_low_bound (location_t loc, tree node)
+{
+ if (TREE_CODE (node) == OMP_ARRAY_SECTION)
+ {
+ tree low_bound = TREE_OPERAND (node, 1);
+ tree ret
+ = omp_array_section_low_bound (loc, TREE_OPERAND (node, 0));
+ return grok_array_decl (loc, ret, low_bound, NULL, tf_warning_or_error);
+ }
+
+ return node;
+}
+
+/* Handle array sections for clause C. On entry *DISCONTIGUOUS is 0 if array
+ section must be contiguous, 1 if it can be discontiguous, and in the latter
+ case it is set to 2 on exit if it is determined to be discontiguous during
+ the function's execution. PC points to the clause to be processed, and
+ *PNEXT to the last mapping node created, if passed as non-NULL. */
static bool
-handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
+handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort,
+ int *discontiguous, bool *strided = NULL)
{
+ tree c = *pc;
bool maybe_zero_len = false;
unsigned int first_non_one = 0;
+ bool non_contiguous = false;
auto_vec<tree, 10> types;
tree *tp = &OMP_CLAUSE_DECL (c);
if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND
|| OMP_CLAUSE_CODE (c) == OMP_CLAUSE_AFFINITY)
- && TREE_CODE (*tp) == TREE_LIST
- && TREE_PURPOSE (*tp)
- && TREE_CODE (TREE_PURPOSE (*tp)) == TREE_VEC)
+ && OMP_ITERATOR_DECL_P (*tp))
tp = &TREE_VALUE (*tp);
tree first = handle_omp_array_sections_1 (c, *tp, types,
maybe_zero_len, first_non_one,
- ort);
+ non_contiguous, ort, discontiguous);
if (first == error_mark_node)
return true;
if (first == NULL_TREE)
@@ -6319,12 +6428,15 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
unsigned int num = types.length (), i;
tree t, side_effects = NULL_TREE, size = NULL_TREE;
tree condition = NULL_TREE;
+ tree ncarray_dims = NULL_TREE;
if (int_size_in_bytes (TREE_TYPE (first)) <= 0)
maybe_zero_len = true;
if (processing_template_decl && maybe_zero_len)
return false;
+ bool higher_discontiguous = false;
+
for (i = num, t = OMP_CLAUSE_DECL (c); i > 0;
t = TREE_OPERAND (t, 0))
{
@@ -6332,6 +6444,7 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
tree low_bound = TREE_OPERAND (t, 1);
tree length = TREE_OPERAND (t, 2);
+ tree stride = TREE_OPERAND (t, 3);
i--;
if (low_bound
@@ -6344,12 +6457,66 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
&& TYPE_PRECISION (TREE_TYPE (length))
> TYPE_PRECISION (sizetype))
length = fold_convert (sizetype, length);
+ if (stride
+ && TREE_CODE (stride) == INTEGER_CST
+ && TYPE_PRECISION (TREE_TYPE (stride))
+ > TYPE_PRECISION (sizetype))
+ stride = fold_convert (sizetype, stride);
if (low_bound == NULL_TREE)
low_bound = integer_zero_node;
+
+ if (non_contiguous)
+ {
+ ncarray_dims = tree_cons (low_bound, length, ncarray_dims);
+ continue;
+ }
+
+ if (stride == NULL_TREE)
+ stride = size_one_node;
+ if (strided && !integer_onep (stride))
+ *strided = true;
+ if (discontiguous && *discontiguous)
+ {
+ /* This condition is similar to the error check below, but
+ whereas that checks for a definitely-discontiguous array
+ section in order to report an error (where such a section is
+ illegal), here we instead need to know if the array section
+ *may be* discontiguous so we can handle that case
+ appropriately (i.e. for rectangular "target update"
+ operations). */
+ bool full_span = false;
+ if (length != NULL_TREE
+ && TREE_CODE (length) == INTEGER_CST
+ && TREE_CODE (types[i]) == ARRAY_TYPE
+ && TYPE_DOMAIN (types[i])
+ && TYPE_MAX_VALUE (TYPE_DOMAIN (types[i]))
+ && TREE_CODE (TYPE_MAX_VALUE (TYPE_DOMAIN (types[i])))
+ == INTEGER_CST)
+ {
+ tree size;
+ size = size_binop (PLUS_EXPR,
+ TYPE_MAX_VALUE (TYPE_DOMAIN (types[i])),
+ size_one_node);
+ if (tree_int_cst_equal (length, size))
+ full_span = true;
+ }
+
+ if (!integer_onep (stride)
+ || (higher_discontiguous
+ && (!integer_zerop (low_bound)
+ || !full_span)))
+ *discontiguous = 2;
+
+ if (!integer_onep (stride)
+ || !integer_zerop (low_bound)
+ || !full_span)
+ higher_discontiguous = true;
+ }
+
if (!maybe_zero_len && i > first_non_one)
{
if (integer_nonzerop (low_bound))
- goto do_warn_noncontiguous;
+ goto is_noncontiguous;
if (length != NULL_TREE
&& TREE_CODE (length) == INTEGER_CST
&& TYPE_DOMAIN (types[i])
@@ -6363,12 +6530,17 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
size_one_node);
if (!tree_int_cst_equal (length, size))
{
- do_warn_noncontiguous:
- error_at (OMP_CLAUSE_LOCATION (c),
- "array section is not contiguous in %qs "
- "clause",
- omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
- return true;
+ is_noncontiguous:
+ if (discontiguous && *discontiguous)
+ *discontiguous = 2;
+ else
+ {
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "array section is not contiguous in %qs "
+ "clause",
+ omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+ return true;
+ }
}
}
if (!processing_template_decl
@@ -6437,6 +6609,14 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
}
if (!processing_template_decl)
{
+ if (non_contiguous)
+ {
+ int kind = OMP_CLAUSE_MAP_KIND (c);
+ OMP_CLAUSE_SET_MAP_KIND (c, kind | GOMP_MAP_NONCONTIG_ARRAY);
+ OMP_CLAUSE_DECL (c) = t;
+ OMP_CLAUSE_SIZE (c) = ncarray_dims;
+ return false;
+ }
if (side_effects)
size = build2 (COMPOUND_EXPR, sizetype, side_effects, size);
if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION
@@ -6477,6 +6657,9 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
OMP_CLAUSE_DECL (c) = t;
return false;
}
+ if (discontiguous && *discontiguous != 2)
+ first = omp_array_section_low_bound (OMP_CLAUSE_LOCATION (c),
+ first);
OMP_CLAUSE_DECL (c) = first;
if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
return false;
@@ -6488,9 +6671,6 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
if (TREE_CODE (t) == FIELD_DECL)
t = finish_non_static_data_member (t, NULL_TREE, NULL_TREE);
- if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
- return false;
-
if (TREE_CODE (first) == INDIRECT_REF)
{
/* Detect and skip adding extra nodes for pointer-to-member
@@ -6517,6 +6697,10 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
}
}
+ if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+ && !(discontiguous && *discontiguous == 2))
+ return false;
+
/* FIRST represents the first item of data that we are mapping.
E.g. if we're mapping an array, FIRST might resemble
"foo.bar.myarray[0]". */
@@ -6528,23 +6712,28 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
cp_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
- tree nc = ai.expand_map_clause (c, first, addr_tokens, ort);
- if (nc != error_mark_node)
+ tree* npc = ai.expand_map_clause (pc, first, addr_tokens, ort);
+ if (npc != NULL)
{
using namespace omp_addr_tokenizer;
- if (ai.maybe_zero_length_array_section (c))
+ c = *pc;
+
+ if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+ && ai.maybe_zero_length_array_section (c))
OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
/* !!! If we're accessing a base decl via chained access
methods (e.g. multiple indirections), duplicate clause
detection won't work properly. Skip it in that case. */
- if ((addr_tokens[0]->type == STRUCTURE_BASE
+ if (pnext
+ && (addr_tokens[0]->type == STRUCTURE_BASE
|| addr_tokens[0]->type == ARRAY_BASE)
&& addr_tokens[0]->u.structure_base_kind == BASE_DECL
&& addr_tokens[1]->type == ACCESS_METHOD
&& omp_access_chain_p (addr_tokens, 1))
- c = nc;
+ /* NPC points to the last node in the new sequence. */
+ *pnext = npc;
return false;
}
@@ -6723,6 +6912,102 @@ omp_reduction_lookup (location_t loc, tree id, tree type, tree *baselinkp,
return id;
}
+/* Return identifier to look up for omp declare mapper. */
+
+tree
+omp_mapper_id (tree mapper_id, tree type)
+{
+ const char *p = NULL;
+ const char *m = NULL;
+
+ if (mapper_id == NULL_TREE)
+ p = "";
+ else if (TREE_CODE (mapper_id) == IDENTIFIER_NODE)
+ p = IDENTIFIER_POINTER (mapper_id);
+ else
+ return error_mark_node;
+
+ if (type != NULL_TREE)
+ m = mangle_type_string (TYPE_MAIN_VARIANT (type));
+
+ const char prefix[] = "omp declare mapper ";
+ size_t lenp = sizeof (prefix);
+ if (strncmp (p, prefix, lenp - 1) == 0)
+ lenp = 1;
+ size_t len = strlen (p);
+ size_t lenm = m ? strlen (m) + 1 : 0;
+ char *name = XALLOCAVEC (char, lenp + len + lenm);
+ memcpy (name, prefix, lenp - 1);
+ memcpy (name + lenp - 1, p, len + 1);
+ if (m)
+ {
+ name[lenp + len - 1] = '~';
+ memcpy (name + lenp + len, m, lenm);
+ }
+ return get_identifier (name);
+}
+
+tree
+cxx_omp_mapper_lookup (tree id, tree type)
+{
+ if (TREE_CODE (type) != RECORD_TYPE
+ && TREE_CODE (type) != UNION_TYPE)
+ return NULL_TREE;
+ id = omp_mapper_id (id, type);
+ return lookup_name (id);
+}
+
+tree
+cxx_omp_extract_mapper_directive (tree vardecl)
+{
+ gcc_assert (TREE_CODE (vardecl) == VAR_DECL);
+
+ /* Instantiate the decl if we haven't already. */
+ mark_used (vardecl);
+ tree body = DECL_INITIAL (vardecl);
+
+ if (TREE_CODE (body) == STATEMENT_LIST)
+ {
+ tree_stmt_iterator tsi = tsi_start (body);
+ gcc_assert (TREE_CODE (tsi_stmt (tsi)) == DECL_EXPR);
+ tsi_next (&tsi);
+ body = tsi_stmt (tsi);
+ }
+
+ gcc_assert (TREE_CODE (body) == OMP_DECLARE_MAPPER);
+
+ return body;
+}
+
+/* For now we can handle singleton OMP_ARRAY_SECTIONs with custom mappers, but
+ nothing more complicated. */
+
+tree
+cxx_omp_map_array_section (location_t loc, tree t)
+{
+ tree low = TREE_OPERAND (t, 1);
+ tree len = TREE_OPERAND (t, 2);
+
+ if (len && integer_onep (len))
+ {
+ t = TREE_OPERAND (t, 0);
+
+ if (!low)
+ low = integer_zero_node;
+
+ if (TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE)
+ t = convert_from_reference (t);
+
+ if (TYPE_PTR_P (TREE_TYPE (t))
+ || TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+ t = build_array_ref (loc, t, low);
+ else
+ t = error_mark_node;
+ }
+
+ return t;
+}
+
/* Helper function for cp_parser_omp_declare_reduction_exprs
and tsubst_omp_udr.
Remove CLEANUP_STMT for data (omp_priv variable).
@@ -6903,6 +7188,69 @@ cp_check_omp_declare_reduction (tree udr)
return true;
}
+
+static bool
+cp_oacc_reduction_defined_type_p (enum tree_code reduction_code, tree t)
+{
+ if (TREE_CODE (t) == INTEGER_TYPE)
+ return true;
+
+ if (FLOAT_TYPE_P (t) || TREE_CODE (t) == COMPLEX_TYPE)
+ switch (reduction_code)
+ {
+ case PLUS_EXPR:
+ case MULT_EXPR:
+ case MINUS_EXPR:
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ return true;
+ case MIN_EXPR:
+ case MAX_EXPR:
+ return TREE_CODE (t) != COMPLEX_TYPE;
+ case BIT_AND_EXPR:
+ case BIT_XOR_EXPR:
+ case BIT_IOR_EXPR:
+ return false;
+ default:
+ gcc_unreachable ();
+ }
+
+ if (TREE_CODE (t) == ARRAY_TYPE)
+ return cp_oacc_reduction_defined_type_p (reduction_code, TREE_TYPE (t));
+
+ if (TREE_CODE (t) == RECORD_TYPE)
+ {
+ for (tree fld = TYPE_FIELDS (t); fld; fld = TREE_CHAIN (fld))
+ if (TREE_CODE (fld) == FIELD_DECL
+ && !cp_oacc_reduction_defined_type_p (reduction_code,
+ TREE_TYPE (fld)))
+ return false;
+ return true;
+ }
+
+ return false;
+}
+
+static const char *
+cp_oacc_reduction_code_name (enum tree_code reduction_code)
+{
+ switch (reduction_code)
+ {
+ case PLUS_EXPR: return "+";
+ case MULT_EXPR: return "*";
+ case MINUS_EXPR: return "-";
+ case TRUTH_ANDIF_EXPR: return "&&";
+ case TRUTH_ORIF_EXPR: return "||";
+ case MIN_EXPR: return "min";
+ case MAX_EXPR: return "max";
+ case BIT_AND_EXPR: return "&";
+ case BIT_XOR_EXPR: return "^";
+ case BIT_IOR_EXPR: return "|";
+ default:
+ gcc_unreachable ();
+ }
+}
+
/* Helper function of finish_omp_clauses. Clone STMT as if we were making
an inline call. But, remap
the OMP_DECL1 VAR_DECL (omp_out resp. omp_orig) to PLACEHOLDER
@@ -6947,7 +7295,8 @@ find_omp_placeholder_r (tree *tp, int *, void *data)
Return true if there is some error and the clause should be removed. */
static bool
-finish_omp_reduction_clause (tree c, bool *need_default_ctor, bool *need_dtor)
+finish_omp_reduction_clause (tree c, enum c_omp_region_type ort,
+ bool *need_default_ctor, bool *need_dtor)
{
tree t = OMP_CLAUSE_DECL (c);
bool predefined = false;
@@ -7048,6 +7397,20 @@ finish_omp_reduction_clause (tree c, bool *need_default_ctor, bool *need_dtor)
return false;
}
+ if (ort == C_ORT_ACC)
+ {
+ enum tree_code r_code = OMP_CLAUSE_REDUCTION_CODE (c);
+ if (!cp_oacc_reduction_defined_type_p (r_code, TREE_TYPE (t)))
+ {
+ const char *r_name = cp_oacc_reduction_code_name (r_code);
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "%qE has invalid type for %<reduction(%s)%>",
+ t, r_name);
+ return true;
+ }
+ return false;
+ }
+
tree id = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c);
type = TYPE_MAIN_VARIANT (type);
@@ -7193,9 +7556,11 @@ finish_omp_reduction_clause (tree c, bool *need_default_ctor, bool *need_dtor)
*need_dtor = true;
else
{
- error_at (OMP_CLAUSE_LOCATION (c),
- "user defined reduction not found for %qE",
- omp_clause_printable_decl (t));
+ /* There are no user-defined reductions for OpenACC (as of 2.6). */
+ if (ort & C_ORT_OMP)
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "user defined reduction not found for %qE",
+ omp_clause_printable_decl (t));
return true;
}
if (TREE_CODE (OMP_CLAUSE_DECL (c)) == MEM_REF)
@@ -7204,6 +7569,29 @@ finish_omp_reduction_clause (tree c, bool *need_default_ctor, bool *need_dtor)
return false;
}
+/* Check an instance of an "omp declare mapper" function. */
+
+bool
+cp_check_omp_declare_mapper (tree udm)
+{
+ tree type = TREE_TYPE (udm);
+ location_t loc = DECL_SOURCE_LOCATION (udm);
+
+ if (type == error_mark_node)
+ return false;
+
+ if (!processing_template_decl
+ && TREE_CODE (type) != RECORD_TYPE
+ && TREE_CODE (type) != UNION_TYPE)
+ {
+ error_at (loc, "%qT is not a struct, union or class type in "
+ "%<#pragma omp declare mapper%>", type);
+ return false;
+ }
+
+ return true;
+}
+
/* Called from finish_struct_1. linear(this) or linear(this:step)
clauses might not be finalized yet because the class has been incomplete
when parsing #pragma omp declare simd methods. Fix those up now. */
@@ -7286,10 +7674,10 @@ cp_omp_finish_iterators (tree iter)
bool ret = false;
for (tree it = iter; it; it = TREE_CHAIN (it))
{
- tree var = TREE_VEC_ELT (it, 0);
- tree begin = TREE_VEC_ELT (it, 1);
- tree end = TREE_VEC_ELT (it, 2);
- tree step = TREE_VEC_ELT (it, 3);
+ tree var = OMP_ITERATORS_VAR (it);
+ tree begin = OMP_ITERATORS_BEGIN (it);
+ tree end = OMP_ITERATORS_END (it);
+ tree step = OMP_ITERATORS_STEP (it);
tree orig_step;
tree type = TREE_TYPE (var);
location_t loc = DECL_SOURCE_LOCATION (var);
@@ -7387,10 +7775,10 @@ cp_omp_finish_iterators (tree iter)
tree it2;
for (it2 = TREE_CHAIN (it); it2; it2 = TREE_CHAIN (it2))
{
- tree var2 = TREE_VEC_ELT (it2, 0);
- tree begin2 = TREE_VEC_ELT (it2, 1);
- tree end2 = TREE_VEC_ELT (it2, 2);
- tree step2 = TREE_VEC_ELT (it2, 3);
+ tree var2 = OMP_ITERATORS_VAR (it2);
+ tree begin2 = OMP_ITERATORS_BEGIN (it2);
+ tree end2 = OMP_ITERATORS_END (it2);
+ tree step2 = OMP_ITERATORS_STEP (it2);
location_t loc2 = DECL_SOURCE_LOCATION (var2);
if (cp_walk_tree (&begin2, find_omp_placeholder_r, var, &pset))
{
@@ -7416,14 +7804,14 @@ cp_omp_finish_iterators (tree iter)
ret = true;
continue;
}
- TREE_VEC_ELT (it, 1) = begin;
- TREE_VEC_ELT (it, 2) = end;
+ OMP_ITERATORS_BEGIN (it) = begin;
+ OMP_ITERATORS_END (it) = end;
if (processing_template_decl)
- TREE_VEC_ELT (it, 3) = orig_step;
+ OMP_ITERATORS_STEP (it) = orig_step;
else
{
- TREE_VEC_ELT (it, 3) = step;
- TREE_VEC_ELT (it, 4) = orig_step;
+ OMP_ITERATORS_STEP (it) = step;
+ OMP_ITERATORS_ORIG_STEP (it) = orig_step;
}
}
return ret;
@@ -7534,6 +7922,731 @@ cp_finish_omp_init_prefer_type (tree pref_type)
}
return t;
}
+/* LIST is a TREE_LIST, its TREE_PURPOSE is not used here, but its (possibly
+ NULL_TREE) value is propegated to any new nodes derived from the node.
+ Its TREE_VALUE is a TREE_LIST representing an OpenMP parameter-list-item.
+ Its TREE_PURPOSE contains an expr storing the location of the item and
+ TREE_VALUE contains the representation of the item. It is a NOP_EXPR,
+ INTEGER_CST, PARM_DECL, or TREE_LIST, this function possibly mutates it, it
+ is not preserved. DECL is the function the clause/clauses that LIST is
+ specified in is applied to. PARM_COUNT is the number of parameters, unless
+ the function has a parameter pack, or if the function is variadic, then
+ PARM_COUNT is 0. Functions with an empty parameter list are not handled
+ here.
+
+ Finalize each element in LIST, diagnose non-unique elements, and mutate
+ the original element appending them to a new list. The finalized parameter
+ indices are 0 based in contrast to OpenMP specifying 1 based parameter
+ indices, that adjustment is done here. NOP_EXPR elements require adjustment
+ from a 1 based index to a 0 based index. INTEGER_CST elements are finalized
+ parameter indices, but are still used for diagnosing duplicate elements.
+ PARM_DECL elements are switched out for their corresponding 0 based index,
+ provided it can be determined. TREE_LIST represents a numeric range. If
+ the number of parameters is known and DECL is non-variadic, relative bounds
+ are folded into literal bounds. If both bounds are non-relative the numeric
+ range is expanded, replacing the TREE_LIST with N INTEGER_CST nodes for each
+ index in the numeric range. If DECL is variadic, numeric ranges with
+ a relative bound are represented the same as in c_parser_omp_parm_list
+ so gimplify.cc:modify_call_for_omp_dispatch can handle them the same way.
+
+ Returns TREE_LIST or error_mark_node if each elt was invalid. */
+
+tree
+finish_omp_parm_list (tree list, const_tree decl, const int parm_count)
+{
+ gcc_assert (list && decl);
+ const tree parms = DECL_ARGUMENTS (decl);
+ /* We assume that the this parameter is included in parms, make sure this is
+ still true. */
+ gcc_assert (!DECL_IOBJ_MEMBER_FUNCTION_P (decl)
+ || is_this_parameter (parms));
+ /* We expect at least one argument, unless the function is variadic, in which
+ case parm_count will be 0. */
+ gcc_assert (parm_count >= 0 && (parms || parm_count == 0));
+
+ hash_map<int_hash<int, -1, -2>, tree> specified_idxs;
+ /* If there are any nodes that were not able to be finalized and/or expanded
+ this gets set to true, this can occur even if we are fully instantiated
+ if the function is a variadic function. */
+ bool dependent = false;
+ tree new_list = NULL_TREE;
+ auto append_to_list = [chain = &new_list] (tree node) mutable
+ {
+ gcc_assert (*chain == NULL_TREE);
+ *chain = node;
+ chain = &TREE_CHAIN (*chain);
+ };
+
+ const int iobj_parm_adjustment = DECL_IOBJ_MEMBER_FUNCTION_P (decl) ? 1 : 0;
+
+ for (tree next, node = list; node; node = next)
+ {
+ /* Nodes are mutated and appended to new_list, retrieve its chain early
+ and remember it. */
+ next = TREE_CHAIN (node);
+ TREE_CHAIN (node) = NULL_TREE;
+ tree parm_list_item = TREE_VALUE (node);
+ switch (TREE_CODE (TREE_VALUE (parm_list_item)))
+ {
+ case NOP_EXPR:
+ {
+ /* cp_parser_omp_parm_list stores parameter index items in a
+ NOP so we know to modify them here. This solution is
+ imperfect, but there isn't time to do it differently. */
+ tree cst = TREE_OPERAND (TREE_VALUE (parm_list_item), 0);
+ /* Adjust the 1 based index to 0 based, potentially adjust for
+ the 'this' parameter. */
+ const int idx = tree_to_shwi (cst) - 1 + iobj_parm_adjustment;
+ TREE_VALUE (parm_list_item)
+ = build_int_cst (integer_type_node, idx);
+ gcc_fallthrough ();
+ }
+ case INTEGER_CST:
+ {
+ /* These are finished, just check for errors and append
+ them to the list.
+ Check everything, we might have new errors if we didn't know
+ how many parameters we had the first time around. */
+ const int idx = tree_to_shwi (TREE_VALUE (parm_list_item));
+ tree *first = specified_idxs.get (idx);
+ if (first)
+ {
+ error_at (EXPR_LOCATION (TREE_PURPOSE (parm_list_item)),
+ "OpenMP parameter list items must specify a "
+ "unique parameter");
+ inform (EXPR_LOCATION (TREE_PURPOSE (*first)),
+ "parameter previously specified here");
+ }
+ else if (parm_count != 0 && idx >= parm_count)
+ {
+ error_at (EXPR_LOCATION (TREE_PURPOSE (parm_list_item)),
+ "parameter list item index is out of range");
+ }
+ else
+ {
+ append_to_list (node);
+ if (specified_idxs.put (idx, parm_list_item))
+ gcc_unreachable ();
+ }
+ break;
+ }
+ case PARM_DECL:
+ {
+ const const_tree parm_to_find = TREE_VALUE (parm_list_item);
+ /* Indices are stored as 0 based, don't adjust for iobj func,
+ the this parameter is present in parms. */
+ int idx = 0;
+ const_tree parm = parms;
+ while (true)
+ {
+ /* We already confirmed that the parameter exists
+ in cp_parser_omp_parm_list, we should never be reaching
+ the end of this list. */
+ gcc_assert (parm);
+ /* Expansion of a parameter pack will change the index of
+ parameters that come after it, we will have to defer this
+ lookup until the fndecl has been substituted. */
+ if (DECL_PACK_P (parm))
+ {
+ gcc_assert (processing_template_decl);
+ /* As explained above, this only happens with a parameter
+ pack in the middle of a function, this slower lookup
+ is fine for such an edge case. */
+ const tree first = [&] ()
+ {
+ for (tree n = new_list; n; n = TREE_CHAIN (n))
+ {
+ tree item = TREE_VALUE (n);
+ if (TREE_CODE (TREE_VALUE (item)) == PARM_DECL
+ /* This comparison works because we make sure
+ to store the original node. */
+ && TREE_VALUE (item) == parm_to_find)
+ return item;
+ }
+ return NULL_TREE;
+ } (); /* IILE. */
+ if (first)
+ {
+ location_t loc
+ = EXPR_LOCATION (TREE_PURPOSE (parm_list_item));
+ error_at (loc,
+ "OpenMP parameter list items must specify "
+ "a unique parameter");
+ inform (EXPR_LOCATION (TREE_PURPOSE (first)),
+ "parameter previously specified here");
+ }
+ else
+ {
+ /* We need to process this again once the pack
+ blocking us has been expanded. */
+ dependent = true;
+ /* Make sure we use the original so the above
+ comparison works when we return here later.
+ This may no longer be required since we are
+ comparing the DECL_NAME of each below, but
+ regardless, use the original. */
+ append_to_list (node);
+ }
+ break;
+ }
+ /* Compare the identifier nodes to so the comparison works
+ even after the node has been substituted. */
+ if (DECL_NAME (parm) == DECL_NAME (parm_to_find))
+ {
+ tree *first = specified_idxs.get (idx);
+ if (first)
+ {
+ location_t loc
+ = EXPR_LOCATION (TREE_PURPOSE (parm_list_item));
+ error_at (loc,
+ "OpenMP parameter list items must specify "
+ "a unique parameter");
+ inform (EXPR_LOCATION (TREE_PURPOSE (*first)),
+ "parameter previously specified here");
+ }
+ else
+ {
+ TREE_VALUE (parm_list_item)
+ = build_int_cst (integer_type_node, idx);
+ append_to_list (node);
+ if (specified_idxs.put (idx, parm_list_item))
+ gcc_unreachable ();
+ }
+ break;
+ }
+ ++idx;
+ parm = DECL_CHAIN (parm);
+ }
+ break;
+ }
+ case TREE_LIST:
+ {
+ /* Mutates bound.
+ This is the final point where indices and ranges are adjusted
+ from OpenMP spec representation (1 based indices, inclusive
+ intervals [lb, ub]) to GCC's internal representation (0 based
+ indices, inclusive lb, exclusive ub [lb, ub)), this is
+ intended to match C++ semantics.
+ Care must be taken to ensure we do not make these adjustments
+ multiple times. */
+ auto do_bound = [&] (tree bound, const int correction)
+ {
+ gcc_assert (-1 <= correction && correction <= 1);
+ if (bound == error_mark_node)
+ return bound;
+
+ tree expr = TREE_VALUE (bound);
+ /* If we already have an integer_cst, the bound has already
+ been converted to a 0 based index. Do not strip location
+ wrappers, they might be a template parameter that got
+ substituted to an INTEGER_CST, but not been finalized
+ yet. */
+ if (TREE_CODE (expr) == INTEGER_CST)
+ return bound;
+
+ const location_t expr_loc = EXPR_LOCATION (expr);
+
+ if (type_dependent_expression_p (expr))
+ {
+ ATTR_IS_DEPENDENT (bound) = true;
+ return bound;
+ }
+ else if (!INTEGRAL_TYPE_P (TREE_TYPE (expr)))
+ {
+ if (TREE_PURPOSE (bound))
+ error_at (expr_loc, "logical offset of a bound must "
+ "be of type %<int%>");
+ else
+ error_at (expr_loc, "expression of a bound must be "
+ "of type %<int%>");
+ return error_mark_node;
+ }
+ else if (value_dependent_expression_p (expr))
+ {
+ ATTR_IS_DEPENDENT (bound) = true;
+ return bound;
+ }
+ /* EXPR is not dependent, get rid of any leftover location
+ wrappers. */
+ expr = tree_strip_any_location_wrapper (expr);
+ /* Unless we want some really good diagnostics, we don't need
+ to wrap expr with a location anymore. Additionally, if we
+ do that we need a new way of differentiating adjusted and
+ unadjusted expressions. */
+
+ /* Do we need to mark this as an rvalue use with
+ mark_rvalue_use as well?
+ We either need to strictly only accept expressions of type
+ int, or warn for conversions.
+ I'm pretty sure this should be manifestly
+ constant-evaluated. We require a constant here,
+ let fold_non_dependent_expr complain. */
+ expr = fold_non_dependent_expr (expr,
+ tf_warning_or_error,
+ true);
+ if (!TREE_CONSTANT (expr))
+ {
+ if (TREE_PURPOSE (bound))
+ error_at (expr_loc, "logical offset of a bound must "
+ "be a constant expression");
+ else
+ error_at (expr_loc, "expression of a bound must be a "
+ "constant expression");
+ return error_mark_node;
+ }
+
+ const int sgn = tree_int_cst_sgn (expr);
+ const int val = tree_to_shwi (expr);
+ /* Technically this can work with omp_num_args+expr but the
+ spec forbids it, we can support it later if we like. */
+ if (sgn < 0)
+ {
+ if (TREE_PURPOSE (bound))
+ error_at (expr_loc, "logical offset of a bound must "
+ "be non negative");
+ else
+ error_at (expr_loc, "expression of a bound must be "
+ "positive");
+ return error_mark_node;
+ }
+
+ const const_tree num_args_marker = TREE_PURPOSE (bound);
+ if (num_args_marker == NULL_TREE)
+ {
+ if (sgn != 1)
+ {
+ error_at (expr_loc, "expression of bound must be "
+ "positive");
+ return error_mark_node;
+ }
+ if (parm_count > 0 && val > parm_count)
+ {
+ /* FIXME: output omp_num_args and parm_count. */
+ error_at (expr_loc, "expression of bound is out "
+ "of range");
+ return error_mark_node;
+ }
+ TREE_VALUE (bound) = build_int_cst (integer_type_node,
+ val + correction);
+ return bound;
+ }
+ else if (num_args_marker
+ == get_identifier ("omp num args plus"))
+ {
+ if (sgn != 0)
+ {
+ error_at (expr_loc,
+ "logical offset must be equal to 0 in a "
+ "bound of the form "
+ "%<omp_num_args+logical-offset%>");
+ return error_mark_node;
+ }
+ TREE_PURPOSE (bound)
+ = get_identifier ("omp relative bound");
+ /* This expresses
+ omp_num_args + correction + logical offset,
+ the only valid value for logical offset is 0. */
+ TREE_VALUE (bound) = build_int_cst (integer_type_node,
+ correction + 0);
+ return bound;
+ }
+ else if (num_args_marker
+ == get_identifier ("omp num args minus"))
+ {
+ gcc_assert (sgn != -1);
+ TREE_PURPOSE (bound)
+ = get_identifier ("omp relative bound");
+ /* Don't invert correction, we are expressing
+ omp_num_args + correction - logical offset. */
+ TREE_VALUE (bound) = build_int_cst (integer_type_node,
+ correction + (-val));
+ return bound;
+ }
+ gcc_unreachable ();
+ };
+ /* Convert both to 0 based indices, upper bound
+ is stored one past the end. */
+ static constexpr int lb_adjustment = -1;
+ static constexpr int ub_adjustment = -1 + 1;
+
+ tree range = TREE_VALUE (parm_list_item);
+ tree lb = do_bound (TREE_PURPOSE (range),
+ lb_adjustment + iobj_parm_adjustment);
+ tree ub = do_bound (TREE_VALUE (range),
+ ub_adjustment + iobj_parm_adjustment);
+ gcc_assert (lb && ub);
+ /* If we know how many params there are for sure we can
+ change this bound to a literal. */
+ auto maybe_fold_relative_bound = [&] (tree bound)
+ {
+ if (bound == error_mark_node
+ || parm_count == 0
+ || !TREE_PURPOSE (bound))
+ return bound;
+ gcc_assert (TREE_PURPOSE (bound)
+ == get_identifier ("omp relative bound"));
+ const int value = tree_to_shwi (TREE_VALUE (bound));
+ gcc_assert (value <= 0 && parm_count >= 1);
+ /* Overflow is impossible. */
+ const int diff = parm_count + value;
+ if (diff < 0)
+ {
+ /* FIXME: output value of omp_num_args. */
+ error_at (EXPR_LOCATION (TREE_CHAIN (bound)),
+ "bound with logical offset evaluates to an "
+ "out of range index");
+ return error_mark_node;
+ }
+ gcc_assert (diff < INT_MAX);
+ TREE_PURPOSE (bound) = NULL_TREE;
+ TREE_VALUE (bound) = build_int_cst (integer_type_node, diff);
+ return bound;
+ };
+ lb = maybe_fold_relative_bound (lb);
+ ub = maybe_fold_relative_bound (ub);
+
+ gcc_assert (lb && ub);
+ const tree range_loc_wrapped = TREE_PURPOSE (parm_list_item);
+
+ auto append_one_idx = [&] (tree purpose, tree loc_expr, int idx)
+ {
+ tree *dupe = specified_idxs.get (idx);
+ gcc_assert (!dupe || *dupe);
+ if (dupe)
+ return *dupe;
+ tree cst = build_int_cst (integer_type_node, idx);
+ tree new_item = build_tree_list (loc_expr, cst);
+ append_to_list (build_tree_list (purpose, new_item));
+ if (specified_idxs.put (idx, parm_list_item))
+ gcc_unreachable ();
+ return NULL_TREE;
+ };
+
+ /* TODO: handle better lol. */
+ if (lb == error_mark_node || ub == error_mark_node)
+ continue;
+ /* Wait until both lb and ub are substituted before trying to
+ process any further, we are also done if both bounds are
+ relative. */
+ if ((TREE_PURPOSE (lb) && TREE_PURPOSE (ub))
+ || value_dependent_expression_p (TREE_VALUE (lb))
+ || value_dependent_expression_p (TREE_VALUE (ub)))
+ {
+ /* If we are instantiating and have unexpanded numeric ranges
+ then this function must be variadic, and thus it doesn't
+ make this parm list dependent.
+ This doesn't really matter since we are high-jacking this
+ flag but it doesn't hurt to be technically correct. */
+ /* Early escape...? */
+ }
+ /* If both bounds are non relative, we can fully expand them. */
+ else if (!TREE_PURPOSE (lb) && !TREE_PURPOSE (ub))
+ {
+ const int lb_val = tree_to_shwi (TREE_VALUE (lb));
+ const int ub_val = tree_to_shwi (TREE_VALUE (ub));
+ /* Empty ranges are not allowed at this point. */
+ if (lb_val >= ub_val)
+ {
+ /* Note that the error message does not match the
+ condition as we altered ub to be one past the end. */
+ error_at (EXPR_LOCATION (range_loc_wrapped),
+ "numeric range lower bound must be less than "
+ "or equal to upper bound");
+ continue;
+ }
+ gcc_assert (lb_val >= 0 && ub_val > 0 && lb_val < ub_val);
+
+ for (int idx = lb_val; idx < ub_val; ++idx)
+ {
+ /* There might be something in PURPOSE that we want to
+ propogate when expanding. */
+ tree dupe = append_one_idx (TREE_PURPOSE (node),
+ range_loc_wrapped,
+ idx);
+ if (dupe)
+ {
+ const int omp_idx = idx + 1;
+ error_at (EXPR_LOCATION (range_loc_wrapped),
+ "expansion of numeric range specifies "
+ "non-unique index %d",
+ omp_idx);
+ inform (EXPR_LOCATION (TREE_PURPOSE (dupe)),
+ "parameter previously specified here");
+ }
+ }
+ /* The range is fully expanded, do not add it back to the
+ list. */
+ TREE_CHAIN (node) = NULL_TREE;
+ continue;
+ }
+ else if (!processing_template_decl)
+ {
+ /* Wait until we are fully instantiated to make this
+ transformation, expanding a bound with omp_num_args after
+ doing this will cause bugs.
+ We also potentially cause bugs if one gets expanded, gets
+ a partial expansion here, and then the other bound gets
+ expanded later. That case is probably fine but we should
+ avoid it anyway. */
+ gcc_assert (!TREE_PURPOSE (lb)
+ || TREE_PURPOSE (lb)
+ == get_identifier ("omp relative bound"));
+ gcc_assert (!TREE_PURPOSE (ub)
+ || TREE_PURPOSE (ub)
+ == get_identifier ("omp relative bound"));
+ /* At least one of lb and ub are NULL_TREE, and the other
+ is omp relative bound. */
+ gcc_assert (TREE_PURPOSE (lb) != TREE_PURPOSE (ub));
+ /* This only adds slight quality of life to diagnostics, it
+ isn't really worth it, but we need parity with the C front
+ end. Alternatively, handling empty numeric ranges could
+ have been removed from modify_call_for_omp_dispatch but
+ it's already there and it isn't that hard to add support
+ here. */
+ if (TREE_PURPOSE (ub))
+ {
+ /* The C front end goes a little further adding all
+ indices between lb and the last real parameter,
+ we aren't going to those efforts here though. */
+ gcc_assert (!TREE_PURPOSE (lb));
+ const int val = tree_to_shwi (TREE_VALUE (lb));
+ gcc_assert (val < INT_MAX);
+ /* We know the index in lb will always be specified. */
+ tree dupe = append_one_idx (TREE_PURPOSE (node),
+ range_loc_wrapped,
+ val);
+ if (dupe)
+ {
+ error_at (EXPR_LOCATION (range_loc_wrapped),
+ "lower bound of numeric range specifies "
+ "non-unique index %d",
+ val);
+ inform (EXPR_LOCATION (TREE_PURPOSE (dupe)),
+ "parameter previously specified here");
+ }
+ /* The value was added above, adjust lb to be ahead by
+ one so it's not added again in
+ modify_call_for_omp_dispatch. */
+ TREE_VALUE (lb) = build_int_cst (integer_type_node,
+ val + 1);
+ }
+ else
+ {
+ gcc_assert (TREE_PURPOSE (lb));
+ const int val = tree_to_shwi (TREE_VALUE (ub));
+ gcc_assert (val > 0);
+ /* We know the index in ub will always be specified. */
+ tree dupe = append_one_idx (TREE_PURPOSE (node),
+ range_loc_wrapped,
+ val);
+ if (dupe)
+ {
+ error_at (EXPR_LOCATION (range_loc_wrapped),
+ "upper bound of numeric range specifies "
+ "non-unique index %d", val);
+ inform (EXPR_LOCATION (TREE_PURPOSE (dupe)),
+ "parameter previously specified here");
+ }
+ /* The value was added above, adjust ub to be behind by
+ one so it's not added again in
+ modify_call_for_omp_dispatch. */
+ TREE_VALUE (ub) = build_int_cst (integer_type_node,
+ val - 1);
+ }
+ /* This is not a full expansion, just a partial, we still
+ to add the numeric range to the final list. */
+ }
+ dependent = processing_template_decl;
+ TREE_PURPOSE (range) = lb;
+ TREE_VALUE (range) = ub;
+ TREE_VALUE (parm_list_item) = range;
+ append_to_list (node);
+ break;
+ }
+ default:
+ gcc_unreachable ();
+ }
+ }
+ if (!new_list)
+ return error_mark_node;
+ /* Kinda a hack, hopefully temporary. */
+ ATTR_IS_DEPENDENT (new_list) = dependent;
+ return new_list;
+}
+
+/* LIST is a TREE_LIST representing an OpenMP parameter-list specified in an
+ adjust_args clause, or multiple concatanated parameter-lists each specified
+ in an adjust_args clause, each of which may have the same clause modifier,
+ or different clause modifiers. The clause modifier of the adjust_args
+ clause the parameter-list-item was specified in is stored in the
+ TREE_PURPOSE of each elt of LIST. DECL is the function decl the clauses
+ are applied to, PARM_COUNT is 0 if the number of parameters is unknown
+ or because the function is variadic, otherwise PARM_COUNT is the number of
+ parameters.
+
+ Check for and diagnose invalid parameter types for each item, remove them
+ from the list so errors are not diagnosed multiple times. Remove items with
+ the "nothing" modifier once everything is done.
+
+ Returns TREE_LIST or NULL_TREE if no items have errors, returns TREE_LIST
+ or error_mark_node if there were errors diagnosed. NULL_TREE is never
+ returned if an error was diagnosed. */
+
+tree
+finish_omp_adjust_args (tree list, const_tree decl, const int parm_count)
+{
+ gcc_assert (list && decl);
+ /* We need to keep track of this so we know whether we can remove items with
+ the "nothing" modifier. */
+ bool has_dependent_list_items = false;
+
+ const const_tree need_device_ptr_id = get_identifier ("need_device_ptr");
+ const const_tree need_device_addr_id = get_identifier ("need_device_addr");
+ const const_tree nothing_id = get_identifier ("nothing");
+ tree *prev_chain = &list;
+ auto keep_node = [&] (tree n) { prev_chain = &TREE_CHAIN (n); };
+ auto remove_node = [&] (tree n) { *prev_chain = TREE_CHAIN (n); };
+ for (tree n = list; n != NULL_TREE; n = TREE_CHAIN (n))
+ {
+ tree parm_list_item = TREE_VALUE (n);
+ if (TREE_CODE (TREE_VALUE (parm_list_item)) == PARM_DECL)
+ {
+ /* We only encounter a PARM_DECL here if a parameter pack comes
+ before it, it will have been replaced by an index by
+ finish_omp_parm_list otherwise. */
+ gcc_assert (processing_template_decl);
+ keep_node (n);
+ has_dependent_list_items = true;
+ continue;
+ }
+ /* Numeric range case. */
+ if (TREE_CODE (TREE_VALUE (parm_list_item)) == TREE_LIST)
+ {
+ /* These will have been expanded by finish_omp_parm_list unless we
+ can't determine the number of parameters. */
+ gcc_assert (processing_template_decl || parm_count == 0);
+ keep_node (n);
+ has_dependent_list_items = true;
+ continue;
+ }
+ gcc_assert (TREE_CODE (TREE_VALUE (parm_list_item)) == INTEGER_CST);
+
+ const const_tree n_modifier = TREE_PURPOSE (n);
+ gcc_assert (n_modifier == nothing_id
+ || n_modifier == need_device_ptr_id
+ || n_modifier == need_device_addr_id);
+ if (n_modifier == nothing_id)
+ {
+ keep_node (n);
+ continue;
+ }
+ const int idx = tree_to_shwi (TREE_VALUE (parm_list_item));
+
+ gcc_assert (idx >= 0 && (parm_count == 0 || idx < parm_count));
+ const const_tree parm_decl = [&] () -> const_tree
+ {
+ const const_tree parms = DECL_ARGUMENTS (decl);
+ gcc_assert (parms != NULL_TREE || parm_count == 0);
+ if (parms == NULL_TREE
+ || (parm_count != 0 && idx >= parm_count))
+ return NULL_TREE;
+
+ int cur_idx = 0;
+ for (const_tree p = parms; p != NULL_TREE; p = DECL_CHAIN (p))
+ {
+ /* This kind of sucks, we really should be building a vec instead
+ of traversing the list of parms each time. */
+ gcc_assert (parm_count == 0 || cur_idx < parm_count);
+ if (DECL_PACK_P (p))
+ return NULL_TREE;
+ if (cur_idx == idx)
+ return p;
+ ++cur_idx;
+ }
+ return NULL_TREE;
+ } (); /* IILE. */
+ /* cp_parser_omp_parm_list handles out of range indices. */
+ gcc_assert (parm_count == 0 || parm_decl);
+
+ if (!parm_decl)
+ has_dependent_list_items = true;
+ else if (n_modifier == need_device_ptr_id)
+ {
+ /* OpenMP 6.0 (332:28-30)
+ If the need_device_ptr adjust-op modifier is present, each list
+ item that appears in the clause that refers to a specific named
+ argument in the declaration of the function variant must be of
+ pointer type or reference to pointer type. */
+ tree parm_type = TREE_TYPE (parm_decl);
+ if (WILDCARD_TYPE_P (parm_type))
+ /* Do nothing for now, it might become a pointer. */;
+ else if (TYPE_REF_P (parm_type)
+ && WILDCARD_TYPE_P (TREE_TYPE (parm_type)))
+ /* It might become a reference to a pointer. */;
+ else if (!TYPE_PTR_P (parm_type))
+ {
+ if (TYPE_REF_P (parm_type)
+ && TYPE_PTR_P (TREE_TYPE (parm_type)))
+ /* The semantics for this are unclear, instead of supporting
+ it incorrectly, just sorry. */
+ sorry_at (DECL_SOURCE_LOCATION (parm_decl),
+ "parameter with type reference to pointer in an "
+ "%<adjust_args%> with the %<need_device_ptr%> "
+ "modifier is not currently supported");
+ else
+ error_at (DECL_SOURCE_LOCATION (parm_decl),
+ "parameter specified in an %<adjust_args%> clause "
+ "with the %<need_device_ptr%> modifier must be of "
+ "pointer type");
+ inform (EXPR_LOCATION (TREE_PURPOSE (parm_list_item)),
+ "parameter specified here");
+ remove_node (n);
+ continue;
+ }
+ }
+ else if (n_modifier == need_device_addr_id)
+ {
+ /* OpenMP 6.0 (332:31-33)
+ If the need_device_addr adjust-op modifier is present, each list
+ item that appears in the clause must refer to an argument in the
+ declaration of the function variant that has a reference type. */
+ tree parm_type = TREE_TYPE (parm_decl);
+ if (WILDCARD_TYPE_P (parm_type))
+ /* Do nothing for now, it might become a ref. */;
+ else if (!TYPE_REF_P (parm_type))
+ {
+ error_at (DECL_SOURCE_LOCATION (parm_decl),
+ "parameter specified in an %<adjust_args%> clause "
+ "with the %<need_device_addr%> modifier must be of "
+ "reference type");
+ inform (EXPR_LOCATION (TREE_PURPOSE (parm_list_item)),
+ "parameter specified here");
+ remove_node (n);
+ continue;
+ }
+ }
+ /* If we get here there were no errors. */
+ keep_node (n);
+ }
+
+ /* All items were removed due to errors. */
+ if (!list)
+ return error_mark_node;
+ if (has_dependent_list_items)
+ return list;
+ /* We no longer need to keep items with the "nothing" modifier. */
+ prev_chain = &list;
+ for (tree n = list; n != NULL_TREE; n = TREE_CHAIN (n))
+ {
+ if (TREE_PURPOSE (n) == nothing_id)
+ remove_node (n);
+ else
+ keep_node (n);
+ }
+ /* If all items had the "nothing" modifier, we might have NULL_TREE here,
+ but that isn't a problem. */
+ return list;
+}
/* For all elements of CLAUSES, validate them vs OpenMP constraints.
Remove any elements from the list that are invalid. */
@@ -7603,7 +8716,14 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
/* We've reached the end of a list of expanded nodes. Reset the group
start pointer. */
if (c == grp_sentinel)
- grp_start_p = NULL;
+ {
+ if (grp_start_p
+ && OMP_CLAUSE_HAS_ITERATORS (*grp_start_p))
+ for (tree gc = *grp_start_p; gc != grp_sentinel;
+ gc = OMP_CLAUSE_CHAIN (gc))
+ OMP_CLAUSE_ITERATORS (gc) = OMP_CLAUSE_ITERATORS (*grp_start_p);
+ grp_start_p = NULL;
+ }
switch (OMP_CLAUSE_CODE (c))
{
@@ -7632,7 +8752,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
t = OMP_CLAUSE_DECL (c);
if (TREE_CODE (t) == OMP_ARRAY_SECTION)
{
- if (handle_omp_array_sections (c, ort))
+ if (handle_omp_array_sections (pc, NULL, ort, NULL))
{
remove = true;
break;
@@ -8677,14 +9797,107 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
break;
}
gcc_unreachable ();
+ case OMP_CLAUSE_USES_ALLOCATORS:
+ t = OMP_CLAUSE_USES_ALLOCATORS_ALLOCATOR (c);
+ if (TREE_CODE (t) == FIELD_DECL)
+ {
+ sorry_at (OMP_CLAUSE_LOCATION (c), "class members not yet "
+ "supported in %<uses_allocators%> clause");
+ remove = true;
+ break;
+ }
+ t = convert_from_reference (t);
+ if (TREE_CODE (TREE_TYPE (t)) != ENUMERAL_TYPE
+ || strcmp (IDENTIFIER_POINTER (TYPE_IDENTIFIER (TREE_TYPE (t))),
+ "omp_allocator_handle_t") != 0)
+ {
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "allocator must be of %<omp_allocator_handle_t%> type");
+ remove = true;
+ break;
+ }
+ if (TREE_CODE (t) == CONST_DECL)
+ {
+ /* Currently for pre-defined allocators in libgomp, we do not
+ require additional init/fini inside target regions, so discard
+ such clauses. */
+ remove = true;
+
+ if (strcmp (IDENTIFIER_POINTER (DECL_NAME (t)),
+ "omp_null_allocator") == 0)
+ {
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "%<omp_null_allocator%> cannot be used in "
+ "%<uses_allocators%> clause");
+ break;
+ }
+
+ if (OMP_CLAUSE_USES_ALLOCATORS_MEMSPACE (c)
+ || OMP_CLAUSE_USES_ALLOCATORS_TRAITS (c))
+ {
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "modifiers cannot be used with pre-defined "
+ "allocators");
+ break;
+ }
+ }
+ t = OMP_CLAUSE_USES_ALLOCATORS_MEMSPACE (c);
+ if (t != NULL_TREE
+ && (TREE_CODE (t) != CONST_DECL
+ || TREE_CODE (TREE_TYPE (t)) != ENUMERAL_TYPE
+ || strcmp (IDENTIFIER_POINTER (TYPE_IDENTIFIER (TREE_TYPE (t))),
+ "omp_memspace_handle_t") != 0))
+ {
+ error_at (OMP_CLAUSE_LOCATION (c), "memspace modifier must be "
+ "constant enum of %<omp_memspace_handle_t%> type");
+ remove = true;
+ break;
+ }
+ t = OMP_CLAUSE_USES_ALLOCATORS_TRAITS (c);
+ if (t != NULL_TREE)
+ {
+ bool type_err = false;
+
+ if (TREE_CODE (TREE_TYPE (t)) != ARRAY_TYPE
+ || DECL_SIZE (t) == NULL_TREE)
+ type_err = true;
+ else
+ {
+ tree elem_t = TREE_TYPE (TREE_TYPE (t));
+ if (TREE_CODE (elem_t) != RECORD_TYPE
+ || strcmp (IDENTIFIER_POINTER (TYPE_IDENTIFIER (elem_t)),
+ "omp_alloctrait_t") != 0
+ || !TYPE_READONLY (elem_t))
+ type_err = true;
+ }
+ if (type_err)
+ {
+ error_at (OMP_CLAUSE_LOCATION (c), "traits array %qE must be of "
+ "%<const omp_alloctrait_t []%> type", t);
+ remove = true;
+ }
+ else
+ {
+ tree cst_val = decl_constant_value (t);
+ if (cst_val == t)
+ {
+ error_at (OMP_CLAUSE_LOCATION (c), "traits array must be "
+ "of constant values");
+
+ remove = true;
+ }
+ }
+ }
+ if (remove)
+ break;
+ pc = &OMP_CLAUSE_CHAIN (c);
+ continue;
case OMP_CLAUSE_DEPEND:
depend_clause = c;
/* FALLTHRU */
case OMP_CLAUSE_AFFINITY:
t = OMP_CLAUSE_DECL (c);
- if (TREE_CODE (t) == TREE_LIST
- && TREE_PURPOSE (t)
- && TREE_CODE (TREE_PURPOSE (t)) == TREE_VEC)
+ if (OMP_ITERATOR_DECL_P (t))
{
if (TREE_PURPOSE (t) != last_iterators)
last_iterators_remove
@@ -8699,7 +9912,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
if (TREE_CODE (t) == OMP_ARRAY_SECTION)
{
- if (handle_omp_array_sections (c, ort))
+ int discontiguous = 1;
+ if (handle_omp_array_sections (pc, NULL, ort, &discontiguous))
remove = true;
else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND
&& (OMP_CLAUSE_DEPEND_KIND (c)
@@ -8848,9 +10062,25 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
case OMP_CLAUSE_MAP:
if (OMP_CLAUSE_MAP_IMPLICIT (c) && !implicit_moved)
goto move_implicit;
+ if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_PUSH_MAPPER_NAME
+ || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POP_MAPPER_NAME)
+ {
+ remove = true;
+ break;
+ }
+ if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_DIM
+ || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_STRIDE)
+ break;
/* FALLTHRU */
case OMP_CLAUSE_TO:
case OMP_CLAUSE_FROM:
+ if (OMP_CLAUSE_ITERATORS (c)
+ && cp_omp_finish_iterators (OMP_CLAUSE_ITERATORS (c)))
+ {
+ t = error_mark_node;
+ break;
+ }
+ /* FALLTHRU */
case OMP_CLAUSE__CACHE_:
{
using namespace omp_addr_tokenizer;
@@ -8861,11 +10091,25 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
{
grp_start_p = pc;
grp_sentinel = OMP_CLAUSE_CHAIN (c);
-
- if (handle_omp_array_sections (c, ort))
+ /* FIXME: Strided target updates not supported together with
+ iterators yet. */
+ int discontiguous
+ = (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO
+ || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM)
+ && !OMP_CLAUSE_ITERATORS (c);
+ bool strided = false;
+ tree *pnext = NULL;
+ if (handle_omp_array_sections (pc, &pnext, ort, &discontiguous,
+ &strided))
remove = true;
else
{
+ if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO
+ || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM)
+ && OMP_CLAUSE_ITERATORS (c) && strided)
+ sorry ("strided target updates with iterators");
+ /* We might have replaced the clause, so refresh C. */
+ c = *pc;
t = OMP_CLAUSE_DECL (c);
if (TREE_CODE (t) != OMP_ARRAY_SECTION
&& !type_dependent_expression_p (t)
@@ -8965,6 +10209,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
clauses, reset the OMP_CLAUSE_SIZE (representing a bias)
to zero here. */
OMP_CLAUSE_SIZE (c) = size_zero_node;
+ if (pnext)
+ c = *pnext;
break;
}
else if (type_dependent_expression_p (t))
@@ -9148,7 +10394,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
bitmap_set_bit (&map_firstprivate_head, DECL_UID (t));
else if (bitmap_bit_p (&map_head, DECL_UID (t))
&& !bitmap_bit_p (&map_field_head, DECL_UID (t))
- && ort != C_ORT_OMP
+ && ort != C_ORT_OMP && ort != C_ORT_OMP_TARGET
&& ort != C_ORT_OMP_EXIT_DATA)
{
if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
@@ -9213,10 +10459,10 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
{
grp_start_p = pc;
grp_sentinel = OMP_CLAUSE_CHAIN (c);
- tree nc = ai.expand_map_clause (c, OMP_CLAUSE_DECL (c),
- addr_tokens, ort);
- if (nc != error_mark_node)
- c = nc;
+ tree *npc = ai.expand_map_clause (pc, OMP_CLAUSE_DECL (c),
+ addr_tokens, ort);
+ if (npc != NULL)
+ c = *npc;
}
}
break;
@@ -9454,10 +10700,11 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
t = OMP_CLAUSE_DECL (c);
if (TREE_CODE (t) == OMP_ARRAY_SECTION)
{
- if (handle_omp_array_sections (c, ort))
+ if (handle_omp_array_sections (pc, NULL, ort, NULL))
remove = true;
else
{
+ c = *pc;
t = OMP_CLAUSE_DECL (c);
while (TREE_CODE (t) == OMP_ARRAY_SECTION)
t = TREE_OPERAND (t, 0);
@@ -9754,6 +11001,11 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
pc = &OMP_CLAUSE_CHAIN (c);
}
+ if (grp_start_p
+ && OMP_CLAUSE_HAS_ITERATORS (*grp_start_p))
+ for (tree gc = *grp_start_p; gc; gc = OMP_CLAUSE_CHAIN (gc))
+ OMP_CLAUSE_ITERATORS (gc) = OMP_CLAUSE_ITERATORS (*grp_start_p);
+
if (reduction_seen < 0 && (ordered_seen || schedule_seen))
reduction_seen = -2;
@@ -9994,7 +11246,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
if (processing_template_decl
&& !VAR_P (t) && TREE_CODE (t) != PARM_DECL)
break;
- if (finish_omp_reduction_clause (c, &need_default_ctor,
+ if (finish_omp_reduction_clause (c, ort, &need_default_ctor,
&need_dtor))
remove = true;
else
@@ -10467,6 +11719,8 @@ struct omp_target_walk_data
/* Local variables declared inside a BIND_EXPR, used to filter out such
variables when recording lambda_objects_accessed. */
hash_set<tree> local_decls;
+
+ omp_mapper_list<tree> *mappers;
};
/* Helper function of finish_omp_target_clauses, called via
@@ -10480,6 +11734,7 @@ finish_omp_target_clauses_r (tree *tp, int *walk_subtrees, void *ptr)
struct omp_target_walk_data *data = (struct omp_target_walk_data *) ptr;
tree current_object = data->current_object;
tree current_closure = data->current_closure;
+ omp_mapper_list<tree> *mlist = data->mappers;
/* References inside of these expression codes shouldn't incur any
form of mapping, so return early. */
@@ -10493,6 +11748,27 @@ finish_omp_target_clauses_r (tree *tp, int *walk_subtrees, void *ptr)
if (TREE_CODE (t) == OMP_CLAUSE)
return NULL_TREE;
+ if (!processing_template_decl)
+ {
+ tree aggr_type = NULL_TREE;
+
+ if (TREE_CODE (t) == COMPONENT_REF
+ && AGGREGATE_TYPE_P (TREE_TYPE (TREE_OPERAND (t, 0))))
+ aggr_type = TREE_TYPE (TREE_OPERAND (t, 0));
+ else if ((TREE_CODE (t) == VAR_DECL
+ || TREE_CODE (t) == PARM_DECL
+ || TREE_CODE (t) == RESULT_DECL)
+ && AGGREGATE_TYPE_P (TREE_TYPE (t)))
+ aggr_type = TREE_TYPE (t);
+
+ if (aggr_type)
+ {
+ tree mapper_fn = cxx_omp_mapper_lookup (NULL_TREE, aggr_type);
+ if (mapper_fn)
+ mlist->add_mapper (NULL_TREE, aggr_type, mapper_fn);
+ }
+ }
+
if (current_object)
{
tree this_expr = TREE_OPERAND (current_object, 0);
@@ -10595,10 +11871,48 @@ finish_omp_target_clauses (location_t loc, tree body, tree *clauses_ptr)
else
data.current_closure = NULL_TREE;
- cp_walk_tree_without_duplicates (&body, finish_omp_target_clauses_r, &data);
-
auto_vec<tree, 16> new_clauses;
+ if (!processing_template_decl)
+ {
+ hash_set<omp_name_type<tree> > seen_types;
+ auto_vec<tree> mapper_fns;
+ omp_mapper_list<tree> mlist (&seen_types, &mapper_fns);
+ data.mappers = &mlist;
+
+ cp_walk_tree_without_duplicates (&body, finish_omp_target_clauses_r,
+ &data);
+
+ unsigned int i;
+ tree mapper_fn;
+ FOR_EACH_VEC_ELT (mapper_fns, i, mapper_fn)
+ c_omp_find_nested_mappers (&mlist, mapper_fn);
+
+ FOR_EACH_VEC_ELT (mapper_fns, i, mapper_fn)
+ {
+ tree mapper = cxx_omp_extract_mapper_directive (mapper_fn);
+ if (mapper == error_mark_node)
+ continue;
+ tree mapper_name = OMP_DECLARE_MAPPER_ID (mapper);
+ tree decl = OMP_DECLARE_MAPPER_DECL (mapper);
+ if (BASELINK_P (mapper_fn))
+ mapper_fn = BASELINK_FUNCTIONS (mapper_fn);
+
+ tree c = build_omp_clause (loc, OMP_CLAUSE__MAPPER_BINDING_);
+ OMP_CLAUSE__MAPPER_BINDING__ID (c) = mapper_name;
+ OMP_CLAUSE__MAPPER_BINDING__DECL (c) = decl;
+ OMP_CLAUSE__MAPPER_BINDING__MAPPER (c) = mapper_fn;
+
+ new_clauses.safe_push (c);
+ }
+ }
+ else
+ {
+ data.mappers = NULL;
+ cp_walk_tree_without_duplicates (&body, finish_omp_target_clauses_r,
+ &data);
+ }
+
tree omp_target_this_expr = NULL_TREE;
tree *explicit_this_deref_map = NULL;
if (data.this_expr_accessed)
@@ -11847,6 +13161,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,
@@ -14053,6 +15660,45 @@ cp_build_bit_cast (location_t loc, tree type, tree arg,
return ret;
}
+/* Build an OpenMP array-shape cast of ARG to TYPE. */
+
+tree
+cp_build_omp_arrayshape_cast (location_t loc, tree type, tree arg,
+ tsubst_flags_t complain)
+{
+ if (error_operand_p (type))
+ return error_mark_node;
+
+ if (!dependent_type_p (type)
+ && !complete_type_or_maybe_complain (type, NULL_TREE, complain))
+ return error_mark_node;
+
+ if (error_operand_p (arg))
+ return error_mark_node;
+
+ if (!type_dependent_expression_p (arg) && !dependent_type_p (type))
+ {
+ if (!trivially_copyable_p (TREE_TYPE (arg)))
+ {
+ error_at (cp_expr_loc_or_loc (arg, loc),
+ "OpenMP array shape source type %qT "
+ "is not trivially copyable", TREE_TYPE (arg));
+ return error_mark_node;
+ }
+
+ /* A pointer to multi-dimensional array conversion isn't normally
+ allowed, but we force it here for array shape operators by creating
+ the node directly. We also want to avoid any overloaded conversions
+ the user might have defined, not that there are likely to be any. */
+ return build1_loc (loc, VIEW_CONVERT_EXPR, type, arg);
+ }
+
+ tree ret = build_min (OMP_ARRAYSHAPE_CAST_EXPR, type, arg);
+ SET_EXPR_LOCATION (ret, loc);
+
+ return ret;
+}
+
/* Diagnose invalid #pragma GCC unroll argument and adjust
it if needed. */
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 88f8f34..46418f9 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -1636,6 +1636,9 @@ structural_comptypes (tree t1, tree t2, int strict)
return false;
if (DECLTYPE_FOR_LAMBDA_PROXY (t1) != DECLTYPE_FOR_LAMBDA_PROXY (t2))
return false;
+ if (DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (t1)
+ != DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (t2))
+ return false;
if (!cp_tree_equal (DECLTYPE_TYPE_EXPR (t1), DECLTYPE_TYPE_EXPR (t2)))
return false;
break;
@@ -4865,12 +4868,12 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
tree
build_omp_array_section (location_t loc, tree array_expr, tree index,
- tree length)
+ tree length, tree stride)
{
if (TREE_CODE (array_expr) == TYPE_DECL
|| type_dependent_expression_p (array_expr))
- return build3_loc (loc, OMP_ARRAY_SECTION, NULL_TREE, array_expr, index,
- length);
+ return build4_loc (loc, OMP_ARRAY_SECTION, NULL_TREE, array_expr, index,
+ length, stride);
tree type = TREE_TYPE (array_expr);
gcc_assert (type);
@@ -4909,8 +4912,8 @@ build_omp_array_section (location_t loc, tree array_expr, tree index,
sectype = build_array_type (eltype, idxtype);
}
- return build3_loc (loc, OMP_ARRAY_SECTION, sectype, array_expr, index,
- length);
+ return build4_loc (loc, OMP_ARRAY_SECTION, sectype, array_expr, index,
+ length, stride);
}
/* Return whether OP is an expression of enum type cast to integer
@@ -8299,6 +8302,9 @@ check_for_casting_away_constness (location_t loc, tree src_type,
src_type, dest_type);
return true;
+ case OMP_ARRAYSHAPE_CAST_EXPR:
+ return true;
+
default:
gcc_unreachable();
}
@@ -10878,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