1. Objc_msgSend Quick search
Objc_msgSend assembler source code:
#endif
ENTRY _objc_msgSend // The method is passed in with the recipient and the method name
UNWIND _objc_msgSend, NoFrame
cmp p0, #0// Assign the receiver to register P0 and determine whether the receiver is 0
// If it is 0, do the following
#if SUPPORT_TAGGED_POINTERS// Whether target_pointers are supported
b.le LNilOrTagged// Go to the LNilOrTagged: method
#else
b.eq LReturnZero // go to the LReturnZero: method below, and send an empty method
#endif
ldr p13, [x0] // Assign the recipient's first address isa to p13, p13 = isa
GetClassFromIsa_p16 p13, 1, x0 // Get the class from this method and assign it to p16 = class, passing in p13, the receiver's ISA, 1, and the receiver.
LGetIsaDone:
// calls imp or objc_msgSend_uncached
CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached// Cache lookup for isa
#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
b.eq LReturnZero // nil check checks if the object is 0 and sends an empty method if 0
GetTaggedClass // Get the class of the little endian object
b LGetIsaDone/ / get the isa
// SUPPORT_TAGGED_POINTERS
#endif
LReturnZero:
// x0 is already zero
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
ret
END_ENTRY _objc_msgSend/ / end
Copy the code
- In the first place to judge
recevier
If yes, check whether the small end is supported. If no, return null. - Small end down, check if it’s negative, negative go
LNilOrTagged
Methods. - Enter the
LNilOrTagged
Then, judge whether the little endian is empty. If it is empty, return empty; if it is not empty, enter the processing of the little endian object and obtainisa
recevier
If not, get isa and passGetClassFromIsa_p16
Get information about the class. In the ARM64 architecture, passisa
& isa_mask Is obtained by memory offsetshiftClass
, get the class information and assign it to P16;
1.1 GetClassFromIsa_p16
.macro GetClassFromIsa_p16 src, needs_auth, auth_address /* note: auth_address is not required if ! needs_auth */
#if SUPPORT_INDEXED_ISA
// Indexed isa
mov p16, \src"// save isa's value in register P16
tbz p16, #ISA_INDEX_IS_NPI_BIT, 1f // Check whether it is pure ISA. done if not non-pointer isa
// Isa in P16 is indexed
adrp x10, _objc_indexed_classes@PAGE// store the _objc_indexed_classes index in x10
add x10, x10, _objc_indexed_classes@PAGEOFF // x10 = x10+ _objc_indexed_classes@PAGE Obtain Offset The memory is offset based on x10
ubfx p16, p16, #ISA_INDEX_SHIFT, #ISA_INDEX_BITS // Extract shiftClass from P16's ISA_INDEX_SHIFT to ISA_INDEX_BITS
ldr p16, [x10, p16, UXTP #PTRSHIFT] // load class from array
1:
#elif __LP64__ / / 64 - bit systems
.if \needs_auth == 0 // _cache_getImp takes an authed class already
mov p16, \src // Whether the isa of the class has been obtained, which is directly assigned to the class
.else
// 64-bit packed isa
ExtractISA p16, \src, \auth_address//p16 = isa &isa_mask (shiftClass)
.endif
#else
// 32-bit raw isaMov p16, SRC directly assign p16 = isa #endifCopy the code
- This parameter is obtained mainly through isa memory offset
shiftClass
Information, and assign the shiftClass value to p16isa
The search is complete.
1.2 CacheLookup lookup
.macro CacheLookup Mode, Function, MissLabelDynamic, MissLabelConstant
mov x15, x16 // stash the original isa
LLookupStart\Function:
// p1 = SEL, p16 = isa
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS // Check whether it is a big-endian emulator
ldr p10, [x16, #CACHE]// p10 = mask|buckets
lsr p11, p10, #48 // p11 = mask
and p10, p10, #0xffffffffffff // p10 = buckets
and w12, w1, w11 // x12 = _cmd & mask
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16 // Check whether the real machine is in small - end mode
ldr p11, [x16, #CACHE]/ / p11 = mask | buckets to isa first address offset 16 bytes, before we know the memory address of isa - superclass - cache this row, access to the cache value to first address the isa translational 16 bytes can get the address of the cache.
#if CONFIG_USE_PREOPT_CACHES / / arm64 real machine
#if __has_feature(ptrauth_calls) / / A12 chips
tbnz p11, #0, LLookupPreopt\Function //p11 tests the bit to determine whether it is 0. If 0, go to LLookupPreopt\Function
and p10, p11, #0x0000ffffffffffff Cache_t & 0x0000FFFFFFFFFF Assigns buckets to P10, where p10 = buckets
#else / / A12
and p10, p11, #0x0000fffffffffffe // p10 = buckets
tbnz p11, #0, LLookupPreopt\Function
#endif
eor p12, p1, p1, LSR #7 P12 = p1^(p1>>7) = _cmd^(_cmd>>7)
and p12, p12, p11, LSR #48//p11>>48 = _bucketsAndMask >>48 get mask p12 = (_cmd ^ (_cmd >> 7)) & mask get hash index, x12 = (_cmd ^ (_cmd >> 7)) & mask
#elseArm64 and P10, P11, #0x0000ffffffffffff // p10 = buckets
and p12, p1, p11, LSR #48 // x12 = _cmd & mask
#endif // CONFIG_USE_PREOPT_CACHES
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
ldr p11, [x16, #CACHE] // p11 = mask|buckets
and p10, p11, #~0xf // p10 = buckets
and p11, p11, #0xf // p11 = maskShift
mov p12, #0xffff
lsr p11, p12, p11 // p11 = mask = 0xffff >> p11
and p12, p1, p11 // x12 = _cmd & mask
#else // It is not supported
#error Unsupported cache mask storage for ARM64.
#endif
Copy the code
- First offsets the class isa by 16 units
cache_t
Assigned top11
. - You get it by wiping out the top zero
buckets
Assigned top10
. - right
cache_t
Offset to get_bucketsAndMaybeMask
By translation, index is obtained under the hash algorithm
// p10 = buckets start address, p12 = index,PTRSHIFT = 3 buceet = sel (8 bytes) + IMP (8 bytes) = 16 bytes
add p13, p10, p12, LSL #(1+PTRSHIFT)
// p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
// do {
1: ldp p17, p9, [x13], #-BUCKET_SIZE {imp, sel} = *bucket--
cmp p9, p1 // if (sel! = _cmd) {
b.ne 3f // Scan more
// } else {
2: CacheHit \Mode Hit: call or return IMP
/ /}
3: cbz p9, \MissLabelDynamic If (sel == 0) goto Miss; if (sel == 0) goto Miss;
cmp p13, p10 // if (bucket >= buckets)
b.hs 1b
Copy the code
- perform
doWhile
Cycle, from_cmd & mask
getindex
Began to takebucket
. Determine if there are any equalsel=cmd
If so, find it and returnimp
. - If you can’t find it, jump out and do a slow search
__objc_msgSend_uncached
- If the first one
bucket
是buckets
The first element of thebucket
Set to the last element for the second loop (bucket > first_probed
) - if
bucket
The first person who was not Buckets continued his search
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS
add p13, p10, w11, UXTW #(1+PTRSHIFT) // On a non-real machine
// p13 = buckets + (mask << 1+PTRSHIFT)
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
add p13, p10, p11, LSR #(48 - (1+PTRSHIFT))/ / under the real machine
// p13 = buckets + (mask << 1+PTRSHIFT)
// see comment about maskZeroBits
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
add p13, p10, p11, LSL #(1+PTRSHIFT)
// p13 = buckets + (mask << 1+PTRSHIFT)
#else/ / error
#error Unsupported cache mask storage for ARM64.
#endif
add p12, p10, p12, LSL #(1+PTRSHIFT)//p10 = buckets,p12
// p12 = first probed bucket
// do {
4: ldp p17, p9, [x13], #-BUCKET_SIZE // {imp, sel} = *bucket--
cmp p9, p1 // if (sel == _cmd)
b.eq 2b // goto hit
cmp p9, #0 // } while (sel ! = 0 &&
ccmp p13, p12, #0, ne // bucket > first_probed)
b.hi 4b // If the current bukect is equal to the p12(_cmd and mask), the bucket is directly connected to miss
LLookupEnd\Function:
LLookupRecover\Function:
b \MissLabelDynamic
Copy the code
The secondary loop
As with the dowhile loop above, it is worth noting that if the current first bukect is equal to the bucket from the p12(calculated from _cmd and mask), it returns directlyMissLabelDynamic
2. To summarize
- Gets the recipient of the current message and makes a judgment
If there is a
, does not support the return of the small – end mode. If yes, obtain the small – end modeisa
- After obtaining isa, the memory was shifted by 16 units
*cache_t
And getbuckets
, according to the_cmd
andmask
To get the hash algorithmindex
. - According to the
index
takebuckets
In thebucket
.bucket
In thesel
and_cmd
Compare. If it’s equal, you find it. If it’s not equal, you recurse*buckets--
Look up untilThe first element is buckets.
- If not, proceed
Quadratic recursive
, locate thebuckets
theThe last one
Element, extractbucket
, and proceed to recursive lookup. If the currentBukect and p12(select bucket from _cmd and mask)
We return if the equality is not foundMissLabelDynamic
Go slow
Here’s a rough flow chart