Skip to content

Commit d789c59

Browse files
committed
[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 #74273
1 parent b53fe2c commit d789c59

File tree

4 files changed

+140
-26
lines changed

4 files changed

+140
-26
lines changed

flang/include/flang/Semantics/symbol.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -755,7 +755,7 @@ class Symbol {
755755
OmpDeclarativeAllocateDirective, OmpExecutableAllocateDirective,
756756
OmpDeclareSimd, OmpDeclareTarget, OmpThreadprivate, OmpDeclareReduction,
757757
OmpFlushed, OmpCriticalLock, OmpIfSpecified, OmpNone, OmpPreDetermined,
758-
OmpImplicit);
758+
OmpImplicit, OmpFromStmtFunction);
759759
using Flags = common::EnumSet<Flag, Flag_enumSize>;
760760

761761
const Scope &owner() const { return *owner_; }

flang/lib/Lower/OpenMP/DataSharingProcessor.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,15 @@ void DataSharingProcessor::collectSymbols(
402402
/*collectSymbols=*/true,
403403
/*collectHostAssociatedSymbols=*/true);
404404

405+
// Add implicitly referenced symbols from statement functions.
406+
if (curScope) {
407+
for (const auto &sym : curScope->GetSymbols()) {
408+
if (sym->test(semantics::Symbol::Flag::OmpFromStmtFunction) &&
409+
sym->test(flag))
410+
allSymbols.insert(&*sym);
411+
}
412+
}
413+
405414
llvm::SetVector<const semantics::Symbol *> symbolsInNestedRegions;
406415
collectSymbolsInNestedRegions(eval, flag, symbolsInNestedRegions);
407416

flang/lib/Semantics/resolve-directives.cpp

