[flang-commits] [flang] [flang][OpenMP] Privatize vars referenced in statement functions (PR #103390)
Leandro Lupori via flang-commits
flang-commits at lists.llvm.org
Wed Aug 21 05:11:33 PDT 2024
https://github.com/luporl updated https://github.com/llvm/llvm-project/pull/103390
>From 962b2268fb2d936f5368ad6e4bff355a51fb2a67 Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Tue, 23 Jul 2024 18:19:35 -0300
Subject: [PATCH 1/2] [flang][OpenMP] Privatize vars referenced in statement
functions
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 https://github.com/llvm/llvm-project/issues/74273
---
flang/include/flang/Semantics/symbol.h | 2 +-
.../lib/Lower/OpenMP/DataSharingProcessor.cpp | 9 ++
flang/lib/Semantics/resolve-directives.cpp | 112 ++++++++++++++----
.../test/Lower/OpenMP/statement-function.f90 | 43 +++++++
4 files changed, 140 insertions(+), 26 deletions(-)
create mode 100644 flang/test/Lower/OpenMP/statement-function.f90
diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index cf0350735b5b94..b4db6689a94271 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 e1a193edc004a7..1b2f926e21bed8 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 cc9f1cc7ed2691..428d3e6c27db9c 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -91,11 +91,12 @@ template <typename T> class DirectiveAttributeVisitor {
void SetContextAssociatedLoopLevel(std::int64_t level) {
GetContext().associatedLoopLevel = level;
}
- Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev, Scope &scope) {
+ Symbol &MakeAssocSymbol(
+ const SourceName &name, const Symbol &prev, Scope &scope) {
const auto pair{scope.try_emplace(name, Attrs{}, HostAssocDetails{prev})};
return *pair.first->second;
}
- 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 <typename T> 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<T>::GetDoConstructIf(
return parser::Unwrap<parser::DoConstruct>(x);
}
+template <typename T>
+Symbol *DirectiveAttributeVisitor<T>::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 <typename T>
Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
const parser::Name &name, Symbol::Flag flag, Scope &scope) {
@@ -785,13 +800,7 @@ template <typename T>
Symbol *DirectiveAttributeVisitor<T>::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;
@@ -2076,13 +2085,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->detailsIf<semantics::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::optional<Symbol::Flag> prevDSA;
for (int dirDepth{0}; dirDepth < (int)dirContext_.size(); ++dirDepth) {
DirContext &dirContext = dirContext_[dirDepth];
@@ -2127,23 +2153,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;
+ else
+ lastDeclSymbols.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);
@@ -2191,7 +2253,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 00000000000000..6cdbcb6e141c7e
--- /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<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[IIMP:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_implicit_useEiimp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.parallel private({{.*firstprivate.*}} %[[IEXP]]#0 -> %[[PRIV_IEXP:.*]] : !fir.ref<i32>,
+!CHECK-SAME: {{.*firstprivate.*}} %[[IIMP]]#0 -> %[[PRIV_IIMP:.*]] : !fir.ref<i32>)
+!CHECK: %{{.*}}:2 = hlfir.declare %[[PRIV_IEXP]] {uniq_name = "_QFtest_implicit_useEiexp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %{{.*}}:2 = hlfir.declare %[[PRIV_IIMP]] {uniq_name = "_QFtest_implicit_useEiimp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+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<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[IIMP:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_implicit_use2Eiimp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.task
+!CHECK: %[[PRIV_IEXP:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_implicit_use2Eiexp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[TEMP0:.*]] = fir.load %[[IEXP]]#0 : !fir.ref<i32>
+!CHECK: hlfir.assign %[[TEMP0]] to %[[PRIV_IEXP]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK: %[[PRIV_IIMP:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_implicit_use2Eiimp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[TEMP1:.*]] = fir.load %[[IIMP]]#0 : !fir.ref<i32>
+!CHECK: hlfir.assign %[[TEMP1]] to %[[PRIV_IIMP]]#0 temporary_lhs : i32, !fir.ref<i32>
+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
>From 5d453c6d9781c6bdadf4e8ca200bc749c81f94b9 Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Tue, 20 Aug 2024 18:12:14 -0300
Subject: [PATCH 2/2] Refactoring: simplify the handling of statement function
symbols
Move the handling of symbols with implicitly determined DSAs to a
separate function, that can be called for each non-dummy symbol of
a statement function, so that they can be handled in the same way
as other types of symbols.
---
flang/lib/Semantics/resolve-directives.cpp | 325 ++++++++++-----------
1 file changed, 149 insertions(+), 176 deletions(-)
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 428d3e6c27db9c..4aecb8b8e7b479 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -738,6 +738,9 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
std::optional<common::OmpAtomicDefaultMemOrderType>);
void IssueNonConformanceWarning(
llvm::omp::Directive D, parser::CharBlock source);
+
+ void CreateImplicitSymbols(
+ const Symbol *symbol, std::optional<Symbol::Flag> setFlag = std::nullopt);
};
template <typename T>
@@ -2040,24 +2043,152 @@ void OmpAttributeVisitor::Post(const parser::OpenMPAllocatorsConstruct &x) {
PopContext();
}
+static bool IsPrivatizable(const Symbol *sym) {
+ auto *misc{sym->detailsIf<MiscDetails>()};
+ return !IsProcedure(*sym) && !IsNamedConstant(*sym) &&
+ !sym->owner().IsDerivedType() &&
+ sym->owner().kind() != Scope::Kind::ImpliedDos &&
+ !sym->detailsIf<semantics::AssocEntityDetails>() &&
+ !sym->detailsIf<semantics::NamelistDetails>() &&
+ (!misc ||
+ (misc->kind() != MiscDetails::Kind::ComplexPartRe &&
+ misc->kind() != MiscDetails::Kind::ComplexPartIm &&
+ misc->kind() != MiscDetails::Kind::KindParamInquiry &&
+ misc->kind() != MiscDetails::Kind::LenParamInquiry &&
+ misc->kind() != MiscDetails::Kind::ConstructName));
+}
+
+void OmpAttributeVisitor::CreateImplicitSymbols(
+ const Symbol *symbol, std::optional<Symbol::Flag> setFlag) {
+ if (!IsPrivatizable(symbol)) {
+ return;
+ }
+
+ // Implicitly determined DSAs
+ // OMP 5.2 5.1.1 - Variables Referenced in a Construct
+ Symbol *lastDeclSymbol = nullptr;
+ std::optional<Symbol::Flag> prevDSA;
+ for (int dirDepth{0}; dirDepth < (int)dirContext_.size(); ++dirDepth) {
+ DirContext &dirContext = dirContext_[dirDepth];
+ std::optional<Symbol::Flag> dsa;
+
+ for (auto symMap : dirContext.objectWithDSA) {
+ // if the `symbol` already has a data-sharing attribute
+ if (symMap.first->name() == symbol->name()) {
+ dsa = symMap.second;
+ break;
+ }
+ }
+
+ // When handling each implicit rule for a given symbol, one of the
+ // following 3 actions may be taken:
+ // 1. Declare a new private symbol.
+ // 2. Create a new association symbol with no flags, that will represent
+ // a shared symbol in the current scope. Note that symbols without
+ // any private flags are considered as shared.
+ // 3. Use the last declared private symbol, by inserting a new symbol
+ // in the scope being processed, associated with it.
+ // If no private symbol was declared previously, then no association
+ // is needed and the symbol from the enclosing scope will be
+ // inherited by the current one.
+ //
+ // Because of how symbols are collected in lowering, not inserting a new
+ // symbol in the last case could lead to the conclusion that a symbol
+ // from an enclosing construct was declared in the current construct,
+ // which would result in wrong privatization code being generated.
+ // Consider the following example:
+ //
+ // !$omp parallel default(private) ! p1
+ // !$omp parallel default(private) shared(x) ! p2
+ // x = 10
+ // !$omp end parallel
+ // !$omp end parallel
+ //
+ // If a new x symbol was not inserted in the inner parallel construct
+ // (p2), it would use the x symbol definition from the enclosing scope.
+ // Then, when p2's default symbols were collected in lowering, the x
+ // symbol from the outer parallel construct (p1) would be collected, as
+ // 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) {
+ const Symbol *hostSymbol =
+ lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate();
+ lastDeclSymbol = DeclareNewPrivateAccessEntity(
+ *hostSymbol, flag, context_.FindScope(dirContext.directiveSource));
+ if (setFlag) {
+ lastDeclSymbol->set(*setFlag);
+ }
+ return lastDeclSymbol;
+ };
+ auto makeSharedSymbol = [&]() {
+ const Symbol *hostSymbol =
+ lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate();
+ MakeAssocSymbol(symbol->name(), *hostSymbol,
+ context_.FindScope(dirContext.directiveSource));
+ };
+ auto useLastDeclSymbol = [&]() {
+ if (lastDeclSymbol) {
+ makeSharedSymbol();
+ }
+ };
+
+ bool taskGenDir = llvm::omp::taskGeneratingSet.test(dirContext.directive);
+ bool targetDir = llvm::omp::allTargetSet.test(dirContext.directive);
+ bool parallelDir = llvm::omp::allParallelSet.test(dirContext.directive);
+ bool teamsDir = llvm::omp::allTeamsSet.test(dirContext.directive);
+
+ if (dsa.has_value()) {
+ if (dsa.value() == Symbol::Flag::OmpShared &&
+ (parallelDir || taskGenDir || teamsDir))
+ makeSharedSymbol();
+ // Private symbols will have been declared already.
+ prevDSA = dsa;
+ continue;
+ }
+
+ if (dirContext.defaultDSA == Symbol::Flag::OmpPrivate ||
+ dirContext.defaultDSA == Symbol::Flag::OmpFirstPrivate ||
+ dirContext.defaultDSA == Symbol::Flag::OmpShared) {
+ // 1) default
+ // Allowed only with parallel, teams and task generating constructs.
+ assert(parallelDir || taskGenDir || teamsDir);
+ if (dirContext.defaultDSA != Symbol::Flag::OmpShared)
+ makePrivateSymbol(dirContext.defaultDSA);
+ else
+ makeSharedSymbol();
+ dsa = dirContext.defaultDSA;
+ } else if (parallelDir) {
+ // 2) parallel -> shared
+ makeSharedSymbol();
+ dsa = Symbol::Flag::OmpShared;
+ } else if (!taskGenDir && !targetDir) {
+ // 3) enclosing context
+ useLastDeclSymbol();
+ dsa = prevDSA;
+ } else if (targetDir) {
+ // TODO 4) not mapped target variable -> firstprivate
+ dsa = prevDSA;
+ } else if (taskGenDir) {
+ // TODO 5) dummy arg in orphaned taskgen construct -> firstprivate
+ if (prevDSA == Symbol::Flag::OmpShared) {
+ // 6) shared in enclosing context -> shared
+ makeSharedSymbol();
+ dsa = Symbol::Flag::OmpShared;
+ } else {
+ // 7) firstprivate
+ dsa = Symbol::Flag::OmpFirstPrivate;
+ makePrivateSymbol(*dsa)->set(Symbol::Flag::OmpImplicit);
+ }
+ }
+ prevDSA = dsa;
+ }
+}
+
// For OpenMP constructs, check all the data-refs within the constructs
// and adjust the symbol for each Name if necessary
void OmpAttributeVisitor::Post(const parser::Name &name) {
auto *symbol{name.symbol};
- auto IsPrivatizable = [](const Symbol *sym) {
- auto *misc{sym->detailsIf<MiscDetails>()};
- return !IsProcedure(*sym) && !IsNamedConstant(*sym) &&
- !sym->owner().IsDerivedType() &&
- sym->owner().kind() != Scope::Kind::ImpliedDos &&
- !sym->detailsIf<semantics::AssocEntityDetails>() &&
- !sym->detailsIf<semantics::NamelistDetails>() &&
- (!misc ||
- (misc->kind() != MiscDetails::Kind::ComplexPartRe &&
- misc->kind() != MiscDetails::Kind::ComplexPartIm &&
- misc->kind() != MiscDetails::Kind::KindParamInquiry &&
- misc->kind() != MiscDetails::Kind::LenParamInquiry &&
- misc->kind() != MiscDetails::Kind::ConstructName));
- };
if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
if (IsPrivatizable(symbol) && !IsObjectWithDSA(*symbol)) {
@@ -2086,7 +2217,6 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
return;
}
- std::set<const Symbol *> stmtFunctionSymbols;
if (auto *stmtFunction{symbol->detailsIf<semantics::SubprogramDetails>()};
stmtFunction && stmtFunction->stmtFunction()) {
// Each non-dummy argument from a statement function must be handled too,
@@ -2094,169 +2224,12 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
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
- std::vector<const Symbol *> lastDeclSymbols;
- std::optional<Symbol::Flag> prevDSA;
- for (int dirDepth{0}; dirDepth < (int)dirContext_.size(); ++dirDepth) {
- DirContext &dirContext = dirContext_[dirDepth];
- std::optional<Symbol::Flag> dsa;
-
- for (auto symMap : dirContext.objectWithDSA) {
- // if the `symbol` already has a data-sharing attribute
- if (symMap.first->name() == name.symbol->name()) {
- dsa = symMap.second;
- break;
- }
- }
-
- // When handling each implicit rule for a given symbol, one of the
- // following 3 actions may be taken:
- // 1. Declare a new private symbol.
- // 2. Create a new association symbol with no flags, that will represent
- // a shared symbol in the current scope. Note that symbols without
- // any private flags are considered as shared.
- // 3. Use the last declared private symbol, by inserting a new symbol
- // in the scope being processed, associated with it.
- // If no private symbol was declared previously, then no association
- // is needed and the symbol from the enclosing scope will be
- // inherited by the current one.
- //
- // Because of how symbols are collected in lowering, not inserting a new
- // symbol in the last case could lead to the conclusion that a symbol
- // from an enclosing construct was declared in the current construct,
- // which would result in wrong privatization code being generated.
- // Consider the following example:
- //
- // !$omp parallel default(private) ! p1
- // !$omp parallel default(private) shared(x) ! p2
- // x = 10
- // !$omp end parallel
- // !$omp end parallel
- //
- // If a new x symbol was not inserted in the inner parallel construct
- // (p2), it would use the x symbol definition from the enclosing scope.
- // Then, when p2's default symbols were collected in lowering, the x
- // symbol from the outer parallel construct (p1) would be collected, as
- // 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.
- // 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;
- else
- lastDeclSymbols.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 = [&]() {
- 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 (!lastDeclSymbols.empty())
- makeSharedSymbol();
- };
-
- bool taskGenDir = llvm::omp::taskGeneratingSet.test(dirContext.directive);
- bool targetDir = llvm::omp::allTargetSet.test(dirContext.directive);
- bool parallelDir = llvm::omp::allParallelSet.test(dirContext.directive);
- bool teamsDir = llvm::omp::allTeamsSet.test(dirContext.directive);
-
- if (dsa.has_value()) {
- if (dsa.value() == Symbol::Flag::OmpShared &&
- (parallelDir || taskGenDir || teamsDir))
- makeSharedSymbol();
- // Private symbols will have been declared already.
- prevDSA = dsa;
- continue;
- }
-
- if (dirContext.defaultDSA == Symbol::Flag::OmpPrivate ||
- dirContext.defaultDSA == Symbol::Flag::OmpFirstPrivate ||
- dirContext.defaultDSA == Symbol::Flag::OmpShared) {
- // 1) default
- // Allowed only with parallel, teams and task generating constructs.
- assert(parallelDir || taskGenDir || teamsDir);
- if (dirContext.defaultDSA != Symbol::Flag::OmpShared)
- makePrivateSymbol(dirContext.defaultDSA);
- else
- makeSharedSymbol();
- dsa = dirContext.defaultDSA;
- } else if (parallelDir) {
- // 2) parallel -> shared
- makeSharedSymbol();
- dsa = Symbol::Flag::OmpShared;
- } else if (!taskGenDir && !targetDir) {
- // 3) enclosing context
- useLastDeclSymbol();
- dsa = prevDSA;
- } else if (targetDir) {
- // TODO 4) not mapped target variable -> firstprivate
- dsa = prevDSA;
- } else if (taskGenDir) {
- // TODO 5) dummy arg in orphaned taskgen construct -> firstprivate
- if (prevDSA == Symbol::Flag::OmpShared) {
- // 6) shared in enclosing context -> shared
- makeSharedSymbol();
- dsa = Symbol::Flag::OmpShared;
- } else {
- // 7) firstprivate
- dsa = Symbol::Flag::OmpFirstPrivate;
- makePrivateSymbol(*dsa, /*implicit=*/true);
+ if (!IsStmtFunctionDummy(sym) && !IsObjectWithDSA(*sym)) {
+ CreateImplicitSymbols(&*sym, Symbol::Flag::OmpFromStmtFunction);
}
}
- prevDSA = dsa;
+ } else {
+ CreateImplicitSymbols(symbol);
}
} // within OpenMP construct
}
More information about the flang-commits
mailing list