https://github.com/loopacino updated 
https://github.com/llvm/llvm-project/pull/206977

>From 0fb5bd42f1e127faea963c359b1e3c69cb969c60 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Wed, 1 Jul 2026 09:06:58 -0400
Subject: [PATCH 01/14] add OMPFlattenDirective ast node

---
 clang/include/clang/AST/RecursiveASTVisitor.h |  3 +
 clang/include/clang/AST/StmtOpenMP.h          | 75 ++++++++++++++++++-
 clang/include/clang/Basic/StmtNodes.td        |  1 +
 clang/lib/AST/StmtOpenMP.cpp                  | 20 +++++
 clang/lib/AST/StmtPrinter.cpp                 |  5 ++
 clang/lib/AST/StmtProfile.cpp                 |  4 +
 clang/lib/Basic/OpenMPKinds.cpp               |  2 +-
 7 files changed, 108 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h 
b/clang/include/clang/AST/RecursiveASTVisitor.h
index b000a34043696..e425f7a31f59c 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -3227,6 +3227,9 @@ DEF_TRAVERSE_STMT(OMPFuseDirective,
 DEF_TRAVERSE_STMT(OMPInterchangeDirective,
                   { TRY_TO(TraverseOMPExecutableDirective(S)); })
 
+DEF_TRAVERSE_STMT(OMPFlattenDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
 DEF_TRAVERSE_STMT(OMPSplitDirective,
                   { TRY_TO(TraverseOMPExecutableDirective(S)); })
 
diff --git a/clang/include/clang/AST/StmtOpenMP.h 
b/clang/include/clang/AST/StmtOpenMP.h
index dbc76e7df8ecd..1420331f37c1f 100644
--- a/clang/include/clang/AST/StmtOpenMP.h
+++ b/clang/include/clang/AST/StmtOpenMP.h
@@ -1041,7 +1041,7 @@ class OMPCanonicalLoopNestTransformationDirective
     Stmt::StmtClass C = T->getStmtClass();
     return C == OMPTileDirectiveClass || C == OMPUnrollDirectiveClass ||
            C == OMPReverseDirectiveClass || C == OMPInterchangeDirectiveClass 
||
-           C == OMPStripeDirectiveClass;
+           C == OMPStripeDirectiveClass || C == OMPFlattenDirectiveClass;
   }
 };
 
@@ -5959,6 +5959,79 @@ class OMPInterchangeDirective final
   }
 };
 
+/// Represents the '#pragma omp flatten' loop transformation directive.
+///
+/// \code{c}
+///   #pragma omp flatten
+///   for (int i = 0; i < m; ++i)
+///     for (int j = 0; j < n; ++j)
+///       ..
+/// \endcode
+class OMPFlattenDirective final
+    : public OMPCanonicalLoopNestTransformationDirective {
+  friend class ASTStmtReader;
+  friend class OMPExecutableDirective;
+
+  /// Offsets of child members.
+  enum {
+    PreInitsOffset = 0,
+    TransformedStmtOffset,
+  };
+
+  explicit OMPFlattenDirective(SourceLocation StartLoc, SourceLocation EndLoc,
+                               unsigned NumLoops)
+      : OMPCanonicalLoopNestTransformationDirective(
+            OMPFlattenDirectiveClass, llvm::omp::OMPD_flatten, StartLoc, 
EndLoc,
+            NumLoops) {}
+
+  void setPreInits(Stmt *PreInits) {
+    Data->getChildren()[PreInitsOffset] = PreInits;
+  }
+
+  void setTransformedStmt(Stmt *S) {
+    Data->getChildren()[TransformedStmtOffset] = S;
+  }
+
+public:
+  /// Create a new AST node representation for '#pragma omp flatten'.
+  ///
+  /// \param C         Context of the AST.
+  /// \param StartLoc  Location of the introducer (e.g. the 'omp' token).
+  /// \param EndLoc    Location of the directive's end (e.g. the tok::eod).
+  /// \param Clauses   The directive's clauses.
+  /// \param NumLoops  Number of affected loops (the flatten depth, currently
+  ///                  always 2).
+  /// \param AssociatedStmt  The outermost associated loop.
+  /// \param TransformedStmt The flattened loop, or nullptr in dependent
+  ///                        contexts.
+  /// \param PreInits  Helper preinits statements for the loop nest.
+  static OMPFlattenDirective *
+  Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+         ArrayRef<OMPClause *> Clauses, unsigned NumLoops, Stmt 
*AssociatedStmt,
+         Stmt *TransformedStmt, Stmt *PreInits);
+
+  /// Build an empty '#pragma omp flatten' AST node for deserialization.
+  ///
+  /// \param C          Context of the AST.
+  /// \param NumClauses Number of clauses to allocate.
+  /// \param NumLoops   Number of associated loops to allocate.
+  static OMPFlattenDirective *
+  CreateEmpty(const ASTContext &C, unsigned NumClauses, unsigned NumLoops);
+
+  /// Gets the flattened loop after the transformation. This is the de-sugared
+  /// replacement or nullptr in dependent contexts.
+  Stmt *getTransformedStmt() const {
+    return Data->getChildren()[TransformedStmtOffset];
+  }
+
+  /// Return preinits statement.
+  Stmt *getPreInits() const { return Data->getChildren()[PreInitsOffset]; }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == OMPFlattenDirectiveClass;
+  }
+};
+
 /// The base class for all transformation directives of canonical loop
 /// sequences (currently only 'fuse')
 class OMPCanonicalLoopSequenceTransformationDirective
diff --git a/clang/include/clang/Basic/StmtNodes.td 
b/clang/include/clang/Basic/StmtNodes.td
index e166894ea024b..0d7a0c1490a1e 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -247,6 +247,7 @@ def OMPReverseDirective : 
StmtNode<OMPCanonicalLoopNestTransformationDirective>;
 def OMPSplitDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>;
 def OMPInterchangeDirective
     : StmtNode<OMPCanonicalLoopNestTransformationDirective>;
+def OMPFlattenDirective : 
StmtNode<OMPCanonicalLoopNestTransformationDirective>;
 def OMPCanonicalLoopSequenceTransformationDirective
     : StmtNode<OMPExecutableDirective, 1>;
 def OMPFuseDirective
diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp
index 9d6b315effb41..477604ef3dafb 100644
--- a/clang/lib/AST/StmtOpenMP.cpp
+++ b/clang/lib/AST/StmtOpenMP.cpp
@@ -552,6 +552,26 @@ OMPInterchangeDirective::CreateEmpty(const ASTContext &C, 
unsigned NumClauses,
       SourceLocation(), SourceLocation(), NumLoops);
 }
 
