Skip to content

Commit 418b409

Browse files
authored
[CIR] Add support for member initialization from constructors (#144583)
Upstream the code to handle member variable initialization in a constructor. At this point only simple scalar values (including members of anonymous unions) are handled.
1 parent 7150b2c commit 418b409

File tree

8 files changed

+337
-6
lines changed

8 files changed

+337
-6
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ struct MissingFeatures {
242242
static bool builtinCallMathErrno() { return false; }
243243
static bool nonFineGrainedBitfields() { return false; }
244244
static bool armComputeVolatileBitfields() { return false; }
245+
static bool ctorMemcpyizer() { return false; }
245246

246247
// Missing types
247248
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 146 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,133 @@ bool CIRGenFunction::isConstructorDelegationValid(
5353
return true;
5454
}
5555

56+
static void emitLValueForAnyFieldInitialization(CIRGenFunction &cgf,
57+
CXXCtorInitializer *memberInit,
58+
LValue &lhs) {
59+
FieldDecl *field = memberInit->getAnyMember();
60+
if (memberInit->isIndirectMemberInitializer()) {
61+
// If we are initializing an anonymous union field, drill down to the field.
62+
IndirectFieldDecl *indirectField = memberInit->getIndirectMember();
63+
for (const auto *nd : indirectField->chain()) {
64+
auto *fd = cast<clang::FieldDecl>(nd);
65+
lhs = cgf.emitLValueForFieldInitialization(lhs, fd, fd->getName());
66+
}
67+
} else {
68+
lhs = cgf.emitLValueForFieldInitialization(lhs, field, field->getName());
69+
}
70+
}
71+
72+
static void emitMemberInitializer(CIRGenFunction &cgf,
73+
const CXXRecordDecl *classDecl,
74+
CXXCtorInitializer *memberInit,
75+
const CXXConstructorDecl *constructor,
76+
FunctionArgList &args) {
77+
assert(memberInit->isAnyMemberInitializer() &&
78+
"Mush have member initializer!");
79+
assert(memberInit->getInit() && "Must have initializer!");
80+
81+
assert(!cir::MissingFeatures::generateDebugInfo());
82+
83+
// non-static data member initializers
84+
FieldDecl *field = memberInit->getAnyMember();
85+
QualType fieldType = field->getType();
86+
87+
mlir::Value thisPtr = cgf.loadCXXThis();
88+
QualType recordTy = cgf.getContext().getTypeDeclType(classDecl);
89+
90+
// If a base constructor is being emitted, create an LValue that has the
91+
// non-virtual alignment.
92+
LValue lhs = (cgf.curGD.getCtorType() == Ctor_Base)
93+
? cgf.makeNaturalAlignPointeeAddrLValue(thisPtr, recordTy)
94+
: cgf.makeNaturalAlignAddrLValue(thisPtr, recordTy);
95+
96+
emitLValueForAnyFieldInitialization(cgf, memberInit, lhs);
97+
98+
// Special case: If we are in a copy or move constructor, and we are copying
99+
// an array off PODs or classes with trivial copy constructors, ignore the AST
100+
// and perform the copy we know is equivalent.
101+
// FIXME: This is hacky at best... if we had a bit more explicit information
102+
// in the AST, we could generalize it more easily.
103+
const ConstantArrayType *array =
104+
cgf.getContext().getAsConstantArrayType(fieldType);
105+
if (array && constructor->isDefaulted() &&
106+
constructor->isCopyOrMoveConstructor()) {
107+
QualType baseElementTy = cgf.getContext().getBaseElementType(array);
108+
// NOTE(cir): CodeGen allows record types to be memcpy'd if applicable,
109+
// whereas ClangIR wants to represent all object construction explicitly.
110+
if (!baseElementTy->isRecordType()) {
111+
cgf.cgm.errorNYI(memberInit->getSourceRange(),
112+
"emitMemberInitializer: array of non-record type");
113+
return;
114+
}
115+
}
116+
117+
cgf.emitInitializerForField(field, lhs, memberInit->getInit());
118+
}
119+
56120
/// This routine generates necessary code to initialize base classes and
57121
/// non-static data members belonging to this constructor.
58122
void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
59123
CXXCtorType ctorType,
60124
FunctionArgList &args) {
61-
if (cd->isDelegatingConstructor())
62-
return emitDelegatingCXXConstructorCall(cd, args);
125+
if (cd->isDelegatingConstructor()) {
126+
emitDelegatingCXXConstructorCall(cd, args);
127+
return;
128+
}
129+
130+
// If there are no member initializers, we can just return.
131+
if (cd->getNumCtorInitializers() == 0)
132+
return;
63133

64-
if (cd->getNumCtorInitializers() != 0) {
65-
// There's much more to do here.
66-
cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: any initializer");
134+
const CXXRecordDecl *classDecl = cd->getParent();
135+
136+
// This code doesn't use range-based iteration because we may need to emit
137+
// code between the virtual base initializers and the non-virtual base or
138+
// between the non-virtual base initializers and the member initializers.
139+
CXXConstructorDecl::init_const_iterator b = cd->init_begin(),
140+
e = cd->init_end();
141+
142+
// Virtual base initializers first, if any. They aren't needed if:
143+
// - This is a base ctor variant
144+
// - There are no vbases
145+
// - The class is abstract, so a complete object of it cannot be constructed
146+
//
147+
// The check for an abstract class is necessary because sema may not have
148+
// marked virtual base destructors referenced.
149+
bool constructVBases = ctorType != Ctor_Base &&
150+
classDecl->getNumVBases() != 0 &&
151+
!classDecl->isAbstract();
152+
if (constructVBases) {
153+
cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: virtual base");
67154
return;
68155
}
156+
157+
if ((*b)->isBaseInitializer()) {
158+
cgm.errorNYI(cd->getSourceRange(),
159+
"emitCtorPrologue: non-virtual base initializer");
160+
return;
161+
}
162+
163+
if (classDecl->isDynamicClass()) {
164+
cgm.errorNYI(cd->getSourceRange(),
165+
"emitCtorPrologue: initialize vtable pointers");
166+
return;
167+
}
168+
169+
// Finally, initialize class members.
170+
FieldConstructionScope fcs(*this, loadCXXThisAddress());
171+
// Classic codegen uses a special class to attempt to replace member
172+
// initializers with memcpy. We could possibly defer that to the
173+
// lowering or optimization phases to keep the memory accesses more
174+
// explicit. For now, we don't insert memcpy at all.
175+
assert(!cir::MissingFeatures::ctorMemcpyizer());
176+
for (; b != e; b++) {
177+
CXXCtorInitializer *member = (*b);
178+
assert(!member->isBaseInitializer());
179+
assert(member->isAnyMemberInitializer() &&
180+
"Delegating initializer on non-delegating constructor");
181+
emitMemberInitializer(*this, cd->getParent(), member, cd, args);
182+
}
69183
}
70184

71185
Address CIRGenFunction::loadCXXThisAddress() {
@@ -84,6 +198,33 @@ Address CIRGenFunction::loadCXXThisAddress() {
84198
return Address(loadCXXThis(), cxxThisAlignment);
85199
}
86200

201+
void CIRGenFunction::emitInitializerForField(FieldDecl *field, LValue lhs,
202+
Expr *init) {
203+
QualType fieldType = field->getType();
204+
switch (getEvaluationKind(fieldType)) {
205+
case cir::TEK_Scalar:
206+
if (lhs.isSimple())
207+
emitExprAsInit(init, field, lhs, false);
208+
else
209+
cgm.errorNYI(field->getSourceRange(),
210+
"emitInitializerForField: non-simple scalar");
211+
break;
212+
case cir::TEK_Complex:
213+
cgm.errorNYI(field->getSourceRange(), "emitInitializerForField: complex");
214+
break;
215+
case cir::TEK_Aggregate: {
216+
cgm.errorNYI(field->getSourceRange(), "emitInitializerForField: aggregate");
217+
break;
218+
}
219+
}
220+
221+
// Ensure that we destroy this object if an exception is thrown later in the
222+
// constructor.
223+
QualType::DestructionKind dtorKind = fieldType.isDestructedType();
224+
(void)dtorKind;
225+
assert(!cir::MissingFeatures::requiresCleanups());
226+
}
227+
87228
void CIRGenFunction::emitDelegateCXXConstructorCall(
88229
const CXXConstructorDecl *ctor, CXXCtorType ctorType,
89230
const FunctionArgList &args, SourceLocation loc) {

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,34 @@ LValue CIRGenFunction::emitLValueForField(LValue base, const FieldDecl *field) {
390390
return lv;
391391
}
392392

393+
LValue CIRGenFunction::emitLValueForFieldInitialization(
394+
LValue base, const clang::FieldDecl *field, llvm::StringRef fieldName) {
395+
QualType fieldType = field->getType();
396+
397+
if (!fieldType->isReferenceType())
398+
return emitLValueForField(base, field);
399+
400+
const CIRGenRecordLayout &layout =
401+
cgm.getTypes().getCIRGenRecordLayout(field->getParent());
402+
unsigned fieldIndex = layout.getCIRFieldNo(field);
403+
404+
Address v =
405+
emitAddrOfFieldStorage(base.getAddress(), field, fieldName, fieldIndex);
406+
407+
// Make sure that the address is pointing to the right type.
408+
mlir::Type memTy = convertTypeForMem(fieldType);
409+
v = builder.createElementBitCast(getLoc(field->getSourceRange()), v, memTy);
410+
411+
// TODO: Generate TBAA information that describes this access as a structure
412+
// member access and not just an access to an object of the field's type. This
413+
// should be similar to what we do in EmitLValueForField().
414+
LValueBaseInfo baseInfo = base.getBaseInfo();
415+
AlignmentSource fieldAlignSource = baseInfo.getAlignmentSource();
416+
LValueBaseInfo fieldBaseInfo(getFieldAlignmentSource(fieldAlignSource));
417+
assert(!cir::MissingFeatures::opTBAA());
418+
return makeAddrLValue(v, fieldType, fieldBaseInfo);
419+
}
420+
393421
mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) {
394422
// Bool has a different representation in memory than in registers,
395423
// but in ClangIR, it is simply represented as a cir.bool value.

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,15 @@ LValue CIRGenFunction::makeNaturalAlignPointeeAddrLValue(mlir::Value val,
550550
return makeAddrLValue(Address(val, align), ty, baseInfo);
551551
}
552552

553+
LValue CIRGenFunction::makeNaturalAlignAddrLValue(mlir::Value val,
554+
QualType ty) {
555+
LValueBaseInfo baseInfo;
556+
CharUnits alignment = cgm.getNaturalTypeAlignment(ty, &baseInfo);
557+
Address addr(val, convertTypeForMem(ty), alignment);
558+
assert(!cir::MissingFeatures::opTBAA());
559+
return makeAddrLValue(addr, ty, baseInfo);
560+
}
561+
553562
clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd,
554563
FunctionArgList &args) {
555564
const auto *fd = cast<FunctionDecl>(gd.getDecl());

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ class CIRGenFunction : public CIRGenTypeCache {
6868
mlir::Value cxxThisValue = nullptr;
6969
clang::CharUnits cxxThisAlignment;
7070

71+
/// The value of 'this' to sue when evaluating CXXDefaultInitExprs within this
72+
/// expression.
73+
Address cxxDefaultInitExprThis = Address::invalid();
74+
7175
// Holds the Decl for the current outermost non-closure context
7276
const clang::Decl *curFuncDecl = nullptr;
7377

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

497+
/// A scope within which we are constructing the fields of an object which
498+
/// might use a CXXDefaultInitExpr. This stashes away a 'this' value to use if
499+
/// we need to evaluate the CXXDefaultInitExpr within the evaluation.
500+
class FieldConstructionScope {
501+
public:
502+
FieldConstructionScope(CIRGenFunction &cgf, Address thisAddr)
503+
: cgf(cgf), oldCXXDefaultInitExprThis(cgf.cxxDefaultInitExprThis) {
504+
cgf.cxxDefaultInitExprThis = thisAddr;
505+
}
506+
~FieldConstructionScope() {
507+
cgf.cxxDefaultInitExprThis = oldCXXDefaultInitExprThis;
508+
}
509+
510+
private:
511+
CIRGenFunction &cgf;
512+
Address oldCXXDefaultInitExprThis;
513+
};
514+
493515
LValue makeNaturalAlignPointeeAddrLValue(mlir::Value v, clang::QualType t);
516+
LValue makeNaturalAlignAddrLValue(mlir::Value val, QualType ty);
494517

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

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

870+
void emitInitializerForField(clang::FieldDecl *field, LValue lhs,
871+
clang::Expr *init);
872+
847873
mlir::Value emitPromotedScalarExpr(const Expr *e, QualType promotionType);
848874

849875
/// Emit the computation of the specified expression of scalar type.
@@ -940,6 +966,13 @@ class CIRGenFunction : public CIRGenTypeCache {
940966
LValue emitLValue(const clang::Expr *e);
941967
LValue emitLValueForField(LValue base, const clang::FieldDecl *field);
942968

969+
/// Like emitLValueForField, excpet that if the Field is a reference, this
970+
/// will return the address of the reference and not the address of the value
971+
/// stored in the reference.
972+
LValue emitLValueForFieldInitialization(LValue base,
973+
const clang::FieldDecl *field,
974+
llvm::StringRef fieldName);
975+
943976
LValue emitMemberExpr(const MemberExpr *e);
944977

945978
/// Given an expression with a pointer type, emit the value and compute our

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
6868

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

clang/lib/CIR/CodeGen/CIRGenTypeCache.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace clang::CIRGen {
2222
/// during IR emission. It's initialized once in CodeGenModule's
2323
/// constructor and then copied around into new CIRGenFunction's.
2424
struct CIRGenTypeCache {
25-
CIRGenTypeCache() = default;
25+
CIRGenTypeCache() {}
2626

2727
// ClangIR void type
2828
cir::VoidType VoidTy;
@@ -49,8 +49,17 @@ struct CIRGenTypeCache {
4949
cir::FP80Type FP80Ty;
5050
cir::FP128Type FP128Ty;
5151

52+
/// intptr_t, size_t, and ptrdiff_t, which we assume are the same size.
53+
union {
54+
mlir::Type UIntPtrTy;
55+
mlir::Type SizeTy;
56+
};
57+
5258
mlir::Type PtrDiffTy;
5359

60+
/// void* in address space 0
61+
cir::PointerType VoidPtrTy;
62+
5463
/// The size and alignment of a pointer into the generic address space.
5564
union {
5665
unsigned char PointerAlignInBytes;

0 commit comments

Comments
 (0)