[flang-commits] [flang] [flang][openmp] Support importing module declare target globals (PR #213930)

Ville-Markus Yli-Suutala via flang-commits flang-commits at lists.llvm.org
Wed Aug 26 04:16:51 PDT 2026


https://github.com/VeeEM updated https://github.com/llvm/llvm-project/pull/213930

>From bd9c1852a8f4cd2c11cfd4c607f57371d6c422e9 Mon Sep 17 00:00:00 2001
From: Ville-Markus Yli-Suutala <ville-markus.yli-suutala at helsinki.fi>
Date: Mon, 3 Aug 2026 18:59:50 +0300
Subject: [PATCH 1/5] [flang][openmp] Write to clause symbols to modfile

Fix bug where the list of variables would be ignored when writing the to
clause of a declare target directive to a modfile.

to clauses are written into a modfile as enter clauses.
---
 flang/lib/Semantics/symbol.cpp                         | 9 +++++++--
 flang/test/Semantics/OpenMP/declare-target-modfile.f90 | 4 ++++
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp
index b802930326a6f..b3acd59b4a299 100644
--- a/flang/lib/Semantics/symbol.cpp
+++ b/flang/lib/Semantics/symbol.cpp
@@ -78,8 +78,13 @@ void WithOmpDeclarative::printClauseSet(llvm::raw_ostream &os,
   size_t idx{0}, size{clauses.count()};
 
   for (llvm::omp::Clause c : clauses) {
-    os << toLower(llvm::omp::getOpenMPClauseName(c, version_));
-    switch (c) {
+    llvm::omp::Clause clause = c;
+    // Write to as enter when writing a mod file
+    if (clause == llvm::omp::Clause::OMPC_to && !name.empty()) {
+      clause = llvm::omp::Clause::OMPC_enter;
+    }
+    os << toLower(llvm::omp::getOpenMPClauseName(clause, version_));
+    switch (clause) {
     case llvm::omp::Clause::OMPC_atomic_default_mem_order:
       os << '(' << toLower(EnumToString(*ompAtomicDefaultMemOrder())) << ')';
       break;
diff --git a/flang/test/Semantics/OpenMP/declare-target-modfile.f90 b/flang/test/Semantics/OpenMP/declare-target-modfile.f90
index 9a2f9a3026947..710c849ca61b4 100644
--- a/flang/test/Semantics/OpenMP/declare-target-modfile.f90
+++ b/flang/test/Semantics/OpenMP/declare-target-modfile.f90
@@ -3,6 +3,8 @@
 module m
 integer :: x
 !$omp declare_target link(x) device_type(nohost)
+integer :: y
+!$omp declare target to(y)
 real :: w(10), u(10)
 common /named_block/ w, u
 !$omp declare_target link(/named_block/)
@@ -28,6 +30,7 @@ subroutine h
 !Expect: m.mod
 !module m
 !integer(4)::x
+!integer(4)::y
 !real(4)::w(1_8:10_8)
 !real(4)::u(1_8:10_8)
 !interface
@@ -38,6 +41,7 @@ subroutine h
 !end interface
 !common/named_block/w,u
 !!$omp declare_target device_type(nohost) link(x)
+!!$omp declare_target enter(y)
 !!$omp declare_target enter(g)
 !!$omp declare_target enter(f)
 !!$omp declare_target enter(h)

>From 04671dd6fc7dc299574c5099a9bd267c09a3e1c3 Mon Sep 17 00:00:00 2001
From: Ville-Markus Yli-Suutala <ville-markus.yli-suutala at helsinki.fi>
Date: Sat, 8 Aug 2026 20:21:24 +0300
Subject: [PATCH 2/5] [flang][openmp] Support importing module declare target
 globals

Attach device type and capture clause attributes to declare target
globals that have been imported from a module file.

Fixes https://github.com/llvm/llvm-project/issues/212333
---
 flang/include/flang/Lower/OpenMP.h |  5 +++
 flang/lib/Lower/OpenMP/OpenMP.cpp  | 57 ++++++++++++++++++++++++------
 2 files changed, 52 insertions(+), 10 deletions(-)

diff --git a/flang/include/flang/Lower/OpenMP.h b/flang/include/flang/Lower/OpenMP.h
index a3f35498b9180..47f27ceece165 100644
--- a/flang/include/flang/Lower/OpenMP.h
+++ b/flang/include/flang/Lower/OpenMP.h
@@ -106,6 +106,11 @@ void materializeOpenMPDeclareMappers(
     Fortran::lower::AbstractConverter &, Fortran::semantics::SemanticsContext &,
     const Fortran::semantics::Scope *scope = nullptr);
 
+// Attach declare target attributes to declare target global variables
+// imported from a module file.
+void attachOpenMPDeclareTargetAttributes(lower::AbstractConverter &converter,
+                                         const lower::pft::Variable &var);
+
 namespace omp {
 /// If \p base carries OpenMP DECLARE VARIANT entries, return the variant symbol
 /// that best matches the enclosing OpenMP context, or nullptr if none matches.
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 7503d33c8df38..6232e6e0468fa 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -7830,8 +7830,12 @@ void Fortran::lower::genOpenMPSymbolProperties(
   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::OmpDeclareTarget)) {
+    if (var.isGlobal())
+      lower::attachOpenMPDeclareTargetAttributes(converter, var);
+    else
+      lower::genDeclareTargetIntGlobal(converter, var);
+  }
 }
 
 void Fortran::lower::genGroupprivateOp(lower::AbstractConverter &converter,
@@ -7931,14 +7935,12 @@ void Fortran::lower::genThreadprivateOp(lower::AbstractConverter &converter,
 // generation.
 void Fortran::lower::genDeclareTargetIntGlobal(
     lower::AbstractConverter &converter, const lower::pft::Variable &var) {
-  if (!var.isGlobal()) {
-    // A non-global variable which can be in a declare target directive must
-    // be a variable in the main program, and it has the implicit SAVE
-    // attribute. We create a GlobalOp for it to simplify the translation to
-    // LLVM IR.
-    globalInitialization(converter, converter.getFirOpBuilder(),
-                         var.getSymbol(), var, converter.getCurrentLocation());
-  }
+  // A non-global variable which can be in a declare target directive must
+  // be a variable in the main program, and it has the implicit SAVE
+  // attribute. We create a GlobalOp for it to simplify the translation to
+  // LLVM IR.
+  globalInitialization(converter, converter.getFirOpBuilder(), var.getSymbol(),
+                       var, converter.getCurrentLocation());
 }
 
 bool Fortran::lower::isOpenMPTargetConstruct(
@@ -8093,3 +8095,38 @@ void Fortran::lower::materializeOpenMPDeclareMappers(
 // Walk scopes and materialize omp.declare_reduction ops for user-defined
 // operator reductions imported from modules (deleted: replaced by lazy,
 // clause-driven materialization).
+
+void Fortran::lower::attachOpenMPDeclareTargetAttributes(
+    lower::AbstractConverter &converter, const lower::pft::Variable &var) {
+  auto module = converter.getModuleOp();
+
+  mlir::Operation *globalOp =
+      module.lookupSymbol(converter.mangleName(var.getSymbol()));
+
+  auto ultimateSymbol = var.getSymbol().GetUltimate();
+
+  if (globalOp && ultimateSymbol.IsFromModFile()) {
+    auto declareTargetOp =
+        llvm::dyn_cast<mlir::omp::DeclareTargetInterface>(globalOp);
+    Fortran::common::visit(
+        [&](const auto &details) {
+          if constexpr (std::is_base_of_v<semantics::WithOmpDeclarative,
+                                          std::decay_t<decltype(details)>>) {
+            mlir::omp::DeclareTargetDeviceType deviceType =
+                toMLIRDeclareTargetDeviceType(
+                    details.ompDeclTargetDeviceType().value_or(
+                        Fortran::common::OmpDeviceType::Any));
+
+            mlir::omp::DeclareTargetCaptureClause clause;
+            if (details.ompDeclTarget().test(llvm::omp::Clause::OMPC_link))
+              clause = mlir::omp::DeclareTargetCaptureClause::link;
+            else
+              clause = mlir::omp::DeclareTargetCaptureClause::enter;
+
+            declareTargetOp.setDeclareTarget(deviceType, clause,
+                                             /*automap=*/false);
+          }
+        },
+        ultimateSymbol.details());
+  }
+}

>From 140cf54d09bd18bd62be9311df12dacaa09cb4f8 Mon Sep 17 00:00:00 2001
From: Ville-Markus Yli-Suutala <ville-markus.yli-suutala at helsinki.fi>
Date: Mon, 10 Aug 2026 16:11:34 +0300
Subject: [PATCH 3/5] fixup! [flang][openmp] Support importing module declare
 target globals

---
 flang/lib/Lower/OpenMP/OpenMP.cpp             |  3 +-
 .../Lower/OpenMP/declare_target_module.f90    | 37 +++++++++++++++++++
 2 files changed, 39 insertions(+), 1 deletion(-)
 create mode 100644 flang/test/Lower/OpenMP/declare_target_module.f90

diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 6232e6e0468fa..daa4e6ecb14a2 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -8098,6 +8098,7 @@ void Fortran::lower::materializeOpenMPDeclareMappers(
 
 void Fortran::lower::attachOpenMPDeclareTargetAttributes(
     lower::AbstractConverter &converter, const lower::pft::Variable &var) {
+  assert(var.isGlobal());
   auto module = converter.getModuleOp();
 
   mlir::Operation *globalOp =
@@ -8107,7 +8108,7 @@ void Fortran::lower::attachOpenMPDeclareTargetAttributes(
 
   if (globalOp && ultimateSymbol.IsFromModFile()) {
     auto declareTargetOp =
-        llvm::dyn_cast<mlir::omp::DeclareTargetInterface>(globalOp);
+        llvm::cast<mlir::omp::DeclareTargetInterface>(globalOp);
     Fortran::common::visit(
         [&](const auto &details) {
           if constexpr (std::is_base_of_v<semantics::WithOmpDeclarative,
diff --git a/flang/test/Lower/OpenMP/declare_target_module.f90 b/flang/test/Lower/OpenMP/declare_target_module.f90
new file mode 100644
index 0000000000000..226b70e9f5137
--- /dev/null
+++ b/flang/test/Lower/OpenMP/declare_target_module.f90
@@ -0,0 +1,37 @@
+! RUN: rm -rf %t && split-file %s %t
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -module-dir %t %t/declare_target_module.f90 -o - > /dev/null
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -J %t %t/use_declare_target_module.f90 -o - | FileCheck %s
+
+!--- declare_target_module.f90
+module declare_target_module
+  implicit none
+  integer, dimension(10) :: global_arr
+  !$omp declare target (global_arr)
+  real :: global_real
+  !$omp declare target link(global_real)
+  integer :: global_integer
+  !$omp declare target to(global_integer)
+  integer :: global_device_integer
+  !$omp declare target enter(global_device_integer) device_type(nohost)
+end module
+
+!--- use_declare_target_module.f90
+module use_declare_target_module
+use declare_target_module
+implicit none
+contains
+subroutine s()
+  !$omp declare target
+  global_arr(1) = 1
+  global_real = 1.0
+  global_integer = 1
+end subroutine
+!CHECK-DAG: fir.global @_QMdeclare_target_moduleEglobal_arr {alignment = 64 : i64, omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false>} : !fir.array<10xi32>
+!CHECK-DAG: fir.global @_QMdeclare_target_moduleEglobal_real {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (link), automap = false>} : f32
+!CHECK-DAG: fir.global @_QMdeclare_target_moduleEglobal_integer {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false>} : i32
+subroutine device_s()
+  !$omp declare target enter(device_s) device_type(nohost)
+  global_device_integer = 1
+end subroutine
+!CHECK-DAG: fir.global @_QMdeclare_target_moduleEglobal_device_integer {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>} : i32
+end module

>From 31b2c15c2c6eb3126a4e8da3d275879c4ba7fbf0 Mon Sep 17 00:00:00 2001
From: Ville-Markus Yli-Suutala <ville-markus.yli-suutala at helsinki.fi>
Date: Mon, 24 Aug 2026 23:15:57 +0300
Subject: [PATCH 4/5] fixup! [flang][openmp] Support importing module declare
 target globals

---
 flang/include/flang/Lower/OpenMP.h            |   7 +-
 flang/include/flang/Semantics/semantics.h     |   4 +
 flang/lib/Lower/Bridge.cpp                    |   5 +
 flang/lib/Lower/OpenMP/OpenMP.cpp             | 102 ++++++++++--------
 .../Lower/OpenMP/declare_target_module.f90    |  19 +++-
 5 files changed, 86 insertions(+), 51 deletions(-)

diff --git a/flang/include/flang/Lower/OpenMP.h b/flang/include/flang/Lower/OpenMP.h
index 47f27ceece165..0ad4d05f63792 100644
--- a/flang/include/flang/Lower/OpenMP.h
+++ b/flang/include/flang/Lower/OpenMP.h
@@ -106,10 +106,9 @@ void materializeOpenMPDeclareMappers(
     Fortran::lower::AbstractConverter &, Fortran::semantics::SemanticsContext &,
     const Fortran::semantics::Scope *scope = nullptr);
 
-// Attach declare target attributes to declare target global variables
-// imported from a module file.
-void attachOpenMPDeclareTargetAttributes(lower::AbstractConverter &converter,
-                                         const lower::pft::Variable &var);
+void markOpenMPImportedDeclareTargets(
+    Fortran::lower::AbstractConverter &converter,
+    semantics::SemanticsContext &semaCtx);
 
 namespace omp {
 /// If \p base carries OpenMP DECLARE VARIANT entries, return the variant symbol
diff --git a/flang/include/flang/Semantics/semantics.h b/flang/include/flang/Semantics/semantics.h
index c41da2302e85b..1b9f314ab4c08 100644
--- a/flang/include/flang/Semantics/semantics.h
+++ b/flang/include/flang/Semantics/semantics.h
@@ -377,6 +377,10 @@ class SemanticsContext {
   // Top-level ProgramTrees are owned by the SemanticsContext for persistence.
   ProgramTree &SaveProgramTree(ProgramTree &&);
 
+  std::list<parser::Program> &GetModFileParseTrees() {
+    return modFileParseTrees_;
+  }
+
   // Label analysis classifies every labeled statement, and only some of those
   // classifications may be named by a statement that branches.  Lowering needs
   // the same distinction when it records the targets of a branch, so the
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 3d71a4dbf9b1b..a7aa7ebb1b79a 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -626,6 +626,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
             bridge.getLoweringOptions().getFPExceptionTraps());
       });
 
+    createBuilderOutsideOfFuncOpAndDo([&]() {
+      Fortran::lower::markOpenMPImportedDeclareTargets(
+          *this, bridge.getSemanticsContext());
+    });
+
     finalizeOpenMPLowering(globalOmpRequiresSymbols);
   }
 
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index daa4e6ecb14a2..64b85404aa648 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -18,6 +18,7 @@
 #include "Decomposer.h"
 #include "Utils.h"
 #include "flang/Common/idioms.h"
+#include "flang/Common/reference-wrapper.h"
 #include "flang/Evaluate/expression.h"
 #include "flang/Evaluate/fold.h"
 #include "flang/Evaluate/tools.h"
@@ -1496,7 +1497,7 @@ static void promoteNonCPtrUseDevicePtrArgsToUseDeviceAddr(
 /// 'declare target' directive and return the intended device type for them.
 static void getDeclareTargetInfo(
     lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,
-    lower::pft::Evaluation &eval,
+    std::optional<common::reference_wrapper<lower::pft::Evaluation>> eval,
     const parser::OmpDeclareTargetDirective &construct,
     mlir::omp::DeclareTargetOperands &clauseOps,
     llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &symbolAndClause) {
@@ -1510,8 +1511,10 @@ static void getDeclareTargetInfo(
     List<Clause> clauses = makeClauses(construct.v.Clauses(), semaCtx);
     if (clauses.empty()) {
       // Case: implicit capture of the enclosing function/subroutine.
+      assert(eval.has_value() &&
+             "expected eval to have value when clauses is empty");
       Fortran::lower::pft::FunctionLikeUnit *owningProc =
-          eval.getOwningProcedure();
+          eval->get().getOwningProcedure();
       bool owningProcNotMainProgram =
           owningProc && !owningProc->isMainProgram();
 
@@ -7830,12 +7833,8 @@ void Fortran::lower::genOpenMPSymbolProperties(
   if (sym.test(semantics::Symbol::Flag::OmpThreadprivate))
     lower::genThreadprivateOp(converter, var);
 
-  if (sym.test(semantics::Symbol::Flag::OmpDeclareTarget)) {
-    if (var.isGlobal())
-      lower::attachOpenMPDeclareTargetAttributes(converter, var);
-    else
-      lower::genDeclareTargetIntGlobal(converter, var);
-  }
+  if (sym.test(semantics::Symbol::Flag::OmpDeclareTarget))
+    lower::genDeclareTargetIntGlobal(converter, var);
 }
 
 void Fortran::lower::genGroupprivateOp(lower::AbstractConverter &converter,
@@ -7935,12 +7934,14 @@ void Fortran::lower::genThreadprivateOp(lower::AbstractConverter &converter,
 // generation.
 void Fortran::lower::genDeclareTargetIntGlobal(
     lower::AbstractConverter &converter, const lower::pft::Variable &var) {
-  // A non-global variable which can be in a declare target directive must
-  // be a variable in the main program, and it has the implicit SAVE
-  // attribute. We create a GlobalOp for it to simplify the translation to
-  // LLVM IR.
-  globalInitialization(converter, converter.getFirOpBuilder(), var.getSymbol(),
-                       var, converter.getCurrentLocation());
+  if (!var.isGlobal()) {
+    // A non-global variable which can be in a declare target directive must
+    // be a variable in the main program, and it has the implicit SAVE
+    // attribute. We create a GlobalOp for it to simplify the translation to
+    // LLVM IR.
+    globalInitialization(converter, converter.getFirOpBuilder(),
+                         var.getSymbol(), var, converter.getCurrentLocation());
+  }
 }
 
 bool Fortran::lower::isOpenMPTargetConstruct(
@@ -8096,38 +8097,53 @@ void Fortran::lower::materializeOpenMPDeclareMappers(
 // operator reductions imported from modules (deleted: replaced by lazy,
 // clause-driven materialization).
 
-void Fortran::lower::attachOpenMPDeclareTargetAttributes(
-    lower::AbstractConverter &converter, const lower::pft::Variable &var) {
-  assert(var.isGlobal());
-  auto module = converter.getModuleOp();
+// Visitor used to mark declare target globals from imported modules.
+struct ModuleDeclareTargetVisitor {
+  Fortran::lower::AbstractConverter &converter;
+  semantics::SemanticsContext &semaCtx;
 
-  mlir::Operation *globalOp =
-      module.lookupSymbol(converter.mangleName(var.getSymbol()));
+  explicit ModuleDeclareTargetVisitor(
+      Fortran::lower::AbstractConverter &converter,
+      semantics::SemanticsContext &ctx)
+      : converter(converter), semaCtx(ctx) {}
 
-  auto ultimateSymbol = var.getSymbol().GetUltimate();
+  template <typename T>
+  bool Pre(const T &) {
+    return true;
+  }
+  template <typename T>
+  void Post(const T &) {}
 
-  if (globalOp && ultimateSymbol.IsFromModFile()) {
-    auto declareTargetOp =
-        llvm::cast<mlir::omp::DeclareTargetInterface>(globalOp);
-    Fortran::common::visit(
-        [&](const auto &details) {
-          if constexpr (std::is_base_of_v<semantics::WithOmpDeclarative,
-                                          std::decay_t<decltype(details)>>) {
-            mlir::omp::DeclareTargetDeviceType deviceType =
-                toMLIRDeclareTargetDeviceType(
-                    details.ompDeclTargetDeviceType().value_or(
-                        Fortran::common::OmpDeviceType::Any));
-
-            mlir::omp::DeclareTargetCaptureClause clause;
-            if (details.ompDeclTarget().test(llvm::omp::Clause::OMPC_link))
-              clause = mlir::omp::DeclareTargetCaptureClause::link;
-            else
-              clause = mlir::omp::DeclareTargetCaptureClause::enter;
+  void Post(const parser::OmpDeclareTargetDirective &directive) {
+    mlir::omp::DeclareTargetOperands clauseOps;
+    llvm::SmallVector<DeclareTargetCaptureInfo> symbolAndClause;
+    mlir::ModuleOp mod = converter.getFirOpBuilder().getModule();
 
-            declareTargetOp.setDeclareTarget(deviceType, clause,
-                                             /*automap=*/false);
-          }
-        },
-        ultimateSymbol.details());
+    getDeclareTargetInfo(converter, semaCtx, std::nullopt, directive, clauseOps,
+                         symbolAndClause);
+
+    for (const DeclareTargetCaptureInfo &symClause : symbolAndClause) {
+      mlir::Operation *op =
+          mod.lookupSymbol(converter.mangleName(symClause.symbol));
+
+      // op not found, so nothing to mark. This happens for variables
+      // and functions that are not actually used in the current
+      // translation unit.
+      if (!op)
+        continue;
+
+      markDeclareTarget(op, converter, symClause.clause, clauseOps.deviceType,
+                        symClause.automap);
+    }
+  }
+};
+
+void Fortran::lower::markOpenMPImportedDeclareTargets(
+    Fortran::lower::AbstractConverter &converter,
+    semantics::SemanticsContext &semaCtx) {
+  std::list<parser::Program> &modTrees = semaCtx.GetModFileParseTrees();
+  ModuleDeclareTargetVisitor visitor{converter, semaCtx};
+  for (auto &modTree : modTrees) {
+    parser::Walk(modTree, visitor);
   }
 }
diff --git a/flang/test/Lower/OpenMP/declare_target_module.f90 b/flang/test/Lower/OpenMP/declare_target_module.f90
index 226b70e9f5137..3a9ec591140ea 100644
--- a/flang/test/Lower/OpenMP/declare_target_module.f90
+++ b/flang/test/Lower/OpenMP/declare_target_module.f90
@@ -13,6 +13,10 @@ module declare_target_module
   !$omp declare target to(global_integer)
   integer :: global_device_integer
   !$omp declare target enter(global_device_integer) device_type(nohost)
+  contains
+  subroutine module_s()
+    !$omp declare target
+  end subroutine
 end module
 
 !--- use_declare_target_module.f90
@@ -20,18 +24,25 @@ module use_declare_target_module
 use declare_target_module
 implicit none
 contains
+
 subroutine s()
   !$omp declare target
   global_arr(1) = 1
   global_real = 1.0
   global_integer = 1
 end subroutine
-!CHECK-DAG: fir.global @_QMdeclare_target_moduleEglobal_arr {alignment = 64 : i64, omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false>} : !fir.array<10xi32>
-!CHECK-DAG: fir.global @_QMdeclare_target_moduleEglobal_real {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (link), automap = false>} : f32
-!CHECK-DAG: fir.global @_QMdeclare_target_moduleEglobal_integer {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false>} : i32
+!CHECK-DAG: fir.global @_QMdeclare_target_moduleEglobal_arr {alignment = 64 : i64, omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter)>} : !fir.array<10xi32>
+!CHECK-DAG: fir.global @_QMdeclare_target_moduleEglobal_real {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (link)>} : f32
+!CHECK-DAG: fir.global @_QMdeclare_target_moduleEglobal_integer {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter)>} : i32
+
 subroutine device_s()
   !$omp declare target enter(device_s) device_type(nohost)
   global_device_integer = 1
 end subroutine
-!CHECK-DAG: fir.global @_QMdeclare_target_moduleEglobal_device_integer {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>} : i32
+!CHECK-DAG: fir.global @_QMdeclare_target_moduleEglobal_device_integer {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter)>} : i32
+
+subroutine call_module_s()
+call module_s()
+end subroutine
+!CHECK-DAG: func.func private @_QMdeclare_target_modulePmodule_s() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter)>}
 end module

>From 327777026780e51d7dc19417f4c358a510aaa27f Mon Sep 17 00:00:00 2001
From: Ville-Markus Yli-Suutala <ville-markus.yli-suutala at helsinki.fi>
Date: Wed, 26 Aug 2026 14:16:24 +0300
Subject: [PATCH 5/5] fixup! [flang][openmp] Support importing module declare
 target globals

---
 flang/include/flang/Lower/OpenMP.h        |  2 +
 flang/include/flang/Semantics/semantics.h |  2 +-
 flang/lib/Lower/OpenMP/OpenMP.cpp         | 77 +++++++++++------------
 3 files changed, 41 insertions(+), 40 deletions(-)

diff --git a/flang/include/flang/Lower/OpenMP.h b/flang/include/flang/Lower/OpenMP.h
index 0ad4d05f63792..863a4ebc897d4 100644
--- a/flang/include/flang/Lower/OpenMP.h
+++ b/flang/include/flang/Lower/OpenMP.h
@@ -106,6 +106,8 @@ void materializeOpenMPDeclareMappers(
     Fortran::lower::AbstractConverter &, Fortran::semantics::SemanticsContext &,
     const Fortran::semantics::Scope *scope = nullptr);
 
+// Mark declare target globals and functions that were imported from a
+// module file.
 void markOpenMPImportedDeclareTargets(
     Fortran::lower::AbstractConverter &converter,
     semantics::SemanticsContext &semaCtx);
diff --git a/flang/include/flang/Semantics/semantics.h b/flang/include/flang/Semantics/semantics.h
index 1b9f314ab4c08..b938749e756f6 100644
--- a/flang/include/flang/Semantics/semantics.h
+++ b/flang/include/flang/Semantics/semantics.h
@@ -377,7 +377,7 @@ class SemanticsContext {
   // Top-level ProgramTrees are owned by the SemanticsContext for persistence.
   ProgramTree &SaveProgramTree(ProgramTree &&);
 
-  std::list<parser::Program> &GetModFileParseTrees() {
+  const std::list<parser::Program> &GetModFileParseTrees() {
     return modFileParseTrees_;
   }
 
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 64b85404aa648..fad071c561d41 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -1855,6 +1855,31 @@ markDeclareTarget(mlir::Operation *op, lower::AbstractConverter &converter,
                                    /*implicit=*/false);
 }
 
+// Take a declare target directive, and mark the globals and functions
+// named in its clauses.
+static void markDeclareTargetWithDirective(
+    lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,
+    std::optional<common::reference_wrapper<lower::pft::Evaluation>> eval,
+    const parser::OmpDeclareTargetDirective &declareTargetConstruct) {
+  mlir::omp::DeclareTargetOperands clauseOps;
+  llvm::SmallVector<DeclareTargetCaptureInfo> symbolAndClause;
+  mlir::ModuleOp mod = converter.getFirOpBuilder().getModule();
+  getDeclareTargetInfo(converter, semaCtx, eval, declareTargetConstruct,
+                       clauseOps, symbolAndClause);
+
+  for (const DeclareTargetCaptureInfo &symClause : symbolAndClause) {
+    mlir::Operation *op =
+        mod.lookupSymbol(converter.mangleName(symClause.symbol));
+
+    // Do nothing if op is not found.
+    if (!op)
+      continue;
+
+    markDeclareTarget(op, converter, symClause.clause, clauseOps.deviceType,
+                      symClause.automap);
+  }
+}
+
 //===----------------------------------------------------------------------===//
 // Op body generation helper structures and functions
 //===----------------------------------------------------------------------===//
@@ -6777,25 +6802,12 @@ static void
 genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
        semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,
        const parser::OmpDeclareTargetDirective &declareTargetConstruct) {
-  mlir::omp::DeclareTargetOperands clauseOps;
-  llvm::SmallVector<DeclareTargetCaptureInfo> symbolAndClause;
-  mlir::ModuleOp mod = converter.getFirOpBuilder().getModule();
-  getDeclareTargetInfo(converter, semaCtx, eval, declareTargetConstruct,
-                       clauseOps, symbolAndClause);
-
-  for (const DeclareTargetCaptureInfo &symClause : symbolAndClause) {
-    mlir::Operation *op =
-        mod.lookupSymbol(converter.mangleName(symClause.symbol));
-
-    // Some symbols are deferred until later in the module, these are handled
-    // upon finalization of the module for OpenMP inside of Bridge, so we simply
-    // skip for now.
-    if (!op)
-      continue;
 
-    markDeclareTarget(op, converter, symClause.clause, clauseOps.deviceType,
-                      symClause.automap);
-  }
+  // Some symbols are deferred until later. These are skipped in
+  // markDeclareTargetWithDirective at this stage and handled later in
+  // finalizeOpenMPLowering.
+  markDeclareTargetWithDirective(converter, semaCtx, eval,
+                                 declareTargetConstruct);
 }
 
 static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
@@ -8097,6 +8109,7 @@ void Fortran::lower::materializeOpenMPDeclareMappers(
 // operator reductions imported from modules (deleted: replaced by lazy,
 // clause-driven materialization).
 
+namespace {
 // Visitor used to mark declare target globals from imported modules.
 struct ModuleDeclareTargetVisitor {
   Fortran::lower::AbstractConverter &converter;
@@ -8115,33 +8128,19 @@ struct ModuleDeclareTargetVisitor {
   void Post(const T &) {}
 
   void Post(const parser::OmpDeclareTargetDirective &directive) {
-    mlir::omp::DeclareTargetOperands clauseOps;
-    llvm::SmallVector<DeclareTargetCaptureInfo> symbolAndClause;
-    mlir::ModuleOp mod = converter.getFirOpBuilder().getModule();
-
-    getDeclareTargetInfo(converter, semaCtx, std::nullopt, directive, clauseOps,
-                         symbolAndClause);
-
-    for (const DeclareTargetCaptureInfo &symClause : symbolAndClause) {
-      mlir::Operation *op =
-          mod.lookupSymbol(converter.mangleName(symClause.symbol));
-
-      // op not found, so nothing to mark. This happens for variables
-      // and functions that are not actually used in the current
-      // translation unit.
-      if (!op)
-        continue;
-
-      markDeclareTarget(op, converter, symClause.clause, clauseOps.deviceType,
-                        symClause.automap);
-    }
+    // The directive might mention symbols that are not used in the
+    // current translation unit. Such symbols are ignored in
+    // markDeclareTargetWithDirective, as there exists no Operation
+    // that needs marking.
+    markDeclareTargetWithDirective(converter, semaCtx, std::nullopt, directive);
   }
 };
+} // namespace
 
 void Fortran::lower::markOpenMPImportedDeclareTargets(
     Fortran::lower::AbstractConverter &converter,
     semantics::SemanticsContext &semaCtx) {
-  std::list<parser::Program> &modTrees = semaCtx.GetModFileParseTrees();
+  const std::list<parser::Program> &modTrees = semaCtx.GetModFileParseTrees();
   ModuleDeclareTargetVisitor visitor{converter, semaCtx};
   for (auto &modTree : modTrees) {
     parser::Walk(modTree, visitor);



More information about the flang-commits mailing list