/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 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-i386.s - i386 code to support objc messaging.
 **
 ********************************************************************
 ********************************************************************/

// The assembler syntax for an immediate value is the same as the
// syntax for a macro argument number (dollar sign followed by the
// digits).  Argument number wins in this ambiguity.  Until the
// assembler is fixed we have to find another way.
#define NO_MACRO_CONSTS
#ifdef NO_MACRO_CONSTS
	kOne        = 1
	kTwo        = 2
	kFour       = 4
	kEight      = 8
	kTwelve     = 12
#endif

/********************************************************************
* Data used by the ObjC runtime.
*
********************************************************************/

.data
// Substitute receiver for messages sent to nil (usually also nil)
// id _objc_nilReceiver
.align 4
.globl __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.

.globl		_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

.globl		_objc_exitPoints
_objc_exitPoints:
	.long	LGetImpExit
	.long	LGetMethodExit
	.long	LMsgSendExit
	.long	LMsgSendFpretExit
	.long	LMsgSendStretExit
	.long	LMsgSendSuperExit
	.long	LMsgSendSuperStretExit
	.long	0


#if defined(__DYNAMIC__)

/* 
 * Thunk to retrieve PC.
 * `call 1; 1: pop` sequence breaks any branch-prediction stack. 
 */

.align 4, 0x90
L_get_pc_thunk.edx:
	movl	(%esp,1), %edx
	ret

#endif

/*
 * Handcrafted dyld stubs for each external call.
 * They should be converted into a local branch after linking. aB.
 */

/* asm_help.h version is not what we want */
#undef CALL_EXTERN

#if defined(__DYNAMIC__)

#define CALL_EXTERN(name)	call    L ## name ## $stub

#define LAZY_PIC_FUNCTION_STUB(name) \
.data                         ;\
.picsymbol_stub               ;\
L ## name ## $stub:           ;\
	.indirect_symbol name     ;\
	call    L_get_pc_thunk.edx ;\
L0$ ## name:                  ;\
	movl    L ## name ## $lz-L0$ ## name(%edx),%ecx ;\
	jmp     %ecx              ;\
L ## name ## $stub_binder:    ;\
    lea     L ## name ## $lz-L0$ ## name(%edx),%eax ;\
    pushl   %eax              ;\
    jmp     dyld_stub_binding_helper ;\
    nop                       ;\
.data                         ;\
.lazy_symbol_pointer          ;\
L ## name ## $lz:             ;\
	.indirect_symbol name     ;\
	.long L ## name ## $stub_binder

#else /* __DYNAMIC__ */

#define CALL_EXTERN(name)	call    name

#define LAZY_PIC_FUNCTION_STUB(name)

#endif /* __DYNAMIC__ */

// _class_lookupMethodAndLoadCache
LAZY_PIC_FUNCTION_STUB(__class_lookupMethodAndLoadCache)

// __objc_error
LAZY_PIC_FUNCTION_STUB(___objc_error) /* No stub needed */

#if defined(PROFILE)
// mcount
LAZY_PIC_FUNCTION_STUB(mcount)
#endif /* PROFILE */

/********************************************************************
 *
 * Constants.
 *
 ********************************************************************/

// In case the implementation is _objc_msgForward, indicate to it
// whether the method was invoked as a word-return or struct-return.
// This flag is passed in a register that is caller-saved, and is
// not part of the parameter passing convention (i.e. it is "out of
// band").  This works because _objc_msgForward is only entered
// from here in the messenger.
	kFwdMsgSend      = 1
	kFwdMsgSendStret = 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


//////////////////////////////////////////////////////////////////////
//
// LOAD_STATIC_WORD	targetReg, symbolName, LOCAL_SYMBOL | EXTERNAL_SYMBOL
//
// Load the value of the named static data word.
//
// Takes: targetReg       - the register, other than r0, to load
//        symbolName      - the name of the symbol
//        LOCAL_SYMBOL    - symbol name used as-is
//        EXTERNAL_SYMBOL - symbol name gets nonlazy treatment
//
// Eats: edx and targetReg
//////////////////////////////////////////////////////////////////////

