Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,9 @@ def CIR_RecordType : CIR_Type<"Record", "record", [
}
llvm_unreachable("Invalid value for RecordType::getKind()");
}
mlir::Type getElementType(size_t idx) {
return getMembers()[idx];
}
std::string getPrefixedName() {
return getKindAsStr() + "." + getName().getValue().str();
}
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,16 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
&ignored);
return fv.bitwiseIsEqual(fpVal);
}
if (const auto recordVal = mlir::dyn_cast<cir::ConstRecordAttr>(attr)) {
for (const auto elt : recordVal.getMembers()) {
// FIXME(cir): the record's ID should not be considered a member.
if (mlir::isa<mlir::StringAttr>(elt))
continue;
if (!isNullValue(elt))
return false;
}
return true;
}

if (const auto arrayVal = mlir::dyn_cast<cir::ConstArrayAttr>(attr)) {
if (mlir::isa<mlir::StringAttr>(arrayVal.getElts()))
Expand Down
42 changes: 40 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,46 @@ static void emitNullBaseClassInitialization(CIRGenFunction &cgf,
if (base->isEmpty())
return;

cgf.cgm.errorNYI(base->getSourceRange(),
"emitNullBaseClassInitialization: not empty");
const ASTRecordLayout &layout = cgf.getContext().getASTRecordLayout(base);
CharUnits nvSize = layout.getNonVirtualSize();

// We cannot simply zero-initialize the entire base sub-object if vbptrs are
// present, they are initialized by the most derived class before calling the
// constructor.
SmallVector<std::pair<CharUnits, CharUnits>, 1> stores;
stores.emplace_back(CharUnits::Zero(), nvSize);

// Each store is split by the existence of a vbptr.
// TODO(cir): This only needs handling for the MS CXXABI.
assert(!cir::MissingFeatures::msabi());

// If the type contains a pointer to data member we can't memset it to zero.
// Instead, create a null constant and copy it to the destination.
// TODO: there are other patterns besides zero that we can usefully memset,
// like -1, which happens to be the pattern used by member-pointers.
// TODO: isZeroInitializable can be over-conservative in the case where a
// virtual base contains a member pointer.
mlir::TypedAttr nullConstantForBase = cgf.cgm.emitNullConstantForBase(base);
if (!cgf.getBuilder().isNullValue(nullConstantForBase)) {
cgf.cgm.errorNYI(
base->getSourceRange(),
"emitNullBaseClassInitialization: base constant is not null");
} else {
// Otherwise, just memset the whole thing to zero. This is legal
// because in LLVM, all default initializers (other than the ones we just
// handled above) are guaranteed to have a bit pattern of all zeros.
// TODO(cir): When the MS CXXABI is supported, we will need to iterate over
// `stores` and create a separate memset for each one. For now, we know that
// there will only be one store and it will begin at offset zero, so that
// simplifies this code considerably.
assert(stores.size() == 1 && "Expected only one store");
assert(stores[0].first == CharUnits::Zero() &&
"Expected store to begin at offset zero");
CIRGenBuilderTy builder = cgf.getBuilder();
mlir::Location loc = cgf.getLoc(base->getBeginLoc());
builder.createStore(loc, builder.getConstant(loc, nullConstantForBase),
destPtr);
}
}

void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
Expand Down
120 changes: 112 additions & 8 deletions clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1521,6 +1521,101 @@ ConstantEmitter::~ConstantEmitter() {
"not finalized after being initialized for non-abstract emission");
}

static mlir::TypedAttr emitNullConstantForBase(CIRGenModule &cgm,
mlir::Type baseType,
const CXXRecordDecl *baseDecl);

