/* Copyright (C) 2002-2014 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   Contributed by Alexandre Oliva <aoliva@redhat.com>

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library.  If not, see
   <http://www.gnu.org/licenses/>.  */

#include <sysdep.h>
#include <tls.h>
#include <pt-machine.h>
#ifndef __ASSEMBLER__
# include <linuxthreads/internals.h>
#endif

#if !defined NOT_IN_libc || defined IS_IN_libpthread || defined IS_IN_librt

# undef PSEUDO
# define PSEUDO(name, syscall_name, args)				\
  .text	;								\
 ENTRY (name)								\
  PUSHARGS_##args							\
  DOARGS_##args								\
  SINGLE_THREAD_P;							\
  bne L(pseudo_cancel);							\
  mov SYS_ify (syscall_name),d0;					\
  syscall 0								\
  POPARGS_##args ;							\
  cmp -126,d0;								\
  bls L(pseudo_end);							\
  jmp SYSCALL_ERROR_LABEL;						\
 L(pseudo_cancel):							\
  add -(16+STACK_SPACE (args)),sp;					\
  SAVE_ARGS_##args							\
  CENABLE								\
  mov d0,r0;								\
  LOAD_ARGS_##args							\
  mov SYS_ify (syscall_name),d0;					\
  syscall 0;								\
  mov d0,(12,sp);							\
  mov r0,d0;								\
  CDISABLE								\
  mov (12,sp),d0;							\
  add +16+STACK_SPACE (args),sp						\
  POPARGS_##args ;							\
  cmp -126,d0;								\
  bls L(pseudo_end);							\
  jmp SYSCALL_ERROR_LABEL;						\
 L(pseudo_end):								\
  mov d0,a0

/* Reserve up to 2 stack slots for a0 and d1, but fewer than that if
   we don't have that many arguments.  */
# define STACK_SPACE(n) (((((n) < 3) * (2 - (n))) + 2) * 4)

# define SAVE_ARGS_0
# define SAVE_ARGS_1	mov a0,(20,sp) ;
# define SAVE_ARGS_2	SAVE_ARGS_1 mov d1,(24,sp) ;
# define SAVE_ARGS_3	SAVE_ARGS_2
# define SAVE_ARGS_4	SAVE_ARGS_3
# define SAVE_ARGS_5	SAVE_ARGS_4
# define SAVE_ARGS_6	SAVE_ARGS_5

# define LOAD_ARGS_0
# define LOAD_ARGS_1	mov (20,sp),a0 ;
# define LOAD_ARGS_2	LOAD_ARGS_1 mov (24,sp),d1 ;
# define LOAD_ARGS_3	LOAD_ARGS_2
# define LOAD_ARGS_4	LOAD_ARGS_3
# define LOAD_ARGS_5	LOAD_ARGS_4
# define LOAD_ARGS_6	LOAD_ARGS_5

# ifdef IS_IN_libpthread
#  define CENABLE	call __pthread_enable_asynccancel,[],0;
#  define CDISABLE	call __pthread_disable_asynccancel,[],0;
# elif defined IS_IN_librt
#  ifdef PIC
#   define CENABLE	movm [a2],(sp); \
			1: mov pc,a2; \
			add _GLOBAL_OFFSET_TABLE_-(1b-.),a2; \
			call +__librt_enable_asynccancel@PLT,[],0; \
			movm (sp),[a2];
#   define CENABLE	movm [a2],(sp); \
			1: mov pc,a2; \
			add _GLOBAL_OFFSET_TABLE_-(1b-.),a2; \
			call +__librt_disable_asynccancel@PLT,[],0; \
			movm (sp),[a2];
#  else
#   define CENABLE	call +__librt_enable_asynccancel,[],0;
#   define CDISABLE	call +__librt_disable_asynccancel,[],0;
#  endif
# else
#  define CENABLE	call +__libc_enable_asynccancel,[],0;
#  define CDISABLE	call +__libc_disable_asynccancel,[],0;
# endif

#if !defined NOT_IN_libc
# define __local_multiple_threads __libc_multiple_threads
#elif defined IS_IN_libpthread
# define __local_multiple_threads __pthread_multiple_threads
#else
# define __local_multiple_threads __librt_multiple_threads
#endif

# ifndef __ASSEMBLER__
#  if defined FLOATING_STACKS && USE___THREAD && defined PIC
#   define SINGLE_THREAD_P \
  __builtin_expect (THREAD_GETMEM (THREAD_SELF,				      \
				   p_header.data.multiple_threads) == 0, 1)
#  else
extern int __local_multiple_threads
#   if !defined NOT_IN_libc || defined IS_IN_libpthread
  attribute_hidden;
#   else
  ;
#   endif
#   define SINGLE_THREAD_P __builtin_expect (__local_multiple_threads == 0, 1)
#  endif
# else
#  if !defined PIC
#   define SINGLE_THREAD_P \
	mov (+__local_multiple_threads),d0; \
	cmp 0,d0
#  elif !defined NOT_IN_libc || defined IS_IN_libpthread
#   define SINGLE_THREAD_P \
	movm [a2],(sp); \
     1: mov pc,a2; \
	add _GLOBAL_OFFSET_TABLE_-(1b-.),a2; \
	mov (+__local_multiple_threads@GOTOFF,a2),d0; \
	movm (sp),[a2]; \
	cmp 0,d0
#  else
#   define SINGLE_THREAD_P \
	movm [a2],(sp); \
     1: mov pc,a2; \
	add _GLOBAL_OFFSET_TABLE_-(1b-.),a2; \
	mov (+__local_multiple_threads@GOT,a2),a2; \
	mov (a2),d0; \
	movm (sp),[a2]; \
	cmp 0,d0
#  endif
# endif

#elif !defined __ASSEMBLER__

/* This code should never be used but we define it anyhow.  */
# define SINGLE_THREAD_P (1)

#endif
