[flang-commits] [flang] [flang][OpenMP] Mark declare target on use-associated module variables (PR #214596)

Spencer Bryngelson via flang-commits flang-commits at lists.llvm.org
Thu Aug 6 18:12:12 PDT 2026


https://github.com/sbryngelson updated https://github.com/llvm/llvm-project/pull/214596

>From 8ac41abc62aca70ac9863a00e41f20424d141355 Mon Sep 17 00:00:00 2001
From: Spencer Bryngelson <sbryngelson at gmail.com>
Date: Thu, 6 Aug 2026 19:31:32 -0500
Subject: [PATCH] [flang][OpenMP] Mark declare target on use-associated module
 variables

A module variable marked `!$omp declare target` keeps its OmpDeclareTarget
flag when it is use-associated into another translation unit, but no declare
target directive is parsed there, so neither markDeclareTarget call site
reaches it and the global is emitted without omp.declare_target.

HostOpFiltering then treats it as an ordinary host global and gives it
internal linkage for the device, replacing the definition with an undefined
local copy. A declare target routine in another TU reading the variable gets
garbage at -O0, and at -O2 the load folds away and the dependent store is
dropped entirely. There is no diagnostic; the program computes wrong results.

Mark the declaration from the capture clause and device type recorded on the
defining symbol. Only globals without an initializer body are marked: a global
defined in this unit is marked later by markDeclareTarget with the clauses as
written, and marking it here first would discard them, since markDeclareTarget
leaves an already marked operation alone.

automap is a modifier rather than a clause, so it is not recorded in the
symbol's clause set and cannot be recovered for a use-associated symbol.

Fixes #214586.

Assisted-by: Claude
---
 flang/lib/Lower/OpenMP/OpenMP.cpp             | 53 ++++++++++++++++++-
 .../Lower/OpenMP/declare-target-modfile.f90   | 28 ++++++++++
 2 files changed, 80 insertions(+), 1 deletion(-)
 create mode 100644 flang/test/Lower/OpenMP/declare-target-modfile.f90

diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 3876799b3a081..80756e64cb6ff 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -6906,6 +6906,55 @@ void Fortran::lower::genOpenMPDeclarativeConstruct(
   genNestedEvaluations(converter, eval);
 }
 
+/// Mark a declare target module variable that is use-associated from another
+/// unit. No directive is parsed here, so \c markDeclareTarget never reaches it
+/// and the declaration would be internalized for the device.
+static void markUseAssociatedDeclareTarget(lower::AbstractConverter &converter,
+                                           const lower::pft::Variable &var) {
+  if (!var.isGlobal())
+    return;
+
+  const semantics::Symbol &ultimate = var.getSymbol().GetUltimate();
+  const auto *details = ultimate.detailsIf<semantics::ObjectEntityDetails>();
+  if (!details)
+    return;
+
+  const auto &clauses = details->ompDeclTarget();
+  if (!clauses.count())
+    return;
+
+  mlir::ModuleOp mod = converter.getFirOpBuilder().getModule();
+  mlir::Operation *op = mod.lookupSymbol(converter.mangleName(ultimate));
+  if (!op)
+    return;
+
+  auto declareTargetOp = llvm::dyn_cast<mlir::omp::DeclareTargetInterface>(op);
+  if (!declareTargetOp || declareTargetOp.isDeclareTarget())
+    return;
+
+  // Declarations only: a definition is marked later by markDeclareTarget with
+  // the clauses as written, and it skips an already marked operation.
+  for (mlir::Region &region : op->getRegions())
+    if (!region.empty())
+      return;
+
+  // `enter` and `to` lower identically.
+  mlir::omp::DeclareTargetCaptureClause captureClause =
+      clauses.test(llvm::omp::Clause::OMPC_link)
+          ? mlir::omp::DeclareTargetCaptureClause::link
+          : mlir::omp::DeclareTargetCaptureClause::to;
+
+  mlir::omp::DeclareTargetDeviceType deviceType =
+      mlir::omp::DeclareTargetDeviceType::any;
+  if (const std::optional<common::OmpDeviceType> &dt =
+          details->ompDeclTargetDeviceType())
+    deviceType = toMLIRDeclareTargetDeviceType(*dt);
+
+  // automap is a modifier, not a clause, so it is not recoverable here.
+  declareTargetOp.setDeclareTarget(deviceType, captureClause,
+                                   /*automap=*/false);
+}
+
 void Fortran::lower::genOpenMPSymbolProperties(
     lower::AbstractConverter &converter, const lower::pft::Variable &var) {
   assert(var.hasSymbol() && "Expecting Symbol");
@@ -6917,8 +6966,10 @@ void Fortran::lower::genOpenMPSymbolProperties(
   if (sym.test(semantics::Symbol::Flag::OmpThreadprivate))
     lower::genThreadprivateOp(converter, var);
 
-  if (sym.test(semantics::Symbol::Flag::OmpDeclareTarget))
+  if (sym.test(semantics::Symbol::Flag::OmpDeclareTarget)) {
     lower::genDeclareTargetIntGlobal(converter, var);
+    markUseAssociatedDeclareTarget(converter, var);
+  }
 }
 
 void Fortran::lower::genGroupprivateOp(lower::AbstractConverter &converter,
diff --git a/flang/test/Lower/OpenMP/declare-target-modfile.f90 b/flang/test/Lower/OpenMP/declare-target-modfile.f90
new file mode 100644
index 0000000000000..c98f1a76ea76d
--- /dev/null
+++ b/flang/test/Lower/OpenMP/declare-target-modfile.f90
@@ -0,0 +1,28 @@
+! Cross-TU propagation of `declare target` on a module variable via .mod files.
+
+! RUN: rm -rf %t && split-file %s %t
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -module-dir %t %t/m.f90 -o - > /dev/null
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -J %t %t/use.f90 -o - | FileCheck %s
+
+! The consumer only USE-associates the module. Its declaration must still carry
+! omp.declare_target, recovered from the .mod file, or it is internalized for
+! the device and the definition is lost.
+
+!--- m.f90
+module dt_mod
+  implicit none
+  integer :: dt_x
+  !$omp declare target(dt_x)
+end module dt_mod
+
+!--- use.f90
+subroutine use_dt_mod(out)
+  use dt_mod
+  implicit none
+  integer, intent(out) :: out
+  !$omp target map(tofrom: out)
+    out = dt_x
+  !$omp end target
+end subroutine use_dt_mod
+
+! CHECK: fir.global @_QMdt_modEdt_x {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to), automap = false>} : i32



More information about the flang-commits mailing list