mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
288 lines
7.9 KiB
Go
288 lines
7.9 KiB
Go
|
|
// Copyright 2025 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.
|
||
|
|
|
||
|
|
//go:build ignore
|
||
|
|
|
||
|
|
package main
|
||
|
|
|
||
|
|
// this generates type-instantiated boilerplate code for
|
||
|
|
// slice operations and tests
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"flag"
|
||
|
|
"fmt"
|
||
|
|
"go/format"
|
||
|
|
"io"
|
||
|
|
"os"
|
||
|
|
"strings"
|
||
|
|
"text/template"
|
||
|
|
)
|
||
|
|
|
||
|
|
func oneTemplate(t *template.Template, baseType string, width, count int, out io.Writer) {
|
||
|
|
b := width * count
|
||
|
|
if b < 128 || b > 512 {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
BaseType := strings.ToUpper(baseType[:1]) + baseType[1:]
|
||
|
|
eType := fmt.Sprintf("%s%d", baseType, width)
|
||
|
|
wxc := fmt.Sprintf("%dx%d", width, count)
|
||
|
|
vType := fmt.Sprintf("%s%s", BaseType, wxc)
|
||
|
|
aOrAn := "a"
|
||
|
|
if strings.Contains("aeiou", baseType[:1]) {
|
||
|
|
aOrAn = "an"
|
||
|
|
}
|
||
|
|
t.Execute(out, struct {
|
||
|
|
Vec string
|
||
|
|
AOrAn string
|
||
|
|
Width int
|
||
|
|
Count int
|
||
|
|
WxC string
|
||
|
|
Type string
|
||
|
|
}{
|
||
|
|
Vec: vType,
|
||
|
|
AOrAn: aOrAn,
|
||
|
|
Width: width,
|
||
|
|
Count: count,
|
||
|
|
WxC: wxc,
|
||
|
|
Type: eType,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func forTemplates(t *template.Template, out io.Writer) {
|
||
|
|
vecs := []int{128, 256, 512}
|
||
|
|
ints := []int{8, 16, 32, 64}
|
||
|
|
floats := []int{32, 64}
|
||
|
|
for _, v := range vecs {
|
||
|
|
for _, w := range ints {
|
||
|
|
c := v / w
|
||
|
|
oneTemplate(t, "int", w, c, out)
|
||
|
|
oneTemplate(t, "uint", w, c, out)
|
||
|
|
}
|
||
|
|
for _, w := range floats {
|
||
|
|
c := v / w
|
||
|
|
oneTemplate(t, "float", w, c, out)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func prologue(s string, out io.Writer) {
|
||
|
|
fmt.Fprintf(out,
|
||
|
|
`// Code generated by '%s'; DO NOT EDIT.
|
||
|
|
|
||
|
|
//go:build goexperiment.simd
|
||
|
|
|
||
|
|
package simd
|
||
|
|
|
||
|
|
`, s)
|
||
|
|
}
|
||
|
|
|
||
|
|
func testPrologue(t, s string, out io.Writer) {
|
||
|
|
fmt.Fprintf(out,
|
||
|
|
`// Code generated by '%s'; DO NOT EDIT.
|
||
|
|
|
||
|
|
//go:build goexperiment.simd
|
||
|
|
|
||
|
|
// This file contains functions testing %s.
|
||
|
|
// Each function in this file is specialized for a
|
||
|
|
// particular simd type <BaseType><Width>x<Count>.
|
||
|
|
|
||
|
|
package simd_test
|
||
|
|
|
||
|
|
import (
|
||
|
|
"simd"
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
`, s, t)
|
||
|
|
}
|
||
|
|
|
||
|
|
func curryTestPrologue(t string) func(s string, out io.Writer) {
|
||
|
|
return func(s string, out io.Writer) {
|
||
|
|
testPrologue(t, s, out)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// //go:noescape
|
||
|
|
// func LoadUint8x16Slice(s []uint8) Uint8x16 {
|
||
|
|
// return LoadUint8x16((*[16]uint8)(s[:16]))
|
||
|
|
// }
|
||
|
|
|
||
|
|
// //go:noescape
|
||
|
|
// func (x Uint8x16) StoreSlice(s []uint8) {
|
||
|
|
// x.Store((*[16]uint8)(s[:16]))
|
||
|
|
// }
|
||
|
|
|
||
|
|
func templateOf(name, temp string) *template.Template {
|
||
|
|
return template.Must(template.New(name).Parse(temp))
|
||
|
|
}
|
||
|
|
|
||
|
|
var sliceTemplate = templateOf("slice", `
|
||
|
|
// Load{{.Vec}}Slice loads {{.AOrAn}} {{.Vec}} from a slice of at least {{.Count}} {{.Type}}s
|
||
|
|
func Load{{.Vec}}Slice(s []{{.Type}}) {{.Vec}} {
|
||
|
|
return Load{{.Vec}}((*[{{.Count}}]{{.Type}})(s))
|
||
|
|
}
|
||
|
|
|
||
|
|
// StoreSlice stores x into a slice of at least {{.Count}} {{.Type}}s
|
||
|
|
func (x {{.Vec}}) StoreSlice(s []{{.Type}}) {
|
||
|
|
x.Store((*[{{.Count}}]{{.Type}})(s))
|
||
|
|
}
|
||
|
|
`)
|
||
|
|
|
||
|
|
var unaryTemplate = templateOf("unary_helpers", `
|
||
|
|
// test{{.Vec}}Unary tests the simd unary method f against the expected behavior generated by want
|
||
|
|
func test{{.Vec}}Unary(t *testing.T, f func(_ simd.{{.Vec}}) simd.{{.Vec}}, want func(_ []{{.Type}}) []{{.Type}}) {
|
||
|
|
n := {{.Count}}
|
||
|
|
t.Helper()
|
||
|
|
forSlice(t, {{.Type}}s, n, func(x []{{.Type}}) bool {
|
||
|
|
t.Helper()
|
||
|
|
a := simd.Load{{.Vec}}Slice(x)
|
||
|
|
g := make([]{{.Type}}, n)
|
||
|
|
f(a).StoreSlice(g)
|
||
|
|
w := want(x)
|
||
|
|
return checkSlicesLogInput(t, g, w, func() {t.Helper(); t.Logf("x=%v", x)})
|
||
|
|
})
|
||
|
|
}
|
||
|
|
`)
|
||
|
|
|
||
|
|
var binaryTemplate = templateOf("binary_helpers", `
|
||
|
|
// test{{.Vec}}Binary tests the simd binary method f against the expected behavior generated by want
|
||
|
|
func test{{.Vec}}Binary(t *testing.T, f func(_, _ simd.{{.Vec}}) simd.{{.Vec}}, want func(_, _ []{{.Type}}) []{{.Type}}) {
|
||
|
|
n := {{.Count}}
|
||
|
|
t.Helper()
|
||
|
|
forSlicePair(t, {{.Type}}s, n, func(x, y []{{.Type}}) bool {
|
||
|
|
t.Helper()
|
||
|
|
a := simd.Load{{.Vec}}Slice(x)
|
||
|
|
b := simd.Load{{.Vec}}Slice(y)
|
||
|
|
g := make([]{{.Type}}, n)
|
||
|
|
f(a, b).StoreSlice(g)
|
||
|
|
w := want(x, y)
|
||
|
|
return checkSlicesLogInput(t, g, w, func() {t.Helper(); t.Logf("x=%v", x); t.Logf("y=%v", y); })
|
||
|
|
})
|
||
|
|
}
|
||
|
|
`)
|
||
|
|
|
||
|
|
var ternaryTemplate = templateOf("ternary_helpers", `
|
||
|
|
// test{{.Vec}}Ternary tests the simd ternary method f against the expected behavior generated by want
|
||
|
|
func test{{.Vec}}Ternary(t *testing.T, f func(_, _, _ simd.{{.Vec}}) simd.{{.Vec}}, want func(_, _, _ []{{.Type}}) []{{.Type}}) {
|
||
|
|
n := {{.Count}}
|
||
|
|
t.Helper()
|
||
|
|
forSliceTriple(t, {{.Type}}s, n, func(x, y, z []{{.Type}}) bool {
|
||
|
|
t.Helper()
|
||
|
|
a := simd.Load{{.Vec}}Slice(x)
|
||
|
|
b := simd.Load{{.Vec}}Slice(y)
|
||
|
|
c := simd.Load{{.Vec}}Slice(z)
|
||
|
|
g := make([]{{.Type}}, n)
|
||
|
|
f(a, b, c).StoreSlice(g)
|
||
|
|
w := want(x, y, z)
|
||
|
|
return checkSlicesLogInput(t, g, w, func() {t.Helper(); t.Logf("x=%v", x); t.Logf("y=%v", y); t.Logf("z=%v", z); })
|
||
|
|
})
|
||
|
|
}
|
||
|
|
`)
|
||
|
|
|
||
|
|
var compareTemplate = templateOf("compare_helpers", `
|
||
|
|
// test{{.Vec}}Compare tests the simd comparison method f against the expected behavior generated by want
|
||
|
|
func test{{.Vec}}Compare(t *testing.T, f func(_, _ simd.{{.Vec}}) simd.Mask{{.WxC}}, want func(_, _ []{{.Type}}) []int64) {
|
||
|
|
n := {{.Count}}
|
||
|
|
t.Helper()
|
||
|
|
forSlicePair(t, {{.Type}}s, n, func(x, y []{{.Type}}) bool {
|
||
|
|
t.Helper()
|
||
|
|
a := simd.Load{{.Vec}}Slice(x)
|
||
|
|
b := simd.Load{{.Vec}}Slice(y)
|
||
|
|
g := make([]int{{.Width}}, n)
|
||
|
|
f(a, b).AsInt{{.WxC}}().StoreSlice(g)
|
||
|
|
w := want(x, y)
|
||
|
|
return checkSlicesLogInput(t, s64(g), w, func() {t.Helper(); t.Logf("x=%v", x); t.Logf("y=%v", y); })
|
||
|
|
})
|
||
|
|
}
|
||
|
|
`)
|
||
|
|
|
||
|
|
// TODO this has not been tested yet.
|
||
|
|
var compareMaskedTemplate = templateOf("comparemasked_helpers", `
|
||
|
|
// test{{.Vec}}CompareMasked tests the simd masked comparison method f against the expected behavior generated by want
|
||
|
|
// The mask is applied to the output of want; anything not in the mask, is zeroed.
|
||
|
|
func test{{.Vec}}CompareMasked(t *testing.T,
|
||
|
|
f func(_, _ simd.{{.Vec}}, m simd.Mask{{.WxC}}) simd.Mask{{.WxC}},
|
||
|
|
want func(_, _ []{{.Type}}) []int64) {
|
||
|
|
n := {{.Count}}
|
||
|
|
t.Helper()
|
||
|
|
forSlicePairMasked(t, {{.Type}}s, n, func(x, y []{{.Type}}, m []bool) bool {
|
||
|
|
t.Helper()
|
||
|
|
a := simd.Load{{.Vec}}Slice(x)
|
||
|
|
b := simd.Load{{.Vec}}Slice(y)
|
||
|
|
k := simd.LoadInt{{.WxC}}Slice(toVect[int{{.Width}}](m)).AsMask{{.WxC}}()
|
||
|
|
g := make([]int{{.Width}}, n)
|
||
|
|
f(a, b, k).AsInt{{.WxC}}().StoreSlice(g)
|
||
|
|
w := want(x, y)
|
||
|
|
for i := range m {
|
||
|
|
if !m[i] {
|
||
|
|
w[i] = 0
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return checkSlicesLogInput(t, s64(g), w, func() {t.Helper(); t.Logf("x=%v", x); t.Logf("y=%v", y); t.Logf("m=%v", m); })
|
||
|
|
})
|
||
|
|
}
|
||
|
|
`)
|
||
|
|
|
||
|
|
func main() {
|
||
|
|
sl := flag.String("sl", "slice_amd64.go", "file name for slice operations")
|
||
|
|
bh := flag.String("bh", "binary_helpers_test.go", "file name for binary test helpers")
|
||
|
|
uh := flag.String("uh", "unary_helpers_test.go", "file name for unary test helpers")
|
||
|
|
th := flag.String("th", "ternary_helpers_test.go", "file name for ternary test helpers")
|
||
|
|
ch := flag.String("ch", "compare_helpers_test.go", "file name for compare test helpers")
|
||
|
|
cmh := flag.String("cmh", "comparemasked_helpers_test.go", "file name for compare-masked test helpers")
|
||
|
|
flag.Parse()
|
||
|
|
|
||
|
|
if *sl != "" {
|
||
|
|
one(*sl, prologue, sliceTemplate)
|
||
|
|
}
|
||
|
|
if *uh != "" {
|
||
|
|
one(*uh, curryTestPrologue("unary simd methods"), unaryTemplate)
|
||
|
|
}
|
||
|
|
if *bh != "" {
|
||
|
|
one(*bh, curryTestPrologue("binary simd methods"), binaryTemplate)
|
||
|
|
}
|
||
|
|
if *th != "" {
|
||
|
|
one(*th, curryTestPrologue("ternary simd methods"), ternaryTemplate)
|
||
|
|
}
|
||
|
|
if *ch != "" {
|
||
|
|
one(*ch, curryTestPrologue("simd methods that compare two operands"), compareTemplate)
|
||
|
|
}
|
||
|
|
if *cmh != "" {
|
||
|
|
one(*cmh, curryTestPrologue("simd methods that compare two operands under a mask"), compareMaskedTemplate)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func one(filename string, prologue func(s string, out io.Writer), t *template.Template) {
|
||
|
|
if filename == "" {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
ofile := os.Stdout
|
||
|
|
|
||
|
|
if filename != "-" {
|
||
|
|
var err error
|
||
|
|
ofile, err = os.Create(filename)
|
||
|
|
if err != nil {
|
||
|
|
fmt.Fprintf(os.Stderr, "Could not create the output file %s for the generated code, %v", filename, err)
|
||
|
|
os.Exit(1)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
out := new(bytes.Buffer)
|
||
|
|
|
||
|
|
prologue("go run genfiles.go", out)
|
||
|
|
forTemplates(t, out)
|
||
|
|
|
||
|
|
b, err := format.Source(out.Bytes())
|
||
|
|
if err != nil {
|
||
|
|
fmt.Fprintf(os.Stderr, "There was a problem formatting the generated code for %s, %v", filename, err)
|
||
|
|
os.Exit(1)
|
||
|
|
} else {
|
||
|
|
ofile.Write(b)
|
||
|
|
ofile.Close()
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|