[go/dev.simd] [dev.simd] simd/archsimd/_gen/specgen: spec to SIMD API generator

6 views
Skip to first unread message

Austin Clements (Gerrit)

unread,
Jul 9, 2026, 2:41:09 PMJul 9
to David Chase, Junyang Shao, goph...@pubsubhelper.golang.org, Cherry Mui, Austin Clements, golang-co...@googlegroups.com
Attention needed from David Chase and Junyang Shao

Austin Clements has uploaded the change for review

Austin Clements would like David Chase and Junyang Shao to review this change.

Commit message

[dev.simd] simd/archsimd/_gen/specgen: spec to SIMD API generator

This package processes the SIMD spec package to produce the SIMD API
description.

The basic mechanism is that, for each exported function in the spec
function, this iterates through every combination of type parameter
values, keeps any combinations that satisfy additional constraint
expressions, and plugs in the concrete types that worked.
Change-Id: Iaef5c67bef2734f9cd5854f5bf7c32f61b9e00ee

Change diff

diff --git a/src/simd/archsimd/_gen/go.mod b/src/simd/archsimd/_gen/go.mod
index c8c570d..75b86e5 100644
--- a/src/simd/archsimd/_gen/go.mod
+++ b/src/simd/archsimd/_gen/go.mod
@@ -4,5 +4,11 @@

