[flang-commits] [flang] [llvm] [Flang][OpenMP] Clear close on descriptor members for box parents in USM (PR #194287)

via flang-commits flang-commits at lists.llvm.org
Sun Apr 26 22:19:25 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

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

Author: Kareem Ergawy (ergawy)

<details>
<summary>Changes</summary>

Extend the MapInfoFinalization walk introduced in #<!-- -->185330 so parent/member close consistency is enforced whenever unified_shared_memory is in effect, not only when the parent map's variable is a fir.RecordType. Allocatable (box) roots expand to member maps the same way as derived-type instances; getDescriptorMapType may add OMP_MAP_CLOSE to implicit descriptor members while the parent map does not set close, which led to bad device behavior under -fopenmp-force-usm with multiple mapped allocatables.

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


3 Files Affected:

- (modified) flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp (+6-12) 
- (modified) flang/test/Transforms/omp-map-info-finalization-usm.fir (+12-12) 
- (added) offload/test/offloading/fortran/usm-box-parent-descriptor-close.f90 (+49) 


``````````diff
diff --git a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
index bc0f96478ddf4..741d3174c29ee 100644
--- a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
+++ b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
@@ -1186,21 +1186,15 @@ class MapInfoFinalizationPass
       });
 
       func->walk([&](mlir::omp::MapInfoOp op) {
-        // If a record type is not mapped with the `close` modifier while some
-        // of its members are (e.g. descriptor maps), then in USM mode, the
-        // memory for the record will be allocated in unified memory while the
-        // the members might be allocated in device memory. This creates an
-        // inconsistent map for the record type where some of its members are
-        // allocated in different address spaces.
-        //
-        // This fixes this issue by taking a conservative approach and removing
-        // the `close` flag from members if it is not used for mapping the
-        // parent record.
+        // If a parent map is not mapped with the `close` modifier while some of
+        // its members are (e.g. implicit descriptor maps from
+        // getDescriptorMapType in USM), those members must not keep `close` —
+        // otherwise the runtime can treat unified and device placement
+        // inconsistently.
         if (op.getMembers().empty())
           return;
 
-        mlir::Type varTy = fir::unwrapRefType(op.getVarPtr().getType());
-        if (!mlir::isa<fir::RecordType>(varTy))
+        if (!moduleRequiresUSM(op->getParentOfType<mlir::ModuleOp>()))
           return;
 
         auto mapFlag = op.getMapType();
diff --git a/flang/test/Transforms/omp-map-info-finalization-usm.fir b/flang/test/Transforms/omp-map-info-finalization-usm.fir
index 5f5a0d7213719..24f08474ed1d5 100644
--- a/flang/test/Transforms/omp-map-info-finalization-usm.fir
+++ b/flang/test/Transforms/omp-map-info-finalization-usm.fir
@@ -1,24 +1,24 @@
 // RUN: fir-opt --split-input-file --omp-map-info-finalization %s | FileCheck %s
 
 // Test that the 'close' map flag is cleared from member maps if the parent map
-// (derived type) does not have the 'close' flag. This typically happens in
-// Unified Shared Memory (USM) mode where the parent is in USM (no close) but
-// members (like descriptors) might have been initially tagged with close.
-
+// does not have the 'close' flag. This typically happens in USM mode.
+// In this test, the parent is a fir.array of derived type.
 module attributes {omp.requires = #omp<clause_requires unified_shared_memory>} {
-  func.func @test_usm_close_flag_cleanup(%arg0: !fir.ref<!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>) {
-    %map = omp.map.info var_ptr(%arg0 : !fir.ref<!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>, !fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>) map_clauses(to) capture(ByRef) -> !fir.ref<!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>> {name = "parent"}
+  func.func @test_usm_close_flag_cleanup_array(%arg0: !fir.ref<!fir.array<10x!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>>) {
+    // The implicit descriptor map for the member, with 'close'
+    %member = omp.map.info var_ptr(%arg0 : !fir.ref<!fir.array<10x!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>>, !fir.array<10x!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>) map_clauses(always, close, descriptor, to) capture(ByRef) -> !fir.ref<!fir.array<10x!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>> {name = "parent.a.implicit_map"}
+
+    // The parent map, which is a fir.array, without 'close'
+    %map = omp.map.info var_ptr(%arg0 : !fir.ref<!fir.array<10x!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>>, !fir.array<10x!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>) map_clauses(to) capture(ByRef) members(%member : [0] : !fir.ref<!fir.array<10x!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>>) -> !fir.ref<!fir.array<10x!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>> {name = "parent"}
 
-    omp.target map_entries(%map -> %arg1 : !fir.ref<!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>) {
-      // Simulate usage to trigger implicit map addition
-      %1 = hlfir.designate %arg1{"a"} : (!fir.ref<!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
+    omp.target map_entries(%map -> %arg1 : !fir.ref<!fir.array<10x!fir.type<t{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>>) {
       omp.terminator
     }
     return
   }
 }
 
-// CHECK-LABEL: func.func @test_usm_close_flag_cleanup
-// CHECK: %[[MEMBER:.*]] = omp.map.info {{.*}} map_clauses(always, to) {{.*}} {name = "parent.a.implicit_map"}
-// CHECK: %[[PARENT:.*]] = omp.map.info {{.*}} map_clauses(to) {{.*}} members(%[[MEMBER]], {{.*}}) {{.*}} {name = "parent", {{.*}}}
+// CHECK-LABEL: func.func @test_usm_close_flag_cleanup_array
+// CHECK: %[[MEMBER:.*]] = omp.map.info {{.*}} map_clauses(always, to) {{.*}} {name = "parent{{.*}}implicit_map"}
+// CHECK: %[[PARENT:.*]] = omp.map.info {{.*}} map_clauses(to) {{.*}} members(%[[MEMBER]]{{.*}}) {{.*}} {name = "parent"}
 // CHECK-NOT: close
diff --git a/offload/test/offloading/fortran/usm-box-parent-descriptor-close.f90 b/offload/test/offloading/fortran/usm-box-parent-descriptor-close.f90
new file mode 100644
index 0000000000000..6bacc1b4a761b
--- /dev/null
+++ b/offload/test/offloading/fortran/usm-box-parent-descriptor-close.f90
@@ -0,0 +1,49 @@
+! Test for PR fixing close flag on descriptor members for box parents in USM
+! REQUIRES: flang, amdgpu
+! RUN: %libomptarget-compile-fortran-generic -fopenmp-force-usm
+! RUN: env LIBOMPTARGET_INFO=16 HSA_XNACK=1 %libomptarget-run-generic 2>&1 | %fcheck-generic
+
+module m
+    implicit none
+    integer :: ng
+    type :: gt
+        integer :: k
+    end type
+    type(gt), allocatable :: g(:)
+    !$omp declare target(ng, g)
+    type :: f
+        real, allocatable :: a(:)
+    end type
+end module m
+
+program r
+    use m
+    implicit none
+    integer :: i
+    type(f), target :: u(2)
+    integer :: ig
+    real, contiguous, pointer :: p(:)
+
+    ng = 1
+    allocate(g(1))
+    g(1)%k = 1
+
+    do i = 1, 2
+        allocate(u(i)%a(1), source=0.0)
+    end do
+    u(1)%a(1) = 1.0
+    u(2)%a(1) = -1.0
+
+    !$omp target enter data map(to: g, ng, u(1)%a, u(2)%a)
+
+    !$omp target teams distribute private(ig, p)
+    do ig = 1, ng
+        p(1:1) => u(2)%a(1:1)
+        p(1) = 3.14
+    end do
+    !$omp end target teams distribute
+
+    ! CHECK: PluginInterface device {{[0-9]+}} info: Launching kernel
+    ! CHECK: Result: 3.14
+    print *, "Result: ", u(2)%a(1)
+end program r

``````````

</details>


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


More information about the flang-commits mailing list