mirror of
https://github.com/odin-lang/Odin.git
synced 2026-09-02 02:03:35 +00:00
rexcode/arm32: the width and saturate fields hold one less than they name
SSAT, SSAT16, USAT and USAT16 encode their saturate position minus one, and SBFX and UBFX their width minus one, so a field of zero means one. All of them printed the raw field, which is not a value the instruction can take -- `ssat r0, #0, r0` is not assemblable. BFI and BFC are different again: their field is the top bit's position, and the width the syntax wants is msb - lsb + 1. They shared an encoding with SBFX and UBFX, which need the opposite arithmetic, so they now have their own. Packing an msb needs the lsb from a sibling operand, so the packer takes the instruction rather than one operand in isolation. Also worth recording: the sweep had been running llvm-mc with `-mattr=+all`, which that target does not recognise and silently ignores, so every CRC32, FP16, v8.1a and dot-product entry looked like a reserved encoding. With the features actually enabled, 815 of 1139 A32 entries round-trip, and 38 rather than 143 are genuinely reserved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018UmHLRF11EoWwNWCJ7JGaA
This commit is contained in:
@@ -529,8 +529,15 @@ unpack_operand :: proc(word: u32, enc: Operand_Encoding, ot: Operand_Type) -> Op
|
||||
return op_imm(i64((word >> 18) & 0xF))
|
||||
|
||||
// ---- Saturate / bit field ----
|
||||
case .SAT_IMM5, .SAT_IMM5_T32, .BFI_MSB:
|
||||
return op_imm(i64((word >> 16) & 0x1F))
|
||||
case .SAT_IMM5, .SAT_IMM5_T32:
|
||||
// The field holds one less than the saturate position it names.
|
||||
return op_imm(i64(((word >> 16) & 0x1F) + 1))
|
||||
case .BFX_WIDTH:
|
||||
// One less than the width.
|
||||
return op_imm(i64(((word >> 16) & 0x1F) + 1))
|
||||
case .BFI_MSB:
|
||||
// The top bit's position; the syntax wants the width.
|
||||
return op_imm(i64(((word >> 16) & 0x1F) - ((word >> 7) & 0x1F) + 1))
|
||||
case .BFI_LSB, .BFI_LSB_T32:
|
||||
return op_imm(i64((word >> 7) & 0x1F))
|
||||
case .NEON_SHIFT_IMM6:
|
||||
|
||||
@@ -202,10 +202,10 @@ encode_one_inline :: #force_inline proc(
|
||||
word = (word & 0x0FFFFFFF) | (u32(inst.cond) << 28)
|
||||
}
|
||||
|
||||
if form.enc[0] != .NONE { word |= pack_operand_inline(&inst.ops[0], form.enc[0], pc, inst_idx, relocs, form) }
|
||||
if form.enc[1] != .NONE { word |= pack_operand_inline(&inst.ops[1], form.enc[1], pc, inst_idx, relocs, form) }
|
||||
if form.enc[2] != .NONE { word |= pack_operand_inline(&inst.ops[2], form.enc[2], pc, inst_idx, relocs, form) }
|
||||
if form.enc[3] != .NONE { word |= pack_operand_inline(&inst.ops[3], form.enc[3], pc, inst_idx, relocs, form) }
|
||||
if form.enc[0] != .NONE { word |= pack_operand_inline(&inst.ops[0], form.enc[0], pc, inst_idx, relocs, form, inst) }
|
||||
if form.enc[1] != .NONE { word |= pack_operand_inline(&inst.ops[1], form.enc[1], pc, inst_idx, relocs, form, inst) }
|
||||
if form.enc[2] != .NONE { word |= pack_operand_inline(&inst.ops[2], form.enc[2], pc, inst_idx, relocs, form, inst) }
|
||||
if form.enc[3] != .NONE { word |= pack_operand_inline(&inst.ops[3], form.enc[3], pc, inst_idx, relocs, form, inst) }
|
||||
|
||||
return word, inst_size_from_bits(form.bits, form.mode), true
|
||||
}
|
||||
@@ -334,6 +334,8 @@ pack_operand_inline :: #force_inline proc(
|
||||
inst_idx: u16,
|
||||
relocs: ^[dynamic]Relocation,
|
||||
form: ^Encoding,
|
||||
// BFI's msb is lsb + width - 1, so packing it needs a sibling operand.
|
||||
inst: ^Instruction,
|
||||
) -> u32 {
|
||||
switch enc {
|
||||
case .NONE, .IMPL:
|
||||
@@ -631,9 +633,15 @@ pack_operand_inline :: #force_inline proc(
|
||||
case .IT_MASK: return u32(op.immediate) & 0xFF
|
||||
case .CPS_IFLAGS: return u32(op.immediate) & 0x1FF
|
||||
case .HINT_FIELD: return u32(op.immediate) & 0xFF
|
||||
case .SAT_IMM5, .SAT_IMM5_T32:
|
||||
return (u32(op.immediate) & 0x1F) << 16
|
||||
case .BFI_MSB: return (u32(op.immediate) & 0x1F) << 16
|
||||
case .SAT_IMM5, .SAT_IMM5_T32, .BFX_WIDTH:
|
||||
return ((u32(op.immediate) - 1) & 0x1F) << 16
|
||||
case .BFI_MSB:
|
||||
// msb = lsb + width - 1; the lsb rides in whichever slot carries it.
|
||||
lsb: u32 = 0
|
||||
for e, k in form.enc {
|
||||
if e == .BFI_LSB || e == .BFI_LSB_T32 { lsb = u32(inst.ops[k].immediate); break }
|
||||
}
|
||||
return ((lsb + u32(op.immediate) - 1) & 0x1F) << 16
|
||||
case .BFI_LSB, .BFI_LSB_T32:
|
||||
return (u32(op.immediate) & 0x1F) << 7
|
||||
case .NEON_SHIFT_IMM6: return (u32(op.immediate) & 0x3F) << 16
|
||||
|
||||
@@ -400,6 +400,9 @@ Operand_Encoding :: enum u8 {
|
||||
SAT_IMM5_T32, // Thumb-2 saturate amount
|
||||
|
||||
// ---- BFC/BFI/SBFX/UBFX ----
|
||||
// SBFX/UBFX hold the width less one, where BFI/BFC hold the top bit's
|
||||
// position and the width is msb - lsb + 1.
|
||||
BFX_WIDTH,
|
||||
BFI_MSB, // bits 20-16 (msb position)
|
||||
BFI_LSB, // bits 11-7 (lsb position; also shift_imm slot)
|
||||
BFI_LSB_T32, // Thumb-2 BFI lsb (different layout)
|
||||
|
||||
@@ -754,14 +754,14 @@ ENCODING_TABLE := #partial [Mnemonic][]Encoding{
|
||||
{.BFI, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD_T32, .RN_T32, .BFI_LSB_T32, .BFI_MSB}, 0xF3600000, 0xFFF08000, .V6T2, .T32, {thumb32=true, cond_in_28=false}, {}},
|
||||
},
|
||||
.SBFX = {
|
||||
{.SBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD, .RM_A32, .BFI_LSB, .BFI_MSB}, 0x07A00050, 0x0FE00070, .V6T2, .A32, {}, {}},
|
||||
{.SBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD, .RM_A32, .BFI_LSB, .BFX_WIDTH}, 0x07A00050, 0x0FE00070, .V6T2, .A32, {}, {}},
|
||||
// T32 SBFX
|
||||
{.SBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD_T32, .RN_T32, .BFI_LSB_T32, .BFI_MSB}, 0xF3400000, 0xFFF08000, .V6T2, .T32, {thumb32=true, cond_in_28=false}, {}},
|
||||
{.SBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD_T32, .RN_T32, .BFI_LSB_T32, .BFX_WIDTH}, 0xF3400000, 0xFFF08000, .V6T2, .T32, {thumb32=true, cond_in_28=false}, {}},
|
||||
},
|
||||
.UBFX = {
|
||||
{.UBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD, .RM_A32, .BFI_LSB, .BFI_MSB}, 0x07E00050, 0x0FE00070, .V6T2, .A32, {}, {}},
|
||||
{.UBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD, .RM_A32, .BFI_LSB, .BFX_WIDTH}, 0x07E00050, 0x0FE00070, .V6T2, .A32, {}, {}},
|
||||
// T32 UBFX
|
||||
{.UBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD_T32, .RN_T32, .BFI_LSB_T32, .BFI_MSB}, 0xF3C00000, 0xFFF08000, .V6T2, .T32, {thumb32=true, cond_in_28=false}, {}},
|
||||
{.UBFX, {.GPR, .GPR, .IMM5, .IMM5_W}, {.RD_T32, .RN_T32, .BFI_LSB_T32, .BFX_WIDTH}, 0xF3C00000, 0xFFF08000, .V6T2, .T32, {thumb32=true, cond_in_28=false}, {}},
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
|
||||
@@ -979,10 +979,10 @@ DECODE_ENTRIES := [1649]lib.Decode_Entry{
|
||||
{ .LDRB, {.GPR,.MEM,.NONE,.NONE}, {.RT_A32,.MEM_REG_OFFSET,.NONE,.NONE}, 0x07500000, 0x0F700010, .BASE, .A32, {}, {.NONE,.NONE} },
|
||||
{ .USAD8, {.GPR,.GPR,.GPR,.NONE}, {.RN_A32,.RM_A32,.RS_A32,.NONE}, 0x0780F010, 0x0FF0F0F0, .V6, .A32, {}, {.NONE,.NONE} },
|
||||
{ .USADA8, {.GPR,.GPR,.GPR,.GPR}, {.RN_A32,.RM_A32,.RS_A32,.RD}, 0x07800010, 0x0FF000F0, .V6, .A32, {}, {.NONE,.NONE} },
|
||||
{ .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFI_MSB}, 0x07A00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} },
|
||||
{ .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFX_WIDTH}, 0x07A00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} },
|
||||
{ .BFC, {.GPR,.IMM5,.IMM5_W,.NONE}, {.RD,.BFI_LSB,.BFI_MSB,.NONE}, 0x07C0001F, 0x0FE0007F, .V6T2, .A32, {}, {.NONE,.NONE} },
|
||||
{ .BFI, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFI_MSB}, 0x07C00010, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} },
|
||||
{ .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFI_MSB}, 0x07E00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} },
|
||||
{ .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFX_WIDTH}, 0x07E00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} },
|
||||
{ .UDF, {.IMM,.NONE,.NONE,.NONE}, {.NONE,.NONE,.NONE,.NONE}, 0xE7F000F0, 0xFFF000F0, .BASE, .A32, {}, {.NONE,.NONE} },
|
||||
{ .STMDA, {.GPR,.GPR_LIST,.NONE,.NONE}, {.RN_A32,.A32_REG_LIST,.NONE,.NONE}, 0x08000000, 0x0FD00000, .BASE, .A32, {}, {.NONE,.NONE} },
|
||||
{ .RFE, {.GPR,.NONE,.NONE,.NONE}, {.RN_A32,.NONE,.NONE,.NONE}, 0xF8100A00, 0xFE10FFFF, .V6, .A32, {}, {.NONE,.NONE} },
|
||||
@@ -1407,8 +1407,8 @@ DECODE_ENTRIES := [1649]lib.Decode_Entry{
|
||||
{ .USAT16, {.GPR,.IMM4_SAT,.GPR,.NONE}, {.RD_T32,.SAT_IMM5_T32,.RN_T32,.NONE}, 0xF3A00000, 0xFFF0F0F0, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
{ .BFC, {.GPR,.IMM5,.IMM5_W,.NONE}, {.RD_T32,.BFI_LSB_T32,.BFI_MSB,.NONE}, 0xF36F0000, 0xFFFF8000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
{ .BFI, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFI_MSB}, 0xF3600000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
{ .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFI_MSB}, 0xF3400000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
{ .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFI_MSB}, 0xF3C00000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
{ .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFX_WIDTH}, 0xF3400000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
{ .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFX_WIDTH}, 0xF3C00000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
{ .SSAT, {.GPR,.IMM4_SAT,.GPR_SHIFTED,.NONE}, {.RD_T32,.SAT_IMM5_T32,.RN_T32,.NONE}, 0xF3000000, 0xFFD08020, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
{ .USAT, {.GPR,.IMM4_SAT,.GPR_SHIFTED,.NONE}, {.RD_T32,.SAT_IMM5_T32,.RN_T32,.NONE}, 0xF3800000, 0xFFD08020, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
{ .MOVW, {.GPR,.IMM16_LO_HI,.NONE,.NONE}, {.RD_T32,.NONE,.NONE,.NONE}, 0xF2400000, 0xFBF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
|
||||
@@ -209,11 +209,11 @@ ENCODE_FORMS := [1649]lib.Encoding{
|
||||
{ .BFI, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFI_MSB}, 0x07C00010, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} },
|
||||
{ .BFI, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFI_MSB}, 0xF3600000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
// .SBFX
|
||||
{ .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFI_MSB}, 0x07A00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} },
|
||||
{ .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFI_MSB}, 0xF3400000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
{ .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFX_WIDTH}, 0x07A00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} },
|
||||
{ .SBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFX_WIDTH}, 0xF3400000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
// .UBFX
|
||||
{ .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFI_MSB}, 0x07E00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} },
|
||||
{ .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFI_MSB}, 0xF3C00000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
{ .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD,.RM_A32,.BFI_LSB,.BFX_WIDTH}, 0x07E00050, 0x0FE00070, .V6T2, .A32, {}, {.NONE,.NONE} },
|
||||
{ .UBFX, {.GPR,.GPR,.IMM5,.IMM5_W}, {.RD_T32,.RN_T32,.BFI_LSB_T32,.BFX_WIDTH}, 0xF3C00000, 0xFFF08000, .V6T2, .T32, {thumb32=true}, {.NONE,.NONE} },
|
||||
// .SXTB
|
||||
{ .SXTB, {.GPR,.GPR,.NONE,.NONE}, {.RD,.RM_A32,.NONE,.NONE}, 0x06AF0070, 0x0FFF0070, .V6, .A32, {}, {.NONE,.NONE} },
|
||||
{ .SXTB, {.GPR_LOW,.GPR_LOW,.NONE,.NONE}, {.RD_T16_LO,.RM_T16_LO,.NONE,.NONE}, 0x0000B240, 0x0000FFC0, .V6, .T32, {}, {.NONE,.NONE} },
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user