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(op.getType())) { + if (mlir::isa(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(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 {{.*}}) +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["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, !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 {{.*}}, %[[I_ARG:.*]]: !s32i {{.*}}) +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] +// CIR: %[[I_ADDR:.*]] = cir.alloca !s32i, !cir.ptr, ["", 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 + +// 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 -// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] -// CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr, !cir.ptr> -// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr>, !cir.ptr -// 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 // CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] // CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr, !cir.ptr> // CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr>, !cir.ptr // CIR: %[[BASE_A_ADDR:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr nonnull [12] -> !cir.ptr -// CIR: cir.call @_ZN1AC2Ev(%[[BASE_A_ADDR]]) nothrow : (!cir.ptr) -> () // CIR: %[[VTABLE:.*]] = cir.vtable.address_point(@_ZTV1B, address_point = ) : !cir.vptr // CIR: %[[B_VPTR:.*]] = cir.vtable.get_vptr %[[THIS]] : !cir.ptr -> !cir.ptr // CIR: cir.store align(8) %[[VTABLE]], %[[B_VPTR]] : !cir.vptr, !cir.ptr @@ -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 -