// Values to specify whether the symbol is plain or nonlazy
LOCAL_SYMBOL	= 0
EXTERNAL_SYMBOL	= 1

.macro	LOAD_STATIC_WORD

#if defined(__DYNAMIC__)
	call	L_get_pc_thunk.edx
1:	
.if $2 == EXTERNAL_SYMBOL
	movl	L$1-1b(%edx),$0
	movl	0($0),$0
.elseif $2 == LOCAL_SYMBOL
	movl	$1-1b(%edx),$0
.else
	!!! Unknown symbol type !!!
.endif
#else
	movl	$1,$0
#endif

.endmacro

//////////////////////////////////////////////////////////////////////
//
// LEA_STATIC_DATA	targetReg, symbolName, LOCAL_SYMBOL | EXTERNAL_SYMBOL
//
// Load the address of the named static data.
//
// Takes: targetReg       - the register, other than edx, to load
//        symbolName      - the name of the symbol
//        LOCAL_SYMBOL    - symbol is local to this module
//        EXTERNAL_SYMBOL - symbol is imported from another module
//
// Eats: edx and targetReg
//////////////////////////////////////////////////////////////////////

.macro	LEA_STATIC_DATA
#if defined(__DYNAMIC__)
	call	L_get_pc_thunk.edx
1:	
.if $2 == EXTERNAL_SYMBOL
	movl	L$1-1b(%edx),$0
.elseif $2 == LOCAL_SYMBOL
	leal	$1-1b(%edx),$0
.else
	!!! Unknown symbol type !!!
.endif
#else
	leal	$1,$0
#endif

.endmacro

//////////////////////////////////////////////////////////////////////
//
// 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

//////////////////////////////////////////////////////////////////////
//
// 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	$kEight,%esp
	// Current stack contents: ret, ebp, pad, pad
	CALL_EXTERN(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; return method triplet)
//
//	  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.
// Overlapped to prevent AGI
.if $0 == WORD_RETURN			// Regular word return
.if $1 == MSG_SEND			// MSG_SEND
	movl	isa(%eax), %eax		//   class = self->isa
	movl	selector(%esp), %ecx	//   get selector
.elseif $1 == MSG_SENDSUPER		// MSG_SENDSUPER
	movl	super(%esp), %eax	//   get objc_super address
	movl	class(%eax), %eax	//   class = caller->class
	movl	selector(%esp), %ecx	//   get selector
.else					// CACHE_GET
	movl	selector(%esp), %ecx	//   get selector - class already in eax
.endif
.else					// Struct return
.if $1 == MSG_SEND			// MSG_SEND (stret)
	movl	isa(%eax), %eax		//   class = self->isa
	movl	(selector_stret)(%esp), %ecx	//   get selector
.elseif $1 == MSG_SENDSUPER		// MSG_SENDSUPER (stret)
	movl	super_stret(%esp), %eax	//   get objc_super address
	movl	class(%eax), %eax	//   class = caller->class
	movl	(selector_stret)(%esp), %ecx	//   get selector
.else					// CACHE_GET
	!! This should not happen.
.endif
.endif

	pushl	%edi			// save scratch register
	movl	cache(%eax), %eax	// 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
	leal	buckets(%eax), %edi	// buckets = &cache->buckets
	movl	mask(%eax), %esi		// mask = cache->mask
	movl	%ecx, %edx		// index = selector
#ifdef NO_MACRO_CONSTS
	shrl	$kTwo, %edx		// index = selector >> 2
#else
	shrl	$2, %edx		// index = selector >> 2
#endif

// search the receiver's cache
LMsgSendProbeCache_$0_$1_$2:
#if defined(OBJC_INSTRUMENTED)
	addl	$kOne, %ebx			// probeCount += 1
#endif
	andl	%esi, %edx		// index &= mask
	movl	(%edi, %edx, 4), %eax	// method = 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	$kOne, %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	$kOne, %esi			// entryCount = mask + 1
#ifdef NO_MACRO_CONSTS
	shll	$kTwo, %esi		// tableSize = entryCount * sizeof(entry)
#else
	shll	$2, %esi			// tableSize = entryCount * sizeof(entry)
