162 lines
5.1 KiB
Go
162 lines
5.1 KiB
Go
// 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)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|