encode: Remove descriptor table
All relevant information is now encoded directly in the numeric value of the mnemonic, significantly shrinking the size of the encoder.
This commit is contained in:
@@ -499,7 +499,6 @@ TEST("\xc4\xe2\xf1\xbf\x06", VFNMSUB231SDrrm, 0, FE_XMM0, FE_XMM1, FE_MEM(FE_SI,
|
||||
|
||||
// VSIB encodings
|
||||
#ifndef ENC_TEST_TYPESAFE
|
||||
TEST("", VGATHERDPS128rmr, 0, FE_XMM0, FE_XMM0, FE_XMM1); // must have memory operand
|
||||
TEST("", VGATHERDPS128rmr, 0, FE_XMM0, FE_MEM(FE_DI, 8, FE_NOREG, 0), FE_XMM1); // must have SIB
|
||||
TEST("", VGATHERDPS128rmr, 0, FE_XMM0, FE_MEM(FE_IP, 0, FE_NOREG, 0), FE_XMM1); // must have SIB
|
||||
#endif
|
||||
|
||||
270
encode.c
270
encode.c
@@ -14,39 +14,38 @@
|
||||
#define UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
enum {
|
||||
// 16:17 = escape
|
||||
OPC_0F = 1 << 16,
|
||||
OPC_0F38 = 2 << 16,
|
||||
OPC_0F3A = 3 << 16,
|
||||
OPC_ESCAPE_MSK = 3 << 16,
|
||||
OPC_VSIB = 1 << 18,
|
||||
OPC_66 = 1 << 19,
|
||||
OPC_F2 = 1 << 20,
|
||||
OPC_F3 = 1 << 21,
|
||||
OPC_REXW = 1 << 22,
|
||||
OPC_LOCK = 1 << 23,
|
||||
OPC_VEX = 1 << 24,
|
||||
OPC_VEXL = 1 << 25,
|
||||
OPC_REXR = 1 << 28,
|
||||
OPC_REXX = 1 << 27,
|
||||
OPC_REXB = 1 << 26,
|
||||
OPC_REX = 1 << 29,
|
||||
OPC_67 = 1 << 30,
|
||||
};
|
||||
#define OPC_66 0x80000
|
||||
#define OPC_F2 0x100000
|
||||
#define OPC_F3 0x200000
|
||||
#define OPC_REXW 0x400000
|
||||
#define OPC_LOCK 0x800000
|
||||
#define OPC_VEXL0 0x1000000
|
||||
#define OPC_VEXL1 0x1800000
|
||||
#define OPC_EVEXL0 0x2000000
|
||||
#define OPC_EVEXL1 0x2800000
|
||||
#define OPC_EVEXL2 0x3000000
|
||||
#define OPC_EVEXL3 0x3800000
|
||||
#define OPC_EVEXB 0x4000000
|
||||
#define OPC_VSIB 0x8000000
|
||||
#define OPC_67 FE_ADDR32
|
||||
#define OPC_SEG_MSK 0xe0000000
|
||||
#define OPC_JMPL FE_JMPL
|
||||
#define OPC_MASK_MSK 0x1e00000000
|
||||
#define OPC_USER_MSK (OPC_67|OPC_SEG_MSK|OPC_MASK_MSK)
|
||||
#define OPC_GPH_OP0 0x200000000000
|
||||
#define OPC_GPH_OP1 0x400000000000
|
||||
|
||||
#define OPC_SEG_IDX 31
|
||||
#define OPC_SEG_MSK ((uint64_t) 0x7l << OPC_SEG_IDX)
|
||||
#define OPC_VEXOP_IDX 34
|
||||
#define OPC_VEXOP_MSK ((uint64_t) 0xfl << OPC_VEXOP_IDX)
|
||||
#define EPFX_REX_MSK 0x0f
|
||||
#define EPFX_REX 0x08
|
||||
#define EPFX_REXR 0x04
|
||||
#define EPFX_REXX 0x02
|
||||
#define EPFX_REXB 0x01
|
||||
#define EPFX_VVVV_IDX 4
|
||||
|
||||
static bool op_mem(FeOp op) { return op < 0; }
|
||||
static bool op_reg(FeOp op) { return op >= 0; }
|
||||
static bool op_reg_gpl(FeOp op) { return (op & ~0xf) == 0x100; }
|
||||
static bool op_reg_gph(FeOp op) { return (op & ~0x3) == 0x204; }
|
||||
static bool op_reg_seg(FeOp op) { return (op & ~0x7) == 0x300 && (op & 7) < 6; }
|
||||
static bool op_reg_fpu(FeOp op) { return (op & ~0x7) == 0x400; }
|
||||
static bool op_reg_mmx(FeOp op) { return (op & ~0x7) == 0x500; }
|
||||
static bool op_reg_xmm(FeOp op) { return (op & ~0xf) == 0x600; }
|
||||
static int64_t op_mem_offset(FeOp op) { return (int32_t) op; }
|
||||
static unsigned op_mem_base(FeOp op) { return (op >> 32) & 0xfff; }
|
||||
@@ -65,11 +64,13 @@ static bool op_imm_n(FeOp imm, unsigned immsz) {
|
||||
|
||||
static
|
||||
unsigned
|
||||
opc_size(uint64_t opc)
|
||||
opc_size(uint64_t opc, uint64_t epfx)
|
||||
{
|
||||
unsigned res = 1;
|
||||
if (opc & OPC_VEX) {
|
||||
if ((opc & (OPC_REXW|OPC_REXX|OPC_REXB|OPC_ESCAPE_MSK)) != OPC_0F)
|
||||
if (opc & OPC_EVEXL0) {
|
||||
res += 4;
|
||||
} else if (opc & OPC_VEXL0) {
|
||||
if (opc & (OPC_REXW|0x20000) || epfx & (EPFX_REXX|EPFX_REXB))
|
||||
res += 3;
|
||||
else
|
||||
res += 2;
|
||||
@@ -78,63 +79,64 @@ opc_size(uint64_t opc)
|
||||
if (opc & OPC_66) res++;
|
||||
if (opc & OPC_F2) res++;
|
||||
if (opc & OPC_F3) res++;
|
||||
if (opc & (OPC_REX|OPC_REXW|OPC_REXR|OPC_REXX|OPC_REXB)) res++;
|
||||
if (opc & OPC_ESCAPE_MSK) res++;
|
||||
if ((opc & OPC_ESCAPE_MSK) == OPC_0F38 || (opc & OPC_ESCAPE_MSK) == OPC_0F3A) res++;
|
||||
if (opc & OPC_REXW || epfx & EPFX_REX_MSK) res++;
|
||||
if (opc & 0x30000) res++;
|
||||
if (opc & 0x20000) res++;
|
||||
}
|
||||
if (opc & OPC_SEG_MSK) res++;
|
||||
if (opc & OPC_67) res++;
|
||||
if ((opc & 0xc000) == 0xc000) res++;
|
||||
if (opc & 0x8000) res++;
|
||||
return res;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
enc_opc(uint8_t** restrict buf, uint64_t opc)
|
||||
enc_opc(uint8_t** restrict buf, uint64_t opc, uint64_t epfx)
|
||||
{
|
||||
if (opc & OPC_SEG_MSK)
|
||||
*(*buf)++ = (0x65643e362e2600 >> (8 * ((opc >> OPC_SEG_IDX) & 7))) & 0xff;
|
||||
*(*buf)++ = (0x65643e362e2600 >> (8 * ((opc >> 29) & 7))) & 0xff;
|
||||
if (opc & OPC_67) *(*buf)++ = 0x67;
|
||||
if (opc & OPC_VEX) {
|
||||
bool vex3 = (opc & (OPC_REXW|OPC_REXX|OPC_REXB|OPC_ESCAPE_MSK)) != OPC_0F;
|
||||
if (opc & OPC_EVEXL0) {
|
||||
return -1;
|
||||
} else if (opc & OPC_VEXL0) {
|
||||
bool vex3 = opc & (OPC_REXW|0x20000) || epfx & (EPFX_REXX|EPFX_REXB);
|
||||
unsigned pp = 0;
|
||||
if (opc & OPC_66) pp = 1;
|
||||
if (opc & OPC_F3) pp = 2;
|
||||
if (opc & OPC_F2) pp = 3;
|
||||
*(*buf)++ = 0xc4 | !vex3;
|
||||
unsigned b2 = pp | (opc & OPC_VEXL ? 0x4 : 0);
|
||||
unsigned b2 = pp | (opc & 0x800000 ? 0x4 : 0);
|
||||
if (vex3) {
|
||||
unsigned b1 = (opc & OPC_ESCAPE_MSK) >> 16;
|
||||
if (!(opc & OPC_REXR)) b1 |= 0x80;
|
||||
if (!(opc & OPC_REXX)) b1 |= 0x40;
|
||||
if (!(opc & OPC_REXB)) b1 |= 0x20;
|
||||
unsigned b1 = opc >> 16 & 3;
|
||||
if (!(epfx & EPFX_REXR)) b1 |= 0x80;
|
||||
if (!(epfx & EPFX_REXX)) b1 |= 0x40;
|
||||
if (!(epfx & EPFX_REXB)) b1 |= 0x20;
|
||||
*(*buf)++ = b1;
|
||||
if (opc & OPC_REXW) b2 |= 0x80;
|
||||
} else {
|
||||
if (!(opc & OPC_REXR)) b2 |= 0x80;
|
||||
if (!(epfx & EPFX_REXR)) b2 |= 0x80;
|
||||
}
|
||||
b2 |= (~((opc & OPC_VEXOP_MSK) >> OPC_VEXOP_IDX) & 0xf) << 3;
|
||||
b2 |= (~(epfx >> EPFX_VVVV_IDX) & 0xf) << 3;
|
||||
*(*buf)++ = b2;
|
||||
} else {
|
||||
if (opc & OPC_LOCK) *(*buf)++ = 0xF0;
|
||||
if (opc & OPC_66) *(*buf)++ = 0x66;
|
||||
if (opc & OPC_F2) *(*buf)++ = 0xF2;
|
||||
if (opc & OPC_F3) *(*buf)++ = 0xF3;
|
||||
if (opc & (OPC_REX|OPC_REXW|OPC_REXR|OPC_REXX|OPC_REXB))
|
||||
{
|
||||
if (opc & OPC_REXW || epfx & (EPFX_REX_MSK)) {
|
||||
unsigned rex = 0x40;
|
||||
if (opc & OPC_REXW) rex |= 8;
|
||||
if (opc & OPC_REXR) rex |= 4;
|
||||
if (opc & OPC_REXX) rex |= 2;
|
||||
if (opc & OPC_REXB) rex |= 1;
|
||||
if (epfx & EPFX_REXR) rex |= 4;
|
||||
if (epfx & EPFX_REXX) rex |= 2;
|
||||
if (epfx & EPFX_REXB) rex |= 1;
|
||||
*(*buf)++ = rex;
|
||||
}
|
||||
if (opc & OPC_ESCAPE_MSK) *(*buf)++ = 0x0F;
|
||||
if ((opc & OPC_ESCAPE_MSK) == OPC_0F38) *(*buf)++ = 0x38;
|
||||
if ((opc & OPC_ESCAPE_MSK) == OPC_0F3A) *(*buf)++ = 0x3A;
|
||||
if (opc & 0x30000) *(*buf)++ = 0x0F;
|
||||
if ((opc & 0x30000) == 0x20000) *(*buf)++ = 0x38;
|
||||
if ((opc & 0x30000) == 0x30000) *(*buf)++ = 0x3A;
|
||||
}
|
||||
*(*buf)++ = opc & 0xff;
|
||||
if ((opc & 0xc000) == 0xc000) *(*buf)++ = (opc >> 8) & 0xff;
|
||||
if (opc & 0x8000) *(*buf)++ = (opc >> 8) & 0xff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -150,32 +152,32 @@ enc_imm(uint8_t** restrict buf, uint64_t imm, unsigned immsz)
|
||||
|
||||
static
|
||||
int
|
||||
enc_o(uint8_t** restrict buf, uint64_t opc, uint64_t op0)
|
||||
enc_o(uint8_t** restrict buf, uint64_t opc, uint64_t epfx, uint64_t op0)
|
||||
{
|
||||
if (op_reg_idx(op0) & 0x8) opc |= OPC_REXB;
|
||||
if (op_reg_idx(op0) & 0x8) epfx |= EPFX_REXB;
|
||||
|
||||
bool has_rex = !!(opc & (OPC_REX|OPC_REXW|OPC_REXR|OPC_REXX|OPC_REXB));
|
||||
bool has_rex = opc & OPC_REXW || epfx & EPFX_REX_MSK;
|
||||
if (has_rex && op_reg_gph(op0)) return -1;
|
||||
|
||||
if (enc_opc(buf, opc)) return -1;
|
||||
if (enc_opc(buf, opc, epfx)) return -1;
|
||||
*(*buf - 1) = (*(*buf - 1) & 0xf8) | (op_reg_idx(op0) & 0x7);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
enc_mr(uint8_t** restrict buf, uint64_t opc, uint64_t op0, uint64_t op1,
|
||||
unsigned immsz)
|
||||
enc_mr(uint8_t** restrict buf, uint64_t opc, uint64_t epfx, uint64_t op0,
|
||||
uint64_t op1, unsigned immsz)
|
||||
{
|
||||
// If !op_reg(op1), it is a constant value for ModRM.reg
|
||||
if (op_reg(op0) && (op_reg_idx(op0) & 0x8)) opc |= OPC_REXB;
|
||||
if (op_mem(op0) && (op_mem_base(op0) & 0x8)) opc |= OPC_REXB;
|
||||
if (op_mem(op0) && (op_mem_idx(op0) & 0x8)) opc |= OPC_REXX;
|
||||
if (op_reg(op1) && op_reg_idx(op1) & 0x8) opc |= OPC_REXR;
|
||||
if (op_reg(op0) && (op_reg_idx(op0) & 0x8)) epfx |= EPFX_REXB;
|
||||
if (op_mem(op0) && (op_mem_base(op0) & 0x8)) epfx |= EPFX_REXB;
|
||||
if (op_mem(op0) && (op_mem_idx(op0) & 0x8)) epfx |= EPFX_REXX;
|
||||
if (op_reg(op1) && op_reg_idx(op1) & 0x8) epfx |= EPFX_REXR;
|
||||
|
||||
bool has_rex = !!(opc & (OPC_REX|OPC_REXW|OPC_REXR|OPC_REXX|OPC_REXB));
|
||||
bool has_rex = opc & OPC_REXW || epfx & EPFX_REX_MSK;
|
||||
if (has_rex && (op_reg_gph(op0) || op_reg_gph(op1))) return -1;
|
||||
unsigned opcsz = opc_size(opc);
|
||||
unsigned opcsz = opc_size(opc, epfx);
|
||||
|
||||
int mod = 0, reg = op1 & 7, rm;
|
||||
int scale = 0, idx = 4, base = 0;
|
||||
@@ -246,7 +248,7 @@ enc_mr(uint8_t** restrict buf, uint64_t opc, uint64_t op0, uint64_t op1,
|
||||
unsigned dispsz = mod == 1 ? 1 : (mod == 2 || mod0off) ? 4 : 0;
|
||||
if (opcsz + 1 + (mod != 3 && rm == 4) + dispsz + immsz > 15) return -1;
|
||||
|
||||
if (enc_opc(buf, opc)) return -1;
|
||||
if (enc_opc(buf, opc, epfx)) return -1;
|
||||
*(*buf)++ = (mod << 6) | (reg << 3) | rm;
|
||||
if (mod != 3 && rm == 4)
|
||||
*(*buf)++ = (scale << 6) | (idx << 3) | base;
|
||||
@@ -308,104 +310,86 @@ const struct EncodingInfo encoding_infos[ENC_MAX] = {
|
||||
[ENC_MRV] = { .modrm = 0^3, .modreg = 1^3, .vexreg = 2^3 },
|
||||
};
|
||||
|
||||
struct EncodeDesc {
|
||||
uint64_t opc : 26;
|
||||
uint64_t enc : 5;
|
||||
uint64_t immsz : 4;
|
||||
uint64_t alt : 13;
|
||||
uint64_t tys : 16;
|
||||
};
|
||||
|
||||
static const struct EncodeDesc descs[] = {
|
||||
static const uint64_t alt_tab[] = {
|
||||
#include <fadec-encode-private.inc>
|
||||
};
|
||||
|
||||
int
|
||||
fe_enc64_impl(uint8_t** restrict buf, uint64_t mnem, FeOp op0, FeOp op1,
|
||||
fe_enc64_impl(uint8_t** restrict buf, uint64_t opc, FeOp op0, FeOp op1,
|
||||
FeOp op2, FeOp op3)
|
||||
{
|
||||
uint8_t* buf_start = *buf;
|
||||
uint64_t ops[4] = {op0, op1, op2, op3};
|
||||
|
||||
uint64_t desc_idx = mnem & FE_MNEM_MASK;
|
||||
if (UNLIKELY(desc_idx >= FE_MNEM_MAX)) goto fail;
|
||||
uint64_t epfx = 0;
|
||||
// Doesn't change between variants
|
||||
if ((opc & OPC_GPH_OP0) && op_reg_gpl(op0) && op0 >= FE_SP)
|
||||
epfx |= EPFX_REX;
|
||||
else if (!(opc & OPC_GPH_OP0) && op_reg_gph(op0))
|
||||
goto fail;
|
||||
if ((opc & OPC_GPH_OP1) && op_reg_gpl(op1) && op1 >= FE_SP)
|
||||
epfx |= EPFX_REX;
|
||||
else if (!(opc & OPC_GPH_OP1) && op_reg_gph(op1))
|
||||
goto fail;
|
||||
|
||||
do
|
||||
{
|
||||
const struct EncodeDesc* desc = &descs[desc_idx];
|
||||
const struct EncodingInfo* ei = &encoding_infos[desc->enc];
|
||||
uint64_t opc = desc->opc;
|
||||
int64_t imm = 0xcc;
|
||||
unsigned immsz = desc->immsz;
|
||||
try_encode:;
|
||||
unsigned enc = (opc >> 51) & 0x1f;
|
||||
const struct EncodingInfo* ei = &encoding_infos[enc];
|
||||
|
||||
if (UNLIKELY(desc->enc == ENC_INVALID)) goto fail;
|
||||
int64_t imm = 0xcc;
|
||||
unsigned immsz = (opc >> 47) & 0xf;
|
||||
|
||||
if (ei->zregidx)
|
||||
if (op_reg_idx(ops[ei->zregidx^3]) != ei->zregval) goto next;
|
||||
if (UNLIKELY(ei->zregidx && op_reg_idx(ops[ei->zregidx^3]) != ei->zregval))
|
||||
goto next;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
unsigned ty = (desc->tys >> (4 * i)) & 0xf;
|
||||
FeOp op = ops[i];
|
||||
if (ty == 0x0) continue;
|
||||
if (ty == 0xf && !op_mem(op)) goto next;
|
||||
if (ty == 0x1 && !op_reg_gpl(op)) goto next;
|
||||
if (ty == 0x2 && !op_reg_gpl(op) && !op_reg_gph(op)) goto next;
|
||||
if (ty == 0x2 && op_reg_gpl(op) && op >= FE_SP) opc |= OPC_REX;
|
||||
if (ty == 0x3 && !op_reg_seg(op)) goto next;
|
||||
if (ty == 0x4 && !op_reg_fpu(op)) goto next;
|
||||
if (ty == 0x5 && !op_reg_mmx(op)) goto next;
|
||||
if (ty == 0x6 && !op_reg_xmm(op)) goto next;
|
||||
if (UNLIKELY(ty >= 7 && ty < 0xf)) goto next; // TODO: support BND, CR, DR
|
||||
if (UNLIKELY(enc == ENC_S)) {
|
||||
if ((op_reg_idx(op0) << 3 & 0x20) != (opc & 0x20)) goto next;
|
||||
opc |= op_reg_idx(op0) << 3;
|
||||
}
|
||||
|
||||
if (ei->immctl > 0) {
|
||||
imm = ops[ei->immidx];
|
||||
if (ei->immctl == 2) {
|
||||
immsz = UNLIKELY(opc & OPC_67) ? 4 : 8;
|
||||
if (immsz == 4) imm = (int32_t) imm; // address are zero-extended
|
||||
}
|
||||
|
||||
if (UNLIKELY(mnem & FE_ADDR32))
|
||||
opc |= OPC_67;
|
||||
if (UNLIKELY(mnem & FE_SEG_MASK))
|
||||
opc |= (mnem & FE_SEG_MASK) << (OPC_SEG_IDX - 16);
|
||||
if (UNLIKELY(desc->enc == ENC_S)) {
|
||||
if ((op_reg_idx(op0) << 3 & 0x20) != (opc & 0x20)) goto next;
|
||||
opc |= op_reg_idx(op0) << 3;
|
||||
if (ei->immctl == 3) {
|
||||
if (!op_reg_xmm(imm)) goto fail;
|
||||
imm = op_reg_idx(imm) << 4;
|
||||
}
|
||||
|
||||
if (ei->immctl > 0) {
|
||||
imm = ops[ei->immidx];
|
||||
if (ei->immctl == 2) {
|
||||
immsz = UNLIKELY(mnem & FE_ADDR32) ? 4 : 8;
|
||||
if (immsz == 4) imm = (int32_t) imm; // address are zero-extended
|
||||
}
|
||||
if (ei->immctl == 3)
|
||||
imm = op_reg_idx(imm) << 4;
|
||||
if (ei->immctl == 6) {
|
||||
if (UNLIKELY(mnem & FE_JMPL) && desc->alt) goto next;
|
||||
imm -= (int64_t) *buf + opc_size(opc) + immsz;
|
||||
}
|
||||
if (UNLIKELY(ei->immctl == 1) && imm != 1) goto next;
|
||||
if (ei->immctl >= 2 && !op_imm_n(imm, immsz)) goto next;
|
||||
if (ei->immctl == 6) {
|
||||
if (UNLIKELY(opc & FE_JMPL) && opc >> 56) goto next;
|
||||
imm -= (int64_t) *buf + opc_size(opc, epfx) + immsz;
|
||||
}
|
||||
if (UNLIKELY(ei->immctl == 1) && imm != 1) goto next;
|
||||
if (ei->immctl >= 2 && !op_imm_n(imm, immsz)) goto next;
|
||||
}
|
||||
|
||||
// NOP has no operands, so this must be the 32-bit OA XCHG
|
||||
if ((desc->opc & ~7) == 0x90 && ops[0] == FE_AX) goto next;
|
||||
// NOP has no operands, so this must be the 32-bit OA XCHG
|
||||
if ((opc & 0xfffffff) == 0x90 && ops[0] == FE_AX) goto next;
|
||||
|
||||
if (ei->modrm) {
|
||||
FeOp modreg = ei->modreg ? ops[ei->modreg^3] : (opc & 0xff00) >> 8;
|
||||
if (ei->vexreg)
|
||||
opc |= ((uint64_t) op_reg_idx(ops[ei->vexreg^3])) << OPC_VEXOP_IDX;
|
||||
epfx |= ((uint64_t) op_reg_idx(ops[ei->vexreg^3])) << EPFX_VVVV_IDX;
|
||||
if (enc_mr(buf, opc, epfx, ops[ei->modrm^3], modreg, immsz)) goto fail;
|
||||
} else if (ei->modreg) {
|
||||
if (enc_o(buf, opc, epfx, ops[ei->modreg^3])) goto fail;
|
||||
} else {
|
||||
if (enc_opc(buf, opc, epfx)) goto fail;
|
||||
}
|
||||
|
||||
if (ei->modrm) {
|
||||
FeOp modreg = ei->modreg ? ops[ei->modreg^3] : (opc & 0xff00) >> 8;
|
||||
if (enc_mr(buf, opc, ops[ei->modrm^3], modreg, immsz)) goto fail;
|
||||
} else if (ei->modreg) {
|
||||
if (enc_o(buf, opc, ops[ei->modreg^3])) goto fail;
|
||||
} else {
|
||||
if (enc_opc(buf, opc)) goto fail;
|
||||
}
|
||||
if (ei->immctl >= 2)
|
||||
if (enc_imm(buf, imm, immsz)) goto fail;
|
||||
|
||||
if (ei->immctl >= 2)
|
||||
if (enc_imm(buf, imm, immsz)) goto fail;
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
|
||||
next:
|
||||
desc_idx = desc->alt;
|
||||
} while (desc_idx != 0);
|
||||
next:;
|
||||
uint64_t alt = opc >> 56;
|
||||
if (alt) { // try alternative encoding, if available
|
||||
opc = alt_tab[alt] | (opc & OPC_USER_MSK);
|
||||
goto try_encode;
|
||||
}
|
||||
|
||||
fail:
|
||||
// Don't advance buffer on error; though we shouldn't write anything.
|
||||
|
||||
17
fadec-enc.h
17
fadec-enc.h
@@ -32,25 +32,20 @@ typedef int64_t FeOp;
|
||||
|
||||
/** Add segment override prefix. This may or may not generate prefixes for the
|
||||
* ignored prefixes ES/CS/DS/SS in 64-bit mode. **/
|
||||
#define FE_SEG(seg) ((((seg) & 0x7) + 1) << 16)
|
||||
#define FE_SEG(seg) ((uint64_t) (((seg) & 0x7) + 1) << 29)
|
||||
/** Do not use. **/
|
||||
#define FE_SEG_MASK 0x70000
|
||||
#define FE_SEG_MASK 0xe0000000
|
||||
/** Overrides address size. **/
|
||||
#define FE_ADDR32 0x80000
|
||||
#define FE_ADDR32 0x10000000
|
||||
/** Used together with a RIP-relative (conditional) jump, this will force the
|
||||
* use of the encoding with the largest distance. Useful for reserving a jump
|
||||
* when the target offset is still unknown; if the jump is re-encoded later on,
|
||||
* FE_JMPL must be specified there, too, so that the encoding lengths match. **/
|
||||
#define FE_JMPL 0x100000
|
||||
/** Do not use. **/
|
||||
#define FE_MNEM_MASK 0xffff
|
||||
#define FE_JMPL 0x100000000
|
||||
#define FE_MASK(kreg) ((uint64_t) ((kreg) & 0x7) << 33)
|
||||
#define FE_MASKZ(kreg) ((uint64_t) ((kreg) & 0x7) << 33 | 0x1000000000)
|
||||
|
||||
enum {
|
||||
#define FE_MNEMONIC(name,value) name = value,
|
||||
#include <fadec-encode-public.inc>
|
||||
#undef FE_MNEMONIC
|
||||
FE_MNEM_MAX
|
||||
};
|
||||
|
||||
/** Do not use. **/
|
||||
#define fe_enc64_1(buf, mnem, op0, op1, op2, op3, ...) fe_enc64_impl(buf, mnem, op0, op1, op2, op3)
|
||||
|
||||
@@ -660,6 +660,8 @@ def encode_mnems(entries):
|
||||
spec_opcode = spec_opcode._replace(rexw="1")
|
||||
if vecsize == 256:
|
||||
spec_opcode = spec_opcode._replace(vexl="1")
|
||||
if spec_opcode.vexl == "IG":
|
||||
spec_opcode = spec_opcode._replace(vexl="0")
|
||||
|
||||
# Construct mnemonic name
|
||||
mnem_name = {"MOVABS": "MOV", "XCHG_NOP": "XCHG"}.get(desc.mnemonic, desc.mnemonic)
|
||||
@@ -690,42 +692,52 @@ def encode_mnems(entries):
|
||||
def encode_table(entries, args):
|
||||
mnemonics = encode_mnems(entries)
|
||||
mnemonics["NOP", 0, ""] = [(Opcode.parse("90"), InstrDesc.parse("NP - - - - NOP"))]
|
||||
descs = ""
|
||||
alt_index = 0
|
||||
mnem_map = {}
|
||||
alt_table = [0] # first entry is unused
|
||||
for (mnem, opsize, ots), variants in mnemonics.items():
|
||||
indices = [f"FE_{mnem}"] + [f"FE_MNEM_MAX+{alt_index+i}" for i in range(len(variants) - 1)]
|
||||
alt_list = indices[1:] + ["0"]
|
||||
alt_index += len(alt_list) - 1
|
||||
for idx, alt, (opcode, desc) in zip(indices, alt_list, variants):
|
||||
supports_high_regs = []
|
||||
if variants[0][1].mnemonic in ("MOVSX", "MOVZX") or opsize == 8:
|
||||
# Should be the same for all variants
|
||||
desc = variants[0][1]
|
||||
for i, (ot, op) in enumerate(zip(ots, desc.operands)):
|
||||
if ot == "r" and op.kind == "GP" and op.abssize(opsize//8) == 1:
|
||||
supports_high_regs.append(i)
|
||||
|
||||
alt_indices = [i + len(alt_table) for i in range(len(variants) - 1)] + [0]
|
||||
enc_opcs = []
|
||||
for alt, (opcode, desc) in zip(alt_indices, variants):
|
||||
opc_i = opcode.opc
|
||||
if opcode.opcext:
|
||||
opc_i |= opcode.opcext << 8
|
||||
if opcode.modreg and opcode.modreg[0] is not None:
|
||||
opc_i |= opcode.modreg[0] << 8
|
||||
opc_s = f"{opc_i}"
|
||||
opc_s += ["","|OPC_0F","|OPC_0F38","|OPC_0F3A"][opcode.escape]
|
||||
if opcode.prefix in ("66", "F2", "F3", "LOCK"):
|
||||
opc_s += "|OPC_" + opcode.prefix
|
||||
if opsize == 16 and opcode.prefix != "66":
|
||||
opc_s += "|OPC_66"
|
||||
opc_s += "|OPC_VSIB" if "VSIB" in desc.flags else ""
|
||||
opc_s += "|OPC_VEX" if opcode.vex else ""
|
||||
opc_s += "|OPC_VEXL" if opcode.vexl == "1" else ""
|
||||
opc_s += "|OPC_REXW" if opcode.rexw == "1" else ""
|
||||
opc_i |= opcode.escape * 0x10000
|
||||
opc_i |= 0x80000 if opcode.prefix == "66" or opsize == 16 else 0
|
||||
opc_i |= 0x100000 if opcode.prefix == "F2" else 0
|
||||
opc_i |= 0x200000 if opcode.prefix == "F3" else 0
|
||||
opc_i |= 0x400000 if opcode.rexw == "1" else 0
|
||||
if opcode.prefix == "LOCK":
|
||||
opc_i |= 0x800000
|
||||
elif opcode.vex:
|
||||
opc_i |= 0x1000000 + 0x800000 * int(opcode.vexl or 0)
|
||||
opc_i |= 0x8000000 if "VSIB" in desc.flags else 0
|
||||
if alt >= 0x100:
|
||||
raise Exception("encode alternate bits exhausted")
|
||||
opc_i |= sum(1 << i for i in supports_high_regs) << 45
|
||||
opc_i |= desc.imm_size(opsize//8) << 47
|
||||
opc_i |= ["INVALID",
|
||||
"NP", "M", "M1", "MI", "MC", "MR", "RM", "RMA", "MRI", "RMI", "MRC",
|
||||
"AM", "MA", "I", "IA", "O", "OI", "OA", "S", "A", "D", "FD", "TD",
|
||||
"RVM", "RVMI", "RVMR", "RMV", "VM", "VMI", "MVR", "MRV",
|
||||
].index(desc.encoding) << 51
|
||||
opc_i |= alt << 56
|
||||
enc_opcs.append(opc_i)
|
||||
mnem_map[f"FE_{mnem}"] = enc_opcs[0]
|
||||
alt_table += enc_opcs[1:]
|
||||
|
||||
desc = {
|
||||
"enc": f"ENC_{desc.encoding}",
|
||||
"immsz": desc.imm_size(opsize//8),
|
||||
"tys": desc.encode_regtys(ots, opsize//8),
|
||||
"opc": opc_s,
|
||||
"alt": alt,
|
||||
}
|
||||
desc_str = ", ".join(f".{k} = {v}" for k, v in desc.items())
|
||||
descs += f"[{idx}] = {{ {desc_str} }},\n"
|
||||
|
||||
mnem_list = sorted(f"FE_{mnem}" for mnem, _, _ in mnemonics.keys())
|
||||
mnem_tab = "".join(f"FE_MNEMONIC({m},{i})\n" for i, m in enumerate(mnem_list))
|
||||
return mnem_tab, descs
|
||||
mnem_tab = "".join(f"#define {m} {v:#x}\n" for m, v in mnem_map.items())
|
||||
alt_tab = "".join(f"[{i}] = {v:#x},\n" for i, v in enumerate(alt_table))
|
||||
return mnem_tab, alt_tab
|
||||
|
||||
def encode2_table(entries, args):
|
||||
mnemonics = encode_mnems(entries)
|
||||
|
||||
Reference in New Issue
Block a user