[clang] 418b409 - [CIR] Add support for member initialization from constructors (#144583)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 24 10:05:51 PDT 2025
Author: Andy Kaylor
Date: 2025-06-24T10:05:48-07:00
New Revision: 418b409df8538bdf1b4865556607c220dc139fa8
URL: https://github.com/llvm/llvm-project/commit/418b409df8538bdf1b4865556607c220dc139fa8
DIFF: https://github.com/llvm/llvm-project/commit/418b409df8538bdf1b4865556607c220dc139fa8.diff
LOG: [CIR] Add support for member initialization from constructors (#144583)
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.
Added:
Modified:
clang/include/clang/CIR/MissingFeatures.h
clang/lib/CIR/CodeGen/CIRGenClass.cpp
clang/lib/CIR/CodeGen/CIRGenExpr.cpp
clang/lib/CIR/CodeGen/CIRGenFunction.cpp
clang/lib/CIR/CodeGen/CIRGenFunction.h
clang/lib/CIR/CodeGen/CIRGenModule.cpp
clang/lib/CIR/CodeGen/CIRGenTypeCache.h
clang/test/CIR/CodeGen/ctor.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 3ba363772316c..bf370a9850d75 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -242,6 +242,7 @@ struct MissingFeatures {
static bool builtinCallMathErrno() { return false; }
static bool nonFineGrainedBitfields() { return false; }
static bool armComputeVolatileBitfields() { 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..278cc8931f308 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -53,19 +53,133 @@ 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);
+
+ // 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);
+
+ 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 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.
+ 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 +198,33 @@ 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 c31754dc11d69..5c6604d784156 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
diff erent 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 82aa7ec9cb220..2e54243f18cff 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.
@@ -940,6 +966,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 ptr
diff _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
More information about the cfe-commits
mailing list