https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/143639
>From aeaef15434292dd7621541f7105356bfb1b9d57c Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Mon, 9 Jun 2025 09:38:55 -0700 Subject: [PATCH 1/2] [CIR] Upstream support for emitting constructors This change upstreams the code to emit simple constructor defintions. --- clang/include/clang/CIR/MissingFeatures.h | 4 + clang/lib/CIR/CodeGen/CIRGenCXX.cpp | 40 +++++++++ clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 21 +++++ clang/lib/CIR/CodeGen/CIRGenCall.cpp | 41 ++++++++++ clang/lib/CIR/CodeGen/CIRGenClass.cpp | 82 +++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 53 ++++++++++-- clang/lib/CIR/CodeGen/CIRGenFunction.h | 18 ++++ clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 80 +++++++++++++++++- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 15 ++-- clang/lib/CIR/CodeGen/CIRGenModule.h | 5 ++ clang/lib/CIR/CodeGen/CMakeLists.txt | 1 + clang/test/CIR/CodeGen/ctor.cpp | 20 ++++- 12 files changed, 364 insertions(+), 16 deletions(-) create mode 100644 clang/lib/CIR/CodeGen/CIRGenCXX.cpp diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index f89d386378e51..df42bf6c1d7cf 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -81,6 +81,7 @@ struct MissingFeatures { static bool opFuncCPUAndFeaturesAttributes() { return false; } static bool opFuncSection() { return false; } static bool opFuncSetComdat() { return false; } + static bool opFuncAttributesForDefinition() { return false; } // CallOp handling static bool opCallBuiltinFunc() { return false; } @@ -225,6 +226,9 @@ struct MissingFeatures { static bool isMemcpyEquivalentSpecialMember() { return false; } static bool isTrivialCtorOrDtor() { return false; } static bool implicitConstructorArgs() { return false; } + static bool emitCtorPrologue() { return false; } + static bool thunks() { return false; } + static bool runCleanupsScope() { return false; } // Missing types static bool dataMemberType() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp new file mode 100644 index 0000000000000..51751483d34e9 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// 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 contains code dealing with C++ code generation. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenFunction.h" +#include "CIRGenModule.h" + +#include "clang/AST/GlobalDecl.h" +#include "clang/CIR/MissingFeatures.h" + +using namespace clang; +using namespace clang::CIRGen; + +cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) { + const CIRGenFunctionInfo &fnInfo = + getTypes().arrangeCXXStructorDeclaration(gd); + cir::FuncType funcType = getTypes().getFunctionType(fnInfo); + cir::FuncOp fn = getAddrOfCXXStructor(gd, &fnInfo, /*FnType=*/nullptr, + /*DontDefer=*/true, ForDefinition); + assert(!cir::MissingFeatures::opFuncLinkage()); + CIRGenFunction cgf{*this, builder}; + curCGF = &cgf; + { + mlir::OpBuilder::InsertionGuard guard(builder); + cgf.generateCode(gd, fn, funcType); + } + curCGF = nullptr; + + setNonAliasAttributes(gd, fn); + assert(!cir::MissingFeatures::opFuncAttributesForDefinition()); + return fn; +} diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 107535ebc7275..7d2b46d50d82e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -37,6 +37,10 @@ class CIRGenCXXABI { void setCXXABIThisValue(CIRGenFunction &cgf, mlir::Value thisPtr); + /// Emit a single constructor/destructor with the gien type from a C++ + /// constructor Decl. + virtual void emitCXXStructor(clang::GlobalDecl gd) = 0; + public: clang::ImplicitParamDecl *getThisDecl(CIRGenFunction &cgf) { return cgf.cxxabiThisDecl; @@ -55,12 +59,29 @@ class CIRGenCXXABI { return md->getParent(); } + /// Return whether the given global decl needs a VTT parameter. + virtual bool needsVTTParameter(clang::GlobalDecl gd) { return false; } + /// Build a parameter variable suitable for 'this'. void buildThisParam(CIRGenFunction &cgf, FunctionArgList ¶ms); /// Loads the incoming C++ this pointer as it was passed by the caller. mlir::Value loadIncomingCXXThis(CIRGenFunction &cgf); + /// Emit constructor variants required by this ABI. + virtual void emitCXXConstructors(const clang::CXXConstructorDecl *d) = 0; + + /// Insert any ABI-specific implicit parameters into the parameter list for a + /// function. This generally involves extra data for constructors and + /// destructors. + /// + /// ABIs may also choose to override the return type, which has been + /// initialized with the type of 'this' if HasThisReturn(CGF.CurGD) is true or + /// the formal return type of the function otherwise. + virtual void addImplicitStructorParams(CIRGenFunction &cgf, + clang::QualType &resTy, + FunctionArgList ¶ms) = 0; + /// 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). diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 9d25eea9e413d..da754e0806b2d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -162,6 +162,47 @@ arrangeCIRFunctionInfo(CIRGenTypes &cgt, SmallVectorImpl<CanQualType> &prefix, return cgt.arrangeCIRFunctionInfo(resultType, prefix, required); } +void CIRGenFunction::emitDelegateCallArg(CallArgList &args, + const VarDecl *param, + SourceLocation loc) { + // StartFunction converted the ABI-lowered parameter(s) into a local alloca. + // We need to turn that into an r-value suitable for emitCall + Address local = getAddrOfLocalVar(param); + + QualType type = param->getType(); + + if (const auto *rd = type->getAsCXXRecordDecl()) { + cgm.errorNYI(param->getSourceRange(), + "emitDelegateCallArg: record argument"); + return; + } + + // GetAddrOfLocalVar returns a pointer-to-pointer for references, but the + // argument needs to be the original pointer. + if (type->isReferenceType()) { + args.add( + RValue::get(builder.createLoad(getLoc(param->getSourceRange()), local)), + type); + } else if (getLangOpts().ObjCAutoRefCount) { + cgm.errorNYI(param->getSourceRange(), + "emitDelegateCallArg: ObjCAutoRefCount"); + // For the most part, we just need to load the alloca, except that aggregate + // r-values are actually pointers to temporaries. + } else { + cgm.errorNYI(param->getSourceRange(), + "emitDelegateCallArg: convertTempToRValue"); + } + + // Deactivate the cleanup for the callee-destructed param that was pushed. + assert(!cir::MissingFeatures::thunks()); + if (type->isRecordType() && + type->castAs<RecordType>()->getDecl()->isParamDestroyedInCallee() && + param->needsDestruction(getContext())) { + cgm.errorNYI(param->getSourceRange(), + "emitDelegateCallArg: callee-destructed param"); + } +} + static const CIRGenFunctionInfo & arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm, const CallArgList &args, diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 8491a66ea6cb4..4590775462009 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -21,6 +21,88 @@ using namespace clang; using namespace clang::CIRGen; +/// Checks whether the given constructor is a valid subject for the +/// complete-to-base constructor delgation optimization, i.e. emitting the +/// complete constructor as a simple call to the base constructor. +bool CIRGenFunction::isConstructorDelegationValid( + const CXXConstructorDecl *ctor) { + + // Currently we disable the optimization for classes with virtual bases + // because (1) the address of parameter variables need to be consistent across + // all initializers but (2) the delegate function call necessarily creates a + // second copy of the parameter variable. + // + // The limiting example (purely theoretical AFAIK): + // struct A { A(int &c) { c++; } }; + // struct A : virtual A { + // B(int count) : A(count) { printf("%d\n", count); } + // }; + // ...although even this example could in principle be emitted as a delegation + // since the address of the parameter doesn't escape. + if (ctor->getParent()->getNumVBases()) + return false; + + // We also disable the optimization for variadic functions because it's + // impossible to "re-pass" varargs. + if (ctor->getType()->castAs<FunctionProtoType>()->isVariadic()) + return false; + + // FIXME: Decide if we can do a delegation of a delegating constructor. + if (ctor->isDelegatingConstructor()) + return false; + + return true; +} + +Address CIRGenFunction::loadCXXThisAddress() { + assert(curFuncDecl && "loading 'this' without a func declaration?"); + assert(isa<CXXMethodDecl>(curFuncDecl)); + + // Lazily compute CXXThisAlignment. + if (cxxThisAlignment.isZero()) { + // Just use the best known alignment for the parent. + // TODO: if we're currently emitting a complete-object ctor/dtor, we can + // always use the complete-object alignment. + auto rd = cast<CXXMethodDecl>(curFuncDecl)->getParent(); + cxxThisAlignment = cgm.getClassPointerAlignment(rd); + } + + return Address(loadCXXThis(), cxxThisAlignment); +} + +void CIRGenFunction::emitDelegateCXXConstructorCall( + const CXXConstructorDecl *ctor, CXXCtorType ctorType, + const FunctionArgList &args, SourceLocation loc) { + CallArgList delegateArgs; + + FunctionArgList::const_iterator i = args.begin(), e = args.end(); + assert(i != e && "no parameters to constructor"); + + // this + Address thisAddr = loadCXXThisAddress(); + delegateArgs.add(RValue::get(thisAddr.getPointer()), (*i)->getType()); + ++i; + + // FIXME: The location of the VTT parameter in the parameter list is specific + // to the Itanium ABI and shouldn't be hardcoded here. + if (cgm.getCXXABI().needsVTTParameter(curGD)) { + cgm.errorNYI(loc, "emitDelegateCXXConstructorCall: VTT parameter"); + return; + } + + // Explicit arguments. + for (; i != e; ++i) { + const VarDecl *param = *i; + // FIXME: per-argument source location + emitDelegateCallArg(delegateArgs, param, loc); + } + + assert(!cir::MissingFeatures::sanitizers()); + + emitCXXConstructorCall(ctor, ctorType, /*ForVirtualBase=*/false, + /*Delegating=*/true, thisAddr, delegateArgs, loc); +} + Address CIRGenFunction::getAddressOfBaseClass( Address value, const CXXRecordDecl *derived, llvm::iterator_range<CastExpr::path_const_iterator> path, diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index e32a5c836be02..5b2094ff7ceb3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -465,7 +465,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, if (isa<CXXDestructorDecl>(funcDecl)) getCIRGenModule().errorNYI(bodyRange, "C++ destructor definition"); else if (isa<CXXConstructorDecl>(funcDecl)) - getCIRGenModule().errorNYI(bodyRange, "C++ constructor definition"); + emitConstructorBody(args); else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice && funcDecl->hasAttr<CUDAGlobalAttr>()) getCIRGenModule().errorNYI(bodyRange, "CUDA kernel"); @@ -496,6 +496,47 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, return fn; } +void CIRGenFunction::emitConstructorBody(FunctionArgList &args) { + assert(!cir::MissingFeatures::sanitizers()); + const auto *ctor = cast<CXXConstructorDecl>(curGD.getDecl()); + CXXCtorType ctorType = curGD.getCtorType(); + + assert((cgm.getTarget().getCXXABI().hasConstructorVariants() || + ctorType == Ctor_Complete) && + "can only generate complete ctor for this ABI"); + + if (ctorType == Ctor_Complete && isConstructorDelegationValid(ctor) && + cgm.getTarget().getCXXABI().hasConstructorVariants()) { + emitDelegateCXXConstructorCall(ctor, Ctor_Base, args, ctor->getEndLoc()); + return; + } + + const FunctionDecl *definition = nullptr; + Stmt *body = ctor->getBody(definition); + assert(definition == ctor && "emitting wrong constructor body"); + + if (isa_and_nonnull<CXXTryStmt>(body)) { + cgm.errorNYI(ctor->getSourceRange(), "emitConstructorBody: try body"); + return; + } + + assert(!cir::MissingFeatures::incrementProfileCounter()); + assert(!cir::MissingFeatures::runCleanupsScope()); + + // TODO: in restricted cases, we can emit the vbase initializers of a + // complete ctor and then delegate to the base ctor. + + assert(!cir::MissingFeatures::emitCtorPrologue()); + + // TODO(cir): propagate this result via mlir::logical result. Just unreachable + // now just to have it handled. + if (mlir::failed(emitStmt(body, true))) { + cgm.errorNYI(ctor->getSourceRange(), + "emitConstructorBody: emit body statement failed."); + return; + } +} + /// Given a value of type T* that may not be to a complete object, construct /// an l-vlaue withi the natural pointee alignment of T. LValue CIRGenFunction::makeNaturalAlignPointeeAddrLValue(mlir::Value val, @@ -522,16 +563,16 @@ clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd, cgm.getCXXABI().buildThisParam(*this, args); } - if (isa<CXXConstructorDecl>(fd)) - cgm.errorNYI(fd->getSourceRange(), - "buildFunctionArgList: CXXConstructorDecl"); + if (const auto *cd = dyn_cast<CXXConstructorDecl>(fd)) + if (cd->getInheritedConstructor()) + cgm.errorNYI(fd->getSourceRange(), + "buildFunctionArgList: inherited constructor"); for (auto *param : fd->parameters()) args.push_back(param); if (md && (isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md))) - cgm.errorNYI(fd->getSourceRange(), - "buildFunctionArgList: implicit structor params"); + cgm.getCXXABI().addImplicitStructorParams(*this, retTy, args); return retTy; } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 7db7f6928fd8f..c45ab8ee66e87 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -60,6 +60,7 @@ class CIRGenFunction : public CIRGenTypeCache { ImplicitParamDecl *cxxabiThisDecl = nullptr; mlir::Value cxxabiThisValue = nullptr; mlir::Value cxxThisValue = nullptr; + clang::CharUnits cxxThisAlignment; // Holds the Decl for the current outermost non-closure context const clang::Decl *curFuncDecl = nullptr; @@ -467,6 +468,9 @@ class CIRGenFunction : public CIRGenTypeCache { bool shouldNullCheckClassCastValue(const CastExpr *ce); + static bool + isConstructorDelegationValid(const clang::CXXConstructorDecl *ctor); + LValue makeNaturalAlignPointeeAddrLValue(mlir::Value v, clang::QualType t); /// Construct an address with the natural alignment of T. If a pointer to T @@ -511,6 +515,7 @@ class CIRGenFunction : public CIRGenTypeCache { assert(cxxThisValue && "no 'this' value for this function"); return cxxThisValue; } + Address loadCXXThisAddress(); /// Get an appropriate 'undef' rvalue for the given type. /// TODO: What's the equivalent for MLIR? Currently we're only using this for @@ -742,6 +747,8 @@ class CIRGenFunction : public CIRGenTypeCache { LValue emitCompoundAssignmentLValue(const clang::CompoundAssignOperator *e); + void emitConstructorBody(FunctionArgList &args); + mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s); void emitCXXConstructExpr(const clang::CXXConstructExpr *e, @@ -830,6 +837,17 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::Type condType, bool buildingTopLevelCase); + void emitDelegateCXXConstructorCall(const clang::CXXConstructorDecl *ctor, + clang::CXXCtorType ctorType, + const FunctionArgList &args, + clang::SourceLocation loc); + + /// We are performing a delegate call; that is, the current function is + /// delegating to another one. Produce a r-value suitable for passing the + /// given parameter. + void emitDelegateCallArg(CallArgList &args, const clang::VarDecl *param, + clang::SourceLocation loc); + /// Emit an `if` on a boolean condition to the specified blocks. /// FIXME: Based on the condition, this might try to simplify the codegen of /// the conditional based on the branch. diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index fdd8b63fb6da0..39b03a12ede9a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -20,7 +20,9 @@ #include "CIRGenCXXABI.h" #include "CIRGenFunction.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/GlobalDecl.h" +#include "clang/CIR/MissingFeatures.h" #include "llvm/Support/ErrorHandling.h" using namespace clang; @@ -35,8 +37,16 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { assert(!cir::MissingFeatures::cxxabiUseARMGuardVarABI()); } - void emitInstanceFunctionProlog(SourceLocation Loc, - CIRGenFunction &CGF) override; + bool needsVTTParameter(clang::GlobalDecl gd) override; + + void emitInstanceFunctionProlog(SourceLocation loc, + CIRGenFunction &cgf) override; + + void addImplicitStructorParams(CIRGenFunction &cgf, QualType &retTy, + FunctionArgList ¶ms) override; + + void emitCXXConstructors(const clang::CXXConstructorDecl *d) override; + void emitCXXStructor(clang::GlobalDecl gd) override; }; } // namespace @@ -72,6 +82,72 @@ void CIRGenItaniumCXXABI::emitInstanceFunctionProlog(SourceLocation loc, } } +void CIRGenItaniumCXXABI::addImplicitStructorParams(CIRGenFunction &cgf, + QualType &retTY, + FunctionArgList ¶ms) { + const auto *md = cast<CXXMethodDecl>(cgf.curGD.getDecl()); + assert(isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md)); + + // Check if we need a VTT parameter as well. + if (needsVTTParameter(cgf.curGD)) { + cgf.cgm.errorNYI(cgf.curFuncDecl->getLocation(), + "addImplicitStructorParams: VTT parameter"); + } +} + +void CIRGenItaniumCXXABI::emitCXXStructor(GlobalDecl gd) { + auto *md = cast<CXXMethodDecl>(gd.getDecl()); + auto *cd = dyn_cast<CXXConstructorDecl>(md); + + if (!cd) { + cgm.errorNYI(md->getSourceRange(), "CXCABI emit destructor"); + return; + } + + if (cgm.getCodeGenOpts().CXXCtorDtorAliases) + cgm.errorNYI(md->getSourceRange(), "Ctor/Dtor aliases"); + + auto fn = cgm.codegenCXXStructor(gd); + + cgm.maybeSetTrivialComdat(*md, fn); +} + +void CIRGenItaniumCXXABI::emitCXXConstructors(const CXXConstructorDecl *d) { + // Just make sure we're in sync with TargetCXXABI. + assert(cgm.getTarget().getCXXABI().hasConstructorVariants()); + + // The constructor used for constructing this as a base class; + // ignores virtual bases. + cgm.emitGlobal(GlobalDecl(d, Ctor_Base)); + + // The constructor used for constructing this as a complete class; + // constructs the virtual bases, then calls the base constructor. + if (!d->getParent()->isAbstract()) { + // We don't need to emit the complete ctro if the class is abstract. + cgm.emitGlobal(GlobalDecl(d, Ctor_Complete)); + } +} + +/// Return whether the given global decl needs a VTT parameter, which it does if +/// it's a base constructor or destructor with virtual bases. +bool CIRGenItaniumCXXABI::needsVTTParameter(GlobalDecl gd) { + auto *md = cast<CXXMethodDecl>(gd.getDecl()); + + // We don't have any virtual bases, just return early. + if (!md->getParent()->getNumVBases()) + return false; + + // Check if we have a base constructor. + if (isa<CXXConstructorDecl>(md) && gd.getCtorType() == Ctor_Base) + return true; + + // Check if we have a base destructor. + if (isa<CXXDestructorDecl>(md) && gd.getDtorType() == Dtor_Base) + return true; + + return false; +} + CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) { switch (cgm.getASTContext().getCXXABIKind()) { case TargetCXXABI::GenericItanium: diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 8407f8fad06ba..434dd376208e1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -226,11 +226,9 @@ mlir::Operation * CIRGenModule::getAddrOfGlobal(GlobalDecl gd, ForDefinition_t isForDefinition) { const Decl *d = gd.getDecl(); - if (isa<CXXConstructorDecl>(d) || isa<CXXDestructorDecl>(d)) { - errorNYI(d->getSourceRange(), - "getAddrOfGlobal: C++ constructor/destructor"); - return nullptr; - } + if (isa<CXXConstructorDecl>(d) || isa<CXXDestructorDecl>(d)) + return getAddrOfCXXStructor(gd, /*FnInfo=*/nullptr, /*FnType=*/nullptr, + /*DontDefer=*/false, isForDefinition); if (isa<CXXMethodDecl>(d)) { const CIRGenFunctionInfo &fi = @@ -411,6 +409,7 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd, cgf.generateCode(gd, funcOp, funcType); } curCGF = nullptr; + assert(!cir::MissingFeatures::opFuncAttributesForDefinition()); } mlir::Operation *CIRGenModule::getGlobalValue(StringRef name) { @@ -771,7 +770,7 @@ void CIRGenModule::emitGlobalDefinition(clang::GlobalDecl gd, // Make sure to emit the definition(s) before we emit the thunks. This is // necessary for the generation of certain thunks. if (isa<CXXConstructorDecl>(method) || isa<CXXDestructorDecl>(method)) - errorNYI(method->getSourceRange(), "C++ ctor/dtor"); + abi->emitCXXStructor(gd); else if (fd->isMultiVersion()) errorNYI(method->getSourceRange(), "multiversion functions"); else @@ -1173,6 +1172,10 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) { case Decl::Empty: break; + case Decl::CXXConstructor: + getCXXABI().emitCXXConstructors(cast<CXXConstructorDecl>(decl)); + break; + // C++ Decls case Decl::LinkageSpec: case Decl::Namespace: diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 9748c0b3ed43a..f76fd8e733642 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -267,6 +267,11 @@ class CIRGenModule : public CIRGenTypeCache { // Make sure that this type is translated. void updateCompletedType(const clang::TagDecl *td); + // Produce code for this constructor/destructor. This method doesn't try to + // apply any ABI rules about which other constructors/destructors are needed + // or if they are alias to each other. + cir::FuncOp codegenCXXStructor(clang::GlobalDecl gd); + bool supportsCOMDAT() const; void maybeSetTrivialComdat(const clang::Decl &d, mlir::Operation *op); diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index 8bfcd2773d07a..5fa3ed70a9a57 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -11,6 +11,7 @@ add_clang_library(clangCIR CIRGenBuilder.cpp CIRGenCall.cpp CIRGenClass.cpp + CIRGenCXX.cpp CIRGenCXXABI.cpp CIRGenCXXExpr.cpp CIRGenDecl.cpp diff --git a/clang/test/CIR/CodeGen/ctor.cpp b/clang/test/CIR/CodeGen/ctor.cpp index 3a1e82e338c1c..1248b2d104161 100644 --- a/clang/test/CIR/CodeGen/ctor.cpp +++ b/clang/test/CIR/CodeGen/ctor.cpp @@ -3,7 +3,7 @@ struct Struk { int a; - Struk(); + Struk() {} }; void baz() { @@ -12,7 +12,23 @@ void baz() { // CHECK: !rec_Struk = !cir.record<struct "Struk" {!s32i}> -// CHECK: cir.func @_ZN5StrukC1Ev(!cir.ptr<!rec_Struk>) +// Note: In the absence of the '-mconstructor-aliases' option, we emit two +// constructors here. The handling of constructor aliases is currently +// NYI, but when it is added this test should be updated to add a RUN +// line that passes '-mconstructor-aliases' to clang_cc1. +// CHECK: cir.func @_ZN5StrukC2Ev(%arg0: !cir.ptr<!rec_Struk> +// CHECK-NEXT: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_Struk>, !cir.ptr<!cir.ptr<!rec_Struk>>, ["this", init] {alignment = 8 : i64} +// CHECK-NEXT: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr<!rec_Struk>, !cir.ptr<!cir.ptr<!rec_Struk>> +// CHECK-NEXT: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_Struk>>, !cir.ptr<!rec_Struk> +// CHECK-NEXT: cir.return + +// CHECK: cir.func @_ZN5StrukC1Ev(%arg0: !cir.ptr<!rec_Struk> +// CHECK-NEXT: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_Struk>, !cir.ptr<!cir.ptr<!rec_Struk>>, ["this", init] {alignment = 8 : i64} +// CHECK-NEXT: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr<!rec_Struk>, !cir.ptr<!cir.ptr<!rec_Struk>> +// CHECK-NEXT: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_Struk>>, !cir.ptr<!rec_Struk> +// CHECK-NEXT: cir.call @_ZN5StrukC2Ev(%[[THIS]]) : (!cir.ptr<!rec_Struk>) -> () +// CHECK-NEXT: cir.return + // CHECK: cir.func @_Z3bazv() // CHECK-NEXT: %[[S_ADDR:.*]] = cir.alloca !rec_Struk, !cir.ptr<!rec_Struk>, ["s", init] {alignment = 4 : i64} // CHECK-NEXT: cir.call @_ZN5StrukC1Ev(%[[S_ADDR]]) : (!cir.ptr<!rec_Struk>) -> () >From 6ba60aa4e14ee142744572baa7bc49912fcac8a7 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Wed, 11 Jun 2025 13:20:25 -0700 Subject: [PATCH 2/2] Address review feedback --- clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 18 +++------- clang/lib/CIR/CodeGen/CIRGenClass.cpp | 3 +- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 9 ++++- clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 21 ++---------- clang/test/CIR/CodeGen/ctor.cpp | 34 +++++++++++++++++++ 5 files changed, 50 insertions(+), 35 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 7d2b46d50d82e..2d967fd307e01 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -37,8 +37,8 @@ class CIRGenCXXABI { void setCXXABIThisValue(CIRGenFunction &cgf, mlir::Value thisPtr); - /// Emit a single constructor/destructor with the gien type from a C++ - /// constructor Decl. + /// Emit a single constructor/destructor with the gen type from a C++ + /// constructor/destructor Decl. virtual void emitCXXStructor(clang::GlobalDecl gd) = 0; public: @@ -59,7 +59,8 @@ class CIRGenCXXABI { return md->getParent(); } - /// Return whether the given global decl needs a VTT parameter. + /// Return whether the given global decl needs a VTT (virtual table table) + /// parameter. virtual bool needsVTTParameter(clang::GlobalDecl gd) { return false; } /// Build a parameter variable suitable for 'this'. @@ -71,17 +72,6 @@ class CIRGenCXXABI { /// Emit constructor variants required by this ABI. virtual void emitCXXConstructors(const clang::CXXConstructorDecl *d) = 0; - /// Insert any ABI-specific implicit parameters into the parameter list for a - /// function. This generally involves extra data for constructors and - /// destructors. - /// - /// ABIs may also choose to override the return type, which has been - /// initialized with the type of 'this' if HasThisReturn(CGF.CurGD) is true or - /// the formal return type of the function otherwise. - virtual void addImplicitStructorParams(CIRGenFunction &cgf, - clang::QualType &resTy, - FunctionArgList ¶ms) = 0; - /// 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). diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 4590775462009..bb4b451c99247 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -22,11 +22,10 @@ using namespace clang; using namespace clang::CIRGen; /// Checks whether the given constructor is a valid subject for the -/// complete-to-base constructor delgation optimization, i.e. emitting the +/// complete-to-base constructor delegation optimization, i.e. emitting the /// complete constructor as a simple call to the base constructor. bool CIRGenFunction::isConstructorDelegationValid( const CXXConstructorDecl *ctor) { - // Currently we disable the optimization for classes with virtual bases // because (1) the address of parameter variables need to be consistent across // all initializers but (2) the delegate function call necessarily creates a diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 5b2094ff7ceb3..53c44c6cc7680 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -527,6 +527,13 @@ void CIRGenFunction::emitConstructorBody(FunctionArgList &args) { // complete ctor and then delegate to the base ctor. assert(!cir::MissingFeatures::emitCtorPrologue()); + if (ctor->isDelegatingConstructor()) { + // This will be handled in emitCtorPrologue, but we should emit a diagnostic + // rather than silently fail to delegate. + cgm.errorNYI(ctor->getSourceRange(), + "emitConstructorBody: delegating ctor"); + return; + } // TODO(cir): propagate this result via mlir::logical result. Just unreachable // now just to have it handled. @@ -572,7 +579,7 @@ clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd, args.push_back(param); if (md && (isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md))) - cgm.getCXXABI().addImplicitStructorParams(*this, retTy, args); + assert(!cir::MissingFeatures::cxxabiStructorImplicitParam()); return retTy; } diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 39b03a12ede9a..cd9096a0188a7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -42,9 +42,6 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { void emitInstanceFunctionProlog(SourceLocation loc, CIRGenFunction &cgf) override; - void addImplicitStructorParams(CIRGenFunction &cgf, QualType &retTy, - FunctionArgList ¶ms) override; - void emitCXXConstructors(const clang::CXXConstructorDecl *d) override; void emitCXXStructor(clang::GlobalDecl gd) override; }; @@ -82,19 +79,6 @@ void CIRGenItaniumCXXABI::emitInstanceFunctionProlog(SourceLocation loc, } } -void CIRGenItaniumCXXABI::addImplicitStructorParams(CIRGenFunction &cgf, - QualType &retTY, - FunctionArgList ¶ms) { - const auto *md = cast<CXXMethodDecl>(cgf.curGD.getDecl()); - assert(isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md)); - - // Check if we need a VTT parameter as well. - if (needsVTTParameter(cgf.curGD)) { - cgf.cgm.errorNYI(cgf.curFuncDecl->getLocation(), - "addImplicitStructorParams: VTT parameter"); - } -} - void CIRGenItaniumCXXABI::emitCXXStructor(GlobalDecl gd) { auto *md = cast<CXXMethodDecl>(gd.getDecl()); auto *cd = dyn_cast<CXXConstructorDecl>(md); @@ -128,8 +112,9 @@ void CIRGenItaniumCXXABI::emitCXXConstructors(const CXXConstructorDecl *d) { } } -/// Return whether the given global decl needs a VTT parameter, which it does if -/// it's a base constructor or destructor with virtual bases. +/// Return whether the given global decl needs a VTT (virtual table table) +/// parameter, which it does if it's a base constructor or destructor with +/// virtual bases. bool CIRGenItaniumCXXABI::needsVTTParameter(GlobalDecl gd) { auto *md = cast<CXXMethodDecl>(gd.getDecl()); diff --git a/clang/test/CIR/CodeGen/ctor.cpp b/clang/test/CIR/CodeGen/ctor.cpp index 1248b2d104161..3b4191fd74c97 100644 --- a/clang/test/CIR/CodeGen/ctor.cpp +++ b/clang/test/CIR/CodeGen/ctor.cpp @@ -33,3 +33,37 @@ void baz() { // CHECK-NEXT: %[[S_ADDR:.*]] = cir.alloca !rec_Struk, !cir.ptr<!rec_Struk>, ["s", init] {alignment = 4 : i64} // CHECK-NEXT: cir.call @_ZN5StrukC1Ev(%[[S_ADDR]]) : (!cir.ptr<!rec_Struk>) -> () // CHECK-NEXT: cir.return + +struct VariadicStruk { + int a; + VariadicStruk(int n, ...) { a = n;} +}; + +void bar() { + VariadicStruk s(1, 2, 3); +} + +// When a variadic constructor is present, we call the C2 constructor directly. + +// CHECK-NOT: cir.func @_ZN13VariadicStrukC2Eiz + +// CHECK: cir.func @_ZN13VariadicStrukC1Eiz(%arg0: !cir.ptr<!rec_VariadicStruk> +// CHECK-SAME: %arg1: !s32i +// CHECK-SAME: ...) { +// CHECK-NEXT: %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init] +// CHECK-NEXT: %[[N_ADDR:.*]] = cir.alloca {{.*}} ["n", init] +// CHECK-NEXT: cir.store %arg0, %[[THIS_ADDR]] +// CHECK-NEXT: cir.store %arg1, %[[N_ADDR]] +// CHECK-NEXT: %[[THIS:.*]] = cir.load{{.*}} %[[THIS_ADDR]] +// CHECK-NEXT: %[[N:.*]] = cir.load{{.*}} %[[N_ADDR]] +// CHECK-NEXT: %[[A_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "a"} +// CHECK-NEXT: cir.store{{.*}} %[[N]], %[[A_ADDR]] +// CHECK-NEXT: cir.return + +// CHECK: cir.func @_Z3barv +// CHECK-NEXT: %[[S_ADDR:.*]] = cir.alloca !rec_VariadicStruk, !cir.ptr<!rec_VariadicStruk>, ["s", init] +// CHECK-NEXT: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i +// CHECK-NEXT: %[[TWO:.*]] = cir.const #cir.int<2> : !s32i +// CHECK-NEXT: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i +// CHECK-NEXT: cir.call @_ZN13VariadicStrukC1Eiz(%[[S_ADDR]], %[[ONE]], %[[TWO]], %[[THREE]]) +// CHECK-NEXT: cir.return _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits