macemu/BasiliskII/src/uae_cpu/compiler/test_codegen_x86.cpp

1009 lines
23 KiB
C++

/******************** -*- mode: C; tab-width: 8 -*- ********************
*
* Dumb and Brute Force Run-time assembler verifier for IA-32 and AMD64
*
***********************************************************************/
/***********************************************************************
*
* Copyright 2004 Gwenole Beauchesne
*
* 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
*
***********************************************************************/
/*
* STATUS: 5.5M variations covering unary register based operations,
* reg/reg operations, imm/reg operations.
*
* TODO:
* - Rewrite to use internal BFD/opcodes format instead of string compares
* - Add reg/mem, imm/mem variations
*/
#define _BSD_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "sysdeps.h"
#undef abort
#define abort() do { \
fprintf(stderr, "ABORT: %s, line %d\n", __FILE__, __LINE__); \
(abort)(); \
} while (0)
#define X86_TARGET_64BIT 1
#define X86_FLAT_REGISTERS 0
#define X86_OPTIMIZE_ALU 1
#define X86_OPTIMIZE_ROTSHI 1
#include "compiler/codegen_x86.h"
#define x86_emit_byte(B) emit_byte(B)
#define x86_emit_word(W) emit_word(W)
#define x86_emit_long(L) emit_long(L)
#define x86_emit_quad(Q) emit_quad(Q)
#define x86_get_target() get_target()
#define x86_emit_failure(MSG) jit_fail(MSG, __FILE__, __LINE__, __FUNCTION__)
static void jit_fail(const char *msg, const char *file, int line, const char *function)
{
fprintf(stderr, "JIT failure in function %s from file %s at line %d: %s\n",
function, file, line, msg);
abort();
}
static uint8 *target;
static inline void emit_byte(uint8 x)
{
*target++ = x;
}
static inline void emit_word(uint16 x)
{
*((uint16 *)target) = x;
target += 2;
}
static inline void emit_long(uint32 x)
{
*((uint32 *)target) = x;
target += 4;
}
static inline void emit_quad(uint64 x)
{
*((uint64 *)target) = x;
target += 8;
}
static inline void set_target(uint8 *t)
{
target = t;
}
static inline uint8 *get_target(void)
{
return target;
}
static uint32 mon_read_byte(uintptr addr)
{
uint8 *m = (uint8 *)addr;
return (uint32)(*m);
}
extern "C" {
#include "disass/dis-asm.h"
int buffer_read_memory(bfd_vma from, bfd_byte *to, unsigned int length, struct disassemble_info *info)
{
while (length--)
*to++ = mon_read_byte(from++);
return 0;
}
void perror_memory(int status, bfd_vma memaddr, struct disassemble_info *info)
{
info->fprintf_func(info->stream, "Unknown error %d\n", status);
}
void generic_print_address(bfd_vma addr, struct disassemble_info *info)
{
if (addr >= UVAL64(0x100000000))
info->fprintf_func(info->stream, "$%08x%08x", (uint32)(addr >> 32), (uint32)addr);
else
info->fprintf_func(info->stream, "$%08x", (uint32)addr);
}
int generic_symbol_at_address(bfd_vma addr, struct disassemble_info *info)
{
return 0;
}
}
struct SFILE {
char *buffer;
char *current;
};
static int mon_sprintf(SFILE *f, const char *format, ...)
{
int n;
va_list args;
va_start(args, format);
vsprintf(f->current, format, args);
f->current += n = strlen(f->current);
va_end(args);
return n;
}
static int disass_x86(char *buf, uintptr adr)
{
disassemble_info info;
SFILE sfile;
sfile.buffer = buf;
sfile.current = buf;
INIT_DISASSEMBLE_INFO(info, (FILE *)&sfile, (fprintf_ftype)mon_sprintf);
info.mach = bfd_mach_x86_64;
info.disassembler_options = "suffix";
return print_insn_i386(adr, &info);
}
enum {
op_disp,
op_reg,
op_base,
op_index,
op_scale,
op_imm,
};
struct operand_t {
int32 disp;
int8 reg;
int8 base;
int8 index;
int8 scale;
int64 imm;
void clear() {
disp = imm = 0;
reg = base = index = -1;
scale = 1;
}
void fill(int optype, int value) {
switch (optype) {
case op_disp: disp = value; break;
case op_reg: reg = value; break;
case op_base: base = value; break;
case op_index: index = value; break;
case op_scale: scale = value; break;
case op_imm: imm = value; break;
default: abort();
}
}
};
struct insn_t {
char name[16];
int n_operands;
#define MAX_OPERANDS 3
operand_t operands[MAX_OPERANDS];
void clear() {
memset(name, 0, sizeof(name));
n_operands = 0;
for (int i = 0; i < MAX_OPERANDS; i++)
operands[i].clear();
}
void pretty_print() {
printf("%s, %d operands\n", name, n_operands);
for (int i = 0; i < n_operands; i++) {
operand_t *op = &operands[i];
if (op->reg != -1)
printf(" reg r%d\n", op->reg);
else {
printf(" mem 0x%08x(", op->disp);
if (op->base != -1)
printf("r%d", op->base);
printf(",");
if (op->index != -1)
printf("r%d", op->index);
printf(",");
if (op->base != -1 || op->index != -1)
printf("%d", op->scale);
printf(")\n");
}
}
}
};
static const struct {
const char *name;
int reg;
}
regnames[] = {
#define _(REG) { #REG, X86_##REG }
_(AL), _(CL), _(DL), _(BL),
_(AH), _(CH), _(DH), _(BH),
_(SPL), _(BPL), _(SIL), _(DIL),
_(R8B), _(R9B), _(R10B), _(R11B), _(R12B), _(R13B), _(R14B), _(R15B),
_(AX), _(CX), _(DX), _(BX), _(SP), _(BP), _(SI), _(DI),
_(R8W), _(R9W), _(R10W), _(R11W), _(R12W), _(R13W), _(R14W), _(R15W),
_(EAX), _(ECX), _(EDX), _(EBX), _(ESP), _(EBP), _(ESI), _(EDI),
_(R8D), _(R9D), _(R10D), _(R11D), _(R12D), _(R13D), _(R14D), _(R15D),
_(RAX), _(RCX), _(RDX), _(RBX), _(RSP), _(RBP), _(RSI), _(RDI),
_(R8), _(R9), _(R10), _(R11), _(R12), _(R13), _(R14), _(R15),
{ NULL, -1 }
#undef _
};
static int parse_reg(operand_t *op, int optype, char *buf)
{
for (int i = 0; regnames[i].name; i++) {
int len = strlen(regnames[i].name);
if (strncasecmp(regnames[i].name, buf, len) == 0) {
op->fill(optype, regnames[i].reg);
return len;
}
}
return 0;
}
static int parse_mem(operand_t *op, char *buf)
{
char *p = buf;
if (strncmp(buf, "0x", 2) == 0) {
unsigned long val = strtoul(buf, &p, 16);
if (val == 0 && errno == EINVAL)
abort();
op->disp = val;
}
if (*p == '(') {
p++;
if (*p == '%') {
p++;
int n = parse_reg(op, op_base, p);
if (n <= 0)
return -3;
p += n;
}
if (*p == ',') {
p++;
if (*p == '%') {
int n = parse_reg(op, op_index, ++p);
if (n <= 0)
return -4;
p += n;
if (*p != ',')
return -5;
p++;
goto do_parse_scale;
}
else if (isdigit(*p)) {
do_parse_scale:
long val = strtol(p, &p, 10);
if (val == 0 && errno == EINVAL)
abort();
op->scale = val;
}
}
if (*p != ')')
return -6;
p++;
}
return p - buf;
}
static void parse_insn(insn_t *ii, char *buf)
{
char *p = buf;
ii->clear();
for (int i = 0; !isspace(*p); i++)
ii->name[i] = *p++;
while (*p && isspace(*p))
p++;
if (*p == '\0')
return;
int n_operands = 0;
int optype = op_reg;
bool done = false;
while (!done) {
int n;
switch (*p) {
case '%':
n = parse_reg(&ii->operands[n_operands], optype, ++p);
if (n <= 0) {
fprintf(stderr, "parse_reg(%s) error %d\n", p, n);
abort();
}
p += n;
break;
case '0': case '(':
n = parse_mem(&ii->operands[n_operands], p);
if (n <= 0) {
fprintf(stderr, "parse_mem(%s) error %d\n", p, n);
abort();
}
p += n;
break;
case '$': {
unsigned long val = strtoul(++p, &p, 16);
if (val == 0 && errno == EINVAL)
abort();
ii->operands[n_operands].imm = val;
break;
}
case '*':
p++;
break;
case ',':
n_operands++;
p++;
break;
case ' ': case '\t':
p++;
break;
case '\0':
done = true;
break;
default:
fprintf(stderr, "parse error> %s\n", p);
abort();
}
}
ii->n_operands = n_operands + 1;
}
static long n_tests, n_failures;
static long n_all_tests, n_all_failures;
static bool check_reg(insn_t *ii, const char *name, int r)
{
if (strcasecmp(ii->name, name) != 0) {
fprintf(stderr, "ERROR: instruction mismatch, expected %s, got %s\n", name, ii->name);
return false;
}
if (ii->n_operands != 1) {
fprintf(stderr, "ERROR: instruction expected 1 operand, got %d\n", ii->n_operands);
return false;
}
int reg = ii->operands[0].reg;
if (reg != r) {
fprintf(stderr, "ERROR: instruction expected r%d as source, got ", r);
if (reg == -1)
fprintf(stderr, "nothing\n");
else
fprintf(stderr, "%d\n", reg);
return false;
}
return true;
}
static bool check_reg_reg(insn_t *ii, const char *name, int s, int d)
{
if (strcasecmp(ii->name, name) != 0) {
fprintf(stderr, "ERROR: instruction mismatch, expected %s, got %s\n", name, ii->name);
return false;
}
if (ii->n_operands != 2) {
fprintf(stderr, "ERROR: instruction expected 2 operands, got %d\n", ii->n_operands);
return false;
}
int srcreg = ii->operands[0].reg;
int dstreg = ii->operands[1].reg;
if (srcreg != s) {
fprintf(stderr, "ERROR: instruction expected r%d as source, got ", s);
if (srcreg == -1)
fprintf(stderr, "nothing\n");
else
fprintf(stderr, "%d\n", srcreg);
return false;
}
if (dstreg != d) {
fprintf(stderr, "ERROR: instruction expected r%d as destination, got ", d);
if (dstreg == -1)
fprintf(stderr, "nothing\n");
else
fprintf(stderr, "%d\n", dstreg);
return false;
}
return true;
}
static bool check_imm_reg(insn_t *ii, const char *name, uint32 v, int d, int mode = -1)
{
if (strcasecmp(ii->name, name) != 0) {
fprintf(stderr, "ERROR: instruction mismatch, expected %s, got %s\n", name, ii->name);
return false;
}
if (ii->n_operands != 2) {
fprintf(stderr, "ERROR: instruction expected 2 operands, got %d\n", ii->n_operands);
return false;
}
uint32 imm = ii->operands[0].imm;
int dstreg = ii->operands[1].reg;
if (mode == -1) {
char suffix = name[strlen(name) - 1];
switch (suffix) {
case 'b': mode = 1; break;
case 'w': mode = 2; break;
case 'l': mode = 4; break;
case 'q': mode = 8; break;
}
}
switch (mode) {
case 1: v &= 0xff; break;
case 2: v &= 0xffff; break;
}
if (imm != v) {
fprintf(stderr, "ERROR: instruction expected 0x%08x as immediate, got ", v);
if (imm == -1)
fprintf(stderr, "nothing\n");
else
fprintf(stderr, "0x%08x\n", imm);
return false;
}
if (dstreg != d) {
fprintf(stderr, "ERROR: instruction expected r%d as destination, got ", d);
if (dstreg == -1)
fprintf(stderr, "nothing\n");
else
fprintf(stderr, "%d\n", dstreg);
return false;
}
return true;
}
static bool check_mem_reg(insn_t *ii, const char *name, uint32 D, int B, int I, int S, int R)
{
if (strcasecmp(ii->name, name) != 0) {
fprintf(stderr, "ERROR: instruction mismatch, expected %s, got %s\n", name, ii->name);
return false;
}
if (ii->n_operands != 2) {
fprintf(stderr, "ERROR: instruction expected 2 operands, got %d\n", ii->n_operands);
return false;
}
operand_t *mem = &ii->operands[0];
operand_t *reg = &ii->operands[1];
uint32 d = mem->disp;
int b = mem->base;
int i = mem->index;
int s = mem->scale;
int r = reg->reg;
if (d != D) {
fprintf(stderr, "ERROR: instruction expected 0x%08x as displacement, got 0x%08x\n", D, d);
return false;
}
if (b != B) {
fprintf(stderr, "ERROR: instruction expected r%d as base, got r%d\n", B, b);
return false;
}
if (i != I) {
fprintf(stderr, "ERROR: instruction expected r%d as index, got r%d\n", I, i);
return false;
}
if (s != S) {
fprintf(stderr, "ERROR: instruction expected %d as scale factor, got %d\n", S, s);
return false;
}
if (r != R) {
fprintf(stderr, "ERROR: instruction expected r%d as reg operand, got r%d\n", R, r);
return false;
}
return true;
}
static int verbose = 2;
int main(void)
{
static char buffer[1024];
#define MAX_INSN_LENGTH 16
#define MAX_INSNS 1024
static uint8 block[MAX_INSNS * MAX_INSN_LENGTH];
static char *insns[MAX_INSNS];
static int modes[MAX_INSNS];
n_all_tests = n_all_failures = 0;
printf("Testing reg forms\n");
n_tests = n_failures = 0;
for (int r = 0; r < 16; r++) {
set_target(block);
uint8 *b = get_target();
int i = 0;
#define GEN(INSN, GENOP) do { \
insns[i++] = INSN; \
GENOP##r(r); \
} while (0)
#define GENA(INSN, GENOP) do { \
GEN(INSN "b", GENOP##B); \
GEN(INSN "w", GENOP##W); \
GEN(INSN "l", GENOP##L); \
GEN(INSN "q", GENOP##Q); \
} while (0)
GENA("not", NOT);
GENA("neg", NEG);
GENA("mul", MUL);
GENA("imul", IMUL);
GENA("div", DIV);
GENA("idiv", IDIV);
GENA("dec", DEC);
GENA("inc", INC);
GEN("callq", CALLs);
GEN("jmpq", JMPs);
GEN("pushl", PUSHQ); // FIXME: disass bug? wrong suffix
GEN("popl", POPQ); // FIXME: disass bug? wrong suffix
GEN("bswap", BSWAPL); // FIXME: disass bug? no suffix
GEN("bswap", BSWAPQ); // FIXME: disass bug? no suffix
GEN("seto", SETO);
GEN("setno", SETNO);
GEN("setb", SETB);
GEN("setae", SETAE);
GEN("sete", SETE);
GEN("setne", SETNE);
GEN("setbe", SETBE);
GEN("seta", SETA);
GEN("sets", SETS);
GEN("setns", SETNS);
GEN("setp", SETP);
GEN("setnp", SETNP);
GEN("setl", SETL);
GEN("setge", SETGE);
GEN("setle", SETLE);
GEN("setg", SETG);
#undef GENA
#undef GEN
int last_insn = i;
uint8 *e = get_target();
uint8 *p = b;
i = 0;
while (p < e) {
int n = disass_x86(buffer, (uintptr)p);
insn_t ii;
parse_insn(&ii, buffer);
if (!check_reg(&ii, insns[i], r)) {
if (verbose > 1)
fprintf(stderr, "%s\n", buffer);
n_failures++;
}
p += n;
i += 1;
n_tests++;
}
if (i != last_insn)
abort();
}
printf(" done %ld/%ld\n", n_tests - n_failures, n_tests);
n_all_tests += n_tests;
n_all_failures += n_failures;
printf("Testing reg,reg forms\n");
n_tests = n_failures = 0;
for (int s = 0; s < 16; s++) {
for (int d = 0; d < 16; d++) {
set_target(block);
uint8 *b = get_target();
int i = 0;
#define GEN(INSN, GENOP) do { \
insns[i++] = INSN; \
GENOP##rr(s, d); \
} while (0)
#define GEN1(INSN, GENOP, OP) do { \
insns[i++] = INSN; \
GENOP##rr(OP, s, d); \
} while (0)
#define GENA(INSN, GENOP) do { \
GEN(INSN "b", GENOP##B); \
GEN(INSN "w", GENOP##W); \
GEN(INSN "l", GENOP##L); \
GEN(INSN "q", GENOP##Q); \
} while (0)
GENA("adc", ADC);
GENA("add", ADD);
GENA("and", AND);
GENA("cmp", CMP);
GENA("or", OR);
GENA("sbb", SBB);
GENA("sub", SUB);
GENA("xor", XOR);
GENA("mov", MOV);
GEN("btw", BTW);
GEN("btl", BTL);
GEN("btq", BTQ);
GEN("btcw", BTCW);
GEN("btcl", BTCL);
GEN("btcq", BTCQ);
GEN("btrw", BTRW);
GEN("btrl", BTRL);
GEN("btrq", BTRQ);
GEN("btsw", BTSW);
GEN("btsl", BTSL);
GEN("btsq", BTSQ);
GEN("imulw", IMULW);
GEN("imull", IMULL);
GEN("imulq", IMULQ);
GEN1("cmove", CMOVW, X86_CC_Z);
GEN1("cmove", CMOVL, X86_CC_Z);
GEN1("cmove", CMOVQ, X86_CC_Z);
GENA("test", TEST);
GENA("cmpxchg", CMPXCHG);
GENA("xadd", XADD);
GENA("xchg", XCHG);
GEN("bsfw", BSFW);
GEN("bsfl", BSFL);
GEN("bsfq", BSFQ);
GEN("bsrw", BSRW);
GEN("bsrl", BSRL);
GEN("bsrq", BSRQ);
GEN("movsbw", MOVSBW);
GEN("movsbl", MOVSBL);
GEN("movsbq", MOVSBQ);
GEN("movzbw", MOVZBW);
GEN("movzbl", MOVZBL);
GEN("movzbq", MOVZBQ);
GEN("movswl", MOVSWL);
GEN("movswq", MOVSWQ);
GEN("movzwl", MOVZWL);
GEN("movzwq", MOVZWQ);
GEN("movslq", MOVSLQ);
#undef GENA
#undef GEN1
#undef GEN
int last_insn = i;
uint8 *e = get_target();
uint8 *p = b;
i = 0;
while (p < e) {
int n = disass_x86(buffer, (uintptr)p);
insn_t ii;
parse_insn(&ii, buffer);
if (!check_reg_reg(&ii, insns[i], s, d)) {
if (verbose > 1)
fprintf(stderr, "%s\n", buffer);
n_failures++;
}
p += n;
i += 1;
n_tests++;
}
if (i != last_insn)
abort();
}
}
printf(" done %ld/%ld\n", n_tests - n_failures, n_tests);
n_all_tests += n_tests;
n_all_failures += n_failures;
printf("Testing cl,reg forms\n");
n_tests = n_failures = 0;
for (int d = 0; d < 16; d++) {
set_target(block);
uint8 *b = get_target();
int i = 0;
#define GEN(INSN, GENOP) do { \
insns[i++] = INSN; \
GENOP##rr(X86_CL, d); \
} while (0)
#define GENA(INSN, GENOP) do { \
GEN(INSN "b", GENOP##B); \
GEN(INSN "w", GENOP##W); \
GEN(INSN "l", GENOP##L); \
GEN(INSN "q", GENOP##Q); \
} while (0)
GENA("rol", ROL);
GENA("ror", ROR);
GENA("rcl", RCL);
GENA("rcr", RCR);
GENA("shl", SHL);
GENA("shr", SHR);
GENA("sar", SAR);
#undef GENA
#undef GEN
int last_insn = i;
uint8 *e = get_target();
uint8 *p = b;
i = 0;
while (p < e) {
int n = disass_x86(buffer, (uintptr)p);
insn_t ii;
parse_insn(&ii, buffer);
if (!check_reg_reg(&ii, insns[i], X86_CL, d)) {
if (verbose > 1)
fprintf(stderr, "%s\n", buffer);
n_failures++;
}
p += n;
i += 1;
n_tests++;
}
if (i != last_insn)
abort();
}
printf(" done %ld/%ld\n", n_tests - n_failures, n_tests);
n_all_tests += n_tests;
n_all_failures += n_failures;
printf("Testing imm,reg forms\n");
static const uint32 imm_table[] = {
0x00000000, 0x00000001, 0x00000002, 0x00000004,
0x00000008, 0x00000010, 0x00000020, 0x00000040,
0x00000080, 0x000000fe, 0x000000ff, 0x00000100,
0x00000101, 0x00000102, 0xfffffffe, 0xffffffff,
0x00000000, 0x10000000, 0x20000000, 0x30000000,
0x40000000, 0x50000000, 0x60000000, 0x70000000,
0x80000000, 0x90000000, 0xa0000000, 0xb0000000,
0xc0000000, 0xd0000000, 0xe0000000, 0xf0000000,
0xfffffffd, 0xfffffffe, 0xffffffff, 0x00000001,
0x00000002, 0x00000003, 0x11111111, 0x22222222,
0x33333333, 0x44444444, 0x55555555, 0x66666666,
0x77777777, 0x88888888, 0x99999999, 0xaaaaaaaa,
0xbbbbbbbb, 0xcccccccc, 0xdddddddd, 0xeeeeeeee,
};
const int n_imm_tab_count = sizeof(imm_table)/sizeof(imm_table[0]);
n_tests = n_failures = 0;
for (int j = 0; j < n_imm_tab_count; j++) {
const uint32 value = imm_table[j];
for (int d = 0; d < 16; d++) {
set_target(block);
uint8 *b = get_target();
int i = 0;
#define GEN(INSN, GENOP) do { \
insns[i] = INSN; \
modes[i] = -1; \
i++; GENOP##ir(value, d); \
} while (0)
#define GENM(INSN, GENOP, MODE) do { \
insns[i] = INSN; \
modes[i] = MODE; \
i++; GENOP##ir(value, d); \
} while (0)
#define GENA(INSN, GENOP) do { \
GEN(INSN "b", GENOP##B); \
GEN(INSN "w", GENOP##W); \
GEN(INSN "l", GENOP##L); \
GEN(INSN "q", GENOP##Q); \
} while (0)
#define GENAM(INSN, GENOP, MODE) do { \
GENM(INSN "b", GENOP##B, MODE); \
GENM(INSN "w", GENOP##W, MODE); \
GENM(INSN "l", GENOP##L, MODE); \
GENM(INSN "q", GENOP##Q, MODE); \
} while (0)
GENA("adc", ADC);
GENA("add", ADD);
GENA("and", AND);
GENA("cmp", CMP);
GENA("or", OR);
GENA("sbb", SBB);
GENA("sub", SUB);
GENA("xor", XOR);
GENA("mov", MOV);
GENM("btw", BTW, 1);
GENM("btl", BTL, 1);
GENM("btq", BTQ, 1);
GENM("btcw", BTCW, 1);
GENM("btcl", BTCL, 1);
GENM("btcq", BTCQ, 1);
GENM("btrw", BTRW, 1);
GENM("btrl", BTRL, 1);
GENM("btrq", BTRQ, 1);
GENM("btsw", BTSW, 1);
GENM("btsl", BTSL, 1);
GENM("btsq", BTSQ, 1);
if (value != 1) {
GENAM("rol", ROL, 1);
GENAM("ror", ROR, 1);
GENAM("rcl", RCL, 1);
GENAM("rcr", RCR, 1);
GENAM("shl", SHL, 1);
GENAM("shr", SHR, 1);
GENAM("sar", SAR, 1);
}
GENA("test", TEST);
#undef GENAM
#undef GENA
#undef GENM
#undef GEN
int last_insn = i;
uint8 *e = get_target();
uint8 *p = b;
i = 0;
while (p < e) {
int n = disass_x86(buffer, (uintptr)p);
insn_t ii;
parse_insn(&ii, buffer);
if (!check_imm_reg(&ii, insns[i], value, d, modes[i])) {
if (verbose > 1)
fprintf(stderr, "%s\n", buffer);
n_failures++;
}
p += n;
i += 1;
n_tests++;
}
if (i != last_insn)
abort();
}
}
printf(" done %ld/%ld\n", n_tests - n_failures, n_tests);
n_all_tests += n_tests;
n_all_failures += n_failures;
printf("Testing mem,reg forms\n");
n_tests = n_failures = 0;
static const uint32 off_table[] = {
0x00000000,
0x00000001,
0x00000040,
0x00000080,
0x000000ff,
0x00000100,
0xfffffffe,
0xffffffff,
};
const int off_table_count = sizeof(off_table) / sizeof(off_table[0]);
for (int d = 0; d < off_table_count; d++) {
const uint32 D = off_table[d];
for (int B = -1; B < 16; B++) {
for (int I = -1; I < 16; I++) {
if (I == X86_RSP)
continue;
for (int S = 1; S < 8; S *= 2) {
if (I == -1)
continue;
for (int r = 0; r < 16; r++) {
set_target(block);
uint8 *b = get_target();
int i = 0;
#define GEN(INSN, GENOP) do { \
insns[i++] = INSN; \
GENOP##mr(D, B, I, S, r); \
} while (0)
#define GENA(INSN, GENOP) do { \
GEN(INSN "b", GENOP##B); \
GEN(INSN "w", GENOP##W); \
GEN(INSN "l", GENOP##L); \
GEN(INSN "q", GENOP##Q); \
} while (0)
GENA("adc", ADC);
GENA("add", ADD);
GENA("and", AND);
GENA("cmp", CMP);
GENA("or", OR);
GENA("sbb", SBB);
GENA("sub", SUB);
GENA("xor", XOR);
GENA("mov", MOV);
GEN("imulw", IMULW);
GEN("imull", IMULL);
GEN("imulq", IMULQ);
GEN("bsfw", BSFW);
GEN("bsfl", BSFL);
GEN("bsfq", BSFQ);
GEN("bsrw", BSRW);
GEN("bsrl", BSRL);
GEN("bsrq", BSRQ);
GEN("movsbw", MOVSBW);
GEN("movsbl", MOVSBL);
GEN("movsbq", MOVSBQ);
GEN("movzbw", MOVZBW);
GEN("movzbl", MOVZBL);
GEN("movzbq", MOVZBQ);
GEN("movswl", MOVSWL);
GEN("movswq", MOVSWQ);
GEN("movzwl", MOVZWL);
GEN("movzwq", MOVZWQ);
GEN("movslq", MOVSLQ);
#undef GENA
#undef GEN
int last_insn = i;
uint8 *e = get_target();
uint8 *p = b;
i = 0;
while (p < e) {
int n = disass_x86(buffer, (uintptr)p);
insn_t ii;
parse_insn(&ii, buffer);
if (!check_mem_reg(&ii, insns[i], D, B, I, S, r)) {
if (verbose > 1)
fprintf(stderr, "%s\n", buffer);
n_failures++;
}
p += n;
i += 1;
n_tests++;
}
if (i != last_insn)
abort();
}
}
}
}
}
printf(" done %ld/%ld\n", n_tests - n_failures, n_tests);
n_all_tests += n_tests;
n_all_failures += n_failures;
printf("\n");
printf("All %ld tests run, %ld failures\n", n_all_tests, n_all_failures);
}