[flang-commits] [flang] 8f5df88 - [Flang][Semantics] Allow declare target to be used on functions external to the declare targets scope (#122546)
via flang-commits
flang-commits at lists.llvm.org
Tue Jan 21 07:05:16 PST 2025
Author: agozillon
Date: 2025-01-21T16:05:12+01:00
New Revision: 8f5df8891840bb698fec682c1d98346708c038be
URL: https://github.com/llvm/llvm-project/commit/8f5df8891840bb698fec682c1d98346708c038be
DIFF: https://github.com/llvm/llvm-project/commit/8f5df8891840bb698fec682c1d98346708c038be.diff
LOG: [Flang][Semantics] Allow declare target to be used on functions external to the declare targets scope (#122546)
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.
Added:
flang/test/Semantics/OpenMP/declare-target08.f90
Modified:
flang/lib/Semantics/resolve-names.cpp
Removed:
################################################################################
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index f3c2a5bf094d04..9857ae61939d7c 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 00000000000000..1438d79d373482
--- /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