| // 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" |
| "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 |
| } |