aboutsummaryrefslogtreecommitdiff
path: root/gcc/c-family
diff options
context:
space:
mode:
authorJulian Brown <julian@codesourcery.com>2023-08-14 12:41:56 +0000
committerJulian Brown <julian@codesourcery.com>2023-12-13 20:30:49 +0000
commit5fdb150cd4bf8f2da335e3f5c3a17aafcbc66dbe (patch)
treea08dda7f55f405825c9af6e21c5fc5887b8e2614 /gcc/c-family
parente1fde9de3ffa0afc804beca654a7540405de54f7 (diff)
downloadgcc-5fdb150cd4bf8f2da335e3f5c3a17aafcbc66dbe.zip
gcc-5fdb150cd4bf8f2da335e3f5c3a17aafcbc66dbe.tar.gz
gcc-5fdb150cd4bf8f2da335e3f5c3a17aafcbc66dbe.tar.bz2
OpenMP/OpenACC: Rework clause expansion and nested struct handling
This patch reworks clause expansion in the C, C++ and (to a lesser extent) Fortran front ends for OpenMP and OpenACC mapping nodes used in GPU offloading support. At present a single clause may be turned into several mapping nodes, or have its mapping type changed, in several places scattered through the front- and middle-end. The analysis relating to which particular transformations are needed for some given expression has become quite hard to follow. Briefly, we manipulate clause types in the following places: 1. During parsing, in c_omp_adjust_map_clauses. Depending on a set of rules, we may change a FIRSTPRIVATE_POINTER (etc.) mapping into ATTACH_DETACH, or mark the decl addressable. 2. In semantics.cc or c-typeck.cc, clauses are expanded in handle_omp_array_sections (called via {c_}finish_omp_clauses, or in finish_omp_clauses itself. The two cases are for processing array sections (the former), or non-array sections (the latter). 3. In gimplify.cc, we build sibling lists for struct accesses, which groups and sorts accesses along with their struct base, creating new ALLOC/RELEASE nodes for pointers. 4. In gimplify.cc:gimplify_adjust_omp_clauses, mapping nodes may be adjusted or created. This patch doesn't completely disrupt this scheme, though clause types are no longer adjusted in c_omp_adjust_map_clauses (step 1). Clause expansion in step 2 (for C and C++) now uses a single, unified mechanism, parts of which are also reused for analysis in step 3. Rather than the kind-of "ad-hoc" pattern matching on addresses used to expand clauses used at present, a new method for analysing addresses is introduced. This does a recursive-descent tree walk on expression nodes, and emits a vector of tokens describing each "part" of the address. This tokenized address can then be translated directly into mapping nodes, with the assurance that no part of the expression has been inadvertently skipped or misinterpreted. In this way, all the variations of ways pointers, arrays, references and component accesses might be combined can be teased apart into easily-understood cases - and we know we've "parsed" the whole address before we start analysis, so the right code paths can easily be selected. For example, a simple access "arr[idx]" might parse as: base-decl access-indexed-array or "mystruct->foo[x]" with a pointer "foo" component might parse as: base-decl access-pointer component-selector access-pointer A key observation is that support for "array" bases, e.g. accesses whose root nodes are not structures, but describe scalars or arrays, and also *one-level deep* structure accesses, have first-class support in gimplify and beyond. Expressions that use deeper struct accesses or e.g. multiple indirections were more problematic: some cases worked, but lots of cases didn't. This patch reimplements the support for those in gimplify.cc, again using the new "address tokenization" support. An expression like "mystruct->foo->bar[0:10]" used in a mapping node will translate the right-hand access directly in the front-end. The base for the access will be "mystruct->foo". This is handled recursively in gimplify.cc -- there may be several accesses of "mystruct"'s members on the same directive, so the sibling-list building machinery can be used again. (This was already being done for OpenACC, but the new implementation differs somewhat in details, and is more robust.) For OpenMP, in the case where the base pointer itself, i.e. "mystruct->foo" here, is NOT mapped on the same directive, we create a "fragile" mapping. This turns the "foo" component access into a zero-length allocation (which is a new feature for the runtime, so support has been added there too). A couple of changes have been made to how mapping clauses are turned into mapping nodes: The first change is based on the observation that it is probably never correct to use GOMP_MAP_ALWAYS_POINTER for component accesses (e.g. for references), because if the containing struct is already mapped on the target then the host version of the pointer in question will be corrupted if the struct is copied back from the target. This patch removes all such uses, across each of C, C++ and Fortran. The second change is to the way that GOMP_MAP_ATTACH_DETACH nodes are processed during sibling-list creation. For OpenMP, for pointer components, we must map the base pointer separately from an array section that uses the base pointer, so e.g. we must have both "map(mystruct.base)" and "map(mystruct.base[0:10])" mappings. These create nodes such as: GOMP_MAP_TOFROM mystruct.base G_M_TOFROM *mystruct.base [len: 10*elemsize] G_M_ATTACH_DETACH mystruct.base Instead of using the first of these directly when building the struct sibling list then skipping the group using GOMP_MAP_ATTACH_DETACH, leading to: GOMP_MAP_STRUCT mystruct [len: 1] GOMP_MAP_TOFROM mystruct.base we now introduce a new "mini-pass", omp_resolve_clause_dependencies, that drops the GOMP_MAP_TOFROM for the base pointer, marks the second group as having had a base-pointer mapping, then omp_build_struct_sibling_lists can create: GOMP_MAP_STRUCT mystruct [len: 1] GOMP_MAP_ALLOC mystruct.base [len: ptrsize] This ends up working better in many cases, particularly those involving references. (The "alloc" space is immediately overwritten by a pointer attachment, so this is mildly more efficient than a redundant TO mapping at runtime also.) There is support in the address tokenizer for "arbitrary" base expressions which aren't rooted at a decl, but that is not used as present because such addresses are disallowed at parse time. In the front-ends, the address tokenization machinery is mostly only used for clause expansion and not for diagnostics at present. It could be used for those too, which would allow more of my previous "address inspector" implementation to be removed. The new bits in gimplify.cc work with OpenACC also. This version of the patch addresses several first-pass review comments from Tobias, and fixes a few previously-missed cases for manually-managed ragged array mappings (including cases using references). Some arbitrary differences between handling of clause expansion for C vs. C++ have also been fixed, and some fragments from later in the patch series have been moved forward (where they were useful for fixing bugs). Several new test cases have been added. 2023-11-29 Julian Brown <julian@codesourcery.com> gcc/c-family/ * c-common.h (c_omp_region_type): Add C_ORT_EXIT_DATA, C_ORT_OMP_EXIT_DATA and C_ORT_ACC_TARGET. (omp_addr_token): Add forward declaration. (c_omp_address_inspector): New class. * c-omp.cc (c_omp_adjust_map_clauses): Mark decls addressable here, but do not change any mapping node types. (c_omp_address_inspector::unconverted_ref_origin, c_omp_address_inspector::component_access_p, c_omp_address_inspector::check_clause, c_omp_address_inspector::get_root_term, c_omp_address_inspector::map_supported_p, c_omp_address_inspector::get_origin, c_omp_address_inspector::maybe_unconvert_ref, c_omp_address_inspector::maybe_zero_length_array_section, c_omp_address_inspector::expand_array_base, c_omp_address_inspector::expand_component_selector, c_omp_address_inspector::expand_map_clause): New methods. (omp_expand_access_chain): New function. gcc/c/ * c-parser.cc (c_parser_oacc_all_clauses): Add TARGET_P parameter. Use to select region type for c_finish_omp_clauses call. (c_parser_oacc_loop): Update calls to c_parser_oacc_all_clauses. (c_parser_oacc_compute): Likewise. (c_parser_omp_target_data, c_parser_omp_target_enter_data): Support ATTACH kind. (c_parser_omp_target_exit_data): Support DETACH kind. (check_clauses): Handle GOMP_MAP_POINTER and GOMP_MAP_ATTACH here. * c-typeck.cc (handle_omp_array_sections_1, handle_omp_array_sections, c_finish_omp_clauses): Use c_omp_address_inspector class and OMP address tokenizer to analyze and expand map clause expressions. Fix some diagnostics. Fix "is OpenACC" condition for C_ORT_ACC_TARGET addition. gcc/cp/ * parser.cc (cp_parser_oacc_all_clauses): Add TARGET_P parameter. Use to select region type for finish_omp_clauses call. (cp_parser_omp_target_data, cp_parser_omp_target_enter_data): Support GOMP_MAP_ATTACH kind. (cp_parser_omp_target_exit_data): Support GOMP_MAP_DETACH kind. (cp_parser_oacc_declare): Update call to cp_parser_oacc_all_clauses. (cp_parser_oacc_loop): Update calls to cp_parser_oacc_all_clauses. (cp_parser_oacc_compute): Likewise. * pt.cc (tsubst_expr): Use C_ORT_ACC_TARGET for call to tsubst_omp_clauses for OpenACC compute regions. * semantics.cc (cp_omp_address_inspector): New class, derived from c_omp_address_inspector. (handle_omp_array_sections_1, handle_omp_array_sections, finish_omp_clauses): Use cp_omp_address_inspector class and OMP address tokenizer to analyze and expand OpenMP map clause expressions. Fix some diagnostics. Support C_ORT_ACC_TARGET. (finish_omp_target): Handle GOMP_MAP_POINTER. gcc/fortran/ * trans-openmp.cc (gfc_trans_omp_array_section): Add OPENMP parameter. Use GOMP_MAP_ATTACH_DETACH instead of GOMP_MAP_ALWAYS_POINTER for derived type components. (gfc_trans_omp_clauses): Update calls to gfc_trans_omp_array_section. gcc/ * gimplify.cc (build_struct_comp_nodes): Don't process GOMP_MAP_ATTACH_DETACH "middle" nodes here. (omp_mapping_group): Add REPROCESS_STRUCT and FRAGILE booleans for nested struct handling. (omp_strip_components_and_deref, omp_strip_indirections): Remove functions. (omp_get_attachment): Handle GOMP_MAP_DETACH here. (omp_group_last): Handle GOMP_MAP_*, GOMP_MAP_DETACH, GOMP_MAP_ATTACH_DETACH groups for "exit data" of reference-to-pointer component array sections. (omp_gather_mapping_groups_1): Initialise reprocess_struct and fragile fields. (omp_group_base): Handle GOMP_MAP_ATTACH_DETACH after GOMP_MAP_STRUCT. (omp_index_mapping_groups_1): Skip reprocess_struct groups. (omp_get_nonfirstprivate_group, omp_directive_maps_explicitly, omp_resolve_clause_dependencies, omp_first_chained_access_token): New functions. (omp_check_mapping_compatibility): Adjust accepted node combinations for "from" clauses using release instead of alloc. (omp_accumulate_sibling_list): Add GROUP_MAP, ADDR_TOKENS, FRAGILE_P, REPROCESSING_STRUCT, ADDED_TAIL parameters. Use OMP address tokenizer to analyze addresses. Reimplement nested struct handling, and implement "fragile groups". (omp_build_struct_sibling_lists): Adjust for changes to omp_accumulate_sibling_list. Recalculate bias for ATTACH_DETACH nodes after GOMP_MAP_STRUCT nodes. (gimplify_scan_omp_clauses): Call omp_resolve_clause_dependencies. Use OMP address tokenizer. (gimplify_adjust_omp_clauses_1): Use build_fold_indirect_ref_loc instead of build_simple_mem_ref_loc. * omp-general.cc (omp-general.h, tree-pretty-print.h): Include. (omp_addr_tokenizer): New namespace. (omp_addr_tokenizer::omp_addr_token): New. (omp_addr_tokenizer::omp_parse_component_selector, omp_addr_tokenizer::omp_parse_ref, omp_addr_tokenizer::omp_parse_pointer, omp_addr_tokenizer::omp_parse_access_method, omp_addr_tokenizer::omp_parse_access_methods, omp_addr_tokenizer::omp_parse_structure_base, omp_addr_tokenizer::omp_parse_structured_expr, omp_addr_tokenizer::omp_parse_array_expr, omp_addr_tokenizer::omp_access_chain_p, omp_addr_tokenizer::omp_accessed_addr): New functions. (omp_parse_expr, debug_omp_tokenized_addr): New functions. * omp-general.h (omp_addr_tokenizer::access_method_kinds, omp_addr_tokenizer::structure_base_kinds, omp_addr_tokenizer::token_type, omp_addr_tokenizer::omp_addr_token, omp_addr_tokenizer::omp_access_chain_p, omp_addr_tokenizer::omp_accessed_addr): New. (omp_addr_token, omp_parse_expr): New. * omp-low.cc (scan_sharing_clauses): Skip error check for references to pointers. * tree.h (OMP_CLAUSE_ATTACHMENT_MAPPING_ERASED): New macro. gcc/testsuite/ * c-c++-common/gomp/clauses-2.c: Fix error output. * c-c++-common/gomp/target-implicit-map-2.c: Adjust scan output. * c-c++-common/gomp/target-50.c: Adjust scan output. * c-c++-common/gomp/target-enter-data-1.c: Adjust scan output. * g++.dg/gomp/static-component-1.C: New test. * gcc.dg/gomp/target-3.c: Adjust scan output. * gfortran.dg/gomp/map-9.f90: Adjust scan output. libgomp/ * target.c (gomp_map_pointer): Modify zero-length array section pointer handling. (gomp_attach_pointer): Likewise. (gomp_map_fields_existing): Use gomp_map_0len_lookup. (gomp_attach_pointer): Allow attaching null pointers (or Fortran "unassociated" pointers). (gomp_map_vars_internal): Handle zero-sized struct members. Add diagnostic for unmapped struct pointer members. * testsuite/libgomp.c-c++-common/baseptrs-1.c: New test. * testsuite/libgomp.c-c++-common/baseptrs-2.c: New test. * testsuite/libgomp.c-c++-common/baseptrs-6.c: New test. * testsuite/libgomp.c-c++-common/baseptrs-7.c: New test. * testsuite/libgomp.c-c++-common/ptr-attach-2.c: New test. * testsuite/libgomp.c-c++-common/target-implicit-map-2.c: Fix missing "free". * testsuite/libgomp.c-c++-common/target-implicit-map-5.c: New test. * testsuite/libgomp.c-c++-common/target-map-zlas-1.c: New test. * testsuite/libgomp.c++/class-array-1.C: New test. * testsuite/libgomp.c++/baseptrs-3.C: New test. * testsuite/libgomp.c++/baseptrs-4.C: New test. * testsuite/libgomp.c++/baseptrs-5.C: New test. * testsuite/libgomp.c++/baseptrs-8.C: New test. * testsuite/libgomp.c++/baseptrs-9.C: New test. * testsuite/libgomp.c++/ref-mapping-1.C: New test. * testsuite/libgomp.c++/target-48.C: New test. * testsuite/libgomp.c++/target-49.C: New test. * testsuite/libgomp.c++/target-exit-data-reftoptr-1.C: New test. * testsuite/libgomp.c++/target-lambda-1.C: Update for OpenMP 5.2 semantics. * testsuite/libgomp.c++/target-this-3.C: Likewise. * testsuite/libgomp.c++/target-this-4.C: Likewise. * testsuite/libgomp.fortran/struct-elem-map-1.f90: Add temporary XFAIL. * testsuite/libgomp.fortran/target-enter-data-6.f90: Likewise.
Diffstat (limited to 'gcc/c-family')
-rw-r--r--gcc/c-family/c-common.h71
-rw-r--r--gcc/c-family/c-omp.cc875
2 files changed, 939 insertions, 7 deletions
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 62d76c8..6e7fc1b 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1279,8 +1279,11 @@ enum c_omp_region_type
C_ORT_ACC = 1 << 1,
C_ORT_DECLARE_SIMD = 1 << 2,
C_ORT_TARGET = 1 << 3,
+ C_ORT_EXIT_DATA = 1 << 4,
C_ORT_OMP_DECLARE_SIMD = C_ORT_OMP | C_ORT_DECLARE_SIMD,
- C_ORT_OMP_TARGET = C_ORT_OMP | C_ORT_TARGET
+ C_ORT_OMP_TARGET = C_ORT_OMP | C_ORT_TARGET,
+ C_ORT_OMP_EXIT_DATA = C_ORT_OMP | C_ORT_EXIT_DATA,
+ C_ORT_ACC_TARGET = C_ORT_ACC | C_ORT_TARGET
};
extern tree c_finish_omp_master (location_t, tree);
@@ -1317,6 +1320,72 @@ extern tree c_omp_check_context_selector (location_t, tree);
extern void c_omp_mark_declare_variant (location_t, tree, tree);
extern void c_omp_adjust_map_clauses (tree, bool);
+namespace omp_addr_tokenizer { struct omp_addr_token; }
+typedef omp_addr_tokenizer::omp_addr_token omp_addr_token;
+
+class c_omp_address_inspector
+{
+ location_t loc;
+ tree root_term;
+ bool indirections;
+ int map_supported;
+
+protected:
+ tree orig;
+
+public:
+ c_omp_address_inspector (location_t loc, tree t)
+ : loc (loc), root_term (NULL_TREE), indirections (false),
+ map_supported (-1), orig (t)
+ {
+ }
+
+ ~c_omp_address_inspector ()
+ {
+ }
+
+ virtual bool processing_template_decl_p ()
+ {
+ return false;
+ }
+
+ virtual void emit_unmappable_type_notes (tree)
+ {
+ }
+
+ virtual tree convert_from_reference (tree)
+ {
+ gcc_unreachable ();
+ }
+
+ virtual tree build_array_ref (location_t loc, tree arr, tree idx)
+ {
+ tree eltype = TREE_TYPE (TREE_TYPE (arr));
+ return build4_loc (loc, ARRAY_REF, eltype, arr, idx, NULL_TREE,
+ NULL_TREE);
+ }
+
+ virtual bool check_clause (tree);
+ tree get_root_term (bool);
+
+ tree unconverted_ref_origin ();
+ bool component_access_p ();
+
+ bool map_supported_p ();
+
+ static tree get_origin (tree);
+ static tree maybe_unconvert_ref (tree);
+
+ bool maybe_zero_length_array_section (tree);
+
+ tree expand_array_base (tree, vec<omp_addr_token *> &, tree, unsigned *,
+ c_omp_region_type);
+ tree expand_component_selector (tree, vec<omp_addr_token *> &, tree,
+ unsigned *, c_omp_region_type);
+ tree expand_map_clause (tree, tree, vec<omp_addr_token *> &,
+ c_omp_region_type);
+};
+
enum c_omp_directive_kind {
C_OMP_DIR_STANDALONE,
C_OMP_DIR_CONSTRUCT,
diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index 95b6c1e..5e534aa 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -3169,8 +3169,9 @@ struct map_clause
decl_mapped (false), omp_declare_target (false) { }
};
-/* Adjust map clauses after normal clause parsing, mainly to turn specific
- base-pointer map cases into attach/detach and mark them addressable. */
+/* Adjust map clauses after normal clause parsing, mainly to mark specific
+ base-pointer map cases addressable that may be turned into attach/detach
+ operations during gimplification. */
void
c_omp_adjust_map_clauses (tree clauses, bool is_target)
{
@@ -3186,7 +3187,6 @@ c_omp_adjust_map_clauses (tree clauses, bool is_target)
&& POINTER_TYPE_P (TREE_TYPE (OMP_CLAUSE_DECL (c))))
{
tree ptr = OMP_CLAUSE_DECL (c);
- OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ATTACH_DETACH);
c_common_mark_addressable_vec (ptr);
}
return;
@@ -3199,7 +3199,7 @@ c_omp_adjust_map_clauses (tree clauses, bool is_target)
&& DECL_P (OMP_CLAUSE_DECL (c)))
{
/* If this is for a target construct, the firstprivate pointer
- is changed to attach/detach if either is true:
+ is marked addressable if either is true:
(1) the base-pointer is mapped in this same construct, or
(2) the base-pointer is a variable place on the device by
"declare target" directives.
@@ -3241,11 +3241,874 @@ c_omp_adjust_map_clauses (tree clauses, bool is_target)
if (mc.firstprivate_ptr_p
&& (mc.decl_mapped || mc.omp_declare_target))
+ c_common_mark_addressable_vec (OMP_CLAUSE_DECL (mc.clause));
+ }
+}
+
+/* Maybe strip off an indirection from a "converted" reference, then find the
+ origin of a pointer (i.e. without any offset). */
+
+tree
+c_omp_address_inspector::unconverted_ref_origin ()
+{
+ tree t = orig;
+
+ /* We may have a reference-typed component access at the outermost level
+ that has had convert_from_reference called on it. Get the un-dereferenced
+ reference itself. */
+ t = maybe_unconvert_ref (t);
+
+ /* Find base pointer for POINTER_PLUS_EXPR, etc. */
+ t = get_origin (t);
+
+ return t;
+}
+
+/* Return TRUE if the address is a component access. */
+
+bool
+c_omp_address_inspector::component_access_p ()
+{
+ tree t = maybe_unconvert_ref (orig);
+
+ t = get_origin (t);
+
+ return TREE_CODE (t) == COMPONENT_REF;
+}
+
+/* Perform various checks on the address, as described by clause CLAUSE (we
+ only use its code and location here). */
+
+bool
+c_omp_address_inspector::check_clause (tree clause)
+{
+ tree t = unconverted_ref_origin ();
+
+ if (TREE_CODE (t) != COMPONENT_REF)
+ return true;
+
+ if (TREE_CODE (TREE_OPERAND (t, 1)) == FIELD_DECL
+ && DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
+ {
+ error_at (OMP_CLAUSE_LOCATION (clause),
+ "bit-field %qE in %qs clause",
+ t, omp_clause_code_name[OMP_CLAUSE_CODE (clause)]);
+ return false;
+ }
+ else if (!processing_template_decl_p ()
+ && !omp_mappable_type (TREE_TYPE (t)))
+ {
+ error_at (OMP_CLAUSE_LOCATION (clause),
+ "%qE does not have a mappable type in %qs clause",
+ t, omp_clause_code_name[OMP_CLAUSE_CODE (clause)]);
+ emit_unmappable_type_notes (TREE_TYPE (t));
+ return false;
+ }
+ else if (TREE_TYPE (t) && TYPE_ATOMIC (TREE_TYPE (t)))
+ {
+ error_at (OMP_CLAUSE_LOCATION (clause),
+ "%<_Atomic%> %qE in %qs clause", t,
+ omp_clause_code_name[OMP_CLAUSE_CODE (clause)]);
+ return false;
+ }
+
+ return true;
+}
+
+/* Find the "root term" for the address. This is the innermost decl, etc.
+ of the access. */
+
+tree
+c_omp_address_inspector::get_root_term (bool checking)
+{
+ if (root_term && !checking)
+ return root_term;
+
+ tree t = unconverted_ref_origin ();
+
+ while (TREE_CODE (t) == COMPONENT_REF)
+ {
+ if (checking
+ && TREE_TYPE (TREE_OPERAND (t, 0))
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == UNION_TYPE)
+ {
+ error_at (loc, "%qE is a member of a union", t);
+ return error_mark_node;
+ }
+ t = TREE_OPERAND (t, 0);
+ while (TREE_CODE (t) == MEM_REF
+ || TREE_CODE (t) == INDIRECT_REF
+ || TREE_CODE (t) == ARRAY_REF)
+ {
+ if (TREE_CODE (t) == MEM_REF
+ || TREE_CODE (t) == INDIRECT_REF)
+ indirections = true;
+ t = TREE_OPERAND (t, 0);
+ STRIP_NOPS (t);
+ if (TREE_CODE (t) == POINTER_PLUS_EXPR)
+ t = TREE_OPERAND (t, 0);
+ }
+ }
+
+ root_term = t;
+
+ return t;
+}
+
+/* Return TRUE if the address is supported in mapping clauses. At present,
+ this means that the innermost expression is a DECL_P, but could be extended
+ to other types of expression in the future. */
+
+bool
+c_omp_address_inspector::map_supported_p ()
+{
+ /* If we've already decided if the mapped address is supported, return
+ that. */
+ if (map_supported != -1)
+ return map_supported;
+
+ tree t = unconverted_ref_origin ();
+
+ STRIP_NOPS (t);
+
+ while (TREE_CODE (t) == INDIRECT_REF
+ || TREE_CODE (t) == MEM_REF
+ || TREE_CODE (t) == ARRAY_REF
+ || TREE_CODE (t) == COMPONENT_REF
+ || TREE_CODE (t) == COMPOUND_EXPR
+ || TREE_CODE (t) == SAVE_EXPR
+ || TREE_CODE (t) == POINTER_PLUS_EXPR
+ || TREE_CODE (t) == NON_LVALUE_EXPR
+ || TREE_CODE (t) == NOP_EXPR)
+ if (TREE_CODE (t) == COMPOUND_EXPR)
+ t = TREE_OPERAND (t, 1);
+ else
+ t = TREE_OPERAND (t, 0);
+
+ STRIP_NOPS (t);
+
+ map_supported = DECL_P (t);
+
+ return map_supported;
+}
+
+/* Get the origin of an address T, stripping off offsets and some other
+ bits. */
+
+tree
+c_omp_address_inspector::get_origin (tree t)
+{
+ while (1)
+ {
+ if (TREE_CODE (t) == COMPOUND_EXPR)
+ {
+ t = TREE_OPERAND (t, 1);
+ STRIP_NOPS (t);
+ }
+ else if (TREE_CODE (t) == POINTER_PLUS_EXPR
+ || TREE_CODE (t) == SAVE_EXPR)
+ t = TREE_OPERAND (t, 0);
+ else if (TREE_CODE (t) == INDIRECT_REF
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == REFERENCE_TYPE)
+ t = TREE_OPERAND (t, 0);
+ else
+ break;
+ }
+ STRIP_NOPS (t);
+ return t;
+}
+
+/* For an address T that might be a reference that has had
+ "convert_from_reference" called on it, return the actual reference without
+ any indirection. */
+
+tree
+c_omp_address_inspector::maybe_unconvert_ref (tree t)
+{
+ if (TREE_CODE (t) == INDIRECT_REF
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == REFERENCE_TYPE)
+ return TREE_OPERAND (t, 0);
+
+ return t;
+}
+
+/* Return TRUE if CLAUSE might describe a zero-length array section. */
+
+bool
+c_omp_address_inspector::maybe_zero_length_array_section (tree clause)
+{
+ switch (OMP_CLAUSE_MAP_KIND (clause))
+ {
+ case GOMP_MAP_ALLOC:
+ case GOMP_MAP_IF_PRESENT:
+ case GOMP_MAP_TO:
+ case GOMP_MAP_FROM:
+ case GOMP_MAP_TOFROM:
+ case GOMP_MAP_ALWAYS_TO:
+ case GOMP_MAP_ALWAYS_FROM:
+ case GOMP_MAP_ALWAYS_TOFROM:
+ case GOMP_MAP_PRESENT_ALLOC:
+ case GOMP_MAP_PRESENT_TO:
+ case GOMP_MAP_PRESENT_FROM:
+ case GOMP_MAP_PRESENT_TOFROM:
+ case GOMP_MAP_ALWAYS_PRESENT_TO:
+ case GOMP_MAP_ALWAYS_PRESENT_FROM:
+ case GOMP_MAP_ALWAYS_PRESENT_TOFROM:
+ case GOMP_MAP_RELEASE:
+ case GOMP_MAP_DELETE:
+ case GOMP_MAP_FORCE_TO:
+ case GOMP_MAP_FORCE_FROM:
+ case GOMP_MAP_FORCE_TOFROM:
+ case GOMP_MAP_FORCE_PRESENT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Expand a chained access. We only expect to see a quite limited range of
+ expression types here, because e.g. you can't have an array of
+ references. */
+
+static tree
+omp_expand_access_chain (tree c, tree expr, vec<omp_addr_token *> &addr_tokens,
+ unsigned *idx, c_omp_region_type ort)
+{
+ using namespace omp_addr_tokenizer;
+ location_t loc = OMP_CLAUSE_LOCATION (c);
+ unsigned i = *idx;
+ tree c2 = NULL_TREE;
+ gomp_map_kind kind;
+
+ if ((ort & C_ORT_EXIT_DATA) != 0
+ || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM
+ || (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+ && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM
+ || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DELETE
+ || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_RELEASE
+ || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_FROM
+ || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FORCE_FROM
+ || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_PRESENT_FROM
+ || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_PRESENT_FROM)))
+ kind = GOMP_MAP_DETACH;
+ else
+ kind = GOMP_MAP_ATTACH;
+
+ switch (addr_tokens[i]->u.access_kind)
+ {
+ case ACCESS_POINTER:
+ case ACCESS_POINTER_OFFSET:
+ {
+ tree virtual_origin
+ = fold_convert_loc (loc, ptrdiff_type_node, addr_tokens[i]->expr);
+ tree data_addr = omp_accessed_addr (addr_tokens, i, expr);
+ c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (c2, kind);
+ OMP_CLAUSE_DECL (c2) = addr_tokens[i]->expr;
+ OMP_CLAUSE_SIZE (c2)
+ = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+ fold_convert_loc (loc, ptrdiff_type_node,
+ data_addr),
+ virtual_origin);
+ }
+ break;
+
+ case ACCESS_INDEXED_ARRAY:
+ break;
+
+ default:
+ return error_mark_node;
+ }
+
+ if (c2)
+ {
+ OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
+ OMP_CLAUSE_CHAIN (c) = c2;
+ c = c2;
+ }
+
+ *idx = ++i;
+
+ if (i < addr_tokens.length ()
+ && addr_tokens[i]->type == ACCESS_METHOD)
+ return omp_expand_access_chain (c, expr, addr_tokens, idx, ort);
+
+ return c;
+}
+
+/* Translate "array_base_decl access_method" to OMP mapping clauses. */
+
+tree
+c_omp_address_inspector::expand_array_base (tree c,
+ vec<omp_addr_token *> &addr_tokens,
+ tree expr, unsigned *idx,
+ c_omp_region_type ort)
+{
+ using namespace omp_addr_tokenizer;
+ location_t loc = OMP_CLAUSE_LOCATION (c);
+ int i = *idx;
+ tree decl = addr_tokens[i + 1]->expr;
+ bool decl_p = DECL_P (decl);
+ bool declare_target_p = (decl_p
+ && is_global_var (decl)
+ && lookup_attribute ("omp declare target",
+ DECL_ATTRIBUTES (decl)));
+ bool map_p = OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP;
+ bool implicit_p = map_p && OMP_CLAUSE_MAP_IMPLICIT (c);
+ bool chain_p = omp_access_chain_p (addr_tokens, i + 1);
+ tree c2 = NULL_TREE, c3 = NULL_TREE;
+ unsigned consume_tokens = 2;
+ bool target_p = (ort & C_ORT_TARGET) != 0;
+ bool openmp_p = (ort & C_ORT_OMP) != 0;
+
+ gcc_assert (i == 0);
+
+ if (!openmp_p
+ && map_p
+ && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+ || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+ {
+ i += 2;
+ *idx = i;
+ return c;
+ }
+
+ switch (addr_tokens[i + 1]->u.access_kind)
+ {
+ case ACCESS_DIRECT:
+ if (decl_p && !target_p)
+ c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+ break;
+
+ case ACCESS_REF:
+ {
+ /* Copy the referenced object. Note that we do this even for !MAP_P
+ clauses. */
+ tree obj = convert_from_reference (addr_tokens[i + 1]->expr);
+ if (TREE_CODE (TREE_TYPE (obj)) == ARRAY_TYPE)
+ /* We have a ref to array: add a [0] element as the ME expects. */
+ OMP_CLAUSE_DECL (c) = build_array_ref (loc, obj, integer_zero_node);
+ else
+ OMP_CLAUSE_DECL (c) = obj;
+ OMP_CLAUSE_SIZE (c) = TYPE_SIZE_UNIT (TREE_TYPE (obj));
+
+ if (!map_p)
+ {
+ if (decl_p)
+ c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+ break;
+ }
+
+ if (!target_p)
+ break;
+
+ /* If we have a reference to a pointer, avoid using
+ FIRSTPRIVATE_REFERENCE here in case the pointer is modified in the
+ offload region (we can only do that if the pointer does not point
+ to a mapped block). We could avoid doing this if we don't have a
+ FROM mapping... */
+ bool ref_to_ptr = TREE_CODE (TREE_TYPE (obj)) == POINTER_TYPE;
+
+ c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+ if (!ref_to_ptr
+ && !declare_target_p
+ && decl_p)
+ OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_REFERENCE);
+ else
+ {
+ OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+ if (decl_p)
+ c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+ }
+ OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+ OMP_CLAUSE_SIZE (c2) = size_zero_node;
+
+ if (ref_to_ptr)
+ {
+ c3 = c2;
+ c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALLOC);
+ OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+ OMP_CLAUSE_SIZE (c2)
+ = TYPE_SIZE_UNIT (TREE_TYPE (OMP_CLAUSE_DECL (c2)));
+ }
+ }
+ break;
+
+ case ACCESS_INDEXED_REF_TO_ARRAY:
+ {
+ if (!map_p)
+ {
+ if (decl_p)
+ c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+ break;
+ }
+
+ if (!target_p)
+ break;
+
+ tree virtual_origin
+ = convert_from_reference (addr_tokens[i + 1]->expr);
+ virtual_origin = build_fold_addr_expr (virtual_origin);
+ virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+ virtual_origin);
+ tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+ c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+ if (decl_p && target_p && !declare_target_p)
+ {
+ /* It appears that omp-low.cc mishandles cases where we have a
+ [reference to an] array of pointers such as:
+
+ int *arr[N]; (or "int *(&arr)[N] = ...")
+ #pragma omp target map(arr[a][b:c])
+ { ... }
+
+ in such cases chain_p will be true. For now, fall back to
+ GOMP_MAP_POINTER. */
+ enum gomp_map_kind k = chain_p ? GOMP_MAP_POINTER
+ : GOMP_MAP_FIRSTPRIVATE_REFERENCE;
+ OMP_CLAUSE_SET_MAP_KIND (c2, k);
+ }
+ else
+ {
+ if (decl_p)
+ c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+ OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+ }
+ OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+ OMP_CLAUSE_SIZE (c2)
+ = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+ fold_convert_loc (loc, ptrdiff_type_node,
+ data_addr),
+ virtual_origin);
+ }
+ break;
+
+ case ACCESS_INDEXED_ARRAY:
+ {
+ if (!map_p)
+ {
+ if (decl_p)
+ c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+ break;
+ }
+
+ /* The code handling "firstprivatize_array_bases" in gimplify.cc is
+ relevant here. What do we need to create for arrays at this
+ stage? (This condition doesn't feel quite right. FIXME?) */
+ if (!target_p
+ && (TREE_CODE (TREE_TYPE (addr_tokens[i + 1]->expr))
+ == ARRAY_TYPE))
+ break;
+
+ tree virtual_origin
+ = build_fold_addr_expr (addr_tokens[i + 1]->expr);
+ virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+ virtual_origin);
+ tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+ c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+ if (decl_p && target_p)
+ {
+ /* See comment for ACCESS_INDEXED_REF_TO_ARRAY above. */
+ enum gomp_map_kind k = chain_p ? GOMP_MAP_POINTER
+ : GOMP_MAP_FIRSTPRIVATE_POINTER;
+ OMP_CLAUSE_SET_MAP_KIND (c2, k);
+ }
+ else
+ {
+ if (decl_p)
+ c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+ OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+ }
+ OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+ OMP_CLAUSE_SIZE (c2)
+ = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+ fold_convert_loc (loc, ptrdiff_type_node,
+ data_addr),
+ virtual_origin);
+ }
+ break;
+
+ case ACCESS_POINTER:
+ case ACCESS_POINTER_OFFSET:
+ {
+ if (!map_p)
+ {
+ if (decl_p)
+ c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+ break;
+ }
+
+ unsigned last_access = i + 1;
+ tree virtual_origin;
+
+ if (chain_p
+ && addr_tokens[i + 2]->type == ACCESS_METHOD
+ && addr_tokens[i + 2]->u.access_kind == ACCESS_INDEXED_ARRAY)
+ {
+ /* !!! This seems wrong for ACCESS_POINTER_OFFSET. */
+ consume_tokens = 3;
+ chain_p = omp_access_chain_p (addr_tokens, i + 2);
+ last_access = i + 2;
+ virtual_origin
+ = build_array_ref (loc, addr_tokens[last_access]->expr,
+ integer_zero_node);
+ virtual_origin = build_fold_addr_expr (virtual_origin);
+ virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+ virtual_origin);
+ }
+ else
+ virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+ addr_tokens[last_access]->expr);
+ tree data_addr = omp_accessed_addr (addr_tokens, last_access, expr);
+ c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+ /* For OpenACC, use FIRSTPRIVATE_POINTER for decls even on non-compute
+ regions (e.g. "acc data" constructs). It'll be removed anyway in
+ gimplify.cc, but doing it this way maintains diagnostic
+ behaviour. */
+ if (decl_p && (target_p || !openmp_p) && !chain_p && !declare_target_p)
+ OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER);
+ else
+ {
+ if (decl_p)
+ c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+ OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+ }
+ OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+ OMP_CLAUSE_SIZE (c2)
+ = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+ fold_convert_loc (loc, ptrdiff_type_node,
+ data_addr),
+ virtual_origin);
+ }
+ break;
+
+ case ACCESS_REF_TO_POINTER:
+ case ACCESS_REF_TO_POINTER_OFFSET:
+ {
+ if (!map_p)
+ {
+ if (decl_p)
+ c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+ break;
+ }
+
+ unsigned last_access = i + 1;
+ tree virtual_origin;
+
+ if (chain_p
+ && addr_tokens[i + 2]->type == ACCESS_METHOD
+ && addr_tokens[i + 2]->u.access_kind == ACCESS_INDEXED_ARRAY)
+ {
+ /* !!! This seems wrong for ACCESS_POINTER_OFFSET. */
+ consume_tokens = 3;
+ chain_p = omp_access_chain_p (addr_tokens, i + 2);
+ last_access = i + 2;
+ virtual_origin
+ = build_array_ref (loc, addr_tokens[last_access]->expr,
+ integer_zero_node);
+ virtual_origin = build_fold_addr_expr (virtual_origin);
+ virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+ virtual_origin);
+ }
+ else
+ {
+ virtual_origin
+ = convert_from_reference (addr_tokens[last_access]->expr);
+ virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+ virtual_origin);
+ }
+
+ tree data_addr = omp_accessed_addr (addr_tokens, last_access, expr);
+ c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+ if (decl_p && target_p && !chain_p && !declare_target_p)
+ {
+ OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_REFERENCE);
+ OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+ }
+ else
+ {
+ if (decl_p)
+ c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+ OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+ OMP_CLAUSE_DECL (c2)
+ = convert_from_reference (addr_tokens[i + 1]->expr);
+ }
+ OMP_CLAUSE_SIZE (c2)
+ = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+ fold_convert_loc (loc, ptrdiff_type_node,
+ data_addr),
+ virtual_origin);
+ }
+ break;
+
+ default:
+ *idx = i + consume_tokens;
+ return error_mark_node;
+ }
+
+ if (c3)
+ {
+ OMP_CLAUSE_CHAIN (c3) = OMP_CLAUSE_CHAIN (c);
+ OMP_CLAUSE_CHAIN (c2) = c3;
+ OMP_CLAUSE_CHAIN (c) = c2;
+ if (implicit_p)
+ {
+ OMP_CLAUSE_MAP_IMPLICIT (c2) = 1;
+ OMP_CLAUSE_MAP_IMPLICIT (c3) = 1;
+ }
+ c = c3;
+ }
+ else if (c2)
+ {
+ OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
+ OMP_CLAUSE_CHAIN (c) = c2;
+ if (implicit_p)
+ OMP_CLAUSE_MAP_IMPLICIT (c2) = 1;
+ c = c2;
+ }
+
+ i += consume_tokens;
+ *idx = i;
+
+ if (chain_p && map_p)
+ return omp_expand_access_chain (c, expr, addr_tokens, idx, ort);
+
+ return c;
+}
+
+/* Translate "component_selector access_method" to OMP mapping clauses. */
+
+tree
+c_omp_address_inspector::expand_component_selector (tree c,
+ vec<omp_addr_token *>
+ &addr_tokens,
+ tree expr, unsigned *idx,
+ c_omp_region_type ort)
+{
+ using namespace omp_addr_tokenizer;
+ location_t loc = OMP_CLAUSE_LOCATION (c);
+ unsigned i = *idx;
+ tree c2 = NULL_TREE, c3 = NULL_TREE;
+ bool chain_p = omp_access_chain_p (addr_tokens, i + 1);
+ bool map_p = OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP;
+
+ switch (addr_tokens[i + 1]->u.access_kind)
+ {
+ case ACCESS_DIRECT:
+ case ACCESS_INDEXED_ARRAY:
+ break;
+
+ case ACCESS_REF:
+ {
+ /* Copy the referenced object. Note that we also do this for !MAP_P
+ clauses. */
+ tree obj = convert_from_reference (addr_tokens[i + 1]->expr);
+ OMP_CLAUSE_DECL (c) = obj;
+ OMP_CLAUSE_SIZE (c) = TYPE_SIZE_UNIT (TREE_TYPE (obj));
+
+ if (!map_p)
+ break;
+
+ c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+ OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+ OMP_CLAUSE_SIZE (c2) = size_zero_node;
+ }
+ break;
+
+ case ACCESS_INDEXED_REF_TO_ARRAY:
+ {
+ if (!map_p)
+ break;
+
+ tree virtual_origin
+ = convert_from_reference (addr_tokens[i + 1]->expr);
+ virtual_origin = build_fold_addr_expr (virtual_origin);
+ virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+ virtual_origin);
+ tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+
+ c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+ OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+ OMP_CLAUSE_SIZE (c2)
+ = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+ fold_convert_loc (loc, ptrdiff_type_node,
+ data_addr),
+ virtual_origin);
+ }
+ break;
+
+ case ACCESS_POINTER:
+ case ACCESS_POINTER_OFFSET:
+ {
+ if (!map_p)
+ break;
+
+ tree virtual_origin
+ = fold_convert_loc (loc, ptrdiff_type_node,
+ addr_tokens[i + 1]->expr);
+ tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+
+ c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+ OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+ OMP_CLAUSE_SIZE (c2)
+ = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+ fold_convert_loc (loc, ptrdiff_type_node,
+ data_addr),
+ virtual_origin);
+ }
+ break;
+
+ case ACCESS_REF_TO_POINTER:
+ case ACCESS_REF_TO_POINTER_OFFSET:
+ {
+ if (!map_p)
+ break;
+
+ tree ptr = convert_from_reference (addr_tokens[i + 1]->expr);
+ tree virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+ ptr);
+ tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+
+ /* Attach the pointer... */
+ c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+ OMP_CLAUSE_DECL (c2) = ptr;
+ OMP_CLAUSE_SIZE (c2)
+ = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+ fold_convert_loc (loc, ptrdiff_type_node,
+ data_addr),
+ virtual_origin);
+
+ /* ...and also the reference. */
+ c3 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (c3, GOMP_MAP_ATTACH_DETACH);
+ OMP_CLAUSE_DECL (c3) = addr_tokens[i + 1]->expr;
+ OMP_CLAUSE_SIZE (c3) = size_zero_node;
+ }
+ break;
+
+ default:
+ *idx = i + 2;
+ return error_mark_node;
+ }
+
+ if (c3)
+ {
+ OMP_CLAUSE_CHAIN (c3) = OMP_CLAUSE_CHAIN (c);
+ OMP_CLAUSE_CHAIN (c2) = c3;
+ OMP_CLAUSE_CHAIN (c) = c2;
+ c = c3;
+ }
+ else if (c2)
+ {
+ OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
+ OMP_CLAUSE_CHAIN (c) = c2;
+ c = c2;
+ }
+
+ i += 2;
+ *idx = i;
+
+ if (chain_p && map_p)
+ return omp_expand_access_chain (c, expr, addr_tokens, idx, ort);
+
+ return c;
+}
+
+/* Expand a map clause into a group of mapping clauses, creating nodes to
+ attach/detach pointers and so forth as necessary. */
+
+tree
+c_omp_address_inspector::expand_map_clause (tree c, tree expr,
+ vec<omp_addr_token *> &addr_tokens,
+ c_omp_region_type ort)
+{
+ using namespace omp_addr_tokenizer;
+ unsigned i, length = addr_tokens.length ();
+
+ for (i = 0; i < length;)
+ {
+ int remaining = length - i;
+
+ if (remaining >= 2
+ && addr_tokens[i]->type == ARRAY_BASE
+ && addr_tokens[i]->u.structure_base_kind == BASE_DECL
+ && addr_tokens[i + 1]->type == ACCESS_METHOD)
+ {
+ c = expand_array_base (c, addr_tokens, expr, &i, ort);
+ if (c == error_mark_node)
+ return error_mark_node;
+ }
+ else if (remaining >= 2
+ && addr_tokens[i]->type == ARRAY_BASE
+ && addr_tokens[i]->u.structure_base_kind == BASE_ARBITRARY_EXPR
+ && addr_tokens[i + 1]->type == ACCESS_METHOD)
+ {
+ c = expand_array_base (c, addr_tokens, expr, &i, ort);
+ if (c == error_mark_node)
+ return error_mark_node;
+ }
+ else if (remaining >= 2
+ && addr_tokens[i]->type == STRUCTURE_BASE
+ && addr_tokens[i]->u.structure_base_kind == BASE_DECL
+ && addr_tokens[i + 1]->type == ACCESS_METHOD)
{
- OMP_CLAUSE_SET_MAP_KIND (mc.clause, GOMP_MAP_ATTACH_DETACH);
- c_common_mark_addressable_vec (OMP_CLAUSE_DECL (mc.clause));
+ if (addr_tokens[i + 1]->u.access_kind == ACCESS_DIRECT)
+ c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+ i += 2;
+ while (addr_tokens[i]->type == ACCESS_METHOD)
+ i++;
}
+ else if (remaining >= 2
+ && addr_tokens[i]->type == STRUCTURE_BASE
+ && addr_tokens[i]->u.structure_base_kind == BASE_ARBITRARY_EXPR
+ && addr_tokens[i + 1]->type == ACCESS_METHOD)
+ {
+ switch (addr_tokens[i + 1]->u.access_kind)
+ {
+ case ACCESS_DIRECT:
+ case ACCESS_POINTER:
+ i += 2;
+ while (addr_tokens[i]->type == ACCESS_METHOD)
+ i++;
+ break;
+ default:
+ return error_mark_node;
+ }
+ }
+ else if (remaining >= 2
+ && addr_tokens[i]->type == COMPONENT_SELECTOR
+ && addr_tokens[i + 1]->type == ACCESS_METHOD)
+ {
+ c = expand_component_selector (c, addr_tokens, expr, &i, ort);
+ /* We used 'expr', so these must have been the last tokens. */
+ gcc_assert (i == length);
+ if (c == error_mark_node)
+ return error_mark_node;
+ }
+ else if (remaining >= 3
+ && addr_tokens[i]->type == COMPONENT_SELECTOR
+ && addr_tokens[i + 1]->type == STRUCTURE_BASE
+ && (addr_tokens[i + 1]->u.structure_base_kind
+ == BASE_COMPONENT_EXPR)
+ && addr_tokens[i + 2]->type == ACCESS_METHOD)
+ {
+ i += 3;
+ while (addr_tokens[i]->type == ACCESS_METHOD)
+ i++;
+ }
+ else
+ break;
}
+
+ if (i == length)
+ return c;
+
+ return error_mark_node;
}
const struct c_omp_directive c_omp_directives[] = {