[flang-commits] [flang] [flang][mlir] Add flang to mlir lowering for groupprivate (PR #180934)
via flang-commits
flang-commits at lists.llvm.org
Thu Jun 11 23:33:25 PDT 2026
https://github.com/skc7 updated https://github.com/llvm/llvm-project/pull/180934
>From 654b3a3cfd769da3f46e1522228e1389609b0a4c 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 01/12] [flang][mlir] Add flang to mlir lowering for
groupprivate
---
flang/include/flang/Lower/OpenMP.h | 1 +
flang/lib/Lower/OpenMP/OpenMP.cpp | 126 +++++++++++-
flang/test/Lower/OpenMP/Todo/groupprivate.f90 | 9 -
flang/test/Lower/OpenMP/groupprivate.f90 | 186 ++++++++++++++++++
4 files changed, 311 insertions(+), 11 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 60b7fdc0e5322..22712ee4c3bf3 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -783,6 +783,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) {
@@ -1342,6 +1425,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()) {
@@ -3030,6 +3117,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(),
hasDeviceAddrObjects, mapObjects,
isDevicePtrObjects)) {
@@ -4627,8 +4719,9 @@ genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx,
lower::pft::Evaluation &eval,
- const parser::OmpGroupprivateDirective &directive) {
- TODO(converter.getCurrentLocation(), "GROUPPRIVATE");
+ const parser::OpenMPGroupprivate &directive) {
+ // The groupprivate directive is lowered when the variable is referenced
+ // inside target/teams regions.
}
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
@@ -5393,6 +5486,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,
@@ -5461,6 +5557,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 975ad58f1e8f0d6e2de33bc7f146bc4cc642d0e7 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 02/12] 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 78f9de9c9420e..8f93a4c23659c 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1432,6 +1432,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.
@@ -7026,6 +7031,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 22712ee4c3bf3..cb8b143e68699 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -793,7 +793,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);
@@ -801,8 +800,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);
@@ -4720,8 +4726,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 2f59fe00ba7881c91aeda580f108e0f99ce94e12 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 03/12] [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 cb8b143e68699..22cd69a4b8f75 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -804,8 +804,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;
@@ -4736,7 +4735,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;
>From ed00e5c90ed6c9a43351e237dd9f083db598d613 Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Wed, 13 May 2026 15:39:16 +0530
Subject: [PATCH 04/12] NFCcode changes
---
flang/include/flang/Lower/OpenMP.h | 2 +-
flang/lib/Lower/OpenMP/OpenMP.cpp | 58 +++++++++++++++---------------
2 files changed, 30 insertions(+), 30 deletions(-)
diff --git a/flang/include/flang/Lower/OpenMP.h b/flang/include/flang/Lower/OpenMP.h
index da1e7d55fbf15..6d858202d04fa 100644
--- a/flang/include/flang/Lower/OpenMP.h
+++ b/flang/include/flang/Lower/OpenMP.h
@@ -89,8 +89,8 @@ void genOpenMPDeclarativeConstruct(AbstractConverter &,
void genOpenMPSymbolProperties(AbstractConverter &converter,
const pft::Variable &var);
-void genThreadprivateOp(AbstractConverter &, const pft::Variable &);
void genGroupprivateOp(AbstractConverter &, const pft::Variable &);
+void genThreadprivateOp(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 22cd69a4b8f75..f9ae94698c75e 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -4724,7 +4724,7 @@ genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx,
lower::pft::Evaluation &eval,
- const parser::OpenMPGroupprivate &directive) {
+ const parser::OmpGroupprivateDirective &directive) {
// 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
@@ -5499,14 +5499,40 @@ void Fortran::lower::genOpenMPSymbolProperties(
assert(var.hasSymbol() && "Expecting Symbol");
const semantics::Symbol &sym = var.getSymbol();
+ if (sym.test(semantics::Symbol::Flag::OmpGroupPrivate))
+ lower::genGroupprivateOp(converter, var);
+
if (sym.test(semantics::Symbol::Flag::OmpThreadprivate))
lower::genThreadprivateOp(converter, var);
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::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.
}
void Fortran::lower::genThreadprivateOp(lower::AbstractConverter &converter,
@@ -5575,32 +5601,6 @@ 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
>From bd5cac623a3b9d898ae8cb7c46f7b59aae645c32 Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Thu, 14 May 2026 16:43:41 +0530
Subject: [PATCH 05/12] [flang][OpenMP] Propagate groupprivate device_type via
.mod files
---
flang/include/flang/Lower/AbstractConverter.h | 7 -
flang/include/flang/Lower/OpenMP.h | 9 --
flang/include/flang/Semantics/semantics.h | 21 +++
flang/lib/Lower/Bridge.cpp | 11 --
flang/lib/Lower/OpenMP/OpenMP.cpp | 53 ++++---
flang/lib/Semantics/mod-file.cpp | 48 +++++-
flang/lib/Semantics/resolve-directives.cpp | 33 ++++
flang/lib/Semantics/semantics.cpp | 14 ++
.../Lower/OpenMP/groupprivate-modfile.f90 | 36 +++++
flang/test/Lower/OpenMP/groupprivate.f90 | 142 +++++++++++++-----
10 files changed, 286 insertions(+), 88 deletions(-)
create mode 100644 flang/test/Lower/OpenMP/groupprivate-modfile.f90
diff --git a/flang/include/flang/Lower/AbstractConverter.h b/flang/include/flang/Lower/AbstractConverter.h
index 08b446af8c1b9..1b19807bff0cf 100644
--- a/flang/include/flang/Lower/AbstractConverter.h
+++ b/flang/include/flang/Lower/AbstractConverter.h
@@ -14,7 +14,6 @@
#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"
@@ -402,12 +401,6 @@ 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.
- 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 6d858202d04fa..882ebad9071bc 100644
--- a/flang/include/flang/Lower/OpenMP.h
+++ b/flang/include/flang/Lower/OpenMP.h
@@ -13,7 +13,6 @@
#ifndef FORTRAN_LOWER_OPENMP_H
#define FORTRAN_LOWER_OPENMP_H
-#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include <cinttypes>
@@ -62,14 +61,6 @@ 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.
-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 *,
mlir::Location);
diff --git a/flang/include/flang/Semantics/semantics.h b/flang/include/flang/Semantics/semantics.h
index 5e98378fc49be..120c927b6b6f2 100644
--- a/flang/include/flang/Semantics/semantics.h
+++ b/flang/include/flang/Semantics/semantics.h
@@ -20,6 +20,7 @@
#include "flang/Support/Fortran-features.h"
#include "flang/Support/LangOptions.h"
#include <iosfwd>
+#include <optional>
#include <set>
#include <string>
#include <vector>
@@ -57,6 +58,11 @@ class Symbol;
class CommonBlockMap;
using CommonBlockList = std::vector<std::pair<SymbolRef, std::size_t>>;
+// `device_type` modifier values that can appear on `!$omp groupprivate`.
+// Kept as a semantics-layer enum so this header does not depend on MLIR.
+// The lowering layer converts to mlir::omp::DeclareTargetDeviceType.
+enum class OmpGroupprivateDeviceType { Any, Host, Nohost };
+
using ConstructNode = std::variant<const parser::AssociateConstruct *,
const parser::BlockConstruct *, const parser::CaseConstruct *,
const parser::ChangeTeamConstruct *, const parser::CriticalConstruct *,
@@ -348,6 +354,19 @@ class SemanticsContext {
return accObjectDuplicates_.count(o) != 0;
}
+ // Record the `device_type` modifier observed on a `!$omp groupprivate`
+ // directive declaring \p symbol. Called during semantic analysis so the
+ // value is available before any OpenMP lowering, removing the dependency on
+ // lowering order between the directive's owning unit and any teams region
+ // that references the symbol.
+ void SetOmpGroupprivateDeviceType(const Symbol &, OmpGroupprivateDeviceType);
+ // Look up the recorded `device_type` modifier for \p symbol. Returns
+ // std::nullopt for symbols not flagged as groupprivate (or flagged before
+ // this mechanism was populated), in which case the caller should default to
+ // OmpGroupprivateDeviceType::Any per the OpenMP spec.
+ std::optional<OmpGroupprivateDeviceType> GetOmpGroupprivateDeviceType(
+ const Symbol &) const;
+
void DumpSymbols(llvm::raw_ostream &);
// Top-level ProgramTrees are owned by the SemanticsContext for persistence.
@@ -409,6 +428,8 @@ class SemanticsContext {
UnorderedSymbolSet isUsed_;
std::set<const parser::AccObject *> accObjectDuplicates_;
std::list<ProgramTree> programTrees_;
+ std::map<SymbolRef, OmpGroupprivateDeviceType, SymbolAddressCompare>
+ ompGroupprivateDeviceTypes_;
};
class Semantics {
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 8f93a4c23659c..78f9de9c9420e 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1432,11 +1432,6 @@ 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.
@@ -7031,12 +7026,6 @@ 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 f9ae94698c75e..ebc32254e44d6 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -783,7 +783,24 @@ static void threadPrivatizeVars(lower::AbstractConverter &converter,
}
}
+// Translate a semantics-layer groupprivate device_type to the MLIR enum used
+// by omp.groupprivate. Kept here (rather than in semantics) to avoid making
+// the semantics layer depend on MLIR.
+static mlir::omp::DeclareTargetDeviceType
+toMLIRDeclareTargetDeviceType(semantics::OmpGroupprivateDeviceType deviceType) {
+ switch (deviceType) {
+ case semantics::OmpGroupprivateDeviceType::Any:
+ return mlir::omp::DeclareTargetDeviceType::any;
+ case semantics::OmpGroupprivateDeviceType::Host:
+ return mlir::omp::DeclareTargetDeviceType::host;
+ case semantics::OmpGroupprivateDeviceType::Nohost:
+ return mlir::omp::DeclareTargetDeviceType::nohost;
+ }
+ llvm_unreachable("invalid OmpGroupprivateDeviceType");
+}
+
static void groupprivatizeVars(lower::AbstractConverter &converter,
+ semantics::SemanticsContext &semaCtx,
lower::pft::Evaluation &eval) {
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
mlir::Location currentLocation = converter.getCurrentLocation();
@@ -800,14 +817,16 @@ 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.
+ // The device_type modifier was recorded on the symbol during semantic
+ // analysis. Default to 'any' when the directive omitted a device_type
+ // clause (per OpenMP spec) or the symbol was flagged before this
+ // mechanism became available (e.g. a stale module file).
mlir::omp::DeclareTargetDeviceType deviceTypeEnum =
mlir::omp::DeclareTargetDeviceType::any;
- const auto &deviceTypeMap = converter.getOMPGroupprivateDeviceTypeInfo();
- auto it = deviceTypeMap.find(&sym.GetUltimate());
- if (it != deviceTypeMap.end())
- deviceTypeEnum = it->second;
+ if (auto recorded =
+ semaCtx.GetOmpGroupprivateDeviceType(sym.GetUltimate())) {
+ deviceTypeEnum = toMLIRDeclareTargetDeviceType(*recorded);
+ }
mlir::omp::DeclareTargetDeviceTypeAttr deviceTypeAttr =
mlir::omp::DeclareTargetDeviceTypeAttr::get(firOpBuilder.getContext(),
deviceTypeEnum);
@@ -1431,7 +1450,7 @@ static void createBodyOfOp(mlir::Operation &op, const OpWithBodyGenInfo &info,
}
if (info.dir == llvm::omp::Directive::OMPD_teams) {
- groupprivatizeVars(info.converter, info.eval);
+ groupprivatizeVars(info.converter, info.semaCtx, info.eval);
}
if (!info.genSkeletonOnly) {
@@ -4725,21 +4744,11 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx,
lower::pft::Evaluation &eval,
const parser::OmpGroupprivateDirective &directive) {
- // 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();
- for (const Object &obj : objects) {
- if (const semantics::Symbol *sym = obj.sym())
- deviceTypeMap[&sym->GetUltimate()] = deviceTypeOps.deviceType;
- }
+ // The OmpGroupPrivate symbol flag and any device_type modifier are
+ // recorded during semantic analysis (see OmpAttributeVisitor::Pre on
+ // OmpGroupprivateDirective). The omp.groupprivate operation itself is
+ // materialised on first use inside a teams region by groupprivatizeVars,
+ // so this declarative directive has no remaining lowering work to do.
}
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index 2865d16c68bb8..6c68cae5894e9 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -62,6 +62,8 @@ static void PutShapeSpec(llvm::raw_ostream &, const ShapeSpec &);
static void PutShape(
llvm::raw_ostream &, const ArraySpec &, char open, char close);
static void PutMapper(llvm::raw_ostream &, const Symbol &, SemanticsContext &);
+static void PutOmpGroupprivateDirective(
+ llvm::raw_ostream &, const Symbol &, SemanticsContext &);
static llvm::raw_ostream &PutAttr(llvm::raw_ostream &, Attr);
static llvm::raw_ostream &PutType(llvm::raw_ostream &, const DeclTypeSpec &);
@@ -590,6 +592,10 @@ void ModFileWriter::PutSymbol(
x.isExplicitBindName(), ""s);
decls_ << "::/" << symbol.name() << "/\n";
}
+ // Common-block symbols are processed by this branch (not the
+ // generic catch-all below), so emit any per-symbol OpenMP
+ // directives - e.g. `!$omp groupprivate(/blk/)` - here too.
+ PutOmpGroupprivateDirective(decls_, symbol, context_);
},
[](const HostAssocDetails &) {},
[](const MiscDetails &) {},
@@ -1313,15 +1319,53 @@ void PutOpenACCDirective(llvm::raw_ostream &os, const Symbol &symbol) {
}
}
-void PutOpenMPDirective(llvm::raw_ostream &os, const Symbol &symbol) {
+// Re-emit `!$omp groupprivate` for symbols flagged during the module's own
+// semantic analysis, including the device_type modifier if one was recorded
+// (otherwise omit it so the default `any` semantics apply). This lets a TU
+// that `use`s this module recover the directive's information from the .mod
+// file rather than the original source - mirroring how `requires` is
+// propagated. Handles both ordinary variables and common-block names (which
+// must be wrapped in slashes when reparsed).
+static void PutOmpGroupprivateDirective(
+ llvm::raw_ostream &os, const Symbol &symbol, SemanticsContext &context) {
+ if (!symbol.test(Symbol::Flag::OmpGroupPrivate)) {
+ return;
+ }
+ os << "!$omp groupprivate(";
+ if (symbol.detailsIf<CommonBlockDetails>()) {
+ os << '/' << symbol.name() << '/';
+ } else {
+ os << symbol.name();
+ }
+ os << ")";
+ if (auto deviceType{
+ context.GetOmpGroupprivateDeviceType(symbol.GetUltimate())}) {
+ switch (*deviceType) {
+ case OmpGroupprivateDeviceType::Any:
+ // Default - emit nothing.
+ break;
+ case OmpGroupprivateDeviceType::Host:
+ os << " device_type(host)";
+ break;
+ case OmpGroupprivateDeviceType::Nohost:
+ os << " device_type(nohost)";
+ break;
+ }
+ }
+ os << "\n";
+}
+
+void PutOpenMPDirective(
+ llvm::raw_ostream &os, const Symbol &symbol, SemanticsContext &context) {
if (symbol.test(Symbol::Flag::OmpThreadprivate)) {
os << "!$omp threadprivate(" << symbol.name() << ")\n";
}
+ PutOmpGroupprivateDirective(os, symbol, context);
}
void ModFileWriter::PutDirective(llvm::raw_ostream &os, const Symbol &symbol) {
PutOpenACCDirective(os, symbol);
- PutOpenMPDirective(os, symbol);
+ PutOpenMPDirective(os, symbol, context_);
}
struct Temp {
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 5d72698b0250d..07bc39a9340e8 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2191,6 +2191,39 @@ bool OmpAttributeVisitor::Pre(const parser::OmpGroupprivateDirective &x) {
ResolveOmpObject(*object, Symbol::Flag::OmpGroupPrivate);
}
}
+
+ // OpenMP 6.0 allows an optional device_type clause on the groupprivate
+ // directive. Decode it once here (defaulting to `any` per the spec) so the
+ // value can be attached to every object listed by the directive.
+ OmpGroupprivateDeviceType deviceType{OmpGroupprivateDeviceType::Any};
+ for (const parser::OmpClause &clause : x.v.Clauses().v) {
+ if (const auto *dt{std::get_if<parser::OmpClause::DeviceType>(&clause.u)}) {
+ switch (dt->v.v) {
+ case parser::OmpDeviceTypeClause::DeviceTypeDescription::Any:
+ deviceType = OmpGroupprivateDeviceType::Any;
+ break;
+ case parser::OmpDeviceTypeClause::DeviceTypeDescription::Host:
+ deviceType = OmpGroupprivateDeviceType::Host;
+ break;
+ case parser::OmpDeviceTypeClause::DeviceTypeDescription::Nohost:
+ deviceType = OmpGroupprivateDeviceType::Nohost;
+ break;
+ }
+ break;
+ }
+ }
+
+ // Record device_type against the ultimate symbol of each resolved object so
+ // it is reachable during lowering regardless of the order in which the
+ // owning unit and its referencing teams regions are lowered within this TU.
+ for (const parser::OmpArgument &arg : x.v.Arguments().v) {
+ if (const parser::OmpObject * object{parser::omp::GetArgumentObject(arg)}) {
+ if (const Symbol * sym{omp::GetObjectSymbol(*object)}) {
+ context_.SetOmpGroupprivateDeviceType(sym->GetUltimate(), deviceType);
+ }
+ }
+ }
+
return true;
}
diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index e4ac2f73c3976..cec4e67faaabc 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -440,6 +440,20 @@ void SemanticsContext::CheckError(const Symbol &symbol) {
}
}
+void SemanticsContext::SetOmpGroupprivateDeviceType(
+ const Symbol &symbol, OmpGroupprivateDeviceType deviceType) {
+ ompGroupprivateDeviceTypes_.insert_or_assign(SymbolRef{symbol}, deviceType);
+}
+
+std::optional<OmpGroupprivateDeviceType>
+SemanticsContext::GetOmpGroupprivateDeviceType(const Symbol &symbol) const {
+ auto it{ompGroupprivateDeviceTypes_.find(SymbolRef{symbol})};
+ if (it == ompGroupprivateDeviceTypes_.end()) {
+ return std::nullopt;
+ }
+ return it->second;
+}
+
bool SemanticsContext::ScopeIndexComparator::operator()(
parser::CharBlock x, parser::CharBlock y) const {
return x.begin() < y.begin() ||
diff --git a/flang/test/Lower/OpenMP/groupprivate-modfile.f90 b/flang/test/Lower/OpenMP/groupprivate-modfile.f90
new file mode 100644
index 0000000000000..cc30d7c8ddb46
--- /dev/null
+++ b/flang/test/Lower/OpenMP/groupprivate-modfile.f90
@@ -0,0 +1,36 @@
+! Cross-TU propagation of `groupprivate` and its device_type via .mod files.
+
+! RUN: rm -rf %t && split-file %s %t
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=60 -module-dir %t %t/m.f90 -o - > /dev/null
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=60 -J %t %t/use.f90 -o - | FileCheck %s
+
+! First RUN builds gp_mod.mod.
+! Second RUN lowers a consumer that only USE-associates the module
+! and checks that the omp.groupprivate op picks up device_type recovered
+! from the .mod file rather than the original source.
+
+!--- m.f90
+module gp_mod
+ implicit none
+ integer, save :: gp_x
+ !$omp groupprivate(gp_x) device_type(host)
+end module gp_mod
+
+!--- use.f90
+program use_gp_mod
+ use gp_mod
+ implicit none
+ !$omp teams
+ gp_x = 42
+ !$omp end teams
+end program use_gp_mod
+
+! CHECK-LABEL: func.func @_QQmain
+! CHECK: fir.address_of(@_QMgp_modEgp_x) : !fir.ref<i32>
+! CHECK: omp.teams {
+! CHECK: %[[GP:.*]] = omp.groupprivate @_QMgp_modEgp_x device_type (host) : !fir.ref<i32>
+! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[GP]] {uniq_name = "_QMgp_modEgp_x"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[C42:.*]] = arith.constant 42 : i32
+! CHECK: hlfir.assign %[[C42]] to %[[DECL]]#0 : i32, !fir.ref<i32>
+! CHECK: omp.terminator
+! CHECK: }
diff --git a/flang/test/Lower/OpenMP/groupprivate.f90 b/flang/test/Lower/OpenMP/groupprivate.f90
index b7bb4407fad30..f84f3f2782a69 100644
--- a/flang/test/Lower/OpenMP/groupprivate.f90
+++ b/flang/test/Lower/OpenMP/groupprivate.f90
@@ -14,9 +14,12 @@ module m
end module
! CHECK-LABEL: func.func @_QPtest_groupprivate
-! CHECK: omp.target
-! CHECK: omp.teams
-! CHECK: %{{.*}} = omp.groupprivate @_QMmEx device_type (any) : !fir.ref<i32>
+! CHECK: omp.target {
+! 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: %[[C10:.*]] = arith.constant 10 : i32
+! CHECK: hlfir.assign %[[C10]] to %[[DECL]]#0 : i32, !fir.ref<i32>
subroutine test_groupprivate()
use m
@@ -37,11 +40,15 @@ module m2
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
+! CHECK: omp.target {
+! CHECK: omp.teams {
+! CHECK: %[[GP:.*]] = omp.groupprivate @blk_ device_type (any) : !fir.ref<!fir.array<12xi8>>
+! CHECK: %[[DECL_X:.*]]:2 = hlfir.declare %{{.*}} storage(%[[GP]][0]) {uniq_name = "_QMm2Ecb_x"}
+! CHECK: %[[DECL_Y:.*]]:2 = hlfir.declare %{{.*}} storage(%[[GP]][4]) {uniq_name = "_QMm2Ecb_y"}
+! CHECK: %[[DECL_Z:.*]]:2 = hlfir.declare %{{.*}} storage(%[[GP]][8]) {uniq_name = "_QMm2Ecb_z"}
+! CHECK: hlfir.assign %{{.*}} to %[[DECL_X]]#0 : i32, !fir.ref<i32>
+! CHECK: hlfir.assign %{{.*}} to %[[DECL_Y]]#0 : i32, !fir.ref<i32>
+! CHECK: hlfir.assign %{{.*}} to %[[DECL_Z]]#0 : f32, !fir.ref<f32>
subroutine test_common_block_groupprivate()
use m2
@@ -56,8 +63,11 @@ subroutine test_common_block_groupprivate()
! 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>
+! CHECK: omp.teams {
+! CHECK: %[[GP:.*]] = omp.groupprivate @_QFtest_local_save_groupprivateElocal_x device_type (any) : !fir.ref<i32>
+! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[GP]] {uniq_name = "_QFtest_local_save_groupprivateElocal_x"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[C42:.*]] = arith.constant 42 : i32
+! CHECK: hlfir.assign %[[C42]] to %[[DECL]]#0 : i32, !fir.ref<i32>
subroutine test_local_save_groupprivate()
integer, save :: local_x
!$omp groupprivate(local_x)
@@ -77,11 +87,17 @@ module m_multi
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
+! CHECK: omp.target {
+! CHECK: omp.teams {
+! CHECK: %[[GP_A:.*]] = omp.groupprivate @_QMm_multiEgp_a device_type (any) : !fir.ref<i32>
+! CHECK: %[[DECL_A:.*]]:2 = hlfir.declare %[[GP_A]] {uniq_name = "_QMm_multiEgp_a"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[GP_B:.*]] = omp.groupprivate @_QMm_multiEgp_b device_type (any) : !fir.ref<i32>
+! CHECK: %[[DECL_B:.*]]:2 = hlfir.declare %[[GP_B]] {uniq_name = "_QMm_multiEgp_b"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[GP_C:.*]] = omp.groupprivate @_QMm_multiEgp_c device_type (any) : !fir.ref<f32>
+! CHECK: %[[DECL_C:.*]]:2 = hlfir.declare %[[GP_C]] {uniq_name = "_QMm_multiEgp_c"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
+! CHECK: hlfir.assign %{{.*}} to %[[DECL_A]]#0 : i32, !fir.ref<i32>
+! CHECK: hlfir.assign %{{.*}} to %[[DECL_B]]#0 : i32, !fir.ref<i32>
+! CHECK: hlfir.assign %{{.*}} to %[[DECL_C]]#0 : f32, !fir.ref<f32>
subroutine test_multiple_groupprivate()
use m_multi
@@ -94,12 +110,19 @@ subroutine test_multiple_groupprivate()
!$omp end target
end subroutine
-! Test 5: Same variable referenced multiple times produces only one op.
+! Test 5: Same variable referenced multiple times produces only one op, and
+! every reference accesses the per-team copy via the same hlfir.declare.
! CHECK-LABEL: func.func @_QPtest_repeated_ref_groupprivate
-! CHECK: omp.teams
-! CHECK: omp.groupprivate @_QMmEx
-! CHECK-NOT: omp.groupprivate @_QMmEx
-! CHECK: omp.terminator
+! 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
+! CHECK: hlfir.assign %{{.*}} to %[[DECL]]#0 : i32, !fir.ref<i32>
+! CHECK: %{{.*}} = fir.load %[[DECL]]#0 : !fir.ref<i32>
+! CHECK: hlfir.assign %{{.*}} to %[[DECL]]#0 : i32, !fir.ref<i32>
+! CHECK: %{{.*}} = fir.load %[[DECL]]#0 : !fir.ref<i32>
+! CHECK: hlfir.assign %{{.*}} to %[[DECL]]#0 : i32, !fir.ref<i32>
+! CHECK: omp.terminator
subroutine test_repeated_ref_groupprivate()
use m
@@ -114,9 +137,12 @@ subroutine test_repeated_ref_groupprivate()
! 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>
+! CHECK-NOT: omp.target
+! 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: %[[C100:.*]] = arith.constant 100 : i32
+! CHECK: hlfir.assign %[[C100]] to %[[DECL]]#0 : i32, !fir.ref<i32>
subroutine test_standalone_teams_groupprivate()
use m
@@ -125,12 +151,17 @@ subroutine test_standalone_teams_groupprivate()
!$omp end teams
end subroutine
-! Test 7: Groupprivate variable is not added to target's implicit map_entries.
+! Test 7: Groupprivate variable is not added to target's implicit map_entries,
+! and the access inside the teams region goes through the omp.groupprivate
+! result rather than the original host global.
! 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
+! CHECK-NOT: omp.map.info
+! CHECK: omp.target {
+! 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: %[[C200:.*]] = arith.constant 200 : i32
+! CHECK: hlfir.assign %[[C200]] to %[[DECL]]#0 : i32, !fir.ref<i32>
subroutine test_target_skip_map_groupprivate()
use m
@@ -150,9 +181,15 @@ module m_types
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>>
+! CHECK: omp.teams {
+! CHECK: %[[GP_R8:.*]] = omp.groupprivate @_QMm_typesEgp_r8 device_type (any) : !fir.ref<f64>
+! CHECK: %[[DECL_R8:.*]]:2 = hlfir.declare %[[GP_R8]] {uniq_name = "_QMm_typesEgp_r8"} : (!fir.ref<f64>) -> (!fir.ref<f64>, !fir.ref<f64>)
+! CHECK: %[[GP_IARR:.*]] = omp.groupprivate @_QMm_typesEgp_iarr device_type (any) : !fir.ref<!fir.array<4xi32>>
+! CHECK: %[[SHAPE:.*]] = fir.shape %{{.*}} : (index) -> !fir.shape<1>
+! CHECK: %[[DECL_IARR:.*]]:2 = hlfir.declare %[[GP_IARR]](%[[SHAPE]]) {uniq_name = "_QMm_typesEgp_iarr"}
+! CHECK: hlfir.assign %{{.*}} to %[[DECL_R8]]#0 : f64, !fir.ref<f64>
+! CHECK: %[[ELT:.*]] = hlfir.designate %[[DECL_IARR]]#0 (%{{.*}}) : (!fir.ref<!fir.array<4xi32>>, index) -> !fir.ref<i32>
+! CHECK: hlfir.assign %{{.*}} to %[[ELT]] : i32, !fir.ref<i32>
subroutine test_types_groupprivate()
use m_types
@@ -173,9 +210,13 @@ module m_blocks
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)
+! CHECK: omp.teams {
+! CHECK: %[[GP_A:.*]] = omp.groupprivate @blka_ device_type (any) : !fir.ref<!fir.array<8xi8>>
+! CHECK: %[[DECL_A1:.*]]:2 = hlfir.declare %{{.*}} storage(%[[GP_A]][0]) {uniq_name = "_QMm_blocksEa1"}
+! CHECK: %[[GP_B:.*]] = omp.groupprivate @blkb_ device_type (any) : !fir.ref<!fir.array<8xi8>>
+! CHECK: %[[DECL_B1:.*]]:2 = hlfir.declare %{{.*}} storage(%[[GP_B]][0]) {uniq_name = "_QMm_blocksEb1"}
+! CHECK: hlfir.assign %{{.*}} to %[[DECL_A1]]#0 : i32, !fir.ref<i32>
+! CHECK: hlfir.assign %{{.*}} to %[[DECL_B1]]#0 : i32, !fir.ref<i32>
subroutine test_multi_common_groupprivate()
use m_blocks
@@ -195,9 +236,13 @@ module m_dt
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>
+! CHECK: omp.teams {
+! CHECK: %[[GP_H:.*]] = omp.groupprivate @_QMm_dtEgp_h device_type (host) : !fir.ref<i32>
+! CHECK: %[[DECL_H:.*]]:2 = hlfir.declare %[[GP_H]] {uniq_name = "_QMm_dtEgp_h"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[GP_NH:.*]] = omp.groupprivate @_QMm_dtEgp_nh device_type (nohost) : !fir.ref<i32>
+! CHECK: %[[DECL_NH:.*]]:2 = hlfir.declare %[[GP_NH]] {uniq_name = "_QMm_dtEgp_nh"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: hlfir.assign %{{.*}} to %[[DECL_H]]#0 : i32, !fir.ref<i32>
+! CHECK: hlfir.assign %{{.*}} to %[[DECL_NH]]#0 : i32, !fir.ref<i32>
subroutine test_device_type_groupprivate()
use m_dt
@@ -206,3 +251,26 @@ subroutine test_device_type_groupprivate()
gp_nh = 2
!$omp end teams
end subroutine
+
+! Test 11: The module owning the !$omp groupprivate directive
+! is declared AFTER a subroutine that already
+! references the variable inside a teams region.
+! CHECK-LABEL: func.func @_QPtest_module_after_subroutine_groupprivate
+! CHECK: omp.teams {
+! CHECK: %[[GP:.*]] = omp.groupprivate @_QMm_lateEgp_late device_type (host) : !fir.ref<i32>
+! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[GP]] {uniq_name = "_QMm_lateEgp_late"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[C7:.*]] = arith.constant 7 : i32
+! CHECK: hlfir.assign %[[C7]] to %[[DECL]]#0 : i32, !fir.ref<i32>
+subroutine test_module_after_subroutine_groupprivate()
+ use m_late
+
+ !$omp teams
+ gp_late = 7
+ !$omp end teams
+end subroutine
+
+module m_late
+ implicit none
+ integer, save :: gp_late
+ !$omp groupprivate(gp_late) device_type(host)
+end module
>From c0bcb998f0838c56bbf36abb7243b56e2a7aa4dc Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Thu, 14 May 2026 16:59:56 +0530
Subject: [PATCH 06/12] code-format fix
---
flang/lib/Semantics/resolve-directives.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 07bc39a9340e8..891005d287eff 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2217,8 +2217,8 @@ bool OmpAttributeVisitor::Pre(const parser::OmpGroupprivateDirective &x) {
// it is reachable during lowering regardless of the order in which the
// owning unit and its referencing teams regions are lowered within this TU.
for (const parser::OmpArgument &arg : x.v.Arguments().v) {
- if (const parser::OmpObject * object{parser::omp::GetArgumentObject(arg)}) {
- if (const Symbol * sym{omp::GetObjectSymbol(*object)}) {
+ if (const parser::OmpObject *object{parser::omp::GetArgumentObject(arg)}) {
+ if (const Symbol *sym{omp::GetObjectSymbol(*object)}) {
context_.SetOmpGroupprivateDeviceType(sym->GetUltimate(), deviceType);
}
}
>From e6db954e6021eefb9ce512c92d5771d007066df1 Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Mon, 8 Jun 2026 12:35:15 +0530
Subject: [PATCH 07/12] [flang][OpenMP] Store groupprivate device_type on
symbol details
---
flang/include/flang/Semantics/semantics.h | 20 ------
flang/include/flang/Semantics/symbol.h | 11 ++++
flang/lib/Lower/OpenMP/OpenMP.cpp | 36 ++++++-----
flang/lib/Semantics/mod-file.cpp | 72 ++++++++--------------
flang/lib/Semantics/resolve-directives.cpp | 47 +++++++-------
flang/lib/Semantics/semantics.cpp | 14 -----
6 files changed, 77 insertions(+), 123 deletions(-)
diff --git a/flang/include/flang/Semantics/semantics.h b/flang/include/flang/Semantics/semantics.h
index 120c927b6b6f2..d4e53279fada2 100644
--- a/flang/include/flang/Semantics/semantics.h
+++ b/flang/include/flang/Semantics/semantics.h
@@ -58,11 +58,6 @@ class Symbol;
class CommonBlockMap;
using CommonBlockList = std::vector<std::pair<SymbolRef, std::size_t>>;
-// `device_type` modifier values that can appear on `!$omp groupprivate`.
-// Kept as a semantics-layer enum so this header does not depend on MLIR.
-// The lowering layer converts to mlir::omp::DeclareTargetDeviceType.
-enum class OmpGroupprivateDeviceType { Any, Host, Nohost };
-
using ConstructNode = std::variant<const parser::AssociateConstruct *,
const parser::BlockConstruct *, const parser::CaseConstruct *,
const parser::ChangeTeamConstruct *, const parser::CriticalConstruct *,
@@ -354,19 +349,6 @@ class SemanticsContext {
return accObjectDuplicates_.count(o) != 0;
}
- // Record the `device_type` modifier observed on a `!$omp groupprivate`
- // directive declaring \p symbol. Called during semantic analysis so the
- // value is available before any OpenMP lowering, removing the dependency on
- // lowering order between the directive's owning unit and any teams region
- // that references the symbol.
- void SetOmpGroupprivateDeviceType(const Symbol &, OmpGroupprivateDeviceType);
- // Look up the recorded `device_type` modifier for \p symbol. Returns
- // std::nullopt for symbols not flagged as groupprivate (or flagged before
- // this mechanism was populated), in which case the caller should default to
- // OmpGroupprivateDeviceType::Any per the OpenMP spec.
- std::optional<OmpGroupprivateDeviceType> GetOmpGroupprivateDeviceType(
- const Symbol &) const;
-
void DumpSymbols(llvm::raw_ostream &);
// Top-level ProgramTrees are owned by the SemanticsContext for persistence.
@@ -428,8 +410,6 @@ class SemanticsContext {
UnorderedSymbolSet isUsed_;
std::set<const parser::AccObject *> accObjectDuplicates_;
std::list<ProgramTree> programTrees_;
- std::map<SymbolRef, OmpGroupprivateDeviceType, SymbolAddressCompare>
- ompGroupprivateDeviceTypes_;
};
class Semantics {
diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index 40c49c594a707..5c0c868fc4ef1 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -76,6 +76,14 @@ class WithOmpDeclarative {
ompDeviceType_ = device;
}
+ const std::optional<common::OmpDeviceType> &
+ ompGroupprivateDeviceType() const {
+ return ompGroupprivateDeviceType_;
+ }
+ void set_ompGroupprivateDeviceType(common::OmpDeviceType device) {
+ ompGroupprivateDeviceType_ = device;
+ }
+
void printClauseSet(llvm::raw_ostream &os, const OmpClauseSet &clauses,
parser::CharBlock name = parser::CharBlock{}) const;
friend llvm::raw_ostream &operator<<(
@@ -99,6 +107,9 @@ class WithOmpDeclarative {
// The argument to DEVICE_TYPE clause. Only needed when the clause is
// present in the ompDeclTarget_ set.
std::optional<common::OmpDeviceType> ompDeviceType_;
+ // The argument to a DEVICE_TYPE clause on a GROUPPRIVATE directive declaring
+ // this symbol. Absent means the spec default (any).
+ std::optional<common::OmpDeviceType> ompGroupprivateDeviceType_;
};
// A module or submodule.
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index ebc32254e44d6..401fb956272ec 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -783,24 +783,22 @@ static void threadPrivatizeVars(lower::AbstractConverter &converter,
}
}
-// Translate a semantics-layer groupprivate device_type to the MLIR enum used
-// by omp.groupprivate. Kept here (rather than in semantics) to avoid making
-// the semantics layer depend on MLIR.
+// Translate a semantics-layer device_type to the MLIR enum used by
+// omp.groupprivate.
static mlir::omp::DeclareTargetDeviceType
-toMLIRDeclareTargetDeviceType(semantics::OmpGroupprivateDeviceType deviceType) {
+toMLIRDeclareTargetDeviceType(Fortran::common::OmpDeviceType deviceType) {
switch (deviceType) {
- case semantics::OmpGroupprivateDeviceType::Any:
+ case Fortran::common::OmpDeviceType::Any:
return mlir::omp::DeclareTargetDeviceType::any;
- case semantics::OmpGroupprivateDeviceType::Host:
+ case Fortran::common::OmpDeviceType::Host:
return mlir::omp::DeclareTargetDeviceType::host;
- case semantics::OmpGroupprivateDeviceType::Nohost:
+ case Fortran::common::OmpDeviceType::Nohost:
return mlir::omp::DeclareTargetDeviceType::nohost;
}
- llvm_unreachable("invalid OmpGroupprivateDeviceType");
+ llvm_unreachable("invalid OmpDeviceType");
}
static void groupprivatizeVars(lower::AbstractConverter &converter,
- semantics::SemanticsContext &semaCtx,
lower::pft::Evaluation &eval) {
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
mlir::Location currentLocation = converter.getCurrentLocation();
@@ -818,15 +816,19 @@ static void groupprivatizeVars(lower::AbstractConverter &converter,
}
// The device_type modifier was recorded on the symbol during semantic
- // analysis. Default to 'any' when the directive omitted a device_type
- // clause (per OpenMP spec) or the symbol was flagged before this
- // mechanism became available (e.g. a stale module file).
+ // analysis.
mlir::omp::DeclareTargetDeviceType deviceTypeEnum =
mlir::omp::DeclareTargetDeviceType::any;
- if (auto recorded =
- semaCtx.GetOmpGroupprivateDeviceType(sym.GetUltimate())) {
- deviceTypeEnum = toMLIRDeclareTargetDeviceType(*recorded);
- }
+ Fortran::common::visit(
+ [&](auto &&details) {
+ using TypeD = llvm::remove_cvref_t<decltype(details)>;
+ if constexpr (std::is_base_of_v<semantics::WithOmpDeclarative,
+ TypeD>) {
+ if (auto dt = details.ompGroupprivateDeviceType())
+ deviceTypeEnum = toMLIRDeclareTargetDeviceType(*dt);
+ }
+ },
+ sym.GetUltimate().details());
mlir::omp::DeclareTargetDeviceTypeAttr deviceTypeAttr =
mlir::omp::DeclareTargetDeviceTypeAttr::get(firOpBuilder.getContext(),
deviceTypeEnum);
@@ -1450,7 +1452,7 @@ static void createBodyOfOp(mlir::Operation &op, const OpWithBodyGenInfo &info,
}
if (info.dir == llvm::omp::Directive::OMPD_teams) {
- groupprivatizeVars(info.converter, info.semaCtx, info.eval);
+ groupprivatizeVars(info.converter, info.eval);
}
if (!info.genSkeletonOnly) {
diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index 6c68cae5894e9..221f0daeb7e11 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -62,8 +62,6 @@ static void PutShapeSpec(llvm::raw_ostream &, const ShapeSpec &);
static void PutShape(
llvm::raw_ostream &, const ArraySpec &, char open, char close);
static void PutMapper(llvm::raw_ostream &, const Symbol &, SemanticsContext &);
-static void PutOmpGroupprivateDirective(
- llvm::raw_ostream &, const Symbol &, SemanticsContext &);
static llvm::raw_ostream &PutAttr(llvm::raw_ostream &, Attr);
static llvm::raw_ostream &PutType(llvm::raw_ostream &, const DeclTypeSpec &);
@@ -410,6 +408,30 @@ static void PutOpenMPDeclarativeDirectives(llvm::raw_ostream &os,
decls->printClauseSet(os, dtgt, symbol.name());
os << "\n";
}
+ // Re-emit `!$omp groupprivate` (and any recorded device_type) so a TU
+ // that `use`s this module recovers the directive from the .mod file.
+ // Common-block names must be wrapped in slashes when reparsed.
+ if (symbol.test(Symbol::Flag::OmpGroupPrivate)) {
+ os << "!$omp groupprivate(";
+ if (symbol.detailsIf<CommonBlockDetails>())
+ os << '/' << symbol.name() << '/';
+ else
+ os << symbol.name();
+ os << ")";
+ if (auto deviceType{decls->ompGroupprivateDeviceType()}) {
+ switch (*deviceType) {
+ case common::OmpDeviceType::Any:
+ break;
+ case common::OmpDeviceType::Host:
+ os << " device_type(host)";
+ break;
+ case common::OmpDeviceType::Nohost:
+ os << " device_type(nohost)";
+ break;
+ }
+ }
+ os << "\n";
+ }
}
}
}
@@ -592,10 +614,6 @@ void ModFileWriter::PutSymbol(
x.isExplicitBindName(), ""s);
decls_ << "::/" << symbol.name() << "/\n";
}
- // Common-block symbols are processed by this branch (not the
- // generic catch-all below), so emit any per-symbol OpenMP
- // directives - e.g. `!$omp groupprivate(/blk/)` - here too.
- PutOmpGroupprivateDirective(decls_, symbol, context_);
},
[](const HostAssocDetails &) {},
[](const MiscDetails &) {},
@@ -1319,53 +1337,15 @@ void PutOpenACCDirective(llvm::raw_ostream &os, const Symbol &symbol) {
}
}
-// Re-emit `!$omp groupprivate` for symbols flagged during the module's own
-// semantic analysis, including the device_type modifier if one was recorded
-// (otherwise omit it so the default `any` semantics apply). This lets a TU
-// that `use`s this module recover the directive's information from the .mod
-// file rather than the original source - mirroring how `requires` is
-// propagated. Handles both ordinary variables and common-block names (which
-// must be wrapped in slashes when reparsed).
-static void PutOmpGroupprivateDirective(
- llvm::raw_ostream &os, const Symbol &symbol, SemanticsContext &context) {
- if (!symbol.test(Symbol::Flag::OmpGroupPrivate)) {
- return;
- }
- os << "!$omp groupprivate(";
- if (symbol.detailsIf<CommonBlockDetails>()) {
- os << '/' << symbol.name() << '/';
- } else {
- os << symbol.name();
- }
- os << ")";
- if (auto deviceType{
- context.GetOmpGroupprivateDeviceType(symbol.GetUltimate())}) {
- switch (*deviceType) {
- case OmpGroupprivateDeviceType::Any:
- // Default - emit nothing.
- break;
- case OmpGroupprivateDeviceType::Host:
- os << " device_type(host)";
- break;
- case OmpGroupprivateDeviceType::Nohost:
- os << " device_type(nohost)";
- break;
- }
- }
- os << "\n";
-}
-
-void PutOpenMPDirective(
- llvm::raw_ostream &os, const Symbol &symbol, SemanticsContext &context) {
+void PutOpenMPDirective(llvm::raw_ostream &os, const Symbol &symbol) {
if (symbol.test(Symbol::Flag::OmpThreadprivate)) {
os << "!$omp threadprivate(" << symbol.name() << ")\n";
}
- PutOmpGroupprivateDirective(os, symbol, context);
}
void ModFileWriter::PutDirective(llvm::raw_ostream &os, const Symbol &symbol) {
PutOpenACCDirective(os, symbol);
- PutOpenMPDirective(os, symbol, context_);
+ PutOpenMPDirective(os, symbol);
}
struct Temp {
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 891005d287eff..a939cd8b15527 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2192,34 +2192,29 @@ bool OmpAttributeVisitor::Pre(const parser::OmpGroupprivateDirective &x) {
}
}
- // OpenMP 6.0 allows an optional device_type clause on the groupprivate
- // directive. Decode it once here (defaulting to `any` per the spec) so the
- // value can be attached to every object listed by the directive.
- OmpGroupprivateDeviceType deviceType{OmpGroupprivateDeviceType::Any};
- for (const parser::OmpClause &clause : x.v.Clauses().v) {
- if (const auto *dt{std::get_if<parser::OmpClause::DeviceType>(&clause.u)}) {
- switch (dt->v.v) {
- case parser::OmpDeviceTypeClause::DeviceTypeDescription::Any:
- deviceType = OmpGroupprivateDeviceType::Any;
- break;
- case parser::OmpDeviceTypeClause::DeviceTypeDescription::Host:
- deviceType = OmpGroupprivateDeviceType::Host;
- break;
- case parser::OmpDeviceTypeClause::DeviceTypeDescription::Nohost:
- deviceType = OmpGroupprivateDeviceType::Nohost;
- break;
- }
- break;
- }
+ // OpenMP 6.0 allows an optional device_type clause on groupprivate. Record it
+ // on each listed object's ultimate symbol so lowering and .mod emission can
+ // read it. An absent clause means the spec default (any).
+ std::optional<common::OmpDeviceType> device;
+ if (auto *devClause{
+ parser::omp::FindClause(x.v, llvm::omp::Clause::OMPC_device_type)}) {
+ device = parser::UnwrapRef<common::OmpDeviceType>(*devClause);
}
- // Record device_type against the ultimate symbol of each resolved object so
- // it is reachable during lowering regardless of the order in which the
- // owning unit and its referencing teams regions are lowered within this TU.
- for (const parser::OmpArgument &arg : x.v.Arguments().v) {
- if (const parser::OmpObject *object{parser::omp::GetArgumentObject(arg)}) {
- if (const Symbol *sym{omp::GetObjectSymbol(*object)}) {
- context_.SetOmpGroupprivateDeviceType(sym->GetUltimate(), deviceType);
+ if (device) {
+ for (const parser::OmpArgument &arg : x.v.Arguments().v) {
+ if (const parser::OmpObject *
+ object{parser::omp::GetArgumentObject(arg)}) {
+ if (const Symbol * sym{omp::GetObjectSymbol(*object)}) {
+ common::visit(
+ [&](auto &d) {
+ using TypeD = llvm::remove_cvref_t<decltype(d)>;
+ if constexpr (std::is_base_of_v<WithOmpDeclarative, TypeD>) {
+ d.set_ompGroupprivateDeviceType(*device);
+ }
+ },
+ const_cast<Symbol &>(sym->GetUltimate()).details());
+ }
}
}
}
diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index cec4e67faaabc..e4ac2f73c3976 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -440,20 +440,6 @@ void SemanticsContext::CheckError(const Symbol &symbol) {
}
}
-void SemanticsContext::SetOmpGroupprivateDeviceType(
- const Symbol &symbol, OmpGroupprivateDeviceType deviceType) {
- ompGroupprivateDeviceTypes_.insert_or_assign(SymbolRef{symbol}, deviceType);
-}
-
-std::optional<OmpGroupprivateDeviceType>
-SemanticsContext::GetOmpGroupprivateDeviceType(const Symbol &symbol) const {
- auto it{ompGroupprivateDeviceTypes_.find(SymbolRef{symbol})};
- if (it == ompGroupprivateDeviceTypes_.end()) {
- return std::nullopt;
- }
- return it->second;
-}
-
bool SemanticsContext::ScopeIndexComparator::operator()(
parser::CharBlock x, parser::CharBlock y) const {
return x.begin() < y.begin() ||
>From 32088da987161f5457257ae37a9c06cd49079a22 Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Mon, 8 Jun 2026 12:51:52 +0530
Subject: [PATCH 08/12] code-foramt fix
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 8 +++-----
flang/lib/Semantics/resolve-directives.cpp | 6 +++---
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 401fb956272ec..73e0b7e884cd8 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -4746,11 +4746,9 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx,
lower::pft::Evaluation &eval,
const parser::OmpGroupprivateDirective &directive) {
- // The OmpGroupPrivate symbol flag and any device_type modifier are
- // recorded during semantic analysis (see OmpAttributeVisitor::Pre on
- // OmpGroupprivateDirective). The omp.groupprivate operation itself is
- // materialised on first use inside a teams region by groupprivatizeVars,
- // so this declarative directive has no remaining lowering work to do.
+ // The semantic analysis sets the flag and device_type on the
+ // symbols; omp.groupprivate is materialised on first use in a teams region
+ // by groupprivatizeVars.
}
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index a939cd8b15527..949e683a7050b 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2203,9 +2203,9 @@ bool OmpAttributeVisitor::Pre(const parser::OmpGroupprivateDirective &x) {
if (device) {
for (const parser::OmpArgument &arg : x.v.Arguments().v) {
- if (const parser::OmpObject *
- object{parser::omp::GetArgumentObject(arg)}) {
- if (const Symbol * sym{omp::GetObjectSymbol(*object)}) {
+ if (const parser::OmpObject *object{
+ parser::omp::GetArgumentObject(arg)}) {
+ if (const Symbol *sym{omp::GetObjectSymbol(*object)}) {
common::visit(
[&](auto &d) {
using TypeD = llvm::remove_cvref_t<decltype(d)>;
>From b5e2436a7d3bbf9c3868dc68649b243396dfdd6c Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Tue, 9 Jun 2026 12:17:26 +0530
Subject: [PATCH 09/12] [flang][OpenMP] Handle groupprivate device_type like
declare_target
---
flang/include/flang/Semantics/semantics.h | 1 -
flang/include/flang/Semantics/symbol.h | 12 ++++++++++--
flang/lib/Semantics/mod-file.cpp | 20 ++++++++------------
flang/lib/Semantics/resolve-directives.cpp | 14 ++++++++------
flang/lib/Semantics/symbol.cpp | 15 ++++++++++++---
5 files changed, 38 insertions(+), 24 deletions(-)
diff --git a/flang/include/flang/Semantics/semantics.h b/flang/include/flang/Semantics/semantics.h
index d4e53279fada2..5e98378fc49be 100644
--- a/flang/include/flang/Semantics/semantics.h
+++ b/flang/include/flang/Semantics/semantics.h
@@ -20,7 +20,6 @@
#include "flang/Support/Fortran-features.h"
#include "flang/Support/LangOptions.h"
#include <iosfwd>
-#include <optional>
#include <set>
#include <string>
#include <vector>
diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index 5c0c868fc4ef1..8a125e2a7810b 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -76,6 +76,9 @@ class WithOmpDeclarative {
ompDeviceType_ = device;
}
+ const OmpClauseSet &ompGroupprivate() const { return ompGroupprivate_; }
+ void set_ompGroupprivate(OmpClauseSet clauses) { ompGroupprivate_ = clauses; }
+
const std::optional<common::OmpDeviceType> &
ompGroupprivateDeviceType() const {
return ompGroupprivateDeviceType_;
@@ -84,8 +87,11 @@ class WithOmpDeclarative {
ompGroupprivateDeviceType_ = device;
}
+ // \p deviceType overrides the DEVICE_TYPE clause value to print (used for
+ // GROUPPRIVATE); when absent the DECLARE_TARGET device_type is used.
void printClauseSet(llvm::raw_ostream &os, const OmpClauseSet &clauses,
- parser::CharBlock name = parser::CharBlock{}) const;
+ parser::CharBlock name = parser::CharBlock{},
+ std::optional<common::OmpDeviceType> deviceType = std::nullopt) const;
friend llvm::raw_ostream &operator<<(
llvm::raw_ostream &, const WithOmpDeclarative &);
@@ -107,8 +113,10 @@ class WithOmpDeclarative {
// The argument to DEVICE_TYPE clause. Only needed when the clause is
// present in the ompDeclTarget_ set.
std::optional<common::OmpDeviceType> ompDeviceType_;
+ // The set of clauses on a GROUPPRIVATE directive declaring this symbol.
+ OmpClauseSet ompGroupprivate_;
// The argument to a DEVICE_TYPE clause on a GROUPPRIVATE directive declaring
- // this symbol. Absent means the spec default (any).
+ // this symbol. Only needed when the clause is present in ompGroupprivate_.
std::optional<common::OmpDeviceType> ompGroupprivateDeviceType_;
};
diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index 221f0daeb7e11..809c27723c240 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -412,23 +412,19 @@ static void PutOpenMPDeclarativeDirectives(llvm::raw_ostream &os,
// that `use`s this module recovers the directive from the .mod file.
// Common-block names must be wrapped in slashes when reparsed.
if (symbol.test(Symbol::Flag::OmpGroupPrivate)) {
- os << "!$omp groupprivate(";
+ os << "!$omp "
+ << parser::ToLowerCaseLetters(llvm::omp::getOpenMPDirectiveName(
+ llvm::omp::Directive::OMPD_groupprivate, version))
+ << "(";
if (symbol.detailsIf<CommonBlockDetails>())
os << '/' << symbol.name() << '/';
else
os << symbol.name();
os << ")";
- if (auto deviceType{decls->ompGroupprivateDeviceType()}) {
- switch (*deviceType) {
- case common::OmpDeviceType::Any:
- break;
- case common::OmpDeviceType::Host:
- os << " device_type(host)";
- break;
- case common::OmpDeviceType::Nohost:
- os << " device_type(nohost)";
- break;
- }
+ if (const OmpClauseSet & gp{decls->ompGroupprivate()}; gp.count()) {
+ os << " ";
+ decls->printClauseSet(
+ os, gp, parser::CharBlock{}, decls->ompGroupprivateDeviceType());
}
os << "\n";
}
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 949e683a7050b..7f82f464c4b56 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2195,13 +2195,13 @@ bool OmpAttributeVisitor::Pre(const parser::OmpGroupprivateDirective &x) {
// OpenMP 6.0 allows an optional device_type clause on groupprivate. Record it
// on each listed object's ultimate symbol so lowering and .mod emission can
// read it. An absent clause means the spec default (any).
- std::optional<common::OmpDeviceType> device;
if (auto *devClause{
parser::omp::FindClause(x.v, llvm::omp::Clause::OMPC_device_type)}) {
- device = parser::UnwrapRef<common::OmpDeviceType>(*devClause);
- }
-
- if (device) {
+ unsigned version{context_.langOptions().OpenMPVersion};
+ common::OmpDeviceType device{
+ parser::UnwrapRef<common::OmpDeviceType>(*devClause)};
+ WithOmpDeclarative::OmpClauseSet clauses;
+ clauses.set(llvm::omp::Clause::OMPC_device_type);
for (const parser::OmpArgument &arg : x.v.Arguments().v) {
if (const parser::OmpObject *object{
parser::omp::GetArgumentObject(arg)}) {
@@ -2210,7 +2210,9 @@ bool OmpAttributeVisitor::Pre(const parser::OmpGroupprivateDirective &x) {
[&](auto &d) {
using TypeD = llvm::remove_cvref_t<decltype(d)>;
if constexpr (std::is_base_of_v<WithOmpDeclarative, TypeD>) {
- d.set_ompGroupprivateDeviceType(*device);
+ d.set_version(version);
+ d.set_ompGroupprivate(clauses);
+ d.set_ompGroupprivateDeviceType(device);
}
},
const_cast<Symbol &>(sym->GetUltimate()).details());
diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp
index 546ed3e2fa023..a3dffdb0af847 100644
--- a/flang/lib/Semantics/symbol.cpp
+++ b/flang/lib/Semantics/symbol.cpp
@@ -71,7 +71,8 @@ static void DumpList(llvm::raw_ostream &os, const char *label, const T &list) {
}
void WithOmpDeclarative::printClauseSet(llvm::raw_ostream &os,
- const OmpClauseSet &clauses, parser::CharBlock name) const {
+ const OmpClauseSet &clauses, parser::CharBlock name,
+ std::optional<common::OmpDeviceType> deviceType) const {
auto toLower = parser::ToLowerCaseLetters;
size_t idx{0}, size{clauses.count()};
@@ -81,9 +82,11 @@ void WithOmpDeclarative::printClauseSet(llvm::raw_ostream &os,
case llvm::omp::Clause::OMPC_atomic_default_mem_order:
os << '(' << toLower(EnumToString(*ompAtomicDefaultMemOrder())) << ')';
break;
- case llvm::omp::Clause::OMPC_device_type:
- os << "(" << toLower(EnumToString(*ompDeviceType())) << ')';
+ case llvm::omp::Clause::OMPC_device_type: {
+ common::OmpDeviceType dt{deviceType ? *deviceType : *ompDeviceType()};
+ os << '(' << toLower(EnumToString(dt)) << ')';
break;
+ }
case llvm::omp::Clause::OMPC_enter:
case llvm::omp::Clause::OMPC_link:
if (!name.empty()) {
@@ -116,6 +119,12 @@ llvm::raw_ostream &operator<<(
x.printClauseSet(os, dtgt);
os << ')';
}
+ if (const OmpClauseSet & gp{x.ompGroupprivate()}; gp.count()) {
+ os << " OmpGroupprivateFlags:(";
+ x.printClauseSet(
+ os, gp, parser::CharBlock{}, x.ompGroupprivateDeviceType());
+ os << ')';
+ }
return os;
}
>From d956fecc4542169d9b8b16f8e4c3c3a8d4772e2c Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Tue, 9 Jun 2026 12:56:31 +0530
Subject: [PATCH 10/12] code-format
---
flang/lib/Semantics/mod-file.cpp | 2 +-
flang/lib/Semantics/symbol.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index 809c27723c240..e61a16b612a6b 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -421,7 +421,7 @@ static void PutOpenMPDeclarativeDirectives(llvm::raw_ostream &os,
else
os << symbol.name();
os << ")";
- if (const OmpClauseSet & gp{decls->ompGroupprivate()}; gp.count()) {
+ if (const OmpClauseSet &gp{decls->ompGroupprivate()}; gp.count()) {
os << " ";
decls->printClauseSet(
os, gp, parser::CharBlock{}, decls->ompGroupprivateDeviceType());
diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp
index a3dffdb0af847..74acc2c7d8adc 100644
--- a/flang/lib/Semantics/symbol.cpp
+++ b/flang/lib/Semantics/symbol.cpp
@@ -119,7 +119,7 @@ llvm::raw_ostream &operator<<(
x.printClauseSet(os, dtgt);
os << ')';
}
- if (const OmpClauseSet & gp{x.ompGroupprivate()}; gp.count()) {
+ if (const OmpClauseSet &gp{x.ompGroupprivate()}; gp.count()) {
os << " OmpGroupprivateFlags:(";
x.printClauseSet(
os, gp, parser::CharBlock{}, x.ompGroupprivateDeviceType());
>From 1fd692f1d3310748a9f991e8325014265b97c994 Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Wed, 10 Jun 2026 15:16:42 +0530
Subject: [PATCH 11/12] [flang][OpenMP] Unify groupprivate device_type handling
with declare_target
---
flang/include/flang/Semantics/symbol.h | 18 ++++----
flang/lib/Lower/OpenMP/OpenMP.cpp | 26 +++++-------
flang/lib/Semantics/mod-file.cpp | 21 ++++------
flang/lib/Semantics/resolve-directives.cpp | 49 +++++++++++-----------
flang/lib/Semantics/symbol.cpp | 20 +++++----
5 files changed, 66 insertions(+), 68 deletions(-)
diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index 8a125e2a7810b..9320932eca85f 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -69,11 +69,11 @@ class WithOmpDeclarative {
const OmpClauseSet &ompDeclTarget() const { return ompDeclTarget_; }
void set_ompDeclTarget(OmpClauseSet clauses) { ompDeclTarget_ = clauses; }
- const std::optional<common::OmpDeviceType> &ompDeviceType() const {
- return ompDeviceType_;
+ const std::optional<common::OmpDeviceType> &ompDeclTargetDeviceType() const {
+ return ompDeclTargetDeviceType_;
}
void set_ompDeclTarget(common::OmpDeviceType device) {
- ompDeviceType_ = device;
+ ompDeclTargetDeviceType_ = device;
}
const OmpClauseSet &ompGroupprivate() const { return ompGroupprivate_; }
@@ -83,15 +83,15 @@ class WithOmpDeclarative {
ompGroupprivateDeviceType() const {
return ompGroupprivateDeviceType_;
}
- void set_ompGroupprivateDeviceType(common::OmpDeviceType device) {
+ void set_ompGroupprivate(common::OmpDeviceType device) {
ompGroupprivateDeviceType_ = device;
}
- // \p deviceType overrides the DEVICE_TYPE clause value to print (used for
- // GROUPPRIVATE); when absent the DECLARE_TARGET device_type is used.
+ // \p dir selects which directive's DEVICE_TYPE value to print, since the
+ // clause can be carried by both DECLARE_TARGET and GROUPPRIVATE.
void printClauseSet(llvm::raw_ostream &os, const OmpClauseSet &clauses,
- parser::CharBlock name = parser::CharBlock{},
- std::optional<common::OmpDeviceType> deviceType = std::nullopt) const;
+ llvm::omp::Directive dir,
+ parser::CharBlock name = parser::CharBlock{}) const;
friend llvm::raw_ostream &operator<<(
llvm::raw_ostream &, const WithOmpDeclarative &);
@@ -112,7 +112,7 @@ class WithOmpDeclarative {
OmpClauseSet ompDeclTarget_;
// The argument to DEVICE_TYPE clause. Only needed when the clause is
// present in the ompDeclTarget_ set.
- std::optional<common::OmpDeviceType> ompDeviceType_;
+ std::optional<common::OmpDeviceType> ompDeclTargetDeviceType_;
// The set of clauses on a GROUPPRIVATE directive declaring this symbol.
OmpClauseSet ompGroupprivate_;
// The argument to a DEVICE_TYPE clause on a GROUPPRIVATE directive declaring
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 73e0b7e884cd8..49dfcb444d6e3 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -53,6 +53,7 @@
#include "mlir/Support/StateStack.h"
#include "mlir/Transforms/RegionUtils.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallSet.h"
using namespace Fortran::lower::omp;
using namespace Fortran::common::openmp;
@@ -834,7 +835,7 @@ static void groupprivatizeVars(lower::AbstractConverter &converter,
deviceTypeEnum);
// omp.groupprivate takes a flat symbol reference and returns
- // the address of the per-team copy of the global variable.
+ // the address of the per-contention group copy of the global variable.
return mlir::omp::GroupprivateOp::create(
firOpBuilder, currentLocation, global.resultType(), global.getSymbol(),
deviceTypeAttr);
@@ -845,20 +846,18 @@ static void groupprivatizeVars(lower::AbstractConverter &converter,
semantics::Symbol::Flag::OmpGroupPrivate,
/*collectSymbols=*/true,
/*collectHostAssociatedSymbols=*/true);
- std::set<semantics::SourceName> groupprivateSymNames;
+ llvm::SmallSet<semantics::SourceName, 8> 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];
+ for (const semantics::Symbol *sym : groupprivateSyms) {
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())
+ if (!groupprivateSymNames.insert(sym->name()).second)
continue;
- groupprivateSymNames.insert(sym->name());
if (const semantics::Symbol *common =
semantics::FindCommonBlockContaining(sym->GetUltimate())) {
@@ -881,9 +880,8 @@ static void groupprivatizeVars(lower::AbstractConverter &converter,
symGroupprivateValue = genGroupprivateOp(*sym);
}
- if (!symGroupprivateValue) {
+ if (!symGroupprivateValue)
continue;
- }
fir::ExtendedValue sexv = converter.getSymbolExtendedValue(*sym);
fir::ExtendedValue symGroupprivateExv =
@@ -1451,9 +1449,9 @@ static void createBodyOfOp(mlir::Operation &op, const OpWithBodyGenInfo &info,
}
}
- if (info.dir == llvm::omp::Directive::OMPD_teams) {
+ // TODO: groupprivate is currently only materialised for `teams` constructs.
+ if (info.dir == llvm::omp::Directive::OMPD_teams)
groupprivatizeVars(info.converter, info.eval);
- }
if (!info.genSkeletonOnly) {
if (ConstructQueue::const_iterator next = std::next(item);
@@ -3144,7 +3142,7 @@ genTargetOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
return;
// Skip groupprivate symbols - they don't need to be mapped because
- // groupprivate creates its own LDS storage.
+ // groupprivate creates its own storage.
if (sym.GetUltimate().test(semantics::Symbol::Flag::OmpGroupPrivate))
return;
@@ -4747,8 +4745,7 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
lower::pft::Evaluation &eval,
const parser::OmpGroupprivateDirective &directive) {
// The semantic analysis sets the flag and device_type on the
- // symbols; omp.groupprivate is materialised on first use in a teams region
- // by groupprivatizeVars.
+ // symbols; omp.groupprivate is materialised by groupprivatizeVars.
}
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
@@ -5540,8 +5537,7 @@ void Fortran::lower::genGroupprivateOp(lower::AbstractConverter &converter,
globalInitialization(converter, firOpBuilder, sym, var, currentLocation);
}
- // The actual omp.groupprivate operation is created by groupprivatizeVars
- // when entering a teams region.
+ // The actual omp.groupprivate operation is created by groupprivatizeVars.
}
void Fortran::lower::genThreadprivateOp(lower::AbstractConverter &converter,
diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index e61a16b612a6b..89a535c6ff6f9 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -387,7 +387,7 @@ static void PutOpenMPRequirements(
os << "!$omp "
<< parser::ToLowerCaseLetters(llvm::omp::getOpenMPDirectiveName(
llvm::omp::Directive::OMPD_requires, version));
- decls->printClauseSet(os, reqs);
+ decls->printClauseSet(os, reqs, llvm::omp::Directive::OMPD_requires);
os << "\n";
}
}
@@ -405,13 +405,14 @@ static void PutOpenMPDeclarativeDirectives(llvm::raw_ostream &os,
<< parser::ToLowerCaseLetters(llvm::omp::getOpenMPDirectiveName(
llvm::omp::Directive::OMPD_declare_target, version))
<< " ";
- decls->printClauseSet(os, dtgt, symbol.name());
+ decls->printClauseSet(
+ os, dtgt, llvm::omp::Directive::OMPD_declare_target, symbol.name());
os << "\n";
}
- // Re-emit `!$omp groupprivate` (and any recorded device_type) so a TU
- // that `use`s this module recovers the directive from the .mod file.
- // Common-block names must be wrapped in slashes when reparsed.
- if (symbol.test(Symbol::Flag::OmpGroupPrivate)) {
+ // Re-emit `!$omp groupprivate` (and its device_type) so a TU that `use`s
+ // this module recovers the directive from the .mod file. Common-block
+ // names must be wrapped in slashes when reparsed.
+ if (const OmpClauseSet &gp{decls->ompGroupprivate()}; gp.count()) {
os << "!$omp "
<< parser::ToLowerCaseLetters(llvm::omp::getOpenMPDirectiveName(
llvm::omp::Directive::OMPD_groupprivate, version))
@@ -420,12 +421,8 @@ static void PutOpenMPDeclarativeDirectives(llvm::raw_ostream &os,
os << '/' << symbol.name() << '/';
else
os << symbol.name();
- os << ")";
- if (const OmpClauseSet &gp{decls->ompGroupprivate()}; gp.count()) {
- os << " ";
- decls->printClauseSet(
- os, gp, parser::CharBlock{}, decls->ompGroupprivateDeviceType());
- }
+ os << ") ";
+ decls->printClauseSet(os, gp, llvm::omp::Directive::OMPD_groupprivate);
os << "\n";
}
}
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 7f82f464c4b56..26a76daa3f6ee 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2192,31 +2192,32 @@ bool OmpAttributeVisitor::Pre(const parser::OmpGroupprivateDirective &x) {
}
}
- // OpenMP 6.0 allows an optional device_type clause on groupprivate. Record it
- // on each listed object's ultimate symbol so lowering and .mod emission can
- // read it. An absent clause means the spec default (any).
+ // groupprivate carries an optional device_type clause (OpenMP 6.0). Always
+ // record it, like declare_target's enter clause, so a clause-less directive
+ // still round-trips through symbol dumps and .mod files; an absent clause
+ // means the spec default (any).
+ common::OmpDeviceType device{common::OmpDeviceType::Any};
if (auto *devClause{
parser::omp::FindClause(x.v, llvm::omp::Clause::OMPC_device_type)}) {
- unsigned version{context_.langOptions().OpenMPVersion};
- common::OmpDeviceType device{
- parser::UnwrapRef<common::OmpDeviceType>(*devClause)};
- WithOmpDeclarative::OmpClauseSet clauses;
- clauses.set(llvm::omp::Clause::OMPC_device_type);
- for (const parser::OmpArgument &arg : x.v.Arguments().v) {
- if (const parser::OmpObject *object{
- parser::omp::GetArgumentObject(arg)}) {
- if (const Symbol *sym{omp::GetObjectSymbol(*object)}) {
- common::visit(
- [&](auto &d) {
- using TypeD = llvm::remove_cvref_t<decltype(d)>;
- if constexpr (std::is_base_of_v<WithOmpDeclarative, TypeD>) {
- d.set_version(version);
- d.set_ompGroupprivate(clauses);
- d.set_ompGroupprivateDeviceType(device);
- }
- },
- const_cast<Symbol &>(sym->GetUltimate()).details());
- }
+ device = parser::UnwrapRef<common::OmpDeviceType>(*devClause);
+ }
+
+ unsigned version{context_.langOptions().OpenMPVersion};
+ WithOmpDeclarative::OmpClauseSet clauses;
+ clauses.set(llvm::omp::Clause::OMPC_device_type);
+ for (const parser::OmpArgument &arg : x.v.Arguments().v) {
+ if (const parser::OmpObject *object{parser::omp::GetArgumentObject(arg)}) {
+ if (const Symbol *sym{omp::GetObjectSymbol(*object)}) {
+ common::visit(
+ [&](auto &d) {
+ using TypeD = llvm::remove_cvref_t<decltype(d)>;
+ if constexpr (std::is_base_of_v<WithOmpDeclarative, TypeD>) {
+ d.set_version(version);
+ d.set_ompGroupprivate(clauses);
+ d.set_ompGroupprivate(device);
+ }
+ },
+ const_cast<Symbol &>(sym->GetUltimate()).details());
}
}
}
@@ -2316,7 +2317,7 @@ bool OmpAttributeVisitor::Pre(const parser::OmpDeclareTargetDirective &x) {
if (device) {
clauseSet.set(llvm::omp::Clause::OMPC_device_type);
auto &deviceType{
- const_cast<decltype(device) &>(d.ompDeviceType())};
+ const_cast<decltype(device) &>(d.ompDeclTargetDeviceType())};
deviceType = device;
}
} else if constexpr (std::is_same_v<GenericDetails, TypeD> ||
diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp
index 74acc2c7d8adc..3560dac0f4a26 100644
--- a/flang/lib/Semantics/symbol.cpp
+++ b/flang/lib/Semantics/symbol.cpp
@@ -71,8 +71,8 @@ static void DumpList(llvm::raw_ostream &os, const char *label, const T &list) {
}
void WithOmpDeclarative::printClauseSet(llvm::raw_ostream &os,
- const OmpClauseSet &clauses, parser::CharBlock name,
- std::optional<common::OmpDeviceType> deviceType) const {
+ const OmpClauseSet &clauses, llvm::omp::Directive dir,
+ parser::CharBlock name) const {
auto toLower = parser::ToLowerCaseLetters;
size_t idx{0}, size{clauses.count()};
@@ -83,8 +83,13 @@ void WithOmpDeclarative::printClauseSet(llvm::raw_ostream &os,
os << '(' << toLower(EnumToString(*ompAtomicDefaultMemOrder())) << ')';
break;
case llvm::omp::Clause::OMPC_device_type: {
- common::OmpDeviceType dt{deviceType ? *deviceType : *ompDeviceType()};
- os << '(' << toLower(EnumToString(dt)) << ')';
+ // device_type is carried by several directives; print the value
+ // recorded for the one being emitted.
+ const std::optional<common::OmpDeviceType> &dt{
+ dir == llvm::omp::Directive::OMPD_groupprivate
+ ? ompGroupprivateDeviceType()
+ : ompDeclTargetDeviceType()};
+ os << '(' << toLower(EnumToString(*dt)) << ')';
break;
}
case llvm::omp::Clause::OMPC_enter:
@@ -111,18 +116,17 @@ llvm::raw_ostream &operator<<(
if (const OmpClauseSet &reqs{x.ompRequires()}; reqs.count()) {
os << " OmpRequirements:(";
- x.printClauseSet(os, reqs);
+ x.printClauseSet(os, reqs, llvm::omp::Directive::OMPD_requires);
os << ')';
}
if (const OmpClauseSet &dtgt{x.ompDeclTarget()}; dtgt.count()) {
os << " OmpDeclareTargetFlags:(";
- x.printClauseSet(os, dtgt);
+ x.printClauseSet(os, dtgt, llvm::omp::Directive::OMPD_declare_target);
os << ')';
}
if (const OmpClauseSet &gp{x.ompGroupprivate()}; gp.count()) {
os << " OmpGroupprivateFlags:(";
- x.printClauseSet(
- os, gp, parser::CharBlock{}, x.ompGroupprivateDeviceType());
+ x.printClauseSet(os, gp, llvm::omp::Directive::OMPD_groupprivate);
os << ')';
}
return os;
>From 2258cad8cdafa650937e79ee4ed54d7fe8f0a9db Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Thu, 11 Jun 2026 19:31:08 +0530
Subject: [PATCH 12/12] NFC changes
---
flang/include/flang/Semantics/symbol.h | 4 ++--
flang/lib/Lower/OpenMP/OpenMP.cpp | 8 ++++----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index 9320932eca85f..23c26ba733e86 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -87,8 +87,8 @@ class WithOmpDeclarative {
ompGroupprivateDeviceType_ = device;
}
- // \p dir selects which directive's DEVICE_TYPE value to print, since the
- // clause can be carried by both DECLARE_TARGET and GROUPPRIVATE.
+ // \p dir indicates to which declarative directive the given clauses
+ // belong to.
void printClauseSet(llvm::raw_ostream &os, const OmpClauseSet &clauses,
llvm::omp::Directive dir,
parser::CharBlock name = parser::CharBlock{}) const;
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 49dfcb444d6e3..82280f38843f2 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -53,6 +53,7 @@
#include "mlir/Support/StateStack.h"
#include "mlir/Transforms/RegionUtils.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallSet.h"
using namespace Fortran::lower::omp;
@@ -812,9 +813,8 @@ static void groupprivatizeVars(lower::AbstractConverter &converter,
auto genGroupprivateOp = [&](const semantics::Symbol &sym) -> mlir::Value {
std::string globalName = converter.mangleName(sym);
fir::GlobalOp global = module.lookupSymbol<fir::GlobalOp>(globalName);
- if (!global) {
+ if (!global)
return mlir::Value();
- }
// The device_type modifier was recorded on the symbol during semantic
// analysis.
@@ -850,7 +850,7 @@ static void groupprivatizeVars(lower::AbstractConverter &converter,
// For a COMMON block, the GroupprivateOp is generated for the block itself
// instead of its members.
- llvm::SetVector<const semantics::Symbol *> commonSyms;
+ llvm::SmallPtrSet<const semantics::Symbol *, 8> commonSyms;
for (const semantics::Symbol *sym : groupprivateSyms) {
mlir::Value symGroupprivateValue;
@@ -5537,7 +5537,7 @@ void Fortran::lower::genGroupprivateOp(lower::AbstractConverter &converter,
globalInitialization(converter, firOpBuilder, sym, var, currentLocation);
}
- // The actual omp.groupprivate operation is created by groupprivatizeVars.
+ // The actual omp.groupprivate operations are created by groupprivatizeVars.
}
void Fortran::lower::genThreadprivateOp(lower::AbstractConverter &converter,
More information about the flang-commits
mailing list