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 6c25afeb9ad3e..c9da348cbfe3f 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(  | 
