- Notifications
You must be signed in to change notification settings - Fork 14.9k
[flang][OpenACC] Relax COMMON block usage restriction in OpenACC directives #162659
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
base: main
Are you sure you want to change the base?
Conversation
for ACC directives.
@llvm/pr-subscribers-flang-semantics @llvm/pr-subscribers-openacc Author: Eugene Epshteyn (eugeneepshteyn) ChangesUnlike OpenMP, OpenACC doesn't require that the COMMON block be defined in the same scope as the directive. Full diff: https://github.com/llvm/llvm-project/pull/162659.diff 2 Files Affected:
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index 18fc63814d973..44cfb75e06164 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -1695,17 +1695,23 @@ void AccAttributeVisitor::Post(const parser::Name &name) { Symbol *AccAttributeVisitor::ResolveAccCommonBlockName( const parser::Name *name) { - if (auto *prev{name - ? GetContext().scope.parent().FindCommonBlock(name->source) - : nullptr}) { - name->symbol = prev; - return prev; - } - // Check if the Common Block is declared in the current scope - if (auto *commonBlockSymbol{ - name ? GetContext().scope.FindCommonBlock(name->source) : nullptr}) { - name->symbol = commonBlockSymbol; - return commonBlockSymbol; + if (!name) { + return nullptr; + } + // Check the local and surrounding scopes first + if (auto *cb{GetProgramUnitOrBlockConstructContaining(GetContext().scope) + .FindCommonBlock(name->source)}) { + name->symbol = cb; + return cb; + } + // Look for COMMON block in the modules + for (const Scope &childScope : context_.globalScope().children()) { + if (childScope.kind() == Scope::Kind::Module) { + if (auto *cb{childScope.FindCommonBlock(name->source)}) { + name->symbol = cb; + return cb; + } + } } return nullptr; } @@ -1755,8 +1761,8 @@ void AccAttributeVisitor::ResolveAccObject( } } else { context_.Say(name.source, - "COMMON block must be declared in the same scoping unit " - "in which the OpenACC directive or clause appears"_err_en_US); + "Could not find COMMON block '%s' used in OpenACC directive"_err_en_US, + name.ToString()); } }, }, diff --git a/flang/test/Semantics/OpenACC/acc-common.f90 b/flang/test/Semantics/OpenACC/acc-common.f90 new file mode 100644 index 0000000000000..c6142bdb915f6 --- /dev/null +++ b/flang/test/Semantics/OpenACC/acc-common.f90 @@ -0,0 +1,19 @@ +! RUN: %python %S/../test_errors.py %s %flang -fopenacc +module acc_common_decl + implicit none + integer a + common /a_common/ a +!$acc declare create (/a_common/) + data a/42/ +end module acc_common_decl + +program acc_decl_test + use acc_common_decl + implicit none + + a = 1 +!$acc update device (/a_common/) + a = 2 +!ERROR: Could not find COMMON block 'a_common_bad' used in OpenACC directive +!$acc update device (/a_common_bad/) +end program |
name->symbol = cb; | ||
return cb; | ||
} | ||
// Look for COMMON block in the modules |
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.
Are you sure that this part makes sense? Do you have an example where NVF looks at non-USE-associated modules? You probably just want to look for a USE-associated common block in the enclosing scopes instead.
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.
It would have been very convenient to have a USE-associated common block available, but I don't see one created. With this program
module acc_decl implicit none integer a common /a_common/ a !$acc declare create (/a_common/) data a/42/ end module acc_decl program acc_decl_test use acc_decl implicit none a = 1 !$acc update device (/a_common/) a = 2 end program
... and -fdebug-dump-symbols
I only see
MainProgram scope: ACC_DECL_TEST size=0 alignment=1 sourceRange=72 bytes a (InDataStmt, InCommonBlock): Use from a in acc_decl
Perhaps this is the problem? Perhaps we should be creating USE-associated symbol from its use in !$acc update device (/a_common/)
?
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.
Yes, I think so.
if (!name) { | ||
return nullptr; | ||
} | ||
// Check the local and surrounding scopes first |
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 will only check the innermost BLOCK
construct or subprogram, not any containing construct or host subprogram.
…. (Currently the code doesn't fix ACC COMMON problem.)
…This resolves OpenMP regressions with COMMON. Still working on USE-association for COMMON blocks
| ||
/// Find COMMON block in current and surrounding scopes, follow USE | ||
/// associations | ||
Symbol *FindCommonBlockInScopes(const SourceName &) const; |
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.
The name could be more precise so that it's immediately understandable at call sites.
…ent scope and COMMON with USE details
| ||
// Find COMMON block in current and surrounding scopes, follow USE | ||
// associations | ||
Symbol *FindCommonBlockInSurroundingScopes(const SourceName &) const; |
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.
The name isn't consistent with the comment; will this member function detect a common block in this scope, or only in its parents?
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.
I would appreciate suggestion for a better name, otherwise it'll be FindCommonBlockInCurrentAndSurroundingScopes()
:-)
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.
Maybe FindCommonBlockInLexicalScopes
or VisibleScopes
.
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.
I went with FindCommonBlockInVisibleScopes()
std::list<Scope> children_; | ||
mapType symbols_; | ||
mapType commonBlocks_; | ||
mapType commonBlockUses_; // USE-assocated COMMON blocks |
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.
Is this data member necessary for correctness?
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.
Having the data member makes it more convenient and avoid disturbing the code that assumes that commonBlocks_
only contain symbols with CommonBlockDetails
. The previous iteration of this PR had guards around various places to ignore UseDetails
and that seemed inelegant.
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.
commonBlocks_
has to exist because their names are in a different namespace ("class" in standard-speak) than normal names. commonBlockUses_
symbols are already in the normal symbol map, so this doesn't seem like necessary new state.
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.
The uses of COMMON block variables are in the normal symbol map, e.g. for this code
module mod_a integer :: a common /mod_a_common/ a end module mod_a module mod_b use mod_a integer :: b common /mod_b_common/ b end module mod_b use mod_b a = 1 b = 2 end
... the symbol table looks like
Module scope: mod_a size=0 alignment=1 sourceRange=66 bytes a, PUBLIC (InCommonBlock) size=4 offset=0: ObjectEntity type: INTEGER(4) mod_a_common size=4 offset=0: CommonBlockDetails alignment=4: a Module scope: mod_b size=0 alignment=1 sourceRange=76 bytes a, PUBLIC (InCommonBlock): Use from a in mod_a b, PUBLIC (InCommonBlock) size=4 offset=0: ObjectEntity type: INTEGER(4) mod_b_common size=4 offset=0: CommonBlockDetails alignment=4: b MainProgram scope: size=0 alignment=1 sourceRange=25 bytes a (InCommonBlock): Use from a in mod_b b (InCommonBlock): Use from b in mod_b
Are you suggesting that mod_a_common
use should go into the normal symbol map, together with it's common variable a
use?
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.
I think I had a bad assumption there about the common block USE associations being in the normal symbol map. No, they aren't, and they can't be. So the new data member seems necessary.
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.
Well, "necessary" is probably too strong; you could rediscover those common blocks with USE-associated members pretty easily, but doing it just once (at the right time) and saving the results is probably best.
✅ With the latest revision this PR passed the C/C++ code formatter. |
Symbol &MakeCommonBlock(SourceName, SourceName location); | ||
Symbol *FindCommonBlock(const SourceName &) const; | ||
bool AddCommonBlockUse(const SourceName &name, Symbol &cbSymbol); | ||
mapType &commonBlockUses() { return commonBlockUses_; } |
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.
Consider adding a const
accessor as well, and moving this up to be with the other accessors.
name ? GetContext().scope.FindCommonBlock(name->source) : nullptr}) { | ||
name->symbol = commonBlockSymbol; | ||
return commonBlockSymbol; | ||
if (!name) { |
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 would be more clear as if (name) { ...
, I think.
if (!name) { | ||
return nullptr; | ||
} | ||
if (auto *cb{ |
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.
Please use const
for pointers and references that are auto
, and maybe const Symbol *
would be even clearer.
if (!currScope().FindCommonBlockInVisibleScopes(name)) { | ||
// Make a symbol, but don't add it to the Scope, since it needs to | ||
// be added to the USE-associated COMMON blocks | ||
Symbol *localCB{&currScope().MakeSymbol( |
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 variable doesn't have to be a pointer. MakeSymbol
returns a reference, and you can declare localCB
to be a reference that you can then just pass directly to AddCommonBlockUse
as such.
} | ||
} | ||
} | ||
// Go through the list of COMMON block symbols in the module scope and add |
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.
What happens when the same COMMON block name arrives from multiple modules?
Unlike OpenMP, OpenACC doesn't require that the COMMON block be defined in the same scope as the directive.