Skip to content

[CIR] Add support for member initialization from constructors #144583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ struct MissingFeatures {
static bool builtinCall() { return false; }
static bool builtinCallF128() { return false; }
static bool builtinCallMathErrno() { return false; }
static bool ctorMemcpyizer() { return false; }

// Missing types
static bool dataMemberType() { return false; }
Expand Down
151 changes: 146 additions & 5 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,133 @@ bool CIRGenFunction::isConstructorDelegationValid(
return true;
}

static void emitLValueForAnyFieldInitialization(CIRGenFunction &cgf,
CXXCtorInitializer *memberInit,
LValue &lhs) {
FieldDecl *field = memberInit->getAnyMember();
if (memberInit->isIndirectMemberInitializer()) {
// If we are initializing an anonymous union field, drill down to the field.
IndirectFieldDecl *indirectField = memberInit->getIndirectMember();
for (const auto *nd : indirectField->chain()) {
auto *fd = cast<clang::FieldDecl>(nd);
lhs = cgf.emitLValueForFieldInitialization(lhs, fd, fd->getName());
}
} else {
lhs = cgf.emitLValueForFieldInitialization(lhs, field, field->getName());
}
}

static void emitMemberInitializer(CIRGenFunction &cgf,
const CXXRecordDecl *classDecl,
CXXCtorInitializer *memberInit,
const CXXConstructorDecl *constructor,
FunctionArgList &args) {
assert(memberInit->isAnyMemberInitializer() &&
"Mush have member initializer!");
assert(memberInit->getInit() && "Must have initializer!");

assert(!cir::MissingFeatures::generateDebugInfo());

// non-static data member initializers
FieldDecl *field = memberInit->getAnyMember();
QualType fieldType = field->getType();

mlir::Value thisPtr = cgf.loadCXXThis();
QualType recordTy = cgf.getContext().getTypeDeclType(classDecl);

// If a base constructor is being emitted, create an LValue that has the
// non-virtual alignment.
LValue lhs = (cgf.curGD.getCtorType() == Ctor_Base)
? cgf.makeNaturalAlignPointeeAddrLValue(thisPtr, recordTy)
: cgf.makeNaturalAlignAddrLValue(thisPtr, recordTy);

emitLValueForAnyFieldInitialization(cgf, memberInit, lhs);

// Special case: If we are in a copy or move constructor, and we are copying
// an array off PODs or classes with trivial copy constructors, ignore the AST
// and perform the copy we know is equivalent.
// FIXME: This is hacky at best... if we had a bit more explicit information
// in the AST, we could generalize it more easily.
const ConstantArrayType *array =
cgf.getContext().getAsConstantArrayType(fieldType);
if (array && constructor->isDefaulted() &&
constructor->isCopyOrMoveConstructor()) {
QualType baseElementTy = cgf.getContext().getBaseElementType(array);
// NOTE(cir): CodeGen allows record types to be memcpy'd if applicable,
// whereas ClangIR wants to represent all object construction explicitly.
if (!baseElementTy->isRecordType()) {
cgf.cgm.errorNYI(memberInit->getSourceRange(),
"emitMemberInitializer: array of non-record type");
return;
}
}

cgf.emitInitializerForField(field, lhs, memberInit->getInit());
}