require (
golang.org/x/arch v0.26.0
+ golang.org/x/tools v0.48.0
gopkg.in/yaml.v3 v3.0.1
)
+
+require (
+ golang.org/x/mod v0.38.0 // indirect
+ golang.org/x/sync v0.22.0 // indirect
+)
diff --git a/src/simd/archsimd/_gen/go.sum b/src/simd/archsimd/_gen/go.sum
index 245d4a6..3f49a97 100644
--- a/src/simd/archsimd/_gen/go.sum
+++ b/src/simd/archsimd/_gen/go.sum
@@ -1,5 +1,13 @@
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/arch v0.26.0 h1:jZ6dpec5haP/fUv1kLCbuJy6dnRrfX6iVK08lZBFpk4=
golang.org/x/arch v0.26.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
+golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
+golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
+golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
+golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
+golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE=
+golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/src/simd/archsimd/_gen/specgen/api.go b/src/simd/archsimd/_gen/specgen/api.go
new file mode 100644
index 0000000..e67e928
--- /dev/null
+++ b/src/simd/archsimd/_gen/specgen/api.go
@@ -0,0 +1,79 @@
+// 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 (
+ "_gen/specgen/specexpr"
+ "fmt"
+ "strings"
+)
+
+// Func represents a function or method in the SIMD API.
+type Func struct {
+ Name string
+
+ // Doc is the function documentation, without any leading comment markers
+ Doc string
+
+ // Recv, if non-zero, is the shape of the receiver. The name of the receiver
+ // is always "x".
+ Recv Arg
+
+ In []Arg
+ Out []Arg
+}
+
+type Arg struct {
+ Name string
+ Type specexpr.Type
+}
+
+func (f *Func) Signature() string {
+ var buf strings.Builder
+ buf.WriteString("func ")
+ argList := func(args []Arg, canShort bool) {
+ if canShort {
+ if len(args) == 0 {
+ return
+ } else if len(args) == 1 && args[0].Name == "" {
+ buf.WriteString(args[0].Type.String())
+ return
+ }
+ }
+ buf.WriteByte('(')
+ for i, arg := range args {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ if arg.Name == "" {
+ panic("empty parameter/result name")
+ }
+ fmt.Fprintf(&buf, "%s %s", arg.Name, arg.Type)
+ }
+ buf.WriteByte(')')
+ }
+ if f.Recv.Type != nil {
+ fmt.Fprintf(&buf, "(%s %s) ", f.Recv.Name, f.Recv.Type)
+ }
+ buf.WriteString(f.Name)
+ argList(f.In, false)
+ if len(f.Out) > 0 {
+ buf.WriteByte(' ')
+ argList(f.Out, true)
+ }
+ return buf.String()
+}
+
+func (f *Func) Decl() string {
+ var buf strings.Builder
+ if f.Doc != "" {
+ for line := range strings.SplitSeq(strings.TrimRight(f.Doc, "\n"), "\n") {
+ fmt.Fprintf(&buf, "// %s\n", line)
+ }
+ }
+ buf.WriteString(f.Signature())
+ buf.WriteByte('\n')
+ return buf.String()
+}
diff --git a/src/simd/archsimd/_gen/specgen/expand.go b/src/simd/archsimd/_gen/specgen/expand.go
new file mode 100644
index 0000000..0b51558
--- /dev/null
+++ b/src/simd/archsimd/_gen/specgen/expand.go
@@ -0,0 +1,149 @@
+// 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 (
+ "_gen/specgen/specexpr"
+ "fmt"
+ "go/types"
+ "regexp"
+ "strings"
+)
+
+func (sFn *specFunc) expand(ctx context, opts *LoadOptions) []*Func {
+ ctx = ctx.at(sFn.Pos)
+
+ var solver specexpr.Solver
+
+ if opts.Trace != nil {
+ fmt.Fprintf(opts.Trace, "## %s%s\n", sFn.Name, sFn.Sig)
+ solver.SetTrace(opts.Trace)
+ }
+
+ // Declare domains of type parameters
+ typeParamVars := make(map[*types.TypeParam]specexpr.Variable)
+ for _, param := range sFn.TypeParams {
+ types, err := constraintToDomain(sFn.Pkg, param.Constraint())
+ if err != nil {
+ panic(err)
+ }
+ varDef := specexpr.Variable("$" + param.String())
+ solver.Declare(varDef, types)
+ typeParamVars[param] = varDef
+ }
+ // Bind shapes of all function parameters and results of Vec type
+ b := &argBinder{ctx, sFn.Pkg, &solver, typeParamVars}
+ argVars := make(map[*types.Var]specexpr.Variable)
+ ok := true
+ for _, v := range sFn.Params {
+ sv, ok1 := b.bindArg(v.Name(), v.Type())
+ argVars[v] = sv
+ ok = ok && ok1
+ }
+ for _, v := range sFn.Results {
+ sv, ok1 := b.bindArg(v.Name(), v.Type())
+ argVars[v] = sv
+ ok = ok && ok1
+ }
+ // Add requirements to the solver
+ for _, expr := range sFn.Requirements {
+ solver.Assert(expr)
+ }
+
+ // Find solutions.
+ defer func() {
+ p := recover()
+ if p != nil {
+ var buf strings.Builder
+ solver.Fprint(&buf)
+ panic(fmt.Sprintf("%s: %s\n%s", ctx.root.fset.Position(sFn.Pos), p, buf.String()))
+ }
+ }()
+ var funcs []*Func
+ for soln, err := range solver.Solve() {
+ if err != nil {
+ ctx.errorf("%s", err)
+ continue
+ }
+
+ fn := sFn.instantiate(ctx, soln, argVars)
+ if fn == nil {
+ continue
+ }
+
+ funcs = append(funcs, fn)
+ }
+
+ if len(funcs) == 0 {
+ ctx.errorf("impossible constraints (try -f %s -trace)", sFn.Name)
+ }
+
+ return funcs
+}
+
+func (sFn *specFunc) instantiate(ctx context, b *specexpr.Bindings, argVars map[*types.Var]specexpr.Variable) *Func {
+ var f Func
+
+ // Function or method?
+ var method bool
+ if len(sFn.Params) > 0 {
+ if t, ok := sFn.Params[0].Type().(*types.Named); ok {
+ if t.Origin() == sFn.Pkg.VecType {
+ method = true
+ }
+ }
+ }
+
+ // Instantiate name
+ name := sFn.NameTmpl.expand(func(s string) string {
+ val := b.Get(specexpr.Variable(s))
+ if val == nil {
+ ctx.errorf("unknown variable %q in function name", s)
+ return ""
+ }
+ str := fmt.Sprint(val)
+ // Make sure str starts with an upper-case letter so it maintains
+ // CamelCase in the overall identifier.
+ str = strings.ToTitle(str[:1]) + str[1:]
+ return str
+ })
+ f.Name = name
+
+ // Instantiate doc
+ doc := sFn.Doc.expand(func(s string) string {
+ val := b.Get(specexpr.Variable(s))
+ if val == nil {
+ ctx.errorf("unknown variable %q in doc", s)
+ return ""
+ }
+ return fmt.Sprint(val)
+ })
+ // Replace name in doc
+ if f.Name == sFn.Name {
+ f.Doc = doc
+ } else {
+ f.Doc = regexp.MustCompile(`\b`+regexp.QuoteMeta(sFn.Name)+`\b`).ReplaceAllLiteralString(doc, f.Name)
+ }
+
+ // Instantiate parameter and result types
+ //
+ // TODO: Should the loader keep these grouped like the original source so
+ // the transformed version keeps the same grouping (modulo pulling off the
+ // receiver)?
+ for _, v := range sFn.Params {
+ t := b.Get(argVars[v]).(specexpr.Type)
+ f.In = append(f.In, Arg{v.Name(), t})
+ }
+ if method && len(f.In) > 0 {
+ f.Recv = f.In[0]
+ f.In = f.In[1:]
+ }
+ for _, v := range sFn.Results {
+ t := b.Get(argVars[v]).(specexpr.Type)
+ f.Out = append(f.Out, Arg{v.Name(), t})
+ }
+
+ return &f
+}
diff --git a/src/simd/archsimd/_gen/specgen/load.go b/src/simd/archsimd/_gen/specgen/load.go
new file mode 100644
index 0000000..be6e6ca
--- /dev/null
+++ b/src/simd/archsimd/_gen/specgen/load.go
@@ -0,0 +1,119 @@
+// 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 (
+ "_gen/specgen/specexpr"
+ "cmp"
+ "errors"
+ "fmt"
+ "go/ast"
+ "go/token"
+ "io"
+ "maps"
+ "slices"
+ "strings"
+)
+
+type LoadOptions struct {
+ // Filter, if non-nil, causes Load to process only spec functions satisfying
+ // Filter.
+ Filter func(*ast.FuncDecl) bool
+
+ // Trace, if non-nil, causes Load to log a debug trace of solver steps to
+ // Trace.
+ Trace io.Writer
+}
+
+// Load loads a Go SIMD spec from the package in directory dir. This is the main
+// entrypoint to this package.
+func Load(dir string, opts *LoadOptions) ([]*Func, error) {
+ if opts == nil {
+ opts = new(LoadOptions)
+ }
+
+ var root contextRoot
+ ctx := context{root: &root}
+
+ pkg := loadSpecPackage(ctx, dir, opts)
+ if err := root.gatherErrors(); err != nil {
+ return nil, err
+ }
+
+ var allFuncs []*Func
+ type funcKey struct {
+ recv specexpr.Type
+ name string
+ }
+ funcSet := make(map[funcKey]*Func)
+ for _, sFn := range pkg.Funcs {
+ expanded := sFn.expand(ctx, opts)
+
+ // Check for duplicates
+ for _, fn := range expanded {
+ key := funcKey{fn.Recv.Type, fn.Name}
+ if ofn := funcSet[key]; ofn != nil {
+ ctx.at(sFn.Pos).errorf("conflicting functions:\n\t%s\n\t%s", fn.Signature(), ofn.Signature())
+ continue
+ }
+ funcSet[key] = fn
+ allFuncs = append(allFuncs, fn)
+ }
+ }
+
+ return allFuncs, root.gatherErrors()
+}
+
+type contextRoot struct {
+ fset token.FileSet
+ errors map[srcError]struct{}
+}
+
+type context struct {
+ root *contextRoot
+ pos token.Pos
+ fn string
+}
+
+func (c context) at(pos token.Pos) context {
+ c.pos = pos
+ return c
+}
+
+func (c context) errorf(msg string, args ...any) {
+ if c.root.errors == nil {
+ c.root.errors = make(map[srcError]struct{})
+ }
+ err := srcError{c.pos, c.fn, fmt.Sprintf(msg, args...)}
+ c.root.errors[err] = struct{}{}
+}
+
+func (r *contextRoot) gatherErrors() error {
+ if len(r.errors) == 0 {
+ return nil
+ }
+ var errs []error
+ var buf strings.Builder
+ for _, err := range slices.SortedFunc(maps.Keys(r.errors), func(a, b srcError) int {
+ return cmp.Or(cmp.Compare(a.pos, b.pos), cmp.Compare(a.msg, b.msg))
+ }) {
+ if err.pos.IsValid() {
+ fmt.Fprintf(&buf, "%s: ", r.fset.Position(err.pos))
+ }
+ buf.WriteString(err.msg)
+ if err.fn != "" {
+ fmt.Fprintf(&buf, " in %s", err.fn)
+ }
+ errs = append(errs, fmt.Errorf("%s", buf.String()))
+ buf.Reset()
+ }
+ return errors.Join(errs...)
+}
+
+type srcError struct {
+ pos token.Pos
+ fn string
+ msg string
+}
diff --git a/src/simd/archsimd/_gen/specgen/loadspec.go b/src/simd/archsimd/_gen/specgen/loadspec.go
new file mode 100644
index 0000000..fa76b5b
--- /dev/null
+++ b/src/simd/archsimd/_gen/specgen/loadspec.go
@@ -0,0 +1,262 @@
+// 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 (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+ "strings"
+
+ "golang.org/x/tools/go/packages"
+
+ "_gen/specgen/specexpr"
+)
+
+// specPackage represents the parsed _gen/spec package.
+type specPackage struct {
+ Fset *token.FileSet
+ Pkg *types.Package
+ TypesInfo *types.Info
+ Funcs []*specFunc
+
+ ElemTypes map[types.Type]specexpr.Basic
+ WidthTypes map[types.Type]specexpr.Width
+
+ VecType types.Type // Uninstantiated Vec type
+ ArrayType types.Type // Uninstantiated Array type
+ UintLType types.Type // Uninstantiated UintL type
+}
+
+// specFunc represents an exported function in the spec source.
+type specFunc struct {
+ Pkg *specPackage
+ Name string // Source name
+ NameTmpl specTemplate // API name template from `//specgen:name` directive, or same as Name.
+ Pos token.Pos
+ Doc specTemplate
+ Sig *types.Signature
+ TypeParams []*types.TypeParam
+ Params []*types.Var
+ Results []*types.Var
+ Requirements []specexpr.Expr
+}
+
+// specTemplate is a template string, with placeholders of the form `{var}`,
+// which will be replaced with variable values from the solver.
+type specTemplate struct {
+ tmpl string // raw template string including patterns
+ fields [][2]int // start:end ranges of fields, including '{}'s, in ascending order
+}
+
+// loadSpecPackage parses the spec package in the given directory path.
+func loadSpecPackage(ctx context, dir string, opts *LoadOptions) *specPackage {
+ cfg := &packages.Config{
+ Mode: packages.LoadSyntax,
+ Dir: dir,
+ Fset: &ctx.root.fset,
+ }
+
+ pkgs, err := packages.Load(cfg, ".")
+ if err != nil {
+ ctx.errorf("failed to load package: %s", err)
+ return nil
+ }
+ if len(pkgs) == 0 {
+ ctx.errorf("no package found in directory %s", dir)
+ return nil
+ }
+ if len(pkgs[0].Errors) > 0 {
+ for _, err := range pkgs[0].Errors {
+ ctx.errorf("%s", err)
+ }
+ return nil
+ }
+
+ srcPkg := pkgs[0]
+ fset := srcPkg.Fset
+ info := srcPkg.TypesInfo
+
+ var pkg specPackage
+
+ // Gather exported functions
+ var funcs []*specFunc
+ for _, file := range srcPkg.Syntax {
+ for _, decl := range file.Decls {
+ d, ok := decl.(*ast.FuncDecl)
+ if !ok || !d.Name.IsExported() {
+ continue
+ }
+ if opts.Filter != nil && !opts.Filter(d) {
+ continue
+ }
+
+ obj := srcPkg.Types.Scope().Lookup(d.Name.Name)
+ if obj == nil {
+ continue
+ }
+ fn, ok := obj.(*types.Func)
+ if !ok {
+ continue
+ }
+
+ sig := fn.Type().(*types.Signature)
+
+ var typeParams []*types.TypeParam
+ tparams := sig.TypeParams()
+ for tparam := range tparams.TypeParams() {
+ typeParams = append(typeParams, tparam)
+ }
+
+ var params []*types.Var
+ p := sig.Params()
+ for v := range p.Variables() {
+ params = append(params, v)
+ }
+
+ var results []*types.Var
+ r := sig.Results()
+ for v := range r.Variables() {
+ results = append(results, v)
+ }
+
+ f := &specFunc{
+ Pkg: &pkg,
+ Name: d.Name.Name,
+ Pos: decl.Pos(),
+ Sig: sig,
+ TypeParams: typeParams,
+ Params: params,
+ Results: results,
+ }
+ f.NameTmpl = specTemplate{tmpl: f.Name}
+ if d.Doc != nil {
+ f.Doc, err = newSpecTemplate(d.Doc.Text())
+ if err != nil {
+ ctx.at(d.Doc.Pos()).errorf("malformed doc comment: %s", err)
+ }
+ for _, comment := range d.Doc.List {
+ if dir, ok := ast.ParseDirective(comment.Slash, comment.Text); ok && dir.Tool == "specgen" {
+ switch dir.Name {
+ case "name":
+ f.NameTmpl, err = newSpecTemplate(dir.Args)
+ if err != nil {
+ ctx.at(dir.Pos()).errorf("malformed //specgen:name directive: %s", err)
+ }
+ case "require":
+ args, err := dir.ParseArgs()
+ if err != nil {
+ ctx.at(dir.Pos()).errorf("malformed //specgen:require directive: %s", err)
+ break
+ }
+ for _, arg := range args {
+ expr, err := specexpr.ParseExpr(arg.Arg)
+ if err != nil {
+ ctx.at(arg.Pos).errorf("failed to parse require argument %q: %s", arg.Arg, err)
+ continue
+ }
+ f.Requirements = append(f.Requirements, expr)
+ }
+ }
+ }
+ }
+ }
+
+ funcs = append(funcs, f)
+ }
+ }
+
+ lookupType := func(name string) types.Type {
+ obj := srcPkg.Types.Scope().Lookup(name)
+ if obj == nil {
+ ctx.errorf("type %q missing from package %s", name, srcPkg.PkgPath)
+ return nil
+ }
+ tn, ok := obj.(*types.TypeName)
+ if !ok {
+ ctx.at(obj.Pos()).errorf("%s expected to be a type", obj.String())
+ return nil
+ }
+ return tn.Type()
+ }
+
+ // Gather types corresponding to shape constraints
+ elemTypes := make(map[types.Type]specexpr.Basic)
+ if eltOrMask := lookupType("EltOrMask"); eltOrMask != nil {
+ for _, elt := range typeSet(eltOrMask) {
+ elemTypes[elt] = shapeElemType(elt)
+ }
+ }
+ widthTypes := make(map[types.Type]specexpr.Width)
+ if width := lookupType("Width"); width != nil {
+ for _, width := range typeSet(width) {
+ val := shapeWidthVal(width)
+ widthTypes[width] = val
+ }
+ }
+
+ // Gather other known types
+ vecType := lookupType("Vec")
+ arrayType := lookupType("Array")
+ uintLType := lookupType("UintL")
+
+ pkg = specPackage{
+ Fset: fset,
+ Pkg: srcPkg.Types,
+ TypesInfo: info,
+ Funcs: funcs,
+ ElemTypes: elemTypes,
+ WidthTypes: widthTypes,
+ VecType: vecType,
+ ArrayType: arrayType,
+ UintLType: uintLType,
+ }
+ return &pkg
+}
+
+// newSpecTemplate parses spec template.
+func newSpecTemplate(tmpl string) (specTemplate, error) {
+ if !strings.ContainsAny(tmpl, "{}") {
+ return specTemplate{tmpl, nil}, nil
+ }
+
+ var fields [][2]int
+ for i := 0; i < len(tmpl); i++ {
+ switch tmpl[i] {
+ case '{':
+ j := i + strings.IndexByte(tmpl[i:], '}') + 1
+ if j <= i {
+ return specTemplate{}, fmt.Errorf("unclosed '{' in template %q", tmpl)
+ }
+ fields = append(fields, [2]int{i, j})
+ i = j - 1
+ case '}':
+ return specTemplate{}, fmt.Errorf("unmatched '}' in template %q", tmpl)
+ }
+ }
+ return specTemplate{
+ tmpl: tmpl,
+ fields: fields,
+ }, nil
+}
+
+// expand replaces placeholders in template s by calling the lookup function to
+// resolve their values.
+func (s *specTemplate) expand(lookup func(string) string) string {
+ if len(s.fields) == 0 {
+ return s.tmpl
+ }
+ var buf strings.Builder
+ pos := 0
+ for _, field := range s.fields {
+ buf.WriteString(s.tmpl[pos:field[0]])
+ val := lookup(s.tmpl[field[0]+1 : field[1]-1])
+ buf.WriteString(val)
+ pos = field[1]
+ }
+ buf.WriteString(s.tmpl[pos:])
+ return buf.String()
+}
diff --git a/src/simd/archsimd/_gen/specgen/loadspec_test.go b/src/simd/archsimd/_gen/specgen/loadspec_test.go
new file mode 100644
index 0000000..c1021f6
--- /dev/null
+++ b/src/simd/archsimd/_gen/specgen/loadspec_test.go
@@ -0,0 +1,196 @@
+// 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 (
+ "strings"
+ "sync"
+ "testing"
+)
+
+var (
+ specPkg *specPackage
+ specLoadErr error
+ loadSpecOnce sync.Once
+)
+
+func loadSpec(t *testing.T) *specPackage {
+ loadSpecOnce.Do(func() {
+ var root contextRoot
+ ctx := context{root: &root}
+
+ specPkg = loadSpecPackage(ctx, "../spec", &LoadOptions{})
+ if err := root.gatherErrors(); err != nil {
+ specLoadErr = err
+ }
+ })
+ if specLoadErr != nil {
+ t.Fatalf("failed to load spec: %v", specLoadErr)
+ }
+ return specPkg
+}
+
+func TestLoadSpec(t *testing.T) {
+ pkg := loadSpec(t)
+
+ // Verify we parsed functions
+ if len(pkg.Funcs) == 0 {
+ t.Errorf("expected parsed functions, got 0")
+ }
+
+ var foundAdd, foundExtend bool
+ for _, f := range pkg.Funcs {
+ if f.Name == "Add" {
+ foundAdd = true
+ if len(f.TypeParams) != 2 {
+ t.Errorf("Add should have 2 type parameters, got %d", len(f.TypeParams))
+ } else {
+ if f.TypeParams[0].Obj().Name() != "E" || !strings.Contains(f.TypeParams[0].Constraint().String(), "Nums") {
+ t.Errorf("unexpected Add type param 0: %s %s", f.TypeParams[0].Obj().Name(), f.TypeParams[0].Constraint())
+ }
+ if f.TypeParams[1].Obj().Name() != "W" || !strings.Contains(f.TypeParams[1].Constraint().String(), "Width") {
+ t.Errorf("unexpected Add type param 1: %s %s", f.TypeParams[1].Obj().Name(), f.TypeParams[1].Constraint())
+ }
+ }
+ if len(f.Params) != 2 {
+ t.Errorf("Add should have 2 params, got %d", len(f.Params))
+ } else {
+ if f.Params[0].Name() != "x" || !strings.Contains(f.Params[0].Type().String(), "Vec[") {
+ t.Errorf("unexpected Add param 0: %s %s", f.Params[0].Name(), f.Params[0].Type())
+ }
+ if f.Params[1].Name() != "y" || !strings.Contains(f.Params[1].Type().String(), "Vec[") {
+ t.Errorf("unexpected Add param 1: %s %s", f.Params[1].Name(), f.Params[1].Type())
+ }
+ }
+ if len(f.Results) != 1 {
+ t.Errorf("Add should have 1 result, got %d", len(f.Results))
+ } else {
+ if !strings.Contains(f.Results[0].Type().String(), "Vec[") {
+ t.Errorf("unexpected Add result type: %s", f.Results[0].Type())
+ }
+ }
+ }
+
+ if f.Name == "ExtendLoLToZ" {
+ foundExtend = true
+ if len(f.Requirements) != 2 {
+ t.Errorf("expected 2 requirements for ExtendLoLToZ, got %d", len(f.Requirements))
+ } else {
+ if f.Requirements[0] == nil || f.Requirements[1] == nil {
+ t.Errorf("expected non-nil parsed requirements")
+ }
+ }
+ }
+ }
+
+ if !foundAdd {
+ t.Errorf("failed to find function Add in parsed package")
+ }
+ if !foundExtend {
+ t.Errorf("failed to find function ExtendLoLToZ in parsed package")
+ }
+}
+
+func TestLoadSpecNameTmpl(t *testing.T) {
+ pkg := loadSpec(t)
+ var found bool
+ for _, f := range pkg.Funcs {
+ if f.Name == "MaskFromBits" {
+ found = true
+ want := "{z}FromBits"
+ if f.NameTmpl.tmpl != want {
+ t.Errorf("MaskFromBits: expected NameTmpl.tmpl %q, got %q", want, f.NameTmpl.tmpl)
+ }
+ break
+ }
+ }
+ if !found {
+ t.Errorf("failed to find function MaskFromBits in parsed package")
+ }
+}
+
+func TestNewSpecTemplate(t *testing.T) {
+ tests := []struct {
+ tmpl string
+ want specTemplate
+ wantErr bool
+ }{
+ {
+ tmpl: "",
+ want: specTemplate{tmpl: "", fields: nil},
+ },
+ {
+ tmpl: "Convert",
+ want: specTemplate{tmpl: "Convert", fields: nil},
+ },
+ {
+ tmpl: "Convert{zL}To{zB}{zN}",
+ want: specTemplate{
+ tmpl: "Convert{zL}To{zB}{zN}",
+ fields: [][2]int{{7, 11}, {13, 17}, {17, 21}},
+ },
+ },
+ {
+ tmpl: "Convert{zL",
+ wantErr: true,
+ },
+ {
+ tmpl: "Convert}",
+ wantErr: true,
+ },
+ {
+ tmpl: "Convert{a{b}}",
+ 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.tmpl != tc.want.tmpl {
+ t.Errorf("newSpecTemplate(%q) tmpl = %q, want %q", tc.tmpl, got.tmpl, tc.want.tmpl)
+ }
+ if len(got.fields) != len(tc.want.fields) {
+ t.Errorf("newSpecTemplate(%q) fields len = %d, want %d", tc.tmpl, len(got.fields), len(tc.want.fields))
+ } else {
+ for i := range got.fields {
+ if got.fields[i] != tc.want.fields[i] {
+ t.Errorf("newSpecTemplate(%q) fields[%d] = %v, want %v", tc.tmpl, i, got.fields[i], tc.want.fields[i])
+ }
+ }
+ }
+ }
+}
+
+func TestSpecTemplateExpand(t *testing.T) {
+ tmpl, err := newSpecTemplate("Convert{zL}To{zB}{zN}")
+ if err != nil {
+ t.Fatalf("unexpected error parsing template: %v", err)
+ }
+
+ lookup := func(name string) string {
+ switch name {
+ case "zL":
+ return "4"
+ case "zB":
+ return "Float"
+ case "zN":
+ return "32"
+ }
+ return ""
+ }
+
+ got := tmpl.expand(lookup)
+ want := "Convert4ToFloat32"
+ if got != want {
+ t.Errorf("expected expanded string %q, got %q", want, got)
+ }
+}
diff --git a/src/simd/archsimd/_gen/specgen/types.go b/src/simd/archsimd/_gen/specgen/types.go
new file mode 100644
index 0000000..4930fc5
--- /dev/null
+++ b/src/simd/archsimd/_gen/specgen/types.go
@@ -0,0 +1,289 @@
+// 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 (
+ "fmt"
+ "go/types"
+ "regexp"
+ "strconv"
+ "strings"
+ "sync"
+
+ "_gen/specgen/specexpr"
+
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// constraintToDomain enumerates all types explicitly listed as satisfying
+// constraint (which must be a type parameter constraint), and translates them
+// to a [specexpr] domain.
+func constraintToDomain(pkg *specPackage, constraint types.Type) ([]any, error) {
+ var vals []any
+ for _, typ := range typeSet(constraint) {
+ elem, ok := pkg.ElemTypes[typ]
+ if ok {
+ vals = append(vals, elem)
+ } else if width, ok := pkg.WidthTypes[typ]; ok {
+ vals = append(vals, width)
+ } else {
+ return nil, fmt.Errorf("type %s satisfies constraint %s, but isn't a known shape type", typ, constraint)
+ }
+ }
+ return vals, nil
+}
+
+var typeSetMemo sync.Map
+
+// typeSet enumerates all concrete types in t's type set.
+func typeSet(t types.Type) []types.Type {
+ // In general types.Types are not comparable, but every type we're dealing
+ // with here has pointer identity, and also there's no correctness issue if
+ // we miss in the memo.
+ ts, ok := typeSetMemo.Load(t)
+ if !ok {
+ var o orderedTypeSet
+ typeSet1(t, &o)
+ ts, _ = typeSetMemo.LoadOrStore(t, o.types)
+ }
+ return ts.([]types.Type)
+}
+
+type orderedTypeSet struct {
+ types []types.Type
+ set typeutil.Map
+}
+
+func (set *orderedTypeSet) add(t types.Type) {
+ if set.set.At(t) == nil {
+ set.set.Set(t, true)
+ set.types = append(set.types, t)
+ }
+}
+
+func (set *orderedTypeSet) intersect(o orderedTypeSet) {
+ i, j := 0, 0
+ for ; i < len(set.types); i++ {
+ t := set.types[i]
+ if o.set.At(t) != nil {
+ set.types[j] = t
+ j++
+ } else {
+ set.set.Delete(t)
+ }
+ }
+ set.types = set.types[:j]
+}
+
+func typeSet1(t types.Type, o *orderedTypeSet) {
+ switch u := t.Underlying().(type) {
+ case *types.Interface:
+ switch u.NumEmbeddeds() {
+ case 0:
+ return
+ case 1:
+ // Fast path for common case
+ typeSet1(u.EmbeddedType(0), o)
+ return
+ }
+ var intersection orderedTypeSet
+ first := true
+ for etyp := range u.EmbeddedTypes() {
+ if first {
+ typeSet1(etyp, &intersection)
+ first = false
+ } else {
+ var tmp orderedTypeSet
+ typeSet1(etyp, &tmp)
+ intersection.intersect(tmp)
+ }
+ }
+ // TODO: This doesn't check method satisfaction. We could do that with
+ // types.Satisfies filter here, but it doesn't matter for our needs.
+ for _, etyp := range intersection.types {
+ o.add(etyp)
+ }
+
+ case *types.Union:
+ for term := range u.Terms() {
+ typeSet1(term.Type(), o)
+ }
+
+ default:
+ o.add(t)
+ }
+}
+
+var basicRe = regexp.MustCompile(`^([a-z]+)([0-9]*)$`)
+
+// shapeElemType parses a basic or mask element spec type.
+func shapeElemType(t types.Type) specexpr.Basic {
+ var name string
+ switch t := t.(type) {
+ case *types.Basic: // E.g., uint32
+ name = t.Name()
+ case *types.Named: // E.g., mask16
+ name = t.Obj().Name()
+ default:
+ panic(fmt.Sprintf("not a shape element type: %s", t))
+ }
+ m := basicRe.FindStringSubmatch(name)
+ if m == nil {
+ panic(fmt.Sprintf("failed to parse element type %s", name))
+ }
+ bits := 0
+ if m[2] != "" {
+ bits, _ = strconv.Atoi(m[2])
+ }
+ return specexpr.Basic{Base: m[1], Bits: bits}
+}
+
+// shapeWidthVal parses a spec width type.
+func shapeWidthVal(t types.Type) specexpr.Width {
+ named, ok := t.(*types.Named)
+ if !ok {
+ panic(fmt.Sprintf("not a shape width type: %s", t))
+ }
+ name := named.Obj().Name()
+ if name == "WidthScalable" {
+ return specexpr.UnitWidth()
+ }
+ var err error
+ if suffix, ok := strings.CutPrefix(name, "Width"); ok {
+ var val int
+ val, err = strconv.Atoi(suffix)
+ if err == nil {
+ return specexpr.FixedWidth(val)
+ }
+ } else {
+ err = fmt.Errorf("does not start with 'Width'")
+ }
+ panic(fmt.Sprintf("parsing width type %s: %s", t, err))
+}
+
+type argBinder struct {
+ ctx context
+ pkg *specPackage
+ s *specexpr.Solver
+ typeParams map[*types.TypeParam]specexpr.Variable
+}
+
+// bindArg assigns all solver variables related to an argument called "name" of
+// type t. It returns the bound variable and an ok bool.
+func (b *argBinder) bindArg(name string, t types.Type) (specexpr.Variable, bool) {
+ expr := b.bind1(name, t)
+ if expr == nil {
+ return specexpr.Variable(""), false
+ }
+ return b.s.Assign(specexpr.Variable(name), expr), true
+}
+
+// bind1 deconstructs t and binds any components to variables derived from
+// "name", and returns the (not yet bound!) expression for t. The caller is
+// expected to bind "name" to the returned expression, or pass it up. It works
+// this way so we can unwrap things like pointer and slice types without
+// creating intermediate names for each level.
+func (b *argBinder) bind1(name string, t types.Type) specexpr.Expr {
+ switch t := t.(type) {
+ case *types.Basic:
+ return &specexpr.Literal{Val: shapeElemType(t)}
+
+ case *types.Pointer:
+ elem := b.bind1(name, t.Elem())
+ if elem == nil {
+ return nil
+ }
+ return specexpr.MakePointer(elem)
+
+ case *types.Slice:
+ elem := b.bind1(name, t.Elem())
+ if elem == nil {
+ return nil
+ }
+ return specexpr.MakeSlice(elem)
+
+ case *types.Named:
+ if types.Identical(t.Origin(), b.pkg.VecType) {
+ xE, xW, _ := b.bindVecLike(name, t)
+ if xE == nil {
+ return nil
+ }
+ return specexpr.MakeVector(xE, xW)
+ }
+ if types.Identical(t.Origin(), b.pkg.ArrayType) {
+ xE, xW, xL := b.bindVecLike(name, t)
+ if xE == nil {
+ return nil
+ }
+ b.s.Assert(&specexpr.BinExpr{Op: specexpr.OpNotEqual, X: xW, Y: &specexpr.Literal{Val: specexpr.UnitWidth()}})
+ return specexpr.MakeArray(xE, widthToInt(xL))
+ }
+ if types.Identical(t.Origin(), b.pkg.UintLType) {
+ xE, xW, xL := b.bindVecLike(name, t)
+ if xE == nil {
+ return nil
+ }
+ b.s.Assert(&specexpr.BinExpr{Op: specexpr.OpNotEqual, X: xW, Y: &specexpr.Literal{Val: specexpr.UnitWidth()}})
+ return specexpr.MakeBasic(&specexpr.Literal{Val: "uint"}, widthToInt(xL))
+ }
+
+ case *types.TypeParam:
+ return b.typeParams[t]
+ }
+
+ b.ctx.errorf("cannot convert spec type %s into API type", t)
+ return nil
+}
+
+var widthToInt = specexpr.MakeFunc1("widthToInt", func(w specexpr.Width) (any, error) {
+ if w, ok := w.(specexpr.FixedWidth); ok {
+ return int(w), nil
+ }
+ return nil, fmt.Errorf("cannot convert scalable width %s to int", w)
+})
+
+func (b *argBinder) bindVecLike(name string, t *types.Named) (xE, xW, xL specexpr.Expr) {
+ args := t.TypeArgs()
+ if args.Len() != 2 {
+ b.ctx.errorf("expected exactly 2 type arguments, got %d", args.Len())
+ return nil, nil, nil
+ }
+
+ // Assign the element type
+ elem := b.bind1(name+"E", args.At(0))
+ if elem == nil {
+ return nil, nil, nil
+ }
+ xE = b.s.Assign(specexpr.Variable(name+"E"), elem)
+
+ // Get the width
+ var wExpr specexpr.Expr
+ switch wt := args.At(1).(type) {
+ case *types.TypeParam:
+ wExpr = b.typeParams[wt]
+ case *types.Named:
+ wVal := b.pkg.WidthTypes[wt]
+ if wVal == nil {
+ b.ctx.errorf("width type argument is not a width")
+ return nil, nil, nil
+ }
+ wExpr = &specexpr.Literal{Val: wVal}
+ default:
+ b.ctx.errorf("width type arguments not a type parameter or named type")
+ return nil, nil, nil
+ }
+ xW = b.s.Assign(specexpr.Variable(name+"W"), wExpr)
+
+ // Bind other variables
+ basicBase := specexpr.MakeField[specexpr.Basic]("Base")
+ basicBits := specexpr.MakeField[specexpr.Basic]("Bits")
+ b.s.Assign(specexpr.Variable(name+"B"), basicBase.Apply(xE))
+ xN := b.s.Assign(specexpr.Variable(name+"N"), basicBits.Apply(xE))
+ xL = b.s.Assign(specexpr.Variable(name+"L"), &specexpr.BinExpr{
+ Op: specexpr.OpDiv, X: xW, Y: xN,
+ })
+
+ return xE, xW, xL
+}
diff --git a/src/simd/archsimd/_gen/specgen/types_test.go b/src/simd/archsimd/_gen/specgen/types_test.go
new file mode 100644
index 0000000..5dd2f34
--- /dev/null
+++ b/src/simd/archsimd/_gen/specgen/types_test.go
@@ -0,0 +1,47 @@
+// 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 (
+ "go/types"
+ "testing"
+)
+
+func TestTypeSet(t *testing.T) {
+ pkg := loadSpec(t)
+
+ obj := pkg.Pkg.Scope().Lookup("Nums")
+ if obj == nil {
+ t.Fatalf("failed to find 'Nums' in spec package scope")
+ }
+
+ typeName, ok := obj.(*types.TypeName)
+ if !ok {
+ t.Fatalf("Nums is not a TypeName, got %T", obj)
+ }
+
+ typesList := typeSet(typeName.Type())
+
+ expected := []string{
+ "float32", "float64",
+ "int8", "int16", "int32", "int64",
+ "uint8", "uint16", "uint32", "uint64",
+ }
+
+ if len(typesList) != len(expected) {
+ t.Errorf("expected %d types, got %d", len(expected), len(typesList))
+ }
+
+ found := make(map[string]bool)
+ for _, ty := range typesList {
+ found[ty.String()] = true
+ }
+
+ for _, exp := range expected {
+ if !found[exp] {
+ t.Errorf("expected type %s not found in satisfying list", exp)
+ }
+ }
+}

