//===------- MicrosoftCXXABI.cpp - AST support for the Microsoft C++ ABI --===// // // 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++ AST support targeting the Microsoft Visual C++ // ABI. // //===----------------------------------------------------------------------===// #include "CXXABI.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/Mangle.h" #include "clang/AST/MangleNumberingContext.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/Type.h" #include "clang/Basic/TargetInfo.h" using namespace clang; namespace { /// Numbers things which need to correspond across multiple TUs. /// Typically these are things like static locals, lambdas, or blocks. class MicrosoftNumberingContext : public MangleNumberingContext { llvm::DenseMap ManglingNumbers; unsigned LambdaManglingNumber = 0; unsigned StaticLocalNumber = 0; unsigned StaticThreadlocalNumber = 0; public: MicrosoftNumberingContext() = default; unsigned getManglingNumber(const CXXMethodDecl *CallOperator) override { return ++LambdaManglingNumber; } unsigned getManglingNumber(const BlockDecl *BD) override { const Type *Ty = nullptr; return ++ManglingNumbers[Ty]; } unsigned getStaticLocalNumber(const VarDecl *VD) override { if (VD->getTLSKind()) return ++StaticThreadlocalNumber; return ++StaticLocalNumber; } unsigned getManglingNumber(const VarDecl *VD, unsigned MSLocalManglingNumber) override { return MSLocalManglingNumber; } unsigned getManglingNumber(const TagDecl *TD, unsigned MSLocalManglingNumber) override { return MSLocalManglingNumber; } }; class MSHIPNumberingContext : public MicrosoftNumberingContext { std::unique_ptr DeviceCtx; public: using MicrosoftNumberingContext::getManglingNumber; MSHIPNumberingContext(MangleContext *DeviceMangler) { DeviceCtx = createItaniumNumberingContext(DeviceMangler); } unsigned getDeviceManglingNumber(const CXXMethodDecl *CallOperator) override { return DeviceCtx->getManglingNumber(CallOperator); } unsigned getManglingNumber(const TagDecl *TD, unsigned MSLocalManglingNumber) override { unsigned DeviceN = DeviceCtx->getManglingNumber(TD, MSLocalManglingNumber); unsigned HostN = MicrosoftNumberingContext::getManglingNumber(TD, MSLocalManglingNumber); if (DeviceN > 0xFFFF || HostN > 0xFFFF) { DiagnosticsEngine &Diags = TD->getASTContext().getDiagnostics(); unsigned DiagID = Diags.getCustomDiagID( DiagnosticsEngine::Error, "Mangling number exceeds limit (65535)"); Diags.Report(TD->getLocation(), DiagID); } return (DeviceN << 16) | HostN; } }; class MSSYCLNumberingContext : public MicrosoftNumberingContext { std::unique_ptr DeviceCtx; public: MSSYCLNumberingContext(MangleContext *DeviceMangler) { DeviceCtx = createItaniumNumberingContext(DeviceMangler); } unsigned getDeviceManglingNumber(const CXXMethodDecl *CallOperator) override { return DeviceCtx->getManglingNumber(CallOperator); } }; class MicrosoftCXXABI : public CXXABI { ASTContext &Context; llvm::SmallDenseMap RecordToCopyCtor; llvm::SmallDenseMap UnnamedTagDeclToDeclaratorDecl; llvm::SmallDenseMap UnnamedTagDeclToTypedefNameDecl; // MangleContext for device numbering context, which is based on Itanium C++ // ABI. std::unique_ptr DeviceMangler; public: MicrosoftCXXABI(ASTContext &Ctx) : Context(Ctx) { if (Context.getLangOpts().CUDA && Context.getAuxTargetInfo()) { assert(Context.getTargetInfo().getCXXABI().isMicrosoft() && Context.getAuxTargetInfo()->getCXXABI().isItaniumFamily() && "Unexpected combination of C++ ABIs."); DeviceMangler.reset( Context.createMangleContext(Context.getAuxTargetInfo())); } else if (Context.getLangOpts().isSYCL()) { DeviceMangler.reset( ItaniumMangleContext::create(Context, Context.getDiagnostics())); } } MemberPointerInfo getMemberPointerInfo(const MemberPointerType *MPT) const override; CallingConv getDefaultMethodCallConv(bool isVariadic) const override { if (!isVariadic && Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86) return CC_X86ThisCall; return Context.getTargetInfo().getDefaultCallingConv(); } bool isNearlyEmpty(const CXXRecordDecl *RD) const override { llvm_unreachable("unapplicable to the MS ABI"); } const CXXConstructorDecl * getCopyConstructorForExceptionObject(CXXRecordDecl *RD) override { return RecordToCopyCtor[RD]; } void addCopyConstructorForExceptionObject(CXXRecordDecl *RD, CXXConstructorDecl *CD) override { assert(CD != nullptr); assert(RecordToCopyCtor[RD] == nullptr || RecordToCopyCtor[RD] == CD); RecordToCopyCtor[RD] = CD; } void addTypedefNameForUnnamedTagDecl(TagDecl *TD, TypedefNameDecl *DD) override { TD = TD->getCanonicalDecl(); DD = DD->getCanonicalDecl(); TypedefNameDecl *&I = UnnamedTagDeclToTypedefNameDecl[TD]; if (!I) I = DD; } TypedefNameDecl *getTypedefNameForUnnamedTagDecl(const TagDecl *TD) override { return UnnamedTagDeclToTypedefNameDecl.lookup( const_cast(TD->getCanonicalDecl())); } void addDeclaratorForUnnamedTagDecl(TagDecl *TD, DeclaratorDecl *DD) override { TD = TD->getCanonicalDecl(); DD = cast(DD->getCanonicalDecl()); DeclaratorDecl *&I = UnnamedTagDeclToDeclaratorDecl[TD]; if (!I) I = DD; } DeclaratorDecl *getDeclaratorForUnnamedTagDecl(const TagDecl *TD) override { return UnnamedTagDeclToDeclaratorDecl.lookup( const_cast(TD->getCanonicalDecl())); } std::unique_ptr createMangleNumberingContext() const override { if (Context.getLangOpts().CUDA && Context.getAuxTargetInfo()) { assert(DeviceMangler && "Missing device mangler"); return std::make_unique(DeviceMangler.get()); } else if (Context.getLangOpts().isSYCL()) { assert(DeviceMangler && "Missing device mangler"); return std::make_unique(DeviceMangler.get()); } return std::make_unique(); } }; } // getNumBases() seems to only give us the number of direct bases, and not the // total. This function tells us if we inherit from anybody that uses MI, or if // we have a non-primary base class, which uses the multiple inheritance model. static bool usesMultipleInheritanceModel(const CXXRecordDecl *RD) { while (RD->getNumBases() > 0) { if (RD->getNumBases() > 1) return true; assert(RD->getNumBases() == 1); const CXXRecordDecl *Base = RD->bases_begin()->getType()->getAsCXXRecordDecl(); if (RD->isPolymorphic() && !Base->isPolymorphic()) return true; RD = Base; } return false; } MSInheritanceModel CXXRecordDecl::calculateInheritanceModel() const { if (!hasDefinition() || isParsingBaseSpecifiers()) return MSInheritanceModel::Unspecified; if (getNumVBases() > 0) return MSInheritanceModel::Virtual; if (usesMultipleInheritanceModel(this)) return MSInheritanceModel::Multiple; return MSInheritanceModel::Single; } MSInheritanceModel CXXRecordDecl::getMSInheritanceModel() const { MSInheritanceAttr *IA = getAttr(); assert(IA && "Expected MSInheritanceAttr on the CXXRecordDecl!"); return IA->getInheritanceModel(); } bool CXXRecordDecl::nullFieldOffsetIsZero() const { return !inheritanceModelHasOnlyOneField(/*IsMemberFunction=*/false, getMSInheritanceModel()) || (hasDefinition() && isPolymorphic()); } MSVtorDispMode CXXRecordDecl::getMSVtorDispMode() const { if (MSVtorDispAttr *VDA = getAttr()) return VDA->getVtorDispMode(); return getASTContext().getLangOpts().getVtorDispMode(); } // Returns the number of pointer and integer slots used to represent a member // pointer in the MS C++ ABI. // // Member function pointers have the following general form; however, fields // are dropped as permitted (under the MSVC interpretation) by the inheritance // model of the actual class. // // struct { // // A pointer to the member function to call. If the member function is // // virtual, this will be a thunk that forwards to the appropriate vftable // // slot. // void *FunctionPointerOrVirtualThunk; // // // An offset to add to the address of the vbtable pointer after // // (possibly) selecting the virtual base but before resolving and calling // // the function. // // Only needed if the class has any virtual bases or bases at a non-zero // // offset. // int NonVirtualBaseAdjustment; // // // The offset of the vb-table pointer within the object. Only needed for // // incomplete types. // int VBPtrOffset; // // // An offset within the vb-table that selects the virtual base containing // // the member. Loading from this offset produces a new offset that is // // added to the address of the vb-table pointer to produce the base. // int VirtualBaseAdjustmentOffset; // }; static std::pair getMSMemberPointerSlots(const MemberPointerType *MPT) { const CXXRecordDecl *RD = MPT->getMostRecentCXXRecordDecl(); MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); unsigned Ptrs = 0; unsigned Ints = 0; if (MPT->isMemberFunctionPointer()) Ptrs = 1; else Ints = 1; if (inheritanceModelHasNVOffsetField(MPT->isMemberFunctionPointer(), Inheritance)) Ints++; if (inheritanceModelHasVBPtrOffsetField(Inheritance)) Ints++; if (inheritanceModelHasVBTableOffsetField(Inheritance)) Ints++; return std::make_pair(Ptrs, Ints); } CXXABI::MemberPointerInfo MicrosoftCXXABI::getMemberPointerInfo( const MemberPointerType *MPT) const { // The nominal struct is laid out with pointers followed by ints and aligned // to a pointer width if any are present and an int width otherwise. const TargetInfo &Target = Context.getTargetInfo(); unsigned PtrSize = Target.getPointerWidth(LangAS::Default); unsigned IntSize = Target.getIntWidth(); unsigned Ptrs, Ints; std::tie(Ptrs, Ints) = getMSMemberPointerSlots(MPT); MemberPointerInfo MPI; MPI.HasPadding = false; MPI.Width = Ptrs * PtrSize + Ints * IntSize; // When MSVC does x86_32 record layout, it aligns aggregate member pointers to // 8 bytes. However, __alignof usually returns 4 for data memptrs and 8 for // function memptrs. if (Ptrs + Ints > 1 && Target.getTriple().isArch32Bit()) MPI.Align = 64; else if (Ptrs) MPI.Align = Target.getPointerAlign(LangAS::Default); else MPI.Align = Target.getIntAlign(); if (Target.getTriple().isArch64Bit()) { MPI.Width = llvm::alignTo(MPI.Width, MPI.Align); MPI.HasPadding = MPI.Width != (Ptrs * PtrSize + Ints * IntSize); } return MPI; } CXXABI *clang::CreateMicrosoftCXXABI(ASTContext &Ctx) { return new MicrosoftCXXABI(Ctx); }