| // 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. | |
| // refgen produces a reference implementation of the SIMD API backed by the spec | |
| // implementation. | |
| package main | |
| import ( | |
| "bytes" | |
| "cmp" | |
| "flag" | |
| "fmt" | |
| "go/types" | |
| "log" | |
| "maps" | |
| "os" | |
| "slices" | |
| "strings" | |
| "simd/archsimd/_gen/gentools" | |
| "simd/archsimd/_gen/specgen" | |
| "simd/archsimd/_gen/specgen/specexpr" | |
| ) | |
| func main() { | |
| genOpts := gentools.RegisterFlags(nil) | |
| flag.Usage = func() { | |
| w := flag.CommandLine.Output() | |
| fmt.Fprintf(w, "usage: refgen [flags]\n") | |
| flag.CommandLine.PrintDefaults() | |
| } | |
| flag.Parse() | |
| if flag.NArg() != 0 { | |
| flag.Usage() | |
| os.Exit(1) | |
| } | |
| specDir := specgen.MustFindSpecDir(genOpts.GOROOT) | |
| funcs, err := specgen.Load(specDir, nil) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "%s\n", err.Error()) | |
| os.Exit(1) | |
| } | |
| var files gentools.Files | |
| defer files.FlushOrExit() | |
| src := &srcWriter{Buffer: files.NewGoFile("simd/internal/simdref/simdref.go")} | |
| fmt.Fprintf(src, `// Code generated by 'refgen'. DO NOT EDIT. | |
| package simdref | |
| import "simd/internal/spec" | |
| `) | |
| // Define all vector types | |
| vecTypeSet := make(map[specexpr.Vector]bool) | |
| for _, fn := range funcs { | |
| if fn.Recv.Type != nil { | |
| vecTypeSet[fn.Recv.Type.(specexpr.Vector)] = true | |
| } | |
| } | |
| vecTypes := slices.SortedFunc(maps.Keys(vecTypeSet), func(a, b specexpr.Vector) int { | |
| if a.Elem != b.Elem { | |
| return cmp.Compare(a.Elem.String(), b.Elem.String()) | |
| } | |
| cmp, ok := a.Width.Compare(b.Width) | |
| if ok { | |
| return cmp | |
| } | |
| _, aScale := a.Width.(specexpr.ScalableWidth) | |
| _, bScale := b.Width.(specexpr.ScalableWidth) | |
| if !aScale && bScale { | |
| return 1 | |
| } | |
| return -1 | |
| }) | |
| fmt.Fprintf(src, "type (\n") | |
| for _, vec := range vecTypes { | |
| elem := vec.Elem.String() | |
| if vec.Elem.Base == "Mask" { | |
| elem = fmt.Sprintf("spec.Mask%d", vec.Elem.Bits) | |
| } | |
| fmt.Fprintf(src, "\t%s struct { v []%s }\n", vec.String(), elem) | |
| } | |
| fmt.Fprintf(src, ")\n\n") | |
| // Define functions | |
| var args []string | |
| for _, fn := range funcs { | |
| fmt.Fprintf(src, "%s {\n", fn.Decl()) | |
| src.id = 0 | |
| specName, specSig, typeArgs := fn.SpecFunc() | |
| specInst, err := types.Instantiate(nil, specSig, typeArgs, false) | |
| if err != nil { | |
| panic(fmt.Sprintf("instantiating spec function %s: %s", specName, err)) | |
| } | |
| specParams := specInst.(*types.Signature).Params() | |
| args = args[:0] | |
| if fn.Recv.Type != nil { | |
| args = append(args, toSpec(fn.Recv.Type, specParams.At(len(args)).Type(), fn.Recv.Name, src)) | |
| } | |
| for _, in := range fn.In { | |
| args = append(args, toSpec(in.Type, specParams.At(len(args)).Type(), in.Name, src)) | |
| } | |
| call := formatCall(specName, typeArgs, args) | |
| specResults := specInst.(*types.Signature).Results() | |
| switch len(fn.Out) { | |
| case 0: | |
| fmt.Fprintf(src, "\t%s\n", call) | |
| case 1: | |
| fmt.Fprintf(src, "\treturn %s\n", fromSpec(fn.Out[0].Type, specResults.At(0).Type(), call, src)) | |
| default: | |
| var tmps []string | |
| var res []string | |
| for i := range fn.Out { | |
| tmp := fmt.Sprintf("r%d", i+1) | |
| tmps = append(tmps, tmp) | |
| res = append(res, fromSpec(fn.Out[i].Type, specResults.At(i).Type(), tmp, src)) | |
| } | |
| fmt.Fprintf(src, "\t%s := %s\n", strings.Join(tmps, ", "), call) | |
| fmt.Fprintf(src, "\treturn %s\n", strings.Join(res, ", ")) | |
| } | |
| fmt.Fprintf(src, "}\n\n") | |
| } | |
| } | |
| type srcWriter struct { | |
| *bytes.Buffer | |
| id int | |
| } | |
| func (w *srcWriter) genIdent() string { | |
| ident := fmt.Sprintf("tmp%d", w.id) | |
| w.id++ | |
| return ident | |
| } | |
| func formatCall(specName string, typeArgs []types.Type, args []string) string { | |
| var callBuf bytes.Buffer | |
| fmt.Fprintf(&callBuf, "spec.%s[", specName) | |
| for i, typeArg := range typeArgs { | |
| if i > 0 { | |
| callBuf.WriteString(", ") | |
| } | |
| types.WriteType(&callBuf, typeArg, specQualifier) | |
| } | |
| fmt.Fprintf(&callBuf, "](%s)", strings.Join(args, ", ")) | |
| return callBuf.String() | |
| } | |
| func specQualifier(pkg *types.Package) string { | |
| if pkg.Path() == "simd/internal/spec" { | |
| return "spec" | |
| } | |
| return "" | |
| } | |
| // toSpec returns an expression that converts val from the specref Go type for t | |
| // to spec type tt. It may write statements to src. | |
| func toSpec(t specexpr.Type, tt types.Type, val string, src *srcWriter) string { | |
| arrayOrSlice := func(tElem specexpr.Type, ttElem types.Type) string { | |
| if eVal := toSpec(tElem, ttElem, val, src); eVal == val { | |
| // Easy case: the values don't need to change. | |
| return val | |
| } | |
| // Hard case: we need to map each element | |
| tmp := src.genIdent() | |
| fmt.Fprintf(src, "var %s %s\n", tmp, types.TypeString(tt, specQualifier)) | |
| fmt.Fprintf(src, "for i := range %s {\n", val) | |
| eVal := fromSpec(tElem, ttElem, "("+val+")[i]", src) | |
| fmt.Fprintf(src, "\t%s[i] = %s\n", tmp, eVal) | |
| fmt.Fprintf(src, "}\n") | |
| return tmp | |
| } | |
| switch t := t.(type) { | |
| case specexpr.Vector: | |
| return val + ".v" | |
| case specexpr.Basic: | |
| switch tt := tt.(type) { | |
| case *types.Named: | |
| if tt.Obj().Name() == "UintN" { | |
| return "spec.UintN(" + val + ")" | |
| } | |
| } | |
| return val | |
| case specexpr.Slice: | |
| tt := tt.Underlying().(*types.Slice) | |
| return arrayOrSlice(t.Elem, tt.Elem()) | |
| case specexpr.Array: | |
| switch tt := tt.(type) { | |
| case *types.Named: | |
| if tt.Obj().Name() == "Array" { | |
| return toSpec(t.Elem, tt.TypeArgs().At(0), val, src) + "[:]" | |
| } | |
| } | |
| tt := tt.Underlying().(*types.Array) | |
| return arrayOrSlice(t.Elem, tt.Elem()) | |
| case specexpr.Pointer: | |
| tt := tt.(*types.Pointer) | |
| eVal := toSpec(t.Elem, tt.Elem(), val, src) | |
| tmp := src.genIdent() | |
| fmt.Fprintf(src, "var %s %s = %s\n", tmp, types.TypeString(tt.Elem(), specQualifier), eVal) | |
| return "&" + tmp | |
| } | |
| log.Fatalf("unexpected specexpr type %s (%T)", t, t) | |
| panic("not reachable") | |
| } | |
| // fromSpec returns an expression that converts val from the spec package type | |
| // tt to the specref Go type for t. It may write statements to src. | |
| func fromSpec(t specexpr.Type, tt types.Type, val string, src *srcWriter) string { | |
| arrayOrSlice := func(tElem specexpr.Type, ttElem types.Type) string { | |
| if eVal := fromSpec(tElem, ttElem, val, src); eVal == val { | |
| // Easy case: the values don't need to change. | |
| return val | |
| } | |
| // Hard case: we need to map each element | |
| tmp := src.genIdent() | |
| fmt.Fprintf(src, "var %s %s\n", tmp, t) | |
| fmt.Fprintf(src, "for i := range %s {\n", val) | |
| eVal := fromSpec(tElem, ttElem, "("+val+")[i]", src) | |
| fmt.Fprintf(src, "\t%s[i] = %s\n", tmp, eVal) | |
| fmt.Fprintf(src, "}\n") | |
| return tmp | |
| } | |
| switch t := t.(type) { | |
| case specexpr.Vector: | |
| return fmt.Sprintf("%s{%s}", t, val) | |
| case specexpr.Basic: | |
| switch tt := tt.(type) { | |
| case *types.Named: | |
| if tt.Obj().Name() == "UintN" { | |
| return fmt.Sprintf("%s(%s)", t, val) | |
| } | |
| } | |
| return val | |
| case specexpr.Slice: | |
| tt := tt.Underlying().(*types.Slice) | |
| return arrayOrSlice(t.Elem, tt.Elem()) | |
| case specexpr.Array: | |
| switch tt := tt.(type) { | |
| case *types.Named: | |
| if tt.Obj().Name() == "Array" { | |
| return fmt.Sprintf("(%s)(%s)", t, fromSpec(t.Elem, tt.TypeArgs().At(0), val, src)) | |
| } | |
| } | |
| tt := tt.Underlying().(*types.Array) | |
| return arrayOrSlice(t.Elem, tt.Elem()) | |
| case specexpr.Pointer: | |
| tt := tt.(*types.Pointer) | |
| eVal := fromSpec(t.Elem, tt.Elem(), val, src) | |
| tmp := src.genIdent() | |
| fmt.Fprintf(src, "var %s %s = %s\n", tmp, t, eVal) | |
| return "&" + tmp | |
| } | |
| log.Fatalf("unexpected specexpr type %s (%T)", t, t) | |
| panic("not reachable") | |
| } |