/*  Copyright (C) 2011 IBM

 Author: Maynard Johnson <maynardj@us.ibm.com>

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

 This program 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
 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, see <http://www.gnu.org/licenses/>.

 The GNU General Public License is contained in the file COPYING.
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

#ifdef HAS_VSX

#include <altivec.h>

#ifndef __powerpc64__
typedef uint32_t HWord_t;
#else
typedef uint64_t HWord_t;
#endif /* __powerpc64__ */

#ifdef VGP_ppc64le_linux
#define isLE 1
#else
#define isLE 0
#endif

register HWord_t r14 __asm__ ("r14");
register HWord_t r15 __asm__ ("r15");
register HWord_t r16 __asm__ ("r16");
register HWord_t r17 __asm__ ("r17");
register double f14 __asm__ ("fr14");
register double f15 __asm__ ("fr15");
register double f16 __asm__ ("fr16");
register double f17 __asm__ ("fr17");

static volatile unsigned int cond_reg;

#define ALLCR "cr0","cr1","cr2","cr3","cr4","cr5","cr6","cr7"

#define SET_CR(_arg) \
      __asm__ __volatile__ ("mtcr  %0" : : "b"(_arg) : ALLCR );

#define SET_XER(_arg) \
      __asm__ __volatile__ ("mtxer %0" : : "b"(_arg) : "xer" );

#define GET_CR(_lval) \
      __asm__ __volatile__ ("mfcr %0"  : "=b"(_lval) )

#define GET_XER(_lval) \
      __asm__ __volatile__ ("mfxer %0" : "=b"(_lval) )

#define GET_CR_XER(_lval_cr,_lval_xer) \
   do { GET_CR(_lval_cr); GET_XER(_lval_xer); } while (0)

#define SET_CR_ZERO \
      SET_CR(0)

#define SET_XER_ZERO \
      SET_XER(0)

#define SET_CR_XER_ZERO \
   do { SET_CR_ZERO; SET_XER_ZERO; } while (0)

#define SET_FPSCR_ZERO \
   do { double _d = 0.0; \
        __asm__ __volatile__ ("mtfsf 0xFF, %0" : : "f"(_d) ); \
   } while (0)


typedef void (*test_func_t)(void);
typedef struct ldst_test ldst_test_t;
typedef struct vsx_logic_test logic_test_t;
typedef struct xs_conv_test xs_conv_test_t;
typedef struct p7_fp_test fp_test_t;
typedef struct vx_fp_test vx_fp_test_t;
typedef struct vsx_move_test move_test_t;
typedef struct vsx_permute_test permute_test_t;
typedef struct test_table test_table_t;

static double *fargs = NULL;
static int nb_fargs;

/* These functions below that construct a table of floating point
 * values were lifted from none/tests/ppc32/jm-insns.c.
 */

#if defined (DEBUG_ARGS_BUILD)
#define AB_DPRINTF(fmt, args...) do { fprintf(stderr, fmt , ##args); } while (0)
#else
#define AB_DPRINTF(fmt, args...) do { } while (0)
#endif

static inline void register_farg (void *farg,
                                  int s, uint16_t _exp, uint64_t mant)
{
   uint64_t tmp;

   tmp = ((uint64_t)s << 63) | ((uint64_t)_exp << 52) | mant;
   *(uint64_t *)farg = tmp;
   AB_DPRINTF("%d %03x %013llx => %016llx %0e\n",
              s, _exp, mant, *(uint64_t *)farg, *(double *)farg);
}

static void build_fargs_table(void)
/*
 * Double precision:
 * Sign goes from zero to one               (1 bit)
 * Exponent goes from 0 to ((1 << 12) - 1)  (11 bits)
 * Mantissa goes from 1 to ((1 << 52) - 1)  (52 bits)
 * + special values:
 * +0.0      : 0 0x000 0x0000000000000 => 0x0000000000000000
 * -0.0      : 1 0x000 0x0000000000000 => 0x8000000000000000
 * +infinity : 0 0x7FF 0x0000000000000 => 0x7FF0000000000000
 * -infinity : 1 0x7FF 0x0000000000000 => 0xFFF0000000000000
 * +QNaN     : 0 0x7FF 0x8000000000000 => 0x7FF8000000000000
 * -QNaN     : 1 0x7FF 0x8000000000000 => 0xFFF8000000000000
 * +SNaN     : 0 0x7FF 0x7FFFFFFFFFFFF => 0x7FF7FFFFFFFFFFFF
 * -SNaN     : 1 0x7FF 0x7FFFFFFFFFFFF => 0xFFF7FFFFFFFFFFFF
 * (8 values)
 *
 * Single precision
 * Sign:     1 bit
 * Exponent: 8 bits
 * Mantissa: 23 bits
 * +0.0      : 0 0x00 0x000000 => 0x00000000
 * -0.0      : 1 0x00 0x000000 => 0x80000000
 * +infinity : 0 0xFF 0x000000 => 0x7F800000
 * -infinity : 1 0xFF 0x000000 => 0xFF800000
 * +QNaN     : 0 0xFF 0x400000 => 0x7FC00000
 * -QNaN     : 1 0xFF 0x400000 => 0xFFC00000
 * +SNaN     : 0 0xFF 0x3FFFFF => 0x7FBFFFFF
 * -SNaN     : 1 0xFF 0x3FFFFF => 0xFFBFFFFF
*/
{
   uint64_t mant;
   uint16_t _exp, e1;
   int s;
   int i=0;

   if (nb_fargs)
      return;

   fargs = malloc( 16 * sizeof(double) );
   for (s = 0; s < 2; s++) {
      for (e1 = 0x001;; e1 = ((e1 + 1) << 13) + 7) {
         if (e1 >= 0x400)
            e1 = 0x3fe;
         _exp = e1;
         for (mant = 0x0000000000001ULL; mant < (1ULL << 52);
         /* Add 'random' bits */
         mant = ((mant + 0x4A6) << 29) + 0x359) {
            register_farg( &fargs[i++], s, _exp, mant );
         }
         if (e1 == 0x3fe)
            break;
      }
   }
   // add a few smaller values to fargs . . .
   s = 0;
   _exp = 0x002;
   mant = 0x0000000000b01ULL;
   register_farg(&fargs[i++], s, _exp, mant);

   _exp = 0x000;
   mant = 0x00000203f0b3dULL;
   register_farg(&fargs[i++], s, _exp, mant);

   mant = 0x00000005a203dULL;
   register_farg(&fargs[i++], s, _exp, mant);

   s = 1;
   _exp = 0x002;
   mant = 0x0000000000b01ULL;
   register_farg(&fargs[i++], s, _exp, mant);

   _exp = 0x000;
   mant = 0x00000203f0b3dULL;
   register_farg(&fargs[i++], s, _exp, mant);

   nb_fargs = i;
}


typedef struct fp_test_args {
   int fra_idx;
   int frb_idx;
   int cr_flags;
} fp_test_args_t;


fp_test_args_t ftdiv_tests[] = {
                              {0, 1, 0x8},
                              {9, 1, 0xa},
                              {1, 12, 0xa},
                              {0, 2, 0xa},
                              {1, 3, 0xa},
                              {3, 0, 0xa},
                              {0, 3, 0xa},
                              {4, 0, 0xa},
                              {7, 1, 0xe},
                              {8, 1, 0xe},
                              {1, 7, 0xe},
                              {0, 13, 0xe},
                              {5, 5, 0xe},
                              {5, 6, 0xe},
};

fp_test_args_t xscmpX_tests[] = {
                                   {8, 8, 0x2},
                                   {8, 14, 0x8},
                                   {8, 6, 0x8},
                                   {8, 5, 0x8},
                                   {8, 4, 0x8},
                                   {8, 7, 0x8},
                                   {8, 9, 0x1},
                                   {8, 11, 0x1},
                                   {14, 8, 0x4},
                                   {14, 14, 0x2},
                                   {14, 6, 0x8},
                                   {14, 5, 0x8},
                                   {14, 4, 0x8},
                                   {14, 7, 0x8},
                                   {14, 9, 0x1},
                                   {14, 11, 0x1},
                                   {6, 8, 0x4},
                                   {6, 14, 0x4},
                                   {6, 6, 0x2},
                                   {6, 5, 0x2},
                                   {6, 4, 0x8},
                                   {6, 7, 0x8},
                                   {6, 9, 0x1},
                                   {6, 11, 0x1},
                                   {5, 8, 0x4},
                                   {5, 14, 0x4},
                                   {5, 6, 0x2},
                                   {5, 5, 0x2},
                                   {5, 4, 0x8},
                                   {5, 7, 0x8},
                                   {5, 9, 0x1},
                                   {5, 11, 0x1},
                                   {4, 8, 0x4},
                                   {4, 14, 0x4},
                                   {4, 6, 0x4},
                                   {4, 5, 0x4},
                                   {4, 1, 0x8},
                                   {4, 7, 0x8},
                                   {4, 9, 0x1},
                                   {4, 11, 0x1},
                                   {7, 8, 0x4},
                                   {7, 14, 0x4},
                                   {7, 6, 0x4},
                                   {7, 5, 0x4},
                                   {7, 4, 0x4},
                                   {7, 7, 0x2},
                                   {7, 9, 0x1},
                                   {7, 11, 0x1},
                                   {10, 8, 0x1},
                                   {10, 14, 0x1},
                                   {10, 6, 0x1},
                                   {10, 5, 0x1},
                                   {10, 4, 0x1},
                                   {10, 7, 0x1},
                                   {10, 9, 0x1},
                                   {10, 11, 0x1},
                                   {12, 8, 0x1},
                                   {12, 14, 0x1},
                                   {12, 6, 0x1},
                                   {12, 5, 0x1},
                                   {12, 4, 0x1},
                                   {12, 7, 0x1},
                                   {12, 9, 0x1},
                                   {12, 11, 0x1},
};

fp_test_args_t xsadddp_tests[] = {
                                   {8, 8, 0x0},
                                   {8, 14, 0x0},
                                   {8, 6, 0x0},
                                   {8, 5, 0x0},
                                   {8, 4, 0x0},
                                   {8, 7, 0x0},
                                   {8, 9, 0x0},
                                   {8, 11, 0x0},
                                   {14, 8, 0x0},
                                   {14, 14, 0x0},
                                   {14, 6, 0x0},
                                   {14, 5, 0x0},
                                   {14, 4, 0x0},
                                   {14, 7, 0x0},
                                   {14, 9, 0x0},
                                   {14, 11, 0x0},
                                   {6, 8, 0x0},
                                   {6, 14, 0x0},
                                   {6, 6, 0x0},
                                   {6, 5, 0x0},
                                   {6, 4, 0x0},
                                   {6, 7, 0x0},
                                   {6, 9, 0x0},
                                   {6, 11, 0x0},
                                   {5, 8, 0x0},
                                   {5, 14, 0x0},
                                   {5, 6, 0x0},
                                   {5, 5, 0x0},
                                   {5, 4, 0x0},
                                   {5, 7, 0x0},
                                   {5, 9, 0x0},
                                   {5, 11, 0x0},
                                   {4, 8, 0x0},
                                   {4, 14, 0x0},
                                   {4, 6, 0x0},
                                   {4, 5, 0x0},
                                   {4, 1, 0x0},
                                   {4, 7, 0x0},
                                   {4, 9, 0x0},
                                   {4, 11, 0x0},
                                   {7, 8, 0x0},
                                   {7, 14, 0x0},
                                   {7, 6, 0x0},
                                   {7, 5, 0x0},
                                   {7, 4, 0x0},
                                   {7, 7, 0x0},
                                   {7, 9, 0x0},
                                   {7, 11, 0x0},
                                   {10, 8, 0x0},
                                   {10, 14, 0x0},
                                   {10, 6, 0x0},
                                   {10, 5, 0x0},
                                   {10, 4, 0x0},
                                   {10, 7, 0x0},
                                   {10, 9, 0x0},
                                   {10, 11, 0x0},
                                   {12, 8, 0x0},
                                   {12, 14, 0x0},
                                   {12, 6, 0x0},
                                   {12, 5, 0x0},
                                   {12, 4, 0x0},
                                   {12, 7, 0x0},
                                   {12, 9, 0x0},
                                   {12, 11, 0x0},
};

fp_test_args_t xsdivdp_tests[] = {
                                   {8, 8, 0x0},
                                   {8, 14, 0x0},
                                   {8, 6, 0x0},
                                   {8, 5, 0x0},
                                   {8, 4, 0x0},
                                   {8, 7, 0x0},
                                   {8, 9, 0x0},
                                   {8, 11, 0x0},
                                   {14, 8, 0x0},
                                   {14, 14, 0x0},
                                   {14, 6, 0x0},
                                   {14, 5, 0x0},
                                   {14, 4, 0x0},
                                   {14, 7, 0x0},
                                   {14, 9, 0x0},
                                   {14, 11, 0x0},
                                   {6, 8, 0x0},
                                   {6, 14, 0x0},
                                   {6, 6, 0x0},
                                   {6, 5, 0x0},
                                   {6, 4, 0x0},
                                   {6, 7, 0x0},
                                   {6, 9, 0x0},
                                   {6, 11, 0x0},
                                   {5, 8, 0x0},
                                   {5, 14, 0x0},
                                   {5, 6, 0x0},
                                   {5, 5, 0x0},
                                   {5, 4, 0x0},
                                   {5, 7, 0x0},
                                   {5, 9, 0x0},
                                   {5, 11, 0x0},
                                   {4, 8, 0x0},
                                   {4, 14, 0x0},
                                   {4, 6, 0x0},
                                   {4, 5, 0x0},
                                   {4, 1, 0x0},
                                   {4, 7, 0x0},
                                   {4, 9, 0x0},
                                   {4, 11, 0x0},
                                   {7, 8, 0x0},
                                   {7, 14, 0x0},
                                   {7, 6, 0x0},
                                   {7, 5, 0x0},
                                   {7, 4, 0x0},
                                   {7, 7, 0x0},
                                   {7, 9, 0x0},
                                   {7, 11, 0x0},
                                   {10, 8, 0x0},
                                   {10, 14, 0x0},
                                   {10, 6, 0x0},
                                   {10, 5, 0x0},
                                   {10, 4, 0x0},
                                   {10, 7, 0x0},
                                   {10, 9, 0x0},
                                   {10, 11, 0x0},
                                   {12, 8, 0x0},
                                   {12, 14, 0x0},
                                   {12, 6, 0x0},
                                   {12, 5, 0x0},
                                   {12, 4, 0x0},
                                   {12, 7, 0x0},
                                   {12, 9, 0x0},
                                   {12, 11, 0x0},
};

fp_test_args_t xsmaddXdp_tests[] = {
                                   {8, 8, 0x0},
                                   {8, 14, 0x0},
                                   {8, 6, 0x0},
                                   {8, 5, 0x0},
                                   {8, 4, 0x0},
                                   {8, 7, 0x0},
                                   {8, 9, 0x0},
                                   {8, 11, 0x0},
                                   {14, 8, 0x0},
                                   {14, 14, 0x0},
                                   {14, 6, 0x0},
                                   {14, 5, 0x0},
                                   {14, 4, 0x0},
                                   {14, 7, 0x0},
                                   {14, 9, 0x0},
                                   {14, 11, 0x0},
                                   {6, 8, 0x0},
                                   {6, 14, 0x0},
                                   {6, 6, 0x0},
                                   {6, 5, 0x0},
                                   {6, 4, 0x0},
                                   {6, 7, 0x0},
                                   {6, 9, 0x0},
                                   {6, 11, 0x0},
                                   {5, 8, 0x0},
                                   {5, 14, 0x0},
                                   {5, 6, 0x0},
                                   {5, 5, 0x0},
                                   {5, 4, 0x0},
                                   {5, 7, 0x0},
                                   {5, 9, 0x0},
                                   {5, 11, 0x0},
                                   {4, 8, 0x0},
                                   {4, 14, 0x0},
                                   {4, 6, 0x0},
                                   {4, 5, 0x0},
                                   {4, 1, 0x0},
                                   {4, 7, 0x0},
                                   {4, 9, 0x0},
                                   {4, 11, 0x0},
                                   {7, 8, 0x0},
                                   {7, 14, 0x0},
                                   {7, 6, 0x0},
                                   {7, 5, 0x0},
                                   {7, 4, 0x0},
                                   {7, 7, 0x0},
                                   {7, 9, 0x0},
                                   {7, 11, 0x0},
                                   {10, 8, 0x0},
                                   {10, 14, 0x0},
                                   {10, 6, 0x0},
                                   {10, 5, 0x0},
                                   {10, 4, 0x0},
                                   {10, 7, 0x0},
                                   {10, 9, 0x0},
                                   {10, 11, 0x0},
                                   {12, 8, 0x0},
                                   {12, 14, 0x0},
                                   {12, 6, 0x0},
                                   {12, 5, 0x0},
                                   {12, 4, 0x0},
                                   {12, 7, 0x0},
                                   {12, 9, 0x0},
                                   {12, 11, 0x0},
};

