/* Copyright (C) 2002, 2003, 2004 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, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#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)								! \
  SINGLE_THREAD_P							! \
  bne icc0,0,L(pseudo_cancel)						! \
  setlos.p SYS_ify (syscall_name), gr7					! \
  setlos -4096, gr4							! \
  tra gr0, gr0								! \
  subcc gr8, gr4, gr0, icc0						! \
  bhi icc0,0x0,L(pseudo_error)						! \
  ret									! \
  L(pseudo_cancel):							! \
  addi sp,-STACK_SPACE(args),sp						! \
  SAVE_ARGS_##args							! \
  CENABLE								! \
  mov gr8, gr4								! \
  LOAD_ARGS_##args							! \
  ldi.p @(sp,4), gr15							! \
  setlos SYS_ify (syscall_name), gr7					! \
  tra gr0, gr0								! \
  sti gr8, @(sp,8)							! \
  mov gr4, gr8								! \
  CDISABLE								! \
  ldd.p @(sp,gr0), gr14							! \
  setlos -4096, gr4							! \
  ldi @(sp,8), gr8							! \
  movgs gr14, lr							! \
  subcc.p gr8, gr4, gr0, icc0						! \
  addi sp,STACK_SPACE(args),sp						! \
  blslr icc0,0x2							! \
  L(pseudo_error):							! \
  SYSCALL_ERROR_HANDLER

# define STACK_SPACE(n) (8 + ((n) == 0) * 8 + 4 * ((n) + (n) & 1))

# define SAVE_ARGS_0	movsg lr,gr14 ! std gr14, @(sp,gr0)
# define SAVE_ARGS_1	SAVE_ARGS_0 ! sti gr8, @(sp, 8)
# define SAVE_ARGS_2	SAVE_ARGS_0 ! stdi gr8, @(sp, 8)
# define SAVE_ARGS_3	SAVE_ARGS_2 ! sti gr10, @(sp, 16)
# define SAVE_ARGS_4	SAVE_ARGS_2 ! stdi gr10, @(sp, 16)
# define SAVE_ARGS_5	SAVE_ARGS_4 ! sti gr12, @(sp, 24)
# define SAVE_ARGS_6	SAVE_ARGS_4 ! stdi gr12, @(sp, 24)

# define LOAD_ARGS_0
# define LOAD_ARGS_1	LOAD_ARGS_0 ! ldi @(sp, 8), gr8
# define LOAD_ARGS_2	LOAD_ARGS_0 ! lddi @(sp, 8), gr8
# define LOAD_ARGS_3	LOAD_ARGS_2 ! ldi @(sp, 16), gr10
# define LOAD_ARGS_4	LOAD_ARGS_2 ! lddi @(sp, 16), gr10
# define LOAD_ARGS_5	LOAD_ARGS_4 ! ldi @(sp, 24), gr12
# define LOAD_ARGS_6	LOAD_ARGS_4 ! lddi @(sp, 24), gr12

# ifdef IS_IN_libpthread
#  define CENABLE	call __pthread_enable_asynccancel
#  define CDISABLE	call __pthread_disable_asynccancel
# elif defined IS_IN_librt
#  define CENABLE	call __librt_enable_asynccancel
#  define CDISABLE	call __librt_disable_asynccancel
# elif !defined NOT_IN_libc
#  define CENABLE	call __libc_enable_asynccancel
#  define CDISABLE	call __libc_disable_asynccancel
# endif

# ifdef IS_IN_libpthread
#  define __local_multiple_threads __pthread_multiple_threads
# elif defined IS_IN_librt
#  define __local_multiple_threads __librt_multiple_threads
# elif !defined NOT_IN_libc
#  define __local_multiple_threads __libc_multiple_threads
# endif

# ifndef __ASSEMBLER__
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)
# else
#  if !defined NOT_IN_libc || defined IS_IN_libpthread
#   define SINGLE_THREAD_P \
	sethi.p #gotoffhi(__local_multiple_threads), gr4	! \
	setlo #gotofflo(__local_multiple_threads), gr4		! \
	ld @(gr15, gr4), gr4					! \
	subcc gr4, gr0, gr0, icc0
#  else
#   define SINGLE_THREAD_P \
	sethi.p #gothi(__local_multiple_threads), gr4		! \
	setlo #gotlo(__local_multiple_threads), gr4		! \
	ld @(gr15, gr4), gr4					! \
	ld @(gr4, gr0), gr4					! \
	subcc gr4, gr0, gr0, icc0
#  endif
# endif

#elif !defined __ASSEMBLER__

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

#endif
