[flang-commits] [flang] [flang][OpenMP] Avoid implicit default mapper on pointer captures (PR #184382)

Akash Banerjee via flang-commits flang-commits at lists.llvm.org
Wed Mar 4 07:03:57 PST 2026


https://github.com/TIFitis updated https://github.com/llvm/llvm-project/pull/184382

>From 3882e1b782f11a82d48c086e1a8af510d3d054df Mon Sep 17 00:00:00 2001
From: Akash Banerjee <Akash.Banerjee at amd.com>
Date: Tue, 3 Mar 2026 16:55:03 +0000
Subject: [PATCH 1/2] [flang][OpenMP] Avoid implicit default mapper on pointer
 captures

This change fixes incorrect implicit declare mapper behavior in Flang OpenMP lowering.

Issue:
Implicit default mappers were being attached/generated for pointer-based implicit captures, and also on data-motion directives. That could trigger recursive component mapping that overlaps/conflicts with explicit user mappings, causing runtime mapping failures.

Fix:
	- Skip implicit default mapper generation for implicit pointer captures (keep support for allocatables).
	- Do not auto-attach implicit mappers on target enter data, target exit data, or target update.
	- Apply the same pointer guard in the implicit target-capture lowering path.
---
 flang/lib/Lower/OpenMP/ClauseProcessor.cpp    | 21 ++++++++++++++-
 flang/lib/Lower/OpenMP/OpenMP.cpp             |  9 ++++---
 ...implicit-map-pointer-no-default-mapper.f90 | 26 +++++++++++++++++++
 .../OpenMP/target-data-skip-mapper-calls.f90  |  6 +++++
 4 files changed, 58 insertions(+), 4 deletions(-)
 create mode 100644 flang/test/Lower/OpenMP/implicit-map-pointer-no-default-mapper.f90

diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index 0ba4eddcfec2a..f618024b1162d 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -1485,9 +1485,21 @@ void ClauseProcessor::processMapObjects(
         mapperId = mlir::FlatSymbolRefAttr();
       } else if (objectTypeSpec) {
         std::string mapperIdName = getDefaultMapperID(objectTypeSpec);
+        bool isAllocOrPointer =
+            semantics::IsAllocatableOrObjectPointer(object.sym());
+        bool isPointer = semantics::IsPointer(*object.sym());
+        bool isImplicitMap =
+            (mapTypeBits & mlir::omp::ClauseMapFlags::implicit) ==
+            mlir::omp::ClauseMapFlags::implicit;
         bool needsDefaultMapper =
-            semantics::IsAllocatableOrObjectPointer(object.sym()) ||
+            isAllocOrPointer ||
             requiresImplicitDefaultDeclareMapper(*objectTypeSpec);
+        // For implicit captures, avoid synthesizing default mappers for pointer
+        // entities (which can over-map pointer payloads) and for plain
+        // non-allocatable/non-pointer entities. Keep implicit mapper support
+        // for allocatables.
+        if (isImplicitMap && (isPointer || !isAllocOrPointer))
+          needsDefaultMapper = false;
         if (!mapperIdName.empty())
           mapperId = addImplicitMapper(object, mapperIdName,
                                        /*allowGenerate=*/needsDefaultMapper);
@@ -1542,7 +1554,14 @@ bool ClauseProcessor::processMap(
     if (attachMod)
       TODO(currentLocation, "ATTACH modifier is not implemented yet");
     mlir::omp::ClauseMapFlags mapTypeBits = mlir::omp::ClauseMapFlags::none;
+    // For data-motion directives we avoid auto-attaching implicit default
+    // mappers. Deep recursive mapping there can conflict with explicit
+    // component enter/exit maps users commonly spell out.
     std::string mapperIdName = "__implicit_mapper";
+    if (directive == llvm::omp::Directive::OMPD_target_enter_data ||
+        directive == llvm::omp::Directive::OMPD_target_exit_data ||
+        directive == llvm::omp::Directive::OMPD_target_update)
+      mapperIdName.clear();
     // If the map type is specified, then process it else set the appropriate
     // default value
     Map::MapType type;
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index e2018add11206..1e27e58f47305 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2845,11 +2845,14 @@ genTargetOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
                 converter.mangleName(mapperIdName, *typeSpec->GetScope());
 
           if (!mapperIdName.empty()) {
-            bool allowImplicitMapper =
-                semantics::IsAllocatableOrObjectPointer(&sym);
+            bool isPointer = semantics::IsPointer(sym);
+            bool allowImplicitMapper = semantics::IsAllocatable(sym);
             bool hasDefaultMapper =
                 converter.getModuleOp().lookupSymbol(mapperIdName);
-            if (hasDefaultMapper || allowImplicitMapper) {
+            // Avoid attaching implicit default mappers to pointer captures.
+            // For large pointer-based derived aggregates this can over-map
+            // nested payloads and conflict with explicit enter/exit maps.
+            if (!isPointer && (hasDefaultMapper || allowImplicitMapper)) {
               if (!hasDefaultMapper) {
                 if (auto recordType = mlir::dyn_cast_or_null<fir::RecordType>(
                         converter.genType(*typeSpec)))
diff --git a/flang/test/Lower/OpenMP/implicit-map-pointer-no-default-mapper.f90 b/flang/test/Lower/OpenMP/implicit-map-pointer-no-default-mapper.f90
new file mode 100644
index 0000000000000..60a6db69acfa8
--- /dev/null
+++ b/flang/test/Lower/OpenMP/implicit-map-pointer-no-default-mapper.f90
@@ -0,0 +1,26 @@
+! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
+program p
+  type t
+    integer :: x
+  end type
+
+  type(t), pointer :: ptr
+  type(t), allocatable :: al
+  allocate(ptr, al)
+
+  !$omp target
+    ptr%x = ptr%x + 1
+    al%x = al%x + 1
+  !$omp end target
+end program
+
+! CHECK-LABEL: omp.declare_mapper @_QQFt_omp_default_mapper
+
+! CHECK-LABEL: func.func @_QQmain
+! The pointer capture should not get an implicit default mapper.
+! CHECK: %[[PTR_PTEE_MAP:.*]] = omp.map.info {{.*}}map_clauses(implicit, tofrom){{.*}} {name = ""}
+! CHECK: %[[PTR_DESC_MAP:.*]] = omp.map.info {{.*}}members(%[[PTR_PTEE_MAP]]{{.*}}){{.*}} {name = "ptr"}
+
+! The allocatable capture should still use the implicit default mapper.
+! CHECK: %[[ALLOC_PTEE_MAP:.*]] = omp.map.info {{.*}}map_clauses(implicit, tofrom){{.*}}mapper(@_QQFt_omp_default_mapper){{.*}} {name = ""}
+! CHECK: %[[ALLOC_DESC_MAP:.*]] = omp.map.info {{.*}}members(%[[ALLOC_PTEE_MAP]]{{.*}}){{.*}} {name = "al"}
diff --git a/flang/test/Lower/OpenMP/target-data-skip-mapper-calls.f90 b/flang/test/Lower/OpenMP/target-data-skip-mapper-calls.f90
index d1d441d0a7c0c..e2302873bd68e 100644
--- a/flang/test/Lower/OpenMP/target-data-skip-mapper-calls.f90
+++ b/flang/test/Lower/OpenMP/target-data-skip-mapper-calls.f90
@@ -23,8 +23,14 @@ program test
 
 subroutine f(x, y)
   integer :: x, y
+  type :: t
+    integer, pointer :: p(:)
+  end type
+  type(t) :: d
   !$omp target data map(tofrom: x, y)
   x = x + y
   !$omp end target data
+  !$omp target enter data map(to: d)
+  !$omp target exit data map(release: d)
 end subroutine
 end

>From 5805308f2690f65aa0da15ecefc7870138823aa2 Mon Sep 17 00:00:00 2001
From: Akash Banerjee <Akash.Banerjee at amd.com>
Date: Wed, 4 Mar 2026 15:03:04 +0000
Subject: [PATCH 2/2] Rename allowImplicitMapper -> isAllocatable.

---
 flang/lib/Lower/OpenMP/OpenMP.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 1e27e58f47305..92934181ece4e 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2846,13 +2846,13 @@ genTargetOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
 
           if (!mapperIdName.empty()) {
             bool isPointer = semantics::IsPointer(sym);
-            bool allowImplicitMapper = semantics::IsAllocatable(sym);
+            bool isAllocatable = semantics::IsAllocatable(sym);
             bool hasDefaultMapper =
                 converter.getModuleOp().lookupSymbol(mapperIdName);
             // Avoid attaching implicit default mappers to pointer captures.
             // For large pointer-based derived aggregates this can over-map
             // nested payloads and conflict with explicit enter/exit maps.
-            if (!isPointer && (hasDefaultMapper || allowImplicitMapper)) {
+            if (!isPointer && (hasDefaultMapper || isAllocatable)) {
               if (!hasDefaultMapper) {
                 if (auto recordType = mlir::dyn_cast_or_null<fir::RecordType>(
                         converter.genType(*typeSpec)))



More information about the flang-commits mailing list