Skip to content

Commit 389f3d9

Browse files
committed
[CIR] Add support for member initialization from constructors
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 9cb7545 commit 389f3d9

File tree

8 files changed

+340
-6
lines changed

8 files changed

+340
-6
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ struct MissingFeatures {
237237
static bool lowerAggregateLoadStore() { return false; }
238238
static bool dataLayoutTypeAllocSize() { return false; }
239239
static bool asmLabelAttr() { return false; }
240+
static bool ctorMemcpyizer() { return false; }
240241

241242
// Missing types
242243
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 149 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,135 @@ 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+
LValue lhs;
90+
91+
// If a base constructor is being emitted, create an LValue that has the
92+
// non-virtual alignment.
93+
if (cgf.curGD.getCtorType() == Ctor_Base)
94+
lhs = cgf.makeNaturalAlignPointeeAddrLValue(thisPtr, recordTy);
95+
else
96+
lhs = cgf.makeNaturalAlignAddrLValue(thisPtr, recordTy);
97+
98+
emitLValueForAnyFieldInitialization(cgf, memberInit, lhs);
99+
100+
// Special case: If we are in a copy or move constructor, and we are copying
101+
// an array off PODs or classes with tirival copy constructors, ignore the AST
102+
// and perform the copy we know is equivalent.
103+
// FIXME: This is hacky at best... if we had a bit more explicit information
104+
// in the AST, we could generalize it more easily.
105+
const ConstantArrayType *array =
106+
cgf.getContext().getAsConstantArrayType(fieldType);
107+
if (array && constructor->isDefaulted() &&
108+
constructor->isCopyOrMoveConstructor()) {
109+
QualType baseElementTy = cgf.getContext().getBaseElementType(array);
110+
// NOTE(cir): CodeGen allows record types to be memcpy'd if applicable,
111+
// whereas ClangIR wants to represent all object construction explicitly.
112+
if (!baseElementTy->isRecordType()) {
113+
cgf.cgm.errorNYI(memberInit->getSourceRange(),
114+
"emitMemberInitializer: array of non-record type");
115+
return;
116+
}
117+
}
118+
119+
cgf.emitInitializerForField(field, lhs, memberInit->getInit());
120+
}
121+
56122
/// This routine generates necessary code to initialize base classes and
57123
/// non-static data members belonging to this constructor.
58124
void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
59125
CXXCtorType ctorType,
60126
FunctionArgList &args) {
61-
if (cd->isDelegatingConstructor())
62-
return emitDelegatingCXXConstructorCall(cd, args);
127+
if (cd->isDelegatingConstructor()) {
128+
emitDelegatingCXXConstructorCall(cd, args);
129+
return;
130+
}
131+
132+
// If there are no member initializers, we can just return.
133+
if (cd->getNumCtorInitializers() == 0)
134+
return;
63135

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

71187
Address CIRGenFunction::loadCXXThisAddress() {
@@ -84,6 +200,34 @@ Address CIRGenFunction::loadCXXThisAddress() {
84200
return Address(loadCXXThis(), cxxThisAlignment);
85201
}
86202

203+
void CIRGenFunction::emitInitializerForField(FieldDecl *field, LValue lhs,
204+
Expr *init) {
205+
QualType fieldType = field->getType();
206+
switch (getEvaluationKind(fieldType)) {
207+
case cir::TEK_Scalar:
208+
if (lhs.isSimple()) {
209+
emitExprAsInit(init, field, lhs, false);
210+
} else {
211+
cgm.errorNYI(field->getSourceRange(),
212+
"emitInitializerForField: non-simple scalar");
213+
}
214+
break;
215+
case cir::TEK_Complex:
216+
cgm.errorNYI(field->getSourceRange(), "emitInitializerForField: complex");
217+
break;
218+
case cir::TEK_Aggregate: {
219+
cgm.errorNYI(field->getSourceRange(), "emitInitializerForField: aggregate");
220+
break;
221+
}
222+
}
223+
224+
// Ensure that we destroy this object if an exception is thrown later in the
225+
// constructor.
226+
QualType::DestructionKind dtorKind = fieldType.isDestructedType();
227+
(void)dtorKind;
228+
assert(!cir::MissingFeatures::requiresCleanups());
229+
}
230+
87231
void CIRGenFunction::emitDelegateCXXConstructorCall(
88232
const CXXConstructorDecl *ctor, CXXCtorType ctorType,
89233
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
@@ -840,6 +863,9 @@ class CIRGenFunction : public CIRGenTypeCache {
840863

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

866+
void emitInitializerForField(clang::FieldDecl *field, LValue lhs,
867+
clang::Expr *init);
868+
843869
mlir::Value emitPromotedScalarExpr(const Expr *e, QualType promotionType);
844870

845871
/// Emit the computation of the specified expression of scalar type.
@@ -934,6 +960,13 @@ class CIRGenFunction : public CIRGenTypeCache {
934960
LValue emitLValue(const clang::Expr *e);
935961
LValue emitLValueForField(LValue base, const clang::FieldDecl *field);
936962

963+
/// Like emitLValueForField, excpet that if the Field is a reference, this
964+
/// will return the address of the reference and not the address of the value
965+
/// stored in the reference.
966+
LValue emitLValueForFieldInitialization(LValue base,
967+
const clang::FieldDecl *field,
968+
llvm::StringRef fieldName);
969+
937970
LValue emitMemberExpr(const MemberExpr *e);
938971

939972
/// 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)