fp_test_args_t xsmsubXdp_tests[] = {
                                   {8, 8, 0x0},
                                   {8, 14, 0x0},
                                   {8, 6, 0x0},
                                   {8, 5, 0x0},
                                   {8, 4, 0x0},
                                   {8, 7, 0x0},
                                   {8, 9, 0x0},
                                   {8, 11, 0x0},
                                   {14, 8, 0x0},
                                   {14, 14, 0x0},
                                   {14, 6, 0x0},
                                   {14, 5, 0x0},
                                   {14, 4, 0x0},
                                   {14, 7, 0x0},
                                   {14, 9, 0x0},
                                   {14, 11, 0x0},
                                   {6, 8, 0x0},
                                   {6, 14, 0x0},
                                   {6, 6, 0x0},
                                   {6, 5, 0x0},
                                   {6, 4, 0x0},
                                   {6, 7, 0x0},
                                   {6, 9, 0x0},
                                   {6, 11, 0x0},
                                   {5, 8, 0x0},
                                   {5, 14, 0x0},
                                   {5, 6, 0x0},
                                   {5, 5, 0x0},
                                   {5, 4, 0x0},
                                   {5, 7, 0x0},
                                   {5, 9, 0x0},
                                   {5, 11, 0x0},
                                   {4, 8, 0x0},
                                   {4, 14, 0x0},
                                   {4, 6, 0x0},
                                   {4, 5, 0x0},
                                   {4, 1, 0x0},
                                   {4, 7, 0x0},
                                   {4, 9, 0x0},
                                   {4, 11, 0x0},
                                   {7, 8, 0x0},
                                   {7, 14, 0x0},
                                   {7, 6, 0x0},
                                   {7, 5, 0x0},
                                   {7, 4, 0x0},
                                   {7, 7, 0x0},
                                   {7, 9, 0x0},
                                   {7, 11, 0x0},
                                   {10, 8, 0x0},
                                   {10, 14, 0x0},
                                   {10, 6, 0x0},
                                   {10, 5, 0x0},
                                   {10, 4, 0x0},
                                   {10, 7, 0x0},
                                   {10, 9, 0x0},
                                   {10, 11, 0x0},
                                   {12, 8, 0x0},
                                   {12, 14, 0x0},
                                   {12, 6, 0x0},
                                   {12, 5, 0x0},
                                   {12, 4, 0x0},
                                   {12, 7, 0x0},
                                   {12, 9, 0x0},
                                   {12, 11, 0x0},
};

fp_test_args_t xsnmaddXdp_tests[] = {
                                     {8, 8, 0x0},
                                     {8, 14, 0x0},
                                     {8, 6, 0x0},
                                     {8, 5, 0x0},
                                     {8, 4, 0x0},
                                     {8, 7, 0x0},
                                     {8, 9, 0x0},
                                     {8, 11, 0x0},
                                     {14, 8, 0x0},
                                     {14, 14, 0x0},
                                     {14, 6, 0x0},
                                     {14, 5, 0x0},
                                     {14, 4, 0x0},
                                     {14, 7, 0x0},
                                     {14, 9, 0x0},
                                     {14, 11, 0x0},
                                     {6, 8, 0x0},
                                     {6, 14, 0x0},
                                     {6, 6, 0x0},
                                     {6, 5, 0x0},
                                     {6, 4, 0x0},
                                     {6, 7, 0x0},
                                     {6, 9, 0x0},
                                     {6, 11, 0x0},
                                     {5, 8, 0x0},
                                     {5, 14, 0x0},
                                     {5, 6, 0x0},
                                     {5, 5, 0x0},
                                     {5, 4, 0x0},
                                     {5, 7, 0x0},
                                     {5, 9, 0x0},
                                     {5, 11, 0x0},
                                     {4, 8, 0x0},
                                     {4, 14, 0x0},
                                     {4, 6, 0x0},
                                     {4, 5, 0x0},
                                     {4, 1, 0x0},
                                     {4, 7, 0x0},
                                     {4, 9, 0x0},
                                     {4, 11, 0x0},
                                     {7, 8, 0x0},
                                     {7, 14, 0x0},
                                     {7, 6, 0x0},
                                     {7, 5, 0x0},
                                     {7, 4, 0x0},
                                     {7, 7, 0x0},
                                     {7, 9, 0x0},
                                     {7, 11, 0x0},
                                     {10, 8, 0x0},
                                     {10, 14, 0x0},
                                     {10, 6, 0x0},
                                     {10, 5, 0x0},
                                     {10, 4, 0x0},
                                     {10, 7, 0x0},
                                     {10, 9, 0x0},
                                     {10, 11, 0x0},
                                     {12, 8, 0x0},
                                     {12, 14, 0x0},
                                     {12, 6, 0x0},
                                     {12, 5, 0x0},
                                     {12, 4, 0x0},
                                     {12, 7, 0x0},
                                     {12, 9, 0x0},
                                     {12, 11, 0x0},
};

fp_test_args_t xsmuldp_tests[] = {
                                  {8, 8, 0x0},
                                  {8, 14, 0x0},
                                  {8, 6, 0x0},
                                  {8, 5, 0x0},
                                  {8, 4, 0x0},
                                  {8, 7, 0x0},
                                  {8, 9, 0x0},
                                  {8, 11, 0x0},
                                  {14, 8, 0x0},
                                  {14, 14, 0x0},
                                  {14, 6, 0x0},
                                  {14, 5, 0x0},
                                  {14, 4, 0x0},
                                  {14, 7, 0x0},
                                  {14, 9, 0x0},
                                  {14, 11, 0x0},
                                  {6, 8, 0x0},
                                  {6, 14, 0x0},
                                  {6, 6, 0x0},
                                  {6, 5, 0x0},
                                  {6, 4, 0x0},
                                  {6, 7, 0x0},
                                  {6, 9, 0x0},
                                  {6, 11, 0x0},
                                  {5, 8, 0x0},
                                  {5, 14, 0x0},
                                  {5, 6, 0x0},
                                  {5, 5, 0x0},
                                  {5, 4, 0x0},
                                  {5, 7, 0x0},
                                  {5, 9, 0x0},
                                  {5, 11, 0x0},
                                  {4, 8, 0x0},
                                  {4, 14, 0x0},
                                  {4, 6, 0x0},
                                  {4, 5, 0x0},
                                  {4, 1, 0x0},
                                  {4, 7, 0x0},
                                  {4, 9, 0x0},
                                  {4, 11, 0x0},
                                  {7, 8, 0x0},
                                  {7, 14, 0x0},
                                  {7, 6, 0x0},
                                  {7, 5, 0x0},
                                  {7, 4, 0x0},
                                  {7, 7, 0x0},
                                  {7, 9, 0x0},
                                  {7, 11, 0x0},
                                  {10, 8, 0x0},
                                  {10, 14, 0x0},
                                  {10, 6, 0x0},
                                  {10, 5, 0x0},
                                  {10, 4, 0x0},
                                  {10, 7, 0x0},
                                  {10, 9, 0x0},
                                  {10, 11, 0x0},
                                  {12, 8, 0x0},
                                  {12, 14, 0x0},
                                  {12, 6, 0x0},
                                  {12, 5, 0x0},
                                  {12, 4, 0x0},
                                  {12, 7, 0x0},
                                  {12, 9, 0x0},
                                  {12, 11, 0x0},
};

fp_test_args_t xssubdp_tests[] = {
                                  {8, 8, 0x0},
                                  {8, 14, 0x0},
                                  {8, 6, 0x0},
                                  {8, 5, 0x0},
                                  {8, 4, 0x0},
                                  {8, 7, 0x0},
                                  {8, 9, 0x0},
                                  {8, 11, 0x0},
                                  {14, 8, 0x0},
                                  {14, 14, 0x0},
                                  {14, 6, 0x0},
                                  {14, 5, 0x0},
                                  {14, 4, 0x0},
                                  {14, 7, 0x0},
                                  {14, 9, 0x0},
                                  {14, 11, 0x0},
                                  {6, 8, 0x0},
                                  {6, 14, 0x0},
                                  {6, 6, 0x0},
                                  {6, 5, 0x0},
                                  {6, 4, 0x0},
                                  {6, 7, 0x0},
                                  {6, 9, 0x0},
                                  {6, 11, 0x0},
                                  {5, 8, 0x0},
                                  {5, 14, 0x0},
                                  {5, 6, 0x0},
                                  {5, 5, 0x0},
                                  {5, 4, 0x0},
                                  {5, 7, 0x0},
                                  {5, 9, 0x0},
                                  {5, 11, 0x0},
                                  {4, 8, 0x0},
                                  {4, 14, 0x0},
                                  {4, 6, 0x0},
                                  {4, 5, 0x0},
                                  {4, 1, 0x0},
                                  {4, 7, 0x0},
                                  {4, 9, 0x0},
                                  {4, 11, 0x0},
                                  {7, 8, 0x0},
                                  {7, 14, 0x0},
                                  {7, 6, 0x0},
                                  {7, 5, 0x0},
                                  {7, 4, 0x0},
                                  {7, 7, 0x0},
                                  {7, 9, 0x0},
                                  {7, 11, 0x0},
                                  {10, 8, 0x0},
                                  {10, 14, 0x0},
                                  {10, 6, 0x0},
                                  {10, 5, 0x0},
                                  {10, 4, 0x0},
                                  {10, 7, 0x0},
                                  {10, 9, 0x0},
                                  {10, 11, 0x0},
                                  {12, 8, 0x0},
                                  {12, 14, 0x0},
                                  {12, 6, 0x0},
                                  {12, 5, 0x0},
                                  {12, 4, 0x0},
                                  {12, 7, 0x0},
                                  {12, 9, 0x0},
                                  {12, 11, 0x0},
};



