@@ -149,7 +149,24 @@ template <typename T> class DirectiveAttributeVisitor {
149149 dataSharingAttributeObjects_.clear ();
150150 }
151151 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.
152168 const parser::Name *GetLoopIndex (const parser::DoConstruct &);
169+
153170 const parser::DoConstruct *GetDoConstructIf (
154171 const parser::ExecutionPartConstruct &);
155172 Symbol *DeclareNewAccessEntity (const Symbol &, Symbol::Flag, Scope &);
@@ -953,6 +970,13 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
953970 privateDataSharingAttributeObjects_.clear ();
954971 }
955972
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+
956980 // Predetermined DSA rules
957981 void PrivatizeAssociatedLoopIndexAndCheckLoopLevel (
958982 const parser::OpenMPLoopConstruct &);
@@ -1028,23 +1052,30 @@ bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject(
10281052}
10291053
10301054template <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) {
10331058 using Bounds = parser::LoopControl::Bounds;
10341059 if (x.GetLoopControl ()) {
10351060 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 } ;
10391064 }
10401065 } else {
10411066 context_
10421067 .Say (std::get<parser::Statement<parser::NonLabelDoStmt>>(x.t ).source ,
10431068 " Loop control is not present in the DO LOOP" _err_en_US)
10441069 .Attach (GetContext ().directiveSource ,
10451070 " associated with the enclosing LOOP construct" _en_US);
1046- return nullptr ;
10471071 }
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));
10481079}
10491080
10501081template <typename T>
@@ -1990,6 +2021,10 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
19902021 }
19912022 }
19922023 }
2024+
2025+ // Must be done before iv privatization
2026+ CheckPerfectNestAndRectangularLoop (x);
2027+
19932028 PrivatizeAssociatedLoopIndexAndCheckLoopLevel (x);
19942029 ordCollapseLevel = GetNumAffectedLoopsFromLoopConstruct (x) + 1 ;
19952030 return true ;
@@ -2185,6 +2220,116 @@ void OmpAttributeVisitor::CollectNumAffectedLoopsFromClauses(
21852220 }
21862221}
21872222
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+
21882333// 2.15.1.1 Data-sharing Attribute Rules - Predetermined
21892334// - The loop iteration variable(s) in the associated do-loop(s) of a do,
21902335// parallel do, taskloop, or distribute construct is (are) private.
0 commit comments