rexcode/arm64: CCMP/CCMN-imm, HINT, MSR-imm, USDOT encode forms

Conditional compare immediate (CCMP_IMM/CCMN_IMM: imm5 at 20:16 via a new
IMM5_HI encoding, bit 11 set), HINT #imm7, MSR <pstatefield>,#imm (new
MSR_PSTATE encoding placing op1 at 18:16 / op2 at 7:5, CRm via the shared
BARRIER_FIELD), and USDOT (I8MM unsigned-by-signed dot product, .2S/.4S).
Hand-written into the core (outside the specgen region). All forms
byte-exact vs llvm-mc and decode-clean; 461 tests green.
This commit is contained in:
Brendan Punsky
2026-06-17 23:34:10 -04:00
committed by Flāvius
parent c506e6c13b
commit aabcdd41b6
11 changed files with 1737 additions and 1659 deletions

View File

@@ -234,6 +234,11 @@ extract_operand_inline :: #force_inline proc "contextless" (
return Operand{immediate = i64(((word >> 11) & 0xF) >> mb), kind = .IMMEDIATE, size = 1}
case .NEON_EXT_IDX:
return Operand{immediate = i64((word >> 11) & 0xF), kind = .IMMEDIATE, size = 1}
case .IMM5_HI:
return Operand{immediate = i64((word >> 16) & 0x1F), kind = .IMMEDIATE, size = 1}
case .MSR_PSTATE:
v := ((word >> 16) & 0x7) << 3 | ((word >> 5) & 0x7)
return Operand{immediate = i64(v), kind = .IMMEDIATE, size = 1}
// ---- Memory operand variants ------------------------------------------
case .OFFSET_BASE_U12:

View File

@@ -478,6 +478,13 @@ pack_operand_inline :: #force_inline proc(
case .NEON_EXT_IDX:
return (u32(op.immediate) & 0xF) << 11
// CCMP/CCMN immediate (imm5 at 20:16) and MSR-immediate PSTATE selector.
case .IMM5_HI:
return (u32(op.immediate) & 0x1F) << 16
case .MSR_PSTATE:
v := u32(op.immediate)
return ((v >> 3) & 0x7) << 16 | (v & 0x7) << 5
// NEON MOVI/FMOV immediate split: abc at bits 18-16, defgh at bits 9-5.
case .NEON_IMM8_FMOV:
v := u32(op.immediate) & 0xFF

View File

@@ -246,6 +246,13 @@ Operand_Encoding :: enum u8 {
NEON_IDX4, // INS source lane index in imm4 (14:11); index << markerbit
NEON_EXT_IDX, // EXT byte index in imm4 (14:11)
// ---- MSR (immediate to PSTATE): pstate field selector op1:op2 ----
// User passes the combined 6-bit selector (op1<<3 | op2); op1 lands at
// bits 18:16, op2 at bits 7:5. The #imm goes to CRm (bits 11:8) via
// BARRIER_FIELD, which it shares with the DMB/DSB barrier encoding.
IMM5_HI, // generic 5-bit immediate at bits 20:16 (CCMP/CCMN immediate)
MSR_PSTATE, // PSTATE field selector: op1 at 18:16, op2 at 7:5
// ---- LSE atomics ------------------------------------------------------
ATOMIC_RS, // Rs (source / compare) at bits 16-20
ATOMIC_RT, // Rt (target) at bits 0-4

View File

@@ -121,8 +121,12 @@ inst_csneg_r_r_r_c :: #force_inline proc "contextless" (dst: Regist
emit_csneg_r_r_r_c :: #force_inline proc(instructions: ^[dynamic]Instruction, dst: Register, src: Register, src2: Register, cond: Cond) { append(instructions, inst_csneg_r_r_r_c(dst, src, src2, cond)) }
inst_ccmp_reg_r_r_i_c :: #force_inline proc "contextless" (dst: Register, src: Register, imm: i64, cond: Cond) -> Instruction { return Instruction{mnemonic = .CCMP_REG, operand_count = 4, length = 4, ops = {op_reg(dst), op_reg(src), op_imm(imm, 1), op_cond(cond)}} }
emit_ccmp_reg_r_r_i_c :: #force_inline proc(instructions: ^[dynamic]Instruction, dst: Register, src: Register, imm: i64, cond: Cond) { append(instructions, inst_ccmp_reg_r_r_i_c(dst, src, imm, cond)) }
inst_ccmp_imm_r_i_i_c :: #force_inline proc "contextless" (dst: Register, imm: i64, imm2: i64, cond: Cond) -> Instruction { return Instruction{mnemonic = .CCMP_IMM, operand_count = 4, length = 4, ops = {op_reg(dst), op_imm(imm, 1), op_imm(imm2, 1), op_cond(cond)}} }
emit_ccmp_imm_r_i_i_c :: #force_inline proc(instructions: ^[dynamic]Instruction, dst: Register, imm: i64, imm2: i64, cond: Cond) { append(instructions, inst_ccmp_imm_r_i_i_c(dst, imm, imm2, cond)) }
inst_ccmn_reg_r_r_i_c :: #force_inline proc "contextless" (dst: Register, src: Register, imm: i64, cond: Cond) -> Instruction { return Instruction{mnemonic = .CCMN_REG, operand_count = 4, length = 4, ops = {op_reg(dst), op_reg(src), op_imm(imm, 1), op_cond(cond)}} }
emit_ccmn_reg_r_r_i_c :: #force_inline proc(instructions: ^[dynamic]Instruction, dst: Register, src: Register, imm: i64, cond: Cond) { append(instructions, inst_ccmn_reg_r_r_i_c(dst, src, imm, cond)) }
inst_ccmn_imm_r_i_i_c :: #force_inline proc "contextless" (dst: Register, imm: i64, imm2: i64, cond: Cond) -> Instruction { return Instruction{mnemonic = .CCMN_IMM, operand_count = 4, length = 4, ops = {op_reg(dst), op_imm(imm, 1), op_imm(imm2, 1), op_cond(cond)}} }
emit_ccmn_imm_r_i_i_c :: #force_inline proc(instructions: ^[dynamic]Instruction, dst: Register, imm: i64, imm2: i64, cond: Cond) { append(instructions, inst_ccmn_imm_r_i_i_c(dst, imm, imm2, cond)) }
inst_extr_r_r_r_i :: #force_inline proc "contextless" (dst: Register, src: Register, src2: Register, imm: i64) -> Instruction { return Instruction{mnemonic = .EXTR, operand_count = 4, length = 4, ops = {op_reg(dst), op_reg(src), op_reg(src2), op_imm(imm, 1)}} }
emit_extr_r_r_r_i :: #force_inline proc(instructions: ^[dynamic]Instruction, dst: Register, src: Register, src2: Register, imm: i64) { append(instructions, inst_extr_r_r_r_i(dst, src, src2, imm)) }
inst_b_l :: #force_inline proc "contextless" (label: u32) -> Instruction { return inst_branch(.B, label) }
@@ -205,8 +209,12 @@ inst_sev_none :: #force_inline proc "contextless" () -> Instru
emit_sev_none :: #force_inline proc(instructions: ^[dynamic]Instruction) { append(instructions, inst_sev_none()) }
inst_sevl_none :: #force_inline proc "contextless" () -> Instruction { return inst_none(.SEVL) }
emit_sevl_none :: #force_inline proc(instructions: ^[dynamic]Instruction) { append(instructions, inst_sevl_none()) }
inst_hint_i :: #force_inline proc "contextless" (imm: i64) -> Instruction { return Instruction{mnemonic = .HINT, operand_count = 1, length = 4, ops = {op_imm(imm, 1), {}, {}, {}}} }
emit_hint_i :: #force_inline proc(instructions: ^[dynamic]Instruction, imm: i64) { append(instructions, inst_hint_i(imm)) }
inst_mrs_r_i :: #force_inline proc "contextless" (dst: Register, imm: i64) -> Instruction { return inst_r_i(.MRS, dst, imm) }
emit_mrs_r_i :: #force_inline proc(instructions: ^[dynamic]Instruction, dst: Register, imm: i64) { append(instructions, inst_mrs_r_i(dst, imm)) }
inst_msr_imm_i_i :: #force_inline proc "contextless" (imm: i64, imm2: i64) -> Instruction { return Instruction{mnemonic = .MSR_IMM, operand_count = 2, length = 4, ops = {op_imm(imm, 4), op_imm(imm2, 1), {}, {}}} }
emit_msr_imm_i_i :: #force_inline proc(instructions: ^[dynamic]Instruction, imm: i64, imm2: i64) { append(instructions, inst_msr_imm_i_i(imm, imm2)) }
inst_msr_reg_i_r :: #force_inline proc "contextless" (imm: i64, src: Register) -> Instruction { return Instruction{mnemonic = .MSR_REG, operand_count = 2, length = 4, ops = {op_imm(imm, 4), op_reg(src), {}, {}}} }
emit_msr_reg_i_r :: #force_inline proc(instructions: ^[dynamic]Instruction, imm: i64, src: Register) { append(instructions, inst_msr_reg_i_r(imm, src)) }
inst_isb_i :: #force_inline proc "contextless" (imm: i64) -> Instruction { return Instruction{mnemonic = .ISB, operand_count = 1, length = 4, ops = {op_imm(imm, 1), {}, {}, {}}} }
@@ -953,6 +961,8 @@ inst_sdot_r_r_r :: #force_inline proc "contextless" (dst: Regist
emit_sdot_r_r_r :: #force_inline proc(instructions: ^[dynamic]Instruction, dst: Register, src: Register, src2: Register) { append(instructions, inst_sdot_r_r_r(dst, src, src2)) }
inst_udot_r_r_r :: #force_inline proc "contextless" (dst: Register, src: Register, src2: Register) -> Instruction { return Instruction{mnemonic = .UDOT, operand_count = 3, length = 4, ops = {op_v_2s(u8(reg_hw(dst))), op_v_8b(u8(reg_hw(src))), op_v_8b(u8(reg_hw(src2))), {}}} }
emit_udot_r_r_r :: #force_inline proc(instructions: ^[dynamic]Instruction, dst: Register, src: Register, src2: Register) { append(instructions, inst_udot_r_r_r(dst, src, src2)) }
inst_usdot_r_r_r :: #force_inline proc "contextless" (dst: Register, src: Register, src2: Register) -> Instruction { return Instruction{mnemonic = .USDOT, operand_count = 3, length = 4, ops = {op_v_2s(u8(reg_hw(dst))), op_v_8b(u8(reg_hw(src))), op_v_8b(u8(reg_hw(src2))), {}}} }
emit_usdot_r_r_r :: #force_inline proc(instructions: ^[dynamic]Instruction, dst: Register, src: Register, src2: Register) { append(instructions, inst_usdot_r_r_r(dst, src, src2)) }
inst_fadd_v_r_r_r :: #force_inline proc "contextless" (dst: Register, src: Register, src2: Register) -> Instruction { return Instruction{mnemonic = .FADD_V, operand_count = 3, length = 4, ops = {op_v_2s(u8(reg_hw(dst))), op_v_2s(u8(reg_hw(src))), op_v_2s(u8(reg_hw(src2))), {}}} }
emit_fadd_v_r_r_r :: #force_inline proc(instructions: ^[dynamic]Instruction, dst: Register, src: Register, src2: Register) { append(instructions, inst_fadd_v_r_r_r(dst, src, src2)) }
inst_fsub_v_r_r_r :: #force_inline proc "contextless" (dst: Register, src: Register, src2: Register) -> Instruction { return Instruction{mnemonic = .FSUB_V, operand_count = 3, length = 4, ops = {op_v_2s(u8(reg_hw(dst))), op_v_2s(u8(reg_hw(src))), op_v_2s(u8(reg_hw(src2))), {}}} }
@@ -2142,8 +2152,12 @@ inst_csneg :: inst_csneg_r_r_r_c
emit_csneg :: emit_csneg_r_r_r_c
inst_ccmp_reg :: inst_ccmp_reg_r_r_i_c
emit_ccmp_reg :: emit_ccmp_reg_r_r_i_c
inst_ccmp_imm :: inst_ccmp_imm_r_i_i_c
emit_ccmp_imm :: emit_ccmp_imm_r_i_i_c
inst_ccmn_reg :: inst_ccmn_reg_r_r_i_c
emit_ccmn_reg :: emit_ccmn_reg_r_r_i_c
inst_ccmn_imm :: inst_ccmn_imm_r_i_i_c
emit_ccmn_imm :: emit_ccmn_imm_r_i_i_c
inst_extr :: inst_extr_r_r_r_i
emit_extr :: emit_extr_r_r_r_i
inst_b :: inst_b_l
@@ -2224,8 +2238,12 @@ inst_sev :: inst_sev_none
emit_sev :: emit_sev_none
inst_sevl :: inst_sevl_none
emit_sevl :: emit_sevl_none
inst_hint :: inst_hint_i
emit_hint :: emit_hint_i
inst_mrs :: inst_mrs_r_i
emit_mrs :: emit_mrs_r_i
inst_msr_imm :: inst_msr_imm_i_i
emit_msr_imm :: emit_msr_imm_i_i
inst_msr_reg :: inst_msr_reg_i_r
emit_msr_reg :: emit_msr_reg_i_r
inst_isb :: inst_isb_i
@@ -2972,6 +2990,8 @@ inst_sdot :: inst_sdot_r_r_r
emit_sdot :: emit_sdot_r_r_r
inst_udot :: inst_udot_r_r_r
emit_udot :: emit_udot_r_r_r
inst_usdot :: inst_usdot_r_r_r
emit_usdot :: emit_usdot_r_r_r
inst_fadd_v :: inst_fadd_v_r_r_r
emit_fadd_v :: emit_fadd_v_r_r_r
inst_fsub_v :: inst_fsub_v_r_r_r

View File

@@ -300,6 +300,24 @@ ENCODING_TABLE := #partial [Mnemonic][]Encoding{
{.CCMN_REG, {.W_REG, .W_REG, .NZCV_IMM, .COND}, {.RN, .RM, .NZCV_FIELD, .COND_HI}, 0x3A400000, 0xFFE00C10, .BASE, {sets_flags=true}},
{.CCMN_REG, {.X_REG, .X_REG, .NZCV_IMM, .COND}, {.RN, .RM, .NZCV_FIELD, .COND_HI}, 0xBA400000, 0xFFE00C10, .BASE, {sets_flags=true, is_64=true}},
},
// Conditional compare (immediate): imm5 at 20:16 replaces Rm; bit 11 = 1.
.CCMP_IMM = {
{.CCMP_IMM, {.W_REG, .IMM_5, .NZCV_IMM, .COND}, {.RN, .IMM5_HI, .NZCV_FIELD, .COND_HI}, 0x7A400800, 0xFFE00C10, .BASE, {sets_flags=true}},
{.CCMP_IMM, {.X_REG, .IMM_5, .NZCV_IMM, .COND}, {.RN, .IMM5_HI, .NZCV_FIELD, .COND_HI}, 0xFA400800, 0xFFE00C10, .BASE, {sets_flags=true, is_64=true}},
},
.CCMN_IMM = {
{.CCMN_IMM, {.W_REG, .IMM_5, .NZCV_IMM, .COND}, {.RN, .IMM5_HI, .NZCV_FIELD, .COND_HI}, 0x3A400800, 0xFFE00C10, .BASE, {sets_flags=true}},
{.CCMN_IMM, {.X_REG, .IMM_5, .NZCV_IMM, .COND}, {.RN, .IMM5_HI, .NZCV_FIELD, .COND_HI}, 0xBA400800, 0xFFE00C10, .BASE, {sets_flags=true, is_64=true}},
},
// HINT #imm7 (imm at 11:5); NOP/YIELD/etc. are specific values of this.
.HINT = { {.HINT, {.IMM_8, .NONE, .NONE, .NONE}, {.HINT_FIELD, .NONE, .NONE, .NONE}, 0xD503201F, 0xFFFFF01F, .BASE, {}} },
// MSR <pstatefield>, #imm: op1:op2 selector (combined) + CRm immediate.
.MSR_IMM = { {.MSR_IMM, {.SYS_REG, .IMM_4, .NONE, .NONE}, {.MSR_PSTATE, .BARRIER_FIELD, .NONE, .NONE}, 0xD500401F, 0xFFF8F01F, .BASE, {}} },
// USDOT (unsigned-by-signed dot product, I8MM): Vd.<2S|4S>, Vn.<8B|16B>, Vm.<8B|16B>.
.USDOT = {
{.USDOT, {.V_2S, .V_8B, .V_8B, .NONE}, {.VD, .VN, .VM, .NONE}, 0x0E809C00, 0xFFE0FC00, .DOT, {}},
{.USDOT, {.V_4S, .V_16B, .V_16B, .NONE}, {.VD, .VN, .VM, .NONE}, 0x4E809C00, 0xFFE0FC00, .DOT, {}},
},
// =========================================================================
// §8 Branches

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff