Skip to content

Commit 83da177

Browse files
authored
[CIR] Add support for delegating constructor initialization (#156757)
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.
1 parent 8796dfd commit 83da177

File tree

5 files changed

+96
-29
lines changed

5 files changed

+96
-29
lines changed

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,15 +1966,23 @@ void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
19661966
// constructor, emit the zero initialization now, unless destination is
19671967
// already zeroed.
19681968
if (e->requiresZeroInitialization() && !dest.isZeroed()) {
1969-
cgm.errorNYI(e->getSourceRange(),
1970-
"emitCXXConstructExpr: requires initialization");
1971-
return;
1969+
switch (e->getConstructionKind()) {
1970+
case CXXConstructionKind::Delegating:
1971+
case CXXConstructionKind::Complete:
1972+
emitNullInitialization(getLoc(e->getSourceRange()), dest.getAddress(),
1973+
e->getType());
1974+
break;
1975+
case CXXConstructionKind::VirtualBase:
1976+
case CXXConstructionKind::NonVirtualBase:
1977+
cgm.errorNYI(e->getSourceRange(),
1978+
"emitCXXConstructExpr: base requires initialization");
1979+
break;
1980+
}
19721981
}
19731982

1974-
// If this is a call to a trivial default constructor:
1975-
// In LLVM: do nothing.
1976-
// In CIR: emit as a regular call, other later passes should lower the
1977-
// ctor call into trivial initialization.
1983+
// If this is a call to a trivial default constructor, do nothing.
1984+
if (cd->isTrivial() && cd->isDefaultConstructor())
1985+
return;
19781986

19791987
// Elide the constructor if we're constructing from a temporary
19801988
if (getLangOpts().ElideConstructors && e->isElidable()) {

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,6 +1368,15 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
13681368
rewriter.replaceOp(op, lowerCirAttrAsValue(op, op.getValue(), rewriter,
13691369
getTypeConverter()));
13701370
return mlir::success();
1371+
} else if (auto recTy = mlir::dyn_cast<cir::RecordType>(op.getType())) {
1372+
if (mlir::isa<cir::ZeroAttr, cir::UndefAttr>(attr)) {
1373+
mlir::Value initVal =
1374+
lowerCirAttrAsValue(op, attr, rewriter, typeConverter);
1375+
rewriter.replaceOp(op, initVal);
1376+
return mlir::success();
1377+
}
1378+
return op.emitError() << "unsupported lowering for record constant type "
1379+
<< op.getType();
13711380
} else if (auto complexTy = mlir::dyn_cast<cir::ComplexType>(op.getType())) {
13721381
mlir::Type complexElemTy = complexTy.getElementType();
13731382
mlir::Type complexElemLLVMTy = typeConverter->convertType(complexElemTy);
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
5+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
7+
8+
struct Delegating {
9+
Delegating();
10+
Delegating(int);
11+
};
12+
13+
// Check that the constructor being delegated to is called with the correct
14+
// arguments.
15+
Delegating::Delegating() : Delegating(0) {}
16+
17+
// CIR: cir.func {{.*}} @_ZN10DelegatingC2Ev(%[[THIS_ARG:.*]]: !cir.ptr<!rec_Delegating> {{.*}})
18+
// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_Delegating>, !cir.ptr<!cir.ptr<!rec_Delegating>>, ["this", init]
19+
// CIR: cir.store{{.*}} %[[THIS_ARG]], %[[THIS_ADDR]]
20+
// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
21+
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
22+
// CIR: cir.call @_ZN10DelegatingC2Ei(%[[THIS]], %[[ZERO]]) : (!cir.ptr<!rec_Delegating>, !s32i) -> ()
23+
24+
// LLVM: define {{.*}} @_ZN10DelegatingC2Ev(ptr %[[THIS_ARG:.*]])
25+
// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
26+
// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
27+
// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
28+
// LLVM: call void @_ZN10DelegatingC2Ei(ptr %[[THIS]], i32 0)
29+
30+
// OGCG: define {{.*}} @_ZN10DelegatingC2Ev(ptr {{.*}} %[[THIS_ARG:.*]])
31+
// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
32+
// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
33+
// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
34+
// OGCG: call void @_ZN10DelegatingC2Ei(ptr {{.*}} %[[THIS]], i32 {{.*}} 0)
35+
36+
struct DelegatingWithZeroing {
37+
int i;
38+
DelegatingWithZeroing() = default;
39+
DelegatingWithZeroing(int);
40+
};
41+
42+
// Check that the delegating constructor performs zero-initialization here.
43+
// FIXME: we should either emit the trivial default constructor or remove the
44+
// call to it in a lowering pass.
45+
DelegatingWithZeroing::DelegatingWithZeroing(int) : DelegatingWithZeroing() {}
46+
47+
// CIR: cir.func {{.*}} @_ZN21DelegatingWithZeroingC2Ei(%[[THIS_ARG:.*]]: !cir.ptr<!rec_DelegatingWithZeroing> {{.*}}, %[[I_ARG:.*]]: !s32i {{.*}})
48+
// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_DelegatingWithZeroing>, !cir.ptr<!cir.ptr<!rec_DelegatingWithZeroing>>, ["this", init]
49+
// CIR: %[[I_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["", init]
50+
// CIR: cir.store{{.*}} %[[THIS_ARG]], %[[THIS_ADDR]]
51+
// CIR: cir.store{{.*}} %[[I_ARG]], %[[I_ADDR]]
52+
// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
53+
// CIR: %[[ZERO:.*]] = cir.const #cir.zero : !rec_DelegatingWithZeroing
54+
// CIR: cir.store{{.*}} %[[ZERO]], %[[THIS]] : !rec_DelegatingWithZeroing, !cir.ptr<!rec_DelegatingWithZeroing>
55+
56+
// LLVM: define {{.*}} void @_ZN21DelegatingWithZeroingC2Ei(ptr %[[THIS_ARG:.*]], i32 %[[I_ARG:.*]])
57+
// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
58+
// LLVM: %[[I_ADDR:.*]] = alloca i32
59+
// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
60+
// LLVM: store i32 %[[I_ARG]], ptr %[[I_ADDR]]
61+
// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
62+
// LLVM: store %struct.DelegatingWithZeroing zeroinitializer, ptr %[[THIS]]
63+
64+
// Note: OGCG elides the call to the default constructor.
65+
66+
// OGCG: define {{.*}} void @_ZN21DelegatingWithZeroingC2Ei(ptr {{.*}} %[[THIS_ARG:.*]], i32 {{.*}} %[[I_ARG:.*]])
67+
// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
68+
// OGCG: %[[I_ADDR:.*]] = alloca i32
69+
// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
70+
// OGCG: store i32 %[[I_ARG]], ptr %[[I_ADDR]]
71+
// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
72+
// OGCG: call void @llvm.memset.p0.i64(ptr align 4 %[[THIS]], i8 0, i64 4, i1 false)

clang/test/CIR/CodeGen/new.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ void test_basic_new() {
2323
// CHECK: %[[EIGHT:.*]] = cir.const #cir.int<8>
2424
// CHECK: %[[NEW_S:.*]] = cir.call @_Znwm(%[[EIGHT]])
2525
// CHECK: %[[NEW_S_PTR:.*]] = cir.cast(bitcast, %[[NEW_S]]
26-
// CHECK: cir.call @_ZN1SC1Ev(%[[NEW_S_PTR]])
2726
// CHECK: cir.store{{.*}} %[[NEW_S_PTR]], %[[PS_ADDR]]
2827
// CHECK: %[[FOUR:.*]] = cir.const #cir.int<4>
2928
// CHECK: %[[NEW_INT:.*]] = cir.call @_Znwm(%[[FOUR]])
@@ -40,16 +39,13 @@ void test_basic_new() {
4039
// LLVM: %[[PN_ADDR:.*]] = alloca ptr, i64 1, align 8
4140
// LLVM: %[[PD_ADDR:.*]] = alloca ptr, i64 1, align 8
4241
// LLVM: %[[NEW_S:.*]] = call{{.*}} ptr @_Znwm(i64 8)
43-
// LLVM: call{{.*}} void @_ZN1SC1Ev(ptr %[[NEW_S]])
4442
// LLVM: store ptr %[[NEW_S]], ptr %[[PS_ADDR]], align 8
4543
// LLVM: %[[NEW_INT:.*]] = call{{.*}} ptr @_Znwm(i64 4)
4644
// LLVM: store ptr %[[NEW_INT]], ptr %[[PN_ADDR]], align 8
4745
// LLVM: %[[NEW_DOUBLE:.*]] = call{{.*}} ptr @_Znwm(i64 8)
4846
// LLVM: store ptr %[[NEW_DOUBLE]], ptr %[[PD_ADDR]], align 8
4947
// LLVM: ret void
5048

51-
// NOTE: OGCG elides the constructor call here, but CIR does not.
52-
5349
// OGCG: define{{.*}} void @_Z14test_basic_newv
5450
// OGCG: %[[PS_ADDR:.*]] = alloca ptr, align 8
5551
// OGCG: %[[PN_ADDR:.*]] = alloca ptr, align 8

clang/test/CIR/CodeGen/vbase.cpp

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,28 +47,12 @@ void ppp() { B b; }
4747

4848
// 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
4949

50-
// Constructor for A
51-
// CIR: cir.func comdat linkonce_odr @_ZN1AC2Ev(%arg0: !cir.ptr<!rec_A>
52-
// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_A>, !cir.ptr<!cir.ptr<!rec_A>>, ["this", init]
53-
// CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr<!rec_A>, !cir.ptr<!cir.ptr<!rec_A>>
54-
// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_A>>, !cir.ptr<!rec_A>
55-
// CIR: cir.return
56-
57-
// LLVM: define{{.*}} void @_ZN1AC2Ev(ptr %[[THIS_ARG:.*]]) {
58-
// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
59-
// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
60-
// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
61-
// LLVM: ret void
62-
63-
// Note: OGCG elides the constructor for A. This is not yet implemented in CIR.
64-
6550
// Constructor for B
6651
// CIR: cir.func comdat linkonce_odr @_ZN1BC1Ev(%arg0: !cir.ptr<!rec_B>
6752
// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_B>, !cir.ptr<!cir.ptr<!rec_B>>, ["this", init]
6853
// CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr<!rec_B>, !cir.ptr<!cir.ptr<!rec_B>>
6954
// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_B>>, !cir.ptr<!rec_B>
7055
// CIR: %[[BASE_A_ADDR:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_B> nonnull [12] -> !cir.ptr<!rec_A>
71-
// CIR: cir.call @_ZN1AC2Ev(%[[BASE_A_ADDR]]) nothrow : (!cir.ptr<!rec_A>) -> ()
7256
// CIR: %[[VTABLE:.*]] = cir.vtable.address_point(@_ZTV1B, address_point = <index = 0, offset = 3>) : !cir.vptr
7357
// CIR: %[[B_VPTR:.*]] = cir.vtable.get_vptr %[[THIS]] : !cir.ptr<!rec_B> -> !cir.ptr<!cir.vptr>
7458
// CIR: cir.store align(8) %[[VTABLE]], %[[B_VPTR]] : !cir.vptr, !cir.ptr<!cir.vptr>
@@ -79,7 +63,6 @@ void ppp() { B b; }
7963
// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
8064
// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
8165
// LLVM: %[[BASE_A_ADDR:.*]] = getelementptr i8, ptr %[[THIS]], i32 12
82-
// LLVM: call void @_ZN1AC2Ev(ptr %[[BASE_A_ADDR]])
8366
// LLVM: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1B, i64 24), ptr %[[THIS]]
8467
// LLVM: ret void
8568

@@ -90,4 +73,3 @@ void ppp() { B b; }
9073
// OGCG: %[[BASE_A_ADDR:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i64 12
9174
// OGCG: store ptr getelementptr inbounds inrange(-24, 0) ({ [3 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 3), ptr %[[THIS]]
9275
// OGCG: ret void
93-

0 commit comments

Comments
 (0)