rexcode/arm64: an instruction can hold five operands

SME's outer products take five -- `smopa za0.s, p0/m, p1/m, z0.b, z1.b`
-- and Instruction held four, so the 14 forms in that family could not
be represented at all, let alone printed. They named one vector where
the instruction multiplies two.

Five Operands is 55 bytes, which is odd, so Mnemonic's alignment costs
one more; three bytes of padding still land Instruction on exactly one
64-byte cache line, as before. Encoding and Decode_Entry each grow by
two.

The tile number was wrong as well: it sits in the low bits, as wide as
the element size leaves room for -- three for .d down to none for .b --
not at bits 23:22 where the encoding read it. Every MOP form named a
tile it was not writing.

Note for anyone regenerating: tablegen re-emits tables.odin from a
template inside gen.odin, so a struct change there has to go in the
template, and the package has to compile before tablegen can run at all.
Same for the builders.

SVE/SME2 against llvm-mc: 697 byte-exact and 0 mismatched, of 704.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018UmHLRF11EoWwNWCJ7JGaA
This commit is contained in:
Brendan Punsky
2026-08-28 19:29:20 -04:00
committed by Flāvius
parent 8b2e7c20a8
commit c67282dfbd
15 changed files with 10014 additions and 10002 deletions

View File

@@ -1,6 +1,6 @@
package rexcode_arm64
Operand_Set :: distinct bit_set[0..<4; u8]
Operand_Set :: distinct bit_set[0..<5; u8]
NZCV_Flags :: distinct bit_set[NZCV_Flag; u8]
NZCV_Flag :: enum u8 {

View File

@@ -128,6 +128,10 @@ decode_one_inline :: #force_inline proc "contextless" (
if entry.ops[3] != .NONE {
inst.ops[3] = extract_operand_inline(word, pc, entry.ops[3], entry.enc[3])
cnt_used = 4
if entry.ops[4] != .NONE {
inst.ops[4] = extract_operand_inline(word, pc, entry.ops[4], entry.enc[4])
cnt_used = 5
}
}
}
}
@@ -572,16 +576,16 @@ extract_operand_inline :: #force_inline proc "contextless" (
// ---- SME ZA tile fields ----
case .ZA_TILE_NUM_B:
return Operand{reg = Register(REG_ZA | 0), kind = .REGISTER,
return Operand{reg = Register(REG_ZA | u16(word & 0x0)), kind = .REGISTER,
size = za_elem_for_type(ot)}
case .ZA_TILE_NUM_H:
return Operand{reg = Register(REG_ZA | u16((word >> 22) & 0x1)), kind = .REGISTER,
return Operand{reg = Register(REG_ZA | u16(word & 0x1)), kind = .REGISTER,
size = za_elem_for_type(ot)}
case .ZA_TILE_NUM_S:
return Operand{reg = Register(REG_ZA | u16((word >> 22) & 0x3)), kind = .REGISTER,
return Operand{reg = Register(REG_ZA | u16(word & 0x3)), kind = .REGISTER,
size = za_elem_for_type(ot)}
case .ZA_TILE_NUM_D:
return Operand{reg = Register(REG_ZA | u16((word >> 21) & 0x7)), kind = .REGISTER,
return Operand{reg = Register(REG_ZA | u16(word & 0x7)), kind = .REGISTER,
size = za_elem_for_type(ot)}
case .SME_PATTERN_FIELD:
return Operand{immediate = i64((word >> 5) & 0xF), kind = .IMMEDIATE, size = 1}

View File

@@ -145,6 +145,7 @@ encode_one_inline :: #force_inline proc(
if form.enc[1] != .NONE { word |= pack_operand_inline(&inst.ops[1], form.enc[1], form, pc, inst_idx, relocs, inst) }
if form.enc[2] != .NONE { word |= pack_operand_inline(&inst.ops[2], form.enc[2], form, pc, inst_idx, relocs, inst) }
if form.enc[3] != .NONE { word |= pack_operand_inline(&inst.ops[3], form.enc[3], form, pc, inst_idx, relocs, inst) }
if form.enc[4] != .NONE { word |= pack_operand_inline(&inst.ops[4], form.enc[4], form, pc, inst_idx, relocs, inst) }
return word, true
}
@@ -155,7 +156,8 @@ encoding_matches_inline :: #force_inline proc "contextless" (
return operand_matches_inline(&inst.ops[0], form.ops[0], form) &&
operand_matches_inline(&inst.ops[1], form.ops[1], form) &&
operand_matches_inline(&inst.ops[2], form.ops[2], form) &&
operand_matches_inline(&inst.ops[3], form.ops[3], form)
operand_matches_inline(&inst.ops[3], form.ops[3], form) &&
operand_matches_inline(&inst.ops[4], form.ops[4], form)
}
@(private="file")
@@ -721,17 +723,21 @@ pack_operand_inline :: #force_inline proc(
// SME ZA tile number fields (position depends on element size).
case .ZA_TILE_NUM_B:
// ZA0.B only -- nothing to encode (single tile of byte form).
return 0
// The tile number sits in the low bits, as wide as the element
// size leaves room for.
return u32(reg_hw(op.reg)) & 0x0
case .ZA_TILE_NUM_H:
// ZA0.H..ZA1.H -- 1-bit tile number at bit 22.
return (u32(op.immediate) & 0x1) << 22
// The tile number sits in the low bits, as wide as the element
// size leaves room for.
return u32(reg_hw(op.reg)) & 0x1
case .ZA_TILE_NUM_S:
// ZA0.S..ZA3.S -- 2-bit tile number at bits 23:22.
return (u32(op.immediate) & 0x3) << 22
// The tile number sits in the low bits, as wide as the element
// size leaves room for.
return u32(reg_hw(op.reg)) & 0x3
case .ZA_TILE_NUM_D:
// ZA0.D..ZA7.D -- 3-bit tile number at bits 23:21.
return (u32(op.immediate) & 0x7) << 21
// The tile number sits in the low bits, as wide as the element
// size leaves room for.
return u32(reg_hw(op.reg)) & 0x7
case .SME_PATTERN_FIELD:
// 4-bit SME pattern/list at bits 8:5 (ZERO instruction list mask).
return (u32(op.immediate) & 0xF) << 5

View File

@@ -459,11 +459,11 @@ Operand_Encoding :: enum u8 {
Encoding :: struct #packed {
mnemonic: Mnemonic, // 2
ops: [4]Operand_Type, // 4
enc: [4]Operand_Encoding, // 4
ops: [5]Operand_Type, // 4
enc: [5]Operand_Encoding, // 4
bits: u32, // 4 -- static field pattern
mask: u32, // 4 -- which bits are static
feature: Feature, // 1
flags: Encoding_Flags, // 1
}
#assert(size_of(Encoding) == 20)
#assert(size_of(Encoding) == 22)

View File

@@ -28,12 +28,13 @@ Instruction_Flags :: bit_field u8 {
// The spare bytes are free: they cost nothing over a 48-byte struct that
// straddles, and new fields land in them without changing the layout.
Instruction :: struct #align(64) {
ops: [4]Operand `fmt:"v,operand_count"`, // 4 * size_of(Operand) = 44
ops: [5]Operand `fmt:"v,operand_count"`, // 5 * size_of(Operand) = 55
mnemonic: Mnemonic, // 2
operand_count: u8, // 1
flags: Instruction_Flags, // 1
length: u8, // 1 -- always 4
_: [15]u8,
// 55 is odd, so mnemonic's alignment costs a byte here; 3 more reach 64.
_: [3]u8,
}
#assert(size_of(Instruction) == 64)
#assert(align_of(Instruction) == 64)
@@ -52,70 +53,70 @@ inst_none :: #force_inline proc "contextless" (m: Mnemonic) -> Instruction {
@(require_results)
inst_r :: #force_inline proc "contextless" (m: Mnemonic, r: Register) -> Instruction {
return Instruction{mnemonic = m, operand_count = 1, length = 4,
ops = {op_reg(r), {}, {}, {}}}
ops = {op_reg(r), {}, {}, {}, {}}}
}
// 2-register (e.g. CLZ, RBIT).
@(require_results)
inst_r_r :: #force_inline proc "contextless" (m: Mnemonic, rd, rn: Register) -> Instruction {
return Instruction{mnemonic = m, operand_count = 2, length = 4,
ops = {op_reg(rd), op_reg(rn), {}, {}}}
ops = {op_reg(rd), op_reg(rn), {}, {}, {}}}
}
// 3-register (e.g. ADD shifted, MUL, UDIV, ASRV).
@(require_results)
inst_r_r_r :: #force_inline proc "contextless" (m: Mnemonic, rd, rn, rm: Register) -> Instruction {
return Instruction{mnemonic = m, operand_count = 3, length = 4,
ops = {op_reg(rd), op_reg(rn), op_reg(rm), {}}}
ops = {op_reg(rd), op_reg(rn), op_reg(rm), {}, {}}}
}
// 4-register R4-type (MADD, MSUB, SMADDL, ...).
@(require_results)
inst_r_r_r_r :: #force_inline proc "contextless" (m: Mnemonic, rd, rn, rm, ra: Register) -> Instruction {
return Instruction{mnemonic = m, operand_count = 4, length = 4,
ops = {op_reg(rd), op_reg(rn), op_reg(rm), op_reg(ra)}}
ops = {op_reg(rd), op_reg(rn), op_reg(rm), op_reg(ra), {}}}
}
// 2-register + immediate (e.g. ADD imm).
@(require_results)
inst_r_r_i :: #force_inline proc "contextless" (m: Mnemonic, rd, rn: Register, imm: i64) -> Instruction {
return Instruction{mnemonic = m, operand_count = 3, length = 4,
ops = {op_reg(rd), op_reg(rn), op_imm(imm), {}}}
ops = {op_reg(rd), op_reg(rn), op_imm(imm), {}, {}}}
}
// 1-register + immediate (e.g. MOVZ).
@(require_results)
inst_r_i :: #force_inline proc "contextless" (m: Mnemonic, rd: Register, imm: i64) -> Instruction {
return Instruction{mnemonic = m, operand_count = 2, length = 4,
ops = {op_reg(rd), op_imm(imm), {}, {}}}
ops = {op_reg(rd), op_imm(imm), {}, {}, {}}}
}
// MOVZ/MOVN/MOVK with explicit hw shift (0/16/32/48).
@(require_results)
inst_mov_imm :: #force_inline proc "contextless" (m: Mnemonic, rd: Register, imm: i64, hw: u8) -> Instruction {
return Instruction{mnemonic = m, operand_count = 3, length = 4,
ops = {op_reg(rd), op_imm(imm), op_imm(i64(hw), 1), {}}}
ops = {op_reg(rd), op_imm(imm), op_imm(i64(hw), 1), {}, {}}}
}
// Load/store register: Rt + memory.
@(require_results)
inst_ldst :: #force_inline proc "contextless" (m: Mnemonic, rt: Register, mm: Memory) -> Instruction {
return Instruction{mnemonic = m, operand_count = 2, length = 4,
ops = {op_reg(rt), op_mem(mm), {}, {}}}
ops = {op_reg(rt), op_mem(mm), {}, {}, {}}}
}
// Load/store pair: Rt, Rt2, memory.
@(require_results)
inst_ldp_stp :: #force_inline proc "contextless" (m: Mnemonic, rt, rt2: Register, mm: Memory) -> Instruction {
return Instruction{mnemonic = m, operand_count = 3, length = 4,
ops = {op_reg(rt), op_reg(rt2), op_mem(mm), {}}}
ops = {op_reg(rt), op_reg(rt2), op_mem(mm), {}, {}}}
}
// PC-relative branch (B, BL).
@(require_results)
inst_branch :: #force_inline proc "contextless" (m: Mnemonic, label_id: u32) -> Instruction {
return Instruction{mnemonic = m, operand_count = 1, length = 4,
ops = {op_label(label_id, 4), {}, {}, {}}}
ops = {op_label(label_id, 4), {}, {}, {}, {}}}
}
// NOTE: the conditional branches, inst_cbz (+cbnz), inst_tbz (+tbnz) and

