19
19
#include " flang/Parser/parse-tree.h"
20
20
#include " flang/Parser/tools.h"
21
21
#include " flang/Semantics/expression.h"
22
+ #include " flang/Semantics/tools.h"
22
23
#include < list>
23
24
#include < map>
24
25
#include < sstream>
@@ -729,7 +730,6 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
729
730
void CheckNameInAllocateStmt (const parser::CharBlock &source,
730
731
const parser::Name &ompObject, const parser::AllocateStmt &allocate);
731
732
732
- bool HasSymbolInEnclosingScope (const Symbol &, Scope &);
733
733
std::int64_t ordCollapseLevel{0 };
734
734
735
735
void AddOmpRequiresToScope (Scope &, WithOmpDeclarative::RequiresFlags,
@@ -2035,15 +2035,22 @@ void OmpAttributeVisitor::Post(const parser::OpenMPAllocatorsConstruct &x) {
2035
2035
// and adjust the symbol for each Name if necessary
2036
2036
void OmpAttributeVisitor::Post (const parser::Name &name) {
2037
2037
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
+
2038
2046
if (symbol && !dirContext_.empty () && GetContext ().withinConstruct ) {
2039
2047
// Exclude construct-names
2040
2048
if (auto *details{symbol->detailsIf <semantics::MiscDetails>()}) {
2041
2049
if (details->kind () == semantics::MiscDetails::Kind::ConstructName) {
2042
2050
return ;
2043
2051
}
2044
2052
}
2045
- if (!symbol->owner ().IsDerivedType () && !IsProcedure (*symbol) &&
2046
- !IsObjectWithDSA (*symbol) && !IsNamedConstant (*symbol)) {
2053
+ if (IsPrivatizable (symbol) && !IsObjectWithDSA (*symbol)) {
2047
2054
// TODO: create a separate function to go through the rules for
2048
2055
// predetermined, explicitly determined, and implicitly
2049
2056
// determined data-sharing attributes (2.15.1.1).
@@ -2068,6 +2075,9 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
2068
2075
if (found->test (semantics::Symbol::Flag::OmpThreadprivate))
2069
2076
return ;
2070
2077
}
2078
+ if (!IsPrivatizable (symbol)) {
2079
+ return ;
2080
+ }
2071
2081
2072
2082
// Implicitly determined DSAs
2073
2083
// OMP 5.2 5.1.1 - Variables Referenced in a Construct
@@ -2085,16 +2095,22 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
2085
2095
}
2086
2096
}
2087
2097
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.
2098
2114
// Consider the following example:
2099
2115
//
2100
2116
// !$omp parallel default(private) ! p1
@@ -2107,48 +2123,56 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
2107
2123
// (p2), it would use the x symbol definition from the enclosing scope.
2108
2124
// Then, when p2's default symbols were collected in lowering, the x
2109
2125
// 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.
2112
2127
// This would make x appear to be defined in p2, causing it to be
2113
2128
// privatized in p2 and its privatization in p1 to be skipped.
2114
- auto declNewSymbol = [&](Symbol::Flag flag) {
2129
+ auto makePrivateSymbol = [&](Symbol::Flag flag) {
2115
2130
Symbol *hostSymbol =
2116
2131
lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate ();
2117
2132
lastDeclSymbol = DeclarePrivateAccessEntity (
2118
2133
*hostSymbol, flag, context_.FindScope (dirContext.directiveSource ));
2119
2134
return lastDeclSymbol;
2120
2135
};
2136
+ auto makeSharedSymbol = [&]() {
2137
+ Symbol *hostSymbol =
2138
+ lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate ();
2139
+ MakeAssocSymbol (symbol->name (), *hostSymbol,
2140
+ context_.FindScope (dirContext.directiveSource ));
2141
+ };
2121
2142
auto useLastDeclSymbol = [&]() {
2122
2143
if (lastDeclSymbol)
2123
2144
MakeAssocSymbol (symbol->name (), *lastDeclSymbol,
2124
2145
context_.FindScope (dirContext.directiveSource ));
2125
2146
};
2126
2147
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
+
2127
2153
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.
2129
2158
prevDSA = dsa;
2130
2159
continue ;
2131
2160
}
2132
2161
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
-
2137
2162
if (dirContext.defaultDSA == Symbol::Flag::OmpPrivate ||
2138
2163
dirContext.defaultDSA == Symbol::Flag::OmpFirstPrivate ||
2139
2164
dirContext.defaultDSA == Symbol::Flag::OmpShared) {
2140
2165
// 1) default
2141
2166
// 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);
2144
2168
if (dirContext.defaultDSA != Symbol::Flag::OmpShared)
2145
- declNewSymbol (dirContext.defaultDSA );
2169
+ makePrivateSymbol (dirContext.defaultDSA );
2146
2170
else
2147
- useLastDeclSymbol ();
2171
+ makeSharedSymbol ();
2148
2172
dsa = dirContext.defaultDSA ;
2149
2173
} else if (parallelDir) {
2150
2174
// 2) parallel -> shared
2151
- useLastDeclSymbol ();
2175
+ makeSharedSymbol ();
2152
2176
dsa = Symbol::Flag::OmpShared;
2153
2177
} else if (!taskGenDir && !targetDir) {
2154
2178
// 3) enclosing context
@@ -2161,12 +2185,12 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
2161
2185
// TODO 5) dummy arg in orphaned taskgen construct -> firstprivate
2162
2186
if (prevDSA == Symbol::Flag::OmpShared) {
2163
2187
// 6) shared in enclosing context -> shared
2164
- useLastDeclSymbol ();
2188
+ makeSharedSymbol ();
2165
2189
dsa = Symbol::Flag::OmpShared;
2166
2190
} else {
2167
2191
// 7) firstprivate
2168
2192
dsa = Symbol::Flag::OmpFirstPrivate;
2169
- declNewSymbol (*dsa)->set (Symbol::Flag::OmpImplicit);
2193
+ makePrivateSymbol (*dsa)->set (Symbol::Flag::OmpImplicit);
2170
2194
}
2171
2195
}
2172
2196
prevDSA = dsa;
@@ -2570,20 +2594,59 @@ void ResolveOmpTopLevelParts(
2570
2594
});
2571
2595
}
2572
2596
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) {
2576
2612
if (const auto *details{symbol.detailsIf <HostAssocDetails>()}) {
2577
- checkSymbol = & details->symbol ();
2613
+ return details->symbol (). test (Symbol::Flag::OmpThreadprivate );
2578
2614
}
2615
+ return symbol.test (Symbol::Flag::OmpThreadprivate);
2616
+ }
2579
2617
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) {
2580
2643
if (ompFlag == Symbol::Flag::OmpCopyIn) {
2581
2644
// List of items/objects that can appear in a 'copyin' clause must be
2582
2645
// 'threadprivate'
2583
- if (!checkSymbol-> test (Symbol::Flag::OmpThreadprivate )) {
2646
+ if (!IsSymbolThreadprivate (symbol )) {
2584
2647
context_.Say (name.source ,
2585
2648
" Non-THREADPRIVATE object '%s' in COPYIN clause" _err_en_US,
2586
- checkSymbol-> name ());
2649
+ symbol. name ());
2587
2650
}
2588
2651
} else if (ompFlag == Symbol::Flag::OmpCopyPrivate &&
2589
2652
GetContext ().directive == llvm::omp::Directive::OMPD_single) {
@@ -2596,18 +2659,13 @@ void OmpAttributeVisitor::CheckDataCopyingClause(
2596
2659
" COPYPRIVATE variable '%s' may not appear on a PRIVATE or "
2597
2660
" FIRSTPRIVATE clause on a SINGLE construct" _err_en_US,
2598
2661
symbol.name ());
2599
- } else {
2662
+ } else if (! IsSymbolThreadprivate (symbol) && ! IsSymbolPrivate (symbol)) {
2600
2663
// List of items/objects that can appear in a 'copyprivate' clause must be
2601
2664
// 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 ());
2611
2669
}
2612
2670
}
2613
2671
}
@@ -2677,12 +2735,6 @@ void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source,
2677
2735
}
2678
2736
}
2679
2737
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
-
2686
2738
// Goes through the names in an OmpObjectList and checks if each name appears
2687
2739
// in the given allocate statement
2688
2740
void OmpAttributeVisitor::CheckAllNamesInAllocateStmt (
0 commit comments