[clang] [OpenACC] Implement 'tile' loop count/tightly nested loop requirement (PR #111038)

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 3 11:25:07 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Erich Keane (erichkeane)

<details>
<summary>Changes</summary>

the 'tile' clause requires that it be followed by N (where N is the
    number of size expressions) 'tightly nested loops'.  This means the
same as it does in 'collapse', so much of the implementation is simliar/shared with that.

---

Patch is 32.39 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/111038.diff


7 Files Affected:

- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+11-11) 
- (modified) clang/include/clang/Sema/SemaOpenACC.h (+53-20) 
- (modified) clang/lib/Sema/SemaOpenACC.cpp (+130-34) 
- (modified) clang/test/ParserOpenACC/parse-clauses.c (+14-4) 
- (modified) clang/test/SemaOpenACC/loop-construct-auto_seq_independent-clauses.c (+12-6) 
- (modified) clang/test/SemaOpenACC/loop-construct-device_type-clause.c (+3-1) 
- (modified) clang/test/SemaOpenACC/loop-construct-tile-clause.cpp (+281) 


``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 59dc81cfeb7111..583475327c5227 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12670,19 +12670,19 @@ def err_acc_size_expr_value
     : Error<
           "OpenACC 'tile' clause size expression must be %select{an asterisk "
           "or a constant expression|positive integer value, evaluated to %1}0">;
-def err_acc_invalid_in_collapse_loop
-    : Error<"%select{OpenACC '%1' construct|while loop|do loop}0 cannot appear "
-            "in intervening code of a 'loop' with a 'collapse' clause">;
-def note_acc_collapse_clause_here
-    : Note<"active 'collapse' clause defined here">;
-def err_acc_collapse_multiple_loops
+def err_acc_invalid_in_loop
+    : Error<"%select{OpenACC '%2' construct|while loop|do loop}0 cannot appear "
+            "in intervening code of a 'loop' with a '%1' clause">;
+def note_acc_active_clause_here
+    : Note<"active '%0' clause defined here">;
+def err_acc_clause_multiple_loops
     : Error<"more than one for-loop in a loop associated with OpenACC 'loop' "
-            "construct with a 'collapse' clause">;
-def err_acc_collapse_insufficient_loops
-    : Error<"'collapse' clause specifies a loop count greater than the number "
+            "construct with a '%select{collapse|tile}0' clause">;
+def err_acc_insufficient_loops
+    : Error<"'%0' clause specifies a loop count greater than the number "
             "of available loops">;
-def err_acc_collapse_intervening_code
-    : Error<"inner loops must be tightly nested inside a 'collapse' clause on "
+def err_acc_intervening_code
+    : Error<"inner loops must be tightly nested inside a '%0' clause on "
             "a 'loop' construct">;
 
 // AMDGCN builtins diagnostics
