/* * @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@ */ #ifdef __arm__ /******************************************************************** * * objc-msg-arm.s - ARM code to support objc messaging * ********************************************************************/ #ifdef ARM11 #define MOVE cpy #define MOVEEQ cpyeq #define MOVENE cpyne #else #define MOVE mov #define MOVEEQ moveq #define MOVENE movne #endif #ifdef VFP_ARGS #define SAVE_VFP fstmfdd sp!, {d0-d7} #define RESTORE_VFP fldmfdd sp!, {d0-d7} #else #define SAVE_VFP /* empty */ #define RESTORE_VFP /* empty */ #endif #if defined(__DYNAMIC__) #define MI_EXTERN(var) \ .non_lazy_symbol_pointer ;\ L ## var ## __non_lazy_ptr: ;\ .indirect_symbol var ;\ .long 0 #else #define MI_EXTERN(var) \ .globl var #endif #if defined(__DYNAMIC__) #define MI_GET_ADDRESS(reg,var) \ ldr reg, 4f ;\ 3: ldr reg, [pc, reg] ;\ b 5f ;\ 4: .long L ## var ## __non_lazy_ptr - (3b + 8) ;\ 5: #else #define MI_GET_ADDRESS(reg,var) \ ldr reg, 3f ;\ b 4f ;\ 3: .long var ;\ 4: #endif #if defined(__DYNAMIC__) #define MI_BRANCH_EXTERNAL(var) \ MI_GET_ADDRESS(ip, var) ;\ bx ip #else #define MI_BRANCH_EXTERNAL(var) ;\ b var #endif #if defined(__DYNAMIC__) #define MI_CALL_EXTERNAL(var) \ MI_GET_ADDRESS(ip,var) ;\ MOVE lr, pc ;\ bx ip #else #define MI_CALL_EXTERNAL(var) \ bl var #endif MI_EXTERN(__class_lookupMethodAndLoadCache) MI_EXTERN(_FwdSel) MI_EXTERN(___objc_error) MI_EXTERN(__objc_forward_handler) MI_EXTERN(__objc_forward_stret_handler) #if 0 // Special section containing a function pointer that dyld will call // when it loads new images. MI_EXTERN(__objc_notify_images) .text .align 2 L__objc_notify_images: MI_BRANCH_EXTERNAL(__objc_notify_images) .section __DATA,__image_notify .long L__objc_notify_images #endif # _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 .globl _objc_entryPoints _objc_entryPoints: .long __cache_getImp .long __cache_getMethod .long _objc_msgSend .long _objc_msgSend_stret .long _objc_msgSendSuper .long _objc_msgSendSuper_stret .long 0 .data .globl _objc_exitPoints _objc_exitPoints: .long LGetImpExit .long LGetMethodExit .long LMsgSendExit .long LMsgSendStretExit .long LMsgSendSuperExit .long LMsgSendSuperStretExit .long 0 /* objc_super parameter to sendSuper */ .set RECEIVER, 0 .set CLASS, 4 /* Selected field offsets in class structure */ .set ISA, 0 #if __OBJC2__ .set CACHE, 8 #else .set CACHE, 32 #endif /* Method descriptor */ .set METHOD_NAME, 0 .set METHOD_IMP, 8 /* Cache header */ .set MASK, 0 .set OCCUPIED, 4 .set BUCKETS, 8 /* variable length array */ ##################################################################### # # ENTRY functionName # # Assembly directives to begin an exported function. # We align on cache boundaries for these few functions. # # Takes: functionName - name of the exported function ##################################################################### .macro ENTRY /* name */ .text .align 2 .globl _$0 _$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 selectorRegister, cacheMissLabel # # Locate the implementation for a selector in a class method cache. # # Takes: # v1 = class whose cache is to be searched # $0 = register containing selector (a2 or a3 ONLY) # cacheMissLabel = label to branch to iff method is not cached # # Kills: # a4, v1, v2, v3, ip # # On exit: (found) method triplet in v1, imp in ip # (not found) jumps to cacheMissLabel # ##################################################################### .macro CacheLookup /* selReg, missLabel */ ldr v2, [v1, #CACHE] /* cache = class->cache */ ldr v3, [v2, #MASK] /* mask = cache->mask */ add a4, v2, #BUCKETS /* buckets = &cache->buckets */ and v2, v3, $0, LSR #2 /* index = mask & (sel >> 2) */ /* search the cache */ /* a1=receiver, a2 or a3=sel, v2=index, v3=mask, a4=buckets, v1=method */ 1: ldr v1, [a4, v2, LSL #2] /* method = buckets[index] */ teq v1, #0 /* if (method == NULL) */ add v2, v2, #1 /* index++ */ beq $1 /* goto cacheMissLabel */ ldr ip, [v1, #METHOD_NAME] /* load method->method_name */ teq $0, ip /* if (method->method_name != sel) */ and v2, v2, v3 /* index &= mask */ bne 1b /* retry */ /* cache hit, v1 == method triplet address */ /* Return triplet in v1 and imp in ip */ ldr ip, [v1, #METHOD_IMP] /* imp = method->method_imp */ .endmacro /******************************************************************** * Method _cache_getMethod(Class cls, SEL sel, IMP msgForward_internal_imp) * * On entry: a1 = class whose cache is to be searched * a2 = selector to search for * a3 = _objc_msgForward_internal IMP * * If found, returns method triplet pointer. * If not found, returns NULL. * * NOTE: _cache_getMethod never returns any cache entry whose implementation * is _objc_msgForward_internal. It returns NULL instead. This prevents thread- * safety and memory management bugs in _class_lookupMethodAndLoadCache. * See _class_lookupMethodAndLoadCache for details. * * _objc_msgForward_internal is passed as a parameter because it's more * efficient to do the (PIC) lookup once in the caller than repeatedly here. ********************************************************************/ ENTRY _cache_getMethod # save registers and load class for CacheLookup stmfd sp!, {a4,v1-v3,r7,lr} add r7, sp, #16 MOVE v1, a1 # search the cache CacheLookup a2, LGetMethodMiss # cache hit, method triplet in v1 and imp in ip teq ip, a3 /* check for _objc_msgForward_internal */ MOVEEQ a1, #1 /* return (Method)1 if forward */ MOVENE a1, v1 /* return triplet if not forward */ ldmfd sp!, {a4,v1-v3,r7,pc} LGetMethodMiss: MOVE a1, #0 /* return nil if cache miss */ ldmfd sp!, {a4,v1-v3,r7,pc} LGetMethodExit: END_ENTRY _cache_getMethod /******************************************************************** * IMP _cache_getImp(Class cls, SEL sel) * * On entry: a1 = class whose cache is to be searched * a2 = selector to search for * * If found, returns method implementation. * If not found, returns NULL. ********************************************************************/ ENTRY _cache_getImp # save registers and load class for CacheLookup stmfd sp!, {a4,v1-v3,r7,lr} add r7, sp, #16 MOVE v1, a1 # search the cache CacheLookup a2, LGetImpMiss # cache hit, method triplet in v1 and imp in ip MOVE a1, ip @ return imp ldmfd sp!, {a4,v1-v3,r7,pc} LGetImpMiss: MOVE a1, #0 @ return nil if cache miss ldmfd sp!, {a4,v1-v3,r7,pc} LGetImpExit: END_ENTRY _cache_getImp /******************************************************************** * id objc_msgSend(id self, * SEL op, * ...) * * On entry: a1 is the message receiver, * a2 is the selector ********************************************************************/ ENTRY objc_msgSend # check whether receiver is nil teq a1, #0 moveq a2, #0 bxeq lr # save registers and load receiver's class for CacheLookup stmfd sp!, {a4,v1-v3} ldr v1, [a1, #ISA] # receiver is non-nil: search the cache CacheLookup a2, LMsgSendCacheMiss # cache hit (imp in ip) - prep for forwarding, restore registers and call teq v1, v1 /* set nonstret (eq) */ ldmfd sp!, {a4,v1-v3} bx ip # cache miss: go search the method lists LMsgSendCacheMiss: ldmfd sp!, {a4,v1-v3} b _objc_msgSend_uncached LMsgSendExit: END_ENTRY objc_msgSend .text .align 2 _objc_msgSend_uncached: # Push stack frame stmfd sp!, {a1-a4,r7,lr} add r7, sp, #16 SAVE_VFP # Load class and selector ldr a1, [a1, #ISA] /* class = receiver->isa */ # MOVE a2, a2 /* selector already in a2 */ # Do the lookup MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache) MOVE ip, a1 # Prep for forwarding, Pop stack frame and call imp teq v1, v1 /* set nonstret (eq) */ RESTORE_VFP ldmfd sp!, {a1-a4,r7,lr} bx ip /******************************************************************** * struct_type objc_msgSend_stret(id self, * SEL op, * ...); * * objc_msgSend_stret is the struct-return form of msgSend. * The ABI calls for a1 to be used as the address of the structure * being returned, with the parameters in the succeeding registers. * * On entry: a1 is the address where the structure is returned, * a2 is the message receiver, * a3 is the selector ********************************************************************/ ENTRY objc_msgSend_stret # check whether receiver is nil teq a2, #0 bxeq lr # save registers and load receiver's class for CacheLookup stmfd sp!, {a4,v1-v3} ldr v1, [a2, #ISA] # receiver is non-nil: search the cache CacheLookup a3, LMsgSendStretCacheMiss # cache hit (imp in ip) - prep for forwarding, restore registers and call tst v1, v1 /* set stret (ne); v1 is nonzero (triplet) */ ldmfd sp!, {a4,v1-v3} bx ip # cache miss: go search the method lists LMsgSendStretCacheMiss: ldmfd sp!, {a4,v1-v3} b _objc_msgSend_stret_uncached LMsgSendStretExit: END_ENTRY objc_msgSend_stret .text .align 2 _objc_msgSend_stret_uncached: # Push stack frame stmfd sp!, {a1-a4,r7,lr} add r7, sp, #16 SAVE_VFP # Load class and selector ldr a1, [a2, #ISA] /* class = receiver->isa */ MOVE a2, a3 /* selector */ # Do the lookup MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache) MOVE ip, a1 # Prep for forwarding, pop stack frame and call imp tst a1, a1 /* set stret (ne); a1 is nonzero (imp) */ RESTORE_VFP ldmfd sp!, {a1-a4,r7,lr} bx ip /******************************************************************** * id objc_msgSendSuper(struct objc_super *super, * SEL op, * ...) * * struct objc_super { * id receiver * Class class * } ********************************************************************/ ENTRY objc_msgSendSuper2 @ objc_super->class is superclass of the class to search ldr r12, [a1, #CLASS] ldr r12, [r12, #4] @ r12 = cls->super_class str r12, [a1, #CLASS] b _objc_msgSendSuper END_ENTRY ENTRY objc_msgSendSuper # save registers and load super class for CacheLookup stmfd sp!, {a4,v1-v3} ldr v1, [a1, #CLASS] # search the cache CacheLookup a2, LMsgSendSuperCacheMiss # cache hit (imp in ip) - prep for forwarding, restore registers and call teq v1, v1 /* set nonstret (eq) */ ldmfd sp!, {a4,v1-v3} ldr a1, [a1, #RECEIVER] @ fetch real receiver bx ip # cache miss: go search the method lists LMsgSendSuperCacheMiss: ldmfd sp!, {a4,v1-v3} b _objc_msgSendSuper_uncached LMsgSendSuperExit: END_ENTRY objc_msgSendSuper .text .align 2 _objc_msgSendSuper_uncached: # Push stack frame stmfd sp!, {a1-a4,r7,lr} add r7, sp, #16 SAVE_VFP # Load class and selector ldr a1, [a1, #CLASS] /* class = super->class */ # MOVE a2, a2 /* selector already in a2 */ # Do the lookup MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache) MOVE ip, a1 # Prep for forwarding, pop stack frame and call imp teq v1, v1 /* set nonstret (eq) */ RESTORE_VFP ldmfd sp!, {a1-a4,r7,lr} ldr a1, [a1, #RECEIVER] @ fetch real receiver bx ip /******************************************************************** * struct_type objc_msgSendSuper_stret(objc_super *super, * SEL op, * ...) * * struct objc_super { * id receiver * Class class * } * * * objc_msgSendSuper_stret is the struct-return form of msgSendSuper. * The ABI calls for a1 to be used as the address of the structure * being returned, with the parameters in the succeeding registers. * * On entry: a1 is the address to which to copy the returned structure, * a2 is the address of the objc_super structure, * a3 is the selector ********************************************************************/ ENTRY objc_msgSendSuper2_stret @ objc_super->class is superclass of the class to search ldr r12, [a2, #CLASS] ldr r12, [r12, #4] @ xx = cls->super_class str r12, [a2, #CLASS] b _objc_msgSendSuper_stret END_ENTRY ENTRY objc_msgSendSuper_stret # save registers and load super class for CacheLookup stmfd sp!, {a4,v1-v3} ldr v1, [a2, #CLASS] # search the cache CacheLookup a3, LMsgSendSuperStretCacheMiss # cache hit (imp in ip) - prep for forwarding, restore registers and call tst v1, v1 /* set stret (ne); v1 is nonzero (triplet) */ ldmfd sp!, {a4,v1-v3} ldr a2, [a2, #RECEIVER] @ fetch real receiver bx ip # cache miss: go search the method lists LMsgSendSuperStretCacheMiss: ldmfd sp!, {a4,v1-v3} b _objc_msgSendSuper_stret_uncached LMsgSendSuperStretExit: END_ENTRY objc_msgSendSuper_stret .text .align 2 _objc_msgSendSuper_stret_uncached: # Push stack frame stmfd sp!, {a1-a4,r7,lr} add r7, sp, #16 SAVE_VFP # Load class and selector ldr a1, [a2, #CLASS] /* class = super->class */ MOVE a2, a3 /* selector */ # Do the lookup MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache) MOVE ip, a1 # Prep for forwarding, pop stack frame and call imp tst v1, v1 /* set stret (ne); v1 is nonzero (triplet) */ RESTORE_VFP ldmfd sp!, {a1-a4,r7,lr} ldr a2, [a2, #RECEIVER] @ fetch real receiver bx ip /******************************************************************** * * id _objc_msgForward(id self, * SEL sel, * ...); * struct_type _objc_msgForward_stret (id self, * SEL sel, * ...); * * Both _objc_msgForward and _objc_msgForward_stret * send the message to a method having the signature: * * - forward:(SEL)sel :(marg_list)args; * * The marg_list's layout is: * d0 <-- args * d1 * d2 | increasing address * d3 v * d4 * d5 * d6 * d7 * a1 * a2 * a3 * a4 * stack args... * * typedef struct objc_sendv_margs { * #ifdef VFP_ARGS * double vfp[8]; * #endif * int a[4]; * int stackArgs[...]; * }; * ********************************************************************/ .data .private_extern _FwdSel _FwdSel: .long 0 .private_extern __objc_forward_handler __objc_forward_handler: .long 0 .private_extern __objc_forward_stret_handler __objc_forward_stret_handler: .long 0 ENTRY _objc_msgForward_internal .private_extern __objc_msgForward_internal // Method cache version // THIS IS NOT A CALLABLE C FUNCTION // Out-of-band condition register is NE for stret, EQ otherwise. bne __objc_msgForward_stret b __objc_msgForward END_ENTRY _objc_msgForward_internal ENTRY _objc_msgForward // Non-stret version # check for user-installed forwarding handler MI_GET_ADDRESS(ip, __objc_forward_handler) ldr ip, [ip] teq ip, #0 bxne ip # build marg_list stmfd sp!, {a1-a4} @ push args to marg_list #ifdef VFP_ARGS fstmfdd sp!, {d0-d7} @ push fp args to marg_list #endif # build forward::'s parameter list (self, forward::, original sel, marg_list) # a1 already is self MOVE a3, a2 @ original sel MI_GET_ADDRESS(a2, _FwdSel) @ "forward::" ldr a2, [a2] MOVE a4, sp @ marg_list # check for forwarding of forward:: itself teq a2, a3 beq LMsgForwardError @ original sel == forward:: - give up # push stack frame str lr, [sp, #-(2*4)]! @ save lr and align stack # send it bl _objc_msgSend # pop stack frame and return ldr lr, [sp] #ifdef VFP_ARGS add sp, sp, #(4 + 4 + 4*4 + 8*8) @ skip lr, pad, a1..a4, d0..d7 #else add sp, sp, #(4 + 4 + 4*4) @ skip lr, pad, a1..a4 #endif bx lr END_ENTRY _objc_msgForward ENTRY _objc_msgForward_stret // Struct-return version # check for user-installed forwarding handler MI_GET_ADDRESS(ip, __objc_forward_stret_handler) ldr ip, [ip] teq ip, #0 bxne ip # build marg_list stmfd sp!, {a1-a4} @ push args to marg_list #ifdef VFP_ARGS fstmfdd sp!, {d0-d7} @ push fp args to marg_list #endif # build forward::'s parameter list (self, forward::, original sel, marg_list) MOVE a1, a2 @ self MI_GET_ADDRESS(a2, _FwdSel) @ "forward::" ldr a2, [a2] # a3 is already original sel MOVE a4, sp @ marg_list # check for forwarding of forward:: itself teq a2, a3 beq LMsgForwardError @ original sel == forward:: - give up # push stack frame str lr, [sp, #-(2*4)]! @ save lr and align stack # send it bl _objc_msgSend # pop stack frame and return ldr lr, [sp] #ifdef VFP_ARGS add sp, sp, #(4 + 4 + 4*4 + 8*8) @ skip lr, pad, a1..a4, d0..d7 #else add sp, sp, #(4 + 4 + 4*4) @ skip lr, pad, a1..a4 #endif bx lr END_ENTRY _objc_msgForward_stret LMsgForwardError: # currently a1=self, a2=forward::, a3 = original sel, a4 = marg_list # call __objc_error(self, format, original sel) add a2, pc, #4 @ pc bias is 8 bytes MI_CALL_EXTERNAL(___objc_error) .ascii "Does not recognize selector %s\0" /******************************************************************** * id objc_msgSendv(id self, * SEL op, * unsigned arg_size, * marg_list arg_frame); * * typedef struct objc_sendv_margs { * #ifdef VFP_ARGS * double vfp[8]; * #endif * int a[4]; * int stackArgs[...]; * }; * * arg_frame is the number of bytes used in a[] plus stackArgs. * It does not include vfp[]. * ********************************************************************/ ENTRY objc_msgSendv # Push stack frame SAVE_VFP stmfd sp!, {a4,v1-v3,r7,lr} @ a4 saved for stack alignment only add r7, sp, #16 # save sendv's parameters # self stays in a1 # sel stays in a2 MOVE v1, a3 @ v1 is arg count in bytes MOVE v2, a4 @ v2 is marg_list # load FP from marg_list #ifdef VFP_ARGS fldmfdd v2!, {d0-d7} #endif # load arg registers from marg_list # v1 is remaining count, v2 is pointer into marg_list # self already in a1 # sel already in a2 cmp v1, #12 ldrhs a3, [v2, #8] @ pop a3 if arg bytes is at least 12 ldrhi a4, [v2, #12] @ pop a4 if arg bytes is more than 12 subs v1, v1, #16 @ skip past register args ble LMsgSendvCall @ call now if no args remain add v2, v2, #16 @ skip past register args # copy stack args from marg_list # v1 is remaining bytes, v2 is pointer into marg_list, sp is pointer into stack tst v1, #4 subne sp, sp, #4 @ push 4-byte pad if word count is odd sub sp, sp, v1 @ move sp to end and copy backwards @ (this preserves ABI's stack constraints) LMsgSendvArgLoop: subs v1, v1, #4 ldr v3, [v2, v1] str v3, [sp, v1] bne LMsgSendvArgLoop LMsgSendvCall: bl _objc_msgSend # Pop stack frame and return MOVE sp, r7 ldmfd sp!, {a4,v1-v3,r7,pc} #ifdef VFP_ARGS #error broken for vfp #endif END_ENTRY objc_msgSendv /******************************************************************** * struct_type objc_msgSendv_stret(id self, * SEL op, * unsigned arg_size, * marg_list arg_frame); * * typedef struct objc_sendv_margs { * #ifdef VFP_ARGS * double vfp[8]; * #endif * int a[4]; * int stackArgs[...]; * }; * * arg_frame is the number of bytes used in a[] plus stackArgs. * It does not include vfp[]. ********************************************************************/ ENTRY objc_msgSendv_stret # Push stack frame stmfd sp!, {a4,v1-v3,r7,lr} @ a4 saved for stack alignment only add r7, sp, #16 SAVE_VFP # save sendv's parameters # stret address stays in a1 # self stays in a2 # sel stays in a3 MOVE v1, a4 @ v1 is arg count in bytes ldr v2, [r7, #24] @ v2 is marg_list # load FP from marg_list #ifdef VFP_ARGS fldmfdd v2!, {d0-d7} #endif # load arg registers from marg_list # v1 is remaining count, v2 is pointer into marg_list # stret already in a1 # self already in a2 # sel already in a3 subs v1, v1, #16 @ skip past register args ldrhs a4, [v2, #12] @ pop a4 if arg bytes is at least 16 beq LMsgSendvStretCall @ call now if no args remain add v2, v2, #16 @ skip past register args # copy stack args from marg_list # v1 is remaining count, v2 is pointer into marg_list, sp is pointer into stack tst v1, #4 subne sp, sp, #4 @ push 4-byte pad if word count is odd sub sp, sp, v1 @ move pointers to end and copy backwards @ (this preserves ABI's stack constraints) LMsgSendvStretArgLoop: subs v1, v1, #4 ldr v3, [v2, v1] str v3, [sp, v1] bne LMsgSendvStretArgLoop LMsgSendvStretCall: bl _objc_msgSend_stret # Pop stack frame and return MOVE sp, r7 ldmfd sp!, {a4,v1-v3,r7,pc} #ifdef VFP_ARGS #error broken for vfp #endif END_ENTRY objc_msgSendv_stret ENTRY method_invoke # a2 is method triplet instead of SEL ldr ip, [a2, #METHOD_IMP] ldr a2, [a2, #METHOD_NAME] bx ip END_ENTRY method_invoke ENTRY method_invoke_stret # a3 is method triplet instead of SEL ldr ip, [a3, #METHOD_IMP] ldr a3, [a3, #METHOD_NAME] bx ip END_ENTRY method_invoke_stret #endif