https://github.com/loopacino updated https://github.com/llvm/llvm-project/pull/206977
>From 0fb5bd42f1e127faea963c359b1e3c69cb969c60 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Wed, 1 Jul 2026 09:06:58 -0400 Subject: [PATCH 01/12] add OMPFlattenDirective ast node --- clang/include/clang/AST/RecursiveASTVisitor.h | 3 + clang/include/clang/AST/StmtOpenMP.h | 75 ++++++++++++++++++- clang/include/clang/Basic/StmtNodes.td | 1 + clang/lib/AST/StmtOpenMP.cpp | 20 +++++ clang/lib/AST/StmtPrinter.cpp | 5 ++ clang/lib/AST/StmtProfile.cpp | 4 + clang/lib/Basic/OpenMPKinds.cpp | 2 +- 7 files changed, 108 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index b000a34043696..e425f7a31f59c 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -3227,6 +3227,9 @@ DEF_TRAVERSE_STMT(OMPFuseDirective, DEF_TRAVERSE_STMT(OMPInterchangeDirective, { TRY_TO(TraverseOMPExecutableDirective(S)); }) +DEF_TRAVERSE_STMT(OMPFlattenDirective, + { TRY_TO(TraverseOMPExecutableDirective(S)); }) + DEF_TRAVERSE_STMT(OMPSplitDirective, { TRY_TO(TraverseOMPExecutableDirective(S)); }) diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h index dbc76e7df8ecd..1420331f37c1f 100644 --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/clang/include/clang/AST/StmtOpenMP.h @@ -1041,7 +1041,7 @@ class OMPCanonicalLoopNestTransformationDirective Stmt::StmtClass C = T->getStmtClass(); return C == OMPTileDirectiveClass || C == OMPUnrollDirectiveClass || C == OMPReverseDirectiveClass || C == OMPInterchangeDirectiveClass || - C == OMPStripeDirectiveClass; + C == OMPStripeDirectiveClass || C == OMPFlattenDirectiveClass; } }; @@ -5959,6 +5959,79 @@ class OMPInterchangeDirective final } }; +/// Represents the '#pragma omp flatten' loop transformation directive. +/// +/// \code{c} +/// #pragma omp flatten +/// for (int i = 0; i < m; ++i) +/// for (int j = 0; j < n; ++j) +/// .. +/// \endcode +class OMPFlattenDirective final + : public OMPCanonicalLoopNestTransformationDirective { + friend class ASTStmtReader; + friend class OMPExecutableDirective; + + /// Offsets of child members. + enum { + PreInitsOffset = 0, + TransformedStmtOffset, + }; + + explicit OMPFlattenDirective(SourceLocation StartLoc, SourceLocation EndLoc, + unsigned NumLoops) + : OMPCanonicalLoopNestTransformationDirective( + OMPFlattenDirectiveClass, llvm::omp::OMPD_flatten, StartLoc, EndLoc, + NumLoops) {} + + void setPreInits(Stmt *PreInits) { + Data->getChildren()[PreInitsOffset] = PreInits; + } + + void setTransformedStmt(Stmt *S) { + Data->getChildren()[TransformedStmtOffset] = S; + } + +public: + /// Create a new AST node representation for '#pragma omp flatten'. + /// + /// \param C Context of the AST. + /// \param StartLoc Location of the introducer (e.g. the 'omp' token). + /// \param EndLoc Location of the directive's end (e.g. the tok::eod). + /// \param Clauses The directive's clauses. + /// \param NumLoops Number of affected loops (the flatten depth, currently + /// always 2). + /// \param AssociatedStmt The outermost associated loop. + /// \param TransformedStmt The flattened loop, or nullptr in dependent + /// contexts. + /// \param PreInits Helper preinits statements for the loop nest. + static OMPFlattenDirective * + Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef<OMPClause *> Clauses, unsigned NumLoops, Stmt *AssociatedStmt, + Stmt *TransformedStmt, Stmt *PreInits); + + /// Build an empty '#pragma omp flatten' AST node for deserialization. + /// + /// \param C Context of the AST. + /// \param NumClauses Number of clauses to allocate. + /// \param NumLoops Number of associated loops to allocate. + static OMPFlattenDirective * + CreateEmpty(const ASTContext &C, unsigned NumClauses, unsigned NumLoops); + + /// Gets the flattened loop after the transformation. This is the de-sugared + /// replacement or nullptr in dependent contexts. + Stmt *getTransformedStmt() const { + return Data->getChildren()[TransformedStmtOffset]; + } + + /// Return preinits statement. + Stmt *getPreInits() const { return Data->getChildren()[PreInitsOffset]; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == OMPFlattenDirectiveClass; + } +}; + /// The base class for all transformation directives of canonical loop /// sequences (currently only 'fuse') class OMPCanonicalLoopSequenceTransformationDirective diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index e166894ea024b..0d7a0c1490a1e 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -247,6 +247,7 @@ def OMPReverseDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>; def OMPSplitDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>; def OMPInterchangeDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>; +def OMPFlattenDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>; def OMPCanonicalLoopSequenceTransformationDirective : StmtNode<OMPExecutableDirective, 1>; def OMPFuseDirective diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp index 9d6b315effb41..477604ef3dafb 100644 --- a/clang/lib/AST/StmtOpenMP.cpp +++ b/clang/lib/AST/StmtOpenMP.cpp @@ -552,6 +552,26 @@ OMPInterchangeDirective::CreateEmpty(const ASTContext &C, unsigned NumClauses, SourceLocation(), SourceLocation(), NumLoops); } +OMPFlattenDirective *OMPFlattenDirective::Create( + const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef<OMPClause *> Clauses, unsigned NumLoops, Stmt *AssociatedStmt, + Stmt *TransformedStmt, Stmt *PreInits) { + OMPFlattenDirective *Dir = createDirective<OMPFlattenDirective>( + C, Clauses, AssociatedStmt, TransformedStmtOffset + 1, StartLoc, EndLoc, + NumLoops); + Dir->setTransformedStmt(TransformedStmt); + Dir->setPreInits(PreInits); + return Dir; +} + +OMPFlattenDirective *OMPFlattenDirective::CreateEmpty(const ASTContext &C, + unsigned NumClauses, + unsigned NumLoops) { + return createEmptyDirective<OMPFlattenDirective>( + C, NumClauses, /*HasAssociatedStmt=*/true, TransformedStmtOffset + 1, + SourceLocation(), SourceLocation(), NumLoops); +} + OMPSplitDirective * OMPSplitDirective::Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, ArrayRef<OMPClause *> Clauses, diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 6c3294573e9d4..0bacb79ed86e3 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -800,6 +800,11 @@ void StmtPrinter::VisitOMPInterchangeDirective(OMPInterchangeDirective *Node) { PrintOMPExecutableDirective(Node); } +void StmtPrinter::VisitOMPFlattenDirective(OMPFlattenDirective *Node) { + Indent() << "#pragma omp flatten"; + PrintOMPExecutableDirective(Node); +} + void StmtPrinter::VisitOMPSplitDirective(OMPSplitDirective *Node) { Indent() << "#pragma omp split"; PrintOMPExecutableDirective(Node); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 90eab530e0c2e..de7ed322d7424 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1071,6 +1071,10 @@ void StmtProfiler::VisitOMPInterchangeDirective( VisitOMPCanonicalLoopNestTransformationDirective(S); } +void StmtProfiler::VisitOMPFlattenDirective(const OMPFlattenDirective *S) { + VisitOMPCanonicalLoopNestTransformationDirective(S); +} + void StmtProfiler::VisitOMPSplitDirective(const OMPSplitDirective *S) { VisitOMPCanonicalLoopNestTransformationDirective(S); } diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp index 675d86349c933..57e0347f8ea77 100644 --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -818,7 +818,7 @@ bool clang::isOpenMPCanonicalLoopNestTransformationDirective( OpenMPDirectiveKind DKind) { return DKind == OMPD_tile || DKind == OMPD_unroll || DKind == OMPD_reverse || DKind == OMPD_split || DKind == OMPD_interchange || - DKind == OMPD_stripe; + DKind == OMPD_stripe || DKind == OMPD_flatten; } bool clang::isOpenMPCanonicalLoopSequenceTransformationDirective( >From 933007b6333584fb358adc4f89a61db1ae15d4cb Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Wed, 8 Jul 2026 16:02:27 -0400 Subject: [PATCH 02/12] add OMPDepthClause ast node --- clang/include/clang/AST/OpenMPClause.h | 68 +++++++++++++++++++ clang/include/clang/AST/RecursiveASTVisitor.h | 6 ++ clang/lib/AST/OpenMPClause.cpp | 25 +++++++ clang/lib/AST/StmtProfile.cpp | 5 ++ clang/lib/Parse/ParseOpenMP.cpp | 1 + llvm/include/llvm/Frontend/OpenMP/OMP.td | 1 + 6 files changed, 106 insertions(+) diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 8ceafc4669297..50d1364a9726d 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -1380,6 +1380,74 @@ class OMPPartialClause final : public OMPClause { } }; +/// This represents the 'depth' clause on the '#pragma omp flatten' (and +/// '#pragma omp fuse') loop-transformation directives. +/// +/// \code +/// #pragma omp flatten depth(3) +/// \endcode +/// In this example the 'flatten' directive has a 'depth' clause whose argument +/// '3' specifies how many perfectly nested loops are combined into one. +/// The argument must be a positive integer constant expression that evaluates +/// to at most the loop nest depth of the associated loop nest. +class OMPDepthClause final : public OMPClause { + friend class OMPClauseReader; + + /// Location of '('. + SourceLocation LParenLoc; + + /// The depth expression (number of loops to flatten). + Stmt *Depth = nullptr; + + /// Build an empty clause. + explicit OMPDepthClause() : OMPClause(llvm::omp::OMPC_depth, {}, {}) {} + + /// Set the depth expression. + void setDepth(Expr *E) { Depth = E; } + + /// Sets the location of '('. + void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; } + +public: + /// Build an AST node for a 'depth' clause. + /// + /// \param C Context of the AST. + /// \param StartLoc Location of the 'depth' identifier. + /// \param LParenLoc Location of '('. + /// \param EndLoc Location of ')'. + /// \param Depth The depth expression. + static OMPDepthClause *Create(const ASTContext &C, SourceLocation StartLoc, + SourceLocation LParenLoc, SourceLocation EndLoc, + Expr *Depth); + + /// Build an empty 'depth' AST node for deserialization. + /// + /// \param C Context of the AST. + static OMPDepthClause *CreateEmpty(const ASTContext &C); + + /// Returns the location of '('. + SourceLocation getLParenLoc() const { return LParenLoc; } + + /// Returns the depth expression or nullptr if not set. + Expr *getDepth() const { return cast_or_null<Expr>(Depth); } + + child_range children() { return child_range(&Depth, &Depth + 1); } + const_child_range children() const { + return const_child_range(&Depth, &Depth + 1); + } + + child_range used_children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range used_children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == llvm::omp::OMPC_depth; + } +}; + /// This represents 'collapse' clause in the '#pragma omp ...' /// directive. /// diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index e425f7a31f59c..da55e97fdfcb5 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -3568,6 +3568,12 @@ bool RecursiveASTVisitor<Derived>::VisitOMPPartialClause(OMPPartialClause *C) { return true; } +template <typename Derived> +bool RecursiveASTVisitor<Derived>::VisitOMPDepthClause(OMPDepthClause *C) { + TRY_TO(TraverseStmt(C->getDepth())); + return true; +} + template <typename Derived> bool RecursiveASTVisitor<Derived>::VisitOMPCollapseClause(OMPCollapseClause *C) { diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index ed00e80144c25..7b2f67a31cad9 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -1057,6 +1057,22 @@ OMPPartialClause *OMPPartialClause::CreateEmpty(const ASTContext &C) { return new (C) OMPPartialClause(); } +OMPDepthClause *OMPDepthClause::Create(const ASTContext &C, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc, Expr *Depth) { + OMPDepthClause *Clause = CreateEmpty(C); + Clause->setLocStart(StartLoc); + Clause->setLParenLoc(LParenLoc); + Clause->setLocEnd(EndLoc); + Clause->setDepth(Depth); + return Clause; +} + +OMPDepthClause *OMPDepthClause::CreateEmpty(const ASTContext &C) { + return new (C) OMPDepthClause(); +} + OMPLoopRangeClause * OMPLoopRangeClause::Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation FirstLoc, @@ -2069,6 +2085,15 @@ void OMPClausePrinter::VisitOMPPermutationClause(OMPPermutationClause *Node) { void OMPClausePrinter::VisitOMPFullClause(OMPFullClause *Node) { OS << "full"; } +void OMPClausePrinter::VisitOMPDepthClause(OMPDepthClause *Node) { + OS << "depth"; + if (Expr *Depth = Node->getDepth()) { + OS << '('; + Depth->printPretty(OS, nullptr, Policy, 0); + OS << ')'; + } +} + void OMPClausePrinter::VisitOMPPartialClause(OMPPartialClause *Node) { OS << "partial"; diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index de7ed322d7424..838d6d02f7446 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -521,6 +521,11 @@ void OMPClauseProfiler::VisitOMPPartialClause(const OMPPartialClause *C) { Profiler->VisitExpr(Factor); } +void OMPClauseProfiler::VisitOMPDepthClause(const OMPDepthClause *C) { + if (const Expr *Depth = C->getDepth()) + Profiler->VisitExpr(Depth); +} + void OMPClauseProfiler::VisitOMPLoopRangeClause(const OMPLoopRangeClause *C) { if (const Expr *First = C->getFirst()) Profiler->VisitExpr(First); diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index af52295df2d8b..952a517205381 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -3247,6 +3247,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, case OMPC_nocontext: case OMPC_filter: case OMPC_partial: + case OMPC_depth: case OMPC_align: case OMPC_message: case OMPC_ompx_dyn_cgroup_mem: diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td index 679a944fc4358..d49c5ee1a0db3 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -161,6 +161,7 @@ def OMPC_Depobj : Clause<[Spelling<"depobj">]> { let isImplicit = true; } def OMPC_Depth : Clause<[Spelling<"depth">]> { + let clangClass = "OMPDepthClause"; let flangClass = "ScalarIntConstantExpr"; } def OMPC_Destroy : Clause<[Spelling<"destroy">]> { >From b63024e1e342cd168ef128a3f6a24909df2a82db Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Wed, 8 Jul 2026 16:04:44 -0400 Subject: [PATCH 03/12] template-transform support for flatten directive & depth clause --- clang/lib/Sema/TreeTransform.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 3b99ff4bb9e23..865622ad50f1c 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1804,6 +1804,14 @@ class TreeTransform { LParenLoc, EndLoc); } + /// Build a new OpenMP 'depth' clause. + OMPClause *RebuildOMPDepthClause(Expr *Depth, SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc) { + return getSema().OpenMP().ActOnOpenMPDepthClause(Depth, StartLoc, LParenLoc, + EndLoc); + } + OMPClause * RebuildOMPLoopRangeClause(Expr *First, Expr *Count, SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation FirstLoc, @@ -9791,6 +9799,17 @@ TreeTransform<Derived>::TransformOMPSplitDirective(OMPSplitDirective *D) { return Res; } +template <typename Derived> +StmtResult +TreeTransform<Derived>::TransformOMPFlattenDirective(OMPFlattenDirective *D) { + DeclarationNameInfo DirName; + getDerived().getSema().OpenMP().StartOpenMPDSABlock( + D->getDirectiveKind(), DirName, nullptr, D->getBeginLoc()); + StmtResult Res = getDerived().TransformOMPExecutableDirective(D); + getDerived().getSema().OpenMP().EndOpenMPDSABlock(Res.get()); + return Res; +} + template <typename Derived> StmtResult TreeTransform<Derived>::TransformOMPFuseDirective(OMPFuseDirective *D) { @@ -10717,6 +10736,20 @@ TreeTransform<Derived>::TransformOMPPartialClause(OMPPartialClause *C) { C->getEndLoc()); } +template <typename Derived> +OMPClause *TreeTransform<Derived>::TransformOMPDepthClause(OMPDepthClause *C) { + ExprResult T = getDerived().TransformExpr(C->getDepth()); + if (T.isInvalid()) + return nullptr; + Expr *Depth = T.get(); + bool Changed = Depth != C->getDepth(); + + if (!Changed && !getDerived().AlwaysRebuild()) + return C; + return RebuildOMPDepthClause(Depth, C->getBeginLoc(), C->getLParenLoc(), + C->getEndLoc()); +} + template <typename Derived> OMPClause * TreeTransform<Derived>::TransformOMPLoopRangeClause(OMPLoopRangeClause *C) { >From deafb222a7d251069c68a44fa267e58e750f1064 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Wed, 8 Jul 2026 16:08:12 -0400 Subject: [PATCH 04/12] serialize flatten --- clang/include/clang/Serialization/ASTBitCodes.h | 1 + clang/lib/Serialization/ASTReaderStmt.cpp | 11 +++++++++++ clang/lib/Serialization/ASTWriterStmt.cpp | 5 +++++ 3 files changed, 17 insertions(+) diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 279380de2f7fe..93a0e0a454b68 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1970,6 +1970,7 @@ enum StmtCode { STMT_OMP_REVERSE_DIRECTIVE, STMT_OMP_SPLIT_DIRECTIVE, STMT_OMP_INTERCHANGE_DIRECTIVE, + STMT_OMP_FLATTEN_DIRECTIVE, STMT_OMP_FUSE_DIRECTIVE, STMT_OMP_FOR_DIRECTIVE, STMT_OMP_FOR_SIMD_DIRECTIVE, diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 02ccf8d4d41c2..8c2debd6cdf32 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2537,6 +2537,10 @@ void ASTStmtReader::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) { VisitOMPCanonicalLoopNestTransformationDirective(D); } +void ASTStmtReader::VisitOMPFlattenDirective(OMPFlattenDirective *D) { + VisitOMPCanonicalLoopNestTransformationDirective(D); +} + void ASTStmtReader::VisitOMPSplitDirective(OMPSplitDirective *D) { VisitOMPCanonicalLoopNestTransformationDirective(D); } @@ -3719,6 +3723,13 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { break; } + case STMT_OMP_FLATTEN_DIRECTIVE: { + unsigned NumLoops = Record[ASTStmtReader::NumStmtFields]; + unsigned NumClauses = Record[ASTStmtReader::NumStmtFields + 1]; + S = OMPFlattenDirective::CreateEmpty(Context, NumClauses, NumLoops); + break; + } + case STMT_OMP_FOR_DIRECTIVE: { unsigned CollapsedNum = Record[ASTStmtReader::NumStmtFields]; unsigned NumClauses = Record[ASTStmtReader::NumStmtFields + 1]; diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 4e9eadd730a56..dfe28a9b915e3 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2547,6 +2547,11 @@ void ASTStmtWriter::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) { Code = serialization::STMT_OMP_INTERCHANGE_DIRECTIVE; } +void ASTStmtWriter::VisitOMPFlattenDirective(OMPFlattenDirective *D) { + VisitOMPCanonicalLoopNestTransformationDirective(D); + Code = serialization::STMT_OMP_FLATTEN_DIRECTIVE; +} + void ASTStmtWriter::VisitOMPSplitDirective(OMPSplitDirective *D) { VisitOMPCanonicalLoopNestTransformationDirective(D); Code = serialization::STMT_OMP_SPLIT_DIRECTIVE; >From 3e2dfeb99230057b7cb9c484182ae33befbe1e41 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Wed, 8 Jul 2026 16:09:03 -0400 Subject: [PATCH 05/12] serialize depth --- clang/lib/Serialization/ASTReader.cpp | 8 ++++++++ clang/lib/Serialization/ASTWriter.cpp | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index f8a6a38bb9b5c..4f51d79a52105 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -11495,6 +11495,9 @@ OMPClause *OMPClauseReader::readClause() { case llvm::omp::OMPC_partial: C = OMPPartialClause::CreateEmpty(Context); break; + case llvm::omp::OMPC_depth: + C = OMPDepthClause::CreateEmpty(Context); + break; case llvm::omp::OMPC_looprange: C = OMPLoopRangeClause::CreateEmpty(Context); break; @@ -11924,6 +11927,11 @@ void OMPClauseReader::VisitOMPPartialClause(OMPPartialClause *C) { C->setLParenLoc(Record.readSourceLocation()); } +void OMPClauseReader::VisitOMPDepthClause(OMPDepthClause *C) { + C->setDepth(Record.readSubExpr()); + C->setLParenLoc(Record.readSourceLocation()); +} + void OMPClauseReader::VisitOMPLoopRangeClause(OMPLoopRangeClause *C) { C->setFirst(Record.readSubExpr()); C->setCount(Record.readSubExpr()); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 357a7f7e95fa0..cbfeaa9e6638a 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -8106,6 +8106,11 @@ void OMPClauseWriter::VisitOMPPartialClause(OMPPartialClause *C) { Record.AddSourceLocation(C->getLParenLoc()); } +void OMPClauseWriter::VisitOMPDepthClause(OMPDepthClause *C) { + Record.AddStmt(C->getDepth()); + Record.AddSourceLocation(C->getLParenLoc()); +} + void OMPClauseWriter::VisitOMPLoopRangeClause(OMPLoopRangeClause *C) { Record.AddStmt(C->getFirst()); Record.AddStmt(C->getCount()); >From 4ae746ca1eeaf11db10e859572a4500029d01cce Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Wed, 8 Jul 2026 16:17:29 -0400 Subject: [PATCH 06/12] add analysis traversal support for flatten directive --- clang/lib/Sema/SemaExceptionSpec.cpp | 1 + clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 40d530a1f3925..995b5b6010978 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1508,6 +1508,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Stmt::OMPUnrollDirectiveClass: case Stmt::OMPReverseDirectiveClass: case Stmt::OMPInterchangeDirectiveClass: + case Stmt::OMPFlattenDirectiveClass: case Stmt::OMPSplitDirectiveClass: case Stmt::OMPFuseDirectiveClass: case Stmt::OMPSingleDirectiveClass: diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index cfb294736ee02..3f666be00cc24 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1783,6 +1783,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPStripeDirectiveClass: case Stmt::OMPTileDirectiveClass: case Stmt::OMPInterchangeDirectiveClass: + case Stmt::OMPFlattenDirectiveClass: case Stmt::OMPSplitDirectiveClass: case Stmt::OMPFuseDirectiveClass: case Stmt::OMPInteropDirectiveClass: >From 415eba88ad0af63c552b4c5c2eabd660bcb29c02 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Wed, 8 Jul 2026 16:21:25 -0400 Subject: [PATCH 07/12] codegen flatten directive --- clang/lib/CodeGen/CGStmt.cpp | 3 +++ clang/lib/CodeGen/CGStmtOpenMP.cpp | 8 ++++++++ clang/lib/CodeGen/CodeGenFunction.h | 1 + 3 files changed, 12 insertions(+) diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 232094777f233..27f48cb63b553 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -236,6 +236,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) { case Stmt::OMPInterchangeDirectiveClass: EmitOMPInterchangeDirective(cast<OMPInterchangeDirective>(*S)); break; + case Stmt::OMPFlattenDirectiveClass: + EmitOMPFlattenDirective(cast<OMPFlattenDirective>(*S)); + break; case Stmt::OMPFuseDirectiveClass: EmitOMPFuseDirective(cast<OMPFuseDirective>(*S)); break; diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp index 95fd6694437fe..25da3430a23fe 100644 --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -218,6 +218,8 @@ class OMPLoopScope : public CodeGenFunction::RunCleanupsScope { } else if (const auto *Interchange = dyn_cast<OMPInterchangeDirective>(&S)) { PreInits = Interchange->getPreInits(); + } else if (const auto *Flatten = dyn_cast<OMPFlattenDirective>(&S)) { + PreInits = Flatten->getPreInits(); } else { llvm_unreachable("Unknown loop-based directive kind."); } @@ -3248,6 +3250,12 @@ void CodeGenFunction::EmitOMPInterchangeDirective( EmitStmt(S.getTransformedStmt()); } +void CodeGenFunction::EmitOMPFlattenDirective(const OMPFlattenDirective &S) { + // Emit the de-sugared statement. + OMPTransformDirectiveScopeRAII FlattenScope(*this, &S); + EmitStmt(S.getTransformedStmt()); +} + void CodeGenFunction::EmitOMPFuseDirective(const OMPFuseDirective &S) { // Emit the de-sugared statement OMPTransformDirectiveScopeRAII FuseScope(*this, &S); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 6d0718c243812..eea0163422cd3 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3934,6 +3934,7 @@ class CodeGenFunction : public CodeGenTypeCache { void EmitOMPReverseDirective(const OMPReverseDirective &S); void EmitOMPSplitDirective(const OMPSplitDirective &S); void EmitOMPInterchangeDirective(const OMPInterchangeDirective &S); + void EmitOMPFlattenDirective(const OMPFlattenDirective &S); void EmitOMPFuseDirective(const OMPFuseDirective &S); void EmitOMPForDirective(const OMPForDirective &S); void EmitOMPForSimdDirective(const OMPForSimdDirective &S); >From 814e2e9fe9bf4e28e4557999fbc16beec144cc39 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Wed, 8 Jul 2026 16:24:42 -0400 Subject: [PATCH 08/12] add NYI CIR stub --- clang/lib/CIR/CodeGen/CIRGenFunction.h | 1 + clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 2 ++ clang/lib/CIR/CodeGen/CIRGenStmtOpenMP.cpp | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 322355fde3957..8374583ffd4d0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -2559,6 +2559,7 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::LogicalResult emitOMPSplitDirective(const OMPSplitDirective &s); mlir::LogicalResult emitOMPInterchangeDirective(const OMPInterchangeDirective &s); + mlir::LogicalResult emitOMPFlattenDirective(const OMPFlattenDirective &s); mlir::LogicalResult emitOMPAssumeDirective(const OMPAssumeDirective &s); mlir::LogicalResult emitOMPMaskedDirective(const OMPMaskedDirective &s); mlir::LogicalResult emitOMPStripeDirective(const OMPStripeDirective &s); diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index d3acac5801e74..33d425b22d61a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -408,6 +408,8 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s, return emitOMPSplitDirective(cast<OMPSplitDirective>(*s)); case Stmt::OMPInterchangeDirectiveClass: return emitOMPInterchangeDirective(cast<OMPInterchangeDirective>(*s)); + case Stmt::OMPFlattenDirectiveClass: + return emitOMPFlattenDirective(cast<OMPFlattenDirective>(*s)); case Stmt::OMPAssumeDirectiveClass: return emitOMPAssumeDirective(cast<OMPAssumeDirective>(*s)); case Stmt::OMPMaskedDirectiveClass: diff --git a/clang/lib/CIR/CodeGen/CIRGenStmtOpenMP.cpp b/clang/lib/CIR/CodeGen/CIRGenStmtOpenMP.cpp index 17a1fb8090f5c..4fffd17b25a16 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmtOpenMP.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmtOpenMP.cpp @@ -587,6 +587,11 @@ CIRGenFunction::emitOMPInterchangeDirective(const OMPInterchangeDirective &s) { return mlir::failure(); } mlir::LogicalResult +CIRGenFunction::emitOMPFlattenDirective(const OMPFlattenDirective &s) { + getCIRGenModule().errorNYI(s.getSourceRange(), "OpenMP OMPFlattenDirective"); + return mlir::failure(); +} +mlir::LogicalResult CIRGenFunction::emitOMPAssumeDirective(const OMPAssumeDirective &s) { getCIRGenModule().errorNYI(s.getSourceRange(), "OpenMP OMPAssumeDirective"); return mlir::failure(); >From 0027ec6ab982a83766bd65f3e1bc45d9a671fc62 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Wed, 8 Jul 2026 16:26:31 -0400 Subject: [PATCH 09/12] add libclang traversal support for depth clause --- clang/tools/libclang/CIndex.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index ac2fad38a1348..f8ab63e3e0dae 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -2378,6 +2378,10 @@ void OMPClauseEnqueue::VisitOMPLoopRangeClause(const OMPLoopRangeClause *C) { Visitor->AddStmt(C->getCount()); } +void OMPClauseEnqueue::VisitOMPDepthClause(const OMPDepthClause *C) { + Visitor->AddStmt(C->getDepth()); +} + void OMPClauseEnqueue::VisitOMPAllocatorClause(const OMPAllocatorClause *C) { Visitor->AddStmt(C->getAllocator()); } >From f5ac5f740c414310c8482f652f1bed936ae7cda3 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Fri, 10 Jul 2026 06:54:16 -0400 Subject: [PATCH 10/12] sema --- clang/include/clang/Sema/SemaOpenMP.h | 10 + clang/lib/Sema/SemaOpenMP.cpp | 295 ++++++++++++++++++++++++++ 2 files changed, 305 insertions(+) diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 3621ce96b8724..7b086388416a0 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -470,6 +470,12 @@ class SemaOpenMP : public SemaBase { SourceLocation StartLoc, SourceLocation EndLoc); + /// Called on well-formed '#pragma omp flatten' after parsing of its + /// clauses and the associated statement. + StmtResult ActOnOpenMPFlattenDirective(ArrayRef<OMPClause *> Clauses, + Stmt *AStmt, SourceLocation StartLoc, + SourceLocation EndLoc); + /// Called on well-formed '#pragma omp fuse' after parsing of its /// clauses and the associated statement. StmtResult ActOnOpenMPFuseDirective(ArrayRef<OMPClause *> Clauses, @@ -935,6 +941,10 @@ class SemaOpenMP : public SemaBase { OMPClause *ActOnOpenMPPartialClause(Expr *FactorExpr, SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc); + /// Called on well-formed 'depth' clause. + OMPClause *ActOnOpenMPDepthClause(Expr *DepthExpr, SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc); /// Called on well-formed 'collapse' clause. OMPClause *ActOnOpenMPCollapseClause(Expr *NumForLoops, SourceLocation StartLoc, diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 247a4a6ad9271..fcc256dec3c88 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -4629,6 +4629,7 @@ void SemaOpenMP::ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, case OMPD_reverse: case OMPD_split: case OMPD_interchange: + case OMPD_flatten: case OMPD_fuse: case OMPD_assume: break; @@ -6475,6 +6476,10 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective( Res = ActOnOpenMPInterchangeDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc); break; + case OMPD_flatten: + Res = ActOnOpenMPFlattenDirective(ClausesWithImplicit, AStmt, StartLoc, + EndLoc); + break; case OMPD_fuse: Res = ActOnOpenMPFuseDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc); @@ -16390,6 +16395,277 @@ StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective( buildPreInits(Context, PreInits)); } +/// Counts the perfectly nested canonical loop depth at \p AStmt. Returns +/// std::nullopt if a loop-generating transformation prevents a static count. +static std::optional<unsigned> getCanonicalLoopNestDepth(Stmt *AStmt) { + unsigned Depth = 0; + Stmt *CurStmt = AStmt ? AStmt->IgnoreContainers() : nullptr; + while (CurStmt) { + if (isa<OMPLoopTransformationDirective>(CurStmt)) + return std::nullopt; + if (auto *CanonLoop = dyn_cast<OMPCanonicalLoop>(CurStmt)) + CurStmt = CanonLoop->getLoopStmt(); + Stmt *Body = nullptr; + if (auto *For = dyn_cast<ForStmt>(CurStmt)) + Body = For->getBody(); + else if (auto *RangeFor = dyn_cast<CXXForRangeStmt>(CurStmt)) + Body = RangeFor->getBody(); + else + break; + ++Depth; + CurStmt = Body ? Body->IgnoreContainers() : nullptr; + } + return Depth; +} + +StmtResult +SemaOpenMP::ActOnOpenMPFlattenDirective(ArrayRef<OMPClause *> Clauses, + Stmt *AStmt, SourceLocation StartLoc, + SourceLocation EndLoc) { + ASTContext &Context = getASTContext(); + DeclContext *CurContext = SemaRef.CurContext; + Scope *CurScope = SemaRef.getCurScope(); + + // Empty statement should only be possible if there already was an error. + if (!AStmt) + return StmtError(); + + // flatten without 'depth' clause combines two loops; 'depth(k)' selects k. + unsigned NumLoops = 2; + bool DepthIsValueDependent = false; + const auto *DepthClause = + OMPExecutableDirective::getSingleClause<OMPDepthClause>(Clauses); + if (DepthClause) { + Expr *DepthExpr = DepthClause->getDepth(); + if (DepthExpr && DepthExpr->isValueDependent()) { + DepthIsValueDependent = true; + } else if (DepthExpr) { + Expr::EvalResult EvalResult; + if (DepthExpr->EvaluateAsInt(EvalResult, Context)) + NumLoops = EvalResult.Val.getInt().getZExtValue(); + } + } + + // Report "expected k for loops, but found only n" when depth exceeds the + // perfect nest (same form as 'collapse'); skip if depth or nest is unknown. + if (DepthClause && !DepthIsValueDependent) { + if (std::optional<unsigned> NestDepth = getCanonicalLoopNestDepth(AStmt); + NestDepth && NumLoops > *NestDepth) { + Diag(AStmt->getBeginLoc(), diag::err_omp_not_for) + << /*expected N for loops form=*/1 + << getOpenMPDirectiveName(OMPD_flatten) << NumLoops + << (*NestDepth > 0) << *NestDepth; + return StmtError(); + } + } + + // Defer when 'depth' is value-dependent (concrete k unknown until + // instantiation). + if (DepthIsValueDependent) + return OMPFlattenDirective::Create(Context, StartLoc, EndLoc, Clauses, + NumLoops, AStmt, nullptr, nullptr); + + // Verify and diagnose loop nest. + SmallVector<OMPLoopBasedDirective::HelperExprs, 4> LoopHelpers(NumLoops); + Stmt *Body = nullptr; + SmallVector<SmallVector<Stmt *>, 4> OriginalInits; + if (!checkTransformableLoopNest(OMPD_flatten, AStmt, NumLoops, LoopHelpers, + Body, OriginalInits)) + return StmtError(); + + // Delay flattening to when template is completely instantiated. + if (CurContext->isDependentContext()) + return OMPFlattenDirective::Create(Context, StartLoc, EndLoc, Clauses, + NumLoops, AStmt, nullptr, nullptr); + + assert(LoopHelpers.size() == NumLoops && + "Expecting loop iteration space dimensionality to match number of " + "affected loops"); + assert(OriginalInits.size() == NumLoops && + "Expecting loop iteration space dimensionality to match number of " + "affected loops"); + + // Find the affected loops. + SmallVector<Stmt *> LoopStmts(NumLoops, nullptr); + collectLoopStmts(AStmt, LoopStmts); + + // Collect pre-init statements in outer-to-inner order. + SmallVector<Stmt *> PreInits; + for (auto I : llvm::seq<int>(NumLoops)) { + OMPLoopBasedDirective::HelperExprs &LoopHelper = LoopHelpers[I]; + assert(LoopHelper.Counters.size() == 1 && + "Single-dimensional loop iteration space expected"); + addLoopPreInits(Context, LoopHelper, LoopStmts[I], OriginalInits[I], + PreInits); + } + + CaptureVars CopyTransformer(SemaRef); + auto MakeNumIterations = [&CopyTransformer, + &LoopHelpers](unsigned I) -> Expr * { + return AssertSuccess( + CopyTransformer.TransformExpr(LoopHelpers[I].NumIterations)); + }; + + OMPLoopBasedDirective::HelperExprs &OutermostHelper = LoopHelpers[0]; + auto *OutermostCntVar = cast<DeclRefExpr>(OutermostHelper.Counters.front()); + SourceLocation OrigVarLoc = OutermostCntVar->getExprLoc(); + SourceLocation OrigVarLocBegin = OutermostCntVar->getBeginLoc(); + SourceLocation OrigVarLocEnd = OutermostCntVar->getEndLoc(); + SourceLocation CondLoc = OutermostHelper.Cond->getExprLoc(); + + // Product of trip counts; mirror 'collapse' IV-width selection to avoid + // overflow when several counts are multiplied. + auto BuildTripCount = [&](unsigned Bits) -> ExprResult { + ExprResult Product; + for (unsigned I = 0; I < NumLoops; ++I) { + ExprResult N = widenIterationCount(Bits, MakeNumIterations(I), SemaRef); + if (!N.isUsable()) + return ExprError(); + if (I == 0) + Product = N; + else + Product = SemaRef.BuildBinOp(CurScope, CondLoc, BO_Mul, Product.get(), + N.get()); + if (!Product.isUsable()) + return ExprError(); + } + return Product; + }; + + bool AllCountsLessThan32Bits = true; + for (unsigned I = 0; I < NumLoops; ++I) + AllCountsLessThan32Bits &= + Context.getTypeSize(LoopHelpers[I].NumIterations->getType()) < 32; + + ExprResult TripCount32 = BuildTripCount(/*Bits=*/32); + ExprResult TripCount64 = BuildTripCount(/*Bits=*/64); + if (!TripCount32.isUsable() || !TripCount64.isUsable()) + return StmtError(); + + ExprResult TripCount = TripCount64; + if (Context.getTypeSize(TripCount32.get()->getType()) == 32 && + (AllCountsLessThan32Bits || NumLoops == 1 || + fitsInto( + /*Bits=*/32, + TripCount32.get()->getType()->hasSignedIntegerRepresentation(), + TripCount64.get(), SemaRef))) + TripCount = TripCount32; + + QualType IVTy = TripCount.get()->getType(); + uint64_t IVWidth = Context.getTypeSize(IVTy); + + auto MakeNumIterationsInIVTy = [&](unsigned I) -> Expr * { + return AssertSuccess(SemaRef.PerformImplicitConversion( + MakeNumIterations(I), IVTy, AssignmentAction::Converting, + /*AllowExplicit=*/true)); + }; + + // \code{.cpp} + // for (auto .flatten.iv = 0; .flatten.iv < n0 * n1 * ...; ++.flatten.iv) { + // .flatten.iv.0 = .flatten.iv / (n1 * ...); + // i0 = ...; // Updates[0] + // .flatten.iv.1 = (.flatten.iv / ...) % n1; + // i1 = ...; // Updates[1] + // ... + // body(i0, i1, ...); + // } + // \endcode + SmallString<64> FlattenedIVName(".flatten.iv"); + VarDecl *FlattenedIVDecl = buildVarDecl(SemaRef, {}, IVTy, FlattenedIVName, + nullptr, OutermostCntVar); + auto MakeFlattenedRef = [&SemaRef = this->SemaRef, FlattenedIVDecl, IVTy, + OrigVarLoc]() { + return buildDeclRefExpr(SemaRef, FlattenedIVDecl, IVTy, OrigVarLoc); + }; + + // For init-statement: + // \code{.cpp} + // auto .flatten.iv = 0; + // \endcode + auto *Zero = IntegerLiteral::Create(Context, llvm::APInt::getZero(IVWidth), + IVTy, OrigVarLoc); + SemaRef.AddInitializerToDecl(FlattenedIVDecl, Zero, /*DirectInit=*/false); + StmtResult Init = new (Context) + DeclStmt(DeclGroupRef(FlattenedIVDecl), OrigVarLocBegin, OrigVarLocEnd); + if (!Init.isUsable()) + return StmtError(); + + // For cond-expression: + // \code{.cpp} + // .flatten.iv < n0 * n1 * ... * n(k-1) + // \endcode + ExprResult Cond = SemaRef.BuildBinOp(CurScope, CondLoc, BO_LT, + MakeFlattenedRef(), TripCount.get()); + if (!Cond.isUsable()) + return StmtError(); + + // For incr-statement: + // \code{.cpp} + // ++.flatten.iv + // \endcode + ExprResult Incr = + SemaRef.BuildUnaryOp(CurScope, OutermostHelper.Inc->getExprLoc(), + UO_PreInc, MakeFlattenedRef()); + if (!Incr.isUsable()) + return StmtError(); + + // Recover each logical iteration counter via mixed-radix div/mod; reuse the + // iteration variables from checkOpenMPLoop so Updates compute user counters. + SmallVector<Stmt *, 8> BodyStmts; + for (unsigned I = 0; I < NumLoops; ++I) { + OMPLoopBasedDirective::HelperExprs &LoopHelper = LoopHelpers[I]; + auto *IVRef = cast<DeclRefExpr>(LoopHelper.IterationVarRef); + auto *IVDecl = cast<VarDecl>(IVRef->getDecl()); + std::string IVName = (".flatten.iv." + llvm::Twine(I)).str(); + IVDecl->setDeclName(&SemaRef.PP.getIdentifierTable().get(IVName)); + + ExprResult Value = MakeFlattenedRef(); + if (I + 1 < NumLoops) { + ExprResult Divisor = MakeNumIterationsInIVTy(I + 1); + for (unsigned J = I + 2; J < NumLoops; ++J) { + Divisor = SemaRef.BuildBinOp(CurScope, OrigVarLoc, BO_Mul, + Divisor.get(), MakeNumIterationsInIVTy(J)); + if (!Divisor.isUsable()) + return StmtError(); + } + Value = SemaRef.BuildBinOp(CurScope, OrigVarLoc, BO_Div, Value.get(), + Divisor.get()); + if (!Value.isUsable()) + return StmtError(); + } + if (I > 0) { + Value = SemaRef.BuildBinOp(CurScope, OrigVarLoc, BO_Rem, Value.get(), + MakeNumIterationsInIVTy(I)); + if (!Value.isUsable()) + return StmtError(); + } + + SemaRef.AddInitializerToDecl(IVDecl, Value.get(), /*DirectInit=*/false); + StmtResult IVStmt = new (Context) + DeclStmt(DeclGroupRef(IVDecl), OrigVarLocBegin, OrigVarLocEnd); + if (!IVStmt.isUsable()) + return StmtError(); + + BodyStmts.push_back(IVStmt.get()); + llvm::append_range(BodyStmts, LoopHelper.Updates); + if (auto *CXXFor = dyn_cast<CXXForRangeStmt>(LoopStmts[I])) + BodyStmts.push_back(CXXFor->getLoopVarStmt()); + } + BodyStmts.push_back(Body); + auto *FlattenedBody = + CompoundStmt::Create(Context, BodyStmts, FPOptionsOverride(), + Body->getBeginLoc(), Body->getEndLoc()); + + auto *FlattenedFor = new (Context) ForStmt( + Context, Init.get(), Cond.get(), nullptr, Incr.get(), FlattenedBody, + OutermostHelper.Init->getBeginLoc(), OutermostHelper.Init->getBeginLoc(), + OutermostHelper.Inc->getEndLoc()); + + return OMPFlattenDirective::Create(Context, StartLoc, EndLoc, Clauses, + NumLoops, AStmt, FlattenedFor, + buildPreInits(Context, PreInits)); +} + StmtResult SemaOpenMP::ActOnOpenMPFuseDirective(ArrayRef<OMPClause *> Clauses, Stmt *AStmt, SourceLocation StartLoc, @@ -16920,6 +17196,9 @@ OMPClause *SemaOpenMP::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, case OMPC_partial: Res = ActOnOpenMPPartialClause(Expr, StartLoc, LParenLoc, EndLoc); break; + case OMPC_depth: + Res = ActOnOpenMPDepthClause(Expr, StartLoc, LParenLoc, EndLoc); + break; case OMPC_message: Res = ActOnOpenMPMessageClause(Expr, StartLoc, LParenLoc, EndLoc); break; @@ -18241,6 +18520,22 @@ OMPClause *SemaOpenMP::ActOnOpenMPPartialClause(Expr *FactorExpr, FactorExpr); } +OMPClause *SemaOpenMP::ActOnOpenMPDepthClause(Expr *DepthExpr, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc) { + // The depth-expr must be a positive integer constant expression and + // not greater than the number of loops in the associated loop nest. + ExprResult DepthResult = VerifyPositiveIntegerConstantInClause( + DepthExpr, OMPC_depth, /*StrictlyPositive=*/true); + if (DepthResult.isInvalid()) + return nullptr; + DepthExpr = DepthResult.get(); + + return OMPDepthClause::Create(getASTContext(), StartLoc, LParenLoc, EndLoc, + DepthExpr); +} + OMPClause *SemaOpenMP::ActOnOpenMPLoopRangeClause( Expr *First, Expr *Count, SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation FirstLoc, SourceLocation CountLoc, SourceLocation EndLoc) { >From 54be7347979638725cd4b6c421d11ec1cd618c43 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Mon, 27 Jul 2026 07:03:06 -0400 Subject: [PATCH 11/12] add libclang traversal support for flatten directive --- clang/tools/libclang/CIndex.cpp | 7 +++++++ clang/tools/libclang/CXCursor.cpp | 3 +++ 2 files changed, 10 insertions(+) diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index f8ab63e3e0dae..9eb0ec8c21158 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -2160,6 +2160,7 @@ class EnqueueVisitor : public ConstStmtVisitor<EnqueueVisitor, void>, void VisitOMPUnrollDirective(const OMPUnrollDirective *D); void VisitOMPReverseDirective(const OMPReverseDirective *D); void VisitOMPInterchangeDirective(const OMPInterchangeDirective *D); + void VisitOMPFlattenDirective(const OMPFlattenDirective *D); void VisitOMPCanonicalLoopSequenceTransformationDirective( const OMPCanonicalLoopSequenceTransformationDirective *D); void VisitOMPFuseDirective(const OMPFuseDirective *D); @@ -3358,6 +3359,10 @@ void EnqueueVisitor::VisitOMPInterchangeDirective( VisitOMPCanonicalLoopNestTransformationDirective(D); } +void EnqueueVisitor::VisitOMPFlattenDirective(const OMPFlattenDirective *D) { + VisitOMPCanonicalLoopNestTransformationDirective(D); +} + void EnqueueVisitor::VisitOMPCanonicalLoopSequenceTransformationDirective( const OMPCanonicalLoopSequenceTransformationDirective *D) { VisitOMPExecutableDirective(D); @@ -6329,6 +6334,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("OMPReverseDirective"); case CXCursor_OMPInterchangeDirective: return cxstring::createRef("OMPInterchangeDirective"); + case CXCursor_OMPFlattenDirective: + return cxstring::createRef("OMPFlattenDirective"); case CXCursor_OMPFuseDirective: return cxstring::createRef("OMPFuseDirective"); case CXCursor_OMPSplitDirective: diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index b2feed578fc0f..97bbfd36558a9 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -703,6 +703,9 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::OMPInterchangeDirectiveClass: K = CXCursor_OMPInterchangeDirective; break; + case Stmt::OMPFlattenDirectiveClass: + K = CXCursor_OMPFlattenDirective; + break; case Stmt::OMPFuseDirectiveClass: K = CXCursor_OMPFuseDirective; break; >From cdedb1045baf373937b0da858624a6ace14a3d21 Mon Sep 17 00:00:00 2001 From: amtiwari <[email protected]> Date: Mon, 27 Jul 2026 07:06:36 -0400 Subject: [PATCH 12/12] bindings --- clang/bindings/python/clang/cindex.py | 3 +++ clang/include/clang-c/Index.h | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 24b737139dba8..ccad140e8c861 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -1456,6 +1456,9 @@ def is_unexposed(self): # OpenMP split directive. OMP_SPLIT_DIRECTIVE = 312 + # OpenMP flatten directive. + OMP_FLATTEN_DIRECTIVE = 313 + # OpenACC Compute Construct. OPEN_ACC_COMPUTE_DIRECTIVE = 320 diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 8427236e0b444..f561fb5724897 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -2170,6 +2170,10 @@ enum CXCursorKind { */ CXCursor_OMPSplitDirective = 312, + /** OpenMP flatten directive. + */ + CXCursor_OMPFlattenDirective = 313, + /** OpenACC Compute Construct. */ CXCursor_OpenACCComputeConstruct = 320, _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
