Skip to content

[CIR] Support inline C++ member function definitions #142484

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions clang/include/clang/CIR/CIRGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,49 @@ class CIRGenerator : public clang::ASTConsumer {

const clang::CodeGenOptions &codeGenOpts;

unsigned handlingTopLevelDecls;

/// Use this when emitting decls to block re-entrant decl emission. It will
/// emit all deferred decls on scope exit. Set EmitDeferred to false if decl
/// emission must be deferred longer, like at the end of a tag definition.
struct HandlingTopLevelDeclRAII {
CIRGenerator &self;
bool emitDeferred;
HandlingTopLevelDeclRAII(CIRGenerator &self, bool emitDeferred = true)
: self{self}, emitDeferred{emitDeferred} {
++self.handlingTopLevelDecls;
}
~HandlingTopLevelDeclRAII() {
unsigned Level = --self.handlingTopLevelDecls;
if (Level == 0 && emitDeferred)
self.emitDeferredDecls();
}
};

protected:
std::unique_ptr<mlir::MLIRContext> mlirContext;
std::unique_ptr<clang::CIRGen::CIRGenModule> cgm;

private:
llvm::SmallVector<clang::FunctionDecl *, 8> deferredInlineMemberFuncDefs;

public:
CIRGenerator(clang::DiagnosticsEngine &diags,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
const clang::CodeGenOptions &cgo);
~CIRGenerator() override;
void Initialize(clang::ASTContext &astContext) override;
bool HandleTopLevelDecl(clang::DeclGroupRef group) override;
void HandleInlineFunctionDefinition(clang::FunctionDecl *d) override;
void CompleteTentativeDefinition(clang::VarDecl *d) override;

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

bool verifyModule() const;

void emitDeferredDecls();
};

} // namespace cir
Expand Down
13 changes: 9 additions & 4 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,15 @@ struct MissingFeatures {
static bool recordZeroInit() { return false; }
static bool zeroSizeRecordMembers() { return false; }

// Misc
// CXXABI
static bool cxxABI() { return false; }
static bool cxxabiThisAlignment() { return false; }
static bool cxxabiUseARMMethodPtrABI() { return false; }
static bool cxxabiUseARMGuardVarABI() { return false; }
static bool cxxabiAppleARM64CXXABI() { return false; }
static bool cxxabiStructorImplicitParam() { return false; }

// Misc
static bool cirgenABIInfo() { return false; }
static bool abiArgInfo() { return false; }
static bool tryEmitAsConstant() { return false; }
Expand Down Expand Up @@ -187,7 +194,6 @@ struct MissingFeatures {
static bool typeChecks() { return false; }
static bool lambdaFieldToName() { return false; }
static bool updateCompletedType() { return false; }
static bool targetSpecificCXXABI() { return false; }
static bool moduleNameHash() { return false; }
static bool constantFoldSwitchStatement() { return false; }
static bool cudaSupport() { return false; }
Expand All @@ -196,13 +202,12 @@ struct MissingFeatures {
static bool constEmitterVectorILE() { return false; }
static bool needsGlobalCtorDtor() { return false; }
static bool emitTypeCheck() { return false; }
static bool cxxabiThisDecl() { return false; }
static bool cxxabiThisAlignment() { return false; }
static bool writebacks() { return false; }
static bool cleanupsToDeactivate() { return false; }
static bool stackBase() { return false; }
static bool deferredDecls() { return false; }
static bool setTargetAttributes() { return false; }
static bool coverageMapping() { return false; }

// Missing types
static bool dataMemberType() { return false; }
Expand Down
23 changes: 17 additions & 6 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,24 @@ void CIRGenCXXABI::buildThisParam(CIRGenFunction &cgf,
&cgm.getASTContext().Idents.get("this"),
md->getThisType(), ImplicitParamKind::CXXThis);
params.push_back(thisDecl);

// Classic codegen save thisDecl in CodeGenFunction::CXXABIThisDecl, but it
// doesn't seem to be needed in CIRGen.
assert(!cir::MissingFeatures::cxxabiThisDecl());
cgf.cxxabiThisDecl = thisDecl;

// Classic codegen computes the alignment of thisDecl and saves it in
// CodeGenFunction::CXXABIThisAlignment, but it doesn't seem to be needed in
// CIRGen.
// CodeGenFunction::CXXABIThisAlignment, but it is only used in emitTypeCheck
// in CodeGenFunction::StartFunction().
assert(!cir::MissingFeatures::cxxabiThisAlignment());
}

mlir::Value CIRGenCXXABI::loadIncomingCXXThis(CIRGenFunction &cgf) {
ImplicitParamDecl *vd = getThisDecl(cgf);
Address addr = cgf.getAddrOfLocalVar(vd);
return cgf.getBuilder().create<cir::LoadOp>(
cgf.getLoc(vd->getLocation()), addr.getElementType(), addr.getPointer());
}

void CIRGenCXXABI::setCXXABIThisValue(CIRGenFunction &cgf,
mlir::Value thisPtr) {
/// Initialize the 'this' slot.
assert(getThisDecl(cgf) && "no 'this' variable for function");
cgf.cxxabiThisValue = thisPtr;
}
14 changes: 14 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#define LLVM_CLANG_LIB_CIR_CIRGENCXXABI_H

#include "CIRGenCall.h"
#include "CIRGenFunction.h"
#include "CIRGenModule.h"

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

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

public:
clang::ImplicitParamDecl *getThisDecl(CIRGenFunction &cgf) {
return cgf.cxxabiThisDecl;
}

/// Emit the ABI-specific prolog for the function
virtual void emitInstanceFunctionProlog(SourceLocation Loc,
CIRGenFunction &cgf) = 0;

/// Get the type of the implicit "this" parameter used by a method. May return
/// zero if no specific type is applicable, e.g. if the ABI expects the "this"
/// parameter to point to some artificial offset in a complete object due to
Expand All @@ -47,6 +58,9 @@ class CIRGenCXXABI {
/// Build a parameter variable suitable for 'this'.
void buildThisParam(CIRGenFunction &cgf, FunctionArgList &params);

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

/// Returns true if the given constructor or destructor is one of the kinds
/// that the ABI says returns 'this' (only applies when called non-virtually
/// for destructors).
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {

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

mlir::Value VisitCXXThisExpr(CXXThisExpr *te) { return cgf.loadCXXThis(); }

/// Emit a conversion from the specified type to the specified destination
/// type, both of which are CIR scalar types.
/// TODO: do we need ScalarConversionOpts here? Should be done in another
Expand Down
22 changes: 21 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,9 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,

curFn = fn;

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

mlir::Block *entryBB = &fn.getBlocks().front();
builder.setInsertionPointToStart(entryBB);
Expand Down Expand Up @@ -385,6 +387,24 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
if (!returnType->isVoidType())
emitAndUpdateRetAlloca(returnType, getLoc(fd->getBody()->getEndLoc()),
getContext().getTypeAlignInChars(returnType));

if (isa_and_nonnull<CXXMethodDecl>(d) &&
cast<CXXMethodDecl>(d)->isInstance()) {
cgm.getCXXABI().emitInstanceFunctionProlog(loc, *this);

const auto *md = cast<CXXMethodDecl>(d);
if (md->getParent()->isLambda() && md->getOverloadedOperator() == OO_Call) {
cgm.errorNYI(loc, "lambda call operator");
} else {
// Not in a lambda; just use 'this' from the method.
// FIXME: Should we generate a new load for each use of 'this'? The fast
// register allocator would be happier...
cxxThisValue = cxxabiThisValue;
}

assert(!cir::MissingFeatures::sanitizers());
assert(!cir::MissingFeatures::emitTypeCheck());
}
}

void CIRGenFunction::finishFunction(SourceLocation endLoc) {}
Expand Down
25 changes: 25 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ class CIRGenFunction : public CIRGenTypeCache {
/// The compiler-generated variable that holds the return value.
std::optional<mlir::Value> fnRetAlloca;

/// CXXThisDecl - When generating code for a C++ member function,
/// this will hold the implicit 'this' declaration.
ImplicitParamDecl *cxxabiThisDecl = nullptr;
mlir::Value cxxabiThisValue = nullptr;
mlir::Value cxxThisValue = nullptr;

// Holds the Decl for the current outermost non-closure context
const clang::Decl *curFuncDecl = nullptr;

/// The function for which code is currently being generated.
cir::FuncOp curFn;

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

/// Return the address of a local variable.
Address getAddrOfLocalVar(const clang::VarDecl *vd) {
auto it = localDeclMap.find(vd);
assert(it != localDeclMap.end() &&
"Invalid argument to getAddrOfLocalVar(), no decl!");
return it->second;
}

/// Load the value for 'this'. This function is only valid while generating
/// code for an C++ member function.
/// FIXME(cir): this should return a mlir::Value!
mlir::Value loadCXXThis() {
assert(cxxThisValue && "no 'this' value for this function");
return cxxThisValue;
}

/// Get an appropriate 'undef' rvalue for the given type.
/// TODO: What's the equivalent for MLIR? Currently we're only using this for
/// void types so it just returns RValue::get(nullptr) but it'll need
Expand Down
90 changes: 90 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This provides C++ code generation targeting the Itanium C++ ABI. The class
// in this file generates structures that follow the Itanium C++ ABI, which is
// documented at:
// https://itanium-cxx-abi.github.io/cxx-abi/abi.html
// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
//
// It also supports the closely-related ARM ABI, documented at:
// https://developer.arm.com/documentation/ihi0041/g/
//
//===----------------------------------------------------------------------===//

#include "CIRGenCXXABI.h"
#include "CIRGenFunction.h"

#include "clang/AST/GlobalDecl.h"
#include "llvm/Support/ErrorHandling.h"

using namespace clang;
using namespace clang::CIRGen;

namespace {

class CIRGenItaniumCXXABI : public CIRGenCXXABI {
public:
CIRGenItaniumCXXABI(CIRGenModule &cgm) : CIRGenCXXABI(cgm) {
assert(!cir::MissingFeatures::cxxabiUseARMMethodPtrABI());
assert(!cir::MissingFeatures::cxxabiUseARMGuardVarABI());
}

void emitInstanceFunctionProlog(SourceLocation Loc,
CIRGenFunction &CGF) override;
};

} // namespace

void CIRGenItaniumCXXABI::emitInstanceFunctionProlog(SourceLocation loc,
CIRGenFunction &cgf) {
// Naked functions have no prolog.
if (cgf.curFuncDecl && cgf.curFuncDecl->hasAttr<NakedAttr>()) {
cgf.cgm.errorNYI(cgf.curFuncDecl->getLocation(),
"emitInstanceFunctionProlog: Naked");
}

/// Initialize the 'this' slot. In the Itanium C++ ABI, no prologue
/// adjustments are required, because they are all handled by thunks.
setCXXABIThisValue(cgf, loadIncomingCXXThis(cgf));

/// Classic codegen has code here to initialize the 'vtt' slot if
// getStructorImplicitParamDecl(cgf) returns a non-null value, but in the
// current implementation (of classic codegen) it never does.
assert(!cir::MissingFeatures::cxxabiStructorImplicitParam());

/// If this is a function that the ABI specifies returns 'this', initialize
/// the return slot to this' at the start of the function.
///
/// Unlike the setting of return types, this is done within the ABI
/// implementation instead of by clients of CIRGenCXXBI because:
/// 1) getThisValue is currently protected
/// 2) in theory, an ABI could implement 'this' returns some other way;
/// HasThisReturn only specifies a contract, not the implementation
if (hasThisReturn(cgf.curGD)) {
cgf.cgm.errorNYI(cgf.curFuncDecl->getLocation(),
"emitInstanceFunctionProlog: hasThisReturn");
}
}

CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) {
switch (cgm.getASTContext().getCXXABIKind()) {
case TargetCXXABI::GenericItanium:
case TargetCXXABI::GenericAArch64:
return new CIRGenItaniumCXXABI(cgm);

case TargetCXXABI::AppleARM64:
// The general Itanium ABI will do until we implement something that
// requires special handling.
assert(!cir::MissingFeatures::cxxabiAppleARM64CXXABI());
return new CIRGenItaniumCXXABI(cgm);

default:
llvm_unreachable("bad or NYI ABI kind");
}
}
8 changes: 0 additions & 8 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,6 @@ static CIRGenCXXABI *createCXXABI(CIRGenModule &cgm) {
llvm_unreachable("invalid C++ ABI kind");
}

namespace clang::CIRGen {
// TODO(cir): Implement target-specific CIRGenCXXABIs
CIRGenCXXABI *CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) {
assert(!cir::MissingFeatures::targetSpecificCXXABI());
return new CIRGenCXXABI(cgm);
}
} // namespace clang::CIRGen

CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
clang::ASTContext &astContext,
const clang::CodeGenOptions &cgo,
Expand Down
Loading
Loading