mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-19 16:42:33 +00:00
Minor code style changes
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user