Change information

Files:
  • M src/simd/archsimd/_gen/go.mod
  • M src/simd/archsimd/_gen/go.sum
  • A src/simd/archsimd/_gen/specgen/api.go
  • A src/simd/archsimd/_gen/specgen/expand.go
  • A src/simd/archsimd/_gen/specgen/load.go
  • A src/simd/archsimd/_gen/specgen/loadspec.go
  • A src/simd/archsimd/_gen/specgen/loadspec_test.go
  • A src/simd/archsimd/_gen/specgen/types.go
  • A src/simd/archsimd/_gen/specgen/types_test.go
Change size: XL
Delta: 9 files changed, 1155 insertions(+), 0 deletions(-)
Open in Gerrit

Related details

Attention is currently required from:
  • David Chase
  • Junyang Shao
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: go
Gerrit-Branch: dev.simd
Gerrit-Change-Id: Iaef5c67bef2734f9cd5854f5bf7c32f61b9e00ee
Gerrit-Change-Number: 798846
Gerrit-PatchSet: 1
Gerrit-Owner: Austin Clements <aus...@google.com>
Gerrit-Reviewer: Austin Clements <aus...@google.com>
Gerrit-Reviewer: David Chase <drc...@google.com>
Gerrit-Reviewer: Junyang Shao <shaoj...@google.com>
Gerrit-CC: Cherry Mui <cher...@google.com>
Gerrit-Attention: David Chase <drc...@google.com>
Gerrit-Attention: Junyang Shao <shaoj...@google.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Austin Clements (Gerrit)

