Skip to content

Commit a22834a

Browse files
authored
[CIR] Handle null base class initialization (#167023)
This adds handling for null base class initialization, but only for the trivial case where the class is empty. This also moves emitCXXConstructExpr to CIRGenExprCXX.cpp for consistency with classic codegen and the incubator repo.
1 parent 24be0ba commit a22834a

File tree

4 files changed

+133
-79
lines changed

4 files changed

+133
-79
lines changed

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,79 +2134,6 @@ RValue CIRGenFunction::emitCXXMemberCallExpr(const CXXMemberCallExpr *ce,
21342134
ce, md, returnValue, hasQualifier, qualifier, isArrow, base);
21352135
}
21362136

2137-
void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
2138-
AggValueSlot dest) {
2139-
assert(!dest.isIgnored() && "Must have a destination!");
2140-
const CXXConstructorDecl *cd = e->getConstructor();
2141-
2142-
// If we require zero initialization before (or instead of) calling the
2143-
// constructor, as can be the case with a non-user-provided default
2144-
// constructor, emit the zero initialization now, unless destination is
2145-
// already zeroed.
2146-
if (e->requiresZeroInitialization() && !dest.isZeroed()) {
2147-
switch (e->getConstructionKind()) {
2148-
case CXXConstructionKind::Delegating:
2149-
case CXXConstructionKind::Complete:
2150-
emitNullInitialization(getLoc(e->getSourceRange()), dest.getAddress(),
2151-
e->getType());
2152-
break;
2153-
case CXXConstructionKind::VirtualBase:
2154-
case CXXConstructionKind::NonVirtualBase:
2155-
cgm.errorNYI(e->getSourceRange(),
2156-
"emitCXXConstructExpr: base requires initialization");
2157-
break;
2158-
}
2159-
}
2160-
2161-
// If this is a call to a trivial default constructor, do nothing.
2162-
if (cd->isTrivial() && cd->isDefaultConstructor())
2163-
return;
2164-
2165-
// Elide the constructor if we're constructing from a temporary
2166-
if (getLangOpts().ElideConstructors && e->isElidable()) {
2167-
// FIXME: This only handles the simplest case, where the source object is
2168-
// passed directly as the first argument to the constructor. This
2169-
// should also handle stepping through implicit casts and conversion
2170-
// sequences which involve two steps, with a conversion operator
2171-
// follwed by a converting constructor.
2172-
const Expr *srcObj = e->getArg(0);
2173-
assert(srcObj->isTemporaryObject(getContext(), cd->getParent()));
2174-
assert(
2175-
getContext().hasSameUnqualifiedType(e->getType(), srcObj->getType()));
2176-
emitAggExpr(srcObj, dest);
2177-
return;
2178-
}
2179-
2180-
if (const ArrayType *arrayType = getContext().getAsArrayType(e->getType())) {
2181-
assert(!cir::MissingFeatures::sanitizers());
2182-
emitCXXAggrConstructorCall(cd, arrayType, dest.getAddress(), e, false);
2183-
} else {
2184-
2185-
clang::CXXCtorType type = Ctor_Complete;
2186-
bool forVirtualBase = false;
2187-
bool delegating = false;
2188-
2189-
switch (e->getConstructionKind()) {
2190-
case CXXConstructionKind::Complete:
2191-
type = Ctor_Complete;
2192-
break;
2193-
case CXXConstructionKind::Delegating:
2194-
// We should be emitting a constructor; GlobalDecl will assert this
2195-
type = curGD.getCtorType();
2196-
delegating = true;
2197-
break;
2198-
case CXXConstructionKind::VirtualBase:
2199-
forVirtualBase = true;
2200-
[[fallthrough]];
2201-
case CXXConstructionKind::NonVirtualBase:
2202-
type = Ctor_Base;
2203-
break;
2204-
}
2205-
2206-
emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e);
2207-
}
2208-
}
2209-
22102137
RValue CIRGenFunction::emitReferenceBindingToExpr(const Expr *e) {
22112138
// Emit the expression as an lvalue.
22122139
LValue lv = emitLValue(e);

clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -779,8 +779,8 @@ void AggExprEmitter::visitCXXParenListOrInitListExpr(
779779
Expr *e, ArrayRef<Expr *> args, FieldDecl *initializedFieldInUnion,
780780
Expr *arrayFiller) {
781781

782-
const AggValueSlot dest =
783-
ensureSlot(cgf.getLoc(e->getSourceRange()), e->getType());
782+
const mlir::Location loc = cgf.getLoc(e->getSourceRange());
783+
const AggValueSlot dest = ensureSlot(loc, e->getType());
784784

785785
if (e->getType()->isConstantArrayType()) {
786786
cir::ArrayType arrayTy =
@@ -819,10 +819,23 @@ void AggExprEmitter::visitCXXParenListOrInitListExpr(
819819
if (auto *cxxrd = dyn_cast<CXXRecordDecl>(record)) {
820820
assert(numInitElements >= cxxrd->getNumBases() &&
821821
"missing initializer for base class");
822-
if (cxxrd->getNumBases() > 0) {
823-
cgf.cgm.errorNYI(e->getSourceRange(),
824-
"visitCXXParenListOrInitListExpr base class init");
825-
return;
822+
for (auto &base : cxxrd->bases()) {
823+
assert(!base.isVirtual() && "should not see vbases here");
824+
CXXRecordDecl *baseRD = base.getType()->getAsCXXRecordDecl();
825+
Address address = cgf.getAddressOfDirectBaseInCompleteClass(
826+
loc, dest.getAddress(), cxxrd, baseRD,
827+
/*baseIsVirtual=*/false);
828+
assert(!cir::MissingFeatures::aggValueSlotGC());
829+
AggValueSlot aggSlot = AggValueSlot::forAddr(
830+
address, Qualifiers(), AggValueSlot::IsDestructed,
831+
AggValueSlot::IsNotAliased,
832+
cgf.getOverlapForBaseInit(cxxrd, baseRD, false));
833+
cgf.emitAggExpr(args[curInitIndex++], aggSlot);
834+
if (base.getType().isDestructedType()) {
835+
cgf.cgm.errorNYI(e->getSourceRange(),
836+
"push deferred deactivation cleanup");
837+
return;
838+
}
826839
}
827840
}
828841

clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,89 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorCall(
234234
return emitCall(fnInfo, callee, returnValue, args, nullptr, loc);
235235
}
236236

237+
static void emitNullBaseClassInitialization(CIRGenFunction &cgf,
238+
Address destPtr,
239+
const CXXRecordDecl *base) {
240+
if (base->isEmpty())
241+
return;
242+
243+
cgf.cgm.errorNYI(base->getSourceRange(),
244+
"emitNullBaseClassInitialization: not empty");
245+
}
246+
247+
void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
248+
AggValueSlot dest) {
249+
assert(!dest.isIgnored() && "Must have a destination!");
250+
const CXXConstructorDecl *cd = e->getConstructor();
251+
252+
// If we require zero initialization before (or instead of) calling the
253+
// constructor, as can be the case with a non-user-provided default
254+
// constructor, emit the zero initialization now, unless destination is
255+
// already zeroed.
256+
if (e->requiresZeroInitialization() && !dest.isZeroed()) {
257+
switch (e->getConstructionKind()) {
258+
case CXXConstructionKind::Delegating:
259+
case CXXConstructionKind::Complete:
260+
emitNullInitialization(getLoc(e->getSourceRange()), dest.getAddress(),
261+
e->getType());
262+
break;
263+
case CXXConstructionKind::VirtualBase:
264+
case CXXConstructionKind::NonVirtualBase:
265+
emitNullBaseClassInitialization(*this, dest.getAddress(),
266+
cd->getParent());
267+
break;
268+
}
269+
}
270+
271+
// If this is a call to a trivial default constructor, do nothing.
272+
if (cd->isTrivial() && cd->isDefaultConstructor())
273+
return;
274+
275+
// Elide the constructor if we're constructing from a temporary
276+
if (getLangOpts().ElideConstructors && e->isElidable()) {
277+
// FIXME: This only handles the simplest case, where the source object is
278+
// passed directly as the first argument to the constructor. This
279+
// should also handle stepping through implicit casts and conversion
280+
// sequences which involve two steps, with a conversion operator
281+
// follwed by a converting constructor.
282+
const Expr *srcObj = e->getArg(0);
283+
assert(srcObj->isTemporaryObject(getContext(), cd->getParent()));
284+
assert(
285+
getContext().hasSameUnqualifiedType(e->getType(), srcObj->getType()));
286+
emitAggExpr(srcObj, dest);
287+
return;
288+
}
289+
290+
if (const ArrayType *arrayType = getContext().getAsArrayType(e->getType())) {
291+
assert(!cir::MissingFeatures::sanitizers());
292+
emitCXXAggrConstructorCall(cd, arrayType, dest.getAddress(), e, false);
293+
} else {
294+
295+
clang::CXXCtorType type = Ctor_Complete;
296+
bool forVirtualBase = false;
297+
bool delegating = false;
298+
299+
switch (e->getConstructionKind()) {
300+
case CXXConstructionKind::Complete:
301+
type = Ctor_Complete;
302+
break;
303+
case CXXConstructionKind::Delegating:
304+
// We should be emitting a constructor; GlobalDecl will assert this
305+
type = curGD.getCtorType();
306+
delegating = true;
307+
break;
308+
case CXXConstructionKind::VirtualBase:
309+
forVirtualBase = true;
310+
[[fallthrough]];
311+
case CXXConstructionKind::NonVirtualBase:
312+
type = Ctor_Base;
313+
break;
314+
}
315+
316+
emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e);
317+
}
318+
}
319+
237320
static CharUnits calculateCookiePadding(CIRGenFunction &cgf,
238321
const CXXNewExpr *e) {
239322
if (!e->isArray())
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir --check-prefix=CIR %s
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 --check-prefix=LLVM %s
5+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll --check-prefix=OGCG %s
7+
8+
struct A {
9+
A() = default;
10+
A(int); // This constructor triggers the null base class initialization.
11+
};
12+
13+
struct B : A {
14+
};
15+
16+
void test_empty_base_null_init() {
17+
B{};
18+
}
19+
20+
// CIR: cir.func {{.*}} @_Z25test_empty_base_null_initv()
21+
// CIR-NEXT: %[[B_ADDR:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["agg.tmp.ensured"]
22+
// CIR-NEXT: %[[A_ADDR:.*]] = cir.base_class_addr %[[B_ADDR]] : !cir.ptr<!rec_B> nonnull [0] -> !cir.ptr<!rec_A>
23+
24+
// LLVM: define{{.*}} @_Z25test_empty_base_null_initv()
25+
// LLVM-NEXT: %[[B:.*]] = alloca %struct.B
26+
// LLVM-NEXT: ret void
27+
28+
// OGCG: define{{.*}} @_Z25test_empty_base_null_initv()
29+
// OGCG-NEXT: entry:
30+
// OGCG-NEXT: %[[B:.*]] = alloca %struct.B
31+
// OGCG-NEXT: ret void

0 commit comments

Comments
 (0)