[clang] [llvm] [Clang][OpenMP] Add `#pragma omp flatten` loop directive + `depth` clause (PR #206977)
Amit Tiwari via cfe-commits
cfe-commits at lists.llvm.org
Sat Jul 25 00:01:43 PDT 2026
https://github.com/loopacino updated https://github.com/llvm/llvm-project/pull/206977
>From 0fb5bd42f1e127faea963c359b1e3c69cb969c60 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Wed, 1 Jul 2026 09:06:58 -0400
Subject: [PATCH 01/10] 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 <amtiwari at amd.com>
Date: Wed, 8 Jul 2026 16:02:27 -0400
Subject: [PATCH 02/10] 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 <amtiwari at amd.com>
Date: Wed, 8 Jul 2026 16:04:44 -0400
Subject: [PATCH 03/10] 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 <amtiwari at amd.com>
Date: Wed, 8 Jul 2026 16:08:12 -0400
Subject: [PATCH 04/10] 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 <amtiwari at amd.com>
Date: Wed, 8 Jul 2026 16:09:03 -0400
Subject: [PATCH 05/10] 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 <amtiwari at amd.com>
Date: Wed, 8 Jul 2026 16:17:29 -0400
Subject: [PATCH 06/10] 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 <amtiwari at amd.com>
Date: Wed, 8 Jul 2026 16:21:25 -0400
Subject: [PATCH 07/10] 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 <amtiwari at amd.com>
Date: Wed, 8 Jul 2026 16:24:42 -0400
Subject: [PATCH 08/10] 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 <amtiwari at amd.com>
Date: Wed, 8 Jul 2026 16:26:31 -0400
Subject: [PATCH 09/10] 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 <amtiwari at amd.com>
Date: Fri, 10 Jul 2026 06:54:16 -0400
Subject: [PATCH 10/10] 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) {
More information about the cfe-commits
mailing list