Source file src/simd/archsimd/_gen/specgen/load.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package specgen
     6  
     7  import (
     8  	"cmp"
     9  	"errors"
    10  	"fmt"
    11  	"go/ast"
    12  	"go/token"
    13  	"io"
    14  	"maps"
    15  	"simd/archsimd/_gen/specgen/specexpr"
    16  	"slices"
    17  	"strings"
    18  )
    19  
    20  type LoadOptions struct {
    21  	// Filter, if non-nil, causes Load to process only spec functions satisfying
    22  	// Filter.
    23  	Filter func(*ast.FuncDecl) bool
    24  
    25  	// Trace, if non-nil, causes Load to log a debug trace of solver steps to
    26  	// Trace.
    27  	Trace io.Writer
    28  }
    29  
    30  // Load loads a Go SIMD spec from the package in directory dir. This is the main
    31  // entrypoint to this package.
    32  func Load(dir string, opts *LoadOptions) ([]*Func, error) {
    33  	if opts == nil {
    34  		opts = new(LoadOptions)
    35  	}
    36  
    37  	var root contextRoot
    38  	ctx := context{root: &root}
    39  
    40  	pkg := loadSpecPackage(ctx, dir, opts)
    41  	if err := root.gatherErrors(); err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	var allFuncs []*Func
    46  	type funcKey struct {
    47  		recv specexpr.Type
    48  		name string
    49  	}
    50  	funcSet := make(map[funcKey]*Func)
    51  	for _, sFn := range pkg.Funcs {
    52  		expanded := sFn.expand(ctx, opts)
    53  
    54  		// Check for duplicates
    55  		for _, fn := range expanded {
    56  			key := funcKey{fn.Recv.Type, fn.Name}
    57  			if ofn := funcSet[key]; ofn != nil {
    58  				ctx.at(sFn.Pos).errorf("conflicting functions:\n\t%s\n\t%s", fn.Signature(), ofn.Signature())
    59  				continue
    60  			}
    61  			funcSet[key] = fn
    62  			allFuncs = append(allFuncs, fn)
    63  		}
    64  	}
    65  
    66  	return allFuncs, root.gatherErrors()
    67  }
    68  
    69  type contextRoot struct {
    70  	fset   token.FileSet
    71  	errors map[srcError]struct{}
    72  }
    73  
    74  type context struct {
    75  	root *contextRoot
    76  	pos  token.Pos
    77  	fn   string
    78  }
    79  
    80  func (c context) at(pos token.Pos) context {
    81  	c.pos = pos
    82  	return c
    83  }
    84  
    85  func (c context) errorf(msg string, args ...any) {
    86  	if c.root.errors == nil {
    87  		c.root.errors = make(map[srcError]struct{})
    88  	}
    89  	err := srcError{c.pos, c.fn, fmt.Sprintf(msg, args...)}
    90  	c.root.errors[err] = struct{}{}
    91  }
    92  
    93  func (r *contextRoot) gatherErrors() error {
    94  	if len(r.errors) == 0 {
    95  		return nil
    96  	}
    97  	var errs []error
    98  	var buf strings.Builder
    99  	for _, err := range slices.SortedFunc(maps.Keys(r.errors), func(a, b srcError) int {
   100  		return cmp.Or(cmp.Compare(a.pos, b.pos), cmp.Compare(a.msg, b.msg))
   101  	}) {
   102  		if err.pos.IsValid() {
   103  			fmt.Fprintf(&buf, "%s: ", r.fset.Position(err.pos))
   104  		}
   105  		buf.WriteString(err.msg)
   106  		if err.fn != "" {
   107  			fmt.Fprintf(&buf, " in %s", err.fn)
   108  		}
   109  		errs = append(errs, fmt.Errorf("%s", buf.String()))
   110  		buf.Reset()
   111  	}
   112  	return errors.Join(errs...)
   113  }
   114  
   115  type srcError struct {
   116  	pos token.Pos
   117  	fn  string
   118  	msg string
   119  }
   120  

View as plain text