美文网首页
从LLVM源码探究调用alloc会走objc_alloc

从LLVM源码探究调用alloc会走objc_alloc

作者: 无悔zero | 来源:发表于2021-06-20 19:47 被阅读0次

    首先简单创建项目,调用alloc并断点:

    然后进入汇编模式运行(Debug -> Debug Workflow -> Always Show Disassembly):

    我们发现原来调用alloc时,底层会调用objc_alloc

    LLVM是构架编译器(compiler)的框架系统,以C++编写而成,用于优化以任意程序语言编写的程序的编译时间(compile-time)、链接时间(link-time)、运行时间(run-time)以及空闲时间(idle-time),对开发者保持开放,并兼容已有脚本。

    2000年LLVM开始开发。
    2005年Apple雇了Chris Lattner,LLVM也相当于成了Apple的官方支持的编译器
    Apple已经将它用在OpenCL的流水线优化,Xcode已经能使用llvm-gcc编译代码。

    1. 今天从LLVM源码探究一下究竟:
    CodeGen::RValue CGObjCRuntime::GeneratePossiblySpecializedMessageSend(
        CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType,
        Selector Sel, llvm::Value *Receiver, const CallArgList &Args,
        const ObjCInterfaceDecl *OID, const ObjCMethodDecl *Method,
        bool isClassMessage) {
        
      if (Optional<llvm::Value *> SpecializedResult =
              tryGenerateSpecializedMessageSend(CGF, ResultType, Receiver, Args,
                                                Sel, Method, isClassMessage)) {
        return RValue::get(SpecializedResult.getValue());
      }
        //没有对象返回就走msgSend
      return GenerateMessageSend(CGF, Return, ResultType, Sel, Receiver, Args, OID,
                                 Method);
    }
    

    首先是先调用tryGenerateSpecializedMessageSend(表示尝试生成特殊消息发送)判断,最终调用函数:

    static Optional<llvm::Value *>
    tryGenerateSpecializedMessageSend(CodeGenFunction &CGF, QualType ResultType,
                                      llvm::Value *Receiver,
                                      const CallArgList& Args, Selector Sel,
                                      const ObjCMethodDecl *method,
                                      bool isClassMessage) {
      ...
      auto &Runtime = CGM.getLangOpts().ObjCRuntime;
      switch (Sel.getMethodFamily()) {
      case OMF_alloc:
        if (isClassMessage &&
            Runtime.shouldUseRuntimeFunctionsForAlloc() &&
            ResultType->isObjCObjectPointerType()) {
            // [Foo alloc] -> objc_alloc(Foo) or
            // [self alloc] -> objc_alloc(self)
            if (Sel.isUnarySelector() && Sel.getNameForSlot(0) == "alloc")
                return CGF.EmitObjCAlloc(Receiver, CGF.ConvertType(ResultType));
      ...
    }
    
    llvm::Value *CodeGenFunction::EmitObjCAlloc(llvm::Value *value,
                                                llvm::Type *resultType) {
      return emitObjCValueOperation(*this, value, resultType,
                                    CGM.getObjCEntrypoints().objc_alloc,
                                    "objc_alloc");
    }
    
    static llvm::Value *emitObjCValueOperation(CodeGenFunction &CGF,
                                               llvm::Value *value,
                                               llvm::Type *returnType,
                                               llvm::FunctionCallee &fn,
                                               StringRef fnName) {
      ...  
      // Call the function. 
      llvm::CallBase *Inst = CGF.EmitCallOrInvoke(fn, value);//调用函数
    
      // Cast the result back to the original type.
      return CGF.Builder.CreateBitCast(Inst, origType);
    }
    

    可以发现,调用alloc方法会执行到EmitObjCAlloc函数, 最终执行objc_alloc。其实这部分是由系统级别的消息处理逻辑,所以NSObject的初始化是由系统完成的,。

    根据文档解释就是,objc_alloc符号函数比msgSend快:

    /// The ObjC runtime may provide entrypoints that are likely to be faster
    /// than an ordinary message send of the appropriate selector.
    ObjC运行时可能提供可能更快的入口点
    比普通的消息发送合适的选择器。
    
    /// If the runtime does support a required entrypoint, then this method will
    /// generate a call and return the resulting value.  Otherwise it will return
    /// None and the caller can generate a msgSend instead. 
    如果运行时确实支持所需的入口点,则此方法将支持
    生成一个调用并返回结果值。否则它会返回
    无,调用者可以生成msgSend代替。
    

    回想之前alloc源码分析文章中的callAlloc

    // Calls [cls alloc].
    id
    objc_alloc(Class cls)
    {
        return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
    }
    
    static ALWAYS_INLINE id
    callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
    {
        if (slowpath(checkNil && !cls)) return nil;
    
    #if __OBJC2__
        if (fastpath(!cls->ISA()->hasCustomAWZ())) {
            // No alloc/allocWithZone implementation. Go straight to the allocator.
            // fixme store hasCustomAWZ in the non-meta class and 
            // add it to canAllocFast's summary
            if (fastpath(cls->canAllocFast())) { ... }
            else {
                // Has ctor or raw isa or something. Use the slower path.
                id obj = class_createInstance(cls, 0);
                if (slowpath(!obj)) return callBadAllocHandler(cls);
                return obj;
            }
        }
    #endif
    
        // No shortcuts available.
        if (allocWithZone) return [cls allocWithZone:nil];
        return [cls alloc];
    }
    

    第一次调用callAlloc时,还没有返回值又调用了alloc,难道是循环alloc吗?当然不是,从LLVMGeneratePossiblySpecializedMessageSend函数看,没有返回值时会调用GenerateMessageSend,回过头来看alloc源码,便是调用alloc->callAlloc->class_createInstance
    (alloc源码和llvm源码分开理解,个人了解还不深,请见谅)

    相关文章

      网友评论

          本文标题:从LLVM源码探究调用alloc会走objc_alloc

          本文链接:https://www.haomeiwen.com/subject/ilmsyltx.html