/*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 1999-2007 Apple Computer, 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-arm.s - ARM code to support objc messaging
*
********************************************************************/
#ifdef __arm__
#include <arm/arch.h>
#ifndef _ARM_ARCH_7
# error requires armv7
#endif
.syntax unified
#define MI_EXTERN(var) \
.non_lazy_symbol_pointer .indirect_symbol var
#define MI_GET_EXTERN(reg,var) \
movw reg, :lower16:(L##var##$$non_lazy_ptr-4f-4) 4: add reg, pc
#define MI_CALL_EXTERNAL(var) \
MI_GET_EXTERN(r12,var)
#define MI_GET_ADDRESS(reg,var) \
movw reg, :lower16:(var-4f-4) 4: add reg, pc
MI_EXTERN(__class_lookupMethodAndLoadCache3)
MI_EXTERN(___objc_error)
// _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:
.long _cache_getImp
.long _objc_msgSend
.long _objc_msgSend_stret
.long _objc_msgSendSuper
.long _objc_msgSendSuper_stret
.long _objc_msgSendSuper2
.long _objc_msgSendSuper2_stret
.long 0
.data
.private_extern _objc_exitPoints
_objc_exitPoints:
.long LGetImpExit
.long LMsgSendExit
.long LMsgSendStretExit
.long LMsgSendSuperExit
.long LMsgSendSuperStretExit
.long LMsgSendSuper2Exit
.long LMsgSendSuper2StretExit
.long 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
.long 4b
.long ENTER
.text
.endmacro
.macro MESSENGER_END_FAST
4:
.section __DATA,__objc_msg_break
.long 4b
.long FAST_EXIT
.text
.endmacro
.macro MESSENGER_END_SLOW
4:
.section __DATA,__objc_msg_break
.long 4b
.long SLOW_EXIT
.text
.endmacro
.macro MESSENGER_END_NIL
4:
.section __DATA,__objc_msg_break
.long 4b
.long NIL_EXIT
.text
.endmacro
/********************************************************************
* Names for relative labels
* DO NOT USE THESE LABELS ELSEWHERE
* Reserved labels: 8: 9:
********************************************************************/
#define LCacheMiss 8
#define LCacheMiss_f 8f
#define LCacheMiss_b 8b
#define LNilReceiver 9
#define LNilReceiver_f 9f
#define LNilReceiver_b 9b
/********************************************************************
* Macro parameters
********************************************************************/
#define NORMAL 0
#define FPRET 1
#define FP2RET 2
#define GETIMP 3
#define STRET 4
#define SUPER 5
#define SUPER2 6
#define SUPER_STRET 7
#define SUPER2_STRET 8
/********************************************************************
*
* Structure definitions.
*
********************************************************************/
/* objc_super parameter to sendSuper */
#define RECEIVER 0
#define CLASS 4
/* Selected field offsets in class structure */
#define ISA 0
#define SUPERCLASS 4
#define CACHE 8
#define CACHE_MASK 12
/* Selected field offsets in method structure */
#define METHOD_NAME 0
#define METHOD_TYPES 4
#define METHOD_IMP 8
//////////////////////////////////////////////////////////////////////
//
// ENTRY functionName
//
// Assembly directives to begin an exported function.
//
// Takes: functionName - name of the exported function
//////////////////////////////////////////////////////////////////////
.macro ENTRY /* name */
.text
.thumb
.align 5
.globl _$0
.thumb_func
_$0:
.endmacro
.macro STATIC_ENTRY /*name*/
.text
.thumb
.align 5
.private_extern _$0
.thumb_func
_$0:
.endmacro
//////////////////////////////////////////////////////////////////////
//
// END_ENTRY functionName
//
// Assembly directives to end an exported function. Just a placeholder,
// a close-parenthesis for ENTRY, until it is needed for something.
//
// Takes: functionName - name of the exported function
//////////////////////////////////////////////////////////////////////
.macro END_ENTRY /* name */
.endmacro
/////////////////////////////////////////////////////////////////////
//
// CacheLookup return-type
//
// Locate the implementation for a selector in a class's method cache.
//
// Takes:
// $0 = NORMAL, STRET, SUPER, SUPER_STRET, SUPER2, SUPER2_STRET, GETIMP
// r0 or r1 (STRET) = receiver
// r1 or r2 (STRET) = selector
// r9 = class to search in
//
// On exit: r9 and r12 clobbered
// (found) calls or returns IMP, eq/ne/r9 set for forwarding
// (not found) jumps to LCacheMiss
//
/////////////////////////////////////////////////////////////////////
.macro CacheHit
.if $0 == GETIMP
ldr r0, [r9, #4] // r0 = bucket->imp
MI_GET_ADDRESS(r1, __objc_msgSend_uncached_impcache)
teq r0, r1
it eq
moveq r0, #0 // don't return msgSend_uncached
bx lr // return imp
.elseif $0 == NORMAL
ldr r12, [r9, #4] // r12 = bucket->imp
// eq already set for nonstret forward
MESSENGER_END_FAST
bx r12 // call imp
.elseif $0 == STRET
ldr r12, [r9, #4] // r12 = bucket->imp
movs r9, #1 // r9=1, Z=0 for stret forwarding
MESSENGER_END_FAST
bx r12 // call imp
.elseif $0 == SUPER
ldr r12, [r9, #4] // r12 = bucket->imp
ldr r9, [r0, #CLASS] // r9 = class to search for forwarding
ldr r0, [r0, #RECEIVER] // fetch real receiver
tst r12, r12 // set ne for forwarding (r12!=0)
MESSENGER_END_FAST
bx r12 // call imp
.elseif $0 == SUPER2
ldr r12, [r9, #4] // r12 = bucket->imp
ldr r9, [r0, #CLASS]
ldr r9, [r9, #SUPERCLASS] // r9 = class to search for forwarding
ldr r0, [r0, #RECEIVER] // fetch real receiver
tst r12, r12 // set ne for forwarding (r12!=0)
MESSENGER_END_FAST
bx r12 // call imp
.elseif $0 == SUPER_STRET
ldr r12, [r9, #4] // r12 = bucket->imp
ldr r9, [r1, #CLASS] // r9 = class to search for forwarding
orr r9, r9, #1 // r9 = class|1 for super_stret forward
ldr r1, [r1, #RECEIVER] // fetch real receiver
tst r12, r12 // set ne for forwarding (r12!=0)
MESSENGER_END_FAST
bx r12 // call imp
.elseif $0 == SUPER2_STRET
ldr r12, [r9, #4] // r12 = bucket->imp
ldr r9, [r1, #CLASS] // r9 = class to search for forwarding
ldr r9, [r9, #SUPERCLASS] // r9 = class to search for forwarding
orr r9, r9, #1 // r9 = class|1 for super_stret forward
ldr r1, [r1, #RECEIVER] // fetch real receiver
tst r12, r12 // set ne for forwarding (r12!=0)
MESSENGER_END_FAST
bx r12 // call imp
.else
.abort oops
.endif
.endmacro
.macro CacheLookup
ldrh r12, [r9, #CACHE_MASK] // r12 = mask
ldr r9, [r9, #CACHE] // r9 = buckets
.if $0 == STRET || $0 == SUPER_STRET
and r12, r12, r2 // r12 = index = SEL & mask
.else
and r12, r12, r1 // r12 = index = SEL & mask
.endif
add r9, r9, r12, LSL #3 // r9 = bucket = buckets+index*8
ldr r12, [r9] // r12 = bucket->sel
2:
.if $0 == STRET || $0 == SUPER_STRET
teq r12, r2
.else
teq r12, r1
.endif
bne 1f
CacheHit $0
1:
cmp r12, #1
blo LCacheMiss_f // if (bucket->sel == 0) cache miss
it eq // if (bucket->sel == 1) cache wrap
ldreq r9, [r9, #4] // bucket->imp is before first bucket
ldr r12, [r9, #8]! // r12 = (++bucket)->sel
b 2b
.endmacro
/********************************************************************
* IMP cache_getImp(Class cls, SEL sel)
*
* On entry: r0 = class whose cache is to be searched
* r1 = selector to search for
*
* If found, returns method implementation.
* If not found, returns NULL.
********************************************************************/
STATIC_ENTRY cache_getImp
mov r9, r0
CacheLookup GETIMP // returns IMP on success
LCacheMiss:
mov r0, #0 // return nil if cache miss
bx lr
LGetImpExit:
END_ENTRY cache_getImp
/********************************************************************
*
* id objc_msgSend(id self, SEL _cmd,...) ********************************************************************/
ENTRY objc_msgSend
MESSENGER_START
cbz r0, LNilReceiver_f
ldr r9, [r0] // r9 = self->isa
CacheLookup NORMAL
// calls IMP or LCacheMiss
LCacheMiss:
MESSENGER_END_SLOW
ldr r9, [r0, #ISA] // class = receiver->isa
b __objc_msgSend_uncached
LNilReceiver:
mov r1, #0
MESSENGER_END_NIL
bx lr
LMsgSendExit:
END_ENTRY objc_msgSend
/********************************************************************
* id objc_msgSend_noarg(id self, SEL op)
*
* On entry: r0 is the message receiver,
* r1 is the selector
********************************************************************/
ENTRY objc_msgSend_noarg
b _objc_msgSend
END_ENTRY objc_msgSend_noarg
/********************************************************************
* void objc_msgSend_stret(void *st_addr, id self, SEL op, ...) * objc_msgSend_stret is the struct-return form of msgSend.
* The ABI calls for r0 to be used as the address of the structure
* being returned, with the parameters in the succeeding registers.
*
* On entry: r0 is the address where the structure is returned,
* r1 is the message receiver,
* r2 is the selector
********************************************************************/
ENTRY objc_msgSend_stret
MESSENGER_START
cbz r1, LNilReceiver_f
ldr r9, [r1] // r9 = self->isa
CacheLookup STRET
// calls IMP or LCacheMiss
LCacheMiss:
MESSENGER_END_SLOW
ldr r9, [r1] // r9 = self->isa
b __objc_msgSend_stret_uncached
LNilReceiver:
MESSENGER_END_NIL
bx lr
LMsgSendStretExit:
END_ENTRY objc_msgSend_stret
/********************************************************************
* id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
*
* struct objc_super {
* id receiver * }
********************************************************************/
ENTRY objc_msgSendSuper
MESSENGER_START
ldr r9, [r0, #CLASS] // r9 = struct super->class
CacheLookup SUPER
// calls IMP or LCacheMiss
LCacheMiss:
MESSENGER_END_SLOW
ldr r9, [r0, #CLASS] // r9 = struct super->class
ldr r0, [r0, #RECEIVER] // load real receiver
b __objc_msgSend_uncached
LMsgSendSuperExit:
END_ENTRY objc_msgSendSuper
/********************************************************************
* id objc_msgSendSuper2(struct objc_super *super, SEL op, ...)
*
* struct objc_super {
* id receiver * }
********************************************************************/
ENTRY objc_msgSendSuper2
MESSENGER_START
ldr r9, [r0, #CLASS] // class = struct super->class
ldr r9, [r9, #SUPERCLASS] // class = class->superclass
CacheLookup SUPER2
// calls IMP or LCacheMiss
LCacheMiss:
MESSENGER_END_SLOW
ldr r9, [r0, #CLASS] // class = struct super->class
ldr r9, [r9, #SUPERCLASS] // class = class->superclass
ldr r0, [r0, #RECEIVER] // load real receiver
b __objc_msgSend_uncached
LMsgSendSuper2Exit:
END_ENTRY objc_msgSendSuper2
/********************************************************************
* void objc_msgSendSuper_stret(void *st_addr, objc_super *self, SEL op, ...) * objc_msgSendSuper_stret is the struct-return form of msgSendSuper.
* The ABI calls for r0 to be used as the address of the structure
* being returned, with the parameters in the succeeding registers.
*
* On entry: r0 is the address where the structure is returned,
* r1 is the address of the objc_super structure,
* r2 is the selector
********************************************************************/
ENTRY objc_msgSendSuper_stret
MESSENGER_START
ldr r9, [r1, #CLASS] // r9 = struct super->class
CacheLookup SUPER_STRET
// calls IMP or LCacheMiss
LCacheMiss:
MESSENGER_END_SLOW
ldr r9, [r1, #CLASS] // r9 = struct super->class
ldr r1, [r1, #RECEIVER] // load real receiver
b __objc_msgSend_stret_uncached
LMsgSendSuperStretExit:
END_ENTRY objc_msgSendSuper_stret
/********************************************************************
* id objc_msgSendSuper2_stret
********************************************************************/
ENTRY objc_msgSendSuper2_stret
MESSENGER_START
ldr r9, [r1, #CLASS] // class = struct super->class
ldr r9, [r9, #SUPERCLASS] // class = class->superclass
CacheLookup SUPER2_STRET
LCacheMiss:
MESSENGER_END_SLOW
ldr r9, [r1, #CLASS] // class = struct super->class
ldr r9, [r9, #SUPERCLASS] // class = class->superclass
ldr r1, [r1, #RECEIVER] // load real receiver
b __objc_msgSend_stret_uncached
LMsgSendSuper2StretExit:
END_ENTRY objc_msgSendSuper2_stret
/********************************************************************
*
* _objc_msgSend_uncached_impcache
* Used to erase method cache entries in-place by
* bouncing them to the uncached lookup.
*
* _objc_msgSend_uncached
* _objc_msgSend_stret_uncached
* The uncached lookup.
*
********************************************************************/
STATIC_ENTRY _objc_msgSend_uncached_impcache
// Method cache version
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band Z is 0 (EQ) for normal, 1 (NE) for stret and/or super
// Out-of-band r9 is 1 for stret, cls for super, cls|1 for super_stret
// Note objc_msgForward_impcache uses the same parameters
MESSENGER_START
nop
MESSENGER_END_SLOW
ite eq
ldreq r9, [r0] // normal: r9 = class = self->isa
tstne r9, #1 // low bit clear?
beq __objc_msgSend_uncached // super: r9 is already the class
// stret or super_stret
eors r9, r9, #1 // clear low bit
it eq // r9 now zero?
ldreq r9, [r1] // stret: r9 = class = self->isa
// super_stret: r9 is already the class
b __objc_msgSend_stret_uncached
END_ENTRY _objc_msgSend_uncached_impcache
STATIC_ENTRY _objc_msgSend_uncached
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band r9 is the class to search
stmfd sp!, {r0-r3,r7,lr}
add r7, sp, #16
// receiver already in r0
// selector already in r1
mov r2, r9 // class to search
MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache3)
mov r12, r0 // r12 = IMP
movs r9, #0 // r9=0, Z=1 for nonstret forwarding
ldmfd sp!, {r0-r3,r7,lr}
bx r12
END_ENTRY _objc_msgSend_uncached
STATIC_ENTRY _objc_msgSend_stret_uncached
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band r9 is the class to search
stmfd sp!, {r0-r3,r7,lr}
add r7, sp, #16
mov r0, r1 // receiver
mov r1, r2 // selector
mov r2, r9 // class to search
MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache3)
mov r12, r0 // r12 = IMP
movs r9, #1 // r9=1, Z=0 for stret forwarding
ldmfd sp!, {r0-r3,r7,lr}
bx r12
END_ENTRY _objc_msgSend_stret_uncached
/********************************************************************
*
* id _objc_msgForward(id self, SEL _cmd,...)* _objc_msgForward and _objc_msgForward_stret are the externally-callable
* functions returned by things like method_getImplementation().
* _objc_msgForward_impcache is the function pointer actually stored in
* method caches.
*
********************************************************************/
MI_EXTERN(__objc_forward_handler)
MI_EXTERN(__objc_forward_stret_handler)
STATIC_ENTRY _objc_msgForward_impcache
// Method cache version
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band Z is 0 (EQ) for normal, 1 (NE) for stret and/or super
// Out-of-band r9 is 1 for stret, cls for super, cls|1 for super_stret
// Note _objc_msgSend_uncached_impcache uses the same parameters
MESSENGER_START
nop
MESSENGER_END_SLOW
it ne
tstne r9, #1
beq __objc_msgForward
b __objc_msgForward_stret
END_ENTRY _objc_msgForward_impcache
ENTRY _objc_msgForward
// Non-stret version
MI_GET_EXTERN(r12, __objc_forward_handler)
ldr r12, [r12]
bx r12
END_ENTRY _objc_msgForward
ENTRY _objc_msgForward_stret
// Struct-return version
MI_GET_EXTERN(r12, __objc_forward_stret_handler)
ldr r12, [r12]
bx r12
END_ENTRY _objc_msgForward_stret
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 objc_msgSend_stret_debug
b _objc_msgSend_stret
END_ENTRY objc_msgSend_stret_debug
ENTRY objc_msgSendSuper2_stret_debug
b _objc_msgSendSuper2_stret
END_ENTRY objc_msgSendSuper2_stret_debug
ENTRY method_invoke
// r1 is method triplet instead of SEL
ldr r12, [r1, #METHOD_IMP]
ldr r1, [r1, #METHOD_NAME]
bx r12
END_ENTRY method_invoke
ENTRY method_invoke_stret
// r2 is method triplet instead of SEL
ldr r12, [r2, #METHOD_IMP]
ldr r2, [r2, #METHOD_NAME]
bx r12
END_ENTRY method_invoke_stret
STATIC_ENTRY _objc_ignored_method
// self is already in a0
bx lr
END_ENTRY _objc_ignored_method
.section __DATA,__objc_msg_break
.long 0
.long 0
#endif