static mlir::TypedAttr emitNullConstant(CIRGenModule &cgm, const RecordDecl *rd,
bool asCompleteObject) {
const CIRGenRecordLayout &layout = cgm.getTypes().getCIRGenRecordLayout(rd);
mlir::Type ty = (asCompleteObject ? layout.getCIRType()
: layout.getBaseSubobjectCIRType());
auto recordTy = mlir::cast<cir::RecordType>(ty);

unsigned numElements = recordTy.getNumElements();
SmallVector<mlir::Attribute> elements(numElements);

auto *cxxrd = dyn_cast<CXXRecordDecl>(rd);
// Fill in all the bases.
if (cxxrd) {
for (const CXXBaseSpecifier &base : cxxrd->bases()) {
if (base.isVirtual()) {
// Ignore virtual bases; if we're laying out for a complete
// object, we'll lay these out later.
continue;
}

const auto *baseDecl = base.getType()->castAsCXXRecordDecl();
// Ignore empty bases.
if (isEmptyRecordForLayout(cgm.getASTContext(), base.getType()) ||
cgm.getASTContext()
.getASTRecordLayout(baseDecl)
.getNonVirtualSize()
.isZero())
continue;

unsigned fieldIndex = layout.getNonVirtualBaseCIRFieldNo(baseDecl);
mlir::Type baseType = recordTy.getElementType(fieldIndex);
elements[fieldIndex] = emitNullConstantForBase(cgm, baseType, baseDecl);
}
}

// Fill in all the fields.
for (const FieldDecl *field : rd->fields()) {
// Fill in non-bitfields. (Bitfields always use a zero pattern, which we
// will fill in later.)
if (!field->isBitField() &&
!isEmptyFieldForLayout(cgm.getASTContext(), field)) {
unsigned fieldIndex = layout.getCIRFieldNo(field);
elements[fieldIndex] = cgm.emitNullConstantAttr(field->getType());
}

// For unions, stop after the first named field.
if (rd->isUnion()) {
if (field->getIdentifier())
break;
if (const auto *fieldRD = field->getType()->getAsRecordDecl())
if (fieldRD->findFirstNamedDataMember())
break;
}
}

// Fill in the virtual bases, if we're working with the complete object.
if (cxxrd && asCompleteObject) {
for ([[maybe_unused]] const CXXBaseSpecifier &vbase : cxxrd->vbases()) {
cgm.errorNYI(vbase.getSourceRange(), "emitNullConstant: virtual base");
return {};
}
}

// Now go through all other fields and zero them out.
for (unsigned i = 0; i != numElements; ++i) {
if (!elements[i]) {
cgm.errorNYI(rd->getSourceRange(), "emitNullConstant: field not zeroed");
return {};
}
}

mlir::MLIRContext *mlirContext = recordTy.getContext();
return cir::ConstRecordAttr::get(recordTy,
mlir::ArrayAttr::get(mlirContext, elements));
}

/// Emit the null constant for a base subobject.
static mlir::TypedAttr emitNullConstantForBase(CIRGenModule &cgm,
mlir::Type baseType,
const CXXRecordDecl *baseDecl) {
const CIRGenRecordLayout &baseLayout =
cgm.getTypes().getCIRGenRecordLayout(baseDecl);

// Just zero out bases that don't have any pointer to data members.
if (baseLayout.isZeroInitializableAsBase())
return cgm.getBuilder().getZeroInitAttr(baseType);

// Otherwise, we can just use its null constant.
return emitNullConstant(cgm, baseDecl, /*asCompleteObject=*/false);
}

mlir::Attribute ConstantEmitter::tryEmitPrivateForVarInit(const VarDecl &d) {
// Make a quick check if variable can be default NULL initialized
// and avoid going through rest of code which may do, for c++11,
Expand Down Expand Up @@ -1820,23 +1915,32 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
}

mlir::Value CIRGenModule::emitNullConstant(QualType t, mlir::Location loc) {
if (t->getAs<PointerType>()) {
return builder.getNullPtr(getTypes().convertTypeForMem(t), loc);
}
return builder.getConstant(loc, emitNullConstantAttr(t));
}

mlir::TypedAttr CIRGenModule::emitNullConstantAttr(QualType t) {
if (t->getAs<PointerType>())
return builder.getConstNullPtrAttr(getTypes().convertTypeForMem(t));

if (getTypes().isZeroInitializable(t))
return builder.getNullValue(getTypes().convertTypeForMem(t), loc);
return builder.getZeroInitAttr(getTypes().convertTypeForMem(t));

if (getASTContext().getAsConstantArrayType(t)) {
errorNYI("CIRGenModule::emitNullConstant ConstantArrayType");
errorNYI("CIRGenModule::emitNullConstantAttr ConstantArrayType");
return {};
}

if (t->isRecordType())
errorNYI("CIRGenModule::emitNullConstant RecordType");
if (const RecordType *rt = t->getAs<RecordType>())
return ::emitNullConstant(*this, rt->getDecl(), /*asCompleteObject=*/true);

assert(t->isMemberDataPointerType() &&
"Should only see pointers to data members here!");

errorNYI("CIRGenModule::emitNullConstant unsupported type");
errorNYI("CIRGenModule::emitNullConstantAttr unsupported type");
return {};
}

