[clang] [OpenMP] Fix OpenMP reduction segfault with non-copyable types and user initializers. (PR #219265)

Zahira Ammarguellat via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 11 07:20:44 PDT 2026


https://github.com/zahiraam updated https://github.com/llvm/llvm-project/pull/219265

>From 2823d09da29cdb5bc215138b284badfa87516765 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Thu, 27 Aug 2026 10:19:04 -0700
Subject: [PATCH 1/5] [OpenMP] Fix OpenMP reduction segfault with non-copyable
 types and user initializers

---
 clang/lib/CodeGen/CGOpenMPRuntime.cpp         |  48 ++++-
 .../test/OpenMP/for_reduction_codegen_UDR.cpp | 184 +++++++++---------
 ...eduction_non_copyable_with_initializer.cpp |  65 +++++++
 3 files changed, 202 insertions(+), 95 deletions(-)
 create mode 100644 clang/test/OpenMP/reduction_non_copyable_with_initializer.cpp

diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
index 9f8822a37bf71..dba5ecbf2318c 100644
--- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp
+++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -1125,9 +1125,9 @@ emitCombinerOrInitializer(CodeGenModule &CGM, QualType Ty,
       In, CGF.EmitLoadOfPointerLValue(AddrIn, PtrTy->castAs<PointerType>())
               .getAddress());
   Address AddrOut = CGF.GetAddrOfLocalVar(OmpOutParm);
-  Scope.addPrivate(
-      Out, CGF.EmitLoadOfPointerLValue(AddrOut, PtrTy->castAs<PointerType>())
-               .getAddress());
+  Address OutDerefAddr = CGF.EmitLoadOfPointerLValue(
+      AddrOut, PtrTy->castAs<PointerType>()).getAddress();
+  Scope.addPrivate(Out, OutDerefAddr);
   (void)Scope.Privatize();
   if (!IsCombiner && Out->hasInit() &&
       !CGF.isTrivialInitializer(Out->getInit())) {
@@ -1135,8 +1135,48 @@ emitCombinerOrInitializer(CodeGenModule &CGM, QualType Ty,
                          Out->getType().getQualifiers(),
                          /*IsInitializer=*/true);
   }
-  if (CombinerInitializer)
+  if (CombinerInitializer) {
+    // For non-trivial types with user initializers, we must emit default
+    // construction first to properly initialize members (e.g., std::string)
+    // before the user initializer assigns to them. This matches GCC behavior.
+    if (!IsCombiner && Ty.isDestructedType() != QualType::DK_none) {
+      if (const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl()) {
+        for (const CXXConstructorDecl *Ctor : RD->ctors()) {
+          if (Ctor->isDefaultConstructor()) {
+            // Synthesize CXXConstructExpr for default construction.
+            // Collect default arguments for parameters with defaults.
+            ASTContext &Ctx = CGM.getContext();
+            SmallVector<Expr*, 4> Args;
+            for (unsigned I = 0; I < Ctor->getNumParams(); ++I) {
+              const ParmVarDecl *Param = Ctor->getParamDecl(I);
+              if (Param->hasDefaultArg()) {
+                Args.push_back(const_cast<Expr*>(Param->getDefaultArg()));
+              }
+            }
+
+            CXXConstructExpr *CtorExpr = CXXConstructExpr::Create(
+                Ctx, Ty, Out->getLocation(),
+                const_cast<CXXConstructorDecl *>(Ctor), /*Elidable=*/false,
+                /*Args=*/Args,
+                /*HadMultipleCandidates=*/false, /*ListInit=*/false,
+                /*StdInitListInit=*/false, /*ZeroInit=*/false,
+                CXXConstructionKind::Complete, SourceRange());
+
+            // Emit the constructor call
+            AggValueSlot Slot = AggValueSlot::forAddr(
+                OutDerefAddr, Ty.getQualifiers(),
+                AggValueSlot::IsDestructed,
+                AggValueSlot::DoesNotNeedGCBarriers,
+                AggValueSlot::IsNotAliased,
+                AggValueSlot::DoesNotOverlap);
+            CGF.EmitCXXConstructExpr(CtorExpr, Slot);
+            break;
+          }
+        }
+      }
+    }
     CGF.EmitIgnoredExpr(CombinerInitializer);
+  }
   Scope.ForceCleanup();
   CGF.FinishFunction();
   return Fn;
diff --git a/clang/test/OpenMP/for_reduction_codegen_UDR.cpp b/clang/test/OpenMP/for_reduction_codegen_UDR.cpp
index 0ac82685803db..a33d93c7ebdc4 100644
--- a/clang/test/OpenMP/for_reduction_codegen_UDR.cpp
+++ b/clang/test/OpenMP/for_reduction_codegen_UDR.cpp
@@ -508,29 +508,29 @@ int main() {
 // CHECK1-NEXT:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[S]], ptr [[S_ADDR]], align 8
-// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[S_ADDR]], align 8, !nonnull [[META3:![0-9]+]], !align [[META4:![0-9]+]]
+// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[S_ADDR]], align 8, !nonnull [[META2:![0-9]+]], !align [[META3:![0-9]+]]
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_IV]], align 4
 // CHECK1-NEXT:    call void @.omp_initializer.(ptr noundef [[S1]], ptr noundef [[TMP0]])
 // CHECK1-NEXT:    br label [[OMP_INNER_FOR_COND:%.*]]
 // CHECK1:       omp.inner.for.cond:
-// CHECK1-NEXT:    [[TMP1:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP5:![0-9]+]]
+// CHECK1-NEXT:    [[TMP1:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP4:![0-9]+]]
 // CHECK1-NEXT:    [[CMP:%.*]] = icmp slt i32 [[TMP1]], 10
 // CHECK1-NEXT:    br i1 [[CMP]], label [[OMP_INNER_FOR_BODY:%.*]], label [[OMP_INNER_FOR_COND_CLEANUP:%.*]]
 // CHECK1:       omp.inner.for.cond.cleanup:
 // CHECK1-NEXT:    br label [[OMP_INNER_FOR_END:%.*]]
 // CHECK1:       omp.inner.for.body:
-// CHECK1-NEXT:    [[TMP2:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP5]]
+// CHECK1-NEXT:    [[TMP2:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP4]]
 // CHECK1-NEXT:    [[MUL:%.*]] = mul nsw i32 [[TMP2]], 1
 // CHECK1-NEXT:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
-// CHECK1-NEXT:    store i32 [[ADD]], ptr [[I]], align 4, !llvm.access.group [[ACC_GRP5]]
+// CHECK1-NEXT:    store i32 [[ADD]], ptr [[I]], align 4, !llvm.access.group [[ACC_GRP4]]
 // CHECK1-NEXT:    br label [[OMP_BODY_CONTINUE:%.*]]
 // CHECK1:       omp.body.continue:
 // CHECK1-NEXT:    br label [[OMP_INNER_FOR_INC:%.*]]
 // CHECK1:       omp.inner.for.inc:
-// CHECK1-NEXT:    [[TMP3:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP5]]
+// CHECK1-NEXT:    [[TMP3:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP4]]
 // CHECK1-NEXT:    [[ADD2:%.*]] = add nsw i32 [[TMP3]], 1
-// CHECK1-NEXT:    store i32 [[ADD2]], ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP5]]
-// CHECK1-NEXT:    br label [[OMP_INNER_FOR_COND]], !llvm.loop [[LOOP6:![0-9]+]]
+// CHECK1-NEXT:    store i32 [[ADD2]], ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP4]]
+// CHECK1-NEXT:    br label [[OMP_INNER_FOR_COND]], !llvm.loop [[LOOP5:![0-9]+]]
 // CHECK1:       omp.inner.for.end:
 // CHECK1-NEXT:    store i32 10, ptr [[I]], align 4
 // CHECK1-NEXT:    call void @.omp_combiner.(ptr noundef [[TMP0]], ptr noundef [[S1]])
@@ -654,7 +654,7 @@ int main() {
 // CHECK1-NEXT:    br i1 [[ARRAYCTOR_DONE8]], label [[ARRAYCTOR_CONT9:%.*]], label [[ARRAYCTOR_LOOP5]]
 // CHECK1:       arrayctor.cont9:
 // CHECK1-NEXT:    store ptr [[S_ARR]], ptr [[VAR3]], align 8
-// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 6, ptr @main.omp_outlined, ptr [[T_VAR]], ptr [[TMP0]], ptr [[VAR1]], ptr [[T_VAR1]], ptr [[VEC]], ptr [[S_ARR]])
 // CHECK1-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[VEC]], i64 0, i64 1
 // CHECK1-NEXT:    [[TMP1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
@@ -668,9 +668,9 @@ int main() {
 // CHECK1-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 4, ptr @main.omp_outlined.11, i64 10, i64 [[TMP2]], ptr [[VLA]], ptr [[ARRS]])
 // CHECK1-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @main.omp_outlined.12, ptr [[VAR2]])
 // CHECK1-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @main.omp_outlined.13, ptr [[VVAR2]])
-// CHECK1-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[VAR3]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[VAR3]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @main.omp_outlined.14, ptr [[TMP5]])
-// CHECK1-NEXT:    [[TMP6:%.*]] = load ptr, ptr [[VAR3]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP6:%.*]] = load ptr, ptr [[VAR3]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 1, ptr @main.omp_outlined.15, ptr [[TMP6]])
 // CHECK1-NEXT:    [[CALL10:%.*]] = call noundef i32 @_Z5tmainIiLi42EET_v()
 // CHECK1-NEXT:    store i32 [[CALL10]], ptr [[RETVAL]], align 4
@@ -769,21 +769,21 @@ int main() {
 // CHECK1-NEXT:    store ptr [[T_VAR1]], ptr [[T_VAR1_ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[VEC]], ptr [[VEC_ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[S_ARR]], ptr [[S_ARR_ADDR]], align 8
-// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[T_VAR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[VAR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[VAR1_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[T_VAR1_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[VEC_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[S_ARR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[T_VAR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[VAR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[VAR1_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[T_VAR1_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[VEC_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[S_ARR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store ptr [[TMP1]], ptr [[TMP]], align 8
-// CHECK1-NEXT:    [[TMP6:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP6:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store ptr [[TMP6]], ptr [[_TMP1]], align 8
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_LB]], align 4
 // CHECK1-NEXT:    store i32 1, ptr [[DOTOMP_UB]], align 4
 // CHECK1-NEXT:    store i32 1, ptr [[DOTOMP_STRIDE]], align 4
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_IS_LAST]], align 4
 // CHECK1-NEXT:    call void @.omp_initializer..2(ptr noundef [[T_VAR3]], ptr noundef [[TMP0]])
