mirror of
https://github.com/odin-lang/Odin.git
synced 2026-09-02 02:03:35 +00:00
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:
@@ -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 {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
Reference in New Issue
Block a user