Skip to content

[Flang][Semantics] Allow declare target to be used on functions external to the declare targets scope #122546

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 92 additions & 2 deletions flang/lib/Semantics/resolve-names.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 &);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This dead declaration has broken several build bots (https://lab.llvm.org/buildbot/#/builders/89/builds/14834).

Please fix or revert.

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;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we find the symbol, but it's not a subroutine/function? Should we keep searching?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much @kparzysz I'll try to get to this PR and update it next week!

In this case to be prudent I've opted to make it only resolve for functions/subroutines for the moment. I was a little unsure on if we wanted to make it apply to variables as well, and the only context I can think of (my Fortran is still not the best) that it may apply is with a nested BLOCK/BLOCKs with a declare target inside the block . However, if you believe it makes more sense to apply to variables (or any other declare targettable type) as well I'd be more than happy to do so! :-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that when you find the symbol, but it's not a function/subroutine, the loop keeps going into the outer scopes. I think it should stop, since we've found something with the same name. Granted, I'm not a Fortran expert, so maybe it's possible that a function and a variable can somehow share the same name without conflict, but this part of the code got my attention.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I wasn't too sure what the ideal solution would be in those cases personally, was one of the pieces I was looking for some input on! I can try to adjust it to just break the loop if we find something that's not a function/subroutine and let the original behavior continue in those cases, we can then adjust the behavior further if we find a need for it. Do you think that would be a reasonable approach in these cases for the time being?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, let's just add a break here, and I think it's good to go.

// 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
Expand Down
41 changes: 41 additions & 0 deletions flang/test/Semantics/OpenMP/declare-target08.f90
Original file line number Diff line number Diff line change
@@ -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
Loading