https://github.com/amitamd7 updated https://github.com/llvm/llvm-project/pull/183261
>From 5ee7cb284ad1e9cf98190cea04fc428b9e31ccf6 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Wed, 25 Feb 2026 03:48:23 -0500 Subject: [PATCH 1/8] Split node creation and registration --- clang/include/clang/AST/StmtOpenMP.h | 74 +++++++++++++++++++ clang/include/clang/Basic/StmtNodes.td | 1 + .../include/clang/Serialization/ASTBitCodes.h | 1 + clang/lib/AST/StmtOpenMP.cpp | 22 ++++++ llvm/include/llvm/Frontend/OpenMP/OMP.td | 5 ++ 5 files changed, 103 insertions(+) diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h index bc6aeaa8d143c..626c39a7b778c 100644 --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/clang/include/clang/AST/StmtOpenMP.h @@ -6065,6 +6065,80 @@ class OMPFuseDirective final } }; +/// Represents the '#pragma omp split' loop transformation directive. +/// +/// \code{c} +/// #pragma omp split +/// for (int i = 0; i < n; ++i) +/// ... +/// \endcode +/// +/// This directive transforms a single loop into multiple loops based on +/// index ranges. The transformation splits the iteration space of the loop +/// into multiple contiguous ranges. +class OMPSplitDirective final + : public OMPCanonicalLoopNestTransformationDirective { + friend class ASTStmtReader; + friend class OMPExecutableDirective; + + /// Offsets of child members. + enum { + PreInitsOffset = 0, + TransformedStmtOffset, + }; + + explicit OMPSplitDirective(SourceLocation StartLoc, SourceLocation EndLoc, + unsigned NumLoops) + : OMPCanonicalLoopNestTransformationDirective( + OMPSplitDirectiveClass, llvm::omp::OMPD_split, StartLoc, EndLoc, + NumLoops) {} + + void setPreInits(Stmt *PreInits) { + Data->getChildren()[PreInitsOffset] = PreInits; + } + + void setTransformedStmt(Stmt *S) { + Data->getChildren()[TransformedStmtOffset] = S; + } + +public: + /// Create a new AST node representation for '#pragma omp split'. + /// + /// \param C Context of the AST. + /// \param StartLoc Location of the introducer (e.g. the 'omp' token). + /// \param EndLoc Location of the directive's end (e.g. the tok::eod). + /// \param NumLoops Number of affected loops (should be 1 for split). + /// \param AssociatedStmt The outermost associated loop. + /// \param TransformedStmt The loop nest after splitting, or nullptr in + /// dependent contexts. + /// \param PreInits Helper preinits statements for the loop nest. + static OMPSplitDirective *Create(const ASTContext &C, + SourceLocation StartLoc, + SourceLocation EndLoc, + Stmt *AssociatedStmt, unsigned NumLoops, + Stmt *TransformedStmt, Stmt *PreInits); + + /// Build an empty '#pragma omp split' AST node for deserialization. + /// + /// \param C Context of the AST. + /// \param NumLoops Number of associated loops to allocate + static OMPSplitDirective *CreateEmpty(const ASTContext &C, + unsigned NumLoops); + + /// Gets/sets the associated loops after the transformation, i.e. after + /// de-sugaring. + Stmt *getTransformedStmt() const { + return Data->getChildren()[TransformedStmtOffset]; + } + + /// Return preinits statement. + Stmt *getPreInits() const { return Data->getChildren()[PreInitsOffset]; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == OMPSplitDirectiveClass; + } +}; + /// This represents '#pragma omp scan' directive. /// /// \code diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index cb869cc210627..77db0e6d73ca3 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -242,6 +242,7 @@ def OMPTileDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>; def OMPStripeDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>; def OMPUnrollDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>; def OMPReverseDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>; +def OMPSplitDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>; def OMPInterchangeDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>; def OMPCanonicalLoopSequenceTransformationDirective diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index d72f1f9db86b2..1786277598f8e 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1959,6 +1959,7 @@ enum StmtCode { STMP_OMP_STRIPE_DIRECTIVE, STMT_OMP_UNROLL_DIRECTIVE, STMT_OMP_REVERSE_DIRECTIVE, + STMT_OMP_SPLIT_DIRECTIVE, STMT_OMP_INTERCHANGE_DIRECTIVE, STMT_OMP_FUSE_DIRECTIVE, STMT_OMP_FOR_DIRECTIVE, diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp index a5b0cd3786a28..ada4e66b280f8 100644 --- a/clang/lib/AST/StmtOpenMP.cpp +++ b/clang/lib/AST/StmtOpenMP.cpp @@ -552,6 +552,28 @@ OMPInterchangeDirective::CreateEmpty(const ASTContext &C, unsigned NumClauses, SourceLocation(), SourceLocation(), NumLoops); } +OMPSplitDirective *OMPSplitDirective::Create(const ASTContext &C, + SourceLocation StartLoc, + SourceLocation EndLoc, + Stmt *AssociatedStmt, + unsigned NumLoops, + Stmt *TransformedStmt, + Stmt *PreInits) { + OMPSplitDirective *Dir = createDirective<OMPSplitDirective>( + C, {}, AssociatedStmt, TransformedStmtOffset + 1, StartLoc, EndLoc, + NumLoops); + Dir->setTransformedStmt(TransformedStmt); + Dir->setPreInits(PreInits); + return Dir; +} + +OMPSplitDirective *OMPSplitDirective::CreateEmpty(const ASTContext &C, + unsigned NumLoops) { + return createEmptyDirective<OMPSplitDirective>( + C, /*NumClauses=*/0, /*HasAssociatedStmt=*/true, + TransformedStmtOffset + 1, SourceLocation(), SourceLocation(), NumLoops); +} + OMPFuseDirective *OMPFuseDirective::Create( const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, ArrayRef<OMPClause *> Clauses, unsigned NumGeneratedTopLevelLoops, diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td index 865cad7769554..38eedd10cfdd7 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -1414,6 +1414,11 @@ def OMP_Stripe : Directive<[Spelling<"stripe">]> { let association = AS_LoopNest; let category = CA_Executable; } +def OMP_Split : Directive<[Spelling<"split">]> { + // TODO: Add counts clause support (OMPC_Counts) + let association = AS_LoopNest; + let category = CA_Executable; +} def OMP_Unknown : Directive<[Spelling<"unknown">]> { let isDefault = true; let association = AS_None; >From 0a57b91396898f01dffa1aa55da4cc3ac8305d54 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Wed, 25 Feb 2026 06:51:02 -0500 Subject: [PATCH 2/8] wip --- clang/include/clang/AST/RecursiveASTVisitor.h | 3 +++ clang/include/clang/AST/StmtOpenMP.h | 14 ++++++-------- clang/lib/AST/StmtOpenMP.cpp | 14 ++++++-------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index f97b54276cbee..323f570439724 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -3194,6 +3194,9 @@ DEF_TRAVERSE_STMT(OMPFuseDirective, DEF_TRAVERSE_STMT(OMPInterchangeDirective, { TRY_TO(TraverseOMPExecutableDirective(S)); }) +DEF_TRAVERSE_STMT(OMPSplitDirective, + { TRY_TO(TraverseOMPExecutableDirective(S)); }) + DEF_TRAVERSE_STMT(OMPForDirective, { TRY_TO(TraverseOMPExecutableDirective(S)); }) diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h index 626c39a7b778c..c5b83e17acbcd 100644 --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/clang/include/clang/AST/StmtOpenMP.h @@ -6088,7 +6088,7 @@ class OMPSplitDirective final }; explicit OMPSplitDirective(SourceLocation StartLoc, SourceLocation EndLoc, - unsigned NumLoops) + unsigned NumLoops) : OMPCanonicalLoopNestTransformationDirective( OMPSplitDirectiveClass, llvm::omp::OMPD_split, StartLoc, EndLoc, NumLoops) {} @@ -6112,18 +6112,16 @@ class OMPSplitDirective final /// \param TransformedStmt The loop nest after splitting, or nullptr in /// dependent contexts. /// \param PreInits Helper preinits statements for the loop nest. - static OMPSplitDirective *Create(const ASTContext &C, - SourceLocation StartLoc, - SourceLocation EndLoc, - Stmt *AssociatedStmt, unsigned NumLoops, - Stmt *TransformedStmt, Stmt *PreInits); + static OMPSplitDirective *Create(const ASTContext &C, SourceLocation StartLoc, + SourceLocation EndLoc, Stmt *AssociatedStmt, + unsigned NumLoops, Stmt *TransformedStmt, + Stmt *PreInits); /// Build an empty '#pragma omp split' AST node for deserialization. /// /// \param C Context of the AST. /// \param NumLoops Number of associated loops to allocate - static OMPSplitDirective *CreateEmpty(const ASTContext &C, - unsigned NumLoops); + static OMPSplitDirective *CreateEmpty(const ASTContext &C, unsigned NumLoops); /// Gets/sets the associated loops after the transformation, i.e. after /// de-sugaring. diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp index ada4e66b280f8..6c939cf7f9aeb 100644 --- a/clang/lib/AST/StmtOpenMP.cpp +++ b/clang/lib/AST/StmtOpenMP.cpp @@ -552,13 +552,11 @@ OMPInterchangeDirective::CreateEmpty(const ASTContext &C, unsigned NumClauses, SourceLocation(), SourceLocation(), NumLoops); } -OMPSplitDirective *OMPSplitDirective::Create(const ASTContext &C, - SourceLocation StartLoc, - SourceLocation EndLoc, - Stmt *AssociatedStmt, - unsigned NumLoops, - Stmt *TransformedStmt, - Stmt *PreInits) { +OMPSplitDirective * +OMPSplitDirective::Create(const ASTContext &C, SourceLocation StartLoc, + SourceLocation EndLoc, Stmt *AssociatedStmt, + unsigned NumLoops, Stmt *TransformedStmt, + Stmt *PreInits) { OMPSplitDirective *Dir = createDirective<OMPSplitDirective>( C, {}, AssociatedStmt, TransformedStmtOffset + 1, StartLoc, EndLoc, NumLoops); @@ -568,7 +566,7 @@ OMPSplitDirective *OMPSplitDirective::Create(const ASTContext &C, } OMPSplitDirective *OMPSplitDirective::CreateEmpty(const ASTContext &C, - unsigned NumLoops) { + unsigned NumLoops) { return createEmptyDirective<OMPSplitDirective>( C, /*NumClauses=*/0, /*HasAssociatedStmt=*/true, TransformedStmtOffset + 1, SourceLocation(), SourceLocation(), NumLoops); >From 02844251d20249820021a9e6fc58e888ebf66d3c Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Fri, 6 Mar 2026 06:29:06 -0500 Subject: [PATCH 3/8] wip --- clang/lib/AST/StmtPrinter.cpp | 5 +++++ clang/lib/AST/StmtProfile.cpp | 4 ++++ clang/lib/Sema/TreeTransform.h | 11 +++++++++++ clang/lib/Serialization/ASTReaderStmt.cpp | 4 ++++ clang/lib/Serialization/ASTWriterStmt.cpp | 5 +++++ 5 files changed, 29 insertions(+) diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index f4ce4a7573aab..143ff0580a205 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -800,6 +800,11 @@ void StmtPrinter::VisitOMPInterchangeDirective(OMPInterchangeDirective *Node) { PrintOMPExecutableDirective(Node); } +void StmtPrinter::VisitOMPSplitDirective(OMPSplitDirective *Node) { + Indent() << "#pragma omp split"; + PrintOMPExecutableDirective(Node); +} + void StmtPrinter::VisitOMPFuseDirective(OMPFuseDirective *Node) { Indent() << "#pragma omp fuse"; PrintOMPExecutableDirective(Node); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 623905188b2dd..375b2207d4dd4 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1051,6 +1051,10 @@ void StmtProfiler::VisitOMPInterchangeDirective( VisitOMPCanonicalLoopNestTransformationDirective(S); } +void StmtProfiler::VisitOMPSplitDirective(const OMPSplitDirective *S) { + VisitOMPCanonicalLoopNestTransformationDirective(S); +} + void StmtProfiler::VisitOMPCanonicalLoopSequenceTransformationDirective( const OMPCanonicalLoopSequenceTransformationDirective *S) { VisitOMPExecutableDirective(S); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 1a050bd6a8737..8f67e71382b1d 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -9761,6 +9761,17 @@ StmtResult TreeTransform<Derived>::TransformOMPInterchangeDirective( return Res; } +template <typename Derived> +StmtResult +TreeTransform<Derived>::TransformOMPSplitDirective(OMPSplitDirective *D) { + DeclarationNameInfo DirName; + getDerived().getSema().OpenMP().StartOpenMPDSABlock( + D->getDirectiveKind(), DirName, nullptr, D->getBeginLoc()); + StmtResult Res = getDerived().TransformOMPExecutableDirective(D); + getDerived().getSema().OpenMP().EndOpenMPDSABlock(Res.get()); + return Res; +} + template <typename Derived> StmtResult TreeTransform<Derived>::TransformOMPFuseDirective(OMPFuseDirective *D) { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index a18fccb6518d2..c338e9bfbf22c 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2510,6 +2510,10 @@ void ASTStmtReader::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) { VisitOMPCanonicalLoopNestTransformationDirective(D); } +void ASTStmtReader::VisitOMPSplitDirective(OMPSplitDirective *D) { + VisitOMPCanonicalLoopNestTransformationDirective(D); +} + void ASTStmtReader::VisitOMPFuseDirective(OMPFuseDirective *D) { VisitOMPCanonicalLoopSequenceTransformationDirective(D); } diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 4fcac4d0261ab..f3531d3d91837 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2522,6 +2522,11 @@ void ASTStmtWriter::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) { Code = serialization::STMT_OMP_INTERCHANGE_DIRECTIVE; } +void ASTStmtWriter::VisitOMPSplitDirective(OMPSplitDirective *D) { + VisitOMPCanonicalLoopNestTransformationDirective(D); + Code = serialization::STMT_OMP_SPLIT_DIRECTIVE; +} + void ASTStmtWriter::VisitOMPCanonicalLoopSequenceTransformationDirective( OMPCanonicalLoopSequenceTransformationDirective *D) { VisitStmt(D); >From 991ac94e4d2376e5c509aea207c69a0d36de9ac7 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Mon, 9 Mar 2026 07:42:22 -0400 Subject: [PATCH 4/8] wip --- clang/lib/CodeGen/CGStmt.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 0658ecc93d88d..f41ea386487f7 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -228,6 +228,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) { case Stmt::OMPReverseDirectiveClass: EmitOMPReverseDirective(cast<OMPReverseDirective>(*S)); break; + case Stmt::OMPSplitDirectiveClass: + EmitOMPSplitDirective(cast<OMPSplitDirective>(*S)); + break; case Stmt::OMPInterchangeDirectiveClass: EmitOMPInterchangeDirective(cast<OMPInterchangeDirective>(*S)); break; >From dcc5f3f7ec2314b81e8618c566c7e6456e2f32ca Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Mon, 9 Mar 2026 08:16:47 -0400 Subject: [PATCH 5/8] wip --- clang/lib/Sema/SemaExceptionSpec.cpp | 1 + clang/lib/Serialization/ASTReaderStmt.cpp | 8 ++++++++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + clang/tools/libclang/CXCursor.cpp | 3 +++ 4 files changed, 13 insertions(+) diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 8df01a8a616c3..4fd800e5e25c4 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1496,6 +1496,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Stmt::OMPUnrollDirectiveClass: case Stmt::OMPReverseDirectiveClass: case Stmt::OMPInterchangeDirectiveClass: + case Stmt::OMPSplitDirectiveClass: case Stmt::OMPFuseDirectiveClass: case Stmt::OMPSingleDirectiveClass: case Stmt::OMPTargetDataDirectiveClass: diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index c338e9bfbf22c..be6e7e0ea90bb 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -3668,6 +3668,14 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { break; } + case STMT_OMP_SPLIT_DIRECTIVE: { + unsigned NumLoops = Record[ASTStmtReader::NumStmtFields]; + assert(Record[ASTStmtReader::NumStmtFields + 1] == 0 && + "Split directive has no clauses"); + S = OMPSplitDirective::CreateEmpty(Context, NumLoops); + break; + } + case STMT_OMP_FUSE_DIRECTIVE: { unsigned NumClauses = Record[ASTStmtReader::NumStmtFields]; S = OMPFuseDirective::CreateEmpty(Context, NumClauses); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 76c382eeb6899..6b5f94a7d2ea2 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1822,6 +1822,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPStripeDirectiveClass: case Stmt::OMPTileDirectiveClass: case Stmt::OMPInterchangeDirectiveClass: + case Stmt::OMPSplitDirectiveClass: case Stmt::OMPFuseDirectiveClass: case Stmt::OMPInteropDirectiveClass: case Stmt::OMPDispatchDirectiveClass: diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index 17f485e5c78a5..d8247f8161506 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -696,6 +696,9 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::OMPReverseDirectiveClass: K = CXCursor_OMPReverseDirective; break; + case Stmt::OMPSplitDirectiveClass: + K = CXCursor_UnexposedStmt; + break; case Stmt::OMPInterchangeDirectiveClass: K = CXCursor_OMPInterchangeDirective; break; >From b087df1f9ee96d5a91b169018ef7cbc007b473bb Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Mon, 9 Mar 2026 12:13:02 -0400 Subject: [PATCH 6/8] wip --- clang/lib/CodeGen/CGStmt.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index f41ea386487f7..5d906c378f1bf 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -229,7 +229,8 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) { EmitOMPReverseDirective(cast<OMPReverseDirective>(*S)); break; case Stmt::OMPSplitDirectiveClass: - EmitOMPSplitDirective(cast<OMPSplitDirective>(*S)); + llvm_unreachable( + "OMPSplitDirective handled by EmitSimpleOMPExecutableDirective"); break; case Stmt::OMPInterchangeDirectiveClass: EmitOMPInterchangeDirective(cast<OMPInterchangeDirective>(*S)); >From 3b9abedcc0fbcda363ca51ba7dd3fe22ecc1c951 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Fri, 13 Mar 2026 09:48:29 -0400 Subject: [PATCH 7/8] sema --- clang/include/clang-c/Index.h | 4 + clang/include/clang/Sema/SemaOpenMP.h | 4 + clang/lib/Basic/OpenMPKinds.cpp | 3 +- clang/lib/CodeGen/CGStmt.cpp | 3 +- clang/lib/CodeGen/CGStmtOpenMP.cpp | 8 + clang/lib/CodeGen/CodeGenFunction.h | 1 + clang/lib/Parse/ParseOpenMP.cpp | 4 + clang/lib/Sema/SemaOpenMP.cpp | 205 ++++++++++++++++++++++++++ clang/test/OpenMP/split_ast_print.cpp | 28 ++++ clang/test/OpenMP/split_simple_test.c | 26 ++++ clang/tools/libclang/CIndex.cpp | 2 + clang/tools/libclang/CXCursor.cpp | 2 +- 12 files changed, 286 insertions(+), 4 deletions(-) create mode 100644 clang/test/OpenMP/split_ast_print.cpp create mode 100644 clang/test/OpenMP/split_simple_test.c diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 203634c80d82a..e9dd734445a74 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -2166,6 +2166,10 @@ enum CXCursorKind { */ CXCursor_OMPFuseDirective = 311, + /** OpenMP split directive. + */ + CXCursor_OMPSplitDirective = 312, + /** OpenACC Compute Construct. */ CXCursor_OpenACCComputeConstruct = 320, diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 7853f29f98c25..57382557fd13f 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -457,6 +457,10 @@ class SemaOpenMP : public SemaBase { /// Called on well-formed '#pragma omp reverse'. StmtResult ActOnOpenMPReverseDirective(Stmt *AStmt, SourceLocation StartLoc, SourceLocation EndLoc); + /// Called on well-formed '#pragma omp split' after parsing of its + /// associated statement. + StmtResult ActOnOpenMPSplitDirective(Stmt *AStmt, SourceLocation StartLoc, + SourceLocation EndLoc); /// Called on well-formed '#pragma omp interchange' after parsing of its /// clauses and the associated statement. StmtResult ActOnOpenMPInterchangeDirective(ArrayRef<OMPClause *> Clauses, diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp index 2c693b1958ee7..ef01943f11ca5 100644 --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -815,7 +815,8 @@ bool clang::isOpenMPLoopBoundSharingDirective(OpenMPDirectiveKind Kind) { bool clang::isOpenMPCanonicalLoopNestTransformationDirective( OpenMPDirectiveKind DKind) { return DKind == OMPD_tile || DKind == OMPD_unroll || DKind == OMPD_reverse || - DKind == OMPD_interchange || DKind == OMPD_stripe; + DKind == OMPD_split || DKind == OMPD_interchange || + DKind == OMPD_stripe; } bool clang::isOpenMPCanonicalLoopSequenceTransformationDirective( diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 5d906c378f1bf..f41ea386487f7 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -229,8 +229,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) { EmitOMPReverseDirective(cast<OMPReverseDirective>(*S)); break; case Stmt::OMPSplitDirectiveClass: - llvm_unreachable( - "OMPSplitDirective handled by EmitSimpleOMPExecutableDirective"); + EmitOMPSplitDirective(cast<OMPSplitDirective>(*S)); break; case Stmt::OMPInterchangeDirectiveClass: EmitOMPInterchangeDirective(cast<OMPInterchangeDirective>(*S)); diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp index ea89db5471089..9b7890347682d 100644 --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -196,6 +196,8 @@ class OMPLoopScope : public CodeGenFunction::RunCleanupsScope { PreInits = Unroll->getPreInits(); } else if (const auto *Reverse = dyn_cast<OMPReverseDirective>(&S)) { PreInits = Reverse->getPreInits(); + } else if (const auto *Split = dyn_cast<OMPSplitDirective>(&S)) { + PreInits = Split->getPreInits(); } else if (const auto *Interchange = dyn_cast<OMPInterchangeDirective>(&S)) { PreInits = Interchange->getPreInits(); @@ -2978,6 +2980,12 @@ void CodeGenFunction::EmitOMPReverseDirective(const OMPReverseDirective &S) { EmitStmt(S.getTransformedStmt()); } +void CodeGenFunction::EmitOMPSplitDirective(const OMPSplitDirective &S) { + // Emit the de-sugared statement (the two split loops). + OMPTransformDirectiveScopeRAII SplitScope(*this, &S); + EmitStmt(S.getTransformedStmt()); +} + void CodeGenFunction::EmitOMPInterchangeDirective( const OMPInterchangeDirective &S) { // Emit the de-sugared statement. diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index ae2956eeac57a..d82a589ae6b7a 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3907,6 +3907,7 @@ class CodeGenFunction : public CodeGenTypeCache { void EmitOMPStripeDirective(const OMPStripeDirective &S); void EmitOMPUnrollDirective(const OMPUnrollDirective &S); void EmitOMPReverseDirective(const OMPReverseDirective &S); + void EmitOMPSplitDirective(const OMPSplitDirective &S); void EmitOMPInterchangeDirective(const OMPInterchangeDirective &S); void EmitOMPFuseDirective(const OMPFuseDirective &S); void EmitOMPForDirective(const OMPForDirective &S); diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index b41803d23cb25..75301f3985354 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -3414,6 +3414,10 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, } Clause = ParseOpenMPPermutationClause(); break; + case OMPC_counts: + // TODO: Implement ParseOpenMPCountsClause() - not yet worked on + SkipUntil(tok::r_paren, tok::comma, StopBeforeMatch); + break; case OMPC_uses_allocators: Clause = ParseOpenMPUsesAllocatorClause(DKind); break; diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index e90884a89bf6e..8c22780b7ae4b 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -4591,6 +4591,7 @@ void SemaOpenMP::ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, case OMPD_stripe: case OMPD_unroll: case OMPD_reverse: + case OMPD_split: case OMPD_interchange: case OMPD_fuse: case OMPD_assume: @@ -6430,6 +6431,13 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective( "reverse directive does not support any clauses"); Res = ActOnOpenMPReverseDirective(AStmt, StartLoc, EndLoc); break; + case OMPD_split: + // TODO: Add counts clause support - not yet worked on + // Currently only supports basic split without clauses. + assert(ClausesWithImplicit.empty() && + "split directive does not support any clauses"); + Res = ActOnOpenMPSplitDirective(AStmt, StartLoc, EndLoc); + break; case OMPD_interchange: Res = ActOnOpenMPInterchangeDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc); @@ -15817,6 +15825,203 @@ StmtResult SemaOpenMP::ActOnOpenMPReverseDirective(Stmt *AStmt, buildPreInits(Context, PreInits)); } +StmtResult SemaOpenMP::ActOnOpenMPSplitDirective(Stmt *AStmt, + SourceLocation StartLoc, + SourceLocation EndLoc) { + ASTContext &Context = getASTContext(); + Scope *CurScope = SemaRef.getCurScope(); + + // Empty statement should only be possible if there already was an error. + if (!AStmt) + return StmtError(); + + constexpr unsigned NumLoops = 1; + Stmt *Body = nullptr; + SmallVector<OMPLoopBasedDirective::HelperExprs, NumLoops> LoopHelpers( + NumLoops); + SmallVector<SmallVector<Stmt *>, NumLoops + 1> OriginalInits; + if (!checkTransformableLoopNest(OMPD_split, AStmt, NumLoops, LoopHelpers, + Body, OriginalInits)) + return StmtError(); + + // Delay applying the transformation to when template is completely + // instantiated. + if (SemaRef.CurContext->isDependentContext()) + return OMPSplitDirective::Create(Context, StartLoc, EndLoc, AStmt, NumLoops, + nullptr, nullptr); + + assert(LoopHelpers.size() == NumLoops && + "Expecting a single-dimensional loop iteration space"); + assert(OriginalInits.size() == NumLoops && + "Expecting a single-dimensional loop iteration space"); + OMPLoopBasedDirective::HelperExprs &LoopHelper = LoopHelpers.front(); + + // Find the loop statement. + Stmt *LoopStmt = nullptr; + collectLoopStmts(AStmt, {LoopStmt}); + + // Determine the PreInit declarations. + SmallVector<Stmt *> PreInits; + addLoopPreInits(Context, LoopHelper, LoopStmt, OriginalInits[0], PreInits); + + auto *IterationVarRef = cast<DeclRefExpr>(LoopHelper.IterationVarRef); + QualType IVTy = IterationVarRef->getType(); + uint64_t IVWidth = Context.getTypeSize(IVTy); + auto *OrigVar = cast<DeclRefExpr>(LoopHelper.Counters.front()); + + // Iteration variable SourceLocations. + SourceLocation OrigVarLoc = OrigVar->getExprLoc(); + SourceLocation OrigVarLocBegin = OrigVar->getBeginLoc(); + SourceLocation OrigVarLocEnd = OrigVar->getEndLoc(); + + // Locations pointing to the transformation. + SourceLocation TransformLoc = StartLoc; + + // Internal variable names. + std::string OrigVarName = OrigVar->getNameInfo().getAsString(); + + // For Subexpressions with more than one use, we define a lambda + // that creates a new AST node at every use. + CaptureVars CopyTransformer(SemaRef); + auto MakeNumIterations = [&CopyTransformer, &LoopHelper]() -> Expr * { + return AssertSuccess( + CopyTransformer.TransformExpr(LoopHelper.NumIterations)); + }; + + // For split, we currently divide the loop into two equal parts. + // First loop: i = 0; i < n/2; ++i + // Second loop: i = n/2; i < n; ++i + // TODO: Add counts clause support - not yet worked on + + // Create iteration variable for the first split loop. + SmallString<64> FirstIVName(".split.first.iv."); + FirstIVName += OrigVarName; + VarDecl *FirstIVDecl = + buildVarDecl(SemaRef, {}, IVTy, FirstIVName, nullptr, OrigVar); + auto MakeFirstRef = [&SemaRef = this->SemaRef, FirstIVDecl, IVTy, + OrigVarLoc]() { + return buildDeclRefExpr(SemaRef, FirstIVDecl, IVTy, OrigVarLoc); + }; + + // Create iteration variable for the second split loop. + SmallString<64> SecondIVName(".split.second.iv."); + SecondIVName += OrigVarName; + VarDecl *SecondIVDecl = + buildVarDecl(SemaRef, {}, IVTy, SecondIVName, nullptr, OrigVar); + auto MakeSecondRef = [&SemaRef = this->SemaRef, SecondIVDecl, IVTy, + OrigVarLoc]() { + return buildDeclRefExpr(SemaRef, SecondIVDecl, IVTy, OrigVarLoc); + }; + + // Create n/2 expression for the split point. + auto *Two = IntegerLiteral::Create(Context, llvm::APInt(IVWidth, 2), IVTy, + TransformLoc); + ExprResult HalfIterations = SemaRef.BuildBinOp(CurScope, TransformLoc, BO_Div, + MakeNumIterations(), Two); + if (!HalfIterations.isUsable()) + return StmtError(); + + // First loop: init-statement: i = 0 + auto *Zero = IntegerLiteral::Create(Context, llvm::APInt::getZero(IVWidth), + FirstIVDecl->getType(), OrigVarLoc); + SemaRef.AddInitializerToDecl(FirstIVDecl, Zero, /*DirectInit=*/false); + StmtResult FirstInit = new (Context) + DeclStmt(DeclGroupRef(FirstIVDecl), OrigVarLocBegin, OrigVarLocEnd); + if (!FirstInit.isUsable()) + return StmtError(); + + // First loop: cond-expression (i < n/2) + ExprResult FirstCond = + SemaRef.BuildBinOp(CurScope, LoopHelper.Cond->getExprLoc(), BO_LT, + MakeFirstRef(), HalfIterations.get()); + if (!FirstCond.isUsable()) + return StmtError(); + + // First loop: incr-statement (++i) + ExprResult FirstIncr = SemaRef.BuildUnaryOp( + CurScope, LoopHelper.Inc->getExprLoc(), UO_PreInc, MakeFirstRef()); + if (!FirstIncr.isUsable()) + return StmtError(); + + // First loop: body - update original variable and execute body + // We need to create a copy of LoopHelper.Updates that uses FirstIV instead + // of the iteration variable. For now, use a simpler approach: directly + // assign the first IV to the original variable. + SmallVector<Stmt *, 4> FirstBodyStmts; + // Create update statement: origVar = .split.first.iv + // We'll use a BinaryOperator for assignment + ExprResult FirstUpdateExpr = SemaRef.BuildBinOp( + CurScope, OrigVarLoc, BO_Assign, OrigVar, MakeFirstRef()); + if (!FirstUpdateExpr.isUsable()) + return StmtError(); + FirstBodyStmts.push_back(FirstUpdateExpr.get()); + if (auto *CXXRangeFor = dyn_cast<CXXForRangeStmt>(LoopStmt)) + FirstBodyStmts.push_back(CXXRangeFor->getLoopVarStmt()); + FirstBodyStmts.push_back(Body); + auto *FirstBody = + CompoundStmt::Create(Context, FirstBodyStmts, FPOptionsOverride(), + Body->getBeginLoc(), Body->getEndLoc()); + + // Create first loop + auto *FirstLoop = new (Context) + ForStmt(Context, FirstInit.get(), FirstCond.get(), nullptr, + FirstIncr.get(), FirstBody, LoopHelper.Init->getBeginLoc(), + LoopHelper.Init->getBeginLoc(), LoopHelper.Inc->getEndLoc()); + + // Second loop: init-statement (i = n/2) + SemaRef.AddInitializerToDecl(SecondIVDecl, HalfIterations.get(), + /*DirectInit=*/false); + StmtResult SecondInit = new (Context) + DeclStmt(DeclGroupRef(SecondIVDecl), OrigVarLocBegin, OrigVarLocEnd); + if (!SecondInit.isUsable()) + return StmtError(); + + // Second loop: cond-expression (i < n) + ExprResult SecondCond = + SemaRef.BuildBinOp(CurScope, LoopHelper.Cond->getExprLoc(), BO_LT, + MakeSecondRef(), MakeNumIterations()); + if (!SecondCond.isUsable()) + return StmtError(); + + // Second loop: incr-statement (++i) + ExprResult SecondIncr = SemaRef.BuildUnaryOp( + CurScope, LoopHelper.Inc->getExprLoc(), UO_PreInc, MakeSecondRef()); + if (!SecondIncr.isUsable()) + return StmtError(); + + // Second loop: body - update original variable and execute body + SmallVector<Stmt *, 4> SecondBodyStmts; + // Create update statement: origVar = .split.second.iv + ExprResult SecondUpdateExpr = SemaRef.BuildBinOp( + CurScope, OrigVarLoc, BO_Assign, OrigVar, MakeSecondRef()); + if (!SecondUpdateExpr.isUsable()) + return StmtError(); + SecondBodyStmts.push_back(SecondUpdateExpr.get()); + if (auto *CXXRangeFor = dyn_cast<CXXForRangeStmt>(LoopStmt)) + SecondBodyStmts.push_back(CXXRangeFor->getLoopVarStmt()); + SecondBodyStmts.push_back(Body); + auto *SecondBody = + CompoundStmt::Create(Context, SecondBodyStmts, FPOptionsOverride(), + Body->getBeginLoc(), Body->getEndLoc()); + + // Create second loop + auto *SecondLoop = new (Context) + ForStmt(Context, SecondInit.get(), SecondCond.get(), nullptr, + SecondIncr.get(), SecondBody, LoopHelper.Init->getBeginLoc(), + LoopHelper.Init->getBeginLoc(), LoopHelper.Inc->getEndLoc()); + + // Combine both loops into a compound statement + SmallVector<Stmt *, 2> SplitLoops; + SplitLoops.push_back(FirstLoop); + SplitLoops.push_back(SecondLoop); + auto *SplitStmt = + CompoundStmt::Create(Context, SplitLoops, FPOptionsOverride(), + FirstLoop->getBeginLoc(), SecondLoop->getEndLoc()); + + return OMPSplitDirective::Create(Context, StartLoc, EndLoc, AStmt, NumLoops, + SplitStmt, buildPreInits(Context, PreInits)); +} + StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective( ArrayRef<OMPClause *> Clauses, Stmt *AStmt, SourceLocation StartLoc, SourceLocation EndLoc) { diff --git a/clang/test/OpenMP/split_ast_print.cpp b/clang/test/OpenMP/split_ast_print.cpp new file mode 100644 index 0000000000000..b24eae4a9bead --- /dev/null +++ b/clang/test/OpenMP/split_ast_print.cpp @@ -0,0 +1,28 @@ +// Check no warnings/errors and that split is recognized +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -fsyntax-only -verify %s +// expected-no-diagnostics + +// Check AST: OMPSplitDirective with associated for-loop +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -ast-dump %s | FileCheck %s --check-prefix=DUMP + +// Check unparsing +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -ast-print %s | FileCheck %s --check-prefix=PRINT + +#ifndef HEADER +#define HEADER + +extern "C" void body(...); + +// PRINT-LABEL: void foo( +// DUMP-LABEL: FunctionDecl {{.*}} foo +void foo(int n) { + // PRINT: #pragma omp split + // DUMP: OMPSplitDirective + #pragma omp split + // PRINT: for (int i = 0; i < n; ++i) + // DUMP: ForStmt + for (int i = 0; i < n; ++i) + body(i); +} + +#endif diff --git a/clang/test/OpenMP/split_simple_test.c b/clang/test/OpenMP/split_simple_test.c new file mode 100644 index 0000000000000..bc0d3c4770890 --- /dev/null +++ b/clang/test/OpenMP/split_simple_test.c @@ -0,0 +1,26 @@ +/* + * Simple test for #pragma omp split: one canonical for-loop is transformed + * into two loops (first half and second half of iterations). This file + * verifies compilation and correct result at runtime. + * + * Compile: clang -fopenmp -fopenmp-version=60 -o split_simple_test split_simple_test.c + * Run: ./split_simple_test + * Expected: prints "sum 0..9 = 45 (expected 45)", exit code 0. + */ +// Verify the split directive compiles and links. +// RUN: %clang -fopenmp -fopenmp-version=60 -o %t %s + +#include <stdio.h> + +int main(void) { + const int n = 10; + int sum = 0; + +#pragma omp split + for (int i = 0; i < n; ++i) { + sum += i; + } + + printf("sum 0..%d = %d (expected %d)\n", n - 1, sum, n * (n - 1) / 2); + return (sum == n * (n - 1) / 2) ? 0 : 1; +} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 31b6a3222d916..74600ea55e45c 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -6326,6 +6326,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("OMPInterchangeDirective"); case CXCursor_OMPFuseDirective: return cxstring::createRef("OMPFuseDirective"); + case CXCursor_OMPSplitDirective: + return cxstring::createRef("OMPSplitDirective"); case CXCursor_OMPForDirective: return cxstring::createRef("OMPForDirective"); case CXCursor_OMPForSimdDirective: diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index d8247f8161506..4a3afa8b64675 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -697,7 +697,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, K = CXCursor_OMPReverseDirective; break; case Stmt::OMPSplitDirectiveClass: - K = CXCursor_UnexposedStmt; + K = CXCursor_OMPSplitDirective; break; case Stmt::OMPInterchangeDirectiveClass: K = CXCursor_OMPInterchangeDirective; >From cd1d79b3f0505fc401528586ab888251c545d908 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Fri, 13 Mar 2026 10:21:40 -0400 Subject: [PATCH 8/8] duplicate_split_def_removed --- llvm/include/llvm/Frontend/OpenMP/OMP.td | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td index fc3da2837b208..0703428186e7c 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -1202,16 +1202,6 @@ def OMP_EndSingle : Directive<[Spelling<"end single">]> { let category = OMP_Single.category; let languages = [L_Fortran]; } -def OMP_Split : Directive<[Spelling<"split">]> { - let allowedClauses = [ - VersionedClause<OMPC_Apply, 60>, - ]; - let allowedOnceClauses = [ - VersionedClause<OMPC_Counts, 60>, - ]; - let association = AS_LoopNest; - let category = CA_Executable; -} def OMP_Target : Directive<[Spelling<"target">]> { let allowedClauses = [ VersionedClause<OMPC_Allocate>, _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
