[flang-commits] [flang] [flang][mlir] Add flang to mlir lowering for groupprivate (PR #180934)

via flang-commits flang-commits at lists.llvm.org
Wed May 6 23:23:37 PDT 2026


https://github.com/skc7 updated https://github.com/llvm/llvm-project/pull/180934

>From ba4ac2b6e1518ae8d6ca032ad50895989b5894c1 Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Wed, 11 Feb 2026 18:24:52 +0530
Subject: [PATCH 1/3] [flang][mlir] Add flang to mlir lowering for groupprivate

---
 flang/include/flang/Lower/OpenMP.h            |   1 +
 flang/lib/Lower/OpenMP/OpenMP.cpp             | 124 +++++++++++-
 flang/test/Lower/OpenMP/Todo/groupprivate.f90 |   9 -
 flang/test/Lower/OpenMP/groupprivate.f90      | 186 ++++++++++++++++++
 4 files changed, 310 insertions(+), 10 deletions(-)
 delete mode 100644 flang/test/Lower/OpenMP/Todo/groupprivate.f90
 create mode 100644 flang/test/Lower/OpenMP/groupprivate.f90

diff --git a/flang/include/flang/Lower/OpenMP.h b/flang/include/flang/Lower/OpenMP.h
index 852a120c3782c..0cc2c6287aac7 100644
--- a/flang/include/flang/Lower/OpenMP.h
+++ b/flang/include/flang/Lower/OpenMP.h
@@ -81,6 +81,7 @@ void genOpenMPSymbolProperties(AbstractConverter &converter,
                                const pft::Variable &var);
 
 void genThreadprivateOp(AbstractConverter &, const pft::Variable &);
+void genGroupprivateOp(AbstractConverter &, const pft::Variable &);
 void genDeclareTargetIntGlobal(AbstractConverter &, const pft::Variable &);
 bool isOpenMPTargetConstruct(const parser::OpenMPConstruct &);
 bool isOpenMPDeviceDeclareTarget(Fortran::lower::AbstractConverter &,
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 88d28cf94b045..50902505cae61 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -693,6 +693,89 @@ static void threadPrivatizeVars(lower::AbstractConverter &converter,
   }
 }
 
+static void groupprivatizeVars(lower::AbstractConverter &converter,
+                               lower::pft::Evaluation &eval) {
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::Location currentLocation = converter.getCurrentLocation();
+  mlir::OpBuilder::InsertionGuard guard(firOpBuilder);
+  firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());
+
+  auto module = converter.getModuleOp();
+
+  // Create a groupprivate operation for the symbol.
+  // TODO: Extract device_type from the groupprivate directive.
+  auto genGroupprivateOp = [&](const semantics::Symbol &sym) -> mlir::Value {
+    std::string globalName = converter.mangleName(sym);
+    fir::GlobalOp global = module.lookupSymbol<fir::GlobalOp>(globalName);
+    if (!global) {
+      return mlir::Value();
+    }
+
+    mlir::omp::DeclareTargetDeviceType deviceTypeEnum =
+        mlir::omp::DeclareTargetDeviceType::any;
+    mlir::omp::DeclareTargetDeviceTypeAttr deviceTypeAttr =
+        mlir::omp::DeclareTargetDeviceTypeAttr::get(firOpBuilder.getContext(),
+                                                    deviceTypeEnum);
+
+    // omp.groupprivate takes a flat symbol reference and returns
+    // the address of the per-team copy of the global variable.
+    return mlir::omp::GroupprivateOp::create(
+        firOpBuilder, currentLocation, global.resultType(), global.getSymbol(),
+        deviceTypeAttr);
+  };
+
+  llvm::SetVector<const semantics::Symbol *> groupprivateSyms;
+  converter.collectSymbolSet(eval, groupprivateSyms,
+                             semantics::Symbol::Flag::OmpGroupPrivate,
+                             /*collectSymbols=*/true,
+                             /*collectHostAssociatedSymbols=*/true);
+  std::set<semantics::SourceName> groupprivateSymNames;
+
+  // For a COMMON block, the GroupprivateOp is generated for the block itself
+  // instead of its members.
+  llvm::SetVector<const semantics::Symbol *> commonSyms;
+
+  for (std::size_t i = 0; i < groupprivateSyms.size(); i++) {
+    const semantics::Symbol *sym = groupprivateSyms[i];
+    mlir::Value symGroupprivateValue;
+    // The variable may be used more than once, and each reference has one
+    // symbol with the same name. Only do once for references of one variable.
+    if (groupprivateSymNames.find(sym->name()) != groupprivateSymNames.end())
+      continue;
+    groupprivateSymNames.insert(sym->name());
+
+    if (const semantics::Symbol *common =
+            semantics::FindCommonBlockContaining(sym->GetUltimate())) {
+      // Handle common block members: create groupprivate op for the entire
+      // common block, then compute member offset.
+      mlir::Value commonGroupprivateValue;
+      if (commonSyms.contains(common)) {
+        commonGroupprivateValue = converter.getSymbolAddress(*common);
+      } else {
+        commonGroupprivateValue = genGroupprivateOp(*common);
+        if (!commonGroupprivateValue)
+          continue;
+        converter.bindSymbol(*common, commonGroupprivateValue);
+        commonSyms.insert(common);
+      }
+      symGroupprivateValue = lower::genCommonBlockMember(
+          converter, currentLocation, sym->GetUltimate(),
+          commonGroupprivateValue, common->size());
+    } else {
+      symGroupprivateValue = genGroupprivateOp(*sym);
+    }
+
+    if (!symGroupprivateValue) {
+      continue;
+    }
+
+    fir::ExtendedValue sexv = converter.getSymbolExtendedValue(*sym);
+    fir::ExtendedValue symGroupprivateExv =
+        getExtendedValue(sexv, symGroupprivateValue);
+    converter.bindSymbol(*sym, symGroupprivateExv);
+  }
+}
+
 static mlir::Operation *setLoopVar(lower::AbstractConverter &converter,
                                    mlir::Location loc, mlir::Value indexVal,
                                    const semantics::Symbol *sym) {
@@ -1248,6 +1331,10 @@ static void createBodyOfOp(mlir::Operation &op, const OpWithBodyGenInfo &info,
     }
   }
 
