Add AltiVec regression testsuite

This commit is contained in:
gbeauche 2004-02-15 17:16:57 +00:00
parent 8d4108dd3a
commit d92989dc53

View File

@ -19,10 +19,15 @@
*/
#include <vector>
#include <limits>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <netinet/in.h> // ntohl(), htonl()
#include <setjmp.h>
#include <signal.h>
#include <ctype.h>
#include <math.h>
#if EMU_KHEPERIX
#include "sysdeps.h"
@ -85,6 +90,8 @@ typedef uintptr_t uintptr;
#define TEST_LOGICAL 1
#define TEST_COMPARE 1
#define TEST_CR_LOGICAL 1
#define TEST_VMX_LOAD 1
#define TEST_VMX_ARITH 1
// Partial PowerPC runtime assembler from GNU lightning
#define _I(X) ((uint32)(X))
@ -98,16 +105,24 @@ typedef uintptr_t uintptr;
#define _u6(I) _ck_u( 6,I)
#define _u9(I) _ck_u( 9,I)
#define _u10(I) _ck_u(10,I)
#define _u11(I) _ck_u(11,I)
#define _s16(I) _ck_s(16,I)
#define _D( OP,RD,RA, DD ) _I((_u6(OP)<<26)|(_u5(RD)<<21)|(_u5(RA)<<16)| _s16(DD) )
#define _X( OP,RD,RA,RB, XO,RC ) _I((_u6(OP)<<26)|(_u5(RD)<<21)|(_u5(RA)<<16)|( _u5(RB)<<11)| (_u10(XO)<<1)|_u1(RC))
#define _XO( OP,RD,RA,RB,OE,XO,RC ) _I((_u6(OP)<<26)|(_u5(RD)<<21)|(_u5(RA)<<16)|( _u5(RB)<<11)|(_u1(OE)<<10)|( _u9(XO)<<1)|_u1(RC))
#define _M( OP,RS,RA,SH,MB,ME,RC ) _I((_u6(OP)<<26)|(_u5(RS)<<21)|(_u5(RA)<<16)|( _u5(SH)<<11)|(_u5(MB)<< 6)|( _u5(ME)<<1)|_u1(RC))
#define _VX( OP,VD,VA,VB, XO ) _I((_u6(OP)<<26)|(_u5(VD)<<21)|(_u5(VA)<<16)|( _u5(VB)<<11)| _u11(XO) )
#define _VXR( OP,VD,VA,VB, XO,RC ) _I((_u6(OP)<<26)|(_u5(VD)<<21)|(_u5(VA)<<16)|( _u5(VB)<<11)| (_u1(RC)<<10)|_u10(XO))
#define _VA( OP,VD,VA,VB,VC,XO ) _I((_u6(OP)<<26)|(_u5(VD)<<21)|(_u5(VA)<<16)|( _u5(VB)<<11)|(_u5(VC)<< 6)| _u6(XO) )
// PowerPC opcodes
static inline uint32 POWERPC_MR(int RD, int RA) { return _X(31,RA,RD,RA,444,0); }
static inline uint32 POWERPC_MFCR(int RD) { return _X(31,RD,00,00,19,0); }
static inline uint32 POWERPC_LVX(int vD, int rA, int rB) { return _X(31,vD,rA,rB,103,0); }
static inline uint32 POWERPC_STVX(int vS, int rA, int rB) { return _X(31,vS,rA,rB,231,0); }
static inline uint32 POWERPC_MFSPR(int rD, int SPR) { return _X(31,rD,(SPR&0x1f),((SPR>>5)&0x1f),339,0); }
static inline uint32 POWERPC_MTSPR(int rS, int SPR) { return _X(31,rS,(SPR&0x1f),((SPR>>5)&0x1f),467,0); }
const uint32 POWERPC_NOP = 0x60000000;
const uint32 POWERPC_BLR = 0x4e800020;
const uint32 POWERPC_BLRL = 0x4e800021;
@ -174,13 +189,6 @@ void powerpc_cpu_base::execute_return(uint32 opcode)
void powerpc_cpu_base::init_decoder()
{
#ifndef PPC_NO_STATIC_II_INDEX_TABLE
static bool initialized = false;
if (initialized)
return;
initialized = true;
#endif
static const instr_info_t return_ii_table[] = {
{ "return",
(execute_pmf)&powerpc_cpu_base::execute_return,
@ -400,6 +408,14 @@ typedef bit_field< 16, 20 > rB_field;
typedef bit_field< 6, 10 > rD_field;
typedef bit_field< 6, 10 > rS_field;
// Vector registers
typedef bit_field< 11, 15 > vA_field;
typedef bit_field< 16, 20 > vB_field;
typedef bit_field< 21, 25 > vC_field;
typedef bit_field< 6, 10 > vD_field;
typedef bit_field< 6, 10 > vS_field;
typedef bit_field< 22, 25 > vSH_field;
// Condition registers
typedef bit_field< 11, 15 > crbA_field;
typedef bit_field< 16, 20 > crbB_field;
@ -433,6 +449,144 @@ typedef bit_field< 2, 2 > XER_CA_field;
#undef SO
#define SO XER_SO_field::mask()
// Flag: does the host support AltiVec instructions?
static bool has_altivec = true;
// A 128-bit AltiVec register
typedef uint8 vector_t[16] __attribute__((aligned(16)));
union vector_helper_t {
vector_t v;
uint8 b[16];
uint16 h[8];
uint32 w[4];
float f[4];
};
static void print_vector(vector_t const & v, char type = 'b')
{
vector_helper_t x;
memcpy(&x.b, &v, sizeof(vector_t));
printf("{");
switch (type) {
case 'b':
default:
for (int i = 0; i < 16; i++) {
if (i != 0)
printf(",");
printf(" %02x", x.b[i]);
}
break;
case 'h':
for (int i = 0; i < 8; i++) {
if (i != 0)
printf(",");
printf(" %04x", x.h[i]);
}
break;
case 'w':
for (int i = 0; i < 4; i++) {
if (i != 0)
printf(",");
printf(" %08x", x.w[i]);
}
break;
case 'f':
case 'e': // estimate result
case 'l': // estimate log2 result
for (int i = 0; i < 4; i++) {
x.w[i] = ntohl(x.w[i]);
if (i != 0)
printf(",");
printf(" %g", x.f[i]);
}
break;
}
printf(" }");
}
static inline bool do_float_equals(float a, float b, float tolerance)
{
if (a == b)
return true;
if (isnan(a) && isnan(b))
return true;
if (isinf(a) && isinf(b) && signbit(a) == signbit(b))
return true;
if ((b < (a + tolerance)) && (b > (a - tolerance)))
return true;
return false;
}
static inline bool float_equals(float a, float b)
{
return do_float_equals(a, b, 3 * std::numeric_limits<float>::epsilon());
}
static bool vector_equals(char type, vector_t const & a, vector_t const & b)
{
// the vector is in ppc big endian format
float tolerance;
switch (type) {
case 'f':
tolerance = 3 * std::numeric_limits<float>::epsilon();
goto do_compare;
case 'l': // FIXME: this does not handle |x-1|<=1/8 case
tolerance = 1. / 32.;
goto do_compare;
case 'e':
tolerance = 1. / 4096.;
do_compare:
for (int i = 0; i < 4; i++) {
union { float f; uint32 i; } u, v;
u.i = ntohl(((uint32 *)&a)[i]);
v.i = ntohl(((uint32 *)&b)[i]);
if (!do_float_equals(u.f, v.f, tolerance))
return false;
}
return true;
}
return memcmp(&a, &b, sizeof(vector_t)) == 0;
}
static bool vector_all_eq(char type, vector_t const & b)
{
uint32 v;
vector_helper_t x;
memcpy(&x.v, &b, sizeof(vector_t));
bool all_eq = true;
switch (type) {
case 'b':
default:
v = x.b[0];
for (int i = 1; all_eq && i < 16; i++)
if (x.b[i] != v)
all_eq = false;
break;
case 'h':
v = x.h[0];
for (int i = 1; all_eq && i < 8; i++)
if (x.h[i] != v)
all_eq = false;
break;
case 'w':
case 'f':
v = x.w[0];
for (int i = 1; all_eq && i < 4; i++)
if (x.w[i] != v)
all_eq = false;
break;
}
return all_eq;
}
// Define PowerPC tester
class powerpc_test_cpu
: public powerpc_cpu_base
@ -477,8 +631,10 @@ private:
FILE *results_file;
uint32 get32();
void put32(uint32 v);
void get_vector(vector_t & v);
void put_vector(vector_t const & v);
// Initial CR0, XER state
// Initial CR0, XER states
uint32 init_cr;
uint32 init_xer;
@ -490,12 +646,36 @@ private:
enum {
RD = 3,
RA = 4,
RB = 5
RB = 5,
RC = 6,
VSCR = 7,
};
// Operands
enum {
__,
vD, vS, vA, vB, vC, vI, vN,
rD, rS, rA, rB, rC,
};
struct vector_test_t {
uint8 name[14];
char type;
char op_type;
uint32 opcode;
uint8 operands[4];
};
struct vector_value_t {
char type;
vector_t v;
};
static const uint32 reg_values[];
static const uint32 imm_values[];
static const uint32 msk_values[];
static const vector_value_t vector_values[];
static const vector_value_t vector_fp_values[];
void test_one_1(uint32 *code, const char *insn, uint32 a1, uint32 a2, uint32 a3, uint32 a0 = 0);
void test_one(uint32 *code, const char *insn, uint32 a1, uint32 a2, uint32 a3, uint32 a0 = 0);
@ -522,6 +702,12 @@ private:
void test_logical(void);
void test_compare(void);
void test_cr_logical(void);
void test_one_vector(uint32 *code, vector_test_t const & vt, uint8 *rA, uint8 *rB = 0, uint8 *rC = 0);
void test_one_vector(uint32 *code, vector_test_t const & vt, vector_t const *vA = 0, vector_t const *vB = 0, vector_t const *vC = 0)
{ test_one_vector(code, vt, (uint8 *)vA, (uint8 *)vB, (uint8 *)vC); }
void test_vector_load(void);
void test_vector_arith(void);
};
powerpc_test_cpu::powerpc_test_cpu()
@ -558,6 +744,22 @@ void powerpc_test_cpu::put32(uint32 v)
}
}
void powerpc_test_cpu::get_vector(vector_t & v)
{
if (fread(&v, sizeof(v), 1, results_file) != 1) {
fprintf(stderr, "ERROR: unexpected end of results file\n");
exit(EXIT_FAILURE);
}
}
void powerpc_test_cpu::put_vector(vector_t const & v)
{
if (fwrite(&v, sizeof(v), 1, results_file) != 1) {
fprintf(stderr, "could not write vector to results file\n");
exit(EXIT_FAILURE);
}
}
void powerpc_test_cpu::execute(uint32 *code_p)
{
static uint32 code[2];
@ -1265,13 +1467,520 @@ void powerpc_test_cpu::test_cr_logical(void)
#endif
}
// Template-generated vector values
const powerpc_test_cpu::vector_value_t powerpc_test_cpu::vector_values[] = {
{'w',{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
{'w',{0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01}},
{'w',{0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02}},
{'w',{0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03}},
{'w',{0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04}},
{'w',{0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05}},
{'w',{0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06}},
{'w',{0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07}},
{'w',{0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08}},
{'w',{0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10}},
{'w',{0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
{'w',{0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20}},
{'w',{0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28}},
{'w',{0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30}},
{'w',{0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38}},
{'w',{0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40}},
{'w',{0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48}},
{'w',{0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50}},
{'w',{0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58}},
{'w',{0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60}},
{'w',{0x68,0x68,0x68,0x68,0x68,0x68,0x68,0x68,0x68,0x68,0x68,0x68,0x68,0x68,0x68,0x68}},
{'w',{0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70}},
{'w',{0x78,0x78,0x78,0x78,0x78,0x78,0x78,0x78,0x78,0x78,0x78,0x78,0x78,0x78,0x78,0x78}},
{'w',{0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00}},
{'w',{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04}},
{'w',{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10}},
{'w',{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}},
{'w',{0x11,0x11,0x11,0x11,0x22,0x22,0x22,0x22,0x33,0x33,0x33,0x33,0x44,0x44,0x44,0x44}},
{'w',{0x88,0x88,0x88,0x88,0x77,0x77,0x77,0x77,0x66,0x66,0x66,0x66,0x55,0x55,0x55,0x55}},
{'w',{0x99,0x99,0x99,0x99,0xaa,0xaa,0xaa,0xaa,0xbb,0xbb,0xbb,0xbb,0xcc,0xcc,0xcc,0xcc}},
{'w',{0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xee,0xee,0xee,0xee,0xdd,0xdd,0xdd,0xdd}},
{'h',{0x00,0x00,0x11,0x11,0x22,0x22,0x33,0x33,0x44,0x44,0x55,0x55,0x66,0x66,0x77,0x77}},
{'h',{0x00,0x01,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x05,0x00,0x06,0x00,0x07,0x00,0x08}},
{'h',{0x00,0x16,0x00,0x15,0x00,0x14,0x00,0x13,0x00,0x12,0x00,0x10,0x00,0x10,0x00,0x09}},
{'b',{0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff}},
{'b',{0xff,0xee,0xdd,0xcc,0xbb,0xaa,0x99,0x88,0x77,0x66,0x55,0x44,0x33,0x22,0x11,0x00}},
{'b',{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}},
{'b',{0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f}},
{'b',{0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20}}
};
const powerpc_test_cpu::vector_value_t powerpc_test_cpu::vector_fp_values[] = {
{'f',{0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00}}, // -0, -0, -0, -0
{'f',{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, // 0, 0, 0, 0
{'f',{0xbf,0x80,0x00,0x00,0xbf,0x80,0x00,0x00,0xbf,0x80,0x00,0x00,0xbf,0x80,0x00,0x00}}, // -1, -1, -1, -1
{'f',{0x3f,0x80,0x00,0x00,0x3f,0x80,0x00,0x00,0x3f,0x80,0x00,0x00,0x3f,0x80,0x00,0x00}}, // 1, 1, 1, 1
{'f',{0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00}}, // -2, -2, -2, -2
{'f',{0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00}}, // 2, 2, 2, 2
{'f',{0xc0,0x00,0x00,0x00,0xbf,0x80,0x00,0x00,0x3f,0x80,0x00,0x00,0x40,0x00,0x00,0x00}}, // -2, -1, 1, 2
{'f',{0xc0,0x40,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x00,0x00}}, // -3, -0, 0, 3
{'f',{0x40,0x00,0x00,0x00,0x3f,0x80,0x00,0x00,0xbf,0x80,0x00,0x00,0xc0,0x00,0x00,0x00}} // 2, 1, -1, -2
};
void powerpc_test_cpu::test_one_vector(uint32 *code, vector_test_t const & vt, uint8 *rAp, uint8 *rBp, uint8 *rCp)
{
#if TEST_VMX_OPS
static vector_t native_vD;
memset(&native_vD, 0, sizeof(native_vD));
static vector_helper_t native_vSCR;
memset(&native_vSCR, 0, sizeof(native_vSCR));
static vector_t dummy_vector;
if (!rAp) rAp = dummy_vector;
if (!rBp) rBp = dummy_vector;
if (!rCp) rCp = dummy_vector;
#if defined(__powerpc__)
// Invoke native code
const uint32 save_cr = native_get_cr();
native_set_cr(init_cr);
native_vSCR.w[3] = 0;
typedef void (*func_t)(uint8 *, uint8 *, uint8 *, uint8 *, uint8 *);
func_t func = (func_t)code;
func((uint8 *)&native_vD, rAp, rBp, rCp, native_vSCR.b);
const uint32 native_cr = native_get_cr();
const uint32 native_vscr = native_vSCR.w[3];
native_set_cr(save_cr);
if (results_file) {
put_vector(native_vD);
put32(native_cr);
put32(native_vscr);
}
#else
get_vector(native_vD);
const uint32 native_cr = get32();
const uint32 native_vscr = get32();
#endif
// Invoke emulated code
static vector_t emul_vD;
memset(&emul_vD, 0, sizeof(emul_vD));
static vector_helper_t emul_vSCR;
memset(&emul_vSCR, 0, sizeof(emul_vSCR));
emul_vSCR.w[3] = 0;
emul_set_cr(init_cr);
set_gpr(RD, (uintptr)&emul_vD);
set_gpr(RA, (uintptr)rAp);
set_gpr(RB, (uintptr)rBp);
set_gpr(RC, (uintptr)rCp);
set_gpr(VSCR, (uintptr)emul_vSCR.b);
execute(code);
const uint32 emul_cr = emul_get_cr();
const uint32 emul_vscr = ntohl(emul_vSCR.w[3]);
++tests;
bool ok = vector_equals(vt.type, native_vD, emul_vD)
&& native_cr == emul_cr
&& native_vscr == emul_vscr;
if (!ok) {
printf("FAIL: %s [%08x]\n", vt.name, vt.opcode);
errors++;
}
else if (verbose) {
printf("PASS: %s [%08x]\n", vt.name, vt.opcode);
}
if (!ok || verbose) {
#if ENABLE_MON
disass_ppc(stdout, (uintptr)code, vt.opcode);
#endif
char op_type = tolower(vt.op_type);
if (!op_type)
op_type = vt.type;
#define PRINT_OPERAND(N, vX, rX) \
switch (vt.operands[N]) { \
case vX: \
printf(#vX " = "); \
print_vector(*((vector_t *)rX##p)); \
printf("\n"); \
break; \
case vI: \
case vN: \
printf(#vX " = %d\n", vX##_field::extract(vt.opcode)); \
break; \
case rX: \
printf(#rX " = %08x", rX##p); \
if (rX##p) switch (op_type) { \
case 'b': printf(" [%02x]", *rX##p); break; \
case 'h': printf(" [%04x]", *((uint16 *)rX##p)); break; \
case 'w': printf(" [%08x]", *((uint32 *)rX##p)); break; \
} \
printf("\n"); \
break; \
}
PRINT_OPERAND(1, vA, rA);
PRINT_OPERAND(2, vB, rB);
PRINT_OPERAND(3, vC, rC);
#undef PRINT_OPERAND
printf("vD.N = ");
print_vector(native_vD, vt.type);
printf("\n");
printf("vD.E = ");
print_vector(emul_vD, vt.type);
printf("\n");
printf("CR.N = %08x ; VSCR.N = %08x\n", native_cr, native_vscr);
printf("CR.E = %08x ; VSCR.E = %08x\n", emul_cr, emul_vscr);
}
#endif
}
void powerpc_test_cpu::test_vector_load(void)
{
#if TEST_VMX_LOAD
// Tested instructions
static const vector_test_t tests[] = {
{ "lvebx", 'b', 0, _X (31,00,00,00, 7,0), { vD, rA, rB } },
{ "lvehx", 'h', 0, _X (31,00,00,00, 39,0), { vD, rA, rB } },
{ "lvewx", 'w', 0, _X (31,00,00,00, 71,0), { vD, rA, rB } }
};
// Code template
static uint32 code[] = {
POWERPC_MFSPR(12, 256), // mfvrsave r12
_D(15,0,0,0x1000), // lis r0,0x1000 ([v3])
POWERPC_MTSPR(0, 256), // mtvrsave r0
POWERPC_LVX(RD, 0, RD), // lvx v3,r3(0)
0, // <insn>
POWERPC_STVX(RD, 0, RD), // stvx v3,r3(0)
POWERPC_MTSPR(12, 256), // mtvrsave r12
POWERPC_BLR // blr
};
int i_opcode = -1;
const int n_instructions = sizeof(code) / sizeof(code[0]);
for (int i = 0; i < n_instructions; i++) {
if (code[i] == 0) {
i_opcode = i;
break;
}
}
assert(i_opcode != -1);
const int n_elements = sizeof(tests) / sizeof(tests[0]);
for (int i = 0; i < n_elements; i++) {
vector_test_t const & vt = tests[i];
code[i_opcode] = vt.opcode;
vD_field::insert(code[i_opcode], RD);
rA_field::insert(code[i_opcode], 00);
rB_field::insert(code[i_opcode], RA);
flush_icache_range(code, sizeof(code));
printf("Testing %s\n", vt.name);
const int n_vector_values = sizeof(vector_values)/sizeof(vector_values[0]);
for (int j = 0; j < n_vector_values; j++) {
switch (vt.type) {
case 'b':
for (int k = 0; k < 16; k++)
test_one_vector(code, vt, ((uint8 *)&vector_values[j].v) + 1 * k);
break;
case 'h':
for (int k = 0; k < 8; k++)
test_one_vector(code, vt, ((uint8 *)&vector_values[j].v) + 2 * k);
break;
case 'w':
for (int k = 0; k < 4; k++)
test_one_vector(code, vt, ((uint8 *)&vector_values[j].v) + 4 * k);
break;
}
}
}
#endif
}
void powerpc_test_cpu::test_vector_arith(void)
{
#if TEST_VMX_ARITH
// Tested instructions
static const vector_test_t tests[] = {
{ "vaddcuw", 'w', 0 , _VX(04,RD,RA,RB, 384), { vD, vA, vB } },
{ "vaddfp", 'f', 0 , _VX(04,RD,RA,RB, 10), { vD, vA, vB } },
{ "vaddsbs", 'b', 0 , _VX(04,RD,RA,RB, 768), { vD, vA, vB } },
{ "vaddshs", 'h', 0 , _VX(04,RD,RA,RB, 832), { vD, vA, vB } },
{ "vaddsws", 'w', 0 , _VX(04,RD,RA,RB, 896), { vD, vA, vB } },
{ "vaddubm", 'b', 0 , _VX(04,RD,RA,RB, 0), { vD, vA, vB } },
{ "vaddubs", 'b', 0 , _VX(04,RD,RA,RB, 512), { vD, vA, vB } },
{ "vadduhm", 'h', 0 , _VX(04,RD,RA,RB, 64), { vD, vA, vB } },
{ "vadduhs", 'h', 0 , _VX(04,RD,RA,RB, 576), { vD, vA, vB } },
{ "vadduwm", 'w', 0 , _VX(04,RD,RA,RB, 128), { vD, vA, vB } },
{ "vadduws", 'w', 0 , _VX(04,RD,RA,RB, 640), { vD, vA, vB } },
{ "vand", 'w', 0 , _VX(04,RD,RA,RB,1028), { vD, vA, vB } },
{ "vandc", 'w', 0 , _VX(04,RD,RA,RB,1092), { vD, vA, vB } },
{ "vavgsb", 'b', 0 , _VX(04,RD,RA,RB,1282), { vD, vA, vB } },
{ "vavgsh", 'h', 0 , _VX(04,RD,RA,RB,1346), { vD, vA, vB } },
{ "vavgsw", 'w', 0 , _VX(04,RD,RA,RB,1410), { vD, vA, vB } },
{ "vavgub", 'b', 0 , _VX(04,RD,RA,RB,1026), { vD, vA, vB } },
{ "vavguh", 'h', 0 , _VX(04,RD,RA,RB,1090), { vD, vA, vB } },
{ "vavguw", 'w', 0 , _VX(04,RD,RA,RB,1154), { vD, vA, vB } },
{ "vcfsx", 'f', 'w', _VX(04,RD,00,RB, 842), { vD, vI, vB } },
{ "vcfux", 'f', 'w', _VX(04,RD,00,RB, 778), { vD, vI, vB } },
{ "vcmpbfp", 'w', 'f', _VXR(04,RD,RA,RB,966,0), { vD, vA, vB } },
{ "vcmpbfp.", 'w', 'f', _VXR(04,RD,RA,RB,966,1), { vD, vA, vB } },
{ "vcmpeqfp", 'w', 'f', _VXR(04,RD,RA,RB,198,0), { vD, vA, vB } },
{ "vcmpeqfp.", 'w', 'f', _VXR(04,RD,RA,RB,198,1), { vD, vA, vB } },
{ "vcmpequb", 'b', 0 , _VXR(04,RD,RA,RB, 6,0), { vD, vA, vB } },
{ "vcmpequb.", 'b', 0 , _VXR(04,RD,RA,RB, 6,1), { vD, vA, vB } },
{ "vcmpequh", 'h', 0 , _VXR(04,RD,RA,RB, 70,0), { vD, vA, vB } },
{ "vcmpequh.", 'h', 0 , _VXR(04,RD,RA,RB, 70,1), { vD, vA, vB } },
{ "vcmpequw", 'w', 0 , _VXR(04,RD,RA,RB,134,0), { vD, vA, vB } },
{ "vcmpequw.", 'w', 0 , _VXR(04,RD,RA,RB,134,1), { vD, vA, vB } },
{ "vcmpgefp", 'w', 'f', _VXR(04,RD,RA,RB,454,0), { vD, vA, vB } },
{ "vcmpgefp.", 'w', 'f', _VXR(04,RD,RA,RB,454,1), { vD, vA, vB } },
{ "vcmpgtfp", 'w', 'f', _VXR(04,RD,RA,RB,710,0), { vD, vA, vB } },
{ "vcmpgtfp.", 'w', 'f', _VXR(04,RD,RA,RB,710,1), { vD, vA, vB } },
{ "vcmpgtsb", 'b', 0 , _VXR(04,RD,RA,RB,774,0), { vD, vA, vB } },
{ "vcmpgtsb.", 'b', 0 , _VXR(04,RD,RA,RB,774,1), { vD, vA, vB } },
{ "vcmpgtsh", 'h', 0 , _VXR(04,RD,RA,RB,838,0), { vD, vA, vB } },
{ "vcmpgtsh.", 'h', 0 , _VXR(04,RD,RA,RB,838,1), { vD, vA, vB } },
{ "vcmpgtsw", 'w', 0 , _VXR(04,RD,RA,RB,902,0), { vD, vA, vB } },
{ "vcmpgtsw.", 'w', 0 , _VXR(04,RD,RA,RB,902,1), { vD, vA, vB } },
{ "vcmpgtub", 'b', 0 , _VXR(04,RD,RA,RB,518,0), { vD, vA, vB } },
{ "vcmpgtub.", 'b', 0 , _VXR(04,RD,RA,RB,518,1), { vD, vA, vB } },
{ "vcmpgtuh", 'h', 0 , _VXR(04,RD,RA,RB,582,0), { vD, vA, vB } },
{ "vcmpgtuh.", 'h', 0 , _VXR(04,RD,RA,RB,582,1), { vD, vA, vB } },
{ "vcmpgtuw", 'w', 0 , _VXR(04,RD,RA,RB,646,0), { vD, vA, vB } },
{ "vcmpgtuw.", 'w', 0 , _VXR(04,RD,RA,RB,646,1), { vD, vA, vB } },
{ "vctsxs", 'w', 'f', _VX(04,RD,00,RB, 970), { vD, vI, vB } },
{ "vctuxs", 'w', 'f', _VX(04,RD,00,RB, 906), { vD, vI, vB } },
{ "vexptefp", 'f', 0 , _VX(04,RD,00,RB, 394), { vD, __, vB } },
{ "vlogefp", 'l', 'f', _VX(04,RD,00,RB, 458), { vD, __, vB } },
{ "vmaddfp", 'f', 0 , _VA(04,RD,RA,RB,RC,46),{ vD, vA, vB, vC } },
{ "vmaxfp", 'f', 0 , _VX(04,RD,RA,RB,1034), { vD, vA, vB } },
{ "vmaxsb", 'b', 0 , _VX(04,RD,RA,RB, 258), { vD, vA, vB } },
{ "vmaxsh", 'h', 0 , _VX(04,RD,RA,RB, 322), { vD, vA, vB } },
{ "vmaxsw", 'w', 0 , _VX(04,RD,RA,RB, 386), { vD, vA, vB } },
{ "vmaxub", 'b', 0 , _VX(04,RD,RA,RB, 2), { vD, vA, vB } },
{ "vmaxuh", 'h', 0 , _VX(04,RD,RA,RB, 66), { vD, vA, vB } },
{ "vmaxuw", 'w', 0 , _VX(04,RD,RA,RB, 130), { vD, vA, vB } },
{ "vmhaddshs", 'h', 0 , _VA(04,RD,RA,RB,RC,32),{ vD, vA, vB, vC } },
{ "vmhraddshs", 'h', 0 , _VA(04,RD,RA,RB,RC,33),{ vD, vA, vB, vC } },
{ "vminfp", 'f', 0 , _VX(04,RD,RA,RB,1098), { vD, vA, vB } },
{ "vminsb", 'b', 0 , _VX(04,RD,RA,RB, 770), { vD, vA, vB } },
{ "vminsh", 'h', 0 , _VX(04,RD,RA,RB, 834), { vD, vA, vB } },
{ "vminsw", 'w', 0 , _VX(04,RD,RA,RB, 898), { vD, vA, vB } },
{ "vminub", 'b', 0 , _VX(04,RD,RA,RB, 514), { vD, vA, vB } },
{ "vminuh", 'h', 0 , _VX(04,RD,RA,RB, 578), { vD, vA, vB } },
{ "vminuw", 'w', 0 , _VX(04,RD,RA,RB, 642), { vD, vA, vB } },
{ "vmladduhm", 'h', 0 , _VA(04,RD,RA,RB,RC,34),{ vD, vA, vB, vC } },
{ "vmrghb", 'b', 0 , _VX(04,RD,RA,RB, 12), { vD, vA, vB } },
{ "vmrghh", 'h', 0 , _VX(04,RD,RA,RB, 76), { vD, vA, vB } },
{ "vmrghw", 'w', 0 , _VX(04,RD,RA,RB, 140), { vD, vA, vB } },
{ "vmrglb", 'b', 0 , _VX(04,RD,RA,RB, 268), { vD, vA, vB } },
{ "vmrglh", 'h', 0 , _VX(04,RD,RA,RB, 332), { vD, vA, vB } },
{ "vmrglw", 'w', 0 , _VX(04,RD,RA,RB, 396), { vD, vA, vB } },
{ "vmsummbm", 'b', 0 , _VA(04,RD,RA,RB,RC,37),{ vD, vA, vB, vC } },
{ "vmsumshm", 'h', 0 , _VA(04,RD,RA,RB,RC,40),{ vD, vA, vB, vC } },
{ "vmsumshs", 'h', 0 , _VA(04,RD,RA,RB,RC,41),{ vD, vA, vB, vC } },
{ "vmsumubm", 'b', 0 , _VA(04,RD,RA,RB,RC,36),{ vD, vA, vB, vC } },
{ "vmsumuhm", 'h', 0 , _VA(04,RD,RA,RB,RC,38),{ vD, vA, vB, vC } },
{ "vmsumuhs", 'h', 0 , _VA(04,RD,RA,RB,RC,39),{ vD, vA, vB, vC } },
{ "vmulesb", 'b', 0 , _VX(04,RD,RA,RB, 776), { vD, vA, vB } },
{ "vmulesh", 'h', 0 , _VX(04,RD,RA,RB, 840), { vD, vA, vB } },
{ "vmuleub", 'b', 0 , _VX(04,RD,RA,RB, 520), { vD, vA, vB } },
{ "vmuleuh", 'h', 0 , _VX(04,RD,RA,RB, 584), { vD, vA, vB } },
{ "vmulosb", 'b', 0 , _VX(04,RD,RA,RB, 264), { vD, vA, vB } },
{ "vmulosh", 'h', 0 , _VX(04,RD,RA,RB, 328), { vD, vA, vB } },
{ "vmuloub", 'b', 0 , _VX(04,RD,RA,RB, 8), { vD, vA, vB } },
{ "vmulouh", 'h', 0 , _VX(04,RD,RA,RB, 72), { vD, vA, vB } },
{ "vnmsubfp", 'f', 0 , _VA(04,RD,RA,RB,RC,47),{ vD, vA, vB, vC } },
{ "vnor", 'w', 0 , _VX(04,RD,RA,RB,1284), { vD, vA, vB } },
{ "vor", 'w', 0 , _VX(04,RD,RA,RB,1156), { vD, vA, vB } },
{ "vperm", 'b', 0 , _VA(04,RD,RA,RB,RC,43),{ vD, vA, vB, vC } },
{ "vpkpx", 'h', 0 , _VX(04,RD,RA,RB, 782), { vD, vA, vB } },
{ "vpkshss", 'b', 0 , _VX(04,RD,RA,RB, 398), { vD, vA, vB } },
{ "vpkshus", 'b', 0 , _VX(04,RD,RA,RB, 270), { vD, vA, vB } },
{ "vpkswss", 'h', 0 , _VX(04,RD,RA,RB, 462), { vD, vA, vB } },
{ "vpkswus", 'h', 0 , _VX(04,RD,RA,RB, 334), { vD, vA, vB } },
{ "vpkuhum", 'b', 0 , _VX(04,RD,RA,RB, 14), { vD, vA, vB } },
{ "vpkuhus", 'b', 0 , _VX(04,RD,RA,RB, 142), { vD, vA, vB } },
{ "vpkuwum", 'h', 0 , _VX(04,RD,RA,RB, 78), { vD, vA, vB } },
{ "vpkuwus", 'h', 0 , _VX(04,RD,RA,RB, 206), { vD, vA, vB } },
{ "vrefp", 'e', 'f', _VX(04,RD,00,RB, 266), { vD, __, vB } },
{ "vrfim", 'f', 0 , _VX(04,RD,00,RB, 714), { vD, __, vB } },
{ "vrfin", 'f', 0 , _VX(04,RD,00,RB, 522), { vD, __, vB } },
{ "vrfip", 'f', 0 , _VX(04,RD,00,RB, 650), { vD, __, vB } },
{ "vrfiz", 'f', 0 , _VX(04,RD,00,RB, 586), { vD, __, vB } },
{ "vrlb", 'b', 0 , _VX(04,RD,RA,RB, 4), { vD, vA, vB } },
{ "vrlh", 'h', 0 , _VX(04,RD,RA,RB, 68), { vD, vA, vB } },
{ "vrlw", 'w', 0 , _VX(04,RD,RA,RB, 132), { vD, vA, vB } },
{ "vrsqrtefp", 'e', 'f', _VX(04,RD,00,RB, 330), { vD, __, vB } },
{ "vsel", 'b', 0 , _VA(04,RD,RA,RB,RC,42),{ vD, vA, vB, vC } },
{ "vsl", 'b', 'B', _VX(04,RD,RA,RB, 452), { vD, vA, vB } },
{ "vslb", 'b', 0 , _VX(04,RD,RA,RB, 260), { vD, vA, vB } },
{ "vsldoi", 'b', 0 , _VA(04,RD,RA,RB,00,44),{ vD, vA, vB, vI } },
{ "vslh", 'h', 0 , _VX(04,RD,RA,RB, 324), { vD, vA, vB } },
{ "vslo", 'b', 0 , _VX(04,RD,RA,RB,1036), { vD, vA, vB } },
{ "vslw", 'w', 0 , _VX(04,RD,RA,RB, 388), { vD, vA, vB } },
{ "vspltb", 'b', 0 , _VX(04,RD,00,RB, 524), { vD, vI, vB } },
{ "vsplth", 'h', 0 , _VX(04,RD,00,RB, 588), { vD, vI, vB } },
{ "vspltisb", 'b', 0 , _VX(04,RD,00,00, 780), { vD, vI } },
{ "vspltish", 'h', 0 , _VX(04,RD,00,00, 844), { vD, vI } },
{ "vspltisw", 'w', 0 , _VX(04,RD,00,00, 908), { vD, vI } },
{ "vspltw", 'w', 0 , _VX(04,RD,00,RB, 652), { vD, vI, vB } },
{ "vsr", 'b', 'B', _VX(04,RD,RA,RB, 708), { vD, vA, vB } },
{ "vsrab", 'b', 0 , _VX(04,RD,RA,RB, 772), { vD, vA, vB } },
{ "vsrah", 'h', 0 , _VX(04,RD,RA,RB, 836), { vD, vA, vB } },
{ "vsraw", 'w', 0 , _VX(04,RD,RA,RB, 900), { vD, vA, vB } },
{ "vsrb", 'b', 0 , _VX(04,RD,RA,RB, 516), { vD, vA, vB } },
{ "vsrh", 'h', 0 , _VX(04,RD,RA,RB, 580), { vD, vA, vB } },
{ "vsro", 'b', 0 , _VX(04,RD,RA,RB,1100), { vD, vA, vB } },
{ "vsrw", 'w', 0 , _VX(04,RD,RA,RB, 644), { vD, vA, vB } },
{ "vsubcuw", 'w', 0 , _VX(04,RD,RA,RB,1408), { vD, vA, vB } },
{ "vsubfp", 'f', 0 , _VX(04,RD,RA,RB, 74), { vD, vA, vB } },
{ "vsubsbs", 'b', 0 , _VX(04,RD,RA,RB,1792), { vD, vA, vB } },
{ "vsubshs", 'h', 0 , _VX(04,RD,RA,RB,1856), { vD, vA, vB } },
{ "vsubsws", 'w', 0 , _VX(04,RD,RA,RB,1920), { vD, vA, vB } },
{ "vsububm", 'b', 0 , _VX(04,RD,RA,RB,1024), { vD, vA, vB } },
{ "vsububs", 'b', 0 , _VX(04,RD,RA,RB,1536), { vD, vA, vB } },
{ "vsubuhm", 'h', 0 , _VX(04,RD,RA,RB,1088), { vD, vA, vB } },
{ "vsubuhs", 'h', 0 , _VX(04,RD,RA,RB,1600), { vD, vA, vB } },
{ "vsubuwm", 'w', 0 , _VX(04,RD,RA,RB,1152), { vD, vA, vB } },
{ "vsubuws", 'w', 0 , _VX(04,RD,RA,RB,1664), { vD, vA, vB } },
{ "vsum2sws", 'w', 0 , _VX(04,RD,RA,RB,1672), { vD, vA, vB } },
{ "vsum4sbs", 'w', 0 , _VX(04,RD,RA,RB,1800), { vD, vA, vB } },
{ "vsum4shs", 'w', 0 , _VX(04,RD,RA,RB,1608), { vD, vA, vB } },
{ "vsum4ubs", 'w', 0 , _VX(04,RD,RA,RB,1544), { vD, vA, vB } },
{ "vsumsws", 'w', 0 , _VX(04,RD,RA,RB,1928), { vD, vA, vB } },
{ "vupkhpx", 'w', 0 , _VX(04,RD,00,RB, 846), { vD, __, vB } },
{ "vupkhsb", 'h', 0 , _VX(04,RD,00,RB, 526), { vD, __, vB } },
{ "vupkhsh", 'w', 0 , _VX(04,RD,00,RB, 590), { vD, __, vB } },
{ "vupklpx", 'w', 0 , _VX(04,RD,00,RB, 974), { vD, __, vB } },
{ "vupklsb", 'h', 0 , _VX(04,RD,00,RB, 654), { vD, __, vB } },
{ "vupklsh", 'w', 0 , _VX(04,RD,00,RB, 718), { vD, __, vB } },
{ "vxor", 'w', 0 , _VX(04,RD,RA,RB,1220), { vD, vA, vB } },
};
// Code template
static uint32 code[] = {
POWERPC_MFSPR(12, 256), // mfvrsave r12
_D(15,0,0,0x1e00), // lis r0,0x9e00 ([v0;v3-v6])
POWERPC_MTSPR(0, 256), // mtvrsave r0
POWERPC_LVX(RA, 0, RA), // lvx v4,r4(0)
POWERPC_LVX(RB, 0, RB), // lvx v5,r5(0)
POWERPC_LVX(RC, 0, RC), // lvx v6,r6(0)
POWERPC_LVX(0, 0, VSCR), // lvx v0,r7(0)
_VX(04,00,00,00,1604), // mtvscr v0
0, // <op> v3,v4,v5
_VX(04,00,00,00,1540), // mfvscr v0
POWERPC_STVX(0, 0, VSCR), // stvx v0,r7(0)
POWERPC_STVX(RD, 0, RD), // stvx v3,r3(0)
POWERPC_MTSPR(12, 256), // mtvrsave r12
POWERPC_BLR // blr
};
int i_opcode = -1;
const int n_instructions = sizeof(code) / sizeof(code[0]);
for (int i = 0; i < n_instructions; i++) {
if (code[i] == 0) {
i_opcode = i;
break;
}
}
assert(i_opcode != -1);
const int n_elements = sizeof(tests) / sizeof(tests[0]);
for (int n = 0; n < n_elements; n++) {
vector_test_t vt = tests[n];
code[i_opcode] = vt.opcode;
flush_icache_range(code, sizeof(code));
// Operand type
char op_type = vt.op_type;
if (!op_type)
op_type = vt.type;
// Operand values
int n_vector_values;
const vector_value_t *vvp;
if (op_type == 'f') {
n_vector_values = sizeof(vector_fp_values)/sizeof(vector_fp_values[0]);
vvp = vector_fp_values;
}
else {
n_vector_values = sizeof(vector_values)/sizeof(vector_values[0]);
vvp = vector_values;
}
printf("Testing %s\n", vt.name);
if (vt.operands[1] == vA && vt.operands[2] == vB && vt.operands[3] == vC) {
for (int i = 0; i < n_vector_values; i++)
for (int j = 0; j < n_vector_values; j++)
for (int k = 0; k < n_vector_values; k++)
test_one_vector(code, vt, &vvp[i].v, &vvp[j].v, &vvp[k].v);
}
else if (vt.operands[1] == vA && vt.operands[2] == vB && vt.operands[3] == vN) {
for (int i = 0; i < 16; i++) {
vSH_field::insert(vt.opcode, i);
code[i_opcode] = vt.opcode;
flush_icache_range(code, sizeof(code));
for (int j = 0; j < n_vector_values; j++)
for (int k = 0; k < n_vector_values; k++)
test_one_vector(code, vt, &vvp[i].v, &vvp[j].v);
}
}
else if (vt.operands[1] == vA && vt.operands[2] == vB) {
for (int i = 0; i < n_vector_values; i++) {
for (int j = 0; j < n_vector_values; j++) {
if (op_type == 'B') {
if (!vector_all_eq('b', vvp[j].v))
continue;
}
test_one_vector(code, vt, &vvp[i].v, &vvp[j].v);
}
}
}
else if (vt.operands[1] == vI && vt.operands[2] == vB) {
for (int i = 0; i < 32; i++) {
rA_field::insert(vt.opcode, i);
code[i_opcode] = vt.opcode;
flush_icache_range(code, sizeof(code));
for (int j = 0; j < n_vector_values; j++)
test_one_vector(code, vt, NULL, &vvp[j].v);
}
}
else if (vt.operands[1] == vI) {
for (int i = 0; i < 32; i++) {
rA_field::insert(vt.opcode, i);
code[i_opcode] = vt.opcode;
flush_icache_range(code, sizeof(code));
test_one_vector(code, vt);
}
}
else if (vt.operands[1] == __ && vt.operands[2] == vB) {
for (int i = 0; i < n_vector_values; i++)
test_one_vector(code, vt, NULL, &vvp[i].v);
}
else {
printf("ERROR: unhandled test case\n");
abort();
}
}
#endif
}
// Illegal handler to catch out AltiVec instruction
#if defined(__powerpc__)
static sigjmp_buf env;
static void sigill_handler(int sig)
{
has_altivec = false;
siglongjmp(env, 1);
}
#endif
bool powerpc_test_cpu::test(void)
{
// Tests initialization
tests = errors = 0;
init_cr = init_xer = 0;
// Execution tests
// Execution ALU tests
#if TEST_ALU_OPS
test_add();
test_sub();
test_mul();
@ -1281,6 +1990,15 @@ bool powerpc_test_cpu::test(void)
test_logical();
test_compare();
test_cr_logical();
#endif
// Execute VMX tests
#if TEST_VMX_OPS
if (has_altivec) {
test_vector_load();
test_vector_arith();
}
#endif
printf("%u errors out of %u tests\n", errors, tests);
return errors != 0;
@ -1319,6 +2037,15 @@ int main(int argc, char *argv[])
}
#endif
// Check if host CPU supports AltiVec instructions
has_altivec = true;
#if defined(__powerpc__)
signal(SIGILL, sigill_handler);
if (!sigsetjmp(env, 1))
asm volatile(".long 0x10000484"); // vor v0,v0,v0
signal(SIGILL, SIG_DFL);
#endif
int ret = ppc.test();
if (fp) fclose(fp);
return !ret;