diff --git a/core/rexcode/x86/decoder.odin b/core/rexcode/x86/decoder.odin index ca63b12c9..c0c7749dc 100644 --- a/core/rexcode/x86/decoder.odin +++ b/core/rexcode/x86/decoder.odin @@ -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 diff --git a/core/rexcode/x86/encoder.odin b/core/rexcode/x86/encoder.odin index 33ee0292f..0e51bdf26 100644 --- a/core/rexcode/x86/encoder.odin +++ b/core/rexcode/x86/encoder.odin @@ -113,7 +113,8 @@ encode :: proc( invalid := false for i in 0..= 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..= 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] diff --git a/core/rexcode/x86/encoding_types.odin b/core/rexcode/x86/encoding_types.odin index 1b410d772..ea6fa4bcb 100644 --- a/core/rexcode/x86/encoding_types.odin +++ b/core/rexcode/x86/encoding_types.odin @@ -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 } diff --git a/core/rexcode/x86/operands.odin b/core/rexcode/x86/operands.odin index ea1b217ed..cfbd6881d 100644 --- a/core/rexcode/x86/operands.odin +++ b/core/rexcode/x86/operands.odin @@ -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} } diff --git a/core/rexcode/x86/printer.odin b/core/rexcode/x86/printer.odin index 95c19e1be..6d391c6bf 100644 --- a/core/rexcode/x86/printer.odin +++ b/core/rexcode/x86/printer.odin @@ -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) { diff --git a/core/rexcode/x86/registers.odin b/core/rexcode/x86/registers.odin index d47cb814e..32dafd041 100644 --- a/core/rexcode/x86/registers.odin +++ b/core/rexcode/x86/registers.odin @@ -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 }