/* Copyright (c) 2008-2014, Avian Contributors

   Permission to use, copy, modify, and/or distribute this software
   for any purpose with or without fee is hereby granted, provided
   that the above copyright notice and this permission notice appear
   in all copies.

   There is NO WARRANTY for this software.  See license.txt for
   details. */

#include "avian/types.h"
#include "avian/target-fields.h"

#define LOCAL(x) .L##x

#if defined __APPLE__ \
   || ((defined __MINGW32__ || defined __CYGWIN32__) && ! defined __x86_64__)
#  define GLOBAL(x) _##x
#else
#  define GLOBAL(x) x   
#endif

.text

#ifdef __x86_64__

#ifdef AVIAN_USE_FRAME_POINTER
#  define ALIGNMENT_ADJUSTMENT 0
#else
#  define ALIGNMENT_ADJUSTMENT 8
#endif
   
#if defined __MINGW32__ || defined __CYGWIN32__

#define CALLEE_SAVED_REGISTER_FOOTPRINT 64 + ALIGNMENT_ADJUSTMENT

.globl GLOBAL(vmInvoke)
GLOBAL(vmInvoke):
   pushq  %rbp
   movq   %rsp,%rbp
   
   // %rcx: thread
   // %rdx: function
   // %r8 : arguments
   // %r9 : argumentsFootprint
   // 48(%rbp) : frameSize
   // 56(%rbp) : returnType (ignored)
   
   // allocate stack space for callee-saved registers
   subq   $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp

   // remember this stack position, since we won't be able to rely on
	 // %rbp being restored when the call returns
   movq   %rsp,TARGET_THREAD_SCRATCH(%rcx)
   
   // save callee-saved registers
   movq   %rbx,0(%rsp)
   movq   %r12,8(%rsp)
   movq   %r13,16(%rsp)
   movq   %r14,24(%rsp)
   movq   %r15,32(%rsp)
   movq   %rsi,40(%rsp)
   movq   %rdi,48(%rsp)
   
   // allocate stack space for arguments
   movl   48(%rbp),%eax
   subq   %rax,%rsp
   
   // we use rbx to hold the thread pointer, by convention
   mov    %rcx,%rbx

   // copy arguments into place
   movq   $0,%r11
   jmp    LOCAL(vmInvoke_argumentTest)

LOCAL(vmInvoke_argumentLoop):
   movq   (%r8,%r11,1),%rsi
   movq   %rsi,(%rsp,%r11,1)
   addq   $8,%r11

LOCAL(vmInvoke_argumentTest):
   cmpq   %r9,%r11
   jb     LOCAL(vmInvoke_argumentLoop)
   
   // call function
   call   *%rdx

.globl GLOBAL(vmInvoke_returnAddress)
GLOBAL(vmInvoke_returnAddress):
   // restore stack pointer
   movq   TARGET_THREAD_SCRATCH(%rbx),%rsp

   // clear MyThread::stack to avoid confusing another thread calling
   // java.lang.Thread.getStackTrace on this one.  See
   // MyProcess::getStackTrace in compile.cpp for details on how we get
   // a reliable stack trace from a thread that might be interrupted at
   // any point in its execution.
   movq   $0,TARGET_THREAD_STACK(%rbx)
   
.globl GLOBAL(vmInvoke_safeStack)
GLOBAL(vmInvoke_safeStack):

#ifdef AVIAN_CONTINUATIONS
#  include "continuations-x86.S"
#endif // AVIAN_CONTINUATIONS

   // restore callee-saved registers
   movq   0(%rsp),%rbx
   movq   8(%rsp),%r12
   movq   16(%rsp),%r13
   movq   24(%rsp),%r14
   movq   32(%rsp),%r15
   movq   40(%rsp),%rsi
   movq   48(%rsp),%rdi

   addq   $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp
   
   // return
   popq   %rbp
   ret
      
.globl GLOBAL(vmJumpAndInvoke)
GLOBAL(vmJumpAndInvoke):
#ifdef AVIAN_CONTINUATIONS
   //     %rcx: thread
   //     %rdx: address
   //     %r8 : stack
   //     %r9 : argumentFootprint
   // 40(%rsp): arguments
   // 48(%rsp): frameSize
   
   // allocate new frame, adding room for callee-saved registers
   movl   48(%rsp),%eax
   subq   %rax,%r8
   subq   $CALLEE_SAVED_REGISTER_FOOTPRINT,%r8
   
   movq   %rcx,%rbx
   
   // set return address
   leaq   GLOBAL(vmInvoke_returnAddress)(%rip),%r10
   movq   %r10,(%r8)
   
   // copy arguments into place
   movq   $0,%r11
   movl   40(%rsp),%eax
   jmp    LOCAL(vmJumpAndInvoke_argumentTest)

LOCAL(vmJumpAndInvoke_argumentLoop):
   movq   (%rax,%r11,1),%r10
   movq   %r10,8(%r8,%r11,1)
   addq   $8,%r11

