@@ -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" 
10441069        .Attach (GetContext ().directiveSource ,
10451070            " associated with the enclosing LOOP construct" 
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" 
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." 
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