/// This routine generates necessary code to initialize base classes and
/// non-static data members belonging to this constructor.
void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
CXXCtorType ctorType,
FunctionArgList &args) {
if (cd->isDelegatingConstructor())
return emitDelegatingCXXConstructorCall(cd, args);
if (cd->isDelegatingConstructor()) {
emitDelegatingCXXConstructorCall(cd, args);
return;
}

// If there are no member initializers, we can just return.
if (cd->getNumCtorInitializers() == 0)
return;

if (cd->getNumCtorInitializers() != 0) {
// There's much more to do here.
cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: any initializer");
const CXXRecordDecl *classDecl = cd->getParent();

// This code doesn't use range-based iteration because we may need to emit
// code between the virtual base initializers and the non-virtual base or
// between the non-virtual base initializers and the member initializers.
CXXConstructorDecl::init_const_iterator b = cd->init_begin(),
e = cd->init_end();

// Virtual base initializers first, if any. They aren't needed if:
// - This is a base ctor variant
// - There are no vbases
// - The class is abstract, so a complete object of it cannot be constructed
//
// The check for an abstract class is necessary because sema may not have
// marked virtual base destructors referenced.
bool constructVBases = ctorType != Ctor_Base &&
classDecl->getNumVBases() != 0 &&
!classDecl->isAbstract();
if (constructVBases) {
cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: virtual base");
return;
}

if ((*b)->isBaseInitializer()) {
cgm.errorNYI(cd->getSourceRange(),
"emitCtorPrologue: non-virtual base initializer");
return;
}

if (classDecl->isDynamicClass()) {
cgm.errorNYI(cd->getSourceRange(),
"emitCtorPrologue: initialize vtable pointers");
return;
}

// Finally, initialize class members.
FieldConstructionScope fcs(*this, loadCXXThisAddress());
// Classic codegen uses a special class to attempt to replace member
// initializers with memcpy. We could possibly defer that to the
// lowering or optimization phases to keep the memory accesses more
// explicit. For now, we don't insert memcpy at all.
assert(!cir::MissingFeatures::ctorMemcpyizer());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

for (; b != e; b++) {
CXXCtorInitializer *member = (*b);
assert(!member->isBaseInitializer());
assert(member->isAnyMemberInitializer() &&
"Delegating initializer on non-delegating constructor");
emitMemberInitializer(*this, cd->getParent(), member, cd, args);
}
}

Address CIRGenFunction::loadCXXThisAddress() {
Expand All @@ -84,6 +198,33 @@ Address CIRGenFunction::loadCXXThisAddress() {
return Address(loadCXXThis(), cxxThisAlignment);
}

void CIRGenFunction::emitInitializerForField(FieldDecl *field, LValue lhs,
Expr *init) {
QualType fieldType = field->getType();
switch (getEvaluationKind(fieldType)) {
case cir::TEK_Scalar:
if (lhs.isSimple())
emitExprAsInit(init, field, lhs, false);
else
cgm.errorNYI(field->getSourceRange(),
"emitInitializerForField: non-simple scalar");
break;
case cir::TEK_Complex:
cgm.errorNYI(field->getSourceRange(), "emitInitializerForField: complex");
break;
case cir::TEK_Aggregate: {
cgm.errorNYI(field->getSourceRange(), "emitInitializerForField: aggregate");
break;
}
}

// Ensure that we destroy this object if an exception is thrown later in the
// constructor.
QualType::DestructionKind dtorKind = fieldType.isDestructedType();
(void)dtorKind;
assert(!cir::MissingFeatures::requiresCleanups());
}

void CIRGenFunction::emitDelegateCXXConstructorCall(
const CXXConstructorDecl *ctor, CXXCtorType ctorType,
const FunctionArgList &args, SourceLocation loc) {
Expand Down
28 changes: 28 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,34 @@ LValue CIRGenFunction::emitLValueForField(LValue base, const FieldDecl *field) {
return lv;
}

LValue CIRGenFunction::emitLValueForFieldInitialization(
LValue base, const clang::FieldDecl *field, llvm::StringRef fieldName) {
QualType fieldType = field->getType();

if (!fieldType->isReferenceType())
return emitLValueForField(base, field);

const CIRGenRecordLayout &layout =
cgm.getTypes().getCIRGenRecordLayout(field->getParent());
unsigned fieldIndex = layout.getCIRFieldNo(field);

Address v =
emitAddrOfFieldStorage(base.getAddress(), field, fieldName, fieldIndex);

// Make sure that the address is pointing to the right type.
mlir::Type memTy = convertTypeForMem(fieldType);
v = builder.createElementBitCast(getLoc(field->getSourceRange()), v, memTy);

// TODO: Generate TBAA information that describes this access as a structure
// member access and not just an access to an object of the field's type. This
// should be similar to what we do in EmitLValueForField().
LValueBaseInfo baseInfo = base.getBaseInfo();
AlignmentSource fieldAlignSource = baseInfo.getAlignmentSource();
LValueBaseInfo fieldBaseInfo(getFieldAlignmentSource(fieldAlignSource));
assert(!cir::MissingFeatures::opTBAA());
return makeAddrLValue(v, fieldType, fieldBaseInfo);
}

mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) {
// Bool has a different representation in memory than in registers,
// but in ClangIR, it is simply represented as a cir.bool value.
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,15 @@ LValue CIRGenFunction::makeNaturalAlignPointeeAddrLValue(mlir::Value val,
return makeAddrLValue(Address(val, align), ty, baseInfo);
}

LValue CIRGenFunction::makeNaturalAlignAddrLValue(mlir::Value val,
QualType ty) {
LValueBaseInfo baseInfo;
CharUnits alignment = cgm.getNaturalTypeAlignment(ty, &baseInfo);
Address addr(val, convertTypeForMem(ty), alignment);
assert(!cir::MissingFeatures::opTBAA());
return makeAddrLValue(addr, ty, baseInfo);
}

clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd,
FunctionArgList &args) {
const auto *fd = cast<FunctionDecl>(gd.getDecl());
Expand Down
33 changes: 33 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::Value cxxThisValue = nullptr;
clang::CharUnits cxxThisAlignment;

/// The value of 'this' to sue when evaluating CXXDefaultInitExprs within this
/// expression.
Address cxxDefaultInitExprThis = Address::invalid();

// Holds the Decl for the current outermost non-closure context
const clang::Decl *curFuncDecl = nullptr;

Expand Down Expand Up @@ -490,7 +494,26 @@ class CIRGenFunction : public CIRGenTypeCache {
static bool
isConstructorDelegationValid(const clang::CXXConstructorDecl *ctor);

/// A scope within which we are constructing the fields of an object which
/// might use a CXXDefaultInitExpr. This stashes away a 'this' value to use if
/// we need to evaluate the CXXDefaultInitExpr within the evaluation.
class FieldConstructionScope {
public:
FieldConstructionScope(CIRGenFunction &cgf, Address thisAddr)
: cgf(cgf), oldCXXDefaultInitExprThis(cgf.cxxDefaultInitExprThis) {
cgf.cxxDefaultInitExprThis = thisAddr;
}
~FieldConstructionScope() {
cgf.cxxDefaultInitExprThis = oldCXXDefaultInitExprThis;
}

private:
CIRGenFunction &cgf;
Address oldCXXDefaultInitExprThis;
};

LValue makeNaturalAlignPointeeAddrLValue(mlir::Value v, clang::QualType t);
LValue makeNaturalAlignAddrLValue(mlir::Value val, QualType ty);

/// Construct an address with the natural alignment of T. If a pointer to T
/// is expected to be signed, the pointer passed to this function must have
Expand Down Expand Up @@ -844,6 +867,9 @@ class CIRGenFunction : public CIRGenTypeCache {

mlir::LogicalResult emitFunctionBody(const clang::Stmt *body);

void emitInitializerForField(clang::FieldDecl *field, LValue lhs,
clang::Expr *init);

mlir::Value emitPromotedScalarExpr(const Expr *e, QualType promotionType);

/// Emit the computation of the specified expression of scalar type.
Expand Down Expand Up @@ -938,6 +964,13 @@ class CIRGenFunction : public CIRGenTypeCache {
LValue emitLValue(const clang::Expr *e);
LValue emitLValueForField(LValue base, const clang::FieldDecl *field);

/// Like emitLValueForField, excpet that if the Field is a reference, this
/// will return the address of the reference and not the address of the value
/// stored in the reference.
LValue emitLValueForFieldInitialization(LValue base,
const clang::FieldDecl *field,
llvm::StringRef fieldName);

LValue emitMemberExpr(const MemberExpr *e);

/// Given an expression with a pointer type, emit the value and compute our
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,

// Initialize cached types
VoidTy = cir::VoidType::get(&getMLIRContext());
VoidPtrTy = cir::PointerType::get(VoidTy);
SInt8Ty = cir::IntType::get(&getMLIRContext(), 8, /*isSigned=*/true);
SInt16Ty = cir::IntType::get(&getMLIRContext(), 16, /*isSigned=*/true);
SInt32Ty = cir::IntType::get(&getMLIRContext(), 32, /*isSigned=*/true);
Expand All @@ -94,6 +95,9 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
// TODO(CIR): Should be updated once TypeSizeInfoAttr is upstreamed
const unsigned sizeTypeSize =
astContext.getTypeSize(astContext.getSignedSizeType());
// In CIRGenTypeCache, UIntPtrTy and SizeType are fields of the same union
UIntPtrTy =
cir::IntType::get(&getMLIRContext(), sizeTypeSize, /*isSigned=*/false);
PtrDiffTy =
cir::IntType::get(&getMLIRContext(), sizeTypeSize, /*isSigned=*/true);

Expand Down
11 changes: 10 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenTypeCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace clang::CIRGen {
/// during IR emission. It's initialized once in CodeGenModule's
/// constructor and then copied around into new CIRGenFunction's.
struct CIRGenTypeCache {
CIRGenTypeCache() = default;
CIRGenTypeCache() {}

// ClangIR void type
cir::VoidType VoidTy;
Expand All @@ -49,8 +49,17 @@ struct CIRGenTypeCache {
cir::FP80Type FP80Ty;
cir::FP128Type FP128Ty;

/// intptr_t, size_t, and ptrdiff_t, which we assume are the same size.
union {
mlir::Type UIntPtrTy;
mlir::Type SizeTy;
};

mlir::Type PtrDiffTy;

/// void* in address space 0
cir::PointerType VoidPtrTy;

/// The size and alignment of a pointer into the generic address space.
union {
unsigned char PointerAlignInBytes;
Expand Down
Loading
Loading