#ifndef _avcall_ia64_c				/*-*- C -*-*/
#define _avcall_ia64_c
/**
  Copyright 1993 Bill Triggs, <Bill.Triggs@inrialpes.fr>
  Copyright 1995-2001 Bruno Haible, <haible@clisp.cons.org>

  This is free software distributed under the GNU General Public
  Licence described in the file COPYING. Contact the author if
  you don't have this or can't live with it. There is ABSOLUTELY
  NO WARRANTY, explicit or implied, on this software.
**/
/*----------------------------------------------------------------------
  !!! THIS ROUTINE MUST BE COMPILED gcc -O !!!

  Foreign function interface for a Intel IA-64 in little-endian mode with gcc.

  This calls a C function with an argument list built up using macros
  defined in av_call.h.

  IA-64 64-bit Argument Passing Conventions:

  The argument sequence is mapped linearly on the registers r32,...,r39,
  and continued on the stack, in [r12+16], [r12+24], ...
  Items in this sequence are word-aligned. Structures larger than a single
  word are even two-word-aligned.
  Integer/pointer arguments are passed in the allocated slots (registers
  or stack slots). The first 8 float/double arguments are passed in
  registers f8,...,f15 instead, but their slots are kept allocated.
  Structure args are passed like multiple integer arguments; except that
  structures consisting only of floats or only of doubles are passed like
  multiple float arguments or multiple double arguments, respectively.

  Integers and pointers are returned in r8, floats and doubles in f8.
  Structures consisting only of at most 8 floats or only of at most 8 doubles
  are returned in f8,...,f15. Other than that, structures of size <= 32 bytes
  are returned in r8,...,r11, as if these were 4 contiguous words in memory.
  Larger structures are returned in memory; the caller passes the address
  of the target memory area in r8, and it is returned unmodified in r8.
  ----------------------------------------------------------------------*/
#include "avcall.h.in"

#define RETURN(TYPE,VAL)	(*(TYPE*)l->raddr = (TYPE)(VAL))

register __avword*	sret	__asm__("r8");  /* structure return pointer */
register __avword	iret	__asm__("r8");
register __avword	iret2	__asm__("r9");
register __avword	iret3	__asm__("r10");
register __avword	iret4	__asm__("r11");
/*register float	fret	__asm__("f8");*/
/*register double	dret	__asm__("f8");*/
register double		farg1	__asm__("f8");
register double		farg2	__asm__("f9");
register double		farg3	__asm__("f10");
register double		farg4	__asm__("f11");
register double		farg5	__asm__("f12");
register double		farg6	__asm__("f13");
register double		farg7	__asm__("f14");
register double		farg8	__asm__("f15");

int
__builtin_avcall(av_alist* l)
{
  register __avword*	sp	__asm__("r12"); /* C names for registers */

  __avword* argframe = (sp -= __AV_ALIST_WORDS) + 2; /* make room for argument list */
  int arglen = l->aptr - l->args;
  int farglen = l->faptr - l->fargs;
  __avword i;

  for (i = 8; i < arglen; i++)		/* push function args onto stack */
    argframe[i-8] = l->args[i];

  /* struct return address */
  if (l->rtype == __AVstruct)
    sret = l->raddr;

  /* put max. 8 double args in registers */
  if (farglen > 0) {
    farg1 = l->fargs[0];
    if (farglen > 1) {
      farg2 = l->fargs[1];
      if (farglen > 2) {
        farg3 = l->fargs[2];
        if (farglen > 3) {
          farg4 = l->fargs[3];
          if (farglen > 4) {
            farg5 = l->fargs[4];
            if (farglen > 5) {
              farg6 = l->fargs[5];
              if (farglen > 6) {
                farg7 = l->fargs[6];
                if (farglen > 7)
                  farg8 = l->fargs[7];
              }
            }
          }
        }
      }
    }
  }

  /* call function, pass 8 integer and 8 double args in registers */
  if (l->rtype == __AVfloat) {
    *(float*)l->raddr = (*(float(*)())l->func)(l->args[0], l->args[1],
					       l->args[2], l->args[3],
					       l->args[4], l->args[5],
					       l->args[6], l->args[7]);
  } else
  if (l->rtype == __AVdouble) {
    *(double*)l->raddr = (*(double(*)())l->func)(l->args[0], l->args[1],
						 l->args[2], l->args[3],
						 l->args[4], l->args[5],
						 l->args[6], l->args[7]);
  } else {
    i = (*l->func)(l->args[0], l->args[1], l->args[2], l->args[3],
		   l->args[4], l->args[5], l->args[6], l->args[7]);

    /* save return value */
    if (l->rtype == __AVvoid) {
    } else
    if (l->rtype == __AVword) {
      RETURN(__avword, i);
    } else
    if (l->rtype == __AVchar) {
      RETURN(char, i);
    } else
    if (l->rtype == __AVschar) {
      RETURN(signed char, i);
    } else
    if (l->rtype == __AVuchar) {
      RETURN(unsigned char, i);
    } else
    if (l->rtype == __AVshort) {
      RETURN(short, i);
    } else
    if (l->rtype == __AVushort) {
      RETURN(unsigned short, i);
    } else
    if (l->rtype == __AVint) {
      RETURN(int, i);
    } else
    if (l->rtype == __AVuint) {
      RETURN(unsigned int, i);
    } else
    if (l->rtype == __AVlong || l->rtype == __AVlonglong) {
      RETURN(long, i);
    } else
    if (l->rtype == __AVulong || l->rtype == __AVulonglong) {
      RETURN(unsigned long, i);
    } else
  /* see above
    if (l->rtype == __AVfloat) {
    } else
    if (l->rtype == __AVdouble) {
    } else
  */
    if (l->rtype == __AVvoidp) {
      RETURN(void*, i);
    } else
    if (l->rtype == __AVstruct) {
      if (l->flags & __AV_PCC_STRUCT_RETURN) {
        /* pcc struct return convention: need a  *(TYPE*)l->raddr = *(TYPE*)i;  */
        if (l->rsize == sizeof(char)) {
          RETURN(char, *(char*)i);
        } else
        if (l->rsize == sizeof(short)) {
          RETURN(short, *(short*)i);
        } else
        if (l->rsize == sizeof(int)) {
          RETURN(int, *(int*)i);
        } else
        if (l->rsize == sizeof(long)) {
          RETURN(long, *(long*)i);
        } else {
          int n = (l->rsize + sizeof(__avword)-1)/sizeof(__avword);
          while (--n >= 0)
            ((__avword*)l->raddr)[n] = ((__avword*)i)[n];
        }
      } else {
        /* normal struct return convention */
        if (l->flags & __AV_REGISTER_STRUCT_RETURN) {
          /* Return structs of size <= 32 in registers. */
          if (l->rsize > 0 && l->rsize <= 32) {
            if (l->rsize >= 1)
              ((unsigned char *)l->raddr)[0] = (unsigned char)(i);
            if (l->rsize >= 2)
              ((unsigned char *)l->raddr)[1] = (unsigned char)(i>>8);
            if (l->rsize >= 3)
              ((unsigned char *)l->raddr)[2] = (unsigned char)(i>>16);
            if (l->rsize >= 4)
              ((unsigned char *)l->raddr)[3] = (unsigned char)(i>>24);
            if (l->rsize >= 5)
              ((unsigned char *)l->raddr)[4] = (unsigned char)(i>>32);
            if (l->rsize >= 6)
              ((unsigned char *)l->raddr)[5] = (unsigned char)(i>>40);
            if (l->rsize >= 7)
              ((unsigned char *)l->raddr)[6] = (unsigned char)(i>>48);
            if (l->rsize >= 8)
              ((unsigned char *)l->raddr)[7] = (unsigned char)(i>>56);
            if (l->rsize >= 9) {
              ((unsigned char *)l->raddr)[8] = (unsigned char)(iret2);
              if (l->rsize >= 10)
                ((unsigned char *)l->raddr)[9] = (unsigned char)(iret2>>8);
              if (l->rsize >= 11)
                ((unsigned char *)l->raddr)[10] = (unsigned char)(iret2>>16);
              if (l->rsize >= 12)
                ((unsigned char *)l->raddr)[11] = (unsigned char)(iret2>>24);
              if (l->rsize >= 13)
                ((unsigned char *)l->raddr)[12] = (unsigned char)(iret2>>32);
              if (l->rsize >= 14)
                ((unsigned char *)l->raddr)[13] = (unsigned char)(iret2>>40);
              if (l->rsize >= 15)
                ((unsigned char *)l->raddr)[14] = (unsigned char)(iret2>>48);
              if (l->rsize >= 16)
                ((unsigned char *)l->raddr)[15] = (unsigned char)(iret2>>56);
              if (l->rsize >= 17) {
                ((unsigned char *)l->raddr)[16] = (unsigned char)(iret3);
                if (l->rsize >= 18)
                  ((unsigned char *)l->raddr)[17] = (unsigned char)(iret3>>8);
                if (l->rsize >= 19)
                  ((unsigned char *)l->raddr)[18] = (unsigned char)(iret3>>16);
                if (l->rsize >= 20)
                  ((unsigned char *)l->raddr)[19] = (unsigned char)(iret3>>24);
                if (l->rsize >= 21)
                  ((unsigned char *)l->raddr)[20] = (unsigned char)(iret3>>32);
                if (l->rsize >= 22)
                  ((unsigned char *)l->raddr)[21] = (unsigned char)(iret3>>40);
                if (l->rsize >= 23)
                  ((unsigned char *)l->raddr)[22] = (unsigned char)(iret3>>48);
                if (l->rsize >= 24)
                  ((unsigned char *)l->raddr)[23] = (unsigned char)(iret3>>56);
                if (l->rsize >= 25) {
                  ((unsigned char *)l->raddr)[24] = (unsigned char)(iret4);
                  if (l->rsize >= 26)
                    ((unsigned char *)l->raddr)[25] = (unsigned char)(iret4>>8);
                  if (l->rsize >= 27)
                    ((unsigned char *)l->raddr)[26] = (unsigned char)(iret4>>16);
                  if (l->rsize >= 28)
                    ((unsigned char *)l->raddr)[27] = (unsigned char)(iret4>>24);
                  if (l->rsize >= 29)
                    ((unsigned char *)l->raddr)[28] = (unsigned char)(iret4>>32);
                  if (l->rsize >= 30)
                    ((unsigned char *)l->raddr)[29] = (unsigned char)(iret4>>40);
                  if (l->rsize >= 31)
                    ((unsigned char *)l->raddr)[30] = (unsigned char)(iret4>>48);
                  if (l->rsize >= 32)
                    ((unsigned char *)l->raddr)[31] = (unsigned char)(iret4>>56);
                }
              }
            }
          }
        }
      }
    }
  }
  return 0;
}

#endif /*_avcall_ia64_c */
