Skip to content

Commit 77834a4

Browse files
authored
[CIR] Upstream support for emitting constructors (llvm#143639)
This change upstreams the code to emit simple constructor defintions.
1 parent 4039fdb commit 77834a4

File tree

12 files changed

+379
-16
lines changed

12 files changed

+379
-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 opCallPseudoDtor() { return false; }
@@ -226,6 +227,9 @@ struct MissingFeatures {
226227
static bool implicitConstructorArgs() { return false; }
227228
static bool intrinsics() { return false; }
228229
static bool attributeNoBuiltin() { return false; }
230+
static bool emitCtorPrologue() { return false; }
231+
static bool thunks() { return false; }
232+
static bool runCleanupsScope() { return false; }
229233

230234
// Missing types
231235
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: 11 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 gen type from a C++
41+
/// constructor/destructor 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,19 @@ class CIRGenCXXABI {
5559
return md->getParent();
5660
}
5761

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

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

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

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 54 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,54 @@ 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+
if (ctor->isDelegatingConstructor()) {
531+
// This will be handled in emitCtorPrologue, but we should emit a diagnostic
532+
// rather than silently fail to delegate.
533+
cgm.errorNYI(ctor->getSourceRange(),
534+
"emitConstructorBody: delegating ctor");
535+
return;
536+
}
537+
538+
// TODO(cir): propagate this result via mlir::logical result. Just unreachable
539+
// now just to have it handled.
540+
if (mlir::failed(emitStmt(body, true))) {
541+
cgm.errorNYI(ctor->getSourceRange(),
542+
"emitConstructorBody: emit body statement failed.");
543+
return;
544+
}
545+
}
546+
499547
/// Given a value of type T* that may not be to a complete object, construct
500548
/// an l-vlaue withi the natural pointee alignment of T.
501549
LValue CIRGenFunction::makeNaturalAlignPointeeAddrLValue(mlir::Value val,
@@ -522,16 +570,16 @@ clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd,
522570
cgm.getCXXABI().buildThisParam(*this, args);
523571
}
524572

525-
if (isa<CXXConstructorDecl>(fd))
526-
cgm.errorNYI(fd->getSourceRange(),
527-
"buildFunctionArgList: CXXConstructorDecl");
573+
if (const auto *cd = dyn_cast<CXXConstructorDecl>(fd))
574+
if (cd->getInheritedConstructor())
575+
cgm.errorNYI(fd->getSourceRange(),
576+
"buildFunctionArgList: inherited constructor");
528577

529578
for (auto *param : fd->parameters())
530579
args.push_back(param);
531580

532581
if (md && (isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md)))
533-
cgm.errorNYI(fd->getSourceRange(),
534-
"buildFunctionArgList: implicit structor params");
582+
assert(!cir::MissingFeatures::cxxabiStructorImplicitParam());
535583

536584
return retTy;
537585
}

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class CIRGenFunction : public CIRGenTypeCache {
6666
ImplicitParamDecl *cxxabiThisDecl = nullptr;
6767
mlir::Value cxxabiThisValue = nullptr;
6868
mlir::Value cxxThisValue = nullptr;
69+
clang::CharUnits cxxThisAlignment;
6970

7071
// Holds the Decl for the current outermost non-closure context
7172
const clang::Decl *curFuncDecl = nullptr;
@@ -473,6 +474,9 @@ class CIRGenFunction : public CIRGenTypeCache {
473474

474475
bool shouldNullCheckClassCastValue(const CastExpr *ce);
475476

477+
static bool
478+
isConstructorDelegationValid(const clang::CXXConstructorDecl *ctor);
479+
476480
LValue makeNaturalAlignPointeeAddrLValue(mlir::Value v, clang::QualType t);
477481

478482
/// Construct an address with the natural alignment of T. If a pointer to T
@@ -517,6 +521,7 @@ class CIRGenFunction : public CIRGenTypeCache {
517521
assert(cxxThisValue && "no 'this' value for this function");
518522
return cxxThisValue;
519523
}
524+
Address loadCXXThisAddress();
520525

521526
/// Get an appropriate 'undef' rvalue for the given type.
522527
/// TODO: What's the equivalent for MLIR? Currently we're only using this for
@@ -753,6 +758,8 @@ class CIRGenFunction : public CIRGenTypeCache {
753758

754759
LValue emitCompoundAssignmentLValue(const clang::CompoundAssignOperator *e);
755760

761+
void emitConstructorBody(FunctionArgList &args);
762+
756763
mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s);
757764

758765
void emitCXXConstructExpr(const clang::CXXConstructExpr *e,
@@ -841,6 +848,17 @@ class CIRGenFunction : public CIRGenTypeCache {
841848
mlir::Type condType,
842849
bool buildingTopLevelCase);
843850

851+
void emitDelegateCXXConstructorCall(const clang::CXXConstructorDecl *ctor,
852+
clang::CXXCtorType ctorType,
853+
const FunctionArgList &args,
854+
clang::SourceLocation loc);
855+
856+
/// We are performing a delegate call; that is, the current function is
857+
/// delegating to another one. Produce a r-value suitable for passing the
858+
/// given parameter.
859+
void emitDelegateCallArg(CallArgList &args, const clang::VarDecl *param,
860+
clang::SourceLocation loc);
861+
844862
/// Emit an `if` on a boolean condition to the specified blocks.
845863
/// FIXME: Based on the condition, this might try to simplify the codegen of
846864
/// the conditional based on the branch.

0 commit comments

Comments
 (0)