@@ -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
741746template <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+
774792template <typename T>
775793Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
776794 const parser::Name &name, Symbol::Flag flag, Scope &scope) {
@@ -785,13 +803,7 @@ template <typename T>
785803Symbol *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
20362190void 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