#define ARGUMENT_SPILL_SIZE (8*10 + 8*16)
.macro MSGSEND receiver, sel
	.cfi_startproc
	cbz    \receiver, 4f                   // Skip everything if the receiver is nil
	                                       // Jump to 6: if this is a small object
	ubfx    x9, \receiver, #0, #SMALLOBJ_BITS
	cbnz   x9, 6f

	ldr    x9, [\receiver]                 // Load class to x9 if not a small int
1:
	ldr    x9, [x9, #DTABLE_OFFSET]        // Dtable -> x9
	ldr    w10, [\sel]                     // selector->index -> x10
	ldr    w11, [x9, #SHIFT_OFFSET]        // dtable->shift -> x11
	
	cmp    x11, #8                         // If this is a small dtable, jump to the
	                                       // small dtable handlers
	b.eq    2f
	cbz    x11, 3f

	ubfx   x11, x10, #16, #8               // Put byte 3 of the sel id in x12
	add    x11, x9, x11, lsl #3            // x11 = dtable address + dtable data offset
	ldr    x9, [x11, #DATA_OFFSET]         // Load, adding in the data offset
2:	                                       // dtable16
	ubfx   x11, x10, #8, #8                // Put byte 2 of the sel id in x12
	add    x11, x9, x11, lsl #3            // x11 = dtable address + dtable data offset
	ldr    x9, [x11, #DATA_OFFSET]         // Load, adding in the data offset
3:                                         // dtable8
	ubfx   x11, x10, #0, #8                // Put low byte of the sel id in x12
	add    x11, x9, x11, lsl #3            // x11 = dtable address + dtable data offset
	ldr    x9, [x11, #DATA_OFFSET]         // Load, adding in the data offset. 
	                                       // Slot pointer is now in x9

	cbz    x9,  5f                         // If the slot is nil, go to the C path

	ldr    x9, [x9, #SLOT_OFFSET]          // Load the method from the slot
	br     x9                              // Tail-call the method

4:	                                       // Nil receiver
	mov    x0, #0
	mov    v0.d[0], x0
	mov    v0.d[1], x0
	br     lr
5:                                        // Slow lookup
	                                      // Save anything that will be clobbered by
	                                      // the call
	stp    x0, x1, [sp, #-(ARGUMENT_SPILL_SIZE)]!
	stp    x2, x3, [sp, #16]              // The order is arbitrary, except that
	stp    x4, x5, [sp, #32]              // fp and lr must be spilled together and
	stp    x6, x7, [sp, #48]              // it's convenient if \receiver is spilled at sp
	stp    q0, q1, [sp, #64]
	stp    q2, q3, [sp, #96]
	stp    q4, q5, [sp, #128]
	stp    q6, q7, [sp, #160]
	stp    fp, lr, [sp, #192]
	add    fp, sp, 192
	stp    \receiver, x8, [sp, #-16]!
	.cfi_def_cfa fp, 0
	.cfi_offset fp, 0
	.cfi_offset lr, 8
	                                      // We now have all argument registers, the link
	                                      // register and the receiver spilled on the
	                                      // stack, with sp containing
	                                      // the address of the receiver

	mov    x0, sp                         // &self, _cmd in arguments
	mov    x1, \sel
	bl     CDECL(slowMsgLookup)           // This is the only place where the CFI directives
	                                      // have to be accurate...
	mov    x9, x0                         // IMP -> x9
	ldp    x0, x1, [sp, #16]              // Reload spilled argument registers
	ldp    x2, x3, [sp, #32]
	ldp    x4, x5, [sp, #64]
	ldp    x6, x7, [sp, #64]
	ldp    q0, q1, [sp, #80]
	ldp    q2, q3, [sp, #112]
	ldp    q4, q5, [sp, #144]
	ldp    q6, q7, [sp, #176]
	ldp    fp, lr, [sp, #208]
	ldp    \receiver, x8, [sp], #16
	br     x9
6:
	adr    x10, SmallObjectClasses
	ldr    x9, [x10, x9, lsl #3]
	b      1b
	.cfi_endproc
.endm

.globl CDECL(objc_msgSend_fpret)
TYPE_DIRECTIVE(CDECL(objc_msgSend_fpret), %function)
.globl CDECL(objc_msgSend)
TYPE_DIRECTIVE(CDECL(objc_msgSend), %function)
.globl CDECL(objc_msgSend_stret)
TYPE_DIRECTIVE(CDECL(objc_msgSend_stret), %function)
CDECL(objc_msgSend):
CDECL(objc_msgSend_fpret):
CDECL(objc_msgSend_stret):
	MSGSEND x0, x1

