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,
@@ -2035,15 +2035,22 @@ void OmpAttributeVisitor::Post(const parser::OpenMPAllocatorsConstruct &x) {
20352035// and adjust the symbol for each Name if necessary
20362036void OmpAttributeVisitor::Post (const parser::Name &name) {
20372037 auto *symbol{name.symbol };
2038+ auto IsPrivatizable = [](const Symbol *sym) {
2039+ return !IsProcedure (*sym) && !IsNamedConstant (*sym) &&
2040+ !sym->owner ().IsDerivedType () &&
2041+ sym->owner ().kind () != Scope::Kind::ImpliedDos &&
2042+ !sym->detailsIf <semantics::AssocEntityDetails>() &&
2043+ !sym->detailsIf <semantics::NamelistDetails>();
2044+ };
2045+
20382046 if (symbol && !dirContext_.empty () && GetContext ().withinConstruct ) {
20392047 // Exclude construct-names
20402048 if (auto *details{symbol->detailsIf <semantics::MiscDetails>()}) {
20412049 if (details->kind () == semantics::MiscDetails::Kind::ConstructName) {
20422050 return ;
20432051 }
20442052 }
2045- if (!symbol->owner ().IsDerivedType () && !IsProcedure (*symbol) &&
2046- !IsObjectWithDSA (*symbol) && !IsNamedConstant (*symbol)) {
2053+ if (IsPrivatizable (symbol) && !IsObjectWithDSA (*symbol)) {
20472054 // TODO: create a separate function to go through the rules for
20482055 // predetermined, explicitly determined, and implicitly
20492056 // determined data-sharing attributes (2.15.1.1).
@@ -2068,6 +2075,9 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
20682075 if (found->test (semantics::Symbol::Flag::OmpThreadprivate))
20692076 return ;
20702077 }
2078+ if (!IsPrivatizable (symbol)) {
2079+ return ;
2080+ }
20712081
20722082 // Implicitly determined DSAs
20732083 // OMP 5.2 5.1.1 - Variables Referenced in a Construct
@@ -2085,16 +2095,22 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
20852095 }
20862096 }
20872097
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.
2098+ // When handling each implicit rule for a given symbol, one of the
2099+ // following 3 actions may be taken:
2100+ // 1. Declare a new private symbol.
2101+ // 2. Create a new association symbol with no flags, that will represent
2102+ // a shared symbol in the current scope. Note that symbols without
2103+ // any private flags are considered as shared.
2104+ // 3. Use the last declared private symbol, by inserting a new symbol
2105+ // in the scope being processed, associated with it.
2106+ // If no private symbol was declared previously, then no association
2107+ // is needed and the symbol from the enclosing scope will be
2108+ // inherited by the current one.
2109+ //
2110+ // Because of how symbols are collected in lowering, not inserting a new
2111+ // symbol in the last case could lead to the conclusion that a symbol
2112+ // from an enclosing construct was declared in the current construct,
2113+ // which would result in wrong privatization code being generated.
20982114 // Consider the following example:
20992115 //
21002116 // !$omp parallel default(private) ! p1
@@ -2107,48 +2123,56 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
21072123 // (p2), it would use the x symbol definition from the enclosing scope.
21082124 // Then, when p2's default symbols were collected in lowering, the x
21092125 // 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).
2126+ // it would have the private flag set.
21122127 // This would make x appear to be defined in p2, causing it to be
21132128 // privatized in p2 and its privatization in p1 to be skipped.
2114- auto declNewSymbol = [&](Symbol::Flag flag) {
2129+ auto makePrivateSymbol = [&](Symbol::Flag flag) {
21152130 Symbol *hostSymbol =
21162131 lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate ();
21172132 lastDeclSymbol = DeclarePrivateAccessEntity (
21182133 *hostSymbol, flag, context_.FindScope (dirContext.directiveSource ));
21192134 return lastDeclSymbol;
21202135 };
2136+ auto makeSharedSymbol = [&]() {
2137+ Symbol *hostSymbol =
2138+ lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate ();
2139+ MakeAssocSymbol (symbol->name (), *hostSymbol,
2140+ context_.FindScope (dirContext.directiveSource ));
2141+ };
21212142 auto useLastDeclSymbol = [&]() {
21222143 if (lastDeclSymbol)
21232144 MakeAssocSymbol (symbol->name (), *lastDeclSymbol,
21242145 context_.FindScope (dirContext.directiveSource ));
21252146 };
21262147
2148+ bool taskGenDir = llvm::omp::taskGeneratingSet.test (dirContext.directive );
2149+ bool targetDir = llvm::omp::allTargetSet.test (dirContext.directive );
2150+ bool parallelDir = llvm::omp::allParallelSet.test (dirContext.directive );
2151+ bool teamsDir = llvm::omp::allTeamsSet.test (dirContext.directive );
2152+
21272153 if (dsa.has_value ()) {
2128- useLastDeclSymbol ();
2154+ if (dsa.value () == Symbol::Flag::OmpShared &&
2155+ (parallelDir || taskGenDir || teamsDir))
2156+ makeSharedSymbol ();
2157+ // Private symbols will have been declared already.
21292158 prevDSA = dsa;
21302159 continue ;
21312160 }
21322161
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-
21372162 if (dirContext.defaultDSA == Symbol::Flag::OmpPrivate ||
21382163 dirContext.defaultDSA == Symbol::Flag::OmpFirstPrivate ||
21392164 dirContext.defaultDSA == Symbol::Flag::OmpShared) {
21402165 // 1) default
21412166 // Allowed only with parallel, teams and task generating constructs.
2142- assert (parallelDir || taskGenDir ||
2143- llvm::omp::allTeamsSet.test (dirContext.directive ));
2167+ assert (parallelDir || taskGenDir || teamsDir);
21442168 if (dirContext.defaultDSA != Symbol::Flag::OmpShared)
2145- declNewSymbol (dirContext.defaultDSA );
2169+ makePrivateSymbol (dirContext.defaultDSA );
21462170 else
2147- useLastDeclSymbol ();
2171+ makeSharedSymbol ();
21482172 dsa = dirContext.defaultDSA ;
21492173 } else if (parallelDir) {
21502174 // 2) parallel -> shared
2151- useLastDeclSymbol ();
2175+ makeSharedSymbol ();
21522176 dsa = Symbol::Flag::OmpShared;
21532177 } else if (!taskGenDir && !targetDir) {
21542178 // 3) enclosing context
@@ -2161,12 +2185,12 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
21612185 // TODO 5) dummy arg in orphaned taskgen construct -> firstprivate
21622186 if (prevDSA == Symbol::Flag::OmpShared) {
21632187 // 6) shared in enclosing context -> shared
2164- useLastDeclSymbol ();
2188+ makeSharedSymbol ();
21652189 dsa = Symbol::Flag::OmpShared;
21662190 } else {
21672191 // 7) firstprivate
21682192 dsa = Symbol::Flag::OmpFirstPrivate;
2169- declNewSymbol (*dsa)->set (Symbol::Flag::OmpImplicit);
2193+ makePrivateSymbol (*dsa)->set (Symbol::Flag::OmpImplicit);
21702194 }
21712195 }
21722196 prevDSA = dsa;
@@ -2570,20 +2594,59 @@ void ResolveOmpTopLevelParts(
25702594 });
25712595}
25722596
2573- void OmpAttributeVisitor::CheckDataCopyingClause (
2574- const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
2575- const auto *checkSymbol{&symbol};
2597+ static bool IsSymbolInCommonBlock (const Symbol &symbol) {
2598+ // TODO Improve the performance of this predicate function.
2599+ // Going through all symbols sequentially, in all common blocks, can be
2600+ // slow when there are many symbols. A possible optimization is to add
2601+ // an OmpInCommonBlock flag to Symbol, to make it possible to quickly
2602+ // test if a given symbol is in a common block.
2603+ for (const auto &cb : symbol.owner ().commonBlocks ()) {
2604+ if (IsCommonBlockContaining (cb.second .get (), symbol)) {
2605+ return true ;
2606+ }
2607+ }
2608+ return false ;
2609+ }
2610+
2611+ static bool IsSymbolThreadprivate (const Symbol &symbol) {
25762612 if (const auto *details{symbol.detailsIf <HostAssocDetails>()}) {
2577- checkSymbol = & details->symbol ();
2613+ return details->symbol (). test (Symbol::Flag::OmpThreadprivate );
25782614 }
2615+ return symbol.test (Symbol::Flag::OmpThreadprivate);
2616+ }
25792617
2618+ static bool IsSymbolPrivate (const Symbol &symbol) {
2619+ if (symbol.test (Symbol::Flag::OmpPrivate) ||
2620+ symbol.test (Symbol::Flag::OmpFirstPrivate)) {
2621+ return true ;
2622+ }
2623+ // A symbol that has not gone through constructs that may privatize the
2624+ // original symbol may be predetermined as private.
2625+ // (OMP 5.2 5.1.1 - Variables Referenced in a Construct)
2626+ if (symbol == symbol.GetUltimate ()) {
2627+ switch (symbol.owner ().kind ()) {
2628+ case Scope::Kind::MainProgram:
2629+ case Scope::Kind::Subprogram:
2630+ case Scope::Kind::BlockConstruct:
2631+ return !symbol.attrs ().test (Attr::SAVE) &&
2632+ !symbol.attrs ().test (Attr::PARAMETER) && !IsAssumedShape (symbol) &&
2633+ !IsSymbolInCommonBlock (symbol);
2634+ default :
2635+ return false ;
2636+ }
2637+ }
2638+ return false ;
2639+ }
2640+
2641+ void OmpAttributeVisitor::CheckDataCopyingClause (
2642+ const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
25802643 if (ompFlag == Symbol::Flag::OmpCopyIn) {
25812644 // List of items/objects that can appear in a 'copyin' clause must be
25822645 // 'threadprivate'
2583- if (!checkSymbol-> test (Symbol::Flag::OmpThreadprivate )) {
2646+ if (!IsSymbolThreadprivate (symbol )) {
25842647 context_.Say (name.source ,
25852648 " Non-THREADPRIVATE object '%s' in COPYIN clause" _err_en_US,
2586- checkSymbol-> name ());
2649+ symbol. name ());
25872650 }
25882651 } else if (ompFlag == Symbol::Flag::OmpCopyPrivate &&
25892652 GetContext ().directive == llvm::omp::Directive::OMPD_single) {
@@ -2596,18 +2659,13 @@ void OmpAttributeVisitor::CheckDataCopyingClause(
25962659 " COPYPRIVATE variable '%s' may not appear on a PRIVATE or "
25972660 " FIRSTPRIVATE clause on a SINGLE construct" _err_en_US,
25982661 symbol.name ());
2599- } else {
2662+ } else if (! IsSymbolThreadprivate (symbol) && ! IsSymbolPrivate (symbol)) {
26002663 // List of items/objects that can appear in a 'copyprivate' clause must be
26012664 // 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- }
2665+ context_.Say (name.source ,
2666+ " COPYPRIVATE variable '%s' is not PRIVATE or THREADPRIVATE in "
2667+ " outer context" _err_en_US,
2668+ symbol.name ());
26112669 }
26122670 }
26132671}
@@ -2677,12 +2735,6 @@ void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source,
26772735 }
26782736}
26792737
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-
26862738// Goes through the names in an OmpObjectList and checks if each name appears
26872739// in the given allocate statement
26882740void OmpAttributeVisitor::CheckAllNamesInAllocateStmt (
0 commit comments