2 * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 /********************************************************************
25 * objc-msg-ppc.s - PowerPC code to support objc messaging.
27 * Copyright 1988-1996 NeXT Software, Inc.
29 * December 2002 Andy Belk (abelk at apple.com)
30 * Use r2 in the messenger - no longer need r10.
31 * Removed "few args" variants (no longer worth it, especially since gcc3 still
32 * doesn't generate code for them).
33 * Add NonNil entry points to objc_msgSend and objc_msgSend_stret.
34 * Align objc_msgSend et al on cache lines.
35 * Replace CALL_EXTERN references (which caused excess mflr/mtlr usage) with
36 * dyld-stub-compatible versions: shorter and become local branches within a dylib.
38 * 8-Nov-2000 Laurent Ramontianu (ramontia@apple.com)
39 * Added "few args" params. to CacheLookup and MethodTableLookup
40 * Added the alternate entry points:
41 * objc_msgSendFew, objc_msgSendFew_stret,
42 * objc_msgSendSuperFew, objc_msgSendSuperFew_stret
44 * 18-Jun-97 David Harrison (harrison@apple.com)
47 * 1-May-97 Umesh Vaishampayan (umeshv@NeXT.com)
48 * Incorporated locking code fixes from
49 * David Harrison (harrison@NeXT.com)
51 * 2-Apr-97 Umesh Vaishampayan (umeshv@NeXT.com)
52 * Incorporated changes for messenger with struct return
53 * Cleaned up the labels to use local labels
54 * Fixed bug in the msgSendSuper that did not do the locking.
56 * 31-Dec-96 Umesh Vaishampayan (umeshv@NeXT.com)
58 ********************************************************************/
64 /********************************************************************
65 * Data used by the ObjC runtime.
67 ********************************************************************/
70 ; Substitute receiver for messages sent to nil (usually also nil)
71 ; id _objc_nilReceiver
73 .private_extern __objc_nilReceiver
77 ; _objc_entryPoints and _objc_exitPoints are used by method dispatch
78 ; caching code to figure out whether any threads are actively
79 ; in the cache for dispatching. The labels surround the asm code
80 ; that do cache lookups. The tables are zero-terminated.
81 .private_extern _objc_entryPoints
84 .long __cache_getMethod
86 .long _objc_msgSend_stret
87 .long _objc_msgSendSuper
88 .long _objc_msgSendSuper_stret
89 .long _objc_msgSend_rtp
92 .private_extern _objc_exitPoints
97 .long LMsgSendStretExit
98 .long LMsgSendSuperExit
99 .long LMsgSendSuperStretExit
100 .long _objc_msgSend_rtp_exit
104 * Handcrafted dyld stubs for each external call.
105 * They should be converted into a local branch after linking. aB.
108 /* asm_help.h version is not what we want */
111 #if defined(__DYNAMIC__)
113 #define CALL_EXTERN(name) bl L ## name ## $stub
115 #define LAZY_PIC_FUNCTION_STUB(name) \
118 L ## name ## $stub: @\
119 .indirect_symbol name @\
121 bcl 20,31,L0$ ## name @\
124 addis r11,r11,ha16(L ## name ## $lazy_ptr-L0$ ## name) @\
126 lwz r12,lo16(L ## name ## $lazy_ptr-L0$ ## name)(r11) @\
128 addi r11,r11,lo16(L ## name ## $lazy_ptr-L0$ ## name) @\
131 .lazy_symbol_pointer @\
132 L ## name ## $lazy_ptr: @\
133 .indirect_symbol name @\
134 .long dyld_stub_binding_helper
136 #else /* __DYNAMIC__ */
138 #define CALL_EXTERN(name) bl name
140 #define LAZY_PIC_FUNCTION_STUB(name)
142 #endif /* __DYNAMIC__ */
144 ; _class_lookupMethodAndLoadCache
145 LAZY_PIC_FUNCTION_STUB(__class_lookupMethodAndLoadCache)
148 LAZY_PIC_FUNCTION_STUB(___objc_error) /* No stub needed */
152 LAZY_PIC_FUNCTION_STUB(mcount)
156 /********************************************************************
158 * Structure definitions.
160 ********************************************************************/
162 ; objc_super parameter to sendSuper
166 ; Selected field offsets in class structure
171 #define METHOD_NAME 0
177 #define BUCKETS 8 // variable length array
179 #if defined(OBJC_INSTRUMENTED)
180 ; Cache instrumentation data, follows buckets
182 #define hitProbes hitCount + 4
183 #define maxHitProbes hitProbes + 4
184 #define missCount maxHitProbes + 4
185 #define missProbes missCount + 4
186 #define maxMissProbes missProbes + 4
187 #define flushCount maxMissProbes + 4
188 #define flushedEntries flushCount + 4
191 /********************************************************************
195 ********************************************************************/
197 // In case the implementation is _objc_msgForward, indicate to it
198 // whether the method was invoked as a word-return or struct-return.
199 // The li instruction costs nothing because it fits into spare
200 // processor cycles. We choose to make the MsgSend indicator non-zero
201 // as r11 is already guaranteed non-zero for a cache hit (no li needed).
203 #define kFwdMsgSend 1
204 #define kFwdMsgSendStret 0
207 /********************************************************************
209 * Useful macros. Macros are used instead of subroutines, for speed.
211 ********************************************************************/
213 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
215 ; LOAD_STATIC_WORD targetReg, symbolName, LOCAL_SYMBOL | EXTERNAL_SYMBOL
217 ; Load the value of the named static data word.
219 ; Takes: targetReg - the register, other than r0, to load
220 ; symbolName - the name of the symbol
221 ; LOCAL_SYMBOL - symbol name used as-is
222 ; EXTERNAL_SYMBOL - symbol name gets nonlazy treatment
224 ; Eats: r0 and targetReg
225 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
227 ; Values to specify whether the symbols is plain or nonlazy
228 #define LOCAL_SYMBOL 0
229 #define EXTERNAL_SYMBOL 1
231 .macro LOAD_STATIC_WORD
233 #if defined(__DYNAMIC__)
235 bcl 20,31,1f ; 31 is cr7[so]
238 .if $2 == EXTERNAL_SYMBOL
239 addis $0,$0,ha16(L$1-1b)
240 lwz $0,lo16(L$1-1b)($0)
242 .elseif $2 == LOCAL_SYMBOL
243 addis $0,$0,ha16($1-1b)
244 lwz $0,lo16($1-1b)($0)
246 !!! Unknown symbol type !!!
255 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
257 ; LEA_STATIC_DATA targetReg, symbolName, LOCAL_SYMBOL | EXTERNAL_SYMBOL
259 ; Load the address of the named static data.
261 ; Takes: targetReg - the register, other than r0, to load
262 ; symbolName - the name of the symbol
263 ; LOCAL_SYMBOL - symbol is local to this module
264 ; EXTERNAL_SYMBOL - symbol is imported from another module
266 ; Eats: r0 and targetReg
267 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
269 .macro LEA_STATIC_DATA
270 #if defined(__DYNAMIC__)
272 bcl 20,31,1f ; 31 is cr7[so]
275 .if $2 == EXTERNAL_SYMBOL
276 addis $0,$0,ha16(L$1-1b)
277 lwz $0,lo16(L$1-1b)($0)
278 .elseif $2 == LOCAL_SYMBOL
279 addis $0,$0,ha16($1-1b)
280 addi $0,$0,lo16($1-1b)
282 !!! Unknown symbol type !!!
291 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
295 ; Assembly directives to begin an exported function.
296 ; We align on cache boundaries for these few functions.
298 ; Takes: functionName - name of the exported function
299 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
308 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
310 ; END_ENTRY functionName
312 ; Assembly directives to end an exported function. Just a placeholder,
313 ; a close-parenthesis for ENTRY, until it is needed for something.
315 ; Takes: functionName - name of the exported function
316 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
321 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
323 ; PLOCK scratchReg, lockName
325 ; Acquire named spinlock.
327 ; Takes: scratchReg - a register, other than r0, that can be mangled
328 ; lockName - the name of a static, aligned, 32-bit lock word
330 ; Eats: r0 and scratchReg
331 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
334 LEA_STATIC_DATA $0, $1, EXTERNAL_SYMBOL
335 b .+16 ; jump into loop at the reserving check
336 lwz r0,0($0) ; check with fast, less intrusive lwz versus lwarx
337 cmplwi r0,0 ; lock held?
338 bne .-8 ; if so, spin until it appears unlocked
339 lwarx r0,0,$0 ; get lock value, acquire memory reservation
340 cmplwi r0,0 ; lock held?
341 bne .-20 ; if locked, go spin waiting for unlock
342 li r0,1 ; get value that means locked
343 stwcx. r0,0,$0 ; store it iff reservation still holds
344 bne- .-20 ; if reservation was lost, go re-reserve
345 isync ; discard effects of prefetched instructions
348 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
350 ; PUNLOCK scratchReg, lockName
352 ; Release named spinlock.
354 ; Takes: scratchReg - a register, other than r0, that can be mangled
355 ; lockName - the name of a static, aligned, 32-bit lock word
357 ; Eats: r0 and scratchReg
358 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
361 sync ; force out changes before unlocking
362 LEA_STATIC_DATA $0, $1, EXTERNAL_SYMBOL
363 li r0,0 ; get value meaning "unlocked"
364 stw r0,0($0) ; unlock the lock
368 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
370 ; CacheLookup selectorRegister, cacheMissLabel
372 ; Locate the implementation for a selector in a class method cache.
375 ; $0 = register containing selector (r4 or r5 ONLY);
376 ; cacheMissLabel = label to branch to iff method is not cached
377 ; r12 = class whose cache is to be searched
379 ; On exit: (found) method triplet in r2, imp in r12, r11 is non-zero
380 ; (not found) jumps to cacheMissLabel
382 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
386 #if defined(OBJC_INSTRUMENTED)
387 ; when instrumented, we use r6 and r7
388 stw r6,36(r1) ; save r6 for use as cache pointer
389 stw r7,40(r1) ; save r7 for use as probe count
390 li r7,0 ; no probes so far!
393 lwz r2,CACHE(r12) ; cache = class->cache
394 stw r9,48(r1) ; save r9
396 #if defined(OBJC_INSTRUMENTED)
397 mr r6,r2 ; save cache pointer
400 lwz r11,MASK(r2) ; mask = cache->mask
401 addi r0,r2,BUCKETS ; buckets = cache->buckets
402 slwi r11,r11,2 ; r11 = mask << 2
403 and r9,$0,r11 ; bytes = sel & (mask<<2)
405 #if defined(OBJC_INSTRUMENTED)
409 ; r6 = cache, r7 = probeCount
410 lwz r9,MASK(r6) ; entryCount = mask + 1
412 slwi r9,r9,2 ; tableSize = entryCount * sizeof(entry)
413 addi r9,r9,BUCKETS ; offset = buckets + tableSize
414 add r11,r6,r9 ; cacheData = &cache->buckets[mask+1]
415 lwz r9,missCount(r11) ; cacheData->missCount += 1
417 stw r9,missCount(r11) ;
418 lwz r9,missProbes(r11) ; cacheData->missProbes += probeCount
420 stw r9,missProbes(r11) ;
421 lwz r9,maxMissProbes(r11) ; if (probeCount > cacheData->maxMissProbes)
422 cmplw r7,r9 ; maxMissProbes = probeCount
424 stw r7,maxMissProbes(r11) ;
426 lwz r6,36(r1) ; restore r6
427 lwz r7,40(r1) ; restore r7
429 b $1 ; goto cacheMissLabel
434 #if defined(OBJC_INSTRUMENTED)
435 addi r7,r7,1 ; probeCount += 1
438 lwzx r2,r9,r0 ; method = buckets[bytes/4]
439 addi r9,r9,4 ; bytes += 4
440 cmplwi r2,0 ; if (method == NULL)
441 #if defined(OBJC_INSTRUMENTED)
444 beq- $1 ; goto cacheMissLabel
447 lwz r12,METHOD_NAME(r2) ; name = method->method_name
448 and r9,r9,r11 ; bytes &= (mask<<2)
449 cmplw r12,$0 ; if (name != selector)
450 bne- LLoop_$0_$1 ; goto loop
452 ; cache hit, r2 == method triplet address
453 ; Return triplet in r2 and imp in r12
454 lwz r12,METHOD_IMP(r2) ; imp = method->method_imp
456 #if defined(OBJC_INSTRUMENTED)
457 ; r6 = cache, r7 = probeCount
458 lwz r9,MASK(r6) ; entryCount = mask + 1
460 slwi r9,r9,2 ; tableSize = entryCount * sizeof(entry)
461 addi r9,r9,BUCKETS ; offset = buckets + tableSize
462 add r11,r6,r9 ; cacheData = &cache->buckets[mask+1]
463 lwz r9,hitCount(r11) ; cache->hitCount += 1
465 stw r9,hitCount(r11) ;
466 lwz r9,hitProbes(r11) ; cache->hitProbes += probeCount
468 stw r9,hitProbes(r11) ;
469 lwz r9,maxHitProbes(r11) ; if (probeCount > cache->maxMissProbes)
470 cmplw r7,r9 ;maxMissProbes = probeCount
472 stw r7,maxHitProbes(r11) ;
474 lwz r6,36(r1) ; restore r6
475 lwz r7,40(r1) ; restore r7
478 lwz r9,48(r1) ; restore r9
482 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
484 ; CacheLookup cache locking - 2001-11-12
485 ; The collecting cache mechanism precludes the need for a cache lock
486 ; in objc_msgSend. The cost of the collecting cache is small: a few
487 ; K of memory for uncollected caches, and less than 1 ms per collection.
488 ; A large app will only run collection a few times.
489 ; Using the code below to lock the cache almost doubles messaging time,
490 ; costing several seconds of CPU across several minutes of operation.
491 ; The code below probably could be improved, but almost all of the
492 ; locking slowdown is in the sync and isync.
494 ; 40 million message test times (G4 1x667):
495 ; no lock 4.390u 0.030s 0:04.59 96.2% 0+0k 0+1io 0pf+0w
496 ; with lock 9.120u 0.010s 0:09.83 92.8% 0+0k 0+0io 0pf+0w
498 ;; LockCache mask_dest, cache
500 ; ; LOCKED mask is NEGATIVE
501 ; lwarx $0, mask, $1 ; mask = reserve(cache->mask)
503 ; blt .-8 ; try again if mask < 0
505 ; stwcx. r0, mask, $1 ; cache->mask = -mask ($0 keeps +mask)
506 ; bne .-20 ; try again if lost reserve
507 ; isync ; flush prefetched instructions after locking
510 ;; UnlockCache (mask<<2), cache
512 ; sync ; finish previous instructions before unlocking
513 ; srwi r0, $0, 2 ; r0 = (mask<<2) >> 2
514 ; stw r0, mask($1) ; cache->mask = +mask
517 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
520 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
523 ; MethodTableLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER
525 ; Takes: WORD_RETURN (r3 is first parameter)
526 ; STRUCT_RETURN (r3 is structure return address, r4 is first parameter)
527 ; MSG_SEND (first parameter is receiver)
528 ; MSG_SENDSUPER (first parameter is address of objc_super structure)
530 ; Eats: r0, r2, r11, r12
531 ; On exit: restores r9 saved by CacheLookup
533 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
535 ; Values to specify to method lookup macros whether the return type of
536 ; the method is an integer or structure.
537 #define WORD_RETURN 0
538 #define STRUCT_RETURN 1
540 ; Values to specify to method lookup macros whether the return type of
541 ; the method is an integer or structure.
543 #define MSG_SENDSUPER 1
545 .macro MethodTableLookup
549 stw r3, 24(r1) ; save arguments
555 ; r9 was saved by CacheLookup
559 ; Save the FP parameter registers.
560 ; We do not spill vector argument registers. This is
561 ; harmless because vector parameters are unsupported.
576 stwu r1,-56-(13*8)(r1) ; grow the stack. Must be 16-byte-aligned.
578 stwu r1,-64(r1) ; grow the stack. Must be 16-byte-aligned.
581 ; Pass parameters to __class_lookupMethodAndLoadCache. First parameter is
582 ; the class pointer. Second parameter is the selector. Where they come
583 ; from depends on who called us. In the int return case, the selector is
585 .if $0 == WORD_RETURN ; WORD_RETURN
586 .if $1 == MSG_SEND ; MSG_SEND
587 lwz r3,ISA(r3) ; class = receiver->isa
588 .else ; MSG_SENDSUPER
589 lwz r3,CLASS(r3) ; class = super->class
592 .else ; STRUCT_RETURN
593 .if $1 == MSG_SEND ; MSG_SEND
594 lwz r3,ISA(r4) ; class = receiver->isa
595 .else ; MSG_SENDSUPER
596 lwz r3,CLASS(r4) ; class = super->class
598 mr r4,r5 ; selector = selector
601 ; We code the call inline rather than using the CALL_EXTERN macro because
602 ; that leads to a lot of extra unnecessary and inefficient instructions.
603 CALL_EXTERN(__class_lookupMethodAndLoadCache)
605 mr r12,r3 ; copy implementation to r12
606 mtctr r3 ; copy imp to ctr
607 lwz r1,0(r1) ; restore the stack pointer
609 mtlr r0 ; restore return pc
613 ; Restore FP parameter registers
628 lwz r3, 24(r1) ; restore parameter registers
634 lwz r9, 48(r1) ; r9 was saved by CacheLookup
641 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
645 ; Macro to call mcount function in profiled builds.
647 ; NOTE: Makes sure to save/restore r11 and r12, even though they
648 ; are not defined to be volatile, because they are used during
651 ; Takes: lr Callers return PC
654 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
658 mflr r0 ; save return pc
661 stwu r1,-208(r1) ; push aligned areas, set stack link
663 stw r3, 56(r1) ; save all volatile registers
671 stw r11,88(r1) ; save r11 and r12, too
688 mr r3, r0 ; pass our callers address
692 lwz r3, 56(r1) ; restore all volatile registers
700 lwz r11,88(r1) ; restore r11 and r12, too
717 lwz r1,0(r1) ; restore the stack pointer
719 mtlr r0 ; restore return pc
724 /********************************************************************
725 * Method _cache_getMethod(Class cls, SEL sel, IMP objc_msgForward_imp)
727 * On entry: r3 = class whose cache is to be searched
728 * r4 = selector to search for
729 * r5 = _objc_msgForward IMP
731 * If found, returns method triplet pointer.
732 * If not found, returns NULL.
734 * NOTE: _cache_getMethod never returns any cache entry whose implementation
735 * is _objc_msgForward. It returns NULL instead. This prevents thread-
736 * safety and memory management bugs in _class_lookupMethodAndLoadCache.
737 * See _class_lookupMethodAndLoadCache for details.
739 * _objc_msgForward is passed as a parameter because it's more efficient
740 * to do the (PIC) lookup once in the caller than repeatedly here.
741 ********************************************************************/
743 .private_extern __cache_getMethod
744 ENTRY __cache_getMethod
745 ; do profiling if enabled
749 mr r12,r3 ; move class to r12 for CacheLookup
750 CacheLookup r4, LGetMethodMiss
752 ; cache hit, method triplet in r2 and imp in r12
753 cmplw r12, r5 ; check for _objc_msgForward
754 mr r3, r2 ; optimistically get the return value
755 bnelr ; Not _objc_msgForward, return the triplet address
758 li r3, 0 ; cache miss or _objc_msgForward, return nil
762 END_ENTRY __cache_getMethod
765 /********************************************************************
766 * IMP _cache_getImp(Class cls, SEL sel)
768 * On entry: r3 = class whose cache is to be searched
769 * r4 = selector to search for
771 * If found, returns method implementation.
772 * If not found, returns NULL.
773 ********************************************************************/
775 .private_extern __cache_getImp
777 ; do profiling if enabled
781 mr r12,r3 ; move class to r12 for CacheLookup
782 CacheLookup r4, LGetImpMiss
784 ; cache hit, method triplet in r2 and imp in r12
785 mr r3, r12 ; return method imp address
789 ; cache miss, return nil
790 li r3, 0 ; return nil
794 END_ENTRY __cache_getImp
797 /********************************************************************
798 * id objc_msgSend(id self,
802 * On entry: r3 is the message receiver,
804 ********************************************************************/
806 ; WARNING - This code may be copied as is to the Objective-C runtime pages.
807 ; The code is copied by rtp_set_up_objc_msgSend() from the
808 ; beginning to the blr marker just prior to the cache miss code.
809 ; Do not add callouts, global variable accesses, or rearrange
810 ; the code without updating rtp_set_up_objc_msgSend.
812 ; Absolute symbols bounding the runtime page version of objc_msgSend.
813 _objc_msgSend_rtp = 0xfffeff00
814 _objc_msgSend_rtp_exit = 0xfffeff00+0x100
816 ENTRY _objc_msgSend_fixup_rtp
817 lwz r4, 4(r4) ; load _cmd from message_ref
819 END_ENTRY _objc_msgSend_fixup_rtp
822 ; check whether receiver is nil or selector is to be ignored
823 cmplwi r3,0 ; receiver nil?
824 xoris r11,r4,((kIgnore>>16) & 0xffff) ; clear hi if equal to ignored
825 cmplwi cr1,r11,(kIgnore & 0xffff) ; selector is to be ignored?
826 beq- LMsgSendNilSelf ; if nil receiver, call handler or return nil
827 lwz r12,ISA(r3) ; class = receiver->isa
828 beqlr- cr1 ; if ignored selector, return self immediately
830 ; guaranteed non-nil entry point (disabled for now)
831 ; .globl _objc_msgSendNonNil
832 ; _objc_msgSendNonNil:
834 ; do profiling when enabled
837 ; receiver is non-nil: search the cache
839 ; class is already in r12
840 CacheLookup r4, LMsgSendCacheMiss
841 ; CacheLookup placed imp in r12
843 ; r11 guaranteed non-zero on exit from CacheLookup with a hit
844 // li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward
847 ; WARNING - The first six instructions of LMsgSendNilSelf are
848 ; rewritten when objc_msgSend is copied to the runtime pages.
849 ; These instructions must be maintained AS IS unless the code in
850 ; rtp_set_up_objc_msgSend is also updated.
851 ; * `mflr r0` must not be changed (not even to use a different register)
852 ; * the load of _objc_nilReceiver value must remain six insns long
853 ; * the value of _objc_nilReceiver must continue to be loaded into r11
855 ; message sent to nil: redirect to nil receiver, if any
857 ; DO NOT CHANGE THE NEXT SIX INSTRUCTIONS - see note above
858 mflr r0 ; save return address
859 bcl 20,31,1f ; 31 is cr7[so]
861 addis r11,r11,ha16(__objc_nilReceiver-1b)
862 lwz r11,lo16(__objc_nilReceiver-1b)(r11)
863 mtlr r0 ; restore return address
864 ; DO NOT CHANGE THE PREVIOUS SIX INSTRUCTIONS - see note above
866 cmplwi r11,0 ; return nil if no new receiver
867 beq LMsgSendReturnZero
869 mr r3,r11 ; send to new receiver
870 lwz r12,ISA(r11) ; class = receiver->isa
876 lis r12, ha16(kRTAddress_zero)
877 lfd f1, lo16(kRTAddress_zero)(r12)
878 lfd f2, lo16(kRTAddress_zero)(r12)
880 ; WARNING - This blr marks the end of the copy to the ObjC runtime pages and
881 ; also marks the beginning of the cache miss code. Do not move
882 ; around without checking the ObjC runtime pages initialization code.
885 ; cache miss: go search the method lists
887 MethodTableLookup WORD_RETURN, MSG_SEND
888 li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward
892 END_ENTRY _objc_msgSend
894 /********************************************************************
896 * double objc_msgSend_fpret(id self, SEL op, ...);
898 ********************************************************************/
900 ENTRY _objc_msgSend_fpret
902 END_ENTRY _objc_msgSend_fpret
904 /********************************************************************
905 * struct_type objc_msgSend_stret(id self,
909 * objc_msgSend_stret is the struct-return form of msgSend.
910 * The ABI calls for r3 to be used as the address of the structure
911 * being returned, with the parameters in the succeeding registers.
913 * On entry: r3 is the address where the structure is returned,
914 * r4 is the message receiver,
916 ********************************************************************/
918 ENTRY _objc_msgSend_stret_fixup_rtp
919 lwz r5, 4(r5) ; load _cmd from message_ref
920 b _objc_msgSend_stret
921 END_ENTRY _objc_msgSend_stret_fixup_rtp
923 ENTRY _objc_msgSend_stret
924 ; check whether receiver is nil
925 cmplwi r4,0 ; receiver nil?
926 beq LMsgSendStretNilSelf ; if so, call handler or just return
928 ; guaranteed non-nil entry point (disabled for now)
929 ; .globl _objc_msgSendNonNil_stret
930 ; _objc_msgSendNonNil_stret:
932 ; do profiling when enabled
935 ; receiver is non-nil: search the cache
936 LMsgSendStretReceiverOk:
937 lwz r12, ISA(r4) ; class = receiver->isa
938 CacheLookup r5, LMsgSendStretCacheMiss
939 ; CacheLookup placed imp in r12
941 li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward
944 ; cache miss: go search the method lists
945 LMsgSendStretCacheMiss:
946 MethodTableLookup STRUCT_RETURN, MSG_SEND
947 li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward
950 ; message sent to nil: redirect to nil receiver, if any
951 LMsgSendStretNilSelf:
952 mflr r0 ; load new receiver
953 bcl 20,31,1f ; 31 is cr7[so]
955 addis r11,r11,ha16(__objc_nilReceiver-1b)
956 lwz r11,lo16(__objc_nilReceiver-1b)(r11)
959 cmplwi r11,0 ; return if no new receiver
962 mr r4,r11 ; send to new receiver
963 b LMsgSendStretReceiverOk
966 END_ENTRY _objc_msgSend_stret
969 /********************************************************************
970 * id objc_msgSendSuper(struct objc_super *super,
974 * struct objc_super {
978 ********************************************************************/
980 ENTRY _objc_msgSendSuper2_fixup_rtp
981 ; objc_super->class is superclass of the class to search
983 lwz r4, 4(r4) ; load _cmd from message_ref
984 lwz r11, 4(r11) ; r11 = cls->super_class
987 END_ENTRY _objc_msgSendSuper2_fixup_rtp
989 ENTRY _objc_msgSendSuper
990 ; do profiling when enabled
993 ; check whether selector is to be ignored
994 xoris r11,r4,((kIgnore>>16) & 0xffff) ; clear hi if to be ignored
995 cmplwi r11,(kIgnore & 0xffff) ; selector is to be ignored?
996 lwz r12,CLASS(r3) ; class = super->class
997 beq- LMsgSendSuperIgnored ; if ignored, return self
1000 ; class is already in r12
1001 CacheLookup r4, LMsgSendSuperCacheMiss
1002 ; CacheLookup placed imp in r12
1004 lwz r3,RECEIVER(r3) ; receiver is the first arg
1005 ; r11 guaranteed non-zero after cache hit
1006 ; li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward
1009 ; cache miss: go search the method lists
1010 LMsgSendSuperCacheMiss:
1011 MethodTableLookup WORD_RETURN, MSG_SENDSUPER
1012 lwz r3,RECEIVER(r3) ; receiver is the first arg
1013 li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward
1016 ; ignored selector: return self
1017 LMsgSendSuperIgnored:
1022 END_ENTRY _objc_msgSendSuper
1025 /********************************************************************
1026 * struct_type objc_msgSendSuper_stret(objc_super *super,
1030 * struct objc_super {
1036 * objc_msgSendSuper_stret is the struct-return form of msgSendSuper.
1037 * The ABI calls for r3 to be used as the address of the structure
1038 * being returned, with the parameters in the succeeding registers.
1040 * On entry: r3 is the address to which to copy the returned structure,
1041 * r4 is the address of the objc_super structure,
1042 * r5 is the selector
1043 ********************************************************************/
1045 ENTRY _objc_msgSendSuper2_stret_fixup_rtp
1046 ; objc_super->class is superclass of the class to search
1048 lwz r5, 4(r5) ; load _cmd from message_ref
1049 lwz r11, 4(r11) ; r11 = cls->super_class
1051 b _objc_msgSendSuper_stret
1052 END_ENTRY _objc_msgSendSuper2_stret_fixup_rtp
1054 ENTRY _objc_msgSendSuper_stret
1055 ; do profiling when enabled
1059 lwz r12,CLASS(r4) ; class = super->class
1060 CacheLookup r5, LMsgSendSuperStretCacheMiss
1061 ; CacheLookup placed imp in r12
1063 lwz r4,RECEIVER(r4) ; receiver is the first arg
1064 li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward
1067 ; cache miss: go search the method lists
1068 LMsgSendSuperStretCacheMiss:
1069 MethodTableLookup STRUCT_RETURN, MSG_SENDSUPER
1070 lwz r4,RECEIVER(r4) ; receiver is the first arg
1071 li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward
1074 LMsgSendSuperStretExit:
1075 END_ENTRY _objc_msgSendSuper_stret
1078 /********************************************************************
1080 * Out-of-band parameter r11 indicates whether it was objc_msgSend or
1081 * objc_msgSend_stret that triggered the message forwarding. The
1083 * Iff r11 == kFwdMsgSend, it is the word-return (objc_msgSend) case,
1084 * and the interface is:
1086 * id _objc_msgForward(id self,
1090 * Iff r11 == kFwdMsgSendStret, it is the structure-return
1091 * (objc_msgSend_stret) case, and the interface is:
1093 * struct_type _objc_msgForward(id self,
1097 * There are numerous reasons why it is better to have one
1098 * _objc_msgForward, rather than adding _objc_msgForward_stret.
1099 * The best one is that _objc_msgForward is the method that
1100 * gets cached when respondsToMethod returns false, and it
1101 * wouldnt know which one to use.
1103 * Sends the message to a method having the signature
1105 * - forward:(SEL)sel :(marg_list)args;
1107 * But the marg_list is prepended with the 13 double precision
1108 * floating point registers that could be used as parameters into
1109 * the method (fortunately, the same registers are used for either
1110 * single or double precision floats). These registers are layed
1111 * down by _objc_msgForward, and picked up by _objc_msgSendv. So
1112 * the "marg_list" is actually:
1114 * typedef struct objc_sendv_margs {
1115 * double floatingPointArgs[13];
1116 * intptr_t linkageArea[6];
1117 * intptr_t registerArgs[8];
1118 * intptr_t stackArgs[variable];
1121 ********************************************************************/
1123 ; _FwdSel is @selector(forward::), set up in map_images().
1124 ; ALWAYS dereference _FwdSel to get to "forward::" !!
1127 .private_extern _FwdSel
1132 LUnkSelStr: .ascii "Does not recognize selector %s\0"
1136 .private_extern __objc_forward_handler
1137 __objc_forward_handler: .long 0
1141 .private_extern __objc_forward_stret_handler
1142 __objc_forward_stret_handler: .long 0
1145 ENTRY __objc_msgForward
1146 ; do profiling when enabled
1149 ; Check return type (stret or not)
1150 cmplwi r11,kFwdMsgSendStret
1151 beq LMsgForwardStretSel
1154 ; Call user handler, if any
1155 LOAD_STATIC_WORD r12, __objc_forward_handler, LOCAL_SYMBOL
1158 bnectr ; call _objc_forward_handler if not NULL
1160 mr r11, r3 ; r11 = receiver
1161 mr r12, r4 ; r12 = SEL
1164 LMsgForwardStretSel:
1166 ; Call user handler, if any
1167 LOAD_STATIC_WORD r12, __objc_forward_stret_handler, LOCAL_SYMBOL
1170 bnectr ; call _objc_forward_stret_handler if not NULL
1172 mr r11, r4 ; r11 = receiver
1173 mr r12, r5 ; r12 = SEL
1176 ; r11 is the receiver
1177 ; r12 is the selector
1179 ; Die if forwarding "forward::"
1180 LOAD_STATIC_WORD r2, _FwdSel, LOCAL_SYMBOL
1182 beq LMsgForwardError
1184 ; Save registers to margs
1214 ; Call [receiver forward:sel :margs]
1215 mr r3, r11 ; receiver
1216 mr r4, r2 ; forward::
1218 subi r6,r1,13*8 ; &margs (on stack)
1220 stwu r1,-56-(13*8)(r1) ; push stack frame
1221 bl _objc_msgSend ; [self forward:sel :objc_sendv_margs]
1222 addi r1,r1,56+13*8 ; pop stack frame
1224 lwz r0,8(r1) ; restore lr
1229 ; Call __objc_error(receiver, "unknown selector %s", "forward::")
1231 LEA_STATIC_DATA r4, LUnkSelStr, LOCAL_SYMBOL
1233 CALL_EXTERN(___objc_error) ; never returns
1236 END_ENTRY __objc_msgForward
1239 /********************************************************************
1240 * id objc_msgSendv(id self,
1242 * unsigned arg_size,
1243 * marg_list arg_frame);
1245 * But the marg_list is prepended with the 13 double precision
1246 * floating point registers that could be used as parameters into
1247 * the method (fortunately, the same registers are used for either
1248 * single or double precision floats). These registers are layed
1249 * down by _objc_msgForward, and picked up by _objc_msgSendv. So
1250 * the "marg_list" is actually:
1252 * typedef struct objc_sendv_margs {
1253 * double floatingPointArgs[13];
1254 * int linkageArea[6];
1255 * int registerArgs[8];
1256 * int stackArgs[variable];
1259 * arg_size is the number of bytes of parameters in registerArgs and
1260 * stackArgs combined (i.e. it is method_getSizeOfArguments(method)).
1261 * Specifically, it is NOT the overall arg_frame size, because that
1262 * would include the floatingPointArgs and linkageArea, which are
1263 * PowerPC-specific. This is consistent with the other architectures.
1264 ********************************************************************/
1266 ENTRY _objc_msgSendv
1268 #if !defined(KERNEL)
1269 ; do profiling when enabled
1273 stw r0,8(r1) ; save lr
1275 cmplwi r5,32 ; check parameter size against minimum
1276 ble+ LMsgSendvMinFrame ; is less than minimum, go use minimum
1277 mr r12,r1 ; remember current stack pointer
1278 sub r11,r1,r5 ; push parameter area
1279 rlwinm r1,r11,0,0,27 ; align stack pointer to 16 byte boundary
1280 stwu r12,-32(r1) ; push aligned linkage area, set stack link
1281 b LMsgSendvHaveFrame
1284 stwu r1,-64(r1) ; push aligned linkage and parameter areas, set stack link
1287 ; restore floating point register parameters from marg_list
1302 ; load the register based arguments from the marg_list
1303 ; the first two parameters are already in r3 and r4, respectively
1304 subi r0,r5,(2*4)-3 ; make word count from byte count rounded up to multiple of 4...
1305 srwi. r0,r0,2 ; ... and subtracting for params already in r3 and r4
1306 beq LMsgSendvSendIt ; branch if there are no parameters to load
1307 mtctr r0 ; counter = number of remaining words
1308 lwz r5,32+(13*8)(r6) ; load 3rd parameter
1309 bdz LMsgSendvSendIt ; decrement counter, branch if result is zero
1310 addi r11,r6,36+(13*8) ; switch to r11, because we are setting r6
1311 lwz r6,0(r11) ; load 4th parameter
1312 bdz LMsgSendvSendIt ; decrement counter, branch if result is zero
1313 lwz r7,4(r11) ; load 5th parameter
1314 bdz LMsgSendvSendIt ; decrement counter, branch if result is zero
1315 lwz r8,8(r11) ; load 6th parameter
1316 bdz LMsgSendvSendIt ; decrement counter, branch if result is zero
1317 lwz r9,12(r11) ; load 7th parameter
1318 bdz LMsgSendvSendIt ; decrement counter, branch if result is zero
1319 lwzu r10,16(r11) ; load 8th parameter, and update r11
1320 bdz LMsgSendvSendIt ; decrement counter, branch if result is zero
1322 ; copy the stack based arguments from the marg_list
1323 addi r12,r1,24+32-4 ; target = address of stack based parameters
1325 lwzu r0,4(r11) ; loop to copy remaining marg_list words to stack
1327 bdnz LMsgSendvArgLoop ; decrement counter, branch if still non-zero
1330 bl _objc_msgSend ; objc_msgSend (self, selector, ...)
1332 lwz r1,0(r1) ; restore stack pointer
1333 lwz r0,8(r1) ; restore lr
1337 trap ; _objc_msgSendv is not for the kernel
1340 END_ENTRY _objc_msgSendv
1342 /********************************************************************
1343 * double objc_msgSendv_fpret(id self, SEL op, unsigned arg_size,
1344 * marg_list arg_frame);
1345 ********************************************************************/
1347 ENTRY _objc_msgSendv_fpret
1349 END_ENTRY _objc_msgSendv_fpret
1351 /********************************************************************
1352 * void objc_msgSendv_stret(void *structStorage,
1355 * unsigned arg_size,
1356 * marg_list arg_frame);
1358 * objc_msgSendv_stret is the struct-return form of msgSendv.
1359 * This function does not use the struct-return ABI; instead, the
1360 * structure return address is passed as a normal parameter.
1361 * The two are functionally identical on ppc, but not on other architectures.
1363 * On entry: r3 is the address in which the returned struct is put,
1364 * r4 is the message receiver,
1365 * r5 is the selector,
1366 * r6 is the size of the marg_list, in bytes,
1367 * r7 is the address of the marg_list
1368 ********************************************************************/
1370 ENTRY _objc_msgSendv_stret
1372 #if !defined(KERNEL)
1373 ; do profiling when enabled
1377 stw r0,8(r1) ; (save return pc)
1379 cmplwi r6,32 ; check parameter size against minimum
1380 ble+ LMsgSendvStretMinFrame ; is less than minimum, go use minimum
1381 mr r12,r1 ; remember current stack pointer
1382 sub r11,r1,r6 ; push parameter area
1383 rlwinm r1,r11,0,0,27 ; align stack pointer to 16 byte boundary
1384 stwu r12,-32(r1) ; push aligned linkage area, set stack link
1385 b LMsgSendvStretHaveFrame
1387 LMsgSendvStretMinFrame:
1388 stwu r1,-64(r1) ; push aligned linkage and parameter areas, set stack link
1390 LMsgSendvStretHaveFrame:
1391 ; restore floating point register parameters from marg_list
1406 ; load the register based arguments from the marg_list
1407 ; the structure return address and the first two parameters
1408 ; are already in r3, r4, and r5, respectively.
1409 ; NOTE: The callers r3 probably, but not necessarily, matches
1410 ; the r3 in the marg_list. That is, the struct-return
1411 ; storage used by the caller could be an intermediate buffer
1412 ; that will end up being copied into the original
1413 ; struct-return buffer (pointed to by the marg_listed r3).
1414 subi r0,r6,(3*4)-3 ; make word count from byte count rounded up to multiple of 4...
1415 srwi. r0,r0,2 ; ... and subtracting for params already in r3 and r4 and r5
1416 beq LMsgSendvStretSendIt ; branch if there are no parameters to load
1417 mtctr r0 ; counter = number of remaining words
1418 lwz r6,36+(13*8)(r7) ; load 4th parameter
1419 bdz LMsgSendvStretSendIt ; decrement counter, branch if result is zero
1420 addi r11,r7,40+(13*8) ; switch to r11, because we are setting r7
1421 lwz r7,0(r11) ; load 5th parameter
1422 bdz LMsgSendvStretSendIt ; decrement counter, branch if result is zero
1423 lwz r8,4(r11) ; load 6th parameter
1424 bdz LMsgSendvStretSendIt ; decrement counter, branch if result is zero
1425 lwz r9,8(r11) ; load 7th parameter
1426 bdz LMsgSendvStretSendIt ; decrement counter, branch if result is zero
1427 lwzu r10,12(r11) ; load 8th parameter, and update r11
1428 bdz LMsgSendvStretSendIt ; decrement counter, branch if result is zero
1430 ; copy the stack based arguments from the marg_list
1431 addi r12,r1,24+32-4 ; target = address of stack based parameters
1432 LMsgSendvStretArgLoop:
1433 lwzu r0,4(r11) ; loop to copy remaining marg_list words to stack
1435 bdnz LMsgSendvStretArgLoop ; decrement counter, branch if still non-zero
1437 LMsgSendvStretSendIt:
1438 bl _objc_msgSend_stret ; struct_type objc_msgSend_stret (self, selector, ...)
1440 lwz r1,0(r1) ; restore stack pointer
1441 lwz r0,8(r1) ; restore return pc
1445 trap ; _objc_msgSendv_stret is not for the kernel
1446 #endif /* !KERNEL */
1448 END_ENTRY _objc_msgSendv_stret
1451 ENTRY _method_invoke
1453 lwz r12, METHOD_IMP(r4)
1454 lwz r4, METHOD_NAME(r4)
1458 END_ENTRY _method_invoke
1461 ENTRY _method_invoke_stret
1463 lwz r12, METHOD_IMP(r5)
1464 lwz r5, METHOD_NAME(r5)
1468 END_ENTRY _method_invoke_stret