+OMPFlattenDirective *OMPFlattenDirective::Create(
+    const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+    ArrayRef<OMPClause *> Clauses, unsigned NumLoops, Stmt *AssociatedStmt,
+    Stmt *TransformedStmt, Stmt *PreInits) {
+  OMPFlattenDirective *Dir = createDirective<OMPFlattenDirective>(
+      C, Clauses, AssociatedStmt, TransformedStmtOffset + 1, StartLoc, EndLoc,
+      NumLoops);
+  Dir->setTransformedStmt(TransformedStmt);
+  Dir->setPreInits(PreInits);
+  return Dir;
+}
+
+OMPFlattenDirective *OMPFlattenDirective::CreateEmpty(const ASTContext &C,
+                                                      unsigned NumClauses,
+                                                      unsigned NumLoops) {
+  return createEmptyDirective<OMPFlattenDirective>(
+      C, NumClauses, /*HasAssociatedStmt=*/true, TransformedStmtOffset + 1,
+      SourceLocation(), SourceLocation(), NumLoops);
+}
+
 OMPSplitDirective *
 OMPSplitDirective::Create(const ASTContext &C, SourceLocation StartLoc,
                           SourceLocation EndLoc, ArrayRef<OMPClause *> Clauses,
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 6c3294573e9d4..0bacb79ed86e3 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -800,6 +800,11 @@ void 
StmtPrinter::VisitOMPInterchangeDirective(OMPInterchangeDirective *Node) {
   PrintOMPExecutableDirective(Node);
 }
 
+void StmtPrinter::VisitOMPFlattenDirective(OMPFlattenDirective *Node) {
+  Indent() << "#pragma omp flatten";
+  PrintOMPExecutableDirective(Node);
+}
+
 void StmtPrinter::VisitOMPSplitDirective(OMPSplitDirective *Node) {
   Indent() << "#pragma omp split";
   PrintOMPExecutableDirective(Node);
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 90eab530e0c2e..de7ed322d7424 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -1071,6 +1071,10 @@ void StmtProfiler::VisitOMPInterchangeDirective(
   VisitOMPCanonicalLoopNestTransformationDirective(S);
 }
 
+void StmtProfiler::VisitOMPFlattenDirective(const OMPFlattenDirective *S) {
+  VisitOMPCanonicalLoopNestTransformationDirective(S);
+}
+
 void StmtProfiler::VisitOMPSplitDirective(const OMPSplitDirective *S) {
   VisitOMPCanonicalLoopNestTransformationDirective(S);
 }
diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp
index 675d86349c933..57e0347f8ea77 100644
--- a/clang/lib/Basic/OpenMPKinds.cpp
+++ b/clang/lib/Basic/OpenMPKinds.cpp
@@ -818,7 +818,7 @@ bool 
clang::isOpenMPCanonicalLoopNestTransformationDirective(
     OpenMPDirectiveKind DKind) {
   return DKind == OMPD_tile || DKind == OMPD_unroll || DKind == OMPD_reverse ||
          DKind == OMPD_split || DKind == OMPD_interchange ||
-         DKind == OMPD_stripe;
+         DKind == OMPD_stripe || DKind == OMPD_flatten;
 }
 
 bool clang::isOpenMPCanonicalLoopSequenceTransformationDirective(

>From 933007b6333584fb358adc4f89a61db1ae15d4cb Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Wed, 8 Jul 2026 16:02:27 -0400
Subject: [PATCH 02/14] add OMPDepthClause ast node

---
 clang/include/clang/AST/OpenMPClause.h        | 68 +++++++++++++++++++
 clang/include/clang/AST/RecursiveASTVisitor.h |  6 ++
 clang/lib/AST/OpenMPClause.cpp                | 25 +++++++
 clang/lib/AST/StmtProfile.cpp                 |  5 ++
 clang/lib/Parse/ParseOpenMP.cpp               |  1 +
 llvm/include/llvm/Frontend/OpenMP/OMP.td      |  1 +
 6 files changed, 106 insertions(+)

diff --git a/clang/include/clang/AST/OpenMPClause.h 
b/clang/include/clang/AST/OpenMPClause.h
index 8ceafc4669297..50d1364a9726d 100644
--- a/clang/include/clang/AST/OpenMPClause.h
+++ b/clang/include/clang/AST/OpenMPClause.h
@@ -1380,6 +1380,74 @@ class OMPPartialClause final : public OMPClause {
   }
 };
 
+/// This represents the 'depth' clause on the '#pragma omp flatten' (and
+/// '#pragma omp fuse') loop-transformation directives.
+///
+/// \code
+/// #pragma omp flatten depth(3)
+/// \endcode
+/// In this example the 'flatten' directive has a 'depth' clause whose argument
+/// '3' specifies how many perfectly nested loops are combined into one.
+/// The argument must be a positive integer constant expression that evaluates
+/// to at most the loop nest depth of the associated loop nest.
+class OMPDepthClause final : public OMPClause {
+  friend class OMPClauseReader;
+
+  /// Location of '('.
+  SourceLocation LParenLoc;
+
+  /// The depth expression (number of loops to flatten).
+  Stmt *Depth = nullptr;
+
+  /// Build an empty clause.
+  explicit OMPDepthClause() : OMPClause(llvm::omp::OMPC_depth, {}, {}) {}
+
+  /// Set the depth expression.
+  void setDepth(Expr *E) { Depth = E; }
+
+  /// Sets the location of '('.
+  void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; }
+
+public:
+  /// Build an AST node for a 'depth' clause.
+  ///
+  /// \param C         Context of the AST.
+  /// \param StartLoc  Location of the 'depth' identifier.
+  /// \param LParenLoc Location of '('.
+  /// \param EndLoc    Location of ')'.
+  /// \param Depth     The depth expression.
+  static OMPDepthClause *Create(const ASTContext &C, SourceLocation StartLoc,
+                                SourceLocation LParenLoc, SourceLocation 
EndLoc,
+                                Expr *Depth);
+
+  /// Build an empty 'depth' AST node for deserialization.
+  ///
+  /// \param C     Context of the AST.
+  static OMPDepthClause *CreateEmpty(const ASTContext &C);
+
+  /// Returns the location of '('.
+  SourceLocation getLParenLoc() const { return LParenLoc; }
+
+  /// Returns the depth expression or nullptr if not set.
+  Expr *getDepth() const { return cast_or_null<Expr>(Depth); }
+
+  child_range children() { return child_range(&Depth, &Depth + 1); }
+  const_child_range children() const {
+    return const_child_range(&Depth, &Depth + 1);
+  }
+
+  child_range used_children() {
+    return child_range(child_iterator(), child_iterator());
+  }
+  const_child_range used_children() const {
+    return const_child_range(const_child_iterator(), const_child_iterator());
+  }
+
+  static bool classof(const OMPClause *T) {
+    return T->getClauseKind() == llvm::omp::OMPC_depth;
+  }
+};
+
 /// This represents 'collapse' clause in the '#pragma omp ...'
 /// directive.
 ///
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h 
b/clang/include/clang/AST/RecursiveASTVisitor.h
index e425f7a31f59c..da55e97fdfcb5 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -3568,6 +3568,12 @@ bool 
RecursiveASTVisitor<Derived>::VisitOMPPartialClause(OMPPartialClause *C) {
   return true;
 }
 
+template <typename Derived>
+bool RecursiveASTVisitor<Derived>::VisitOMPDepthClause(OMPDepthClause *C) {
+  TRY_TO(TraverseStmt(C->getDepth()));
+  return true;
+}
+
 template <typename Derived>
 bool
 RecursiveASTVisitor<Derived>::VisitOMPCollapseClause(OMPCollapseClause *C) {
diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp
index ed00e80144c25..7b2f67a31cad9 100644
--- a/clang/lib/AST/OpenMPClause.cpp
+++ b/clang/lib/AST/OpenMPClause.cpp
@@ -1057,6 +1057,22 @@ OMPPartialClause *OMPPartialClause::CreateEmpty(const 
ASTContext &C) {
   return new (C) OMPPartialClause();
 }
 
+OMPDepthClause *OMPDepthClause::Create(const ASTContext &C,
+                                       SourceLocation StartLoc,
+                                       SourceLocation LParenLoc,
+                                       SourceLocation EndLoc, Expr *Depth) {
+  OMPDepthClause *Clause = CreateEmpty(C);
+  Clause->setLocStart(StartLoc);
+  Clause->setLParenLoc(LParenLoc);
+  Clause->setLocEnd(EndLoc);
+  Clause->setDepth(Depth);
+  return Clause;
+}
+
+OMPDepthClause *OMPDepthClause::CreateEmpty(const ASTContext &C) {
+  return new (C) OMPDepthClause();
+}
+
 OMPLoopRangeClause *
 OMPLoopRangeClause::Create(const ASTContext &C, SourceLocation StartLoc,
                            SourceLocation LParenLoc, SourceLocation FirstLoc,
@@ -2069,6 +2085,15 @@ void 
OMPClausePrinter::VisitOMPPermutationClause(OMPPermutationClause *Node) {
 
 void OMPClausePrinter::VisitOMPFullClause(OMPFullClause *Node) { OS << "full"; 
}
 
+void OMPClausePrinter::VisitOMPDepthClause(OMPDepthClause *Node) {
+  OS << "depth";
+  if (Expr *Depth = Node->getDepth()) {
+    OS << '(';
+    Depth->printPretty(OS, nullptr, Policy, 0);
+    OS << ')';
+  }
+}
+
 void OMPClausePrinter::VisitOMPPartialClause(OMPPartialClause *Node) {
   OS << "partial";
 
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index de7ed322d7424..838d6d02f7446 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -521,6 +521,11 @@ void OMPClauseProfiler::VisitOMPPartialClause(const 
OMPPartialClause *C) {
     Profiler->VisitExpr(Factor);
 }
 
+void OMPClauseProfiler::VisitOMPDepthClause(const OMPDepthClause *C) {
+  if (const Expr *Depth = C->getDepth())
+    Profiler->VisitExpr(Depth);
+}
+
 void OMPClauseProfiler::VisitOMPLoopRangeClause(const OMPLoopRangeClause *C) {
   if (const Expr *First = C->getFirst())
     Profiler->VisitExpr(First);
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index af52295df2d8b..952a517205381 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -3247,6 +3247,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind 
DKind,
   case OMPC_nocontext:
   case OMPC_filter:
   case OMPC_partial:
+  case OMPC_depth:
   case OMPC_align:
   case OMPC_message:
   case OMPC_ompx_dyn_cgroup_mem:
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td 
b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 679a944fc4358..d49c5ee1a0db3 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -161,6 +161,7 @@ def OMPC_Depobj : Clause<[Spelling<"depobj">]> {
   let isImplicit = true;
 }
 def OMPC_Depth : Clause<[Spelling<"depth">]> {
+  let clangClass = "OMPDepthClause";
   let flangClass = "ScalarIntConstantExpr";
 }
 def OMPC_Destroy : Clause<[Spelling<"destroy">]> {

>From b63024e1e342cd168ef128a3f6a24909df2a82db Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Wed, 8 Jul 2026 16:04:44 -0400
Subject: [PATCH 03/14] template-transform support for flatten directive &
 depth clause

---
 clang/lib/Sema/TreeTransform.h | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 3b99ff4bb9e23..865622ad50f1c 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -1804,6 +1804,14 @@ class TreeTransform {
                                                        LParenLoc, EndLoc);
   }
 
+  /// Build a new OpenMP 'depth' clause.
+  OMPClause *RebuildOMPDepthClause(Expr *Depth, SourceLocation StartLoc,
+                                   SourceLocation LParenLoc,
+                                   SourceLocation EndLoc) {
+    return getSema().OpenMP().ActOnOpenMPDepthClause(Depth, StartLoc, 
LParenLoc,
+                                                     EndLoc);
+  }
+
   OMPClause *
   RebuildOMPLoopRangeClause(Expr *First, Expr *Count, SourceLocation StartLoc,
                             SourceLocation LParenLoc, SourceLocation FirstLoc,
@@ -9791,6 +9799,17 @@ 
TreeTransform<Derived>::TransformOMPSplitDirective(OMPSplitDirective *D) {
   return Res;
 }
 
+template <typename Derived>
+StmtResult
+TreeTransform<Derived>::TransformOMPFlattenDirective(OMPFlattenDirective *D) {
+  DeclarationNameInfo DirName;
+  getDerived().getSema().OpenMP().StartOpenMPDSABlock(
+      D->getDirectiveKind(), DirName, nullptr, D->getBeginLoc());
+  StmtResult Res = getDerived().TransformOMPExecutableDirective(D);
+  getDerived().getSema().OpenMP().EndOpenMPDSABlock(Res.get());
+  return Res;
+}
+
 template <typename Derived>
 StmtResult
 TreeTransform<Derived>::TransformOMPFuseDirective(OMPFuseDirective *D) {
@@ -10717,6 +10736,20 @@ 
TreeTransform<Derived>::TransformOMPPartialClause(OMPPartialClause *C) {
                                  C->getEndLoc());
 }
 
+template <typename Derived>
+OMPClause *TreeTransform<Derived>::TransformOMPDepthClause(OMPDepthClause *C) {
+  ExprResult T = getDerived().TransformExpr(C->getDepth());
+  if (T.isInvalid())
+    return nullptr;
+  Expr *Depth = T.get();
+  bool Changed = Depth != C->getDepth();
+
+  if (!Changed && !getDerived().AlwaysRebuild())
+    return C;
+  return RebuildOMPDepthClause(Depth, C->getBeginLoc(), C->getLParenLoc(),
+                               C->getEndLoc());
+}
+
 template <typename Derived>
 OMPClause *
 TreeTransform<Derived>::TransformOMPLoopRangeClause(OMPLoopRangeClause *C) {

>From deafb222a7d251069c68a44fa267e58e750f1064 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Wed, 8 Jul 2026 16:08:12 -0400
Subject: [PATCH 04/14] serialize flatten

---
 clang/include/clang/Serialization/ASTBitCodes.h |  1 +
 clang/lib/Serialization/ASTReaderStmt.cpp       | 11 +++++++++++
 clang/lib/Serialization/ASTWriterStmt.cpp       |  5 +++++
 3 files changed, 17 insertions(+)

diff --git a/clang/include/clang/Serialization/ASTBitCodes.h 
b/clang/include/clang/Serialization/ASTBitCodes.h
index 279380de2f7fe..93a0e0a454b68 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1970,6 +1970,7 @@ enum StmtCode {
   STMT_OMP_REVERSE_DIRECTIVE,
   STMT_OMP_SPLIT_DIRECTIVE,
   STMT_OMP_INTERCHANGE_DIRECTIVE,
+  STMT_OMP_FLATTEN_DIRECTIVE,
   STMT_OMP_FUSE_DIRECTIVE,
   STMT_OMP_FOR_DIRECTIVE,
   STMT_OMP_FOR_SIMD_DIRECTIVE,
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp 
b/clang/lib/Serialization/ASTReaderStmt.cpp
index 02ccf8d4d41c2..8c2debd6cdf32 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2537,6 +2537,10 @@ void 
ASTStmtReader::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) {
   VisitOMPCanonicalLoopNestTransformationDirective(D);
 }
 
+void ASTStmtReader::VisitOMPFlattenDirective(OMPFlattenDirective *D) {
+  VisitOMPCanonicalLoopNestTransformationDirective(D);
+}
+
 void ASTStmtReader::VisitOMPSplitDirective(OMPSplitDirective *D) {
   VisitOMPCanonicalLoopNestTransformationDirective(D);
 }
@@ -3719,6 +3723,13 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       break;
     }
 
+    case STMT_OMP_FLATTEN_DIRECTIVE: {
+      unsigned NumLoops = Record[ASTStmtReader::NumStmtFields];
+      unsigned NumClauses = Record[ASTStmtReader::NumStmtFields + 1];
+      S = OMPFlattenDirective::CreateEmpty(Context, NumClauses, NumLoops);
+      break;
+    }
+
     case STMT_OMP_FOR_DIRECTIVE: {
       unsigned CollapsedNum = Record[ASTStmtReader::NumStmtFields];
       unsigned NumClauses = Record[ASTStmtReader::NumStmtFields + 1];
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp 
b/clang/lib/Serialization/ASTWriterStmt.cpp
index 4e9eadd730a56..dfe28a9b915e3 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2547,6 +2547,11 @@ void 
ASTStmtWriter::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) {
   Code = serialization::STMT_OMP_INTERCHANGE_DIRECTIVE;
 }
 
+void ASTStmtWriter::VisitOMPFlattenDirective(OMPFlattenDirective *D) {
+  VisitOMPCanonicalLoopNestTransformationDirective(D);
+  Code = serialization::STMT_OMP_FLATTEN_DIRECTIVE;
+}
+
 void ASTStmtWriter::VisitOMPSplitDirective(OMPSplitDirective *D) {
   VisitOMPCanonicalLoopNestTransformationDirective(D);
   Code = serialization::STMT_OMP_SPLIT_DIRECTIVE;

>From 3e2dfeb99230057b7cb9c484182ae33befbe1e41 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Wed, 8 Jul 2026 16:09:03 -0400
Subject: [PATCH 05/14] serialize depth

---
 clang/lib/Serialization/ASTReader.cpp | 8 ++++++++
 clang/lib/Serialization/ASTWriter.cpp | 5 +++++
 2 files changed, 13 insertions(+)

diff --git a/clang/lib/Serialization/ASTReader.cpp 
b/clang/lib/Serialization/ASTReader.cpp
index f8a6a38bb9b5c..4f51d79a52105 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -11495,6 +11495,9 @@ OMPClause *OMPClauseReader::readClause() {
   case llvm::omp::OMPC_partial:
     C = OMPPartialClause::CreateEmpty(Context);
     break;
+  case llvm::omp::OMPC_depth:
+    C = OMPDepthClause::CreateEmpty(Context);
+    break;
   case llvm::omp::OMPC_looprange:
     C = OMPLoopRangeClause::CreateEmpty(Context);
     break;
@@ -11924,6 +11927,11 @@ void 
OMPClauseReader::VisitOMPPartialClause(OMPPartialClause *C) {
   C->setLParenLoc(Record.readSourceLocation());
 }
 
+void OMPClauseReader::VisitOMPDepthClause(OMPDepthClause *C) {
+  C->setDepth(Record.readSubExpr());
+  C->setLParenLoc(Record.readSourceLocation());
+}
+
 void OMPClauseReader::VisitOMPLoopRangeClause(OMPLoopRangeClause *C) {
   C->setFirst(Record.readSubExpr());
   C->setCount(Record.readSubExpr());
diff --git a/clang/lib/Serialization/ASTWriter.cpp 
b/clang/lib/Serialization/ASTWriter.cpp
index 357a7f7e95fa0..cbfeaa9e6638a 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -8106,6 +8106,11 @@ void 
OMPClauseWriter::VisitOMPPartialClause(OMPPartialClause *C) {
   Record.AddSourceLocation(C->getLParenLoc());
 }
 
+void OMPClauseWriter::VisitOMPDepthClause(OMPDepthClause *C) {
+  Record.AddStmt(C->getDepth());
+  Record.AddSourceLocation(C->getLParenLoc());
+}
+
 void OMPClauseWriter::VisitOMPLoopRangeClause(OMPLoopRangeClause *C) {
   Record.AddStmt(C->getFirst());
   Record.AddStmt(C->getCount());

>From 4ae746ca1eeaf11db10e859572a4500029d01cce Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Wed, 8 Jul 2026 16:17:29 -0400
Subject: [PATCH 06/14] add analysis traversal support for flatten directive

---
 clang/lib/Sema/SemaExceptionSpec.cpp         | 1 +
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 +
 2 files changed, 2 insertions(+)

diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp 
b/clang/lib/Sema/SemaExceptionSpec.cpp
index 40d530a1f3925..995b5b6010978 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1508,6 +1508,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Stmt::OMPUnrollDirectiveClass:
   case Stmt::OMPReverseDirectiveClass:
   case Stmt::OMPInterchangeDirectiveClass:
+  case Stmt::OMPFlattenDirectiveClass:
   case Stmt::OMPSplitDirectiveClass:
   case Stmt::OMPFuseDirectiveClass:
   case Stmt::OMPSingleDirectiveClass:
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index cfb294736ee02..3f666be00cc24 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1783,6 +1783,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::OMPStripeDirectiveClass:
     case Stmt::OMPTileDirectiveClass:
     case Stmt::OMPInterchangeDirectiveClass:
+    case Stmt::OMPFlattenDirectiveClass:
     case Stmt::OMPSplitDirectiveClass:
     case Stmt::OMPFuseDirectiveClass:
     case Stmt::OMPInteropDirectiveClass:

>From 415eba88ad0af63c552b4c5c2eabd660bcb29c02 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Wed, 8 Jul 2026 16:21:25 -0400
Subject: [PATCH 07/14] codegen flatten directive

---
 clang/lib/CodeGen/CGStmt.cpp        | 3 +++
 clang/lib/CodeGen/CGStmtOpenMP.cpp  | 8 ++++++++
 clang/lib/CodeGen/CodeGenFunction.h | 1 +
 3 files changed, 12 insertions(+)

diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 232094777f233..27f48cb63b553 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -236,6 +236,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S, 
ArrayRef<const Attr *> Attrs) {
   case Stmt::OMPInterchangeDirectiveClass:
     EmitOMPInterchangeDirective(cast<OMPInterchangeDirective>(*S));
     break;
+  case Stmt::OMPFlattenDirectiveClass:
+    EmitOMPFlattenDirective(cast<OMPFlattenDirective>(*S));
+    break;
   case Stmt::OMPFuseDirectiveClass:
     EmitOMPFuseDirective(cast<OMPFuseDirective>(*S));
     break;
diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp 
b/clang/lib/CodeGen/CGStmtOpenMP.cpp
index 95fd6694437fe..25da3430a23fe 100644
--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -218,6 +218,8 @@ class OMPLoopScope : public 
CodeGenFunction::RunCleanupsScope {
     } else if (const auto *Interchange =
                    dyn_cast<OMPInterchangeDirective>(&S)) {
       PreInits = Interchange->getPreInits();
+    } else if (const auto *Flatten = dyn_cast<OMPFlattenDirective>(&S)) {
+      PreInits = Flatten->getPreInits();
     } else {
       llvm_unreachable("Unknown loop-based directive kind.");
     }
@@ -3248,6 +3250,12 @@ void CodeGenFunction::EmitOMPInterchangeDirective(
   EmitStmt(S.getTransformedStmt());
 }
 
+void CodeGenFunction::EmitOMPFlattenDirective(const OMPFlattenDirective &S) {
+  // Emit the de-sugared statement.
+  OMPTransformDirectiveScopeRAII FlattenScope(*this, &S);
+  EmitStmt(S.getTransformedStmt());
+}
+
 void CodeGenFunction::EmitOMPFuseDirective(const OMPFuseDirective &S) {
   // Emit the de-sugared statement
   OMPTransformDirectiveScopeRAII FuseScope(*this, &S);
diff --git a/clang/lib/CodeGen/CodeGenFunction.h 
b/clang/lib/CodeGen/CodeGenFunction.h
index 6d0718c243812..eea0163422cd3 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3934,6 +3934,7 @@ class CodeGenFunction : public CodeGenTypeCache {
   void EmitOMPReverseDirective(const OMPReverseDirective &S);
   void EmitOMPSplitDirective(const OMPSplitDirective &S);
   void EmitOMPInterchangeDirective(const OMPInterchangeDirective &S);
+  void EmitOMPFlattenDirective(const OMPFlattenDirective &S);
   void EmitOMPFuseDirective(const OMPFuseDirective &S);
   void EmitOMPForDirective(const OMPForDirective &S);
   void EmitOMPForSimdDirective(const OMPForSimdDirective &S);

>From 814e2e9fe9bf4e28e4557999fbc16beec144cc39 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Wed, 8 Jul 2026 16:24:42 -0400
Subject: [PATCH 08/14] add NYI CIR stub

---
 clang/lib/CIR/CodeGen/CIRGenFunction.h     | 1 +
 clang/lib/CIR/CodeGen/CIRGenStmt.cpp       | 2 ++
 clang/lib/CIR/CodeGen/CIRGenStmtOpenMP.cpp | 5 +++++
 3 files changed, 8 insertions(+)

diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 322355fde3957..8374583ffd4d0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -2559,6 +2559,7 @@ class CIRGenFunction : public CIRGenTypeCache {
   mlir::LogicalResult emitOMPSplitDirective(const OMPSplitDirective &s);
   mlir::LogicalResult
   emitOMPInterchangeDirective(const OMPInterchangeDirective &s);
+  mlir::LogicalResult emitOMPFlattenDirective(const OMPFlattenDirective &s);
   mlir::LogicalResult emitOMPAssumeDirective(const OMPAssumeDirective &s);
   mlir::LogicalResult emitOMPMaskedDirective(const OMPMaskedDirective &s);
   mlir::LogicalResult emitOMPStripeDirective(const OMPStripeDirective &s);
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp 
b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index d3acac5801e74..33d425b22d61a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -408,6 +408,8 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
     return emitOMPSplitDirective(cast<OMPSplitDirective>(*s));
   case Stmt::OMPInterchangeDirectiveClass:
     return emitOMPInterchangeDirective(cast<OMPInterchangeDirective>(*s));
+  case Stmt::OMPFlattenDirectiveClass:
+    return emitOMPFlattenDirective(cast<OMPFlattenDirective>(*s));
   case Stmt::OMPAssumeDirectiveClass:
     return emitOMPAssumeDirective(cast<OMPAssumeDirective>(*s));
   case Stmt::OMPMaskedDirectiveClass:
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmtOpenMP.cpp 
b/clang/lib/CIR/CodeGen/CIRGenStmtOpenMP.cpp
index 17a1fb8090f5c..4fffd17b25a16 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmtOpenMP.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmtOpenMP.cpp
@@ -587,6 +587,11 @@ CIRGenFunction::emitOMPInterchangeDirective(const 
OMPInterchangeDirective &s) {
   return mlir::failure();
 }
 mlir::LogicalResult
+CIRGenFunction::emitOMPFlattenDirective(const OMPFlattenDirective &s) {
+  getCIRGenModule().errorNYI(s.getSourceRange(), "OpenMP OMPFlattenDirective");
+  return mlir::failure();
+}
+mlir::LogicalResult
 CIRGenFunction::emitOMPAssumeDirective(const OMPAssumeDirective &s) {
   getCIRGenModule().errorNYI(s.getSourceRange(), "OpenMP OMPAssumeDirective");
   return mlir::failure();

>From 0027ec6ab982a83766bd65f3e1bc45d9a671fc62 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Wed, 8 Jul 2026 16:26:31 -0400
Subject: [PATCH 09/14] add libclang traversal support for depth clause

---
 clang/tools/libclang/CIndex.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index ac2fad38a1348..f8ab63e3e0dae 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -2378,6 +2378,10 @@ void OMPClauseEnqueue::VisitOMPLoopRangeClause(const 
OMPLoopRangeClause *C) {
   Visitor->AddStmt(C->getCount());
 }
 
+void OMPClauseEnqueue::VisitOMPDepthClause(const OMPDepthClause *C) {
+  Visitor->AddStmt(C->getDepth());
+}
+
 void OMPClauseEnqueue::VisitOMPAllocatorClause(const OMPAllocatorClause *C) {
   Visitor->AddStmt(C->getAllocator());
 }

>From f5ac5f740c414310c8482f652f1bed936ae7cda3 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Fri, 10 Jul 2026 06:54:16 -0400
Subject: [PATCH 10/14] sema

---
 clang/include/clang/Sema/SemaOpenMP.h |  10 +
 clang/lib/Sema/SemaOpenMP.cpp         | 295 ++++++++++++++++++++++++++
 2 files changed, 305 insertions(+)

diff --git a/clang/include/clang/Sema/SemaOpenMP.h 
b/clang/include/clang/Sema/SemaOpenMP.h
index 3621ce96b8724..7b086388416a0 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -470,6 +470,12 @@ class SemaOpenMP : public SemaBase {
                                              SourceLocation StartLoc,
                                              SourceLocation EndLoc);
 
+  /// Called on well-formed '#pragma omp flatten' after parsing of its
+  /// clauses and the associated statement.
+  StmtResult ActOnOpenMPFlattenDirective(ArrayRef<OMPClause *> Clauses,
+                                         Stmt *AStmt, SourceLocation StartLoc,
+                                         SourceLocation EndLoc);
+
   /// Called on well-formed '#pragma omp fuse' after parsing of its
   /// clauses and the associated statement.
   StmtResult ActOnOpenMPFuseDirective(ArrayRef<OMPClause *> Clauses,
@@ -935,6 +941,10 @@ class SemaOpenMP : public SemaBase {
   OMPClause *ActOnOpenMPPartialClause(Expr *FactorExpr, SourceLocation 
StartLoc,
                                       SourceLocation LParenLoc,
                                       SourceLocation EndLoc);
+  /// Called on well-formed 'depth' clause.
+  OMPClause *ActOnOpenMPDepthClause(Expr *DepthExpr, SourceLocation StartLoc,
+                                    SourceLocation LParenLoc,
+                                    SourceLocation EndLoc);
   /// Called on well-formed 'collapse' clause.
   OMPClause *ActOnOpenMPCollapseClause(Expr *NumForLoops,
                                        SourceLocation StartLoc,
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 247a4a6ad9271..fcc256dec3c88 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -4629,6 +4629,7 @@ void 
SemaOpenMP::ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind,
   case OMPD_reverse:
   case OMPD_split:
   case OMPD_interchange:
+  case OMPD_flatten:
   case OMPD_fuse:
   case OMPD_assume:
     break;
@@ -6475,6 +6476,10 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
     Res = ActOnOpenMPInterchangeDirective(ClausesWithImplicit, AStmt, StartLoc,
                                           EndLoc);
     break;
+  case OMPD_flatten:
+    Res = ActOnOpenMPFlattenDirective(ClausesWithImplicit, AStmt, StartLoc,
+                                      EndLoc);
+    break;
   case OMPD_fuse:
     Res =
         ActOnOpenMPFuseDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc);
@@ -16390,6 +16395,277 @@ StmtResult 
SemaOpenMP::ActOnOpenMPInterchangeDirective(
                                          buildPreInits(Context, PreInits));
 }
 
+/// Counts the perfectly nested canonical loop depth at \p AStmt. Returns
+/// std::nullopt if a loop-generating transformation prevents a static count.
+static std::optional<unsigned> getCanonicalLoopNestDepth(Stmt *AStmt) {
+  unsigned Depth = 0;
+  Stmt *CurStmt = AStmt ? AStmt->IgnoreContainers() : nullptr;
+  while (CurStmt) {
+    if (isa<OMPLoopTransformationDirective>(CurStmt))
+      return std::nullopt;
+    if (auto *CanonLoop = dyn_cast<OMPCanonicalLoop>(CurStmt))
+      CurStmt = CanonLoop->getLoopStmt();
+    Stmt *Body = nullptr;
+    if (auto *For = dyn_cast<ForStmt>(CurStmt))
+      Body = For->getBody();
+    else if (auto *RangeFor = dyn_cast<CXXForRangeStmt>(CurStmt))
+      Body = RangeFor->getBody();
+    else
+      break;
+    ++Depth;
+    CurStmt = Body ? Body->IgnoreContainers() : nullptr;
+  }
+  return Depth;
+}
+
+StmtResult
+SemaOpenMP::ActOnOpenMPFlattenDirective(ArrayRef<OMPClause *> Clauses,
+                                        Stmt *AStmt, SourceLocation StartLoc,
+                                        SourceLocation EndLoc) {
+  ASTContext &Context = getASTContext();
+  DeclContext *CurContext = SemaRef.CurContext;
+  Scope *CurScope = SemaRef.getCurScope();
+
+  // Empty statement should only be possible if there already was an error.
+  if (!AStmt)
+    return StmtError();
+
+  // flatten without 'depth' clause combines two loops; 'depth(k)' selects k.
+  unsigned NumLoops = 2;
+  bool DepthIsValueDependent = false;
+  const auto *DepthClause =
+      OMPExecutableDirective::getSingleClause<OMPDepthClause>(Clauses);
+  if (DepthClause) {
+    Expr *DepthExpr = DepthClause->getDepth();
+    if (DepthExpr && DepthExpr->isValueDependent()) {
+      DepthIsValueDependent = true;
+    } else if (DepthExpr) {
+      Expr::EvalResult EvalResult;
+      if (DepthExpr->EvaluateAsInt(EvalResult, Context))
+        NumLoops = EvalResult.Val.getInt().getZExtValue();
+    }
+  }
+
+  // Report "expected k for loops, but found only n" when depth exceeds the
+  // perfect nest (same form as 'collapse'); skip if depth or nest is unknown.
+  if (DepthClause && !DepthIsValueDependent) {
+    if (std::optional<unsigned> NestDepth = getCanonicalLoopNestDepth(AStmt);
+        NestDepth && NumLoops > *NestDepth) {
+      Diag(AStmt->getBeginLoc(), diag::err_omp_not_for)
+          << /*expected N for loops form=*/1
+          << getOpenMPDirectiveName(OMPD_flatten) << NumLoops
+          << (*NestDepth > 0) << *NestDepth;
+      return StmtError();
+    }
+  }
+
+  // Defer when 'depth' is value-dependent (concrete k unknown until
+  // instantiation).
+  if (DepthIsValueDependent)
+    return OMPFlattenDirective::Create(Context, StartLoc, EndLoc, Clauses,
+                                       NumLoops, AStmt, nullptr, nullptr);
+
+  // Verify and diagnose loop nest.
+  SmallVector<OMPLoopBasedDirective::HelperExprs, 4> LoopHelpers(NumLoops);
+  Stmt *Body = nullptr;
+  SmallVector<SmallVector<Stmt *>, 4> OriginalInits;
+  if (!checkTransformableLoopNest(OMPD_flatten, AStmt, NumLoops, LoopHelpers,
+                                  Body, OriginalInits))
+    return StmtError();
+
+  // Delay flattening to when template is completely instantiated.
+  if (CurContext->isDependentContext())
+    return OMPFlattenDirective::Create(Context, StartLoc, EndLoc, Clauses,
+                                       NumLoops, AStmt, nullptr, nullptr);
+
+  assert(LoopHelpers.size() == NumLoops &&
+         "Expecting loop iteration space dimensionality to match number of "
+         "affected loops");
+  assert(OriginalInits.size() == NumLoops &&
+         "Expecting loop iteration space dimensionality to match number of "
+         "affected loops");
+
+  // Find the affected loops.
+  SmallVector<Stmt *> LoopStmts(NumLoops, nullptr);
+  collectLoopStmts(AStmt, LoopStmts);
+
+  // Collect pre-init statements in outer-to-inner order.
+  SmallVector<Stmt *> PreInits;
+  for (auto I : llvm::seq<int>(NumLoops)) {
+    OMPLoopBasedDirective::HelperExprs &LoopHelper = LoopHelpers[I];
+    assert(LoopHelper.Counters.size() == 1 &&
+           "Single-dimensional loop iteration space expected");
+    addLoopPreInits(Context, LoopHelper, LoopStmts[I], OriginalInits[I],
+                    PreInits);
+  }
+
+  CaptureVars CopyTransformer(SemaRef);
+  auto MakeNumIterations = [&CopyTransformer,
+                            &LoopHelpers](unsigned I) -> Expr * {
+    return AssertSuccess(
+        CopyTransformer.TransformExpr(LoopHelpers[I].NumIterations));
+  };
+
+  OMPLoopBasedDirective::HelperExprs &OutermostHelper = LoopHelpers[0];
+  auto *OutermostCntVar = cast<DeclRefExpr>(OutermostHelper.Counters.front());
+  SourceLocation OrigVarLoc = OutermostCntVar->getExprLoc();
+  SourceLocation OrigVarLocBegin = OutermostCntVar->getBeginLoc();
+  SourceLocation OrigVarLocEnd = OutermostCntVar->getEndLoc();
+  SourceLocation CondLoc = OutermostHelper.Cond->getExprLoc();
+
+  // Product of trip counts; mirror 'collapse' IV-width selection to avoid
+  // overflow when several counts are multiplied.
+  auto BuildTripCount = [&](unsigned Bits) -> ExprResult {
+    ExprResult Product;
+    for (unsigned I = 0; I < NumLoops; ++I) {
+      ExprResult N = widenIterationCount(Bits, MakeNumIterations(I), SemaRef);
+      if (!N.isUsable())
+        return ExprError();
+      if (I == 0)
+        Product = N;
+      else
+        Product = SemaRef.BuildBinOp(CurScope, CondLoc, BO_Mul, Product.get(),
+                                     N.get());
+      if (!Product.isUsable())
+        return ExprError();
+    }
+    return Product;
+  };
+
+  bool AllCountsLessThan32Bits = true;
+  for (unsigned I = 0; I < NumLoops; ++I)
+    AllCountsLessThan32Bits &=
+        Context.getTypeSize(LoopHelpers[I].NumIterations->getType()) < 32;
+
+  ExprResult TripCount32 = BuildTripCount(/*Bits=*/32);
+  ExprResult TripCount64 = BuildTripCount(/*Bits=*/64);
+  if (!TripCount32.isUsable() || !TripCount64.isUsable())
+    return StmtError();
+
+  ExprResult TripCount = TripCount64;
+  if (Context.getTypeSize(TripCount32.get()->getType()) == 32 &&
+      (AllCountsLessThan32Bits || NumLoops == 1 ||
+       fitsInto(
+           /*Bits=*/32,
+           TripCount32.get()->getType()->hasSignedIntegerRepresentation(),
+           TripCount64.get(), SemaRef)))
+    TripCount = TripCount32;
+
+  QualType IVTy = TripCount.get()->getType();
+  uint64_t IVWidth = Context.getTypeSize(IVTy);
+
+  auto MakeNumIterationsInIVTy = [&](unsigned I) -> Expr * {
+    return AssertSuccess(SemaRef.PerformImplicitConversion(
+        MakeNumIterations(I), IVTy, AssignmentAction::Converting,
+        /*AllowExplicit=*/true));
+  };
+
+  // \code{.cpp}
+  //   for (auto .flatten.iv = 0; .flatten.iv < n0 * n1 * ...; ++.flatten.iv) {
+  //     .flatten.iv.0 = .flatten.iv / (n1 * ...);
+  //     i0 = ...;                                        // Updates[0]
+  //     .flatten.iv.1 = (.flatten.iv / ...) % n1;
+  //     i1 = ...;                                        // Updates[1]
+  //     ...
+  //     body(i0, i1, ...);
+  //   }
+  // \endcode
+  SmallString<64> FlattenedIVName(".flatten.iv");
+  VarDecl *FlattenedIVDecl = buildVarDecl(SemaRef, {}, IVTy, FlattenedIVName,
+                                          nullptr, OutermostCntVar);
+  auto MakeFlattenedRef = [&SemaRef = this->SemaRef, FlattenedIVDecl, IVTy,
+                           OrigVarLoc]() {
+    return buildDeclRefExpr(SemaRef, FlattenedIVDecl, IVTy, OrigVarLoc);
+  };
+
+  // For init-statement:
+  // \code{.cpp}
+  //   auto .flatten.iv = 0;
+  // \endcode
+  auto *Zero = IntegerLiteral::Create(Context, llvm::APInt::getZero(IVWidth),
+                                      IVTy, OrigVarLoc);
+  SemaRef.AddInitializerToDecl(FlattenedIVDecl, Zero, /*DirectInit=*/false);
+  StmtResult Init = new (Context)
+      DeclStmt(DeclGroupRef(FlattenedIVDecl), OrigVarLocBegin, OrigVarLocEnd);
+  if (!Init.isUsable())
+    return StmtError();
+
+  // For cond-expression:
+  // \code{.cpp}
+  //   .flatten.iv < n0 * n1 * ... * n(k-1)
+  // \endcode
+  ExprResult Cond = SemaRef.BuildBinOp(CurScope, CondLoc, BO_LT,
+                                       MakeFlattenedRef(), TripCount.get());
+  if (!Cond.isUsable())
+    return StmtError();
+
+  // For incr-statement:
+  // \code{.cpp}
+  //   ++.flatten.iv
+  // \endcode
+  ExprResult Incr =
+      SemaRef.BuildUnaryOp(CurScope, OutermostHelper.Inc->getExprLoc(),
+                           UO_PreInc, MakeFlattenedRef());
+  if (!Incr.isUsable())
+    return StmtError();
+
+  // Recover each logical iteration counter via mixed-radix div/mod; reuse the
+  // iteration variables from checkOpenMPLoop so Updates compute user counters.
+  SmallVector<Stmt *, 8> BodyStmts;
+  for (unsigned I = 0; I < NumLoops; ++I) {
+    OMPLoopBasedDirective::HelperExprs &LoopHelper = LoopHelpers[I];
+    auto *IVRef = cast<DeclRefExpr>(LoopHelper.IterationVarRef);
+    auto *IVDecl = cast<VarDecl>(IVRef->getDecl());
+    std::string IVName = (".flatten.iv." + llvm::Twine(I)).str();
+    IVDecl->setDeclName(&SemaRef.PP.getIdentifierTable().get(IVName));
+
+    ExprResult Value = MakeFlattenedRef();
+    if (I + 1 < NumLoops) {
+      ExprResult Divisor = MakeNumIterationsInIVTy(I + 1);
+      for (unsigned J = I + 2; J < NumLoops; ++J) {
+        Divisor = SemaRef.BuildBinOp(CurScope, OrigVarLoc, BO_Mul,
+                                     Divisor.get(), 
MakeNumIterationsInIVTy(J));
+        if (!Divisor.isUsable())
+          return StmtError();
+      }
+      Value = SemaRef.BuildBinOp(CurScope, OrigVarLoc, BO_Div, Value.get(),
+                                 Divisor.get());
+      if (!Value.isUsable())
+        return StmtError();
+    }
+    if (I > 0) {
+      Value = SemaRef.BuildBinOp(CurScope, OrigVarLoc, BO_Rem, Value.get(),
+                                 MakeNumIterationsInIVTy(I));
+      if (!Value.isUsable())
+        return StmtError();
+    }
+
+    SemaRef.AddInitializerToDecl(IVDecl, Value.get(), /*DirectInit=*/false);
+    StmtResult IVStmt = new (Context)
+        DeclStmt(DeclGroupRef(IVDecl), OrigVarLocBegin, OrigVarLocEnd);
+    if (!IVStmt.isUsable())
+      return StmtError();
+
+    BodyStmts.push_back(IVStmt.get());
+    llvm::append_range(BodyStmts, LoopHelper.Updates);
+    if (auto *CXXFor = dyn_cast<CXXForRangeStmt>(LoopStmts[I]))
+      BodyStmts.push_back(CXXFor->getLoopVarStmt());
+  }
+  BodyStmts.push_back(Body);
+  auto *FlattenedBody =
+      CompoundStmt::Create(Context, BodyStmts, FPOptionsOverride(),
+                           Body->getBeginLoc(), Body->getEndLoc());
+
+  auto *FlattenedFor = new (Context) ForStmt(
+      Context, Init.get(), Cond.get(), nullptr, Incr.get(), FlattenedBody,
+      OutermostHelper.Init->getBeginLoc(), OutermostHelper.Init->getBeginLoc(),
+      OutermostHelper.Inc->getEndLoc());
+
+  return OMPFlattenDirective::Create(Context, StartLoc, EndLoc, Clauses,
+                                     NumLoops, AStmt, FlattenedFor,
+                                     buildPreInits(Context, PreInits));
+}
+
 StmtResult SemaOpenMP::ActOnOpenMPFuseDirective(ArrayRef<OMPClause *> Clauses,
                                                 Stmt *AStmt,
                                                 SourceLocation StartLoc,
@@ -16920,6 +17196,9 @@ OMPClause 
*SemaOpenMP::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind,
   case OMPC_partial:
     Res = ActOnOpenMPPartialClause(Expr, StartLoc, LParenLoc, EndLoc);
     break;
+  case OMPC_depth:
+    Res = ActOnOpenMPDepthClause(Expr, StartLoc, LParenLoc, EndLoc);
+    break;
   case OMPC_message:
     Res = ActOnOpenMPMessageClause(Expr, StartLoc, LParenLoc, EndLoc);
     break;
@@ -18241,6 +18520,22 @@ OMPClause *SemaOpenMP::ActOnOpenMPPartialClause(Expr 
*FactorExpr,
                                   FactorExpr);
 }
 
+OMPClause *SemaOpenMP::ActOnOpenMPDepthClause(Expr *DepthExpr,
+                                              SourceLocation StartLoc,
+                                              SourceLocation LParenLoc,
+                                              SourceLocation EndLoc) {
+  // The depth-expr must be a positive integer constant expression and
+  // not greater than the number of loops in the associated loop nest.
+  ExprResult DepthResult = VerifyPositiveIntegerConstantInClause(
+      DepthExpr, OMPC_depth, /*StrictlyPositive=*/true);
+  if (DepthResult.isInvalid())
+    return nullptr;
+  DepthExpr = DepthResult.get();
+
+  return OMPDepthClause::Create(getASTContext(), StartLoc, LParenLoc, EndLoc,
+                                DepthExpr);
+}
+
 OMPClause *SemaOpenMP::ActOnOpenMPLoopRangeClause(
     Expr *First, Expr *Count, SourceLocation StartLoc, SourceLocation 
LParenLoc,
     SourceLocation FirstLoc, SourceLocation CountLoc, SourceLocation EndLoc) {

>From 54be7347979638725cd4b6c421d11ec1cd618c43 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Mon, 27 Jul 2026 07:03:06 -0400
Subject: [PATCH 11/14] add libclang traversal support for flatten directive

---
 clang/tools/libclang/CIndex.cpp   | 7 +++++++
 clang/tools/libclang/CXCursor.cpp | 3 +++
 2 files changed, 10 insertions(+)

diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index f8ab63e3e0dae..9eb0ec8c21158 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -2160,6 +2160,7 @@ class EnqueueVisitor : public 
ConstStmtVisitor<EnqueueVisitor, void>,
   void VisitOMPUnrollDirective(const OMPUnrollDirective *D);
   void VisitOMPReverseDirective(const OMPReverseDirective *D);
   void VisitOMPInterchangeDirective(const OMPInterchangeDirective *D);
+  void VisitOMPFlattenDirective(const OMPFlattenDirective *D);
   void VisitOMPCanonicalLoopSequenceTransformationDirective(
       const OMPCanonicalLoopSequenceTransformationDirective *D);
   void VisitOMPFuseDirective(const OMPFuseDirective *D);
@@ -3358,6 +3359,10 @@ void EnqueueVisitor::VisitOMPInterchangeDirective(
   VisitOMPCanonicalLoopNestTransformationDirective(D);
 }
 
+void EnqueueVisitor::VisitOMPFlattenDirective(const OMPFlattenDirective *D) {
+  VisitOMPCanonicalLoopNestTransformationDirective(D);
+}
+
 void EnqueueVisitor::VisitOMPCanonicalLoopSequenceTransformationDirective(
     const OMPCanonicalLoopSequenceTransformationDirective *D) {
   VisitOMPExecutableDirective(D);
@@ -6329,6 +6334,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind 
Kind) {
     return cxstring::createRef("OMPReverseDirective");
   case CXCursor_OMPInterchangeDirective:
     return cxstring::createRef("OMPInterchangeDirective");
+  case CXCursor_OMPFlattenDirective:
+    return cxstring::createRef("OMPFlattenDirective");
   case CXCursor_OMPFuseDirective:
     return cxstring::createRef("OMPFuseDirective");
   case CXCursor_OMPSplitDirective:
diff --git a/clang/tools/libclang/CXCursor.cpp 
b/clang/tools/libclang/CXCursor.cpp
index b2feed578fc0f..97bbfd36558a9 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -703,6 +703,9 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl 
*Parent,
   case Stmt::OMPInterchangeDirectiveClass:
     K = CXCursor_OMPInterchangeDirective;
     break;
+  case Stmt::OMPFlattenDirectiveClass:
+    K = CXCursor_OMPFlattenDirective;
+    break;
   case Stmt::OMPFuseDirectiveClass:
     K = CXCursor_OMPFuseDirective;
     break;

>From cdedb1045baf373937b0da858624a6ace14a3d21 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Mon, 27 Jul 2026 07:06:36 -0400
Subject: [PATCH 12/14] bindings

---
 clang/bindings/python/clang/cindex.py | 3 +++
 clang/include/clang-c/Index.h         | 4 ++++
 2 files changed, 7 insertions(+)

diff --git a/clang/bindings/python/clang/cindex.py 
b/clang/bindings/python/clang/cindex.py
index 24b737139dba8..ccad140e8c861 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -1456,6 +1456,9 @@ def is_unexposed(self):
     # OpenMP split directive.
     OMP_SPLIT_DIRECTIVE = 312
 
+    # OpenMP flatten directive.
+    OMP_FLATTEN_DIRECTIVE = 313
+
     # OpenACC Compute Construct.
     OPEN_ACC_COMPUTE_DIRECTIVE = 320
 
diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 8427236e0b444..f561fb5724897 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -2170,6 +2170,10 @@ enum CXCursorKind {
    */
   CXCursor_OMPSplitDirective = 312,
 
+  /** OpenMP flatten directive.
+   */
+  CXCursor_OMPFlattenDirective = 313,
+
   /** OpenACC Compute Construct.
    */
   CXCursor_OpenACCComputeConstruct = 320,

>From 94dee8fe1351f74760df0fb2d9903a291a5ccf66 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Wed, 5 Aug 2026 04:46:14 -0400
Subject: [PATCH 13/14] core tests-flatten and depth

---
 clang/test/OpenMP/flatten_codegen.cpp         |  65 ++++++++
 clang/test/OpenMP/flatten_depth_codegen.cpp   | 154 ++++++++++++++++++
 .../test/transform/flatten/depth-intfor.c     |  40 +++++
 .../runtime/test/transform/flatten/intfor.c   |  33 ++++
 .../test/transform/flatten/lit.local.cfg      |   5 +
 5 files changed, 297 insertions(+)
 create mode 100644 clang/test/OpenMP/flatten_codegen.cpp
 create mode 100644 clang/test/OpenMP/flatten_depth_codegen.cpp
 create mode 100644 openmp/runtime/test/transform/flatten/depth-intfor.c
 create mode 100644 openmp/runtime/test/transform/flatten/intfor.c
 create mode 100644 openmp/runtime/test/transform/flatten/lit.local.cfg

diff --git a/clang/test/OpenMP/flatten_codegen.cpp 
b/clang/test/OpenMP/flatten_codegen.cpp
new file mode 100644
index 0000000000000..f77b2fb00ce9b
--- /dev/null
+++ b/clang/test/OpenMP/flatten_codegen.cpp
@@ -0,0 +1,65 @@
+// Check code generation
+// RUN: %clang_cc1 -verify -triple x86_64-pc-linux-gnu -std=c++20 -fopenmp 
-fopenmp-version=60 -emit-llvm %s -o - | FileCheck %s
+
+// expected-no-diagnostics
+
+#ifndef HEADER
+#define HEADER
+
+extern "C" void body(int, int);
+
+// CHECK-LABEL: define {{.*}}void @foo(
+// CHECK:   %.flatten.iv = alloca i64
+// CHECK:   %.flatten.iv.0 = alloca i32
+// CHECK:   %.flatten.iv.1 = alloca i32
+// CHECK:   store i64 0, ptr %.flatten.iv,
+// CHECK:   %[[COND_IV:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[MUL:.+]] = mul nsw i64 %{{.+}}, %{{.+}}
+// CHECK:   icmp slt i64 %[[COND_IV]], %[[MUL]]
+// CHECK:   %[[DIV_IV:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[DIV:.+]] = sdiv i64 %[[DIV_IV]], %{{.+}}
+// CHECK:   store i32 %{{.+}}, ptr %.flatten.iv.0
+// CHECK:   %[[REM_IV:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[REM:.+]] = srem i64 %[[REM_IV]], %{{.+}}
+// CHECK:   store i32 %{{.+}}, ptr %.flatten.iv.1
+// CHECK:   %[[I_VAL:.+]] = load i32, ptr %i,
+// CHECK:   %[[J_VAL:.+]] = load i32, ptr %j,
+// CHECK:   call void @body(i32{{.*}} %[[I_VAL]], i32{{.*}} %[[J_VAL]])
+// CHECK: for.inc:
+// CHECK:   %[[INC_IV:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[NEXT:.+]] = add nsw i64 %[[INC_IV]], 1
+// CHECK:   store i64 %[[NEXT]], ptr %.flatten.iv,
+// CHECK:   br label %for.cond
+extern "C" void foo(int n, int m) {
+#pragma omp flatten
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      body(i, j);
+}
+
+// CHECK-LABEL: define {{.*}}void @bar(
+// CHECK:   icmp slt i32 %{{.+}}, 63
+// CHECK:   sdiv i32 %{{.+}}, 9
+// CHECK:   srem i32 %{{.+}}, 9
+// CHECK:   call void @body(
+extern "C" void bar() {
+#pragma omp flatten
+  for (int i = 0; i < 7; ++i)
+    for (int j = 0; j < 9; ++j)
+      body(i, j);
+}
+
+// CHECK-LABEL: define {{.*}}void @baz(
+// CHECK:   %.flatten.iv = alloca i64
+// CHECK:   icmp slt i64 %{{.+}}, 10000000000
+// CHECK:   sdiv i64 %{{.+}}, 100000
+// CHECK:   srem i64 %{{.+}}, 100000
+// CHECK:   call void @body(
+extern "C" void baz() {
+#pragma omp flatten
+  for (int i = 0; i < 100000; ++i)
+    for (int j = 0; j < 100000; ++j)
+      body(i, j);
+}
+
+#endif
diff --git a/clang/test/OpenMP/flatten_depth_codegen.cpp 
b/clang/test/OpenMP/flatten_depth_codegen.cpp
new file mode 100644
index 0000000000000..77408fbcbdcf1
--- /dev/null
+++ b/clang/test/OpenMP/flatten_depth_codegen.cpp
@@ -0,0 +1,154 @@
+// Check code generation for the 'depth' clause on '#pragma omp flatten'.
+// RUN: %clang_cc1 -verify -triple x86_64-pc-linux-gnu -std=c++20 -fopenmp 
-fopenmp-version=61 -emit-llvm %s -o - | FileCheck %s
+
+// expected-no-diagnostics
+
+#ifndef HEADER
+#define HEADER
+
+extern "C" void body(int, int, int);
+
+// CHECK-LABEL: define {{.*}}void @foo3(
+// CHECK:   %.flatten.iv = alloca i64
+// CHECK:   %.flatten.iv.0 = alloca i32
+// CHECK:   %.flatten.iv.1 = alloca i32
+// CHECK:   %.flatten.iv.2 = alloca i32
+// CHECK:   %[[CIV:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[T01:.+]] = mul nsw i64 %{{.+}}, %{{.+}}
+// CHECK:   %[[TALL:.+]] = mul nsw i64 %[[T01]], %{{.+}}
+// CHECK:   icmp slt i64 %[[CIV]], %[[TALL]]
+// CHECK:   %[[D0IV:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[D0M:.+]] = mul nsw i64 %{{.+}}, %{{.+}}
+// CHECK:   %[[D0:.+]] = sdiv i64 %[[D0IV]], %[[D0M]]
+// CHECK:   store i32 %{{.+}}, ptr %.flatten.iv.0
+// CHECK:   %[[D1IV:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[D1:.+]] = sdiv i64 %[[D1IV]], %{{.+}}
+// CHECK:   %[[R1:.+]] = srem i64 %[[D1]], %{{.+}}
+// CHECK:   store i32 %{{.+}}, ptr %.flatten.iv.1
+// CHECK:   %[[R2IV:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[R2:.+]] = srem i64 %[[R2IV]], %{{.+}}
+// CHECK:   store i32 %{{.+}}, ptr %.flatten.iv.2
+// CHECK:   call void @body(
+extern "C" void foo3(int n, int m, int p) {
+#pragma omp flatten depth(3)
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      for (int k = 0; k < p; ++k)
+        body(i, j, k);
+}
+
+// CHECK-LABEL: define {{.*}}void @foo2(
+// CHECK:   %.flatten.iv = alloca i64
+// CHECK:   %.flatten.iv.0 = alloca i32
+// CHECK:   %.flatten.iv.1 = alloca i32
+// CHECK:   %[[CIV2:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[M:.+]] = mul nsw i64 %{{.+}}, %{{.+}}
+// CHECK:   icmp slt i64 %[[CIV2]], %[[M]]
+// CHECK:   sdiv i64 %{{.+}}, %{{.+}}
+// CHECK:   store i32 %{{.+}}, ptr %.flatten.iv.0
+// CHECK:   srem i64 %{{.+}}, %{{.+}}
+// CHECK:   store i32 %{{.+}}, ptr %.flatten.iv.1
+extern "C" void foo2(int n, int m) {
+#pragma omp flatten depth(2)
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      body(i, j, 0);
+}
+
+// CHECK-LABEL: define {{.*}}void @foo1(
+// CHECK:   %.flatten.iv = alloca i32
+// CHECK:   %.flatten.iv.0 = alloca i32
+// CHECK: for.body:
+// CHECK:   %[[FV:.+]] = load i32, ptr %.flatten.iv,
+// CHECK:   store i32 %[[FV]], ptr %.flatten.iv.0,
+// CHECK-NOT: sdiv
+// CHECK-NOT: srem
+// CHECK:   call void @body(
+extern "C" void foo1(int n) {
+#pragma omp flatten depth(1)
+  for (int i = 0; i < n; ++i)
+    body(i, 0, 0);
+}
+
+// CHECK-LABEL: define {{.*}}void @foo2partial(
+// CHECK:   %.flatten.iv = alloca i64
+// CHECK:   %.flatten.iv.0 = alloca i32
+// CHECK:   %.flatten.iv.1 = alloca i32
+// CHECK-NOT: %.flatten.iv.2 = alloca
+// CHECK:   %[[CIV:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[M:.+]] = mul nsw i64 %{{.+}}, %{{.+}}
+// CHECK:   icmp slt i64 %[[CIV]], %[[M]]
+// CHECK:   sdiv i64 %{{.+}}, %{{.+}}
+// CHECK:   store i32 %{{.+}}, ptr %.flatten.iv.0
+// CHECK:   srem i64 %{{.+}}, %{{.+}}
+// CHECK:   store i32 %{{.+}}, ptr %.flatten.iv.1
+// CHECK:   br label %for.cond{{.*}}
+// CHECK: for.cond{{.*}}:
+// CHECK:   icmp slt i32 %{{.+}}, 5
+extern "C" void foo2partial(int n, int m) {
+#pragma omp flatten depth(2)
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      for (int k = 0; k < 5; ++k)
+        body(i, j, k);
+}
+
+// A value-dependent 'depth' (the argument is a non-type template parameter) is
+// deferred until instantiation, where the concrete depth drives the 
flattening.
+
+// CHECK-LABEL: define {{.*}}void @_Z11tmpl_depth3ILi3EEviii(
+// CHECK:   %.flatten.iv = alloca i64
+// CHECK:   %.flatten.iv.0 = alloca i32
+// CHECK:   %.flatten.iv.1 = alloca i32
+// CHECK:   %.flatten.iv.2 = alloca i32
+// CHECK:   %[[CIV:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[T01:.+]] = mul nsw i64 %{{.+}}, %{{.+}}
+// CHECK:   %[[TALL:.+]] = mul nsw i64 %[[T01]], %{{.+}}
+// CHECK:   icmp slt i64 %[[CIV]], %[[TALL]]
+// CHECK:   %[[D0IV:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[D0M:.+]] = mul nsw i64 %{{.+}}, %{{.+}}
+// CHECK:   %[[D0:.+]] = sdiv i64 %[[D0IV]], %[[D0M]]
+// CHECK:   store i32 %{{.+}}, ptr %.flatten.iv.0
+// CHECK:   %[[D1IV:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[D1:.+]] = sdiv i64 %[[D1IV]], %{{.+}}
+// CHECK:   %[[R1:.+]] = srem i64 %[[D1]], %{{.+}}
+// CHECK:   store i32 %{{.+}}, ptr %.flatten.iv.1
+// CHECK:   %[[R2IV:.+]] = load i64, ptr %.flatten.iv,
+// CHECK:   %[[R2:.+]] = srem i64 %[[R2IV]], %{{.+}}
+// CHECK:   store i32 %{{.+}}, ptr %.flatten.iv.2
+// CHECK:   call void @body(
+template <int D>
+void tmpl_depth3(int n, int m, int p) {
+#pragma omp flatten depth(D)
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      for (int k = 0; k < p; ++k)
+        body(i, j, k);
+}
+
+// A value-dependent 'depth' that instantiates to depth(1) on a single loop is
+// well-formed: the shallower-than-default nest is accepted because validation 
is
+// deferred to instantiation. The single loop needs no div/mod recovery.
+
+// CHECK-LABEL: define {{.*}}void @_Z11tmpl_depth1ILi1EEvi(
+// CHECK:   %.flatten.iv = alloca i32
+// CHECK:   %.flatten.iv.0 = alloca i32
+// CHECK: for.body:
+// CHECK:   %[[FV:.+]] = load i32, ptr %.flatten.iv,
+// CHECK:   store i32 %[[FV]], ptr %.flatten.iv.0,
+// CHECK-NOT: sdiv
+// CHECK-NOT: srem
+// CHECK:   call void @body(
+template <int D>
+void tmpl_depth1(int n) {
+#pragma omp flatten depth(D)
+  for (int i = 0; i < n; ++i)
+    body(i, 0, 0);
+}
+
+void inst() {
+  tmpl_depth3<3>(1, 1, 1);
+  tmpl_depth1<1>(1);
+}
+
+#endif
diff --git a/openmp/runtime/test/transform/flatten/depth-intfor.c 
b/openmp/runtime/test/transform/flatten/depth-intfor.c
new file mode 100644
index 0000000000000..7211e02e25313
--- /dev/null
+++ b/openmp/runtime/test/transform/flatten/depth-intfor.c
@@ -0,0 +1,40 @@
+// RUN: %libomp-compile-and-run | FileCheck %s --match-full-lines
+
+// 'depth(3)' fully flattens a three-deep perfectly nested loop into one loop.
+// This verifies that the flattened loop preserves the exact row-major
+// visitation order of the original nest, including non-unit steps and non-zero
+// start values.
+
+#ifndef HEADER
+#define HEADER
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int main() {
+  printf("do\n");
+#pragma omp flatten depth(3)
+  for (int i = 7; i < 11; i += 2)
+    for (int j = 1; j < 4; j += 1)
+      for (int k = 0; k < 2; ++k)
+        printf("i=%d j=%d k=%d\n", i, j, k);
+  printf("done\n");
+  return EXIT_SUCCESS;
+}
+
+#endif /* HEADER */
+
+// CHECK:      do
+// CHECK-NEXT: i=7 j=1 k=0
+// CHECK-NEXT: i=7 j=1 k=1
+// CHECK-NEXT: i=7 j=2 k=0
+// CHECK-NEXT: i=7 j=2 k=1
+// CHECK-NEXT: i=7 j=3 k=0
+// CHECK-NEXT: i=7 j=3 k=1
+// CHECK-NEXT: i=9 j=1 k=0
+// CHECK-NEXT: i=9 j=1 k=1
+// CHECK-NEXT: i=9 j=2 k=0
+// CHECK-NEXT: i=9 j=2 k=1
+// CHECK-NEXT: i=9 j=3 k=0
+// CHECK-NEXT: i=9 j=3 k=1
+// CHECK-NEXT: done
diff --git a/openmp/runtime/test/transform/flatten/intfor.c 
b/openmp/runtime/test/transform/flatten/intfor.c
new file mode 100644
index 0000000000000..f937659fbebe1
--- /dev/null
+++ b/openmp/runtime/test/transform/flatten/intfor.c
@@ -0,0 +1,33 @@
+// RUN: %libomp-compile-and-run | FileCheck %s --match-full-lines
+
+#ifndef HEADER
+#define HEADER
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int main() {
+  printf("do\n");
+#pragma omp flatten
+  for (int i = 5; i < 11; i += 2)
+    for (int j = 1; j < 7; j += 2)
+      printf("i=%d j=%d\n", i, j);
+  printf("done\n");
+  return EXIT_SUCCESS;
+}
+
+#endif /* HEADER */
+
+// The flattened loop visits the original iteration space in the original
+// (row-major) order: for each outer iteration the inner loop runs fully.
+// CHECK:      do
+// CHECK-NEXT: i=5 j=1
+// CHECK-NEXT: i=5 j=3
+// CHECK-NEXT: i=5 j=5
+// CHECK-NEXT: i=7 j=1
+// CHECK-NEXT: i=7 j=3
+// CHECK-NEXT: i=7 j=5
+// CHECK-NEXT: i=9 j=1
+// CHECK-NEXT: i=9 j=3
+// CHECK-NEXT: i=9 j=5
+// CHECK-NEXT: done
diff --git a/openmp/runtime/test/transform/flatten/lit.local.cfg 
b/openmp/runtime/test/transform/flatten/lit.local.cfg
new file mode 100644
index 0000000000000..e05bd627df1e9
--- /dev/null
+++ b/openmp/runtime/test/transform/flatten/lit.local.cfg
@@ -0,0 +1,5 @@
+# The flatten directive's depth clause requires OpenMP 6.1.
+for i, (pattern, replacement) in enumerate(config.substitutions):
+    if pattern == "%openmp_flags":
+        config.substitutions[i] = (pattern, replacement + " 
-fopenmp-version=61")
+        break

>From 667fe837a39dbc711dcc5288b736aa52fbe893b6 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Wed, 5 Aug 2026 04:48:45 -0400
Subject: [PATCH 14/14] TBD

---
 clang/docs/OpenMPSupport.rst                  |   2 +-
 clang/include/clang/ASTMatchers/ASTMatchers.h |  14 ++
 clang/lib/ASTMatchers/ASTMatchersInternal.cpp |   2 +
 clang/lib/ASTMatchers/Dynamic/Registry.cpp    |   1 +
 clang/test/Index/openmp-flatten.c             |  12 ++
 clang/test/OpenMP/flatten_ast_print.cpp       | 124 ++++++++++++++++++
 clang/test/OpenMP/flatten_codegen_for.cpp     |  67 ++++++++++
 clang/test/OpenMP/flatten_depth_ast_print.cpp |  74 +++++++++++
 clang/test/OpenMP/flatten_depth_messages.cpp  |  48 +++++++
 clang/test/OpenMP/flatten_messages.cpp        |  89 +++++++++++++
 .../test/OpenMP/flatten_serialize_module.cpp  |  25 ++++
 .../ASTMatchers/ASTMatchersNodeTest.cpp       |  22 ++++
 flatten-verify-tmp/01_default_depth2.cpp      |   8 ++
 flatten-verify-tmp/02_depth3_full.cpp         |   9 ++
 flatten-verify-tmp/03_depth2_partial.cpp      |  10 ++
 flatten-verify-tmp/04_depth1_identity.cpp     |   8 ++
 flatten-verify-tmp/05_rangefor_template.cpp   |  20 +++
 flatten-verify-tmp/README.md                  |  39 ++++++
 flatten-verify-tmp/end_result_check.cpp       |  82 ++++++++++++
 .../test/transform/flatten/depth-partial.c    |  45 +++++++
 openmp/runtime/test/transform/flatten/empty.c |  42 ++++++
 .../test/transform/flatten/foreach.cpp        |  31 +++++
 .../flatten/parallel-wsloop-foreach.cpp       |  32 +++++
 23 files changed, 805 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/Index/openmp-flatten.c
 create mode 100644 clang/test/OpenMP/flatten_ast_print.cpp
 create mode 100644 clang/test/OpenMP/flatten_codegen_for.cpp
 create mode 100644 clang/test/OpenMP/flatten_depth_ast_print.cpp
 create mode 100644 clang/test/OpenMP/flatten_depth_messages.cpp
 create mode 100644 clang/test/OpenMP/flatten_messages.cpp
 create mode 100644 clang/test/OpenMP/flatten_serialize_module.cpp
 create mode 100644 flatten-verify-tmp/01_default_depth2.cpp
 create mode 100644 flatten-verify-tmp/02_depth3_full.cpp
 create mode 100644 flatten-verify-tmp/03_depth2_partial.cpp
 create mode 100644 flatten-verify-tmp/04_depth1_identity.cpp
 create mode 100644 flatten-verify-tmp/05_rangefor_template.cpp
 create mode 100644 flatten-verify-tmp/README.md
 create mode 100644 flatten-verify-tmp/end_result_check.cpp
 create mode 100644 openmp/runtime/test/transform/flatten/depth-partial.c
 create mode 100644 openmp/runtime/test/transform/flatten/empty.c
 create mode 100644 openmp/runtime/test/transform/flatten/foreach.cpp
 create mode 100644 
openmp/runtime/test/transform/flatten/parallel-wsloop-foreach.cpp

diff --git a/clang/docs/OpenMPSupport.rst b/clang/docs/OpenMPSupport.rst
index 0c21aa41ff269..c11a46c3bb6a6 100644
--- a/clang/docs/OpenMPSupport.rst
+++ b/clang/docs/OpenMPSupport.rst
@@ -666,7 +666,7 @@ implementation.
 
+=============================================================+===========================+===========================+==========================================================================+
 | dyn_groupprivate clause                                     | 
:part:`partial`           | :part:`In Progress`       | C/C++: Host device 
support missing                                       |
 
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
-| loop flatten transformation                                 | 
:none:`unclaimed`         | :none:`unclaimed`         |                         
                                                 |
+| loop flatten transformation                                 | 
:part:`partial`           | :none:`unclaimed`         | Clang: depth clause 
supported (OpenMP 6.1); apply clause not yet supported|
 
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
 | loop grid/tile modifiers for sizes clause                   | 
:none:`unclaimed`         | :none:`unclaimed`         |                         
                                                 |
 
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h 
b/clang/include/clang/ASTMatchers/ASTMatchers.h
index bc0f35898a2c9..602bab9aa8cb8 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -8842,6 +8842,20 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt,
 extern const internal::VariadicDynCastAllOfMatcher<Stmt, OMPSplitDirective>
     ompSplitDirective;
 
+/// Matches OpenMP ``flatten`` directive.
+///
+/// Given
+///
+/// \code
+///   #pragma omp flatten
+///   for (int i = 0; i < m; ++i)
+///     for (int j = 0; j < n; ++j) {}
+/// \endcode
+///
+/// ``ompFlattenDirective()`` matches the flatten directive.
+extern const internal::VariadicDynCastAllOfMatcher<Stmt, OMPFlattenDirective>
+    ompFlattenDirective;
+
 /// Matches OpenMP ``counts`` clause used by ``#pragma omp split``.
 ///
 /// Given ``#pragma omp split counts(1, 2, omp_fill)``, ``ompCountsClause()``
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp 
b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 5cbf134620e34..e2c0c25518b70 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -1141,6 +1141,8 @@ const internal::VariadicDynCastAllOfMatcher<Stmt, 
OMPTargetUpdateDirective>
     ompTargetUpdateDirective;
 const internal::VariadicDynCastAllOfMatcher<Stmt, OMPSplitDirective>
     ompSplitDirective;
+const internal::VariadicDynCastAllOfMatcher<Stmt, OMPFlattenDirective>
+    ompFlattenDirective;
 const internal::VariadicDynCastAllOfMatcher<OMPClause, OMPCountsClause>
     ompCountsClause;
 const internal::VariadicDynCastAllOfMatcher<OMPClause, OMPDefaultClause>
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp 
b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index a04070971f0eb..a6252363e08ce 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -534,6 +534,7 @@ RegistryMaps::RegistryMaps() {
   REGISTER_MATCHER(ompFromClause);
   REGISTER_MATCHER(ompToClause);
   REGISTER_MATCHER(ompExecutableDirective);
+  REGISTER_MATCHER(ompFlattenDirective);
   REGISTER_MATCHER(ompSplitDirective);
   REGISTER_MATCHER(ompTargetUpdateDirective);
   REGISTER_MATCHER(on);
diff --git a/clang/test/Index/openmp-flatten.c 
b/clang/test/Index/openmp-flatten.c
new file mode 100644
index 0000000000000..74bc3cda3ef1a
--- /dev/null
+++ b/clang/test/Index/openmp-flatten.c
@@ -0,0 +1,12 @@
+// RUN: c-index-test -test-load-source local %s -fopenmp=libomp 
-fopenmp-version=60 | FileCheck %s
+
+void test(void) {
+#pragma omp flatten
+  for (int i = 0; i < 20; i += 1)
+    for (int j = 0; j < 30; j += 1)
+      ;
+}
+
+// CHECK: openmp-flatten.c:4:1: OMPFlattenDirective= Extent=[4:1 - 4:20]
+// CHECK: openmp-flatten.c:5:3: ForStmt= Extent=[5:3 - 7:8]
+// CHECK: openmp-flatten.c:6:5: ForStmt= Extent=[6:5 - 7:8]
diff --git a/clang/test/OpenMP/flatten_ast_print.cpp 
b/clang/test/OpenMP/flatten_ast_print.cpp
new file mode 100644
index 0000000000000..e1115bf112c40
--- /dev/null
+++ b/clang/test/OpenMP/flatten_ast_print.cpp
@@ -0,0 +1,124 @@
+// Check no warnings/errors
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 
-fsyntax-only -verify %s
+// expected-no-diagnostics
+
+// Check AST and unparsing
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 
-ast-dump  %s | FileCheck %s --check-prefix=DUMP
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 
-ast-print %s | FileCheck %s --check-prefix=PRINT
+
+// Check same results after serialization round-trip
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 
-emit-pch -o %t %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 
-include-pch %t -ast-dump-all %s | FileCheck %s --check-prefix=DUMP
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 
-include-pch %t -ast-print    %s | FileCheck %s --check-prefix=PRINT
+
+#ifndef HEADER
+#define HEADER
+
+// placeholder for loop body code.
+extern "C" void body(...);
+
+// PRINT-LABEL: void foo1(
+// DUMP-LABEL:  FunctionDecl {{.*}} foo1
+void foo1() {
+  // PRINT:     #pragma omp flatten
+  // DUMP:      OMPFlattenDirective
+  #pragma omp flatten
+  // PRINT: for (int i = 0; i < 7; i += 1)
+  // DUMP-NEXT: ForStmt
+  for (int i = 0; i < 7; i += 1)
+    // PRINT: for (int j = 0; j < 9; j += 1)
+    // DUMP:  ForStmt
+    for (int j = 0; j < 9; j += 1)
+      // PRINT: body(i, j);
+      // DUMP:  CallExpr
+      body(i, j);
+}
+
+// PRINT-LABEL: void foo2(
+// DUMP-LABEL:  FunctionDecl {{.*}} foo2
+void foo2(int start1, int end1, int step1, int start2, int end2, int step2) {
+  // PRINT:     #pragma omp flatten
+  // DUMP:      OMPFlattenDirective
+  #pragma omp flatten
+  // PRINT: for (int i = start1; i < end1; i += step1)
+  // DUMP-NEXT: ForStmt
+  for (int i = start1; i < end1; i += step1)
+    // PRINT: for (int j = start2; j < end2; j += step2)
+    // DUMP:  ForStmt
+    for (int j = start2; j < end2; j += step2)
+      // PRINT: body(i, j);
+      // DUMP:  CallExpr
+      body(i, j);
+}
+
+// PRINT-LABEL: void foo3(
+// DUMP-LABEL:  FunctionDecl {{.*}} foo3
+void foo3() {
+  // Verify that a flattened loop can itself be the operand of another loop
+  // transformation. The outer reverse consumes the loop generated by flatten.
+  // PRINT:     #pragma omp reverse
+  // DUMP:      OMPReverseDirective
+  #pragma omp reverse
+  // PRINT:     #pragma omp flatten
+  // DUMP:      OMPFlattenDirective
+  #pragma omp flatten
+  // PRINT: for (int i = 0; i < 13; i += 1)
+  // DUMP-NEXT: ForStmt
+  for (int i = 0; i < 13; i += 1)
+    // PRINT: for (int j = 0; j < 17; j += 1)
+    // DUMP:  ForStmt
+    for (int j = 0; j < 17; j += 1)
+      // PRINT: body(i, j);
+      // DUMP:  CallExpr
+      body(i, j);
+}
+
+// Range-based for loops are supported as affected loops.
+// PRINT-LABEL: void foo4(
+// DUMP-LABEL:  FunctionDecl {{.*}} foo4
+struct Range {
+  int *begin();
+  int *end();
+};
+void foo4(Range r1, Range r2) {
+  // PRINT:     #pragma omp flatten
+  // DUMP:      OMPFlattenDirective
+  #pragma omp flatten
+  // PRINT: for (int i : r1)
+  // DUMP-NEXT: CXXForRangeStmt
+  for (int i : r1)
+    // PRINT: for (int j : r2)
+    // DUMP:  CXXForRangeStmt
+    for (int j : r2)
+      // PRINT: body(i, j);
+      // DUMP:  CallExpr
+      body(i, j);
+}
+
+// The directive is instantiated together with its enclosing template.
+// PRINT-LABEL: template <typename T, int LO, int HI> void foo5()
+// DUMP-LABEL:  FunctionTemplateDecl {{.*}} foo5
+template <typename T, int LO, int HI>
+void foo5() {
+  // PRINT:     #pragma omp flatten
+  // DUMP:      OMPFlattenDirective
+  #pragma omp flatten
+  // PRINT: for (T i = LO; i < HI; ++i)
+  // DUMP-NEXT: ForStmt
+  for (T i = LO; i < HI; ++i)
+    // PRINT: for (T j = LO; j < HI; ++j)
+    // DUMP:  ForStmt
+    for (T j = LO; j < HI; ++j)
+      // PRINT: body(i, j);
+      // DUMP:  CallExpr
+      body(i, j);
+}
+
+// PRINT-LABEL: template<> void foo5<int, 0, 8>()
+// DUMP-LABEL:  FunctionDecl {{.*}} foo5 'void ()' implicit_instantiation
+// DUMP:        OMPFlattenDirective
+void inst() {
+  foo5<int, 0, 8>();
+}
+
+#endif
diff --git a/clang/test/OpenMP/flatten_codegen_for.cpp 
b/clang/test/OpenMP/flatten_codegen_for.cpp
new file mode 100644
index 0000000000000..d64128bd25d78
--- /dev/null
+++ b/clang/test/OpenMP/flatten_codegen_for.cpp
@@ -0,0 +1,67 @@
+// Check that the loop generated by 'flatten' can be consumed by a loop
+// associated directive ('omp for' / 'omp parallel for'), and that signed and
+// unsigned induction variables generate signed/unsigned division.
+
+// RUN: %clang_cc1 -verify -triple x86_64-pc-linux-gnu -std=c++20 -fopenmp 
-fopenmp-version=60 -emit-llvm %s -o - | FileCheck %s
+
+// expected-no-diagnostics
+
+#ifndef HEADER
+#define HEADER
+
+extern "C" void body(int, int);
+extern "C" void ubody(unsigned, unsigned);
+
+// 'omp for' worksharing over the flattened iteration space: the flattened loop
+// uses a 64-bit induction variable for the variable-bound product trip count,
+// so the worksharing loop is distributed with the 64-bit static-init call and
+// the body recovers the logical counters via signed division/remainder.
+//
+// CHECK-LABEL: define {{.*}}void @wsfor(
+// CHECK:   mul nsw i64
+// CHECK:   call void @__kmpc_for_static_init_8(
+// CHECK:   sdiv i64
+// CHECK:   srem i64
+// CHECK:   call void @body(
+// CHECK:   call void @__kmpc_for_static_fini(
+extern "C" void wsfor(int n, int m) {
+#pragma omp for
+#pragma omp flatten
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      body(i, j);
+}
+
+// 'omp parallel for' outlines the flattened loop and worksharing-distributes
+// it.
+//
+// CHECK-LABEL: define {{.*}}void @pfor(
+// CHECK:   call {{.*}}@__kmpc_fork_call(
+// CHECK-LABEL: define internal void @pfor.omp_outlined(
+// CHECK:   call void @__kmpc_for_static_init_8(
+// CHECK:   sdiv i64
+// CHECK:   srem i64
+// CHECK:   call void @body(
+extern "C" void pfor(int n, int m) {
+#pragma omp parallel for
+#pragma omp flatten
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      body(i, j);
+}
+
+// Unsigned induction variables produce unsigned division/remainder.
+//
+// CHECK-LABEL: define {{.*}}void @ufor(
+// CHECK:   icmp ult i32 %{{.+}}, 63
+// CHECK:   udiv i32 %{{.+}}, 9
+// CHECK:   urem i32 %{{.+}}, 9
+// CHECK:   call void @ubody(
+extern "C" void ufor() {
+#pragma omp flatten
+  for (unsigned i = 0; i < 7u; ++i)
+    for (unsigned j = 0; j < 9u; ++j)
+      ubody(i, j);
+}
+
+#endif
diff --git a/clang/test/OpenMP/flatten_depth_ast_print.cpp 
b/clang/test/OpenMP/flatten_depth_ast_print.cpp
new file mode 100644
index 0000000000000..ceaaa68e1f79a
--- /dev/null
+++ b/clang/test/OpenMP/flatten_depth_ast_print.cpp
@@ -0,0 +1,74 @@
+// Check no warnings/errors
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=61 
-fsyntax-only -verify %s
+// expected-no-diagnostics
+
+// Check AST and unparsing
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=61 
-ast-dump  %s | FileCheck %s --check-prefix=DUMP
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=61 
-ast-print %s | FileCheck %s --check-prefix=PRINT
+
+// Check same results after serialization round-trip
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=61 
-emit-pch -o %t %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=61 
-include-pch %t -ast-dump-all %s | FileCheck %s --check-prefix=DUMP
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=61 
-include-pch %t -ast-print    %s | FileCheck %s --check-prefix=PRINT
+
+#ifndef HEADER
+#define HEADER
+
+// placeholder for loop body code.
+extern "C" void body(...);
+
+// The depth clause round-trips through -ast-print and PCH serialization.
+// PRINT-LABEL: void foo_depth3(
+// DUMP-LABEL:  FunctionDecl {{.*}} foo_depth3
+void foo_depth3(int n, int m, int p) {
+  // PRINT:     #pragma omp flatten depth(3)
+  // DUMP:      OMPFlattenDirective
+  // DUMP-NEXT: OMPDepthClause
+  #pragma omp flatten depth(3)
+  // PRINT: for (int i = 0; i < n; ++i)
+  // DUMP:  ForStmt
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      for (int k = 0; k < p; ++k)
+        // PRINT: body(i, j, k);
+        // DUMP:  CallExpr
+        body(i, j, k);
+}
+
+// depth(2) is accepted and printed like any other depth argument.
+// PRINT-LABEL: void foo_depth2(
+// DUMP-LABEL:  FunctionDecl {{.*}} foo_depth2
+void foo_depth2(int n, int m) {
+  // PRINT:     #pragma omp flatten depth(2)
+  // DUMP:      OMPFlattenDirective
+  // DUMP-NEXT: OMPDepthClause
+  #pragma omp flatten depth(2)
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      body(i, j);
+}
+
+// The depth clause is instantiated together with its enclosing template.
+// PRINT-LABEL: template <typename T> void foo_tmpl()
+// DUMP-LABEL:  FunctionTemplateDecl {{.*}} foo_tmpl
+template <typename T>
+void foo_tmpl() {
+  // PRINT:     #pragma omp flatten depth(3)
+  // DUMP:      OMPFlattenDirective
+  // DUMP-NEXT: OMPDepthClause
+  #pragma omp flatten depth(3)
+  for (T i = 0; i < 8; ++i)
+    for (T j = 0; j < 8; ++j)
+      for (T k = 0; k < 8; ++k)
+        body(i, j, k);
+}
+
+// PRINT-LABEL: template<> void foo_tmpl<int>()
+// DUMP-LABEL:  FunctionDecl {{.*}} foo_tmpl 'void ()' implicit_instantiation
+// DUMP:        OMPFlattenDirective
+// DUMP-NEXT:   OMPDepthClause
+void inst() {
+  foo_tmpl<int>();
+}
+
+#endif
diff --git a/clang/test/OpenMP/flatten_depth_messages.cpp 
b/clang/test/OpenMP/flatten_depth_messages.cpp
new file mode 100644
index 0000000000000..16f60318d0d06
--- /dev/null
+++ b/clang/test/OpenMP/flatten_depth_messages.cpp
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++20 -fopenmp 
-fopenmp-version=61 -fsyntax-only -Wuninitialized -verify %s
+
+extern "C" void body(...);
+
+// expected-note@+1 {{declared here}}
+void func(int n) {
+
+  // The depth argument must be a positive integer constant expression.
+  // expected-error@+1 {{argument to 'depth' clause must be a strictly 
positive integer value}}
+  #pragma omp flatten depth(0)
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < n; ++j)
+      body(i, j);
+
+  // expected-error@+1 {{argument to 'depth' clause must be a strictly 
positive integer value}}
+  #pragma omp flatten depth(-1)
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < n; ++j)
+      body(i, j);
+
+  // A non-constant argument is rejected.
+  // expected-error@+2 {{expression is not an integral constant expression}}
+  // expected-note@+1 {{function parameter 'n' with unknown value cannot be 
used in a constant expression}}
+  #pragma omp flatten depth(n)
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < n; ++j)
+      body(i, j);
+
+  // At most one 'depth' clause may appear on the directive.
+  // expected-error@+1 {{directive '#pragma omp flatten' cannot contain more 
than one 'depth' clause}}
+  #pragma omp flatten depth(2) depth(2)
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < n; ++j)
+      body(i, j);
+
+  // The depth must be at most the loop nest depth: depth(3) requires three
+  // perfectly nested loops, but only two are present here.
+  #pragma omp flatten depth(3)
+  // expected-error@+1 {{expected 3 for loops after '#pragma omp flatten', but 
found only 2}}
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < n; ++j)
+      body(i, j);
+
+  // depth(1) flattens a single loop and is well-formed (no diagnostic).
+  #pragma omp flatten depth(1)
+  for (int i = 0; i < n; ++i)
+    body(i);
+}
diff --git a/clang/test/OpenMP/flatten_messages.cpp 
b/clang/test/OpenMP/flatten_messages.cpp
new file mode 100644
index 0000000000000..a022e0e8999d2
--- /dev/null
+++ b/clang/test/OpenMP/flatten_messages.cpp
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++20 -fopenmp 
-fopenmp-version=60 -fsyntax-only -Wuninitialized -verify %s
+
+extern "C" void body(...);
+
+void func(int n) {
+
+  // The associated statement must be a for loop.
+  // expected-error@+2 {{statement after '#pragma omp flatten' must be a for 
loop}}
+  #pragma omp flatten
+  ;
+
+  // A non-loop statement is rejected as well.
+  // expected-error@+2 {{statement after '#pragma omp flatten' must be a for 
loop}}
+  #pragma omp flatten
+  int b = 0;
+
+  // A single loop is not enough: flatten combines two perfectly nested loops,
+  // so the body of the outer loop must itself be a for loop.
+  #pragma omp flatten
+  for (int i = 0; i < 7; ++i)
+    // expected-error@+1 {{statement after '#pragma omp flatten' must be a for 
loop}}
+    ;
+
+  // The associated statement of a directive is not a for loop.
+  // expected-error@+2 {{statement after '#pragma omp flatten' must be a for 
loop}}
+  #pragma omp flatten
+  #pragma omp for
+  for (int i = 0; i < 7; ++i)
+    for (int j = 0; j < 7; ++j)
+      body(i, j);
+
+  {
+    // expected-error@+2 {{expected statement}}
+    #pragma omp flatten
+  }
+
+  // The loops must be perfectly nested: no code is allowed between them.
+  #pragma omp flatten
+  for (int i = 0; i < n; ++i) {
+    int x = 0;
+    // expected-error@-2 {{statement after '#pragma omp flatten' must be a for 
loop}}
+    for (int j = 0; j < n; ++j)
+      body(i, j, x);
+  }
+
+  // Each affected loop must be in OpenMP canonical form.
+  #pragma omp flatten
+  for (int i = 0; i < n; ++i)
+    // expected-error@+1 {{condition of OpenMP for loop must be a relational 
comparison ('<', '<=', '>', '>=', or '!=') of loop variable 'j'}}
+    for (int j = 0; j / 3 < n; ++j)
+      body(i, j);
+
+  // The affected loops must be rectangular: an inner bound may not depend on 
an
+  // outer loop counter.
+  #pragma omp flatten
+  for (int i = 0; i < n; ++i)
+    // expected-error@+1 {{expected loop invariant expression}}
+    for (int j = i; j < n; ++j)
+      body(i, j);
+
+  // The 'sizes' clause is not allowed on 'flatten'.
+  // expected-error@+1 {{unexpected OpenMP clause 'sizes' in directive 
'#pragma omp flatten'}}
+  #pragma omp flatten sizes(2)
+  for (int i = 0; i < 7; ++i)
+    for (int j = 0; j < 9; ++j)
+      body(i, j);
+
+  // The 'permutation' clause is not allowed on 'flatten'.
+  // expected-error@+1 {{unexpected OpenMP clause 'permutation' in directive 
'#pragma omp flatten'}}
+  #pragma omp flatten permutation(2, 1)
+  for (int i = 0; i < 7; ++i)
+    for (int j = 0; j < 9; ++j)
+      body(i, j);
+
+  // Tokens after the directive name are ignored with a warning.
+  // expected-warning@+1 {{extra tokens at the end of '#pragma omp flatten' 
are ignored}}
+  #pragma omp flatten foo
+  for (int i = 0; i < 7; ++i)
+    for (int j = 0; j < 9; ++j)
+      body(i, j);
+
+  // The 'depth' clause is only available from OpenMP 6.1; it is rejected here
+  // under -fopenmp-version=60.
+  // expected-error@+1 {{unexpected OpenMP clause 'depth' in directive 
'#pragma omp flatten'}}
+  #pragma omp flatten depth(2)
+  for (int i = 0; i < 7; ++i)
+    for (int j = 0; j < 9; ++j)
+      body(i, j);
+}
diff --git a/clang/test/OpenMP/flatten_serialize_module.cpp 
b/clang/test/OpenMP/flatten_serialize_module.cpp
new file mode 100644
index 0000000000000..5d3420500b282
--- /dev/null
+++ b/clang/test/OpenMP/flatten_serialize_module.cpp
@@ -0,0 +1,25 @@
+// C++20 module interface with `#pragma omp flatten` — emit BMI + import; AST 
retains directive.
+//
+// RUN: rm -rf %t && split-file %s %t && cd %t
+// RUN: %clang_cc1 -std=c++20 -fopenmp -fopenmp-version=60 -triple 
x86_64-unknown-linux-gnu %t/FlattenMod.cppm -emit-module-interface -o 
%t/FlattenMod.pcm
+// RUN: %clang_cc1 -std=c++20 -fopenmp -fopenmp-version=60 -triple 
x86_64-unknown-linux-gnu %t/UseFlattenMod.cpp 
-fmodule-file=FlattenMod=%t/FlattenMod.pcm -ast-dump-all | FileCheck 
%t/FlattenMod.cppm
+
+// expected-no-diagnostics
+
+//--- FlattenMod.cppm
+module;
+export module FlattenMod;
+
+export void flattenfoo(int n, int m) {
+// CHECK: OMPFlattenDirective
+// CHECK: ForStmt
+#pragma omp flatten
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j) {
+    }
+}
+
+//--- UseFlattenMod.cpp
+import FlattenMod;
+
+void g(void) { flattenfoo(10, 20); }
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp 
b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
index 4190d4703e37d..34c0fd27821d1 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -3126,6 +3126,28 @@ void f() {
   EXPECT_TRUE(notMatchesWithOpenMP60(ParallelOnly, Matcher));
 }
 