unread,
Jul 10, 2026, 8:39:47 AMJul 10
to Austin Clements, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
Attention needed from Austin Clements, David Chase and Junyang Shao

Austin Clements uploaded new patchset

Austin Clements uploaded patch set #2 to this change.
Following approvals got outdated and were removed:
Open in Gerrit

Related details

Attention is currently required from:
  • Austin Clements
  • David Chase
  • Junyang Shao
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newpatchset
Gerrit-Project: go
Gerrit-Branch: dev.simd
Gerrit-Change-Id: Iaef5c67bef2734f9cd5854f5bf7c32f61b9e00ee
Gerrit-Change-Number: 798846
Gerrit-PatchSet: 2
Gerrit-Owner: Austin Clements <aus...@google.com>
Gerrit-Reviewer: Austin Clements <aus...@google.com>
Gerrit-Reviewer: David Chase <drc...@google.com>
Gerrit-Reviewer: Junyang Shao <shaoj...@google.com>
Gerrit-CC: Cherry Mui <cher...@google.com>
Gerrit-Attention: David Chase <drc...@google.com>
Gerrit-Attention: Junyang Shao <shaoj...@google.com>
Gerrit-Attention: Austin Clements <aus...@google.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Austin Clements (Gerrit)

unread,
Jul 10, 2026, 2:38:05 PMJul 10
to Austin Clements, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
Attention needed from Austin Clements, David Chase and Junyang Shao

