Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
72 changes: 72 additions & 0 deletions clang/test/CIR/CodeGen/delegating-ctor.cpp
Original file line number Diff line number Diff line change
@@ -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)
4 changes: 0 additions & 4 deletions clang/test/CIR/CodeGen/new.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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]])
Expand All @@ -40,16 +39,13 @@ 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
// LLVM: %[[NEW_DOUBLE:.*]] = call{{.*}} ptr @_Znwm(i64 8)
// 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
Expand Down
18 changes: 0 additions & 18 deletions clang/test/CIR/CodeGen/vbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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

Expand All @@ -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