x86-asm: Reorganize instr_type
Disjoint instruction types don't need to be a bit field, so introduce an enumeration (3 bits). Also the 0x0f prefix can be expressed by a bit, doesn't need a byte in the opcode field. That enables to encode further prefixes still in 16 bit. To not have to touch all insns do some macro fiddling filtering out a 0x0f byte in the second position.
This commit is contained in:
parent
4af6e087dd
commit
bde802df29
3 changed files with 65 additions and 46 deletions
107
i386-asm.c
107
i386-asm.c
|
@ -29,19 +29,26 @@
|
||||||
#define TOK_ASM_last TOK_ASM_emms
|
#define TOK_ASM_last TOK_ASM_emms
|
||||||
#define TOK_ASM_alllast TOK_ASM_subps
|
#define TOK_ASM_alllast TOK_ASM_subps
|
||||||
|
|
||||||
#define OPC_JMP 0x01 /* jmp operand */
|
#define OPC_B 0x01 /* only used with OPC_WL */
|
||||||
#define OPC_B 0x02 /* only used with OPC_WL */
|
#define OPC_WL 0x02 /* accepts w, l or no suffix */
|
||||||
#define OPC_WL 0x04 /* accepts w, l or no suffix */
|
|
||||||
#define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */
|
#define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */
|
||||||
#define OPC_REG 0x08 /* register is added to opcode */
|
#define OPC_REG 0x04 /* register is added to opcode */
|
||||||
#define OPC_MODRM 0x10 /* modrm encoding */
|
#define OPC_MODRM 0x08 /* modrm encoding */
|
||||||
#define OPC_FWAIT 0x20 /* add fwait opcode */
|
|
||||||
#define OPC_TEST 0x40 /* test opcodes */
|
#define OPCT_MASK 0x70
|
||||||
#define OPC_SHIFT 0x80 /* shift opcodes */
|
#define OPC_FWAIT 0x10 /* add fwait opcode */
|
||||||
#define OPC_D16 0x0100 /* generate data16 prefix */
|
#define OPC_SHIFT 0x20 /* shift opcodes */
|
||||||
#define OPC_ARITH 0x0200 /* arithmetic opcodes */
|
#define OPC_ARITH 0x30 /* arithmetic opcodes */
|
||||||
#define OPC_SHORTJMP 0x0400 /* short jmp operand */
|
#define OPC_FARITH 0x40 /* FPU arithmetic opcodes */
|
||||||
#define OPC_FARITH 0x0800 /* FPU arithmetic opcodes */
|
#define OPC_TEST 0x50 /* test opcodes */
|
||||||
|
#define OPC_JMP_TEST 0x60 /* A test and jmp opcode */
|
||||||
|
#define OPC_JMP 0x70 /* A non-testing jmp opcode */
|
||||||
|
#define OPCT_IS(v,i) (((v) & OPCT_MASK) == (i))
|
||||||
|
|
||||||
|
#define OPC_SHORTJMP 0x80 /* short jmp operand */
|
||||||
|
|
||||||
|
#define OPC_0F 0x100 /* Is secondary map (0x0f prefix) */
|
||||||
|
#define OPC_D16 0x0800 /* generate data16 prefix */
|
||||||
#ifdef TCC_TARGET_X86_64
|
#ifdef TCC_TARGET_X86_64
|
||||||
# define OPC_WLQ 0x1000 /* accepts w, l, q or no suffix */
|
# define OPC_WLQ 0x1000 /* accepts w, l, q or no suffix */
|
||||||
# define OPC_BWLQ (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */
|
# define OPC_BWLQ (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */
|
||||||
|
@ -211,11 +218,15 @@ static const uint8_t segment_prefixes[] = {
|
||||||
|
|
||||||
static const ASMInstr asm_instrs[] = {
|
static const ASMInstr asm_instrs[] = {
|
||||||
#define ALT(x) x
|
#define ALT(x) x
|
||||||
|
/* This removes a 0x0f in the second byte */
|
||||||
|
#define O(o) ((((o) & 0xff00) == 0x0f00) ? ((((o) >> 8) & ~0xff) | ((o) & 0xff)) : (o))
|
||||||
|
/* This constructs instr_type from opcode, type and group. */
|
||||||
|
#define T(o,i,g) ((i) | ((g) << OPC_GROUP_SHIFT) | ((((o) & 0xff00) == 0x0f00) ? OPC_0F : 0))
|
||||||
#define DEF_ASM_OP0(name, opcode)
|
#define DEF_ASM_OP0(name, opcode)
|
||||||
#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 },
|
#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 0 },
|
||||||
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }},
|
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 1, { op0 }},
|
||||||
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }},
|
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 2, { op0, op1 }},
|
||||||
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 3, { op0, op1, op2 }},
|
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 3, { op0, op1, op2 }},
|
||||||
#ifdef TCC_TARGET_X86_64
|
#ifdef TCC_TARGET_X86_64
|
||||||
# include "x86_64-asm.h"
|
# include "x86_64-asm.h"
|
||||||
#else
|
#else
|
||||||
|
@ -594,12 +605,13 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
|
||||||
/* optimize matching by using a lookup table (no hashing is needed
|
/* optimize matching by using a lookup table (no hashing is needed
|
||||||
!) */
|
!) */
|
||||||
for(pa = asm_instrs; pa->sym != 0; pa++) {
|
for(pa = asm_instrs; pa->sym != 0; pa++) {
|
||||||
|
int it = pa->instr_type & OPCT_MASK;
|
||||||
s = 0;
|
s = 0;
|
||||||
if (pa->instr_type & OPC_FARITH) {
|
if (it == OPC_FARITH) {
|
||||||
v = opcode - pa->sym;
|
v = opcode - pa->sym;
|
||||||
if (!((unsigned)v < 8 * 6 && (v % 6) == 0))
|
if (!((unsigned)v < 8 * 6 && (v % 6) == 0))
|
||||||
continue;
|
continue;
|
||||||
} else if (pa->instr_type & OPC_ARITH) {
|
} else if (it == OPC_ARITH) {
|
||||||
if (!(opcode >= pa->sym && opcode < pa->sym + 8*NBWLX))
|
if (!(opcode >= pa->sym && opcode < pa->sym + 8*NBWLX))
|
||||||
continue;
|
continue;
|
||||||
s = (opcode - pa->sym) % NBWLX;
|
s = (opcode - pa->sym) % NBWLX;
|
||||||
|
@ -612,11 +624,11 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
|
||||||
continue;
|
continue;
|
||||||
s++;
|
s++;
|
||||||
}
|
}
|
||||||
} else if (pa->instr_type & OPC_SHIFT) {
|
} else if (it == OPC_SHIFT) {
|
||||||
if (!(opcode >= pa->sym && opcode < pa->sym + 7*NBWLX))
|
if (!(opcode >= pa->sym && opcode < pa->sym + 7*NBWLX))
|
||||||
continue;
|
continue;
|
||||||
s = (opcode - pa->sym) % NBWLX;
|
s = (opcode - pa->sym) % NBWLX;
|
||||||
} else if (pa->instr_type & OPC_TEST) {
|
} else if (it == OPC_TEST || it == OPC_JMP_TEST) {
|
||||||
if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES))
|
if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES))
|
||||||
continue;
|
continue;
|
||||||
/* cmovxx is a test opcode but accepts multiple sizes.
|
/* cmovxx is a test opcode but accepts multiple sizes.
|
||||||
|
@ -650,7 +662,8 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
|
||||||
should only be done if we really have an >32bit imm64, and that
|
should only be done if we really have an >32bit imm64, and that
|
||||||
is hardcoded. Ignore it here. */
|
is hardcoded. Ignore it here. */
|
||||||
if (pa->opcode == 0xb0 && ops[0].type != OP_IM64
|
if (pa->opcode == 0xb0 && ops[0].type != OP_IM64
|
||||||
&& ops[1].type == OP_REG64)
|
&& ops[1].type == OP_REG64
|
||||||
|
&& !(pa->instr_type & OPC_0F))
|
||||||
continue;
|
continue;
|
||||||
#endif
|
#endif
|
||||||
/* now decode and check each operand */
|
/* now decode and check each operand */
|
||||||
|
@ -777,12 +790,14 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* now generates the operation */
|
/* now generates the operation */
|
||||||
if (pa->instr_type & OPC_FWAIT)
|
if (OPCT_IS(pa->instr_type, OPC_FWAIT))
|
||||||
g(0x9b);
|
g(0x9b);
|
||||||
if (seg_prefix)
|
if (seg_prefix)
|
||||||
g(seg_prefix);
|
g(seg_prefix);
|
||||||
|
|
||||||
v = pa->opcode;
|
v = pa->opcode;
|
||||||
|
if (pa->instr_type & OPC_0F)
|
||||||
|
v = ((v & ~0xff) << 8) | 0x0f00 | (v & 0xff);
|
||||||
if ((v == 0x69 || v == 0x6b) && nb_ops == 2) {
|
if ((v == 0x69 || v == 0x6b) && nb_ops == 2) {
|
||||||
/* kludge for imul $im, %reg */
|
/* kludge for imul $im, %reg */
|
||||||
nb_ops = 3;
|
nb_ops = 3;
|
||||||
|
@ -802,25 +817,23 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
|
||||||
} else if (v <= 0x05) {
|
} else if (v <= 0x05) {
|
||||||
/* arith case */
|
/* arith case */
|
||||||
v += ((opcode - TOK_ASM_addb) / NBWLX) << 3;
|
v += ((opcode - TOK_ASM_addb) / NBWLX) << 3;
|
||||||
} else if ((pa->instr_type & (OPC_FARITH | OPC_MODRM)) == OPC_FARITH) {
|
} else if ((pa->instr_type & (OPCT_MASK | OPC_MODRM)) == OPC_FARITH) {
|
||||||
/* fpu arith case */
|
/* fpu arith case */
|
||||||
v += ((opcode - pa->sym) / 6) << 3;
|
v += ((opcode - pa->sym) / 6) << 3;
|
||||||
}
|
}
|
||||||
if (pa->instr_type & OPC_REG) {
|
if (pa->instr_type & OPC_REG) {
|
||||||
|
/* mov $im, %reg case */
|
||||||
|
if (v == 0xb0 && s >= 1)
|
||||||
|
v += 7;
|
||||||
for(i = 0; i < nb_ops; i++) {
|
for(i = 0; i < nb_ops; i++) {
|
||||||
if (op_type[i] & (OP_REG | OP_ST)) {
|
if (op_type[i] & (OP_REG | OP_ST)) {
|
||||||
v += ops[i].reg;
|
v += ops[i].reg;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* mov $im, %reg case */
|
|
||||||
if (pa->opcode == 0xb0 && s >= 1)
|
|
||||||
v += 7;
|
|
||||||
}
|
}
|
||||||
if (pa->instr_type & OPC_B)
|
if (pa->instr_type & OPC_B)
|
||||||
v += s >= 1;
|
v += s >= 1;
|
||||||
if (pa->instr_type & OPC_TEST)
|
|
||||||
v += test_bits[opcode - pa->sym];
|
|
||||||
if (pa->instr_type & OPC_SHORTJMP) {
|
if (pa->instr_type & OPC_SHORTJMP) {
|
||||||
Sym *sym;
|
Sym *sym;
|
||||||
int jmp_disp;
|
int jmp_disp;
|
||||||
|
@ -839,32 +852,36 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
|
||||||
op_type[0] = OP_IM8S;
|
op_type[0] = OP_IM8S;
|
||||||
} else {
|
} else {
|
||||||
no_short_jump:
|
no_short_jump:
|
||||||
if (pa->instr_type & OPC_JMP) {
|
/* long jump will be allowed. need to modify the
|
||||||
/* long jump will be allowed. need to modify the
|
opcode slightly */
|
||||||
opcode slightly */
|
if (v == 0xeb) /* jmp */
|
||||||
if (v == 0xeb)
|
v = 0xe9;
|
||||||
v = 0xe9;
|
else if (v == 0x70) /* jcc */
|
||||||
else
|
v += 0x0f10;
|
||||||
v += 0x0f10;
|
else
|
||||||
} else {
|
tcc_error("invalid displacement");
|
||||||
tcc_error("invalid displacement");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
op1 = v >> 8;
|
if (OPCT_IS(pa->instr_type, OPC_TEST)
|
||||||
|
|| OPCT_IS(pa->instr_type, OPC_JMP_TEST))
|
||||||
|
v += test_bits[opcode - pa->sym];
|
||||||
|
op1 = v >> 16;
|
||||||
|
if (op1)
|
||||||
|
g(op1);
|
||||||
|
op1 = (v >> 8) & 0xff;
|
||||||
if (op1)
|
if (op1)
|
||||||
g(op1);
|
g(op1);
|
||||||
g(v);
|
g(v);
|
||||||
|
|
||||||
/* search which operand will used for modrm */
|
/* search which operand will used for modrm */
|
||||||
modrm_index = 0;
|
modrm_index = 0;
|
||||||
if (pa->instr_type & OPC_SHIFT) {
|
if (OPCT_IS(pa->instr_type, OPC_SHIFT)) {
|
||||||
reg = (opcode - pa->sym) / NBWLX;
|
reg = (opcode - pa->sym) / NBWLX;
|
||||||
if (reg == 6)
|
if (reg == 6)
|
||||||
reg = 7;
|
reg = 7;
|
||||||
} else if (pa->instr_type & OPC_ARITH) {
|
} else if (OPCT_IS(pa->instr_type, OPC_ARITH)) {
|
||||||
reg = (opcode - pa->sym) / NBWLX;
|
reg = (opcode - pa->sym) / NBWLX;
|
||||||
} else if (pa->instr_type & OPC_FARITH) {
|
} else if (OPCT_IS(pa->instr_type, OPC_FARITH)) {
|
||||||
reg = (opcode - pa->sym) / 6;
|
reg = (opcode - pa->sym) / 6;
|
||||||
} else {
|
} else {
|
||||||
reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7;
|
reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7;
|
||||||
|
@ -902,7 +919,8 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
|
||||||
|
|
||||||
/* emit constants */
|
/* emit constants */
|
||||||
#ifndef TCC_TARGET_X86_64
|
#ifndef TCC_TARGET_X86_64
|
||||||
if (pa->opcode == 0x9a || pa->opcode == 0xea) {
|
if (!(pa->instr_type & OPC_0F)
|
||||||
|
&& (pa->opcode == 0x9a || pa->opcode == 0xea)) {
|
||||||
/* ljmp or lcall kludge */
|
/* ljmp or lcall kludge */
|
||||||
gen_expr32(&ops[1].e);
|
gen_expr32(&ops[1].e);
|
||||||
if (ops[0].e.sym)
|
if (ops[0].e.sym)
|
||||||
|
@ -938,7 +956,8 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
|
||||||
} else if (v & OP_IM64) {
|
} else if (v & OP_IM64) {
|
||||||
gen_expr64(&ops[i].e);
|
gen_expr64(&ops[i].e);
|
||||||
#endif
|
#endif
|
||||||
} else if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) {
|
} else if (OPCT_IS (pa->instr_type, OPC_JMP)
|
||||||
|
|| OPCT_IS (pa->instr_type, OPC_JMP_TEST)) {
|
||||||
gen_disp32(&ops[i].e);
|
gen_disp32(&ops[i].e);
|
||||||
} else {
|
} else {
|
||||||
gen_expr32(&ops[i].e);
|
gen_expr32(&ops[i].e);
|
||||||
|
|
|
@ -221,7 +221,7 @@ ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
|
||||||
DEF_ASM_OP0(lret, 0xcb)
|
DEF_ASM_OP0(lret, 0xcb)
|
||||||
ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
|
ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
|
||||||
|
|
||||||
ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
|
ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP_TEST, OPT_ADDR))
|
||||||
DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||||||
DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||||||
DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||||||
|
|
|
@ -226,7 +226,7 @@ ALT(DEF_ASM_OP1(retq, 0xc2, 0, 0, OPT_IM16))
|
||||||
DEF_ASM_OP0(lret, 0xcb)
|
DEF_ASM_OP0(lret, 0xcb)
|
||||||
ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
|
ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
|
||||||
|
|
||||||
ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
|
ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP_TEST, OPT_ADDR))
|
||||||
DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||||||
DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||||||
DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||||||
|
|
Loading…
Add table
Reference in a new issue