https://github.com/amitamd7 updated https://github.com/llvm/llvm-project/pull/183261
>From a741c84aba518b7c6fbca2adcd998807c56b6d56 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Wed, 25 Feb 2026 03:48:23 -0500 Subject: [PATCH 01/15] Split node creation and registration --- clang/include/clang/AST/StmtOpenMP.h | 74 +++++++++++++++++++ clang/include/clang/Basic/StmtNodes.td | 1 + .../include/clang/Serialization/ASTBitCodes.h | 1 + clang/lib/AST/StmtOpenMP.cpp | 22 ++++++ llvm/include/llvm/Frontend/OpenMP/OMP.td | 5 ++ 5 files changed, 103 insertions(+) diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h index bc6aeaa8d143c..626c39a7b778c 100644 --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/clang/include/clang/AST/StmtOpenMP.h @@ -6065,6 +6065,80 @@ class OMPFuseDirective final } }; +/// Represents the '#pragma omp split' loop transformation directive. +/// +/// \code{c} +/// #pragma omp split +/// for (int i = 0; i < n; ++i) +/// ... +/// \endcode +/// +/// This directive transforms a single loop into multiple loops based on +/// index ranges. The transformation splits the iteration space of the loop +/// into multiple contiguous ranges. +class OMPSplitDirective final + : public OMPCanonicalLoopNestTransformationDirective { + friend class ASTStmtReader; + friend class OMPExecutableDirective; + + /// Offsets of child members. + enum { + PreInitsOffset = 0, + TransformedStmtOffset, + }; + + explicit OMPSplitDirective(SourceLocation StartLoc, SourceLocation EndLoc, + unsigned NumLoops) + : OMPCanonicalLoopNestTransformationDirective( + OMPSplitDirectiveClass, llvm::omp::OMPD_split, StartLoc, EndLoc, + NumLoops) {} + + void setPreInits(Stmt *PreInits) { + Data->getChildren()[PreInitsOffset] = PreInits; + } + + void setTransformedStmt(Stmt *S) { + Data->getChildren()[TransformedStmtOffset] = S; + } + +public: + /// Create a new AST node representation for '#pragma omp split'. + /// + /// \param C Context of the AST. + /// \param StartLoc Location of the introducer (e.g. the 'omp' token). + /// \param EndLoc Location of the directive's end (e.g. the tok::eod). + /// \param NumLoops Number of affected loops (should be 1 for split). + /// \param AssociatedStmt The outermost associated loop. + /// \param TransformedStmt The loop nest after splitting, or nullptr in + /// dependent contexts. + /// \param PreInits Helper preinits statements for the loop nest. + static OMPSplitDirective *Create(const ASTContext &C, + SourceLocation StartLoc, + SourceLocation EndLoc, + Stmt *AssociatedStmt, unsigned NumLoops, + Stmt *TransformedStmt, Stmt *PreInits); + + /// Build an empty '#pragma omp split' AST node for deserialization. + /// + /// \param C Context of the AST. + /// \param NumLoops Number of associated loops to allocate + static OMPSplitDirective *CreateEmpty(const ASTContext &C, + unsigned NumLoops); + + /// Gets/sets the associated loops after the transformation, i.e. after + /// de-sugaring. + Stmt *getTransformedStmt() const { + return Data->getChildren()[TransformedStmtOffset]; + } + + /// Return preinits statement. + Stmt *getPreInits() const { return Data->getChildren()[PreInitsOffset]; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == OMPSplitDirectiveClass; + } +}; + /// This represents '#pragma omp scan' directive. /// /// \code diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index b196382025c95..5ddba5124640a 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -243,6 +243,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 bd5e3d29ca35571d8656e3aa3cb760db93e41c2a Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Wed, 25 Feb 2026 06:51:02 -0500 Subject: [PATCH 02/15] wip --- clang/include/clang/AST/RecursiveASTVisitor.h | 3 +++ clang/include/clang/AST/StmtOpenMP.h | 14 ++++++-------- clang/lib/AST/StmtOpenMP.cpp | 14 ++++++-------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 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 1a6b8949150f61b10d66869b2405e9236dc21b25 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Fri, 6 Mar 2026 06:29:06 -0500 Subject: [PATCH 03/15] wip --- clang/lib/AST/StmtPrinter.cpp | 5 +++++ clang/lib/AST/StmtProfile.cpp | 4 ++++ clang/lib/Sema/TreeTransform.h | 11 +++++++++++ clang/lib/Serialization/ASTReaderStmt.cpp | 4 ++++ clang/lib/Serialization/ASTWriterStmt.cpp | 5 +++++ 5 files changed, 29 insertions(+) diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 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 dc7fd352a67b2..7c1898833e52d 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 f351e185e5b58..b368c127f22a3 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2519,6 +2519,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 d9b95e53f2da0..89448ddee0d44 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2533,6 +2533,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 bb06e0e5ae1744dcbf413b0215ae9df985e155d5 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Mon, 9 Mar 2026 07:42:22 -0400 Subject: [PATCH 04/15] wip --- clang/lib/CodeGen/CGStmt.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 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 46fe4722ab61f2a80004a027659be9783b86b9e8 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Mon, 9 Mar 2026 08:16:47 -0400 Subject: [PATCH 05/15] wip --- clang/lib/Sema/SemaExceptionSpec.cpp | 1 + clang/lib/Serialization/ASTReaderStmt.cpp | 8 ++++++++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + clang/tools/libclang/CXCursor.cpp | 3 +++ 4 files changed, 13 insertions(+) diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 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 b368c127f22a3..f52022f37231f 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -3681,6 +3681,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 0fb8f1d00b618..b1b9b31986e0c 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 3bee79848016b523b8919d8d9f07f0e2f0fa187b Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Mon, 9 Mar 2026 12:13:02 -0400 Subject: [PATCH 06/15] wip --- clang/lib/CodeGen/CGStmt.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 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 a6cc670015416a348fcdf7a2d4a6370a6a401364 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Fri, 13 Mar 2026 09:48:29 -0400 Subject: [PATCH 07/15] sema --- clang/include/clang-c/Index.h | 4 + clang/include/clang/Sema/SemaOpenMP.h | 4 + clang/lib/Basic/OpenMPKinds.cpp | 3 +- clang/lib/CodeGen/CGStmt.cpp | 3 +- clang/lib/CodeGen/CGStmtOpenMP.cpp | 8 + clang/lib/CodeGen/CodeGenFunction.h | 1 + clang/lib/Parse/ParseOpenMP.cpp | 4 + clang/lib/Sema/SemaOpenMP.cpp | 205 ++++++++++++++++++++++++++ clang/test/OpenMP/split_ast_print.cpp | 28 ++++ clang/test/OpenMP/split_simple_test.c | 26 ++++ clang/tools/libclang/CIndex.cpp | 2 + clang/tools/libclang/CXCursor.cpp | 2 +- 12 files changed, 286 insertions(+), 4 deletions(-) create mode 100644 clang/test/OpenMP/split_ast_print.cpp create mode 100644 clang/test/OpenMP/split_simple_test.c diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 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 cc85de9221eef..4be245ff9728f 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(); @@ -2979,6 +2981,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 85c058ba237ee..10dbfdfe199b4 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3919,6 +3919,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 f3641e2453a171df6d3fdcebad8d4371b9d64226 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Fri, 13 Mar 2026 10:21:40 -0400 Subject: [PATCH 08/15] duplicate_split_def_removed --- llvm/include/llvm/Frontend/OpenMP/OMP.td | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td index 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 9ad0132ef7788c93a03ae81fcbd692ab622a153f Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Fri, 13 Mar 2026 12:29:17 -0400 Subject: [PATCH 09/15] cursorkind_enum_added --- clang/bindings/python/clang/cindex.py | 3 +++ clang/test/OpenMP/split_simple_test.c | 20 +++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index c34a1c0db01e9..d32345322237c 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 9977685494b72e5c82857d2fb6c000dd58dd5b46 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Thu, 19 Mar 2026 04:32:37 -0400 Subject: [PATCH 10/15] counts_clause_defined --- clang/include/clang/AST/OpenMPClause.h | 87 ++++++++++++++++++++++++ clang/include/clang/AST/StmtOpenMP.h | 15 ++-- clang/lib/AST/OpenMPClause.cpp | 31 +++++++++ clang/lib/AST/StmtOpenMP.cpp | 13 ++-- clang/lib/Basic/OpenMPKinds.cpp | 2 + llvm/include/llvm/Frontend/OpenMP/OMP.td | 8 ++- 6 files changed, 144 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index af5d3f4698eda..dbc22e23c3704 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -1023,6 +1023,93 @@ class OMPSizesClause final } }; +/// This represents the 'counts' clause in the '#pragma omp split' directive. +/// +/// \code +/// #pragma omp split counts(3, 5, 2) +/// for (int i = 0; i < n; ++i) { ... } +/// \endcode +class OMPCountsClause final + : public OMPClause, + private llvm::TrailingObjects<OMPCountsClause, Expr *> { + friend class OMPClauseReader; + friend class llvm::TrailingObjects<OMPCountsClause, Expr *>; + + /// Location of '('. + SourceLocation LParenLoc; + + /// Number of count expressions in the clause. + unsigned NumCounts; + + /// Build an empty clause. + explicit OMPCountsClause(int NumCounts) + : OMPClause(llvm::omp::OMPC_counts, SourceLocation(), SourceLocation()), + NumCounts(NumCounts) {} + +public: + /// Build a 'counts' AST node. + /// + /// \param C Context of the AST. + /// \param StartLoc Location of the 'counts' identifier. + /// \param LParenLoc Location of '('. + /// \param EndLoc Location of ')'. + /// \param Counts Content of the clause. + static OMPCountsClause *Create(const ASTContext &C, SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc, + ArrayRef<Expr *> Counts); + + /// Build an empty 'counts' AST node for deserialization. + /// + /// \param C Context of the AST. + /// \param NumCounts Number of items in the clause. + static OMPCountsClause *CreateEmpty(const ASTContext &C, unsigned NumCounts); + + /// Sets the location of '('. + void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; } + + /// Returns the location of '('. + SourceLocation getLParenLoc() const { return LParenLoc; } + + /// Returns the number of list items. + unsigned getNumCounts() const { return NumCounts; } + + /// Returns the count expressions. + MutableArrayRef<Expr *> getCountsRefs() { + return getTrailingObjects(NumCounts); + } + ArrayRef<Expr *> getCountsRefs() const { + return getTrailingObjects(NumCounts); + } + + /// Sets the count expressions. + void setCountsRefs(ArrayRef<Expr *> VL) { + assert(VL.size() == NumCounts); + llvm::copy(VL, getCountsRefs().begin()); + } + + child_range children() { + MutableArrayRef<Expr *> Counts = getCountsRefs(); + return child_range(reinterpret_cast<Stmt **>(Counts.begin()), + reinterpret_cast<Stmt **>(Counts.end())); + } + const_child_range children() const { + ArrayRef<Expr *> Counts = getCountsRefs(); + return const_child_range(reinterpret_cast<Stmt *const *>(Counts.begin()), + reinterpret_cast<Stmt *const *>(Counts.end())); + } + child_range used_children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range used_children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == llvm::omp::OMPC_counts; + } +}; + /// This class represents the 'permutation' clause in the /// '#pragma omp interchange' directive. /// diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h index c5b83e17acbcd..bdaf73c0a6607 100644 --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/clang/include/clang/AST/StmtOpenMP.h @@ -6107,21 +6107,26 @@ class OMPSplitDirective final /// \param C Context of the AST. /// \param StartLoc Location of the introducer (e.g. the 'omp' token). /// \param EndLoc Location of the directive's end (e.g. the tok::eod). + /// \param Clauses The directive's clauses (e.g. the required \c counts + /// clause). /// \param NumLoops Number of affected loops (should be 1 for split). /// \param AssociatedStmt The outermost associated loop. /// \param TransformedStmt The loop nest after splitting, or nullptr in /// dependent contexts. /// \param PreInits Helper preinits statements for the loop nest. static OMPSplitDirective *Create(const ASTContext &C, SourceLocation StartLoc, - SourceLocation EndLoc, Stmt *AssociatedStmt, - unsigned NumLoops, Stmt *TransformedStmt, - Stmt *PreInits); + SourceLocation EndLoc, + ArrayRef<OMPClause *> Clauses, + unsigned NumLoops, Stmt *AssociatedStmt, + Stmt *TransformedStmt, Stmt *PreInits); /// Build an empty '#pragma omp split' AST node for deserialization. /// /// \param C Context of the AST. - /// \param NumLoops Number of associated loops to allocate - static OMPSplitDirective *CreateEmpty(const ASTContext &C, unsigned NumLoops); + /// \param NumClauses Number of clauses to allocate. + /// \param NumLoops Number of associated loops to allocate. + static OMPSplitDirective *CreateEmpty(const ASTContext &C, + unsigned NumClauses, unsigned NumLoops); /// Gets/sets the associated loops after the transformation, i.e. after /// de-sugaring. diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index d4826c3c6edca..ab6fa1c673411 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -986,6 +986,25 @@ OMPSizesClause *OMPSizesClause::CreateEmpty(const ASTContext &C, return new (Mem) OMPSizesClause(NumSizes); } +OMPCountsClause *OMPCountsClause::Create(const ASTContext &C, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc, + ArrayRef<Expr *> Counts) { + OMPCountsClause *Clause = CreateEmpty(C, Counts.size()); + Clause->setLocStart(StartLoc); + Clause->setLParenLoc(LParenLoc); + Clause->setLocEnd(EndLoc); + Clause->setCountsRefs(Counts); + return Clause; +} + +OMPCountsClause *OMPCountsClause::CreateEmpty(const ASTContext &C, + unsigned NumCounts) { + void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(NumCounts)); + return new (Mem) OMPCountsClause(NumCounts); +} + OMPPermutationClause *OMPPermutationClause::Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc, @@ -1984,6 +2003,18 @@ void OMPClausePrinter::VisitOMPSizesClause(OMPSizesClause *Node) { OS << ")"; } +void OMPClausePrinter::VisitOMPCountsClause(OMPCountsClause *Node) { + OS << "counts("; + bool First = true; + for (auto *Count : Node->getCountsRefs()) { + if (!First) + OS << ", "; + Count->printPretty(OS, nullptr, Policy, 0); + First = false; + } + OS << ")"; +} + void OMPClausePrinter::VisitOMPPermutationClause(OMPPermutationClause *Node) { OS << "permutation("; llvm::interleaveComma(Node->getArgsRefs(), OS, [&](const Expr *E) { diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp index 6c939cf7f9aeb..9d6b315effb41 100644 --- a/clang/lib/AST/StmtOpenMP.cpp +++ b/clang/lib/AST/StmtOpenMP.cpp @@ -554,11 +554,11 @@ OMPInterchangeDirective::CreateEmpty(const ASTContext &C, unsigned NumClauses, OMPSplitDirective * OMPSplitDirective::Create(const ASTContext &C, SourceLocation StartLoc, - SourceLocation EndLoc, Stmt *AssociatedStmt, - unsigned NumLoops, Stmt *TransformedStmt, - Stmt *PreInits) { + SourceLocation EndLoc, ArrayRef<OMPClause *> Clauses, + unsigned NumLoops, Stmt *AssociatedStmt, + Stmt *TransformedStmt, Stmt *PreInits) { OMPSplitDirective *Dir = createDirective<OMPSplitDirective>( - C, {}, AssociatedStmt, TransformedStmtOffset + 1, StartLoc, EndLoc, + C, Clauses, AssociatedStmt, TransformedStmtOffset + 1, StartLoc, EndLoc, NumLoops); Dir->setTransformedStmt(TransformedStmt); Dir->setPreInits(PreInits); @@ -566,10 +566,11 @@ OMPSplitDirective::Create(const ASTContext &C, SourceLocation StartLoc, } OMPSplitDirective *OMPSplitDirective::CreateEmpty(const ASTContext &C, + unsigned NumClauses, unsigned NumLoops) { return createEmptyDirective<OMPSplitDirective>( - C, /*NumClauses=*/0, /*HasAssociatedStmt=*/true, - TransformedStmtOffset + 1, SourceLocation(), SourceLocation(), NumLoops); + C, NumClauses, /*HasAssociatedStmt=*/true, TransformedStmtOffset + 1, + SourceLocation(), SourceLocation(), NumLoops); } OMPFuseDirective *OMPFuseDirective::Create( diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp index ef01943f11ca5..287eb217ba458 100644 --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -256,6 +256,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, StringRef Str, case OMPC_safelen: case OMPC_simdlen: case OMPC_sizes: + case OMPC_counts: case OMPC_permutation: case OMPC_allocator: case OMPC_collapse: @@ -635,6 +636,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind, case OMPC_safelen: case OMPC_simdlen: case OMPC_sizes: + case OMPC_counts: case OMPC_permutation: case OMPC_allocator: case OMPC_collapse: diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td index 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 d98262dfc89769f28d299ee733adb4403d3fe967 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Thu, 19 Mar 2026 04:33:51 -0400 Subject: [PATCH 11/15] parse & sema --- clang/include/clang/AST/RecursiveASTVisitor.h | 7 + clang/include/clang/Parse/Parser.h | 3 + clang/include/clang/Sema/SemaOpenMP.h | 8 +- clang/lib/Parse/ParseOpenMP.cpp | 24 +- clang/lib/Sema/SemaOpenMP.cpp | 278 +++++++++--------- clang/lib/Sema/TreeTransform.h | 29 ++ 6 files changed, 205 insertions(+), 144 deletions(-) diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 0802871c4a720..1a14dd2c666b5 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -3506,6 +3506,13 @@ bool RecursiveASTVisitor<Derived>::VisitOMPSizesClause(OMPSizesClause *C) { return true; } +template <typename Derived> +bool RecursiveASTVisitor<Derived>::VisitOMPCountsClause(OMPCountsClause *C) { + for (Expr *E : C->getCountsRefs()) + TRY_TO(TraverseStmt(E)); + return true; +} + template <typename Derived> bool RecursiveASTVisitor<Derived>::VisitOMPPermutationClause( OMPPermutationClause *C) { diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 5ae02e2b4e8ad..74a2a565bd613 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -6774,6 +6774,9 @@ class Parser : public CodeCompletionHandler { /// Parses the 'sizes' clause of a '#pragma omp tile' directive. OMPClause *ParseOpenMPSizesClause(); + /// Parses the 'counts' clause of a '#pragma omp split' directive. + OMPClause *ParseOpenMPCountsClause(); + /// Parses the 'permutation' clause of a '#pragma omp interchange' directive. OMPClause *ParseOpenMPPermutationClause(); diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 57382557fd13f..c4c1b2ad33f71 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -459,7 +459,8 @@ class SemaOpenMP : public SemaBase { SourceLocation EndLoc); /// Called on well-formed '#pragma omp split' after parsing of its /// associated statement. - StmtResult ActOnOpenMPSplitDirective(Stmt *AStmt, SourceLocation StartLoc, + StmtResult ActOnOpenMPSplitDirective(ArrayRef<OMPClause *> Clauses, + Stmt *AStmt, SourceLocation StartLoc, SourceLocation EndLoc); /// Called on well-formed '#pragma omp interchange' after parsing of its /// clauses and the associated statement. @@ -915,6 +916,11 @@ class SemaOpenMP : public SemaBase { SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc); + /// Called on well-formed 'counts' clause after parsing its arguments. + OMPClause *ActOnOpenMPCountsClause(ArrayRef<Expr *> CountExprs, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc); /// Called on well-form 'permutation' clause after parsing its arguments. OMPClause *ActOnOpenMPPermutationClause(ArrayRef<Expr *> PermExprs, SourceLocation StartLoc, diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 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 6385460eca7ac5d6dbf23f874d2bc66094daa8b9 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Thu, 19 Mar 2026 04:34:58 -0400 Subject: [PATCH 12/15] serialisation --- clang/lib/AST/StmtProfile.cpp | 6 ++++++ clang/lib/Serialization/ASTReader.cpp | 11 +++++++++++ clang/lib/Serialization/ASTReaderStmt.cpp | 5 ++--- clang/lib/Serialization/ASTWriter.cpp | 7 +++++++ clang/tools/libclang/CIndex.cpp | 5 +++++ 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 7c1898833e52d..4ebece01e5df0 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -498,6 +498,12 @@ void OMPClauseProfiler::VisitOMPSizesClause(const OMPSizesClause *C) { Profiler->VisitExpr(E); } +void OMPClauseProfiler::VisitOMPCountsClause(const OMPCountsClause *C) { + for (auto *E : C->getCountsRefs()) + if (E) + Profiler->VisitExpr(E); +} + void OMPClauseProfiler::VisitOMPPermutationClause( const OMPPermutationClause *C) { for (Expr *E : C->getArgsRefs()) diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 03b1b02859b81..15e071d1f9f21 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -11393,6 +11393,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); @@ -11806,6 +11811,12 @@ void OMPClauseReader::VisitOMPSizesClause(OMPSizesClause *C) { C->setLParenLoc(Record.readSourceLocation()); } +void OMPClauseReader::VisitOMPCountsClause(OMPCountsClause *C) { + for (Expr *&E : C->getCountsRefs()) + E = Record.readSubExpr(); + C->setLParenLoc(Record.readSourceLocation()); +} + void OMPClauseReader::VisitOMPPermutationClause(OMPPermutationClause *C) { for (Expr *&E : C->getArgsRefs()) E = Record.readSubExpr(); diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index f52022f37231f..c68d07a39d651 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -3683,9 +3683,8 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { case STMT_OMP_SPLIT_DIRECTIVE: { unsigned NumLoops = Record[ASTStmtReader::NumStmtFields]; - assert(Record[ASTStmtReader::NumStmtFields + 1] == 0 && - "Split directive has no clauses"); - S = OMPSplitDirective::CreateEmpty(Context, NumLoops); + unsigned NumClauses = Record[ASTStmtReader::NumStmtFields + 1]; + S = OMPSplitDirective::CreateEmpty(Context, NumClauses, NumLoops); break; } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index e3db39a1acb74..8852c0a11b6e6 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -8006,6 +8006,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 a6bb9e833b938df739e8df0fc6e79cda840b6dda Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Thu, 19 Mar 2026 04:36:32 -0400 Subject: [PATCH 13/15] basic_test --- clang/test/OpenMP/split_ast_print.cpp | 5 +-- clang/test/OpenMP/split_counts_verify.c | 44 +++++++++++++++++++++++++ clang/test/OpenMP/split_simple_test.c | 15 +++++---- 3 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 clang/test/OpenMP/split_counts_verify.c diff --git a/clang/test/OpenMP/split_ast_print.cpp b/clang/test/OpenMP/split_ast_print.cpp index b24eae4a9bead..07dd7b28e1a13 100644 --- a/clang/test/OpenMP/split_ast_print.cpp +++ b/clang/test/OpenMP/split_ast_print.cpp @@ -16,9 +16,10 @@ extern "C" void body(...); // PRINT-LABEL: void foo( // DUMP-LABEL: FunctionDecl {{.*}} foo void foo(int n) { - // PRINT: #pragma omp split + // PRINT: #pragma omp split counts(2, 3) // DUMP: OMPSplitDirective - #pragma omp split + // DUMP: OMPCountsClause + #pragma omp split counts(2, 3) // PRINT: for (int i = 0; i < n; ++i) // DUMP: ForStmt for (int i = 0; i < n; ++i) diff --git a/clang/test/OpenMP/split_counts_verify.c b/clang/test/OpenMP/split_counts_verify.c new file mode 100644 index 0000000000000..4b2ec2ca20bcd --- /dev/null +++ b/clang/test/OpenMP/split_counts_verify.c @@ -0,0 +1,44 @@ +/* + * Verify #pragma omp split counts(c1, c2, ...) at AST, IR, and runtime. + * counts(3, 5, 2) splits 10 iterations into: [0..3), [3..8), [8..10). + * Sum 0+1+...+9 = 45. + */ +// REQUIRES: x86-registered-target + +// 1) Syntax and semantics only +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -fsyntax-only -verify %s +// expected-no-diagnostics + +// 2) AST dump should show OMPSplitDirective with OMPCountsClause node. +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -ast-dump %s 2>&1 | FileCheck %s --check-prefix=AST + +// 3) Emit LLVM: three sequential loops (multiple phi/br for loop structure) +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fopenmp -fopenmp-version=60 -emit-llvm %s -o - 2>&1 | FileCheck %s --check-prefix=IR + +// 4) Compile and run: exit 0 if sum == 45 +// RUN: %clang -fopenmp -fopenmp-version=60 -O0 %s -o %t.exe +// RUN: %t.exe + +int main(void) { + const int n = 10; + int sum = 0; + +#pragma omp split counts(3, 5, 2) + for (int i = 0; i < n; ++i) { + sum += i; + } + + return (sum == 45) ? 0 : 1; +} + +// AST: OMPSplitDirective +// AST: OMPCountsClause + +// IR: define +// IR: .split.iv.0 +// IR: icmp slt i32 {{.*}}, 3 +// IR: .split.iv.1 +// IR: icmp slt i32 {{.*}}, 8 +// IR: .split.iv.2 +// IR: icmp slt i32 {{.*}}, 10 +// IR: icmp eq i32 {{.*}}, 45 diff --git a/clang/test/OpenMP/split_simple_test.c b/clang/test/OpenMP/split_simple_test.c index 62dbc1cd861e5..021759e6a70e6 100644 --- a/clang/test/OpenMP/split_simple_test.c +++ b/clang/test/OpenMP/split_simple_test.c @@ -1,16 +1,15 @@ /* - * Simple test for #pragma omp split: one canonical for-loop is transformed - * into two loops (first half and second half of iterations). + * Simple test for #pragma omp split counts: one for-loop is transformed + * into two loops (counts(5, 5) => [0..5) and [5..10)). */ // Verify the split directive compiles and emits IR (two sequential loops). -// RUN: %clang_cc1 -fopenmp -fopenmp-version=60 -triple x86_64-unknown-unknown -// -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -fopenmp -fopenmp-version=60 -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s int main(void) { const int n = 10; int sum = 0; -#pragma omp split +#pragma omp split counts(5, 5) for (int i = 0; i < n; ++i) { sum += i; } @@ -20,5 +19,9 @@ int main(void) { // CHECK: define // CHECK: load -// Split produces two sequential loops; ensure we have loop structure in IR. +// Split produces two sequential loops (counts(5, 5) => bounds 5, 10). +// CHECK: .split.iv +// CHECK: icmp slt i32 {{.*}}, 5 +// CHECK: .split.iv +// CHECK: icmp slt i32 {{.*}}, 10 // CHECK: br i1 >From d43e1561055f8ea66bd6815a831bd34ebbeddaa2 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Thu, 19 Mar 2026 06:49:54 -0400 Subject: [PATCH 14/15] test_fix --- clang/test/OpenMP/split_counts_verify.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/clang/test/OpenMP/split_counts_verify.c b/clang/test/OpenMP/split_counts_verify.c index 4b2ec2ca20bcd..3eec02f974e07 100644 --- a/clang/test/OpenMP/split_counts_verify.c +++ b/clang/test/OpenMP/split_counts_verify.c @@ -15,9 +15,6 @@ // 3) Emit LLVM: three sequential loops (multiple phi/br for loop structure) // RUN: %clang_cc1 -triple x86_64-unknown-unknown -fopenmp -fopenmp-version=60 -emit-llvm %s -o - 2>&1 | FileCheck %s --check-prefix=IR -// 4) Compile and run: exit 0 if sum == 45 -// RUN: %clang -fopenmp -fopenmp-version=60 -O0 %s -o %t.exe -// RUN: %t.exe int main(void) { const int n = 10; >From 5b0253911f9874d88a2c0fef837687389a6b68a0 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Fri, 20 Mar 2026 04:36:15 -0400 Subject: [PATCH 15/15] revised --- clang/include/clang/AST/StmtOpenMP.h | 2 +- clang/lib/AST/OpenMPClause.cpp | 10 +++------- clang/lib/CodeGen/CGStmtOpenMP.cpp | 2 +- clang/lib/Sema/SemaOpenMP.cpp | 15 ++++----------- clang/test/OpenMP/split_ast_print.cpp | 2 ++ 5 files changed, 11 insertions(+), 20 deletions(-) diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h index bdaf73c0a6607..3d8962afa2b7c 100644 --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/clang/include/clang/AST/StmtOpenMP.h @@ -6067,7 +6067,7 @@ class OMPFuseDirective final /// Represents the '#pragma omp split' loop transformation directive. /// -/// \code{c} +/// \code{.c} /// #pragma omp split /// for (int i = 0; i < n; ++i) /// ... diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index ab6fa1c673411..f6c03eba668c8 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -2005,13 +2005,9 @@ void OMPClausePrinter::VisitOMPSizesClause(OMPSizesClause *Node) { void OMPClausePrinter::VisitOMPCountsClause(OMPCountsClause *Node) { OS << "counts("; - bool First = true; - for (auto *Count : Node->getCountsRefs()) { - if (!First) - OS << ", "; - Count->printPretty(OS, nullptr, Policy, 0); - First = false; - } + llvm::interleaveComma(Node->getCountsRefs(), OS, [&](const Expr *E) { + E->printPretty(OS, nullptr, Policy, 0); + }); OS << ")"; } diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp index 4be245ff9728f..499560223213a 100644 --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -2982,7 +2982,7 @@ void CodeGenFunction::EmitOMPReverseDirective(const OMPReverseDirective &S) { } void CodeGenFunction::EmitOMPSplitDirective(const OMPSplitDirective &S) { - // Emit the de-sugared statement (the two split loops). + // Emit the de-sugared statement (the split loops). OMPTransformDirectiveScopeRAII SplitScope(*this, &S); EmitStmt(S.getTransformedStmt()); } diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
