/*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 2011 Apple Inc. All Rights Reserved.
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/********************************************************************
*
* objc-msg-arm64.s - ARM64 code to support objc messaging
*
********************************************************************/
#ifdef __arm64__
#include <arm/arch.h>
// _objc_entryPoints and _objc_exitPoints are used by method dispatch
// caching code to figure out whether any threads are actively
// in the cache for dispatching. The labels surround the asm code
// that do cache lookups. The tables are zero-terminated.
.data
.private_extern _objc_entryPoints
_objc_entryPoints:
.quad _cache_getImp
.quad _objc_msgSend
.quad _objc_msgSendSuper
.quad _objc_msgSendSuper2
.quad 0
.data
.private_extern _objc_exitPoints
_objc_exitPoints:
.quad LExit_cache_getImp
.quad LExit_objc_msgSend
.quad LExit_objc_msgSendSuper
.quad LExit_objc_msgSendSuper2
.quad 0
/********************************************************************
* List every exit insn from every messenger for debugger use.
* Format:
* (
* 1 word instruction's address
* 1 word type (ENTER or FAST_EXIT or SLOW_EXIT or NIL_EXIT)
* )
* 1 word zero
*
* ENTER is the start of a dispatcher
* FAST_EXIT is method dispatch
* SLOW_EXIT is uncached method lookup
* NIL_EXIT is returning zero from a message sent to nil
* These must match objc-gdb.h.
********************************************************************/
#define ENTER 1
#define FAST_EXIT 2
#define SLOW_EXIT 3
#define NIL_EXIT 4
.section __DATA,__objc_msg_break
.globl _gdb_objc_messenger_breakpoints
_gdb_objc_messenger_breakpoints:
// contents populated by the macros below
.macro MESSENGER_START
4:
.section __DATA,__objc_msg_break
.quad 4b
.quad ENTER
.text
.endmacro
.macro MESSENGER_END_FAST
4:
.section __DATA,__objc_msg_break
.quad 4b
.quad FAST_EXIT
.text
.endmacro
.macro MESSENGER_END_SLOW
4:
.section __DATA,__objc_msg_break
.quad 4b
.quad SLOW_EXIT
.text
.endmacro
.macro MESSENGER_END_NIL
4:
.section __DATA,__objc_msg_break
.quad 4b
.quad NIL_EXIT
.text
.endmacro
/* objc_super parameter to sendSuper */
#define RECEIVER 0
#define CLASS 8
/* Selected field offsets in class structure */
#define SUPERCLASS 8
#define CACHE 16
/* Selected field offsets in isa field */
#define ISA_MASK 0x00000001fffffff8
/* Selected field offsets in method structure */
#define METHOD_NAME 0
#define METHOD_TYPES 8
#define METHOD_IMP 16
/********************************************************************
* ENTRY functionName
* STATIC_ENTRY functionName
* END_ENTRY functionName
********************************************************************/
.macro ENTRY /* name */
.text
.align 5
.globl $0
$0:
.endmacro
.macro STATIC_ENTRY /*name*/
.text
.align 5
.private_extern $0
$0:
.endmacro
.macro END_ENTRY /* name */
LExit$0:
.endmacro
/********************************************************************
*
* CacheLookup NORMAL|GETIMP
*
* Locate the implementation for a selector in a class method cache.
*
* Takes:
* x1 = selector
* x9 = class to be searched
*
* Kills:
* x10,x11,x12, x16,x17
*
* On exit: (found) exits CacheLookup
* with x9 = class, x17 = IMP
* (not found) jumps to LCacheMiss
*
********************************************************************/
#define NORMAL 0
#define GETIMP 1
.macro CacheHit
MESSENGER_END_FAST
.if $0 == NORMAL
br x17 // call imp
.else
b LGetImpHit
.endif
.endmacro
.macro CheckMiss
.if $0 == NORMAL // miss if bucket->cls == 0
cbz x16, __objc_msgSend_uncached_impcache
.else
cbz x16, LGetImpMiss
.endif
.endmacro
.macro CacheLookup
// x1 = SEL, x9 = isa
ldp x10, x11, [x9, #CACHE] // x10 = buckets, x11 = occupied|mask
and w12, w1, w11 // x12 = _cmd & mask
add x12, x10, x12, LSL #4 // x12 = buckets + ((_cmd & mask)<<4)
ldp x16, x17, [x12] // {x16, x17} = *bucket
1: cmp x16, x1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp
2: // not hit: x12 = not-hit bucket
CheckMiss $0 // miss if bucket->cls == 0
cmp x12, x10 // wrap if bucket == buckets
b.eq 3f
ldp x16, x17, [x12, #-16]! // {x16, x17} = *--bucket
b 1b // loop
3: // wrap: x12 = first bucket, w11 = mask
add x12, x12, w11, UXTW #4 // x12 = buckets+(mask<<4)
// clone scanning loop to crash instead of hang when cache is corrupt
ldp x16, x17, [x12] // {x16, x17} = *bucket
1: cmp x16, x1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp
2: // not hit: x12 = not-hit bucket
CheckMiss $0 // miss if bucket->cls == 0
cmp x12, x10 // wrap if bucket == buckets
b.eq 3f
ldp x16, x17, [x12, #-16]! // {x16, x17} = *--bucket
b 1b // loop
3: // double wrap - busted
// x0 = receiver
// x1 = SEL
mov x2, x9 // x2 = isa
.if $0 == GETIMP
mov x0, #0
b _cache_getImp_corrupt_cache_error
.else
b _objc_msgSend_corrupt_cache_error
.endif
.endmacro
.data
.align 3
.globl _objc_debug_taggedpointer_classes
_objc_debug_taggedpointer_classes:
.fill 16, 8, 0
ENTRY _objc_msgSend
MESSENGER_START
cmp x0, #0 // nil check and tagged pointer check
b.le LNilOrTagged // (MSB tagged pointer looks negative)
ldr x13, [x0] // x13 = isa
and x9, x13, #ISA_MASK // x9 = class
LGetIsaDone:
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
LNilOrTagged:
b.eq LReturnZero // nil check
// tagged
adrp x10, _objc_debug_taggedpointer_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
ubfx x11, x0, #60, #4
ldr x9, [x10, x11, LSL #3]
b LGetIsaDone
LReturnZero:
// x0 is already zero
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
MESSENGER_END_NIL
ret
END_ENTRY _objc_msgSend
ENTRY _objc_msgSendSuper
MESSENGER_START
ldr x9, [x0, #CLASS] // load class to search
ldr x0, [x0, #RECEIVER] // load real receiver
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
END_ENTRY _objc_msgSendSuper
ENTRY _objc_msgSendSuper2
MESSENGER_START
ldr x9, [x0, #CLASS]
ldr x9, [x9, #SUPERCLASS] // load class to search
ldr x0, [x0, #RECEIVER] // load real receiver
CacheLookup NORMAL
END_ENTRY _objc_msgSendSuper2
ENTRY _objc_msgSend_noarg
b _objc_msgSend
END_ENTRY _objc_msgSend_noarg
STATIC_ENTRY __objc_msgSend_uncached_impcache
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band x9 is the class to search
MESSENGER_START
// push frame
stp fp, lr, [sp, #-16]!
mov fp, sp
MESSENGER_END_SLOW
// save parameter registers: x0..x8, q0..q7
sub sp, sp, #(10*8 + 8*16)
stp q0, q1, [sp, #(0*16)]
stp q2, q3, [sp, #(2*16)]
stp q4, q5, [sp, #(4*16)]
stp q6, q7, [sp, #(6*16)]
stp x0, x1, [sp, #(8*16+0*8)]
stp x2, x3, [sp, #(8*16+2*8)]
stp x4, x5, [sp, #(8*16+4*8)]
stp x6, x7, [sp, #(8*16+6*8)]
str x8, [sp, #(8*16+8*8)]
// receiver and selector already in x0 and x1
mov x2, x9
bl __class_lookupMethodAndLoadCache3
// imp in x0
mov x17, x0
// restore registers and return
ldp q0, q1, [sp, #(0*16)]
ldp q2, q3, [sp, #(2*16)]
ldp q4, q5, [sp, #(4*16)]
ldp q6, q7, [sp, #(6*16)]
ldp x0, x1, [sp, #(8*16+0*8)]
ldp x2, x3, [sp, #(8*16+2*8)]
ldp x4, x5, [sp, #(8*16+4*8)]
ldp x6, x7, [sp, #(8*16+6*8)]
ldr x8, [sp, #(8*16+8*8)]
mov sp, fp
ldp fp, lr, [sp], #16
br x17
END_ENTRY __objc_msgSend_uncached_impcache
.section __LD,__compact_unwind,regular,debug
.quad _objc_msgSend
.set LUnwind_objc_msgSend, LExit_objc_msgSend-_objc_msgSend
.long LUnwind_objc_msgSend
.long 0x02000000 // no frame, no SP adjustment
.quad 0 // no personality
.quad 0 // no LSDA
.section __LD,__compact_unwind,regular,debug
.quad _objc_msgSendSuper
.set LUnwind_objc_msgSendSuper, LExit_objc_msgSendSuper-_objc_msgSendSuper
.long LUnwind_objc_msgSendSuper
.long 0x02000000 // no frame, no SP adjustment
.quad 0 // no personality
.quad 0 // no LSDA
.section __LD,__compact_unwind,regular,debug
.quad _objc_msgSendSuper2
.set LUnwind_objc_msgSendSuper2, LExit_objc_msgSendSuper2-_objc_msgSendSuper2
.long LUnwind_objc_msgSendSuper2
.long 0x02000000 // no frame, no SP adjustment
.quad 0 // no personality
.quad 0 // no LSDA
.section __LD,__compact_unwind,regular,debug
.quad __objc_msgSend_uncached_impcache
.set LUnwind__objc_msgSend_uncached_impcache, LExit__objc_msgSend_uncached_impcache-__objc_msgSend_uncached_impcache
.long LUnwind__objc_msgSend_uncached_impcache
.long 0x04000000 // frame, no non-volatile registers saved
.quad 0 // no personality
.quad 0 // no LSDA
STATIC_ENTRY _cache_getImp
and x9, x0, #ISA_MASK
CacheLookup GETIMP
LGetImpHit:
// imp in x17
// don't return msgSend_uncached
adrp x16, __objc_msgSend_uncached_impcache@PAGE
add x16, x16, __objc_msgSend_uncached_impcache@PAGEOFF
cmp x16, x17
csel x0, x17, xzr, ne // if imp!=uncached then imp else 0
ret
LGetImpMiss:
mov x0, #0
ret
END_ENTRY _cache_getImp
/********************************************************************
*
* id _objc_msgForward(id self, SEL _cmd,...)* _objc_msgForward is the externally-callable
* function returned by things like method_getImplementation().
* _objc_msgForward_impcache is the function pointer actually stored in
* method caches.
*
********************************************************************/
STATIC_ENTRY __objc_msgForward_impcache
MESSENGER_START
nop
MESSENGER_END_SLOW
// No stret specialization.
b __objc_msgForward
END_ENTRY __objc_msgForward_impcache
ENTRY __objc_msgForward
adrp x17, __objc_forward_handler@PAGE
ldr x17, [x17, __objc_forward_handler@PAGEOFF]
br x17
END_ENTRY __objc_msgForward
ENTRY _objc_msgSend_debug
b _objc_msgSend
END_ENTRY _objc_msgSend_debug
ENTRY _objc_msgSendSuper2_debug
b _objc_msgSendSuper2
END_ENTRY _objc_msgSendSuper2_debug
ENTRY _method_invoke
// x1 is method triplet instead of SEL
ldr x17, [x1, #METHOD_IMP]
ldr x1, [x1, #METHOD_NAME]
br x17
END_ENTRY _method_invoke
STATIC_ENTRY __objc_ignored_method
// self is already in x0
ret
END_ENTRY __objc_ignored_method
#endif