static int nb_special_fargs;
static double * spec_fargs;

static void build_special_fargs_table(void)
{
   /* The special floating point values created below are for
    * use in the ftdiv tests for setting the fe_flag and fg_flag,
    * but they can also be used for other tests (e.g., xscmpudp).
    *
    * Note that fl_flag is 'always '1' on ppc64 Linux.
    *
  Entry  Sign Exp   fraction                  Special value
   0      0   3fd   0x8000000000000ULL         Positive finite number
   1      0   404   0xf000000000000ULL         ...
   2      0   001   0x8000000b77501ULL         ...
   3      0   7fe   0x800000000051bULL         ...
   4      0   012   0x3214569900000ULL         ...
   5      0   000   0x0000000000000ULL         +0.0 (+zero)
   6      1   000   0x0000000000000ULL         -0.0 (-zero)
   7      0   7ff   0x0000000000000ULL         +infinity
   8      1   7ff   0x0000000000000ULL         -infinity
   9      0   7ff   0x7FFFFFFFFFFFFULL         +SNaN
   10     1   7ff   0x7FFFFFFFFFFFFULL         -SNaN
   11     0   7ff   0x8000000000000ULL         +QNaN
   12     1   7ff   0x8000000000000ULL         -QNaN
   13     1   000   0x8340000078000ULL         Denormalized val (zero exp and non-zero fraction)
   14     1   40d   0x0650f5a07b353ULL         Negative finite number
    */

   uint64_t mant;
   uint16_t _exp;
   int s;
   int i = 0;

   if (spec_fargs)
      return;

   spec_fargs = malloc( 16 * sizeof(double) );

   // #0
   s = 0;
   _exp = 0x3fd;
   mant = 0x8000000000000ULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   // #1
   s = 0;
   _exp = 0x404;
   mant = 0xf000000000000ULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   /* None of the ftdiv tests succeed.
    * FRA = value #0; FRB = value #1
    * ea_ = -2; e_b = 5
    * fl_flag || fg_flag || fe_flag = 100
    */

   /*************************************************
    *     fe_flag tests
    *
    *************************************************/

   /* fe_flag <- 1 if FRA is a NaN
    * FRA = value #9; FRB = value #1
    * e_a = 1024; e_b = 5
    * fl_flag || fg_flag || fe_flag = 101
    */

   /* fe_flag <- 1 if FRB is a NaN
    * FRA = value #1; FRB = value #12
    * e_a = 5; e_b = 1024
    * fl_flag || fg_flag || fe_flag = 101
    */

   /* fe_flag <- 1 if e_b <= -1022
    * FRA = value #0; FRB = value #2
    * e_a = -2; e_b = -1022
    * fl_flag || fg_flag || fe_flag = 101
    *
    */
   // #2
   s = 0;
   _exp = 0x001;
   mant = 0x8000000b77501ULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   /* fe_flag <- 1 if e_b >= 1021
    * FRA = value #1; FRB = value #3
    * e_a = 5; e_b = 1023
    * fl_flag || fg_flag || fe_flag = 101
    */
   // #3
   s = 0;
   _exp = 0x7fe;
   mant = 0x800000000051bULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   /* fe_flag <- 1 if FRA != 0 && e_a - e_b >= 1023
    * Let FRA = value #3 and FRB be value #0.
    * e_a = 1023; e_b = -2
    * fl_flag || fg_flag || fe_flag = 101
    */

   /* fe_flag <- 1 if FRA != 0 && e_a - e_b <= -1023
    * Let FRA = value #0 above and FRB be value #3 above
    * e_a = -2; e_b = 1023
    * fl_flag || fg_flag || fe_flag = 101
    */

   /* fe_flag <- 1 if FRA != 0 && e_a <= -970
    * Let FRA = value #4 and FRB be value #0
    * e_a = -1005; e_b = -2
    * fl_flag || fg_flag || fe_flag = 101
   */
   // #4
   s = 0;
   _exp = 0x012;
   mant = 0x3214569900000ULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   /*************************************************
    *     fg_flag tests
    *
    *************************************************/
   /* fg_flag <- 1 if FRA is an Infinity
    * NOTE: FRA = Inf also sets fe_flag
    * Do two tests, using values #7 and #8 (+/- Inf) for FRA.
    * Test 1:
    *   Let FRA be value #7 and FRB be value #1
    *   e_a = 1024; e_b = 5
    *   fl_flag || fg_flag || fe_flag = 111
    *
    * Test 2:
    *   Let FRA be value #8 and FRB be value #1
    *   e_a = 1024; e_b = 5
    *   fl_flag || fg_flag || fe_flag = 111
    *
    */

   /* fg_flag <- 1 if FRB is an Infinity
    * NOTE: FRB = Inf also sets fe_flag
    * Let FRA be value #1 and FRB be value #7
    * e_a = 5; e_b = 1024
    * fl_flag || fg_flag || fe_flag = 111
    */

   /* fg_flag <- 1 if FRB is denormalized
    * NOTE: e_b < -1022 ==> fe_flag <- 1
    * Let FRA be value #0 and FRB be value #13
    * e_a = -2; e_b = -1023
    * fl_flag || fg_flag || fe_flag = 111
    */

   /* fg_flag <- 1 if FRB is +zero
    * NOTE: FRA = Inf also sets fe_flag
    * Let FRA = val #5; FRB = val #5
    * ea_ = -1023; e_b = -1023
    * fl_flag || fg_flag || fe_flag = 111
    */

   /* fg_flag <- 1 if FRB is -zero
    * NOTE: FRA = Inf also sets fe_flag
    * Let FRA = val #5; FRB = val #6
    * ea_ = -1023; e_b = -1023
    * fl_flag || fg_flag || fe_flag = 111
    */

   /* Special values */
   /* +0.0      : 0 0x000 0x0000000000000 */
   // #5
   s = 0;
   _exp = 0x000;
   mant = 0x0000000000000ULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   /* -0.0      : 1 0x000 0x0000000000000 */
   // #6
   s = 1;
   _exp = 0x000;
   mant = 0x0000000000000ULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   /* +infinity : 0 0x7FF 0x0000000000000  */
   // #7
   s = 0;
   _exp = 0x7FF;
   mant = 0x0000000000000ULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   /* -infinity : 1 0x7FF 0x0000000000000 */
   // #8
   s = 1;
   _exp = 0x7FF;
   mant = 0x0000000000000ULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   /* +SNaN     : 0 0x7FF 0x7FFFFFFFFFFFF */
   // #9
   s = 0;
   _exp = 0x7FF;
   mant = 0x7FFFFFFFFFFFFULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   /* -SNaN     : 1 0x7FF 0x7FFFFFFFFFFFF */
   // #10
   s = 1;
   _exp = 0x7FF;
   mant = 0x7FFFFFFFFFFFFULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   /* +QNaN     : 0 0x7FF 0x8000000000000 */
   // #11
   s = 0;
   _exp = 0x7FF;
   mant = 0x8000000000000ULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   /* -QNaN     : 1 0x7FF 0x8000000000000 */
   // #12
   s = 1;
   _exp = 0x7FF;
   mant = 0x8000000000000ULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   /* denormalized value */
   // #13
   s = 1;
   _exp = 0x000;
   mant = 0x8340000078000ULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   /* Negative finite number */
   // #14
   s = 1;
   _exp = 0x40d;
   mant = 0x0650f5a07b353ULL;
   register_farg(&spec_fargs[i++], s, _exp, mant);

   nb_special_fargs = i;
}


struct test_table
{
   test_func_t test_category;
   char * name;
};

struct p7_fp_test
{
   test_func_t test_func;
   const char *name;
   int single;  // 1=single precision result; 0=double precision result
};

typedef enum {
   VX_FP_CMP,
   VX_FP_SMA,
   VX_FP_SMS,
   VX_FP_SNMA,
   VX_FP_OTHER
} vx_fp_test_type;

struct vx_fp_test
{
   test_func_t test_func;
   const char *name;
   fp_test_args_t * targs;
   int num_tests;
   vx_fp_test_type test_type;
};

struct xs_conv_test
{
   test_func_t test_func;
   const char *name;
   int num_tests;
};

typedef enum {
   VSX_LOAD =1,
   VSX_LOAD_SPLAT,
   VSX_STORE
} vsx_ldst_type;

struct ldst_test
{
   test_func_t test_func;
   const char *name;
   void * base_addr;
   uint32_t offset;
   int num_words_to_process;
   vsx_ldst_type type;
};

typedef enum {
   VSX_AND = 1,
   VSX_XOR,
   VSX_ANDC,
   VSX_OR,
   VSX_NOR
} vsx_log_op;

struct vsx_logic_test
{
   test_func_t test_func;
   const char *name;
   vsx_log_op op;
};

