[clang] 93e1bed - [OpenMP] Support capturing structured bindings in OpenMP regions. (#190832)

via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 22 06:22:10 PDT 2026


Author: Zahira Ammarguellat
Date: 2026-09-22T06:21:59-07:00
New Revision: 93e1bed81a47c0b3c3df6de2d5f7d7e4f450ae87

URL: https://github.com/llvm/llvm-project/commit/93e1bed81a47c0b3c3df6de2d5f7d7e4f450ae87
DIFF: https://github.com/llvm/llvm-project/commit/93e1bed81a47c0b3c3df6de2d5f7d7e4f450ae87.diff

LOG: [OpenMP] Support capturing structured bindings in OpenMP regions. (#190832)

This patch adds support for capturing structured bindings (C++20) in
`OpenMP` parallel regions, which previously resulted in an error:

  `error: capturing a structured binding is not yet supported in OpenMP`

Added: 
    clang/test/OpenMP/structured-bindings-ast.cpp
    clang/test/OpenMP/structured-bindings-codegen.cpp
    clang/test/OpenMP/structured-bindings-messages.cpp
    clang/test/OpenMP/structured-bindings-nontemporal.cpp
    clang/test/OpenMP/structured-bindings-target-map-different-types.cpp
    clang/test/OpenMP/structured-bindings-template-instantiation.cpp

Modified: 
    clang/docs/OpenMPSupport.md
    clang/docs/ReleaseNotes.md
    clang/include/clang/AST/DeclCXX.h
    clang/include/clang/AST/TextNodeDumper.h
    clang/include/clang/Basic/Attr.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Sema/Sema.h
    clang/lib/AST/DeclCXX.cpp
    clang/lib/AST/TextNodeDumper.cpp
    clang/lib/CodeGen/CGExpr.cpp
    clang/lib/CodeGen/CGOpenMPRuntime.cpp
    clang/lib/CodeGen/CGStmtOpenMP.cpp
    clang/lib/CodeGen/CodeGenFunction.h
    clang/lib/Sema/SemaExpr.cpp
    clang/lib/Sema/SemaLambda.cpp
    clang/lib/Sema/SemaOpenMP.cpp
    clang/lib/Sema/SemaStmt.cpp
    clang/test/SemaCXX/decomposition-openmp.cpp
    clang/utils/TableGen/ClangAttrEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/OpenMPSupport.md b/clang/docs/OpenMPSupport.md
index a04c1085a87ca..1cc39fabe948a 100644
--- a/clang/docs/OpenMPSupport.md
+++ b/clang/docs/OpenMPSupport.md
@@ -41,6 +41,19 @@ see {ref}`OpenMP implementation details <openmp-implementation-details>` and
   known at compile time. To prevent this conservative choice and use
   at most 32 bits, compile your program with the
   `-fopenmp-optimistic-collapse`.
+- C++20 structured bindings are now supported in OpenMP constructs.
+  Bindings from structured binding declarations can be used in data-sharing
+  clauses (``private``, ``firstprivate``, ``lastprivate``, ``shared``,
+  ``linear``), and in ``map`` clauses for target directives.
+  Limitations: tuple-like bindings (using the tuple protocol with ``get<N>()``)
+  are not yet supported; conditional lastprivate and reductions are not yet
+  supported.
+  Important restriction for target regions: if the original variable is
+  explicitly mapped (e.g., ``map(tofrom: t)``) but only bindings from it,
+  and not the original variable itself, are used in the target region, the
+  compiler emits an error. Either use the original variable directly in the
+  target region, or map the bindings explicitly instead of the original
+  variable.
 
 ## GPU devices support
 

diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index ba944a62a1fff..68ab4fb414c56 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -985,6 +985,21 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use
   conditions to `llvm.assume`, enabling downstream optimizations. Previously
   the clause was parsed but its condition was discarded without effect.
 
+- Added support for capturing structured bindings in OpenMP regions
+  (a C++20 extension; warned as an extension in C++17). Individual bindings
+  form aggregate decompositions(structs, classes, and arrays) can now be used
+  in data-sharing clauses (``private``, ``firstprivate``, ``lastprivate``,
+  ``shared``, ``linear``) and ``map`` clauses for target directives.
+  Tuple-like bindings (types using the tuple protocol with ``get<N>()``,
+  such as ``std::pair`` or ``std::tuple``) are not yet supported and
+  will produce a compilation error. Reduction clauses with structured bindings
+  are not yet supported.
+  When the original variable is explicitly mapped in a target region
+  but only bindings from it are used (not the original variable itself),
+  modifications to the bindings will not be reflected in the original variable.
+  To ensure correct behavior, either use the original variable directly in the
+  target region or map the bindings explicitly instead.
+
 ### SYCL Support
 
 #### Improvements

diff  --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index ff2223070dc15..6941f0179647a 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4344,6 +4344,24 @@ class DecompositionDecl final
 
   void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
 
+  /// Result of attempting to extract the original variable from a
+  /// DecompositionDecl.
+  struct OriginalVarResult {
+    enum DiagnosticKind {
+      CallExpr = 0,     // Function call.
+      InitListExpr = 1, // Initializer list.
+      Temporary = 2,    // Temporary object.
+      MoveExpr = 3,     // Move expression.
+    };
+    const VarDecl *Var = nullptr;
+    DiagnosticKind DiagKind = Temporary;
+  };
+
+  /// If this decomposition was initialized from a variable (e.g., auto [a,b] =
+  /// p), returns the variable. Otherwise returns nullptr with a diagnostic kind
+  /// indicating why extraction failed.
+  OriginalVarResult getOriginalVar() const;
+
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == Decomposition; }
 };

diff  --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 7219a0d3f8e50..592aa2f1bc942 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -439,6 +439,7 @@ class TextNodeDumper
   void VisitOpenACCDeclareDecl(const OpenACCDeclareDecl *D);
   void VisitOpenACCRoutineDecl(const OpenACCRoutineDecl *D);
   void VisitOpenACCRoutineDeclAttr(const OpenACCRoutineDeclAttr *A);
+  void VisitOMPCaptureKindAttr(const OMPCaptureKindAttr *A);
   void VisitEmbedExpr(const EmbedExpr *S);
   void VisitAtomicExpr(const AtomicExpr *AE);
   void VisitConvertVectorExpr(const ConvertVectorExpr *S);

diff  --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 569475f4f74fe..b9eb41654a81b 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -728,6 +728,10 @@ class Attr {
   // typical attribute serialization. This will cause tablegen to emit a call to
   // ASTRecordWriter::Add<Name>Attr and ASTRecordReader::read<Name>Attr.
   bit HasCustomSerialization = 0;
+  // Set to true if this attribute has a custom text node dump implementation
+  // in TextNodeDumper.cpp. This will prevent the automatic generation of a
+  // Visit method in AttrTextNodeDump.inc.
+  bit HasCustomTextNodeDump = 0;
   // Set to true if all of the attribute's arguments should be parsed in an
   // unevaluated context.
   bit ParseArgumentsAsUnevaluated = 0;
@@ -4782,6 +4786,7 @@ def OMPCaptureKind : Attr {
   let SemaHandler = 0;
   let Args = [UnsignedArgument<"CaptureKindVal">];
   let Documentation = [InternalOnly];
+  let HasCustomTextNodeDump = 1;
   let AdditionalMembers = [{
     llvm::omp::Clause getCaptureKind() const {
       return static_cast<llvm::omp::Clause>(getCaptureKindVal());

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 7190ba9d92685..071cb41fd6902 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10304,8 +10304,8 @@ def err_reference_to_local_in_enclosing_context : Error<
   "%select{%3|block literal|lambda expression|context}2">;
 def err_local_nested_class_invalid_scope : Error<
   "nested local class %0 must be defined in the same block scope as %1">;
-def err_capture_binding_openmp : Error<
-  "capturing a structured binding is not yet supported in OpenMP">;
+def err_capture_tuple_binding_openmp : Error<
+  "capturing tuple-like structured binding %0 is not yet supported in OpenMP">;
 
 def err_static_data_member_not_allowed_in_local_class : Error<
   "static data member %0 not allowed in local %sub{select_tag_type_kind}2 %1">;
@@ -12416,6 +12416,12 @@ def err_omp_parent_cancel_region_ordered : Error<
   "parent region for 'omp %select{cancellation point|cancel}0' construct cannot be ordered">;
 def err_omp_reduction_wrong_type : Error<"reduction type cannot be %select{qualified with 'const', 'volatile' or 'restrict'|a function|a reference|an array}0 type">;
 def err_omp_wrong_var_in_declare_reduction : Error<"only %select{'omp_priv' or 'omp_orig'|'omp_in' or 'omp_out'}0 variables are allowed in %select{initializer|combiner}0 expression">;
+def err_omp_unsupported_on_binding : Error<
+  "%select{reductions|conditional lastprivate|linear clause with parallel constructs|capturing in a lambda or block}0 on structured bindings "
+  "%plural{0:are|:is}0 not yet supported">;
+def err_omp_loop_var_is_structured_binding : Error<
+  "structured binding %0 cannot be used as the loop control variable of an "
+  "OpenMP loop">;
 def err_omp_declare_reduction_redefinition : Error<"redefinition of user-defined reduction for type %0">;
 def err_omp_mapper_wrong_type : Error<
   "mapper type must be of struct, union or class type">;
@@ -12708,6 +12714,18 @@ def note_omp_flush_order_clause_here : Note<
   "memory order clause '%0' is specified here">;
 def err_omp_non_lvalue_in_map_or_motion_clauses: Error<
   "expected addressable lvalue in '%0' clause">;
+def err_omp_unsupported_structured_binding_init : Error<
+  "mapping of %select{structured binding initialized from function call|"
+  "structured binding initialized from initializer list|"
+  "structured binding initialized from temporary object|"
+  "structured binding initialized from move expression|"
+  "tuple-like structured binding}0 is not yet supported">;
+def err_omp_decomposition_bindings_
diff erent_capture_kinds : Error<
+  "bindings from structured binding %0 require conflicting capture kinds "
+  "(by-reference vs. by-copy)">;
+def err_omp_original_var_mapped_bindings_only_used : Error<
+  "original variable %0 is explicitly mapped but only bindings from it are used "
+  "in target region; either use %0 directly or map the bindings explicitly">;
 def err_omp_var_expected : Error<
   "expected variable of the '%0' type%select{|, not %2}1">;
 def err_omp_non_pointer_type_array_shaping_base : Error<

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5becfc9fae152..5f24adbf9e9cf 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9254,7 +9254,8 @@ class Sema final : public SemaBase {
                                    const sema::Capture &From);
 
   /// Build a FieldDecl suitable to hold the given capture.
-  FieldDecl *BuildCaptureField(RecordDecl *RD, const sema::Capture &Capture);
+  FieldDecl *BuildCaptureField(RecordDecl *RD, const sema::Capture &Capture,
+                               bool IsOpenMP = false);
 
   /// Initialize the given capture with a suitable expression.
   ExprResult BuildCaptureInit(const sema::Capture &Capture,

diff  --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index f0da56542ae7e..ac468f6879f7b 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3775,6 +3775,65 @@ void DecompositionDecl::printName(llvm::raw_ostream &OS,
   OS << ']';
 }
 
+DecompositionDecl::OriginalVarResult DecompositionDecl::getOriginalVar() const {
+  OriginalVarResult Result;
+  const Expr *Init = getInit();
+  if (!Init)
+    return Result;
+
+  // Helper to determine diagnostic kind from a CallExpr based on value
+  // category.
+  auto GetDiagKindFromCall = [](const CallExpr *Call) {
+    return Call->isXValue() ? OriginalVarResult::MoveExpr
+                            : OriginalVarResult::CallExpr;
+  };
+  const Expr *Stripped = Init->IgnoreParenImpCasts();
+  if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Stripped))
+    Stripped = BTE->getSubExpr()->IgnoreParenImpCasts();
+  if (const auto *DRE = dyn_cast<DeclRefExpr>(Stripped)) {
+    Result.Var = dyn_cast<VarDecl>(DRE->getDecl());
+    return Result;
+  }
+  if (const auto *CE = dyn_cast<CXXConstructExpr>(Stripped)) {
+    if (CE->getNumArgs() == 1) {
+      const Expr *ArgStripped = CE->getArg(0)->IgnoreParenImpCasts();
+      if (const auto *ArgDRE = dyn_cast<DeclRefExpr>(ArgStripped)) {
+        Result.Var = dyn_cast<VarDecl>(ArgDRE->getDecl());
+        return Result;
+      }
+      if (const auto *Call = dyn_cast<CallExpr>(ArgStripped))
+        Result.DiagKind = GetDiagKindFromCall(Call);
+      else
+        Result.DiagKind = OriginalVarResult::Temporary;
+      return Result;
+    }
+  }
+  Result.DiagKind = OriginalVarResult::Temporary;
+  if (const auto *Call = dyn_cast<CallExpr>(Stripped)) {
+    Result.DiagKind = GetDiagKindFromCall(Call);
+  } else if (isa<InitListExpr, CXXStdInitializerListExpr>(Stripped)) {
+    Result.DiagKind = OriginalVarResult::InitListExpr;
+  } else if (const auto *FCE = dyn_cast<CXXFunctionalCastExpr>(Stripped)) {
+    const Expr *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
+    if (isa<InitListExpr>(SubExpr)) {
+      Result.DiagKind = OriginalVarResult::InitListExpr;
+    } else if (const auto *Call = dyn_cast<CallExpr>(SubExpr)) {
+      Result.DiagKind = GetDiagKindFromCall(Call);
+    } else if (const auto *CE = dyn_cast<CXXConstructExpr>(SubExpr)) {
+      if (CE->getNumArgs() == 1) {
+        if (const auto *ArgCall =
+                dyn_cast<CallExpr>(CE->getArg(0)->IgnoreParenImpCasts()))
+          Result.DiagKind = GetDiagKindFromCall(ArgCall);
+      }
+    }
+  } else if (isa<MaterializeTemporaryExpr, CXXBindTemporaryExpr>(Stripped)) {
+    Result.DiagKind = OriginalVarResult::Temporary;
+  } else if (Stripped->isXValue()) {
+    Result.DiagKind = OriginalVarResult::MoveExpr;
+  }
+  return Result;
+}
+
 void MSPropertyDecl::anchor() {}
 
 MSPropertyDecl *MSPropertyDecl::Create(ASTContext &C, DeclContext *DC,

diff  --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 70008d531177d..45981683ad171 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -3412,6 +3412,10 @@ void TextNodeDumper::VisitOpenACCRoutineDeclAttr(
     });
 }
 
+void TextNodeDumper::VisitOMPCaptureKindAttr(const OMPCaptureKindAttr *A) {
+  OS << " " << llvm::omp::getOpenMPClauseName(A->getCaptureKind());
+}
+
 void TextNodeDumper::VisitEmbedExpr(const EmbedExpr *S) {
   AddChild("begin", [=] { OS << S->getStartingElementPos(); });
   AddChild("number of elements", [=] { OS << S->getDataElementCount(); });

diff  --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 2a28ae041cee0..aca0415d7f544 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3615,6 +3615,69 @@ static bool canEmitSpuriousReferenceToVariable(CodeGenFunction &CGF,
   }
 }
 
+/// Emit an LValue for a structured binding captured in an OpenMP region.
+/// Handles extracting individual bindings from the captured decomposed
+/// declaration (struct fields, array elements, etc.).
+LValue CodeGenFunction::EmitOMPCapturedBindingLValue(const BindingDecl *BD) {
+  assert(CapturedStmtInfo && "Expected to be inside a captured region");
+  assert(CapturedStmtInfo->getKind() == CapturedRegionKind::CR_OpenMP &&
+         "Expected OpenMP captured region");
+  assert(CGM.getLangOpts().OpenMP && "Expected OpenMP to be enabled");
+
+  if (auto It = LocalDeclMap.find(BD->getCanonicalDecl());
+      It != LocalDeclMap.end())
+    return MakeAddrLValue(It->second, BD->getType());
+
+  const auto *DD = cast<VarDecl>(BD->getDecomposedDecl());
+
+  // Use getNonReferenceType() because we need the actual object type, not the
+  // reference type. DeclRefExpr with VK_LValue requires a non-reference type
+  // (AST invariant). EmitDeclRefLValue will load any reference for us.
+  QualType DREType = DD->getType().getNonReferenceType();
+  DeclRefExpr DRE(getContext(), const_cast<VarDecl *>(DD),
+                  /*RefersToEnclosingVariableOrCapture=*/true, DREType,
+                  VK_LValue, SourceLocation());
+  LValue BaseLVal = EmitDeclRefLValue(&DRE);
+
+  // Ensure the Address has the correct element type for DD's type.
+  // EmitDeclRefLValue might return an address with a 
diff erent element type
+  // if reference unwrapping occurred.
+  Address BaseAddr = BaseLVal.getAddress();
+  QualType DDType = DD->getType();
+  llvm::Type *ExpectedTy = CGM.getTypes().ConvertTypeForMem(DDType);
+  if (BaseAddr.getElementType() != ExpectedTy)
+    BaseAddr = BaseAddr.withElementType(ExpectedTy);
+
+  // Now emit the binding expression (array subscript, member access, etc.)
+  // by temporarily installing the decomposed storage address, then routing
+  // through EmitLValue for the binding expression.
+  Expr *BindingExpr = BD->getBinding();
+  auto It = LocalDeclMap.find(DD);
+  bool WasMapped = It != LocalDeclMap.end();
+  Address SavedAddr = WasMapped ? It->second : Address::invalid();
+  Address MapAddr = BaseAddr;
+  if (DD->getType()->isReferenceType()) {
+    RawAddress RefSlot = CreateMemTemp(DD->getType(), "omp.binding.ref");
+    Builder.CreateStore(BaseAddr.emitRawPointer(*this), RefSlot);
+    MapAddr = RefSlot;
+  }
+  if (WasMapped)
+    It->second = MapAddr;
+  else
+    LocalDeclMap.insert({DD, MapAddr});
+  llvm::scope_exit Guard([&] {
+    if (WasMapped) {
+      auto RestoreIt = LocalDeclMap.find(DD);
+      assert(RestoreIt != LocalDeclMap.end() && "DD should still be in map");
+      RestoreIt->second = SavedAddr;
+    } else {
+      LocalDeclMap.erase(DD);
+    }
+  });
+
+  return EmitLValue(BindingExpr);
+}
+
 LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
   const NamedDecl *ND = E->getDecl();
   QualType T = E->getType();
@@ -3797,6 +3860,33 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
   // an enclosing scope.
   if (const auto *BD = dyn_cast<BindingDecl>(ND)) {
     if (E->refersToEnclosingVariableOrCapture()) {
+      auto ApplyNontemporal = [&](LValue LV) {
+        if (getLangOpts().OpenMP &&
+            CGM.getOpenMPRuntime().isNontemporalDecl(BD))
+          LV.setNontemporal(/*Value=*/true);
+        return LV;
+      };
+
+      // Try direct lookup first.
+      auto It = LocalDeclMap.find(BD->getCanonicalDecl());
+      if (It != LocalDeclMap.end()) {
+        return ApplyNontemporal(
+            MakeAddrLValue(It->second, E->getType(), AlignmentSource::Decl));
+      }
+
+      // OpenMP case: binding was captured via its decomposed decl.
+      if (CapturedStmtInfo &&
+          CapturedStmtInfo->getKind() == CapturedRegionKind::CR_OpenMP &&
+          CGM.getLangOpts().OpenMP) {
+        auto NameIt = OMPPrivatizedBindings.find(
+            cast<BindingDecl>(BD->getCanonicalDecl()));
+        if (NameIt != OMPPrivatizedBindings.end()) {
+          return ApplyNontemporal(MakeAddrLValue(NameIt->second, E->getType(),
+                                                 AlignmentSource::Decl));
+        }
+        return ApplyNontemporal(EmitOMPCapturedBindingLValue(BD));
+      }
+      // Non-OpenMP case: lambda capture.
       auto *FD = LambdaCaptureFields.lookup(BD);
       return EmitCapturedFieldLValue(*this, FD, CXXABIThisValue);
     }

diff  --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
index 862274bf89355..6de9fea26954e 100644
--- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp
+++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -1895,7 +1895,11 @@ void CGOpenMPRuntime::registerVTable(const OMPExecutableDirective &D) {
 
     const VarDecl *VD = nullptr;
     if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
-      VD = cast<VarDecl>(DRE->getDecl());
+      // Handle BindingDecls by redirecting to their DecompositionDecl.
+      if (auto *BD = dyn_cast<BindingDecl>(DRE->getDecl()))
+        VD = cast<VarDecl>(BD->getDecomposedDecl());
+      else
+        VD = cast<VarDecl>(DRE->getDecl());
     } else if (auto *MRE = dyn_cast<MemberExpr>(E)) {
       if (auto *BaseDRE = dyn_cast<DeclRefExpr>(MRE->getBase())) {
         if (auto *BaseVD = dyn_cast<VarDecl>(BaseDRE->getDecl()))
@@ -3034,6 +3038,14 @@ struct PrivateHelpersTy {
 typedef std::pair<CharUnits /*Align*/, PrivateHelpersTy> PrivateDataTy;
 } // anonymous namespace
 
+/// For BindingDecls, returns the DecomposedDecl as the original VarDecl.
+/// For regular VarDecls, returns the VarDecl itself.
+static const VarDecl *getOriginalVarDecl(const ValueDecl *Decl) {
+  if (const auto *BD = dyn_cast<BindingDecl>(Decl))
+    return cast<VarDecl>(BD->getDecomposedDecl());
+  return cast<VarDecl>(Decl);
+}
+
 static bool isAllocatableDecl(const VarDecl *VD) {
   const VarDecl *CVD = VD->getCanonicalDecl();
   if (!CVD->hasAttr<OMPAllocateDeclAttr>())
@@ -3055,7 +3067,18 @@ createPrivatesRecordDecl(CodeGenModule &CGM, ArrayRef<PrivateDataTy> Privates) {
     RD->startDefinition();
     for (const auto &Pair : Privates) {
       const VarDecl *VD = Pair.second.Original;
-      QualType Type = VD->getType().getNonReferenceType();
+      const VarDecl *PrivateCopy = Pair.second.PrivateCopy;
+      // For BindingDecls, use PrivateCopy type (binding's actual type).
+      // For regular variables, use Original type to preserve qualifiers.
+      // Check OriginalRef to detect BindingDecls since Original may be the
+      // DecompositionDecl.
+      bool IsBinding =
+          Pair.second.OriginalRef &&
+          isa<BindingDecl>(
+              cast<DeclRefExpr>(Pair.second.OriginalRef)->getDecl());
+      QualType Type = IsBinding ? PrivateCopy->getType().getNonReferenceType()
+                                : VD->getType().getNonReferenceType();
+
       // If the private variable is a local variable with lvalue ref type,
       // allocate the pointer instead of the pointee type.
       if (Pair.second.isLocalPrivate()) {
@@ -3327,7 +3350,9 @@ emitTaskPrivateMappingFunction(CodeGenModule &CGM, SourceLocation Loc,
       C.getPointerType(PrivatesQTy).withConst().withRestrict(),
       ImplicitParamKind::Other);
   Args.push_back(TaskPrivatesArg);
-  llvm::DenseMap<CanonicalDeclPtr<const VarDecl>, unsigned> PrivateVarsPos;
+  llvm::SmallDenseMap<CanonicalDeclPtr<const VarDecl>, unsigned> PrivateVarsPos;
+  // Track BindingDecl positions separately since BindingDecl is not a VarDecl.
+  llvm::SmallDenseMap<const BindingDecl *, unsigned> BindingDeclPos;
   unsigned Counter = 1;
   for (const Expr *E : Data.PrivateVars) {
     Args.push_back(ImplicitParamDecl::Create(
@@ -3336,8 +3361,11 @@ emitTaskPrivateMappingFunction(CodeGenModule &CGM, SourceLocation Loc,
             .withConst()
             .withRestrict(),
         ImplicitParamKind::Other));
-    const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl());
-    PrivateVarsPos[VD] = Counter;
+    const ValueDecl *VD = cast<DeclRefExpr>(E)->getDecl();
+    if (const auto *BD = dyn_cast<BindingDecl>(VD))
+      BindingDeclPos[cast<BindingDecl>(BD->getCanonicalDecl())] = Counter;
+    else
+      PrivateVarsPos[cast<VarDecl>(VD)] = Counter;
     ++Counter;
   }
   for (const Expr *E : Data.FirstprivateVars) {
@@ -3347,8 +3375,11 @@ emitTaskPrivateMappingFunction(CodeGenModule &CGM, SourceLocation Loc,
             .withConst()
             .withRestrict(),
         ImplicitParamKind::Other));
-    const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl());
-    PrivateVarsPos[VD] = Counter;
+    const ValueDecl *VD = cast<DeclRefExpr>(E)->getDecl();
+    if (const auto *BD = dyn_cast<BindingDecl>(VD))
+      BindingDeclPos[cast<BindingDecl>(BD->getCanonicalDecl())] = Counter;
+    else
+      PrivateVarsPos[cast<VarDecl>(VD)] = Counter;
     ++Counter;
   }
   for (const Expr *E : Data.LastprivateVars) {
@@ -3358,8 +3389,11 @@ emitTaskPrivateMappingFunction(CodeGenModule &CGM, SourceLocation Loc,
             .withConst()
             .withRestrict(),
         ImplicitParamKind::Other));
-    const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl());
-    PrivateVarsPos[VD] = Counter;
+    const ValueDecl *VD = cast<DeclRefExpr>(E)->getDecl();
+    if (const auto *BD = dyn_cast<BindingDecl>(VD))
+      BindingDeclPos[cast<BindingDecl>(BD->getCanonicalDecl())] = Counter;
+    else
+      PrivateVarsPos[cast<VarDecl>(VD)] = Counter;
     ++Counter;
   }
   for (const VarDecl *VD : Data.PrivateLocals) {
@@ -3406,7 +3440,28 @@ emitTaskPrivateMappingFunction(CodeGenModule &CGM, SourceLocation Loc,
   Counter = 0;
   for (const FieldDecl *Field : PrivatesQTyRD->fields()) {
     LValue FieldLVal = CGF.EmitLValueForField(Base, Field);
-    const VarDecl *VD = Args[PrivateVarsPos[Privates[Counter].second.Original]];
+    // Lookup by the original declaration (BindingDecl or VarDecl).
+    const ValueDecl *LookupVD;
+    if (Privates[Counter].second.OriginalRef) {
+      LookupVD =
+          cast<DeclRefExpr>(Privates[Counter].second.OriginalRef)->getDecl();
+    } else {
+      LookupVD = Privates[Counter].second.Original;
+    }
+
+    // For BindingDecls, the privates record now stores each binding's type
+    // directly (not the full DecompositionDecl), so FieldLVal is already
+    // correct.
+
+    unsigned Position;
+    if (const auto *BD = dyn_cast<BindingDecl>(LookupVD)) {
+      Position =
+          BindingDeclPos.lookup(cast<BindingDecl>(BD->getCanonicalDecl()));
+      assert(Position && "binding not in privates mapping");
+    } else {
+      Position = PrivateVarsPos[cast<VarDecl>(LookupVD)];
+    }
+    const VarDecl *VD = Args[Position];
     LValue RefLVal =
         CGF.MakeAddrLValue(CGF.GetAddrOfLocalVar(VD), VD->getType());
     LValue RefLoadLVal = CGF.EmitLoadOfPointerLValue(
@@ -3481,9 +3536,41 @@ static void emitPrivatesInit(CodeGenFunction &CGF,
               CGF.MakeAddrLValue(CGF.GetAddrOfLocalVar(OriginalVD), Type);
         } else if (ForDup) {
           SharedRefLValue = CGF.EmitLValueForField(SrcBase, SharedField);
+          // For BindingDecls, access the specific binding field within the
+          // captured DecompositionDecl.
+          if (Pair.second.OriginalRef) {
+            if (const auto *DRE =
+                    dyn_cast<DeclRefExpr>(Pair.second.OriginalRef)) {
+              if (const auto *BD = dyn_cast<BindingDecl>(DRE->getDecl())) {
+                // Emit the binding subobject (member or array element) with
+                // the decomposed decl temporarily mapped to the capture.
+                const VarDecl *DD = cast<VarDecl>(BD->getDecomposedDecl());
+                auto It = CGF.findLocalDecl(DD);
+                bool WasMapped = It != CGF.localDeclMapEnd();
+                Address Saved = WasMapped ? It->second : Address::invalid();
+                if (WasMapped)
+                  It->second = SharedRefLValue.getAddress();
+                else
+                  CGF.insertLocalDecl(DD, SharedRefLValue.getAddress());
+                SharedRefLValue = CGF.EmitLValue(BD->getBinding());
+                if (WasMapped) {
+                  auto RestoreIt = CGF.findLocalDecl(DD);
+                  RestoreIt->second = Saved;
+                } else {
+                  CGF.eraseLocalDecl(DD);
+                }
+              }
+            }
+          }
+          bool IsBinding =
+              Pair.second.OriginalRef &&
+              isa<BindingDecl>(
+                  cast<DeclRefExpr>(Pair.second.OriginalRef)->getDecl());
           SharedRefLValue = CGF.MakeAddrLValue(
               SharedRefLValue.getAddress().withAlignment(
-                  C.getDeclAlign(OriginalVD)),
+                  IsBinding ? C.toCharUnitsFromBits(
+                                  C.getTypeAlign(SharedRefLValue.getType()))
+                            : C.getDeclAlign(OriginalVD)),
               SharedRefLValue.getType(), LValueBaseInfo(AlignmentSource::Decl),
               SharedRefLValue.getTBAAInfo());
         } else if (CGF.LambdaCaptureFields.count(
@@ -3532,6 +3619,72 @@ static void emitPrivatesInit(CodeGenFunction &CGF,
       } else {
         CGF.EmitExprAsInit(Init, VD, PrivateLValue, /*capturedByInit=*/false);
       }
+    } else if (const VarDecl *OriginalVD = Pair.second.Original) {
+      // Handle array bindings without initializers (firstprivate only).
+      // For private clause (PrivateElemInit is nullptr), skip initialization.
+      // Check if OriginalRef is a BindingDecl with array type.
+      const BindingDecl *BD = nullptr;
+      if (Pair.second.OriginalRef) {
+        if (const auto *DRE = dyn_cast<DeclRefExpr>(Pair.second.OriginalRef)) {
+          BD = dyn_cast<BindingDecl>(DRE->getDecl());
+        }
+      }
+      if (BD && BD->getType()->isArrayType() && Pair.second.PrivateElemInit) {
+        LValue PrivateLValue = CGF.EmitLValueForField(PrivatesBase, *FI);
+        QualType Type = PrivateLValue.getType();
+        const FieldDecl *SharedField = CapturesInfo.lookup(OriginalVD);
+
+        // Lambda to temporarily map DecompositionDecl and emit the binding
+        // expression.
+        auto EmitBindingWithTempMap = [&CGF](const BindingDecl *BD,
+                                             Address DDAddr) -> LValue {
+          const VarDecl *DD = cast<VarDecl>(BD->getDecomposedDecl());
+          auto It = CGF.findLocalDecl(DD);
+          bool WasMapped = It != CGF.localDeclMapEnd();
+          Address Saved = WasMapped ? It->second : Address::invalid();
+          if (WasMapped)
+            It->second = DDAddr;
+          else
+            CGF.insertLocalDecl(DD, DDAddr);
+
+          LValue Result = CGF.EmitLValue(BD->getBinding());
+
+          // Restore the mapping
+          if (WasMapped) {
+            auto RestoreIt = CGF.findLocalDecl(DD);
+            RestoreIt->second = Saved;
+          } else {
+            CGF.eraseLocalDecl(DD);
+          }
+
+          return Result;
+        };
+
+        LValue SharedRefLValue;
+        if (ForDup) {
+          SharedRefLValue = CGF.EmitLValueForField(SrcBase, SharedField);
+          SharedRefLValue =
+              EmitBindingWithTempMap(BD, SharedRefLValue.getAddress());
+          SharedRefLValue = CGF.MakeAddrLValue(
+              SharedRefLValue.getAddress().withAlignment(
+                  C.getDeclAlign(OriginalVD)),
+              SharedRefLValue.getType(), LValueBaseInfo(AlignmentSource::Decl),
+              SharedRefLValue.getTBAAInfo());
+        } else {
+          // For !ForDup (first task), emit binding from parent scope using
+          // InlinedOpenMPRegionRAII to access the correct scope
+          InlinedOpenMPRegionRAII Region(
+              CGF, [](CodeGenFunction &, PrePostActionTy &) {}, OMPD_unknown,
+              /*HasCancel=*/false, /*NoInheritance=*/true);
+
+          // Get the decomposed decl address from parent scope
+          const VarDecl *DD = cast<VarDecl>(BD->getDecomposedDecl());
+          Address DDAddr = CGF.GetAddrOfLocalVar(DD);
+          SharedRefLValue = EmitBindingWithTempMap(BD, DDAddr);
+        }
+        // Perform simple memcpy for array binding
+        CGF.EmitAggregateAssign(PrivateLValue, SharedRefLValue, Type);
+      }
     }
     ++FI;
   }
@@ -3784,32 +3937,35 @@ CGOpenMPRuntime::emitTaskInit(CodeGenFunction &CGF, SourceLocation Loc,
   // Aggregate privates and sort them by the alignment.
   const auto *I = Data.PrivateCopies.begin();
   for (const Expr *E : Data.PrivateVars) {
-    const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl());
-    Privates.emplace_back(
-        C.getDeclAlign(VD),
-        PrivateHelpersTy(E, VD, cast<VarDecl>(cast<DeclRefExpr>(*I)->getDecl()),
-                         /*PrivateElemInit=*/nullptr));
+    const auto *Decl = cast<DeclRefExpr>(E)->getDecl();
+    const auto *VD = getOriginalVarDecl(Decl);
+    const auto *CopyVD = cast<VarDecl>(cast<DeclRefExpr>(*I)->getDecl());
+    Privates.emplace_back(C.getDeclAlign(VD),
+                          PrivateHelpersTy(E, VD, CopyVD,
+                                           /*PrivateElemInit=*/nullptr));
     ++I;
   }
   I = Data.FirstprivateCopies.begin();
   const auto *IElemInitRef = Data.FirstprivateInits.begin();
   for (const Expr *E : Data.FirstprivateVars) {
-    const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl());
-    Privates.emplace_back(
-        C.getDeclAlign(VD),
-        PrivateHelpersTy(
-            E, VD, cast<VarDecl>(cast<DeclRefExpr>(*I)->getDecl()),
-            cast<VarDecl>(cast<DeclRefExpr>(*IElemInitRef)->getDecl())));
+    const auto *Decl = cast<DeclRefExpr>(E)->getDecl();
+    const auto *VD = getOriginalVarDecl(Decl);
+    const auto *CopyVD = cast<VarDecl>(cast<DeclRefExpr>(*I)->getDecl());
+    const auto *InitVD =
+        cast<VarDecl>(cast<DeclRefExpr>(*IElemInitRef)->getDecl());
+    Privates.emplace_back(C.getDeclAlign(VD),
+                          PrivateHelpersTy(E, VD, CopyVD, InitVD));
     ++I;
     ++IElemInitRef;
   }
   I = Data.LastprivateCopies.begin();
   for (const Expr *E : Data.LastprivateVars) {
-    const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl());
-    Privates.emplace_back(
-        C.getDeclAlign(VD),
-        PrivateHelpersTy(E, VD, cast<VarDecl>(cast<DeclRefExpr>(*I)->getDecl()),
-                         /*PrivateElemInit=*/nullptr));
+    const auto *Decl = cast<DeclRefExpr>(E)->getDecl();
+    const auto *VD = getOriginalVarDecl(Decl);
+    const auto *CopyVD = cast<VarDecl>(cast<DeclRefExpr>(*I)->getDecl());
+    Privates.emplace_back(C.getDeclAlign(VD),
+                          PrivateHelpersTy(E, VD, CopyVD,
+                                           /*PrivateElemInit=*/nullptr));
     ++I;
   }
   for (const VarDecl *VD : Data.PrivateLocals) {
@@ -5685,8 +5841,15 @@ static std::string generateUniqueName(CodeGenModule &CGM, StringRef Prefix,
   llvm::raw_svector_ostream Out(Buffer);
   const clang::DeclRefExpr *DE;
   const VarDecl *D = ::getBaseDecl(Ref, DE);
-  if (!D)
-    D = cast<VarDecl>(cast<DeclRefExpr>(Ref)->getDecl());
+  if (!D) {
+    auto *DRE = cast<DeclRefExpr>(Ref);
+    if (const auto *BD = dyn_cast<BindingDecl>(DRE->getDecl())) {
+      // For BindingDecls, use the decomposed declaration as the base.
+      D = cast<VarDecl>(BD->getDecomposedDecl());
+    } else {
+      D = cast<VarDecl>(DRE->getDecl());
+    }
+  }
   D = D->getCanonicalDecl();
   std::string Name = CGM.getOpenMPRuntime().getName(
       {D->isLocalVarDeclOrParm() ? D->getName() : CGM.getMangledName(D)});
@@ -9391,9 +9554,12 @@ class MappableExprsHandler {
       : CurDir(&Dir), CGF(CGF), AttachPtrComparator(*this) {
     // Extract firstprivate clause information.
     for (const auto *C : Dir.getClausesOfKind<OMPFirstprivateClause>())
-      for (const auto *D : C->varlist())
-        FirstPrivateDecls.try_emplace(
-            cast<VarDecl>(cast<DeclRefExpr>(D)->getDecl()), C->isImplicit());
+      for (const auto *D : C->varlist()) {
+        const ValueDecl *VD = cast<DeclRefExpr>(D)->getDecl();
+        if (const auto *BD = dyn_cast<BindingDecl>(VD))
+          VD = cast<VarDecl>(BD->getDecomposedDecl());
+        FirstPrivateDecls.try_emplace(cast<VarDecl>(VD), C->isImplicit());
+      }
     // Extract implicit firstprivates from uses_allocators clauses.
     for (const auto *C : Dir.getClausesOfKind<OMPUsesAllocatorsClause>()) {
       for (unsigned I = 0, E = C->getNumberOfAllocators(); I < E; ++I) {

diff  --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp
index 1a805b05b401e..cff5801fd097b 100644
--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -190,8 +190,10 @@ class OMPLoopScope : public CodeGenFunction::RunCleanupsScope {
       // Mark private vars as undefs.
       for (const auto *C : LD->getClausesOfKind<OMPPrivateClause>()) {
         for (const Expr *IRef : C->varlist()) {
-          const auto *OrigVD =
-              cast<VarDecl>(cast<DeclRefExpr>(IRef)->getDecl());
+          const auto *OrigDecl = cast<DeclRefExpr>(IRef)->getDecl();
+          const auto *OrigVD = dyn_cast<VarDecl>(OrigDecl);
+          if (!OrigVD)
+            continue;
           if (EmittedAsPrivate.insert(OrigVD->getCanonicalDecl()).second) {
             QualType OrigVDTy = OrigVD->getType().getNonReferenceType();
             (void)PreCondVars.setVarAddr(
@@ -385,6 +387,21 @@ static void emitCommonOMPTargetDirective(CodeGenFunction &CGF,
                                          const OMPExecutableDirective &S,
                                          const RegionCodeGenTy &CodeGen);
 
+Address CodeGenFunction::EmitOMPBindingOriginalAddr(const BindingDecl *BD,
+                                                    SourceLocation Loc) {
+  if (CapturedStmtInfo &&
+      CapturedStmtInfo->getKind() == CapturedRegionKind::CR_OpenMP) {
+    if (const auto *DD = dyn_cast<VarDecl>(BD->getDecomposedDecl())) {
+      if (CapturedStmtInfo->lookup(DD))
+        return EmitOMPCapturedBindingLValue(BD).getAddress();
+    }
+  }
+  DeclRefExpr DRE(getContext(), const_cast<BindingDecl *>(BD),
+                  /*RefersToEnclosingVariableOrCapture=*/false, BD->getType(),
+                  VK_LValue, Loc);
+  return EmitLValue(&DRE).getAddress();
+}
+
 LValue CodeGenFunction::EmitOMPSharedLValue(const Expr *E) {
   if (const auto *OrigDRE = dyn_cast<DeclRefExpr>(E)) {
     if (const auto *OrigVD = dyn_cast<VarDecl>(OrigDRE->getDecl())) {
@@ -397,6 +414,17 @@ LValue CodeGenFunction::EmitOMPSharedLValue(const Expr *E) {
                       OrigDRE->getType(), VK_LValue, OrigDRE->getExprLoc());
       return EmitLValue(&DRE);
     }
+    if (const auto *OrigBD = dyn_cast<BindingDecl>(OrigDRE->getDecl())) {
+      OrigBD = cast<BindingDecl>(OrigBD->getCanonicalDecl());
+      const auto *DD = cast<VarDecl>(OrigBD->getDecomposedDecl());
+      bool IsCaptured = LambdaCaptureFields.lookup(OrigBD) ||
+                        (CapturedStmtInfo && CapturedStmtInfo->lookup(DD)) ||
+                        isa_and_nonnull<BlockDecl>(CurCodeDecl);
+      DeclRefExpr DRE(getContext(), const_cast<BindingDecl *>(OrigBD),
+                      IsCaptured, OrigDRE->getType(), VK_LValue,
+                      OrigDRE->getExprLoc());
+      return EmitLValue(&DRE);
+    }
   }
   return EmitLValue(E);
 }
@@ -1155,12 +1183,12 @@ bool CodeGenFunction::EmitOMPFirstprivateClause(const OMPExecutableDirective &D,
   bool DeviceConstTarget = getLangOpts().OpenMPIsTargetDevice &&
                            isOpenMPTargetExecutionDirective(EKind);
   bool FirstprivateIsLastprivate = false;
-  llvm::DenseMap<const VarDecl *, OpenMPLastprivateModifier> Lastprivates;
+  llvm::SmallDenseMap<const Decl *, OpenMPLastprivateModifier> Lastprivates;
   for (const auto *C : D.getClausesOfKind<OMPLastprivateClause>()) {
-    for (const auto *D : C->varlist())
-      Lastprivates.try_emplace(
-          cast<VarDecl>(cast<DeclRefExpr>(D)->getDecl())->getCanonicalDecl(),
-          C->getKind());
+    for (const auto *D : C->varlist()) {
+      const auto *VD = cast<DeclRefExpr>(D)->getDecl();
+      Lastprivates.try_emplace(VD->getCanonicalDecl(), C->getKind());
+    }
   }
   llvm::DenseSet<const VarDecl *> EmittedAsFirstprivate;
   llvm::SmallVector<OpenMPDirectiveKind, 4> CaptureRegions;
@@ -1173,11 +1201,91 @@ bool CodeGenFunction::EmitOMPFirstprivateClause(const OMPExecutableDirective &D,
     const auto *IRef = C->varlist_begin();
     const auto *InitsRef = C->inits().begin();
     for (const Expr *IInit : C->private_copies()) {
-      const auto *OrigVD = cast<VarDecl>(cast<DeclRefExpr>(*IRef)->getDecl());
+      const auto *OrigDecl = cast<DeclRefExpr>(*IRef)->getDecl();
+      const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(IInit)->getDecl());
+
+      if (const auto *BD = dyn_cast<BindingDecl>(OrigDecl)) {
+        // Check if this binding is also lastprivate.
+        bool ThisFirstprivateIsLastprivate =
+            Lastprivates.count(BD->getCanonicalDecl()) > 0;
+        const auto *DD = cast<VarDecl>(BD->getDecomposedDecl());
+        // If the decomposition is captured by copy, the captured field is
+        // already a private copy; map the binding to its member directly.
+        if (!MustEmitFirstprivateCopy && !ThisFirstprivateIsLastprivate) {
+          if (const FieldDecl *FD = CapturedStmtInfo->lookup(DD)) {
+            if (!FD->getType()->isReferenceType()) {
+              bool IsRegistered = PrivateScope.addPrivate(
+                  BD, EmitOMPCapturedBindingLValue(BD).getAddress());
+              assert(IsRegistered &&
+                     "firstprivate var already registered as firstprivate");
+              (void)IsRegistered;
+              ++IRef;
+              ++InitsRef;
+              continue;
+            }
+          }
+        }
+        const auto *VDInit =
+            cast<VarDecl>(cast<DeclRefExpr>(*InitsRef)->getDecl());
+        Address OriginalAddr =
+            EmitOMPBindingOriginalAddr(BD, (*IRef)->getExprLoc());
+
+        QualType Type = VD->getType();
+        bool IsRegistered;
+        if (Type->isArrayType()) {
+          // For array bindings, use array copy logic
+          AutoVarEmission Emission = EmitAutoVarAlloca(*VD);
+          const Expr *Init = VD->getInit();
+          LValue OriginalLVal = MakeAddrLValue(OriginalAddr, Type);
+          if (!Init || !isa<CXXConstructExpr>(Init) ||
+              isTrivialInitializer(Init)) {
+            // Perform simple memcpy.
+            LValue Dest = MakeAddrLValue(Emission.getAllocatedAddress(), Type);
+            EmitAggregateAssign(Dest, OriginalLVal, Type);
+          } else {
+            EmitOMPAggregateAssign(
+                Emission.getAllocatedAddress(), OriginalAddr, Type,
+                [this, VDInit, Init](Address DestElement, Address SrcElement) {
+                  // Clean up any temporaries needed by the initialization.
+                  RunCleanupsScope InitScope(*this);
+                  // Emit initialization for single element.
+                  setAddrOfLocalVar(VDInit, SrcElement);
+                  EmitAnyExprToMem(Init, DestElement,
+                                   Init->getType().getQualifiers(),
+                                   /*IsInitializer*/ false);
+                  LocalDeclMap.erase(VDInit);
+                });
+          }
+          EmitAutoVarCleanups(Emission);
+          IsRegistered =
+              PrivateScope.addPrivate(BD, Emission.getAllocatedAddress());
+        } else {
+          // VD now has the binding's type (e.g., int), not the struct type.
+          // Emit VD initialized from the binding's field address.
+          setAddrOfLocalVar(VDInit, OriginalAddr);
+          EmitDecl(*VD);
+          LocalDeclMap.erase(VDInit);
+          Address VDAddr = GetAddrOfLocalVar(VD);
+          // VD is the private copy of the binding, map BD to VDAddr directly
+          IsRegistered = PrivateScope.addPrivate(BD, VDAddr);
+        }
+
+        assert(IsRegistered &&
+               "firstprivate var already registered as firstprivate");
+        (void)IsRegistered;
+        FirstprivateIsLastprivate =
+            FirstprivateIsLastprivate || ThisFirstprivateIsLastprivate;
+        ++IRef;
+        ++InitsRef;
+        continue;
+      }
+
+      // Original VarDecl logic.
+      const VarDecl *OrigVD = dyn_cast<VarDecl>(OrigDecl);
+      assert(OrigVD && "Expected VarDecl for non-BindingDecl firstprivate");
       bool ThisFirstprivateIsLastprivate =
           Lastprivates.count(OrigVD->getCanonicalDecl()) > 0;
       const FieldDecl *FD = CapturedStmtInfo->lookup(OrigVD);
-      const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(IInit)->getDecl());
       if (!MustEmitFirstprivateCopy && !ThisFirstprivateIsLastprivate && FD &&
           !FD->getType()->isReferenceType() &&
           (!VD || !VD->hasAttr<OMPAllocateDeclAttr>())) {
@@ -1296,19 +1404,18 @@ void CodeGenFunction::EmitOMPPrivateClause(
     CodeGenFunction::OMPPrivateScope &PrivateScope) {
   if (!HaveInsertPoint())
     return;
-  llvm::DenseSet<const VarDecl *> EmittedAsPrivate;
+  llvm::SmallDenseSet<const ValueDecl *> EmittedAsPrivate;
   for (const auto *C : D.getClausesOfKind<OMPPrivateClause>()) {
     auto IRef = C->varlist_begin();
     for (const Expr *IInit : C->private_copies()) {
-      const auto *OrigVD = cast<VarDecl>(cast<DeclRefExpr>(*IRef)->getDecl());
-      if (EmittedAsPrivate.insert(OrigVD->getCanonicalDecl()).second) {
-        const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(IInit)->getDecl());
+      const auto *OrigDecl = cast<DeclRefExpr>(*IRef)->getDecl();
+      const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(IInit)->getDecl());
+      if (EmittedAsPrivate.insert(cast<ValueDecl>(OrigDecl->getCanonicalDecl()))
+              .second) {
         EmitDecl(*VD);
-        // Emit private VarDecl with copy init.
         bool IsRegistered =
-            PrivateScope.addPrivate(OrigVD, GetAddrOfLocalVar(VD));
+            PrivateScope.addPrivate(OrigDecl, GetAddrOfLocalVar(VD));
         assert(IsRegistered && "private var already registered as private");
-        // Silence the warning about unused variable.
         (void)IsRegistered;
       }
       ++IRef;
@@ -1402,7 +1509,7 @@ bool CodeGenFunction::EmitOMPLastprivateClauseInit(
           cast<VarDecl>(cast<DeclRefExpr>(C)->getDecl())->getCanonicalDecl());
     }
   }
-  llvm::DenseSet<const VarDecl *> AlreadyEmittedVars;
+  llvm::SmallDenseSet<const ValueDecl *> AlreadyEmittedVars;
   for (const auto *C : D.getClausesOfKind<OMPLastprivateClause>()) {
     HasAtLeastOneLastprivate = true;
     if (isOpenMPTaskLoopDirective(EKind) && !getLangOpts().OpenMPSimd)
@@ -1412,7 +1519,34 @@ bool CodeGenFunction::EmitOMPLastprivateClauseInit(
     for (const Expr *IInit : C->private_copies()) {
       // Keep the address of the original variable for future update at the end
       // of the loop.
-      const auto *OrigVD = cast<VarDecl>(cast<DeclRefExpr>(*IRef)->getDecl());
+      const auto *OrigDecl = cast<DeclRefExpr>(*IRef)->getDecl();
+      // Handle BindingDecls with the same level of support as VarDecls.
+      if (const auto *BD = dyn_cast<BindingDecl>(OrigDecl)) {
+        if (AlreadyEmittedVars.insert(cast<ValueDecl>(BD->getCanonicalDecl()))
+                .second) {
+          const auto *DestVD =
+              cast<VarDecl>(cast<DeclRefExpr>(*IDestRef)->getDecl());
+
+          // Get the original binding address.
+          Address OrigAddr =
+              EmitOMPBindingOriginalAddr(BD, (*IRef)->getExprLoc());
+          PrivateScope.addPrivate(DestVD, OrigAddr);
+          if (IInit) {
+            const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(IInit)->getDecl());
+            // Emit private VarDecl with copy init.
+            EmitDecl(*VD);
+            Address VDAddr = GetAddrOfLocalVar(VD);
+            bool IsRegistered = PrivateScope.addPrivate(BD, VDAddr);
+            assert(IsRegistered &&
+                   "lastprivate binding already registered as private");
+            (void)IsRegistered;
+          }
+        }
+        ++IRef;
+        ++IDestRef;
+        continue;
+      }
+      const auto *OrigVD = cast<VarDecl>(OrigDecl);
       // Taskloops do not require additional initialization, it is done in
       // runtime support library.
       if (AlreadyEmittedVars.insert(OrigVD->getCanonicalDecl()).second) {
@@ -1482,8 +1616,8 @@ void CodeGenFunction::EmitOMPLastprivateClauseFinal(
     Builder.CreateCondBr(IsLastIterCond, ThenBB, DoneBB);
     EmitBlock(ThenBB);
   }
-  llvm::DenseSet<const VarDecl *> AlreadyEmittedVars;
-  llvm::DenseMap<const VarDecl *, const Expr *> LoopCountersAndUpdates;
+  llvm::DenseSet<const ValueDecl *> AlreadyEmittedVars;
+  llvm::SmallDenseMap<const VarDecl *, const Expr *> LoopCountersAndUpdates;
   if (const auto *LoopDirective = dyn_cast<OMPLoopDirective>(&D)) {
     auto IC = LoopDirective->counters().begin();
     for (const Expr *F : LoopDirective->finals()) {
@@ -1501,27 +1635,69 @@ void CodeGenFunction::EmitOMPLastprivateClauseFinal(
     auto ISrcRef = C->source_exprs().begin();
     auto IDestRef = C->destination_exprs().begin();
     for (const Expr *AssignOp : C->assignment_ops()) {
-      const auto *PrivateVD =
-          cast<VarDecl>(cast<DeclRefExpr>(*IRef)->getDecl());
-      QualType Type = PrivateVD->getType();
-      const auto *CanonicalVD = PrivateVD->getCanonicalDecl();
-      if (AlreadyEmittedVars.insert(CanonicalVD).second) {
+      const auto *PrivateDecl = cast<DeclRefExpr>(*IRef)->getDecl();
+
+      // For BindingDecls, check if we should use .lastprivate.src or the BD
+      // itself.
+      const VarDecl *PrivateVD = nullptr;
+      const BindingDecl *BD = nullptr;
+      if ((BD = dyn_cast<BindingDecl>(PrivateDecl))) {
+        // Check if .lastprivate.src is available (taskloop case).
+        const auto *SrcVD =
+            cast<VarDecl>(cast<DeclRefExpr>(*ISrcRef)->getDecl());
+
+        if (LocalDeclMap.count(SrcVD)) {
+          // Taskloop case: use .lastprivate.src.
+          PrivateVD = SrcVD;
+        } else if (OMPPrivatizedBindings.count(BD)) {
+          // Parallel for case: BindingDecl is directly privatized.
+          // Leave PrivateVD as nullptr to handle specially below.
+        }
+      } else {
+        PrivateVD = cast<VarDecl>(PrivateDecl);
+      }
+
+      QualType Type = PrivateVD ? PrivateVD->getType() : BD->getType();
+      const auto *CanonicalVD =
+          PrivateVD ? PrivateVD->getCanonicalDecl() : nullptr;
+
+      // Check if already emitted.
+      bool ShouldEmit =
+          AlreadyEmittedVars
+              .insert(PrivateVD ? static_cast<const ValueDecl *>(CanonicalVD)
+                                : static_cast<const ValueDecl *>(BD))
+              .second;
+      if (ShouldEmit) {
         // If lastprivate variable is a loop control variable for loop-based
         // directive, update its value before copyin back to original
         // variable.
-        if (const Expr *FinalExpr = LoopCountersAndUpdates.lookup(CanonicalVD))
-          EmitIgnoredExpr(FinalExpr);
+        if (CanonicalVD) {
+          if (const Expr *FinalExpr =
+                  LoopCountersAndUpdates.lookup(CanonicalVD))
+            EmitIgnoredExpr(FinalExpr);
+        }
         const auto *SrcVD =
             cast<VarDecl>(cast<DeclRefExpr>(*ISrcRef)->getDecl());
         const auto *DestVD =
             cast<VarDecl>(cast<DeclRefExpr>(*IDestRef)->getDecl());
+
         // Get the address of the private variable.
-        Address PrivateAddr = GetAddrOfLocalVar(PrivateVD);
-        if (const auto *RefTy = PrivateVD->getType()->getAs<ReferenceType>())
-          PrivateAddr = Address(
-              Builder.CreateLoad(PrivateAddr),
-              CGM.getTypes().ConvertTypeForMem(RefTy->getPointeeType()),
-              CGM.getNaturalTypeAlignment(RefTy->getPointeeType()));
+        Address PrivateAddr = Address::invalid();
+        if (PrivateVD) {
+          PrivateAddr = GetAddrOfLocalVar(PrivateVD);
+        } else {
+          auto It = OMPPrivatizedBindings.find(BD);
+          assert(It != OMPPrivatizedBindings.end() &&
+                 "BindingDecl should be privatized");
+          PrivateAddr = It->second;
+        }
+        if (PrivateVD) {
+          if (const auto *RefTy = PrivateVD->getType()->getAs<ReferenceType>())
+            PrivateAddr = Address(
+                Builder.CreateLoad(PrivateAddr),
+                CGM.getTypes().ConvertTypeForMem(RefTy->getPointeeType()),
+                CGM.getNaturalTypeAlignment(RefTy->getPointeeType()));
+        }
         // Store the last value to the private copy in the last iteration.
         if (C->getKind() == OMPC_LASTPRIVATE_conditional)
           CGM.getOpenMPRuntime().emitLastprivateConditionalFinalUpdate(
@@ -1580,7 +1756,6 @@ void CodeGenFunction::EmitOMPReductionClauseInit(
   auto *IPriv = Privates.begin();
   for (const Expr *IRef : Shareds) {
     const auto *PrivateVD = cast<VarDecl>(cast<DeclRefExpr>(*IPriv)->getDecl());
-    // Emit private VarDecl with reduction init.
     RedCG.emitSharedOrigLValue(*this, Count);
     RedCG.emitAggregateType(*this, Count);
     AutoVarEmission Emission = EmitAutoVarAlloca(*PrivateVD);
@@ -1847,7 +2022,11 @@ checkForLastprivateConditionalUpdate(CodeGenFunction &CGF,
       const auto *DRE = dyn_cast<DeclRefExpr>(Ref->IgnoreParenImpCasts());
       if (!DRE)
         continue;
-      PrivateDecls.insert(cast<VarDecl>(DRE->getDecl()));
+      // Skip BindingDecls - lastprivate conditional only applies to VarDecls.
+      const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
+      if (!VD)
+        continue;
+      PrivateDecls.insert(VD);
       CGF.CGM.getOpenMPRuntime().checkAndEmitLastprivateConditional(CGF, Ref);
     }
   }
@@ -1858,8 +2037,12 @@ checkForLastprivateConditionalUpdate(CodeGenFunction &CGF,
       const auto *DRE = dyn_cast<DeclRefExpr>(Ref->IgnoreParenImpCasts());
       if (!DRE)
         continue;
-      PrivateDecls.insert(cast<VarDecl>(DRE->getDecl()));
-      CGF.CGM.getOpenMPRuntime().checkAndEmitLastprivateConditional(CGF, Ref);
+      // Skip BindingDecls - they don't use the same conditional lastprivate
+      // mechanism.
+      if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
+        PrivateDecls.insert(VD);
+        CGF.CGM.getOpenMPRuntime().checkAndEmitLastprivateConditional(CGF, Ref);
+      }
     }
   }
   for (const auto *C : S.getClausesOfKind<OMPLinearClause>()) {
@@ -1869,8 +2052,10 @@ checkForLastprivateConditionalUpdate(CodeGenFunction &CGF,
       const auto *DRE = dyn_cast<DeclRefExpr>(Ref->IgnoreParenImpCasts());
       if (!DRE)
         continue;
-      PrivateDecls.insert(cast<VarDecl>(DRE->getDecl()));
-      CGF.CGM.getOpenMPRuntime().checkAndEmitLastprivateConditional(CGF, Ref);
+      if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
+        PrivateDecls.insert(VD);
+        CGF.CGM.getOpenMPRuntime().checkAndEmitLastprivateConditional(CGF, Ref);
+      }
     }
   }
   // Privates should ne analyzed since they are not captured at all.
@@ -1884,7 +2069,9 @@ checkForLastprivateConditionalUpdate(CodeGenFunction &CGF,
       const auto *DRE = dyn_cast<DeclRefExpr>(Ref->IgnoreParenImpCasts());
       if (!DRE)
         continue;
-      PrivateDecls.insert(cast<VarDecl>(DRE->getDecl()));
+      // Only track VarDecl, not BindingDecl.
+      if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()))
+        PrivateDecls.insert(VD);
     }
   }
   CGF.CGM.getOpenMPRuntime().checkAndEmitSharedLastprivateConditional(
@@ -2547,17 +2734,21 @@ bool CodeGenFunction::EmitOMPLinearClauseInit(const OMPLoopDirective &D) {
       const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(Init)->getDecl());
       if (const auto *Ref =
               dyn_cast<DeclRefExpr>(VD->getInit()->IgnoreImpCasts())) {
-        AutoVarEmission Emission = EmitAutoVarAlloca(*VD);
-        const auto *OrigVD = cast<VarDecl>(Ref->getDecl());
-        DeclRefExpr DRE(getContext(), const_cast<VarDecl *>(OrigVD),
-                        CapturedStmtInfo->lookup(OrigVD) != nullptr,
-                        VD->getInit()->getType(), VK_LValue,
-                        VD->getInit()->getExprLoc());
-        EmitExprAsInit(
-            &DRE, VD,
-            MakeAddrLValue(Emission.getAllocatedAddress(), VD->getType()),
-            /*capturedByInit=*/false);
-        EmitAutoVarCleanups(Emission);
+        if (isa<BindingDecl>(Ref->getDecl())) {
+          EmitVarDecl(*VD);
+        } else {
+          AutoVarEmission Emission = EmitAutoVarAlloca(*VD);
+          const auto *OrigVD = cast<VarDecl>(Ref->getDecl());
+          DeclRefExpr DRE(getContext(), const_cast<VarDecl *>(OrigVD),
+                          CapturedStmtInfo->lookup(OrigVD) != nullptr,
+                          VD->getInit()->getType(), VK_LValue,
+                          VD->getInit()->getExprLoc());
+          EmitExprAsInit(
+              &DRE, VD,
+              MakeAddrLValue(Emission.getAllocatedAddress(), VD->getType()),
+              /*capturedByInit=*/false);
+          EmitAutoVarCleanups(Emission);
+        }
       } else {
         EmitVarDecl(*VD);
       }
@@ -2594,13 +2785,19 @@ void CodeGenFunction::EmitOMPLinearClauseFinal(
           EmitBlock(ThenBB);
         }
       }
-      const auto *OrigVD = cast<VarDecl>(cast<DeclRefExpr>(*IC)->getDecl());
-      DeclRefExpr DRE(getContext(), const_cast<VarDecl *>(OrigVD),
-                      CapturedStmtInfo->lookup(OrigVD) != nullptr,
-                      (*IC)->getType(), VK_LValue, (*IC)->getExprLoc());
-      Address OrigAddr = EmitLValue(&DRE).getAddress();
+      const auto *OrigDecl = cast<DeclRefExpr>(*IC)->getDecl();
+      Address OrigAddr = Address::invalid();
+      if (const auto *BD = dyn_cast<BindingDecl>(OrigDecl)) {
+        OrigAddr = EmitOMPBindingOriginalAddr(BD, (*IC)->getExprLoc());
+      } else {
+        const auto *OrigVD = cast<VarDecl>(OrigDecl);
+        DeclRefExpr DRE(getContext(), const_cast<VarDecl *>(OrigVD),
+                        CapturedStmtInfo->lookup(OrigVD) != nullptr,
+                        (*IC)->getType(), VK_LValue, (*IC)->getExprLoc());
+        OrigAddr = EmitLValue(&DRE).getAddress();
+      }
       CodeGenFunction::OMPPrivateScope VarScope(*this);
-      VarScope.addPrivate(OrigVD, OrigAddr);
+      VarScope.addPrivate(OrigDecl, OrigAddr);
       (void)VarScope.Privatize();
       EmitIgnoredExpr(F);
       ++IC;
@@ -2743,14 +2940,17 @@ void CodeGenFunction::EmitOMPLinearClause(
   for (const auto *C : D.getClausesOfKind<OMPLinearClause>()) {
     auto CurPrivate = C->privates().begin();
     for (const Expr *E : C->varlist()) {
-      const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl());
+      const auto *VD = cast<DeclRefExpr>(E)->getDecl();
       const auto *PrivateVD =
           cast<VarDecl>(cast<DeclRefExpr>(*CurPrivate)->getDecl());
-      if (!SIMDLCVs.count(VD->getCanonicalDecl())) {
+      bool IsSIMDLCV = false;
+      if (const auto *VarD = dyn_cast<VarDecl>(VD))
+        IsSIMDLCV = SIMDLCVs.count(VarD->getCanonicalDecl());
+      if (!IsSIMDLCV) {
         // Emit private VarDecl with copy init.
         EmitVarDecl(*PrivateVD);
-        bool IsRegistered =
-            PrivateScope.addPrivate(VD, GetAddrOfLocalVar(PrivateVD));
+        Address PrivateAddr = GetAddrOfLocalVar(PrivateVD);
+        bool IsRegistered = PrivateScope.addPrivate(VD, PrivateAddr);
         assert(IsRegistered && "linear var already registered as private");
         // Silence the warning about unused variable.
         (void)IsRegistered;
@@ -5237,13 +5437,14 @@ void CodeGenFunction::EmitOMPTaskBasedDirective(
   }
   // The first function argument for tasks is a thread id, the second one is a
   // part id (0 for tied tasks, >=0 for untied task).
-  llvm::DenseSet<const VarDecl *> EmittedAsPrivate;
+  llvm::DenseSet<const ValueDecl *> EmittedAsPrivate;
   // Get list of private variables.
   for (const auto *C : S.getClausesOfKind<OMPPrivateClause>()) {
     auto IRef = C->varlist_begin();
     for (const Expr *IInit : C->private_copies()) {
-      const auto *OrigVD = cast<VarDecl>(cast<DeclRefExpr>(*IRef)->getDecl());
-      if (EmittedAsPrivate.insert(OrigVD->getCanonicalDecl()).second) {
+      const auto *OrigDecl = cast<DeclRefExpr>(*IRef)->getDecl();
+      if (EmittedAsPrivate.insert(cast<ValueDecl>(OrigDecl->getCanonicalDecl()))
+              .second) {
         Data.PrivateVars.push_back(*IRef);
         Data.PrivateCopies.push_back(IInit);
       }
@@ -5256,8 +5457,9 @@ void CodeGenFunction::EmitOMPTaskBasedDirective(
     auto IRef = C->varlist_begin();
     auto IElemInitRef = C->inits().begin();
     for (const Expr *IInit : C->private_copies()) {
-      const auto *OrigVD = cast<VarDecl>(cast<DeclRefExpr>(*IRef)->getDecl());
-      if (EmittedAsPrivate.insert(OrigVD->getCanonicalDecl()).second) {
+      const auto *OrigDecl = cast<DeclRefExpr>(*IRef)->getDecl();
+      if (EmittedAsPrivate.insert(cast<ValueDecl>(OrigDecl->getCanonicalDecl()))
+              .second) {
         Data.FirstprivateVars.push_back(*IRef);
         Data.FirstprivateCopies.push_back(IInit);
         Data.FirstprivateInits.push_back(*IElemInitRef);
@@ -5267,21 +5469,26 @@ void CodeGenFunction::EmitOMPTaskBasedDirective(
     }
   }
   // Get list of lastprivate variables (for taskloops).
-  llvm::MapVector<const VarDecl *, const DeclRefExpr *> LastprivateDstsOrigs;
+  llvm::MapVector<const ValueDecl *, const DeclRefExpr *> LastprivateDstsOrigs;
+  llvm::MapVector<const ValueDecl *, const DeclRefExpr *> LastprivateSrcsOrigs;
   for (const auto *C : S.getClausesOfKind<OMPLastprivateClause>()) {
     auto IRef = C->varlist_begin();
     auto ID = C->destination_exprs().begin();
+    auto IS = C->source_exprs().begin();
     for (const Expr *IInit : C->private_copies()) {
-      const auto *OrigVD = cast<VarDecl>(cast<DeclRefExpr>(*IRef)->getDecl());
-      if (EmittedAsPrivate.insert(OrigVD->getCanonicalDecl()).second) {
+      const auto *OrigDecl = cast<DeclRefExpr>(*IRef)->getDecl();
+      if (EmittedAsPrivate.insert(cast<ValueDecl>(OrigDecl->getCanonicalDecl()))
+              .second) {
         Data.LastprivateVars.push_back(*IRef);
         Data.LastprivateCopies.push_back(IInit);
       }
-      LastprivateDstsOrigs.insert(
-          std::make_pair(cast<VarDecl>(cast<DeclRefExpr>(*ID)->getDecl()),
-                         cast<DeclRefExpr>(*IRef)));
+      LastprivateDstsOrigs.insert(std::make_pair(
+          cast<DeclRefExpr>(*ID)->getDecl(), cast<DeclRefExpr>(*IRef)));
+      LastprivateSrcsOrigs.insert(std::make_pair(
+          cast<DeclRefExpr>(*IS)->getDecl(), cast<DeclRefExpr>(*IRef)));
       ++IRef;
       ++ID;
+      ++IS;
     }
   }
   SmallVector<const Expr *, 4> LHSs;
@@ -5307,8 +5514,8 @@ void CodeGenFunction::EmitOMPTaskBasedDirective(
                               Checker.getPrivateDecls().end());
   }
   auto &&CodeGen = [&Data, &S, CS, &BodyGen, &LastprivateDstsOrigs,
-                    CapturedRegion](CodeGenFunction &CGF,
-                                    PrePostActionTy &Action) {
+                    &LastprivateSrcsOrigs, CapturedRegion](
+                       CodeGenFunction &CGF, PrePostActionTy &Action) {
     llvm::MapVector<CanonicalDeclPtr<const VarDecl>,
                     std::pair<Address, Address>>
         UntiedLocalVars;
@@ -5374,7 +5581,8 @@ void CodeGenFunction::EmitOMPTaskBasedDirective(
         }
       }
     }
-    llvm::SmallVector<std::pair<const VarDecl *, Address>, 16> FirstprivatePtrs;
+    llvm::SmallVector<std::pair<const ValueDecl *, Address>, 16>
+        FirstprivatePtrs;
     if (!Data.PrivateVars.empty() || !Data.FirstprivateVars.empty() ||
         !Data.LastprivateVars.empty() || !Data.PrivateLocals.empty()) {
       enum { PrivatesParam = 2, CopyFnParam = 3 };
@@ -5383,13 +5591,13 @@ void CodeGenFunction::EmitOMPTaskBasedDirective(
       llvm::Value *PrivatesPtr = CGF.Builder.CreateLoad(CGF.GetAddrOfLocalVar(
           CS->getCapturedDecl()->getParam(PrivatesParam)));
       // Map privates.
-      llvm::SmallVector<std::pair<const VarDecl *, Address>, 16> PrivatePtrs;
+      llvm::SmallVector<std::pair<const ValueDecl *, Address>, 16> PrivatePtrs;
       llvm::SmallVector<llvm::Value *, 16> CallArgs;
       llvm::SmallVector<llvm::Type *, 4> ParamTypes;
       CallArgs.push_back(PrivatesPtr);
       ParamTypes.push_back(PrivatesPtr->getType());
       for (const Expr *E : Data.PrivateVars) {
-        const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl());
+        const auto *VD = cast<DeclRefExpr>(E)->getDecl();
         RawAddress PrivatePtr = CGF.CreateMemTempWithoutCast(
             CGF.getContext().getPointerType(E->getType()), ".priv.ptr.addr");
         PrivatePtrs.emplace_back(VD, PrivatePtr);
@@ -5397,7 +5605,7 @@ void CodeGenFunction::EmitOMPTaskBasedDirective(
         ParamTypes.push_back(PrivatePtr.getType());
       }
       for (const Expr *E : Data.FirstprivateVars) {
-        const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl());
+        const auto *VD = cast<DeclRefExpr>(E)->getDecl();
         RawAddress PrivatePtr = CGF.CreateMemTempWithoutCast(
             CGF.getContext().getPointerType(E->getType()),
             ".firstpriv.ptr.addr");
@@ -5407,7 +5615,7 @@ void CodeGenFunction::EmitOMPTaskBasedDirective(
         ParamTypes.push_back(PrivatePtr.getType());
       }
       for (const Expr *E : Data.LastprivateVars) {
-        const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl());
+        const auto *VD = cast<DeclRefExpr>(E)->getDecl();
         RawAddress PrivatePtr = CGF.CreateMemTempWithoutCast(
             CGF.getContext().getPointerType(E->getType()),
             ".lastpriv.ptr.addr");
@@ -5437,13 +5645,21 @@ void CodeGenFunction::EmitOMPTaskBasedDirective(
       CGF.CGM.getOpenMPRuntime().emitOutlinedFunctionCall(
           CGF, S.getBeginLoc(), {CopyFnTy, CopyFn}, CallArgs);
       for (const auto &Pair : LastprivateDstsOrigs) {
-        const auto *OrigVD = cast<VarDecl>(Pair.second->getDecl());
-        DeclRefExpr DRE(CGF.getContext(), const_cast<VarDecl *>(OrigVD),
-                        /*RefersToEnclosingVariableOrCapture=*/
-                        CGF.CapturedStmtInfo->lookup(OrigVD) != nullptr,
-                        Pair.second->getType(), VK_LValue,
-                        Pair.second->getExprLoc());
-        Scope.addPrivate(Pair.first, CGF.EmitLValue(&DRE).getAddress());
+        const auto *OrigDecl = Pair.second->getDecl();
+        if (const auto *BD = dyn_cast<BindingDecl>(OrigDecl)) {
+          // For BindingDecls, emit the binding's LValue directly.
+          Address OrigAddr =
+              CGF.EmitOMPBindingOriginalAddr(BD, Pair.second->getExprLoc());
+          Scope.addPrivate(Pair.first, OrigAddr);
+        } else {
+          const auto *OrigVD = cast<VarDecl>(OrigDecl);
+          DeclRefExpr DRE(CGF.getContext(), const_cast<VarDecl *>(OrigVD),
+                          /*RefersToEnclosingVariableOrCapture=*/
+                          CGF.CapturedStmtInfo->lookup(OrigVD) != nullptr,
+                          Pair.second->getType(), VK_LValue,
+                          Pair.second->getExprLoc());
+          Scope.addPrivate(Pair.first, CGF.EmitLValue(&DRE).getAddress());
+        }
       }
       for (const auto &Pair : PrivatePtrs) {
         Address Replacement = Address(
@@ -5451,11 +5667,25 @@ void CodeGenFunction::EmitOMPTaskBasedDirective(
             CGF.ConvertTypeForMem(Pair.first->getType().getNonReferenceType()),
             CGF.getContext().getDeclAlign(Pair.first));
         Scope.addPrivate(Pair.first, Replacement);
+
+        // For BindingDecls with lastprivate, also map the .lastprivate.src
+        // pseudo-variable to the same private address.
+        if (isa<BindingDecl>(Pair.first)) {
+          for (const auto &SrcPair : LastprivateSrcsOrigs) {
+            if (SrcPair.second->getDecl() == Pair.first) {
+              Scope.addPrivate(SrcPair.first, Replacement);
+              break;
+            }
+          }
+        }
+
         if (auto *DI = CGF.getDebugInfo())
           if (CGF.CGM.getCodeGenOpts().hasReducedDebugInfo())
-            (void)DI->EmitDeclareOfAutoVariable(
-                Pair.first, Pair.second.getBasePointer(), CGF.Builder,
-                /*UsePointerValue*/ true);
+            // Only emit debug info for VarDecls, not BindingDecls.
+            if (const auto *VD = dyn_cast<VarDecl>(Pair.first))
+              (void)DI->EmitDeclareOfAutoVariable(
+                  VD, Pair.second.getBasePointer(), CGF.Builder,
+                  /*UsePointerValue*/ true);
       }
       // Adjust mapping for internal locals by mapping actual memory instead of
       // a pointer to this memory.
@@ -5705,13 +5935,13 @@ void CodeGenFunction::EmitOMPTargetTaskBasedDirective(
       llvm::Value *PrivatesPtr = CGF.Builder.CreateLoad(CGF.GetAddrOfLocalVar(
           CS->getCapturedDecl()->getParam(PrivatesParam)));
       // Map privates.
-      llvm::SmallVector<std::pair<const VarDecl *, Address>, 16> PrivatePtrs;
+      llvm::SmallVector<std::pair<const ValueDecl *, Address>, 16> PrivatePtrs;
       llvm::SmallVector<llvm::Value *, 16> CallArgs;
       llvm::SmallVector<llvm::Type *, 4> ParamTypes;
       CallArgs.push_back(PrivatesPtr);
       ParamTypes.push_back(PrivatesPtr->getType());
       for (const Expr *E : Data.FirstprivateVars) {
-        const auto *VD = cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl());
+        const auto *VD = cast<DeclRefExpr>(E)->getDecl();
         RawAddress PrivatePtr = CGF.CreateMemTempWithoutCast(
             CGF.getContext().getPointerType(E->getType()),
             ".firstpriv.ptr.addr");

diff  --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index c9d06ef34907b..fe6c7c5deb7eb 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -1157,9 +1157,10 @@ class CodeGenFunction : public CodeGenTypeCache {
     /// Sets the address of the variable \p LocalVD to be \p TempAddr in
     /// function \p CGF.
     /// \return true if at least one variable was set already, false otherwise.
-    bool setVarAddr(CodeGenFunction &CGF, const VarDecl *LocalVD,
+    bool setVarAddr(CodeGenFunction &CGF, const ValueDecl *LocalVD,
                     Address TempAddr) {
-      LocalVD = LocalVD->getCanonicalDecl();
+      LocalVD = cast<ValueDecl>(LocalVD->getCanonicalDecl());
+
       // Only save it once.
       if (SavedLocals.count(LocalVD))
         return false;
@@ -1178,6 +1179,8 @@ class CodeGenFunction : public CodeGenTypeCache {
         CGF.Builder.CreateStore(TempAddr.emitRawPointer(CGF), Temp);
         TempAddr = Temp;
       }
+      if (const auto *BD = dyn_cast<BindingDecl>(LocalVD))
+        CGF.OMPPrivatizedBindings.insert_or_assign(BD, TempAddr);
       SavedTempAddresses.try_emplace(LocalVD, TempAddr);
 
       return true;
@@ -1220,6 +1223,7 @@ class CodeGenFunction : public CodeGenTypeCache {
     OMPMapVars MappedVars;
     OMPPrivateScope(const OMPPrivateScope &) = delete;
     void operator=(const OMPPrivateScope &) = delete;
+    llvm::DenseMap<const BindingDecl *, Address> BindingChanges;
 
   public:
     /// Enter a new OpenMP private scope.
@@ -1230,8 +1234,14 @@ class CodeGenFunction : public CodeGenTypeCache {
     /// PrivateGen is the address of the generated private variable.
     /// \return true if the variable is registered as private, false if it has
     /// been privatized already.
-    bool addPrivate(const VarDecl *LocalVD, Address Addr) {
+    bool addPrivate(const ValueDecl *LocalVD, Address Addr) {
       assert(PerformCleanup && "adding private to dead scope");
+      if (const auto *BD = dyn_cast<BindingDecl>(LocalVD->getCanonicalDecl())) {
+        auto It = CGF.OMPPrivatizedBindings.find(BD);
+        BindingChanges.insert({BD, It != CGF.OMPPrivatizedBindings.end()
+                                       ? It->second
+                                       : Address::invalid()});
+      }
       return MappedVars.setVarAddr(CGF, LocalVD, Addr);
     }
 
@@ -1254,6 +1264,17 @@ class CodeGenFunction : public CodeGenTypeCache {
     ~OMPPrivateScope() {
       if (PerformCleanup)
         ForceCleanup();
+      for (auto &Change : BindingChanges) {
+        if (Change.second.isValid()) {
+          auto It = CGF.OMPPrivatizedBindings.find(Change.first);
+          if (It != CGF.OMPPrivatizedBindings.end())
+            It->second = Change.second;
+          else
+            CGF.OMPPrivatizedBindings.insert({Change.first, Change.second});
+        } else {
+          CGF.OMPPrivatizedBindings.erase(Change.first);
+        }
+      }
     }
 
     /// Checks if the global variable is captured in current function.
@@ -1561,6 +1582,11 @@ class CodeGenFunction : public CodeGenTypeCache {
   /// decls.
   DeclMapTy LocalDeclMap;
 
+  /// Lookup map for privatized BindingDecls.
+  /// Used when BindingDecls are remapped during OpenMP outlining, since the
+  /// remapped BindingDecl has a 
diff erent pointer than the original.
+  llvm::SmallDenseMap<const BindingDecl *, Address> OMPPrivatizedBindings;
+
   // Keep track of the cleanups for callee-destructed parameters pushed to the
   // cleanup stack so that they can be deactivated later.
   llvm::DenseMap<const ParmVarDecl *, EHScopeStack::stable_iterator>
@@ -2244,6 +2270,17 @@ class CodeGenFunction : public CodeGenTypeCache {
 
   const TargetInfo &getTarget() const { return Target; }
   llvm::LLVMContext &getLLVMContext() { return CGM.getLLVMContext(); }
+
+  /// Accessors for LocalDeclMap.
+  DeclMapTy::iterator findLocalDecl(const Decl *D) {
+    return LocalDeclMap.find(D);
+  }
+  DeclMapTy::iterator localDeclMapEnd() { return LocalDeclMap.end(); }
+  std::pair<DeclMapTy::iterator, bool> insertLocalDecl(const Decl *D,
+                                                       Address Addr) {
+    return LocalDeclMap.insert({D, Addr});
+  }
+  void eraseLocalDecl(const Decl *D) { LocalDeclMap.erase(D); }
   const TargetCodeGenInfo &getTargetHooks() const {
     return CGM.getTargetCodeGenInfo();
   }
@@ -4154,6 +4191,9 @@ class CodeGenFunction : public CodeGenTypeCache {
   /// Emits the lvalue for the expression with possibly captured variable.
   LValue EmitOMPSharedLValue(const Expr *E);
 
+  /// Emits the original address for a structured binding.
+  Address EmitOMPBindingOriginalAddr(const BindingDecl *BD, SourceLocation Loc);
+
 private:
   /// Helpers for blocks.
   llvm::Value *EmitBlockLiteral(const CGBlockInfo &Info);
@@ -4499,6 +4539,7 @@ class CodeGenFunction : public CodeGenTypeCache {
   // Note: only available for agg return types
   LValue EmitVAArgExprLValue(const VAArgExpr *E);
   LValue EmitDeclRefLValue(const DeclRefExpr *E);
+  LValue EmitOMPCapturedBindingLValue(const BindingDecl *BD);
   LValue EmitStringLiteralLValue(const StringLiteral *E);
   LValue EmitObjCEncodeExprLValue(const ObjCEncodeExpr *E);
   LValue EmitPredefinedLValue(const PredefinedExpr *E);

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index c93e9fcd1dde6..be1dc9f85d4f7 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -19656,6 +19656,15 @@ static bool isVariableCapturable(CapturingScopeInfo *CSI, ValueDecl *Var,
   bool IsBlock = isa<BlockScopeInfo>(CSI);
   bool IsLambda = isa<LambdaScopeInfo>(CSI);
 
+  // Reject bindings referenced from a lambda or block that wraps an OpenMP
+  // region.
+  if ((IsLambda || IsBlock) && S.getLangOpts().OpenMP &&
+      isa<DecompositionDecl>(Var)) {
+    if (Diagnose)
+      S.Diag(Loc, diag::err_omp_unsupported_on_binding) << 3;
+    return false;
+  }
+
   // Lambdas are not allowed to capture unnamed variables
   // (e.g. anonymous unions).
   // FIXME: The C++11 rule don't actually state this explicitly, but I'm
@@ -19707,7 +19716,23 @@ static bool isVariableCapturable(CapturingScopeInfo *CSI, ValueDecl *Var,
     return false;
   }
 
-  if (isa<BindingDecl>(Var)) {
+  if (auto *BD = dyn_cast<BindingDecl>(Var)) {
+    if (auto *RSI = dyn_cast<CapturedRegionScopeInfo>(CSI)) {
+      if (RSI->CapRegionKind == CR_OpenMP) {
+        if (BD->getHoldingVar()) {
+          if (Diagnose) {
+            S.Diag(Loc, diag::err_capture_tuple_binding_openmp) << Var;
+            S.Diag(Var->getLocation(), diag::note_entity_declared_at) << Var;
+          }
+          return false;
+        }
+        if (Diagnose && S.getLangOpts().CPlusPlus) {
+          S.DiagCompat(Loc, diag_compat::capture_binding) << Var;
+          S.Diag(Var->getLocation(), diag::note_entity_declared_at) << Var;
+        }
+        return true;
+      }
+    }
     if (!IsLambda || !S.getLangOpts().CPlusPlus) {
       if (Diagnose)
         diagnoseUncapturableValueReferenceOrBinding(S, Loc, Var);
@@ -19797,23 +19822,46 @@ static bool captureInCapturedRegion(
     Sema &S, bool Invalid) {
   // By default, capture variables by reference.
   bool ByRef = true;
+  bool IsBindingDecl = isa<BindingDecl>(Var);
+  ValueDecl *DSAVar = Var;
   if (IsTopScope && Kind != TryCaptureKind::Implicit) {
     ByRef = (Kind == TryCaptureKind::ExplicitByRef);
   } else if (S.getLangOpts().OpenMP && RSI->CapRegionKind == CR_OpenMP) {
     // Using an LValue reference type is consistent with Lambdas (see below).
-    if (S.OpenMP().isOpenMPCapturedDecl(Var)) {
+    if (VarDecl *VD = S.OpenMP().isOpenMPCapturedDecl(Var)) {
+      Var = VD; // Capture the DecompositionDecl.
       bool HasConst = DeclRefType.isConstQualified();
+      // Note: DeclRefType should remain the BindingDecl's type (e.g., int),
+      // not the DecompositionDecl's type (e.g., Point). The variable being
+      // captured is the DecompositionDecl, but expressions still reference
+      // the individual binding's type.
       DeclRefType = DeclRefType.getUnqualifiedType();
       // Don't lose diagnostics about assignments to const.
       if (HasConst)
         DeclRefType.addConst();
     }
-    // Do not capture firstprivates in tasks.
-    if (S.OpenMP().isOpenMPPrivateDecl(Var, RSI->OpenMPLevel,
-                                       RSI->OpenMPCaptureLevel) != OMPC_unknown)
+    // Do not capture firstprivates in tasks. For bindings the DSA is on the
+    // binding, not on the DecompositionDecl; the task firstprivate path still
+    // needs the DecompositionDecl capture, so skip only private.
+    OpenMPClauseKind PrivateKind = S.OpenMP().isOpenMPPrivateDecl(
+        IsBindingDecl ? DSAVar : Var, RSI->OpenMPLevel,
+        RSI->OpenMPCaptureLevel);
+    if (IsBindingDecl ? PrivateKind == OMPC_private
+                      : PrivateKind != OMPC_unknown)
       return true;
-    ByRef = S.OpenMP().isOpenMPCapturedByRef(Var, RSI->OpenMPLevel,
+    ByRef = S.OpenMP().isOpenMPCapturedByRef(DSAVar, RSI->OpenMPLevel,
                                              RSI->OpenMPCaptureLevel);
+    // Bindings share the DecompositionDecl storage; a second capture with
+    // a 
diff erent capture kind is not representable.
+    if (BuildAndDiagnose && IsBindingDecl) {
+      unsigned Idx = RSI->CaptureMap.lookup(Var);
+      if (Idx != 0 && RSI->Captures[Idx - 1].isReferenceCapture() != ByRef) {
+        S.Diag(Loc,
+               diag::err_omp_decomposition_bindings_
diff erent_capture_kinds)
+            << DSAVar;
+        return false;
+      }
+    }
   }
 
   if (ByRef)
@@ -19826,6 +19874,11 @@ static bool captureInCapturedRegion(
     RSI->addCapture(Var, /*isBlock*/ false, ByRef, RefersToCapturedVariable,
                     Loc, SourceLocation(), CaptureType, Invalid);
 
+  if (BuildAndDiagnose && IsBindingDecl)
+    // Key the binding to its own capture entry so repeated uses hit the
+    // already-captured path.
+    RSI->CaptureMap[DSAVar] = RSI->Captures.size();
+
   return !Invalid;
 }
 
@@ -20212,14 +20265,6 @@ bool Sema::tryCaptureVariable(
         // just break here. Similarly, global variables that are captured in a
         // target region should not be captured outside the scope of the region.
         if (RSI->CapRegionKind == CR_OpenMP) {
-          // FIXME: We should support capturing structured bindings in OpenMP.
-          if (isa<BindingDecl>(Var)) {
-            if (BuildAndDiagnose) {
-              Diag(ExprLoc, diag::err_capture_binding_openmp) << Var;
-              Diag(Var->getLocation(), diag::note_entity_declared_at) << Var;
-            }
-            return true;
-          }
           OpenMPClauseKind IsOpenMPPrivateDecl = OpenMP().isOpenMPPrivateDecl(
               Var, RSI->OpenMPLevel, RSI->OpenMPCaptureLevel);
           // If the variable is private (i.e. not captured) and has variably

diff  --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 288f3c4f664cb..0c9a92cdd0c32 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1988,9 +1988,21 @@ ExprResult Sema::BuildCaptureInit(const Capture &Cap,
   } else {
     assert(Cap.isVariableCapture() && "unknown kind of capture");
     ValueDecl *Var = Cap.getVariable();
+    // For OpenMP structured bindings, capture the decomposed decl, not the
+    // binding.
+    auto *BD = dyn_cast<BindingDecl>(Var);
+    if (IsOpenMPMapping && BD)
+      // When capturing a BindingDecl in an OpenMP mapping context, we need to
+      // capture the DecompositionDecl instead. BindingDecls are references to
+      // storage owned by the DecompositionDecl.
+      // Example:
+      //   auto [a, b] = p;
+      //   auto lambda = [a]() { return a; };  // In OpenMP context.
+      // This is reached during lambda capture for OpenMP mappings.
+      Var = BD->getDecomposedDecl();
     Name = Var->getIdentifier();
     Init = BuildDeclarationNameExpr(
-      CXXScopeSpec(), DeclarationNameInfo(Var->getDeclName(), Loc), Var);
+        CXXScopeSpec(), DeclarationNameInfo(Var->getDeclName(), Loc), Var);
   }
 
   // In OpenMP, the capture kind doesn't actually describe how to capture:
@@ -2096,14 +2108,32 @@ bool Sema::DiagnoseUnusedLambdaCapture(SourceRange CaptureRange,
 
 /// Create a field within the lambda class or captured statement record for the
 /// given capture.
-FieldDecl *Sema::BuildCaptureField(RecordDecl *RD,
-                                   const sema::Capture &Capture) {
+FieldDecl *Sema::BuildCaptureField(RecordDecl *RD, const sema::Capture &Capture,
+                                   bool IsOpenMP) {
   SourceLocation Loc = Capture.getLocation();
   QualType FieldType = Capture.getCaptureType();
-
   TypeSourceInfo *TSI = nullptr;
   if (Capture.isVariableCapture()) {
-    const auto *Var = dyn_cast_or_null<VarDecl>(Capture.getVariable());
+    const VarDecl *Var = nullptr;
+    if (IsOpenMP) {
+      if (auto *BD = dyn_cast_or_null<BindingDecl>(Capture.getVariable())) {
+        Var = cast<VarDecl>(BD->getDecomposedDecl());
+        FieldType = Var->getType().getNonReferenceType();
+        if (Capture.isReferenceCapture())
+          FieldType = Context.getLValueReferenceType(FieldType);
+      } else if (auto *DD = dyn_cast_or_null<DecompositionDecl>(
+                     Capture.getVariable())) {
+        // OpenMP already transformed BindingDecl to DecompositionDecl
+        // before buildCapturedStmtCaptureList. Use the DecompositionDecl
+        // type.
+        Var = DD;
+        FieldType = DD->getType().getNonReferenceType();
+        if (Capture.isReferenceCapture())
+          FieldType = Context.getLValueReferenceType(DD->getType());
+      }
+    }
+    if (!Var)
+      Var = dyn_cast_or_null<VarDecl>(Capture.getVariable());
     if (Var && Var->isInitCapture())
       TSI = Var->getTypeSourceInfo();
   }

diff  --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 88b642b73895f..5825a441d3abb 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -24,6 +24,7 @@
 #include "clang/AST/DeclOpenMP.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/AST/OpenMPClause.h"
+#include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/StmtOpenMP.h"
 #include "clang/AST/StmtVisitor.h"
@@ -179,6 +180,8 @@ class DSAStackTy {
     UsedRefMapTy NontemporalMap;
     MappedExprComponentsTy MappedExprComponents;
     LoopControlVariablesMapTy LCVMap;
+    /// Track DecompositionDecls and their data-sharing attributes to detect
+    /// conflicting clauses on bindings from the same decomposition.
     DefaultDataSharingAttributes DefaultAttr = DSA_unspecified;
     SourceLocation DefaultAttrLoc;
     DefaultDataSharingVCAttributes DefaultVCAttr = DSA_VC_all;
@@ -1364,7 +1367,11 @@ DSAStackTy::DSAVarData DSAStackTy::getDSA(const_iterator &Iter,
     DVar.ImplicitDSALoc = Iter->DefaultAttrLoc;
     DVar.Modifier = Data.Modifier;
     DVar.AppliedToPointee = Data.AppliedToPointee;
-    return DVar;
+    // For BindingDecls with OMPC_unknown, fall through to implicit DSA logic
+    // instead of treating them as explicitly specified.
+    if (!(isa<BindingDecl>(D) && DVar.CKind == OMPC_unknown)) {
+      return DVar;
+    }
   }
 
   DefaultDataSharingAttributes IterDA = Iter->DefaultAttr;
@@ -2181,6 +2188,8 @@ bool SemaOpenMP::isOpenMPCapturedByRef(const ValueDecl *D, unsigned Level,
   // Find the directive that is associated with the provided scope.
   D = cast<ValueDecl>(D->getCanonicalDecl());
   QualType Ty = D->getType();
+  if (const auto *BD = dyn_cast<BindingDecl>(D))
+    Ty = BD->getDecomposedDecl()->getType();
 
   bool IsVariableUsedInMapClause = false;
   if (DSAStack->hasExplicitDirective(isOpenMPTargetExecutionDirective, Level)) {
@@ -2432,6 +2441,10 @@ VarDecl *SemaOpenMP::isOpenMPCapturedDecl(ValueDecl *D, bool CheckScopeInfo,
   assert(getLangOpts().OpenMP && "OpenMP is not allowed");
   D = getCanonicalDecl(D);
 
+  if (auto *BD = dyn_cast<BindingDecl>(D)) {
+    if (!BD->getHoldingVar())
+      D = cast<VarDecl>(BD->getDecomposedDecl());
+  }
   auto *VD = dyn_cast<VarDecl>(D);
   // Do not capture constexpr variables.
   if (VD && VD->isConstexpr())
@@ -2973,7 +2986,30 @@ void SemaOpenMP::EndOpenMPDSABlock(Stmt *CurDirective) {
         continue;
       }
       auto *DRE = cast<DeclRefExpr>(DE->IgnoreParens());
-      auto *VD = cast<VarDecl>(DRE->getDecl());
+      auto *D = DRE->getDecl();
+      if (auto *BD = dyn_cast<BindingDecl>(D)) {
+        QualType Type = BD->getType().getNonReferenceType();
+        const DSAStackTy::DSAVarData DVar =
+            DSAStack->getTopDSA(BD, /*FromParent=*/false);
+        if (DVar.CKind != OMPC_lastprivate) {
+          // The variable is also a firstprivate, so initialization sequence
+          // for private copy is generated already.
+          PrivateCopies.push_back(nullptr);
+          continue;
+        }
+        VarDecl *VDPrivate = buildVarDecl(
+            SemaRef, DE->getExprLoc(), Type.getUnqualifiedType(), BD->getName(),
+            BD->hasAttrs() ? &BD->getAttrs() : nullptr, DRE);
+        SemaRef.ActOnUninitializedDecl(VDPrivate);
+        if (VDPrivate->isInvalidDecl()) {
+          PrivateCopies.push_back(nullptr);
+          continue;
+        }
+        PrivateCopies.push_back(buildDeclRefExpr(
+            SemaRef, VDPrivate, DE->getType(), DE->getExprLoc()));
+        continue;
+      }
+      auto *VD = cast<VarDecl>(D);
       QualType Type = VD->getType().getNonReferenceType();
       const DSAStackTy::DSAVarData DVar =
           DSAStack->getTopDSA(VD, /*FromParent=*/false);
@@ -3867,6 +3903,212 @@ static void reportOriginalDsa(Sema &SemaRef, const DSAStackTy *Stack,
   }
 }
 
+namespace {
+/// Visitor to collect variables used in a statement.
+class VarUsageVisitor : public DynamicRecursiveASTVisitor {
+  llvm::SmallPtrSet<const VarDecl *, 8> &UsedVars;
+  llvm::SmallPtrSet<const BindingDecl *, 8> &UsedBindings;
+
+public:
+  VarUsageVisitor(llvm::SmallPtrSet<const VarDecl *, 8> &UsedVars,
+                  llvm::SmallPtrSet<const BindingDecl *, 8> &UsedBindings)
+      : UsedVars(UsedVars), UsedBindings(UsedBindings) {}
+
+  bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
+    if (auto *VD = dyn_cast<VarDecl>(DRE->getDecl()))
+      UsedVars.insert(cast<VarDecl>(VD->getCanonicalDecl()));
+    else if (auto *BD = dyn_cast<BindingDecl>(DRE->getDecl()))
+      UsedBindings.insert(cast<BindingDecl>(BD->getCanonicalDecl()));
+    return true;
+  }
+};
+} // namespace
+
+/// Check if bindings from the same structured binding have conflicting
+/// capture kinds (by-ref vs by-copy). Bindings share the underlying
+/// DecompositionDecl storage, so mixing by-ref and by-copy clauses on
+/// 
diff erent bindings from the same decomposition is not representable.
+/// For example: map(a) creates by-ref, firstprivate(b) creates by-copy.
+static bool checkDecompositionCaptureConflict(
+    Sema &SemaRef, OpenMPDirectiveKind DKind,
+    llvm::SmallDenseMap<const DecompositionDecl *,
+                        std::pair<bool, SourceLocation>, 4> &SeenDecompositions,
+    const ValueDecl *D, SourceLocation ELoc, OpenMPClauseKind ClauseKind) {
+  const auto *BD = dyn_cast<BindingDecl>(D);
+  if (!BD)
+    return false;
+
+  const auto *DD = dyn_cast<DecompositionDecl>(BD->getDecomposedDecl());
+  if (!DD)
+    return false;
+
+  // Determine if this clause creates by-ref or by-copy capture.
+  bool IsByRef = false;
+  switch (ClauseKind) {
+  case OMPC_map:
+  case OMPC_shared:
+    // Map and shared clauses are by-reference.
+    IsByRef = true;
+    break;
+  case OMPC_firstprivate:
+  case OMPC_private:
+  case OMPC_lastprivate:
+    // These are by-copy.
+    IsByRef = false;
+    break;
+  default:
+    // Other clauses don't create capture conflicts.
+    return false;
+  }
+  auto [It, Inserted] = SeenDecompositions.insert({DD, {IsByRef, ELoc}});
+  if (!Inserted && It->second.first != IsByRef) {
+    // Conflict: same DecompositionDecl needs both by-ref and by-copy
+    // Emit diagnostic showing the binding name, not the decomposition.
+    SemaRef.Diag(ELoc,
+                 diag::err_omp_decomposition_bindings_
diff erent_capture_kinds)
+        << BD;
+    return true;
+  }
+  return false;
+}
+
+/// Helper to check all clauses in a directive for structured binding
+/// capture conflicts. Returns true if an error was found. If Body is
+/// provided and the directive has default(shared), bindings used in the
+/// body but not listed in explicit clauses are treated as implicit
+/// shared (by-reference).
+static bool checkClausesForDecompositionConflicts(Sema &SemaRef,
+                                                  OpenMPDirectiveKind DKind,
+                                                  ArrayRef<OMPClause *> Clauses,
+                                                  Stmt *Body = nullptr) {
+  llvm::SmallDenseMap<const DecompositionDecl *,
+                      std::pair<bool, SourceLocation>, 4>
+      SeenDecompositions;
+  llvm::SmallPtrSet<const BindingDecl *, 8> ExplicitBindings;
+  bool HasError = false;
+  bool HasDefaultShared = false;
+  SourceLocation DefaultSharedLoc;
+  for (OMPClause *C : Clauses) {
+    if (auto *DC = dyn_cast<OMPDefaultClause>(C)) {
+      if (DC->getDefaultKind() == llvm::omp::DefaultKind::OMP_DEFAULT_shared) {
+        HasDefaultShared = true;
+        DefaultSharedLoc = DC->getBeginLoc();
+      }
+      continue;
+    }
+    OpenMPClauseKind CK = C->getClauseKind();
+    if (CK != OMPC_map && CK != OMPC_firstprivate && CK != OMPC_private &&
+        CK != OMPC_shared && CK != OMPC_lastprivate)
+      continue;
+    ArrayRef<Expr *> Varlist;
+    if (auto *MPC = dyn_cast<OMPMapClause>(C))
+      Varlist = MPC->varlist();
+    else if (auto *FPC = dyn_cast<OMPFirstprivateClause>(C))
+      Varlist = FPC->varlist();
+    else if (auto *PC = dyn_cast<OMPPrivateClause>(C))
+      Varlist = PC->varlist();
+    else if (auto *SC = dyn_cast<OMPSharedClause>(C))
+      Varlist = SC->varlist();
+    else if (auto *LPC = dyn_cast<OMPLastprivateClause>(C))
+      Varlist = LPC->varlist();
+
+    for (Expr *VE : Varlist) {
+      if (auto *DRE = dyn_cast<DeclRefExpr>(VE->IgnoreParenImpCasts())) {
+        ValueDecl *D = DRE->getDecl();
+        // Look through OMPCapturedExprDecl (used when clauses reference
+        // variables from an enclosing captured region) to find the
+        // underlying BindingDecl.
+        if (auto *CED = dyn_cast<OMPCapturedExprDecl>(D)) {
+          if (auto *InitDRE =
+                  dyn_cast<DeclRefExpr>(CED->getInit()->IgnoreParenImpCasts()))
+            D = InitDRE->getDecl();
+        }
+        if (auto *BD = dyn_cast<BindingDecl>(D))
+          ExplicitBindings.insert(cast<BindingDecl>(BD->getCanonicalDecl()));
+        if (checkDecompositionCaptureConflict(
+                SemaRef, DKind, SeenDecompositions, D, VE->getExprLoc(), CK))
+          HasError = true;
+      }
+    }
+  }
+
+  // With default(shared), bindings used in the body but not in an explicit
+  // clause become implicit shared (by-reference). Check those for conflicts
+  // with explicit by-copy clauses on the same decomposition.
+  if (HasDefaultShared && Body) {
+    llvm::SmallPtrSet<const VarDecl *, 8> UsedVars;
+    llvm::SmallPtrSet<const BindingDecl *, 8> UsedBindings;
+    VarUsageVisitor Visitor(UsedVars, UsedBindings);
+    Visitor.TraverseStmt(Body);
+    for (const BindingDecl *BD : UsedBindings) {
+      if (ExplicitBindings.contains(BD))
+        continue;
+      if (checkDecompositionCaptureConflict(SemaRef, DKind, SeenDecompositions,
+                                            BD, DefaultSharedLoc, OMPC_shared))
+        HasError = true;
+    }
+  }
+  return HasError;
+}
+
+/// Check if original variable is explicitly mapped but only bindings are used.
+/// Returns true if an error was found.
+static bool checkOriginalVarMappedButOnlyBindingsUsed(
+    Sema &SemaRef, ArrayRef<OMPClause *> Clauses, Stmt *Body) {
+  llvm::SmallDenseMap<const VarDecl *, SourceLocation, 4> MappedOrigVars;
+
+  for (OMPClause *C : Clauses) {
+    if (auto *MPC = dyn_cast<OMPMapClause>(C)) {
+      for (Expr *VE : MPC->varlist()) {
+        if (auto *DRE = dyn_cast<DeclRefExpr>(VE->IgnoreParenImpCasts())) {
+          if (auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
+            MappedOrigVars[VD->getCanonicalDecl()] = VE->getExprLoc();
+          }
+        }
+      }
+    }
+  }
+  if (MappedOrigVars.empty())
+    return false;
+
+  llvm::SmallPtrSet<const VarDecl *, 8> UsedVars;
+  llvm::SmallPtrSet<const BindingDecl *, 8> UsedBindings;
+  VarUsageVisitor Visitor(UsedVars, UsedBindings);
+  Visitor.TraverseStmt(Body);
+  bool HasError = false;
+  for (const auto &Entry : MappedOrigVars) {
+    const VarDecl *OrigVar = Entry.first;
+    SourceLocation Loc = Entry.second;
+
+    // Check if this original variable has bindings that are used.
+    // For reference bindings (`auto &[a, b] = p`), the bindings alias the
+    // original variable, so mapping `p` covers them correctly.
+    bool BindingsFromThisVarUsed = false;
+    for (const BindingDecl *BD : UsedBindings) {
+      if (auto *DD = dyn_cast<DecompositionDecl>(BD->getDecomposedDecl())) {
+        if (DD->getType()->isReferenceType())
+          continue;
+        if (auto *OrigFromDD = DD->getOriginalVar().Var) {
+          if (OrigFromDD->getCanonicalDecl() == OrigVar) {
+            BindingsFromThisVarUsed = true;
+            break;
+          }
+        }
+      }
+    }
+
+    // Error if: original is mapped, bindings are used, but original is not
+    // used.
+    if (BindingsFromThisVarUsed && !UsedVars.count(OrigVar)) {
+      SemaRef.Diag(Loc, diag::err_omp_original_var_mapped_bindings_only_used)
+          << OrigVar;
+      HasError = true;
+    }
+  }
+
+  return HasError;
+}
+
 static OpenMPMapClauseKind
 getMapClauseKindFromModifier(OpenMPDefaultmapClauseModifier M,
                              bool IsAggregateOrDeclareTarget,
@@ -3948,6 +4190,19 @@ static bool hasConstQualifiedMappingType(QualType T) {
 }
 
 namespace {
+/// Try to extract the original variable from a DecompositionDecl.
+/// If extraction fails, emit a diagnostic. Returns the original VarDecl* on
+/// success, nullptr on failure.
+static const VarDecl *getOriginalVarOrDiagnose(Sema &S,
+                                               const DecompositionDecl *DD,
+                                               SourceLocation Loc) {
+  auto Result = DD->getOriginalVar();
+  if (!Result.Var)
+    S.Diag(Loc, diag::err_omp_unsupported_structured_binding_init)
+        << Result.DiagKind;
+  return Result.Var;
+}
+
 struct VariableImplicitInfo {
   static const unsigned MapKindNum = OMPC_MAP_unknown;
   static const unsigned DefaultmapKindNum = OMPC_DEFAULTMAP_unknown + 1;
@@ -4019,14 +4274,21 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
         E->isInstantiationDependent() ||
         E->isNonOdrUse() == clang::NOUR_Unevaluated)
       return;
-    if (auto *VD = dyn_cast<VarDecl>(E->getDecl())) {
+    ValueDecl *D = const_cast<ValueDecl *>(E->getDecl());
+    BindingDecl *BD = dyn_cast<BindingDecl>(D);
+    if (BD)
+      D = BD->getDecomposedDecl();
+    if (auto *VD = dyn_cast<VarDecl>(D)) {
+      // For BindingDecls, use the original binding for DSA lookups;
+      // fall back to DecompositionDecl if lookup fails.
+      ValueDecl *LookupDecl = BD ? static_cast<ValueDecl *>(BD) : VD;
       // 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->getTopDSA(LookupDecl, /*FromParent=*/false).RefExpr &&
                   !Stack->isImplicitDefaultFirstprivateFD(VD))) {
         if (auto *CED = dyn_cast<OMPCapturedExprDecl>(VD))
           if (!CED->hasAttr<OMPCaptureNoInitAttr>()) {
-            Visit(CED->getInit());
+            Visit(const_cast<Expr *>(CED->getInit()));
             return;
           }
       } else if (VD->isImplicit() || isa<OMPCapturedExprDecl>(VD))
@@ -4035,6 +4297,29 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
         if (!Stack->isImplicitDefaultFirstprivateFD(VD))
           return;
       VD = VD->getCanonicalDecl();
+      // Skip DecompositionDecls (but not BindingDecls in DSA contexts) -
+      // they should be handled through explicit mapping of the original
+      // variable or as member expressions. When bindings are captured,
+      // the original variable is what needs to be mapped, not the
+      // decomposition itself. However, BindingDecls need DSA checking for
+      // default(none)/private/firstprivate contexts, but NOT in target
+      // offloading contexts (where map clause handles them).
+      if (isa<DecompositionDecl>(VD)) {
+        // For BindingDecls, continue checking only if:
+        // - We are in a DSA context (tasking/parallel/worksharing/teams), or
+        // - We are in a target context and the binding is scalar (implicit
+        // firstprivate).
+        bool InTargetAndScalar = BD &&
+                                 isOpenMPTargetExecutionDirective(DKind) &&
+                                 BD->getType()->isScalarType();
+        if (!BD ||
+            (isOpenMPTargetExecutionDirective(DKind) && !InTargetAndScalar) ||
+            (!InTargetAndScalar && !isImplicitOrExplicitTaskingRegion(DKind) &&
+             !isOpenMPParallelDirective(DKind) &&
+             !isOpenMPWorksharingDirective(DKind) &&
+             !isOpenMPTeamsDirective(DKind)))
+          return;
+      }
       // Skip internally declared variables.
       if (VD->hasLocalStorage() && CS && !CS->capturesVariable(VD) &&
           !Stack->isImplicitDefaultFirstprivateFD(VD) &&
@@ -4044,9 +4329,16 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
       if (Stack->isUsesAllocatorsDecl(VD))
         return;
 
-      DSAStackTy::DSAVarData DVar = Stack->getTopDSA(VD, /*FromParent=*/false);
+      DSAStackTy::DSAVarData DVar =
+          Stack->getTopDSA(LookupDecl, /*FromParent=*/false);
+      // If lookup on BindingDecl failed, try on DecompositionDecl.
+      if (BD && !DVar.RefExpr)
+        DVar = Stack->getTopDSA(VD, /*FromParent=*/false);
       // Check if the variable has explicit DSA set and stop analysis if it so.
-      if (DVar.RefExpr || !ImplicitDeclarations.insert(VD).second)
+      if (DVar.RefExpr ||
+          !ImplicitDeclarations
+               .insert(cast<ValueDecl>(LookupDecl->getCanonicalDecl()))
+               .second)
         return;
 
       // Skip internally declared static variables.
@@ -4074,11 +4366,14 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
         if (!InheritedDSA && (Stack->getDefaultDSA() == DSA_firstprivate ||
                               Stack->getDefaultDSA() == DSA_private)) {
           DSAStackTy::DSAVarData DVar =
-              Stack->getImplicitDSA(VD, /*FromParent=*/false);
+              Stack->getImplicitDSA(LookupDecl, /*FromParent=*/false);
+          if (BD && DVar.CKind == OMPC_unknown)
+            DVar = Stack->getImplicitDSA(VD, /*FromParent=*/false);
           InheritedDSA = DVar.CKind == OMPC_unknown;
         }
         if (InheritedDSA)
-          VarsWithInheritedDSA[VD] = E;
+          VarsWithInheritedDSA[cast<ValueDecl>(
+              LookupDecl->getCanonicalDecl())] = E;
         if (Stack->getDefaultDSA() == DSA_none)
           return;
       }
@@ -4091,8 +4386,10 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
       // data-sharing attribute clause (including a data-sharing attribute
       // clause on a combined construct where target. is one of the
       // constituent constructs), or an is_device_ptr clause.
-      OpenMPDefaultmapClauseKind ClauseKind =
-          getVariableCategoryFromDecl(SemaRef.getLangOpts(), VD);
+      // For BindingDecls, use the binding's type to determine category.
+      OpenMPDefaultmapClauseKind ClauseKind = getVariableCategoryFromDecl(
+          SemaRef.getLangOpts(), BD ? static_cast<const ValueDecl *>(BD)
+                                    : static_cast<const ValueDecl *>(VD));
       if (SemaRef.getLangOpts().OpenMP >= 50) {
         bool IsModifierNone = Stack->getDefaultmapModifier(ClauseKind) ==
                               OMPC_DEFAULTMAP_MODIFIER_none;
@@ -4110,7 +4407,8 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
                     auto ME = MapExprComponents.rend();
                     return MI != ME && MI->getAssociatedDeclaration() == VD;
                   })) {
-            VarsWithInheritedDSA[VD] = E;
+            VarsWithInheritedDSA[cast<ValueDecl>(
+                LookupDecl->getCanonicalDecl())] = E;
             return;
           }
         }
@@ -4129,34 +4427,51 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
 
       if (isOpenMPTargetExecutionDirective(DKind) &&
           !Stack->isLoopControlVariable(VD).first) {
-        if (!Stack->checkMappableExprComponentListsForDecl(
-                VD, /*CurrentRegionOnly=*/true,
-                [this](OMPClauseMappableExprCommon::MappableExprComponentListRef
-                           StackComponents,
-                       OpenMPClauseKind) {
+        // Check if VD is already mapped. For DecompositionDecls, also check if
+        // the original variable they decompose has been mapped (via BindingDecl
+        // map clauses).
+        bool AlreadyMapped = Stack->checkMappableExprComponentListsForDecl(
+            VD, /*CurrentRegionOnly=*/true, [this](auto StackComponents, auto) {
+              if (SemaRef.LangOpts.OpenMP >= 50)
+                return !StackComponents.empty();
+              // Variable is used if it has been marked as an array, array
+              // section, array shaping or the variable itself.
+              return StackComponents.size() == 1 ||
+                     llvm::all_of(
+                         llvm::drop_begin(llvm::reverse(StackComponents)),
+                         [](const auto &MC) {
+                           return MC.getAssociatedDeclaration() == nullptr &&
+                                  (isa<ArraySectionExpr>(
+                                       MC.getAssociatedExpression()) ||
+                                   isa<OMPArrayShapingExpr>(
+                                       MC.getAssociatedExpression()) ||
+                                   isa<ArraySubscriptExpr>(
+                                       MC.getAssociatedExpression()));
+                         });
+            });
+
+        // For DecompositionDecls, check if the original variable has been
+        // mapped.
+        const auto *DD = dyn_cast<DecompositionDecl>(VD);
+        if (!AlreadyMapped && DD) {
+          // Don't diagnose here. Just check if we can extract the original
+          // var. Diagnostics happen when processing explicit map clauses.
+          if (const VarDecl *OrigVar = DD->getOriginalVar().Var) {
+            AlreadyMapped = Stack->checkMappableExprComponentListsForDecl(
+                OrigVar, /*CurrentRegionOnly=*/true,
+                [this](auto StackComponents, auto) {
                   if (SemaRef.LangOpts.OpenMP >= 50)
                     return !StackComponents.empty();
-                  // Variable is used if it has been marked as an array, array
-                  // section, array shaping or the variable itself.
-                  return StackComponents.size() == 1 ||
-                         llvm::all_of(
-                             llvm::drop_begin(llvm::reverse(StackComponents)),
-                             [](const OMPClauseMappableExprCommon::
-                                    MappableComponent &MC) {
-                               return MC.getAssociatedDeclaration() ==
-                                          nullptr &&
-                                      (isa<ArraySectionExpr>(
-                                           MC.getAssociatedExpression()) ||
-                                       isa<OMPArrayShapingExpr>(
-                                           MC.getAssociatedExpression()) ||
-                                       isa<ArraySubscriptExpr>(
-                                           MC.getAssociatedExpression()));
-                             });
-                })) {
+                  return StackComponents.size() == 1;
+                });
+          }
+        }
+        if (!AlreadyMapped) {
           bool IsFirstprivate = false;
           // By default lambdas are captured as firstprivates.
+          QualType CheckType = BD ? BD->getType() : VD->getType();
           if (const auto *RD =
-                  VD->getType().getNonReferenceType()->getAsCXXRecordDecl())
+                  CheckType.getNonReferenceType()->getAsCXXRecordDecl())
             IsFirstprivate = RD->isLambda();
           IsFirstprivate =
               IsFirstprivate || (Stack->mustBeFirstprivate(ClauseKind) && !Res);
@@ -4183,7 +4498,7 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
       //  enclosing worksharing or parallel construct may not be accessed in an
       //  explicit task.
       DVar = Stack->hasInnermostDSA(
-          VD,
+          LookupDecl,
           [](OpenMPClauseKind C, bool AppliedToPointee) {
             return C == OMPC_reduction && !AppliedToPointee;
           },
@@ -4195,12 +4510,14 @@ class DSAAttrChecker final : public StmtVisitor<DSAAttrChecker, void> {
       if (isOpenMPTaskingDirective(DKind) && DVar.CKind == OMPC_reduction) {
         ErrorFound = true;
         SemaRef.Diag(ELoc, diag::err_omp_reduction_in_task);
-        reportOriginalDsa(SemaRef, Stack, VD, DVar);
+        reportOriginalDsa(SemaRef, Stack, BD ? LookupDecl : VD, DVar);
         return;
       }
 
       // Define implicit data-sharing attributes for task.
-      DVar = Stack->getImplicitDSA(VD, /*FromParent=*/false);
+      DVar = Stack->getImplicitDSA(LookupDecl, /*FromParent=*/false);
+      if (BD && DVar.CKind == OMPC_unknown)
+        DVar = Stack->getImplicitDSA(VD, /*FromParent=*/false);
       if (((isOpenMPTaskingDirective(DKind) && DVar.CKind != OMPC_shared) ||
            (((Stack->getDefaultDSA() == DSA_firstprivate &&
               DVar.CKind == OMPC_firstprivate) ||
@@ -4697,12 +5014,21 @@ static OMPCapturedExprDecl *buildCaptureDecl(Sema &S, IdentifierInfo *Id,
 static DeclRefExpr *buildCapture(Sema &S, ValueDecl *D, Expr *CaptureExpr,
                                  bool WithInit) {
   OMPCapturedExprDecl *CD;
-  if (VarDecl *VD = S.OpenMP().isOpenMPCapturedDecl(D))
-    CD = cast<OMPCapturedExprDecl>(VD);
-  else
+  // For BindingDecls, always create a new capture instead of reusing the
+  // decomposed decl, since the decomposed decl is a regular VarDecl, not an
+  // OMPCapturedExprDecl.
+  if (!isa<BindingDecl>(D)) {
+    if (VarDecl *VD = S.OpenMP().isOpenMPCapturedDecl(D))
+      CD = cast<OMPCapturedExprDecl>(VD);
+    else
+      CD = buildCaptureDecl(S, D->getIdentifier(), CaptureExpr, WithInit,
+                            S.CurContext,
+                            /*AsExpression=*/false);
+  } else {
     CD = buildCaptureDecl(S, D->getIdentifier(), CaptureExpr, WithInit,
                           S.CurContext,
                           /*AsExpression=*/false);
+  }
   return buildDeclRefExpr(S, CD, CD->getType().getNonReferenceType(),
                           CaptureExpr->getExprLoc());
 }
@@ -5454,7 +5780,7 @@ getPrivateItem(Sema &S, Expr *&RefExpr, SourceLocation &ELoc,
   RefExpr = RefExpr->IgnoreParenImpCasts();
   auto *DE = dyn_cast_or_null<DeclRefExpr>(RefExpr);
   auto *ME = dyn_cast_or_null<MemberExpr>(RefExpr);
-  if ((!DE || !isa<VarDecl>(DE->getDecl())) &&
+  if ((!DE || !isa<VarDecl, BindingDecl>(DE->getDecl())) &&
       (S.getCurrentThisType().isNull() || !ME ||
        !isa<CXXThisExpr>(ME->getBase()->IgnoreParenImpCasts()) ||
        !isa<FieldDecl>(ME->getMemberDecl()))) {
@@ -8033,6 +8359,11 @@ SemaOpenMP::ActOnOpenMPParallelDirective(ArrayRef<OMPClause *> Clauses,
   if (!AStmt)
     return StmtError();
 
+  // Check for conflicting capture kinds on structured bindings.
+  if (checkClausesForDecompositionConflicts(SemaRef, OMPD_parallel, Clauses,
+                                            AStmt))
+    return StmtError();
+
   setBranchProtectedScope(SemaRef, OMPD_parallel, AStmt);
 
   return OMPParallelDirective::Create(
@@ -8570,6 +8901,19 @@ bool OpenMPIterationSpaceChecker::checkAndSetInit(Stmt *S, bool EmitDiags) {
     }
   }
 
+  // Helper lambda to reject structured bindings used as OpenMP loop
+  // control variables. Loop counters are implicitly private, but bindings
+  // share storage with their decomposition, so this is not representable.
+  auto CheckBindingAsLoopVar = [&](ValueDecl *LoopVar,
+                                   SourceLocation Loc) -> bool {
+    if (!isa<BindingDecl>(LoopVar))
+      return false;
+    if (EmitDiags)
+      SemaRef.Diag(Loc, diag::err_omp_loop_var_is_structured_binding)
+          << LoopVar;
+    return true;
+  };
+
   // Helper lambda to check if a loop variable is already used in an outer
   // loop.
   auto CheckLoopVarReuse = [&](ValueDecl *LoopVar, SourceLocation Loc) -> bool {
@@ -8598,6 +8942,8 @@ bool OpenMPIterationSpaceChecker::checkAndSetInit(Stmt *S, bool EmitDiags) {
             return setLCDeclAndLB(LoopVar, ME, BO->getRHS(), EmitDiags);
           }
         ValueDecl *LoopVar = DRE->getDecl();
+        if (CheckBindingAsLoopVar(LoopVar, DRE->getLocation()))
+          return true;
         if (CheckLoopVarReuse(LoopVar, DRE->getLocation()))
           return true;
         return setLCDeclAndLB(LoopVar, DRE, BO->getRHS(), EmitDiags);
@@ -8645,6 +8991,8 @@ bool OpenMPIterationSpaceChecker::checkAndSetInit(Stmt *S, bool EmitDiags) {
             return setLCDeclAndLB(LoopVar, ME, CE->getArg(1), EmitDiags);
           }
         ValueDecl *LoopVar = DRE->getDecl();
+        if (CheckBindingAsLoopVar(LoopVar, DRE->getLocation()))
+          return true;
         if (CheckLoopVarReuse(LoopVar, DRE->getLocation()))
           return true;
         return setLCDeclAndLB(LoopVar, DRE, CE->getArg(1), EmitDiags);
@@ -11358,6 +11706,9 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsGenericLoopDirective(
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(SemaRef, OMPD_teams_loop, Clauses))
+    return StmtError();
+
   // OpenMP 5.1 [2.11.7, loop construct, Restrictions]
   // A list item may not appear in a lastprivate clause unless it is the
   // loop iteration variable of a loop that is associated with the construct.
@@ -11400,6 +11751,13 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsGenericLoopDirective(
                                   DSAStack))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(SemaRef, OMPD_target_teams_loop,
+                                            Clauses))
+    return StmtError();
+
+  if (checkOriginalVarMappedButOnlyBindingsUsed(SemaRef, Clauses, AStmt))
+    return StmtError();
+
   CapturedStmt *CS =
       setBranchProtectedScope(SemaRef, OMPD_target_teams_loop, AStmt);
 
@@ -11461,6 +11819,13 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetParallelGenericLoopDirective(
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(SemaRef, OMPD_target_parallel_loop,
+                                            Clauses))
+    return StmtError();
+
+  if (checkOriginalVarMappedButOnlyBindingsUsed(SemaRef, Clauses, AStmt))
+    return StmtError();
+
   // OpenMP 5.1 [2.11.7, loop construct, Restrictions]
   // A list item may not appear in a lastprivate clause unless it is the
   // loop iteration variable of a loop that is associated with the construct.
@@ -11731,6 +12096,10 @@ StmtResult SemaOpenMP::ActOnOpenMPTaskDirective(ArrayRef<OMPClause *> Clauses,
                                     {OMPC_detach, OMPC_mergeable}))
     return StmtError();
 
+  // Check for conflicting capture kinds on structured bindings.
+  if (checkClausesForDecompositionConflicts(SemaRef, OMPD_task, Clauses, AStmt))
+    return StmtError();
+
   setBranchProtectedScope(SemaRef, OMPD_task, AStmt);
 
   return OMPTaskDirective::Create(getASTContext(), StartLoc, EndLoc, Clauses,
@@ -13711,6 +14080,13 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetDirective(ArrayRef<OMPClause *> Clauses,
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  // Check for conflicting capture kinds on structured bindings.
+  if (checkClausesForDecompositionConflicts(SemaRef, OMPD_target, Clauses))
+    return StmtError();
+
+  if (checkOriginalVarMappedButOnlyBindingsUsed(SemaRef, Clauses, AStmt))
+    return StmtError();
+
   CapturedStmt *CS = setBranchProtectedScope(SemaRef, OMPD_target, AStmt);
 
   // OpenMP [2.16, Nesting of Regions]
@@ -13765,6 +14141,14 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetParallelDirective(
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  // Check for conflicting capture kinds on structured bindings.
+  if (checkClausesForDecompositionConflicts(SemaRef, OMPD_target_parallel,
+                                            Clauses))
+    return StmtError();
+
+  if (checkOriginalVarMappedButOnlyBindingsUsed(SemaRef, Clauses, AStmt))
+    return StmtError();
+
   setBranchProtectedScope(SemaRef, OMPD_target_parallel, AStmt);
 
   return OMPTargetParallelDirective::Create(
@@ -13781,6 +14165,13 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetParallelForDirective(
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(SemaRef, OMPD_target_parallel_for,
+                                            Clauses))
+    return StmtError();
+
+  if (checkOriginalVarMappedButOnlyBindingsUsed(SemaRef, Clauses, AStmt))
+    return StmtError();
+
   CapturedStmt *CS =
       setBranchProtectedScope(SemaRef, OMPD_target_parallel_for, AStmt);
 
@@ -13942,6 +14333,9 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDirective(ArrayRef<OMPClause *> Clauses,
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(SemaRef, OMPD_teams, Clauses))
+    return StmtError();
+
   // Report affected OpenMP target offloading behavior when in HIP lang-mode.
   if (getLangOpts().HIP && (DSAStack->getParentDirective() == OMPD_target))
     Diag(StartLoc, diag::warn_hip_omp_target_directives);
@@ -14527,6 +14921,13 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetParallelForSimdDirective(
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(
+          SemaRef, OMPD_target_parallel_for_simd, Clauses))
+    return StmtError();
+
+  if (checkOriginalVarMappedButOnlyBindingsUsed(SemaRef, Clauses, AStmt))
+    return StmtError();
+
   CapturedStmt *CS =
       setBranchProtectedScope(SemaRef, OMPD_target_parallel_for_simd, AStmt);
 
@@ -14559,6 +14960,12 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetSimdDirective(
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(SemaRef, OMPD_target_simd, Clauses))
+    return StmtError();
+
+  if (checkOriginalVarMappedButOnlyBindingsUsed(SemaRef, Clauses, AStmt))
+    return StmtError();
+
   CapturedStmt *CS = setBranchProtectedScope(SemaRef, OMPD_target_simd, AStmt);
 
   OMPLoopBasedDirective::HelperExprs B;
@@ -14590,6 +14997,10 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeDirective(
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(SemaRef, OMPD_teams_distribute,
+                                            Clauses))
+    return StmtError();
+
   CapturedStmt *CS =
       setBranchProtectedScope(SemaRef, OMPD_teams_distribute, AStmt);
 
@@ -14620,6 +15031,9 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeSimdDirective(
 
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
+  if (checkClausesForDecompositionConflicts(SemaRef, OMPD_teams_distribute_simd,
+                                            Clauses))
+    return StmtError();
 
   CapturedStmt *CS =
       setBranchProtectedScope(SemaRef, OMPD_teams_distribute_simd, AStmt);
@@ -14655,6 +15069,10 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeParallelForSimdDirective(
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(
+          SemaRef, OMPD_teams_distribute_parallel_for_simd, Clauses))
+    return StmtError();
+
   CapturedStmt *CS = setBranchProtectedScope(
       SemaRef, OMPD_teams_distribute_parallel_for_simd, AStmt);
 
@@ -14689,6 +15107,10 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDistributeParallelForDirective(
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(
+          SemaRef, OMPD_teams_distribute_parallel_for, Clauses))
+    return StmtError();
+
   CapturedStmt *CS = setBranchProtectedScope(
       SemaRef, OMPD_teams_distribute_parallel_for, AStmt);
 
@@ -14719,6 +15141,13 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDirective(
   if (!AStmt)
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(SemaRef, OMPD_target_teams,
+                                            Clauses))
+    return StmtError();
+
+  if (checkOriginalVarMappedButOnlyBindingsUsed(SemaRef, Clauses, AStmt))
+    return StmtError();
+
   setBranchProtectedScope(SemaRef, OMPD_target_teams, AStmt);
 
   if (validateMultidimClauses(*this, Clauses, /*MayHaveBareClause=*/true))
@@ -14737,6 +15166,13 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeDirective(
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(
+          SemaRef, OMPD_target_teams_distribute, Clauses))
+    return StmtError();
+
+  if (checkOriginalVarMappedButOnlyBindingsUsed(SemaRef, Clauses, AStmt))
+    return StmtError();
+
   CapturedStmt *CS =
       setBranchProtectedScope(SemaRef, OMPD_target_teams_distribute, AStmt);
 
@@ -14766,6 +15202,13 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeParallelForDirective(
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(
+          SemaRef, OMPD_target_teams_distribute_parallel_for, Clauses))
+    return StmtError();
+
+  if (checkOriginalVarMappedButOnlyBindingsUsed(SemaRef, Clauses, AStmt))
+    return StmtError();
+
   CapturedStmt *CS = setBranchProtectedScope(
       SemaRef, OMPD_target_teams_distribute_parallel_for, AStmt);
 
@@ -14796,6 +15239,13 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeParallelForSimdDirective(
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(
+          SemaRef, OMPD_target_teams_distribute_parallel_for_simd, Clauses))
+    return StmtError();
+
+  if (checkOriginalVarMappedButOnlyBindingsUsed(SemaRef, Clauses, AStmt))
+    return StmtError();
+
   CapturedStmt *CS = setBranchProtectedScope(
       SemaRef, OMPD_target_teams_distribute_parallel_for_simd, AStmt);
 
@@ -14829,6 +15279,13 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeSimdDirective(
   if (validateMultidimClauses(*this, Clauses))
     return StmtError();
 
+  if (checkClausesForDecompositionConflicts(
+          SemaRef, OMPD_target_teams_distribute_simd, Clauses))
+    return StmtError();
+
+  if (checkOriginalVarMappedButOnlyBindingsUsed(SemaRef, Clauses, AStmt))
+    return StmtError();
+
   CapturedStmt *CS = setBranchProtectedScope(
       SemaRef, OMPD_target_teams_distribute_simd, AStmt);
 
@@ -19942,7 +20399,8 @@ OMPClause *SemaOpenMP::ActOnOpenMPPrivateClause(ArrayRef<Expr *> VarList,
         SemaRef, VDPrivate, RefExpr->getType().getUnqualifiedType(), ELoc);
 
     DeclRefExpr *Ref = nullptr;
-    if (!VD && !SemaRef.CurContext->isDependentContext()) {
+    bool IsBindingDecl = isa<BindingDecl>(D);
+    if (!VD && !IsBindingDecl && !SemaRef.CurContext->isDependentContext()) {
       auto *FD = dyn_cast<FieldDecl>(D);
       VarDecl *VD = FD ? DSAStack->getImplicitFDCapExprDecl(FD) : nullptr;
       if (VD)
@@ -19951,11 +20409,13 @@ OMPClause *SemaOpenMP::ActOnOpenMPPrivateClause(ArrayRef<Expr *> VarList,
       else
         Ref = buildCapture(SemaRef, D, SimpleRefExpr, /*WithInit=*/false);
     }
-    if (!IsImplicitClause)
+    if (!IsImplicitClause) {
       DSAStack->addDSA(D, RefExpr->IgnoreParens(), OMPC_private, Ref);
-    Vars.push_back((VD || SemaRef.CurContext->isDependentContext())
-                       ? RefExpr->IgnoreParens()
-                       : Ref);
+    }
+    Vars.push_back(
+        (VD || IsBindingDecl || SemaRef.CurContext->isDependentContext())
+            ? RefExpr->IgnoreParens()
+            : Ref);
     PrivateCopies.push_back(VDPrivateRefExpr);
   }
 
@@ -20172,19 +20632,54 @@ OMPClause *SemaOpenMP::ActOnOpenMPFirstprivateClause(ArrayRef<Expr *> VarList,
     }
 
     Type = Type.getUnqualifiedType();
-    VarDecl *VDPrivate =
-        buildVarDecl(SemaRef, ELoc, Type, D->getName(),
-                     D->hasAttrs() ? &D->getAttrs() : nullptr,
-                     VD ? cast<DeclRefExpr>(SimpleRefExpr) : nullptr);
+
+    // For BindingDecls, use the DecompositionDecl's type so all bindings from
+    // the same decomposition are accessible in the privatized region. We do
+    // this after conflict checking to preserve diagnostic errors. Don't modify
+    // RefExpr/SimpleRefExpr to keep diagnostics working.
+    bool IsBindingDecl = isa<BindingDecl>(D);
+    VarDecl *PrivateVD = VD;
+    QualType PrivateType = Type;
+    if (IsBindingDecl) {
+      const auto *BD = cast<BindingDecl>(D);
+      PrivateVD = cast<VarDecl>(BD->getDecomposedDecl());
+      // PrivateType stays as the BindingDecl's type (Type), not the
+      // DecompositionDecl's type.
+    }
+
+    // For regular arrays, pass the original var ref. For BindingDecls, don't
+    // pass the DecompositionDecl ref since VDPrivate has the binding's type,
+    // not the decomposition type.
+    VarDecl *VDPrivate = buildVarDecl(SemaRef, ELoc, PrivateType, D->getName(),
+                                      D->hasAttrs() ? &D->getAttrs() : nullptr,
+                                      (PrivateVD && !IsBindingDecl)
+                                          ? cast<DeclRefExpr>(SimpleRefExpr)
+                                          : nullptr);
+
     // Generate helper private variable and initialize it with the value of the
     // original variable. The address of the original variable is replaced by
     // the address of the new private variable in the CodeGen. This new variable
     // is not added to IdResolver, so the code in the OpenMP region uses
     // original variable for proper diagnostics and variable capturing.
     Expr *VDInitRefExpr = nullptr;
-    // For arrays generate initializer for single element and replace it by the
-    // original array element in CodeGen.
-    if (Type->isArrayType()) {
+
+    // For BindingDecls, VDPrivate should have the binding's type (not the
+    // DecompositionDecl's type), and will be initialized from the binding's
+    // field in the original DecompositionDecl.
+    if (IsBindingDecl && !PrivateType->isArrayType()) {
+      // For non-array bindings, create a simple copy initialization.
+      VarDecl *VDInit = buildVarDecl(SemaRef, RefExpr->getExprLoc(),
+                                     PrivateType, ".firstprivate.temp");
+      VDInitRefExpr =
+          buildDeclRefExpr(SemaRef, VDInit, PrivateType, RefExpr->getExprLoc());
+
+      // Initialize VDPrivate from VDInit (which will point to the field).
+      SemaRef.AddInitializerToDecl(
+          VDPrivate, SemaRef.DefaultLvalueConversion(VDInitRefExpr).get(),
+          /*DirectInit=*/false);
+    } else if (Type->isArrayType()) {
+      // For arrays generate initializer for single element and replace it by
+      // the original array element in CodeGen.
       VarDecl *VDInit =
           buildVarDecl(SemaRef, RefExpr->getExprLoc(), ElemType, D->getName());
       VDInitRefExpr = buildDeclRefExpr(SemaRef, VDInit, ElemType, ELoc);
@@ -20228,7 +20723,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPFirstprivateClause(ArrayRef<Expr *> VarList,
     if (!VD && !SemaRef.CurContext->isDependentContext()) {
       if (TopDVar.CKind == OMPC_lastprivate) {
         Ref = TopDVar.PrivateCopy;
-      } else {
+      } else if (!IsBindingDecl) {
         auto *FD = dyn_cast<FieldDecl>(D);
         VarDecl *VD = FD ? DSAStack->getImplicitFDCapExprDecl(FD) : nullptr;
         if (VD)
@@ -20243,9 +20738,10 @@ OMPClause *SemaOpenMP::ActOnOpenMPFirstprivateClause(ArrayRef<Expr *> VarList,
     }
     if (!IsImplicitClause)
       DSAStack->addDSA(D, RefExpr->IgnoreParens(), OMPC_firstprivate, Ref);
-    Vars.push_back((VD || SemaRef.CurContext->isDependentContext())
-                       ? RefExpr->IgnoreParens()
-                       : Ref);
+    Vars.push_back(
+        (VD || IsBindingDecl || SemaRef.CurContext->isDependentContext())
+            ? RefExpr->IgnoreParens()
+            : Ref);
     PrivateCopies.push_back(VDPrivateRefExpr);
     Inits.push_back(VDInitRefExpr);
   }
@@ -20297,6 +20793,14 @@ OMPClause *SemaOpenMP::ActOnOpenMPLastprivateClause(
     QualType Type = D->getType();
     auto *VD = dyn_cast<VarDecl>(D);
 
+    // Structured bindings with conditional modifier are currently not
+    // supported.
+    if (LPKind == OMPC_LASTPRIVATE_conditional && isa<BindingDecl>(D)) {
+      Diag(ELoc, diag::err_omp_unsupported_on_binding) << 1;
+      Diag(D->getLocation(), diag::note_defined_here) << D;
+      continue;
+    }
+
     // OpenMP [2.14.3.5, Restrictions, C/C++, p.2]
     //  A variable that appears in a lastprivate clause must not have an
     //  incomplete type or a reference type.
@@ -20408,9 +20912,10 @@ OMPClause *SemaOpenMP::ActOnOpenMPLastprivateClause(
         if (!isOpenMPCapturedDecl(D))
           ExprCaptures.push_back(Ref->getDecl());
       }
-      if ((TopDVar.CKind == OMPC_firstprivate && !TopDVar.PrivateCopy) ||
-          (!isOpenMPCapturedDecl(D) &&
-           Ref->getDecl()->hasAttr<OMPCaptureNoInitAttr>())) {
+      if (Ref &&
+          ((TopDVar.CKind == OMPC_firstprivate && !TopDVar.PrivateCopy) ||
+           (!isOpenMPCapturedDecl(D) &&
+            Ref->getDecl()->hasAttr<OMPCaptureNoInitAttr>()))) {
         ExprResult RefRes = SemaRef.DefaultLvalueConversion(Ref);
         if (!RefRes.isUsable())
           continue;
@@ -20424,9 +20929,11 @@ OMPClause *SemaOpenMP::ActOnOpenMPLastprivateClause(
       }
     }
     DSAStack->addDSA(D, RefExpr->IgnoreParens(), OMPC_lastprivate, Ref);
-    Vars.push_back((VD || SemaRef.CurContext->isDependentContext())
-                       ? RefExpr->IgnoreParens()
-                       : Ref);
+    bool IsBindingDecl = isa<BindingDecl>(D);
+    Vars.push_back(
+        (VD || IsBindingDecl || SemaRef.CurContext->isDependentContext())
+            ? RefExpr->IgnoreParens()
+            : Ref);
     SrcExprs.push_back(PseudoSrcExpr);
     DstExprs.push_back(PseudoDstExpr);
     AssignmentOps.push_back(AssignmentOp.get());
@@ -21116,6 +21623,13 @@ static bool actOnOMPReductionKindClause(
     }
     auto *VD = dyn_cast<VarDecl>(D);
 
+    auto *BD = dyn_cast<BindingDecl>(D);
+    if (BD) {
+      // FIXME: reductions on bindings are rejected.
+      S.Diag(ELoc, diag::err_omp_unsupported_on_binding) << 0;
+      continue;
+    }
+
     // OpenMP [2.9.3.3, Restrictions, C/C++, p.3]
     //  A variable that appears in a private clause must not have an incomplete
     //  type or a reference type.
@@ -21672,7 +22186,8 @@ static bool actOnOMPReductionKindClause(
 
     DeclRefExpr *Ref = nullptr;
     Expr *VarsExpr = RefExpr->IgnoreParens();
-    if (!VD && !S.CurContext->isDependentContext()) {
+    bool IsBindingDecl = isa<BindingDecl>(D);
+    if (!VD && !IsBindingDecl && !S.CurContext->isDependentContext()) {
       if (ASE || OASE) {
         TransformExprToCaptures RebuildToCapture(S, D);
         VarsExpr =
@@ -21910,6 +22425,15 @@ OMPClause *SemaOpenMP::ActOnOpenMPLinearClause(
     if (!D)
       continue;
 
+    // Linear on bindings only works for simple simd, not parallel constructs.
+    if (isa<BindingDecl>(D)) {
+      OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
+      if (isOpenMPParallelDirective(DKind)) {
+        Diag(ELoc, diag::err_omp_unsupported_on_binding) << 2;
+        continue;
+      }
+    }
+
     QualType Type = D->getType();
     auto *VD = dyn_cast<VarDecl>(D);
 
@@ -21940,7 +22464,8 @@ OMPClause *SemaOpenMP::ActOnOpenMPLinearClause(
     VarDecl *Init = buildVarDecl(SemaRef, ELoc, Type, ".linear.start");
     Expr *InitExpr;
     DeclRefExpr *Ref = nullptr;
-    if (!VD && !SemaRef.CurContext->isDependentContext()) {
+    bool IsBindingDecl = isa<BindingDecl>(D);
+    if (!VD && !IsBindingDecl && !SemaRef.CurContext->isDependentContext()) {
       Ref = buildCapture(SemaRef, D, SimpleRefExpr, /*WithInit=*/false);
       if (!isOpenMPCapturedDecl(D)) {
         ExprCaptures.push_back(Ref->getDecl());
@@ -21961,16 +22486,17 @@ OMPClause *SemaOpenMP::ActOnOpenMPLinearClause(
     if (LinKind == OMPC_LINEAR_uval)
       InitExpr = VD ? VD->getInit() : SimpleRefExpr;
     else
-      InitExpr = VD ? SimpleRefExpr : Ref;
+      InitExpr = (VD || IsBindingDecl) ? SimpleRefExpr : Ref;
     SemaRef.AddInitializerToDecl(
         Init, SemaRef.DefaultLvalueConversion(InitExpr).get(),
         /*DirectInit=*/false);
     DeclRefExpr *InitRef = buildDeclRefExpr(SemaRef, Init, Type, ELoc);
 
     DSAStack->addDSA(D, RefExpr->IgnoreParens(), OMPC_linear, Ref);
-    Vars.push_back((VD || SemaRef.CurContext->isDependentContext())
-                       ? RefExpr->IgnoreParens()
-                       : Ref);
+    Vars.push_back(
+        (VD || IsBindingDecl || SemaRef.CurContext->isDependentContext())
+            ? RefExpr->IgnoreParens()
+            : Ref);
     Privates.push_back(PrivateRef);
     Inits.push_back(InitRef);
   }
@@ -22069,13 +22595,16 @@ static bool FinishOpenMPLinearClause(OMPLinearClause &Clause, DeclRefExpr *IV,
     // Build privatized reference to the current linear var.
     auto *DE = cast<DeclRefExpr>(SimpleRefExpr);
     Expr *CapturedRef;
-    if (LinKind == OMPC_LINEAR_uval)
+    if (isa<BindingDecl>(DE->getDecl())) {
+      CapturedRef = SimpleRefExpr;
+    } else if (LinKind == OMPC_LINEAR_uval) {
       CapturedRef = cast<VarDecl>(DE->getDecl())->getInit();
-    else
+    } else {
       CapturedRef =
           buildDeclRefExpr(SemaRef, cast<VarDecl>(DE->getDecl()),
                            DE->getType().getUnqualifiedType(), DE->getExprLoc(),
                            /*RefersToCapture=*/true);
+    }
 
     // Build update: Var = InitExpr + IV * Step
     ExprResult Update;
@@ -23040,14 +23569,91 @@ class MapBaseChecker final : public StmtVisitor<MapBaseChecker, bool> {
 
 public:
   bool VisitDeclRefExpr(DeclRefExpr *DRE) {
-    if (!isa<VarDecl>(DRE->getDecl())) {
+    ValueDecl *D = DRE->getDecl();
+    Expr *E = DRE;
+
+    // Handle BindingDecls by mapping them as member accesses.
+    // When the user writes:
+    //   auto [a, b] = p;
+    //   #pragma omp target map(tofrom:a) map(to:b)
+    // we transform it to:
+    //   #pragma omp target map(tofrom:p.x) map(to:p.y)
+    // This avoids conflicts when 
diff erent bindings have 
diff erent map types.
+    if (auto *BD = dyn_cast<BindingDecl>(D)) {
+      auto *DD = cast<DecompositionDecl>(BD->getDecomposedDecl());
+      Expr *BindingExpr = BD->getBinding();
+
+      // Check if the binding is a member expression (struct/class
+      // decomposition).
+      if (auto *ME = dyn_cast_or_null<MemberExpr>(BindingExpr)) {
+
+        // Use the DecompositionDecl as the base for the member expression.
+        // The structured binding creates a copy (if initialized from a
+        // variable) or holds the only storage (if initialized from a prvalue).
+        // Using DD ensures map clauses reference the correct storage.
+        Expr *BaseExpr =
+            SemaRef
+                .BuildDeclarationNameExpr(
+                    CXXScopeSpec(),
+                    DeclarationNameInfo(DD->getDeclName(), DRE->getLocation()),
+                    DD)
+                .get();
+
+        // Create member expression: base.member.
+        E = MemberExpr::Create(
+            SemaRef.Context, BaseExpr, /*IsArrow=*/false, ME->getOperatorLoc(),
+            ME->getQualifierLoc(), ME->getTemplateKeywordLoc(),
+            ME->getMemberDecl(), ME->getFoundDecl(), ME->getMemberNameInfo(),
+            /*TemplateArgs=*/nullptr, ME->getType(), ME->getValueKind(),
+            ME->getObjectKind(), ME->isNonOdrUse());
+
+        // Now process this as a member expression, which will properly
+        // handle the field-level mapping.
+        return Visit(E);
+      }
+      if (auto *ASE = dyn_cast_or_null<ArraySubscriptExpr>(BindingExpr)) {
+        Expr *BaseExpr =
+            SemaRef
+                .BuildDeclarationNameExpr(
+                    CXXScopeSpec(),
+                    DeclarationNameInfo(DD->getDeclName(), DRE->getLocation()),
+                    DD)
+                .get();
+        E = new (SemaRef.Context) ArraySubscriptExpr(
+            BaseExpr, ASE->getIdx(), ASE->getType(), ASE->getValueKind(),
+            ASE->getObjectKind(), ASE->getRBracketLoc());
+        return Visit(E);
+      }
+      // Tuple-like should already be rejected; do not map DD as a silent
+      // fallback.
+      SemaRef.Diag(DRE->getExprLoc(),
+                   diag::err_omp_unsupported_structured_binding_init)
+          << 4;
+      return false;
+    }
+    // Handle DecompositionDecl directly (implicit captures).
+    else if (auto *DD = dyn_cast<DecompositionDecl>(D)) {
+      if (const VarDecl *OrigVar =
+              getOriginalVarOrDiagnose(SemaRef, DD, DRE->getExprLoc())) {
+        D = const_cast<VarDecl *>(OrigVar);
+        DeclarationNameInfo NameInfo(D->getDeclName(), DRE->getLocation());
+        E = DeclRefExpr::Create(SemaRef.Context, DRE->getQualifierLoc(),
+                                DRE->getTemplateKeywordLoc(), D,
+                                /*RefersToEnclosingVariableOrCapture=*/false,
+                                NameInfo, D->getType(), DRE->getValueKind(),
+                                DRE->getFoundDecl(),
+                                /*TemplateArgs=*/nullptr, DRE->isNonOdrUse());
+      } else {
+        return false;
+      }
+    } else if (!isa<VarDecl>(D)) {
       emitErrorMsg();
       return false;
     }
     assert(!RelevantExpr && "RelevantExpr is expected to be nullptr");
-    RelevantExpr = DRE;
+    RelevantExpr = E;
     // Record the component.
-    Components.emplace_back(DRE, DRE->getDecl(), IsNonContiguous);
+    Components.emplace_back(E, D, IsNonContiguous);
     return true;
   }
 
@@ -24018,7 +24624,6 @@ static void checkMappableExpressionList(
     }
 
     Expr *SimpleExpr = RE->IgnoreParenCasts();
-
     if (!RE->isLValue()) {
       if (SemaRef.getLangOpts().OpenMP < 50) {
         SemaRef.Diag(
@@ -24031,6 +24636,30 @@ static void checkMappableExpressionList(
       continue;
     }
 
+    // Check for unsupported structured bindings early.
+    if (!NoDiagnose) {
+      if (const auto *DRE = dyn_cast<DeclRefExpr>(SimpleExpr)) {
+        const DecompositionDecl *DD = nullptr;
+        const BindingDecl *BD = nullptr;
+        if (const auto *B = dyn_cast<BindingDecl>(DRE->getDecl())) {
+          BD = B;
+          DD = cast<DecompositionDecl>(B->getDecomposedDecl());
+        } else if (const auto *D =
+                       dyn_cast<DecompositionDecl>(DRE->getDecl())) {
+          DD = D;
+        }
+        if (DD) {
+          if (BD && BD->getHoldingVar()) {
+            SemaRef.Diag(ELoc,
+                         diag::err_omp_unsupported_structured_binding_init)
+                << 4;
+            continue;
+          }
+          if (!getOriginalVarOrDiagnose(SemaRef, DD, ELoc))
+            continue;
+        }
+      }
+    }
     OMPClauseMappableExprCommon::MappableExprComponentList CurComponents;
     ValueDecl *CurDeclaration = nullptr;
 

diff  --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 331c1866e36ec..74fe253efa137 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -4708,10 +4708,43 @@ static bool
 buildCapturedStmtCaptureList(Sema &S, CapturedRegionScopeInfo *RSI,
                              SmallVectorImpl<CapturedStmt::Capture> &Captures,
                              SmallVectorImpl<Expr *> &CaptureInits) {
+  bool HasError = false; // Track if any errors occurred.
+  llvm::SmallPtrSet<VarDecl *, 4> CapturedDecomposed;
   for (const sema::Capture &Cap : RSI->Captures) {
     if (Cap.isInvalid())
       continue;
 
+    ValueDecl *CapVar = nullptr;
+    if (Cap.isVariableCapture()) {
+      CapVar = Cap.getVariable();
+      if (auto *BD = dyn_cast<BindingDecl>(CapVar)) {
+        // Detect structured bindings in OpenMP captured regions.
+        // When a BindingDecl (e.g., 'a' from 'auto [a, b] = p')
+        // is referenced inside an OpenMP region.
+        // isVariableCapturable() in SemaExpr.cpp already resets this to the
+        // DecompositionDecl during per-use expression checking. This runs
+        // later, at region-end (ActOnCapturedRegionEnd), over the
+        // already-built capture list, catching captures added without going
+        // through that per-use path (e.g. via explicit map clauses).
+        if (RSI->CapRegionKind == CR_OpenMP && BD->getHoldingVar()) {
+          S.Diag(Cap.getLocation(), diag::err_capture_tuple_binding_openmp)
+              << CapVar;
+          S.Diag(CapVar->getLocation(), diag::note_entity_declared_at)
+              << CapVar;
+          HasError = true; // Mark error but continue.
+          continue;        // Skip this capture, move to next.
+        }
+        CapVar = cast<VarDecl>(BD->getDecomposedDecl());
+      }
+      if (RSI->CapRegionKind == CR_OpenMP) {
+        if (auto *DD = dyn_cast<DecompositionDecl>(CapVar)) {
+          if (!CapturedDecomposed.insert(DD).second) {
+            continue; // Skip duplicate
+          }
+        }
+      }
+    }
+
     // Form the initializer for the capture.
     ExprResult Init = S.BuildCaptureInit(Cap, Cap.getLocation(),
                                          RSI->CapRegionKind == CR_OpenMP);
@@ -4719,32 +4752,38 @@ buildCapturedStmtCaptureList(Sema &S, CapturedRegionScopeInfo *RSI,
     // FIXME: Bail out now if the capture is not used and the initializer has
     // no side-effects.
 
-    // Create a field for this capture.
-    FieldDecl *Field = S.BuildCaptureField(RSI->TheRecordDecl, Cap);
+    // Build the capture field. For OpenMP, pass IsOpenMP=true to handle
+    // DecompositionDecl captures correctly.
+    FieldDecl *Field = S.BuildCaptureField(RSI->TheRecordDecl, Cap,
+                                           RSI->CapRegionKind == CR_OpenMP);
 
     // Add the capture to our list of captures.
     if (Cap.isThisCapture()) {
-      Captures.push_back(CapturedStmt::Capture(Cap.getLocation(),
-                                               CapturedStmt::VCK_This));
+      Captures.push_back(
+          CapturedStmt::Capture(Cap.getLocation(), CapturedStmt::VCK_This));
     } else if (Cap.isVLATypeCapture()) {
       Captures.push_back(
           CapturedStmt::Capture(Cap.getLocation(), CapturedStmt::VCK_VLAType));
     } else {
       assert(Cap.isVariableCapture() && "unknown kind of capture");
 
-      if (S.getLangOpts().OpenMP && RSI->CapRegionKind == CR_OpenMP)
-        S.OpenMP().setOpenMPCaptureKind(Field, Cap.getVariable(),
-                                        RSI->OpenMPLevel);
-
-      Captures.push_back(CapturedStmt::Capture(
-          Cap.getLocation(),
-          Cap.isReferenceCapture() ? CapturedStmt::VCK_ByRef
-                                   : CapturedStmt::VCK_ByCopy,
-          cast<VarDecl>(Cap.getVariable())));
+      if (S.getLangOpts().OpenMP && RSI->CapRegionKind == CR_OpenMP) {
+        const ValueDecl *DSAVar = Cap.getVariable();
+        // DSAs are tracked per binding; a captured DecompositionDecl has no
+        // own DSA entry.
+        if (const auto *DD = dyn_cast<DecompositionDecl>(DSAVar))
+          if (!DD->bindings().empty())
+            DSAVar = *DD->bindings().begin();
+        S.OpenMP().setOpenMPCaptureKind(Field, DSAVar, RSI->OpenMPLevel);
+      }
+      Captures.emplace_back(Cap.getLocation(),
+                            Cap.isReferenceCapture() ? CapturedStmt::VCK_ByRef
+                                                     : CapturedStmt::VCK_ByCopy,
+                            cast<VarDecl>(CapVar));
     }
     CaptureInits.push_back(Init.get());
   }
-  return false;
+  return HasError;
 }
 
 static std::optional<int>

diff  --git a/clang/test/OpenMP/structured-bindings-ast.cpp b/clang/test/OpenMP/structured-bindings-ast.cpp
new file mode 100644
index 0000000000000..0eee8158ca798
--- /dev/null
+++ b/clang/test/OpenMP/structured-bindings-ast.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -fopenmp -std=c++20 -triple x86_64-unknown-linux-gnu \
+// RUN:   -ast-dump %s | FileCheck %s
+
+
+struct Point {
+  int x, y;
+};
+
+void test_target_firstprivate() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp target firstprivate(a)
+  {
+    a = 1;
+  }
+}
+
+// CHECK-LABEL: test_target_firstprivate
+// CHECK: OMPCaptureKindAttr {{.*}} firstprivate
+// CHECK-NOT: OMPCaptureKindAttr {{.*}} map
+
+void test_target_parallel_firstprivate() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp target parallel firstprivate(a)
+  {
+    a = 1;
+  }
+}
+
+// CHECK-LABEL: test_target_parallel_firstprivate
+// CHECK: OMPCaptureKindAttr {{.*}} firstprivate
+// CHECK-NOT: OMPCaptureKindAttr {{.*}} map 

diff  --git a/clang/test/OpenMP/structured-bindings-codegen.cpp b/clang/test/OpenMP/structured-bindings-codegen.cpp
new file mode 100644
index 0000000000000..56788e8f331b3
--- /dev/null
+++ b/clang/test/OpenMP/structured-bindings-codegen.cpp
@@ -0,0 +1,4372 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --filter-out-after "getelem.*kernel" --filter-out "= alloca.*" --include-generated-funcs --replace-value-regex "__omp_offloading_[0-9a-z]+_[0-9a-z]+" "reduction_size[.].+[.]" "pl_cond[.].+[.|,]" --prefix-filecheck-ir-name _ --global-value-regex "\.offload_.*" --global-hex-value-regex ".offload_maptypes.*" --version 6
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -std=c++20 -triple x86_64-unknown-unknown -fopenmp-targets=x86_64-pc-linux-gnu -emit-llvm %s -o - | FileCheck %s
+
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -std=c++20 -triple x86_64-unknown-unknown -fopenmp-targets=x86_64-pc-linux-gnu -emit-pch -o %t %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -std=c++20 -triple x86_64-unknown-unknown -fopenmp-targets=x86_64-pc-linux-gnu -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s
+
+// expected-no-diagnostics
+
+#ifndef HEADER
+#define HEADER
+
+struct Point { int x, y; };
+struct Point3D { int x, y, z; };
+struct Triple { int x, y, z; };
+
+void test_binding_and_orig_separate() {
+  Triple t{1, 2, 3};
+  auto [a, b, c] = t;
+
+#pragma omp target map(tofrom: t)
+  {
+    t.x = 100;
+    a = a + 1;
+
+  }
+}
+
+void test_target_implicit_map() {
+  Point p{1, 2};
+  auto [a, b] = p;
+
+#pragma omp target
+  {
+    int sum = a + b;
+  }
+}
+
+void test_target_parallel() {
+  Point p{3, 4};
+  auto [a, b] = p;
+
+#pragma omp target parallel
+  {
+    int sum = a + b;
+  }
+}
+
+void test_target_parallel_for() {
+  Point p{5, 6};
+  auto [a, b] = p;
+
+#pragma omp target parallel for
+  for (int i = 0; i < 10; i++) {
+    int result = a + b + i;
+  }
+}
+
+void test_firstprivate_dsa() {
+  Point p{7, 8};
+  auto [a, b] = p;
+
+#pragma omp parallel firstprivate(p)
+  {
+    int sum = a + b;
+  }
+}
+
+void test_shared_dsa() {
+  Point p{9, 10};
+  auto [a, b] = p;
+
+#pragma omp parallel shared(p)
+  {
+    int sum = a + b;
+  }
+}
+
+void test_reduction_dsa() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  int sum = 0;
+
+#pragma omp parallel for reduction(+:sum)
+  for (int i = 0; i < 10; i++) {
+    sum += a + b;
+  }
+}
+
+void test_parallel_for_() {
+  Point p{11, 12};
+  auto [a, b] = p;
+
+#pragma omp parallel for
+  for (int i = 0; i < 10; i++) {
+    int result = a + b + i;
+  }
+}
+
+void test_parallel_for_simd_() {
+  Point p{13, 14};
+  auto [a, b] = p;
+
+#pragma omp parallel for simd
+  for (int i = 0; i < 10; i++) {
+    int result = a + b + i;
+  }
+}
+
+void test_target_teams_distribute() {
+  Point p{15, 16};
+  auto [a, b] = p;
+
+#pragma omp target teams distribute
+  for (int i = 0; i < 10; i++) {
+    int result = a + b + i;
+  }
+}
+
+void test_task() {
+  Point p{17, 18};
+  auto [a, b] = p;
+
+#pragma omp task
+  {
+    int sum = a + b;
+  }
+}
+
+void test_task_firstprivate() {
+  Point p{18, 19};
+  auto [a, b] = p;
+
+#pragma omp task firstprivate(a) firstprivate(b)
+  {
+    int sum = a + b;
+  }
+}
+
+void test_task_firstprivate_with_shared() {
+  Point p{18, 19};
+  auto [a, b] = p;
+  int ra = 0, rb = 0;
+#pragma omp task firstprivate(a, b) shared(ra, rb)
+  {
+    ra = a;
+    rb = b;
+  }
+#pragma omp taskwait
+}
+
+void test_task_depend() {
+  Point p{19, 20};
+  auto [a, b] = p;
+
+#pragma omp task depend(in: a, b)
+  {
+    int sum = a + b;
+  }
+}
+
+void test_taskloop_() {
+  Point p{21, 22};
+  auto [a, b] = p;
+
+#pragma omp taskloop
+  for (int i = 0; i < 10; i++) {
+    int result = a + b + i;
+  }
+}
+
+template<typename T>
+int test_template_bas(T p) {
+  auto [a, b] = p;
+  int result = 0;
+
+#pragma omp parallel reduction(+:result)
+  {
+    result = a + b;
+  }
+  return result;
+}
+
+template<typename T>
+int test_template_target(T p) {
+  auto [a, b] = p;
+  int result = 0;
+
+#pragma omp target map(tofrom: result)
+  {
+    result = a + b;
+  }
+  return result;
+}
+
+template<typename T>
+int test_template_task(T p) {
+  auto [a, b] = p;
+  int result = 0;
+
+#pragma omp task shared(result)
+  {
+    result = a + b;
+  }
+#pragma omp taskwait
+  return result;
+}
+
+template<typename T>
+int test_template_3_bindings(T p) {
+  auto [x, y, z] = p;
+  int result = 0;
+
+#pragma omp parallel reduction(+:result)
+  {
+    result = x + y + z;
+  }
+  return result;
+}
+
+void instantiate_templates() {
+  Point p2{1, 2};
+  Point3D p3{1, 2, 3};
+
+  test_template_bas(p2);
+  test_template_bas(Point{3, 4});
+  test_template_target(p2);
+  test_template_task(p2);
+  test_template_3_bindings(p3);
+}
+
+void test_static_binding() {
+  static Point p{23, 24};
+  static auto [a, b] = p;
+
+#pragma omp parallel
+  {
+    int sum = a + b;
+  }
+}
+
+void test_static_binding_shared() {
+  static Point p{25, 26};
+  static auto [a, b] = p;
+
+#pragma omp parallel shared(p)
+  {
+    a = a + 1;
+    b = b + 1;
+  }
+}
+
+void test_array_target() {
+  int arr[2] = {27, 28};
+  auto [a, b] = arr;
+
+#pragma omp target
+  {
+    int sum = a + b;
+  }
+}
+
+void test_array_task() {
+  int arr[2] = {29, 30};
+  auto [a, b] = arr;
+
+#pragma omp task
+  {
+    int sum = a + b;
+  }
+}
+
+void test_nested() {
+  Point p{31, 32};
+  auto [a, b] = p;
+
+#pragma omp parallel
+  {
+#pragma omp critical
+    {
+      int sum = a + b;
+    }
+
+#pragma omp task
+    {
+      int product = a * b;
+    }
+  }
+}
+
+void test_reference_binding() {
+  Point p{31, 32};
+  auto& [a, b] = p;
+
+#pragma omp parallel
+  {
+    int sum = a + b;
+  }
+}
+
+void test_const_binding() {
+  const Point p{33, 34};
+  const auto [a, b] = p;
+
+#pragma omp parallel
+  {
+    int sum = a + b;
+  }
+}
+
+void test_multiple_bindings() {
+  Point p1{33, 34};
+  Point p2{35, 36};
+  auto [a, b] = p1;
+  auto [c, d] = p2;
+
+#pragma omp parallel
+  {
+    int sum = a + b + c + d;
+  }
+}
+
+void test_multiple_bindings_mixed_dsa() {
+  Point p1{37, 38};
+  Point p2{39, 40};
+  auto [a, b] = p1;
+  auto [c, d] = p2;
+
+#pragma omp parallel firstprivate(p1) shared(p2)
+  {
+    int result = a + b + c + d;
+  }
+}
+
+void test_array_3_elements() {
+  int arr[3] = {35, 36, 37};
+  auto [a, b, c] = arr;
+
+#pragma omp parallel
+  {
+    int sum = a + b + c;
+  }
+}
+
+void test_single() {
+  Point p{38, 39};
+  auto [a, b] = p;
+
+#pragma omp parallel
+#pragma omp single
+  {
+    int sum = a + b;
+  }
+}
+
+void test_sections() {
+  Point p{40, 41};
+  auto [a, b] = p;
+
+#pragma omp parallel sections
+  {
+#pragma omp section
+    { int sum = a + b; }
+#pragma omp section
+    { int 
diff  = a - b; }
+  }
+}
+
+void test_nested_parallel() {
+  Point p{42, 43};
+  auto [a, b] = p;
+
+#pragma omp parallel
+  {
+#pragma omp parallel
+    {
+      int sum = a + b;
+    }
+  }
+}
+
+void test_simd_() {
+  Point p{44, 45};
+  auto [a, b] = p;
+
+#pragma omp simd
+  for (int i = 0; i < 10; i++) {
+    int result = a + b + i;
+  }
+}
+
+void test_simd_write_binding() {
+  Point p{1, 2};
+  auto [a, b] = p;
+
+#pragma omp simd
+  for (int i = 0; i < 10; ++i)
+    a = i;
+}
+
+void test_simd_lastprivate() {
+  Point p{1, 2};
+  auto [a, b] = p;
+
+#pragma omp simd lastprivate(a)
+  for (int i = 0; i < 10; ++i)
+    a = i;
+}
+
+void test_private_individual_bindings() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp parallel private(a)
+  {
+    a = 2;
+  }
+}
+
+void test_firstprivate_individual_bindings() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp parallel firstprivate(b)
+  {
+    b = b + 10;
+  }
+}
+
+void test_static_bindings() {
+  static auto [a, b] = Point{1, 2};
+#pragma omp parallel
+  {
+    (void)(a + b);
+  }
+}
+
+void use(int a);
+
+void test_shadowing() {
+  auto [a, b] = Point{1, 2};
+#pragma omp parallel private(a)
+  {
+    use(a);
+    {
+      auto [a, b] = Point{10, 20};
+      use(a);
+    }
+    use(a);
+  }
+  use(a);
+}
+
+void test_simd_private_then_parallel() {
+  Point p{1,2};
+  auto [a,b] = p;
+#pragma omp simd private(a)
+  for (int i=0;i<10;++i) a += i;
+#pragma omp parallel
+  {
+    use(a);
+  }
+}
+
+void test_linear_binding() {
+  Point p{1, 2};
+  auto [a, b] = p;
+
+#pragma omp simd linear(a:1)
+  for (int i = 0; i < 10; ++i) {
+    a += 1;
+    use(a);
+  }
+}
+
+void test_lastprivate_binding() {
+  Point p{1, 2};
+  auto [a, b] = p;
+
+#pragma omp parallel for lastprivate(a)
+  for (int i = 0; i < 10; ++i) {
+    a = i * 10;
+  }
+  use(a);
+}
+
+void test_binding_name_collision() {
+  Point p1{1, 2};
+  auto [a, b] = p1;
+
+#pragma omp parallel
+  {
+    Point p2{10, 20};
+    auto [a, b] = p2;
+    a += 1;
+    use(a);
+  }
+}
+
+struct NonTrivialCopy {
+  int value;
+  int copy_count;
+
+  NonTrivialCopy(int v) : value(v), copy_count(0) {}
+  NonTrivialCopy() : value(0), copy_count(0) {}
+  NonTrivialCopy(const NonTrivialCopy &other)
+    : value(other.value), copy_count(other.copy_count + 1) {}
+
+  ~NonTrivialCopy() {}
+};
+
+struct Pair {
+  NonTrivialCopy x;
+  NonTrivialCopy y;
+};
+
+void test_firstprivate_nontrivial_sb() {
+  Pair p{NonTrivialCopy(10), NonTrivialCopy(20)};
+  auto [a, b] = p;
+
+#pragma omp parallel firstprivate(a)
+  {
+    (void)a.value;
+  }
+}
+
+void test_firstprivate_ref_binding_sb() {
+  Pair p{NonTrivialCopy(10), NonTrivialCopy(20)};
+  auto& [a, b] = p;
+
+#pragma omp parallel firstprivate(a)
+  {
+    (void)a.value;
+  }
+}
+
+void test_firstprivate_const_ref_binding_sb() {
+  Pair p{NonTrivialCopy(10), NonTrivialCopy(20)};
+  const auto& [a, b] = p;
+
+#pragma omp parallel firstprivate(a)
+  {
+    (void)a.value;
+  }
+}
+
+void test_firstprivate_multiple_bindings_sb() {
+  Pair p{NonTrivialCopy(10), NonTrivialCopy(20)};
+  auto [a, b] = p;
+
+#pragma omp parallel firstprivate(a, b)
+  {
+    (void)(a.value + b.value);
+  }
+}
+
+struct WithDtor {
+  int *ptr;
+  WithDtor(int v) : ptr(new int(v)) {}
+  WithDtor(const WithDtor &other) : ptr(new int(*other.ptr)) {}
+  ~WithDtor() { delete ptr; }
+};
+
+struct PairWithDtor {
+  WithDtor x;
+  WithDtor y;
+};
+
+void test_firstprivate_with_destructor_sb() {
+  PairWithDtor p{WithDtor(100), WithDtor(200)};
+  auto [a, b] = p;
+
+#pragma omp parallel firstprivate(a)
+  {
+    (void)(*a.ptr);
+  }
+}
+
+void test_firstprivate_array_bindings_sb() {
+  NonTrivialCopy arr[3] = {NonTrivialCopy(1), NonTrivialCopy(2), NonTrivialCopy(3)};
+  auto [a, b, c] = arr;
+
+
+#pragma omp parallel firstprivate(a, b, c)
+  {
+    (void)(a.value + b.value + c.value);
+  }
+}
+
+struct Inner {
+  int val;
+  Inner(int v) : val(v) {}
+  Inner(const Inner &o) : val(o.val * 2) {}
+  ~Inner() {}
+};
+
+struct Outer {
+  Inner i1;
+  Inner i2;
+};
+
+void test_firstprivate_nested_struct_sb() {
+  Outer o{Inner(5), Inner(10)};
+  auto [x, y] = o;
+
+#pragma omp parallel firstprivate(x)
+  {
+    (void)x.val;
+  }
+}
+
+void test_firstprivate_ref_binding_both_bindings_sb() {
+  Pair p{NonTrivialCopy(10), NonTrivialCopy(20)};
+  auto& [a, b] = p;
+
+#pragma omp parallel firstprivate(a, b)
+  {
+    (void)(a.value + b.value);
+  }
+}
+
+
+void test_firstprivate_const_ref_binding_both_bindings_sb() {
+  Pair p{NonTrivialCopy(10), NonTrivialCopy(20)};
+  const auto& [a, b] = p;
+
+#pragma omp parallel firstprivate(a, b)
+  {
+    (void)(a.value + b.value);
+  }
+}
+
+void test_lambda_capture_binding_by_value() {
+  Point p{10, 20};
+  auto [a, b] = p;
+  auto lambda = [a]() {
+    return a + a;
+  };
+  int result = lambda();
+}
+
+
+void test_lambda_capture_binding_by_ref() {
+  Point p{10, 20};
+  auto [a, b] = p;
+  auto lambda = [&a]() {
+    a = 100;
+  };
+  lambda();
+}
+
+
+void test_lambda_capture_multiple_bindings() {
+  Point p{10, 20};
+  auto [a, b] = p;
+  auto lambda = [a, &b]() {
+    b = a + 50;
+  };
+  lambda();
+}
+
+
+void test_lambda_implicit_capture() {
+  Point p{10, 20};
+  auto [a, b] = p;
+  auto lambda = [=]() {
+    return a + b;
+  };
+  lambda();
+}
+
+// Test parallel for with linear clause on binding.
+void test_parallel_for_linear() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp parallel
+  {
+#pragma omp for linear(a : 1)
+    for (int i = 0; i < 10; ++i) {
+      a += 1;
+      use(a);
+    }
+  }
+}
+
+// Test taskloop with lastprivate clause on binding.
+void test_taskloop_lastprivate() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp parallel
+  {
+#pragma omp taskloop lastprivate(a)
+    for (int i = 0; i < 10; ++i)
+      a = i * 10;
+  }
+}
+
+// Test task with shared clause on binding.
+void test_task_shared() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp parallel
+  {
+#pragma omp task shared(a)
+    { a = 42; }
+  }
+}
+
+// Test parallel for with firstprivate and lastprivate on binding.
+void test_parallel_for_firstprivate_lastprivate() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp parallel
+  {
+#pragma omp for firstprivate(a) lastprivate(a)
+    for (int i = 0; i < 10; ++i)
+      a = i * 10;
+  }
+}
+
+// Test parallel for with private and lastprivate on 
diff erent bindings.
+void test_private_plus_lastprivate() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp parallel for private(a) lastprivate(b)
+  for (int i = 0; i < 10; ++i) {
+    a = i;
+    b = a;
+  }
+}
+
+void test_target_map_orig_use_binding() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp target map(tofrom: p)
+  {
+    p.x = 1;
+    a = 2;
+  }
+}
+#endif
+// CHECK-LABEL: define dso_local void @_Z30test_binding_and_orig_separatev(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[T:%.*]], ptr align 4 @__const._Z30test_binding_and_orig_separatev.t, i64 12, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[T]], i64 12, i1 false)
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[T]], ptr [[TMP1]], align 8
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[T]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_MAPPERS:%.*]], i64 0, i64 0
+// CHECK:    store ptr null, ptr [[TMP3]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 1
+// CHECK:    store ptr [[TMP0]], ptr [[TMP4]], align 8
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 1
+// CHECK:    store ptr [[TMP0]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 1
+// CHECK:    store ptr null, ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 2
+// CHECK:    store ptr null, ptr [[TMP7]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 2
+// CHECK:    store ptr null, ptr [[TMP8]], align 8
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 2
+// CHECK:    store ptr null, ptr [[TMP9]], align 8
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 0
+// CHECK:    [[TMP11:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 0
+// CHECK:    [[TMP12:%.*]] = getelementptr inbounds nuw [[STRUCT___TGT_KERNEL_ARGUMENTS:%.*]], ptr [[KERNEL_ARGS:%.*]], i32 0, i32 0
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z30test_binding_and_orig_separatev_l20(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(12) [[T:%.*]], ptr noundef nonnull align 4 dereferenceable(12) [[TMP0:%.*]], ptr noalias noundef [[DYN_PTR:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[T]], ptr [[T_ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[T_ADDR]], align 8, !nonnull [[META18:![0-9]+]], !align [[META19:![0-9]+]]
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    store i32 100, ptr [[X]], align 4
+// CHECK:    [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE]], ptr [[TMP2]], i32 0, i32 0
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[X1]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP3]], 1
+// CHECK:    [[X2:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE]], ptr [[TMP2]], i32 0, i32 0
+// CHECK:    store i32 [[ADD]], ptr [[X2]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z24test_target_implicit_mapv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z24test_target_implicit_mapv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP1]], align 8
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_MAPPERS:%.*]], i64 0, i64 0
+// CHECK:    store ptr null, ptr [[TMP3]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 1
+// CHECK:    store ptr null, ptr [[TMP4]], align 8
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 1
+// CHECK:    store ptr null, ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 1
+// CHECK:    store ptr null, ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 0
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 0
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds nuw [[STRUCT___TGT_KERNEL_ARGUMENTS:%.*]], ptr [[KERNEL_ARGS:%.*]], i32 0, i32 0
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z24test_target_implicit_mapv_l32(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noalias noundef [[DYN_PTR:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[A:%.*]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    store i32 [[TMP3]], ptr [[B:%.*]], align 4
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[B]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP4]], [[TMP5]]
+// CHECK:    store i32 [[ADD]], ptr [[SUM:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z20test_target_parallelv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z20test_target_parallelv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP1]], align 8
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_MAPPERS:%.*]], i64 0, i64 0
+// CHECK:    store ptr null, ptr [[TMP3]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 1
+// CHECK:    store ptr null, ptr [[TMP4]], align 8
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 1
+// CHECK:    store ptr null, ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 1
+// CHECK:    store ptr null, ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 0
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 0
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds nuw [[STRUCT___TGT_KERNEL_ARGUMENTS:%.*]], ptr [[KERNEL_ARGS:%.*]], i32 0, i32 0
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z20test_target_parallelv_l42(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noalias noundef [[DYN_PTR:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1:[0-9]+]], i32 1, ptr @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z20test_target_parallelv_l42.omp_outlined, ptr [[TMP1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z20test_target_parallelv_l42.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[A:%.*]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    store i32 [[TMP3]], ptr [[B:%.*]], align 4
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[B]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP4]], [[TMP5]]
+// CHECK:    store i32 [[ADD]], ptr [[SUM:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z24test_target_parallel_forv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z24test_target_parallel_forv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP1]], align 8
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_MAPPERS:%.*]], i64 0, i64 0
+// CHECK:    store ptr null, ptr [[TMP3]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 1
+// CHECK:    store ptr null, ptr [[TMP4]], align 8
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 1
+// CHECK:    store ptr null, ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 1
+// CHECK:    store ptr null, ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 0
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 0
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds nuw [[STRUCT___TGT_KERNEL_ARGUMENTS:%.*]], ptr [[KERNEL_ARGS:%.*]], i32 0, i32 0
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z24test_target_parallel_forv_l52(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noalias noundef [[DYN_PTR:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z24test_target_parallel_forv_l52.omp_outlined, ptr [[TMP1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z24test_target_parallel_forv_l52.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 0, ptr [[DOTOMP_LB:%.*]], align 4
+// CHECK:    store i32 9, ptr [[DOTOMP_UB:%.*]], align 4
+// CHECK:    store i32 1, ptr [[DOTOMP_STRIDE:%.*]], align 4
+// CHECK:    store i32 0, ptr [[DOTOMP_IS_LAST:%.*]], align 4
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[A:%.*]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    store i32 [[TMP3]], ptr [[B:%.*]], align 4
+// CHECK:    [[TMP4:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[TMP4]], align 4
+// CHECK:    call void @__kmpc_for_static_init_4(ptr @[[GLOB2:[0-9]+]], i32 [[TMP5]], i32 34, ptr [[DOTOMP_IS_LAST]], ptr [[DOTOMP_LB]], ptr [[DOTOMP_UB]], ptr [[DOTOMP_STRIDE]], i32 1, i32 1)
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP:%.*]] = icmp sgt i32 [[TMP6]], 9
+// CHECK:    br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+// CHECK:       [[COND_TRUE]]:
+// CHECK:    br label %[[COND_END:.*]]
+// CHECK:       [[COND_FALSE]]:
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    br label %[[COND_END]]
+// CHECK:       [[COND_END]]:
+// CHECK:    [[COND:%.*]] = phi i32 [ 9, %[[COND_TRUE]] ], [ [[TMP7]], %[[COND_FALSE]] ]
+// CHECK:    store i32 [[COND]], ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[DOTOMP_LB]], align 4
+// CHECK:    store i32 [[TMP8]], ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP1:%.*]] = icmp sle i32 [[TMP9]], [[TMP10]]
+// CHECK:    br i1 [[CMP1]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP11]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4
+// CHECK:    [[TMP12:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    [[TMP13:%.*]] = load i32, ptr [[B]], align 4
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[TMP12]], [[TMP13]]
+// CHECK:    [[TMP14:%.*]] = load i32, ptr [[I]], align 4
+// CHECK:    [[ADD3:%.*]] = add nsw i32 [[ADD2]], [[TMP14]]
+// CHECK:    store i32 [[ADD3]], ptr [[RESULT:%.*]], align 4
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP15:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[ADD4:%.*]] = add nsw i32 [[TMP15]], 1
+// CHECK:    store i32 [[ADD4]], ptr [[DOTOMP_IV]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    br label %[[OMP_LOOP_EXIT:.*]]
+// CHECK:       [[OMP_LOOP_EXIT]]:
+// CHECK:    call void @__kmpc_for_static_fini(ptr @[[GLOB2]], i32 [[TMP5]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z21test_firstprivate_dsav(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z21test_firstprivate_dsav.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 2, ptr @_Z21test_firstprivate_dsav.omp_outlined, ptr [[TMP0]], ptr [[P]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z21test_firstprivate_dsav.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[P:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[P]], ptr [[P_ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[P_ADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P1:%.*]], ptr align 4 [[TMP2]], i64 8, i1 false)
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP3]], [[TMP4]]
+// CHECK:    store i32 [[ADD]], ptr [[SUM:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z15test_shared_dsav(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z15test_shared_dsav.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z15test_shared_dsav.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z15test_shared_dsav.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP2]], [[TMP3]]
+// CHECK:    store i32 [[ADD]], ptr [[SUM:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z18test_reduction_dsav(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z18test_reduction_dsav.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    store i32 0, ptr [[SUM:%.*]], align 4
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 2, ptr @_Z18test_reduction_dsav.omp_outlined, ptr [[SUM]], ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z18test_reduction_dsav.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[SUM:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[SUM]], ptr [[SUM_ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[SUM_ADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 0, ptr [[DOTOMP_LB:%.*]], align 4
+// CHECK:    store i32 9, ptr [[DOTOMP_UB:%.*]], align 4
+// CHECK:    store i32 1, ptr [[DOTOMP_STRIDE:%.*]], align 4
+// CHECK:    store i32 0, ptr [[DOTOMP_IS_LAST:%.*]], align 4
+// CHECK:    store i32 0, ptr [[SUM1:%.*]], align 4
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4
+// CHECK:    call void @__kmpc_for_static_init_4(ptr @[[GLOB2]], i32 [[TMP4]], i32 34, ptr [[DOTOMP_IS_LAST]], ptr [[DOTOMP_LB]], ptr [[DOTOMP_UB]], ptr [[DOTOMP_STRIDE]], i32 1, i32 1)
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP:%.*]] = icmp sgt i32 [[TMP5]], 9
+// CHECK:    br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+// CHECK:       [[COND_TRUE]]:
+// CHECK:    br label %[[COND_END:.*]]
+// CHECK:       [[COND_FALSE]]:
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    br label %[[COND_END]]
+// CHECK:       [[COND_END]]:
+// CHECK:    [[COND:%.*]] = phi i32 [ 9, %[[COND_TRUE]] ], [ [[TMP6]], %[[COND_FALSE]] ]
+// CHECK:    store i32 [[COND]], ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[DOTOMP_LB]], align 4
+// CHECK:    store i32 [[TMP7]], ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP2:%.*]] = icmp sle i32 [[TMP8]], [[TMP9]]
+// CHECK:    br i1 [[CMP2]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP10]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP2]], i32 0, i32 0
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP2]], i32 0, i32 1
+// CHECK:    [[TMP12:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD3:%.*]] = add nsw i32 [[TMP11]], [[TMP12]]
+// CHECK:    [[TMP13:%.*]] = load i32, ptr [[SUM1]], align 4
+// CHECK:    [[ADD4:%.*]] = add nsw i32 [[TMP13]], [[ADD3]]
+// CHECK:    store i32 [[ADD4]], ptr [[SUM1]], align 4
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP14:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[ADD5:%.*]] = add nsw i32 [[TMP14]], 1
+// CHECK:    store i32 [[ADD5]], ptr [[DOTOMP_IV]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    br label %[[OMP_LOOP_EXIT:.*]]
+// CHECK:       [[OMP_LOOP_EXIT]]:
+// CHECK:    call void @__kmpc_for_static_fini(ptr @[[GLOB2]], i32 [[TMP4]])
+// CHECK:    [[TMP15:%.*]] = getelementptr inbounds [1 x ptr], ptr [[DOTOMP_REDUCTION_RED_LIST:%.*]], i64 0, i64 0
+// CHECK:    store ptr [[SUM1]], ptr [[TMP15]], align 8
+// CHECK:    [[TMP16:%.*]] = call i32 @__kmpc_reduce_nowait(ptr @[[GLOB3:[0-9]+]], i32 [[TMP4]], i32 1, i64 8, ptr [[DOTOMP_REDUCTION_RED_LIST]], ptr @_Z18test_reduction_dsav.omp_outlined.omp.reduction.reduction_func, ptr @.gomp_critical_user_.reduction.var)
+// CHECK:    switch i32 [[TMP16]], label %[[DOTOMP_REDUCTION_DEFAULT:.*]] [
+// CHECK:      i32 1, label %[[DOTOMP_REDUCTION_CASE1:.*]]
+// CHECK:      i32 2, label %[[DOTOMP_REDUCTION_CASE2:.*]]
+// CHECK:    ]
+// CHECK:       [[DOTOMP_REDUCTION_CASE1]]:
+// CHECK:    [[TMP17:%.*]] = load i32, ptr [[TMP1]], align 4
+// CHECK:    [[TMP18:%.*]] = load i32, ptr [[SUM1]], align 4
+// CHECK:    [[ADD6:%.*]] = add nsw i32 [[TMP17]], [[TMP18]]
+// CHECK:    store i32 [[ADD6]], ptr [[TMP1]], align 4
+// CHECK:    call void @__kmpc_end_reduce_nowait(ptr @[[GLOB3]], i32 [[TMP4]], ptr @.gomp_critical_user_.reduction.var)
+// CHECK:    br label %[[DOTOMP_REDUCTION_DEFAULT]]
+// CHECK:       [[DOTOMP_REDUCTION_CASE2]]:
+// CHECK:    [[TMP19:%.*]] = load i32, ptr [[SUM1]], align 4
+// CHECK:    [[TMP20:%.*]] = atomicrmw add ptr [[TMP1]], i32 [[TMP19]] monotonic, align 4
+// CHECK:    br label %[[DOTOMP_REDUCTION_DEFAULT]]
+// CHECK:       [[DOTOMP_REDUCTION_DEFAULT]]:
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z18test_reduction_dsav.omp_outlined.omp.reduction.reduction_func(
+// CHECK-SAME: ptr noundef [[TMP0:%.*]], ptr noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds [1 x ptr], ptr [[TMP3]], i64 0, i64 0
+// CHECK:    [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [1 x ptr], ptr [[TMP2]], i64 0, i64 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[TMP7]], align 4
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[TMP5]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP8]], [[TMP9]]
+// CHECK:    store i32 [[ADD]], ptr [[TMP7]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z18test_parallel_for_v(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z18test_parallel_for_v.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z18test_parallel_for_v.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z18test_parallel_for_v.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 0, ptr [[DOTOMP_LB:%.*]], align 4
+// CHECK:    store i32 9, ptr [[DOTOMP_UB:%.*]], align 4
+// CHECK:    store i32 1, ptr [[DOTOMP_STRIDE:%.*]], align 4
+// CHECK:    store i32 0, ptr [[DOTOMP_IS_LAST:%.*]], align 4
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[TMP2]], align 4
+// CHECK:    call void @__kmpc_for_static_init_4(ptr @[[GLOB2]], i32 [[TMP3]], i32 34, ptr [[DOTOMP_IS_LAST]], ptr [[DOTOMP_LB]], ptr [[DOTOMP_UB]], ptr [[DOTOMP_STRIDE]], i32 1, i32 1)
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP:%.*]] = icmp sgt i32 [[TMP4]], 9
+// CHECK:    br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+// CHECK:       [[COND_TRUE]]:
+// CHECK:    br label %[[COND_END:.*]]
+// CHECK:       [[COND_FALSE]]:
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    br label %[[COND_END]]
+// CHECK:       [[COND_END]]:
+// CHECK:    [[COND:%.*]] = phi i32 [ 9, %[[COND_TRUE]] ], [ [[TMP5]], %[[COND_FALSE]] ]
+// CHECK:    store i32 [[COND]], ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[DOTOMP_LB]], align 4
+// CHECK:    store i32 [[TMP6]], ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP1:%.*]] = icmp sle i32 [[TMP7]], [[TMP8]]
+// CHECK:    br i1 [[CMP1]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP9]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[TMP10]], [[TMP11]]
+// CHECK:    [[TMP12:%.*]] = load i32, ptr [[I]], align 4
+// CHECK:    [[ADD3:%.*]] = add nsw i32 [[ADD2]], [[TMP12]]
+// CHECK:    store i32 [[ADD3]], ptr [[RESULT:%.*]], align 4
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP13:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[ADD4:%.*]] = add nsw i32 [[TMP13]], 1
+// CHECK:    store i32 [[ADD4]], ptr [[DOTOMP_IV]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    br label %[[OMP_LOOP_EXIT:.*]]
+// CHECK:       [[OMP_LOOP_EXIT]]:
+// CHECK:    call void @__kmpc_for_static_fini(ptr @[[GLOB2]], i32 [[TMP3]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z23test_parallel_for_simd_v(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z23test_parallel_for_simd_v.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z23test_parallel_for_simd_v.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z23test_parallel_for_simd_v.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 0, ptr [[DOTOMP_LB:%.*]], align 4
+// CHECK:    store i32 9, ptr [[DOTOMP_UB:%.*]], align 4
+// CHECK:    store i32 1, ptr [[DOTOMP_STRIDE:%.*]], align 4
+// CHECK:    store i32 0, ptr [[DOTOMP_IS_LAST:%.*]], align 4
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[TMP2]], align 4
+// CHECK:    call void @__kmpc_for_static_init_4(ptr @[[GLOB2]], i32 [[TMP3]], i32 34, ptr [[DOTOMP_IS_LAST]], ptr [[DOTOMP_LB]], ptr [[DOTOMP_UB]], ptr [[DOTOMP_STRIDE]], i32 1, i32 1)
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP:%.*]] = icmp sgt i32 [[TMP4]], 9
+// CHECK:    br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+// CHECK:       [[COND_TRUE]]:
+// CHECK:    br label %[[COND_END:.*]]
+// CHECK:       [[COND_FALSE]]:
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    br label %[[COND_END]]
+// CHECK:       [[COND_END]]:
+// CHECK:    [[COND:%.*]] = phi i32 [ 9, %[[COND_TRUE]] ], [ [[TMP5]], %[[COND_FALSE]] ]
+// CHECK:    store i32 [[COND]], ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[DOTOMP_LB]], align 4
+// CHECK:    store i32 [[TMP6]], ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP22:![0-9]+]]
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4, !llvm.access.group [[ACC_GRP22]]
+// CHECK:    [[CMP1:%.*]] = icmp sle i32 [[TMP7]], [[TMP8]]
+// CHECK:    br i1 [[CMP1]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP22]]
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP9]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4, !llvm.access.group [[ACC_GRP22]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[X]], align 4, !llvm.access.group [[ACC_GRP22]]
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[Y]], align 4, !llvm.access.group [[ACC_GRP22]]
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[TMP10]], [[TMP11]]
+// CHECK:    [[TMP12:%.*]] = load i32, ptr [[I]], align 4, !llvm.access.group [[ACC_GRP22]]
+// CHECK:    [[ADD3:%.*]] = add nsw i32 [[ADD2]], [[TMP12]]
+// CHECK:    store i32 [[ADD3]], ptr [[RESULT:%.*]], align 4, !llvm.access.group [[ACC_GRP22]]
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP13:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP22]]
+// CHECK:    [[ADD4:%.*]] = add nsw i32 [[TMP13]], 1
+// CHECK:    store i32 [[ADD4]], ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP22]]
+// CHECK:    br label %[[OMP_INNER_FOR_COND]], !llvm.loop [[LOOP23:![0-9]+]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    br label %[[OMP_LOOP_EXIT:.*]]
+// CHECK:       [[OMP_LOOP_EXIT]]:
+// CHECK:    call void @__kmpc_for_static_fini(ptr @[[GLOB2]], i32 [[TMP3]])
+// CHECK:    [[TMP14:%.*]] = load i32, ptr [[DOTOMP_IS_LAST]], align 4
+// CHECK:    [[TMP15:%.*]] = icmp ne i32 [[TMP14]], 0
+// CHECK:    br i1 [[TMP15]], label %[[DOTOMP_FINAL_THEN:.*]], label %[[DOTOMP_FINAL_DONE:.*]]
+// CHECK:       [[DOTOMP_FINAL_THEN]]:
+// CHECK:    store i32 10, ptr [[I]], align 4
+// CHECK:    br label %[[DOTOMP_FINAL_DONE]]
+// CHECK:       [[DOTOMP_FINAL_DONE]]:
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z28test_target_teams_distributev(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z28test_target_teams_distributev.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP1]], align 8
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_MAPPERS:%.*]], i64 0, i64 0
+// CHECK:    store ptr null, ptr [[TMP3]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 1
+// CHECK:    store ptr null, ptr [[TMP4]], align 8
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 1
+// CHECK:    store ptr null, ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 1
+// CHECK:    store ptr null, ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 0
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 0
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds nuw [[STRUCT___TGT_KERNEL_ARGUMENTS:%.*]], ptr [[KERNEL_ARGS:%.*]], i32 0, i32 0
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z28test_target_teams_distributev_l113(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noalias noundef [[DYN_PTR:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_teams(ptr @[[GLOB1]], i32 1, ptr @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z28test_target_teams_distributev_l113.omp_outlined, ptr [[TMP1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z28test_target_teams_distributev_l113.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 0, ptr [[DOTOMP_LB:%.*]], align 4
+// CHECK:    store i32 9, ptr [[DOTOMP_UB:%.*]], align 4
+// CHECK:    store i32 1, ptr [[DOTOMP_STRIDE:%.*]], align 4
+// CHECK:    store i32 0, ptr [[DOTOMP_IS_LAST:%.*]], align 4
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[A:%.*]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    store i32 [[TMP3]], ptr [[B:%.*]], align 4
+// CHECK:    [[TMP4:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[TMP4]], align 4
+// CHECK:    call void @__kmpc_for_static_init_4(ptr @[[GLOB4:[0-9]+]], i32 [[TMP5]], i32 92, ptr [[DOTOMP_IS_LAST]], ptr [[DOTOMP_LB]], ptr [[DOTOMP_UB]], ptr [[DOTOMP_STRIDE]], i32 1, i32 1)
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP:%.*]] = icmp sgt i32 [[TMP6]], 9
+// CHECK:    br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+// CHECK:       [[COND_TRUE]]:
+// CHECK:    br label %[[COND_END:.*]]
+// CHECK:       [[COND_FALSE]]:
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    br label %[[COND_END]]
+// CHECK:       [[COND_END]]:
+// CHECK:    [[COND:%.*]] = phi i32 [ 9, %[[COND_TRUE]] ], [ [[TMP7]], %[[COND_FALSE]] ]
+// CHECK:    store i32 [[COND]], ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[DOTOMP_LB]], align 4
+// CHECK:    store i32 [[TMP8]], ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP1:%.*]] = icmp sle i32 [[TMP9]], [[TMP10]]
+// CHECK:    br i1 [[CMP1]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP11]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4
+// CHECK:    [[TMP12:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    [[TMP13:%.*]] = load i32, ptr [[B]], align 4
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[TMP12]], [[TMP13]]
+// CHECK:    [[TMP14:%.*]] = load i32, ptr [[I]], align 4
+// CHECK:    [[ADD3:%.*]] = add nsw i32 [[ADD2]], [[TMP14]]
+// CHECK:    store i32 [[ADD3]], ptr [[RESULT:%.*]], align 4
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP15:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[ADD4:%.*]] = add nsw i32 [[TMP15]], 1
+// CHECK:    store i32 [[ADD4]], ptr [[DOTOMP_IV]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    br label %[[OMP_LOOP_EXIT:.*]]
+// CHECK:       [[OMP_LOOP_EXIT]]:
+// CHECK:    call void @__kmpc_for_static_fini(ptr @[[GLOB4]], i32 [[TMP5]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z9test_taskv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[TMP1:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]])
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z9test_taskv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON:%.*]], ptr [[AGG_CAPTURED:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = call ptr @__kmpc_omp_task_alloc(ptr @[[GLOB1]], i32 [[TMP1]], i32 1, i64 48, i64 8, ptr @.omp_task_entry.)
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP6:%.*]] = load ptr, ptr [[TMP5]], align 8
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP6]], ptr align 8 [[AGG_CAPTURED]], i64 8, i1 false)
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T:%.*]], ptr [[TMP7]], i32 0, i32 0
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP9]], ptr [[TMP8]], align 8
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T]], ptr [[TMP7]], i32 0, i32 1
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    store i32 [[TMP11]], ptr [[TMP10]], align 4
+// CHECK:    [[TMP12:%.*]] = call i32 @__kmpc_omp_task(ptr @[[GLOB1]], i32 [[TMP1]], ptr [[TMP3]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @.omp_task_privates_map.(
+// CHECK-SAME: ptr noalias noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]], ptr noalias noundef [[TMP2:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    store ptr [[TMP2]], ptr [[DOTADDR2:%.*]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    store ptr [[TMP4]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[DOTADDR2]], align 8
+// CHECK:    store ptr [[TMP6]], ptr [[TMP7]], align 8
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal noundef i32 @.omp_task_entry.(
+// CHECK-SAME: i32 noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store i32 [[TMP0]], ptr [[DOTADDR:%.*]], align 4
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTADDR]], align 4
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP4]], i32 0, i32 2
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META26:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META29:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META31:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META33:![0-9]+]])
+// CHECK:    store i32 [[TMP2]], ptr [[DOTGLOBAL_TID__ADDR_I:%.*]], align 4, !noalias [[META35:![0-9]+]]
+// CHECK:    store ptr [[TMP5]], ptr [[DOTPART_ID__ADDR_I:%.*]], align 8, !noalias [[META35]]
+// CHECK:    store ptr [[TMP8]], ptr [[DOTPRIVATES__ADDR_I:%.*]], align 8, !noalias [[META35]]
+// CHECK:    store ptr @.omp_task_privates_map., ptr [[DOTCOPY_FN__ADDR_I:%.*]], align 8, !noalias [[META35]]
+// CHECK:    store ptr [[TMP3]], ptr [[DOTTASK_T__ADDR_I:%.*]], align 8, !noalias [[META35]]
+// CHECK:    store ptr [[TMP7]], ptr [[__CONTEXT_ADDR_I:%.*]], align 8, !noalias [[META35]]
+// CHECK:    [[TMP9:%.*]] = load ptr, ptr [[__CONTEXT_ADDR_I]], align 8, !noalias [[META35]]
+// CHECK:    [[TMP10:%.*]] = load ptr, ptr [[DOTCOPY_FN__ADDR_I]], align 8, !noalias [[META35]]
+// CHECK:    [[TMP11:%.*]] = load ptr, ptr [[DOTPRIVATES__ADDR_I]], align 8, !noalias [[META35]]
+// CHECK:    call void [[TMP10]](ptr [[TMP11]], ptr [[DOTFIRSTPRIV_PTR_ADDR_I:%.*]], ptr [[DOTFIRSTPRIV_PTR_ADDR1_I:%.*]]) #{{[0-9]+}}, !inline_history [[META36:![0-9]+]]
+// CHECK:    [[TMP12:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR_I]], align 8, !noalias [[META35]]
+// CHECK:    [[TMP13:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR1_I]], align 8, !noalias [[META35]]
+// CHECK:    [[TMP14:%.*]] = load i32, ptr [[TMP12]], align 4
+// CHECK:    [[TMP15:%.*]] = load i32, ptr [[TMP13]], align 4
+// CHECK:    [[ADD_I:%.*]] = add nsw i32 [[TMP14]], [[TMP15]]
+// CHECK:    store i32 [[ADD_I]], ptr [[SUM_I:%.*]], align 4, !noalias [[META35]]
+// CHECK:    ret i32 0
+//
+//
+// CHECK-LABEL: define dso_local void @_Z22test_task_firstprivatev(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[TMP1:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]])
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z22test_task_firstprivatev.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_0:%.*]], ptr [[AGG_CAPTURED:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = call ptr @__kmpc_omp_task_alloc(ptr @[[GLOB1]], i32 [[TMP1]], i32 1, i64 48, i64 8, ptr @.omp_task_entry..11)
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_2:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP6:%.*]] = load ptr, ptr [[TMP5]], align 8
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP6]], ptr align 8 [[AGG_CAPTURED]], i64 8, i1 false)
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_2]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_1:%.*]], ptr [[TMP7]], i32 0, i32 0
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP9]], ptr [[TMP8]], align 8
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_1]], ptr [[TMP7]], i32 0, i32 1
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    store i32 [[TMP11]], ptr [[TMP10]], align 4
+// CHECK:    [[TMP12:%.*]] = call i32 @__kmpc_omp_task(ptr @[[GLOB1]], i32 [[TMP1]], ptr [[TMP3]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @.omp_task_privates_map..10(
+// CHECK-SAME: ptr noalias noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]], ptr noalias noundef [[TMP2:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    store ptr [[TMP2]], ptr [[DOTADDR2:%.*]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_1:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    store ptr [[TMP4]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_1]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[DOTADDR2]], align 8
+// CHECK:    store ptr [[TMP6]], ptr [[TMP7]], align 8
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal noundef i32 @.omp_task_entry..11(
+// CHECK-SAME: i32 noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store i32 [[TMP0]], ptr [[DOTADDR:%.*]], align 4
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTADDR]], align 4
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_2:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP4]], i32 0, i32 2
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_2]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META37:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META40:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META42:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META44:![0-9]+]])
+// CHECK:    store i32 [[TMP2]], ptr [[DOTGLOBAL_TID__ADDR_I:%.*]], align 4, !noalias [[META46:![0-9]+]]
+// CHECK:    store ptr [[TMP5]], ptr [[DOTPART_ID__ADDR_I:%.*]], align 8, !noalias [[META46]]
+// CHECK:    store ptr [[TMP8]], ptr [[DOTPRIVATES__ADDR_I:%.*]], align 8, !noalias [[META46]]
+// CHECK:    store ptr @.omp_task_privates_map..10, ptr [[DOTCOPY_FN__ADDR_I:%.*]], align 8, !noalias [[META46]]
+// CHECK:    store ptr [[TMP3]], ptr [[DOTTASK_T__ADDR_I:%.*]], align 8, !noalias [[META46]]
+// CHECK:    store ptr [[TMP7]], ptr [[__CONTEXT_ADDR_I:%.*]], align 8, !noalias [[META46]]
+// CHECK:    [[TMP9:%.*]] = load ptr, ptr [[__CONTEXT_ADDR_I]], align 8, !noalias [[META46]]
+// CHECK:    [[TMP10:%.*]] = load ptr, ptr [[DOTCOPY_FN__ADDR_I]], align 8, !noalias [[META46]]
+// CHECK:    [[TMP11:%.*]] = load ptr, ptr [[DOTPRIVATES__ADDR_I]], align 8, !noalias [[META46]]
+// CHECK:    call void [[TMP10]](ptr [[TMP11]], ptr [[DOTFIRSTPRIV_PTR_ADDR_I:%.*]], ptr [[DOTFIRSTPRIV_PTR_ADDR1_I:%.*]]) #{{[0-9]+}}, !inline_history [[META47:![0-9]+]]
+// CHECK:    [[TMP12:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR_I]], align 8, !noalias [[META46]]
+// CHECK:    [[TMP13:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR1_I]], align 8, !noalias [[META46]]
+// CHECK:    [[TMP14:%.*]] = load i32, ptr [[TMP12]], align 4
+// CHECK:    [[TMP15:%.*]] = load i32, ptr [[TMP13]], align 4
+// CHECK:    [[ADD_I:%.*]] = add nsw i32 [[TMP14]], [[TMP15]]
+// CHECK:    store i32 [[ADD_I]], ptr [[SUM_I:%.*]], align 4, !noalias [[META46]]
+// CHECK:    ret i32 0
+//
+//
+// CHECK-LABEL: define dso_local void @_Z34test_task_firstprivate_with_sharedv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[TMP1:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]])
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z34test_task_firstprivate_with_sharedv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    store i32 0, ptr [[RA:%.*]], align 4
+// CHECK:    store i32 0, ptr [[RB:%.*]], align 4
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_3:%.*]], ptr [[AGG_CAPTURED:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[RA]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_3]], ptr [[AGG_CAPTURED]], i32 0, i32 1
+// CHECK:    store ptr [[TMP0]], ptr [[TMP3]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_3]], ptr [[AGG_CAPTURED]], i32 0, i32 2
+// CHECK:    store ptr [[RB]], ptr [[TMP4]], align 8
+// CHECK:    [[TMP5:%.*]] = call ptr @__kmpc_omp_task_alloc(ptr @[[GLOB1]], i32 [[TMP1]], i32 1, i64 48, i64 24, ptr @.omp_task_entry..14)
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_5:%.*]], ptr [[TMP5]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP6]], i32 0, i32 0
+// CHECK:    [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP8]], ptr align 8 [[AGG_CAPTURED]], i64 24, i1 false)
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_5]], ptr [[TMP5]], i32 0, i32 1
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_4:%.*]], ptr [[TMP9]], i32 0, i32 0
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP11]], ptr [[TMP10]], align 8
+// CHECK:    [[TMP12:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_4]], ptr [[TMP9]], i32 0, i32 1
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    [[TMP13:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    store i32 [[TMP13]], ptr [[TMP12]], align 4
+// CHECK:    [[TMP14:%.*]] = call i32 @__kmpc_omp_task(ptr @[[GLOB1]], i32 [[TMP1]], ptr [[TMP5]])
+// CHECK:    [[TMP15:%.*]] = call i32 @__kmpc_omp_taskwait(ptr @[[GLOB1]], i32 [[TMP1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @.omp_task_privates_map..13(
+// CHECK-SAME: ptr noalias noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]], ptr noalias noundef [[TMP2:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    store ptr [[TMP2]], ptr [[DOTADDR2:%.*]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_4:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    store ptr [[TMP4]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_4]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[DOTADDR2]], align 8
+// CHECK:    store ptr [[TMP6]], ptr [[TMP7]], align 8
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal noundef i32 @.omp_task_entry..14(
+// CHECK-SAME: i32 noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store i32 [[TMP0]], ptr [[DOTADDR:%.*]], align 4
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTADDR]], align 4
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_5:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP4]], i32 0, i32 2
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_5]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META48:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META51:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META53:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META55:![0-9]+]])
+// CHECK:    store i32 [[TMP2]], ptr [[DOTGLOBAL_TID__ADDR_I:%.*]], align 4, !noalias [[META57:![0-9]+]]
+// CHECK:    store ptr [[TMP5]], ptr [[DOTPART_ID__ADDR_I:%.*]], align 8, !noalias [[META57]]
+// CHECK:    store ptr [[TMP8]], ptr [[DOTPRIVATES__ADDR_I:%.*]], align 8, !noalias [[META57]]
+// CHECK:    store ptr @.omp_task_privates_map..13, ptr [[DOTCOPY_FN__ADDR_I:%.*]], align 8, !noalias [[META57]]
+// CHECK:    store ptr [[TMP3]], ptr [[DOTTASK_T__ADDR_I:%.*]], align 8, !noalias [[META57]]
+// CHECK:    store ptr [[TMP7]], ptr [[__CONTEXT_ADDR_I:%.*]], align 8, !noalias [[META57]]
+// CHECK:    [[TMP9:%.*]] = load ptr, ptr [[__CONTEXT_ADDR_I]], align 8, !noalias [[META57]]
+// CHECK:    [[TMP10:%.*]] = load ptr, ptr [[DOTCOPY_FN__ADDR_I]], align 8, !noalias [[META57]]
+// CHECK:    [[TMP11:%.*]] = load ptr, ptr [[DOTPRIVATES__ADDR_I]], align 8, !noalias [[META57]]
+// CHECK:    call void [[TMP10]](ptr [[TMP11]], ptr [[DOTFIRSTPRIV_PTR_ADDR_I:%.*]], ptr [[DOTFIRSTPRIV_PTR_ADDR1_I:%.*]]) #{{[0-9]+}}, !inline_history [[META58:![0-9]+]]
+// CHECK:    [[TMP12:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR_I]], align 8, !noalias [[META57]]
+// CHECK:    [[TMP13:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR1_I]], align 8, !noalias [[META57]]
+// CHECK:    [[TMP14:%.*]] = load i32, ptr [[TMP12]], align 4
+// CHECK:    [[TMP15:%.*]] = load ptr, ptr [[TMP9]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 [[TMP14]], ptr [[TMP15]], align 4
+// CHECK:    [[TMP16:%.*]] = load i32, ptr [[TMP13]], align 4
+// CHECK:    [[TMP17:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_3:%.*]], ptr [[TMP9]], i32 0, i32 2
+// CHECK:    [[TMP18:%.*]] = load ptr, ptr [[TMP17]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 [[TMP16]], ptr [[TMP18]], align 4
+// CHECK:    ret i32 0
+//
+//
+// CHECK-LABEL: define dso_local void @_Z16test_task_dependv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[TMP1:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]])
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z16test_task_dependv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_6:%.*]], ptr [[AGG_CAPTURED:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = call ptr @__kmpc_omp_task_alloc(ptr @[[GLOB1]], i32 [[TMP1]], i32 1, i64 48, i64 8, ptr @.omp_task_entry..17)
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_8:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP6:%.*]] = load ptr, ptr [[TMP5]], align 8
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP6]], ptr align 8 [[AGG_CAPTURED]], i64 8, i1 false)
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_8]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_7:%.*]], ptr [[TMP7]], i32 0, i32 0
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP9]], ptr [[TMP8]], align 8
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_7]], ptr [[TMP7]], i32 0, i32 1
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    store i32 [[TMP11]], ptr [[TMP10]], align 4
+// CHECK:    [[TMP12:%.*]] = getelementptr inbounds [2 x [[STRUCT_KMP_DEPEND_INFO:%.*]]], ptr [[DOTDEP_ARR_ADDR:%.*]], i64 0, i64 0
+// CHECK:    [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP13:%.*]] = ptrtoint ptr [[X1]] to i64
+// CHECK:    [[TMP14:%.*]] = getelementptr [[STRUCT_KMP_DEPEND_INFO]], ptr [[TMP12]], i64 0
+// CHECK:    [[TMP15:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_DEPEND_INFO]], ptr [[TMP14]], i32 0, i32 0
+// CHECK:    store i64 [[TMP13]], ptr [[TMP15]], align 8
+// CHECK:    [[TMP16:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_DEPEND_INFO]], ptr [[TMP14]], i32 0, i32 1
+// CHECK:    store i64 4, ptr [[TMP16]], align 8
+// CHECK:    [[TMP17:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_DEPEND_INFO]], ptr [[TMP14]], i32 0, i32 2
+// CHECK:    store i8 1, ptr [[TMP17]], align 8
+// CHECK:    [[Y2:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    [[TMP18:%.*]] = ptrtoint ptr [[Y2]] to i64
+// CHECK:    [[TMP19:%.*]] = getelementptr [[STRUCT_KMP_DEPEND_INFO]], ptr [[TMP12]], i64 1
+// CHECK:    [[TMP20:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_DEPEND_INFO]], ptr [[TMP19]], i32 0, i32 0
+// CHECK:    store i64 [[TMP18]], ptr [[TMP20]], align 8
+// CHECK:    [[TMP21:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_DEPEND_INFO]], ptr [[TMP19]], i32 0, i32 1
+// CHECK:    store i64 4, ptr [[TMP21]], align 8
+// CHECK:    [[TMP22:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_DEPEND_INFO]], ptr [[TMP19]], i32 0, i32 2
+// CHECK:    store i8 1, ptr [[TMP22]], align 8
+// CHECK:    store i64 2, ptr [[DEP_COUNTER_ADDR:%.*]], align 8
+// CHECK:    [[TMP23:%.*]] = call i32 @__kmpc_omp_task_with_deps(ptr @[[GLOB1]], i32 [[TMP1]], ptr [[TMP3]], i32 2, ptr [[TMP12]], i32 0, ptr null)
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @.omp_task_privates_map..16(
+// CHECK-SAME: ptr noalias noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]], ptr noalias noundef [[TMP2:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    store ptr [[TMP2]], ptr [[DOTADDR2:%.*]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_7:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    store ptr [[TMP4]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_7]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[DOTADDR2]], align 8
+// CHECK:    store ptr [[TMP6]], ptr [[TMP7]], align 8
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal noundef i32 @.omp_task_entry..17(
+// CHECK-SAME: i32 noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store i32 [[TMP0]], ptr [[DOTADDR:%.*]], align 4
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTADDR]], align 4
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_8:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP4]], i32 0, i32 2
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_8]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META59:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META62:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META64:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META66:![0-9]+]])
+// CHECK:    store i32 [[TMP2]], ptr [[DOTGLOBAL_TID__ADDR_I:%.*]], align 4, !noalias [[META68:![0-9]+]]
+// CHECK:    store ptr [[TMP5]], ptr [[DOTPART_ID__ADDR_I:%.*]], align 8, !noalias [[META68]]
+// CHECK:    store ptr [[TMP8]], ptr [[DOTPRIVATES__ADDR_I:%.*]], align 8, !noalias [[META68]]
+// CHECK:    store ptr @.omp_task_privates_map..16, ptr [[DOTCOPY_FN__ADDR_I:%.*]], align 8, !noalias [[META68]]
+// CHECK:    store ptr [[TMP3]], ptr [[DOTTASK_T__ADDR_I:%.*]], align 8, !noalias [[META68]]
+// CHECK:    store ptr [[TMP7]], ptr [[__CONTEXT_ADDR_I:%.*]], align 8, !noalias [[META68]]
+// CHECK:    [[TMP9:%.*]] = load ptr, ptr [[__CONTEXT_ADDR_I]], align 8, !noalias [[META68]]
+// CHECK:    [[TMP10:%.*]] = load ptr, ptr [[DOTCOPY_FN__ADDR_I]], align 8, !noalias [[META68]]
+// CHECK:    [[TMP11:%.*]] = load ptr, ptr [[DOTPRIVATES__ADDR_I]], align 8, !noalias [[META68]]
+// CHECK:    call void [[TMP10]](ptr [[TMP11]], ptr [[DOTFIRSTPRIV_PTR_ADDR_I:%.*]], ptr [[DOTFIRSTPRIV_PTR_ADDR1_I:%.*]]) #{{[0-9]+}}, !inline_history [[META69:![0-9]+]]
+// CHECK:    [[TMP12:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR_I]], align 8, !noalias [[META68]]
+// CHECK:    [[TMP13:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR1_I]], align 8, !noalias [[META68]]
+// CHECK:    [[TMP14:%.*]] = load i32, ptr [[TMP12]], align 4
+// CHECK:    [[TMP15:%.*]] = load i32, ptr [[TMP13]], align 4
+// CHECK:    [[ADD_I:%.*]] = add nsw i32 [[TMP14]], [[TMP15]]
+// CHECK:    store i32 [[ADD_I]], ptr [[SUM_I:%.*]], align 4, !noalias [[META68]]
+// CHECK:    ret i32 0
+//
+//
+// CHECK-LABEL: define dso_local void @_Z14test_taskloop_v(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[TMP1:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]])
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z14test_taskloop_v.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_9:%.*]], ptr [[AGG_CAPTURED:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP2]], align 8
+// CHECK:    call void @__kmpc_taskgroup(ptr @[[GLOB1]], i32 [[TMP1]])
+// CHECK:    [[TMP3:%.*]] = call ptr @__kmpc_omp_task_alloc(ptr @[[GLOB1]], i32 [[TMP1]], i32 1, i64 88, i64 8, ptr @.omp_task_entry..20)
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_11:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12:%.*]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP6:%.*]] = load ptr, ptr [[TMP5]], align 8
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP6]], ptr align 8 [[AGG_CAPTURED]], i64 8, i1 false)
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_11]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_10:%.*]], ptr [[TMP7]], i32 0, i32 0
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP9]], ptr [[TMP8]], align 8
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_10]], ptr [[TMP7]], i32 0, i32 1
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    store i32 [[TMP11]], ptr [[TMP10]], align 4
+// CHECK:    [[TMP12:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 5
+// CHECK:    store i64 0, ptr [[TMP12]], align 8
+// CHECK:    [[TMP13:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 6
+// CHECK:    store i64 9, ptr [[TMP13]], align 8
+// CHECK:    [[TMP14:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 7
+// CHECK:    store i64 1, ptr [[TMP14]], align 8
+// CHECK:    [[TMP15:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 9
+// CHECK:    call void @llvm.memset.p0.i64(ptr align 8 [[TMP15]], i8 0, i64 8, i1 false)
+// CHECK:    [[TMP16:%.*]] = load i64, ptr [[TMP14]], align 8
+// CHECK:    call void @__kmpc_taskloop(ptr @[[GLOB1]], i32 [[TMP1]], ptr [[TMP3]], i32 1, ptr [[TMP12]], ptr [[TMP13]], i64 [[TMP16]], i32 1, i32 0, i64 0, ptr null)
+// CHECK:    call void @__kmpc_end_taskgroup(ptr @[[GLOB1]], i32 [[TMP1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @.omp_task_privates_map..19(
+// CHECK-SAME: ptr noalias noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]], ptr noalias noundef [[TMP2:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    store ptr [[TMP2]], ptr [[DOTADDR2:%.*]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_10:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    store ptr [[TMP4]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_10]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[DOTADDR2]], align 8
+// CHECK:    store ptr [[TMP6]], ptr [[TMP7]], align 8
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal noundef i32 @.omp_task_entry..20(
+// CHECK-SAME: i32 noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store i32 [[TMP0]], ptr [[DOTADDR:%.*]], align 4
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTADDR]], align 4
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_11:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12:%.*]], ptr [[TMP4]], i32 0, i32 2
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_11]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 5
+// CHECK:    [[TMP10:%.*]] = load i64, ptr [[TMP9]], align 8
+// CHECK:    [[TMP11:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 6
+// CHECK:    [[TMP12:%.*]] = load i64, ptr [[TMP11]], align 8
+// CHECK:    [[TMP13:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 7
+// CHECK:    [[TMP14:%.*]] = load i64, ptr [[TMP13]], align 8
+// CHECK:    [[TMP15:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 8
+// CHECK:    [[TMP16:%.*]] = load i32, ptr [[TMP15]], align 8
+// CHECK:    [[TMP17:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 9
+// CHECK:    [[TMP18:%.*]] = load ptr, ptr [[TMP17]], align 8
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META70:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META73:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META75:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META77:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META79:![0-9]+]])
+// CHECK:    store i32 [[TMP2]], ptr [[DOTGLOBAL_TID__ADDR_I:%.*]], align 4, !noalias [[META81:![0-9]+]]
+// CHECK:    store ptr [[TMP5]], ptr [[DOTPART_ID__ADDR_I:%.*]], align 8, !noalias [[META81]]
+// CHECK:    store ptr [[TMP8]], ptr [[DOTPRIVATES__ADDR_I:%.*]], align 8, !noalias [[META81]]
+// CHECK:    store ptr @.omp_task_privates_map..19, ptr [[DOTCOPY_FN__ADDR_I:%.*]], align 8, !noalias [[META81]]
+// CHECK:    store ptr [[TMP3]], ptr [[DOTTASK_T__ADDR_I:%.*]], align 8, !noalias [[META81]]
+// CHECK:    store i64 [[TMP10]], ptr [[DOTLB__ADDR_I:%.*]], align 8, !noalias [[META81]]
+// CHECK:    store i64 [[TMP12]], ptr [[DOTUB__ADDR_I:%.*]], align 8, !noalias [[META81]]
+// CHECK:    store i64 [[TMP14]], ptr [[DOTST__ADDR_I:%.*]], align 8, !noalias [[META81]]
+// CHECK:    store i32 [[TMP16]], ptr [[DOTLITER__ADDR_I:%.*]], align 4, !noalias [[META81]]
+// CHECK:    store ptr [[TMP18]], ptr [[DOTREDUCTIONS__ADDR_I:%.*]], align 8, !noalias [[META81]]
+// CHECK:    store ptr [[TMP7]], ptr [[__CONTEXT_ADDR_I:%.*]], align 8, !noalias [[META81]]
+// CHECK:    [[TMP19:%.*]] = load ptr, ptr [[__CONTEXT_ADDR_I]], align 8, !noalias [[META81]]
+// CHECK:    [[TMP20:%.*]] = load ptr, ptr [[DOTCOPY_FN__ADDR_I]], align 8, !noalias [[META81]]
+// CHECK:    [[TMP21:%.*]] = load ptr, ptr [[DOTPRIVATES__ADDR_I]], align 8, !noalias [[META81]]
+// CHECK:    call void [[TMP20]](ptr [[TMP21]], ptr [[DOTFIRSTPRIV_PTR_ADDR_I:%.*]], ptr [[DOTFIRSTPRIV_PTR_ADDR1_I:%.*]]) #{{[0-9]+}}, !inline_history [[META82:![0-9]+]]
+// CHECK:    [[TMP22:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR_I]], align 8, !noalias [[META81]]
+// CHECK:    [[TMP23:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR1_I]], align 8, !noalias [[META81]]
+// CHECK:    [[TMP24:%.*]] = load i64, ptr [[DOTLB__ADDR_I]], align 8, !noalias [[META81]]
+// CHECK:    [[CONV_I:%.*]] = trunc i64 [[TMP24]] to i32
+// CHECK:    store i32 [[CONV_I]], ptr [[DOTOMP_IV_I:%.*]], align 4, !noalias [[META81]]
+// CHECK:    [[TMP25:%.*]] = load ptr, ptr [[TMP19]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    br label %[[OMP_INNER_FOR_COND_I:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND_I]]:
+// CHECK:    [[TMP26:%.*]] = load i32, ptr [[DOTOMP_IV_I]], align 4, !noalias [[META81]]
+// CHECK:    [[CONV2_I:%.*]] = sext i32 [[TMP26]] to i64
+// CHECK:    [[TMP27:%.*]] = load i64, ptr [[DOTUB__ADDR_I]], align 8, !noalias [[META81]]
+// CHECK:    [[CMP_I:%.*]] = icmp ule i64 [[CONV2_I]], [[TMP27]]
+// CHECK:    br i1 [[CMP_I]], label %[[OMP_INNER_FOR_BODY_I:.*]], label %[[DOTOMP_OUTLINED__18_EXIT:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY_I]]:
+// CHECK:    [[TMP28:%.*]] = load i32, ptr [[DOTOMP_IV_I]], align 4, !noalias [[META81]]
+// CHECK:    store i32 [[TMP28]], ptr [[I_I:%.*]], align 4, !noalias [[META81]]
+// CHECK:    [[TMP29:%.*]] = load i32, ptr [[TMP22]], align 4
+// CHECK:    [[TMP30:%.*]] = load i32, ptr [[TMP23]], align 4
+// CHECK:    [[ADD3_I:%.*]] = add nsw i32 [[TMP29]], [[TMP30]]
+// CHECK:    [[TMP31:%.*]] = load i32, ptr [[I_I]], align 4, !noalias [[META81]]
+// CHECK:    [[ADD4_I:%.*]] = add nsw i32 [[ADD3_I]], [[TMP31]]
+// CHECK:    store i32 [[ADD4_I]], ptr [[RESULT_I:%.*]], align 4, !noalias [[META81]]
+// CHECK:    [[TMP32:%.*]] = load i32, ptr [[DOTOMP_IV_I]], align 4, !noalias [[META81]]
+// CHECK:    [[ADD5_I:%.*]] = add nsw i32 [[TMP32]], 1
+// CHECK:    store i32 [[ADD5_I]], ptr [[DOTOMP_IV_I]], align 4, !noalias [[META81]]
+// CHECK:    br label %[[OMP_INNER_FOR_COND_I]]
+// CHECK:       [[DOTOMP_OUTLINED__18_EXIT]]:
+// CHECK:    ret i32 0
+//
+//
+// CHECK-LABEL: define dso_local void @_Z21instantiate_templatesv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P2:%.*]], ptr align 4 @__const._Z21instantiate_templatesv.p2, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P3:%.*]], ptr align 4 @__const._Z21instantiate_templatesv.p3, i64 12, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[AGG_TMP:%.*]], ptr align 4 [[P2]], i64 8, i1 false)
+// CHECK:    [[TMP0:%.*]] = load i64, ptr [[AGG_TMP]], align 4
+// CHECK:    [[CALL:%.*]] = call noundef i32 @_Z17test_template_basI5PointEiT_(i64 [[TMP0]])
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[AGG_TMP1:%.*]], i32 0, i32 0
+// CHECK:    store i32 3, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[AGG_TMP1]], i32 0, i32 1
+// CHECK:    store i32 4, ptr [[Y]], align 4
+// CHECK:    [[TMP1:%.*]] = load i64, ptr [[AGG_TMP1]], align 4
+// CHECK:    [[CALL2:%.*]] = call noundef i32 @_Z17test_template_basI5PointEiT_(i64 [[TMP1]])
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[AGG_TMP3:%.*]], ptr align 4 [[P2]], i64 8, i1 false)
+// CHECK:    [[TMP2:%.*]] = load i64, ptr [[AGG_TMP3]], align 4
+// CHECK:    [[CALL4:%.*]] = call noundef i32 @_Z20test_template_targetI5PointEiT_(i64 [[TMP2]])
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[AGG_TMP5:%.*]], ptr align 4 [[P2]], i64 8, i1 false)
+// CHECK:    [[TMP3:%.*]] = load i64, ptr [[AGG_TMP5]], align 4
+// CHECK:    [[CALL6:%.*]] = call noundef i32 @_Z18test_template_taskI5PointEiT_(i64 [[TMP3]])
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[AGG_TMP7:%.*]], ptr align 4 [[P3]], i64 12, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[AGG_TMP7_COERCE:%.*]], ptr align 4 [[AGG_TMP7]], i64 12, i1 false)
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw { i64, i32 }, ptr [[AGG_TMP7_COERCE]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = load i64, ptr [[TMP4]], align 4
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw { i64, i32 }, ptr [[AGG_TMP7_COERCE]], i32 0, i32 1
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[TMP6]], align 4
+// CHECK:    [[CALL8:%.*]] = call noundef i32 @_Z24test_template_3_bindingsI7Point3DEiT_(i64 [[TMP5]], i32 [[TMP7]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr noundef i32 @_Z17test_template_basI5PointEiT_(
+// CHECK-SAME: i64 [[P_COERCE:%.*]]) #{{[0-9]+}} comdat {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store i64 [[P_COERCE]], ptr [[P:%.*]], align 4
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    store i32 0, ptr [[RESULT:%.*]], align 4
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 2, ptr @_Z17test_template_basI5PointEiT_.omp_outlined, ptr [[RESULT]], ptr [[TMP0]])
+// CHECK:    [[TMP1:%.*]] = load i32, ptr [[RESULT]], align 4
+// CHECK:    ret i32 [[TMP1]]
+//
+//
+// CHECK-LABEL: define linkonce_odr noundef i32 @_Z20test_template_targetI5PointEiT_(
+// CHECK-SAME: i64 [[P_COERCE:%.*]]) #{{[0-9]+}} comdat {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store i64 [[P_COERCE]], ptr [[P:%.*]], align 4
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    store i32 0, ptr [[RESULT:%.*]], align 4
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[RESULT]], ptr [[TMP1]], align 8
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[RESULT]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_MAPPERS:%.*]], i64 0, i64 0
+// CHECK:    store ptr null, ptr [[TMP3]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 1
+// CHECK:    store ptr [[TMP0]], ptr [[TMP4]], align 8
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 1
+// CHECK:    store ptr [[TMP0]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 1
+// CHECK:    store ptr null, ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 2
+// CHECK:    store ptr null, ptr [[TMP7]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 2
+// CHECK:    store ptr null, ptr [[TMP8]], align 8
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 2
+// CHECK:    store ptr null, ptr [[TMP9]], align 8
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 0
+// CHECK:    [[TMP11:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 0
+// CHECK:    [[TMP12:%.*]] = getelementptr inbounds nuw [[STRUCT___TGT_KERNEL_ARGUMENTS:%.*]], ptr [[KERNEL_ARGS:%.*]], i32 0, i32 0
+//
+//
+// CHECK-LABEL: define linkonce_odr noundef i32 @_Z18test_template_taskI5PointEiT_(
+// CHECK-SAME: i64 [[P_COERCE:%.*]]) #{{[0-9]+}} comdat {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[TMP1:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]])
+// CHECK:    store i64 [[P_COERCE]], ptr [[P:%.*]], align 4
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    store i32 0, ptr [[RESULT:%.*]], align 4
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_14:%.*]], ptr [[AGG_CAPTURED:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[RESULT]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_14]], ptr [[AGG_CAPTURED]], i32 0, i32 1
+// CHECK:    store ptr [[TMP0]], ptr [[TMP3]], align 8
+// CHECK:    [[TMP4:%.*]] = call ptr @__kmpc_omp_task_alloc(ptr @[[GLOB1]], i32 [[TMP1]], i32 1, i64 48, i64 16, ptr @.omp_task_entry..25)
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_16:%.*]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP5]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP7]], ptr align 8 [[AGG_CAPTURED]], i64 16, i1 false)
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_16]], ptr [[TMP4]], i32 0, i32 1
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_15:%.*]], ptr [[TMP8]], i32 0, i32 0
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP10]], ptr [[TMP9]], align 8
+// CHECK:    [[TMP11:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_15]], ptr [[TMP8]], i32 0, i32 1
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    [[TMP12:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    store i32 [[TMP12]], ptr [[TMP11]], align 4
+// CHECK:    [[TMP13:%.*]] = call i32 @__kmpc_omp_task(ptr @[[GLOB1]], i32 [[TMP1]], ptr [[TMP4]])
+// CHECK:    [[TMP14:%.*]] = call i32 @__kmpc_omp_taskwait(ptr @[[GLOB1]], i32 [[TMP1]])
+// CHECK:    [[TMP15:%.*]] = load i32, ptr [[RESULT]], align 4
+// CHECK:    ret i32 [[TMP15]]
+//
+//
+// CHECK-LABEL: define linkonce_odr noundef i32 @_Z24test_template_3_bindingsI7Point3DEiT_(
+// CHECK-SAME: i64 [[P_COERCE0:%.*]], i32 [[P_COERCE1:%.*]]) #{{[0-9]+}} comdat {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds nuw { i64, i32 }, ptr [[COERCE:%.*]], i32 0, i32 0
+// CHECK:    store i64 [[P_COERCE0]], ptr [[TMP1]], align 4
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds nuw { i64, i32 }, ptr [[COERCE]], i32 0, i32 1
+// CHECK:    store i32 [[P_COERCE1]], ptr [[TMP2]], align 4
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 [[COERCE]], i64 12, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 12, i1 false)
+// CHECK:    store i32 0, ptr [[RESULT:%.*]], align 4
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 2, ptr @_Z24test_template_3_bindingsI7Point3DEiT_.omp_outlined, ptr [[RESULT]], ptr [[TMP0]])
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[RESULT]], align 4
+// CHECK:    ret i32 [[TMP3]]
+//
+//
+// CHECK-LABEL: define internal void @_Z17test_template_basI5PointEiT_.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[RESULT:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[RESULT]], ptr [[RESULT_ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[RESULT_ADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 0, ptr [[RESULT1:%.*]], align 4
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP2]], i32 0, i32 0
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP2]], i32 0, i32 1
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP3]], [[TMP4]]
+// CHECK:    store i32 [[ADD]], ptr [[RESULT1]], align 4
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds [1 x ptr], ptr [[DOTOMP_REDUCTION_RED_LIST:%.*]], i64 0, i64 0
+// CHECK:    store ptr [[RESULT1]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[TMP6]], align 4
+// CHECK:    [[TMP8:%.*]] = call i32 @__kmpc_reduce_nowait(ptr @[[GLOB3]], i32 [[TMP7]], i32 1, i64 8, ptr [[DOTOMP_REDUCTION_RED_LIST]], ptr @_Z17test_template_basI5PointEiT_.omp_outlined.omp.reduction.reduction_func, ptr @.gomp_critical_user_.reduction.var)
+// CHECK:    switch i32 [[TMP8]], label %[[DOTOMP_REDUCTION_DEFAULT:.*]] [
+// CHECK:      i32 1, label %[[DOTOMP_REDUCTION_CASE1:.*]]
+// CHECK:      i32 2, label %[[DOTOMP_REDUCTION_CASE2:.*]]
+// CHECK:    ]
+// CHECK:       [[DOTOMP_REDUCTION_CASE1]]:
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[TMP1]], align 4
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[RESULT1]], align 4
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[TMP9]], [[TMP10]]
+// CHECK:    store i32 [[ADD2]], ptr [[TMP1]], align 4
+// CHECK:    call void @__kmpc_end_reduce_nowait(ptr @[[GLOB3]], i32 [[TMP7]], ptr @.gomp_critical_user_.reduction.var)
+// CHECK:    br label %[[DOTOMP_REDUCTION_DEFAULT]]
+// CHECK:       [[DOTOMP_REDUCTION_CASE2]]:
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[RESULT1]], align 4
+// CHECK:    [[TMP12:%.*]] = atomicrmw add ptr [[TMP1]], i32 [[TMP11]] monotonic, align 4
+// CHECK:    br label %[[DOTOMP_REDUCTION_DEFAULT]]
+// CHECK:       [[DOTOMP_REDUCTION_DEFAULT]]:
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z17test_template_basI5PointEiT_.omp_outlined.omp.reduction.reduction_func(
+// CHECK-SAME: ptr noundef [[TMP0:%.*]], ptr noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds [1 x ptr], ptr [[TMP3]], i64 0, i64 0
+// CHECK:    [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [1 x ptr], ptr [[TMP2]], i64 0, i64 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[TMP7]], align 4
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[TMP5]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP8]], [[TMP9]]
+// CHECK:    store i32 [[ADD]], ptr [[TMP7]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z20test_template_targetI5PointEiT__l188(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(4) [[RESULT:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noalias noundef [[DYN_PTR:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[RESULT]], ptr [[RESULT_ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[RESULT_ADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP2]], i32 0, i32 0
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP3]], ptr [[A:%.*]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP2]], i32 0, i32 1
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    store i32 [[TMP4]], ptr [[B:%.*]], align 4
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[B]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP5]], [[TMP6]]
+// CHECK:    store i32 [[ADD]], ptr [[TMP1]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @.omp_task_privates_map..24(
+// CHECK-SAME: ptr noalias noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]], ptr noalias noundef [[TMP2:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    store ptr [[TMP2]], ptr [[DOTADDR2:%.*]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_15:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    store ptr [[TMP4]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_15]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[DOTADDR2]], align 8
+// CHECK:    store ptr [[TMP6]], ptr [[TMP7]], align 8
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal noundef i32 @.omp_task_entry..25(
+// CHECK-SAME: i32 noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store i32 [[TMP0]], ptr [[DOTADDR:%.*]], align 4
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTADDR]], align 4
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_16:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP4]], i32 0, i32 2
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_16]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META83:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META86:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META88:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META90:![0-9]+]])
+// CHECK:    store i32 [[TMP2]], ptr [[DOTGLOBAL_TID__ADDR_I:%.*]], align 4, !noalias [[META92:![0-9]+]]
+// CHECK:    store ptr [[TMP5]], ptr [[DOTPART_ID__ADDR_I:%.*]], align 8, !noalias [[META92]]
+// CHECK:    store ptr [[TMP8]], ptr [[DOTPRIVATES__ADDR_I:%.*]], align 8, !noalias [[META92]]
+// CHECK:    store ptr @.omp_task_privates_map..24, ptr [[DOTCOPY_FN__ADDR_I:%.*]], align 8, !noalias [[META92]]
+// CHECK:    store ptr [[TMP3]], ptr [[DOTTASK_T__ADDR_I:%.*]], align 8, !noalias [[META92]]
+// CHECK:    store ptr [[TMP7]], ptr [[__CONTEXT_ADDR_I:%.*]], align 8, !noalias [[META92]]
+// CHECK:    [[TMP9:%.*]] = load ptr, ptr [[__CONTEXT_ADDR_I]], align 8, !noalias [[META92]]
+// CHECK:    [[TMP10:%.*]] = load ptr, ptr [[DOTCOPY_FN__ADDR_I]], align 8, !noalias [[META92]]
+// CHECK:    [[TMP11:%.*]] = load ptr, ptr [[DOTPRIVATES__ADDR_I]], align 8, !noalias [[META92]]
+// CHECK:    call void [[TMP10]](ptr [[TMP11]], ptr [[DOTFIRSTPRIV_PTR_ADDR_I:%.*]], ptr [[DOTFIRSTPRIV_PTR_ADDR1_I:%.*]]) #{{[0-9]+}}, !inline_history [[META93:![0-9]+]]
+// CHECK:    [[TMP12:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR_I]], align 8, !noalias [[META92]]
+// CHECK:    [[TMP13:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR1_I]], align 8, !noalias [[META92]]
+// CHECK:    [[TMP14:%.*]] = load i32, ptr [[TMP12]], align 4
+// CHECK:    [[TMP15:%.*]] = load i32, ptr [[TMP13]], align 4
+// CHECK:    [[ADD_I:%.*]] = add nsw i32 [[TMP14]], [[TMP15]]
+// CHECK:    [[TMP16:%.*]] = load ptr, ptr [[TMP9]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 [[ADD_I]], ptr [[TMP16]], align 4
+// CHECK:    ret i32 0
+//
+//
+// CHECK-LABEL: define internal void @_Z24test_template_3_bindingsI7Point3DEiT_.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[RESULT:%.*]], ptr noundef nonnull align 4 dereferenceable(12) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[RESULT]], ptr [[RESULT_ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[RESULT_ADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 0, ptr [[RESULT1:%.*]], align 4
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT3D:%.*]], ptr [[TMP2]], i32 0, i32 0
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT3D]], ptr [[TMP2]], i32 0, i32 1
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP3]], [[TMP4]]
+// CHECK:    [[Z:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT3D]], ptr [[TMP2]], i32 0, i32 2
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[Z]], align 4
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[ADD]], [[TMP5]]
+// CHECK:    store i32 [[ADD2]], ptr [[RESULT1]], align 4
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [1 x ptr], ptr [[DOTOMP_REDUCTION_RED_LIST:%.*]], i64 0, i64 0
+// CHECK:    store ptr [[RESULT1]], ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[TMP7]], align 4
+// CHECK:    [[TMP9:%.*]] = call i32 @__kmpc_reduce_nowait(ptr @[[GLOB3]], i32 [[TMP8]], i32 1, i64 8, ptr [[DOTOMP_REDUCTION_RED_LIST]], ptr @_Z24test_template_3_bindingsI7Point3DEiT_.omp_outlined.omp.reduction.reduction_func, ptr @.gomp_critical_user_.reduction.var)
+// CHECK:    switch i32 [[TMP9]], label %[[DOTOMP_REDUCTION_DEFAULT:.*]] [
+// CHECK:      i32 1, label %[[DOTOMP_REDUCTION_CASE1:.*]]
+// CHECK:      i32 2, label %[[DOTOMP_REDUCTION_CASE2:.*]]
+// CHECK:    ]
+// CHECK:       [[DOTOMP_REDUCTION_CASE1]]:
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[TMP1]], align 4
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[RESULT1]], align 4
+// CHECK:    [[ADD3:%.*]] = add nsw i32 [[TMP10]], [[TMP11]]
+// CHECK:    store i32 [[ADD3]], ptr [[TMP1]], align 4
+// CHECK:    call void @__kmpc_end_reduce_nowait(ptr @[[GLOB3]], i32 [[TMP8]], ptr @.gomp_critical_user_.reduction.var)
+// CHECK:    br label %[[DOTOMP_REDUCTION_DEFAULT]]
+// CHECK:       [[DOTOMP_REDUCTION_CASE2]]:
+// CHECK:    [[TMP12:%.*]] = load i32, ptr [[RESULT1]], align 4
+// CHECK:    [[TMP13:%.*]] = atomicrmw add ptr [[TMP1]], i32 [[TMP12]] monotonic, align 4
+// CHECK:    br label %[[DOTOMP_REDUCTION_DEFAULT]]
+// CHECK:       [[DOTOMP_REDUCTION_DEFAULT]]:
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z24test_template_3_bindingsI7Point3DEiT_.omp_outlined.omp.reduction.reduction_func(
+// CHECK-SAME: ptr noundef [[TMP0:%.*]], ptr noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds [1 x ptr], ptr [[TMP3]], i64 0, i64 0
+// CHECK:    [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [1 x ptr], ptr [[TMP2]], i64 0, i64 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[TMP7]], align 4
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[TMP5]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP8]], [[TMP9]]
+// CHECK:    store i32 [[ADD]], ptr [[TMP7]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z19test_static_bindingv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[TMP0:%.*]] = load atomic i8, ptr @_ZGVZ19test_static_bindingvEDC1a1bE acquire, align 8
+// CHECK:    [[GUARD_UNINITIALIZED:%.*]] = icmp eq i8 [[TMP0]], 0
+// CHECK:    br i1 [[GUARD_UNINITIALIZED]], label %[[INIT_CHECK:.*]], label %[[INIT_END:.*]], !prof [[PROF94:![0-9]+]]
+// CHECK:       [[INIT_CHECK]]:
+// CHECK:    [[TMP1:%.*]] = call i32 @__cxa_guard_acquire(ptr @_ZGVZ19test_static_bindingvEDC1a1bE) #{{[0-9]+}}
+// CHECK:    [[TOBOOL:%.*]] = icmp ne i32 [[TMP1]], 0
+// CHECK:    br i1 [[TOBOOL]], label %[[INIT:.*]], label %[[INIT_END]]
+// CHECK:       [[INIT]]:
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 @_ZZ19test_static_bindingvEDC1a1bE, ptr align 4 @_ZZ19test_static_bindingvE1p, i64 8, i1 false)
+// CHECK:    call void @__cxa_guard_release(ptr @_ZGVZ19test_static_bindingvEDC1a1bE) #{{[0-9]+}}
+// CHECK:    br label %[[INIT_END]]
+// CHECK:       [[INIT_END]]:
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @_Z19test_static_bindingv.omp_outlined)
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z19test_static_bindingv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    [[TMP0:%.*]] = load i32, ptr @_ZZ19test_static_bindingvEDC1a1bE, align 4
+// CHECK:    [[TMP1:%.*]] = load i32, ptr getelementptr inbounds nuw (i8, ptr @_ZZ19test_static_bindingvEDC1a1bE, i64 4), align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP0]], [[TMP1]]
+// CHECK:    store i32 [[ADD]], ptr [[SUM:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z26test_static_binding_sharedv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[TMP0:%.*]] = load atomic i8, ptr @_ZGVZ26test_static_binding_sharedvEDC1a1bE acquire, align 8
+// CHECK:    [[GUARD_UNINITIALIZED:%.*]] = icmp eq i8 [[TMP0]], 0
+// CHECK:    br i1 [[GUARD_UNINITIALIZED]], label %[[INIT_CHECK:.*]], label %[[INIT_END:.*]], !prof [[PROF94]]
+// CHECK:       [[INIT_CHECK]]:
+// CHECK:    [[TMP1:%.*]] = call i32 @__cxa_guard_acquire(ptr @_ZGVZ26test_static_binding_sharedvEDC1a1bE) #{{[0-9]+}}
+// CHECK:    [[TOBOOL:%.*]] = icmp ne i32 [[TMP1]], 0
+// CHECK:    br i1 [[TOBOOL]], label %[[INIT:.*]], label %[[INIT_END]]
+// CHECK:       [[INIT]]:
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 @_ZZ26test_static_binding_sharedvEDC1a1bE, ptr align 4 @_ZZ26test_static_binding_sharedvE1p, i64 8, i1 false)
+// CHECK:    call void @__cxa_guard_release(ptr @_ZGVZ26test_static_binding_sharedvEDC1a1bE) #{{[0-9]+}}
+// CHECK:    br label %[[INIT_END]]
+// CHECK:       [[INIT_END]]:
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @_Z26test_static_binding_sharedv.omp_outlined)
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z26test_static_binding_sharedv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    [[TMP0:%.*]] = load i32, ptr @_ZZ26test_static_binding_sharedvEDC1a1bE, align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP0]], 1
+// CHECK:    store i32 [[ADD]], ptr @_ZZ26test_static_binding_sharedvEDC1a1bE, align 4
+// CHECK:    [[TMP1:%.*]] = load i32, ptr getelementptr inbounds nuw (i8, ptr @_ZZ26test_static_binding_sharedvEDC1a1bE, i64 4), align 4
+// CHECK:    [[ADD1:%.*]] = add nsw i32 [[TMP1]], 1
+// CHECK:    store i32 [[ADD1]], ptr getelementptr inbounds nuw (i8, ptr @_ZZ26test_static_binding_sharedvEDC1a1bE, i64 4), align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z17test_array_targetv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*]]:
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR:%.*]], ptr align 4 @__const._Z17test_array_targetv.arr, i64 8, i1 false)
+// CHECK:    [[ARRAYINIT_BEGIN:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP0:%.*]], i64 0, i64 0
+// CHECK:    br label %[[ARRAYINIT_BODY:.*]]
+// CHECK:       [[ARRAYINIT_BODY]]:
+// CHECK:    [[ARRAYINIT_INDEX:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[ARRAYINIT_NEXT:%.*]], %[[ARRAYINIT_BODY]] ]
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[ARRAYINIT_BEGIN]], i64 [[ARRAYINIT_INDEX]]
+// CHECK:    [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [2 x i32], ptr [[ARR]], i64 0, i64 [[ARRAYINIT_INDEX]]
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[TMP1]], align 4
+// CHECK:    [[ARRAYINIT_NEXT]] = add nuw i64 [[ARRAYINIT_INDEX]], 1
+// CHECK:    [[ARRAYINIT_DONE:%.*]] = icmp eq i64 [[ARRAYINIT_NEXT]], 2
+// CHECK:    br i1 [[ARRAYINIT_DONE]], label %[[ARRAYINIT_END:.*]], label %[[ARRAYINIT_BODY]]
+// CHECK:       [[ARRAYINIT_END]]:
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP3]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP4]], align 8
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_MAPPERS:%.*]], i64 0, i64 0
+// CHECK:    store ptr null, ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 1
+// CHECK:    store ptr null, ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 1
+// CHECK:    store ptr null, ptr [[TMP7]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 1
+// CHECK:    store ptr null, ptr [[TMP8]], align 8
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 0
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds [2 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 0
+// CHECK:    [[TMP11:%.*]] = getelementptr inbounds nuw [[STRUCT___TGT_KERNEL_ARGUMENTS:%.*]], ptr [[KERNEL_ARGS:%.*]], i32 0, i32 0
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z17test_array_targetv_l256(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noalias noundef [[DYN_PTR:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP1]], i64 0, i64 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[A:%.*]], align 4
+// CHECK:    [[ARRAYIDX1:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP1]], i64 0, i64 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX1]], align 4
+// CHECK:    store i32 [[TMP3]], ptr [[B:%.*]], align 4
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[B]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP4]], [[TMP5]]
+// CHECK:    store i32 [[ADD]], ptr [[SUM:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z15test_array_taskv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*]]:
+// CHECK:    [[TMP1:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]])
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR:%.*]], ptr align 4 @__const._Z15test_array_taskv.arr, i64 8, i1 false)
+// CHECK:    [[ARRAYINIT_BEGIN:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP0:%.*]], i64 0, i64 0
+// CHECK:    br label %[[ARRAYINIT_BODY:.*]]
+// CHECK:       [[ARRAYINIT_BODY]]:
+// CHECK:    [[ARRAYINIT_INDEX:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[ARRAYINIT_NEXT:%.*]], %[[ARRAYINIT_BODY]] ]
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[ARRAYINIT_BEGIN]], i64 [[ARRAYINIT_INDEX]]
+// CHECK:    [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [2 x i32], ptr [[ARR]], i64 0, i64 [[ARRAYINIT_INDEX]]
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
+// CHECK:    store i32 [[TMP3]], ptr [[TMP2]], align 4
+// CHECK:    [[ARRAYINIT_NEXT]] = add nuw i64 [[ARRAYINIT_INDEX]], 1
+// CHECK:    [[ARRAYINIT_DONE:%.*]] = icmp eq i64 [[ARRAYINIT_NEXT]], 2
+// CHECK:    br i1 [[ARRAYINIT_DONE]], label %[[ARRAYINIT_END:.*]], label %[[ARRAYINIT_BODY]]
+// CHECK:       [[ARRAYINIT_END]]:
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_17:%.*]], ptr [[AGG_CAPTURED:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP4]], align 8
+// CHECK:    [[TMP5:%.*]] = call ptr @__kmpc_omp_task_alloc(ptr @[[GLOB1]], i32 [[TMP1]], i32 1, i64 48, i64 8, ptr @.omp_task_entry..30)
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_19:%.*]], ptr [[TMP5]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP6]], i32 0, i32 0
+// CHECK:    [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP8]], ptr align 8 [[AGG_CAPTURED]], i64 8, i1 false)
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_19]], ptr [[TMP5]], i32 0, i32 1
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_18:%.*]], ptr [[TMP9]], i32 0, i32 0
+// CHECK:    [[ARRAYIDX1:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP0]], i64 0, i64 0
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[ARRAYIDX1]], align 4
+// CHECK:    store i32 [[TMP11]], ptr [[TMP10]], align 8
+// CHECK:    [[TMP12:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_18]], ptr [[TMP9]], i32 0, i32 1
+// CHECK:    [[ARRAYIDX2:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP0]], i64 0, i64 1
+// CHECK:    [[TMP13:%.*]] = load i32, ptr [[ARRAYIDX2]], align 4
+// CHECK:    store i32 [[TMP13]], ptr [[TMP12]], align 4
+// CHECK:    [[TMP14:%.*]] = call i32 @__kmpc_omp_task(ptr @[[GLOB1]], i32 [[TMP1]], ptr [[TMP5]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @.omp_task_privates_map..29(
+// CHECK-SAME: ptr noalias noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]], ptr noalias noundef [[TMP2:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    store ptr [[TMP2]], ptr [[DOTADDR2:%.*]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_18:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    store ptr [[TMP4]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_18]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[DOTADDR2]], align 8
+// CHECK:    store ptr [[TMP6]], ptr [[TMP7]], align 8
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal noundef i32 @.omp_task_entry..30(
+// CHECK-SAME: i32 noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store i32 [[TMP0]], ptr [[DOTADDR:%.*]], align 4
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTADDR]], align 4
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_19:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP4]], i32 0, i32 2
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_19]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META95:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META98:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META100:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META102:![0-9]+]])
+// CHECK:    store i32 [[TMP2]], ptr [[DOTGLOBAL_TID__ADDR_I:%.*]], align 4, !noalias [[META104:![0-9]+]]
+// CHECK:    store ptr [[TMP5]], ptr [[DOTPART_ID__ADDR_I:%.*]], align 8, !noalias [[META104]]
+// CHECK:    store ptr [[TMP8]], ptr [[DOTPRIVATES__ADDR_I:%.*]], align 8, !noalias [[META104]]
+// CHECK:    store ptr @.omp_task_privates_map..29, ptr [[DOTCOPY_FN__ADDR_I:%.*]], align 8, !noalias [[META104]]
+// CHECK:    store ptr [[TMP3]], ptr [[DOTTASK_T__ADDR_I:%.*]], align 8, !noalias [[META104]]
+// CHECK:    store ptr [[TMP7]], ptr [[__CONTEXT_ADDR_I:%.*]], align 8, !noalias [[META104]]
+// CHECK:    [[TMP9:%.*]] = load ptr, ptr [[__CONTEXT_ADDR_I]], align 8, !noalias [[META104]]
+// CHECK:    [[TMP10:%.*]] = load ptr, ptr [[DOTCOPY_FN__ADDR_I]], align 8, !noalias [[META104]]
+// CHECK:    [[TMP11:%.*]] = load ptr, ptr [[DOTPRIVATES__ADDR_I]], align 8, !noalias [[META104]]
+// CHECK:    call void [[TMP10]](ptr [[TMP11]], ptr [[DOTFIRSTPRIV_PTR_ADDR_I:%.*]], ptr [[DOTFIRSTPRIV_PTR_ADDR1_I:%.*]]) #{{[0-9]+}}, !inline_history [[META105:![0-9]+]]
+// CHECK:    [[TMP12:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR_I]], align 8, !noalias [[META104]]
+// CHECK:    [[TMP13:%.*]] = load ptr, ptr [[DOTFIRSTPRIV_PTR_ADDR1_I]], align 8, !noalias [[META104]]
+// CHECK:    [[TMP14:%.*]] = load i32, ptr [[TMP12]], align 4
+// CHECK:    [[TMP15:%.*]] = load i32, ptr [[TMP13]], align 4
+// CHECK:    [[ADD_I:%.*]] = add nsw i32 [[TMP14]], [[TMP15]]
+// CHECK:    store i32 [[ADD_I]], ptr [[SUM_I:%.*]], align 4, !noalias [[META104]]
+// CHECK:    ret i32 0
+//
+//
+// CHECK-LABEL: define dso_local void @_Z11test_nestedv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z11test_nestedv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z11test_nestedv.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z11test_nestedv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[TMP2]], align 4
+// CHECK:    call void @__kmpc_critical(ptr @[[GLOB1]], i32 [[TMP3]], ptr @.gomp_critical_user_.var)
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP4]], [[TMP5]]
+// CHECK:    store i32 [[ADD]], ptr [[SUM:%.*]], align 4
+// CHECK:    call void @__kmpc_end_critical(ptr @[[GLOB1]], i32 [[TMP3]], ptr @.gomp_critical_user_.var)
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_20:%.*]], ptr [[AGG_CAPTURED:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP1]], ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = call ptr @__kmpc_omp_task_alloc(ptr @[[GLOB1]], i32 [[TMP3]], i32 1, i64 40, i64 8, ptr @.omp_task_entry..32)
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_21:%.*]], ptr [[TMP7]], i32 0, i32 0
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP8]], i32 0, i32 0
+// CHECK:    [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP10]], ptr align 8 [[AGG_CAPTURED]], i64 8, i1 false)
+// CHECK:    [[TMP11:%.*]] = call i32 @__kmpc_omp_task(ptr @[[GLOB1]], i32 [[TMP3]], ptr [[TMP7]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal noundef i32 @.omp_task_entry..32(
+// CHECK-SAME: i32 noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store i32 [[TMP0]], ptr [[DOTADDR:%.*]], align 4
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTADDR]], align 4
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_21:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP4]], i32 0, i32 2
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META106:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META109:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META111:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META113:![0-9]+]])
+// CHECK:    store i32 [[TMP2]], ptr [[DOTGLOBAL_TID__ADDR_I:%.*]], align 4, !noalias [[META115:![0-9]+]]
+// CHECK:    store ptr [[TMP5]], ptr [[DOTPART_ID__ADDR_I:%.*]], align 8, !noalias [[META115]]
+// CHECK:    store ptr null, ptr [[DOTPRIVATES__ADDR_I:%.*]], align 8, !noalias [[META115]]
+// CHECK:    store ptr null, ptr [[DOTCOPY_FN__ADDR_I:%.*]], align 8, !noalias [[META115]]
+// CHECK:    store ptr [[TMP3]], ptr [[DOTTASK_T__ADDR_I:%.*]], align 8, !noalias [[META115]]
+// CHECK:    store ptr [[TMP7]], ptr [[__CONTEXT_ADDR_I:%.*]], align 8, !noalias [[META115]]
+// CHECK:    [[TMP8:%.*]] = load ptr, ptr [[__CONTEXT_ADDR_I]], align 8, !noalias [[META115]]
+// CHECK:    [[TMP9:%.*]] = load ptr, ptr [[TMP8]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[TMP9]], align 4
+// CHECK:    [[TMP11:%.*]] = load ptr, ptr [[TMP8]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[Y_I:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP11]], i32 0, i32 1
+// CHECK:    [[TMP12:%.*]] = load i32, ptr [[Y_I]], align 4
+// CHECK:    [[MUL_I:%.*]] = mul nsw i32 [[TMP10]], [[TMP12]]
+// CHECK:    store i32 [[MUL_I]], ptr [[PRODUCT_I:%.*]], align 4, !noalias [[META115]]
+// CHECK:    ret i32 0
+//
+//
+// CHECK-LABEL: define dso_local void @_Z22test_reference_bindingv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z22test_reference_bindingv.p, i64 8, i1 false)
+// CHECK:    store ptr [[P]], ptr [[TMP0:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z22test_reference_bindingv.omp_outlined, ptr [[TMP1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z22test_reference_bindingv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store ptr [[TMP1]], ptr [[TMP:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store ptr [[TMP2]], ptr [[OMP_BINDING_REF:%.*]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[OMP_BINDING_REF]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[TMP5:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store ptr [[TMP5]], ptr [[OMP_BINDING_REF1:%.*]], align 8
+// CHECK:    [[TMP6:%.*]] = load ptr, ptr [[OMP_BINDING_REF1]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP6]], i32 0, i32 1
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP4]], [[TMP7]]
+// CHECK:    store i32 [[ADD]], ptr [[SUM:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z18test_const_bindingv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z18test_const_bindingv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 @__const._Z18test_const_bindingv., i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z18test_const_bindingv.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z18test_const_bindingv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP2]], [[TMP3]]
+// CHECK:    store i32 [[ADD]], ptr [[SUM:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z22test_multiple_bindingsv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P1:%.*]], ptr align 4 @__const._Z22test_multiple_bindingsv.p1, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P2:%.*]], ptr align 4 @__const._Z22test_multiple_bindingsv.p2, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P1]], i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP1:%.*]], ptr align 4 [[P2]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 2, ptr @_Z22test_multiple_bindingsv.omp_outlined, ptr [[TMP0]], ptr [[TMP1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z22test_multiple_bindingsv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP2]], i32 0, i32 0
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP2]], i32 0, i32 1
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP4]], [[TMP5]]
+// CHECK:    [[X2:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[X2]], align 4
+// CHECK:    [[ADD3:%.*]] = add nsw i32 [[ADD]], [[TMP6]]
+// CHECK:    [[Y4:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[Y4]], align 4
+// CHECK:    [[ADD5:%.*]] = add nsw i32 [[ADD3]], [[TMP7]]
+// CHECK:    store i32 [[ADD5]], ptr [[SUM:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z32test_multiple_bindings_mixed_dsav(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P1:%.*]], ptr align 4 @__const._Z32test_multiple_bindings_mixed_dsav.p1, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P2:%.*]], ptr align 4 @__const._Z32test_multiple_bindings_mixed_dsav.p2, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P1]], i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP1:%.*]], ptr align 4 [[P2]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 3, ptr @_Z32test_multiple_bindings_mixed_dsav.omp_outlined, ptr [[TMP0]], ptr [[TMP1]], ptr [[P1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z32test_multiple_bindings_mixed_dsav.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP1:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[P1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    store ptr [[P1]], ptr [[P1_ADDR:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP4:%.*]] = load ptr, ptr [[P1_ADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P12:%.*]], ptr align 4 [[TMP4]], i64 8, i1 false)
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP2]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP2]], i32 0, i32 1
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP5]], [[TMP6]]
+// CHECK:    [[X3:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[X3]], align 4
+// CHECK:    [[ADD4:%.*]] = add nsw i32 [[ADD]], [[TMP7]]
+// CHECK:    [[Y5:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[Y5]], align 4
+// CHECK:    [[ADD6:%.*]] = add nsw i32 [[ADD4]], [[TMP8]]
+// CHECK:    store i32 [[ADD6]], ptr [[RESULT:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z21test_array_3_elementsv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*]]:
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR:%.*]], ptr align 4 @__const._Z21test_array_3_elementsv.arr, i64 12, i1 false)
+// CHECK:    [[ARRAYINIT_BEGIN:%.*]] = getelementptr inbounds [3 x i32], ptr [[TMP0:%.*]], i64 0, i64 0
+// CHECK:    br label %[[ARRAYINIT_BODY:.*]]
+// CHECK:       [[ARRAYINIT_BODY]]:
+// CHECK:    [[ARRAYINIT_INDEX:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[ARRAYINIT_NEXT:%.*]], %[[ARRAYINIT_BODY]] ]
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[ARRAYINIT_BEGIN]], i64 [[ARRAYINIT_INDEX]]
+// CHECK:    [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [3 x i32], ptr [[ARR]], i64 0, i64 [[ARRAYINIT_INDEX]]
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[TMP1]], align 4
+// CHECK:    [[ARRAYINIT_NEXT]] = add nuw i64 [[ARRAYINIT_INDEX]], 1
+// CHECK:    [[ARRAYINIT_DONE:%.*]] = icmp eq i64 [[ARRAYINIT_NEXT]], 3
+// CHECK:    br i1 [[ARRAYINIT_DONE]], label %[[ARRAYINIT_END:.*]], label %[[ARRAYINIT_BODY]]
+// CHECK:       [[ARRAYINIT_END]]:
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z21test_array_3_elementsv.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z21test_array_3_elementsv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(12) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[ARRAYIDX:%.*]] = getelementptr inbounds [3 x i32], ptr [[TMP1]], i64 0, i64 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
+// CHECK:    [[ARRAYIDX1:%.*]] = getelementptr inbounds [3 x i32], ptr [[TMP1]], i64 0, i64 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX1]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP2]], [[TMP3]]
+// CHECK:    [[ARRAYIDX2:%.*]] = getelementptr inbounds [3 x i32], ptr [[TMP1]], i64 0, i64 2
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[ARRAYIDX2]], align 4
+// CHECK:    [[ADD3:%.*]] = add nsw i32 [[ADD]], [[TMP4]]
+// CHECK:    store i32 [[ADD3]], ptr [[SUM:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z11test_singlev(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z11test_singlev.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z11test_singlev.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z11test_singlev.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[TMP2]], align 4
+// CHECK:    [[TMP4:%.*]] = call i32 @__kmpc_single(ptr @[[GLOB1]], i32 [[TMP3]])
+// CHECK:    [[TMP5:%.*]] = icmp ne i32 [[TMP4]], 0
+// CHECK:    br i1 [[TMP5]], label %[[OMP_IF_THEN:.*]], label %[[OMP_IF_END:.*]]
+// CHECK:       [[OMP_IF_THEN]]:
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP6]], [[TMP7]]
+// CHECK:    store i32 [[ADD]], ptr [[SUM:%.*]], align 4
+// CHECK:    call void @__kmpc_end_single(ptr @[[GLOB1]], i32 [[TMP3]])
+// CHECK:    br label %[[OMP_IF_END]]
+// CHECK:       [[OMP_IF_END]]:
+// CHECK:    call void @__kmpc_barrier(ptr @[[GLOB5:[0-9]+]], i32 [[TMP3]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z13test_sectionsv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z13test_sectionsv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z13test_sectionsv.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z13test_sectionsv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 0, ptr [[DOTOMP_SECTIONS_LB_:%.*]], align 4
+// CHECK:    store i32 1, ptr [[DOTOMP_SECTIONS_UB_:%.*]], align 4
+// CHECK:    store i32 1, ptr [[DOTOMP_SECTIONS_ST_:%.*]], align 4
+// CHECK:    store i32 0, ptr [[DOTOMP_SECTIONS_IL_:%.*]], align 4
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[TMP2]], align 4
+// CHECK:    call void @__kmpc_for_static_init_4(ptr @[[GLOB6:[0-9]+]], i32 [[TMP3]], i32 34, ptr [[DOTOMP_SECTIONS_IL_]], ptr [[DOTOMP_SECTIONS_LB_]], ptr [[DOTOMP_SECTIONS_UB_]], ptr [[DOTOMP_SECTIONS_ST_]], i32 1, i32 1)
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[DOTOMP_SECTIONS_UB_]], align 4
+// CHECK:    [[TMP5:%.*]] = icmp slt i32 [[TMP4]], 1
+// CHECK:    [[TMP6:%.*]] = select i1 [[TMP5]], i32 [[TMP4]], i32 1
+// CHECK:    store i32 [[TMP6]], ptr [[DOTOMP_SECTIONS_UB_]], align 4
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[DOTOMP_SECTIONS_LB_]], align 4
+// CHECK:    store i32 [[TMP7]], ptr [[DOTOMP_SECTIONS_IV_:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[DOTOMP_SECTIONS_IV_]], align 4
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[DOTOMP_SECTIONS_UB_]], align 4
+// CHECK:    [[CMP:%.*]] = icmp sle i32 [[TMP8]], [[TMP9]]
+// CHECK:    br i1 [[CMP]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[DOTOMP_SECTIONS_IV_]], align 4
+// CHECK:    switch i32 [[TMP10]], label %[[DOTOMP_SECTIONS_EXIT:.*]] [
+// CHECK:      i32 0, label %[[DOTOMP_SECTIONS_CASE:.*]]
+// CHECK:      i32 1, label %[[DOTOMP_SECTIONS_CASE1:.*]]
+// CHECK:    ]
+// CHECK:       [[DOTOMP_SECTIONS_CASE]]:
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP12:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP11]], [[TMP12]]
+// CHECK:    store i32 [[ADD]], ptr [[SUM:%.*]], align 4
+// CHECK:    br label %[[DOTOMP_SECTIONS_EXIT]]
+// CHECK:       [[DOTOMP_SECTIONS_CASE1]]:
+// CHECK:    [[X2:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP13:%.*]] = load i32, ptr [[X2]], align 4
+// CHECK:    [[Y3:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP14:%.*]] = load i32, ptr [[Y3]], align 4
+// CHECK:    [[SUB:%.*]] = sub nsw i32 [[TMP13]], [[TMP14]]
+// CHECK:    store i32 [[SUB]], ptr [[DIFF:%.*]], align 4
+// CHECK:    br label %[[DOTOMP_SECTIONS_EXIT]]
+// CHECK:       [[DOTOMP_SECTIONS_EXIT]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP15:%.*]] = load i32, ptr [[DOTOMP_SECTIONS_IV_]], align 4
+// CHECK:    [[INC:%.*]] = add nsw i32 [[TMP15]], 1
+// CHECK:    store i32 [[INC]], ptr [[DOTOMP_SECTIONS_IV_]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    call void @__kmpc_for_static_fini(ptr @[[GLOB6]], i32 [[TMP3]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z20test_nested_parallelv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z20test_nested_parallelv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z20test_nested_parallelv.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z20test_nested_parallelv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z20test_nested_parallelv.omp_outlined.omp_outlined, ptr [[TMP1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z20test_nested_parallelv.omp_outlined.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP2]], [[TMP3]]
+// CHECK:    store i32 [[ADD]], ptr [[SUM:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z10test_simd_v(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z10test_simd_v.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    store i32 0, ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP1:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP116:![0-9]+]]
+// CHECK:    [[CMP:%.*]] = icmp slt i32 [[TMP1]], 10
+// CHECK:    br i1 [[CMP]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP116]]
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP2]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4, !llvm.access.group [[ACC_GRP116]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[X]], align 4, !llvm.access.group [[ACC_GRP116]]
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[Y]], align 4, !llvm.access.group [[ACC_GRP116]]
+// CHECK:    [[ADD1:%.*]] = add nsw i32 [[TMP3]], [[TMP4]]
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[I]], align 4, !llvm.access.group [[ACC_GRP116]]
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[ADD1]], [[TMP5]]
+// CHECK:    store i32 [[ADD2]], ptr [[RESULT:%.*]], align 4, !llvm.access.group [[ACC_GRP116]]
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP116]]
+// CHECK:    [[ADD3:%.*]] = add nsw i32 [[TMP6]], 1
+// CHECK:    store i32 [[ADD3]], ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP116]]
+// CHECK:    br label %[[OMP_INNER_FOR_COND]], !llvm.loop [[LOOP117:![0-9]+]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    store i32 10, ptr [[I]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z23test_simd_write_bindingv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z23test_simd_write_bindingv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    store i32 0, ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP1:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP119:![0-9]+]]
+// CHECK:    [[CMP:%.*]] = icmp slt i32 [[TMP1]], 10
+// CHECK:    br i1 [[CMP]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP119]]
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP2]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4, !llvm.access.group [[ACC_GRP119]]
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[I]], align 4, !llvm.access.group [[ACC_GRP119]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    store i32 [[TMP3]], ptr [[X]], align 4, !llvm.access.group [[ACC_GRP119]]
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP119]]
+// CHECK:    [[ADD1:%.*]] = add nsw i32 [[TMP4]], 1
+// CHECK:    store i32 [[ADD1]], ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP119]]
+// CHECK:    br label %[[OMP_INNER_FOR_COND]], !llvm.loop [[LOOP120:![0-9]+]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    store i32 10, ptr [[I]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z21test_simd_lastprivatev(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z21test_simd_lastprivatev.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    store ptr [[X]], ptr [[A:%.*]], align 8
+// CHECK:    store i32 0, ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP1:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP122:![0-9]+]]
+// CHECK:    [[CMP:%.*]] = icmp slt i32 [[TMP1]], 10
+// CHECK:    br i1 [[CMP]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP122]]
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP2]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4, !llvm.access.group [[ACC_GRP122]]
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[I]], align 4, !llvm.access.group [[ACC_GRP122]]
+// CHECK:    store i32 [[TMP3]], ptr [[A2:%.*]], align 4, !llvm.access.group [[ACC_GRP122]]
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP122]]
+// CHECK:    [[ADD3:%.*]] = add nsw i32 [[TMP4]], 1
+// CHECK:    store i32 [[ADD3]], ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP122]]
+// CHECK:    br label %[[OMP_INNER_FOR_COND]], !llvm.loop [[LOOP123:![0-9]+]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    store i32 10, ptr [[I]], align 4
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[A2]], align 4
+// CHECK:    store i32 [[TMP5]], ptr [[X1]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z32test_private_individual_bindingsv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z32test_private_individual_bindingsv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @_Z32test_private_individual_bindingsv.omp_outlined)
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z32test_private_individual_bindingsv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store i32 2, ptr [[A:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z37test_firstprivate_individual_bindingsv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z37test_firstprivate_individual_bindingsv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z37test_firstprivate_individual_bindingsv.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z37test_firstprivate_individual_bindingsv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[B:%.*]], align 4
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[B]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP3]], 10
+// CHECK:    store i32 [[ADD]], ptr [[B]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z20test_static_bindingsv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @_Z20test_static_bindingsv.omp_outlined)
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z20test_static_bindingsv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    [[TMP0:%.*]] = load i32, ptr @_ZZ20test_static_bindingsvEDC1a1bE, align 4
+// CHECK:    [[TMP1:%.*]] = load i32, ptr getelementptr inbounds nuw (i8, ptr @_ZZ20test_static_bindingsvEDC1a1bE, i64 4), align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP0]], [[TMP1]]
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z14test_shadowingv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 @__const._Z14test_shadowingv., i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @_Z14test_shadowingv.omp_outlined)
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP1:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    call void @_Z3usei(i32 noundef [[TMP1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z14test_shadowingv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load i32, ptr [[A:%.*]], align 4
+// CHECK:    call void @_Z3usei(i32 noundef [[TMP1]])
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 @"__const.<captured>.", i64 8, i1 false)
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    call void @_Z3usei(i32 noundef [[TMP2]])
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    call void @_Z3usei(i32 noundef [[TMP3]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z31test_simd_private_then_parallelv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z31test_simd_private_then_parallelv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    store i32 0, ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP1:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP125:![0-9]+]]
+// CHECK:    [[CMP:%.*]] = icmp slt i32 [[TMP1]], 10
+// CHECK:    br i1 [[CMP]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP125]]
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP2]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4, !llvm.access.group [[ACC_GRP125]]
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[I]], align 4, !llvm.access.group [[ACC_GRP125]]
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[A:%.*]], align 4, !llvm.access.group [[ACC_GRP125]]
+// CHECK:    [[ADD1:%.*]] = add nsw i32 [[TMP4]], [[TMP3]]
+// CHECK:    store i32 [[ADD1]], ptr [[A]], align 4, !llvm.access.group [[ACC_GRP125]]
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP125]]
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[TMP5]], 1
+// CHECK:    store i32 [[ADD2]], ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP125]]
+// CHECK:    br label %[[OMP_INNER_FOR_COND]], !llvm.loop [[LOOP126:![0-9]+]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    store i32 10, ptr [[I]], align 4
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z31test_simd_private_then_parallelv.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z31test_simd_private_then_parallelv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    call void @_Z3usei(i32 noundef [[TMP2]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z19test_linear_bindingv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z19test_linear_bindingv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    store i32 0, ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP1:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP1]], ptr [[DOTLINEAR_START:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP128:![0-9]+]]
+// CHECK:    [[CMP:%.*]] = icmp slt i32 [[TMP2]], 10
+// CHECK:    br i1 [[CMP]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP128]]
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP3]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4, !llvm.access.group [[ACC_GRP128]]
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[DOTLINEAR_START]], align 4, !llvm.access.group [[ACC_GRP128]]
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP128]]
+// CHECK:    [[MUL1:%.*]] = mul nsw i32 [[TMP5]], 1
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[TMP4]], [[MUL1]]
+// CHECK:    store i32 [[ADD2]], ptr [[A:%.*]], align 4, !llvm.access.group [[ACC_GRP128]]
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[A]], align 4, !llvm.access.group [[ACC_GRP128]]
+// CHECK:    [[ADD3:%.*]] = add nsw i32 [[TMP6]], 1
+// CHECK:    store i32 [[ADD3]], ptr [[A]], align 4, !llvm.access.group [[ACC_GRP128]]
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[A]], align 4, !llvm.access.group [[ACC_GRP128]]
+// CHECK:    call void @_Z3usei(i32 noundef [[TMP7]]), !llvm.access.group [[ACC_GRP128]]
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP128]]
+// CHECK:    [[ADD4:%.*]] = add nsw i32 [[TMP8]], 1
+// CHECK:    store i32 [[ADD4]], ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP128]]
+// CHECK:    br label %[[OMP_INNER_FOR_COND]], !llvm.loop [[LOOP129:![0-9]+]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    store i32 10, ptr [[I]], align 4
+// CHECK:    [[X5:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    [[X6:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    store i32 [[TMP9]], ptr [[X6]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z24test_lastprivate_bindingv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z24test_lastprivate_bindingv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    store ptr [[X]], ptr [[A:%.*]], align 8
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z24test_lastprivate_bindingv.omp_outlined, ptr [[TMP0]])
+// CHECK:    [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP1:%.*]] = load i32, ptr [[X1]], align 4
+// CHECK:    call void @_Z3usei(i32 noundef [[TMP1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z24test_lastprivate_bindingv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 0, ptr [[DOTOMP_LB:%.*]], align 4
+// CHECK:    store i32 9, ptr [[DOTOMP_UB:%.*]], align 4
+// CHECK:    store i32 1, ptr [[DOTOMP_STRIDE:%.*]], align 4
+// CHECK:    store i32 0, ptr [[DOTOMP_IS_LAST:%.*]], align 4
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[TMP2]], align 4
+// CHECK:    call void @__kmpc_for_static_init_4(ptr @[[GLOB2]], i32 [[TMP3]], i32 34, ptr [[DOTOMP_IS_LAST]], ptr [[DOTOMP_LB]], ptr [[DOTOMP_UB]], ptr [[DOTOMP_STRIDE]], i32 1, i32 1)
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP:%.*]] = icmp sgt i32 [[TMP4]], 9
+// CHECK:    br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+// CHECK:       [[COND_TRUE]]:
+// CHECK:    br label %[[COND_END:.*]]
+// CHECK:       [[COND_FALSE]]:
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    br label %[[COND_END]]
+// CHECK:       [[COND_END]]:
+// CHECK:    [[COND:%.*]] = phi i32 [ 9, %[[COND_TRUE]] ], [ [[TMP5]], %[[COND_FALSE]] ]
+// CHECK:    store i32 [[COND]], ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[DOTOMP_LB]], align 4
+// CHECK:    store i32 [[TMP6]], ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP1:%.*]] = icmp sle i32 [[TMP7]], [[TMP8]]
+// CHECK:    br i1 [[CMP1]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP9]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[I]], align 4
+// CHECK:    [[MUL2:%.*]] = mul nsw i32 [[TMP10]], 10
+// CHECK:    store i32 [[MUL2]], ptr [[A:%.*]], align 4
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[ADD3:%.*]] = add nsw i32 [[TMP11]], 1
+// CHECK:    store i32 [[ADD3]], ptr [[DOTOMP_IV]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    br label %[[OMP_LOOP_EXIT:.*]]
+// CHECK:       [[OMP_LOOP_EXIT]]:
+// CHECK:    call void @__kmpc_for_static_fini(ptr @[[GLOB2]], i32 [[TMP3]])
+// CHECK:    [[TMP12:%.*]] = load i32, ptr [[DOTOMP_IS_LAST]], align 4
+// CHECK:    [[TMP13:%.*]] = icmp ne i32 [[TMP12]], 0
+// CHECK:    br i1 [[TMP13]], label %[[DOTOMP_LASTPRIVATE_THEN:.*]], label %[[DOTOMP_LASTPRIVATE_DONE:.*]]
+// CHECK:       [[DOTOMP_LASTPRIVATE_THEN]]:
+// CHECK:    [[TMP14:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    store i32 [[TMP14]], ptr [[X]], align 4
+// CHECK:    br label %[[DOTOMP_LASTPRIVATE_DONE]]
+// CHECK:       [[DOTOMP_LASTPRIVATE_DONE]]:
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z27test_binding_name_collisionv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P1:%.*]], ptr align 4 @__const._Z27test_binding_name_collisionv.p1, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P1]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @_Z27test_binding_name_collisionv.omp_outlined)
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z27test_binding_name_collisionv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P2:%.*]], ptr align 4 @"__const.<captured>.p2", i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P2]], i64 8, i1 false)
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP1:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP1]], 1
+// CHECK:    store i32 [[ADD]], ptr [[X]], align 4
+// CHECK:    [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X1]], align 4
+// CHECK:    call void @_Z3usei(i32 noundef [[TMP2]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z31test_firstprivate_nontrivial_sbv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[P:%.*]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[X]], i32 noundef 10)
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR]], ptr [[P]], i32 0, i32 1
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[Y]], i32 noundef 20)
+// CHECK:    call void @_ZN4PairC1ERKS_(ptr noundef nonnull align 4 dereferenceable(16) [[TMP0:%.*]], ptr noundef nonnull align 4 dereferenceable(16) [[P]])
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z31test_firstprivate_nontrivial_sbv.omp_outlined, ptr [[TMP0]])
+// CHECK:    call void @_ZN4PairD1Ev(ptr noundef nonnull align 4 dead_on_return(16) dereferenceable(16) [[TMP0]]) #{{[0-9]+}}
+// CHECK:    call void @_ZN4PairD1Ev(ptr noundef nonnull align 4 dead_on_return(16) dereferenceable(16) [[P]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN14NonTrivialCopyC1Ei(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[THIS:%.*]], i32 noundef [[V:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store i32 [[V]], ptr [[V_ADDR:%.*]], align 4
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[TMP0:%.*]] = load i32, ptr [[V_ADDR]], align 4
+// CHECK:    call void @_ZN14NonTrivialCopyC2Ei(ptr noundef nonnull align 4 dereferenceable(8) [[THIS1]], i32 noundef [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN4PairC1ERKS_(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(16) [[THIS:%.*]], ptr noundef nonnull align 4 dereferenceable(16) [[TMP0:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    call void @_ZN4PairC2ERKS_(ptr noundef nonnull align 4 dereferenceable(16) [[THIS1]], ptr noundef nonnull align 4 dereferenceable(16) [[TMP1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z31test_firstprivate_nontrivial_sbv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(16) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[A:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[X]])
+// CHECK:    [[VALUE:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY:%.*]], ptr [[A]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[A]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN14NonTrivialCopyC1ERKS_(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[THIS:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[OTHER:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store ptr [[OTHER]], ptr [[OTHER_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[TMP0:%.*]] = load ptr, ptr [[OTHER_ADDR]], align 8
+// CHECK:    call void @_ZN14NonTrivialCopyC2ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[THIS1]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN14NonTrivialCopyD1Ev(
+// CHECK-SAME: ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[THIS:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    call void @_ZN14NonTrivialCopyD2Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[THIS1]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN4PairD1Ev(
+// CHECK-SAME: ptr noundef nonnull align 4 dead_on_return(16) dereferenceable(16) [[THIS:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    call void @_ZN4PairD2Ev(ptr noundef nonnull align 4 dead_on_return(16) dereferenceable(16) [[THIS1]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN14NonTrivialCopyC2Ei(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[THIS:%.*]], i32 noundef [[V:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store i32 [[V]], ptr [[V_ADDR:%.*]], align 4
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[VALUE:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK:    [[TMP0:%.*]] = load i32, ptr [[V_ADDR]], align 4
+// CHECK:    store i32 [[TMP0]], ptr [[VALUE]], align 4
+// CHECK:    [[COPY_COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY]], ptr [[THIS1]], i32 0, i32 1
+// CHECK:    store i32 0, ptr [[COPY_COUNT]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN4PairC2ERKS_(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(16) [[THIS:%.*]], ptr noundef nonnull align 4 dereferenceable(16) [[TMP0:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X2:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[X]], ptr noundef nonnull align 4 dereferenceable(8) [[X2]])
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR]], ptr [[THIS1]], i32 0, i32 1
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[Y3:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR]], ptr [[TMP2]], i32 0, i32 1
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[Y]], ptr noundef nonnull align 4 dereferenceable(8) [[Y3]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN14NonTrivialCopyC2ERKS_(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[THIS:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[OTHER:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store ptr [[OTHER]], ptr [[OTHER_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[VALUE:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK:    [[TMP0:%.*]] = load ptr, ptr [[OTHER_ADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[VALUE2:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP1:%.*]] = load i32, ptr [[VALUE2]], align 4
+// CHECK:    store i32 [[TMP1]], ptr [[VALUE]], align 4
+// CHECK:    [[COPY_COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY]], ptr [[THIS1]], i32 0, i32 1
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[OTHER_ADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[COPY_COUNT3:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY]], ptr [[TMP2]], i32 0, i32 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[COPY_COUNT3]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP3]], 1
+// CHECK:    store i32 [[ADD]], ptr [[COPY_COUNT]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN14NonTrivialCopyD2Ev(
+// CHECK-SAME: ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[THIS:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN4PairD2Ev(
+// CHECK-SAME: ptr noundef nonnull align 4 dead_on_return(16) dereferenceable(16) [[THIS:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[THIS1]], i32 0, i32 1
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[Y]]) #{{[0-9]+}}
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR]], ptr [[THIS1]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[X]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z32test_firstprivate_ref_binding_sbv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[P:%.*]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[X]], i32 noundef 10)
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR]], ptr [[P]], i32 0, i32 1
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[Y]], i32 noundef 20)
+// CHECK:    store ptr [[P]], ptr [[TMP0:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z32test_firstprivate_ref_binding_sbv.omp_outlined, ptr [[TMP1]])
+// CHECK:    call void @_ZN4PairD1Ev(ptr noundef nonnull align 4 dead_on_return(16) dereferenceable(16) [[P]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z32test_firstprivate_ref_binding_sbv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(16) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store ptr [[TMP1]], ptr [[TMP:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store ptr [[TMP2]], ptr [[OMP_BINDING_REF:%.*]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[OMP_BINDING_REF]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[A:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[X]])
+// CHECK:    [[VALUE:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY:%.*]], ptr [[A]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[A]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z38test_firstprivate_const_ref_binding_sbv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[P:%.*]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[X]], i32 noundef 10)
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR]], ptr [[P]], i32 0, i32 1
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[Y]], i32 noundef 20)
+// CHECK:    store ptr [[P]], ptr [[TMP0:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z38test_firstprivate_const_ref_binding_sbv.omp_outlined, ptr [[TMP1]])
+// CHECK:    call void @_ZN4PairD1Ev(ptr noundef nonnull align 4 dead_on_return(16) dereferenceable(16) [[P]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z38test_firstprivate_const_ref_binding_sbv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(16) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store ptr [[TMP1]], ptr [[TMP:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store ptr [[TMP2]], ptr [[OMP_BINDING_REF:%.*]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[OMP_BINDING_REF]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[A:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[X]])
+// CHECK:    [[VALUE:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY:%.*]], ptr [[A]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[A]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z38test_firstprivate_multiple_bindings_sbv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[P:%.*]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[X]], i32 noundef 10)
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR]], ptr [[P]], i32 0, i32 1
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[Y]], i32 noundef 20)
+// CHECK:    call void @_ZN4PairC1ERKS_(ptr noundef nonnull align 4 dereferenceable(16) [[TMP0:%.*]], ptr noundef nonnull align 4 dereferenceable(16) [[P]])
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z38test_firstprivate_multiple_bindings_sbv.omp_outlined, ptr [[TMP0]])
+// CHECK:    call void @_ZN4PairD1Ev(ptr noundef nonnull align 4 dead_on_return(16) dereferenceable(16) [[TMP0]]) #{{[0-9]+}}
+// CHECK:    call void @_ZN4PairD1Ev(ptr noundef nonnull align 4 dead_on_return(16) dereferenceable(16) [[P]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z38test_firstprivate_multiple_bindings_sbv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(16) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[A:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[X]])
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[B:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[Y]])
+// CHECK:    [[VALUE:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY:%.*]], ptr [[A]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[VALUE]], align 4
+// CHECK:    [[VALUE1:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY]], ptr [[B]], i32 0, i32 0
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[VALUE1]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP2]], [[TMP3]]
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[B]]) #{{[0-9]+}}
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[A]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z36test_firstprivate_with_destructor_sbv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIRWITHDTOR:%.*]], ptr [[P:%.*]], i32 0, i32 0
+// CHECK:    call void @_ZN8WithDtorC1Ei(ptr noundef nonnull align 8 dereferenceable(8) [[X]], i32 noundef 100)
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIRWITHDTOR]], ptr [[P]], i32 0, i32 1
+// CHECK:    call void @_ZN8WithDtorC1Ei(ptr noundef nonnull align 8 dereferenceable(8) [[Y]], i32 noundef 200)
+// CHECK:    call void @_ZN12PairWithDtorC1ERKS_(ptr noundef nonnull align 8 dereferenceable(16) [[TMP0:%.*]], ptr noundef nonnull align 8 dereferenceable(16) [[P]])
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z36test_firstprivate_with_destructor_sbv.omp_outlined, ptr [[TMP0]])
+// CHECK:    call void @_ZN12PairWithDtorD1Ev(ptr noundef nonnull align 8 dead_on_return(16) dereferenceable(16) [[TMP0]]) #{{[0-9]+}}
+// CHECK:    call void @_ZN12PairWithDtorD1Ev(ptr noundef nonnull align 8 dead_on_return(16) dereferenceable(16) [[P]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN8WithDtorC1Ei(
+// CHECK-SAME: ptr noundef nonnull align 8 dereferenceable(8) [[THIS:%.*]], i32 noundef [[V:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store i32 [[V]], ptr [[V_ADDR:%.*]], align 4
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[TMP0:%.*]] = load i32, ptr [[V_ADDR]], align 4
+// CHECK:    call void @_ZN8WithDtorC2Ei(ptr noundef nonnull align 8 dereferenceable(8) [[THIS1]], i32 noundef [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN12PairWithDtorC1ERKS_(
+// CHECK-SAME: ptr noundef nonnull align 8 dereferenceable(16) [[THIS:%.*]], ptr noundef nonnull align 8 dereferenceable(16) [[TMP0:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    call void @_ZN12PairWithDtorC2ERKS_(ptr noundef nonnull align 8 dereferenceable(16) [[THIS1]], ptr noundef nonnull align 8 dereferenceable(16) [[TMP1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z36test_firstprivate_with_destructor_sbv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 8 dereferenceable(16) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META131:![0-9]+]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIRWITHDTOR:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    call void @_ZN8WithDtorC1ERKS_(ptr noundef nonnull align 8 dereferenceable(8) [[A:%.*]], ptr noundef nonnull align 8 dereferenceable(8) [[X]])
+// CHECK:    [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_WITHDTOR:%.*]], ptr [[A]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[PTR]], align 8
+// CHECK:    call void @_ZN8WithDtorD1Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) [[A]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN8WithDtorC1ERKS_(
+// CHECK-SAME: ptr noundef nonnull align 8 dereferenceable(8) [[THIS:%.*]], ptr noundef nonnull align 8 dereferenceable(8) [[OTHER:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store ptr [[OTHER]], ptr [[OTHER_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[TMP0:%.*]] = load ptr, ptr [[OTHER_ADDR]], align 8
+// CHECK:    call void @_ZN8WithDtorC2ERKS_(ptr noundef nonnull align 8 dereferenceable(8) [[THIS1]], ptr noundef nonnull align 8 dereferenceable(8) [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN8WithDtorD1Ev(
+// CHECK-SAME: ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) [[THIS:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    call void @_ZN8WithDtorD2Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) [[THIS1]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN12PairWithDtorD1Ev(
+// CHECK-SAME: ptr noundef nonnull align 8 dead_on_return(16) dereferenceable(16) [[THIS:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    call void @_ZN12PairWithDtorD2Ev(ptr noundef nonnull align 8 dead_on_return(16) dereferenceable(16) [[THIS1]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN8WithDtorC2Ei(
+// CHECK-SAME: ptr noundef nonnull align 8 dereferenceable(8) [[THIS:%.*]], i32 noundef [[V:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store i32 [[V]], ptr [[V_ADDR:%.*]], align 4
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_WITHDTOR:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #{{[0-9]+}}
+// CHECK:    [[TMP0:%.*]] = load i32, ptr [[V_ADDR]], align 4
+// CHECK:    store i32 [[TMP0]], ptr [[CALL]], align 4
+// CHECK:    store ptr [[CALL]], ptr [[PTR]], align 8
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN12PairWithDtorC2ERKS_(
+// CHECK-SAME: ptr noundef nonnull align 8 dereferenceable(16) [[THIS:%.*]], ptr noundef nonnull align 8 dereferenceable(16) [[TMP0:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIRWITHDTOR:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META131]]
+// CHECK:    [[X2:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIRWITHDTOR]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    call void @_ZN8WithDtorC1ERKS_(ptr noundef nonnull align 8 dereferenceable(8) [[X]], ptr noundef nonnull align 8 dereferenceable(8) [[X2]])
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIRWITHDTOR]], ptr [[THIS1]], i32 0, i32 1
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META131]]
+// CHECK:    [[Y3:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIRWITHDTOR]], ptr [[TMP2]], i32 0, i32 1
+// CHECK:    call void @_ZN8WithDtorC1ERKS_(ptr noundef nonnull align 8 dereferenceable(8) [[Y]], ptr noundef nonnull align 8 dereferenceable(8) [[Y3]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN8WithDtorC2ERKS_(
+// CHECK-SAME: ptr noundef nonnull align 8 dereferenceable(8) [[THIS:%.*]], ptr noundef nonnull align 8 dereferenceable(8) [[OTHER:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store ptr [[OTHER]], ptr [[OTHER_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_WITHDTOR:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #{{[0-9]+}}
+// CHECK:    [[TMP0:%.*]] = load ptr, ptr [[OTHER_ADDR]], align 8, !nonnull [[META18]], !align [[META131]]
+// CHECK:    [[PTR2:%.*]] = getelementptr inbounds nuw [[STRUCT_WITHDTOR]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[PTR2]], align 8
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[CALL]], align 4
+// CHECK:    store ptr [[CALL]], ptr [[PTR]], align 8
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN8WithDtorD2Ev(
+// CHECK-SAME: ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) [[THIS:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_WITHDTOR:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK:    [[TMP0:%.*]] = load ptr, ptr [[PTR]], align 8
+// CHECK:    [[ISNULL:%.*]] = icmp eq ptr [[TMP0]], null
+// CHECK:    br i1 [[ISNULL]], label %[[DELETE_END:.*]], label %[[DELETE_NOTNULL:.*]]
+// CHECK:       [[DELETE_NOTNULL]]:
+// CHECK:    call void @_ZdlPvm(ptr noundef [[TMP0]], i64 noundef 4) #{{[0-9]+}}
+// CHECK:    br label %[[DELETE_END]]
+// CHECK:       [[DELETE_END]]:
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN12PairWithDtorD2Ev(
+// CHECK-SAME: ptr noundef nonnull align 8 dead_on_return(16) dereferenceable(16) [[THIS:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIRWITHDTOR:%.*]], ptr [[THIS1]], i32 0, i32 1
+// CHECK:    call void @_ZN8WithDtorD1Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) [[Y]]) #{{[0-9]+}}
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIRWITHDTOR]], ptr [[THIS1]], i32 0, i32 0
+// CHECK:    call void @_ZN8WithDtorD1Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) [[X]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z35test_firstprivate_array_bindings_sbv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*]]:
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[ARR:%.*]], i32 noundef 1)
+// CHECK:    [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_NONTRIVIALCOPY:%.*]], ptr [[ARR]], i64 1
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[ARRAYINIT_ELEMENT]], i32 noundef 2)
+// CHECK:    [[ARRAYINIT_ELEMENT1:%.*]] = getelementptr inbounds [[STRUCT_NONTRIVIALCOPY]], ptr [[ARR]], i64 2
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[ARRAYINIT_ELEMENT1]], i32 noundef 3)
+// CHECK:    [[ARRAYINIT_BEGIN:%.*]] = getelementptr inbounds [3 x [[STRUCT_NONTRIVIALCOPY]]], ptr [[TMP0:%.*]], i64 0, i64 0
+// CHECK:    br label %[[ARRAYINIT_BODY:.*]]
+// CHECK:       [[ARRAYINIT_BODY]]:
+// CHECK:    [[ARRAYINIT_INDEX:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[ARRAYINIT_NEXT:%.*]], %[[ARRAYINIT_BODY]] ]
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_NONTRIVIALCOPY]], ptr [[ARRAYINIT_BEGIN]], i64 [[ARRAYINIT_INDEX]]
+// CHECK:    [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [3 x [[STRUCT_NONTRIVIALCOPY]]], ptr [[ARR]], i64 0, i64 [[ARRAYINIT_INDEX]]
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[TMP1]], ptr noundef nonnull align 4 dereferenceable(8) [[ARRAYIDX]])
+// CHECK:    [[ARRAYINIT_NEXT]] = add nuw i64 [[ARRAYINIT_INDEX]], 1
+// CHECK:    [[ARRAYINIT_DONE:%.*]] = icmp eq i64 [[ARRAYINIT_NEXT]], 3
+// CHECK:    br i1 [[ARRAYINIT_DONE]], label %[[ARRAYINIT_END:.*]], label %[[ARRAYINIT_BODY]]
+// CHECK:       [[ARRAYINIT_END]]:
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z35test_firstprivate_array_bindings_sbv.omp_outlined, ptr [[TMP0]])
+// CHECK:    [[ARRAY_BEGIN:%.*]] = getelementptr inbounds [3 x [[STRUCT_NONTRIVIALCOPY]]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_NONTRIVIALCOPY]], ptr [[ARRAY_BEGIN]], i64 3
+// CHECK:    br label %[[ARRAYDESTROY_BODY:.*]]
+// CHECK:       [[ARRAYDESTROY_BODY]]:
+// CHECK:    [[ARRAYDESTROY_ELEMENTPAST:%.*]] = phi ptr [ [[TMP2]], %[[ARRAYINIT_END]] ], [ [[ARRAYDESTROY_ELEMENT:%.*]], %[[ARRAYDESTROY_BODY]] ]
+// CHECK:    [[ARRAYDESTROY_ELEMENT]] = getelementptr inbounds [[STRUCT_NONTRIVIALCOPY]], ptr [[ARRAYDESTROY_ELEMENTPAST]], i64 -1
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[ARRAYDESTROY_ELEMENT]]) #{{[0-9]+}}
+// CHECK:    [[ARRAYDESTROY_DONE:%.*]] = icmp eq ptr [[ARRAYDESTROY_ELEMENT]], [[ARRAY_BEGIN]]
+// CHECK:    br i1 [[ARRAYDESTROY_DONE]], label %[[ARRAYDESTROY_DONE2:.*]], label %[[ARRAYDESTROY_BODY]]
+// CHECK:       [[ARRAYDESTROY_DONE2]]:
+// CHECK:    [[ARRAY_BEGIN3:%.*]] = getelementptr inbounds [3 x [[STRUCT_NONTRIVIALCOPY]]], ptr [[ARR]], i32 0, i32 0
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds [[STRUCT_NONTRIVIALCOPY]], ptr [[ARRAY_BEGIN3]], i64 3
+// CHECK:    br label %[[ARRAYDESTROY_BODY4:.*]]
+// CHECK:       [[ARRAYDESTROY_BODY4]]:
+// CHECK:    [[ARRAYDESTROY_ELEMENTPAST5:%.*]] = phi ptr [ [[TMP3]], %[[ARRAYDESTROY_DONE2]] ], [ [[ARRAYDESTROY_ELEMENT6:%.*]], %[[ARRAYDESTROY_BODY4]] ]
+// CHECK:    [[ARRAYDESTROY_ELEMENT6]] = getelementptr inbounds [[STRUCT_NONTRIVIALCOPY]], ptr [[ARRAYDESTROY_ELEMENTPAST5]], i64 -1
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[ARRAYDESTROY_ELEMENT6]]) #{{[0-9]+}}
+// CHECK:    [[ARRAYDESTROY_DONE7:%.*]] = icmp eq ptr [[ARRAYDESTROY_ELEMENT6]], [[ARRAY_BEGIN3]]
+// CHECK:    br i1 [[ARRAYDESTROY_DONE7]], label %[[ARRAYDESTROY_DONE8:.*]], label %[[ARRAYDESTROY_BODY4]]
+// CHECK:       [[ARRAYDESTROY_DONE8]]:
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z35test_firstprivate_array_bindings_sbv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(24) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[ARRAYIDX:%.*]] = getelementptr inbounds [3 x [[STRUCT_NONTRIVIALCOPY:%.*]]], ptr [[TMP1]], i64 0, i64 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[A:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[ARRAYIDX]])
+// CHECK:    [[ARRAYIDX1:%.*]] = getelementptr inbounds [3 x [[STRUCT_NONTRIVIALCOPY]]], ptr [[TMP1]], i64 0, i64 1
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[B:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[ARRAYIDX1]])
+// CHECK:    [[ARRAYIDX2:%.*]] = getelementptr inbounds [3 x [[STRUCT_NONTRIVIALCOPY]]], ptr [[TMP1]], i64 0, i64 2
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[C:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[ARRAYIDX2]])
+// CHECK:    [[VALUE:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY]], ptr [[A]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[VALUE]], align 4
+// CHECK:    [[VALUE3:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY]], ptr [[B]], i32 0, i32 0
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[VALUE3]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP2]], [[TMP3]]
+// CHECK:    [[VALUE4:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY]], ptr [[C]], i32 0, i32 0
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[VALUE4]], align 4
+// CHECK:    [[ADD5:%.*]] = add nsw i32 [[ADD]], [[TMP4]]
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[C]]) #{{[0-9]+}}
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[B]]) #{{[0-9]+}}
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[A]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z34test_firstprivate_nested_struct_sbv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[I1:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER:%.*]], ptr [[O:%.*]], i32 0, i32 0
+// CHECK:    call void @_ZN5InnerC1Ei(ptr noundef nonnull align 4 dereferenceable(4) [[I1]], i32 noundef 5)
+// CHECK:    [[I2:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[O]], i32 0, i32 1
+// CHECK:    call void @_ZN5InnerC1Ei(ptr noundef nonnull align 4 dereferenceable(4) [[I2]], i32 noundef 10)
+// CHECK:    call void @_ZN5OuterC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[O]])
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z34test_firstprivate_nested_struct_sbv.omp_outlined, ptr [[TMP0]])
+// CHECK:    call void @_ZN5OuterD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[TMP0]]) #{{[0-9]+}}
+// CHECK:    call void @_ZN5OuterD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[O]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN5InnerC1Ei(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], i32 noundef [[V:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store i32 [[V]], ptr [[V_ADDR:%.*]], align 4
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[TMP0:%.*]] = load i32, ptr [[V_ADDR]], align 4
+// CHECK:    call void @_ZN5InnerC2Ei(ptr noundef nonnull align 4 dereferenceable(4) [[THIS1]], i32 noundef [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN5OuterC1ERKS_(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[THIS:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    call void @_ZN5OuterC2ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[THIS1]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP1]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z34test_firstprivate_nested_struct_sbv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[I1:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    call void @_ZN5InnerC1ERKS_(ptr noundef nonnull align 4 dereferenceable(4) [[X:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[I1]])
+// CHECK:    [[VAL:%.*]] = getelementptr inbounds nuw [[STRUCT_INNER:%.*]], ptr [[X]], i32 0, i32 0
+// CHECK:    call void @_ZN5InnerD1Ev(ptr noundef nonnull align 4 dead_on_return(4) dereferenceable(4) [[X]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN5InnerC1ERKS_(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[O:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store ptr [[O]], ptr [[O_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[TMP0:%.*]] = load ptr, ptr [[O_ADDR]], align 8
+// CHECK:    call void @_ZN5InnerC2ERKS_(ptr noundef nonnull align 4 dereferenceable(4) [[THIS1]], ptr noundef nonnull align 4 dereferenceable(4) [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN5InnerD1Ev(
+// CHECK-SAME: ptr noundef nonnull align 4 dead_on_return(4) dereferenceable(4) [[THIS:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    call void @_ZN5InnerD2Ev(ptr noundef nonnull align 4 dead_on_return(4) dereferenceable(4) [[THIS1]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN5OuterD1Ev(
+// CHECK-SAME: ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[THIS:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    call void @_ZN5OuterD2Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[THIS1]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN5InnerC2Ei(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], i32 noundef [[V:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store i32 [[V]], ptr [[V_ADDR:%.*]], align 4
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[VAL:%.*]] = getelementptr inbounds nuw [[STRUCT_INNER:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK:    [[TMP0:%.*]] = load i32, ptr [[V_ADDR]], align 4
+// CHECK:    store i32 [[TMP0]], ptr [[VAL]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN5OuterC2ERKS_(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[THIS:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[I1:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[I12:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    call void @_ZN5InnerC1ERKS_(ptr noundef nonnull align 4 dereferenceable(4) [[I1]], ptr noundef nonnull align 4 dereferenceable(4) [[I12]])
+// CHECK:    [[I2:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[THIS1]], i32 0, i32 1
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[I23:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP2]], i32 0, i32 1
+// CHECK:    call void @_ZN5InnerC1ERKS_(ptr noundef nonnull align 4 dereferenceable(4) [[I2]], ptr noundef nonnull align 4 dereferenceable(4) [[I23]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN5InnerC2ERKS_(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[O:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    store ptr [[O]], ptr [[O_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[VAL:%.*]] = getelementptr inbounds nuw [[STRUCT_INNER:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK:    [[TMP0:%.*]] = load ptr, ptr [[O_ADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[VAL2:%.*]] = getelementptr inbounds nuw [[STRUCT_INNER]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP1:%.*]] = load i32, ptr [[VAL2]], align 4
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP1]], 2
+// CHECK:    store i32 [[MUL]], ptr [[VAL]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN5InnerD2Ev(
+// CHECK-SAME: ptr noundef nonnull align 4 dead_on_return(4) dereferenceable(4) [[THIS:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN5OuterD2Ev(
+// CHECK-SAME: ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[THIS:%.*]]) unnamed_addr #{{[0-9]+}} comdat align 2 {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[THIS]], ptr [[THIS_ADDR:%.*]], align 8
+// CHECK:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK:    [[I2:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER:%.*]], ptr [[THIS1]], i32 0, i32 1
+// CHECK:    call void @_ZN5InnerD1Ev(ptr noundef nonnull align 4 dead_on_return(4) dereferenceable(4) [[I2]]) #{{[0-9]+}}
+// CHECK:    [[I1:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[THIS1]], i32 0, i32 0
+// CHECK:    call void @_ZN5InnerD1Ev(ptr noundef nonnull align 4 dead_on_return(4) dereferenceable(4) [[I1]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z46test_firstprivate_ref_binding_both_bindings_sbv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[P:%.*]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[X]], i32 noundef 10)
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR]], ptr [[P]], i32 0, i32 1
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[Y]], i32 noundef 20)
+// CHECK:    store ptr [[P]], ptr [[TMP0:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z46test_firstprivate_ref_binding_both_bindings_sbv.omp_outlined, ptr [[TMP1]])
+// CHECK:    call void @_ZN4PairD1Ev(ptr noundef nonnull align 4 dead_on_return(16) dereferenceable(16) [[P]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z46test_firstprivate_ref_binding_both_bindings_sbv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(16) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store ptr [[TMP1]], ptr [[TMP:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store ptr [[TMP2]], ptr [[OMP_BINDING_REF:%.*]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[OMP_BINDING_REF]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[A:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[X]])
+// CHECK:    [[TMP4:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store ptr [[TMP4]], ptr [[OMP_BINDING_REF1:%.*]], align 8
+// CHECK:    [[TMP5:%.*]] = load ptr, ptr [[OMP_BINDING_REF1]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR]], ptr [[TMP5]], i32 0, i32 1
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[B:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[Y]])
+// CHECK:    [[VALUE:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY:%.*]], ptr [[A]], i32 0, i32 0
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[VALUE]], align 4
+// CHECK:    [[VALUE2:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY]], ptr [[B]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[VALUE2]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP6]], [[TMP7]]
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[B]]) #{{[0-9]+}}
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[A]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z52test_firstprivate_const_ref_binding_both_bindings_sbv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[P:%.*]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[X]], i32 noundef 10)
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR]], ptr [[P]], i32 0, i32 1
+// CHECK:    call void @_ZN14NonTrivialCopyC1Ei(ptr noundef nonnull align 4 dereferenceable(8) [[Y]], i32 noundef 20)
+// CHECK:    store ptr [[P]], ptr [[TMP0:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z52test_firstprivate_const_ref_binding_both_bindings_sbv.omp_outlined, ptr [[TMP1]])
+// CHECK:    call void @_ZN4PairD1Ev(ptr noundef nonnull align 4 dead_on_return(16) dereferenceable(16) [[P]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z52test_firstprivate_const_ref_binding_both_bindings_sbv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(16) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store ptr [[TMP1]], ptr [[TMP:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store ptr [[TMP2]], ptr [[OMP_BINDING_REF:%.*]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[OMP_BINDING_REF]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[A:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[X]])
+// CHECK:    [[TMP4:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store ptr [[TMP4]], ptr [[OMP_BINDING_REF1:%.*]], align 8
+// CHECK:    [[TMP5:%.*]] = load ptr, ptr [[OMP_BINDING_REF1]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_PAIR]], ptr [[TMP5]], i32 0, i32 1
+// CHECK:    call void @_ZN14NonTrivialCopyC1ERKS_(ptr noundef nonnull align 4 dereferenceable(8) [[B:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[Y]])
+// CHECK:    [[VALUE:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY:%.*]], ptr [[A]], i32 0, i32 0
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[VALUE]], align 4
+// CHECK:    [[VALUE2:%.*]] = getelementptr inbounds nuw [[STRUCT_NONTRIVIALCOPY]], ptr [[B]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[VALUE2]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP6]], [[TMP7]]
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[B]]) #{{[0-9]+}}
+// CHECK:    call void @_ZN14NonTrivialCopyD1Ev(ptr noundef nonnull align 4 dead_on_return(8) dereferenceable(8) [[A]]) #{{[0-9]+}}
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z36test_lambda_capture_binding_by_valuev(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z36test_lambda_capture_binding_by_valuev.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds nuw [[CLASS_ANON:%.*]], ptr [[LAMBDA:%.*]], i32 0, i32 0
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[TMP1]], align 4
+// CHECK:    [[CALL:%.*]] = call noundef i32 @"_ZZ36test_lambda_capture_binding_by_valuevENK3$_0clEv"(ptr noundef nonnull align 4 dereferenceable(4) [[LAMBDA]])
+// CHECK:    store i32 [[CALL]], ptr [[RESULT:%.*]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z34test_lambda_capture_binding_by_refv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z34test_lambda_capture_binding_by_refv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds nuw [[CLASS_ANON_22:%.*]], ptr [[LAMBDA:%.*]], i32 0, i32 0
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    store ptr [[X]], ptr [[TMP1]], align 8
+// CHECK:    call void @"_ZZ34test_lambda_capture_binding_by_refvENK3$_0clEv"(ptr noundef nonnull align 8 dereferenceable(8) [[LAMBDA]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z37test_lambda_capture_multiple_bindingsv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z37test_lambda_capture_multiple_bindingsv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds nuw [[CLASS_ANON_23:%.*]], ptr [[LAMBDA:%.*]], i32 0, i32 0
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[TMP1]], align 8
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds nuw [[CLASS_ANON_23]], ptr [[LAMBDA]], i32 0, i32 1
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    store ptr [[Y]], ptr [[TMP3]], align 8
+// CHECK:    call void @"_ZZ37test_lambda_capture_multiple_bindingsvENK3$_0clEv"(ptr noundef nonnull align 8 dereferenceable(16) [[LAMBDA]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z28test_lambda_implicit_capturev(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z28test_lambda_implicit_capturev.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds nuw [[CLASS_ANON_24:%.*]], ptr [[LAMBDA:%.*]], i32 0, i32 0
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[TMP1]], align 4
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds nuw [[CLASS_ANON_24]], ptr [[LAMBDA]], i32 0, i32 1
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    store i32 [[TMP4]], ptr [[TMP3]], align 4
+// CHECK:    [[CALL:%.*]] = call noundef i32 @"_ZZ28test_lambda_implicit_capturevENK3$_0clEv"(ptr noundef nonnull align 4 dereferenceable(8) [[LAMBDA]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z24test_parallel_for_linearv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z24test_parallel_for_linearv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z24test_parallel_for_linearv.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z24test_parallel_for_linearv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[DOTLINEAR_START:%.*]], align 4
+// CHECK:    store i32 0, ptr [[DOTOMP_LB:%.*]], align 4
+// CHECK:    store i32 9, ptr [[DOTOMP_UB:%.*]], align 4
+// CHECK:    store i32 1, ptr [[DOTOMP_STRIDE:%.*]], align 4
+// CHECK:    store i32 0, ptr [[DOTOMP_IS_LAST:%.*]], align 4
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4
+// CHECK:    call void @__kmpc_barrier(ptr @[[GLOB7:[0-9]+]], i32 [[TMP4]])
+// CHECK:    call void @__kmpc_for_static_init_4(ptr @[[GLOB2]], i32 [[TMP4]], i32 34, ptr [[DOTOMP_IS_LAST]], ptr [[DOTOMP_LB]], ptr [[DOTOMP_UB]], ptr [[DOTOMP_STRIDE]], i32 1, i32 1)
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP:%.*]] = icmp sgt i32 [[TMP5]], 9
+// CHECK:    br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+// CHECK:       [[COND_TRUE]]:
+// CHECK:    br label %[[COND_END:.*]]
+// CHECK:       [[COND_FALSE]]:
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    br label %[[COND_END]]
+// CHECK:       [[COND_END]]:
+// CHECK:    [[COND:%.*]] = phi i32 [ 9, %[[COND_TRUE]] ], [ [[TMP6]], %[[COND_FALSE]] ]
+// CHECK:    store i32 [[COND]], ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[DOTOMP_LB]], align 4
+// CHECK:    store i32 [[TMP7]], ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP1:%.*]] = icmp sle i32 [[TMP8]], [[TMP9]]
+// CHECK:    br i1 [[CMP1]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP10]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[DOTLINEAR_START]], align 4
+// CHECK:    [[TMP12:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[MUL2:%.*]] = mul nsw i32 [[TMP12]], 1
+// CHECK:    [[ADD3:%.*]] = add nsw i32 [[TMP11]], [[MUL2]]
+// CHECK:    store i32 [[ADD3]], ptr [[A:%.*]], align 4
+// CHECK:    [[TMP13:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    [[ADD4:%.*]] = add nsw i32 [[TMP13]], 1
+// CHECK:    store i32 [[ADD4]], ptr [[A]], align 4
+// CHECK:    [[TMP14:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    call void @_Z3usei(i32 noundef [[TMP14]])
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP15:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[ADD5:%.*]] = add nsw i32 [[TMP15]], 1
+// CHECK:    store i32 [[ADD5]], ptr [[DOTOMP_IV]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    br label %[[OMP_LOOP_EXIT:.*]]
+// CHECK:       [[OMP_LOOP_EXIT]]:
+// CHECK:    call void @__kmpc_for_static_fini(ptr @[[GLOB2]], i32 [[TMP4]])
+// CHECK:    [[TMP16:%.*]] = load i32, ptr [[DOTOMP_IS_LAST]], align 4
+// CHECK:    [[TMP17:%.*]] = icmp ne i32 [[TMP16]], 0
+// CHECK:    br i1 [[TMP17]], label %[[DOTOMP_LINEAR_PU:.*]], label %[[DOTOMP_LINEAR_PU_DONE:.*]]
+// CHECK:       [[DOTOMP_LINEAR_PU]]:
+// CHECK:    [[X6:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP18:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    store i32 [[TMP18]], ptr [[X6]], align 4
+// CHECK:    br label %[[DOTOMP_LINEAR_PU_DONE]]
+// CHECK:       [[DOTOMP_LINEAR_PU_DONE]]:
+// CHECK:    call void @__kmpc_barrier(ptr @[[GLOB7]], i32 [[TMP4]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z25test_taskloop_lastprivatev(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z25test_taskloop_lastprivatev.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z25test_taskloop_lastprivatev.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z25test_taskloop_lastprivatev.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_25:%.*]], ptr [[AGG_CAPTURED:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP1]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4
+// CHECK:    call void @__kmpc_taskgroup(ptr @[[GLOB1]], i32 [[TMP4]])
+// CHECK:    [[TMP5:%.*]] = call ptr @__kmpc_omp_task_alloc(ptr @[[GLOB1]], i32 [[TMP4]], i32 1, i64 88, i64 8, ptr @.omp_task_entry..35)
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_27:%.*]], ptr [[TMP5]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12:%.*]], ptr [[TMP6]], i32 0, i32 0
+// CHECK:    [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP8]], ptr align 8 [[AGG_CAPTURED]], i64 8, i1 false)
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_27]], ptr [[TMP5]], i32 0, i32 1
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP6]], i32 0, i32 5
+// CHECK:    store i64 0, ptr [[TMP10]], align 8
+// CHECK:    [[TMP11:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP6]], i32 0, i32 6
+// CHECK:    store i64 9, ptr [[TMP11]], align 8
+// CHECK:    [[TMP12:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP6]], i32 0, i32 7
+// CHECK:    store i64 1, ptr [[TMP12]], align 8
+// CHECK:    [[TMP13:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP6]], i32 0, i32 9
+// CHECK:    call void @llvm.memset.p0.i64(ptr align 8 [[TMP13]], i8 0, i64 8, i1 false)
+// CHECK:    [[TMP14:%.*]] = load i64, ptr [[TMP12]], align 8
+// CHECK:    call void @__kmpc_taskloop(ptr @[[GLOB1]], i32 [[TMP4]], ptr [[TMP5]], i32 1, ptr [[TMP10]], ptr [[TMP11]], i64 [[TMP14]], i32 1, i32 0, i64 0, ptr @.omp_task_dup.)
+// CHECK:    call void @__kmpc_end_taskgroup(ptr @[[GLOB1]], i32 [[TMP4]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @.omp_task_privates_map..34(
+// CHECK-SAME: ptr noalias noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds nuw [[STRUCT__KMP_PRIVATES_T_26:%.*]], ptr [[TMP2]], i32 0, i32 0
+// CHECK:    [[TMP4:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    store ptr [[TMP3]], ptr [[TMP4]], align 8
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal noundef i32 @.omp_task_entry..35(
+// CHECK-SAME: i32 noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store i32 [[TMP0]], ptr [[DOTADDR:%.*]], align 4
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTADDR]], align 4
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_27:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12:%.*]], ptr [[TMP4]], i32 0, i32 2
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_27]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 5
+// CHECK:    [[TMP10:%.*]] = load i64, ptr [[TMP9]], align 8
+// CHECK:    [[TMP11:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 6
+// CHECK:    [[TMP12:%.*]] = load i64, ptr [[TMP11]], align 8
+// CHECK:    [[TMP13:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 7
+// CHECK:    [[TMP14:%.*]] = load i64, ptr [[TMP13]], align 8
+// CHECK:    [[TMP15:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 8
+// CHECK:    [[TMP16:%.*]] = load i32, ptr [[TMP15]], align 8
+// CHECK:    [[TMP17:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12]], ptr [[TMP4]], i32 0, i32 9
+// CHECK:    [[TMP18:%.*]] = load ptr, ptr [[TMP17]], align 8
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META132:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META135:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META137:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META139:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META141:![0-9]+]])
+// CHECK:    store i32 [[TMP2]], ptr [[DOTGLOBAL_TID__ADDR_I:%.*]], align 4, !noalias [[META143:![0-9]+]]
+// CHECK:    store ptr [[TMP5]], ptr [[DOTPART_ID__ADDR_I:%.*]], align 8, !noalias [[META143]]
+// CHECK:    store ptr [[TMP8]], ptr [[DOTPRIVATES__ADDR_I:%.*]], align 8, !noalias [[META143]]
+// CHECK:    store ptr @.omp_task_privates_map..34, ptr [[DOTCOPY_FN__ADDR_I:%.*]], align 8, !noalias [[META143]]
+// CHECK:    store ptr [[TMP3]], ptr [[DOTTASK_T__ADDR_I:%.*]], align 8, !noalias [[META143]]
+// CHECK:    store i64 [[TMP10]], ptr [[DOTLB__ADDR_I:%.*]], align 8, !noalias [[META143]]
+// CHECK:    store i64 [[TMP12]], ptr [[DOTUB__ADDR_I:%.*]], align 8, !noalias [[META143]]
+// CHECK:    store i64 [[TMP14]], ptr [[DOTST__ADDR_I:%.*]], align 8, !noalias [[META143]]
+// CHECK:    store i32 [[TMP16]], ptr [[DOTLITER__ADDR_I:%.*]], align 4, !noalias [[META143]]
+// CHECK:    store ptr [[TMP18]], ptr [[DOTREDUCTIONS__ADDR_I:%.*]], align 8, !noalias [[META143]]
+// CHECK:    store ptr [[TMP7]], ptr [[__CONTEXT_ADDR_I:%.*]], align 8, !noalias [[META143]]
+// CHECK:    [[TMP19:%.*]] = load ptr, ptr [[__CONTEXT_ADDR_I]], align 8, !noalias [[META143]]
+// CHECK:    [[TMP20:%.*]] = load ptr, ptr [[DOTCOPY_FN__ADDR_I]], align 8, !noalias [[META143]]
+// CHECK:    [[TMP21:%.*]] = load ptr, ptr [[DOTPRIVATES__ADDR_I]], align 8, !noalias [[META143]]
+// CHECK:    call void [[TMP20]](ptr [[TMP21]], ptr [[DOTLASTPRIV_PTR_ADDR_I:%.*]]) #{{[0-9]+}}, !inline_history [[META144:![0-9]+]]
+// CHECK:    [[TMP22:%.*]] = load ptr, ptr [[TMP19]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP23:%.*]] = load ptr, ptr [[DOTLASTPRIV_PTR_ADDR_I]], align 8, !noalias [[META143]]
+// CHECK:    [[TMP24:%.*]] = load i64, ptr [[DOTLB__ADDR_I]], align 8, !noalias [[META143]]
+// CHECK:    [[CONV_I:%.*]] = trunc i64 [[TMP24]] to i32
+// CHECK:    store i32 [[CONV_I]], ptr [[DOTOMP_IV_I:%.*]], align 4, !noalias [[META143]]
+// CHECK:    [[TMP25:%.*]] = load ptr, ptr [[TMP19]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    br label %[[OMP_INNER_FOR_COND_I:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND_I]]:
+// CHECK:    [[TMP26:%.*]] = load i32, ptr [[DOTOMP_IV_I]], align 4, !noalias [[META143]]
+// CHECK:    [[CONV1_I:%.*]] = sext i32 [[TMP26]] to i64
+// CHECK:    [[TMP27:%.*]] = load i64, ptr [[DOTUB__ADDR_I]], align 8, !noalias [[META143]]
+// CHECK:    [[CMP_I:%.*]] = icmp ule i64 [[CONV1_I]], [[TMP27]]
+// CHECK:    br i1 [[CMP_I]], label %[[OMP_INNER_FOR_BODY_I:.*]], label %[[OMP_INNER_FOR_END_I:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY_I]]:
+// CHECK:    [[TMP28:%.*]] = load i32, ptr [[DOTOMP_IV_I]], align 4, !noalias [[META143]]
+// CHECK:    store i32 [[TMP28]], ptr [[I_I:%.*]], align 4, !noalias [[META143]]
+// CHECK:    [[TMP29:%.*]] = load i32, ptr [[I_I]], align 4, !noalias [[META143]]
+// CHECK:    [[MUL2_I:%.*]] = mul nsw i32 [[TMP29]], 10
+// CHECK:    store i32 [[MUL2_I]], ptr [[TMP23]], align 4
+// CHECK:    [[TMP30:%.*]] = load i32, ptr [[DOTOMP_IV_I]], align 4, !noalias [[META143]]
+// CHECK:    [[ADD3_I:%.*]] = add nsw i32 [[TMP30]], 1
+// CHECK:    store i32 [[ADD3_I]], ptr [[DOTOMP_IV_I]], align 4, !noalias [[META143]]
+// CHECK:    br label %[[OMP_INNER_FOR_COND_I]]
+// CHECK:       [[OMP_INNER_FOR_END_I]]:
+// CHECK:    [[TMP31:%.*]] = load i32, ptr [[DOTLITER__ADDR_I]], align 4, !noalias [[META143]]
+// CHECK:    [[TMP32:%.*]] = icmp ne i32 [[TMP31]], 0
+// CHECK:    br i1 [[TMP32]], label %[[DOTOMP_LASTPRIVATE_THEN_I:.*]], label %[[DOTOMP_OUTLINED__33_EXIT:.*]]
+// CHECK:       [[DOTOMP_LASTPRIVATE_THEN_I]]:
+// CHECK:    [[TMP33:%.*]] = load i32, ptr [[TMP23]], align 4
+// CHECK:    store i32 [[TMP33]], ptr [[TMP22]], align 4
+// CHECK:    br label %[[DOTOMP_OUTLINED__33_EXIT]]
+// CHECK:       [[DOTOMP_OUTLINED__33_EXIT]]:
+// CHECK:    ret i32 0
+//
+//
+// CHECK-LABEL: define internal void @.omp_task_dup.(
+// CHECK-SAME: ptr noundef [[TMP0:%.*]], ptr noundef [[TMP1:%.*]], i32 noundef [[TMP2:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    store i32 [[TMP2]], ptr [[DOTADDR2:%.*]], align 4
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_27:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_12:%.*]], ptr [[TMP4]], i32 0, i32 8
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[DOTADDR2]], align 4
+// CHECK:    store i32 [[TMP6]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_27]], ptr [[TMP3]], i32 0, i32 1
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z16test_task_sharedv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z16test_task_sharedv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z16test_task_sharedv.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z16test_task_sharedv.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON_28:%.*]], ptr [[AGG_CAPTURED:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP1]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4
+// CHECK:    [[TMP5:%.*]] = call ptr @__kmpc_omp_task_alloc(ptr @[[GLOB1]], i32 [[TMP4]], i32 1, i64 40, i64 8, ptr @.omp_task_entry..37)
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_29:%.*]], ptr [[TMP5]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP6]], i32 0, i32 0
+// CHECK:    [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP8]], ptr align 8 [[AGG_CAPTURED]], i64 8, i1 false)
+// CHECK:    [[TMP9:%.*]] = call i32 @__kmpc_omp_task(ptr @[[GLOB1]], i32 [[TMP4]], ptr [[TMP5]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal noundef i32 @.omp_task_entry..37(
+// CHECK-SAME: i32 noundef [[TMP0:%.*]], ptr noalias noundef [[TMP1:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store i32 [[TMP0]], ptr [[DOTADDR:%.*]], align 4
+// CHECK:    store ptr [[TMP1]], ptr [[DOTADDR1:%.*]], align 8
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[DOTADDR]], align 4
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T_WITH_PRIVATES_29:%.*]], ptr [[TMP3]], i32 0, i32 0
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T:%.*]], ptr [[TMP4]], i32 0, i32 2
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds nuw [[STRUCT_KMP_TASK_T]], ptr [[TMP4]], i32 0, i32 0
+// CHECK:    [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META145:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META148:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META150:![0-9]+]])
+// CHECK:    call void @llvm.experimental.noalias.scope.decl(metadata [[META152:![0-9]+]])
+// CHECK:    store i32 [[TMP2]], ptr [[DOTGLOBAL_TID__ADDR_I:%.*]], align 4, !noalias [[META154:![0-9]+]]
+// CHECK:    store ptr [[TMP5]], ptr [[DOTPART_ID__ADDR_I:%.*]], align 8, !noalias [[META154]]
+// CHECK:    store ptr null, ptr [[DOTPRIVATES__ADDR_I:%.*]], align 8, !noalias [[META154]]
+// CHECK:    store ptr null, ptr [[DOTCOPY_FN__ADDR_I:%.*]], align 8, !noalias [[META154]]
+// CHECK:    store ptr [[TMP3]], ptr [[DOTTASK_T__ADDR_I:%.*]], align 8, !noalias [[META154]]
+// CHECK:    store ptr [[TMP7]], ptr [[__CONTEXT_ADDR_I:%.*]], align 8, !noalias [[META154]]
+// CHECK:    [[TMP8:%.*]] = load ptr, ptr [[__CONTEXT_ADDR_I]], align 8, !noalias [[META154]]
+// CHECK:    [[TMP9:%.*]] = load ptr, ptr [[TMP8]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 42, ptr [[TMP9]], align 4
+// CHECK:    ret i32 0
+//
+//
+// CHECK-LABEL: define dso_local void @_Z42test_parallel_for_firstprivate_lastprivatev(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z42test_parallel_for_firstprivate_lastprivatev.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z42test_parallel_for_firstprivate_lastprivatev.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z42test_parallel_for_firstprivate_lastprivatev.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 0, ptr [[DOTOMP_LB:%.*]], align 4
+// CHECK:    store i32 9, ptr [[DOTOMP_UB:%.*]], align 4
+// CHECK:    store i32 1, ptr [[DOTOMP_STRIDE:%.*]], align 4
+// CHECK:    store i32 0, ptr [[DOTOMP_IS_LAST:%.*]], align 4
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    store i32 [[TMP2]], ptr [[A:%.*]], align 4
+// CHECK:    [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP3:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4
+// CHECK:    call void @__kmpc_for_static_init_4(ptr @[[GLOB2]], i32 [[TMP4]], i32 34, ptr [[DOTOMP_IS_LAST]], ptr [[DOTOMP_LB]], ptr [[DOTOMP_UB]], ptr [[DOTOMP_STRIDE]], i32 1, i32 1)
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP:%.*]] = icmp sgt i32 [[TMP5]], 9
+// CHECK:    br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+// CHECK:       [[COND_TRUE]]:
+// CHECK:    br label %[[COND_END:.*]]
+// CHECK:       [[COND_FALSE]]:
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    br label %[[COND_END]]
+// CHECK:       [[COND_END]]:
+// CHECK:    [[COND:%.*]] = phi i32 [ 9, %[[COND_TRUE]] ], [ [[TMP6]], %[[COND_FALSE]] ]
+// CHECK:    store i32 [[COND]], ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[DOTOMP_LB]], align 4
+// CHECK:    store i32 [[TMP7]], ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP2:%.*]] = icmp sle i32 [[TMP8]], [[TMP9]]
+// CHECK:    br i1 [[CMP2]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP10]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[I]], align 4
+// CHECK:    [[MUL3:%.*]] = mul nsw i32 [[TMP11]], 10
+// CHECK:    store i32 [[MUL3]], ptr [[A]], align 4
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP12:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[ADD4:%.*]] = add nsw i32 [[TMP12]], 1
+// CHECK:    store i32 [[ADD4]], ptr [[DOTOMP_IV]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    br label %[[OMP_LOOP_EXIT:.*]]
+// CHECK:       [[OMP_LOOP_EXIT]]:
+// CHECK:    call void @__kmpc_for_static_fini(ptr @[[GLOB2]], i32 [[TMP4]])
+// CHECK:    [[TMP13:%.*]] = load i32, ptr [[DOTOMP_IS_LAST]], align 4
+// CHECK:    [[TMP14:%.*]] = icmp ne i32 [[TMP13]], 0
+// CHECK:    br i1 [[TMP14]], label %[[DOTOMP_LASTPRIVATE_THEN:.*]], label %[[DOTOMP_LASTPRIVATE_DONE:.*]]
+// CHECK:       [[DOTOMP_LASTPRIVATE_THEN]]:
+// CHECK:    [[TMP15:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    store i32 [[TMP15]], ptr [[X1]], align 4
+// CHECK:    br label %[[DOTOMP_LASTPRIVATE_DONE]]
+// CHECK:       [[DOTOMP_LASTPRIVATE_DONE]]:
+// CHECK:    call void @__kmpc_barrier(ptr @[[GLOB7]], i32 [[TMP4]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z29test_private_plus_lastprivatev(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z29test_private_plus_lastprivatev.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    store ptr [[Y]], ptr [[B:%.*]], align 8
+// CHECK:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @_Z29test_private_plus_lastprivatev.omp_outlined, ptr [[TMP0]])
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_Z29test_private_plus_lastprivatev.omp_outlined(
+// CHECK-SAME: ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    store i32 0, ptr [[DOTOMP_LB:%.*]], align 4
+// CHECK:    store i32 9, ptr [[DOTOMP_UB:%.*]], align 4
+// CHECK:    store i32 1, ptr [[DOTOMP_STRIDE:%.*]], align 4
+// CHECK:    store i32 0, ptr [[DOTOMP_IS_LAST:%.*]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[TMP2]], align 4
+// CHECK:    call void @__kmpc_for_static_init_4(ptr @[[GLOB2]], i32 [[TMP3]], i32 34, ptr [[DOTOMP_IS_LAST]], ptr [[DOTOMP_LB]], ptr [[DOTOMP_UB]], ptr [[DOTOMP_STRIDE]], i32 1, i32 1)
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP:%.*]] = icmp sgt i32 [[TMP4]], 9
+// CHECK:    br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+// CHECK:       [[COND_TRUE]]:
+// CHECK:    br label %[[COND_END:.*]]
+// CHECK:       [[COND_FALSE]]:
+// CHECK:    [[TMP5:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    br label %[[COND_END]]
+// CHECK:       [[COND_END]]:
+// CHECK:    [[COND:%.*]] = phi i32 [ 9, %[[COND_TRUE]] ], [ [[TMP5]], %[[COND_FALSE]] ]
+// CHECK:    store i32 [[COND]], ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[TMP6:%.*]] = load i32, ptr [[DOTOMP_LB]], align 4
+// CHECK:    store i32 [[TMP6]], ptr [[DOTOMP_IV:%.*]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND:.*]]
+// CHECK:       [[OMP_INNER_FOR_COND]]:
+// CHECK:    [[TMP7:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[TMP8:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4
+// CHECK:    [[CMP1:%.*]] = icmp sle i32 [[TMP7]], [[TMP8]]
+// CHECK:    br i1 [[CMP1]], label %[[OMP_INNER_FOR_BODY:.*]], label %[[OMP_INNER_FOR_END:.*]]
+// CHECK:       [[OMP_INNER_FOR_BODY]]:
+// CHECK:    [[TMP9:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[MUL:%.*]] = mul nsw i32 [[TMP9]], 1
+// CHECK:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
+// CHECK:    store i32 [[ADD]], ptr [[I:%.*]], align 4
+// CHECK:    [[TMP10:%.*]] = load i32, ptr [[I]], align 4
+// CHECK:    store i32 [[TMP10]], ptr [[A:%.*]], align 4
+// CHECK:    [[TMP11:%.*]] = load i32, ptr [[A]], align 4
+// CHECK:    store i32 [[TMP11]], ptr [[B:%.*]], align 4
+// CHECK:    br label %[[OMP_BODY_CONTINUE:.*]]
+// CHECK:       [[OMP_BODY_CONTINUE]]:
+// CHECK:    br label %[[OMP_INNER_FOR_INC:.*]]
+// CHECK:       [[OMP_INNER_FOR_INC]]:
+// CHECK:    [[TMP12:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[TMP12]], 1
+// CHECK:    store i32 [[ADD2]], ptr [[DOTOMP_IV]], align 4
+// CHECK:    br label %[[OMP_INNER_FOR_COND]]
+// CHECK:       [[OMP_INNER_FOR_END]]:
+// CHECK:    br label %[[OMP_LOOP_EXIT:.*]]
+// CHECK:       [[OMP_LOOP_EXIT]]:
+// CHECK:    call void @__kmpc_for_static_fini(ptr @[[GLOB2]], i32 [[TMP3]])
+// CHECK:    [[TMP13:%.*]] = load i32, ptr [[DOTOMP_IS_LAST]], align 4
+// CHECK:    [[TMP14:%.*]] = icmp ne i32 [[TMP13]], 0
+// CHECK:    br i1 [[TMP14]], label %[[DOTOMP_LASTPRIVATE_THEN:.*]], label %[[DOTOMP_LASTPRIVATE_DONE:.*]]
+// CHECK:       [[DOTOMP_LASTPRIVATE_THEN]]:
+// CHECK:    [[TMP15:%.*]] = load i32, ptr [[B]], align 4
+// CHECK:    store i32 [[TMP15]], ptr [[Y]], align 4
+// CHECK:    br label %[[DOTOMP_LASTPRIVATE_DONE]]
+// CHECK:       [[DOTOMP_LASTPRIVATE_DONE]]:
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z32test_target_map_orig_use_bindingv(
+// CHECK-SAME: ) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z32test_target_map_orig_use_bindingv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[P]], ptr [[TMP1]], align 8
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[P]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_MAPPERS:%.*]], i64 0, i64 0
+// CHECK:    store ptr null, ptr [[TMP3]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 1
+// CHECK:    store ptr [[TMP0]], ptr [[TMP4]], align 8
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 1
+// CHECK:    store ptr [[TMP0]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 1
+// CHECK:    store ptr null, ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 2
+// CHECK:    store ptr null, ptr [[TMP7]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 2
+// CHECK:    store ptr null, ptr [[TMP8]], align 8
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 2
+// CHECK:    store ptr null, ptr [[TMP9]], align 8
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 0
+// CHECK:    [[TMP11:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 0
+// CHECK:    [[TMP12:%.*]] = getelementptr inbounds nuw [[STRUCT___TGT_KERNEL_ARGUMENTS:%.*]], ptr [[KERNEL_ARGS:%.*]], i32 0, i32 0
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z32test_target_map_orig_use_bindingv_l732(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[P:%.*]], ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noalias noundef [[DYN_PTR:%.*]]) #{{[0-9]+}} {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[P]], ptr [[P_ADDR:%.*]], align 8
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META18]], !align [[META19]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    store i32 1, ptr [[X]], align 4
+// CHECK:    [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP2]], i32 0, i32 0
+// CHECK:    store i32 2, ptr [[X1]], align 4
+// CHECK:    ret void
+//

diff  --git a/clang/test/OpenMP/structured-bindings-messages.cpp b/clang/test/OpenMP/structured-bindings-messages.cpp
new file mode 100644
index 0000000000000..647de0055068c
--- /dev/null
+++ b/clang/test/OpenMP/structured-bindings-messages.cpp
@@ -0,0 +1,565 @@
+// RUN: %clang_cc1 -verify -std=c++20 -triple x86_64-pc-linux-gnu -fopenmp \
+// RUN: -fsyntax-only %s
+
+namespace std {
+  typedef unsigned long size_t;
+  
+  // move.
+  template<typename T>
+  constexpr T&& move(T& t) noexcept {
+    return static_cast<T&&>(t);
+  }
+
+  // pair.
+  template <typename T1, typename T2>
+  struct pair {
+    T1 first;
+    T2 second;
+  };
+
+  template <typename T1, typename T2>
+  pair<T1, T2> make_pair(T1 a, T2 b) {
+    return {a, b};
+  }
+
+  // tuple.
+  template <typename... Ts>
+  struct tuple;
+
+  template <typename T, typename... Ts>
+  struct tuple<T, Ts...> {
+    T head;
+    tuple<Ts...> tail;
+  };
+
+  template <>
+  struct tuple<> {};
+
+  template <size_t I, typename T>
+  struct tuple_element;
+
+  template <typename T1, typename T2>
+  struct tuple_element<0, pair<T1, T2>> { using type = T1; };
+
+  template <typename T1, typename T2>
+  struct tuple_element<1, pair<T1, T2>> { using type = T2; };
+
+  template <typename T, typename... Ts>
+  struct tuple_element<0, tuple<T, Ts...>> { using type = T; };
+
+  template <size_t I, typename T, typename... Ts>
+  struct tuple_element<I, tuple<T, Ts...>> {
+    using type = typename tuple_element<I-1, tuple<Ts...>>::type;
+  };
+
+  template <size_t N, typename T>
+  struct tuple_element<0, T[N]> { using type = T; };
+
+  template <typename T>
+  struct tuple_size;
+
+  template <typename T1, typename T2>
+  struct tuple_size<pair<T1, T2>> {
+    static constexpr size_t value = 2;
+  };
+
+  template <typename... Ts>
+  struct tuple_size<tuple<Ts...>> {
+    static constexpr size_t value = sizeof...(Ts);
+  };
+
+  template <typename T, size_t N>
+  struct tuple_size<T[N]> {
+    static constexpr size_t value = N;
+  };
+
+  template <size_t I, typename T1, typename T2>
+  typename tuple_element<I, pair<T1, T2>>::type &
+  get(pair<T1, T2> &p) {
+    if constexpr (I == 0) return p.first;
+    else return p.second;
+  }
+
+  template <size_t I, typename T1, typename T2>
+  typename tuple_element<I, pair<T1, T2>>::type &&
+  get(pair<T1, T2> &&p) {
+    if constexpr (I == 0) return static_cast<T1&&>(p.first);
+    else return static_cast<T2&&>(p.second);
+  }
+
+  template <size_t I, typename T, typename... Ts>
+  auto& get(tuple<T, Ts...> &t) {
+    if constexpr (I == 0) return t.head;
+    else return get<I-1>(t.tail);
+  }
+
+  template <size_t I, typename T, typename... Ts>
+  auto&& get(tuple<T, Ts...> &&t) {
+    if constexpr (I == 0) return static_cast<T&&>(t.head);
+    else return get<I-1>(static_cast<tuple<Ts...>&&>(t.tail));
+  }
+
+  // array.
+  template <typename T, size_t N>
+  struct array {
+    T data[N];
+    T& operator[](size_t i) { return data[i]; }
+    const T& operator[](size_t i) const { return data[i]; }
+  };
+
+  template <size_t I, typename T, size_t N>
+  struct tuple_element<I, array<T, N>> { using type = T; };
+
+  template <typename T, size_t N>
+  struct tuple_size<array<T, N>> {
+    static constexpr size_t value = N;
+  };
+
+  template <size_t I, typename T, size_t N>
+  T& get(array<T, N> &a) { return a.data[I]; }
+
+  template <size_t I, typename T, size_t N>
+  T&& get(array<T, N> &&a) { return static_cast<T&&>(a.data[I]); }
+}
+
+void use(int);
+
+void test_pair() {
+  auto [a, b] = std::make_pair(1, 2);
+  // expected-note at -1{{'a' declared here}}
+  // expected-note at -2{{'b' declared here}}
+#pragma omp parallel
+  {
+    use(a + b);
+    // expected-error at -1{{capturing tuple-like structured binding 'a' is not yet supported in OpenMP}}
+    // expected-error at -2{{capturing tuple-like structured binding 'b' is not yet supported in OpenMP}}
+  }
+}
+
+void test_pair_map() {
+  auto [a, b] = std::make_pair(1, 2);
+  // expected-note at -1{{'a' declared here}}
+  // expected-error at +1{{mapping of tuple-like structured binding is not yet supported}}
+#pragma omp target map(a)
+  {
+    // expected-error at +1{{capturing tuple-like structured binding 'a' is not yet supported in OpenMP}}
+    use(a);
+  }
+}
+
+void test_tuple() {
+  std::tuple<int, int, int> t = {1, 2, 3};
+  auto [x, y, z] = t;
+  // expected-note at -1{{'x' declared here}}
+  // expected-note at -2{{'y' declared here}}
+  // expected-note at -3{{'z' declared here}}
+#pragma omp parallel
+  {
+    use(x + y + z);
+    // expected-error at -1{{capturing tuple-like structured binding 'x' is not yet supported in OpenMP}}
+    // expected-error at -2{{capturing tuple-like structured binding 'y' is not yet supported in OpenMP}}
+    // expected-error at -3{{capturing tuple-like structured binding 'z' is not yet supported in OpenMP}}
+  }
+}
+
+void test_array() {
+  std::array<int, 2> arr = {1, 2};
+  auto [p, q] = arr;
+  // expected-note at -1{{'p' declared here}}
+  // expected-note at -2{{'q' declared here}}
+#pragma omp parallel
+  {
+    use(p + q);
+    // expected-error at -1{{capturing tuple-like structured binding 'p' is not yet supported in OpenMP}}
+    // expected-error at -2{{capturing tuple-like structured binding 'q' is not yet supported in OpenMP}}
+  }
+}
+
+struct Point {
+  int x, y;
+};
+
+struct ArrayStruct {
+  int arr[2];
+};
+
+#pragma omp declare reduction(mysum: int: omp_out += omp_in) initializer(omp_priv = 0)
+
+Point make_point() { return {1, 2}; }
+
+void test_function_call() {
+  auto [a, b] = make_point();
+  // expected-error at +1{{mapping of structured binding initialized from function call is not yet supported}}
+#pragma omp target map(a)
+  {
+    a++;
+  }
+}
+
+void test_brace_init() {
+  auto [a, b] = Point{1, 2};
+// expected-error at +1{{mapping of structured binding initialized from initializer list is not yet supported}}
+#pragma omp target map(a)
+  {
+    a++;
+  }
+}
+
+void test_move() {
+  Point p{1, 2};
+  auto [a, b] = std::move(p);
+  // expected-error at +1{{mapping of structured binding initialized from move expression is not yet supported}}
+#pragma omp target map(a)
+  { 
+    a++;
+  }
+}
+
+void test_constructor_with_function_call() {
+  auto [a, b] = Point(make_point());
+  // expected-error at +1{{mapping of structured binding initialized from function call is not yet supported}}
+#pragma omp target map(a)
+  {
+    a++;
+  }
+}
+
+void test_constructor_with_move_arg() {
+  Point p{1, 2};
+  auto [a, b] = Point(std::move(p));
+  // expected-error at +1{{mapping of structured binding initialized from move expression is not yet supported}}
+#pragma omp target map(a)
+  {
+    a++;
+  }
+}
+
+void test_constructor_with_member() {
+  struct Container { Point p; } c{{1, 2}};
+  auto [a, b] = Point(c.p);
+  // expected-error at +1{{mapping of structured binding initialized from temporary object is not yet supported}}
+#pragma omp target map(a)
+  {
+    a++;
+  }
+}
+
+
+void test_conflicting_capture_kinds_map_firstprivate() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  // map(a) creates by-ref capture, firstprivate(b) creates by-copy capture
+  // expected-error at +1{{bindings from structured binding 'b' require conflicting capture kinds (by-reference vs. by-copy)}}
+#pragma omp target map(a) firstprivate(b)
+  {
+    a++;
+    b++;
+  }
+}
+
+void test_conflicting_capture_kinds_map_private() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  // expected-error at +1{{bindings from structured binding 'b' require conflicting capture kinds (by-reference vs. by-copy)}}
+#pragma omp target map(a) private(b)
+  {
+    a++;
+    b++;
+  }
+}
+
+void test_conflicting_capture_kinds_parallel_shared_firstprivate() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  // expected-error at +1{{bindings from structured binding 'b' require conflicting capture kinds (by-reference vs. by-copy)}}
+#pragma omp parallel shared(a) firstprivate(b)
+  {
+    a = b;
+  }
+}
+
+void test_conflicting_capture_kinds_parallel_shared_private() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  // expected-error at +1{{bindings from structured binding 'b' require conflicting capture kinds (by-reference vs. by-copy)}}
+#pragma omp parallel private(a) shared(b)
+  {
+    a = 100;
+    b++;
+  }
+}
+
+void test_conflicting_capture_kinds_parallel_default_shared_firstprivate() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  // expected-error at +1{{bindings from structured binding 'b' require conflicting capture kinds (by-reference vs. by-copy)}}
+#pragma omp parallel default(shared) firstprivate(a)
+  {
+    a++;
+    b++;
+  }
+}
+
+void test_binding_as_loop_var_for() {
+  Point p{0, 0};
+  auto [a, b] = p;
+#pragma omp for
+  // expected-error at +1{{structured binding 'a' cannot be used as the loop control variable of an OpenMP loop}}
+  for (a = 0; a < 10; ++a)
+    ;
+}
+
+void test_binding_as_loop_var_parallel_for() {
+  Point p{0, 0};
+  auto [a, b] = p;
+#pragma omp parallel for
+  // expected-error at +1{{structured binding 'a' cannot be used as the loop control variable of an OpenMP loop}}
+  for (a = 0; a < 10; ++a)
+    ;
+}
+
+void test_binding_as_loop_var_simd() {
+  Point p{0, 0};
+  auto [a, b] = p;
+#pragma omp simd
+  // expected-error at +1{{structured binding 'a' cannot be used as the loop control variable of an OpenMP loop}}
+  for (a = 0; a < 10; ++a)
+    ;
+}
+
+void test_conflicting_capture_kinds_target_teams() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  // expected-error at +1{{bindings from structured binding 'b' require conflicting capture kinds (by-reference vs. by-copy)}}
+#pragma omp target teams map(a) firstprivate(b)
+  {
+    a++;
+    b++;
+  }
+}
+
+void test_conflicting_capture_kinds_target_teams_distribute() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  // expected-error at +1{{bindings from structured binding 'b' require conflicting capture kinds (by-reference vs. by-copy)}}
+#pragma omp target teams distribute map(a) firstprivate(b)
+  for (int i = 0; i < 10; ++i) {
+    a++;
+    b++;
+  }
+}
+
+void test_conflicting_capture_kinds_target_teams_loop() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  // expected-error at +1{{bindings from structured binding 'b' require conflicting capture kinds (by-reference vs. by-copy)}}
+#pragma omp target teams loop map(a) firstprivate(b)
+  for (int i = 0; i < 10; ++i) {
+    a++;
+    b++;
+  }
+}
+
+void test_conflicting_capture_kinds_target_teams_distribute_parallel_for() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  // expected-error at +1{{bindings from structured binding 'b' require conflicting capture kinds (by-reference vs. by-copy)}}
+#pragma omp target teams distribute parallel for map(a) firstprivate(b)
+  for (int i = 0; i < 10; ++i) {
+    a++;
+    b++;
+  }
+}
+
+void test_conflicting_capture_kinds_target_teams_distribute_parallel_for_simd() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  // expected-error at +1{{bindings from structured binding 'b' require conflicting capture kinds (by-reference vs. by-copy)}}
+#pragma omp target teams distribute parallel for simd map(a) firstprivate(b)
+  for (int i = 0; i < 10; ++i) {
+    a++;
+    b++;
+  }
+}
+
+void test_conflicting_capture_kinds_target_teams_distribute_simd() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  // expected-error at +1{{bindings from structured binding 'b' require conflicting capture kinds (by-reference vs. by-copy)}}
+#pragma omp target teams distribute simd map(a) firstprivate(b)
+  for (int i = 0; i < 10; ++i) {
+    a++;
+    b++;
+  }
+}
+
+void test_lastprivate_conditional() {
+  Point pt{1, 2};
+  auto [a, b] = pt;
+  // expected-note at -1{{'a' defined here}}
+  // expected-note at -2{{'b' defined here}}
+  // expected-error at +1{{conditional lastprivate on structured bindings is not yet supported}}
+#pragma omp parallel for lastprivate(conditional: a)
+  for (int i = 0; i < 10; ++i) {
+    if (i == 5)
+      a = i;
+  }
+  // expected-error at +1{{conditional lastprivate on structured bindings is not yet supported}}
+#pragma omp parallel for lastprivate(conditional: b)
+  for (int i = 0; i < 10; ++i) {
+    b = i;
+  }
+}
+
+void test_reduction_task() {
+  Point p{0, 0};
+  auto [a, b] = p;
+  // expected-error at +1{{reductions on structured bindings are not yet supported}}
+#pragma omp parallel reduction(task, +:a)
+  {
+    a += 1;
+  }
+}
+
+void test_reduction_inscan() {
+  Point p{0, 0};
+  auto [a, b] = p;
+  // expected-error at +1{{reductions on structured bindings are not yet supported}}
+#pragma omp for reduction(inscan, +:a)
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +1{{the list item must appear in 'reduction' clause with the 'inscan' modifier of the parent directive}}
+#pragma omp scan inclusive(a)
+    a += i;
+  }
+}
+
+void test_reduction_binding_nontrivial() {
+  struct NonTrivial {
+    int value;
+    NonTrivial() : value(0) {}
+    NonTrivial(int v) : value(v) {}
+    ~NonTrivial() {}
+    NonTrivial& operator+=(int x) { value += x; return *this; }
+    NonTrivial& operator+=(const NonTrivial& other) { value += other.value; return *this; }
+  };
+  struct PairNonTrivial {
+    NonTrivial a;
+    NonTrivial b;
+  };
+
+  PairNonTrivial p{NonTrivial(0), NonTrivial(0)};
+  auto [a, b] = p;
+
+  // expected-error at +1{{reductions on structured bindings are not yet supported}}
+#pragma omp parallel for reduction(+:a)
+  for (int i = 0; i < 10; ++i) {
+    a += i;
+  }
+  use(a.value);
+}
+
+void test_reduction_binding_sum() {
+  Point p{0, 0};
+  auto [a, b] = p;
+
+  // expected-error at +1{{reductions on structured bindings are not yet supported}}
+#pragma omp parallel for reduction(+:a)
+  for (int i = 0; i < 100; ++i) {
+    a += i;
+  }
+  use(a);
+}
+
+void test_reduction_binding_operators() {
+  Point p{1, 100};
+  auto [a, b] = p;
+
+  // expected-error at +1 2{{reductions on structured bindings are not yet supported}}
+#pragma omp parallel for reduction(*:a) reduction(min:b)
+  for (int i = 1; i <= 10; ++i) {
+    a *= 2;
+    if (i < b) b = i;
+  }
+  use(a);
+  use(b);
+}
+
+void test_reduction_binding_max() {
+  Point p{-100, -100};
+  auto [a, b] = p;
+
+  // expected-error at +1 2{{reductions on structured bindings are not yet supported}}
+#pragma omp parallel for reduction(max:a,b)
+  for (int i = 0; i < 100; ++i) {
+    if (i > a) a = i;
+    if (i > b) b = i;
+  }
+  use(a);
+  use(b);
+}
+
+void test_array_reduction() {
+  ArrayStruct s{{1, 2}};
+  auto [arr] = s;
+
+  // expected-error at +1{{reductions on structured bindings are not yet supported}}
+#pragma omp parallel for reduction(+:arr)
+  for (int i = 0; i < 10; ++i) {
+    arr[0] += i;
+  }
+}
+
+void test_udr_reduction() {
+  Point p{0, 0};
+  auto [a, b] = p;
+
+  // expected-error at +1{{reductions on structured bindings are not yet supported}}
+#pragma omp parallel for reduction(mysum:a)
+  for (int i = 0; i < 10; ++i) {
+    a += i;
+  }
+}
+
+void test_linear_clause_parallel_for_simd() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  // expected-error at +1{{linear clause with parallel constructs on structured bindings is not yet supported}}
+#pragma omp parallel for simd linear(a : 1)
+  for (int i = 0; i < 10; ++i)
+    b += a;
+  // Note: This also fails for regular variables (pre-existing Clang bug)
+}
+
+void test_linear_clause_parallel_for() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  // expected-error at +1{{linear clause with parallel constructs on structured bindings is not yet supported}}
+#pragma omp parallel for linear(a : 1)
+  for (int i = 0; i < 10; ++i)
+    b += a;
+  // Note: This also fails for regular variables (pre-existing Clang bug)
+}
+
+void test_capture_binding_in_lambda() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  [&] {
+    // expected-error at +1{{capturing in a lambda or block on structured bindings is not yet supported}}
+#pragma omp target map(tofrom: a)
+    { a++; }
+  }();
+}
+
+struct Triple { int x, y, z; };
+
+void test_bindings_only_orig_not_dereferenced() {
+  Triple t{1, 2, 3};
+  auto [a, b, c] = t;
+  // expected-error at +1{{original variable 't' is explicitly mapped but only bindings from it are used in target region; either use 't' directly or map the bindings explicitly}}
+#pragma omp target map(tofrom: t)
+  {
+    a = a + 10;
+    b = b + 20;
+    c = c + 30;
+  }
+}

diff  --git a/clang/test/OpenMP/structured-bindings-nontemporal.cpp b/clang/test/OpenMP/structured-bindings-nontemporal.cpp
new file mode 100644
index 0000000000000..37e9405a036ca
--- /dev/null
+++ b/clang/test/OpenMP/structured-bindings-nontemporal.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -std=c++20 -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s
+
+// expected-no-diagnostics
+
+struct Point { int x, y; };
+
+// CHECK-LABEL: @_Z21test_simd_nontemporalv
+void test_simd_nontemporal() {
+  Point p{1, 2};
+  auto [a, b] = p;
+  int arr[10];
+
+#pragma omp simd nontemporal(a, b)
+  for (int i = 0; i < 10; ++i) {
+    // CHECK: load i32,{{.*}}!nontemporal
+    // CHECK: load i32,{{.*}}!nontemporal
+    arr[i] = a + b;
+  }
+}
+
+// CHECK-LABEL: @_Z30test_simd_nontemporal_capturedv
+void test_simd_nontemporal_captured() {
+  Point p{3, 4};
+  auto [a, b] = p;
+  int sum = 0;
+
+#pragma omp parallel
+  {
+#pragma omp simd nontemporal(a)
+    for (int i = 0; i < 10; ++i) {
+      // CHECK: load i32,{{.*}}!nontemporal
+      sum += a;
+    }
+  }
+}

diff  --git a/clang/test/OpenMP/structured-bindings-target-map-
diff erent-types.cpp b/clang/test/OpenMP/structured-bindings-target-map-
diff erent-types.cpp
new file mode 100644
index 0000000000000..abe55288a0e7c
--- /dev/null
+++ b/clang/test/OpenMP/structured-bindings-target-map-
diff erent-types.cpp
@@ -0,0 +1,336 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-globals all --filter-out-after "getelem.*kernel" --filter-out "= alloca.*" --include-generated-funcs --replace-value-regex "__omp_offloading_[0-9a-z]+_[0-9a-z]+" "reduction_size[.].+[.]" "pl_cond[.].+[.|,]" --prefix-filecheck-ir-name _ --global-value-regex "\.offload_.*" --global-hex-value-regex ".offload_maptypes.*" --version 6
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -std=c++20 \
+// RUN: -fopenmp-targets=x86_64-unknown-unknown \
+// RUN: -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s
+
+// expected-no-diagnostics
+
+struct Point { int x, y; };
+
+// Test that 
diff erent map clauses on bindings from the same decomposition
+// result in consistent capture behavior. The DecompositionDecl is captured
+// once (due to deduplication), and the capture kind should be determined
+// consistently regardless of which binding's map clause is processed first.
+
+
+void test_
diff erent_map_type() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp target map(tofrom:a) map(to:b)
+  {
+    a = a + 1;
+    b = b + 2;
+  }
+}
+
+void test_same_map_type_
diff erent() {
+  Point p{1, 2};
+  auto [a, b] = p;
+
+#pragma omp target map(tofrom:a) map(tofrom:b)
+  {
+    a = a + 1;
+    b = b + 2;
+  }
+}
+
+void test_one_binding_mapped() {
+  Point p{1, 2};
+  auto [a, b] = p;
+
+#pragma omp target map(tofrom:a)
+  {
+    a = a + 1;
+    b = b + 2;
+  }
+}
+
+void test_mixed_map_types_three_way() {
+  struct Triple { int x, y, z; };
+  Triple t{1, 2, 3};
+  auto [a, b, c] = t;
+
+#pragma omp target map(tofrom:a) map(to:b) map(from:c)
+  {
+    a = a + 1;
+    b = b + 2;
+    c = c + 3;
+  }
+}
+
+//.
+// CHECK: @.offload_sizes = private unnamed_addr constant [5 x i64] [i64 8, i64 0, i64 4, i64 4, i64 0]
+// CHECK: @.offload_maptypes = private unnamed_addr constant [5 x i64] [i64 [[#0x223]], i64 [[#0x0]], i64 [[#0x2000000000003]], i64 [[#0x2000000000001]], i64 [[#0x120]]]
+// CHECK: @.offload_sizes.1 = private unnamed_addr constant [5 x i64] [i64 8, i64 0, i64 4, i64 4, i64 0]
+// CHECK: @.offload_maptypes.2 = private unnamed_addr constant [5 x i64] [i64 [[#0x223]], i64 [[#0x0]], i64 [[#0x2000000000003]], i64 [[#0x2000000000003]], i64 [[#0x120]]]
+// CHECK: @.offload_sizes.3 = private unnamed_addr constant [3 x i64] [i64 8, i64 4, i64 0]
+// CHECK: @.offload_maptypes.4 = private unnamed_addr constant [3 x i64] [i64 [[#0x223]], i64 [[#0x3]], i64 [[#0x120]]]
+// CHECK: @.offload_sizes.5 = private unnamed_addr constant [6 x i64] [i64 12, i64 0, i64 4, i64 4, i64 4, i64 0]
+// CHECK: @.offload_maptypes.6 = private unnamed_addr constant [6 x i64] [i64 [[#0x223]], i64 [[#0x0]], i64 [[#0x2000000000003]], i64 [[#0x2000000000001]], i64 [[#0x2000000000002]], i64 [[#0x120]]]
+//.
+// CHECK-LABEL: define dso_local void @_Z23test_
diff erent_map_typev(
+// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z23test_
diff erent_map_typev.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    [[TMP1:%.*]] = getelementptr i32, ptr [[Y]], i32 1
+// CHECK:    [[TMP2:%.*]] = ptrtoaddr ptr [[TMP1]] to i64
+// CHECK:    [[TMP3:%.*]] = ptrtoaddr ptr [[X]] to i64
+// CHECK:    [[TMP4:%.*]] = sub i64 [[TMP2]], [[TMP3]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[DOTOFFLOAD_SIZES:%.*]], ptr align 8 @.offload_sizes, i64 40, i1 false)
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_BASEPTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_PTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_MAPPERS:%.*]], i64 0, i64 0
+// CHECK:    store ptr null, ptr [[TMP7]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 1
+// CHECK:    store ptr [[P]], ptr [[TMP8]], align 8
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 1
+// CHECK:    store ptr [[X]], ptr [[TMP9]], align 8
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds [5 x i64], ptr [[DOTOFFLOAD_SIZES]], i32 0, i32 1
+// CHECK:    store i64 [[TMP4]], ptr [[TMP10]], align 8
+// CHECK:    [[TMP11:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 1
+// CHECK:    store ptr null, ptr [[TMP11]], align 8
+// CHECK:    [[TMP12:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 2
+// CHECK:    store ptr [[P]], ptr [[TMP12]], align 8
+// CHECK:    [[TMP13:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 2
+// CHECK:    store ptr [[X]], ptr [[TMP13]], align 8
+// CHECK:    [[TMP14:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 2
+// CHECK:    store ptr null, ptr [[TMP14]], align 8
+// CHECK:    [[TMP15:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 3
+// CHECK:    store ptr [[P]], ptr [[TMP15]], align 8
+// CHECK:    [[TMP16:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 3
+// CHECK:    store ptr [[Y]], ptr [[TMP16]], align 8
+// CHECK:    [[TMP17:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 3
+// CHECK:    store ptr null, ptr [[TMP17]], align 8
+// CHECK:    [[TMP18:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 4
+// CHECK:    store ptr null, ptr [[TMP18]], align 8
+// CHECK:    [[TMP19:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 4
+// CHECK:    store ptr null, ptr [[TMP19]], align 8
+// CHECK:    [[TMP20:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 4
+// CHECK:    store ptr null, ptr [[TMP20]], align 8
+// CHECK:    [[TMP21:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 0
+// CHECK:    [[TMP22:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 0
+// CHECK:    [[TMP23:%.*]] = getelementptr inbounds [5 x i64], ptr [[DOTOFFLOAD_SIZES]], i32 0, i32 0
+// CHECK:    [[TMP24:%.*]] = getelementptr inbounds nuw [[STRUCT___TGT_KERNEL_ARGUMENTS:%.*]], ptr [[KERNEL_ARGS:%.*]], i32 0, i32 0
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z23test_
diff erent_map_typev_l19(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noalias noundef [[DYN_PTR:%.*]]) #[[ATTR2:[0-9]+]] {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META10:![0-9]+]], !align [[META11:![0-9]+]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP2]], 1
+// CHECK:    [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    store i32 [[ADD]], ptr [[X1]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[TMP3]], 2
+// CHECK:    [[Y3:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    store i32 [[ADD2]], ptr [[Y3]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z28test_same_map_type_
diff erentv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z28test_same_map_type_
diff erentv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    [[TMP1:%.*]] = getelementptr i32, ptr [[Y]], i32 1
+// CHECK:    [[TMP2:%.*]] = ptrtoaddr ptr [[TMP1]] to i64
+// CHECK:    [[TMP3:%.*]] = ptrtoaddr ptr [[X]] to i64
+// CHECK:    [[TMP4:%.*]] = sub i64 [[TMP2]], [[TMP3]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[DOTOFFLOAD_SIZES:%.*]], ptr align 8 @.offload_sizes.1, i64 40, i1 false)
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_BASEPTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_PTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_MAPPERS:%.*]], i64 0, i64 0
+// CHECK:    store ptr null, ptr [[TMP7]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 1
+// CHECK:    store ptr [[P]], ptr [[TMP8]], align 8
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 1
+// CHECK:    store ptr [[X]], ptr [[TMP9]], align 8
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds [5 x i64], ptr [[DOTOFFLOAD_SIZES]], i32 0, i32 1
+// CHECK:    store i64 [[TMP4]], ptr [[TMP10]], align 8
+// CHECK:    [[TMP11:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 1
+// CHECK:    store ptr null, ptr [[TMP11]], align 8
+// CHECK:    [[TMP12:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 2
+// CHECK:    store ptr [[P]], ptr [[TMP12]], align 8
+// CHECK:    [[TMP13:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 2
+// CHECK:    store ptr [[X]], ptr [[TMP13]], align 8
+// CHECK:    [[TMP14:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 2
+// CHECK:    store ptr null, ptr [[TMP14]], align 8
+// CHECK:    [[TMP15:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 3
+// CHECK:    store ptr [[P]], ptr [[TMP15]], align 8
+// CHECK:    [[TMP16:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 3
+// CHECK:    store ptr [[Y]], ptr [[TMP16]], align 8
+// CHECK:    [[TMP17:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 3
+// CHECK:    store ptr null, ptr [[TMP17]], align 8
+// CHECK:    [[TMP18:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 4
+// CHECK:    store ptr null, ptr [[TMP18]], align 8
+// CHECK:    [[TMP19:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 4
+// CHECK:    store ptr null, ptr [[TMP19]], align 8
+// CHECK:    [[TMP20:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 4
+// CHECK:    store ptr null, ptr [[TMP20]], align 8
+// CHECK:    [[TMP21:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 0
+// CHECK:    [[TMP22:%.*]] = getelementptr inbounds [5 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 0
+// CHECK:    [[TMP23:%.*]] = getelementptr inbounds [5 x i64], ptr [[DOTOFFLOAD_SIZES]], i32 0, i32 0
+// CHECK:    [[TMP24:%.*]] = getelementptr inbounds nuw [[STRUCT___TGT_KERNEL_ARGUMENTS:%.*]], ptr [[KERNEL_ARGS:%.*]], i32 0, i32 0
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z28test_same_map_type_
diff erentv_l30(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noalias noundef [[DYN_PTR:%.*]]) #[[ATTR2]] {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META10]], !align [[META11]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP2]], 1
+// CHECK:    [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    store i32 [[ADD]], ptr [[X1]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[TMP3]], 2
+// CHECK:    [[Y3:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    store i32 [[ADD2]], ptr [[Y3]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z23test_one_binding_mappedv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[P:%.*]], ptr align 4 @__const._Z23test_one_binding_mappedv.p, i64 8, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[P]], i64 8, i1 false)
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[TMP1:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP1]], align 8
+// CHECK:    [[TMP2:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP2]], align 8
+// CHECK:    [[TMP3:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_MAPPERS:%.*]], i64 0, i64 0
+// CHECK:    store ptr null, ptr [[TMP3]], align 8
+// CHECK:    [[TMP4:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 1
+// CHECK:    store ptr [[P]], ptr [[TMP4]], align 8
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 1
+// CHECK:    store ptr [[X]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 1
+// CHECK:    store ptr null, ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 2
+// CHECK:    store ptr null, ptr [[TMP7]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 2
+// CHECK:    store ptr null, ptr [[TMP8]], align 8
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 2
+// CHECK:    store ptr null, ptr [[TMP9]], align 8
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 0
+// CHECK:    [[TMP11:%.*]] = getelementptr inbounds [3 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 0
+// CHECK:    [[TMP12:%.*]] = getelementptr inbounds nuw [[STRUCT___TGT_KERNEL_ARGUMENTS:%.*]], ptr [[KERNEL_ARGS:%.*]], i32 0, i32 0
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z23test_one_binding_mappedv_l41(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(8) [[TMP0:%.*]], ptr noalias noundef [[DYN_PTR:%.*]]) #[[ATTR2]] {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META10]], !align [[META11]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP2]], 1
+// CHECK:    [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    store i32 [[ADD]], ptr [[X1]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[TMP3]], 2
+// CHECK:    [[Y3:%.*]] = getelementptr inbounds nuw [[STRUCT_POINT]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    store i32 [[ADD2]], ptr [[Y3]], align 4
+// CHECK:    ret void
+//
+//
+// CHECK-LABEL: define dso_local void @_Z30test_mixed_map_types_three_wayv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[T:%.*]], ptr align 4 @__const._Z30test_mixed_map_types_three_wayv.t, i64 12, i1 false)
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[TMP0:%.*]], ptr align 4 [[T]], i64 12, i1 false)
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE:%.*]], ptr [[TMP0]], i32 0, i32 0
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE]], ptr [[TMP0]], i32 0, i32 1
+// CHECK:    [[Z:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE]], ptr [[TMP0]], i32 0, i32 2
+// CHECK:    [[TMP1:%.*]] = getelementptr i32, ptr [[Z]], i32 1
+// CHECK:    [[TMP2:%.*]] = ptrtoaddr ptr [[TMP1]] to i64
+// CHECK:    [[TMP3:%.*]] = ptrtoaddr ptr [[X]] to i64
+// CHECK:    [[TMP4:%.*]] = sub i64 [[TMP2]], [[TMP3]]
+// CHECK:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[DOTOFFLOAD_SIZES:%.*]], ptr align 8 @.offload_sizes.5, i64 48, i1 false)
+// CHECK:    [[TMP5:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_BASEPTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP5]], align 8
+// CHECK:    [[TMP6:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_PTRS:%.*]], i32 0, i32 0
+// CHECK:    store ptr [[TMP0]], ptr [[TMP6]], align 8
+// CHECK:    [[TMP7:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_MAPPERS:%.*]], i64 0, i64 0
+// CHECK:    store ptr null, ptr [[TMP7]], align 8
+// CHECK:    [[TMP8:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 1
+// CHECK:    store ptr [[T]], ptr [[TMP8]], align 8
+// CHECK:    [[TMP9:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 1
+// CHECK:    store ptr [[X]], ptr [[TMP9]], align 8
+// CHECK:    [[TMP10:%.*]] = getelementptr inbounds [6 x i64], ptr [[DOTOFFLOAD_SIZES]], i32 0, i32 1
+// CHECK:    store i64 [[TMP4]], ptr [[TMP10]], align 8
+// CHECK:    [[TMP11:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 1
+// CHECK:    store ptr null, ptr [[TMP11]], align 8
+// CHECK:    [[TMP12:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 2
+// CHECK:    store ptr [[T]], ptr [[TMP12]], align 8
+// CHECK:    [[TMP13:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 2
+// CHECK:    store ptr [[X]], ptr [[TMP13]], align 8
+// CHECK:    [[TMP14:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 2
+// CHECK:    store ptr null, ptr [[TMP14]], align 8
+// CHECK:    [[TMP15:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 3
+// CHECK:    store ptr [[T]], ptr [[TMP15]], align 8
+// CHECK:    [[TMP16:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 3
+// CHECK:    store ptr [[Y]], ptr [[TMP16]], align 8
+// CHECK:    [[TMP17:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 3
+// CHECK:    store ptr null, ptr [[TMP17]], align 8
+// CHECK:    [[TMP18:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 4
+// CHECK:    store ptr [[T]], ptr [[TMP18]], align 8
+// CHECK:    [[TMP19:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 4
+// CHECK:    store ptr [[Z]], ptr [[TMP19]], align 8
+// CHECK:    [[TMP20:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 4
+// CHECK:    store ptr null, ptr [[TMP20]], align 8
+// CHECK:    [[TMP21:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 5
+// CHECK:    store ptr null, ptr [[TMP21]], align 8
+// CHECK:    [[TMP22:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 5
+// CHECK:    store ptr null, ptr [[TMP22]], align 8
+// CHECK:    [[TMP23:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_MAPPERS]], i64 0, i64 5
+// CHECK:    store ptr null, ptr [[TMP23]], align 8
+// CHECK:    [[TMP24:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_BASEPTRS]], i32 0, i32 0
+// CHECK:    [[TMP25:%.*]] = getelementptr inbounds [6 x ptr], ptr [[DOTOFFLOAD_PTRS]], i32 0, i32 0
+// CHECK:    [[TMP26:%.*]] = getelementptr inbounds [6 x i64], ptr [[DOTOFFLOAD_SIZES]], i32 0, i32 0
+// CHECK:    [[TMP27:%.*]] = getelementptr inbounds nuw [[STRUCT___TGT_KERNEL_ARGUMENTS:%.*]], ptr [[KERNEL_ARGS:%.*]], i32 0, i32 0
+//
+//
+// CHECK-LABEL: define internal void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z30test_mixed_map_types_three_wayv_l53(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(12) [[TMP0:%.*]], ptr noalias noundef [[DYN_PTR:%.*]]) #[[ATTR2]] {
+// CHECK:  [[ENTRY:.*:]]
+// CHECK:    store ptr [[TMP0]], ptr [[DOTADDR:%.*]], align 8
+// CHECK:    store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR:%.*]], align 8
+// CHECK:    [[TMP1:%.*]] = load ptr, ptr [[DOTADDR]], align 8, !nonnull [[META10]], !align [[META11]]
+// CHECK:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE:%.*]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// CHECK:    [[ADD:%.*]] = add nsw i32 [[TMP2]], 1
+// CHECK:    [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE]], ptr [[TMP1]], i32 0, i32 0
+// CHECK:    store i32 [[ADD]], ptr [[X1]], align 4
+// CHECK:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    [[TMP3:%.*]] = load i32, ptr [[Y]], align 4
+// CHECK:    [[ADD2:%.*]] = add nsw i32 [[TMP3]], 2
+// CHECK:    [[Y3:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE]], ptr [[TMP1]], i32 0, i32 1
+// CHECK:    store i32 [[ADD2]], ptr [[Y3]], align 4
+// CHECK:    [[Z:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE]], ptr [[TMP1]], i32 0, i32 2
+// CHECK:    [[TMP4:%.*]] = load i32, ptr [[Z]], align 4
+// CHECK:    [[ADD4:%.*]] = add nsw i32 [[TMP4]], 3
+// CHECK:    [[Z5:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE]], ptr [[TMP1]], i32 0, i32 2
+// CHECK:    store i32 [[ADD4]], ptr [[Z5]], align 4
+// CHECK:    ret void
+//

diff  --git a/clang/test/OpenMP/structured-bindings-template-instantiation.cpp b/clang/test/OpenMP/structured-bindings-template-instantiation.cpp
new file mode 100644
index 0000000000000..99425b56d381a
--- /dev/null
+++ b/clang/test/OpenMP/structured-bindings-template-instantiation.cpp
@@ -0,0 +1,511 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -x c++ -std=c++20 \
+// RUN: -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s
+
+// RUN: %clang_cc1 -verify -std=c++20 -fopenmp -triple x86_64-pc-linux-gnu \
+// RUN: -ast-print %s | FileCheck %s --check-prefix=AST
+
+// expected-no-diagnostics
+
+// Test template instantiation with structured bindings in OpenMP regions.
+// This verifies that skipping duplicate captures (when both bindings from
+// the same decomposition are used) doesn't break template instantiation.
+
+void use(int);
+
+struct Point {
+  int x, y;
+};
+
+struct Point3D {
+  int x, y, z;
+};
+
+// CHECK-LABEL: define {{.*}} @_Z28test_template_single_bindingI5PointEvT_(
+// CHECK: call void {{.*}}@__kmpc_fork_call(ptr @{{[0-9]+}}, i32 1, ptr @{{.*}}.omp_outlined, ptr
+//
+// AST: void use(int);
+// AST: struct Point {
+// AST:     int x;
+// AST:     int y;
+// AST: };
+// AST: struct Point3D {
+// AST:     int x;
+// AST:     int y;
+// AST:     int z;
+// AST: };
+// AST: template <typename T> void test_template_single_binding(T p) {
+// AST:     auto [a, b] = p;
+// AST:     #pragma omp parallel
+// AST:         {
+// AST:             use(a);
+// AST:         }
+// AST: }
+// AST: template<> void test_template_single_binding<Point>(Point p) {
+// AST:     auto [a, b] = p;
+// AST:     #pragma omp parallel
+// AST:         {
+// AST:             use(a);
+// AST:         }
+// AST: }
+template<typename T>
+void test_template_single_binding(T p) {
+  auto [a, b] = p;
+#pragma omp parallel
+  {
+    use(a);
+  }
+}
+
+// CHECK-LABEL: define {{.*}}@_Z26test_template_two_bindingsI5PointEvT_
+// CHECK: call void {{.*}}@__kmpc_fork_call(ptr {{.*}}, i32 2, ptr {{.*}}, ptr
+//
+// AST: template <typename T> void test_template_two_bindings(T p) {
+// AST:     auto [a, b] = p;
+// AST:     int result = 0;
+// AST:     #pragma omp parallel reduction(+: result)
+// AST:         {
+// AST:             result = a + b;
+// AST:         }
+// AST: }
+// AST: template<> void test_template_two_bindings<Point>(Point p) {
+// AST:     auto [a, b] = p;
+// AST:     int result = 0;
+// AST:     #pragma omp parallel reduction(+: result)
+// AST:         {
+// AST:             result = a + b;
+// AST:         }
+// AST: }
+template<typename T>
+void test_template_two_bindings(T p) {
+  auto [a, b] = p;
+  int result = 0;
+#pragma omp parallel reduction(+:result)
+  {
+    result = a + b;
+  }
+}
+
+// CHECK-LABEL: define {{.*}}@_Z28test_template_three_bindingsI7Point3DEiT_
+// CHECK: call void {{.*}}@__kmpc_fork_call(ptr {{.*}}, i32 2, ptr {{.*}}, ptr
+//
+// AST: template <typename T> int test_template_three_bindings(T p) {
+// AST:     auto [x, y, z] = p;
+// AST:     int result = 0;
+// AST:     #pragma omp parallel reduction(+: result)
+// AST:         {
+// AST:             result = x + y + z;
+// AST:         }
+// AST:     return result;
+// AST: }
+// AST: template<> int test_template_three_bindings<Point3D>(Point3D p) {
+// AST:     auto [x, y, z] = p;
+// AST:     int result = 0;
+// AST:     #pragma omp parallel reduction(+: result)
+// AST:         {
+// AST:             result = x + y + z;
+// AST:         }
+// AST:     return result;
+// AST: }
+template<typename T>
+int test_template_three_bindings(T p) {
+  auto [x, y, z] = p;
+
+  int result = 0;
+#pragma omp parallel reduction(+:result)
+  {
+    result = x + y + z;
+  }
+  return result;
+}
+
+// CHECK-LABEL: define {{.*}}@_Z28test_template_reuse_bindingsI5PointEiT_
+// CHECK: call void {{.*}}@__kmpc_fork_call(ptr {{.*}}, i32 2, ptr {{.*}}, ptr
+//
+// AST: template <typename T> int test_template_reuse_bindings(T p) {
+// AST:     auto [a, b] = p;
+// AST:     int result = 0;
+// AST:     #pragma omp parallel reduction(+: result)
+// AST:         {
+// AST:             result = a + b + a * 2 + b * 3;
+// AST:         }
+// AST:     return result;
+// AST: }
+// AST: template<> int test_template_reuse_bindings<Point>(Point p) {
+// AST:     auto [a, b] = p;
+// AST:     int result = 0;
+// AST:     #pragma omp parallel reduction(+: result)
+// AST:         {
+// AST:             result = a + b + a * 2 + b * 3;
+// AST:         }
+// AST:     return result;
+// AST: }
+template<typename T>
+int test_template_reuse_bindings(T p) {
+  auto [a, b] = p;
+  int result = 0;
+#pragma omp parallel reduction(+:result)
+  {
+    result = a + b + a * 2 + b * 3;
+  }
+  return result;
+}
+
+// CHECK-LABEL: define {{.*}}@_Z20test_template_nestedI5PointEiT_
+// CHECK: call void {{.*}}@__kmpc_fork_call(ptr {{.*}}, i32 2, ptr {{.*}}, ptr
+//
+// AST: template <typename T> int test_template_nested(T p) {
+// AST:     auto [a, b] = p;
+// AST:     int result = 0;
+// AST:     #pragma omp parallel
+// AST:         {
+// AST:             #pragma omp critical
+// AST:                 {
+// AST:                     result += a + b;
+// AST:                 }
+// AST:         }
+// AST:     return result;
+// AST: }
+// AST: template<> int test_template_nested<Point>(Point p) {
+// AST:     auto [a, b] = p;
+// AST:     int result = 0;
+// AST:     #pragma omp parallel
+// AST:         {
+// AST:             #pragma omp critical
+// AST:                 {
+// AST:                     result += a + b;
+// AST:                 }
+// AST:         }
+// AST:     return result;
+// AST: }
+template<typename T>
+int test_template_nested(T p) {
+  auto [a, b] = p;
+  int result = 0;
+#pragma omp parallel
+  {
+#pragma omp critical
+    {
+      result += a + b;
+    }
+  }
+  return result;
+}
+
+// CHECK-LABEL: define {{.*}}@_Z30test_template_multiple_regionsI5PointEvT_
+// CHECK: call void {{.*}}@__kmpc_fork_call(ptr {{.*}}, i32 2, ptr {{.*}}, ptr
+//
+// AST: template <typename T> void test_template_multiple_regions(T p) {
+// AST:     auto [a, b] = p;
+// AST:     int result1 = 0, result2 = 0;
+// AST:     #pragma omp parallel reduction(+: result1)
+// AST:         {
+// AST:             result1 = a;
+// AST:         }
+// AST: }
+// AST: template<> void test_template_multiple_regions<Point>(Point p) {
+// AST:     auto [a, b] = p;
+// AST:     int result1 = 0, result2 = 0;
+// AST:     #pragma omp parallel reduction(+: result1)
+// AST:         {
+// AST:             result1 = a;
+// AST:         }
+// AST: }
+template<typename T>
+void test_template_multiple_regions(T p) {
+  auto [a, b] = p;
+  int result1 = 0, result2 = 0;
+#pragma omp parallel reduction(+:result1)
+  {
+    result1 = a;
+  }
+}
+
+typedef unsigned int size_t;
+// CHECK-LABEL: define {{.*}}@_Z19test_template_arrayIiLj2EEiRAT0__T_
+// CHECK: call void {{.*}}@__kmpc_fork_call(ptr {{.*}}, i32 2, ptr {{.*}}, ptr
+//
+// AST: typedef unsigned int size_t;
+// AST: template <typename T, size_t N> int test_template_array(T (&arr)[N]) {
+// AST:     auto [a, b] = arr;
+// AST:     int result = 0;
+// AST:     #pragma omp parallel reduction(+: result)
+// AST:         {
+// AST:             result = a + b;
+// AST:         }
+// AST:     return result;
+// AST: }
+// AST: template<> int test_template_array<int, 2U>(int (&arr)[2]) {
+// AST:     auto [a, b] = {arr[*]};
+// AST:     int result = 0;
+// AST:     #pragma omp parallel reduction(+: result)
+// AST:         {
+// AST:             result = a + b;
+// AST:         }
+// AST:     return result;
+// AST: }
+template<typename T, size_t N>
+int test_template_array(T (&arr)[N]) {
+  auto [a, b] = arr;
+  int result = 0;
+#pragma omp parallel reduction(+:result)
+  {
+    result = a + b;
+  }
+  return result;
+}
+
+template<typename T>
+struct Pair {
+  T first;
+  T second;
+};
+
+// CHECK-LABEL: define {{.*}}@_Z28test_dependent_decompositionI{{.*}}
+// AST: template <typename T> void test_dependent_decomposition(Pair<T> p) {
+// AST:    auto [a, b] = p;
+// AST:    #pragma omp parallel shared(a,b)
+// AST:       {
+// AST:            a = a + T(1);
+// AST:            b = b + T(2);
+// AST:        }
+// AST: }
+// AST: template<> void test_dependent_decomposition<int>(Pair<int> p) {
+// AST:     auto [a, b] = p;
+// AST:     #pragma omp parallel shared(a,b)
+// AST:         {
+// AST:             a = a + int(1);
+// AST:             b = b + int(2);
+// AST:         }
+// AST: }
+// AST: template<> void test_dependent_decomposition<double>(Pair<double> p) {
+// AST:         auto [a, b] = p;
+// AST:     #pragma omp parallel shared(a,b)
+// AST:         {
+// AST:             a = a + double(1);
+// AST:             b = b + double(2);
+// AST:         }
+// AST: }
+
+template<typename T>
+void test_dependent_decomposition(Pair<T> p) {
+  auto [a, b] = p;
+
+#pragma omp parallel shared(a, b)
+  {
+    a = a + T(1);
+    b = b + T(2);
+  }
+}
+
+// CHECK-LABEL: define {{.*}}@_Z24test_dependent_reductionI{{.*}}
+// AST: template <typename T> T test_dependent_reduction(Pair<T> p) {
+// AST:     auto [a, b] = p;
+// AST:     T sum = T(0);
+// AST:     #pragma omp parallel for reduction(+: sum)
+// AST:         for (int i = 0; i < 10; ++i) {
+// AST:             sum = sum + a + b;
+// AST:         }
+// AST:     return sum;
+// AST: }
+// AST: template<> int test_dependent_reduction<int>(Pair<int> p) {
+// AST:     auto [a, b] = p;
+// AST:     int sum = int(0);
+// AST:     #pragma omp parallel for reduction(+: sum)
+// AST:         for (int i = 0; i < 10; ++i) {
+// AST:             sum = sum + a + b;
+// AST:         }
+// AST:     return sum;
+// AST: }
+// AST: template<> double test_dependent_reduction<double>(Pair<double> p) {
+// AST:     auto [a, b] = p;
+// AST:     double sum = double(0);
+// AST:     #pragma omp parallel for reduction(+: sum)
+// AST:         for (int i = 0; i < 10; ++i) {
+// AST:             sum = sum + a + b;
+// AST:         }
+// AST:     return sum;
+// AST: }
+template<typename T>
+T test_dependent_reduction(Pair<T> p) {
+  auto [a, b] = p;
+  T sum = T(0);
+
+#pragma omp parallel for reduction(+:sum)
+  for (int i = 0; i < 10; ++i) {
+    sum = sum + a + b;
+  }
+  return sum;
+}
+
+// CHECK-LABEL: define {{.*}}@_Z27test_dependent_firstprivateI{{.*}}
+// AST: template <typename T> void test_dependent_firstprivate(Pair<T> p) {
+// AST:     auto [a, b] = p;
+// AST:     #pragma omp parallel firstprivate(a,b)
+// AST:         {
+// AST:             T local = a + b;
+// AST:         }
+// AST: }
+// AST: template<> void test_dependent_firstprivate<int>(Pair<int> p) {
+// AST:     auto [a, b] = p;
+// AST:     #pragma omp parallel firstprivate(a,b)
+// AST:         {
+// AST:             int local = a + b;
+// AST:         }
+// AST: }
+// AST: template<> void test_dependent_firstprivate<double>(Pair<double> p) {
+// AST:     auto [a, b] = p;
+// AST:     #pragma omp parallel firstprivate(a,b)
+// AST:         {
+// AST:             double local = a + b;
+// AST:         }
+// AST: }
+template<typename T>
+void test_dependent_firstprivate(Pair<T> p) {
+  auto [a, b] = p;
+
+#pragma omp parallel firstprivate(a, b)
+  {
+    T local = a + b;
+  }
+}
+
+// CHECK-LABEL: define {{.*}}@_Z19test_dependent_taskI{{.*}}
+// AST: template <typename T> void test_dependent_task(Pair<T> p) {
+// AST:     auto [a, b] = p;
+// AST:     #pragma omp task shared(a)
+// AST:         {
+// AST:             a = a + T(10);
+// AST:         }
+// AST: }
+// AST: template<> void test_dependent_task<int>(Pair<int> p) {
+// AST:     auto [a, b] = p;
+// AST:     #pragma omp task shared(a)
+// AST:         {
+// AST:             a = a + int(10);
+// AST:         }
+// AST: }
+// AST: template<> void test_dependent_task<double>(Pair<double> p) {
+// AST:     auto [a, b] = p;
+// AST:     #pragma omp task shared(a)
+// AST:         {
+// AST:             a = a + double(10);
+// AST:         }
+// AST: }
+template<typename T>
+void test_dependent_task(Pair<T> p) {
+  auto [a, b] = p;
+
+#pragma omp task shared(a)
+  {
+    a = a + T(10);
+  }
+}
+
+template<typename T>
+struct Triple {
+  T x, y, z;
+};
+
+// CHECK-LABEL: define {{.*}}@_Z30test_dependent_partial_captureIiEv6TripleIT_E
+// AST: template <typename T> void test_dependent_partial_capture(Triple<T> p) {
+// AST:     auto [a, b, c] = p;
+// AST:     #pragma omp parallel firstprivate(a)
+// AST:         {
+// AST:             T result = a + b + c;
+// AST:         }
+// AST: }
+// AST: template<> void test_dependent_partial_capture<int>(Triple<int> p) {
+// AST:     auto [a, b, c] = p;
+// AST:     #pragma omp parallel firstprivate(a)
+// AST:         {
+// AST:             int result = a + b + c;
+// AST:         }
+// AST: }
+// AST: template<> void test_dependent_partial_capture<double>(Triple<double> p) {
+// AST:     auto [a, b, c] = p;
+// AST:     #pragma omp parallel firstprivate(a)
+// AST:         {
+// AST:             double result = a + b + c;
+// AST:         }
+// AST: }
+template<typename T>
+void test_dependent_partial_capture(Triple<T> p) {
+  auto [a, b, c] = p;
+#pragma omp parallel firstprivate(a)
+  {
+    T result = a + b + c;
+  }
+}
+
+// CHECK-LABEL: define {{.*}}@_Z32test_dependent_multiple_capturesI{{.*}}
+// AST: template <typename T> void test_dependent_multiple_captures(Pair<T> p1, Pair<T> p2) {
+// AST:     auto [a, b] = p1;
+// AST:     auto [c, d] = p2;
+// AST:     #pragma omp parallel shared(a,b,c,d)
+// AST:         {
+// AST:             T result = a + b + c + d;
+// AST:         }
+// AST: }
+// AST: template<> void test_dependent_multiple_captures<int>(Pair<int> p1, Pair<int> p2) {
+// AST:     auto [a, b] = p1;
+// AST:     auto [c, d] = p2;
+// AST:     #pragma omp parallel shared(a,b,c,d)
+// AST:         {
+// AST:             int result = a + b + c + d;
+// AST:         }
+// AST: }
+// AST: template<> void test_dependent_multiple_captures<double>(Pair<double> p1, Pair<double> p2) {
+// AST:     auto [a, b] = p1;
+// AST:     auto [c, d] = p2;
+// AST:     #pragma omp parallel shared(a,b,c,d)
+// AST:         {
+// AST:             double result = a + b + c + d;
+// AST:         }
+// AST: }
+template<typename T>
+void test_dependent_multiple_captures(Pair<T> p1, Pair<T> p2) {
+  auto [a, b] = p1;
+  auto [c, d] = p2;
+
+#pragma omp parallel shared(a, b, c, d)
+  {
+    T result = a + b + c + d;
+  }
+}
+
+void instantiate_tests() {
+  Point p1{1, 2};
+  Point3D p2{1, 2, 3};
+  int arr[2] = {1, 2};
+  test_template_single_binding(p1);
+  test_template_two_bindings(p1);
+  test_template_three_bindings(p2);
+  test_template_reuse_bindings(p1);
+  test_template_nested(p1);
+  test_template_multiple_regions(p1);
+  test_template_array(arr);
+
+  Pair<int> pi{1, 2};
+  test_dependent_decomposition(pi);
+  test_dependent_reduction(pi);
+  test_dependent_firstprivate(pi);
+  test_dependent_task(pi);
+
+  Pair<int> pi2{3, 4};
+  test_dependent_multiple_captures(pi, pi2);
+
+  Triple<int> ti{1, 2, 3};
+  test_dependent_partial_capture(ti);
+
+  Pair<double> pd{1.5, 2.5};
+  test_dependent_decomposition(pd);
+  test_dependent_reduction(pd);
+  test_dependent_firstprivate(pd);
+  test_dependent_task(pd);
+
+  Pair<double> pd2{3.5, 4.5};
+  test_dependent_multiple_captures(pd, pd2);
+
+  Triple<double> td{1.5, 2.5, 3.5};
+  test_dependent_partial_capture(td);
+}

diff  --git a/clang/test/SemaCXX/decomposition-openmp.cpp b/clang/test/SemaCXX/decomposition-openmp.cpp
index 2185f3db83d4e..78135d90a561b 100644
--- a/clang/test/SemaCXX/decomposition-openmp.cpp
+++ b/clang/test/SemaCXX/decomposition-openmp.cpp
@@ -1,10 +1,17 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=cxx17 -std=c++17 -fopenmp %s
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 -fopenmp %s
 
+// expected-no-diagnostics
+
 // Okay, not an OpenMP capture.
 auto f() {
   int i[2] = {};
+  // cxx17-note at +2{{'a' declared here}}
+  // cxx17-note at +1{{'b' declared here}}
   auto [a, b] = i;
+  // cxx17-warning at +1{{captured structured bindings are a C++20 extension}}
   return [=, &a] {
+    // cxx17-warning at +1{{captured structured bindings are a C++20 extension}}
     return a + b;
   };
 }
@@ -15,18 +22,24 @@ void g() {
   #pragma omp parallel
   {
     int i[2] = {};
+    // cxx17-note at +2{{'a' declared here}}
+    // cxx17-note at +1{{'b' declared here}}
     auto [a, b] = i;
+    // cxx17-warning at +2{{captured structured bindings are a C++20 extension}}
+    // cxx17-warning at +1{{captured structured bindings are a C++20 extension}}
     auto L = [&] { foo(a+b); };
   }
 }
 
-// FIXME: OpenMP should support capturing structured bindings
 void h() {
   int i[2] = {};
-  auto [a, b] = i; // expected-note 2{{declared here}}
+  // cxx17-note at +2{{'a' declared here}}
+  // cxx17-note at +1{{'b' declared here}}
+  auto [a, b] = i;
   #pragma omp parallel
   {
-    // expected-error at +1 2{{capturing a structured binding is not yet supported in OpenMP}}
+    // cxx17-warning at +2{{captured structured bindings are a C++20 extension}}
+    // cxx17-warning at +1{{captured structured bindings are a C++20 extension}}
     foo(a + b);
   }
 }

diff  --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 8ab5948f65ffd..59aac4c507772 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -5305,6 +5305,9 @@ void EmitClangAttrTextNodeDump(const RecordKeeper &Records, raw_ostream &OS) {
     if (!R.getValueAsBit("ASTNode"))
       continue;
 
+    if (R.getValueAsBit("HasCustomTextNodeDump"))
+      continue;
+
     // If the attribute has a semantically-meaningful name (which is determined
     // by whether there is a Spelling enumeration for it), then write out the
     // spelling used for the attribute.


        


More information about the cfe-commits mailing list