-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[CIR] Add support for delegating constructor initialization #156757
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
Conversation
This adds support for zero-initialization during delegating constructor processing. Note, this also adds code to skip emitting constructors that are trivial and default to match the classic codegen behavior. The incubator does not skip these constructors, but I have found a case where this results in a call to a default constructor that is never defined.
@llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) ChangesThis adds support for zero-initialization during delegating constructor processing. Note, this also adds code to skip emitting constructors that are trivial and default to match the classic codegen behavior. The incubator does not skip these constructors, but I have found a case where this results in a call to a default constructor that is never defined. Full diff: https://github.com/llvm/llvm-project/pull/156757.diff 5 Files Affected:
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index aec60d01fc238..d8c7903a4888d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1966,15 +1966,23 @@ void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
// constructor, emit the zero initialization now, unless destination is
// already zeroed.
if (e->requiresZeroInitialization() && !dest.isZeroed()) {
- cgm.errorNYI(e->getSourceRange(),
- "emitCXXConstructExpr: requires initialization");
- return;
+ switch (e->getConstructionKind()) {
+ case CXXConstructionKind::Delegating:
+ case CXXConstructionKind::Complete:
+ emitNullInitialization(getLoc(e->getSourceRange()), dest.getAddress(),
+ e->getType());
+ break;
+ case CXXConstructionKind::VirtualBase:
+ case CXXConstructionKind::NonVirtualBase:
+ cgm.errorNYI(e->getSourceRange(),
+ "emitCXXConstructExpr: base requires initialization");
+ break;
+ }
}
- // If this is a call to a trivial default constructor:
- // In LLVM: do nothing.
- // In CIR: emit as a regular call, other later passes should lower the
- // ctor call into trivial initialization.
+ // If this is a call to a trivial default constructor, do nothing.
+ if (cd->isTrivial() && cd->isDefaultConstructor())
+ return;
// Elide the constructor if we're constructing from a temporary
if (getLangOpts().ElideConstructors && e->isElidable()) {
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 3b76c0981fe80..ee9f58c829ca9 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1368,6 +1368,15 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
rewriter.replaceOp(op, lowerCirAttrAsValue(op, op.getValue(), rewriter,
getTypeConverter()));
return mlir::success();
+ } else if (auto recTy = mlir::dyn_cast<cir::RecordType>(op.getType())) {
+ if (mlir::isa<cir::ZeroAttr, cir::UndefAttr>(attr)) {
+ mlir::Value initVal =
+ lowerCirAttrAsValue(op, attr, rewriter, typeConverter);
+ rewriter.replaceOp(op, initVal);
+ return mlir::success();
+ }
+ return op.emitError() << "unsupported lowering for record constant type "
+ << op.getType();
} else if (auto complexTy = mlir::dyn_cast<cir::ComplexType>(op.getType())) {
mlir::Type complexElemTy = complexTy.getElementType();
mlir::Type complexElemLLVMTy = typeConverter->convertType(complexElemTy);
diff --git a/clang/test/CIR/CodeGen/delegating-ctor.cpp b/clang/test/CIR/CodeGen/delegating-ctor.cpp
new file mode 100644
index 0000000000000..a9cfc5d02173d
--- /dev/null
+++ b/clang/test/CIR/CodeGen/delegating-ctor.cpp
@@ -0,0 +1,72 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+struct Delegating {
+ Delegating();
+ Delegating(int);
+};
+
+// Check that the constructor being delegated to is called with the correct
+// arguments.
+Delegating::Delegating() : Delegating(0) {}
+
+// CIR: cir.func {{.*}} @_ZN10DelegatingC2Ev(%[[THIS_ARG:.*]]: !cir.ptr<!rec_Delegating> {{.*}})
+// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_Delegating>, !cir.ptr<!cir.ptr<!rec_Delegating>>, ["this", init]
+// CIR: cir.store{{.*}} %[[THIS_ARG]], %[[THIS_ADDR]]
+// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
+// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CIR: cir.call @_ZN10DelegatingC2Ei(%[[THIS]], %[[ZERO]]) : (!cir.ptr<!rec_Delegating>, !s32i) -> ()
+
+// LLVM: define {{.*}} @_ZN10DelegatingC2Ev(ptr %[[THIS_ARG:.*]])
+// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
+// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// LLVM: call void @_ZN10DelegatingC2Ei(ptr %[[THIS]], i32 0)
+
+// OGCG: define {{.*}} @_ZN10DelegatingC2Ev(ptr {{.*}} %[[THIS_ARG:.*]])
+// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
+// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// OGCG: call void @_ZN10DelegatingC2Ei(ptr {{.*}} %[[THIS]], i32 {{.*}} 0)
+
+struct DelegatingWithZeroing {
+ int i;
+ DelegatingWithZeroing() = default;
+ DelegatingWithZeroing(int);
+};
+
+// Check that the delegating constructor performs zero-initialization here.
+// FIXME: we should either emit the trivial default constructor or remove the
+// call to it in a lowering pass.
+DelegatingWithZeroing::DelegatingWithZeroing(int) : DelegatingWithZeroing() {}
+
+// CIR: cir.func {{.*}} @_ZN21DelegatingWithZeroingC2Ei(%[[THIS_ARG:.*]]: !cir.ptr<!rec_DelegatingWithZeroing> {{.*}}, %[[I_ARG:.*]]: !s32i {{.*}})
+// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_DelegatingWithZeroing>, !cir.ptr<!cir.ptr<!rec_DelegatingWithZeroing>>, ["this", init]
+// CIR: %[[I_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["", init]
+// CIR: cir.store{{.*}} %[[THIS_ARG]], %[[THIS_ADDR]]
+// CIR: cir.store{{.*}} %[[I_ARG]], %[[I_ADDR]]
+// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
+// CIR: %[[ZERO:.*]] = cir.const #cir.zero : !rec_DelegatingWithZeroing
+// CIR: cir.store{{.*}} %[[ZERO]], %[[THIS]] : !rec_DelegatingWithZeroing, !cir.ptr<!rec_DelegatingWithZeroing>
+
+// LLVM: define {{.*}} void @_ZN21DelegatingWithZeroingC2Ei(ptr %[[THIS_ARG:.*]], i32 %[[I_ARG:.*]])
+// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
+// LLVM: %[[I_ADDR:.*]] = alloca i32
+// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// LLVM: store i32 %[[I_ARG]], ptr %[[I_ADDR]]
+// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// LLVM: store %struct.DelegatingWithZeroing zeroinitializer, ptr %[[THIS]]
+
+// Note: OGCG elides the call to the default constructor.
+
+// OGCG: define {{.*}} void @_ZN21DelegatingWithZeroingC2Ei(ptr {{.*}} %[[THIS_ARG:.*]], i32 {{.*}} %[[I_ARG:.*]])
+// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
+// OGCG: %[[I_ADDR:.*]] = alloca i32
+// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// OGCG: store i32 %[[I_ARG]], ptr %[[I_ADDR]]
+// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// OGCG: call void @llvm.memset.p0.i64(ptr align 4 %[[THIS]], i8 0, i64 4, i1 false)
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
index 4f88addc6116c..31adb9bf4859b 100644
--- a/clang/test/CIR/CodeGen/new.cpp
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -23,7 +23,6 @@ void test_basic_new() {
// CHECK: %[[EIGHT:.*]] = cir.const #cir.int<8>
// CHECK: %[[NEW_S:.*]] = cir.call @_Znwm(%[[EIGHT]])
// CHECK: %[[NEW_S_PTR:.*]] = cir.cast(bitcast, %[[NEW_S]]
-// CHECK: cir.call @_ZN1SC1Ev(%[[NEW_S_PTR]])
// CHECK: cir.store{{.*}} %[[NEW_S_PTR]], %[[PS_ADDR]]
// CHECK: %[[FOUR:.*]] = cir.const #cir.int<4>
// CHECK: %[[NEW_INT:.*]] = cir.call @_Znwm(%[[FOUR]])
@@ -40,7 +39,6 @@ void test_basic_new() {
// LLVM: %[[PN_ADDR:.*]] = alloca ptr, i64 1, align 8
// LLVM: %[[PD_ADDR:.*]] = alloca ptr, i64 1, align 8
// LLVM: %[[NEW_S:.*]] = call{{.*}} ptr @_Znwm(i64 8)
-// LLVM: call{{.*}} void @_ZN1SC1Ev(ptr %[[NEW_S]])
// LLVM: store ptr %[[NEW_S]], ptr %[[PS_ADDR]], align 8
// LLVM: %[[NEW_INT:.*]] = call{{.*}} ptr @_Znwm(i64 4)
// LLVM: store ptr %[[NEW_INT]], ptr %[[PN_ADDR]], align 8
@@ -48,8 +46,6 @@ void test_basic_new() {
// LLVM: store ptr %[[NEW_DOUBLE]], ptr %[[PD_ADDR]], align 8
// LLVM: ret void
-// NOTE: OGCG elides the constructor call here, but CIR does not.
-
// OGCG: define{{.*}} void @_Z14test_basic_newv
// OGCG: %[[PS_ADDR:.*]] = alloca ptr, align 8
// OGCG: %[[PN_ADDR:.*]] = alloca ptr, align 8
diff --git a/clang/test/CIR/CodeGen/vbase.cpp b/clang/test/CIR/CodeGen/vbase.cpp
index 1d1b5e083bfc9..ee6471c944a42 100644
--- a/clang/test/CIR/CodeGen/vbase.cpp
+++ b/clang/test/CIR/CodeGen/vbase.cpp
@@ -24,28 +24,12 @@ void ppp() { B b; }
// OGCG: @_ZTV1B = linkonce_odr unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr inttoptr (i64 12 to ptr), ptr null, ptr @_ZTI1B] }, comdat, align 8
-// Constructor for A
-// CIR: cir.func comdat linkonce_odr @_ZN1AC2Ev(%arg0: !cir.ptr<!rec_A>
-// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_A>, !cir.ptr<!cir.ptr<!rec_A>>, ["this", init]
-// CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr<!rec_A>, !cir.ptr<!cir.ptr<!rec_A>>
-// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_A>>, !cir.ptr<!rec_A>
-// CIR: cir.return
-
-// LLVM: define{{.*}} void @_ZN1AC2Ev(ptr %[[THIS_ARG:.*]]) {
-// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
-// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
-// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
-// LLVM: ret void
-
-// Note: OGCG elides the constructor for A. This is not yet implemented in CIR.
-
// Constructor for B
// CIR: cir.func comdat linkonce_odr @_ZN1BC1Ev(%arg0: !cir.ptr<!rec_B>
// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_B>, !cir.ptr<!cir.ptr<!rec_B>>, ["this", init]
// CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr<!rec_B>, !cir.ptr<!cir.ptr<!rec_B>>
// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_B>>, !cir.ptr<!rec_B>
// CIR: %[[BASE_A_ADDR:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_B> nonnull [12] -> !cir.ptr<!rec_A>
-// CIR: cir.call @_ZN1AC2Ev(%[[BASE_A_ADDR]]) nothrow : (!cir.ptr<!rec_A>) -> ()
// CIR: %[[VTABLE:.*]] = cir.vtable.address_point(@_ZTV1B, address_point = <index = 0, offset = 3>) : !cir.vptr
// CIR: %[[B_VPTR:.*]] = cir.vtable.get_vptr %[[THIS]] : !cir.ptr<!rec_B> -> !cir.ptr<!cir.vptr>
// CIR: cir.store align(8) %[[VTABLE]], %[[B_VPTR]] : !cir.vptr, !cir.ptr<!cir.vptr>
@@ -56,7 +40,6 @@ void ppp() { B b; }
// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
// LLVM: %[[BASE_A_ADDR:.*]] = getelementptr i8, ptr %[[THIS]], i32 12
-// LLVM: call void @_ZN1AC2Ev(ptr %[[BASE_A_ADDR]])
// LLVM: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1B, i64 24), ptr %[[THIS]]
// LLVM: ret void
@@ -67,4 +50,3 @@ void ppp() { B b; }
// OGCG: %[[BASE_A_ADDR:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i64 12
// OGCG: store ptr getelementptr inbounds inrange(-24, 0) ({ [3 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 3), ptr %[[THIS]]
// OGCG: ret void
-
|
@llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesThis adds support for zero-initialization during delegating constructor processing. Note, this also adds code to skip emitting constructors that are trivial and default to match the classic codegen behavior. The incubator does not skip these constructors, but I have found a case where this results in a call to a default constructor that is never defined. Full diff: https://github.com/llvm/llvm-project/pull/156757.diff 5 Files Affected:
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index aec60d01fc238..d8c7903a4888d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1966,15 +1966,23 @@ void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
// constructor, emit the zero initialization now, unless destination is
// already zeroed.
if (e->requiresZeroInitialization() && !dest.isZeroed()) {
- cgm.errorNYI(e->getSourceRange(),
- "emitCXXConstructExpr: requires initialization");
- return;
+ switch (e->getConstructionKind()) {
+ case CXXConstructionKind::Delegating:
+ case CXXConstructionKind::Complete:
+ emitNullInitialization(getLoc(e->getSourceRange()), dest.getAddress(),
+ e->getType());
+ break;
+ case CXXConstructionKind::VirtualBase:
+ case CXXConstructionKind::NonVirtualBase:
+ cgm.errorNYI(e->getSourceRange(),
+ "emitCXXConstructExpr: base requires initialization");
+ break;
+ }
}
- // If this is a call to a trivial default constructor:
- // In LLVM: do nothing.
- // In CIR: emit as a regular call, other later passes should lower the
- // ctor call into trivial initialization.
+ // If this is a call to a trivial default constructor, do nothing.
+ if (cd->isTrivial() && cd->isDefaultConstructor())
+ return;
// Elide the constructor if we're constructing from a temporary
if (getLangOpts().ElideConstructors && e->isElidable()) {
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 3b76c0981fe80..ee9f58c829ca9 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1368,6 +1368,15 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
rewriter.replaceOp(op, lowerCirAttrAsValue(op, op.getValue(), rewriter,
getTypeConverter()));
return mlir::success();
+ } else if (auto recTy = mlir::dyn_cast<cir::RecordType>(op.getType())) {
+ if (mlir::isa<cir::ZeroAttr, cir::UndefAttr>(attr)) {
+ mlir::Value initVal =
+ lowerCirAttrAsValue(op, attr, rewriter, typeConverter);
+ rewriter.replaceOp(op, initVal);
+ return mlir::success();
+ }
+ return op.emitError() << "unsupported lowering for record constant type "
+ << op.getType();
} else if (auto complexTy = mlir::dyn_cast<cir::ComplexType>(op.getType())) {
mlir::Type complexElemTy = complexTy.getElementType();
mlir::Type complexElemLLVMTy = typeConverter->convertType(complexElemTy);
diff --git a/clang/test/CIR/CodeGen/delegating-ctor.cpp b/clang/test/CIR/CodeGen/delegating-ctor.cpp
new file mode 100644
index 0000000000000..a9cfc5d02173d
--- /dev/null
+++ b/clang/test/CIR/CodeGen/delegating-ctor.cpp
@@ -0,0 +1,72 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+struct Delegating {
+ Delegating();
+ Delegating(int);
+};
+
+// Check that the constructor being delegated to is called with the correct
+// arguments.
+Delegating::Delegating() : Delegating(0) {}
+
+// CIR: cir.func {{.*}} @_ZN10DelegatingC2Ev(%[[THIS_ARG:.*]]: !cir.ptr<!rec_Delegating> {{.*}})
+// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_Delegating>, !cir.ptr<!cir.ptr<!rec_Delegating>>, ["this", init]
+// CIR: cir.store{{.*}} %[[THIS_ARG]], %[[THIS_ADDR]]
+// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
+// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CIR: cir.call @_ZN10DelegatingC2Ei(%[[THIS]], %[[ZERO]]) : (!cir.ptr<!rec_Delegating>, !s32i) -> ()
+
+// LLVM: define {{.*}} @_ZN10DelegatingC2Ev(ptr %[[THIS_ARG:.*]])
+// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
+// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// LLVM: call void @_ZN10DelegatingC2Ei(ptr %[[THIS]], i32 0)
+
+// OGCG: define {{.*}} @_ZN10DelegatingC2Ev(ptr {{.*}} %[[THIS_ARG:.*]])
+// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
+// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// OGCG: call void @_ZN10DelegatingC2Ei(ptr {{.*}} %[[THIS]], i32 {{.*}} 0)
+
+struct DelegatingWithZeroing {
+ int i;
+ DelegatingWithZeroing() = default;
+ DelegatingWithZeroing(int);
+};
+
+// Check that the delegating constructor performs zero-initialization here.
+// FIXME: we should either emit the trivial default constructor or remove the
+// call to it in a lowering pass.
+DelegatingWithZeroing::DelegatingWithZeroing(int) : DelegatingWithZeroing() {}
+
+// CIR: cir.func {{.*}} @_ZN21DelegatingWithZeroingC2Ei(%[[THIS_ARG:.*]]: !cir.ptr<!rec_DelegatingWithZeroing> {{.*}}, %[[I_ARG:.*]]: !s32i {{.*}})
+// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_DelegatingWithZeroing>, !cir.ptr<!cir.ptr<!rec_DelegatingWithZeroing>>, ["this", init]
+// CIR: %[[I_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["", init]
+// CIR: cir.store{{.*}} %[[THIS_ARG]], %[[THIS_ADDR]]
+// CIR: cir.store{{.*}} %[[I_ARG]], %[[I_ADDR]]
+// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
+// CIR: %[[ZERO:.*]] = cir.const #cir.zero : !rec_DelegatingWithZeroing
+// CIR: cir.store{{.*}} %[[ZERO]], %[[THIS]] : !rec_DelegatingWithZeroing, !cir.ptr<!rec_DelegatingWithZeroing>
+
+// LLVM: define {{.*}} void @_ZN21DelegatingWithZeroingC2Ei(ptr %[[THIS_ARG:.*]], i32 %[[I_ARG:.*]])
+// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
+// LLVM: %[[I_ADDR:.*]] = alloca i32
+// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// LLVM: store i32 %[[I_ARG]], ptr %[[I_ADDR]]
+// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// LLVM: store %struct.DelegatingWithZeroing zeroinitializer, ptr %[[THIS]]
+
+// Note: OGCG elides the call to the default constructor.
+
+// OGCG: define {{.*}} void @_ZN21DelegatingWithZeroingC2Ei(ptr {{.*}} %[[THIS_ARG:.*]], i32 {{.*}} %[[I_ARG:.*]])
+// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
+// OGCG: %[[I_ADDR:.*]] = alloca i32
+// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// OGCG: store i32 %[[I_ARG]], ptr %[[I_ADDR]]
+// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// OGCG: call void @llvm.memset.p0.i64(ptr align 4 %[[THIS]], i8 0, i64 4, i1 false)
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
index 4f88addc6116c..31adb9bf4859b 100644
--- a/clang/test/CIR/CodeGen/new.cpp
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -23,7 +23,6 @@ void test_basic_new() {
// CHECK: %[[EIGHT:.*]] = cir.const #cir.int<8>
// CHECK: %[[NEW_S:.*]] = cir.call @_Znwm(%[[EIGHT]])
// CHECK: %[[NEW_S_PTR:.*]] = cir.cast(bitcast, %[[NEW_S]]
-// CHECK: cir.call @_ZN1SC1Ev(%[[NEW_S_PTR]])
// CHECK: cir.store{{.*}} %[[NEW_S_PTR]], %[[PS_ADDR]]
// CHECK: %[[FOUR:.*]] = cir.const #cir.int<4>
// CHECK: %[[NEW_INT:.*]] = cir.call @_Znwm(%[[FOUR]])
@@ -40,7 +39,6 @@ void test_basic_new() {
// LLVM: %[[PN_ADDR:.*]] = alloca ptr, i64 1, align 8
// LLVM: %[[PD_ADDR:.*]] = alloca ptr, i64 1, align 8
// LLVM: %[[NEW_S:.*]] = call{{.*}} ptr @_Znwm(i64 8)
-// LLVM: call{{.*}} void @_ZN1SC1Ev(ptr %[[NEW_S]])
// LLVM: store ptr %[[NEW_S]], ptr %[[PS_ADDR]], align 8
// LLVM: %[[NEW_INT:.*]] = call{{.*}} ptr @_Znwm(i64 4)
// LLVM: store ptr %[[NEW_INT]], ptr %[[PN_ADDR]], align 8
@@ -48,8 +46,6 @@ void test_basic_new() {
// LLVM: store ptr %[[NEW_DOUBLE]], ptr %[[PD_ADDR]], align 8
// LLVM: ret void
-// NOTE: OGCG elides the constructor call here, but CIR does not.
-
// OGCG: define{{.*}} void @_Z14test_basic_newv
// OGCG: %[[PS_ADDR:.*]] = alloca ptr, align 8
// OGCG: %[[PN_ADDR:.*]] = alloca ptr, align 8
diff --git a/clang/test/CIR/CodeGen/vbase.cpp b/clang/test/CIR/CodeGen/vbase.cpp
index 1d1b5e083bfc9..ee6471c944a42 100644
--- a/clang/test/CIR/CodeGen/vbase.cpp
+++ b/clang/test/CIR/CodeGen/vbase.cpp
@@ -24,28 +24,12 @@ void ppp() { B b; }
// OGCG: @_ZTV1B = linkonce_odr unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr inttoptr (i64 12 to ptr), ptr null, ptr @_ZTI1B] }, comdat, align 8
-// Constructor for A
-// CIR: cir.func comdat linkonce_odr @_ZN1AC2Ev(%arg0: !cir.ptr<!rec_A>
-// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_A>, !cir.ptr<!cir.ptr<!rec_A>>, ["this", init]
-// CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr<!rec_A>, !cir.ptr<!cir.ptr<!rec_A>>
-// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_A>>, !cir.ptr<!rec_A>
-// CIR: cir.return
-
-// LLVM: define{{.*}} void @_ZN1AC2Ev(ptr %[[THIS_ARG:.*]]) {
-// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
-// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
-// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
-// LLVM: ret void
-
-// Note: OGCG elides the constructor for A. This is not yet implemented in CIR.
-
// Constructor for B
// CIR: cir.func comdat linkonce_odr @_ZN1BC1Ev(%arg0: !cir.ptr<!rec_B>
// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_B>, !cir.ptr<!cir.ptr<!rec_B>>, ["this", init]
// CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr<!rec_B>, !cir.ptr<!cir.ptr<!rec_B>>
// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_B>>, !cir.ptr<!rec_B>
// CIR: %[[BASE_A_ADDR:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_B> nonnull [12] -> !cir.ptr<!rec_A>
-// CIR: cir.call @_ZN1AC2Ev(%[[BASE_A_ADDR]]) nothrow : (!cir.ptr<!rec_A>) -> ()
// CIR: %[[VTABLE:.*]] = cir.vtable.address_point(@_ZTV1B, address_point = <index = 0, offset = 3>) : !cir.vptr
// CIR: %[[B_VPTR:.*]] = cir.vtable.get_vptr %[[THIS]] : !cir.ptr<!rec_B> -> !cir.ptr<!cir.vptr>
// CIR: cir.store align(8) %[[VTABLE]], %[[B_VPTR]] : !cir.vptr, !cir.ptr<!cir.vptr>
@@ -56,7 +40,6 @@ void ppp() { B b; }
// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
// LLVM: %[[BASE_A_ADDR:.*]] = getelementptr i8, ptr %[[THIS]], i32 12
-// LLVM: call void @_ZN1AC2Ev(ptr %[[BASE_A_ADDR]])
// LLVM: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1B, i64 24), ptr %[[THIS]]
// LLVM: ret void
@@ -67,4 +50,3 @@ void ppp() { B b; }
// OGCG: %[[BASE_A_ADDR:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i64 12
// OGCG: store ptr getelementptr inbounds inrange(-24, 0) ({ [3 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 3), ptr %[[THIS]]
// OGCG: ret void
-
|
Here is an example where the incubator emits a declaration of a default constructor that is never defined. https://godbolt.org/z/qM1Mf5xMc A comment in the code claimed that a later pass would lower the ctor call to trivial initialization, but it never does. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Can you add a couple more test cases?
struct WithBraceInit {
int i{};
WithBraceInit() = default;
WithBraceInit(int);
};
WithBraceInit:WithBraceInit(int) : WithBraceInit() {}
int main() {
// Destinations are zeroed
static DelegatingWithZeroing dwz(123);
static WithBraceInit wbi(456);
}
Those cases run into other things that aren't yet implemented. For the default constructor of |
This adds support for zero-initialization during delegating constructor processing.
Note, this also adds code to skip emitting constructors that are trivial and default to match the classic codegen behavior. The incubator does not skip these constructors, but I have found a case where this results in a call to a default constructor that is never defined.