LOCAL(vmJumpAndInvoke_argumentTest):
   cmpq   %r9,%r11
   jb     LOCAL(vmJumpAndInvoke_argumentLoop)

   // the arguments have been copied, so we can set the real stack
   // pointer now
   movq   %r8,%rsp
   
   jmp    *%rdx
#else // not AVIAN_CONTINUATIONS
   // vmJumpAndInvoke should only be called when continuations are
   // enabled
   int3
#endif // not AVIAN_CONTINUATIONS

#else // not __MINGW32__ || __CYGWIN32__

#define CALLEE_SAVED_REGISTER_FOOTPRINT 48 + ALIGNMENT_ADJUSTMENT

.globl GLOBAL(vmInvoke)
GLOBAL(vmInvoke):
   pushq  %rbp
   movq   %rsp,%rbp
      
   // %rdi: thread
   // %rsi: function
   // %rdx: arguments
   // %rcx: argumentFootprint
   // %r8 : frameSize
   // %r9 : returnType (ignored)
   
   // allocate stack space for callee-saved registers
   subq   $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp
   
   // remember this stack position, since we won't be able to rely on
	 // %rbp being restored when the call returns
   movq   %rsp,TARGET_THREAD_SCRATCH(%rdi)
   
   // save callee-saved registers
   movq   %rbx,0(%rsp)
   movq   %r12,8(%rsp)
   movq   %r13,16(%rsp)
   movq   %r14,24(%rsp)
   movq   %r15,32(%rsp)
   
   // allocate stack space for arguments
   subq   %r8,%rsp
   
   // we use rbx to hold the thread pointer, by convention
   mov    %rdi,%rbx

   // copy arguments into place
   movq   $0,%r9
   jmp    LOCAL(vmInvoke_argumentTest)

LOCAL(vmInvoke_argumentLoop):
   movq   (%rdx,%r9,1),%r8
   movq   %r8,(%rsp,%r9,1)
   addq   $8,%r9

LOCAL(vmInvoke_argumentTest):
   cmpq   %rcx,%r9
   jb     LOCAL(vmInvoke_argumentLoop)
   
   // call function
   call   *%rsi
   
.globl GLOBAL(vmInvoke_returnAddress)
GLOBAL(vmInvoke_returnAddress):
   // restore stack pointer
   movq   TARGET_THREAD_SCRATCH(%rbx),%rsp
   
   // clear MyThread::stack to avoid confusing another thread calling
   // java.lang.Thread.getStackTrace on this one.  See
   // MyProcess::getStackTrace in compile.cpp for details on how we get
   // a reliable stack trace from a thread that might be interrupted at
   // any point in its execution.
   movq   $0,TARGET_THREAD_STACK(%rbx)
   
.globl GLOBAL(vmInvoke_safeStack)
GLOBAL(vmInvoke_safeStack):

#ifdef AVIAN_CONTINUATIONS
#  include "continuations-x86.S"
#endif // AVIAN_CONTINUATIONS

   // restore callee-saved registers
   movq   0(%rsp),%rbx
   movq   8(%rsp),%r12
   movq   16(%rsp),%r13
   movq   24(%rsp),%r14
   movq   32(%rsp),%r15

   addq   $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp
   
   // return
   popq   %rbp
   ret
   
.globl GLOBAL(vmJumpAndInvoke)
GLOBAL(vmJumpAndInvoke):
#ifdef AVIAN_CONTINUATIONS
   //    %rdi: thread
   //    %rsi: address
   //    %rdx: stack
   //    %rcx: argumentFootprint
   //    %r8 : arguments
   //    %r9 : frameSize
         
   // allocate new frame, adding room for callee-saved registers
   subq   %r9,%rdx
   subq   $CALLEE_SAVED_REGISTER_FOOTPRINT,%rdx
   
   movq   %rdi,%rbx
   
   // set return address
   movq   GLOBAL(vmInvoke_returnAddress)@GOTPCREL(%rip),%r10
   movq   %r10,(%rdx)
   
   // copy arguments into place
   movq   $0,%r11
   jmp    LOCAL(vmJumpAndInvoke_argumentTest)

LOCAL(vmJumpAndInvoke_argumentLoop):
   movq   (%r8,%r11,1),%r10
   movq   %r10,8(%rdx,%r11,1)
   addq   $8,%r11

LOCAL(vmJumpAndInvoke_argumentTest):
   cmpq   %rcx,%r11
   jb     LOCAL(vmJumpAndInvoke_argumentLoop)

   // the arguments have been copied, so we can set the real stack
   // pointer now
   movq   %rdx,%rsp
   
   jmp    *%rsi
#else // not AVIAN_CONTINUATIONS
   // vmJumpAndInvoke should only be called when continuations are
   // enabled
   int3
#endif // not AVIAN_CONTINUATIONS

#endif // not __MINGW32__ || __CYGWIN32__
   
#elif defined __i386__

#ifdef AVIAN_USE_FRAME_POINTER
#  define ALIGNMENT_ADJUSTMENT 0
#else
#  define ALIGNMENT_ADJUSTMENT 12
#endif

#define CALLEE_SAVED_REGISTER_FOOTPRINT 16 + ALIGNMENT_ADJUSTMENT

