[clang] [llvm] [clang][OpenMP] Prototype #1 of directive splitting (PR #108855)

Krzysztof Parzyszek via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 16 09:49:34 PDT 2024


https://github.com/kparzysz created https://github.com/llvm/llvm-project/pull/108855

My dev branch with everything squashed together, and debug code removed.

The large number of changed files primarily comes from introducing new AST nodes. This can easily be extracted into its own commit.

This isn't 100% finished, but it should be enough to illustrate the changes needed to implement the feature.

; (ignore 28a51f719a7b23d6c9e063e36662d403359fe678)

>From dbe0575e0a60591c7e6ede0e39a9c4c8c28a255f Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Fri, 30 Aug 2024 13:57:13 -0500
Subject: [PATCH] [clang][OpenMP] Prototype #1 of directive splitting

My dev branch with everything squashed together, and debug code
removed.

The large number of changed files primarily comes from introducing
new AST nodes. This can easily be extracted into its own commit.

This isn't 100% finished, but it should be enough to illustrate
the changes needed to implement the feature.

; (ignore 28a51f719a7b23d6c9e063e36662d403359fe678)
---
 clang/bindings/python/clang/cindex.py         |    6 +
 clang/include/clang-c/Index.h                 |    8 +
 clang/include/clang/AST/OpenMPClause.h        |    9 +-
 clang/include/clang/AST/RecursiveASTVisitor.h |    6 +
 clang/include/clang/AST/Stmt.h                |   10 +-
 clang/include/clang/AST/StmtOpenMP.h          |  202 ++
 clang/include/clang/AST/TextNodeDumper.h      |    2 +
 clang/include/clang/Basic/OpenMPKinds.h       |   18 +-
 clang/include/clang/Basic/StmtNodes.td        |    2 +
 clang/include/clang/Sema/SemaOpenMP.h         |   59 +-
 .../include/clang/Serialization/ASTBitCodes.h |    2 +
 clang/lib/AST/Stmt.cpp                        |   15 +-
 clang/lib/AST/StmtOpenMP.cpp                  |   93 +
 clang/lib/AST/StmtPrinter.cpp                 |   35 +
 clang/lib/AST/StmtProfile.cpp                 |   10 +
 clang/lib/AST/TextNodeDumper.cpp              |   14 +
 clang/lib/Basic/OpenMPKinds.cpp               |   70 +-
 clang/lib/CodeGen/CGStmt.cpp                  |    5 +
 clang/lib/CodeGen/CGStmtOpenMP.cpp            |    6 +-
 clang/lib/Parse/ParseOpenMP.cpp               |   45 +-
 clang/lib/Sema/CMakeLists.txt                 |    1 +
 clang/lib/Sema/SemaExceptionSpec.cpp          |    2 +
 clang/lib/Sema/SemaExpr.cpp                   |   19 +-
 clang/lib/Sema/SemaOpenMP.cpp                 | 2901 +++++++++++------
 clang/lib/Sema/SemaOpenMPExt.cpp              | 1547 +++++++++
 clang/lib/Sema/SemaOpenMPExt.h                |  428 +++
 clang/lib/Sema/TreeTransform.h                |  130 +-
 clang/lib/Serialization/ASTReaderStmt.cpp     |   77 +-
 clang/lib/Serialization/ASTWriterStmt.cpp     |   57 +-
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |    2 +
 .../OpenMP/default_firstprivate_ast_print.cpp |    2 +-
 .../test/OpenMP/default_private_ast_print.cpp |    2 +-
 clang/test/OpenMP/generic_loop_ast_print.cpp  |    2 +-
 clang/test/OpenMP/interop_ast_print.cpp       |    8 +-
 clang/test/OpenMP/scope_ast_print.cpp         |    2 +-
 clang/tools/libclang/CIndex.cpp               |   14 +
 clang/tools/libclang/CXCursor.cpp             |    6 +
 .../Frontend/OpenMP/ConstructDecompositionT.h |  106 +-
 llvm/include/llvm/Frontend/OpenMP/OMP.h       |    2 +
 llvm/include/llvm/Frontend/OpenMP/OMP.td      |    1 +
 llvm/lib/Frontend/OpenMP/OMP.cpp              |   54 +
 41 files changed, 4809 insertions(+), 1171 deletions(-)
 create mode 100644 clang/lib/Sema/SemaOpenMPExt.cpp
 create mode 100644 clang/lib/Sema/SemaOpenMPExt.h

diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py
index 4da99e899e7f7c..057e468c71b630 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -1408,6 +1408,12 @@ def is_unexposed(self):
     # OpenMP scope directive.
     OMP_SCOPE_DIRECTIVE = 306
 
+    # OpenMP compound block-associated directive.
+    OMP_COMPOUND_BLOCK_DIRECTIVE = 310
+
+    # OpenMP compound loop-associated directive.
+    OMP_COMPOUND_LOOP_DIRECTIVE = 311
+
     # 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 4f99bf4ebe309b..ba4c14872de232 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -2158,6 +2158,14 @@ enum CXCursorKind {
    */
   CXCursor_OMPAssumeDirective = 309,
 
+  /** OpenMP compound block-associated directive.
+  */
+  CXCursor_OMPCompoundBlockDirective = 310,
+
+  /** OpenMP compound loop-associated directive.
+  */
+  CXCursor_OMPCompoundLoopDirective = 311,
+
   /** OpenACC Compute Construct.
    */
   CXCursor_OpenACCComputeConstruct = 320,
diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h
index 3a1d6852d2a708..ddfb86d1d8d943 100644
--- a/clang/include/clang/AST/OpenMPClause.h
+++ b/clang/include/clang/AST/OpenMPClause.h
@@ -388,6 +388,13 @@ template <class T> class OMPDirectiveListClause : public OMPClause {
         NumKinds);
   }
 
+  ArrayRef<OpenMPDirectiveKind> getDirectiveKinds() const {
+    return ArrayRef<OpenMPDirectiveKind>(
+        static_cast<const T *>(this)
+            ->template getTrailingObjects<OpenMPDirectiveKind>(),
+        NumKinds);
+  }
+
   void setDirectiveKinds(ArrayRef<OpenMPDirectiveKind> DK) {
     assert(
         DK.size() == NumKinds &&
@@ -6284,7 +6291,7 @@ class OMPMapClause final : public OMPMappableExprListClause<OMPMapClause>,
                                    const OMPMappableExprListSizeTy &Sizes);
 
   /// Fetches Expr * of iterator modifier.
-  Expr *getIteratorModifier() {
+  Expr *getIteratorModifier() const {
     return getTrailingObjects<Expr *>()[2 * varlist_size()];
   }
 
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index cd9947f7ab9805..263460a4c4dd77 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -3025,6 +3025,12 @@ RecursiveASTVisitor<Derived>::TraverseOMPLoopDirective(OMPLoopDirective *S) {
   return TraverseOMPExecutableDirective(S);
 }
 
+DEF_TRAVERSE_STMT(OMPCompoundBlockDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
+DEF_TRAVERSE_STMT(OMPCompoundLoopDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
 DEF_TRAVERSE_STMT(OMPMetaDirective,
                   { TRY_TO(TraverseOMPExecutableDirective(S)); })
 
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 7aed83e9c68bb7..61e94c3e0a55ba 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -1435,9 +1435,15 @@ class alignas(void *) Stmt {
 
   /// Skip no-op (attributed, compound) container stmts and skip captured
   /// stmt at the top, if \a IgnoreCaptured is true.
-  Stmt *IgnoreContainers(bool IgnoreCaptured = false);
+  Stmt *IgnoreContainers(bool IgnoreCaptured = false) {
+    return stripContainers(static_cast<int>(IgnoreCaptured));
+  }
   const Stmt *IgnoreContainers(bool IgnoreCaptured = false) const {
-    return const_cast<Stmt *>(this)->IgnoreContainers(IgnoreCaptured);
+    return stripContainers(static_cast<int>(IgnoreCaptured));
+  }
+  Stmt *stripContainers(int NumCaptured = 0);
+  const Stmt *stripContainers(int NumCaptured = 0) const {
+    return const_cast<Stmt *>(this)->stripContainers(NumCaptured);
   }
 
   const Stmt *stripLabelLikeStatements() const;
diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h
index 930670d17f2d1c..919ebfe217cc00 100644
--- a/clang/include/clang/AST/StmtOpenMP.h
+++ b/clang/include/clang/AST/StmtOpenMP.h
@@ -1520,6 +1520,7 @@ class OMPLoopDirective : public OMPLoopBasedDirective {
   static bool classof(const Stmt *T) {
     return T->getStmtClass() == OMPSimdDirectiveClass ||
            T->getStmtClass() == OMPForDirectiveClass ||
+           T->getStmtClass() == OMPCompoundLoopDirectiveClass ||
            T->getStmtClass() == OMPForSimdDirectiveClass ||
            T->getStmtClass() == OMPParallelForDirectiveClass ||
            T->getStmtClass() == OMPParallelForSimdDirectiveClass ||
@@ -1559,6 +1560,207 @@ class OMPLoopDirective : public OMPLoopBasedDirective {
   }
 };
 
+/// This represents a block-associated compound OpenMP directive.
+/// This is a directive which is a combination of two or more leaf
+/// directives, where none of the constituent leaf directives are
+/// loop-associated.
+///
+/// \code
+/// #pragma omp parallel sections private(a,b) reduction(+:c,d)
+/// \endcode
+/// In this example the directive is '#pragma omp parallel sections', and
+/// it has clauses 'private' with the variables 'a' and 'b', and 'reduction'
+/// with operator '+' and variables 'c' and 'd'.
+class OMPCompoundBlockDirective final : public OMPExecutableDirective {
+  friend class ASTStmtReader;
+  friend class OMPExecutableDirective;
+
+  /// true if current directive has inner cancel directive.
+  bool HasCancel = false;
+
+  /// Region for cancel/cancellation-point directives.
+  OpenMPDirectiveKind CancelRegion = llvm::omp::OMPD_unknown;
+
+  /// Name of the directive for the "critical" directive.
+  DeclarationNameInfo DirName;
+
+  /// Build directive with the given start and end location.
+  ///
+  /// \param Kind The OpenMP directive kind.
+  /// \param StartLoc Starting location of the directive kind.
+  /// \param EndLoc Ending location of the directive.
+  ///
+  OMPCompoundBlockDirective(OpenMPDirectiveKind Kind, SourceLocation StartLoc,
+                            SourceLocation EndLoc)
+      : OMPExecutableDirective(OMPCompoundBlockDirectiveClass, Kind, StartLoc,
+                               EndLoc) {}
+
+  /// Build an empty directive.
+  ///
+  /// \param Kind The OpenMP directive kind.
+  ///
+  explicit OMPCompoundBlockDirective(OpenMPDirectiveKind Kind)
+      : OMPExecutableDirective(OMPCompoundBlockDirectiveClass, Kind,
+                               SourceLocation(), SourceLocation()) {}
+
+  /// Sets special task reduction descriptor.
+  void setTaskReductionRefExpr(Expr *E) { Data->getChildren()[0] = E; }
+
+  /// Set cancel state.
+  void setHasCancel(bool Has) { HasCancel = Has; }
+
+  /// Set cancellation region.
+  void setCancelRegion(OpenMPDirectiveKind CR) { CancelRegion = CR; }
+
+  /// Set directive name.
+  void setDirectiveName(const DeclarationNameInfo &Name) { DirName = Name; }
+
+public:
+  /// Creates directive with a list of \a Clauses.
+  ///
+  /// \param C AST context.
+  /// \param StartLoc Starting location of the directive kind.
+  /// \param EndLoc Ending Location of the directive.
+  /// \param Kind The OpenMP directive kind.
+  /// \param Clauses List of clauses.
+  /// \param AssociatedStmt Statement, associated with the directive.
+  /// \param TaskRedRef Task reduction special reference expression to handle
+  /// taskgroup descriptor.
+  /// \param HasCancel true if current directive has inner cancel directive.
+  /// \param CancelRegion Cancellation region, applicable for cancel or
+  /// cancellation-point directives.
+  /// \param DirName Name of the directive (used if DKind is OMPD_critical).
+  static OMPCompoundBlockDirective *
+  Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+         OpenMPDirectiveKind DKind, ArrayRef<OMPClause *> Clauses,
+         Stmt *AssociatedStmt, Expr *TaskRedRef, bool HasCancel,
+         OpenMPDirectiveKind CancelRegion, const DeclarationNameInfo &DirName);
+
+  /// Creates an empty directive with the place for \a NumClauses
+  /// clauses.
+  ///
+  /// \param C AST context.
+  /// \param Kind The OpenMP directive kind.
+  /// \param NumClauses Number of clauses.
+  ///
+  static OMPCompoundBlockDirective *CreateEmpty(const ASTContext &C,
+                                                OpenMPDirectiveKind Kind,
+                                                unsigned NumClauses,
+                                                bool HasAssociatedStmt,
+                                                EmptyShell);
+
+  /// Returns special task reduction reference expression.
+  Expr *getTaskReductionRefExpr() const {
+    return cast_or_null<Expr>(Data->getChildren()[0]);
+  }
+
+  /// Return true if current directive has inner cancel directive.
+  bool hasCancel() const { return HasCancel; }
+
+  /// Return cancellation region.
+  OpenMPDirectiveKind getCancelRegion() const { return CancelRegion; }
+
+  /// Return the directive name (valid for "critical" only).
+  DeclarationNameInfo getDirectiveName() const { return DirName; }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == OMPCompoundBlockDirectiveClass;
+  }
+};
+
+/// This represents a loop-associated compound OpenMP directive.
+/// This is a directive which is a combination of two or more leaf
+/// directives, where at least one of the constituent leaf directives
+/// is loop-associated.
+///
+/// \code
+/// #pragma omp distribute parallel for private(a,b)
+/// \endcode
+/// In this example directive '#pragma omp distribute parallel for' has clause
+/// 'private' with the variables 'a' and 'b'
+///
+class OMPCompoundLoopDirective final : public OMPLoopDirective {
+  friend class ASTStmtReader;
+  friend class OMPExecutableDirective;
+
+  /// true if current directive has inner cancel directive.
+  bool HasCancel = false;
+
+  /// Build directive with the given start and end location.
+  ///
+  /// \param Kind The OpenMP directive kind.
+  /// \param StartLoc Starting location of the directive kind.
+  /// \param EndLoc Ending location of the directive.
+  /// \param CollapsedNum Number of collapsed nested loops.
+  ///
+  OMPCompoundLoopDirective(OpenMPDirectiveKind Kind, SourceLocation StartLoc,
+                           SourceLocation EndLoc, unsigned CollapsedNum)
+      : OMPLoopDirective(OMPCompoundLoopDirectiveClass, Kind, StartLoc, EndLoc,
+                         CollapsedNum) {}
+
+  /// Build an empty directive.
+  ///
+  /// \param Kind The OpenMP directive kind.
+  /// \param CollapsedNum Number of collapsed nested loops.
+  ///
+  explicit OMPCompoundLoopDirective(OpenMPDirectiveKind Kind,
+                                    unsigned CollapsedNum)
+      : OMPLoopDirective(OMPCompoundLoopDirectiveClass, Kind, SourceLocation(),
+                         SourceLocation(), CollapsedNum) {}
+
+  /// Sets special task reduction descriptor.
+  void setTaskReductionRefExpr(Expr *E) {
+    size_t Index = numLoopChildren(getLoopsNumber(), getDirectiveKind());
+    Data->getChildren()[Index] = E;
+  }
+
+  /// Set cancel state.
+  void setHasCancel(bool Has) { HasCancel = Has; }
+
+public:
+  /// Creates directive with a list of \a Clauses.
+  ///
+  /// \param C AST context.
+  /// \param StartLoc Starting location of the directive kind.
+  /// \param EndLoc Ending Location of the directive.
+  /// \param Kind The OpenMP directive kind.
+  /// \param CollapsedNum Number of collapsed loops.
+  /// \param Clauses List of clauses.
+  /// \param AssociatedStmt Statement, associated with the directive.
+  /// \param Exprs Helper expressions for CodeGen.
+  ///
+  static OMPCompoundLoopDirective *
+  Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+         OpenMPDirectiveKind Kind, unsigned CollapsedNum,
+         ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt,
+         const HelperExprs &Exprs, Expr *TaskRedRef, bool HasCancel);
+
+  /// Creates an empty directive with the place
+  /// for \a NumClauses clauses.
+  ///
+  /// \param C AST context.
+  /// \param Kind The OpenMP directive kind.
+  /// \param CollapsedNum Number of collapsed nested loops.
+  /// \param NumClauses Number of clauses.
+  ///
+  static OMPCompoundLoopDirective *
+  CreateEmpty(const ASTContext &C, OpenMPDirectiveKind Kind,
+              unsigned NumClauses, unsigned CollapsedNum, EmptyShell);
+
+  /// Returns special task reduction reference expression.
+  Expr *getTaskReductionRefExpr() const {
+    size_t Index = numLoopChildren(getLoopsNumber(), getDirectiveKind());
+    return cast_or_null<Expr>(Data->getChildren()[Index]);
+  }
+
+  /// Return true if current directive has inner cancel directive.
+  bool hasCancel() const { return HasCancel; }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == OMPCompoundLoopDirectiveClass;
+  }
+};
+
 /// This represents '#pragma omp simd' directive.
 ///
 /// \code
diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 57100e7ede171c..2b7608089f92de 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -366,6 +366,8 @@ class TextNodeDumper
   void VisitPragmaCommentDecl(const PragmaCommentDecl *D);
   void VisitPragmaDetectMismatchDecl(const PragmaDetectMismatchDecl *D);
   void VisitOMPExecutableDirective(const OMPExecutableDirective *D);
+  void VisitOMPCompoundBlockDirective(const OMPCompoundBlockDirective *D);
+  void VisitOMPCompoundLoopDirective(const OMPCompoundLoopDirective *D);
   void VisitOMPDeclareReductionDecl(const OMPDeclareReductionDecl *D);
   void VisitOMPRequiresDecl(const OMPRequiresDecl *D);
   void VisitOMPCapturedExprDecl(const OMPCapturedExprDecl *D);
diff --git a/clang/include/clang/Basic/OpenMPKinds.h b/clang/include/clang/Basic/OpenMPKinds.h
index 1acdafa8572211..c1188a745d089c 100644
--- a/clang/include/clang/Basic/OpenMPKinds.h
+++ b/clang/include/clang/Basic/OpenMPKinds.h
@@ -385,9 +385,25 @@ bool isOpenMPInformationalDirective(OpenMPDirectiveKind DKind);
 
 /// Checks if the specified directive can capture variables.
 /// \param DKind Specified directive.
+/// \param OrderedIsStandalone Assume that the "ordered" directive is
+/// standalone (i.e. without a statement). The default association of
+/// "ordered" is "None", which corresponds to the default value of the
+/// parameter.
 /// \return true - if the above condition is met for this directive
 /// otherwise - false.
-bool isOpenMPCapturingDirective(OpenMPDirectiveKind DKind);
+bool isOpenMPCapturingDirective(OpenMPDirectiveKind DKind,
+                                bool OrderedIsStandalone = true);
+
+/// Checks if the specified directive has an associated statement.
+/// \param DKind Specified directive.
+/// \param OrderedIsStandalone Assume that the "ordered" directive is
+/// standalone (i.e. without a statement). The default association of
+/// "ordered" is "None", which corresponds to the default value of the
+/// parameter.
+/// \return true - if the above condition is met for this directive
+/// otherwise - false.
+bool isOpenMPDirectiveWithStatement(OpenMPDirectiveKind DKind,
+                                    bool OrderedIsStandalone = true);
 }
 
 template <>
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index 30f2c8f1dbfde8..a4183e6504513a 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -225,6 +225,8 @@ def OMPExecutableDirective : StmtNode<Stmt, 1>;
 def OMPMetaDirective : StmtNode<OMPExecutableDirective>;
 def OMPLoopBasedDirective : StmtNode<OMPExecutableDirective, 1>;
 def OMPLoopDirective : StmtNode<OMPLoopBasedDirective, 1>;
+def OMPCompoundBlockDirective : StmtNode<OMPExecutableDirective>;
+def OMPCompoundLoopDirective : StmtNode<OMPLoopDirective>;
 def OMPParallelDirective : StmtNode<OMPExecutableDirective>;
 def OMPSimdDirective : StmtNode<OMPLoopDirective>;
 def OMPLoopTransformationDirective : StmtNode<OMPLoopBasedDirective, 1>;
diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h
index 53191e7bb4272b..ded06306de2ad2 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -45,6 +45,8 @@ class DeclGroupRef;
 class ParsedAttr;
 class Scope;
 
+struct ConstructDecomposition;
+
 class SemaOpenMP : public SemaBase {
 public:
   SemaOpenMP(Sema &S);
@@ -149,6 +151,9 @@ class SemaOpenMP : public SemaBase {
   VarDecl *isOpenMPCapturedDecl(ValueDecl *D, bool CheckScopeInfo = false,
                                 unsigned StopAt = 0);
 
+  bool shouldCaptureInRegion(ValueDecl *D, unsigned Level,
+                             unsigned OpenMPCaptureLevel) const;
+
   /// The member expression(this->fd) needs to be rebuilt in the template
   /// instantiation to generate private copy for OpenMP when default
   /// clause is used. The function will return true if default
@@ -378,7 +383,8 @@ class SemaOpenMP : public SemaBase {
   static int getOpenMPCaptureLevels(OpenMPDirectiveKind Kind);
 
   /// Initialization of captured region for OpenMP region.
-  void ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, Scope *CurScope);
+  void ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, Scope *CurScope,
+                              bool HasAssociatedStmt);
 
   /// Called for syntactical loops (ForStmt or CXXForRangeStmt) associated to
   /// an OpenMP loop directive.
@@ -849,6 +855,16 @@ class SemaOpenMP : public SemaBase {
       ArrayRef<OMPInteropInfo> AppendArgs, SourceLocation AdjustArgsLoc,
       SourceLocation AppendArgsLoc, SourceRange SR);
 
+  StmtResult ActOnOpenMPCompoundBlockDirective(
+      OpenMPDirectiveKind Kind, ArrayRef<OMPClause *> Clauses, Stmt *AStmt,
+      OpenMPDirectiveKind CancelRegion, const DeclarationNameInfo &DirName,
+      SourceLocation StartLoc, SourceLocation EndLoc);
+
+  StmtResult ActOnOpenMPCompoundLoopDirective(
+      OpenMPDirectiveKind Kind, ArrayRef<OMPClause *> Clauses, Stmt *AStmt,
+      SourceLocation StartLoc, SourceLocation EndLoc,
+      VarsWithInheritedDSAType &VarsWithImplicitDSA);
+
   OMPClause *ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, Expr *Expr,
                                          SourceLocation StartLoc,
                                          SourceLocation LParenLoc,
@@ -1394,6 +1410,37 @@ class SemaOpenMP : public SemaBase {
 
   void handleOMPAssumeAttr(Decl *D, const ParsedAttr &AL);
 
+  struct VariableImplicitInfo {
+    static const unsigned MapKindNum = OMPC_MAP_unknown;
+    static const unsigned DefaultmapKindNum = OMPC_DEFAULTMAP_unknown + 1;
+
+    llvm::SmallVector<Expr *> Privates;
+    llvm::SmallVector<Expr *> Firstprivates;
+    llvm::SmallVector<Expr *> Mappings[DefaultmapKindNum][MapKindNum];
+    llvm::SmallVector<OpenMPMapModifierKind, NumberOfOMPMapClauseModifiers>
+        MapModifiers[DefaultmapKindNum];
+    llvm::SmallVector<Expr *> ReductionMappings;
+
+    llvm::SmallVector<SourceLocation, NumberOfOMPMapClauseModifiers>
+        ImplicitMapModifiersLoc[DefaultmapKindNum];
+
+    void addPrivate(Expr *E);
+    void addFirstprivate(Expr *E);
+    void addMapping(Expr *E, unsigned DefKind, unsigned MapKind);
+    void addMapModifier(unsigned MapKind, unsigned DefKind);
+    void addReductionMapping(Expr *E);
+
+    VariableImplicitInfo &include(const VariableImplicitInfo &Other);
+
+  #ifndef NDEBUG
+    bool empty() const;
+  #endif
+
+  private:
+    std::pair<Stmt *, Decl *> getDecl(Expr *E);
+    bool isDeclPresent(ArrayRef<Expr *> Range, Expr *E);
+  };
+
 private:
   void *VarDataSharingAttributesStack;
 
@@ -1409,7 +1456,15 @@ class SemaOpenMP : public SemaBase {
 
   /// Adjusts the function scopes index for the target-based regions.
   void adjustOpenMPTargetScopeIndex(unsigned &FunctionScopesIndex,
-                                    unsigned Level) const;
+                                    unsigned Level,
+                                    unsigned CaptureLevel) const;
+
+  /// Adjusts the function scopes index for regions that privatize
+  /// thread-local globals.
+  void adjustOpenMPGlobalScopeIndex(ValueDecl *VD,
+                                    unsigned &FunctionScopesIndex,
+                                    unsigned Level,
+                                    unsigned CaptureLevel) const;
 
   /// Returns the number of scopes associated with the construct on the given
   /// OpenMP level.
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 4410df296d8efc..196adacb205522 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1895,6 +1895,8 @@ enum StmtCode {
   STMT_SEH_TRY,                     // SEHTryStmt
 
   // OpenMP directives
+  STMT_OMP_COMPOUND_BLOCK_DIRECTIVE,
+  STMT_OMP_COMPOUND_LOOP_DIRECTIVE,
   STMT_OMP_META_DIRECTIVE,
   STMT_OMP_CANONICAL_LOOP,
   STMT_OMP_PARALLEL_DIRECTIVE,
diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp
index fe59d6070b3e81..56f8aa3ffda0fb 100644
--- a/clang/lib/AST/Stmt.cpp
+++ b/clang/lib/AST/Stmt.cpp
@@ -194,11 +194,20 @@ Stmt::determineLikelihoodConflict(const Stmt *Then, const Stmt *Else) {
 
 /// Skip no-op (attributed, compound) container stmts and skip captured
 /// stmt at the top, if \a IgnoreCaptured is true.
-Stmt *Stmt::IgnoreContainers(bool IgnoreCaptured) {
+Stmt *Stmt::stripContainers(int NumCaptured) {
   Stmt *S = this;
-  if (IgnoreCaptured)
-    if (auto CapS = dyn_cast_or_null<CapturedStmt>(S))
+  if (NumCaptured >= 0) {
+    // If the number of captured statements to skip is non-negative,
+    // then try to skip that exact number of them.
+    while (NumCaptured--)
+      if (auto CapS = dyn_cast_or_null<CapturedStmt>(S))
+        S = CapS->getCapturedStmt();
+  } else {
+    // If the number of captured statements to skip is negative,
+    // then skip all of them.
+    while (auto CapS = dyn_cast_or_null<CapturedStmt>(S))
       S = CapS->getCapturedStmt();
+  }
   while (true) {
     if (auto AS = dyn_cast_or_null<AttributedStmt>(S))
       S = AS->getSubStmt();
diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp
index 5adfd919574603..3f2a2d57838c02 100644
--- a/clang/lib/AST/StmtOpenMP.cpp
+++ b/clang/lib/AST/StmtOpenMP.cpp
@@ -259,6 +259,99 @@ void OMPLoopDirective::setFinalsConditions(ArrayRef<Expr *> A) {
   llvm::copy(A, getFinalsConditions().begin());
 }
 
+OMPCompoundBlockDirective *OMPCompoundBlockDirective::Create(
+    const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+    OpenMPDirectiveKind DKind, ArrayRef<OMPClause *> Clauses,
+    Stmt *AssociatedStmt, Expr *TaskRedRef, bool HasCancel,
+    OpenMPDirectiveKind CancelRegion, const DeclarationNameInfo &DirName) {
+  // The child is TaskReductionRefExpr.
+  auto *Dir = createDirective<OMPCompoundBlockDirective>(
+      C, Clauses, AssociatedStmt, /*NumChildren=*/1, DKind, StartLoc, EndLoc);
+  Dir->setTaskReductionRefExpr(TaskRedRef);
+  Dir->setHasCancel(HasCancel);
+  Dir->setCancelRegion(CancelRegion);
+  Dir->setDirectiveName(DirName);
+  return Dir;
+}
+
+OMPCompoundBlockDirective *OMPCompoundBlockDirective::CreateEmpty(
+    const ASTContext &C, OpenMPDirectiveKind DKind, unsigned NumClauses,
+    bool HasAssociatedStmt, EmptyShell) {
+  // The child is TaskReductionRefExpr.
+  return createEmptyDirective<OMPCompoundBlockDirective>(
+      C, NumClauses, HasAssociatedStmt, /*NumChildren=*/1, DKind);
+}
+
+OMPCompoundLoopDirective *OMPCompoundLoopDirective::Create(
+    const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+    OpenMPDirectiveKind Kind, unsigned CollapsedNum,
+    ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt,
+    const HelperExprs &Exprs, Expr *TaskRedRef, bool HasCancel) {
+  // The +1 in the number of children is for TaskReductionRefExpr.
+  auto *Dir = createDirective<OMPCompoundLoopDirective>(
+      C, Clauses, AssociatedStmt, numLoopChildren(CollapsedNum, Kind) + 1, Kind,
+      StartLoc, EndLoc, CollapsedNum);
+  Dir->setIterationVariable(Exprs.IterationVarRef);
+  Dir->setLastIteration(Exprs.LastIteration);
+  Dir->setCalcLastIteration(Exprs.CalcLastIteration);
+  Dir->setPreCond(Exprs.PreCond);
+  Dir->setCond(Exprs.Cond);
+  Dir->setInit(Exprs.Init);
+  Dir->setInc(Exprs.Inc);
+  Dir->setPreInits(Exprs.PreInits);
+
+  if (isOpenMPWorksharingDirective(Kind) ||
+      isOpenMPGenericLoopDirective(Kind) || isOpenMPTaskLoopDirective(Kind) ||
+      isOpenMPDistributeDirective(Kind)) {
+    Dir->setIsLastIterVariable(Exprs.IL);
+    Dir->setLowerBoundVariable(Exprs.LB);
+    Dir->setUpperBoundVariable(Exprs.UB);
+    Dir->setStrideVariable(Exprs.ST);
+    Dir->setEnsureUpperBound(Exprs.EUB);
+    Dir->setNextLowerBound(Exprs.NLB);
+    Dir->setNextUpperBound(Exprs.NUB);
+    Dir->setNumIterations(Exprs.NumIterations);
+  }
+
+  if (isOpenMPLoopBoundSharingDirective(Kind)) {
+    Dir->setPrevLowerBoundVariable(Exprs.PrevLB);
+    Dir->setPrevUpperBoundVariable(Exprs.PrevUB);
+    Dir->setDistInc(Exprs.DistInc);
+    Dir->setPrevEnsureUpperBound(Exprs.PrevEUB);
+    Dir->setCombinedLowerBoundVariable(Exprs.DistCombinedFields.LB);
+    Dir->setCombinedUpperBoundVariable(Exprs.DistCombinedFields.UB);
+    Dir->setCombinedEnsureUpperBound(Exprs.DistCombinedFields.EUB);
+    Dir->setCombinedInit(Exprs.DistCombinedFields.Init);
+    Dir->setCombinedCond(Exprs.DistCombinedFields.Cond);
+    Dir->setCombinedNextLowerBound(Exprs.DistCombinedFields.NLB);
+    Dir->setCombinedNextUpperBound(Exprs.DistCombinedFields.NUB);
+    Dir->setCombinedDistCond(Exprs.DistCombinedFields.DistCond);
+    Dir->setCombinedParForInDistCond(Exprs.DistCombinedFields.ParForInDistCond);
+  }
+
+  Dir->setCounters(Exprs.Counters);
+  Dir->setPrivateCounters(Exprs.PrivateCounters);
+  Dir->setInits(Exprs.Inits);
+  Dir->setUpdates(Exprs.Updates);
+  Dir->setFinals(Exprs.Finals);
+  Dir->setDependentCounters(Exprs.DependentCounters);
+  Dir->setDependentInits(Exprs.DependentInits);
+  Dir->setFinalsConditions(Exprs.FinalsConditions);
+
+  Dir->setTaskReductionRefExpr(TaskRedRef);
+  Dir->setHasCancel(HasCancel);
+  return Dir;
+}
+
+OMPCompoundLoopDirective *OMPCompoundLoopDirective::CreateEmpty(
+    const ASTContext &C, OpenMPDirectiveKind Kind, unsigned NumClauses,
+    unsigned CollapsedNum, EmptyShell) {
+  // The +1 in the number of children is for TaskReductionRefExpr.
+  return createEmptyDirective<OMPCompoundLoopDirective>(
+      C, NumClauses, /*HasAssociatedStmt=*/true,
+      numLoopChildren(CollapsedNum, Kind) + 1, Kind, CollapsedNum);
+}
+
 OMPMetaDirective *OMPMetaDirective::Create(const ASTContext &C,
                                            SourceLocation StartLoc,
                                            SourceLocation EndLoc,
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index e1b5bec7a50d0a..9917e0d40e64bc 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -738,6 +738,41 @@ void StmtPrinter::PrintOMPExecutableDirective(OMPExecutableDirective *S,
     PrintStmt(S->getRawStmt());
 }
 
+void StmtPrinter::VisitOMPCompoundBlockDirective(
+    OMPCompoundBlockDirective *Node) {
+  OpenMPDirectiveKind DKind = Node->getDirectiveKind();
+  bool ForceNoStmt = false;
+
+  Indent() << "#pragma omp " << llvm::omp::getOpenMPDirectiveName(DKind);
+  switch (DKind) {
+  case llvm::omp::OMPD_cancel:
+  case llvm::omp::OMPD_cancellation_point:
+    if (Node->getCancelRegion() != llvm::omp::OMPD_unknown)
+      OS << ' ' << llvm::omp::getOpenMPDirectiveName(Node->getCancelRegion());
+    break;
+  case llvm::omp::OMPD_critical:
+    if (Node->getDirectiveName().getName()) {
+      OS << " (";
+      Node->getDirectiveName().printName(OS, Policy);
+      OS << ")";
+    }
+    break;
+  case llvm::omp::OMPD_target_enter_data:
+  case llvm::omp::OMPD_target_exit_data:
+  case llvm::omp::OMPD_target_update:
+    ForceNoStmt = true;
+    break;
+  }
+  PrintOMPExecutableDirective(Node, ForceNoStmt);
+}
+
+void StmtPrinter::VisitOMPCompoundLoopDirective(
+    OMPCompoundLoopDirective *Node) {
+  Indent() << "#pragma omp "
+           << llvm::omp::getOpenMPDirectiveName(Node->getDirectiveKind());
+  PrintOMPExecutableDirective(Node);
+}
+
 void StmtPrinter::VisitOMPMetaDirective(OMPMetaDirective *Node) {
   Indent() << "#pragma omp metadirective";
   PrintOMPExecutableDirective(Node);
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index ad4281986f668e..23757580fcae4b 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -960,6 +960,16 @@ StmtProfiler::VisitOMPExecutableDirective(const OMPExecutableDirective *S) {
       P.Visit(*I);
 }
 
+void StmtProfiler::VisitOMPCompoundBlockDirective(
+    const OMPCompoundBlockDirective *S) {
+  VisitOMPExecutableDirective(S);
+}
+
+void StmtProfiler::VisitOMPCompoundLoopDirective(
+    const OMPCompoundLoopDirective *S) {
+  VisitOMPExecutableDirective(S);
+}
+
 void StmtProfiler::VisitOMPCanonicalLoop(const OMPCanonicalLoop *L) {
   VisitStmt(L);
 }
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 3c51c746471829..7690d140acd675 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -2347,6 +2347,20 @@ void TextNodeDumper::VisitOMPExecutableDirective(
     OS << " openmp_standalone_directive";
 }
 
+void TextNodeDumper::VisitOMPCompoundBlockDirective(
+    const OMPCompoundBlockDirective *D) {
+  VisitOMPExecutableDirective(D);
+  OpenMPDirectiveKind DKind = D->getDirectiveKind();
+  OS << " '" << llvm::omp::getOpenMPDirectiveName(DKind) << '\'';
+}
+
+void TextNodeDumper::VisitOMPCompoundLoopDirective(
+    const OMPCompoundLoopDirective *D) {
+  VisitOMPExecutableDirective(D);
+  OpenMPDirectiveKind DKind = D->getDirectiveKind();
+  OS << " '" << llvm::omp::getOpenMPDirectiveName(DKind) << '\'';
+}
+
 void TextNodeDumper::VisitOMPDeclareReductionDecl(
     const OMPDeclareReductionDecl *D) {
   dumpName(D);
diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp
index 630a8898aa2293..329c980489db56 100644
--- a/clang/lib/Basic/OpenMPKinds.cpp
+++ b/clang/lib/Basic/OpenMPKinds.cpp
@@ -717,7 +717,8 @@ bool clang::isOpenMPInformationalDirective(OpenMPDirectiveKind DKind) {
   return Cat == Category::Informational;
 }
 
-bool clang::isOpenMPCapturingDirective(OpenMPDirectiveKind DKind) {
+bool clang::isOpenMPCapturingDirective(OpenMPDirectiveKind DKind,
+                                       bool OrderedIsStandalone) {
   if (isOpenMPExecutableDirective(DKind)) {
     switch (DKind) {
     case OMPD_atomic:
@@ -728,13 +729,17 @@ bool clang::isOpenMPCapturingDirective(OpenMPDirectiveKind DKind) {
     case OMPD_depobj:
     case OMPD_error:
     case OMPD_flush:
+    case OMPD_interop:
     case OMPD_masked:
     case OMPD_master:
+    case OMPD_scan:
     case OMPD_section:
     case OMPD_taskwait:
     case OMPD_taskyield:
     case OMPD_assume:
       return false;
+    case OMPD_ordered:
+      return !OrderedIsStandalone;
     default:
       return !isOpenMPLoopTransformationDirective(DKind);
     }
@@ -753,8 +758,12 @@ bool clang::isOpenMPCapturingDirective(OpenMPDirectiveKind DKind) {
 void clang::getOpenMPCaptureRegions(
     SmallVectorImpl<OpenMPDirectiveKind> &CaptureRegions,
     OpenMPDirectiveKind DKind) {
-  assert(unsigned(DKind) < llvm::omp::Directive_enumSize);
-  assert(isOpenMPCapturingDirective(DKind) && "Expecting capturing directive");
+  assert(static_cast<size_t>(DKind) < llvm::omp::Directive_enumSize);
+  assert(isOpenMPCapturingDirective(DKind, /*OrderedIsStandalone=*/false) &&
+         "Expecting capturing directive");
+
+  size_t StartSize = CaptureRegions.size();
+  bool IsComposite = llvm::omp::isCompositeConstruct(DKind);
 
   auto GetRegionsForLeaf = [&](OpenMPDirectiveKind LKind) {
     assert(isLeafConstruct(LKind) && "Epecting leaf directive");
@@ -790,9 +799,11 @@ void clang::getOpenMPCaptureRegions(
       // bind clause or the parent directive when there is no bind clause.
       // If any of the directives that push regions here are parents of 'loop',
       // assume 'parallel'. Otherwise do nothing.
-      if (!CaptureRegions.empty() &&
+      if (CaptureRegions.size() != StartSize &&
           !llvm::is_contained(CaptureRegions, OMPD_parallel))
         CaptureRegions.push_back(OMPD_parallel);
+      else if (!IsComposite)
+        CaptureRegions.push_back(OMPD_unknown);
       else
         return true;
       break;
@@ -810,7 +821,11 @@ void clang::getOpenMPCaptureRegions(
       // but when they're constituents of a compound directive, and other
       // leafs from that directive have specific regions, then these directives
       // add no additional regions.
-      return true;
+      if (!IsComposite)
+        CaptureRegions.push_back(OMPD_unknown);
+      else
+        return true;
+      break;
     case OMPD_masked:
     case OMPD_master:
       return false;
@@ -822,20 +837,35 @@ void clang::getOpenMPCaptureRegions(
   };
 
   bool MayNeedUnknownRegion = false;
-  for (OpenMPDirectiveKind L : getLeafConstructsOrSelf(DKind))
-    MayNeedUnknownRegion |= GetRegionsForLeaf(L);
+  if (IsComposite) {
+    // If it's a composite directive, look at individual leafs.
+    for (OpenMPDirectiveKind L : getLeafConstructsOrSelf(DKind))
+      MayNeedUnknownRegion |= GetRegionsForLeaf(L);
+  } else {
+    // If it's not a composite construct, look at constituent constructs
+    // which may be leaf or composite.
+    SmallVector<OpenMPDirectiveKind> Parts;
+    for (OpenMPDirectiveKind L : getLeafOrCompositeConstructs(DKind, Parts)) {
+      if (isLeafConstruct(L))
+        MayNeedUnknownRegion |= GetRegionsForLeaf(L);
+      else
+        getOpenMPCaptureRegions(CaptureRegions, L);
+    }
+  }
 
   // We need OMPD_unknown when no regions were added, and specific leaf
   // constructs were present. Push a single OMPD_unknown as the capture
   /// region.
-  if (CaptureRegions.empty() && MayNeedUnknownRegion)
+  if (CaptureRegions.size() == StartSize && MayNeedUnknownRegion)
     CaptureRegions.push_back(OMPD_unknown);
 
   // OMPD_unknown is only expected as the only region. If other regions
   // are present OMPD_unknown should not be present.
-  assert((CaptureRegions[0] == OMPD_unknown ||
-          !llvm::is_contained(CaptureRegions, OMPD_unknown)) &&
-         "Misplaced OMPD_unknown");
+  if (IsComposite) {
+    assert((CaptureRegions[StartSize] == OMPD_unknown ||
+            !llvm::is_contained(CaptureRegions, OMPD_unknown)) &&
+           "Misplaced OMPD_unknown");
+  }
 }
 
 bool clang::checkFailClauseParameter(OpenMPClauseKind FailClauseParameter) {
@@ -844,3 +874,21 @@ bool clang::checkFailClauseParameter(OpenMPClauseKind FailClauseParameter) {
          FailClauseParameter == llvm::omp::OMPC_seq_cst;
 }
 
+bool clang::isOpenMPDirectiveWithStatement(OpenMPDirectiveKind DKind,
+                                           bool OrderedIsStandalone) {
+  switch (DKind) {
+  // The association of these in the spec is either "none" or "separating",
+  // but they do have an associated statement in clang.
+  case OMPD_section:
+  case OMPD_target_enter_data:
+  case OMPD_target_exit_data:
+  case OMPD_target_update:
+    return true;
+  case OMPD_ordered:
+    return !OrderedIsStandalone;
+  default:
+    break;
+  }
+  Association Assoc = getDirectiveAssociation(DKind);
+  return Assoc != Association::None && Assoc != Association::Separating;
+}
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index b138c87a853495..44d99ab696be80 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -204,6 +204,11 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
   case Stmt::SEHTryStmtClass:
     EmitSEHTryStmt(cast<SEHTryStmt>(*S));
     break;
+  case Stmt::OMPCompoundBlockDirectiveClass:
+  case Stmt::OMPCompoundLoopDirectiveClass:
+    // These are catch-all nodes for compound OpenMP directives in templates.
+    // Template instantiation should replace them with specific nodes.
+    llvm_unreachable("These nodes should have been eliminated");
   case Stmt::OMPMetaDirectiveClass:
     EmitOMPMetaDirective(cast<OMPMetaDirective>(*S));
     break;
diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp
index 8afe2abf2cc494..055131ffd348d7 100644
--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -92,6 +92,7 @@ class OMPLexicalScope : public CodeGenFunction::LexicalScope {
     assert(S.hasAssociatedStmt() &&
            "Expected associated statement for inlined directive.");
     const CapturedStmt *CS = S.getCapturedStmt(*CapturedRegion);
+
     for (const auto &C : CS->captures()) {
       if (C.capturesVariable() || C.capturesVariableByCopy()) {
         auto *VD = C.getCapturedVar();
@@ -1605,6 +1606,9 @@ static void emitCommonOMPParallelDirective(
       CGF.CGM.getOpenMPRuntime().emitParallelOutlinedFunction(
           CGF, S, *CS->getCapturedDecl()->param_begin(), InnermostKind,
           CodeGen);
+  // The codegen for the clauses below may require variables created by their
+  // pre-inits.
+  OMPParallelScope Scope(CGF, S);
   if (const auto *NumThreadsClause = S.getSingleClause<OMPNumThreadsClause>()) {
     CodeGenFunction::RunCleanupsScope NumThreadsScope(CGF);
     NumThreads = CGF.EmitScalarExpr(NumThreadsClause->getNumThreads(),
@@ -1625,8 +1629,6 @@ static void emitCommonOMPParallelDirective(
       break;
     }
   }
-
-  OMPParallelScope Scope(CGF, S);
   llvm::SmallVector<llvm::Value *, 16> CapturedVars;
   // Combining 'distribute' with 'for' requires sharing each 'distribute' chunk
   // lower and upper bounds with the pragma 'for' chunking mechanism.
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 64dfcd47296998..55f060e4608018 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2404,15 +2404,10 @@ StmtResult Parser::ParseOpenMPExecutableDirective(
     bool ReadDirectiveWithinMetadirective) {
   assert(isOpenMPExecutableDirective(DKind) && "Unexpected directive category");
 
-  bool HasAssociatedStatement = true;
-  Association Assoc = getDirectiveAssociation(DKind);
-
-  // OMPD_ordered has None as association, but it comes in two variants,
-  // the second of which is associated with a block.
-  // OMPD_scan and OMPD_section are both "separating", but section is treated
-  // as if it was associated with a statement, while scan is not.
-  if (DKind != OMPD_ordered && DKind != OMPD_section &&
-      (Assoc == Association::None || Assoc == Association::Separating)) {
+  bool HasAssociatedStatement =
+      isOpenMPDirectiveWithStatement(DKind, /*OrderedIsStandalone=*/false);
+
+  if (!HasAssociatedStatement) {
     if ((StmtCtx & ParsedStmtContext::AllowStandaloneOpenMPDirectives) ==
         ParsedStmtContext()) {
       Diag(Tok, diag::err_omp_immediate_directive)
@@ -2422,7 +2417,6 @@ StmtResult Parser::ParseOpenMPExecutableDirective(
         return StmtError();
       }
     }
-    HasAssociatedStatement = false;
   }
 
   SourceLocation EndLoc;
@@ -2539,9 +2533,17 @@ StmtResult Parser::ParseOpenMPExecutableDirective(
   }
 
   StmtResult AssociatedStmt;
-  if (HasAssociatedStatement) {
+  Actions.OpenMP().ActOnOpenMPRegionStart(DKind, getCurScope(),
+                                          HasAssociatedStatement);
+  // These directives do have an associated statement in clang, but they
+  // are standalone in the source.
+  if (DKind == OMPD_target_update || DKind == OMPD_target_enter_data ||
+      DKind == OMPD_target_exit_data) {
+    AssociatedStmt = (Sema::CompoundScopeRAII(Actions),
+                      Actions.ActOnCompoundStmt(Loc, Loc, std::nullopt,
+                                                /*isStmtExpr=*/false));
+  } else if (HasAssociatedStatement) {
     // The body is a block scope like in Lambdas and Blocks.
-    Actions.OpenMP().ActOnOpenMPRegionStart(DKind, getCurScope());
     // FIXME: We create a bogus CompoundStmt scope to hold the contents of
     // the captured region. Code elsewhere assumes that any FunctionScopeInfo
     // should have at least one compound statement scope within it.
@@ -2555,18 +2557,10 @@ StmtResult Parser::ParseOpenMPExecutableDirective(
         AssociatedStmt =
             Actions.OpenMP().ActOnOpenMPLoopnest(AssociatedStmt.get());
     }
-    AssociatedStmt =
-        Actions.OpenMP().ActOnOpenMPRegionEnd(AssociatedStmt, Clauses);
-  } else if (DKind == OMPD_target_update || DKind == OMPD_target_enter_data ||
-             DKind == OMPD_target_exit_data) {
-    Actions.OpenMP().ActOnOpenMPRegionStart(DKind, getCurScope());
-    AssociatedStmt = (Sema::CompoundScopeRAII(Actions),
-                      Actions.ActOnCompoundStmt(Loc, Loc, std::nullopt,
-                                                /*isStmtExpr=*/false));
-    AssociatedStmt =
-        Actions.OpenMP().ActOnOpenMPRegionEnd(AssociatedStmt, Clauses);
   }
 
+  AssociatedStmt =
+      Actions.OpenMP().ActOnOpenMPRegionEnd(AssociatedStmt, Clauses);
   StmtResult Directive = Actions.OpenMP().ActOnOpenMPExecutableDirective(
       DKind, DirName, CancelRegion, Clauses, AssociatedStmt.get(), Loc, EndLoc);
 
@@ -2621,17 +2615,18 @@ StmtResult Parser::ParseOpenMPInformationalDirective(
   ConsumeAnnotationToken();
 
   StmtResult AssociatedStmt;
+  Actions.OpenMP().ActOnOpenMPRegionStart(DKind, getCurScope(),
+                                          HasAssociatedStatement);
   if (HasAssociatedStatement) {
-    Actions.OpenMP().ActOnOpenMPRegionStart(DKind, getCurScope());
     ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false);
     {
       Sema::CompoundScopeRAII Scope(Actions);
       AssociatedStmt = ParseStatement();
     }
-    AssociatedStmt =
-        Actions.OpenMP().ActOnOpenMPRegionEnd(AssociatedStmt, Clauses);
   }
 
+  AssociatedStmt =
+      Actions.OpenMP().ActOnOpenMPRegionEnd(AssociatedStmt, Clauses);
   StmtResult Directive = Actions.OpenMP().ActOnOpenMPInformationalDirective(
       DKind, DirName, Clauses, AssociatedStmt.get(), Loc, EndLoc);
 
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 2cee4f5ef6e99c..f8630d730749ec 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -71,6 +71,7 @@ add_clang_library(clangSema
   SemaOpenACC.cpp
   SemaOpenCL.cpp
   SemaOpenMP.cpp
+  SemaOpenMPExt.cpp
   SemaOverload.cpp
   SemaPPC.cpp
   SemaPseudoObject.cpp
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index 8aedbfcf878a11..af954db9d715a1 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1436,6 +1436,8 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Stmt::OMPBarrierDirectiveClass:
   case Stmt::OMPCancelDirectiveClass:
   case Stmt::OMPCancellationPointDirectiveClass:
+  case Stmt::OMPCompoundBlockDirectiveClass:
+  case Stmt::OMPCompoundLoopDirectiveClass:
   case Stmt::OMPCriticalDirectiveClass:
   case Stmt::OMPDistributeDirectiveClass:
   case Stmt::OMPDistributeParallelForDirectiveClass:
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 8f3e15cc9a9bb7..fac87677ba27c6 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18517,6 +18517,9 @@ static bool captureInCapturedRegion(
     ByRef = (Kind == Sema::TryCapture_ExplicitByRef);
   } else if (S.getLangOpts().OpenMP && RSI->CapRegionKind == CR_OpenMP) {
     // Using an LValue reference type is consistent with Lambdas (see below).
+    if (!S.OpenMP().shouldCaptureInRegion(Var, RSI->OpenMPLevel,
+                                          RSI->OpenMPCaptureLevel))
+      return true;
     if (S.OpenMP().isOpenMPCapturedDecl(Var)) {
       bool HasConst = DeclRefType.isConstQualified();
       DeclRefType = DeclRefType.getUnqualifiedType();
@@ -18524,10 +18527,6 @@ static bool captureInCapturedRegion(
       if (HasConst)
         DeclRefType.addConst();
     }
-    // Do not capture firstprivates in tasks.
-    if (S.OpenMP().isOpenMPPrivateDecl(Var, RSI->OpenMPLevel,
-                                       RSI->OpenMPCaptureLevel) != OMPC_unknown)
-      return true;
     ByRef = S.OpenMP().isOpenMPCapturedByRef(Var, RSI->OpenMPLevel,
                                              RSI->OpenMPCaptureLevel);
   }
@@ -18931,9 +18930,7 @@ bool Sema::tryCaptureVariable(
             QualType QTy = Var->getType();
             if (ParmVarDecl *PVD = dyn_cast_or_null<ParmVarDecl>(Var))
               QTy = PVD->getOriginalType();
-            for (int I = 1,
-                     E = OpenMP().getNumberOfConstructScopes(RSI->OpenMPLevel);
-                 I < E; ++I) {
+            for (int I = 1, E = RSI->OpenMPCaptureLevel + 1; I < E; ++I) {
               auto *OuterRSI = cast<CapturedRegionScopeInfo>(
                   FunctionScopes[FunctionScopesIndex - I]);
               assert(RSI->OpenMPLevel == OuterRSI->OpenMPLevel &&
@@ -18955,8 +18952,8 @@ bool Sema::tryCaptureVariable(
           // target region, therefore we need to propagate the capture from the
           // enclosing region. Therefore, the capture is not initially nested.
           if (IsTargetCap)
-            OpenMP().adjustOpenMPTargetScopeIndex(FunctionScopesIndex,
-                                                  RSI->OpenMPLevel);
+            OpenMP().adjustOpenMPTargetScopeIndex(
+                FunctionScopesIndex, RSI->OpenMPLevel, RSI->OpenMPCaptureLevel);
 
           if (IsTargetCap || IsOpenMPPrivateDecl == OMPC_private ||
               (IsGlobal && !IsGlobalCap)) {
@@ -18967,6 +18964,10 @@ bool Sema::tryCaptureVariable(
             if (HasConst)
               DeclRefType.addConst();
             CaptureType = Context.getLValueReferenceType(DeclRefType);
+            if (!IsTargetCap)
+              OpenMP().adjustOpenMPGlobalScopeIndex(Var, FunctionScopesIndex,
+                                                    RSI->OpenMPLevel,
+                                                    RSI->OpenMPCaptureLevel);
             break;
           }
         }
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index b952ffbd69f5d5..49932d68a0ec18 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -13,6 +13,7 @@
 
 #include "clang/Sema/SemaOpenMP.h"
 
+#include "SemaOpenMPExt.h"
 #include "TreeTransform.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTMutationListener.h"
@@ -38,6 +39,7 @@
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaInternal.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/IndexedMap.h"
 #include "llvm/ADT/PointerEmbeddedInt.h"
 #include "llvm/ADT/STLExtras.h"
@@ -45,6 +47,7 @@
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/Frontend/OpenMP/ClauseT.h"
 #include "llvm/Frontend/OpenMP/OMPAssume.h"
 #include "llvm/Frontend/OpenMP/OMPConstants.h"
 #include "llvm/IR/Assumptions.h"
@@ -73,6 +76,12 @@ enum DefaultDataSharingAttributes {
   DSA_firstprivate = 1 << 3, /// Default data sharing attribute 'firstprivate'.
 };
 
+struct UnitConstruct {
+  OpenMPDirectiveKind DKind;
+  SmallVector<OMPClause *> Clauses;
+  SmallVector<omp::Clause> Pending; // Clauses that need AST nodes.
+};
+
 /// Stack for tracking declarations used in OpenMP directives and
 /// clauses and their data-sharing attributes.
 class DSAStackTy {
@@ -172,6 +181,7 @@ class DSAStackTy {
     OpenMPDirectiveKind Directive = OMPD_unknown;
     DeclarationNameInfo DirectiveName;
     Scope *CurScope = nullptr;
+    // Used to detemine variables local to OpenMP constructs.
     DeclContext *Context = nullptr;
     SourceLocation ConstructLoc;
     /// Set of 'depend' clauses with 'sink|source' dependence kind. Required to
@@ -224,6 +234,21 @@ class DSAStackTy {
         ImplicitDefaultFirstprivateFDs;
     Expr *DeclareMapperVar = nullptr;
     SmallVector<VarDecl *, 16> IteratorVarDecls;
+    // List of leaf-or-composite constructs with clauses created from the
+    // current directive. This is created in ActOnOpenMPRegionEnd, and then
+    // used to create the AST in createASTForDirective. After that it's no
+    // longer needed.
+    SmallVector<UnitConstruct> UnitConstructs;
+    // List of capture initialization statements. These statements declare
+    // and initialize captures for `num_teams` and `thread_limit` clauses,
+    // whose expressions need to be evaluated before the outermost leaf
+    // construct. [5.2:341:24-30]
+    llvm::DenseMap<OMPClause *, Stmt *> PreInits;
+    // Variable DSA info collected during creating capture regions.
+    // Used for contructing leaf-or-composite directives.
+    SemaOpenMP::VarsWithInheritedDSAType VarsWithInheritedDSA;
+    bool HasDSAError = false;
+
     SharingMapTy(OpenMPDirectiveKind DKind, DeclarationNameInfo Name,
                  Scope *CurScope, SourceLocation Loc)
         : Directive(DKind), DirectiveName(Name), CurScope(CurScope),
@@ -288,6 +313,8 @@ class DSAStackTy {
   const SharingMapTy *getTopOfStackOrNull() const {
     return const_cast<DSAStackTy &>(*this).getTopOfStackOrNull();
   }
+
+public:
   SharingMapTy &getTopOfStack() {
     assert(!isStackEmpty() && "no current directive");
     return *getTopOfStackOrNull();
@@ -296,6 +323,7 @@ class DSAStackTy {
     return const_cast<DSAStackTy &>(*this).getTopOfStack();
   }
 
+private:
   SharingMapTy *getSecondOnStackOrNull() {
     size_t Size = getStackSize();
     if (Size <= 1)
@@ -1221,6 +1249,14 @@ class DSAStackTy {
     }
     assert((StackLevel > 0 && I != EndI) || (StackLevel == 0 && I == EndI));
   }
+  /// Add a capture initialization statement
+  void addPreInit(OMPClause *Clause, Stmt *Init) {
+    bool Inserted = getTopOfStack().PreInits.insert({Clause, Init}).second;
+    assert(Inserted);
+  }
+  llvm::DenseMap<OMPClause *, Stmt *> &getPreInits() {
+    return getTopOfStack().PreInits;
+  }
 };
 
 bool isImplicitTaskingRegion(OpenMPDirectiveKind DKind) {
@@ -2281,6 +2317,16 @@ bool SemaOpenMP::isOpenMPCapturedByRef(const ValueDecl *D, unsigned Level,
   return IsByRef;
 }
 
+bool SemaOpenMP::shouldCaptureInRegion(ValueDecl *D, unsigned Level,
+                                       unsigned OpenMPCaptureLevel) const {
+  assert(getLangOpts().OpenMP && "OpenMP is not allowed");
+  // Do not capture firstprivates in tasks.
+  if (isOpenMPPrivateDecl(D, Level, OpenMPCaptureLevel) != OMPC_unknown)
+    return false;
+
+  return true;
+}
+
 unsigned SemaOpenMP::getOpenMPNestingLevel() const {
   assert(getLangOpts().OpenMP);
   return DSAStack->getNestingLevel();
@@ -2320,10 +2366,10 @@ bool SemaOpenMP::isOpenMPRebuildMemberExpr(ValueDecl *D) {
   return false;
 }
 
-static OMPCapturedExprDecl *buildCaptureDecl(Sema &S, IdentifierInfo *Id,
-                                             Expr *CaptureExpr, bool WithInit,
-                                             DeclContext *CurContext,
-                                             bool AsExpression);
+static VarDecl *buildCaptureDecl(Sema &S, IdentifierInfo *Id, Expr *CaptureExpr,
+                                 bool WithInit, DeclContext *CurContext,
+                                 bool AsExpression,
+                                 bool MakeOMPCapturedExprDecl);
 
 VarDecl *SemaOpenMP::isOpenMPCapturedDecl(ValueDecl *D, bool CheckScopeInfo,
                                           unsigned StopAt) {
@@ -2468,9 +2514,10 @@ VarDecl *SemaOpenMP::isOpenMPCapturedDecl(ValueDecl *D, bool CheckScopeInfo,
           DeclAccessPair::make(FD, FD->getAccess()),
           /*HadMultipleCandidates=*/false, DeclarationNameInfo(), FD->getType(),
           VK_LValue, OK_Ordinary);
-      OMPCapturedExprDecl *CD = buildCaptureDecl(
+      VarDecl *CD = buildCaptureDecl(
           SemaRef, FD->getIdentifier(), ME, DVarPrivate.CKind != OMPC_private,
-          SemaRef.CurContext->getParent(), /*AsExpression=*/false);
+          SemaRef.CurContext->getParent(), /*AsExpression=*/false,
+          /*MakeOMPCapturedExprDecl=*/true);
       DeclRefExpr *VDPrivateRefExpr = buildDeclRefExpr(
           SemaRef, CD, CD->getType().getNonReferenceType(), SourceLocation());
       VD = cast<VarDecl>(VDPrivateRefExpr->getDecl());
@@ -2487,8 +2534,30 @@ VarDecl *SemaOpenMP::isOpenMPCapturedDecl(ValueDecl *D, bool CheckScopeInfo,
 }
 
 void SemaOpenMP::adjustOpenMPTargetScopeIndex(unsigned &FunctionScopesIndex,
-                                              unsigned Level) const {
-  FunctionScopesIndex -= getOpenMPCaptureLevels(DSAStack->getDirective(Level));
+                                              unsigned Level,
+                                              unsigned CaptureLevel) const {
+  assert(isOpenMPTargetExecutionDirective(DSAStack->getDirective(Level)));
+  FunctionScopesIndex -= (CaptureLevel + 1);
+}
+
+void SemaOpenMP::adjustOpenMPGlobalScopeIndex(ValueDecl *VD,
+                                              unsigned &FunctionScopesIndex,
+                                              unsigned Level,
+                                              unsigned CaptureLevel) const {
+  if (auto *V = dyn_cast<VarDecl>(VD); V && DSAStack->isThreadPrivate(V)) {
+    OpenMPDirectiveKind Kind = DSAStack->getDirective(Level);
+    SmallVector<OpenMPDirectiveKind> Regions;
+    getOpenMPCaptureRegions(Regions, Kind);
+    for (unsigned CL = CaptureLevel + 1; CL > 0; --CL) {
+      OpenMPDirectiveKind RKind = Regions[CL - 1];
+      // FunctionScopesIndex is the index of the parent of the first capturing
+      // scope.
+      --FunctionScopesIndex;
+      // Looking for a team-generating directive.
+      if (RKind == OMPD_parallel || RKind == OMPD_teams)
+        break;
+    }
+  }
 }
 
 void SemaOpenMP::startOpenMPLoop() {
@@ -2795,21 +2864,7 @@ static void checkReductionClauses(Sema &S, DSAStackTy *Stack,
     if (RC->getModifier() == OMPC_REDUCTION_inscan) {
       InscanFound = true;
       InscanLoc = RC->getModifierLoc();
-      continue;
-    }
-    if (RC->getModifier() == OMPC_REDUCTION_task) {
-      // OpenMP 5.0, 2.19.5.4 reduction Clause.
-      // A reduction clause with the task reduction-modifier may only appear on
-      // a parallel construct, a worksharing construct or a combined or
-      // composite construct for which any of the aforementioned constructs is a
-      // constituent construct and simd or loop are not constituent constructs.
-      OpenMPDirectiveKind CurDir = Stack->getCurrentDirective();
-      if (!(isOpenMPParallelDirective(CurDir) ||
-            isOpenMPWorksharingDirective(CurDir)) ||
-          isOpenMPSimdDirective(CurDir))
-        S.Diag(RC->getModifierLoc(),
-               diag::err_omp_reduction_task_not_parallel_or_worksharing);
-      continue;
+      break;
     }
   }
   if (InscanFound) {
@@ -2847,8 +2902,6 @@ static void checkReductionClauses(Sema &S, DSAStackTy *Stack,
 
 static void checkAllocateClauses(Sema &S, DSAStackTy *Stack,
                                  ArrayRef<OMPClause *> Clauses);
-static DeclRefExpr *buildCapture(Sema &S, ValueDecl *D, Expr *CaptureExpr,
-                                 bool WithInit);
 
 static void reportOriginalDsa(Sema &SemaRef, const DSAStackTy *Stack,
                               const ValueDecl *D,
@@ -2861,119 +2914,166 @@ void SemaOpenMP::EndOpenMPDSABlock(Stmt *CurDirective) {
   //  clause requires an accessible, unambiguous default constructor for the
   //  class type, unless the list item is also specified in a firstprivate
   //  clause.
-  if (const auto *D = dyn_cast_or_null<OMPExecutableDirective>(CurDirective)) {
-    for (OMPClause *C : D->clauses()) {
-      if (auto *Clause = dyn_cast<OMPLastprivateClause>(C)) {
-        SmallVector<Expr *, 8> PrivateCopies;
-        for (Expr *DE : Clause->varlist()) {
-          if (DE->isValueDependent() || DE->isTypeDependent()) {
-            PrivateCopies.push_back(nullptr);
-            continue;
-          }
-          auto *DRE = cast<DeclRefExpr>(DE->IgnoreParens());
-          auto *VD = cast<VarDecl>(DRE->getDecl());
-          QualType Type = VD->getType().getNonReferenceType();
-          const DSAStackTy::DSAVarData DVar =
-              DSAStack->getTopDSA(VD, /*FromParent=*/false);
-          if (DVar.CKind == OMPC_lastprivate) {
-            // Generate helper private variable and initialize it with the
-            // default value. The address of the original variable is replaced
-            // by the address of the new private variable in CodeGen. This new
-            // variable is not added to IdResolver, so the code in the OpenMP
-            // region uses original variable for proper diagnostics.
-            VarDecl *VDPrivate = buildVarDecl(
-                SemaRef, DE->getExprLoc(), Type.getUnqualifiedType(),
-                VD->getName(), VD->hasAttrs() ? &VD->getAttrs() : nullptr, DRE);
-            SemaRef.ActOnUninitializedDecl(VDPrivate);
-            if (VDPrivate->isInvalidDecl()) {
-              PrivateCopies.push_back(nullptr);
-              continue;
-            }
-            PrivateCopies.push_back(buildDeclRefExpr(
-                SemaRef, VDPrivate, DE->getType(), DE->getExprLoc()));
-          } else {
-            // The variable is also a firstprivate, so initialization sequence
-            // for private copy is generated already.
-            PrivateCopies.push_back(nullptr);
-          }
+  auto finalizeLastprivate = [&](OMPLastprivateClause *Clause) {
+    SmallVector<Expr *, 8> PrivateCopies;
+    for (Expr *DE : Clause->varlist()) {
+      if (DE->isValueDependent() || DE->isTypeDependent()) {
+        PrivateCopies.push_back(nullptr);
+        continue;
+      }
+      auto *DRE = cast<DeclRefExpr>(DE->IgnoreParens());
+      auto *VD = cast<VarDecl>(DRE->getDecl());
+      QualType Type = VD->getType().getNonReferenceType();
+      const DSAStackTy::DSAVarData DVar =
+          DSAStack->getTopDSA(VD, /*FromParent=*/false);
+      if (DVar.CKind == OMPC_lastprivate) {
+        // Generate helper private variable and initialize it with the
+        // default value. The address of the original variable is replaced
+        // by the address of the new private variable in CodeGen. This new
+        // variable is not added to IdResolver, so the code in the OpenMP
+        // region uses original variable for proper diagnostics.
+        VarDecl *VDPrivate = buildVarDecl(
+            SemaRef, DE->getExprLoc(), Type.getUnqualifiedType(), VD->getName(),
+            VD->hasAttrs() ? &VD->getAttrs() : nullptr, DRE);
+        SemaRef.ActOnUninitializedDecl(VDPrivate);
+        if (VDPrivate->isInvalidDecl()) {
+          PrivateCopies.push_back(nullptr);
+          continue;
         }
-        Clause->setPrivateCopies(PrivateCopies);
+        PrivateCopies.push_back(buildDeclRefExpr(
+            SemaRef, VDPrivate, DE->getType(), DE->getExprLoc()));
+      } else {
+        // The variable is also a firstprivate, so initialization sequence
+        // for private copy is generated already.
+        PrivateCopies.push_back(nullptr);
+      }
+    }
+    Clause->setPrivateCopies(PrivateCopies);
+  };
+
+  auto finalizeNontemporal = [&](OMPNontemporalClause *Clause) {
+    // Finalize nontemporal clause by handling private copies, if any.
+    SmallVector<Expr *, 8> PrivateRefs;
+    for (Expr *RefExpr : Clause->varlist()) {
+      assert(RefExpr && "NULL expr in OpenMP nontemporal clause.");
+      SourceLocation ELoc;
+      SourceRange ERange;
+      Expr *SimpleRefExpr = RefExpr;
+      auto Res = getPrivateItem(SemaRef, SimpleRefExpr, ELoc, ERange);
+      if (Res.second)
+        // It will be analyzed later.
+        PrivateRefs.push_back(RefExpr);
+      ValueDecl *D = Res.first;
+      if (!D)
+        continue;
+
+      const DSAStackTy::DSAVarData DVar =
+          DSAStack->getTopDSA(D, /*FromParent=*/false);
+      PrivateRefs.push_back(DVar.PrivateCopy ? DVar.PrivateCopy
+                                             : SimpleRefExpr);
+    }
+    Clause->setPrivateRefs(PrivateRefs);
+  };
+
+  auto finalizeAllocators = [&](OMPUsesAllocatorsClause *Clause) {
+    for (unsigned I = 0, E = Clause->getNumberOfAllocators(); I < E; ++I) {
+      OMPUsesAllocatorsClause::Data D = Clause->getAllocatorData(I);
+      auto *DRE = dyn_cast<DeclRefExpr>(D.Allocator->IgnoreParenImpCasts());
+      if (!DRE)
         continue;
+      ValueDecl *VD = DRE->getDecl();
+      if (!VD || !isa<VarDecl>(VD))
+        continue;
+      DSAStackTy::DSAVarData DVar =
+          DSAStack->getTopDSA(VD, /*FromParent=*/false);
+      // OpenMP [2.12.5, target Construct]
+      // Memory allocators that appear in a uses_allocators clause cannot
+      // appear in other data-sharing attribute clauses or data-mapping
+      // attribute clauses in the same construct.
+      Expr *MapExpr = nullptr;
+      if (DVar.RefExpr ||
+          DSAStack->checkMappableExprComponentListsForDecl(
+              VD, /*CurrentRegionOnly=*/true,
+              [VD, &MapExpr](
+                  OMPClauseMappableExprCommon::MappableExprComponentListRef
+                      MapExprComponents,
+                  OpenMPClauseKind C) {
+                auto MI = MapExprComponents.rbegin();
+                auto ME = MapExprComponents.rend();
+                if (MI != ME &&
+                    MI->getAssociatedDeclaration()->getCanonicalDecl() ==
+                        VD->getCanonicalDecl()) {
+                  MapExpr = MI->getAssociatedExpression();
+                  return true;
+                }
+                return false;
+              })) {
+        Diag(D.Allocator->getExprLoc(), diag::err_omp_allocator_used_in_clauses)
+            << D.Allocator->getSourceRange();
+        if (DVar.RefExpr)
+          reportOriginalDsa(SemaRef, DSAStack, VD, DVar);
+        else
+          Diag(MapExpr->getExprLoc(), diag::note_used_here)
+              << MapExpr->getSourceRange();
       }
-      // Finalize nontemporal clause by handling private copies, if any.
-      if (auto *Clause = dyn_cast<OMPNontemporalClause>(C)) {
-        SmallVector<Expr *, 8> PrivateRefs;
-        for (Expr *RefExpr : Clause->varlist()) {
-          assert(RefExpr && "NULL expr in OpenMP nontemporal clause.");
-          SourceLocation ELoc;
-          SourceRange ERange;
-          Expr *SimpleRefExpr = RefExpr;
-          auto Res = getPrivateItem(SemaRef, SimpleRefExpr, ELoc, ERange);
-          if (Res.second)
-            // It will be analyzed later.
-            PrivateRefs.push_back(RefExpr);
-          ValueDecl *D = Res.first;
-          if (!D)
-            continue;
+    }
+  };
 
-          const DSAStackTy::DSAVarData DVar =
-              DSAStack->getTopDSA(D, /*FromParent=*/false);
-          PrivateRefs.push_back(DVar.PrivateCopy ? DVar.PrivateCopy
-                                                 : SimpleRefExpr);
+  auto finalizeDirective = [&](Stmt *Dir) {
+    if (const auto *D = dyn_cast_or_null<OMPExecutableDirective>(Dir)) {
+      for (OMPClause *C : D->clauses()) {
+        if (auto *Clause = dyn_cast<OMPLastprivateClause>(C)) {
+          finalizeLastprivate(Clause);
+        } else if (auto *Clause = dyn_cast<OMPNontemporalClause>(C)) {
+          finalizeNontemporal(Clause);
+        } else if (auto *Clause = dyn_cast<OMPUsesAllocatorsClause>(C)) {
+          finalizeAllocators(Clause);
         }
-        Clause->setPrivateRefs(PrivateRefs);
-        continue;
       }
-      if (auto *Clause = dyn_cast<OMPUsesAllocatorsClause>(C)) {
-        for (unsigned I = 0, E = Clause->getNumberOfAllocators(); I < E; ++I) {
-          OMPUsesAllocatorsClause::Data D = Clause->getAllocatorData(I);
-          auto *DRE = dyn_cast<DeclRefExpr>(D.Allocator->IgnoreParenImpCasts());
-          if (!DRE)
-            continue;
-          ValueDecl *VD = DRE->getDecl();
-          if (!VD || !isa<VarDecl>(VD))
+      // Check allocate clauses.
+      if (!SemaRef.CurContext->isDependentContext())
+        checkAllocateClauses(SemaRef, DSAStack, D->clauses());
+      checkReductionClauses(SemaRef, DSAStack, D->clauses());
+    }
+  };
+
+  SmallVector<Stmt *> Directives;
+  OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
+
+  if (isa_and_present<OMPExecutableDirective>(CurDirective)) {
+    if (!SemaRef.CurContext->isDependentContext()) {
+      SmallVector<OpenMPDirectiveKind> Parts;
+      OpenMPDirectiveKind CurKind =
+          cast<OMPExecutableDirective>(CurDirective)->getDirectiveKind();
+      if (isLeafConstruct(CurKind) || isCompositeConstruct(CurKind)) {
+        std::ignore = getLeafOrCompositeConstructs(DKind, Parts);
+      } else {
+        // XXX Is this a good way to detect splitting errors?
+        // If there was an error in splitting the directive, we keep it
+        // in the original (compound) form.
+        Parts.push_back(DKind);
+      }
+
+      Stmt *S = CurDirective;
+      for (OpenMPDirectiveKind D : Parts) {
+        Directives.push_back(S);
+        assert(isa<OMPExecutableDirective>(S) &&
+               "Expecting executable directive");
+        if (cast<OMPExecutableDirective>(S)->hasAssociatedStmt()) {
+          S = cast<OMPExecutableDirective>(S)->getAssociatedStmt();
+          if (!isOpenMPCapturingDirective(D, /*OrderedIsStandalone=*/false))
             continue;
-          DSAStackTy::DSAVarData DVar =
-              DSAStack->getTopDSA(VD, /*FromParent=*/false);
-          // OpenMP [2.12.5, target Construct]
-          // Memory allocators that appear in a uses_allocators clause cannot
-          // appear in other data-sharing attribute clauses or data-mapping
-          // attribute clauses in the same construct.
-          Expr *MapExpr = nullptr;
-          if (DVar.RefExpr ||
-              DSAStack->checkMappableExprComponentListsForDecl(
-                  VD, /*CurrentRegionOnly=*/true,
-                  [VD, &MapExpr](
-                      OMPClauseMappableExprCommon::MappableExprComponentListRef
-                          MapExprComponents,
-                      OpenMPClauseKind C) {
-                    auto MI = MapExprComponents.rbegin();
-                    auto ME = MapExprComponents.rend();
-                    if (MI != ME &&
-                        MI->getAssociatedDeclaration()->getCanonicalDecl() ==
-                            VD->getCanonicalDecl()) {
-                      MapExpr = MI->getAssociatedExpression();
-                      return true;
-                    }
-                    return false;
-                  })) {
-            Diag(D.Allocator->getExprLoc(),
-                 diag::err_omp_allocator_used_in_clauses)
-                << D.Allocator->getSourceRange();
-            if (DVar.RefExpr)
-              reportOriginalDsa(SemaRef, DSAStack, VD, DVar);
-            else
-              Diag(MapExpr->getExprLoc(), diag::note_used_here)
-                  << MapExpr->getSourceRange();
-          }
+          for (int I = 0, E = getOpenMPCaptureLevels(D); I != E; ++I)
+            S = cast<CapturedStmt>(S)->getCapturedStmt();
         }
-        continue;
       }
+    } else {
+      // Inside templates we use a single compound directive.
+      Directives.push_back(CurDirective);
     }
-    // Check allocate clauses.
-    if (!SemaRef.CurContext->isDependentContext())
-      checkAllocateClauses(SemaRef, DSAStack, D->clauses());
-    checkReductionClauses(SemaRef, DSAStack, D->clauses());
+
+    for (Stmt *Dir : llvm::reverse(Directives))
+      finalizeDirective(Dir);
   }
 
   DSAStack->pop();
@@ -3707,6 +3807,87 @@ getMapClauseKindFromModifier(OpenMPDefaultmapClauseModifier M,
   return Kind;
 }
 
+void SemaOpenMP::VariableImplicitInfo::addPrivate(Expr *E) {
+  if (!isDeclPresent(Privates, E))
+    Privates.push_back(E);
+}
+
+void SemaOpenMP::VariableImplicitInfo::addFirstprivate(Expr *E) {
+  if (!isDeclPresent(Firstprivates, E))
+    Firstprivates.push_back(E);
+}
+
+void SemaOpenMP::VariableImplicitInfo::addMapping(Expr *E, unsigned DefKind,
+                                                  unsigned MapKind) {
+  auto &Ms = Mappings[DefKind][MapKind];
+  if (!isDeclPresent(Ms, E))
+    Ms.push_back(E);
+}
+
+void SemaOpenMP::VariableImplicitInfo::addMapModifier(unsigned MapKind,
+                                                      unsigned DefKind) {
+  if (!llvm::is_contained(MapModifiers[DefKind], MapKind))
+    MapModifiers[DefKind].push_back(
+        static_cast<OpenMPMapModifierKind>(MapKind));
+}
+
+void SemaOpenMP::VariableImplicitInfo::addReductionMapping(Expr *E) {
+  if (!isDeclPresent(ReductionMappings, E))
+    ReductionMappings.push_back(E);
+}
+
+SemaOpenMP::VariableImplicitInfo &
+SemaOpenMP::VariableImplicitInfo::include(const VariableImplicitInfo &Other) {
+  for (Expr *E : Other.Privates)
+    addPrivate(E);
+  for (Expr *E : Other.Firstprivates)
+    addFirstprivate(E);
+  for (unsigned I = 0; I != DefaultmapKindNum; ++I) {
+    for (unsigned J = 0; J != MapKindNum; ++J) {
+      for (Expr *E : Other.Mappings[I][J])
+        addMapping(E, I, J);
+    }
+    for (unsigned MapKind : Other.MapModifiers[I])
+      addMapModifier(MapKind, I);
+  }
+  for (Expr *E : Other.ReductionMappings)
+    addReductionMapping(E);
+
+  return *this;
+}
+
+#ifndef NDEBUG
+bool SemaOpenMP::VariableImplicitInfo::empty() const {
+  if (!Privates.empty() || !Firstprivates.empty() || !ReductionMappings.empty())
+    return false;
+  for (unsigned I = 0; I != DefaultmapKindNum; ++I) {
+    for (unsigned J = 0; J != MapKindNum; ++J) {
+      if (!Mappings[I][J].empty())
+        return false;
+    }
+  }
+  return true;
+}
+#endif
+
+std::pair<Stmt *, Decl *> SemaOpenMP::VariableImplicitInfo::getDecl(Expr *E) {
+  if (auto *DRE = dyn_cast<DeclRefExpr>(E))
+    return {nullptr, DRE->getDecl()};
+  if (auto *ME = dyn_cast<MemberExpr>(E))
+    return {ME->getBase(), ME->getMemberDecl()};
+  if (isa<ArraySectionExpr, ArraySubscriptExpr>(E))
+    return {nullptr, nullptr};
+  llvm_unreachable("Unexpected expression");
+}
+
+bool SemaOpenMP::VariableImplicitInfo::isDeclPresent(ArrayRef<Expr *> Range,
+                                                     Expr *E) {
+  std::pair<Stmt *, Decl *> D = getDecl(E);
+  if (!D.second)
+    return false;
+  return llvm::any_of(Range, [&](Expr *T) { return D == getDecl(T); });
+}
+
 namespace {
 struct VariableImplicitInfo {
   static const unsigned MapKindNum = OMPC_MAP_unknown;
@@ -3725,12 +3906,22 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
   OpenMPDirectiveKind DKind = OMPD_unknown;
   bool ErrorFound = false;
   bool TryCaptureCXXThisMembers = false;
-  CapturedStmt *CS = nullptr;
+  llvm::SmallDenseSet<std::pair<VarDecl *, SourceLocation>, 8> CapturedVars;
+  llvm::SmallDenseSet<VarDecl *, 8> CapturedCanonDecls;
+  bool CapturedThis = false;
+  bool CheckCaptures = false;
 
-  VariableImplicitInfo ImpInfo;
+  SemaOpenMP::VariableImplicitInfo ImpInfo;
   SemaOpenMP::VarsWithInheritedDSAType VarsWithInheritedDSA;
   llvm::SmallDenseSet<const ValueDecl *, 4> ImplicitDeclarations;
 
+  void visitCaptureThisMembers(Stmt *S) {
+    bool SavedTryCaptureCXXThisMembers = TryCaptureCXXThisMembers;
+    TryCaptureCXXThisMembers = true;
+    Visit(S);
+    TryCaptureCXXThisMembers = SavedTryCaptureCXXThisMembers;
+  }
+
   void VisitSubCaptures(OMPExecutableDirective *S) {
     // Check implicitly captured variables.
     if (!S->hasAssociatedStmt() || !S->getAssociatedStmt())
@@ -3750,15 +3941,8 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
     // Try to capture inner this->member references to generate correct mappings
     // and diagnostics.
     if (TryCaptureCXXThisMembers ||
-        (isOpenMPTargetExecutionDirective(DKind) &&
-         llvm::any_of(S->getInnermostCapturedStmt()->captures(),
-                      [](const CapturedStmt::Capture &C) {
-                        return C.capturesThis();
-                      }))) {
-      bool SavedTryCaptureCXXThisMembers = TryCaptureCXXThisMembers;
-      TryCaptureCXXThisMembers = true;
-      Visit(S->getInnermostCapturedStmt()->getCapturedStmt());
-      TryCaptureCXXThisMembers = SavedTryCaptureCXXThisMembers;
+        (isOpenMPTargetExecutionDirective(DKind) && CapturedThis)) {
+      visitCaptureThisMembers(S->getInnermostCapturedStmt()->getCapturedStmt());
     }
     // In tasks firstprivates are not captured anymore, need to analyze them
     // explicitly.
@@ -3772,6 +3956,15 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
     }
   }
 
+  bool isCaptured(VarDecl *VD) const {
+    assert(CheckCaptures);
+    return CapturedCanonDecls.count(VD->getCanonicalDecl());
+  }
+
+  bool isInternal(VarDecl *VD) const {
+    return CheckCaptures && !isCaptured(VD);
+  }
+
 public:
   void VisitDeclRefExpr(DeclRefExpr *E) {
     if (TryCaptureCXXThisMembers || E->isTypeDependent() ||
@@ -3781,22 +3974,24 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
       return;
     if (auto *VD = dyn_cast<VarDecl>(E->getDecl())) {
       // Check the datasharing rules for the expressions in the clauses.
-      if (!CS || (isa<OMPCapturedExprDecl>(VD) && !CS->capturesVariable(VD) &&
-                  !Stack->getTopDSA(VD, /*FromParent=*/false).RefExpr &&
-                  !Stack->isImplicitDefaultFirstprivateFD(VD))) {
+      if (!CheckCaptures ||
+          (isa<OMPCapturedExprDecl>(VD) && isInternal(VD) &&
+           !Stack->getTopDSA(VD, /*FromParent=*/false).RefExpr &&
+           !Stack->isImplicitDefaultFirstprivateFD(VD))) {
         if (auto *CED = dyn_cast<OMPCapturedExprDecl>(VD))
           if (!CED->hasAttr<OMPCaptureNoInitAttr>()) {
             Visit(CED->getInit());
             return;
           }
-      } else if (VD->isImplicit() || isa<OMPCapturedExprDecl>(VD))
+      } else if (VD->isImplicit() || isa<OMPCapturedExprDecl>(VD)) {
         // Do not analyze internal variables and do not enclose them into
         // implicit clauses.
         if (!Stack->isImplicitDefaultFirstprivateFD(VD))
           return;
+      }
       VD = VD->getCanonicalDecl();
       // Skip internally declared variables.
-      if (VD->hasLocalStorage() && CS && !CS->capturesVariable(VD) &&
+      if (VD->hasLocalStorage() && isInternal(VD) &&
           !Stack->isImplicitDefaultFirstprivateFD(VD) &&
           !Stack->isImplicitTaskFirstprivate(VD))
         return;
@@ -3812,7 +4007,7 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
       // Skip internally declared static variables.
       std::optional<OMPDeclareTargetDeclAttr::MapTypeTy> Res =
           OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(VD);
-      if (VD->hasGlobalStorage() && CS && !CS->capturesVariable(VD) &&
+      if (VD->hasGlobalStorage() && isInternal(VD) &&
           (Stack->hasRequiresDeclWithClause<OMPUnifiedSharedMemoryClause>() ||
            !Res || *Res != OMPDeclareTargetDeclAttr::MT_Link) &&
           !Stack->isImplicitDefaultFirstprivateFD(VD) &&
@@ -3878,13 +4073,8 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
       if (SemaRef.getLangOpts().OpenMP > 50) {
         bool IsModifierPresent = Stack->getDefaultmapModifier(ClauseKind) ==
                                  OMPC_DEFAULTMAP_MODIFIER_present;
-        if (IsModifierPresent) {
-          if (!llvm::is_contained(ImpInfo.MapModifiers[ClauseKind],
-                                  OMPC_MAP_MODIFIER_present)) {
-            ImpInfo.MapModifiers[ClauseKind].push_back(
-                OMPC_MAP_MODIFIER_present);
-          }
-        }
+        if (IsModifierPresent)
+          ImpInfo.addMapModifier(OMPC_MAP_MODIFIER_present, ClauseKind);
       }
 
       if (isOpenMPTargetExecutionDirective(DKind) &&
@@ -3921,13 +4111,13 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
           IsFirstprivate =
               IsFirstprivate || (Stack->mustBeFirstprivate(ClauseKind) && !Res);
           if (IsFirstprivate) {
-            ImpInfo.Firstprivates.insert(E);
+            ImpInfo.addFirstprivate(E);
           } else {
             OpenMPDefaultmapClauseModifier M =
                 Stack->getDefaultmapModifier(ClauseKind);
             OpenMPMapClauseKind Kind = getMapClauseKindFromModifier(
                 M, ClauseKind == OMPC_DEFAULTMAP_aggregate || Res);
-            ImpInfo.Mappings[ClauseKind][Kind].insert(E);
+            ImpInfo.addMapping(E, ClauseKind, Kind);
           }
           return;
         }
@@ -3964,9 +4154,10 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
             !DVar.RefExpr)) &&
           !Stack->isLoopControlVariable(VD).first) {
         if (Stack->getDefaultDSA() == DSA_private)
-          ImpInfo.Privates.insert(E);
-        else
-          ImpInfo.Firstprivates.insert(E);
+          ImpInfo.addPrivate(E);
+        else {
+          ImpInfo.addFirstprivate(E);
+        }
         return;
       }
 
@@ -4023,7 +4214,7 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
             getVariableCategoryFromDecl(SemaRef.getLangOpts(), FD);
         OpenMPMapClauseKind Kind = getMapClauseKindFromModifier(
             Modifier, /*IsAggregateOrDeclareTarget=*/true);
-        ImpInfo.Mappings[ClauseKind][Kind].insert(E);
+        ImpInfo.addMapping(E, ClauseKind, Kind);
         return;
       }
 
@@ -4058,7 +4249,7 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
         // expression.
         // TODO: try to make it firstprivate.
         if (DVar.CKind != OMPC_unknown)
-          ImpInfo.Firstprivates.insert(E);
+          ImpInfo.addFirstprivate(E);
       }
       return;
     }
@@ -4160,41 +4351,99 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
     }
   }
 
+  void visitCapturedVariable(VarDecl *VD, SourceLocation Loc) {
+    // Do not try to map the variable if it or its sub-component was mapped
+    // already.
+    if (isOpenMPTargetExecutionDirective(DKind) &&
+        Stack->checkMappableExprComponentListsForDecl(
+            VD, /*CurrentRegionOnly=*/true,
+            [](OMPClauseMappableExprCommon::MappableExprComponentListRef,
+               OpenMPClauseKind) { return true; }))
+      return;
+    DeclRefExpr *DRE = buildDeclRefExpr(
+        SemaRef, VD, VD->getType().getNonLValueExprType(SemaRef.Context), Loc,
+        /*RefersToCapture=*/true);
+    Visit(DRE);
+  }
+
   void visitSubCaptures(CapturedStmt *S) {
     for (const CapturedStmt::Capture &Cap : S->captures()) {
       if (!Cap.capturesVariable() && !Cap.capturesVariableByCopy())
         continue;
-      VarDecl *VD = Cap.getCapturedVar();
-      // Do not try to map the variable if it or its sub-component was mapped
-      // already.
-      if (isOpenMPTargetExecutionDirective(DKind) &&
-          Stack->checkMappableExprComponentListsForDecl(
-              VD, /*CurrentRegionOnly=*/true,
-              [](OMPClauseMappableExprCommon::MappableExprComponentListRef,
-                 OpenMPClauseKind) { return true; }))
-        continue;
-      DeclRefExpr *DRE = buildDeclRefExpr(
-          SemaRef, VD, VD->getType().getNonLValueExprType(SemaRef.Context),
-          Cap.getLocation(), /*RefersToCapture=*/true);
-      Visit(DRE);
+      visitCapturedVariable(Cap.getCapturedVar(), Cap.getLocation());
     }
   }
+
+  void visitOpenCaptures() {
+    for (auto [CV, Loc] : CapturedVars)
+      visitCapturedVariable(CV, Loc);
+  }
+
   bool isErrorFound() const { return ErrorFound; }
-  const VariableImplicitInfo &getImplicitInfo() const { return ImpInfo; }
+  const SemaOpenMP::VariableImplicitInfo &getImplicitInfo() const {
+    return ImpInfo;
+  }
   const SemaOpenMP::VarsWithInheritedDSAType &getVarsWithInheritedDSA() const {
     return VarsWithInheritedDSA;
   }
 
-  DSAAttrChecker(DSAStackTy *S, Sema &SemaRef, CapturedStmt *CS)
-      : Stack(S), SemaRef(SemaRef), ErrorFound(false), CS(CS) {
-    DKind = S->getCurrentDirective();
+  void processLinkGlobals() {
     // Process declare target link variables for the target directives.
     if (isOpenMPTargetExecutionDirective(DKind)) {
       for (DeclRefExpr *E : Stack->getLinkGlobals())
         Visit(E);
     }
   }
+
+  DSAAttrChecker(DSAStackTy *S, Sema &SemaRef, CapturedStmt *CS)
+      : Stack(S), SemaRef(SemaRef), CheckCaptures(CS != nullptr) {
+    DKind = S->getCurrentDirective();
+
+    auto addCaptures = [&](CapturedStmt *C) {
+      for (CapturedStmt::Capture &Cap : C->captures()) {
+        if (Cap.capturesVariable() || Cap.capturesVariableByCopy()) {
+          VarDecl *VD = Cap.getCapturedVar();
+          CapturedVars.insert({VD, Cap.getLocation()});
+          CapturedCanonDecls.insert(VD->getCanonicalDecl());
+        } else if (Cap.capturesThis()) {
+          CapturedThis = true;
+        }
+      }
+    };
+
+    while (auto *C = dyn_cast_or_null<CapturedStmt>(CS)) {
+      addCaptures(C);
+      CS = dyn_cast<CapturedStmt>(C->getCapturedStmt());
+    }
+    // alternative:
+    // if (CS) addCaptures(CS);
+    processLinkGlobals();
+  }
+
+  struct UseScopes {};
+  // RegionIndex: innermost == 0
+  DSAAttrChecker(DSAStackTy *S, Sema &SemaRef, size_t RegionIndex, UseScopes)
+      : Stack(S), SemaRef(SemaRef), CheckCaptures(true) {
+    DKind = S->getCurrentDirective();
+    ArrayRef<FunctionScopeInfo *> Scopes = SemaRef.getFunctionScopes();
+    assert(RegionIndex < Scopes.size());
+
+    FunctionScopeInfo *FSI = Scopes[Scopes.size() - RegionIndex - 1];
+    for (sema::Capture &Cap : cast<CapturingScopeInfo>(FSI)->Captures) {
+      if (Cap.isThisCapture())
+        CapturedThis = true;
+      if (!Cap.isVariableCapture())
+        continue;
+      if (auto *VD = dyn_cast<VarDecl>(Cap.getVariable())) {
+        CapturedVars.insert({VD, Cap.getLocation()});
+        CapturedCanonDecls.insert(VD->getCanonicalDecl());
+      }
+    }
+
+    processLinkGlobals();
+  }
 };
+
 } // namespace
 
 static void handleDeclareVariantConstructTrait(DSAStackTy *Stack,
@@ -4324,7 +4573,6 @@ static void processCapturedRegions(Sema &SemaRef, OpenMPDirectiveKind DKind,
                                    Scope *CurScope, SourceLocation Loc) {
   SmallVector<OpenMPDirectiveKind> Regions;
   getOpenMPCaptureRegions(Regions, DKind);
-
   bool LoopBoundSharing = isOpenMPLoopBoundSharingDirective(DKind);
 
   auto MarkAsInlined = [&](CapturedRegionScopeInfo *CSI) {
@@ -4365,7 +4613,7 @@ static void processCapturedRegions(Sema &SemaRef, OpenMPDirectiveKind DKind,
       break;
     case OMPD_unknown:
       SemaRef.ActOnCapturedRegionStart(Loc, CurScope, CR_OpenMP,
-                                       getUnknownRegionParams(SemaRef));
+                                       getUnknownRegionParams(SemaRef), Level);
       break;
     case OMPD_metadirective:
     case OMPD_nothing:
@@ -4376,7 +4624,11 @@ static void processCapturedRegions(Sema &SemaRef, OpenMPDirectiveKind DKind,
 }
 
 void SemaOpenMP::ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind,
-                                        Scope *CurScope) {
+                                        Scope *CurScope,
+                                        bool HasAssociatedStmt) {
+  if (!HasAssociatedStmt)
+    return;
+
   switch (DKind) {
   case OMPD_atomic:
   case OMPD_critical:
@@ -4409,10 +4661,10 @@ int SemaOpenMP::getOpenMPCaptureLevels(OpenMPDirectiveKind DKind) {
   return CaptureRegions.size();
 }
 
-static OMPCapturedExprDecl *buildCaptureDecl(Sema &S, IdentifierInfo *Id,
-                                             Expr *CaptureExpr, bool WithInit,
-                                             DeclContext *CurContext,
-                                             bool AsExpression) {
+static VarDecl *buildCaptureDecl(Sema &S, IdentifierInfo *Id, Expr *CaptureExpr,
+                                 bool WithInit, DeclContext *CurContext,
+                                 bool AsExpression,
+                                 bool MakeOMPCapturedExprDecl) {
   assert(CaptureExpr);
   ASTContext &C = S.getASTContext();
   Expr *Init = AsExpression ? CaptureExpr : CaptureExpr->IgnoreImpCasts();
@@ -4430,38 +4682,49 @@ static OMPCapturedExprDecl *buildCaptureDecl(Sema &S, IdentifierInfo *Id,
     }
     WithInit = true;
   }
-  auto *CED = OMPCapturedExprDecl::Create(C, CurContext, Id, Ty,
-                                          CaptureExpr->getBeginLoc());
-  if (!WithInit)
-    CED->addAttr(OMPCaptureNoInitAttr::CreateImplicit(C));
-  CurContext->addHiddenDecl(CED);
+
+  VarDecl *VD;
+  if (MakeOMPCapturedExprDecl) {
+    VD = OMPCapturedExprDecl::Create(C, CurContext, Id, Ty,
+                                     CaptureExpr->getBeginLoc());
+    if (!WithInit)
+      VD->addAttr(OMPCaptureNoInitAttr::CreateImplicit(C));
+  } else {
+    SourceLocation Loc = CaptureExpr->getBeginLoc();
+    VD = VarDecl::Create(C, CurContext, Loc, Loc, Id, Ty,
+                         C.getTrivialTypeSourceInfo(Ty), SC_None);
+  }
+
+  CurContext->addHiddenDecl(VD);
   Sema::TentativeAnalysisScope Trap(S);
-  S.AddInitializerToDecl(CED, Init, /*DirectInit=*/false);
-  return CED;
+  S.AddInitializerToDecl(VD, Init, /*DirectInit=*/false);
+  return VD;
 }
 
 static DeclRefExpr *buildCapture(Sema &S, ValueDecl *D, Expr *CaptureExpr,
                                  bool WithInit) {
-  OMPCapturedExprDecl *CD;
+  VarDecl *CD;
   if (VarDecl *VD = S.OpenMP().isOpenMPCapturedDecl(D))
     CD = cast<OMPCapturedExprDecl>(VD);
   else
     CD = buildCaptureDecl(S, D->getIdentifier(), CaptureExpr, WithInit,
-                          S.CurContext,
-                          /*AsExpression=*/false);
+                          S.CurContext, /*AsExpression=*/false,
+                          /*MakeOMPCapturedExprDecl=*/true);
   return buildDeclRefExpr(S, CD, CD->getType().getNonReferenceType(),
                           CaptureExpr->getExprLoc());
 }
 
 static ExprResult buildCapture(Sema &S, Expr *CaptureExpr, DeclRefExpr *&Ref,
-                               StringRef Name) {
+                               bool RefersToCapture, StringRef Name,
+                               bool MakeOMPCapturedExprDecl = true) {
   CaptureExpr = S.DefaultLvalueConversion(CaptureExpr).get();
   if (!Ref) {
-    OMPCapturedExprDecl *CD = buildCaptureDecl(
-        S, &S.getASTContext().Idents.get(Name), CaptureExpr,
-        /*WithInit=*/true, S.CurContext, /*AsExpression=*/true);
+    VarDecl *CD =
+        buildCaptureDecl(S, &S.getASTContext().Idents.get(Name), CaptureExpr,
+                         /*WithInit=*/true, S.CurContext, /*AsExpression=*/true,
+                         MakeOMPCapturedExprDecl);
     Ref = buildDeclRefExpr(S, CD, CD->getType().getNonReferenceType(),
-                           CaptureExpr->getExprLoc());
+                           CaptureExpr->getExprLoc(), RefersToCapture);
   }
   ExprResult Res = Ref;
   if (!S.getLangOpts().CPlusPlus &&
@@ -4573,62 +4836,30 @@ static bool checkOrderedOrderSpecified(Sema &S,
   return false;
 }
 
-StmtResult SemaOpenMP::ActOnOpenMPRegionEnd(StmtResult S,
-                                            ArrayRef<OMPClause *> Clauses) {
-  handleDeclareVariantConstructTrait(DSAStack, DSAStack->getCurrentDirective(),
-                                     /*ScopeEntry=*/false);
-  if (!isOpenMPCapturingDirective(DSAStack->getCurrentDirective()))
-    return S;
+static void
+getImplicitInfoFromClauses(Sema &SemaRef, OpenMPDirectiveKind DKind,
+                           ArrayRef<OMPClause *> Clauses,
+                           SemaOpenMP::VariableImplicitInfo &ImpInfo);
 
+static OMPClause *
+createImplicitClause(Sema &SemaRef, OpenMPDirectiveKind LeafKind,
+                     DSAStackTy *Stack,
+                     const SemaOpenMP::VariableImplicitInfo &ImpInfo,
+                     const omp::Clause &ExtClause);
+
+static OMPClause *processImplicitMapWithDefaultMappers(Sema &S,
+                                                       DSAStackTy *Stack,
+                                                       OMPClause *Clause);
+
+static bool checkOrderedClause(Sema &SemaRef, OpenMPDirectiveKind DKind,
+                               ArrayRef<OMPClause *> Clauses) {
+  // Diagnose violations of clause restrictions
   bool ErrorFound = false;
-  CaptureRegionUnwinderRAII CaptureRegionUnwinder(
-      SemaRef, ErrorFound, DSAStack->getCurrentDirective());
-  if (!S.isUsable()) {
-    ErrorFound = true;
-    return StmtError();
-  }
 
-  SmallVector<OpenMPDirectiveKind, 4> CaptureRegions;
-  getOpenMPCaptureRegions(CaptureRegions, DSAStack->getCurrentDirective());
   OMPOrderedClause *OC = nullptr;
   OMPScheduleClause *SC = nullptr;
   SmallVector<const OMPLinearClause *, 4> LCs;
-  SmallVector<const OMPClauseWithPreInit *, 4> PICs;
-  // This is required for proper codegen.
   for (OMPClause *Clause : Clauses) {
-    if (!getLangOpts().OpenMPSimd &&
-        (isOpenMPTaskingDirective(DSAStack->getCurrentDirective()) ||
-         DSAStack->getCurrentDirective() == OMPD_target) &&
-        Clause->getClauseKind() == OMPC_in_reduction) {
-      // Capture taskgroup task_reduction descriptors inside the tasking regions
-      // with the corresponding in_reduction items.
-      auto *IRC = cast<OMPInReductionClause>(Clause);
-      for (Expr *E : IRC->taskgroup_descriptors())
-        if (E)
-          SemaRef.MarkDeclarationsReferencedInExpr(E);
-    }
-    if (isOpenMPPrivate(Clause->getClauseKind()) ||
-        Clause->getClauseKind() == OMPC_copyprivate ||
-        (getLangOpts().OpenMPUseTLS &&
-         getASTContext().getTargetInfo().isTLSSupported() &&
-         Clause->getClauseKind() == OMPC_copyin)) {
-      DSAStack->setForceVarCapturing(Clause->getClauseKind() == OMPC_copyin);
-      // Mark all variables in private list clauses as used in inner region.
-      for (Stmt *VarRef : Clause->children()) {
-        if (auto *E = cast_or_null<Expr>(VarRef)) {
-          SemaRef.MarkDeclarationsReferencedInExpr(E);
-        }
-      }
-      DSAStack->setForceVarCapturing(/*V=*/false);
-    } else if (CaptureRegions.size() > 1 ||
-               CaptureRegions.back() != OMPD_unknown) {
-      if (auto *C = OMPClauseWithPreInit::get(Clause))
-        PICs.push_back(C);
-      if (auto *C = OMPClauseWithPostUpdate::get(Clause)) {
-        if (Expr *E = C->getPostUpdateExpr())
-          SemaRef.MarkDeclarationsReferencedInExpr(E);
-      }
-    }
     if (Clause->getClauseKind() == OMPC_schedule)
       SC = cast<OMPScheduleClause>(Clause);
     else if (Clause->getClauseKind() == OMPC_ordered)
@@ -4636,9 +4867,6 @@ StmtResult SemaOpenMP::ActOnOpenMPRegionEnd(StmtResult S,
     else if (Clause->getClauseKind() == OMPC_linear)
       LCs.push_back(cast<OMPLinearClause>(Clause));
   }
-  // Capture allocator expressions if used.
-  for (Expr *E : DSAStack->getInnerAllocators())
-    SemaRef.MarkDeclarationsReferencedInExpr(E);
   // OpenMP, 2.7.1 Loop Construct, Restrictions
   // The nonmonotonic modifier cannot be specified if an ordered clause is
   // specified.
@@ -4647,10 +4875,11 @@ StmtResult SemaOpenMP::ActOnOpenMPRegionEnd(StmtResult S,
        SC->getSecondScheduleModifier() ==
            OMPC_SCHEDULE_MODIFIER_nonmonotonic) &&
       OC) {
-    Diag(SC->getFirstScheduleModifier() == OMPC_SCHEDULE_MODIFIER_nonmonotonic
-             ? SC->getFirstScheduleModifierLoc()
-             : SC->getSecondScheduleModifierLoc(),
-         diag::err_omp_simple_clause_incompatible_with_ordered)
+    SemaRef.Diag(SC->getFirstScheduleModifier() ==
+                         OMPC_SCHEDULE_MODIFIER_nonmonotonic
+                     ? SC->getFirstScheduleModifierLoc()
+                     : SC->getSecondScheduleModifierLoc(),
+                 diag::err_omp_simple_clause_incompatible_with_ordered)
         << getOpenMPClauseName(OMPC_schedule)
         << getOpenMPSimpleClauseTypeName(OMPC_schedule,
                                          OMPC_SCHEDULE_MODIFIER_nonmonotonic)
@@ -4658,44 +4887,222 @@ StmtResult SemaOpenMP::ActOnOpenMPRegionEnd(StmtResult S,
     ErrorFound = true;
   }
   // OpenMP 5.0, 2.9.2 Worksharing-Loop Construct, Restrictions.
-  // If an order(concurrent) clause is present, an ordered clause may not appear
-  // on the same directive.
+  // If an order(concurrent) clause is present, an ordered clause may not
+  // appear on the same directive.
   if (checkOrderedOrderSpecified(SemaRef, Clauses))
     ErrorFound = true;
   if (!LCs.empty() && OC && OC->getNumForLoops()) {
     for (const OMPLinearClause *C : LCs) {
-      Diag(C->getBeginLoc(), diag::err_omp_linear_ordered)
+      SemaRef.Diag(C->getBeginLoc(), diag::err_omp_linear_ordered)
           << SourceRange(OC->getBeginLoc(), OC->getEndLoc());
     }
     ErrorFound = true;
   }
-  if (isOpenMPWorksharingDirective(DSAStack->getCurrentDirective()) &&
-      isOpenMPSimdDirective(DSAStack->getCurrentDirective()) && OC &&
-      OC->getNumForLoops()) {
-    Diag(OC->getBeginLoc(), diag::err_omp_ordered_simd)
-        << getOpenMPDirectiveName(DSAStack->getCurrentDirective());
+  if (isOpenMPWorksharingDirective(DKind) && isOpenMPSimdDirective(DKind) &&
+      OC && OC->getNumForLoops()) {
+    SemaRef.Diag(OC->getBeginLoc(), diag::err_omp_ordered_simd)
+        << getOpenMPDirectiveName(DKind);
     ErrorFound = true;
   }
-  if (ErrorFound) {
-    return StmtError();
+
+  return ErrorFound;
+}
+
+// The ImpInfo will be used to create omp::Clause's, but it also contains
+// additional info, like source locations.
+struct VariableImplicitInfoQueues : public SemaOpenMP::VariableImplicitInfo {
+  VariableImplicitInfoQueues() {
+    PrivatesQ = Privates;
+    FirstprivatesQ = Firstprivates;
+    for (size_t I = 0; I != DefaultmapKindNum; ++I) {
+      for (size_t J = 0; J != MapKindNum; ++J)
+        MappingsQ[I][J] = Mappings[I][J];
+      MapModifiersQ[I] = MapModifiers[I];
+    }
+    ReductionMappingsQ = ReductionMappings;
   }
-  StmtResult SR = S;
-  unsigned CompletedRegions = 0;
-  for (OpenMPDirectiveKind ThisCaptureRegion : llvm::reverse(CaptureRegions)) {
+
+  using MappingTy = std::remove_reference_t<decltype(Mappings[0][0])>;
+  using MapModifierTy = std::remove_reference_t<decltype(MapModifiers[0])>;
+  QueueAdapter<decltype(Privates)> PrivatesQ;
+  QueueAdapter<decltype(Firstprivates)> FirstprivatesQ;
+  QueueAdapter<MappingTy> MappingsQ[DefaultmapKindNum][MapKindNum];
+  QueueAdapter<MapModifierTy> MapModifiersQ[DefaultmapKindNum];
+  QueueAdapter<decltype(ReductionMappings)> ReductionMappingsQ;
+};
+
+static void generateExtClauseForBind(Sema &SemaRef, DSAStackTy *Stack,
+                                     ArrayRef<OMPClause *> Clauses,
+                                     SmallVectorImpl<omp::Clause> &ExtClauses) {
+  OpenMPDirectiveKind DKind = Stack->getCurrentDirective();
+  unsigned OpenMPVersion = SemaRef.getLangOpts().OpenMP;
+  if (OpenMPVersion < 50 || !isOpenMPGenericLoopDirective(DKind))
+    return;
+
+  auto IsBind = [](OMPClause *C) { return C->getClauseKind() == OMPC_bind; };
+
+  auto F = llvm::find_if(Clauses, IsBind);
+  if (F != Clauses.end()) {
+    // Bind clause is already present.
+    return;
+  }
+
+  // Bind clause not found, infer the binding type from the constituent
+  // directives.
+  // [5.0:129:25-28] If the bind clause is not present on the construct and
+  // the loop construct is closely nested inside a teams or parallel
+  // construct, the binding region is the corresponding teams or parallel
+  // region. If none of those conditions hold, the binding region is not
+  // defined.
+  auto determineBinding = [&](OpenMPDirectiveKind DFrom) {
+    OpenMPBindClauseKind BindKind = OMPC_BIND_unknown;
+    ArrayRef<OpenMPDirectiveKind> Leafs = getLeafConstructsOrSelf(DFrom);
+
+    for (OpenMPDirectiveKind L : llvm::reverse(Leafs)) {
+      if (L == OMPD_parallel)
+        BindKind = OMPC_BIND_parallel;
+      else if (L == OMPD_teams)
+        BindKind = OMPC_BIND_teams;
+      else if (L == OMPD_simd)
+        BindKind = OMPC_BIND_thread;
+      if (BindKind != OMPC_BIND_unknown)
+        break;
+    }
+
+    return BindKind;
+  };
+
+  // "Kind" may be a compound directive that contains "loop" as a leaf.
+  // Try to determine the binding from it first, then from the parent.
+  OpenMPBindClauseKind BindKind = determineBinding(DKind);
+  if (BindKind == OMPC_BIND_unknown)
+    BindKind = determineBinding(Stack->getParentDirective());
+
+  // Default bind(thread) if binding is unknown.
+  if (BindKind == OMPC_BIND_unknown) {
+    SemaRef.Diag(Stack->getDefaultDSALocation(),
+                 diag::err_omp_bind_required_on_loop);
+    BindKind = OMPC_BIND_thread;
+  }
+
+  using TagType = omp::Clause::TagType;
+  assert(BindKind != OMPC_BIND_unknown && "Binding must be known");
+
+  auto C = omp::clause::Bind{/*Binding=*/*ext::conv(BindKind)};
+  ExtClauses.push_back(omp::Clause{{OMPC_bind, C}, TagType::get()});
+}
+
+static void
+generateExtClausesFromImplicit(VariableImplicitInfoQueues &ImpInfoQs,
+                               SmallVectorImpl<omp::Clause> &ExtClauses) {
+  using TagType = omp::Clause::TagType;
+
+  // The compound directive splitting will assign clauses to leaf constructs
+  // based only on the OpenMP rules. For example, in
+  //   #pragma omp target parallel num_threads(n + k)
+  //     {...use(k)...}
+  // both n and k may end up being implicitly firstprivate. The OpenMP rules
+  // will dictate that the firstprivate clause applies to both `target`, and
+  // `parallel`. The problem is that the parallel region does not use n,
+  // and the codegen for firstprivate(n) will try to insert copy-in for it
+  // into the region. This will fail, since the original n is not available
+  // there.
+  //
+  // Create individual clauses for each variable. This will allow to filter
+  // out the unnecessary ones, where the variable is not captured in the
+  // corresponding region.
+
+  while (!ImpInfoQs.PrivatesQ.empty()) {
+    Expr *E = ImpInfoQs.PrivatesQ.take();
+    auto C = ext::clause::Private{/*List=*/{ext::makeObject(E)}};
+    ExtClauses.push_back(ext::Clause{{OMPC_private, C}, TagType::get()});
+  }
+
+  while (!ImpInfoQs.FirstprivatesQ.empty()) {
+    Expr *E = ImpInfoQs.FirstprivatesQ.take();
+    auto C = ext::clause::Firstprivate{/*List=*/{ext::makeObject(E)}};
+    ExtClauses.push_back(ext::Clause{{OMPC_firstprivate, C}, TagType::get()});
+  }
+
+  for (size_t I = 0; I != ImpInfoQs.DefaultmapKindNum; ++I) {
+    ext::clause::Map::MapTypeModifiers MapMods;
+
+    while (!ImpInfoQs.MapModifiersQ[I].empty()) {
+      OpenMPMapModifierKind M = ImpInfoQs.MapModifiersQ[I].take();
+      if (auto MaybeMod = ext::conv(M))
+        MapMods.push_back(*MaybeMod);
+    }
+
+    for (size_t J = 0; J != ImpInfoQs.MapKindNum; ++J) {
+      auto &MappingsQ = ImpInfoQs.MappingsQ[I][J];
+      ArrayRef<Expr *> Tail = MappingsQ.takeAll();
+      if (Tail.empty())
+        continue;
+
+      auto MaybeType = ext::conv(static_cast<OpenMPMapClauseKind>(J));
+      auto C =
+          ext::clause::Map{{/*MapType=*/MaybeType, /*MapTypeModifiers=*/MapMods,
+                            /*Mappers=*/std::nullopt, /*Iterator=*/std::nullopt,
+                            /*LocatorList=*/ext::makeObjects(Tail)}};
+      ExtClauses.push_back(ext::Clause{{OMPC_map, C}, TagType::get(I, J)});
+    }
+  }
+
+  // Do all reduction mappings in a single clause.
+  if (!ImpInfoQs.ReductionMappingsQ.empty()) {
+    ArrayRef<Expr *> Tail = ImpInfoQs.ReductionMappingsQ.takeAll();
+    auto RedMapC =
+        ext::clause::Map{{/*MapType=*/ext::clause::Map::MapType::Tofrom,
+                          /*MapTypeModifiers=*/std::nullopt,
+                          /*Mappers=*/std::nullopt, /*Iterator=*/std::nullopt,
+                          /*LocatorList=*/ext::makeObjects(Tail)}};
+    ExtClauses.push_back(ext::Clause{{OMPC_map, RedMapC}, TagType::get()});
+  }
+}
+
+static void captureObjectsInClause(Sema &SemaRef, DSAStackTy *Stack,
+                                   OpenMPDirectiveKind DKind,
+                                   OMPClause *Clause) {
+  OpenMPClauseKind CKind = Clause->getClauseKind();
+  if (!SemaRef.getLangOpts().OpenMPSimd &&
+      (isOpenMPTaskingDirective(DKind) || DKind == OMPD_target) &&
+      CKind == OMPC_in_reduction) {
+    // Capture taskgroup task_reduction descriptors inside the tasking
+    // regions with the corresponding in_reduction items.
+    auto *IRC = cast<OMPInReductionClause>(Clause);
+    for (Expr *E : IRC->taskgroup_descriptors())
+      if (E)
+        SemaRef.MarkDeclarationsReferencedInExpr(E);
+  }
+  if (isOpenMPPrivate(CKind) || CKind == OMPC_copyprivate ||
+      (SemaRef.getLangOpts().OpenMPUseTLS &&
+       SemaRef.getASTContext().getTargetInfo().isTLSSupported() &&
+       CKind == OMPC_copyin)) {
+    Stack->setForceVarCapturing(CKind == OMPC_copyin);
     // Mark all variables in private list clauses as used in inner region.
-    // Required for proper codegen of combined directives.
-    // TODO: add processing for other clauses.
-    if (ThisCaptureRegion != OMPD_unknown) {
-      for (const clang::OMPClauseWithPreInit *C : PICs) {
-        OpenMPDirectiveKind CaptureRegion = C->getCaptureRegion();
-        // Find the particular capture region for the clause if the
-        // directive is a combined one with multiple capture regions.
-        // If the directive is not a combined one, the capture region
-        // associated with the clause is OMPD_unknown and is generated
-        // only once.
-        if (CaptureRegion == ThisCaptureRegion ||
-            CaptureRegion == OMPD_unknown) {
-          if (auto *DS = cast_or_null<DeclStmt>(C->getPreInitStmt())) {
+    for (Stmt *VarRef : Clause->children()) {
+      if (auto *E = cast_or_null<Expr>(VarRef))
+        SemaRef.MarkDeclarationsReferencedInExpr(E);
+    }
+    Stack->setForceVarCapturing(/*V=*/false);
+  } else {
+    if (auto *PIC = OMPClauseWithPreInit::get(Clause)) {
+      assert(!PIC->getPreInitStmt() && "PreInit stmt no longer expected");
+      if (CKind != OMPC_num_teams && CKind != OMPC_thread_limit) {
+        // This used to be done indirectly by creating the captured
+        // expression for the pre-init.
+        for (auto *Ch : Clause->children()) {
+          if (auto *E = dyn_cast_or_null<Expr>(Ch))
+            SemaRef.MarkDeclarationsReferencedInExpr(E);
+        }
+      } else {
+        // Either num_teams or thread_limit.
+        assert(CKind == OMPC_num_teams || CKind == OMPC_thread_limit);
+        auto &PreInits = Stack->getPreInits();
+        for (auto [C, P] : PreInits) {
+          if (C != Clause)
+            continue;
+          if (auto *DS = cast_or_null<DeclStmt>(P)) {
             for (Decl *D : DS->decls())
               SemaRef.MarkVariableReferenced(D->getLocation(),
                                              cast<VarDecl>(D));
@@ -4703,42 +5110,223 @@ StmtResult SemaOpenMP::ActOnOpenMPRegionEnd(StmtResult S,
         }
       }
     }
-    if (ThisCaptureRegion == OMPD_target) {
-      // Capture allocator traits in the target region. They are used implicitly
-      // and, thus, are not captured by default.
-      for (OMPClause *C : Clauses) {
-        if (const auto *UAC = dyn_cast<OMPUsesAllocatorsClause>(C)) {
-          for (unsigned I = 0, End = UAC->getNumberOfAllocators(); I < End;
-               ++I) {
-            OMPUsesAllocatorsClause::Data D = UAC->getAllocatorData(I);
-            if (Expr *E = D.AllocatorTraits)
-              SemaRef.MarkDeclarationsReferencedInExpr(E);
-          }
-          continue;
-        }
-      }
+    if (auto *PUC = OMPClauseWithPostUpdate::get(Clause)) {
+      if (Expr *E = PUC->getPostUpdateExpr())
+        SemaRef.MarkDeclarationsReferencedInExpr(E);
     }
-    if (ThisCaptureRegion == OMPD_parallel) {
-      // Capture temp arrays for inscan reductions and locals in aligned
-      // clauses.
-      for (OMPClause *C : Clauses) {
-        if (auto *RC = dyn_cast<OMPReductionClause>(C)) {
-          if (RC->getModifier() != OMPC_REDUCTION_inscan)
-            continue;
-          for (Expr *E : RC->copy_array_temps())
-            if (E)
-              SemaRef.MarkDeclarationsReferencedInExpr(E);
-        }
-        if (auto *AC = dyn_cast<OMPAlignedClause>(C)) {
-          for (Expr *E : AC->varlist())
-            SemaRef.MarkDeclarationsReferencedInExpr(E);
+  }
+
+  if (auto *Alloc = dyn_cast<OMPAllocateClause>(Clause)) {
+    if (Expr *E = Alloc->getAllocator())
+      SemaRef.MarkDeclarationsReferencedInExpr(E);
+  } else if (auto *Aligned = dyn_cast<OMPAlignedClause>(Clause)) {
+    // The children of "aligned" don't seem to be visited anywhere.
+    // Is this an isolated case, or do we need to visit more clauses here?
+    if (Expr *E = Aligned->getAlignment())
+      SemaRef.MarkDeclarationsReferencedInExpr(E);
+    for (Stmt *VarRef : Aligned->children()) {
+      if (Expr *E = dyn_cast<Expr>(VarRef))
+        SemaRef.MarkDeclarationsReferencedInExpr(E);
+    }
+  } else if (const auto *UAC = dyn_cast<OMPUsesAllocatorsClause>(Clause)) {
+    // Capture allocator traits in the target region. They are used
+    // implicitly and, thus, are not captured by default.
+    for (unsigned I = 0, N = UAC->getNumberOfAllocators(); I < N; ++I) {
+      OMPUsesAllocatorsClause::Data D = UAC->getAllocatorData(I);
+      if (Expr *E = D.AllocatorTraits)
+        SemaRef.MarkDeclarationsReferencedInExpr(E);
+    }
+  }
+}
+
+StmtResult SemaOpenMP::ActOnOpenMPRegionEnd(StmtResult S,
+                                            ArrayRef<OMPClause *> Clauses) {
+  OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
+  if (!S.isUnset())
+    handleDeclareVariantConstructTrait(DSAStack, DKind, /*ScopeEntry=*/false);
+  StmtResult SR = S;
+  bool IsCapturing =
+      isOpenMPCapturingDirective(DKind, /*OrderedIsStandalone=*/S.isUnset());
+  bool IsTemplate = SemaRef.CurContext->isDependentContext();
+  unsigned OpenMPVersion = SemaRef.getLangOpts().OpenMP;
+
+  SemaOpenMP::VarsWithInheritedDSAType VarsWithInheritedDSA;
+  VariableImplicitInfoQueues ImpInfoQs;
+
+  SmallVector<OpenMPDirectiveKind, 4> CaptureRegions;
+  if (IsCapturing)
+    getOpenMPCaptureRegions(CaptureRegions, DKind);
+  size_t NumCaptureRegions = CaptureRegions.size();
+  size_t RemainingCaptureRegions = NumCaptureRegions;
+
+  bool ErrorFound = false;
+
+  auto makeOriginal = [&] {
+    UnitConstruct Original;
+    Original.DKind = DKind;
+    Original.Clauses.assign(Clauses.begin(), Clauses.end());
+    return Original;
+  };
+
+  if (S.isInvalid() && !IsCapturing)
+    return StmtError();
+
+  CaptureRegionUnwinderRAII CaptureRegionUnwinder(SemaRef, ErrorFound, DKind);
+  if (S.isInvalid()) {
+    ErrorFound = true;
+    return StmtError();
+  }
+
+  if (IsCapturing && !IsTemplate && SR.isUsable()) {
+    Stmt *T = SR.get();
+    for (size_t I = 0; I != NumCaptureRegions - RemainingCaptureRegions; ++I)
+      T = cast<CapturedStmt>(T)->getCapturedStmt();
+    DSAAttrChecker DSAChecker(DSAStack, SemaRef, /*RegionIndex=*/0,
+                              DSAAttrChecker::UseScopes{});
+    DSAChecker.Visit(T);
+    DSAChecker.visitOpenCaptures();
+
+    DSAStack->getTopOfStack().HasDSAError |= DSAChecker.isErrorFound();
+    ImpInfoQs.include(DSAChecker.getImplicitInfo());
+    getImplicitInfoFromClauses(SemaRef, DKind, Clauses, ImpInfoQs);
+    for (auto [Decl, Expr] : DSAChecker.getVarsWithInheritedDSA())
+      VarsWithInheritedDSA.insert(std::make_pair(Decl, Expr));
+  }
+
+  // Create the initial set of clauses
+  llvm::SmallVector<omp::Clause> ExtClauses;
+  if (!IsTemplate)
+    llvm::append_range(ExtClauses, ext::makeClauses(Clauses));
+
+  ErrorFound = checkOrderedClause(SemaRef, DKind, Clauses) || ErrorFound;
+  if (ErrorFound)
+    return StmtError();
+
+  generateExtClauseForBind(SemaRef, DSAStack, Clauses, ExtClauses);
+
+  // Convert unprocessed implicit info to ext clauses.
+  generateExtClausesFromImplicit(ImpInfoQs, ExtClauses);
+
+  auto &UnitConstructs = DSAStack->getTopOfStack().UnitConstructs;
+  std::unique_ptr<ExtConstructDecomposition> Splitter;
+
+  auto AppendImplicitClause = [&](UnitConstruct &Unit, const ext::Clause &EC) {
+    OMPClause *IC =
+        createImplicitClause(SemaRef, Unit.DKind, DSAStack, ImpInfoQs, EC);
+    Unit.Clauses.push_back(IC);
+    OMPClause *DM = processImplicitMapWithDefaultMappers(SemaRef, DSAStack, IC);
+    if (DM)
+      Unit.Clauses.push_back(DM);
+  };
+
+  if (!IsTemplate) {
+    Splitter = std::make_unique<ExtConstructDecomposition>(DKind, ExtClauses,
+                                                           OpenMPVersion);
+
+    // Transfer the initial breakdown of the original directive into
+    // UnitConstructs.
+    for (ExtConstructDecomposition::ExtConstruct &Con : Splitter->Output) {
+      UnitConstruct Unit{Con.DKind};
+
+      while (!Con.ClausesQ.empty()) {
+        const ext::Clause &EC = Con.ClausesQ.take();
+        if (EC.tag.getFlags() == EC.tag.Explicit) {
+          if (auto *P = static_cast<OMPClause *>(EC.tag.getPointer()))
+            Unit.Clauses.push_back(P);
+          else
+            AppendImplicitClause(Unit, EC);
+        } else {
+          // Delay creating these (because "task")
+          Unit.Pending.push_back(std::move(EC));
         }
       }
+
+      UnitConstructs.push_back(std::move(Unit));
     }
-    if (++CompletedRegions == CaptureRegions.size())
-      DSAStack->setBodyComplete();
-    SR = SemaRef.ActOnCapturedRegionEnd(SR.get());
   }
+
+  if (!Splitter || Splitter->Output.empty()) {
+    // This is intended to cover the case of IsTemplate being true,
+    // and the case when splitting failed. In both cases, treat the
+    // original directive with explicit clauses as the only unit.
+    assert(IsTemplate == !Splitter);
+    assert(!IsTemplate || ImpInfoQs.empty());
+    assert(UnitConstructs.empty());
+    UnitConstructs.push_back(makeOriginal());
+  }
+
+  auto CreatePendingClauses = [&](UnitConstruct &Unit) {
+    for (ext::Clause &EC : Unit.Pending)
+      AppendImplicitClause(Unit, EC);
+    Unit.Pending.clear();
+  };
+
+  for (size_t ConIdx = UnitConstructs.size(); ConIdx != 0; --ConIdx) {
+    UnitConstruct &Unit = UnitConstructs[ConIdx - 1];
+    // If the unit directive is not task, create AST nodes for the pending
+    // clauses early on. This will potentially capture variables from these
+    // clauses, somrthing that should be avoided for task.
+    if (Unit.DKind != OMPD_task)
+      CreatePendingClauses(Unit);
+    if (!isOpenMPCapturingDirective(Unit.DKind,
+                                    /*OrderedIsStandalone=*/S.isUnset())) {
+      continue;
+    }
+    DSAAttrChecker PerUnitChecker(DSAStack, SemaRef, /*RegionIndex=*/0,
+                                  DSAAttrChecker::UseScopes{});
+
+    SmallVector<OpenMPDirectiveKind, 4> Regions;
+    getOpenMPCaptureRegions(Regions, Unit.DKind);
+
+    for (OMPClause *Clause : Unit.Clauses)
+      captureObjectsInClause(SemaRef, DSAStack, Unit.DKind, Clause);
+
+    OpenMPDirectiveKind PrevRegion = OMPD_unknown;
+    for (OpenMPDirectiveKind ThisRegion : llvm::reverse(Regions)) {
+      if (ThisRegion != OMPD_task || PrevRegion != OMPD_target) {
+        PerUnitChecker.visitOpenCaptures();
+        for (auto [Decl, Expr] : PerUnitChecker.getVarsWithInheritedDSA())
+          VarsWithInheritedDSA.insert(std::make_pair(Decl, Expr));
+      }
+
+      PrevRegion = ThisRegion;
+      assert(RemainingCaptureRegions > 0 && "Processed too many regions");
+      --RemainingCaptureRegions;
+      SR = SemaRef.ActOnCapturedRegionEnd(SR.get());
+    } // for (ThisRegion)
+
+    // Create implicit clauses after closing the regions to avoid creating
+    // unwanted captures for variables referenced in these clauses.
+    if (Unit.DKind == OMPD_task)
+      CreatePendingClauses(Unit);
+
+    if (IsTemplate)
+      continue;
+
+    // Make ext clauses from the implicit info from PerUnitChecker.
+    ImpInfoQs.include(PerUnitChecker.getImplicitInfo());
+    DSAStack->getTopOfStack().HasDSAError |= PerUnitChecker.isErrorFound();
+
+    QueueAdapter ExtClausesQ(ExtClauses);
+    generateExtClausesFromImplicit(ImpInfoQs, ExtClauses);
+
+    // Apply the clauses created above.
+    while (!ExtClausesQ.empty()) {
+      omp::Clause &EC = ExtClausesQ.take();
+      Splitter->postApply(EC, nullptr); // Ignore result.
+      // Extract the newly added clauses from Splitter.Output, and append them
+      // to Unit.Pending for ExtConstructs that haven't been processed yet.
+      for (size_t Idx = 0; Idx < ConIdx - 1; ++Idx) {
+        auto &Queue = Splitter->Output[Idx].ClausesQ;
+        llvm::append_range(UnitConstructs[Idx].Pending, Queue.takeAll());
+      }
+    }
+  } // for (ConIdx)
+
+  if (RemainingCaptureRegions == 0)
+    DSAStack->setBodyComplete();
+
+  DSAStack->getTopOfStack().VarsWithInheritedDSA = VarsWithInheritedDSA;
   return SR;
 }
 
@@ -5757,136 +6345,135 @@ static ExprResult buildUserDefinedMapperRef(Sema &SemaRef, Scope *S,
 /// Perform DFS through the structure/class data members trying to find
 /// member(s) with user-defined 'default' mapper and generate implicit map
 /// clauses for such members with the found 'default' mapper.
-static void
-processImplicitMapsWithDefaultMappers(Sema &S, DSAStackTy *Stack,
-                                      SmallVectorImpl<OMPClause *> &Clauses) {
+static OMPClause *processImplicitMapWithDefaultMappers(Sema &S,
+                                                       DSAStackTy *Stack,
+                                                       OMPClause *Clause) {
   // Check for the default mapper for data members.
   if (S.getLangOpts().OpenMP < 50)
-    return;
-  SmallVector<OMPClause *, 4> ImplicitMaps;
-  for (int Cnt = 0, EndCnt = Clauses.size(); Cnt < EndCnt; ++Cnt) {
-    auto *C = dyn_cast<OMPMapClause>(Clauses[Cnt]);
-    if (!C)
+    return nullptr;
+
+  auto *MapC = dyn_cast<OMPMapClause>(Clause);
+  if (!MapC)
+    return nullptr;
+
+  SmallVector<Expr *, 4> SubExprs;
+  auto *MI = MapC->mapperlist_begin();
+  for (auto I = MapC->varlist_begin(), End = MapC->varlist_end(); I != End;
+       ++I, ++MI) {
+    // Expression is mapped using mapper - skip it.
+    if (*MI)
       continue;
-    SmallVector<Expr *, 4> SubExprs;
-    auto *MI = C->mapperlist_begin();
-    for (auto I = C->varlist_begin(), End = C->varlist_end(); I != End;
-         ++I, ++MI) {
-      // Expression is mapped using mapper - skip it.
-      if (*MI)
+    Expr *E = *I;
+    // Expression is dependent - skip it, build the mapper when it gets
+    // instantiated.
+    if (E->isTypeDependent() || E->isValueDependent() ||
+        E->containsUnexpandedParameterPack())
+      continue;
+    // Array section - need to check for the mapping of the array section
+    // element.
+    QualType CanonType = E->getType().getCanonicalType();
+    if (CanonType->isSpecificBuiltinType(BuiltinType::ArraySection)) {
+      const auto *OASE = cast<ArraySectionExpr>(E->IgnoreParenImpCasts());
+      QualType BaseType =
+          ArraySectionExpr::getBaseOriginalType(OASE->getBase());
+      QualType ElemType;
+      if (const auto *ATy = BaseType->getAsArrayTypeUnsafe())
+        ElemType = ATy->getElementType();
+      else
+        ElemType = BaseType->getPointeeType();
+      CanonType = ElemType;
+    }
+
+    // DFS over data members in structures/classes.
+    SmallVector<std::pair<QualType, FieldDecl *>, 4> Types(
+        1, {CanonType, nullptr});
+    llvm::DenseMap<const Type *, Expr *> Visited;
+    SmallVector<std::pair<FieldDecl *, unsigned>, 4> ParentChain(1,
+                                                                 {nullptr, 1});
+    while (!Types.empty()) {
+      QualType BaseType;
+      FieldDecl *CurFD;
+      std::tie(BaseType, CurFD) = Types.pop_back_val();
+      while (ParentChain.back().second == 0)
+        ParentChain.pop_back();
+      --ParentChain.back().second;
+      if (BaseType.isNull())
         continue;
-      Expr *E = *I;
-      // Expression is dependent - skip it, build the mapper when it gets
-      // instantiated.
-      if (E->isTypeDependent() || E->isValueDependent() ||
-          E->containsUnexpandedParameterPack())
+      // Only structs/classes are allowed to have mappers.
+      const RecordDecl *RD = BaseType.getCanonicalType()->getAsRecordDecl();
+      if (!RD)
         continue;
-      // Array section - need to check for the mapping of the array section
-      // element.
-      QualType CanonType = E->getType().getCanonicalType();
-      if (CanonType->isSpecificBuiltinType(BuiltinType::ArraySection)) {
-        const auto *OASE = cast<ArraySectionExpr>(E->IgnoreParenImpCasts());
-        QualType BaseType =
-            ArraySectionExpr::getBaseOriginalType(OASE->getBase());
-        QualType ElemType;
-        if (const auto *ATy = BaseType->getAsArrayTypeUnsafe())
-          ElemType = ATy->getElementType();
-        else
-          ElemType = BaseType->getPointeeType();
-        CanonType = ElemType;
-      }
-
-      // DFS over data members in structures/classes.
-      SmallVector<std::pair<QualType, FieldDecl *>, 4> Types(
-          1, {CanonType, nullptr});
-      llvm::DenseMap<const Type *, Expr *> Visited;
-      SmallVector<std::pair<FieldDecl *, unsigned>, 4> ParentChain(
-          1, {nullptr, 1});
-      while (!Types.empty()) {
-        QualType BaseType;
-        FieldDecl *CurFD;
-        std::tie(BaseType, CurFD) = Types.pop_back_val();
-        while (ParentChain.back().second == 0)
-          ParentChain.pop_back();
-        --ParentChain.back().second;
-        if (BaseType.isNull())
-          continue;
-        // Only structs/classes are allowed to have mappers.
-        const RecordDecl *RD = BaseType.getCanonicalType()->getAsRecordDecl();
-        if (!RD)
+      auto It = Visited.find(BaseType.getTypePtr());
+      if (It == Visited.end()) {
+        // Try to find the associated user-defined mapper.
+        CXXScopeSpec MapperIdScopeSpec;
+        DeclarationNameInfo DefaultMapperId;
+        DefaultMapperId.setName(S.Context.DeclarationNames.getIdentifier(
+            &S.Context.Idents.get("default")));
+        DefaultMapperId.setLoc(E->getExprLoc());
+        ExprResult ER = buildUserDefinedMapperRef(
+            S, Stack->getCurScope(), MapperIdScopeSpec, DefaultMapperId,
+            BaseType, /*UnresolvedMapper=*/nullptr);
+        if (ER.isInvalid())
           continue;
-        auto It = Visited.find(BaseType.getTypePtr());
-        if (It == Visited.end()) {
-          // Try to find the associated user-defined mapper.
-          CXXScopeSpec MapperIdScopeSpec;
-          DeclarationNameInfo DefaultMapperId;
-          DefaultMapperId.setName(S.Context.DeclarationNames.getIdentifier(
-              &S.Context.Idents.get("default")));
-          DefaultMapperId.setLoc(E->getExprLoc());
-          ExprResult ER = buildUserDefinedMapperRef(
-              S, Stack->getCurScope(), MapperIdScopeSpec, DefaultMapperId,
-              BaseType, /*UnresolvedMapper=*/nullptr);
-          if (ER.isInvalid())
-            continue;
-          It = Visited.try_emplace(BaseType.getTypePtr(), ER.get()).first;
-        }
-        // Found default mapper.
-        if (It->second) {
-          auto *OE = new (S.Context) OpaqueValueExpr(E->getExprLoc(), CanonType,
-                                                     VK_LValue, OK_Ordinary, E);
-          OE->setIsUnique(/*V=*/true);
-          Expr *BaseExpr = OE;
-          for (const auto &P : ParentChain) {
-            if (P.first) {
-              BaseExpr = S.BuildMemberExpr(
-                  BaseExpr, /*IsArrow=*/false, E->getExprLoc(),
-                  NestedNameSpecifierLoc(), SourceLocation(), P.first,
-                  DeclAccessPair::make(P.first, P.first->getAccess()),
-                  /*HadMultipleCandidates=*/false, DeclarationNameInfo(),
-                  P.first->getType(), VK_LValue, OK_Ordinary);
-              BaseExpr = S.DefaultLvalueConversion(BaseExpr).get();
-            }
-          }
-          if (CurFD)
+        It = Visited.try_emplace(BaseType.getTypePtr(), ER.get()).first;
+      }
+      // Found default mapper.
+      if (It->second) {
+        auto *OE = new (S.Context) OpaqueValueExpr(E->getExprLoc(), CanonType,
+                                                   VK_LValue, OK_Ordinary, E);
+        OE->setIsUnique(/*V=*/true);
+        Expr *BaseExpr = OE;
+        for (const auto &P : ParentChain) {
+          if (P.first) {
             BaseExpr = S.BuildMemberExpr(
                 BaseExpr, /*IsArrow=*/false, E->getExprLoc(),
-                NestedNameSpecifierLoc(), SourceLocation(), CurFD,
-                DeclAccessPair::make(CurFD, CurFD->getAccess()),
+                NestedNameSpecifierLoc(), SourceLocation(), P.first,
+                DeclAccessPair::make(P.first, P.first->getAccess()),
                 /*HadMultipleCandidates=*/false, DeclarationNameInfo(),
-                CurFD->getType(), VK_LValue, OK_Ordinary);
-          SubExprs.push_back(BaseExpr);
-          continue;
-        }
-        // Check for the "default" mapper for data members.
-        bool FirstIter = true;
-        for (FieldDecl *FD : RD->fields()) {
-          if (!FD)
-            continue;
-          QualType FieldTy = FD->getType();
-          if (FieldTy.isNull() ||
-              !(FieldTy->isStructureOrClassType() || FieldTy->isUnionType()))
-            continue;
-          if (FirstIter) {
-            FirstIter = false;
-            ParentChain.emplace_back(CurFD, 1);
-          } else {
-            ++ParentChain.back().second;
+                P.first->getType(), VK_LValue, OK_Ordinary);
+            BaseExpr = S.DefaultLvalueConversion(BaseExpr).get();
           }
-          Types.emplace_back(FieldTy, FD);
         }
+        if (CurFD)
+          BaseExpr = S.BuildMemberExpr(
+              BaseExpr, /*IsArrow=*/false, E->getExprLoc(),
+              NestedNameSpecifierLoc(), SourceLocation(), CurFD,
+              DeclAccessPair::make(CurFD, CurFD->getAccess()),
+              /*HadMultipleCandidates=*/false, DeclarationNameInfo(),
+              CurFD->getType(), VK_LValue, OK_Ordinary);
+        SubExprs.push_back(BaseExpr);
+        continue;
+      }
+      // Check for the "default" mapper for data members.
+      bool FirstIter = true;
+      for (FieldDecl *FD : RD->fields()) {
+        if (!FD)
+          continue;
+        QualType FieldTy = FD->getType();
+        if (FieldTy.isNull() ||
+            !(FieldTy->isStructureOrClassType() || FieldTy->isUnionType()))
+          continue;
+        if (FirstIter) {
+          FirstIter = false;
+          ParentChain.emplace_back(CurFD, 1);
+        } else {
+          ++ParentChain.back().second;
+        }
+        Types.emplace_back(FieldTy, FD);
       }
     }
-    if (SubExprs.empty())
-      continue;
-    CXXScopeSpec MapperIdScopeSpec;
-    DeclarationNameInfo MapperId;
-    if (OMPClause *NewClause = S.OpenMP().ActOnOpenMPMapClause(
-            nullptr, C->getMapTypeModifiers(), C->getMapTypeModifiersLoc(),
-            MapperIdScopeSpec, MapperId, C->getMapType(),
-            /*IsMapTypeImplicit=*/true, SourceLocation(), SourceLocation(),
-            SubExprs, OMPVarListLocTy()))
-      Clauses.push_back(NewClause);
   }
+  if (SubExprs.empty())
+    return nullptr;
+
+  CXXScopeSpec MapperIdScopeSpec;
+  DeclarationNameInfo MapperId;
+  return S.OpenMP().ActOnOpenMPMapClause(
+      nullptr, MapC->getMapTypeModifiers(), MapC->getMapTypeModifiersLoc(),
+      MapperIdScopeSpec, MapperId, MapC->getMapType(),
+      /*IsMapTypeImplicit=*/true, SourceLocation(), SourceLocation(), SubExprs,
+      OMPVarListLocTy());
 }
 
 namespace {
@@ -5961,523 +6548,387 @@ static bool teamsLoopCanBeParallelFor(Stmt *AStmt, Sema &SemaRef) {
   return Checker.teamsLoopCanBeParallelFor();
 }
 
-StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
-    OpenMPDirectiveKind Kind, const DeclarationNameInfo &DirName,
-    OpenMPDirectiveKind CancelRegion, ArrayRef<OMPClause *> Clauses,
-    Stmt *AStmt, SourceLocation StartLoc, SourceLocation EndLoc) {
-  assert(isOpenMPExecutableDirective(Kind) && "Unexpected directive category");
-
-  StmtResult Res = StmtError();
-  OpenMPBindClauseKind BindKind = OMPC_BIND_unknown;
-  llvm::SmallVector<OMPClause *, 8> ClausesWithImplicit;
+static Stmt *getRawStmt(Stmt *S) {
+  while (isa<CapturedStmt>(S))
+    S = cast<CapturedStmt>(S)->getCapturedStmt();
+  return S;
+}
 
-  if (const OMPBindClause *BC =
-          OMPExecutableDirective::getSingleClause<OMPBindClause>(Clauses))
-    BindKind = BC->getBindKind();
+static void
+getImplicitInfoFromClauses(Sema &SemaRef, OpenMPDirectiveKind DKind,
+                           ArrayRef<OMPClause *> Clauses,
+                           SemaOpenMP::VariableImplicitInfo &ImpInfo) {
+  constexpr unsigned DefaultmapKindNum =
+      SemaOpenMP::VariableImplicitInfo::DefaultmapKindNum;
 
-  if (Kind == OMPD_loop && BindKind == OMPC_BIND_unknown) {
-    const OpenMPDirectiveKind ParentDirective = DSAStack->getParentDirective();
-
-    // Setting the enclosing teams or parallel construct for the loop
-    // directive without bind clause.
-    // [5.0:129:25-28] If the bind clause is not present on the construct and
-    // the loop construct is closely nested inside a teams or parallel
-    // construct, the binding region is the corresponding teams or parallel
-    // region. If none of those conditions hold, the binding region is not
-    // defined.
-    BindKind = OMPC_BIND_thread; // Default bind(thread) if binding is unknown
-    ArrayRef<OpenMPDirectiveKind> ParentLeafs =
-        getLeafConstructsOrSelf(ParentDirective);
-
-    if (ParentDirective == OMPD_unknown) {
-      Diag(DSAStack->getDefaultDSALocation(),
-           diag::err_omp_bind_required_on_loop);
-    } else if (ParentLeafs.back() == OMPD_parallel) {
-      BindKind = OMPC_BIND_parallel;
-    } else if (ParentLeafs.back() == OMPD_teams) {
-      BindKind = OMPC_BIND_teams;
-    }
+  // Get the original location of present modifier from Defaultmap clause.
+  SourceLocation PresentModifierLocs[DefaultmapKindNum];
+  for (OMPClause *C : Clauses) {
+    if (auto *DMC = dyn_cast<OMPDefaultmapClause>(C))
+      if (DMC->getDefaultmapModifier() == OMPC_DEFAULTMAP_MODIFIER_present)
+        PresentModifierLocs[DMC->getDefaultmapKind()] =
+            DMC->getDefaultmapModifierLoc();
+  }
 
-    assert(BindKind != OMPC_BIND_unknown && "Expecting BindKind");
+  for (unsigned VC = 0; VC < DefaultmapKindNum; ++VC) {
+    auto K = static_cast<OpenMPDefaultmapClauseKind>(VC);
+    std::fill_n(std::back_inserter(ImpInfo.ImplicitMapModifiersLoc[VC]),
+                ImpInfo.MapModifiers[K].size(), PresentModifierLocs[VC]);
+  }
 
-    OMPClause *C =
-        ActOnOpenMPBindClause(BindKind, SourceLocation(), SourceLocation(),
-                              SourceLocation(), SourceLocation());
-    ClausesWithImplicit.push_back(C);
+  // Mark taskgroup task_reduction descriptors as implicitly firstprivate.
+  for (OMPClause *C : Clauses) {
+    if (auto *IRC = dyn_cast<OMPInReductionClause>(C)) {
+      for (Expr *E : IRC->taskgroup_descriptors())
+        if (E)
+          ImpInfo.addFirstprivate(E);
+    }
+    // OpenMP 5.0, 2.10.1 task Construct
+    // [detach clause]... The event-handle will be considered as if it was
+    // specified on a firstprivate clause.
+    if (auto *DC = dyn_cast<OMPDetachClause>(C))
+      ImpInfo.addFirstprivate(DC->getEventHandler());
   }
 
-  // Diagnose "loop bind(teams)" with "reduction".
-  if (Kind == OMPD_loop && BindKind == OMPC_BIND_teams) {
+  // OpenMP 5.0 [2.19.7]
+  // If a list item appears in a reduction, lastprivate or linear
+  // clause on a combined target construct then it is treated as
+  // if it also appears in a map clause with a map-type of tofrom
+  if (SemaRef.getLangOpts().OpenMP >= 50 && DKind != OMPD_target &&
+      isOpenMPTargetExecutionDirective(DKind)) {
     for (OMPClause *C : Clauses) {
-      if (C->getClauseKind() == OMPC_reduction)
-        Diag(DSAStack->getDefaultDSALocation(),
-             diag::err_omp_loop_reduction_clause);
+      if (auto *RC = dyn_cast<OMPReductionClause>(C)) {
+        for (Expr *E : RC->varlist()) {
+          if (!isa<DeclRefExpr>(E->IgnoreParenImpCasts()))
+            ImpInfo.addReductionMapping(E);
+        }
+      }
     }
   }
+}
 
-  // First check CancelRegion which is then used in checkNestingOfRegions.
-  if (checkCancelRegion(SemaRef, Kind, CancelRegion, StartLoc) ||
-      checkNestingOfRegions(SemaRef, DSAStack, Kind, DirName, CancelRegion,
-                            BindKind, StartLoc)) {
-    return StmtError();
-  }
+static StmtResult
+createASTForUnit(Sema &SemaRef, DSAStackTy *Stack, Stmt *AStmt,
+                 SourceLocation StartLoc, SourceLocation EndLoc,
+                 const DeclarationNameInfo &DirName,
+                 OpenMPDirectiveKind CancelRegion,
+                 SemaOpenMP::VarsWithInheritedDSAType &VarsWithInheritedDSA,
+                 const UnitConstruct &Unit) {
+  SemaOpenMP &S = SemaRef.OpenMP();
+  StmtResult Res;
+
+  // Some implicit clauses (firstprivate in particular) may end up assigned
+  // to a construct that does not use the variable[1]. This may cause issues
+  // later on in codegen, so filter these clauses out. Implicit private and
+  // firstprivate clauses are generated for a single variable to facilitate
+  // this filtering.
+  // [1] Clauses are assigned based on OpenMP rules that ignore variables
+  // unless they force creation of additional clauses.
+  auto isClauseForUsedVariable = [=](OMPClause *C) {
+    // Tasks have special handling of first/private variables, and they
+    // don't capture them in the AST.
+    if (isOpenMPTaskingDirective(Unit.DKind))
+      return true;
+    auto *CS = dyn_cast_or_null<CapturedStmt>(AStmt);
+    if (!CS)
+      return true;
 
-  // Report affected OpenMP target offloading behavior when in HIP lang-mode.
-  if (getLangOpts().HIP && (isOpenMPTargetExecutionDirective(Kind) ||
-                            isOpenMPTargetDataManagementDirective(Kind)))
-    Diag(StartLoc, diag::warn_hip_omp_target_directives);
+    auto IsVarCaptured = [=](Expr *Var) {
+      if (auto *DRE = dyn_cast<DeclRefExpr>(Var)) {
+        if (auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
+          if (CS->capturesVariable(VD))
+            return true;
+        }
+      }
+      return false;
+    };
 
-  VarsWithInheritedDSAType VarsWithInheritedDSA;
-  bool ErrorFound = false;
-  ClausesWithImplicit.append(Clauses.begin(), Clauses.end());
+    if (auto *FC = dyn_cast<OMPFirstprivateClause>(C))
+      return llvm::any_of(FC->varlist(), IsVarCaptured);
 
-  if (AStmt && !SemaRef.CurContext->isDependentContext() &&
-      isOpenMPCapturingDirective(Kind)) {
-    assert(isa<CapturedStmt>(AStmt) && "Captured statement expected");
+    return true;
+  };
 
-    // Check default data sharing attributes for referenced variables.
-    DSAAttrChecker DSAChecker(DSAStack, SemaRef, cast<CapturedStmt>(AStmt));
-    int ThisCaptureLevel = getOpenMPCaptureLevels(Kind);
-    Stmt *S = AStmt;
-    while (--ThisCaptureLevel >= 0)
-      S = cast<CapturedStmt>(S)->getCapturedStmt();
-    DSAChecker.Visit(S);
-    if (!isOpenMPTargetDataManagementDirective(Kind) &&
-        !isOpenMPTaskingDirective(Kind)) {
-      // Visit subcaptures to generate implicit clauses for captured vars.
-      auto *CS = cast<CapturedStmt>(AStmt);
-      SmallVector<OpenMPDirectiveKind, 4> CaptureRegions;
-      getOpenMPCaptureRegions(CaptureRegions, Kind);
-      // Ignore outer tasking regions for target directives.
-      if (CaptureRegions.size() > 1 && CaptureRegions.front() == OMPD_task)
-        CS = cast<CapturedStmt>(CS->getCapturedStmt());
-      DSAChecker.visitSubCaptures(CS);
-    }
-    if (DSAChecker.isErrorFound())
-      return StmtError();
-    // Generate list of implicitly defined firstprivate variables.
-    VarsWithInheritedDSA = DSAChecker.getVarsWithInheritedDSA();
-    VariableImplicitInfo ImpInfo = DSAChecker.getImplicitInfo();
-
-    SmallVector<SourceLocation, NumberOfOMPMapClauseModifiers>
-        ImplicitMapModifiersLoc[VariableImplicitInfo::DefaultmapKindNum];
-    // Get the original location of present modifier from Defaultmap clause.
-    SourceLocation PresentModifierLocs[VariableImplicitInfo::DefaultmapKindNum];
-    for (OMPClause *C : Clauses) {
-      if (auto *DMC = dyn_cast<OMPDefaultmapClause>(C))
-        if (DMC->getDefaultmapModifier() == OMPC_DEFAULTMAP_MODIFIER_present)
-          PresentModifierLocs[DMC->getDefaultmapKind()] =
-              DMC->getDefaultmapModifierLoc();
-    }
+  SmallVector<OMPClause *> Clauses;
+  llvm::copy_if(Unit.Clauses, std::back_inserter(Clauses),
+                isClauseForUsedVariable);
 
-    for (OpenMPDefaultmapClauseKind K :
-         llvm::enum_seq_inclusive<OpenMPDefaultmapClauseKind>(
-             OpenMPDefaultmapClauseKind(), OMPC_DEFAULTMAP_unknown)) {
-      std::fill_n(std::back_inserter(ImplicitMapModifiersLoc[K]),
-                  ImpInfo.MapModifiers[K].size(), PresentModifierLocs[K]);
-    }
-    // Mark taskgroup task_reduction descriptors as implicitly firstprivate.
-    for (OMPClause *C : Clauses) {
-      if (auto *IRC = dyn_cast<OMPInReductionClause>(C)) {
-        for (Expr *E : IRC->taskgroup_descriptors())
-          if (E)
-            ImpInfo.Firstprivates.insert(E);
-      }
-      // OpenMP 5.0, 2.10.1 task Construct
-      // [detach clause]... The event-handle will be considered as if it was
-      // specified on a firstprivate clause.
-      if (auto *DC = dyn_cast<OMPDetachClause>(C))
-        ImpInfo.Firstprivates.insert(DC->getEventHandler());
-    }
-    if (!ImpInfo.Firstprivates.empty()) {
-      if (OMPClause *Implicit = ActOnOpenMPFirstprivateClause(
-              ImpInfo.Firstprivates.getArrayRef(), SourceLocation(),
-              SourceLocation(), SourceLocation())) {
-        ClausesWithImplicit.push_back(Implicit);
-        ErrorFound = cast<OMPFirstprivateClause>(Implicit)->varlist_size() !=
-                     ImpInfo.Firstprivates.size();
-      } else {
-        ErrorFound = true;
-      }
-    }
-    if (!ImpInfo.Privates.empty()) {
-      if (OMPClause *Implicit = ActOnOpenMPPrivateClause(
-              ImpInfo.Privates.getArrayRef(), SourceLocation(),
-              SourceLocation(), SourceLocation())) {
-        ClausesWithImplicit.push_back(Implicit);
-        ErrorFound = cast<OMPPrivateClause>(Implicit)->varlist_size() !=
-                     ImpInfo.Privates.size();
-      } else {
-        ErrorFound = true;
-      }
-    }
-    // OpenMP 5.0 [2.19.7]
-    // If a list item appears in a reduction, lastprivate or linear
-    // clause on a combined target construct then it is treated as
-    // if it also appears in a map clause with a map-type of tofrom
-    if (getLangOpts().OpenMP >= 50 && Kind != OMPD_target &&
-        isOpenMPTargetExecutionDirective(Kind)) {
-      SmallVector<Expr *, 4> ImplicitExprs;
-      for (OMPClause *C : Clauses) {
-        if (auto *RC = dyn_cast<OMPReductionClause>(C))
-          for (Expr *E : RC->varlist())
-            if (!isa<DeclRefExpr>(E->IgnoreParenImpCasts()))
-              ImplicitExprs.emplace_back(E);
-      }
-      if (!ImplicitExprs.empty()) {
-        ArrayRef<Expr *> Exprs = ImplicitExprs;
-        CXXScopeSpec MapperIdScopeSpec;
-        DeclarationNameInfo MapperId;
-        if (OMPClause *Implicit = ActOnOpenMPMapClause(
-                nullptr, OMPC_MAP_MODIFIER_unknown, SourceLocation(),
-                MapperIdScopeSpec, MapperId, OMPC_MAP_tofrom,
-                /*IsMapTypeImplicit=*/true, SourceLocation(), SourceLocation(),
-                Exprs, OMPVarListLocTy(), /*NoDiagnose=*/true))
-          ClausesWithImplicit.emplace_back(Implicit);
-      }
-    }
-    for (unsigned I = 0; I < VariableImplicitInfo::DefaultmapKindNum; ++I) {
-      int ClauseKindCnt = -1;
-      for (unsigned J = 0; J < VariableImplicitInfo::MapKindNum; ++J) {
-        ArrayRef<Expr *> ImplicitMap = ImpInfo.Mappings[I][J].getArrayRef();
-        ++ClauseKindCnt;
-        if (ImplicitMap.empty())
-          continue;
-        CXXScopeSpec MapperIdScopeSpec;
-        DeclarationNameInfo MapperId;
-        auto K = static_cast<OpenMPMapClauseKind>(ClauseKindCnt);
-        if (OMPClause *Implicit = ActOnOpenMPMapClause(
-                nullptr, ImpInfo.MapModifiers[I], ImplicitMapModifiersLoc[I],
-                MapperIdScopeSpec, MapperId, K, /*IsMapTypeImplicit=*/true,
-                SourceLocation(), SourceLocation(), ImplicitMap,
-                OMPVarListLocTy())) {
-          ClausesWithImplicit.emplace_back(Implicit);
-          ErrorFound |= cast<OMPMapClause>(Implicit)->varlist_size() !=
-                        ImplicitMap.size();
-        } else {
-          ErrorFound = true;
-        }
-      }
-    }
-    // Build expressions for implicit maps of data members with 'default'
-    // mappers.
-    if (getLangOpts().OpenMP >= 50)
-      processImplicitMapsWithDefaultMappers(SemaRef, DSAStack,
-                                            ClausesWithImplicit);
-  }
+  OpenMPDirectiveKind Saved = Stack->getTopOfStack().Directive;
+  Stack->getTopOfStack().Directive = Unit.DKind;
 
-  switch (Kind) {
+  switch (Unit.DKind) {
   case OMPD_parallel:
-    Res = ActOnOpenMPParallelDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                       EndLoc);
+    Res = S.ActOnOpenMPParallelDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_simd:
-    Res = ActOnOpenMPSimdDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc,
-                                   VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPSimdDirective(Clauses, AStmt, StartLoc, EndLoc,
+                                     VarsWithInheritedDSA);
     break;
   case OMPD_tile:
-    Res =
-        ActOnOpenMPTileDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc);
+    Res = S.ActOnOpenMPTileDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_unroll:
-    Res = ActOnOpenMPUnrollDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                     EndLoc);
+    Res = S.ActOnOpenMPUnrollDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_reverse:
-    assert(ClausesWithImplicit.empty() &&
-           "reverse directive does not support any clauses");
-    Res = ActOnOpenMPReverseDirective(AStmt, StartLoc, EndLoc);
+    assert(Clauses.empty() && "reverse directive does not support any clauses");
+    Res = S.ActOnOpenMPReverseDirective(AStmt, StartLoc, EndLoc);
     break;
   case OMPD_interchange:
-    Res = ActOnOpenMPInterchangeDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                          EndLoc);
+    Res = S.ActOnOpenMPInterchangeDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_for:
-    Res = ActOnOpenMPForDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc,
-                                  VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPForDirective(Clauses, AStmt, StartLoc, EndLoc,
+                                    VarsWithInheritedDSA);
     break;
   case OMPD_for_simd:
-    Res = ActOnOpenMPForSimdDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                      EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPForSimdDirective(Clauses, AStmt, StartLoc, EndLoc,
+                                        VarsWithInheritedDSA);
     break;
   case OMPD_sections:
-    Res = ActOnOpenMPSectionsDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                       EndLoc);
+    Res = S.ActOnOpenMPSectionsDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_section:
-    assert(ClausesWithImplicit.empty() &&
+    assert(Clauses.empty() &&
            "No clauses are allowed for 'omp section' directive");
-    Res = ActOnOpenMPSectionDirective(AStmt, StartLoc, EndLoc);
+    Res = S.ActOnOpenMPSectionDirective(AStmt, StartLoc, EndLoc);
     break;
   case OMPD_single:
-    Res = ActOnOpenMPSingleDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                     EndLoc);
+    Res = S.ActOnOpenMPSingleDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_master:
-    assert(ClausesWithImplicit.empty() &&
+    assert(Clauses.empty() &&
            "No clauses are allowed for 'omp master' directive");
-    Res = ActOnOpenMPMasterDirective(AStmt, StartLoc, EndLoc);
+    Res = S.ActOnOpenMPMasterDirective(AStmt, StartLoc, EndLoc);
     break;
   case OMPD_masked:
-    Res = ActOnOpenMPMaskedDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                     EndLoc);
+    Res = S.ActOnOpenMPMaskedDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_critical:
-    Res = ActOnOpenMPCriticalDirective(DirName, ClausesWithImplicit, AStmt,
-                                       StartLoc, EndLoc);
+    Res = S.ActOnOpenMPCriticalDirective(DirName, Clauses, AStmt, StartLoc,
+                                         EndLoc);
     break;
   case OMPD_parallel_for:
-    Res = ActOnOpenMPParallelForDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                          EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPParallelForDirective(Clauses, AStmt, StartLoc, EndLoc,
+                                            VarsWithInheritedDSA);
     break;
   case OMPD_parallel_for_simd:
-    Res = ActOnOpenMPParallelForSimdDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPParallelForSimdDirective(Clauses, AStmt, StartLoc,
+                                                EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_scope:
-    Res =
-        ActOnOpenMPScopeDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc);
+    Res = S.ActOnOpenMPScopeDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_parallel_master:
-    Res = ActOnOpenMPParallelMasterDirective(ClausesWithImplicit, AStmt,
-                                             StartLoc, EndLoc);
+    Res =
+        S.ActOnOpenMPParallelMasterDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_parallel_masked:
-    Res = ActOnOpenMPParallelMaskedDirective(ClausesWithImplicit, AStmt,
-                                             StartLoc, EndLoc);
+    Res =
+        S.ActOnOpenMPParallelMaskedDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_parallel_sections:
-    Res = ActOnOpenMPParallelSectionsDirective(ClausesWithImplicit, AStmt,
-                                               StartLoc, EndLoc);
+    Res = S.ActOnOpenMPParallelSectionsDirective(Clauses, AStmt, StartLoc,
+                                                 EndLoc);
     break;
   case OMPD_task:
-    Res =
-        ActOnOpenMPTaskDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc);
+    Res = S.ActOnOpenMPTaskDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_taskyield:
-    assert(ClausesWithImplicit.empty() &&
+    assert(Clauses.empty() &&
            "No clauses are allowed for 'omp taskyield' directive");
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp taskyield' directive");
-    Res = ActOnOpenMPTaskyieldDirective(StartLoc, EndLoc);
+    Res = S.ActOnOpenMPTaskyieldDirective(StartLoc, EndLoc);
     break;
   case OMPD_error:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp error' directive");
-    Res = ActOnOpenMPErrorDirective(ClausesWithImplicit, StartLoc, EndLoc);
+    Res = S.ActOnOpenMPErrorDirective(Clauses, StartLoc, EndLoc);
     break;
   case OMPD_barrier:
-    assert(ClausesWithImplicit.empty() &&
+    assert(Clauses.empty() &&
            "No clauses are allowed for 'omp barrier' directive");
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp barrier' directive");
-    Res = ActOnOpenMPBarrierDirective(StartLoc, EndLoc);
+    Res = S.ActOnOpenMPBarrierDirective(StartLoc, EndLoc);
     break;
   case OMPD_taskwait:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp taskwait' directive");
-    Res = ActOnOpenMPTaskwaitDirective(ClausesWithImplicit, StartLoc, EndLoc);
+    Res = S.ActOnOpenMPTaskwaitDirective(Clauses, StartLoc, EndLoc);
     break;
   case OMPD_taskgroup:
-    Res = ActOnOpenMPTaskgroupDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                        EndLoc);
+    Res = S.ActOnOpenMPTaskgroupDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_flush:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp flush' directive");
-    Res = ActOnOpenMPFlushDirective(ClausesWithImplicit, StartLoc, EndLoc);
+    Res = S.ActOnOpenMPFlushDirective(Clauses, StartLoc, EndLoc);
     break;
   case OMPD_depobj:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp depobj' directive");
-    Res = ActOnOpenMPDepobjDirective(ClausesWithImplicit, StartLoc, EndLoc);
+    Res = S.ActOnOpenMPDepobjDirective(Clauses, StartLoc, EndLoc);
     break;
   case OMPD_scan:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp scan' directive");
-    Res = ActOnOpenMPScanDirective(ClausesWithImplicit, StartLoc, EndLoc);
+    Res = S.ActOnOpenMPScanDirective(Clauses, StartLoc, EndLoc);
     break;
   case OMPD_ordered:
-    Res = ActOnOpenMPOrderedDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                      EndLoc);
+    Res = S.ActOnOpenMPOrderedDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_atomic:
-    Res = ActOnOpenMPAtomicDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                     EndLoc);
+    Res = S.ActOnOpenMPAtomicDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_teams:
-    Res =
-        ActOnOpenMPTeamsDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc);
+    Res = S.ActOnOpenMPTeamsDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_target:
-    Res = ActOnOpenMPTargetDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                     EndLoc);
+    Res = S.ActOnOpenMPTargetDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_target_parallel:
-    Res = ActOnOpenMPTargetParallelDirective(ClausesWithImplicit, AStmt,
-                                             StartLoc, EndLoc);
+    Res =
+        S.ActOnOpenMPTargetParallelDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_target_parallel_for:
-    Res = ActOnOpenMPTargetParallelForDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTargetParallelForDirective(Clauses, AStmt, StartLoc,
+                                                  EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_cancellation_point:
-    assert(ClausesWithImplicit.empty() &&
+    assert(Clauses.empty() &&
            "No clauses are allowed for 'omp cancellation point' directive");
-    assert(AStmt == nullptr && "No associated statement allowed for 'omp "
-                               "cancellation point' directive");
-    Res = ActOnOpenMPCancellationPointDirective(StartLoc, EndLoc, CancelRegion);
+    Res =
+        S.ActOnOpenMPCancellationPointDirective(StartLoc, EndLoc, CancelRegion);
     break;
   case OMPD_cancel:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp cancel' directive");
-    Res = ActOnOpenMPCancelDirective(ClausesWithImplicit, StartLoc, EndLoc,
-                                     CancelRegion);
+    Res = S.ActOnOpenMPCancelDirective(Clauses, StartLoc, EndLoc, CancelRegion);
     break;
   case OMPD_target_data:
-    Res = ActOnOpenMPTargetDataDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                         EndLoc);
+    Res = S.ActOnOpenMPTargetDataDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_target_enter_data:
-    Res = ActOnOpenMPTargetEnterDataDirective(ClausesWithImplicit, StartLoc,
-                                              EndLoc, AStmt);
+    Res =
+        S.ActOnOpenMPTargetEnterDataDirective(Clauses, StartLoc, EndLoc, AStmt);
     break;
   case OMPD_target_exit_data:
-    Res = ActOnOpenMPTargetExitDataDirective(ClausesWithImplicit, StartLoc,
-                                             EndLoc, AStmt);
+    Res =
+        S.ActOnOpenMPTargetExitDataDirective(Clauses, StartLoc, EndLoc, AStmt);
     break;
   case OMPD_taskloop:
-    Res = ActOnOpenMPTaskLoopDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                       EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTaskLoopDirective(Clauses, AStmt, StartLoc, EndLoc,
+                                         VarsWithInheritedDSA);
     break;
   case OMPD_taskloop_simd:
-    Res = ActOnOpenMPTaskLoopSimdDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                           EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTaskLoopSimdDirective(Clauses, AStmt, StartLoc, EndLoc,
+                                             VarsWithInheritedDSA);
     break;
   case OMPD_master_taskloop:
-    Res = ActOnOpenMPMasterTaskLoopDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPMasterTaskLoopDirective(Clauses, AStmt, StartLoc, EndLoc,
+                                               VarsWithInheritedDSA);
     break;
   case OMPD_masked_taskloop:
-    Res = ActOnOpenMPMaskedTaskLoopDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPMaskedTaskLoopDirective(Clauses, AStmt, StartLoc, EndLoc,
+                                               VarsWithInheritedDSA);
     break;
   case OMPD_master_taskloop_simd:
-    Res = ActOnOpenMPMasterTaskLoopSimdDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPMasterTaskLoopSimdDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_masked_taskloop_simd:
-    Res = ActOnOpenMPMaskedTaskLoopSimdDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPMaskedTaskLoopSimdDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_parallel_master_taskloop:
-    Res = ActOnOpenMPParallelMasterTaskLoopDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPParallelMasterTaskLoopDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_parallel_masked_taskloop:
-    Res = ActOnOpenMPParallelMaskedTaskLoopDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPParallelMaskedTaskLoopDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_parallel_master_taskloop_simd:
-    Res = ActOnOpenMPParallelMasterTaskLoopSimdDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPParallelMasterTaskLoopSimdDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_parallel_masked_taskloop_simd:
-    Res = ActOnOpenMPParallelMaskedTaskLoopSimdDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPParallelMaskedTaskLoopSimdDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_distribute:
-    Res = ActOnOpenMPDistributeDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                         EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPDistributeDirective(Clauses, AStmt, StartLoc, EndLoc,
+                                           VarsWithInheritedDSA);
     break;
   case OMPD_target_update:
-    Res = ActOnOpenMPTargetUpdateDirective(ClausesWithImplicit, StartLoc,
-                                           EndLoc, AStmt);
+    Res = S.ActOnOpenMPTargetUpdateDirective(Clauses, StartLoc, EndLoc, AStmt);
     break;
   case OMPD_distribute_parallel_for:
-    Res = ActOnOpenMPDistributeParallelForDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPDistributeParallelForDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_distribute_parallel_for_simd:
-    Res = ActOnOpenMPDistributeParallelForSimdDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPDistributeParallelForSimdDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_distribute_simd:
-    Res = ActOnOpenMPDistributeSimdDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPDistributeSimdDirective(Clauses, AStmt, StartLoc, EndLoc,
+                                               VarsWithInheritedDSA);
     break;
   case OMPD_target_parallel_for_simd:
-    Res = ActOnOpenMPTargetParallelForSimdDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTargetParallelForSimdDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_target_simd:
-    Res = ActOnOpenMPTargetSimdDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                         EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTargetSimdDirective(Clauses, AStmt, StartLoc, EndLoc,
+                                           VarsWithInheritedDSA);
     break;
   case OMPD_teams_distribute:
-    Res = ActOnOpenMPTeamsDistributeDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTeamsDistributeDirective(Clauses, AStmt, StartLoc,
+                                                EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_teams_distribute_simd:
-    Res = ActOnOpenMPTeamsDistributeSimdDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTeamsDistributeSimdDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_teams_distribute_parallel_for_simd:
-    Res = ActOnOpenMPTeamsDistributeParallelForSimdDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTeamsDistributeParallelForSimdDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_teams_distribute_parallel_for:
-    Res = ActOnOpenMPTeamsDistributeParallelForDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTeamsDistributeParallelForDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_target_teams:
-    Res = ActOnOpenMPTargetTeamsDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                          EndLoc);
+    Res = S.ActOnOpenMPTargetTeamsDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_target_teams_distribute:
-    Res = ActOnOpenMPTargetTeamsDistributeDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTargetTeamsDistributeDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_target_teams_distribute_parallel_for:
-    Res = ActOnOpenMPTargetTeamsDistributeParallelForDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTargetTeamsDistributeParallelForDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_target_teams_distribute_parallel_for_simd:
-    Res = ActOnOpenMPTargetTeamsDistributeParallelForSimdDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTargetTeamsDistributeParallelForSimdDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_target_teams_distribute_simd:
-    Res = ActOnOpenMPTargetTeamsDistributeSimdDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTargetTeamsDistributeSimdDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_interop:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp interop' directive");
-    Res = ActOnOpenMPInteropDirective(ClausesWithImplicit, StartLoc, EndLoc);
+    Res = S.ActOnOpenMPInteropDirective(Clauses, StartLoc, EndLoc);
     break;
   case OMPD_dispatch:
-    Res = ActOnOpenMPDispatchDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                       EndLoc);
+    Res = S.ActOnOpenMPDispatchDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_loop:
-    Res = ActOnOpenMPGenericLoopDirective(ClausesWithImplicit, AStmt, StartLoc,
-                                          EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPGenericLoopDirective(Clauses, AStmt, StartLoc, EndLoc,
+                                            VarsWithInheritedDSA);
     break;
   case OMPD_teams_loop:
-    Res = ActOnOpenMPTeamsGenericLoopDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTeamsGenericLoopDirective(Clauses, AStmt, StartLoc,
+                                                 EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_target_teams_loop:
-    Res = ActOnOpenMPTargetTeamsGenericLoopDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTargetTeamsGenericLoopDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_parallel_loop:
-    Res = ActOnOpenMPParallelGenericLoopDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPParallelGenericLoopDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_target_parallel_loop:
-    Res = ActOnOpenMPTargetParallelGenericLoopDirective(
-        ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
+    Res = S.ActOnOpenMPTargetParallelGenericLoopDirective(
+        Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_declare_target:
   case OMPD_end_declare_target:
@@ -6496,6 +6947,205 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
     llvm_unreachable("Unknown OpenMP directive");
   }
 
+  Stack->getTopOfStack().Directive = Saved;
+  return Res;
+}
+
+static StmtResult createASTForDirective(
+    Sema &SemaRef, OpenMPDirectiveKind Kind, ArrayRef<OMPClause *> Clauses,
+    Stmt *AStmt, SourceLocation StartLoc, SourceLocation EndLoc,
+    const DeclarationNameInfo &DirName, OpenMPDirectiveKind CancelRegion,
+    SemaOpenMP::VarsWithInheritedDSAType &VarsWithInheritedDSA,
+    DSAStackTy *Stack) {
+  SemaOpenMP &S = SemaRef.OpenMP();
+
+  if (Kind != OMPD_ordered && AStmt != nullptr &&
+      !isOpenMPDirectiveWithStatement(Kind)) {
+    std::string Name = getOpenMPDirectiveName(Kind).str();
+    std::string Msg =
+        "No associated statement allowed for '" + Name + "' directive";
+    llvm_unreachable(Msg.data());
+  }
+  if (AStmt == nullptr && isOpenMPDirectiveWithStatement(Kind))
+    return StmtError();
+
+  for (OMPClause *C : Clauses) {
+    if (!isAllowedClauseForDirective(Kind, C->getClauseKind(),
+                                     S.getLangOpts().OpenMP))
+      return StmtError();
+  }
+
+  StmtResult Res =
+      isa_and_present<CapturedStmt>(AStmt) ? getRawStmt(AStmt) : AStmt;
+
+  SmallVector<CapturedStmt *> CapStmts;
+  for (auto *CS = dyn_cast_or_null<CapturedStmt>(AStmt); CS != nullptr;
+       CS = dyn_cast<CapturedStmt>(CS->getCapturedStmt()))
+    CapStmts.push_back(CS);
+
+  auto ReconstructCS = [&](CapturedStmt *CS, Stmt *T) {
+    // Regenerate given CapturedStmt with a different "body" statement.
+    //
+    // The T in
+    //    CapturedStmt
+    //    `-CapturedDecl
+    //      `-T
+    // is stored in both CapturedStmt, and CapturedDecl. To replace T, the
+    // decl can be updated via `setBody`, but the stmt must be rebuilt.
+    CapturedDecl *CD = CS->getCapturedDecl();
+    CD->setBody(T);
+    SmallVector<CapturedStmt::Capture> Captures(CS->capture_begin(),
+                                                CS->capture_end());
+    SmallVector<Expr *> CaptureInits(CS->capture_init_begin(),
+                                     CS->capture_init_end());
+    return CapturedStmt::Create(
+        S.getASTContext(), T, CS->getCapturedRegionKind(), Captures,
+        CaptureInits, CD,
+        const_cast<RecordDecl *>(CS->getCapturedRecordDecl()));
+  };
+
+  auto ProcessPreInits = [&](StmtResult Inp, bool IsOuter) {
+    StmtResult Res = Inp;
+    SmallVector<Stmt *> Inits;
+    for (auto [C, P] : Stack->getPreInits()) {
+      OpenMPClauseKind CK = C->getClauseKind();
+      bool NeedsOuter = CK == OMPC_num_teams || CK == OMPC_thread_limit;
+      if (IsOuter == NeedsOuter)
+        Inits.push_back(P);
+    }
+    if (!Inits.empty()) {
+      Inits.push_back(Inp.get());
+      Res = CompoundStmt::Create(S.getASTContext(), Inits, FPOptionsOverride(),
+                                 SourceLocation(), SourceLocation());
+    }
+    return Res;
+  };
+
+  auto &UnitConstructs = Stack->getTopOfStack().UnitConstructs;
+
+  for (UnitConstruct &Unit : llvm::reverse(UnitConstructs)) {
+    SmallVector<OpenMPDirectiveKind, 4> CapRegions;
+    // "ordered" may be either block-associated or standalone. It will have
+    // an associated statement or not, respectively. Both cases are treated
+    // as legal.
+    if (Unit.DKind != OMPD_ordered || AStmt != nullptr) {
+      if (isOpenMPCapturingDirective(Unit.DKind,
+                                     /*OrderedIsStandalone=*/AStmt == nullptr))
+        getOpenMPCaptureRegions(CapRegions, Unit.DKind);
+    }
+
+    Stmt *AS = nullptr;
+    if (!CapRegions.empty()) {
+      size_t NumRegions = CapRegions.size();
+      assert(CapStmts.size() >= NumRegions && "Not enough captured statenents");
+
+      auto Tail =
+          MutableArrayRef<CapturedStmt *>(CapStmts).take_back(NumRegions);
+      Stmt *T = Res.get();
+      for (CapturedStmt *&CS : llvm::reverse(Tail))
+        T = CS = ReconstructCS(CS, T);
+
+      AS = CapStmts[CapStmts.size() - CapRegions.size()];
+      CapStmts.resize(CapStmts.size() - CapRegions.size());
+    } else {
+      AS = Res.get();
+    }
+
+    Res = createASTForUnit(SemaRef, Stack, AS, StartLoc, EndLoc, DirName,
+                           CancelRegion, VarsWithInheritedDSA, Unit);
+
+    if (Res.isInvalid())
+      break;
+
+    // Emit pre-inits for the clauses, except num_teams and thread_limit
+    // (those must be evaluated outside of the construct, by OpenMP rules).
+    Res = ProcessPreInits(Res, /*IsOuter=*/false);
+  }
+
+  if (Res.isUsable())
+    Res = ProcessPreInits(Res, /*IsOuter=*/true);
+
+  return Res;
+}
+
+StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
+    OpenMPDirectiveKind Kind, const DeclarationNameInfo &DirName,
+    OpenMPDirectiveKind CancelRegion, ArrayRef<OMPClause *> Clauses,
+    Stmt *AStmt, SourceLocation StartLoc, SourceLocation EndLoc) {
+  assert(isOpenMPExecutableDirective(Kind) && "Unexpected directive category");
+  StmtResult Res = StmtError();
+
+  // Diagnose "loop bind(teams)" with "reduction".
+  OpenMPBindClauseKind BindKind = OMPC_BIND_unknown;
+  if (const OMPBindClause *BC =
+          OMPExecutableDirective::getSingleClause<OMPBindClause>(Clauses))
+    BindKind = BC->getBindKind();
+  if (Kind == OMPD_loop && BindKind == OMPC_BIND_teams) {
+    for (OMPClause *C : Clauses) {
+      if (C->getClauseKind() == OMPC_reduction)
+        Diag(DSAStack->getDefaultDSALocation(),
+             diag::err_omp_loop_reduction_clause);
+    }
+  }
+
+  // First check CancelRegion which is then used in checkNestingOfRegions.
+  if (checkCancelRegion(SemaRef, Kind, CancelRegion, StartLoc) ||
+      checkNestingOfRegions(SemaRef, DSAStack, Kind, DirName, CancelRegion,
+                            BindKind, StartLoc)) {
+    return StmtError();
+  }
+
+  // Report affected OpenMP target offloading behavior when in HIP lang-mode.
+  if (getLangOpts().HIP && (isOpenMPTargetExecutionDirective(Kind) ||
+                            isOpenMPTargetDataManagementDirective(Kind)))
+    Diag(StartLoc, diag::warn_hip_omp_target_directives);
+
+  VarsWithInheritedDSAType VarsWithInheritedDSA;
+  bool ErrorFound = false;
+
+  if (AStmt && !SemaRef.CurContext->isDependentContext() &&
+      isOpenMPCapturingDirective(Kind, /*OrderedIsStandalone=*/false)) {
+    assert(isa<CapturedStmt>(AStmt) && "Captured statement expected");
+
+    // Check default data sharing attributes for referenced variables.
+    VarsWithInheritedDSA = DSAStack->getTopOfStack().VarsWithInheritedDSA;
+
+    if (DSAStack->getTopOfStack().HasDSAError)
+      return StmtError();
+  }
+
+  for (OMPClause *C : Clauses) {
+    if (C->getClauseKind() != OMPC_reduction)
+      continue;
+    auto *RC = cast<OMPReductionClause>(C);
+    if (RC->getModifier() == OMPC_REDUCTION_task) {
+      // OpenMP 5.0, 2.19.5.4 reduction Clause.
+      // A reduction clause with the task reduction-modifier may only appear on
+      // a parallel construct, a worksharing construct or a combined or
+      // composite construct for which any of the aforementioned constructs is a
+      // constituent construct and simd or loop are not constituent constructs.
+      if (!(isOpenMPParallelDirective(Kind) ||
+            isOpenMPWorksharingDirective(Kind)) ||
+          isOpenMPSimdDirective(Kind))
+        Diag(RC->getModifierLoc(),
+             diag::err_omp_reduction_task_not_parallel_or_worksharing);
+    }
+  }
+
+  if (!SemaRef.CurContext->isDependentContext()) {
+    Res = createASTForDirective(SemaRef, Kind, Clauses, AStmt, StartLoc, EndLoc,
+                                DirName, CancelRegion, VarsWithInheritedDSA,
+                                DSAStack);
+  } else {
+    if (getDirectiveAssociation(Kind) == Association::Loop) {
+      Res = ActOnOpenMPCompoundLoopDirective(Kind, Clauses, AStmt, StartLoc,
+                                             EndLoc, VarsWithInheritedDSA);
+    } else {
+      Res = ActOnOpenMPCompoundBlockDirective(
+          Kind, Clauses, AStmt, CancelRegion, DirName, StartLoc, EndLoc);
+    }
+  }
+
   ErrorFound = Res.isInvalid() || ErrorFound;
 
   // Check variables in the clauses if default(none) or
@@ -8397,7 +9047,9 @@ bool OpenMPIterationSpaceChecker::checkAndSetInc(Expr *S) {
 static ExprResult
 tryBuildCapture(Sema &SemaRef, Expr *Capture,
                 llvm::MapVector<const Expr *, DeclRefExpr *> &Captures,
-                StringRef Name = ".capture_expr.") {
+                bool RefersToCapture, // = false,
+                StringRef Name = ".capture_expr.",
+                bool MakeOMPCapturedExprDecl = true) {
   if (SemaRef.CurContext->isDependentContext() || Capture->containsErrors())
     return Capture;
   if (Capture->isEvaluatable(SemaRef.Context, Expr::SE_AllowSideEffects))
@@ -8407,9 +9059,11 @@ tryBuildCapture(Sema &SemaRef, Expr *Capture,
                                              /*AllowExplicit=*/true);
   auto I = Captures.find(Capture);
   if (I != Captures.end())
-    return buildCapture(SemaRef, Capture, I->second, Name);
+    return buildCapture(SemaRef, Capture, I->second, RefersToCapture, Name,
+                        MakeOMPCapturedExprDecl);
   DeclRefExpr *Ref = nullptr;
-  ExprResult Res = buildCapture(SemaRef, Capture, Ref, Name);
+  ExprResult Res = buildCapture(SemaRef, Capture, Ref, RefersToCapture, Name,
+                                MakeOMPCapturedExprDecl);
   Captures[Capture] = Ref;
   return Res;
 }
@@ -8421,7 +9075,8 @@ calculateNumIters(Sema &SemaRef, Scope *S, SourceLocation DefaultLoc,
                   Expr *Lower, Expr *Upper, Expr *Step, QualType LCTy,
                   bool TestIsStrictOp, bool RoundToStep,
                   llvm::MapVector<const Expr *, DeclRefExpr *> &Captures) {
-  ExprResult NewStep = tryBuildCapture(SemaRef, Step, Captures, ".new_step");
+  ExprResult NewStep =
+      tryBuildCapture(SemaRef, Step, Captures, /*RefersToCapture=*/false);
   if (!NewStep.isUsable())
     return nullptr;
   llvm::APSInt LRes, SRes;
@@ -8643,10 +9298,12 @@ Expr *OpenMPIterationSpaceChecker::buildNumIterations(
     if (!LBMaxVal.isUsable())
       return nullptr;
 
-    Expr *LBMin =
-        tryBuildCapture(SemaRef, LBMinVal.get(), Captures, ".lb_min").get();
-    Expr *LBMax =
-        tryBuildCapture(SemaRef, LBMaxVal.get(), Captures, ".lb_max").get();
+    Expr *LBMin = tryBuildCapture(SemaRef, LBMinVal.get(), Captures,
+                                  /*RefersToCapture=*/false)
+                      .get();
+    Expr *LBMax = tryBuildCapture(SemaRef, LBMaxVal.get(), Captures,
+                                  /*RefersToCapture=*/false)
+                      .get();
     if (!LBMin || !LBMax)
       return nullptr;
     // LB(MinVal) < LB(MaxVal)
@@ -8654,9 +9311,9 @@ Expr *OpenMPIterationSpaceChecker::buildNumIterations(
         SemaRef.BuildBinOp(S, DefaultLoc, BO_LT, LBMin, LBMax);
     if (!MinLessMaxRes.isUsable())
       return nullptr;
-    Expr *MinLessMax =
-        tryBuildCapture(SemaRef, MinLessMaxRes.get(), Captures, ".min_less_max")
-            .get();
+    Expr *MinLessMax = tryBuildCapture(SemaRef, MinLessMaxRes.get(), Captures,
+                                       /*RefersToCapture=*/false)
+                           .get();
     if (!MinLessMax)
       return nullptr;
     if (*TestIsLessOp) {
@@ -8729,10 +9386,12 @@ Expr *OpenMPIterationSpaceChecker::buildNumIterations(
     if (!UBMaxVal.isUsable())
       return nullptr;
 
-    Expr *UBMin =
-        tryBuildCapture(SemaRef, UBMinVal.get(), Captures, ".ub_min").get();
-    Expr *UBMax =
-        tryBuildCapture(SemaRef, UBMaxVal.get(), Captures, ".ub_max").get();
+    Expr *UBMin = tryBuildCapture(SemaRef, UBMinVal.get(), Captures,
+                                  /*RefersToCapture=*/false)
+                      .get();
+    Expr *UBMax = tryBuildCapture(SemaRef, UBMaxVal.get(), Captures,
+                                  /*RefersToCapture=*/false)
+                      .get();
     if (!UBMin || !UBMax)
       return nullptr;
     // UB(MinVal) > UB(MaxVal)
@@ -8741,7 +9400,7 @@ Expr *OpenMPIterationSpaceChecker::buildNumIterations(
     if (!MinGreaterMaxRes.isUsable())
       return nullptr;
     Expr *MinGreaterMax = tryBuildCapture(SemaRef, MinGreaterMaxRes.get(),
-                                          Captures, ".min_greater_max")
+                                          Captures, /*RefersToCapture=*/false)
                               .get();
     if (!MinGreaterMax)
       return nullptr;
@@ -8765,8 +9424,12 @@ Expr *OpenMPIterationSpaceChecker::buildNumIterations(
   }
   Expr *UBExpr = *TestIsLessOp ? UBVal : LBVal;
   Expr *LBExpr = *TestIsLessOp ? LBVal : UBVal;
-  Expr *Upper = tryBuildCapture(SemaRef, UBExpr, Captures, ".upper").get();
-  Expr *Lower = tryBuildCapture(SemaRef, LBExpr, Captures, ".lower").get();
+  Expr *Upper =
+      tryBuildCapture(SemaRef, UBExpr, Captures, /*RefersToCapture=*/false)
+          .get();
+  Expr *Lower =
+      tryBuildCapture(SemaRef, LBExpr, Captures, /*RefersToCapture=*/false)
+          .get();
   if (!Upper || !Lower)
     return nullptr;
 
@@ -8835,10 +9498,14 @@ std::pair<Expr *, Expr *> OpenMPIterationSpaceChecker::buildMinMaxValues(
       *TestIsLessOp ? InitDependOnLC.has_value() : CondDependOnLC.has_value();
   bool UBNonRect =
       *TestIsLessOp ? CondDependOnLC.has_value() : InitDependOnLC.has_value();
-  Expr *Lower =
-      LBNonRect ? LBExpr : tryBuildCapture(SemaRef, LBExpr, Captures).get();
-  Expr *Upper =
-      UBNonRect ? UBExpr : tryBuildCapture(SemaRef, UBExpr, Captures).get();
+  Expr *Lower = LBNonRect ? LBExpr
+                          : tryBuildCapture(SemaRef, LBExpr, Captures,
+                                            /*RefersToCapture=*/false)
+                                .get();
+  Expr *Upper = UBNonRect ? UBExpr
+                          : tryBuildCapture(SemaRef, UBExpr, Captures,
+                                            /*RefersToCapture=*/false)
+                                .get();
   if (!Upper || !Lower)
     return std::make_pair(nullptr, nullptr);
 
@@ -8862,7 +9529,8 @@ std::pair<Expr *, Expr *> OpenMPIterationSpaceChecker::buildMinMaxValues(
   if (!Diff.isUsable())
     return std::make_pair(nullptr, nullptr);
 
-  ExprResult NewStep = tryBuildCapture(SemaRef, Step, Captures, ".new_step");
+  ExprResult NewStep =
+      tryBuildCapture(SemaRef, Step, Captures, /*RefersToCapture=*/false);
   if (!NewStep.isUsable())
     return std::make_pair(nullptr, nullptr);
   Diff = SemaRef.BuildBinOp(S, DefaultLoc, BO_Mul, Diff.get(), NewStep.get());
@@ -8951,8 +9619,10 @@ Expr *OpenMPIterationSpaceChecker::buildPreCond(
   // Try to build LB <op> UB, where <op> is <, >, <=, or >=.
   Sema::TentativeAnalysisScope Trap(SemaRef);
 
-  ExprResult NewLB = tryBuildCapture(SemaRef, LB, Captures);
-  ExprResult NewUB = tryBuildCapture(SemaRef, UB, Captures);
+  ExprResult NewLB =
+      tryBuildCapture(SemaRef, LB, Captures, /*RefersToCapture=*/false);
+  ExprResult NewUB =
+      tryBuildCapture(SemaRef, UB, Captures, /*RefersToCapture=*/false);
   if (!NewLB.isUsable() || !NewUB.isUsable())
     return nullptr;
 
@@ -9036,10 +9706,14 @@ Expr *OpenMPIterationSpaceChecker::buildOrderedLoopData(
       !SemaRef.getLangOpts().CPlusPlus)
     return nullptr;
   // Upper - Lower
-  Expr *Upper =
-      *TestIsLessOp ? Cnt : tryBuildCapture(SemaRef, LB, Captures).get();
-  Expr *Lower =
-      *TestIsLessOp ? tryBuildCapture(SemaRef, LB, Captures).get() : Cnt;
+  Expr *Upper = *TestIsLessOp ? Cnt
+                              : tryBuildCapture(SemaRef, LB, Captures,
+                                                /*RefersToCapture=*/false)
+                                    .get();
+  Expr *Lower = *TestIsLessOp ? tryBuildCapture(SemaRef, LB, Captures,
+                                                /*RefersToCapture=*/false)
+                                    .get()
+                              : Cnt;
   if (!Upper || !Lower)
     return nullptr;
 
@@ -9377,7 +10051,8 @@ buildCounterInit(Sema &SemaRef, Scope *S, SourceLocation Loc, ExprResult VarRef,
   // Build 'VarRef = Start.
   ExprResult NewStart = IsNonRectangularLB
                             ? Start.get()
-                            : tryBuildCapture(SemaRef, Start.get(), Captures);
+                            : tryBuildCapture(SemaRef, Start.get(), Captures,
+                                              /*RefersToCapture=*/false);
   if (!NewStart.isUsable())
     return ExprError();
   if (!SemaRef.Context.hasSameType(NewStart.get()->getType(),
@@ -9408,7 +10083,8 @@ static ExprResult buildCounterUpdate(
 
   ExprResult NewStep = Step;
   if (Captures)
-    NewStep = tryBuildCapture(SemaRef, Step.get(), *Captures);
+    NewStep = tryBuildCapture(SemaRef, Step.get(), *Captures,
+                              /*RefersToCapture=*/false);
   if (NewStep.isInvalid())
     return ExprError();
   ExprResult Update =
@@ -9424,7 +10100,8 @@ static ExprResult buildCounterUpdate(
   if (!NewStart.isUsable())
     return ExprError();
   if (Captures && !IsNonRectangularLB)
-    NewStart = tryBuildCapture(SemaRef, Start.get(), *Captures);
+    NewStart = tryBuildCapture(SemaRef, Start.get(), *Captures,
+                               /*RefersToCapture=*/false);
   if (NewStart.isInvalid())
     return ExprError();
 
@@ -9613,6 +10290,19 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
                 DSAStackTy &DSA,
                 SemaOpenMP::VarsWithInheritedDSAType &VarsWithImplicitDSA,
                 OMPLoopBasedDirective::HelperExprs &Built) {
+  if (!isOpenMPLoopTransformationDirective(DKind)) {
+    // Find the innermost CapturedStmt that is not "unknown".
+    // The "unknown" capture statements don't have all the required
+    // parameters for generating loop code.
+    SmallVector<OpenMPDirectiveKind> CaptureRegions;
+    getOpenMPCaptureRegions(CaptureRegions, DKind);
+    size_t LastIdx = CaptureRegions.size();
+    while (LastIdx && CaptureRegions[LastIdx - 1] == OMPD_unknown)
+      --LastIdx;
+    while (LastIdx-- > 1)
+      AStmt = cast<CapturedStmt>(AStmt)->getCapturedStmt();
+  }
+
   unsigned NestedLoopCount = 1;
   bool SupportsNonPerfectlyNested = (SemaRef.LangOpts.OpenMP >= 50) &&
                                     !isOpenMPLoopTransformationDirective(DKind);
@@ -9659,9 +10349,10 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
   llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
   unsigned NumLoops = std::max(OrderedLoopCount, NestedLoopCount);
   SmallVector<LoopIterationSpace, 4> IterSpaces(NumLoops);
+  int StripCaptured = isOpenMPLoopTransformationDirective(DKind) ? 1 : -1;
   if (!OMPLoopBasedDirective::doForAllLoops(
-          AStmt->IgnoreContainers(!isOpenMPLoopTransformationDirective(DKind)),
-          SupportsNonPerfectlyNested, NumLoops,
+          AStmt->stripContainers(StripCaptured), SupportsNonPerfectlyNested,
+          NumLoops,
           [DKind, &SemaRef, &DSA, NumLoops, NestedLoopCount,
            CollapseLoopCountExpr, OrderedLoopCountExpr, &VarsWithImplicitDSA,
            &IterSpaces, &Captures,
@@ -9834,8 +10525,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
   bool IsConstant = LastIteration.get()->isIntegerConstantExpr(SemaRef.Context);
   ExprResult CalcLastIteration;
   if (!IsConstant) {
-    ExprResult SaveRef =
-        tryBuildCapture(SemaRef, LastIteration.get(), Captures);
+    ExprResult SaveRef = tryBuildCapture(SemaRef, LastIteration.get(), Captures,
+                                         /*RefersToCapture=*/false);
     LastIteration = SaveRef;
 
     // Prepare SaveRef + 1.
@@ -10378,7 +11069,7 @@ StmtResult SemaOpenMP::ActOnOpenMPSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS = setBranchProtectedScope(SemaRef, OMPD_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_simd, AStmt);
 
   assert(isa<CapturedStmt>(AStmt) && "Captured statement expected");
   OMPLoopBasedDirective::HelperExprs B;
@@ -10386,7 +11077,7 @@ StmtResult SemaOpenMP::ActOnOpenMPSimdDirective(
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_simd, getCollapseNumberExpr(Clauses), getOrderedNumberExpr(Clauses),
-      CS, SemaRef, *DSAStack, VarsWithImplicitDSA, B);
+      AStmt, SemaRef, *DSAStack, VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
 
@@ -10432,7 +11123,7 @@ StmtResult SemaOpenMP::ActOnOpenMPForSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS = setBranchProtectedScope(SemaRef, OMPD_for_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_for_simd, AStmt);
 
   assert(isa<CapturedStmt>(AStmt) && "Captured statement expected");
   OMPLoopBasedDirective::HelperExprs B;
@@ -10440,7 +11131,7 @@ StmtResult SemaOpenMP::ActOnOpenMPForSimdDirective(
   // define the nested loops number.
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_for_simd, getCollapseNumberExpr(Clauses),
-                      getOrderedNumberExpr(Clauses), CS, SemaRef, *DSAStack,
+                      getOrderedNumberExpr(Clauses), AStmt, SemaRef, *DSAStack,
                       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -10640,14 +11331,14 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsGenericLoopDirective(
   if (checkGenericLoopLastprivate(SemaRef, Clauses, OMPD_teams_loop, DSAStack))
     return StmtError();
 
-  CapturedStmt *CS = setBranchProtectedScope(SemaRef, OMPD_teams_loop, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_teams_loop, AStmt);
 
   OMPLoopDirective::HelperExprs B;
   // In presence of clause 'collapse', it will define the nested loops number.
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_teams_loop, getCollapseNumberExpr(Clauses),
-                      /*OrderedLoopCountExpr=*/nullptr, CS, SemaRef, *DSAStack,
-                      VarsWithImplicitDSA, B);
+                      /*OrderedLoopCountExpr=*/nullptr, AStmt, SemaRef,
+                      *DSAStack, VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
 
@@ -10673,15 +11364,14 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsGenericLoopDirective(
                                   DSAStack))
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_target_teams_loop, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_target_teams_loop, AStmt);
 
   OMPLoopDirective::HelperExprs B;
   // In presence of clause 'collapse', it will define the nested loops number.
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_target_teams_loop, getCollapseNumberExpr(Clauses),
-                      /*OrderedLoopCountExpr=*/nullptr, CS, SemaRef, *DSAStack,
-                      VarsWithImplicitDSA, B);
+                      /*OrderedLoopCountExpr=*/nullptr, AStmt, SemaRef,
+                      *DSAStack, VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
 
@@ -10706,15 +11396,14 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelGenericLoopDirective(
                                   DSAStack))
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_parallel_loop, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_parallel_loop, AStmt);
 
   OMPLoopDirective::HelperExprs B;
   // In presence of clause 'collapse', it will define the nested loops number.
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_parallel_loop, getCollapseNumberExpr(Clauses),
-                      /*OrderedLoopCountExpr=*/nullptr, CS, SemaRef, *DSAStack,
-                      VarsWithImplicitDSA, B);
+                      /*OrderedLoopCountExpr=*/nullptr, AStmt, SemaRef,
+                      *DSAStack, VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
 
@@ -10738,15 +11427,14 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetParallelGenericLoopDirective(
                                   DSAStack))
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_target_parallel_loop, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_target_parallel_loop, AStmt);
 
   OMPLoopDirective::HelperExprs B;
   // In presence of clause 'collapse', it will define the nested loops number.
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_target_parallel_loop, getCollapseNumberExpr(Clauses),
-                      /*OrderedLoopCountExpr=*/nullptr, CS, SemaRef, *DSAStack,
-                      VarsWithImplicitDSA, B);
+                      /*OrderedLoopCountExpr=*/nullptr, AStmt, SemaRef,
+                      *DSAStack, VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
 
@@ -10902,15 +11590,14 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelForSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_parallel_for_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_parallel_for_simd, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' or 'ordered' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_parallel_for_simd, getCollapseNumberExpr(Clauses),
-                      getOrderedNumberExpr(Clauses), CS, SemaRef, *DSAStack,
+                      getOrderedNumberExpr(Clauses), AStmt, SemaRef, *DSAStack,
                       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -12992,15 +13679,14 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetParallelForDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_target_parallel_for, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_target_parallel_for, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' or 'ordered' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_target_parallel_for, getCollapseNumberExpr(Clauses),
-                      getOrderedNumberExpr(Clauses), CS, SemaRef, *DSAStack,
+                      getOrderedNumberExpr(Clauses), AStmt, SemaRef, *DSAStack,
                       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -13285,8 +13971,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTaskLoopSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_taskloop_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_taskloop_simd, AStmt);
 
   assert(isa<CapturedStmt>(AStmt) && "Captured statement expected");
   OMPLoopBasedDirective::HelperExprs B;
@@ -13294,8 +13979,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTaskLoopSimdDirective(
   // define the nested loops number.
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_taskloop_simd, getCollapseNumberExpr(Clauses),
-                      /*OrderedLoopCountExpr=*/nullptr, CS, SemaRef, *DSAStack,
-                      VarsWithImplicitDSA, B);
+                      /*OrderedLoopCountExpr=*/nullptr, AStmt, SemaRef,
+                      *DSAStack, VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
 
@@ -13402,8 +14087,7 @@ StmtResult SemaOpenMP::ActOnOpenMPMasterTaskLoopSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_master_taskloop_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_master_taskloop_simd, AStmt);
 
   assert(isa<CapturedStmt>(AStmt) && "Captured statement expected");
   OMPLoopBasedDirective::HelperExprs B;
@@ -13411,8 +14095,8 @@ StmtResult SemaOpenMP::ActOnOpenMPMasterTaskLoopSimdDirective(
   // define the nested loops number.
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_master_taskloop_simd, getCollapseNumberExpr(Clauses),
-                      /*OrderedLoopCountExpr=*/nullptr, CS, SemaRef, *DSAStack,
-                      VarsWithImplicitDSA, B);
+                      /*OrderedLoopCountExpr=*/nullptr, AStmt, SemaRef,
+                      *DSAStack, VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
 
@@ -13443,8 +14127,7 @@ StmtResult SemaOpenMP::ActOnOpenMPMaskedTaskLoopSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_masked_taskloop_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_masked_taskloop_simd, AStmt);
 
   assert(isa<CapturedStmt>(AStmt) && "Captured statement expected");
   OMPLoopBasedDirective::HelperExprs B;
@@ -13452,8 +14135,8 @@ StmtResult SemaOpenMP::ActOnOpenMPMaskedTaskLoopSimdDirective(
   // define the nested loops number.
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_masked_taskloop_simd, getCollapseNumberExpr(Clauses),
-                      /*OrderedLoopCountExpr=*/nullptr, CS, SemaRef, *DSAStack,
-                      VarsWithImplicitDSA, B);
+                      /*OrderedLoopCountExpr=*/nullptr, AStmt, SemaRef,
+                      *DSAStack, VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
 
@@ -13484,15 +14167,14 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelMasterTaskLoopDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_parallel_master_taskloop, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_parallel_master_taskloop, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' or 'ordered' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_parallel_master_taskloop, getCollapseNumberExpr(Clauses),
-      /*OrderedLoopCountExpr=*/nullptr, CS, SemaRef, *DSAStack,
+      /*OrderedLoopCountExpr=*/nullptr, AStmt, SemaRef, *DSAStack,
       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -13523,15 +14205,14 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelMaskedTaskLoopDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_parallel_masked_taskloop, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_parallel_masked_taskloop, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' or 'ordered' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_parallel_masked_taskloop, getCollapseNumberExpr(Clauses),
-      /*OrderedLoopCountExpr=*/nullptr, CS, SemaRef, *DSAStack,
+      /*OrderedLoopCountExpr=*/nullptr, AStmt, SemaRef, *DSAStack,
       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -13562,15 +14243,14 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelMasterTaskLoopSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS = setBranchProtectedScope(
-      SemaRef, OMPD_parallel_master_taskloop_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_parallel_master_taskloop_simd, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' or 'ordered' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_parallel_master_taskloop_simd, getCollapseNumberExpr(Clauses),
-      /*OrderedLoopCountExpr=*/nullptr, CS, SemaRef, *DSAStack,
+      /*OrderedLoopCountExpr=*/nullptr, AStmt, SemaRef, *DSAStack,
       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -13602,15 +14282,14 @@ StmtResult SemaOpenMP::ActOnOpenMPParallelMaskedTaskLoopSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS = setBranchProtectedScope(
-      SemaRef, OMPD_parallel_masked_taskloop_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_parallel_masked_taskloop_simd, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' or 'ordered' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_parallel_masked_taskloop_simd, getCollapseNumberExpr(Clauses),
-      /*OrderedLoopCountExpr=*/nullptr, CS, SemaRef, *DSAStack,
+      /*OrderedLoopCountExpr=*/nullptr, AStmt, SemaRef, *DSAStack,
       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -13668,15 +14347,14 @@ StmtResult SemaOpenMP::ActOnOpenMPDistributeParallelForDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_distribute_parallel_for, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_distribute_parallel_for, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_distribute_parallel_for, getCollapseNumberExpr(Clauses),
-      nullptr /*ordered not a clause on distribute*/, CS, SemaRef, *DSAStack,
+      nullptr /*ordered not a clause on distribute*/, AStmt, SemaRef, *DSAStack,
       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -13695,15 +14373,14 @@ StmtResult SemaOpenMP::ActOnOpenMPDistributeParallelForSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS = setBranchProtectedScope(
-      SemaRef, OMPD_distribute_parallel_for_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_distribute_parallel_for_simd, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_distribute_parallel_for_simd, getCollapseNumberExpr(Clauses),
-      nullptr /*ordered not a clause on distribute*/, CS, SemaRef, *DSAStack,
+      nullptr /*ordered not a clause on distribute*/, AStmt, SemaRef, *DSAStack,
       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -13724,15 +14401,14 @@ StmtResult SemaOpenMP::ActOnOpenMPDistributeSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_distribute_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_distribute_simd, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_distribute_simd, getCollapseNumberExpr(Clauses),
-                      nullptr /*ordered not a clause on distribute*/, CS,
+                      nullptr /*ordered not a clause on distribute*/, AStmt,
                       SemaRef, *DSAStack, VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -13753,15 +14429,14 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetParallelForSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_target_parallel_for_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_target_parallel_for_simd, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' or 'ordered' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_target_parallel_for_simd, getCollapseNumberExpr(Clauses),
-      getOrderedNumberExpr(Clauses), CS, SemaRef, *DSAStack,
+      getOrderedNumberExpr(Clauses), AStmt, SemaRef, *DSAStack,
       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -13782,14 +14457,14 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS = setBranchProtectedScope(SemaRef, OMPD_target_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_target_simd, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' with number of loops, it will define the
   // nested loops number.
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_target_simd, getCollapseNumberExpr(Clauses),
-                      getOrderedNumberExpr(Clauses), CS, SemaRef, *DSAStack,
+                      getOrderedNumberExpr(Clauses), AStmt, SemaRef, *DSAStack,
                       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -13810,15 +14485,14 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_teams_distribute, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_teams_distribute, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_teams_distribute, getCollapseNumberExpr(Clauses),
-                      nullptr /*ordered not a clause on distribute*/, CS,
+                      nullptr /*ordered not a clause on distribute*/, AStmt,
                       SemaRef, *DSAStack, VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -13838,15 +14512,14 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_teams_distribute_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_teams_distribute_simd, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_teams_distribute_simd, getCollapseNumberExpr(Clauses),
-      nullptr /*ordered not a clause on distribute*/, CS, SemaRef, *DSAStack,
+      nullptr /*ordered not a clause on distribute*/, AStmt, SemaRef, *DSAStack,
       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -13869,15 +14542,15 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeParallelForSimdDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS = setBranchProtectedScope(
-      SemaRef, OMPD_teams_distribute_parallel_for_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_teams_distribute_parallel_for_simd,
+                          AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_teams_distribute_parallel_for_simd, getCollapseNumberExpr(Clauses),
-      nullptr /*ordered not a clause on distribute*/, CS, SemaRef, *DSAStack,
+      nullptr /*ordered not a clause on distribute*/, AStmt, SemaRef, *DSAStack,
       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -13900,15 +14573,14 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeParallelForDirective(
   if (!AStmt)
     return StmtError();
 
-  CapturedStmt *CS = setBranchProtectedScope(
-      SemaRef, OMPD_teams_distribute_parallel_for, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_teams_distribute_parallel_for, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_teams_distribute_parallel_for, getCollapseNumberExpr(Clauses),
-      nullptr /*ordered not a clause on distribute*/, CS, SemaRef, *DSAStack,
+      nullptr /*ordered not a clause on distribute*/, AStmt, SemaRef, *DSAStack,
       VarsWithImplicitDSA, B);
 
   if (NestedLoopCount == 0)
@@ -13971,15 +14643,14 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeDirective(
           *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
     return StmtError();
 
-  CapturedStmt *CS =
-      setBranchProtectedScope(SemaRef, OMPD_target_teams_distribute, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_target_teams_distribute, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_target_teams_distribute, getCollapseNumberExpr(Clauses),
-      nullptr /*ordered not a clause on distribute*/, CS, SemaRef, *DSAStack,
+      nullptr /*ordered not a clause on distribute*/, AStmt, SemaRef, *DSAStack,
       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -14003,15 +14674,15 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeParallelForDirective(
           *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
     return StmtError();
 
-  CapturedStmt *CS = setBranchProtectedScope(
-      SemaRef, OMPD_target_teams_distribute_parallel_for, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_target_teams_distribute_parallel_for,
+                          AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_target_teams_distribute_parallel_for, getCollapseNumberExpr(Clauses),
-      nullptr /*ordered not a clause on distribute*/, CS, SemaRef, *DSAStack,
+      nullptr /*ordered not a clause on distribute*/, AStmt, SemaRef, *DSAStack,
       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -14036,7 +14707,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeParallelForSimdDirective(
           *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
     return StmtError();
 
-  CapturedStmt *CS = setBranchProtectedScope(
+  setBranchProtectedScope(
       SemaRef, OMPD_target_teams_distribute_parallel_for_simd, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
@@ -14045,7 +14716,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeParallelForSimdDirective(
   unsigned NestedLoopCount =
       checkOpenMPLoop(OMPD_target_teams_distribute_parallel_for_simd,
                       getCollapseNumberExpr(Clauses),
-                      nullptr /*ordered not a clause on distribute*/, CS,
+                      nullptr /*ordered not a clause on distribute*/, AStmt,
                       SemaRef, *DSAStack, VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -14072,15 +14743,14 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeSimdDirective(
           *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
     return StmtError();
 
-  CapturedStmt *CS = setBranchProtectedScope(
-      SemaRef, OMPD_target_teams_distribute_simd, AStmt);
+  setBranchProtectedScope(SemaRef, OMPD_target_teams_distribute_simd, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
   // In presence of clause 'collapse' with number of loops, it will
   // define the nested loops number.
   unsigned NestedLoopCount = checkOpenMPLoop(
       OMPD_target_teams_distribute_simd, getCollapseNumberExpr(Clauses),
-      nullptr /*ordered not a clause on distribute*/, CS, SemaRef, *DSAStack,
+      nullptr /*ordered not a clause on distribute*/, AStmt, SemaRef, *DSAStack,
       VarsWithImplicitDSA, B);
   if (NestedLoopCount == 0)
     return StmtError();
@@ -15238,16 +15908,22 @@ OMPClause *SemaOpenMP::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind,
 static OpenMPDirectiveKind getOpenMPCaptureRegionForClause(
     OpenMPDirectiveKind DKind, OpenMPClauseKind CKind, unsigned OpenMPVersion,
     OpenMPDirectiveKind NameModifier = OMPD_unknown) {
+  // DKind may be a compound directive. This function is called
+  // from ActOnXyxClause, which are called before directive splitting.
   assert(isAllowedClauseForDirective(DKind, CKind, OpenMPVersion) &&
          "Invalid directive with CKind-clause");
 
-  // Invalid modifier will be diagnosed separately, just return OMPD_unknown.
-  if (NameModifier != OMPD_unknown &&
-      !isAllowedClauseForDirective(NameModifier, CKind, OpenMPVersion))
-    return OMPD_unknown;
-
   ArrayRef<OpenMPDirectiveKind> Leafs = getLeafConstructsOrSelf(DKind);
 
+  // Invalid modifier will be diagnosed separately, just return OMPD_unknown.
+  if (NameModifier != OMPD_unknown) {
+    if (!isAllowedClauseForDirective(NameModifier, CKind, OpenMPVersion))
+      return OMPD_unknown;
+    if (!llvm::is_contained(Leafs, NameModifier))
+      return OMPD_unknown;
+    DKind = NameModifier;
+  }
+
   // [5.2:341:24-30]
   // If the clauses have expressions on them, such as for various clauses where
   // the argument of the clause is an expression, or lower-bound, length, or
@@ -15304,47 +15980,42 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause(
     break;
   }
 
-  // If none of the special cases above applied, and DKind is a capturing
-  // directive, find the innermost enclosing leaf construct that allows the
-  // clause, and returns the corresponding capture region.
+  if (!isOpenMPCapturingDirective(DKind, /*OrderedIsStandalone=*/false))
+    return OMPD_unknown;
 
-  auto GetEnclosingRegion = [&](int EndIdx, OpenMPClauseKind Clause) {
-    // Find the index in "Leafs" of the last leaf that allows the given
-    // clause. The search will only include indexes [0, EndIdx).
-    // EndIdx may be set to the index of the NameModifier, if present.
-    int InnermostIdx = [&]() {
-      for (int I = EndIdx - 1; I >= 0; --I) {
-        if (isAllowedClauseForDirective(Leafs[I], Clause, OpenMPVersion))
-          return I;
-      }
-      return -1;
-    }();
+  // Find the outermost leaf that allows the clause.
+  size_t Outermost = Leafs.size();
 
-    // Find the nearest enclosing capture region.
-    SmallVector<OpenMPDirectiveKind, 2> Regions;
-    for (int I = InnermostIdx - 1; I >= 0; --I) {
-      if (!isOpenMPCapturingDirective(Leafs[I]))
-        continue;
-      Regions.clear();
-      getOpenMPCaptureRegions(Regions, Leafs[I]);
-      if (Regions[0] != OMPD_unknown)
-        return Regions.back();
-    }
+  if (NameModifier == OMPD_unknown) {
+    auto F = llvm::find_if(Leafs, [=](OpenMPDirectiveKind Leaf) {
+      return isAllowedClauseForDirective(Leaf, CKind, OpenMPVersion);
+    });
+    assert(F != Leafs.end() && "No leafs allow the clause");
+    Outermost = &*F - &Leafs.front();
+  } else {
+    auto F = llvm::find_if(
+        Leafs, [=](OpenMPDirectiveKind Leaf) { return Leaf == NameModifier; });
+    assert(F != Leafs.end() && "Could not find name modifier");
+    Outermost = &*F - &Leafs.front();
+  }
+  if (Outermost == 0) {
+    // The capturing region is not a part of this directive.
     return OMPD_unknown;
-  };
-
-  if (isOpenMPCapturingDirective(DKind)) {
-    auto GetLeafIndex = [&](OpenMPDirectiveKind Dir) {
-      for (int I = 0, E = Leafs.size(); I != E; ++I) {
-        if (Leafs[I] == Dir)
-          return I + 1;
-      }
-      return 0;
-    };
+  }
 
-    int End = NameModifier == OMPD_unknown ? Leafs.size()
-                                           : GetLeafIndex(NameModifier);
-    return GetEnclosingRegion(End, CKind);
+  // Search outwards for the first capturing region that is not
+  // OMPD_unknown. It may correspond to a different leaf.
+  for (size_t LeafIdx = Outermost; LeafIdx > 0; --LeafIdx) {
+    OpenMPDirectiveKind Leaf = Leafs[LeafIdx - 1];
+    if (!isOpenMPCapturingDirective(Leaf, /*OrderedIsStandalone=*/false))
+      continue;
+    SmallVector<OpenMPDirectiveKind, 2> Regions;
+    getOpenMPCaptureRegions(Regions, Leaf);
+    auto F =
+        std::find_if(Regions.rbegin(), Regions.rend(),
+                     [](OpenMPDirectiveKind R) { return R != OMPD_unknown; });
+    if (F != Regions.rend())
+      return *F;
   }
 
   return OMPD_unknown;
@@ -15369,13 +16040,6 @@ OMPClause *SemaOpenMP::ActOnOpenMPIfClause(
     OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
     CaptureRegion = getOpenMPCaptureRegionForClause(
         DKind, OMPC_if, getLangOpts().OpenMP, NameModifier);
-    if (CaptureRegion != OMPD_unknown &&
-        !SemaRef.CurContext->isDependentContext()) {
-      ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-      llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
-      ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
-      HelperValStmt = buildPreInits(getASTContext(), Captures);
-    }
   }
 
   return new (getASTContext())
@@ -15402,13 +16066,6 @@ OMPClause *SemaOpenMP::ActOnOpenMPFinalClause(Expr *Condition,
     OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
     CaptureRegion = getOpenMPCaptureRegionForClause(DKind, OMPC_final,
                                                     getLangOpts().OpenMP);
-    if (CaptureRegion != OMPD_unknown &&
-        !SemaRef.CurContext->isDependentContext()) {
-      ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-      llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
-      ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
-      HelperValStmt = buildPreInits(getASTContext(), Captures);
-    }
   }
 
   return new (getASTContext()) OMPFinalClause(
@@ -15491,12 +16148,16 @@ isNonNegativeIntegerValue(Expr *&ValExpr, Sema &SemaRef, OpenMPClauseKind CKind,
       return true;
     *CaptureRegion =
         getOpenMPCaptureRegionForClause(DKind, CKind, SemaRef.LangOpts.OpenMP);
-    if (*CaptureRegion != OMPD_unknown &&
-        !SemaRef.CurContext->isDependentContext()) {
-      ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-      llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
-      ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
-      *HelperValStmt = buildPreInits(SemaRef.Context, Captures);
+    if (CKind == OMPC_num_teams || CKind == OMPC_thread_limit) {
+      if (*CaptureRegion != OMPD_unknown &&
+          !SemaRef.CurContext->isDependentContext()) {
+        ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
+        llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
+        ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures,
+                                  /*RefersToCapture=*/true)
+                      .get();
+        *HelperValStmt = buildPreInits(SemaRef.Context, Captures);
+      }
     }
   }
   return true;
@@ -15518,13 +16179,6 @@ OMPClause *SemaOpenMP::ActOnOpenMPNumThreadsClause(Expr *NumThreads,
   OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
   OpenMPDirectiveKind CaptureRegion = getOpenMPCaptureRegionForClause(
       DKind, OMPC_num_threads, getLangOpts().OpenMP);
-  if (CaptureRegion != OMPD_unknown &&
-      !SemaRef.CurContext->isDependentContext()) {
-    ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-    llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
-    ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
-    HelperValStmt = buildPreInits(getASTContext(), Captures);
-  }
 
   return new (getASTContext()) OMPNumThreadsClause(
       ValExpr, HelperValStmt, CaptureRegion, StartLoc, LParenLoc, EndLoc);
@@ -16384,10 +17038,6 @@ OMPClause *SemaOpenMP::ActOnOpenMPScheduleClause(
                      DSAStack->getCurrentDirective(), OMPC_schedule,
                      getLangOpts().OpenMP) != OMPD_unknown &&
                  !SemaRef.CurContext->isDependentContext()) {
-        ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-        llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
-        ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
-        HelperValStmt = buildPreInits(getASTContext(), Captures);
       }
     }
   }
@@ -16890,13 +17540,6 @@ OMPClause *SemaOpenMP::ActOnOpenMPNovariantsClause(Expr *Condition,
     OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
     CaptureRegion = getOpenMPCaptureRegionForClause(DKind, OMPC_novariants,
                                                     getLangOpts().OpenMP);
-    if (CaptureRegion != OMPD_unknown &&
-        !SemaRef.CurContext->isDependentContext()) {
-      ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-      llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
-      ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
-      HelperValStmt = buildPreInits(getASTContext(), Captures);
-    }
   }
 
   return new (getASTContext()) OMPNovariantsClause(
@@ -16922,13 +17565,6 @@ OMPClause *SemaOpenMP::ActOnOpenMPNocontextClause(Expr *Condition,
     OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
     CaptureRegion = getOpenMPCaptureRegionForClause(DKind, OMPC_nocontext,
                                                     getLangOpts().OpenMP);
-    if (CaptureRegion != OMPD_unknown &&
-        !SemaRef.CurContext->isDependentContext()) {
-      ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-      llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
-      ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
-      HelperValStmt = buildPreInits(getASTContext(), Captures);
-    }
   }
 
   return new (getASTContext()) OMPNocontextClause(
@@ -16945,13 +17581,6 @@ OMPClause *SemaOpenMP::ActOnOpenMPFilterClause(Expr *ThreadID,
   OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
   OpenMPDirectiveKind CaptureRegion =
       getOpenMPCaptureRegionForClause(DKind, OMPC_filter, getLangOpts().OpenMP);
-  if (CaptureRegion != OMPD_unknown &&
-      !SemaRef.CurContext->isDependentContext()) {
-    ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-    llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
-    ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
-    HelperValStmt = buildPreInits(getASTContext(), Captures);
-  }
 
   return new (getASTContext()) OMPFilterClause(
       ValExpr, HelperValStmt, CaptureRegion, StartLoc, LParenLoc, EndLoc);
@@ -19038,14 +19667,22 @@ OMPClause *SemaOpenMP::ActOnOpenMPReductionClause(
   // worksharing-loop construct, a worksharing-loop SIMD construct, a simd
   // construct, a parallel worksharing-loop construct or a parallel
   // worksharing-loop SIMD construct.
-  if (Modifier == OMPC_REDUCTION_inscan &&
-      (DSAStack->getCurrentDirective() != OMPD_for &&
-       DSAStack->getCurrentDirective() != OMPD_for_simd &&
-       DSAStack->getCurrentDirective() != OMPD_simd &&
-       DSAStack->getCurrentDirective() != OMPD_parallel_for &&
-       DSAStack->getCurrentDirective() != OMPD_parallel_for_simd)) {
-    Diag(ModifierLoc, diag::err_omp_wrong_inscan_reduction);
-    return nullptr;
+  // [5.2:136:1-4] A reduction clause with the inscan reduction-modifier may
+  // only appear on a worksharing-loop construct, a simd construct or a
+  // combined or composite construct for which any of the aforementioned
+  // constructs is a constituent construct and distribute is not a constituent
+  // construct.
+  if (Modifier == OMPC_REDUCTION_inscan) {
+    SmallVector<OpenMPDirectiveKind, 4> LeafOrComposite;
+    ArrayRef<OpenMPDirectiveKind> CurrentLOC = getLeafOrCompositeConstructs(
+        DSAStack->getCurrentDirective(), LeafOrComposite);
+    bool Valid = llvm::any_of(CurrentLOC, [](OpenMPDirectiveKind DK) {
+      return llvm::is_contained({OMPD_for, OMPD_simd, OMPD_for_simd}, DK);
+    });
+    if (!Valid) {
+      Diag(ModifierLoc, diag::err_omp_wrong_inscan_reduction);
+      return nullptr;
+    }
   }
 
   ReductionData RD(VarList.size(), Modifier);
@@ -20113,13 +20750,6 @@ OMPClause *SemaOpenMP::ActOnOpenMPDeviceClause(
   OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
   OpenMPDirectiveKind CaptureRegion =
       getOpenMPCaptureRegionForClause(DKind, OMPC_device, getLangOpts().OpenMP);
-  if (CaptureRegion != OMPD_unknown &&
-      !SemaRef.CurContext->isDependentContext()) {
-    ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-    llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
-    ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
-    HelperValStmt = buildPreInits(getASTContext(), Captures);
-  }
 
   return new (getASTContext())
       OMPDeviceClause(Modifier, ValExpr, HelperValStmt, CaptureRegion, StartLoc,
@@ -21937,9 +22567,11 @@ SemaOpenMP::DeclGroupPtrTy SemaOpenMP::ActOnOpenMPDeclareMapperDirective(
   // Build expressions for implicit maps of data members with 'default'
   // mappers.
   SmallVector<OMPClause *, 4> ClausesWithImplicit(Clauses);
-  if (getLangOpts().OpenMP >= 50)
-    processImplicitMapsWithDefaultMappers(SemaRef, DSAStack,
-                                          ClausesWithImplicit);
+  for (OMPClause *C : Clauses) {
+    OMPClause *DM = processImplicitMapWithDefaultMappers(SemaRef, DSAStack, C);
+    if (DM)
+      ClausesWithImplicit.push_back(DM);
+  }
   auto *DMD = OMPDeclareMapperDecl::Create(getASTContext(), DC, StartLoc, Name,
                                            MapperType, VN, ClausesWithImplicit,
                                            PrevDMD);
@@ -22027,13 +22659,19 @@ OMPClause *SemaOpenMP::ActOnOpenMPNumTeamsClause(ArrayRef<Expr *> VarList,
   SmallVector<Expr *, 3> Vars;
   for (Expr *ValExpr : VarList) {
     ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-    ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
+    ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures,
+                              /*RefersToCapture=*/true, ".nt",
+                              /*MakeOMPCapturedExprDecl=*/false)
+                  .get();
     Vars.push_back(ValExpr);
   }
 
-  Stmt *PreInit = buildPreInits(getASTContext(), Captures);
-  return OMPNumTeamsClause::Create(getASTContext(), CaptureRegion, StartLoc,
-                                   LParenLoc, EndLoc, Vars, PreInit);
+  OMPClause *Clause =
+      OMPNumTeamsClause::Create(getASTContext(), CaptureRegion, StartLoc,
+                                LParenLoc, EndLoc, Vars, /*PreInit=*/nullptr);
+  if (Stmt *PreInit = buildPreInits(getASTContext(), Captures))
+    DSAStack->addPreInit(Clause, PreInit);
+  return Clause;
 }
 
 OMPClause *SemaOpenMP::ActOnOpenMPThreadLimitClause(ArrayRef<Expr *> VarList,
@@ -22063,13 +22701,19 @@ OMPClause *SemaOpenMP::ActOnOpenMPThreadLimitClause(ArrayRef<Expr *> VarList,
   SmallVector<Expr *, 3> Vars;
   for (Expr *ValExpr : VarList) {
     ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-    ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
+    ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures,
+                              /*RefersToCapture=*/true, ".tl",
+                              /*MakeOMPCapturedExprDecl=*/false)
+                  .get();
     Vars.push_back(ValExpr);
   }
 
-  Stmt *PreInit = buildPreInits(getASTContext(), Captures);
-  return OMPThreadLimitClause::Create(getASTContext(), CaptureRegion, StartLoc,
-                                      LParenLoc, EndLoc, Vars, PreInit);
+  OMPClause *Clause = OMPThreadLimitClause::Create(
+      getASTContext(), CaptureRegion, StartLoc, LParenLoc, EndLoc, Vars,
+      /*PreInit=*/nullptr);
+  if (Stmt *PreInit = buildPreInits(getASTContext(), Captures))
+    DSAStack->addPreInit(Clause, PreInit);
+  return Clause;
 }
 
 OMPClause *SemaOpenMP::ActOnOpenMPPriorityClause(Expr *Priority,
@@ -22276,14 +22920,6 @@ OMPClause *SemaOpenMP::ActOnOpenMPDistScheduleClause(
               << "dist_schedule" << ChunkSize->getSourceRange();
           return nullptr;
         }
-      } else if (getOpenMPCaptureRegionForClause(
-                     DSAStack->getCurrentDirective(), OMPC_dist_schedule,
-                     getLangOpts().OpenMP) != OMPD_unknown &&
-                 !SemaRef.CurContext->isDependentContext()) {
-        ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-        llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
-        ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
-        HelperValStmt = buildPreInits(getASTContext(), Captures);
       }
     }
   }
@@ -23188,6 +23824,123 @@ StmtResult SemaOpenMP::ActOnOpenMPScopeDirective(ArrayRef<OMPClause *> Clauses,
                                    AStmt);
 }
 
+StmtResult SemaOpenMP::ActOnOpenMPCompoundBlockDirective(
+    OpenMPDirectiveKind DKind, ArrayRef<OMPClause *> Clauses, Stmt *AStmt,
+    OpenMPDirectiveKind CancelRegion, const DeclarationNameInfo &DirName,
+    SourceLocation StartLoc, SourceLocation EndLoc) {
+  if (!AStmt && isOpenMPDirectiveWithStatement(DKind))
+    return StmtError();
+
+  // XXX: metadirective, nothing, ordered, scan, interop dont have a statement,
+  // but are capturing...
+  if (AStmt && isOpenMPCapturingDirective(DKind, /*OrderedIsStandalone=*/false))
+    setBranchProtectedScope(SemaRef, DKind, AStmt);
+
+  Expr *ReductionRef = nullptr;
+  assert(!isOpenMPLoopDirective(DKind) && "Unexpected loop directive");
+  if (DKind == OMPD_taskgroup || isOpenMPParallelDirective(DKind) ||
+      isOpenMPWorksharingDirective(DKind))
+    ReductionRef = DSAStack->getTaskgroupReductionRef();
+
+  return OMPCompoundBlockDirective::Create(
+      getASTContext(), StartLoc, EndLoc, DKind, Clauses, AStmt, ReductionRef,
+      DSAStack->isCancelRegion(), CancelRegion, DirName);
+}
+
+StmtResult SemaOpenMP::ActOnOpenMPCompoundLoopDirective(
+    OpenMPDirectiveKind DKind, ArrayRef<OMPClause *> Clauses, Stmt *AStmt,
+    SourceLocation StartLoc, SourceLocation EndLoc,
+    VarsWithInheritedDSAType &VarsWithImplicitDSA) {
+  if (!AStmt)
+    return StmtError();
+
+  if (isOpenMPLoopTransformationDirective(DKind)) {
+    switch (DKind) {
+    case OMPD_tile:
+      return ActOnOpenMPTileDirective(Clauses, AStmt, StartLoc, EndLoc);
+    case OMPD_unroll:
+      return ActOnOpenMPUnrollDirective(Clauses, AStmt, StartLoc, EndLoc);
+    case OMPD_reverse:
+      return ActOnOpenMPReverseDirective(AStmt, StartLoc, EndLoc);
+    case OMPD_interchange:
+      return ActOnOpenMPInterchangeDirective(Clauses, AStmt, StartLoc, EndLoc);
+    default:
+      llvm_unreachable("Unexpected loop-transformatinal directive");
+    }
+  }
+
+  ArrayRef<OpenMPDirectiveKind> Leafs = getLeafConstructsOrSelf(DKind);
+
+  if (isOpenMPGenericLoopDirective(DKind)) {
+    // OpenMP 5.1 [2.11.7, loop construct, Restrictions]
+    // A list item may not appear in a lastprivate clause unless it is the
+    // loop iteration variable of a loop that is associated with the construct.
+    if (checkGenericLoopLastprivate(SemaRef, Clauses, DKind, DSAStack))
+      return StmtError();
+  }
+
+  CapturedStmt *CS = [&]() {
+    switch (DKind) {
+    default:
+      setBranchProtectedScope(SemaRef, DKind, AStmt);
+      LLVM_FALLTHROUGH;
+    case OMPD_distribute:
+    case OMPD_for:
+    case OMPD_taskloop:
+      return cast<CapturedStmt>(AStmt);
+    }
+  }();
+
+  // assert(isa<CapturedStmt>(AStmt) && "Captured statement expected");
+  OMPLoopBasedDirective::HelperExprs B;
+  // In presence of clause 'collapse' or 'ordered' with number of loops, it will
+  // define the nested loops number.
+  unsigned NestedLoopCount = checkOpenMPLoop(
+      DKind, getCollapseNumberExpr(Clauses), getOrderedNumberExpr(Clauses), CS,
+      SemaRef, *DSAStack, VarsWithImplicitDSA, B);
+  if (NestedLoopCount == 0)
+    return StmtError();
+
+  assert((SemaRef.CurContext->isDependentContext() || B.builtAll()) &&
+         "omp for loop exprs were not built");
+
+  if (finishLinearClauses(SemaRef, Clauses, B, DSAStack))
+    return StmtError();
+
+  if (isOpenMPSimdDirective(DKind)) {
+    if (checkSimdlenSafelenSpecified(SemaRef, Clauses))
+      return StmtError();
+  }
+
+  if (llvm::is_contained(Leafs, OMPD_taskloop)) {
+    // OpenMP, [2.9.2 taskloop Construct, Restrictions]
+    // The grainsize clause and num_tasks clause are mutually exclusive and may
+    // not appear on the same taskloop directive.
+    if (checkMutuallyExclusiveClauses(SemaRef, Clauses,
+                                      {OMPC_grainsize, OMPC_num_tasks}))
+      return StmtError();
+    // OpenMP, [2.9.2 taskloop Construct, Restrictions]
+    // If a reduction clause is present on the taskloop directive, the nogroup
+    // clause must not be specified.
+    if (checkReductionClauseWithNogroup(SemaRef, Clauses))
+      return StmtError();
+  }
+
+  if (llvm::is_contained(Leafs, OMPD_teams))
+    DSAStack->setParentTeamsRegionLoc(StartLoc);
+
+  Expr *ReductionRef = nullptr;
+  assert(DKind != OMPD_taskgroup && "Unexpected block directive");
+  if ((isOpenMPParallelDirective(DKind) ||
+       isOpenMPWorksharingDirective(DKind)) &&
+      !isOpenMPSimdDirective(DKind))
+    ReductionRef = DSAStack->getTaskgroupReductionRef();
+
+  return OMPCompoundLoopDirective::Create(
+      getASTContext(), StartLoc, EndLoc, DKind, NestedLoopCount, Clauses, AStmt,
+      B, ReductionRef, DSAStack->isCancelRegion());
+}
+
 OMPClause *SemaOpenMP::ActOnOpenMPInclusiveClause(ArrayRef<Expr *> VarList,
                                                   SourceLocation StartLoc,
                                                   SourceLocation LParenLoc,
@@ -23496,13 +24249,6 @@ OMPClause *SemaOpenMP::ActOnOpenMPXDynCGroupMemClause(Expr *Size,
   OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
   OpenMPDirectiveKind CaptureRegion = getOpenMPCaptureRegionForClause(
       DKind, OMPC_ompx_dyn_cgroup_mem, getLangOpts().OpenMP);
-  if (CaptureRegion != OMPD_unknown &&
-      !SemaRef.CurContext->isDependentContext()) {
-    ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-    llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
-    ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
-    HelperValStmt = buildPreInits(getASTContext(), Captures);
-  }
 
   return new (getASTContext()) OMPXDynCGroupMemClause(
       ValExpr, HelperValStmt, CaptureRegion, StartLoc, LParenLoc, EndLoc);
@@ -24187,5 +24933,90 @@ void SemaOpenMP::handleOMPAssumeAttr(Decl *D, const ParsedAttr &AL) {
   D->addAttr(::new (getASTContext()) OMPAssumeAttr(getASTContext(), AL, Str));
 }
 
+static SmallVector<Expr *> getVarList(const ext::ObjectList &Refs) {
+  SmallVector<Expr *> Vars;
+  llvm::transform(Refs, std::back_inserter(Vars), [](const ext::Object &Obj) {
+    return const_cast<Expr *>(Obj.ref());
+  });
+  return Vars;
+}
+
+static OMPClause *
+createImplicitClause(Sema &SemaRef, OpenMPDirectiveKind LeafKind,
+                     DSAStackTy *Stack,
+                     const SemaOpenMP::VariableImplicitInfo &ImpInfo,
+                     const omp::Clause &ExtClause) {
+
+  SemaOpenMP &S = SemaRef.OpenMP();
+  SourceLocation LocNone;
+  OMPClause *C = nullptr;
+
+  OpenMPDirectiveKind Save = Stack->getTopOfStack().Directive;
+  Stack->getTopOfStack().Directive = LeafKind;
+
+  switch (ExtClause.id) {
+  case OMPC_bind: {
+    auto V = std::get<ext::clause::Bind>(ExtClause.u);
+    C = S.ActOnOpenMPBindClause(ext::vnoc(V.v), LocNone, LocNone, LocNone,
+                                LocNone);
+    break;
+  }
+  case OMPC_firstprivate: {
+    auto V = std::get<ext::clause::Firstprivate>(ExtClause.u);
+    C = S.ActOnOpenMPFirstprivateClause(getVarList(V.v), LocNone, LocNone,
+                                        LocNone);
+    break;
+  }
+  case OMPC_if: {
+    auto V = std::get<ext::clause::If>(ExtClause.u);
+    using DirectiveNameModifier = omp::clause::If::DirectiveNameModifier;
+    auto MaybeMod = std::get<std::optional<DirectiveNameModifier>>(V.t);
+    OpenMPDirectiveKind Mod = MaybeMod ? *MaybeMod : OMPD_unknown;
+    const Expr *IfExpr = std::get<omp::clause::If::IfExpression>(V.t);
+    C = S.ActOnOpenMPIfClause(Mod, const_cast<Expr *>(IfExpr), LocNone, LocNone,
+                              LocNone, LocNone, LocNone);
+    break;
+  }
+  case OMPC_map: {
+    assert(ExtClause.tag.getFlags() == ExtClause.tag.Simple ||
+           ExtClause.tag.getFlags() == ExtClause.tag.Mapping);
+    auto V = std::get<omp::clause::Map>(ExtClause.u);
+    CXXScopeSpec MapperIdScopeSpec;
+    DeclarationNameInfo MapperId;
+
+    auto &Locators = std::get<omp::clause::Map::LocatorList>(V.t);
+    if (ExtClause.tag.getFlags() == ExtClause.tag.Simple) {
+      C = S.ActOnOpenMPMapClause(nullptr, OMPC_MAP_MODIFIER_unknown, LocNone,
+                                 MapperIdScopeSpec, MapperId, OMPC_MAP_tofrom,
+                                 /*IsMapTypeImplicit=*/true, LocNone, LocNone,
+                                 getVarList(Locators), OMPVarListLocTy(),
+                                 /*NoDiagnose=*/true);
+    } else if (ExtClause.tag.getFlags() == ExtClause.tag.Mapping) {
+      auto [I, J] = ExtClause.tag.getMapKinds();
+      auto MapType = static_cast<OpenMPMapClauseKind>(J);
+
+      C = S.ActOnOpenMPMapClause(
+          nullptr, ImpInfo.MapModifiers[I], ImpInfo.ImplicitMapModifiersLoc[I],
+          MapperIdScopeSpec, MapperId, MapType, /*IsMapTypeImplicit=*/true,
+          LocNone, LocNone, getVarList(Locators), OMPVarListLocTy());
+    }
+    break;
+  }
+  case OMPC_private: {
+    auto V = std::get<ext::clause::Private>(ExtClause.u);
+    C = S.ActOnOpenMPPrivateClause(getVarList(V.v), LocNone, LocNone, LocNone);
+    break;
+  }
+  default:
+    std::string msg =
+        "Unhandled clause: " + getOpenMPClauseName(ExtClause.id).str();
+    llvm_unreachable(msg.c_str());
+    break;
+  }
+
+  Stack->getTopOfStack().Directive = Save;
+  return C;
+}
+
 SemaOpenMP::SemaOpenMP(Sema &S)
     : SemaBase(S), VarDataSharingAttributesStack(nullptr) {}
diff --git a/clang/lib/Sema/SemaOpenMPExt.cpp b/clang/lib/Sema/SemaOpenMPExt.cpp
new file mode 100644
index 00000000000000..524ca50ffe184a
--- /dev/null
+++ b/clang/lib/Sema/SemaOpenMPExt.cpp
@@ -0,0 +1,1547 @@
+#include "SemaOpenMPExt.h"
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclOpenMP.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprOpenMP.h"
+#include "clang/AST/OpenMPClause.h"
+#include "clang/AST/Type.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Frontend/OpenMP/ClauseT.h"
+#include "llvm/Frontend/OpenMP/ConstructDecompositionT.h"
+
+#include <iterator>
+#include <list>
+#include <memory>
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+using namespace clang;
+
+#define MAKE_EMPTY_CLASS(cls, from_cls)                                        \
+  cls make(const from_cls &) {                                                 \
+    static_assert(cls::EmptyTrait::value);                                     \
+    return cls{};                                                              \
+  }                                                                            \
+  [[maybe_unused]] extern int xyzzy_semicolon_absorber
+
+#define MS(x, y) CLAUSET_SCOPED_ENUM_MEMBER_CONVERT(x, y)
+#define MU(x, y) CLAUSET_UNSCOPED_ENUM_MEMBER_CONVERT(x, y)
+
+namespace ext {
+std::optional<clause::Default::DataSharingAttribute>
+conv(llvm::omp::DefaultKind T) {
+  // OMP_DEFAULT_unknown -> std::nullopt
+  using FromTy = llvm::omp::DefaultKind;
+  using ToTy = clause::Default::DataSharingAttribute;
+
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::OMP_DEFAULT_firstprivate, ToTy::Firstprivate},
+      {FromTy::OMP_DEFAULT_none, ToTy::None},
+      {FromTy::OMP_DEFAULT_private, ToTy::Private},
+      {FromTy::OMP_DEFAULT_shared, ToTy::Shared},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::ProcBind::AffinityPolicy>
+conv(llvm::omp::ProcBindKind T) {
+  // OMP_PROC_BIND_default -> assert
+  // OMP_PROC_BIND_unknown -> std::nullopt
+  using FromTy = llvm::omp::ProcBindKind;
+  using ToTy = clause::ProcBind::AffinityPolicy;
+
+  assert(T != FromTy::OMP_PROC_BIND_default && "Unexpected kind");
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::OMP_PROC_BIND_close, ToTy::Close},
+      {FromTy::OMP_PROC_BIND_master, ToTy::Master},
+      {FromTy::OMP_PROC_BIND_primary, ToTy::Primary},
+      {FromTy::OMP_PROC_BIND_spread, ToTy::Spread},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::At::ActionTime> conv(OpenMPAtClauseKind T) {
+  // OMPC_AT_unknown -> std::nullopt
+  using FromTy = OpenMPAtClauseKind;
+  using ToTy = clause::At::ActionTime;
+
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_AT_compilation, ToTy::Compilation},
+      {OMPC_AT_execution, ToTy::Execution},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<MemoryOrder> conv(OpenMPAtomicDefaultMemOrderClauseKind T) {
+  // OMPC_ATOMIC_DEFAULT_MEM_ORDER_unknown -> std::nullopt
+  using FromTy = OpenMPAtomicDefaultMemOrderClauseKind;
+  using ToTy = MemoryOrder;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_ATOMIC_DEFAULT_MEM_ORDER_acq_rel, ToTy::AcqRel},
+      {OMPC_ATOMIC_DEFAULT_MEM_ORDER_relaxed, ToTy::Relaxed},
+      {OMPC_ATOMIC_DEFAULT_MEM_ORDER_seq_cst, ToTy::SeqCst},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::Bind::Binding> conv(OpenMPBindClauseKind T) {
+  // OMPC_BIND_unknown -> std::nullopt
+  using FromTy = OpenMPBindClauseKind;
+  using ToTy = clause::Bind::Binding;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_BIND_parallel, ToTy::Parallel},
+      {OMPC_BIND_teams, ToTy::Teams},
+      {OMPC_BIND_thread, ToTy::Thread},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::Defaultmap::VariableCategory>
+conv(OpenMPDefaultmapClauseKind T) {
+  // OMPC_DEFAULTMAP_all -> assert
+  // OMPC_DEFAULTMAP_unknown -> std::nullopt
+  using FromTy = OpenMPDefaultmapClauseKind;
+  using ToTy = clause::Defaultmap::VariableCategory;
+  assert(T != OMPC_DEFAULTMAP_all && "Unexpected kind");
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_DEFAULTMAP_aggregate, ToTy::Aggregate},
+      {OMPC_DEFAULTMAP_pointer, ToTy::Pointer},
+      {OMPC_DEFAULTMAP_scalar, ToTy::Scalar},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::Defaultmap::ImplicitBehavior>
+conv(OpenMPDefaultmapClauseModifier T) {
+  // OMPC_DEFAULTMAP_MODIFIER_unknown -> std::nullopt
+  // OMPC_DEFAULTMAP_MODIFIER_last -> std::nullopt
+  using FromTy = OpenMPDefaultmapClauseModifier;
+  using ToTy = clause::Defaultmap::ImplicitBehavior;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_DEFAULTMAP_MODIFIER_alloc, ToTy::Alloc},
+      {OMPC_DEFAULTMAP_MODIFIER_default, ToTy::Default},
+      {OMPC_DEFAULTMAP_MODIFIER_firstprivate, ToTy::Firstprivate},
+      {OMPC_DEFAULTMAP_MODIFIER_from, ToTy::From},
+      {OMPC_DEFAULTMAP_MODIFIER_none, ToTy::None},
+      {OMPC_DEFAULTMAP_MODIFIER_present, ToTy::Present},
+      {OMPC_DEFAULTMAP_MODIFIER_to, ToTy::To},
+      {OMPC_DEFAULTMAP_MODIFIER_tofrom, ToTy::Tofrom},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::Device::DeviceModifier>
+conv(OpenMPDeviceClauseModifier T) {
+  // OMPC_DEVICE_unknown -> std::nullopt
+  using FromTy = OpenMPDeviceClauseModifier;
+  using ToTy = clause::Device::DeviceModifier;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_DEVICE_ancestor, ToTy::Ancestor},
+      {OMPC_DEVICE_device_num, ToTy::DeviceNum},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::DistSchedule::Kind> conv(OpenMPDistScheduleClauseKind T) {
+  // OMPC_DIST_SCHEDULE_unknown -> std::nullopt
+  using FromTy = OpenMPDistScheduleClauseKind;
+  using ToTy = clause::DistSchedule::Kind;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_DIST_SCHEDULE_static, ToTy::Static},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::Grainsize::Prescriptiveness>
+conv(OpenMPGrainsizeClauseModifier T) {
+  // OMPC_GRAINSIZE_unknown -> std::nullopt
+  using FromTy = OpenMPGrainsizeClauseModifier;
+  using ToTy = clause::Grainsize::Prescriptiveness;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_GRAINSIZE_strict, ToTy::Strict},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::Lastprivate::LastprivateModifier>
+conv(OpenMPLastprivateModifier T) {
+  // OMPC_LASTPRIVATE_unknown -> std::nullopt
+  using FromTy = OpenMPLastprivateModifier;
+  using ToTy = clause::Lastprivate::LastprivateModifier;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_LASTPRIVATE_conditional, ToTy::Conditional},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::Linear::LinearModifier> conv(OpenMPLinearClauseKind T) {
+  // OMPC_LINEAR_step -> assert
+  // OMPC_LINEAR_unknown -> std::nullopt
+  using FromTy = OpenMPLinearClauseKind;
+  using ToTy = clause::Linear::LinearModifier;
+  assert(T != OMPC_LINEAR_step && "Unexpected kind");
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_LINEAR_ref, ToTy::Ref},
+      {OMPC_LINEAR_uval, ToTy::Uval},
+      {OMPC_LINEAR_val, ToTy::Val},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::Map::MapType> conv(OpenMPMapClauseKind T) {
+  // OMPC_MAP_unknown -> std::nullopt
+  using FromTy = OpenMPMapClauseKind;
+  using ToTy = clause::Map::MapType;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_MAP_alloc, ToTy::Alloc},   {OMPC_MAP_to, ToTy::To},
+      {OMPC_MAP_from, ToTy::From},     {OMPC_MAP_tofrom, ToTy::Tofrom},
+      {OMPC_MAP_delete, ToTy::Delete}, {OMPC_MAP_release, ToTy::Release},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::Map::MapTypeModifier> conv(OpenMPMapModifierKind T) {
+  // OMPC_MAP_MODIFIER_last -> std::nullopt
+  // OMPC_MAP_MODIFIER_unknown -> std::nullopt
+  using FromTy = OpenMPMapModifierKind;
+  using ToTy = clause::Map::MapTypeModifier;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_MAP_MODIFIER_always, ToTy::Always},
+      {OMPC_MAP_MODIFIER_close, ToTy::Close},
+      {OMPC_MAP_MODIFIER_present, ToTy::Present},
+      {OMPC_MAP_MODIFIER_ompx_hold, ToTy::OmpxHold},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::NumTasks::Prescriptiveness>
+conv(OpenMPNumTasksClauseModifier T) {
+  // OMPC_NUMTASKS_unknown -> std::nullopt
+  using FromTy = OpenMPNumTasksClauseModifier;
+  using ToTy = clause::NumTasks::Prescriptiveness;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_NUMTASKS_strict, ToTy::Strict},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::Order::Ordering> conv(OpenMPOrderClauseKind T) {
+  // OMPC_ORDER_unknown -> std::nullopt
+  using FromTy = OpenMPOrderClauseKind;
+  using ToTy = clause::Order::Ordering;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_ORDER_concurrent, ToTy::Concurrent},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::Order::OrderModifier> conv(OpenMPOrderClauseModifier T) {
+  // OMPC_ORDER_MODIFIER_last -> std::nullopt
+  // OMPC_ORDER_MODIFIER_unknown -> std::nullopt
+  using FromTy = OpenMPOrderClauseModifier;
+  using ToTy = clause::Order::OrderModifier;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_ORDER_MODIFIER_reproducible, ToTy::Reproducible},
+      {OMPC_ORDER_MODIFIER_unconstrained, ToTy::Unconstrained},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::Reduction::ReductionModifier>
+conv(OpenMPReductionClauseModifier T) {
+  // OMPC_REDUCTION_unknown -> std::nullopt
+  using FromTy = OpenMPReductionClauseModifier;
+  using ToTy = clause::Reduction::ReductionModifier;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_REDUCTION_default, ToTy::Default},
+      {OMPC_REDUCTION_inscan, ToTy::Inscan},
+      {OMPC_REDUCTION_task, ToTy::Task},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+std::optional<clause::Severity::SevLevel> conv(OpenMPSeverityClauseKind T) {
+  // OMPC_SEVERITY_unknown -> std::nullopt
+  using FromTy = OpenMPSeverityClauseKind;
+  using ToTy = clause::Severity::SevLevel;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OMPC_SEVERITY_fatal, ToTy::Fatal},
+      {OMPC_SEVERITY_warning, ToTy::Warning},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  return std::nullopt;
+}
+
+clause::DefinedOperator::IntrinsicOperator conv(OverloadedOperatorKind T) {
+  using FromTy = OverloadedOperatorKind;
+  using ToTy = clause::DefinedOperator::IntrinsicOperator;
+
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {OO_Plus, ToTy::Add},      {OO_Minus, ToTy::Subtract},
+      {OO_Star, ToTy::Multiply}, {OO_Amp, ToTy::AND},
+      {OO_Pipe, ToTy::OR},       {OO_Caret, ToTy::NEQV},
+      {OO_AmpAmp, ToTy::AND},    {OO_PipePipe, ToTy::OR},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+// Reverse conversions
+
+llvm::omp::DefaultKind vnoc(clause::Default::DataSharingAttribute T) {
+  using FromTy = clause::Default::DataSharingAttribute;
+  using ToTy = llvm::omp::DefaultKind;
+
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Firstprivate, ToTy::OMP_DEFAULT_firstprivate},
+      {FromTy::None, ToTy::OMP_DEFAULT_none},
+      {FromTy::Private, ToTy::OMP_DEFAULT_private},
+      {FromTy::Shared, ToTy::OMP_DEFAULT_shared},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+llvm::omp::ProcBindKind vnoc(clause::ProcBind::AffinityPolicy T) {
+  using FromTy = clause::ProcBind::AffinityPolicy;
+  using ToTy = llvm::omp::ProcBindKind;
+
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Close, ToTy::OMP_PROC_BIND_close},
+      {FromTy::Master, ToTy::OMP_PROC_BIND_master},
+      {FromTy::Primary, ToTy::OMP_PROC_BIND_primary},
+      {FromTy::Spread, ToTy::OMP_PROC_BIND_spread},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPAtClauseKind vnoc(clause::At::ActionTime T) {
+  using FromTy = clause::At::ActionTime;
+  using ToTy = OpenMPAtClauseKind;
+
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Compilation, OMPC_AT_compilation},
+      {FromTy::Execution, OMPC_AT_execution},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPAtomicDefaultMemOrderClauseKind vnoc(MemoryOrder T) {
+  using FromTy = MemoryOrder;
+  using ToTy = OpenMPAtomicDefaultMemOrderClauseKind;
+
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::AcqRel, OMPC_ATOMIC_DEFAULT_MEM_ORDER_acq_rel},
+      {FromTy::Relaxed, OMPC_ATOMIC_DEFAULT_MEM_ORDER_relaxed},
+      {FromTy::SeqCst, OMPC_ATOMIC_DEFAULT_MEM_ORDER_seq_cst},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPBindClauseKind vnoc(clause::Bind::Binding T) {
+  using FromTy = clause::Bind::Binding;
+  using ToTy = OpenMPBindClauseKind;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Parallel, OMPC_BIND_parallel},
+      {FromTy::Teams, OMPC_BIND_teams},
+      {FromTy::Thread, OMPC_BIND_thread},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPDefaultmapClauseKind vnoc(clause::Defaultmap::VariableCategory T) {
+  using FromTy = clause::Defaultmap::VariableCategory;
+  using ToTy = OpenMPDefaultmapClauseKind;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Aggregate, OMPC_DEFAULTMAP_aggregate},
+      {FromTy::Pointer, OMPC_DEFAULTMAP_pointer},
+      {FromTy::Scalar, OMPC_DEFAULTMAP_scalar},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPDefaultmapClauseModifier vnoc(clause::Defaultmap::ImplicitBehavior T) {
+  using FromTy = clause::Defaultmap::ImplicitBehavior;
+  using ToTy = OpenMPDefaultmapClauseModifier;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Alloc, OMPC_DEFAULTMAP_MODIFIER_alloc},
+      {FromTy::Default, OMPC_DEFAULTMAP_MODIFIER_default},
+      {FromTy::Firstprivate, OMPC_DEFAULTMAP_MODIFIER_firstprivate},
+      {FromTy::From, OMPC_DEFAULTMAP_MODIFIER_from},
+      {FromTy::None, OMPC_DEFAULTMAP_MODIFIER_none},
+      {FromTy::Present, OMPC_DEFAULTMAP_MODIFIER_present},
+      {FromTy::To, OMPC_DEFAULTMAP_MODIFIER_to},
+      {FromTy::Tofrom, OMPC_DEFAULTMAP_MODIFIER_tofrom},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPDeviceClauseModifier vnoc(clause::Device::DeviceModifier T) {
+  using FromTy = clause::Device::DeviceModifier;
+  using ToTy = OpenMPDeviceClauseModifier;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Ancestor, OMPC_DEVICE_ancestor},
+      {FromTy::DeviceNum, OMPC_DEVICE_device_num},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPDistScheduleClauseKind vnoc(clause::DistSchedule::Kind T) {
+  using FromTy = clause::DistSchedule::Kind;
+  using ToTy = OpenMPDistScheduleClauseKind;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Static, OMPC_DIST_SCHEDULE_static},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPGrainsizeClauseModifier vnoc(clause::Grainsize::Prescriptiveness T) {
+  using FromTy = clause::Grainsize::Prescriptiveness;
+  using ToTy = OpenMPGrainsizeClauseModifier;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Strict, OMPC_GRAINSIZE_strict},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPLastprivateModifier vnoc(clause::Lastprivate::LastprivateModifier T) {
+  using FromTy = clause::Lastprivate::LastprivateModifier;
+  using ToTy = OpenMPLastprivateModifier;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Conditional, OMPC_LASTPRIVATE_conditional},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPLinearClauseKind vnoc(clause::Linear::LinearModifier T) {
+  using FromTy = clause::Linear::LinearModifier;
+  using ToTy = OpenMPLinearClauseKind;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Ref, OMPC_LINEAR_ref},
+      {FromTy::Uval, OMPC_LINEAR_uval},
+      {FromTy::Val, OMPC_LINEAR_val},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPMapClauseKind vnoc(clause::Map::MapType T) {
+  using FromTy = clause::Map::MapType;
+  using ToTy = OpenMPMapClauseKind;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Alloc, OMPC_MAP_alloc},   {FromTy::To, OMPC_MAP_to},
+      {FromTy::From, OMPC_MAP_from},     {FromTy::Tofrom, OMPC_MAP_tofrom},
+      {FromTy::Delete, OMPC_MAP_delete}, {FromTy::Release, OMPC_MAP_release},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPMapModifierKind vnoc(clause::Map::MapTypeModifier T) {
+  using FromTy = clause::Map::MapTypeModifier;
+  using ToTy = OpenMPMapModifierKind;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Always, OMPC_MAP_MODIFIER_always},
+      {FromTy::Close, OMPC_MAP_MODIFIER_close},
+      {FromTy::Present, OMPC_MAP_MODIFIER_present},
+      {FromTy::OmpxHold, OMPC_MAP_MODIFIER_ompx_hold},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPNumTasksClauseModifier vnoc(clause::NumTasks::Prescriptiveness T) {
+  // OMPC_NUMTASKS_unknown -> std::nullopt
+  using FromTy = clause::NumTasks::Prescriptiveness;
+  using ToTy = OpenMPNumTasksClauseModifier;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Strict, OMPC_NUMTASKS_strict},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPOrderClauseKind vnoc(clause::Order::Ordering T) {
+  using FromTy = clause::Order::Ordering;
+  using ToTy = OpenMPOrderClauseKind;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Concurrent, OMPC_ORDER_concurrent},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPOrderClauseModifier vnoc(clause::Order::OrderModifier T) {
+  using FromTy = clause::Order::OrderModifier;
+  using ToTy = OpenMPOrderClauseModifier;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Reproducible, OMPC_ORDER_MODIFIER_reproducible},
+      {FromTy::Unconstrained, OMPC_ORDER_MODIFIER_unconstrained},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPReductionClauseModifier vnoc(clause::Reduction::ReductionModifier T) {
+  using FromTy = clause::Reduction::ReductionModifier;
+  using ToTy = OpenMPReductionClauseModifier;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Default, OMPC_REDUCTION_default},
+      {FromTy::Inscan, OMPC_REDUCTION_inscan},
+      {FromTy::Task, OMPC_REDUCTION_task},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OpenMPSeverityClauseKind vnoc(clause::Severity::SevLevel T) {
+  using FromTy = clause::Severity::SevLevel;
+  using ToTy = OpenMPSeverityClauseKind;
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Fatal, OMPC_SEVERITY_fatal},
+      {FromTy::Warning, OMPC_SEVERITY_warning},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+
+OverloadedOperatorKind vnoc(clause::DefinedOperator::IntrinsicOperator T) {
+  using FromTy = clause::DefinedOperator::IntrinsicOperator;
+  using ToTy = OverloadedOperatorKind;
+
+  static const llvm::DenseMap<FromTy, ToTy> Conv = {
+      {FromTy::Add, OO_Plus},      {FromTy::Subtract, OO_Minus},
+      {FromTy::Multiply, OO_Star}, {FromTy::AND, OO_Amp},
+      {FromTy::OR, OO_Pipe},       {FromTy::NEQV, OO_Caret},
+      {FromTy::AND, OO_AmpAmp},    {FromTy::OR, OO_PipePipe},
+  };
+  if (auto F = Conv.find(T); F != Conv.end())
+    return F->second;
+  llvm_unreachable("Unexpected value");
+}
+} // namespace ext
+
+namespace omp {
+using tomp::makeList;
+
+// "Convert" possibly null pointer to std::optional:
+// if the pointer is null, return std::nullopt, otherwise return
+// std::optional(ptr).
+template <typename T> //
+std::optional<std::remove_volatile_t<T> *> maybe(T *ptr) {
+  if (ptr == nullptr)
+    return std::nullopt;
+  return ptr;
+}
+
+// Conditionally "convert" given value into std::optional<T>:
+// if func(val) returns false, return std::nullopt, otherwise
+// return std::optional(val).
+// This is a macro to avoid evaluating "val" if the condition
+// is false.
+#define maybeIf(val, cond)                                                     \
+  ((cond) ? std::optional<std::remove_reference_t<decltype(val)>>(val)         \
+          : std::optional<std::remove_reference_t<decltype(val)>>{})
+
+static const Expr *getPointerFromOffsetOp(const BinaryOperator *B) {
+  BinaryOperatorKind Opc = B->getOpcode();
+  assert(Opc == BO_Add || Opc == BO_Sub);
+  if (B->getLHS()->getType()->isPointerType())
+    return B->getLHS();
+  assert(B->getRHS()->getType()->isPointerType());
+  return B->getRHS();
+}
+
+// Taken from SemaOpenMP.cpp
+static const Expr *getExprAsWritten(const Expr *E) {
+  if (const auto *FE = dyn_cast<FullExpr>(E))
+    E = FE->getSubExpr();
+
+  if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E))
+    E = MTE->getSubExpr();
+
+  while (const auto *Binder = dyn_cast<CXXBindTemporaryExpr>(E))
+    E = Binder->getSubExpr();
+
+  if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E))
+    E = ICE->getSubExprAsWritten();
+  return E->IgnoreParens();
+}
+
+// Taken from SemaOpenMP.cpp
+static const ValueDecl *getCanonicalDecl(const ValueDecl *D) {
+  if (const auto *CED = dyn_cast<OMPCapturedExprDecl>(D))
+    if (const auto *ME = dyn_cast<MemberExpr>(getExprAsWritten(CED->getInit())))
+      D = ME->getMemberDecl();
+
+  D = cast<ValueDecl>(D->getCanonicalDecl());
+  return D;
+}
+
+Object makeObject(const Decl *D, const Expr *E = nullptr) {
+  assert(D != nullptr);
+  return Object{D, E};
+}
+
+Object makeObject(const Expr *E) {
+  if (E->containsErrors())
+    return Object{};
+  if (auto *D = llvm::dyn_cast<DeclRefExpr>(E))
+    return Object{getCanonicalDecl(D->getDecl()), E}; // ValueDecl
+  if (auto *D = llvm::dyn_cast<MemberExpr>(E))
+    return Object{getCanonicalDecl(D->getMemberDecl()), E}; // ValueDecl
+
+  if (auto *A = llvm::dyn_cast<ArraySubscriptExpr>(E))
+    return Object{makeObject(A->getBase()).id(), E};
+  if (auto *A = llvm::dyn_cast<ArraySectionExpr>(E))
+    return Object{makeObject(A->getBase()).id(), E};
+  if (auto *A = llvm::dyn_cast<OMPArrayShapingExpr>(E))
+    return Object{makeObject(A->getBase()).id(), E};
+
+  if (auto *P = llvm::dyn_cast<ParenExpr>(E))
+    return makeObject(P->getSubExpr());
+  if (auto *P = llvm::dyn_cast<ImplicitCastExpr>(E))
+    return makeObject(P->getSubExpr());
+  if (auto *P = llvm::dyn_cast<OpaqueValueExpr>(E))
+    return makeObject(P->getSourceExpr());
+
+  if (auto *P = llvm::dyn_cast<BinaryOperator>(E)) {
+    if (P->isAssignmentOp())
+      return makeObject(P->getLHS());
+    BinaryOperatorKind Opc = P->getOpcode();
+    if (Opc == BO_Add || Opc == BO_Sub)
+      return Object{makeObject(getPointerFromOffsetOp(P)).id(), E};
+  }
+  if (auto *P = llvm::dyn_cast<UnaryOperator>(E)) {
+    UnaryOperatorKind Opc = P->getOpcode();
+    if (Opc == UO_Deref || Opc == UO_AddrOf)
+      return Object{makeObject(P->getSubExpr()).id(), E};
+  }
+
+  if (auto *P = llvm::dyn_cast<UnresolvedLookupExpr>(E))
+    return Object{};
+  if (auto *P = llvm::dyn_cast<CXXThisExpr>(E))
+    return Object{P, E};
+
+  E->dump();
+  llvm_unreachable("Expecting DeclRefExpr");
+}
+
+ObjectList makeObjects(llvm::ArrayRef<const Expr *> Vars) {
+  return makeList(Vars, [](const Expr *E) { return makeObject(E); });
+}
+
+clause::DefinedOperator makeDefinedOperator(OverloadedOperatorKind OOK) {
+  // From the OpenMP 5.2 spec:
+  // A reduction identifier is either an id-expression or one of the following
+  // operators: +, - (deprecated), *, &, |, ^, && and ||.
+  // Min and max are included in the implicitly-declared reduction operators.
+  return clause::DefinedOperator{ext::conv(OOK)};
+}
+
+List<clause::ReductionOperator>
+makeReductionOperators(const DeclarationName &RedId,
+                       llvm::ArrayRef<const Expr *> Ops) {
+  using ReductionOperator = clause::ReductionOperator;
+  assert(!RedId.isDependentName());
+  List<clause::ReductionOperator> RedOps;
+
+  DeclarationName::NameKind Kind = RedId.getNameKind();
+  if (Kind == DeclarationName::CXXOperatorName) {
+    OverloadedOperatorKind OOK = RedId.getCXXOverloadedOperator();
+    RedOps.push_back(ReductionOperator{makeDefinedOperator(OOK)});
+    return RedOps;
+  }
+
+  assert(Kind == DeclarationName::Identifier);
+  const auto *II = RedId.getAsIdentifierInfo();
+  if (II->getName() == "min") {
+    auto MinOp = clause::DefinedOperator::IntrinsicOperator::Min;
+    RedOps.push_back(ReductionOperator{clause::DefinedOperator{MinOp}});
+    return RedOps;
+  }
+  if (II->getName() == "max") {
+    auto MaxOp = clause::DefinedOperator::IntrinsicOperator::Max;
+    RedOps.push_back(ReductionOperator{clause::DefinedOperator{MaxOp}});
+    return RedOps;
+  }
+
+  for (const Expr *Op : Ops) {
+    if (Op == nullptr) {
+      // This can happen in an invalid code.
+      Object invalid{};
+      RedOps.clear();
+      RedOps.push_back(ReductionOperator{clause::ProcedureDesignator{invalid}});
+      return RedOps;
+    }
+
+    // If it's not an intrinsic operator, then it should be a function call.
+    auto *CallOp = cast<CallExpr>(Op);
+    Object func = makeObject(CallOp->getCallee());
+    RedOps.push_back(ReductionOperator{clause::ProcedureDesignator{func}});
+  }
+
+  return RedOps;
+}
+
+std::optional<Iterator> tryMakeIterator(const Expr *E) {
+  if (E == nullptr)
+    return std::nullopt;
+
+  Iterator Iter;
+  const auto &IterExpr = *llvm::cast<OMPIteratorExpr>(E);
+
+  using IteratorSpecifier =
+      tomp::type::IteratorSpecifierT<TypeTy, IdentTy, ExprTy>;
+  using Range = tomp::type::RangeT<ExprTy>;
+
+  for (int i = 0, e = IterExpr.numOfIterators(); i != e; ++i) {
+    auto *ID = const_cast<Decl *>(IterExpr.getIteratorDecl(i));
+    const OMPIteratorExpr::IteratorRange &IR = IterExpr.getIteratorRange(i);
+    QualType Type = llvm::cast<VarDecl>(ID)->getType();
+    Range R{{IR.Begin, IR.End, maybe(IR.Step)}};
+    Iter.emplace_back(IteratorSpecifier{{Type, Object{ID, nullptr}, R}});
+  }
+
+  return Iter;
+}
+
+template <typename IteratorTy>
+List<Mapper> makeMappers(llvm::iterator_range<IteratorTy> &&Range) {
+  List<Mapper> Mappers;
+  if (Range.empty())
+    return Mappers;
+
+  for (const Expr *M : Range) {
+    if (M == nullptr || isa<UnresolvedLookupExpr>(M))
+      continue;
+    Object obj = makeObject(M);
+    if (obj.id().index() != 0)
+      Mappers.push_back(Mapper{/*MapperIdentifier=*/obj});
+  }
+  return Mappers;
+}
+
+namespace clause {
+MAKE_EMPTY_CLASS(AcqRel, OMPAcqRelClause);
+MAKE_EMPTY_CLASS(Acquire, OMPAcquireClause);
+MAKE_EMPTY_CLASS(Capture, OMPCaptureClause);
+MAKE_EMPTY_CLASS(Compare, OMPCompareClause);
+MAKE_EMPTY_CLASS(DynamicAllocators, OMPDynamicAllocatorsClause);
+MAKE_EMPTY_CLASS(Full, OMPFullClause);
+// MAKE_EMPTY_CLASS(Inbranch, OMPInbranchClause);
+MAKE_EMPTY_CLASS(Mergeable, OMPMergeableClause);
+MAKE_EMPTY_CLASS(Nogroup, OMPNogroupClause);
+MAKE_EMPTY_CLASS(NoOpenmp, OMPNoOpenMPClause);
+MAKE_EMPTY_CLASS(NoOpenmpRoutines, OMPNoOpenMPRoutinesClause);
+MAKE_EMPTY_CLASS(NoParallelism, OMPNoParallelismClause);
+// MAKE_EMPTY_CLASS(Notinbranch, OMPNotinbranchClause);
+MAKE_EMPTY_CLASS(Nowait, OMPNowaitClause);
+MAKE_EMPTY_CLASS(OmpxAttribute, OMPXAttributeClause);
+MAKE_EMPTY_CLASS(OmpxBare, OMPXBareClause);
+MAKE_EMPTY_CLASS(Read, OMPReadClause);
+MAKE_EMPTY_CLASS(Relaxed, OMPRelaxedClause);
+MAKE_EMPTY_CLASS(Release, OMPReleaseClause);
+MAKE_EMPTY_CLASS(ReverseOffload, OMPReverseOffloadClause);
+MAKE_EMPTY_CLASS(SeqCst, OMPSeqCstClause);
+MAKE_EMPTY_CLASS(Simd, OMPSIMDClause);
+MAKE_EMPTY_CLASS(Threads, OMPThreadsClause);
+MAKE_EMPTY_CLASS(UnifiedAddress, OMPUnifiedAddressClause);
+MAKE_EMPTY_CLASS(UnifiedSharedMemory, OMPUnifiedSharedMemoryClause);
+// MAKE_EMPTY_CLASS(Unknown, OMPUnknownClause);
+MAKE_EMPTY_CLASS(Untied, OMPUntiedClause);
+MAKE_EMPTY_CLASS(Weak, OMPWeakClause);
+MAKE_EMPTY_CLASS(Write, OMPWriteClause);
+
+// Make artificial clauses
+MAKE_EMPTY_CLASS(Depobj, OMPDepobjClause);
+MAKE_EMPTY_CLASS(Flush, OMPFlushClause);
+
+// Other clauses
+
+Absent make(const OMPAbsentClause &C) {
+  ArrayRef<OpenMPDirectiveKind> Kinds = C.getDirectiveKinds();
+  return Absent{/*List=*/{Kinds.begin(), Kinds.end()}};
+}
+
+// AdjustArgs
+
+Affinity make(const OMPAffinityClause &C) {
+  return Affinity{
+      {/*Iterator=*/std::nullopt,
+       /*LocatorList=*/makeObjects(C.getVarRefs())},
+  };
+}
+
+Align make(const OMPAlignClause &C) {
+  return Align{/*Alignment=*/C.getAlignment()};
+}
+
+Aligned make(const OMPAlignedClause &C) {
+  return Aligned{
+      {/*Alignment=*/C.getAlignment(),
+       /*List=*/makeObjects(C.getVarRefs())},
+  };
+}
+
+Allocate make(const OMPAllocateClause &C) {
+  return Allocate{
+      {/*AllocatorSimpleModifier=*/C.getAllocator(),
+       /*AllocatorComplexModifier=*/std::nullopt,
+       /*AlignModifier=*/std::nullopt,
+       /*List=*/makeObjects(C.getVarRefs())},
+  };
+}
+
+Allocator make(const OMPAllocatorClause &C) {
+  return Allocator{/*Allocator=*/C.getAllocator()};
+}
+
+// AppendArgs
+
+At make(const OMPAtClause &C) {
+  return At{/*ActionTime=*/*ext::conv(C.getAtKind())};
+}
+
+AtomicDefaultMemOrder make(const OMPAtomicDefaultMemOrderClause &C) {
+  return AtomicDefaultMemOrder{
+      /*MemoryOrder=*/*ext::conv(C.getAtomicDefaultMemOrderKind())};
+}
+
+Bind make(const OMPBindClause &C) {
+  return Bind{/*Binding=*/*ext::conv(C.getBindKind())};
+}
+
+Collapse make(const OMPCollapseClause &C) {
+  return Collapse{/*N=*/C.getNumForLoops()};
+}
+
+Contains make(const OMPContainsClause &C) {
+  ArrayRef<OpenMPDirectiveKind> Kinds = C.getDirectiveKinds();
+  return Contains{/*List=*/{Kinds.begin(), Kinds.end()}};
+}
+
+Copyin make(const OMPCopyinClause &C) {
+  return Copyin{/*List=*/makeObjects(C.getVarRefs())};
+}
+
+Copyprivate make(const OMPCopyprivateClause &C) {
+  return Copyprivate{/*List=*/makeObjects(C.getVarRefs())};
+}
+
+Default make(const OMPDefaultClause &C) {
+  return Default{/*DataSharingAttribute=*/*ext::conv(C.getDefaultKind())};
+}
+
+Defaultmap make(const OMPDefaultmapClause &C) {
+  return Defaultmap{
+      {
+          /*ImplicitBehavior=*/*ext::conv(C.getDefaultmapModifier()),
+          /*VariableCategory=*/ext::conv(C.getDefaultmapKind()),
+      },
+  };
+}
+
+Doacross make(Doacross::DependenceType DepType,
+              llvm::ArrayRef<const Expr *> Vars);
+
+Depend make(const OMPDependClause &C) {
+  using TaskDependenceType = tomp::type::TaskDependenceType;
+
+  CLAUSET_ENUM_CONVERT( //
+      convertDep, OpenMPDependClauseKind, TaskDependenceType,
+      // clang-format off
+      MU(OMPC_DEPEND_in, In)
+      MU(OMPC_DEPEND_out, Out)
+      MU(OMPC_DEPEND_inout, Inout)
+      MU(OMPC_DEPEND_mutexinoutset, Mutexinoutset)
+      MU(OMPC_DEPEND_inoutset, Inoutset)
+      MU(OMPC_DEPEND_depobj, Depobj)
+      // clang-format on
+  );
+
+  std::optional<Iterator> maybeIterator = tryMakeIterator(C.getModifier());
+  using WithLocators = Depend::WithLocators;
+
+  OpenMPDependClauseKind DepType = C.getDependencyKind();
+  if (DepType == OMPC_DEPEND_outallmemory)
+    return Depend{
+        WithLocators{{/*TaskDependenceType=*/TaskDependenceType::Out,
+                      /*Iterator=*/std::move(maybeIterator),
+                      /*LocatorList=*/{}}},
+    };
+  if (DepType == OMPC_DEPEND_inoutallmemory)
+    return Depend{
+        WithLocators{{/*TaskDependenceType=*/TaskDependenceType::Inout,
+                      /*Iterator=*/std::move(maybeIterator),
+                      /*LocatorList=*/{}}},
+    };
+  if (DepType != OMPC_DEPEND_source && DepType != OMPC_DEPEND_sink)
+    return Depend{
+        WithLocators{{/*TaskDependenceType=*/convertDep(DepType),
+                      /*Iterator=*/std::move(maybeIterator),
+                      /*LocatorList=*/makeObjects(C.getVarRefs())}},
+    };
+
+  // XXX The distance vectors are unavailable: they are in DSAStack, which is
+  // a private member of Sema without an accessor.
+  CLAUSET_ENUM_CONVERT( //
+      convertDoa, OpenMPDependClauseKind, Doacross::DependenceType,
+      // clang-format off
+      MU(OMPC_DEPEND_source, Source)
+      MU(OMPC_DEPEND_sink, Sink)
+      // clang-format on
+  );
+
+  return Depend{make(convertDoa(DepType), C.getVarRefs())};
+}
+
+Destroy make(const OMPDestroyClause &C) {
+  if (Expr *Var = C.getInteropVar())
+    return Destroy{/*DestroyVar=*/makeObject(Var)};
+  return Destroy{/*DestroyVar=*/std::nullopt};
+}
+
+Detach make(const OMPDetachClause &C) {
+  return Detach{/*EventHandle=*/makeObject(C.getEventHandler())};
+}
+
+Device make(const OMPDeviceClause &C) {
+  OpenMPDeviceClauseModifier Modifier = C.getModifier();
+  return Device{
+      {/*DeviceModifier=*/ext::conv(Modifier),
+       /*DeviceDescription=*/C.getDevice()},
+  };
+}
+
+// DeviceType
+
+DistSchedule make(const OMPDistScheduleClause &C) {
+  return DistSchedule{
+      {/*Kind=*/*ext::conv(C.getDistScheduleKind()),
+       /*ChunkSize=*/maybe(C.getChunkSize())},
+  };
+}
+
+Doacross make(Doacross::DependenceType DepType,
+              llvm::ArrayRef<const Expr *> Vars) {
+  // XXX The loop iteration distance vector is unavailable (it's not exported
+  // from Sema/SemaOpenMP.
+  return Doacross{{/*DependenceType=*/DepType, /*Vector=*/{}}};
+}
+
+Doacross make(const OMPDoacrossClause &C) {
+  CLAUSET_ENUM_CONVERT( //
+      convert, OpenMPDoacrossClauseModifier, Doacross::DependenceType,
+      // clang-format off
+      MU(OMPC_DOACROSS_source, Source)
+      MU(OMPC_DOACROSS_source_omp_cur_iteration, Source)
+      MU(OMPC_DOACROSS_sink, Sink)
+      MU(OMPC_DOACROSS_sink_omp_cur_iteration, Sink)
+      // clang-format on
+  );
+
+  OpenMPDoacrossClauseModifier DepType = C.getDependenceType();
+  if (DepType == OMPC_DOACROSS_source_omp_cur_iteration ||
+      DepType == OMPC_DOACROSS_sink_omp_cur_iteration)
+    return make(convert(DepType), {});
+
+  return make(convert(DepType), C.getVarRefs());
+}
+
+// Enter
+
+Exclusive make(const OMPExclusiveClause &C) {
+  return Exclusive{/*List=*/makeObjects(C.getVarRefs())};
+}
+
+Fail make(const OMPFailClause &C) {
+  CLAUSET_ENUM_CONVERT( //
+      convert, llvm::omp::Clause, tomp::type::MemoryOrder,
+      // clang-format off
+      MS(OMPC_acq_rel, AcqRel)
+      MS(OMPC_acquire, Acquire)
+      MS(OMPC_relaxed, Relaxed)
+      MS(OMPC_release, Release)
+      MS(OMPC_seq_cst, SeqCst)
+      // clang-format on
+  );
+
+  return Fail{/*MemoryOrder=*/convert(C.getFailParameter())};
+}
+
+Filter make(const OMPFilterClause &C) {
+  return Filter{/*ThreadNum=*/C.getThreadID()};
+}
+
+Final make(const OMPFinalClause &C) {
+  return Final{/*Finalize=*/C.getCondition()};
+}
+
+Firstprivate make(const OMPFirstprivateClause &C) {
+  return Firstprivate{/*List=*/makeObjects(C.getVarRefs())};
+}
+
+From make(const OMPFromClause &C) {
+  std::optional<tomp::type::MotionExpectation> maybeExp;
+  if (llvm::is_contained(C.getMotionModifiers(), OMPC_MOTION_MODIFIER_present))
+    maybeExp = tomp::type::MotionExpectation::Present;
+
+  List<Mapper> Mappers = makeMappers(C.mapperlists());
+  if (!Mappers.empty())
+    assert(Mappers.size() == 1 || Mappers.size() == C.getVarRefs().size());
+
+  return From{
+      {/*Expectation=*/std::move(maybeExp),
+       /*Mappers=*/maybeIf(Mappers, !Mappers.empty()),
+       /*Iterator=*/std::nullopt,
+       /*LocatorList=*/makeObjects(C.getVarRefs())},
+  };
+}
+
+Grainsize make(const OMPGrainsizeClause &C) {
+  return Grainsize{
+      {/*Prescriptiveness=*/ext::conv(C.getModifier()),
+       /*GrainSize=*/C.getGrainsize()},
+  };
+}
+
+HasDeviceAddr make(const OMPHasDeviceAddrClause &C) {
+  return HasDeviceAddr{/*List=*/makeObjects(C.getVarRefs())};
+}
+
+Hint make(const OMPHintClause &C) {
+  return Hint{/*HintExpr=*/C.getHint()}; //
+}
+
+Holds make(const OMPHoldsClause &C) { return Holds{/*E=*/C.getExpr()}; }
+
+If make(const OMPIfClause &C) {
+  return If{
+      {/*DirectiveNameModifier=*/ext::conv(C.getNameModifier()),
+       /*IfExpression=*/C.getCondition()},
+  };
+}
+
+Inclusive make(const OMPInclusiveClause &C) {
+  return Inclusive{/*List=*/makeObjects(C.getVarRefs())};
+}
+
+// Indirect
+
+Init make(const OMPInitClause &C) {
+  const Expr *Var = C.getInteropVar();
+  Init::InteropTypes Types(2);
+  if (C.getIsTarget())
+    Types.push_back(Init::InteropType::Target);
+  if (C.getIsTargetSync())
+    Types.push_back(Init::InteropType::Targetsync);
+  auto Range = C.prefs();
+  llvm::ArrayRef<const Expr *> Prefs(Range.begin(), Range.end());
+
+  using T = decltype(Init::t);
+  return Init{
+      T{/*InteropPreference=*/maybeIf(Prefs, !Prefs.empty()),
+        /*InteropTypes=*/Types,
+        /*InteropVar=*/makeObject(Var)},
+  };
+}
+
+// Initializer
+
+InReduction make(const OMPInReductionClause &C) {
+  auto OpsRange = C.reduction_ops();
+  List<ReductionOperator> RedOps = makeReductionOperators(
+      C.getNameInfo().getName(), {OpsRange.begin(), OpsRange.end()});
+  assert(RedOps.size() == 1 || RedOps.size() == C.getVarRefs().size());
+
+  return InReduction{
+      {/*ReductionIdentifiers=*/RedOps,
+       /*List=*/makeObjects(C.getVarRefs())},
+  };
+}
+
+IsDevicePtr make(const OMPIsDevicePtrClause &C) {
+  return IsDevicePtr{/*List=*/makeObjects(C.getVarRefs())};
+}
+
+Lastprivate make(const OMPLastprivateClause &C) {
+  return Lastprivate{
+      {/*LastprivateModifier=*/ext::conv(C.getKind()),
+       /*List=*/makeObjects(C.getVarRefs())},
+  };
+}
+
+Linear make(const OMPLinearClause &C) {
+  return Linear{
+      {/*StepSimpleModifier=*/std::nullopt,
+       /*StepComplexModifier=*/C.getStep(),
+       /*LinearModifier=*/*ext::conv(C.getModifier()),
+       /*List=*/makeObjects(C.getVarRefs())},
+  };
+}
+
+// Link
+
+Map make(const OMPMapClause &C) {
+  OpenMPMapClauseKind MapType = C.getMapType();
+  List<Mapper> Mappers = makeMappers(C.mapperlists());
+  if (!Mappers.empty())
+    assert(Mappers.size() == 1 || Mappers.size() == C.getVarRefs().size());
+  std::optional<Iterator> maybeIterator =
+      tryMakeIterator(C.getIteratorModifier());
+
+  Map::MapTypeModifiers Modifiers;
+  auto &&KnownModifiers = llvm::make_filter_range(
+      C.getMapTypeModifiers(), [](OpenMPMapModifierKind M) {
+        // Skip "mapper" and "iterator" modifiers, because they're already
+        // handled.
+        return M != OMPC_MAP_MODIFIER_unknown &&
+               M != OMPC_MAP_MODIFIER_mapper && M != OMPC_MAP_MODIFIER_iterator;
+      });
+  llvm::transform(KnownModifiers, std::back_inserter(Modifiers),
+                  [](OpenMPMapModifierKind M) { return *ext::conv(M); });
+
+  return Map{
+      {/*MapType=*/ext::conv(MapType),
+       /*MapTypeModifiers=*/
+       maybeIf(Modifiers, !Modifiers.empty()),
+       /*Mappers=*/maybeIf(Mappers, !Mappers.empty()),
+       /*Iterator=*/std::move(maybeIterator),
+       /*LocatorList=*/makeObjects(C.getVarRefs())},
+  };
+}
+
+// Match
+
+Message make(const OMPMessageClause &C) {
+  return Message{/*MsgString=*/C.getMessageString()};
+}
+
+Nocontext make(const OMPNocontextClause &C) {
+  return Nocontext{/*DoNotUpdateContext=*/C.getCondition()};
+}
+
+Nontemporal make(const OMPNontemporalClause &C) {
+  return Nontemporal{/*List=*/makeObjects(C.getVarRefs())};
+}
+
+Novariants make(const OMPNovariantsClause &C) {
+  return Novariants{/*DoNotUseVariant=*/C.getCondition()};
+}
+
+NumTasks make(const OMPNumTasksClause &C) {
+  using T = decltype(NumTasks::t);
+  return NumTasks{
+      T{/*Prescriptiveness=*/ext::conv(C.getModifier()),
+        /*NumTasks=*/C.getNumTasks()},
+  };
+}
+
+NumTeams make(const OMPNumTeamsClause &C) {
+  ArrayRef<const Expr *> UpperBounds = C.getVarRefs();
+  List<NumTeams::Range> Ranges;
+  for (const Expr *E : UpperBounds) {
+    Ranges.push_back(
+        {{/*LowerBound=*/std::nullopt, /*UpperBound=*/const_cast<Expr *>(E)}});
+  }
+  return NumTeams{std::move(Ranges)};
+}
+
+NumThreads make(const OMPNumThreadsClause &C) {
+  return NumThreads{/*Nthreads=*/C.getNumThreads()};
+}
+
+OmpxDynCgroupMem make(const OMPXDynCGroupMemClause &C) {
+  return OmpxDynCgroupMem{C.getSize()};
+}
+
+Order make(const OMPOrderClause &C) {
+  return Order{
+      {/*OrderModifier=*/ext::conv(C.getModifier()),
+       /*Ordering=*/*ext::conv(C.getKind())},
+  };
+}
+
+Ordered make(const OMPOrderedClause &C) {
+  return Ordered{/*N=*/C.getNumForLoops()};
+}
+
+// Otherwise
+
+Partial make(const OMPPartialClause &C) {
+  return Partial{/*UnrollFactor=*/C.getFactor()};
+}
+
+Priority make(const OMPPriorityClause &C) {
+  return Priority{/*PriorityValue=*/C.getPriority()};
+}
+
+Private make(const OMPPrivateClause &C) {
+  return Private{/*List=*/makeObjects(C.getVarRefs())};
+}
+
+ProcBind make(const OMPProcBindClause &C) {
+  return ProcBind{/*AffinityPolicy=*/*ext::conv(C.getProcBindKind())};
+}
+
+Reduction make(const OMPReductionClause &C) {
+  auto OpsRange = C.reduction_ops();
+  List<ReductionOperator> RedOps = makeReductionOperators(
+      C.getNameInfo().getName(), {OpsRange.begin(), OpsRange.end()});
+  assert(RedOps.size() == 1 || RedOps.size() == C.getVarRefs().size());
+
+  return Reduction{
+      {/*ReductionModifier=*/ext::conv(C.getModifier()),
+       /*ReductionIdentifiers=*/RedOps,
+       /*List=*/makeObjects(C.getVarRefs())},
+  };
+}
+
+Safelen make(const OMPSafelenClause &C) {
+  return Safelen{/*Length=*/C.getSafelen()};
+}
+
+Schedule make(const OMPScheduleClause &C) {
+  CLAUSET_ENUM_CONVERT( //
+      convertKind, OpenMPScheduleClauseKind, Schedule::Kind,
+      // clang-format off
+      MU(OMPC_SCHEDULE_static, Static)
+      MU(OMPC_SCHEDULE_dynamic, Dynamic)
+      MU(OMPC_SCHEDULE_guided, Guided)
+      MU(OMPC_SCHEDULE_auto, Auto)
+      MU(OMPC_SCHEDULE_runtime, Runtime)
+      // clang-format on
+  );
+
+  CLAUSET_ENUM_CONVERT( //
+      convertOrdMod, OpenMPScheduleClauseModifier, Schedule::OrderingModifier,
+      // clang-format off
+      MU(OMPC_SCHEDULE_MODIFIER_monotonic, Monotonic)
+      MU(OMPC_SCHEDULE_MODIFIER_nonmonotonic, Nonmonotonic)
+      // clang-format on
+  );
+
+  CLAUSET_ENUM_CONVERT( //
+      convertChkMod, OpenMPScheduleClauseModifier, Schedule::ChunkModifier,
+      // clang-format off
+      MU(OMPC_SCHEDULE_MODIFIER_simd, Simd)
+      // clang-format on
+  );
+
+  OpenMPScheduleClauseModifier OrdMod = C.getFirstScheduleModifier();
+  OpenMPScheduleClauseModifier ChkMod = C.getSecondScheduleModifier();
+  if (OrdMod == OMPC_SCHEDULE_MODIFIER_simd)
+    std::swap(OrdMod, ChkMod);
+
+  const Expr *ChkSize = C.getChunkSize();
+
+  // clang-format off
+  return Schedule{
+      {/*Kind=*/convertKind(C.getScheduleKind()),
+       /*OrderingModifier=*/maybeIf(convertOrdMod(OrdMod),
+          OrdMod != OMPC_SCHEDULE_MODIFIER_unknown),
+       /*ChunkModifier=*/maybeIf(convertChkMod(ChkMod),
+          ChkMod != OMPC_SCHEDULE_MODIFIER_unknown),
+       /*ChunkSize=*/maybeIf(ChkSize, ChkSize != nullptr)},
+  };
+  // clang-format on
+}
+
+Severity make(const OMPSeverityClause &C) {
+  return Severity{/*SevLevel=*/*ext::conv(C.getSeverityKind())};
+}
+
+Shared make(const OMPSharedClause &C) {
+  return Shared{/*List=*/makeObjects(C.getVarRefs())};
+}
+
+Simdlen make(const OMPSimdlenClause &C) {
+  return Simdlen{/*Length=*/C.getSimdlen()};
+}
+
+Sizes make(const OMPSizesClause &C) {
+  llvm::ArrayRef<const Expr *> S = C.getSizesRefs();
+  return Sizes{/*SizeList=*/Sizes::SizeList(S.begin(), S.end())};
+}
+
+TaskReduction make(const OMPTaskReductionClause &C) {
+  auto OpsRange = C.reduction_ops();
+  List<ReductionOperator> RedOps = makeReductionOperators(
+      C.getNameInfo().getName(), {OpsRange.begin(), OpsRange.end()});
+  assert(RedOps.size() == 1 || RedOps.size() == C.getVarRefs().size());
+
+  using T = decltype(TaskReduction::t);
+  return TaskReduction{
+      T{/*ReductionIdentifiers=*/RedOps,
+        /*List=*/makeObjects(C.getVarRefs())},
+  };
+}
+
+ThreadLimit make(const OMPThreadLimitClause &C) {
+  /// XXX
+  // return ThreadLimit{/*Threadlim=*/C.getThreadLimit()};
+  return ThreadLimit{/*Threadlim=*/C.getVarRefs()[0]};
+}
+
+To make(const OMPToClause &C) {
+  std::optional<tomp::type::MotionExpectation> maybeExp;
+  if (llvm::is_contained(C.getMotionModifiers(), OMPC_MOTION_MODIFIER_present))
+    maybeExp = tomp::type::MotionExpectation::Present;
+
+  List<Mapper> Mappers = makeMappers(C.mapperlists());
+  if (!Mappers.empty())
+    assert(Mappers.size() == 1 || Mappers.size() == C.getVarRefs().size());
+
+  return To{
+      {/*Expectation=*/std::move(maybeExp),
+       /*Mapper=*/maybeIf(Mappers, !Mappers.empty()),
+       /*Iterator=*/std::nullopt,
+       /*LocatorList=*/makeObjects(C.getVarRefs())},
+  };
+}
+
+// Uniform
+
+Update make(const OMPUpdateClause &C) {
+  if (!C.isExtended())
+    return Update{/*TaskDependenceType=*/std::nullopt};
+
+  using TaskDependenceType = tomp::type::TaskDependenceType;
+  CLAUSET_ENUM_CONVERT( //
+      convert, OpenMPDependClauseKind, TaskDependenceType,
+      // clang-format off
+      MU(OMPC_DEPEND_in, In)
+      MU(OMPC_DEPEND_out, Out)
+      MU(OMPC_DEPEND_inout, Inout)
+      MU(OMPC_DEPEND_mutexinoutset, Mutexinoutset)
+      MU(OMPC_DEPEND_inoutset, Inoutset)
+      // OMPC_DEPEND_depobj is not allowed
+      // clang-format on
+  );
+
+  OpenMPDependClauseKind DepType = C.getDependencyKind();
+  return Update{/*TaskDependenceType=*/maybeIf(convert(DepType),
+                                               DepType != OMPC_DEPEND_unknown)};
+}
+
+Use make(const OMPUseClause &C) {
+  return Use{/*InteropVar=*/makeObject(C.getInteropVar())};
+}
+
+UseDeviceAddr make(const OMPUseDeviceAddrClause &C) {
+  return UseDeviceAddr{/*List=*/makeObjects(C.getVarRefs())};
+}
+
+UseDevicePtr make(const OMPUseDevicePtrClause &C) {
+  return UseDevicePtr{/*List=*/makeObjects(C.getVarRefs())};
+}
+
+UsesAllocators make(const OMPUsesAllocatorsClause &C) {
+  UsesAllocators::Allocators AllocList;
+  AllocList.reserve(C.getNumberOfAllocators());
+
+  using S = decltype(UsesAllocators::AllocatorSpec::t);
+  for (int i = 0, e = C.getNumberOfAllocators(); i != e; ++i) {
+    OMPUsesAllocatorsClause::Data Data = C.getAllocatorData(i);
+    AllocList.push_back(UsesAllocators::AllocatorSpec{
+        S{/*MemSpace=*/std::nullopt, /*TraitsArray=*/
+          maybeIf(makeObject(Data.AllocatorTraits),
+                  Data.AllocatorTraits != nullptr),
+          /*Allocator=*/Data.Allocator}});
+  }
+
+  return UsesAllocators{/*Allocators=*/std::move(AllocList)};
+}
+
+// When
+
+} // namespace clause
+
+Clause makeClause(OMPClause *C) {
+#define GEN_CLANG_CLAUSE_CLASS
+#define CLAUSE_CLASS(Enum, Str, Class)                                         \
+  if (auto S = llvm::dyn_cast<Class>(C))                                       \
+    return Clause{/*Base class*/ {C->getClauseKind(), clause::make(*S)}, C};
+#include "llvm/Frontend/OpenMP/OMP.inc"
+  llvm_unreachable("Unexpected clause");
+}
+
+List<Clause> makeClauses(llvm::ArrayRef<OMPClause *> Clauses) {
+  return makeList(Clauses, [](OMPClause *C) { return makeClause(C); });
+}
+} // namespace omp
+
+namespace clang {
+bool ExtConstructDecomposition::ExtConstruct::addClause(const omp::Clause &C) {
+  if (llvm::omp::isUniqueClause(C.id)) {
+    // If C is a unique clause, only add it if one like this isn't
+    // already there.
+    auto F = llvm::find_if(
+        clauses, [Id = C.id](const omp::Clause &T) { return T.id == Id; });
+    if (F != clauses.end())
+      return false;
+  } else {
+    // Don't add multiple clauses that correspond to the same AST clause.
+    if (C.tag.getFlags() == C.tag.Explicit && C.tag.getPointer()) {
+      void *Ptr = C.tag.getPointer();
+      auto F = llvm::find_if(clauses, [=](const omp::Clause &T) {
+        return T.tag.getFlags() == T.tag.Explicit && T.tag.getPointer() == Ptr;
+      });
+      if (F != clauses.end())
+        return false;
+    }
+  }
+  clauses.push_back(C);
+  return true;
+}
+
+ExtConstructDecomposition::ExtConstructDecomposition(
+    OpenMPDirectiveKind DKind, llvm::ArrayRef<omp::Clause> Clauses,
+    uint32_t OMPVersion)
+    : InputDKind(DKind), ClauseStorage(Clauses.begin(), Clauses.end()) {
+
+  Decomposer =
+      std::make_unique<DecomposerTy>(OMPVersion, *this, DKind, ClauseStorage,
+                                     /*makeImplicit=*/false);
+  if (Decomposer->output.empty())
+    return;
+
+  llvm::SmallVector<OpenMPDirectiveKind> UnitKinds;
+  std::ignore = llvm::omp::getLeafOrCompositeConstructs(DKind, UnitKinds);
+  for (OpenMPDirectiveKind Unit : UnitKinds) {
+    Output.push_back(ExtConstruct(Unit));
+    for (OpenMPDirectiveKind Leaf : llvm::omp::getLeafConstructsOrSelf(Unit)) {
+      auto P = CompositionMap.insert(std::make_pair(Leaf, Unit));
+      assert(P.second && "Repeated leaf in compound directive");
+    }
+  }
+
+  for (tomp::DirectiveWithClauses<omp::Clause> &DWC : Decomposer->output) {
+    ExtConstruct &Con = getConstruct(DWC.id);
+    for (const omp::Clause &C : DWC.clauses)
+      Con.addClause(C);
+  }
+}
+
+bool ExtConstructDecomposition::postApply(
+    const omp::Clause &C, llvm::SmallVector<OpenMPDirectiveKind> *Modified) {
+  llvm::SmallVector<size_t> ClauseCount;
+
+  for (tomp::DirectiveWithClauses<omp::Clause> &DWC : Decomposer->output)
+    ClauseCount.push_back(DWC.clauses.size());
+
+  ClauseStorage.push_back(C);
+  bool Applied = Decomposer->postApply(C);
+
+  if (!Applied)
+    return false;
+
+  for (size_t I = 0, E = Decomposer->output.size(); I != E; ++I) {
+    tomp::DirectiveWithClauses<omp::Clause> &DWC = Decomposer->output[I];
+    size_t SizeThen = ClauseCount[I], SizeNow = DWC.clauses.size();
+    if (SizeThen < SizeNow) {
+      ExtConstruct &Con = getConstruct(DWC.id);
+      for (size_t J = SizeThen; J != SizeNow; ++J)
+        Con.addClause(DWC.clauses[J]);
+      if (Modified)
+        Modified->push_back(Con.DKind);
+    }
+  }
+  return true;
+}
+
+ExtConstructDecomposition::ExtConstruct &
+ExtConstructDecomposition::getConstruct(OpenMPDirectiveKind Leaf) {
+  assert(llvm::omp::isLeafConstruct(Leaf) && "Expecting leaf construct");
+  OpenMPDirectiveKind ConKind = CompositionMap.at(Leaf);
+  auto F = llvm::find_if(
+      Output, [=](const ExtConstruct &T) { return T.DKind == ConKind; });
+  assert(F != Output.end() && "Constituent not found");
+  return *F;
+}
+
+// Given an object, return its base object if one exists.
+std::optional<omp::Object>
+ExtConstructDecomposition::getBaseObject(const omp::Object &Obj) {
+  return std::nullopt; // XXX
+}
+
+// Return the iteration variable of the associated loop if any.
+std::optional<omp::Object> ExtConstructDecomposition::getLoopIterVar() {
+  return std::nullopt; // XXX
+}
+} // namespace clang
diff --git a/clang/lib/Sema/SemaOpenMPExt.h b/clang/lib/Sema/SemaOpenMPExt.h
new file mode 100644
index 00000000000000..7e3c158466d075
--- /dev/null
+++ b/clang/lib/Sema/SemaOpenMPExt.h
@@ -0,0 +1,428 @@
+#ifndef CLANG_SEMA_SEMAOPENMPEXT_H
+#define CLANG_SEMA_SEMAOPENMPEXT_H
+
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/OpenMPKinds.h"
+#include "llvm/ADT/PointerIntPair.h"
+#include "llvm/Frontend/OpenMP/ClauseT.h"
+#include "llvm/Frontend/OpenMP/ConstructDecompositionT.h"
+#include "llvm/Frontend/OpenMP/OMPConstants.h"
+#include "llvm/Support/MathExtras.h"
+
+#include <iterator>
+#include <memory>
+#include <optional>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+namespace clang {
+class Decl;
+class Expr;
+class OMPClause;
+} // namespace clang
+
+namespace omp {
+using TypeTy = clang::QualType;
+using IdentTy = std::variant<std::monostate, const clang::Decl *,
+                             const clang::CXXThisExpr *>;
+using ExprTy = const clang::Expr *;
+} // namespace omp
+
+template <> struct tomp::type::ObjectT<omp::IdentTy, omp::ExprTy> {
+  using IdType = omp::IdentTy;
+  using ExprType = omp::ExprTy;
+
+  IdType id() const { return declaration; }
+  ExprType ref() const { return reference; }
+
+  IdType declaration;
+  ExprType reference;
+};
+
+namespace omp {
+template <typename T> //
+using List = tomp::type::ListT<T>;
+
+using Object = tomp::type::ObjectT<IdentTy, ExprTy>;
+using ObjectList = tomp::type::ObjectListT<IdentTy, ExprTy>;
+using Iterator = tomp::type::IteratorT<TypeTy, IdentTy, ExprTy>;
+using Range = tomp::type::RangeT<ExprTy>;
+using Mapper = tomp::type::MapperT<IdentTy, ExprTy>;
+
+namespace clause {
+using DefinedOperator = tomp::type::DefinedOperatorT<IdentTy, ExprTy>;
+using ProcedureDesignator = tomp::type::ProcedureDesignatorT<IdentTy, ExprTy>;
+using ReductionOperator = tomp::type::ReductionIdentifierT<IdentTy, ExprTy>;
+
+using Absent = tomp::clause::AbsentT<TypeTy, IdentTy, ExprTy>;
+using AcqRel = tomp::clause::AcqRelT<TypeTy, IdentTy, ExprTy>;
+using Acquire = tomp::clause::AcquireT<TypeTy, IdentTy, ExprTy>;
+using AdjustArgs = tomp::clause::AdjustArgsT<TypeTy, IdentTy, ExprTy>;
+using Affinity = tomp::clause::AffinityT<TypeTy, IdentTy, ExprTy>;
+using Align = tomp::clause::AlignT<TypeTy, IdentTy, ExprTy>;
+using Aligned = tomp::clause::AlignedT<TypeTy, IdentTy, ExprTy>;
+using Allocate = tomp::clause::AllocateT<TypeTy, IdentTy, ExprTy>;
+using Allocator = tomp::clause::AllocatorT<TypeTy, IdentTy, ExprTy>;
+using AppendArgs = tomp::clause::AppendArgsT<TypeTy, IdentTy, ExprTy>;
+using At = tomp::clause::AtT<TypeTy, IdentTy, ExprTy>;
+using AtomicDefaultMemOrder =
+    tomp::clause::AtomicDefaultMemOrderT<TypeTy, IdentTy, ExprTy>;
+using Bind = tomp::clause::BindT<TypeTy, IdentTy, ExprTy>;
+using Capture = tomp::clause::CaptureT<TypeTy, IdentTy, ExprTy>;
+using Collapse = tomp::clause::CollapseT<TypeTy, IdentTy, ExprTy>;
+using Compare = tomp::clause::CompareT<TypeTy, IdentTy, ExprTy>;
+using Contains = tomp::clause::ContainsT<TypeTy, IdentTy, ExprTy>;
+using Copyin = tomp::clause::CopyinT<TypeTy, IdentTy, ExprTy>;
+using Copyprivate = tomp::clause::CopyprivateT<TypeTy, IdentTy, ExprTy>;
+using Default = tomp::clause::DefaultT<TypeTy, IdentTy, ExprTy>;
+using Defaultmap = tomp::clause::DefaultmapT<TypeTy, IdentTy, ExprTy>;
+using Depend = tomp::clause::DependT<TypeTy, IdentTy, ExprTy>;
+using Destroy = tomp::clause::DestroyT<TypeTy, IdentTy, ExprTy>;
+using Detach = tomp::clause::DetachT<TypeTy, IdentTy, ExprTy>;
+using Device = tomp::clause::DeviceT<TypeTy, IdentTy, ExprTy>;
+using DeviceType = tomp::clause::DeviceTypeT<TypeTy, IdentTy, ExprTy>;
+using DistSchedule = tomp::clause::DistScheduleT<TypeTy, IdentTy, ExprTy>;
+using Doacross = tomp::clause::DoacrossT<TypeTy, IdentTy, ExprTy>;
+using DynamicAllocators =
+    tomp::clause::DynamicAllocatorsT<TypeTy, IdentTy, ExprTy>;
+using Enter = tomp::clause::EnterT<TypeTy, IdentTy, ExprTy>;
+using Exclusive = tomp::clause::ExclusiveT<TypeTy, IdentTy, ExprTy>;
+using Fail = tomp::clause::FailT<TypeTy, IdentTy, ExprTy>;
+using Filter = tomp::clause::FilterT<TypeTy, IdentTy, ExprTy>;
+using Final = tomp::clause::FinalT<TypeTy, IdentTy, ExprTy>;
+using Firstprivate = tomp::clause::FirstprivateT<TypeTy, IdentTy, ExprTy>;
+using From = tomp::clause::FromT<TypeTy, IdentTy, ExprTy>;
+using Full = tomp::clause::FullT<TypeTy, IdentTy, ExprTy>;
+using Grainsize = tomp::clause::GrainsizeT<TypeTy, IdentTy, ExprTy>;
+using HasDeviceAddr = tomp::clause::HasDeviceAddrT<TypeTy, IdentTy, ExprTy>;
+using Hint = tomp::clause::HintT<TypeTy, IdentTy, ExprTy>;
+using Holds = tomp::clause::HoldsT<TypeTy, IdentTy, ExprTy>;
+using If = tomp::clause::IfT<TypeTy, IdentTy, ExprTy>;
+using Inbranch = tomp::clause::InbranchT<TypeTy, IdentTy, ExprTy>;
+using Inclusive = tomp::clause::InclusiveT<TypeTy, IdentTy, ExprTy>;
+using Indirect = tomp::clause::IndirectT<TypeTy, IdentTy, ExprTy>;
+using Init = tomp::clause::InitT<TypeTy, IdentTy, ExprTy>;
+using Initializer = tomp::clause::InitializerT<TypeTy, IdentTy, ExprTy>;
+using InReduction = tomp::clause::InReductionT<TypeTy, IdentTy, ExprTy>;
+using IsDevicePtr = tomp::clause::IsDevicePtrT<TypeTy, IdentTy, ExprTy>;
+using Lastprivate = tomp::clause::LastprivateT<TypeTy, IdentTy, ExprTy>;
+using Linear = tomp::clause::LinearT<TypeTy, IdentTy, ExprTy>;
+using Link = tomp::clause::LinkT<TypeTy, IdentTy, ExprTy>;
+using Map = tomp::clause::MapT<TypeTy, IdentTy, ExprTy>;
+using Match = tomp::clause::MatchT<TypeTy, IdentTy, ExprTy>;
+using Mergeable = tomp::clause::MergeableT<TypeTy, IdentTy, ExprTy>;
+using Message = tomp::clause::MessageT<TypeTy, IdentTy, ExprTy>;
+using Nocontext = tomp::clause::NocontextT<TypeTy, IdentTy, ExprTy>;
+using Nogroup = tomp::clause::NogroupT<TypeTy, IdentTy, ExprTy>;
+using Nontemporal = tomp::clause::NontemporalT<TypeTy, IdentTy, ExprTy>;
+using NoOpenmp = tomp::clause::NoOpenmpT<TypeTy, IdentTy, ExprTy>;
+using NoOpenmpRoutines =
+    tomp::clause::NoOpenmpRoutinesT<TypeTy, IdentTy, ExprTy>;
+using NoParallelism = tomp::clause::NoParallelismT<TypeTy, IdentTy, ExprTy>;
+using Notinbranch = tomp::clause::NotinbranchT<TypeTy, IdentTy, ExprTy>;
+using Novariants = tomp::clause::NovariantsT<TypeTy, IdentTy, ExprTy>;
+using Nowait = tomp::clause::NowaitT<TypeTy, IdentTy, ExprTy>;
+using NumTasks = tomp::clause::NumTasksT<TypeTy, IdentTy, ExprTy>;
+using NumTeams = tomp::clause::NumTeamsT<TypeTy, IdentTy, ExprTy>;
+using NumThreads = tomp::clause::NumThreadsT<TypeTy, IdentTy, ExprTy>;
+using OmpxAttribute = tomp::clause::OmpxAttributeT<TypeTy, IdentTy, ExprTy>;
+using OmpxBare = tomp::clause::OmpxBareT<TypeTy, IdentTy, ExprTy>;
+using OmpxDynCgroupMem =
+    tomp::clause::OmpxDynCgroupMemT<TypeTy, IdentTy, ExprTy>;
+using Order = tomp::clause::OrderT<TypeTy, IdentTy, ExprTy>;
+using Ordered = tomp::clause::OrderedT<TypeTy, IdentTy, ExprTy>;
+using Otherwise = tomp::clause::OtherwiseT<TypeTy, IdentTy, ExprTy>;
+using Partial = tomp::clause::PartialT<TypeTy, IdentTy, ExprTy>;
+using Priority = tomp::clause::PriorityT<TypeTy, IdentTy, ExprTy>;
+using Private = tomp::clause::PrivateT<TypeTy, IdentTy, ExprTy>;
+using ProcBind = tomp::clause::ProcBindT<TypeTy, IdentTy, ExprTy>;
+using Read = tomp::clause::ReadT<TypeTy, IdentTy, ExprTy>;
+using Reduction = tomp::clause::ReductionT<TypeTy, IdentTy, ExprTy>;
+using Relaxed = tomp::clause::RelaxedT<TypeTy, IdentTy, ExprTy>;
+using Release = tomp::clause::ReleaseT<TypeTy, IdentTy, ExprTy>;
+using ReverseOffload = tomp::clause::ReverseOffloadT<TypeTy, IdentTy, ExprTy>;
+using Safelen = tomp::clause::SafelenT<TypeTy, IdentTy, ExprTy>;
+using Schedule = tomp::clause::ScheduleT<TypeTy, IdentTy, ExprTy>;
+using SeqCst = tomp::clause::SeqCstT<TypeTy, IdentTy, ExprTy>;
+using Severity = tomp::clause::SeverityT<TypeTy, IdentTy, ExprTy>;
+using Shared = tomp::clause::SharedT<TypeTy, IdentTy, ExprTy>;
+using Simd = tomp::clause::SimdT<TypeTy, IdentTy, ExprTy>;
+using Simdlen = tomp::clause::SimdlenT<TypeTy, IdentTy, ExprTy>;
+using Sizes = tomp::clause::SizesT<TypeTy, IdentTy, ExprTy>;
+using TaskReduction = tomp::clause::TaskReductionT<TypeTy, IdentTy, ExprTy>;
+using ThreadLimit = tomp::clause::ThreadLimitT<TypeTy, IdentTy, ExprTy>;
+using Threads = tomp::clause::ThreadsT<TypeTy, IdentTy, ExprTy>;
+using To = tomp::clause::ToT<TypeTy, IdentTy, ExprTy>;
+using UnifiedAddress = tomp::clause::UnifiedAddressT<TypeTy, IdentTy, ExprTy>;
+using UnifiedSharedMemory =
+    tomp::clause::UnifiedSharedMemoryT<TypeTy, IdentTy, ExprTy>;
+using Uniform = tomp::clause::UniformT<TypeTy, IdentTy, ExprTy>;
+using Unknown = tomp::clause::UnknownT<TypeTy, IdentTy, ExprTy>;
+using Untied = tomp::clause::UntiedT<TypeTy, IdentTy, ExprTy>;
+using Update = tomp::clause::UpdateT<TypeTy, IdentTy, ExprTy>;
+using Use = tomp::clause::UseT<TypeTy, IdentTy, ExprTy>;
+using UseDeviceAddr = tomp::clause::UseDeviceAddrT<TypeTy, IdentTy, ExprTy>;
+using UseDevicePtr = tomp::clause::UseDevicePtrT<TypeTy, IdentTy, ExprTy>;
+using UsesAllocators = tomp::clause::UsesAllocatorsT<TypeTy, IdentTy, ExprTy>;
+using Weak = tomp::clause::WeakT<TypeTy, IdentTy, ExprTy>;
+using When = tomp::clause::WhenT<TypeTy, IdentTy, ExprTy>;
+using Write = tomp::clause::WriteT<TypeTy, IdentTy, ExprTy>;
+} // namespace clause
+
+using tomp::type::operator==;
+
+// Manually define artificial clauses
+struct Depobj {
+  using EmptyTrait = std::true_type;
+};
+struct Flush {
+  using EmptyTrait = std::true_type;
+};
+
+using ClauseBase = tomp::ClauseT<TypeTy, IdentTy, ExprTy,
+                                 // Artificial clauses:
+                                 Depobj, Flush>;
+
+struct Clause : public ClauseBase {
+  struct TagType;
+
+  Clause(ClauseBase &&Base, TagType Tag)
+      : ClauseBase(std::move(Base)), tag(Tag) {}
+  Clause(ClauseBase &&Base, const clang::OMPClause *C = nullptr)
+      : Clause(std::move(Base), TagType::get(C)) {}
+
+  struct TagType {
+    using StorageTy = std::common_type_t<uint64_t, uintptr_t>;
+
+    enum : unsigned {
+      Explicit = 0, // The clause should be treated as explicit. If the clause
+                    // originated from AST, Storage contains the OMPClause*,
+                    // otherwise nullptr.
+                    // This is the value when TagType is default-initialized.
+                    // Explicit clauses will be applied early (before captured
+                    // region is closed), non-explicit clauses may be applied
+                    // after closing of the region.
+      Simple,       // The clause is implicit, and should be turned into an
+                    // AST node with default settings (empty location, etc.).
+                    // Storage is ignored.
+      Mapping,      // The clause implicit and is "map", Storage is A[2]:
+                    // A[0] = Defaultmap category, A[1] = MapType.
+    };
+
+    static TagType get(const clang::OMPClause *C) {
+      return TagType{Explicit, reinterpret_cast<StorageTy>(C)};
+    }
+    static TagType get() { return TagType{Simple, 0x0}; }
+    static TagType get(unsigned DefType, unsigned MapType) {
+      return TagType{Mapping, llvm::Make_64(/*Hi=*/MapType, /*Lo=*/DefType)};
+    }
+
+    unsigned getFlags() const { return Flags; }
+    void *getPointer() const { return reinterpret_cast<void *>(Storage); }
+    std::pair<unsigned, unsigned> getMapKinds() const {
+      return {llvm::Lo_32(Storage), llvm::Hi_32(Storage)};
+    }
+
+    unsigned Flags = 0x0;
+    StorageTy Storage = 0x0;
+  };
+
+  TagType tag;
+};
+
+Clause makeClause(clang::OMPClause *C);
+List<Clause> makeClauses(llvm::ArrayRef<clang::OMPClause *> AstClauses);
+
+Object makeObject(const clang::Expr *E);
+ObjectList makeObjects(llvm::ArrayRef<const clang::Expr *> Vars);
+} // namespace omp
+
+namespace ext {
+// "Rename" the omp namespace to ext to make it visibly distinct from
+// other OpenMP symbols.
+using namespace omp;
+
+using MemoryOrder = tomp::type::MemoryOrder;
+
+inline std::optional<tomp::type::DirectiveName> conv(llvm::omp::Directive T) {
+  if (T != llvm::omp::Directive::OMPD_unknown)
+    return T;
+  return std::nullopt;
+}
+
+// Conversions between enums used by omp::Clause and clang.
+// Using trailing return, otherwise these declarations are visually unparseable.
+auto conv(llvm::omp::DefaultKind T)
+    -> std::optional<clause::Default::DataSharingAttribute>;
+auto conv(llvm::omp::ProcBindKind T)
+    -> std::optional<clause::ProcBind::AffinityPolicy>;
+auto conv(clang::OpenMPAtClauseKind T) //
+    -> std::optional<clause::At::ActionTime>;
+auto conv(clang::OpenMPAtomicDefaultMemOrderClauseKind T)
+    -> std::optional<MemoryOrder>;
+auto conv(clang::OpenMPBindClauseKind T)
+    -> std::optional<clause::Bind::Binding>;
+auto conv(clang::OpenMPDefaultmapClauseKind T)
+    -> std::optional<clause::Defaultmap::VariableCategory>;
+auto conv(clang::OpenMPDefaultmapClauseModifier T)
+    -> std::optional<clause::Defaultmap::ImplicitBehavior>;
+auto conv(clang::OpenMPDeviceClauseModifier T)
+    -> std::optional<clause::Device::DeviceModifier>;
+auto conv(clang::OpenMPDistScheduleClauseKind T)
+    -> std::optional<clause::DistSchedule::Kind>;
+auto conv(clang::OpenMPGrainsizeClauseModifier T)
+    -> std::optional<clause::Grainsize::Prescriptiveness>;
+auto conv(clang::OpenMPLastprivateModifier T)
+    -> std::optional<clause::Lastprivate::LastprivateModifier>;
+auto conv(clang::OpenMPLinearClauseKind T)
+    -> std::optional<clause::Linear::LinearModifier>;
+auto conv(clang::OpenMPMapClauseKind T) //
+    -> std::optional<clause::Map::MapType>;
+auto conv(clang::OpenMPMapModifierKind T)
+    -> std::optional<clause::Map::MapTypeModifier>;
+auto conv(clang::OpenMPNumTasksClauseModifier T)
+    -> std::optional<clause::NumTasks::Prescriptiveness>;
+auto conv(clang::OpenMPOrderClauseKind T)
+    -> std::optional<clause::Order::Ordering>;
+auto conv(clang::OpenMPOrderClauseModifier T)
+    -> std::optional<clause::Order::OrderModifier>;
+auto conv(clang::OpenMPReductionClauseModifier T)
+    -> std::optional<clause::Reduction::ReductionModifier>;
+auto conv(clang::OpenMPSeverityClauseKind T)
+    -> std::optional<clause::Severity::SevLevel>;
+auto conv(clang::OverloadedOperatorKind T)
+    -> clause::DefinedOperator::IntrinsicOperator;
+
+auto vnoc(clause::Default::DataSharingAttribute T) -> llvm::omp::DefaultKind;
+auto vnoc(clause::ProcBind::AffinityPolicy T) -> llvm::omp::ProcBindKind;
+auto vnoc(clause::At::ActionTime T) -> clang::OpenMPAtClauseKind;
+auto vnoc(MemoryOrder T) -> clang::OpenMPAtomicDefaultMemOrderClauseKind;
+auto vnoc(clause::Bind::Binding T) -> clang::OpenMPBindClauseKind;
+auto vnoc(clause::Defaultmap::VariableCategory T)
+    -> clang::OpenMPDefaultmapClauseKind;
+auto vnoc(clause::Defaultmap::ImplicitBehavior T)
+    -> clang::OpenMPDefaultmapClauseModifier;
+auto vnoc(clause::Device::DeviceModifier T)
+    -> clang::OpenMPDeviceClauseModifier;
+auto vnoc(clause::DistSchedule::Kind T) -> clang::OpenMPDistScheduleClauseKind;
+auto vnoc(clause::Grainsize::Prescriptiveness T)
+    -> clang::OpenMPGrainsizeClauseModifier;
+auto vnoc(clause::Lastprivate::LastprivateModifier T)
+    -> clang::OpenMPLastprivateModifier;
+auto vnoc(clause::Linear::LinearModifier T) -> clang::OpenMPLinearClauseKind;
+auto vnoc(clause::Map::MapType T) -> clang::OpenMPMapClauseKind;
+auto vnoc(clause::Map::MapTypeModifier T) -> clang::OpenMPMapModifierKind;
+auto vnoc(clause::NumTasks::Prescriptiveness)
+    -> clang::OpenMPNumTasksClauseModifier;
+auto vnoc(clause::Order::Ordering T) -> clang::OpenMPOrderClauseKind;
+auto vnoc(clause::Order::OrderModifier T) -> clang::OpenMPOrderClauseModifier;
+auto vnoc(clause::Reduction::ReductionModifier T)
+    -> clang::OpenMPReductionClauseModifier;
+auto vnoc(clause::Severity::SevLevel T) -> clang::OpenMPSeverityClauseKind;
+} // namespace ext
+
+namespace clang {
+using OpenMPDirectiveKind = llvm::omp::Directive;
+using OpenMPClauseKind = llvm::omp::Clause;
+
+template <typename ContainerTy> struct QueueAdapter {
+  using value_type = typename ContainerTy::value_type;
+  using iterator = typename ContainerTy::iterator;
+  using const_iterator = typename ContainerTy::const_iterator;
+
+  QueueAdapter() : Container(nullptr), StartIdx(0) {}
+  QueueAdapter(ContainerTy &C) : Container(&C), StartIdx(C.size()) {}
+  template <typename OtherTy,
+            std::enable_if_t<std::is_same_v<value_type, OtherTy::value_type>,
+                             int> = 0>
+  QueueAdapter &operator=(OtherTy &C) {
+    Container = &C;
+    StartIdx = C.size();
+  }
+
+  value_type &take() {
+    assert(StartIdx < Container->size() && "Taking from empty queue");
+    return (*Container)[StartIdx++];
+  }
+
+  ArrayRef<value_type> takeAll() {
+    ArrayRef Res(begin(), end());
+    StartIdx += Res.size();
+    return Res;
+  }
+
+  size_t size() const { return Container->size() - StartIdx; }
+  bool empty() const { return size() == 0; }
+
+  ContainerTy *Container;
+
+  iterator begin() {
+    iterator it = Container->begin();
+    std::advance(it, StartIdx);
+    return it;
+  }
+  iterator end() { return Container->end(); }
+  const iterator begin() const {
+    const iterator it = Container->begin();
+    std::advance(it, StartIdx);
+    return it;
+  }
+  const_iterator end() const { return Container->end(); }
+
+private:
+  size_t StartIdx;
+};
+
+template <typename ContainerTy>
+QueueAdapter(ContainerTy) -> QueueAdapter<ContainerTy>;
+
+struct ExtConstructDecomposition {
+  struct ExtConstruct : private tomp::DirectiveWithClauses<omp::Clause> {
+    using BaseTy = tomp::DirectiveWithClauses<omp::Clause>;
+
+    ExtConstruct(const ExtConstruct &Con)
+        : BaseTy(Con), DKind(id), ClausesQ(clauses) {}
+    ExtConstruct(ExtConstruct &&Con)
+        : BaseTy(std::move(Con)), DKind(id), ClausesQ(clauses) {}
+    ExtConstruct(OpenMPDirectiveKind DK = llvm::omp::OMPD_unknown)
+        : DKind(id), ClausesQ(clauses) {
+      id = DK;
+    }
+    bool addClause(const omp::Clause &C);
+
+    OpenMPDirectiveKind &DKind;
+    QueueAdapter<decltype(clauses)> ClausesQ;
+  };
+
+  ExtConstructDecomposition(OpenMPDirectiveKind DKind,
+                            llvm::ArrayRef<omp::Clause> Clauses,
+                            uint32_t OMPVersion);
+
+  bool postApply(const omp::Clause &C,
+                 llvm::SmallVector<OpenMPDirectiveKind> *Modified);
+
+  // Given an object, return its base object if one exists.
+  std::optional<omp::Object> getBaseObject(const omp::Object &Obj);
+
+  // Return the iteration variable of the associated loop if any.
+  std::optional<omp::Object> getLoopIterVar();
+
+  OpenMPDirectiveKind InputDKind;
+  llvm::SmallVector<omp::Clause> ClauseStorage;
+  // Map: leaf -> leaf-or-composite construct containing the leaf,
+  // for each leaf constituent of the original directive.
+  llvm::DenseMap<OpenMPDirectiveKind, OpenMPDirectiveKind> CompositionMap;
+  llvm::SmallVector<ExtConstruct> Output;
+
+private:
+  using DecomposerTy =
+      tomp::ConstructDecompositionT<omp::Clause, ExtConstructDecomposition>;
+  std::unique_ptr<DecomposerTy> Decomposer;
+
+  ExtConstruct &getConstruct(OpenMPDirectiveKind Leaf);
+};
+} // namespace clang
+
+#endif // CLANG_SEMA_SEMAOPENMPEXT_H
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index ff745b3385fcd9..d7ff8ac79acc20 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9204,6 +9204,8 @@ TreeTransform<Derived>::TransformOMPCanonicalLoop(OMPCanonicalLoop *L) {
 template <typename Derived>
 StmtResult TreeTransform<Derived>::TransformOMPExecutableDirective(
     OMPExecutableDirective *D) {
+  // If D is OMPCompoundBlockDirective or OMPCompoundLoopDirective,
+  // then D->getDirectiveKind() will return the actual directive kind.
 
   // Transform the clauses
   llvm::SmallVector<OMPClause *, 16> TClauses;
@@ -9222,32 +9224,31 @@ StmtResult TreeTransform<Derived>::TransformOMPExecutableDirective(
     }
   }
   StmtResult AssociatedStmt;
-  if (D->hasAssociatedStmt() && D->getAssociatedStmt()) {
-    getDerived().getSema().OpenMP().ActOnOpenMPRegionStart(
-        D->getDirectiveKind(),
-        /*CurScope=*/nullptr);
-    StmtResult Body;
-    {
-      Sema::CompoundScopeRAII CompoundScope(getSema());
-      Stmt *CS;
-      if (D->getDirectiveKind() == OMPD_atomic ||
-          D->getDirectiveKind() == OMPD_critical ||
-          D->getDirectiveKind() == OMPD_section ||
-          D->getDirectiveKind() == OMPD_master)
-        CS = D->getAssociatedStmt();
-      else
-        CS = D->getRawStmt();
-      Body = getDerived().TransformStmt(CS);
-      if (Body.isUsable() && isOpenMPLoopDirective(D->getDirectiveKind()) &&
-          getSema().getLangOpts().OpenMPIRBuilder)
-        Body = getDerived().RebuildOMPCanonicalLoop(Body.get());
-    }
-    AssociatedStmt =
-        getDerived().getSema().OpenMP().ActOnOpenMPRegionEnd(Body, TClauses);
-    if (AssociatedStmt.isInvalid()) {
-      return StmtError();
-    }
-  }
+  bool HasAssociatedStatement =
+      D->hasAssociatedStmt() && D->getAssociatedStmt();
+  getDerived().getSema().OpenMP().ActOnOpenMPRegionStart(
+      D->getDirectiveKind(), /*CurScope=*/nullptr, HasAssociatedStatement);
+  if (HasAssociatedStatement) {
+    Sema::CompoundScopeRAII CompoundScope(getSema());
+    Stmt *CS;
+    if (D->getDirectiveKind() == OMPD_atomic ||
+        D->getDirectiveKind() == OMPD_critical ||
+        D->getDirectiveKind() == OMPD_section ||
+        D->getDirectiveKind() == OMPD_master)
+      CS = D->getAssociatedStmt();
+    else
+      CS = D->getRawStmt();
+    AssociatedStmt = getDerived().TransformStmt(CS);
+    if (AssociatedStmt.isUsable() &&
+        isOpenMPLoopDirective(D->getDirectiveKind()) &&
+        getSema().getLangOpts().OpenMPIRBuilder)
+      AssociatedStmt =
+          getDerived().RebuildOMPCanonicalLoop(AssociatedStmt.get());
+  }
+  AssociatedStmt = getDerived().getSema().OpenMP().ActOnOpenMPRegionEnd(
+      AssociatedStmt, TClauses);
+  if (AssociatedStmt.isInvalid())
+    return StmtError();
   if (TClauses.size() != Clauses.size()) {
     return StmtError();
   }
@@ -9255,14 +9256,29 @@ StmtResult TreeTransform<Derived>::TransformOMPExecutableDirective(
   // Transform directive name for 'omp critical' directive.
   DeclarationNameInfo DirName;
   if (D->getDirectiveKind() == OMPD_critical) {
-    DirName = cast<OMPCriticalDirective>(D)->getDirectiveName();
+    if (auto *C = dyn_cast<OMPCriticalDirective>(D))
+      DirName = C->getDirectiveName();
+    else if (auto *C = dyn_cast<OMPCompoundBlockDirective>(D))
+      DirName = C->getDirectiveName();
+    else
+      llvm_unreachable("Unexpected directive class");
     DirName = getDerived().TransformDeclarationNameInfo(DirName);
   }
   OpenMPDirectiveKind CancelRegion = OMPD_unknown;
   if (D->getDirectiveKind() == OMPD_cancellation_point) {
-    CancelRegion = cast<OMPCancellationPointDirective>(D)->getCancelRegion();
+    if (auto *C = dyn_cast<OMPCancellationPointDirective>(D))
+      CancelRegion = C->getCancelRegion();
+    else if (auto *C = dyn_cast<OMPCompoundBlockDirective>(D))
+      CancelRegion = C->getCancelRegion();
+    else
+      llvm_unreachable("Unexpected directive class");
   } else if (D->getDirectiveKind() == OMPD_cancel) {
-    CancelRegion = cast<OMPCancelDirective>(D)->getCancelRegion();
+    if (auto *C = dyn_cast<OMPCancelDirective>(D))
+      CancelRegion = C->getCancelRegion();
+    else if (auto *C = dyn_cast<OMPCompoundBlockDirective>(D))
+      CancelRegion = C->getCancelRegion();
+    else
+      llvm_unreachable("Unexpected directive class");
   }
 
   return getDerived().RebuildOMPExecutableDirective(
@@ -9295,23 +9311,21 @@ StmtResult TreeTransform<Derived>::TransformOMPInformationalDirective(
     }
   }
   StmtResult AssociatedStmt;
-  if (D->hasAssociatedStmt() && D->getAssociatedStmt()) {
-    getDerived().getSema().OpenMP().ActOnOpenMPRegionStart(
-        D->getDirectiveKind(),
-        /*CurScope=*/nullptr);
-    StmtResult Body;
-    {
-      Sema::CompoundScopeRAII CompoundScope(getSema());
-      assert(D->getDirectiveKind() == OMPD_assume &&
-             "Unexpected informational directive");
-      Stmt *CS = D->getAssociatedStmt();
-      Body = getDerived().TransformStmt(CS);
-    }
-    AssociatedStmt =
-        getDerived().getSema().OpenMP().ActOnOpenMPRegionEnd(Body, TClauses);
-    if (AssociatedStmt.isInvalid())
-      return StmtError();
-  }
+  bool HasAssociatedStatement =
+      D->hasAssociatedStmt() && D->getAssociatedStmt();
+  getDerived().getSema().OpenMP().ActOnOpenMPRegionStart(
+      D->getDirectiveKind(), /*CurScope=*/nullptr, HasAssociatedStatement);
+  if (HasAssociatedStatement) {
+    Sema::CompoundScopeRAII CompoundScope(getSema());
+    assert(D->getDirectiveKind() == OMPD_assume &&
+           "Unexpected informational directive");
+    Stmt *CS = D->getAssociatedStmt();
+    AssociatedStmt = getDerived().TransformStmt(CS);
+  }
+  AssociatedStmt = getDerived().getSema().OpenMP().ActOnOpenMPRegionEnd(
+      AssociatedStmt, TClauses);
+  if (AssociatedStmt.isInvalid())
+    return StmtError();
   if (TClauses.size() != Clauses.size())
     return StmtError();
 
@@ -9322,6 +9336,30 @@ StmtResult TreeTransform<Derived>::TransformOMPInformationalDirective(
       D->getBeginLoc(), D->getEndLoc());
 }
 
+template <typename Derived>
+StmtResult
+TreeTransform<Derived>::TransformOMPCompoundBlockDirective(
+    OMPCompoundBlockDirective *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>::TransformOMPCompoundLoopDirective(
+    OMPCompoundLoopDirective *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>::TransformOMPMetaDirective(OMPMetaDirective *D) {
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 84743a52d4c8b8..a0f1816ba463a4 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2394,7 +2394,6 @@ void ASTStmtReader::VisitOMPExecutableDirective(OMPExecutableDirective *E) {
 }
 
 void ASTStmtReader::VisitOMPLoopBasedDirective(OMPLoopBasedDirective *D) {
-  VisitStmt(D);
   // Field CollapsedNum was read in ReadStmtFromStream.
   Record.skipInts(1);
   VisitOMPExecutableDirective(D);
@@ -2404,6 +2403,25 @@ void ASTStmtReader::VisitOMPLoopDirective(OMPLoopDirective *D) {
   VisitOMPLoopBasedDirective(D);
 }
 
+void ASTStmtReader::VisitOMPCompoundBlockDirective(
+    OMPCompoundBlockDirective *D) {
+  VisitStmt(D);
+  // The DKind was read in ReadStmtFromStream.
+  Record.skipInts(1);
+  VisitOMPExecutableDirective(D);
+  D->setHasCancel(Record.readBool());
+  D->setCancelRegion(Record.readEnum<OpenMPDirectiveKind>());
+  D->setDirectiveName(Record.readDeclarationNameInfo());
+}
+
+void ASTStmtReader::VisitOMPCompoundLoopDirective(OMPCompoundLoopDirective *D) {
+  VisitStmt(D);
+  // The DKind was read in ReadStmtFromStream.
+  Record.skipInts(1);
+  VisitOMPLoopDirective(D);
+  D->setHasCancel(Record.readBool());
+}
+
 void ASTStmtReader::VisitOMPMetaDirective(OMPMetaDirective *D) {
   VisitStmt(D);
   // The NumClauses field was read in ReadStmtFromStream.
@@ -2418,11 +2436,13 @@ void ASTStmtReader::VisitOMPParallelDirective(OMPParallelDirective *D) {
 }
 
 void ASTStmtReader::VisitOMPSimdDirective(OMPSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPLoopTransformationDirective(
     OMPLoopTransformationDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopBasedDirective(D);
   D->setNumGeneratedLoops(Record.readUInt32());
 }
@@ -2444,11 +2464,13 @@ void ASTStmtReader::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) {
 }
 
 void ASTStmtReader::VisitOMPForDirective(OMPForDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   D->setHasCancel(Record.readBool());
 }
 
 void ASTStmtReader::VisitOMPForSimdDirective(OMPForSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
@@ -2486,12 +2508,14 @@ void ASTStmtReader::VisitOMPCriticalDirective(OMPCriticalDirective *D) {
 }
 
 void ASTStmtReader::VisitOMPParallelForDirective(OMPParallelForDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   D->setHasCancel(Record.readBool());
 }
 
 void ASTStmtReader::VisitOMPParallelForSimdDirective(
     OMPParallelForSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
@@ -2613,6 +2637,7 @@ void ASTStmtReader::VisitOMPTargetParallelDirective(
 
 void ASTStmtReader::VisitOMPTargetParallelForDirective(
     OMPTargetParallelForDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   D->setHasCancel(Record.readBool());
 }
@@ -2636,59 +2661,70 @@ void ASTStmtReader::VisitOMPCancelDirective(OMPCancelDirective *D) {
 }
 
 void ASTStmtReader::VisitOMPTaskLoopDirective(OMPTaskLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   D->setHasCancel(Record.readBool());
 }
 
 void ASTStmtReader::VisitOMPTaskLoopSimdDirective(OMPTaskLoopSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPMasterTaskLoopDirective(
     OMPMasterTaskLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   D->setHasCancel(Record.readBool());
 }
 
 void ASTStmtReader::VisitOMPMaskedTaskLoopDirective(
     OMPMaskedTaskLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   D->setHasCancel(Record.readBool());
 }
 
 void ASTStmtReader::VisitOMPMasterTaskLoopSimdDirective(
     OMPMasterTaskLoopSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPMaskedTaskLoopSimdDirective(
     OMPMaskedTaskLoopSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPParallelMasterTaskLoopDirective(
     OMPParallelMasterTaskLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   D->setHasCancel(Record.readBool());
 }
 
 void ASTStmtReader::VisitOMPParallelMaskedTaskLoopDirective(
     OMPParallelMaskedTaskLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   D->setHasCancel(Record.readBool());
 }
 
 void ASTStmtReader::VisitOMPParallelMasterTaskLoopSimdDirective(
     OMPParallelMasterTaskLoopSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPParallelMaskedTaskLoopSimdDirective(
     OMPParallelMaskedTaskLoopSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPDistributeDirective(OMPDistributeDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
@@ -2699,46 +2735,55 @@ void ASTStmtReader::VisitOMPTargetUpdateDirective(OMPTargetUpdateDirective *D) {
 
 void ASTStmtReader::VisitOMPDistributeParallelForDirective(
     OMPDistributeParallelForDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   D->setHasCancel(Record.readBool());
 }
 
 void ASTStmtReader::VisitOMPDistributeParallelForSimdDirective(
     OMPDistributeParallelForSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPDistributeSimdDirective(
     OMPDistributeSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPTargetParallelForSimdDirective(
     OMPTargetParallelForSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPTargetSimdDirective(OMPTargetSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPTeamsDistributeDirective(
     OMPTeamsDistributeDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPTeamsDistributeSimdDirective(
     OMPTeamsDistributeSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPTeamsDistributeParallelForSimdDirective(
     OMPTeamsDistributeParallelForSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPTeamsDistributeParallelForDirective(
     OMPTeamsDistributeParallelForDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   D->setHasCancel(Record.readBool());
 }
@@ -2750,22 +2795,26 @@ void ASTStmtReader::VisitOMPTargetTeamsDirective(OMPTargetTeamsDirective *D) {
 
 void ASTStmtReader::VisitOMPTargetTeamsDistributeDirective(
     OMPTargetTeamsDistributeDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPTargetTeamsDistributeParallelForDirective(
     OMPTargetTeamsDistributeParallelForDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   D->setHasCancel(Record.readBool());
 }
 
 void ASTStmtReader::VisitOMPTargetTeamsDistributeParallelForSimdDirective(
     OMPTargetTeamsDistributeParallelForSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPTargetTeamsDistributeSimdDirective(
     OMPTargetTeamsDistributeSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
@@ -2786,27 +2835,32 @@ void ASTStmtReader::VisitOMPMaskedDirective(OMPMaskedDirective *D) {
 }
 
 void ASTStmtReader::VisitOMPGenericLoopDirective(OMPGenericLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPTeamsGenericLoopDirective(
     OMPTeamsGenericLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPTargetTeamsGenericLoopDirective(
     OMPTargetTeamsGenericLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   D->setCanBeParallelFor(Record.readBool());
 }
 
 void ASTStmtReader::VisitOMPParallelGenericLoopDirective(
     OMPParallelGenericLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
 void ASTStmtReader::VisitOMPTargetParallelGenericLoopDirective(
     OMPTargetParallelGenericLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
 }
 
@@ -3445,6 +3499,27 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       S = OMPCanonicalLoop::createEmpty(Context);
       break;
 
+    case STMT_OMP_COMPOUND_BLOCK_DIRECTIVE: {
+      unsigned DKind = Record[ASTStmtReader::NumStmtFields];
+      unsigned NumClauses = Record[ASTStmtReader::NumStmtFields + 1];
+      // NumChildren is at index + 2.
+      bool HasAssociatedStmt = Record[ASTStmtReader::NumStmtFields + 3];
+      S = OMPCompoundBlockDirective::CreateEmpty(
+          Context, static_cast<OpenMPDirectiveKind>(DKind), NumClauses,
+          HasAssociatedStmt, Empty);
+      break;
+    }
+
+    case STMT_OMP_COMPOUND_LOOP_DIRECTIVE: {
+      unsigned DKind = Record[ASTStmtReader::NumStmtFields];
+      unsigned CollapsedNum = Record[ASTStmtReader::NumStmtFields + 1];
+      unsigned NumClauses = Record[ASTStmtReader::NumStmtFields + 2];
+      S = OMPCompoundLoopDirective::CreateEmpty(
+          Context, static_cast<OpenMPDirectiveKind>(DKind), NumClauses,
+          CollapsedNum, Empty);
+      break;
+    }
+
     case STMT_OMP_META_DIRECTIVE:
       S = OMPMetaDirective::CreateEmpty(
           Context, Record[ASTStmtReader::NumStmtFields], Empty);
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 837136600181c1..93272fcb143035 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2393,7 +2393,6 @@ void ASTStmtWriter::VisitOMPExecutableDirective(OMPExecutableDirective *E) {
 }
 
 void ASTStmtWriter::VisitOMPLoopBasedDirective(OMPLoopBasedDirective *D) {
-  VisitStmt(D);
   Record.writeUInt32(D->getLoopsNumber());
   VisitOMPExecutableDirective(D);
 }
@@ -2402,6 +2401,26 @@ void ASTStmtWriter::VisitOMPLoopDirective(OMPLoopDirective *D) {
   VisitOMPLoopBasedDirective(D);
 }
 
+void ASTStmtWriter::VisitOMPCompoundBlockDirective(
+    OMPCompoundBlockDirective *D) {
+  VisitStmt(D);
+  Record.writeUInt32(static_cast<unsigned>(D->getDirectiveKind()));
+  VisitOMPExecutableDirective(D);
+  Record.writeBool(D->hasCancel());
+  Record.writeEnum(D->getCancelRegion());
+  Record.AddDeclarationNameInfo(D->getDirectiveName());
+  Code = serialization::STMT_OMP_COMPOUND_BLOCK_DIRECTIVE;
+}
+
+void ASTStmtWriter::VisitOMPCompoundLoopDirective(
+    OMPCompoundLoopDirective *D) {
+  VisitStmt(D);
+  Record.writeUInt32(static_cast<unsigned>(D->getDirectiveKind()));
+  VisitOMPLoopDirective(D);
+  Record.writeBool(D->hasCancel());
+  Code = serialization::STMT_OMP_COMPOUND_LOOP_DIRECTIVE;
+}
+
 void ASTStmtWriter::VisitOMPMetaDirective(OMPMetaDirective *D) {
   VisitStmt(D);
   Record.push_back(D->getNumClauses());
@@ -2417,12 +2436,14 @@ void ASTStmtWriter::VisitOMPParallelDirective(OMPParallelDirective *D) {
 }
 
 void ASTStmtWriter::VisitOMPSimdDirective(OMPSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_SIMD_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPLoopTransformationDirective(
     OMPLoopTransformationDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopBasedDirective(D);
   Record.writeUInt32(D->getNumGeneratedLoops());
 }
@@ -2448,12 +2469,14 @@ void ASTStmtWriter::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) {
 }
 
 void ASTStmtWriter::VisitOMPForDirective(OMPForDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Record.writeBool(D->hasCancel());
   Code = serialization::STMT_OMP_FOR_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPForSimdDirective(OMPForSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_FOR_SIMD_DIRECTIVE;
 }
@@ -2498,6 +2521,7 @@ void ASTStmtWriter::VisitOMPCriticalDirective(OMPCriticalDirective *D) {
 }
 
 void ASTStmtWriter::VisitOMPParallelForDirective(OMPParallelForDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Record.writeBool(D->hasCancel());
   Code = serialization::STMT_OMP_PARALLEL_FOR_DIRECTIVE;
@@ -2505,6 +2529,7 @@ void ASTStmtWriter::VisitOMPParallelForDirective(OMPParallelForDirective *D) {
 
 void ASTStmtWriter::VisitOMPParallelForSimdDirective(
     OMPParallelForSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_PARALLEL_FOR_SIMD_DIRECTIVE;
 }
@@ -2583,6 +2608,7 @@ void ASTStmtWriter::VisitOMPTargetParallelDirective(
 
 void ASTStmtWriter::VisitOMPTargetParallelForDirective(
     OMPTargetParallelForDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Record.writeBool(D->hasCancel());
   Code = serialization::STMT_OMP_TARGET_PARALLEL_FOR_DIRECTIVE;
@@ -2672,18 +2698,21 @@ void ASTStmtWriter::VisitOMPCancelDirective(OMPCancelDirective *D) {
 }
 
 void ASTStmtWriter::VisitOMPTaskLoopDirective(OMPTaskLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Record.writeBool(D->hasCancel());
   Code = serialization::STMT_OMP_TASKLOOP_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPTaskLoopSimdDirective(OMPTaskLoopSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_TASKLOOP_SIMD_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPMasterTaskLoopDirective(
     OMPMasterTaskLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Record.writeBool(D->hasCancel());
   Code = serialization::STMT_OMP_MASTER_TASKLOOP_DIRECTIVE;
@@ -2691,6 +2720,7 @@ void ASTStmtWriter::VisitOMPMasterTaskLoopDirective(
 
 void ASTStmtWriter::VisitOMPMaskedTaskLoopDirective(
     OMPMaskedTaskLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Record.writeBool(D->hasCancel());
   Code = serialization::STMT_OMP_MASKED_TASKLOOP_DIRECTIVE;
@@ -2698,18 +2728,21 @@ void ASTStmtWriter::VisitOMPMaskedTaskLoopDirective(
 
 void ASTStmtWriter::VisitOMPMasterTaskLoopSimdDirective(
     OMPMasterTaskLoopSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_MASTER_TASKLOOP_SIMD_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPMaskedTaskLoopSimdDirective(
     OMPMaskedTaskLoopSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_MASKED_TASKLOOP_SIMD_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPParallelMasterTaskLoopDirective(
     OMPParallelMasterTaskLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Record.writeBool(D->hasCancel());
   Code = serialization::STMT_OMP_PARALLEL_MASTER_TASKLOOP_DIRECTIVE;
@@ -2717,6 +2750,7 @@ void ASTStmtWriter::VisitOMPParallelMasterTaskLoopDirective(
 
 void ASTStmtWriter::VisitOMPParallelMaskedTaskLoopDirective(
     OMPParallelMaskedTaskLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Record.writeBool(D->hasCancel());
   Code = serialization::STMT_OMP_PARALLEL_MASKED_TASKLOOP_DIRECTIVE;
@@ -2724,17 +2758,20 @@ void ASTStmtWriter::VisitOMPParallelMaskedTaskLoopDirective(
 
 void ASTStmtWriter::VisitOMPParallelMasterTaskLoopSimdDirective(
     OMPParallelMasterTaskLoopSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_PARALLEL_MASTER_TASKLOOP_SIMD_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPParallelMaskedTaskLoopSimdDirective(
     OMPParallelMaskedTaskLoopSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_PARALLEL_MASKED_TASKLOOP_SIMD_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPDistributeDirective(OMPDistributeDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_DISTRIBUTE_DIRECTIVE;
 }
@@ -2747,6 +2784,7 @@ void ASTStmtWriter::VisitOMPTargetUpdateDirective(OMPTargetUpdateDirective *D) {
 
 void ASTStmtWriter::VisitOMPDistributeParallelForDirective(
     OMPDistributeParallelForDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Record.writeBool(D->hasCancel());
   Code = serialization::STMT_OMP_DISTRIBUTE_PARALLEL_FOR_DIRECTIVE;
@@ -2754,47 +2792,55 @@ void ASTStmtWriter::VisitOMPDistributeParallelForDirective(
 
 void ASTStmtWriter::VisitOMPDistributeParallelForSimdDirective(
     OMPDistributeParallelForSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_DISTRIBUTE_PARALLEL_FOR_SIMD_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPDistributeSimdDirective(
     OMPDistributeSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_DISTRIBUTE_SIMD_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPTargetParallelForSimdDirective(
     OMPTargetParallelForSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_TARGET_PARALLEL_FOR_SIMD_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPTargetSimdDirective(OMPTargetSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_TARGET_SIMD_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPTeamsDistributeDirective(
     OMPTeamsDistributeDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_TEAMS_DISTRIBUTE_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPTeamsDistributeSimdDirective(
     OMPTeamsDistributeSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_TEAMS_DISTRIBUTE_SIMD_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPTeamsDistributeParallelForSimdDirective(
     OMPTeamsDistributeParallelForSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_TEAMS_DISTRIBUTE_PARALLEL_FOR_SIMD_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPTeamsDistributeParallelForDirective(
     OMPTeamsDistributeParallelForDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Record.writeBool(D->hasCancel());
   Code = serialization::STMT_OMP_TEAMS_DISTRIBUTE_PARALLEL_FOR_DIRECTIVE;
@@ -2808,12 +2854,14 @@ void ASTStmtWriter::VisitOMPTargetTeamsDirective(OMPTargetTeamsDirective *D) {
 
 void ASTStmtWriter::VisitOMPTargetTeamsDistributeDirective(
     OMPTargetTeamsDistributeDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_TARGET_TEAMS_DISTRIBUTE_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPTargetTeamsDistributeParallelForDirective(
     OMPTargetTeamsDistributeParallelForDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Record.writeBool(D->hasCancel());
   Code = serialization::STMT_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_FOR_DIRECTIVE;
@@ -2821,6 +2869,7 @@ void ASTStmtWriter::VisitOMPTargetTeamsDistributeParallelForDirective(
 
 void ASTStmtWriter::VisitOMPTargetTeamsDistributeParallelForSimdDirective(
     OMPTargetTeamsDistributeParallelForSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::
       STMT_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_FOR_SIMD_DIRECTIVE;
@@ -2828,6 +2877,7 @@ void ASTStmtWriter::VisitOMPTargetTeamsDistributeParallelForSimdDirective(
 
 void ASTStmtWriter::VisitOMPTargetTeamsDistributeSimdDirective(
     OMPTargetTeamsDistributeSimdDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD_DIRECTIVE;
 }
@@ -2852,18 +2902,21 @@ void ASTStmtWriter::VisitOMPMaskedDirective(OMPMaskedDirective *D) {
 }
 
 void ASTStmtWriter::VisitOMPGenericLoopDirective(OMPGenericLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_GENERIC_LOOP_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPTeamsGenericLoopDirective(
     OMPTeamsGenericLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_TEAMS_GENERIC_LOOP_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPTargetTeamsGenericLoopDirective(
     OMPTargetTeamsGenericLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Record.writeBool(D->canBeParallelFor());
   Code = serialization::STMT_OMP_TARGET_TEAMS_GENERIC_LOOP_DIRECTIVE;
@@ -2871,12 +2924,14 @@ void ASTStmtWriter::VisitOMPTargetTeamsGenericLoopDirective(
 
 void ASTStmtWriter::VisitOMPParallelGenericLoopDirective(
     OMPParallelGenericLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_PARALLEL_GENERIC_LOOP_DIRECTIVE;
 }
 
 void ASTStmtWriter::VisitOMPTargetParallelGenericLoopDirective(
     OMPTargetParallelGenericLoopDirective *D) {
+  VisitStmt(D);
   VisitOMPLoopDirective(D);
   Code = serialization::STMT_OMP_TARGET_PARALLEL_GENERIC_LOOP_DIRECTIVE;
 }
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index fdabba46992b08..1981fa158d2dff 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1758,6 +1758,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::SEHLeaveStmtClass:
     case Stmt::SEHFinallyStmtClass:
     case Stmt::OMPCanonicalLoopClass:
+    case Stmt::OMPCompoundBlockDirectiveClass:
+    case Stmt::OMPCompoundLoopDirectiveClass:
     case Stmt::OMPParallelDirectiveClass:
     case Stmt::OMPSimdDirectiveClass:
     case Stmt::OMPForDirectiveClass:
diff --git a/clang/test/OpenMP/default_firstprivate_ast_print.cpp b/clang/test/OpenMP/default_firstprivate_ast_print.cpp
index 4bf9fc664c3047..c9c5e9e492bc39 100644
--- a/clang/test/OpenMP/default_firstprivate_ast_print.cpp
+++ b/clang/test/OpenMP/default_firstprivate_ast_print.cpp
@@ -48,7 +48,7 @@ struct SomeKernel {
     // PRINT-NEXT:     return this->targetDev++;
     // PRINT-NEXT:  }();
     // PRINT-NEXT: }
-    // DUMP: -OMPParallelDirective
+    // DUMP: -OMPCompoundBlockDirective{{.*}}'parallel'
     // DUMP-NEXT: -OMPDefaultClause
     // DUMP-NOT:   -OMPFirstprivateClause
   }
diff --git a/clang/test/OpenMP/default_private_ast_print.cpp b/clang/test/OpenMP/default_private_ast_print.cpp
index b15a76402cca60..62c3f5c57a3a93 100644
--- a/clang/test/OpenMP/default_private_ast_print.cpp
+++ b/clang/test/OpenMP/default_private_ast_print.cpp
@@ -44,7 +44,7 @@ struct SomeKernel {
     // PRINT-NEXT: {
     // PRINT-NEXT:  this->targetDev++;
     // CHECK-NEXT: }
-    // DUMP: -OMPParallelDirective
+    // DUMP: -OMPCompoundBlockDirective{{.*}}'parallel'
     // DUMP->NEXT: -OMPDefaultClause
   }
   // PRINT: template<> void apply<32U>()
diff --git a/clang/test/OpenMP/generic_loop_ast_print.cpp b/clang/test/OpenMP/generic_loop_ast_print.cpp
index b361724c12a0d1..0eed0b2a39ca6b 100644
--- a/clang/test/OpenMP/generic_loop_ast_print.cpp
+++ b/clang/test/OpenMP/generic_loop_ast_print.cpp
@@ -31,7 +31,7 @@
 //DUMP: FunctionTemplateDecl{{.*}}templ_foo
 //DUMP: TemplateTypeParmDecl{{.*}}T
 //DUMP: NonTypeTemplateParmDecl{{.*}}C
-//DUMP: OMPGenericLoopDirective
+//DUMP: OMPCompoundLoopDirective{{.*}}'loop'
 //DUMP: OMPCollapseClause
 //DUMP: DeclRefExpr{{.*}}'C' 'int'
 //DUMP: OMPReductionClause
diff --git a/clang/test/OpenMP/interop_ast_print.cpp b/clang/test/OpenMP/interop_ast_print.cpp
index fed6febc63085e..07f3b36f468c3e 100644
--- a/clang/test/OpenMP/interop_ast_print.cpp
+++ b/clang/test/OpenMP/interop_ast_print.cpp
@@ -259,7 +259,7 @@ void fooTemp() {
   omp_interop_t interop_var;
   //PRINT: #pragma omp interop init(prefer_type(I,4,"level_one"), target : interop_var)
   //DUMP: FunctionDecl{{.*}}fooTemp
-  //DUMP: OMPInteropDirective
+  //DUMP: OMPCompoundBlockDirective{{.*}}'interop'
   //DUMP: OMPInitClause
   //DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}'interop_var'
   //DUMP: DeclRefExpr{{.*}}NonTypeTemplateParm{{.*}}'I' 'int'
@@ -287,7 +287,7 @@ void barTemp(T t) {
   //PRINT: #pragma omp interop init(prefer_type(4,"level_one"), target : t)
   //DUMP: FunctionDecl{{.*}}barTemp 'void (T)'
   //DUMP: ParmVarDecl{{.*}}t 'T'
-  //DUMP: OMPInteropDirective
+  //DUMP: OMPCompoundBlockDirective{{.*}}'interop'
   //DUMP: OMPInitClause
   //DUMP: DeclRefExpr{{.*}}ParmVar{{.*}}'t' 'T'
   //DUMP: IntegerLiteral{{.*}}'int' 4
@@ -295,13 +295,13 @@ void barTemp(T t) {
   #pragma omp interop init(prefer_type(4,"level_one"), target: t)
 
   //PRINT: #pragma omp interop use(t)
-  //DUMP: OMPInteropDirective
+  //DUMP: OMPCompoundBlockDirective{{.*}}'interop'
   //DUMP: OMPUseClause
   //DUMP: DeclRefExpr{{.*}}ParmVar{{.*}}'t' 'T'
   #pragma omp interop use(t)
 
   //PRINT: #pragma omp interop destroy(t)
-  //DUMP: OMPInteropDirective
+  //DUMP: OMPCompoundBlockDirective{{.*}}'interop'
   //DUMP: OMPDestroyClause
   //DUMP: DeclRefExpr{{.*}}ParmVar{{.*}}'t' 'T'
   #pragma omp interop destroy(t)
diff --git a/clang/test/OpenMP/scope_ast_print.cpp b/clang/test/OpenMP/scope_ast_print.cpp
index c5c3a29738a72b..650603156d5b5e 100644
--- a/clang/test/OpenMP/scope_ast_print.cpp
+++ b/clang/test/OpenMP/scope_ast_print.cpp
@@ -71,7 +71,7 @@ int template_test() {
 //DUMP: FunctionTemplateDecl {{.*}}run
 //DUMP: TemplateTypeParmDecl {{.*}}referenced typename depth 0 index 0 T
 //DUMP: FunctionDecl {{.*}}run 'T ()'
-//DUMP: OMPScopeDirective
+//DUMP: OMPCompoundBlockDirective{{.*}}'scope'
 //DUMP: OMPPrivateClause
 //DUMP: DeclRefExpr {{.*}}'T' lvalue Var {{.*}} 'a' 'T'
 //DUMP: OMPReductionClause
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index e821c5e4c588b6..1fae7fa54914e7 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -2186,6 +2186,8 @@ class EnqueueVisitor : public ConstStmtVisitor<EnqueueVisitor, void>,
   void VisitOMPExecutableDirective(const OMPExecutableDirective *D);
   void VisitOMPLoopBasedDirective(const OMPLoopBasedDirective *D);
   void VisitOMPLoopDirective(const OMPLoopDirective *D);
+  void VisitOMPCompoundBlockDirective(const OMPCompoundBlockDirective *D);
+  void VisitOMPCompoundLoopDirective(const OMPCompoundLoopDirective *D);
   void VisitOMPParallelDirective(const OMPParallelDirective *D);
   void VisitOMPSimdDirective(const OMPSimdDirective *D);
   void
@@ -3234,6 +3236,14 @@ void EnqueueVisitor::VisitOMPLoopDirective(const OMPLoopDirective *D) {
   VisitOMPLoopBasedDirective(D);
 }
 
+void EnqueueVisitor::VisitOMPCompoundBlockDirective(const OMPCompoundBlockDirective *D) {
+  VisitOMPExecutableDirective(D);
+}
+
+void EnqueueVisitor::VisitOMPCompoundLoopDirective(const OMPCompoundLoopDirective *D) {
+  VisitOMPLoopDirective(D);
+}
+
 void EnqueueVisitor::VisitOMPParallelDirective(const OMPParallelDirective *D) {
   VisitOMPExecutableDirective(D);
 }
@@ -6278,6 +6288,10 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) {
     return cxstring::createRef("OMPParallelGenericLoopDirective");
   case CXCursor_OMPTargetParallelGenericLoopDirective:
     return cxstring::createRef("OMPTargetParallelGenericLoopDirective");
+  case CXCursor_OMPCompoundBlockDirective:
+    return cxstring::createRef("OMPCompoundBlockDirective");
+  case CXCursor_OMPCompoundLoopDirective:
+    return cxstring::createRef("OMPCompoundLoopDirective");
   case CXCursor_OverloadCandidate:
     return cxstring::createRef("OverloadCandidate");
   case CXCursor_TypeAliasTemplateDecl:
diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
index 4e068f272a153f..30ea48dc584fc2 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -887,6 +887,12 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
   case Stmt::OMPTargetParallelGenericLoopDirectiveClass:
     K = CXCursor_OMPTargetParallelGenericLoopDirective;
     break;
+  case Stmt::OMPCompoundBlockDirectiveClass:
+    K = CXCursor_OMPCompoundBlockDirective;
+    break;
+  case Stmt::OMPCompoundLoopDirectiveClass:
+    K = CXCursor_OMPCompoundLoopDirective;
+    break;
   case Stmt::BuiltinBitCastExprClass:
     K = CXCursor_BuiltinBitCastExpr;
     break;
diff --git a/llvm/include/llvm/Frontend/OpenMP/ConstructDecompositionT.h b/llvm/include/llvm/Frontend/OpenMP/ConstructDecompositionT.h
index b93bc594a82bf3..e15f78909d6b0a 100644
--- a/llvm/include/llvm/Frontend/OpenMP/ConstructDecompositionT.h
+++ b/llvm/include/llvm/Frontend/OpenMP/ConstructDecompositionT.h
@@ -75,10 +75,11 @@ namespace tomp {
 // HelperType - A class that implements two member functions:
 //
 //   // Return the base object of the given object, if any.
-//   std::optional<Object> getBaseObject(const Object &object) const
+// - std::optional<Object> getBaseObject(const Object &object) const
 //   // Return the iteration variable of the outermost loop associated
 //   // with the construct being worked on, if any.
-//   std::optional<Object> getLoopIterVar() const
+// - std::optional<Object> getLoopIterVar() const
+//
 template <typename ClauseType, typename HelperType>
 struct ConstructDecompositionT {
   using ClauseTy = ClauseType;
@@ -93,8 +94,9 @@ struct ConstructDecompositionT {
 
   ConstructDecompositionT(uint32_t ver, HelperType &helper,
                           llvm::omp::Directive dir,
-                          llvm::ArrayRef<ClauseTy> clauses)
-      : version(ver), construct(dir), helper(helper) {
+                          llvm::ArrayRef<ClauseTy> clauses,
+                          bool makeImplicit = true)
+      : version(ver), construct(dir), makeImplicit(makeImplicit), helper(helper) {
     for (const ClauseTy &clause : clauses)
       nodes.push_back(&clause);
 
@@ -114,6 +116,15 @@ struct ConstructDecompositionT {
     }
   }
 
+  // Apply a clause to the prior split.
+  // Note that in some cases the order in which clauses are processed
+  // is important (e.g. linear, allocate). This function will simply process
+  // the clause as per OpenMP rules for clauses on compound constructs.
+  // NOTE: In order for this function to work, the caller must keep alive
+  // the original clauses (passed to the constructor as `clauses`), and any
+  // clauses passed to prior calls to postApply.
+  bool postApply(const ClauseTy &clause);
+
   tomp::ListT<DirectiveWithClauses<ClauseType>> output;
 
 private:
@@ -242,6 +253,7 @@ struct ConstructDecompositionT {
 
   uint32_t version;
   llvm::omp::Directive construct;
+  bool makeImplicit;   // Whether to create implicit clauses.
   HelperType &helper;
   ListT<LeafReprInternal> leafs;
   tomp::ListT<const ClauseTy *> nodes;
@@ -539,7 +551,7 @@ bool ConstructDecompositionT<C, H>::applyClause(
     dirDistribute->clauses.push_back(node);
     applied = true;
     // [5.2:340:17]
-    if (dirTeams != nullptr) {
+    if (makeImplicit && dirTeams != nullptr) {
       auto *shared = makeClause(
           llvm::omp::Clause::OMPC_shared,
           tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/clause.v});
@@ -580,7 +592,7 @@ bool ConstructDecompositionT<C, H>::applyClause(
     if (dirTaskloop == nullptr && dirWorksharing == nullptr) {
       dirParallel->clauses.push_back(node);
       applied = true;
-    } else {
+    } else if (makeImplicit) {
       // [5.2:340:15]
       auto *shared = makeClause(
           llvm::omp::Clause::OMPC_shared,
@@ -607,10 +619,17 @@ bool ConstructDecompositionT<C, H>::applyClause(
           return !inLastprivate(object) && !mapBases.count(object.id());
         });
     if (!objects.empty()) {
-      auto *firstp = makeClause(
-          llvm::omp::Clause::OMPC_firstprivate,
-          tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>{/*List=*/objects});
-      dirTarget->clauses.push_back(firstp);
+      if (objects.size() == clause.v.size()) {
+        // If copied everything, then just add the original clause.
+        dirTarget->clauses.push_back(node);
+      } else {
+        auto *firstp =
+            makeClause(llvm::omp::Clause::OMPC_firstprivate,
+                       tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>{
+                           /*List=*/objects});
+        addClauseSymsToMap(objects, firstp);
+        dirTarget->clauses.push_back(firstp);
+      }
       applied = true;
     }
   }
@@ -654,6 +673,9 @@ bool ConstructDecompositionT<C, H>::applyClause(
   if (!applied)
     return false;
 
+  if (!makeImplicit)
+    return applied;
+
   auto inFirstprivate = [&](const ObjectTy &object) {
     if (ClauseSet *set = findClausesWith(object)) {
       return llvm::find_if(*set, [](const ClauseTy *c) {
@@ -678,7 +700,6 @@ bool ConstructDecompositionT<C, H>::applyClause(
           llvm::omp::Clause::OMPC_shared,
           tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
       dirParallel->clauses.push_back(shared);
-      applied = true;
     }
 
     // [5.2:340:24]
@@ -687,7 +708,6 @@ bool ConstructDecompositionT<C, H>::applyClause(
           llvm::omp::Clause::OMPC_shared,
           tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
       dirTeams->clauses.push_back(shared);
-      applied = true;
     }
   }
 
@@ -707,9 +727,9 @@ bool ConstructDecompositionT<C, H>::applyClause(
                          {/*MapType=*/MapType::Tofrom,
                           /*MapTypeModifier=*/std::nullopt,
                           /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
-                          /*LocatorList=*/std::move(tofrom)}});
+                          /*LocatorList=*/tofrom}});
+      addClauseSymsToMap(tofrom, map);
       dirTarget->clauses.push_back(map);
-      applied = true;
     }
   }
 
@@ -793,7 +813,7 @@ bool ConstructDecompositionT<C, H>::applyClause(
   // assigned to which leaf constructs.
 
   // [5.2:340:33]
-  auto canMakePrivateCopy = [](llvm::omp::Clause id) {
+  auto hasPrivatizationProperty = [](llvm::omp::Clause id) {
     switch (id) {
     // Clauses with "privatization" property:
     case llvm::omp::Clause::OMPC_firstprivate:
@@ -809,9 +829,20 @@ bool ConstructDecompositionT<C, H>::applyClause(
     }
   };
 
+  using Allocate = tomp::clause::AllocateT<TypeTy, IdTy, ExprTy>;
+  auto &objects = std::get<typename Allocate::List>(clause.t);
+
   bool applied = applyIf(node, [&](const auto &leaf) {
     return llvm::any_of(leaf.clauses, [&](const ClauseTy *n) {
-      return canMakePrivateCopy(n->id);
+      if (!hasPrivatizationProperty(n->id))
+        return false;
+      for (auto &object : objects) {
+        if (auto *clauses = findClausesWith(object)) {
+          if (clauses->count(n))
+            return true;
+        }
+      }
+      return false;
     });
   });
 
@@ -909,12 +940,13 @@ bool ConstructDecompositionT<C, H>::applyClause(
     llvm_unreachable("Unexpected modifier");
   };
 
-  auto *unmodified = makeClause(
+  auto *demodified = makeClause(
       llvm::omp::Clause::OMPC_reduction,
       ReductionTy{
           {/*ReductionModifier=*/std::nullopt,
            /*ReductionIdentifiers=*/std::get<ReductionIdentifiers>(clause.t),
            /*List=*/objects}});
+  addClauseSymsToMap(objects, demodified);
 
   ReductionModifier effective =
       modifier.has_value() ? *modifier : ReductionModifier::Default;
@@ -935,14 +967,14 @@ bool ConstructDecompositionT<C, H>::applyClause(
       effectiveApplied = true;
     } else {
       // Apply clause without modifier.
-      leaf.clauses.push_back(unmodified);
+      leaf.clauses.push_back(demodified);
     }
     // The modifier must be applied to some construct.
     applied = effectiveApplied;
   }
 
-  if (!applied)
-    return false;
+  if (!applied || !makeImplicit)
+    return applied;
 
   tomp::ObjectListT<IdTy, ExprTy> sharedObjects;
   llvm::transform(objects, std::back_inserter(sharedObjects),
@@ -985,8 +1017,8 @@ bool ConstructDecompositionT<C, H>::applyClause(
           tomp::clause::MapT<TypeTy, IdTy, ExprTy>{
               {/*MapType=*/MapType::Tofrom, /*MapTypeModifier=*/std::nullopt,
                /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
-               /*LocatorList=*/std::move(tofrom)}});
-
+               /*LocatorList=*/tofrom}});
+      addClauseSymsToMap(tofrom, map);
       dirTarget->clauses.push_back(map);
       applied = true;
     }
@@ -1017,14 +1049,16 @@ bool ConstructDecompositionT<C, H>::applyClause(
 
   if (modifier) {
     llvm::omp::Directive dirId = *modifier;
-    auto *unmodified =
+    if (!isAllowedClauseForDirective(dirId, node->id, version))
+      return false;
+    auto *demodified =
         makeClause(llvm::omp::Clause::OMPC_if,
                    tomp::clause::IfT<TypeTy, IdTy, ExprTy>{
                        {/*DirectiveNameModifier=*/std::nullopt,
                         /*IfExpression=*/std::get<IfExpression>(clause.t)}});
 
     if (auto *hasDir = findDirective(dirId)) {
-      hasDir->clauses.push_back(unmodified);
+      hasDir->clauses.push_back(demodified);
       return true;
     }
     return false;
@@ -1057,6 +1091,8 @@ bool ConstructDecompositionT<C, H>::applyClause(
   // [5.2:341:15.1]
   if (!applyToInnermost(node))
     return false;
+  if (!makeImplicit)
+    return true;
 
   // [5.2:341:15.2], [5.2:341:19]
   auto dirSimd = findDirective(llvm::omp::Directive::OMPD_simd);
@@ -1176,6 +1212,28 @@ template <typename C, typename H> bool ConstructDecompositionT<C, H>::split() {
   return success;
 }
 
+template <typename C, typename H>
+bool ConstructDecompositionT<C, H>::postApply(const ClauseTy &clause) {
+  nodes.push_back(&clause);
+  addClauseSymsToMap(clause, &clause);
+  if (output.size() == 0)
+    return false;
+
+  bool success =
+      std::visit([&](auto &&s) { return applyClause(s, &clause); }, clause.u);
+
+  // Transfer the updates to the clause lists to the output.
+  assert(output.size() == leafs.size() && "Internal lists differ in lengths");
+  for (size_t i = 0, e = output.size(); i != e; ++i) {
+    auto &leaf = leafs[i];
+    auto &out = output[i];
+    assert(leaf.clauses.size() >= out.clauses.size() &&
+           "Output longer than internal worklist");
+    for (size_t j = out.clauses.size(), f = leaf.clauses.size(); j != f; ++j)
+      out.clauses.push_back(*leaf.clauses[j]);
+  }
+  return success;
+}
 } // namespace tomp
 
 #endif // LLVM_FRONTEND_OPENMP_CONSTRUCTDECOMPOSITIONT_H
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.h b/llvm/include/llvm/Frontend/OpenMP/OMP.h
index 54ae672755ba89..985b98f6c13ead 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.h
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.h
@@ -32,6 +32,8 @@ bool isLeafConstruct(Directive D);
 bool isCompositeConstruct(Directive D);
 bool isCombinedConstruct(Directive D);
 
+bool isUniqueClause(Clause C);
+
 /// Create a nicer version of a function name for humans to look at.
 std::string prettifyFunctionName(StringRef FunctionName);
 
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 1f747df43eeb80..b7097431025080 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -970,6 +970,7 @@ def OMP_Target : Directive<"target"> {
     VersionedClause<OMPC_IsDevicePtr>,
     VersionedClause<OMPC_Map>,
     VersionedClause<OMPC_OMPX_Attribute>,
+    VersionedClause<OMPC_OMPX_Bare>,
     VersionedClause<OMPC_Private>,
     VersionedClause<OMPC_UsesAllocators, 50>,
   ];
diff --git a/llvm/lib/Frontend/OpenMP/OMP.cpp b/llvm/lib/Frontend/OpenMP/OMP.cpp
index 5720655442be3d..8e804abd1c1812 100644
--- a/llvm/lib/Frontend/OpenMP/OMP.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMP.cpp
@@ -193,6 +193,60 @@ bool isCombinedConstruct(Directive D) {
   return !getLeafConstructs(D).empty() && !isCompositeConstruct(D);
 }
 
+bool isUniqueClause(Clause C) {
+  switch (C) {
+  case OMPC_affinity:
+  case OMPC_align:
+  case OMPC_allocator:
+  case OMPC_append_args:
+  case OMPC_at:
+  case OMPC_bind:
+  case OMPC_collapse:
+  case OMPC_default:
+  case OMPC_defaultmap:
+  case OMPC_detach:
+  case OMPC_device:
+  case OMPC_device_type:
+  case OMPC_dist_schedule:
+  case OMPC_exclusive:
+  case OMPC_filter:
+  case OMPC_final:
+  case OMPC_full:
+  case OMPC_grainsize:
+  case OMPC_hint:
+  case OMPC_inclusive:
+  case OMPC_indirect:
+  // case OMPC_initializer: present in spec, but not defined in header
+  case OMPC_match:
+  case OMPC_mergeable:
+  case OMPC_message:
+  // case OMPC_nocontext: present in spec, but not defined in header
+  case OMPC_nogroup:
+  case OMPC_novariants:
+  case OMPC_nowait:
+  case OMPC_num_tasks:
+  case OMPC_num_teams:
+  case OMPC_num_threads:
+  case OMPC_order:
+  case OMPC_ordered:
+  // case OMPC_otherwise: present in spec, but not defined in header
+  case OMPC_partial:
+  case OMPC_priority:
+  case OMPC_proc_bind:
+  case OMPC_safelen:
+  case OMPC_schedule:
+  case OMPC_severity:
+  case OMPC_simdlen:
+  case OMPC_sizes:
+  case OMPC_thread_limit:
+  case OMPC_untied:
+  case OMPC_update:
+    return true;
+  default:
+    return false;
+  }
+}
+
 std::string prettifyFunctionName(StringRef FunctionName) {
   // Internalized functions have the right name, but simply a suffix.
   if (FunctionName.ends_with(".internalized"))



More information about the cfe-commits mailing list