From f6ddf1b4f9cc7b2a5016d46fd6d0d2726946988c Mon Sep 17 00:00:00 2001 From: David Majnemer Date: Fri, 6 Mar 2015 18:53:55 +0000 Subject: [PATCH] MS ABI: Insert copy-constructors into the CatchableType Find all unambiguous public classes of the exception object's class type and reference all of their copy constructors. Yes, this is not conforming but it is necessary in order to implement their ABI. This is because the copy constructor is actually referenced by the metadata describing which catch handlers are eligible to handle the exception object. N.B. This doesn't yet handle the copy constructor closure case yet, that work is ongoing. Differential Revision: http://reviews.llvm.org/D8101 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@231499 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 6 ++ include/clang/AST/Mangle.h | 4 +- lib/AST/ASTContext.cpp | 13 ++++ lib/AST/CXXABI.h | 9 +++ lib/AST/ItaniumCXXABI.cpp | 8 +++ lib/AST/MicrosoftCXXABI.cpp | 23 +++++-- lib/AST/MicrosoftMangle.cpp | 28 ++++++--- lib/CodeGen/MicrosoftCXXABI.cpp | 19 +++--- lib/Sema/SemaExprCXX.cpp | 80 +++++++++++++++++++++---- test/CodeGenCXX/microsoft-abi-throw.cpp | 6 +- 10 files changed, 161 insertions(+), 35 deletions(-) diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 9b22aa4a83..de6b8c668d 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -2193,6 +2193,12 @@ public: /// it is not used. bool DeclMustBeEmitted(const Decl *D); + const CXXConstructorDecl * + getCopyConstructorForExceptionObject(CXXRecordDecl *RD); + + void addCopyConstructorForExceptionObject(CXXRecordDecl *RD, + CXXConstructorDecl *CD); + void setManglingNumber(const NamedDecl *ND, unsigned Number); unsigned getManglingNumber(const NamedDecl *ND) const; diff --git a/include/clang/AST/Mangle.h b/include/clang/AST/Mangle.h index be5c2e2855..ef030d26b4 100644 --- a/include/clang/AST/Mangle.h +++ b/include/clang/AST/Mangle.h @@ -203,8 +203,8 @@ public: virtual void mangleCXXCatchableTypeArray(QualType T, uint32_t NumEntries, raw_ostream &Out) = 0; - virtual void mangleCXXCatchableType(QualType T, uint32_t Size, - raw_ostream &Out) = 0; + virtual void mangleCXXCatchableType(QualType T, const CXXConstructorDecl *CD, + uint32_t Size, raw_ostream &Out) = 0; virtual void mangleCXXRTTIBaseClassDescriptor( const CXXRecordDecl *Derived, uint32_t NVOffset, int32_t VBPtrOffset, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index fc40f7e8e2..9cfc97b926 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -8189,6 +8189,19 @@ MangleNumberingContext *ASTContext::createMangleNumberingContext() const { return ABI->createMangleNumberingContext(); } +const CXXConstructorDecl * +ASTContext::getCopyConstructorForExceptionObject(CXXRecordDecl *RD) { + return ABI->getCopyConstructorForExceptionObject( + cast(RD->getFirstDecl())); +} + +void ASTContext::addCopyConstructorForExceptionObject(CXXRecordDecl *RD, + CXXConstructorDecl *CD) { + return ABI->addCopyConstructorForExceptionObject( + cast(RD->getFirstDecl()), + cast(CD->getFirstDecl())); +} + void ASTContext::setParameterIndex(const ParmVarDecl *D, unsigned int index) { ParamIndices[D] = index; } diff --git a/lib/AST/CXXABI.h b/lib/AST/CXXABI.h index 8e9e358525..62c96f901e 100644 --- a/lib/AST/CXXABI.h +++ b/lib/AST/CXXABI.h @@ -20,6 +20,7 @@ namespace clang { class ASTContext; +class CXXConstructorDecl; class MemberPointerType; class MangleNumberingContext; @@ -41,6 +42,14 @@ public: /// Returns a new mangling number context for this C++ ABI. virtual MangleNumberingContext *createMangleNumberingContext() const = 0; + + /// Adds a mapping from class to copy constructor for this C++ ABI. + virtual void addCopyConstructorForExceptionObject(CXXRecordDecl *, + CXXConstructorDecl *) = 0; + + /// Retrieves the mapping from class to copy constructor for this C++ ABI. + virtual const CXXConstructorDecl * + getCopyConstructorForExceptionObject(CXXRecordDecl *) = 0; }; /// Creates an instance of a C++ ABI class. diff --git a/lib/AST/ItaniumCXXABI.cpp b/lib/AST/ItaniumCXXABI.cpp index 378121c8e5..13d4dbf1f4 100644 --- a/lib/AST/ItaniumCXXABI.cpp +++ b/lib/AST/ItaniumCXXABI.cpp @@ -133,6 +133,14 @@ public: return Layout.getNonVirtualSize() == PointerSize; } + const CXXConstructorDecl * + getCopyConstructorForExceptionObject(CXXRecordDecl *RD) override { + return nullptr; + } + + void addCopyConstructorForExceptionObject(CXXRecordDecl *RD, + CXXConstructorDecl *CD) override {} + MangleNumberingContext *createMangleNumberingContext() const override { return new ItaniumNumberingContext(); } diff --git a/lib/AST/MicrosoftCXXABI.cpp b/lib/AST/MicrosoftCXXABI.cpp index 0603d3b7b9..71b21bff77 100644 --- a/lib/AST/MicrosoftCXXABI.cpp +++ b/lib/AST/MicrosoftCXXABI.cpp @@ -63,6 +63,8 @@ public: class MicrosoftCXXABI : public CXXABI { ASTContext &Context; + llvm::SmallDenseMap RecordToCopyCtor; + public: MicrosoftCXXABI(ASTContext &Ctx) : Context(Ctx) { } @@ -82,13 +84,26 @@ public: return false; const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); - + // In the Microsoft ABI, classes can have one or two vtable pointers. - CharUnits PointerSize = - Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); + CharUnits PointerSize = + Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); return Layout.getNonVirtualSize() == PointerSize || Layout.getNonVirtualSize() == PointerSize * 2; - } + } + + 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; + } MangleNumberingContext *createMangleNumberingContext() const override { return new MicrosoftNumberingContext(); diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index 29af2cb9c2..3f660c9f69 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -114,8 +114,8 @@ public: uint32_t NumEntries, raw_ostream &Out) override; void mangleCXXCatchableTypeArray(QualType T, uint32_t NumEntries, raw_ostream &Out) override; - void mangleCXXCatchableType(QualType T, uint32_t Size, - raw_ostream &Out) override; + void mangleCXXCatchableType(QualType T, const CXXConstructorDecl *CD, + uint32_t Size, raw_ostream &Out) override; void mangleCXXRTTI(QualType T, raw_ostream &Out) override; void mangleCXXRTTIName(QualType T, raw_ostream &Out) override; void mangleCXXRTTIBaseClassDescriptor(const CXXRecordDecl *Derived, @@ -2307,13 +2307,25 @@ void MicrosoftMangleContextImpl::mangleCXXCatchableTypeArray( Mangler.mangleType(T, SourceRange(), MicrosoftCXXNameMangler::QMM_Result); } -void MicrosoftMangleContextImpl::mangleCXXCatchableType(QualType T, - uint32_t Size, - raw_ostream &Out) { +void MicrosoftMangleContextImpl::mangleCXXCatchableType( + QualType T, const CXXConstructorDecl *CD, uint32_t Size, raw_ostream &Out) { MicrosoftCXXNameMangler Mangler(*this, Out); - Mangler.getStream() << "_CT??_R0"; - Mangler.mangleType(T, SourceRange(), MicrosoftCXXNameMangler::QMM_Result); - Mangler.getStream() << "@8"; + Mangler.getStream() << "_CT"; + + llvm::SmallString<64> RTTIMangling; + { + llvm::raw_svector_ostream Stream(RTTIMangling); + mangleCXXRTTI(T, Stream); + } + Mangler.getStream() << RTTIMangling.substr(1); + + llvm::SmallString<64> CopyCtorMangling; + if (CD) { + llvm::raw_svector_ostream Stream(CopyCtorMangling); + mangleCXXCtor(CD, Ctor_Complete, Stream); + } + Mangler.getStream() << CopyCtorMangling.substr(1); + Mangler.getStream() << Size; } diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index dd4c274e24..4e3d50b408 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -17,6 +17,7 @@ #include "CGCXXABI.h" #include "CGVTables.h" #include "CodeGenModule.h" +#include "CodeGenTypes.h" #include "TargetInfo.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" @@ -3225,11 +3226,14 @@ llvm::Constant *MicrosoftCXXABI::getCatchableType(QualType T, uint32_t VBIndex) { assert(!T->isReferenceType()); + CXXRecordDecl *RD = T->getAsCXXRecordDecl(); + const CXXConstructorDecl *CD = + RD ? CGM.getContext().getCopyConstructorForExceptionObject(RD) : nullptr; uint32_t Size = getContext().getTypeSizeInChars(T).getQuantity(); SmallString<256> MangledName; { llvm::raw_svector_ostream Out(MangledName); - getMangleContext().mangleCXXCatchableType(T, Size, Out); + getMangleContext().mangleCXXCatchableType(T, CD, Size, Out); } if (llvm::GlobalVariable *GV = CGM.getModule().getNamedGlobal(MangledName)) return getImageRelativeConstant(GV); @@ -3241,16 +3245,15 @@ llvm::Constant *MicrosoftCXXABI::getCatchableType(QualType T, // The runtime is responsible for calling the copy constructor if the // exception is caught by value. llvm::Constant *CopyCtor = - getImageRelativeConstant(llvm::Constant::getNullValue(CGM.Int8PtrTy)); + CD ? llvm::ConstantExpr::getBitCast( + CGM.getAddrOfCXXStructor(CD, StructorType::Complete), + CGM.Int8PtrTy) + : llvm::Constant::getNullValue(CGM.Int8PtrTy); + CopyCtor = getImageRelativeConstant(CopyCtor); - bool IsScalar = true; + bool IsScalar = !RD; bool HasVirtualBases = false; bool IsStdBadAlloc = false; // std::bad_alloc is special for some reason. - if (T->getAsCXXRecordDecl()) { - IsScalar = false; - // TODO: Fill in the CopyCtor here! This is not trivial due to - // copy-constructors possessing things like default arguments. - } QualType PointeeType = T; if (T->isPointerType()) PointeeType = T->getPointeeType(); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 29e5150073..0cedf349b9 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -657,6 +657,55 @@ ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex, CXXThrowExpr(Ex, Context.VoidTy, OpLoc, IsThrownVarInScope); } +static void +collectPublicBases(CXXRecordDecl *RD, + llvm::DenseMap &SubobjectsSeen, + llvm::SmallPtrSetImpl &VBases, + llvm::SetVector &PublicSubobjectsSeen, + bool ParentIsPublic) { + for (const CXXBaseSpecifier &BS : RD->bases()) { + CXXRecordDecl *BaseDecl = BS.getType()->getAsCXXRecordDecl(); + bool NewSubobject; + // Virtual bases constitute the same subobject. Non-virtual bases are + // always distinct subobjects. + if (BS.isVirtual()) + NewSubobject = VBases.insert(BaseDecl).second; + else + NewSubobject = true; + + if (NewSubobject) + ++SubobjectsSeen[BaseDecl]; + + // Only add subobjects which have public access throughout the entire chain. + bool PublicPath = ParentIsPublic && BS.getAccessSpecifier() == AS_public; + if (PublicPath) + PublicSubobjectsSeen.insert(BaseDecl); + + // Recurse on to each base subobject. + collectPublicBases(BaseDecl, SubobjectsSeen, VBases, PublicSubobjectsSeen, + PublicPath); + } +} + +static void getUnambiguousPublicSubobjects( + CXXRecordDecl *RD, llvm::SmallVectorImpl &Objects) { + llvm::DenseMap SubobjectsSeen; + llvm::SmallSet VBases; + llvm::SetVector PublicSubobjectsSeen; + SubobjectsSeen[RD] = 1; + PublicSubobjectsSeen.insert(RD); + collectPublicBases(RD, SubobjectsSeen, VBases, PublicSubobjectsSeen, + /*ParentIsPublic=*/true); + + for (CXXRecordDecl *PublicSubobject : PublicSubobjectsSeen) { + // Skip ambiguous objects. + if (SubobjectsSeen[PublicSubobject] > 1) + continue; + + Objects.push_back(PublicSubobject); + } +} + /// CheckCXXThrowOperand - Validate the operand of a throw. ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E, bool IsThrownVarInScope) { @@ -723,18 +772,29 @@ ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E, return E; // If the class has a destructor, we must be able to call it. - if (RD->hasIrrelevantDestructor()) - return E; + if (!RD->hasIrrelevantDestructor()) { + if (CXXDestructorDecl *Destructor = LookupDestructor(RD)) { + MarkFunctionReferenced(E->getExprLoc(), Destructor); + CheckDestructorAccess(E->getExprLoc(), Destructor, + PDiag(diag::err_access_dtor_exception) << Ty); + if (DiagnoseUseOfDecl(Destructor, E->getExprLoc())) + return ExprError(); + } + } - CXXDestructorDecl *Destructor = LookupDestructor(RD); - if (!Destructor) - return E; + if (Context.getTargetInfo().getCXXABI().isMicrosoft()) { + llvm::SmallVector UnambiguousPublicSubobjects; + getUnambiguousPublicSubobjects(RD, UnambiguousPublicSubobjects); + for (CXXRecordDecl *Subobject : UnambiguousPublicSubobjects) { + if (CXXConstructorDecl *CD = LookupCopyingConstructor(Subobject, 0)) { + if (CD->isTrivial()) + continue; + MarkFunctionReferenced(E->getExprLoc(), CD); + Context.addCopyConstructorForExceptionObject(Subobject, CD); + } + } + } - MarkFunctionReferenced(E->getExprLoc(), Destructor); - CheckDestructorAccess(E->getExprLoc(), Destructor, - PDiag(diag::err_access_dtor_exception) << Ty); - if (DiagnoseUseOfDecl(Destructor, E->getExprLoc())) - return ExprError(); return E; } diff --git a/test/CodeGenCXX/microsoft-abi-throw.cpp b/test/CodeGenCXX/microsoft-abi-throw.cpp index 93d051e8eb..f0151c5d39 100644 --- a/test/CodeGenCXX/microsoft-abi-throw.cpp +++ b/test/CodeGenCXX/microsoft-abi-throw.cpp @@ -1,16 +1,16 @@ // RUN: %clang_cc1 -emit-llvm -o - -triple=i386-pc-win32 %s -fcxx-exceptions | FileCheck %s // CHECK-DAG: @"\01??_R0?AUY@@@8" = linkonce_odr global %rtti.TypeDescriptor7 { i8** @"\01??_7type_info@@6B@", i8* null, [8 x i8] c".?AUY@@\00" }, comdat -// CHECK-DAG: @"_CT??_R0?AUY@@@88" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 4, i8* bitcast (%rtti.TypeDescriptor7* @"\01??_R0?AUY@@@8" to i8*), i32 0, i32 -1, i32 0, i32 8, i8* null }, comdat +// CHECK-DAG: @"_CT??_R0?AUY@@@8??0Y@@QAE@ABU0@@Z8" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 4, i8* bitcast (%rtti.TypeDescriptor7* @"\01??_R0?AUY@@@8" to i8*), i32 0, i32 -1, i32 0, i32 8, i8* bitcast (%struct.Y* (%struct.Y*, %struct.Y*, i32)* @"\01??0Y@@QAE@ABU0@@Z" to i8*) }, comdat // CHECK-DAG: @"\01??_R0?AUZ@@@8" = linkonce_odr global %rtti.TypeDescriptor7 { i8** @"\01??_7type_info@@6B@", i8* null, [8 x i8] c".?AUZ@@\00" }, comdat // CHECK-DAG: @"_CT??_R0?AUZ@@@81" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i8* bitcast (%rtti.TypeDescriptor7* @"\01??_R0?AUZ@@@8" to i8*), i32 0, i32 -1, i32 0, i32 1, i8* null }, comdat // CHECK-DAG: @"\01??_R0?AUW@@@8" = linkonce_odr global %rtti.TypeDescriptor7 { i8** @"\01??_7type_info@@6B@", i8* null, [8 x i8] c".?AUW@@\00" }, comdat -// CHECK-DAG: @"_CT??_R0?AUW@@@84" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 4, i8* bitcast (%rtti.TypeDescriptor7* @"\01??_R0?AUW@@@8" to i8*), i32 4, i32 -1, i32 0, i32 4, i8* null }, comdat +// CHECK-DAG: @"_CT??_R0?AUW@@@8??0W@@QAE@ABU0@@Z4" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 4, i8* bitcast (%rtti.TypeDescriptor7* @"\01??_R0?AUW@@@8" to i8*), i32 4, i32 -1, i32 0, i32 4, i8* bitcast (%struct.W* (%struct.W*, %struct.W*, i32)* @"\01??0W@@QAE@ABU0@@Z" to i8*) }, comdat // CHECK-DAG: @"\01??_R0?AUM@@@8" = linkonce_odr global %rtti.TypeDescriptor7 { i8** @"\01??_7type_info@@6B@", i8* null, [8 x i8] c".?AUM@@\00" }, comdat // CHECK-DAG: @"_CT??_R0?AUM@@@81" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i8* bitcast (%rtti.TypeDescriptor7* @"\01??_R0?AUM@@@8" to i8*), i32 8, i32 -1, i32 0, i32 1, i8* null }, comdat // CHECK-DAG: @"\01??_R0?AUV@@@8" = linkonce_odr global %rtti.TypeDescriptor7 { i8** @"\01??_7type_info@@6B@", i8* null, [8 x i8] c".?AUV@@\00" }, comdat // CHECK-DAG: @"_CT??_R0?AUV@@@81" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 0, i8* bitcast (%rtti.TypeDescriptor7* @"\01??_R0?AUV@@@8" to i8*), i32 0, i32 4, i32 4, i32 1, i8* null }, comdat -// CHECK-DAG: @"_CTA5?AUY@@" = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.5 { i32 5, [5 x %eh.CatchableType*] [%eh.CatchableType* @"_CT??_R0?AUY@@@88", %eh.CatchableType* @"_CT??_R0?AUZ@@@81", %eh.CatchableType* @"_CT??_R0?AUW@@@84", %eh.CatchableType* @"_CT??_R0?AUM@@@81", %eh.CatchableType* @"_CT??_R0?AUV@@@81"] }, comdat +// CHECK-DAG: @"_CTA5?AUY@@" = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.5 { i32 5, [5 x %eh.CatchableType*] [%eh.CatchableType* @"_CT??_R0?AUY@@@8??0Y@@QAE@ABU0@@Z8", %eh.CatchableType* @"_CT??_R0?AUZ@@@81", %eh.CatchableType* @"_CT??_R0?AUW@@@8??0W@@QAE@ABU0@@Z4", %eh.CatchableType* @"_CT??_R0?AUM@@@81", %eh.CatchableType* @"_CT??_R0?AUV@@@81"] }, comdat // CHECK-DAG: @"_TI5?AUY@@" = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i8* bitcast (void (%struct.Y*)* @"\01??_DY@@QAE@XZ" to i8*), i8* null, i8* bitcast (%eh.CatchableTypeArray.5* @"_CTA5?AUY@@" to i8*) }, comdat -- 2.40.0