diff --git a/clang/include/clang/Sema/SemaOpenACC.h b/clang/include/clang/Sema/SemaOpenACC.h
index d25c52ec3be43a..97386d2378b758 100644
--- a/clang/include/clang/Sema/SemaOpenACC.h
+++ b/clang/include/clang/Sema/SemaOpenACC.h
@@ -42,6 +42,23 @@ class SemaOpenACC : public SemaBase {
   /// above collection.
   bool InsideComputeConstruct = false;
 
+  /// Certain clauses care about the same things that aren't specific to the
+  /// individual clause, but can be shared by a few, so store them here. All
+  /// require a 'no intervening constructs' rule, so we know they are all from
+  /// the same 'place'.
+  struct LoopCheckingInfo {
+    /// Records whether we've seen the top level 'for'. We already diagnose
+    /// later that the 'top level' is a for loop, so we use this to suppress the
+    /// 'collapse inner loop not a 'for' loop' diagnostic.
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned TopLevelLoopSeen : 1;
+
+    /// Records whether this 'tier' of the loop has already seen a 'for' loop,
+    /// used to diagnose if there are multiple 'for' loops at any one level.
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned CurLevelHasLoopAlready : 1;
+  } LoopInfo{/*TopLevelLoopSeen=*/false, /*CurLevelHasLoopAlready=*/false};
+
   /// The 'collapse' clause requires quite a bit of checking while
   /// parsing/instantiating its body, so this structure/object keeps all of the
   /// necessary information as we do checking.  This should rarely be directly
@@ -59,25 +76,27 @@ class SemaOpenACC : public SemaBase {
     /// else it should be 'N' minus the current depth traversed.
     std::optional<llvm::APSInt> CurCollapseCount;
 
-    /// Records whether we've seen the top level 'for'. We already diagnose
-    /// later that the 'top level' is a for loop, so we use this to suppress the
-    /// 'collapse inner loop not a 'for' loop' diagnostic.
-    LLVM_PREFERRED_TYPE(bool)
-    unsigned TopLevelLoopSeen : 1;
-
-    /// Records whether this 'tier' of the loop has already seen a 'for' loop,
-    /// used to diagnose if there are multiple 'for' loops at any one level.
-    LLVM_PREFERRED_TYPE(bool)
-    unsigned CurLevelHasLoopAlready : 1;
-
     /// Records whether we've hit a CurCollapseCount of '0' on the way down,
     /// which allows us to diagnose if the value of 'N' is too large for the
     /// current number of 'for' loops.
-    LLVM_PREFERRED_TYPE(bool)
-    unsigned CollapseDepthSatisfied : 1;
-  } CollapseInfo{nullptr, std::nullopt, /*TopLevelLoopSeen=*/false,
-                 /*CurLevelHasLoopAlready=*/false,
-                 /*CollapseDepthSatisfied=*/true};
+    bool CollapseDepthSatisfied = true;
+  } CollapseInfo;
+
+  /// The 'tile' clause requires a bit of additional checking as well, so like
+  /// the `CollapseCheckingInfo`, ensure we maintain information here too.
+  struct TileCheckingInfo {
+    OpenACCTileClause *ActiveTile = nullptr;
+
+    /// This is the number of expressions on a 'tile' clause.  This doesn't have
+    /// to be an APSInt because it isn't the result of a constexpr, just by our
+    /// own counting of elements.
+    std::optional<unsigned> CurTileCount;
+
+    /// Records whether we've hit a 'CurTileCount' of '0' on the wya down,
+    /// which allows us to diagnose if the number of arguments is too large for
+    /// the current number of 'for' loops.
+    bool TileDepthSatisfied = true;
+  } TileInfo;
 
 public:
   // Redeclaration of the version in OpenACCClause.h.
