Minor code style changes

This commit is contained in:
gingerBill
2026-06-14 21:00:38 +01:00
parent 2f0c1457e5
commit 15a426c6b3
6 changed files with 198 additions and 152 deletions

View File

@@ -683,7 +683,7 @@ decode_operands_vex :: proc(state: ^Decoder_State, entry: ^VEX_Decode_Entry) ->
decode_single_operand :: proc(state: ^Decoder_State, op_type: Operand_Type, op_enc: Operand_Encoding,
modrm_info: ModRM_Info, sib_info: SIB_Info, has_sib: bool) -> (op: Operand, err: Error_Code) {
#partial switch op_enc {
switch op_enc {
case .NONE:
return {}, .NONE
@@ -805,8 +805,7 @@ decode_single_operand :: proc(state: ^Decoder_State, op_type: Operand_Type, op_e
}
decode_single_operand_vex :: proc(state: ^Decoder_State, op_type: Operand_Type, op_enc: Operand_Encoding,
modrm_info: ModRM_Info, sib_info: SIB_Info, has_sib: bool) -> (op: Operand, err: Error_Code) {
modrm_info: ModRM_Info, sib_info: SIB_Info, has_sib: bool) -> (op: Operand, err: Error_Code) {
#partial switch op_enc {
case .REG:
// Register in ModR/M.reg, extended by VEX.R

View File

@@ -113,7 +113,8 @@ encode :: proc(
invalid := false
for i in 0..<inst.operand_count {
op := &inst.ops[i]
if op.kind == .REGISTER {
#partial switch op.kind {
case .REGISTER:
// R8-R15, XMM8-31, YMM8-31, ZMM8-31 require REX/VEX/EVEX extension.
if reg_needs_rex(op.reg) { invalid = true; break }
// SPL/BPL/SIL/DIL (REG_GPR8 hw 4-7) don't exist in i386;
@@ -123,7 +124,7 @@ encode :: proc(
hw := reg_hw(op.reg)
if hw >= 4 && hw <= 7 { invalid = true; break }
}
} else if op.kind == .MEMORY {
case .MEMORY:
m := op.mem
if (mem_has_base(m) && m.base_ext) ||
(mem_has_index(m) && m.index_ext) {
@@ -198,7 +199,8 @@ encode :: proc(
// --- VEX/EVEX or Legacy Encoding ---
if enc.flags.vex_type == .VEX {
#partial switch enc.flags.vex_type{
case .VEX:
// VEX prefix encoding
r: u8 = 1; x: u8 = 1; b: u8 = 1
vvvv: u8 = 0xF; l: u8 = 0; pp: u8 = 0; mmmmm: u8 = 1; w: u8 = 0
@@ -224,19 +226,20 @@ encode :: proc(
}
// Check operands for REX bits
for i in 0..<4 {
for enc_type, i in enc.enc {
user_op := get_user_op_inline(&inst, enc, i)
if user_op == nil { continue }
#partial switch enc.enc[i] {
#partial switch enc_type {
case .REG:
if user_op.kind == .REGISTER && reg_needs_rex(user_op.reg) { r = 0 }
case .MR:
if user_op.kind == .REGISTER {
#partial switch user_op.kind {
case .REGISTER:
if reg_needs_rex(user_op.reg) { b = 0 }
} else if user_op.kind == .MEMORY {
case .MEMORY:
m := user_op.mem
if mem_has_base(m) && m.base_ext { b = 0 }
if mem_has_base(m) && m.base_ext { b = 0 }
if mem_has_index(m) && m.index_ext { x = 0 }
}
case .VVVV:
@@ -256,7 +259,7 @@ encode :: proc(
pos += 3
}
} else if enc.flags.vex_type == .EVEX {
case .EVEX:
// EVEX prefix encoding (4 bytes)
r: u8 = 1; x: u8 = 1; b: u8 = 1; rr: u8 = 1
mm: u8 = 1; w: u8 = 0; vvvv: u8 = 0xF; pp: u8 = 0
@@ -295,10 +298,11 @@ encode :: proc(
if hw >= 16 { rr = 0 }
}
case .MR:
if user_op.kind == .REGISTER {
#partial switch user_op.kind {
case .REGISTER:
hw := reg_hw(user_op.reg)
if hw >= 8 { b = 0 }
} else if user_op.kind == .MEMORY {
case .MEMORY:
m := user_op.mem
if mem_has_base(m) && m.base_ext { b = 0 }
if mem_has_index(m) && m.index_ext { x = 0 }
@@ -322,9 +326,7 @@ encode :: proc(
out[pos+3] = (z << 7) | (ll << 5) | (bb << 4) | (vvv << 3) | aaa
pos += 4
} else {
// Legacy encoding
case: // Legacy encoding
// Operand size override (66h)
needs_66 := false
for i in 0..<inst.operand_count {
@@ -353,18 +355,19 @@ encode :: proc(
rex: u8 = 0
if enc.flags.force_rex_w { rex |= 0x48 }
for i in 0..<4 {
if enc.enc[i] == .NONE { continue }
for enc_type, i in enc.enc {
if enc_type == .NONE { continue }
user_op := get_user_op_inline(&inst, enc, i)
if user_op == nil { continue }
#partial switch enc.enc[i] {
#partial switch enc_type {
case .REG:
if user_op.kind == .REGISTER && reg_needs_rex(user_op.reg) { rex |= 0x44 }
case .MR:
if user_op.kind == .REGISTER {
#partial switch user_op.kind {
case .REGISTER:
if reg_needs_rex(user_op.reg) { rex |= 0x41 }
} else if user_op.kind == .MEMORY {
case .MEMORY:
m := user_op.mem
if mem_has_base(m) && m.base_ext { rex |= 0x41 }
if mem_has_index(m) && m.index_ext { rex |= 0x42 }
@@ -425,8 +428,8 @@ encode :: proc(
x87_fixed_modrm := opcode >= 0xD8 && opcode <= 0xDF && enc.ext >= 0xC0
opr_index: u8 = 0
opr_seen := false
for i in 0..<4 {
if enc.enc[i] == .OP_R {
for enc_type, i in enc.enc {
if enc_type == .OP_R {
user_op := get_user_op_inline(&inst, enc, i)
if user_op != nil && user_op.kind == .REGISTER {
opr_index = reg_hw(user_op.reg) & 0x07
@@ -444,24 +447,24 @@ encode :: proc(
// --- ModR/M and SIB ---
has_modrm := false
mr_slot: int = -1
mr_slot: int = -1
reg_slot: int = -1
for i in 0..<4 {
#partial switch enc.enc[i] {
case .MR: mr_slot = i; has_modrm = true
for enc_type, i in enc.enc {
#partial switch enc_type {
case .MR: mr_slot = i; has_modrm = true
case .REG: reg_slot = i; has_modrm = true
}
}
if has_modrm {
mod: u8 = 0
reg_field: u8 = 0
rm: u8 = 0
has_sib := false
sib: u8 = 0
disp: i32 = 0
displacement_size: u8 = 0
mod: u8 = 0
reg_field: u8 = 0
rm: u8 = 0
sib: u8 = 0
disp: i32 = 0
displacement_size: u8 = 0
// Reg field
if enc.flags.modrm_reg_ext {
@@ -477,10 +480,11 @@ encode :: proc(
if mr_slot >= 0 {
mr_op := get_user_op_inline(&inst, enc, mr_slot)
if mr_op != nil {
if mr_op.kind == .REGISTER {
#partial switch mr_op.kind {
case .REGISTER:
mod = 0b11
rm = reg_hw(mr_op.reg) & 0x07
} else if mr_op.kind == .MEMORY {
case .MEMORY:
m := mr_op.mem
if mem_is_rip_relative(m) {
@@ -580,15 +584,16 @@ encode :: proc(
}
// --- Immediates ---
for i in 0..<4 {
#partial switch enc.enc[i] {
for enc_type, i in enc.enc {
#partial switch enc_type {
case .IB:
user_op := get_user_op_inline(&inst, enc, i)
if user_op != nil {
if user_op.kind == .IMMEDIATE {
#partial switch user_op.kind {
case .IMMEDIATE:
out[pos] = u8(user_op.immediate)
pos += 1
} else if user_op.kind == .RELATIVE {
case .RELATIVE:
// Relative reference - record relocation
label_id := u32(user_op.relative)
append(&pending_relocations, Relocation{code_pos + pos, label_id, 0, .REL8, 1, u16(instruction_index)})
@@ -608,12 +613,13 @@ encode :: proc(
case .ID:
user_op := get_user_op_inline(&inst, enc, i)
if user_op != nil {
if user_op.kind == .IMMEDIATE {
#partial switch user_op.kind {
case .IMMEDIATE:
immediate_val := u32(user_op.immediate)
out[pos] = u8(immediate_val); out[pos+1] = u8(immediate_val >> 8)
out[pos+2] = u8(immediate_val >> 16); out[pos+3] = u8(immediate_val >> 24)
pos += 4
} else if user_op.kind == .RELATIVE {
case .RELATIVE:
label_id := u32(user_op.relative)
append(&pending_relocations, Relocation{code_pos + pos, label_id, 0, .REL32, 4, u16(instruction_index)})
out[pos] = 0; out[pos+1] = 0; out[pos+2] = 0; out[pos+3] = 0
@@ -704,8 +710,7 @@ encoding_matches_inline :: #force_inline proc "contextless" (inst: ^Instruction,
// Count non-implicit encoding operands
encoding_operand_count: u8 = 0
for i in 0..<4 {
op_type := enc.ops[i]
for op_type in enc.ops {
if op_type == .NONE { break }
if !is_implicit_op_inline(op_type) { encoding_operand_count += 1 }
}
@@ -716,8 +721,7 @@ encoding_matches_inline :: #force_inline proc "contextless" (inst: ^Instruction,
// 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
for i in 0..<4 {
op_type := enc.ops[i]
for op_type in enc.ops {
if op_type == .NONE { break }
if is_implicit_op_inline(op_type) && implicit_operand_matches(last_user_op, op_type) {
found_matching_implicit = true
@@ -728,8 +732,7 @@ encoding_matches_inline :: #force_inline proc "contextless" (inst: ^Instruction,
// Match the first (operand_count - 1) user operands against non-implicit encoding operands
user_idx: u8 = 0
for i in 0..<4 {
op_type := enc.ops[i]
for op_type in enc.ops {
if op_type == .NONE { break }
if is_implicit_op_inline(op_type) { continue }
@@ -746,8 +749,7 @@ encoding_matches_inline :: #force_inline proc "contextless" (inst: ^Instruction,
// Match each user operand against non-implicit encoding operands
user_idx: u8 = 0
for i in 0..<4 {
op_type := enc.ops[i]
for op_type in enc.ops {
if op_type == .NONE { break }
if is_implicit_op_inline(op_type) { continue }
@@ -777,7 +779,7 @@ implicit_operand_matches :: #force_inline proc "contextless" (op: ^Operand, op_t
is_implicit_op_inline :: #force_inline proc "contextless" (op: Operand_Type) -> bool {
#partial switch op {
case .AL_IMPL, .AX_IMPL, .EAX_IMPL, .RAX_IMPL,
.CL_IMPL, .DX_IMPL, .ONE_IMPL, .ST0_IMPL, .XMM0_IMPL:
.CL_IMPL, .DX_IMPL, .ONE_IMPL, .ST0_IMPL, .XMM0_IMPL:
return true
}
return false
@@ -785,13 +787,13 @@ is_implicit_op_inline :: #force_inline proc "contextless" (op: Operand_Type) ->
operand_matches_inline :: #force_inline proc "contextless" (op: ^Operand, op_type: Operand_Type) -> bool {
switch op.kind {
case .NONE: return op_type == .NONE
case .NONE: return op_type == .NONE
case .REGISTER: return reg_matches_inline(op, op_type)
case .MEMORY: return mem_matches_inline(op, op_type)
case .IMMEDIATE: return imm_matches_inline(op, op_type)
case .MEMORY: return mem_matches_inline(op, op_type)
case .IMMEDIATE: return imm_matches_inline(op, op_type)
case .RELATIVE:
// Respect user's size preference: size=1 -> REL8, size=4 -> REL32
if op.size == 1 { return op_type == .REL8 }
if op.size == 1 { return op_type == .REL8 }
if op.size == 4 { return op_type == .REL32 }
// Default: accept either
return op_type == .REL8 || op_type == .REL32
@@ -802,19 +804,19 @@ operand_matches_inline :: #force_inline proc "contextless" (op: ^Operand, op_typ
reg_matches_inline :: #force_inline proc "contextless" (op: ^Operand, op_type: Operand_Type) -> bool {
class := reg_class(op.reg)
#partial switch op_type {
case .R8, .RM8: return class == REG_GPR8 || class == REG_GPR8H
case .R16, .RM16: return class == REG_GPR16
case .R32, .RM32: return class == REG_GPR32
case .R64, .RM64: return class == REG_GPR64
case .R8, .RM8: return class == REG_GPR8 || class == REG_GPR8H
case .R16, .RM16: return class == REG_GPR16
case .R32, .RM32: return class == REG_GPR32
case .R64, .RM64: return class == REG_GPR64
case .XMM, .XMM_M32, .XMM_M64, .XMM_M128: return class == REG_XMM
case .YMM, .YMM_M256: return class == REG_YMM
case .ZMM, .ZMM_M512: return class == REG_ZMM
case .MM, .MM_M64: return class == REG_MM
case .K, .K_M8, .K_M16, .K_M32, .K_M64: return class == REG_K
case .SREG: return class == REG_SEG
case .CR: return class == REG_CR
case .DR: return class == REG_DR
case .STI: return class == REG_ST
case .YMM, .YMM_M256: return class == REG_YMM
case .ZMM, .ZMM_M512: return class == REG_ZMM
case .MM, .MM_M64: return class == REG_MM
case .K, .K_M8, .K_M16, .K_M32, .K_M64: return class == REG_K
case .SREG: return class == REG_SEG
case .CR: return class == REG_CR
case .DR: return class == REG_DR
case .STI: return class == REG_ST
}
return false
}
@@ -875,9 +877,9 @@ imm_matches_inline :: #force_inline proc "contextless" (op: ^Operand, op_type: O
get_user_op_inline :: #force_inline proc "contextless" (inst: ^Instruction, enc: ^Encoding, slot: int) -> ^Operand {
user_idx := 0
for i in 0..<4 {
if enc.ops[i] == .NONE { break }
if is_implicit_op_inline(enc.ops[i]) { continue }
for op, i in enc.ops {
if op == .NONE { break }
if is_implicit_op_inline(op) { continue }
if i == slot {
if user_idx < int(inst.operand_count) {
return &inst.ops[user_idx]

View File

@@ -264,12 +264,12 @@ Encoding_Flags :: bit_field u32 {
// -----------------------------------------------------------------------------
Encoding :: struct #packed {
mnemonic: Mnemonic, // 2 bytes
ops: [4]Operand_Type, // 4 bytes - operand types
enc: [4]Operand_Encoding, // 4 bytes - operand encodings
opcode: u8, // 1 byte - primary opcode byte
ext: u8, // 1 byte - ModR/M reg extension (/0-/7) or secondary opcode
flags: Encoding_Flags, // 4 bytes
mnemonic: Mnemonic, // 2 bytes
ops: [4]Operand_Type, // 4 bytes - operand types
enc: [4]Operand_Encoding, // 4 bytes - operand encodings
opcode: u8, // 1 byte - primary opcode byte
ext: u8, // 1 byte - ModR/M reg extension (/0-/7) or secondary opcode
flags: Encoding_Flags, // 4 bytes
}
#assert(size_of(Encoding) == 16)
@@ -283,32 +283,32 @@ PREFIX_F2 :: 3
// -----------------------------------------------------------------------------
encoding_flags :: #force_inline proc "contextless" (
esc: Escape = .NONE,
prefix: u8 = 0,
vex_type: VEX_Type = .NONE,
vex_w: VEX_W = .WIG,
vex_l: VEX_L = .LIG,
default_64: bool = false,
force_rex_w: bool = false,
no_rex: bool = false,
lock_ok: bool = false,
rep_ok: bool = false,
modrm_reg_ext: bool = false,
mode_32_only: bool = false,
esc: Escape = .NONE,
prefix: u8 = 0,
vex_type: VEX_Type = .NONE,
vex_w: VEX_W = .WIG,
vex_l: VEX_L = .LIG,
default_64: bool = false,
force_rex_w: bool = false,
no_rex: bool = false,
lock_ok: bool = false,
rep_ok: bool = false,
modrm_reg_ext: bool = false,
mode_32_only: bool = false,
) -> Encoding_Flags {
return Encoding_Flags{
esc = esc,
prefix = prefix,
vex_type = vex_type,
vex_w = vex_w,
vex_l = vex_l,
default_64 = default_64,
force_rex_w = force_rex_w,
no_rex = no_rex,
lock_ok = lock_ok,
rep_ok = rep_ok,
esc = esc,
prefix = prefix,
vex_type = vex_type,
vex_w = vex_w,
vex_l = vex_l,
default_64 = default_64,
force_rex_w = force_rex_w,
no_rex = no_rex,
lock_ok = lock_ok,
rep_ok = rep_ok,
modrm_reg_ext = modrm_reg_ext,
mode_32_only = mode_32_only,
mode_32_only = mode_32_only,
}
}
@@ -318,21 +318,13 @@ encoding_flags :: #force_inline proc "contextless" (
op_type_to_size :: proc(op_type: Operand_Type) -> u8 {
#partial switch op_type {
case .R8, .RM8, .M8, .IMM8:
return 1
case .R16, .RM16, .M16, .IMM16:
return 2
case .R32, .RM32, .M32, .IMM32, .XMM_M32:
return 4
case .R64, .RM64, .M64, .IMM64, .XMM_M64:
return 8
case .XMM, .XMM_M128, .M128:
return 16
case .YMM, .YMM_M256, .M256:
return 32
case .ZMM, .ZMM_M512, .M512:
return 64
case:
return 0
case .R8, .RM8, .M8, .IMM8: return 1
case .R16, .RM16, .M16, .IMM16: return 2
case .R32, .RM32, .M32, .IMM32, .XMM_M32: return 4
case .R64, .RM64, .M64, .IMM64, .XMM_M64: return 8
case .XMM, .XMM_M128, .M128: return 16
case .YMM, .YMM_M256, .M256: return 32
case .ZMM, .ZMM_M512, .M512: return 64
}
return 0
}

View File

@@ -81,22 +81,27 @@ mem_make :: proc "contextless" (base: Register, index: Register, scale: u8, disp
// SECTION: 2.3 Memory Utility Functions
// -----------------------------------------------------------------------------
@(require_results)
mem_scale :: #force_inline proc "contextless" (m: Memory) -> u8 {
return 1 << m.scale_enc
}
@(require_results)
mem_is_rip_relative :: #force_inline proc "contextless" (m: Memory) -> bool {
return m.base_hw == MEM_BASE_RIP
}
@(require_results)
mem_has_base :: #force_inline proc "contextless" (m: Memory) -> bool {
return m.base_hw != MEM_BASE_NONE
}
@(require_results)
mem_has_index :: #force_inline proc "contextless" (m: Memory) -> bool {
return m.index_hw != MEM_INDEX_NONE
}
@(require_results)
mem_base :: proc "contextless" (m: Memory) -> Register {
if m.base_hw == MEM_BASE_NONE { return NONE }
if m.base_hw == MEM_BASE_RIP { return RIP }
@@ -104,6 +109,7 @@ mem_base :: proc "contextless" (m: Memory) -> Register {
return Register(class | u16(m.base_hw))
}
@(require_results)
mem_index :: proc "contextless" (m: Memory) -> Register {
if m.index_hw == MEM_INDEX_NONE { return NONE }
class := u16(m.index_class) << 8
@@ -116,22 +122,27 @@ mem_index :: proc "contextless" (m: Memory) -> Register {
// -----------------------------------------------------------------------------
// Convenience constructors for common memory patterns
@(require_results)
mem_base_only :: #force_inline proc "contextless" (base: Register) -> Memory {
return mem_make(base, NONE, 1, 0, NONE)
}
@(require_results)
mem_base_disp :: #force_inline proc "contextless" (base: Register, disp: i32) -> Memory {
return mem_make(base, NONE, 1, disp, NONE)
}
@(require_results)
mem_base_index :: #force_inline proc "contextless" (base, index: Register, scale: u8) -> Memory {
return mem_make(base, index, scale, 0, NONE)
}
@(require_results)
mem_base_index_disp :: #force_inline proc "contextless" (base, index: Register, scale: u8, disp: i32) -> Memory {
return mem_make(base, index, scale, disp, NONE)
}
@(require_results)
mem_rip_disp :: #force_inline proc "contextless" (disp: i32) -> Memory {
return mem_make(RIP, NONE, 1, disp, NONE)
}
@@ -143,34 +154,34 @@ mem_rip_disp :: #force_inline proc "contextless" (disp: i32) -> Memory {
// The unified Operand type
Operand :: struct #packed {
using _: struct #raw_union {
reg: Register, // for Register kind
mem: Memory, // for Memory kind (packed)
immediate: i64, // for Immediate kind
relative: i64, // for Relative kind (offset or label id)
reg: Register, // for Register kind
mem: Memory, // for Memory kind (packed)
immediate: i64, // for Immediate kind
relative: i64, // for Relative kind (offset or label id)
},
kind: Operand_Kind,
size: u8, // operand size in bytes (1, 2, 4, 8, 16, 32, 64)
kind: Operand_Kind,
size: u8, // operand size in bytes (1, 2, 4, 8, 16, 32, 64)
flags: Operand_Flags,
_pad: [4]u8,
_: [4]u8,
}
#assert(size_of(Operand) == 16)
// EVEX broadcast mode values for Operand_Flags.broadcast
// Used for EVEX instructions to broadcast a scalar element to all lanes
Broadcast :: enum u8 {
NONE = 0, // No broadcast (full vector load)
B1TO2 = 1, // Broadcast 1 element to 2 (64-bit element to 128-bit vector)
B1TO4 = 2, // Broadcast 1 element to 4 (32-bit to 128-bit, or 64-bit to 256-bit)
B1TO8 = 3, // Broadcast 1 element to 8 (32-bit to 256-bit, or 64-bit to 512-bit)
NONE = 0, // No broadcast (full vector load)
B1TO2 = 1, // Broadcast 1 element to 2 (64-bit element to 128-bit vector)
B1TO4 = 2, // Broadcast 1 element to 4 (32-bit to 128-bit, or 64-bit to 256-bit)
B1TO8 = 3, // Broadcast 1 element to 8 (32-bit to 256-bit, or 64-bit to 512-bit)
B1TO16 = 4, // Broadcast 1 element to 16 (32-bit to 512-bit)
}
Operand_Flags :: bit_field u16 {
// EVEX-specific
mask: u8 | 3, // opmask register K1-K7 (0 = no mask)
zeroing: bool | 1, // merge (0) vs zero (1) masking
mask: u8 | 3, // opmask register K1-K7 (0 = no mask)
zeroing: bool | 1, // merge (0) vs zero (1) masking
broadcast: Broadcast | 3, // broadcast mode (see Broadcast enum)
er_sae: u8 | 2, // embedded rounding / SAE (0=none, 1=RN-SAE, 2=RD-SAE, 3=RU-SAE/RZ-SAE)
er_sae: u8 | 2, // embedded rounding / SAE (0=none, 1=RN-SAE, 2=RD-SAE, 3=RU-SAE/RZ-SAE)
}
// -----------------------------------------------------------------------------
@@ -178,55 +189,66 @@ Operand_Flags :: bit_field u16 {
// -----------------------------------------------------------------------------
// Operand constructors
@(require_results)
op_reg :: #force_inline proc "contextless" (r: Register) -> Operand {
return Operand{reg = r, kind = .REGISTER, size = u8(reg_size(r) / 8)}
}
@(require_results)
op_mem :: #force_inline proc "contextless" (m: Memory, size: u8) -> Operand {
return Operand{mem = m, kind = .MEMORY, size = size}
}
@(require_results)
op_mem_from_parts :: #force_inline proc "contextless" (
base: Register,
base: Register,
index: Register,
scale: u8,
disp: i32,
size: u8,
disp: i32,
size: u8,
) -> Operand {
return op_mem(mem_make(base, index, scale, disp, NONE), size)
}
// Generic immediate constructor — value + explicit byte size. The typed
// op_imm8/16/32/64 variants below are convenience wrappers.
@(require_results)
op_imm :: #force_inline proc "contextless" (v: i64, size: u8) -> Operand {
return Operand{immediate = v, kind = .IMMEDIATE, size = size}
}
@(require_results)
op_imm8 :: #force_inline proc "contextless" (v: i8) -> Operand {
return Operand{immediate = i64(v), kind = .IMMEDIATE, size = 1}
}
@(require_results)
op_imm16 :: #force_inline proc "contextless" (v: i16) -> Operand {
return Operand{immediate = i64(v), kind = .IMMEDIATE, size = 2}
}
@(require_results)
op_imm32 :: #force_inline proc "contextless" (v: i32) -> Operand {
return Operand{immediate = i64(v), kind = .IMMEDIATE, size = 4}
}
@(require_results)
op_imm64 :: #force_inline proc "contextless" (v: i64) -> Operand {
return Operand{immediate = v, kind = .IMMEDIATE, size = 8}
}
@(require_results)
op_rel8 :: #force_inline proc "contextless" (offset: i8) -> Operand {
return Operand{relative = i64(offset), kind = .RELATIVE, size = 1}
}
@(require_results)
op_rel32 :: #force_inline proc "contextless" (offset: i32) -> Operand {
return Operand{relative = i64(offset), kind = .RELATIVE, size = 4}
}
// Create a relative operand referencing a label
@(require_results)
op_label :: #force_inline proc "contextless" (label_id: u32, size: u8 = 4) -> Operand {
return Operand{relative = i64(label_id), kind = .RELATIVE, size = size}
}
@@ -237,62 +259,77 @@ op_label :: #force_inline proc "contextless" (label_id: u32, size: u8 = 4) -> Op
// These provide compile-time type safety: op_gpr64(.XMM0) is a compile error.
// The enum value IS the hardware number, so we just cast - no table lookup.
@(require_results)
op_gpr64 :: #force_inline proc "contextless" (r: GPR64) -> Operand {
return Operand{reg = Register(REG_GPR64 | u16(r)), kind = .REGISTER, size = 8}
}
@(require_results)
op_gpr32 :: #force_inline proc "contextless" (r: GPR32) -> Operand {
return Operand{reg = Register(REG_GPR32 | u16(r)), kind = .REGISTER, size = 4}
}
@(require_results)
op_gpr16 :: #force_inline proc "contextless" (r: GPR16) -> Operand {
return Operand{reg = Register(REG_GPR16 | u16(r)), kind = .REGISTER, size = 2}
}
@(require_results)
op_gpr8 :: #force_inline proc "contextless" (r: GPR8) -> Operand {
return Operand{reg = Register(REG_GPR8 | u16(r)), kind = .REGISTER, size = 1}
}
@(require_results)
op_gpr8h :: #force_inline proc "contextless" (r: GPR8H) -> Operand {
return Operand{reg = Register(REG_GPR8H | u16(r)), kind = .REGISTER, size = 1}
}
@(require_results)
op_xmm :: #force_inline proc "contextless" (r: XMM) -> Operand {
return Operand{reg = Register(REG_XMM | u16(r)), kind = .REGISTER, size = 16}
}
@(require_results)
op_ymm :: #force_inline proc "contextless" (r: YMM) -> Operand {
return Operand{reg = Register(REG_YMM | u16(r)), kind = .REGISTER, size = 32}
}
@(require_results)
op_zmm :: #force_inline proc "contextless" (r: ZMM) -> Operand {
return Operand{reg = Register(REG_ZMM | u16(r)), kind = .REGISTER, size = 64}
}
@(require_results)
op_kreg :: #force_inline proc "contextless" (r: KREG) -> Operand {
return Operand{reg = Register(REG_K | u16(r)), kind = .REGISTER, size = 8}
}
@(require_results)
op_sreg :: #force_inline proc "contextless" (r: SREG) -> Operand {
return Operand{reg = Register(REG_SEG | u16(r)), kind = .REGISTER, size = 2}
}
@(require_results)
op_mm :: #force_inline proc "contextless" (r: MM) -> Operand {
return Operand{reg = Register(REG_MM | u16(r)), kind = .REGISTER, size = 8}
}
@(require_results)
op_creg :: #force_inline proc "contextless" (r: CREG) -> Operand {
return Operand{reg = Register(REG_CR | u16(r)), kind = .REGISTER, size = 8}
}
@(require_results)
op_dreg :: #force_inline proc "contextless" (r: DREG) -> Operand {
return Operand{reg = Register(REG_DR | u16(r)), kind = .REGISTER, size = 8}
}
@(require_results)
op_st :: #force_inline proc "contextless" (r: ST) -> Operand {
return Operand{reg = Register(REG_ST | u16(r)), kind = .REGISTER, size = 10}
}
@(require_results)
op_bnd :: #force_inline proc "contextless" (r: BND) -> Operand {
return Operand{reg = Register(REG_BND | u16(r)), kind = .REGISTER, size = 16}
}

View File

@@ -49,7 +49,7 @@ Print_Result :: isa.Print_Result
mnemonic_to_string :: proc(m: Mnemonic, lowercase: bool) -> string {
#partial switch m {
case .INVALID: return "???"
case .INVALID: return "???"
case .MOVSD_SSE: return lowercase ? "movsd" : "MOVSD"
}
if name, ok := reflect.enum_name_from_value(m); ok {
@@ -122,8 +122,8 @@ size_to_suffix :: proc(size: u8) -> u8 {
case 16: return 'x'
case 32: return 'y'
case 64: return 'z'
case: return 0
}
return 0
}
// -----------------------------------------------------------------------------
@@ -279,7 +279,7 @@ sbprint :: proc(
inst_info: []Instruction_Info,
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil, // Optional: for named label output (id → name)
) {
options := options != nil ? options^ : DEFAULT_PRINT_OPTIONS
@@ -424,10 +424,10 @@ sbprint :: proc(
// write_memory_with_tokens: Print memory operand with token metadata
@(private="file")
write_memory_with_tokens :: proc(
sb: ^strings.Builder,
m: Memory,
options: ^Print_Options,
tokens: ^[dynamic]Token,
sb: ^strings.Builder,
m: Memory,
options: ^Print_Options,
tokens: ^[dynamic]Token,
instruction_index: u16,
) {
emit_token :: proc(tokens: ^[dynamic]Token, sb: ^strings.Builder, kind: Token_Kind, instruction_index: u16, start: int) {

View File

@@ -294,58 +294,67 @@ RIP :: Register(0xFFFE)
// -----------------------------------------------------------------------------
// Register utility functions - all branchless single-cycle operations
@(require_results)
reg_hw :: #force_inline proc "contextless" (r: Register) -> u8 {
return u8(r) & 0x1F
}
@(require_results)
reg_class :: #force_inline proc "contextless" (r: Register) -> u16 {
return u16(r) & 0xFF00
}
@(require_results)
reg_needs_rex :: #force_inline proc "contextless" (r: Register) -> bool {
return (u16(r) & 0x08) != 0
}
@(require_results)
reg_needs_rex_ext :: #force_inline proc "contextless" (r: Register) -> bool {
return (u16(r) & 0x08) != 0 && reg_class(r) < REG_K
}
@(require_results)
reg_needs_evex :: #force_inline proc "contextless" (r: Register) -> bool {
return (u16(r) & 0x10) != 0
}
@(require_results)
reg_is_gpr :: #force_inline proc "contextless" (r: Register) -> bool {
c := reg_class(r)
return c >= REG_GPR64 && c <= REG_GPR8H
}
@(require_results)
reg_is_vector :: #force_inline proc "contextless" (r: Register) -> bool {
c := reg_class(r)
return c >= REG_XMM && c <= REG_ZMM
}
@(require_results)
reg_is_high_byte :: #force_inline proc "contextless" (r: Register) -> bool {
return reg_class(r) == REG_GPR8H
}
// Size in bits for register
@(require_results)
reg_size :: proc "contextless" (r: Register) -> u16 {
switch reg_class(r) {
case REG_GPR64: return 64
case REG_GPR32: return 32
case REG_GPR16: return 16
case REG_GPR64: return 64
case REG_GPR32: return 32
case REG_GPR16: return 16
case REG_GPR8, REG_GPR8H: return 8
case REG_XMM: return 128
case REG_YMM: return 256
case REG_ZMM: return 512
case REG_K: return 64
case REG_MM: return 64
case REG_ST: return 80
case REG_SEG: return 16
case REG_CR, REG_DR: return 64
case REG_BND: return 128
case: return 0
case REG_XMM: return 128
case REG_YMM: return 256
case REG_ZMM: return 512
case REG_K: return 64
case REG_MM: return 64
case REG_ST: return 80
case REG_SEG: return 16
case REG_CR, REG_DR: return 64
case REG_BND: return 128
}
return 0
}
// -----------------------------------------------------------------------------
@@ -356,14 +365,17 @@ reg_size :: proc "contextless" (r: Register) -> u16 {
// Since Register = class | hardware_number, and num IS the hardware number,
// we just OR with the class constant. This is O(1) with no stack allocation.
@(require_results)
gpr64_from_num :: #force_inline proc "contextless" (num: u8) -> Register {
return num < 16 ? Register(REG_GPR64 | u16(num)) : NONE
}
@(require_results)
gpr32_from_num :: #force_inline proc "contextless" (num: u8) -> Register {
return num < 16 ? Register(REG_GPR32 | u16(num)) : NONE
}
@(require_results)
gpr16_from_num :: #force_inline proc "contextless" (num: u8) -> Register {
return num < 16 ? Register(REG_GPR16 | u16(num)) : NONE
}
@@ -383,18 +395,22 @@ gpr8_from_num :: proc(num: u8, has_rex: bool) -> Register {
}
}
@(require_results)
xmm_from_num :: #force_inline proc "contextless" (num: u8) -> Register {
return num < 32 ? Register(REG_XMM | u16(num)) : NONE
}
@(require_results)
ymm_from_num :: #force_inline proc "contextless" (num: u8) -> Register {
return num < 32 ? Register(REG_YMM | u16(num)) : NONE
}
@(require_results)
zmm_from_num :: #force_inline proc "contextless" (num: u8) -> Register {
return num < 32 ? Register(REG_ZMM | u16(num)) : NONE
}
@(require_results)
mm_from_num :: #force_inline proc "contextless" (num: u8) -> Register {
return num < 8 ? Register(REG_MM | u16(num)) : NONE
}