[flang-commits] [flang] [Flang][Semantics] Allow declare target to be used on functions external to the declare targets scope (PR #122546)
via flang-commits
flang-commits at lists.llvm.org
Mon Jan 20 08:20:18 PST 2025
https://github.com/agozillon updated https://github.com/llvm/llvm-project/pull/122546
>From 180641e47f69172dc87beeb0d141cf0a784e4916 Mon Sep 17 00:00:00 2001
From: agozillon <Andrew.Gozillon at amd.com>
Date: Fri, 10 Jan 2025 13:41:07 -0600
Subject: [PATCH] [Flang][Semantics] Allow declare target to be used on
functions external to the declare targets scope
Whilst a little contrived, OpenMP allows you to utilise declare target in the scope of
one function to mark another function declare target, currently this leads to a semantic
error.
This appears to be because when we process the declare target directive in the scope
of another function (referring to another function), we do not search externally from
that functions scope to find possible prior definitions, we only search in the current
scope, this leads to us implicitly defining a new variable and using that when implicit
none is not specified and then error'ng out or error'ng out earlier when implict none is
defined. This patch tries to address this behaviour by looking externally for a function
first and using that, before defaulting back to the prior behaviour.
---
flang/lib/Semantics/resolve-names.cpp | 94 ++++++++++++++++++-
.../Semantics/OpenMP/declare-target08.f90 | 41 ++++++++
2 files changed, 133 insertions(+), 2 deletions(-)
create mode 100644 flang/test/Semantics/OpenMP/declare-target08.f90
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index f3c2a5bf094d04b..9857ae61939d7cc 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -736,6 +736,8 @@ class ScopeHandler : public ImplicitRulesVisitor {
std::vector<const std::list<parser::EquivalenceObject> *> equivalenceSets;
// Names of all common block objects in the scope
std::set<SourceName> commonBlockObjects;
+ // Names of all names that show in a declare target declaration
+ std::set<SourceName> declareTargetNames;
// Info about SAVE statements and attributes in current scope
struct {
std::optional<SourceName> saveAll; // "SAVE" without entity list
@@ -1223,6 +1225,7 @@ class DeclarationVisitor : public ArraySpecVisitor,
const parser::Name *FindComponent(const parser::Name *, const parser::Name &);
void Initialization(const parser::Name &, const parser::Initialization &,
bool inComponentDecl);
+ bool FindAndMarkDeclareTargetSymbol(const parser::Name &);
bool PassesLocalityChecks(
const parser::Name &name, Symbol &symbol, Symbol::Flag flag);
bool CheckForHostAssociatedImplicit(const parser::Name &);
@@ -1524,7 +1527,47 @@ class OmpVisitor : public virtual DeclarationVisitor {
return true;
}
void Post(const parser::OpenMPThreadprivate &) { SkipImplicitTyping(false); }
- bool Pre(const parser::OpenMPDeclareTargetConstruct &) {
+ bool Pre(const parser::OpenMPDeclareTargetConstruct &x) {
+ const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)};
+ auto populateDeclareTargetNames{
+ [this](const parser::OmpObjectList &objectList) {
+ for (const auto &ompObject : objectList.v) {
+ common::visit(
+ common::visitors{
+ [&](const parser::Designator &designator) {
+ if (const auto *name{
+ semantics::getDesignatorNameIfDataRef(
+ designator)}) {
+ specPartState_.declareTargetNames.insert(name->source);
+ }
+ },
+ [&](const parser::Name &name) {
+ specPartState_.declareTargetNames.insert(name.source);
+ },
+ },
+ ompObject.u);
+ }
+ }};
+
+ if (const auto *objectList{parser::Unwrap<parser::OmpObjectList>(spec.u)}) {
+ populateDeclareTargetNames(*objectList);
+ } else if (const auto *clauseList{
+ parser::Unwrap<parser::OmpClauseList>(spec.u)}) {
+ for (const auto &clause : clauseList->v) {
+ if (const auto *toClause{
+ std::get_if<parser::OmpClause::To>(&clause.u)}) {
+ populateDeclareTargetNames(
+ std::get<parser::OmpObjectList>(toClause->v.t));
+ } else if (const auto *linkClause{
+ std::get_if<parser::OmpClause::Link>(&clause.u)}) {
+ populateDeclareTargetNames(linkClause->v);
+ } else if (const auto *enterClause{
+ std::get_if<parser::OmpClause::Enter>(&clause.u)}) {
+ populateDeclareTargetNames(enterClause->v);
+ }
+ }
+ }
+
SkipImplicitTyping(true);
return true;
}
@@ -8126,7 +8169,12 @@ const parser::Name *DeclarationVisitor::ResolveDataRef(
// If implicit types are allowed, ensure name is in the symbol table.
// Otherwise, report an error if it hasn't been declared.
const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
- FindSymbol(name);
+ if (!FindSymbol(name)) {
+ if (FindAndMarkDeclareTargetSymbol(name)) {
+ return &name;
+ }
+ }
+
if (CheckForHostAssociatedImplicit(name)) {
NotePossibleBadForwardRef(name);
return &name;
@@ -8313,6 +8361,48 @@ const parser::Name *DeclarationVisitor::FindComponent(
return nullptr;
}
+bool DeclarationVisitor::FindAndMarkDeclareTargetSymbol(
+ const parser::Name &name) {
+ if (!specPartState_.declareTargetNames.empty()) {
+ if (specPartState_.declareTargetNames.count(name.source)) {
+ if (!currScope().IsTopLevel()) {
+ // Search preceding scopes until we find a matching symbol or run out
+ // of scopes to search, we skip the current scope as it's already been
+ // designated as implicit here.
+ Symbol *symbol = nullptr;
+ for (auto *scope = &currScope().parent();; scope = &scope->parent()) {
+ if (Symbol * symbol{scope->FindSymbol(name.source)}) {
+ if (symbol->test(Symbol::Flag::Subroutine) ||
+ symbol->test(Symbol::Flag::Function)) {
+ const auto [sym, success]{currScope().try_emplace(
+ symbol->name(), Attrs{}, HostAssocDetails{*symbol})};
+ assert(success &&
+ "FindAndMarkDeclareTargetSymbol could not emplace new "
+ "subroutine/function symbol");
+ name.symbol = &*sym->second;
+ symbol->test(Symbol::Flag::Subroutine)
+ ? name.symbol->set(Symbol::Flag::Subroutine)
+ : name.symbol->set(Symbol::Flag::Function);
+ return true;
+ }
+ // if we find a symbol that is not a function or subroutine, we
+ // currently escape without doing anything.
+ break;
+ }
+
+ // This is our loop exit condition, as parent() has an inbuilt assert
+ // if you call it on a top level scope, rather than returning a null
+ // value.
+ if (scope->IsTopLevel()) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
void DeclarationVisitor::Initialization(const parser::Name &name,
const parser::Initialization &init, bool inComponentDecl) {
// Traversal of the initializer was deferred to here so that the
diff --git a/flang/test/Semantics/OpenMP/declare-target08.f90 b/flang/test/Semantics/OpenMP/declare-target08.f90
new file mode 100644
index 000000000000000..1438d79d373482b
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/declare-target08.f90
@@ -0,0 +1,41 @@
+! RUN: %flang_fc1 -fopenmp -fdebug-dump-symbols %s | FileCheck %s
+
+subroutine bar(i, a)
+ !$omp declare target
+ real :: a
+ integer :: i
+ a = a - i
+end subroutine
+
+function baz(a)
+ !$omp declare target
+ real, intent(in) :: a
+ baz = a
+end function baz
+
+program main
+real a
+!CHECK: bar (Subroutine, OmpDeclareTarget): HostAssoc
+!CHECK: baz (Function, OmpDeclareTarget): HostAssoc
+!$omp declare target(bar)
+!$omp declare target(baz)
+
+a = baz(a)
+call bar(2,a)
+call foo(a)
+return
+end
+
+subroutine foo(a)
+real a
+integer i
+!CHECK: bar (Subroutine, OmpDeclareTarget): HostAssoc
+!CHECK: baz (Function, OmpDeclareTarget): HostAssoc
+!$omp declare target(bar)
+!$omp declare target(baz)
+!$omp target
+ a = baz(a)
+ call bar(i,a)
+!$omp end target
+return
+end
More information about the flang-commits
mailing list