#endif
	addl	$buckets, %esi		// offset = buckets + tableSize
	addl	%edx, %esi		// cacheData = &cache->buckets[mask+1]

	movl	missCount(%esi), %edi	// 
	addl	$kOne, %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
#ifdef NO_MACRO_CONSTS
	shll	$kTwo, %ebx		// convert probeCount to histogram index
#else
	shll	$2, %ebx			// convert probeCount to histogram index
#endif
	addl	%ebx, %esi		// calculate &CacheMissHistogram[probeCount<<2]
	movl	0(%esi), %edi		// get current tally
	addl	$kOne, %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), %eax	//  get messaged object
	movl	isa(%eax), %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), %esi	//  get messaged object
	movl	%esi, 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), %eax	//  get messaged object
	movl	isa(%eax), %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), %esi	//  get messaged object
	movl	%esi, 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

	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	$kOne, %esi			// entryCount = mask + 1
#ifdef NO_MACRO_CONSTS
	shll	$kTwo, %esi		// tableSize = entryCount * sizeof(entry)
#else
	shll	$2, %esi			// tableSize = entryCount * sizeof(entry)
#endif
	addl	$buckets, %esi		// offset = buckets + tableSize
	addl	%edx, %esi		// cacheData = &cache->buckets[mask+1]

	movl	hitCount(%esi), %edi
	addl	$kOne, %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
#ifdef NO_MACRO_CONSTS
	shll	$kTwo, %ebx		// convert probeCount to histogram index
#else
	shll	$2, %ebx			// convert probeCount to histogram index
#endif
	addl	%ebx, %esi		// calculate &CacheHitHistogram[probeCount<<2]
	movl	0(%esi), %edi		// get current tally
	addl	$kOne, %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)
//
// Stack must be at 0xXXXXXXXc on entrance.
//
// On exit: Register parameters restored from CacheLookup
//	  imp in eax
//
/////////////////////////////////////////////////////////////////////

.macro MethodTableLookup

#ifdef NO_MACRO_CONSTS
	subl    $kFour, %esp		// 16-byte align the stack
#else
	subl    $4, %esp		// 16-byte align the stack
#endif
	// push args (class, selector)
	pushl	%ecx
	pushl	%eax
	CALL_EXTERN(__class_lookupMethodAndLoadCache)
#ifdef NO_MACRO_CONSTS
	addl    $kTwelve, %esp		// pop parameters and alignment
#else
	addl    $12, %esp		// pop parameters and alignment
#endif
.endmacro


/********************************************************************
 * Method _cache_getMethod(Class cls, SEL sel, IMP objc_msgForward_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. It returns NULL instead. This prevents thread-
 * safety and memory management bugs in _class_lookupMethodAndLoadCache. 
 * See _class_lookupMethodAndLoadCache for details.
 *
 * _objc_msgForward is passed as a parameter because it's more efficient
 * to do the (PIC) lookup once in the caller than repeatedly here.
 ********************************************************************/
        
	ENTRY __cache_getMethod

// load the class into eax
	movl	self(%esp), %eax

// do lookup
	CacheLookup WORD_RETURN, CACHE_GET, LGetMethodMiss

// cache hit, method triplet in %eax
	movl    first_arg(%esp), %ecx   // check for _objc_msgForward
	cmpl    method_imp(%eax), %ecx
	je      LGetMethodMiss          // if (imp==_objc_msgForward) return nil
	ret                             // else return method triplet address

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.
 ********************************************************************/

	ENTRY __cache_getImp

// load the class into eax
	movl	self(%esp), %eax

// 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

	movl	self(%esp), %eax

// check whether receiver is nil 
	testl	%eax, %eax
	je	LMsgSendNilSelf

// receiver is non-nil: search the cache
LMsgSendReceiverOk:
	CacheLookup WORD_RETURN, MSG_SEND, LMsgSendCacheMiss
	movl	$kFwdMsgSend, %edx	// flag word-return for _objc_msgForward
	jmp	*%eax			// goto *imp

// cache miss: go search the method lists
LMsgSendCacheMiss:
	MethodTableLookup WORD_RETURN, MSG_SEND
	movl	$kFwdMsgSend, %edx	// flag word-return for _objc_msgForward
	jmp	*%eax			// goto *imp

