From 359b885e12815e8d63adcfb5760696bad82c6908 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 25 Jan 2013 22:30:49 +0000 Subject: [PATCH] First pass at abstracting out a class for the target C++ ABI. llvm-svn: 173514 --- clang/include/clang/Basic/TargetCXXABI.h | 159 ++++++++++++++++++++++++++++ clang/include/clang/Basic/TargetInfo.h | 36 ++----- clang/lib/AST/ASTContext.cpp | 21 ++-- clang/lib/AST/RecordLayout.cpp | 7 +- clang/lib/AST/RecordLayoutBuilder.cpp | 8 +- clang/lib/AST/VTableBuilder.cpp | 3 +- clang/lib/Basic/TargetInfo.cpp | 16 ++- clang/lib/Basic/Targets.cpp | 5 +- clang/lib/CodeGen/CGClass.cpp | 5 +- clang/lib/CodeGen/CGRecordLayoutBuilder.cpp | 2 +- clang/lib/CodeGen/CodeGenModule.cpp | 11 +- clang/lib/Sema/SemaType.cpp | 15 ++- 12 files changed, 230 insertions(+), 58 deletions(-) create mode 100644 clang/include/clang/Basic/TargetCXXABI.h diff --git a/clang/include/clang/Basic/TargetCXXABI.h b/clang/include/clang/Basic/TargetCXXABI.h new file mode 100644 index 0000000..2a8f5d0 --- /dev/null +++ b/clang/include/clang/Basic/TargetCXXABI.h @@ -0,0 +1,159 @@ +//===--- TargetCXXABI.h - C++ ABI Target Configuration ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines the TargetCXXABI class, which abstracts details of the +/// C++ ABI that we're targeting. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TARGETCXXABI_H +#define LLVM_CLANG_TARGETCXXABI_H + +#include "llvm/ADT/Triple.h" +#include "llvm/Support/ErrorHandling.h" + +namespace clang { + +/// \brief The basic abstraction for the target C++ ABI. +class TargetCXXABI { +public: + /// \brief The basic C++ ABI kind. + enum Kind { + /// The generic Itanium ABI is the standard ABI of most open-source + /// and Unix-like platforms. It is the primary ABI targeted by + /// many compilers, including Clang and GCC. + /// + /// It is documented here: + /// http://www.codesourcery.com/public/cxx-abi/ + GenericItanium, + + /// The generic ARM ABI is a modified version of the Itanium ABI + /// proposed by ARM for use on ARM-based platforms. + /// + /// These changes include: + /// - the representation of member function pointers is adjusted + /// to not conflict with the 'thumb' bit of ARM function pointers; + /// - constructors and destructors return 'this'; + /// - guard variables are smaller; + /// - inline functions are never key functions; + /// - array cookies have a slightly different layout; + /// - additional convenience functions are specified; + /// - and more! + /// + /// It is documented here: + /// http://infocenter.arm.com + /// /help/topic/com.arm.doc.ihi0041c/IHI0041C_cppabi.pdf + GenericARM, + + /// The iOS ABI is a partial implementation of the ARM ABI. + /// Several of the features of the ARM ABI were not fully implemented + /// in the compilers that iOS was launched with. + /// + /// Essentially, the iOS ABI includes the ARM changes to: + /// - member function pointers, + /// - guard variables, + /// - array cookies, and + /// - constructor/destructor signatures. + iOS, + + /// The Microsoft ABI is the ABI used by Microsoft Visual Studio (and + /// compatible compilers). + /// + /// FIXME: should this be split into Win32 and Win64 variants? + /// + /// Only scattered and incomplete official documentation exists. + Microsoft + }; + +private: + // Right now, this class is passed around as a cheap value type. + // If you add more members, especially non-POD members, please + // audit the users to pass it by reference instead. + Kind TheKind; + +public: + /// A bogus initialization of the platform ABI. + TargetCXXABI() : TheKind(GenericItanium) {} + + TargetCXXABI(Kind kind) : TheKind(kind) {} + + void set(Kind kind) { + TheKind = kind; + } + + Kind getKind() const { return TheKind; } + + /// \brief Does this ABI generally fall into the Itanium family of ABIs? + bool isItaniumFamily() const { + switch (getKind()) { + case GenericItanium: + case GenericARM: + case iOS: + return true; + + case Microsoft: + return false; + } + llvm_unreachable("bad ABI kind"); + } + + /// \brief Is this ABI an MSVC-compatible ABI? + bool isMicrosoft() const { + switch (getKind()) { + case GenericItanium: + case GenericARM: + case iOS: + return false; + + case Microsoft: + return true; + } + llvm_unreachable("bad ABI kind"); + } + + /// \brief Is the default C++ member function calling convention + /// the same as the default calling convention? + bool isMemberFunctionCCDefault() const { + // Right now, this is always true for Microsoft. + return !isMicrosoft(); + } + + /// \brief Does this ABI have different entrypoints for complete-object + /// and base-subobject constructors? + bool hasConstructorVariants() const { + return isItaniumFamily(); + } + + /// \brief Does this ABI have different entrypoints for complete-object + /// and base-subobject destructors? + bool hasDestructorVariants() const { + return isItaniumFamily(); + } + + /// \brief Does this ABI allow virtual bases to be primary base classes? + bool hasPrimaryVBases() const { + return isItaniumFamily(); + } + + /// Try to parse an ABI name, returning false on error. + bool tryParse(llvm::StringRef name); + + friend bool operator==(const TargetCXXABI &left, const TargetCXXABI &right) { + return left.getKind() == right.getKind(); + } + + friend bool operator!=(const TargetCXXABI &left, const TargetCXXABI &right) { + return !(left == right); + } +}; + +} // end namespace clang + +#endif diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 463c2ea..716d776 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -16,6 +16,7 @@ #define LLVM_CLANG_BASIC_TARGETINFO_H #include "clang/Basic/AddressSpaces.h" +#include "clang/Basic/TargetCXXABI.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetOptions.h" @@ -43,22 +44,6 @@ class SourceManager; namespace Builtin { struct Info; } -/// \brief The types of C++ ABIs for which we can generate code. -enum TargetCXXABI { - /// The generic ("Itanium") C++ ABI, documented at: - /// http://www.codesourcery.com/public/cxx-abi/ - CXXABI_Itanium, - - /// The ARM C++ ABI, based largely on the Itanium ABI but with - /// significant differences. - /// http://infocenter.arm.com - /// /help/topic/com.arm.doc.ihi0041c/IHI0041C_cppabi.pdf - CXXABI_ARM, - - /// The Visual Studio ABI. Only scattered official documentation exists. - CXXABI_Microsoft -}; - /// \brief Exposes information about the current target. /// class TargetInfo : public RefCountedBase { @@ -89,7 +74,7 @@ protected: const llvm::fltSemantics *HalfFormat, *FloatFormat, *DoubleFormat, *LongDoubleFormat; unsigned char RegParmMax, SSERegParmMax; - TargetCXXABI CXXABI; + TargetCXXABI TheCXXABI; const LangAS::Map *AddrSpaceMap; mutable StringRef PlatformName; @@ -634,8 +619,8 @@ public: } /// \brief Get the C++ ABI currently in use. - virtual TargetCXXABI getCXXABI() const { - return CXXABI; + TargetCXXABI getCXXABI() const { + return TheCXXABI; } /// \brief Target the specified CPU. @@ -655,14 +640,9 @@ public: /// \brief Use this specified C++ ABI. /// /// \return False on error (invalid C++ ABI name). - bool setCXXABI(const std::string &Name) { - static const TargetCXXABI Unknown = static_cast(-1); - TargetCXXABI ABI = llvm::StringSwitch(Name) - .Case("arm", CXXABI_ARM) - .Case("itanium", CXXABI_Itanium) - .Case("microsoft", CXXABI_Microsoft) - .Default(Unknown); - if (ABI == Unknown) return false; + bool setCXXABI(llvm::StringRef name) { + TargetCXXABI ABI; + if (!ABI.tryParse(name)) return false; return setCXXABI(ABI); } @@ -670,7 +650,7 @@ public: /// /// \return False on error (ABI not valid on this target) virtual bool setCXXABI(TargetCXXABI ABI) { - CXXABI = ABI; + TheCXXABI = ABI; return true; } diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index fff2f82..b5d60f5 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -583,12 +583,13 @@ ASTContext::getCanonicalTemplateTemplateParmDecl( CXXABI *ASTContext::createCXXABI(const TargetInfo &T) { if (!LangOpts.CPlusPlus) return 0; - switch (T.getCXXABI()) { - case CXXABI_ARM: + switch (T.getCXXABI().getKind()) { + case TargetCXXABI::GenericARM: + case TargetCXXABI::iOS: return CreateARMCXXABI(*this); - case CXXABI_Itanium: + case TargetCXXABI::GenericItanium: return CreateItaniumCXXABI(*this); - case CXXABI_Microsoft: + case TargetCXXABI::Microsoft: return CreateMicrosoftCXXABI(*this); } llvm_unreachable("Invalid CXXABI type!"); @@ -7607,7 +7608,8 @@ CallingConv ASTContext::getDefaultCXXMethodCallConv(bool isVariadic) { } CallingConv ASTContext::getCanonicalCallConv(CallingConv CC) const { - if (CC == CC_C && !LangOpts.MRTD && getTargetInfo().getCXXABI() != CXXABI_Microsoft) + if (CC == CC_C && !LangOpts.MRTD && + getTargetInfo().getCXXABI().isMemberFunctionCCDefault()) return CC_Default; return CC; } @@ -7618,11 +7620,12 @@ bool ASTContext::isNearlyEmpty(const CXXRecordDecl *RD) const { } MangleContext *ASTContext::createMangleContext() { - switch (Target->getCXXABI()) { - case CXXABI_ARM: - case CXXABI_Itanium: + switch (Target->getCXXABI().getKind()) { + case TargetCXXABI::GenericItanium: + case TargetCXXABI::GenericARM: + case TargetCXXABI::iOS: return createItaniumMangleContext(*this, getDiagnostics()); - case CXXABI_Microsoft: + case TargetCXXABI::Microsoft: return createMicrosoftMangleContext(*this, getDiagnostics()); } llvm_unreachable("Unsupported ABI"); diff --git a/clang/lib/AST/RecordLayout.cpp b/clang/lib/AST/RecordLayout.cpp index 2ae0aab..f6cfe63 100644 --- a/clang/lib/AST/RecordLayout.cpp +++ b/clang/lib/AST/RecordLayout.cpp @@ -75,10 +75,9 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, #ifndef NDEBUG if (const CXXRecordDecl *PrimaryBase = getPrimaryBase()) { if (isPrimaryBaseVirtual()) { - // Microsoft ABI doesn't have primary virtual base - if (Ctx.getTargetInfo().getCXXABI() != CXXABI_Microsoft) { - assert(getVBaseClassOffset(PrimaryBase).isZero() && - "Primary virtual base must be at offset 0!"); + if (Ctx.getTargetInfo().getCXXABI().hasPrimaryVBases()) { + assert(getVBaseClassOffset(PrimaryBase).isZero() && + "Primary virtual base must be at offset 0!"); } } else { assert(getBaseClassOffset(PrimaryBase).isZero() && diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp index 7f84c56..08f6d5c 100644 --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -676,8 +676,12 @@ protected: bool FieldPacked, const FieldDecl *D); void LayoutBitField(const FieldDecl *D); + TargetCXXABI getCXXABI() const { + return Context.getTargetInfo().getCXXABI(); + } + bool isMicrosoftCXXABI() const { - return Context.getTargetInfo().getCXXABI() == CXXABI_Microsoft; + return getCXXABI().isMicrosoft(); } void MSLayoutVirtualBases(const CXXRecordDecl *RD); @@ -2606,7 +2610,7 @@ static void DumpCXXRecordLayout(raw_ostream &OS, // Vtable pointer. if (RD->isDynamicClass() && !PrimaryBase && - C.getTargetInfo().getCXXABI() != CXXABI_Microsoft) { + !C.getTargetInfo().getCXXABI().isMicrosoft()) { PrintOffset(OS, Offset, IndentLevel); OS << '(' << *RD << " vtable pointer)\n"; } diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp index 00a186e..5af3b65 100644 --- a/clang/lib/AST/VTableBuilder.cpp +++ b/clang/lib/AST/VTableBuilder.cpp @@ -2197,7 +2197,8 @@ VTableLayout::~VTableLayout() { } VTableContext::VTableContext(ASTContext &Context) : Context(Context), - IsMicrosoftABI(Context.getTargetInfo().getCXXABI() == CXXABI_Microsoft) { } + IsMicrosoftABI(Context.getTargetInfo().getCXXABI().isMicrosoft()) { +} VTableContext::~VTableContext() { llvm::DeleteContainerSeconds(VTableLayouts); diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 9cf04af..fe68a9d 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -84,7 +84,7 @@ TargetInfo::TargetInfo(const std::string &T) : TargetOpts(), Triple(T) ComplexLongDoubleUsesFP2Ret = false; // Default to using the Itanium ABI. - CXXABI = CXXABI_Itanium; + TheCXXABI.set(TargetCXXABI::GenericItanium); // Default to an empty address space map. AddrSpaceMap = &DefaultAddrSpaceMap; @@ -496,3 +496,17 @@ bool TargetInfo::validateInputConstraint(ConstraintInfo *OutputConstraints, return true; } + +bool TargetCXXABI::tryParse(llvm::StringRef name) { + const Kind unknown = static_cast(-1); + Kind kind = llvm::StringSwitch(name) + .Case("arm", GenericARM) + .Case("ios", iOS) + .Case("itanium", GenericItanium) + .Case("microsoft", Microsoft) + .Default(unknown); + if (kind == unknown) return false; + + set(kind); + return true; +} diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp index 084b6b3..b90d49e 100644 --- a/clang/lib/Basic/Targets.cpp +++ b/clang/lib/Basic/Targets.cpp @@ -3081,7 +3081,7 @@ public: } // ARM targets default to using the ARM C++ ABI. - CXXABI = CXXABI_ARM; + TheCXXABI.set(TargetCXXABI::GenericARM); // ARM has atomics up to 8 bytes // FIXME: Set MaxAtomicInlineWidth if we have the feature v6e @@ -3491,6 +3491,9 @@ public: // iOS always has 64-bit atomic instructions. // FIXME: This should be based off of the target features in ARMTargetInfo. MaxAtomicInlineWidth = 64; + + // Darwin on iOS uses a variant of the ARM C++ ABI. + TheCXXABI.set(TargetCXXABI::iOS); } }; } // end anonymous namespace. diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 585d3ba..5d1e30f 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -721,7 +721,7 @@ void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) { // Before we go any further, try the complete->base constructor // delegation optimization. if (CtorType == Ctor_Complete && IsConstructorDelegationValid(Ctor) && - CGM.getContext().getTargetInfo().getCXXABI() != CXXABI_Microsoft) { + CGM.getContext().getTargetInfo().getCXXABI().hasConstructorVariants()) { if (CGDebugInfo *DI = getDebugInfo()) DI->EmitLocation(Builder, Ctor->getLocEnd()); EmitDelegateCXXConstructorCall(Ctor, Ctor_Base, Args); @@ -920,7 +920,8 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) { // Enter the cleanup scopes for virtual bases. EnterDtorCleanups(Dtor, Dtor_Complete); - if (!isTryBody && CGM.getContext().getTargetInfo().getCXXABI() != CXXABI_Microsoft) { + if (!isTryBody && + CGM.getContext().getTargetInfo().getCXXABI().hasDestructorVariants()) { EmitCXXDestructorCall(Dtor, Dtor_Base, /*ForVirtualBase=*/false, LoadCXXThis()); break; diff --git a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp index 02920cd..2c6438b 100644 --- a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -814,7 +814,7 @@ bool CGRecordLayoutBuilder::LayoutFields(const RecordDecl *D) { // Lay out the virtual bases. The MS ABI uses a different // algorithm here due to the lack of primary virtual bases. - if (Types.getContext().getTargetInfo().getCXXABI() != CXXABI_Microsoft) { + if (Types.getContext().getTargetInfo().getCXXABI().hasPrimaryVBases()) { RD->getIndirectPrimaryBases(IndirectPrimaryBases); if (Layout.isPrimaryBaseVirtual()) IndirectPrimaryBases.insert(Layout.getPrimaryBase()); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index d5284f8..2da9dba 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -52,10 +52,13 @@ using namespace CodeGen; static const char AnnotationSection[] = "llvm.metadata"; static CGCXXABI &createCXXABI(CodeGenModule &CGM) { - switch (CGM.getContext().getTargetInfo().getCXXABI()) { - case CXXABI_ARM: return *CreateARMCXXABI(CGM); - case CXXABI_Itanium: return *CreateItaniumCXXABI(CGM); - case CXXABI_Microsoft: return *CreateMicrosoftCXXABI(CGM); + switch (CGM.getContext().getTargetInfo().getCXXABI().getKind()) { + // For IR-generation purposes, there's no significant difference + // between the ARM and iOS ABIs. + case TargetCXXABI::GenericARM: return *CreateARMCXXABI(CGM); + case TargetCXXABI::iOS: return *CreateARMCXXABI(CGM); + case TargetCXXABI::GenericItanium: return *CreateItaniumCXXABI(CGM); + case TargetCXXABI::Microsoft: return *CreateMicrosoftCXXABI(CGM); } llvm_unreachable("invalid C++ ABI kind"); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index f6cad26..4819363 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1667,11 +1667,16 @@ QualType Sema::BuildMemberPointerType(QualType T, QualType Class, return QualType(); } - // In the Microsoft ABI, the class is allowed to be an incomplete - // type. In such cases, the compiler makes a worst-case assumption. - // We make no such assumption right now, so emit an error if the - // class isn't a complete type. - if (Context.getTargetInfo().getCXXABI() == CXXABI_Microsoft && + // C++ allows the class type in a member pointer to be an incomplete type. + // In the Microsoft ABI, the size of the member pointer can vary + // according to the class type, which means that we really need a + // complete type if possible, which means we need to instantiate templates. + // + // For now, just require a complete type, which will instantiate + // templates. This will also error if the type is just forward-declared, + // which is a bug, but it's a bug that saves us from dealing with some + // complexities at the moment. + if (Context.getTargetInfo().getCXXABI().isMicrosoft() && RequireCompleteType(Loc, Class, diag::err_incomplete_type)) return QualType(); -- 2.7.4