-// CHECK1-NEXT:    [[TMP7:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP7:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[VAR4]], i64 4
 // CHECK1-NEXT:    [[ADD_PTR5:%.*]] = getelementptr inbounds i8, ptr [[TMP7]], i64 4
 // CHECK1-NEXT:    call void @.omp_initializer..4(ptr noundef [[ADD_PTR]], ptr noundef [[ADD_PTR5]])
@@ -826,7 +826,7 @@ int main() {
 // CHECK1-NEXT:    [[IDXPROM:%.*]] = sext i32 [[TMP18]] to i64
 // CHECK1-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP4]], i64 0, i64 [[IDXPROM]]
 // CHECK1-NEXT:    store i32 [[CONV]], ptr [[ARRAYIDX]], align 4
-// CHECK1-NEXT:    [[TMP19:%.*]] = load ptr, ptr [[_TMP6]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP19:%.*]] = load ptr, ptr [[_TMP6]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    [[TMP20:%.*]] = load i32, ptr [[I]], align 4
 // CHECK1-NEXT:    [[IDXPROM10:%.*]] = sext i32 [[TMP20]] to i64
 // CHECK1-NEXT:    [[ARRAYIDX11:%.*]] = getelementptr inbounds [4 x [[STRUCT_S_0]]], ptr [[TMP5]], i64 0, i64 [[IDXPROM10]]
@@ -970,6 +970,7 @@ int main() {
 // CHECK1-NEXT:    store ptr [[TMP1]], ptr [[DOTADDR1]], align 8
 // CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
 // CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK1-NEXT:    call void @_ZN1SIfEC1Ev(ptr noundef nonnull align 4 dereferenceable(12) [[TMP3]])
 // CHECK1-NEXT:    [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 4
 // CHECK1-NEXT:    [[ADD_PTR2:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 4
 // CHECK1-NEXT:    call void @_Z5init1R6BaseS1RKS_(ptr noundef nonnull align 4 dereferenceable(4) [[ADD_PTR]], ptr noundef nonnull align 4 dereferenceable(4) [[ADD_PTR2]])
@@ -1065,9 +1066,9 @@ int main() {
 // CHECK1-NEXT:    store ptr [[ARRS]], ptr [[ARRS_ADDR]], align 8
 // CHECK1-NEXT:    [[TMP0:%.*]] = load i64, ptr [[VLA_ADDR]], align 8
 // CHECK1-NEXT:    [[TMP1:%.*]] = load i64, ptr [[VLA_ADDR2]], align 8
-// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[VEC_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[ARRS_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[VEC_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[ARRS_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_LB]], align 4
 // CHECK1-NEXT:    store i32 9, ptr [[DOTOMP_UB]], align 4
 // CHECK1-NEXT:    store i32 1, ptr [[DOTOMP_STRIDE]], align 4
@@ -1403,8 +1404,8 @@ int main() {
 // CHECK1-NEXT:    store ptr [[ARRS]], ptr [[ARRS_ADDR]], align 8
 // CHECK1-NEXT:    [[TMP0:%.*]] = load i64, ptr [[VLA_ADDR]], align 8
 // CHECK1-NEXT:    [[TMP1:%.*]] = load i64, ptr [[VLA_ADDR2]], align 8
-// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[ARRS_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[ARRS_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_LB]], align 4
 // CHECK1-NEXT:    store i32 9, ptr [[DOTOMP_UB]], align 4
 // CHECK1-NEXT:    store i32 1, ptr [[DOTOMP_STRIDE]], align 4
@@ -1663,7 +1664,7 @@ int main() {
 // CHECK1-NEXT:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[VAR2]], ptr [[VAR2_ADDR]], align 8
-// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR2_ADDR]], align 8, !nonnull [[META3]], !align [[META11:![0-9]+]]
+// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR2_ADDR]], align 8, !nonnull [[META2]], !align [[META10:![0-9]+]]
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_LB]], align 4
 // CHECK1-NEXT:    store i32 9, ptr [[DOTOMP_UB]], align 4
 // CHECK1-NEXT:    store i32 1, ptr [[DOTOMP_STRIDE]], align 4
@@ -1872,7 +1873,7 @@ int main() {
 // CHECK1-NEXT:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[VVAR2]], ptr [[VVAR2_ADDR]], align 8
-// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VVAR2_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VVAR2_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_LB]], align 4
 // CHECK1-NEXT:    store i32 9, ptr [[DOTOMP_UB]], align 4
 // CHECK1-NEXT:    store i32 1, ptr [[DOTOMP_STRIDE]], align 4
@@ -2057,17 +2058,17 @@ int main() {
 // CHECK1-NEXT:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[VAR3]], ptr [[VAR3_ADDR]], align 8
-// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR3_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR3_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store ptr [[TMP0]], ptr [[TMP]], align 8
-// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store ptr [[TMP1]], ptr [[_TMP1]], align 8
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_LB]], align 4
 // CHECK1-NEXT:    store i32 9, ptr [[DOTOMP_UB]], align 4
 // CHECK1-NEXT:    store i32 1, ptr [[DOTOMP_STRIDE]], align 4
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_IS_LAST]], align 4
-// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [4 x [[STRUCT_S_0]]], ptr [[TMP2]], i64 0, i64 1
-// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    [[ARRAYIDX3:%.*]] = getelementptr inbounds nuw [4 x [[STRUCT_S_0]]], ptr [[TMP3]], i64 0, i64 2
 // CHECK1-NEXT:    [[ARRAY_BEGIN:%.*]] = getelementptr inbounds [2 x [[STRUCT_S_0]]], ptr [[VAR34]], i32 0, i32 0
 // CHECK1-NEXT:    [[TMP4:%.*]] = getelementptr [[STRUCT_S_0]], ptr [[ARRAY_BEGIN]], i64 2
@@ -2084,7 +2085,7 @@ int main() {
 // CHECK1-NEXT:    [[OMP_ARRAYCPY_DONE:%.*]] = icmp eq ptr [[OMP_ARRAYCPY_DEST_ELEMENT6]], [[TMP4]]
 // CHECK1-NEXT:    br i1 [[OMP_ARRAYCPY_DONE]], label [[OMP_ARRAYINIT_DONE]], label [[OMP_ARRAYINIT_BODY]]
 // CHECK1:       omp.arrayinit.done:
-// CHECK1-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    [[TMP6:%.*]] = ptrtoaddr ptr [[TMP5]] to i64
 // CHECK1-NEXT:    [[TMP7:%.*]] = ptrtoaddr ptr [[ARRAYIDX]] to i64
 // CHECK1-NEXT:    [[TMP8:%.*]] = sub i64 [[TMP6]], [[TMP7]]
@@ -2248,15 +2249,15 @@ int main() {
 // CHECK1-NEXT:    store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[VAR3]], ptr [[VAR3_ADDR]], align 8
-// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR3_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR3_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store ptr [[TMP0]], ptr [[TMP]], align 8
-// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store ptr [[TMP1]], ptr [[_TMP1]], align 8
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_LB]], align 4
 // CHECK1-NEXT:    store i32 9, ptr [[DOTOMP_UB]], align 4
 // CHECK1-NEXT:    store i32 1, ptr [[DOTOMP_STRIDE]], align 4
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_IS_LAST]], align 4
-// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[DOTGLOBAL_TID__ADDR]], align 8
 // CHECK1-NEXT:    [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4
 // CHECK1-NEXT:    [[DOTVAR3__VOID_ADDR:%.*]] = call ptr @__kmpc_alloc(i32 [[TMP4]], i64 48, ptr inttoptr (i64 6 to ptr))
@@ -2435,11 +2436,11 @@ int main() {
 // CHECK1-NEXT:    [[ARRAYCTOR_DONE:%.*]] = icmp eq ptr [[ARRAYCTOR_NEXT]], [[ARRAYCTOR_END]]
 // CHECK1-NEXT:    br i1 [[ARRAYCTOR_DONE]], label [[ARRAYCTOR_CONT:%.*]], label [[ARRAYCTOR_LOOP]]
 // CHECK1:       arrayctor.cont:
-// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 6, ptr @_Z5tmainIiLi42EET_v.omp_outlined, ptr [[T_VAR]], ptr [[TMP0]], ptr [[VAR1]], ptr [[T_VAR1]], ptr [[VEC]], ptr [[S_ARR]])
-// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 4, ptr @_Z5tmainIiLi42EET_v.omp_outlined.22, ptr [[T_VAR]], ptr [[VEC]], ptr [[S_ARR]], ptr [[TMP1]])
-// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 5, ptr @_Z5tmainIiLi42EET_v.omp_outlined.25, ptr [[ARR]], ptr [[VEC]], ptr [[T_VAR]], ptr [[S_ARR]], ptr [[TMP2]])
 // CHECK1-NEXT:    store i32 0, ptr [[RETVAL]], align 4
 // CHECK1-NEXT:    [[ARRAY_BEGIN1:%.*]] = getelementptr inbounds [42 x [[STRUCT_S]]], ptr [[ARR]], i32 0, i32 0
@@ -2557,21 +2558,21 @@ int main() {
 // CHECK1-NEXT:    store ptr [[T_VAR1]], ptr [[T_VAR1_ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[VEC]], ptr [[VEC_ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[S_ARR]], ptr [[S_ARR_ADDR]], align 8
-// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[T_VAR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[VAR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[VAR1_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[T_VAR1_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[VEC_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[S_ARR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[T_VAR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[VAR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[VAR1_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[T_VAR1_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[VEC_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[S_ARR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store ptr [[TMP1]], ptr [[TMP]], align 8
-// CHECK1-NEXT:    [[TMP6:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP6:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store ptr [[TMP6]], ptr [[_TMP1]], align 8
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_LB]], align 4
 // CHECK1-NEXT:    store i32 1, ptr [[DOTOMP_UB]], align 4
 // CHECK1-NEXT:    store i32 1, ptr [[DOTOMP_STRIDE]], align 4
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_IS_LAST]], align 4
 // CHECK1-NEXT:    call void @.omp_initializer..17(ptr noundef [[T_VAR3]], ptr noundef [[TMP0]])
-// CHECK1-NEXT:    [[TMP7:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP7:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[VAR4]], i64 4
 // CHECK1-NEXT:    [[ADD_PTR5:%.*]] = getelementptr inbounds i8, ptr [[TMP7]], i64 4
 // CHECK1-NEXT:    call void @.omp_initializer..4(ptr noundef [[ADD_PTR]], ptr noundef [[ADD_PTR5]])
