Skip to content

[OpenMP][WIP] Use ATTACH maps for array-sections/subscripts on pointers. #153683

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 76 commits into
base: main
Choose a base branch
from

Conversation

abhinavgaba
Copy link
Contributor

@abhinavgaba abhinavgaba commented Aug 14, 2025

This is the initial clang change to support using ATTACH map-type for pointer-attachment.

This includes changes from the following:

For example, for the following:

  int *p;
  #pragma omp target enter data map(p[1:10])

The following maps are now emitted by clang:

  (A)
  &p[0], &p[1], 10 * sizeof(p[1]), TO | FROM
  &p, &p[1], sizeof(p), ATTACH

Previously, the two possible maps emitted by clang were:

  (B)
  &p[0], &p[1], 10 * sizeof(p[1]), TO | FROM

  (C)
  &p, &p[1], 10 * sizeof(p[1]), TO | FROM | PTR_AND_OBJ

(B) does not perform any pointer attachment, while (C) also maps the
pointer p, both of which are incorrect.


With this change, we are using ATTACH-style maps, like (A), for cases where the expression has a base-pointer. For example:

  int *p, **pp;
  S *ps, **pps;
  ... map(p[0])
  ... map(p[10:20])
  ... map(*p)
  ... map(([20])p)
  ... map(ps->a)
  ... map(pps->p->a)
  ... map(pp[0][0])
  ... map(*(pp + 10)[0])

We also group mapping of clauses with the same base decl in the order of the increasing complexity of their base-pointers, e.g. for something like:

  S **spp;
  map(spp[0][0], spp[0][0].a), // attach-ptr: spp[0]
  map(spp[0]),                // attach-ptr: spp
  map(spp),                   // attach-ptr: N/A

We first map spp, then spp[0] then spp[0][0] and spp[0][0].a.

This allows us to also group "struct" allocation based on their attach pointers.