mlir::TypedAttr
CIRGenModule::emitNullConstantForBase(const CXXRecordDecl *record) {
return ::emitNullConstant(*this, record, false);
}
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,12 @@ class CIRGenModule : public CIRGenTypeCache {
/// expression of the given type.
mlir::Value emitNullConstant(QualType t, mlir::Location loc);

mlir::TypedAttr emitNullConstantAttr(QualType t);

/// Return a null constant appropriate for zero-initializing a base class with
/// the given type. This is usually, but not always, an LLVM null constant.
mlir::TypedAttr emitNullConstantForBase(const CXXRecordDecl *record);

llvm::StringRef getMangledName(clang::GlobalDecl gd);

void emitTentativeDefinition(const VarDecl *d);
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ class CIRGenRecordLayout {
return fieldIdxMap.lookup(fd);
}

unsigned getNonVirtualBaseCIRFieldNo(const CXXRecordDecl *rd) const {
assert(nonVirtualBases.count(rd) && "Invalid non-virtual base!");
return nonVirtualBases.lookup(rd);
}

/// Check whether this struct can be C++ zero-initialized
/// with a zeroinitializer.
bool isZeroInitializable() const { return zeroInitializable; }
Expand Down
60 changes: 60 additions & 0 deletions clang/test/CIR/CodeGen/ctor-null-init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,63 @@ void test_empty_base_null_init() {
// OGCG-NEXT: entry:
// OGCG-NEXT: %[[B:.*]] = alloca %struct.B
// OGCG-NEXT: ret void


struct C {
int c;
C() = default;
C(int); // This constructor triggers the null base class initialization.
};

struct D : C {
};

void test_non_empty_base_null_init() {
D{};
}

// CIR: cir.func {{.*}} @_Z29test_non_empty_base_null_initv()
// CIR: %[[TMP:.*]] = cir.alloca !rec_D, !cir.ptr<!rec_D>, ["agg.tmp.ensured"]
// CIR: %[[BASE:.*]] = cir.base_class_addr %[[TMP]] : !cir.ptr<!rec_D> nonnull [0] -> !cir.ptr<!rec_C>
// CIR: %[[ZERO:.*]] = cir.const #cir.const_record<{#cir.int<0> : !s32i}> : !rec_C
// CIR: cir.store{{.*}} %[[ZERO]], %[[BASE]]

// LLVM: define{{.*}} void @_Z29test_non_empty_base_null_initv()
// LLVM: %[[TMP:.*]] = alloca %struct.D
// LLVM: store %struct.C zeroinitializer, ptr %[[TMP]]

// OGCG: define {{.*}} void @_Z29test_non_empty_base_null_initv()
// OGCG: %[[TMP:.*]] = alloca %struct.D
// OGCG: %[[BASE:.*]] = getelementptr inbounds i8, ptr %[[TMP]], i64 0
// OGCG: call void @llvm.memset.p0.i64(ptr{{.*}} %[[BASE]], i8 0, i64 4, i1 false)

struct E {
int e;
};

struct F : E {
F() = default;
F(int);
};

struct G : F {
};

void test_base_chain_null_init() {
G{};
}

// CIR: cir.func {{.*}} @_Z25test_base_chain_null_initv()
// CIR: %[[TMP:.*]] = cir.alloca !rec_G, !cir.ptr<!rec_G>, ["agg.tmp.ensured"]
// CIR: %[[BASE:.*]] = cir.base_class_addr %[[TMP]] : !cir.ptr<!rec_G> nonnull [0] -> !cir.ptr<!rec_F>
// CIR: %[[ZERO:.*]] = cir.const #cir.const_record<{#cir.zero : !rec_E}> : !rec_F
// CIR: cir.store{{.*}} %[[ZERO]], %[[BASE]]

// LLVM: define{{.*}} void @_Z25test_base_chain_null_initv()
// LLVM: %[[TMP:.*]] = alloca %struct.G
// LLVM: store %struct.F zeroinitializer, ptr %[[TMP]]

// OGCG: define {{.*}} void @_Z25test_base_chain_null_initv()
// OGCG: %[[TMP:.*]] = alloca %struct.G
// OGCG: %[[BASE:.*]] = getelementptr inbounds i8, ptr %[[TMP]], i64 0
// OGCG: call void @llvm.memset.p0.i64(ptr{{.*}} %[[BASE]], i8 0, i64 4, i1 false)