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

2021-06-20  本文已影响0人  无悔zero

首先简单创建项目,调用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源码分开理解,个人了解还不深,请见谅)

上一篇下一篇

猜你喜欢

热点阅读