[llvm-branch-commits] [clang] [llvm] [clang][OpenMP] Prototype #2 of directive splitting (PR #109288)

Krzysztof Parzyszek via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Sep 19 07:08:57 PDT 2024


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

This is proto #1 minus the introduction of opaque AST nodes.

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

Proto #1: https://github.com/llvm/llvm-project/pull/108855

>From 915fe94064e727d3e75127ab4cb1786226297250 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 #2 of directive splitting

This is proto #1 minus the introduction of opaque AST nodes.

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

Proto #1: https://github.com/llvm/llvm-project/pull/108855
---
 clang/include/clang/AST/OpenMPClause.h        |    9 +-
 clang/include/clang/AST/Stmt.h                |   10 +-
 clang/include/clang/Basic/OpenMPKinds.h       |   18 +-
 clang/include/clang/Sema/SemaOpenMP.h         |   49 +-
 clang/lib/AST/Stmt.cpp                        |   15 +-
 clang/lib/Basic/OpenMPKinds.cpp               |   70 +-
 clang/lib/CodeGen/CGStmtOpenMP.cpp            |    6 +-
 clang/lib/Parse/ParseOpenMP.cpp               |   45 +-
 clang/lib/Sema/CMakeLists.txt                 |    1 +
 clang/lib/Sema/SemaExpr.cpp                   |   19 +-
 clang/lib/Sema/SemaOpenMP.cpp                 | 2336 +++++++++++------
 clang/lib/Sema/SemaOpenMPExt.cpp              | 1547 +++++++++++
 clang/lib/Sema/SemaOpenMPExt.h                |  428 +++
 clang/lib/Sema/TreeTransform.h                |   83 +-
 .../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 +
 18 files changed, 3853 insertions(+), 946 deletions(-)
 create mode 100644 clang/lib/Sema/SemaOpenMPExt.cpp
 create mode 100644 clang/lib/Sema/SemaOpenMPExt.h

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/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/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/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h
index 819b9fe347568d..ced79e3df92b8b 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.
@@ -1404,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;
 
@@ -1419,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/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/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/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/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 2f7e9c754ce095..e364d8b2b62f80 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18553,6 +18553,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();
@@ -18560,10 +18563,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);
   }
@@ -18967,9 +18966,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 &&
@@ -18991,8 +18988,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)) {
@@ -19003,6 +19000,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 c8109d41704f7b..1a238c3f781bbf 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,
@@ -2967,20 +3020,61 @@ void SemaOpenMP::EndOpenMPDSABlock(Stmt *CurDirective) {
     }
   };
 
-  if (const auto *D = dyn_cast_or_null<OMPExecutableDirective>(CurDirective)) {
-    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);
+  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);
+        }
       }
+      // 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;
+          for (int I = 0, E = getOpenMPCaptureLevels(D); I != E; ++I)
+            S = cast<CapturedStmt>(S)->getCapturedStmt();
+        }
+      }
+    } 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();
@@ -3714,6 +3808,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;
@@ -3732,12 +3907,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())
@@ -3757,15 +3942,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.
@@ -3779,6 +3957,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() ||
@@ -3788,22 +3975,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;
@@ -3819,7 +4008,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) &&
@@ -3885,13 +4074,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) &&
@@ -3928,13 +4112,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;
         }
@@ -3971,9 +4155,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;
       }
 
@@ -4030,7 +4215,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;
       }
 
@@ -4065,7 +4250,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;
     }
@@ -4167,41 +4352,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,
@@ -4331,7 +4574,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) {
@@ -4372,7 +4614,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:
@@ -4383,7 +4625,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:
@@ -4416,10 +4662,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();
@@ -4437,38 +4683,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 &&
@@ -4580,62 +4837,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)
@@ -4643,9 +4868,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.
@@ -4654,10 +4876,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)
@@ -4665,44 +4888,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));
@@ -4710,42 +5111,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;
 }
 
