Skip to content

Commit 19dcec9

Browse files
authored
[CIR] Support inline C++ member function definitions (#142484)
This change upstreams the code to support emitting inline C++ function definitions, including the ASTConsumer handler for inline definitions and the code to load the 'this' pointer. This necessitates introducing the Itanium CXXABI class. No other CXXABI subclasses are supported at this time. The Itanium CXXABI is used for AppleARM64, which will require its own handler for a few special cases (such as array cookies) later.
1 parent cc68367 commit 19dcec9

14 files changed

+326
-23
lines changed

clang/include/clang/CIR/CIRGenerator.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,24 +43,49 @@ class CIRGenerator : public clang::ASTConsumer {
4343

4444
const clang::CodeGenOptions &codeGenOpts;
4545

46+
unsigned handlingTopLevelDecls;
47+
48+
/// Use this when emitting decls to block re-entrant decl emission. It will
49+
/// emit all deferred decls on scope exit. Set EmitDeferred to false if decl
50+
/// emission must be deferred longer, like at the end of a tag definition.
51+
struct HandlingTopLevelDeclRAII {
52+
CIRGenerator &self;
53+
bool emitDeferred;
54+
HandlingTopLevelDeclRAII(CIRGenerator &self, bool emitDeferred = true)
55+
: self{self}, emitDeferred{emitDeferred} {
56+
++self.handlingTopLevelDecls;
57+
}
58+
~HandlingTopLevelDeclRAII() {
59+
unsigned Level = --self.handlingTopLevelDecls;
60+
if (Level == 0 && emitDeferred)
61+
self.emitDeferredDecls();
62+
}
63+
};
64+
4665
protected:
4766
std::unique_ptr<mlir::MLIRContext> mlirContext;
4867
std::unique_ptr<clang::CIRGen::CIRGenModule> cgm;
4968

69+
private:
70+
llvm::SmallVector<clang::FunctionDecl *, 8> deferredInlineMemberFuncDefs;
71+
5072
public:
5173
CIRGenerator(clang::DiagnosticsEngine &diags,
5274
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
5375
const clang::CodeGenOptions &cgo);
5476
~CIRGenerator() override;
5577
void Initialize(clang::ASTContext &astContext) override;
5678
bool HandleTopLevelDecl(clang::DeclGroupRef group) override;
79+
void HandleInlineFunctionDefinition(clang::FunctionDecl *d) override;
5780
void CompleteTentativeDefinition(clang::VarDecl *d) override;
5881

5982
mlir::ModuleOp getModule() const;
6083
mlir::MLIRContext &getMLIRContext() { return *mlirContext; };
6184
const mlir::MLIRContext &getMLIRContext() const { return *mlirContext; };
6285

6386
bool verifyModule() const;
87+
88+
void emitDeferredDecls();
6489
};
6590

6691
} // namespace cir

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,15 @@ struct MissingFeatures {
137137
static bool recordZeroInit() { return false; }
138138
static bool zeroSizeRecordMembers() { return false; }
139139

140-
// Misc
140+
// CXXABI
141141
static bool cxxABI() { return false; }
142+
static bool cxxabiThisAlignment() { return false; }
143+
static bool cxxabiUseARMMethodPtrABI() { return false; }
144+
static bool cxxabiUseARMGuardVarABI() { return false; }
145+
static bool cxxabiAppleARM64CXXABI() { return false; }
146+
static bool cxxabiStructorImplicitParam() { return false; }
147+
148+
// Misc
142149
static bool cirgenABIInfo() { return false; }
143150
static bool abiArgInfo() { return false; }
144151
static bool tryEmitAsConstant() { return false; }
@@ -187,7 +194,6 @@ struct MissingFeatures {
187194
static bool typeChecks() { return false; }
188195
static bool lambdaFieldToName() { return false; }
189196
static bool updateCompletedType() { return false; }
190-
static bool targetSpecificCXXABI() { return false; }
191197
static bool moduleNameHash() { return false; }
192198
static bool constantFoldSwitchStatement() { return false; }
193199
static bool cudaSupport() { return false; }
@@ -196,13 +202,12 @@ struct MissingFeatures {
196202
static bool constEmitterVectorILE() { return false; }
197203
static bool needsGlobalCtorDtor() { return false; }
198204
static bool emitTypeCheck() { return false; }
199-
static bool cxxabiThisDecl() { return false; }
200-
static bool cxxabiThisAlignment() { return false; }
201205
static bool writebacks() { return false; }
202206
static bool cleanupsToDeactivate() { return false; }
203207
static bool stackBase() { return false; }
204208
static bool deferredDecls() { return false; }
205209
static bool setTargetAttributes() { return false; }
210+
static bool coverageMapping() { return false; }
206211

207212
// Missing types
208213
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,24 @@ void CIRGenCXXABI::buildThisParam(CIRGenFunction &cgf,
3333
&cgm.getASTContext().Idents.get("this"),
3434
md->getThisType(), ImplicitParamKind::CXXThis);
3535
params.push_back(thisDecl);
36-
37-
// Classic codegen save thisDecl in CodeGenFunction::CXXABIThisDecl, but it
38-
// doesn't seem to be needed in CIRGen.
39-
assert(!cir::MissingFeatures::cxxabiThisDecl());
36+
cgf.cxxabiThisDecl = thisDecl;
4037

4138
// Classic codegen computes the alignment of thisDecl and saves it in
42-
// CodeGenFunction::CXXABIThisAlignment, but it doesn't seem to be needed in
43-
// CIRGen.
39+
// CodeGenFunction::CXXABIThisAlignment, but it is only used in emitTypeCheck
40+
// in CodeGenFunction::StartFunction().
4441
assert(!cir::MissingFeatures::cxxabiThisAlignment());
4542
}
43+
44+
mlir::Value CIRGenCXXABI::loadIncomingCXXThis(CIRGenFunction &cgf) {
45+
ImplicitParamDecl *vd = getThisDecl(cgf);
46+
Address addr = cgf.getAddrOfLocalVar(vd);
47+
return cgf.getBuilder().create<cir::LoadOp>(
48+
cgf.getLoc(vd->getLocation()), addr.getElementType(), addr.getPointer());
49+
}
50+
51+
void CIRGenCXXABI::setCXXABIThisValue(CIRGenFunction &cgf,
52+
mlir::Value thisPtr) {
53+
/// Initialize the 'this' slot.
54+
assert(getThisDecl(cgf) && "no 'this' variable for function");
55+
cgf.cxxabiThisValue = thisPtr;
56+
}

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define LLVM_CLANG_LIB_CIR_CIRGENCXXABI_H
1616

1717
#include "CIRGenCall.h"
18+
#include "CIRGenFunction.h"
1819
#include "CIRGenModule.h"
1920

2021
#include "clang/AST/Mangle.h"
@@ -34,7 +35,17 @@ class CIRGenCXXABI {
3435
: cgm(cgm), mangleContext(cgm.getASTContext().createMangleContext()) {}
3536
virtual ~CIRGenCXXABI();
3637

38+
void setCXXABIThisValue(CIRGenFunction &cgf, mlir::Value thisPtr);
39+
3740
public:
41+
clang::ImplicitParamDecl *getThisDecl(CIRGenFunction &cgf) {
42+
return cgf.cxxabiThisDecl;
43+
}
44+
45+
/// Emit the ABI-specific prolog for the function
46+
virtual void emitInstanceFunctionProlog(SourceLocation Loc,
47+
CIRGenFunction &cgf) = 0;
48+
3849
/// Get the type of the implicit "this" parameter used by a method. May return
3950
/// zero if no specific type is applicable, e.g. if the ABI expects the "this"
4051
/// parameter to point to some artificial offset in a complete object due to
@@ -47,6 +58,9 @@ class CIRGenCXXABI {
4758
/// Build a parameter variable suitable for 'this'.
4859
void buildThisParam(CIRGenFunction &cgf, FunctionArgList &params);
4960

61+
/// Loads the incoming C++ this pointer as it was passed by the caller.
62+
mlir::Value loadIncomingCXXThis(CIRGenFunction &cgf);
63+
5064
/// Returns true if the given constructor or destructor is one of the kinds
5165
/// that the ABI says returns 'this' (only applies when called non-virtually
5266
/// for destructors).

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
569569

570570
mlir::Value VisitUnaryLNot(const UnaryOperator *e);
571571

572+
mlir::Value VisitCXXThisExpr(CXXThisExpr *te) { return cgf.loadCXXThis(); }
573+
572574
/// Emit a conversion from the specified type to the specified destination
573575
/// type, both of which are CIR scalar types.
574576
/// TODO: do we need ScalarConversionOpts here? Should be done in another

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,9 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
343343

344344
curFn = fn;
345345

346-
const auto *fd = dyn_cast_or_null<FunctionDecl>(gd.getDecl());
346+
const Decl *d = gd.getDecl();
347+
const auto *fd = dyn_cast_or_null<FunctionDecl>(d);
348+
curFuncDecl = d->getNonClosureContext();
347349

348350
mlir::Block *entryBB = &fn.getBlocks().front();
349351
builder.setInsertionPointToStart(entryBB);
@@ -385,6 +387,24 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
385387
if (!returnType->isVoidType())
386388
emitAndUpdateRetAlloca(returnType, getLoc(fd->getBody()->getEndLoc()),
387389
getContext().getTypeAlignInChars(returnType));
390+
391+
if (isa_and_nonnull<CXXMethodDecl>(d) &&
392+
cast<CXXMethodDecl>(d)->isInstance()) {
393+
cgm.getCXXABI().emitInstanceFunctionProlog(loc, *this);
394+
395+
const auto *md = cast<CXXMethodDecl>(d);
396+
if (md->getParent()->isLambda() && md->getOverloadedOperator() == OO_Call) {
397+
cgm.errorNYI(loc, "lambda call operator");
398+
} else {
399+
// Not in a lambda; just use 'this' from the method.
400+
// FIXME: Should we generate a new load for each use of 'this'? The fast
401+
// register allocator would be happier...
402+
cxxThisValue = cxxabiThisValue;
403+
}
404+
405+
assert(!cir::MissingFeatures::sanitizers());
406+
assert(!cir::MissingFeatures::emitTypeCheck());
407+
}
388408
}
389409

390410
void CIRGenFunction::finishFunction(SourceLocation endLoc) {}

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ class CIRGenFunction : public CIRGenTypeCache {
5555
/// The compiler-generated variable that holds the return value.
5656
std::optional<mlir::Value> fnRetAlloca;
5757

58+
/// CXXThisDecl - When generating code for a C++ member function,
59+
/// this will hold the implicit 'this' declaration.
60+
ImplicitParamDecl *cxxabiThisDecl = nullptr;
61+
mlir::Value cxxabiThisValue = nullptr;
62+
mlir::Value cxxThisValue = nullptr;
63+
64+
// Holds the Decl for the current outermost non-closure context
65+
const clang::Decl *curFuncDecl = nullptr;
66+
5867
/// The function for which code is currently being generated.
5968
cir::FuncOp curFn;
6069

@@ -307,6 +316,22 @@ class CIRGenFunction : public CIRGenTypeCache {
307316
return LValue::makeAddr(addr, ty, baseInfo);
308317
}
309318

319+
/// Return the address of a local variable.
320+
Address getAddrOfLocalVar(const clang::VarDecl *vd) {
321+
auto it = localDeclMap.find(vd);
322+
assert(it != localDeclMap.end() &&
323+
"Invalid argument to getAddrOfLocalVar(), no decl!");
324+
return it->second;
325+
}
326+
327+
/// Load the value for 'this'. This function is only valid while generating
328+
/// code for an C++ member function.
329+
/// FIXME(cir): this should return a mlir::Value!
330+
mlir::Value loadCXXThis() {
331+
assert(cxxThisValue && "no 'this' value for this function");
332+
return cxxThisValue;
333+
}
334+
310335
/// Get an appropriate 'undef' rvalue for the given type.
311336
/// TODO: What's the equivalent for MLIR? Currently we're only using this for
312337
/// void types so it just returns RValue::get(nullptr) but it'll need
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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 provides C++ code generation targeting the Itanium C++ ABI. The class
10+
// in this file generates structures that follow the Itanium C++ ABI, which is
11+
// documented at:
12+
// https://itanium-cxx-abi.github.io/cxx-abi/abi.html
13+
// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
14+
//
15+
// It also supports the closely-related ARM ABI, documented at:
16+
// https://developer.arm.com/documentation/ihi0041/g/
17+
//
18+
//===----------------------------------------------------------------------===//
19+
20+
#include "CIRGenCXXABI.h"
21+
#include "CIRGenFunction.h"
22+
23+
#include "clang/AST/GlobalDecl.h"
24+
#include "llvm/Support/ErrorHandling.h"
25+
26+
using namespace clang;
27+
using namespace clang::CIRGen;
28+
29+
namespace {
30+
31+
class CIRGenItaniumCXXABI : public CIRGenCXXABI {
32+
public:
33+
CIRGenItaniumCXXABI(CIRGenModule &cgm) : CIRGenCXXABI(cgm) {
34+
assert(!cir::MissingFeatures::cxxabiUseARMMethodPtrABI());
35+
assert(!cir::MissingFeatures::cxxabiUseARMGuardVarABI());
36+
}
37+
38+
void emitInstanceFunctionProlog(SourceLocation Loc,
39+
CIRGenFunction &CGF) override;
40+
};
41+
42+
} // namespace
43+
44+
void CIRGenItaniumCXXABI::emitInstanceFunctionProlog(SourceLocation loc,
45+
CIRGenFunction &cgf) {
46+
// Naked functions have no prolog.
47+
if (cgf.curFuncDecl && cgf.curFuncDecl->hasAttr<NakedAttr>()) {
48+
cgf.cgm.errorNYI(cgf.curFuncDecl->getLocation(),
49+
"emitInstanceFunctionProlog: Naked");
50+
}
51+
52+
/// Initialize the 'this' slot. In the Itanium C++ ABI, no prologue
53+
/// adjustments are required, because they are all handled by thunks.
54+
setCXXABIThisValue(cgf, loadIncomingCXXThis(cgf));
55+
56+
/// Classic codegen has code here to initialize the 'vtt' slot if
57+
// getStructorImplicitParamDecl(cgf) returns a non-null value, but in the
58+
// current implementation (of classic codegen) it never does.
59+
assert(!cir::MissingFeatures::cxxabiStructorImplicitParam());
60+
61+
/// If this is a function that the ABI specifies returns 'this', initialize
62+
/// the return slot to this' at the start of the function.
63+
///
64+
/// Unlike the setting of return types, this is done within the ABI
65+
/// implementation instead of by clients of CIRGenCXXBI because:
66+
/// 1) getThisValue is currently protected
67+
/// 2) in theory, an ABI could implement 'this' returns some other way;
68+
/// HasThisReturn only specifies a contract, not the implementation
69+
if (hasThisReturn(cgf.curGD)) {
70+
cgf.cgm.errorNYI(cgf.curFuncDecl->getLocation(),
71+
"emitInstanceFunctionProlog: hasThisReturn");
72+
}
73+
}
74+
75+
CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) {
76+
switch (cgm.getASTContext().getCXXABIKind()) {
77+
case TargetCXXABI::GenericItanium:
78+
case TargetCXXABI::GenericAArch64:
79+
return new CIRGenItaniumCXXABI(cgm);
80+
81+
case TargetCXXABI::AppleARM64:
82+
// The general Itanium ABI will do until we implement something that
83+
// requires special handling.
84+
assert(!cir::MissingFeatures::cxxabiAppleARM64CXXABI());
85+
return new CIRGenItaniumCXXABI(cgm);
86+
87+
default:
88+
llvm_unreachable("bad or NYI ABI kind");
89+
}
90+
}

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,6 @@ static CIRGenCXXABI *createCXXABI(CIRGenModule &cgm) {
5555
llvm_unreachable("invalid C++ ABI kind");
5656
}
5757

58-
namespace clang::CIRGen {
59-
// TODO(cir): Implement target-specific CIRGenCXXABIs
60-
CIRGenCXXABI *CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) {
61-
assert(!cir::MissingFeatures::targetSpecificCXXABI());
62-
return new CIRGenCXXABI(cgm);
63-
}
64-
} // namespace clang::CIRGen
65-
6658
CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
6759
clang::ASTContext &astContext,
6860
const clang::CodeGenOptions &cgo,

0 commit comments

Comments
 (0)