[clang] [llvm] [Clang][OpenMP] Implement Loop splitting `#pragma omp split` directive (PR #183261)
Amit Tiwari via llvm-commits
llvm-commits at lists.llvm.org
Fri Mar 27 04:36:25 PDT 2026
https://github.com/amitamd7 updated https://github.com/llvm/llvm-project/pull/183261
>From ce654aab7da2106a8a2bac487d10da5f951f76cd Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Wed, 25 Feb 2026 03:48:23 -0500
Subject: [PATCH 01/22] 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 61d76bafdfcde..e166894ea024b 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -244,6 +244,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 4e8fe1d32d42e..e9fd13bfd0f79 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1962,6 +1962,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 d1dddf76152ec..214366c3be59b 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -1435,6 +1435,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 bbae18066d458078714d0148c5fef2761f05ca8c Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Wed, 25 Feb 2026 06:51:02 -0500
Subject: [PATCH 02/22] 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 ce6ad723191e0..0802871c4a720 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -3202,6 +3202,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 9e90bbd81eb88acf41a0abe4990de7eb04094e9e Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Fri, 6 Mar 2026 06:29:06 -0500
Subject: [PATCH 03/22] 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 4d364fdcd5502..e0b930ba0a21a 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 e8c1f8a8ecb5f..6f929c060ebe7 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 8ae5df367e0dd..b22c500048854 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9759,6 +9759,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 801eed43c2440..cb7aa7a1b7cf1 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2529,6 +2529,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 934a95df1be7e..4612cd2a7944d 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2546,6 +2546,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 d6b0bde3263629e5dff9b48a2cfe37bce2576773 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Mon, 9 Mar 2026 07:42:22 -0400
Subject: [PATCH 04/22] 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 a923002bec9b6..ec81f4b639faf 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -230,6 +230,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 96880d7012eb99369e9315e18b15da66efca1e0f Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Mon, 9 Mar 2026 08:16:47 -0400
Subject: [PATCH 05/22] 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 56079ea8e1bf8..40d530a1f3925 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1508,6 +1508,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
case Stmt::OMPUnrollDirectiveClass:
case Stmt::OMPReverseDirectiveClass:
case Stmt::OMPInterchangeDirectiveClass:
+ case Stmt::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 cb7aa7a1b7cf1..79a6a03b3f276 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -3691,6 +3691,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 e9522a7975515..b6d2c96627520 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1812,6 +1812,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 d31d2c0c9bb67..dd815147c48dc 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -697,6 +697,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 4675b2b5104ea98523d95463b963bf172d6f3c7f Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Mon, 9 Mar 2026 12:13:02 -0400
Subject: [PATCH 06/22] 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 ec81f4b639faf..f178b85a9d0c8 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -231,7 +231,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 24e863bb0d610d73e0797e2ce40cbb33f673fa54 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Fri, 13 Mar 2026 09:48:29 -0400
Subject: [PATCH 07/22] 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 dcf1f4f1b4258..119bd68ff9814 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 f178b85a9d0c8..ec81f4b639faf 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -231,8 +231,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 990ec47488465..2acced1f0da1a 100644
--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -197,6 +197,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();
@@ -3203,6 +3205,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 0ff93d2ce7363..0d953747887fa 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3922,6 +3922,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 29397d67b5bcc..a0783a3737ad8 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -3432,6 +3432,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 0d3c7fc4907a2..9358316132ca4 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -4627,6 +4627,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:
@@ -6466,6 +6467,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);
@@ -15907,6 +15915,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 3ee37ed2dfc27..9016bc6e6e418 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 dd815147c48dc..242380c68c667 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -698,7 +698,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 15b73124610f82eaed3e814823533b5f5e5c701d Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Fri, 13 Mar 2026 10:21:40 -0400
Subject: [PATCH 08/22] 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 214366c3be59b..7e1a232d6bad7 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -1203,16 +1203,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 4042cbeb60fe9febcad4bcb8162bcde73527a638 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Fri, 13 Mar 2026 12:29:17 -0400
Subject: [PATCH 09/22] 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 b71f9ed2275e0..a90d48cf6d481 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -1453,6 +1453,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 c6757ef2abcc5222654186463c4bbdcc01b2961c Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Thu, 19 Mar 2026 04:32:37 -0400
Subject: [PATCH 10/22] 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 7e1a232d6bad7..0f2074c549c83 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";
@@ -1426,7 +1427,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 c42963573ca1d364f6cb9255c5a9a9516b05206a Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Thu, 19 Mar 2026 04:33:51 -0400
Subject: [PATCH 11/22] 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 08a3d88ee6a36..bd313d37cc4b5 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -6812,6 +6812,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 a0783a3737ad8..65345462ae740 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2424,6 +2424,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) {
@@ -2986,6 +2990,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;
@@ -3433,8 +3448,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 9358316132ca4..67466f5ad5f8f 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -6467,13 +6467,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);
@@ -15915,7 +15917,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();
@@ -15925,6 +15932,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(
@@ -15937,8 +15950,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");
@@ -15954,6 +15967,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);
@@ -15963,153 +15978,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(
@@ -18060,6 +18031,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 b22c500048854..bc78f64f0e095 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -1769,6 +1769,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,
@@ -10626,6 +10634,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 71ac338ad9aafa3b1d1afec7196d6eced41d6c98 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Thu, 19 Mar 2026 04:34:58 -0400
Subject: [PATCH 12/22] 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 6f929c060ebe7..c75652e5c1dd3 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 7c3a6fceb3623..29926d9361235 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -11394,6 +11394,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);
@@ -11807,6 +11812,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 79a6a03b3f276..fb81e4fefdebb 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -3693,9 +3693,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 9f05daaf65850..3ba4ec08bbcbd 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -8047,6 +8047,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 9016bc6e6e418..f1532d0b064b3 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 4e20c045f7c39a350aaecd1150a73e3f10d04ed7 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Thu, 19 Mar 2026 04:36:32 -0400
Subject: [PATCH 13/22] 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 acba3bce620a5af73065ee08f9d123a30bad626a Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Thu, 19 Mar 2026 06:49:54 -0400
Subject: [PATCH 14/22] 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 168af0dff0a22728434e36c656fdfe460905aa55 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Fri, 20 Mar 2026 04:36:15 -0400
Subject: [PATCH 15/22] 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 2acced1f0da1a..59d0e6825a975 100644
--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -3206,7 +3206,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 67466f5ad5f8f..8b097694454cc 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -15984,24 +15984,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] →
@@ -16021,9 +16016,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
>From 5bbb10db902de64e99abf3eaba9ab76b62c8af45 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Sat, 21 Mar 2026 10:08:20 -0400
Subject: [PATCH 16/22] omp_fill
---
.../clang/Basic/DiagnosticSemaKinds.td | 6 +
clang/include/clang/Sema/SemaOpenMP.h | 9 +
clang/lib/AST/OpenMPClause.cpp | 7 +
clang/lib/Parse/ParseOpenMP.cpp | 37 +++-
clang/lib/Sema/SemaOpenMP.cpp | 162 +++++++++++++++---
5 files changed, 198 insertions(+), 23 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index db1e3630435d0..56a1af0502bb9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11168,6 +11168,12 @@ def err_omp_bind_required_on_loop : Error<
"construct">;
def err_omp_loop_reduction_clause : Error<
"'reduction' clause not allowed with '#pragma omp loop bind(teams)'">;
+def err_omp_split_counts_multiple_omp_fill : Error<
+ "at most one 'omp_fill' may appear in the 'counts' clause">;
+def err_omp_split_counts_omp_fill_not_last : Error<
+ "'omp_fill' must be the last item in the 'counts' clause">;
+def err_omp_split_counts_omp_fill_no_trip : Error<
+ "'omp_fill' requires a computable loop iteration count">;
def warn_break_binds_to_switch : Warning<
"'break' is bound to loop, GCC binds it to switch">,
InGroup<GccCompat>;
diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h
index c4c1b2ad33f71..11df5816d2b35 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -42,6 +42,7 @@ class FunctionScopeInfo;
class DeclContext;
class DeclGroupRef;
+class EnumConstantDecl;
class ParsedAttr;
class Scope;
@@ -921,6 +922,8 @@ class SemaOpenMP : public SemaBase {
SourceLocation StartLoc,
SourceLocation LParenLoc,
SourceLocation EndLoc);
+ /// Build the OpenMP \c omp_fill placeholder for a \c counts clause.
+ ExprResult ActOnOpenMPCountsFillExpr(SourceLocation Loc);
/// Called on well-form 'permutation' clause after parsing its arguments.
OMPClause *ActOnOpenMPPermutationClause(ArrayRef<Expr *> PermExprs,
SourceLocation StartLoc,
@@ -1646,6 +1649,12 @@ class SemaOpenMP : public SemaBase {
/// Device number identifier specified by the context selector.
StringRef DeviceNumID;
+
+ /// Implicit enumerator used to represent \c omp_fill in \c counts clauses.
+ EnumConstantDecl *OMPFillCountMarker = nullptr;
+
+ EnumConstantDecl *getOrCreateOMPFillCountMarker();
+ bool isOMPFillCountExpr(const Expr *E) const;
};
} // namespace clang
diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp
index f6c03eba668c8..d68a53189ee65 100644
--- a/clang/lib/AST/OpenMPClause.cpp
+++ b/clang/lib/AST/OpenMPClause.cpp
@@ -15,6 +15,7 @@
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclOpenMP.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/ExprOpenMP.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/OpenMPKinds.h"
@@ -2006,6 +2007,12 @@ void OMPClausePrinter::VisitOMPSizesClause(OMPSizesClause *Node) {
void OMPClausePrinter::VisitOMPCountsClause(OMPCountsClause *Node) {
OS << "counts(";
llvm::interleaveComma(Node->getCountsRefs(), OS, [&](const Expr *E) {
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
+ if (const auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl()))
+ if (ECD->isImplicit() && ECD->getName() == "omp_fill") {
+ OS << "omp_fill";
+ return;
+ }
E->printPretty(OS, nullptr, Policy, 0);
});
OS << ")";
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 65345462ae740..b5864baea600d 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2993,9 +2993,42 @@ OMPClause *Parser::ParseOpenMPSizesClause() {
OMPClause *Parser::ParseOpenMPCountsClause() {
SourceLocation ClauseNameLoc, OpenLoc, CloseLoc;
SmallVector<Expr *, 4> ValExprs;
- if (ParseOpenMPExprListClause(OMPC_counts, ClauseNameLoc, OpenLoc, CloseLoc,
- ValExprs))
+
+ assert(getOpenMPClauseName(OMPC_counts) == PP.getSpelling(Tok) &&
+ "Expected parsing to start at clause name");
+ ClauseNameLoc = ConsumeToken();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_paren;
+ return nullptr;
+ }
+
+ do {
+ if (Tok.is(tok::identifier) &&
+ Tok.getIdentifierInfo()->getName() == "omp_fill") {
+ SourceLocation FillLoc = Tok.getLocation();
+ ConsumeToken();
+ ExprResult ER = Actions.OpenMP().ActOnOpenMPCountsFillExpr(FillLoc);
+ if (!ER.isUsable()) {
+ T.skipToEnd();
+ return nullptr;
+ }
+ ValExprs.push_back(ER.get());
+ } else {
+ ExprResult Val = ParseConstantExpression();
+ if (!Val.isUsable()) {
+ T.skipToEnd();
+ return nullptr;
+ }
+ ValExprs.push_back(Val.get());
+ }
+ } while (TryConsumeToken(tok::comma));
+
+ if (T.consumeClose())
return nullptr;
+ OpenLoc = T.getOpenLocation();
+ CloseLoc = T.getCloseLocation();
return Actions.OpenMP().ActOnOpenMPCountsClause(ValExprs, ClauseNameLoc,
OpenLoc, CloseLoc);
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 8b097694454cc..f20f8ae68cedb 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -6468,10 +6468,9 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
Res = ActOnOpenMPReverseDirective(AStmt, StartLoc, EndLoc);
break;
case OMPD_split: {
- const OMPCountsClause *CountsClause =
- OMPExecutableDirective::getSingleClause<OMPCountsClause>(
- ClausesWithImplicit);
- assert(CountsClause && "split directive requires counts clause");
+ assert(OMPExecutableDirective::getSingleClause<OMPCountsClause>(
+ ClausesWithImplicit) &&
+ "split directive requires counts clause");
Res =
ActOnOpenMPSplitDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc);
break;
@@ -15981,37 +15980,75 @@ StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(ArrayRef<OMPClause *> Clauses,
// Internal variable names.
std::string OrigVarName = OrigVar->getNameInfo().getAsString();
- // Collect constant count values from the counts clause
- SmallVector<uint64_t, 4> CountValues;
+ enum class SplitCountKind { Constant, Fill };
+ SmallVector<std::pair<SplitCountKind, uint64_t>, 4> Entries;
for (Expr *CountExpr : CountsClause->getCountsRefs()) {
if (!CountExpr)
return OMPSplitDirective::Create(Context, StartLoc, EndLoc, Clauses,
NumLoops, AStmt, nullptr, nullptr);
+ if (isOMPFillCountExpr(CountExpr)) {
+ Entries.push_back({SplitCountKind::Fill, 0});
+ continue;
+ }
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());
+ Entries.push_back({SplitCountKind::Constant, OptVal->getZExtValue()});
+ }
+
+ if (Entries.empty())
+ return StmtError();
+
+ unsigned NumFill = 0;
+ unsigned FillPos = 0;
+ for (unsigned I = 0; I < Entries.size(); ++I) {
+ if (Entries[I].first == SplitCountKind::Fill) {
+ ++NumFill;
+ FillPos = I;
+ }
+ }
+ if (NumFill > 1) {
+ Diag(CountsClause->getBeginLoc(),
+ diag::err_omp_split_counts_multiple_omp_fill);
+ return StmtError();
+ }
+ if (NumFill == 1 && FillPos != Entries.size() - 1) {
+ Diag(CountsClause->getBeginLoc(),
+ diag::err_omp_split_counts_omp_fill_not_last);
+ return StmtError();
}
- if (CountValues.empty())
+ Expr *NumIterExpr = LoopHelper.NumIterations;
+ if (NumFill == 1 && !NumIterExpr) {
+ Diag(CountsClause->getBeginLoc(),
+ diag::err_omp_split_counts_omp_fill_no_trip);
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]);
+ struct SplitSeg {
+ uint64_t Start;
+ bool EndIsTripCount;
+ uint64_t EndConst;
+ };
+ SmallVector<SplitSeg, 4> Segs;
+ uint64_t Cur = 0;
+ for (unsigned I = 0; I < Entries.size(); ++I) {
+ const auto &Ent = Entries[I];
+ if (Ent.first == SplitCountKind::Constant) {
+ uint64_t Nxt = Cur + Ent.second;
+ Segs.push_back({Cur, false, Nxt});
+ Cur = Nxt;
+ } else
+ Segs.push_back({Cur, true, 0});
+ }
- size_t NumSegments = CountValues.size();
+ size_t NumSegments = Segs.size();
SmallVector<Stmt *, 4> SplitLoops;
for (size_t Seg = 0; Seg < NumSegments; ++Seg) {
- uint64_t StartVal = Starts[Seg];
- uint64_t EndVal = Starts[Seg + 1];
+ uint64_t StartVal = Segs[Seg].Start;
// Segment IV: .split.iv.<Seg>.<OrigVarName>, init to StartVal, bound by
// EndVal.
@@ -16023,9 +16060,15 @@ StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(ArrayRef<OMPClause *> Clauses,
};
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);
+
+ Expr *EndBound = nullptr;
+ if (Segs[Seg].EndIsTripCount)
+ EndBound = NumIterExpr;
+ else {
+ llvm::APInt EndAP(IVWidth, Segs[Seg].EndConst, /*isSigned=*/false);
+ EndBound = IntegerLiteral::Create(Context, EndAP, IVTy, OrigVarLoc);
+ }
SemaRef.AddInitializerToDecl(IVDecl, StartLit, /*DirectInit=*/false);
StmtResult InitStmt = new (Context)
@@ -16034,7 +16077,7 @@ StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(ArrayRef<OMPClause *> Clauses,
return StmtError();
ExprResult CondExpr = SemaRef.BuildBinOp(
- CurScope, LoopHelper.Cond->getExprLoc(), BO_LT, MakeIVRef(), EndLit);
+ CurScope, LoopHelper.Cond->getExprLoc(), BO_LT, MakeIVRef(), EndBound);
if (!CondExpr.isUsable())
return StmtError();
@@ -18024,6 +18067,81 @@ OMPClause *SemaOpenMP::ActOnOpenMPSizesClause(ArrayRef<Expr *> SizeExprs,
SanitizedSizeExprs);
}
+EnumConstantDecl *SemaOpenMP::getOrCreateOMPFillCountMarker() {
+ if (OMPFillCountMarker)
+ return OMPFillCountMarker;
+
+ ASTContext &Ctx = getASTContext();
+ TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
+ Preprocessor &PP = SemaRef.PP;
+ IdentifierInfo *EnumII =
+ &PP.getIdentifierTable().get("__clang_omp_counts_fill_tag");
+ EnumDecl *ED = EnumDecl::Create(
+ Ctx, TU, SourceLocation{}, SourceLocation{}, EnumII, /*PrevDecl=*/nullptr,
+ /*IsScoped=*/false, /*IsScopedUsingClassTag=*/false, /*IsFixed=*/false);
+ ED->setImplicit(true);
+
+ QualType IntTy = Ctx.IntTy;
+ ED->setIntegerType(IntTy);
+ ED->setPromotionType(IntTy);
+
+ IdentifierInfo *FillII = &PP.getIdentifierTable().get("omp_fill");
+ llvm::APSInt Zero(Ctx.getIntWidth(IntTy),
+ /*isUnsigned=*/!IntTy->isSignedIntegerType());
+ Zero.setIsSigned(IntTy->isSignedIntegerType());
+ IntegerLiteral *IL =
+ IntegerLiteral::Create(Ctx, Zero, IntTy, SourceLocation{});
+ EnumConstantDecl *ECD = EnumConstantDecl::Create(Ctx, ED, SourceLocation{},
+ FillII, IntTy, IL, Zero);
+ ECD->setImplicit(true);
+
+ ED->addDecl(ECD);
+
+ llvm::SmallVector<Decl *, 1> Elements;
+ Elements.push_back(ECD);
+ unsigned NumNegativeBits = 0;
+ unsigned NumPositiveBits = 0;
+ Ctx.computeEnumBits(Elements, NumNegativeBits, NumPositiveBits);
+
+ ED->completeDefinition(IntTy, IntTy, NumPositiveBits, NumNegativeBits);
+
+ if (!SemaRef.getLangOpts().CPlusPlus)
+ ECD->setType(IntTy);
+ else {
+ QualType EnumTy = Ctx.getTagType(ElaboratedTypeKeyword::None,
+ /*Qualifier=*/std::nullopt, ED,
+ /*OwnsTag=*/false);
+ ECD->setType(EnumTy);
+ }
+
+ TU->addDecl(ED);
+
+ OMPFillCountMarker = ECD;
+ return ECD;
+}
+
+ExprResult SemaOpenMP::ActOnOpenMPCountsFillExpr(SourceLocation Loc) {
+ EnumConstantDecl *ECD = getOrCreateOMPFillCountMarker();
+ ASTContext &Ctx = getASTContext();
+ QualType T = ECD->getType();
+ return DeclRefExpr::Create(Ctx, NestedNameSpecifierLoc(), SourceLocation(),
+ ECD, /*RefersToEnclosingVariableOrCapture=*/false,
+ Loc, T, VK_PRValue, ECD);
+}
+
+bool SemaOpenMP::isOMPFillCountExpr(const Expr *E) const {
+ if (!E)
+ return false;
+ E = E->IgnoreParenImpCasts();
+ const auto *DRE = dyn_cast<DeclRefExpr>(E);
+ if (!DRE)
+ return false;
+ if (OMPFillCountMarker)
+ return DRE->getDecl() == OMPFillCountMarker;
+ const auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl());
+ return ECD && ECD->isImplicit() && ECD->getName() == "omp_fill";
+}
+
OMPClause *SemaOpenMP::ActOnOpenMPCountsClause(ArrayRef<Expr *> CountExprs,
SourceLocation StartLoc,
SourceLocation LParenLoc,
@@ -18033,6 +18151,8 @@ OMPClause *SemaOpenMP::ActOnOpenMPCountsClause(ArrayRef<Expr *> CountExprs,
for (Expr *&CountExpr : SanitizedCountExprs) {
if (!CountExpr)
continue;
+ if (isOMPFillCountExpr(CountExpr))
+ continue;
bool IsValid = isNonNegativeIntegerValue(CountExpr, SemaRef, OMPC_counts,
/*StrictlyPositive=*/false);
>From b05cb713babd6a65e58d44d9377ce534e958aa46 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Sat, 21 Mar 2026 10:09:51 -0400
Subject: [PATCH 17/22] omp_fill-test
---
clang/test/OpenMP/split_ast_print.cpp | 6 ++---
clang/test/OpenMP/split_no_fill_print.c | 34 +++++++++++++++++++++++++
2 files changed, 36 insertions(+), 4 deletions(-)
create mode 100644 clang/test/OpenMP/split_no_fill_print.c
diff --git a/clang/test/OpenMP/split_ast_print.cpp b/clang/test/OpenMP/split_ast_print.cpp
index 2d53f589500cd..4707d88e9328c 100644
--- a/clang/test/OpenMP/split_ast_print.cpp
+++ b/clang/test/OpenMP/split_ast_print.cpp
@@ -15,13 +15,11 @@ 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)
+ // PRINT: #pragma omp split counts(2, omp_fill)
// DUMP: OMPSplitDirective
// DUMP: OMPCountsClause
- #pragma omp split counts(2, 3)
+ #pragma omp split counts(2, omp_fill)
// PRINT: for (int i = 0; i < n; ++i)
// DUMP: ForStmt
for (int i = 0; i < n; ++i)
diff --git a/clang/test/OpenMP/split_no_fill_print.c b/clang/test/OpenMP/split_no_fill_print.c
new file mode 100644
index 0000000000000..017b4a27846d5
--- /dev/null
+++ b/clang/test/OpenMP/split_no_fill_print.c
@@ -0,0 +1,34 @@
+/* Simple split + counts without omp_fill: syntax, AST dump, ast-print, IR. */
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -fsyntax-only -verify %s
+// expected-no-diagnostics
+//
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -ast-dump %s | FileCheck %s --check-prefix=DUMP
+//
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -ast-print %s | FileCheck %s --check-prefix=PRINT
+//
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -O0 -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+
+void body(int);
+
+// PRINT-LABEL: void foo(
+// DUMP-LABEL: FunctionDecl {{.*}} foo
+void foo(int n) {
+ // PRINT: #pragma omp split counts(3, 7)
+ // DUMP: OMPSplitDirective
+ // DUMP-NEXT: |-OMPCountsClause
+ // DUMP-NEXT: | |-IntegerLiteral {{.*}} 'int' 3
+ // DUMP-NEXT: | `-IntegerLiteral {{.*}} 'int' 7
+ // DUMP-NEXT: {{.*}}`-ForStmt
+#pragma omp split counts(3, 7)
+ // PRINT: for (int i = 0; i < n; ++i)
+ for (int i = 0; i < n; ++i)
+ body(i);
+}
+
+// LLVM-LABEL: define {{.*}}void @foo(
+// LLVM: .split.iv.0.i
+// LLVM: icmp slt i32 {{.*}}, 3
+// LLVM: call void @body(
+// LLVM: store i32 3, ptr %.split.iv.1.i
+// LLVM: icmp slt i32 {{.*}}, 10
+// LLVM: call void @body(
>From 2a5838107c5cbba31d2d4c2f52d5ad369a96f85e Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Wed, 25 Mar 2026 03:48:57 -0400
Subject: [PATCH 18/22] ast-unittests
---
clang/include/clang/ASTMatchers/ASTMatchers.h | 20 ++++++
clang/lib/ASTMatchers/ASTMatchersInternal.cpp | 4 ++
clang/lib/ASTMatchers/Dynamic/Registry.cpp | 2 +
.../ASTMatchers/ASTMatchersNodeTest.cpp | 65 +++++++++++++++++++
clang/unittests/ASTMatchers/ASTMatchersTest.h | 14 ++++
5 files changed, 105 insertions(+)
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index e8e7643e0dddd..a3d82baa69819 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -8781,6 +8781,26 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt,
OMPTargetUpdateDirective>
ompTargetUpdateDirective;
+/// Matches any ``#pragma omp split`` executable directive.
+///
+/// Given
+///
+/// \code
+/// #pragma omp split counts(2, 3)
+/// for (int i = 0; i < n; ++i) {}
+/// \endcode
+///
+/// ``ompSplitDirective()`` matches the split directive.
+extern const internal::VariadicDynCastAllOfMatcher<Stmt, OMPSplitDirective>
+ ompSplitDirective;
+
+/// Matches OpenMP ``counts`` clause used by ``#pragma omp split``.
+///
+/// Given ``#pragma omp split counts(1, 2, omp_fill)``, ``ompCountsClause()``
+/// matches the ``counts`` clause node.
+extern const internal::VariadicDynCastAllOfMatcher<OMPClause, OMPCountsClause>
+ ompCountsClause;
+
/// Matches OpenMP ``default`` clause.
///
/// Given
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index d6860ca660987..5cbf134620e34 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -1139,6 +1139,10 @@ const internal::VariadicDynCastAllOfMatcher<Stmt, OMPExecutableDirective>
ompExecutableDirective;
const internal::VariadicDynCastAllOfMatcher<Stmt, OMPTargetUpdateDirective>
ompTargetUpdateDirective;
+const internal::VariadicDynCastAllOfMatcher<Stmt, OMPSplitDirective>
+ ompSplitDirective;
+const internal::VariadicDynCastAllOfMatcher<OMPClause, OMPCountsClause>
+ ompCountsClause;
const internal::VariadicDynCastAllOfMatcher<OMPClause, OMPDefaultClause>
ompDefaultClause;
const internal::VariadicDynCastAllOfMatcher<OMPClause, OMPFromClause>
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index f31684f93f6f3..a04070971f0eb 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -529,10 +529,12 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(objcTryStmt);
REGISTER_MATCHER(ofClass);
REGISTER_MATCHER(ofKind);
+ REGISTER_MATCHER(ompCountsClause);
REGISTER_MATCHER(ompDefaultClause);
REGISTER_MATCHER(ompFromClause);
REGISTER_MATCHER(ompToClause);
REGISTER_MATCHER(ompExecutableDirective);
+ REGISTER_MATCHER(ompSplitDirective);
REGISTER_MATCHER(ompTargetUpdateDirective);
REGISTER_MATCHER(on);
REGISTER_MATCHER(onImplicitObjectArgument);
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
index 7338ff5f302f6..b3b9b5cd5a54b 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -7,7 +7,9 @@
//===----------------------------------------------------------------------===//
#include "ASTMatchersTest.h"
+#include "clang/AST/OpenMPClause.h"
#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/StmtOpenMP.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
@@ -3103,6 +3105,69 @@ TEST(ASTMatchersTestOpenMP, OMPTargetUpdateDirective_CountExpression) {
}
}
+// OpenMP 6 split directive / counts clause
+TEST(ASTMatchersTestOpenMP, OMPSplitDirective) {
+ auto Matcher = stmt(ompSplitDirective(hasStructuredBlock(forStmt())));
+
+ StringRef SplitOk = R"(
+void f() {
+#pragma omp split counts(2, 3)
+ for (int i = 0; i < 10; ++i) {}
+}
+)";
+ EXPECT_TRUE(matchesWithOpenMP60(SplitOk, Matcher));
+
+ StringRef ParallelOnly = R"(
+void f() {
+#pragma omp parallel
+ ;
+}
+)";
+ EXPECT_TRUE(notMatchesWithOpenMP60(ParallelOnly, Matcher));
+}
+
+TEST(ASTMatchersTestOpenMP, OMPSplitDirective_HasCountsClause) {
+ auto Matcher = stmt(ompSplitDirective(hasAnyClause(ompCountsClause())));
+
+ StringRef Source0 = R"(
+void f() {
+#pragma omp split counts(2, omp_fill)
+ for (int i = 0; i < 10; ++i) {}
+}
+)";
+ EXPECT_TRUE(matchesWithOpenMP60(Source0, Matcher));
+}
+
+TEST(ASTMatchersTestOpenMP, OMPCountsClause_OmpFillOperand) {
+ StringRef Source0 = R"(
+void f() {
+#pragma omp split counts(1, omp_fill)
+ for (int i = 0; i < 10; ++i) {}
+}
+)";
+ auto AST = tooling::buildASTFromCodeWithArgs(
+ Source0, {"-std=gnu++11", "-target", "i386-unknown-unknown",
+ "-fopenmp=libomp", "-fopenmp-version=60"});
+ ASSERT_TRUE(AST);
+ auto Results = match(ompSplitDirective().bind("split"), AST->getASTContext());
+ ASSERT_EQ(Results.size(), 1u);
+ const auto *Dir = Results[0].getNodeAs<OMPSplitDirective>("split");
+ ASSERT_TRUE(Dir);
+ const OMPCountsClause *Counts = nullptr;
+ for (OMPClause *C : Dir->clauses()) {
+ if ((Counts = dyn_cast<OMPCountsClause>(C)))
+ break;
+ }
+ ASSERT_TRUE(Counts);
+ ASSERT_EQ(Counts->getNumCounts(), 2u);
+ const Expr *FillExpr = Counts->getCountsRefs()[1]->IgnoreParenImpCasts();
+ const auto *DRE = dyn_cast<DeclRefExpr>(FillExpr);
+ ASSERT_TRUE(DRE);
+ const auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl());
+ ASSERT_TRUE(ECD);
+ EXPECT_EQ(ECD->getName(), "omp_fill");
+}
+
TEST(ASTMatchersTest, Finder_DynamicOnlyAcceptsSomeMatchers) {
MatchFinder Finder;
EXPECT_TRUE(Finder.addDynamicMatcher(decl(), nullptr));
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTest.h b/clang/unittests/ASTMatchers/ASTMatchersTest.h
index c1d4daea2c9f1..8a1427d03d60e 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTest.h
+++ b/clang/unittests/ASTMatchers/ASTMatchersTest.h
@@ -289,6 +289,20 @@ testing::AssertionResult notMatchesWithOpenMP51(const Twine &Code,
{"-fopenmp=libomp", "-fopenmp-version=51"});
}
+template <typename T>
+testing::AssertionResult matchesWithOpenMP60(const Twine &Code,
+ const T &AMatcher) {
+ return matchesConditionally(Code, AMatcher, true,
+ {"-fopenmp=libomp", "-fopenmp-version=60"});
+}
+
+template <typename T>
+testing::AssertionResult notMatchesWithOpenMP60(const Twine &Code,
+ const T &AMatcher) {
+ return matchesConditionally(Code, AMatcher, false,
+ {"-fopenmp=libomp", "-fopenmp-version=60"});
+}
+
template <typename T>
testing::AssertionResult matchesWithFixedpoint(const std::string &Code,
const T &AMatcher) {
>From 4ee94b7290e20ddc072428b67a09ae1cde47c62a Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Wed, 25 Mar 2026 03:53:03 -0400
Subject: [PATCH 19/22] index-test
---
clang/test/Index/openmp-split.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
create mode 100644 clang/test/Index/openmp-split.c
diff --git a/clang/test/Index/openmp-split.c b/clang/test/Index/openmp-split.c
new file mode 100644
index 0000000000000..41ce0e1e87713
--- /dev/null
+++ b/clang/test/Index/openmp-split.c
@@ -0,0 +1,12 @@
+// RUN: c-index-test -test-load-source local %s -fopenmp=libomp -fopenmp-version=60 | FileCheck %s
+
+void test(void) {
+#pragma omp split counts(3, 7)
+ for (int i = 0; i < 20; i += 1)
+ ;
+}
+
+// CHECK: openmp-split.c:4:1: OMPSplitDirective= Extent=[4:1 - 4:31]
+// CHECK: openmp-split.c:4:26: IntegerLiteral= Extent=[4:26 - 4:27]
+// CHECK: openmp-split.c:4:29: IntegerLiteral= Extent=[4:29 - 4:30]
+// CHECK: openmp-split.c:5:3: ForStmt= Extent=[5:3 - 6:6]
>From eccb3bf2137d53b0286a3fed9db6989e8122962c Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Wed, 25 Mar 2026 03:54:06 -0400
Subject: [PATCH 20/22] ast-dump
---
clang/test/AST/ast-dump-openmp-split.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
create mode 100644 clang/test/AST/ast-dump-openmp-split.c
diff --git a/clang/test/AST/ast-dump-openmp-split.c b/clang/test/AST/ast-dump-openmp-split.c
new file mode 100644
index 0000000000000..e4eb51becd54a
--- /dev/null
+++ b/clang/test/AST/ast-dump-openmp-split.c
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fopenmp -fopenmp-version=60 -ast-dump %s | FileCheck %s
+//
+// OMPSplitDirective / OMPCountsClause;
+
+void body(int);
+
+void test(void) {
+#pragma omp split counts(3, 7)
+ for (int i = 0; i < 10; ++i)
+ body(i);
+}
+
+// CHECK: OMPSplitDirective
+// CHECK: OMPCountsClause
+// CHECK: IntegerLiteral{{.*}}3
+// CHECK: IntegerLiteral{{.*}}7
+// CHECK: ForStmt
+// CHECK: <<<NULL>>>
+// CHECK: CallExpr
>From 3c74f9a85795fe6eb2dd5b6f59d830fef72c9896 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Wed, 25 Mar 2026 03:55:19 -0400
Subject: [PATCH 21/22] format-test
---
clang/unittests/ASTMatchers/ASTMatchersTest.h | 2 +-
clang/unittests/Format/FormatTest.cpp | 5 +++++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTest.h b/clang/unittests/ASTMatchers/ASTMatchersTest.h
index 8a1427d03d60e..932e75360405b 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTest.h
+++ b/clang/unittests/ASTMatchers/ASTMatchersTest.h
@@ -291,7 +291,7 @@ testing::AssertionResult notMatchesWithOpenMP51(const Twine &Code,
template <typename T>
testing::AssertionResult matchesWithOpenMP60(const Twine &Code,
- const T &AMatcher) {
+ const T &AMatcher) {
return matchesConditionally(Code, AMatcher, true,
{"-fopenmp=libomp", "-fopenmp-version=60"});
}
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 2701a7fca7346..c0e09861b178b 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -23054,6 +23054,11 @@ TEST_F(FormatTest, UnderstandsPragmaOmpTarget) {
getLLVMStyleWithColumns(26));
}
+TEST_F(FormatTest, UnderstandsPragmaOmpSplit) {
+ verifyFormat("#pragma omp split counts(2, 3)");
+ verifyFormat("#pragma omp split counts(2, omp_fill)");
+}
+
TEST_F(FormatTest, UnderstandPragmaOption) {
verifyFormat("#pragma option -C -A");
>From 5d7f5f3fbb9907af7d1b87a0f3deabec0966a628 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Fri, 27 Mar 2026 07:27:48 -0400
Subject: [PATCH 22/22] revised
---
clang/include/clang/AST/OpenMPClause.h | 21 +-
clang/include/clang/AST/StmtOpenMP.h | 5 +-
clang/include/clang/ASTMatchers/ASTMatchers.h | 2 +-
.../clang/Basic/DiagnosticSemaKinds.td | 8 +-
clang/include/clang/Sema/SemaOpenMP.h | 12 +-
clang/lib/AST/OpenMPClause.cpp | 32 ++-
clang/lib/Parse/ParseOpenMP.cpp | 20 +-
clang/lib/Sema/SemaOpenMP.cpp | 247 +++++++-----------
clang/lib/Sema/TreeTransform.h | 11 +-
clang/lib/Serialization/ASTReader.cpp | 2 +
clang/lib/Serialization/ASTWriter.cpp | 2 +
clang/test/AST/ast-dump-openmp-split.c | 4 +-
clang/test/Index/openmp-split.c | 5 +-
clang/test/OpenMP/split_counts_verify.c | 7 +-
...split_no_fill_print.c => split_omp_fill.c} | 10 +-
.../{split_simple_test.c => split_test.c} | 8 +-
.../ASTMatchers/ASTMatchersNodeTest.cpp | 11 +-
clang/unittests/Format/FormatTest.cpp | 5 -
18 files changed, 180 insertions(+), 232 deletions(-)
rename clang/test/OpenMP/{split_no_fill_print.c => split_omp_fill.c} (81%)
rename clang/test/OpenMP/{split_simple_test.c => split_test.c} (72%)
diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h
index dbc22e23c3704..d330f7042fc95 100644
--- a/clang/include/clang/AST/OpenMPClause.h
+++ b/clang/include/clang/AST/OpenMPClause.h
@@ -39,6 +39,7 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/TrailingObjects.h"
#include <cassert>
+#include <climits>
#include <cstddef>
#include <iterator>
#include <utility>
@@ -1026,7 +1027,7 @@ class OMPSizesClause final
/// This represents the 'counts' clause in the '#pragma omp split' directive.
///
/// \code
-/// #pragma omp split counts(3, 5, 2)
+/// #pragma omp split counts(3, omp_fill, 2)
/// for (int i = 0; i < n; ++i) { ... }
/// \endcode
class OMPCountsClause final
@@ -1041,10 +1042,16 @@ class OMPCountsClause final
/// Number of count expressions in the clause.
unsigned NumCounts;
+ /// 0-based index of the omp_fill list item, or UINT_MAX if absent.
+ unsigned OmpFillIndex;
+
+ /// Source location of the omp_fill keyword.
+ SourceLocation OmpFillLoc;
+
/// Build an empty clause.
explicit OMPCountsClause(int NumCounts)
: OMPClause(llvm::omp::OMPC_counts, SourceLocation(), SourceLocation()),
- NumCounts(NumCounts) {}
+ NumCounts(NumCounts), OmpFillIndex(UINT_MAX) {}
public:
/// Build a 'counts' AST node.
@@ -1056,8 +1063,8 @@ class OMPCountsClause final
/// \param Counts Content of the clause.
static OMPCountsClause *Create(const ASTContext &C, SourceLocation StartLoc,
SourceLocation LParenLoc,
- SourceLocation EndLoc,
- ArrayRef<Expr *> Counts);
+ SourceLocation EndLoc, ArrayRef<Expr *> Counts,
+ unsigned FillIdx, SourceLocation FillLoc);
/// Build an empty 'counts' AST node for deserialization.
///
@@ -1074,6 +1081,12 @@ class OMPCountsClause final
/// Returns the number of list items.
unsigned getNumCounts() const { return NumCounts; }
+ unsigned getOmpFillIndex() const { return OmpFillIndex; }
+ SourceLocation getOmpFillLoc() const { return OmpFillLoc; }
+ bool hasOmpFill() const { return OmpFillIndex != UINT_MAX; }
+ void setOmpFillIndex(unsigned Idx) { OmpFillIndex = Idx; }
+ void setOmpFillLoc(SourceLocation Loc) { OmpFillLoc = Loc; }
+
/// Returns the count expressions.
MutableArrayRef<Expr *> getCountsRefs() {
return getTrailingObjects(NumCounts);
diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h
index 3d8962afa2b7c..dbc76e7df8ecd 100644
--- a/clang/include/clang/AST/StmtOpenMP.h
+++ b/clang/include/clang/AST/StmtOpenMP.h
@@ -6068,14 +6068,15 @@ class OMPFuseDirective final
/// Represents the '#pragma omp split' loop transformation directive.
///
/// \code{.c}
-/// #pragma omp split
+/// #pragma omp split counts(3, omp_fill, 2)
/// 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.
+/// into multiple contiguous ranges. The \c counts clause is required and
+/// exactly one list item must be \c omp_fill.
class OMPSplitDirective final
: public OMPCanonicalLoopNestTransformationDirective {
friend class ASTStmtReader;
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index a3d82baa69819..87b6dbefa7a62 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -8786,7 +8786,7 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt,
/// Given
///
/// \code
-/// #pragma omp split counts(2, 3)
+/// #pragma omp split counts(2, omp_fill)
/// for (int i = 0; i < n; ++i) {}
/// \endcode
///
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 56a1af0502bb9..362fa77af5581 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11168,12 +11168,8 @@ def err_omp_bind_required_on_loop : Error<
"construct">;
def err_omp_loop_reduction_clause : Error<
"'reduction' clause not allowed with '#pragma omp loop bind(teams)'">;
-def err_omp_split_counts_multiple_omp_fill : Error<
- "at most one 'omp_fill' may appear in the 'counts' clause">;
-def err_omp_split_counts_omp_fill_not_last : Error<
- "'omp_fill' must be the last item in the 'counts' clause">;
-def err_omp_split_counts_omp_fill_no_trip : Error<
- "'omp_fill' requires a computable loop iteration count">;
+def err_omp_split_counts_not_one_omp_fill : Error<
+ "exactly one 'omp_fill' must appear in the 'counts' clause">;
def warn_break_binds_to_switch : Warning<
"'break' is bound to loop, GCC binds it to switch">,
InGroup<GccCompat>;
diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h
index 11df5816d2b35..38969abd786b3 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -921,9 +921,9 @@ class SemaOpenMP : public SemaBase {
OMPClause *ActOnOpenMPCountsClause(ArrayRef<Expr *> CountExprs,
SourceLocation StartLoc,
SourceLocation LParenLoc,
- SourceLocation EndLoc);
- /// Build the OpenMP \c omp_fill placeholder for a \c counts clause.
- ExprResult ActOnOpenMPCountsFillExpr(SourceLocation Loc);
+ SourceLocation EndLoc, unsigned FillIdx,
+ SourceLocation FillLoc,
+ unsigned FillCount);
/// Called on well-form 'permutation' clause after parsing its arguments.
OMPClause *ActOnOpenMPPermutationClause(ArrayRef<Expr *> PermExprs,
SourceLocation StartLoc,
@@ -1649,12 +1649,6 @@ class SemaOpenMP : public SemaBase {
/// Device number identifier specified by the context selector.
StringRef DeviceNumID;
-
- /// Implicit enumerator used to represent \c omp_fill in \c counts clauses.
- EnumConstantDecl *OMPFillCountMarker = nullptr;
-
- EnumConstantDecl *getOrCreateOMPFillCountMarker();
- bool isOMPFillCountExpr(const Expr *E) const;
};
} // namespace clang
diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp
index d68a53189ee65..607f73d41f98f 100644
--- a/clang/lib/AST/OpenMPClause.cpp
+++ b/clang/lib/AST/OpenMPClause.cpp
@@ -987,16 +987,18 @@ 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 *
+OMPCountsClause::Create(const ASTContext &C, SourceLocation StartLoc,
+ SourceLocation LParenLoc, SourceLocation EndLoc,
+ ArrayRef<Expr *> Counts, unsigned FillIdx,
+ SourceLocation FillLoc) {
OMPCountsClause *Clause = CreateEmpty(C, Counts.size());
Clause->setLocStart(StartLoc);
Clause->setLParenLoc(LParenLoc);
Clause->setLocEnd(EndLoc);
Clause->setCountsRefs(Counts);
+ Clause->setOmpFillIndex(FillIdx);
+ Clause->setOmpFillLoc(FillLoc);
return Clause;
}
@@ -2006,15 +2008,17 @@ void OMPClausePrinter::VisitOMPSizesClause(OMPSizesClause *Node) {
void OMPClausePrinter::VisitOMPCountsClause(OMPCountsClause *Node) {
OS << "counts(";
- llvm::interleaveComma(Node->getCountsRefs(), OS, [&](const Expr *E) {
- if (const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
- if (const auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl()))
- if (ECD->isImplicit() && ECD->getName() == "omp_fill") {
- OS << "omp_fill";
- return;
- }
- E->printPretty(OS, nullptr, Policy, 0);
- });
+ unsigned FillIdx = Node->getOmpFillIndex();
+ ArrayRef<Expr *> Refs = Node->getCountsRefs();
+ for (unsigned I = 0, N = Refs.size(); I < N; ++I) {
+ if (I)
+ OS << ", ";
+ if (I == FillIdx) {
+ OS << "omp_fill";
+ } else {
+ Refs[I]->printPretty(OS, nullptr, Policy, 0);
+ }
+ }
OS << ")";
}
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index b5864baea600d..d529139a1907c 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -28,6 +28,7 @@
#include "llvm/Frontend/OpenMP/DirectiveNameParser.h"
#include "llvm/Frontend/OpenMP/OMPAssume.h"
#include "llvm/Frontend/OpenMP/OMPContext.h"
+#include <climits>
#include <optional>
using namespace clang;
@@ -2993,6 +2994,9 @@ OMPClause *Parser::ParseOpenMPSizesClause() {
OMPClause *Parser::ParseOpenMPCountsClause() {
SourceLocation ClauseNameLoc, OpenLoc, CloseLoc;
SmallVector<Expr *, 4> ValExprs;
+ unsigned FillIdx = UINT_MAX;
+ unsigned FillCount = 0;
+ SourceLocation FillLoc;
assert(getOpenMPClauseName(OMPC_counts) == PP.getSpelling(Tok) &&
"Expected parsing to start at clause name");
@@ -3007,14 +3011,12 @@ OMPClause *Parser::ParseOpenMPCountsClause() {
do {
if (Tok.is(tok::identifier) &&
Tok.getIdentifierInfo()->getName() == "omp_fill") {
- SourceLocation FillLoc = Tok.getLocation();
+ if (FillCount == 0)
+ FillIdx = ValExprs.size();
+ ++FillCount;
+ FillLoc = Tok.getLocation();
ConsumeToken();
- ExprResult ER = Actions.OpenMP().ActOnOpenMPCountsFillExpr(FillLoc);
- if (!ER.isUsable()) {
- T.skipToEnd();
- return nullptr;
- }
- ValExprs.push_back(ER.get());
+ ValExprs.push_back(nullptr);
} else {
ExprResult Val = ParseConstantExpression();
if (!Val.isUsable()) {
@@ -3030,8 +3032,8 @@ OMPClause *Parser::ParseOpenMPCountsClause() {
OpenLoc = T.getOpenLocation();
CloseLoc = T.getCloseLocation();
- return Actions.OpenMP().ActOnOpenMPCountsClause(ValExprs, ClauseNameLoc,
- OpenLoc, CloseLoc);
+ return Actions.OpenMP().ActOnOpenMPCountsClause(
+ ValExprs, ClauseNameLoc, OpenLoc, CloseLoc, FillIdx, FillLoc, FillCount);
}
OMPClause *Parser::ParseOpenMPLoopRangeClause() {
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index f20f8ae68cedb..cc8e266e0d051 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -15980,78 +15980,101 @@ StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(ArrayRef<OMPClause *> Clauses,
// Internal variable names.
std::string OrigVarName = OrigVar->getNameInfo().getAsString();
- enum class SplitCountKind { Constant, Fill };
- SmallVector<std::pair<SplitCountKind, uint64_t>, 4> Entries;
- for (Expr *CountExpr : CountsClause->getCountsRefs()) {
+ unsigned FillIdx = CountsClause->getOmpFillIndex();
+ if (!CountsClause->hasOmpFill()) {
+ Diag(CountsClause->getBeginLoc(),
+ diag::err_omp_split_counts_not_one_omp_fill);
+ return StmtError();
+ }
+
+ unsigned NumItems = CountsClause->getNumCounts();
+ SmallVector<uint64_t, 4> CountValues(NumItems, 0);
+ ArrayRef<Expr *> Refs = CountsClause->getCountsRefs();
+ for (unsigned I = 0; I < NumItems; ++I) {
+ if (I == FillIdx)
+ continue;
+ Expr *CountExpr = Refs[I];
if (!CountExpr)
return OMPSplitDirective::Create(Context, StartLoc, EndLoc, Clauses,
NumLoops, AStmt, nullptr, nullptr);
- if (isOMPFillCountExpr(CountExpr)) {
- Entries.push_back({SplitCountKind::Fill, 0});
- continue;
- }
std::optional<llvm::APSInt> OptVal =
CountExpr->getIntegerConstantExpr(Context);
if (!OptVal || OptVal->isNegative())
return OMPSplitDirective::Create(Context, StartLoc, EndLoc, Clauses,
NumLoops, AStmt, nullptr, nullptr);
- Entries.push_back({SplitCountKind::Constant, OptVal->getZExtValue()});
- }
-
- if (Entries.empty())
- return StmtError();
-
- unsigned NumFill = 0;
- unsigned FillPos = 0;
- for (unsigned I = 0; I < Entries.size(); ++I) {
- if (Entries[I].first == SplitCountKind::Fill) {
- ++NumFill;
- FillPos = I;
- }
- }
- if (NumFill > 1) {
- Diag(CountsClause->getBeginLoc(),
- diag::err_omp_split_counts_multiple_omp_fill);
- return StmtError();
- }
- if (NumFill == 1 && FillPos != Entries.size() - 1) {
- Diag(CountsClause->getBeginLoc(),
- diag::err_omp_split_counts_omp_fill_not_last);
- return StmtError();
+ CountValues[I] = OptVal->getZExtValue();
}
Expr *NumIterExpr = LoopHelper.NumIterations;
- if (NumFill == 1 && !NumIterExpr) {
- Diag(CountsClause->getBeginLoc(),
- diag::err_omp_split_counts_omp_fill_no_trip);
- return StmtError();
- }
- struct SplitSeg {
- uint64_t Start;
- bool EndIsTripCount;
- uint64_t EndConst;
+ uint64_t RightSum = 0;
+ for (unsigned I = FillIdx + 1; I < NumItems; ++I)
+ RightSum += CountValues[I];
+
+ auto MakeIntLit = [&](uint64_t Val) {
+ return IntegerLiteral::Create(Context, llvm::APInt(IVWidth, Val), IVTy,
+ OrigVarLoc);
};
- SmallVector<SplitSeg, 4> Segs;
- uint64_t Cur = 0;
- for (unsigned I = 0; I < Entries.size(); ++I) {
- const auto &Ent = Entries[I];
- if (Ent.first == SplitCountKind::Constant) {
- uint64_t Nxt = Cur + Ent.second;
- Segs.push_back({Cur, false, Nxt});
- Cur = Nxt;
- } else
- Segs.push_back({Cur, true, 0});
- }
- size_t NumSegments = Segs.size();
+ size_t NumSegments = NumItems;
SmallVector<Stmt *, 4> SplitLoops;
+ uint64_t LeftAccum = 0;
+ uint64_t RightRemaining = RightSum;
+
for (size_t Seg = 0; Seg < NumSegments; ++Seg) {
- uint64_t StartVal = Segs[Seg].Start;
+ Expr *StartExpr = nullptr;
+ Expr *EndExpr = nullptr;
+
+ if (Seg < FillIdx) {
+ StartExpr = MakeIntLit(LeftAccum);
+ LeftAccum += CountValues[Seg];
+ EndExpr = MakeIntLit(LeftAccum);
+ } else if (Seg == FillIdx) {
+ StartExpr = MakeIntLit(LeftAccum);
+ if (RightRemaining == 0) {
+ EndExpr = NumIterExpr;
+ } else {
+ ExprResult Sub =
+ SemaRef.BuildBinOp(CurScope, OrigVarLoc, BO_Sub, NumIterExpr,
+ MakeIntLit(RightRemaining));
+ if (!Sub.isUsable())
+ return StmtError();
+ EndExpr = Sub.get();
+ }
+ } else {
+ if (RightRemaining == RightSum) {
+ if (RightSum == 0)
+ StartExpr = NumIterExpr;
+ else {
+ ExprResult Sub =
+ SemaRef.BuildBinOp(CurScope, OrigVarLoc, BO_Sub, NumIterExpr,
+ MakeIntLit(RightRemaining));
+ if (!Sub.isUsable())
+ return StmtError();
+ StartExpr = Sub.get();
+ }
+ } else {
+ ExprResult Sub =
+ SemaRef.BuildBinOp(CurScope, OrigVarLoc, BO_Sub, NumIterExpr,
+ MakeIntLit(RightRemaining));
+ if (!Sub.isUsable())
+ return StmtError();
+ StartExpr = Sub.get();
+ }
+ RightRemaining -= CountValues[Seg];
+ if (RightRemaining == 0)
+ EndExpr = NumIterExpr;
+ else {
+ ExprResult Sub =
+ SemaRef.BuildBinOp(CurScope, OrigVarLoc, BO_Sub, NumIterExpr,
+ MakeIntLit(RightRemaining));
+ if (!Sub.isUsable())
+ return StmtError();
+ EndExpr = Sub.get();
+ }
+ }
- // Segment IV: .split.iv.<Seg>.<OrigVarName>, init to StartVal, bound by
- // EndVal.
SmallString<64> IVName(".split.iv.");
IVName += (Twine(Seg) + "." + OrigVarName).str();
VarDecl *IVDecl = buildVarDecl(SemaRef, {}, IVTy, IVName, nullptr, OrigVar);
@@ -16059,25 +16082,14 @@ StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(ArrayRef<OMPClause *> Clauses,
return buildDeclRefExpr(SemaRef, IVDecl, IVTy, OrigVarLoc);
};
- llvm::APInt StartAP(IVWidth, StartVal, /*isSigned=*/false);
- auto *StartLit = IntegerLiteral::Create(Context, StartAP, IVTy, OrigVarLoc);
-
- Expr *EndBound = nullptr;
- if (Segs[Seg].EndIsTripCount)
- EndBound = NumIterExpr;
- else {
- llvm::APInt EndAP(IVWidth, Segs[Seg].EndConst, /*isSigned=*/false);
- EndBound = IntegerLiteral::Create(Context, EndAP, IVTy, OrigVarLoc);
- }
-
- SemaRef.AddInitializerToDecl(IVDecl, StartLit, /*DirectInit=*/false);
+ SemaRef.AddInitializerToDecl(IVDecl, StartExpr, /*DirectInit=*/false);
StmtResult InitStmt = new (Context)
DeclStmt(DeclGroupRef(IVDecl), OrigVarLocBegin, OrigVarLocEnd);
if (!InitStmt.isUsable())
return StmtError();
ExprResult CondExpr = SemaRef.BuildBinOp(
- CurScope, LoopHelper.Cond->getExprLoc(), BO_LT, MakeIVRef(), EndBound);
+ CurScope, LoopHelper.Cond->getExprLoc(), BO_LT, MakeIVRef(), EndExpr);
if (!CondExpr.isUsable())
return StmtError();
@@ -16086,7 +16098,6 @@ StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(ArrayRef<OMPClause *> Clauses,
if (!IncrExpr.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())
@@ -16106,10 +16117,9 @@ StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(ArrayRef<OMPClause *> Clauses,
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());
@@ -18067,91 +18077,17 @@ OMPClause *SemaOpenMP::ActOnOpenMPSizesClause(ArrayRef<Expr *> SizeExprs,
SanitizedSizeExprs);
}
-EnumConstantDecl *SemaOpenMP::getOrCreateOMPFillCountMarker() {
- if (OMPFillCountMarker)
- return OMPFillCountMarker;
-
- ASTContext &Ctx = getASTContext();
- TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
- Preprocessor &PP = SemaRef.PP;
- IdentifierInfo *EnumII =
- &PP.getIdentifierTable().get("__clang_omp_counts_fill_tag");
- EnumDecl *ED = EnumDecl::Create(
- Ctx, TU, SourceLocation{}, SourceLocation{}, EnumII, /*PrevDecl=*/nullptr,
- /*IsScoped=*/false, /*IsScopedUsingClassTag=*/false, /*IsFixed=*/false);
- ED->setImplicit(true);
-
- QualType IntTy = Ctx.IntTy;
- ED->setIntegerType(IntTy);
- ED->setPromotionType(IntTy);
-
- IdentifierInfo *FillII = &PP.getIdentifierTable().get("omp_fill");
- llvm::APSInt Zero(Ctx.getIntWidth(IntTy),
- /*isUnsigned=*/!IntTy->isSignedIntegerType());
- Zero.setIsSigned(IntTy->isSignedIntegerType());
- IntegerLiteral *IL =
- IntegerLiteral::Create(Ctx, Zero, IntTy, SourceLocation{});
- EnumConstantDecl *ECD = EnumConstantDecl::Create(Ctx, ED, SourceLocation{},
- FillII, IntTy, IL, Zero);
- ECD->setImplicit(true);
-
- ED->addDecl(ECD);
-
- llvm::SmallVector<Decl *, 1> Elements;
- Elements.push_back(ECD);
- unsigned NumNegativeBits = 0;
- unsigned NumPositiveBits = 0;
- Ctx.computeEnumBits(Elements, NumNegativeBits, NumPositiveBits);
-
- ED->completeDefinition(IntTy, IntTy, NumPositiveBits, NumNegativeBits);
-
- if (!SemaRef.getLangOpts().CPlusPlus)
- ECD->setType(IntTy);
- else {
- QualType EnumTy = Ctx.getTagType(ElaboratedTypeKeyword::None,
- /*Qualifier=*/std::nullopt, ED,
- /*OwnsTag=*/false);
- ECD->setType(EnumTy);
- }
-
- TU->addDecl(ED);
-
- OMPFillCountMarker = ECD;
- return ECD;
-}
-
-ExprResult SemaOpenMP::ActOnOpenMPCountsFillExpr(SourceLocation Loc) {
- EnumConstantDecl *ECD = getOrCreateOMPFillCountMarker();
- ASTContext &Ctx = getASTContext();
- QualType T = ECD->getType();
- return DeclRefExpr::Create(Ctx, NestedNameSpecifierLoc(), SourceLocation(),
- ECD, /*RefersToEnclosingVariableOrCapture=*/false,
- Loc, T, VK_PRValue, ECD);
-}
-
-bool SemaOpenMP::isOMPFillCountExpr(const Expr *E) const {
- if (!E)
- return false;
- E = E->IgnoreParenImpCasts();
- const auto *DRE = dyn_cast<DeclRefExpr>(E);
- if (!DRE)
- return false;
- if (OMPFillCountMarker)
- return DRE->getDecl() == OMPFillCountMarker;
- const auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl());
- return ECD && ECD->isImplicit() && ECD->getName() == "omp_fill";
-}
-
-OMPClause *SemaOpenMP::ActOnOpenMPCountsClause(ArrayRef<Expr *> CountExprs,
- SourceLocation StartLoc,
- SourceLocation LParenLoc,
- SourceLocation EndLoc) {
+OMPClause *SemaOpenMP::ActOnOpenMPCountsClause(
+ ArrayRef<Expr *> CountExprs, SourceLocation StartLoc,
+ SourceLocation LParenLoc, SourceLocation EndLoc, unsigned FillIdx,
+ SourceLocation FillLoc, unsigned FillCount) {
SmallVector<Expr *> SanitizedCountExprs(CountExprs);
- for (Expr *&CountExpr : SanitizedCountExprs) {
- if (!CountExpr)
+ for (unsigned I = 0; I < SanitizedCountExprs.size(); ++I) {
+ Expr *&CountExpr = SanitizedCountExprs[I];
+ if (I == FillIdx)
continue;
- if (isOMPFillCountExpr(CountExpr))
+ if (!CountExpr)
continue;
bool IsValid = isNonNegativeIntegerValue(CountExpr, SemaRef, OMPC_counts,
@@ -18165,8 +18101,13 @@ OMPClause *SemaOpenMP::ActOnOpenMPCountsClause(ArrayRef<Expr *> CountExprs,
CountExpr = nullptr;
}
+ if (FillCount != 1) {
+ Diag(FillCount == 0 ? StartLoc : FillLoc,
+ diag::err_omp_split_counts_not_one_omp_fill);
+ }
+
return OMPCountsClause::Create(getASTContext(), StartLoc, LParenLoc, EndLoc,
- SanitizedCountExprs);
+ SanitizedCountExprs, FillIdx, FillLoc);
}
OMPClause *SemaOpenMP::ActOnOpenMPPermutationClause(ArrayRef<Expr *> PermExprs,
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index bc78f64f0e095..1192a207b56bd 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -1772,9 +1772,11 @@ class TreeTransform {
OMPClause *RebuildOMPCountsClause(ArrayRef<Expr *> Counts,
SourceLocation StartLoc,
SourceLocation LParenLoc,
- SourceLocation EndLoc) {
- return getSema().OpenMP().ActOnOpenMPCountsClause(Counts, StartLoc,
- LParenLoc, EndLoc);
+ SourceLocation EndLoc, unsigned FillIdx,
+ SourceLocation FillLoc) {
+ unsigned FillCount = (FillIdx != UINT_MAX) ? 1 : 0;
+ return getSema().OpenMP().ActOnOpenMPCountsClause(
+ Counts, StartLoc, LParenLoc, EndLoc, FillIdx, FillLoc, FillCount);
}
/// Build a new OpenMP 'permutation' clause.
@@ -10652,7 +10654,8 @@ TreeTransform<Derived>::TransformOMPCountsClause(OMPCountsClause *C) {
}
return RebuildOMPCountsClause(TransformedCounts, C->getBeginLoc(),
- C->getLParenLoc(), C->getEndLoc());
+ C->getLParenLoc(), C->getEndLoc(),
+ C->getOmpFillIndex(), C->getOmpFillLoc());
}
template <typename Derived>
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 29926d9361235..78136e36983bc 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -11813,6 +11813,8 @@ void OMPClauseReader::VisitOMPSizesClause(OMPSizesClause *C) {
}
void OMPClauseReader::VisitOMPCountsClause(OMPCountsClause *C) {
+ C->setOmpFillIndex(Record.readInt());
+ C->setOmpFillLoc(Record.readSourceLocation());
for (Expr *&E : C->getCountsRefs())
E = Record.readSubExpr();
C->setLParenLoc(Record.readSourceLocation());
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 3ba4ec08bbcbd..96fb16c123ac6 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -8049,6 +8049,8 @@ void OMPClauseWriter::VisitOMPSizesClause(OMPSizesClause *C) {
void OMPClauseWriter::VisitOMPCountsClause(OMPCountsClause *C) {
Record.push_back(C->getNumCounts());
+ Record.push_back(C->getOmpFillIndex());
+ Record.AddSourceLocation(C->getOmpFillLoc());
for (Expr *Count : C->getCountsRefs())
Record.AddStmt(Count);
Record.AddSourceLocation(C->getLParenLoc());
diff --git a/clang/test/AST/ast-dump-openmp-split.c b/clang/test/AST/ast-dump-openmp-split.c
index e4eb51becd54a..821badae55e66 100644
--- a/clang/test/AST/ast-dump-openmp-split.c
+++ b/clang/test/AST/ast-dump-openmp-split.c
@@ -5,7 +5,7 @@
void body(int);
void test(void) {
-#pragma omp split counts(3, 7)
+#pragma omp split counts(3, omp_fill)
for (int i = 0; i < 10; ++i)
body(i);
}
@@ -13,7 +13,7 @@ void test(void) {
// CHECK: OMPSplitDirective
// CHECK: OMPCountsClause
// CHECK: IntegerLiteral{{.*}}3
-// CHECK: IntegerLiteral{{.*}}7
+// CHECK: <<<NULL>>>
// CHECK: ForStmt
// CHECK: <<<NULL>>>
// CHECK: CallExpr
diff --git a/clang/test/Index/openmp-split.c b/clang/test/Index/openmp-split.c
index 41ce0e1e87713..0c63f12297930 100644
--- a/clang/test/Index/openmp-split.c
+++ b/clang/test/Index/openmp-split.c
@@ -1,12 +1,11 @@
// RUN: c-index-test -test-load-source local %s -fopenmp=libomp -fopenmp-version=60 | FileCheck %s
void test(void) {
-#pragma omp split counts(3, 7)
+#pragma omp split counts(3, omp_fill)
for (int i = 0; i < 20; i += 1)
;
}
-// CHECK: openmp-split.c:4:1: OMPSplitDirective= Extent=[4:1 - 4:31]
+// CHECK: openmp-split.c:4:1: OMPSplitDirective= Extent=[4:1 - 4:38]
// CHECK: openmp-split.c:4:26: IntegerLiteral= Extent=[4:26 - 4:27]
-// CHECK: openmp-split.c:4:29: IntegerLiteral= Extent=[4:29 - 4:30]
// CHECK: openmp-split.c:5:3: ForStmt= Extent=[5:3 - 6:6]
diff --git a/clang/test/OpenMP/split_counts_verify.c b/clang/test/OpenMP/split_counts_verify.c
index 3eec02f974e07..6c19999045698 100644
--- a/clang/test/OpenMP/split_counts_verify.c
+++ b/clang/test/OpenMP/split_counts_verify.c
@@ -1,6 +1,6 @@
/*
* 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).
+ * counts(3, omp_fill, 2) with n=10 splits into: [0..3), [3..8), [8..10).
* Sum 0+1+...+9 = 45.
*/
// REQUIRES: x86-registered-target
@@ -12,7 +12,7 @@
// 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)
+// 3) Emit LLVM: three sequential loops
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fopenmp -fopenmp-version=60 -emit-llvm %s -o - 2>&1 | FileCheck %s --check-prefix=IR
@@ -20,7 +20,7 @@ int main(void) {
const int n = 10;
int sum = 0;
-#pragma omp split counts(3, 5, 2)
+#pragma omp split counts(3, omp_fill, 2)
for (int i = 0; i < n; ++i) {
sum += i;
}
@@ -38,4 +38,3 @@ int main(void) {
// 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_no_fill_print.c b/clang/test/OpenMP/split_omp_fill.c
similarity index 81%
rename from clang/test/OpenMP/split_no_fill_print.c
rename to clang/test/OpenMP/split_omp_fill.c
index 017b4a27846d5..075985bd6ec82 100644
--- a/clang/test/OpenMP/split_no_fill_print.c
+++ b/clang/test/OpenMP/split_omp_fill.c
@@ -1,4 +1,4 @@
-/* Simple split + counts without omp_fill: syntax, AST dump, ast-print, IR. */
+/* Split + counts with omp_fill: syntax, AST dump, ast-print, IR. */
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -fsyntax-only -verify %s
// expected-no-diagnostics
//
@@ -13,13 +13,13 @@ void body(int);
// PRINT-LABEL: void foo(
// DUMP-LABEL: FunctionDecl {{.*}} foo
void foo(int n) {
- // PRINT: #pragma omp split counts(3, 7)
+ // PRINT: #pragma omp split counts(3, omp_fill)
// DUMP: OMPSplitDirective
// DUMP-NEXT: |-OMPCountsClause
// DUMP-NEXT: | |-IntegerLiteral {{.*}} 'int' 3
- // DUMP-NEXT: | `-IntegerLiteral {{.*}} 'int' 7
+ // DUMP-NEXT: | `-{{.*}}
// DUMP-NEXT: {{.*}}`-ForStmt
-#pragma omp split counts(3, 7)
+#pragma omp split counts(3, omp_fill)
// PRINT: for (int i = 0; i < n; ++i)
for (int i = 0; i < n; ++i)
body(i);
@@ -30,5 +30,5 @@ void foo(int n) {
// LLVM: icmp slt i32 {{.*}}, 3
// LLVM: call void @body(
// LLVM: store i32 3, ptr %.split.iv.1.i
-// LLVM: icmp slt i32 {{.*}}, 10
+// LLVM: icmp slt i32 {{.*}}, %{{.*}}
// LLVM: call void @body(
diff --git a/clang/test/OpenMP/split_simple_test.c b/clang/test/OpenMP/split_test.c
similarity index 72%
rename from clang/test/OpenMP/split_simple_test.c
rename to clang/test/OpenMP/split_test.c
index 021759e6a70e6..af9686ce1f729 100644
--- a/clang/test/OpenMP/split_simple_test.c
+++ b/clang/test/OpenMP/split_test.c
@@ -1,6 +1,6 @@
/*
* Simple test for #pragma omp split counts: one for-loop is transformed
- * into two loops (counts(5, 5) => [0..5) and [5..10)).
+ * into two loops (counts(5, omp_fill) with n=10 => [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
@@ -9,7 +9,7 @@ int main(void) {
const int n = 10;
int sum = 0;
-#pragma omp split counts(5, 5)
+#pragma omp split counts(5, omp_fill)
for (int i = 0; i < n; ++i) {
sum += i;
}
@@ -19,9 +19,9 @@ int main(void) {
// CHECK: define
// CHECK: load
-// Split produces two sequential loops (counts(5, 5) => bounds 5, 10).
+// Split produces two sequential loops (counts(5, omp_fill) with n=10).
// CHECK: .split.iv
// CHECK: icmp slt i32 {{.*}}, 5
// CHECK: .split.iv
-// CHECK: icmp slt i32 {{.*}}, 10
+// CHECK: icmp slt
// CHECK: br i1
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
index b3b9b5cd5a54b..f725784361bf6 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -3111,7 +3111,7 @@ TEST(ASTMatchersTestOpenMP, OMPSplitDirective) {
StringRef SplitOk = R"(
void f() {
-#pragma omp split counts(2, 3)
+#pragma omp split counts(2, omp_fill)
for (int i = 0; i < 10; ++i) {}
}
)";
@@ -3160,12 +3160,9 @@ void f() {
}
ASSERT_TRUE(Counts);
ASSERT_EQ(Counts->getNumCounts(), 2u);
- const Expr *FillExpr = Counts->getCountsRefs()[1]->IgnoreParenImpCasts();
- const auto *DRE = dyn_cast<DeclRefExpr>(FillExpr);
- ASSERT_TRUE(DRE);
- const auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl());
- ASSERT_TRUE(ECD);
- EXPECT_EQ(ECD->getName(), "omp_fill");
+ EXPECT_TRUE(Counts->hasOmpFill());
+ EXPECT_EQ(Counts->getOmpFillIndex(), 1u);
+ EXPECT_FALSE(Counts->getCountsRefs()[1]);
}
TEST(ASTMatchersTest, Finder_DynamicOnlyAcceptsSomeMatchers) {
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index c0e09861b178b..2701a7fca7346 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -23054,11 +23054,6 @@ TEST_F(FormatTest, UnderstandsPragmaOmpTarget) {
getLLVMStyleWithColumns(26));
}
-TEST_F(FormatTest, UnderstandsPragmaOmpSplit) {
- verifyFormat("#pragma omp split counts(2, 3)");
- verifyFormat("#pragma omp split counts(2, omp_fill)");
-}
-
TEST_F(FormatTest, UnderstandPragmaOption) {
verifyFormat("#pragma option -C -A");
More information about the llvm-commits
mailing list