.globl GLOBAL(vmInvoke)
GLOBAL(vmInvoke):
   pushl  %ebp
   movl   %esp,%ebp

   //  8(%ebp): thread
   // 12(%ebp): function
   // 16(%ebp): arguments
   // 20(%ebp): argumentFootprint
   // 24(%ebp): frameSize
   // 28(%ebp): returnType
   
   // allocate stack space for callee-saved registers
   subl   $CALLEE_SAVED_REGISTER_FOOTPRINT,%esp
   
   // remember this stack position, since we won't be able to rely on
	 // %rbp being restored when the call returns
   movl   8(%ebp),%eax
   movl   %esp,TARGET_THREAD_SCRATCH(%eax)

   movl   %ebx,0(%esp)
   movl   %esi,4(%esp)
   movl   %edi,8(%esp)

   // allocate stack space for arguments
	 subl   24(%ebp),%esp
   
   // we use ebx to hold the thread pointer, by convention
   mov    %eax,%ebx
   
   // copy arguments into place
   movl   $0,%ecx
   movl   16(%ebp),%edx
   jmp    LOCAL(vmInvoke_argumentTest)

LOCAL(vmInvoke_argumentLoop):
   movl   (%edx,%ecx,1),%eax
   movl   %eax,(%esp,%ecx,1)
   addl   $4,%ecx

LOCAL(vmInvoke_argumentTest):
   cmpl   20(%ebp),%ecx
   jb     LOCAL(vmInvoke_argumentLoop)

   // call function
   call   *12(%ebp)

.globl GLOBAL(vmInvoke_returnAddress)
GLOBAL(vmInvoke_returnAddress):
   // restore stack pointer
   movl   TARGET_THREAD_SCRATCH(%ebx),%esp
   
   // clear MyThread::stack to avoid confusing another thread calling
   // java.lang.Thread.getStackTrace on this one.  See
   // MyProcess::getStackTrace in compile.cpp for details on how we get
   // a reliable stack trace from a thread that might be interrupted at
   // any point in its execution.
   movl   $0,TARGET_THREAD_STACK(%ebx)
   
.globl GLOBAL(vmInvoke_safeStack)
GLOBAL(vmInvoke_safeStack):

#ifdef AVIAN_CONTINUATIONS
#  include "continuations-x86.S"
#endif // AVIAN_CONTINUATIONS
   
   // restore callee-saved registers
   movl   0(%esp),%ebx
   movl   4(%esp),%esi
   movl   8(%esp),%edi
   
   addl   $CALLEE_SAVED_REGISTER_FOOTPRINT,%esp

   // handle return value based on expected type
   movl   28(%esp),%ecx

   popl   %ebp
   ret

LOCAL(getPC):
   movl   (%esp),%esi
   ret
   
.globl GLOBAL(vmJumpAndInvoke)
GLOBAL(vmJumpAndInvoke):
#ifdef AVIAN_CONTINUATIONS
   //  4(%esp): thread
   //  8(%esp): address
   // 12(%esp): stack
   // 16(%esp): argumentFootprint
   // 20(%esp): arguments
   // 24(%esp): frameSize

   movl   12(%esp),%ecx
   
   // allocate new frame, adding room for callee-saved registers,
   // return address, and frame pointer
   subl   24(%esp),%ecx
   subl   $CALLEE_SAVED_REGISTER_FOOTPRINT+8,%ecx
   
   movl   4(%esp),%ebx
   
   // set return address
#if defined __MINGW32__ || defined __CYGWIN32__
   movl   $GLOBAL(vmInvoke_returnAddress),%esi
#else
   call   LOCAL(getPC)
#  if defined __APPLE__
LOCAL(vmJumpAndInvoke_offset):
   leal   GLOBAL(vmInvoke_returnAddress)-LOCAL(vmJumpAndInvoke_offset)(%esi),%esi
#  else
   addl   $_GLOBAL_OFFSET_TABLE_,%esi
   movl   GLOBAL(vmInvoke_returnAddress)@GOT(%esi),%esi
#  endif
#endif
   movl   %esi,(%ecx)
   
   // copy arguments into place
   movl   $0,%esi
   movl   16(%esp),%edx
   movl   20(%esp),%eax
   jmp    LOCAL(vmJumpAndInvoke_argumentTest)

LOCAL(vmJumpAndInvoke_argumentLoop):
   movl   (%eax,%esi,1),%edi
   movl   %edi,4(%ecx,%esi,1)
   addl   $4,%esi

LOCAL(vmJumpAndInvoke_argumentTest):
   cmpl   %edx,%esi
   jb     LOCAL(vmJumpAndInvoke_argumentLoop)

   movl   8(%esp),%esi
   
   // the arguments have been copied, so we can set the real stack
   // pointer now
   movl   %ecx,%esp

   jmp    *%esi
#else // not AVIAN_CONTINUATIONS
   // vmJumpAndInvoke should only be called when continuations are
   // enabled
   int3
#endif // AVIAN_CONTINUATIONS
   
#else
#error unsupported architecture
#endif //def __x86_64__
