Created
May 28, 2019 00:41
-
-
Save tom-seddon/3cd4f739ea8e88e45f4e3888c4101ef0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -*- mode:c; c-file-style: "GNU" -*- | |
/* This file is to be #include'd into any file that uses it. | |
* Everything's static, so no problem. | |
*/ | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* TODO | |
* ==== | |
* | |
* . I think the BASIC_INSTR vs MEM_DEST_INSTR thing is stupid (and | |
* LEAQ could be merged into the same list...) but I'm not doing | |
* anything about it just yet. | |
* | |
* . <<probably more stuff>>> | |
*/ | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* clang is good at folding the constants and following through, but | |
* it's foiled by memcpy - which is why constants are written out by | |
* hand, e.g., in gen_lit32 or gen_litn. | |
* | |
* Without memcpy: | |
* | |
* movaps LCPI0_0(%rip), %xmm0 ## xmm0 = [72,129,192,120,86,52,18,72,1,134,0,5,0,0,72,1] | |
* movups %xmm0, (%r14) | |
* | |
* With memcpy: | |
* | |
* movb $72, (%rbx) | |
* movb $-127, 1(%rbx) | |
* movb $-64, 2(%rbx) | |
* movl $305419896, 3(%rbx) ## imm = 0x12345678 [this is the memcpy] | |
* movb $72, 7(%rbx) | |
* | |
* Probably the opposite tradeoff for variables of course. | |
* | |
* __builtin_constant_p is presumably there to fix this, but it didn't | |
* improve things when I tried using it. | |
*/ | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* As a general policy, each register is accessible only as qword, | |
* dword, word and (low) byte. Instructions that access the high bytes | |
* of [ABCD]X aren't accessible. | |
* | |
* This mostly makes things simpler, but there are a couple of | |
* exceptions. | |
*/ | |
union X64Register | |
{ | |
struct | |
{ | |
uint8_t r : 3; | |
uint8_t rex : 1; | |
}; | |
uint8_t reg_all; | |
}; | |
typedef union X64Register X64Register; | |
static ATTRIBUTE_UNUSED const X64Register RAX = {.r = 0, .rex = 0}; | |
static ATTRIBUTE_UNUSED const X64Register RCX = {.r = 1, .rex = 0}; | |
static ATTRIBUTE_UNUSED const X64Register RDX = {.r = 2, .rex = 0}; | |
static ATTRIBUTE_UNUSED const X64Register RBX = {.r = 3, .rex = 0}; | |
static ATTRIBUTE_UNUSED const X64Register RSP = {.r = 4, .rex = 0}; | |
static ATTRIBUTE_UNUSED const X64Register RBP = {.r = 5, .rex = 0}; | |
static ATTRIBUTE_UNUSED const X64Register RSI = {.r = 6, .rex = 0}; | |
static ATTRIBUTE_UNUSED const X64Register RDI = {.r = 7, .rex = 0}; | |
static ATTRIBUTE_UNUSED const X64Register R8 = {.r = 0, .rex = 1}; | |
static ATTRIBUTE_UNUSED const X64Register R9 = {.r = 1, .rex = 1}; | |
static ATTRIBUTE_UNUSED const X64Register R10 = {.r = 2, .rex = 1}; | |
static ATTRIBUTE_UNUSED const X64Register R11 = {.r = 3, .rex = 1}; | |
static ATTRIBUTE_UNUSED const X64Register R12 = {.r = 4, .rex = 1}; | |
static ATTRIBUTE_UNUSED const X64Register R13 = {.r = 5, .rex = 1}; | |
static ATTRIBUTE_UNUSED const X64Register R14 = {.r = 6, .rex = 1}; | |
static ATTRIBUTE_UNUSED const X64Register R15 = {.r = 7, .rex = 1}; | |
static const X64Register *const REGISTERS[] = | |
{ | |
&RAX, &RCX, &RDX, &RBX, | |
&RSP, &RBP, &RSI, &RDI, | |
&R8, &R9, &R10, &R11, | |
&R12, &R13, &R14, &R15, | |
NULL, | |
}; | |
static const char *const REGISTER_QNAMES[] = | |
{ | |
"rax", "rcx", "rdx", "rbx", | |
"rsp", "rbp", "rsi", "rdi", | |
"r8", "r9", "r10", "r11", | |
"r12", "r13", "r14", "r15", | |
}; | |
static const char *const REGISTER_DNAMES[] = | |
{ | |
"eax", "ecx", "edx", "ebx", | |
"esp", "ebp", "esi", "edi", | |
"r8d", "r9d", "r10d", "r11d", | |
"r12d", "r13d", "r14d", "r15d", | |
}; | |
static const char *const REGISTER_WNAMES[] = | |
{ | |
"ax", "cx", "dx", "bx", | |
"sp", "bp", "si", "di", | |
"r8w", "r9w", "r10w", "r11w", | |
"r12w", "r13w", "r14w", "r15w", | |
}; | |
static const char *const REGISTER_BNAMES[] = | |
{ | |
"al", "cl", "dl", "bl", | |
"spl", "bpl", "sil", "dil", | |
"r8b", "r9b", "r10b", "r11b", | |
"r12b", "r13b", "r14b", "r15b", | |
}; | |
static const char *const *REGISTER_NAMES[] = | |
{ | |
REGISTER_BNAMES, | |
REGISTER_WNAMES, | |
REGISTER_DNAMES, | |
REGISTER_QNAMES, | |
}; | |
static const char * | |
get_register_name (int i, uint8_t op_size) | |
{ | |
switch (op_size) | |
{ | |
default: | |
assert (0); | |
/* fall through */ | |
case 0: | |
return REGISTER_BNAMES[i]; | |
case 1: | |
return REGISTER_WNAMES[i]; | |
case 2: | |
return REGISTER_DNAMES[i]; | |
case 3: | |
return REGISTER_QNAMES[i]; | |
} | |
} | |
static const char * | |
get_size_keyword (uint8_t op_size) | |
{ | |
switch (op_size) | |
{ | |
default: | |
assert (0); | |
/* fall through */ | |
case 0: | |
return "byte"; | |
case 1: | |
return "word"; | |
case 2: | |
return "dword"; | |
case 3: | |
return "qword"; | |
} | |
} | |
/* static const char *get_register_name (int size, X64Register reg) */ | |
/* { */ | |
/* for (const X64Register *const *p = REGISTERS; *p; ++p) */ | |
/* { */ | |
/* if ((*p)->reg_all == reg.reg_all) */ | |
/* { */ | |
/* ptrdiff_t i = p - REGISTERS; */ | |
/* switch (size) */ | |
/* { */ | |
/* case 1: */ | |
/* return REGISTER_BNAMES[i]; */ | |
/* case 2: */ | |
/* return REGISTER_WNAMES[i]; */ | |
/* case 4: */ | |
/* return REGISTER_DNAMES[i]; */ | |
/* case 8: */ | |
/* return REGISTER_QNAMES[i]; */ | |
/* default: */ | |
/* assert (0); */ | |
/* break; */ | |
/* } */ | |
/* } */ | |
/* } */ | |
/* return NULL; */ | |
/* } */ | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
struct X64Scale { | |
struct | |
{ | |
uint8_t s : 2; | |
}; | |
uint8_t scale_all; | |
}; | |
typedef struct X64Scale X64Scale; | |
static ATTRIBUTE_UNUSED const X64Scale X1 = {.s = 0}; | |
static ATTRIBUTE_UNUSED const X64Scale X2 = {.s = 1}; | |
static ATTRIBUTE_UNUSED const X64Scale X4 = {.s = 2}; | |
static ATTRIBUTE_UNUSED const X64Scale X8 = {.s = 3}; | |
/* static int */ | |
/* get_actual_scale (X64Scale s) */ | |
/* { */ | |
/* switch (s.s) { */ | |
/* default: */ | |
/* assert (0); */ | |
/* /\* fall through *\/ */ | |
/* case 0: */ | |
/* return 1; */ | |
/* case 1: */ | |
/* return 2; */ | |
/* case 2: */ | |
/* return 4; */ | |
/* case 3: */ | |
/* return 8; */ | |
/* } */ | |
/* } */ | |
static const char *get_scale_string (X64Scale s) | |
{ | |
switch (s.s) | |
{ | |
default: | |
assert (0); | |
/* fall through */ | |
case 0: | |
return ""; | |
case 1: | |
return "*2"; | |
case 2: | |
return "*4"; | |
case 3: | |
return "*8"; | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* struct X64Size { */ | |
/* uint8_t rex : 1; */ | |
/* uint8_t size_prefix : 1; */ | |
/* uint8_t w_d_bits : 2; */ | |
/* uint8_t imm_size : 4; */ | |
/* }; */ | |
/* typedef struct X64Size X64Size; */ | |
/* static const X64Size B={.w_d_bits = 0, .imm_size = 1}; */ | |
/* static const X64Size W={.size_prefix = 1, .w_d_bits = 1, .imm_size = 2}; */ | |
/* static const X64Size D={.w_d_bits = 1, .imm_size = 4}; */ | |
/* static const X64Size Q={.rex = 1, .w_d_bits = 1, .imm_size = 4}; */ | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
union X64ModRM | |
{ | |
struct | |
{ | |
uint8_t rm : 3, reg : 3, mod : 2; | |
//uint8_t mod : 2, reg : 3, rm : 3; | |
}; | |
uint8_t mod_rm_all; | |
}; | |
typedef union X64ModRM X64ModRM; | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
union X64REX | |
{ | |
struct | |
{ | |
uint8_t b : 1, x : 1, r : 1, w : 1, _ : 4; | |
}; | |
uint8_t rex_all; | |
}; | |
typedef union X64REX X64REX; | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
union X64SIB | |
{ | |
struct | |
{ | |
uint8_t b : 3, i : 3, s : 2; | |
}; | |
uint8_t sib_all; | |
}; | |
typedef union X64SIB X64SIB; | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
enum X64Condition { | |
COND_O = 0x0, | |
COND_NO = 0x1, | |
COND_B = 0x2, COND_C = 0x2, COND_NAE = 0x2, | |
COND_AE = 0x3, COND_NB = 0x3, COND_NC = 0x3, | |
COND_E = 0x4, COND_Z = 0x4, | |
COND_NE = 0x5, COND_NZ = 0x5, | |
COND_NA = 0x6, COND_BE = 0x6, | |
COND_A = 0x7, COND_NBE = 0x7, | |
COND_S = 0x8, | |
COND_NS = 0x9, | |
COND_P = 0xa, COND_PE = 0xa, | |
COND_NP = 0xb, COND_PO = 0xb, | |
COND_L = 0xc, COND_NGE = 0xc, | |
COND_NL = 0xd, COND_GE = 0xd, | |
COND_NG = 0xe, COND_LE = 0xe, | |
COND_NLE = 0xf, COND_G = 0xf, | |
}; | |
#define ALL_CONDITIONS \ | |
COND (O, O) /* 0 */ \ | |
COND (NO, NO) /* 1 */ \ | |
COND (B, C) COND (C, C) COND (NAE, C) /* 2 */ \ | |
COND (AE, NC) COND (NB, NC) COND (NC, NC) /* 3 */ \ | |
COND (E, Z) COND (Z, Z) /* 4 */ \ | |
COND (NE, NZ) COND (NZ, NZ) /* 5 */ \ | |
COND (NA, NA) COND (BE, NA) /* 6 */ \ | |
COND (A, A) COND (NBE, A) /* 7 */ \ | |
COND (S, S) /* 8 */ \ | |
COND (NS, NS) /* 9 */ \ | |
COND (P, PE) COND (PE, PE) /* A */ \ | |
COND (NP, PO) COND (PO, PO) /* B */ \ | |
COND (L, L) COND (NGE, L) /* C */ \ | |
COND (NL, NL) COND (GE, NL) /* D */ \ | |
COND (NG, NG) COND (LE, NG) /* E */ \ | |
COND (NLE, G) COND (G, G) /* F */ | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* enum X64Mnemonics */ | |
/* { */ | |
/* MNEM_NONE = 0, */ | |
/* #define MNEM(X) MNEM_##X, */ | |
/* #include "x64mnemonics.inl" */ | |
/* #undef MNEM */ | |
/* }; */ | |
/* static const char *const MNEMONICS[] = */ | |
/* { */ | |
/* [MNEM_NONE] = NULL, */ | |
/* #define MNEM(X) [MNEM_##X] = #X, */ | |
/* #include "x64mnemonics.inl" */ | |
/* #undef MNEM */ | |
/* }; */ | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* The mnemonic/name separation is a relic and needs to be removed. */ | |
struct X64Instruction | |
{ | |
/* 0 */ | |
uint8_t modrm_reg : 3; /* ModR/M reg field value, for unary | |
operations or immediate values */ | |
uint8_t op_size : 2; /* size log2 - 0=byte 1=word 2=dword | |
3=qword */ | |
uint8_t imm_size : 2; /* size log2 - 0=byte 1=word 2=dword | |
3=qword */ | |
/* 1 */ | |
uint8_t f : 1; /* opcode begins with 0x0f */ | |
uint8_t got_rm_imm : 1; /* use rm_imm_opcode for r/m,imm */ | |
uint8_t got_r_rm : 1; /* use r_rm_opcode for r,r/m */ | |
uint8_t got_rm_r : 1; /* use rm_r_opcode for r/m,r */ | |
uint8_t no_r_r : 1; /* r,r is not supported */ | |
uint8_t rel32_f : 1; /* rel32 version begins with 0x0f */ | |
/* 2 */ | |
union { | |
uint8_t r_rm_opcode; /* mov r,m */ | |
uint8_t rel8_opcode; /* jcc rel8 */ | |
}; | |
/* 3 */ | |
union { | |
uint8_t rm_r_opcode; /* mov m,r */ | |
uint8_t rel32_opcode; /* jcc rel32 */ | |
}; | |
/* 4 */ | |
uint8_t rm_imm_opcode; /* mov r,n */ | |
/* 5 */ | |
uint8_t opcode; /* various */ | |
}; | |
typedef struct X64Instruction X64Instruction; | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
#define BASIC_INSTR_3(NAME, MNEMONIC,R_IMM, R_IMM_MODRM_REG, R_RM, RM_R, OP_SIZE) \ | |
static ATTRIBUTE_UNUSED const X64Instruction NAME = { \ | |
.rm_imm_opcode = (R_IMM), \ | |
.modrm_reg = (R_IMM_MODRM_REG), \ | |
.r_rm_opcode = (R_RM), \ | |
.rm_r_opcode = (RM_R), \ | |
.op_size = (OP_SIZE), \ | |
.imm_size = (OP_SIZE) == 3 ? 2 : (OP_SIZE), \ | |
.got_rm_imm = 1, \ | |
.got_r_rm = 1, \ | |
.got_rm_r = 1, \ | |
}; | |
#define BASIC_INSTR_2(MNEM, R_IMM, R_IMM_MODRM_REG, R_RM, RM_R) \ | |
BASIC_INSTR_3 (MNEM##B, MNEM, R_IMM, R_IMM_MODRM_REG, R_RM, RM_R, 0) \ | |
BASIC_INSTR_3 (MNEM##W, MNEM, R_IMM | 1, R_IMM_MODRM_REG, R_RM | 1, RM_R | 1, 1) \ | |
BASIC_INSTR_3 (MNEM##D, MNEM, R_IMM | 1, R_IMM_MODRM_REG, R_RM | 1, RM_R | 1, 2) \ | |
BASIC_INSTR_3 (MNEM##Q, MNEM, R_IMM | 1, R_IMM_MODRM_REG, R_RM | 1, RM_R | 1, 3) | |
/* 8080-style opcodes. */ | |
#define BASIC_INSTR(MNEMONIC, INDEX) \ | |
BASIC_INSTR_2 (MNEMONIC, 0x80, INDEX, (INDEX) * 8 + 2, (INDEX) * 8 + 0) | |
#define BASIC_INSTRS \ | |
BASIC_INSTR (ADD, 0) \ | |
BASIC_INSTR (OR, 1) \ | |
BASIC_INSTR (ADC, 2) \ | |
BASIC_INSTR (SBB, 3) \ | |
BASIC_INSTR (AND, 4) \ | |
BASIC_INSTR (SUB, 5) \ | |
BASIC_INSTR (XOR, 6) \ | |
BASIC_INSTR (CMP, 7) | |
BASIC_INSTRS | |
#undef BASIC_INSTR | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* MOVD and MOVQ are also SSE mnemonics... remains to be seen what, if | |
anything, ought to be done about this. */ | |
BASIC_INSTR_2 (MOV, 0xc6, 0, 0x8a, 0x88) | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* These instructions can't have memory as a source. */ | |
#define MEM_DEST_INSTR2(NAME, RM_R_OPCODE, RM_IMM_OPCODE, R, IMM_SIZE, OP_SIZE) \ | |
static ATTRIBUTE_UNUSED const X64Instruction NAME = \ | |
{ \ | |
.rm_imm_opcode = (RM_IMM_OPCODE), \ | |
.modrm_reg = (R), \ | |
.rm_r_opcode = (RM_R_OPCODE) == -1 ? 0 : (RM_R_OPCODE), \ | |
.op_size = (OP_SIZE), \ | |
.imm_size = (IMM_SIZE) >= 0 ? (IMM_SIZE) : ((OP_SIZE) == 3 ? 2 : (OP_SIZE)), \ | |
.got_rm_imm = 1, \ | |
.got_rm_r = (RM_R_OPCODE) >= 0, \ | |
}; | |
#define MEM_DEST_INSTR(NAME, RM_R_OPCODE, RM_IMM_OPCODE, R, IMM_SIZE) \ | |
MEM_DEST_INSTR2 (NAME##B, RM_R_OPCODE, RM_IMM_OPCODE, R, IMM_SIZE, 0) \ | |
MEM_DEST_INSTR2 (NAME##W, RM_R_OPCODE, RM_IMM_OPCODE | 1, R, IMM_SIZE, 1) \ | |
MEM_DEST_INSTR2 (NAME##D, RM_R_OPCODE, RM_IMM_OPCODE | 1, R, IMM_SIZE, 2) \ | |
MEM_DEST_INSTR2 (NAME##Q, RM_R_OPCODE, RM_IMM_OPCODE | 1, R, IMM_SIZE, 3) | |
#define MEM_DEST_INSTRS \ | |
MEM_DEST_INSTR (TEST, 0x84, 0xf6, 0, -1) \ | |
MEM_DEST_INSTR (SHR, -1, 0xc0, 5, 0) \ | |
MEM_DEST_INSTR (SHL, -1, 0xc0, 4, 0) \ | |
MEM_DEST_INSTR (SAR, -1, 0xc0, 7, 0) | |
MEM_DEST_INSTRS | |
#undef MEM_DEST_INSTR | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
#define UNARY_INSTR_2(NAME, MNEMONIC, OPCODE, MODRM, OP_SIZE) \ | |
static ATTRIBUTE_UNUSED const X64Instruction NAME = { \ | |
.modrm_reg = (MODRM), \ | |
.opcode = (OPCODE), \ | |
.op_size = (OP_SIZE), \ | |
}; | |
#define UNARY_INSTR(MNEMONIC, OPCODE, MODRM) \ | |
UNARY_INSTR_2 (MNEMONIC##B, MNEMONIC, OPCODE, MODRM, 0) \ | |
UNARY_INSTR_2 (MNEMONIC##W, MNEMONIC, OPCODE | 1, MODRM, 1) \ | |
UNARY_INSTR_2 (MNEMONIC##D, MNEMONIC, OPCODE | 1, MODRM, 2) \ | |
UNARY_INSTR_2 (MNEMONIC##Q, MNEMONIC, OPCODE | 1, MODRM, 3) | |
#define UNARY_INSTRS \ | |
UNARY_INSTR (INC, 0xfe, 0) \ | |
UNARY_INSTR (DEC, 0xfe, 1) \ | |
UNARY_INSTR (NOT, 0xf6, 2) \ | |
UNARY_INSTR (NEG, 0xf6, 3) \ | |
UNARY_INSTR (MUL, 0xf6, 4) \ | |
UNARY_INSTR (IMUL, 0xf6, 5) \ | |
UNARY_INSTR (DIV, 0xf6, 6) \ | |
UNARY_INSTR (IDIV, 0xf6, 7) \ | |
UNARY_INSTR (SHR1, 0xd0, 5) \ | |
UNARY_INSTR (SHL1, 0xd0, 4) \ | |
UNARY_INSTR (SAR1, 0xd0, 7) | |
UNARY_INSTRS | |
#undef UNARY_INSTR | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* Oddities */ | |
static ATTRIBUTE_UNUSED const X64Instruction CALL = | |
{ | |
.modrm_reg = 2, | |
.opcode = 0xff, | |
.op_size = 3, | |
.rel32_opcode = 0xe8, | |
}; | |
/* static ATTRIBUTE_UNUSED const X64Instruction IMUL2W = */ | |
/* { */ | |
/* .r_rm_opcode = 0xAF, .f = 1, .got_r_rm = 1, .op_size = 1, */ | |
/* }; */ | |
/* static ATTRIBUTE_UNUSED const X64Instruction IMUL2D = */ | |
/* { */ | |
/* .r_rm_opcode = 0xAF, .f = 1, .got_r_rm = 1, .op_size = 2, */ | |
/* }; */ | |
/* static ATTRIBUTE_UNUSED const X64Instruction IMUL2Q = */ | |
/* { */ | |
/* .r_rm_opcode = 0xAF, .f = 1, .got_r_rm = 1, .op_size = 3, */ | |
/* }; */ | |
static ATTRIBUTE_UNUSED const X64Instruction LEAQ = | |
{ | |
.r_rm_opcode = 0x8d, | |
.op_size = 3, | |
.got_r_rm = 1, | |
.no_r_r = 1, | |
}; | |
static ATTRIBUTE_UNUSED const X64Instruction JMP = | |
{ | |
.rel8_opcode = 0xeb, | |
.rel32_opcode = 0xe9, | |
.modrm_reg = 4, | |
.opcode = 0xff, | |
.op_size = 3, | |
}; | |
static ATTRIBUTE_UNUSED const X64Instruction PUSHQ = | |
{ | |
.modrm_reg = 6, | |
.opcode = 0xff, | |
.op_size = 3, | |
}; | |
static ATTRIBUTE_UNUSED const X64Instruction POPQ = | |
{ | |
.modrm_reg = 0, | |
.opcode = 0x8f, | |
.op_size = 3, | |
}; | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
#define COND(CC, NDISASM_CC) \ | |
static ATTRIBUTE_UNUSED const X64Instruction J##CC = \ | |
{ \ | |
.rel8_opcode = 0x70 | (COND_##CC), \ | |
.rel32_f = 1, \ | |
.rel32_opcode = 0x80 | (COND_##CC), \ | |
.op_size = 0, \ | |
}; | |
ALL_CONDITIONS | |
#undef COND | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
#define COND(CC, NDISASM_CC) \ | |
static ATTRIBUTE_UNUSED const X64Instruction SET##CC = \ | |
{ \ | |
.f = 1, \ | |
.opcode = 0x90 | (COND_##CC), \ | |
}; | |
ALL_CONDITIONS | |
#undef COND | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
#define CMOV_INSTR(NAME, CC, OP_SIZE) \ | |
static ATTRIBUTE_UNUSED const X64Instruction NAME = \ | |
{ \ | |
.f = 1, \ | |
.r_rm_opcode = 0x40 | (COND_##CC), \ | |
.got_r_rm = 1, \ | |
.op_size = (OP_SIZE), \ | |
}; | |
/* I decided the size would go with the CMOV, rather than being a | |
prefix, for symmetry with MOV (for good or for ill). */ | |
#define COND(CC, NDISASM_CC) \ | |
CMOV_INSTR(CMOVW##CC, CC, 1) \ | |
CMOV_INSTR(CMOVD##CC, CC, 2) \ | |
CMOV_INSTR(CMOVQ##CC, CC, 3) | |
ALL_CONDITIONS | |
#undef COND | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
struct Gen { | |
uint8_t *dest; | |
uint8_t *base; | |
void *context; | |
}; | |
typedef struct Gen Gen; | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
init_gen (Gen *g, size_t n) | |
{ | |
g->dest = calloc (n, 1); | |
g->base = g->dest; | |
} | |
static void | |
set_gen_context (Gen *g, void *context) | |
{ | |
g->context = context; | |
} | |
static void | |
destroy_gen (Gen *g) | |
{ | |
g->dest = NULL; | |
free (g->base); | |
g->base = NULL; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* static void */ | |
/* gen_lit8(Gen *g, uint8_t value) */ | |
/* { */ | |
/* *g->dest++ = value; */ | |
/* } */ | |
/* static void */ | |
/* gen_lit16(Gen *g, uint16_t value) */ | |
/* { */ | |
/* *g->dest++ = value; */ | |
/* *g->dest++ = value >> 8; */ | |
/* } */ | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
gen_lit32 (Gen *g, uint32_t value) | |
{ | |
*g->dest++ = value; | |
*g->dest++ = value >> 8; | |
*g->dest++ = value >> 16; | |
*g->dest++ = value >> 24; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
gen_lit64 (Gen *g, uint64_t value) | |
{ | |
*g->dest++ = value; | |
*g->dest++ = value >> 8; | |
*g->dest++ = value >> 16; | |
*g->dest++ = value >> 24; | |
*g->dest++ = value >> 32; | |
*g->dest++ = value >> 40; | |
*g->dest++ = value >> 48; | |
*g->dest++ = value >> 56; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
gen_imm (Gen *g, uint32_t value, uint8_t imm_size) | |
{ | |
*g->dest++ = value; | |
if (imm_size >= 1) | |
*g->dest++ = value >> 8; | |
if (imm_size >= 2) | |
{ | |
*g->dest++ = value >> 16; | |
*g->dest++ = value >> 24; | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
gen_fixup_rel32 (Gen *g, uint8_t *rel32, uint8_t *target) | |
{ | |
ptrdiff_t diff = target - (rel32 + 5); | |
int32_t disp = (int32_t)diff; | |
assert (*(int32_t *)rel32 == -5); | |
assert (diff == disp); | |
memcpy (rel32, &disp, 4); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static uint8_t * | |
gen_rel (Gen *g, X64Instruction instr, uint8_t *target) | |
{ | |
ptrdiff_t diff; | |
/* 8-bit? */ | |
if (target && instr.rel8_opcode != 0) | |
{ | |
diff = target - (g->dest + 2); | |
if (diff == (int8_t) diff) | |
{ | |
*g->dest++ = instr.rel8_opcode; | |
*g->dest++ = diff; | |
return NULL; | |
} | |
} | |
/* 32-bit. */ | |
if (instr.rel32_f) | |
*g->dest++ = 0x0f; | |
*g->dest++ = instr.rel32_opcode; | |
diff = target - (g->dest + 4); | |
if (diff != (int32_t) diff) | |
{ | |
assert (0); | |
/* Should record this error somewhere. */ | |
} | |
if (target) | |
{ | |
gen_lit32 (g, diff); | |
return NULL; | |
} | |
else | |
{ | |
gen_lit32 (g, 0); | |
return g->dest - 4; | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* emits a size prefix, if necessay. */ | |
static void | |
gen_size_prefix (Gen *g, X64Instruction instr) | |
{ | |
if (instr.op_size == 1) | |
*g->dest++ = 0x66; | |
} | |
/* emits any opcode prefixes necessary, and the byte | |
itself. Call after adding the REX byte. */ | |
static void | |
gen_opcode_bytes (Gen *g, X64Instruction instr, uint8_t opcode) | |
{ | |
if (instr.f) | |
*g->dest++ = 0x0f; | |
*g->dest++ = opcode; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* generate REX prefix with the given bit values if necessary. | |
* | |
* The dest/src register is required so that the REX prefix can be | |
* added to access the low bytes of RDI/RSI/RBP/RSP. Pass RAX when | |
* this isn't relevant. | |
*/ | |
static void | |
gen_rex_bytes (Gen *g, X64Instruction instr, X64Register reg, uint8_t r, uint8_t x, uint8_t b) | |
{ | |
if ((instr.op_size == 3) | r | x | b) | |
*g->dest++ = (X64REX) {._ = 4, .w = instr.op_size == 3, .r = r, .x = x, .b = b}.rex_all; | |
else if (instr.op_size == 0 && reg.r >= 4) | |
{ | |
/* And reg.rex is clear too. This is implied as it will | |
(should...) have been passed in as one of the r, x or b | |
arguments. */ | |
assert (!reg.rex); | |
assert (!r); | |
assert (!x); | |
assert (!b); | |
*g->dest++ = (X64REX) {._ = 4}.rex_all; | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* Helper for the helpers. Does no error checking. */ | |
static void | |
gen_mbisd_bytes_internal (Gen *g, | |
uint8_t reg, | |
X64Register base, | |
X64Register index, | |
X64Scale scale, | |
int32_t disp) | |
{ | |
X64SIB sib = {.s = scale.s, .i = index.r, .b = base.r}; | |
/* When the base's non-REX part is 5 (RBP/R13), a displacement is | |
mandatory. */ | |
if (disp == 0 && base.r != 5) | |
{ | |
*g->dest++ = (X64ModRM) {.mod = 0, .rm = 4, .reg = reg}.mod_rm_all; | |
*g->dest++ = sib.sib_all; | |
} | |
else if (disp == (int8_t) disp) | |
{ | |
*g->dest++ = (X64ModRM) {.mod = 1, .rm = 4, .reg = reg}.mod_rm_all; | |
*g->dest++ = sib.sib_all; | |
*g->dest++ = disp; | |
} | |
else | |
{ | |
*g->dest++ = (X64ModRM) {.mod = 2, .rm = 4, .reg = reg}.mod_rm_all; | |
*g->dest++ = sib.sib_all; | |
gen_lit32 (g, disp); | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* generate ModR/M and SIB bytes for the given operands. */ | |
static void | |
gen_mbisd_bytes (Gen *g, | |
uint8_t reg, | |
X64Register base, | |
X64Register index, | |
X64Scale scale, | |
int32_t disp) | |
{ | |
assert (!(index.r == 4 && index.rex == 0)); /* index can't be RSP */ | |
gen_mbisd_bytes_internal (g, reg, base, index, scale, disp); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* generate ModR/M byte for the given operands. */ | |
static void | |
gen_mbd_bytes (Gen *g, uint8_t reg, X64Register base, int32_t disp) | |
{ | |
if (base.r == 4) | |
{ | |
/* Have to encode this the long way round. */ | |
gen_mbisd_bytes_internal (g, reg, base, RSP, X1, disp); | |
} | |
else | |
{ | |
/* When the base's non-REX part is 5 (RBP/R13), a displacement | |
is mandatory. */ | |
if (disp == 0 && base.r != 5) | |
*g->dest++ = (X64ModRM) {.mod = 0, .rm = base.r, .reg = reg}.mod_rm_all; | |
else if (disp == (int8_t)disp) | |
{ | |
*g->dest++ = (X64ModRM) {.mod = 1, .rm = base.r, .reg = reg}.mod_rm_all; | |
*g->dest++ = disp; | |
} | |
else | |
{ | |
*g->dest++ = (X64ModRM) {.mod = 2, .rm = base.r, .reg = reg}.mod_rm_all; | |
gen_lit32 (g, disp); | |
} | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
gen_nop (Gen *g) | |
{ | |
*g->dest++ = 0x90; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
gen_ret (Gen *g) | |
{ | |
*g->dest++ = 0xc3; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* The 64-bit MOVs are a special case encodings, so they get their own | |
extra specially unique functions. */ | |
static void | |
gen_mov_r_imm64 (Gen *g, X64Register reg, int64_t imm64) | |
{ | |
*g->dest++ = (X64REX) {._ = 4, .w = 0, .r = 0, .x = 0, .b = reg.rex}.rex_all; | |
*g->dest++ = 0xb8 | reg.r; | |
gen_lit64 (g, imm64); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* unary r */ | |
static void | |
gen_r (Gen *g, X64Instruction instr, X64Register reg) | |
{ | |
gen_size_prefix (g, instr); | |
gen_rex_bytes (g, instr, reg, 0, 0, reg.rex); | |
gen_opcode_bytes (g, instr, instr.opcode); | |
*g->dest++ = (X64ModRM) {.mod = 3, .rm = reg.r, .reg = instr.modrm_reg}.mod_rm_all; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* unary [base+disp] */ | |
static void | |
gen_mbd (Gen *g, X64Instruction instr, X64Register base, int32_t disp) | |
{ | |
gen_size_prefix (g, instr); | |
gen_rex_bytes (g, instr, RAX, 0, 0, base.rex); | |
gen_opcode_bytes (g, instr, instr.opcode); | |
gen_mbd_bytes (g, instr.modrm_reg, base, disp); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* unary [base+index*scale+disp] */ | |
static void | |
gen_mbisd (Gen *g, | |
X64Instruction instr, | |
X64Register base, X64Register index, X64Scale scale, int32_t disp) | |
{ | |
gen_size_prefix (g, instr); | |
gen_rex_bytes (g, instr, RAX, 0, index.rex, base.rex); | |
gen_opcode_bytes (g, instr, instr.opcode); | |
gen_mbisd_bytes (g, instr.modrm_reg, base, index, scale, disp); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* binary reg, imm */ | |
static void | |
gen_r_imm (Gen *g, X64Instruction instr, X64Register reg, int32_t imm) | |
{ | |
assert (instr.got_rm_imm); | |
gen_size_prefix (g, instr); | |
gen_rex_bytes (g, instr, reg, 0, 0, reg.rex); | |
gen_opcode_bytes (g, instr, instr.rm_imm_opcode); | |
*g->dest++ = (X64ModRM) {.mod = 3, .rm = reg.r, .reg = instr.modrm_reg}.mod_rm_all; | |
gen_imm (g, imm, instr.imm_size); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* binary [base+disp], imm */ | |
static void | |
gen_mbd_imm (Gen *g, X64Instruction instr, X64Register base, int32_t disp, int32_t imm) | |
{ | |
assert (instr.got_rm_imm); | |
gen_size_prefix (g, instr); | |
gen_rex_bytes (g, instr, RAX, 0, 0, base.rex); | |
gen_opcode_bytes (g, instr, instr.rm_imm_opcode); | |
gen_mbd_bytes (g, instr.modrm_reg, base, disp); | |
gen_imm (g, imm, instr.imm_size); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* binary [base+index*scale+disp], imm */ | |
static void | |
gen_mbisd_imm (Gen *g, | |
X64Instruction instr, | |
X64Register base, | |
X64Register index, | |
X64Scale scale, | |
int32_t disp, | |
int32_t imm) | |
{ | |
assert (instr.got_rm_imm); | |
gen_size_prefix (g, instr); | |
gen_rex_bytes (g, instr, RAX, 0, index.rex, base.rex); | |
gen_opcode_bytes (g, instr, instr.rm_imm_opcode); | |
gen_mbisd_bytes (g, instr.modrm_reg, base, index, scale, disp); | |
gen_imm (g, imm, instr.imm_size); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* binary reg, [base+disp] | |
* binary [base+disp], reg | |
*/ | |
static void | |
gen_r_mbd_or_mbd_r (Gen *g, | |
X64Instruction instr, | |
uint8_t opcode, | |
X64Register reg, | |
X64Register base, | |
int32_t disp) | |
{ | |
gen_size_prefix (g, instr); | |
gen_rex_bytes (g, instr, reg, reg.rex, 0, base.rex); | |
gen_opcode_bytes (g, instr, opcode); | |
gen_mbd_bytes (g, reg.r, base, disp); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* binary reg, [base+index*scale+disp] | |
* binary [base+index*scale+disp], reg | |
*/ | |
static void | |
gen_r_mbisd_or_mbisd_r (Gen *g, | |
X64Instruction instr, | |
uint8_t opcode, | |
X64Register reg, | |
X64Register base, | |
X64Register index, | |
X64Scale scale, | |
int32_t disp) | |
{ | |
gen_size_prefix (g, instr); | |
gen_rex_bytes (g, instr, reg, reg.rex, index.rex, base.rex); | |
gen_opcode_bytes (g, instr, opcode); | |
gen_mbisd_bytes (g, reg.r, base, index, scale, disp); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* binary reg, reg */ | |
static void | |
gen_r_r (Gen *g, | |
X64Instruction instr, | |
X64Register dest, | |
X64Register src) | |
{ | |
assert (!instr.no_r_r); | |
if (instr.got_rm_r) | |
{ | |
gen_size_prefix (g, instr); | |
gen_rex_bytes (g, instr, (X64Register){.reg_all = dest.reg_all | src.reg_all}, src.rex, 0, dest.rex); | |
gen_opcode_bytes (g, instr, instr.rm_r_opcode); | |
*g->dest++ = (X64ModRM) {.mod = 3, .rm = dest.r, .reg = src.r}.mod_rm_all; | |
} | |
else if (instr.got_r_rm) | |
{ | |
gen_size_prefix (g, instr); | |
gen_rex_bytes (g, instr, (X64Register){.reg_all = dest.reg_all | src.reg_all}, dest.rex, 0, src.rex); | |
gen_opcode_bytes (g, instr, instr.r_rm_opcode); | |
*g->dest++ = (X64ModRM) {.mod = 3, .rm = src.r, .reg = dest.r}.mod_rm_all; | |
} | |
else | |
assert (0); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* binary reg, [base+disp] */ | |
static void | |
gen_r_mbd (Gen *g, | |
X64Instruction instr, | |
X64Register reg, | |
X64Register base, | |
int32_t disp) | |
{ | |
assert (instr.got_r_rm); | |
gen_r_mbd_or_mbd_r (g, instr, instr.r_rm_opcode, reg, base, disp); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* binary [base+disp], reg */ | |
static void | |
gen_mbd_r (Gen *g, | |
X64Instruction instr, | |
X64Register base, | |
int32_t disp, | |
X64Register reg) | |
{ | |
assert (instr.got_rm_r); | |
gen_r_mbd_or_mbd_r (g, instr, instr.rm_r_opcode, reg, base, disp); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* binary reg, [base+index*scale+disp] */ | |
static void | |
gen_r_mbisd (Gen *g, | |
X64Instruction instr, | |
X64Register reg, | |
X64Register base, | |
X64Register index, | |
X64Scale scale, | |
int32_t disp) | |
{ | |
assert (instr.got_r_rm); | |
gen_r_mbisd_or_mbisd_r (g, instr, instr.r_rm_opcode, reg, base, index, scale, disp); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* binary [base+index*scale+disp], reg */ | |
static void gen_mbisd_r (Gen *g, | |
X64Instruction instr, | |
X64Register base, | |
X64Register index, | |
X64Scale scale, | |
int32_t disp, | |
X64Register reg) | |
{ | |
assert (instr.got_rm_r); | |
gen_r_mbisd_or_mbisd_r (g, instr, instr.rm_r_opcode, reg, base, index, scale, disp); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -*- mode:c; c-file-style: "GNU"; compile-command: "make -f Makefile.jit_x64" -*- | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <assert.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <stddef.h> | |
#include <stdarg.h> | |
#include <ctype.h> | |
#include <inttypes.h> | |
#define ATTRIBUTE_UNUSED | |
#include "jit_x64gen.h" | |
/* Test the x64gen fuctions. | |
* | |
* The highly (?) sophisticated (?) approach employed here is to | |
* generate all combinations of operands, along with the expected | |
* disassembly in each case, then save the result to a file and have | |
* ndisasm disassemble that. Then use GNU diff to check the expected | |
* disassembly matches the actual disassembly. | |
* | |
* Needs gcc/clang... | |
*/ | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
struct TestLine | |
{ | |
size_t offset; | |
char *text; | |
struct TestLine *next; | |
}; | |
typedef struct TestLine TestLine; | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
struct TestLines | |
{ | |
TestLine *first, **next_ptr; | |
size_t mark; | |
}; | |
typedef struct TestLines TestLines; | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static Gen * | |
create_test_gen (void) | |
{ | |
Gen *g = calloc (1, sizeof *g); | |
TestLines *tls = calloc (1, sizeof *tls); | |
init_gen (g, 16 * 1048576); | |
set_gen_context (g, tls); | |
tls->next_ptr = &tls->first; | |
return g; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
destroy_test_gen (Gen *g) | |
{ | |
TestLines *tls = g->context; | |
TestLine *tl = tls->first; | |
while (tl) | |
{ | |
TestLine *next = tl->next; | |
free (tl->text); | |
free (tl); | |
tl = next; | |
} | |
tls->first = NULL; | |
tls->next_ptr = NULL; | |
destroy_gen (g); | |
free (g); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static size_t | |
get_offset (Gen *g) | |
{ | |
return (size_t) (g->dest - g->base); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
mark (Gen *g) | |
{ | |
TestLines *tls = g->context; | |
assert (tls->mark == 0); | |
tls->mark = get_offset (g); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
__attribute__ ((format(printf, 2, 3))) | |
add_line (Gen *g, const char *fmt, ...) | |
{ | |
TestLines *tls = g->context; | |
va_list v; | |
TestLine *tl; | |
tl = malloc (sizeof *tl); | |
if (tls->mark == 0) | |
tl->offset = get_offset (g); | |
else | |
{ | |
tl->offset = tls->mark; | |
tls->mark = 0; | |
} | |
va_start (v, fmt); | |
vasprintf (&tl->text, fmt, v); | |
va_end (v); | |
tl->next = NULL; | |
*tls->next_ptr = tl; | |
tls->next_ptr = &tl->next; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
save_data (Gen *g, const char *stem) | |
{ | |
TestLines *tls = g->context; | |
char *dat_fname, *txt_fname; | |
asprintf (&txt_fname, "%s/%s", GOT_FOLDER, stem); | |
asprintf (&dat_fname, "%s/%s", DAT_FOLDER, stem); | |
{ | |
FILE *f = fopen (txt_fname, "wt"); | |
assert (f); | |
for (const TestLine *tl = tls->first; tl; tl = tl->next) | |
{ | |
size_t i = tl->offset, next = tl->next ? tl->next->offset : g->dest - g->base; | |
while (i < next) | |
{ | |
int print = i == tl->offset; | |
if (print) | |
fprintf (f, "%08X ", (unsigned) tl->offset); | |
else | |
fprintf (f, " -"); | |
for (size_t j = 0; j < 8; ++j) | |
{ | |
if (i + j < next) | |
fprintf (f, "%02X", g->base[i + j]); | |
else if (print) | |
fprintf (f, " "); | |
} | |
if (print) | |
fprintf (f, " %s", tl->text); | |
fprintf (f, "\n"); | |
i += 8; | |
} | |
} | |
fclose (f); | |
} | |
{ | |
FILE *f = fopen (dat_fname, "wb"); | |
assert (f); | |
fwrite (g->base, g->dest - g->base, 1, f); | |
fclose (f); | |
} | |
free (dat_fname); | |
dat_fname = NULL; | |
free (txt_fname); | |
txt_fname = NULL; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static char * | |
get_lower_case (const char *name) | |
{ | |
char *tmp = strdup (name), *p; | |
for (p = tmp; *p != 0; ++p) | |
*p = tolower (*p); | |
return tmp; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
typedef void (*bisd_callback_fn) (Gen *g, | |
X64Instruction instr, | |
const char *mnemonic, | |
int base_i, | |
int index_i, | |
X64Scale scale, | |
int32_t disp, | |
void *context); | |
static const int32_t DISPS[] = {0, 64, 1024, -1}; | |
static void | |
for_each_bisd (Gen *g, | |
X64Instruction instr, | |
const char *mnemonic, | |
bisd_callback_fn fn, | |
void *context) | |
{ | |
for (int b = 0; b < 16; ++b) | |
{ | |
for (int i = -1; i < 16; ++i) | |
{ | |
if (i == 4) | |
continue; | |
for (int j = 0; DISPS[j] >= 0; ++j) | |
{ | |
if (i >= 0) | |
{ | |
(*fn) (g, instr, mnemonic, b, i, X1, DISPS[j], context); | |
(*fn) (g, instr, mnemonic, b, i, X2, DISPS[j], context); | |
(*fn) (g, instr, mnemonic, b, i, X4, DISPS[j], context); | |
(*fn) (g, instr, mnemonic, b, i, X8, DISPS[j], context); | |
} | |
else | |
(*fn) (g, instr, mnemonic, b, i, X1, DISPS[j], context); | |
} | |
} | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
typedef void (*r_mbisd_callback_fn) (Gen *g, | |
X64Instruction instr, | |
const char *mnemonic, | |
int reg_i, | |
int base_i, | |
int index_i, | |
X64Scale scale, | |
int32_t disp, | |
void *context); | |
struct For_Each_R_MBISD_Context { | |
r_mbisd_callback_fn fn; | |
void *context; | |
int reg_i; | |
}; | |
typedef struct For_Each_R_MBISD_Context For_Each_R_MBISD_Context; | |
static void | |
do_r_mbisd_callback (Gen *g, | |
X64Instruction instr, | |
const char *mnemonic, | |
int base_i, | |
int index_i, | |
X64Scale scale, | |
int32_t disp, | |
void *context) | |
{ | |
For_Each_R_MBISD_Context *c = context; | |
(*c->fn) (g, instr, mnemonic, c->reg_i, base_i, index_i, scale, disp, c->context); | |
} | |
static void | |
for_each_r_mbisd (Gen *g, | |
X64Instruction instr, | |
const char *mnemonic, | |
r_mbisd_callback_fn fn, | |
void *context) | |
{ | |
For_Each_R_MBISD_Context c; | |
c.fn = fn; | |
c.context = context; | |
for (c.reg_i = 0; c.reg_i < 16; ++c.reg_i) | |
{ | |
for_each_bisd (g, instr, mnemonic, &do_r_mbisd_callback, &c); | |
} | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static char * | |
get_bisd_string (X64Instruction instr, | |
int base_i, | |
int index_i, | |
X64Scale scale, | |
int32_t disp) | |
{ | |
char *str; | |
/* When the base's non-REX part is 5 (i.e., RBP or R13), a | |
displacement is mandatory, and ndisasm will then print one. So | |
this function checks for that case specially. */ | |
if (index_i >= 0) | |
{ | |
if (disp == 0 && (base_i & 7) != 5) | |
{ | |
asprintf (&str, "[%s+%s%s]", | |
REGISTER_QNAMES[base_i],//get_register_name (base_i, instr.op_size), | |
REGISTER_QNAMES[index_i],//get_register_name (index_i, instr.op_size), | |
get_scale_string (scale)); | |
} | |
else | |
{ | |
asprintf (&str, "[%s+%s%s+0x%x]", | |
REGISTER_QNAMES[base_i],//get_register_name (base_i, instr.op_size), | |
REGISTER_QNAMES[index_i],//get_register_name (index_i, instr.op_size), | |
get_scale_string (scale), | |
disp); | |
} | |
} | |
else | |
{ | |
if (disp == 0 && (base_i & 7) != 5) | |
{ | |
asprintf (&str, "[%s]", | |
REGISTER_QNAMES[base_i]);//get_register_name (base_i, instr.op_size)); | |
} | |
else | |
{ | |
asprintf (&str, "[%s+0x%x]", | |
REGISTER_QNAMES[base_i],//get_register_name (base_i, instr.op_size), | |
disp); | |
} | |
} | |
return str; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static const int32_t IMM_VALUES[] = | |
{ | |
0x12, | |
0x1234, | |
0x12345678, | |
0x12345678 | |
}; | |
static void | |
do_basic_instr_r_mbisd_callback(Gen *g, | |
X64Instruction instr, | |
const char *mnemonic, | |
int reg_i, | |
int base_i, | |
int index_i, | |
X64Scale scale, | |
int32_t disp, | |
void *context) | |
{ | |
char *bisd = get_bisd_string (instr, base_i, index_i, scale, disp); | |
X64Register rr = *REGISTERS[reg_i]; | |
X64Register rb = *REGISTERS[base_i]; | |
X64Register ri = *REGISTERS[index_i]; | |
if (instr.got_r_rm) | |
{ | |
/* r,m */ | |
add_line (g, "%s %s,%s", mnemonic, get_register_name (reg_i, instr.op_size), bisd); | |
if (index_i >= 0) | |
gen_r_mbisd (g, instr, rr, rb, ri, scale, disp); | |
else | |
gen_r_mbd (g, instr, rr, rb, disp); | |
} | |
if (instr.got_rm_r) | |
{ | |
/* m,r */ | |
add_line (g, "%s %s,%s", mnemonic, bisd, get_register_name (reg_i, instr.op_size)); | |
if (index_i >= 0) | |
gen_mbisd_r (g, instr, rb, ri, scale, disp, rr); | |
else | |
gen_mbd_r (g, instr, rb, disp, rr); | |
} | |
free (bisd); | |
bisd = NULL; | |
} | |
static void | |
do_basic_instr_m_imm_callback (Gen *g, | |
X64Instruction instr, | |
const char *mnemonic, | |
int base_i, | |
int index_i, | |
X64Scale scale, | |
int32_t disp, | |
void *context) | |
{ | |
char *bisd = get_bisd_string (instr, base_i, index_i, scale, disp); | |
if (instr.got_rm_imm) | |
{ | |
/* m,imm */ | |
int32_t imm = IMM_VALUES[instr.op_size]; | |
add_line (g, "%s %s %s,0x%x", mnemonic, get_size_keyword (instr.op_size), bisd, imm); | |
if (index_i >= 0) | |
gen_mbisd_imm (g, instr, *REGISTERS[base_i], *REGISTERS[index_i], scale, disp, imm); | |
else | |
gen_mbd_imm (g, instr, *REGISTERS[base_i], disp, imm); | |
} | |
free (bisd); | |
bisd = NULL; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
test_basic_instr (const char *name, | |
const X64Instruction *instrb, | |
const X64Instruction *instrw, | |
const X64Instruction *instrd, | |
const X64Instruction *instrq) | |
{ | |
fprintf (stderr, "generating %s...\n", name); | |
const X64Instruction *instrs[4] = | |
{ | |
instrb, | |
instrw, | |
instrd, | |
instrq, | |
}; | |
Gen *g = create_test_gen (); | |
char *mnemonic = get_lower_case (name); | |
/* r,m; m,r */ | |
for (int i = 0; i < 4; ++i) | |
{ | |
if (instrs[i]) | |
for_each_r_mbisd (g, *instrs[i], mnemonic, &do_basic_instr_r_mbisd_callback, NULL); | |
} | |
/* r,imm */ | |
for (unsigned i = 0; i < 16; ++i) | |
{ | |
for (int j = 0; j < 4; ++j) | |
{ | |
if (instrs[j] && instrs[j]->got_rm_imm) | |
{ | |
add_line (g, "%s %s,0x%x", mnemonic, REGISTER_NAMES[j][i], IMM_VALUES[j]); | |
gen_r_imm (g, *instrs[j], *REGISTERS[i], IMM_VALUES[j]); | |
} | |
} | |
} | |
/* m,imm */ | |
for (int i = 0; i < 4; ++i) | |
{ | |
if (instrs[i]) | |
for_each_bisd (g, *instrs[i], mnemonic, &do_basic_instr_m_imm_callback, NULL); | |
} | |
/* r,r */ | |
for (unsigned i = 0; i < 16; ++i) | |
{ | |
for (unsigned j = 0; j < 16; ++j) | |
{ | |
for (unsigned k = 0; k < 4; ++k) | |
{ | |
if (instrs[k] && !instrs[k]->no_r_r) | |
{ | |
add_line (g, "%s %s,%s", mnemonic, REGISTER_NAMES[k][i], REGISTER_NAMES[k][j]); | |
gen_r_r (g, *instrs[k], *REGISTERS[i], *REGISTERS[j]); | |
} | |
} | |
} | |
} | |
save_data (g, mnemonic); | |
free (mnemonic); | |
mnemonic = NULL; | |
destroy_test_gen (g); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
do_unary_instr_callback(Gen *g, | |
X64Instruction instr, | |
const char *mnemonic, | |
int reg_i, | |
int base_i, | |
int index_i, | |
X64Scale scale, | |
int32_t disp, | |
void *context) | |
{ | |
char *bisd = get_bisd_string (instr, base_i, index_i, scale, disp); | |
add_line (g, "%s %s %s%s", mnemonic, get_size_keyword (instr.op_size), bisd, context); | |
if (index_i >= 0) | |
gen_mbisd (g, instr, *REGISTERS[base_i], *REGISTERS[index_i], scale, disp); | |
else | |
gen_mbd (g, instr, *REGISTERS[base_i], disp); | |
free (bisd); | |
bisd = NULL; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
test_unary_instr (const char *name, | |
const X64Instruction *instrb, | |
const X64Instruction *instrw, | |
const X64Instruction *instrd, | |
const X64Instruction *instrq) | |
{ | |
fprintf (stderr, "generating %s...\n", name); | |
Gen *g = create_test_gen (); | |
char *mnemonic = get_lower_case (name); | |
char *fname = get_lower_case (name); | |
char *extra = ""; | |
/* Instruction-specific bodges :( */ | |
{ | |
char *c = &mnemonic[strlen (mnemonic) - 1]; | |
char *dot = strchr (mnemonic, '.'); | |
if (*c == '1') | |
{ | |
/* Bodge for the shift instructions' syntax. */ | |
extra = ",1"; | |
*c = 0; | |
} | |
else if (dot) | |
{ | |
/* Bodge for any instruction, as required - everything after | |
the '.' is part of the file name, and not the mnemonic. */ | |
*dot = 0; | |
} | |
} | |
const X64Instruction *instrs[4] = | |
{ | |
instrb, | |
instrw, | |
instrd, | |
instrq, | |
}; | |
for (int i = 0; i < 4; ++i) | |
{ | |
if (instrs[i]) | |
for_each_r_mbisd (g, *instrs[i], mnemonic, &do_unary_instr_callback, extra); | |
} | |
/* if (instrb) */ | |
/* for_each_r_mbisd (g, *instrb, mnemonic, &do_unary_instr_callback, extra); */ | |
/* if (instrw) */ | |
/* for_each_r_mbisd (g, *instrw, mnemonic, &do_unary_instr_callback, extra); */ | |
/* if (instrd) */ | |
/* for_each_r_mbisd (g, *instrd, mnemonic, &do_unary_instr_callback, extra); */ | |
/* if (instrq) */ | |
/* for_each_r_mbisd (g, *instrq, mnemonic, &do_unary_instr_callback, extra); */ | |
for (unsigned i = 0; i < 16; ++i) | |
{ | |
for (int j = 0; j < 4; ++j) | |
{ | |
if (instrs[j]) | |
{ | |
add_line (g, "%s %s%s", mnemonic, REGISTER_NAMES[j][i], extra); | |
gen_r (g, *instrs[j], *REGISTERS[i]); | |
} | |
} | |
} | |
/* if (instrb) */ | |
/* { */ | |
/* add_line (g, "%s %s%s", mnemonic, REGISTER_BNAMES[i], extra); */ | |
/* gen_r (g, *instrb, *REGISTERS[i]); */ | |
/* } */ | |
/* if (instrw) */ | |
/* { */ | |
/* add_line (g, "%s %s%s", mnemonic, REGISTER_WNAMES[i], extra); */ | |
/* gen_r (g, *instrw, *REGISTERS[i]); */ | |
/* } */ | |
/* if (instrd) */ | |
/* { */ | |
/* add_line (g, "%s %s%s", mnemonic, REGISTER_DNAMES[i], extra); */ | |
/* gen_r (g, *instrd, *REGISTERS[i]); */ | |
/* } */ | |
/* if (instrq) */ | |
/* { */ | |
/* add_line (g, "%s %s%s", mnemonic, REGISTER_QNAMES[i], extra); */ | |
/* gen_r (g, *instrq, *REGISTERS[i]); */ | |
/* } */ | |
/* } */ | |
save_data (g, fname); | |
free (fname); | |
fname = NULL; | |
free (mnemonic); | |
mnemonic = NULL; | |
destroy_test_gen (g); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
do_setcc_callback (Gen *g, | |
X64Instruction instr, | |
const char *mnemonic, | |
int reg_i, | |
int base_i, | |
int index_i, | |
X64Scale scale, | |
int32_t disp, | |
void *context) | |
{ | |
char *bisd = get_bisd_string (instr, base_i, index_i, scale, disp); | |
add_line (g, "%s %s", mnemonic, bisd); | |
if (index_i >= 0) | |
gen_mbisd (g, instr, *REGISTERS[base_i], *REGISTERS[index_i], scale, disp); | |
else | |
gen_mbd (g, instr, *REGISTERS[base_i], disp); | |
free (bisd); | |
bisd = NULL; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
test_setcc (const char *name, const char *ndisasm_name, X64Instruction instr) | |
{ | |
fprintf (stderr, "generating %s...\n", name); | |
Gen *g = create_test_gen (); | |
char *mnemonic = get_lower_case (ndisasm_name); | |
char *name_lc = get_lower_case (name); | |
for_each_r_mbisd (g, instr, mnemonic, &do_setcc_callback, NULL); | |
for (unsigned i = 0; i < 16; ++i) | |
{ | |
add_line (g, "%s %s", mnemonic, REGISTER_BNAMES[i]); | |
gen_r (g, instr, *REGISTERS[i]); | |
} | |
save_data (g, name_lc); | |
free (name_lc); | |
name_lc = NULL; | |
free (mnemonic); | |
mnemonic = NULL; | |
destroy_test_gen (g); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
/* static void */ | |
/* nops (Gen *g, int n) */ | |
/* { */ | |
/* } */ | |
static void | |
test_jcc (const char *name, const char *ndisasm_name, X64Instruction instr) | |
{ | |
fprintf (stderr, "generating %s...\n", name); | |
Gen *g = create_test_gen (); | |
char *mnemonic = get_lower_case (ndisasm_name); | |
char *name_lc = get_lower_case (name); | |
for (int i = 0; i < 123; ++i) | |
{ | |
add_line (g, "nop"); | |
gen_nop (g); | |
} | |
/* syntax hack. */ | |
const char *byte_extra = ""; | |
if (strcmp (mnemonic, "jmp") == 0) | |
{ | |
byte_extra = "short "; | |
} | |
if (instr.rel8_opcode != 0) | |
{ | |
/* byte backwards */ | |
{ | |
uint8_t *dest = g->dest - 123; | |
add_line (g, "%s %s0x%x", mnemonic, byte_extra, (unsigned) (dest - g->base)); | |
gen_rel (g, instr, dest); | |
} | |
/* byte, forwards. */ | |
{ | |
uint8_t *dest = g->dest + 129; | |
add_line (g, "%s %s0x%x", mnemonic, byte_extra, (unsigned) (dest - g->base)); | |
gen_rel (g, instr, dest); | |
} | |
} | |
/* dword, backwards */ | |
{ | |
uint8_t *dest = g->dest - 128; | |
add_line (g, "%s qword 0x%" PRIx64, mnemonic, (int64_t) (dest - g->base)); | |
gen_rel (g, instr, dest); | |
} | |
/* dword, forwards */ | |
{ | |
uint8_t *dest = g->dest + 1024; | |
add_line (g, "%s qword 0x%" PRIx64, mnemonic, (int64_t) (dest - g->base)); | |
gen_rel (g, instr, dest); | |
} | |
save_data (g, name_lc); | |
free (name_lc); | |
name_lc = NULL; | |
free (mnemonic); | |
mnemonic = NULL; | |
destroy_test_gen (g); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
do_test_mem_dest_callback (Gen *g, | |
X64Instruction instr, | |
const char *mnemonic, | |
int base_i, | |
int index_i, | |
X64Scale scale, | |
int32_t disp, | |
void *context) | |
{ | |
int shift = *(int *) context; | |
char *bisd = get_bisd_string (instr, base_i, index_i, scale, disp); | |
int32_t imm = IMM_VALUES[instr.imm_size]; | |
add_line (g, "%s %s %s,%s%s0x%x", | |
mnemonic, | |
get_size_keyword (instr.op_size), | |
bisd, | |
shift ? get_size_keyword (instr.imm_size) : "", | |
shift ? " " : "", | |
imm); | |
if (index_i >= 0) | |
gen_mbisd_imm (g, instr, *REGISTERS[base_i], *REGISTERS[index_i], scale, disp, imm); | |
else | |
gen_mbd_imm (g, instr, *REGISTERS[base_i], disp, imm); | |
free (bisd); | |
bisd = NULL; | |
} | |
static void | |
test_mem_dest_instr (const char *name, const X64Instruction *instrs) | |
{ | |
fprintf (stderr, "generating %s...\n", name); | |
Gen *g = create_test_gen (); | |
char *mnemonic = get_lower_case (name); | |
/* Annoying inconsistency... I should probably actually be | |
treating the shift instruction as their own thing. */ | |
int shift = (strcmp (mnemonic, "shl") == 0 | |
|| strcmp (mnemonic, "shr") == 0 | |
|| strcmp (mnemonic, "sar") == 0); | |
for (int size = 0; size < 4; ++size) | |
{ | |
X64Instruction instr = instrs[size]; | |
for (int i = 0; i < 16; ++i) | |
{ | |
add_line (g, "%s %s,%s%s0x%x", | |
mnemonic, | |
REGISTER_NAMES[size][i], | |
shift ? get_size_keyword (instr.imm_size) : "", | |
shift ? " " : "", | |
IMM_VALUES[instr.imm_size]); | |
gen_r_imm (g, instr, *REGISTERS[i], IMM_VALUES[instr.imm_size]); | |
} | |
for_each_bisd (g, instr, mnemonic, &do_test_mem_dest_callback, &shift); | |
} | |
save_data (g, mnemonic); | |
free (mnemonic); | |
mnemonic = NULL; | |
destroy_test_gen (g); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
do_test_cmovcc_callback (Gen *g, | |
X64Instruction instr, | |
const char *mnemonic, | |
int reg_i, | |
int base_i, | |
int index_i, | |
X64Scale scale, | |
int32_t disp, | |
void *context) | |
{ | |
char *bisd = get_bisd_string (instr, base_i, index_i, scale, disp); | |
add_line (g, "%s %s,%s", mnemonic, REGISTER_NAMES[instr.op_size][reg_i], bisd); | |
if (index_i >= 0) | |
gen_r_mbisd (g, instr, *REGISTERS[reg_i], *REGISTERS[base_i], *REGISTERS[index_i], scale, disp); | |
else | |
gen_r_mbd (g, instr, *REGISTERS[reg_i], *REGISTERS[base_i], disp); | |
free (bisd); | |
bisd = NULL; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
test_cmovcc (const char *name, | |
const char *ndisasm_name, | |
const X64Instruction *instrs) | |
{ | |
fprintf (stderr, "generating %s...\n", name); | |
Gen *g = create_test_gen (); | |
char *mnemonic = get_lower_case (ndisasm_name); | |
char *name_lc = get_lower_case (name); | |
/* There's no byte CMOV. */ | |
for (int size = 1; size < 4; ++size) | |
{ | |
for_each_r_mbisd (g, instrs[size], mnemonic, &do_test_cmovcc_callback, NULL); | |
} | |
save_data (g, name_lc); | |
free (name_lc); | |
name_lc = NULL; | |
free (mnemonic); | |
mnemonic = NULL; | |
destroy_test_gen (g); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
static void | |
test_misc (void) | |
{ | |
Gen *g = create_test_gen (); | |
add_line (g, "ret"); | |
gen_ret (g); | |
add_line (g, "nop"); | |
gen_nop (g); | |
for (int i = 0; i < 16; ++i) | |
{ | |
int64_t imm64 = 0x123456789abcedf0ull; | |
add_line (g, "mov %s,0x%" PRIx64, REGISTER_NAMES[i], imm64); | |
gen_mov_r_imm64 (g, *REGISTERS[i], imm64); | |
} | |
destroy_test_gen (g); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
int main (int argc, char *argv[]) | |
{ | |
const char *what = NULL; | |
if (argc >= 2) | |
what = argv[1]; | |
assert ((X64ModRM) {.mod = 3}.mod_rm_all == 0300); | |
assert ((X64ModRM) {.reg=7}.mod_rm_all == 0070); | |
assert ((X64ModRM) {.rm = 7}.mod_rm_all == 0007); | |
assert ((X64SIB) {.s = 3}.sib_all == 0300); | |
assert ((X64SIB) {.i = 7}.sib_all == 0070); | |
assert ((X64SIB) {.b = 7}.sib_all == 0007); | |
assert ((X64REX) {.b = 1}.rex_all == 0x01); | |
assert ((X64REX) {.x = 1}.rex_all == 0x02); | |
assert ((X64REX) {.r = 1}.rex_all == 0x04); | |
assert ((X64REX) {.w = 1}.rex_all == 0x08); | |
assert ((X64REX) {._ = 15}.rex_all == 0xf0); | |
/* | |
a | |
b - basic | |
c - cmovcc | |
d | |
e | |
f | |
g | |
h | |
i | |
j - jcc | |
k | |
l | |
m - mem dest | |
n | |
o - other | |
p | |
q | |
r | |
s - setcc | |
t | |
u - unary | |
v | |
w | |
x | |
y | |
z | |
*/ | |
if (!what || strchr (what, 'b')) | |
{ | |
#define BASIC_INSTR(NAME, INDEX) test_basic_instr (#NAME, &(NAME##B), &(NAME##W), &(NAME##D), &(NAME##Q)); | |
BASIC_INSTRS | |
; | |
#undef BASIC_INSTR | |
test_basic_instr ("mov", &MOVB, &MOVW, &MOVD, &MOVQ); | |
} | |
if (!what || strchr (what, 'm')) | |
{ | |
#define MEM_DEST_INSTR(NAME, RM, RM_IMM, R, IMM_SIZE) test_mem_dest_instr (#NAME, (X64Instruction[]) {NAME##B, NAME##W, NAME##D, NAME##Q}); | |
MEM_DEST_INSTRS | |
; | |
#undef MEM_DEST_INSTR | |
} | |
if (!what || strchr (what, 'u')) | |
{ | |
#define UNARY_INSTR(NAME, OP, INDEX) test_unary_instr (#NAME, &(NAME##B), &(NAME##W), &(NAME##D), &(NAME##Q)); | |
UNARY_INSTRS | |
; | |
#undef UNARY_INSTR | |
} | |
if (!what || strchr (what, 'o')) | |
{ | |
test_jcc ("call", "call", CALL); | |
test_unary_instr ("call.unary", NULL, NULL, NULL, &CALL); | |
test_jcc ("jmp", "jmp", JMP); | |
test_unary_instr ("jmp.unary", NULL, NULL, NULL, &JMP); | |
test_unary_instr ("push", NULL, NULL, NULL, &PUSHQ); | |
test_unary_instr ("pop", NULL, NULL, NULL, &POPQ); | |
test_basic_instr ("lea", NULL, NULL, NULL, &LEAQ); | |
test_misc (); | |
} | |
#define COND(CC, NDISASM_CC) \ | |
if (!what || strchr (what, 's')) \ | |
test_setcc ("set" #CC, "set" #NDISASM_CC, SET##CC); \ | |
if (!what || strchr (what, 'j')) \ | |
test_jcc ("j" #CC, "j" #NDISASM_CC, J##CC); \ | |
if (!what || strchr (what, 'c')) \ | |
test_cmovcc ("cmov" #CC, "cmov" #NDISASM_CC, (X64Instruction[]) {[1] = CMOVW##CC, CMOVD##CC, CMOVQ##CC}); | |
ALL_CONDITIONS | |
; | |
#undef COND | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment