blob: c7b74288c32119d59790d7f99a362df54bf08f96 [file]
// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package specgen
import (
"simd/archsimd/_gen/specgen/specexpr"
"strings"
"testing"
)
func TestNewSpecTemplate(t *testing.T) {
tests := []struct {
tmpl string
isTmpl bool
wantErr bool
}{
{
tmpl: "",
isTmpl: false,
},
{
tmpl: "Convert",
isTmpl: false,
},
{
tmpl: "Convert{{.zL}}To{{.zB}}{{.zN}}",
isTmpl: true,
},
{
tmpl: "Convert{{.zL",
wantErr: true,
},
{
tmpl: "Convert{{if}}",
wantErr: true,
},
}
for _, tc := range tests {
got, err := newSpecTemplate(tc.tmpl)
if (err != nil) != tc.wantErr {
t.Errorf("newSpecTemplate(%q) returned error: %v, wantErr: %v", tc.tmpl, err, tc.wantErr)
continue
}
if tc.wantErr {
continue
}
if got.raw != tc.tmpl {
t.Errorf("newSpecTemplate(%q) raw = %q, want %q", tc.tmpl, got.raw, tc.tmpl)
}
if (got.tmpl != nil) != tc.isTmpl {
t.Errorf("newSpecTemplate(%q) isTmpl = %v, want %v", tc.tmpl, got.tmpl != nil, tc.isTmpl)
}
}
}
func TestSpecTemplateExpand(t *testing.T) {
tmpl, err := newSpecTemplate("Convert{{.zL}}To{{.zB}}{{.zN}}")
if err != nil {
t.Fatalf("unexpected error parsing template: %v", err)
}
data := map[string]any{
"zL": specexpr.Int(4),
"zB": "Float",
"zN": specexpr.Int(32),
}
got, err := tmpl.expand(data)
if err != nil {
t.Fatalf("unexpected error expanding template: %v", err)
}
want := "Convert4ToFloat32"
if got != want {
t.Errorf("expected expanded string %q, got %q", want, got)
}
}
func TestSpecTemplateTitle(t *testing.T) {
tmpl, err := newSpecTemplate("ConvertTo{{.zE | title}}")
if err != nil {
t.Fatalf("unexpected error parsing template: %v", err)
}
data := map[string]any{
"zE": specexpr.Basic{Base: "float", Bits: 32},
}
got, err := tmpl.expand(data)
if err != nil {
t.Fatalf("unexpected error expanding template: %v", err)
}
want := "ConvertToFloat32"
if got != want {
t.Errorf("expected %q, got %q", want, got)
}
}
func TestSpecTemplateConditionals(t *testing.T) {
tmplStr := `Doc for {{.Name}}.
{{if lt .zL .xL}}
Upper elements of the result are zeroed.
{{end}}`
tmpl, err := newSpecTemplate(tmplStr)
if err != nil {
t.Fatalf("unexpected error parsing template: %v", err)
}
// Case 1: zL < xL
data1 := map[string]any{
"Name": "ConvertLo4ToFloat32",
"zL": specexpr.Int(4),
"xL": specexpr.Int(8),
}
got1, err := tmpl.expand(data1)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !strings.Contains(got1, "Upper elements of the result are zeroed.") {
t.Errorf("expected conditional text when zL < xL, got: %q", got1)
}
// Case 2: zL >= xL
data2 := map[string]any{
"Name": "ConvertLo8ToFloat32",
"zL": specexpr.Int(8),
"xL": specexpr.Int(8),
}
got2, err := tmpl.expand(data2)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if strings.Contains(got2, "Upper elements of the result are zeroed.") {
t.Errorf("expected no conditional text when zL == xL, got: %q", got2)
}
}
func TestSpecTemplateScalableWidthCompare(t *testing.T) {
tmpl, err := newSpecTemplate("{{if lt .zL .xL}}smaller{{else}}same-or-larger{{end}}")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// zL = VW/32, xL = VW/16 => zL < xL
zL, err := specexpr.VW().Div(specexpr.Int(32))
if err != nil {
t.Fatal(err)
}
xL, err := specexpr.VW().Div(specexpr.Int(16))
if err != nil {
t.Fatal(err)
}
data := map[string]any{
"zL": zL,
"xL": xL,
}
got, err := tmpl.expand(data)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != "smaller" {
t.Errorf("expected 'smaller', got %q", got)
}
}
func TestSpecTemplateMissingKey(t *testing.T) {
tmpl, err := newSpecTemplate("Convert{{.missing}}")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
_, err = tmpl.expand(map[string]any{})
if err == nil {
t.Errorf("expected error on missing key, got nil")
}
}
func TestCleanDocNewlines(t *testing.T) {
tests := []struct {
input string
want string
}{
{
input: "hello\n\n\n\nworld\n",
want: "hello\n\nworld\n",
},
{
input: "hello\n\n\n",
want: "hello\n",
},
{
input: "",
want: "",
},
}
for _, tc := range tests {
got := cleanDocNewlines(tc.input)
if got != tc.want {
t.Errorf("cleanDocNewlines(%q) = %q, want %q", tc.input, got, tc.want)
}
}
}
func TestReshapeDiagram(t *testing.T) {
vec := func(n, l int) specexpr.Vector {
return specexpr.Vector{Elem: specexpr.Basic{Base: "uint", Bits: specexpr.Int(n)}, Width: specexpr.Int(n * l)}
}
u8s := specexpr.Vector{Elem: specexpr.Basic{Base: "uint", Bits: 8}, Width: specexpr.VW()}
u16s := specexpr.Vector{Elem: specexpr.Basic{Base: "uint", Bits: 16}, Width: specexpr.VW()}
tests := []struct {
name string
x, z specexpr.Vector
want string
}{
{
// Basic, no elision case
name: "Uint32x4 -> Uint64x2",
x: vec(32, 4),
z: vec(64, 2),
want: "\t x[3] x[2] x[1] x[0]\n" +
"\t | 31 .. 0 | 31 .. 0 | 31 .. 0 | 31 .. 0 |\n" +
"\t | 63 .... 0 | 63 .... 0 |\n" +
"\t z[1] z[0]",
},
{
// Small to big, with elided middle
name: "Uint32x8 -> Uint64x4",
x: vec(32, 8),
z: vec(64, 4),
want: "\t x[7] x[6] ⋯ x[1] x[0]\n" +
"\t | 31 .. 0 | 31 .. 0 | ⋯ | 31 .. 0 | 31 .. 0 |\n" +
"\t | 63 .... 0 | ⋯ | 63 .... 0 |\n" +
"\t z[3] ⋯ z[0]",
},
{
// Big to small, with elided middle
name: "Uint64x4 -> Uint32x8",
x: vec(64, 4),
z: vec(32, 8),
want: "\t x[3] ⋯ x[0]\n" +
"\t | 63 .... 0 | ⋯ | 63 .... 0 |\n" +
"\t | 31 .. 0 | 31 .. 0 | ⋯ | 31 .. 0 | 31 .. 0 |\n" +
"\t z[7] z[6] ⋯ z[1] z[0]",
},
{
// Eliding elements within each big element
name: "Uint64x2 -> Uint8x16",
x: vec(64, 2),
z: vec(8, 16),
want: "\t x[1] x[0]\n" +
"\t | 63 .... 0 | 63 .... 0 |\n" +
"\t | 7 .. 0 | 7 .. 0 | ⋯ | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | ⋯ | 7 .. 0 | 7 .. 0 |\n" +
"\t z[15] z[14] ⋯ z[9] z[8] z[7] z[6] ⋯ z[1] z[0]",
},
{
// Eliding elements within each big element, and elided middle
name: "Uint8x64 -> Uint64x8",
x: vec(8, 64),
z: vec(64, 8),
want: "\t x[63] x[62] ⋯ x[57] x[56] ⋯ x[7] x[6] ⋯ x[1] x[0]\n" +
"\t | 7 .. 0 | 7 .. 0 | ⋯ | 7 .. 0 | 7 .. 0 | ⋯ | 7 .. 0 | 7 .. 0 | ⋯ | 7 .. 0 | 7 .. 0 |\n" +
"\t | 63 .... 0 | ⋯ | 63 .... 0 |\n" +
"\t z[7] ⋯ z[0]",
},
{
// Scalable
name: "Uint8s -> Uint16s (Scalable)",
x: u8s,
z: u16s,
want: "\t ⋯ x[3] x[2] x[1] x[0]\n" +
"\t ⋯ | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 |\n" +
"\t ⋯ | 15 .... 0 | 15 .... 0 |\n" +
"\t ⋯ z[1] z[0]",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := reshapeDiagram(tc.x, tc.z)
if got != tc.want {
t.Errorf("reshapeDiagram(%s, %s):\ngot:\n%s\nwant:\n%s", tc.x, tc.z, got, tc.want)
}
})
}
}