/* HOW TO COMPILE:
* 32bit build:
gcc -Winline -Wall -g -O -mregnames -maltivec -m32
* 64bit build:
gcc -Winline -Wall -g -O -mregnames -maltivec -m64
* test_isa_2_07_part1.c:
* PPC tests for the ISA 2.07. This file is based on the
* jm-insns.c file for the new instructions in the ISA 2.07. The
* test structure has been kept the same as the original file to
* the extent possible.
*
* Copyright (C) 2013 IBM
*
* Authors: Carl Love <carll@us.ibm.com>
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/*
* Operation details
* -----------------
*
* The 'loops' (e.g. int_loops) do the actual work:
* - loops over as many arguments as the insn needs (regs | imms)
* - sets up the environment (reset cr,xer, assign src regs...)
* - maybe modifies the asm instn to test different imm args
* - calls the test function
* - retrieves relevant register data (rD,cr,xer,...)
* - prints argument and result data.
*
* More specifically...
*
* all_tests[i] holds insn tests
* - of which each holds: {instn_test_arr[], description, flags}
*
* flags hold 3 instn classifiers: {family, type, arg_type}
*
* // The main test loop:
* do_tests( user_ctl_flags ) {
* foreach(curr_test = all_test[i]) {
*
* // flags are used to control what tests are run:
* if (curr_test->flags && !user_ctl_flags)
* continue;
*
* // a 'loop_family_arr' is chosen based on the 'family' flag...
* switch(curr_test->flags->family) {
* case x: loop_family_arr = int_loops;
* ...
* }
*
* // ...and the actual test_loop to run is found by indexing into
* // the loop_family_arr with the 'arg_type' flag:
* test_loop = loop_family[curr_test->flags->arg_type]
*
* // finally, loop over all instn tests for this test:
* foreach (instn_test = curr_test->instn_test_arr[i]) {
*
* // and call the test_loop with the current instn_test function,name
* test_loop( instn_test->func, instn_test->name )
* }
* }
* }
*
*/
/**********************************************************************/
/* Uncomment to enable output of CR flags for float tests */
//#define TEST_FLOAT_FLAGS
/* Uncomment to enable debug output */
//#define DEBUG_ARGS_BUILD
//#define DEBUG_FILTER
/**********************************************************************/
#include <stdio.h>
#ifdef HAS_ISA_2_07
#include "config.h"
#include <altivec.h>
#include <stdint.h>
#include <assert.h>
#include <ctype.h> // isspace
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // getopt
#if !defined (__TEST_PPC_H__)
#define __TEST_PPC_H__
#include "tests/sys_mman.h"
#include "tests/malloc.h" // memalign16
#define STATIC_ASSERT(e) sizeof(struct { int:-!(e); })
/* Something of the same size as void*, so can be safely be coerced
* to/from a pointer type. Also same size as the host's gp registers.
* According to the AltiVec section of the GCC manual, the syntax does
* not allow the use of a typedef name as a type specifier in conjunction
* with the vector keyword, so typedefs uint[32|64]_t are #undef'ed here
* and redefined using #define.
*/
#undef uint32_t
#undef uint64_t
#define uint32_t unsigned int
#define uint64_t unsigned long long int
#ifndef __powerpc64__
typedef uint32_t HWord_t;
#define ZERO 0
#else
typedef uint64_t HWord_t;
#define ZERO 0ULL
#endif /* __powerpc64__ */
#ifdef VGP_ppc64le_linux
#define isLE 1
#else
#define isLE 0
#endif
typedef uint64_t Word_t;
enum {
compile_time_test1 = STATIC_ASSERT(sizeof(uint32_t) == 4),
compile_time_test2 = STATIC_ASSERT(sizeof(uint64_t) == 8),
};
#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)
#define DEFAULT_VSCR 0x0
static vector unsigned long long vec_out, vec_inA, vec_inB, vec_inC;
static vector unsigned int vec_inA_wd, vec_inB_wd;
/* XXXX these must all be callee-save regs! */
register double f14 __asm__ ("fr14");
register double f15 __asm__ ("fr15");
register double f16 __asm__ ("fr16");
register double f17 __asm__ ("fr17");
register HWord_t r14 __asm__ ("r14");
register HWord_t r15 __asm__ ("r15");
register HWord_t r16 __asm__ ("r16");
register HWord_t r17 __asm__ ("r17");
typedef void (*test_func_t) (void);
typedef struct _test test_t;
typedef struct _test_table test_table_t;
struct _test {
test_func_t func;
const char *name;
};
struct _test_table {
test_t *tests;
const char *name;
uint32_t flags;
};
typedef void (*test_loop_t) (const char *name, test_func_t func,
uint32_t flags);
enum test_flags {
/* Nb arguments */
PPC_ONE_ARG = 0x00000001,
PPC_TWO_ARGS = 0x00000002,
PPC_THREE_ARGS = 0x00000003,
PPC_CMP_ARGS = 0x00000004, // family: compare
PPC_CMPI_ARGS = 0x00000005, // family: compare
PPC_TWO_I16 = 0x00000006, // family: arith/logical
PPC_SPECIAL = 0x00000007, // family: logical
PPC_LD_ARGS = 0x00000008, // family: ldst
PPC_LDX_ARGS = 0x00000009, // family: ldst
PPC_ST_ARGS = 0x0000000A, // family: ldst
PPC_STX_ARGS = 0x0000000B, // family: ldst
PPC_STQ_ARGS = 0x0000000C, // family: ldst, two args, imm
PPC_LDQ_ARGS = 0x0000000D, // family: ldst, two args, imm
PPC_STQX_ARGS = 0x0000000E, // family: ldst, three args
PPC_LDQX_ARGS = 0x0000000F, // family: ldst, three_args
PPC_NB_ARGS = 0x0000000F,
/* Type */
PPC_ARITH = 0x00000100,
PPC_LOGICAL = 0x00000200,
PPC_COMPARE = 0x00000300,
PPC_CROP = 0x00000400,
PPC_LDST = 0x00000500,
PPC_POPCNT = 0x00000600,
PPC_ARITH_DRES = 0x00000700,
PPC_DOUBLE_IN_IRES = 0x00000800,
PPC_MOV = 0x00000A00,
PPC_SHA_OR_BCD = 0x00000B00,
PPC_TYPE = 0x00000F00,
/* Family */
PPC_INTEGER = 0x00010000,
PPC_FLOAT = 0x00020000,
PPC_405 = 0x00030000, // Leave so we keep numbering consistent
PPC_ALTIVEC = 0x00040000,
PPC_FALTIVEC = 0x00050000,
PPC_ALTIVECD = 0x00060000, /* double word Altivec tests */
PPC_ALTIVECQ = 0x00070000,
PPC_FAMILY = 0x000F0000,
/* Flags: these may be combined, so use separate bitfields. */
PPC_CR = 0x01000000,
PPC_XER_CA = 0x02000000,
};
#endif /* !defined (__TEST_PPC_H__) */
/* -------------- END #include "test-ppc.h" -------------- */
#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
#if defined (DEBUG_FILTER)
#define FDPRINTF(fmt, args...) do { fprintf(stderr, fmt , ##args); } while (0)
#else
#define FDPRINTF(fmt, args...) do { } while (0)
#endif
#define unused __attribute__ (( unused ))
typedef struct special {
const char *name;
void (*test_cb)(const char* name, test_func_t func,
unused uint32_t test_flags);
} special_t;
static void test_stq(void)
{
__asm__ __volatile__ ("stq %0, 0(%1)" : :"r" (r14), "r" (r16));
}
static test_t tests_istq_ops_two_i16[] = {
{ &test_stq , "stq", },
{ NULL, NULL, },
};
static void test_lq(void)
{
__asm__ __volatile__ ("lq %0, 0(%1)" : :"r" (r14), "r" (r16));
}
static test_t tests_ildq_ops_two_i16[] = {
{ &test_lq , "lq", },
{ NULL, NULL, },
};
#ifdef HAS_ISA_2_07
Word_t * mem_resv;
static void test_stbcx(void)
{
/* Have to do the lbarx to the memory address to create the reservation
* or the store will not occur.
*/
__asm__ __volatile__ ("lbarx %0, %1, %2" : :"r" (r14), "r" (r16),"r" (r17));
r14 = (HWord_t) 0xABEFCD0145236789ULL;
r15 = (HWord_t) 0x1155337744226688ULL;
__asm__ __volatile__ ("stbcx. %0, %1, %2" : :"r" (r14), "r" (r16),"r" (r17));
}
static void test_sthcx(void)
{
/* Have to do the lharx to the memory address to create the reservation
* or the store will not occur.
*/
__asm__ __volatile__ ("lharx %0, %1, %2" : :"r" (r14), "r" (r16),"r" (r17));
r14 = (HWord_t) 0xABEFCD0145236789ULL;
r15 = (HWord_t) 0x1155337744226688ULL;
__asm__ __volatile__ ("sthcx. %0, %1, %2" : :"r" (r14), "r" (r16),"r" (r17));
}
#endif
static void test_stqcx(void)
{
/* Have to do the lqarx to the memory address to create the reservation
* or the store will not occur.
*/
__asm__ __volatile__ ("lqarx %0, %1, %2" : :"r" (r14), "r" (r16),"r" (r17));
r14 = (HWord_t) 0xABEFCD0145236789ULL;
r15 = (HWord_t) 0x1155337744226688ULL;
__asm__ __volatile__ ("stqcx. %0, %1, %2" : :"r" (r14), "r" (r16),"r" (r17));
}
static test_t tests_stq_ops_three[] = {
#ifdef HAS_ISA_2_07
{ &test_stbcx , "stbcx.", },
{ &test_sthcx , "sthcx.", },
#endif
{ &test_stqcx , "stqcx.", },
{ NULL, NULL, },
};
#ifdef HAS_ISA_2_07
static void test_lbarx(void)
{
__asm__ __volatile__ ("lbarx %0, %1, %2, 0" : :"r" (r14), "r" (r16),"r" (r17));
}
static void test_lharx(void)
{
__asm__ __volatile__ ("lharx %0, %1, %2, 0" : :"r" (r14), "r" (r16),"r" (r17));
}
#endif
static void test_lqarx(void)
{
__asm__ __volatile__ ("lqarx %0, %1, %2, 0" : :"r" (r14), "r" (r16),"r" (r17));
}
static test_t tests_ldq_ops_three[] = {
#ifdef HAS_ISA_2_07
{ &test_lbarx , "lbarx", },
{ &test_lharx , "lharx", },
#endif
{ &test_lqarx , "lqarx", },
{ NULL, NULL, },
};
static void test_fmrgew (void)
{
__asm__ __volatile__ ("fmrgew 17,14,15");
};
static void test_fmrgow (void)
{
__asm__ __volatile__ ("fmrgow 17,14,15");
};
// VSX move instructions
static void test_mfvsrd (void)
{
__asm__ __volatile__ ("mfvsrd %0,%x1" : "=r" (r14) : "ws" (vec_inA));
};
static void test_mfvsrwz (void)
{
__asm__ __volatile__ ("mfvsrwz %0,%x1" : "=r" (r14) : "ws" (vec_inA));
};
static void test_mtvsrd (void)
{
__asm__ __volatile__ ("mtvsrd %x0,%1" : "=ws" (vec_out) : "r" (r14));
};
static void test_mtvsrwz (void)
{
__asm__ __volatile__ ("mtvsrwz %x0,%1" : "=ws" (vec_out) : "r" (r14));
};
static void test_mtfprwa (void)
{
__asm__ __volatile__ ("mtfprwa %x0,%1" : "=ws" (vec_out) : "r" (r14));
};
static test_t tests_move_ops_spe[] = {
{ &test_mfvsrd , "mfvsrd" },
{ &test_mfvsrwz , "mfvsrwz" },
{ &test_mtvsrd , "mtvsrd" },
{ &test_mtvsrwz , "mtvsrwz" },
{ &test_mtfprwa , "mtfprwa" },
{ NULL, NULL }
};
/* NOTE: Since these are "vector" instructions versus VSX, we must use
* vector constraints.
*
* Vector Double Word tests.
*/
static void test_vpkudum (void)
{
__asm__ __volatile__ ("vpkudum %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vaddudm (void)
{
__asm__ __volatile__ ("vaddudm %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vsubudm (void)
{
__asm__ __volatile__ ("vsubudm %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vmaxud (void)
{
__asm__ __volatile__ ("vmaxud %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vmaxsd (void)
{
__asm__ __volatile__ ("vmaxsd %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vminud (void)
{
__asm__ __volatile__ ("vminud %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vminsd (void)
{
__asm__ __volatile__ ("vminsd %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vcmpequd (void)
{
__asm__ __volatile__ ("vcmpequd %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vcmpgtud (void)
{
__asm__ __volatile__ ("vcmpgtud %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vcmpgtsd (void)
{
__asm__ __volatile__ ("vcmpgtsd %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vrld (void)
{
__asm__ __volatile__ ("vrld %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vsld (void)
{
__asm__ __volatile__ ("vsld %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vsrad (void)
{
__asm__ __volatile__ ("vsrad %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vsrd (void)
{
__asm__ __volatile__ ("vsrd %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
/* Vector Double Word saturate tests.*/
static void test_vpkudus (void)
{
__asm__ __volatile__ ("vpkudus %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vpksdus (void)
{
__asm__ __volatile__ ("vpksdus %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vpksdss (void)
{
__asm__ __volatile__ ("vpksdss %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
/* Vector unpack two words from one vector arg */
static void test_vupkhsw (void)
{
__asm__ __volatile__ ("vupkhsw %0, %1" : "=v" (vec_out): "v" (vec_inB_wd));
}
static void test_vupklsw (void)
{
__asm__ __volatile__ ("vupklsw %0, %1" : "=v" (vec_out): "v" (vec_inB_wd));
}
/* Vector Integer Word tests.*/
static void test_vmulouw (void)
{
__asm__ __volatile__ ("vmulouw %0, %1, %2" : "=v" (vec_out): "v" (vec_inA_wd),"v" (vec_inB_wd));
}
static void test_vmuluwm (void)
{
__asm__ __volatile__ ("vmuluwm %0, %1, %2" : "=v" (vec_out): "v" (vec_inA_wd),"v" (vec_inB_wd));
}
static void test_vmulosw (void)
{
__asm__ __volatile__ ("vmulosw %0, %1, %2" : "=v" (vec_out): "v" (vec_inA_wd),"v" (vec_inB_wd));
}
static void test_vmuleuw (void)
{
__asm__ __volatile__ ("vmuleuw %0, %1, %2" : "=v" (vec_out): "v" (vec_inA_wd),"v" (vec_inB_wd));
}
static void test_vmulesw (void)
{
__asm__ __volatile__ ("vmulesw %0, %1, %2" : "=v" (vec_out): "v" (vec_inA_wd),"v" (vec_inB_wd));
}
static void test_vmrgew (void)
{
__asm__ __volatile__ ("vmrgew %0, %1, %2" : "=v" (vec_out): "v" (vec_inA_wd),"v" (vec_inB_wd));
}
static void test_vmrgow (void)
{
__asm__ __volatile__ ("vmrgow %0, %1, %2" : "=v" (vec_out): "v" (vec_inA_wd),"v" (vec_inB_wd));
}
static void test_vpmsumb (void)
{
__asm__ __volatile__ ("vpmsumb %0, %1, %2" : "=v" (vec_out): "v" (vec_inA_wd),"v" (vec_inB_wd));
}
static void test_vpmsumh (void)
{
__asm__ __volatile__ ("vpmsumh %0, %1, %2" : "=v" (vec_out): "v" (vec_inA_wd),"v" (vec_inB_wd));
}
static void test_vpmsumw (void)
{
__asm__ __volatile__ ("vpmsumw %0, %1, %2" : "=v" (vec_out): "v" (vec_inA_wd),"v" (vec_inB_wd));
}
static void test_vpermxor (void)
{
__asm__ __volatile__ ("vpermxor %0, %1, %2, %3" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB),"v" (vec_inC));
}
static void test_vpmsumd (void)
{
__asm__ __volatile__ ("vpmsumd %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vnand (void)
{
__asm__ __volatile__ ("vnand %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vorc (void)
{
__asm__ __volatile__ ("vorc %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_veqv (void)
{
__asm__ __volatile__ ("veqv %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vcipher (void)
{
__asm__ __volatile__ ("vcipher %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vcipherlast (void)
{
__asm__ __volatile__ ("vcipherlast %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vncipher (void)
{
__asm__ __volatile__ ("vncipher %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vncipherlast (void)
{
__asm__ __volatile__ ("vncipherlast %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vclzb (void)
{
__asm__ __volatile__ ("vclzb %0, %1" : "=v" (vec_out): "v" (vec_inB));
}
static void test_vclzw (void)
{
__asm__ __volatile__ ("vclzw %0, %1" : "=v" (vec_out): "v" (vec_inB));
}
static void test_vclzh (void)
{
__asm__ __volatile__ ("vclzh %0, %1" : "=v" (vec_out): "v" (vec_inB));
}
static void test_vclzd (void)
{
__asm__ __volatile__ ("vclzd %0, %1" : "=v" (vec_out): "v" (vec_inB));
}
static void test_vpopcntb (void)
{
__asm__ __volatile__ ("vpopcntb %0, %1" : "=v" (vec_out): "v" (vec_inB));
}
static void test_vpopcnth (void)
{
__asm__ __volatile__ ("vpopcnth %0, %1" : "=v" (vec_out): "v" (vec_inB));
}
static void test_vpopcntw (void)
{
__asm__ __volatile__ ("vpopcntw %0, %1" : "=v" (vec_out): "v" (vec_inB));
}
static void test_vpopcntd (void)
{
__asm__ __volatile__ ("vpopcntd %0, %1" : "=v" (vec_out): "v" (vec_inB));
}
static void test_vsbox (void)
{
__asm__ __volatile__ ("vsbox %0, %1" : "=v" (vec_out): "v" (vec_inB));
}
static int st_six;
static void test_vshasigmad (void)
{
switch (st_six) {
case 0x00:
__asm__ __volatile__ ("vshasigmad %0, %1, 0, 0" : "=v" (vec_out): "v" (vec_inA));
break;
case 0x0f:
__asm__ __volatile__ ("vshasigmad %0, %1, 0, 15" : "=v" (vec_out): "v" (vec_inA));
break;
case 0x10:
__asm__ __volatile__ ("vshasigmad %0, %1, 1, 0" : "=v" (vec_out): "v" (vec_inA));
break;
case 0x1f:
__asm__ __volatile__ ("vshasigmad %0, %1, 1, 15" : "=v" (vec_out): "v" (vec_inA));
break;
}
}
static void test_vshasigmaw (void)
{
switch (st_six) {
case 0x00:
__asm__ __volatile__ ("vshasigmaw %0, %1, 0, 0" : "=v" (vec_out): "v" (vec_inA));
break;
case 0x0f:
__asm__ __volatile__ ("vshasigmaw %0, %1, 0, 15" : "=v" (vec_out): "v" (vec_inA));
break;
case 0x10:
__asm__ __volatile__ ("vshasigmaw %0, %1, 1, 0" : "=v" (vec_out): "v" (vec_inA));
break;
case 0x1f:
__asm__ __volatile__ ("vshasigmaw %0, %1, 1, 15" : "=v" (vec_out): "v" (vec_inA));
break;
}
}
static int PS_bit;
static void test_bcdadd (void)
{
if (PS_bit)
__asm__ __volatile__ ("bcdadd. %0, %1, %2, 1" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
else
__asm__ __volatile__ ("bcdadd. %0, %1, %2, 0" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_bcdsub (void)
{
if (PS_bit)
__asm__ __volatile__ ("bcdsub. %0, %1, %2, 1" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
else
__asm__ __volatile__ ("bcdsub. %0, %1, %2, 0" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vaddcuq (void)
{
__asm__ __volatile__ ("vaddcuq %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vadduqm (void)
{
__asm__ __volatile__ ("vadduqm %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vaddecuq (void)
{
__asm__ __volatile__ ("vaddecuq %0, %1, %2, %3" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB),"v" (vec_inC));
}
static void test_vaddeuqm (void)
{
__asm__ __volatile__ ("vaddeuqm %0, %1, %2, %3" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB),"v" (vec_inC));
}
static void test_vsubcuq (void)
{
__asm__ __volatile__ ("vsubcuq %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vsubuqm (void)
{
__asm__ __volatile__ ("vsubuqm %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vsubecuq (void)
{
__asm__ __volatile__ ("vsubecuq %0, %1, %2, %3" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB),"v" (vec_inC));
}
static void test_vsubeuqm (void)
{
__asm__ __volatile__ ("vsubeuqm %0, %1, %2, %3" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB),"v" (vec_inC));
}
static void test_vbpermq (void)
{
__asm__ __volatile__ ("vbpermq %0, %1, %2" : "=v" (vec_out): "v" (vec_inA),"v" (vec_inB));
}
static void test_vgbbd (void)
{
__asm__ __volatile__ ("vgbbd %0, %1" : "=v" (vec_out): "v" (vec_inB));
}
static test_t tests_aa_quadword_two_args[] = {
{ &test_vaddcuq , "vaddcuq" },
{ &test_vadduqm , "vadduqm" },
{ &test_vsubcuq , "vsubcuq" },
{ &test_vsubuqm , "vsubuqm" },
{ &test_vbpermq , "vbpermq" },
{ NULL , NULL },
};
static test_t tests_aa_quadword_three_args[] = {
{ &test_vaddecuq , "vaddecuq" },
{ &test_vaddeuqm , "vaddeuqm" },
{ &test_vsubecuq , "vsubecuq" },
{ &test_vsubeuqm , "vsubeuqm" },
{ NULL , NULL },
};
static test_t tests_aa_bcd_ops[] = {
{ &test_bcdadd , "bcdadd." },
{ &test_bcdsub , "bcdsub." },
{ NULL , NULL },
};
static test_t tests_aa_SHA_ops[] = {
{ &test_vshasigmad , "vshasigmad" },
{ &test_vshasigmaw , "vshasigmaw" },
{ NULL , NULL },
};
static test_t tests_aa_ops_three[] = {
{ &test_vpermxor , "vpermxor" },
{ NULL , NULL },
};
static test_t tests_aa_word_ops_one_arg_dres[] = {
{ &test_vupkhsw , "vupkhsw" },
{ &test_vupklsw , "vupklsw" },
{ NULL , NULL }
};
static test_t tests_aa_word_ops_two_args_dres[] = {
{ &test_vmulouw , "vmulouw" },
{ &test_vmuluwm , "vmuluwm" },
{ &test_vmulosw , "vmulosw" },
{ &test_vmuleuw , "vmuleuw" },
{ &test_vmulesw , "vmulesw" },
{ &test_vmrgew , "vmrgew" },
{ &test_vmrgow , "vmrgow" },
{ &test_vpmsumb , "vpmsumb" },
{ &test_vpmsumh , "vpmsumh" },
{ &test_vpmsumw , "vpmsumw" },
{ NULL , NULL }
};
static test_t tests_aa_dbl_ops_two_args[] = {
{ &test_vaddudm , "vaddudm", },
{ &test_vsubudm , "vsubudm", },
{ &test_vmaxud , "vmaxud", },
{ &test_vmaxsd , "vmaxsd", },
{ &test_vminud , "vminud", },
{ &test_vminsd , "vminsd", },
{ &test_vcmpequd , "vcmpequd", },
{ &test_vcmpgtud , "vcmpgtud", },
{ &test_vcmpgtsd , "vcmpgtsd", },
{ &test_vrld , "vrld", },
{ &test_vsld , "vsld", },
{ &test_vsrad , "vsrad", },
{ &test_vsrd , "vsrd", },
{ &test_vpkudum , "vpkudum", },
{ &test_vpmsumd , "vpmsumd", },
{ &test_vnand , "vnand", },
{ &test_vorc , "vorc", },
{ &test_veqv , "veqv", },
{ &test_vcipher , "vcipher" },
{ &test_vcipherlast , "vcipherlast" },
{ &test_vncipher , "vncipher" },
{ &test_vncipherlast , "vncipherlast" },
{ NULL , NULL, },
};
static test_t tests_aa_dbl_ops_one_arg[] = {
{ &test_vclzb , "vclzb" },
{ &test_vclzw , "vclzw" },
{ &test_vclzh , "vclzh" },
{ &test_vclzd , "vclzd" },
{ &test_vpopcntb , "vpopcntb" },
{ &test_vpopcnth , "vpopcnth" },
{ &test_vpopcntw , "vpopcntw" },
{ &test_vpopcntd , "vpopcntd" },
{ &test_vsbox , "vsbox" },
{ &test_vgbbd , "vgbbd" },
{ NULL , NULL, }
};
static test_t tests_aa_dbl_to_int_two_args[] = {
{ &test_vpkudus , "vpkudus", },
{ &test_vpksdus , "vpksdus", },
{ &test_vpksdss , "vpksdss", },
{ NULL , NULL, },
};
static int verbose = 0;
static int arg_list_size = 0;
static unsigned long long * vdargs = NULL;
static unsigned long long * vdargs_x = NULL;
#define NB_VDARGS 4
static void build_vdargs_table (void)
{
// Each VSX register holds two doubleword integer values
vdargs = memalign16(NB_VDARGS * sizeof(unsigned long long));
vdargs[0] = 0x0102030405060708ULL;
vdargs[1] = 0x090A0B0C0E0D0E0FULL;
vdargs[2] = 0xF1F2F3F4F5F6F7F8ULL;
vdargs[3] = 0xF9FAFBFCFEFDFEFFULL;
vdargs_x = memalign16(NB_VDARGS * sizeof(unsigned long long));
vdargs_x[0] = 0x000000007c118a2bULL;
vdargs_x[1] = 0x00000000f1112345ULL;
vdargs_x[2] = 0x01F2F3F4F5F6F7F8ULL;
vdargs_x[3] = 0xF9FAFBFCFEFDFEFFULL;
}
static unsigned int * vwargs = NULL;
#define NB_VWARGS 8
static void build_vwargs_table (void)
{
// Each VSX register holds 4 integer word values
size_t i = 0;
vwargs = memalign(8, 8 * sizeof(int));
assert(vwargs);
assert(0 == ((8-1) & (unsigned long)vwargs));
vwargs[i++] = 0x01020304;
vwargs[i++] = 0x05060708;
vwargs[i++] = 0x090A0B0C;
vwargs[i++] = 0x0E0D0E0F;
vwargs[i++] = 0xF1F2F3F4;
vwargs[i++] = 0xF5F6F7F8;
vwargs[i++] = 0xF9FAFBFC;
vwargs[i++] = 0xFEFDFEFF;
}
static unsigned long long vbcd_args[] __attribute__ ((aligned (16))) = {
0x8045090189321003ULL, // Negative BCD value
0x001122334556677dULL,
0x0000107600000001ULL, // Positive BCD value
0x319293945142031aULL,
0x0ULL, // Valid BCD zero
0xaULL,
0x0ULL, // Invalid BCD zero (no sign code)
0x0ULL
};
#define NUM_VBCD_VALS (sizeof vbcd_args/sizeof vbcd_args[0])
static void build_vargs_table (void)
{
build_vdargs_table();
build_vwargs_table();
}
static double *fargs = NULL;
static int nb_fargs = 0;
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)
* Ref only:
* 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, e0, e1;
int s;
int i=0;
/* Note: VEX isn't so hot with denormals, so don't bother
testing them: set _exp > 0
*/
if ( arg_list_size == 1 ) { // Large
fargs = malloc(200 * sizeof(double));
for (s=0; s<2; s++) {
for (e0=0; e0<2; e0++) {
for (e1=0x001; ; e1 = ((e1 + 1) << 2) + 6) {
if (e1 >= 0x400)
e1 = 0x3fe;
_exp = (e0 << 10) | e1;
for (mant = 0x0000000000001ULL; mant < (1ULL << 52);
/* Add 'random' bits */
mant = ((mant + 0x4A6) << 13) + 0x359) {
register_farg(&fargs[i++], s, _exp, mant);
}
if (e1 == 0x3fe)
break;
}
}
}
} else { // Default
fargs = malloc(16 * sizeof(double));
for (s=0; s<2; s++) { // x2
for (e1=0x001; ; e1 = ((e1 + 1) << 13) + 7) { // x2
if (e1 >= 0x400)
e1 = 0x3fe;
_exp = e1;
for (mant = 0x0000000000001ULL; mant < (1ULL << 52);
/* Add 'random' bits */
mant = ((mant + 0x4A6) << 29) + 0x359) { // x2
register_farg(&fargs[i++], s, _exp, mant);
}
if (e1 == 0x3fe)
break;
}
}
}
/* Special values */
/* +0.0 : 0 0x000 0x0000000000000 */
s = 0;
_exp = 0x000;
mant = 0x0000000000000ULL;
register_farg(&fargs[i++], s, _exp, mant);
/* -0.0 : 1 0x000 0x0000000000000 */
s = 1;
_exp = 0x000;
mant = 0x0000000000000ULL;
register_farg(&fargs[i++], s, _exp, mant);
/* +infinity : 0 0x7FF 0x0000000000000 */
s = 0;
_exp = 0x7FF;
mant = 0x0000000000000ULL;
register_farg(&fargs[i++], s, _exp, mant);
/* -infinity : 1 0x7FF 0x0000000000000 */
s = 1;
_exp = 0x7FF;
mant = 0x0000000000000ULL;
register_farg(&fargs[i++], s, _exp, mant);
/* +QNaN : 0 0x7FF 0x7FFFFFFFFFFFF */
s = 0;
_exp = 0x7FF;
mant = 0x7FFFFFFFFFFFFULL;
register_farg(&fargs[i++], s, _exp, mant);
/* -QNaN : 1 0x7FF 0x7FFFFFFFFFFFF */
s = 1;
_exp = 0x7FF;
mant = 0x7FFFFFFFFFFFFULL;
register_farg(&fargs[i++], s, _exp, mant);
/* +SNaN : 0 0x7FF 0x8000000000000 */
s = 0;
_exp = 0x7FF;
mant = 0x8000000000000ULL;
register_farg(&fargs[i++], s, _exp, mant);
/* -SNaN : 1 0x7FF 0x8000000000000 */
s = 1;
_exp = 0x7FF;
mant = 0x8000000000000ULL;
register_farg(&fargs[i++], s, _exp, mant);
AB_DPRINTF("Registered %d fargs values\n", i);
nb_fargs = i;
}
static int check_filter (char *filter)
{
char *c;
int ret = 1;
if (filter != NULL) {
c = strchr(filter, '*');
if (c != NULL) {
*c = '\0';
ret = 0;
}
}
return ret;
}
static int check_name (const char* name, const char *filter,
int exact)
{
int nlen, flen;
int ret = 0;
if (filter != NULL) {
for (; isspace(*name); name++)
continue;
FDPRINTF("Check '%s' againt '%s' (%s match)\n",
name, filter, exact ? "exact" : "starting");
nlen = strlen(name);
flen = strlen(filter);
if (exact) {
if (nlen == flen && memcmp(name, filter, flen) == 0)
ret = 1;
} else {
if (flen <= nlen && memcmp(name, filter, flen) == 0)
ret = 1;
}
} else {
ret = 1;
}
return ret;
}
typedef struct insn_sel_flags_t_struct {
int one_arg, two_args, three_args;
int arith, logical, compare, ldst;
int integer, floats, altivec, faltivec;
int cr;
} insn_sel_flags_t;
static void test_float_two_args (const char* name, test_func_t func,
unused uint32_t test_flags)
{
double res;
Word_t u0, u1, ur;
volatile uint32_t flags;
int i, j;
for (i=0; i<nb_fargs; i+=3) {
for (j=0; j<nb_fargs; j+=5) {
u0 = *(Word_t *)(&fargs[i]);
u1 = *(Word_t *)(&fargs[j]);
f14 = fargs[i];
f15 = fargs[j];
SET_FPSCR_ZERO;
SET_CR_XER_ZERO;
(*func)();
GET_CR(flags);
res = f17;
ur = *(uint64_t *)(&res);
printf("%s %016llx, %016llx => %016llx",
name, u0, u1, ur);
#if defined TEST_FLOAT_FLAGS
printf(" (%08x)", flags);
#endif
printf("\n");
}
if (verbose) printf("\n");
}
}
static void mfvs(const char* name, test_func_t func,
unused uint32_t test_flags)
{
/* This test is for move instructions where the input is a scalar register
* and the destination is a vector register.
*/
int i;
volatile Word_t result;
result = 0ULL;
for (i=0; i < NB_VDARGS; i++) {
r14 = ZERO;
if (isLE)
vec_inA = (vector unsigned long long){ 0ULL, vdargs[i] };
else
vec_inA = (vector unsigned long long){ vdargs[i], 0ULL };
(*func)();
result = r14;
printf("%s: %016llx => %016llx\n", name, vdargs[i], result);
}
}
static void mtvs(const char* name, test_func_t func,
unused uint32_t test_flags)
{
/* This test is for move instructions where the input is a scalar register
* and the destination is a vector register.
*/
unsigned long long *dst;
int i;
for (i=0; i < NB_VDARGS; i++) {
r14 = vdargs[i];
vec_out = (vector unsigned long long){ 0ULL, 0ULL };
(*func)();
dst = (unsigned long long *) &vec_out;
if (isLE)
dst++;
printf("%s: %016llx => %016llx\n", name, vdargs[i], *dst);
}
}
static void mtvs2s(const char* name, test_func_t func,
unused uint32_t test_flags)
{
/* This test is the mtvsrwa instruction.
*/
unsigned long long *dst;
int i;
for (i=0; i < NB_VDARGS; i++) {
// Only the lower half of the vdarg doubleword arg will be used as input by mtvsrwa
unsigned int * src = (unsigned int *)&vdargs[i];
if (!isLE)
src++;
r14 = vdargs[i];
vec_out = (vector unsigned long long){ 0ULL, 0ULL };
(*func)();
// Only doubleword 0 is used in output
dst = (unsigned long long *) &vec_out;
if (isLE)
dst++;
printf("%s: %08x => %016llx\n", name, *src, *dst);
}
}
static void test_special (special_t *table,
const char* name, test_func_t func,
unused uint32_t test_flags)
{
const char *tmp;
int i;
for (tmp = name; isspace(*tmp); tmp++)
continue;
for (i=0; table[i].name != NULL; i++) {
if (strcmp(table[i].name, tmp) == 0) {
(*table[i].test_cb)(name, func, test_flags);
return;
}
}
fprintf(stderr, "ERROR: no test found for op '%s'\n", name);
}
static special_t special_move_ops[] = {
{
"mfvsrd", /* move from vector to scalar reg doubleword */
&mfvs,
},
{
"mtvsrd", /* move from scalar to vector reg doubleword */
&mtvs,
},
{
"mtfprwa", /* (extended mnemonic for mtvsrwa) move from scalar to vector reg with two’s-complement */
&mtvs2s,
},
{
"mfvsrwz", /* move from vector to scalar reg word */
&mfvs,
},
{
"mtvsrwz", /* move from scalar to vector reg word */
&mtvs2s,
}
};
static void test_move_special(const char* name, test_func_t func,
uint32_t test_flags)
{
test_special(special_move_ops, name, func, test_flags);
}
/* Vector Double Word tests */
static void test_av_dint_two_args (const char* name, test_func_t func,
unused uint32_t test_flags)
{
unsigned long long * dst;
unsigned int * dst_int;
int i,j;
int family = test_flags & PPC_FAMILY;
int is_vpkudum, is_vpmsumd;
if (strcmp(name, "vpkudum") == 0)
is_vpkudum = 1;
else
is_vpkudum = 0;
if (strcmp(name, "vpmsumd") == 0)
is_vpmsumd = 1;
else
is_vpmsumd = 0;
for (i = 0; i < NB_VDARGS; i+=2) {
if (isLE && family == PPC_ALTIVECQ)
vec_inA = (vector unsigned long long){ vdargs[i+1], vdargs[i] };
else
vec_inA = (vector unsigned long long){ vdargs[i], vdargs[i+1] };
for (j = 0; j < NB_VDARGS; j+=2) {
if (isLE && family == PPC_ALTIVECQ)
vec_inB = (vector unsigned long long){ vdargs[j+1], vdargs[j] };
else
vec_inB = (vector unsigned long long){ vdargs[j], vdargs[j+1] };
vec_out = (vector unsigned long long){ 0,0 };
(*func)();
dst_int = (unsigned int *)&vec_out;
dst = (unsigned long long*)&vec_out;
printf("%s: ", name);
if (is_vpkudum) {
printf("Inputs: %08llx %08llx %08llx %08llx\n", vdargs[i] & 0x00000000ffffffffULL,
vdargs[i+1] & 0x00000000ffffffffULL, vdargs[j] & 0x00000000ffffffffULL,
vdargs[j+1] & 0x00000000ffffffffULL);
if (isLE)
printf(" Output: %08x %08x %08x %08x\n", dst_int[2], dst_int[3],
dst_int[0], dst_int[1]);
else
printf(" Output: %08x %08x %08x %08x\n", dst_int[0], dst_int[1],
dst_int[2], dst_int[3]);
} else if (is_vpmsumd) {
printf("%016llx @@ %016llx ", vdargs[i], vdargs[j]);
if (isLE)
printf(" ==> %016llx\n", dst[1]);
else
printf(" ==> %016llx\n", dst[0]);
printf("\t%016llx @@ %016llx ", vdargs[i+1], vdargs[j+1]);
if (isLE)
printf(" ==> %016llx\n", dst[0]);
else
printf(" ==> %016llx\n", dst[1]);
} else if (family == PPC_ALTIVECQ) {
if (isLE)
printf("%016llx%016llx @@ %016llx%016llx ==> %016llx%016llx\n",
vdargs[i], vdargs[i+1], vdargs[j], vdargs[j+1],
dst[1], dst[0]);
else
printf("%016llx%016llx @@ %016llx%016llx ==> %016llx%016llx\n",
vdargs[i], vdargs[i+1], vdargs[j], vdargs[j+1],
dst[0], dst[1]);
} else {
printf("%016llx @@ %016llx ", vdargs[i], vdargs[j]);
printf(" ==> %016llx\n", dst[0]);
printf("\t%016llx @@ %016llx ", vdargs[i+1], vdargs[j+1]);
printf(" ==> %016llx\n", dst[1]);
}
}
}
}
static void test_av_dint_one_arg (const char* name, test_func_t func,
unused uint32_t test_flags)
{
unsigned long long * dst;
int i;
for (i = 0; i < NB_VDARGS; i+=2) {
vec_inB = (vector unsigned long long){ vdargs[i], vdargs[i+1] };
vec_out = (vector unsigned long long){ 0,0 };
(*func)();
dst = (unsigned long long*)&vec_out;
printf("%s: ", name);
printf("%016llx @@ %016llx ", vdargs[i], vdargs[i + 1]);
printf(" ==> %016llx%016llx\n", dst[0], dst[1]);
}
}
static void test_av_dint_one_arg_SHA (const char* name, test_func_t func,
unused uint32_t test_flags)
{
unsigned long long * dst;
int i, st, six;
for (i = 0; i < NB_VDARGS; i+=2) {
vec_inA = (vector unsigned long long){ vdargs[i], vdargs[i+1] };
vec_out = (vector unsigned long long){ 0,0 };
for (st = 0; st < 2; st++) {
for (six = 0; six < 16; six+=15) {
st_six = (st << 4) | six;
(*func)();
dst = (unsigned long long*)&vec_out;
printf("%s: ", name);
printf("%016llx @@ %016llx ", vdargs[i], vdargs[i + 1]);
printf(" ==> %016llx || %016llx\n", dst[0], dst[1]);
}
}
}
}
static void test_av_bcd (const char* name, test_func_t func,
unused uint32_t test_flags)
{
unsigned long long * dst;
int i, j;
for (i = 0; i < NUM_VBCD_VALS; i+=2) {
if (isLE)
vec_inA = (vector unsigned long long){ vbcd_args[i+1], vbcd_args[i] };
else
vec_inA = (vector unsigned long long){ vbcd_args[i], vbcd_args[i+1] };
for (j = 0; j < NUM_VBCD_VALS; j+=2) {
if (isLE)
vec_inB = (vector unsigned long long){ vbcd_args[j+1], vbcd_args[j] };
else
vec_inB = (vector unsigned long long){ vbcd_args[j], vbcd_args[j+1] };
vec_out = (vector unsigned long long){ 0, 0 };
for (PS_bit = 0; PS_bit < 2; PS_bit++) {
(*func)();
dst = (unsigned long long*)&vec_out;
printf("%s: ", name);
printf("%016llx || %016llx @@ %016llx || %016llx",
vbcd_args[i], vbcd_args[i + 1],
vbcd_args[j], vbcd_args[j + 1]);
if (isLE)
printf(" ==> %016llx || %016llx\n", dst[1], dst[0]);
else
printf(" ==> %016llx || %016llx\n", dst[0], dst[1]);
}
}
}
}
/* Vector doubleword-to-int tests, two input args, integer result */
static void test_av_dint_to_int_two_args (const char* name, test_func_t func,
unused uint32_t test_flags)
{
unsigned int * dst_int;
int i,j;
for (i = 0; i < NB_VDARGS; i+=2) {
vec_inA = (vector unsigned long long){ vdargs_x[i], vdargs_x[i+1] };
for (j = 0; j < NB_VDARGS; j+=2) {
vec_inB = (vector unsigned long long){ vdargs_x[j], vdargs_x[j+1] };
vec_out = (vector unsigned long long){ 0,0 };
(*func)();
dst_int = (unsigned int *)&vec_out;
printf("%s: ", name);
printf("%016llx, %016llx @@ %016llx, %016llx ",
vdargs_x[i], vdargs_x[i+1],
vdargs_x[j], vdargs_x[j+1]);
if (isLE)
printf(" ==> %08x %08x %08x %08x\n", dst_int[2], dst_int[3],
dst_int[0], dst_int[1]);
else
printf(" ==> %08x %08x %08x %08x\n", dst_int[0], dst_int[1],
dst_int[2], dst_int[3]);
}
}
}
/* Vector Word tests; two integer args, with double word result */
static void test_av_wint_two_args_dres (const char* name, test_func_t func,
unused uint32_t test_flags)
{
unsigned long long * dst;
int i,j;
for (i = 0; i < NB_VWARGS; i+=4) {
if (isLE)
vec_inA_wd = (vector unsigned int){ vwargs[i+3], vwargs[i+2], vwargs[i+1], vwargs[i] };
else
vec_inA_wd = (vector unsigned int){ vwargs[i], vwargs[i+1], vwargs[i+2], vwargs[i+3] };
for (j = 0; j < NB_VWARGS; j+=4) {
if (isLE)
vec_inB_wd = (vector unsigned int){ vwargs[j+3], vwargs[j+2], vwargs[j+1], vwargs[j] };
else
vec_inB_wd = (vector unsigned int){ vwargs[j], vwargs[j+1], vwargs[j+2], vwargs[j+3] };
vec_out = (vector unsigned long long){ 0, 0 };
(*func)();
dst = (unsigned long long *)&vec_out;
printf("%s: ", name);
if (isLE)
printf("%08x %08x %08x %08x ==> %016llx %016llx\n",
vwargs[i], vwargs[i+1], vwargs[i+2], vwargs[i+3], dst[1], dst[0]);
else
printf("%08x %08x %08x %08x ==> %016llx %016llx\n",
vwargs[i], vwargs[i+1], vwargs[i+2], vwargs[i+3], dst[0], dst[1]);
}
}
}
/* Vector Word tests; one input arg, with double word result */
static void test_av_wint_one_arg_dres (const char* name, test_func_t func,
unused uint32_t test_flags)
{
unsigned long long * dst;
int i;
for (i = 0; i < NB_VWARGS; i+=4) {
if (isLE)
vec_inB_wd = (vector unsigned int){ vwargs[i+3], vwargs[i+2], vwargs[i+1], vwargs[i] };
else
vec_inB_wd = (vector unsigned int){ vwargs[i], vwargs[i+1], vwargs[i+2], vwargs[i+3] };
vec_out = (vector unsigned long long){ 0, 0 };
(*func)();
dst = (unsigned long long *)&vec_out;
printf("%s: ", name);
if (isLE)
printf("%08x %08x %08x %08x ==> %016llx %016llx\n",
vwargs[i], vwargs[i+1], vwargs[i+2], vwargs[i+3], dst[1], dst[0]);
else
printf("%08x %08x %08x %08x ==> %016llx %016llx\n",
vwargs[i], vwargs[i+1], vwargs[i+2], vwargs[i+3], dst[0], dst[1]);
}
}
static void test_int_stq_two_regs_imm16 (const char* name,
test_func_t func_IN,
unused uint32_t test_flags)
{
/* Store quad word from register pair */
int offs, k;
HWord_t base;
Word_t *iargs_priv;
// private iargs table to store to, note storing pair of regs
iargs_priv = memalign16(2 * sizeof(Word_t));
base = (HWord_t)&iargs_priv[0];
for (k = 0; k < 2; k++) // clear array
iargs_priv[k] = 0;
offs = 0;
/* setup source register pair */
r14 = (HWord_t) 0xABCDEF0123456789ULL;
r15 = (HWord_t) 0x1133557722446688ULL;
r16 = base; // store to r16 + offs
(*func_IN)();
#ifndef __powerpc64__
printf("%s %08x,%08x, %2d => "
#else
printf("%s %016llx,%016llx, %3d => "
#endif
"%016llx,%016llx)\n",
name, r14, r15, offs, iargs_priv[0], iargs_priv[1]);
if (verbose) printf("\n");
free(iargs_priv);
}
static void test_int_stq_three_regs (const char* name,
test_func_t func_IN,
unused uint32_t test_flags)
{
/* Store quad word from register pair */
volatile uint32_t flags, xer;
int k;
HWord_t base;
base = (HWord_t)&mem_resv[0];
for (k = 0; k < 2; k++) // setup array for lqarx inst
mem_resv[k] = k;
/* setup source register pair for store */
r14 = ZERO;
r15 = ZERO;
r16 = base; // store to r16 + r17
r17 = ZERO;
/* In order for the store to occur, the lqarx instruction must first
* be used to load from the address thus creating a reservation at the
* memory address. The lqarx instruction is done in the test_stqcx(),
* then registers 14, r15 are changed to the data to be stored in memory
* by the stqcx instruction.
*/
SET_CR_XER_ZERO;
(*func_IN)();
GET_CR_XER(flags,xer);
#ifndef __powerpc64__
printf("%s %08x,%08x, => "
#else
printf("%s %016llx,%016llx => "
#endif
"%016llx,%016llx; CR=%08x\n",
name, r14, r15, mem_resv[0], mem_resv[1], flags);
if (verbose) printf("\n");
}
static void test_int_ldq_two_regs_imm16 (const char* name,
test_func_t func_IN,
unused uint32_t test_flags)
{
/* load quad word from register pair */
volatile uint32_t flags, xer;
Word_t * mem_priv;
HWord_t base;
// private iargs table to store to, note storing pair of regs
mem_priv = memalign16(2 * sizeof(Word_t)); // want 128-bits
base = (HWord_t)&mem_priv[0];
mem_priv[0] = 0xAACCEE0011335577ULL;
mem_priv[1] = 0xABCDEF0123456789ULL;
r14 = 0;
r15 = 0;
r16 = base; // fetch from r16 + offs
SET_CR_XER_ZERO;
(*func_IN)();
GET_CR_XER(flags,xer);
#ifndef __powerpc64__
printf("%s (0x%016llx, 0x%016llx) => (reg_pair = %08x,%08x)\n",
#else
printf("%s (0x%016llx, 0x%016llx) => (reg_pair = 0x%016llx, 0x%016llx)\n",
#endif
name, mem_priv[0], mem_priv[1], r14, r15);
if (verbose) printf("\n");
free(mem_priv);
}
static void test_int_ldq_three_regs (const char* name,
test_func_t func_IN,
unused uint32_t test_flags)
{
/* load quad word from register pair */
HWord_t base;
base = (HWord_t)&mem_resv[0];
mem_resv[0] = 0xAACCEE0011335577ULL;
mem_resv[1] = 0xABCDEF0123456789ULL;
r14 = 0;
r15 = 0;
r16 = base; // fetch from r16 + r17
r17 = 0;
(*func_IN)();
#ifndef __powerpc64__
printf("%s (0x%016llx, 0x%016llx) => (reg_pair = 0x%08x, 0x%08x)\n",
#else
printf("%s (0x%016llx, 0x%016llx) => (reg_pair = 0x%016llx, 0x%016llx)\n",
#endif
name, mem_resv[0], mem_resv[1], r14, r15);
if (verbose) printf("\n");
}
static void test_av_dint_three_args (const char* name, test_func_t func,
unused uint32_t test_flags)
{
unsigned long long * dst;
int i,j, k;
int family = test_flags & PPC_FAMILY;
unsigned long long cin_vals[] = {
// First pair of ULLs have LSB=0, so cin is '0'.
// Second pair of ULLs have LSB=1, so cin is '1'.
0xf000000000000000ULL, 0xf000000000000000ULL,
0xf000000000000000ULL, 0xf000000000000001ULL
};
for (i = 0; i < NB_VDARGS; i+=2) {
if (isLE)
vec_inA = (vector unsigned long long){ vdargs[i+1], vdargs[i] };
else
vec_inA = (vector unsigned long long){ vdargs[i], vdargs[i+1] };
for (j = 0; j < NB_VDARGS; j+=2) {
if (isLE)
vec_inB = (vector unsigned long long){ vdargs[j+1], vdargs[j] };
else
vec_inB = (vector unsigned long long){ vdargs[j], vdargs[j+1] };
for (k = 0; k < 4; k+=2) {
if (family == PPC_ALTIVECQ) {
if (isLE)
vec_inC = (vector unsigned long long){ cin_vals[k+1], cin_vals[k] };
else
vec_inC = (vector unsigned long long){ cin_vals[k], cin_vals[k+1] };
} else {
if (isLE)
vec_inC = (vector unsigned long long){ vdargs[k+1], vdargs[k] };
else
vec_inC = (vector unsigned long long){ vdargs[k], vdargs[k+1] };
}
vec_out = (vector unsigned long long){ 0,0 };
(*func)();
dst = (unsigned long long*)&vec_out;
printf("%s: ", name);
if (family == PPC_ALTIVECQ) {
if (isLE)
printf("%016llx%016llx @@ %016llx%016llx @@ %llx ==> %016llx%016llx\n",
vdargs[i], vdargs[i+1], vdargs[j], vdargs[j+1], cin_vals[k+1],
dst[1], dst[0]);
else
printf("%016llx%016llx @@ %016llx%016llx @@ %llx ==> %016llx%016llx\n",
vdargs[i], vdargs[i+1], vdargs[j], vdargs[j+1], cin_vals[k+1],
dst[0], dst[1]);
} else {
printf("%016llx @@ %016llx @@ %016llx ", vdargs[i], vdargs[j], vdargs[k]);
if (isLE)
printf(" ==> %016llx\n", dst[1]);
else
printf(" ==> %016llx\n", dst[0]);
printf("\t%016llx @@ %016llx @@ %016llx ", vdargs[i+1], vdargs[j+1], vdargs[k+1]);
if (isLE)
printf(" ==> %016llx\n", dst[0]);
else
printf(" ==> %016llx\n", dst[1]);
}
}
}
}
}
/* The ALTIVEC_LOOPS and altive_loops defined below are used in do_tests.
* Add new values to the end; do not change order, since the altivec_loops
* array is indexed using the enumerated values defined by ALTIVEC_LOOPS.
*/
enum ALTIVEC_LOOPS {
ALTV_MOV,
ALTV_DINT,
ALTV_INT_DRES,
ALTV_DINT_IRES,
ALTV_ONE_INT_DRES,
ALTV_DINT_THREE_ARGS,
ALTV_DINT_ONE_ARG,
ALTV_SHA,
ATLV_BCD
};
static test_loop_t altivec_loops[] = {
&test_move_special,
&test_av_dint_two_args,
&test_av_wint_two_args_dres,
&test_av_dint_to_int_two_args,
&test_av_wint_one_arg_dres,
&test_av_dint_three_args,
&test_av_dint_one_arg,
&test_av_dint_one_arg_SHA,
&test_av_bcd,
NULL
};
/* Used in do_tests, indexed by flags->nb_args
Elements correspond to enum test_flags::num args
*/
static test_loop_t int_loops[] = {
/* The #defines for the family, number registers need the array
* to be properly indexed. This test is for the new ISA 2.0.7
* instructions. The infrastructure has been left for the momemnt
*/
NULL, //&test_int_one_arg,
NULL, //&test_int_two_args,
NULL, //&test_int_three_args,
NULL, //&test_int_two_args,
NULL, //&test_int_one_reg_imm16,
NULL, //&test_int_one_reg_imm16,
NULL, //&test_int_special,
NULL, //&test_int_ld_one_reg_imm16,
NULL, //&test_int_ld_two_regs,
NULL, //&test_int_st_two_regs_imm16,
NULL, //&test_int_st_three_regs,
&test_int_stq_two_regs_imm16,
&test_int_ldq_two_regs_imm16,
&test_int_stq_three_regs,
&test_int_ldq_three_regs,
};
/* Used in do_tests, indexed by flags->nb_args
Elements correspond to enum test_flags::num args
Must have NULL for last entry.
*/
static test_loop_t float_loops[] = {
NULL,
&test_float_two_args,
};
static test_t tests_fa_ops_two[] = {
{ &test_fmrgew , "fmrgew", },
{ &test_fmrgow , "fmrgow", },
{ NULL, NULL, },
};
static test_table_t all_tests[] = {
{
tests_move_ops_spe,
"PPC VSR special move insns",
PPC_ALTIVECD | PPC_MOV | PPC_ONE_ARG,
},
{
tests_aa_dbl_ops_two_args,
"PPC altivec double word integer insns (arith, compare) with two args",
PPC_ALTIVECD | PPC_ARITH | PPC_TWO_ARGS,
},
{
tests_aa_word_ops_two_args_dres,
"PPC altivec integer word instructions with two input args, double word result",
PPC_ALTIVEC | PPC_ARITH_DRES | PPC_TWO_ARGS,
},
{
tests_aa_dbl_to_int_two_args,
"PPC altivec doubleword-to-integer instructions with two input args, saturated integer result",
PPC_ALTIVECD | PPC_DOUBLE_IN_IRES | PPC_TWO_ARGS,
},
{
tests_aa_word_ops_one_arg_dres,
"PPC altivec integer word instructions with one input arg, double word result",
PPC_ALTIVEC | PPC_ARITH_DRES | PPC_ONE_ARG,
},
{
tests_istq_ops_two_i16,
"PPC store quadword insns\n with one register + one 16 bits immediate args with flags update",
0x0001050c,
},
{
tests_ildq_ops_two_i16,
"PPC load quadword insns\n with one register + one 16 bits immediate args with flags update",
0x0001050d,
},
{
tests_ldq_ops_three,
"PPC load quadword insns\n with three register args",
0x0001050f,
},
{
tests_stq_ops_three,
"PPC store quadword insns\n with three register args",
0x0001050e,
},
{
tests_fa_ops_two,
"PPC floating point arith insns with two args",
0x00020102,
},
{
tests_aa_ops_three ,
"PPC altivec integer logical insns with three args",
0x00060203,
},
{
tests_aa_dbl_ops_one_arg,
"PPC altivec one vector input arg, hex result",
0x00060201,
},
{
tests_aa_SHA_ops,
"PPC altivec SSH insns",
0x00040B01,
},
{
tests_aa_bcd_ops,
"PPC altivec BCD insns",
0x00040B02,
},
{
tests_aa_quadword_two_args,
"PPC altivec quadword insns, two input args",
0x00070102,
},
{
tests_aa_quadword_three_args,
"PPC altivec quadword insns, three input args",
0x00070103
},
{ NULL, NULL, 0x00000000, },
};
static void do_tests ( insn_sel_flags_t seln_flags,
char *filter)
{
test_loop_t *loop;
test_t *tests;
int nb_args, type, family;
int i, j, n;
int exact;
exact = check_filter(filter);
n = 0;
for (i=0; all_tests[i].name != NULL; i++) {
nb_args = all_tests[i].flags & PPC_NB_ARGS;
/* Check number of arguments */
if ((nb_args == 1 && !seln_flags.one_arg) ||
(nb_args == 2 && !seln_flags.two_args) ||
(nb_args == 3 && !seln_flags.three_args)){
continue;
}
/* Check instruction type */
type = all_tests[i].flags & PPC_TYPE;
if ((type == PPC_ARITH && !seln_flags.arith) ||
(type == PPC_LOGICAL && !seln_flags.logical) ||
(type == PPC_COMPARE && !seln_flags.compare) ||
(type == PPC_LDST && !seln_flags.ldst) ||
(type == PPC_MOV && !seln_flags.ldst) ||
(type == PPC_POPCNT && !seln_flags.arith)) {
continue;
}
/* Check instruction family */
family = all_tests[i].flags & PPC_FAMILY;
if ((family == PPC_INTEGER && !seln_flags.integer) ||
(family == PPC_FLOAT && !seln_flags.floats) ||
(family == PPC_ALTIVEC && !seln_flags.altivec) ||
(family == PPC_ALTIVECD && !seln_flags.altivec) ||
(family == PPC_ALTIVECQ && !seln_flags.altivec) ||
(family == PPC_FALTIVEC && !seln_flags.faltivec)) {
continue;
}
/* Check flags update */
if (((all_tests[i].flags & PPC_CR) && seln_flags.cr == 0) ||
(!(all_tests[i].flags & PPC_CR) && seln_flags.cr == 1))
continue;
/* All passed, do the tests */
tests = all_tests[i].tests;
loop = NULL;
/* Select the test loop */
switch (family) {
case PPC_INTEGER:
mem_resv = memalign16(2 * sizeof(HWord_t)); // want 128-bits
loop = &int_loops[nb_args - 1];
break;
case PPC_FLOAT:
loop = &float_loops[nb_args - 1];
break;
case PPC_ALTIVECQ:
if (nb_args == 2)
loop = &altivec_loops[ALTV_DINT];
else if (nb_args == 3)
loop = &altivec_loops[ALTV_DINT_THREE_ARGS];
break;
case PPC_ALTIVECD:
switch (type) {
case PPC_MOV:
loop = &altivec_loops[ALTV_MOV];
break;
case PPC_ARITH:
loop = &altivec_loops[ALTV_DINT];
break;
case PPC_DOUBLE_IN_IRES:
loop = &altivec_loops[ALTV_DINT_IRES];
break;
case PPC_LOGICAL:
if (nb_args == 3)
loop = &altivec_loops[ALTV_DINT_THREE_ARGS];
else if (nb_args ==1)
loop = &altivec_loops[ALTV_DINT_ONE_ARG];
break;
default:
printf("No altivec test defined for type %x\n", type);
}
break;
case PPC_FALTIVEC:
printf("Currently there are no floating altivec tests in this testsuite.\n");
break;
case PPC_ALTIVEC:
switch (type) {
case PPC_ARITH_DRES:
{
switch (nb_args) {
case 1:
loop = &altivec_loops[ALTV_ONE_INT_DRES];
break;
case 2:
loop = &altivec_loops[ALTV_INT_DRES];
break;
default:
printf("No altivec test defined for number args %d\n", nb_args);
}
break;
}
case PPC_SHA_OR_BCD:
if (nb_args == 1)
loop = &altivec_loops[ALTV_SHA];
else
loop = &altivec_loops[ATLV_BCD];
break;
default:
printf("No altivec test defined for type %x\n", type);
}
break;
default:
printf("ERROR: unknown insn family %08x\n", family);
continue;
}
if (1 || verbose > 0)
for (j=0; tests[j].name != NULL; j++) {
if (check_name(tests[j].name, filter, exact)) {
if (verbose > 1)
printf("Test instruction %s\n", tests[j].name);
if (loop != NULL)
(*loop)(tests[j].name, tests[j].func, all_tests[i].flags);
printf("\n");
n++;
}
}
if (verbose) printf("\n");
}
printf("All done. Tested %d different instructions\n", n);
}
static void usage (void)
{
fprintf(stderr,
"Usage: jm-insns [OPTION]\n"
"\t-i: test integer instructions (default)\n"
"\t-f: test floating point instructions\n"
"\t-a: test altivec instructions\n"
"\t-A: test all (int, fp, altivec) instructions\n"
"\t-v: be verbose\n"
"\t-h: display this help and exit\n"
);
}
#endif
int main (int argc, char **argv)
{
#ifdef HAS_ISA_2_07
/* Simple usage:
./jm-insns -i => int insns
./jm-insns -f => fp insns
./jm-insns -a => av insns
./jm-insns -A => int, fp and avinsns
*/
char *filter = NULL;
insn_sel_flags_t flags;
int c;
// Args
flags.one_arg = 1;
flags.two_args = 1;
flags.three_args = 1;
// Type
flags.arith = 1;
flags.logical = 1;
flags.compare = 1;
flags.ldst = 1;
// Family
flags.integer = 0;
flags.floats = 0;
flags.altivec = 0;
flags.faltivec = 0;
// Flags
flags.cr = 2;
while ((c = getopt(argc, argv, "ifahvA")) != -1) {
switch (c) {
case 'i':
flags.integer = 1;
break;
case 'f':
build_fargs_table();
flags.floats = 1;
break;
case 'a':
flags.altivec = 1;
flags.faltivec = 1;
break;
case 'A':
flags.integer = 1;
flags.floats = 1;
flags.altivec = 1;
flags.faltivec = 1;
break;
case 'h':
usage();
return 0;
case 'v':
verbose++;
break;
default:
usage();
fprintf(stderr, "Unknown argument: '%c'\n", c);
return 1;
}
}
arg_list_size = 0;
build_vargs_table();
if (verbose > 1) {
printf("\nInstruction Selection:\n");
printf(" n_args: \n");
printf(" one_arg = %d\n", flags.one_arg);
printf(" two_args = %d\n", flags.two_args);
printf(" three_args = %d\n", flags.three_args);
printf(" type: \n");
printf(" arith = %d\n", flags.arith);
printf(" logical = %d\n", flags.logical);
printf(" compare = %d\n", flags.compare);
printf(" ldst = %d\n", flags.ldst);
printf(" family: \n");
printf(" integer = %d\n", flags.integer);
printf(" floats = %d\n", flags.floats);
printf(" altivec = %d\n", flags.altivec);
printf(" faltivec = %d\n", flags.faltivec);
printf(" cr update: \n");
printf(" cr = %d\n", flags.cr);
printf("\n");
}
do_tests( flags, filter );
#else
printf("NO ISA 2.07 SUPPORT\n");
#endif
return 0;
}