// message sent to nil: redirect to nil receiver, if any
LMsgSendNilSelf:
	call	L_get_pc_thunk.edx	// load new receiver
1:	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
LMsgSendReturnZero:
	// %eax is already zero
	movl	$0,%edx
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;
 *		Class	class;
 * };
 ********************************************************************/

	ENTRY	_objc_msgSendSuper
	CALL_MCOUNTER

	movl	super(%esp), %eax

// receiver is non-nil: search the cache
	CacheLookup WORD_RETURN, MSG_SENDSUPER, LMsgSendSuperCacheMiss
	movl	$kFwdMsgSend, %edx	// flag word-return for _objc_msgForward
	jmp	*%eax			// goto *imp

// cache miss: go search the method lists
LMsgSendSuperCacheMiss:
	MethodTableLookup WORD_RETURN, MSG_SENDSUPER
	movl	$kFwdMsgSend, %edx	// flag word-return for _objc_msgForward
	jmp	*%eax			// goto *imp

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

	movl	self(%esp), %eax

// check whether receiver is nil 
	testl	%eax, %eax
	je	LMsgSendFpretNilSelf

// receiver is non-nil: search the cache
LMsgSendFpretReceiverOk:
	CacheLookup WORD_RETURN, MSG_SEND, LMsgSendFpretCacheMiss
	movl	$kFwdMsgSend, %edx	// flag word-return for _objc_msgForward
	jmp	*%eax			// goto *imp

// cache miss: go search the method lists
LMsgSendFpretCacheMiss:
	MethodTableLookup WORD_RETURN, MSG_SEND
	movl	$kFwdMsgSend, %edx	// flag word-return for _objc_msgForward
	jmp	*%eax			// goto *imp

// message sent to nil: redirect to nil receiver, if any
LMsgSendFpretNilSelf:
	call	L_get_pc_thunk.edx	// load new receiver
1:	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
LMsgSendFpretReturnZero:
	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

	movl	self_stret(%esp), %eax

// check whether receiver is nil 
	testl	%eax, %eax
	je	LMsgSendStretNilSelf

// receiver is non-nil: search the cache
LMsgSendStretReceiverOk:
	CacheLookup STRUCT_RETURN, MSG_SEND, LMsgSendStretCacheMiss
	movl	$kFwdMsgSendStret, %edx	// flag struct-return for _objc_msgForward
	jmp	*%eax			// goto *imp

// cache miss: go search the method lists
LMsgSendStretCacheMiss:
	MethodTableLookup STRUCT_RETURN, MSG_SEND
	movl	$kFwdMsgSendStret, %edx	// flag struct-return for _objc_msgForward
	jmp	*%eax			// goto *imp

// message sent to nil: redirect to nil receiver, if any
LMsgSendStretNilSelf:
	call	L_get_pc_thunk.edx	// load new receiver
1:	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
LMsgSendStretDone:
	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;
 *		Class	class;
 * };
 *
 * 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

	movl	super_stret(%esp), %eax

// receiver is non-nil: search the cache
	CacheLookup STRUCT_RETURN, MSG_SENDSUPER, LMsgSendSuperStretCacheMiss
	movl	$kFwdMsgSendStret, %edx	// flag struct-return for _objc_msgForward
	jmp	*%eax			// goto *imp

// cache miss: go search the method lists
LMsgSendSuperStretCacheMiss:
	MethodTableLookup STRUCT_RETURN, MSG_SENDSUPER
	movl	$kFwdMsgSendStret, %edx	// flag struct-return for _objc_msgForward
	jmp	*%eax			// goto *imp

LMsgSendSuperStretExit:
	END_ENTRY	_objc_msgSendSuper_stret


/********************************************************************
 * id 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.
 * 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 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,...);
 *
 ********************************************************************/

// Location LFwdStr contains the string "forward::"
// Location LFwdSel contains a pointer to LFwdStr, that can be changed
// to point to another forward:: string for selector uniquing
// purposes.  ALWAYS dereference LFwdSel to get to "forward::" !!
	.objc_meth_var_names
	.align	2
LFwdStr:.ascii	"forward::\0"

	.objc_message_refs
	.align	2
