Index: lib/CodeGen/CGCXXABI.h =================================================================== --- lib/CodeGen/CGCXXABI.h +++ lib/CodeGen/CGCXXABI.h @@ -16,6 +16,7 @@ #define LLVM_CLANG_LIB_CODEGEN_CGCXXABI_H #include "CodeGenFunction.h" +#include "CGCleanup.h" #include "clang/Basic/LLVM.h" namespace llvm { @@ -242,7 +243,9 @@ /// analysis. virtual bool canSpeculativelyEmitVTable(const CXXRecordDecl *RD) const = 0; - virtual void emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C) = 0; + virtual void + emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C, + ArrayRef LaterHandlers) = 0; virtual llvm::CallInst * emitTerminateForUnexpectedException(CodeGenFunction &CGF, Index: lib/CodeGen/CGCleanup.h =================================================================== --- lib/CodeGen/CGCleanup.h +++ lib/CodeGen/CGCleanup.h @@ -220,9 +220,13 @@ delete getHandler(I).Block; } - typedef const Handler *iterator; - iterator begin() const { return getHandlers(); } - iterator end() const { return getHandlers() + getNumHandlers(); } + typedef const Handler *const_iterator; + const_iterator begin() const { return getHandlers(); } + const_iterator end() const { return getHandlers() + getNumHandlers(); } + + typedef Handler *iterator; + iterator begin() { return getHandlers(); } + iterator end() { return getHandlers() + getNumHandlers(); } static bool classof(const EHScope *Scope) { return Scope->getKind() == Catch; Index: lib/CodeGen/CGException.cpp =================================================================== --- lib/CodeGen/CGException.cpp +++ lib/CodeGen/CGException.cpp @@ -557,12 +557,12 @@ llvm::BasicBlock *Handler = createBasicBlock("catch"); if (C->getExceptionDecl()) { - // FIXME: Dropping the reference type on the type into makes it - // impossible to correctly implement catch-by-reference - // semantics for pointers. Unfortunately, this is what all - // existing compilers do, and it's not clear that the standard - // personality routine is capable of doing this right. See C++ DR 388: - // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#388 + // Note that dropping the reference type may result in us catching + // exceptions we're not supposed to catch: for instance, a 'T&' handler + // cannot catch an exception that requires conversion to 'T', but a + // 'const T&' handler can; see C++ DR 388. The ABI-specific emission of + // catch handlers is expected to deal with this by rethrowing exceptions + // it doesn't want to catch. Qualifiers CaughtTypeQuals; QualType CaughtType = CGM.getContext().getUnqualifiedArrayType( C->getCaughtType().getNonReferenceType(), CaughtTypeQuals); @@ -1070,7 +1070,8 @@ // Initialize the catch variable and set up the cleanups. SaveAndRestore RestoreCurrentFuncletPad( CurrentFuncletPad); - CGM.getCXXABI().emitBeginCatch(*this, C); + CGM.getCXXABI().emitBeginCatch( + *this, C, llvm::makeArrayRef(Handlers.begin() + I, Handlers.end())); // Emit the PGO counter increment. incrementProfileCounter(C); Index: lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ lib/CodeGen/ItaniumCXXABI.cpp @@ -181,7 +181,11 @@ void emitRethrow(CodeGenFunction &CGF, bool isNoReturn) override; void emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) override; - void emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C) override; + void InitCatchParam(CodeGenFunction &CGF, const VarDecl &CatchParam, + Address ParamAddr, SourceLocation Loc, + ArrayRef LaterHandlers); + void emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C, + ArrayRef LaterHandlers) override; llvm::CallInst * emitTerminateForUnexpectedException(CodeGenFunction &CGF, @@ -2580,6 +2584,9 @@ /// \param DLLExport - true to mark the RTTI value as DLLExport llvm::Constant *BuildTypeInfo(QualType Ty, bool Force = false, bool DLLExport = false); + + llvm::Value *EmitCompareTypeInfo(CodeGenFunction &CGF, QualType Ty, + llvm::Value *TypeInfo); }; } @@ -3763,12 +3770,61 @@ return call; } +llvm::Value *ItaniumRTTIBuilder::EmitCompareTypeInfo(CodeGenFunction &CGF, + QualType Ty, + llvm::Value *TypeInfo) { + // Start by just comparing the address of the type_info. + llvm::Value *Equal = CGF.Builder.CreateICmpEQ(BuildTypeInfo(Ty), TypeInfo); + + // For types with unique type_info, the address comparison is sufficient. + llvm::GlobalVariable::LinkageTypes Linkage = getTypeInfoLinkage(CGM, Ty); + if (CXXABI.classifyRTTIUniqueness(Ty, Linkage) == ItaniumCXXABI::RUK_Unique) + return Equal; + + // Otherwise, we need to compare the type name string. + llvm::BasicBlock *CurBlock = CGF.Builder.GetInsertBlock(); + llvm::BasicBlock *CmpTypenameBlock = CGF.createBasicBlock("exc.typename.cmp"); + llvm::BasicBlock *CmpDoneBlock = CGF.createBasicBlock("exc.typename.done"); + CGF.Builder.CreateCondBr(Equal, CmpDoneBlock, CmpTypenameBlock); + + CGF.EmitBlock(CmpTypenameBlock); + + // The type name is the second pointer member, just after the vptr. Remove + // the high "requires string comparison" bit. + // FIXME: This is ARM64-specific. + llvm::Value *TypeNameField = CGF.Builder.CreateConstGEP1_32( + CGF.Builder.CreateBitCast(TypeInfo, CGF.CGM.Int8PtrPtrTy), 1); + TypeNameField = CGF.Builder.CreatePtrToInt(TypeNameField, CGM.Int64Ty); + llvm::Constant *Mask = + llvm::ConstantInt::get(CGM.Int64Ty, (uint64_t(1) << 63) - 1); + TypeNameField = CGF.Builder.CreateAnd(TypeNameField, Mask); + TypeNameField = CGF.Builder.CreateIntToPtr(TypeNameField, CGM.Int8PtrTy); + + // Emit a strcmp between the type_info names. + Address TypeInfoStringAddr(TypeNameField, CGF.getPointerAlign()); + llvm::Value *TypeInfoString = CGF.Builder.CreateLoad(TypeInfoStringAddr); + llvm::Value *Strcmp = CGM.CreateRuntimeFunction( + llvm::FunctionType::get(CGM.IntTy, {CGM.Int8PtrTy, CGM.Int8PtrTy}, + /*IsVarArgs=*/false), + "strcmp"); + llvm::CallInst *StrcmpValue = CGF.EmitNounwindRuntimeCall( + Strcmp, {TypeInfoString, GetAddrOfTypeName(Ty, Linkage)}); + llvm::Value *StrcmpResult = CGF.Builder.CreateICmpEQ( + StrcmpValue, llvm::ConstantInt::get(CGM.IntTy, 0)); + CGF.EmitBranch(CmpDoneBlock); + + CGF.EmitBlock(CmpDoneBlock); + llvm::PHINode *PHI = CGF.Builder.CreatePHI(Equal->getType(), 2); + PHI->addIncoming(Equal, CurBlock); + PHI->addIncoming(StrcmpResult, CmpTypenameBlock); + return PHI; +} + /// A "special initializer" callback for initializing a catch /// parameter during catch initialization. -static void InitCatchParam(CodeGenFunction &CGF, - const VarDecl &CatchParam, - Address ParamAddr, - SourceLocation Loc) { +void ItaniumCXXABI::InitCatchParam( + CodeGenFunction &CGF, const VarDecl &CatchParam, Address ParamAddr, + SourceLocation Loc, ArrayRef LaterHandlers) { // Load the exception from where the landing pad saved it. llvm::Value *Exn = CGF.getExceptionFromSlot(); @@ -3778,62 +3834,124 @@ // If we're catching by reference, we can just cast the object // pointer to the appropriate pointer. - if (isa(CatchType)) { - QualType CaughtType = cast(CatchType)->getPointeeType(); + if (auto *RT = dyn_cast(CatchType)) { + QualType CaughtType = RT->getPointeeType(); bool EndCatchMightThrow = CaughtType->isRecordType(); + bool CanBindToTemporary = + CaughtType.isConstQualified() && !CaughtType.isVolatileQualified(); + + // Determine whether a conversion was applied to the exception in + // forming the reference binding. + llvm::Value *MaybeTypeMatch = nullptr; + if (CaughtType->isPointerType() || + (CaughtType->isMemberPointerType() && !CanBindToTemporary)) { + // Locate the type_info for the exception. + unsigned HeaderSize = + CGF.CGM.getTargetCodeGenInfo().getSizeOfCxaException(); + llvm::Value *TypeInfoPtrPtr = CGF.Builder.CreateBitCast( + CGF.Builder.CreateConstGEP1_32(Exn, -HeaderSize), + CGF.CGM.VoidPtrPtrTy); + llvm::Value *TypeInfoPtr = CGF.Builder.CreateLoad( + Address(TypeInfoPtrPtr, CGF.getPointerAlign()), "exn.type_info"); + MaybeTypeMatch = ItaniumRTTIBuilder(*this).EmitCompareTypeInfo( + CGF, CaughtType.getUnqualifiedType(), TypeInfoPtr); + } // __cxa_begin_catch returns the adjusted object pointer. llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn, EndCatchMightThrow); - // We have no way to tell the personality function that we're - // catching by reference, so if we're catching a pointer, - // __cxa_begin_catch will actually return that pointer by value. - if (const PointerType *PT = dyn_cast(CaughtType)) { - QualType PointeeType = PT->getPointeeType(); - - // When catching by reference, generally we should just ignore - // this by-value pointer and use the exception object instead. - if (!PointeeType->isRecordType()) { - - // Exn points to the struct _Unwind_Exception header, which - // we have to skip past in order to reach the exception data. - unsigned HeaderSize = + // If the reference is to a pointer or pointer-to-member type and + // is not const (non-volatile) reference, and was type-converted, resume + // unwinding rather than re-entering this catch block. Note that we need + // subsequent catches in the same try statement to catch such a rethrow. + // See C++ DR 388. + if (!CanBindToTemporary && MaybeTypeMatch) { + llvm::BasicBlock *MismatchBlock = CGF.createBasicBlock("exn.mismatch"); + llvm::BasicBlock *CatchBlock = CGF.createBasicBlock("exn.match"); + CGF.Builder.CreateCondBr(MaybeTypeMatch, CatchBlock, MismatchBlock); + + // Pass the exception off to the C++ ABI library to deal with. + CGF.EmitBlock(MismatchBlock); + if (LaterHandlers.empty()) { + emitRethrow(CGF, true); + } else { + // FIXME: We could filter out definitely-unreachable catch handlers. + EHCatchScope *LaterCatchScope = + CGF.EHStack.pushCatch(LaterHandlers.size()); + std::uninitialized_copy(LaterHandlers.begin(), LaterHandlers.end(), + LaterCatchScope->begin()); + emitRethrow(CGF, true); + CGF.popCatchScope(); + } + + CGF.EmitBlock(CatchBlock); + MaybeTypeMatch = nullptr; + } + + // __cxa_begin_catch returns pointers by value, so we need to find the + // actual exception object ourselves. Exn points to the struct + // _Unwind_Exception header, which we have to skip past in order to reach + // the exception data. + // We assume the ABI library returns a pointer to the exception object + // for non-pointer exceptions (if no conversion is applied). + llvm::Value *ExnObject; + if (CaughtType->isPointerType()) { + unsigned HeaderSize = CGF.CGM.getTargetCodeGenInfo().getSizeOfUnwindException(); - AdjustedExn = CGF.Builder.CreateConstGEP1_32(Exn, HeaderSize); + llvm::Value *ExnObj = CGF.Builder.CreateConstGEP1_32(Exn, HeaderSize); + ExnObject = CGF.Builder.CreateBitCast(ExnObj, LLVMCatchTy, "exn.orig"); + } else { + ExnObject = + CGF.Builder.CreateBitCast(AdjustedExn, LLVMCatchTy, "exn.byref"); + } - // However, if we're catching a pointer-to-record type that won't - // work, because the personality function might have adjusted - // the pointer. There's actually no way for us to fully satisfy - // the language/ABI contract here: we can't use Exn because it - // might have the wrong adjustment, but we can't use the by-value - // pointer because it's off by a level of abstraction. - // - // The current solution is to dump the adjusted pointer into an - // alloca, which breaks language semantics (because changing the - // pointer doesn't change the exception) but at least works. - // The better solution would be to filter out non-exact matches - // and rethrow them, but this is tricky because the rethrow - // really needs to be catchable by other sites at this landing - // pad. The best solution is to fix the personality function. - } else { + // If a conversion may have been performed, we need to materialize a local + // temporary to hold the converted value (even if the conversion didn't + // actually change the value, it must have a different address from that + // of the exception object). + if (MaybeTypeMatch) { + assert(CaughtType->isPointerType() || CaughtType->isMemberPointerType()); + llvm::BasicBlock *PreBlock = CGF.Builder.GetInsertBlock(); + llvm::BasicBlock *ConvBlock = CGF.createBasicBlock("exn.conv"); + llvm::BasicBlock *ContBlock = CGF.createBasicBlock("exn.cont"); + CGF.Builder.CreateCondBr(MaybeTypeMatch, ContBlock, ConvBlock); + + // If the pointer was converted, the personality function might have + // adjusted it, and we need to materialize a separate temporary + // ourselves. + CGF.EmitBlock(ConvBlock); + llvm::Value *Temporary; + { // Pull the pointer for the reference type off. llvm::Type *PtrTy = cast(LLVMCatchTy)->getElementType(); // Create the temporary and write the adjusted pointer into it. - Address ExnPtrTmp = - CGF.CreateTempAlloca(PtrTy, CGF.getPointerAlign(), "exn.byref.tmp"); - llvm::Value *Casted = CGF.Builder.CreateBitCast(AdjustedExn, PtrTy); - CGF.Builder.CreateStore(Casted, ExnPtrTmp); + CharUnits Align = CGF.getNaturalTypeAlignment(CaughtType); + Address ExnPtrTmp = CGF.CreateTempAlloca(PtrTy, Align, "exn.byref.tmp"); + llvm::Value *Value; + if (CaughtType->isPointerType()) + Value = CGF.Builder.CreateBitCast(AdjustedExn, PtrTy); + else { + Address ExnAddr(AdjustedExn, Align); + ExnAddr = CGF.Builder.CreateElementBitCast(ExnAddr, PtrTy); + Value = CGF.EmitLoadOfScalar(ExnAddr, false, CaughtType, Loc); + } + CGF.EmitStoreOfScalar(Value, ExnPtrTmp, false, CaughtType); // Bind the reference to the temporary. - AdjustedExn = ExnPtrTmp.getPointer(); + Temporary = ExnPtrTmp.getPointer(); + CGF.EmitBranch(ContBlock); } + + CGF.EmitBlock(ContBlock); + llvm::PHINode *Result = CGF.Builder.CreatePHI(LLVMCatchTy, 2); + Result->addIncoming(ExnObject, PreBlock); + Result->addIncoming(Temporary, ConvBlock); + ExnObject = Result; } - llvm::Value *ExnCast = - CGF.Builder.CreateBitCast(AdjustedExn, LLVMCatchTy, "exn.byref"); - CGF.Builder.CreateStore(ExnCast, ParamAddr); + CGF.Builder.CreateStore(ExnObject, ParamAddr); return; } @@ -3943,8 +4061,9 @@ /// Begins a catch statement by initializing the catch variable and /// calling __cxa_begin_catch. -void ItaniumCXXABI::emitBeginCatch(CodeGenFunction &CGF, - const CXXCatchStmt *S) { +void ItaniumCXXABI::emitBeginCatch( + CodeGenFunction &CGF, const CXXCatchStmt *S, + ArrayRef LaterHandlers) { // We have to be very careful with the ordering of cleanups here: // C++ [except.throw]p4: // The destruction [of the exception temporary] occurs @@ -3977,7 +4096,8 @@ // Emit the local. CodeGenFunction::AutoVarEmission var = CGF.EmitAutoVarAlloca(*CatchParam); - InitCatchParam(CGF, *CatchParam, var.getObjectAddress(CGF), S->getLocStart()); + InitCatchParam(CGF, *CatchParam, var.getObjectAddress(CGF), S->getLocStart(), + LaterHandlers); CGF.EmitAutoVarCleanups(var); } Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -120,7 +120,8 @@ void emitRethrow(CodeGenFunction &CGF, bool isNoReturn) override; void emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) override; - void emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C) override; + void emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C, + ArrayRef LaterHandlers) override; llvm::GlobalVariable *getMSCompleteObjectLocator(const CXXRecordDecl *RD, const VPtrInfo &Info); @@ -904,8 +905,9 @@ }; } -void MicrosoftCXXABI::emitBeginCatch(CodeGenFunction &CGF, - const CXXCatchStmt *S) { +void MicrosoftCXXABI::emitBeginCatch( + CodeGenFunction &CGF, const CXXCatchStmt *S, + ArrayRef LaterHandlers) { // In the MS ABI, the runtime handles the copy, and the catch handler is // responsible for destruction. VarDecl *CatchParam = S->getExceptionDecl(); Index: lib/CodeGen/TargetInfo.h =================================================================== --- lib/CodeGen/TargetInfo.h +++ lib/CodeGen/TargetInfo.h @@ -65,6 +65,11 @@ virtual void emitTargetMD(const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &M) const {} + /// Determines the size of struct __cxa_exception on this platform, + /// in 8-bit units, excluding the trailing _Unwind_Exception member. + /// The Itanium ABI defines the layout of this struct. + virtual unsigned getSizeOfCxaException() const; + /// Determines the size of struct _Unwind_Exception on this platform, /// in 8-bit units. The Itanium ABI defines this as: /// struct _Unwind_Exception { Index: lib/CodeGen/TargetInfo.cpp =================================================================== --- lib/CodeGen/TargetInfo.cpp +++ lib/CodeGen/TargetInfo.cpp @@ -369,6 +369,16 @@ TargetCodeGenInfo::~TargetCodeGenInfo() { delete Info; } +unsigned TargetCodeGenInfo::getSizeOfCxaException() const { + // __cxa_exception comprises 5 pointers followed by 2 ints followed by 4 + // more pointers, then the _Unwind_Exception object. We assume that no + // padding is introduced between these fields. + ASTContext &Ctx = Info->getContext(); + return (9 * Ctx.getTypeSizeInChars(Ctx.VoidPtrTy) + + 2 * Ctx.getTypeSizeInChars(Ctx.IntTy)) + .getQuantity(); +} + // If someone can figure out a general rule for this, that would be great. // It's probably just doomed to be platform-dependent, though. unsigned TargetCodeGenInfo::getSizeOfUnwindException() const {