[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