/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

.globl _of_forward
.globl _of_forward_stret

.section __TEXT, __objc_methname, cstring_literals
L_str_forwardingTargetForSelector:
	.asciz "forwardingTargetForSelector:"

.section __DATA, __objc_selrefs, literal_pointers, no_dead_strip
L_sel_forwardingTargetForSelector:
	.quad L_str_forwardingTargetForSelector

.section __DATA, __objc_imageinfo, regular, no_dead_strip
	.long 0, 0

.section __TEXT, __text, regular, pure_instructions
_of_forward:
	pushq	%rbp
	movq	%rsp, %rbp

	/* Save all arguments */
	subq	$0xC0, %rsp	/* 16-byte alignment */
	movq	 %rax, 0xA8(%rsp)
	movq	 %rdi, 0xA0(%rsp)
	movq	 %rsi, 0x98(%rsp)
	movq	 %rdx, 0x90(%rsp)
	movq	 %rcx, 0x88(%rsp)
	movq	  %r8, 0x80(%rsp)
	movq	  %r9, 0x78(%rsp)
	movd	%xmm0, 0x70(%rsp)
	movd	%xmm1, 0x60(%rsp)
	movd	%xmm2, 0x50(%rsp)
	movd	%xmm3, 0x40(%rsp)
	movd	%xmm4, 0x30(%rsp)
	movd	%xmm5, 0x20(%rsp)
	movd	%xmm6, 0x10(%rsp)
	movd	%xmm7,     (%rsp)

	call	_object_getClass

	movq	%rax, %rdi
	movq	L_sel_forwardingTargetForSelector(%rip), %rsi
	call	_class_respondsToSelector
	testq	%rax, %rax
	jz	fail

	movq	0xA0(%rsp), %rdi
	movq	L_sel_forwardingTargetForSelector(%rip), %rsi
	movq	0x98(%rsp), %rdx
	call	_objc_msgSend
	movq	%rax, %rdi

	testq	%rdi, %rdi
	jz	fail

	cmpq	0xA0(%rsp), %rdi
	je	fail

	/* Restore all arguments, except %rdi */
	movd	    (%rsp), %xmm7
	movd	0x10(%rsp), %xmm6
	movd	0x20(%rsp), %xmm5
	movd	0x30(%rsp), %xmm4
	movd	0x40(%rsp), %xmm3
	movd	0x50(%rsp), %xmm2
	movd	0x60(%rsp), %xmm1
	movd	0x70(%rsp), %xmm0
	movq	0x78(%rsp),   %r9
	movq	0x80(%rsp),   %r8
	movq	0x88(%rsp),  %rcx
	movq	0x90(%rsp),  %rdx
	movq	0x98(%rsp),  %rsi
	movq	0xA8(%rsp),  %rax

	movq	%rbp, %rsp
	popq	%rbp

	jmp	_objc_msgSend

fail:
	movq	0xA0(%rsp),  %rdi
	movq	0x98(%rsp),  %rsi

	movq	%rbp, %rsp
	popq	%rbp

	jmp	_of_method_not_found

_of_forward_stret:
	pushq	%rbp
	movq	%rsp, %rbp

	/* Save all arguments */
	subq	$0xC0, %rsp	/* 16-byte alignment */
	movq	 %rax, 0xA8(%rsp)
	movq	 %rdi, 0xA0(%rsp)
	movq	 %rsi, 0x98(%rsp)
	movq	 %rdx, 0x90(%rsp)
	movq	 %rcx, 0x88(%rsp)
	movq	  %r8, 0x80(%rsp)
	movq	  %r9, 0x78(%rsp)
	movd	%xmm0, 0x70(%rsp)
	movd	%xmm1, 0x60(%rsp)
	movd	%xmm2, 0x50(%rsp)
	movd	%xmm3, 0x40(%rsp)
	movd	%xmm4, 0x30(%rsp)
	movd	%xmm5, 0x20(%rsp)
	movd	%xmm6, 0x10(%rsp)
	movd	%xmm7,     (%rsp)

	movq	%rsi, %rdi
	call	_object_getClass

	movq	%rax, %rdi
	movq	L_sel_forwardingTargetForSelector(%rip), %rsi
	call	_class_respondsToSelector
	testq	%rax, %rax
	jz	fail_stret

	movq	0x98(%rsp), %rdi
	movq	L_sel_forwardingTargetForSelector(%rip), %rsi
	movq	0x90(%rsp), %rdx
	call	_objc_msgSend
	movq	%rax, %rsi

	testq	%rsi, %rsi
	jz	fail_stret

	cmpq	0x98(%rsp), %rsi
	je	fail_stret

	/* Restore all arguments, except %rsi */
	movd	    (%rsp), %xmm7
	movd	0x10(%rsp), %xmm6
	movd	0x20(%rsp), %xmm5
	movd	0x30(%rsp), %xmm4
	movd	0x40(%rsp), %xmm3
	movd	0x50(%rsp), %xmm2
	movd	0x60(%rsp), %xmm1
	movd	0x70(%rsp), %xmm0
	movq	0x78(%rsp),   %r9
	movq	0x80(%rsp),   %r8
	movq	0x88(%rsp),  %rcx
	movq	0x90(%rsp),  %rdx
	movq	0xA0(%rsp),  %rdi
	movq	0xA8(%rsp),  %rax

	movq	%rbp, %rsp
	popq	%rbp

	jmp	_objc_msgSend_stret

fail_stret:
	movq	0x98(%rsp),  %rdi
	movq	0x90(%rsp),  %rsi

	movq	%rbp, %rsp
	popq	%rbp

	jmp	_of_method_not_found