struct vsx_move_test
{
   test_func_t test_func;
   const char *name;
};

struct vsx_permute_test
{
   test_func_t test_func;
   const char *name;
   unsigned int xa[4];
   unsigned int xb[4];
};

static vector unsigned int vec_out, vec_inA, vec_inB;

static void test_lxsdx(void)
{
   __asm__ __volatile__ ("lxsdx          %x0, %1, %2" : "=wa" (vec_out): "b" (r14),"r" (r15));
}

static void
test_lxvd2x(void)
{
   __asm__ __volatile__ ("lxvd2x          %x0, %1, %2" : "=wa" (vec_out): "b" (r14),"r" (r15));
}

static void test_lxvdsx(void)
{
   __asm__ __volatile__ ("lxvdsx          %x0, %1, %2" : "=wa" (vec_out): "b" (r14),"r" (r15));
}

static void test_lxvw4x(void)
{
   __asm__ __volatile__ ("lxvw4x          %x0, %1, %2" : "=wa" (vec_out): "b" (r14),"r" (r15));
}

static void test_stxsdx(void)
{
   __asm__ __volatile__ ("stxsdx          %x0, %1, %2" : : "wa" (vec_inA), "b" (r14),"r" (r15));
}

static void test_stxvd2x(void)
{
   __asm__ __volatile__ ("stxvd2x          %x0, %1, %2" : : "wa" (vec_inA), "b" (r14),"r" (r15));
}

static void test_stxvw4x(void)
{
   __asm__ __volatile__ ("stxvw4x          %x0, %1, %2" : : "wa" (vec_inA), "b" (r14),"r" (r15));
}

