diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/CodeGen/CGObjCMac.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/CodeGen/CGObjCMac.cpp | 833 |
1 files changed, 437 insertions, 396 deletions
diff --git a/contrib/llvm/tools/clang/lib/CodeGen/CGObjCMac.cpp b/contrib/llvm/tools/clang/lib/CodeGen/CGObjCMac.cpp index d3bafd7..01ead9e 100644 --- a/contrib/llvm/tools/clang/lib/CodeGen/CGObjCMac.cpp +++ b/contrib/llvm/tools/clang/lib/CodeGen/CGObjCMac.cpp @@ -16,13 +16,14 @@ #include "CGRecordLayout.h" #include "CodeGenModule.h" #include "CodeGenFunction.h" +#include "CGException.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtObjC.h" #include "clang/Basic/LangOptions.h" -#include "clang/CodeGen/CodeGenOptions.h" +#include "clang/Frontend/CodeGenOptions.h" #include "llvm/Intrinsics.h" #include "llvm/LLVMContext.h" @@ -31,6 +32,7 @@ #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/CallSite.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetData.h" #include <cstdio> @@ -440,6 +442,15 @@ public: return CGM.CreateRuntimeFunction(FTy, "objc_exception_throw"); } + /// ExceptionRethrowFn - LLVM objc_exception_rethrow function. + llvm::Constant *getExceptionRethrowFn() { + // void objc_exception_rethrow(void) + std::vector<const llvm::Type*> Args; + llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Args, true); + return CGM.CreateRuntimeFunction(FTy, "objc_exception_rethrow"); + } + /// SyncEnterFn - LLVM object_sync_enter function. llvm::Constant *getSyncEnterFn() { // void objc_sync_enter (id) @@ -843,6 +854,9 @@ protected: /// MethodVarNames - uniqued method variable names. llvm::DenseMap<Selector, llvm::GlobalVariable*> MethodVarNames; + /// DefinedCategoryNames - list of category names in form Class_Category. + llvm::SetVector<std::string> DefinedCategoryNames; + /// MethodVarTypes - uniqued method type signatures. We have to use /// a StringMap here because have no other unique reference. llvm::StringMap<llvm::GlobalVariable*> MethodVarTypes; @@ -1120,7 +1134,8 @@ private: /// EmitSelector - Return a Value*, of type ObjCTypes.SelectorPtrTy, /// for the given selector. - llvm::Value *EmitSelector(CGBuilderTy &Builder, Selector Sel); + llvm::Value *EmitSelector(CGBuilderTy &Builder, Selector Sel, + bool lval=false); public: CGObjCMac(CodeGen::CodeGenModule &cgm); @@ -1151,7 +1166,8 @@ public: virtual llvm::Value *GetClass(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID); - virtual llvm::Value *GetSelector(CGBuilderTy &Builder, Selector Sel); + virtual llvm::Value *GetSelector(CGBuilderTy &Builder, Selector Sel, + bool lval = false); /// The NeXT/Apple runtimes do not support typed selectors; just emit an /// untyped one. @@ -1170,8 +1186,11 @@ public: virtual llvm::Constant *GetCopyStructFunction(); virtual llvm::Constant *EnumerationMutationFunction(); - virtual void EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, - const Stmt &S); + virtual void EmitTryStmt(CodeGen::CodeGenFunction &CGF, + const ObjCAtTryStmt &S); + virtual void EmitSynchronizedStmt(CodeGen::CodeGenFunction &CGF, + const ObjCAtSynchronizedStmt &S); + void EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, const Stmt &S); virtual void EmitThrowStmt(CodeGen::CodeGenFunction &CGF, const ObjCAtThrowStmt &S); virtual llvm::Value * EmitObjCWeakRead(CodeGen::CodeGenFunction &CGF, @@ -1187,7 +1206,7 @@ public: llvm::Value *src, llvm::Value *dest); virtual void EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF, llvm::Value *dest, llvm::Value *src, - QualType Ty); + llvm::Value *size); virtual LValue EmitObjCValueForIvar(CodeGen::CodeGenFunction &CGF, QualType ObjectTy, @@ -1319,7 +1338,8 @@ private: /// EmitSelector - Return a Value*, of type ObjCTypes.SelectorPtrTy, /// for the given selector. - llvm::Value *EmitSelector(CGBuilderTy &Builder, Selector Sel); + llvm::Value *EmitSelector(CGBuilderTy &Builder, Selector Sel, + bool lval=false); /// GetInterfaceEHType - Get the cached ehtype for the given Objective-C /// interface. The return value has type EHTypePtrTy. @@ -1382,8 +1402,9 @@ public: virtual llvm::Value *GetClass(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID); - virtual llvm::Value *GetSelector(CGBuilderTy &Builder, Selector Sel) - { return EmitSelector(Builder, Sel); } + virtual llvm::Value *GetSelector(CGBuilderTy &Builder, Selector Sel, + bool lvalue = false) + { return EmitSelector(Builder, Sel, lvalue); } /// The NeXT/Apple runtimes do not support typed selectors; just emit an /// untyped one. @@ -1412,8 +1433,10 @@ public: return ObjCTypes.getEnumerationMutationFn(); } - virtual void EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, - const Stmt &S); + virtual void EmitTryStmt(CodeGen::CodeGenFunction &CGF, + const ObjCAtTryStmt &S); + virtual void EmitSynchronizedStmt(CodeGen::CodeGenFunction &CGF, + const ObjCAtSynchronizedStmt &S); virtual void EmitThrowStmt(CodeGen::CodeGenFunction &CGF, const ObjCAtThrowStmt &S); virtual llvm::Value * EmitObjCWeakRead(CodeGen::CodeGenFunction &CGF, @@ -1429,7 +1452,7 @@ public: llvm::Value *src, llvm::Value *dest); virtual void EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF, llvm::Value *dest, llvm::Value *src, - QualType Ty); + llvm::Value *size); virtual LValue EmitObjCValueForIvar(CodeGen::CodeGenFunction &CGF, QualType ObjectTy, llvm::Value *BaseValue, @@ -1483,8 +1506,9 @@ llvm::Value *CGObjCMac::GetClass(CGBuilderTy &Builder, } /// GetSelector - Return the pointer to the unique'd string for this selector. -llvm::Value *CGObjCMac::GetSelector(CGBuilderTy &Builder, Selector Sel) { - return EmitSelector(Builder, Sel); +llvm::Value *CGObjCMac::GetSelector(CGBuilderTy &Builder, Selector Sel, + bool lval) { + return EmitSelector(Builder, Sel, lval); } llvm::Value *CGObjCMac::GetSelector(CGBuilderTy &Builder, const ObjCMethodDecl *Method) { @@ -1620,30 +1644,23 @@ CGObjCCommonMac::EmitLegacyMessageSend(CodeGen::CodeGenFunction &CGF, const llvm::FunctionType *FTy = Types.GetFunctionType(FnInfo, Method ? Method->isVariadic() : false); + if (Method) + assert(CGM.getContext().getCanonicalType(Method->getResultType()) == + CGM.getContext().getCanonicalType(ResultType) && + "Result type mismatch!"); + llvm::Constant *Fn = NULL; - if (CGM.ReturnTypeUsesSret(FnInfo)) { + if (CGM.ReturnTypeUsesSRet(FnInfo)) { Fn = (ObjCABI == 2) ? ObjCTypes.getSendStretFn2(IsSuper) : ObjCTypes.getSendStretFn(IsSuper); - } else if (ResultType->isFloatingType()) { - if (ObjCABI == 2) { - if (const BuiltinType *BT = ResultType->getAs<BuiltinType>()) { - BuiltinType::Kind k = BT->getKind(); - Fn = (k == BuiltinType::LongDouble) ? ObjCTypes.getSendFpretFn2(IsSuper) - : ObjCTypes.getSendFn2(IsSuper); - } else { - Fn = ObjCTypes.getSendFn2(IsSuper); - } - } else - // FIXME. This currently matches gcc's API for x86-32. May need to change - // for others if we have their API. - Fn = ObjCTypes.getSendFpretFn(IsSuper); + } else if (CGM.ReturnTypeUsesFPRet(ResultType)) { + Fn = (ObjCABI == 2) ? ObjCTypes.getSendFpretFn2(IsSuper) + : ObjCTypes.getSendFpretFn(IsSuper); } else { Fn = (ObjCABI == 2) ? ObjCTypes.getSendFn2(IsSuper) : ObjCTypes.getSendFn(IsSuper); } - assert(Fn && "EmitLegacyMessageSend - unknown API"); - Fn = llvm::ConstantExpr::getBitCast(Fn, - llvm::PointerType::getUnqual(FTy)); + Fn = llvm::ConstantExpr::getBitCast(Fn, llvm::PointerType::getUnqual(FTy)); return CGF.EmitCall(FnInfo, Fn, Return, ActualArgs); } @@ -1909,10 +1926,18 @@ llvm::Constant *CGObjCCommonMac::EmitPropertyList(llvm::Twine Name, Properties.push_back(llvm::ConstantStruct::get(ObjCTypes.PropertyTy, Prop)); } - if (const ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(OCD)) + if (const ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(OCD)) { for (ObjCInterfaceDecl::protocol_iterator P = OID->protocol_begin(), E = OID->protocol_end(); P != E; ++P) - PushProtocolProperties(PropertySet, Properties, Container, (*P), ObjCTypes); + PushProtocolProperties(PropertySet, Properties, Container, (*P), + ObjCTypes); + } + else if (const ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(OCD)) { + for (ObjCCategoryDecl::protocol_iterator P = CD->protocol_begin(), + E = CD->protocol_end(); P != E; ++P) + PushProtocolProperties(PropertySet, Properties, Container, (*P), + ObjCTypes); + } // Return null for empty list. if (Properties.empty()) @@ -2049,6 +2074,7 @@ void CGObjCMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { "__OBJC,__category,regular,no_dead_strip", 4, true); DefinedCategories.push_back(GV); + DefinedCategoryNames.insert(ExtName.str()); } // FIXME: Get from somewhere? @@ -2494,11 +2520,52 @@ llvm::Constant *CGObjCMac::EnumerationMutationFunction() { return ObjCTypes.getEnumerationMutationFn(); } +void CGObjCMac::EmitTryStmt(CodeGenFunction &CGF, const ObjCAtTryStmt &S) { + return EmitTryOrSynchronizedStmt(CGF, S); +} + +void CGObjCMac::EmitSynchronizedStmt(CodeGenFunction &CGF, + const ObjCAtSynchronizedStmt &S) { + return EmitTryOrSynchronizedStmt(CGF, S); +} + /* Objective-C setjmp-longjmp (sjlj) Exception Handling -- + A catch buffer is a setjmp buffer plus: + - a pointer to the exception that was caught + - a pointer to the previous exception data buffer + - two pointers of reserved storage + Therefore catch buffers form a stack, with a pointer to the top + of the stack kept in thread-local storage. + + objc_exception_try_enter pushes a catch buffer onto the EH stack. + objc_exception_try_exit pops the given catch buffer, which is + required to be the top of the EH stack. + objc_exception_throw pops the top of the EH stack, writes the + thrown exception into the appropriate field, and longjmps + to the setjmp buffer. It crashes the process (with a printf + and an abort()) if there are no catch buffers on the stack. + objc_exception_extract just reads the exception pointer out of the + catch buffer. + + There's no reason an implementation couldn't use a light-weight + setjmp here --- something like __builtin_setjmp, but API-compatible + with the heavyweight setjmp. This will be more important if we ever + want to implement correct ObjC/C++ exception interactions for the + fragile ABI. + + Note that for this use of setjmp/longjmp to be correct, we may need + to mark some local variables volatile: if a non-volatile local + variable is modified between the setjmp and the longjmp, it has + indeterminate value. For the purposes of LLVM IR, it may be + sufficient to make loads and stores within the @try (to variables + declared outside the @try) volatile. This is necessary for + optimized correctness, but is not currently being done; this is + being tracked as rdar://problem/8160285 + The basic framework for a @try-catch-finally is as follows: { objc_exception_data d; @@ -2560,37 +2627,33 @@ llvm::Constant *CGObjCMac::EnumerationMutationFunction() { Rethrows and Jumps-Through-Finally -- - Support for implicit rethrows and jumping through the finally block is - handled by storing the current exception-handling context in - ObjCEHStack. - - In order to implement proper @finally semantics, we support one basic - mechanism for jumping through the finally block to an arbitrary - destination. Constructs which generate exits from a @try or @catch - block use this mechanism to implement the proper semantics by chaining - jumps, as necessary. - - This mechanism works like the one used for indirect goto: we - arbitrarily assign an ID to each destination and store the ID for the - destination in a variable prior to entering the finally block. At the - end of the finally block we simply create a switch to the proper - destination. - - Code gen for @synchronized(expr) stmt; - Effectively generating code for: - objc_sync_enter(expr); - @try stmt @finally { objc_sync_exit(expr); } + '@throw;' is supported by pushing the currently-caught exception + onto ObjCEHStack while the @catch blocks are emitted. + + Branches through the @finally block are handled with an ordinary + normal cleanup. We do not register an EH cleanup; fragile-ABI ObjC + exceptions are not compatible with C++ exceptions, and this is + hardly the only place where this will go wrong. + + @synchronized(expr) { stmt; } is emitted as if it were: + id synch_value = expr; + objc_sync_enter(synch_value); + @try { stmt; } @finally { objc_sync_exit(synch_value); } */ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, const Stmt &S) { bool isTry = isa<ObjCAtTryStmt>(S); - // Create various blocks we refer to for handling @finally. - llvm::BasicBlock *FinallyBlock = CGF.createBasicBlock("finally"); - llvm::BasicBlock *FinallyExit = CGF.createBasicBlock("finally.exit"); - llvm::BasicBlock *FinallyNoExit = CGF.createBasicBlock("finally.noexit"); - llvm::BasicBlock *FinallyRethrow = CGF.createBasicBlock("finally.throw"); - llvm::BasicBlock *FinallyEnd = CGF.createBasicBlock("finally.end"); + + // A destination for the fall-through edges of the catch handlers to + // jump to. + CodeGenFunction::JumpDest FinallyEnd = + CGF.getJumpDestInCurrentScope("finally.end"); + + // A destination for the rethrow edge of the catch handlers to jump + // to. + CodeGenFunction::JumpDest FinallyRethrow = + CGF.getJumpDestInCurrentScope("finally.rethrow"); // For @synchronized, call objc_sync_enter(sync.expr). The // evaluation of the expression must occur before we enter the @@ -2601,75 +2664,139 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, SyncArg = CGF.EmitScalarExpr(cast<ObjCAtSynchronizedStmt>(S).getSynchExpr()); SyncArg = CGF.Builder.CreateBitCast(SyncArg, ObjCTypes.ObjectPtrTy); - CGF.Builder.CreateCall(ObjCTypes.getSyncEnterFn(), SyncArg); + CGF.Builder.CreateCall(ObjCTypes.getSyncEnterFn(), SyncArg) + ->setDoesNotThrow(); } - // Push an EH context entry, used for handling rethrows and jumps - // through finally. - CGF.PushCleanupBlock(FinallyBlock); - - if (CGF.ObjCEHValueStack.empty()) - CGF.ObjCEHValueStack.push_back(0); - // If This is a nested @try, caught exception is that of enclosing @try. - else - CGF.ObjCEHValueStack.push_back(CGF.ObjCEHValueStack.back()); // Allocate memory for the exception data and rethrow pointer. llvm::Value *ExceptionData = CGF.CreateTempAlloca(ObjCTypes.ExceptionDataTy, "exceptiondata.ptr"); llvm::Value *RethrowPtr = CGF.CreateTempAlloca(ObjCTypes.ObjectPtrTy, "_rethrow"); - llvm::Value *CallTryExitPtr = CGF.CreateTempAlloca( - llvm::Type::getInt1Ty(VMContext), + + // Create a flag indicating whether the cleanup needs to call + // objc_exception_try_exit. This is true except when + // - no catches match and we're branching through the cleanup + // just to rethrow the exception, or + // - a catch matched and we're falling out of the catch handler. + llvm::Value *CallTryExitVar = CGF.CreateTempAlloca(CGF.Builder.getInt1Ty(), "_call_try_exit"); CGF.Builder.CreateStore(llvm::ConstantInt::getTrue(VMContext), - CallTryExitPtr); + CallTryExitVar); + + // Push a normal cleanup to leave the try scope. + { + CodeGenFunction::CleanupBlock FinallyScope(CGF, NormalCleanup); + + // Check whether we need to call objc_exception_try_exit. + // In optimized code, this branch will always be folded. + llvm::BasicBlock *FinallyCallExit = + CGF.createBasicBlock("finally.call_exit"); + llvm::BasicBlock *FinallyNoCallExit = + CGF.createBasicBlock("finally.no_call_exit"); + CGF.Builder.CreateCondBr(CGF.Builder.CreateLoad(CallTryExitVar), + FinallyCallExit, FinallyNoCallExit); + + CGF.EmitBlock(FinallyCallExit); + CGF.Builder.CreateCall(ObjCTypes.getExceptionTryExitFn(), ExceptionData) + ->setDoesNotThrow(); + + CGF.EmitBlock(FinallyNoCallExit); + + if (isTry) { + if (const ObjCAtFinallyStmt* FinallyStmt = + cast<ObjCAtTryStmt>(S).getFinallyStmt()) + CGF.EmitStmt(FinallyStmt->getFinallyBody()); + + // ~CleanupBlock requires there to be an exit block. + CGF.EnsureInsertPoint(); + } else { + // Emit objc_sync_exit(expr); as finally's sole statement for + // @synchronized. + CGF.Builder.CreateCall(ObjCTypes.getSyncExitFn(), SyncArg) + ->setDoesNotThrow(); + } + } - // Enter a new try block and call setjmp. - CGF.Builder.CreateCall(ObjCTypes.getExceptionTryEnterFn(), ExceptionData); - llvm::Value *JmpBufPtr = CGF.Builder.CreateStructGEP(ExceptionData, 0, - "jmpbufarray"); - JmpBufPtr = CGF.Builder.CreateStructGEP(JmpBufPtr, 0, "tmp"); - llvm::Value *SetJmpResult = CGF.Builder.CreateCall(ObjCTypes.getSetJmpFn(), - JmpBufPtr, "result"); + // Enter a try block: + // - Call objc_exception_try_enter to push ExceptionData on top of + // the EH stack. + CGF.Builder.CreateCall(ObjCTypes.getExceptionTryEnterFn(), ExceptionData) + ->setDoesNotThrow(); + // - Call setjmp on the exception data buffer. + llvm::Constant *Zero = llvm::ConstantInt::get(CGF.Builder.getInt32Ty(), 0); + llvm::Value *GEPIndexes[] = { Zero, Zero, Zero }; + llvm::Value *SetJmpBuffer = + CGF.Builder.CreateGEP(ExceptionData, GEPIndexes, GEPIndexes+3, "setjmp_buffer"); + llvm::CallInst *SetJmpResult = + CGF.Builder.CreateCall(ObjCTypes.getSetJmpFn(), SetJmpBuffer, "setjmp_result"); + SetJmpResult->setDoesNotThrow(); + + // If setjmp returned 0, enter the protected block; otherwise, + // branch to the handler. llvm::BasicBlock *TryBlock = CGF.createBasicBlock("try"); llvm::BasicBlock *TryHandler = CGF.createBasicBlock("try.handler"); - CGF.Builder.CreateCondBr(CGF.Builder.CreateIsNotNull(SetJmpResult, "threw"), - TryHandler, TryBlock); + llvm::Value *DidCatch = + CGF.Builder.CreateIsNull(SetJmpResult, "did_catch_exception"); + CGF.Builder.CreateCondBr(DidCatch, TryBlock, TryHandler); - // Emit the @try block. + // Emit the protected block. CGF.EmitBlock(TryBlock); CGF.EmitStmt(isTry ? cast<ObjCAtTryStmt>(S).getTryBody() - : cast<ObjCAtSynchronizedStmt>(S).getSynchBody()); + : cast<ObjCAtSynchronizedStmt>(S).getSynchBody()); CGF.EmitBranchThroughCleanup(FinallyEnd); - // Emit the "exception in @try" block. + // Emit the exception handler block. CGF.EmitBlock(TryHandler); // Retrieve the exception object. We may emit multiple blocks but // nothing can cross this so the value is already in SSA form. - llvm::Value *Caught = + llvm::CallInst *Caught = CGF.Builder.CreateCall(ObjCTypes.getExceptionExtractFn(), ExceptionData, "caught"); - CGF.ObjCEHValueStack.back() = Caught; - if (!isTry) { - CGF.Builder.CreateStore(Caught, RethrowPtr); + Caught->setDoesNotThrow(); + + // Remember the exception to rethrow. + CGF.Builder.CreateStore(Caught, RethrowPtr); + + // Note: at this point, objc_exception_throw already popped the + // catch handler, so anything that branches to the cleanup needs + // to set CallTryExitVar to false. + + // For a @synchronized (or a @try with no catches), just branch + // through the cleanup to the rethrow block. + if (!isTry || !cast<ObjCAtTryStmt>(S).getNumCatchStmts()) { + // Tell the cleanup not to re-pop the exit. CGF.Builder.CreateStore(llvm::ConstantInt::getFalse(VMContext), - CallTryExitPtr); + CallTryExitVar); + CGF.EmitBranchThroughCleanup(FinallyRethrow); - } else if (cast<ObjCAtTryStmt>(S).getNumCatchStmts()) { + + // Otherwise, we have to match against the caught exceptions. + } else { + // Push the exception to rethrow onto the EH value stack for the + // benefit of any @throws in the handlers. + CGF.ObjCEHValueStack.push_back(Caught); + const ObjCAtTryStmt* AtTryStmt = cast<ObjCAtTryStmt>(&S); // Enter a new exception try block (in case a @catch block throws - // an exception). - CGF.Builder.CreateCall(ObjCTypes.getExceptionTryEnterFn(), ExceptionData); + // an exception). Now CallTryExitVar (currently true) is back in + // synch with reality. + CGF.Builder.CreateCall(ObjCTypes.getExceptionTryEnterFn(), ExceptionData) + ->setDoesNotThrow(); - llvm::Value *SetJmpResult = CGF.Builder.CreateCall(ObjCTypes.getSetJmpFn(), - JmpBufPtr, "result"); - llvm::Value *Threw = CGF.Builder.CreateIsNotNull(SetJmpResult, "threw"); + llvm::CallInst *SetJmpResult = + CGF.Builder.CreateCall(ObjCTypes.getSetJmpFn(), SetJmpBuffer, + "setjmp.result"); + SetJmpResult->setDoesNotThrow(); + + llvm::Value *Threw = + CGF.Builder.CreateIsNotNull(SetJmpResult, "did_catch_exception"); llvm::BasicBlock *CatchBlock = CGF.createBasicBlock("catch"); - llvm::BasicBlock *CatchHandler = CGF.createBasicBlock("catch.handler"); + llvm::BasicBlock *CatchHandler = CGF.createBasicBlock("catch_for_catch"); CGF.Builder.CreateCondBr(Threw, CatchHandler, CatchBlock); CGF.EmitBlock(CatchBlock); @@ -2680,7 +2807,6 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, bool AllMatched = false; for (unsigned I = 0, N = AtTryStmt->getNumCatchStmts(); I != N; ++I) { const ObjCAtCatchStmt *CatchStmt = AtTryStmt->getCatchStmt(I); - llvm::BasicBlock *NextCatchBlock = CGF.createBasicBlock("catch"); const VarDecl *CatchParam = CatchStmt->getCatchParamDecl(); const ObjCObjectPointerType *OPT = 0; @@ -2691,47 +2817,67 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, } else { OPT = CatchParam->getType()->getAs<ObjCObjectPointerType>(); - // catch(id e) always matches. + // catch(id e) always matches under this ABI, since only + // ObjC exceptions end up here in the first place. // FIXME: For the time being we also match id<X>; this should // be rejected by Sema instead. if (OPT && (OPT->isObjCIdType() || OPT->isObjCQualifiedIdType())) AllMatched = true; } + // If this is a catch-all, we don't need to test anything. if (AllMatched) { + CodeGenFunction::RunCleanupsScope CatchVarCleanups(CGF); + if (CatchParam) { CGF.EmitLocalBlockVarDecl(*CatchParam); assert(CGF.HaveInsertPoint() && "DeclStmt destroyed insert point?"); + + // These types work out because ConvertType(id) == i8*. CGF.Builder.CreateStore(Caught, CGF.GetAddrOfLocalVar(CatchParam)); } CGF.EmitStmt(CatchStmt->getCatchBody()); + + // The scope of the catch variable ends right here. + CatchVarCleanups.ForceCleanup(); + CGF.EmitBranchThroughCleanup(FinallyEnd); break; } assert(OPT && "Unexpected non-object pointer type in @catch"); const ObjCObjectType *ObjTy = OPT->getObjectType(); + + // FIXME: @catch (Class c) ? ObjCInterfaceDecl *IDecl = ObjTy->getInterface(); assert(IDecl && "Catch parameter must have Objective-C type!"); // Check if the @catch block matches the exception object. llvm::Value *Class = EmitClassRef(CGF.Builder, IDecl); - llvm::Value *Match = + llvm::CallInst *Match = CGF.Builder.CreateCall2(ObjCTypes.getExceptionMatchFn(), Class, Caught, "match"); + Match->setDoesNotThrow(); - llvm::BasicBlock *MatchedBlock = CGF.createBasicBlock("matched"); + llvm::BasicBlock *MatchedBlock = CGF.createBasicBlock("match"); + llvm::BasicBlock *NextCatchBlock = CGF.createBasicBlock("catch.next"); CGF.Builder.CreateCondBr(CGF.Builder.CreateIsNotNull(Match, "matched"), MatchedBlock, NextCatchBlock); // Emit the @catch block. CGF.EmitBlock(MatchedBlock); + + // Collect any cleanups for the catch variable. The scope lasts until + // the end of the catch body. + CodeGenFunction::RunCleanupsScope CatchVarCleanups(CGF); + CGF.EmitLocalBlockVarDecl(*CatchParam); assert(CGF.HaveInsertPoint() && "DeclStmt destroyed insert point?"); + // Initialize the catch variable. llvm::Value *Tmp = CGF.Builder.CreateBitCast(Caught, CGF.ConvertType(CatchParam->getType()), @@ -2739,11 +2885,17 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, CGF.Builder.CreateStore(Tmp, CGF.GetAddrOfLocalVar(CatchParam)); CGF.EmitStmt(CatchStmt->getCatchBody()); + + // We're done with the catch variable. + CatchVarCleanups.ForceCleanup(); + CGF.EmitBranchThroughCleanup(FinallyEnd); CGF.EmitBlock(NextCatchBlock); } + CGF.ObjCEHValueStack.pop_back(); + if (!AllMatched) { // None of the handlers caught the exception, so store it to be // rethrown at the end of the @finally block. @@ -2753,59 +2905,34 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, // Emit the exception handler for the @catch blocks. CGF.EmitBlock(CatchHandler); - CGF.Builder.CreateStore( - CGF.Builder.CreateCall(ObjCTypes.getExceptionExtractFn(), - ExceptionData), - RethrowPtr); - CGF.Builder.CreateStore(llvm::ConstantInt::getFalse(VMContext), - CallTryExitPtr); - CGF.EmitBranchThroughCleanup(FinallyRethrow); - } else { + + // Rethrow the new exception, not the old one. + Caught = CGF.Builder.CreateCall(ObjCTypes.getExceptionExtractFn(), + ExceptionData); + Caught->setDoesNotThrow(); CGF.Builder.CreateStore(Caught, RethrowPtr); + + // Don't pop the catch handler; the throw already did. CGF.Builder.CreateStore(llvm::ConstantInt::getFalse(VMContext), - CallTryExitPtr); + CallTryExitVar); CGF.EmitBranchThroughCleanup(FinallyRethrow); } - // Pop the exception-handling stack entry. It is important to do - // this now, because the code in the @finally block is not in this - // context. - CodeGenFunction::CleanupBlockInfo Info = CGF.PopCleanupBlock(); - - CGF.ObjCEHValueStack.pop_back(); - - // Emit the @finally block. - CGF.EmitBlock(FinallyBlock); - llvm::Value* CallTryExit = CGF.Builder.CreateLoad(CallTryExitPtr, "tmp"); + // Pop the cleanup. + CGF.PopCleanupBlock(); + CGF.EmitBlock(FinallyEnd.Block); - CGF.Builder.CreateCondBr(CallTryExit, FinallyExit, FinallyNoExit); - - CGF.EmitBlock(FinallyExit); - CGF.Builder.CreateCall(ObjCTypes.getExceptionTryExitFn(), ExceptionData); - - CGF.EmitBlock(FinallyNoExit); - if (isTry) { - if (const ObjCAtFinallyStmt* FinallyStmt = - cast<ObjCAtTryStmt>(S).getFinallyStmt()) - CGF.EmitStmt(FinallyStmt->getFinallyBody()); - } else { - // Emit objc_sync_exit(expr); as finally's sole statement for - // @synchronized. - CGF.Builder.CreateCall(ObjCTypes.getSyncExitFn(), SyncArg); + // Emit the rethrow block. + CGF.Builder.ClearInsertionPoint(); + CGF.EmitBlock(FinallyRethrow.Block, true); + if (CGF.HaveInsertPoint()) { + CGF.Builder.CreateCall(ObjCTypes.getExceptionThrowFn(), + CGF.Builder.CreateLoad(RethrowPtr)) + ->setDoesNotThrow(); + CGF.Builder.CreateUnreachable(); } - // Emit the switch block - if (Info.SwitchBlock) - CGF.EmitBlock(Info.SwitchBlock); - if (Info.EndBlock) - CGF.EmitBlock(Info.EndBlock); - - CGF.EmitBlock(FinallyRethrow); - CGF.Builder.CreateCall(ObjCTypes.getExceptionThrowFn(), - CGF.Builder.CreateLoad(RethrowPtr)); - CGF.Builder.CreateUnreachable(); - - CGF.EmitBlock(FinallyEnd); + CGF.Builder.SetInsertPoint(FinallyEnd.Block); } void CGObjCMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF, @@ -2822,7 +2949,8 @@ void CGObjCMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF, ExceptionAsObject = CGF.ObjCEHValueStack.back(); } - CGF.Builder.CreateCall(ObjCTypes.getExceptionThrowFn(), ExceptionAsObject); + CGF.Builder.CreateCall(ObjCTypes.getExceptionThrowFn(), ExceptionAsObject) + ->setDoesNotReturn(); CGF.Builder.CreateUnreachable(); // Clear the insertion point to indicate we are in unreachable code. @@ -2929,15 +3057,11 @@ void CGObjCMac::EmitObjCStrongCastAssign(CodeGen::CodeGenFunction &CGF, void CGObjCMac::EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF, llvm::Value *DestPtr, llvm::Value *SrcPtr, - QualType Ty) { - // Get size info for this aggregate. - std::pair<uint64_t, unsigned> TypeInfo = CGM.getContext().getTypeInfo(Ty); - unsigned long size = TypeInfo.first/8; + llvm::Value *size) { SrcPtr = CGF.Builder.CreateBitCast(SrcPtr, ObjCTypes.Int8PtrTy); DestPtr = CGF.Builder.CreateBitCast(DestPtr, ObjCTypes.Int8PtrTy); - llvm::Value *N = llvm::ConstantInt::get(ObjCTypes.LongTy, size); CGF.Builder.CreateCall3(ObjCTypes.GcMemmoveCollectableFn(), - DestPtr, SrcPtr, N); + DestPtr, SrcPtr, size); return; } @@ -2997,12 +3121,14 @@ void CGObjCCommonMac::EmitImageInfo() { // We never allow @synthesize of a superclass property. flags |= eImageInfo_CorrectedSynthesize; + const llvm::Type *Int32Ty = llvm::Type::getInt32Ty(VMContext); + // Emitted as int[2]; llvm::Constant *values[2] = { - llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), version), - llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), flags) + llvm::ConstantInt::get(Int32Ty, version), + llvm::ConstantInt::get(Int32Ty, flags) }; - llvm::ArrayType *AT = llvm::ArrayType::get(llvm::Type::getInt32Ty(VMContext), 2); + llvm::ArrayType *AT = llvm::ArrayType::get(Int32Ty, 2); const char *Section; if (ObjCABI == 1) @@ -3102,7 +3228,8 @@ llvm::Value *CGObjCMac::EmitClassRef(CGBuilderTy &Builder, return Builder.CreateLoad(Entry, "tmp"); } -llvm::Value *CGObjCMac::EmitSelector(CGBuilderTy &Builder, Selector Sel) { +llvm::Value *CGObjCMac::EmitSelector(CGBuilderTy &Builder, Selector Sel, + bool lvalue) { llvm::GlobalVariable *&Entry = SelectorReferences[Sel]; if (!Entry) { @@ -3115,6 +3242,8 @@ llvm::Value *CGObjCMac::EmitSelector(CGBuilderTy &Builder, Selector Sel) { 4, true); } + if (lvalue) + return Entry; return Builder.CreateLoad(Entry, "tmp"); } @@ -3632,8 +3761,14 @@ void CGObjCMac::FinishModule() { OS << "\t.objc_class_name_" << (*I)->getName() << "=0\n" << "\t.globl .objc_class_name_" << (*I)->getName() << "\n"; for (llvm::SetVector<IdentifierInfo*>::iterator I = LazySymbols.begin(), - e = LazySymbols.end(); I != e; ++I) + e = LazySymbols.end(); I != e; ++I) { OS << "\t.lazy_reference .objc_class_name_" << (*I)->getName() << "\n"; + } + + for (size_t i = 0; i < DefinedCategoryNames.size(); ++i) { + OS << "\t.objc_category_name_" << DefinedCategoryNames[i] << "=0\n" + << "\t.globl .objc_category_name_" << DefinedCategoryNames[i] << "\n"; + } CGM.getModule().setModuleInlineAsm(OS.str()); } @@ -3949,8 +4084,9 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) llvm::Type::getInt8PtrTy(VMContext), 4); ExceptionDataTy = - llvm::StructType::get(VMContext, llvm::ArrayType::get(llvm::Type::getInt32Ty(VMContext), - SetJmpBufferSize), + llvm::StructType::get(VMContext, + llvm::ArrayType::get(llvm::Type::getInt32Ty(VMContext), + SetJmpBufferSize), StackPtrTy, NULL); CGM.getModule().addTypeName("struct._objc_exception_data", ExceptionDataTy); @@ -5147,7 +5283,7 @@ CodeGen::RValue CGObjCNonFragileABIMac::EmitMessageSend( FunctionType::ExtInfo()); llvm::Constant *Fn = 0; std::string Name("\01l_"); - if (CGM.ReturnTypeUsesSret(FnInfo)) { + if (CGM.ReturnTypeUsesSRet(FnInfo)) { #if 0 // unlike what is documented. gcc never generates this API!! if (Receiver->getType() == ObjCTypes.ObjectPtrTy) { @@ -5164,14 +5300,9 @@ CodeGen::RValue CGObjCNonFragileABIMac::EmitMessageSend( Fn = ObjCTypes.getMessageSendStretFixupFn(); Name += "objc_msgSend_stret_fixup"; } - } else if (!IsSuper && ResultType->isFloatingType()) { - if (ResultType->isSpecificBuiltinType(BuiltinType::LongDouble)) { - Fn = ObjCTypes.getMessageSendFpretFixupFn(); - Name += "objc_msgSend_fpret_fixup"; - } else { - Fn = ObjCTypes.getMessageSendFixupFn(); - Name += "objc_msgSend_fixup"; - } + } else if (!IsSuper && CGM.ReturnTypeUsesFPRet(ResultType)) { + Fn = ObjCTypes.getMessageSendFpretFixupFn(); + Name += "objc_msgSend_fpret_fixup"; } else { #if 0 // unlike what is documented. gcc never generates this API!! @@ -5403,7 +5534,7 @@ CGObjCNonFragileABIMac::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, } llvm::Value *CGObjCNonFragileABIMac::EmitSelector(CGBuilderTy &Builder, - Selector Sel) { + Selector Sel, bool lval) { llvm::GlobalVariable *&Entry = SelectorReferences[Sel]; if (!Entry) { @@ -5418,6 +5549,8 @@ llvm::Value *CGObjCNonFragileABIMac::EmitSelector(CGBuilderTy &Builder, CGM.AddUsedGlobal(Entry); } + if (lval) + return Entry; return Builder.CreateLoad(Entry, "tmp"); } /// EmitObjCIvarAssign - Code gen for assigning to a __strong object. @@ -5467,15 +5600,11 @@ void CGObjCNonFragileABIMac::EmitGCMemmoveCollectable( CodeGen::CodeGenFunction &CGF, llvm::Value *DestPtr, llvm::Value *SrcPtr, - QualType Ty) { - // Get size info for this aggregate. - std::pair<uint64_t, unsigned> TypeInfo = CGM.getContext().getTypeInfo(Ty); - unsigned long size = TypeInfo.first/8; + llvm::Value *Size) { SrcPtr = CGF.Builder.CreateBitCast(SrcPtr, ObjCTypes.Int8PtrTy); DestPtr = CGF.Builder.CreateBitCast(DestPtr, ObjCTypes.Int8PtrTy); - llvm::Value *N = llvm::ConstantInt::get(ObjCTypes.LongTy, size); CGF.Builder.CreateCall3(ObjCTypes.GcMemmoveCollectableFn(), - DestPtr, SrcPtr, N); + DestPtr, SrcPtr, Size); return; } @@ -5535,75 +5664,92 @@ void CGObjCNonFragileABIMac::EmitObjCGlobalAssign(CodeGen::CodeGenFunction &CGF, } void -CGObjCNonFragileABIMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, - const Stmt &S) { - bool isTry = isa<ObjCAtTryStmt>(S); - llvm::BasicBlock *TryBlock = CGF.createBasicBlock("try"); - llvm::BasicBlock *PrevLandingPad = CGF.getInvokeDest(); - llvm::BasicBlock *TryHandler = CGF.createBasicBlock("try.handler"); - llvm::BasicBlock *FinallyBlock = CGF.createBasicBlock("finally"); - llvm::BasicBlock *FinallyRethrow = CGF.createBasicBlock("finally.throw"); - llvm::BasicBlock *FinallyEnd = CGF.createBasicBlock("finally.end"); +CGObjCNonFragileABIMac::EmitSynchronizedStmt(CodeGen::CodeGenFunction &CGF, + const ObjCAtSynchronizedStmt &S) { + // Evaluate the lock operand. This should dominate the cleanup. + llvm::Value *SyncArg = CGF.EmitScalarExpr(S.getSynchExpr()); - // For @synchronized, call objc_sync_enter(sync.expr). The - // evaluation of the expression must occur before we enter the - // @synchronized. We can safely avoid a temp here because jumps into - // @synchronized are illegal & this will dominate uses. - llvm::Value *SyncArg = 0; - if (!isTry) { - SyncArg = - CGF.EmitScalarExpr(cast<ObjCAtSynchronizedStmt>(S).getSynchExpr()); - SyncArg = CGF.Builder.CreateBitCast(SyncArg, ObjCTypes.ObjectPtrTy); - CGF.Builder.CreateCall(ObjCTypes.getSyncEnterFn(), SyncArg); + // Acquire the lock. + SyncArg = CGF.Builder.CreateBitCast(SyncArg, ObjCTypes.ObjectPtrTy); + CGF.Builder.CreateCall(ObjCTypes.getSyncEnterFn(), SyncArg) + ->setDoesNotThrow(); + + // Register an all-paths cleanup to release the lock. + { + CodeGenFunction::CleanupBlock ReleaseScope(CGF, NormalAndEHCleanup); + + CGF.Builder.CreateCall(ObjCTypes.getSyncExitFn(), SyncArg) + ->setDoesNotThrow(); } - // Push an EH context entry, used for handling rethrows and jumps - // through finally. - CGF.PushCleanupBlock(FinallyBlock); + // Emit the body of the statement. + CGF.EmitStmt(S.getSynchBody()); - CGF.setInvokeDest(TryHandler); + // Pop the lock-release cleanup. + CGF.PopCleanupBlock(); +} - CGF.EmitBlock(TryBlock); - CGF.EmitStmt(isTry ? cast<ObjCAtTryStmt>(S).getTryBody() - : cast<ObjCAtSynchronizedStmt>(S).getSynchBody()); - CGF.EmitBranchThroughCleanup(FinallyEnd); +namespace { + struct CatchHandler { + const VarDecl *Variable; + const Stmt *Body; + llvm::BasicBlock *Block; + llvm::Value *TypeInfo; + }; - // Emit the exception handler. + struct CallObjCEndCatch : EHScopeStack::LazyCleanup { + CallObjCEndCatch(bool MightThrow, llvm::Value *Fn) : + MightThrow(MightThrow), Fn(Fn) {} + bool MightThrow; + llvm::Value *Fn; - CGF.EmitBlock(TryHandler); + void Emit(CodeGenFunction &CGF, bool IsForEH) { + if (!MightThrow) { + CGF.Builder.CreateCall(Fn)->setDoesNotThrow(); + return; + } + + CGF.EmitCallOrInvoke(Fn, 0, 0); + } + }; +} - llvm::Value *llvm_eh_exception = - CGF.CGM.getIntrinsic(llvm::Intrinsic::eh_exception); - llvm::Value *llvm_eh_selector = - CGF.CGM.getIntrinsic(llvm::Intrinsic::eh_selector); - llvm::Value *llvm_eh_typeid_for = - CGF.CGM.getIntrinsic(llvm::Intrinsic::eh_typeid_for); - llvm::Value *Exc = CGF.Builder.CreateCall(llvm_eh_exception, "exc"); - llvm::Value *RethrowPtr = CGF.CreateTempAlloca(Exc->getType(), "_rethrow"); - - llvm::SmallVector<llvm::Value*, 8> SelectorArgs; - SelectorArgs.push_back(Exc); - SelectorArgs.push_back(ObjCTypes.getEHPersonalityPtr()); - - // Construct the lists of (type, catch body) to handle. - llvm::SmallVector<std::pair<const VarDecl*, const Stmt*>, 8> Handlers; - bool HasCatchAll = false; - if (isTry) { - const ObjCAtTryStmt &AtTry = cast<ObjCAtTryStmt>(S); - for (unsigned I = 0, N = AtTry.getNumCatchStmts(); I != N; ++I) { - const ObjCAtCatchStmt *CatchStmt = AtTry.getCatchStmt(I); +void CGObjCNonFragileABIMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF, + const ObjCAtTryStmt &S) { + // Jump destination for falling out of catch bodies. + CodeGenFunction::JumpDest Cont; + if (S.getNumCatchStmts()) + Cont = CGF.getJumpDestInCurrentScope("eh.cont"); + + CodeGenFunction::FinallyInfo FinallyInfo; + if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) + FinallyInfo = CGF.EnterFinallyBlock(Finally->getFinallyBody(), + ObjCTypes.getObjCBeginCatchFn(), + ObjCTypes.getObjCEndCatchFn(), + ObjCTypes.getExceptionRethrowFn()); + + llvm::SmallVector<CatchHandler, 8> Handlers; + + // Enter the catch, if there is one. + if (S.getNumCatchStmts()) { + for (unsigned I = 0, N = S.getNumCatchStmts(); I != N; ++I) { + const ObjCAtCatchStmt *CatchStmt = S.getCatchStmt(I); const VarDecl *CatchDecl = CatchStmt->getCatchParamDecl(); - Handlers.push_back(std::make_pair(CatchDecl, CatchStmt->getCatchBody())); - // catch(...) always matches. + Handlers.push_back(CatchHandler()); + CatchHandler &Handler = Handlers.back(); + Handler.Variable = CatchDecl; + Handler.Body = CatchStmt->getCatchBody(); + Handler.Block = CGF.createBasicBlock("catch"); + + // @catch(...) always matches. if (!CatchDecl) { - // Use i8* null here to signal this is a catch all, not a cleanup. - llvm::Value *Null = llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy); - SelectorArgs.push_back(Null); - HasCatchAll = true; + Handler.TypeInfo = 0; // catch-all + // Don't consider any other catches. break; } + // There's a particular fixed type info for 'id'. if (CatchDecl->getType()->isObjCIdType() || CatchDecl->getType()->isObjCQualifiedIdType()) { llvm::Value *IDEHType = @@ -5614,7 +5760,7 @@ CGObjCNonFragileABIMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, false, llvm::GlobalValue::ExternalLinkage, 0, "OBJC_EHTYPE_id"); - SelectorArgs.push_back(IDEHType); + Handler.TypeInfo = IDEHType; } else { // All other types should be Objective-C interface pointer types. const ObjCObjectPointerType *PT = @@ -5622,207 +5768,101 @@ CGObjCNonFragileABIMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, assert(PT && "Invalid @catch type."); const ObjCInterfaceType *IT = PT->getInterfaceType(); assert(IT && "Invalid @catch type."); - llvm::Value *EHType = GetInterfaceEHType(IT->getDecl(), false); - SelectorArgs.push_back(EHType); + Handler.TypeInfo = GetInterfaceEHType(IT->getDecl(), false); } } - } - // We use a cleanup unless there was already a catch all. - if (!HasCatchAll) { - // Even though this is a cleanup, treat it as a catch all to avoid the C++ - // personality behavior of terminating the process if only cleanups are - // found in the exception handling stack. - SelectorArgs.push_back(llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy)); - Handlers.push_back(std::make_pair((const ParmVarDecl*) 0, (const Stmt*) 0)); + EHCatchScope *Catch = CGF.EHStack.pushCatch(Handlers.size()); + for (unsigned I = 0, E = Handlers.size(); I != E; ++I) + Catch->setHandler(I, Handlers[I].TypeInfo, Handlers[I].Block); } + + // Emit the try body. + CGF.EmitStmt(S.getTryBody()); - llvm::Value *Selector = - CGF.Builder.CreateCall(llvm_eh_selector, - SelectorArgs.begin(), SelectorArgs.end(), - "selector"); - for (unsigned i = 0, e = Handlers.size(); i != e; ++i) { - const VarDecl *CatchParam = Handlers[i].first; - const Stmt *CatchBody = Handlers[i].second; - - llvm::BasicBlock *Next = 0; - - // The last handler always matches. - if (i + 1 != e) { - assert(CatchParam && "Only last handler can be a catch all."); + // Leave the try. + if (S.getNumCatchStmts()) + CGF.EHStack.popCatch(); - llvm::BasicBlock *Match = CGF.createBasicBlock("match"); - Next = CGF.createBasicBlock("catch.next"); - llvm::Value *Id = - CGF.Builder.CreateCall(llvm_eh_typeid_for, - CGF.Builder.CreateBitCast(SelectorArgs[i+2], - ObjCTypes.Int8PtrTy)); - CGF.Builder.CreateCondBr(CGF.Builder.CreateICmpEQ(Selector, Id), - Match, Next); + // Remember where we were. + CGBuilderTy::InsertPoint SavedIP = CGF.Builder.saveAndClearIP(); - CGF.EmitBlock(Match); - } + // Emit the handlers. + for (unsigned I = 0, E = Handlers.size(); I != E; ++I) { + CatchHandler &Handler = Handlers[I]; - if (CatchBody) { - llvm::BasicBlock *MatchEnd = CGF.createBasicBlock("match.end"); - - // Cleanups must call objc_end_catch. - CGF.PushCleanupBlock(MatchEnd); - - llvm::Value *ExcObject = - CGF.Builder.CreateCall(ObjCTypes.getObjCBeginCatchFn(), Exc); - - // Bind the catch parameter if it exists. - if (CatchParam) { - ExcObject = - CGF.Builder.CreateBitCast(ExcObject, - CGF.ConvertType(CatchParam->getType())); - // CatchParam is a ParmVarDecl because of the grammar - // construction used to handle this, but for codegen purposes - // we treat this as a local decl. - CGF.EmitLocalBlockVarDecl(*CatchParam); - CGF.Builder.CreateStore(ExcObject, CGF.GetAddrOfLocalVar(CatchParam)); - } + CGF.EmitBlock(Handler.Block); + llvm::Value *RawExn = CGF.Builder.CreateLoad(CGF.getExceptionSlot()); - // Exceptions inside the catch block must be rethrown. We set a special - // purpose invoke destination for this which just collects the thrown - // exception and overwrites the object in RethrowPtr, branches through the - // match.end to make sure we call objc_end_catch, before branching to the - // rethrow handler. - llvm::BasicBlock *MatchHandler = CGF.createBasicBlock("match.handler"); - CGF.setInvokeDest(MatchHandler); - CGF.ObjCEHValueStack.push_back(ExcObject); - CGF.EmitStmt(CatchBody); - CGF.ObjCEHValueStack.pop_back(); - CGF.setInvokeDest(0); + // Enter the catch. + llvm::CallInst *Exn = + CGF.Builder.CreateCall(ObjCTypes.getObjCBeginCatchFn(), RawExn, + "exn.adjusted"); + Exn->setDoesNotThrow(); - CGF.EmitBranchThroughCleanup(FinallyEnd); + // Add a cleanup to leave the catch. + bool EndCatchMightThrow = (Handler.Variable == 0); + CGF.EHStack.pushLazyCleanup<CallObjCEndCatch>(NormalAndEHCleanup, + EndCatchMightThrow, + ObjCTypes.getObjCEndCatchFn()); - // Don't emit the extra match handler if there we no unprotected calls in - // the catch block. - if (MatchHandler->use_empty()) { - delete MatchHandler; - } else { - CGF.EmitBlock(MatchHandler); - llvm::Value *Exc = CGF.Builder.CreateCall(llvm_eh_exception, "exc"); - // We are required to emit this call to satisfy LLVM, even - // though we don't use the result. - CGF.Builder.CreateCall3(llvm_eh_selector, - Exc, ObjCTypes.getEHPersonalityPtr(), - llvm::ConstantInt::get( - llvm::Type::getInt32Ty(VMContext), 0), - "unused_eh_selector"); - CGF.Builder.CreateStore(Exc, RethrowPtr); - CGF.EmitBranchThroughCleanup(FinallyRethrow); - } + // Bind the catch parameter if it exists. + if (const VarDecl *CatchParam = Handler.Variable) { + const llvm::Type *CatchType = CGF.ConvertType(CatchParam->getType()); + llvm::Value *CastExn = CGF.Builder.CreateBitCast(Exn, CatchType); - CodeGenFunction::CleanupBlockInfo Info = CGF.PopCleanupBlock(); - - CGF.EmitBlock(MatchEnd); - - // Unfortunately, we also have to generate another EH frame here - // in case this throws. - llvm::BasicBlock *MatchEndHandler = - CGF.createBasicBlock("match.end.handler"); - llvm::BasicBlock *Cont = CGF.createBasicBlock("invoke.cont"); - CGF.Builder.CreateInvoke(ObjCTypes.getObjCEndCatchFn(), - Cont, MatchEndHandler); - - CGF.EmitBlock(Cont); - if (Info.SwitchBlock) - CGF.EmitBlock(Info.SwitchBlock); - if (Info.EndBlock) - CGF.EmitBlock(Info.EndBlock); - - CGF.EmitBlock(MatchEndHandler); - llvm::Value *Exc = CGF.Builder.CreateCall(llvm_eh_exception, "exc"); - // We are required to emit this call to satisfy LLVM, even - // though we don't use the result. - CGF.Builder.CreateCall3(llvm_eh_selector, - Exc, ObjCTypes.getEHPersonalityPtr(), - llvm::ConstantInt::get( - llvm::Type::getInt32Ty(VMContext), 0), - "unused_eh_selector"); - CGF.Builder.CreateStore(Exc, RethrowPtr); - CGF.EmitBranchThroughCleanup(FinallyRethrow); + CGF.EmitLocalBlockVarDecl(*CatchParam); + CGF.Builder.CreateStore(CastExn, CGF.GetAddrOfLocalVar(CatchParam)); + } - if (Next) - CGF.EmitBlock(Next); - } else { - assert(!Next && "catchup should be last handler."); + CGF.ObjCEHValueStack.push_back(Exn); + CGF.EmitStmt(Handler.Body); + CGF.ObjCEHValueStack.pop_back(); - CGF.Builder.CreateStore(Exc, RethrowPtr); - CGF.EmitBranchThroughCleanup(FinallyRethrow); - } - } + // Leave the earlier cleanup. + CGF.PopCleanupBlock(); - // Pop the cleanup entry, the @finally is outside this cleanup - // scope. - CodeGenFunction::CleanupBlockInfo Info = CGF.PopCleanupBlock(); - CGF.setInvokeDest(PrevLandingPad); + CGF.EmitBranchThroughCleanup(Cont); + } - CGF.EmitBlock(FinallyBlock); + // Go back to the try-statement fallthrough. + CGF.Builder.restoreIP(SavedIP); - if (isTry) { - if (const ObjCAtFinallyStmt* FinallyStmt = - cast<ObjCAtTryStmt>(S).getFinallyStmt()) - CGF.EmitStmt(FinallyStmt->getFinallyBody()); - } else { - // Emit 'objc_sync_exit(expr)' as finally's sole statement for - // @synchronized. - CGF.Builder.CreateCall(ObjCTypes.getSyncExitFn(), SyncArg); - } - - if (Info.SwitchBlock) - CGF.EmitBlock(Info.SwitchBlock); - if (Info.EndBlock) - CGF.EmitBlock(Info.EndBlock); - - // Branch around the rethrow code. - CGF.EmitBranch(FinallyEnd); - - // Generate the rethrow code, taking care to use an invoke if we are in a - // nested exception scope. - CGF.EmitBlock(FinallyRethrow); - if (PrevLandingPad) { - llvm::BasicBlock *Cont = CGF.createBasicBlock("invoke.cont"); - CGF.Builder.CreateInvoke(ObjCTypes.getUnwindResumeOrRethrowFn(), - Cont, PrevLandingPad, - CGF.Builder.CreateLoad(RethrowPtr)); - CGF.EmitBlock(Cont); - } else { - CGF.Builder.CreateCall(ObjCTypes.getUnwindResumeOrRethrowFn(), - CGF.Builder.CreateLoad(RethrowPtr)); - } - CGF.Builder.CreateUnreachable(); + // Pop out of the normal cleanup on the finally. + if (S.getFinallyStmt()) + CGF.ExitFinallyBlock(FinallyInfo); - CGF.EmitBlock(FinallyEnd); + if (Cont.Block) + CGF.EmitBlock(Cont.Block); } /// EmitThrowStmt - Generate code for a throw statement. void CGObjCNonFragileABIMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF, const ObjCAtThrowStmt &S) { llvm::Value *Exception; + llvm::Constant *FunctionThrowOrRethrow; if (const Expr *ThrowExpr = S.getThrowExpr()) { Exception = CGF.EmitScalarExpr(ThrowExpr); + FunctionThrowOrRethrow = ObjCTypes.getExceptionThrowFn(); } else { assert((!CGF.ObjCEHValueStack.empty() && CGF.ObjCEHValueStack.back()) && "Unexpected rethrow outside @catch block."); Exception = CGF.ObjCEHValueStack.back(); + FunctionThrowOrRethrow = ObjCTypes.getExceptionRethrowFn(); } llvm::Value *ExceptionAsObject = CGF.Builder.CreateBitCast(Exception, ObjCTypes.ObjectPtrTy, "tmp"); llvm::BasicBlock *InvokeDest = CGF.getInvokeDest(); if (InvokeDest) { - llvm::BasicBlock *Cont = CGF.createBasicBlock("invoke.cont"); - CGF.Builder.CreateInvoke(ObjCTypes.getExceptionThrowFn(), - Cont, InvokeDest, + CGF.Builder.CreateInvoke(FunctionThrowOrRethrow, + CGF.getUnreachableBlock(), InvokeDest, &ExceptionAsObject, &ExceptionAsObject + 1); - CGF.EmitBlock(Cont); - } else - CGF.Builder.CreateCall(ObjCTypes.getExceptionThrowFn(), ExceptionAsObject); - CGF.Builder.CreateUnreachable(); + } else { + CGF.Builder.CreateCall(FunctionThrowOrRethrow, ExceptionAsObject) + ->setDoesNotReturn(); + CGF.Builder.CreateUnreachable(); + } // Clear the insertion point to indicate we are in unreachable code. CGF.Builder.ClearInsertionPoint(); @@ -5863,7 +5903,8 @@ CGObjCNonFragileABIMac::GetInterfaceEHType(const ObjCInterfaceDecl *ID, llvm::GlobalValue::ExternalLinkage, 0, VTableName); - llvm::Value *VTableIdx = llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), 2); + llvm::Value *VTableIdx = + llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), 2); std::vector<llvm::Constant*> Values(3); Values[0] = llvm::ConstantExpr::getGetElementPtr(VTableGV, &VTableIdx, 1); |