[flang][OpenMP] Privatize vars referenced in statement functions by luporl · Pull Request #103390 · llvm/llvm-project (original) (raw)
@llvm/pr-subscribers-flang-fir-hlfir
@llvm/pr-subscribers-flang-semantics
@llvm/pr-subscribers-flang-openmp
Author: Leandro Lupori (luporl)
Changes
Variables referenced in the body of statement functions need to be
handled as if they are explicitly referenced. Otherwise, they are
skipped during implicit privatization, because statement functions
are represented as procedures in the parse tree.
To avoid missing symbols referenced only in statement functions
during implicit privatization, new symbols, associated with them,
are created and inserted into the context of the directive that
privatizes them. They are later collected and processed in
lowering. To avoid confusing these new symbols with regular ones,
they are tagged with the new OmpFromStmtFunction flag.
Fixes #74273
Full diff: https://github.com/llvm/llvm-project/pull/103390.diff
4 Files Affected:
- (modified) flang/include/flang/Semantics/symbol.h (+1-1)
- (modified) flang/lib/Lower/OpenMP/DataSharingProcessor.cpp (+9)
- (modified) flang/lib/Semantics/resolve-directives.cpp (+87-25)
- (added) flang/test/Lower/OpenMP/statement-function.f90 (+43)
diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h index cf0350735b5b9..b4db6689a9427 100644 --- a/flang/include/flang/Semantics/symbol.h +++ b/flang/include/flang/Semantics/symbol.h @@ -755,7 +755,7 @@ class Symbol { OmpDeclarativeAllocateDirective, OmpExecutableAllocateDirective, OmpDeclareSimd, OmpDeclareTarget, OmpThreadprivate, OmpDeclareReduction, OmpFlushed, OmpCriticalLock, OmpIfSpecified, OmpNone, OmpPreDetermined,
OmpImplicit);
OmpImplicit, OmpFromStmtFunction);using Flags = common::EnumSet<Flag, Flag_enumSize>;
const Scope &owner() const { return *owner_; }
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp index e1a193edc004a..1b2f926e21bed 100644 --- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp +++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp @@ -402,6 +402,15 @@ void DataSharingProcessor::collectSymbols( /collectSymbols=/true, /collectHostAssociatedSymbols=/true);
- // Add implicitly referenced symbols from statement functions.
- if (curScope) {
- for (const auto &sym : curScope->GetSymbols()) {
if (sym->test(semantics::Symbol::Flag::OmpFromStmtFunction) &&sym->test(flag))allSymbols.insert(&*sym);- }
- }
- llvm::SetVector<const semantics::Symbol *> symbolsInNestedRegions; collectSymbolsInNestedRegions(eval, flag, symbolsInNestedRegions);
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index d635a7b8b7874..04642aa4e0279 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -91,11 +91,12 @@ template class DirectiveAttributeVisitor { void SetContextAssociatedLoopLevel(std::int64_t level) { GetContext().associatedLoopLevel = level; }
- Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev, Scope &scope) {
- Symbol &MakeAssocSymbol(
const auto pair{scope.try_emplace(name, Attrs{}, HostAssocDetails{prev})}; return *pair.first->second; }const SourceName &name, const Symbol &prev, Scope &scope) {
- Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev) {
- Symbol &MakeAssocSymbol(const SourceName &name, const Symbol &prev) { return MakeAssocSymbol(name, prev, currScope()); } void AddDataSharingAttributeObject(SymbolRef object) { @@ -108,6 +109,7 @@ template class DirectiveAttributeVisitor { const parser::Name *GetLoopIndex(const parser::DoConstruct &); const parser::DoConstruct *GetDoConstructIf( const parser::ExecutionPartConstruct &);
- Symbol *DeclareNewPrivateAccessEntity(const Symbol &, Symbol::Flag, Scope &); Symbol *DeclarePrivateAccessEntity( const parser::Name &, Symbol::Flag, Scope &); Symbol *DeclarePrivateAccessEntity(Symbol &, Symbol::Flag, Scope &); @@ -771,6 +773,19 @@ const parser::DoConstruct *DirectiveAttributeVisitor::GetDoConstructIf( return parser::Unwrapparser::DoConstruct(x); }
+template +Symbol *DirectiveAttributeVisitor::DeclareNewPrivateAccessEntity(
- const Symbol &object, Symbol::Flag flag, Scope &scope) {
- assert(object.owner() != currScope());
- auto &symbol{MakeAssocSymbol(object.name(), object, scope)};
- symbol.set(flag);
- if (flag == Symbol::Flag::OmpCopyIn) {
- // The symbol in copyin clause must be threadprivate entity.
- symbol.set(Symbol::Flag::OmpThreadprivate);
- }
- return &symbol; +}
- template Symbol *DirectiveAttributeVisitor::DeclarePrivateAccessEntity( const parser::Name &name, Symbol::Flag flag, Scope &scope) {
@@ -785,13 +800,7 @@ template Symbol *DirectiveAttributeVisitor::DeclarePrivateAccessEntity( Symbol &object, Symbol::Flag flag, Scope &scope) { if (object.owner() != currScope()) {
- auto &symbol{MakeAssocSymbol(object.name(), object, scope)};
- symbol.set(flag);
- if (flag == Symbol::Flag::OmpCopyIn) {
// The symbol in copyin clause must be threadprivate entity.symbol.set(Symbol::Flag::OmpThreadprivate);- }
- return &symbol;
- return DeclareNewPrivateAccessEntity(object, flag, scope);
} else { object.set(flag); return &object; @@ -2075,13 +2084,30 @@ void OmpAttributeVisitor::Post(const parser::Name &name) { if (found->test(semantics::Symbol::Flag::OmpThreadprivate)) return; }
- if (!IsPrivatizable(symbol)) {
std::set<const Symbol *> stmtFunctionSymbols;
if (auto *stmtFunction{symbol->detailsIfsemantics::SubprogramDetails()};
stmtFunction && stmtFunction->stmtFunction()) {// Each non-dummy argument from a statement function must be handled too,// as if it was explicitly referenced.semantics::UnorderedSymbolSet symbols{CollectSymbols(stmtFunction->stmtFunction().value())};for (const auto &sym : symbols) {if (!IsStmtFunctionDummy(sym) && IsPrivatizable(&*sym) &&!IsObjectWithDSA(*sym)) {stmtFunctionSymbols.insert(&*sym);}}if (stmtFunctionSymbols.empty()) {return;}} else if (!IsPrivatizable(symbol)) { return; }
// Implicitly determined DSAs // OMP 5.2 5.1.1 - Variables Referenced in a Construct
- Symbol *lastDeclSymbol = nullptr;
- std::vector<const Symbol *> lastDeclSymbols; std::optionalSymbol::Flag prevDSA; for (int dirDepth{0}; dirDepth < (int)dirContext_.size(); ++dirDepth) { DirContext &dirContext = dirContext_[dirDepth]; @@ -2126,23 +2152,59 @@ void OmpAttributeVisitor::Post(const parser::Name &name) { // it would have the private flag set. // This would make x appear to be defined in p2, causing it to be // privatized in p2 and its privatization in p1 to be skipped.
auto makePrivateSymbol = [&](Symbol::Flag flag) {Symbol *hostSymbol =lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate();lastDeclSymbol = DeclarePrivateAccessEntity(*hostSymbol, flag, context_.FindScope(dirContext.directiveSource));return lastDeclSymbol;
// TODO Move the lambda functions below to a separate class.auto hostSymbol = [&](const Symbol *sym, int index = 0) {if (lastDeclSymbols.empty())return &sym->GetUltimate();return lastDeclSymbols[index];};auto declNewPrivateSymbol = [&](const Symbol *sym, Symbol::Flag flag,bool implicit) {Symbol *newSym = DeclareNewPrivateAccessEntity(*sym, flag, context_.FindScope(dirContext.directiveSource));if (implicit)newSym->set(Symbol::Flag::OmpImplicit);return newSym;};auto makePrivateSymbol = [&](Symbol::Flag flag, bool implicit = false) {bool hasLastDeclSymbols = !lastDeclSymbols.empty();auto updateLastDeclSymbols = [&](const Symbol *sym, int index = 0) {if (hasLastDeclSymbols)lastDeclSymbols[index] = sym;elselastDeclSymbols.push_back(sym);};if (stmtFunctionSymbols.empty()) {const Symbol *newSym =declNewPrivateSymbol(hostSymbol(symbol), flag, implicit);updateLastDeclSymbols(newSym);return;}int i = 0;for (const auto *sym : stmtFunctionSymbols) {Symbol *newSym =declNewPrivateSymbol(hostSymbol(sym, i), flag, implicit);newSym->set(Symbol::Flag::OmpFromStmtFunction);updateLastDeclSymbols(newSym, i++);} }; auto makeSharedSymbol = [&]() {
Symbol *hostSymbol =lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate();MakeAssocSymbol(symbol->name(), *hostSymbol,context_.FindScope(dirContext.directiveSource));
if (stmtFunctionSymbols.empty()) {MakeAssocSymbol(symbol->name(), *hostSymbol(symbol),context_.FindScope(dirContext.directiveSource));} else {int i = 0;for (const auto *sym : stmtFunctionSymbols) {MakeAssocSymbol(sym->name(), *hostSymbol(sym, i++),context_.FindScope(dirContext.directiveSource));}} }; auto useLastDeclSymbol = [&]() {
if (lastDeclSymbol)MakeAssocSymbol(symbol->name(), *lastDeclSymbol,context_.FindScope(dirContext.directiveSource));
if (!lastDeclSymbols.empty())makeSharedSymbol(); }; bool taskGenDir = llvm::omp::taskGeneratingSet.test(dirContext.directive);
@@ -2190,7 +2252,7 @@ void OmpAttributeVisitor::Post(const parser::Name &name) { } else { // 7) firstprivate dsa = Symbol::Flag::OmpFirstPrivate;
makePrivateSymbol(*dsa)->set(Symbol::Flag::OmpImplicit);
makePrivateSymbol(*dsa, /*implicit=*/true); } } prevDSA = dsa;
diff --git a/flang/test/Lower/OpenMP/statement-function.f90 b/flang/test/Lower/OpenMP/statement-function.f90 new file mode 100644 index 0000000000000..6cdbcb6e141c7 --- /dev/null +++ b/flang/test/Lower/OpenMP/statement-function.f90 @@ -0,0 +1,43 @@ +! Test privatization within OpenMP constructs containing statement functions. +! RUN: %flang_fc1 -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s + +!CHECK-LABEL: func @_QPtest_implicit_use +!CHECK: %[[IEXP:.]]:2 = hlfir.declare %{{.}} {uniq_name = "_QFtest_implicit_useEiexp"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[IIMP:.]]:2 = hlfir.declare %{{.}} {uniq_name = "_QFtest_implicit_useEiimp"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: omp.parallel private({{.firstprivate.}} %[[IEXP]]#0 -> %[[PRIV_IEXP:.]] : !fir.ref, +!CHECK-SAME: {{.firstprivate.}} %[[IIMP]]#0 -> %[[PRIV_IIMP:.]] : !fir.ref) +!CHECK: %{{.}}:2 = hlfir.declare %[[PRIV_IEXP]] {uniq_name = "_QFtest_implicit_useEiexp"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %{{.}}:2 = hlfir.declare %[[PRIV_IIMP]] {uniq_name = "_QFtest_implicit_useEiimp"} : (!fir.ref) -> (!fir.ref, !fir.ref) +subroutine test_implicit_use()
- implicit none
- integer :: iexp, iimp
- integer, external :: ifun
- integer :: sf
- sf(iexp)=ifun(iimp)+iexp
- !$omp parallel default(firstprivate)
iexp = sf(iexp)- !$omp end parallel +end subroutine
- +!CHECK-LABEL: func @_QPtest_implicit_use2 +!CHECK: %[[IEXP:.]]:2 = hlfir.declare %{{.}} {uniq_name = "_QFtest_implicit_use2Eiexp"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[IIMP:.]]:2 = hlfir.declare %{{.}} {uniq_name = "_QFtest_implicit_use2Eiimp"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: omp.task +!CHECK: %[[PRIV_IEXP:.]]:2 = hlfir.declare %{{.}} {uniq_name = "_QFtest_implicit_use2Eiexp"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[TEMP0:.]] = fir.load %[[IEXP]]#0 : !fir.ref +!CHECK: hlfir.assign %[[TEMP0]] to %[[PRIV_IEXP]]#0 temporary_lhs : i32, !fir.ref +!CHECK: %[[PRIV_IIMP:.]]:2 = hlfir.declare %{{.}} {uniq_name = "_QFtest_implicit_use2Eiimp"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[TEMP1:.]] = fir.load %[[IIMP]]#0 : !fir.ref +!CHECK: hlfir.assign %[[TEMP1]] to %[[PRIV_IIMP]]#0 temporary_lhs : i32, !fir.ref +subroutine test_implicit_use2()
- implicit none
- integer :: iexp, iimp
- integer, external :: ifun
- integer :: sf
- sf(iexp)=ifun(iimp)
- !$omp task
iexp = sf(iexp)- !$omp end task +end subroutine