[flang-commits] [flang] dfe2f8a - [Flang][OpenMP][MLIR] Materialize groupprivate for target without teams (#214316)

via flang-commits flang-commits at lists.llvm.org
Mon Aug 10 08:51:39 PDT 2026


Author: Jason Van Beusekom
Date: 2026-08-10T10:51:32-05:00
New Revision: dfe2f8aefb47239bfbd11f30906c303ef34a27af

URL: https://github.com/llvm/llvm-project/commit/dfe2f8aefb47239bfbd11f30906c303ef34a27af
DIFF: https://github.com/llvm/llvm-project/commit/dfe2f8aefb47239bfbd11f30906c303ef34a27af.diff

LOG: [Flang][OpenMP][MLIR]  Materialize groupprivate for target without teams (#214316)

Prior to this change, `groupprivate` variables were only materialized
for teams constructs. A `groupprivate` variable used inside a bare
target region was not materialized.

This commit materializes the `groupprivate` copy on the target construct
itself when target does not contain a teams construct. Making the
behavior equivalent to target teams num_teams(1).

Assisted-by: Opus 4.8

Added: 
    

Modified: 
    flang/lib/Lower/OpenMP/OpenMP.cpp
    flang/test/Lower/OpenMP/groupprivate.f90
    mlir/test/Target/LLVMIR/omptarget-groupprivate.mlir

Removed: 
    


################################################################################
diff  --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 3876799b3a081..e5ea98d491a18 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2129,7 +2129,6 @@ static void createBodyOfOp(mlir::Operation &op, const OpWithBodyGenInfo &info,
     }
   }
 
-  // TODO: groupprivate is currently only materialised for `teams` constructs.
   if (info.dir == llvm::omp::Directive::OMPD_teams)
     groupprivatizeVars(info.converter, info.eval);
 
@@ -2381,6 +2380,22 @@ static void genBodyOfTargetOp(
   // Create the insertion point after the marker.
   firOpBuilder.setInsertionPointAfter(undefMarker.getDefiningOp());
 
+  bool immediatelyNestsTeams = false;
+  if (std::next(item) != queue.end()) {
+    immediatelyNestsTeams = llvm::omp::topTeamsSet.test(std::next(item)->id);
+  } else if (lower::pft::Evaluation *nestedEval =
+                 extractOnlyOmpNestedEval(eval)) {
+    const auto &ompEval = nestedEval->get<parser::OpenMPConstruct>();
+    llvm::omp::Directive nestedDir =
+        parser::omp::GetOmpDirectiveName(ompEval).v;
+    llvm::omp::Directive firstLeafDir =
+        llvm::omp::getLeafConstructsOrSelf(nestedDir).front();
+    immediatelyNestsTeams = llvm::omp::topTeamsSet.test(firstLeafDir);
+  }
+  // No enclosing teams: materialise the copy on the target itself
+  if (!immediatelyNestsTeams)
+    groupprivatizeVars(converter, eval);
+
   if (ConstructQueue::const_iterator next = std::next(item);
       next != queue.end()) {
     genOMPDispatch(converter, symTable, semaCtx, eval, currentLocation, queue,

diff  --git a/flang/test/Lower/OpenMP/groupprivate.f90 b/flang/test/Lower/OpenMP/groupprivate.f90
index 7b32822228f92..f81896cb9ef03 100644
--- a/flang/test/Lower/OpenMP/groupprivate.f90
+++ b/flang/test/Lower/OpenMP/groupprivate.f90
@@ -274,3 +274,57 @@ module m_late
   integer, save :: gp_late
   !$omp groupprivate(gp_late) device_type(host)
 end module
+
+! Test 12: groupprivate used in a plain 'target' with no enclosing 'teams'. The
+! per-contention-group copy is materialised on the 'target' construct itself
+! CHECK-LABEL: func.func @_QPtest_target_no_teams_groupprivate
+! CHECK:         omp.target kernel_type(generic) {
+! CHECK-NOT:       omp.teams
+! CHECK:           %[[GP:.*]] = omp.groupprivate @_QMmEx device_type (any) : !fir.ref<i32>
+! CHECK:           %[[DECL:.*]]:2 = hlfir.declare %[[GP]] {uniq_name = "_QMmEx"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK:           %[[C10:.*]] = arith.constant 10 : i32
+! CHECK:           hlfir.assign %[[C10]] to %[[DECL]]#0 : i32, !fir.ref<i32>
+subroutine test_target_no_teams_groupprivate()
+  use m
+
+  !$omp target
+    x = 10
+  !$omp end target
+end subroutine
+
+! Test 13: separated 'target' with a nested 'teams'. The copy is materialised on
+! the 'teams' construct only; no omp.groupprivate is emitted directly on the
+! 'target' construct.
+! CHECK-LABEL: func.func @_QPtest_target_nested_teams_groupprivate
+! CHECK:         omp.target kernel_type(generic) {
+! CHECK-NOT:       omp.groupprivate
+! CHECK:           omp.teams {
+! CHECK:             %[[GP:.*]] = omp.groupprivate @_QMmEx device_type (any) : !fir.ref<i32>
+! CHECK:             %[[DECL:.*]]:2 = hlfir.declare %[[GP]] {uniq_name = "_QMmEx"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK-NOT:       omp.groupprivate @_QMmEx
+subroutine test_target_nested_teams_groupprivate()
+  use m
+
+  !$omp target
+    !$omp teams
+      x = 10
+    !$omp end teams
+  !$omp end target
+end subroutine
+
+! Test 14: combined 'target teams num_teams(1)'. The copy is materialised on the
+! 'teams' leaf only; no omp.groupprivate is emitted on the 'target' leaf.
+! CHECK-LABEL: func.func @_QPtest_target_teams_one_groupprivate
+! CHECK:         omp.target kernel_type(generic) host_eval(
+! CHECK-NOT:       omp.groupprivate
+! CHECK:           omp.teams
+! CHECK:             %[[GP:.*]] = omp.groupprivate @_QMmEx device_type (any) : !fir.ref<i32>
+! CHECK:             %[[DECL:.*]]:2 = hlfir.declare %[[GP]] {uniq_name = "_QMmEx"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK-NOT:       omp.groupprivate @_QMmEx
+subroutine test_target_teams_one_groupprivate()
+  use m
+
+  !$omp target teams num_teams(1)
+    x = 10
+  !$omp end target teams
+end subroutine

diff  --git a/mlir/test/Target/LLVMIR/omptarget-groupprivate.mlir b/mlir/test/Target/LLVMIR/omptarget-groupprivate.mlir
index 30065212839c1..5a640622512ed 100644
--- a/mlir/test/Target/LLVMIR/omptarget-groupprivate.mlir
+++ b/mlir/test/Target/LLVMIR/omptarget-groupprivate.mlir
@@ -22,10 +22,30 @@ module attributes {omp.is_target_device = true, llvm.target_triple = "amdgcn-amd
     }
     llvm.return
   }
+
+  // A groupprivate directive is equivalent whether it appears directly inside a
+  // 'target' region (as in @_QQmain above) or nested in a 'teams' region: both
+  // allocate a per-contention-group copy in the shared address space.
+  llvm.func @teams_equiv() {
+    %gt = llvm.mlir.addressof @global_teams : !llvm.ptr
+    %map_t = omp.map.info var_ptr(%gt : !llvm.ptr, i32) map_clauses(tofrom) capture(ByCopy) -> !llvm.ptr {name = "t"}
+    omp.target kernel_type(generic) map_entries(%map_t -> %arg1 : !llvm.ptr) {
+      %loaded = llvm.load %arg1 : !llvm.ptr -> i32
+      omp.teams {
+        %teams_gp = omp.groupprivate @global_teams device_type(any) : !llvm.ptr
+        llvm.store %loaded, %teams_gp : i32, !llvm.ptr
+        omp.terminator
+      }
+      omp.terminator
+    }
+    llvm.return
+  }
+
   llvm.mlir.global internal @global_a() : i32
   llvm.mlir.global internal @global_any() : i32
   llvm.mlir.global internal @global_host() : i32
   llvm.mlir.global internal @global_nohost() : i32
+  llvm.mlir.global internal @global_teams() : i32
 }
 
 // CHECK-DAG: @global_a = internal global i32 undef
@@ -34,8 +54,11 @@ module attributes {omp.is_target_device = true, llvm.target_triple = "amdgcn-amd
 // CHECK-DAG: @global_nohost = internal global i32 undef
 // CHECK-DAG: @[[SHARED_ANY:global_any.*]] = internal addrspace(3) global i32 poison
 // CHECK-DAG: @[[SHARED_NOHOST:global_nohost.*]] = internal addrspace(3) global i32 poison
+// CHECK-DAG: @[[SHARED_TEAMS:global_teams.*]] = internal addrspace(3) global i32 poison
 // CHECK: define {{.*}} amdgpu_kernel void @__omp_offloading_{{.*}}_{{.*}}__QQmain_{{.*}}(ptr %{{.*}}, ptr %{{.*}}) #{{[0-9]+}} {
 // CHECK:        %[[LOAD:.*]] = load i32, ptr %{{.*}}, align 4
 // CHECK-NEXT :  store i32 %[[LOAD]], ptr addrspace(3) @[[SHARED_ANY]], align 4
 // CHECK-NEXT :  store i32 %[[LOAD]], ptr @global_host, align 4
 // CHECK-NEXT :  store i32 %[[LOAD]], ptr addrspace(3) @[[SHARED_NOHOST]], align 4
+
+// CHECK: store i32 %{{.*}}, ptr addrspace(3) @[[SHARED_TEAMS]], align 4


        


More information about the flang-commits mailing list