ios底层探索消息发送 cache 查找(下)

Posted WeaterMr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ios底层探索消息发送 cache 查找(下)相关的知识,希望对你有一定的参考价值。

ios底层探索消息发送 cache 查找(下)

通过sel从缓存中找到对应的 imp

//NORMAL, _objc_msgSend, __objc_msgSend_uncached
.macro CacheLookup Mode, Function, MissLabelDynamic, MissLabelConstant
    //   requirements:
    //   //缓存不存在返回NULL,x0设置为0
    //   GETIMP:
    //     The cache-miss is just returning NULL (setting x0 to 0)
    //   参数说明
    //   NORMAL and LOOKUP:
    //   - x0 contains the receiver
    //   - x1 contains the selector
    //   - x16 contains the isa
    //   - other registers are set as per calling conventions
    //
//调用过来的p16存储的是cls,将cls存储在x15.
    mov x15, x16            // stash the original isa
//_objc_msgSend
LLookupStart\\Function:
    // p1 = SEL, p16 = isa
//arm64 64 OSX/SIMULATOR
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS
    //isa->cache,首地址也就是_bucketsAndMaybeMask
    ldr p10, [x16, #CACHE]              // p10 = mask|buckets
    //lsr逻辑右移 p11 = _bucketsAndMaybeMask >> 48 也就是 mask
    lsr p11, p10, #48           // p11 = mask
    //p10 = _bucketsAndMaybeMask & 0xffffffffffff = buckets(保留后48位)
    and p10, p10, #0xffffffffffff   // p10 = buckets
    //x12 = cmd & mask   w1为第二个参数cmd(self,cmd...),w11也就是p11 也就是执行cache_hash。这里没有>>7位的操作
    and w12, w1, w11            // x12 = _cmd & mask
//arm64 64 真机这里p11计算后是_bucketsAndMaybeMask
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    ldr p11, [x16, #CACHE]          // p11 = mask|buckets
//arm64 + iOS + !模拟器 + 非mac应用
#if CONFIG_USE_PREOPT_CACHES
//iphone 12以后指针验证
#if __has_feature(ptrauth_calls)
    //tbnz 测试位不为0则跳转。与tbz对应。 p11 第0位不为0则跳转 LLookupPreopt\\Function。
    tbnz    p11, #0, LLookupPreopt\\Function
    //p10 = _bucketsAndMaybeMask & 0x0000ffffffffffff = buckets
    and p10, p11, #0x0000ffffffffffff   // p10 = buckets
#else
    //p10 = _bucketsAndMaybeMask & 0x0000fffffffffffe = buckets
    and p10, p11, #0x0000fffffffffffe   // p10 = buckets
    //p11 第0位不为0则跳转 LLookupPreopt\\Function。
    tbnz    p11, #0, LLookupPreopt\\Function
#endif
    //eor 逻辑异或(^) 格式为:EOR{S}{cond} Rd, Rn, Operand2
    //p12 = selector ^ (selector >> 7) select 右移7位&自己给到p12
    eor p12, p1, p1, LSR #7
    //p12 = p12 & (_bucketsAndMaybeMask >> 48) = index & mask值 = buckets中的下标
    and p12, p12, p11, LSR #48      // x12 = (_cmd ^ (_cmd >> 7)) & mask
#else
    //p10 = _bucketsAndMaybeMask & 0x0000ffffffffffff = buckets
    and p10, p11, #0x0000ffffffffffff   // p10 = buckets
    //p12 = selector & (_bucketsAndMaybeMask >>48) = sel & mask = buckets中的下标
    and p12, p1, p11, LSR #48       // x12 = _cmd & mask
#endif // CONFIG_USE_PREOPT_CACHES
//arm64 32
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
    //后4位为mask前置0的个数的case
    ldr p11, [x16, #CACHE]              // p11 = mask|buckets
    and p10, p11, #~0xf         // p10 = buckets 相当于后4位置为0,取前32位
    and p11, p11, #0xf          // p11 = maskShift 取的是后4位,为mask前置位的0的个数
    mov p12, #0xffff
    lsr p11, p12, p11           // p11 = mask = 0xffff >> p11
    and p12, p1, p11            // x12 = _cmd & mask
#else
#error Unsupported cache mask storage for ARM64.
#endif
    //通过上面的计算 p10 = buckets,p11 = mask(arm64真机是_bucketsAndMaybeMask), p12 = index
    // p13(bucket_t) = buckets + 下标 << 4   PTRSHIFT arm64 为3.  <<4 位为16字节 buckets + 下标 *16 = buckets + index *16 也就是直接平移到了第几个元素的地址。
    add p13, p10, p12, LSL #(1+PTRSHIFT)
                        // p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
    //这里就直接遍历查找了,因为arm64下cache_next相当于遍历(这里只扫描了前面)
                        // do {
    //p17 = imp, p9 = sel
1:  ldp p17, p9, [x13], #-BUCKET_SIZE   //     {imp, sel} = *bucket--
    //sel - _cmd != 0 则跳转 3:,也就意味着没有找到就跳转到__objc_msgSend_uncached
    cmp p9, p1              //     if (sel != _cmd) {
    b.ne    3f              //         scan more
                        //     } else {
    //找到则调用或者返回imp,Mode为 NORMAL
2:  CacheHit \\Mode              // hit:    call or return imp  命中
                        //     }
//__objc_msgSend_uncached
//缓存中找不到方法就走__objc_msgSend_uncached逻辑了。
    //cbz 为0跳转 sel == nil 跳转 \\MissLabelDynamic
3:  cbz p9, \\MissLabelDynamic       //     if (sel == 0) goto Miss; 有空位没有找到说明没有缓存
    //bucket_t - buckets 由于是递减操作
    cmp p13, p10            // } while (bucket >= buckets) //⚠️ 这里一直是往前找,后面的元素在后面还有一次循环。
    //无符号大于等于 则跳转1:f b 分别代表front与back
    b.hs    1b

//没有命中cache  查找 p13 = mask对应的元素,也就是倒数第二个
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS
    //p13 = buckets + (mask << 4) 平移找到对应mask的bucket_t。UXTW 将w11扩展为64位后左移4
    add p13, p10, w11, UXTW #(1+PTRSHIFT)
                        // p13 = buckets + (mask << 1+PTRSHIFT)
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    //p13 = buckets + (mask >> 44) 这里右移44位,少移动4位就不用再左移了。因为maskZeroBits的存在 就找到了mask对应元素的地址
    add p13, p10, p11, LSR #(48 - (1+PTRSHIFT))
                        // p13 = buckets + (mask << 1+PTRSHIFT)
                        // see comment about maskZeroBits
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
    //p13 = buckets + (mask << 4) 找到对应mask的bucket_t。
    add p13, p10, p11, LSL #(1+PTRSHIFT)
                        // p13 = buckets + (mask << 1+PTRSHIFT)
#else
#error Unsupported cache mask storage for ARM64.
#endif
    //p12 = buckets + (p12<<4) index对应的bucket_t
    add p12, p10, p12, LSL #(1+PTRSHIFT)
                        // p12 = first probed bucket

    //之前已经往前查找过了,这里从后往index查找
                        // do {
    //p17 = imp p9 = sel
4:  ldp p17, p9, [x13], #-BUCKET_SIZE   //     {imp, sel} = *bucket--
    //sel - _cmd
    cmp p9, p1              //     if (sel == _cmd)
    //sel == _cmd跳转CacheHit
    b.eq    2b              //         goto hit
    //sel != nil
    cmp p9, #0              // } while (sel != 0 &&
    //
    ccmp    p13, p12, #0, ne        //     bucket > first_probed)
    //有值跳转4:
    b.hi    4b

LLookupEnd\\Function:
LLookupRecover\\Function:
//仍然没有找到缓存,缓存彻底不存在 __objc_msgSend_uncached()
    b   \\MissLabelDynamic

上一章我们已经获取到class,上面的代码主要流程是通过class-->catch-->buckets-->index(通过哈希操作得到)-->bucket -->imp
1.判断receiver是否存在。
2.通过receiver对象中的isa&mask获取当前的class信息。
3.class通过内存平移0x10获取对应的成员变量 cache
4.通过cache中的_bucketsAndMaybeMask成员,获取到当前buckets的首地址。
5.通过哈希 sel & mask获取第一次查找的index
6.buckets + index << 4index << 4 即index乘以16得到当前的index前面的内存的大小)然后buckets移动对应大小内存,到达对应的index 对应的buckets。
7.do-while循环找缓存,这里将从index向前查找 index-->0,然后再通过mask-->index查找流程,当命中内存返回对应的imp,转码进行调用。当找不到,或sel为空,即走慢速查找流程。

未完待续。。。

以上是关于ios底层探索消息发送 cache 查找(下)的主要内容,如果未能解决你的问题,请参考以下文章

iOS底层探索之Runtime: objc_msgSend&汇编快速查找分析

iOS底层探索之类的结构—cache分析(下)

iOS底层开发消息发送与转发流程

iOS底层探索之Runtime:运行时&方法的本质

iOS底层探索之类的结构—cache分析(上)

iOS底层原理类探索之cache分析