@@ -537,12 +556,15 @@ class SemaOpenACC : public SemaBase {
   /// into a loop (for, etc) inside the construct.
   class LoopInConstructRAII {
     SemaOpenACC &SemaRef;
+    LoopCheckingInfo OldLoopInfo;
     CollapseCheckingInfo OldCollapseInfo;
+    TileCheckingInfo OldTileInfo;
     bool PreserveDepth;
 
   public:
     LoopInConstructRAII(SemaOpenACC &SemaRef, bool PreserveDepth = true)
-        : SemaRef(SemaRef), OldCollapseInfo(SemaRef.CollapseInfo),
+        : SemaRef(SemaRef), OldLoopInfo(SemaRef.LoopInfo),
+          OldCollapseInfo(SemaRef.CollapseInfo), OldTileInfo(SemaRef.TileInfo),
           PreserveDepth(PreserveDepth) {}
     ~LoopInConstructRAII() {
       // The associated-statement level of this should NOT preserve this, as it
@@ -551,12 +573,20 @@ class SemaOpenACC : public SemaBase {
       bool CollapseDepthSatisified =
           PreserveDepth ? SemaRef.CollapseInfo.CollapseDepthSatisfied
                         : OldCollapseInfo.CollapseDepthSatisfied;
+      bool TileDepthSatisfied = PreserveDepth
+                                    ? SemaRef.TileInfo.TileDepthSatisfied
+                                    : OldTileInfo.TileDepthSatisfied;
       bool CurLevelHasLoopAlready =
-          PreserveDepth ? SemaRef.CollapseInfo.CurLevelHasLoopAlready
-                        : OldCollapseInfo.CurLevelHasLoopAlready;
+          PreserveDepth ? SemaRef.LoopInfo.CurLevelHasLoopAlready
+                        : OldLoopInfo.CurLevelHasLoopAlready;
+
+      SemaRef.LoopInfo = OldLoopInfo;
       SemaRef.CollapseInfo = OldCollapseInfo;
+      SemaRef.TileInfo = OldTileInfo;
+
       SemaRef.CollapseInfo.CollapseDepthSatisfied = CollapseDepthSatisified;
-      SemaRef.CollapseInfo.CurLevelHasLoopAlready = CurLevelHasLoopAlready;
+      SemaRef.TileInfo.TileDepthSatisfied = TileDepthSatisfied;
+      SemaRef.LoopInfo.CurLevelHasLoopAlready = CurLevelHasLoopAlready;
     }
   };
 
@@ -577,6 +607,9 @@ class SemaOpenACC : public SemaBase {
     void SetCollapseInfoBeforeAssociatedStmt(
         ArrayRef<const OpenACCClause *> UnInstClauses,
         ArrayRef<OpenACCClause *> Clauses);
+    void SetTileInfoBeforeAssociatedStmt(
+        ArrayRef<const OpenACCClause *> UnInstClauses,
+        ArrayRef<OpenACCClause *> Clauses);
     ~AssociatedStmtRAII();
   };
 };
diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp
index f3840c6ffba82b..66f8029a2754b9 100644
--- a/clang/lib/Sema/SemaOpenACC.cpp
+++ b/clang/lib/Sema/SemaOpenACC.cpp
@@ -1128,6 +1128,7 @@ SemaOpenACC::AssociatedStmtRAII::AssociatedStmtRAII(
     SemaRef.ParentlessLoopConstructs.swap(ParentlessLoopConstructs);
   } else if (DirKind == OpenACCDirectiveKind::Loop) {
     SetCollapseInfoBeforeAssociatedStmt(UnInstClauses, Clauses);
+    SetTileInfoBeforeAssociatedStmt(UnInstClauses, Clauses);
   }
 }
 
