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 judgerecevierIf yes, check whether the small end is supported. If no, return null.
  • Small end down, check if it’s negative, negative goLNilOrTaggedMethods.
  • Enter theLNilOrTaggedThen, 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
  • recevierIf not, get isa and passGetClassFromIsa_p16Get 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 offsetshiftClassInformation, and assign the shiftClass value to p16isaThe 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 unitscache_tAssigned top11.
  • You get it by wiping out the top zerobucketsAssigned top10.
  • rightcache_tOffset to get_bucketsAndMaybeMaskBy 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
  • performdoWhileCycle, from_cmd & maskgetindexBegan to takebucket. Determine if there are any equalsel=cmdIf so, find it and returnimp.
  • If you can’t find it, jump out and do a slow search __objc_msgSend_uncached
  • If the first onebucketbucketsThe first element of thebucketSet to the last element for the second loop (bucket > first_probed)
  • ifbucketThe 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 loopAs 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

  1. Gets the recipient of the current message and makes a judgmentIf there is a, does not support the return of the small – end mode. If yes, obtain the small – end modeisa
  2. After obtaining isa, the memory was shifted by 16 units*cache_tAnd getbuckets, according to the_cmdandmaskTo get the hash algorithmindex.
  3. According to theindextakebucketsIn thebucket.bucketIn theseland_cmdCompare. If it’s equal, you find it. If it’s not equal, you recurse*buckets-- Look up untilThe first element is buckets.
  4. If not, proceedQuadratic recursive, locate thebucketstheThe last oneElement, extractbucket, and proceed to recursive lookup. If the currentBukect and p12(select bucket from _cmd and mask)We return if the equality is not foundMissLabelDynamicGo slow

Here’s a rough flow chart