+// OpenMP 6 flatten directive
+TEST(ASTMatchersTestOpenMP, OMPFlattenDirective) {
+  auto Matcher = stmt(ompFlattenDirective());
+
+  StringRef FlattenOk = R"(
+void f() {
+#pragma omp flatten
+  for (int i = 0; i < 10; ++i)
+    for (int j = 0; j < 10; ++j) {}
+}
+)";
+  EXPECT_TRUE(matchesWithOpenMP60(FlattenOk, Matcher));
+
+  StringRef ParallelOnly = R"(
+void f() {
+#pragma omp parallel
+  ;
+}
+)";
+  EXPECT_TRUE(notMatchesWithOpenMP60(ParallelOnly, Matcher));
+}
+
 TEST(ASTMatchersTestOpenMP, OMPSplitDirective_HasCountsClause) {
   auto Matcher = stmt(ompSplitDirective(hasAnyClause(ompCountsClause())));
 
diff --git a/flatten-verify-tmp/01_default_depth2.cpp 
b/flatten-verify-tmp/01_default_depth2.cpp
new file mode 100644
index 0000000000000..900d0263622cd
--- /dev/null
+++ b/flatten-verify-tmp/01_default_depth2.cpp
@@ -0,0 +1,8 @@
+// Default flatten: combine 2 perfectly nested loops (depth defaults to 2).
+extern "C" void body(int, int);
+void t(int n, int m) {
+#pragma omp flatten
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      body(i, j);
+}
diff --git a/flatten-verify-tmp/02_depth3_full.cpp 
b/flatten-verify-tmp/02_depth3_full.cpp
new file mode 100644
index 0000000000000..ba9f0b7ed8808
--- /dev/null
+++ b/flatten-verify-tmp/02_depth3_full.cpp
@@ -0,0 +1,9 @@
+// depth(3): fully flatten a three-deep perfect nest (OpenMP 6.1).
+extern "C" void body(int, int, int);
+void t(int n, int m, int p) {
+#pragma omp flatten depth(3)
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      for (int k = 0; k < p; ++k)
+        body(i, j, k);
+}
diff --git a/flatten-verify-tmp/03_depth2_partial.cpp 
b/flatten-verify-tmp/03_depth2_partial.cpp
new file mode 100644
index 0000000000000..35b9dc83834fd
--- /dev/null
+++ b/flatten-verify-tmp/03_depth2_partial.cpp
@@ -0,0 +1,10 @@
+// depth(2) on a three-deep nest: only the outer two loops are combined; the
+// innermost loop must remain as ordinary body of the flattened loop.
+extern "C" void body(int, int, int);
+void t(int n, int m, int p) {
+#pragma omp flatten depth(2)
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      for (int k = 0; k < p; ++k)
+        body(i, j, k);
+}
diff --git a/flatten-verify-tmp/04_depth1_identity.cpp 
b/flatten-verify-tmp/04_depth1_identity.cpp
new file mode 100644
index 0000000000000..ae62a11312fe8
--- /dev/null
+++ b/flatten-verify-tmp/04_depth1_identity.cpp
@@ -0,0 +1,8 @@
+// depth(1): identity transform. A single loop is "combined"; no mixed-radix
+// decomposition (no srem), the flattened counter maps straight to the loop 
var.
+extern "C" void body(int);
+void t(int n) {
+#pragma omp flatten depth(1)
+  for (int i = 0; i < n; ++i)
+    body(i);
+}
diff --git a/flatten-verify-tmp/05_rangefor_template.cpp 
b/flatten-verify-tmp/05_rangefor_template.cpp
new file mode 100644
index 0000000000000..89928c34a8a15
--- /dev/null
+++ b/flatten-verify-tmp/05_rangefor_template.cpp
@@ -0,0 +1,20 @@
+// Range-for (CXXForRangeStmt) nest + a function template instantiated with a
+// dependent depth(K). Exercises the __begin/__end pre-inits and the deferred
+// (dependent-context) desugaring path.
+#include <stdlibc++.h>
+extern "C" void body(int, int);
+
+void ranges(std::vector<int> &a, std::vector<int> &b) {
+#pragma omp flatten
+  for (int i : a)
+    for (int j : b)
+      body(i, j);
+}
+
+template <int K> void tmpl(int n, int m) {
+#pragma omp flatten depth(K)
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      body(i, j);
+}
+template void tmpl<2>(int, int);
diff --git a/flatten-verify-tmp/README.md b/flatten-verify-tmp/README.md
new file mode 100644
index 0000000000000..f48f4d06cfec0
--- /dev/null
+++ b/flatten-verify-tmp/README.md
@@ -0,0 +1,39 @@
+# Temporary flatten + depth verification cases
+
+Throwaway sandbox for eyeballing the flatten desugaring (AST / IR / runtime).
+Not part of any PR — delete freely. The committed regression tests live under
+`clang/test/OpenMP/flatten*` and `openmp/runtime/test/transform/flatten/*`.
+
+`CLANG=build/bin/clang`, `CLANGXX=build/bin/clang++`, run from the repo root.
+
+| File | Case | What to look for |
+|------|------|------------------|
+| `01_default_depth2.cpp` | default (no clause) = depth 2 | i64 `.flatten.iv`, 
`mul nsw`, `sdiv`+`srem` |
+| `02_depth3_full.cpp` | `depth(3)` full flatten | 3 `.flatten.iv.N`, 
two-level mixed radix |
+| `03_depth2_partial.cpp` | `depth(2)` on 3-deep nest | 2 flatten IVs, **inner 
`%k` loop stays** |
+| `04_depth1_identity.cpp` | `depth(1)` identity | i32 IV, **no `srem`**, 
direct store |
+| `05_rangefor_template.cpp` | range-for + template `depth(K)` | ast-print 
round-trips; instantiation |
+| `end_result_check.cpp` | runtime semantic check | prints `OK`, exit 0 
(order+set preserved) |
+
+## Commands
+
+AST (round-trip original loops) and the desugared node:
+```
+$CLANG -cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=61 
-ast-print FILE
+$CLANG -cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=61 -ast-dump 
 FILE
+```
+
+IR (the div/mod arithmetic that implements the transform):
+```
+$CLANG -cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=61 
-emit-llvm -o - FILE
+```
+
+Range-for / template need the driver (libstdc++ + template instantiation):
+```
+$CLANGXX -fopenmp -fopenmp-version=61 -Xclang -ast-print -fsyntax-only 
05_rangefor_template.cpp
+```
+
+Runtime end-result (must print OK, exit 0):
+```
+$CLANGXX -fopenmp -fopenmp-version=61 -O0 end_result_check.cpp -o /tmp/erc && 
/tmp/erc
+```
diff --git a/flatten-verify-tmp/end_result_check.cpp 
b/flatten-verify-tmp/end_result_check.cpp
new file mode 100644
index 0000000000000..3aa8850829058
--- /dev/null
+++ b/flatten-verify-tmp/end_result_check.cpp
@@ -0,0 +1,82 @@
+// End-result (semantic) verification: flatten must preserve BOTH the set of
+// iterations and their row-major visitation ORDER. We record the sequence of
+// visited tuples from a flattened nest and compare it against the sequence 
from
+// an identical non-flattened reference nest. Exit code 0 == identical.
+//
+// flatten is a pure frontend AST desugaring to an ordinary loop, so this runs
+// as a normal program (build with: clang++ -fopenmp -fopenmp-version=61).
+#include <cassert>
+#include <cstdio>
+#include <vector>
+
+using Seq = std::vector<long>;
+
+static void rec(Seq &s, int i, int j, int k = -1) {
+  s.push_back(i);
+  s.push_back(j);
+  if (k >= 0)
+    s.push_back(k);
+}
+
+int main() {
+  const int n = 3, m = 4, p = 2;
+
+  // --- depth(2) default over 2 loops ---
+  {
+    Seq flat, ref;
+#pragma omp flatten
+    for (int i = 0; i < n; ++i)
+      for (int j = 0; j < m; ++j)
+        rec(flat, i, j);
+    for (int i = 0; i < n; ++i)
+      for (int j = 0; j < m; ++j)
+        rec(ref, i, j);
+    assert(flat == ref && "depth(2) order/set mismatch");
+  }
+
+  // --- depth(3) full flatten over 3 loops ---
+  {
+    Seq flat, ref;
+#pragma omp flatten depth(3)
+    for (int i = 0; i < n; ++i)
+      for (int j = 0; j < m; ++j)
+        for (int k = 0; k < p; ++k)
+          rec(flat, i, j, k);
+    for (int i = 0; i < n; ++i)
+      for (int j = 0; j < m; ++j)
+        for (int k = 0; k < p; ++k)
+          rec(ref, i, j, k);
+    assert(flat == ref && "depth(3) order/set mismatch");
+  }
+
+  // --- depth(2) partial: inner k-loop stays intact ---
+  {
+    Seq flat, ref;
+#pragma omp flatten depth(2)
+    for (int i = 0; i < n; ++i)
+      for (int j = 0; j < m; ++j)
+        for (int k = 0; k < p; ++k)
+          rec(flat, i, j, k);
+    for (int i = 0; i < n; ++i)
+      for (int j = 0; j < m; ++j)
+        for (int k = 0; k < p; ++k)
+          rec(ref, i, j, k);
+    assert(flat == ref && "depth(2)-partial order/set mismatch");
+  }
+
+  // --- non-unit step / non-zero start must survive ---
+  {
+    Seq flat, ref;
+#pragma omp flatten
+    for (int i = 5; i < 12; i += 2)
+      for (int j = 1; j < 7; j += 3)
+        rec(flat, i, j);
+    for (int i = 5; i < 12; i += 2)
+      for (int j = 1; j < 7; j += 3)
+        rec(ref, i, j);
+    assert(flat == ref && "stride/offset mismatch");
+  }
+
+  std::puts("flatten end-result preserved: OK");
+  return 0;
+}
diff --git a/openmp/runtime/test/transform/flatten/depth-partial.c 
b/openmp/runtime/test/transform/flatten/depth-partial.c
new file mode 100644
index 0000000000000..2fda3a04cbd84
--- /dev/null
+++ b/openmp/runtime/test/transform/flatten/depth-partial.c
@@ -0,0 +1,45 @@
+// RUN: %libomp-compile-and-run | FileCheck %s --match-full-lines
+
+// A depth(2) clause on a three-deep loop nest flattens only the outermost two
+// loops; the innermost loop is left untouched and executes normally inside the
+// flattened loop body.
+
+#ifndef HEADER
+#define HEADER
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int main() {
+  printf("do\n");
+#pragma omp flatten depth(2)
+  for (int i = 5; i < 11; i += 2)
+    for (int j = 1; j < 7; j += 2)
+      for (int k = 0; k < 2; ++k)
+        printf("i=%d j=%d k=%d\n", i, j, k);
+  printf("done\n");
+  return EXIT_SUCCESS;
+}
+
+#endif /* HEADER */
+
+// CHECK:      do
+// CHECK-NEXT: i=5 j=1 k=0
+// CHECK-NEXT: i=5 j=1 k=1
+// CHECK-NEXT: i=5 j=3 k=0
+// CHECK-NEXT: i=5 j=3 k=1
+// CHECK-NEXT: i=5 j=5 k=0
+// CHECK-NEXT: i=5 j=5 k=1
+// CHECK-NEXT: i=7 j=1 k=0
+// CHECK-NEXT: i=7 j=1 k=1
+// CHECK-NEXT: i=7 j=3 k=0
+// CHECK-NEXT: i=7 j=3 k=1
+// CHECK-NEXT: i=7 j=5 k=0
+// CHECK-NEXT: i=7 j=5 k=1
+// CHECK-NEXT: i=9 j=1 k=0
+// CHECK-NEXT: i=9 j=1 k=1
+// CHECK-NEXT: i=9 j=3 k=0
+// CHECK-NEXT: i=9 j=3 k=1
+// CHECK-NEXT: i=9 j=5 k=0
+// CHECK-NEXT: i=9 j=5 k=1
+// CHECK-NEXT: done
diff --git a/openmp/runtime/test/transform/flatten/empty.c 
b/openmp/runtime/test/transform/flatten/empty.c
new file mode 100644
index 0000000000000..ecf1e8ef622f2
--- /dev/null
+++ b/openmp/runtime/test/transform/flatten/empty.c
@@ -0,0 +1,42 @@
+// RUN: %libomp-compile-and-run | FileCheck %s --match-full-lines
+
+#ifndef HEADER
+#define HEADER
+
+#include <stdlib.h>
+#include <stdio.h>
+
+// The bounds are runtime values so that an empty iteration space does not
+// trigger a compile-time division-by-zero diagnostic for the (never executed)
+// induction-variable recovery.
+static void flatten(int n, int m) {
+#pragma omp flatten
+  for (int i = 0; i < n; ++i)
+    for (int j = 0; j < m; ++j)
+      printf("i=%d j=%d\n", i, j);
+}
+
+int main() {
+  printf("empty-inner-begin\n");
+  flatten(3, 0);
+  printf("empty-inner-end\n");
+
+  printf("empty-outer-begin\n");
+  flatten(0, 3);
+  printf("empty-outer-end\n");
+
+  printf("single-begin\n");
+  flatten(1, 1);
+  printf("single-end\n");
+  return EXIT_SUCCESS;
+}
+
+#endif /* HEADER */
+
+// CHECK:      empty-inner-begin
+// CHECK-NEXT: empty-inner-end
+// CHECK-NEXT: empty-outer-begin
+// CHECK-NEXT: empty-outer-end
+// CHECK-NEXT: single-begin
+// CHECK-NEXT: i=0 j=0
+// CHECK-NEXT: single-end
diff --git a/openmp/runtime/test/transform/flatten/foreach.cpp 
b/openmp/runtime/test/transform/flatten/foreach.cpp
new file mode 100644
index 0000000000000..14997f7c4051d
--- /dev/null
+++ b/openmp/runtime/test/transform/flatten/foreach.cpp
@@ -0,0 +1,31 @@
+// RUN: %libomp-cxx-compile-and-run | FileCheck %s --match-full-lines
+
+#ifndef HEADER
+#define HEADER
+
+#include <cstdlib>
+#include <cstdio>
+#include <vector>
+
+int main() {
+  printf("do\n");
+  std::vector<int> outer{10, 20};
+  std::vector<int> inner{1, 2, 3};
+#pragma omp flatten
+  for (int a : outer)
+    for (int b : inner)
+      printf("a=%d b=%d\n", a, b);
+  printf("done\n");
+  return EXIT_SUCCESS;
+}
+
+#endif /* HEADER */
+
+// CHECK:      do
+// CHECK-NEXT: a=10 b=1
+// CHECK-NEXT: a=10 b=2
+// CHECK-NEXT: a=10 b=3
+// CHECK-NEXT: a=20 b=1
+// CHECK-NEXT: a=20 b=2
+// CHECK-NEXT: a=20 b=3
+// CHECK-NEXT: done
diff --git a/openmp/runtime/test/transform/flatten/parallel-wsloop-foreach.cpp 
b/openmp/runtime/test/transform/flatten/parallel-wsloop-foreach.cpp
new file mode 100644
index 0000000000000..7ddcde2fd622c
--- /dev/null
+++ b/openmp/runtime/test/transform/flatten/parallel-wsloop-foreach.cpp
@@ -0,0 +1,32 @@
+// RUN: %libomp-cxx-compile-and-run | FileCheck %s --match-full-lines
+
+#ifndef HEADER
+#define HEADER
+
+#include <cstdlib>
+#include <cstdio>
+
+int main() {
+  printf("do\n");
+  // The loop generated by 'flatten' is the canonical loop associated with the
+  // worksharing 'omp for'. With a single thread the iteration order is the
+  // original row-major order.
+#pragma omp parallel for num_threads(1)
+#pragma omp flatten
+  for (int i = 0; i < 2; ++i)
+    for (int j = 0; j < 3; ++j)
+      printf("i=%d j=%d\n", i, j);
+  printf("done\n");
+  return EXIT_SUCCESS;
+}
+
+#endif /* HEADER */
+
+// CHECK:      do
+// CHECK-NEXT: i=0 j=0
+// CHECK-NEXT: i=0 j=1
+// CHECK-NEXT: i=0 j=2
+// CHECK-NEXT: i=1 j=0
+// CHECK-NEXT: i=1 j=1
+// CHECK-NEXT: i=1 j=2
+// CHECK-NEXT: done

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to