Cases that need handling:

  • When a class member like p is a base-pointer in a map from a member function within the same class, p is not being privatized, instead, we still try to create an implicit map of this[0:1], and access p through that, which is incorrect.
 struct S { int *p;
 void f1() {
   #pragma omp target data map(p[0:1])
      printf("%p %p\n", &p, p);
 }
  • Attach-style maps for declare mappers. That should be a separate PR.
  • use_device_addr clause does not work properly, because we don't have a proper component-list set-up for it, just one component, so we cannot find the proper attach-ptr. For use_device_addr, we should match existing maps whose attach-ptr matches the attach-ptr of the use_device_addr operand.
  • use_device_ptr handling has some issues too. Need debugging.
  • Member_of update is not working properly for groups of component-lists after the first one.
  • Other issues that haven't been found yet.

Some tests still haven't been updated. These include:

  Clang :: OpenMP/map_struct_ordering.cpp
  Clang :: OpenMP/target_data_use_device_addr_codegen.cpp
  Clang :: OpenMP/target_data_use_device_ptr_codegen.cpp
  Clang :: OpenMP/target_enter_data_codegen.cpp
  Clang :: OpenMP/target_enter_data_depend_codegen.cpp
  Clang :: OpenMP/target_exit_data_codegen.cpp
  Clang :: OpenMP/target_exit_data_depend_codegen.cpp
  Clang :: OpenMP/target_map_codegen_18c.cpp
  Clang :: OpenMP/target_map_codegen_18d.cpp
  Clang :: OpenMP/target_map_codegen_28.cpp
  Clang :: OpenMP/target_map_codegen_29.cpp
  Clang :: OpenMP/target_map_codegen_31.cpp
  Clang :: OpenMP/target_map_codegen_hold.cpp
  Clang :: OpenMP/target_map_deref_array_codegen.cpp
  Clang :: OpenMP/target_map_member_expr_codegen.cpp
  Clang :: OpenMP/target_update_codegen.cpp
  Clang :: OpenMP/target_update_depend_codegen.cpp

For the following:

```c
int *p;
  \#pragma omp target map(p[0]) //    (A)
  (void)p;

  \#pragma omp target map(p) //       (B)
  (void)p;

  \#pragma omp target map(p, p[0]) // (C)
  (void)p;

  \#pragma omp target map(p[0], p) // (D)
  (void)p;
```

For (A), the pointer `p` is predetermined `firstprivate`, so it
should be (and is) captured by-copy. However, for (B), (C), and
(D), since `p` is already listed in a `map` clause, it's not
predetermined `firstprivate`, and hence, should be captured
by-reference, like any other mapped variable.

To ensure the correct handling of (C) and (D), the following
changes were made:

1. In SemaOpenMP, we now ensure that `p` is marked to be
captured by-reference in these cases.

2. We no longer ignore `map(p)` during codegen of `target`
constructs, even if there's another map like `map(p[0])` that
would have been mapped using a PTR_AND_OBJ map.

3. For cases like (D), we now handle `map(p)` before `map(p[0])`,
so the former gets the TARGET_PARAM flag and sets the kernel
argument.
The output of the compile-and-run tests is incorrect. These will be
used for reference in future commits that resolve the issues.

Also updated the existing clang LIT test,
target_map_both_pointer_pointee_codegen.cpp, with more regions and
more narrowed-down update_cc_test_checks filters.
This patch introduces libomptarget support for the ATTACH map-type,
which can be used to implement OpenMP conditional compliant pointer attachment,
based on whether the pointer/pointee is newly mapped on a given construct.

For example, for the following:

```c
  int *p;
  #pragma omp target enter data map(p[1:10])
```

The following maps can be emitted by clang:
```
  (A)
  &p[0], &p[1], 10 * sizeof(p[1]), TO | FROM
  &p, &p[1], sizeof(p), ATTACH
```

Without this map-type, the two possible maps emitted by clang:
```
  (B)
  &p[0], &p[1], 10 * sizeof(p[1]), TO | FROM

  (C)
  &p, &p[1], 10 * sizeof(p[1]), TO | FROM | PTR_AND_OBJ
````

(B) does not perform any pointer attachment, while (C) also maps the
pointer p, which are both incorrect.

In terms of implementation, maps with the ATTACH map-type are handled after
all other maps have been processed, as it requires knowledge of which new
allocations happened as part of the construct. As per OpenMP 5.0, an
attachment should happen only when either the pointer or the pointee was
newly mapped while handling the construct.

Maps with ATTACH map-type-bit do not increase/decrease the ref-count.

With OpenMP 6.1, `attach(always/never)` can be used to force/prevent
attachment. For `attach(always)`, the compiler will insert the ALWAYS
map-type, which would let libomptarget bypass the check about one of the
pointer/pointee being new. With `attach(never)`, the ATTACH map will not
be emitted at all.

The size argument of the ATTACH map-type can specify values greater than
`sizeof(void*)` which can be used to support pointer attachment on Fortran
descriptors. Note that this also requires shadow-pointer tracking to also
support them. That has not been implemented in this patch.

This was worked upon in coordination with Ravi Narayanaswamy, who has
since retired. Happy retirement, Ravi!
This patch introduces libomptarget support for the ATTACH map-type,
which can be used to implement OpenMP conditional compliant pointer attachment,
based on whether the pointer/pointee is newly mapped on a given construct.

For example, for the following:

```c
  int *p;
  #pragma omp target enter data map(p[1:10])
```

The following maps can be emitted by clang:
```
  (A)
  &p[0], &p[1], 10 * sizeof(p[1]), TO | FROM
  &p, &p[1], sizeof(p), ATTACH
```

Without this map-type, the two possible maps emitted by clang:
```
  (B)
  &p[0], &p[1], 10 * sizeof(p[1]), TO | FROM

  (C)
  &p, &p[1], 10 * sizeof(p[1]), TO | FROM | PTR_AND_OBJ
````

(B) does not perform any pointer attachment, while (C) also maps the
pointer p, which are both incorrect.

In terms of implementation, maps with the ATTACH map-type are handled after
all other maps have been processed, as it requires knowledge of which new
allocations happened as part of the construct. As per OpenMP 5.0, an
attachment should happen only when either the pointer or the pointee was
newly mapped while handling the construct.

Maps with ATTACH map-type-bit do not increase/decrease the ref-count.

With OpenMP 6.1, `attach(always/never)` can be used to force/prevent
attachment. For `attach(always)`, the compiler will insert the ALWAYS
map-type, which would let libomptarget bypass the check about one of the
pointer/pointee being new. With `attach(never)`, the ATTACH map will not
be emitted at all.

The size argument of the ATTACH map-type can specify values greater than
`sizeof(void*)` which can be used to support pointer attachment on Fortran
descriptors. Note that this also requires shadow-pointer tracking to also
support them. That has not been implemented in this patch.

This was worked upon in coordination with Ravi Narayanaswamy, who has
since retired. Happy retirement, Ravi!
…erateInfoForCaptureFromClauseInfo.

This will be useful for future changes where we use the information
outside this function.
target-data, fix matching of use-device-ptr/addr.
@abhinavgaba abhinavgaba changed the title Use ATTACH maps for array-sections/subscripts on pointers. [OpenMP][WIP] Use ATTACH maps for array-sections/subscripts on pointers. Aug 14, 2025
Copy link

github-actions bot commented Aug 14, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions inc,c,cpp,h -- clang/include/clang/AST/OpenMPClause.h clang/include/clang/Basic/OpenMPKinds.h clang/lib/Basic/OpenMPKinds.cpp clang/lib/CodeGen/CGOpenMPRuntime.cpp clang/lib/Sema/SemaOpenMP.cpp clang/test/OpenMP/bug60602.cpp clang/test/OpenMP/copy-gaps-1.cpp clang/test/OpenMP/copy-gaps-6.cpp clang/test/OpenMP/reduction_implicit_map.cpp clang/test/OpenMP/target_data_codegen.cpp clang/test/OpenMP/target_data_map_codegen_hold.cpp clang/test/OpenMP/target_data_map_pointer_array_subscript_codegen.cpp clang/test/OpenMP/target_data_use_device_addr_codegen.cpp clang/test/OpenMP/target_data_use_device_ptr_codegen.cpp clang/test/OpenMP/target_data_use_device_ptr_if_codegen.cpp clang/test/OpenMP/target_map_array_section_no_length_codegen.cpp clang/test/OpenMP/target_map_both_pointer_pointee_codegen.cpp clang/test/OpenMP/target_map_both_pointer_pointee_codegen_global.cpp clang/test/OpenMP/target_map_codegen_18.inc clang/test/OpenMP/target_map_codegen_19.cpp clang/test/OpenMP/target_map_codegen_20.cpp clang/test/OpenMP/target_map_codegen_21.cpp clang/test/OpenMP/target_map_codegen_22.cpp clang/test/OpenMP/target_map_codegen_23.cpp clang/test/OpenMP/target_map_codegen_26.cpp clang/test/OpenMP/target_map_codegen_27.cpp clang/test/OpenMP/target_map_codegen_33.cpp clang/test/OpenMP/target_map_pointer_defalut_mapper_codegen.cpp clang/test/OpenMP/target_map_ptr_and_star_global.cpp clang/test/OpenMP/target_map_ptr_and_star_local.cpp clang/test/OpenMP/target_map_structptr_and_member_global.cpp clang/test/OpenMP/target_map_structptr_and_member_local.cpp clang/test/OpenMP/target_task_affinity_codegen.cpp llvm/include/llvm/Frontend/OpenMP/OMPConstants.h llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp offload/include/OpenMP/Mapping.h offload/include/device.h offload/include/omptarget.h offload/libomptarget/device.cpp offload/libomptarget/interface.cpp offload/libomptarget/omptarget.cpp offload/plugins-nextgen/amdgpu/src/rtl.cpp offload/plugins-nextgen/common/include/PluginInterface.h offload/plugins-nextgen/common/src/PluginInterface.cpp offload/plugins-nextgen/cuda/src/rtl.cpp offload/plugins-nextgen/host/src/rtl.cpp offload/test/mapping/map_ptr_and_star_global.c offload/test/mapping/map_ptr_and_star_local.c offload/test/mapping/map_ptr_and_subscript_global.c offload/test/mapping/map_ptr_and_subscript_local.c offload/test/mapping/map_structptr_and_member_global.c offload/test/mapping/map_structptr_and_member_local.c
View the diff from clang-format here.
diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
index c1a92970c..7f6cc22d0 100644
--- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp
+++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -8993,8 +8993,9 @@ private:
             IE->dump();
           }
         }