@@ -5764,136 +6346,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 {
@@ -5954,30 +6535,129 @@ class TeamsLoopChecker final : public ConstStmtVisitor<TeamsLoopChecker> {
       if (Child)
         Visit(Child);
   }
-  explicit TeamsLoopChecker(Sema &SemaRef)
-      : SemaRef(SemaRef), TeamsLoopCanBeParallelFor(true) {}
+  explicit TeamsLoopChecker(Sema &SemaRef)
+      : SemaRef(SemaRef), TeamsLoopCanBeParallelFor(true) {}
+
+private:
+  bool TeamsLoopCanBeParallelFor;
+};
+} // namespace
+
+static bool teamsLoopCanBeParallelFor(Stmt *AStmt, Sema &SemaRef) {
+  TeamsLoopChecker Checker(SemaRef);
+  Checker.Visit(AStmt);
+  return Checker.teamsLoopCanBeParallelFor();
+}
+
+static Stmt *getRawStmt(Stmt *S) {
+  while (isa<CapturedStmt>(S))
+    S = cast<CapturedStmt>(S)->getCapturedStmt();
+  return S;
+}
+
+static void
+getImplicitInfoFromClauses(Sema &SemaRef, OpenMPDirectiveKind DKind,
+                           ArrayRef<OMPClause *> Clauses,
+                           SemaOpenMP::VariableImplicitInfo &ImpInfo) {
+  constexpr unsigned DefaultmapKindNum =
+      SemaOpenMP::VariableImplicitInfo::DefaultmapKindNum;
+
+  // 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();
+  }
+
+  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]);
+  }
+
+  // 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());
+  }
+
+  // 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 (auto *RC = dyn_cast<OMPReductionClause>(C)) {
+        for (Expr *E : RC->varlist()) {
+          if (!isa<DeclRefExpr>(E->IgnoreParenImpCasts()))
+            ImpInfo.addReductionMapping(E);
+        }
+      }
+    }
+  }
+}
 
-private:
-  bool TeamsLoopCanBeParallelFor;
-};
-} // namespace
+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;
 
-static bool teamsLoopCanBeParallelFor(Stmt *AStmt, Sema &SemaRef) {
-  TeamsLoopChecker Checker(SemaRef);
-  Checker.Visit(AStmt);
-  return Checker.teamsLoopCanBeParallelFor();
-}
+    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;
+    };
 
-static StmtResult createASTForDirective(
-    Sema &SemaRef, OpenMPDirectiveKind Kind, ArrayRef<OMPClause *> Clauses,
-    Stmt *AStmt, SourceLocation StartLoc, SourceLocation EndLoc,
-    const DeclarationNameInfo &DirName, OpenMPDirectiveKind CancelRegion,
-    SemaOpenMP::VarsWithInheritedDSAType &VarsWithInheritedDSA) {
+    if (auto *FC = dyn_cast<OMPFirstprivateClause>(C))
+      return llvm::any_of(FC->varlist(), IsVarCaptured);
 
-  SemaOpenMP &S = SemaRef.OpenMP();
-  StmtResult Res = StmtError();
+    return true;
+  };
 
-  switch (Kind) {
+  SmallVector<OMPClause *> Clauses;
+  llvm::copy_if(Unit.Clauses, std::back_inserter(Clauses),
+                isClauseForUsedVariable);
+
+  OpenMPDirectiveKind Saved = Stack->getTopOfStack().Directive;
+  Stack->getTopOfStack().Directive = Unit.DKind;
+
+  switch (Unit.DKind) {
   case OMPD_parallel:
     Res = S.ActOnOpenMPParallelDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
@@ -6058,43 +6738,29 @@ static StmtResult createASTForDirective(
   case OMPD_taskyield:
     assert(Clauses.empty() &&
            "No clauses are allowed for 'omp taskyield' directive");
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp taskyield' directive");
     Res = S.ActOnOpenMPTaskyieldDirective(StartLoc, EndLoc);
     break;
   case OMPD_error:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp error' directive");
     Res = S.ActOnOpenMPErrorDirective(Clauses, StartLoc, EndLoc);
     break;
   case OMPD_barrier:
     assert(Clauses.empty() &&
            "No clauses are allowed for 'omp barrier' directive");
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp barrier' directive");
     Res = S.ActOnOpenMPBarrierDirective(StartLoc, EndLoc);
     break;
   case OMPD_taskwait:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp taskwait' directive");
     Res = S.ActOnOpenMPTaskwaitDirective(Clauses, StartLoc, EndLoc);
     break;
   case OMPD_taskgroup:
     Res = S.ActOnOpenMPTaskgroupDirective(Clauses, AStmt, StartLoc, EndLoc);
     break;
   case OMPD_flush:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp flush' directive");
     Res = S.ActOnOpenMPFlushDirective(Clauses, StartLoc, EndLoc);
     break;
   case OMPD_depobj:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp depobj' directive");
     Res = S.ActOnOpenMPDepobjDirective(Clauses, StartLoc, EndLoc);
     break;
   case OMPD_scan:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp scan' directive");
     Res = S.ActOnOpenMPScanDirective(Clauses, StartLoc, EndLoc);
     break;
   case OMPD_ordered:
@@ -6120,14 +6786,10 @@ static StmtResult createASTForDirective(
   case OMPD_cancellation_point:
     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 =
         S.ActOnOpenMPCancellationPointDirective(StartLoc, EndLoc, CancelRegion);
     break;
   case OMPD_cancel:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp cancel' directive");
     Res = S.ActOnOpenMPCancelDirective(Clauses, StartLoc, EndLoc, CancelRegion);
     break;
   case OMPD_target_data:
@@ -6244,8 +6906,6 @@ static StmtResult createASTForDirective(
         Clauses, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
     break;
   case OMPD_interop:
-    assert(AStmt == nullptr &&
-           "No associated statement allowed for 'omp interop' directive");
     Res = S.ActOnOpenMPInteropDirective(Clauses, StartLoc, EndLoc);
     break;
   case OMPD_dispatch:
@@ -6287,6 +6947,125 @@ static StmtResult createASTForDirective(
   default:
     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;
 }
 
@@ -6295,47 +7074,13 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
     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;
 
+  // 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_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;
-    }
-
-    assert(BindKind != OMPC_BIND_unknown && "Expecting BindKind");
-
-    OMPClause *C =
-        ActOnOpenMPBindClause(BindKind, SourceLocation(), SourceLocation(),
-                              SourceLocation(), SourceLocation());
-    ClausesWithImplicit.push_back(C);
-  }
-
-  // Diagnose "loop bind(teams)" with "reduction".
   if (Kind == OMPD_loop && BindKind == OMPC_BIND_teams) {
     for (OMPClause *C : Clauses) {
       if (C->getClauseKind() == OMPC_reduction)
@@ -6358,156 +7103,48 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
 
   VarsWithInheritedDSAType VarsWithInheritedDSA;
   bool ErrorFound = false;
-  ClausesWithImplicit.append(Clauses.begin(), Clauses.end());
 
   if (AStmt && !SemaRef.CurContext->isDependentContext() &&
-      isOpenMPCapturingDirective(Kind)) {
+      isOpenMPCapturingDirective(Kind, /*OrderedIsStandalone=*/false)) {
     assert(isa<CapturedStmt>(AStmt) && "Captured statement expected");
 
     // 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())
+    VarsWithInheritedDSA = DSAStack->getTopOfStack().VarsWithInheritedDSA;
+
+    if (DSAStack->getTopOfStack().HasDSAError)
       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();
-    }
+  }
 
-    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;
-        }
-      }
+  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);
     }
