diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index cf82ac696200..e830444d24de 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -230,6 +230,10 @@ class CIRGenCXXABI { /// this emits virtual table tables. virtual void emitVirtualInheritanceTables(const CXXRecordDecl *RD) = 0; + virtual bool exportThunk() = 0; + virtual void setThunkLinkage(cir::FuncOp Thunk, bool ForVTable, GlobalDecl GD, + bool ReturnAdjustment) = 0; + virtual mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc, QualType Ty) = 0; virtual CatchTypeInfo diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 937bdd2c4191..4e505443477a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -1296,6 +1296,14 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCXXMethodCall( info, paramInfos, required); } +const CIRGenFunctionInfo & +CIRGenTypes::arrangeUnprototypedMustTailThunk(const CXXMethodDecl *md) { + assert(md->isVirtual() && "only methods have thunks"); + CanQual FTP = GetFormalType(md); + CanQualType ArgTys[] = {DeriveThisType(md->getParent(), md)}; + return arrangeCIRFunctionInfo(astContext.VoidTy, cir::FnInfoOpts::None, + ArgTys, FTP->getExtInfo(), {}, RequiredArgs(1)); +} /// Figure out the rules for calling a function with the given formal type using /// the given arguments. The arguments are necessary because the function might /// be unprototyped, in which case it's target-dependent in crazy ways. diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 1b2d22239194..6ca0d9fed0aa 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -227,6 +227,16 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { void emitVTableDefinitions(CIRGenVTables &CGVT, const CXXRecordDecl *RD) override; void emitVirtualInheritanceTables(const CXXRecordDecl *RD) override; + + void setThunkLinkage(cir::FuncOp Thunk, bool ForVTable, GlobalDecl GD, + bool ReturnAdjustment) override { + if (ForVTable && !Thunk.hasLocalLinkage()) + Thunk.setLinkage(cir::GlobalLinkageKind::AvailableExternallyLinkage); + const auto *ND = cast(GD.getDecl()); + CGM.setGVProperties(Thunk.getOperation(), ND); + } + + bool exportThunk() override { return true; } mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc, QualType Ty) override; bool useThunkForDtorVariant(const CXXDestructorDecl *Dtor, diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 133d9e3bd2fb..d80f8b70964e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -3065,6 +3065,7 @@ void CIRGenModule::setFunctionAttributes(GlobalDecl globalDecl, // NOTE(cir): Original CodeGen checks if this is an intrinsic. In CIR we // represent them in dedicated ops. The correct attributes are ensured during // translation to LLVM. Thus, we don't need to check for them here. + assert(!isThunk && "isThunk NYI"); if (!isIncompleteFunction) { setCIRFunctionAttributes(globalDecl, @@ -3100,8 +3101,6 @@ cir::FuncOp CIRGenModule::GetOrCreateCIRFunction( StringRef mangledName, mlir::Type ty, GlobalDecl gd, bool forVTable, bool dontDefer, bool isThunk, ForDefinition_t isForDefinition, mlir::ArrayAttr extraAttrs) { - assert(!isThunk && "NYI"); - const auto *d = gd.getDecl(); // Any attempts to use a MultiVersion function should result in retrieving the diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 0f3b680651a8..b4920a820f95 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -359,7 +359,7 @@ class CIRGenModule : public CIRGenTypeCache { cir::GlobalViewAttr getAddrOfGlobalVarAttr(const VarDecl *D, mlir::Type Ty = {}, ForDefinition_t IsForDefinition = NotForDefinition); - + cir::FuncOp getAddrOfThunk(StringRef name, mlir::Type fnTy, GlobalDecl gd); /// Get a reference to the target of VD. mlir::Operation *getWeakRefReference(const ValueDecl *VD); diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h index 084bc7c126b7..8917c8f7e99c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h @@ -245,6 +245,8 @@ class CIRGenTypes { const clang::FunctionProtoType *type, RequiredArgs required, unsigned numPrefixArgs); + const CIRGenFunctionInfo & + arrangeUnprototypedMustTailThunk(const CXXMethodDecl *md); /// C++ methods have some special rules and also have implicit parameters. const CIRGenFunctionInfo & arrangeCXXMethodDeclaration(const clang::CXXMethodDecl *MD); diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp index 3619fcaf7a1b..938fdb5802fc 100644 --- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp @@ -35,6 +35,18 @@ using namespace clang::CIRGen; CIRGenVTables::CIRGenVTables(CIRGenModule &CGM) : CGM(CGM), VTContext(CGM.getASTContext().getVTableContext()) {} +cir::FuncOp CIRGenModule::getAddrOfThunk(StringRef name, mlir::Type fnTy, + GlobalDecl gd) { + return GetOrCreateCIRFunction(name, fnTy, gd, /*ForVTable=*/true, + /*DontDefer=*/true, /*IsThunk=*/true); +} + +static void setThunkProperties(CIRGenModule &cgm, const ThunkInfo &thunk, + cir::FuncOp thunkFn, bool forVTable, + GlobalDecl gd) { + llvm_unreachable("NYI"); +} + static bool UseRelativeLayout(const CIRGenModule &CGM) { return CGM.getTarget().getCXXABI().isItaniumFamily() && CGM.getItaniumVTableContext().isRelativeLayout(); @@ -474,8 +486,6 @@ cir::GlobalLinkageKind CIRGenModule::getVTableLinkage(const CXXRecordDecl *RD) { auto r = shouldEmitAvailableExternallyVTable(*this, RD) ? cir::GlobalLinkageKind::AvailableExternallyLinkage : cir::GlobalLinkageKind::ExternalLinkage; - assert(r == cir::GlobalLinkageKind::ExternalLinkage && - "available external NYI"); return r; } @@ -644,6 +654,134 @@ void CIRGenVTables::emitVTTDefinition(cir::GlobalOp VTT, assert(!cir::MissingFeatures::setComdat()); } } +static bool shouldEmitVTableThunk(CIRGenModule &CGM, const CXXMethodDecl *MD, + bool IsUnprototyped, bool ForVTable) { + // Always emit thunks in the MS C++ ABI. We cannot rely on other TUs to + // provide thunks for us. + if (CGM.getTarget().getCXXABI().isMicrosoft()) + return true; + + // In the Itanium C++ ABI, vtable thunks are provided by TUs that provide + // definitions of the main method. Therefore, emitting thunks with the vtable + // is purely an optimization. Emit the thunk if optimizations are enabled and + // all of the parameter types are complete. + if (ForVTable) + return CGM.getCodeGenOpts().OptimizationLevel && !IsUnprototyped; + + // Always emit thunks along with the method definition. + return true; +} + +cir::FuncOp CIRGenVTables::maybeEmitThunk(GlobalDecl GD, + const ThunkInfo &ThunkAdjustments, + bool ForVTable) { + const CXXMethodDecl *MD = cast(GD.getDecl()); + SmallString<256> Name; + MangleContext &MCtx = CGM.getCXXABI().getMangleContext(); + + llvm::raw_svector_ostream Out(Name); + if (const CXXDestructorDecl *DD = dyn_cast(MD)) { + MCtx.mangleCXXDtorThunk(DD, GD.getDtorType(), ThunkAdjustments, + /* elideOverrideInfo */ false, Out); + } else + MCtx.mangleThunk(MD, ThunkAdjustments, /* elideOverrideInfo */ false, Out); + + if (CGM.getASTContext().useAbbreviatedThunkName(GD, Name.str())) { + Name = ""; + if (const CXXDestructorDecl *dd = dyn_cast(MD)) + MCtx.mangleCXXDtorThunk(dd, GD.getDtorType(), ThunkAdjustments, + /* elideOverrideInfo */ true, Out); + else + MCtx.mangleThunk(MD, ThunkAdjustments, /* elideOverrideInfo */ true, Out); + } + + cir::FuncType ThunkVTableTy = CGM.getTypes().GetFunctionTypeForVTable(GD); + cir::FuncOp Thunk = CGM.getAddrOfThunk(Name, ThunkVTableTy, GD); + + // If we don't need to emit a definition, return this declaration as is. + bool IsUnprototyped = !CGM.getTypes().isFuncTypeConvertible( + MD->getType()->castAs()); + if (!shouldEmitVTableThunk(CGM, MD, IsUnprototyped, ForVTable)) + return Thunk; + + // Arrange a function prototype appropriate for a function definition. In some + // cases in the MS ABI, we may need to build an unprototyped musttail thunk. + const CIRGenFunctionInfo &FnInfo = + IsUnprototyped ? CGM.getTypes().arrangeUnprototypedMustTailThunk(MD) + : CGM.getTypes().arrangeGlobalDeclaration(GD); + cir::FuncType ThunkFnTy = CGM.getTypes().GetFunctionType(FnInfo); + + // This is to replace OG's casting to a function, keeping it here to + // streamline the 1-to-1 mapping from OG starting below + cir::FuncOp ThunkFn = Thunk; + if (Thunk.getFunctionType() != ThunkFnTy) { + cir::FuncOp OldThunkFn = ThunkFn; + + assert(OldThunkFn.isDeclaration() && "Shouldn't replace non-declaration"); + + // Remove the name from the old thunk function and get a new thunk. + OldThunkFn.setName(StringRef()); + auto thunkFn = + cir::FuncOp::create(CGM.getBuilder(), Thunk->getLoc(), Name.str(), + ThunkFnTy, cir::GlobalLinkageKind::ExternalLinkage); + CGM.setCIRFunctionAttributes(MD, FnInfo, thunkFn, /*IsThunk=*/false); + + if (!OldThunkFn->use_empty()) { + OldThunkFn->replaceAllUsesWith(thunkFn); + } + + // Remove the old thunk. + OldThunkFn->erase(); + } + bool ABIHasKeyFunctions = CGM.getTarget().getCXXABI().hasKeyFunctions(); + bool UseAvailableExternallyLinkage = ForVTable && ABIHasKeyFunctions; + // If the type of the underlying GlobalValue is wrong, we'll have to replace + // it. It should be a declaration. + if (!ThunkFn.isDeclaration()) { + if (!ABIHasKeyFunctions || UseAvailableExternallyLinkage) { + // There is already a thunk emitted for this function, do nothing. + return ThunkFn; + } + + setThunkProperties(CGM, ThunkAdjustments, ThunkFn, ForVTable, GD); + return ThunkFn; + } + if (IsUnprototyped) + ThunkFn->setAttr("thunk", mlir::UnitAttr::get(&CGM.getMLIRContext())); + + CGM.setCIRFunctionAttributesForDefinition(GD.getDecl(), ThunkFn); + // + // Thunks for variadic methods are special because in general variadic + // arguments cannot be perfectly forwarded. In the general case, clang + // implements such thunks by cloning the original function body. However, for + // thunks with no return adjustment on targets that support musttail, we can + // use musttail to perfectly forward the variadic arguments. + bool ShouldCloneVarArgs = false; + if (!IsUnprototyped && ThunkFn.getFunctionType().isVarArg()) { + ShouldCloneVarArgs = true; + if (ThunkAdjustments.Return.isEmpty()) { + switch (CGM.getTriple().getArch()) { + case llvm::Triple::x86_64: + case llvm::Triple::x86: + case llvm::Triple::aarch64: + ShouldCloneVarArgs = false; + break; + default: + break; + } + } + } + if (ShouldCloneVarArgs) { + if (UseAvailableExternallyLinkage) + return ThunkFn; + llvm_unreachable("NYI method, see OG GenerateVarArgsThunk"); + } else { + llvm_unreachable("NYI method, see OG generateThunk"); + } + + setThunkProperties(CGM, ThunkAdjustments, ThunkFn, ForVTable, GD); + return ThunkFn; +} void CIRGenVTables::emitThunks(GlobalDecl GD) { const CXXMethodDecl *MD = @@ -659,8 +797,8 @@ void CIRGenVTables::emitThunks(GlobalDecl GD) { if (!ThunkInfoVector) return; - for ([[maybe_unused]] const ThunkInfo &Thunk : *ThunkInfoVector) - llvm_unreachable("NYI"); + for (const ThunkInfo &Thunk : *ThunkInfoVector) + maybeEmitThunk(GD, Thunk, /*ForVTable=*/false); } bool CIRGenModule::AlwaysHasLTOVisibilityPublic(const CXXRecordDecl *RD) { diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.h b/clang/lib/CIR/CodeGen/CIRGenVTables.h index 451ea2cbec4f..bfbcef59e554 100644 --- a/clang/lib/CIR/CodeGen/CIRGenVTables.h +++ b/clang/lib/CIR/CodeGen/CIRGenVTables.h @@ -57,6 +57,10 @@ class CIRGenVTables { /// Cache for the deleted virtual member call function. cir::FuncOp DeletedVirtualFn = nullptr; + /// Get the address of a thunk and emit it if necessary. + cir::FuncOp maybeEmitThunk(GlobalDecl gd, const ThunkInfo &thunkAdjustments, + bool forVTable); + void addVTableComponent(ConstantArrayBuilder &builder, const VTableLayout &layout, unsigned componentIndex, mlir::Attribute rtti, unsigned &nextVTableThunkIndex,