// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause package arch import "testing" func TestFromFilename(t *testing.T) { cases := map[string]Arch{ "avx2_amd64.s": AMD64, "foo_arm64.s": ARM64, "portable.s": Unknown, "decode_ARM64.S": ARM64, "kernels_amd64.s": AMD64, "kernel_riscv64.s": RISCV, "kernel_loong64.s": LOONG64, } for name, want := range cases { if got := FromFilename(name); got != want { t.Errorf("FromFilename(%q) = %q, want %q", name, got, want) } } } func TestAMD64Registers(t *testing.T) { tab := ForArch(AMD64) for _, reg := range []string{"AX", "BX", "R15", "AL", "X0", "Y15", "Z31", "K7"} { if !tab.IsRegister(reg) { t.Errorf("amd64: %s should be a register", reg) } } for _, not := range []string{"vec1", "R16", "Z32", "K8", "swin_base"} { if tab.IsRegister(not) { t.Errorf("amd64: %s should NOT be a register", not) } } if r, ok := tab.Register("Y0"); !ok || r.Class != Vector { t.Errorf("Y0 class = %+v, want Vector", r) } if r, ok := tab.Register("K1"); !ok || r.Class != Mask { t.Errorf("K1 class = %+v, want Mask", r) } } func TestARM64Registers(t *testing.T) { tab := ForArch(ARM64) for _, reg := range []string{"R0", "R30", "SP", "ZR", "F0", "V31"} { if !tab.IsRegister(reg) { t.Errorf("arm64: %s should be a register", reg) } } if tab.IsRegister("AX") { t.Error("arm64: AX must not be a register") } } func TestAMD64Instructions(t *testing.T) { tab := ForArch(AMD64) // Every mnemonic used in the go-flac kernels must be known. used := []string{ "MOVQ", "MOVL", "MOVB", "LEAQ", "ADDQ", "SUBL", "ANDQ", "ORL", "XORL", "CMPQ", "CMPL", "TESTQ", "IMUL3L", "IMULQ", "INCW", "LZCNTL", "CMOVLGT", "SETNE", "JMP", "JGE", "JNE", "JLE", "JLT", "JGT", "JZ", "JNZ", "RET", "VPCMPEQD", "VPSLLD", "VPXOR", "VPSUBD", "VPADDD", "VPADDQ", "VMOVDQU", "VEXTRACTI128", "VPSHUFD", "VMOVQ", "VMOVMSKPS", "VZEROUPPER", "VPUNPCKLDQ", "VPUNPCKHDQ", "VPSRAD", "VPOR", "VPMOVSXDQ", "VPMULDQ", "VPCMPGTQ", "VPSRLQ", "VPANDN", "VPMOVMSKB", "VPBROADCASTD", "VPSHUFB", "VPACKSSDW", "VPERMQ", "VPERM2I128", "VPMOVZXDQ", "VFMADD231PD", "VCVTDQ2PD", "VMOVUPD", "VMULPD", "VADDPD", "VADDSD", "VMOVSD", "VMULSD", "CVTSL2SD", "VEXTRACTF128", "VPMOVSXWD", "MOVWLSX", "MOVBLZX", "MOVLQSX", // AVX-512. "VMOVDQU32", "VPXORD", "VALIGND", "VPERMD", "VPMULLD", "VPMOVDW", "VPSRAQ", "KTESTW", "KMOVW", "VPXORQ", "VPMOVQD", "VEXTRACTF64X4", "VEXTRACTI64X4", "VPBROADCASTQ", "VPMULLQ", "VPCMPEQD", } for _, m := range used { if _, ok := tab.Lookup(m); !ok { t.Errorf("amd64: instruction %s is missing from the table", m) } } } func TestOperandCounts(t *testing.T) { tab := ForArch(AMD64) if in, _ := tab.Lookup("RET"); in.MinOps != 0 || in.MaxOps != 0 { t.Errorf("RET counts = %d/%d, want 0/0", in.MinOps, in.MaxOps) } if in, _ := tab.Lookup("JMP"); in.MinOps != 1 || in.MaxOps != 1 { t.Errorf("JMP counts = %d/%d, want 1/1", in.MinOps, in.MaxOps) } if in, _ := tab.Lookup("MOVQ"); in.MinOps != 2 || in.MaxOps != 2 { t.Errorf("MOVQ counts = %d/%d, want 2/2", in.MinOps, in.MaxOps) } if in, _ := tab.Lookup("IMUL3L"); in.MinOps != 3 || in.MaxOps != 3 { t.Errorf("IMUL3L counts = %d/%d, want 3/3", in.MinOps, in.MaxOps) } } func TestPseudoRegs(t *testing.T) { for _, p := range []string{"FP", "SP", "SB", "PC"} { if !IsPseudoReg(p) { t.Errorf("%s should be a pseudo-register", p) } } if IsPseudoReg("AX") { t.Error("AX must not be a pseudo-register") } } func TestRISCVRegisters(t *testing.T) { tab := ForArch(RISCV) for _, reg := range []string{"X0", "X31", "F0", "F31", "ZERO", "RA", "SP", "A0", "S11", "T6", "FA0"} { if !tab.IsRegister(reg) { t.Errorf("riscv: %s should be a register", reg) } } if tab.IsRegister("AX") { t.Error("riscv: AX must not be a register") } } func TestLOONG64Registers(t *testing.T) { tab := ForArch(LOONG64) for _, reg := range []string{"R0", "R31", "F0", "F31", "V0", "V31", "X0", "X31"} { if !tab.IsRegister(reg) { t.Errorf("loong64: %s should be a register", reg) } } if tab.IsRegister("AX") { t.Error("loong64: AX must not be a register") } } func TestRISCVInstructions(t *testing.T) { tab := ForArch(RISCV) for _, m := range []string{"ADD", "ADDI", "SUB", "MUL", "DIV", "BEQ", "BNE", "JAL", "JALR", "LW", "SW", "FADDD", "AMOSWAPD"} { if _, ok := tab.Lookup(m); !ok { t.Errorf("riscv: instruction %s is missing", m) } } } func TestLOONG64Instructions(t *testing.T) { tab := ForArch(LOONG64) for _, m := range []string{"ADD", "ADDD", "SUBD", "MULD", "BEQ", "BNE", "BGE", "BGEZ", "JIRL", "MOVD", "MOVW"} { if _, ok := tab.Lookup(m); !ok { t.Errorf("loong64: instruction %s is missing", m) } } } // TestGeneratedTableSize sanity-checks that the toolchain-derived tables are // the full instruction sets, not a partial hand-written subset. func TestGeneratedTableSize(t *testing.T) { min := map[Arch]int{AMD64: 1000, ARM64: 400, RISCV: 800, LOONG64: 600} for a, want := range min { if n := len(ForArch(a).Instructions()); n < want { t.Errorf("%s: only %d instructions, want >= %d (generation incomplete?)", a, n, want) } } }