[flang-commits] [flang] [Flang][OpenMP] Restrict implicit default declare mapper from applying deep-copies of pointer members (PR #197885)

via flang-commits flang-commits at lists.llvm.org
Fri May 15 02:03:22 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: agozillon

<details>
<summary>Changes</summary>

According to the OpenMP specification, only allocatables should get deep-copy behaviour inside of implicit default declare mappers. This PR restricts this behaviour. Relevant specification exert, added as a comment for a reminder:

    // "If a component of a derived type list item is a map clause list item
    // that results  from the predefined default mapper for that derived type,
    // and the component is not also an explicit list item or the array base
    // of an explicit list item on the same construct, then: if it has the
    // POINTER attribute, it is attach-INELIGIBLE. If a list item in a map
    // clause is an associated pointer that is attach-ineligible, the effect
    // of the map clause does not apply to its pointer target."

This prevents certain programs from unexpected over-mapping via pointer nesting, doesn't prevent that for allocatables, but that's OpenMP specification mandated foot shooting, so it's free game.

---
Full diff: https://github.com/llvm/llvm-project/pull/197885.diff


2 Files Affected:

- (modified) flang/lib/Utils/OpenMP.cpp (+32) 
- (added) flang/test/Lower/OpenMP/implicit-mapper-no-pointer-map.f90 (+74) 


``````````diff
diff --git a/flang/lib/Utils/OpenMP.cpp b/flang/lib/Utils/OpenMP.cpp
index 834ced682a34d..b55265845c524 100644
--- a/flang/lib/Utils/OpenMP.cpp
+++ b/flang/lib/Utils/OpenMP.cpp
@@ -228,6 +228,36 @@ mlir::FlatSymbolRefAttr getOrGenImplicitDefaultDeclareMapper(
   for (const auto &entry : llvm::enumerate(recordType.getTypeList())) {
     const auto &memberName = entry.value().first;
     const auto &memberType = entry.value().second;
+
+    // OpenMP 5.0, 5.1, 5.2: Default Map Clause
+    //
+    // "If a component of a derived type list item is a map clause list item
+    // that results  from the predefined default mapper for that derived type,
+    // and the component is not also an explicit list item or the array base
+    // of an explicit list item on the same construct, then: if it has the
+    // POINTER attribute, it is attach-INELIGIBLE. If a list item in a map
+    // clause is an associated pointer that is attach-ineligible, the effect
+    // of the map clause does not apply to its pointer target."
+    //
+    // What this comes down to is we wish to skip emitting a map inside of
+    // the implicit declare mapper generation for pointer components. As well
+    // as preventing any nested record types that are pointers from being
+    // processed further by the declare mapper infrastructure. The
+    // descriptor ("pointer") should be mapped by the containing derived
+    // type, this prevents the data ("pointee") from being mapped and
+    // processed any further. We should, however, keep an eye on if the
+    // record types mapping applying to this descriptor poses issues, or
+    // if attach-ineligible still requires an attach map to be emitted
+    // alongside the descriptor, even if the pointee has no map emitted.
+    // if either case applies, then we will need to emit the maps here and
+    // then opt out of base address expansion for these implicit declare
+    // mappers in the MapInfoFinalization pass.
+    //
+    // Notably, this caveat does not apply to allocatables. They get
+    // deep-copy semantics.
+    if (fir::isPointerType(fir::unwrapRefType(memberType)))
+      continue;
+
     mlir::FlatSymbolRefAttr mapperId;
     if (auto recType = mlir::dyn_cast<fir::RecordType>(
             fir::getFortranElementType(memberType))) {
@@ -238,6 +268,8 @@ mlir::FlatSymbolRefAttr getOrGenImplicitDefaultDeclareMapper(
           firOpBuilder, loc, recType, mapperIdName, mangler);
     }
 
+    // -  do we prevent maps of internal pointer record types with this? need to
+    // test that
     auto ref =
         getFieldRef(declareOp.getBase(), memberName, memberType, recordType);
     llvm::SmallVector<mlir::Value> bounds;
diff --git a/flang/test/Lower/OpenMP/implicit-mapper-no-pointer-map.f90 b/flang/test/Lower/OpenMP/implicit-mapper-no-pointer-map.f90
new file mode 100644
index 0000000000000..9338f9ba87888
--- /dev/null
+++ b/flang/test/Lower/OpenMP/implicit-mapper-no-pointer-map.f90
@@ -0,0 +1,74 @@
+! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
+
+! Test that pointer components (including pointers to nested record types)
+! do not have maps emitted inside implicit declare mappers, while
+! allocatable components do have maps. Tests three levels of nesting.
+
+program test_implicit_mapper_no_pointer_map
+  implicit none
+
+  type :: leaf_type
+    integer              :: leaf_val = 0
+    real(8), allocatable :: leaf_arr(:)
+    real(8), pointer     :: leaf_ptr_arr(:) => null()  ! Should NOT be mapped
+  end type leaf_type
+
+  type :: inner_type
+    integer              :: val = 0
+    real(8), allocatable :: arr(:)
+    real(8), pointer     :: ptr_arr(:) => null()          ! Should NOT be mapped
+    type(leaf_type), allocatable :: alloc_leaf            ! SHOULD be mapped with nested mapper
+    type(leaf_type), pointer     :: ptr_leaf => null()    ! Should NOT be mapped
+  end type inner_type
+
+  type :: outer_type
+    integer                       :: id = 0
+    type(inner_type), allocatable :: alloc_inner          ! SHOULD be mapped with nested mapper
+    type(inner_type), pointer     :: ptr_inner => null()  ! Should NOT be mapped
+    real(8), allocatable          :: alloc_arr(:)         ! SHOULD be mapped
+    integer, pointer              :: ptr_scalar => null() ! Should NOT be mapped
+  end type outer_type
+
+  type(outer_type), allocatable :: obj
+
+  !$omp target
+    obj%id = 1
+  !$omp end target
+
+end program test_implicit_mapper_no_pointer_map
+
+! CHECK-LABEL: omp.declare_mapper @{{.*}}leaf_type_omp_default_mapper : !fir.type<_QFTleaf_type{
+! CHECK: %[[LEAF_VAL:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref<i32>, i32){{.*}}map_clauses(implicit, tofrom)
+! CHECK: %[[LEAF_ARR_DATA:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(implicit, tofrom){{.*}}-> !fir.llvm_ptr
+! CHECK: %[[LEAF_ARR_DESC:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(always, implicit, to)
+! CHECK: %[[LEAF_ARR_ATTACH:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(attach, ref_ptr, ref_ptee)
+! CHECK: %[[LEAF_PARENT:.*]] = omp.map.info var_ptr({{.*}}!fir.type<_QFTleaf_type{{.*}}>){{.*}}members(%[[LEAF_VAL]], %[[LEAF_ARR_DESC]], %[[LEAF_ARR_DATA]] : [0], [1], [1, 0] :
+! CHECK: omp.declare_mapper.info map_entries(%[[LEAF_PARENT]], %[[LEAF_VAL]], %[[LEAF_ARR_DESC]], %[[LEAF_ARR_ATTACH]], %[[LEAF_ARR_DATA]] :
+
+! CHECK-LABEL: omp.declare_mapper @{{.*}}inner_type_omp_default_mapper : !fir.type<_QFTinner_type{
+! CHECK: %[[INNER_VAL:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref<i32>, i32){{.*}}map_clauses(implicit, tofrom)
+! CHECK: %[[INNER_ARR_DATA:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(implicit, tofrom){{.*}}-> !fir.llvm_ptr{{.*}}{name = ""}
+! CHECK: %[[INNER_ARR_DESC:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(always, implicit, to){{.*}}{name = ""}
+! CHECK: %[[INNER_ARR_ATTACH:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(attach, ref_ptr, ref_ptee){{.*}}{name = ""}
+! CHECK: %[[INNER_ALLOC_LEAF_DATA:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(implicit, tofrom){{.*}}mapper(@{{.*}}leaf_type_omp_default_mapper){{.*}}-> !fir.llvm_ptr
+! CHECK: %[[INNER_ALLOC_LEAF_DESC:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(always, implicit, to){{.*}}{name = ""}
+! CHECK: %[[INNER_ALLOC_LEAF_ATTACH:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(attach, ref_ptr, ref_ptee){{.*}}{name = ""}
+! CHECK: %[[INNER_PARENT:.*]] = omp.map.info var_ptr({{.*}}!fir.type<_QFTinner_type{{.*}}>){{.*}}members(%[[INNER_VAL]], %[[INNER_ARR_DESC]], %[[INNER_ARR_DATA]], %[[INNER_ALLOC_LEAF_DESC]], %[[INNER_ALLOC_LEAF_DATA]] : [0], [1], [1, 0], [3], [3, 0] :
+! CHECK: omp.declare_mapper.info map_entries(%[[INNER_PARENT]], %[[INNER_VAL]], %[[INNER_ARR_DESC]], %[[INNER_ALLOC_LEAF_DESC]], %[[INNER_ARR_ATTACH]], %[[INNER_ALLOC_LEAF_ATTACH]], %[[INNER_ARR_DATA]], %[[INNER_ALLOC_LEAF_DATA]] :
+
+! CHECK-LABEL: omp.declare_mapper @{{.*}}outer_type_omp_default_mapper : !fir.type<_QFTouter_type{
+! CHECK: %[[OUTER_ID:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref<i32>, i32){{.*}}map_clauses(implicit, tofrom)
+! CHECK: %[[OUTER_ALLOC_INNER_DATA:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(implicit, tofrom){{.*}}mapper(@{{.*}}inner_type_omp_default_mapper){{.*}}-> !fir.llvm_ptr
+! CHECK: %[[OUTER_ALLOC_INNER_DESC:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(always, implicit, to){{.*}}{name = ""}
+! CHECK: %[[OUTER_ALLOC_INNER_ATTACH:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(attach, ref_ptr, ref_ptee){{.*}}{name = ""}
+! CHECK: %[[OUTER_ALLOC_ARR_DATA:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(implicit, tofrom){{.*}}-> !fir.llvm_ptr{{.*}}{name = ""}
+! CHECK: %[[OUTER_ALLOC_ARR_DESC:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(always, implicit, to){{.*}}{name = ""}
+! CHECK: %[[OUTER_ALLOC_ARR_ATTACH:.*]] = omp.map.info var_ptr(%{{.*}}){{.*}}map_clauses(attach, ref_ptr, ref_ptee){{.*}}{name = ""}
+! CHECK: %[[OUTER_PARENT:.*]] = omp.map.info var_ptr({{.*}}!fir.type<_QFTouter_type{{.*}}>){{.*}}members(%[[OUTER_ID]], %[[OUTER_ALLOC_INNER_DESC]], %[[OUTER_ALLOC_INNER_DATA]], %[[OUTER_ALLOC_ARR_DESC]], %[[OUTER_ALLOC_ARR_DATA]] : [0], [1], [1, 0], [3], [3, 0] :
+! CHECK: omp.declare_mapper.info map_entries(%[[OUTER_PARENT]], %[[OUTER_ID]], %[[OUTER_ALLOC_INNER_DESC]], %[[OUTER_ALLOC_ARR_DESC]], %[[OUTER_ALLOC_INNER_ATTACH]], %[[OUTER_ALLOC_ARR_ATTACH]], %[[OUTER_ALLOC_INNER_DATA]], %[[OUTER_ALLOC_ARR_DATA]] :
+
+! CHECK-LABEL: func.func @_QQmain
+! CHECK: %[[DATA_MAP:.*]] = omp.map.info var_ptr({{.*}}){{.*}}map_clauses(implicit, tofrom){{.*}}mapper(@{{.*}}outer_type_omp_default_mapper){{.*}}-> !fir.llvm_ptr
+! CHECK: %[[DESC_MAP:.*]] = omp.map.info var_ptr({{.*}}){{.*}}map_clauses(always, implicit, to){{.*}}members(%[[DATA_MAP]] : [0] :{{.*}}){{.*}}{name = "obj"}
+! CHECK: %[[ATTACH_MAP:.*]] = omp.map.info var_ptr({{.*}}){{.*}}map_clauses(attach, ref_ptr, ref_ptee){{.*}}{name = "obj"}
+! CHECK: omp.target map_entries(%[[DESC_MAP]] -> %{{.*}}, %[[ATTACH_MAP]] -> %{{.*}}, %[[DATA_MAP]] -> %{{.*}} :

``````````

</details>


https://github.com/llvm/llvm-project/pull/197885


More information about the flang-commits mailing list