/* 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; }