Files
gasm-sdk/arch/arch_test.go
T

162 lines
5.1 KiB
Go
Raw Permalink Normal View History

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (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)
}
}
}