feat(arch): add the extended-instruction layer with SVE arithmetic
Test / test (push) Successful in 3m38s
Test / test (push) Successful in 3m38s
This commit is contained in:
@@ -0,0 +1,400 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user