File diff suppressed because it is too large Load Diff

View File

@@ -44,8 +44,8 @@ PATH_LOADER :: #directory + "/../tables.odin"
Entry :: struct {
mnemonic: lib.Mnemonic,
ops: [4]lib.Operand_Type,
enc: [4]lib.Operand_Encoding,
ops: [5]lib.Operand_Type,
enc: [5]lib.Operand_Encoding,
bits: u32,
mask: u32,
feature: lib.Feature,
@@ -260,13 +260,13 @@ emit_range :: proc(sb: ^strings.Builder, name: string, ranges: []Range) {
// Shared row + flags formatting (compact, matching arm64's original generator)
// -----------------------------------------------------------------------------
write_row :: proc(sb: ^strings.Builder, mn: lib.Mnemonic, ops: [4]lib.Operand_Type,
enc: [4]lib.Operand_Encoding, bits, mask: u32, feature: lib.Feature, flags: lib.Encoding_Flags) {
fmt.sbprintf(sb, "\t{{ .%v, {{.%v,.%v,.%v,.%v}}, {{.%v,.%v,.%v,.%v}}, 0x%08X, 0x%08X, .%v, {{%s}} }},\n",
mn, ops[0], ops[1], ops[2], ops[3], enc[0], enc[1], enc[2], enc[3], bits, mask, feature, flags_lit(flags, ops))
write_row :: proc(sb: ^strings.Builder, mn: lib.Mnemonic, ops: [5]lib.Operand_Type,
enc: [5]lib.Operand_Encoding, bits, mask: u32, feature: lib.Feature, flags: lib.Encoding_Flags) {
fmt.sbprintf(sb, "\t{{ .%v, {{.%v,.%v,.%v,.%v,.%v}}, {{.%v,.%v,.%v,.%v,.%v}}, 0x%08X, 0x%08X, .%v, {{%s}} }},\n",
mn, ops[0], ops[1], ops[2], ops[3], ops[4], enc[0], enc[1], enc[2], enc[3], enc[4], bits, mask, feature, flags_lit(flags, ops))
}
flags_lit :: proc(f: lib.Encoding_Flags, ops: [4]lib.Operand_Type) -> string {
flags_lit :: proc(f: lib.Encoding_Flags, ops: [5]lib.Operand_Type) -> string {
parts: [dynamic]string
defer delete(parts)
if f.branch { append(&parts, "branch=true") }
@@ -321,14 +321,14 @@ Encode_Run :: struct {
Decode_Entry :: struct #packed {
mnemonic: Mnemonic, // 2
ops: [4]Operand_Type, // 4
enc: [4]Operand_Encoding, // 4
ops: [5]Operand_Type, // 4
enc: [5]Operand_Encoding, // 4
bits: u32, // 4
mask: u32, // 4
feature: Feature, // 1
flags: Encoding_Flags, // 1
}
#assert(size_of(Decode_Entry) == 20)
#assert(size_of(Decode_Entry) == 22)
Decode_Index :: struct #packed {
start: u16,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -25,14 +25,14 @@ Encode_Run :: struct {
Decode_Entry :: struct #packed {
mnemonic: Mnemonic, // 2
ops: [4]Operand_Type, // 4
enc: [4]Operand_Encoding, // 4
ops: [5]Operand_Type, // 4
enc: [5]Operand_Encoding, // 4
bits: u32, // 4
mask: u32, // 4
feature: Feature, // 1
flags: Encoding_Flags, // 1
}
#assert(size_of(Decode_Entry) == 20)
#assert(size_of(Decode_Entry) == 22)
Decode_Index :: struct #packed {
start: u16,

View File

@@ -108,7 +108,7 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .ADD, operand_count = 3, length = 4,
ops = {a.op_reg(a.X0), a.op_reg(a.X1), a.op_shifted(a.X2, .LSL, 3), {}},
ops = {a.op_reg(a.X0), a.op_reg(a.X1), a.op_shifted(a.X2, .LSL, 3), {}, {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -126,7 +126,7 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .ADD, operand_count = 3, length = 4,
ops = {a.op_reg(a.X0), a.op_reg(a.SP), a.op_extended(a.W1, .UXTW, 2), {}},
ops = {a.op_reg(a.X0), a.op_reg(a.SP), a.op_extended(a.W1, .UXTW, 2), {}, {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -281,7 +281,7 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .ADR, operand_count = 2, length = 4,
ops = {a.op_reg(a.X0), a.op_label(0, 4), {}, {}},
ops = {a.op_reg(a.X0), a.op_label(0, 4), {}, {}, {}},
},
a.inst_none(.NOP),
a.inst_none(.NOP),
@@ -308,11 +308,11 @@ run_pipeline_tests :: proc() {
a.inst_none(.NOP),
a.Instruction{
mnemonic = .SVC, operand_count = 1, length = 4,
ops = {a.op_imm(1, 2), {}, {}, {}},
ops = {a.op_imm(1, 2), {}, {}, {}, {}},
},
a.Instruction{
mnemonic = .MRS, operand_count = 2, length = 4,
ops = {a.op_reg(a.X0), a.op_sysreg(a.NZCV), {}, {}},
ops = {a.op_reg(a.X0), a.op_sysreg(a.NZCV), {}, {}, {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -484,7 +484,7 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .ADD, operand_count = 3, length = 4,
ops = {a.op_z_s(0), a.op_z_s(1), a.op_z_s(2), {}},
ops = {a.op_z_s(0), a.op_z_s(1), a.op_z_s(2), {}, {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -511,7 +511,7 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .ADD, operand_count = 4, length = 4,
ops = {a.op_z_s(0), a.op_reg(p0), a.op_z_s(0), a.op_z_s(1)},
ops = {a.op_z_s(0), a.op_reg(p0), a.op_z_s(0), a.op_z_s(1), {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -529,7 +529,7 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .PTRUE, operand_count = 2, length = 4,
ops = {a.op_reg(p0), a.op_imm(0x1F, 1), {}, {}},
ops = {a.op_reg(p0), a.op_imm(0x1F, 1), {}, {}, {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -568,8 +568,9 @@ run_pipeline_tests :: proc() {
p1 := a.Register(a.REG_P | 1)
insts := []a.Instruction{
a.Instruction{
mnemonic = .FMOPA, operand_count = 4, length = 4,
ops = {a.op_imm(0, 1), a.op_reg(p0), a.op_reg(p1), a.op_z_s(0)},
mnemonic = .FMOPA, operand_count = 5, length = 4,
ops = {a.op_reg(a.Register(a.REG_ZA | 0)), a.op_reg(p0), a.op_reg(p1),
a.op_z_s(0), a.op_z_s(0)},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -698,19 +699,19 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .MRS, operand_count = 2, length = 4,
ops = {a.op_reg(a.X0), a.op_sysreg(a.NZCV), {}, {}},
ops = {a.op_reg(a.X0), a.op_sysreg(a.NZCV), {}, {}, {}},
},
a.Instruction{
mnemonic = .MRS, operand_count = 2, length = 4,
ops = {a.op_reg(a.X1), a.op_sysreg(a.TPIDR_EL0), {}, {}},
ops = {a.op_reg(a.X1), a.op_sysreg(a.TPIDR_EL0), {}, {}, {}},
},
a.Instruction{
mnemonic = .MRS, operand_count = 2, length = 4,
ops = {a.op_reg(a.X2), a.op_sysreg(a.CNTVCT_EL0), {}, {}},
ops = {a.op_reg(a.X2), a.op_sysreg(a.CNTVCT_EL0), {}, {}, {}},
},
a.Instruction{
mnemonic = .MRS, operand_count = 2, length = 4,
ops = {a.op_reg(a.X3), a.op_sysreg(a.DCZID_EL0), {}, {}},
ops = {a.op_reg(a.X3), a.op_sysreg(a.DCZID_EL0), {}, {}, {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -753,7 +754,7 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .CMP, operand_count = 2, length = 4,
ops = {a.op_reg(a.X0), a.op_shifted(a.X1, .LSL, 0), {}, {}},
ops = {a.op_reg(a.X0), a.op_shifted(a.X1, .LSL, 0), {}, {}, {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -775,15 +776,15 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .CPYP, operand_count = 3, length = 4,
ops = {a.op_reg(a.X0), a.op_reg(a.X1), a.op_reg(a.X2), {}},
ops = {a.op_reg(a.X0), a.op_reg(a.X1), a.op_reg(a.X2), {}, {}},
},
a.Instruction{
mnemonic = .CPYM, operand_count = 3, length = 4,
ops = {a.op_reg(a.X0), a.op_reg(a.X1), a.op_reg(a.X2), {}},
ops = {a.op_reg(a.X0), a.op_reg(a.X1), a.op_reg(a.X2), {}, {}},
},
a.Instruction{
mnemonic = .CPYE, operand_count = 3, length = 4,
ops = {a.op_reg(a.X0), a.op_reg(a.X1), a.op_reg(a.X2), {}},
ops = {a.op_reg(a.X0), a.op_reg(a.X1), a.op_reg(a.X2), {}, {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -805,7 +806,7 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .FMLA, operand_count = 4, length = 4,
ops = {a.op_z_s(0), a.op_z_s(1), a.op_z_s(2), a.op_imm(2, 1)},
ops = {a.op_z_s(0), a.op_z_s(1), a.op_z_s(2), a.op_imm(2, 1), {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -832,7 +833,7 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .LD1W, operand_count = 3, length = 4,
ops = {a.op_z_s(0), a.op_reg(p0), a.op_mem(mem), {}},
ops = {a.op_z_s(0), a.op_reg(p0), a.op_mem(mem), {}, {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -859,7 +860,7 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .LD1B, operand_count = 3, length = 4,
ops = {a.op_za_slice(0, 2, 5, a.ZSHAPE_B, true), a.op_reg(p5), a.op_mem(mem), {}},
ops = {a.op_za_slice(0, 2, 5, a.ZSHAPE_B, true), a.op_reg(p5), a.op_mem(mem), {}, {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -888,7 +889,7 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .FCMLA, operand_count = 4, length = 4,
ops = {a.op_v_4s(a.V0), a.op_v_4s(a.V1), a.op_v_4s(a.V2), a.op_imm(0, 1)},
ops = {a.op_v_4s(a.V0), a.op_v_4s(a.V1), a.op_v_4s(a.V2), a.op_imm(0, 1), {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
@@ -958,7 +959,7 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.Instruction{
mnemonic = .MRS, operand_count = 2, length = 4,
ops = {a.op_reg(a.X7), a.op_sysreg(a.RNDR), {}, {}},
ops = {a.op_reg(a.X7), a.op_sysreg(a.RNDR), {}, {}, {}},
},
}
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)

View File

@@ -77,12 +77,12 @@ Operand_Category :: enum {
}
Operand_Signature :: struct {
types: [4]a.Operand_Type,
types: [5]a.Operand_Type,
// The encoding each operand uses. Needed because the operand TYPE is not
// always enough: .VEC_INDEX is a lane index under NEON_IDX*/NEON_LANE_*,
// which prints glued to its register, but EXT's byte index shares the type
// and prints as a plain `#3`.
encs: [4]a.Operand_Encoding,
encs: [5]a.Operand_Encoding,
count: int,
}
@@ -305,7 +305,7 @@ operand_suffix :: proc(t: a.Operand_Type) -> string {
// included; only truly implicit operands (enc == .IMPL, which AArch64's tables
// never actually use) carry no param.
build_signature :: proc(form: a.Encoding) -> (sig: Operand_Signature, ok: bool) {
for i in 0..<4 {
for i in 0..<5 {
op := form.ops[i]
if op == .NONE { continue }
@@ -333,8 +333,8 @@ Param :: struct {
// the shift/extend kind and amount. This is the single source of truth for
// parameter names; param_list derives the typed declarations from it so the
// declared params always match the expressions that reference them.
operand_primary_names :: proc(sig: Operand_Signature) -> [4][3]string {
result: [4][3]string
operand_primary_names :: proc(sig: Operand_Signature) -> [5][3]string {
result: [5][3]string
reg_count := 0
imm_count := 0
zp_count := 0
@@ -620,7 +620,7 @@ write_inst_fallback :: proc(sb: ^strings.Builder, entry: Proc_Entry) {
defer delete(mstr)
fmt.sbprintf(sb, "Instruction{{mnemonic = .%s, operand_count = %d, length = 4, ops = {{", mstr, sig.count)
for i in 0..<4 {
for i in 0..<5 {
if i > 0 { strings.write_string(sb, ", ") }
if i < sig.count {
write_operand_expr(sb, sig.types[i], sig.encs[i], pnames[i])