[flang-commits] [flang] [Flang][OpenMP][Semantics] Warn about non-declare target global variable usage in declare target device functions (PR #223804)

via flang-commits flang-commits at lists.llvm.org
Tue Sep 15 13:07:11 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-semantics

Author: agozillon

<details>
<summary>Changes</summary>

This PR aims to warn when a user is utilizing a global variable that has not been marked declare target inside of a declare target Device/Any function, as while these scenarios can compile and link they lead to invalid runtime behavior in most cases. Rather than hard error we simply emit a warning as a suggestion for correction as the code is technically not strictly illegal, just incorrect.

---
Full diff: https://github.com/llvm/llvm-project/pull/223804.diff


2 Files Affected:

- (modified) flang/lib/Semantics/resolve-directives.cpp (+83-4) 
- (added) flang/test/Semantics/OpenMP/declare-target-device-global-use.f90 (+42) 


``````````diff
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 143fe385768d79..b9fc8add184889 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -409,8 +409,10 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
 // Data-sharing and Data-mapping attributes for data-refs in OpenMP construct
 class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
 public:
-  explicit OmpAttributeVisitor(SemanticsContext &context)
-      : DirectiveAttributeVisitor(context) {}
+  explicit OmpAttributeVisitor(
+      SemanticsContext &context, bool warnNonDeclareTargetDeviceUses = false)
+      : DirectiveAttributeVisitor(context),
+        warnNonDeclareTargetDeviceUses_{warnNonDeclareTargetDeviceUses} {}
 
   static bool HasStaticStorageDuration(const Symbol &symbol) {
     auto &ultSym = symbol.GetUltimate();
@@ -1007,6 +1009,8 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
       Symbol::Flag::OmpCopyIn, Symbol::Flag::OmpCopyPrivate};
 
   UnorderedSymbolSet stmtFunctionExprSymbols_;
+  UnorderedSymbolSet warnedNonDeclareTargetDeviceUseSymbols_;
+  bool warnNonDeclareTargetDeviceUses_{false};
 
   enum class PartKind : int {
     // There are also other "parts", such as internal-subprogram-part, etc,
@@ -1047,6 +1051,8 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
       const std::optional<common::OmpMemoryOrderType> &);
 
   void CreateImplicitSymbols(const parser::Name &, const Symbol *symbol);
+  const Symbol *GetEnclosingNonHostDeclareTargetSubprogram(parser::CharBlock);
+  void WarnIfNonDeclareTargetDeviceUse(const parser::Name &);
 
   void AddToContextObjectWithExplicitDSA(Symbol &symbol, Symbol::Flag flag) {
     AddToContextObjectWithDSA(symbol, flag);
@@ -1072,8 +1078,10 @@ void ResolveOmpParts(
       // sequential loop (2.15.1.1) can only be determined when visiting
       // the corresponding DoConstruct, a second walk is to adjust the
       // symbols for all the data-refs of that loop iteration variable
-      // prior to the DoConstruct.
-      OmpAttributeVisitor{context}.Walk(node);
+      // prior to the DoConstruct. Emit diagnostics that require completed
+      // directive attribution during this second walk.
+      OmpAttributeVisitor{context, /*warnNonDeclareTargetDeviceUses=*/true}
+          .Walk(node);
     }
   }
 }
@@ -2933,6 +2941,22 @@ static bool IsOpenMPScalar(const Symbol &symbol) {
   return false;
 }
 
