_objc_msgSendfunction

_objc_msgSend Assembly code

	ENTRY _objc_msgSend
	UNWIND _objc_msgSend, NoFrame

	cmp	p0, #0			// nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
	b.le	LNilOrTagged		// (MSB tagged pointer looks negative)
#else
	b.eq	LReturnZero
#endif
	ldr	p13, [x0]		// p13 = isa
	GetClassFromIsa_p16 p13, 1, x0	// p16 = class
LGetIsaDone:
	// calls imp or objc_msgSend_uncached
	CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached
Copy the code
  1. cmp p0, #0judgep0– Whether the first parameter is null. If it is null, enter 2; otherwise, enter 3
  2. judgeSUPPORT_TAGGED_POINTERSWhether it istruefortrue, the callLNilOrTagged, otherwise callLReturnZero
  3. willisaPointer stored top13In the
  4. willclassPointer stored top16In, it can be seen that willp13&#ISA_MASKAfter that, it’s savedp16In the.
.macro GetClassFromIsa_p16 src, needs_auth, auth_address
ExtractISA p16, \src, \auth_address
.endmacro

.macro ExtractISA
	and    $0, $1, #ISA_MASK
.endmacro
// View arm64 architecture only, other code omitted
Copy the code
  1. Class found, enterCacheLookUpLookup process

CacheLookUp

Look only at the CACHE_MASK_STORAGE_HIGH_16 environment

.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
	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
	ldr	p11, [x16, #CACHE]			// p11 = mask|buckets
#if CONFIG_USE_PREOPT_CACHES
#if __has_feature(ptrauth_calls)
	tbnz	p11, #0, LLookupPreopt\Function
	and	p10, p11, #0x0000ffffffffffff	// p10 = buckets
#else
	and	p10, p11, #0x0000fffffffffffe	// p10 = buckets
	tbnz	p11, #0, LLookupPreopt\Function
#endif
	eor	p12, p1, p1, LSR #7
	and	p12, p12, p11, LSR #48		// x12 = (_cmd ^ (_cmd >> 7)) & mask
#else
	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
#error Unsupported cache mask storage for ARM64.
#endif

	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;
	cmp	p13, p10			// } while (bucket >= buckets)
	b.hs	1b

#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS
	add	p13, p10, w11, UXTW #(1+PTRSHIFT)
						// p13 = buckets + (mask << 1+PTRSHIFT)
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
	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
	add	p13, p10, p11, LSL #(1+PTRSHIFT)
						// p13 = buckets + (mask << 1+PTRSHIFT)
#else
#error Unsupported cache mask storage for ARM64.
#endif
	add	p12, p10, p12, LSL #(1+PTRSHIFT)
						// 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

LLookupEnd\Function:
LLookupRecover\Function:
	b	\MissLabelDynamic

// Omit some code

.endmacro
Copy the code
  1. ldr p11, [x16, #CACHE].x16namelyclassI’ll shift it by 16cacheEndure top11
#define CACHE            (2 * __SIZEOF_POINTER__)

and	p10, p11, #0x0000fffffffffffe	// p10 = buckets
tbnz	p11, #0, LLookupPreopt\Function
Copy the code

2. Obtain the IP address based on the maskbucketsEndure top10

3._cmdMove 7 bits to the right and xoreorAssigned top12.p11Move 48 bits right to getmaskAnd thenp12andmaskthroughandOperation to get the subscript, and then assign top12And then you get_cmdThe subscriptindex.

eor	p12, p1, p1, LSR #7
and	p12, p12, p11, LSR #48		// x12 = (_cmd ^ (_cmd >> 7)) & mask
Copy the code
  1. PTRSHIFT = 3That will bep12We move 4 to the left, we getindex*16And then addbucketsThe first address gets to the current_cmdThe correspondingbucketAddress, stored inp13.
add    p13, p10, p12, LSL #(1+PTRSHIFT)  // p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
Copy the code
  1. I start the loop,
  • According to thex13Translation is{imp, sel}, deposit top17andp9, thanp9andp1, such as the sameCacheHitDifferent, go to 3.
  • ifp9Is 0, enterMissLabelDynamicOtherwise, subscript conflict exists; Is currently obtainedbucketAddress with the firstbucketAddresses for comparison;
  • ifp13Greater than or equal top10First address, move forward 1, continue to compare the flow, loop lookup.
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;
	cmp	p13, p10			// } while (bucket >= buckets)
	b.hs	1b
Copy the code
  1. If you still can’t find it, locate itbucketsThe last place, save top13
add	p13, p10, p11, LSR #(48 - (1+PTRSHIFT)) // p13 = buckets + (mask << 1+PTRSHIFT)
						// see comment about maskZeroBits
Copy the code

7. Repeat the search process

add	p12, p10, p12, LSL #(1+PTRSHIFT)
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
Copy the code

8. If still not found, enter MissLabelDynamic

CacheHit

In CacheLookup, Mode is passed as NORMAL. TailCallCachedImp is executed. TailCallCachedImp returns IMP

.macro CacheHit
.if $0 == NORMAL
	TailCallCachedImp x17, x10, x1, x16	// authenticate and call imp
.elseif $0 == GETIMP
	mov	p0, p17
	cbz	p0, 9f			// don't ptrauth a nil imp
	AuthAndResignAsIMP x0, x10, x1, x16	// authenticate imp and re-sign as IMP
9:	ret				// return IMP
.elseif $0 == LOOKUP
	// No nil check for ptrauth: the caller would crash anyway when they
	// jump to a nil IMP. We don't care if that jump also fails ptrauth.
	AuthAndResignAsIMP x17, x10, x1, x16	// authenticate imp and re-sign as IMP
	cmp	x16, x15
	cinc	x16, x16, ne			// x16 += 1 when x15 ! = x16 (for instrumentation ; fallback to the parent class)
	ret				// return imp via x17
.else
.abort oops
.endif
.endmacro

.macro TailCallCachedImp
	// $0 = cached imp, $1 = address of cached imp, $2 = SEL, $3 = isa
	eor	$0, $0, $3
	br	$0
.endmacro
Copy the code

MissLabelDynamicnamely__objc_msgSend_uncached

MissLabelDynamic is the second parameter to CacheLookup, __objc_msgSend_uncached, and gets it cached. Finally, the _lookUpImpOrForward method is used to enter the slow lookup process.

        STATIC_ENTRY __objc_msgSend_uncached
	UNWIND __objc_msgSend_uncached, FrameWithNoSaves

	// THIS IS NOT A CALLABLE C FUNCTION
	// Out-of-band p15 is the class to search
	
	MethodTableLookup
	TailCallFunctionPointer x17

	END_ENTRY __objc_msgSend_uncached
Copy the code
.macro MethodTableLookup
	
	SAVE_REGS MSGSEND

	// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
	// receiver and selector already in x0 and x1
	mov	x2, x16
	mov	x3, #3
	bl	_lookUpImpOrForward

	// IMP in x0
	mov	x17, x0

	RESTORE_REGS MSGSEND

.endmacro
Copy the code