-    // Build expressions for implicit maps of data members with 'default'
-    // mappers.
-    if (getLangOpts().OpenMP >= 50)
-      processImplicitMapsWithDefaultMappers(SemaRef, DSAStack,
-                                            ClausesWithImplicit);
   }
 
   if (!SemaRef.CurContext->isDependentContext()) {
-    Res = createASTForDirective(SemaRef, Kind, ClausesWithImplicit, AStmt,
-                                StartLoc, EndLoc, DirName, CancelRegion,
-                                VarsWithInheritedDSA);
+    Res = createASTForDirective(SemaRef, Kind, Clauses, AStmt, StartLoc, EndLoc,
+                                DirName, CancelRegion, VarsWithInheritedDSA,
+                                DSAStack);
   } else {
-    if (getDirectiveAssociation(Kind) == Association::Loop)
-      Res = ActOnOpenMPOpaqueLoopDirective(Kind, ClausesWithImplicit, AStmt,
-                                           StartLoc, EndLoc,
-                                           VarsWithInheritedDSA);
-    else
-      Res = ActOnOpenMPOpaqueBlockDirective(Kind, ClausesWithImplicit, AStmt,
-                                            CancelRegion, DirName, StartLoc,
-                                            EndLoc);
+    if (getDirectiveAssociation(Kind) == Association::Loop) {
+      Res = ActOnOpenMPOpaqueLoopDirective(Kind, Clauses, AStmt, StartLoc,
+                                           EndLoc, VarsWithInheritedDSA);
+    } else {
+      Res = ActOnOpenMPOpaqueBlockDirective(Kind, Clauses, AStmt, CancelRegion,
+                                            DirName, StartLoc, EndLoc);
+    }
   }
 
   ErrorFound = Res.isInvalid() || ErrorFound;
@@ -8411,7 +9048,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))
@@ -8421,9 +9060,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;
 }
@@ -8435,7 +9076,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;
@@ -8657,10 +9299,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)
@@ -8668,9 +9312,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) {
@@ -8743,10 +9387,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)
@@ -8755,7 +9401,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;
@@ -8779,8 +9425,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;
 
@@ -8849,10 +9499,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);
 
@@ -8876,7 +9530,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());
@@ -8965,8 +9620,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;
 
@@ -9050,10 +9707,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;
 
@@ -9391,7 +10052,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(),
@@ -9422,7 +10084,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 =
@@ -9438,7 +10101,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();
 
@@ -9627,6 +10291,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);
@@ -9673,9 +10350,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,
@@ -9848,8 +10526,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.
@@ -10392,7 +11070,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;
@@ -10400,7 +11078,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();
 
@@ -10446,7 +11124,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;
@@ -10454,7 +11132,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();
@@ -10654,14 +11332,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();
 
@@ -10687,15 +11365,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();
 
