[flang-commits] [flang] 56c9a2c - [flang][mlir] Add flang to mlir lowering for dyn_groupprivate (#180938)

via flang-commits flang-commits at lists.llvm.org
Mon Jun 1 04:36:25 PDT 2026


Author: Chaitanya
Date: 2026-06-01T17:06:20+05:30
New Revision: 56c9a2c796661fd8b2ca627ce1f39cb3e97a203f

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

LOG: [flang][mlir] Add flang to mlir lowering for dyn_groupprivate (#180938)

This PR implements the Flang frontend lowering for the
`dyn_groupprivate` clause
Changes:
- Add ClauseProcessor handling for DynGroupprivate clause
- Generate appropriate MLIR representation for dyn_groupprivate
- Add/update test cases for dyn_groupprivate lowering
- Remove TODO marker for dyn_groupprivate clause

Added: 
    flang/test/Lower/OpenMP/dyn-groupprivate-clause.f90

Modified: 
    flang/lib/Lower/OpenMP/ClauseProcessor.cpp
    flang/lib/Lower/OpenMP/ClauseProcessor.h
    flang/lib/Lower/OpenMP/OpenMP.cpp
    llvm/include/llvm/Frontend/OpenMP/ConstructDecompositionT.h

Removed: 
    flang/test/Lower/OpenMP/Todo/dyn-groupprivate-clause.f90


################################################################################
diff  --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index e2a2c8575ffc8..ddca47f4ba771 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -1472,6 +1472,74 @@ bool ClauseProcessor::processDepend(lower::SymMap &symMap,
   return findRepeatableClause<omp::clause::Depend>(process);
 }
 
+bool ClauseProcessor::processDynGroupprivate(
+    lower::StatementContext &stmtCtx,
+    mlir::omp::DynGroupprivateClauseOps &result) const {
+  using DynGroupprivate = omp::clause::DynGroupprivate;
+
+  // OpenMP 6.1 allows the `dyn_groupprivate` clause to appear more than once
+  // on the same construct (with distinct access-group modifiers). Semantics
+  // already rejects two clauses sharing the same access-group, but multiple
+  // clauses with 
diff erent access-groups are spec-legal. The current MLIR
+  // representation (`mlir::omp::DynGroupprivateClauseOps`) and the OMPIRBuilder
+  // only support a single set of modifiers + size, so reject the multi-clause
+  // form up-front.
+  unsigned count = 0;
+  parser::CharBlock duplicateSource;
+  findRepeatableClause<DynGroupprivate>(
+      [&](const DynGroupprivate &, const parser::CharBlock &source) {
+        if (++count == 2)
+          duplicateSource = source;
+      });
+  if (count > 1) {
+    TODO(converter.genLocation(duplicateSource),
+         "multiple dyn_groupprivate clauses on the same construct");
+  }
+
+  if (auto *clause = findUniqueClause<DynGroupprivate>()) {
+    fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+    mlir::MLIRContext *context = firOpBuilder.getContext();
+
+    // Process AccessGroup modifier (cgroup).
+    if (auto accessGroup =
+            std::get<std::optional<DynGroupprivate::AccessGroup>>(clause->t)) {
+      switch (*accessGroup) {
+      case DynGroupprivate::AccessGroup::Cgroup:
+        result.dynGroupprivateAccessGroup =
+            mlir::omp::AccessGroupModifierAttr::get(
+                context, mlir::omp::AccessGroupModifier::cgroup);
+        break;
+      }
+    }
+
+    // Process Fallback modifier (abort, default_mem, null).
+    if (auto fallback =
+            std::get<std::optional<DynGroupprivate::Fallback>>(clause->t)) {
+      switch (*fallback) {
+      case DynGroupprivate::Fallback::Abort:
+        result.dynGroupprivateFallback = mlir::omp::FallbackModifierAttr::get(
+            context, mlir::omp::FallbackModifier::abort);
+        break;
+      case DynGroupprivate::Fallback::Default_Mem:
+        result.dynGroupprivateFallback = mlir::omp::FallbackModifierAttr::get(
+            context, mlir::omp::FallbackModifier::default_mem);
+        break;
+      case DynGroupprivate::Fallback::Null:
+        result.dynGroupprivateFallback = mlir::omp::FallbackModifierAttr::get(
+            context, mlir::omp::FallbackModifier::null);
+        break;
+      }
+    }
+
+    // Process size expression.
+    const auto &sizeExpr = std::get<SomeExpr>(clause->t);
+    result.dynGroupprivateSize =
+        fir::getBase(converter.genExprValue(sizeExpr, stmtCtx));
+    return true;
+  }
+  return false;
+}
+
 bool ClauseProcessor::processGrainsize(
     lower::StatementContext &stmtCtx,
     mlir::omp::GrainsizeClauseOps &result) const {

diff  --git a/flang/lib/Lower/OpenMP/ClauseProcessor.h b/flang/lib/Lower/OpenMP/ClauseProcessor.h
index 1fc221b721ebf..319dbe25bf651 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.h
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.h
@@ -124,7 +124,6 @@ class ClauseProcessor {
   bool processThreadLimit(lower::StatementContext &stmtCtx,
                           mlir::omp::ThreadLimitClauseOps &result) const;
   bool processUntied(mlir::omp::UntiedClauseOps &result) const;
-
   bool processDetach(mlir::omp::DetachClauseOps &result) const;
   // 'Repeatable' clauses: They can appear multiple times in the clause list.
   bool processAffinity(mlir::omp::AffinityClauseOps &result) const;
@@ -138,6 +137,9 @@ class ClauseProcessor {
   bool processDepend(lower::SymMap &symMap, lower::StatementContext &stmtCtx,
                      mlir::omp::DependClauseOps &result) const;
   bool
+  processDynGroupprivate(lower::StatementContext &stmtCtx,
+                         mlir::omp::DynGroupprivateClauseOps &result) const;
+  bool
   processEnter(llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &result) const;
   bool processIf(omp::clause::If::DirectiveNameModifier directiveName,
                  mlir::omp::IfClauseOps &result) const;

diff  --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 266b06f353675..620eeadbc0711 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -1881,6 +1881,7 @@ genTargetClauses(lower::AbstractConverter &converter,
   cp.processDefaultMap(stmtCtx, defaultMaps);
   cp.processDepend(symTable, stmtCtx, clauseOps);
   cp.processDevice(stmtCtx, clauseOps);
+  cp.processDynGroupprivate(stmtCtx, clauseOps);
   cp.processHasDeviceAddr(stmtCtx, clauseOps, hasDeviceAddrObjects);
   if (HostEvalInfo *hostEvalInfo = getHostEvalInfoStackTop(converter)) {
     // Only process host_eval if compiling for the host device.
@@ -1893,7 +1894,6 @@ genTargetClauses(lower::AbstractConverter &converter,
                 &mapObjects);
   cp.processNowait(clauseOps);
   cp.processThreadLimit(stmtCtx, clauseOps);
-
   cp.processTODO<clause::Allocate, clause::InReduction, clause::UsesAllocators>(
       loc, llvm::omp::Directive::OMPD_target);
 
@@ -2026,6 +2026,9 @@ static void genTeamsClauses(lower::AbstractConverter &converter,
                             llvm::SmallVectorImpl<Object> &reductionObjects) {
   ClauseProcessor cp(converter, semaCtx, clauses);
   cp.processAllocate(clauseOps);
+  // TODO: Only evaluate it here if it's not host-evaluated, like num_teams and
+  // thread_limit.
+  cp.processDynGroupprivate(stmtCtx, clauseOps);
   cp.processIf(llvm::omp::Directive::OMPD_teams, clauseOps);
 
   HostEvalInfo *hostEvalInfo = getHostEvalInfoStackTop(converter);
@@ -2035,6 +2038,7 @@ static void genTeamsClauses(lower::AbstractConverter &converter,
   }
 
   cp.processReduction(loc, clauseOps, reductionObjects);
+  cp.processDynGroupprivate(stmtCtx, clauseOps);
   // TODO Support delayed privatization.
 }
 
@@ -4841,7 +4845,8 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
         !std::holds_alternative<clause::Untied>(clause.u) &&
         !std::holds_alternative<clause::TaskReduction>(clause.u) &&
         !std::holds_alternative<clause::Detach>(clause.u) &&
-        !std::holds_alternative<clause::Device>(clause.u)) {
+        !std::holds_alternative<clause::Device>(clause.u) &&
+        !std::holds_alternative<clause::DynGroupprivate>(clause.u)) {
       const common::LangOptions &options = semaCtx.langOptions();
       if (!options.OpenMPSimd) {
         std::string name =

diff  --git a/flang/test/Lower/OpenMP/Todo/dyn-groupprivate-clause.f90 b/flang/test/Lower/OpenMP/Todo/dyn-groupprivate-clause.f90
deleted file mode 100644
index e06470f772bf8..0000000000000
--- a/flang/test/Lower/OpenMP/Todo/dyn-groupprivate-clause.f90
+++ /dev/null
@@ -1,10 +0,0 @@
-!RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=61 -o - %s 2>&1 | FileCheck %s
-
-!CHECK: not yet implemented: DYN_GROUPPRIVATE clause is not implemented yet
-subroutine f00(n)
-  implicit none
-  integer :: n
-  !$omp target dyn_groupprivate(n)
-  !$omp end target
-end
-

diff  --git a/flang/test/Lower/OpenMP/dyn-groupprivate-clause.f90 b/flang/test/Lower/OpenMP/dyn-groupprivate-clause.f90
new file mode 100644
index 0000000000000..fcf8a388183f1
--- /dev/null
+++ b/flang/test/Lower/OpenMP/dyn-groupprivate-clause.f90
@@ -0,0 +1,186 @@
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=61 -o - %s 2>&1 | FileCheck %s
+
+! Lowering of the OpenMP 6.1 `dyn_groupprivate` clause on the directives that
+! currently accept it in flang.
+
+! test 0: target with bare size, no modifiers.
+! CHECK-LABEL: func.func @_QPf00
+! CHECK: omp.target dyn_groupprivate({{.*}} : i32)
+subroutine f00(n)
+  implicit none
+  integer :: n
+  !$omp target dyn_groupprivate(n)
+  !$omp end target
+end subroutine
+
+! test 1: target with cgroup-only modifier.
+! CHECK-LABEL: func.func @_QPf01
+! CHECK: omp.target dyn_groupprivate(cgroup, {{.*}} : i32)
+subroutine f01(n)
+  implicit none
+  integer :: n
+  !$omp target dyn_groupprivate(cgroup: n)
+  !$omp end target
+end subroutine
+
+! test 2: target with fallback(abort), no access-group.
+! CHECK-LABEL: func.func @_QPf02
+! CHECK: omp.target dyn_groupprivate(fallback(abort), {{.*}} : i32)
+subroutine f02(n)
+  implicit none
+  integer :: n
+  !$omp target dyn_groupprivate(fallback(abort): n)
+  !$omp end target
+end subroutine
+
+! test 3: target with fallback(default_mem), no access-group.
+! CHECK-LABEL: func.func @_QPf03
+! CHECK: omp.target dyn_groupprivate(fallback(default_mem), {{.*}} : i32)
+subroutine f03(n)
+  implicit none
+  integer :: n
+  !$omp target dyn_groupprivate(fallback(default_mem): n)
+  !$omp end target
+end subroutine
+
+! test 4: target with fallback(null), no access-group.
+! CHECK-LABEL: func.func @_QPf04
+! CHECK: omp.target dyn_groupprivate(fallback(null), {{.*}} : i32)
+subroutine f04(n)
+  implicit none
+  integer :: n
+  !$omp target dyn_groupprivate(fallback(null): n)
+  !$omp end target
+end subroutine
+
+! test 5: target with cgroup + fallback(abort).
+! CHECK-LABEL: func.func @_QPf05
+! CHECK: omp.target dyn_groupprivate(cgroup, fallback(abort), {{.*}} : i32)
+subroutine f05(n)
+  implicit none
+  integer :: n
+  !$omp target dyn_groupprivate(cgroup, fallback(abort): n)
+  !$omp end target
+end subroutine
+
+! test 6: target with cgroup + fallback(default_mem).
+! CHECK-LABEL: func.func @_QPf06
+! CHECK: omp.target dyn_groupprivate(cgroup, fallback(default_mem), {{.*}} : i32)
+subroutine f06(n)
+  implicit none
+  integer :: n
+  !$omp target dyn_groupprivate(cgroup, fallback(default_mem): n)
+  !$omp end target
+end subroutine
+
+! test 7: target with cgroup + fallback(null).
+! CHECK-LABEL: func.func @_QPf07
+! CHECK: omp.target dyn_groupprivate(cgroup, fallback(null), {{.*}} : i32)
+subroutine f07(n)
+  implicit none
+  integer :: n
+  !$omp target dyn_groupprivate(cgroup, fallback(null): n)
+  !$omp end target
+end subroutine
+
+! test 8: Constant integer literal as the size operand.
+! CHECK-LABEL: func.func @_QPf08
+! CHECK: %[[CST:.*]] = arith.constant 1024 : i32
+! CHECK: omp.target dyn_groupprivate(%[[CST]] : i32)
+subroutine f08()
+  !$omp target dyn_groupprivate(1024)
+  !$omp end target
+end subroutine
+
+! test 9: Arithmetic expression as the size operand (n*1024).
+! CHECK-LABEL: func.func @_QPf09
+! CHECK: %{{.*}} = arith.muli {{.*}} : i32
+! CHECK: omp.target dyn_groupprivate({{.*}} : i32)
+subroutine f09(n)
+  implicit none
+  integer :: n
+  !$omp target dyn_groupprivate(n*1024)
+  !$omp end target
+end subroutine
+
+! test 10: 64-bit (kind=8) integer size operand: verify the size type is propagated.
+! CHECK-LABEL: func.func @_QPf10
+! CHECK: omp.target dyn_groupprivate({{.*}} : i64)
+subroutine f10(n)
+  implicit none
+  integer(kind=8) :: n
+  !$omp target dyn_groupprivate(n)
+  !$omp end target
+end subroutine
+
+! test 11: teams with bare size, no modifiers.
+! CHECK-LABEL: func.func @_QPf11
+! CHECK: omp.teams dyn_groupprivate({{.*}} : i32)
+subroutine f11(n)
+  implicit none
+  integer :: n
+  integer :: x
+  !$omp teams dyn_groupprivate(n)
+  x = 1
+  !$omp end teams
+end subroutine
+
+! test 12: teams with cgroup-only modifier.
+! CHECK-LABEL: func.func @_QPf12
+! CHECK: omp.teams dyn_groupprivate(cgroup, {{.*}} : i32)
+subroutine f12(n)
+  implicit none
+  integer :: n
+  integer :: x
+  !$omp teams dyn_groupprivate(cgroup: n)
+  x = 1
+  !$omp end teams
+end subroutine
+
+! test 13: teams with fallback(abort), no access-group.
+! CHECK-LABEL: func.func @_QPf13
+! CHECK: omp.teams dyn_groupprivate(fallback(abort), {{.*}} : i32)
+subroutine f13(n)
+  implicit none
+  integer :: n
+  integer :: x
+  !$omp teams dyn_groupprivate(fallback(abort): n)
+  x = 1
+  !$omp end teams
+end subroutine
+
+! test 14: teams with fallback(default_mem), no access-group.
+! CHECK-LABEL: func.func @_QPf14
+! CHECK: omp.teams dyn_groupprivate(fallback(default_mem), {{.*}} : i32)
+subroutine f14(n)
+  implicit none
+  integer :: n
+  integer :: x
+  !$omp teams dyn_groupprivate(fallback(default_mem): n)
+  x = 1
+  !$omp end teams
+end subroutine
+
+! test 15: teams with cgroup + fallback(default_mem).
+! CHECK-LABEL: func.func @_QPf15
+! CHECK: omp.teams dyn_groupprivate(cgroup, fallback(default_mem), {{.*}} : i32)
+subroutine f15(n)
+  implicit none
+  integer :: n
+  integer :: x
+  !$omp teams dyn_groupprivate(cgroup, fallback(default_mem): n)
+  x = 1
+  !$omp end teams
+end subroutine
+
+! test 16: teams with cgroup + fallback(null).
+! CHECK-LABEL: func.func @_QPf16
+! CHECK: omp.teams dyn_groupprivate(cgroup, fallback(null), {{.*}} : i32)
+subroutine f16(n)
+  implicit none
+  integer :: n
+  integer :: x
+  !$omp teams dyn_groupprivate(cgroup, fallback(null): n)
+  x = 1
+  !$omp end teams
+end subroutine

diff  --git a/llvm/include/llvm/Frontend/OpenMP/ConstructDecompositionT.h b/llvm/include/llvm/Frontend/OpenMP/ConstructDecompositionT.h
index 9e49d8f6fca46..bf812860a2543 100644
--- a/llvm/include/llvm/Frontend/OpenMP/ConstructDecompositionT.h
+++ b/llvm/include/llvm/Frontend/OpenMP/ConstructDecompositionT.h
@@ -231,6 +231,9 @@ struct ConstructDecompositionT {
                    const ClauseTy *);
   bool applyClause(const tomp::clause::DefaultT<TypeTy, IdTy, ExprTy> &clause,
                    const ClauseTy *);
+  bool applyClause(
+      const tomp::clause::DynGroupprivateT<TypeTy, IdTy, ExprTy> &clause,
+      const ClauseTy *);
   bool
   applyClause(const tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy> &clause,
               const ClauseTy *);
@@ -539,6 +542,21 @@ bool ConstructDecompositionT<C, H>::applyClause(
   return true;
 }
 
+// DYN_GROUPPRIVATE
+// [6.1] dyn_groupprivate clause
+// Directives: target, teams
+//
+// The effect of the dyn_groupprivate clause is as if it is applied to the
+// outermost leaf construct that permits it.
+template <typename C, typename H>
+bool ConstructDecompositionT<C, H>::applyClause(
+    const tomp::clause::DynGroupprivateT<TypeTy, IdTy, ExprTy> &clause,
+    const ClauseTy *node) {
+  if (!applyToOutermost(node))
+    return error(node, ErrorCode::NoLeafAllowing);
+  return true;
+}
+
 // FIRSTPRIVATE
 // [5.2:112:5-7]
 // Directives: distribute, do, for, parallel, scope, sections, single, target,


        


More information about the flang-commits mailing list