Skip to content

Commit b9329fe

Browse files
authored
[CIR] Upstream support for calling constructors (#143579)
This change adds support for calling C++ constructors. The support for actually defining a constructor is still missing and will be added in a later change.
1 parent ad479dd commit b9329fe

File tree

10 files changed

+336
-8
lines changed

10 files changed

+336
-8
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ struct MissingFeatures {
222222
static bool instrumentation() { return false; }
223223
static bool cleanupAfterErrorDiags() { return false; }
224224
static bool cxxRecordStaticMembers() { return false; }
225+
static bool isMemcpyEquivalentSpecialMember() { return false; }
226+
static bool isTrivialCtorOrDtor() { return false; }
227+
static bool implicitConstructorArgs() { return false; }
225228

226229
// Missing types
227230
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenCall.cpp

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const {
6060
return *this;
6161
}
6262

63+
/// Returns the canonical formal type of the given C++ method.
64+
static CanQual<FunctionProtoType> getFormalType(const CXXMethodDecl *md) {
65+
return md->getType()
66+
->getCanonicalTypeUnqualified()
67+
.getAs<FunctionProtoType>();
68+
}
69+
6370
/// Adds the formal parameters in FPT to the given prefix. If any parameter in
6471
/// FPT has pass_object_size_attrs, then we'll add parameters for those, too.
6572
/// TODO(cir): this should be shared with LLVM codegen
@@ -76,6 +83,48 @@ static void appendParameterTypes(const CIRGenTypes &cgt,
7683
cgt.getCGModule().errorNYI("appendParameterTypes: hasExtParameterInfos");
7784
}
7885

86+
const CIRGenFunctionInfo &
87+
CIRGenTypes::arrangeCXXStructorDeclaration(GlobalDecl gd) {
88+
auto *md = cast<CXXMethodDecl>(gd.getDecl());
89+
90+
llvm::SmallVector<CanQualType, 16> argTypes;
91+
argTypes.push_back(deriveThisType(md->getParent(), md));
92+
93+
bool passParams = true;
94+
95+
if (auto *cd = dyn_cast<CXXConstructorDecl>(md)) {
96+
// A base class inheriting constructor doesn't get forwarded arguments
97+
// needed to construct a virtual base (or base class thereof)
98+
if (cd->getInheritedConstructor())
99+
cgm.errorNYI(cd->getSourceRange(),
100+
"arrangeCXXStructorDeclaration: inheriting constructor");
101+
}
102+
103+
CanQual<FunctionProtoType> fpt = getFormalType(md);
104+
105+
if (passParams)
106+
appendParameterTypes(*this, argTypes, fpt);
107+
108+
assert(!cir::MissingFeatures::implicitConstructorArgs());
109+
110+
RequiredArgs required =
111+
(passParams && md->isVariadic() ? RequiredArgs(argTypes.size())
112+
: RequiredArgs::All);
113+
114+
CanQualType resultType = theCXXABI.hasThisReturn(gd) ? argTypes.front()
115+
: theCXXABI.hasMostDerivedReturn(gd)
116+
? astContext.VoidPtrTy
117+
: astContext.VoidTy;
118+
119+
assert(!theCXXABI.hasThisReturn(gd) &&
120+
"Please send PR with a test and remove this");
121+
122+
assert(!cir::MissingFeatures::opCallCIRGenFuncInfoExtParamInfo());
123+
assert(!cir::MissingFeatures::opCallFnInfoOpts());
124+
125+
return arrangeCIRFunctionInfo(resultType, argTypes, required);
126+
}
127+
79128
/// Derives the 'this' type for CIRGen purposes, i.e. ignoring method CVR
80129
/// qualification. Either or both of `rd` and `md` may be null. A null `rd`
81130
/// indicates that there is no meaningful 'this' type, and a null `md` can occur
@@ -103,13 +152,13 @@ CanQualType CIRGenTypes::deriveThisType(const CXXRecordDecl *rd,
103152
/// top of any implicit parameters already stored.
104153
static const CIRGenFunctionInfo &
105154
arrangeCIRFunctionInfo(CIRGenTypes &cgt, SmallVectorImpl<CanQualType> &prefix,
106-
CanQual<FunctionProtoType> ftp) {
155+
CanQual<FunctionProtoType> fpt) {
107156
assert(!cir::MissingFeatures::opCallFnInfoOpts());
108157
RequiredArgs required =
109-
RequiredArgs::getFromProtoWithExtraSlots(ftp, prefix.size());
158+
RequiredArgs::getFromProtoWithExtraSlots(fpt, prefix.size());
110159
assert(!cir::MissingFeatures::opCallExtParameterInfo());
111-
appendParameterTypes(cgt, prefix, ftp);
112-
CanQualType resultType = ftp->getReturnType().getUnqualifiedType();
160+
appendParameterTypes(cgt, prefix, fpt);
161+
CanQualType resultType = fpt->getReturnType().getUnqualifiedType();
113162
return cgt.arrangeCIRFunctionInfo(resultType, prefix, required);
114163
}
115164

@@ -141,6 +190,44 @@ arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm,
141190
return cgt.arrangeCIRFunctionInfo(retType, argTypes, required);
142191
}
143192

193+
/// Arrange a call to a C++ method, passing the given arguments.
194+
///
195+
/// passProtoArgs indicates whether `args` has args for the parameters in the
196+
/// given CXXConstructorDecl.
197+
const CIRGenFunctionInfo &CIRGenTypes::arrangeCXXConstructorCall(
198+
const CallArgList &args, const CXXConstructorDecl *d, CXXCtorType ctorKind,
199+
bool passProtoArgs) {
200+
201+
// FIXME: Kill copy.
202+
llvm::SmallVector<CanQualType, 16> argTypes;
203+
for (const auto &arg : args)
204+
argTypes.push_back(astContext.getCanonicalParamType(arg.ty));
205+
206+
assert(!cir::MissingFeatures::implicitConstructorArgs());
207+
// +1 for implicit this, which should always be args[0]
208+
unsigned totalPrefixArgs = 1;
209+
210+
CanQual<FunctionProtoType> fpt = getFormalType(d);
211+
RequiredArgs required =
212+
passProtoArgs
213+
? RequiredArgs::getFromProtoWithExtraSlots(fpt, totalPrefixArgs)
214+
: RequiredArgs::All;
215+
216+
GlobalDecl gd(d, ctorKind);
217+
if (theCXXABI.hasThisReturn(gd))
218+
cgm.errorNYI(d->getSourceRange(),
219+
"arrangeCXXConstructorCall: hasThisReturn");
220+
if (theCXXABI.hasMostDerivedReturn(gd))
221+
cgm.errorNYI(d->getSourceRange(),
222+
"arrangeCXXConstructorCall: hasMostDerivedReturn");
223+
CanQualType resultType = astContext.VoidTy;
224+
225+
assert(!cir::MissingFeatures::opCallFnInfoOpts());
226+
assert(!cir::MissingFeatures::opCallCIRGenFuncInfoExtParamInfo());
227+
228+
return arrangeCIRFunctionInfo(resultType, argTypes, required);
229+
}
230+
144231
/// Arrange a call to a C++ method, passing the given arguments.
145232
///
146233
/// numPrefixArgs is the number of the ABI-specific prefix arguments we have. It
@@ -198,7 +285,7 @@ CIRGenTypes::arrangeCXXMethodDeclaration(const CXXMethodDecl *md) {
198285
/// constructor or destructor.
199286
const CIRGenFunctionInfo &
200287
CIRGenTypes::arrangeCXXMethodType(const CXXRecordDecl *rd,
201-
const FunctionProtoType *ftp,
288+
const FunctionProtoType *fpt,
202289
const CXXMethodDecl *md) {
203290
llvm::SmallVector<CanQualType, 16> argTypes;
204291

@@ -208,7 +295,7 @@ CIRGenTypes::arrangeCXXMethodType(const CXXRecordDecl *rd,
208295
assert(!cir::MissingFeatures::opCallFnInfoOpts());
209296
return ::arrangeCIRFunctionInfo(
210297
*this, argTypes,
211-
ftp->getCanonicalTypeUnqualified().getAs<FunctionProtoType>());
298+
fpt->getCanonicalTypeUnqualified().getAs<FunctionProtoType>());
212299
}
213300

214301
/// Arrange the argument and result information for the declaration or

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "CIRGenCXXABI.h"
1314
#include "CIRGenFunction.h"
1415

16+
#include "clang/AST/ExprCXX.h"
1517
#include "clang/AST/RecordLayout.h"
18+
#include "clang/AST/Type.h"
1619
#include "clang/CIR/MissingFeatures.h"
1720

1821
using namespace clang;
@@ -63,3 +66,74 @@ Address CIRGenFunction::getAddressOfBaseClass(
6366

6467
return value;
6568
}
69+
70+
void CIRGenFunction::emitCXXConstructorCall(const clang::CXXConstructorDecl *d,
71+
clang::CXXCtorType type,
72+
bool forVirtualBase,
73+
bool delegating,
74+
AggValueSlot thisAVS,
75+
const clang::CXXConstructExpr *e) {
76+
CallArgList args;
77+
Address thisAddr = thisAVS.getAddress();
78+
QualType thisType = d->getThisType();
79+
mlir::Value thisPtr = thisAddr.getPointer();
80+
81+
assert(!cir::MissingFeatures::addressSpace());
82+
83+
args.add(RValue::get(thisPtr), thisType);
84+
85+
// In LLVM Codegen: If this is a trivial constructor, just emit what's needed.
86+
// If this is a union copy constructor, we must emit a memcpy, because the AST
87+
// does not model that copy.
88+
assert(!cir::MissingFeatures::isMemcpyEquivalentSpecialMember());
89+
90+
const FunctionProtoType *fpt = d->getType()->castAs<FunctionProtoType>();
91+
92+
assert(!cir::MissingFeatures::opCallArgEvaluationOrder());
93+
94+
emitCallArgs(args, fpt, e->arguments(), e->getConstructor(),
95+
/*ParamsToSkip=*/0);
96+
97+
assert(!cir::MissingFeatures::sanitizers());
98+
emitCXXConstructorCall(d, type, forVirtualBase, delegating, thisAddr, args,
99+
e->getExprLoc());
100+
}
101+
102+
void CIRGenFunction::emitCXXConstructorCall(
103+
const CXXConstructorDecl *d, CXXCtorType type, bool forVirtualBase,
104+
bool delegating, Address thisAddr, CallArgList &args, SourceLocation loc) {
105+
106+
const CXXRecordDecl *crd = d->getParent();
107+
108+
// If this is a call to a trivial default constructor:
109+
// In LLVM: do nothing.
110+
// In CIR: emit as a regular call, other later passes should lower the
111+
// ctor call into trivial initialization.
112+
assert(!cir::MissingFeatures::isTrivialCtorOrDtor());
113+
114+
assert(!cir::MissingFeatures::isMemcpyEquivalentSpecialMember());
115+
116+
bool passPrototypeArgs = true;
117+
118+
// Check whether we can actually emit the constructor before trying to do so.
119+
if (d->getInheritedConstructor()) {
120+
cgm.errorNYI(d->getSourceRange(),
121+
"emitCXXConstructorCall: inherited constructor");
122+
return;
123+
}
124+
125+
// Insert any ABI-specific implicit constructor arguments.
126+
assert(!cir::MissingFeatures::implicitConstructorArgs());
127+
128+
// Emit the call.
129+
auto calleePtr = cgm.getAddrOfCXXStructor(GlobalDecl(d, type));
130+
const CIRGenFunctionInfo &info = cgm.getTypes().arrangeCXXConstructorCall(
131+
args, d, type, passPrototypeArgs);
132+
CIRGenCallee callee = CIRGenCallee::forDirect(calleePtr, GlobalDecl(d, type));
133+
cir::CIRCallOpInterface c;
134+
emitCall(info, callee, ReturnValueSlot(), args, &c, getLoc(loc));
135+
136+
if (cgm.getCodeGenOpts().OptimizationLevel != 0 && !crd->isDynamicClass() &&
137+
type != Ctor_Base && cgm.getCodeGenOpts().StrictVTablePointers)
138+
cgm.errorNYI(d->getSourceRange(), "vtable assumption loads");
139+
}

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,6 +1393,57 @@ RValue CIRGenFunction::emitCXXMemberCallExpr(const CXXMemberCallExpr *ce,
13931393
ce, md, returnValue, hasQualifier, qualifier, isArrow, base);
13941394
}
13951395

1396+
void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
1397+
AggValueSlot dest) {
1398+
assert(!dest.isIgnored() && "Must have a destination!");
1399+
const CXXConstructorDecl *cd = e->getConstructor();
1400+
1401+
// If we require zero initialization before (or instead of) calling the
1402+
// constructor, as can be the case with a non-user-provided default
1403+
// constructor, emit the zero initialization now, unless destination is
1404+
// already zeroed.
1405+
if (e->requiresZeroInitialization() && !dest.isZeroed()) {
1406+
cgm.errorNYI(e->getSourceRange(),
1407+
"emitCXXConstructExpr: requires initialization");
1408+
return;
1409+
}
1410+
1411+
// If this is a call to a trivial default constructor:
1412+
// In LLVM: do nothing.
1413+
// In CIR: emit as a regular call, other later passes should lower the
1414+
// ctor call into trivial initialization.
1415+
1416+
// Elide the constructor if we're constructing from a temporary
1417+
if (getLangOpts().ElideConstructors && e->isElidable()) {
1418+
cgm.errorNYI(e->getSourceRange(),
1419+
"emitCXXConstructExpr: elidable constructor");
1420+
return;
1421+
}
1422+
1423+
if (getContext().getAsArrayType(e->getType())) {
1424+
cgm.errorNYI(e->getSourceRange(), "emitCXXConstructExpr: array type");
1425+
return;
1426+
}
1427+
1428+
clang::CXXCtorType type = Ctor_Complete;
1429+
bool forVirtualBase = false;
1430+
bool delegating = false;
1431+
1432+
switch (e->getConstructionKind()) {
1433+
case CXXConstructionKind::Complete:
1434+
type = Ctor_Complete;
1435+
break;
1436+
case CXXConstructionKind::Delegating:
1437+
case CXXConstructionKind::VirtualBase:
1438+
case CXXConstructionKind::NonVirtualBase:
1439+
cgm.errorNYI(e->getSourceRange(),
1440+
"emitCXXConstructExpr: other construction kind");
1441+
return;
1442+
}
1443+
1444+
emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e);
1445+
}
1446+
13961447
RValue CIRGenFunction::emitReferenceBindingToExpr(const Expr *e) {
13971448
// Emit the expression as an lvalue.
13981449
LValue lv = emitLValue(e);

clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
5151
void Visit(Expr *e) { StmtVisitor<AggExprEmitter>::Visit(e); }
5252

5353
void VisitInitListExpr(InitListExpr *e);
54+
void VisitCXXConstructExpr(const CXXConstructExpr *e);
5455

5556
void visitCXXParenListOrInitListExpr(Expr *e, ArrayRef<Expr *> args,
5657
FieldDecl *initializedFieldInUnion,
@@ -213,6 +214,11 @@ void AggExprEmitter::emitInitializationToLValue(Expr *e, LValue lv) {
213214
}
214215
}
215216

217+
void AggExprEmitter::VisitCXXConstructExpr(const CXXConstructExpr *e) {
218+
AggValueSlot slot = ensureSlot(cgf.getLoc(e->getSourceRange()), e->getType());
219+
cgf.emitCXXConstructExpr(e, slot);
220+
}
221+
216222
void AggExprEmitter::emitNullInitializationToLValue(mlir::Location loc,
217223
LValue lv) {
218224
const QualType type = lv.getType();

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,19 @@ class CIRGenFunction : public CIRGenTypeCache {
744744

745745
mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s);
746746

747+
void emitCXXConstructExpr(const clang::CXXConstructExpr *e,
748+
AggValueSlot dest);
749+
750+
void emitCXXConstructorCall(const clang::CXXConstructorDecl *d,
751+
clang::CXXCtorType type, bool forVirtualBase,
752+
bool delegating, AggValueSlot thisAVS,
753+
const clang::CXXConstructExpr *e);
754+
755+
void emitCXXConstructorCall(const clang::CXXConstructorDecl *d,
756+
clang::CXXCtorType type, bool forVirtualBase,
757+
bool delegating, Address thisAddr,
758+
CallArgList &args, clang::SourceLocation loc);
759+
747760
mlir::LogicalResult emitCXXForRangeStmt(const CXXForRangeStmt &s,
748761
llvm::ArrayRef<const Attr *> attrs);
749762

0 commit comments

Comments
 (0)