@@ -2612,7 +2613,7 @@ int main() {
 // CHECK1-NEXT:    [[IDXPROM:%.*]] = sext i32 [[TMP17]] to i64
 // CHECK1-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP4]], i64 0, i64 [[IDXPROM]]
 // CHECK1-NEXT:    store i32 [[TMP16]], ptr [[ARRAYIDX]], align 4
-// CHECK1-NEXT:    [[TMP18:%.*]] = load ptr, ptr [[_TMP6]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP18:%.*]] = load ptr, ptr [[_TMP6]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    [[TMP19:%.*]] = load i32, ptr [[I]], align 4
 // CHECK1-NEXT:    [[IDXPROM10:%.*]] = sext i32 [[TMP19]] to i64
 // CHECK1-NEXT:    [[ARRAYIDX11:%.*]] = getelementptr inbounds [2 x [[STRUCT_S]]], ptr [[TMP5]], i64 0, i64 [[IDXPROM10]]
@@ -2727,6 +2728,7 @@ int main() {
 // CHECK1-NEXT:    store ptr [[TMP1]], ptr [[DOTADDR1]], align 8
 // CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[DOTADDR1]], align 8
 // CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[DOTADDR]], align 8
+// CHECK1-NEXT:    call void @_ZN1SIiEC1Ev(ptr noundef nonnull align 4 dereferenceable(12) [[TMP3]])
 // CHECK1-NEXT:    [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 4
 // CHECK1-NEXT:    [[ADD_PTR2:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 4
 // CHECK1-NEXT:    call void @_Z5init2R6BaseS1RKS_(ptr noundef nonnull align 4 dereferenceable(4) [[ADD_PTR]], ptr noundef nonnull align 4 dereferenceable(4) [[ADD_PTR2]])
@@ -2823,12 +2825,12 @@ int main() {
 // CHECK1-NEXT:    store ptr [[VEC]], ptr [[VEC_ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[S_ARR]], ptr [[S_ARR_ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[VAR]], ptr [[VAR_ADDR]], align 8
-// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[T_VAR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[VEC_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[S_ARR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[VAR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[T_VAR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[VEC_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[S_ARR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[VAR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store ptr [[TMP3]], ptr [[TMP]], align 8
-// CHECK1-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store ptr [[TMP4]], ptr [[_TMP1]], align 8
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_LB]], align 4
 // CHECK1-NEXT:    store i32 1, ptr [[DOTOMP_UB]], align 4
@@ -2868,7 +2870,7 @@ int main() {
 // CHECK1-NEXT:    [[IDXPROM:%.*]] = sext i32 [[TMP15]] to i64
 // CHECK1-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP1]], i64 0, i64 [[IDXPROM]]
 // CHECK1-NEXT:    store i32 [[TMP14]], ptr [[ARRAYIDX]], align 4
-// CHECK1-NEXT:    [[TMP16:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP16:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    [[TMP17:%.*]] = load i32, ptr [[I]], align 4
 // CHECK1-NEXT:    [[IDXPROM5:%.*]] = sext i32 [[TMP17]] to i64
 // CHECK1-NEXT:    [[ARRAYIDX6:%.*]] = getelementptr inbounds [2 x [[STRUCT_S:%.*]]], ptr [[TMP2]], i64 0, i64 [[IDXPROM5]]
@@ -2967,13 +2969,13 @@ int main() {
 // CHECK1-NEXT:    store ptr [[T_VAR]], ptr [[T_VAR_ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[S_ARR]], ptr [[S_ARR_ADDR]], align 8
 // CHECK1-NEXT:    store ptr [[VAR]], ptr [[VAR_ADDR]], align 8
-// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[VEC_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[T_VAR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[S_ARR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
-// CHECK1-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[VAR_ADDR]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[VEC_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[T_VAR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[S_ARR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
+// CHECK1-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[VAR_ADDR]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store ptr [[TMP4]], ptr [[TMP]], align 8
-// CHECK1-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    store ptr [[TMP5]], ptr [[_TMP1]], align 8
 // CHECK1-NEXT:    store i32 0, ptr [[DOTOMP_LB]], align 4
 // CHECK1-NEXT:    store i32 1, ptr [[DOTOMP_UB]], align 4
@@ -3035,7 +3037,7 @@ int main() {
 // CHECK1-NEXT:    [[IDXPROM:%.*]] = sext i32 [[TMP21]] to i64
 // CHECK1-NEXT:    [[ARRAYIDX8:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP1]], i64 0, i64 [[IDXPROM]]
 // CHECK1-NEXT:    store i32 [[TMP20]], ptr [[ARRAYIDX8]], align 4
-// CHECK1-NEXT:    [[TMP22:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META3]], !align [[META4]]
+// CHECK1-NEXT:    [[TMP22:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META2]], !align [[META3]]
 // CHECK1-NEXT:    [[TMP23:%.*]] = load i32, ptr [[I]], align 4
 // CHECK1-NEXT:    [[IDXPROM9:%.*]] = sext i32 [[TMP23]] to i64
 // CHECK1-NEXT:    [[ARRAYIDX10:%.*]] = getelementptr inbounds [2 x [[STRUCT_S]]], ptr [[TMP3]], i64 0, i64 [[IDXPROM9]]
@@ -3206,24 +3208,24 @@ int main() {
 // CHECK3-NEXT:    call void @.omp_initializer.(ptr noundef [[S1]], ptr noundef [[S]])
 // CHECK3-NEXT:    br label [[OMP_INNER_FOR_COND:%.*]]
 // CHECK3:       omp.inner.for.cond:
-// CHECK3-NEXT:    [[TMP0:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP2:![0-9]+]]
+// CHECK3-NEXT:    [[TMP0:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP1:![0-9]+]]
 // CHECK3-NEXT:    [[CMP:%.*]] = icmp slt i32 [[TMP0]], 10
 // CHECK3-NEXT:    br i1 [[CMP]], label [[OMP_INNER_FOR_BODY:%.*]], label [[OMP_INNER_FOR_COND_CLEANUP:%.*]]
 // CHECK3:       omp.inner.for.cond.cleanup:
 // CHECK3-NEXT:    br label [[OMP_INNER_FOR_END:%.*]]
 // CHECK3:       omp.inner.for.body:
-// CHECK3-NEXT:    [[TMP1:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP2]]
+// CHECK3-NEXT:    [[TMP1:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP1]]
 // CHECK3-NEXT:    [[MUL:%.*]] = mul nsw i32 [[TMP1]], 1
 // CHECK3-NEXT:    [[ADD:%.*]] = add nsw i32 0, [[MUL]]
-// CHECK3-NEXT:    store i32 [[ADD]], ptr [[I]], align 4, !llvm.access.group [[ACC_GRP2]]
+// CHECK3-NEXT:    store i32 [[ADD]], ptr [[I]], align 4, !llvm.access.group [[ACC_GRP1]]
 // CHECK3-NEXT:    br label [[OMP_BODY_CONTINUE:%.*]]
 // CHECK3:       omp.body.continue:
 // CHECK3-NEXT:    br label [[OMP_INNER_FOR_INC:%.*]]
 // CHECK3:       omp.inner.for.inc:
-// CHECK3-NEXT:    [[TMP2:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP2]]
+// CHECK3-NEXT:    [[TMP2:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP1]]
 // CHECK3-NEXT:    [[ADD2:%.*]] = add nsw i32 [[TMP2]], 1
-// CHECK3-NEXT:    store i32 [[ADD2]], ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP2]]
-// CHECK3-NEXT:    br label [[OMP_INNER_FOR_COND]], !llvm.loop [[LOOP3:![0-9]+]]
+// CHECK3-NEXT:    store i32 [[ADD2]], ptr [[DOTOMP_IV]], align 4, !llvm.access.group [[ACC_GRP1]]
+// CHECK3-NEXT:    br label [[OMP_INNER_FOR_COND]], !llvm.loop [[LOOP2:![0-9]+]]
 // CHECK3:       omp.inner.for.end:
 // CHECK3-NEXT:    store i32 10, ptr [[I]], align 4
 // CHECK3-NEXT:    call void @.omp_combiner.(ptr noundef [[S]], ptr noundef [[S1]])
@@ -3348,9 +3350,9 @@ int main() {
 // CHECK3-NEXT:    br i1 [[ARRAYCTOR_DONE8]], label [[ARRAYCTOR_CONT9:%.*]], label [[ARRAYCTOR_LOOP5]]
 // CHECK3:       arrayctor.cont9:
 // CHECK3-NEXT:    store ptr [[S_ARR]], ptr [[VAR3]], align 8
-// CHECK3-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META6:![0-9]+]], !align [[META7:![0-9]+]]
+// CHECK3-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META5:![0-9]+]], !align [[META6:![0-9]+]]
 // CHECK3-NEXT:    store ptr [[TMP0]], ptr [[TMP]], align 8
-// CHECK3-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    store ptr [[TMP1]], ptr [[_TMP10]], align 8
 // CHECK3-NEXT:    store i32 0, ptr [[I]], align 4
 // CHECK3-NEXT:    br label [[FOR_COND:%.*]]
@@ -3365,7 +3367,7 @@ int main() {
 // CHECK3-NEXT:    [[IDXPROM:%.*]] = sext i32 [[TMP4]] to i64
 // CHECK3-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[VEC]], i64 0, i64 [[IDXPROM]]
 // CHECK3-NEXT:    store i32 [[CONV]], ptr [[ARRAYIDX]], align 4
-// CHECK3-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[_TMP10]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[_TMP10]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    [[TMP6:%.*]] = load i32, ptr [[I]], align 4
 // CHECK3-NEXT:    [[IDXPROM11:%.*]] = sext i32 [[TMP6]] to i64
 // CHECK3-NEXT:    [[ARRAYIDX12:%.*]] = getelementptr inbounds [4 x [[STRUCT_S_0]]], ptr [[S_ARR]], i64 0, i64 [[IDXPROM11]]
@@ -3375,7 +3377,7 @@ int main() {
 // CHECK3-NEXT:    [[TMP7:%.*]] = load i32, ptr [[I]], align 4
 // CHECK3-NEXT:    [[INC:%.*]] = add nsw i32 [[TMP7]], 1
 // CHECK3-NEXT:    store i32 [[INC]], ptr [[I]], align 4
-// CHECK3-NEXT:    br label [[FOR_COND]], !llvm.loop [[LOOP8:![0-9]+]]
+// CHECK3-NEXT:    br label [[FOR_COND]], !llvm.loop [[LOOP7:![0-9]+]]
 // CHECK3:       for.end:
 // CHECK3-NEXT:    [[ARRAYIDX14:%.*]] = getelementptr inbounds [2 x i32], ptr [[VEC]], i64 0, i64 1
 // CHECK3-NEXT:    [[TMP8:%.*]] = load i32, ptr [[ARRAYIDX14]], align 4
@@ -3405,7 +3407,7 @@ int main() {
 // CHECK3-NEXT:    [[TMP16:%.*]] = load i32, ptr [[I15]], align 4
 // CHECK3-NEXT:    [[INC24:%.*]] = add nsw i32 [[TMP16]], 1
 // CHECK3-NEXT:    store i32 [[INC24]], ptr [[I15]], align 4
-// CHECK3-NEXT:    br label [[FOR_COND16]], !llvm.loop [[LOOP10:![0-9]+]]
+// CHECK3-NEXT:    br label [[FOR_COND16]], !llvm.loop [[LOOP9:![0-9]+]]
 // CHECK3:       for.end25:
 // CHECK3-NEXT:    store i32 0, ptr [[I26]], align 4
 // CHECK3-NEXT:    br label [[FOR_COND27:%.*]]
@@ -3427,7 +3429,7 @@ int main() {
 // CHECK3-NEXT:    [[TMP21:%.*]] = load i32, ptr [[I26]], align 4
 // CHECK3-NEXT:    [[INC35:%.*]] = add nsw i32 [[TMP21]], 1
 // CHECK3-NEXT:    store i32 [[INC35]], ptr [[I26]], align 4
-// CHECK3-NEXT:    br label [[FOR_COND27]], !llvm.loop [[LOOP11:![0-9]+]]
+// CHECK3-NEXT:    br label [[FOR_COND27]], !llvm.loop [[LOOP10:![0-9]+]]
 // CHECK3:       for.end36:
 // CHECK3-NEXT:    store i32 0, ptr [[I37]], align 4
 // CHECK3-NEXT:    br label [[FOR_COND38:%.*]]
@@ -3441,7 +3443,7 @@ int main() {
 // CHECK3-NEXT:    [[TMP23:%.*]] = load i32, ptr [[I37]], align 4
 // CHECK3-NEXT:    [[INC42:%.*]] = add nsw i32 [[TMP23]], 1
 // CHECK3-NEXT:    store i32 [[INC42]], ptr [[I37]], align 4
-// CHECK3-NEXT:    br label [[FOR_COND38]], !llvm.loop [[LOOP12:![0-9]+]]
+// CHECK3-NEXT:    br label [[FOR_COND38]], !llvm.loop [[LOOP11:![0-9]+]]
 // CHECK3:       for.end43:
 // CHECK3-NEXT:    store i32 0, ptr [[I44]], align 4
 // CHECK3-NEXT:    br label [[FOR_COND45:%.*]]
@@ -3455,11 +3457,11 @@ int main() {
 // CHECK3-NEXT:    [[TMP25:%.*]] = load i32, ptr [[I44]], align 4
 // CHECK3-NEXT:    [[INC49:%.*]] = add nsw i32 [[TMP25]], 1
 // CHECK3-NEXT:    store i32 [[INC49]], ptr [[I44]], align 4
-// CHECK3-NEXT:    br label [[FOR_COND45]], !llvm.loop [[LOOP13:![0-9]+]]
+// CHECK3-NEXT:    br label [[FOR_COND45]], !llvm.loop [[LOOP12:![0-9]+]]
 // CHECK3:       for.end50:
-// CHECK3-NEXT:    [[TMP26:%.*]] = load ptr, ptr [[VAR3]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP26:%.*]] = load ptr, ptr [[VAR3]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    store ptr [[TMP26]], ptr [[_TMP51]], align 8
-// CHECK3-NEXT:    [[TMP27:%.*]] = load ptr, ptr [[_TMP51]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP27:%.*]] = load ptr, ptr [[_TMP51]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    store ptr [[TMP27]], ptr [[_TMP52]], align 8
 // CHECK3-NEXT:    store i32 0, ptr [[I53]], align 4
 // CHECK3-NEXT:    br label [[FOR_COND54:%.*]]
@@ -3473,11 +3475,11 @@ int main() {
 // CHECK3-NEXT:    [[TMP29:%.*]] = load i32, ptr [[I53]], align 4
 // CHECK3-NEXT:    [[INC58:%.*]] = add nsw i32 [[TMP29]], 1
 // CHECK3-NEXT:    store i32 [[INC58]], ptr [[I53]], align 4
-// CHECK3-NEXT:    br label [[FOR_COND54]], !llvm.loop [[LOOP14:![0-9]+]]
+// CHECK3-NEXT:    br label [[FOR_COND54]], !llvm.loop [[LOOP13:![0-9]+]]
 // CHECK3:       for.end59:
-// CHECK3-NEXT:    [[TMP30:%.*]] = load ptr, ptr [[VAR3]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP30:%.*]] = load ptr, ptr [[VAR3]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    store ptr [[TMP30]], ptr [[_TMP60]], align 8
-// CHECK3-NEXT:    [[TMP31:%.*]] = load ptr, ptr [[_TMP60]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP31:%.*]] = load ptr, ptr [[_TMP60]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    store ptr [[TMP31]], ptr [[_TMP61]], align 8
 // CHECK3-NEXT:    store i32 0, ptr [[I62]], align 4
 // CHECK3-NEXT:    br label [[FOR_COND63:%.*]]
@@ -3491,7 +3493,7 @@ int main() {
 // CHECK3-NEXT:    [[TMP33:%.*]] = load i32, ptr [[I62]], align 4
 // CHECK3-NEXT:    [[INC67:%.*]] = add nsw i32 [[TMP33]], 1
 // CHECK3-NEXT:    store i32 [[INC67]], ptr [[I62]], align 4
-// CHECK3-NEXT:    br label [[FOR_COND63]], !llvm.loop [[LOOP15:![0-9]+]]
+// CHECK3-NEXT:    br label [[FOR_COND63]], !llvm.loop [[LOOP14:![0-9]+]]
 // CHECK3:       for.end68:
 // CHECK3-NEXT:    [[CALL69:%.*]] = call noundef i32 @_Z5tmainIiLi42EET_v()
 // CHECK3-NEXT:    store i32 [[CALL69]], ptr [[RETVAL]], align 4
@@ -3596,9 +3598,9 @@ int main() {
 // CHECK3-NEXT:    [[ARRAYCTOR_DONE:%.*]] = icmp eq ptr [[ARRAYCTOR_NEXT]], [[ARRAYCTOR_END]]
 // CHECK3-NEXT:    br i1 [[ARRAYCTOR_DONE]], label [[ARRAYCTOR_CONT:%.*]], label [[ARRAYCTOR_LOOP]]
 // CHECK3:       arrayctor.cont:
-// CHECK3-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    store ptr [[TMP0]], ptr [[TMP]], align 8
-// CHECK3-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TMP]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    store ptr [[TMP1]], ptr [[_TMP1]], align 8
 // CHECK3-NEXT:    store i32 0, ptr [[I]], align 4
 // CHECK3-NEXT:    br label [[FOR_COND:%.*]]
@@ -3612,7 +3614,7 @@ int main() {
 // CHECK3-NEXT:    [[IDXPROM:%.*]] = sext i32 [[TMP4]] to i64
 // CHECK3-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[VEC]], i64 0, i64 [[IDXPROM]]
 // CHECK3-NEXT:    store i32 [[TMP3]], ptr [[ARRAYIDX]], align 4
-// CHECK3-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[_TMP1]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    [[TMP6:%.*]] = load i32, ptr [[I]], align 4
 // CHECK3-NEXT:    [[IDXPROM2:%.*]] = sext i32 [[TMP6]] to i64
 // CHECK3-NEXT:    [[ARRAYIDX3:%.*]] = getelementptr inbounds [2 x [[STRUCT_S]]], ptr [[S_ARR]], i64 0, i64 [[IDXPROM2]]
@@ -3622,11 +3624,11 @@ int main() {
 // CHECK3-NEXT:    [[TMP7:%.*]] = load i32, ptr [[I]], align 4
 // CHECK3-NEXT:    [[INC:%.*]] = add nsw i32 [[TMP7]], 1
 // CHECK3-NEXT:    store i32 [[INC]], ptr [[I]], align 4
-// CHECK3-NEXT:    br label [[FOR_COND]], !llvm.loop [[LOOP16:![0-9]+]]
+// CHECK3-NEXT:    br label [[FOR_COND]], !llvm.loop [[LOOP15:![0-9]+]]
 // CHECK3:       for.end:
-// CHECK3-NEXT:    [[TMP8:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP8:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    store ptr [[TMP8]], ptr [[_TMP4]], align 8
-// CHECK3-NEXT:    [[TMP9:%.*]] = load ptr, ptr [[_TMP4]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP9:%.*]] = load ptr, ptr [[_TMP4]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    store ptr [[TMP9]], ptr [[_TMP5]], align 8
 // CHECK3-NEXT:    store i32 0, ptr [[I6]], align 4
 // CHECK3-NEXT:    br label [[FOR_COND7:%.*]]
@@ -3640,7 +3642,7 @@ int main() {
 // CHECK3-NEXT:    [[IDXPROM10:%.*]] = sext i32 [[TMP12]] to i64
 // CHECK3-NEXT:    [[ARRAYIDX11:%.*]] = getelementptr inbounds [2 x i32], ptr [[VEC]], i64 0, i64 [[IDXPROM10]]
 // CHECK3-NEXT:    store i32 [[TMP11]], ptr [[ARRAYIDX11]], align 4
-// CHECK3-NEXT:    [[TMP13:%.*]] = load ptr, ptr [[_TMP5]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP13:%.*]] = load ptr, ptr [[_TMP5]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    [[TMP14:%.*]] = load i32, ptr [[I6]], align 4
 // CHECK3-NEXT:    [[IDXPROM12:%.*]] = sext i32 [[TMP14]] to i64
 // CHECK3-NEXT:    [[ARRAYIDX13:%.*]] = getelementptr inbounds [2 x [[STRUCT_S]]], ptr [[S_ARR]], i64 0, i64 [[IDXPROM12]]
@@ -3650,11 +3652,11 @@ int main() {
 // CHECK3-NEXT:    [[TMP15:%.*]] = load i32, ptr [[I6]], align 4
 // CHECK3-NEXT:    [[INC16:%.*]] = add nsw i32 [[TMP15]], 1
 // CHECK3-NEXT:    store i32 [[INC16]], ptr [[I6]], align 4
-// CHECK3-NEXT:    br label [[FOR_COND7]], !llvm.loop [[LOOP17:![0-9]+]]
+// CHECK3-NEXT:    br label [[FOR_COND7]], !llvm.loop [[LOOP16:![0-9]+]]
 // CHECK3:       for.end17:
-// CHECK3-NEXT:    [[TMP16:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP16:%.*]] = load ptr, ptr [[VAR]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    store ptr [[TMP16]], ptr [[_TMP18]], align 8
-// CHECK3-NEXT:    [[TMP17:%.*]] = load ptr, ptr [[_TMP18]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP17:%.*]] = load ptr, ptr [[_TMP18]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    store ptr [[TMP17]], ptr [[_TMP19]], align 8
 // CHECK3-NEXT:    store i32 0, ptr [[I20]], align 4
 // CHECK3-NEXT:    br label [[FOR_COND21:%.*]]
@@ -3668,7 +3670,7 @@ int main() {
 // CHECK3-NEXT:    [[IDXPROM24:%.*]] = sext i32 [[TMP20]] to i64
 // CHECK3-NEXT:    [[ARRAYIDX25:%.*]] = getelementptr inbounds [2 x i32], ptr [[VEC]], i64 0, i64 [[IDXPROM24]]
 // CHECK3-NEXT:    store i32 [[TMP19]], ptr [[ARRAYIDX25]], align 4
-// CHECK3-NEXT:    [[TMP21:%.*]] = load ptr, ptr [[_TMP19]], align 8, !nonnull [[META6]], !align [[META7]]
+// CHECK3-NEXT:    [[TMP21:%.*]] = load ptr, ptr [[_TMP19]], align 8, !nonnull [[META5]], !align [[META6]]
 // CHECK3-NEXT:    [[TMP22:%.*]] = load i32, ptr [[I20]], align 4
 // CHECK3-NEXT:    [[IDXPROM26:%.*]] = sext i32 [[TMP22]] to i64
 // CHECK3-NEXT:    [[ARRAYIDX27:%.*]] = getelementptr inbounds [2 x [[STRUCT_S]]], ptr [[S_ARR]], i64 0, i64 [[IDXPROM26]]
@@ -3678,7 +3680,7 @@ int main() {
 // CHECK3-NEXT:    [[TMP23:%.*]] = load i32, ptr [[I20]], align 4
 // CHECK3-NEXT:    [[INC30:%.*]] = add nsw i32 [[TMP23]], 1
 // CHECK3-NEXT:    store i32 [[INC30]], ptr [[I20]], align 4
-// CHECK3-NEXT:    br label [[FOR_COND21]], !llvm.loop [[LOOP18:![0-9]+]]
+// CHECK3-NEXT:    br label [[FOR_COND21]], !llvm.loop [[LOOP17:![0-9]+]]
 // CHECK3:       for.end31:
 // CHECK3-NEXT:    store i32 0, ptr [[RETVAL]], align 4
 // CHECK3-NEXT:    [[ARRAY_BEGIN32:%.*]] = getelementptr inbounds [42 x [[STRUCT_S]]], ptr [[ARR]], i32 0, i32 0
diff --git a/clang/test/OpenMP/reduction_non_copyable_with_initializer.cpp b/clang/test/OpenMP/reduction_non_copyable_with_initializer.cpp
new file mode 100644
index 0000000000000..c5926648291ab
--- /dev/null
+++ b/clang/test/OpenMP/reduction_non_copyable_with_initializer.cpp
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=60 -x c++ -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+// Simple wrapper class that requires construction before assignment
+struct Wrapper {
+  int* ptr;
+  Wrapper() : ptr(new int(0)) {}
+  ~Wrapper() { delete ptr; }
+
+  // Non-copyable
+  Wrapper(const Wrapper&) = delete;
+  Wrapper& operator=(const Wrapper&) = delete;
+
+  void assign(int val) { *ptr = val; }
+  int get() const { return *ptr; }
+};
+
+struct my_struct {
+  int a;
+  int b;
+  Wrapper w; // Non-trivial member that needs construction
+
+  my_struct() : a(1), b(1), w() {}
+
+  // Non-copyable: deleted copy constructor and assignment
+  my_struct(const my_struct&) = delete;
+  my_struct& operator=(const my_struct&) = delete;
+};
+
+void my_init_default(my_struct& t) {
+  t.a = 0;
+  t.b = 0;
+  t.w.assign(0); // This requires w to be constructed first
+}
+
+void my_add(my_struct& lhs, const my_struct& rhs) {
+  lhs.a += rhs.a;
+  lhs.b += rhs.b;
+}
+
+static my_struct x;
+my_struct y;
+
+// Custom reduction with user-defined initializer.
+// The initializer uses operations that require the object to be properly
+// constructed first (especially the non-trivial Wrapper member).
+// Without emitting the default constructor first, this would crash.
+#pragma omp declare reduction(my_reduction_add : my_struct : my_add(omp_out, omp_in)) \
+    initializer(my_init_default(omp_priv))
+
+void foo() {
+  #pragma omp parallel reduction(my_reduction_add:y)
+  my_add(y, x);
+}
+
+// Verify that the .omp_initializer function emits the default constructor
+// BEFORE calling the user-defined initializer function. This is critical for
+// types with non-trivial members (like Wrapper with its pointer management).
+// Without the constructor call first, the user initializer would operate on
+// uninitialized memory, causing crashes.
+
+// CHECK-LABEL: define internal void @.omp_initializer.
+// CHECK: call {{.*}} @_ZN9my_structC1Ev(ptr {{[^)]+}})
+// CHECK-NEXT: call void @_Z15my_init_defaultR9my_struct(ptr {{[^)]+}})
+// CHECK-NEXT: ret void

>From e6305777b226ceceee7adc1b27457d1cb00ea0e5 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Fri, 28 Aug 2026 05:47:29 -0700
Subject: [PATCH 2/5] Applied reviewer's suggestion and fixed format

---
 clang/lib/CodeGen/CGOpenMPRuntime.cpp | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
index dba5ecbf2318c..9dfbc8adcc12b 100644
--- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp
+++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -1125,8 +1125,9 @@ emitCombinerOrInitializer(CodeGenModule &CGM, QualType Ty,
       In, CGF.EmitLoadOfPointerLValue(AddrIn, PtrTy->castAs<PointerType>())
               .getAddress());
   Address AddrOut = CGF.GetAddrOfLocalVar(OmpOutParm);
-  Address OutDerefAddr = CGF.EmitLoadOfPointerLValue(
-      AddrOut, PtrTy->castAs<PointerType>()).getAddress();
+  Address OutDerefAddr =
+      CGF.EmitLoadOfPointerLValue(AddrOut, PtrTy->castAs<PointerType>())
+          .getAddress();
   Scope.addPrivate(Out, OutDerefAddr);
   (void)Scope.Privatize();
   if (!IsCombiner && Out->hasInit() &&
@@ -1146,13 +1147,11 @@ emitCombinerOrInitializer(CodeGenModule &CGM, QualType Ty,
             // Synthesize CXXConstructExpr for default construction.
             // Collect default arguments for parameters with defaults.
             ASTContext &Ctx = CGM.getContext();
-            SmallVector<Expr*, 4> Args;
-            for (unsigned I = 0; I < Ctor->getNumParams(); ++I) {
-              const ParmVarDecl *Param = Ctor->getParamDecl(I);
-              if (Param->hasDefaultArg()) {
-                Args.push_back(const_cast<Expr*>(Param->getDefaultArg()));
-              }
-            }
+            SmallVector<Expr *, 4> Args;
+            for (unsigned I = 0; I < Ctor->getNumParams(); ++I)
+              if (const ParmVarDecl *Param = Ctor->getParamDecl(I);
+                  Param->hasDefaultArg())
+                Args.push_back(const_cast<Expr *>(Param->getDefaultArg()));
 
             CXXConstructExpr *CtorExpr = CXXConstructExpr::Create(
                 Ctx, Ty, Out->getLocation(),
@@ -1164,10 +1163,8 @@ emitCombinerOrInitializer(CodeGenModule &CGM, QualType Ty,
 
             // Emit the constructor call
             AggValueSlot Slot = AggValueSlot::forAddr(
-                OutDerefAddr, Ty.getQualifiers(),
-                AggValueSlot::IsDestructed,
-                AggValueSlot::DoesNotNeedGCBarriers,
-                AggValueSlot::IsNotAliased,
+                OutDerefAddr, Ty.getQualifiers(), AggValueSlot::IsDestructed,
+                AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsNotAliased,
                 AggValueSlot::DoesNotOverlap);
             CGF.EmitCXXConstructExpr(CtorExpr, Slot);
             break;

>From 77034dd532c36eaf093d7f562cf6b949401b55fd Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Wed, 2 Sep 2026 14:25:12 -0700
Subject: [PATCH 3/5] Addressed review comments

---
 clang/lib/CodeGen/CGOpenMPRuntime.cpp         | 61 +++++++------------
 clang/lib/Sema/SemaOpenMP.cpp                 | 57 +++++++++++++++++
 .../test/OpenMP/for_reduction_codegen_UDR.cpp | 26 ++++----
 3 files changed, 93 insertions(+), 51 deletions(-)

diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
index 9dfbc8adcc12b..5004944342b33 100644
--- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp
+++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -1125,10 +1125,9 @@ emitCombinerOrInitializer(CodeGenModule &CGM, QualType Ty,
       In, CGF.EmitLoadOfPointerLValue(AddrIn, PtrTy->castAs<PointerType>())
               .getAddress());
   Address AddrOut = CGF.GetAddrOfLocalVar(OmpOutParm);
-  Address OutDerefAddr =
-      CGF.EmitLoadOfPointerLValue(AddrOut, PtrTy->castAs<PointerType>())
-          .getAddress();
-  Scope.addPrivate(Out, OutDerefAddr);
+  Scope.addPrivate(
+      Out, CGF.EmitLoadOfPointerLValue(AddrOut, PtrTy->castAs<PointerType>())
+               .getAddress());
   (void)Scope.Privatize();
   if (!IsCombiner && Out->hasInit() &&
       !CGF.isTrivialInitializer(Out->getInit())) {
@@ -1137,42 +1136,28 @@ emitCombinerOrInitializer(CodeGenModule &CGM, QualType Ty,
                          /*IsInitializer=*/true);
   }
   if (CombinerInitializer) {
-    // For non-trivial types with user initializers, we must emit default
-    // construction first to properly initialize members (e.g., std::string)
-    // before the user initializer assigns to them. This matches GCC behavior.
-    if (!IsCombiner && Ty.isDestructedType() != QualType::DK_none) {
-      if (const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl()) {
-        for (const CXXConstructorDecl *Ctor : RD->ctors()) {
-          if (Ctor->isDefaultConstructor()) {
-            // Synthesize CXXConstructExpr for default construction.
-            // Collect default arguments for parameters with defaults.
-            ASTContext &Ctx = CGM.getContext();
-            SmallVector<Expr *, 4> Args;
-            for (unsigned I = 0; I < Ctor->getNumParams(); ++I)
-              if (const ParmVarDecl *Param = Ctor->getParamDecl(I);
-                  Param->hasDefaultArg())
-                Args.push_back(const_cast<Expr *>(Param->getDefaultArg()));
-
-            CXXConstructExpr *CtorExpr = CXXConstructExpr::Create(
-                Ctx, Ty, Out->getLocation(),
-                const_cast<CXXConstructorDecl *>(Ctor), /*Elidable=*/false,
-                /*Args=*/Args,
-                /*HadMultipleCandidates=*/false, /*ListInit=*/false,
-                /*StdInitListInit=*/false, /*ZeroInit=*/false,
-                CXXConstructionKind::Complete, SourceRange());
-
-            // Emit the constructor call
-            AggValueSlot Slot = AggValueSlot::forAddr(
-                OutDerefAddr, Ty.getQualifiers(), AggValueSlot::IsDestructed,
-                AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsNotAliased,
-                AggValueSlot::DoesNotOverlap);
-            CGF.EmitCXXConstructExpr(CtorExpr, Slot);
-            break;
-          }
-        }
+    // For non-trivial types with user initializers wrapped in StmtExpr,
+    // manually emit each statement to ensure constructor targets the right
+    // address.
+    if (!IsCombiner && Ty.isDestructedType() != QualType::DK_none &&
+        isa<StmtExpr>(CombinerInitializer)) {
+      const auto *SE = cast<StmtExpr>(CombinerInitializer);
+      const CompoundStmt *CS = SE->getSubStmt();
+      AggValueSlot Slot = AggValueSlot::forAddr(
+          CGF.GetAddrOfLocalVar(Out), Ty.getQualifiers(),
+          AggValueSlot::IsDestructed, AggValueSlot::DoesNotNeedGCBarriers,
+          AggValueSlot::IsNotAliased, AggValueSlot::DoesNotOverlap);
+
+      // Emit each statement individually, passing slot to constructor.
+      for (const Stmt *S : CS->body()) {
+        if (const auto *CtorExpr = dyn_cast<CXXConstructExpr>(S))
+          CGF.EmitCXXConstructExpr(CtorExpr, Slot);
+        else
+          CGF.EmitStmt(S);
       }
+    } else {
+      CGF.EmitIgnoredExpr(CombinerInitializer);
     }
-    CGF.EmitIgnoredExpr(CombinerInitializer);
   }
   Scope.ForceCleanup();
   CGF.FinishFunction();
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 7ddda157dad9d..b11fb45a47481 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -23,6 +23,7 @@
 #include "clang/AST/DeclOpenMP.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/AST/OpenMPClause.h"
+#include "clang/AST/Stmt.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/StmtOpenMP.h"
 #include "clang/AST/StmtVisitor.h"
@@ -24377,6 +24378,62 @@ VarDecl *SemaOpenMP::ActOnOpenMPDeclareReductionInitializerStart(Scope *S,
 void SemaOpenMP::ActOnOpenMPDeclareReductionInitializerEnd(
     Decl *D, Expr *Initializer, VarDecl *OmpPrivParm) {
   auto *DRD = cast<OMPDeclareReductionDecl>(D);
+
+  // For non-trivial types with user initializers, build an AST that
+  // includes default construction first to initialize members before user
+  // initializer. This must be done BEFORE popping contexts.
+  if (Initializer) {
+    QualType ReductionType = DRD->getType();
+    if (ReductionType.isDestructedType() != QualType::DK_none) {
+      if (const auto *RD = ReductionType->getAsCXXRecordDecl()) {
+        CXXConstructorDecl *DefaultCtor =
+            SemaRef.LookupDefaultConstructor(const_cast<CXXRecordDecl *>(RD));
+
+        if (DefaultCtor && !DefaultCtor->isDeleted()) {
+          // Build default arguments for constructor parameters.
+          SmallVector<Expr *, 4> CtorArgs;
+          for (unsigned I : llvm::seq(DefaultCtor->getNumParams())) {
+            const ParmVarDecl *Param = DefaultCtor->getParamDecl(I);
+            if (Param->hasDefaultArg()) {
+              ExprResult DefArg = SemaRef.BuildCXXDefaultArgExpr(
+                  D->getLocation(), DefaultCtor,
+                  const_cast<ParmVarDecl *>(Param));
+              if (DefArg.isUsable())
+                CtorArgs.push_back(DefArg.get());
+            }
+          }
+
+          // Build constructor expression targeting omp_priv.
+          ExprResult CtorCall = SemaRef.BuildCXXConstructExpr(
+              D->getLocation(), ReductionType, DefaultCtor,
+              /*Elidable=*/false, CtorArgs,
+              /*HadMultipleCandidates=*/false,
+              /*IsListInitialization=*/false,
+              /*IsStdInitListInitialization=*/false,
+              /*RequiresZeroInit=*/false, CXXConstructionKind::Complete,
+              SourceRange());
+
+          if (CtorCall.isUsable()) {
+            // Wrap constructor and user initializer in StmtExpr.
+            // Create CompoundStmt directly since we don't have an active
+            // scope.
+            SmallVector<Stmt *, 2> Stmts;
+            Stmts.push_back(CtorCall.get());
+            Stmts.push_back(Initializer);
+
+            CompoundStmt *CS = CompoundStmt::Create(
+                SemaRef.Context, Stmts, FPOptionsOverride(), D->getLocation(),
+                D->getLocation());
+
+            Initializer = new (SemaRef.Context)
+                StmtExpr(CS, ReductionType, D->getLocation(), D->getLocation(),
+                         /*TemplateDepth=*/0);
+          }
+        }
+      }
+    }
+  }
+
   SemaRef.DiscardCleanupsInEvaluationContext();
   SemaRef.PopExpressionEvaluationContext();
 
diff --git a/clang/test/OpenMP/for_reduction_codegen_UDR.cpp b/clang/test/OpenMP/for_reduction_codegen_UDR.cpp
index a33d93c7ebdc4..58de296b96c8f 100644
--- a/clang/test/OpenMP/for_reduction_codegen_UDR.cpp
+++ b/clang/test/OpenMP/for_reduction_codegen_UDR.cpp
@@ -3719,40 +3719,40 @@ int main() {
 // CHECK3-NEXT:    ret void
 //
 //
-// CHECK3-LABEL: define {{[^@]+}}@_ZN1SIiEC2Ev
+// CHECK3-LABEL: define {{[^@]+}}@_ZN1SIfEC2Ev
 // CHECK3-SAME: (ptr noundef nonnull align 4 dereferenceable(12) [[THIS:%.*]]) unnamed_addr #[[ATTR0]] align 2 {
 // CHECK3-NEXT:  entry:
 // CHECK3-NEXT:    [[THIS_ADDR:%.*]] = alloca ptr, align 8
 // CHECK3-NEXT:    store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
 // CHECK3-NEXT:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
 // CHECK3-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[THIS1]], i64 4
-// CHECK3-NEXT:    [[F:%.*]] = getelementptr inbounds nuw [[STRUCT_S:%.*]], ptr [[THIS1]], i32 0, i32 2
+// CHECK3-NEXT:    [[F:%.*]] = getelementptr inbounds nuw [[STRUCT_S_0:%.*]], ptr [[THIS1]], i32 0, i32 2
 // CHECK3-NEXT:    [[TMP1:%.*]] = load volatile double, ptr @g, align 8
-// CHECK3-NEXT:    [[CONV:%.*]] = fptosi double [[TMP1]] to i32
-// CHECK3-NEXT:    store i32 [[CONV]], ptr [[F]], align 4
+// CHECK3-NEXT:    [[CONV:%.*]] = fptrunc double [[TMP1]] to float
+// CHECK3-NEXT:    store float [[CONV]], ptr [[F]], align 4
 // CHECK3-NEXT:    ret void
 //
 //
-// CHECK3-LABEL: define {{[^@]+}}@_ZN1SIiED2Ev
-// CHECK3-SAME: (ptr noundef nonnull align 4 dead_on_return(12) dereferenceable(12) [[THIS:%.*]]) unnamed_addr #[[ATTR0]] align 2 {
+// CHECK3-LABEL: define {{[^@]+}}@_ZN1SIiEC2Ev
+// CHECK3-SAME: (ptr noundef nonnull align 4 dereferenceable(12) [[THIS:%.*]]) unnamed_addr #[[ATTR0]] align 2 {
 // CHECK3-NEXT:  entry:
 // CHECK3-NEXT:    [[THIS_ADDR:%.*]] = alloca ptr, align 8
 // CHECK3-NEXT:    store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
 // CHECK3-NEXT:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK3-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[THIS1]], i64 4
+// CHECK3-NEXT:    [[F:%.*]] = getelementptr inbounds nuw [[STRUCT_S:%.*]], ptr [[THIS1]], i32 0, i32 2
+// CHECK3-NEXT:    [[TMP1:%.*]] = load volatile double, ptr @g, align 8
+// CHECK3-NEXT:    [[CONV:%.*]] = fptosi double [[TMP1]] to i32
+// CHECK3-NEXT:    store i32 [[CONV]], ptr [[F]], align 4
 // CHECK3-NEXT:    ret void
 //
 //
-// CHECK3-LABEL: define {{[^@]+}}@_ZN1SIfEC2Ev
-// CHECK3-SAME: (ptr noundef nonnull align 4 dereferenceable(12) [[THIS:%.*]]) unnamed_addr #[[ATTR0]] align 2 {
+// CHECK3-LABEL: define {{[^@]+}}@_ZN1SIiED2Ev
+// CHECK3-SAME: (ptr noundef nonnull align 4 dead_on_return(12) dereferenceable(12) [[THIS:%.*]]) unnamed_addr #[[ATTR0]] align 2 {
 // CHECK3-NEXT:  entry:
 // CHECK3-NEXT:    [[THIS_ADDR:%.*]] = alloca ptr, align 8
 // CHECK3-NEXT:    store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
 // CHECK3-NEXT:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
-// CHECK3-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[THIS1]], i64 4
-// CHECK3-NEXT:    [[F:%.*]] = getelementptr inbounds nuw [[STRUCT_S_0:%.*]], ptr [[THIS1]], i32 0, i32 2
-// CHECK3-NEXT:    [[TMP1:%.*]] = load volatile double, ptr @g, align 8
-// CHECK3-NEXT:    [[CONV:%.*]] = fptrunc double [[TMP1]] to float
-// CHECK3-NEXT:    store float [[CONV]], ptr [[F]], align 4
 // CHECK3-NEXT:    ret void
 //
 //

>From 8161dc3c268a63f634c1deee4f16a95d903601e4 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Thu, 10 Sep 2026 07:43:14 -0700
Subject: [PATCH 4/5] Applied suggestions

---
 clang/lib/CodeGen/CGOpenMPRuntime.cpp |  3 +-
 clang/lib/Sema/SemaOpenMP.cpp         | 86 +++++++++++++--------------
 2 files changed, 43 insertions(+), 46 deletions(-)

diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
index 5004944342b33..e1acf0028d923 100644
--- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp
+++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -1139,8 +1139,7 @@ emitCombinerOrInitializer(CodeGenModule &CGM, QualType Ty,
     // For non-trivial types with user initializers wrapped in StmtExpr,
     // manually emit each statement to ensure constructor targets the right
     // address.
-    if (!IsCombiner && Ty.isDestructedType() != QualType::DK_none &&
-        isa<StmtExpr>(CombinerInitializer)) {
+    if (!IsCombiner && isa<StmtExpr>(CombinerInitializer)) {
       const auto *SE = cast<StmtExpr>(CombinerInitializer);
       const CompoundStmt *CS = SE->getSubStmt();
       AggValueSlot Slot = AggValueSlot::forAddr(
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index b11fb45a47481..6f30980b86a4e 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -24382,53 +24382,51 @@ void SemaOpenMP::ActOnOpenMPDeclareReductionInitializerEnd(
   // For non-trivial types with user initializers, build an AST that
   // includes default construction first to initialize members before user
   // initializer. This must be done BEFORE popping contexts.
-  if (Initializer) {
+  if (Initializer && !DRD->getDeclContext()->isDependentContext()) {
     QualType ReductionType = DRD->getType();
-    if (ReductionType.isDestructedType() != QualType::DK_none) {
-      if (const auto *RD = ReductionType->getAsCXXRecordDecl()) {
-        CXXConstructorDecl *DefaultCtor =
-            SemaRef.LookupDefaultConstructor(const_cast<CXXRecordDecl *>(RD));
-
-        if (DefaultCtor && !DefaultCtor->isDeleted()) {
-          // Build default arguments for constructor parameters.
-          SmallVector<Expr *, 4> CtorArgs;
-          for (unsigned I : llvm::seq(DefaultCtor->getNumParams())) {
-            const ParmVarDecl *Param = DefaultCtor->getParamDecl(I);
-            if (Param->hasDefaultArg()) {
-              ExprResult DefArg = SemaRef.BuildCXXDefaultArgExpr(
-                  D->getLocation(), DefaultCtor,
-                  const_cast<ParmVarDecl *>(Param));
-              if (DefArg.isUsable())
-                CtorArgs.push_back(DefArg.get());
-            }
+    if (const auto *RD = ReductionType->getAsCXXRecordDecl()) {
+      CXXConstructorDecl *DefaultCtor =
+          SemaRef.LookupDefaultConstructor(const_cast<CXXRecordDecl *>(RD));
+      if (DefaultCtor && !DefaultCtor->isDeleted() &&
+          !DefaultCtor->isTrivial()) {
+        // Build default arguments for constructor parameters.
+        SmallVector<Expr *, 4> CtorArgs;
+        for (unsigned I : llvm::seq(DefaultCtor->getNumParams())) {
+          const ParmVarDecl *Param = DefaultCtor->getParamDecl(I);
+          if (Param->hasDefaultArg()) {
+            ExprResult DefArg = SemaRef.BuildCXXDefaultArgExpr(
+                D->getLocation(), DefaultCtor,
+                const_cast<ParmVarDecl *>(Param));
+            if (DefArg.isUsable())
+              CtorArgs.push_back(DefArg.get());
           }
+        }
 
-          // Build constructor expression targeting omp_priv.
-          ExprResult CtorCall = SemaRef.BuildCXXConstructExpr(
-              D->getLocation(), ReductionType, DefaultCtor,
-              /*Elidable=*/false, CtorArgs,
-              /*HadMultipleCandidates=*/false,
-              /*IsListInitialization=*/false,
-              /*IsStdInitListInitialization=*/false,
-              /*RequiresZeroInit=*/false, CXXConstructionKind::Complete,
-              SourceRange());
-
-          if (CtorCall.isUsable()) {
-            // Wrap constructor and user initializer in StmtExpr.
-            // Create CompoundStmt directly since we don't have an active
-            // scope.
-            SmallVector<Stmt *, 2> Stmts;
-            Stmts.push_back(CtorCall.get());
-            Stmts.push_back(Initializer);
-
-            CompoundStmt *CS = CompoundStmt::Create(
-                SemaRef.Context, Stmts, FPOptionsOverride(), D->getLocation(),
-                D->getLocation());
-
-            Initializer = new (SemaRef.Context)
-                StmtExpr(CS, ReductionType, D->getLocation(), D->getLocation(),
-                         /*TemplateDepth=*/0);
-          }
+        // Build constructor expression targeting omp_priv.
+        ExprResult CtorCall = SemaRef.BuildCXXConstructExpr(
+            D->getLocation(), ReductionType, DefaultCtor,
+            /*Elidable=*/false, CtorArgs,
+            /*HadMultipleCandidates=*/false,
+            /*IsListInitialization=*/false,
+            /*IsStdInitListInitialization=*/false,
+            /*RequiresZeroInit=*/false, CXXConstructionKind::Complete,
+            SourceRange());
+
+        if (CtorCall.isUsable()) {
+          // Wrap constructor and user initializer in StmtExpr.
+          // Create CompoundStmt directly since we don't have an active
+          // scope.
+          SmallVector<Stmt *, 2> Stmts;
+          Stmts.push_back(CtorCall.get());
+          Stmts.push_back(Initializer);
+
+          CompoundStmt *CS =
+              CompoundStmt::Create(SemaRef.Context, Stmts, FPOptionsOverride(),
+                                   D->getLocation(), D->getLocation());
+
+          Initializer = new (SemaRef.Context) StmtExpr(
+              CS, SemaRef.Context.VoidTy, D->getLocation(), D->getLocation(),
+              /*TemplateDepth=*/0);
         }
       }
     }

>From 9cd28b04a102c8d8c5213090d95d2a906f0e85e4 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Fri, 11 Sep 2026 07:20:26 -0700
Subject: [PATCH 5/5] Used standard functions

---
 clang/lib/CodeGen/CGOpenMPRuntime.cpp         |  5 ++-
 clang/lib/Sema/SemaOpenMP.cpp                 | 35 ++++---------------
 .../test/OpenMP/for_reduction_codegen_UDR.cpp | 18 +++++-----
 3 files changed, 20 insertions(+), 38 deletions(-)

diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
index e1acf0028d923..b99697c498eb8 100644
--- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp
+++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -1149,7 +1149,10 @@ emitCombinerOrInitializer(CodeGenModule &CGM, QualType Ty,
 
       // Emit each statement individually, passing slot to constructor.
       for (const Stmt *S : CS->body()) {
-        if (const auto *CtorExpr = dyn_cast<CXXConstructExpr>(S))
+        const Stmt *Inner = S;
+        if (const auto *EWC = dyn_cast<ExprWithCleanups>(S))
+          Inner = EWC->getSubExpr();
+        if (const auto *CtorExpr = dyn_cast<CXXConstructExpr>(Inner))
           CGF.EmitCXXConstructExpr(CtorExpr, Slot);
         else
           CGF.EmitStmt(S);
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 6f30980b86a4e..979614afd4b32 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -24381,7 +24381,7 @@ void SemaOpenMP::ActOnOpenMPDeclareReductionInitializerEnd(
 
   // For non-trivial types with user initializers, build an AST that
   // includes default construction first to initialize members before user
-  // initializer. This must be done BEFORE popping contexts.
+  // initializer. This must be done before popping contexts.
   if (Initializer && !DRD->getDeclContext()->isDependentContext()) {
     QualType ReductionType = DRD->getType();
     if (const auto *RD = ReductionType->getAsCXXRecordDecl()) {
@@ -24389,35 +24389,14 @@ void SemaOpenMP::ActOnOpenMPDeclareReductionInitializerEnd(
           SemaRef.LookupDefaultConstructor(const_cast<CXXRecordDecl *>(RD));
       if (DefaultCtor && !DefaultCtor->isDeleted() &&
           !DefaultCtor->isTrivial()) {
-        // Build default arguments for constructor parameters.
-        SmallVector<Expr *, 4> CtorArgs;
-        for (unsigned I : llvm::seq(DefaultCtor->getNumParams())) {
-          const ParmVarDecl *Param = DefaultCtor->getParamDecl(I);
-          if (Param->hasDefaultArg()) {
-            ExprResult DefArg = SemaRef.BuildCXXDefaultArgExpr(
-                D->getLocation(), DefaultCtor,
-                const_cast<ParmVarDecl *>(Param));
-            if (DefArg.isUsable())
-              CtorArgs.push_back(DefArg.get());
-          }
-        }
+        SemaRef.ActOnUninitializedDecl(OmpPrivParm);
+        if (Expr *DefaultInit = OmpPrivParm->getInit()) {
+          OmpPrivParm->setInit(nullptr);
 
-        // Build constructor expression targeting omp_priv.
-        ExprResult CtorCall = SemaRef.BuildCXXConstructExpr(
-            D->getLocation(), ReductionType, DefaultCtor,
-            /*Elidable=*/false, CtorArgs,
-            /*HadMultipleCandidates=*/false,
-            /*IsListInitialization=*/false,
-            /*IsStdInitListInitialization=*/false,
-            /*RequiresZeroInit=*/false, CXXConstructionKind::Complete,
-            SourceRange());
-
-        if (CtorCall.isUsable()) {
-          // Wrap constructor and user initializer in StmtExpr.
-          // Create CompoundStmt directly since we don't have an active
-          // scope.
+          // Wrap default-init and user initializer in a StmtExpr. Create
+          // CompoundStmt directly since we don't have an active scope.
           SmallVector<Stmt *, 2> Stmts;
-          Stmts.push_back(CtorCall.get());
+          Stmts.push_back(DefaultInit);
           Stmts.push_back(Initializer);
 
           CompoundStmt *CS =
diff --git a/clang/test/OpenMP/for_reduction_codegen_UDR.cpp b/clang/test/OpenMP/for_reduction_codegen_UDR.cpp
index 58de296b96c8f..fcc6b6fe6b9ea 100644
--- a/clang/test/OpenMP/for_reduction_codegen_UDR.cpp
+++ b/clang/test/OpenMP/for_reduction_codegen_UDR.cpp
@@ -3733,6 +3733,15 @@ int main() {
 // CHECK3-NEXT:    ret void
 //
 //
+// CHECK3-LABEL: define {{[^@]+}}@_ZN1SIfED2Ev
+// CHECK3-SAME: (ptr noundef nonnull align 4 dead_on_return(12) dereferenceable(12) [[THIS:%.*]]) unnamed_addr #[[ATTR0]] align 2 {
+// CHECK3-NEXT:  entry:
+// CHECK3-NEXT:    [[THIS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK3-NEXT:    store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
+// CHECK3-NEXT:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK3-NEXT:    ret void
+//
+//
 // CHECK3-LABEL: define {{[^@]+}}@_ZN1SIiEC2Ev
 // CHECK3-SAME: (ptr noundef nonnull align 4 dereferenceable(12) [[THIS:%.*]]) unnamed_addr #[[ATTR0]] align 2 {
 // CHECK3-NEXT:  entry:
@@ -3756,15 +3765,6 @@ int main() {
 // CHECK3-NEXT:    ret void
 //
 //
-// CHECK3-LABEL: define {{[^@]+}}@_ZN1SIfED2Ev
-// CHECK3-SAME: (ptr noundef nonnull align 4 dead_on_return(12) dereferenceable(12) [[THIS:%.*]]) unnamed_addr #[[ATTR0]] align 2 {
-// CHECK3-NEXT:  entry:
-// CHECK3-NEXT:    [[THIS_ADDR:%.*]] = alloca ptr, align 8
-// CHECK3-NEXT:    store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
-// CHECK3-NEXT:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
-// CHECK3-NEXT:    ret void
-//
-//
 // CHECK3-LABEL: define {{[^@]+}}@_ZN1SIfEC2Ef
 // CHECK3-SAME: (ptr noundef nonnull align 4 dereferenceable(12) [[THIS:%.*]], float noundef [[A:%.*]]) unnamed_addr #[[ATTR0]] align 2 {
 // CHECK3-NEXT:  entry:



More information about the cfe-commits mailing list