@@ -1136,8 +1137,9 @@ void SemaOpenACC::AssociatedStmtRAII::SetCollapseInfoBeforeAssociatedStmt(
     ArrayRef<OpenACCClause *> Clauses) {
 
   // Reset this checking for loops that aren't covered in a RAII object.
-  SemaRef.CollapseInfo.CurLevelHasLoopAlready = false;
+  SemaRef.LoopInfo.CurLevelHasLoopAlready = false;
   SemaRef.CollapseInfo.CollapseDepthSatisfied = true;
+  SemaRef.TileInfo.TileDepthSatisfied = true;
 
   // We make sure to take an optional list of uninstantiated clauses, so that
   // we can check to make sure we don't 'double diagnose' in the event that
@@ -1176,6 +1178,26 @@ void SemaOpenACC::AssociatedStmtRAII::SetCollapseInfoBeforeAssociatedStmt(
       cast<ConstantExpr>(LoopCount)->getResultAsAPSInt();
 }
 
+void SemaOpenACC::AssociatedStmtRAII::SetTileInfoBeforeAssociatedStmt(
+    ArrayRef<const OpenACCClause *> UnInstClauses,
+    ArrayRef<OpenACCClause *> Clauses) {
+  // We don't diagnose if this is during instantiation, since the only thing we
+  // care about is the number of arguments, which we can figure out without
+  // instantiation, so we don't want to double-diagnose.
+  if (UnInstClauses.size() > 0)
+    return;
+  auto *TileClauseItr =
+      llvm::find_if(Clauses, llvm::IsaPred<OpenACCTileClause>);
+
+  if (Clauses.end() == TileClauseItr)
+    return;
+
+  OpenACCTileClause *TileClause = cast<OpenACCTileClause>(*TileClauseItr);
+  SemaRef.TileInfo.ActiveTile = TileClause;
+  SemaRef.TileInfo.TileDepthSatisfied = false;
+  SemaRef.TileInfo.CurTileCount = TileClause->getSizeExprs().size();
+}
+
 SemaOpenACC::AssociatedStmtRAII::~AssociatedStmtRAII() {
   SemaRef.InsideComputeConstruct = WasInsideComputeConstruct;
   if (DirKind == OpenACCDirectiveKind::Parallel ||
@@ -1771,38 +1793,66 @@ void SemaOpenACC::ActOnWhileStmt(SourceLocation WhileLoc) {
   if (!getLangOpts().OpenACC)
     return;
 
-  if (!CollapseInfo.TopLevelLoopSeen)
+  if (!LoopInfo.TopLevelLoopSeen)
     return;
 
   if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) {
-    Diag(WhileLoc, diag::err_acc_invalid_in_collapse_loop) << /*while loop*/ 1;
+    Diag(WhileLoc, diag::err_acc_invalid_in_loop)
+        << /*while loop*/ 1 << OpenACCClauseKind::Collapse;
     assert(CollapseInfo.ActiveCollapse && "Collapse count without object?");
     Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
-         diag::note_acc_collapse_clause_here);
+         diag::note_acc_active_clause_here)
+        << OpenACCClauseKind::Collapse;
 
     // Remove the value so that we don't get cascading errors in the body. The
     // caller RAII object will restore this.
     CollapseInfo.CurCollapseCount = std::nullopt;
   }
+
+  if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) {
+    Diag(WhileLoc, diag::err_acc_invalid_in_loop)
+        << /*while loop*/ 1 << OpenACCClauseKind::Tile;
+    assert(TileInfo.ActiveTile && "tile count without object?");
+    Diag(TileInfo.ActiveTile->getBeginLoc(), diag::note_acc_active_clause_here)
+        << OpenACCClauseKind::Tile;
+
+    // Remove the value so that we don't get cascading errors in the body. The
+    // caller RAII object will restore this.
+    TileInfo.CurTileCount = std::nullopt;
+  }
 }
 
 void SemaOpenACC::ActOnDoStmt(SourceLocation DoLoc) {
   if (!getLangOpts().OpenACC)
     return;
 
-  if (!CollapseInfo.TopLevelLoopSeen)
+  if (!LoopInfo.TopLevelLoopSeen)
     return;
 
   if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) {
-    Diag(DoLoc, diag::err_acc_invalid_in_collapse_loop) << /*do loop*/ 2;
+    Diag(DoLoc, diag::err_acc_invalid_in_loop)
+        << /*do loop*/ 2 << OpenACCClauseKind::Collapse;
     assert(CollapseInfo.ActiveCollapse && "Collapse count without object?");
     Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
-         diag::note_acc_collapse_clause_here);
+         diag::note_acc_active_clause_here)
+        << OpenACCClauseKind::Collapse;
 
     // Remove the value so that we don't get cascading errors in the body. The
     // caller RAII object will restore this.
     CollapseInfo.CurCollapseCount = std::nullopt;
   }
+
+  if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) {
+    Diag(DoLoc, diag::err_acc_invalid_in_loop)
+        << /*do loop*/ 2 << OpenACCClauseKind::Tile;
+    assert(TileInfo.ActiveTile && "tile count without object?");
+    Diag(TileInfo.ActiveTile->getBeginLoc(), diag::note_acc_active_clause_here)
+        << OpenACCClauseKind::Tile;
+
+    // Remove the value so that we don't get cascading errors in the body. The
+    // caller RAII object will restore this.
+    TileInfo.CurTileCount = std::nullopt;
+  }
 }
 
 void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) {
@@ -1810,7 +1860,7 @@ void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) {
     return;
 
   // Enable the while/do-while checking.
-  CollapseInfo.TopLevelLoopSeen = true;
+  LoopInfo.TopLevelLoopSeen = true;
 
   if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) {
 
@@ -1819,11 +1869,12 @@ void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) {
     // or loop nest.
     // This checks for more than 1 loop at the current level, the
     // 'depth'-satisifed checking manages the 'not zero' case.
-    if (CollapseInfo.CurLevelHasLoopAlready) {
-      Diag(ForLoc, diag::err_acc_collapse_multiple_loops);
+    if (LoopInfo.CurLevelHasLoopAlready) {
+      Diag(ForLoc, diag::err_acc_clause_multiple_loops) << /*Collapse*/ 0;
       assert(CollapseInfo.ActiveCollapse && "No collapse object?");
       Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
-           diag::note_acc_collapse_clause_here);
+           diag::note_acc_active_clause_here)
+          << OpenACCClauseKind::Collapse;
     } else {
       --(*CollapseInfo.CurCollapseCount);
 
@@ -1834,13 +1885,29 @@ void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) {
     }
   }
 
