2026-10-02 17:58:55 +02:00
|
|
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
|
|
|
|
|
package asm
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"sourcedock.dev/petrbalvin/gasm-sdk/arch"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// TestExtensionRegistryARM64 checks the mnemonic lookup over and above the
|
|
|
|
|
// generated arm64 table: one mnemonic, several forms, case-insensitive, and
|
|
|
|
|
// nothing offered for a spelling the layer does not carry.
|
|
|
|
|
func TestExtensionRegistryARM64(t *testing.T) {
|
|
|
|
|
add, ok := LookupExtension(arch.ARM64, "ADD")
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Fatal("LookupExtension(ARM64, ADD) found nothing")
|
|
|
|
|
}
|
|
|
|
|
var forms []arch.ExtForm
|
|
|
|
|
for _, in := range add {
|
|
|
|
|
if in.Name != "ADD" {
|
|
|
|
|
t.Errorf("candidate %q leaked into the ADD lookup", in.Name)
|
|
|
|
|
}
|
|
|
|
|
forms = append(forms, in.Form)
|
|
|
|
|
}
|
|
|
|
|
if len(forms) != 3 ||
|
|
|
|
|
forms[0] != arch.ExtFormVectors ||
|
|
|
|
|
forms[1] != arch.ExtFormPredicated ||
|
|
|
|
|
forms[2] != arch.ExtFormImmediate {
|
|
|
|
|
t.Errorf("ADD registers forms %v, want unpredicated, predicated and immediate", forms)
|
|
|
|
|
}
|
|
|
|
|
if _, ok := LookupExtension(arch.ARM64, "add"); !ok {
|
|
|
|
|
t.Error("the lookup is case-sensitive")
|
|
|
|
|
}
|
|
|
|
|
if _, ok := LookupExtension(arch.ARM64, "NOSUCHINSTR"); ok {
|
|
|
|
|
t.Error("a non-extended mnemonic resolved")
|
|
|
|
|
}
|
|
|
|
|
sqadd, ok := LookupExtension(arch.ARM64, "SQADD")
|
|
|
|
|
if !ok || len(sqadd) != 2 {
|
|
|
|
|
t.Errorf("SQADD registers %d forms, want the unpredicated and immediate pair", len(sqadd))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestExtensionAboveGeneratedTable pins the layering: SQADD is nowhere in the
|
|
|
|
|
// generated arm64 table (the toolchain knows only the NEON spelling VSQADD)
|
|
|
|
|
// yet the extension layer carries it, while ADD sits in both layers
|
|
|
|
|
// independently.
|
|
|
|
|
func TestExtensionAboveGeneratedTable(t *testing.T) {
|
|
|
|
|
if _, found := arch.ForArch(arch.ARM64).Lookup("SQADD"); found {
|
|
|
|
|
t.Error("SQADD is in the generated table, the layering assumption broke")
|
|
|
|
|
}
|
|
|
|
|
if _, ok := LookupExtension(arch.ARM64, "SQADD"); !ok {
|
|
|
|
|
t.Error("SQADD is missing from the extension layer")
|
|
|
|
|
}
|
|
|
|
|
if _, found := arch.ForArch(arch.ARM64).Lookup("ADD"); !found {
|
|
|
|
|
t.Error("ADD vanished from the generated table")
|
|
|
|
|
}
|
|
|
|
|
if add, ok := LookupExtension(arch.ARM64, "ADD"); !ok || len(add) != 3 {
|
|
|
|
|
t.Errorf("ADD carries %d extension forms, want 3", len(add))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestEncodeExtensionGolden encodes through the registry and pins the same
|
|
|
|
|
// golden words the arch table tests pin, proving the registry resolves to the
|
|
|
|
|
// right encoding.
|
|
|
|
|
func TestEncodeExtensionGolden(t *testing.T) {
|
|
|
|
|
for _, tt := range []struct {
|
|
|
|
|
name string
|
|
|
|
|
mnem string
|
|
|
|
|
ops []arch.ExtOperand
|
|
|
|
|
want uint32
|
|
|
|
|
}{
|
|
|
|
|
{"unpredicated add", "ADD",
|
|
|
|
|
[]arch.ExtOperand{
|
|
|
|
|
arch.ExtVector(2, arch.ExtArrB), arch.ExtVector(0, arch.ExtArrB), arch.ExtVector(0, arch.ExtArrB),
|
|
|
|
|
},
|
|
|
|
|
0x04200040},
|
|
|
|
|
{"predicated mul", "MUL",
|
|
|
|
|
[]arch.ExtOperand{
|
|
|
|
|
arch.ExtVector(0, arch.ExtArrB), arch.ExtPredicate(2, arch.ExtQualMerging), arch.ExtVector(0, arch.ExtArrB),
|
|
|
|
|
},
|
|
|
|
|
0x04100800},
|
|
|
|
|
{"immediate add with derived shift", "ADD",
|
|
|
|
|
[]arch.ExtOperand{arch.ExtImmediate(32512), arch.ExtVector(0, arch.ExtArrH)},
|
|
|
|
|
0x2560efe0},
|
|
|
|
|
{"signed immediate mul", "MUL",
|
|
|
|
|
[]arch.ExtOperand{arch.ExtImmediate(-1), arch.ExtVector(0, arch.ExtArrB)},
|
|
|
|
|
0x2530dfe0},
|
|
|
|
|
} {
|
|
|
|
|
got, err := EncodeExtension(arch.ARM64, tt.mnem, tt.ops...)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("%s: encode: %v", tt.name, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if want := hex.EncodeToString([]byte{
|
|
|
|
|
byte(tt.want), byte(tt.want >> 8), byte(tt.want >> 16), byte(tt.want >> 24),
|
|
|
|
|
}); hex.EncodeToString(got) != want {
|
|
|
|
|
t.Errorf("%s:\n got %x\n want %s", tt.name, got, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestEncodeExtensionErrors checks the registry's diagnostics: a wrong arity
|
|
|
|
|
// names every form's count, an operand the first candidate rejects surfaces
|
|
|
|
|
// its own message once a later form takes over.
|
|
|
|
|
func TestEncodeExtensionErrors(t *testing.T) {
|
|
|
|
|
if _, err := EncodeExtension(arch.ARM64, "ADD", arch.ExtVector(0, arch.ExtArrB)); err == nil {
|
|
|
|
|
t.Error("one operand encoded, want an arity error")
|
|
|
|
|
} else if !strings.Contains(err.Error(), "2 or 3 operands") {
|
|
|
|
|
t.Errorf("arity error %q does not name the counts", err)
|
|
|
|
|
}
|
|
|
|
|
// The predicated candidate must answer for its own operands: the /Z
|
|
|
|
|
// qualifier is rejected with the merging message, not the unpredicated
|
|
|
|
|
// form's register-kind complaint.
|
|
|
|
|
_, err := EncodeExtension(arch.ARM64, "ADD",
|
|
|
|
|
arch.ExtVector(0, arch.ExtArrB), arch.ExtPredicate(0, arch.ExtQualZeroing), arch.ExtVector(0, arch.ExtArrB))
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("/Z encoded, want an error")
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(err.Error(), "/M") {
|
|
|
|
|
t.Errorf("error %q does not name the merging qualifier", err)
|
|
|
|
|
}
|
|
|
|
|
if _, err := EncodeExtension(arch.ARM64, "NOSUCHINSTR", arch.ExtVector(0, arch.ExtArrB)); err == nil ||
|
|
|
|
|
!strings.Contains(err.Error(), "registers no extended instruction") {
|
|
|
|
|
t.Errorf("unknown mnemonic error = %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestExtensionEncodable checks the predicate the later Encodable hook will
|
|
|
|
|
// call: true exactly when the registry encodes the operand list.
|
|
|
|
|
func TestExtensionEncodable(t *testing.T) {
|
|
|
|
|
if !ExtensionEncodable(arch.ARM64, "ADD",
|
|
|
|
|
arch.ExtVector(0, arch.ExtArrS), arch.ExtVector(1, arch.ExtArrS), arch.ExtVector(2, arch.ExtArrS)) {
|
|
|
|
|
t.Error("an encodable unpredicated add reported false")
|
|
|
|
|
}
|
|
|
|
|
if !ExtensionEncodable(arch.ARM64, "ADD",
|
|
|
|
|
arch.ExtVector(1, arch.ExtArrS), arch.ExtPredicate(0, arch.ExtQualMerging), arch.ExtVector(0, arch.ExtArrS)) {
|
|
|
|
|
t.Error("an encodable predicated add reported false")
|
|
|
|
|
}
|
|
|
|
|
if ExtensionEncodable(arch.ARM64, "ADD",
|
|
|
|
|
arch.ExtVector(0, arch.ExtArrB), arch.ExtVector(0, arch.ExtArrS), arch.ExtVector(0, arch.ExtArrB)) {
|
|
|
|
|
t.Error("mismatched arrangements reported encodable")
|
|
|
|
|
}
|
|
|
|
|
if ExtensionEncodable(arch.ARM64, "ADD", arch.ExtVector(0, arch.ExtArrB)) {
|
|
|
|
|
t.Error("a one-operand add reported encodable")
|
|
|
|
|
}
|
|
|
|
|
if ExtensionEncodable(arch.ARM64, "NOSUCHINSTR") {
|
|
|
|
|
t.Error("an unregistered mnemonic reported encodable")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-06 23:43:23 +02:00
|
|
|
// TestExtensionArchIsolation is the architecture-binding negative case: no
|
|
|
|
|
// architecture answers the arm64 mnemonics but arm64, and the amd64 layer
|
|
|
|
|
// answers nothing of the arm64 family either (its own mnemonics live in
|
|
|
|
|
// extension_amd64_test.go).
|
2026-10-02 17:58:55 +02:00
|
|
|
func TestExtensionArchIsolation(t *testing.T) {
|
|
|
|
|
ops := []arch.ExtOperand{
|
|
|
|
|
arch.ExtVector(0, arch.ExtArrB), arch.ExtVector(0, arch.ExtArrB), arch.ExtVector(0, arch.ExtArrB),
|
|
|
|
|
}
|
2026-10-06 23:43:23 +02:00
|
|
|
for _, a := range []arch.Arch{arch.RISCV, arch.LOONG64, arch.Unknown} {
|
2026-10-02 17:58:55 +02:00
|
|
|
if cands, ok := LookupExtension(a, "ADD"); ok || cands != nil {
|
|
|
|
|
t.Errorf("LookupExtension(%s, ADD) offered %d candidates", a, len(cands))
|
|
|
|
|
}
|
|
|
|
|
if cands, ok := LookupExtension(a, "MUL"); ok || cands != nil {
|
|
|
|
|
t.Errorf("LookupExtension(%s, MUL) offered %d candidates", a, len(cands))
|
|
|
|
|
}
|
|
|
|
|
if got, err := EncodeExtension(a, "ADD", ops...); err == nil {
|
|
|
|
|
t.Errorf("EncodeExtension(%s, ADD) encoded %x, want a refusal", a, got)
|
|
|
|
|
} else if !strings.Contains(err.Error(), string(a)) {
|
|
|
|
|
t.Errorf("EncodeExtension(%s) error %q does not name the architecture", a, err)
|
|
|
|
|
}
|
|
|
|
|
if ExtensionEncodable(a, "ADD", ops...) {
|
|
|
|
|
t.Errorf("ExtensionEncodable(%s, ADD) reported true", a)
|
|
|
|
|
}
|
|
|
|
|
if names := ExtensionNames(a); len(names) != 0 {
|
|
|
|
|
t.Errorf("ExtensionNames(%s) = %v, want none", a, names)
|
|
|
|
|
}
|
|
|
|
|
if got := arch.Extensions(a); len(got) != 0 {
|
|
|
|
|
t.Errorf("arch.Extensions(%s) carries %d instructions", a, len(got))
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-06 23:43:23 +02:00
|
|
|
// The amd64 layer exists but stays silent about the arm64 family.
|
|
|
|
|
if cands, ok := LookupExtension(arch.AMD64, "ADD"); ok || cands != nil {
|
|
|
|
|
t.Errorf("LookupExtension(AMD64, ADD) offered %d candidates", len(cands))
|
|
|
|
|
}
|
|
|
|
|
if got, err := EncodeExtension(arch.AMD64, "ADD", ops...); err == nil {
|
|
|
|
|
t.Errorf("EncodeExtension(AMD64, ADD) encoded %x, want a refusal", got)
|
|
|
|
|
} else if !strings.Contains(err.Error(), string(arch.AMD64)) {
|
|
|
|
|
t.Errorf("EncodeExtension(AMD64) error %q does not name the architecture", err)
|
|
|
|
|
}
|
|
|
|
|
if ExtensionEncodable(arch.AMD64, "ADD", ops...) {
|
|
|
|
|
t.Error("ExtensionEncodable(AMD64, ADD) reported true")
|
|
|
|
|
}
|
|
|
|
|
if names := ExtensionNames(arch.AMD64); len(names) == 0 {
|
|
|
|
|
t.Error("the amd64 layer registers no names")
|
|
|
|
|
}
|
|
|
|
|
if got := arch.Extensions(arch.AMD64); len(got) == 0 {
|
|
|
|
|
t.Error("arch.Extensions(AMD64) is empty")
|
|
|
|
|
}
|
2026-10-02 17:58:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestExtensionNamesARM64 checks the completion-facing name list: every
|
|
|
|
|
// distinct mnemonic of the family, first-occurrence order, no duplicates.
|
|
|
|
|
func TestExtensionNamesARM64(t *testing.T) {
|
2026-10-07 00:11:40 +02:00
|
|
|
want := []string{
|
|
|
|
|
// The SVE integer add/subtract/multiply family.
|
|
|
|
|
"ADD", "SUB", "SQADD", "UQADD", "SQSUB", "UQSUB", "MUL", "SMULH", "UMULH", "SUBR",
|
|
|
|
|
// The SVE and SVE2.1 predicate family: the logical operations, the
|
|
|
|
|
// breaks, the permutations, the singles, the first-fault group and
|
|
|
|
|
// the while compares.
|
|
|
|
|
"PAND", "PANDS", "PBIC", "PBICS", "PEOR", "PEORS",
|
|
|
|
|
"PNAND", "PNANDS", "PNOR", "PNORS", "PORN", "PORNS", "PORR", "PORRS",
|
|
|
|
|
"PSEL",
|
|
|
|
|
"PBRKA", "PBRKAS", "PBRKB", "PBRKBS", "PBRKN", "PBRKNS",
|
|
|
|
|
"PBRKPA", "PBRKPAS", "PBRKPB", "PBRKPBS",
|
|
|
|
|
"PTRN1", "PTRN2", "PUZP1", "PUZP2", "PZIP1", "PZIP2",
|
|
|
|
|
"PPFALSE", "PPFIRST", "PPNEXT", "PPTEST", "PPTRUE", "PPUNPKHI", "PPUNPKLO",
|
|
|
|
|
"PRDFFR", "PRDFFRS", "PWRFFR", "PREV", "SETFFR",
|
|
|
|
|
"PWHILEGE", "PWHILEGT", "PWHILEHI", "PWHILEHS",
|
|
|
|
|
"PWHILELE", "PWHILELO", "PWHILELS", "PWHILELT", "PWHILERW", "PWHILEWR",
|
2026-10-07 02:20:25 +02:00
|
|
|
// The SVE2.1 Z-alias families, in first-occurrence order.
|
|
|
|
|
"ZABS",
|
|
|
|
|
"ZREVB",
|
|
|
|
|
"ZREVH",
|
|
|
|
|
"ZSXTB",
|
|
|
|
|
"ZSXTH",
|
|
|
|
|
"ZUXTB",
|
|
|
|
|
"ZUXTH",
|
|
|
|
|
"ZADD",
|
|
|
|
|
"ZAND",
|
|
|
|
|
"ZBIC",
|
|
|
|
|
"ZCLS",
|
|
|
|
|
"ZCLZ",
|
|
|
|
|
"ZCNT",
|
|
|
|
|
"ZCOMPACT",
|
|
|
|
|
"ZDUP",
|
|
|
|
|
"ZEOR",
|
|
|
|
|
"ZEXPAND",
|
|
|
|
|
"ZINSR",
|
|
|
|
|
"ZLASTA",
|
|
|
|
|
"ZLASTB",
|
|
|
|
|
"ZMOVPRFX",
|
|
|
|
|
"ZREVD",
|
|
|
|
|
"ZREVW",
|
|
|
|
|
"ZSXTW",
|
|
|
|
|
"ZUXTW",
|
|
|
|
|
"ZNEG",
|
|
|
|
|
"ZNOT",
|
|
|
|
|
"ZORR",
|
|
|
|
|
"ZRBIT",
|
|
|
|
|
"ZREV",
|
|
|
|
|
"ZSEL",
|
|
|
|
|
"ZSQABS",
|
|
|
|
|
"ZSQNEG",
|
|
|
|
|
"ZSUB",
|
|
|
|
|
"ZSUBR",
|
|
|
|
|
"ZSUNPKHI",
|
|
|
|
|
"ZSUNPKLO",
|
|
|
|
|
"ZTBX",
|
|
|
|
|
"ZTBXQ",
|
|
|
|
|
"ZTRN1",
|
|
|
|
|
"ZTRN2",
|
|
|
|
|
"ZUUNPKHI",
|
|
|
|
|
"ZUUNPKLO",
|
|
|
|
|
"ZUZP1",
|
|
|
|
|
"ZUZP2",
|
|
|
|
|
"ZUZPQ1",
|
|
|
|
|
"ZUZPQ2",
|
|
|
|
|
"ZZIP1",
|
|
|
|
|
"ZZIP2",
|
|
|
|
|
"ZZIPQ1",
|
|
|
|
|
"ZZIPQ2",
|
2026-10-07 12:59:42 +02:00
|
|
|
// The stage-three families: the SVE2 crypto group, the predicate
|
|
|
|
|
// counters and loop terminators with the 32-bit while compares, and
|
|
|
|
|
// the reductions into a SIMD register.
|
|
|
|
|
"ZADCLB",
|
|
|
|
|
"ZADCLT",
|
|
|
|
|
"ZSBCLB",
|
|
|
|
|
"ZSBCLT",
|
|
|
|
|
"ZRAX1",
|
|
|
|
|
"ZSM4EKEY",
|
|
|
|
|
"ZSM4E",
|
|
|
|
|
"ZAESD",
|
|
|
|
|
"ZAESE",
|
|
|
|
|
"ZAESIMC",
|
|
|
|
|
"ZAESMC",
|
|
|
|
|
"CTERMEQ",
|
|
|
|
|
"CTERMEQW",
|
|
|
|
|
"CTERMNE",
|
|
|
|
|
"CTERMNEW",
|
|
|
|
|
"PCNTP",
|
|
|
|
|
"PFIRSTP",
|
|
|
|
|
"PLASTP",
|
|
|
|
|
"PDECP",
|
|
|
|
|
"PINCP",
|
|
|
|
|
"PSQDECP",
|
|
|
|
|
"PSQINCP",
|
|
|
|
|
"PUQDECP",
|
|
|
|
|
"PUQINCP",
|
|
|
|
|
"PUQDECPW",
|
|
|
|
|
"PUQINCPW",
|
|
|
|
|
"PSQDECPW",
|
|
|
|
|
"PSQINCPW",
|
|
|
|
|
"PWHILEGEW",
|
|
|
|
|
"PWHILEGTW",
|
|
|
|
|
"PWHILEHIW",
|
|
|
|
|
"PWHILEHSW",
|
|
|
|
|
"PWHILELEW",
|
|
|
|
|
"PWHILELOW",
|
|
|
|
|
"PWHILELSW",
|
|
|
|
|
"PWHILELTW",
|
|
|
|
|
"ZSADDVD",
|
|
|
|
|
"ZUADDVD",
|
|
|
|
|
"ZANDVB",
|
|
|
|
|
"ZANDVH",
|
|
|
|
|
"ZANDVS",
|
|
|
|
|
"ZANDVD",
|
|
|
|
|
"ZEORVB",
|
|
|
|
|
"ZEORVH",
|
|
|
|
|
"ZEORVS",
|
|
|
|
|
"ZEORVD",
|
|
|
|
|
"ZORVB",
|
|
|
|
|
"ZORVH",
|
|
|
|
|
"ZORVS",
|
|
|
|
|
"ZORVD",
|
|
|
|
|
"ZSMAXVB",
|
|
|
|
|
"ZSMAXVH",
|
|
|
|
|
"ZSMAXVS",
|
|
|
|
|
"ZSMAXVD",
|
|
|
|
|
"ZSMINVB",
|
|
|
|
|
"ZSMINVH",
|
|
|
|
|
"ZSMINVS",
|
|
|
|
|
"ZSMINVD",
|
|
|
|
|
"ZUMAXVB",
|
|
|
|
|
"ZUMAXVH",
|
|
|
|
|
"ZUMAXVS",
|
|
|
|
|
"ZUMAXVD",
|
|
|
|
|
"ZUMINVB",
|
|
|
|
|
"ZUMINVH",
|
|
|
|
|
"ZUMINVS",
|
|
|
|
|
"ZUMINVD",
|
|
|
|
|
"ZFADDVH",
|
|
|
|
|
"ZFADDVS",
|
|
|
|
|
"ZFADDVD",
|
|
|
|
|
"ZFMAXNMVH",
|
|
|
|
|
"ZFMAXNMVS",
|
|
|
|
|
"ZFMAXNMVD",
|
|
|
|
|
"ZFMAXVH",
|
|
|
|
|
"ZFMAXVS",
|
|
|
|
|
"ZFMAXVD",
|
|
|
|
|
"ZFMINNMVH",
|
|
|
|
|
"ZFMINNMVS",
|
|
|
|
|
"ZFMINNMVD",
|
|
|
|
|
"ZFMINVH",
|
|
|
|
|
"ZFMINVS",
|
|
|
|
|
"ZFMINVD",
|
|
|
|
|
"ZFADDAH",
|
|
|
|
|
"ZFADDAS",
|
|
|
|
|
"ZFADDAD",
|
2026-10-07 13:24:20 +02:00
|
|
|
// The gather loads and scatter stores: plain, sign-extended and
|
|
|
|
|
// first-fault loads, and the stores, in first-occurrence order.
|
|
|
|
|
"ZLD1B",
|
|
|
|
|
"ZLD1D",
|
|
|
|
|
"ZLD1H",
|
|
|
|
|
"ZLD1SB",
|
|
|
|
|
"ZLD1SH",
|
|
|
|
|
"ZLD1SW",
|
|
|
|
|
"ZLD1W",
|
|
|
|
|
"ZLDFF1B",
|
|
|
|
|
"ZLDFF1D",
|
|
|
|
|
"ZLDFF1H",
|
|
|
|
|
"ZLDFF1SB",
|
|
|
|
|
"ZLDFF1SH",
|
|
|
|
|
"ZLDFF1SW",
|
|
|
|
|
"ZLDFF1W",
|
|
|
|
|
"ZST1B",
|
|
|
|
|
"ZST1D",
|
|
|
|
|
"ZST1H",
|
|
|
|
|
"ZST1W",
|
2026-10-07 00:11:40 +02:00
|
|
|
}
|
2026-10-02 17:58:55 +02:00
|
|
|
got := ExtensionNames(arch.ARM64)
|
|
|
|
|
if strings.Join(got, ",") != strings.Join(want, ",") {
|
|
|
|
|
t.Errorf("ExtensionNames(ARM64) = %v, want %v", got, want)
|
|
|
|
|
}
|
2026-10-07 13:24:20 +02:00
|
|
|
if n := len(arch.Extensions(arch.ARM64)); n != 381 {
|
|
|
|
|
t.Errorf("the family registers %d instructions, want 381", n)
|
2026-10-02 17:58:55 +02:00
|
|
|
}
|
|
|
|
|
}
|