@@ -149,7 +149,24 @@ template <typename T> class DirectiveAttributeVisitor {
149
149
dataSharingAttributeObjects_.clear ();
150
150
}
151
151
bool HasDataSharingAttributeObject (const Symbol &);
152
+
153
+ // / Extract the iv and bounds of a DO loop:
154
+ // / 1. The loop index/induction variable
155
+ // / 2. The lower bound
156
+ // / 3. The upper bound
157
+ // / 4. The step/increment (or nullptr if not present)
158
+ // /
159
+ // / Each returned tuple value can be nullptr if not present. Diagnoses an
160
+ // / error if the the DO loop is a DO WHILE or DO CONCURRENT loop.
161
+ std::tuple<const parser::Name *, const parser::ScalarExpr *,
162
+ const parser::ScalarExpr *, const parser::ScalarExpr *>
163
+ GetLoopBounds (const parser::DoConstruct &);
164
+
165
+ // / Extract the loop index/induction variable from a DO loop. Diagnoses an
166
+ // / error if the the DO loop is a DO WHILE or DO CONCURRENT loop and returns
167
+ // / nullptr.
152
168
const parser::Name *GetLoopIndex (const parser::DoConstruct &);
169
+
153
170
const parser::DoConstruct *GetDoConstructIf (
154
171
const parser::ExecutionPartConstruct &);
155
172
Symbol *DeclareNewAccessEntity (const Symbol &, Symbol::Flag, Scope &);
@@ -953,6 +970,13 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
953
970
privateDataSharingAttributeObjects_.clear ();
954
971
}
955
972
973
+ // / Check that loops in the loop nest are perfectly nested, as well that lower
974
+ // / bound, upper bound, and step expressions do not use the iv
975
+ // / of a surrounding loop of the associated loops nest.
976
+ // / We do not support non-perfectly nested loops not non-rectangular loops yet
977
+ // / (both introduced in OpenMP 5.0)
978
+ void CheckPerfectNestAndRectangularLoop (const parser::OpenMPLoopConstruct &x);
979
+
956
980
// Predetermined DSA rules
957
981
void PrivatizeAssociatedLoopIndexAndCheckLoopLevel (
958
982
const parser::OpenMPLoopConstruct &);
@@ -1028,23 +1052,30 @@ bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject(
1028
1052
}
1029
1053
1030
1054
template <typename T>
1031
- const parser::Name *DirectiveAttributeVisitor<T>::GetLoopIndex(
1032
- const parser::DoConstruct &x) {
1055
+ std::tuple<const parser::Name *, const parser::ScalarExpr *,
1056
+ const parser::ScalarExpr *, const parser::ScalarExpr *>
1057
+ DirectiveAttributeVisitor<T>::GetLoopBounds(const parser::DoConstruct &x) {
1033
1058
using Bounds = parser::LoopControl::Bounds;
1034
1059
if (x.GetLoopControl ()) {
1035
1060
if (const Bounds * b{std::get_if<Bounds>(&x.GetLoopControl ()->u )}) {
1036
- return & b->name . thing ;
1037
- } else {
1038
- return nullptr ;
1061
+ auto &step = b->step ;
1062
+ return {&b-> name . thing , &b-> lower , &b-> upper ,
1063
+ step. has_value () ? &step. value () : nullptr } ;
1039
1064
}
1040
1065
} else {
1041
1066
context_
1042
1067
.Say (std::get<parser::Statement<parser::NonLabelDoStmt>>(x.t ).source ,
1043
1068
" Loop control is not present in the DO LOOP" _err_en_US)
1044
1069
.Attach (GetContext ().directiveSource ,
1045
1070
" associated with the enclosing LOOP construct" _en_US);
1046
- return nullptr ;
1047
1071
}
1072
+ return {nullptr , nullptr , nullptr , nullptr };
1073
+ }
1074
+
1075
+ template <typename T>
1076
+ const parser::Name *DirectiveAttributeVisitor<T>::GetLoopIndex(
1077
+ const parser::DoConstruct &x) {
1078
+ return std::get<const parser::Name *>(GetLoopBounds (x));
1048
1079
}
1049
1080
1050
1081
template <typename T>
@@ -1990,6 +2021,10 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
1990
2021
}
1991
2022
}
1992
2023
}
2024
+
2025
+ // Must be done before iv privatization
2026
+ CheckPerfectNestAndRectangularLoop (x);
2027
+
1993
2028
PrivatizeAssociatedLoopIndexAndCheckLoopLevel (x);
1994
2029
ordCollapseLevel = GetNumAffectedLoopsFromLoopConstruct (x) + 1 ;
1995
2030
return true ;
@@ -2185,6 +2220,116 @@ void OmpAttributeVisitor::CollectNumAffectedLoopsFromClauses(
2185
2220
}
2186
2221
}
2187
2222
2223
+ void OmpAttributeVisitor::CheckPerfectNestAndRectangularLoop (
2224
+ const parser::OpenMPLoopConstruct &x) {
2225
+ auto &dirContext{GetContext ()};
2226
+ std::int64_t dirDepth{dirContext.associatedLoopLevel };
2227
+ if (dirDepth <= 0 )
2228
+ return ;
2229
+
2230
+ auto checkExprHasSymbols = [&](llvm::SmallVector<Symbol *> &ivs,
2231
+ const parser::ScalarExpr *bound) {
2232
+ if (ivs.empty ())
2233
+ return ;
2234
+ auto boundExpr{semantics::AnalyzeExpr (context_, *bound)};
2235
+ if (!boundExpr)
2236
+ return ;
2237
+ semantics::UnorderedSymbolSet boundSyms{
2238
+ evaluate::CollectSymbols (*boundExpr)};
2239
+ if (boundSyms.empty ())
2240
+ return ;
2241
+ for (Symbol *iv : ivs) {
2242
+ if (boundSyms.count (*iv) != 0 ) {
2243
+ // TODO: Point to occurence of iv in boundExpr, directiveSource as a
2244
+ // note
2245
+ context_.Say (dirContext.directiveSource ,
2246
+ " Trip count must be computable and invariant" _err_en_US);
2247
+ }
2248
+ }
2249
+ };
2250
+
2251
+ // Find the associated region by skipping nested loop-associated constructs
2252
+ // such as loop transformations
2253
+ const parser::NestedConstruct *innermostAssocRegion{nullptr };
2254
+ const parser::OpenMPLoopConstruct *innermostConstruct{&x};
2255
+ while (const auto &innerAssocStmt{
2256
+ std::get<std::optional<parser::NestedConstruct>>(
2257
+ innermostConstruct->t )}) {
2258
+ innermostAssocRegion = &(innerAssocStmt.value ());
2259
+ if (const auto *innerConstruct{
2260
+ std::get_if<common::Indirection<parser::OpenMPLoopConstruct>>(
2261
+ innermostAssocRegion)}) {
2262
+ innermostConstruct = &innerConstruct->value ();
2263
+ } else {
2264
+ break ;
2265
+ }
2266
+ }
2267
+
2268
+ if (!innermostAssocRegion)
2269
+ return ;
2270
+ const auto &outer{std::get_if<parser::DoConstruct>(innermostAssocRegion)};
2271
+ if (!outer)
2272
+ return ;
2273
+
2274
+ llvm::SmallVector<Symbol *> ivs;
2275
+ int curLevel{0 };
2276
+ const parser::DoConstruct *loop{outer};
2277
+ while (true ) {
2278
+ auto [iv, lb, ub, step] = GetLoopBounds (*loop);
2279
+
2280
+ if (lb)
2281
+ checkExprHasSymbols (ivs, lb);
2282
+ if (ub)
2283
+ checkExprHasSymbols (ivs, ub);
2284
+ if (step)
2285
+ checkExprHasSymbols (ivs, step);
2286
+ if (iv) {
2287
+ if (auto *symbol{currScope ().FindSymbol (iv->source )})
2288
+ ivs.push_back (symbol);
2289
+ }
2290
+
2291
+ // Stop after processing all affected loops
2292
+ if (curLevel + 1 >= dirDepth)
2293
+ break ;
2294
+
2295
+ // Recurse into nested loop
2296
+ const auto &block{std::get<parser::Block>(loop->t )};
2297
+ if (block.empty ()) {
2298
+ // Insufficient number of nested loops already reported by
2299
+ // CheckAssocLoopLevel()
2300
+ break ;
2301
+ }
2302
+
2303
+ loop = GetDoConstructIf (block.front ());
2304
+ if (!loop) {
2305
+ // Insufficient number of nested loops already reported by
2306
+ // CheckAssocLoopLevel()
2307
+ break ;
2308
+ }
2309
+
2310
+ auto checkPerfectNest = [&, this ]() {
2311
+ auto blockSize = block.size ();
2312
+ if (blockSize <= 1 )
2313
+ return ;
2314
+
2315
+ if (parser::Unwrap<parser::ContinueStmt>(x))
2316
+ blockSize -= 1 ;
2317
+
2318
+ if (blockSize <= 1 )
2319
+ return ;
2320
+
2321
+ // Non-perfectly nested loop
2322
+ // TODO: Point to non-DO statement, directiveSource as a note
2323
+ context_.Say (dirContext.directiveSource ,
2324
+ " Canonical loop nest must be perfectly nested." _err_en_US);
2325
+ };
2326
+
2327
+ checkPerfectNest ();
2328
+
2329
+ ++curLevel;
2330
+ }
2331
+ }
2332
+
2188
2333
// 2.15.1.1 Data-sharing Attribute Rules - Predetermined
2189
2334
// - The loop iteration variable(s) in the associated do-loop(s) of a do,
2190
2335
// parallel do, taskloop, or distribute construct is (are) private.
0 commit comments