https://github.com/amitamd7 updated 
https://github.com/llvm/llvm-project/pull/183261

>From 5ee7cb284ad1e9cf98190cea04fc428b9e31ccf6 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Wed, 25 Feb 2026 03:48:23 -0500
Subject: [PATCH 01/15] Split node creation and registration

---
 clang/include/clang/AST/StmtOpenMP.h          | 74 +++++++++++++++++++
 clang/include/clang/Basic/StmtNodes.td        |  1 +
 .../include/clang/Serialization/ASTBitCodes.h |  1 +
 clang/lib/AST/StmtOpenMP.cpp                  | 22 ++++++
 llvm/include/llvm/Frontend/OpenMP/OMP.td      |  5 ++
 5 files changed, 103 insertions(+)

diff --git a/clang/include/clang/AST/StmtOpenMP.h 
b/clang/include/clang/AST/StmtOpenMP.h
index bc6aeaa8d143c..626c39a7b778c 100644
--- a/clang/include/clang/AST/StmtOpenMP.h
+++ b/clang/include/clang/AST/StmtOpenMP.h
@@ -6065,6 +6065,80 @@ class OMPFuseDirective final
   }
 };
 
+/// Represents the '#pragma omp split' loop transformation directive.
+///
+/// \code{c}
+///   #pragma omp split
+///   for (int i = 0; i < n; ++i)
+///     ...
+/// \endcode
+///
+/// This directive transforms a single loop into multiple loops based on
+/// index ranges. The transformation splits the iteration space of the loop
+/// into multiple contiguous ranges.
+class OMPSplitDirective final
+    : public OMPCanonicalLoopNestTransformationDirective {
+  friend class ASTStmtReader;
+  friend class OMPExecutableDirective;
+
+  /// Offsets of child members.
+  enum {
+    PreInitsOffset = 0,
+    TransformedStmtOffset,
+  };
+
+  explicit OMPSplitDirective(SourceLocation StartLoc, SourceLocation EndLoc,
+                            unsigned NumLoops)
+      : OMPCanonicalLoopNestTransformationDirective(
+            OMPSplitDirectiveClass, llvm::omp::OMPD_split, 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 split'.
+  ///
+  /// \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 NumLoops  Number of affected loops (should be 1 for split).
+  /// \param AssociatedStmt  The outermost associated loop.
+  /// \param TransformedStmt The loop nest after splitting, or nullptr in
+  ///                        dependent contexts.
+  /// \param PreInits   Helper preinits statements for the loop nest.
+  static OMPSplitDirective *Create(const ASTContext &C,
+                                   SourceLocation StartLoc,
+                                   SourceLocation EndLoc,
+                                   Stmt *AssociatedStmt, unsigned NumLoops,
+                                   Stmt *TransformedStmt, Stmt *PreInits);
+
+  /// Build an empty '#pragma omp split' AST node for deserialization.
+  ///
+  /// \param C          Context of the AST.
+  /// \param NumLoops   Number of associated loops to allocate
+  static OMPSplitDirective *CreateEmpty(const ASTContext &C,
+                                        unsigned NumLoops);
+
+  /// Gets/sets the associated loops after the transformation, i.e. after
+  /// de-sugaring.
+  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() == OMPSplitDirectiveClass;
+  }
+};
+
 /// This represents '#pragma omp scan' directive.
 ///
 /// \code
diff --git a/clang/include/clang/Basic/StmtNodes.td 
b/clang/include/clang/Basic/StmtNodes.td
index cb869cc210627..77db0e6d73ca3 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -242,6 +242,7 @@ def OMPTileDirective : 
StmtNode<OMPCanonicalLoopNestTransformationDirective>;
 def OMPStripeDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>;
 def OMPUnrollDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>;
 def OMPReverseDirective : 
StmtNode<OMPCanonicalLoopNestTransformationDirective>;
+def OMPSplitDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>;
 def OMPInterchangeDirective
     : StmtNode<OMPCanonicalLoopNestTransformationDirective>;
 def OMPCanonicalLoopSequenceTransformationDirective
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h 
b/clang/include/clang/Serialization/ASTBitCodes.h
index d72f1f9db86b2..1786277598f8e 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1959,6 +1959,7 @@ enum StmtCode {
   STMP_OMP_STRIPE_DIRECTIVE,
   STMT_OMP_UNROLL_DIRECTIVE,
   STMT_OMP_REVERSE_DIRECTIVE,
+  STMT_OMP_SPLIT_DIRECTIVE,
   STMT_OMP_INTERCHANGE_DIRECTIVE,
   STMT_OMP_FUSE_DIRECTIVE,
   STMT_OMP_FOR_DIRECTIVE,
diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp
index a5b0cd3786a28..ada4e66b280f8 100644
--- a/clang/lib/AST/StmtOpenMP.cpp
+++ b/clang/lib/AST/StmtOpenMP.cpp
@@ -552,6 +552,28 @@ OMPInterchangeDirective::CreateEmpty(const ASTContext &C, 
unsigned NumClauses,
       SourceLocation(), SourceLocation(), NumLoops);
 }
 