+  if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) {
+    if (LoopInfo.CurLevelHasLoopAlready) {
+      Diag(ForLoc, diag::err_acc_clause_multiple_loops) << /*Tile*/ 1;
+      assert(TileInfo.ActiveTile && "No tile object?");
+      Diag(TileInfo.ActiveTile->getBeginLoc(),
+           diag::note_acc_active_clause_here)
+          << OpenACCClauseKind::Tile;
+    } else {
+      --(*TileInfo.CurTileCount);
+      // Once we've hit zero here, we know we have deep enough 'for' loops to
+      // get to the bottom.
+      if (*TileInfo.CurTileCount == 0)
+        TileInfo.TileDepthSatisfied = true;
+    }
+  }
+
   // Set this to 'false' for the body of this loop, so that the next level
   // checks independently.
-  CollapseInfo.CurLevelHasLoopAlready = false;
+  LoopInfo.CurLevelHasLoopAlready = false;
 }
 
 namespace {
-SourceLocation FindInterveningCodeInCollapseLoop(const Stmt *CurStmt) {
+SourceLocation FindInterveningCodeInLoop(const Stmt *CurStmt) {
   // We should diagnose on anything except `CompoundStmt`, `NullStmt`,
   // `ForStmt`, `CXXForRangeStmt`, since those are legal, and `WhileStmt` and
   // `DoStmt`, as those are caught as a violation elsewhere.
@@ -1858,8 +1925,7 @@ SourceLocation FindInterveningCodeInCollapseLoop(const Stmt *CurStmt) {
   // of compound statements, as long as there isn't any code inside.
   if (const auto *CS = dyn_cast<CompoundStmt>(CurStmt)) {
     for (const auto *ChildStmt : CS->children()) {
-      SourceLocation ChildStmtLoc =
-          FindInterveningCodeInCollapseLoop(ChildStmt);
+      SourceLocation ChildStmtLoc = FindInterveningCodeInLoop(ChildStmt);
       if (ChildStmtLoc.isValid())
         return ChildStmtLoc;
     }
@@ -1874,24 +1940,33 @@ void SemaOpenACC::ActOnForStmtEnd(SourceLocation ForLoc, StmtResult Body) {
   if (!getLangOpts().OpenACC)
     return;
   // Set this to 'true' so if we find another one at this level we can diagnose.
-  CollapseInfo.CurLevelHasLoopAlready = true;
+  LoopInfo.CurLevelHasLoopAlready = true;
 
   if (!Body.isUsable())
     return;
 
-  if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0 &&
-      !CollapseInfo.ActiveCollapse->hasForce()) {
-    // OpenACC 3.3: 2.9.1
-    // If the 'force' modifier does not appear, then the associated loops must
-    // be tightly nested.  If the force modifier appears, then any intervening
-    // code may be executed multiple times as needed to perform the collapse.
+  bool IsActiveCollapse = CollapseInfo.CurCollapseCount &&
+                          *CollapseInfo.CurCollapseCount > 0 &&
+                          !CollapseInfo.ActiveCollapse->hasForce();
+  bool IsActiveTile = TileInfo.CurTileCount && *TileInfo.CurTileCount > 0;
 
-    SourceLocation OtherStmtLoc = FindInterveningCodeInCollapseLoop(Body.get());
+  if (IsActiveCollapse || IsActiveTile) {
+    SourceLocation OtherStmtLoc = FindInterveningCodeInLoop(Body.get());
 
-    if (OtherStmtLoc.isValid()) {
-      Diag(OtherStmtLoc, diag::err_acc_collapse_intervening_code);
+    if (OtherStmtLoc.isValid() && IsActiveCollapse) {
+      Diag(OtherStmtLoc, diag::err_acc_intervening_code)
+          << OpenACCClauseKind::Collapse;
       Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
-           diag::note_acc_collapse_clause_here);
+           diag::note_acc_active_clause_here)
+          << OpenACCClauseKind::Collapse;
+    }
+
+    if (OtherStmtLoc.isValid() && IsActiveTile) {
+      Diag(OtherStmtLoc, diag::err_acc_intervening_code)
+          << OpenACCClauseKind::Tile;
+      Diag(TileInfo.ActiveTile->getBeginLoc(),
+           diag::note_acc_active_clause_here)
+          << OpenACCClauseKind::Tile;
     }
   }
 }
@@ -1907,11 +1982,19 @@ bool SemaOpenACC::ActOnStartStmtDirective(OpenACCDirectiveKind K,
   //
   // ALL constructs are ill-formed if there is an active 'collapse'
   if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) {
-    Diag(StartLoc, diag::err_acc_invalid_in_collapse_loop)
-        << /*OpenACC Construct*/ 0 << K;
+    Diag(StartLoc, diag::err_acc_invalid_in_loop)
+        << /*OpenACC Construct*/ 0 << OpenACCClauseKind::Collapse << K;
     assert(CollapseInfo.ActiveCollapse && "Collapse count without object?");
     Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
-         diag::note_acc_collapse_clause_here);
+         diag::note_acc_active_clause_here)
+        << OpenACCClauseKind::Collapse;
+  }
+  if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) {
+    Diag(StartLoc, diag::err_acc_invalid_in_loop)
+        << /*OpenACC Construct*/ 0 << OpenACCClauseKind::Tile << K;
+    assert(TileInfo.ActiveTile && "Tile count without object?");
+    Diag(TileInfo.ActiveTile->getBeginLoc(), diag::note_acc_active_clause_here)
+        << OpenACCClauseKind::Tile;
   }
 
   return diagnoseConstructAppertainment(*this, K, StartLoc, /*IsStmt=*/true);
@@ -1986,11 +2069,24 @@ StmtResult SemaOpenACC::ActOnAssociatedStmt(SourceLocation DirectiveLoc,
       return StmtError();
     }
 
-    if (!CollapseInfo.CollapseDepthSatisfied) {
-      Diag(DirectiveLoc, diag::err_acc_collapse_insufficient_loops);
-      assert(CollapseInfo.ActiveCollapse && "Collapse count without object?");
-      Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
-           diag::note_acc_collapse_clause_here);
+    if (!CollapseInfo.CollapseDepthSatisfied || !TileInfo.TileDepthSatisfied) {
+      if (!CollapseInfo.CollapseDepthSatisfied) {
+        Diag(DirectiveLoc, diag::err_acc_insufficient_loops)
+            << OpenACCClauseKind::Collapse;
+        assert(CollapseInfo.ActiveCollapse && "Collapse count without object?");
+        Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
+             diag::note_acc_active_clause_here)
+            << OpenACCClauseKind::Collapse;
+      }
+
+      if (!TileInfo.TileDepthSatisfied) {
+        Diag(DirectiveLoc, diag::err_acc_insufficient_loops)
+            << OpenACCClauseKind::Tile;
+        assert(TileInfo.ActiveTile && "Collapse count without object?");
+        Diag(TileInfo.ActiveTile->getBeginLoc(),
+             diag::note_acc_active_clause_here)
+            << OpenACCClauseKind::Tile;
+      }
       return StmtError();
     }
 
diff --git a/clang/test/ParserOpenACC/parse-clauses.c b/clang/test/ParserOpenACC/parse-clauses.c
index a752b2ebd18b6a..6c382379a8a7ea 100644
--- a/clang/test/ParserOpenACC/parse-clauses.c
...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/111038


More information about the cfe-commits mailing list