+static bool IsHostOnlyDeclareTarget(const Symbol &symbol) {
+  const Symbol &ultimate{symbol.GetUltimate()};
+  bool isHostOnly{false};
+  common::visit(
+      [&](auto &&details) {
+        using TypeD = llvm::remove_cvref_t<decltype(details)>;
+        if constexpr (std::is_base_of_v<WithOmpDeclarative, TypeD>) {
+          if (const auto &deviceType{details.ompDeclTargetDeviceType()}) {
+            isHostOnly = *deviceType == common::OmpDeviceType::Host;
+          }
+        }
+      },
+      ultimate.details());
+  return isHostOnly;
+}
+
 static bool DefaultMapCategoryMatchesSymbol(
     parser::OmpVariableCategory::Value category, const Symbol &symbol) {
   using VarCat = parser::OmpVariableCategory::Value;
@@ -2951,11 +2975,66 @@ static bool DefaultMapCategoryMatchesSymbol(
   return false;
 }
 
+// Return the enclosing subprogram if it is a declare target routine that can
+// execute on the device. Host-only declare target routines are intentionally
+// ignored.
+const Symbol *OmpAttributeVisitor::GetEnclosingNonHostDeclareTargetSubprogram(
+    parser::CharBlock source) {
+  const Scope &scope{context_.FindScope(source)};
+  if (scope.IsTopLevel()) {
+    return nullptr;
+  }
+
+  const Scope &programUnit{GetProgramUnitContaining(scope)};
+  const Symbol *symbol{programUnit.symbol()};
+  if (!symbol || !symbol->IsSubprogram()) {
+    return nullptr;
+  }
+
+  const Symbol &ultimate{symbol->GetUltimate()};
+  if (!ultimate.test(Symbol::Flag::OmpDeclareTarget) ||
+      IsHostOnlyDeclareTarget(ultimate)) {
+    return nullptr;
+  }
+
+  return &ultimate;
+}
+
+void OmpAttributeVisitor::WarnIfNonDeclareTargetDeviceUse(
+    const parser::Name &name) {
+  if (!warnNonDeclareTargetDeviceUses_ || !name.symbol) {
+    return;
+  }
+
+  const Symbol *declareTargetSubprogram{
+      GetEnclosingNonHostDeclareTargetSubprogram(name.source)};
+  if (!declareTargetSubprogram) {
+    return;
+  }
+
+  const Symbol &symbol{name.symbol->GetUltimate()};
+  if (&symbol == declareTargetSubprogram ||
+      !warnedNonDeclareTargetDeviceUseSymbols_.insert(symbol).second ||
+      symbol.test(Symbol::Flag::OmpDeclareTarget) ||
+      symbol.test(Symbol::Flag::OmpThreadprivate) ||
+      symbol.test(Symbol::Flag::CompilerCreated) || symbol.IsSubprogram() ||
+      !symbol.has<ObjectEntityDetails>() || IsNamedConstant(symbol) ||
+      IsDummy(symbol) || symbol.owner().kind() != Scope::Kind::Module) {
+    return;
+  }
+
+  context_.Warn(common::UsageWarning::OpenMPUsage, name.source,
+      "Variable '%s' is referenced from an OpenMP DECLARE TARGET procedure but is not marked DECLARE TARGET"_warn_en_US,
+      symbol.name());
+}
+
 // For OpenMP constructs, check all the data-refs within the constructs
 // and adjust the symbol for each Name if necessary
 void OmpAttributeVisitor::Post(const parser::Name &name) {
   auto *symbol{name.symbol};
 
+  WarnIfNonDeclareTargetDeviceUse(name);
+
   if (symbol && WithinConstruct()) {
     if (omp::IsPrivatizable(*symbol) && !IsObjectWithDSA(*symbol) &&
         !IsLocalInsideScope(*symbol, currScope())) {
diff --git a/flang/test/Semantics/OpenMP/declare-target-device-global-use.f90 b/flang/test/Semantics/OpenMP/declare-target-device-global-use.f90
new file mode 100644
index 00000000000000..f265ead6f37218
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/declare-target-device-global-use.f90
@@ -0,0 +1,42 @@
+! RUN: split-file %s %t
+! RUN: %flang_fc1 -fsyntax-only -fopenmp -fopenmp-version=52 -J%t %t/declare-target-device-globals.f90
+! RUN: %flang_fc1 -fsyntax-only -fopenmp -fopenmp-version=52 -J%t %t/declare-target-device-use.f90 2>&1 | FileCheck %s
+
+!--- declare-target-device-globals.f90
+module declare_target_device_globals
+  implicit none
+  real :: marked
+  real :: unmarked
+  real, parameter :: c = 1.0
+
+  !$omp declare target(marked)
+end module
+
+!--- declare-target-device-use.f90
+module declare_target_device_use
+  use declare_target_device_globals
+  implicit none
+
+contains
+  subroutine uses_marked(x)
+    !$omp declare target
+    real :: x
+    x = marked
+  end subroutine
+
+  subroutine uses_unmarked(x)
+    !$omp declare target
+    real :: x
+    real :: local
+    local = x
+    ! CHECK: warning: Variable 'unmarked' is referenced from an OpenMP DECLARE TARGET procedure but is not marked DECLARE TARGET [-Wopenmp-usage]
+    ! CHECK-NOT: warning:
+    x = unmarked + local + c
+  end subroutine
+
+  subroutine host_only_uses_unmarked(x)
+    !$omp declare target enter(host_only_uses_unmarked) device_type(host)
+    real :: x
+    x = unmarked
+  end subroutine
+end module

``````````

</details>


https://github.com/llvm/llvm-project/pull/223804


More information about the flang-commits mailing list