/*
* Copyright (c) 1999-2007 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_OS_SIMULATOR
/********************************************************************
********************************************************************
**
** objc-msg-i386.s - i386 code to support objc messaging.
**
********************************************************************
********************************************************************/
/********************************************************************
* Data used by the ObjC runtime.
*
********************************************************************/
.data
// _objc_entryPoints and _objc_exitPoints are used by objc
// to get the critical regions for which method caches
// cannot be garbage collected.
.align 2
.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_msgSendSuper_stret
.long 0
.private_extern _objc_exitPoints
_objc_exitPoints:
.long LGetImpExit
.long LGetMethodExit
.long LMsgSendExit
.long LMsgSendFpretExit
.long LMsgSendStretExit
.long LMsgSendSuperExit
.long LMsgSendSuperStretExit
.long 0
/********************************************************************
*
* Common offsets.
*
********************************************************************/
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
/********************************************************************
*
* Structure definitions.
*
********************************************************************/
// objc_super parameter to sendSuper
receiver = 0
class = 4
// Selected field offsets in class structure
isa = 0
cache = 32
// Method descriptor
method_name = 0
method_imp = 8
// Cache header
mask = 0
occupied = 4
buckets = 8 // variable length array
#if defined(OBJC_INSTRUMENTED)
// Cache instrumentation data, follows buckets
hitCount = 0
hitProbes = hitCount + 4
maxHitProbes = hitProbes + 4
missCount = maxHitProbes + 4
missProbes = missCount + 4
maxMissProbes = missProbes + 4
flushCount = maxMissProbes + 4
flushedEntries = flushCount + 4
// Buckets in CacheHitHistogram and CacheMissHistogram
CACHE_HISTOGRAM_SIZE = 512
#endif
//////////////////////////////////////////////////////////////////////
//
// ENTRY functionName
//
// Assembly directives to begin an exported function.
//
// Takes: functionName - name of the exported function
//////////////////////////////////////////////////////////////////////
.macro ENTRY
.text
.globl $0
.align 4, 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
//////////////////////////////////////////////////////////////////////
//
// CALL_MCOUNTER
//
// Calls mcount() profiling routine. Must be called immediately on
// function entry, before any prologue executes.
//
//////////////////////////////////////////////////////////////////////
.macro CALL_MCOUNTER
#ifdef PROFILE
// Current stack contents: ret
pushl %ebp
movl %esp,%ebp
subl $$8,%esp
// Current stack contents: ret, ebp, pad, pad
call mcount
movl %ebp,%esp
popl %ebp
#endif
.endmacro
/////////////////////////////////////////////////////////////////////
//
//
// CacheLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER | 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 (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: 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
CACHE_GET = 2 // 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
#if defined(OBJC_INSTRUMENTED)
pushl %ebx // save non-volatile register
pushl %eax // save cache pointer
xorl %ebx, %ebx // probeCount = 0
#endif
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:
#if defined(OBJC_INSTRUMENTED)
addl $$1, %ebx // probeCount += 1
#endif
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 defined(OBJC_INSTRUMENTED)
popl %edx // retrieve cache pointer
movl mask(%edx), %esi // mask = cache->mask
testl %esi, %esi // a mask of zero is only for the...
je LMsgSendMissInstrumentDone_$0_$1_$2 // ... emptyCache, do not record anything
// locate and update the CacheInstrumentation structure
addl $$1, %esi // entryCount = mask + 1
shll $$2, %esi // tableSize = entryCount * sizeof(entry)
addl $buckets, %esi // offset = buckets + tableSize
addl %edx, %esi // cacheData = &cache->buckets[mask+1]
movl missCount(%esi), %edi //
addl $$1, %edi //
movl %edi, missCount(%esi) // cacheData->missCount += 1
movl missProbes(%esi), %edi //
addl %ebx, %edi //
movl %edi, missProbes(%esi) // cacheData->missProbes += probeCount
movl maxMissProbes(%esi), %edi// if (cacheData->maxMissProbes < probeCount)
cmpl %ebx, %edi //
jge LMsgSendMaxMissProbeOK_$0_$1_$2 //
movl %ebx, maxMissProbes(%esi)// cacheData->maxMissProbes = probeCount
LMsgSendMaxMissProbeOK_$0_$1_$2:
// update cache miss probe histogram
cmpl $CACHE_HISTOGRAM_SIZE, %ebx // pin probeCount to max index
jl LMsgSendMissHistoIndexSet_$0_$1_$2
movl $(CACHE_HISTOGRAM_SIZE-1), %ebx
LMsgSendMissHistoIndexSet_$0_$1_$2:
LEA_STATIC_DATA %esi, _CacheMissHistogram, EXTERNAL_SYMBOL
shll $$2, %ebx // convert probeCount to histogram index
addl %ebx, %esi // calculate &CacheMissHistogram[probeCount<<2]
movl 0(%esi), %edi // get current tally
addl $$1, %edi //
movl %edi, 0(%esi) // tally += 1
LMsgSendMissInstrumentDone_$0_$1_$2:
popl %ebx // restore non-volatile register
#endif
.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 // MSG_SENDSUPER
// 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
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 // MSG_SENDSUPER (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
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:
#if defined(OBJC_INSTRUMENTED)
popl %edx // retrieve cache pointer
movl mask(%edx), %esi // mask = cache->mask
testl %esi, %esi // a mask of zero is only for the...
je LMsgSendHitInstrumentDone_$0_$1_$2 // ... emptyCache, do not record anything
// locate and update the CacheInstrumentation structure
addl $$1, %esi // entryCount = mask + 1
shll $$2, %esi // tableSize = entryCount * sizeof(entry)
addl $buckets, %esi // offset = buckets + tableSize
addl %edx, %esi // cacheData = &cache->buckets[mask+1]
movl hitCount(%esi), %edi
addl $$1, %edi
movl %edi, hitCount(%esi) // cacheData->hitCount += 1
movl hitProbes(%esi), %edi
addl %ebx, %edi
movl %edi, hitProbes(%esi) // cacheData->hitProbes += probeCount
movl maxHitProbes(%esi), %edi// if (cacheData->maxHitProbes < probeCount)
cmpl %ebx, %edi
jge LMsgSendMaxHitProbeOK_$0_$1_$2
movl %ebx, maxHitProbes(%esi)// cacheData->maxHitProbes = probeCount
LMsgSendMaxHitProbeOK_$0_$1_$2:
// update cache hit probe histogram
cmpl $CACHE_HISTOGRAM_SIZE, %ebx // pin probeCount to max index
jl LMsgSendHitHistoIndexSet_$0_$1_$2
movl $(CACHE_HISTOGRAM_SIZE-1), %ebx
LMsgSendHitHistoIndexSet_$0_$1_$2:
LEA_STATIC_DATA %esi, _CacheHitHistogram, EXTERNAL_SYMBOL
shll $$2, %ebx // convert probeCount to histogram index
addl %ebx, %esi // calculate &CacheHitHistogram[probeCount<<2]
movl 0(%esi), %edi // get current tally
addl $$1, %edi //
movl %edi, 0(%esi) // tally += 1
LMsgSendHitInstrumentDone_$0_$1_$2:
popl %ebx // restore non-volatile register
#endif
// 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 // MSG_SENDSUPER
// 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 // MSG_SENDSUPER (stret)
// 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 has return address and nothing else
subl $$(12+5*16), %esp
movdqa %xmm3, 4*16(%esp)
movdqa %xmm2, 3*16(%esp)
movdqa %xmm1, 2*16(%esp)
movdqa %xmm0, 1*16(%esp)
movl %eax, 8(%esp) // class
movl %ecx, 4(%esp) // selector
movl %edx, 0(%esp) // receiver
call __class_lookupMethodAndLoadCache3
movdqa 4*16(%esp), %xmm3
movdqa 3*16(%esp), %xmm2
movdqa 2*16(%esp), %xmm1
movdqa 1*16(%esp), %xmm0
addl $$(12+5*16), %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_impcache. It returns 1 instead. This prevents thread-
* safety and memory management bugs in _class_lookupMethodAndLoadCache.
* See _class_lookupMethodAndLoadCache for details.
*
* _objc_msgForward_impcache is passed as a parameter because it's more
* efficient to do the (PIC) lookup once in the caller than repeatedly here.
********************************************************************/
STATIC_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_impcache
cmpl method_imp(%eax), %ecx // if (imp==_objc_msgForward_impcache)
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.
********************************************************************/
STATIC_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
CALL_MCOUNTER
// load receiver and selector
movl selector(%esp), %ecx
movl self(%esp), %eax
// 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:
// %eax is already zero
movl $0,%edx
xorps %xmm0, %xmm0
LMsgSendDone:
ret
// guaranteed non-nil entry point (disabled for now)
// .globl _objc_msgSendNonNil
// _objc_msgSendNonNil:
// movl self(%esp), %eax
// jmp LMsgSendReceiverOk
LMsgSendExit:
END_ENTRY _objc_msgSend
/********************************************************************
*
* id objc_msgSendSuper(struct objc_super *super, SEL _cmd,...) * struct objc_super {
* id receiver * }
ENTRY _objc_msgSendSuper
CALL_MCOUNTER
// 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
// 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
/********************************************************************
* id objc_msgSendv(id self, SEL _cmd, unsigned size, marg_list frame) * On entry:
* (sp+4) is the message receiver,
* (sp+8) is the selector,
* (sp+12) is the size of the marg_list, in bytes,
* (sp+16) is the address of the marg_list
*
********************************************************************/
ENTRY _objc_msgSendv
#if defined(KERNEL)
trap // _objc_msgSendv is not for the kernel
#else
pushl %ebp
movl %esp, %ebp
// stack is currently aligned assuming no extra arguments
movl (marg_list+4)(%ebp), %edx
addl $8, %edx // skip self & selector
movl (marg_size+4)(%ebp), %ecx
subl $8, %ecx // skip self & selector
shrl $2, %ecx
je LMsgSendvArgsOK
// %esp = %esp - (16 - ((numVariableArguments & 3) << 2))
movl %ecx, %eax // 16-byte align stack
andl $3, %eax
shll $2, %eax
subl $16, %esp
addl %eax, %esp
LMsgSendvArgLoop:
decl %ecx
movl 0(%edx, %ecx, 4), %eax
pushl %eax
jg LMsgSendvArgLoop
LMsgSendvArgsOK:
movl (selector+4)(%ebp), %ecx
pushl %ecx
movl (self+4)(%ebp),%ecx
pushl %ecx
call _objc_msgSend
movl %ebp,%esp
popl %ebp
ret
#endif
END_ENTRY _objc_msgSendv
/********************************************************************
*
* double objc_msgSend_fpret(id self, SEL _cmd,...) ********************************************************************/
ENTRY _objc_msgSend_fpret
CALL_MCOUNTER
// load receiver and selector
movl selector(%esp), %ecx
movl self(%esp), %eax
// 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:
// %eax is already zero
fldz
LMsgSendFpretDone:
ret
LMsgSendFpretExit:
END_ENTRY _objc_msgSend_fpret
/********************************************************************
* double objc_msgSendv_fpret(id self, SEL _cmd, unsigned size, marg_list frame) * On entry:
* (sp+4) is the message receiver,
* (sp+8) is the selector,
* (sp+12) is the size of the marg_list, in bytes,
* (sp+16) is the address of the marg_list
*
********************************************************************/
ENTRY _objc_msgSendv_fpret
#if defined(KERNEL)
trap // _objc_msgSendv is not for the kernel
#else
pushl %ebp
movl %esp, %ebp
// stack is currently aligned assuming no extra arguments
movl (marg_list+4)(%ebp), %edx
addl $8, %edx // skip self & selector
movl (marg_size+4)(%ebp), %ecx
subl $8, %ecx // skip self & selector
shrl $2, %ecx
je LMsgSendvFpretArgsOK
// %esp = %esp - (16 - ((numVariableArguments & 3) << 2))
movl %ecx, %eax // 16-byte align stack
andl $3, %eax
shll $2, %eax
subl $16, %esp
addl %eax, %esp
LMsgSendvFpretArgLoop:
decl %ecx
movl 0(%edx, %ecx, 4), %eax
pushl %eax
jg LMsgSendvFpretArgLoop
LMsgSendvFpretArgsOK:
movl (selector+4)(%ebp), %ecx
pushl %ecx
movl (self+4)(%ebp),%ecx
pushl %ecx
call _objc_msgSend_fpret
movl %ebp,%esp
popl %ebp
ret
#endif
END_ENTRY _objc_msgSendv_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
CALL_MCOUNTER
// 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:
ret $4 // pop struct return address (#2995932)
// guaranteed non-nil entry point (disabled for now)
// .globl _objc_msgSendNonNil_stret
// _objc_msgSendNonNil_stret:
// CALL_MCOUNTER
// movl self_stret(%esp), %eax
// jmp LMsgSendStretReceiverOk
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
CALL_MCOUNTER
// 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
/********************************************************************
* void objc_msgSendv_stret(void *st_addr, id self, SEL _cmd, unsigned size, marg_list frame) * objc_msgSendv_stret is the struct-return form of msgSendv.
* This function does not use the struct-return ABI *
* On entry: (sp+4) is the address in which the returned struct is put,
* (sp+8) is the message receiver,
* (sp+12) is the selector,
* (sp+16) is the size of the marg_list, in bytes,
* (sp+20) is the address of the marg_list
*
********************************************************************/
ENTRY _objc_msgSendv_stret
#if defined(KERNEL)
trap // _objc_msgSendv_stret is not for the kernel
#else
pushl %ebp
movl %esp, %ebp
subl $12, %esp // align stack assuming no extra arguments
movl (marg_list_stret+4)(%ebp), %edx
addl $8, %edx // skip self & selector
movl (marg_size_stret+4)(%ebp), %ecx
subl $5, %ecx // skip self & selector
shrl $2, %ecx
jle LMsgSendvStretArgsOK
// %esp = %esp - (16 - ((numVariableArguments & 3) << 2))
movl %ecx, %eax // 16-byte align stack
andl $3, %eax
shll $2, %eax
subl $16, %esp
addl %eax, %esp
LMsgSendvStretArgLoop:
decl %ecx
movl 0(%edx, %ecx, 4), %eax
pushl %eax
jg LMsgSendvStretArgLoop
LMsgSendvStretArgsOK:
movl (selector_stret+4)(%ebp), %ecx
pushl %ecx
movl (self_stret+4)(%ebp),%ecx
pushl %ecx
movl (struct_addr+4)(%ebp),%ecx
pushl %ecx
call _objc_msgSend_stret
movl %ebp,%esp
popl %ebp
ret
#endif
END_ENTRY _objc_msgSendv_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 (while forwarding %s)\0"
.non_lazy_symbol_pointer
L_forward_handler:
.indirect_symbol __objc_forward_handler
.long 0
L_forward_stret_handler:
.indirect_symbol __objc_forward_stret_handler
.long 0
STATIC_ENTRY __objc_msgForward_impcache
// 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_impcache
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 L_forward_handler-L__objc_msgForward$pic_base(%edx),%ecx
movl (%ecx), %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 %s", "forward::", forwardedSel)
subl $8, %esp // 16-byte align the stack
pushl (selector+4+4)(%ebp) // the forwarded selector
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 L_forward_stret_handler-L__objc_msgForwardStret$pic_base(%edx), %ecx
movl (%ecx), %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 %s", "forward::", forwardedSelector)
subl $8, %esp // 16-byte align the stack
pushl (selector_stret+4+4)(%ebp) // the forwarded selector
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 _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
.section __DATA,__objc_msg_break
.long 0
.long 0
#endif