先记录一部分笔记,剩下的继续学习补充
// movb(8位)、movw(16位)、movl(32位)、movq(64位)
// 最后的b,w,l,q 是区分不同的位数
cache_getImp
- a1 = class whose cache is to be searched a1 是class
- a2 = selector to search for a2 是sel
/********************************************************************
* IMP cache_getImp(Class cls, SEL sel)
*
* On entry: a1 = class whose cache is to be searched
* a2 = selector to search for
*
* If found, returns method implementation.
* If not found, returns NULL.
********************************************************************/
STATIC_ENTRY _cache_getImp
// do lookup
// 将class 复制给 r10 位 CacheLookup 做准备
movq %a1, %r10 // move class to r10 for CacheLookup
// returns IMP on success
CacheLookup NORMAL, GETIMP, _cache_getImp
// _cache_getImp 查找缓存是没有找到的跳转
LCacheMiss_cache_getImp:
// cache miss, return nil
xorl %eax, %eax
ret
END_ENTRY _cache_getImp
CacheLookup
调用方式: CacheLookup NORMAL, GETIMP, _cache_getImp
所以 1 就是GETIMP $2就是 _cache_getImp
第一个参数 normal 或者 Strat 表示 isa 和cm的参数的位置不同
第二个参数 GETIMP : 返回计算后的img , CALL 直接调用imp。 LOOKUP 将计算得到的imp 存入r11
第三个参数: 这是一个标记, 比如cachemiss 是跳转到哪里
- r11 64-bit register
- r11d 是Lower 32 bits
.macro CacheLookup
//
// Restart protocol:
//
// As soon as we're past the LLookupStart$1 label we may have loaded
// an invalid cache pointer or mask.
//
// When task_restartable_ranges_synchronize() is called,
// (or when a signal hits us) before we're past LLookupEnd$1,
// then our PC will be reset to LCacheMiss$1 which forcefully
// jumps to the cache-miss codepath which have the following
// requirements:
//
// GETIMP:
// The cache-miss is just returning NULL (setting %rax to 0)
//
// NORMAL and STRET:
// - a1 or a2 (STRET) contains the receiver class
// - a2 or a3 (STRET) contains the selector
// - r10 contains the isa
// - other registers are set as per calling conventions
//
LLookupStart$2:
// 将sel 复制给 r11
.if $0 != STRET
movq %a2, %r11 // r11 = _cmd
.else
movq %a3, %r11 // r11 = _cmd
.endif
// r11d -> r11去8位数据 24(%r10) r10 isa的指针 偏移 24个字节(isa 8, superclass 8 , class->cache.buckets 8) class->cache.mask
// 根据 cmd 和 _cmd & class->cache.mask 计算hashkey 并复制给r11
andl 24(%r10), %r11d // r11 = _cmd & class->cache.mask
// $$4 -> 4 这是根据hashkey << 4 计算hashkey个 16位/2字节 bucket是2个字节大小(sel 8, imp 8)
// 根据hashkey 之后计算 hashkey对应的偏移量
shlq $$4, %r11 // r11 = offset = (_cmd & mask)<<4
// 类似 24(%r10) 16(%r10) 就是 class->cache.bucket
// 根据偏移量,找到对应hashkey的 bucket地址
addq 16(%r10), %r11 // r11 = class->cache.buckets + offset
// 判断当权位置是否等于sel hash冲突判断
.if $0 != STRET
cmpq cached_sel(%r11), %a2 // if (bucket->sel != _cmd)
.else
cmpq cached_sel(%r11), %a3 // if (bucket->sel != _cmd)
.endif
// 如果不想等 则去下一个查找 (根据cache的存储逻辑)
jne 1f // scan more
// 相等 跳转CacheHit $0 , $1 ; $0,$1 是传入的参数
CacheHit $0, $1 // call or return imp
// 检查是否已经到了最后一位/或者下一位是空的。就返回查不到
// 否则, 计算下一个bucket的地址
1:
// loop
// r11 是刚才计算到hashkey对应的 bucket
// cache_sel 位0 => 0(%r11) 就是 bucket.sel
cmpq $$1, cached_sel(%r11)
// if (bucket->sel <= 1) 就是说查不到, 跳转到退出
jbe 3f // if (bucket->sel <= 1) wrap or miss
// 计算下一个入职的bucket地址 复制给r11
addq $$16, %r11 // bucket++
//
2:
// 比较 cmd 和当前 bucket->sel 是否相同
.if $0 != STRET
cmpq cached_sel(%r11), %a2 // if (bucket->sel != _cmd)
.else
cmpq cached_sel(%r11), %a3 // if (bucket->sel != _cmd)
.endif
// 不相同 hashkey冲突,继续乡下解决
jne 1b // scan more
// 相同,则就是跳转cachehit
CacheHit $0, $1 // call or return imp
// 查找下一个的bucket出现:
// 空的存储 -> 跳转cacheMiss
// end标志 -> end标志的imp (第一个bucket的地址)复制给r11 ,继续查找
3:
// 根据上面的判断,cmpq $$1, cached_sel(%r11) 再判断
// 如果是小于, 这属于 下一个是空的,找不到了
// wrap or miss
jb LCacheMiss$2 // if (bucket->sel < 1) cache miss
// 否则 下一个 bucket->sel == 1 表示到了最后的结尾,其结尾的imp 存的是开始的bucket地址
// 将下一个bucket设置位 第一个bucket 的地址,再次查询比较
movq cached_imp(%r11), %r11 // bucket->imp is really first bucket
jmp 2f
// Clone scanning loop to miss instead of hang when cache is corrupt.
// The slow path may detect any corruption and halt later.
1:
// loop
cmpq $$1, cached_sel(%r11)
jbe 3f // if (bucket->sel <= 1) wrap or miss
addq $$16, %r11 // bucket++
2:
.if $0 != STRET
cmpq cached_sel(%r11), %a2 // if (bucket->sel != _cmd)
.else
cmpq cached_sel(%r11), %a3 // if (bucket->sel != _cmd)
.endif
jne 1b // scan more
CacheHit $0, $1 // call or return imp
3:
// double wrap or miss
jmp LCacheMiss$2
LLookupEnd$2:
.endmacro
CacheHit
就是找到了imp。然后根据类型返回或者执行.其中r11 是在之前CacheLookup中找到的 bucket的地址
- CALL : 执行IMP,并返回imp 放在r11
- LOOKUP: 返回IMP ^ isa
.macro CacheHit
// r11 是在
.if $1 == GETIMP
// r11 是bucket r10 isa
// #define cached_imp 8 cached_imp(%r11)就是bucket的imp地址
// %rax
% rax 作为函数返回值使用。
movq cached_imp(%r11), %rax // return imp
// 判断imp 是否为0
cmpq $$0, %rax // $$0 就是直接取值 0 就是判断imp 是否为nil
// 为0 直接返回
jz 9f // don't xor a nil imp
// 如果rmp 不为nil ,则按照原先存的的逻辑,, 在xor 计算回来。参考cache_t存储imp的存储逻辑
// r10 是isa
xorq %r10, %rax // xor the isa with the imp
9: ret
.else
.if $1 == CALL
movq cached_imp(%r11), %r11 // load imp
xorq %r10, %r11 // xor imp and isa
.if $0 != STRET
// ne already set for forwarding by `xor`
.else
cmp %r11, %r11 // set eq for stret forwarding
.endif
// 跳转到imp的地址 去执行imp
jmp *%r11 // call imp
.elseif $1 == LOOKUP
movq cached_imp(%r11), %r11
xorq %r10, %r11
ret
.else
.abort oops
.endif
.endif
.endmacro
GetIsaFast
调用: GetIsaFast NORMAL
根据isa的 nonpointer 位的标示,获取正确的isa地址
将isa的地址 保存到 r10上
- a1 or a2 (STRET) contains the receiver
- a2 or a3 (STRET) contains the selector
MethodTableLookup
去执行 lookUpImpOrForward 方法 。能查到方法,放回方法。
没有查到方法,返回的是_objc_msgForward_impcache 消息转发流程
// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
// 参数的赋值 a1 obj, a2 sel a3 cls a4 behavior
if $0 == NORMAL
// receiver already in a1
// selector already in a2
.else
movq %a2, %a1
movq %a3, %a2
.endif
movq %r10, %a3
movl $$3, %a4d
call _lookUpImpOrForward
其他的是传参数
__objc_msgSend_uncached
STATIC_ENTRY __objc_msgSend_uncached
UNWIND __objc_msgSend_uncached, FrameWithNoSaves
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band r10 is the searched class
// r10 is already the class to search
MethodTableLookup NORMAL // r11 = IMP
jmp *%r11 // goto *imp
END_ENTRY __objc_msgSend_uncached
_objc_msgLookup
先去缓存查找 CacheLookup NORMAL, LOOKUP, _objc_msgLookup
- 缓存中找到imp,放入r11 然后判断是否是null 再将isa 放入r10
- 找不到跳转 LCacheMiss_objc_msgLookup
cachemiss_objc_msgLookup 再去方法列表中查找 -> __objc_msgLookup_uncached -> MethodTableLookup
ENTRY _objc_msgLookup
// #define NORMAL 0
NilTest NORMAL
// 就是获取对应的isa的值 (如果是优化过的,需要使用ISA_MASK去获取)然后赋值到 r10上
GetIsaFast NORMAL // r10 = self->isa
// returns IMP on success
// 去缓存查找
CacheLookup NORMAL, LOOKUP, _objc_msgLookup
// returns an IMP in r11 that returns zero.
NilTestReturnIMP NORMAL
// 再将isa 放入r10
GetIsaSupport NORMAL
// cache miss: go search the method lists
LCacheMiss_objc_msgLookup:
// isa still in r10
jmp __objc_msgLookup_uncached
END_ENTRY _objc_msgLookup
_objc_msgSend
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
NilTest NORMAL
//对象/类 isa数据根据 nonpointer位,获取其class的地址 / mataclass 的地址
GetIsaFast NORMAL // r10 = self->isa
// calls IMP on success
// 去cache中查找 ,如果找到直接执行imp,并放回imp, (参考 CacheHit NORMAL CALL)
// 如果没有查找到imp, 就会 跳转到 LCacheMiss_objc_msgSend ($2 _objc_msgSend)
CacheLookup NORMAL, CALL, _objc_msgSend
// 判断返回是否是nil
NilTestReturnZero NORMAL
GetIsaSupport NORMAL
// cache miss: go search the method lists
//
LCacheMiss_objc_msgSend:
// isa still in r10
// __objc_msgSend_uncached 就回去执行MethodTableLookup
// 然后执行 lookUpImpOrForward 就是去查方法列表,
// 然后执行返回的方法
jmp __objc_msgSend_uncached
END_ENTRY _objc_msgSend
总结
msg_send.jpg重点:
- 先查找缓存,找到直接执行
- cache_t 的方法存储逻辑,以及查找逻辑
- cacheHit 根据参数 $1 参数 处理(CALL 直接执行, GET_IMP 判断是否nil,返回IMP,LOOKUP (不判断nil, r11 存储IMP))。
- LCacheMiss_objc_msgSend 去类的方法列表中去查,执行 _lookUpImpOrForward, behavior 设为LOOKUP_INITIALIZE | LOOKUP_RESOLVER
网友评论