diff options
Diffstat (limited to 'lib/CodeGen/CGObjCGNU.cpp')
-rw-r--r-- | lib/CodeGen/CGObjCGNU.cpp | 378 |
1 files changed, 151 insertions, 227 deletions
diff --git a/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp index 6c25afe..c9da348 100644 --- a/lib/CodeGen/CGObjCGNU.cpp +++ b/lib/CodeGen/CGObjCGNU.cpp @@ -17,6 +17,7 @@ #include "CGObjCRuntime.h" #include "CodeGenModule.h" #include "CodeGenFunction.h" +#include "CGException.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -162,7 +163,8 @@ public: const ObjCMethodDecl *Method); virtual llvm::Value *GetClass(CGBuilderTy &Builder, const ObjCInterfaceDecl *OID); - virtual llvm::Value *GetSelector(CGBuilderTy &Builder, Selector Sel); + virtual llvm::Value *GetSelector(CGBuilderTy &Builder, Selector Sel, + bool lval = false); virtual llvm::Value *GetSelector(CGBuilderTy &Builder, const ObjCMethodDecl *Method); @@ -179,8 +181,10 @@ public: virtual llvm::Function *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); virtual void EmitThrowStmt(CodeGen::CodeGenFunction &CGF, const ObjCAtThrowStmt &S); virtual llvm::Value * EmitObjCWeakRead(CodeGen::CodeGenFunction &CGF, @@ -197,7 +201,7 @@ public: virtual void EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF, llvm::Value *DestPtr, llvm::Value *SrcPtr, - QualType Ty); + llvm::Value *Size); virtual LValue EmitObjCValueForIvar(CodeGen::CodeGenFunction &CGF, QualType ObjectTy, llvm::Value *BaseValue, @@ -360,14 +364,16 @@ llvm::Value *CGObjCGNU::GetClass(CGBuilderTy &Builder, return Builder.CreateCall(ClassLookupFn, ClassName); } -llvm::Value *CGObjCGNU::GetSelector(CGBuilderTy &Builder, Selector Sel) { +llvm::Value *CGObjCGNU::GetSelector(CGBuilderTy &Builder, Selector Sel, + bool lval) { llvm::GlobalAlias *&US = UntypedSelectors[Sel.getAsString()]; if (US == 0) US = new llvm::GlobalAlias(llvm::PointerType::getUnqual(SelectorTy), llvm::GlobalValue::PrivateLinkage, ".objc_untyped_selector_alias"+Sel.getAsString(), NULL, &TheModule); - + if (lval) + return US; return Builder.CreateLoad(US); } @@ -624,8 +630,8 @@ CGObjCGNU::GenerateMessageSend(CodeGen::CodeGenFunction &CGF, // to be on the stack / in those registers at the time) on most platforms, // and generates a SegV on SPARC. With LLVM it corrupts the stack. bool isPointerSizedReturn = false; - if (ResultType->isAnyPointerType() || ResultType->isIntegralType() || - ResultType->isVoidType()) + if (ResultType->isAnyPointerType() || + ResultType->isIntegralOrEnumerationType() || ResultType->isVoidType()) isPointerSizedReturn = true; llvm::BasicBlock *startBB = 0; @@ -1848,245 +1854,168 @@ llvm::Constant *CGObjCGNU::EnumerationMutationFunction() { return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation"); } -void CGObjCGNU::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, - const Stmt &S) { - // Pointer to the personality function - llvm::Constant *Personality = - CGM.CreateRuntimeFunction(llvm::FunctionType::get(llvm::Type::getInt32Ty(VMContext), - true), - "__gnu_objc_personality_v0"); - Personality = llvm::ConstantExpr::getBitCast(Personality, PtrTy); - std::vector<const llvm::Type*> Params; - Params.push_back(PtrTy); - llvm::Value *RethrowFn = - CGM.CreateRuntimeFunction(llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), - Params, false), "_Unwind_Resume"); - - 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 *CatchInCatch = CGF.createBasicBlock("catch.rethrow"); - llvm::BasicBlock *FinallyBlock = CGF.createBasicBlock("finally"); - llvm::BasicBlock *FinallyRethrow = CGF.createBasicBlock("finally.throw"); - llvm::BasicBlock *FinallyEnd = CGF.createBasicBlock("finally.end"); - - // @synchronized() - if (!isTry) { - std::vector<const llvm::Type*> Args(1, IdTy); - llvm::FunctionType *FTy = - llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Args, false); - llvm::Value *SyncEnter = CGM.CreateRuntimeFunction(FTy, "objc_sync_enter"); - llvm::Value *SyncArg = - CGF.EmitScalarExpr(cast<ObjCAtSynchronizedStmt>(S).getSynchExpr()); - SyncArg = CGF.Builder.CreateBitCast(SyncArg, IdTy); - CGF.Builder.CreateCall(SyncEnter, SyncArg); - } +void CGObjCGNU::EmitSynchronizedStmt(CodeGen::CodeGenFunction &CGF, + const ObjCAtSynchronizedStmt &S) { + std::vector<const llvm::Type*> Args(1, IdTy); + llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Args, false); + // Evaluate the lock operand. This should dominate the cleanup. + llvm::Value *SyncArg = + CGF.EmitScalarExpr(S.getSynchExpr()); - // Push an EH context entry, used for handling rethrows and jumps - // through finally. - CGF.PushCleanupBlock(FinallyBlock); - - // Emit the statements in the @try {} block - CGF.setInvokeDest(TryHandler); - - CGF.EmitBlock(TryBlock); - CGF.EmitStmt(isTry ? cast<ObjCAtTryStmt>(S).getTryBody() - : cast<ObjCAtSynchronizedStmt>(S).getSynchBody()); - - // Jump to @finally if there is no exception - CGF.EmitBranchThroughCleanup(FinallyEnd); - - // Emit the handlers - CGF.EmitBlock(TryHandler); - - // Get the correct versions of the exception handling intrinsics - 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); - - // Exception object - llvm::Value *Exc = CGF.Builder.CreateCall(llvm_eh_exception, "exc"); - llvm::Value *RethrowPtr = CGF.CreateTempAlloca(Exc->getType(), "_rethrow"); - - llvm::SmallVector<llvm::Value*, 8> ESelArgs; - llvm::SmallVector<std::pair<const VarDecl*, const Stmt*>, 8> Handlers; - - ESelArgs.push_back(Exc); - ESelArgs.push_back(Personality); - - bool HasCatchAll = false; - // Only @try blocks are allowed @catch blocks, but both can have @finally - if (isTry) { - if (cast<ObjCAtTryStmt>(S).getNumCatchStmts()) { - const ObjCAtTryStmt &AtTry = cast<ObjCAtTryStmt>(S); - CGF.setInvokeDest(CatchInCatch); - - for (unsigned I = 0, N = AtTry.getNumCatchStmts(); I != N; ++I) { - const ObjCAtCatchStmt *CatchStmt = AtTry.getCatchStmt(I); - const VarDecl *CatchDecl = CatchStmt->getCatchParamDecl(); - Handlers.push_back(std::make_pair(CatchDecl, - CatchStmt->getCatchBody())); - - // @catch() and @catch(id) both catch any ObjC exception - if (!CatchDecl || CatchDecl->getType()->isObjCIdType() - || CatchDecl->getType()->isObjCQualifiedIdType()) { - // Use i8* null here to signal this is a catch all, not a cleanup. - ESelArgs.push_back(NULLPtr); - HasCatchAll = true; - // No further catches after this one will ever by reached - break; - } - - // All other types should be Objective-C interface pointer types. - const ObjCObjectPointerType *OPT = - CatchDecl->getType()->getAs<ObjCObjectPointerType>(); - assert(OPT && "Invalid @catch type."); - const ObjCInterfaceDecl *IDecl = - OPT->getObjectType()->getInterface(); - assert(IDecl && "Invalid @catch type."); - llvm::Value *EHType = - MakeConstantString(IDecl->getNameAsString()); - ESelArgs.push_back(EHType); - } - } - } + // Acquire the lock. + llvm::Value *SyncEnter = CGM.CreateRuntimeFunction(FTy, "objc_sync_enter"); + SyncArg = CGF.Builder.CreateBitCast(SyncArg, IdTy); + CGF.Builder.CreateCall(SyncEnter, SyncArg); - // We use a cleanup unless there was already a catch all. - if (!HasCatchAll) { - ESelArgs.push_back(llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), 0)); - Handlers.push_back(std::make_pair((const ParmVarDecl*) 0, (const Stmt*) 0)); + // Register an all-paths cleanup to release the lock. + { + CodeGenFunction::CleanupBlock + ReleaseScope(CGF, CodeGenFunction::NormalAndEHCleanup); + + llvm::Value *SyncExit = CGM.CreateRuntimeFunction(FTy, "objc_sync_exit"); + SyncArg = CGF.Builder.CreateBitCast(SyncArg, IdTy); + CGF.Builder.CreateCall(SyncExit, SyncArg); } - // Find which handler was matched. - llvm::Value *ESelector = CGF.Builder.CreateCall(llvm_eh_selector, - ESelArgs.begin(), ESelArgs.end(), "selector"); + // Emit the body of the statement. + CGF.EmitStmt(S.getSynchBody()); - for (unsigned i = 0, e = Handlers.size(); i != e; ++i) { - const VarDecl *CatchParam = Handlers[i].first; - const Stmt *CatchBody = Handlers[i].second; + // Pop the lock-release cleanup. + CGF.PopCleanupBlock(); +} - llvm::BasicBlock *Next = 0; +namespace { + struct CatchHandler { + const VarDecl *Variable; + const Stmt *Body; + llvm::BasicBlock *Block; + llvm::Value *TypeInfo; + }; +} - // The last handler always matches. - if (i + 1 != e) { - assert(CatchParam && "Only last handler can be a catch all."); +void CGObjCGNU::EmitTryStmt(CodeGen::CodeGenFunction &CGF, + const ObjCAtTryStmt &S) { + // Unlike the Apple non-fragile runtimes, which also uses + // unwind-based zero cost exceptions, the GNU Objective C runtime's + // EH support isn't a veneer over C++ EH. Instead, exception + // objects are created by __objc_exception_throw and destroyed by + // the personality function; this avoids the need for bracketing + // catch handlers with calls to __blah_begin_catch/__blah_end_catch + // (or even _Unwind_DeleteException), but probably doesn't + // interoperate very well with foreign exceptions. + + // Jump destination for falling out of catch bodies. + CodeGenFunction::JumpDest Cont; + if (S.getNumCatchStmts()) + Cont = CGF.getJumpDestInCurrentScope("eh.cont"); + + // We handle @finally statements by pushing them as a cleanup + // before entering the catch. + CodeGenFunction::FinallyInfo FinallyInfo; + if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) { + std::vector<const llvm::Type*> Args(1, IdTy); + llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Args, false); + llvm::Constant *Rethrow = + CGM.CreateRuntimeFunction(FTy, "objc_exception_throw"); - // Test whether this block matches the type for the selector and branch - // to Match if it does, or to the next BB if it doesn't. - llvm::BasicBlock *Match = CGF.createBasicBlock("match"); - Next = CGF.createBasicBlock("catch.next"); - llvm::Value *Id = CGF.Builder.CreateCall(llvm_eh_typeid_for, - CGF.Builder.CreateBitCast(ESelArgs[i+2], PtrTy)); - CGF.Builder.CreateCondBr(CGF.Builder.CreateICmpEQ(ESelector, Id), Match, - Next); + FinallyInfo = CGF.EnterFinallyBlock(Finally->getFinallyBody(), 0, 0, + Rethrow); + } - CGF.EmitBlock(Match); - } + llvm::SmallVector<CatchHandler, 8> Handlers; - if (CatchBody) { - llvm::Value *ExcObject = CGF.Builder.CreateBitCast(Exc, - CGF.ConvertType(CatchParam->getType())); - - // Bind the catch parameter if it exists. - if (CatchParam) { - // 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)); - } + // 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(); - CGF.ObjCEHValueStack.push_back(ExcObject); - CGF.EmitStmt(CatchBody); - CGF.ObjCEHValueStack.pop_back(); + Handlers.push_back(CatchHandler()); + CatchHandler &Handler = Handlers.back(); + Handler.Variable = CatchDecl; + Handler.Body = CatchStmt->getCatchBody(); + Handler.Block = CGF.createBasicBlock("catch"); - CGF.EmitBranchThroughCleanup(FinallyEnd); + // @catch() and @catch(id) both catch any ObjC exception. + // Treat them as catch-alls. + // FIXME: this is what this code was doing before, but should 'id' + // really be catching foreign exceptions? + if (!CatchDecl + || CatchDecl->getType()->isObjCIdType() + || CatchDecl->getType()->isObjCQualifiedIdType()) { - if (Next) - CGF.EmitBlock(Next); - } else { - assert(!Next && "catchup should be last handler."); + Handler.TypeInfo = 0; // catch-all + + // Don't consider any other catches. + break; + } - CGF.Builder.CreateStore(Exc, RethrowPtr); - CGF.EmitBranchThroughCleanup(FinallyRethrow); + // All other types should be Objective-C interface pointer types. + const ObjCObjectPointerType *OPT = + CatchDecl->getType()->getAs<ObjCObjectPointerType>(); + assert(OPT && "Invalid @catch type."); + const ObjCInterfaceDecl *IDecl = + OPT->getObjectType()->getInterface(); + assert(IDecl && "Invalid @catch type."); + Handler.TypeInfo = MakeConstantString(IDecl->getNameAsString()); } + + 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); } - // The @finally block is a secondary landing pad for any exceptions thrown in - // @catch() blocks - CGF.EmitBlock(CatchInCatch); - Exc = CGF.Builder.CreateCall(llvm_eh_exception, "exc"); - ESelArgs.clear(); - ESelArgs.push_back(Exc); - ESelArgs.push_back(Personality); - // If there is a @catch or @finally clause in outside of this one then we - // need to make sure that we catch and rethrow it. - if (PrevLandingPad) { - ESelArgs.push_back(NULLPtr); - } else { - ESelArgs.push_back(llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), 0)); - } - CGF.Builder.CreateCall(llvm_eh_selector, ESelArgs.begin(), ESelArgs.end(), - "selector"); - CGF.Builder.CreateCall(llvm_eh_typeid_for, - CGF.Builder.CreateIntToPtr(ESelArgs[2], PtrTy)); - CGF.Builder.CreateStore(Exc, RethrowPtr); - CGF.EmitBranchThroughCleanup(FinallyRethrow); + + // Emit the try body. + CGF.EmitStmt(S.getTryBody()); - CodeGenFunction::CleanupBlockInfo Info = CGF.PopCleanupBlock(); + // Leave the try. + if (S.getNumCatchStmts()) + CGF.EHStack.popCatch(); - CGF.setInvokeDest(PrevLandingPad); + // Remember where we were. + CGBuilderTy::InsertPoint SavedIP = CGF.Builder.saveAndClearIP(); - CGF.EmitBlock(FinallyBlock); + // Emit the handlers. + for (unsigned I = 0, E = Handlers.size(); I != E; ++I) { + CatchHandler &Handler = Handlers[I]; + CGF.EmitBlock(Handler.Block); + llvm::Value *Exn = CGF.Builder.CreateLoad(CGF.getExceptionSlot()); - 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. - std::vector<const llvm::Type*> Args(1, IdTy); - llvm::FunctionType *FTy = - llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Args, false); - llvm::Value *SyncExit = CGM.CreateRuntimeFunction(FTy, "objc_sync_exit"); - llvm::Value *SyncArg = - CGF.EmitScalarExpr(cast<ObjCAtSynchronizedStmt>(S).getSynchExpr()); - SyncArg = CGF.Builder.CreateBitCast(SyncArg, IdTy); - CGF.Builder.CreateCall(SyncExit, SyncArg); - } + // Bind the catch parameter if it exists. + if (const VarDecl *CatchParam = Handler.Variable) { + const llvm::Type *CatchType = CGF.ConvertType(CatchParam->getType()); + Exn = CGF.Builder.CreateBitCast(Exn, CatchType); - if (Info.SwitchBlock) - CGF.EmitBlock(Info.SwitchBlock); - if (Info.EndBlock) - CGF.EmitBlock(Info.EndBlock); + CGF.EmitLocalBlockVarDecl(*CatchParam); + CGF.Builder.CreateStore(Exn, CGF.GetAddrOfLocalVar(CatchParam)); + } - // Branch around the rethrow code. - CGF.EmitBranch(FinallyEnd); + CGF.ObjCEHValueStack.push_back(Exn); + CGF.EmitStmt(Handler.Body); + CGF.ObjCEHValueStack.pop_back(); - CGF.EmitBlock(FinallyRethrow); + CGF.EmitBranchThroughCleanup(Cont); + } - llvm::Value *ExceptionObject = CGF.Builder.CreateLoad(RethrowPtr); - llvm::BasicBlock *UnwindBB = CGF.getInvokeDest(); - if (!UnwindBB) { - CGF.Builder.CreateCall(RethrowFn, ExceptionObject); - // Exception always thrown, next instruction is never reached. - CGF.Builder.CreateUnreachable(); - } else { - // If there is a @catch block outside this scope, we invoke instead of - // calling because we may return to this function. This is very slow, but - // some people still do it. It would be nice to add an optimised path for - // this. - CGF.Builder.CreateInvoke(RethrowFn, UnwindBB, UnwindBB, &ExceptionObject, - &ExceptionObject+1); - } + // Go back to the try-statement fallthrough. + CGF.Builder.restoreIP(SavedIP); + + // Pop out of the finally. + if (S.getFinallyStmt()) + CGF.ExitFinallyBlock(FinallyInfo); - CGF.EmitBlock(FinallyEnd); + if (Cont.Block) { + if (Cont.Block->use_empty()) + delete Cont.Block; + else { + CGF.EmitBranch(Cont.Block); + CGF.EmitBlock(Cont.Block); + } + } } void CGObjCGNU::EmitThrowStmt(CodeGen::CodeGenFunction &CGF, @@ -2174,17 +2103,12 @@ void CGObjCGNU::EmitObjCStrongCastAssign(CodeGen::CodeGenFunction &CGF, void CGObjCGNU::EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF, llvm::Value *DestPtr, llvm::Value *SrcPtr, - QualType Ty) { + llvm::Value *Size) { CGBuilderTy B = CGF.Builder; DestPtr = EnforceType(B, DestPtr, IdTy); SrcPtr = EnforceType(B, SrcPtr, PtrToIdTy); - std::pair<uint64_t, unsigned> TypeInfo = CGM.getContext().getTypeInfo(Ty); - unsigned long size = TypeInfo.first/8; - // FIXME: size_t - llvm::Value *N = llvm::ConstantInt::get(LongTy, size); - - B.CreateCall3(MemMoveFn, DestPtr, SrcPtr, N); + B.CreateCall3(MemMoveFn, DestPtr, SrcPtr, Size); } llvm::GlobalVariable *CGObjCGNU::ObjCIvarOffsetVariable( |