+OMPSplitDirective *OMPSplitDirective::Create(const ASTContext &C,
+                                             SourceLocation StartLoc,
+                                             SourceLocation EndLoc,
+                                             Stmt *AssociatedStmt,
+                                             unsigned NumLoops,
+                                             Stmt *TransformedStmt,
+                                             Stmt *PreInits) {
+  OMPSplitDirective *Dir = createDirective<OMPSplitDirective>(
+      C, {}, AssociatedStmt, TransformedStmtOffset + 1, StartLoc, EndLoc,
+      NumLoops);
+  Dir->setTransformedStmt(TransformedStmt);
+  Dir->setPreInits(PreInits);
+  return Dir;
+}
+
+OMPSplitDirective *OMPSplitDirective::CreateEmpty(const ASTContext &C,
+                                                   unsigned NumLoops) {
+  return createEmptyDirective<OMPSplitDirective>(
+      C, /*NumClauses=*/0, /*HasAssociatedStmt=*/true,
+      TransformedStmtOffset + 1, SourceLocation(), SourceLocation(), NumLoops);
+}
+
 OMPFuseDirective *OMPFuseDirective::Create(
     const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
     ArrayRef<OMPClause *> Clauses, unsigned NumGeneratedTopLevelLoops,
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td 
b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 865cad7769554..38eedd10cfdd7 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -1414,6 +1414,11 @@ def OMP_Stripe : Directive<[Spelling<"stripe">]> {
   let association = AS_LoopNest;
   let category = CA_Executable;
 }
+def OMP_Split : Directive<[Spelling<"split">]> {
+  // TODO: Add counts clause support (OMPC_Counts)
+  let association = AS_LoopNest;
+  let category = CA_Executable;
+}
 def OMP_Unknown : Directive<[Spelling<"unknown">]> {
   let isDefault = true;
   let association = AS_None;

>From 0a57b91396898f01dffa1aa55da4cc3ac8305d54 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Wed, 25 Feb 2026 06:51:02 -0500
Subject: [PATCH 02/15] wip

---
 clang/include/clang/AST/RecursiveASTVisitor.h |  3 +++
 clang/include/clang/AST/StmtOpenMP.h          | 14 ++++++--------
 clang/lib/AST/StmtOpenMP.cpp                  | 14 ++++++--------
 3 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h 
b/clang/include/clang/AST/RecursiveASTVisitor.h
index f97b54276cbee..323f570439724 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -3194,6 +3194,9 @@ DEF_TRAVERSE_STMT(OMPFuseDirective,
 DEF_TRAVERSE_STMT(OMPInterchangeDirective,
                   { TRY_TO(TraverseOMPExecutableDirective(S)); })
 
+DEF_TRAVERSE_STMT(OMPSplitDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
 DEF_TRAVERSE_STMT(OMPForDirective,
                   { TRY_TO(TraverseOMPExecutableDirective(S)); })
 
diff --git a/clang/include/clang/AST/StmtOpenMP.h 
b/clang/include/clang/AST/StmtOpenMP.h
index 626c39a7b778c..c5b83e17acbcd 100644
--- a/clang/include/clang/AST/StmtOpenMP.h
+++ b/clang/include/clang/AST/StmtOpenMP.h
@@ -6088,7 +6088,7 @@ class OMPSplitDirective final
   };
 
   explicit OMPSplitDirective(SourceLocation StartLoc, SourceLocation EndLoc,
-                            unsigned NumLoops)
+                             unsigned NumLoops)
       : OMPCanonicalLoopNestTransformationDirective(
             OMPSplitDirectiveClass, llvm::omp::OMPD_split, StartLoc, EndLoc,
             NumLoops) {}
@@ -6112,18 +6112,16 @@ class OMPSplitDirective final
   /// \param TransformedStmt The loop nest after splitting, or nullptr in
   ///                        dependent contexts.
   /// \param PreInits   Helper preinits statements for the loop nest.
-  static OMPSplitDirective *Create(const ASTContext &C,
-                                   SourceLocation StartLoc,
-                                   SourceLocation EndLoc,
-                                   Stmt *AssociatedStmt, unsigned NumLoops,
-                                   Stmt *TransformedStmt, Stmt *PreInits);
+  static OMPSplitDirective *Create(const ASTContext &C, SourceLocation 
StartLoc,
+                                   SourceLocation EndLoc, Stmt *AssociatedStmt,
+                                   unsigned NumLoops, Stmt *TransformedStmt,
+                                   Stmt *PreInits);
 
   /// Build an empty '#pragma omp split' AST node for deserialization.
   ///
   /// \param C          Context of the AST.
   /// \param NumLoops   Number of associated loops to allocate
-  static OMPSplitDirective *CreateEmpty(const ASTContext &C,
-                                        unsigned NumLoops);
+  static OMPSplitDirective *CreateEmpty(const ASTContext &C, unsigned 
NumLoops);
 
   /// Gets/sets the associated loops after the transformation, i.e. after
   /// de-sugaring.
diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp
index ada4e66b280f8..6c939cf7f9aeb 100644
--- a/clang/lib/AST/StmtOpenMP.cpp
+++ b/clang/lib/AST/StmtOpenMP.cpp
@@ -552,13 +552,11 @@ OMPInterchangeDirective::CreateEmpty(const ASTContext &C, 
unsigned NumClauses,
       SourceLocation(), SourceLocation(), NumLoops);
 }
 
-OMPSplitDirective *OMPSplitDirective::Create(const ASTContext &C,
-                                             SourceLocation StartLoc,
-                                             SourceLocation EndLoc,
-                                             Stmt *AssociatedStmt,
-                                             unsigned NumLoops,
-                                             Stmt *TransformedStmt,
-                                             Stmt *PreInits) {
+OMPSplitDirective *
+OMPSplitDirective::Create(const ASTContext &C, SourceLocation StartLoc,
+                          SourceLocation EndLoc, Stmt *AssociatedStmt,
+                          unsigned NumLoops, Stmt *TransformedStmt,
+                          Stmt *PreInits) {
   OMPSplitDirective *Dir = createDirective<OMPSplitDirective>(
       C, {}, AssociatedStmt, TransformedStmtOffset + 1, StartLoc, EndLoc,
       NumLoops);
@@ -568,7 +566,7 @@ OMPSplitDirective *OMPSplitDirective::Create(const 
ASTContext &C,
 }
 
 OMPSplitDirective *OMPSplitDirective::CreateEmpty(const ASTContext &C,
-                                                   unsigned NumLoops) {
+                                                  unsigned NumLoops) {
   return createEmptyDirective<OMPSplitDirective>(
       C, /*NumClauses=*/0, /*HasAssociatedStmt=*/true,
       TransformedStmtOffset + 1, SourceLocation(), SourceLocation(), NumLoops);

>From 02844251d20249820021a9e6fc58e888ebf66d3c Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Fri, 6 Mar 2026 06:29:06 -0500
Subject: [PATCH 03/15] wip

---
 clang/lib/AST/StmtPrinter.cpp             |  5 +++++
 clang/lib/AST/StmtProfile.cpp             |  4 ++++
 clang/lib/Sema/TreeTransform.h            | 11 +++++++++++
 clang/lib/Serialization/ASTReaderStmt.cpp |  4 ++++
 clang/lib/Serialization/ASTWriterStmt.cpp |  5 +++++
 5 files changed, 29 insertions(+)

diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index f4ce4a7573aab..143ff0580a205 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::VisitOMPSplitDirective(OMPSplitDirective *Node) {
+  Indent() << "#pragma omp split";
+  PrintOMPExecutableDirective(Node);
+}
+
 void StmtPrinter::VisitOMPFuseDirective(OMPFuseDirective *Node) {
   Indent() << "#pragma omp fuse";
   PrintOMPExecutableDirective(Node);
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 623905188b2dd..375b2207d4dd4 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -1051,6 +1051,10 @@ void StmtProfiler::VisitOMPInterchangeDirective(
   VisitOMPCanonicalLoopNestTransformationDirective(S);
 }
 
+void StmtProfiler::VisitOMPSplitDirective(const OMPSplitDirective *S) {
+  VisitOMPCanonicalLoopNestTransformationDirective(S);
+}
+
 void StmtProfiler::VisitOMPCanonicalLoopSequenceTransformationDirective(
     const OMPCanonicalLoopSequenceTransformationDirective *S) {
   VisitOMPExecutableDirective(S);
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 1a050bd6a8737..8f67e71382b1d 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9761,6 +9761,17 @@ StmtResult 
TreeTransform<Derived>::TransformOMPInterchangeDirective(
   return Res;
 }
 
+template <typename Derived>
+StmtResult
+TreeTransform<Derived>::TransformOMPSplitDirective(OMPSplitDirective *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) {
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp 
b/clang/lib/Serialization/ASTReaderStmt.cpp
index a18fccb6518d2..c338e9bfbf22c 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2510,6 +2510,10 @@ void 
ASTStmtReader::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) {
   VisitOMPCanonicalLoopNestTransformationDirective(D);
 }
 
+void ASTStmtReader::VisitOMPSplitDirective(OMPSplitDirective *D) {
+  VisitOMPCanonicalLoopNestTransformationDirective(D);
+}
+
 void ASTStmtReader::VisitOMPFuseDirective(OMPFuseDirective *D) {
   VisitOMPCanonicalLoopSequenceTransformationDirective(D);
 }
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp 
b/clang/lib/Serialization/ASTWriterStmt.cpp
index 4fcac4d0261ab..f3531d3d91837 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2522,6 +2522,11 @@ void 
ASTStmtWriter::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) {
   Code = serialization::STMT_OMP_INTERCHANGE_DIRECTIVE;
 }
 
+void ASTStmtWriter::VisitOMPSplitDirective(OMPSplitDirective *D) {
+  VisitOMPCanonicalLoopNestTransformationDirective(D);
+  Code = serialization::STMT_OMP_SPLIT_DIRECTIVE;
+}
+
 void ASTStmtWriter::VisitOMPCanonicalLoopSequenceTransformationDirective(
     OMPCanonicalLoopSequenceTransformationDirective *D) {
   VisitStmt(D);

>From 991ac94e4d2376e5c509aea207c69a0d36de9ac7 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Mon, 9 Mar 2026 07:42:22 -0400
Subject: [PATCH 04/15] wip

---
 clang/lib/CodeGen/CGStmt.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 0658ecc93d88d..f41ea386487f7 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -228,6 +228,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S, 
ArrayRef<const Attr *> Attrs) {
   case Stmt::OMPReverseDirectiveClass:
     EmitOMPReverseDirective(cast<OMPReverseDirective>(*S));
     break;
+  case Stmt::OMPSplitDirectiveClass:
+    EmitOMPSplitDirective(cast<OMPSplitDirective>(*S));
+    break;
   case Stmt::OMPInterchangeDirectiveClass:
     EmitOMPInterchangeDirective(cast<OMPInterchangeDirective>(*S));
     break;

>From dcc5f3f7ec2314b81e8618c566c7e6456e2f32ca Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Mon, 9 Mar 2026 08:16:47 -0400
Subject: [PATCH 05/15] wip

---
 clang/lib/Sema/SemaExceptionSpec.cpp         | 1 +
 clang/lib/Serialization/ASTReaderStmt.cpp    | 8 ++++++++
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 +
 clang/tools/libclang/CXCursor.cpp            | 3 +++
 4 files changed, 13 insertions(+)

diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp 
b/clang/lib/Sema/SemaExceptionSpec.cpp
index 8df01a8a616c3..4fd800e5e25c4 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1496,6 +1496,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Stmt::OMPUnrollDirectiveClass:
   case Stmt::OMPReverseDirectiveClass:
   case Stmt::OMPInterchangeDirectiveClass:
+  case Stmt::OMPSplitDirectiveClass:
   case Stmt::OMPFuseDirectiveClass:
   case Stmt::OMPSingleDirectiveClass:
   case Stmt::OMPTargetDataDirectiveClass:
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp 
b/clang/lib/Serialization/ASTReaderStmt.cpp
index c338e9bfbf22c..be6e7e0ea90bb 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -3668,6 +3668,14 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       break;
     }
 
+    case STMT_OMP_SPLIT_DIRECTIVE: {
+      unsigned NumLoops = Record[ASTStmtReader::NumStmtFields];
+      assert(Record[ASTStmtReader::NumStmtFields + 1] == 0 &&
+             "Split directive has no clauses");
+      S = OMPSplitDirective::CreateEmpty(Context, NumLoops);
+      break;
+    }
+
     case STMT_OMP_FUSE_DIRECTIVE: {
       unsigned NumClauses = Record[ASTStmtReader::NumStmtFields];
       S = OMPFuseDirective::CreateEmpty(Context, NumClauses);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 76c382eeb6899..6b5f94a7d2ea2 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1822,6 +1822,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::OMPStripeDirectiveClass:
     case Stmt::OMPTileDirectiveClass:
     case Stmt::OMPInterchangeDirectiveClass:
+    case Stmt::OMPSplitDirectiveClass:
     case Stmt::OMPFuseDirectiveClass:
     case Stmt::OMPInteropDirectiveClass:
     case Stmt::OMPDispatchDirectiveClass:
diff --git a/clang/tools/libclang/CXCursor.cpp 
b/clang/tools/libclang/CXCursor.cpp
index 17f485e5c78a5..d8247f8161506 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -696,6 +696,9 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl 
*Parent,
   case Stmt::OMPReverseDirectiveClass:
     K = CXCursor_OMPReverseDirective;
     break;
+  case Stmt::OMPSplitDirectiveClass:
+    K = CXCursor_UnexposedStmt;
+    break;
   case Stmt::OMPInterchangeDirectiveClass:
     K = CXCursor_OMPInterchangeDirective;
     break;

>From b087df1f9ee96d5a91b169018ef7cbc007b473bb Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Mon, 9 Mar 2026 12:13:02 -0400
Subject: [PATCH 06/15] wip

---
 clang/lib/CodeGen/CGStmt.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index f41ea386487f7..5d906c378f1bf 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -229,7 +229,8 @@ void CodeGenFunction::EmitStmt(const Stmt *S, 
ArrayRef<const Attr *> Attrs) {
     EmitOMPReverseDirective(cast<OMPReverseDirective>(*S));
     break;
   case Stmt::OMPSplitDirectiveClass:
-    EmitOMPSplitDirective(cast<OMPSplitDirective>(*S));
+    llvm_unreachable(
+        "OMPSplitDirective handled by EmitSimpleOMPExecutableDirective");
     break;
   case Stmt::OMPInterchangeDirectiveClass:
     EmitOMPInterchangeDirective(cast<OMPInterchangeDirective>(*S));

>From 3b9abedcc0fbcda363ca51ba7dd3fe22ecc1c951 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Fri, 13 Mar 2026 09:48:29 -0400
Subject: [PATCH 07/15] sema

---
 clang/include/clang-c/Index.h         |   4 +
 clang/include/clang/Sema/SemaOpenMP.h |   4 +
 clang/lib/Basic/OpenMPKinds.cpp       |   3 +-
 clang/lib/CodeGen/CGStmt.cpp          |   3 +-
 clang/lib/CodeGen/CGStmtOpenMP.cpp    |   8 +
 clang/lib/CodeGen/CodeGenFunction.h   |   1 +
 clang/lib/Parse/ParseOpenMP.cpp       |   4 +
 clang/lib/Sema/SemaOpenMP.cpp         | 205 ++++++++++++++++++++++++++
 clang/test/OpenMP/split_ast_print.cpp |  28 ++++
 clang/test/OpenMP/split_simple_test.c |  26 ++++
 clang/tools/libclang/CIndex.cpp       |   2 +
 clang/tools/libclang/CXCursor.cpp     |   2 +-
 12 files changed, 286 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/OpenMP/split_ast_print.cpp
 create mode 100644 clang/test/OpenMP/split_simple_test.c

diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 203634c80d82a..e9dd734445a74 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -2166,6 +2166,10 @@ enum CXCursorKind {
    */
   CXCursor_OMPFuseDirective = 311,
 
+  /** OpenMP split directive.
+   */
+  CXCursor_OMPSplitDirective = 312,
+
   /** OpenACC Compute Construct.
    */
   CXCursor_OpenACCComputeConstruct = 320,
diff --git a/clang/include/clang/Sema/SemaOpenMP.h 
b/clang/include/clang/Sema/SemaOpenMP.h
index 7853f29f98c25..57382557fd13f 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -457,6 +457,10 @@ class SemaOpenMP : public SemaBase {
   /// Called on well-formed '#pragma omp reverse'.
   StmtResult ActOnOpenMPReverseDirective(Stmt *AStmt, SourceLocation StartLoc,
                                          SourceLocation EndLoc);
+  /// Called on well-formed '#pragma omp split' after parsing of its
+  /// associated statement.
+  StmtResult ActOnOpenMPSplitDirective(Stmt *AStmt, SourceLocation StartLoc,
+                                       SourceLocation EndLoc);
   /// Called on well-formed '#pragma omp interchange' after parsing of its
   /// clauses and the associated statement.
   StmtResult ActOnOpenMPInterchangeDirective(ArrayRef<OMPClause *> Clauses,
diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp
index 2c693b1958ee7..ef01943f11ca5 100644
--- a/clang/lib/Basic/OpenMPKinds.cpp
+++ b/clang/lib/Basic/OpenMPKinds.cpp
@@ -815,7 +815,8 @@ bool 
clang::isOpenMPLoopBoundSharingDirective(OpenMPDirectiveKind Kind) {
 bool clang::isOpenMPCanonicalLoopNestTransformationDirective(
     OpenMPDirectiveKind DKind) {
   return DKind == OMPD_tile || DKind == OMPD_unroll || DKind == OMPD_reverse ||
-         DKind == OMPD_interchange || DKind == OMPD_stripe;
+         DKind == OMPD_split || DKind == OMPD_interchange ||
+         DKind == OMPD_stripe;
 }
 
 bool clang::isOpenMPCanonicalLoopSequenceTransformationDirective(
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 5d906c378f1bf..f41ea386487f7 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -229,8 +229,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S, 
ArrayRef<const Attr *> Attrs) {
     EmitOMPReverseDirective(cast<OMPReverseDirective>(*S));
     break;
   case Stmt::OMPSplitDirectiveClass:
-    llvm_unreachable(
-        "OMPSplitDirective handled by EmitSimpleOMPExecutableDirective");
+    EmitOMPSplitDirective(cast<OMPSplitDirective>(*S));
     break;
   case Stmt::OMPInterchangeDirectiveClass:
     EmitOMPInterchangeDirective(cast<OMPInterchangeDirective>(*S));
diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp 
b/clang/lib/CodeGen/CGStmtOpenMP.cpp
index ea89db5471089..9b7890347682d 100644
--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -196,6 +196,8 @@ class OMPLoopScope : public 
CodeGenFunction::RunCleanupsScope {
       PreInits = Unroll->getPreInits();
     } else if (const auto *Reverse = dyn_cast<OMPReverseDirective>(&S)) {
       PreInits = Reverse->getPreInits();
+    } else if (const auto *Split = dyn_cast<OMPSplitDirective>(&S)) {
+      PreInits = Split->getPreInits();
     } else if (const auto *Interchange =
                    dyn_cast<OMPInterchangeDirective>(&S)) {
       PreInits = Interchange->getPreInits();
@@ -2978,6 +2980,12 @@ void CodeGenFunction::EmitOMPReverseDirective(const 
OMPReverseDirective &S) {
   EmitStmt(S.getTransformedStmt());
 }
 
+void CodeGenFunction::EmitOMPSplitDirective(const OMPSplitDirective &S) {
+  // Emit the de-sugared statement (the two split loops).
+  OMPTransformDirectiveScopeRAII SplitScope(*this, &S);
+  EmitStmt(S.getTransformedStmt());
+}
+
 void CodeGenFunction::EmitOMPInterchangeDirective(
     const OMPInterchangeDirective &S) {
   // Emit the de-sugared statement.
diff --git a/clang/lib/CodeGen/CodeGenFunction.h 
b/clang/lib/CodeGen/CodeGenFunction.h
index ae2956eeac57a..d82a589ae6b7a 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3907,6 +3907,7 @@ class CodeGenFunction : public CodeGenTypeCache {
   void EmitOMPStripeDirective(const OMPStripeDirective &S);
   void EmitOMPUnrollDirective(const OMPUnrollDirective &S);
   void EmitOMPReverseDirective(const OMPReverseDirective &S);
+  void EmitOMPSplitDirective(const OMPSplitDirective &S);
   void EmitOMPInterchangeDirective(const OMPInterchangeDirective &S);
   void EmitOMPFuseDirective(const OMPFuseDirective &S);
   void EmitOMPForDirective(const OMPForDirective &S);
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index b41803d23cb25..75301f3985354 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -3414,6 +3414,10 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind 
DKind,
     }
     Clause = ParseOpenMPPermutationClause();
     break;
+  case OMPC_counts:
+    // TODO: Implement ParseOpenMPCountsClause() - not yet worked on
+    SkipUntil(tok::r_paren, tok::comma, StopBeforeMatch);
+    break;
   case OMPC_uses_allocators:
     Clause = ParseOpenMPUsesAllocatorClause(DKind);
     break;
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index e90884a89bf6e..8c22780b7ae4b 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -4591,6 +4591,7 @@ void 
SemaOpenMP::ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind,
   case OMPD_stripe:
   case OMPD_unroll:
   case OMPD_reverse:
+  case OMPD_split:
   case OMPD_interchange:
   case OMPD_fuse:
   case OMPD_assume:
@@ -6430,6 +6431,13 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
            "reverse directive does not support any clauses");
     Res = ActOnOpenMPReverseDirective(AStmt, StartLoc, EndLoc);
     break;
+  case OMPD_split:
+    // TODO: Add counts clause support - not yet worked on
+    // Currently only supports basic split without clauses.
+    assert(ClausesWithImplicit.empty() &&
+           "split directive does not support any clauses");
+    Res = ActOnOpenMPSplitDirective(AStmt, StartLoc, EndLoc);
+    break;
   case OMPD_interchange:
     Res = ActOnOpenMPInterchangeDirective(ClausesWithImplicit, AStmt, StartLoc,
                                           EndLoc);
@@ -15817,6 +15825,203 @@ StmtResult 
SemaOpenMP::ActOnOpenMPReverseDirective(Stmt *AStmt,
                                      buildPreInits(Context, PreInits));
 }
 
+StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(Stmt *AStmt,
+                                                 SourceLocation StartLoc,
+                                                 SourceLocation EndLoc) {
+  ASTContext &Context = getASTContext();
+  Scope *CurScope = SemaRef.getCurScope();
+
+  // Empty statement should only be possible if there already was an error.
+  if (!AStmt)
+    return StmtError();
+
+  constexpr unsigned NumLoops = 1;
+  Stmt *Body = nullptr;
+  SmallVector<OMPLoopBasedDirective::HelperExprs, NumLoops> LoopHelpers(
+      NumLoops);
+  SmallVector<SmallVector<Stmt *>, NumLoops + 1> OriginalInits;
+  if (!checkTransformableLoopNest(OMPD_split, AStmt, NumLoops, LoopHelpers,
+                                  Body, OriginalInits))
+    return StmtError();
+
+  // Delay applying the transformation to when template is completely
+  // instantiated.
+  if (SemaRef.CurContext->isDependentContext())
+    return OMPSplitDirective::Create(Context, StartLoc, EndLoc, AStmt, 
NumLoops,
+                                     nullptr, nullptr);
+
+  assert(LoopHelpers.size() == NumLoops &&
+         "Expecting a single-dimensional loop iteration space");
+  assert(OriginalInits.size() == NumLoops &&
+         "Expecting a single-dimensional loop iteration space");
+  OMPLoopBasedDirective::HelperExprs &LoopHelper = LoopHelpers.front();
+
+  // Find the loop statement.
+  Stmt *LoopStmt = nullptr;
+  collectLoopStmts(AStmt, {LoopStmt});
+
+  // Determine the PreInit declarations.
+  SmallVector<Stmt *> PreInits;
+  addLoopPreInits(Context, LoopHelper, LoopStmt, OriginalInits[0], PreInits);
+
+  auto *IterationVarRef = cast<DeclRefExpr>(LoopHelper.IterationVarRef);
+  QualType IVTy = IterationVarRef->getType();
+  uint64_t IVWidth = Context.getTypeSize(IVTy);
+  auto *OrigVar = cast<DeclRefExpr>(LoopHelper.Counters.front());
+
+  // Iteration variable SourceLocations.
+  SourceLocation OrigVarLoc = OrigVar->getExprLoc();
+  SourceLocation OrigVarLocBegin = OrigVar->getBeginLoc();
+  SourceLocation OrigVarLocEnd = OrigVar->getEndLoc();
+
+  // Locations pointing to the transformation.
+  SourceLocation TransformLoc = StartLoc;
+
+  // Internal variable names.
+  std::string OrigVarName = OrigVar->getNameInfo().getAsString();
+
+  // For Subexpressions with more than one use, we define a lambda
+  // that creates a new AST node at every use.
+  CaptureVars CopyTransformer(SemaRef);
+  auto MakeNumIterations = [&CopyTransformer, &LoopHelper]() -> Expr * {
+    return AssertSuccess(
+        CopyTransformer.TransformExpr(LoopHelper.NumIterations));
+  };
+
+  // For split, we currently divide the loop into two equal parts.
+  // First loop: i = 0; i < n/2; ++i
+  // Second loop: i = n/2; i < n; ++i
+  // TODO: Add counts clause support - not yet worked on
+
+  // Create iteration variable for the first split loop.
+  SmallString<64> FirstIVName(".split.first.iv.");
+  FirstIVName += OrigVarName;
+  VarDecl *FirstIVDecl =
+      buildVarDecl(SemaRef, {}, IVTy, FirstIVName, nullptr, OrigVar);
+  auto MakeFirstRef = [&SemaRef = this->SemaRef, FirstIVDecl, IVTy,
+                       OrigVarLoc]() {
+    return buildDeclRefExpr(SemaRef, FirstIVDecl, IVTy, OrigVarLoc);
+  };
+
+  // Create iteration variable for the second split loop.
+  SmallString<64> SecondIVName(".split.second.iv.");
+  SecondIVName += OrigVarName;
+  VarDecl *SecondIVDecl =
+      buildVarDecl(SemaRef, {}, IVTy, SecondIVName, nullptr, OrigVar);
+  auto MakeSecondRef = [&SemaRef = this->SemaRef, SecondIVDecl, IVTy,
+                        OrigVarLoc]() {
+    return buildDeclRefExpr(SemaRef, SecondIVDecl, IVTy, OrigVarLoc);
+  };
+
+  // Create n/2 expression for the split point.
+  auto *Two = IntegerLiteral::Create(Context, llvm::APInt(IVWidth, 2), IVTy,
+                                     TransformLoc);
+  ExprResult HalfIterations = SemaRef.BuildBinOp(CurScope, TransformLoc, 
BO_Div,
+                                                 MakeNumIterations(), Two);
+  if (!HalfIterations.isUsable())
+    return StmtError();
+
+  // First loop: init-statement: i = 0
+  auto *Zero = IntegerLiteral::Create(Context, llvm::APInt::getZero(IVWidth),
+                                      FirstIVDecl->getType(), OrigVarLoc);
+  SemaRef.AddInitializerToDecl(FirstIVDecl, Zero, /*DirectInit=*/false);
+  StmtResult FirstInit = new (Context)
+      DeclStmt(DeclGroupRef(FirstIVDecl), OrigVarLocBegin, OrigVarLocEnd);
+  if (!FirstInit.isUsable())
+    return StmtError();
+
+  // First loop: cond-expression (i < n/2)
+  ExprResult FirstCond =
+      SemaRef.BuildBinOp(CurScope, LoopHelper.Cond->getExprLoc(), BO_LT,
+                         MakeFirstRef(), HalfIterations.get());
+  if (!FirstCond.isUsable())
+    return StmtError();
+
+  // First loop: incr-statement (++i)
+  ExprResult FirstIncr = SemaRef.BuildUnaryOp(
+      CurScope, LoopHelper.Inc->getExprLoc(), UO_PreInc, MakeFirstRef());
+  if (!FirstIncr.isUsable())
+    return StmtError();
+
+  // First loop: body - update original variable and execute body
+  // We need to create a copy of LoopHelper.Updates that uses FirstIV instead
+  // of the iteration variable. For now, use a simpler approach: directly
+  // assign the first IV to the original variable.
+  SmallVector<Stmt *, 4> FirstBodyStmts;
+  // Create update statement: origVar = .split.first.iv
+  // We'll use a BinaryOperator for assignment
+  ExprResult FirstUpdateExpr = SemaRef.BuildBinOp(
+      CurScope, OrigVarLoc, BO_Assign, OrigVar, MakeFirstRef());
+  if (!FirstUpdateExpr.isUsable())
+    return StmtError();
+  FirstBodyStmts.push_back(FirstUpdateExpr.get());
+  if (auto *CXXRangeFor = dyn_cast<CXXForRangeStmt>(LoopStmt))
+    FirstBodyStmts.push_back(CXXRangeFor->getLoopVarStmt());
+  FirstBodyStmts.push_back(Body);
+  auto *FirstBody =
+      CompoundStmt::Create(Context, FirstBodyStmts, FPOptionsOverride(),
+                           Body->getBeginLoc(), Body->getEndLoc());
+
+  // Create first loop
+  auto *FirstLoop = new (Context)
+      ForStmt(Context, FirstInit.get(), FirstCond.get(), nullptr,
+              FirstIncr.get(), FirstBody, LoopHelper.Init->getBeginLoc(),
+              LoopHelper.Init->getBeginLoc(), LoopHelper.Inc->getEndLoc());
+
+  // Second loop: init-statement (i = n/2)
+  SemaRef.AddInitializerToDecl(SecondIVDecl, HalfIterations.get(),
+                               /*DirectInit=*/false);
+  StmtResult SecondInit = new (Context)
+      DeclStmt(DeclGroupRef(SecondIVDecl), OrigVarLocBegin, OrigVarLocEnd);
+  if (!SecondInit.isUsable())
+    return StmtError();
+
+  // Second loop: cond-expression (i < n)
+  ExprResult SecondCond =
+      SemaRef.BuildBinOp(CurScope, LoopHelper.Cond->getExprLoc(), BO_LT,
+                         MakeSecondRef(), MakeNumIterations());
+  if (!SecondCond.isUsable())
+    return StmtError();
+
+  // Second loop: incr-statement (++i)
+  ExprResult SecondIncr = SemaRef.BuildUnaryOp(
+      CurScope, LoopHelper.Inc->getExprLoc(), UO_PreInc, MakeSecondRef());
+  if (!SecondIncr.isUsable())
+    return StmtError();
+
+  // Second loop: body - update original variable and execute body
+  SmallVector<Stmt *, 4> SecondBodyStmts;
+  // Create update statement: origVar = .split.second.iv
+  ExprResult SecondUpdateExpr = SemaRef.BuildBinOp(
+      CurScope, OrigVarLoc, BO_Assign, OrigVar, MakeSecondRef());
+  if (!SecondUpdateExpr.isUsable())
+    return StmtError();
+  SecondBodyStmts.push_back(SecondUpdateExpr.get());
+  if (auto *CXXRangeFor = dyn_cast<CXXForRangeStmt>(LoopStmt))
+    SecondBodyStmts.push_back(CXXRangeFor->getLoopVarStmt());
+  SecondBodyStmts.push_back(Body);
+  auto *SecondBody =
+      CompoundStmt::Create(Context, SecondBodyStmts, FPOptionsOverride(),
+                           Body->getBeginLoc(), Body->getEndLoc());
+
+  // Create second loop
+  auto *SecondLoop = new (Context)
+      ForStmt(Context, SecondInit.get(), SecondCond.get(), nullptr,
+              SecondIncr.get(), SecondBody, LoopHelper.Init->getBeginLoc(),
+              LoopHelper.Init->getBeginLoc(), LoopHelper.Inc->getEndLoc());
+
+  // Combine both loops into a compound statement
+  SmallVector<Stmt *, 2> SplitLoops;
+  SplitLoops.push_back(FirstLoop);
+  SplitLoops.push_back(SecondLoop);
+  auto *SplitStmt =
+      CompoundStmt::Create(Context, SplitLoops, FPOptionsOverride(),
+                           FirstLoop->getBeginLoc(), SecondLoop->getEndLoc());
+
+  return OMPSplitDirective::Create(Context, StartLoc, EndLoc, AStmt, NumLoops,
+                                   SplitStmt, buildPreInits(Context, 
PreInits));
+}
+
 StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective(
     ArrayRef<OMPClause *> Clauses, Stmt *AStmt, SourceLocation StartLoc,
     SourceLocation EndLoc) {
diff --git a/clang/test/OpenMP/split_ast_print.cpp 
b/clang/test/OpenMP/split_ast_print.cpp
new file mode 100644
index 0000000000000..b24eae4a9bead
--- /dev/null
+++ b/clang/test/OpenMP/split_ast_print.cpp
@@ -0,0 +1,28 @@
+// Check no warnings/errors and that split is recognized
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 
-fsyntax-only -verify %s
+// expected-no-diagnostics
+
+// Check AST: OMPSplitDirective with associated for-loop
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 
-ast-dump %s | FileCheck %s --check-prefix=DUMP
+
+// Check unparsing
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 
-ast-print %s | FileCheck %s --check-prefix=PRINT
+
+#ifndef HEADER
+#define HEADER
+
+extern "C" void body(...);
+
+// PRINT-LABEL: void foo(
+// DUMP-LABEL:  FunctionDecl {{.*}} foo
+void foo(int n) {
+  // PRINT:     #pragma omp split
+  // DUMP:      OMPSplitDirective
+  #pragma omp split
+  // PRINT: for (int i = 0; i < n; ++i)
+  // DUMP:      ForStmt
+  for (int i = 0; i < n; ++i)
+    body(i);
+}
+
+#endif
diff --git a/clang/test/OpenMP/split_simple_test.c 
b/clang/test/OpenMP/split_simple_test.c
new file mode 100644
index 0000000000000..bc0d3c4770890
--- /dev/null
+++ b/clang/test/OpenMP/split_simple_test.c
@@ -0,0 +1,26 @@
+/*
+ * Simple test for #pragma omp split: one canonical for-loop is transformed
+ * into two loops (first half and second half of iterations). This file
+ * verifies compilation and correct result at runtime.
+ *
+ * Compile: clang -fopenmp -fopenmp-version=60 -o split_simple_test 
split_simple_test.c
+ * Run:     ./split_simple_test
+ * Expected: prints "sum 0..9 = 45 (expected 45)", exit code 0.
+ */
+// Verify the split directive compiles and links.
+// RUN: %clang -fopenmp -fopenmp-version=60 -o %t %s
+
+#include <stdio.h>
+
+int main(void) {
+  const int n = 10;
+  int sum = 0;
+
+#pragma omp split
+  for (int i = 0; i < n; ++i) {
+    sum += i;
+  }
+
+  printf("sum 0..%d = %d (expected %d)\n", n - 1, sum, n * (n - 1) / 2);
+  return (sum == n * (n - 1) / 2) ? 0 : 1;
+}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 31b6a3222d916..74600ea55e45c 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -6326,6 +6326,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind 
Kind) {
     return cxstring::createRef("OMPInterchangeDirective");
   case CXCursor_OMPFuseDirective:
     return cxstring::createRef("OMPFuseDirective");
+  case CXCursor_OMPSplitDirective:
+    return cxstring::createRef("OMPSplitDirective");
   case CXCursor_OMPForDirective:
     return cxstring::createRef("OMPForDirective");
   case CXCursor_OMPForSimdDirective:
diff --git a/clang/tools/libclang/CXCursor.cpp 
b/clang/tools/libclang/CXCursor.cpp
index d8247f8161506..4a3afa8b64675 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -697,7 +697,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl 
*Parent,
     K = CXCursor_OMPReverseDirective;
     break;
   case Stmt::OMPSplitDirectiveClass:
-    K = CXCursor_UnexposedStmt;
+    K = CXCursor_OMPSplitDirective;
     break;
   case Stmt::OMPInterchangeDirectiveClass:
     K = CXCursor_OMPInterchangeDirective;

>From cd1d79b3f0505fc401528586ab888251c545d908 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Fri, 13 Mar 2026 10:21:40 -0400
Subject: [PATCH 08/15] duplicate_split_def_removed

---
 llvm/include/llvm/Frontend/OpenMP/OMP.td | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td 
b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index fc3da2837b208..0703428186e7c 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -1202,16 +1202,6 @@ def OMP_EndSingle : Directive<[Spelling<"end single">]> {
   let category = OMP_Single.category;
   let languages = [L_Fortran];
 }
-def OMP_Split : Directive<[Spelling<"split">]> {
-  let allowedClauses = [
-    VersionedClause<OMPC_Apply, 60>,
-  ];
-  let allowedOnceClauses = [
-    VersionedClause<OMPC_Counts, 60>,
-  ];
-  let association = AS_LoopNest;
-  let category = CA_Executable;
-}
 def OMP_Target : Directive<[Spelling<"target">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,

>From 523ceefec4f7a9e424cf2068716ef4f2f933316a Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Fri, 13 Mar 2026 12:29:17 -0400
Subject: [PATCH 09/15] cursorkind_enum_added

---
 clang/bindings/python/clang/cindex.py |  3 +++
 clang/test/OpenMP/split_simple_test.c | 20 +++++++++-----------
 2 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/clang/bindings/python/clang/cindex.py 
b/clang/bindings/python/clang/cindex.py
index 1896a0a9c1c34..8c4542057576d 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -1448,6 +1448,9 @@ def is_unexposed(self):
     # OpenMP fuse directive.
     OMP_FUSE_DIRECTIVE = 311
 
+    # OpenMP split directive.
+    OMP_SPLIT_DIRECTIVE = 312
+
     # OpenACC Compute Construct.
     OPEN_ACC_COMPUTE_DIRECTIVE = 320
 
diff --git a/clang/test/OpenMP/split_simple_test.c 
b/clang/test/OpenMP/split_simple_test.c
index bc0d3c4770890..62dbc1cd861e5 100644
--- a/clang/test/OpenMP/split_simple_test.c
+++ b/clang/test/OpenMP/split_simple_test.c
@@ -1,16 +1,10 @@
 /*
  * Simple test for #pragma omp split: one canonical for-loop is transformed
- * into two loops (first half and second half of iterations). This file
- * verifies compilation and correct result at runtime.
- *
- * Compile: clang -fopenmp -fopenmp-version=60 -o split_simple_test 
split_simple_test.c
- * Run:     ./split_simple_test
- * Expected: prints "sum 0..9 = 45 (expected 45)", exit code 0.
+ * into two loops (first half and second half of iterations).
  */
-// Verify the split directive compiles and links.
-// RUN: %clang -fopenmp -fopenmp-version=60 -o %t %s
-
-#include <stdio.h>
+// Verify the split directive compiles and emits IR (two sequential loops).
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=60 -triple x86_64-unknown-unknown
+// -emit-llvm %s -o - | FileCheck %s
 
 int main(void) {
   const int n = 10;
@@ -21,6 +15,10 @@ int main(void) {
     sum += i;
   }
 
-  printf("sum 0..%d = %d (expected %d)\n", n - 1, sum, n * (n - 1) / 2);
   return (sum == n * (n - 1) / 2) ? 0 : 1;
 }
+
+// CHECK: define
+// CHECK: load
+// Split produces two sequential loops; ensure we have loop structure in IR.
+// CHECK: br i1

>From 600f48a40270ff1e131fe8f19530d504714ef862 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Thu, 19 Mar 2026 04:32:37 -0400
Subject: [PATCH 10/15] counts_clause_defined

---
 clang/include/clang/AST/OpenMPClause.h   | 87 ++++++++++++++++++++++++
 clang/include/clang/AST/StmtOpenMP.h     | 15 ++--
 clang/lib/AST/OpenMPClause.cpp           | 31 +++++++++
 clang/lib/AST/StmtOpenMP.cpp             | 13 ++--
 clang/lib/Basic/OpenMPKinds.cpp          |  2 +
 llvm/include/llvm/Frontend/OpenMP/OMP.td |  8 ++-
 6 files changed, 144 insertions(+), 12 deletions(-)

diff --git a/clang/include/clang/AST/OpenMPClause.h 
b/clang/include/clang/AST/OpenMPClause.h
index af5d3f4698eda..dbc22e23c3704 100644
--- a/clang/include/clang/AST/OpenMPClause.h
+++ b/clang/include/clang/AST/OpenMPClause.h
@@ -1023,6 +1023,93 @@ class OMPSizesClause final
   }
 };
 
+/// This represents the 'counts' clause in the '#pragma omp split' directive.
+///
+/// \code
+/// #pragma omp split counts(3, 5, 2)
+/// for (int i = 0; i < n; ++i) { ... }
+/// \endcode
+class OMPCountsClause final
+    : public OMPClause,
+      private llvm::TrailingObjects<OMPCountsClause, Expr *> {
+  friend class OMPClauseReader;
+  friend class llvm::TrailingObjects<OMPCountsClause, Expr *>;
+
+  /// Location of '('.
+  SourceLocation LParenLoc;
+
+  /// Number of count expressions in the clause.
+  unsigned NumCounts;
+
+  /// Build an empty clause.
+  explicit OMPCountsClause(int NumCounts)
+      : OMPClause(llvm::omp::OMPC_counts, SourceLocation(), SourceLocation()),
+        NumCounts(NumCounts) {}
+
+public:
+  /// Build a 'counts' AST node.
+  ///
+  /// \param C         Context of the AST.
+  /// \param StartLoc  Location of the 'counts' identifier.
+  /// \param LParenLoc Location of '('.
+  /// \param EndLoc    Location of ')'.
+  /// \param Counts    Content of the clause.
+  static OMPCountsClause *Create(const ASTContext &C, SourceLocation StartLoc,
+                                 SourceLocation LParenLoc,
+                                 SourceLocation EndLoc,
+                                 ArrayRef<Expr *> Counts);
+
+  /// Build an empty 'counts' AST node for deserialization.
+  ///
+  /// \param C          Context of the AST.
+  /// \param NumCounts   Number of items in the clause.
+  static OMPCountsClause *CreateEmpty(const ASTContext &C, unsigned NumCounts);
+
+  /// Sets the location of '('.
+  void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; }
+
+  /// Returns the location of '('.
+  SourceLocation getLParenLoc() const { return LParenLoc; }
+
+  /// Returns the number of list items.
+  unsigned getNumCounts() const { return NumCounts; }
+
+  /// Returns the count expressions.
+  MutableArrayRef<Expr *> getCountsRefs() {
+    return getTrailingObjects(NumCounts);
+  }
+  ArrayRef<Expr *> getCountsRefs() const {
+    return getTrailingObjects(NumCounts);
+  }
+
+  /// Sets the count expressions.
+  void setCountsRefs(ArrayRef<Expr *> VL) {
+    assert(VL.size() == NumCounts);
+    llvm::copy(VL, getCountsRefs().begin());
+  }
+
+  child_range children() {
+    MutableArrayRef<Expr *> Counts = getCountsRefs();
+    return child_range(reinterpret_cast<Stmt **>(Counts.begin()),
+                       reinterpret_cast<Stmt **>(Counts.end()));
+  }
+  const_child_range children() const {
+    ArrayRef<Expr *> Counts = getCountsRefs();
+    return const_child_range(reinterpret_cast<Stmt *const *>(Counts.begin()),
+                             reinterpret_cast<Stmt *const *>(Counts.end()));
+  }
+  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_counts;
+  }
+};
+
 /// This class represents the 'permutation' clause in the
 /// '#pragma omp interchange' directive.
 ///
diff --git a/clang/include/clang/AST/StmtOpenMP.h 
b/clang/include/clang/AST/StmtOpenMP.h
index c5b83e17acbcd..bdaf73c0a6607 100644
--- a/clang/include/clang/AST/StmtOpenMP.h
+++ b/clang/include/clang/AST/StmtOpenMP.h
@@ -6107,21 +6107,26 @@ class OMPSplitDirective final
   /// \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 (e.g. the required \c counts
+  ///                  clause).
   /// \param NumLoops  Number of affected loops (should be 1 for split).
   /// \param AssociatedStmt  The outermost associated loop.
   /// \param TransformedStmt The loop nest after splitting, or nullptr in
   ///                        dependent contexts.
   /// \param PreInits   Helper preinits statements for the loop nest.
   static OMPSplitDirective *Create(const ASTContext &C, SourceLocation 
StartLoc,
-                                   SourceLocation EndLoc, Stmt *AssociatedStmt,
-                                   unsigned NumLoops, Stmt *TransformedStmt,
-                                   Stmt *PreInits);
+                                   SourceLocation EndLoc,
+                                   ArrayRef<OMPClause *> Clauses,
+                                   unsigned NumLoops, Stmt *AssociatedStmt,
+                                   Stmt *TransformedStmt, Stmt *PreInits);
 
   /// Build an empty '#pragma omp split' AST node for deserialization.
   ///
   /// \param C          Context of the AST.
-  /// \param NumLoops   Number of associated loops to allocate
-  static OMPSplitDirective *CreateEmpty(const ASTContext &C, unsigned 
NumLoops);
+  /// \param NumClauses Number of clauses to allocate.
+  /// \param NumLoops   Number of associated loops to allocate.
+  static OMPSplitDirective *CreateEmpty(const ASTContext &C,
+                                        unsigned NumClauses, unsigned 
NumLoops);
 
   /// Gets/sets the associated loops after the transformation, i.e. after
   /// de-sugaring.
diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp
index d4826c3c6edca..ab6fa1c673411 100644
--- a/clang/lib/AST/OpenMPClause.cpp
+++ b/clang/lib/AST/OpenMPClause.cpp
@@ -986,6 +986,25 @@ OMPSizesClause *OMPSizesClause::CreateEmpty(const 
ASTContext &C,
   return new (Mem) OMPSizesClause(NumSizes);
 }
 
+OMPCountsClause *OMPCountsClause::Create(const ASTContext &C,
+                                         SourceLocation StartLoc,
+                                         SourceLocation LParenLoc,
+                                         SourceLocation EndLoc,
+                                         ArrayRef<Expr *> Counts) {
+  OMPCountsClause *Clause = CreateEmpty(C, Counts.size());
+  Clause->setLocStart(StartLoc);
+  Clause->setLParenLoc(LParenLoc);
+  Clause->setLocEnd(EndLoc);
+  Clause->setCountsRefs(Counts);
+  return Clause;
+}
+
+OMPCountsClause *OMPCountsClause::CreateEmpty(const ASTContext &C,
+                                              unsigned NumCounts) {
+  void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(NumCounts));
+  return new (Mem) OMPCountsClause(NumCounts);
+}
+
 OMPPermutationClause *OMPPermutationClause::Create(const ASTContext &C,
                                                    SourceLocation StartLoc,
                                                    SourceLocation LParenLoc,
@@ -1984,6 +2003,18 @@ void 
OMPClausePrinter::VisitOMPSizesClause(OMPSizesClause *Node) {
   OS << ")";
 }
 
+void OMPClausePrinter::VisitOMPCountsClause(OMPCountsClause *Node) {
+  OS << "counts(";
+  bool First = true;
+  for (auto *Count : Node->getCountsRefs()) {
+    if (!First)
+      OS << ", ";
+    Count->printPretty(OS, nullptr, Policy, 0);
+    First = false;
+  }
+  OS << ")";
+}
+
 void OMPClausePrinter::VisitOMPPermutationClause(OMPPermutationClause *Node) {
   OS << "permutation(";
   llvm::interleaveComma(Node->getArgsRefs(), OS, [&](const Expr *E) {
diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp
index 6c939cf7f9aeb..9d6b315effb41 100644
--- a/clang/lib/AST/StmtOpenMP.cpp
+++ b/clang/lib/AST/StmtOpenMP.cpp
@@ -554,11 +554,11 @@ OMPInterchangeDirective::CreateEmpty(const ASTContext &C, 
unsigned NumClauses,
 
 OMPSplitDirective *
 OMPSplitDirective::Create(const ASTContext &C, SourceLocation StartLoc,
-                          SourceLocation EndLoc, Stmt *AssociatedStmt,
-                          unsigned NumLoops, Stmt *TransformedStmt,
-                          Stmt *PreInits) {
+                          SourceLocation EndLoc, ArrayRef<OMPClause *> Clauses,
+                          unsigned NumLoops, Stmt *AssociatedStmt,
+                          Stmt *TransformedStmt, Stmt *PreInits) {
   OMPSplitDirective *Dir = createDirective<OMPSplitDirective>(
-      C, {}, AssociatedStmt, TransformedStmtOffset + 1, StartLoc, EndLoc,
+      C, Clauses, AssociatedStmt, TransformedStmtOffset + 1, StartLoc, EndLoc,
       NumLoops);
   Dir->setTransformedStmt(TransformedStmt);
   Dir->setPreInits(PreInits);
@@ -566,10 +566,11 @@ OMPSplitDirective::Create(const ASTContext &C, 
SourceLocation StartLoc,
 }
 
 OMPSplitDirective *OMPSplitDirective::CreateEmpty(const ASTContext &C,
+                                                  unsigned NumClauses,
                                                   unsigned NumLoops) {
   return createEmptyDirective<OMPSplitDirective>(
-      C, /*NumClauses=*/0, /*HasAssociatedStmt=*/true,
-      TransformedStmtOffset + 1, SourceLocation(), SourceLocation(), NumLoops);
+      C, NumClauses, /*HasAssociatedStmt=*/true, TransformedStmtOffset + 1,
+      SourceLocation(), SourceLocation(), NumLoops);
 }
 
 OMPFuseDirective *OMPFuseDirective::Create(
diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp
index ef01943f11ca5..287eb217ba458 100644
--- a/clang/lib/Basic/OpenMPKinds.cpp
+++ b/clang/lib/Basic/OpenMPKinds.cpp
@@ -256,6 +256,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind 
Kind, StringRef Str,
   case OMPC_safelen:
   case OMPC_simdlen:
   case OMPC_sizes:
+  case OMPC_counts:
   case OMPC_permutation:
   case OMPC_allocator:
   case OMPC_collapse:
@@ -635,6 +636,7 @@ const char 
*clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind,
   case OMPC_safelen:
   case OMPC_simdlen:
   case OMPC_sizes:
+  case OMPC_counts:
   case OMPC_permutation:
   case OMPC_allocator:
   case OMPC_collapse:
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td 
b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 0703428186e7c..303a3d0622a4c 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -142,6 +142,7 @@ def OMPC_CopyPrivate : Clause<[Spelling<"copyprivate">]> {
   let flangClass = "OmpObjectList";
 }
 def OMPC_Counts : Clause<[Spelling<"counts">]> {
+  let clangClass = "OMPCountsClause";
 }
 def OMPC_Default : Clause<[Spelling<"default">]> {
   let clangClass = "OMPDefaultClause";
@@ -1425,7 +1426,12 @@ def OMP_Stripe : Directive<[Spelling<"stripe">]> {
   let category = CA_Executable;
 }
 def OMP_Split : Directive<[Spelling<"split">]> {
-  // TODO: Add counts clause support (OMPC_Counts)
+  let allowedOnceClauses = [
+    VersionedClause<OMPC_Counts, 60>,
+  ];
+  let requiredClauses = [
+    VersionedClause<OMPC_Counts, 60>,
+  ];
   let association = AS_LoopNest;
   let category = CA_Executable;
 }

>From 502a1a637b16407e1e75e9f370e06d4d9f141d13 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Thu, 19 Mar 2026 04:33:51 -0400
Subject: [PATCH 11/15] parse & sema

---
 clang/include/clang/AST/RecursiveASTVisitor.h |   7 +
 clang/include/clang/Parse/Parser.h            |   3 +
 clang/include/clang/Sema/SemaOpenMP.h         |   8 +-
 clang/lib/Parse/ParseOpenMP.cpp               |  24 +-
 clang/lib/Sema/SemaOpenMP.cpp                 | 278 +++++++++---------
 clang/lib/Sema/TreeTransform.h                |  29 ++
 6 files changed, 205 insertions(+), 144 deletions(-)

diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h 
b/clang/include/clang/AST/RecursiveASTVisitor.h
index 0802871c4a720..1a14dd2c666b5 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -3506,6 +3506,13 @@ bool 
RecursiveASTVisitor<Derived>::VisitOMPSizesClause(OMPSizesClause *C) {
   return true;
 }
 
+template <typename Derived>
+bool RecursiveASTVisitor<Derived>::VisitOMPCountsClause(OMPCountsClause *C) {
+  for (Expr *E : C->getCountsRefs())
+    TRY_TO(TraverseStmt(E));
+  return true;
+}
+
 template <typename Derived>
 bool RecursiveASTVisitor<Derived>::VisitOMPPermutationClause(
     OMPPermutationClause *C) {
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 5ae02e2b4e8ad..74a2a565bd613 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -6774,6 +6774,9 @@ class Parser : public CodeCompletionHandler {
   /// Parses the 'sizes' clause of a '#pragma omp tile' directive.
   OMPClause *ParseOpenMPSizesClause();
 
+  /// Parses the 'counts' clause of a '#pragma omp split' directive.
+  OMPClause *ParseOpenMPCountsClause();
+
   /// Parses the 'permutation' clause of a '#pragma omp interchange' directive.
   OMPClause *ParseOpenMPPermutationClause();
 
diff --git a/clang/include/clang/Sema/SemaOpenMP.h 
b/clang/include/clang/Sema/SemaOpenMP.h
index 57382557fd13f..c4c1b2ad33f71 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -459,7 +459,8 @@ class SemaOpenMP : public SemaBase {
                                          SourceLocation EndLoc);
   /// Called on well-formed '#pragma omp split' after parsing of its
   /// associated statement.
-  StmtResult ActOnOpenMPSplitDirective(Stmt *AStmt, SourceLocation StartLoc,
+  StmtResult ActOnOpenMPSplitDirective(ArrayRef<OMPClause *> Clauses,
+                                       Stmt *AStmt, SourceLocation StartLoc,
                                        SourceLocation EndLoc);
   /// Called on well-formed '#pragma omp interchange' after parsing of its
   /// clauses and the associated statement.
@@ -915,6 +916,11 @@ class SemaOpenMP : public SemaBase {
                                     SourceLocation StartLoc,
                                     SourceLocation LParenLoc,
                                     SourceLocation EndLoc);
+  /// Called on well-formed 'counts' clause after parsing its arguments.
+  OMPClause *ActOnOpenMPCountsClause(ArrayRef<Expr *> CountExprs,
+                                     SourceLocation StartLoc,
+                                     SourceLocation LParenLoc,
+                                     SourceLocation EndLoc);
   /// Called on well-form 'permutation' clause after parsing its arguments.
   OMPClause *ActOnOpenMPPermutationClause(ArrayRef<Expr *> PermExprs,
                                           SourceLocation StartLoc,
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 32d684e034200..e8756e0dc9e0b 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2406,6 +2406,10 @@ StmtResult Parser::ParseOpenMPExecutableDirective(
     Diag(Loc, diag::err_omp_required_clause)
         << getOpenMPDirectiveName(DKind, OMPVersion) << "sizes";
   }
+  if (DKind == OMPD_split && !SeenClauses[unsigned(OMPC_counts)]) {
+    Diag(Loc, diag::err_omp_required_clause)
+        << getOpenMPDirectiveName(DKind, OMPVersion) << "counts";
+  }
 
   StmtResult AssociatedStmt;
   if (HasAssociatedStatement) {
@@ -2968,6 +2972,17 @@ OMPClause *Parser::ParseOpenMPSizesClause() {
                                                  OpenLoc, CloseLoc);
 }
 
+OMPClause *Parser::ParseOpenMPCountsClause() {
+  SourceLocation ClauseNameLoc, OpenLoc, CloseLoc;
+  SmallVector<Expr *, 4> ValExprs;
+  if (ParseOpenMPExprListClause(OMPC_counts, ClauseNameLoc, OpenLoc, CloseLoc,
+                                ValExprs))
+    return nullptr;
+
+  return Actions.OpenMP().ActOnOpenMPCountsClause(ValExprs, ClauseNameLoc,
+                                                  OpenLoc, CloseLoc);
+}
+
 OMPClause *Parser::ParseOpenMPLoopRangeClause() {
   SourceLocation ClauseNameLoc = ConsumeToken();
   SourceLocation FirstLoc, CountLoc;
@@ -3415,8 +3430,13 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind 
DKind,
     Clause = ParseOpenMPPermutationClause();
     break;
   case OMPC_counts:
-    // TODO: Implement ParseOpenMPCountsClause() - not yet worked on
-    SkipUntil(tok::r_paren, tok::comma, StopBeforeMatch);
+    if (!FirstClause) {
+      Diag(Tok, diag::err_omp_more_one_clause)
+          << getOpenMPDirectiveName(DKind, OMPVersion)
+          << getOpenMPClauseName(CKind) << 0;
+      ErrorFound = true;
+    }
+    Clause = ParseOpenMPCountsClause();
     break;
   case OMPC_uses_allocators:
     Clause = ParseOpenMPUsesAllocatorClause(DKind);
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 26176541782a1..01c799017e501 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -6431,13 +6431,15 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
            "reverse directive does not support any clauses");
     Res = ActOnOpenMPReverseDirective(AStmt, StartLoc, EndLoc);
     break;
-  case OMPD_split:
-    // TODO: Add counts clause support - not yet worked on
-    // Currently only supports basic split without clauses.
-    assert(ClausesWithImplicit.empty() &&
-           "split directive does not support any clauses");
-    Res = ActOnOpenMPSplitDirective(AStmt, StartLoc, EndLoc);
+  case OMPD_split: {
+    const OMPCountsClause *CountsClause =
+        OMPExecutableDirective::getSingleClause<OMPCountsClause>(
+            ClausesWithImplicit);
+    assert(CountsClause && "split directive requires counts clause");
+    Res =
+        ActOnOpenMPSplitDirective(ClausesWithImplicit, AStmt, StartLoc, 
EndLoc);
     break;
+  }
   case OMPD_interchange:
     Res = ActOnOpenMPInterchangeDirective(ClausesWithImplicit, AStmt, StartLoc,
                                           EndLoc);
@@ -15879,7 +15881,12 @@ StmtResult 
SemaOpenMP::ActOnOpenMPReverseDirective(Stmt *AStmt,
                                      buildPreInits(Context, PreInits));
 }
 
-StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(Stmt *AStmt,
+/// Build the AST for \#pragma omp split counts(c1, c2, ...).
+///
+/// Splits the single associated loop into N consecutive loops, where N is the
+/// number of count expressions.
+StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(ArrayRef<OMPClause *> Clauses,
+                                                 Stmt *AStmt,
                                                  SourceLocation StartLoc,
                                                  SourceLocation EndLoc) {
   ASTContext &Context = getASTContext();
@@ -15889,6 +15896,12 @@ StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(Stmt 
*AStmt,
   if (!AStmt)
     return StmtError();
 
+  const OMPCountsClause *CountsClause =
+      OMPExecutableDirective::getSingleClause<OMPCountsClause>(Clauses);
+  if (!CountsClause)
+    return StmtError();
+
+  // Split applies to a single loop; check it is transformable and get helpers.
   constexpr unsigned NumLoops = 1;
   Stmt *Body = nullptr;
   SmallVector<OMPLoopBasedDirective::HelperExprs, NumLoops> LoopHelpers(
@@ -15901,8 +15914,8 @@ StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(Stmt 
*AStmt,
   // Delay applying the transformation to when template is completely
   // instantiated.
   if (SemaRef.CurContext->isDependentContext())
-    return OMPSplitDirective::Create(Context, StartLoc, EndLoc, AStmt, 
NumLoops,
-                                     nullptr, nullptr);
+    return OMPSplitDirective::Create(Context, StartLoc, EndLoc, Clauses,
+                                     NumLoops, AStmt, nullptr, nullptr);
 
   assert(LoopHelpers.size() == NumLoops &&
          "Expecting a single-dimensional loop iteration space");
@@ -15918,6 +15931,8 @@ StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(Stmt 
*AStmt,
   SmallVector<Stmt *> PreInits;
   addLoopPreInits(Context, LoopHelper, LoopStmt, OriginalInits[0], PreInits);
 
+  // Type and name of the original loop variable; we create one IV per segment
+  // and assign it to the original var so the body sees the same name.
   auto *IterationVarRef = cast<DeclRefExpr>(LoopHelper.IterationVarRef);
   QualType IVTy = IterationVarRef->getType();
   uint64_t IVWidth = Context.getTypeSize(IVTy);
@@ -15927,153 +15942,109 @@ StmtResult 
SemaOpenMP::ActOnOpenMPSplitDirective(Stmt *AStmt,
   SourceLocation OrigVarLoc = OrigVar->getExprLoc();
   SourceLocation OrigVarLocBegin = OrigVar->getBeginLoc();
   SourceLocation OrigVarLocEnd = OrigVar->getEndLoc();
-
-  // Locations pointing to the transformation.
-  SourceLocation TransformLoc = StartLoc;
-
   // Internal variable names.
   std::string OrigVarName = OrigVar->getNameInfo().getAsString();
 
-  // For Subexpressions with more than one use, we define a lambda
-  // that creates a new AST node at every use.
-  CaptureVars CopyTransformer(SemaRef);
-  auto MakeNumIterations = [&CopyTransformer, &LoopHelper]() -> Expr * {
-    return AssertSuccess(
-        CopyTransformer.TransformExpr(LoopHelper.NumIterations));
-  };
-
-  // For split, we currently divide the loop into two equal parts.
-  // First loop: i = 0; i < n/2; ++i
-  // Second loop: i = n/2; i < n; ++i
-  // TODO: Add counts clause support - not yet worked on
-
-  // Create iteration variable for the first split loop.
-  SmallString<64> FirstIVName(".split.first.iv.");
-  FirstIVName += OrigVarName;
-  VarDecl *FirstIVDecl =
-      buildVarDecl(SemaRef, {}, IVTy, FirstIVName, nullptr, OrigVar);
-  auto MakeFirstRef = [&SemaRef = this->SemaRef, FirstIVDecl, IVTy,
-                       OrigVarLoc]() {
-    return buildDeclRefExpr(SemaRef, FirstIVDecl, IVTy, OrigVarLoc);
-  };
-
-  // Create iteration variable for the second split loop.
-  SmallString<64> SecondIVName(".split.second.iv.");
-  SecondIVName += OrigVarName;
-  VarDecl *SecondIVDecl =
-      buildVarDecl(SemaRef, {}, IVTy, SecondIVName, nullptr, OrigVar);
-  auto MakeSecondRef = [&SemaRef = this->SemaRef, SecondIVDecl, IVTy,
-                        OrigVarLoc]() {
-    return buildDeclRefExpr(SemaRef, SecondIVDecl, IVTy, OrigVarLoc);
-  };
-
-  // Create n/2 expression for the split point.
-  auto *Two = IntegerLiteral::Create(Context, llvm::APInt(IVWidth, 2), IVTy,
-                                     TransformLoc);
-  ExprResult HalfIterations = SemaRef.BuildBinOp(CurScope, TransformLoc, 
BO_Div,
-                                                 MakeNumIterations(), Two);
-  if (!HalfIterations.isUsable())
-    return StmtError();
-
-  // First loop: init-statement: i = 0
-  auto *Zero = IntegerLiteral::Create(Context, llvm::APInt::getZero(IVWidth),
-                                      FirstIVDecl->getType(), OrigVarLoc);
-  SemaRef.AddInitializerToDecl(FirstIVDecl, Zero, /*DirectInit=*/false);
-  StmtResult FirstInit = new (Context)
-      DeclStmt(DeclGroupRef(FirstIVDecl), OrigVarLocBegin, OrigVarLocEnd);
-  if (!FirstInit.isUsable())
-    return StmtError();
+  // Collect constant count values from the counts clause
+  SmallVector<uint64_t, 4> CountValues;
+  for (Expr *CountExpr : CountsClause->getCountsRefs()) {
+    if (!CountExpr) {
+      return OMPSplitDirective::Create(Context, StartLoc, EndLoc, Clauses,
+                                       NumLoops, AStmt, nullptr, nullptr);
+    }
+    std::optional<llvm::APSInt> OptVal =
+        CountExpr->getIntegerConstantExpr(Context);
+    if (!OptVal || OptVal->isNegative()) {
+      return OMPSplitDirective::Create(Context, StartLoc, EndLoc, Clauses,
+                                       NumLoops, AStmt, nullptr, nullptr);
+    }
+    CountValues.push_back(OptVal->getZExtValue());
+  }
 
-  // First loop: cond-expression (i < n/2)
-  ExprResult FirstCond =
-      SemaRef.BuildBinOp(CurScope, LoopHelper.Cond->getExprLoc(), BO_LT,
-                         MakeFirstRef(), HalfIterations.get());
-  if (!FirstCond.isUsable())
+  if (CountValues.empty()) {
+    Diag(CountsClause->getBeginLoc(), diag::err_omp_unexpected_clause_value)
+        << "at least one non-negative integer expression" << "counts";
     return StmtError();
+  }
 
-  // First loop: incr-statement (++i)
-  ExprResult FirstIncr = SemaRef.BuildUnaryOp(
-      CurScope, LoopHelper.Inc->getExprLoc(), UO_PreInc, MakeFirstRef());
-  if (!FirstIncr.isUsable())
-    return StmtError();
+  // Cumulative segment starts: Starts[0]=0,
+  // Starts[j]=Starts[j-1]+CountValues[j-1]. Example: CountValues [3,5,2] →
+  // Starts [0,3,8,10]. Segment k runs [Starts[k], Starts[k+1]).
+  SmallVector<uint64_t, 4> Starts;
+  Starts.push_back(0);
+  for (size_t j = 0; j < CountValues.size(); ++j)
+    Starts.push_back(Starts.back() + CountValues[j]);
+
+  size_t NumSegments = CountValues.size();
+  SmallVector<Stmt *, 4> SplitLoops;
+
+  for (size_t Seg = 0; Seg < NumSegments; ++Seg) {
+    uint64_t StartVal = Starts[Seg];
+    uint64_t EndVal = Starts[Seg + 1];
+
+    // Segment IV: .split.iv.<Seg>.<OrigVarName>, init to StartVal, bound by
+    // EndVal.
+    SmallString<64> IVName(".split.iv.");
+    IVName += Twine(Seg).str();
+    IVName += ".";
+    IVName += OrigVarName;
+    VarDecl *IVDecl = buildVarDecl(SemaRef, {}, IVTy, IVName, nullptr, 
OrigVar);
+    auto MakeIVRef = [&SemaRef = this->SemaRef, IVDecl, IVTy, OrigVarLoc]() {
+      return buildDeclRefExpr(SemaRef, IVDecl, IVTy, OrigVarLoc);
+    };
 
-  // First loop: body - update original variable and execute body
-  // We need to create a copy of LoopHelper.Updates that uses FirstIV instead
-  // of the iteration variable. For now, use a simpler approach: directly
-  // assign the first IV to the original variable.
-  SmallVector<Stmt *, 4> FirstBodyStmts;
-  // Create update statement: origVar = .split.first.iv
-  // We'll use a BinaryOperator for assignment
-  ExprResult FirstUpdateExpr = SemaRef.BuildBinOp(
-      CurScope, OrigVarLoc, BO_Assign, OrigVar, MakeFirstRef());
-  if (!FirstUpdateExpr.isUsable())
-    return StmtError();
-  FirstBodyStmts.push_back(FirstUpdateExpr.get());
-  if (auto *CXXRangeFor = dyn_cast<CXXForRangeStmt>(LoopStmt))
-    FirstBodyStmts.push_back(CXXRangeFor->getLoopVarStmt());
-  FirstBodyStmts.push_back(Body);
-  auto *FirstBody =
-      CompoundStmt::Create(Context, FirstBodyStmts, FPOptionsOverride(),
-                           Body->getBeginLoc(), Body->getEndLoc());
+    llvm::APInt StartAP(IVWidth, StartVal, /*isSigned=*/false);
+    llvm::APInt EndAP(IVWidth, EndVal, /*isSigned=*/false);
+    auto *StartLit = IntegerLiteral::Create(Context, StartAP, IVTy, 
OrigVarLoc);
+    auto *EndLit = IntegerLiteral::Create(Context, EndAP, IVTy, OrigVarLoc);
 
-  // Create first loop
-  auto *FirstLoop = new (Context)
-      ForStmt(Context, FirstInit.get(), FirstCond.get(), nullptr,
-              FirstIncr.get(), FirstBody, LoopHelper.Init->getBeginLoc(),
-              LoopHelper.Init->getBeginLoc(), LoopHelper.Inc->getEndLoc());
+    SemaRef.AddInitializerToDecl(IVDecl, StartLit, /*DirectInit=*/false);
+    StmtResult InitStmt = new (Context)
+        DeclStmt(DeclGroupRef(IVDecl), OrigVarLocBegin, OrigVarLocEnd);
+    if (!InitStmt.isUsable())
+      return StmtError();
 
-  // Second loop: init-statement (i = n/2)
-  SemaRef.AddInitializerToDecl(SecondIVDecl, HalfIterations.get(),
-                               /*DirectInit=*/false);
-  StmtResult SecondInit = new (Context)
-      DeclStmt(DeclGroupRef(SecondIVDecl), OrigVarLocBegin, OrigVarLocEnd);
-  if (!SecondInit.isUsable())
-    return StmtError();
+    ExprResult CondExpr = SemaRef.BuildBinOp(
+        CurScope, LoopHelper.Cond->getExprLoc(), BO_LT, MakeIVRef(), EndLit);
+    if (!CondExpr.isUsable())
+      return StmtError();
 
-  // Second loop: cond-expression (i < n)
-  ExprResult SecondCond =
-      SemaRef.BuildBinOp(CurScope, LoopHelper.Cond->getExprLoc(), BO_LT,
-                         MakeSecondRef(), MakeNumIterations());
-  if (!SecondCond.isUsable())
-    return StmtError();
+    ExprResult IncrExpr = SemaRef.BuildUnaryOp(
+        CurScope, LoopHelper.Inc->getExprLoc(), UO_PreInc, MakeIVRef());
+    if (!IncrExpr.isUsable())
+      return StmtError();
 
-  // Second loop: incr-statement (++i)
-  ExprResult SecondIncr = SemaRef.BuildUnaryOp(
-      CurScope, LoopHelper.Inc->getExprLoc(), UO_PreInc, MakeSecondRef());
-  if (!SecondIncr.isUsable())
-    return StmtError();
+    // orig_var = IV so the original body sees the same variable.
+    ExprResult UpdateExpr = SemaRef.BuildBinOp(CurScope, OrigVarLoc, BO_Assign,
+                                               OrigVar, MakeIVRef());
+    if (!UpdateExpr.isUsable())
+      return StmtError();
 
-  // Second loop: body - update original variable and execute body
-  SmallVector<Stmt *, 4> SecondBodyStmts;
-  // Create update statement: origVar = .split.second.iv
-  ExprResult SecondUpdateExpr = SemaRef.BuildBinOp(
-      CurScope, OrigVarLoc, BO_Assign, OrigVar, MakeSecondRef());
-  if (!SecondUpdateExpr.isUsable())
-    return StmtError();
-  SecondBodyStmts.push_back(SecondUpdateExpr.get());
-  if (auto *CXXRangeFor = dyn_cast<CXXForRangeStmt>(LoopStmt))
-    SecondBodyStmts.push_back(CXXRangeFor->getLoopVarStmt());
-  SecondBodyStmts.push_back(Body);
-  auto *SecondBody =
-      CompoundStmt::Create(Context, SecondBodyStmts, FPOptionsOverride(),
-                           Body->getBeginLoc(), Body->getEndLoc());
+    SmallVector<Stmt *, 4> BodyStmts;
+    BodyStmts.push_back(UpdateExpr.get());
+    if (auto *CXXRangeFor = dyn_cast<CXXForRangeStmt>(LoopStmt))
+      BodyStmts.push_back(CXXRangeFor->getLoopVarStmt());
+    BodyStmts.push_back(Body);
 
-  // Create second loop
-  auto *SecondLoop = new (Context)
-      ForStmt(Context, SecondInit.get(), SecondCond.get(), nullptr,
-              SecondIncr.get(), SecondBody, LoopHelper.Init->getBeginLoc(),
-              LoopHelper.Init->getBeginLoc(), LoopHelper.Inc->getEndLoc());
+    auto *LoopBody =
+        CompoundStmt::Create(Context, BodyStmts, FPOptionsOverride(),
+                             Body->getBeginLoc(), Body->getEndLoc());
 
-  // Combine both loops into a compound statement
-  SmallVector<Stmt *, 2> SplitLoops;
-  SplitLoops.push_back(FirstLoop);
-  SplitLoops.push_back(SecondLoop);
-  auto *SplitStmt =
-      CompoundStmt::Create(Context, SplitLoops, FPOptionsOverride(),
-                           FirstLoop->getBeginLoc(), SecondLoop->getEndLoc());
+    auto *For = new (Context)
+        ForStmt(Context, InitStmt.get(), CondExpr.get(), nullptr,
+                IncrExpr.get(), LoopBody, LoopHelper.Init->getBeginLoc(),
+                LoopHelper.Init->getBeginLoc(), LoopHelper.Inc->getEndLoc());
+    // Push the splitted for loops into SplitLoops
+    SplitLoops.push_back(For);
+  }
+  // Combine all the loops into a compound statement
+  auto *SplitStmt = CompoundStmt::Create(
+      Context, SplitLoops, FPOptionsOverride(),
+      SplitLoops.front()->getBeginLoc(), SplitLoops.back()->getEndLoc());
 
-  return OMPSplitDirective::Create(Context, StartLoc, EndLoc, AStmt, NumLoops,
-                                   SplitStmt, buildPreInits(Context, 
PreInits));
+  return OMPSplitDirective::Create(Context, StartLoc, EndLoc, Clauses, 
NumLoops,
+                                   AStmt, SplitStmt,
+                                   buildPreInits(Context, PreInits));
 }
 
 StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective(
@@ -18024,6 +17995,31 @@ OMPClause 
*SemaOpenMP::ActOnOpenMPSizesClause(ArrayRef<Expr *> SizeExprs,
                                 SanitizedSizeExprs);
 }
 
+OMPClause *SemaOpenMP::ActOnOpenMPCountsClause(ArrayRef<Expr *> CountExprs,
+                                               SourceLocation StartLoc,
+                                               SourceLocation LParenLoc,
+                                               SourceLocation EndLoc) {
+  SmallVector<Expr *> SanitizedCountExprs(CountExprs);
+
+  for (Expr *&CountExpr : SanitizedCountExprs) {
+    if (!CountExpr)
+      continue;
+
+    bool IsValid = isNonNegativeIntegerValue(CountExpr, SemaRef, OMPC_counts,
+                                             /*StrictlyPositive=*/false);
+
+    QualType CountTy = CountExpr->getType();
+    if (!CountTy->isIntegerType())
+      IsValid = false;
+
+    if (!CountExpr->isInstantiationDependent() && !IsValid)
+      CountExpr = nullptr;
+  }
+
+  return OMPCountsClause::Create(getASTContext(), StartLoc, LParenLoc, EndLoc,
+                                 SanitizedCountExprs);
+}
+
 OMPClause *SemaOpenMP::ActOnOpenMPPermutationClause(ArrayRef<Expr *> PermExprs,
                                                     SourceLocation StartLoc,
                                                     SourceLocation LParenLoc,
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 52c70d9c10a23..edd937322af62 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -1772,6 +1772,14 @@ class TreeTransform {
                                                      EndLoc);
   }
 
+  OMPClause *RebuildOMPCountsClause(ArrayRef<Expr *> Counts,
+                                    SourceLocation StartLoc,
+                                    SourceLocation LParenLoc,
+                                    SourceLocation EndLoc) {
+    return getSema().OpenMP().ActOnOpenMPCountsClause(Counts, StartLoc,
+                                                      LParenLoc, EndLoc);
+  }
+
   /// Build a new OpenMP 'permutation' clause.
   OMPClause *RebuildOMPPermutationClause(ArrayRef<Expr *> PermExprs,
                                          SourceLocation StartLoc,
@@ -10628,6 +10636,27 @@ OMPClause 
*TreeTransform<Derived>::TransformOMPSizesClause(OMPSizesClause *C) {
                                C->getLParenLoc(), C->getEndLoc());
 }
 
+template <typename Derived>
+OMPClause *
+TreeTransform<Derived>::TransformOMPCountsClause(OMPCountsClause *C) {
+  SmallVector<Expr *, 4> TransformedCounts;
+  TransformedCounts.reserve(C->getNumCounts());
+  for (Expr *E : C->getCountsRefs()) {
+    if (!E) {
+      TransformedCounts.push_back(nullptr);
+      continue;
+    }
+
+    ExprResult T = getDerived().TransformExpr(E);
+    if (T.isInvalid())
+      return nullptr;
+    TransformedCounts.push_back(T.get());
+  }
+
+  return RebuildOMPCountsClause(TransformedCounts, C->getBeginLoc(),
+                                C->getLParenLoc(), C->getEndLoc());
+}
+
 template <typename Derived>
 OMPClause *
 TreeTransform<Derived>::TransformOMPPermutationClause(OMPPermutationClause *C) 
{

>From 54e4787e56e98a7e8a24f011bd878cb205f44876 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Thu, 19 Mar 2026 04:34:58 -0400
Subject: [PATCH 12/15] serialisation

---
 clang/lib/AST/StmtProfile.cpp             |  6 ++++++
 clang/lib/Serialization/ASTReader.cpp     | 11 +++++++++++
 clang/lib/Serialization/ASTReaderStmt.cpp |  5 ++---
 clang/lib/Serialization/ASTWriter.cpp     |  7 +++++++
 clang/tools/libclang/CIndex.cpp           |  5 +++++
 5 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 7c1898833e52d..4ebece01e5df0 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -498,6 +498,12 @@ void OMPClauseProfiler::VisitOMPSizesClause(const 
OMPSizesClause *C) {
       Profiler->VisitExpr(E);
 }
 
+void OMPClauseProfiler::VisitOMPCountsClause(const OMPCountsClause *C) {
+  for (auto *E : C->getCountsRefs())
+    if (E)
+      Profiler->VisitExpr(E);
+}
+
 void OMPClauseProfiler::VisitOMPPermutationClause(
     const OMPPermutationClause *C) {
   for (Expr *E : C->getArgsRefs())
diff --git a/clang/lib/Serialization/ASTReader.cpp 
b/clang/lib/Serialization/ASTReader.cpp
index 515eaf8d1caed..ab8c050e222d1 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -11390,6 +11390,11 @@ OMPClause *OMPClauseReader::readClause() {
     C = OMPSizesClause::CreateEmpty(Context, NumSizes);
     break;
   }
+  case llvm::omp::OMPC_counts: {
+    unsigned NumCounts = Record.readInt();
+    C = OMPCountsClause::CreateEmpty(Context, NumCounts);
+    break;
+  }
   case llvm::omp::OMPC_permutation: {
     unsigned NumLoops = Record.readInt();
     C = OMPPermutationClause::CreateEmpty(Context, NumLoops);
@@ -11803,6 +11808,12 @@ void 
OMPClauseReader::VisitOMPSizesClause(OMPSizesClause *C) {
   C->setLParenLoc(Record.readSourceLocation());
 }
 
+void OMPClauseReader::VisitOMPCountsClause(OMPCountsClause *C) {
+  for (Expr *&E : C->getCountsRefs())
+    E = Record.readSubExpr();
+  C->setLParenLoc(Record.readSourceLocation());
+}
+
 void OMPClauseReader::VisitOMPPermutationClause(OMPPermutationClause *C) {
   for (Expr *&E : C->getArgsRefs())
     E = Record.readSubExpr();
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp 
b/clang/lib/Serialization/ASTReaderStmt.cpp
index f52022f37231f..c68d07a39d651 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -3683,9 +3683,8 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
 
     case STMT_OMP_SPLIT_DIRECTIVE: {
       unsigned NumLoops = Record[ASTStmtReader::NumStmtFields];
-      assert(Record[ASTStmtReader::NumStmtFields + 1] == 0 &&
-             "Split directive has no clauses");
-      S = OMPSplitDirective::CreateEmpty(Context, NumLoops);
+      unsigned NumClauses = Record[ASTStmtReader::NumStmtFields + 1];
+      S = OMPSplitDirective::CreateEmpty(Context, NumClauses, NumLoops);
       break;
     }
 
diff --git a/clang/lib/Serialization/ASTWriter.cpp 
b/clang/lib/Serialization/ASTWriter.cpp
index e21a86b688dbf..01805cfe004c7 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -8003,6 +8003,13 @@ void OMPClauseWriter::VisitOMPSizesClause(OMPSizesClause 
*C) {
   Record.AddSourceLocation(C->getLParenLoc());
 }
 
+void OMPClauseWriter::VisitOMPCountsClause(OMPCountsClause *C) {
+  Record.push_back(C->getNumCounts());
+  for (Expr *Count : C->getCountsRefs())
+    Record.AddStmt(Count);
+  Record.AddSourceLocation(C->getLParenLoc());
+}
+
 void OMPClauseWriter::VisitOMPPermutationClause(OMPPermutationClause *C) {
   Record.push_back(C->getNumLoops());
   for (Expr *Size : C->getArgsRefs())
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 74600ea55e45c..7587b44488cf9 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -2362,6 +2362,11 @@ void OMPClauseEnqueue::VisitOMPSizesClause(const 
OMPSizesClause *C) {
     Visitor->AddStmt(E);
 }
 
+void OMPClauseEnqueue::VisitOMPCountsClause(const OMPCountsClause *C) {
+  for (auto E : C->getCountsRefs())
+    Visitor->AddStmt(E);
+}
+
 void OMPClauseEnqueue::VisitOMPPermutationClause(
     const OMPPermutationClause *C) {
   for (auto E : C->getArgsRefs())

>From ebe04ea2f98d81bcc42b59341daca0b9e2f8436c Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Thu, 19 Mar 2026 04:36:32 -0400
Subject: [PATCH 13/15] basic_test

---
 clang/test/OpenMP/split_ast_print.cpp   |  5 +--
 clang/test/OpenMP/split_counts_verify.c | 44 +++++++++++++++++++++++++
 clang/test/OpenMP/split_simple_test.c   | 15 +++++----
 3 files changed, 56 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/OpenMP/split_counts_verify.c

diff --git a/clang/test/OpenMP/split_ast_print.cpp 
b/clang/test/OpenMP/split_ast_print.cpp
index b24eae4a9bead..07dd7b28e1a13 100644
--- a/clang/test/OpenMP/split_ast_print.cpp
+++ b/clang/test/OpenMP/split_ast_print.cpp
@@ -16,9 +16,10 @@ extern "C" void body(...);
 // PRINT-LABEL: void foo(
 // DUMP-LABEL:  FunctionDecl {{.*}} foo
 void foo(int n) {
-  // PRINT:     #pragma omp split
+  // PRINT:     #pragma omp split counts(2, 3)
   // DUMP:      OMPSplitDirective
-  #pragma omp split
+  // DUMP: OMPCountsClause
+  #pragma omp split counts(2, 3)
   // PRINT: for (int i = 0; i < n; ++i)
   // DUMP:      ForStmt
   for (int i = 0; i < n; ++i)
diff --git a/clang/test/OpenMP/split_counts_verify.c 
b/clang/test/OpenMP/split_counts_verify.c
new file mode 100644
index 0000000000000..4b2ec2ca20bcd
--- /dev/null
+++ b/clang/test/OpenMP/split_counts_verify.c
@@ -0,0 +1,44 @@
+/*
+ * Verify #pragma omp split counts(c1, c2, ...) at AST, IR, and runtime.
+ * counts(3, 5, 2) splits 10 iterations into: [0..3), [3..8), [8..10).
+ * Sum 0+1+...+9 = 45.
+ */
+// REQUIRES: x86-registered-target
+
+// 1) Syntax and semantics only
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 
-fsyntax-only -verify %s
+// expected-no-diagnostics
+
+// 2) AST dump should show OMPSplitDirective with OMPCountsClause node.
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 
-ast-dump %s 2>&1 | FileCheck %s --check-prefix=AST
+
+// 3) Emit LLVM: three sequential loops (multiple phi/br for loop structure)
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fopenmp -fopenmp-version=60 
-emit-llvm %s -o - 2>&1 | FileCheck %s --check-prefix=IR
+
+// 4) Compile and run: exit 0 if sum == 45
+// RUN: %clang -fopenmp -fopenmp-version=60 -O0 %s -o %t.exe
+// RUN: %t.exe
+
+int main(void) {
+  const int n = 10;
+  int sum = 0;
+
+#pragma omp split counts(3, 5, 2)
+  for (int i = 0; i < n; ++i) {
+    sum += i;
+  }
+
+  return (sum == 45) ? 0 : 1;
+}
+
+// AST: OMPSplitDirective
+// AST: OMPCountsClause
+
+// IR: define
+// IR: .split.iv.0
+// IR: icmp slt i32 {{.*}}, 3
+// IR: .split.iv.1
+// IR: icmp slt i32 {{.*}}, 8
+// IR: .split.iv.2
+// IR: icmp slt i32 {{.*}}, 10
+// IR: icmp eq i32 {{.*}}, 45
diff --git a/clang/test/OpenMP/split_simple_test.c 
b/clang/test/OpenMP/split_simple_test.c
index 62dbc1cd861e5..021759e6a70e6 100644
--- a/clang/test/OpenMP/split_simple_test.c
+++ b/clang/test/OpenMP/split_simple_test.c
@@ -1,16 +1,15 @@
 /*
- * Simple test for #pragma omp split: one canonical for-loop is transformed
- * into two loops (first half and second half of iterations).
+ * Simple test for #pragma omp split counts: one for-loop is transformed
+ * into two loops (counts(5, 5) => [0..5) and [5..10)).
  */
 // Verify the split directive compiles and emits IR (two sequential loops).
-// RUN: %clang_cc1 -fopenmp -fopenmp-version=60 -triple x86_64-unknown-unknown
-// -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=60 -triple x86_64-unknown-unknown 
-emit-llvm %s -o - | FileCheck %s
 
 int main(void) {
   const int n = 10;
   int sum = 0;
 
-#pragma omp split
+#pragma omp split counts(5, 5)
   for (int i = 0; i < n; ++i) {
     sum += i;
   }
@@ -20,5 +19,9 @@ int main(void) {
 
 // CHECK: define
 // CHECK: load
-// Split produces two sequential loops; ensure we have loop structure in IR.
+// Split produces two sequential loops (counts(5, 5) => bounds 5, 10).
+// CHECK: .split.iv
+// CHECK: icmp slt i32 {{.*}}, 5
+// CHECK: .split.iv
+// CHECK: icmp slt i32 {{.*}}, 10
 // CHECK: br i1

>From c4c7d8ff1c57b4d537b0a43b9a86cca97c2ee24c Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Thu, 19 Mar 2026 06:49:54 -0400
Subject: [PATCH 14/15] test_fix

---
 clang/test/OpenMP/split_counts_verify.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/clang/test/OpenMP/split_counts_verify.c 
b/clang/test/OpenMP/split_counts_verify.c
index 4b2ec2ca20bcd..3eec02f974e07 100644
--- a/clang/test/OpenMP/split_counts_verify.c
+++ b/clang/test/OpenMP/split_counts_verify.c
@@ -15,9 +15,6 @@
 // 3) Emit LLVM: three sequential loops (multiple phi/br for loop structure)
 // RUN: %clang_cc1 -triple x86_64-unknown-unknown -fopenmp -fopenmp-version=60 
-emit-llvm %s -o - 2>&1 | FileCheck %s --check-prefix=IR
 
-// 4) Compile and run: exit 0 if sum == 45
-// RUN: %clang -fopenmp -fopenmp-version=60 -O0 %s -o %t.exe
-// RUN: %t.exe
 
 int main(void) {
   const int n = 10;

>From 561c0f2174fe3ef4406a4a7a4e4c4568d8bd70f3 Mon Sep 17 00:00:00 2001
From: amtiwari <[email protected]>
Date: Fri, 20 Mar 2026 04:36:15 -0400
Subject: [PATCH 15/15] revised

---
 clang/include/clang/AST/StmtOpenMP.h  |  2 +-
 clang/lib/AST/OpenMPClause.cpp        | 10 +++-------
 clang/lib/CodeGen/CGStmtOpenMP.cpp    |  2 +-
 clang/lib/Sema/SemaOpenMP.cpp         | 15 ++++-----------
 clang/test/OpenMP/split_ast_print.cpp |  2 ++
 5 files changed, 11 insertions(+), 20 deletions(-)

diff --git a/clang/include/clang/AST/StmtOpenMP.h 
b/clang/include/clang/AST/StmtOpenMP.h
index bdaf73c0a6607..3d8962afa2b7c 100644
--- a/clang/include/clang/AST/StmtOpenMP.h
+++ b/clang/include/clang/AST/StmtOpenMP.h
@@ -6067,7 +6067,7 @@ class OMPFuseDirective final
 
 /// Represents the '#pragma omp split' loop transformation directive.
 ///
-/// \code{c}
+/// \code{.c}
 ///   #pragma omp split
 ///   for (int i = 0; i < n; ++i)
 ///     ...
diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp
index ab6fa1c673411..f6c03eba668c8 100644
--- a/clang/lib/AST/OpenMPClause.cpp
+++ b/clang/lib/AST/OpenMPClause.cpp
@@ -2005,13 +2005,9 @@ void 
OMPClausePrinter::VisitOMPSizesClause(OMPSizesClause *Node) {
 
 void OMPClausePrinter::VisitOMPCountsClause(OMPCountsClause *Node) {
   OS << "counts(";
-  bool First = true;
-  for (auto *Count : Node->getCountsRefs()) {
-    if (!First)
-      OS << ", ";
-    Count->printPretty(OS, nullptr, Policy, 0);
-    First = false;
-  }
+  llvm::interleaveComma(Node->getCountsRefs(), OS, [&](const Expr *E) {
+    E->printPretty(OS, nullptr, Policy, 0);
+  });
   OS << ")";
 }
 
diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp 
b/clang/lib/CodeGen/CGStmtOpenMP.cpp
index 4be245ff9728f..499560223213a 100644
--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -2982,7 +2982,7 @@ void CodeGenFunction::EmitOMPReverseDirective(const 
OMPReverseDirective &S) {
 }
 
 void CodeGenFunction::EmitOMPSplitDirective(const OMPSplitDirective &S) {
-  // Emit the de-sugared statement (the two split loops).
+  // Emit the de-sugared statement (the split loops).
   OMPTransformDirectiveScopeRAII SplitScope(*this, &S);
   EmitStmt(S.getTransformedStmt());
 }
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 01c799017e501..3efb75200b48a 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -15948,24 +15948,19 @@ StmtResult 
SemaOpenMP::ActOnOpenMPSplitDirective(ArrayRef<OMPClause *> Clauses,
   // Collect constant count values from the counts clause
   SmallVector<uint64_t, 4> CountValues;
   for (Expr *CountExpr : CountsClause->getCountsRefs()) {
-    if (!CountExpr) {
+    if (!CountExpr)
       return OMPSplitDirective::Create(Context, StartLoc, EndLoc, Clauses,
                                        NumLoops, AStmt, nullptr, nullptr);
-    }
     std::optional<llvm::APSInt> OptVal =
         CountExpr->getIntegerConstantExpr(Context);
-    if (!OptVal || OptVal->isNegative()) {
+    if (!OptVal || OptVal->isNegative())
       return OMPSplitDirective::Create(Context, StartLoc, EndLoc, Clauses,
                                        NumLoops, AStmt, nullptr, nullptr);
-    }
     CountValues.push_back(OptVal->getZExtValue());
   }
 
-  if (CountValues.empty()) {
-    Diag(CountsClause->getBeginLoc(), diag::err_omp_unexpected_clause_value)
-        << "at least one non-negative integer expression" << "counts";
+  if (CountValues.empty())
     return StmtError();
-  }
 
   // Cumulative segment starts: Starts[0]=0,
   // Starts[j]=Starts[j-1]+CountValues[j-1]. Example: CountValues [3,5,2] →
@@ -15985,9 +15980,7 @@ StmtResult 
SemaOpenMP::ActOnOpenMPSplitDirective(ArrayRef<OMPClause *> Clauses,
     // Segment IV: .split.iv.<Seg>.<OrigVarName>, init to StartVal, bound by
     // EndVal.
     SmallString<64> IVName(".split.iv.");
-    IVName += Twine(Seg).str();
-    IVName += ".";
-    IVName += OrigVarName;
+    IVName += (Twine(Seg) + "." + OrigVarName).str();
     VarDecl *IVDecl = buildVarDecl(SemaRef, {}, IVTy, IVName, nullptr, 
OrigVar);
     auto MakeIVRef = [&SemaRef = this->SemaRef, IVDecl, IVTy, OrigVarLoc]() {
       return buildDeclRefExpr(SemaRef, IVDecl, IVTy, OrigVarLoc);
diff --git a/clang/test/OpenMP/split_ast_print.cpp 
b/clang/test/OpenMP/split_ast_print.cpp
index 07dd7b28e1a13..2d53f589500cd 100644
--- a/clang/test/OpenMP/split_ast_print.cpp
+++ b/clang/test/OpenMP/split_ast_print.cpp
@@ -15,6 +15,8 @@ extern "C" void body(...);
 
 // PRINT-LABEL: void foo(
 // DUMP-LABEL:  FunctionDecl {{.*}} foo
+// OpenMP spec: one counts item may be the keyword (e.g. omp_fill) when loop
+// bound is not constant; this test uses literal counts for ast-print check.
 void foo(int n) {
   // PRINT:     #pragma omp split counts(2, 3)
   // DUMP:      OMPSplitDirective

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

Reply via email to