Lines changed: 87 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,12 @@ template <typename T> class DirectiveAttributeVisitor {
9191
void SetContextAssociatedLoopLevel(std::int64_t level) {
9292
GetContext().associatedLoopLevel = level;
9393
}
94-
Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev, Scope &scope) {
94+
Symbol &MakeAssocSymbol(
95+
const SourceName &name, const Symbol &prev, Scope &scope) {
9596
const auto pair{scope.try_emplace(name, Attrs{}, HostAssocDetails{prev})};
9697
return *pair.first->second;
9798
}
98-
Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev) {
99+
Symbol &MakeAssocSymbol(const SourceName &name, const Symbol &prev) {
99100
return MakeAssocSymbol(name, prev, currScope());
100101
}
101102
void AddDataSharingAttributeObject(SymbolRef object) {
@@ -108,6 +109,7 @@ template <typename T> class DirectiveAttributeVisitor {
108109
const parser::Name *GetLoopIndex(const parser::DoConstruct &);
109110
const parser::DoConstruct *GetDoConstructIf(
110111
const parser::ExecutionPartConstruct &);
112+
Symbol *DeclareNewPrivateAccessEntity(const Symbol &, Symbol::Flag, Scope &);
111113
Symbol *DeclarePrivateAccessEntity(
112114
const parser::Name &, Symbol::Flag, Scope &);
113115
Symbol *DeclarePrivateAccessEntity(Symbol &, Symbol::Flag, Scope &);
@@ -771,6 +773,19 @@ const parser::DoConstruct *DirectiveAttributeVisitor<T>::GetDoConstructIf(
771773
return parser::Unwrap<parser::DoConstruct>(x);
772774
}
773775

776+
template <typename T>
777+
Symbol *DirectiveAttributeVisitor<T>::DeclareNewPrivateAccessEntity(
778+
const Symbol &object, Symbol::Flag flag, Scope &scope) {
779+
assert(object.owner() != currScope());
780+
auto &symbol{MakeAssocSymbol(object.name(), object, scope)};
781+
symbol.set(flag);
782+
if (flag == Symbol::Flag::OmpCopyIn) {
783+
// The symbol in copyin clause must be threadprivate entity.
784+
symbol.set(Symbol::Flag::OmpThreadprivate);
785+
}
786+
return &symbol;
787+
}
788+
774789
template <typename T>
775790
Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
776791
const parser::Name &name, Symbol::Flag flag, Scope &scope) {
@@ -785,13 +800,7 @@ template <typename T>
785800
Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
786801
Symbol &object, Symbol::Flag flag, Scope &scope) {
787802
if (object.owner() != currScope()) {
788-
auto &symbol{MakeAssocSymbol(object.name(), object, scope)};
789-
symbol.set(flag);
790-
if (flag == Symbol::Flag::OmpCopyIn) {
791-
// The symbol in copyin clause must be threadprivate entity.
792-
symbol.set(Symbol::Flag::OmpThreadprivate);
793-
}
794-
return &symbol;
803+
return DeclareNewPrivateAccessEntity(object, flag, scope);
795804
} else {
796805
object.set(flag);
797806
return &object;
@@ -2075,13 +2084,30 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
20752084
if (found->test(semantics::Symbol::Flag::OmpThreadprivate))
20762085
return;
20772086
}
2078-
if (!IsPrivatizable(symbol)) {
2087+
2088+
std::set<const Symbol *> stmtFunctionSymbols;
2089+
if (auto *stmtFunction{symbol->detailsIf<semantics::SubprogramDetails>()};
2090+
stmtFunction && stmtFunction->stmtFunction()) {
2091+
// Each non-dummy argument from a statement function must be handled too,
2092+
// as if it was explicitly referenced.
2093+
semantics::UnorderedSymbolSet symbols{
2094+
CollectSymbols(stmtFunction->stmtFunction().value())};
2095+
for (const auto &sym : symbols) {
2096+
if (!IsStmtFunctionDummy(sym) && IsPrivatizable(&*sym) &&
2097+
!IsObjectWithDSA(*sym)) {
2098+
stmtFunctionSymbols.insert(&*sym);
2099+
}
2100+
}
2101+
if (stmtFunctionSymbols.empty()) {
2102+
return;
2103+
}
2104+
} else if (!IsPrivatizable(symbol)) {
20792105
return;
20802106
}
20812107

20822108
// Implicitly determined DSAs
20832109
// OMP 5.2 5.1.1 - Variables Referenced in a Construct
2084-
Symbol *lastDeclSymbol = nullptr;
2110+
std::vector<const Symbol *> lastDeclSymbols;
20852111
std::optional<Symbol::Flag> prevDSA;
20862112
for (int dirDepth{0}; dirDepth < (int)dirContext_.size(); ++dirDepth) {
20872113
DirContext &dirContext = dirContext_[dirDepth];
@@ -2126,23 +2152,59 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
21262152
// it would have the private flag set.
21272153
// This would make x appear to be defined in p2, causing it to be
21282154
// privatized in p2 and its privatization in p1 to be skipped.
2129-
auto makePrivateSymbol = [&](Symbol::Flag flag) {
2130-
Symbol *hostSymbol =
2131-
lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate();
2132-
lastDeclSymbol = DeclarePrivateAccessEntity(
2133-
*hostSymbol, flag, context_.FindScope(dirContext.directiveSource));
2134-
return lastDeclSymbol;
2155+
// TODO Move the lambda functions below to a separate class.
2156+
auto hostSymbol = [&](const Symbol *sym, int index = 0) {
2157+
if (lastDeclSymbols.empty())
2158+
return &sym->GetUltimate();
2159+
return lastDeclSymbols[index];
2160+
};
2161+
auto declNewPrivateSymbol = [&](const Symbol *sym, Symbol::Flag flag,
2162+
bool implicit) {
2163+
Symbol *newSym = DeclareNewPrivateAccessEntity(
2164+
*sym, flag, context_.FindScope(dirContext.directiveSource));
2165+
if (implicit)
2166+
newSym->set(Symbol::Flag::OmpImplicit);
2167+
return newSym;
2168+
};
2169+
auto makePrivateSymbol = [&](Symbol::Flag flag, bool implicit = false) {
2170+
bool hasLastDeclSymbols = !lastDeclSymbols.empty();
2171+
auto updateLastDeclSymbols = [&](const Symbol *sym, int index = 0) {
2172+
if (hasLastDeclSymbols)
2173+
lastDeclSymbols[index] = sym;
2174+
else
2175+
lastDeclSymbols.push_back(sym);
2176+
};
2177+
2178+
if (stmtFunctionSymbols.empty()) {
2179+
const Symbol *newSym =
2180+
declNewPrivateSymbol(hostSymbol(symbol), flag, implicit);
2181+
updateLastDeclSymbols(newSym);
2182+
return;
2183+
}
2184+
2185+
int i = 0;
2186+
for (const auto *sym : stmtFunctionSymbols) {
2187+
Symbol *newSym =
2188+
declNewPrivateSymbol(hostSymbol(sym, i), flag, implicit);
2189+
newSym->set(Symbol::Flag::OmpFromStmtFunction);
2190+
updateLastDeclSymbols(newSym, i++);
2191+
}
21352192
};
21362193
auto makeSharedSymbol = [&]() {
2137-
Symbol *hostSymbol =
2138-
lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate();
2139-
MakeAssocSymbol(symbol->name(), *hostSymbol,
2140-
context_.FindScope(dirContext.directiveSource));
2194+
if (stmtFunctionSymbols.empty()) {
2195+
MakeAssocSymbol(symbol->name(), *hostSymbol(symbol),
2196+
context_.FindScope(dirContext.directiveSource));
2197+
} else {
2198+
int i = 0;
2199+
for (const auto *sym : stmtFunctionSymbols) {
2200+
MakeAssocSymbol(sym->name(), *hostSymbol(sym, i++),
2201+
context_.FindScope(dirContext.directiveSource));
2202+
}
2203+
}
21412204
};
21422205
auto useLastDeclSymbol = [&]() {
2143-
if (lastDeclSymbol)
2144-
MakeAssocSymbol(symbol->name(), *lastDeclSymbol,
2145-
context_.FindScope(dirContext.directiveSource));
2206+
if (!lastDeclSymbols.empty())
2207+
makeSharedSymbol();
21462208
};
21472209

21482210
bool taskGenDir = llvm::omp::taskGeneratingSet.test(dirContext.directive);
@@ -2190,7 +2252,7 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
21902252
} else {
21912253
// 7) firstprivate
21922254
dsa = Symbol::Flag::OmpFirstPrivate;
2193-
makePrivateSymbol(*dsa)->set(Symbol::Flag::OmpImplicit);
2255+
makePrivateSymbol(*dsa, /*implicit=*/true);
21942256
}
21952257
}
21962258
prevDSA = dsa;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
! Test privatization within OpenMP constructs containing statement functions.
2+
! RUN: %flang_fc1 -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s
3+
4+
!CHECK-LABEL: func @_QPtest_implicit_use
5+
!CHECK: %[[IEXP:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_implicit_useEiexp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
6+
!CHECK: %[[IIMP:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_implicit_useEiimp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
7+
!CHECK: omp.parallel private({{.*firstprivate.*}} %[[IEXP]]#0 -> %[[PRIV_IEXP:.*]] : !fir.ref<i32>,
8+
!CHECK-SAME: {{.*firstprivate.*}} %[[IIMP]]#0 -> %[[PRIV_IIMP:.*]] : !fir.ref<i32>)
9+
!CHECK: %{{.*}}:2 = hlfir.declare %[[PRIV_IEXP]] {uniq_name = "_QFtest_implicit_useEiexp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
10+
!CHECK: %{{.*}}:2 = hlfir.declare %[[PRIV_IIMP]] {uniq_name = "_QFtest_implicit_useEiimp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
11+
subroutine test_implicit_use()
12+
implicit none
13+
integer :: iexp, iimp
14+
integer, external :: ifun
15+
integer :: sf
16+
17+
sf(iexp)=ifun(iimp)+iexp
18+
!$omp parallel default(firstprivate)
19+
iexp = sf(iexp)
20+
!$omp end parallel
21+
end subroutine
22+
23+
!CHECK-LABEL: func @_QPtest_implicit_use2
24+
!CHECK: %[[IEXP:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_implicit_use2Eiexp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
25+
!CHECK: %[[IIMP:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_implicit_use2Eiimp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
26+
!CHECK: omp.task
27+
!CHECK: %[[PRIV_IEXP:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_implicit_use2Eiexp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
28+
!CHECK: %[[TEMP0:.*]] = fir.load %[[IEXP]]#0 : !fir.ref<i32>
29+
!CHECK: hlfir.assign %[[TEMP0]] to %[[PRIV_IEXP]]#0 temporary_lhs : i32, !fir.ref<i32>
30+
!CHECK: %[[PRIV_IIMP:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_implicit_use2Eiimp"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
31+
!CHECK: %[[TEMP1:.*]] = fir.load %[[IIMP]]#0 : !fir.ref<i32>
32+
!CHECK: hlfir.assign %[[TEMP1]] to %[[PRIV_IIMP]]#0 temporary_lhs : i32, !fir.ref<i32>
33+
subroutine test_implicit_use2()
34+
implicit none
35+
integer :: iexp, iimp
36+
integer, external :: ifun
37+
integer :: sf
38+
39+
sf(iexp)=ifun(iimp)
40+
!$omp task
41+
iexp = sf(iexp)
42+
!$omp end task
43+
end subroutine

0 commit comments

Comments
 (0)