Austin Clements uploaded new patchset

Austin Clements uploaded patch set #3 to this change.
Following approvals got outdated and were removed:
Open in Gerrit

Related details

Attention is currently required from:
  • Austin Clements
  • David Chase
  • Junyang Shao
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newpatchset
Gerrit-Project: go
Gerrit-Branch: dev.simd
Gerrit-Change-Id: Iaef5c67bef2734f9cd5854f5bf7c32f61b9e00ee
Gerrit-Change-Number: 798846
Gerrit-PatchSet: 3
unsatisfied_requirement
satisfied_requirement
open
diffy

Austin Clements (Gerrit)

unread,
Jul 10, 2026, 3:07:59 PMJul 10
to Austin Clements, goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, David Chase, Junyang Shao, Cherry Mui, golang-co...@googlegroups.com
Attention needed from David Chase and Junyang Shao

Austin Clements added 1 comment

Patchset-level comments
File-level comment, Patchset 3 (Latest):
Austin Clements . resolved

The longtest failure is real, but I don't understand why THIS CL triggered it. We have plenty of other imports within simd/archsimd/_gen already.

Open in Gerrit

Related details

Attention is currently required from:
  • David Chase
  • Junyang Shao
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: go
Gerrit-Branch: dev.simd
Gerrit-Change-Id: Iaef5c67bef2734f9cd5854f5bf7c32f61b9e00ee
Gerrit-Change-Number: 798846
Gerrit-PatchSet: 3
Gerrit-Owner: Austin Clements <aus...@google.com>
Gerrit-Reviewer: Austin Clements <aus...@google.com>
Gerrit-Reviewer: David Chase <drc...@google.com>
Gerrit-Reviewer: Junyang Shao <shaoj...@google.com>
Gerrit-CC: Cherry Mui <cher...@google.com>
Gerrit-Attention: David Chase <drc...@google.com>
Gerrit-Attention: Junyang Shao <shaoj...@google.com>
Gerrit-Comment-Date: Fri, 10 Jul 2026 19:07:54 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
unsatisfied_requirement
satisfied_requirement
open
diffy

Austin Clements (Gerrit)

unread,
Jul 12, 2026, 9:30:15 PMJul 12
to Austin Clements, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
Attention needed from Austin Clements, David Chase and Junyang Shao

Austin Clements uploaded new patchset

Austin Clements uploaded patch set #5 to this change.
Following approvals got outdated and were removed:
Open in Gerrit

Related details

Attention is currently required from:
  • Austin Clements
  • David Chase
  • Junyang Shao
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newpatchset
Gerrit-Project: go
Gerrit-Branch: dev.simd
Gerrit-Change-Id: Iaef5c67bef2734f9cd5854f5bf7c32f61b9e00ee
Gerrit-Change-Number: 798846
Gerrit-PatchSet: 5
Gerrit-Owner: Austin Clements <aus...@google.com>
Gerrit-Reviewer: Austin Clements <aus...@google.com>
Gerrit-Reviewer: David Chase <drc...@google.com>
Gerrit-Reviewer: Junyang Shao <shaoj...@google.com>
Gerrit-CC: Cherry Mui <cher...@google.com>
Gerrit-Attention: David Chase <drc...@google.com>
Gerrit-Attention: Junyang Shao <shaoj...@google.com>
Gerrit-Attention: Austin Clements <aus...@google.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Austin Clements (Gerrit)

unread,
Jul 12, 2026, 9:54:10 PMJul 12
to Austin Clements, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
Attention needed from Austin Clements, David Chase and Junyang Shao

Austin Clements uploaded new patchset

Austin Clements uploaded patch set #6 to this change.
Following approvals got outdated and were removed:
Open in Gerrit

Related details

Attention is currently required from:
  • Austin Clements
  • David Chase
  • Junyang Shao
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newpatchset
Gerrit-Project: go
Gerrit-Branch: dev.simd
Gerrit-Change-Id: Iaef5c67bef2734f9cd5854f5bf7c32f61b9e00ee
Gerrit-Change-Number: 798846
Gerrit-PatchSet: 6
unsatisfied_requirement
satisfied_requirement
open
diffy

Junyang Shao (Gerrit)

unread,
Jul 15, 2026, 5:22:05 PMJul 15
to Austin Clements, goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, David Chase, Cherry Mui, golang-co...@googlegroups.com
Attention needed from Austin Clements and David Chase

Junyang Shao added 1 comment

Patchset-level comments
File-level comment, Patchset 3:
Austin Clements . unresolved

The longtest failure is real, but I don't understand why THIS CL triggered it. We have plenty of other imports within simd/archsimd/_gen already.

Junyang Shao

CL 798560 modified `src/simd/archsimd/_gen/go.mod` to use `module simd/archsimd/_gen`.

Will changing the imports of
"_gen/specgen/specexpr"
to fully-qualified
"simd/archsimd/_gen/specgen/specexpr"
help?

Open in Gerrit

Related details

