Hi Jakub, On 2021/6/24 9:15 PM, Jakub Jelinek wrote: > On Fri, Jun 18, 2021 at 10:25:16PM +0800, Chung-Lin Tang wrote: > > Note, you'll need to rebase your patch, it clashes with > r12-1768-g7619d33471c10fe3d149dcbb701d99ed3dd23528. > Sorry for that. And sorry for patch review delay. > >> --- a/gcc/c/c-typeck.c >> +++ b/gcc/c/c-typeck.c >> @@ -13104,6 +13104,12 @@ handle_omp_array_sections_1 (tree c, tree t, vec &types, >> return error_mark_node; >> } >> t = TREE_OPERAND (t, 0); >> + if ((ort == C_ORT_ACC || ort == C_ORT_OMP) > > Map clauses never appear on declare simd, so > (ort == C_ORT_ACC || ort == C_ORT_OMP) > previously meant always and since the in_reduction change is incorrect > (as C_ORT_OMP_TARGET is used for target construct but not for > e.g. target data* or target update). > >> + && TREE_CODE (t) == MEM_REF) Upon reviewing, it appears that most of these C_ORT_* tests are no longer needed, removed in new patch. > So please just use if (TREE_CODE (t) == MEM_REF) > or explain when it shouldn't trigger. > >> @@ -14736,6 +14743,11 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort) >> { >> while (TREE_CODE (t) == COMPONENT_REF) >> t = TREE_OPERAND (t, 0); >> + if (TREE_CODE (t) == MEM_REF) >> + { >> + t = TREE_OPERAND (t, 0); >> + STRIP_NOPS (t); >> + } > > This doesn't look correct. At least the parsing (and the spec AFAIK) > doesn't ensure that if there is ->, it must come before all the dots. > So, if one uses map (s->x.y) the above would work, but if map (s->x.y->z) or > map (s.a->b->c->d->e) is used, it wouldn't. I'd expect a single > while loop that looks through COMPONENT_REFs and MEM_REFs as they appear. > Maybe the handle_omp_array_sections_1 MEM_REF case too? > > Or do you want to have it done incrementally, start with supporting only > a single -> first before all the dots and later on add support for the rest? > > I think the 5.0 and especially 5.1 wording basically says that map clause > operand is arbitrary lvalue expression that includes array section support > too, so eventually we should just have somewhere in parsing scope a bool > whether OpenMP array sections are allowed or not, add OMP_ARRAY_REF or > similar tree code for those and after parsing the expression, ensure > array sections appear only where they can appear and for a subset of the > lvalue expressions where we have decl plus series of -> field or . field > or [ index ] or [ array section stuff ] handle those specially. > That arbitrary lvalue can certainly be done incrementally. > map (foo(123)->a.b[3]->c.d[:7]) and the like. Indeed this kind of modification is sort of "as encountered", so there are probably many cases that are not completely handled yet; it's not just the front-end, but also changes in gimplify_scan_omp_clauses(). However, I had another patch that should've plowed a bit further on this: https://gcc.gnu.org/pipermail/gcc-patches/2021-May/570075.html as well as those patch sets that Julian is working on. (our current plan is to have my sets go in first, and Julian's on top, to minimize clashing) >> if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP >> && OMP_CLAUSE_MAP_IMPLICIT (c) >> && (bitmap_bit_p (&map_head, DECL_UID (t)) >> @@ -14802,6 +14814,15 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort) >> bias) to zero here, so it is not set erroneously to the pointer >> size later on in gimplify.c. */ >> OMP_CLAUSE_SIZE (c) = size_zero_node; >> + indir_component_ref_p = false; >> + if ((ort == C_ORT_ACC || ort == C_ORT_OMP) > > Same comment about ort tests. > >> + && TREE_CODE (t) == COMPONENT_REF >> + && TREE_CODE (TREE_OPERAND (t, 0)) == MEM_REF) >> + { >> + t = TREE_OPERAND (TREE_OPERAND (t, 0), 0); >> + indir_component_ref_p = true; >> + STRIP_NOPS (t); >> + } > > Again, this can handle only a single -> > >> @@ -42330,16 +42328,10 @@ cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok, >> cclauses[C_OMP_CLAUSE_SPLIT_TARGET] = tc; >> } >> } >> - tree stmt = make_node (OMP_TARGET); >> - TREE_TYPE (stmt) = void_type_node; >> - OMP_TARGET_CLAUSES (stmt) = cclauses[C_OMP_CLAUSE_SPLIT_TARGET]; >> - c_omp_adjust_map_clauses (OMP_TARGET_CLAUSES (stmt), true); >> - OMP_TARGET_BODY (stmt) = body; >> - OMP_TARGET_COMBINED (stmt) = 1; >> - SET_EXPR_LOCATION (stmt, pragma_tok->location); >> - add_stmt (stmt); >> - pc = &OMP_TARGET_CLAUSES (stmt); >> - goto check_clauses; >> + c_omp_adjust_map_clauses (cclauses[C_OMP_CLAUSE_SPLIT_TARGET], true); >> + finish_omp_target (pragma_tok->location, >> + cclauses[C_OMP_CLAUSE_SPLIT_TARGET], body, true); > > What is the advantage of finish_omp_target. Perhaps the check_clauses label > can be renamed and more things common to both paths moved after the label if > needed, but as long as it isn't something also called during instantiation, > I find it cleaner to do it in cp_parser_omp_target at one place. > The reason for e.g. finish_omp_parallel is that it is called from both > parsing and instantiation. Originally, finish_omp_target was also meant for calling from both parsing and instantiation, but later factoring turned that part into finish_omp_target_clauses, while finish_omp_target was still kept. But having this part factored as a finish_omp_target function seems quite consistent with usual C/C++ front-end conventions. >> --- a/gcc/cp/semantics.c >> +++ b/gcc/cp/semantics.c >> @@ -4990,6 +4990,9 @@ handle_omp_array_sections_1 (tree c, tree t, vec &types, >> { >> if (error_operand_p (t)) >> return error_mark_node; >> + if ((ort == C_ORT_ACC || ort == C_ORT_OMP) > > See above about ort. > Declare simd only allows uniform, linear, aligned, simdlen, inbranch and > notinbranch clauses and none of those support array sections. Removed such conditions. >> + && TREE_CODE (t) == FIELD_DECL) >> + t = finish_non_static_data_member (t, NULL_TREE, NULL_TREE); > > handle_omp_array_sections_1 already has recent: > if (TREE_CODE (t) == FIELD_DECL > && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_AFFINITY > || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND)) > ret = finish_non_static_data_member (t, NULL_TREE, NULL_TREE); > so shouldn't that be extended to map/to/from clauses too? > And I guess we should check reduction/in_reduction/task_reduction clauses > too. The use of finish_non_static_data_member() here appears to not need to be divided between clause codes now, so the OMP_CLAUSE_AFFINITY/DEPEND test has been removed here. Also, the cases in handle_omp_array_sections_1 and finish_omp_clauses that are related to "'this' is allowed in OpenMP only in declare simd" are removed, since these restrictions are now lifted. >> @@ -9003,6 +9037,493 @@ finish_omp_construct (enum tree_code code, tree body, tree clauses) >> return add_stmt (stmt); >> } >> >> +/* Used to walk OpenMP target directive body. */ >> + >> +struct omp_target_walk_data >> +{ >> + tree current_object; >> + bool this_expr_accessed; >> + >> + hash_map ptr_members_accessed; >> + hash_set lambda_objects_accessed; >> + >> + tree current_closure; >> + hash_set closure_vars_accessed; >> + >> + hash_set local_decls; >> +}; >> + >> +static tree >> +finish_omp_target_clauses_r (tree *tp, int *walk_subtrees, void *ptr) >> +{ >> + tree t = *tp; >> + struct omp_target_walk_data *data = (struct omp_target_walk_data *) ptr; >> + tree current_object = data->current_object; >> + tree current_closure = data->current_closure; > > This is something that we'll eventually need to do e.g. for declare mapper > in all the 3 FEs, gather what variables might need to be mapped and > for all their types look up the mappers (recursively for nested types and > for all types mentioned in those declare mappers etc.) and remember that > somehow until gimplification. Probably, which will probably need more foundational plumbing to establish ways to pass that kind of information to the middle-end. > If it is only preliminary and covers might appear rather than appears, > I think it should be fine. What this routine does is ultimate, if you see > this somewhere, you say it is accessed, if you see a lambda, again, it has > to be accessed etc. I'm afraid that is unsafe though. > The IL at this point isn't folded yet, one could have sizeof (this) or other > unevaluated context appear there, or something could appear in a private clause > on some inner construct that doesn't imply an access on the outer target, etc. We'll just have to handle those cases as they come up. Right now, 'this' related constructs do not seem to be allowed on private() clause at all. > So, I think either this function would need to be more careful, especially > for nested OpenMP constructs, or can't it be done through langhooks at > gimplification time when we should know exactly what appears and what > doesn't in the body? I have added a case to ignore SIZEOF_EXPR and ALIGNOF_EXPR expressions, not sure if there are more, but usually if we map something unnecessary, it should only be inefficient but not wrong code. Further cases can be handled as we encounter them. >> + if (TREE_TYPE(t) && LAMBDA_TYPE_P (TREE_TYPE (t))) > > Formatting, missing space before (. Fixed. >> + for (hash_set::iterator i = data.closure_vars_accessed.begin (); >> + i != data.closure_vars_accessed.end (); ++i) >> + { >> + tree orig_decl = *i; >> + tree closure_expr = DECL_VALUE_EXPR (orig_decl); >> + >> + if (TREE_CODE (TREE_TYPE (orig_decl)) == POINTER_TYPE) >> + { >> + /* this-pointer is processed outside this loop. */ >> + if (operand_equal_p (closure_expr, omp_target_this_expr)) >> + continue; >> + >> + tree c = build_omp_clause (loc, OMP_CLAUSE_MAP); >> + OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALLOC); >> + OMP_CLAUSE_DECL (c) >> + = build_indirect_ref (loc, closure_expr, RO_UNARY_STAR); >> + OMP_CLAUSE_SIZE (c) = size_zero_node; >> + OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1; >> + new_clauses.safe_push (c); >> + >> + c = build_omp_clause (loc, OMP_CLAUSE_MAP); >> + OMP_CLAUSE_SET_MAP_KIND >> + (c, GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION); >> + OMP_CLAUSE_DECL (c) = closure_expr; >> + OMP_CLAUSE_SIZE (c) = size_zero_node; >> + new_clauses.safe_push (c); >> + } >> + else if (TREE_CODE (TREE_TYPE (orig_decl)) == REFERENCE_TYPE) >> + { >> + tree c = build_omp_clause (loc, OMP_CLAUSE_MAP); >> + OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO); >> + OMP_CLAUSE_DECL (c) >> + = build1 (INDIRECT_REF, >> + TREE_TYPE (TREE_TYPE (closure_expr)), >> + closure_expr); >> + OMP_CLAUSE_SIZE (c) >> + = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (closure_expr))); >> + new_clauses.safe_push (c); >> + >> + c = build_omp_clause (loc, OMP_CLAUSE_MAP); >> + OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALWAYS_POINTER); >> + OMP_CLAUSE_DECL (c) = closure_expr; >> + OMP_CLAUSE_SIZE (c) = size_zero_node; >> + new_clauses.safe_push (c); > > Is it guaranteed everything added here can't have an explicit map clause > already? I have re-checked the cases, and reorganized the code a bit. Right now all cases in this C++ front-end patch should be ensured to not create new map clauses when a user-explicit one exists. Re-tested without regressions on trunk on x86_64-linux with nvptx offloading, is this okay for trunk? Thanks, Chung-Lin 2021-11-16 Chung-Lin Tang PR middle-end/92120 gcc/cp/ChangeLog: * cp-tree.h (finish_omp_target): New declaration. (finish_omp_target_clauses): Likewise. * parser.c (cp_parser_omp_clause_map): Adjust call to cp_parser_omp_var_list_no_open to set 'allow_deref' argument to true. (cp_parser_omp_target): Factor out code, adjust into calls to new function finish_omp_target. * pt.c (tsubst_expr): Add call to finish_omp_target_clauses for OMP_TARGET case. * semantics.c (handle_omp_array_sections_1): Add handling to create 'this->member' from 'member' FIELD_DECL. Remove case of rejecting 'this' when not in declare simd. (handle_omp_array_sections): Likewise. (finish_omp_clauses): Likewise. Adjust to allow 'this[]' in OpenMP map clauses. Handle 'A->member' case in map clauses. Remove case of rejecting 'this' when not in declare simd. (struct omp_target_walk_data): New struct for walking over target-directive tree body. (finish_omp_target_clauses_r): New function for tree walk. (finish_omp_target_clauses): New function. (finish_omp_target): New function. gcc/c/ChangeLog: * c-parser.c (c_parser_omp_clause_map): Set 'allow_deref' argument in call to c_parser_omp_variable_list to 'true'. * c-typeck.c (handle_omp_array_sections_1): Add strip of MEM_REF in array base handling. (c_finish_omp_clauses): Handle 'A->member' case in map clauses. gcc/ChangeLog: * gimplify.c ("tree-hash-traits.h"): Add include. (gimplify_scan_omp_clauses): Change struct_map_to_clause to type hash_map *. Adjust struct map handling to handle cases of *A and A->B expressions. Under !DECL_P case of GOMP_CLAUSE_MAP handling, add STRIP_NOPS for indir_p case, add to struct_deref_set for map(*ptr_to_struct) cases. Add MEM_REF case when handling component_ref_p case. Add unshare_expr and gimplification when created GOMP_MAP_STRUCT is not a DECL. Add code to add firstprivate pointer for *pointer-to-struct case. (gimplify_adjust_omp_clauses): Move GOMP_MAP_STRUCT removal code for exit data directives code to earlier position. * omp-low.c (lower_omp_target): Handle GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION, and GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION map kinds. * tree-pretty-print.c (dump_omp_clause): Likewise. gcc/testsuite/ChangeLog: * gcc.dg/gomp/target-3.c: New testcase. * g++.dg/gomp/target-3.C: New testcase. * g++.dg/gomp/target-lambda-1.C: New testcase. * g++.dg/gomp/target-lambda-2.C: New testcase. * g++.dg/gomp/target-this-1.C: New testcase. * g++.dg/gomp/target-this-2.C: New testcase. * g++.dg/gomp/target-this-3.C: New testcase. * g++.dg/gomp/target-this-4.C: New testcase. * g++.dg/gomp/target-this-5.C: New testcase. * g++.dg/gomp/this-2.C: Adjust testcase. include/ChangeLog: * gomp-constants.h (enum gomp_map_kind): Add GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION, and GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION map kinds. (GOMP_MAP_POINTER_P): Include GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION. libgomp/ChangeLog: * libgomp.h (gomp_attach_pointer): Add bool parameter. * oacc-mem.c (acc_attach_async): Update call to gomp_attach_pointer. (goacc_enter_data_internal): Likewise. * target.c (gomp_map_vars_existing): Update assert condition to include GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION. (gomp_map_pointer): Add 'bool allow_zero_length_array_sections' parameter, add support for mapping a pointer with NULL target. (gomp_attach_pointer): Add 'bool allow_zero_length_array_sections' parameter, add support for attaching a pointer with NULL target. (gomp_map_vars_internal): Update calls to gomp_map_pointer and gomp_attach_pointer, add handling for GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION, and GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION cases. * testsuite/libgomp.c++/target-23.C: New testcase. * testsuite/libgomp.c++/target-lambda-1.C: New testcase. * testsuite/libgomp.c++/target-lambda-2.C: New testcase. * testsuite/libgomp.c++/target-this-1.C: New testcase. * testsuite/libgomp.c++/target-this-2.C: New testcase. * testsuite/libgomp.c++/target-this-3.C: New testcase. * testsuite/libgomp.c++/target-this-4.C: New testcase. * testsuite/libgomp.c++/target-this-5.C: New testcase.