#include <stdlib.h>
#include <stddef.h>
#include <altivec.h>

#ifndef RTYPE
#define RTYPE TYPE
#endif

#ifdef DO_TRACE
#include <stdio.h>

#define TRACE(STRING, NUM)						\
do									\
  {									\
    fprintf (stderr, "%s: %2d\n", STRING, (int) NUM);			\
    fflush (stderr);							\
  }									\
while (0)

#ifndef FAIL_FORMAT
#define FAIL_FORMAT "%ld"
#define FAIL_CAST(X) ((long)(X))
#endif

#define FAIL(EXP, GOT)							 \
do									 \
  {									 \
    fprintf (stderr, "Expected: " FAIL_FORMAT ", got " FAIL_FORMAT "\n", \
	     FAIL_CAST (EXP), FAIL_CAST (GOT));				 \
    fflush (stderr);							 \
    abort ();								 \
  }									 \
while (0)

#else
#define TRACE(STRING, NUM)
#define FAIL(EXP, GOT) abort ()
#endif

static void
check (RTYPE, RTYPE) __attribute__((__noinline__));

static vector TYPE
deoptimize (vector TYPE) __attribute__((__noinline__));

static vector TYPE
*deoptimize_ptr (vector TYPE *)	__attribute__((__noinline__));

static void
check (RTYPE expected, RTYPE got)
{
  if (expected != got)
    FAIL (expected, got);
}

static vector TYPE
deoptimize (vector TYPE a)
{
  __asm__ (" # %x0" : "+v" (a));
  return a;
}

static vector TYPE *
deoptimize_ptr (vector TYPE *p)
{
  __asm__ (" # %0" : "+r" (p));
  return p;
}


RTYPE
get_auto_0 (vector TYPE a)
{
  TRACE ("get_auto_", 0);
  return (RTYPE) vec_extract (a, 0);
}

RTYPE
get_auto_1 (vector TYPE a)
{
  TRACE ("get_auto_", 1);
  return (RTYPE) vec_extract (a, 1);
}

#if ELEMENTS >= 4
RTYPE
get_auto_2 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 2);
}

RTYPE
get_auto_3 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 3);
}

#if ELEMENTS >= 8
RTYPE
get_auto_4 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 4);
}

RTYPE
get_auto_5 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 5);
}

RTYPE
get_auto_6 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 6);
}

RTYPE
get_auto_7 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 7);
}

#if ELEMENTS >= 16
RTYPE
get_auto_8 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 8);
}

RTYPE
get_auto_9 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 9);
}

RTYPE
get_auto_10 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 10);
}

RTYPE
get_auto_11 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 11);
}

RTYPE
get_auto_12 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 12);
}

RTYPE
get_auto_13 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 13);
}

RTYPE
get_auto_14 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 14);
}

RTYPE
get_auto_15 (vector TYPE a)
{
  return (RTYPE) vec_extract (a, 15);
}

#endif
#endif
#endif


/* Tests for the normal case of vec_extract where the vector is in a register
   and returning the result in a register as a return value.  */
#ifdef DISABLE_INLINE_OF_GET_AUTO_N
__attribute__ ((__noinline__))
#else
/* gcc issues warning: always_inline function might not be inlinable

   __attribute__ ((__always_inline__))
*/
#endif
RTYPE
get_auto_n (vector TYPE a, ssize_t n)
{
  return (RTYPE) vec_extract (a, n);
}

typedef RTYPE (*auto_func_type) (vector TYPE);

static auto_func_type get_auto_const[] = {
  get_auto_0,
  get_auto_1,
#if ELEMENTS >= 4
  get_auto_2,
  get_auto_3,
#if ELEMENTS >= 8
  get_auto_4,
  get_auto_5,
  get_auto_6,
  get_auto_7,
#if ELEMENTS >= 16
  get_auto_8,
  get_auto_9,
  get_auto_10,
  get_auto_11,
  get_auto_12,
  get_auto_13,
  get_auto_14,
  get_auto_15,
#endif
#endif
#endif
};

extern void do_auto (vector TYPE a) __attribute__((__noinline__));

void
do_auto (vector TYPE a)
{
  size_t i;

  for (i = 1; i < 40; i += 3)
    {
      TRACE ("do_auto, i: ", i);
      TRACE ("  get_auto_const[i] returns: ",
	     (*get_auto_const [i % ELEMENTS]) (a));
      TRACE ("  get_auto_n returns", get_auto_n (a, i));
      check (get_auto_n (a, i), (*get_auto_const [i % ELEMENTS]) (a));
    }
}



/* Main program to test all of the possibilities.  */
int
main (void)
{
  size_t i;
  vector TYPE x = INITIAL;
  vector TYPE *p, *p2, a, y;
  vector TYPE z[2];

  a = deoptimize (x);

  do_auto (a);

  return 0;
}