| // 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" | |
| "simd/archsimd/_gen/specgen/specexpr" | |
| "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} | |
| 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 | |
| } | |
| fn.typeParamVars = typeParamVars | |
| 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 { | |
| var f Func | |
| f.Commutative = sFn.Commutative | |
| f.Category = sFn.Category | |
| f.Pos = sFn.Pkg.Fset.Position(sFn.Pos) | |
| f.specFunc = sFn | |
| f.instance = b | |
| // 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 and doc | |
| var rawDoc string | |
| f.Name, rawDoc = sFn.expandNameAndDoc(ctx, b) | |
| f.Doc = parseSpecDoc(ctx.at(sFn.Pos), rawDoc) | |
| // 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 := 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 | |
| } |