diff options
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/ChangeLog | 26 | ||||
-rw-r--r-- | gcc/cp/ChangeLog.omp | 332 | ||||
-rw-r--r-- | gcc/cp/constexpr.cc | 25 | ||||
-rw-r--r-- | gcc/cp/cp-gimplify.cc | 6 | ||||
-rw-r--r-- | gcc/cp/cp-objcp-common.cc | 1 | ||||
-rw-r--r-- | gcc/cp/cp-objcp-common.h | 9 | ||||
-rw-r--r-- | gcc/cp/cp-tree.def | 12 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 56 | ||||
-rw-r--r-- | gcc/cp/decl.cc | 410 | ||||
-rw-r--r-- | gcc/cp/decl2.cc | 26 | ||||
-rw-r--r-- | gcc/cp/error.cc | 30 | ||||
-rw-r--r-- | gcc/cp/mangle.cc | 1 | ||||
-rw-r--r-- | gcc/cp/operators.def | 1 | ||||
-rw-r--r-- | gcc/cp/parser.cc | 2646 | ||||
-rw-r--r-- | gcc/cp/parser.h | 12 | ||||
-rw-r--r-- | gcc/cp/pt.cc | 285 | ||||
-rw-r--r-- | gcc/cp/semantics.cc | 1800 | ||||
-rw-r--r-- | gcc/cp/typeck.cc | 21 |
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 |