x86: Precompute the explicit_count and has_implicit in the Encoding_Flags

This commit is contained in:
gingerBill
2026-06-15 22:25:32 +01:00
parent c53d4ba69e
commit 9fead11180
4 changed files with 2261 additions and 2235 deletions

View File

@@ -706,16 +706,20 @@ encoding_matches_inline :: proc "contextless" (inst: ^Instruction, enc: ^Encodin
// when not in Mode._32.
if enc.flags.mode_32_only && mode != ._32 { return false }
// Count non-implicit encoding operands
encoding_operand_count: u8 = 0
for op_type in enc.ops {
if op_type == .NONE { break }
if !is_implicit_op_inline(op_type) { encoding_operand_count += 1 }
explicit_count := enc.flags.explicit_count
if !enc.flags.has_implicit {
if inst.operand_count != explicit_count { return false }
for i in 0 ..< explicit_count {
eff := mode_rewrite_op_type(enc.ops[i], mode, enc.flags.default_64)
operand_matches_inline(&inst.ops[i], eff) or_return
}
return true
}
// Special case: if user provides exactly one more operand than non-implicit count,
// check if the extra operand matches an implicit operand (e.g., CL for shifts)
if inst.operand_count == encoding_operand_count + 1 {
if inst.operand_count == explicit_count + 1 {
// Check if the last user operand matches an implicit operand in the encoding
last_user_op := &inst.ops[inst.operand_count - 1]
found_matching_implicit := false
@@ -743,7 +747,7 @@ encoding_matches_inline :: proc "contextless" (inst: ^Instruction, enc: ^Encodin
}
// STandard case: operand count must match non-implicit count
if inst.operand_count != encoding_operand_count { return false }
if inst.operand_count != explicit_count { return false }
// Match each user operand against non-implicit encoding operands
user_idx: u8 = 0

View File

@@ -246,18 +246,20 @@ VEX_L :: enum u8 {
// -----------------------------------------------------------------------------
Encoding_Flags :: bit_field u32 {
esc: Escape | 2, // escape sequence
prefix: u8 | 2, // mandatory prefix: 0=none, 1=66, 2=F3, 3=F2
vex_type: VEX_Type | 2, // VEX/EVEX/XOP
vex_w: VEX_W | 2, // VEX.W requirement
vex_l: VEX_L | 2, // VEX.L requirement
default_64: bool | 1, // default to 64-bit operand size (PUSH, POP, etc.)
force_rex_w: bool | 1, // always emit REX.W
no_rex: bool | 1, // REX prefix not allowed (high byte regs)
lock_ok: bool | 1, // LOCK prefix valid
rep_ok: bool | 1, // REP prefix valid
modrm_reg_ext: bool | 1, // ModR/M reg field is opcode extension (use ext field)
mode_32_only: bool | 1, // only valid in Mode._32 (e.g. short-form INC/DEC at 0x40-0x4F)
esc: Escape | 2, // escape sequence
prefix: u8 | 2, // mandatory prefix: 0=none, 1=66, 2=F3, 3=F2
vex_type: VEX_Type | 2, // VEX/EVEX/XOP
vex_w: VEX_W | 2, // VEX.W requirement
vex_l: VEX_L | 2, // VEX.L requirement
default_64: bool | 1, // default to 64-bit operand size (PUSH, POP, etc.)
force_rex_w: bool | 1, // always emit REX.W
no_rex: bool | 1, // REX prefix not allowed (high byte regs)
lock_ok: bool | 1, // LOCK prefix valid
rep_ok: bool | 1, // REP prefix valid
modrm_reg_ext: bool | 1, // ModR/M reg field is opcode extension (use ext field)
mode_32_only: bool | 1, // only valid in Mode._32 (e.g. short-form INC/DEC at 0x40-0x4F)
explicit_count: u8 | 3, // 0..<4 non-implicit operands
has_implicit: bool | 1, // any implicit operand
}
// -----------------------------------------------------------------------------

View File

@@ -135,7 +135,7 @@ write_encoding :: proc(sb: ^strings.Builder, e: lib.Encoding, max_name: int) {
for en, i in e.enc { print_enum_buffered(sb, en, 4, i+1 < len(e.enc)) }
strings.write_string(sb, "}, ")
fmt.sbprintf(sb, "0x%02X, %d, ", e.opcode, e.ext)
write_flags(sb, e.flags)
write_flags(sb, e, e.flags)
strings.write_string(sb, "},\n")
}
@@ -228,7 +228,7 @@ gen_entries :: proc(sb: ^strings.Builder, name, typ: string, entries: []Collecte
strings.write_string(sb, "}, {")
for en, i in e.enc { print_enum_buffered(sb, en, 4, i+1 < len(e.enc)) }
strings.write_string(sb, "}, ")
write_flags(sb, e.flags)
write_flags(sb, nil, e.flags)
strings.write_string(sb, "},\n")
}
strings.write_string(sb, "}\n\n")
@@ -366,7 +366,7 @@ print_enum_buffered :: proc(sb: ^strings.Builder, x: $T, max_name: int, comma: b
// Complete Encoding_Flags emitter -- every field, so ENCODE_FORMS round-trips
// the SoT exactly (mode_32_only is read by the encoder).
write_flags :: proc(sb: ^strings.Builder, flags: lib.Encoding_Flags) {
write_flags :: proc(sb: ^strings.Builder, enc: Maybe(Encoding), flags: lib.Encoding_Flags) {
parts: [dynamic]string
defer delete(parts)
if flags.esc != .NONE { append(&parts, fmt.tprintf("esc=.%v", flags.esc)) }
@@ -381,6 +381,26 @@ write_flags :: proc(sb: ^strings.Builder, flags: lib.Encoding_Flags) {
if flags.rep_ok { append(&parts, "rep_ok=true") }
if flags.modrm_reg_ext { append(&parts, "modrm_reg_ext=true") }
if flags.mode_32_only { append(&parts, "mode_32_only=true") }
if e, ok := enc.?; ok {
encoding_operand_count: u8 = 0
has_implict := false
for op_type in e.ops {
if op_type == .NONE { break }
if lib.is_implicit_op_inline(op_type) {
has_implict = true
} else {
encoding_operand_count += 1
}
}
if encoding_operand_count > 0 {
append(&parts, fmt.tprintf("explicit_count=%d", encoding_operand_count))
}
if has_implict {
append(&parts, "has_implict=true")
}
}
strings.write_string(sb, "{")
for part, i in parts {
if i > 0 { strings.write_string(sb, ", ") }

File diff suppressed because it is too large Load Diff