objc-msg-simulator-i386.s [plain text]
/*
* Copyright (c) 1999-2009 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* 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@
*/
#include <TargetConditionals.h>
#if defined(__i386__) && TARGET_IPHONE_SIMULATOR
#define __OBJC2__ 1
#include "objc-config.h"
.data
// Substitute receiver for messages sent to nil (usually also nil)
// id _objc_nilReceiver
.align 4
.private_extern __objc_nilReceiver
__objc_nilReceiver:
.long 0
// _objc_entryPoints and _objc_exitPoints are used by objc
// to get the critical regions for which method caches
// cannot be garbage collected.
.private_extern _objc_entryPoints
_objc_entryPoints:
.long __cache_getImp
.long __cache_getMethod
.long _objc_msgSend
.long _objc_msgSend_fpret
.long _objc_msgSend_stret
.long _objc_msgSendSuper
.long _objc_msgSendSuper2
.long _objc_msgSendSuper_stret
.long _objc_msgSendSuper2_stret
.long 0
.private_extern _objc_exitPoints
_objc_exitPoints:
.long LGetImpExit
.long LGetMethodExit
.long LMsgSendExit
.long LMsgSendFpretExit
.long LMsgSendStretExit
.long LMsgSendSuperExit
.long LMsgSendSuper2Exit
.long LMsgSendSuperStretExit
.long LMsgSendSuper2StretExit
.long 0
/********************************************************************
*
* Structure definitions.
*
********************************************************************/
// Offsets from %esp
self = 4
super = 4
selector = 8
marg_size = 12
marg_list = 16
first_arg = 12
struct_addr = 4
self_stret = 8
super_stret = 8
selector_stret = 12
marg_size_stret = 16
marg_list_stret = 20
// objc_super parameter to sendSuper
receiver = 0
class = 4
// Selected field offsets in class structure
isa = 0
superclass = 4
#if __OBJC2__
cache = 8
#else
cache = 32
#endif
// Method descriptor
method_name = 0
method_imp = 8
// Cache header
mask = 0
occupied = 4
buckets = 8 // variable length array
//////////////////////////////////////////////////////////////////////
//
// ENTRY functionName
//
// Assembly directives to begin an exported function.
//
// Takes: functionName - name of the exported function
//////////////////////////////////////////////////////////////////////
.macro ENTRY
.text
.globl $0
.align 2, 0x90
$0:
.endmacro
.macro STATIC_ENTRY
.text
.private_extern $0
.align 4, 0x90
$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
.endmacro
/////////////////////////////////////////////////////////////////////
//
//
// CacheLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER | MSG_SENDSUPER2 | CACHE_GET, cacheMissLabel
//
// Locate the implementation for a selector in a class method cache.
//
// Takes: WORD_RETURN (first parameter is at sp+4)
// STRUCT_RETURN (struct address is at sp+4, first parameter at sp+8)
// MSG_SEND (first parameter is receiver)
// MSG_SENDSUPER[2] (first parameter is address of objc_super structure)
// CACHE_GET (first parameter is class// class to search in %edx
//
// cacheMissLabel = label to branch to iff method is not cached
//
// On exit: (found) MSG_SEND and MSG_SENDSUPER[2]: return imp in eax
// (found) CACHE_GET: return method triplet in eax
// (not found) jumps to cacheMissLabel
//
/////////////////////////////////////////////////////////////////////
// Values to specify to method lookup macros whether the return type of
// the method is word or structure.
WORD_RETURN = 0
STRUCT_RETURN = 1
// Values to specify to method lookup macros whether the first argument
// is an object/class reference or a 'objc_super' structure.
MSG_SEND = 0 // first argument is receiver, search the isa
MSG_SENDSUPER = 1 // first argument is objc_super, search the class
MSG_SENDSUPER2 = 2 // first argument is objc_super, search the class
CACHE_GET = 3 // first argument is class, search that class
.macro CacheLookup
// load variables and save caller registers.
pushl %edi // save scratch register
movl cache(%edx), %edi // cache = class->cache
pushl %esi // save scratch register
movl mask(%edi), %esi // mask = cache->mask
movl %ecx, %edx // index = selector
shrl $$2, %edx // index = selector >> 2
// search the receiver's cache
// ecx = selector
// edi = cache
// esi = mask
// edx = index
// eax = method (soon)
LMsgSendProbeCache_$0_$1_$2:
andl %esi, %edx // index &= mask
movl buckets(%edi, %edx, 4), %eax // meth = cache->buckets[index]
testl %eax, %eax // check for end of bucket
je LMsgSendCacheMiss_$0_$1_$2 // go to cache miss code
cmpl method_name(%eax), %ecx // check for method name match
je LMsgSendCacheHit_$0_$1_$2 // go handle cache hit
addl $$1, %edx // bump index ...
jmp LMsgSendProbeCache_$0_$1_$2 // ... and loop
// not found in cache: restore state and go to callers handler
LMsgSendCacheMiss_$0_$1_$2:
.if $0 == WORD_RETURN // Regular word return
.if $1 == MSG_SEND // MSG_SEND
popl %esi // restore callers register
popl %edi // restore callers register
movl self(%esp), %edx // get messaged object
movl isa(%edx), %eax // get objects class
.elseif $1 == MSG_SENDSUPER || $1 == MSG_SENDSUPER2 // MSG_SENDSUPER[2]
// replace "super" arg with "receiver"
movl super+8(%esp), %edi // get super structure
movl receiver(%edi), %edx // get messaged object
movl %edx, super+8(%esp) // make it the first argument
movl class(%edi), %eax // get messaged class
.if $1 == MSG_SENDSUPER2
movl superclass(%eax), %eax // get messaged class
.endif
popl %esi // restore callers register
popl %edi // restore callers register
.else // CACHE_GET
popl %esi // restore callers register
popl %edi // restore callers register
.endif
.else // Struct return
.if $1 == MSG_SEND // MSG_SEND (stret)
popl %esi // restore callers register
popl %edi // restore callers register
movl self_stret(%esp), %edx // get messaged object
movl isa(%edx), %eax // get objects class
.elseif $1 == MSG_SENDSUPER || $1 == MSG_SENDSUPER2 // MSG_SENDSUPER[2] (stret)
// replace "super" arg with "receiver"
movl super_stret+8(%esp), %edi// get super structure
movl receiver(%edi), %edx // get messaged object
movl %edx, super_stret+8(%esp)// make it the first argument
movl class(%edi), %eax // get messaged class
.if $1 == MSG_SENDSUPER2
movl superclass(%eax), %eax // get messaged class
.endif
popl %esi // restore callers register
popl %edi // restore callers register
.else // CACHE_GET
!! This should not happen.
.endif
.endif
// edx = receiver
// ecx = selector
// eax = class
jmp $2 // go to callers handler
// eax points to matching cache entry
.align 4, 0x90
LMsgSendCacheHit_$0_$1_$2:
// load implementation address, restore state, and we're done
.if $1 == CACHE_GET
// method triplet is already in eax
.else
movl method_imp(%eax), %eax // imp = method->method_imp
.endif
.if $0 == WORD_RETURN // Regular word return
.if $1 == MSG_SENDSUPER || $1 == MSG_SENDSUPER2
// replace "super" arg with "self"
movl super+8(%esp), %edi
movl receiver(%edi), %esi
movl %esi, super+8(%esp)
.endif
.else // Struct return
.if $1 == MSG_SENDSUPER || $1 == MSG_SENDSUPER2
// replace "super" arg with "self"
movl super_stret+8(%esp), %edi
movl receiver(%edi), %esi
movl %esi, super_stret+8(%esp)
.endif
.endif
// restore caller registers
popl %esi
popl %edi
.endmacro
/////////////////////////////////////////////////////////////////////
//
// MethodTableLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER
//
// Takes: WORD_RETURN (first parameter is at sp+4)
// STRUCT_RETURN (struct address is at sp+4, first parameter at sp+8)
// MSG_SEND (first parameter is receiver)
// MSG_SENDSUPER (first parameter is address of objc_super structure)
//
// edx = receiver
// ecx = selector
// eax = class
// (all set by CacheLookup's miss case)
//
// Stack must be at 0xXXXXXXXc on entrance.
//
// On exit: esp unchanged
// imp in eax
//
/////////////////////////////////////////////////////////////////////
.macro MethodTableLookup
// stack is already aligned
pushl %eax // class
pushl %ecx // selector
pushl %edx // receiver
call __class_lookupMethodAndLoadCache3
addl $$12, %esp // pop parameters
.endmacro
/********************************************************************
* Method _cache_getMethod(Class cls, SEL sel, IMP 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 1 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.
********************************************************************/
.private_extern __cache_getMethod
ENTRY __cache_getMethod
// load the class and selector
movl selector(%esp), %ecx
movl self(%esp), %edx
// do lookup
CacheLookup WORD_RETURN, CACHE_GET, LGetMethodMiss
// cache hit, method triplet in %eax
movl first_arg(%esp), %ecx // check for _objc_msgForward_internal
cmpl method_imp(%eax), %ecx // if (imp==_objc_msgForward_internal)
je 1f // return (Method)1
ret // else return method triplet address
1: movl $1, %eax
ret
LGetMethodMiss:
// cache miss, return nil
xorl %eax, %eax // zero %eax
ret
LGetMethodExit:
END_ENTRY __cache_getMethod
/********************************************************************
* IMP _cache_getImp(Class cls, SEL sel)
*
* If found, returns method implementation.
* If not found, returns NULL.
********************************************************************/
.private_extern __cache_getImp
ENTRY __cache_getImp
// load the class and selector
movl selector(%esp), %ecx
movl self(%esp), %edx
// do lookup
CacheLookup WORD_RETURN, CACHE_GET, LGetImpMiss
// cache hit, method triplet in %eax
movl method_imp(%eax), %eax // return method imp
ret
LGetImpMiss:
// cache miss, return nil
xorl %eax, %eax // zero %eax
ret
LGetImpExit:
END_ENTRY __cache_getImp
/********************************************************************
*
* id objc_msgSend(id self, SEL _cmd,...) ********************************************************************/
ENTRY _objc_msgSend
// load receiver and selector
movl selector(%esp), %ecx
movl self(%esp), %eax
#if SUPPORT_IGNORED_SELECTOR_CONSTANT
// check whether selector is ignored
cmpl $ kIgnore, %ecx
je LMsgSendDone // return self from %eax
#endif
// check whether receiver is nil
testl %eax, %eax
je LMsgSendNilSelf
// receiver (in %eax) is non-nil: search the cache
LMsgSendReceiverOk:
movl isa(%eax), %edx // class = self->isa
CacheLookup WORD_RETURN, MSG_SEND, LMsgSendCacheMiss
xor %edx, %edx // set nonstret for msgForward_internal
jmp *%eax
// cache miss: go search the method lists
LMsgSendCacheMiss:
MethodTableLookup WORD_RETURN, MSG_SEND
xor %edx, %edx // set nonstret for msgForward_internal
jmp *%eax // goto *imp
// message sent to nil: redirect to nil receiver, if any
LMsgSendNilSelf:
call 1f // load new receiver
1: popl %edx
movl __objc_nilReceiver-1b(%edx),%eax
testl %eax, %eax // return nil if no new receiver
je LMsgSendReturnZero
movl %eax, self(%esp) // send to new receiver
jmp LMsgSendReceiverOk // receiver must be in %eax
LMsgSendReturnZero:
// %eax is already zero
movl $0,%edx
LMsgSendDone:
ret
LMsgSendExit:
END_ENTRY _objc_msgSend
/********************************************************************
*
* id objc_msgSendSuper(struct objc_super *super, SEL _cmd,...) * struct objc_super {
* id receiver * }
ENTRY _objc_msgSendSuper
// load selector and class to search
movl super(%esp), %eax // struct objc_super
movl selector(%esp), %ecx
movl class(%eax), %edx // struct objc_super->class
#if SUPPORT_IGNORED_SELECTOR_CONSTANT
// check whether selector is ignored
cmpl $ kIgnore, %ecx
je LMsgSendSuperIgnored // return self from %eax
#endif
// search the cache (class in %edx)
CacheLookup WORD_RETURN, MSG_SENDSUPER, LMsgSendSuperCacheMiss
xor %edx, %edx // set nonstret for msgForward_internal
jmp *%eax // goto *imp
// cache miss: go search the method lists
LMsgSendSuperCacheMiss:
MethodTableLookup WORD_RETURN, MSG_SENDSUPER
xor %edx, %edx // set nonstret for msgForward_internal
jmp *%eax // goto *imp
// ignored selector: return self
LMsgSendSuperIgnored:
movl super(%esp), %eax
movl receiver(%eax), %eax
ret
LMsgSendSuperExit:
END_ENTRY _objc_msgSendSuper
ENTRY _objc_msgSendSuper2
// load selector and class to search
movl super(%esp), %eax // struct objc_super
movl selector(%esp), %ecx
movl class(%eax), %eax // struct objc_super->class
mov superclass(%eax), %edx // edx = objc_super->class->super_class
#if SUPPORT_IGNORED_SELECTOR_CONSTANT
// check whether selector is ignored
cmpl $ kIgnore, %ecx
je LMsgSendSuperIgnored // return self from %eax
#endif
// search the cache (class in %edx)
CacheLookup WORD_RETURN, MSG_SENDSUPER2, LMsgSendSuper2CacheMiss
xor %edx, %edx // set nonstret for msgForward_internal
jmp *%eax // goto *imp
// cache miss: go search the method lists
LMsgSendSuper2CacheMiss:
MethodTableLookup WORD_RETURN, MSG_SENDSUPER2
xor %edx, %edx // set nonstret for msgForward_internal
jmp *%eax // goto *imp
// ignored selector: return self
LMsgSendSuper2Ignored:
movl super(%esp), %eax
movl receiver(%eax), %eax
ret
LMsgSendSuper2Exit:
END_ENTRY _objc_msgSendSuper2
/********************************************************************
*
* double objc_msgSend_fpret(id self, SEL _cmd,...) ********************************************************************/
ENTRY _objc_msgSend_fpret
// load receiver and selector
movl selector(%esp), %ecx
movl self(%esp), %eax
#if SUPPORT_IGNORED_SELECTOR_CONSTANT
// check whether selector is ignored
cmpl $ kIgnore, %ecx
je LMsgSendFpretDone // return self from %eax
#endif
// check whether receiver is nil
testl %eax, %eax
je LMsgSendFpretNilSelf
// receiver (in %eax) is non-nil: search the cache
LMsgSendFpretReceiverOk:
movl isa(%eax), %edx // class = self->isa
CacheLookup WORD_RETURN, MSG_SEND, LMsgSendFpretCacheMiss
xor %edx, %edx // set nonstret for msgForward_internal
jmp *%eax // goto *imp
// cache miss: go search the method lists
LMsgSendFpretCacheMiss:
MethodTableLookup WORD_RETURN, MSG_SEND
xor %edx, %edx // set nonstret for msgForward_internal
jmp *%eax // goto *imp
// message sent to nil: redirect to nil receiver, if any
LMsgSendFpretNilSelf:
call 1f // load new receiver
1: popl %edx
movl __objc_nilReceiver-1b(%edx),%eax
testl %eax, %eax // return zero if no new receiver
je LMsgSendFpretReturnZero
movl %eax, self(%esp) // send to new receiver
jmp LMsgSendFpretReceiverOk // receiver must be in %eax
LMsgSendFpretReturnZero:
fldz
LMsgSendFpretDone:
ret
LMsgSendFpretExit:
END_ENTRY _objc_msgSend_fpret
/********************************************************************
*
* void objc_msgSend_stret(void *st_addr , id self, SEL _cmd, ...) *
* objc_msgSend_stret is the struct-return form of msgSend.
* The ABI calls for (sp+4) to be used as the address of the structure
* being returned, with the parameters in the succeeding locations.
*
* On entry: (sp+4)is the address where the structure is returned,
* (sp+8) is the message receiver,
* (sp+12) is the selector
********************************************************************/
ENTRY _objc_msgSend_stret
// load receiver and selector
movl self_stret(%esp), %eax
movl (selector_stret)(%esp), %ecx
// check whether receiver is nil
testl %eax, %eax
je LMsgSendStretNilSelf
// receiver (in %eax) is non-nil: search the cache
LMsgSendStretReceiverOk:
movl isa(%eax), %edx // class = self->isa
CacheLookup STRUCT_RETURN, MSG_SEND, LMsgSendStretCacheMiss
movl $1, %edx // set stret for objc_msgForward
jmp *%eax // goto *imp
// cache miss: go search the method lists
LMsgSendStretCacheMiss:
MethodTableLookup STRUCT_RETURN, MSG_SEND
movl $1, %edx // set stret for objc_msgForward
jmp *%eax // goto *imp
// message sent to nil: redirect to nil receiver, if any
LMsgSendStretNilSelf:
call 1f // load new receiver
1: popl %edx
movl __objc_nilReceiver-1b(%edx),%eax
testl %eax, %eax // return nil if no new receiver
je LMsgSendStretDone
movl %eax, self_stret(%esp) // send to new receiver
jmp LMsgSendStretReceiverOk // receiver must be in %eax
LMsgSendStretDone:
ret $4 // pop struct return address (#2995932)
LMsgSendStretExit:
END_ENTRY _objc_msgSend_stret
/********************************************************************
*
* void objc_msgSendSuper_stret(void *st_addr, struct objc_super *super, SEL _cmd, ...) * struct objc_super {
* id receiver * } * objc_msgSendSuper_stret is the struct-return form of msgSendSuper.
* The ABI calls for (sp+4) to be used as the address of the structure
* being returned, with the parameters in the succeeding registers.
*
* On entry: (sp+4)is the address where the structure is returned,
* (sp+8) is the address of the objc_super structure,
* (sp+12) is the selector
*
********************************************************************/
ENTRY _objc_msgSendSuper_stret
// load selector and class to search
movl super_stret(%esp), %eax // struct objc_super
movl (selector_stret)(%esp), %ecx // get selector
movl class(%eax), %edx // struct objc_super->class
// search the cache (class in %edx)
CacheLookup STRUCT_RETURN, MSG_SENDSUPER, LMsgSendSuperStretCacheMiss
movl $1, %edx // set stret for objc_msgForward
jmp *%eax // goto *imp
// cache miss: go search the method lists
LMsgSendSuperStretCacheMiss:
MethodTableLookup STRUCT_RETURN, MSG_SENDSUPER
movl $1, %edx // set stret for objc_msgForward
jmp *%eax // goto *imp
LMsgSendSuperStretExit:
END_ENTRY _objc_msgSendSuper_stret
ENTRY _objc_msgSendSuper2_stret
// load selector and class to search
movl super_stret(%esp), %eax // struct objc_super
movl (selector_stret)(%esp), %ecx // get selector
movl class(%eax), %eax // struct objc_super->class
mov superclass(%eax), %edx // edx = objc_super->class->super_class
// search the cache (class in %edx)
CacheLookup STRUCT_RETURN, MSG_SENDSUPER2, LMsgSendSuper2StretCacheMiss
movl $1, %edx // set stret for objc_msgForward
jmp *%eax // goto *imp
// cache miss: go search the method lists
LMsgSendSuper2StretCacheMiss:
MethodTableLookup STRUCT_RETURN, MSG_SENDSUPER2
movl $1, %edx // set stret for objc_msgForward
jmp *%eax // goto *imp
LMsgSendSuper2StretExit:
END_ENTRY _objc_msgSendSuper2_stret
/********************************************************************
*
* id _objc_msgForward(id self, SEL _cmd,...) ********************************************************************/
// _FwdSel is @selector(forward::), set up in map_images().
// ALWAYS dereference _FwdSel to get to "forward::" !!
.data
.align 2
.private_extern _FwdSel
_FwdSel: .long 0
.cstring
.align 2
LUnkSelStr: .ascii "Does not recognize selector %s\0"
.data
.align 2
.private_extern __objc_forward_handler
__objc_forward_handler: .long 0
.data
.align 2
.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 register %edx is nonzero for stret, zero otherwise
// Check return type (stret or not)
testl %edx, %edx
jnz __objc_msgForward_stret
jmp __objc_msgForward
END_ENTRY _objc_msgForward_internal
ENTRY __objc_msgForward
// Non-struct return version
// Get PIC base into %edx
call L__objc_msgForward$pic_base
L__objc_msgForward$pic_base:
popl %edx
// Call user handler, if any
movl __objc_forward_handler-L__objc_msgForward$pic_base(%edx),%ecx
testl %ecx, %ecx // if not NULL
je 1f // skip to default handler
jmp *%ecx // call __objc_forward_handler
1:
// No user handler
// Push stack frame
pushl %ebp
movl %esp, %ebp
// Die if forwarding "forward::"
movl (selector+4)(%ebp), %eax
movl _FwdSel-L__objc_msgForward$pic_base(%edx),%ecx
cmpl %ecx, %eax
je LMsgForwardError
// Call [receiver forward:sel :margs]
subl $8, %esp // 16-byte align the stack
leal (self+4)(%ebp), %ecx
pushl %ecx // &margs
pushl %eax // sel
movl _FwdSel-L__objc_msgForward$pic_base(%edx),%ecx
pushl %ecx // forward::
pushl (self+4)(%ebp) // receiver
call _objc_msgSend
movl %ebp, %esp
popl %ebp
ret
LMsgForwardError:
// Call __objc_error(receiver, "unknown selector %s", "forward::")
subl $12, %esp // 16-byte align the stack
movl _FwdSel-L__objc_msgForward$pic_base(%edx),%eax
pushl %eax
leal LUnkSelStr-L__objc_msgForward$pic_base(%edx),%eax
pushl %eax
pushl (self+4)(%ebp)
call ___objc_error // never returns
END_ENTRY __objc_msgForward
ENTRY __objc_msgForward_stret
// Struct return version
// Get PIC base into %edx
call L__objc_msgForwardStret$pic_base
L__objc_msgForwardStret$pic_base:
popl %edx
// Call user handler, if any
movl __objc_forward_stret_handler-L__objc_msgForwardStret$pic_base(%edx), %ecx
testl %ecx, %ecx // if not NULL
je 1f // skip to default handler
jmp *%ecx // call __objc_forward_stret_handler
1:
// No user handler
// Push stack frame
pushl %ebp
movl %esp, %ebp
// Die if forwarding "forward::"
movl (selector_stret+4)(%ebp), %eax
movl _FwdSel-L__objc_msgForwardStret$pic_base(%edx), %ecx
cmpl %ecx, %eax
je LMsgForwardStretError
// Call [receiver forward:sel :margs]
subl $8, %esp // 16-byte align the stack
leal (self_stret+4)(%ebp), %ecx
pushl %ecx // &margs
pushl %eax // sel
movl _FwdSel-L__objc_msgForwardStret$pic_base(%edx),%ecx
pushl %ecx // forward::
pushl (self_stret+4)(%ebp) // receiver
call _objc_msgSend
movl %ebp, %esp
popl %ebp
ret $4 // pop struct return address (#2995932)
LMsgForwardStretError:
// Call __objc_error(receiver, "unknown selector %s", "forward::")
subl $12, %esp // 16-byte align the stack
leal _FwdSel-L__objc_msgForwardStret$pic_base(%edx),%eax
pushl %eax
leal LUnkSelStr-L__objc_msgForwardStret$pic_base(%edx),%eax
pushl %eax
pushl (self_stret+4)(%ebp)
call ___objc_error // never returns
END_ENTRY __objc_msgForward_stret
ENTRY _objc_msgSend_debug
jmp _objc_msgSend
END_ENTRY _objc_msgSend_debug
ENTRY _objc_msgSendSuper2_debug
jmp _objc_msgSendSuper2
END_ENTRY _objc_msgSendSuper2_debug
ENTRY _objc_msgSend_stret_debug
jmp _objc_msgSend_stret
END_ENTRY _objc_msgSend_stret_debug
ENTRY _objc_msgSendSuper2_stret_debug
jmp _objc_msgSendSuper2_stret
END_ENTRY _objc_msgSendSuper2_stret_debug
ENTRY _objc_msgSend_fpret_debug
jmp _objc_msgSend_fpret
END_ENTRY _objc_msgSend_fpret_debug
ENTRY _objc_msgSend_noarg
jmp _objc_msgSend
END_ENTRY _objc_msgSend_noarg
ENTRY _method_invoke
movl selector(%esp), %ecx
movl method_name(%ecx), %edx
movl method_imp(%ecx), %eax
movl %edx, selector(%esp)
jmp *%eax
END_ENTRY _method_invoke
ENTRY _method_invoke_stret
movl selector_stret(%esp), %ecx
movl method_name(%ecx), %edx
movl method_imp(%ecx), %eax
movl %edx, selector_stret(%esp)
jmp *%eax
END_ENTRY _method_invoke_stret
#if !defined(NDEBUG)
STATIC_ENTRY __objc_ignored_method
movl self(%esp), %eax
ret
END_ENTRY __objc_ignored_method
#endif
#endif