https://github.com/andykaylor updated 
https://github.com/llvm/llvm-project/pull/144583

>From 80cd92fd1ea93a2f1c1b037d3a5354ee94f565f6 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akay...@nvidia.com>
Date: Mon, 16 Jun 2025 16:39:15 -0700
Subject: [PATCH 1/3] [CIR] Add support for member initialization from
 constructors

Upstream the code to handle member variable initialization in a constructor.
At this point only simple scalar values (including members of anonymous
unions) are handled.
---
 clang/include/clang/CIR/MissingFeatures.h |   1 +
 clang/lib/CIR/CodeGen/CIRGenClass.cpp     | 154 +++++++++++++++++++++-
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp      |  28 ++++
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp  |   9 ++
 clang/lib/CIR/CodeGen/CIRGenFunction.h    |  33 +++++
 clang/lib/CIR/CodeGen/CIRGenModule.cpp    |   4 +
 clang/lib/CIR/CodeGen/CIRGenTypeCache.h   |  11 +-
 clang/test/CIR/CodeGen/ctor.cpp           | 106 +++++++++++++++
 8 files changed, 340 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index 3d120903dea19..a3ba40f73d06f 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -240,6 +240,7 @@ struct MissingFeatures {
   static bool builtinCall() { return false; }
   static bool builtinCallF128() { return false; }
   static bool builtinCallMathErrno() { return false; }
+  static bool ctorMemcpyizer() { return false; }
 
   // Missing types
   static bool dataMemberType() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp 
b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index e59a1fdb837cb..25f3e19befa48 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -53,19 +53,135 @@ bool CIRGenFunction::isConstructorDelegationValid(
   return true;
 }
 
+static void emitLValueForAnyFieldInitialization(CIRGenFunction &cgf,
+                                                CXXCtorInitializer *memberInit,
+                                                LValue &lhs) {
+  FieldDecl *field = memberInit->getAnyMember();
+  if (memberInit->isIndirectMemberInitializer()) {
+    // If we are initializing an anonymous union field, drill down to the 
field.
+    IndirectFieldDecl *indirectField = memberInit->getIndirectMember();
+    for (const auto *nd : indirectField->chain()) {
+      auto *fd = cast<clang::FieldDecl>(nd);
+      lhs = cgf.emitLValueForFieldInitialization(lhs, fd, fd->getName());
+    }
+  } else {
+    lhs = cgf.emitLValueForFieldInitialization(lhs, field, field->getName());
+  }
+}
+
+static void emitMemberInitializer(CIRGenFunction &cgf,
+                                  const CXXRecordDecl *classDecl,
+                                  CXXCtorInitializer *memberInit,
+                                  const CXXConstructorDecl *constructor,
+                                  FunctionArgList &args) {
+  assert(memberInit->isAnyMemberInitializer() &&
+         "Mush have member initializer!");
+  assert(memberInit->getInit() && "Must have initializer!");
+
+  assert(!cir::MissingFeatures::generateDebugInfo());
+
+  // non-static data member initializers
+  FieldDecl *field = memberInit->getAnyMember();
+  QualType fieldType = field->getType();
+
+  mlir::Value thisPtr = cgf.loadCXXThis();
+  QualType recordTy = cgf.getContext().getTypeDeclType(classDecl);
+  LValue lhs;
+
+  // If a base constructor is being emitted, create an LValue that has the
+  // non-virtual alignment.
+  if (cgf.curGD.getCtorType() == Ctor_Base)
+    lhs = cgf.makeNaturalAlignPointeeAddrLValue(thisPtr, recordTy);
+  else
+    lhs = cgf.makeNaturalAlignAddrLValue(thisPtr, recordTy);
+
+  emitLValueForAnyFieldInitialization(cgf, memberInit, lhs);
+
+  // Special case: If we are in a copy or move constructor, and we are copying
+  // an array off PODs or classes with tirival copy constructors, ignore the 
AST
+  // and perform the copy we know is equivalent.
+  // FIXME: This is hacky at best... if we had a bit more explicit information
+  // in the AST, we could generalize it more easily.
+  const ConstantArrayType *array =
+      cgf.getContext().getAsConstantArrayType(fieldType);
+  if (array && constructor->isDefaulted() &&
+      constructor->isCopyOrMoveConstructor()) {
+    QualType baseElementTy = cgf.getContext().getBaseElementType(array);
+    // NOTE(cir): CodeGen allows record types to be memcpy'd if applicable,
+    // whereas ClangIR wants to represent all object construction explicitly.
+    if (!baseElementTy->isRecordType()) {
+      cgf.cgm.errorNYI(memberInit->getSourceRange(),
+                       "emitMemberInitializer: array of non-record type");
+      return;
+    }
+  }
+
+  cgf.emitInitializerForField(field, lhs, memberInit->getInit());
+}
+
 /// This routine generates necessary code to initialize base classes and
 /// non-static data members belonging to this constructor.
 void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
                                       CXXCtorType ctorType,
                                       FunctionArgList &args) {
-  if (cd->isDelegatingConstructor())
-    return emitDelegatingCXXConstructorCall(cd, args);
+  if (cd->isDelegatingConstructor()) {
+    emitDelegatingCXXConstructorCall(cd, args);
+    return;
+  }
+
+  // If there are no member initializers, we can just return.
+  if (cd->getNumCtorInitializers() == 0)
+    return;
 
-  if (cd->getNumCtorInitializers() != 0) {
-    // There's much more to do here.
-    cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: any initializer");
+  const CXXRecordDecl *classDecl = cd->getParent();
+
+  // This code doesn't use range-based iteration because we may need to emit
+  // code between the virtual base initializers and the non-virtual base or
+  // between the non-virtual base initializers and the member initializers.
+  CXXConstructorDecl::init_const_iterator b = cd->init_begin(),
+                                          e = cd->init_end();
+
+  // Virtual base initializers first, if any. They aren't needed if:
+  // - This is a base ctor variant
+  // - There are no vbases
+  // - The class is abstract, so a complete object of it cannot be constructed
+  //
+  // The check for an abstract class is necessary because sema may not have
+  // marked virtual base destructors referenced.
+  bool constructVBases = ctorType != Ctor_Base &&
+                         classDecl->getNumVBases() != 0 &&
+                         !classDecl->isAbstract();
+  if (constructVBases) {
+    cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: virtual base");
     return;
   }
+
+  if ((*b)->isBaseInitializer()) {
+    cgm.errorNYI(cd->getSourceRange(),
+                 "emitCtorPrologue: non-virtual base initializer");
+    return;
+  }
+
+  if (classDecl->isDynamicClass()) {
+    cgm.errorNYI(cd->getSourceRange(),
+                 "emitCtorPrologue: initialize vtable pointers");
+    return;
+  }
+
+  // Finally, initialize class members.
+  FieldConstructionScope fcs(*this, loadCXXThisAddress());
+  // Classic codegen uses a special class to attempt to replace member
+  // initializers with memcpy. We could possibly defer that to the
+  // lowering or optimization phases to keep the memory accesses more
+  // explicit. For now, we don't insert memcpy at all.
+  assert(!cir::MissingFeatures::ctorMemcpyizer());
+  for (; b != e; b++) {
+    CXXCtorInitializer *member = (*b);
+    assert(!member->isBaseInitializer());
+    assert(member->isAnyMemberInitializer() &&
+           "Delegating initializer on non-delegating constructor");
+    emitMemberInitializer(*this, cd->getParent(), member, cd, args);
+  }
 }
 
 Address CIRGenFunction::loadCXXThisAddress() {
@@ -84,6 +200,34 @@ Address CIRGenFunction::loadCXXThisAddress() {
   return Address(loadCXXThis(), cxxThisAlignment);
 }
 
+void CIRGenFunction::emitInitializerForField(FieldDecl *field, LValue lhs,
+                                             Expr *init) {
+  QualType fieldType = field->getType();
+  switch (getEvaluationKind(fieldType)) {
+  case cir::TEK_Scalar:
+    if (lhs.isSimple()) {
+      emitExprAsInit(init, field, lhs, false);
+    } else {
+      cgm.errorNYI(field->getSourceRange(),
+                   "emitInitializerForField: non-simple scalar");
+    }
+    break;
+  case cir::TEK_Complex:
+    cgm.errorNYI(field->getSourceRange(), "emitInitializerForField: complex");
+    break;
+  case cir::TEK_Aggregate: {
+    cgm.errorNYI(field->getSourceRange(), "emitInitializerForField: 
aggregate");
+    break;
+  }
+  }
+
+  // Ensure that we destroy this object if an exception is thrown later in the
+  // constructor.
+  QualType::DestructionKind dtorKind = fieldType.isDestructedType();
+  (void)dtorKind;
+  assert(!cir::MissingFeatures::requiresCleanups());
+}
+
 void CIRGenFunction::emitDelegateCXXConstructorCall(
     const CXXConstructorDecl *ctor, CXXCtorType ctorType,
     const FunctionArgList &args, SourceLocation loc) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 4f2046ad26d72..95480dc8d4333 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -390,6 +390,34 @@ LValue CIRGenFunction::emitLValueForField(LValue base, 
const FieldDecl *field) {
   return lv;
 }
 
+LValue CIRGenFunction::emitLValueForFieldInitialization(
+    LValue base, const clang::FieldDecl *field, llvm::StringRef fieldName) {
+  QualType fieldType = field->getType();
+
+  if (!fieldType->isReferenceType())
+    return emitLValueForField(base, field);
+
+  const CIRGenRecordLayout &layout =
+      cgm.getTypes().getCIRGenRecordLayout(field->getParent());
+  unsigned fieldIndex = layout.getCIRFieldNo(field);
+
+  Address v =
+      emitAddrOfFieldStorage(base.getAddress(), field, fieldName, fieldIndex);
+
+  // Make sure that the address is pointing to the right type.
+  mlir::Type memTy = convertTypeForMem(fieldType);
+  v = builder.createElementBitCast(getLoc(field->getSourceRange()), v, memTy);
+
+  // TODO: Generate TBAA information that describes this access as a structure
+  // member access and not just an access to an object of the field's type. 
This
+  // should be similar to what we do in EmitLValueForField().
+  LValueBaseInfo baseInfo = base.getBaseInfo();
+  AlignmentSource fieldAlignSource = baseInfo.getAlignmentSource();
+  LValueBaseInfo fieldBaseInfo(getFieldAlignmentSource(fieldAlignSource));
+  assert(!cir::MissingFeatures::opTBAA());
+  return makeAddrLValue(v, fieldType, fieldBaseInfo);
+}
+
 mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) {
   // Bool has a different representation in memory than in registers,
   // but in ClangIR, it is simply represented as a cir.bool value.
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index fd413fe86383a..c029853929a58 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -550,6 +550,15 @@ LValue 
CIRGenFunction::makeNaturalAlignPointeeAddrLValue(mlir::Value val,
   return makeAddrLValue(Address(val, align), ty, baseInfo);
 }
 
+LValue CIRGenFunction::makeNaturalAlignAddrLValue(mlir::Value val,
+                                                  QualType ty) {
+  LValueBaseInfo baseInfo;
+  CharUnits alignment = cgm.getNaturalTypeAlignment(ty, &baseInfo);
+  Address addr(val, convertTypeForMem(ty), alignment);
+  assert(!cir::MissingFeatures::opTBAA());
+  return makeAddrLValue(addr, ty, baseInfo);
+}
+
 clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd,
                                                      FunctionArgList &args) {
   const auto *fd = cast<FunctionDecl>(gd.getDecl());
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 6c490a72b2e93..93b9b504feb38 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -68,6 +68,10 @@ class CIRGenFunction : public CIRGenTypeCache {
   mlir::Value cxxThisValue = nullptr;
   clang::CharUnits cxxThisAlignment;
 
+  /// The value of 'this' to sue when evaluating CXXDefaultInitExprs within 
this
+  /// expression.
+  Address cxxDefaultInitExprThis = Address::invalid();
+
   // Holds the Decl for the current outermost non-closure context
   const clang::Decl *curFuncDecl = nullptr;
 
@@ -490,7 +494,26 @@ class CIRGenFunction : public CIRGenTypeCache {
   static bool
   isConstructorDelegationValid(const clang::CXXConstructorDecl *ctor);
 
+  /// A scope within which we are constructing the fields of an object which
+  /// might use a CXXDefaultInitExpr. This stashes away a 'this' value to use 
if
+  /// we need to evaluate the CXXDefaultInitExpr within the evaluation.
+  class FieldConstructionScope {
+  public:
+    FieldConstructionScope(CIRGenFunction &cgf, Address thisAddr)
+        : cgf(cgf), oldCXXDefaultInitExprThis(cgf.cxxDefaultInitExprThis) {
+      cgf.cxxDefaultInitExprThis = thisAddr;
+    }
+    ~FieldConstructionScope() {
+      cgf.cxxDefaultInitExprThis = oldCXXDefaultInitExprThis;
+    }
+
+  private:
+    CIRGenFunction &cgf;
+    Address oldCXXDefaultInitExprThis;
+  };
+
   LValue makeNaturalAlignPointeeAddrLValue(mlir::Value v, clang::QualType t);
+  LValue makeNaturalAlignAddrLValue(mlir::Value val, QualType ty);
 
   /// Construct an address with the natural alignment of T. If a pointer to T
   /// is expected to be signed, the pointer passed to this function must have
@@ -844,6 +867,9 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   mlir::LogicalResult emitFunctionBody(const clang::Stmt *body);
 
+  void emitInitializerForField(clang::FieldDecl *field, LValue lhs,
+                               clang::Expr *init);
+
   mlir::Value emitPromotedScalarExpr(const Expr *e, QualType promotionType);
 
   /// Emit the computation of the specified expression of scalar type.
@@ -938,6 +964,13 @@ class CIRGenFunction : public CIRGenTypeCache {
   LValue emitLValue(const clang::Expr *e);
   LValue emitLValueForField(LValue base, const clang::FieldDecl *field);
 
+  /// Like emitLValueForField, excpet that if the Field is a reference, this
+  /// will return the address of the reference and not the address of the value
+  /// stored in the reference.
+  LValue emitLValueForFieldInitialization(LValue base,
+                                          const clang::FieldDecl *field,
+                                          llvm::StringRef fieldName);
+
   LValue emitMemberExpr(const MemberExpr *e);
 
   /// Given an expression with a pointer type, emit the value and compute our
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 434dd376208e1..68ab81ed53af9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -68,6 +68,7 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
 
   // Initialize cached types
   VoidTy = cir::VoidType::get(&getMLIRContext());
+  VoidPtrTy = cir::PointerType::get(VoidTy);
   SInt8Ty = cir::IntType::get(&getMLIRContext(), 8, /*isSigned=*/true);
   SInt16Ty = cir::IntType::get(&getMLIRContext(), 16, /*isSigned=*/true);
   SInt32Ty = cir::IntType::get(&getMLIRContext(), 32, /*isSigned=*/true);
@@ -94,6 +95,9 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
   // TODO(CIR): Should be updated once TypeSizeInfoAttr is upstreamed
   const unsigned sizeTypeSize =
       astContext.getTypeSize(astContext.getSignedSizeType());
+  // In CIRGenTypeCache, UIntPtrTy and SizeType are fields of the same union
+  UIntPtrTy =
+      cir::IntType::get(&getMLIRContext(), sizeTypeSize, /*isSigned=*/false);
   PtrDiffTy =
       cir::IntType::get(&getMLIRContext(), sizeTypeSize, /*isSigned=*/true);
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h 
b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h
index a5b7f0c9579b4..12dbc3297a072 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h
@@ -22,7 +22,7 @@ namespace clang::CIRGen {
 /// during IR emission. It's initialized once in CodeGenModule's
 /// constructor and then copied around into new CIRGenFunction's.
 struct CIRGenTypeCache {
-  CIRGenTypeCache() = default;
+  CIRGenTypeCache() {}
 
   // ClangIR void type
   cir::VoidType VoidTy;
@@ -49,8 +49,17 @@ struct CIRGenTypeCache {
   cir::FP80Type FP80Ty;
   cir::FP128Type FP128Ty;
 
+  /// intptr_t, size_t, and ptrdiff_t, which we assume are the same size.
+  union {
+    mlir::Type UIntPtrTy;
+    mlir::Type SizeTy;
+  };
+
   mlir::Type PtrDiffTy;
 
+  /// void* in address space 0
+  cir::PointerType VoidPtrTy;
+
   /// The size and alignment of a pointer into the generic address space.
   union {
     unsigned char PointerAlignInBytes;
diff --git a/clang/test/CIR/CodeGen/ctor.cpp b/clang/test/CIR/CodeGen/ctor.cpp
index 1a36eb0d9d3a6..0b009442b2f87 100644
--- a/clang/test/CIR/CodeGen/ctor.cpp
+++ b/clang/test/CIR/CodeGen/ctor.cpp
@@ -113,3 +113,109 @@ void bam() {
 // CHECK-NEXT:    %[[S_ADDR:.*]] = cir.alloca {{.*}} ["s", init]
 // CHECK-NEXT:    cir.call @_ZN15DelegatingStrukC1Ev(%[[S_ADDR]])
 // CHECK-NEXT:    cir.return
+
+struct MemberInitStruk {
+  int a;
+  MemberInitStruk() : a(0) {}
+};
+
+void init_member() {
+  MemberInitStruk s;
+}
+
+// CHECK:      cir.func @_ZN15MemberInitStrukC2Ev(%arg0: 
!cir.ptr<!rec_MemberInitStruk>
+// CHECK-NEXT:   %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init]
+// CHECK-NEXT:   cir.store %arg0, %[[THIS_ADDR]]
+// CHECK-NEXT:   %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
+// CHECK-NEXT:   %[[A_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "a"}
+// CHECK-NEXT:   %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CHECK-NEXT:   cir.store align(4) %[[ZERO]], %[[A_ADDR]]
+// CHECK-NEXT:   cir.return
+
+// CHECK:      cir.func @_ZN15MemberInitStrukC1Ev(%arg0: 
!cir.ptr<!rec_MemberInitStruk>
+// CHECK-NEXT:   %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init]
+// CHECK-NEXT:   cir.store %arg0, %[[THIS_ADDR]]
+// CHECK-NEXT:   %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
+// CHECK-NEXT:   cir.call @_ZN15MemberInitStrukC2Ev(%[[THIS]])
+// CHECK-NEXT:   cir.return
+
+// CHECK: cir.func @_Z11init_memberv
+// CHECK-NEXT:    %[[S_ADDR:.*]] = cir.alloca {{.*}} ["s", init]
+// CHECK-NEXT:    cir.call @_ZN15MemberInitStrukC1Ev(%[[S_ADDR]])
+// CHECK-NEXT:    cir.return
+
+struct ParamMemberInitStruk {
+  int a;
+  ParamMemberInitStruk(int n) : a(n) {}
+};
+
+void init_param_member() {
+  ParamMemberInitStruk s(0);
+}
+
+// CHECK:      cir.func @_ZN20ParamMemberInitStrukC2Ei(%arg0: 
!cir.ptr<!rec_ParamMemberInitStruk>
+// CHECK-SAME:                                         %arg1: !s32i
+// CHECK-NEXT:   %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init]
+// CHECK-NEXT:   %[[N_ADDR:.*]] = cir.alloca {{.*}} ["n", init]
+// CHECK-NEXT:   cir.store %arg0, %[[THIS_ADDR]]
+// CHECK-NEXT:   cir.store %arg1, %[[N_ADDR]]
+// CHECK-NEXT:   %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
+// CHECK-NEXT:   %[[A_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "a"}
+// CHECK-NEXT:   %[[N:.*]] = cir.load{{.*}} %[[N_ADDR]]
+// CHECK-NEXT:   cir.store{{.*}} %[[N]], %[[A_ADDR]]
+// CHECK-NEXT:   cir.return
+
+// CHECK:      cir.func @_ZN20ParamMemberInitStrukC1Ei(%arg0: 
!cir.ptr<!rec_ParamMemberInitStruk>
+// CHECK-SAME:                                         %arg1: !s32i
+// CHECK-NEXT:   %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init]
+// CHECK-NEXT:   %[[N_ADDR:.*]] = cir.alloca {{.*}} ["n", init]
+// CHECK-NEXT:   cir.store %arg0, %[[THIS_ADDR]]
+// CHECK-NEXT:   cir.store %arg1, %[[N_ADDR]]
+// CHECK-NEXT:   %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
+// CHECK-NEXT:   %[[N:.*]] = cir.load{{.*}} %[[N_ADDR]]
+// CHECK-NEXT:   cir.call @_ZN20ParamMemberInitStrukC2Ei(%[[THIS]], %[[N]])
+// CHECK-NEXT:   cir.return
+
+// CHECK: cir.func @_Z17init_param_memberv
+// CHECK-NEXT:    %[[S_ADDR:.*]] = cir.alloca {{.*}} ["s", init]
+// CHECK-NEXT:    %[[ZERO:.*]] = cir.const #cir.int<0>
+// CHECK-NEXT:    cir.call @_ZN20ParamMemberInitStrukC1Ei(%[[S_ADDR]], 
%[[ZERO]])
+// CHECK-NEXT:    cir.return
+
+struct UnionInitStruk {
+  union {
+    int a;
+    union {
+        float b;
+        double c;
+    };
+  };
+  UnionInitStruk() : c(0.0) {}
+};
+
+void init_union() {
+  UnionInitStruk s;
+}
+
+// CHECK:      cir.func @_ZN14UnionInitStrukC2Ev(%arg0: 
!cir.ptr<!rec_UnionInitStruk>
+// CHECK-NEXT:   %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init]
+// CHECK-NEXT:   cir.store %arg0, %[[THIS_ADDR]]
+// CHECK-NEXT:   %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
+// CHECK-NEXT:   %[[AU1_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = ""}
+// CHECK-NEXT:   %[[AU2_ADDR:.*]] = cir.get_member %[[AU1_ADDR]][1] {name = ""}
+// CHECK-NEXT:   %[[C_ADDR:.*]] = cir.get_member %[[AU2_ADDR]][1] {name = "c"}
+// CHECK-NEXT:   %[[ZERO:.*]] = cir.const #cir.fp<0.000000e+00>
+// CHECK-NEXT:   cir.store{{.*}} %[[ZERO]], %[[C_ADDR]]
+// CHECK-NEXT:   cir.return
+
+// CHECK:      cir.func @_ZN14UnionInitStrukC1Ev(%arg0: 
!cir.ptr<!rec_UnionInitStruk>
+// CHECK-NEXT:   %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init]
+// CHECK-NEXT:   cir.store %arg0, %[[THIS_ADDR]]
+// CHECK-NEXT:   %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
+// CHECK-NEXT:   cir.call @_ZN14UnionInitStrukC2Ev
+// CHECK-NEXT:   cir.return
+
+// CHECK: cir.func @_Z10init_unionv
+// CHECK-NEXT:    %[[S_ADDR:.*]] = cir.alloca {{.*}} ["s", init]
+// CHECK-NEXT:    cir.call @_ZN14UnionInitStrukC1Ev(%[[S_ADDR]])
+// CHECK-NEXT:    cir.return

>From 34d7139bd1462cb87aeae5e8176a5b2c0cf71f91 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akay...@nvidia.com>
Date: Wed, 18 Jun 2025 16:49:00 -0700
Subject: [PATCH 2/3] Address review feedback

---
 clang/lib/CIR/CodeGen/CIRGenClass.cpp | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp 
b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 25f3e19befa48..0184f5abdd9eb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -86,19 +86,17 @@ static void emitMemberInitializer(CIRGenFunction &cgf,
 
   mlir::Value thisPtr = cgf.loadCXXThis();
   QualType recordTy = cgf.getContext().getTypeDeclType(classDecl);
-  LValue lhs;
 
   // If a base constructor is being emitted, create an LValue that has the
   // non-virtual alignment.
-  if (cgf.curGD.getCtorType() == Ctor_Base)
-    lhs = cgf.makeNaturalAlignPointeeAddrLValue(thisPtr, recordTy);
-  else
-    lhs = cgf.makeNaturalAlignAddrLValue(thisPtr, recordTy);
+  LValue lhs = (cgf.curGD.getCtorType() == Ctor_Base) ?
+                  cgf.makeNaturalAlignPointeeAddrLValue(thisPtr, recordTy) :
+                  cgf.makeNaturalAlignAddrLValue(thisPtr, recordTy);
 
   emitLValueForAnyFieldInitialization(cgf, memberInit, lhs);
 
   // Special case: If we are in a copy or move constructor, and we are copying
-  // an array off PODs or classes with tirival copy constructors, ignore the 
AST
+  // an array off PODs or classes with trivial copy constructors, ignore the 
AST
   // and perform the copy we know is equivalent.
   // FIXME: This is hacky at best... if we had a bit more explicit information
   // in the AST, we could generalize it more easily.
@@ -205,12 +203,11 @@ void CIRGenFunction::emitInitializerForField(FieldDecl 
*field, LValue lhs,
   QualType fieldType = field->getType();
   switch (getEvaluationKind(fieldType)) {
   case cir::TEK_Scalar:
-    if (lhs.isSimple()) {
+    if (lhs.isSimple())
       emitExprAsInit(init, field, lhs, false);
-    } else {
+    else
       cgm.errorNYI(field->getSourceRange(),
                    "emitInitializerForField: non-simple scalar");
-    }
     break;
   case cir::TEK_Complex:
     cgm.errorNYI(field->getSourceRange(), "emitInitializerForField: complex");

>From 358fe916d79b37a69de7bdff0821e8c714aa6d54 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akay...@nvidia.com>
Date: Wed, 18 Jun 2025 16:59:30 -0700
Subject: [PATCH 3/3] Fix formatting

---
 clang/lib/CIR/CodeGen/CIRGenClass.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp 
b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 0184f5abdd9eb..278cc8931f308 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -89,9 +89,9 @@ static void emitMemberInitializer(CIRGenFunction &cgf,
 
   // If a base constructor is being emitted, create an LValue that has the
   // non-virtual alignment.
-  LValue lhs = (cgf.curGD.getCtorType() == Ctor_Base) ?
-                  cgf.makeNaturalAlignPointeeAddrLValue(thisPtr, recordTy) :
-                  cgf.makeNaturalAlignAddrLValue(thisPtr, recordTy);
+  LValue lhs = (cgf.curGD.getCtorType() == Ctor_Base)
+                   ? cgf.makeNaturalAlignPointeeAddrLValue(thisPtr, recordTy)
+                   : cgf.makeNaturalAlignAddrLValue(thisPtr, recordTy);
 
   emitLValueForAnyFieldInitialization(cgf, memberInit, lhs);
 

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to