/* * @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 #ifndef _ARM_ARCH_7 # error requires armv7 #endif .syntax unified #define MI_EXTERN(var) \ .non_lazy_symbol_pointer ;\ L ## var ## $$non_lazy_ptr: ;\ .indirect_symbol var ;\ .long 0 #define MI_GET_EXTERN(reg,var) \ movw reg, :lower16:(L##var##$$non_lazy_ptr-4f-4) ;\ movt reg, :upper16:(L##var##$$non_lazy_ptr-4f-4) ;\ 4: add reg, pc ;\ ldr reg, [reg] #define MI_CALL_EXTERNAL(var) \ MI_GET_EXTERN(r12,var) ;\ blx r12 #define MI_GET_ADDRESS(reg,var) \ movw reg, :lower16:(var-4f-4) ;\ movt reg, :upper16:(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; * Class cls; // the class to search * } ********************************************************************/ 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; * Class cls; // SUBCLASS of the class to search * } ********************************************************************/ 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