LFwdSel:.long	LFwdStr

	.cstring
	.align	2
LUnkSelStr:    .ascii	"Does not recognize selector %s\0"

	ENTRY	__objc_msgForward

#if defined(KERNEL)
	trap				// _objc_msgForward is not for the kernel
#else
	cmpl	$kFwdMsgSendStret, %edx	// check secret flag for word vs struct return
	je	LForwardStretVersion	// jump to struct return version...

	// non-stret version ...
	pushl   %ebp
	movl    %esp,%ebp
	movl    (selector+4)(%ebp), %eax
#if defined(__DYNAMIC__)
	call	L_get_pc_thunk.edx
L__objc_msgForward$pic_base:
	leal	LFwdSel-L__objc_msgForward$pic_base(%edx),%ecx
	cmpl	%ecx, %eax
#else
	cmpl	LFwdSel, %eax
#endif
	je	LMsgForwardError

	subl    $8, %esp		// 16-byte align the stack
	leal    (self+4)(%ebp), %ecx
	pushl	%ecx
	pushl	%eax
#if defined(__DYNAMIC__)
	movl	LFwdSel-L__objc_msgForward$pic_base(%edx),%ecx
#else
	movl	LFwdSel,%ecx
#endif
	pushl	%ecx
	pushl   (self+4)(%ebp)
	call	_objc_msgSend
	movl    %ebp,%esp
	popl    %ebp
	ret

// call error handler with unrecognized selector message
	.align	4, 0x90
LMsgForwardError:
	subl    $12, %esp		// 16-byte align the stack
#if defined(__DYNAMIC__)
	leal	LFwdSel-L__objc_msgForward$pic_base(%edx),%eax
	pushl 	%eax
	leal	LUnkSelStr-L__objc_msgForward$pic_base(%edx),%eax
	pushl 	%eax
#else
	pushl	$LFwdSel
	pushl	$LUnkSelStr
#endif
	pushl   (self+4)(%ebp)
	CALL_EXTERN(___objc_error)	// volatile, will not return

// ***** Stret version of function below
// ***** offsets have been changed (by adding a word to make room for the 
// ***** structure, and labels have been changed to be unique.

LForwardStretVersion:
	pushl   %ebp
	movl    %esp,%ebp
	movl    (selector_stret+4)(%ebp), %eax

#if defined(__DYNAMIC__)
	call	L_get_pc_thunk.edx
L__objc_msgForwardStret$pic_base:
	leal	LFwdSel-L__objc_msgForwardStret$pic_base(%edx),%ecx
	cmpl	%ecx, %eax
#else
	cmpl	LFwdSel, %eax
#endif
	je	LMsgForwardStretError

	subl    $8, %esp		// 16-byte align the stack
	leal    (self_stret+4)(%ebp), %ecx
	pushl	%ecx
	pushl	%eax
#if defined(__DYNAMIC__)
	movl	LFwdSel-L__objc_msgForwardStret$pic_base(%edx),%ecx
#else
	movl	LFwdSel,%ecx
#endif
	pushl	%ecx
	pushl   (self_stret+4)(%ebp)
	call	_objc_msgSend
	movl    %ebp,%esp
	popl    %ebp
	ret	$4			// pop struct return address (#2995932)

// call error handler with unrecognized selector message
	.align	4, 0x90
LMsgForwardStretError:
	subl    $12, %esp		// 16-byte align the stack
#if defined(__DYNAMIC__)
	leal	LFwdSel-L__objc_msgForwardStret$pic_base(%edx),%eax
	pushl 	%eax
	leal	LUnkSelStr-L__objc_msgForwardStret$pic_base(%edx),%eax
	pushl 	%eax
#else
	pushl	$LFwdSel
	pushl	$LUnkSelStr
#endif
	pushl   (self_stret+4)(%ebp)
	CALL_EXTERN(___objc_error)	// volatile, will not return

#endif /* defined (KERNEL) */
	END_ENTRY	__objc_msgForward


// Special section containing a function pointer that dyld will call
// when it loads new images.
LAZY_PIC_FUNCTION_STUB(__objc_notify_images)
.data
.section __DATA,__image_notify
.long L__objc_notify_images$stub
