diff options
Diffstat (limited to 'gcc/c')
-rw-r--r-- | gcc/c/ChangeLog.omp | 237 | ||||
-rw-r--r-- | gcc/c/c-decl.cc | 172 | ||||
-rw-r--r-- | gcc/c/c-lang.h | 8 | ||||
-rw-r--r-- | gcc/c/c-objc-common.h | 12 | ||||
-rw-r--r-- | gcc/c/c-parser.cc | 2391 | ||||
-rw-r--r-- | gcc/c/c-tree.h | 13 | ||||
-rw-r--r-- | gcc/c/c-typeck.cc | 616 |
7 files changed, 3201 insertions, 248 deletions
diff --git a/gcc/c/ChangeLog.omp b/gcc/c/ChangeLog.omp new file mode 100644 index 0000000..ff881b5 --- /dev/null +++ b/gcc/c/ChangeLog.omp @@ -0,0 +1,237 @@ +2025-05-15 waffl3x <waffl3x@baylibre.com> + + PR c++/119659 + PR c++/118859 + PR c++/119601 + PR c++/119602 + PR c++/119775 + * c-parser.cc (c_omp_numeric_ranges_always_overlap): New function. + (c_parser_omp_parm_list): New function. + (c_finish_omp_declare_variant): Use c_parser_omp_parm_list instead + of c_parser_omp_variable_list. Refactor, change format of + "omp declare variant variant args" attribute. + +2025-05-15 Kwok Cheung Yeung <kcyeung@baylibre.com> + Andrew Stubbs <ams@baylibre.com> + + * c-parser.cc (c_parser_omp_clause_map): Apply iterator to push and + pop mapper clauses. + +2025-05-15 Kwok Cheung Yeung <kcyeung@baylibre.com> + + * c-parser.cc (c_parser_omp_iterators): Use macros for accessing + iterator elements. + (c_parser_omp_clause_affinity): Likewise. + (c_parser_omp_clause_depend): Likewise. + (c_parser_omp_clause_map): Likewise. + (c_parser_omp_clause_from_to): Likewise. + * c-typeck.cc (c_omp_finish_iterators): Likewise. + +2025-05-15 Kwok Cheung Yeung <kcyeung@baylibre.com> + + * c-typeck.cc (handle_omp_array_sections): Add extra argument. Set + argument to true if array section has a stride that is not one. + (c_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> + + * c-parser.cc (c_parser_omp_clause_from_to): Parse 'iterator' modifier. + * c-typeck.cc (c_finish_omp_clauses): Finish iterators for to/from + clauses. + +2025-05-15 Kwok Cheung Yeung <kcyeung@baylibre.com> + Andrew Stubbs <ams@baylibre.com> + + * c-parser.cc (c_parser_omp_clause_map): Parse 'iterator' modifier. + * c-typeck.cc (c_finish_omp_clauses): Finish iterators. Apply + iterators to generated clauses. + +2025-05-15 Kwok Cheung Yeung <kcyeung@baylibre.com> + + * c-typeck.cc (handle_omp_array_sections): Use OMP_ITERATOR_DECL_P. + (c_finish_omp_clauses): Likewise. + +2025-05-15 Chung-Lin Tang <cltang@baylibre.com> + + * c-parser.cc (c_parser_omp_variable_list): Adjust parsing of opening + square bracket. + (c_parser_omp_clause_reduction): Adjustments for + OpenACC-specific cases. + * c-typeck.cc (c_oacc_reduction_defined_type_p): New function. + (c_oacc_reduction_code_name): Likewise. + (c_finish_omp_clauses): Handle OpenACC cases using new functions. + +2025-05-15 Sandra Loosemore <sloosemore@baylibre.com> + Julian Brown <julian@codesourcery.com> + + * c-decl.cc (current_omp_declare_variant_attribute): Define. + * c-lang.h (struct c_omp_declare_variant_attr): Declare. + (current_omp_declare_variant_attribute): Declare. + * c-parser.cc (c_parser_skip_to_pragma_omp_end_declare_variant): New. + (c_parser_translation_unit): Check for "omp begin declare variant" + with no matching "end". + (c_parser_declaration_or_fndef): Handle functions in "omp begin + declare variant" block. + (c_finish_omp_declare_variant): Merge context selectors with + surrounding "omp begin declare variant". + (JOIN_STR): Define. + (omp_start_variant_function): New. + (omp_finish_variant_function): New. + (c_parser_omp_begin): Handle "omp begin declare variant". + (c_parser_omp_end): Likewise. + +2025-05-15 waffl3x <waffl3x@baylibre.com> + Tobias Burnus <tobias@codesourcery.com> + + * c-parser.cc (c_parser_omp_allocate): Fix typo in diagnostic. + +2025-05-15 Julian Brown <julian@codesourcery.com> + Andrew Stubbs <ams@baylibre.com> + Kwok Cheung Yeung <kcyeung@baylibre.com> + Sandra Loosemore <sloosemore@baylibre.com> + + * c-parser.cc (c_parser_omp_variable_list): Support array-shaping + operator in 'declare mapper' definitions. + (c_parser_omp_clause_map): Pass C_ORT_OMP_DECLARE_MAPPER to + c_parser_omp_variable_list in mapper definitions. + (c_parser_omp_clause_from_to): Add parsing for mapper modifier. + (c_parser_omp_target_update): Instantiate mappers. + +2025-05-15 Julian Brown <julian@codesourcery.com> + + * c-parser.cc (c_parser_braced_init): Disallow array-shaping operator + in braced init. + (c_parser_conditional_expression): Disallow array-shaping operator in + conditional expression. + (c_parser_cast_expression): Add array-shaping operator support. + (c_parser_postfix_expression): Disallow array-shaping operator in + statement expressions. + (c_parser_postfix_expression_after_primary): Add OpenMP array section + stride support. + (c_parser_expr_list): Disallow array-shaping operator in expression + lists. + (c_array_type_nelts_total): New function. + (c_parser_omp_variable_list): Support array-shaping operator. + (c_parser_omp_target_update): Recognize GOMP_MAP_TO_GRID and + GOMP_MAP_FROM_GRID map kinds as well as OMP_CLAUSE_TO/OMP_CLAUSE_FROM. + * c-tree.h (c_omp_array_shaping_op_p, c_omp_has_array_shape_p): New + extern declarations. + (create_omp_arrayshape_type): Add prototype. + * c-typeck.cc (c_omp_array_shaping_op_p, c_omp_has_array_shape_p): New + globals. + (build_omp_array_section): Permit integral types, not just integer + constants, when creating array types for array sections. + (create_omp_arrayshape_type): New function. + (handle_omp_array_sections_1): Add DISCONTIGUOUS parameter. Add + strided/rectangular array section support. + (omp_array_section_low_bound): New function. + (handle_omp_array_sections): Add DISCONTIGUOUS parameter. Add + strided/rectangular array section support. + (c_finish_omp_clauses): Update calls to handle_omp_array_sections. + Handle discontiguous updates. + +2025-05-15 Julian Brown <julian@codesourcery.com> + + * c-parser.cc (c_parser_postfix_expression_after_primary): Dummy stride + support (for now). + (struct omp_dim): Add stride support. + (c_parser_omp_variable_list): Likewise. + * c-tree.h (build_omp_array_section): Update prototype. + * c-typeck.cc (mark_exp_read): Add stride support for + OMP_ARRAY_SECTION. + (build_omp_array_section): Add stride support. + (handle_omp_array_sections_1): Add minimal stride support. + +2025-05-15 Julian Brown <julian@codesourcery.com> + + * c-typeck.cc (handle_omp_array_sections): Pass pointer to clause to + process instead of clause. + (c_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> + + * c-parser.cc (c_parser_omp_target_data): Instantiate mappers for + 'omp target data'. + (c_parser_omp_target_enter_data): Instantiate mappers for 'omp target + enter data'. + (c_parser_omp_target_exit_data): Instantiate mappers for 'omp target + exit data'. + (c_parser_omp_target): Add c_omp_region_type argument to + c_omp_instantiate_mappers call. + * c-tree.h (c_omp_instantiate_mappers): Remove spurious prototype. + +2025-05-15 Julian Brown <julian@codesourcery.com> + + * c-decl.cc (c_omp_mapper_id, c_omp_mapper_decl, c_omp_mapper_lookup, + c_omp_extract_mapper_directive, c_omp_map_array_section, + c_omp_scan_mapper_bindings_r, c_omp_scan_mapper_bindings): New + functions. + * c-objc-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 for C. + * c-parser.cc (c_parser_omp_clause_map): Add KIND parameter. Handle + mapper modifier. + (c_parser_omp_all_clauses): Update call to c_parser_omp_clause_map with + new kind argument. + (c_parser_omp_target): Instantiate explicit mappers and record bindings + for implicit mappers. + (c_parser_omp_declare_mapper): Parse "declare mapper" directives. + (c_parser_omp_declare): Support "declare mapper". + * c-tree.h (c_omp_finish_mapper_clauses, c_omp_mapper_lookup, + c_omp_extract_mapper_directive, c_omp_map_array_section, + c_omp_mapper_id, c_omp_mapper_decl, c_omp_scan_mapper_bindings, + c_omp_instantiate_mappers): Add prototypes. + * c-typeck.cc (c_finish_omp_clauses): Handle GOMP_MAP_PUSH_MAPPER_NAME + and GOMP_MAP_POP_MAPPER_NAME. + (c_omp_finish_mapper_clauses): New function (langhook). + +2025-05-15 Tobias Burnus <tobias@codesourcery.com> + + * c-parser.cc (c_parser_omp_clause_uses_allocators): New. + (c_parser_omp_clause_name, c_parser_omp_all_clauses, + OMP_TARGET_CLAUSE_MASK): Handle uses_allocators. + * c-typeck.cc (c_finish_omp_clauses): Likewise. + +2025-05-15 Chung-Lin Tang <cltang@codesourcery.com> + Paul-Antoine Arras <parras@baylibre.com> + + * c-typeck.cc (c_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> + + * c-parser.cc (c_parser_omp_variable_list): New c_omp_region_type + argument. Use it to specialize handling of OMP_CLAUSE_REDUCTION for + OpenACC. + (c_parser_omp_var_list_parens): Add region-type argument to call. + (c_parser_oacc_data_clause): Likewise. + (c_parser_oacc_data_clause_deviceptr): Likewise. + (c_parser_omp_clause_reduction): Change is_omp boolean parameter to + c_omp_region_type. Update call to c_parser_omp_variable_list. + (c_parser_omp_clause_map): Update call to + c_parser_omp_variable_list. + (c_parser_omp_clause_from_to): Likewise. + (c_parser_omp_clause_init): Likewise. + (c_parser_oacc_all_clauses): Update calls to + c_parser_omp_clause_reduction. + (c_parser_omp_all_clauses): Likewise. + (c_parser_oacc_cache): Update call to c_parser_omp_variable_list. + * c-typeck.cc (c_finish_omp_clauses): Emit an error on orphan OpenACC + gang reductions. Suppress user-defined reduction error for OpenACC. + +2025-05-15 Chung-Lin Tang <cltang@codesourcery.com> + Kwok Cheung Yeung <kcy@codesourcery.com> + Paul-Antoine Arras <parras@baylibre.com> + + PR other/76739 + * c-typeck.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/c/c-decl.cc b/gcc/c/c-decl.cc index 8c420f2..00cf955 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -163,6 +163,9 @@ vec<c_omp_declare_target_attr, va_gc> *current_omp_declare_target_attribute; we are in. */ vec<c_omp_begin_assumes_data, va_gc> *current_omp_begin_assumes; +/* Vector of "omp begin/end declare variant" blocks we are in. */ +vec<c_omp_declare_variant_attr, va_gc> *current_omp_declare_variant_attribute; + /* Vector of loop names with C_DECL_LOOP_NAME or C_DECL_SWITCH_NAME marked LABEL_DECL as the last and canonical for each loop or switch. */ static vec<tree> loop_names; @@ -13857,6 +13860,175 @@ c_check_omp_declare_reduction_r (tree *tp, int *, void *data) return NULL_TREE; } +/* Return identifier to look up for omp declare reduction. */ + +tree +c_omp_mapper_id (tree mapper_id) +{ + const char *p = NULL; + + const char prefix[] = "omp declare mapper "; + + if (mapper_id == NULL_TREE) + p = "<default>"; + else if (TREE_CODE (mapper_id) == IDENTIFIER_NODE) + p = IDENTIFIER_POINTER (mapper_id); + else + return error_mark_node; + + size_t lenp = sizeof (prefix); + size_t len = strlen (p); + char *name = XALLOCAVEC (char, lenp + len); + memcpy (name, prefix, lenp - 1); + memcpy (name + lenp - 1, p, len + 1); + return get_identifier (name); +} + +/* Lookup MAPPER_ID in the current scope, or create an artificial + VAR_DECL, bind it into the current scope and return it. */ + +tree +c_omp_mapper_decl (tree mapper_id) +{ + struct c_binding *b = I_SYMBOL_BINDING (mapper_id); + if (b != NULL && B_IN_CURRENT_SCOPE (b)) + return b->decl; + + tree decl = build_decl (BUILTINS_LOCATION, VAR_DECL, + mapper_id, integer_type_node); + DECL_ARTIFICIAL (decl) = 1; + DECL_EXTERNAL (decl) = 1; + TREE_STATIC (decl) = 1; + TREE_PUBLIC (decl) = 0; + bind (mapper_id, decl, current_scope, true, false, BUILTINS_LOCATION); + return decl; +} + +/* Lookup MAPPER_ID in the first scope where it has entry for TYPE. */ + +tree +c_omp_mapper_lookup (tree mapper_id, tree type) +{ + if (TREE_CODE (type) != RECORD_TYPE + && TREE_CODE (type) != UNION_TYPE) + return NULL_TREE; + + mapper_id = c_omp_mapper_id (mapper_id); + + struct c_binding *b = I_SYMBOL_BINDING (mapper_id); + while (b) + { + tree t; + for (t = DECL_INITIAL (b->decl); t; t = TREE_CHAIN (t)) + if (comptypes (TREE_PURPOSE (t), type)) + return TREE_VALUE (t); + b = b->shadowed; + } + return NULL_TREE; +} + +/* For C, we record a pointer to the mapper itself without wrapping it in an + artificial function or similar. So, just return it. */ + +tree +c_omp_extract_mapper_directive (tree mapper) +{ + return mapper; +} + +/* For now we can handle singleton OMP_ARRAY_SECTIONs with custom mappers, but + nothing more complicated. */ + +tree +c_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; + + t = build_array_ref (loc, t, low); + } + + return t; +} + +/* Helper function for below function. */ + +static tree +c_omp_scan_mapper_bindings_r (tree *tp, int *walk_subtrees, void *ptr) +{ + tree t = *tp; + omp_mapper_list<tree> *mlist = (omp_mapper_list<tree> *) ptr; + tree aggr_type = NULL_TREE; + + if (TREE_CODE (t) == SIZEOF_EXPR + || TREE_CODE (t) == ALIGNOF_EXPR) + { + *walk_subtrees = 0; + return NULL_TREE; + } + + if (TREE_CODE (t) == OMP_CLAUSE) + return 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 = c_omp_mapper_lookup (NULL_TREE, aggr_type); + if (mapper_fn) + mlist->add_mapper (NULL_TREE, aggr_type, mapper_fn); + } + + return NULL_TREE; +} + +/* Scan an offload region's body, and record uses of struct- or union-typed + variables. Add _mapper_binding_ fake clauses to *CLAUSES_PTR. */ + +void +c_omp_scan_mapper_bindings (location_t loc, tree *clauses_ptr, tree body) +{ + hash_set<omp_name_type<tree>> seen_types; + auto_vec<tree> mappers; + omp_mapper_list<tree> mlist (&seen_types, &mappers); + + walk_tree_without_duplicates (&body, c_omp_scan_mapper_bindings_r, &mlist); + + unsigned int i; + tree mapper; + FOR_EACH_VEC_ELT (mappers, i, mapper) + c_omp_find_nested_mappers (&mlist, mapper); + + FOR_EACH_VEC_ELT (mappers, i, mapper) + { + if (mapper == error_mark_node) + continue; + tree mapper_name = OMP_DECLARE_MAPPER_ID (mapper); + tree decl = OMP_DECLARE_MAPPER_DECL (mapper); + + 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; + + OMP_CLAUSE_CHAIN (c) = *clauses_ptr; + *clauses_ptr = c; + } +} bool c_check_in_current_scope (tree decl) diff --git a/gcc/c/c-lang.h b/gcc/c/c-lang.h index 4b93d18..cd68fc0 100644 --- a/gcc/c/c-lang.h +++ b/gcc/c/c-lang.h @@ -72,6 +72,11 @@ struct GTY(()) c_omp_begin_assumes_data { bool attr_syntax; }; +struct GTY(()) c_omp_declare_variant_attr { + bool attr_syntax; + tree selector; +}; + /* If non-empty, implicit "omp declare target" attribute is added into the attribute lists. */ extern GTY(()) vec<c_omp_declare_target_attr, va_gc> @@ -80,5 +85,8 @@ extern GTY(()) vec<c_omp_declare_target_attr, va_gc> #pragma omp end assumes (and how many times when nested). */ extern GTY(()) vec<c_omp_begin_assumes_data, va_gc> *current_omp_begin_assumes; +/* And similarly for #pragma omp begin/end declare variant. */ +extern GTY(()) vec<c_omp_declare_variant_attr, va_gc> + *current_omp_declare_variant_attribute; #endif /* ! GCC_C_LANG_H */ diff --git a/gcc/c/c-objc-common.h b/gcc/c/c-objc-common.h index 84bd357..84f6fd3 100644 --- a/gcc/c/c-objc-common.h +++ b/gcc/c/c-objc-common.h @@ -137,6 +137,18 @@ static const scoped_attribute_specs *const c_objc_attribute_table[] = #undef LANG_HOOKS_OMP_CLAUSE_ASSIGN_OP #define LANG_HOOKS_OMP_CLAUSE_ASSIGN_OP c_omp_clause_copy_ctor +#undef LANG_HOOKS_OMP_FINISH_MAPPER_CLAUSES +#define LANG_HOOKS_OMP_FINISH_MAPPER_CLAUSES c_omp_finish_mapper_clauses + +#undef LANG_HOOKS_OMP_MAPPER_LOOKUP +#define LANG_HOOKS_OMP_MAPPER_LOOKUP c_omp_mapper_lookup + +#undef LANG_HOOKS_OMP_EXTRACT_MAPPER_DIRECTIVE +#define LANG_HOOKS_OMP_EXTRACT_MAPPER_DIRECTIVE c_omp_extract_mapper_directive + +#undef LANG_HOOKS_OMP_MAP_ARRAY_SECTION +#define LANG_HOOKS_OMP_MAP_ARRAY_SECTION c_omp_map_array_section + #undef LANG_HOOKS_TREE_INLINING_VAR_MOD_TYPE_P #define LANG_HOOKS_TREE_INLINING_VAR_MOD_TYPE_P c_var_mod_p #endif /* GCC_C_OBJC_COMMON */ diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 22ec0f8..9af7440 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -1456,6 +1456,55 @@ c_parser_skip_to_pragma_eol (c_parser *parser, bool error_if_not_eol = true) parser->error = false; } +/* Skip tokens up to and including "#pragma omp end declare variant". + Properly handle nested "#pragma omp begin declare variant" pragmas. */ +static void +c_parser_skip_to_pragma_omp_end_declare_variant (c_parser *parser) +{ + for (int depth = 0; depth >= 0; ) + { + c_token *token = c_parser_peek_token (parser); + + switch (token->type) + { + case CPP_PRAGMA_EOL: + if (!parser->in_pragma) + break; + /* FALLTHRU */ + case CPP_EOF: + /* If we've run out of tokens, stop. */ + return; + + case CPP_PRAGMA: + if ((token->pragma_kind == PRAGMA_OMP_BEGIN + || token->pragma_kind == PRAGMA_OMP_END) + && c_parser_peek_nth_token (parser, 2)->type == CPP_NAME + && c_parser_peek_nth_token (parser, 3)->type == CPP_NAME) + { + tree id1 = c_parser_peek_nth_token (parser, 2)->value; + tree id2 = c_parser_peek_nth_token (parser, 3)->value; + if (strcmp (IDENTIFIER_POINTER (id1), "declare") == 0 + && strcmp (IDENTIFIER_POINTER (id2), "variant") == 0) + { + if (token->pragma_kind == PRAGMA_OMP_BEGIN) + depth++; + else + depth--; + } + } + c_parser_consume_pragma (parser); + c_parser_skip_to_pragma_eol (parser, false); + continue; + + default: + break; + } + + /* Consume the token. */ + c_parser_consume_token (parser); + } +} + /* Skip tokens until we have consumed an entire block, or until we have consumed a non-nested ';'. */ @@ -1979,6 +2028,13 @@ c_parser_translation_unit (c_parser *parser) "#pragma omp end declare target"); vec_safe_truncate (current_omp_declare_target_attribute, 0); } + if (vec_safe_length (current_omp_declare_variant_attribute)) + { + if (!errorcount) + error ("%<omp begin declare variant%> without corresponding " + "%<omp end declare variant%>"); + vec_safe_truncate (current_omp_declare_variant_attribute, 0); + } if (vec_safe_length (current_omp_begin_assumes)) { if (!errorcount) @@ -2112,6 +2168,8 @@ static void c_parser_handle_directive_omp_attributes (tree &, vec<c_token> *&, vec<c_token> *); static void c_finish_omp_declare_simd (c_parser *, tree, tree, vec<c_token> *); static void c_finish_oacc_routine (struct oacc_routine_data *, tree, bool); +static tree omp_start_variant_function (c_declarator *, tree); +static void omp_finish_variant_function (tree, tree, tree); /* Build and add a DEBUG_BEGIN_STMT statement with location LOC. */ @@ -3064,6 +3122,21 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, pedwarn (here, OPT_Wpedantic, "ISO C forbids nested functions"); c_push_function_context (); } + + /* 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; + if (!vec_safe_is_empty (current_omp_declare_variant_attribute)) + { + c_omp_declare_variant_attr a + = current_omp_declare_variant_attribute->last (); + dv_ctx = copy_list (a.selector); + dv_base = omp_start_variant_function (declarator, dv_ctx); + } + if (!start_function (specs, declarator, all_prefix_attrs)) { /* At this point we've consumed: @@ -3141,6 +3214,11 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, DECL_STRUCT_FUNCTION (current_function_decl)->function_start_locus = startloc; location_t endloc = startloc; + /* 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 && current_function_decl != error_mark_node) + omp_finish_variant_function (current_function_decl, dv_base, dv_ctx); /* If the definition was marked with __RTL, use the RTL parser now, consuming the function body. */ @@ -6438,7 +6516,9 @@ c_parser_braced_init (c_parser *parser, tree type, bool nested_p, gcc_obstack_init (&braced_init_obstack); gcc_assert (c_parser_next_token_is (parser, CPP_OPEN_BRACE)); bool save_c_omp_array_section_p = c_omp_array_section_p; + bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p; c_omp_array_section_p = false; + c_omp_array_shaping_op_p = false; bool zero_init_padding_bits = false; matching_braces braces; braces.consume_open (parser); @@ -6500,6 +6580,7 @@ c_parser_braced_init (c_parser *parser, tree type, bool nested_p, } } c_omp_array_section_p = save_c_omp_array_section_p; + c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p; c_token *next_tok = c_parser_peek_token (parser); if (next_tok->type != CPP_CLOSE_BRACE) { @@ -9900,6 +9981,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after, struct c_expr cond, exp1, exp2, ret; location_t start, cond_loc, colon_loc; bool save_c_omp_array_section_p = c_omp_array_section_p; + bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p; gcc_assert (!after || c_dialect_objc ()); @@ -9908,6 +9990,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after, if (c_parser_next_token_is_not (parser, CPP_QUERY)) return cond; c_omp_array_section_p = false; + c_omp_array_shaping_op_p = false; if (cond.value != error_mark_node) start = cond.get_start (); else @@ -9961,6 +10044,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after, ret.original_code = ERROR_MARK; ret.original_type = NULL; c_omp_array_section_p = save_c_omp_array_section_p; + c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p; return ret; } { @@ -10008,6 +10092,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after, set_c_expr_source_range (&ret, start, exp2.get_finish ()); ret.m_decimal = 0; c_omp_array_section_p = save_c_omp_array_section_p; + c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p; return ret; } @@ -10389,6 +10474,8 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after) if (after) return c_parser_postfix_expression_after_primary (parser, cast_loc, *after); + bool save_c_omp_has_array_shape_p = c_omp_has_array_shape_p; + c_omp_has_array_shape_p = false; /* If the expression begins with a parenthesized type name, it may be either a cast or a compound literal; we need to see whether the next character is '{' to tell the difference. If not, it is @@ -10397,6 +10484,10 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after) if (c_parser_next_token_is (parser, CPP_OPEN_PAREN) && c_token_starts_compound_literal (c_parser_peek_2nd_token (parser))) { + bool save_c_omp_array_section_p = c_omp_array_section_p; + bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p; + c_omp_array_section_p = false; + c_omp_array_shaping_op_p = false; struct c_declspecs *scspecs; struct c_type_name *type_name; struct c_expr ret; @@ -10408,6 +10499,8 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after) parens.skip_until_found_close (parser); if (type_name == NULL) { + c_omp_array_section_p = save_c_omp_array_section_p; + c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p; ret.set_error (); ret.original_code = ERROR_MARK; ret.original_type = NULL; @@ -10418,9 +10511,15 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after) used_types_insert (type_name->specs->type); if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) - return c_parser_postfix_expression_after_paren_type (parser, scspecs, - type_name, - cast_loc); + { + c_expr r = c_parser_postfix_expression_after_paren_type (parser, + scspecs, + type_name, + cast_loc); + c_omp_array_section_p = save_c_omp_array_section_p; + c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p; + return r; + } if (scspecs) error_at (cast_loc, "storage class specifier in cast"); if (type_name->specs->alignas_p) @@ -10437,10 +10536,61 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after) ret.original_code = ERROR_MARK; ret.original_type = NULL; ret.m_decimal = 0; + c_omp_array_section_p = save_c_omp_array_section_p; + c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p; + return ret; + } + else if (c_omp_array_shaping_op_p + && c_parser_next_token_is (parser, CPP_OPEN_PAREN) + && c_parser_peek_2nd_token (parser)->type == CPP_OPEN_SQUARE) + { + bool save_c_omp_array_section_p = c_omp_array_section_p; + bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p; + c_omp_array_section_p = false; + c_omp_array_shaping_op_p = false; + auto_vec<tree, 4> omp_shape_dims; + struct c_expr expr, ret; + matching_parens parens; + parens.consume_open (parser); + while (c_parser_next_token_is (parser, CPP_OPEN_SQUARE)) + { + c_parser_consume_token (parser); + c_expr e = c_parser_expression (parser); + if (e.value == error_mark_node) + break; + omp_shape_dims.safe_push (e.value); + if (!c_parser_require (parser, CPP_CLOSE_SQUARE, + "expected %<]%>")) + break; + } + parens.require_close (parser); + c_omp_array_section_p = save_c_omp_array_section_p; + c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p; + { + location_t expr_loc = c_parser_peek_token (parser)->location; + bool save_c_omp_has_array_shape_p = c_omp_has_array_shape_p; + c_omp_has_array_shape_p = true; + expr = c_parser_cast_expression (parser, NULL); + c_omp_has_array_shape_p = save_c_omp_has_array_shape_p; + /* NOTE: We don't want to introduce conversions here. */ + expr = convert_lvalue_to_rvalue (expr_loc, expr, false, true); + } + tree arrtype + = create_omp_arrayshape_type (expr.value, &omp_shape_dims); + ret.value = build1_loc (cast_loc, VIEW_CONVERT_EXPR, arrtype, + expr.value); + if (ret.value && expr.value) + set_c_expr_source_range (&ret, cast_loc, expr.get_finish ()); + ret.original_code = ERROR_MARK; + ret.original_type = NULL; + ret.m_decimal = 0; return ret; } else - return c_parser_unary_expression (parser); + { + c_omp_has_array_shape_p = save_c_omp_has_array_shape_p; + return c_parser_unary_expression (parser); + } } /* Parse an unary expression (C90 6.3.3, C99 6.5.3, C11 6.5.3). @@ -11528,6 +11678,7 @@ c_parser_postfix_expression (c_parser *parser) tree stmt; location_t brace_loc; bool save_c_omp_array_section_p = c_omp_array_section_p; + bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p; c_parser_consume_token (parser); brace_loc = c_parser_peek_token (parser)->location; c_parser_consume_token (parser); @@ -11545,6 +11696,7 @@ c_parser_postfix_expression (c_parser *parser) break; } c_omp_array_section_p = false; + c_omp_array_shaping_op_p = false; stmt = c_begin_stmt_expr (); c_parser_compound_statement_nostart (parser); location_t close_loc = c_parser_peek_token (parser)->location; @@ -11556,6 +11708,7 @@ c_parser_postfix_expression (c_parser *parser) set_c_expr_source_range (&expr, loc, close_loc); mark_exp_read (expr.value); c_omp_array_section_p = save_c_omp_array_section_p; + c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p; } else { @@ -13628,15 +13781,38 @@ c_parser_postfix_expression_after_primary (c_parser *parser, if (c_omp_array_section_p && c_parser_next_token_is (parser, CPP_COLON)) { + tree stride = NULL_TREE; + c_parser_consume_token (parser); if (c_parser_next_token_is_not (parser, CPP_CLOSE_SQUARE)) len = c_parser_expression (parser).value; + if (c_parser_next_token_is (parser, CPP_COLON)) + { + c_parser_consume_token (parser); + if (c_parser_next_token_is_not (parser, CPP_CLOSE_SQUARE)) + stride = c_parser_expression (parser).value; + } + expr.value = build_omp_array_section (op_loc, expr.value, idx, - len); + len, stride); } else - expr.value = build_array_ref (op_loc, expr.value, idx); + { + if (c_omp_has_array_shape_p) + /* 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 + c_parser_omp_variable_list). */ + expr.value = build_omp_array_section (op_loc, expr.value, idx, + size_one_node, NULL_TREE); + else + expr.value = build_array_ref (op_loc, expr.value, idx); + } c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, "expected %<]%>"); @@ -13834,8 +14010,8 @@ c_parser_postfix_expression_after_primary (c_parser *parser, finish = c_parser_peek_token (parser)->get_finish (); c_parser_consume_token (parser); expr = default_function_array_read_conversion (expr_loc, expr); - expr.value = build_unary_op (op_loc, POSTINCREMENT_EXPR, - expr.value, false); + expr.value + = build_unary_op (op_loc, POSTINCREMENT_EXPR, expr.value, false); set_c_expr_source_range (&expr, start, finish); expr.original_code = ERROR_MARK; expr.original_type = NULL; @@ -13987,7 +14163,9 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p, struct c_expr expr; unsigned int idx = 0; bool save_c_omp_array_section_p = c_omp_array_section_p; + bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p; c_omp_array_section_p = false; + c_omp_array_shaping_op_p = false; ret = make_tree_vector (); if (p_orig_types == NULL) @@ -14069,6 +14247,7 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p, if (orig_types) *p_orig_types = orig_types; c_omp_array_section_p = save_c_omp_array_section_p; + c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p; return ret; } @@ -16244,6 +16423,8 @@ c_parser_omp_clause_name (c_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)) @@ -16319,6 +16500,24 @@ c_parser_oacc_wait_list (c_parser *parser, location_t clause_loc, tree list) return list; } +/* Return, as an INTEGER_CST node, the number of elements for TYPE + (which is an ARRAY_TYPE). This one is a recursive count of all + ARRAY_TYPEs that are clumped together. (From cp/tree.cc). */ + +static tree +c_array_type_nelts_total (tree type) +{ + tree sz = array_type_nelts_top (type); + type = TREE_TYPE (type); + while (TREE_CODE (type) == ARRAY_TYPE) + { + tree n = array_type_nelts_top (type); + sz = fold_build2_loc (input_location, MULT_EXPR, sizetype, sz, n); + type = TREE_TYPE (type); + } + return sz; +} + /* OpenACC 2.0, OpenMP 2.5: variable-list: identifier @@ -16332,22 +16531,23 @@ c_parser_oacc_wait_list (c_parser *parser, location_t clause_loc, tree list) in TREE_PURPOSE and the location in TREE_VALUE (accessible using EXPR_LOCATION); return the list created. - The optional ALLOW_DEREF argument is true if list items can use the deref + The optional MAP_LVALUE argument is true if list items can use the deref (->) operator. */ 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 c_parser_omp_variable_list (c_parser *parser, location_t clause_loc, enum omp_clause_code kind, tree list, + enum c_omp_region_type ort = C_ORT_OMP, bool map_lvalue = false) { auto_vec<omp_dim> dims; @@ -16447,12 +16647,26 @@ c_parser_omp_variable_list (c_parser *parser, { location_t loc = c_parser_peek_token (parser)->location; bool save_c_omp_array_section_p = c_omp_array_section_p; + bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p; c_omp_array_section_p = true; + c_omp_array_shaping_op_p + = (kind == OMP_CLAUSE_TO + || kind == OMP_CLAUSE_FROM + || ort == C_ORT_OMP_DECLARE_MAPPER); c_expr expr = c_parser_expr_no_commas (parser, NULL); if (expr.value != error_mark_node) mark_exp_read (expr.value); c_omp_array_section_p = save_c_omp_array_section_p; + c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p; tree decl = expr.value; + tree reshaped_to = NULL_TREE; + + if (TREE_CODE (decl) == VIEW_CONVERT_EXPR + && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE) + { + 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 @@ -16465,41 +16679,143 @@ c_parser_omp_variable_list (c_parser *parser, 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 (maybe_ptr_based_noncontig_update && sections == 0) + break; + if (TREE_CODE (decl) == COMPOUND_EXPR) { decl = TREE_OPERAND (decl, 1); STRIP_NOPS (decl); } - 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. */ + decl = build_indirect_ref (loc, decl, RO_UNARY_STAR); + decl = build1_loc (loc, VIEW_CONVERT_EXPR, reshaped_to, + decl); } } for (int i = dims.length () - 1; i >= 0; i--) decl = build_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) { @@ -16508,7 +16824,7 @@ c_parser_omp_variable_list (c_parser *parser, STRIP_NOPS (decl); decl = build_omp_array_section (loc, decl, integer_zero_node, - integer_one_node); + integer_one_node, NULL_TREE); } else if (TREE_CODE (decl) == ARRAY_REF) { @@ -16517,7 +16833,16 @@ c_parser_omp_variable_list (c_parser *parser, decl = TREE_OPERAND (decl, 0); STRIP_NOPS (decl); - decl = build_omp_array_section (loc, decl, idx, integer_one_node); + decl = build_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 = c_array_type_nelts_total (reshaped_to); + decl = build_omp_array_section (loc, decl, size_zero_node, elems, + NULL_TREE); } else if (TREE_CODE (decl) == NON_LVALUE_EXPR || CONVERT_EXPR_P (decl)) @@ -16670,7 +16995,8 @@ c_parser_omp_variable_list (c_parser *parser, break; } - 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 (t != error_mark_node) @@ -16694,7 +17020,8 @@ c_parser_omp_variable_list (c_parser *parser, for (unsigned i = 0; i < dims.length (); i++) t = build_omp_array_section (clause_loc, t, dims[i].low_bound, - dims[i].length); + dims[i].length, + dims[i].stride); } if ((kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY) @@ -16754,12 +17081,14 @@ c_parser_omp_variable_list (c_parser *parser, } /* Similarly, but expect leading and trailing parenthesis. This is a very - common case for OpenACC and OpenMP clauses. The optional ALLOW_DEREF + common case for OpenACC and OpenMP clauses. The optional MAP_LVALUE argument is true if list items can use the deref (->) operator. */ static tree c_parser_omp_var_list_parens (c_parser *parser, enum omp_clause_code kind, - tree list, bool map_lvalue = false) + tree list, + enum c_omp_region_type ort = C_ORT_OMP, + bool map_lvalue = false) { /* The clauses location. */ location_t loc = c_parser_peek_token (parser)->location; @@ -16780,12 +17109,729 @@ c_parser_omp_var_list_parens (c_parser *parser, enum omp_clause_code kind, matching_parens parens; if (parens.require_open (parser)) { - list = c_parser_omp_variable_list (parser, loc, kind, list, map_lvalue); + list = c_parser_omp_variable_list (parser, loc, kind, list, ort, + map_lvalue); parens.skip_until_found_close (parser); } return list; } +/* Helper for c_parser_omp_parm_list and c_finish_omp_declare_variant. + Compare two OpenMP parameter-list-item numeric ranges with a relative bound. + Returns true if they always overlap for any value of omp_num_args, + returns false otherwise. + + Literal bounds are never compared with each other here, + c_parser_omp_parm_list already handles that case. + + In hindsight, this was never really worth doing. If there is ever a case + found that this function gets wrong the best course of action is probably + to just disable the section that causes a problem. This function only + serves to diagnose overlapping numeric ranges in variadic functions early, + gimplify.cc:modify_call_for_omp_dispatch will always catch these problems + when the numeric range is expanded even if this function misses any cases. + + If I could go back in time, I would stop myself from writing this, but it's + already done now. It technically does serve its purpose of providing better + diagnostics for niche scenarios, so until it breaks, here it is. */ + +static bool +c_omp_numeric_ranges_always_overlap (tree first, tree second) +{ + gcc_assert (first && TREE_CODE (first) == TREE_LIST + && second && TREE_CODE (second) == TREE_LIST); + + auto bound_is_relative = [] (tree bound) -> bool + { + gcc_assert (!TREE_PURPOSE (bound) + || TREE_PURPOSE (bound) + == get_identifier ("omp relative bound")); + /* NULL_TREE means literal, the only other possible value is + get_identifier ("omp relative bound"), I hate this design though. */ + return TREE_PURPOSE (bound); + }; + + tree lb1 = TREE_PURPOSE (first); + tree ub1 = TREE_VALUE (first); + gcc_assert (lb1 && ub1); + const bool lb1_relative = bound_is_relative (lb1); + const bool ub1_relative = bound_is_relative (ub1); + const bool first_mixed = !(lb1_relative && ub1_relative); + + tree lb2 = TREE_PURPOSE (second); + tree ub2 = TREE_VALUE (second); + gcc_assert (lb2 && ub2); + const bool lb2_relative = bound_is_relative (lb2); + const bool ub2_relative = bound_is_relative (ub2); + const bool second_mixed = !(lb2_relative && ub2_relative); + + /* Both ranges must have a relative bound. */ + gcc_assert ((lb1_relative || ub1_relative) + && (lb2_relative || ub2_relative)); + + /* Both fully relative. */ + if (!first_mixed && !second_mixed) + { + /* (relative : relative), (relative : relative) */ + wi::tree_to_widest_ref lb1_v = wi::to_widest (TREE_VALUE (lb1)); + wi::tree_to_widest_ref ub1_v = wi::to_widest (TREE_VALUE (ub1)); + wi::tree_to_widest_ref lb2_v = wi::to_widest (TREE_VALUE (lb2)); + wi::tree_to_widest_ref ub2_v = wi::to_widest (TREE_VALUE (ub2)); + /* We compare lower bound to upper bound including equality because + upper bounds are stored as one past the end of the range. */ + return (lb1_v >= lb2_v && lb1_v < ub2_v) + || (ub1_v > lb2_v && ub1_v <= ub2_v); + } + else if (first_mixed && second_mixed) + { + /* Note that this is a comparison, not logical and/or. */ + if (lb1_relative == lb2_relative) + { + /* FIRST SECOND + LB1 UB1 LB2 UB2 + (literal : relative), (literal : relative) + (relative : literal), (relative : literal) */ + + /* Simply compare the relative bounds, if they match the two ranges + will always overlap. + There is some other static analysis that can be done, but it isn't + worth the time to implement. */ + gcc_assert (ub1_relative == ub2_relative); + if (lb1_relative) + { + /* (relative : literal), (relative : literal) */ + return wi::to_widest (TREE_VALUE (lb1)) + == wi::to_widest (TREE_VALUE (lb2)); + } + else + { + /* (literal : relative), (literal : relative) */ + return wi::to_widest (TREE_VALUE (ub1)) + == wi::to_widest (TREE_VALUE (ub2)); + } + } + else + { + /* FIRST SECOND + LB1 UB1 LB2 UB2 + (literal : relative), (relative : literal) + (relative : literal), (literal : relative) */ + gcc_assert (lb1_relative != lb2_relative + && ub2_relative != ub2_relative + && (lb1_relative == ub2_relative + || lb2_relative == ub1_relative)); + /* There is definitely more interesting static analysis that can + be done here but it would probably be a waste of time. */ + tree relative_lb = lb1_relative ? lb1 : lb2; + tree relative_ub = ub1_relative ? ub1 : ub2; + return wi::to_widest (TREE_VALUE (relative_lb)) + >= wi::to_widest (TREE_VALUE (relative_ub)); + } + } + else + { + /* FIRST SECOND + LB1 UB1 LB2 UB2 + (literal : relative), (relative : relative) + (relative : relative), (literal : relative) + + (relative : relative), (relative : literal) + (relative : literal), (relative : relative) */ + gcc_assert ((first_mixed && !second_mixed) + || (!first_mixed && second_mixed)); + tree lb_mixed = first_mixed ? lb1 : lb2; + tree ub_mixed = first_mixed ? ub1 : ub2; + + tree lb_full_relative = !first_mixed ? lb1 : lb2; + tree ub_full_relative = !first_mixed ? ub1 : ub2; + + if (bound_is_relative (lb_mixed)) + { + return wi::to_widest (TREE_VALUE (lb_mixed)) + >= wi::to_widest (TREE_VALUE (lb_full_relative)) + && wi::to_widest (TREE_VALUE (lb_mixed)) + < wi::to_widest (TREE_VALUE (ub_full_relative)); + } + else + { + gcc_assert (bound_is_relative (ub_mixed)); + return wi::to_widest (TREE_VALUE (ub_mixed)) + > wi::to_widest (TREE_VALUE (lb_full_relative)) + && wi::to_widest (TREE_VALUE (ub_mixed)) + <= wi::to_widest (TREE_VALUE (ub_full_relative)); + } + } + gcc_unreachable (); +} + + +/* Parse an OpenMP parameter-list. + parameter-list: + parameter-list-item[, parameter-list-item [, ...]] + + parameter-list-item: + named parameter list item + parameter index (1 based) + numeric-range + + + numeric-range: + [bound]:[bound] + + bound: + index-expr + omp_num_args[±logical_offset] + + A named parameter list item is the name of a parameter. A parameter index + is a positive integer literal that is the 1 based index of a parameter. + A numeric-range is a pair of bounds of the form lb:ub, the values of each + bound form a closed interval of parameter indices. Bounds can be literal or + relative. An index-expr is a non-negative integer constant-expression that + is the value of a literal bound. The special identifier omp_num_args is + equal to the number of arguments passed to the function at the call site, + including the number of varargs. Optionally, a plus or minus with a + logical_offset may follow omp_num_args, logical_offset is a non-negative + integer constant-expression. A bound formed with omp_num_args is a relative + bound. If a bound is omitted, a default value is used. The default value + of lb is as if 1 were specified, the default value of ub is as if + omp_num_args were specified. + + Each parameter-list-item is stored in a TREE_LIST. The PURPOSE is for + general use and left NULL_TREE here, and the item is stored in the VALUE. + An item is a TREE_LIST, the PURPOSE is an expression with the location of + the list item, and the VALUE is a representation of the item. + Each parameter-list-item is stored in a TREE_LIST node VALUE. The PURPOSE + is unused, and the VALUE is the item-repr. + + Node - PUPOSE: NULL_TREE + - VALUE: item-with-location + item-with-location - PURPOSE: expr-with-location + - VALUE: item-repr + + An item-repr is a INTEGER_CST or a TREE_LIST. An INTEGER_CST is the 0 based + index of a specified parameter, derived from a named parameter list item or + a parameter index. A TREE_LIST is a numeric-range where its PURPOSE is a + TREE_LIST representing the lb, and its VALUE is a TREE_LIST representing the + ub. + + item-repr + INTEGER_CST - parameter index (0 based) + TREE_LIST - PURPOSE: TREE_LIST (lb) + - VALUE: TREE_LIST (ub) + + lb and ub are a TREE_LIST of the following form; + TREE_LIST - PURPOSE: relative bound marker (NULL_TREE if literal) + - VALUE: expr-value + + In non-variadic functions numeric ranges are immediately expanded into + INTEGER_CST nodes corresponding to each index specified by the interval. + + The expr-value is an INTEGER_CST node of type integer_type_node, the value + corresponding to the expr. The value of lb is adjusted to be 0 based, while + the value of ub is adjusted to be 0 based, and one past the end to support + empty ranges. In other words, lb is adjusted by -1, and ub remains the + same. + + Parameters that are specified but are not defined, out of range indices and + duplicate specifications are diagnosed. Additionally, numeric ranges that + can be proven to always overlap for any value of omp_num_args even before + expansion are also diagnosed. This provides diagnostics that occur before + the function is used. In hindsight, I wish I didn't waste my time on that + last one. + If a diagnostic is issued for a list item, it is not appened to the list and + parsing continues. Returns NULL_TREE if no valid list items are parsed. + + This function strictly handles a parameter-list, it does not parse clause + modifiers, or parenthesis other than in the expr of a numeric range. */ + +static tree +c_parser_omp_parm_list (c_parser *parser, tree decl, const int parm_count) +{ + /* TODO: C++ front end was enhanced a little, gotta make changes in here + to match it. */ + tree list = NULL_TREE; + /* Even though an adjust_args clause on a non-variadic function with 0 + parameters is silly, we should still probably handle it gracefully. */ + const bool variadic_p = TYPE_ARG_TYPES (TREE_TYPE (decl)) != void_list_node + && parm_count == 0; + const int omp_num_args_value = parm_count; + + auto unique_append_to_list = [&list, &variadic_p] (int idx, location_t loc) + { + gcc_assert (idx >= 0); + /* Keep track of the last chain to append to the list. */ + tree *chain = &list; + for (tree node = list; node; node = TREE_CHAIN (node)) + { + chain = &TREE_CHAIN (node); + tree item = TREE_VALUE (node); + /* Skip numeric range nodes, only valid for variadic functions. */ + if (variadic_p && TREE_CODE (TREE_VALUE (item)) != INTEGER_CST) + /* Early exit. */; + else if (wi::to_widest (TREE_VALUE (item)) == idx) + /* Return the item for diagnostic purposes. */ + return item; + } + gcc_assert (*chain == NULL_TREE); + /* Store the location in PURPOSE for use in diagnostics. */ + tree item = build_tree_list (build_empty_stmt (loc), + build_int_cst (integer_type_node, idx)); + /* Leave PURPOSE unused for use by the caller of + c_parser_omp_parm_list. */ + *chain = build_tree_list (NULL_TREE, item); + return NULL_TREE; + }; + + auto tok_terminates_item_p = [] (c_token *tok) + { + return tok->type == CPP_COMMA + || tok->type == CPP_CLOSE_PAREN; + }; + /* The first list item is (obviously) not preceded by a comma. */ + goto first_element; + do + { + /* Consume the comma. */ + c_parser_consume_token (parser); + first_element: + c_token *const tok = c_parser_peek_token (parser); + + /* OpenMP 6.0 (162:29-34) + A parameter list item can be one of the following: + • A named parameter list item; + • The position of a parameter in a parameter specification specified + by a positive integer, where 1 represents the first parameter; or + • A parameter range specified by lb : ub where both lb and ub must + be an expression of integer OpenMP type with the constant property + and the positive property. + + The spec does not support arbitrary expression outside of a numeric + range. In theory they could be supported as a parameter index, but + for now we do not support that case. */ + + /* If we don't see a comma or close paren this can't be a named parameter + list item or a parameter index, it can only be a numeric range. + As far as I can tell, there is no well-formed code that could break + this assumption. */ + if (!tok_terminates_item_p (c_parser_peek_2nd_token (parser)) + /* Or this edge case, there is a default lower bound. */ + || tok->type == CPP_COLON) + /* Early exit, numeric range case handled below. */; + else if (tok->type == CPP_NAME + && tok->id_kind == C_ID_ID) + { + if (strcmp (IDENTIFIER_POINTER (tok->value), "omp_num_args") == 0) + { + error_at (tok->location, "%<omp_num_args%> may only be used at " + "the start of a numeric range bound"); + c_parser_consume_token (parser); + continue; + } + tree parm_decl = lookup_name (tok->value); + + if (parm_decl && TREE_CODE (parm_decl) == PARM_DECL) + { + tree parm = DECL_ARGUMENTS (decl); + /* We store indices in 0 based form internally. */ + int idx = 0; + while (parm != parm_decl) + { + gcc_assert (parm != NULL_TREE && parm != void_list_node); + ++idx; + parm = DECL_CHAIN (parm); + } + if (tree dupe = unique_append_to_list (idx, tok->location)) + { + error_at (tok->location, + "OpenMP parameter list items must specify a " + "unique parameter"); + inform (EXPR_LOCATION (TREE_PURPOSE (dupe)), + "parameter previously specified here"); + } + } + else + { + /* It feels like the only reasonable solution is to cook our own + solution for this, undeclared_variable doesn't give us what + we wan't for more than a few reasons. */ + error_at (tok->location, + "%qs is not a function parameter", + IDENTIFIER_POINTER (tok->value)); + /* FIXME: Something like this is a good idea. */ + /* if (parm_decl && TREE_CONSTANT (parm_decl)) + inform (tok->location, + "an expression is only allowed in a " + "numeric range"); */ + /* Don't use undeclared_variable if we are parsing a decl + instead of a declaration, it breaks subsequent lookups in + later functions. */ + } + c_parser_consume_token (parser); + continue; + } + else if (tok->type == CPP_NUMBER) + { + if (wi::to_widest (tok->value) <= 0) + error_at (tok->location, "parameter indices in an OpenMP " + "parameter list must be positive"); + else if (wi::to_widest (tok->value) > INT_MAX) + error_at (tok->location, "parameter index is too big"); + else + { + /* We store indices 0 based internally, OpenMP specifies + 1 based indices, modify it. */ + const int idx = tree_to_shwi (tok->value) - 1; + if (!variadic_p && idx >= parm_count) + error_at (tok->location, + "parameter list item index is out of range"); + else + { + if (tree dupe = unique_append_to_list (idx, tok->location)) + { + error_at (tok->location, + "OpenMP parameter list items must specify a " + "unique parameter"); + inform (EXPR_LOCATION (TREE_PURPOSE (dupe)), + "parameter previously specified here"); + } + } + } + c_parser_consume_token (parser); + continue; + } + else + { + gcc_checking_assert (tok_terminates_item_p + (c_parser_peek_2nd_token (parser))); + error_at (tok->location, "expected parameter or integer"); + c_parser_consume_token (parser); + continue; + } + /* We have a numeric range or something ill formed now, this can be + an arbitrary expression. */ + + /* Empty bounds are delimited differently for lower and upper bounds, + handle them before calling parse_bound. */ + auto parse_bound = [&] () + { + enum omp_num_args + { + num_args_none, + num_args_plus, + num_args_minus, + num_args_no_offset + }; + /* (OpenMP 6.0, 162:35-37) + In both lb and ub, an expression using omp_num_args, that enables + identification of parameters relative to the last argument of the + call, can be used with the form: + omp_num_args [± logical_offset] */ + + const omp_num_args parsed_omp_num_args = [&] () + { + c_token *tok = c_parser_peek_token (parser); + if (tok->type == CPP_NAME + && tok->id_kind == C_ID_ID + && strcmp (IDENTIFIER_POINTER (tok->value), "omp_num_args") + == 0) + { + /* Consume omp_num_args. */ + c_parser_consume_token (parser); + c_token *op_tok = c_parser_peek_token (parser); + if (op_tok->type == CPP_PLUS) + { + c_parser_consume_token (parser); + return num_args_plus; + } + else if (op_tok->type == CPP_MINUS) + { + c_parser_consume_token (parser); + return num_args_minus; + } + return num_args_no_offset; + } + else + return num_args_none; + } (); /* IILE. */ + + /* If there was omp_num_args but no operator an expr is not + permitted, we are finished with this bound. */ + if (parsed_omp_num_args == num_args_no_offset) + return build_int_cst (integer_type_node, omp_num_args_value); + gcc_assert (parsed_omp_num_args < num_args_no_offset); + + c_expr expr = c_parser_expr_no_commas (parser, NULL); + /* I don't know if this location is correct. */ + const location_t expr_loc = expr.get_location (); + /* I don't think read_p true is correct. */ + expr = convert_lvalue_to_rvalue (expr_loc, expr, false, true); + if (expr.value == error_mark_node) + return error_mark_node; + tree folded = c_fully_fold (expr.value, false, NULL); + if (!TREE_CONSTANT (folded)) + { + error_at (expr_loc, "expression of a bound must be a " + "constant expression"); + return error_mark_node; + } + /* This seems wrong... */ + gcc_assert (TREE_CODE (folded) == INTEGER_CST); + /* If we have omp_num_args, expr can be 0, + if we don't, expr must be positive. */ + const int sgn = tree_int_cst_sgn (folded); + /* I'm sure this is wrong but I dunno a better way right now. */ + const ptrdiff_t value = tree_to_shwi (folded); + switch (parsed_omp_num_args) + { + case num_args_none: + { + if (sgn != 1) + { + error_at (expr_loc, "expression of bound must be " + "positive"); + return error_mark_node; + } + if (!variadic_p && value > omp_num_args_value) + { + error_at (expr_loc, "expression of bound is out " + "of range"); + return error_mark_node; + } + return build_int_cst (integer_type_node, value); + } + case num_args_plus: + { + if (sgn != 0) + { + error_at (expr_loc, + "logical offset must be equal to 0 in a bound " + "of the form %<omp_num_args+logical-offset%>"); + return error_mark_node; + } + return build_int_cst (integer_type_node, omp_num_args_value); + } + case num_args_minus: + { + if (sgn == -1) + { + error_at (expr_loc, + "logical offset must be non-negative"); + return error_mark_node; + } + if (variadic_p) + return build_int_cst (integer_type_node, -value); + const ptrdiff_t parm_index = omp_num_args_value - value; + if (parm_index <= 0) + { + error_at (expr_loc, + "bound with logical offset evaluates to an " + "out of range index"); + return error_mark_node; + } + return build_int_cst (integer_type_node, parm_index); + } + case num_args_no_offset: + /* Handled above. */ + default: + gcc_unreachable (); + } + gcc_unreachable (); + }; + const location_t num_range_loc_begin = tok->location; + + /* As stated above, empty bounds are handled here. */ + tree lb = c_parser_next_token_is (parser, CPP_COLON) ? NULL_TREE + : parse_bound (); + /* I wish we could error here saying that we expect an unqualified-id, + an integer, or an expression. Parsing the expression emits the error + right away though. */ + if (lb && error_operand_p (lb)) + { + c_parser_skip_to_end_of_parameter (parser); + continue; + } + /* Tokens get consumed by parse_bound. */ + if (c_parser_next_token_is_not (parser, CPP_COLON)) + { + /* lower_bound can only be null if the next token was a colon. */ + gcc_assert (lb != NULL_TREE); + c_parser_error (parser, "expected %<:%>"); + if (tok_terminates_item_p (c_parser_peek_token (parser))) + { + const location_t loc = make_location (num_range_loc_begin, + num_range_loc_begin, + input_location); + inform (loc, "an expression is only allowed in a numeric range"); + } + c_parser_skip_to_end_of_parameter (parser); + continue; + } + const location_t colon_loc = c_parser_peek_token (parser)->location; + c_parser_consume_token (parser); + + tree ub = tok_terminates_item_p (c_parser_peek_token (parser)) + ? NULL_TREE : parse_bound (); + if (!ub || ub == error_mark_node) + c_parser_skip_to_end_of_parameter (parser); + + location_t num_range_loc_end = ub != NULL_TREE ? input_location + : colon_loc; + location_t num_range_loc = make_location (num_range_loc_begin, + num_range_loc_begin, + num_range_loc_end); + /* I think we are supposed to have some sort of diagnostic here, I'm just + not sure what it should be. */ + if (lb == error_mark_node || ub == error_mark_node) + continue; + /* Handle default bounds. */ + const ptrdiff_t lb_val = lb ? tree_to_shwi (lb) + : 1; + const ptrdiff_t ub_val = ub ? tree_to_shwi (ub) + : omp_num_args_value; + + gcc_assert (variadic_p || (lb_val > 0 && ub_val > 0)); + /* We only know this at this point if they are both negative/zero or both + positive, so basically if both or neither use omp_num_args. */ + /* FIXME: need a test for this case, I think we are missing this case + in the C++ front end, so add it. */ + if (((lb_val <= 0) == (ub_val <= 0)) && lb_val > ub_val) + { + error_at (num_range_loc, + "numeric range lower bound must be less than " + "or equal to upper bound"); + continue; + } + + auto add_range_known = [&] (const int lb, const int ub) + { + gcc_assert (lb > 0 && ub > 0 && lb <= ub); + + for (int idx = lb; idx <= ub; ++idx) + { + gcc_assert (variadic_p || idx <= parm_count); + if (tree dupe = unique_append_to_list (idx - 1, num_range_loc)) + { + error_at (num_range_loc, + "expansion of numeric range specifies " + "non-unique index %d", idx); + inform (EXPR_LOCATION (TREE_PURPOSE (dupe)), + "parameter previously specified here"); + } + } + }; + /* Store ub as exclusive (one past the end) so we can differentiate an + empty range from a range of one index without ever encoding lb as + greater than ub. + Semantically, OpenMP does not allow this as numeric range bounds are + specified to be inclusive, but we utilize it for diagnostic purposes. + This is explained in detail below. */ + auto add_range_unknown = [&] (const int lb_in, + const bool lb_relative_p, + const int ub_in, + const bool ub_relative_p) + { + /* If both bounds are relative, then lb should be <= ub. */ + gcc_assert ((!(lb_relative_p && ub_relative_p) || lb_in <= ub_in) + /* We only deal with ranges that aren't known here, so + at least one bound should be relative to num args. */ + && (lb_relative_p || ub_relative_p)); + /* Adjust to be 0 based, -1 now corresponds to the last arg. */ + const int lb = lb_in - 1; + /* Adjust to be 0 based, but add 1 to make it one past the end. */ + const int ub = ub_in - 1 + 1; + /* We don't check against the non-range indices, we already check + that by adding any indices we can be sure of WAY below. */ + auto build_bound = [] (int val, bool add_num_args) + { + return build_tree_list (add_num_args + ? get_identifier ("omp relative bound") + : NULL_TREE, + build_int_cst (integer_type_node, val)); + }; + tree lb_node = build_bound (lb, lb_relative_p); + tree ub_node = build_bound (ub, ub_relative_p); + tree new_range = build_tree_list (lb_node, ub_node); + /* Keep track of the last chain to append to the list. */ + tree *chain = &list; + for (tree node = list; node; node = TREE_CHAIN (node)) + { + chain = &TREE_CHAIN (node); + tree item = TREE_VALUE (node); + gcc_assert (TREE_PURPOSE (item)); + if (TREE_CODE (TREE_VALUE (item)) == INTEGER_CST) + continue; + + tree range = TREE_VALUE (item); + if (c_omp_numeric_ranges_always_overlap (range, new_range)) + { + error_at (num_range_loc, + "numeric range always overlaps with another " + "range"); + inform (EXPR_LOCATION (TREE_PURPOSE (item)), + "overlaps with this range"); + /* Do not add this range. */ + return; + } + } + tree item = build_tree_list (build_empty_stmt (num_range_loc), + new_range); + /* Leave PURPOSE unused for use by the caller of + c_parser_omp_parm_list. */ + *chain = build_tree_list (NULL_TREE, item); + }; + + if (lb_val > 0 && ub_val > 0) + { + gcc_assert (variadic_p + || (lb_val <= parm_count && ub_val <= parm_count)); + add_range_known (lb_val, ub_val); + } + else if (lb_val <= 0 && ub_val <= 0) + { + gcc_assert (variadic_p); + add_range_unknown (lb_val, true, ub_val, true); + } + /* Add the indices that will be specified for all well-formed calls to + the function. This lets us diagnose indices that were specified + (or rather, will be when the numeric range is expanded) multiple times + before the function is even called. We must adjust the literal bound + of the numeric range accordingly depending on how many indices we + add to prevent them from being specified again erroneously once the + range is expanded at the call site. + We can do this because we support expansion of unknown ranges + evaluating to an empty interval, as mentioned above in + add_range_unknown. */ + else if (lb_val > 0) + { + gcc_assert (variadic_p); + /* FIXME: Make sure to add a test where lb > parm_count, that + originally could break this realized that would break this + optimization. */ + /* In the case that UB refers to the last argument, we can assume all + non-variadic arguments between LB and the last non-variadic arg, + if any, will always be specified. */ + const int known_upper_bound = ub_val == 0 && lb_val <= parm_count + ? parm_count : lb_val; + add_range_known (lb_val, known_upper_bound); + add_range_unknown (known_upper_bound + 1, false, ub_val, true); + } + else if (ub_val > 0) + { + gcc_assert (variadic_p); + /* We can do this because numeric ranges are inclusive, any + well-formed call to this function will cause the range to evaluate + to include the literal index. */ + add_range_known (ub_val, ub_val); + add_range_unknown (lb_val, true, ub_val - 1, false); + } + else + gcc_unreachable (); + + } while (c_parser_next_token_is (parser, CPP_COMMA)); + return list; +} + + /* OpenACC 2.0: copy ( variable-list ) copyin ( variable-list ) @@ -16875,7 +17921,7 @@ c_parser_oacc_data_clause (c_parser *parser, pragma_omp_clause c_kind, } } nl = c_parser_omp_variable_list (parser, open_loc, OMP_CLAUSE_MAP, list, - false); + C_ORT_ACC, false); parens.skip_until_found_close (parser); } @@ -16900,7 +17946,8 @@ c_parser_oacc_data_clause_deviceptr (c_parser *parser, tree list) /* Can't use OMP_CLAUSE_MAP here (that is, can't use the generic c_parser_oacc_data_clause), as for PRAGMA_OACC_CLAUSE_DEVICEPTR, variable-list must only allow for pointer variables. */ - vars = c_parser_omp_var_list_parens (parser, OMP_CLAUSE_ERROR, NULL); + vars = c_parser_omp_var_list_parens (parser, OMP_CLAUSE_ERROR, NULL, + C_ORT_ACC); for (t = vars; t && t; t = TREE_CHAIN (t)) { tree v = TREE_PURPOSE (t); @@ -18459,7 +19506,7 @@ c_parser_omp_clause_private (c_parser *parser, tree list) static tree c_parser_omp_clause_reduction (c_parser *parser, enum omp_clause_code kind, - bool is_omp, tree list) + enum c_omp_region_type ort, tree list) { location_t clause_loc = c_parser_peek_token (parser)->location; matching_parens parens; @@ -18470,7 +19517,7 @@ c_parser_omp_clause_reduction (c_parser *parser, enum omp_clause_code kind, enum tree_code code = ERROR_MARK; tree reduc_id = NULL_TREE; - if (kind == OMP_CLAUSE_REDUCTION && is_omp) + if (kind == OMP_CLAUSE_REDUCTION && ort == C_ORT_OMP) { if (c_parser_next_token_is_keyword (parser, RID_DEFAULT) && c_parser_peek_2nd_token (parser)->type == CPP_COMMA) @@ -18535,13 +19582,21 @@ c_parser_omp_clause_reduction (c_parser *parser, enum omp_clause_code kind, code = MAX_EXPR; break; } + if (ort == C_ORT_ACC) + goto name_error; reduc_id = c_parser_peek_token (parser)->value; break; } default: - c_parser_error (parser, - "expected %<+%>, %<*%>, %<-%>, %<&%>, " - "%<^%>, %<|%>, %<&&%>, %<||%> or identifier"); + name_error: + if (ort == C_ORT_OMP) + c_parser_error (parser, + "expected %<+%>, %<*%>, %<-%>, %<&%>, " + "%<^%>, %<|%>, %<&&%>, %<||%> or identifier"); + else + c_parser_error (parser, + "expected %<+%>, %<*%>, %<-%>, %<&%>, " + "%<^%>, %<|%>, %<&&%>, %<||%>, %<min%> or %<max%>"); c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, 0); return list; } @@ -18551,9 +19606,15 @@ c_parser_omp_clause_reduction (c_parser *parser, enum omp_clause_code kind, { tree nl, c; - nl = c_parser_omp_variable_list (parser, clause_loc, kind, list); + nl = c_parser_omp_variable_list (parser, clause_loc, kind, list, ort); + for (c = nl; 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; + tree d = OMP_CLAUSE_DECL (c), type; if (TREE_CODE (d) != OMP_ARRAY_SECTION) type = TREE_TYPE (d); @@ -18577,7 +19638,6 @@ c_parser_omp_clause_reduction (c_parser *parser, enum omp_clause_code kind, } while (TREE_CODE (type) == ARRAY_TYPE) type = TREE_TYPE (type); - OMP_CLAUSE_REDUCTION_CODE (c) = code; if (task) OMP_CLAUSE_REDUCTION_TASK (c) = 1; else if (inscan) @@ -19212,6 +20272,213 @@ c_parser_omp_clause_allocate (c_parser *parser, tree list) return nl; } +/* 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 +c_parser_omp_clause_uses_allocators (c_parser *parser, tree list) +{ + location_t clause_loc = c_parser_peek_token (parser)->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 (c_parser_next_token_is (parser, CPP_NAME)) + { + c_token *tok = c_parser_peek_token (parser); + it.name.id = tok->value; + it.name.loc = tok->location; + c_parser_consume_token (parser); + + if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) + { + matching_parens parens2; + parens2.consume_open (parser); + + if (c_parser_next_token_is (parser, CPP_NAME)) + { + tok = c_parser_peek_token (parser); + it.arg.id = tok->value; + it.arg.loc = tok->location; + c_parser_consume_token (parser); + } + else + { + c_parser_error (parser, "expected identifier"); + parens2.skip_until_found_close (parser); + goto end; + } + parens2.skip_until_found_close (parser); + } + } + + cur_list->safe_push (it); + + if (c_parser_next_token_is (parser, CPP_COMMA)) + c_parser_consume_token (parser); + else if (c_parser_next_token_is (parser, CPP_COLON)) + { + if (modifiers) + { + c_parser_error (parser, "expected %<)%>"); + goto end; + } + else + { + c_parser_consume_token (parser); + modifiers = cur_list; + cur_list = new auto_vec<item> (4); + } + } + else if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) + { + gcc_assert (allocators == NULL); + allocators = cur_list; + cur_list = NULL; + break; + } + else + { + c_parser_error (parser, "expected %<)%>"); + 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 = lookup_name (it.arg.id); + if (t == NULL_TREE) + { + undeclared_variable (it.arg.loc, it.arg.id); + t = error_mark_node; + } + 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 = lookup_name (it.name.id); + if (t == NULL_TREE) + { + undeclared_variable (it.name.loc, it.name.id); + goto end; + } + else if (t != error_mark_node) + { + tree t2 = NULL_TREE; + if (it.arg.id) + { + t2 = lookup_name (it.arg.id); + if (t2 == NULL_TREE) + { + undeclared_variable (it.arg.loc, it.arg.id); + 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; + parens.skip_until_found_close (parser); + return nl; +} + /* OpenMP 4.0: linear ( variable-list ) linear ( variable-list : expression ) @@ -19651,10 +20918,10 @@ c_parser_omp_iterators (c_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 (c_parser_next_token_is (parser, CPP_COMMA)) @@ -19719,7 +20986,7 @@ c_parser_omp_clause_affinity (c_parser *parser, tree list) tree block = pop_scope (); if (iterators != error_mark_node) { - TREE_VEC_ELT (iterators, 5) = block; + OMP_ITERATORS_BLOCK (iterators) = block; for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c)) OMP_CLAUSE_DECL (c) = build_tree_list (iterators, OMP_CLAUSE_DECL (c)); @@ -19836,7 +21103,7 @@ c_parser_omp_clause_depend (c_parser *parser, tree list) if (iterators == error_mark_node) iterators = NULL_TREE; else - TREE_VEC_ELT (iterators, 5) = block; + OMP_ITERATORS_BLOCK (iterators) = block; } for (c = nl; c != list; c = OMP_CLAUSE_CHAIN (c)) @@ -19936,13 +21203,12 @@ c_parser_omp_clause_doacross (c_parser *parser, tree list) map ( [map-type-modifier[,] ...] map-kind: variable-list ) map-type-modifier: - always | close */ + always | close | present | iterator (iterators-definition) */ static tree -c_parser_omp_clause_map (c_parser *parser, tree list) +c_parser_omp_clause_map (c_parser *parser, tree list, enum gomp_map_kind kind) { location_t clause_loc = c_parser_peek_token (parser)->location; - enum gomp_map_kind kind = GOMP_MAP_TOFROM; tree nl, c; matching_parens parens; @@ -19951,22 +21217,57 @@ c_parser_omp_clause_map (c_parser *parser, tree list) int pos = 1; int map_kind_pos = 0; - while (c_parser_peek_nth_token_raw (parser, pos)->type == CPP_NAME) + int iterator_length = 0; + for (;;) { - if (c_parser_peek_nth_token_raw (parser, pos + 1)->type == CPP_COLON) + c_token *tok = c_parser_peek_nth_token_raw (parser, pos); + if (tok->type != CPP_NAME) + break; + + const char *p = IDENTIFIER_POINTER (tok->value); + c_token *next_tok = c_parser_peek_nth_token_raw (parser, pos + 1); + if (strcmp (p, "iterator") == 0 && next_tok->type == CPP_OPEN_PAREN) + { + unsigned n = pos + 2; + if (c_parser_check_balanced_raw_token_sequence (parser, &n) + && c_parser_peek_nth_token_raw (parser, n)->type + == CPP_CLOSE_PAREN) + { + iterator_length = n - pos + 1; + pos = n; + next_tok = c_parser_peek_nth_token_raw (parser, pos + 1); + } + } + + if (next_tok->type == CPP_COLON) { map_kind_pos = pos; break; } - if (c_parser_peek_nth_token_raw (parser, pos + 1)->type == CPP_COMMA) + if (next_tok->type == CPP_COMMA) pos++; + else if (c_parser_peek_nth_token_raw (parser, pos + 1)->type + == CPP_OPEN_PAREN) + { + unsigned int npos = pos + 2; + if (c_parser_check_balanced_raw_token_sequence (parser, &npos) + && (c_parser_peek_nth_token_raw (parser, npos)->type + == CPP_CLOSE_PAREN) + && (c_parser_peek_nth_token_raw (parser, npos + 1)->type + == CPP_COMMA)) + pos = npos + 1; + } + pos++; } int always_modifier = 0; int close_modifier = 0; int present_modifier = 0; + int mapper_modifier = 0; + tree mapper_name = NULL_TREE; + tree iterators = NULL_TREE; for (int pos = 1; pos < map_kind_pos; ++pos) { c_token *tok = c_parser_peek_token (parser); @@ -19987,6 +21288,7 @@ c_parser_omp_clause_map (c_parser *parser, tree list) return list; } always_modifier++; + c_parser_consume_token (parser); } else if (strcmp ("close", p) == 0) { @@ -19997,6 +21299,60 @@ c_parser_omp_clause_map (c_parser *parser, tree list) return list; } close_modifier++; + c_parser_consume_token (parser); + } + else if (strcmp ("mapper", p) == 0) + { + c_parser_consume_token (parser); + + matching_parens mparens; + if (mparens.require_open (parser)) + { + if (mapper_modifier) + { + c_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. */ + c_parser_consume_token (parser); + mparens.require_close (parser); + parens.skip_until_found_close (parser); + return list; + } + + tok = c_parser_peek_token (parser); + + switch (tok->type) + { + case CPP_NAME: + { + mapper_name = tok->value; + c_parser_consume_token (parser); + } + break; + + case CPP_KEYWORD: + if (tok->keyword == RID_DEFAULT) + { + c_parser_consume_token (parser); + break; + } + /* Fallthrough. */ + + default: + error_at (tok->location, + "expected identifier or %<default%>"); + return list; + } + + if (!mparens.require_close (parser)) + { + parens.skip_until_found_close (parser); + return list; + } + + mapper_modifier++; + pos += 3; + } } else if (strcmp ("present", p) == 0) { @@ -20007,16 +21363,29 @@ c_parser_omp_clause_map (c_parser *parser, tree list) return list; } present_modifier++; + c_parser_consume_token (parser); + } + else if (strcmp ("iterator", p) == 0 + && c_parser_peek_2nd_token (parser)->type == CPP_OPEN_PAREN) + { + if (iterators) + { + c_parser_error (parser, "too many %<iterator%> modifiers"); + parens.skip_until_found_close (parser); + return list; + } + iterators = c_parser_omp_iterators (parser); + pos += iterator_length - 1; + continue; } else { c_parser_error (parser, "%<map%> clause with map-type modifier other " - "than %<always%>, %<close%> or %<present%>"); + "than %<always%>, %<close%>, %<iterator%>, " + "%<mapper%> or %<present%>"); parens.skip_until_found_close (parser); return list; } - - c_parser_consume_token (parser); } if (c_parser_next_token_is (parser, CPP_NAME) @@ -20058,10 +21427,48 @@ c_parser_omp_clause_map (c_parser *parser, tree list) } nl = c_parser_omp_variable_list (parser, clause_loc, OMP_CLAUSE_MAP, list, - true); + (kind == GOMP_MAP_UNSET + ? C_ORT_OMP_DECLARE_MAPPER + : C_ORT_OMP), true); + + tree last_new = NULL_TREE; + + if (iterators) + { + tree block = pop_scope (); + if (iterators == error_mark_node) + iterators = NULL_TREE; + else + OMP_ITERATORS_BLOCK (iterators) = block; + } for (c = nl; 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) = 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; + } parens.skip_until_found_close (parser); return nl; @@ -20301,8 +21708,11 @@ c_parser_omp_clause_device_type (c_parser *parser, tree list) 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 c_parser_omp_clause_from_to (c_parser *parser, enum omp_clause_code kind, @@ -20313,25 +21723,179 @@ c_parser_omp_clause_from_to (c_parser *parser, enum omp_clause_code kind, if (!parens.require_open (parser)) return list; - bool present = false; - c_token *token = c_parser_peek_token (parser); + int pos = 1, colon_pos = 0; + int iterator_length = 0; - if (token->type == CPP_NAME - && strcmp (IDENTIFIER_POINTER (token->value), "present") == 0 - && c_parser_peek_2nd_token (parser)->type == CPP_COLON) + while (c_parser_peek_nth_token_raw (parser, pos)->type == CPP_NAME) { - present = true; - c_parser_consume_token (parser); - c_parser_consume_token (parser); + const char *identifier = + IDENTIFIER_POINTER (c_parser_peek_nth_token_raw (parser, pos)->value); + if (c_parser_peek_nth_token_raw (parser, pos + 1)->type + == CPP_OPEN_PAREN) + { + unsigned int npos = pos + 2; + if (c_parser_check_balanced_raw_token_sequence (parser, &npos) + && (c_parser_peek_nth_token_raw (parser, npos)->type + == CPP_CLOSE_PAREN)) + { + if (strcmp (identifier, "iterator") == 0) + iterator_length = npos - pos + 1; + pos = npos; + } + } + if (c_parser_peek_nth_token_raw (parser, pos + 1)->type == CPP_COMMA) + pos += 2; + else + pos++; + if (c_parser_peek_nth_token_raw (parser, pos)->type == CPP_COLON) + { + colon_pos = pos; + break; + } + } + + int present_modifier = false; + int mapper_modifier = false; + tree mapper_name = NULL_TREE; + tree iterators = NULL_TREE; + + for (int pos = 1; pos < colon_pos; ++pos) + { + c_token *tok = c_parser_peek_token (parser); + if (tok->type == CPP_COMMA) + { + c_parser_consume_token (parser); + continue; + } + const char *p = IDENTIFIER_POINTER (tok->value); + if (strcmp ("present", p) == 0) + { + if (present_modifier) + { + c_parser_error (parser, "too many %<present%> modifiers"); + parens.skip_until_found_close (parser); + return list; + } + present_modifier++; + c_parser_consume_token (parser); + } + else if (strcmp ("iterator", p) == 0) + { + if (iterators) + { + c_parser_error (parser, "too many %<iterator%> modifiers"); + parens.skip_until_found_close (parser); + return list; + } + iterators = c_parser_omp_iterators (parser); + pos += iterator_length - 1; + } + else if (strcmp ("mapper", p) == 0) + { + c_parser_consume_token (parser); + + matching_parens mparens; + if (mparens.require_open (parser)) + { + if (mapper_modifier) + { + c_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. */ + c_parser_consume_token (parser); + mparens.require_close (parser); + parens.skip_until_found_close (parser); + return list; + } + + tok = c_parser_peek_token (parser); + + switch (tok->type) + { + case CPP_NAME: + { + mapper_name = tok->value; + c_parser_consume_token (parser); + } + break; + + case CPP_KEYWORD: + if (tok->keyword == RID_DEFAULT) + { + c_parser_consume_token (parser); + break; + } + /* Fallthrough. */ + + default: + error_at (tok->location, + "expected identifier or %<default%>"); + return list; + } + + if (!mparens.require_close (parser)) + { + parens.skip_until_found_close (parser); + return list; + } + + mapper_modifier++; + pos += 3; + } + } + else + { + c_parser_error (parser, "%<to%> or %<from%> clause with modifier " + "other than %<iterator%>, %<mapper%> or %<present%>"); + parens.skip_until_found_close (parser); + return list; + } } - tree nl = c_parser_omp_variable_list (parser, loc, kind, list); + if (colon_pos) + c_parser_require (parser, CPP_COLON, "expected %<:%>"); + + tree nl = c_parser_omp_variable_list (parser, loc, kind, list, C_ORT_OMP, true); parens.skip_until_found_close (parser); - if (present) + 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; + OMP_CLAUSE_CHAIN (name) = OMP_CLAUSE_CHAIN (last_new); + OMP_CLAUSE_CHAIN (last_new) = name; + } + + if (iterators) + { + tree block = pop_scope (); + 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; } @@ -20876,8 +22440,7 @@ c_parser_omp_clause_init (c_parser *parser, tree list) error_at (loc, "missing required %<target%> and/or %<targetsync%> modifier"); - tree nl = c_parser_omp_variable_list (parser, loc, OMP_CLAUSE_INIT, list, - false); + tree nl = c_parser_omp_variable_list (parser, loc, OMP_CLAUSE_INIT, list); parens.skip_until_found_close (parser); for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c)) @@ -21069,7 +22632,7 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask, case PRAGMA_OACC_CLAUSE_REDUCTION: clauses = c_parser_omp_clause_reduction (parser, OMP_CLAUSE_REDUCTION, - false, clauses); + C_ORT_ACC, clauses); c_name = "reduction"; break; case PRAGMA_OACC_CLAUSE_SELF: @@ -21235,7 +22798,7 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask, case PRAGMA_OMP_CLAUSE_IN_REDUCTION: clauses = c_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: @@ -21281,7 +22844,7 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask, case PRAGMA_OMP_CLAUSE_REDUCTION: clauses = c_parser_omp_clause_reduction (parser, OMP_CLAUSE_REDUCTION, - true, clauses); + C_ORT_OMP, clauses); c_name = "reduction"; break; case PRAGMA_OMP_CLAUSE_SCHEDULE: @@ -21295,7 +22858,7 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask, case PRAGMA_OMP_CLAUSE_TASK_REDUCTION: clauses = c_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: @@ -21397,6 +22960,10 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask, clauses = c_parser_omp_clause_allocate (parser, clauses); c_name = "allocate"; break; + case PRAGMA_OMP_CLAUSE_USES_ALLOCATORS: + clauses = c_parser_omp_clause_uses_allocators (parser, clauses); + c_name = "uses_allocators"; + break; case PRAGMA_OMP_CLAUSE_LINEAR: clauses = c_parser_omp_clause_linear (parser, clauses); c_name = "linear"; @@ -21430,7 +22997,7 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask, c_name = "interop"; break; case PRAGMA_OMP_CLAUSE_MAP: - clauses = c_parser_omp_clause_map (parser, clauses); + clauses = c_parser_omp_clause_map (parser, clauses, GOMP_MAP_TOFROM); c_name = "map"; break; case PRAGMA_OMP_CLAUSE_USE_DEVICE_PTR: @@ -21588,7 +23155,7 @@ c_parser_oacc_cache (location_t loc, c_parser *parser) readonly = true; } clauses = c_parser_omp_variable_list (parser, open_loc, - OMP_CLAUSE__CACHE_, NULL_TREE); + OMP_CLAUSE__CACHE_, NULL_TREE, C_ORT_ACC); parens.skip_until_found_close (parser); } @@ -22457,7 +24024,7 @@ c_parser_omp_allocate (c_parser *parser) != get_identifier ("omp_allocator_handle_t")) { error_at (expr_loc, - "%<allocator%> clause allocator expression has type " + "%<allocator%> clause expression has type " "%qT rather than %<omp_allocator_handle_t%>", TREE_TYPE (allocator)); allocator = NULL_TREE; @@ -25890,7 +27457,9 @@ c_parser_omp_target_data (location_t loc, c_parser *parser, bool *if_p) tree clauses = c_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK, - "#pragma omp target data"); + "#pragma omp target data", false); + clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP); + clauses = c_finish_omp_clauses (clauses, C_ORT_OMP); c_omp_adjust_map_clauses (clauses, false); int map_seen = 0; for (tree *pc = &clauses; *pc;) @@ -25981,9 +27550,41 @@ c_parser_omp_target_update (location_t loc, c_parser *parser, tree clauses = c_parser_omp_all_clauses (parser, OMP_TARGET_UPDATE_CLAUSE_MASK, - "#pragma omp target update"); - if (omp_find_clause (clauses, OMP_CLAUSE_TO) == NULL_TREE - && omp_find_clause (clauses, OMP_CLAUSE_FROM) == NULL_TREE) + "#pragma omp target update", false); + clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP_UPDATE); + clauses = c_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 (loc, "%<#pragma omp target update%> must contain at least one " @@ -26048,7 +27649,9 @@ c_parser_omp_target_enter_data (location_t loc, c_parser *parser, tree clauses = c_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK, - "#pragma omp target enter data"); + "#pragma omp target enter data", false); + clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP); + clauses = c_finish_omp_clauses (clauses, C_ORT_OMP); c_omp_adjust_map_clauses (clauses, false); int map_seen = 0; for (tree *pc = &clauses; *pc;) @@ -26159,6 +27762,7 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser, tree clauses = c_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK, "#pragma omp target exit data", false); + clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP_EXIT_DATA); clauses = c_finish_omp_clauses (clauses, C_ORT_OMP_EXIT_DATA); c_omp_adjust_map_clauses (clauses, false); int map_seen = 0; @@ -26242,14 +27846,15 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser, | (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 c_parser_omp_target (c_parser *parser, enum pragma_context context, bool *if_p) { location_t loc = c_parser_peek_token (parser)->location; c_parser_consume_pragma (parser); - tree *pc = NULL, stmt, block; + tree *pc = NULL, stmt, block, body, clauses; if (context != pragma_stmt && context != pragma_compound) { @@ -26404,10 +28009,9 @@ c_parser_omp_target (c_parser *parser, enum pragma_context context, bool *if_p) stmt = make_node (OMP_TARGET); TREE_TYPE (stmt) = void_type_node; - OMP_TARGET_CLAUSES (stmt) - = c_parser_omp_all_clauses (parser, OMP_TARGET_CLAUSE_MASK, - "#pragma omp target", false); - for (tree c = OMP_TARGET_CLAUSES (stmt); c; c = OMP_CLAUSE_CHAIN (c)) + clauses = c_parser_omp_all_clauses (parser, OMP_TARGET_CLAUSE_MASK, + "#pragma omp target", false); + for (tree c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION) { tree nc = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP); @@ -26416,14 +28020,19 @@ c_parser_omp_target (c_parser *parser, enum pragma_context context, bool *if_p) OMP_CLAUSE_CHAIN (nc) = OMP_CLAUSE_CHAIN (c); OMP_CLAUSE_CHAIN (c) = nc; } - OMP_TARGET_CLAUSES (stmt) - = c_finish_omp_clauses (OMP_TARGET_CLAUSES (stmt), C_ORT_OMP_TARGET); - c_omp_adjust_map_clauses (OMP_TARGET_CLAUSES (stmt), true); + clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP_TARGET); + clauses = c_finish_omp_clauses (clauses, C_ORT_OMP_TARGET); + c_omp_adjust_map_clauses (clauses, true); - pc = &OMP_TARGET_CLAUSES (stmt); keep_next_level (); block = c_begin_compound_stmt (true); - add_stmt (c_parser_omp_structured_block (parser, if_p)); + body = c_parser_omp_structured_block (parser, if_p); + + c_omp_scan_mapper_bindings (loc, &clauses, body); + + add_stmt (body); + OMP_TARGET_CLAUSES (stmt) = clauses; + pc = &OMP_TARGET_CLAUSES (stmt); OMP_TARGET_BODY (stmt) = c_end_compound_stmt (loc, block, true); SET_EXPR_LOCATION (stmt, loc); @@ -26938,16 +28547,56 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) parens.require_close (parser); + const int parm_count = [&] () + { + tree parm = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); + int parm_count = 0; + while (parm != NULL_TREE && parm != void_list_node) + { + ++parm_count; + parm = TREE_CHAIN (parm); + } + gcc_assert (!parm || parm == void_list_node); + return parm == void_list_node ? parm_count : 0; + } (); /* IILE. */ + /* Do we care about non-variadic functions with 0 parameters? I don't think + we do, but lets handle for that case anyway, at least as long as we aren't + diagnosing for it. */ + const bool variadic_p = TYPE_ARG_TYPES (TREE_TYPE (fndecl)) == void_list_node + ? false : parm_count == 0; + tree append_args_tree = NULL_TREE; tree append_args_last; - vec<tree> adjust_args_list = vNULL; + hash_map<int_hash<int, -1, -2>, tree> adjust_args_idxs; bool has_match = false, has_adjust_args = false; location_t adjust_args_loc = UNKNOWN_LOCATION; location_t append_args_loc = UNKNOWN_LOCATION; location_t match_loc = UNKNOWN_LOCATION; - tree need_device_ptr_list = NULL_TREE; tree ctx = error_mark_node; + tree adjust_args_list = NULL_TREE; + auto append_adjust_args = [chain = &adjust_args_list] (tree node) mutable + { + gcc_assert (chain && *chain == NULL_TREE); + *chain = node; + chain = &TREE_CHAIN (node); + }; + + auto compare_ranges = [&] (tree item) + { + for (tree n2 = adjust_args_list; n2; n2 = TREE_CHAIN (n2)) + { + tree item2 = TREE_VALUE (n2); + if (TREE_CODE (TREE_VALUE (item2)) == INTEGER_CST) + continue; + else if (c_omp_numeric_ranges_always_overlap (TREE_VALUE (item2), + TREE_VALUE (item))) + /* Return the location. */ + return TREE_PURPOSE (item2); + } + return NULL_TREE; + }; + do { if (c_parser_next_token_is (parser, CPP_COMMA) @@ -26992,7 +28641,8 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) if (!parens.require_open (parser)) goto fail; - + /* This almost certainly causes problems with technically correct, but + insane functions that are variadic with no params. */ if (parms == NULL_TREE) parms = error_mark_node; @@ -27006,6 +28656,20 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) 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 (current_omp_declare_variant_attribute)) + { + c_omp_declare_variant_attr a + = current_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) { if (TREE_CODE (variant) != FUNCTION_DECL) @@ -27037,62 +28701,119 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) if (c_parser_next_token_is (parser, CPP_NAME) && c_parser_peek_2nd_token (parser)->type == CPP_COLON) { - const char *p - = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); + tree modifier_id = c_parser_peek_token (parser)->value; + const char *p = IDENTIFIER_POINTER (modifier_id); if (strcmp (p, "need_device_ptr") == 0 || strcmp (p, "nothing") == 0) { - c_parser_consume_token (parser); // need_device_ptr + c_parser_consume_token (parser); // need_device_ptr / nothing c_parser_consume_token (parser); // : loc = c_parser_peek_token (parser)->location; - tree list - = c_parser_omp_variable_list (parser, loc, OMP_CLAUSE_ERROR, - NULL_TREE); + const tree parm_list + = c_parser_omp_parm_list (parser, fndecl, parm_count); - tree arg; if (variant != error_mark_node) - for (tree c = list; c != NULL_TREE; c = TREE_CHAIN (c)) + for (tree next, n = parm_list; n != NULL_TREE; n = next) { - tree decl = TREE_PURPOSE (c); - location_t arg_loc = EXPR_LOCATION (TREE_VALUE (c)); - int idx; - for (arg = parms, idx = 0; arg != NULL; - arg = TREE_CHAIN (arg), idx++) - if (arg == decl) - break; - if (arg == NULL_TREE) + next = TREE_CHAIN (n); + TREE_CHAIN (n) = NULL_TREE; + TREE_PURPOSE (n) = modifier_id; + + tree item = TREE_VALUE (n); + const location_t item_loc + = EXPR_LOCATION (TREE_PURPOSE (item)); + if (TREE_CODE (TREE_VALUE (item)) == TREE_LIST) { - error_at (arg_loc, - "%qD is not a function argument", - decl); - goto fail; + /* Ranges are expanded by c_parser_omp_parm_list + in non-variadic functions. */ + gcc_assert (variadic_p); + if (tree dupe = compare_ranges (item)) + { + const location_t dupe_item_loc + = EXPR_LOCATION (dupe); + + error_at (item_loc, + "numeric range always overlaps with " + "previously specified numeric " + "range"); + inform (dupe_item_loc, + "previously specified here"); + } + else + append_adjust_args (n); + continue; } - if (adjust_args_list.contains (arg)) + gcc_assert (TREE_CODE (TREE_VALUE (item)) + == INTEGER_CST); + const int idx = tree_to_shwi (TREE_VALUE (item)); + /* Indices are 0 based, c_parser_omp_parm_list is + supposed to handle out of range indices. */ + gcc_assert (idx >= 0 + && (variadic_p || idx < parm_count)); + + if (tree *dupe = adjust_args_idxs.get (idx)) { - error_at (arg_loc, - "%qD is specified more than once", - decl); + const location_t prev_item_loc + = EXPR_LOCATION (TREE_PURPOSE (*dupe)); + /* Ensure the wording matches that in + c_parser_omp_parm_list. */ + error_at (item_loc, + "parameter list item specified more " + "than once"); + inform (prev_item_loc, + "previously specified here"); + /* FIXME: Don't fail, keep going. */ goto fail; } - if (strcmp (p, "need_device_ptr") == 0 - && TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE) + /* Unconditionally push idx so we don't emit the + following errors multiple times. */ + if (adjust_args_idxs.put (idx, item)) + gcc_unreachable (); + + if (strcmp (p, "need_device_ptr") == 0) { - error_at (loc, "%qD is not of pointer type", decl); - goto fail; + const tree parm = [&] () + { + if (idx >= parm_count) + return NULL_TREE; + int curr_idx = 0; + tree parm = parms; + while (parm != NULL_TREE) + { + if (curr_idx == idx) + return parm; + ++curr_idx; + parm = TREE_CHAIN (parm); + } + /* We already confirmed a parm exists in + c_parser_omp_parm_list. */ + gcc_unreachable (); + } (); /* IILE. */ + /* If we don't have an argument (because the index + is to a variadic arg) we can't check this. */ + if (parm + && TREE_CODE (TREE_TYPE (parm)) + != POINTER_TYPE) + { + error_at (DECL_SOURCE_LOCATION (parm), + "%qD is not of pointer type", parm); + inform (item_loc, "specified here"); + /* FIXME: Don't fail, keep going. */ + goto fail; + } + append_adjust_args (n); } - adjust_args_list.safe_push (arg); - if (strcmp (p, "need_device_ptr") == 0) + else if (strcmp (p, "nothing") == 0) { - need_device_ptr_list = chainon ( - need_device_ptr_list, - build_tree_list ( - NULL_TREE, - build_int_cst ( - integer_type_node, - idx))); // Store 0-based argument index, - // as in gimplify_call_expr + /* We only need to save parameter list items from a + clause with the nothing modifier if the function + is variadic. */ + if (variadic_p) + append_adjust_args (n); } + else + gcc_unreachable (); } } else @@ -27321,11 +29042,13 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) } if ((ctx != error_mark_node && variant != error_mark_node) - && (need_device_ptr_list || append_args_tree)) + && (adjust_args_list || append_args_tree)) { tree variant_decl = tree_strip_nop_conversions (variant); - tree t = build_tree_list (need_device_ptr_list, - NULL_TREE /* need_device_addr */); + tree t = build_tree_list (CHECKING_P + ? get_identifier ("omp adjust args idxs") + : NULL_TREE, + adjust_args_list); TREE_CHAIN (t) = append_args_tree; DECL_ATTRIBUTES (variant_decl) = tree_cons (get_identifier ("omp declare variant variant args"), t, @@ -27412,6 +29135,87 @@ c_finish_omp_declare_simd (c_parser *parser, tree fndecl, tree parms, clauses[0].type = CPP_PRAGMA; } +/* This is consistent with the C++ front end. */ + +#if !defined (NO_DOT_IN_LABEL) +#define JOIN_STR "." +#elif !defined (NO_DOLLAR_IN_LABEL) +#define JOIN_STR "$" +#else +#define JOIN_STR "_" +#endif + +/* 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 (c_declarator *declarator, tree ctx) +{ + c_declarator *id = declarator; + while (id->kind != cdk_id) + { + id = id->declarator; + gcc_assert (id); + } + tree name = id->u.id.id; + id->u.id.id = 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, 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 (tree decl, tree base_name, tree ctx) +{ + /* First look up BASE_NAME and ensure it matches DECL. */ + tree base_decl = lookup_name (base_name); + 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"); + return; + } + + if (!comptypes (TREE_TYPE (decl), TREE_TYPE (base_decl))) + { + error_at (DECL_SOURCE_LOCATION (decl), + "variant function definition does not match previous " + "declaration of %qE", base_decl); + return; + } + + /* Now set the attributes on the base and variant decls for the middle + end. */ + omp_check_for_duplicate_variant (DECL_SOURCE_LOCATION (decl), + base_decl, 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 (base_decl); + tree match_loc_node + = maybe_wrap_with_location (integer_zero_node, + DECL_SOURCE_LOCATION (base_decl)); + tree loc_node = tree_cons (match_loc_node, integer_zero_node, + build_tree_list (match_loc_node, + integer_zero_node)); + attrs = tree_cons (get_identifier ("omp declare variant base"), + tree_cons (decl, ctx, loc_node), attrs); + DECL_ATTRIBUTES (base_decl) = attrs; + + /* Variant functions are essentially anonymous and cannot be referenced + outside the compilation unit. */ + TREE_PUBLIC (decl) = 0; + DECL_COMDAT (decl) = 0; +} + + /* D should be C_TOKEN_VEC from omp::decl attribute. If it contains a threadprivate, groupprivate, allocate or declare target directive, return true and parse it for DECL. */ @@ -27644,7 +29448,9 @@ c_parser_omp_declare_target (c_parser *parser) /* 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) \ @@ -27684,11 +29490,75 @@ c_parser_omp_begin (c_parser *parser) indirect }; vec_safe_push (current_omp_declare_target_attribute, attr); } - else + else if (strcmp (p, "variant") == 0) { - c_parser_error (parser, "expected %<target%>"); + c_parser_consume_token (parser); + const char *clause = ""; + matching_parens parens; + location_t match_loc = c_parser_peek_token (parser)->location; + if (c_parser_next_token_is (parser, CPP_NAME)) + { + tree id = c_parser_peek_token (parser)->value; + clause = IDENTIFIER_POINTER (id); + } + if (strcmp (clause, "match") != 0) + { + c_parser_error (parser, "expected %<match%>"); + c_parser_skip_to_pragma_eol (parser); + return; + } + + c_parser_consume_token (parser); + + if (!parens.require_open (parser)) + { + c_parser_skip_to_pragma_eol (parser, false); + return; + } + + tree ctx = + c_parser_omp_context_selector_specification (parser, NULL_TREE); + 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 (current_omp_declare_variant_attribute)) + { + c_omp_declare_variant_attr a + = current_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. */ + c_parser_skip_to_pragma_eol (parser, false); + c_parser_skip_to_pragma_omp_end_declare_variant (parser); + return; + } + else + { + bool attr_syntax = parser->in_omp_attribute_pragma != NULL; + c_omp_declare_variant_attr a = { attr_syntax, ctx }; + vec_safe_push (current_omp_declare_variant_attribute, a); + } + + parens.require_close (parser); c_parser_skip_to_pragma_eol (parser); } + else + { + c_parser_error (parser, "expected %<target%> or %<variant%>"); + c_parser_skip_to_pragma_eol (parser, false); + } } else if (strcmp (p, "assumes") == 0) { @@ -27700,7 +29570,8 @@ c_parser_omp_begin (c_parser *parser) } else { - c_parser_error (parser, "expected %<declare target%> or %<assumes%>"); + c_parser_error (parser, "expected %<declare target%>, " + "%<declare variant%>, or %<assumes%>"); c_parser_skip_to_pragma_eol (parser); } } @@ -27709,7 +29580,8 @@ c_parser_omp_begin (c_parser *parser) #pragma omp end declare target OpenMP 5.1 - #pragma omp end assumes */ + #pragma omp end assumes + #pragma omp end declare variant new-line */ static void c_parser_omp_end (c_parser *parser) @@ -27722,44 +29594,74 @@ c_parser_omp_end (c_parser *parser) if (strcmp (p, "declare") == 0) { c_parser_consume_token (parser); - if (c_parser_next_token_is (parser, CPP_NAME) - && strcmp (IDENTIFIER_POINTER (c_parser_peek_token (parser)->value), - "target") == 0) - c_parser_consume_token (parser); - else + p = ""; + if (c_parser_next_token_is (parser, CPP_NAME)) + p = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); + if (strcmp (p, "target") == 0) { - c_parser_error (parser, "expected %<target%>"); + c_parser_consume_token (parser); + bool attr_syntax = parser->in_omp_attribute_pragma != NULL; c_parser_skip_to_pragma_eol (parser); - return; + if (!vec_safe_length (current_omp_declare_target_attribute)) + error_at (loc, "%<#pragma omp end declare target%> without " + "corresponding %<#pragma omp declare target%> or " + "%<#pragma omp begin declare target%>"); + else + { + c_omp_declare_target_attr + a = current_omp_declare_target_attribute->pop (); + if (a.attr_syntax != attr_syntax) + { + if (a.attr_syntax) + error_at (loc, + "%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 (loc, + "%qs in pragma syntax terminated " + "with %qs in attribute syntax", + a.device_type >= 0 ? "begin declare target" + : "declare target", + "end declare target"); + } + } } - bool attr_syntax = parser->in_omp_attribute_pragma != NULL; - c_parser_skip_to_pragma_eol (parser); - if (!vec_safe_length (current_omp_declare_target_attribute)) - error_at (loc, "%<#pragma omp end declare target%> without " - "corresponding %<#pragma omp declare target%> or " - "%<#pragma omp begin declare target%>"); - else + else if (strcmp (p, "variant") == 0) { - c_omp_declare_target_attr - a = current_omp_declare_target_attribute->pop (); - if (a.attr_syntax != attr_syntax) + c_parser_consume_token (parser); + bool attr_syntax = parser->in_omp_attribute_pragma != NULL; + c_parser_skip_to_pragma_eol (parser); + if (!vec_safe_length (current_omp_declare_variant_attribute)) + error_at (loc, "%<#pragma omp end declare variant%> without " + "corresponding %<#pragma omp begin declare variant%>"); + else { - if (a.attr_syntax) - error_at (loc, - "%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 (loc, - "%qs in pragma syntax terminated " - "with %qs in attribute syntax", - a.device_type >= 0 ? "begin declare target" - : "declare target", - "end declare target"); + c_omp_declare_variant_attr + a = current_omp_declare_variant_attribute->pop (); + if (a.attr_syntax != attr_syntax) + { + if (a.attr_syntax) + error_at (loc, + "%<begin declare variant%> in attribute syntax " + "terminated with " + "%<end declare variant%> in pragma syntax"); + else + error_at (loc, + "%<begin declare variant%> in pragma syntax " + "terminated with " + "%<end declare variant%> in attribute syntax"); + } } } + else + { + c_parser_error (parser, "expected %<target%> or %<variant%>"); + c_parser_skip_to_pragma_eol (parser); + return; + } } else if (strcmp (p, "assumes") == 0) { @@ -27795,6 +29697,151 @@ c_parser_omp_end (c_parser *parser) } } +/* OpenMP 5.0 + #pragma omp declare mapper ([mapper-identifier :] type var) \ + [clause [ [,] clause ] ... ] new-line */ + +static void +c_parser_omp_declare_mapper (c_parser *parser, enum pragma_context context) +{ + tree type, mapper_name = NULL_TREE, var = NULL_TREE, stmt, stmtlist; + tree maplist = NULL_TREE, mapper_id, mapper_decl, t; + c_token *token; + + if (context == pragma_struct || context == pragma_param) + { + error ("%<#pragma omp declare reduction%> not at file or block scope"); + goto fail; + } + + if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) + goto fail; + + token = c_parser_peek_token (parser); + + if (c_parser_peek_2nd_token (parser)->type == CPP_COLON) + { + switch (token->type) + { + case CPP_NAME: + mapper_name = token->value; + c_parser_consume_token (parser); + break; + case CPP_KEYWORD: + if (token->keyword == RID_DEFAULT) + { + mapper_name = NULL_TREE; + c_parser_consume_token (parser); + break; + } + /* Fallthrough. */ + default: + error_at (token->location, "expected identifier or %<default%>"); + c_parser_skip_to_pragma_eol (parser, false); + return; + } + + if (!c_parser_require (parser, CPP_COLON, "expected %<:%>")) + goto fail; + } + + mapper_id = c_omp_mapper_id (mapper_name); + mapper_decl = c_omp_mapper_decl (mapper_id); + + { + location_t loc = c_parser_peek_token (parser)->location; + struct c_type_name *ctype = c_parser_type_name (parser); + type = groktypename (ctype, NULL, NULL); + if (type == error_mark_node) + goto fail; + if (TREE_CODE (type) != RECORD_TYPE + && TREE_CODE (type) != UNION_TYPE) + { + error_at (loc, "%qT is not a struct or union type in " + "%<#pragma omp declare mapper%>", type); + c_parser_skip_to_pragma_eol (parser, false); + return; + } + for (tree t = DECL_INITIAL (mapper_decl); t; t = TREE_CHAIN (t)) + if (comptypes (TREE_PURPOSE (t), type)) + { + error_at (loc, "redeclaration of %qs %<#pragma omp declare " + "mapper%> for type %qT", IDENTIFIER_POINTER (mapper_id) + + sizeof ("omp declare mapper ") - 1, + type); + tree prevmapper = TREE_VALUE (t); + /* Hmm, this location might not be very accurate. */ + location_t ploc + = DECL_SOURCE_LOCATION (OMP_DECLARE_MAPPER_DECL (prevmapper)); + error_at (ploc, "previous %<#pragma omp declare mapper%>"); + c_parser_skip_to_pragma_eol (parser, false); + return; + } + } + + token = c_parser_peek_token (parser); + if (token->type == CPP_NAME) + { + var = build_decl (token->location, VAR_DECL, token->value, type); + c_parser_consume_token (parser); + DECL_ARTIFICIAL (var) = 1; + } + else + { + error_at (token->location, "expected identifier"); + goto fail; + } + + if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) + goto fail; + + push_scope (); + stmtlist = push_stmt_list (); + pushdecl (var); + DECL_CONTEXT (var) = current_function_decl; + + while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL)) + { + location_t here; + pragma_omp_clause c_kind; + here = c_parser_peek_token (parser)->location; + c_kind = c_parser_omp_clause_name (parser); + if (c_kind != PRAGMA_OMP_CLAUSE_MAP) + { + error_at (here, "unexpected clause"); + goto fail; + } + maplist = c_parser_omp_clause_map (parser, maplist, GOMP_MAP_UNSET); + } + + if (maplist == NULL_TREE) + { + error_at (input_location, "missing %<map%> clause"); + goto fail; + } + + stmt = make_node (OMP_DECLARE_MAPPER); + TREE_TYPE (stmt) = type; + OMP_DECLARE_MAPPER_ID (stmt) = mapper_name; + OMP_DECLARE_MAPPER_DECL (stmt) = var; + OMP_DECLARE_MAPPER_CLAUSES (stmt) = maplist; + + add_stmt (stmt); + + pop_stmt_list (stmtlist); + pop_scope (); + + c_parser_skip_to_pragma_eol (parser); + + t = tree_cons (type, stmt, DECL_INITIAL (mapper_decl)); + DECL_INITIAL (mapper_decl) = t; + + return; + + fail: + c_parser_skip_to_pragma_eol (parser); +} + /* OpenMP 4.0 #pragma omp declare reduction (reduction-id : typename-list : expression) \ initializer-clause[opt] new-line @@ -28186,6 +30233,12 @@ c_parser_omp_declare (c_parser *parser, enum pragma_context context) c_parser_omp_declare_reduction (parser, context); return false; } + if (strcmp (p, "mapper") == 0) + { + c_parser_consume_token (parser); + c_parser_omp_declare_mapper (parser, context); + return false; + } if (!flag_openmp) /* flag_openmp_simd */ { c_parser_skip_to_pragma_eol (parser, false); diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index 2098120..3d2d174 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -768,6 +768,8 @@ extern int in_sizeof; extern int in_typeof; extern bool c_in_omp_for; extern bool c_omp_array_section_p; +extern bool c_omp_array_shaping_op_p; +extern bool c_omp_has_array_shape_p; extern tree c_last_sizeof_arg; extern location_t c_last_sizeof_loc; @@ -822,7 +824,9 @@ extern tree build_component_ref (location_t, tree, tree, location_t, location_t, bool = true); extern tree handle_counted_by_for_component_ref (location_t, tree); extern tree build_array_ref (location_t, tree, tree); -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 create_omp_arrayshape_type (tree expr, + vec<tree> *omp_shape_dims); extern tree build_external_ref (location_t, tree, bool, tree *); extern void pop_maybe_used (bool); extern struct c_expr c_expr_sizeof_expr (location_t, struct c_expr); @@ -884,6 +888,10 @@ extern tree c_finish_omp_task (location_t, tree, tree); extern void c_finish_omp_cancel (location_t, tree); extern void c_finish_omp_cancellation_point (location_t, tree); extern tree c_finish_omp_clauses (tree, enum c_omp_region_type); +extern tree c_omp_finish_mapper_clauses (tree); +extern tree c_omp_mapper_lookup (tree, tree); +extern tree c_omp_extract_mapper_directive (tree); +extern tree c_omp_map_array_section (location_t, tree); extern tree c_build_va_arg (location_t, tree, location_t, tree); extern tree c_finish_transaction (location_t, tree, int); extern bool c_tree_equal (tree, tree); @@ -942,6 +950,9 @@ extern tree c_omp_reduction_id (enum tree_code, tree); extern tree c_omp_reduction_decl (tree); extern tree c_omp_reduction_lookup (tree, tree); extern tree c_check_omp_declare_reduction_r (tree *, int *, void *); +extern tree c_omp_mapper_id (tree); +extern tree c_omp_mapper_decl (tree); +extern void c_omp_scan_mapper_bindings (location_t, tree *, tree); extern bool c_check_in_current_scope (tree); extern void c_pushtag (location_t, tree, tree); extern void c_bind (location_t, tree, bool); diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index ea83451..18e9b71 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -81,6 +81,13 @@ bool c_in_omp_for; /* True when parsing OpenMP map clause. */ bool c_omp_array_section_p; +/* True when parsing OpenMP to/from clause. */ +bool c_omp_array_shaping_op_p; + +/* True if we have an OpenMP array-shaping "cast" expression. This adjusts + the parsed representation for e.g. array refs. */ +bool c_omp_has_array_shape_p; + /* The argument of last parsed sizeof expression, only to be tested if expr.original_code == SIZEOF_EXPR. */ tree c_last_sizeof_arg; @@ -2380,6 +2387,8 @@ mark_exp_read (tree exp) mark_exp_read (TREE_OPERAND (exp, 1)); if (TREE_OPERAND (exp, 2)) mark_exp_read (TREE_OPERAND (exp, 2)); + if (TREE_OPERAND (exp, 3)) + mark_exp_read (TREE_OPERAND (exp, 3)); break; default: break; @@ -3441,7 +3450,8 @@ build_array_ref (location_t loc, tree array, tree index) instead. */ tree -build_omp_array_section (location_t loc, tree array, tree index, tree length) +build_omp_array_section (location_t loc, tree array, tree index, tree length, + tree stride) { tree type = TREE_TYPE (array); gcc_assert (type); @@ -3478,7 +3488,48 @@ build_omp_array_section (location_t loc, tree array, tree index, tree length) sectype = c_build_array_type (eltype, idxtype); } - return build3_loc (loc, OMP_ARRAY_SECTION, sectype, array, index, length); + return build4_loc (loc, OMP_ARRAY_SECTION, sectype, array, index, length, + stride); +} + +/* Build an array type whose dimensions are given by OMP_SHAPE_DIMS and whose + elements are of the type pointed to by the "base" node of EXPR with outer + OMP_ARRAY_SECTIONs and ARRAY_REFs stripped off, e.g. the type of "*myptr" + in "myptr[0:2:3][4][5:6]". */ + +tree +create_omp_arrayshape_type (tree expr, vec<tree> *omp_shape_dims) +{ + tree strip_sections = expr; + + while (TREE_CODE (strip_sections) == OMP_ARRAY_SECTION + || TREE_CODE (strip_sections) == ARRAY_REF) + strip_sections = TREE_OPERAND (strip_sections, 0); + + tree 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; + tree dim; + FOR_EACH_VEC_ELT_REVERSE (*omp_shape_dims, i, dim) + { + tree maxidx = fold_convert (sizetype, dim); + maxidx = size_binop (MINUS_EXPR, maxidx, size_one_node); + tree index = build_index_type (maxidx); + type = build_array_type (type, index); + } + + return type; } @@ -15172,14 +15223,16 @@ c_finish_omp_cancellation_point (location_t loc, tree clauses) <= 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) { @@ -15257,15 +15310,23 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types, } 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 == 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))) @@ -15282,6 +15343,13 @@ 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 && TREE_CODE (low_bound) == INTEGER_CST && TYPE_PRECISION (TREE_TYPE (low_bound)) @@ -15292,8 +15360,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)) @@ -15412,12 +15487,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 && !integer_onep (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) @@ -15425,7 +15517,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)) { @@ -15484,7 +15576,9 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types, return error_mark_node; } /* If there is a pointer type anywhere but in the very first - array-section-subscript, the array section could be non-contiguous. */ + array-section-subscript, the array section could be non-contiguous. + Note that OpenACC does accept these kinds of non-contiguous pointer + based arrays. */ if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_AFFINITY && TREE_CODE (TREE_OPERAND (t, 0)) == OMP_ARRAY_SECTION) @@ -15496,12 +15590,37 @@ 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; + } } } } @@ -15513,7 +15632,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 = save_expr (low_bound); if (lb != low_bound) @@ -15521,28 +15640,56 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types, TREE_OPERAND (t, 1) = lb; low_bound = lb; } - ret = build_array_ref (OMP_CLAUSE_LOCATION (c), ret, low_bound); + /* 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 = build_omp_array_section (OMP_CLAUSE_LOCATION (c), ret, low_bound, + length, stride); + else + ret = build_array_ref (OMP_CLAUSE_LOCATION (c), ret, low_bound); 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 build_array_ref (loc, ret, low_bound); + } + + 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. */ 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) @@ -15576,15 +15723,19 @@ 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; + bool higher_discontiguous = false; + for (i = num, t = OMP_CLAUSE_DECL (c); i > 0; t = TREE_OPERAND (t, 0)) { tree low_bound = TREE_OPERAND (t, 1); tree length = TREE_OPERAND (t, 2); + tree stride = TREE_OPERAND (t, 3); i--; if (low_bound @@ -15597,12 +15748,65 @@ 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]) @@ -15616,12 +15820,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 (length != NULL_TREE @@ -15695,6 +15904,14 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort) size = size_binop (MULT_EXPR, size, l); } } + 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 @@ -15733,6 +15950,8 @@ 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); first = c_fully_fold (first, false, NULL); OMP_CLAUSE_DECL (c) = first; if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR) @@ -15748,7 +15967,8 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort) OMP_CLAUSE_SIZE (c) = size; } - if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP) + if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP + && !(discontiguous && *discontiguous == 2)) return false; auto_vec<omp_addr_token *, 10> addr_tokens; @@ -15758,23 +15978,28 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort) c_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 - || addr_tokens[0]->type == ARRAY_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; } @@ -15871,10 +16096,10 @@ c_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); @@ -15948,10 +16173,10 @@ c_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); tree type2 = TREE_TYPE (var2); location_t loc2 = DECL_SOURCE_LOCATION (var2); struct c_find_omp_var_s data = { var, &pset }; @@ -15986,10 +16211,10 @@ c_omp_finish_iterators (tree iter) ret = true; continue; } - TREE_VEC_ELT (it, 1) = begin; - TREE_VEC_ELT (it, 2) = end; - TREE_VEC_ELT (it, 3) = step; - TREE_VEC_ELT (it, 4) = orig_step; + OMP_ITERATORS_BEGIN (it) = begin; + OMP_ITERATORS_END (it) = end; + OMP_ITERATORS_STEP (it) = step; + OMP_ITERATORS_ORIG_STEP (it) = orig_step; } return ret; } @@ -16023,6 +16248,68 @@ c_oacc_check_attachments (tree c) return false; } +static bool +c_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 c_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 + && !c_oacc_reduction_defined_type_p (reduction_code, + TREE_TYPE (fld))) + return false; + return true; + } + + return false; +} + +static const char * +c_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 (); + } +} + /* For all elements of CLAUSES, validate them against their constraints. Remove any elements from the list that are invalid. */ @@ -16096,7 +16383,14 @@ c_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)) { @@ -16128,12 +16422,13 @@ c_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; } + c = *pc; t = OMP_CLAUSE_DECL (c); if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION && OMP_CLAUSE_REDUCTION_INSCAN (c)) @@ -16221,9 +16516,22 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort) break; } } - if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) == NULL_TREE - && (FLOAT_TYPE_P (type) - || TREE_CODE (type) == COMPLEX_TYPE)) + if (ort == C_ORT_ACC) + { + enum tree_code r_code = OMP_CLAUSE_REDUCTION_CODE (c); + if (!c_oacc_reduction_defined_type_p (r_code, TREE_TYPE (t))) + { + const char *r_name = c_oacc_reduction_code_name (r_code); + error_at (OMP_CLAUSE_LOCATION (c), + "%qE has invalid type for %<reduction(%s)%>", + t, r_name); + remove = true; + break; + } + } + else if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) == NULL_TREE + && (FLOAT_TYPE_P (type) + || TREE_CODE (type) == COMPLEX_TYPE)) { enum tree_code r_code = OMP_CLAUSE_REDUCTION_CODE (c); const char *r_name = NULL; @@ -16267,8 +16575,11 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort) } else if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) == error_mark_node) { - error_at (OMP_CLAUSE_LOCATION (c), - "user defined reduction not found for %qE", t); + /* There are no user-defined reductions in OpenACC (as of + 2.6). */ + if (ort & C_ORT_OMP) + error_at (OMP_CLAUSE_LOCATION (c), + "user defined reduction not found for %qE", t); remove = true; break; } @@ -16730,14 +17041,117 @@ c_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 ((VAR_P (t) || TREE_CODE (t) == PARM_DECL) + && (bitmap_bit_p (&generic_head, DECL_UID (t)) + || bitmap_bit_p (&map_head, DECL_UID (t)) + || bitmap_bit_p (&firstprivate_head, DECL_UID (t)) + || bitmap_bit_p (&lastprivate_head, DECL_UID (t)))) + { + error_at (OMP_CLAUSE_LOCATION (c), + "%qE appears more than once in data clauses", t); + remove = true; + break; + } + else + bitmap_set_bit (&generic_head, DECL_UID (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) + { + if (TREE_CODE (t) != ERROR_MARK) + error_at (OMP_CLAUSE_LOCATION (c), "traits array %qE must " + "be of %<const omp_alloctrait_t []%> type", t); + else + error_at (OMP_CLAUSE_LOCATION (c), "traits array must " + "be of %<const omp_alloctrait_t []%> type"); + remove = true; + } + else + { + tree cst_val = decl_constant_value_1 (t, true); + 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 @@ -16751,10 +17165,12 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort) last_iterators = NULL_TREE; 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 if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND - && OMP_CLAUSE_DEPEND_KIND (c) == OMP_CLAUSE_DEPEND_DEPOBJ) + else if ((c = *pc) + && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND + && (OMP_CLAUSE_DEPEND_KIND (c) + == OMP_CLAUSE_DEPEND_DEPOBJ)) { error_at (OMP_CLAUSE_LOCATION (c), "%<depend%> clause with %<depobj%> dependence " @@ -16837,10 +17253,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort) break; } } - if (TREE_CODE (OMP_CLAUSE_DECL (c)) == TREE_LIST - && TREE_PURPOSE (OMP_CLAUSE_DECL (c)) - && (TREE_CODE (TREE_PURPOSE (OMP_CLAUSE_DECL (c))) - == TREE_VEC)) + if (OMP_ITERATOR_DECL_P (OMP_CLAUSE_DECL (c))) TREE_VALUE (OMP_CLAUSE_DECL (c)) = t; else OMP_CLAUSE_DECL (c) = t; @@ -16850,9 +17263,25 @@ c_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) + && c_omp_finish_iterators (OMP_CLAUSE_ITERATORS (c))) + { + t = error_mark_node; + break; + } + /* FALLTHRU */ case OMP_CLAUSE__CACHE_: { using namespace omp_addr_tokenizer; @@ -16864,10 +17293,24 @@ c_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)) + tree *pnext = NULL; + /* 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; + 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"); + c = *pc; t = OMP_CLAUSE_DECL (c); if (!omp_mappable_type (TREE_TYPE (t))) { @@ -16963,6 +17406,8 @@ c_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 (!omp_parse_expr (addr_tokens, t)) @@ -17104,7 +17549,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort) } 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) @@ -17161,10 +17606,10 @@ c_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; @@ -17264,10 +17709,11 @@ c_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) == ARRAY_REF) t = TREE_OPERAND (t, 0); @@ -17581,6 +18027,11 @@ c_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 (simdlen && safelen && tree_int_cst_lt (OMP_CLAUSE_SAFELEN_EXPR (safelen), @@ -17790,6 +18241,15 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort) return clauses; } +/* Do processing necessary to make CLAUSES well-formed, where CLAUSES result + from implicit instantiation of user-defined mappers (in gimplify.cc). */ + +tree +c_omp_finish_mapper_clauses (tree clauses) +{ + return c_finish_omp_clauses (clauses, C_ORT_OMP); +} + /* Return code to initialize DST with a copy constructor from SRC. C doesn't have copy constructors nor assignment operators, only for _Atomic vars we need to perform __atomic_load from src into a temporary |