Skip to content

Commit 4a4035c

Browse files
authored
[CIR] Add support for delegating constructors (#143932)
This change adds the necessary support for handling delegating constructors in ClangIR. The implementation is kept as small as possible by not handling any other sort of initialization (members, base classes, etc.). That will be added in a future commit.
1 parent 1ac61c8 commit 4a4035c

File tree

10 files changed

+183
-20
lines changed

10 files changed

+183
-20
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ struct MissingFeatures {
172172
static bool astVarDeclInterface() { return false; }
173173
static bool stackSaveOp() { return false; }
174174
static bool aggValueSlot() { return false; }
175+
static bool aggValueSlotMayOverlap() { return false; }
175176
static bool generateDebugInfo() { return false; }
176177
static bool pointerOverflowSanitizer() { return false; }
177178
static bool fpConstraints() { return false; }
@@ -227,7 +228,6 @@ struct MissingFeatures {
227228
static bool implicitConstructorArgs() { return false; }
228229
static bool intrinsics() { return false; }
229230
static bool attributeNoBuiltin() { return false; }
230-
static bool emitCtorPrologue() { return false; }
231231
static bool thunks() { return false; }
232232
static bool runCleanupsScope() { return false; }
233233

clang/lib/CIR/CodeGen/CIRGenCall.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,7 @@ void CIRGenFunction::emitDelegateCallArg(CallArgList &args,
189189
// For the most part, we just need to load the alloca, except that aggregate
190190
// r-values are actually pointers to temporaries.
191191
} else {
192-
cgm.errorNYI(param->getSourceRange(),
193-
"emitDelegateCallArg: convertTempToRValue");
192+
args.add(convertTempToRValue(local, type, loc), type);
194193
}
195194

196195
// Deactivate the cleanup for the callee-destructed param that was pushed.

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,21 @@ bool CIRGenFunction::isConstructorDelegationValid(
5353
return true;
5454
}
5555

56+
/// This routine generates necessary code to initialize base classes and
57+
/// non-static data members belonging to this constructor.
58+
void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
59+
CXXCtorType ctorType,
60+
FunctionArgList &args) {
61+
if (cd->isDelegatingConstructor())
62+
return emitDelegatingCXXConstructorCall(cd, args);
63+
64+
if (cd->getNumCtorInitializers() != 0) {
65+
// There's much more to do here.
66+
cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: any initializer");
67+
return;
68+
}
69+
}
70+
5671
Address CIRGenFunction::loadCXXThisAddress() {
5772
assert(curFuncDecl && "loading 'this' without a func declaration?");
5873
assert(isa<CXXMethodDecl>(curFuncDecl));
@@ -102,6 +117,29 @@ void CIRGenFunction::emitDelegateCXXConstructorCall(
102117
/*Delegating=*/true, thisAddr, delegateArgs, loc);
103118
}
104119

120+
void CIRGenFunction::emitDelegatingCXXConstructorCall(
121+
const CXXConstructorDecl *ctor, const FunctionArgList &args) {
122+
assert(ctor->isDelegatingConstructor());
123+
124+
Address thisPtr = loadCXXThisAddress();
125+
126+
assert(!cir::MissingFeatures::objCGC());
127+
assert(!cir::MissingFeatures::sanitizers());
128+
AggValueSlot aggSlot = AggValueSlot::forAddr(
129+
thisPtr, Qualifiers(), AggValueSlot::IsDestructed,
130+
AggValueSlot::IsNotAliased, AggValueSlot::MayOverlap,
131+
AggValueSlot::IsNotZeroed);
132+
133+
emitAggExpr(ctor->init_begin()[0]->getInit(), aggSlot);
134+
135+
const CXXRecordDecl *classDecl = ctor->getParent();
136+
if (cgm.getLangOpts().Exceptions && !classDecl->hasTrivialDestructor()) {
137+
cgm.errorNYI(ctor->getSourceRange(),
138+
"emitDelegatingCXXConstructorCall: exception");
139+
return;
140+
}
141+
}
142+
105143
Address CIRGenFunction::getAddressOfBaseClass(
106144
Address value, const CXXRecordDecl *derived,
107145
llvm::iterator_range<CastExpr::path_const_iterator> path,

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,12 @@ void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d,
259259
return;
260260
}
261261
case cir::TEK_Aggregate:
262-
emitAggExpr(init, AggValueSlot::forLValue(lvalue));
262+
// The overlap flag here should be calculated.
263+
assert(!cir::MissingFeatures::aggValueSlotMayOverlap());
264+
emitAggExpr(init,
265+
AggValueSlot::forLValue(lvalue, AggValueSlot::IsDestructed,
266+
AggValueSlot::IsNotAliased,
267+
AggValueSlot::MayOverlap));
263268
return;
264269
}
265270
llvm_unreachable("bad evaluation kind");

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,23 @@ Address CIRGenFunction::emitArrayToPointerDecay(const Expr *e) {
12611261
return Address(ptr, addr.getAlignment());
12621262
}
12631263

1264+
/// Given the address of a temporary variable, produce an r-value of its type.
1265+
RValue CIRGenFunction::convertTempToRValue(Address addr, clang::QualType type,
1266+
clang::SourceLocation loc) {
1267+
LValue lvalue = makeAddrLValue(addr, type, AlignmentSource::Decl);
1268+
switch (getEvaluationKind(type)) {
1269+
case cir::TEK_Complex:
1270+
cgm.errorNYI(loc, "convertTempToRValue: complex type");
1271+
return RValue::get(nullptr);
1272+
case cir::TEK_Aggregate:
1273+
cgm.errorNYI(loc, "convertTempToRValue: aggregate type");
1274+
return RValue::get(nullptr);
1275+
case cir::TEK_Scalar:
1276+
return RValue::get(emitLoadOfScalar(lvalue, loc));
1277+
}
1278+
llvm_unreachable("bad evaluation kind");
1279+
}
1280+
12641281
/// Emit an `if` on a boolean condition, filling `then` and `else` into
12651282
/// appropriated regions.
12661283
mlir::LogicalResult CIRGenFunction::emitIfOnBoolExpr(const Expr *cond,
@@ -1473,6 +1490,10 @@ void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
14731490
type = Ctor_Complete;
14741491
break;
14751492
case CXXConstructionKind::Delegating:
1493+
// We should be emitting a constructor; GlobalDecl will assert this
1494+
type = curGD.getCtorType();
1495+
delegating = true;
1496+
break;
14761497
case CXXConstructionKind::VirtualBase:
14771498
case CXXConstructionKind::NonVirtualBase:
14781499
cgm.errorNYI(e->getSourceRange(),

clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,11 @@ void AggExprEmitter::emitInitializationToLValue(Expr *e, LValue lv) {
203203
cgf.cgm.errorNYI("emitInitializationToLValue TEK_Complex");
204204
break;
205205
case cir::TEK_Aggregate:
206-
cgf.emitAggExpr(e, AggValueSlot::forLValue(lv));
206+
cgf.emitAggExpr(e, AggValueSlot::forLValue(lv, AggValueSlot::IsDestructed,
207+
AggValueSlot::IsNotAliased,
208+
AggValueSlot::MayOverlap,
209+
dest.isZeroed()));
210+
207211
return;
208212
case cir::TEK_Scalar:
209213
if (lv.isSimple())
@@ -284,6 +288,8 @@ LValue CIRGenFunction::emitAggExprToLValue(const Expr *e) {
284288
assert(hasAggregateEvaluationKind(e->getType()) && "Invalid argument!");
285289
Address temp = createMemTemp(e->getType(), getLoc(e->getSourceRange()));
286290
LValue lv = makeAddrLValue(temp, e->getType());
287-
emitAggExpr(e, AggValueSlot::forLValue(lv));
291+
emitAggExpr(e, AggValueSlot::forLValue(lv, AggValueSlot::IsNotDestructed,
292+
AggValueSlot::IsNotAliased,
293+
AggValueSlot::DoesNotOverlap));
288294
return lv;
289295
}

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -526,14 +526,8 @@ void CIRGenFunction::emitConstructorBody(FunctionArgList &args) {
526526
// TODO: in restricted cases, we can emit the vbase initializers of a
527527
// complete ctor and then delegate to the base ctor.
528528

529-
assert(!cir::MissingFeatures::emitCtorPrologue());
530-
if (ctor->isDelegatingConstructor()) {
531-
// This will be handled in emitCtorPrologue, but we should emit a diagnostic
532-
// rather than silently fail to delegate.
533-
cgm.errorNYI(ctor->getSourceRange(),
534-
"emitConstructorBody: delegating ctor");
535-
return;
536-
}
529+
// Emit the constructor prologue, i.e. the base and member initializers.
530+
emitCtorPrologue(ctor, ctorType, args);
537531

538532
// TODO(cir): propagate this result via mlir::logical result. Just unreachable
539533
// now just to have it handled.

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,9 @@ class CIRGenFunction : public CIRGenTypeCache {
474474

475475
bool shouldNullCheckClassCastValue(const CastExpr *ce);
476476

477+
RValue convertTempToRValue(Address addr, clang::QualType type,
478+
clang::SourceLocation loc);
479+
477480
static bool
478481
isConstructorDelegationValid(const clang::CXXConstructorDecl *ctor);
479482

@@ -797,6 +800,16 @@ class CIRGenFunction : public CIRGenTypeCache {
797800
const CXXMethodDecl *md,
798801
ReturnValueSlot returnValue);
799802

803+
void emitCtorPrologue(const clang::CXXConstructorDecl *ctor,
804+
clang::CXXCtorType ctorType, FunctionArgList &args);
805+
806+
// It's important not to confuse this and emitDelegateCXXConstructorCall.
807+
// Delegating constructors are the C++11 feature. The constructor delegate
808+
// optimization is used to reduce duplication in the base and complete
809+
// constructors where they are substantially the same.
810+
void emitDelegatingCXXConstructorCall(const CXXConstructorDecl *ctor,
811+
const FunctionArgList &args);
812+
800813
mlir::LogicalResult emitDoStmt(const clang::DoStmt &s);
801814

802815
/// Emit an expression as an initializer for an object (variable, field, etc.)

clang/lib/CIR/CodeGen/CIRGenValue.h

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,23 +267,64 @@ class AggValueSlot {
267267
Address addr;
268268
clang::Qualifiers quals;
269269

270+
/// This is set to true if some external code is responsible for setting up a
271+
/// destructor for the slot. Otherwise the code which constructs it should
272+
/// push the appropriate cleanup.
273+
LLVM_PREFERRED_TYPE(bool)
274+
[[maybe_unused]] unsigned destructedFlag : 1;
275+
270276
/// This is set to true if the memory in the slot is known to be zero before
271277
/// the assignment into it. This means that zero fields don't need to be set.
272-
bool zeroedFlag : 1;
278+
LLVM_PREFERRED_TYPE(bool)
279+
unsigned zeroedFlag : 1;
280+
281+
/// This is set to true if the slot might be aliased and it's not undefined
282+
/// behavior to access it through such an alias. Note that it's always
283+
/// undefined behavior to access a C++ object that's under construction
284+
/// through an alias derived from outside the construction process.
285+
///
286+
/// This flag controls whether calls that produce the aggregate
287+
/// value may be evaluated directly into the slot, or whether they
288+
/// must be evaluated into an unaliased temporary and then memcpy'ed
289+
/// over. Since it's invalid in general to memcpy a non-POD C++
290+
/// object, it's important that this flag never be set when
291+
/// evaluating an expression which constructs such an object.
292+
LLVM_PREFERRED_TYPE(bool)
293+
[[maybe_unused]] unsigned aliasedFlag : 1;
294+
295+
/// This is set to true if the tail padding of this slot might overlap
296+
/// another object that may have already been initialized (and whose
297+
/// value must be preserved by this initialization). If so, we may only
298+
/// store up to the dsize of the type. Otherwise we can widen stores to
299+
/// the size of the type.
300+
LLVM_PREFERRED_TYPE(bool)
301+
[[maybe_unused]] unsigned overlapFlag : 1;
273302

274303
public:
304+
enum IsDestructed_t { IsNotDestructed, IsDestructed };
275305
enum IsZeroed_t { IsNotZeroed, IsZeroed };
306+
enum IsAliased_t { IsNotAliased, IsAliased };
307+
enum Overlap_t { MayOverlap, DoesNotOverlap };
276308

277-
AggValueSlot(Address addr, clang::Qualifiers quals, bool zeroedFlag)
278-
: addr(addr), quals(quals), zeroedFlag(zeroedFlag) {}
309+
AggValueSlot(Address addr, clang::Qualifiers quals, bool destructedFlag,
310+
bool zeroedFlag, bool aliasedFlag, bool overlapFlag)
311+
: addr(addr), quals(quals), destructedFlag(destructedFlag),
312+
zeroedFlag(zeroedFlag), aliasedFlag(aliasedFlag),
313+
overlapFlag(overlapFlag) {}
279314

280315
static AggValueSlot forAddr(Address addr, clang::Qualifiers quals,
316+
IsDestructed_t isDestructed,
317+
IsAliased_t isAliased, Overlap_t mayOverlap,
281318
IsZeroed_t isZeroed = IsNotZeroed) {
282-
return AggValueSlot(addr, quals, isZeroed);
319+
return AggValueSlot(addr, quals, isDestructed, isZeroed, isAliased,
320+
mayOverlap);
283321
}
284322

285-
static AggValueSlot forLValue(const LValue &lv) {
286-
return forAddr(lv.getAddress(), lv.getQuals());
323+
static AggValueSlot forLValue(const LValue &LV, IsDestructed_t isDestructed,
324+
IsAliased_t isAliased, Overlap_t mayOverlap,
325+
IsZeroed_t isZeroed = IsNotZeroed) {
326+
return forAddr(LV.getAddress(), LV.getQuals(), isDestructed, isAliased,
327+
mayOverlap, isZeroed);
287328
}
288329

289330
clang::Qualifiers getQualifiers() const { return quals; }

clang/test/CIR/CodeGen/ctor.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,49 @@ void bar() {
6767
// CHECK-NEXT: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i
6868
// CHECK-NEXT: cir.call @_ZN13VariadicStrukC1Eiz(%[[S_ADDR]], %[[ONE]], %[[TWO]], %[[THREE]])
6969
// CHECK-NEXT: cir.return
70+
71+
struct DelegatingStruk {
72+
int a;
73+
DelegatingStruk(int n) { a = n; }
74+
DelegatingStruk() : DelegatingStruk(0) {}
75+
};
76+
77+
void bam() {
78+
DelegatingStruk s;
79+
}
80+
81+
// CHECK: cir.func @_ZN15DelegatingStrukC2Ei(%arg0: !cir.ptr<!rec_DelegatingStruk>
82+
// CHECK-SAME: %arg1: !s32i
83+
// CHECK-NEXT: %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init]
84+
// CHECK-NEXT: %[[N_ADDR:.*]] = cir.alloca {{.*}} ["n", init]
85+
// CHECK-NEXT: cir.store %arg0, %[[THIS_ADDR]]
86+
// CHECK-NEXT: cir.store %arg1, %[[N_ADDR]]
87+
// CHECK-NEXT: %[[THIS:.*]] = cir.load{{.*}} %[[THIS_ADDR]]
88+
// CHECK-NEXT: %[[N:.*]] = cir.load{{.*}} %[[N_ADDR]]
89+
// CHECK-NEXT: %[[A_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "a"}
90+
// CHECK-NEXT: cir.store{{.*}} %[[N]], %[[A_ADDR]]
91+
// CHECK-NEXT: cir.return
92+
93+
// CHECK: cir.func @_ZN15DelegatingStrukC1Ei(%arg0: !cir.ptr<!rec_DelegatingStruk>
94+
// CHECK-SAME: %arg1: !s32i
95+
// CHECK-NEXT: %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init]
96+
// CHECK-NEXT: %[[N_ADDR:.*]] = cir.alloca {{.*}} ["n", init]
97+
// CHECK-NEXT: cir.store %arg0, %[[THIS_ADDR]]
98+
// CHECK-NEXT: cir.store %arg1, %[[N_ADDR]]
99+
// CHECK-NEXT: %[[THIS:.*]] = cir.load{{.*}} %[[THIS_ADDR]]
100+
// CHECK-NEXT: %[[N:.*]] = cir.load{{.*}} %[[N_ADDR]]
101+
// CHECK-NEXT: cir.call @_ZN15DelegatingStrukC2Ei(%[[THIS]], %[[N]])
102+
// CHECK-NEXT: cir.return
103+
104+
// CHECK: cir.func @_ZN15DelegatingStrukC1Ev(%arg0: !cir.ptr<!rec_DelegatingStruk>
105+
// CHECK-NEXT: %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init]
106+
// CHECK-NEXT: cir.store %arg0, %[[THIS_ADDR]]
107+
// CHECK-NEXT: %[[THIS:.*]] = cir.load{{.*}} %[[THIS_ADDR]]
108+
// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
109+
// CHECK-NEXT: cir.call @_ZN15DelegatingStrukC1Ei(%[[THIS]], %[[ZERO]])
110+
// CHECK-NEXT: cir.return
111+
112+
// CHECK: cir.func @_Z3bamv
113+
// CHECK-NEXT: %[[S_ADDR:.*]] = cir.alloca {{.*}} ["s", init]
114+
// CHECK-NEXT: cir.call @_ZN15DelegatingStrukC1Ev(%[[S_ADDR]])
115+
// CHECK-NEXT: cir.return

0 commit comments

Comments
 (0)