-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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! :-) There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
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 |
There was a problem hiding this comment.
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.