decode: Minor tweaks for performance

This commit is contained in:
Alexis Engelke
2023-01-13 11:42:13 +01:00
parent 955f139025
commit b48495805e
3 changed files with 65 additions and 65 deletions

112
decode.c
View File

@@ -61,8 +61,7 @@ enum
PREFIX_REXR = 0x04,
PREFIX_REXW = 0x08,
PREFIX_REX = 0x40,
PREFIX_VEXL = 0x10,
PREFIX_REXRR = 0x80,
PREFIX_REXRR = 0x10,
};
struct InstrDesc
@@ -129,6 +128,7 @@ fd_decode(const uint8_t* buffer, size_t len_sz, int mode_int, uintptr_t address,
uint8_t addr_size = mode == DECODE_64 ? 3 : 2;
unsigned prefix_rex = 0;
int rex_off = -1;
unsigned vexl = 0;
unsigned prefix_evex = 0;
instr->segment = FD_REG_NONE;
@@ -192,6 +192,7 @@ prefix_end:
return FD_ERR_PARTIAL;
unsigned opcode_escape = 0;
uint8_t mandatory_prefix = 0; // without escape/VEX/EVEX, this is ignored.
if (buffer[off] == 0x0f)
{
if (UNLIKELY(off + 1 >= len))
@@ -203,8 +204,12 @@ prefix_end:
else
opcode_escape = 1;
off += opcode_escape >= 2 ? 2 : 1;
// If there is no REP/REPNZ prefix offer 66h as mandatory prefix. If
// there is a REP prefix, then the 66h prefix is ignored here.
mandatory_prefix = prefix_rep ? prefix_rep : !!prefix_66;
}
else if (UNLIKELY((buffer[off] & 0xfe) == 0xc4 || buffer[off] == 0x62))
else if (UNLIKELY((unsigned) buffer[off] - 0xc4 < 2 || buffer[off] == 0x62))
{
unsigned vex_prefix = buffer[off];
// VEX (C4/C5) or EVEX (62)
@@ -220,22 +225,24 @@ prefix_end:
return FD_ERR_UD;
uint8_t byte = buffer[off + 1];
prefix_rex |= byte & 0x80 ? 0 : PREFIX_REXR;
if (vex_prefix == 0xc5) // 2-byte VEX
{
opcode_escape = 1 | 4; // 4 is table index with VEX, 0f escape
prefix_rex = byte & 0x80 ? 0 : PREFIX_REXR;
}
else // 3-byte VEX or EVEX
{
prefix_rex |= byte & 0x40 ? 0 : PREFIX_REXX;
// SDM Vol 2A 2-15 (Dec. 2016): Ignored in 32-bit mode
prefix_rex |= mode != DECODE_64 || (byte & 0x20) ? 0 : PREFIX_REXB;
if (mode == DECODE_64)
prefix_rex = byte >> 5 ^ 0x7;
if (vex_prefix == 0x62) // EVEX
{
if (byte & 0x08) // Bit 3 of opcode_escape must be clear.
return FD_ERR_UD;
opcode_escape = (byte & 0x07) | 8; // 8 is table index with EVEX
prefix_rex |= mode != DECODE_64 || (byte & 0x10) ? 0 : PREFIX_REXRR;
_Static_assert(PREFIX_REXRR == 0x10, "wrong REXRR value");
if (mode == DECODE_64)
prefix_rex |= (byte & PREFIX_REXRR) ^ PREFIX_REXRR;
}
else // 3-byte VEX
{
@@ -251,8 +258,7 @@ prefix_end:
prefix_rex |= byte & 0x80 ? PREFIX_REXW : 0;
}
prefix_rep = (byte & 2) ? (byte & 3) : 0;
prefix_66 = (byte & 3) == 1;
mandatory_prefix = byte & 3;
vex_operand = ((byte & 0x78) >> 3) ^ 0xf;
if (vex_prefix == 0x62) // EVEX
@@ -263,7 +269,8 @@ prefix_end:
return FD_ERR_PARTIAL;
byte = buffer[off + 3];
// prefix_evex is z:L'L/RC:b:V':aaa
prefix_evex = byte | 0x08; // Ensure that prefix_evex is non-zero.
vexl = (byte >> 5) & 3;
prefix_evex = byte | 0x100; // Ensure that prefix_evex is non-zero.
if (mode == DECODE_64) // V' causes UD in 32-bit mode
vex_operand |= byte & 0x08 ? 0 : 0x10; // V'
else if (!(byte & 0x08))
@@ -272,8 +279,8 @@ prefix_end:
}
else // VEX
{
prefix_rex |= byte & 0x04 ? PREFIX_VEXL : 0;
off += vex_prefix == 0xc4 ? 3 : 2;
vexl = byte & 0x04 ? 1 : 0;
off += 0xc7 - vex_prefix; // 3 for c4, 2 for c5
}
skipvex:;
@@ -286,9 +293,6 @@ prefix_end:
// Handle mandatory prefixes (which behave like an opcode ext.).
if (kind == ENTRY_TABLE_PREFIX)
{
// If there is no REP/REPNZ prefix offer 66h as mandatory prefix. If
// there is a REP prefix, then the 66h prefix is ignored here.
uint8_t mandatory_prefix = prefix_rep ? prefix_rep : !!prefix_66;
table_idx = table_walk(table_idx, mandatory_prefix, &kind);
}
@@ -306,10 +310,9 @@ prefix_end:
{
uint8_t index = 0;
index |= prefix_rex & PREFIX_REXW ? (1 << 0) : 0;
index |= prefix_rex & PREFIX_VEXL ? (1 << 1) : 0;
// When EVEX.L'L is the rounding mode, the instruction must not have
// L'L constraints.
index |= (prefix_evex >> 4) & 6;
index |= vexl << 1;
table_idx = table_walk(table_idx, index, &kind);
}
@@ -338,7 +341,6 @@ prefix_end:
return FD_ERR_PARTIAL;
unsigned op_byte = buffer[off - 1] | (!DESC_MODRM(desc) ? 0xc0 : 0);
unsigned vexl = !!(prefix_rex & PREFIX_VEXL);
if (UNLIKELY(prefix_evex)) {
// VSIB inst (gather/scatter) without mask register or w/EVEX.z is UD
if (DESC_VSIB(desc) && (!(prefix_evex & 0x07) || (prefix_evex & 0x80)))
@@ -351,7 +353,6 @@ prefix_end:
if ((prefix_evex & 0x87) == 0x80)
return FD_ERR_UD;
vexl = (prefix_evex >> 5) & 3;
// Cases for SAE/RC (reg operands only):
// - ER supported -> all ok
// - SAE supported -> assume L'L is RC, but ignored (undocumented)
@@ -369,6 +370,9 @@ prefix_end:
return FD_ERR_UD;
instr->evex = prefix_evex & 0x87; // clear RC, clear B
}
if (DESC_VSIB(desc))
vex_operand &= 0xf; // EVEX.V' is used as index extension instead.
} else {
instr->evex = 0;
}
@@ -420,17 +424,15 @@ prefix_end:
{
FdOp* op_modreg = &instr->operands[DESC_MODREG_IDX(desc)];
unsigned reg_idx = (op_byte & 0x38) >> 3;
unsigned reg_ty = DESC_REGTY_MODREG(desc); // GPL VEC MSK - MMX SEG
op_modreg->misc = (0350761 >> (3 * reg_ty)) & 0x7;
if (LIKELY(!(reg_ty & 4)))
unsigned reg_ty = DESC_REGTY_MODREG(desc);
op_modreg->misc = reg_ty;
if (LIKELY(reg_ty < 2))
reg_idx += prefix_rex & PREFIX_REXR ? 8 : 0;
if (reg_ty == 2 && reg_idx >= 8) // REXR can't be set in 32-bit mode
return FD_ERR_UD;
if (reg_ty == 2 && (prefix_evex & 0x80)) // EVEX.z with mask as dest
return FD_ERR_UD;
if (reg_ty == 1) // PREFIX_REXRR ignored above in 32-bit mode
else if (reg_ty == 7 && (prefix_rex & PREFIX_REXR || prefix_evex & 0x80))
return FD_ERR_UD; // REXR in 64-bit mode or EVEX.z with mask as dest
if (UNLIKELY(reg_ty == FD_RT_VEC)) // REXRR ignored above in 32-bit mode
reg_idx += prefix_rex & PREFIX_REXRR ? 16 : 0;
else if (prefix_rex & PREFIX_REXRR)
else if (UNLIKELY(prefix_rex & PREFIX_REXRR))
return FD_ERR_UD;
op_modreg->type = FD_OT_REG;
op_modreg->size = operand_sizes[(desc->operand_sizes >> 2) & 3];
@@ -442,22 +444,22 @@ prefix_end:
FdOp* op_modrm = &instr->operands[DESC_MODRM_IDX(desc)];
op_modrm->size = operand_sizes[(desc->operand_sizes >> 0) & 3];
unsigned mod = op_byte & 0xc0;
unsigned rm = op_byte & 0x07;
if (mod == 0xc0)
if (op_byte >= 0xc0)
{
uint8_t reg_idx = rm;
unsigned reg_ty = DESC_REGTY_MODRM(desc); // GPL VEC - - MMX FPU MSK
op_modrm->misc = (07450061 >> (3 * reg_ty)) & 0x7;
if (LIKELY(!(reg_ty & 4)))
unsigned reg_ty = DESC_REGTY_MODRM(desc);
op_modrm->misc = reg_ty;
if (LIKELY(reg_ty < 2))
reg_idx += prefix_rex & PREFIX_REXB ? 8 : 0;
if (prefix_evex && reg_ty == 1) // vector registers only
if (prefix_evex && reg_ty == 0) // vector registers only
reg_idx += prefix_rex & PREFIX_REXX ? 16 : 0;
op_modrm->type = FD_OT_REG;
op_modrm->reg = reg_idx;
}
else
{
unsigned mod = op_byte & 0xc0;
bool vsib = UNLIKELY(DESC_VSIB(desc));
// SIB byte
@@ -473,9 +475,9 @@ prefix_end:
base = sib & 0x07;
if (!vsib && idx == 4)
idx = FD_REG_NONE;
if (vsib) {
idx |= vex_operand & 0x10;
vex_operand &= 0xf;
if (vsib && prefix_evex) {
// EVEX.V':EVEX.X:SIB.idx
idx |= prefix_evex & 0x8 ? 0 : 0x10;
}
op_modrm->misc = scale | idx;
}
@@ -488,11 +490,19 @@ prefix_end:
}
// EVEX.z for memory destination operand is UD.
if (DESC_MODRM_IDX(desc) == 0 && (prefix_evex & 0x80))
if (UNLIKELY(prefix_evex & 0x80) && DESC_MODRM_IDX(desc) == 0)
return FD_ERR_UD;
// RIP-relative addressing only if SIB-byte is absent
if (mod == 0 && rm == 5 && mode == DECODE_64)
op_modrm->reg = FD_REG_IP;
else if (mod == 0 && base == 5)
op_modrm->reg = FD_REG_NONE;
else
op_modrm->reg = base + (prefix_rex & PREFIX_REXB ? 8 : 0);
// EVEX.b for memory-operand without broadcast support is UD.
unsigned scale = op_modrm->size - 1;
unsigned scale = 0;
if (UNLIKELY(prefix_evex & 0x10)) {
if (UNLIKELY(!DESC_EVEX_BCST(desc)))
return FD_ERR_UD;
@@ -503,27 +513,19 @@ prefix_end:
instr->segment |= scale << 6; // Store broadcast size
op_modrm->type = FD_OT_MEMBCST;
} else {
if (UNLIKELY(prefix_evex))
scale = op_modrm->size - 1;
op_modrm->type = FD_OT_MEM;
}
// RIP-relative addressing only if SIB-byte is absent
if (mod == 0 && rm == 5 && mode == DECODE_64)
op_modrm->reg = FD_REG_IP;
else if (mod == 0 && base == 5)
op_modrm->reg = FD_REG_NONE;
else
op_modrm->reg = base + (prefix_rex & PREFIX_REXB ? 8 : 0);
if (mod == 0x40)
if (op_byte & 0x40)
{
if (UNLIKELY(off + 1 > len))
return FD_ERR_PARTIAL;
instr->disp = (int8_t) LOAD_LE_1(&buffer[off]);
if (prefix_evex)
instr->disp <<= scale;
instr->disp = (int8_t) LOAD_LE_1(&buffer[off]) << scale;
off += 1;
}
else if (mod == 0x80 || (mod == 0 && base == 5))
else if (op_byte & 0x80 || (mod == 0 && base == 5))
{
if (UNLIKELY(off + 4 > len))
return FD_ERR_PARTIAL;
@@ -550,11 +552,11 @@ prefix_end:
// EVEX.vvvv to refer to non-vector registers. Verified in parseinstrs.
operand->reg = vex_operand | DESC_ZEROREG_VAL(desc);
unsigned reg_ty = DESC_REGTY_VEXREG(desc); // GPL VEC MSK FPU
unsigned reg_ty = DESC_REGTY_VEXREG(desc); // VEC GPL MSK FPU
// In 64-bit mode: UD if FD_RT_MASK and vex_operand&8 != 0
if (reg_ty == 2 && vex_operand >= 8)
return FD_ERR_UD;
operand->misc = (04761 >> (3 * reg_ty)) & 0x7;
operand->misc = (04710 >> (3 * reg_ty)) & 0x7;
}
else if (vex_operand != 0)
{
@@ -562,7 +564,7 @@ prefix_end:
return FD_ERR_UD;
}
uint32_t imm_control = DESC_IMM_CONTROL(desc);
uint32_t imm_control = UNLIKELY(DESC_IMM_CONTROL(desc));
if (UNLIKELY(imm_control == 1))
{
// 1 = immediate constant 1, used for shifts

View File

@@ -53,8 +53,8 @@ typedef enum {
} FdOpType;
typedef enum {
/** Register type is encoded in mnemonic **/
FD_RT_IMP = 0,
/** Vector (SSE/AVX) register XMMn/YMMn/ZMMn **/
FD_RT_VEC = 0,
/** Low general purpose register **/
FD_RT_GPL = 1,
/** High-byte general purpose register **/
@@ -65,8 +65,6 @@ typedef enum {
FD_RT_FPU = 4,
/** MMX register MMn **/
FD_RT_MMX = 5,
/** Vector (SSE/AVX) register XMMn/YMMn/ZMMn **/
FD_RT_VEC = 6,
/** Vector mask (AVX-512) register Kn **/
FD_RT_MASK = 7,
/** Bound register BNDn **/

View File

@@ -181,12 +181,12 @@ class InstrDesc(NamedTuple):
flags: FrozenSet[str]
OPKIND_REGTYS = {
("modrm", "GP"): 0, ("modreg", "GP"): 0, ("vexreg", "GP"): 0,
("modrm", "XMM"): 1, ("modreg", "XMM"): 1, ("vexreg", "XMM"): 1,
("modrm", "MMX"): 4, ("modreg", "MMX"): 4,
("modrm", "FPU"): 5, ("vexreg", "FPU"): 3,
("modrm", "MASK"): 6, ("modreg", "MASK"): 2, ("vexreg", "MASK"): 2,
("modreg", "SEG"): 5,
("modrm", "GP"): 1, ("modreg", "GP"): 1, ("vexreg", "GP"): 1,
("modrm", "XMM"): 0, ("modreg", "XMM"): 0, ("vexreg", "XMM"): 0,
("modrm", "MMX"): 5, ("modreg", "MMX"): 5,
("modrm", "FPU"): 4, ("vexreg", "FPU"): 3,
("modrm", "MASK"): 7, ("modreg", "MASK"): 7, ("vexreg", "MASK"): 2,
("modreg", "SEG"): 3,
("modreg", "DR"): 0, # handled in code
("modreg", "CR"): 0, # handled in code
("modrm", "MEM"): 0,