Skip to content

Commit 216ba6b

Browse files
authored
[flang][OpenMP] Privatize vars referenced in statement functions (#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 #74273
1 parent 54eb89f commit 216ba6b

File tree

4 files changed

+227
-140
lines changed

4 files changed

+227
-140
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: 174 additions & 139 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 &);
@@ -736,6 +738,9 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
736738
std::optional<common::OmpAtomicDefaultMemOrderType>);
737739
void IssueNonConformanceWarning(
738740
llvm::omp::Directive D, parser::CharBlock source);
741+
742+
void CreateImplicitSymbols(
743+
const Symbol *symbol, std::optional<Symbol::Flag> setFlag = std::nullopt);
739744
};
740745

741746
template <typename T>
@@ -771,6 +776,19 @@ const parser::DoConstruct *DirectiveAttributeVisitor<T>::GetDoConstructIf(
771776
return parser::Unwrap<parser::DoConstruct>(x);
772777
}
773778

779+
template <typename T>
780+
Symbol *DirectiveAttributeVisitor<T>::DeclareNewPrivateAccessEntity(
781+
const Symbol &object, Symbol::Flag flag, Scope &scope) {
782+
assert(object.owner() != currScope());
783+
auto &symbol{MakeAssocSymbol(object.name(), object, scope)};
784+
symbol.set(flag);
785+
if (flag == Symbol::Flag::OmpCopyIn) {
786+
// The symbol in copyin clause must be threadprivate entity.
787+
symbol.set(Symbol::Flag::OmpThreadprivate);
788+
}
789+
return &symbol;
790+
}
791+
774792
template <typename T>
775793
Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
776794
const parser::Name &name, Symbol::Flag flag, Scope &scope) {
@@ -785,13 +803,7 @@ template <typename T>
785803
Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
786804
Symbol &object, Symbol::Flag flag, Scope &scope) {
787805
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;
806+
return DeclareNewPrivateAccessEntity(object, flag, scope);
795807
} else {
796808
object.set(flag);
797809
return &object;
@@ -2031,24 +2043,152 @@ void OmpAttributeVisitor::Post(const parser::OpenMPAllocatorsConstruct &x) {
20312043
PopContext();
20322044
}
20332045

2046+
static bool IsPrivatizable(const Symbol *sym) {
2047+
auto *misc{sym->detailsIf<MiscDetails>()};
2048+
return !IsProcedure(*sym) && !IsNamedConstant(*sym) &&
2049+
!sym->owner().IsDerivedType() &&
2050+
sym->owner().kind() != Scope::Kind::ImpliedDos &&
2051+
!sym->detailsIf<semantics::AssocEntityDetails>() &&
2052+
!sym->detailsIf<semantics::NamelistDetails>() &&
2053+
(!misc ||
2054+
(misc->kind() != MiscDetails::Kind::ComplexPartRe &&
2055+
misc->kind() != MiscDetails::Kind::ComplexPartIm &&
2056+
misc->kind() != MiscDetails::Kind::KindParamInquiry &&
2057+
misc->kind() != MiscDetails::Kind::LenParamInquiry &&
2058+
misc->kind() != MiscDetails::Kind::ConstructName));
2059+
}
2060+
2061+
void OmpAttributeVisitor::CreateImplicitSymbols(
2062+
const Symbol *symbol, std::optional<Symbol::Flag> setFlag) {
2063+
if (!IsPrivatizable(symbol)) {
2064+
return;
2065+
}
2066+
2067+
// Implicitly determined DSAs
2068+
// OMP 5.2 5.1.1 - Variables Referenced in a Construct
2069+
Symbol *lastDeclSymbol = nullptr;
2070+
std::optional<Symbol::Flag> prevDSA;
2071+
for (int dirDepth{0}; dirDepth < (int)dirContext_.size(); ++dirDepth) {
2072+
DirContext &dirContext = dirContext_[dirDepth];
2073+
std::optional<Symbol::Flag> dsa;
2074+
2075+
for (auto symMap : dirContext.objectWithDSA) {
2076+
// if the `symbol` already has a data-sharing attribute
2077+
if (symMap.first->name() == symbol->name()) {
2078+
dsa = symMap.second;
2079+
break;
2080+
}
2081+
}
2082+
2083+
// When handling each implicit rule for a given symbol, one of the
2084+
// following 3 actions may be taken:
2085+
// 1. Declare a new private symbol.
2086+
// 2. Create a new association symbol with no flags, that will represent
2087+
// a shared symbol in the current scope. Note that symbols without
2088+
// any private flags are considered as shared.
2089+
// 3. Use the last declared private symbol, by inserting a new symbol
2090+
// in the scope being processed, associated with it.
2091+
// If no private symbol was declared previously, then no association
2092+
// is needed and the symbol from the enclosing scope will be
2093+
// inherited by the current one.
2094+
//
2095+
// Because of how symbols are collected in lowering, not inserting a new
2096+
// symbol in the last case could lead to the conclusion that a symbol
2097+
// from an enclosing construct was declared in the current construct,
2098+
// which would result in wrong privatization code being generated.
2099+
// Consider the following example:
2100+
//
2101+
// !$omp parallel default(private) ! p1
2102+
// !$omp parallel default(private) shared(x) ! p2
2103+
// x = 10
2104+
// !$omp end parallel
2105+
// !$omp end parallel
2106+
//
2107+
// If a new x symbol was not inserted in the inner parallel construct
2108+
// (p2), it would use the x symbol definition from the enclosing scope.
2109+
// Then, when p2's default symbols were collected in lowering, the x
2110+
// symbol from the outer parallel construct (p1) would be collected, as
2111+
// it would have the private flag set.
2112+
// This would make x appear to be defined in p2, causing it to be
2113+
// privatized in p2 and its privatization in p1 to be skipped.
2114+
auto makePrivateSymbol = [&](Symbol::Flag flag) {
2115+
const Symbol *hostSymbol =
2116+
lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate();
2117+
lastDeclSymbol = DeclareNewPrivateAccessEntity(
2118+
*hostSymbol, flag, context_.FindScope(dirContext.directiveSource));
2119+
if (setFlag) {
2120+
lastDeclSymbol->set(*setFlag);
2121+
}
2122+
return lastDeclSymbol;
2123+
};
2124+
auto makeSharedSymbol = [&]() {
2125+
const Symbol *hostSymbol =
2126+
lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate();
2127+
MakeAssocSymbol(symbol->name(), *hostSymbol,
2128+
context_.FindScope(dirContext.directiveSource));
2129+
};
2130+
auto useLastDeclSymbol = [&]() {
2131+
if (lastDeclSymbol) {
2132+
makeSharedSymbol();
2133+
}
2134+
};
2135+
2136+
bool taskGenDir = llvm::omp::taskGeneratingSet.test(dirContext.directive);
2137+
bool targetDir = llvm::omp::allTargetSet.test(dirContext.directive);
2138+
bool parallelDir = llvm::omp::allParallelSet.test(dirContext.directive);
2139+
bool teamsDir = llvm::omp::allTeamsSet.test(dirContext.directive);
2140+
2141+
if (dsa.has_value()) {
2142+
if (dsa.value() == Symbol::Flag::OmpShared &&
2143+
(parallelDir || taskGenDir || teamsDir))
2144+
makeSharedSymbol();
2145+
// Private symbols will have been declared already.
2146+
prevDSA = dsa;
2147+
continue;
2148+
}
2149+
2150+
if (dirContext.defaultDSA == Symbol::Flag::OmpPrivate ||
2151+
dirContext.defaultDSA == Symbol::Flag::OmpFirstPrivate ||
2152+
dirContext.defaultDSA == Symbol::Flag::OmpShared) {
2153+
// 1) default
2154+
// Allowed only with parallel, teams and task generating constructs.
2155+
assert(parallelDir || taskGenDir || teamsDir);
2156+
if (dirContext.defaultDSA != Symbol::Flag::OmpShared)
2157+
makePrivateSymbol(dirContext.defaultDSA);
2158+
else
2159+
makeSharedSymbol();
2160+
dsa = dirContext.defaultDSA;
2161+
} else if (parallelDir) {
2162+
// 2) parallel -> shared
2163+
makeSharedSymbol();
2164+
dsa = Symbol::Flag::OmpShared;
2165+
} else if (!taskGenDir && !targetDir) {
2166+
// 3) enclosing context
2167+
useLastDeclSymbol();
2168+
dsa = prevDSA;
2169+
} else if (targetDir) {
2170+
// TODO 4) not mapped target variable -> firstprivate
2171+
dsa = prevDSA;
2172+
} else if (taskGenDir) {
2173+
// TODO 5) dummy arg in orphaned taskgen construct -> firstprivate
2174+
if (prevDSA == Symbol::Flag::OmpShared) {
2175+
// 6) shared in enclosing context -> shared
2176+
makeSharedSymbol();
2177+
dsa = Symbol::Flag::OmpShared;
2178+
} else {
2179+
// 7) firstprivate
2180+
dsa = Symbol::Flag::OmpFirstPrivate;
2181+
makePrivateSymbol(*dsa)->set(Symbol::Flag::OmpImplicit);
2182+
}
2183+
}
2184+
prevDSA = dsa;
2185+
}
2186+
}
2187+
20342188
// For OpenMP constructs, check all the data-refs within the constructs
20352189
// and adjust the symbol for each Name if necessary
20362190
void OmpAttributeVisitor::Post(const parser::Name &name) {
20372191
auto *symbol{name.symbol};
2038-
auto IsPrivatizable = [](const Symbol *sym) {
2039-
auto *misc{sym->detailsIf<MiscDetails>()};
2040-
return !IsProcedure(*sym) && !IsNamedConstant(*sym) &&
2041-
!sym->owner().IsDerivedType() &&
2042-
sym->owner().kind() != Scope::Kind::ImpliedDos &&
2043-
!sym->detailsIf<semantics::AssocEntityDetails>() &&
2044-
!sym->detailsIf<semantics::NamelistDetails>() &&
2045-
(!misc ||
2046-
(misc->kind() != MiscDetails::Kind::ComplexPartRe &&
2047-
misc->kind() != MiscDetails::Kind::ComplexPartIm &&
2048-
misc->kind() != MiscDetails::Kind::KindParamInquiry &&
2049-
misc->kind() != MiscDetails::Kind::LenParamInquiry &&
2050-
misc->kind() != MiscDetails::Kind::ConstructName));
2051-
};
20522192

20532193
if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
20542194
if (IsPrivatizable(symbol) && !IsObjectWithDSA(*symbol)) {
@@ -2076,125 +2216,20 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
20762216
if (found->test(semantics::Symbol::Flag::OmpThreadprivate))
20772217
return;
20782218
}
2079-
if (!IsPrivatizable(symbol)) {
2080-
return;
2081-
}
2082-
2083-
// Implicitly determined DSAs
2084-
// OMP 5.2 5.1.1 - Variables Referenced in a Construct
2085-
Symbol *lastDeclSymbol = nullptr;
2086-
std::optional<Symbol::Flag> prevDSA;
2087-
for (int dirDepth{0}; dirDepth < (int)dirContext_.size(); ++dirDepth) {
2088-
DirContext &dirContext = dirContext_[dirDepth];
2089-
std::optional<Symbol::Flag> dsa;
20902219

2091-
for (auto symMap : dirContext.objectWithDSA) {
2092-
// if the `symbol` already has a data-sharing attribute
2093-
if (symMap.first->name() == name.symbol->name()) {
2094-
dsa = symMap.second;
2095-
break;
2096-
}
2097-
}
2098-
2099-
// When handling each implicit rule for a given symbol, one of the
2100-
// following 3 actions may be taken:
2101-
// 1. Declare a new private symbol.
2102-
// 2. Create a new association symbol with no flags, that will represent
2103-
// a shared symbol in the current scope. Note that symbols without
2104-
// any private flags are considered as shared.
2105-
// 3. Use the last declared private symbol, by inserting a new symbol
2106-
// in the scope being processed, associated with it.
2107-
// If no private symbol was declared previously, then no association
2108-
// is needed and the symbol from the enclosing scope will be
2109-
// inherited by the current one.
2110-
//
2111-
// Because of how symbols are collected in lowering, not inserting a new
2112-
// symbol in the last case could lead to the conclusion that a symbol
2113-
// from an enclosing construct was declared in the current construct,
2114-
// which would result in wrong privatization code being generated.
2115-
// Consider the following example:
2116-
//
2117-
// !$omp parallel default(private) ! p1
2118-
// !$omp parallel default(private) shared(x) ! p2
2119-
// x = 10
2120-
// !$omp end parallel
2121-
// !$omp end parallel
2122-
//
2123-
// If a new x symbol was not inserted in the inner parallel construct
2124-
// (p2), it would use the x symbol definition from the enclosing scope.
2125-
// Then, when p2's default symbols were collected in lowering, the x
2126-
// symbol from the outer parallel construct (p1) would be collected, as
2127-
// it would have the private flag set.
2128-
// This would make x appear to be defined in p2, causing it to be
2129-
// privatized in p2 and its privatization in p1 to be skipped.
2130-
auto makePrivateSymbol = [&](Symbol::Flag flag) {
2131-
Symbol *hostSymbol =
2132-
lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate();
2133-
lastDeclSymbol = DeclarePrivateAccessEntity(
2134-
*hostSymbol, flag, context_.FindScope(dirContext.directiveSource));
2135-
return lastDeclSymbol;
2136-
};
2137-
auto makeSharedSymbol = [&]() {
2138-
Symbol *hostSymbol =
2139-
lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate();
2140-
MakeAssocSymbol(symbol->name(), *hostSymbol,
2141-
context_.FindScope(dirContext.directiveSource));
2142-
};
2143-
auto useLastDeclSymbol = [&]() {
2144-
if (lastDeclSymbol)
2145-
MakeAssocSymbol(symbol->name(), *lastDeclSymbol,
2146-
context_.FindScope(dirContext.directiveSource));
2147-
};
2148-
2149-
bool taskGenDir = llvm::omp::taskGeneratingSet.test(dirContext.directive);
2150-
bool targetDir = llvm::omp::allTargetSet.test(dirContext.directive);
2151-
bool parallelDir = llvm::omp::allParallelSet.test(dirContext.directive);
2152-
bool teamsDir = llvm::omp::allTeamsSet.test(dirContext.directive);
2153-
2154-
if (dsa.has_value()) {
2155-
if (dsa.value() == Symbol::Flag::OmpShared &&
2156-
(parallelDir || taskGenDir || teamsDir))
2157-
makeSharedSymbol();
2158-
// Private symbols will have been declared already.
2159-
prevDSA = dsa;
2160-
continue;
2161-
}
2162-
2163-
if (dirContext.defaultDSA == Symbol::Flag::OmpPrivate ||
2164-
dirContext.defaultDSA == Symbol::Flag::OmpFirstPrivate ||
2165-
dirContext.defaultDSA == Symbol::Flag::OmpShared) {
2166-
// 1) default
2167-
// Allowed only with parallel, teams and task generating constructs.
2168-
assert(parallelDir || taskGenDir || teamsDir);
2169-
if (dirContext.defaultDSA != Symbol::Flag::OmpShared)
2170-
makePrivateSymbol(dirContext.defaultDSA);
2171-
else
2172-
makeSharedSymbol();
2173-
dsa = dirContext.defaultDSA;
2174-
} else if (parallelDir) {
2175-
// 2) parallel -> shared
2176-
makeSharedSymbol();
2177-
dsa = Symbol::Flag::OmpShared;
2178-
} else if (!taskGenDir && !targetDir) {
2179-
// 3) enclosing context
2180-
useLastDeclSymbol();
2181-
dsa = prevDSA;
2182-
} else if (targetDir) {
2183-
// TODO 4) not mapped target variable -> firstprivate
2184-
dsa = prevDSA;
2185-
} else if (taskGenDir) {
2186-
// TODO 5) dummy arg in orphaned taskgen construct -> firstprivate
2187-
if (prevDSA == Symbol::Flag::OmpShared) {
2188-
// 6) shared in enclosing context -> shared
2189-
makeSharedSymbol();
2190-
dsa = Symbol::Flag::OmpShared;
2191-
} else {
2192-
// 7) firstprivate
2193-
dsa = Symbol::Flag::OmpFirstPrivate;
2194-
makePrivateSymbol(*dsa)->set(Symbol::Flag::OmpImplicit);
2220+
if (auto *stmtFunction{symbol->detailsIf<semantics::SubprogramDetails>()};
2221+
stmtFunction && stmtFunction->stmtFunction()) {
2222+
// Each non-dummy argument from a statement function must be handled too,
2223+
// as if it was explicitly referenced.
2224+
semantics::UnorderedSymbolSet symbols{
2225+
CollectSymbols(stmtFunction->stmtFunction().value())};
2226+
for (const auto &sym : symbols) {
2227+
if (!IsStmtFunctionDummy(sym) && !IsObjectWithDSA(*sym)) {
2228+
CreateImplicitSymbols(&*sym, Symbol::Flag::OmpFromStmtFunction);
21952229
}
21962230
}
2197-
prevDSA = dsa;
2231+
} else {
2232+
CreateImplicitSymbols(symbol);
21982233
}
21992234
} // within OpenMP construct
22002235
}

0 commit comments

Comments
 (0)