-
Notifications
You must be signed in to change notification settings - Fork 14k
[CIR] Add support for member initialization from constructors #144583
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
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.
@llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) ChangesUpstream the code to handle member variable initialization in a constructor. At this point only simple scalar values (including members of anonymous unions) are handled. Patch is 20.13 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/144583.diff 8 Files Affected:
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 3dc28e6f2e5bf..9659d94ef6a8f 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -237,6 +237,7 @@ struct MissingFeatures {
static bool lowerAggregateLoadStore() { return false; }
static bool dataLayoutTypeAllocSize() { return false; }
static bool asmLabelAttr() { 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 de6ef2a69faf1..c5d8a59b47e5c 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
@@ -840,6 +863,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.
@@ -934,6 +960,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_ADD...
[truncated]
|
@llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesUpstream the code to handle member variable initialization in a constructor. At this point only simple scalar values (including members of anonymous unions) are handled. Patch is 20.13 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/144583.diff 8 Files Affected:
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 3dc28e6f2e5bf..9659d94ef6a8f 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -237,6 +237,7 @@ struct MissingFeatures {
static bool lowerAggregateLoadStore() { return false; }
static bool dataLayoutTypeAllocSize() { return false; }
static bool asmLabelAttr() { 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 de6ef2a69faf1..c5d8a59b47e5c 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
@@ -840,6 +863,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.
@@ -934,6 +960,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_ADD...
[truncated]
|
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.