1919#include " flang/Parser/parse-tree.h"
2020#include " flang/Parser/tools.h"
2121#include " flang/Semantics/expression.h"
22+ #include " flang/Semantics/tools.h"
2223#include < list>
2324#include < map>
2425#include < sstream>
@@ -729,7 +730,6 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
729730 void CheckNameInAllocateStmt (const parser::CharBlock &source,
730731 const parser::Name &ompObject, const parser::AllocateStmt &allocate);
731732
732- bool HasSymbolInEnclosingScope (const Symbol &, Scope &);
733733 std::int64_t ordCollapseLevel{0 };
734734
735735 void AddOmpRequiresToScope (Scope &, WithOmpDeclarative::RequiresFlags,
@@ -2085,16 +2085,22 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
20852085 }
20862086 }
20872087
2088- // When handling each implicit rule, either a new private symbol is
2089- // declared or the last declared symbol is used.
2090- // In the latter case, it's necessary to insert a new symbol in the scope
2091- // being processed, associated with the last declared symbol.
2092- // This captures the fact that, although we are using the last declared
2093- // symbol, its DSA could be different in this scope.
2094- // Also, because of how symbols are collected in lowering, not inserting
2095- // a new symbol in this scope could lead to the conclusion that the
2096- // symbol was declared in this construct, which would result in wrong
2097- // privatization code being generated.
2088+ // When handling each implicit rule for a given symbol, one of the
2089+ // following 3 actions may be taken:
2090+ // 1. Declare a new private symbol.
2091+ // 2. Create a new association symbol with no flags, that will represent
2092+ // a shared symbol in the current scope. Note that symbols without
2093+ // any private flags are considered as shared.
2094+ // 3. Use the last declared private symbol, by inserting a new symbol
2095+ // in the scope being processed, associated with it.
2096+ // If no private symbol was declared previously, then no association
2097+ // is needed and the symbol from the enclosing scope will be
2098+ // inherited by the current one.
2099+ //
2100+ // Because of how symbols are collected in lowering, not inserting a new
2101+ // symbol in the last case could lead to the conclusion that a symbol
2102+ // from an enclosing construct was declared in the current construct,
2103+ // which would result in wrong privatization code being generated.
20982104 // Consider the following example:
20992105 //
21002106 // !$omp parallel default(private) ! p1
@@ -2107,48 +2113,56 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
21072113 // (p2), it would use the x symbol definition from the enclosing scope.
21082114 // Then, when p2's default symbols were collected in lowering, the x
21092115 // symbol from the outer parallel construct (p1) would be collected, as
2110- // it would have the private flag set (note that symbols that don't have
2111- // any private flag are considered as shared).
2116+ // it would have the private flag set.
21122117 // This would make x appear to be defined in p2, causing it to be
21132118 // privatized in p2 and its privatization in p1 to be skipped.
2114- auto declNewSymbol = [&](Symbol::Flag flag) {
2119+ auto makePrivateSymbol = [&](Symbol::Flag flag) {
21152120 Symbol *hostSymbol =
21162121 lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate ();
21172122 lastDeclSymbol = DeclarePrivateAccessEntity (
21182123 *hostSymbol, flag, context_.FindScope (dirContext.directiveSource ));
21192124 return lastDeclSymbol;
21202125 };
2126+ auto makeSharedSymbol = [&]() {
2127+ Symbol *hostSymbol =
2128+ lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate ();
2129+ MakeAssocSymbol (symbol->name (), *hostSymbol,
2130+ context_.FindScope (dirContext.directiveSource ));
2131+ };
21212132 auto useLastDeclSymbol = [&]() {
21222133 if (lastDeclSymbol)
21232134 MakeAssocSymbol (symbol->name (), *lastDeclSymbol,
21242135 context_.FindScope (dirContext.directiveSource ));
21252136 };
21262137
2138+ bool taskGenDir = llvm::omp::taskGeneratingSet.test (dirContext.directive );
2139+ bool targetDir = llvm::omp::allTargetSet.test (dirContext.directive );
2140+ bool parallelDir = llvm::omp::allParallelSet.test (dirContext.directive );
2141+ bool teamsDir = llvm::omp::allTeamsSet.test (dirContext.directive );
2142+
21272143 if (dsa.has_value ()) {
2128- useLastDeclSymbol ();
2144+ if (dsa.value () == Symbol::Flag::OmpShared &&
2145+ (parallelDir || taskGenDir || teamsDir))
2146+ makeSharedSymbol ();
2147+ // Private symbols will have been declared already.
21292148 prevDSA = dsa;
21302149 continue ;
21312150 }
21322151
2133- bool taskGenDir = llvm::omp::taskGeneratingSet.test (dirContext.directive );
2134- bool targetDir = llvm::omp::allTargetSet.test (dirContext.directive );
2135- bool parallelDir = llvm::omp::allParallelSet.test (dirContext.directive );
2136-
21372152 if (dirContext.defaultDSA == Symbol::Flag::OmpPrivate ||
21382153 dirContext.defaultDSA == Symbol::Flag::OmpFirstPrivate ||
21392154 dirContext.defaultDSA == Symbol::Flag::OmpShared) {
21402155 // 1) default
21412156 // Allowed only with parallel, teams and task generating constructs.
2142- assert (parallelDir || taskGenDir ||
2143- llvm::omp::allTeamsSet.test (dirContext.directive ));
2157+ assert (parallelDir || taskGenDir || teamsDir);
21442158 if (dirContext.defaultDSA != Symbol::Flag::OmpShared)
2145- declNewSymbol (dirContext.defaultDSA );
2159+ makePrivateSymbol (dirContext.defaultDSA );
21462160 else
2147- useLastDeclSymbol ();
2161+ makeSharedSymbol ();
21482162 dsa = dirContext.defaultDSA ;
21492163 } else if (parallelDir) {
21502164 // 2) parallel -> shared
2151- useLastDeclSymbol ();
2165+ makeSharedSymbol ();
21522166 dsa = Symbol::Flag::OmpShared;
21532167 } else if (!taskGenDir && !targetDir) {
21542168 // 3) enclosing context
@@ -2161,12 +2175,12 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
21612175 // TODO 5) dummy arg in orphaned taskgen construct -> firstprivate
21622176 if (prevDSA == Symbol::Flag::OmpShared) {
21632177 // 6) shared in enclosing context -> shared
2164- useLastDeclSymbol ();
2178+ makeSharedSymbol ();
21652179 dsa = Symbol::Flag::OmpShared;
21662180 } else {
21672181 // 7) firstprivate
21682182 dsa = Symbol::Flag::OmpFirstPrivate;
2169- declNewSymbol (*dsa)->set (Symbol::Flag::OmpImplicit);
2183+ makePrivateSymbol (*dsa)->set (Symbol::Flag::OmpImplicit);
21702184 }
21712185 }
21722186 prevDSA = dsa;
@@ -2570,20 +2584,59 @@ void ResolveOmpTopLevelParts(
25702584 });
25712585}
25722586
2573- void OmpAttributeVisitor::CheckDataCopyingClause (
2574- const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
2575- const auto *checkSymbol{&symbol};
2587+ static bool IsSymbolInCommonBlock (const Symbol &symbol) {
2588+ // TODO Improve the performance of this predicate function.
2589+ // Going through all symbols sequentially, in all common blocks, can be
2590+ // slow when there are many symbols. A possible optimization is to add
2591+ // an OmpInCommonBlock flag to Symbol, to make it possible to quickly
2592+ // test if a given symbol is in a common block.
2593+ for (const auto &cb : symbol.owner ().commonBlocks ()) {
2594+ if (IsCommonBlockContaining (cb.second .get (), symbol)) {
2595+ return true ;
2596+ }
2597+ }
2598+ return false ;
2599+ }
2600+
2601+ static bool IsSymbolThreadprivate (const Symbol &symbol) {
25762602 if (const auto *details{symbol.detailsIf <HostAssocDetails>()}) {
2577- checkSymbol = &details->symbol ();
2603+ return details->symbol ().test (Symbol::Flag::OmpThreadprivate);
2604+ }
2605+ return symbol.test (Symbol::Flag::OmpThreadprivate);
2606+ }
2607+
2608+ static bool IsSymbolPrivate (const Symbol &symbol) {
2609+ if (symbol.test (Symbol::Flag::OmpPrivate) ||
2610+ symbol.test (Symbol::Flag::OmpFirstPrivate)) {
2611+ return true ;
2612+ }
2613+ // A symbol that has not gone through constructs that may privatize the
2614+ // original symbol may be predetermined as private.
2615+ // (OMP 5.2 5.1.1 - Variables Referenced in a Construct)
2616+ if (symbol == symbol.GetUltimate ()) {
2617+ switch (symbol.owner ().kind ()) {
2618+ case Scope::Kind::MainProgram:
2619+ case Scope::Kind::Subprogram:
2620+ case Scope::Kind::BlockConstruct:
2621+ return !symbol.attrs ().test (Attr::SAVE) &&
2622+ !symbol.attrs ().test (Attr::PARAMETER) && !IsAssumedShape (symbol) &&
2623+ !IsSymbolInCommonBlock (symbol);
2624+ default :
2625+ return false ;
2626+ }
25782627 }
2628+ return false ;
2629+ }
25792630
2631+ void OmpAttributeVisitor::CheckDataCopyingClause (
2632+ const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
25802633 if (ompFlag == Symbol::Flag::OmpCopyIn) {
25812634 // List of items/objects that can appear in a 'copyin' clause must be
25822635 // 'threadprivate'
2583- if (!checkSymbol-> test (Symbol::Flag::OmpThreadprivate )) {
2636+ if (!IsSymbolThreadprivate (symbol )) {
25842637 context_.Say (name.source ,
25852638 " Non-THREADPRIVATE object '%s' in COPYIN clause" _err_en_US,
2586- checkSymbol-> name ());
2639+ symbol. name ());
25872640 }
25882641 } else if (ompFlag == Symbol::Flag::OmpCopyPrivate &&
25892642 GetContext ().directive == llvm::omp::Directive::OMPD_single) {
@@ -2596,18 +2649,13 @@ void OmpAttributeVisitor::CheckDataCopyingClause(
25962649 " COPYPRIVATE variable '%s' may not appear on a PRIVATE or "
25972650 " FIRSTPRIVATE clause on a SINGLE construct" _err_en_US,
25982651 symbol.name ());
2599- } else {
2652+ } else if (! IsSymbolThreadprivate (symbol) && ! IsSymbolPrivate (symbol)) {
26002653 // List of items/objects that can appear in a 'copyprivate' clause must be
26012654 // either 'private' or 'threadprivate' in enclosing context.
2602- if (!checkSymbol->test (Symbol::Flag::OmpThreadprivate) &&
2603- !(HasSymbolInEnclosingScope (symbol, currScope ()) &&
2604- (symbol.test (Symbol::Flag::OmpPrivate) ||
2605- symbol.test (Symbol::Flag::OmpFirstPrivate)))) {
2606- context_.Say (name.source ,
2607- " COPYPRIVATE variable '%s' is not PRIVATE or THREADPRIVATE in "
2608- " outer context" _err_en_US,
2609- symbol.name ());
2610- }
2655+ context_.Say (name.source ,
2656+ " COPYPRIVATE variable '%s' is not PRIVATE or THREADPRIVATE in "
2657+ " outer context" _err_en_US,
2658+ symbol.name ());
26112659 }
26122660 }
26132661}
@@ -2677,12 +2725,6 @@ void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source,
26772725 }
26782726}
26792727
2680- bool OmpAttributeVisitor::HasSymbolInEnclosingScope (
2681- const Symbol &symbol, Scope &scope) {
2682- const auto symbols{scope.parent ().GetSymbols ()};
2683- return llvm::is_contained (symbols, symbol);
2684- }
2685-
26862728// Goes through the names in an OmpObjectList and checks if each name appears
26872729// in the given allocate statement
26882730void OmpAttributeVisitor::CheckAllNamesInAllocateStmt (
0 commit comments