+  if (info.dir == llvm::omp::Directive::OMPD_teams) {
+    groupprivatizeVars(info.converter, info.eval);
+  }
+
   if (!info.genSkeletonOnly) {
     if (ConstructQueue::const_iterator next = std::next(item);
         next != queue.end()) {
@@ -2918,6 +3005,11 @@ genTargetOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
         !symbolsWithDynamicSubstring.contains(&sym.GetUltimate()))
       return;
 
+    // Skip groupprivate symbols - they don't need to be mapped because
+    // groupprivate creates its own LDS storage.
+    if (sym.GetUltimate().test(semantics::Symbol::Flag::OmpGroupPrivate))
+      return;
+
     if (!isDuplicateMappedSymbol(sym, dsp.getAllSymbolsToPrivatize(),
                                  hasDeviceAddrSyms, mapSyms, isDevicePtrSyms)) {
       if (const auto *details =
@@ -4392,7 +4484,8 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
                    semantics::SemanticsContext &semaCtx,
                    lower::pft::Evaluation &eval,
                    const parser::OpenMPGroupprivate &directive) {
-  TODO(converter.getCurrentLocation(), "GROUPPRIVATE");
+  // The groupprivate directive is lowered when the variable is referenced
+  // inside target/teams regions.
 }
 
 static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
@@ -4806,6 +4899,9 @@ void Fortran::lower::genOpenMPSymbolProperties(
 
   if (sym.test(semantics::Symbol::Flag::OmpDeclareTarget))
     lower::genDeclareTargetIntGlobal(converter, var);
+
+  if (sym.test(semantics::Symbol::Flag::OmpGroupPrivate))
+    lower::genGroupprivateOp(converter, var);
 }
 
 void Fortran::lower::genThreadprivateOp(lower::AbstractConverter &converter,
@@ -4874,6 +4970,32 @@ void Fortran::lower::genThreadprivateOp(lower::AbstractConverter &converter,
   converter.bindSymbol(sym, symThreadprivateExv);
 }
 
+void Fortran::lower::genGroupprivateOp(lower::AbstractConverter &converter,
+                                       const lower::pft::Variable &var) {
+  const semantics::Symbol &sym = var.getSymbol();
+
+  // For common block members, the groupprivate op is generated for the entire
+  // common block in groupprivatizeVars, not for individual members here.
+  // The common block already has a global, so nothing to do here.
+  if (semantics::FindCommonBlockContaining(sym.GetUltimate()))
+    return;
+
+  // Handle non-global variables: local variables with the SAVE attribute can
+  // appear in a groupprivate directive. Promote them to fir.global so that
+  // omp.groupprivate can reference them by symbol name.
+  if (!var.isGlobal()) {
+    fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+    mlir::Location currentLocation = converter.getCurrentLocation();
+    auto module = converter.getModuleOp();
+    std::string globalName = converter.mangleName(sym);
+    if (!module.lookupSymbol<fir::GlobalOp>(globalName))
+      globalInitialization(converter, firOpBuilder, sym, var, currentLocation);
+  }
+
+  // The actual omp.groupprivate operation is created by groupprivatizeVars
+  // when entering a teams region.
+}
+
 // This function replicates threadprivate's behaviour of generating
 // an internal fir.GlobalOp for non-global variables in the main program
 // that have the implicit SAVE attribute, to simplifiy LLVM-IR and MLIR
diff --git a/flang/test/Lower/OpenMP/Todo/groupprivate.f90 b/flang/test/Lower/OpenMP/Todo/groupprivate.f90
deleted file mode 100644
index 9ad9b9382760c..0000000000000
--- a/flang/test/Lower/OpenMP/Todo/groupprivate.f90
+++ /dev/null
@@ -1,9 +0,0 @@
-!RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=60 -o - %s 2>&1 | FileCheck %s
-
-!CHECK: not yet implemented: GROUPPRIVATE
-
-module m
-implicit none
-integer :: x
-!$omp groupprivate(x)
-end module
diff --git a/flang/test/Lower/OpenMP/groupprivate.f90 b/flang/test/Lower/OpenMP/groupprivate.f90
new file mode 100644
index 0000000000000..9d3809b2a2999
--- /dev/null
+++ b/flang/test/Lower/OpenMP/groupprivate.f90
@@ -0,0 +1,186 @@
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=60 %s -o - | FileCheck %s
+
+! Test lowering of groupprivate directive to omp.groupprivate.
+
+! CHECK-DAG: fir.global common @blk_
+! CHECK-DAG: fir.global common @blka_
+! CHECK-DAG: fir.global common @blkb_
+
+! Test 1: Basic groupprivate with single module variable.
+module m
+  implicit none
+  integer, save :: x
+  !$omp groupprivate(x)
+end module
+
+! CHECK-LABEL: func.func @_QPtest_groupprivate
+! CHECK: omp.target
+! CHECK:   omp.teams
+! CHECK:     %{{.*}} = omp.groupprivate @_QMmEx device_type (any) : !fir.ref<i32>
+subroutine test_groupprivate()
+  use m
+
+  !$omp target
+    !$omp teams
+      x = 10
+    !$omp end teams
+  !$omp end target
+end subroutine
+
+! Test 2: Groupprivate with common block.
+module m2
+  implicit none
+  integer :: cb_x, cb_y
+  real :: cb_z
+  common /blk/ cb_x, cb_y, cb_z
+  !$omp groupprivate(/blk/)
+end module
+
+! CHECK-LABEL: func.func @_QPtest_common_block_groupprivate
+! CHECK: omp.target
+! CHECK:   omp.teams
+! CHECK:     %{{.*}} = omp.groupprivate @blk_ device_type (any) : !fir.ref<!fir.array<12xi8>>
+! CHECK:     fir.coordinate_of
+! CHECK:     fir.convert
+subroutine test_common_block_groupprivate()
+  use m2
+
+  !$omp target
+    !$omp teams
+      cb_x = 1
+      cb_y = 2
+      cb_z = 3.0
+    !$omp end teams
+  !$omp end target
+end subroutine
+
+! Test 3: Local SAVE variable promoted to fir.global by globalInitialization.
+! CHECK-LABEL: func.func @_QPtest_local_save_groupprivate
+! CHECK: omp.teams
+! CHECK:   %{{.*}} = omp.groupprivate @_QFtest_local_save_groupprivateElocal_x device_type (any) : !fir.ref<i32>
+subroutine test_local_save_groupprivate()
+  integer, save :: local_x
+  !$omp groupprivate(local_x)
+
+  !$omp teams
+    local_x = 42
+  !$omp end teams
+end subroutine
+
+! Test 4: Multiple groupprivate variables in same teams region.
+module m_multi
+  implicit none
+  integer, save :: gp_a
+  integer, save :: gp_b
+  real,    save :: gp_c
+  !$omp groupprivate(gp_a, gp_b, gp_c)
+end module
+
+! CHECK-LABEL: func.func @_QPtest_multiple_groupprivate
+! CHECK: omp.target
+! CHECK:   omp.teams
+! CHECK-DAG: omp.groupprivate @_QMm_multiEgp_a
+! CHECK-DAG: omp.groupprivate @_QMm_multiEgp_b
+! CHECK-DAG: omp.groupprivate @_QMm_multiEgp_c
+subroutine test_multiple_groupprivate()
+  use m_multi
+
+  !$omp target
+    !$omp teams
+      gp_a = 1
+      gp_b = 2
+      gp_c = 3.0
+    !$omp end teams
+  !$omp end target
+end subroutine
+
+! Test 5: Same variable referenced multiple times produces only one op.
+! CHECK-LABEL: func.func @_QPtest_repeated_ref_groupprivate
+! CHECK: omp.teams
+! CHECK: omp.groupprivate @_QMmEx
+! CHECK-NOT: omp.groupprivate @_QMmEx
+! CHECK: omp.terminator
+subroutine test_repeated_ref_groupprivate()
+  use m
+
+  !$omp target
+    !$omp teams
+      x = 10
+      x = x + 5
+      x = x * 2
+    !$omp end teams
+  !$omp end target
+end subroutine
+
+! Test 6: Standalone teams (no enclosing target) still triggers groupprivate.
+! CHECK-LABEL: func.func @_QPtest_standalone_teams_groupprivate
+! CHECK-NOT: omp.target
+! CHECK: omp.teams
+! CHECK:   %{{.*}} = omp.groupprivate @_QMmEx device_type (any) : !fir.ref<i32>
+subroutine test_standalone_teams_groupprivate()
+  use m
+
+  !$omp teams
+    x = 100
+  !$omp end teams
+end subroutine
+
+! Test 7: Groupprivate variable is not added to target's implicit map_entries.
+! CHECK-LABEL: func.func @_QPtest_target_skip_map_groupprivate
+! CHECK-NOT: omp.map.info {{.*}}@_QMmEx
+! CHECK: omp.target
+! CHECK:   omp.teams
+! CHECK:     omp.groupprivate @_QMmEx
+subroutine test_target_skip_map_groupprivate()
+  use m
+
+  !$omp target
+    !$omp teams
+      x = 200
+    !$omp end teams
+  !$omp end target
+end subroutine
+
+! Test 8: Groupprivate works with various element types (scalar, array, real).
+module m_types
+  implicit none
+  real(8), save :: gp_r8
+  integer, save :: gp_iarr(4)
+  !$omp groupprivate(gp_r8, gp_iarr)
+end module
+
+! CHECK-LABEL: func.func @_QPtest_types_groupprivate
+! CHECK: omp.teams
+! CHECK-DAG: omp.groupprivate @_QMm_typesEgp_r8 device_type (any) : !fir.ref<f64>
+! CHECK-DAG: omp.groupprivate @_QMm_typesEgp_iarr device_type (any) : !fir.ref<!fir.array<4xi32>>
+subroutine test_types_groupprivate()
+  use m_types
+
+  !$omp teams
+    gp_r8 = 1.0d0
+    gp_iarr(1) = 99
+  !$omp end teams
+end subroutine
+
+! Test 9: Multiple distinct common blocks each get their own omp.groupprivate.
+module m_blocks
+  implicit none
+  integer :: a1, a2
+  integer :: b1, b2
+  common /blka/ a1, a2
+  common /blkb/ b1, b2
+  !$omp groupprivate(/blka/, /blkb/)
+end module
+
+! CHECK-LABEL: func.func @_QPtest_multi_common_groupprivate
+! CHECK: omp.teams
+! CHECK-DAG: omp.groupprivate @blka_ device_type (any)
+! CHECK-DAG: omp.groupprivate @blkb_ device_type (any)
+subroutine test_multi_common_groupprivate()
+  use m_blocks
+
+  !$omp teams
+    a1 = 1
+    b1 = 2
+  !$omp end teams
+end subroutine

>From 58663713a3b4536c86dc072c948e5b982f450621 Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Mon, 4 May 2026 15:26:33 +0530
Subject: [PATCH 2/3] support device_type groupprivate lowering

---
 flang/include/flang/Lower/AbstractConverter.h | 11 ++++++++
 flang/include/flang/Lower/OpenMP.h            | 11 ++++++++
 flang/lib/Lower/Bridge.cpp                    | 11 ++++++++
 flang/lib/Lower/OpenMP/OpenMP.cpp             | 25 ++++++++++++++++---
 flang/test/Lower/OpenMP/groupprivate.f90      | 22 ++++++++++++++++
 5 files changed, 77 insertions(+), 3 deletions(-)

diff --git a/flang/include/flang/Lower/AbstractConverter.h b/flang/include/flang/Lower/AbstractConverter.h
index 1b19807bff0cf..db3ad4411508c 100644
--- a/flang/include/flang/Lower/AbstractConverter.h
+++ b/flang/include/flang/Lower/AbstractConverter.h
@@ -77,6 +77,10 @@ class StatementContext;
 
 using ExprToValueMap = llvm::DenseMap<const SomeExpr *, mlir::Value>;
 
+/// Forward declaration only; the full definition lives in
+/// `flang/Lower/OpenMP.h`.
+struct OMPGroupprivateDeviceTypeInfo;
+
 //===----------------------------------------------------------------------===//
 // AbstractConverter interface
 //===----------------------------------------------------------------------===//
@@ -401,6 +405,13 @@ class AbstractConverter {
 
   virtual mlir::StateStack &getStateStack() = 0;
 
+  /// Return the per-converter table recording the device_type clause
+  /// associated with each groupprivate symbol. Used to communicate state from
+  /// the lowering of a `!$omp groupprivate` directive to the later creation of
+  /// `omp.groupprivate` operations inside teams regions. The returned object
+  /// is fully defined in `flang/Lower/OpenMP.h`.
+  virtual OMPGroupprivateDeviceTypeInfo &getOMPGroupprivateDeviceTypeInfo() = 0;
+
 private:
   /// Options controlling lowering behavior.
   const Fortran::lower::LoweringOptions &loweringOptions;
diff --git a/flang/include/flang/Lower/OpenMP.h b/flang/include/flang/Lower/OpenMP.h
index 0cc2c6287aac7..992546024fc50 100644
--- a/flang/include/flang/Lower/OpenMP.h
+++ b/flang/include/flang/Lower/OpenMP.h
@@ -13,6 +13,7 @@
 #ifndef FORTRAN_LOWER_OPENMP_H
 #define FORTRAN_LOWER_OPENMP_H
 
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
 
 #include <cinttypes>
@@ -61,6 +62,16 @@ struct OMPDeferredDeclareTargetInfo {
   const Fortran::semantics::Symbol &sym;
 };
 
+/// Per-converter table that records the `device_type` modifier associated with
+/// each groupprivate symbol on the `!$omp groupprivate` directive that
+/// declared it. Populated when the directive is lowered and consumed when
+/// `omp.groupprivate` operations are emitted inside teams regions.
+struct OMPGroupprivateDeviceTypeInfo {
+  llvm::DenseMap<const Fortran::semantics::Symbol *,
+                 mlir::omp::DeclareTargetDeviceType>
+      map;
+};
+
 // Generate the OpenMP terminator for Operation at Location.
 mlir::Operation *genOpenMPTerminator(fir::FirOpBuilder &, mlir::Operation *,
                                      mlir::Location);
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index c5709e1cd94d4..9d8ad54e8ca88 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1444,6 +1444,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
 
   mlir::StateStack &getStateStack() override { return stateStack; }
 
+  Fortran::lower::OMPGroupprivateDeviceTypeInfo &
+  getOMPGroupprivateDeviceTypeInfo() override {
+    return ompGroupprivateDeviceTypeInfo;
+  }
+
   /// Add the symbol to the local map and return `true`. If the symbol is
   /// already in the map and \p forced is `false`, the map is not updated.
   /// Instead the value `false` is returned.
@@ -7445,6 +7450,12 @@ class FirConverter : public Fortran::lower::AbstractConverter {
   llvm::SmallVector<Fortran::lower::OMPDeferredDeclareTargetInfo>
       ompDeferredDeclareTarget;
 
+  /// Per-converter table recording the `device_type` clause for each symbol
+  /// declared in a `!$omp groupprivate` directive. Populated when the
+  /// directive is lowered and consumed when `omp.groupprivate` ops are emitted
+  /// inside teams regions.
+  Fortran::lower::OMPGroupprivateDeviceTypeInfo ompGroupprivateDeviceTypeInfo;
+
   const Fortran::lower::ExprToValueMap *exprValueOverrides{nullptr};
 
   /// Stack of derived type under construction to avoid infinite loops when
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 50902505cae61..3ca7e46f00666 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -703,7 +703,6 @@ static void groupprivatizeVars(lower::AbstractConverter &converter,
   auto module = converter.getModuleOp();
 
   // Create a groupprivate operation for the symbol.
-  // TODO: Extract device_type from the groupprivate directive.
   auto genGroupprivateOp = [&](const semantics::Symbol &sym) -> mlir::Value {
     std::string globalName = converter.mangleName(sym);
     fir::GlobalOp global = module.lookupSymbol<fir::GlobalOp>(globalName);
@@ -711,8 +710,15 @@ static void groupprivatizeVars(lower::AbstractConverter &converter,
       return mlir::Value();
     }
 
+    // Look up the device_type recorded when the !$omp groupprivate directive
+    // was lowered. Default to 'any' if no explicit device_type was given.
     mlir::omp::DeclareTargetDeviceType deviceTypeEnum =
         mlir::omp::DeclareTargetDeviceType::any;
+    const auto &deviceTypeMap =
+        converter.getOMPGroupprivateDeviceTypeInfo().map;
+    auto it = deviceTypeMap.find(&sym.GetUltimate());
+    if (it != deviceTypeMap.end())
+      deviceTypeEnum = it->second;
     mlir::omp::DeclareTargetDeviceTypeAttr deviceTypeAttr =
         mlir::omp::DeclareTargetDeviceTypeAttr::get(firOpBuilder.getContext(),
                                                     deviceTypeEnum);
@@ -4484,8 +4490,21 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
                    semantics::SemanticsContext &semaCtx,
                    lower::pft::Evaluation &eval,
                    const parser::OpenMPGroupprivate &directive) {
-  // The groupprivate directive is lowered when the variable is referenced
-  // inside target/teams regions.
+  // The omp.groupprivate operation itself is created lazily when the symbol
+  // is referenced inside a teams region (see groupprivatizeVars). Here we
+  // only extract the device_type clause (if any) and record it per-symbol so
+  // that the later op-creation can emit it on omp.groupprivate.
+  ObjectList objects = makeObjects(directive.v.Arguments(), semaCtx);
+  List<Clause> clauses = makeClauses(directive.v.Clauses(), semaCtx);
+  ClauseProcessor cp(converter, semaCtx, clauses);
+  mlir::omp::DeviceTypeClauseOps deviceTypeOps;
+  cp.processDeviceType(deviceTypeOps);
+
+  auto &deviceTypeMap = converter.getOMPGroupprivateDeviceTypeInfo().map;
+  for (const Object &obj : objects) {
+    if (const semantics::Symbol *sym = obj.sym())
+      deviceTypeMap[&sym->GetUltimate()] = deviceTypeOps.deviceType;
+  }
 }
 
 static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
diff --git a/flang/test/Lower/OpenMP/groupprivate.f90 b/flang/test/Lower/OpenMP/groupprivate.f90
index 9d3809b2a2999..b7bb4407fad30 100644
--- a/flang/test/Lower/OpenMP/groupprivate.f90
+++ b/flang/test/Lower/OpenMP/groupprivate.f90
@@ -184,3 +184,25 @@ subroutine test_multi_common_groupprivate()
     b1 = 2
   !$omp end teams
 end subroutine
+
+! Test 10: device_type(host) and device_type(nohost) clauses are honored.
+module m_dt
+  implicit none
+  integer, save :: gp_h
+  integer, save :: gp_nh
+  !$omp groupprivate(gp_h)  device_type(host)
+  !$omp groupprivate(gp_nh) device_type(nohost)
+end module
+
+! CHECK-LABEL: func.func @_QPtest_device_type_groupprivate
+! CHECK: omp.teams
+! CHECK-DAG: omp.groupprivate @_QMm_dtEgp_h device_type (host) : !fir.ref<i32>
+! CHECK-DAG: omp.groupprivate @_QMm_dtEgp_nh device_type (nohost) : !fir.ref<i32>
+subroutine test_device_type_groupprivate()
+  use m_dt
+
+  !$omp teams
+    gp_h = 1
+    gp_nh = 2
+  !$omp end teams
+end subroutine

>From c303ec1b4deea8a8d5e13be3188400c940e8e634 Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Thu, 7 May 2026 11:43:18 +0530
Subject: [PATCH 3/3] [flang][OpenMP] Replace OMPGroupprivateDeviceTypeInfo
 struct with a using alias

---
 flang/include/flang/Lower/AbstractConverter.h | 8 ++------
 flang/include/flang/Lower/OpenMP.h            | 8 +++-----
 flang/lib/Lower/OpenMP/OpenMP.cpp             | 5 ++---
 3 files changed, 7 insertions(+), 14 deletions(-)

diff --git a/flang/include/flang/Lower/AbstractConverter.h b/flang/include/flang/Lower/AbstractConverter.h
index db3ad4411508c..08b446af8c1b9 100644
--- a/flang/include/flang/Lower/AbstractConverter.h
+++ b/flang/include/flang/Lower/AbstractConverter.h
@@ -14,6 +14,7 @@
 #define FORTRAN_LOWER_ABSTRACTCONVERTER_H
 
 #include "flang/Lower/LoweringOptions.h"
+#include "flang/Lower/OpenMP.h"
 #include "flang/Lower/PFTDefs.h"
 #include "flang/Lower/StatementContext.h"
 #include "flang/Lower/Support/Utils.h"
@@ -77,10 +78,6 @@ class StatementContext;
 
 using ExprToValueMap = llvm::DenseMap<const SomeExpr *, mlir::Value>;
 
-/// Forward declaration only; the full definition lives in
-/// `flang/Lower/OpenMP.h`.
-struct OMPGroupprivateDeviceTypeInfo;
-
 //===----------------------------------------------------------------------===//
 // AbstractConverter interface
 //===----------------------------------------------------------------------===//
@@ -408,8 +405,7 @@ class AbstractConverter {
   /// Return the per-converter table recording the device_type clause
   /// associated with each groupprivate symbol. Used to communicate state from
   /// the lowering of a `!$omp groupprivate` directive to the later creation of
-  /// `omp.groupprivate` operations inside teams regions. The returned object
-  /// is fully defined in `flang/Lower/OpenMP.h`.
+  /// `omp.groupprivate` operations inside teams regions.
   virtual OMPGroupprivateDeviceTypeInfo &getOMPGroupprivateDeviceTypeInfo() = 0;
 
 private:
diff --git a/flang/include/flang/Lower/OpenMP.h b/flang/include/flang/Lower/OpenMP.h
index 992546024fc50..da1e7d55fbf15 100644
--- a/flang/include/flang/Lower/OpenMP.h
+++ b/flang/include/flang/Lower/OpenMP.h
@@ -66,11 +66,9 @@ struct OMPDeferredDeclareTargetInfo {
 /// each groupprivate symbol on the `!$omp groupprivate` directive that
 /// declared it. Populated when the directive is lowered and consumed when
 /// `omp.groupprivate` operations are emitted inside teams regions.
-struct OMPGroupprivateDeviceTypeInfo {
-  llvm::DenseMap<const Fortran::semantics::Symbol *,
-                 mlir::omp::DeclareTargetDeviceType>
-      map;
-};
+using OMPGroupprivateDeviceTypeInfo =
+    llvm::DenseMap<const Fortran::semantics::Symbol *,
+                   mlir::omp::DeclareTargetDeviceType>;
 
 // Generate the OpenMP terminator for Operation at Location.
 mlir::Operation *genOpenMPTerminator(fir::FirOpBuilder &, mlir::Operation *,
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 3ca7e46f00666..fd4a70c4461f7 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -714,8 +714,7 @@ static void groupprivatizeVars(lower::AbstractConverter &converter,
     // was lowered. Default to 'any' if no explicit device_type was given.
     mlir::omp::DeclareTargetDeviceType deviceTypeEnum =
         mlir::omp::DeclareTargetDeviceType::any;
-    const auto &deviceTypeMap =
-        converter.getOMPGroupprivateDeviceTypeInfo().map;
+    const auto &deviceTypeMap = converter.getOMPGroupprivateDeviceTypeInfo();
     auto it = deviceTypeMap.find(&sym.GetUltimate());
     if (it != deviceTypeMap.end())
       deviceTypeEnum = it->second;
@@ -4500,7 +4499,7 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
   mlir::omp::DeviceTypeClauseOps deviceTypeOps;
   cp.processDeviceType(deviceTypeOps);
 
-  auto &deviceTypeMap = converter.getOMPGroupprivateDeviceTypeInfo().map;
+  auto &deviceTypeMap = converter.getOMPGroupprivateDeviceTypeInfo();
   for (const Object &obj : objects) {
     if (const semantics::Symbol *sym = obj.sym())
       deviceTypeMap[&sym->GetUltimate()] = deviceTypeOps.deviceType;



More information about the flang-commits mailing list