static void test_xxlxor(void)
{
   __asm__ __volatile__ ("xxlxor          %x0, %x1, %x2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxlor(void)
{
   __asm__ __volatile__ ("xxlor          %x0, %x1, %x2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxlnor(void)
{
   __asm__ __volatile__ ("xxlnor          %x0, %x1, %x2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxland(void)
{
   __asm__ __volatile__ ("xxland          %x0, %x1, %x2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxlandc(void)
{
   __asm__ __volatile__ ("xxlandc          %x0, %x1, %x2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxmrghw(void)
{
   __asm__ __volatile__ ("xxmrghw          %x0, %x1, %x2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxmrglw(void)
{
   __asm__ __volatile__ ("xxmrglw          %x0, %x1, %x2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxpermdi_00(void)
{
   __asm__ __volatile__ ("xxpermdi         %x0, %x1, %x2, 0x0" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxpermdi_01(void)
{
   __asm__ __volatile__ ("xxpermdi         %x0, %x1, %x2, 0x1" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxpermdi_10(void)
{
   __asm__ __volatile__ ("xxpermdi         %x0, %x1, %x2, 0x2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxpermdi_11(void)
{
   __asm__ __volatile__ ("xxpermdi         %x0, %x1, %x2, 0x3" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxsldwi_0(void)
{
   __asm__ __volatile__ ("xxsldwi         %x0, %x1, %x2, 0" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxsldwi_1(void)
{
   __asm__ __volatile__ ("xxsldwi         %x0, %x1, %x2, 1" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxsldwi_2(void)
{
   __asm__ __volatile__ ("xxsldwi         %x0, %x1, %x2, 2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xxsldwi_3(void)
{
   __asm__ __volatile__ ("xxsldwi         %x0, %x1, %x2, 3" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_fcfids (void)
{
    __asm__ __volatile__ ("fcfids          %0, %1" : "=f" (f17): "d" (f14));
}

static void test_fcfidus (void)
{
    __asm__ __volatile__ ("fcfidus          %0, %1" : "=f" (f17): "d" (f14));
}

static void test_fcfidu (void)
{
    __asm__ __volatile__ ("fcfidu          %0, %1" : "=f" (f17): "d" (f14));
}

static void test_xsabsdp (void)
{
   __asm__ __volatile__ ("xsabsdp          %x0, %x1" : "=wa" (vec_out): "wa" (vec_inB));
}

static void test_xscpsgndp (void)
{
   __asm__ __volatile__ ("xscpsgndp          %x0, %x1, %x2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xsnabsdp (void)
{
   __asm__ __volatile__ ("xsnabsdp          %x0, %x1" : "=wa" (vec_out): "wa" (vec_inB));
}

static void test_xsnegdp (void)
{
   __asm__ __volatile__ ("xsnegdp          %x0, %x1" : "=wa" (vec_out): "wa" (vec_inB));
}

static int do_cmpudp;
static void test_xscmp (void)
{
   if (do_cmpudp)
      __asm__ __volatile__ ("xscmpudp          cr1, %x0, %x1" : : "wa" (vec_inA),"wa" (vec_inB));
   else
      __asm__ __volatile__ ("xscmpodp          cr1, %x0, %x1" : : "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xsadddp(void)
{
   __asm__ __volatile__ ("xsadddp          %x0, %x1, %x2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xsdivdp(void)
{
   __asm__ __volatile__ ("xsdivdp          %x0, %x1, %x2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static int do_adp;
static void test_xsmadd(void)
{
   if (do_adp)
      __asm__ __volatile__ ("xsmaddadp          %x0, %x1, %x2" : "+wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
   else
      __asm__ __volatile__ ("xsmaddmdp          %x0, %x1, %x2" : "+wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xsmsub(void)
{
   if (do_adp)
      __asm__ __volatile__ ("xsmsubadp          %x0, %x1, %x2" : "+wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
   else
      __asm__ __volatile__ ("xsmsubmdp          %x0, %x1, %x2" : "+wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xsnmadd(void)
{
   if (do_adp)
      __asm__ __volatile__ ("xsnmaddadp          %x0, %x1, %x2" : "+wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
   else
      __asm__ __volatile__ ("xsnmaddmdp          %x0, %x1, %x2" : "+wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xsmuldp(void)
{
   __asm__ __volatile__ ("xsmuldp          %x0, %x1, %x2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xssubdp(void)
{
   __asm__ __volatile__ ("xssubdp          %x0, %x1, %x2" : "=wa" (vec_out): "wa" (vec_inA),"wa" (vec_inB));
}

static void test_xscvdpsxds (void)
{
   __asm__ __volatile__ ("xscvdpsxds          %x0, %x1" : "=wa" (vec_out): "wa" (vec_inB));
}

static void test_xscvsxddp (void)
{
   __asm__ __volatile__ ("xscvsxddp          %x0, %x1" : "=wa" (vec_out): "wa" (vec_inB));
}

static void test_xscvuxddp (void)
{
   __asm__ __volatile__ ("xscvuxddp          %x0, %x1" : "=wa" (vec_out): "wa" (vec_inB));
}

static unsigned int vstg[] __attribute__ ((aligned (16))) = { 0, 0, 0,0,
                                                              0, 0, 0, 0 };

#define NUM_VSTG_INTS (sizeof vstg/sizeof vstg[0])
#define NUM_VSTG_VECS (NUM_VSTG_INTS/4)

static unsigned int viargs[] __attribute__ ((aligned (16))) = { 0x01234567,
                                                                0x89abcdef,
                                                                0x00112233,
                                                                0x44556677,
                                                                0x8899aabb,
                                                                0x91929394,
                                                                0xa1a2a3a4,
                                                                0xb1b2b3b4,
                                                                0xc1c2c3c4,
                                                                0xd1d2d3d4,
                                                                0x7a6b5d3e
};
#define NUM_VIARGS_INTS (sizeof viargs/sizeof viargs[0])
#define NUM_VIARGS_VECS  (NUM_VIARGS_INTS/4)

static ldst_test_t ldst_tests[] = { { &test_lxsdx, "lxsdx", viargs, 0, 2, VSX_LOAD },
                                     { &test_lxsdx, "lxsdx", viargs, 4, 2, VSX_LOAD },
                                     { &test_lxvd2x, "lxvd2x", viargs, 0, 4, VSX_LOAD },
                                     { &test_lxvd2x, "lxvd2x", viargs, 4, 4, VSX_LOAD },
                                     { &test_lxvdsx, "lxvdsx", viargs, 0, 4, VSX_LOAD_SPLAT },
                                     { &test_lxvdsx, "lxvdsx", viargs, 4, 4, VSX_LOAD_SPLAT },
                                     { &test_lxvw4x, "lxvw4x", viargs, 0, 4, VSX_LOAD },
                                     { &test_lxvw4x, "lxvw4x", viargs, 4, 4, VSX_LOAD },
                                     { &test_stxsdx, "stxsdx", vstg, 0, 2, VSX_STORE },
                                     { &test_stxsdx, "stxsdx", vstg, 4, 2, VSX_STORE },
                                     { &test_stxvd2x, "stxvd2x", vstg, 0, 4, VSX_STORE },
                                     { &test_stxvd2x, "stxvd2x", vstg, 4, 4, VSX_STORE },
                                     { &test_stxvw4x, "stxvw4x", vstg, 0, 4, VSX_STORE },
                                     { &test_stxvw4x, "stxvw4x", vstg, 4, 4, VSX_STORE },
                                     { NULL, NULL, NULL, 0, 0, 0 } };

static logic_test_t logic_tests[] = { { &test_xxlxor, "xxlxor", VSX_XOR },
                                      { &test_xxlor, "xxlor", VSX_OR } ,
                                      { &test_xxlnor, "xxlnor", VSX_NOR },
                                      { &test_xxland, "xxland", VSX_AND },
                                      { &test_xxlandc, "xxlandc", VSX_ANDC },
                                      { NULL, NULL, 0}};

static move_test_t move_tests[] = { { &test_xsabsdp, "xsabsdp" },
                                    { &test_xscpsgndp, "xscpsgndp" },
                                    { &test_xsnabsdp, "xsnabsdp" },
                                    { &test_xsnegdp, "xsnegdp" },
                                    { NULL, NULL }

};

static permute_test_t permute_tests[] =
{
  { &test_xxmrghw, "xxmrghw", 
    { 0x11111111, 0x22222222, 0x33333333, 0x44444444 }, /* XA input */
    { 0x55555555, 0x66666666, 0x77777777, 0x88888888 }, /* XB input */
  },
  { &test_xxmrghw, "xxmrghw", 
    { 0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff }, /* XA input */
    { 0x11111111, 0x22222222, 0x33333333, 0x44444444 }, /* XB input */
  },
  { &test_xxmrglw, "xxmrglw", 
    { 0x11111111, 0x22222222, 0x33333333, 0x44444444 }, /* XA input */
    { 0x55555555, 0x66666666, 0x77777777, 0x88888888 }, /* XB input */
  },
  { &test_xxmrglw, "xxmrglw", 
    { 0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff}, /* XA input */
    { 0x11111111, 0x22222222, 0x33333333, 0x44444444}, /* XB input */
  },
  { &test_xxpermdi_00, "xxpermdi DM=00", 
    { 0x11111111, 0x22222222, 0x33333333, 0x44444444 }, /* XA input */
    { 0x55555555, 0x66666666, 0x77777777, 0x88888888 }, /* XB input */
  },
  { &test_xxpermdi_01, "xxpermdi DM=01", 
    { 0x11111111, 0x22222222, 0x33333333, 0x44444444 }, /* XA input */
    { 0x55555555, 0x66666666, 0x77777777, 0x88888888 }, /* XB input */
  },
  { &test_xxpermdi_10, "xxpermdi DM=10", 
    { 0x11111111, 0x22222222, 0x33333333, 0x44444444 }, /* XA input */
    { 0x55555555, 0x66666666, 0x77777777, 0x88888888 }, /* XB input */
  },
  { &test_xxpermdi_11, "xxpermdi DM=11", 
    { 0x11111111, 0x22222222, 0x33333333, 0x44444444 }, /* XA input */
    { 0x55555555, 0x66666666, 0x77777777, 0x88888888 }, /* XB input */
  },
  { &test_xxsldwi_0, "xxsldwi SHW=0", 
    { 0x11111111, 0x22222222, 0x33333333, 0x44444444 }, /* XA input */
    { 0x55555555, 0x66666666, 0x77777777, 0x88888888 }, /* XB input */
  },
  { &test_xxsldwi_1, "xxsldwi SHW=1", 
    { 0x11111111, 0x22222222, 0x33333333, 0x44444444 }, /* XA input */
    { 0x55555555, 0x66666666, 0x77777777, 0x88888888 }, /* XB input */
  },
  { &test_xxsldwi_2, "xxsldwi SHW=2", 
    { 0x11111111, 0x22222222, 0x33333333, 0x44444444 }, /* XA input */
    { 0x55555555, 0x66666666, 0x77777777, 0x88888888 }, /* XB input */
  },
  { &test_xxsldwi_3, "xxsldwi SHW=3", 
    { 0x11111111, 0x22222222, 0x33333333, 0x44444444 }, /* XA input */
    { 0x55555555, 0x66666666, 0x77777777, 0x88888888 }, /* XB input */
  },
  { NULL, NULL }
};

static fp_test_t fp_tests[] = { { &test_fcfids, "fcfids", 1 },
                                { &test_fcfidus, "fcfidus", 1 },
                                { &test_fcfidu, "fcfidu", 1 },
                                { NULL, NULL, 0 },

};

static vx_fp_test_t vx_fp_tests[] = {
                                     { &test_xscmp, "xscmp", xscmpX_tests, 64, VX_FP_CMP},
                                     { &test_xsadddp, "xsadddp", xsadddp_tests, 64, VX_FP_OTHER},
                                     { &test_xsdivdp, "xsdivdp", xsdivdp_tests, 64, VX_FP_OTHER},
                                     { &test_xsmadd, "xsmadd", xsmaddXdp_tests, 64, VX_FP_SMA},
                                     { &test_xsmsub, "xsmsub", xsmsubXdp_tests, 64, VX_FP_SMS},
                                     { &test_xsnmadd, "xsnmadd", xsnmaddXdp_tests, 64, VX_FP_SNMA},
                                     { & test_xsmuldp, "xsmuldp", xsmuldp_tests, 64, VX_FP_OTHER},
                                     { & test_xssubdp, "xssubdp", xssubdp_tests, 64, VX_FP_OTHER},
                                     { NULL, NULL, NULL, 0, 0 }
};

static xs_conv_test_t xs_conv_tests[] = {
                                         { &test_xscvdpsxds, "xscvdpsxds", 15},
                                         { &test_xscvsxddp, "xscvsxddp", 15},
                                         { &test_xscvuxddp, "xscvuxddp", 15},
                                         { NULL, NULL, 0}
};

#ifdef __powerpc64__
static void test_ldbrx(void)
{
   int i;
   HWord_t reg_out;
   unsigned char * byteIn, * byteOut;
   r14 = (HWord_t)viargs;
   // Just try the instruction an arbitrary number of times at different r15 offsets.
   for (i = 0; i < 3; i++) {
      int j, k;
      reg_out = 0;
      r15 = i * 4;
      __asm__ __volatile__ ("ldbrx          %0, %1, %2" : "=r" (reg_out): "b" (r14),"r" (r15));
      byteIn = ((unsigned char *)(r14 + r15));
      byteOut = (unsigned char *)&reg_out;

      printf("ldbrx:");
      for (k = 0; k < 8; k++) {
         printf( " %02x", (byteIn[k]));
      }
      printf(" (reverse) =>");
      for (j = 0; j < 8; j++) {
         printf( " %02x", (byteOut[j]));
      }
      printf("\n");
   }
   printf( "\n" );
}

static void
test_popcntd(void)
{
   uint64_t res;
   unsigned long long src = 0x9182736405504536ULL;
   r14 = src;
   __asm__ __volatile__ ("popcntd          %0, %1" : "=r" (res): "r" (r14));
   printf("popcntd: 0x%llx => %d\n", src, (int)res);
   printf( "\n" );
}
#endif

static void
test_lfiwzx(void)
{
   unsigned int i;
   unsigned int * src;
   uint64_t reg_out;
   r14 = (HWord_t)viargs;
   // Just try the instruction an arbitrary number of times at different r15 offsets.
   for (i = 0; i < 3; i++) {
      reg_out = 0;
      r15 = i * 4;
      __asm__ __volatile__ ("lfiwzx          %0, %1, %2" : "=d" (reg_out): "b" (r14),"r" (r15));
      src = ((unsigned int *)(r14 + r15));
      printf("lfiwzx: %u => %llu.00\n", *src, (unsigned long long)reg_out);

   }
   printf( "\n" );
}

static void test_vx_fp_ops(void)
{

   test_func_t func;
   int k;
   char * test_name = (char *)malloc(20);
   k = 0;

   build_special_fargs_table();
   while ((func = vx_fp_tests[k].test_func)) {
      int i, condreg, repeat = 0;
      unsigned int flags;
      unsigned long long * frap, * frbp, * dst;
      vx_fp_test_t test_group = vx_fp_tests[k];
      vx_fp_test_type test_type = test_group.test_type;

      switch (test_type) {
         case VX_FP_CMP:
            strcpy(test_name, "xscmp");
            if (!repeat) {
               repeat = 1;
               strcat(test_name, "udp");
               do_cmpudp = 1;
            }
            break;
         case VX_FP_SMA:
         case VX_FP_SMS:
         case VX_FP_SNMA:
            if (test_type == VX_FP_SMA)
               strcpy(test_name, "xsmadd");
            else if (test_type == VX_FP_SMS)
               strcpy(test_name, "xsmsub");
            else
               strcpy(test_name, "xsnmadd");
            if (!repeat) {
               repeat = 1;
               strcat(test_name, "adp");
               do_adp = 1;
            }
            break;
         case VX_FP_OTHER:
            strcpy(test_name, test_group.name);
            break;
         default:
            printf("ERROR:  Invalid VX FP test type %d\n", test_type);
            exit(1);
      }

again:
      for (i = 0; i < test_group.num_tests; i++) {
         unsigned int * inA, * inB, * pv;
         double * dpA = (double *)&vec_inA;
         double * dpB = (double *)&vec_inB;
         double * dpT = (double *)&vec_out;

         fp_test_args_t aTest = test_group.targs[i];
         inA = (unsigned int *)&spec_fargs[aTest.fra_idx];
         inB = (unsigned int *)&spec_fargs[aTest.frb_idx];
         frap = (unsigned long long *)&spec_fargs[aTest.fra_idx];
         frbp = (unsigned long long *)&spec_fargs[aTest.frb_idx];
         // Only need to copy one doubleword into each vector's element 0
         if (isLE) {
            // With LE, vector element 0 is the second doubleword from the left
            memset(dpA, 0, 8);
            memset(dpB, 0, 8);
            dpA++;
            dpB++;
         }
         memcpy(dpA, inA, 8);
         memcpy(dpB, inB, 8);

         switch (test_type) {
            case VX_FP_CMP:
               SET_FPSCR_ZERO;
               SET_CR_XER_ZERO;
               (*func)();
               GET_CR(flags);
               condreg = (flags & 0x0f000000) >> 24;
               printf("#%d: %s %016llx <=> %016llx ? %x (CRx)\n", i, test_name, *frap, *frbp, condreg);
              // printf("\tFRA: %e;  FRB: %e\n", spec_fargs[aTest.fra_idx], spec_fargs[aTest.frb_idx]);
               if ( condreg != aTest.cr_flags) {
                  printf("Error: Expected CR flags 0x%x; actual flags: 0x%x\n", aTest.cr_flags, condreg);
               }
               break;
            case VX_FP_SMA:
            case VX_FP_SMS:
            case VX_FP_SNMA:
            case VX_FP_OTHER:
            {
               int idx;
               unsigned long long vsr_XT;
               pv = (unsigned int *)&vec_out;
               // clear vec_out
               for (idx = 0; idx < 4; idx++, pv++)
                  *pv = 0;

               if (test_type != VX_FP_OTHER) {
                  /* Then we need a third src argument, which is stored in element 0 of
                   * VSX[XT] -- i.e., vec_out.  For the xs<ZZZ>mdp cases, VSX[XT] holds
                   * src3 and VSX[XB] holds src2; for the xs<ZZZ>adp cases, VSX[XT] holds
                   * src2 and VSX[XB] holds src3.  The fp_test_args_t that holds the test
                   * data (input args) contain only two inputs, so I arbitrarily
                   * use spec_fargs elements 4 and 14 (alternating) for the third source
                   * argument.  We can use the same input data for a given pair of
                   * adp/mdp-type instructions by swapping the src2 and src3 arguments; thus
                   * the expected result should be the same.
                   */
                  int extra_arg_idx;
                  if (i % 2)
                     extra_arg_idx = 4;
                  else
                     extra_arg_idx = 14;

                  if (repeat) {
                     /* We're on the first time through of one of the VX_FP_SMx
                      * test types, meaning we're testing a xs<ZZZ>adp case, thus we
                      * have to swap inputs as described above:
                      *    src2 <= VSX[XT]
                      *    src3 <= VSX[XB]
                      */
                     if (isLE)
                        dpT++;
                     memcpy(dpT, inB, 8);  // src2
                     memcpy(dpB, &spec_fargs[extra_arg_idx], 8);  //src3
                     frbp = (unsigned long long *)&spec_fargs[extra_arg_idx];
                  } else {
                     // Don't need to init src2, as it's done before the switch()
                     if (isLE)
                        dpT++;
                     memcpy(dpT, &spec_fargs[extra_arg_idx], 8);  //src3
                  }
                  memcpy(&vsr_XT, dpT, 8);
               }

               (*func)();
               dst = (unsigned long long *) &vec_out;
               if (isLE)
                  dst++;
               if (test_type == VX_FP_OTHER)
                  printf("#%d: %s %016llx %016llx = %016llx\n", i, test_name, *frap, *frbp, *dst);
               else
                  printf( "#%d: %s %016llx %016llx %016llx = %016llx\n", i,
                          test_name, vsr_XT, *frap, *frbp, *dst );

               /*
              {
                  // Debug code.  Keep this block commented out except when debugging.
                  double result, expected;
                  memcpy(&result, dst, 8);
                  memcpy(&expected, &aTest.dp_bin_result, 8);
                  printf( "\tFRA + FRB: %e + %e: Expected = %e; Actual = %e\n",
                          spec_fargs[aTest.fra_idx], spec_fargs[aTest.frb_idx],
                          expected, result );
               }
              */
               break;
            }
         }


      }
      printf( "\n" );

      if (repeat) {
         repeat = 0;
         switch (test_type) {
            case VX_FP_CMP:
               strcpy(test_name, "xscmp");
               strcat(test_name, "odp");
               do_cmpudp = 0;
               break;
            case VX_FP_SMA:
            case VX_FP_SMS:
            case VX_FP_SNMA:
               if (test_type == VX_FP_SMA)
                  strcpy(test_name, "xsmadd");
               else if (test_type == VX_FP_SMS)
                  strcpy(test_name, "xsmsub");
               else
                  strcpy(test_name, "xsnmadd");
               strcat(test_name, "mdp");
               do_adp = 0;
               break;
            case VX_FP_OTHER:
               break;
         }
         goto again;
      }
      k++;
   }
   printf( "\n" );
   free(test_name);
}

static void test_xs_conv_ops(void)
{

   test_func_t func;
   int k = 0;
   double * dpB = (double *)&vec_inB;
   if (isLE) {
      memset(dpB, 0, 8);
      dpB++;
   }

   build_special_fargs_table();
   while ((func = xs_conv_tests[k].test_func)) {
      int i;
      unsigned long long * frbp, * dst;
      xs_conv_test_t test_group = xs_conv_tests[k];
      for (i = 0; i < test_group.num_tests; i++) {
         unsigned int * inB, * pv;
         int idx;
         inB = (unsigned int *)&spec_fargs[i];
         frbp = (unsigned long long *)&spec_fargs[i];

         memcpy(dpB, inB, 8);
         pv = (unsigned int *)&vec_out;
         // clear vec_out
         for (idx = 0; idx < 4; idx++, pv++)
            *pv = 0;
         (*func)();
         dst = (unsigned long long *) &vec_out;
         if (isLE)
            dst++;
         printf("#%d: %s %016llx => %016llx\n", i, test_group.name, *frbp, *dst);

      }
      k++;
      printf("\n");
   }
   printf( "\n" );
}

static void do_load_test(ldst_test_t loadTest)
{
   test_func_t func;
   unsigned int *src, *dst;
   int splat = loadTest.type == VSX_LOAD_SPLAT ? 1: 0;
   int i, j, m, k;
   i = j = 0;

   func = loadTest.test_func;
   for (i = 0, r14 = (HWord_t) loadTest.base_addr; i < NUM_VIARGS_VECS; i++) {
      int again;
      j = 0;
       r14 += i * 16;
      do {
         unsigned int * pv = (unsigned int *)&vec_out;
         int idx;
         // clear vec_out
         for (idx = 0; idx < 4; idx++, pv+=idx)
            *pv = 0;

         again = 0;
         r15 = j;

         // execute test insn
         (*func)();

         src = (unsigned int*) (((unsigned char *)r14) + j);
         dst = (unsigned int*) &vec_out;

         printf( "%s:", loadTest.name);
         for (m = 0; m < loadTest.num_words_to_process; m++) {
            printf( " %08x", src[splat ? m % 2 : m]);
         }
         printf( " =>");
         m = 0;
         k = loadTest.num_words_to_process;
         if (isLE) {
            if (loadTest.num_words_to_process == 2) {
               m = 2;
               k += 2;
            }
         }

         for (; m < k; m++) {
            printf( " %08x", dst[m]);
         }
         printf("\n");
         if (j == 0 && loadTest.offset) {
            again = 1;
            j += loadTest.offset;
         }
      }
      while (again);
   }
}

static void
do_store_test ( ldst_test_t storeTest )
{
   test_func_t func;
   unsigned int *src, *dst;
   int m;

   func = storeTest.test_func;
   r14 = (HWord_t) storeTest.base_addr;
   r15 = (HWord_t) storeTest.offset;
   unsigned int * pv = (unsigned int *) storeTest.base_addr;
   int idx;
   // clear out storage destination
   for (idx = 0; idx < 4; idx++, pv += idx)
      *pv = 0;

   memcpy(&vec_inA, &viargs[0], sizeof(vector unsigned char));

   // execute test insn
   (*func)();
   src = &viargs[0];
   dst = (unsigned int*) (((unsigned char *) r14) + storeTest.offset);

   printf( "%s:", storeTest.name );
   for (m = 0; m < storeTest.num_words_to_process; m++) {
      printf( " %08x", src[m] );
   }
   printf( " =>" );
   for (m = 0; m < storeTest.num_words_to_process; m++) {
      printf( " %08x", dst[m] );
   }
   printf( "\n" );
}


static void test_ldst(void)
{
   int k = 0;

   while (ldst_tests[k].test_func) {
      if (ldst_tests[k].type == VSX_STORE)
         do_store_test(ldst_tests[k]);
      else
         do_load_test(ldst_tests[k]);
      k++;
      printf("\n");
   }
}

static void test_ftdiv(void)
{
   int i, num_tests, crx;
   unsigned int flags;
   unsigned long long * frap, * frbp;
   build_special_fargs_table();

   num_tests = sizeof ftdiv_tests/sizeof ftdiv_tests[0];

   for (i = 0; i < num_tests; i++) {
      fp_test_args_t aTest = ftdiv_tests[i];
      f14 = spec_fargs[aTest.fra_idx];
      f15 = spec_fargs[aTest.frb_idx];
      frap = (unsigned long long *)&spec_fargs[aTest.fra_idx];
      frbp = (unsigned long long *)&spec_fargs[aTest.frb_idx];
      SET_FPSCR_ZERO;
      SET_CR_XER_ZERO;
      __asm__ __volatile__ ("ftdiv           cr1, %0, %1" : : "d" (f14), "d" (f15));
      GET_CR(flags);
      crx = (flags & 0x0f000000) >> 24;
      printf( "ftdiv: %016llx <=> %016llx ? %x (CRx)\n", *frap, *frbp, crx);
//      printf("\tFRA: %e;  FRB: %e\n", f14, f15);
      if ( crx != aTest.cr_flags) {
         printf("Error: Expected CR flags 0x%x; actual flags: 0x%x\n", aTest.cr_flags, crx);
      }
   }
   printf( "\n" );
}


static void test_p7_fpops ( void )
{
   int k = 0;
   test_func_t func;

   build_fargs_table();
   while ((func = fp_tests[k].test_func)) {
      float res;
      double resd;
      unsigned long long u0;
      int i;
      //  fcfids  - 64-bit fp converted to inf precise fp integer, rounded to SP. (32)
      //  fcfidus - 64-bit fp converted to inf precise fp integer, rounded to SP. (32)
      //  fcfidu  - 64-bit fp converted to inf precise fp integer, rounded to DP. (64)
      int res32 = (
                   (strcmp(fp_tests[k].name, "fcfids")==0) ||
                   (strcmp(fp_tests[k].name, "fcfidus")==0)    );

      for (i = 0; i < nb_fargs; i++) {
         u0 = *(unsigned long long *) (&fargs[i]);
         f14 = fargs[i];
         (*func)();
         if (res32) {
            res = f17;
            printf( "%s %016llx => (raw sp) %08x)",
                    fp_tests[k].name, u0, *((unsigned int *)&res));
         } else {
            resd = f17;
            printf( "%s %016llx => (raw sp) %016llx)",
                    fp_tests[k].name, u0, *(unsigned long long *)(&resd));
         }
         printf( "\n" );
      }

      k++;
      printf( "\n" );
   }
}

static void test_vsx_logic(void)
{
   logic_test_t aTest;
   test_func_t func;
   int k;
   k = 0;

   while ((func = logic_tests[k].test_func)) {
      unsigned int * pv;
      int startA, startB;
      unsigned int * inA, * inB, * dst;
      int idx, i;
      startA = 0;
      aTest = logic_tests[k];
      for (i = 0; i <= (NUM_VIARGS_INTS - (NUM_VIARGS_VECS * sizeof(int))); i++, startA++) {
         startB = startA + 4;
         pv = (unsigned int *)&vec_out;
         inA = &viargs[startA];
         inB = &viargs[startB];
         memcpy(&vec_inA, inA, sizeof(vector unsigned char));
         memcpy(&vec_inB, inB, sizeof(vector unsigned char));
         // clear vec_out
         for (idx = 0; idx < 4; idx++, pv++)
            *pv = 0;

         // execute test insn
         (*func)();
         dst = (unsigned int*) &vec_out;

         printf( "%s:", aTest.name);
         printf( " %08x %08x %08x %08x %s", inA[0], inA[1], inA[2], inA[3], aTest.name);
         printf( " %08x %08x %08x %08x", inB[0], inB[1], inB[2], inB[3]);
         printf(" => %08x %08x %08x %08x\n", dst[0], dst[1], dst[2], dst[3]);

      }
      k++;
   }
   printf( "\n" );
}

static vector unsigned long long vec_args[] __attribute__ ((aligned (16))) =
{
 { 0x0123456789abcdefULL, 0x0011223344556677ULL},
 { 0x8899aabb19293942ULL, 0xa1a2a3a4b1b2b3b4ULL},
 { 0xc1c2c3c4d1d2d3d4ULL, 0x7a6b5d3efc032778ULL}
};
#define NUM_VEC_ARGS_LONGS (sizeof vec_args/sizeof vec_args[0])

static void test_move_ops (void)
{
   move_test_t aTest;
   test_func_t func;
   int k;
   k = 0;

   while ((func = move_tests[k].test_func)) {
      unsigned int * pv;
      int startA, startB;
      unsigned long long * inA, * inB, * dst;
      int use_vecA = (strcmp(move_tests[k].name, "xscpsgndp") == 0);
      int idx;
      inA = NULL;
      aTest = move_tests[k];
      for (startB = 0; startB < NUM_VEC_ARGS_LONGS; startB++) {
         inB = (unsigned long long *)&vec_args[startB];
         memcpy(&vec_inB, inB, sizeof(vector unsigned char));
         if (isLE)
            inB++;
         startA = 0;
repeat:
         if (use_vecA) {
            inA = (unsigned long long *)&vec_args[startA];
            memcpy(&vec_inA, inA, sizeof(vector unsigned char));
            startA++;
         }
         pv = (unsigned int *)&vec_out;
         // clear vec_out
         for (idx = 0; idx < 4; idx++, pv++)
            *pv = 0;

         // execute test insn
         (*func)();
         dst = (unsigned long long *) &vec_out;
         if (isLE) {
            dst++;
            inA++;
         }

         printf( "%s:", aTest.name);
         if (use_vecA)
            printf( " X[A]: %016llx ", *inA);
         printf( " X[B]: %016llx", *inB);
         printf(" => %016llx\n", *dst);

         if (use_vecA && startA < NUM_VEC_ARGS_LONGS)
            goto repeat;
      }
      k++;
      printf( "\n" );
   }
}

static void test_permute_ops (void)
{
  permute_test_t *aTest;
  unsigned int *dst = (unsigned int *) &vec_out;

  for (aTest = &(permute_tests[0]); aTest->test_func != NULL; aTest++)
    {
      /* Grab test input and clear output vector.  */
      memcpy(&vec_inA, aTest->xa, sizeof(vec_inA));
      memcpy(&vec_inB, aTest->xb, sizeof(vec_inB));
      memset(dst, 0, sizeof(vec_out));

      /* execute test insn */
      aTest->test_func();

      printf( "%s:\n", aTest->name);
      printf( "        XA[%08x,%08x,%08x,%08x]\n",
              aTest->xa[0], aTest->xa[1], aTest->xa[2], aTest->xa[3]);
      printf( "        XB[%08x,%08x,%08x,%08x]\n",
              aTest->xb[0], aTest->xb[1], aTest->xb[2], aTest->xb[3]);
      printf( "   =>   XT[%08x,%08x,%08x,%08x]\n",
              dst[0], dst[1], dst[2], dst[3]);

    }
  printf( "\n" );
}

static test_table_t all_tests[] = { { &test_ldst,
                                       "Test VSX load/store instructions" },
                                     { &test_vsx_logic,
                                       "Test VSX logic instructions" },
#ifdef __powerpc64__
                                     { &test_ldbrx,
                                       "Test ldbrx instruction" },
                                     { &test_popcntd,
                                       "Test popcntd instruction" },
#endif
                                     { &test_lfiwzx,
                                       "Test lfiwzx instruction" },
                                     { &test_p7_fpops,
                                       "Test P7 floating point convert instructions"},
                                     { &test_ftdiv,
                                       "Test ftdiv instruction" },
                                     { &test_move_ops,
                                       "Test VSX move instructions"},
                                     { &test_permute_ops,
                                       "Test VSX permute instructions"},
                                     { &test_vx_fp_ops,
                                       "Test VSX floating point instructions"},
                                     { &test_xs_conv_ops,
                                       "Test VSX scalar integer conversion instructions" },
                                     { NULL, NULL }
};
#endif // HAS_VSX

int main(int argc, char *argv[])
{
#ifdef HAS_VSX

   test_table_t aTest;
   test_func_t func;
   int i = 0;

   while ((func = all_tests[i].test_category)) {
      aTest = all_tests[i];
      printf( "%s\n", aTest.name );
      (*func)();
      i++;
   }

#endif // HAS _VSX

   return 0;
}
