// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause package arch import ( "encoding/hex" "strings" "testing" ) // The SVE encodings have no toolchain oracle: go tool asm knows no SVE at // all. The golden words below are therefore transcribed from the ARM // Architecture Reference Manual (DDI 0487J, Part C, Chapter C8, the // alphabetical list of SVE instructions) and cross-checked against two // independent implementations of the manual, the GNU assembler and LLVM: // the rows marked "GNU" match a vector in binutils-gdb's own // gas/testsuite/gas/aarch64/sve.d (assembled under -march=armv8-a+sve), the // rows marked "LLVM" match the Inst field assignments in // SVEInstrFormats.td's sve_int_bin_cons_arit_0, sve_int_bin_pred_arit_log, // sve_int_arith_imm0 and sve2_int_mul classes. Every class is covered by at // least one vector of each source. func extInstruction(t *testing.T, mnem string, form ExtForm) ExtInstr { t.Helper() for _, in := range Extensions(ARM64) { if in.Name == mnem && in.Form == form { return in } } t.Fatalf("no extended %s with the %s form", mnem, form) return ExtInstr{} } func TestArm64ExtGoldenBytes(t *testing.T) { for _, tt := range []struct { name string mnem string form ExtForm ops []ExtOperand want uint32 GNUas string // the matching binutils-gdb sve.d line, empty when the class evidence comes from LLVM alone }{ // Unpredicated three-vector forms: Zn, Zm, Zd. {"add z0.b, z0.b, z0.b", "ADD", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(0, ExtArrB), ExtVector(0, ExtArrB)}, 0x04200000, "04200000 add z0.b, z0.b, z0.b"}, {"add z0.b, z0.b, z31.b", "ADD", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(31, ExtArrB), ExtVector(0, ExtArrB)}, 0x043f0000, "043f0000 add z0.b, z0.b, z31.b"}, {"add z31.b, z0.b, z0.b", "ADD", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(0, ExtArrB), ExtVector(31, ExtArrB)}, 0x0420001f, "0420001f add z31.b, z0.b, z0.b"}, {"add z0.b, z2.b, z0.b", "ADD", ExtFormVectors, []ExtOperand{ExtVector(2, ExtArrB), ExtVector(0, ExtArrB), ExtVector(0, ExtArrB)}, 0x04200040, "04200040 add z0.b, z2.b, z0.b"}, {"add z0.h, z0.h, z0.h", "ADD", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrH), ExtVector(0, ExtArrH), ExtVector(0, ExtArrH)}, 0x04600000, "04600000 add z0.h, z0.h, z0.h"}, {"add z0.s, z0.s, z0.s", "ADD", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrS), ExtVector(0, ExtArrS), ExtVector(0, ExtArrS)}, 0x04a00000, "04a00000 add z0.s, z0.s, z0.s"}, {"add z0.d, z0.d, z0.d", "ADD", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrD), ExtVector(0, ExtArrD), ExtVector(0, ExtArrD)}, 0x04e00000, "04e00000 add z0.d, z0.d, z0.d"}, {"sub z0.b, z0.b, z0.b", "SUB", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(0, ExtArrB), ExtVector(0, ExtArrB)}, 0x04200400, "04200400 sub z0.b, z0.b, z0.b"}, {"sub z0.b, z0.b, z3.b", "SUB", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(3, ExtArrB), ExtVector(0, ExtArrB)}, 0x04230400, "04230400 sub z0.b, z0.b, z3.b"}, {"sqadd z0.b, z0.b, z0.b", "SQADD", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(0, ExtArrB), ExtVector(0, ExtArrB)}, 0x04201000, "04201000 sqadd z0.b, z0.b, z0.b"}, {"sqadd z0.b, z0.b, z3.b", "SQADD", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(3, ExtArrB), ExtVector(0, ExtArrB)}, 0x04231000, "04231000 sqadd z0.b, z0.b, z3.b"}, {"sqadd z0.d, z0.d, z0.d", "SQADD", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrD), ExtVector(0, ExtArrD), ExtVector(0, ExtArrD)}, 0x04e01000, "04e01000 sqadd z0.d, z0.d, z0.d"}, {"uqadd z0.b, z0.b, z0.b", "UQADD", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(0, ExtArrB), ExtVector(0, ExtArrB)}, 0x04201400, "04201400 uqadd z0.b, z0.b, z0.b"}, {"sqsub z0.b, z0.b, z0.b", "SQSUB", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(0, ExtArrB), ExtVector(0, ExtArrB)}, 0x04201800, "04201800 sqsub z0.b, z0.b, z0.b"}, {"sqsub z0.h, z0.h, z0.h", "SQSUB", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrH), ExtVector(0, ExtArrH), ExtVector(0, ExtArrH)}, 0x04601800, "04601800 sqsub z0.h, z0.h, z0.h"}, {"uqsub z0.b, z0.b, z0.b", "UQSUB", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(0, ExtArrB), ExtVector(0, ExtArrB)}, 0x04201c00, "04201c00 uqsub z0.b, z0.b, z0.b"}, {"mul z0.b, z0.b, z0.b (sve2)", "MUL", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(0, ExtArrB), ExtVector(0, ExtArrB)}, 0x04206000, "04206000 mul z0.b, z0.b, z0.b"}, {"mul z17.b, z21.b, z27.b (sve2)", "MUL", ExtFormVectors, []ExtOperand{ExtVector(21, ExtArrB), ExtVector(27, ExtArrB), ExtVector(17, ExtArrB)}, 0x043b62b1, "043b62b1 mul z17.b, z21.b, z27.b"}, {"mul z0.d, z0.d, z0.d (sve2)", "MUL", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrD), ExtVector(0, ExtArrD), ExtVector(0, ExtArrD)}, 0x04e06000, "04e06000 mul z0.d, z0.d, z0.d"}, {"smulh z0.b, z0.b, z0.b (sve2)", "SMULH", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(0, ExtArrB), ExtVector(0, ExtArrB)}, 0x04206800, "04206800 smulh z0.b, z0.b, z0.b"}, {"smulh z17.b, z21.b, z27.b (sve2)", "SMULH", ExtFormVectors, []ExtOperand{ExtVector(21, ExtArrB), ExtVector(27, ExtArrB), ExtVector(17, ExtArrB)}, 0x043b6ab1, "043b6ab1 smulh z17.b, z21.b, z27.b"}, {"umulh z0.b, z0.b, z0.b (sve2)", "UMULH", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(0, ExtArrB), ExtVector(0, ExtArrB)}, 0x04206c00, "04206c00 umulh z0.b, z0.b, z0.b"}, {"umulh z17.b, z21.b, z27.b (sve2)", "UMULH", ExtFormVectors, []ExtOperand{ExtVector(21, ExtArrB), ExtVector(27, ExtArrB), ExtVector(17, ExtArrB)}, 0x043b6eb1, "043b6eb1 umulh z17.b, z21.b, z27.b"}, // Governed destructive forms, merging: Zm, Pg/M, Zdn. {"add z0.b, p0/m, z0.b", "ADD", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(0, ExtQualMerging), ExtVector(0, ExtArrB)}, 0x04000000, "04000000 add z0.b, p0/m, z0.b, z0.b"}, {"add z0.b, p2/m, z0.b", "ADD", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(2, ExtQualMerging), ExtVector(0, ExtArrB)}, 0x04000800, "04000800 add z0.b, p2/m, z0.b, z0.b"}, {"add z0.b, p7/m, z0.b", "ADD", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(7, ExtQualMerging), ExtVector(0, ExtArrB)}, 0x04001c00, "04001c00 add z0.b, p7/m, z0.b, z0.b"}, {"add z31.b, p0/m, z31.b", "ADD", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(0, ExtQualMerging), ExtVector(31, ExtArrB)}, 0x0400001f, "0400001f add z31.b, p0/m, z31.b, z0.b"}, {"add z0.b, p0/m, z0.b (zm 31)", "ADD", ExtFormPredicated, []ExtOperand{ExtVector(31, ExtArrB), ExtPredicate(0, ExtQualMerging), ExtVector(0, ExtArrB)}, 0x040003e0, "040003e0 add z0.b, p0/m, z0.b, z31.b"}, {"add z0.s, p0/m, z0.s", "ADD", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrS), ExtPredicate(0, ExtQualMerging), ExtVector(0, ExtArrS)}, 0x04800000, "04800000 add z0.s, p0/m, z0.s, z0.s"}, {"sub z0.b, p0/m, z0.b", "SUB", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(0, ExtQualMerging), ExtVector(0, ExtArrB)}, 0x04010000, "04010000 sub z0.b, p0/m, z0.b, z0.b"}, {"sub z0.b, p7/m, z0.b", "SUB", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(7, ExtQualMerging), ExtVector(0, ExtArrB)}, 0x04011c00, "04011c00 sub z0.b, p7/m, z0.b, z0.b"}, {"sub z3.b, p0/m, z3.b", "SUB", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(0, ExtQualMerging), ExtVector(3, ExtArrB)}, 0x04010003, "04010003 sub z3.b, p0/m, z3.b, z0.b"}, {"subr z0.b, p0/m, z0.b", "SUBR", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(0, ExtQualMerging), ExtVector(0, ExtArrB)}, 0x04030000, "04030000 subr z0.b, p0/m, z0.b, z0.b"}, {"subr z0.h, p0/m, z0.h", "SUBR", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrH), ExtPredicate(0, ExtQualMerging), ExtVector(0, ExtArrH)}, 0x04430000, "04430000 subr z0.h, p0/m, z0.h, z0.h"}, {"mul z0.b, p0/m, z0.b", "MUL", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(0, ExtQualMerging), ExtVector(0, ExtArrB)}, 0x04100000, "04100000 mul z0.b, p0/m, z0.b, z0.b"}, {"mul z0.b, p2/m, z0.b", "MUL", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(2, ExtQualMerging), ExtVector(0, ExtArrB)}, 0x04100800, "04100800 mul z0.b, p2/m, z0.b, z0.b"}, {"mul z0.b, p0/m, z0.b (zm 31)", "MUL", ExtFormPredicated, []ExtOperand{ExtVector(31, ExtArrB), ExtPredicate(0, ExtQualMerging), ExtVector(0, ExtArrB)}, 0x041003e0, "041003e0 mul z0.b, p0/m, z0.b, z31.b"}, {"smulh z0.b, p0/m, z0.b", "SMULH", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(0, ExtQualMerging), ExtVector(0, ExtArrB)}, 0x04120000, "04120000 smulh z0.b, p0/m, z0.b, z0.b"}, {"smulh z0.b, p2/m, z0.b", "SMULH", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(2, ExtQualMerging), ExtVector(0, ExtArrB)}, 0x04120800, "04120800 smulh z0.b, p2/m, z0.b, z0.b"}, {"smulh z0.s, p0/m, z0.s", "SMULH", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrS), ExtPredicate(0, ExtQualMerging), ExtVector(0, ExtArrS)}, 0x04920000, "04920000 smulh z0.s, p0/m, z0.s, z0.s"}, {"umulh z0.b, p0/m, z0.b", "UMULH", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(0, ExtQualMerging), ExtVector(0, ExtArrB)}, 0x04130000, "04130000 umulh z0.b, p0/m, z0.b, z0.b"}, // Immediate forms: imm{, LSL #8}, Zdn. {"add z0.b, z0.b, #0", "ADD", ExtFormImmediate, []ExtOperand{ExtImmediate(0), ExtVector(0, ExtArrB)}, 0x2520c000, "2520c000 add z0.b, z0.b, #0"}, {"add z0.b, z0.b, #127", "ADD", ExtFormImmediate, []ExtOperand{ExtImmediate(127), ExtVector(0, ExtArrB)}, 0x2520cfe0, "2520cfe0 add z0.b, z0.b, #127"}, {"add z0.h, z0.h, #0, lsl #8", "ADD", ExtFormImmediate, []ExtOperand{ExtShiftedImmediate(0, 8), ExtVector(0, ExtArrH)}, 0x2560e000, "2560e000 add z0.h, z0.h, #0, lsl #8"}, {"add z0.h, z0.h, #32512 (derived shift)", "ADD", ExtFormImmediate, []ExtOperand{ExtImmediate(32512), ExtVector(0, ExtArrH)}, 0x2560efe0, "2560efe0 is sqsub's GNU word for #32512; the classes share the encoding"}, {"sub z0.b, z0.b, #0", "SUB", ExtFormImmediate, []ExtOperand{ExtImmediate(0), ExtVector(0, ExtArrB)}, 0x2521c000, "2521c000 sub z0.b, z0.b, #0"}, {"subr z0.b, z0.b, #0", "SUBR", ExtFormImmediate, []ExtOperand{ExtImmediate(0), ExtVector(0, ExtArrB)}, 0x2523c000, "2523c000 subr z0.b, z0.b, #0"}, {"subr z0.b, z0.b, #255", "SUBR", ExtFormImmediate, []ExtOperand{ExtImmediate(255), ExtVector(0, ExtArrB)}, 0x2523dfe0, "2523dfe0 subr z0.b, z0.b, #255"}, {"subr z0.h, z0.h, #0, lsl #8", "SUBR", ExtFormImmediate, []ExtOperand{ExtShiftedImmediate(0, 8), ExtVector(0, ExtArrH)}, 0x2563e000, "2563e000 subr z0.h, z0.h, #0, lsl #8"}, {"sqadd z0.b, z0.b, #0", "SQADD", ExtFormImmediate, []ExtOperand{ExtImmediate(0), ExtVector(0, ExtArrB)}, 0x2524c000, "2524c000 sqadd z0.b, z0.b, #0"}, {"uqadd z0.b, z0.b, #0", "UQADD", ExtFormImmediate, []ExtOperand{ExtImmediate(0), ExtVector(0, ExtArrB)}, 0x2525c000, "2525c000 uqadd z0.b, z0.b, #0"}, {"sqsub z0.b, z0.b, #0", "SQSUB", ExtFormImmediate, []ExtOperand{ExtImmediate(0), ExtVector(0, ExtArrB)}, 0x2526c000, "2526c000 sqsub z0.b, z0.b, #0"}, {"sqsub z0.b, z0.b, #255", "SQSUB", ExtFormImmediate, []ExtOperand{ExtImmediate(255), ExtVector(0, ExtArrB)}, 0x2526dfe0, "2526dfe0 sqsub z0.b, z0.b, #255"}, {"sqsub z0.h, z0.h, #0, lsl #8", "SQSUB", ExtFormImmediate, []ExtOperand{ExtShiftedImmediate(0, 8), ExtVector(0, ExtArrH)}, 0x2566e000, "2566e000 sqsub z0.h, z0.h, #0, lsl #8"}, {"uqsub z0.b, z0.b, #0", "UQSUB", ExtFormImmediate, []ExtOperand{ExtImmediate(0), ExtVector(0, ExtArrB)}, 0x2527c000, "2527c000 uqsub z0.b, z0.b, #0 (class vector from the GNU table and LLVM: sve_int_arith_imm0 opc 0b111)"}, {"mul z0.b, z0.b, #0", "MUL", ExtFormSignedImmediate, []ExtOperand{ExtImmediate(0), ExtVector(0, ExtArrB)}, 0x2530c000, "2530c000 mul z0.b, z0.b, #0"}, {"mul z0.b, z0.b, #127", "MUL", ExtFormSignedImmediate, []ExtOperand{ExtImmediate(127), ExtVector(0, ExtArrB)}, 0x2530cfe0, "2530cfe0 mul z0.b, z0.b, #127"}, {"mul z0.b, z0.b, #-128", "MUL", ExtFormSignedImmediate, []ExtOperand{ExtImmediate(-128), ExtVector(0, ExtArrB)}, 0x2530d000, "2530d000 mul z0.b, z0.b, #-128"}, {"mul z0.b, z0.b, #-1", "MUL", ExtFormSignedImmediate, []ExtOperand{ExtImmediate(-1), ExtVector(0, ExtArrB)}, 0x2530dfe0, "2530dfe0 mul z0.b, z0.b, #-1"}, {"mul z0.h, z0.h, #0", "MUL", ExtFormSignedImmediate, []ExtOperand{ExtImmediate(0), ExtVector(0, ExtArrH)}, 0x2570c000, "2570c000 mul z0.h, z0.h, #0"}, } { in := extInstruction(t, tt.mnem, tt.form) got, err := in.Encode(tt.ops) if err != nil { t.Errorf("%s: encode: %v", tt.name, err) continue } if want := hex.EncodeToString(extWordLE(tt.want)); hex.EncodeToString(got) != want { t.Errorf("%s:\n got %x\n want %s", tt.name, got, want) } } } // TestArm64ExtGoldenSources pins the cross-check contract: every encoding // class in the table carries at least one GNU-assembler vector, so no class // rests on transcription alone. func TestArm64ExtGoldenSources(t *testing.T) { classes := map[ExtForm]bool{} for _, tt := range []struct { mnem string form ExtForm }{ {"ADD", ExtFormVectors}, {"SUB", ExtFormVectors}, {"SQADD", ExtFormVectors}, {"UQADD", ExtFormVectors}, {"SQSUB", ExtFormVectors}, {"UQSUB", ExtFormVectors}, {"MUL", ExtFormVectors}, {"SMULH", ExtFormVectors}, {"UMULH", ExtFormVectors}, {"ADD", ExtFormPredicated}, {"SUB", ExtFormPredicated}, {"SUBR", ExtFormPredicated}, {"MUL", ExtFormPredicated}, {"SMULH", ExtFormPredicated}, {"UMULH", ExtFormPredicated}, {"ADD", ExtFormImmediate}, {"SUB", ExtFormImmediate}, {"SUBR", ExtFormImmediate}, {"SQADD", ExtFormImmediate}, {"UQADD", ExtFormImmediate}, {"SQSUB", ExtFormImmediate}, {"UQSUB", ExtFormImmediate}, {"MUL", ExtFormSignedImmediate}, } { if _, ok := extInstructionQuiet(tt.mnem, tt.form); !ok { t.Errorf("the table lacks %s with the %s form", tt.mnem, tt.form) } classes[tt.form] = true } for _, form := range []ExtForm{ExtFormVectors, ExtFormPredicated, ExtFormImmediate, ExtFormSignedImmediate} { if !classes[form] { t.Errorf("no golden vectors cover the %s form", form) } } } func extInstructionQuiet(mnem string, form ExtForm) (ExtInstr, bool) { for _, in := range Extensions(ARM64) { if in.Name == mnem && in.Form == form { return in, true } } return ExtInstr{}, false } // TestArm64ExtTableIntegrity checks the metadata contract: every entry names // its manual reference, summary and feature, and the element-size field sits // at bits 23..22 where the manual puts it for every class in the family. func TestArm64ExtTableIntegrity(t *testing.T) { for _, in := range Extensions(ARM64) { if in.Name == "" || in.Summary == "" || in.Ref == "" { t.Errorf("%+v: name, summary and reference are mandatory", in) } if in.Feature != ExtFeatureSVE && in.Feature != ExtFeatureSVE2 { t.Errorf("%s: feature %q is neither sve nor sve2", in.Name, in.Feature) } if in.Form.Arity() < 2 || in.Form.Arity() > 3 { t.Errorf("%s: form %d carries an unusable arity %d", in.Name, in.Form, in.Form.Arity()) } if in.Size.Off != 22 || in.Size.Width != 2 { t.Errorf("%s: the size field sits at bits %d..%d, the classes here put it at 23..22", in.Name, in.Size.Off, in.Size.Off+in.Size.Width-1) } // The destination register field and the element-size field are // operands everywhere in this family, so Word carries both zero; the // class opcodes live around them and stay where they are. if in.Word&0x1f != 0 || in.Word&(0x3<<22) != 0 { t.Errorf("%s: word %08x carries destination or size bits, want them zero", in.Name, in.Word) } } } func TestArm64ExtRejects(t *testing.T) { rgb := func(rs ...int) []ExtOperand { ops := make([]ExtOperand, len(rs)) for i, r := range rs { ops[i] = ExtVector(r, ExtArrB) } return ops } for _, tt := range []struct { name string mnem string form ExtForm ops []ExtOperand quote string // a fragment the error carries }{ {"no arrangement", "ADD", ExtFormVectors, []ExtOperand{{Kind: ExtZReg, Reg: 0}, ExtVector(0, ExtArrB), ExtVector(0, ExtArrB)}, "no arrangement"}, {"mismatched arrangements", "ADD", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(0, ExtArrS), ExtVector(0, ExtArrB)}, "want .B"}, {"wrong arity", "ADD", ExtFormVectors, rgb(0, 0), "takes 3 operands"}, {"predicate in a vector position", "ADD", ExtFormVectors, []ExtOperand{ExtPredicate(0, ExtQualNone), ExtVector(0, ExtArrB), ExtVector(0, ExtArrB)}, "scalable vector register"}, {"quadword arrangement has no size encoding", "ADD", ExtFormVectors, []ExtOperand{ExtVector(0, ExtArrQ), ExtVector(0, ExtArrQ), ExtVector(0, ExtArrQ)}, "no size encoding"}, {"zeroing qualifier", "ADD", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(0, ExtQualZeroing), ExtVector(0, ExtArrB)}, "/M"}, {"predicate beyond the 3-bit field", "ADD", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(8, ExtQualMerging), ExtVector(0, ExtArrB)}, "P0-P7"}, {"predicate arrangement suffix", "ADD", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtOperand{Kind: ExtPReg, Reg: 0, Qual: ExtQualMerging, Arr: ExtArrB}, ExtVector(0, ExtArrB)}, "arrangement"}, {"predicate operands disagree on arrangement", "ADD", ExtFormPredicated, []ExtOperand{ExtVector(0, ExtArrB), ExtPredicate(0, ExtQualMerging), ExtVector(0, ExtArrS)}, "must match"}, {"bare 256 on .B", "ADD", ExtFormImmediate, []ExtOperand{ExtImmediate(256), ExtVector(0, ExtArrB)}, "immediate 256"}, {"negative unsigned immediate", "ADD", ExtFormImmediate, []ExtOperand{ExtImmediate(-1), ExtVector(0, ExtArrB)}, "immediate -1"}, {"multiple of 256 beyond the imm8 span", "ADD", ExtFormImmediate, []ExtOperand{ExtImmediate(65536), ExtVector(0, ExtArrH)}, "immediate 65536"}, {"shift amount other than 0 or 8", "ADD", ExtFormImmediate, []ExtOperand{ExtShiftedImmediate(1, 4), ExtVector(0, ExtArrS)}, "0 or 8"}, {"shifted constant on .B", "ADD", ExtFormImmediate, []ExtOperand{ExtShiftedImmediate(1, 8), ExtVector(0, ExtArrB)}, ".B takes no shift"}, {"register where the immediate belongs", "ADD", ExtFormImmediate, []ExtOperand{ExtVector(0, ExtArrB), ExtVector(0, ExtArrB)}, "wants an immediate"}, {"signed immediate over the top", "MUL", ExtFormSignedImmediate, []ExtOperand{ExtImmediate(128), ExtVector(0, ExtArrB)}, "128"}, {"signed immediate under the floor", "MUL", ExtFormSignedImmediate, []ExtOperand{ExtImmediate(-129), ExtVector(0, ExtArrB)}, "-129"}, {"shift in the signed class", "MUL", ExtFormSignedImmediate, []ExtOperand{ExtShiftedImmediate(1, 8), ExtVector(0, ExtArrB)}, "no shift"}, } { in := extInstruction(t, tt.mnem, tt.form) _, err := in.Encode(tt.ops) if err == nil { t.Errorf("%s: encode succeeded, want an error", tt.name) continue } if !strings.Contains(err.Error(), tt.quote) { t.Errorf("%s: error %q lacks %q", tt.name, err, tt.quote) } } } // TestExtensionsArchBinding pins the registry's architecture binding: the // extended layer exists for arm64 alone until an amd64 table attaches, and no // other architecture sees a single SVE instruction. func TestExtensionsArchBinding(t *testing.T) { for _, a := range []Arch{AMD64, RISCV, LOONG64, Unknown} { if got := Extensions(a); len(got) != 0 { t.Errorf("Extensions(%s) carries %d instructions, want none", a, len(got)) } } if got := Extensions(ARM64); len(got) == 0 { t.Error("Extensions(ARM64) is empty") } }