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,
@@ -2085,16 +2085,22 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
2085
2085
}
2086
2086
}
2087
2087
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.
2098
2104
// Consider the following example:
2099
2105
//
2100
2106
// !$omp parallel default(private) ! p1
@@ -2107,48 +2113,56 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
2107
2113
// (p2), it would use the x symbol definition from the enclosing scope.
2108
2114
// Then, when p2's default symbols were collected in lowering, the x
2109
2115
// 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.
2112
2117
// This would make x appear to be defined in p2, causing it to be
2113
2118
// privatized in p2 and its privatization in p1 to be skipped.
2114
- auto declNewSymbol = [&](Symbol::Flag flag) {
2119
+ auto makePrivateSymbol = [&](Symbol::Flag flag) {
2115
2120
Symbol *hostSymbol =
2116
2121
lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate ();
2117
2122
lastDeclSymbol = DeclarePrivateAccessEntity (
2118
2123
*hostSymbol, flag, context_.FindScope (dirContext.directiveSource ));
2119
2124
return lastDeclSymbol;
2120
2125
};
2126
+ auto makeSharedSymbol = [&]() {
2127
+ Symbol *hostSymbol =
2128
+ lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate ();
2129
+ MakeAssocSymbol (symbol->name (), *hostSymbol,
2130
+ context_.FindScope (dirContext.directiveSource ));
2131
+ };
2121
2132
auto useLastDeclSymbol = [&]() {
2122
2133
if (lastDeclSymbol)
2123
2134
MakeAssocSymbol (symbol->name (), *lastDeclSymbol,
2124
2135
context_.FindScope (dirContext.directiveSource ));
2125
2136
};
2126
2137
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
+
2127
2143
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.
2129
2148
prevDSA = dsa;
2130
2149
continue ;
2131
2150
}
2132
2151
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
2152
if (dirContext.defaultDSA == Symbol::Flag::OmpPrivate ||
2138
2153
dirContext.defaultDSA == Symbol::Flag::OmpFirstPrivate ||
2139
2154
dirContext.defaultDSA == Symbol::Flag::OmpShared) {
2140
2155
// 1) default
2141
2156
// 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);
2144
2158
if (dirContext.defaultDSA != Symbol::Flag::OmpShared)
2145
- declNewSymbol (dirContext.defaultDSA );
2159
+ makePrivateSymbol (dirContext.defaultDSA );
2146
2160
else
2147
- useLastDeclSymbol ();
2161
+ makeSharedSymbol ();
2148
2162
dsa = dirContext.defaultDSA ;
2149
2163
} else if (parallelDir) {
2150
2164
// 2) parallel -> shared
2151
- useLastDeclSymbol ();
2165
+ makeSharedSymbol ();
2152
2166
dsa = Symbol::Flag::OmpShared;
2153
2167
} else if (!taskGenDir && !targetDir) {
2154
2168
// 3) enclosing context
@@ -2161,12 +2175,12 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
2161
2175
// TODO 5) dummy arg in orphaned taskgen construct -> firstprivate
2162
2176
if (prevDSA == Symbol::Flag::OmpShared) {
2163
2177
// 6) shared in enclosing context -> shared
2164
- useLastDeclSymbol ();
2178
+ makeSharedSymbol ();
2165
2179
dsa = Symbol::Flag::OmpShared;
2166
2180
} else {
2167
2181
// 7) firstprivate
2168
2182
dsa = Symbol::Flag::OmpFirstPrivate;
2169
- declNewSymbol (*dsa)->set (Symbol::Flag::OmpImplicit);
2183
+ makePrivateSymbol (*dsa)->set (Symbol::Flag::OmpImplicit);
2170
2184
}
2171
2185
}
2172
2186
prevDSA = dsa;
@@ -2570,20 +2584,59 @@ void ResolveOmpTopLevelParts(
2570
2584
});
2571
2585
}
2572
2586
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) {
2576
2602
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
+ }
2578
2627
}
2628
+ return false ;
2629
+ }
2579
2630
2631
+ void OmpAttributeVisitor::CheckDataCopyingClause (
2632
+ const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
2580
2633
if (ompFlag == Symbol::Flag::OmpCopyIn) {
2581
2634
// List of items/objects that can appear in a 'copyin' clause must be
2582
2635
// 'threadprivate'
2583
- if (!checkSymbol-> test (Symbol::Flag::OmpThreadprivate )) {
2636
+ if (!IsSymbolThreadprivate (symbol )) {
2584
2637
context_.Say (name.source ,
2585
2638
" Non-THREADPRIVATE object '%s' in COPYIN clause" _err_en_US,
2586
- checkSymbol-> name ());
2639
+ symbol. name ());
2587
2640
}
2588
2641
} else if (ompFlag == Symbol::Flag::OmpCopyPrivate &&
2589
2642
GetContext ().directive == llvm::omp::Directive::OMPD_single) {
@@ -2596,18 +2649,13 @@ void OmpAttributeVisitor::CheckDataCopyingClause(
2596
2649
" COPYPRIVATE variable '%s' may not appear on a PRIVATE or "
2597
2650
" FIRSTPRIVATE clause on a SINGLE construct" _err_en_US,
2598
2651
symbol.name ());
2599
- } else {
2652
+ } else if (! IsSymbolThreadprivate (symbol) && ! IsSymbolPrivate (symbol)) {
2600
2653
// List of items/objects that can appear in a 'copyprivate' clause must be
2601
2654
// 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 ());
2611
2659
}
2612
2660
}
2613
2661
}
@@ -2677,12 +2725,6 @@ void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source,
2677
2725
}
2678
2726
}
2679
2727
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
2728
// Goes through the names in an OmpObjectList and checks if each name appears
2687
2729
// in the given allocate statement
2688
2730
void OmpAttributeVisitor::CheckAllNamesInAllocateStmt (
0 commit comments