-        // For use_device_addr, we match an existing map clause if its attach-ptr
-        // is same as the attach-ptr of the use_device_addr clause. e.g.
+        // For use_device_addr, we match an existing map clause if its
+        // attach-ptr is same as the attach-ptr of the use_device_addr clause.
+        // e.g.
         //   map(p) use_device_addr(p)         // match
         //   map(p[1]) use_device_addr(p[0])   // match
         //   map(ps->a) use_device_addr(ps->b) // match
@@ -9762,7 +9763,8 @@ public:
                    ->isPointerType()) || /*&&
                // Check if both component lists share the same attach pointer
                // This allows struct member access through pointers when they
-               // belong to the same struct, including nested cases like ps->qs->x
+               // belong to the same struct, including nested cases like
+               ps->qs->x
                !(AttachPtr1 &&
                  AttachPtr2 &&
                  AttachPtr1 == AttachPtr2)) ||*/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is currently failing. Please ignore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is currently failing. The IR hasn't been updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please look at the comments in this example to see the maps we are emitting. It's a smaller/easier to read test.

// CHECK: @.offload_maptypes.2 = private unnamed_addr constant [4 x i64] [i64 32, i64 281474976710659, i64 281474976710659, i64 281474976711171]
// CHECK: @.offload_sizes.3 = private unnamed_addr constant [6 x i64] [i64 8, i64 8, i64 0, i64 0, i64 0, i64 4]
// CHECK: @.offload_maptypes.4 = private unnamed_addr constant [6 x i64] [i64 35, i64 16, i64 16, i64 844424930131971, i64 844424930131971, i64 844424930132483]
// &spp[0], &spp[0], 0, IMPLICIT | PARAM
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shows how we handle mapping of struct-members that have the same attach-ptr. They are allocated as a contiguous chunk.

// CHECK: @.offload_maptypes.2 = private unnamed_addr constant [4 x i64] [i64 32, i64 281474976710659, i64 281474976710659, i64 281474976711171]
// CHECK: @.offload_sizes.3 = private unnamed_addr constant [6 x i64] [i64 8, i64 8, i64 0, i64 0, i64 0, i64 4]
// CHECK: @.offload_maptypes.4 = private unnamed_addr constant [6 x i64] [i64 35, i64 16, i64 16, i64 844424930131971, i64 844424930131971, i64 844424930132483]
// &spp[0], &spp[0], 0, IMPLICIT | PARAM
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implicit map is because spp is a pointer referenced within the target construct, and neither spp, nor an expression that has spp as its attach-ptr is mapped on the construct.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants