/* { dg-do run { target powerpc64-*-* } } */
/* { dg-require-effective-target lp64 } */
/* { dg-options "-O2" } */
#include <stdarg.h>
#include <signal.h>
#include <stdio.h>

/* Testcase to check for ABI compliance of parameter passing
   for the PowerPC64 ABI.  
   Parameter passing of integral and floating point is tested.  */

extern void abort (void);

typedef struct
{
  unsigned long gprs[8];
  double fprs[13];
} reg_parms_t;

reg_parms_t gparms;


/* Testcase could break on future gcc's, if parameter regs
   are changed before this asm.  */

#ifndef __MACH__
#define save_parms(lparms)				\
    asm volatile ("ld 11,gparms@got(2)\n\t"                \
                  "std 3,0(11)\n\t"		        \
	          "std 4,8(11)\n\t"			\
	          "std 5,16(11)\n\t"			\
	          "std 6,24(11)\n\t"			\
	          "std 7,32(11)\n\t"			\
	          "std 8,40(11)\n\t"			\
	          "std 9,48(11)\n\t"			\
	          "std 10,56(11)\n\t"                 \
                  "stfd 1,64(11)\n\t"		        \
	          "stfd 2,72(11)\n\t"			\
	          "stfd 3,80(11)\n\t"			\
	          "stfd 4,88(11)\n\t"			\
	          "stfd 5,96(11)\n\t"			\
	          "stfd 6,104(11)\n\t"			\
	          "stfd 7,112(11)\n\t"			\
	          "stfd 8,120(11)\n\t"			\
	          "stfd 9,128(11)\n\t"			\
	          "stfd 10,136(11)\n\t"			\
	          "stfd 11,144(11)\n\t"			\
	          "stfd 12,152(11)\n\t"                 \
	          "stfd 13,160(11)\n\t":::"11", "memory");  \
                  lparms = gparms;
#else
#define save_parms(lparms)				\
    asm volatile ("ld r11,gparms@got(r2)\n\t"           \
                  "std r3,0(r11)\n\t"		        \
	          "std r4,8(r11)\n\t"			\
	          "std r5,16(r11)\n\t"			\
	          "std r6,24(r11)\n\t"			\
	          "std r7,32(r11)\n\t"			\
	          "std r8,40(r11)\n\t"			\
	          "std r9,48(r11)\n\t"			\
	          "std r10,56(r11)\n\t"                 \
                  "stfd f1,64(r11)\n\t"		        \
	          "stfd f2,72(r11)\n\t"			\
	          "stfd f3,80(r11)\n\t"			\
	          "stfd f4,88(r11)\n\t"			\
	          "stfd f5,96(r11)\n\t"			\
	          "stfd f6,104(r11)\n\t"		\
	          "stfd f7,112(r11)\n\t"		\
	          "stfd f8,120(r11)\n\t"		\
	          "stfd f9,128(r11)\n\t"		\
	          "stfd f10,136(r11)\n\t"		\
	          "stfd f11,144(r11)\n\t"		\
	          "stfd f12,152(r11)\n\t"               \
	          "stfd f13,160(r11)\n\t":::"r11", "memory");  \
                  lparms = gparms;
#endif

/* Stackframe structure relevant for parameter passing.  */
typedef union
{
  double d;
  unsigned long l;
  unsigned int i[2];
} parm_t;

typedef struct sf
{
  struct sf *backchain;
  long a1;
  long a2;
  long a3;
  long a4;
  long a5;
  parm_t slot[100];
} stack_frame_t;


/* Paramter passing.
   s : gpr 3
   l : gpr 4
   d : fpr 1
*/
void __attribute__ ((noinline)) fcld (char *s, long l, double d)
{
  reg_parms_t lparms;
  save_parms (lparms);

  if (s != (char *) lparms.gprs[0])
    abort ();

  if (l != lparms.gprs[1])
    abort ();

  if (d != lparms.fprs[0])
    abort ();
}

/* Paramter passing.
   s : gpr 3
   l : gpr 4
   d : fpr 2
   i : gpr 5
*/
void __attribute__ ((noinline))
fcldi (char *s, long l, double d, signed int i)
{
  reg_parms_t lparms;
  save_parms (lparms);

  if (s != (char *) lparms.gprs[0])
    abort ();

  if (l != lparms.gprs[1])
    abort ();

  if (d != lparms.fprs[0])
    abort ();

  if ((signed long) i != lparms.gprs[3])
    abort ();
}

/* Paramter passing.
   s : gpr 3
   l : gpr 4
   d : fpr 2
   i : gpr 5
*/
void __attribute__ ((noinline))
fcldu (char *s, long l, float d, unsigned int i)
{
  reg_parms_t lparms;
  save_parms (lparms);

  if (s != (char *) lparms.gprs[0])
    abort ();

  if (l != lparms.gprs[1])
    abort ();

  if ((double) d != lparms.fprs[0])
    abort ();

  if ((unsigned long) i != lparms.gprs[3])
    abort ();
}

/* Paramter passing.
   s : gpr 3
   l : slot 1
   d : slot 2
*/

void __attribute__ ((noinline)) fceld (char *s, ...)
{
  stack_frame_t *sp;
  reg_parms_t lparms;
  va_list arg;
  double d;
  long l;
  save_parms (lparms);

  va_start (arg, s);

  if (s != (char *) lparms.gprs[0])
    abort ();

  l = va_arg (arg, long);
  d = va_arg (arg, double);

  /* Go back one frame.  */
  sp = __builtin_frame_address (0);
  sp = sp->backchain;

  if (sp->slot[1].l != l)
    abort ();

  if (sp->slot[2].d != d)
    abort ();
}

/* Paramter passing.
   s : gpr 3
   i : gpr 4
   j : gpr 5
   d : slot 3
   l : slot 4
*/
void __attribute__ ((noinline)) fciiedl (char *s, int i, int j, ...)
{
  stack_frame_t *sp;
  reg_parms_t lparms;
  va_list arg;
  double d;
  long l;
  save_parms (lparms);

  va_start (arg, j);

  if (s != (char *) lparms.gprs[0])
    abort ();

  if ((long) i != lparms.gprs[1])
    abort ();

  if ((long) j != lparms.gprs[2])
    abort ();

  d = va_arg (arg, double);
  l = va_arg (arg, long);

  sp = __builtin_frame_address (0);
  sp = sp->backchain;

  if (sp->slot[3].d != d)
    abort ();

  if (sp->slot[4].l != l)
    abort ();
}

/* 
Parameter     Register     Offset in parameter save area
c             r3           0-7    (not stored in parameter save area)
ff            f1           8-15   (not stored)
d             r5           16-23  (not stored)
ld            f2           24-31  (not stored)
f             r7           32-39  (not stored)
s             r8,r9        40-55  (not stored)
gg            f3           56-63  (not stored)
t             (none)       64-79  (stored in parameter save area)
e             (none)       80-87  (stored)
hh            f4           88-95  (stored)  

*/

typedef struct
{
  int a;
  double dd;
} sparm;

typedef union
{
  int i[2];
  long l;
  double d;
} double_t;

/* Example from ABI documentation with slight changes.
   Paramter passing. 
   c  : gpr 3
   ff : fpr 1
   d  : gpr 5
   ld : fpr 2
   f  : gpr 7
   s  : gpr 8 - 9
   gg : fpr 3
   t  : save area offset 64 - 79 
   e  : save area offset 80 - 88
   hh : fpr 4   
*/

void __attribute__ ((noinline))
fididisdsid (int c, double ff, int d, double ld, int f,
	     sparm s, double gg, sparm t, int e, double hh)
{
  stack_frame_t *sp;
  reg_parms_t lparms;
  double_t dx, dy;

  save_parms (lparms);

  /* Parm 0: int.  */
  if ((long) c != lparms.gprs[0])
    abort ();

  /* Parm 1: double.  */
  if (ff != lparms.fprs[0])
    abort ();

  /* Parm 2: int.  */
  if ((long) d != lparms.gprs[2])
    abort ();

  /* Parm 3: double.  */
  if (ld != lparms.fprs[1])
    abort ();

  /* Parm 4: int.  */
  if ((long) f != lparms.gprs[4])
    abort ();

  /* Parm 5: struct sparm.  */
  dx.l = lparms.gprs[5];
  dy.l = lparms.gprs[6];

  if (s.a != dx.i[0])
    abort ();
  if (s.dd != dy.d)
    abort ();

  /* Parm 6: double.  */
  if (gg != lparms.fprs[2])
    abort ();

  sp = __builtin_frame_address (0);
  sp = sp->backchain;

  /* Parm 7: struct sparm.  */
  dx.l = sp->slot[8].l;
  dy.l = sp->slot[9].l;
  if (t.a != dx.i[0])
    abort ();
  if (t.dd != dy.d)
    abort ();

  /* Parm 8: int.  */
  if (e != sp->slot[10].l)
    abort ();

  /* Parm 9: double.  */

  if (hh != lparms.fprs[3])
    abort ();
}

int
main ()
{
  char *s = "ii";

  fcld (s, 1, 1.0);
  fcldi (s, 1, 1.0, -2);
  fcldu (s, 1, 1.0, 2);
  fceld (s, 1, 1.0);
  fciiedl (s, 1, 2, 1.0, 3);
  fididisdsid (1, 1.0, 2, 2.0, -1, (sparm)
	       {
	       3, 3.0}, 4.0, (sparm)
	       {
	       5, 5.0}, 6, 7.0);
  return 0;
}
