Skip to content

Commit aeaef15

Browse files
committed
[CIR] Upstream support for emitting constructors
This change upstreams the code to emit simple constructor defintions.
1 parent b9329fe commit aeaef15

File tree

12 files changed

+364
-16
lines changed

12 files changed

+364
-16
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ struct MissingFeatures {
8181
static bool opFuncCPUAndFeaturesAttributes() { return false; }
8282
static bool opFuncSection() { return false; }
8383
static bool opFuncSetComdat() { return false; }
84+
static bool opFuncAttributesForDefinition() { return false; }
8485

8586
// CallOp handling
8687
static bool opCallBuiltinFunc() { return false; }
@@ -225,6 +226,9 @@ struct MissingFeatures {
225226
static bool isMemcpyEquivalentSpecialMember() { return false; }
226227
static bool isTrivialCtorOrDtor() { return false; }
227228
static bool implicitConstructorArgs() { return false; }
229+
static bool emitCtorPrologue() { return false; }
230+
static bool thunks() { return false; }
231+
static bool runCleanupsScope() { return false; }
228232

229233
// Missing types
230234
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenCXX.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This contains code dealing with C++ code generation.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "CIRGenFunction.h"
14+
#include "CIRGenModule.h"
15+
16+
#include "clang/AST/GlobalDecl.h"
17+
#include "clang/CIR/MissingFeatures.h"
18+
19+
using namespace clang;
20+
using namespace clang::CIRGen;
21+
22+
cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) {
23+
const CIRGenFunctionInfo &fnInfo =
24+
getTypes().arrangeCXXStructorDeclaration(gd);
25+
cir::FuncType funcType = getTypes().getFunctionType(fnInfo);
26+
cir::FuncOp fn = getAddrOfCXXStructor(gd, &fnInfo, /*FnType=*/nullptr,
27+
/*DontDefer=*/true, ForDefinition);
28+
assert(!cir::MissingFeatures::opFuncLinkage());
29+
CIRGenFunction cgf{*this, builder};
30+
curCGF = &cgf;
31+
{
32+
mlir::OpBuilder::InsertionGuard guard(builder);
33+
cgf.generateCode(gd, fn, funcType);
34+
}
35+
curCGF = nullptr;
36+
37+
setNonAliasAttributes(gd, fn);
38+
assert(!cir::MissingFeatures::opFuncAttributesForDefinition());
39+
return fn;
40+
}

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ class CIRGenCXXABI {
3737

3838
void setCXXABIThisValue(CIRGenFunction &cgf, mlir::Value thisPtr);
3939

40+
/// Emit a single constructor/destructor with the gien type from a C++
41+
/// constructor Decl.
42+
virtual void emitCXXStructor(clang::GlobalDecl gd) = 0;
43+
4044
public:
4145
clang::ImplicitParamDecl *getThisDecl(CIRGenFunction &cgf) {
4246
return cgf.cxxabiThisDecl;
@@ -55,12 +59,29 @@ class CIRGenCXXABI {
5559
return md->getParent();
5660
}
5761

62+
/// Return whether the given global decl needs a VTT parameter.
63+
virtual bool needsVTTParameter(clang::GlobalDecl gd) { return false; }
64+
5865
/// Build a parameter variable suitable for 'this'.
5966
void buildThisParam(CIRGenFunction &cgf, FunctionArgList &params);
6067

6168
/// Loads the incoming C++ this pointer as it was passed by the caller.
6269
mlir::Value loadIncomingCXXThis(CIRGenFunction &cgf);
6370

71+
/// Emit constructor variants required by this ABI.
72+
virtual void emitCXXConstructors(const clang::CXXConstructorDecl *d) = 0;
73+
74+
/// Insert any ABI-specific implicit parameters into the parameter list for a
75+
/// function. This generally involves extra data for constructors and
76+
/// destructors.
77+
///
78+
/// ABIs may also choose to override the return type, which has been
79+
/// initialized with the type of 'this' if HasThisReturn(CGF.CurGD) is true or
80+
/// the formal return type of the function otherwise.
81+
virtual void addImplicitStructorParams(CIRGenFunction &cgf,
82+
clang::QualType &resTy,
83+
FunctionArgList &params) = 0;
84+
6485
/// Returns true if the given constructor or destructor is one of the kinds
6586
/// that the ABI says returns 'this' (only applies when called non-virtually
6687
/// for destructors).

clang/lib/CIR/CodeGen/CIRGenCall.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,47 @@ arrangeCIRFunctionInfo(CIRGenTypes &cgt, SmallVectorImpl<CanQualType> &prefix,
162162
return cgt.arrangeCIRFunctionInfo(resultType, prefix, required);
163163
}
164164

165+
void CIRGenFunction::emitDelegateCallArg(CallArgList &args,
166+
const VarDecl *param,
167+
SourceLocation loc) {
168+
// StartFunction converted the ABI-lowered parameter(s) into a local alloca.
169+
// We need to turn that into an r-value suitable for emitCall
170+
Address local = getAddrOfLocalVar(param);
171+
172+
QualType type = param->getType();
173+
174+
if (const auto *rd = type->getAsCXXRecordDecl()) {
175+
cgm.errorNYI(param->getSourceRange(),
176+
"emitDelegateCallArg: record argument");
177+
return;
178+
}
179+
180+
// GetAddrOfLocalVar returns a pointer-to-pointer for references, but the
181+
// argument needs to be the original pointer.
182+
if (type->isReferenceType()) {
183+
args.add(
184+
RValue::get(builder.createLoad(getLoc(param->getSourceRange()), local)),
185+
type);
186+
} else if (getLangOpts().ObjCAutoRefCount) {
187+
cgm.errorNYI(param->getSourceRange(),
188+
"emitDelegateCallArg: ObjCAutoRefCount");
189+
// For the most part, we just need to load the alloca, except that aggregate
190+
// r-values are actually pointers to temporaries.
191+
} else {
192+
cgm.errorNYI(param->getSourceRange(),
193+
"emitDelegateCallArg: convertTempToRValue");
194+
}
195+
196+
// Deactivate the cleanup for the callee-destructed param that was pushed.
197+
assert(!cir::MissingFeatures::thunks());
198+
if (type->isRecordType() &&
199+
type->castAs<RecordType>()->getDecl()->isParamDestroyedInCallee() &&
200+
param->needsDestruction(getContext())) {
201+
cgm.errorNYI(param->getSourceRange(),
202+
"emitDelegateCallArg: callee-destructed param");
203+
}
204+
}
205+
165206
static const CIRGenFunctionInfo &
166207
arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm,
167208
const CallArgList &args,

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,88 @@
2121
using namespace clang;
2222
using namespace clang::CIRGen;
2323

24+
/// Checks whether the given constructor is a valid subject for the
25+
/// complete-to-base constructor delgation optimization, i.e. emitting the
26+
/// complete constructor as a simple call to the base constructor.
27+
bool CIRGenFunction::isConstructorDelegationValid(
28+
const CXXConstructorDecl *ctor) {
29+
30+
// Currently we disable the optimization for classes with virtual bases
31+
// because (1) the address of parameter variables need to be consistent across
32+
// all initializers but (2) the delegate function call necessarily creates a
33+
// second copy of the parameter variable.
34+
//
35+
// The limiting example (purely theoretical AFAIK):
36+
// struct A { A(int &c) { c++; } };
37+
// struct A : virtual A {
38+
// B(int count) : A(count) { printf("%d\n", count); }
39+
// };
40+
// ...although even this example could in principle be emitted as a delegation
41+
// since the address of the parameter doesn't escape.
42+
if (ctor->getParent()->getNumVBases())
43+
return false;
44+
45+
// We also disable the optimization for variadic functions because it's
46+
// impossible to "re-pass" varargs.
47+
if (ctor->getType()->castAs<FunctionProtoType>()->isVariadic())
48+
return false;
49+
50+
// FIXME: Decide if we can do a delegation of a delegating constructor.
51+
if (ctor->isDelegatingConstructor())
52+
return false;
53+
54+
return true;
55+
}
56+
57+
Address CIRGenFunction::loadCXXThisAddress() {
58+
assert(curFuncDecl && "loading 'this' without a func declaration?");
59+
assert(isa<CXXMethodDecl>(curFuncDecl));
60+
61+
// Lazily compute CXXThisAlignment.
62+
if (cxxThisAlignment.isZero()) {
63+
// Just use the best known alignment for the parent.
64+
// TODO: if we're currently emitting a complete-object ctor/dtor, we can
65+
// always use the complete-object alignment.
66+
auto rd = cast<CXXMethodDecl>(curFuncDecl)->getParent();
67+
cxxThisAlignment = cgm.getClassPointerAlignment(rd);
68+
}
69+
70+
return Address(loadCXXThis(), cxxThisAlignment);
71+
}
72+
73+
void CIRGenFunction::emitDelegateCXXConstructorCall(
74+
const CXXConstructorDecl *ctor, CXXCtorType ctorType,
75+
const FunctionArgList &args, SourceLocation loc) {
76+
CallArgList delegateArgs;
77+
78+
FunctionArgList::const_iterator i = args.begin(), e = args.end();
79+
assert(i != e && "no parameters to constructor");
80+
81+
// this
82+
Address thisAddr = loadCXXThisAddress();
83+
delegateArgs.add(RValue::get(thisAddr.getPointer()), (*i)->getType());
84+
++i;
85+
86+
// FIXME: The location of the VTT parameter in the parameter list is specific
87+
// to the Itanium ABI and shouldn't be hardcoded here.
88+
if (cgm.getCXXABI().needsVTTParameter(curGD)) {
89+
cgm.errorNYI(loc, "emitDelegateCXXConstructorCall: VTT parameter");
90+
return;
91+
}
92+
93+
// Explicit arguments.
94+
for (; i != e; ++i) {
95+
const VarDecl *param = *i;
96+
// FIXME: per-argument source location
97+
emitDelegateCallArg(delegateArgs, param, loc);
98+
}
99+
100+
assert(!cir::MissingFeatures::sanitizers());
101+
102+
emitCXXConstructorCall(ctor, ctorType, /*ForVirtualBase=*/false,
103+
/*Delegating=*/true, thisAddr, delegateArgs, loc);
104+
}
105+
24106
Address CIRGenFunction::getAddressOfBaseClass(
25107
Address value, const CXXRecordDecl *derived,
26108
llvm::iterator_range<CastExpr::path_const_iterator> path,

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
465465
if (isa<CXXDestructorDecl>(funcDecl))
466466
getCIRGenModule().errorNYI(bodyRange, "C++ destructor definition");
467467
else if (isa<CXXConstructorDecl>(funcDecl))
468-
getCIRGenModule().errorNYI(bodyRange, "C++ constructor definition");
468+
emitConstructorBody(args);
469469
else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice &&
470470
funcDecl->hasAttr<CUDAGlobalAttr>())
471471
getCIRGenModule().errorNYI(bodyRange, "CUDA kernel");
@@ -496,6 +496,47 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
496496
return fn;
497497
}
498498

499+
void CIRGenFunction::emitConstructorBody(FunctionArgList &args) {
500+
assert(!cir::MissingFeatures::sanitizers());
501+
const auto *ctor = cast<CXXConstructorDecl>(curGD.getDecl());
502+
CXXCtorType ctorType = curGD.getCtorType();
503+
504+
assert((cgm.getTarget().getCXXABI().hasConstructorVariants() ||
505+
ctorType == Ctor_Complete) &&
506+
"can only generate complete ctor for this ABI");
507+
508+
if (ctorType == Ctor_Complete && isConstructorDelegationValid(ctor) &&
509+
cgm.getTarget().getCXXABI().hasConstructorVariants()) {
510+
emitDelegateCXXConstructorCall(ctor, Ctor_Base, args, ctor->getEndLoc());
511+
return;
512+
}
513+
514+
const FunctionDecl *definition = nullptr;
515+
Stmt *body = ctor->getBody(definition);
516+
assert(definition == ctor && "emitting wrong constructor body");
517+
518+
if (isa_and_nonnull<CXXTryStmt>(body)) {
519+
cgm.errorNYI(ctor->getSourceRange(), "emitConstructorBody: try body");
520+
return;
521+
}
522+
523+
assert(!cir::MissingFeatures::incrementProfileCounter());
524+
assert(!cir::MissingFeatures::runCleanupsScope());
525+
526+
// TODO: in restricted cases, we can emit the vbase initializers of a
527+
// complete ctor and then delegate to the base ctor.
528+
529+
assert(!cir::MissingFeatures::emitCtorPrologue());
530+
531+
// TODO(cir): propagate this result via mlir::logical result. Just unreachable
532+
// now just to have it handled.
533+
if (mlir::failed(emitStmt(body, true))) {
534+
cgm.errorNYI(ctor->getSourceRange(),
535+
"emitConstructorBody: emit body statement failed.");
536+
return;
537+
}
538+
}
539+
499540
/// Given a value of type T* that may not be to a complete object, construct
500541
/// an l-vlaue withi the natural pointee alignment of T.
501542
LValue CIRGenFunction::makeNaturalAlignPointeeAddrLValue(mlir::Value val,
@@ -522,16 +563,16 @@ clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd,
522563
cgm.getCXXABI().buildThisParam(*this, args);
523564
}
524565

525-
if (isa<CXXConstructorDecl>(fd))
526-
cgm.errorNYI(fd->getSourceRange(),
527-
"buildFunctionArgList: CXXConstructorDecl");
566+
if (const auto *cd = dyn_cast<CXXConstructorDecl>(fd))
567+
if (cd->getInheritedConstructor())
568+
cgm.errorNYI(fd->getSourceRange(),
569+
"buildFunctionArgList: inherited constructor");
528570

529571
for (auto *param : fd->parameters())
530572
args.push_back(param);
531573

532574
if (md && (isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md)))
533-
cgm.errorNYI(fd->getSourceRange(),
534-
"buildFunctionArgList: implicit structor params");
575+
cgm.getCXXABI().addImplicitStructorParams(*this, retTy, args);
535576

536577
return retTy;
537578
}

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class CIRGenFunction : public CIRGenTypeCache {
6060
ImplicitParamDecl *cxxabiThisDecl = nullptr;
6161
mlir::Value cxxabiThisValue = nullptr;
6262
mlir::Value cxxThisValue = nullptr;
63+
clang::CharUnits cxxThisAlignment;
6364

6465
// Holds the Decl for the current outermost non-closure context
6566
const clang::Decl *curFuncDecl = nullptr;
@@ -467,6 +468,9 @@ class CIRGenFunction : public CIRGenTypeCache {
467468

468469
bool shouldNullCheckClassCastValue(const CastExpr *ce);
469470

471+
static bool
472+
isConstructorDelegationValid(const clang::CXXConstructorDecl *ctor);
473+
470474
LValue makeNaturalAlignPointeeAddrLValue(mlir::Value v, clang::QualType t);
471475

472476
/// Construct an address with the natural alignment of T. If a pointer to T
@@ -511,6 +515,7 @@ class CIRGenFunction : public CIRGenTypeCache {
511515
assert(cxxThisValue && "no 'this' value for this function");
512516
return cxxThisValue;
513517
}
518+
Address loadCXXThisAddress();
514519

515520
/// Get an appropriate 'undef' rvalue for the given type.
516521
/// TODO: What's the equivalent for MLIR? Currently we're only using this for
@@ -742,6 +747,8 @@ class CIRGenFunction : public CIRGenTypeCache {
742747

743748
LValue emitCompoundAssignmentLValue(const clang::CompoundAssignOperator *e);
744749

750+
void emitConstructorBody(FunctionArgList &args);
751+
745752
mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s);
746753

747754
void emitCXXConstructExpr(const clang::CXXConstructExpr *e,
@@ -830,6 +837,17 @@ class CIRGenFunction : public CIRGenTypeCache {
830837
mlir::Type condType,
831838
bool buildingTopLevelCase);
832839

840+
void emitDelegateCXXConstructorCall(const clang::CXXConstructorDecl *ctor,
841+
clang::CXXCtorType ctorType,
842+
const FunctionArgList &args,
843+
clang::SourceLocation loc);
844+
845+
/// We are performing a delegate call; that is, the current function is
846+
/// delegating to another one. Produce a r-value suitable for passing the
847+
/// given parameter.
848+
void emitDelegateCallArg(CallArgList &args, const clang::VarDecl *param,
849+
clang::SourceLocation loc);
850+
833851
/// Emit an `if` on a boolean condition to the specified blocks.
834852
/// FIXME: Based on the condition, this might try to simplify the codegen of
835853
/// the conditional based on the branch.

0 commit comments

Comments
 (0)