mirror of
https://github.com/intel/llvm.git
synced 2026-01-13 19:08:21 +08:00
[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.
This commit is contained in:
@@ -2134,79 +2134,6 @@ RValue CIRGenFunction::emitCXXMemberCallExpr(const CXXMemberCallExpr *ce,
|
||||
ce, md, returnValue, hasQualifier, qualifier, isArrow, base);
|
||||
}
|
||||
|
||||
void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
|
||||
AggValueSlot dest) {
|
||||
assert(!dest.isIgnored() && "Must have a destination!");
|
||||
const CXXConstructorDecl *cd = e->getConstructor();
|
||||
|
||||
// If we require zero initialization before (or instead of) calling the
|
||||
// constructor, as can be the case with a non-user-provided default
|
||||
// constructor, emit the zero initialization now, unless destination is
|
||||
// already zeroed.
|
||||
if (e->requiresZeroInitialization() && !dest.isZeroed()) {
|
||||
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, do nothing.
|
||||
if (cd->isTrivial() && cd->isDefaultConstructor())
|
||||
return;
|
||||
|
||||
// Elide the constructor if we're constructing from a temporary
|
||||
if (getLangOpts().ElideConstructors && e->isElidable()) {
|
||||
// FIXME: This only handles the simplest case, where the source object is
|
||||
// passed directly as the first argument to the constructor. This
|
||||
// should also handle stepping through implicit casts and conversion
|
||||
// sequences which involve two steps, with a conversion operator
|
||||
// follwed by a converting constructor.
|
||||
const Expr *srcObj = e->getArg(0);
|
||||
assert(srcObj->isTemporaryObject(getContext(), cd->getParent()));
|
||||
assert(
|
||||
getContext().hasSameUnqualifiedType(e->getType(), srcObj->getType()));
|
||||
emitAggExpr(srcObj, dest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const ArrayType *arrayType = getContext().getAsArrayType(e->getType())) {
|
||||
assert(!cir::MissingFeatures::sanitizers());
|
||||
emitCXXAggrConstructorCall(cd, arrayType, dest.getAddress(), e, false);
|
||||
} else {
|
||||
|
||||
clang::CXXCtorType type = Ctor_Complete;
|
||||
bool forVirtualBase = false;
|
||||
bool delegating = false;
|
||||
|
||||
switch (e->getConstructionKind()) {
|
||||
case CXXConstructionKind::Complete:
|
||||
type = Ctor_Complete;
|
||||
break;
|
||||
case CXXConstructionKind::Delegating:
|
||||
// We should be emitting a constructor; GlobalDecl will assert this
|
||||
type = curGD.getCtorType();
|
||||
delegating = true;
|
||||
break;
|
||||
case CXXConstructionKind::VirtualBase:
|
||||
forVirtualBase = true;
|
||||
[[fallthrough]];
|
||||
case CXXConstructionKind::NonVirtualBase:
|
||||
type = Ctor_Base;
|
||||
break;
|
||||
}
|
||||
|
||||
emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e);
|
||||
}
|
||||
}
|
||||
|
||||
RValue CIRGenFunction::emitReferenceBindingToExpr(const Expr *e) {
|
||||
// Emit the expression as an lvalue.
|
||||
LValue lv = emitLValue(e);
|
||||
|
||||
@@ -779,8 +779,8 @@ void AggExprEmitter::visitCXXParenListOrInitListExpr(
|
||||
Expr *e, ArrayRef<Expr *> args, FieldDecl *initializedFieldInUnion,
|
||||
Expr *arrayFiller) {
|
||||
|
||||
const AggValueSlot dest =
|
||||
ensureSlot(cgf.getLoc(e->getSourceRange()), e->getType());
|
||||
const mlir::Location loc = cgf.getLoc(e->getSourceRange());
|
||||
const AggValueSlot dest = ensureSlot(loc, e->getType());
|
||||
|
||||
if (e->getType()->isConstantArrayType()) {
|
||||
cir::ArrayType arrayTy =
|
||||
@@ -819,10 +819,23 @@ void AggExprEmitter::visitCXXParenListOrInitListExpr(
|
||||
if (auto *cxxrd = dyn_cast<CXXRecordDecl>(record)) {
|
||||
assert(numInitElements >= cxxrd->getNumBases() &&
|
||||
"missing initializer for base class");
|
||||
if (cxxrd->getNumBases() > 0) {
|
||||
cgf.cgm.errorNYI(e->getSourceRange(),
|
||||
"visitCXXParenListOrInitListExpr base class init");
|
||||
return;
|
||||
for (auto &base : cxxrd->bases()) {
|
||||
assert(!base.isVirtual() && "should not see vbases here");
|
||||
CXXRecordDecl *baseRD = base.getType()->getAsCXXRecordDecl();
|
||||
Address address = cgf.getAddressOfDirectBaseInCompleteClass(
|
||||
loc, dest.getAddress(), cxxrd, baseRD,
|
||||
/*baseIsVirtual=*/false);
|
||||
assert(!cir::MissingFeatures::aggValueSlotGC());
|
||||
AggValueSlot aggSlot = AggValueSlot::forAddr(
|
||||
address, Qualifiers(), AggValueSlot::IsDestructed,
|
||||
AggValueSlot::IsNotAliased,
|
||||
cgf.getOverlapForBaseInit(cxxrd, baseRD, false));
|
||||
cgf.emitAggExpr(args[curInitIndex++], aggSlot);
|
||||
if (base.getType().isDestructedType()) {
|
||||
cgf.cgm.errorNYI(e->getSourceRange(),
|
||||
"push deferred deactivation cleanup");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -234,6 +234,89 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorCall(
|
||||
return emitCall(fnInfo, callee, returnValue, args, nullptr, loc);
|
||||
}
|
||||
|
||||
static void emitNullBaseClassInitialization(CIRGenFunction &cgf,
|
||||
Address destPtr,
|
||||
const CXXRecordDecl *base) {
|
||||
if (base->isEmpty())
|
||||
return;
|
||||
|
||||
cgf.cgm.errorNYI(base->getSourceRange(),
|
||||
"emitNullBaseClassInitialization: not empty");
|
||||
}
|
||||
|
||||
void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
|
||||
AggValueSlot dest) {
|
||||
assert(!dest.isIgnored() && "Must have a destination!");
|
||||
const CXXConstructorDecl *cd = e->getConstructor();
|
||||
|
||||
// If we require zero initialization before (or instead of) calling the
|
||||
// constructor, as can be the case with a non-user-provided default
|
||||
// constructor, emit the zero initialization now, unless destination is
|
||||
// already zeroed.
|
||||
if (e->requiresZeroInitialization() && !dest.isZeroed()) {
|
||||
switch (e->getConstructionKind()) {
|
||||
case CXXConstructionKind::Delegating:
|
||||
case CXXConstructionKind::Complete:
|
||||
emitNullInitialization(getLoc(e->getSourceRange()), dest.getAddress(),
|
||||
e->getType());
|
||||
break;
|
||||
case CXXConstructionKind::VirtualBase:
|
||||
case CXXConstructionKind::NonVirtualBase:
|
||||
emitNullBaseClassInitialization(*this, dest.getAddress(),
|
||||
cd->getParent());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
// FIXME: This only handles the simplest case, where the source object is
|
||||
// passed directly as the first argument to the constructor. This
|
||||
// should also handle stepping through implicit casts and conversion
|
||||
// sequences which involve two steps, with a conversion operator
|
||||
// follwed by a converting constructor.
|
||||
const Expr *srcObj = e->getArg(0);
|
||||
assert(srcObj->isTemporaryObject(getContext(), cd->getParent()));
|
||||
assert(
|
||||
getContext().hasSameUnqualifiedType(e->getType(), srcObj->getType()));
|
||||
emitAggExpr(srcObj, dest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const ArrayType *arrayType = getContext().getAsArrayType(e->getType())) {
|
||||
assert(!cir::MissingFeatures::sanitizers());
|
||||
emitCXXAggrConstructorCall(cd, arrayType, dest.getAddress(), e, false);
|
||||
} else {
|
||||
|
||||
clang::CXXCtorType type = Ctor_Complete;
|
||||
bool forVirtualBase = false;
|
||||
bool delegating = false;
|
||||
|
||||
switch (e->getConstructionKind()) {
|
||||
case CXXConstructionKind::Complete:
|
||||
type = Ctor_Complete;
|
||||
break;
|
||||
case CXXConstructionKind::Delegating:
|
||||
// We should be emitting a constructor; GlobalDecl will assert this
|
||||
type = curGD.getCtorType();
|
||||
delegating = true;
|
||||
break;
|
||||
case CXXConstructionKind::VirtualBase:
|
||||
forVirtualBase = true;
|
||||
[[fallthrough]];
|
||||
case CXXConstructionKind::NonVirtualBase:
|
||||
type = Ctor_Base;
|
||||
break;
|
||||
}
|
||||
|
||||
emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e);
|
||||
}
|
||||
}
|
||||
|
||||
static CharUnits calculateCookiePadding(CIRGenFunction &cgf,
|
||||
const CXXNewExpr *e) {
|
||||
if (!e->isArray())
|
||||
|
||||
31
clang/test/CIR/CodeGen/ctor-null-init.cpp
Normal file
31
clang/test/CIR/CodeGen/ctor-null-init.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
|
||||
// RUN: FileCheck --input-file=%t.cir --check-prefix=CIR %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
|
||||
// RUN: FileCheck --input-file=%t-cir.ll --check-prefix=LLVM %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
|
||||
// RUN: FileCheck --input-file=%t.ll --check-prefix=OGCG %s
|
||||
|
||||
struct A {
|
||||
A() = default;
|
||||
A(int); // This constructor triggers the null base class initialization.
|
||||
};
|
||||
|
||||
struct B : A {
|
||||
};
|
||||
|
||||
void test_empty_base_null_init() {
|
||||
B{};
|
||||
}
|
||||
|
||||
// CIR: cir.func {{.*}} @_Z25test_empty_base_null_initv()
|
||||
// CIR-NEXT: %[[B_ADDR:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["agg.tmp.ensured"]
|
||||
// CIR-NEXT: %[[A_ADDR:.*]] = cir.base_class_addr %[[B_ADDR]] : !cir.ptr<!rec_B> nonnull [0] -> !cir.ptr<!rec_A>
|
||||
|
||||
// LLVM: define{{.*}} @_Z25test_empty_base_null_initv()
|
||||
// LLVM-NEXT: %[[B:.*]] = alloca %struct.B
|
||||
// LLVM-NEXT: ret void
|
||||
|
||||
// OGCG: define{{.*}} @_Z25test_empty_base_null_initv()
|
||||
// OGCG-NEXT: entry:
|
||||
// OGCG-NEXT: %[[B:.*]] = alloca %struct.B
|
||||
// OGCG-NEXT: ret void
|
||||
Reference in New Issue
Block a user