Skip to content

Commit c329de7

Browse files
authored
[CIR][CIRGen] Implement delegating constructors (#821)
This is a straightforward adaption from CodeGen. I checked the uses of the Delegating arg that's passed in various places, and it only appears to be used by virtual inheritance, which should be handled by #624.
1 parent 97357e1 commit c329de7

File tree

4 files changed

+146
-4
lines changed

4 files changed

+146
-4
lines changed

clang/lib/CIR/CodeGen/CIRGenClass.cpp

+48-3
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ bool CIRGenFunction::IsConstructorDelegationValid(
5050

5151
// FIXME: Decide if we can do a delegation of a delegating constructor.
5252
if (Ctor->isDelegatingConstructor())
53-
llvm_unreachable("NYI");
53+
return false;
5454

5555
return true;
5656
}
@@ -585,7 +585,7 @@ void CIRGenFunction::buildCtorPrologue(const CXXConstructorDecl *CD,
585585
CXXCtorType CtorType,
586586
FunctionArgList &Args) {
587587
if (CD->isDelegatingConstructor())
588-
llvm_unreachable("NYI");
588+
return buildDelegatingCXXConstructorCall(CD, Args);
589589

590590
const CXXRecordDecl *ClassDecl = CD->getParent();
591591

@@ -1379,6 +1379,51 @@ void CIRGenFunction::EnterDtorCleanups(const CXXDestructorDecl *DD,
13791379
assert(!MissingFeatures::sanitizeDtor());
13801380
}
13811381

1382+
namespace {
1383+
struct CallDelegatingCtorDtor final : EHScopeStack::Cleanup {
1384+
const CXXDestructorDecl *Dtor;
1385+
Address Addr;
1386+
CXXDtorType Type;
1387+
1388+
CallDelegatingCtorDtor(const CXXDestructorDecl *D, Address Addr,
1389+
CXXDtorType Type)
1390+
: Dtor(D), Addr(Addr), Type(Type) {}
1391+
1392+
void Emit(CIRGenFunction &CGF, Flags flags) override {
1393+
// We are calling the destructor from within the constructor.
1394+
// Therefore, "this" should have the expected type.
1395+
QualType ThisTy = Dtor->getFunctionObjectParameterType();
1396+
CGF.buildCXXDestructorCall(Dtor, Type, /*ForVirtualBase=*/false,
1397+
/*Delegating=*/true, Addr, ThisTy);
1398+
}
1399+
};
1400+
} // end anonymous namespace
1401+
1402+
void CIRGenFunction::buildDelegatingCXXConstructorCall(
1403+
const CXXConstructorDecl *Ctor, const FunctionArgList &Args) {
1404+
assert(Ctor->isDelegatingConstructor());
1405+
1406+
Address ThisPtr = LoadCXXThisAddress();
1407+
1408+
AggValueSlot AggSlot = AggValueSlot::forAddr(
1409+
ThisPtr, Qualifiers(), AggValueSlot::IsDestructed,
1410+
AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsNotAliased,
1411+
AggValueSlot::MayOverlap, AggValueSlot::IsNotZeroed,
1412+
// Checks are made by the code that calls constructor.
1413+
AggValueSlot::IsSanitizerChecked);
1414+
1415+
buildAggExpr(Ctor->init_begin()[0]->getInit(), AggSlot);
1416+
1417+
const CXXRecordDecl *ClassDecl = Ctor->getParent();
1418+
if (CGM.getLangOpts().Exceptions && !ClassDecl->hasTrivialDestructor()) {
1419+
CXXDtorType Type =
1420+
CurGD.getCtorType() == Ctor_Complete ? Dtor_Complete : Dtor_Base;
1421+
1422+
EHStack.pushCleanup<CallDelegatingCtorDtor>(
1423+
EHCleanup, ClassDecl->getDestructor(), ThisPtr, Type);
1424+
}
1425+
}
1426+
13821427
void CIRGenFunction::buildCXXDestructorCall(const CXXDestructorDecl *DD,
13831428
CXXDtorType Type,
13841429
bool ForVirtualBase,
@@ -1710,4 +1755,4 @@ void CIRGenFunction::buildCXXAggrConstructorCall(
17101755

17111756
if (constantCount.use_empty())
17121757
constantCount.erase();
1713-
}
1758+
}

clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,9 @@ void CIRGenFunction::buildCXXConstructExpr(const CXXConstructExpr *E,
402402
Type = Ctor_Complete;
403403
break;
404404
case CXXConstructionKind::Delegating:
405-
llvm_unreachable("NYI");
405+
// We should be emitting a constructor; GlobalDecl will assert this
406+
Type = CurGD.getCtorType();
407+
Delegating = true;
406408
break;
407409
case CXXConstructionKind::VirtualBase:
408410
ForVirtualBase = true;

clang/lib/CIR/CodeGen/CIRGenFunction.h

+7
Original file line numberDiff line numberDiff line change
@@ -1649,6 +1649,13 @@ class CIRGenFunction : public CIRGenTypeCache {
16491649
const FunctionArgList &Args,
16501650
clang::SourceLocation Loc);
16511651

1652+
// It's important not to confuse this and the previous function. Delegating
1653+
// constructors are the C++11 feature. The constructor delegate optimization
1654+
// is used to reduce duplication in the base and complete constructors where
1655+
// they are substantially the same.
1656+
void buildDelegatingCXXConstructorCall(const CXXConstructorDecl *Ctor,
1657+
const FunctionArgList &Args);
1658+
16521659
/// We are performing a delegate call; that is, the current function is
16531660
/// delegating to another one. Produce a r-value suitable for passing the
16541661
/// given parameter.
+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -fexceptions -fcxx-exceptions %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
struct Delegating {
5+
Delegating();
6+
Delegating(int);
7+
};
8+
9+
// Check that the constructor being delegated to is called with the correct
10+
// arguments.
11+
Delegating::Delegating() : Delegating(0) {}
12+
13+
// CHECK-LABEL: cir.func @_ZN10DelegatingC2Ev(%arg0: !cir.ptr<!ty_22Delegating22> {{.*}}) {{.*}} {
14+
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!ty_22Delegating22>, !cir.ptr<!cir.ptr<!ty_22Delegating22>>, ["this", init] {alignment = 8 : i64}
15+
// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr<!ty_22Delegating22>, !cir.ptr<!cir.ptr<!ty_22Delegating22>>
16+
// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_22Delegating22>>, !cir.ptr<!ty_22Delegating22>
17+
// CHECK-NEXT: %2 = cir.const #cir.int<0> : !s32i
18+
// CHECK-NEXT: cir.call @_ZN10DelegatingC2Ei(%1, %2) : (!cir.ptr<!ty_22Delegating22>, !s32i) -> ()
19+
// CHECK-NEXT: cir.return
20+
// CHECK-NEXT: }
21+
22+
struct DelegatingWithZeroing {
23+
int i;
24+
DelegatingWithZeroing() = default;
25+
DelegatingWithZeroing(int);
26+
};
27+
28+
// Check that the delegating constructor performs zero-initialization here.
29+
// FIXME: we should either emit the trivial default constructor or remove the
30+
// call to it in a lowering pass.
31+
DelegatingWithZeroing::DelegatingWithZeroing(int) : DelegatingWithZeroing() {}
32+
33+
// CHECK-LABEL: cir.func @_ZN21DelegatingWithZeroingC2Ei(%arg0: !cir.ptr<!ty_22DelegatingWithZeroing22> {{.*}}, %arg1: !s32i {{.*}}) {{.*}} {
34+
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!ty_22DelegatingWithZeroing22>, !cir.ptr<!cir.ptr<!ty_22DelegatingWithZeroing22>>, ["this", init] {alignment = 8 : i64}
35+
// CHECK-NEXT: %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["", init] {alignment = 4 : i64}
36+
// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr<!ty_22DelegatingWithZeroing22>, !cir.ptr<!cir.ptr<!ty_22DelegatingWithZeroing22>>
37+
// CHECK-NEXT: cir.store %arg1, %1 : !s32i, !cir.ptr<!s32i>
38+
// CHECK-NEXT: %2 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_22DelegatingWithZeroing22>>, !cir.ptr<!ty_22DelegatingWithZeroing22>
39+
// CHECK-NEXT: %3 = cir.const #cir.zero : !ty_22DelegatingWithZeroing22
40+
// CHECK-NEXT: cir.store %3, %2 : !ty_22DelegatingWithZeroing22, !cir.ptr<!ty_22DelegatingWithZeroing22>
41+
// CHECK-NEXT: cir.call @_ZN21DelegatingWithZeroingC2Ev(%2) : (!cir.ptr<!ty_22DelegatingWithZeroing22>) -> () extra(#fn_attr1)
42+
// CHECK-NEXT: cir.return
43+
// CHECK-NEXT: }
44+
45+
void canThrow();
46+
struct HasNonTrivialDestructor {
47+
HasNonTrivialDestructor();
48+
HasNonTrivialDestructor(int);
49+
~HasNonTrivialDestructor();
50+
};
51+
52+
// Check that we call the destructor whenever a cleanup is needed.
53+
// FIXME: enable and check this when exceptions are fully supported.
54+
#if 0
55+
HasNonTrivialDestructor::HasNonTrivialDestructor(int)
56+
: HasNonTrivialDestructor() {
57+
canThrow();
58+
}
59+
#endif
60+
61+
// From clang/test/CodeGenCXX/cxx0x-delegating-ctors.cpp, check that virtual
62+
// inheritance and delegating constructors interact correctly.
63+
// FIXME: enable and check this when virtual inheritance is fully supported.
64+
#if 0
65+
namespace PR14588 {
66+
void other();
67+
68+
class Base {
69+
public:
70+
Base() { squawk(); }
71+
virtual ~Base() {}
72+
73+
virtual void squawk() { other(); }
74+
};
75+
76+
class Foo : public virtual Base {
77+
public:
78+
Foo();
79+
Foo(const void *inVoid);
80+
virtual ~Foo() {}
81+
82+
virtual void squawk() { other(); }
83+
};
84+
85+
Foo::Foo() : Foo(nullptr) { other(); }
86+
Foo::Foo(const void *inVoid) { squawk(); }
87+
} // namespace PR14588
88+
#endif

0 commit comments

Comments
 (0)