@@ -10720,15 +11397,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();
 
@@ -10752,15 +11428,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();
 
@@ -10916,15 +11591,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();
@@ -13017,15 +13691,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();
@@ -13310,8 +13983,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;
@@ -13319,8 +13991,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();
 
@@ -13427,8 +14099,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;
@@ -13436,8 +14107,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();
 
@@ -13468,8 +14139,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;
@@ -13477,8 +14147,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();
 
@@ -13509,15 +14179,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();
@@ -13548,15 +14217,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();
@@ -13587,15 +14255,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();
@@ -13627,15 +14294,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();
@@ -13693,15 +14359,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();
@@ -13720,15 +14385,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();
@@ -13749,15 +14413,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();
@@ -13778,15 +14441,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();
@@ -13807,14 +14469,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();
@@ -13835,15 +14497,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();
@@ -13863,15 +14524,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();
@@ -13894,15 +14554,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();
@@ -13925,15 +14585,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)
@@ -13996,15 +14655,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();
@@ -14028,15 +14686,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();
@@ -14061,7 +14719,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;
@@ -14070,7 +14728,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();
@@ -14097,15 +14755,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();
@@ -15263,16 +15920,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
@@ -15329,47 +15992,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;
@@ -15394,13 +16052,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())
@@ -15427,13 +16078,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(
@@ -15516,12 +16160,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;
@@ -15543,13 +16191,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);
@@ -16409,10 +17050,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);
       }
     }
   }
@@ -16915,13 +17552,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(
@@ -16947,13 +17577,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(
@@ -16970,13 +17593,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);
@@ -19063,14 +19679,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);
@@ -20138,13 +20762,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,
@@ -21962,9 +22579,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);
@@ -22052,13 +22671,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,
@@ -22088,13 +22713,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,
@@ -22301,14 +22932,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);
       }
     }
   }
@@ -23219,22 +23842,10 @@ StmtResult SemaOpenMP::ActOnOpenMPOpaqueBlockDirective(
     OpenMPDirectiveKind DKind, ArrayRef<OMPClause *> Clauses, Stmt *AStmt,
     OpenMPDirectiveKind CancelRegion, const DeclarationNameInfo &DirName,
     SourceLocation StartLoc, SourceLocation EndLoc) {
-  bool NeedsStmt = false;
-  if (DKind == OMPD_section || DKind == OMPD_target_enter_data ||
-      DKind == OMPD_target_exit_data || DKind == OMPD_target_update) {
-    // The association of these in the spec is either "none" or "separating",
-    // but they do have an associated statement in clang.
-    NeedsStmt = true;
-  } else if (DKind != OMPD_ordered) {
-    // "ordered" has two versions, one with and one without a statement.
-    Association Assoc = getDirectiveAssociation(DKind);
-    NeedsStmt = Assoc != Association::None && Assoc != Association::Separating;
-  }
-
-  if (!AStmt && NeedsStmt)
+  if (!AStmt && isOpenMPDirectiveWithStatement(DKind))
     return StmtError();
 
-  if (AStmt && isOpenMPCapturingDirective(DKind))
+  if (AStmt && isOpenMPCapturingDirective(DKind, /*OrderedIsStandalone=*/false))
     setBranchProtectedScope(SemaRef, DKind, AStmt);
 
   // "scan" is the only executable, non-loop-associated directive so far
@@ -23247,7 +23858,7 @@ StmtResult SemaOpenMP::ActOnOpenMPOpaqueBlockDirective(
   }
 
   Expr *ReductionRef = nullptr;
-  assert(!isOpenMPSimdDirective(DKind) && "Unexpected loop directive");
+  assert(!isOpenMPLoopDirective(DKind) && "Unexpected loop directive");
   if (DKind == OMPD_taskgroup || isOpenMPParallelDirective(DKind) ||
       isOpenMPWorksharingDirective(DKind))
     ReductionRef = DSAStack->getTaskgroupReductionRef();
@@ -23291,12 +23902,13 @@ StmtResult SemaOpenMP::ActOnOpenMPOpaqueLoopDirective(
 
   CapturedStmt *CS = [&]() {
     switch (DKind) {
+    default:
+      setBranchProtectedScope(SemaRef, DKind, AStmt);
+      LLVM_FALLTHROUGH;
     case OMPD_distribute:
     case OMPD_for:
     case OMPD_taskloop:
       return cast<CapturedStmt>(AStmt);
-    default:
-      return setBranchProtectedScope(SemaRef, DKind, AStmt);
     }
   }();
 
@@ -23658,13 +24270,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);
@@ -24349,5 +24954,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 183c29d607f8c0..db644635c1e43b 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9233,32 +9233,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();
   }
@@ -9321,23 +9320,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();
 
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 llvm-branch-commits mailing list