[flang-commits] [flang] [flang][OpenMP] Privatize vars referenced in statement functions (PR #103390)
Leandro Lupori via flang-commits
flang-commits at lists.llvm.org
Tue Aug 13 11:12:59 PDT 2024
https://github.com/luporl created https://github.com/llvm/llvm-project/pull/103390
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
>From d789c59d7efd5c7b5c6381ecfc8d7e4a6c8b5473 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] [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 d635a7b8b7874f..04642aa4e02796 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;
@@ -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->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];
@@ -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;
+ 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);
@@ -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 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
More information about the flang-commits
mailing list