Attention is currently required from:
  • Austin Clements
  • David Chase
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: dev.simd
    Gerrit-Change-Id: Iaef5c67bef2734f9cd5854f5bf7c32f61b9e00ee
    Gerrit-Change-Number: 798846
    Gerrit-PatchSet: 6
    Gerrit-Owner: Austin Clements <aus...@google.com>
    Gerrit-Reviewer: Austin Clements <aus...@google.com>
    Gerrit-Reviewer: David Chase <drc...@google.com>
    Gerrit-Reviewer: Junyang Shao <shaoj...@google.com>
    Gerrit-CC: Cherry Mui <cher...@google.com>
    Gerrit-Attention: David Chase <drc...@google.com>
    Gerrit-Attention: Austin Clements <aus...@google.com>
    Gerrit-Comment-Date: Wed, 15 Jul 2026 21:21:59 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Austin Clements <aus...@google.com>
    unsatisfied_requirement
    open
    diffy

    Junyang Shao (Gerrit)

    unread,
    Jul 15, 2026, 5:35:31 PMJul 15
    to Austin Clements, goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, David Chase, Cherry Mui, golang-co...@googlegroups.com
    Attention needed from Austin Clements and David Chase

    Junyang Shao added 2 comments

    File src/simd/archsimd/_gen/specgen/flag.go
    Line 29, Patchset 6 (Latest):func MustFindSpecDir() string {
    path, err := FindSpecDir()
    if err != nil {
    fmt.Fprintln(os.Stderr, err.Error())
    Junyang Shao . unresolved

    This is a must happen function, should we exit here?

    Line 38, Patchset 6 (Latest): out, err := exec.Command("go", "env", "GOROOT").Output()
    Junyang Shao . unresolved

    If the user is using a release toolchain, will this be problematic?

    Gerrit-Comment-Date: Wed, 15 Jul 2026 21:35:26 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    open
    diffy

    David Chase (Gerrit)

    unread,
    Jul 20, 2026, 1:49:23 PMJul 20
    to Austin Clements, goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, Junyang Shao, Cherry Mui, golang-co...@googlegroups.com
    Attention needed from Austin Clements

    David Chase added 1 comment

    File src/simd/archsimd/_gen/specgen/flag.go
    Line 38, Patchset 6 (Latest): out, err := exec.Command("go", "env", "GOROOT").Output()
    Junyang Shao . unresolved

    If the user is using a release toolchain, will this be problematic?

    David Chase

    Agreed, I had to edit _gen/main.go to not do this. This code is not necessarily compiled with a Go compiler that has the specification directory.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Austin Clements
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: dev.simd
    Gerrit-Change-Id: Iaef5c67bef2734f9cd5854f5bf7c32f61b9e00ee
    Gerrit-Change-Number: 798846
    Gerrit-PatchSet: 6
    Gerrit-Owner: Austin Clements <aus...@google.com>
    Gerrit-Reviewer: Austin Clements <aus...@google.com>
    Gerrit-Reviewer: David Chase <drc...@google.com>
    Gerrit-Reviewer: Junyang Shao <shaoj...@google.com>
    Gerrit-CC: Cherry Mui <cher...@google.com>
    Gerrit-Attention: Austin Clements <aus...@google.com>
    Gerrit-Comment-Date: Mon, 20 Jul 2026 17:49:20 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Junyang Shao <shaoj...@google.com>
    unsatisfied_requirement
    open
    diffy

    Austin Clements (Gerrit)

    unread,
    Aug 6, 2026, 2:58:59 PM (yesterday) Aug 6
    to Austin Clements, goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, David Chase, Junyang Shao, Cherry Mui, golang-co...@googlegroups.com
    Attention needed from David Chase and Junyang Shao

    Austin Clements added 2 comments

    File src/simd/archsimd/_gen/specgen/flag.go
    Line 29, Patchset 6:func MustFindSpecDir() string {

    path, err := FindSpecDir()
    if err != nil {
    fmt.Fprintln(os.Stderr, err.Error())
    Junyang Shao . resolved

    This is a must happen function, should we exit here?

    Austin Clements

    Done

    Line 38, Patchset 6: out, err := exec.Command("go", "env", "GOROOT").Output()
    Junyang Shao . unresolved

    If the user is using a release toolchain, will this be problematic?

    David Chase

    Agreed, I had to edit _gen/main.go to not do this. This code is not necessarily compiled with a Go compiler that has the specification directory.

    Austin Clements

    Why? If you don't have the spec directory, it doesn't make any sense to run specgen.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • David Chase
    • Junyang Shao
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: dev.simd
    Gerrit-Change-Id: Iaef5c67bef2734f9cd5854f5bf7c32f61b9e00ee
    Gerrit-Change-Number: 798846
    Gerrit-PatchSet: 7
    Gerrit-Owner: Austin Clements <aus...@google.com>
    Gerrit-Reviewer: Austin Clements <aus...@google.com>
    Gerrit-Reviewer: David Chase <drc...@google.com>
    Gerrit-Reviewer: Junyang Shao <shaoj...@google.com>
    Gerrit-CC: Cherry Mui <cher...@google.com>
    Gerrit-Attention: David Chase <drc...@google.com>
    Gerrit-Attention: Junyang Shao <shaoj...@google.com>
    Gerrit-Comment-Date: Thu, 06 Aug 2026 18:58:49 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: David Chase <drc...@google.com>
    Comment-In-Reply-To: Junyang Shao <shaoj...@google.com>
    unsatisfied_requirement
    open
    diffy

    Austin Clements (Gerrit)

    unread,
    Aug 6, 2026, 2:59:00 PM (yesterday) Aug 6
    to Austin Clements, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
    Attention needed from David Chase and Junyang Shao

    Austin Clements uploaded new patchset

    Austin Clements uploaded patch set #7 to this change.
    Following approvals got outdated and were removed:
    Open in Gerrit

    Related details

    Attention is currently required from:
    • David Chase
    • Junyang Shao
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: newpatchset
    unsatisfied_requirement
    open
    diffy

    Austin Clements (Gerrit)

    unread,
    Aug 6, 2026, 3:07:30 PM (yesterday) Aug 6
    to Austin Clements, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
    Attention needed from David Chase and Junyang Shao

    Austin Clements uploaded new patchset

    Austin Clements uploaded patch set #8 to this change.
    Open in Gerrit

    Related details

    Attention is currently required from:
    • David Chase
    • Junyang Shao
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: newpatchset
    Gerrit-Project: go
    Gerrit-Branch: dev.simd
    Gerrit-Change-Id: Iaef5c67bef2734f9cd5854f5bf7c32f61b9e00ee
    Gerrit-Change-Number: 798846
    Gerrit-PatchSet: 8
    unsatisfied_requirement
    open
    diffy

    Austin Clements (Gerrit)

    unread,
    5:16 PM (5 hours ago) 5:16 PM
    to David Chase, Junyang Shao, goph...@pubsubhelper.golang.org, Cherry Mui, Austin Clements, golang-co...@googlegroups.com
    Attention needed from David Chase and Junyang Shao

    Austin Clements has uploaded the change for review

    Austin Clements would like David Chase and Junyang Shao to review this change.

    Commit message

    simd/archsimd/_gen/specgen: spec to SIMD API generator


    This package processes the SIMD spec package to produce the SIMD API
    description.

    The basic mechanism is that, for each exported function in the spec
    function, this iterates through every combination of type parameter
    values, keeps any combinations that satisfy additional constraint
    expressions, and plugs in the concrete types that worked.
    Change-Id: Iaef5c67bef2734f9cd5854f5bf7c32f61b9e00ee

    Change diff

    diff --git a/src/simd/archsimd/_gen/go.mod b/src/simd/archsimd/_gen/go.mod
    index 515f54f..e100626 100644
    index 0000000..0a2c0cc
    --- /dev/null
    +++ b/src/simd/archsimd/_gen/specgen/api.go
    @@ -0,0 +1,78 @@

    +// 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 (
    + "fmt"
    +	"simd/archsimd/_gen/specgen/specexpr"
    +	return buf.String()
    +}
    diff --git a/src/simd/archsimd/_gen/specgen/expand.go b/src/simd/archsimd/_gen/specgen/expand.go
    new file mode 100644
    index 0000000..06e3376
    --- /dev/null
    +++ b/src/simd/archsimd/_gen/specgen/expand.go
    @@ -0,0 +1,152 @@

    +// 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 (
    + "fmt"
    + "go/types"
    + "regexp"
    +	"simd/archsimd/_gen/specgen/specexpr"
    +	argGet := make(map[*types.Var]func(*specexpr.Bindings) specexpr.Type)

    + ok := true
    + for _, v := range sFn.Params {
    +		get := b.bindArg(v.Name(), v.Type())
    + ok = ok && (get != nil)
    + argGet[v] = get

    + }
    + for _, v := range sFn.Results {
    +		get := b.bindArg(v.Name(), v.Type())
    + ok = ok && (get != nil)
    + argGet[v] = get

    + }
    + // Add requirements to the solver
    + for _, expr := range sFn.Requirements {
    + solver.Assert(expr)
    + }
    +	if !ok {

    + return nil
    + }
    +
    +	// Find solutions.
    + defer func() {
    + p := recover()
    + if p != nil {
    + var buf strings.Builder
    + solver.Fprint(&buf)
    + panic(fmt.Sprintf("%s: %s\n%s", ctx.root.fset.Position(sFn.Pos), p, buf.String()))
    + }
    + }()
    + var funcs []*Func
    + for soln, err := range solver.Solve() {
    + if err != nil {
    + ctx.errorf("%s", err)
    + continue
    + }
    +
    +		fn := sFn.instantiate(ctx, soln, argGet)

    + if fn == nil {
    + continue
    + }
    +
    + funcs = append(funcs, fn)
    + }
    +
    + if len(funcs) == 0 {
    + ctx.errorf("impossible constraints (try -f %s -trace)", sFn.Name)
    + }
    +
    + return funcs
    +}
    +
    +func (sFn *specFunc) instantiate(ctx context, b *specexpr.Bindings, argGet map[*types.Var]func(*specexpr.Bindings) specexpr.Type) *Func {
    +		t := argGet[v](b)

    + f.In = append(f.In, Arg{v.Name(), t})
    + }
    + if method && len(f.In) > 0 {
    + f.Recv = f.In[0]
    + f.In = f.In[1:]
    + }
    + for _, v := range sFn.Results {
    +		t := argGet[v](b)

    + f.Out = append(f.Out, Arg{v.Name(), t})
    + }
    +
    + return &f
    +}
    diff --git a/src/simd/archsimd/_gen/specgen/flag.go b/src/simd/archsimd/_gen/specgen/flag.go
    new file mode 100644
    index 0000000..ad333da
    --- /dev/null
    +++ b/src/simd/archsimd/_gen/specgen/flag.go
    @@ -0,0 +1,44 @@

    +// 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 (
    + "fmt"
    +	"os"
    + "os/exec"
    + "path/filepath"
    + "strings"
    +)
    +
    +// FindSpecDir returns the path to the standard spec package
    +// $GOROOT/simd/internal/spec.
    +func FindSpecDir() (string, error) {
    + goroot, err := goEnvGoroot()

    + if err != nil {
    +		return "", fmt.Errorf("could not find GOROOT: %w", err)
    + }
    + path := filepath.Join(goroot, "src/simd/internal/spec")
    + if _, err := os.Stat(path); err != nil {
    + return "", fmt.Errorf("could not find spec package: %w", err)
    + }
    + return path, nil
    +}
    +
    +func MustFindSpecDir() string {
    + path, err := FindSpecDir()

    + if err != nil {
    +		fmt.Fprintln(os.Stderr, err.Error())
    + os.Exit(1)
    + }
    + return path
    +}
    +
    +func goEnvGoroot() (string, error) {
    + out, err := exec.Command("go", "env", "GOROOT").Output()

    + if err != nil {
    +		return "", err
    + }
    + return strings.TrimSpace(string(out)), nil

    +}
    diff --git a/src/simd/archsimd/_gen/specgen/load.go b/src/simd/archsimd/_gen/specgen/load.go
    new file mode 100644
    index 0000000..4e935bb

    --- /dev/null
    +++ b/src/simd/archsimd/_gen/specgen/load.go
    @@ -0,0 +1,119 @@
    +// 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 (
    +	"cmp"
    + "errors"
    + "fmt"
    + "go/ast"
    + "go/token"
    + "io"
    + "maps"
    +	"simd/archsimd/_gen/specgen/specexpr"
    index 0000000..4b96e30
    --- /dev/null
    +++ b/src/simd/archsimd/_gen/specgen/loadspec.go
    @@ -0,0 +1,264 @@

    +// 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 (
    + "fmt"
    + "go/ast"
    + "go/token"
    + "go/types"
    +	"simd/archsimd/_gen/specgen/specexpr"
    +)
    +
    +// specPackage represents the parsed _gen/spec package.
    +type specPackage struct {
    + Fset *token.FileSet
    + Pkg *types.Package
    + TypesInfo *types.Info
    + Funcs []*specFunc
    +
    +	TypeElems  map[types.Type]specexpr.Basic
    + TypeWidths map[types.Type]specexpr.Num

    +
    + VecType types.Type // Uninstantiated Vec type
    + ArrayType types.Type // Uninstantiated Array type
    +	UintNType types.Type // Uninstantiated UintN type
    +						default:
    + ctx.at(dir.Pos()).errorf("unknown //specgen directive")
    +	typeElems := make(map[types.Type]specexpr.Basic)

    + if eltOrMask := lookupType("EltOrMask"); eltOrMask != nil {
    + for _, elt := range typeSet(eltOrMask) {
    +			basic := shapeElemType(elt)
    + typeElems[elt] = basic
    + }
    + }
    + typeWidths := make(map[types.Type]specexpr.Num)

    + if width := lookupType("Width"); width != nil {
    + for _, width := range typeSet(width) {
    + val := shapeWidthVal(width)
    +			typeWidths[width] = val

    + }
    + }
    +
    + // Gather other known types
    + vecType := lookupType("Vec")
    + arrayType := lookupType("Array")
    +	uintNType := lookupType("UintN")

    +
    + pkg = specPackage{
    + Fset: fset,
    + Pkg: srcPkg.Types,
    + TypesInfo: info,
    + Funcs: funcs,
    +		TypeElems:  typeElems,
    + TypeWidths: typeWidths,

    + VecType: vecType,
    + ArrayType: arrayType,
    +		UintNType:  uintNType,
    index 0000000..f556523
    +		specPkg = loadSpecPackage(ctx, "../../../internal/spec", &LoadOptions{})
    index 0000000..07b0a7b
    --- /dev/null
    +++ b/src/simd/archsimd/_gen/specgen/types.go
    @@ -0,0 +1,294 @@

    +// 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 (
    + "fmt"
    + "go/types"
    + "regexp"
    +	"simd/archsimd/_gen/specgen/specexpr"

    + "strconv"
    + "strings"
    + "sync"
    +
    +	"golang.org/x/tools/go/types/typeutil"
    +)
    +
    +// constraintToDomain enumerates all types explicitly listed as satisfying
    +// constraint (which must be a type parameter constraint), and translates them
    +// to a [specexpr] domain.
    +func constraintToDomain(pkg *specPackage, constraint types.Type) ([]any, error) {
    + var vals []any
    + for _, typ := range typeSet(constraint) {
    +		elem, ok := pkg.TypeElems[typ]

    + if ok {
    + vals = append(vals, elem)
    +		} else if width, ok := pkg.TypeWidths[typ]; ok {
    +var basicRe = regexp.MustCompile(`^([a-z]+|Mask)([0-9]*)$`)

    +
    +// shapeElemType parses a basic or mask element spec type.
    +func shapeElemType(t types.Type) specexpr.Basic {
    + var name string
    + switch t := t.(type) {
    + case *types.Basic: // E.g., uint32
    + name = t.Name()
    +	case *types.Named: // E.g., Mask16

    + name = t.Obj().Name()
    + default:
    + panic(fmt.Sprintf("not a shape element type: %s", t))
    + }
    + m := basicRe.FindStringSubmatch(name)
    + if m == nil {
    + panic(fmt.Sprintf("failed to parse element type %s", name))
    + }
    + bits := 0
    + if m[2] != "" {
    + bits, _ = strconv.Atoi(m[2])
    + }
    +	return specexpr.Basic{Base: m[1], Bits: specexpr.Int(bits)}

    +}
    +
    +// shapeWidthVal parses a spec width type.
    +func shapeWidthVal(t types.Type) specexpr.Num {

    + named, ok := t.(*types.Named)
    + if !ok {
    + panic(fmt.Sprintf("not a shape width type: %s", t))
    + }
    + name := named.Obj().Name()
    + if name == "WidthScalable" {
    +		return specexpr.VW()

    + }
    + var err error
    + if suffix, ok := strings.CutPrefix(name, "Width"); ok {
    + var val int
    + val, err = strconv.Atoi(suffix)
    + if err == nil {
    +			return specexpr.Int(val)

    + }
    + } else {
    + err = fmt.Errorf("does not start with 'Width'")
    + }
    + panic(fmt.Sprintf("parsing width type %s: %s", t, err))
    +}
    +
    +type argBinder struct {
    + ctx context
    + pkg *specPackage
    + s *specexpr.Solver
    + typeParams map[*types.TypeParam]specexpr.Variable
    +}
    +
    +// bindArg assigns all solver variables related to an argument called "name" of
    +// type t. It returns a function that retrieves the resolved type, or nil on
    +// error.
    +func (b *argBinder) bindArg(name string, t types.Type) func(*specexpr.Bindings) specexpr.Type {

    + expr := b.bind1(name, t)
    + if expr == nil {
    +		return nil
    + }
    + v := b.s.Assign(specexpr.Variable(name), expr)
    + return func(b *specexpr.Bindings) specexpr.Type {
    + return b.Get(v).(specexpr.Type)
    + }
    +	case *types.Array:

    + elem := b.bind1(name, t.Elem())
    + if elem == nil {
    + return nil
    + }
    +		return specexpr.MakeArray(elem, specexpr.Int(t.Len()))

    +
    + case *types.Named:
    + if types.Identical(t.Origin(), b.pkg.VecType) {
    + xE, xW, _ := b.bindVecLike(name, t)
    + if xE == nil {
    + return nil
    + }
    + return specexpr.MakeVector(xE, xW)
    + }
    + if types.Identical(t.Origin(), b.pkg.ArrayType) {
    + xE, xW, xL := b.bindVecLike(name, t)
    + if xE == nil {
    + return nil
    + }
    +			b.s.Assert(funcAssertFixed(xW))
    + return specexpr.MakeArray(xE, xL)
    + }
    + if types.Identical(t.Origin(), b.pkg.UintNType) {
    + xN := specexpr.Variable(name + "N")
    + b.s.Declare(xN, []any{8, 16, 32, 64})
    + b.s.Assert(funcAssertFixed(xN))
    + return specexpr.MakeBasic(&specexpr.Literal{Val: "uint"}, xN)

    + }
    +
    + case *types.TypeParam:
    + return b.typeParams[t]
    + }
    +
    + b.ctx.errorf("cannot convert spec type %s into API type", t)
    + return nil
    +}
    +
    +var funcAssertFixed = specexpr.MakeFunc1("assertFixed", func(w specexpr.Num) (any, error) {
    + _, ok := w.(specexpr.Int)
    + return ok, nil

    +})
    +
    +func (b *argBinder) bindVecLike(name string, t *types.Named) (xE, xW, xL specexpr.Expr) {
    + args := t.TypeArgs()
    + if args.Len() != 2 {
    + b.ctx.errorf("expected exactly 2 type arguments, got %d", args.Len())
    + return nil, nil, nil
    + }
    +
    + // Assign the element type
    + elem := b.bind1(name+"E", args.At(0))
    + if elem == nil {
    + return nil, nil, nil
    + }
    + xE = b.s.Assign(specexpr.Variable(name+"E"), elem)
    +
    + // Get the width
    + var wExpr specexpr.Expr
    + switch wt := args.At(1).(type) {
    + case *types.TypeParam:
    + wExpr = b.typeParams[wt]
    + case *types.Named:
    +		wExpr = b.pkg.TypeWidths[wt]
    + if wExpr == nil {

    + b.ctx.errorf("width type argument is not a width")
    + return nil, nil, nil
    + }

    Change information

    Files:
      • A src/simd/archsimd/_gen/specgen/flag.go
      • A src/simd/archsimd/_gen/specgen/load.go
      • A src/simd/archsimd/_gen/specgen/loadspec.go
      • A src/simd/archsimd/_gen/specgen/loadspec_test.go
      • A src/simd/archsimd/_gen/specgen/types.go
      • A src/simd/archsimd/_gen/specgen/types_test.go
        Change size: XL
        Delta: 10 files changed, 1208 insertions(+), 0 deletions(-)
        Open in Gerrit

        Related details

        Attention is currently required from:
        • David Chase
        • Junyang Shao
        Submit Requirements:
          • requirement is not satisfiedCode-Review
          • requirement satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          • requirement is not satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: newchange
          Gerrit-Project: go
          Gerrit-Branch: master
          Gerrit-Change-Id: Iaef5c67bef2734f9cd5854f5bf7c32f61b9e00ee
          Gerrit-Change-Number: 812062
          Gerrit-PatchSet: 1
          Gerrit-Owner: Austin Clements <aus...@google.com>
          Gerrit-Reviewer: Austin Clements <aus...@google.com>
          Gerrit-Reviewer: David Chase <drc...@google.com>
          Gerrit-Reviewer: Junyang Shao <shaoj...@google.com>
          unsatisfied_requirement
          satisfied_requirement
          open
          diffy
          Reply all
          Reply to author
          Forward
          0 new messages