| // 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 midway |
| |
| import ( |
| "cmd/compile/internal/syntax" |
| "cmd/compile/internal/types2" |
| ) |
| |
| // Analyzer holds the state for SIMD dependency analysis |
| type Analyzer struct { |
| pkg *types2.Package |
| info *types2.Info |
| dependentObj map[types2.Object]bool |
| visited map[types2.Type]bool |
| inSimd bool |
| } |
| |
| func NewAnalyzer(pkg *types2.Package, info *types2.Info) *Analyzer { |
| return &Analyzer{ |
| pkg: pkg, |
| info: info, |
| dependentObj: make(map[types2.Object]bool), |
| visited: make(map[types2.Type]bool), |
| inSimd: pkg.Path() == simdPkg, |
| } |
| } |
| |
| // Analyze builds the set of SIMD-dependent objects |
| func (a *Analyzer) Analyze(files []*syntax.File) bool { |
| // Phase 1: Seed dependence from types and signatures |
| for _, obj := range a.info.Defs { |
| if obj != nil { |
| a.markIfDependent(obj) |
| } |
| } |
| for _, obj := range a.info.Uses { |
| if obj != nil { |
| a.markIfDependent(obj) |
| } |
| } |
| |
| // Phase 2: Transitive closure via function bodies |
| changed := true |
| for changed { |
| changed = false |
| for _, file := range files { |
| for _, decl := range file.DeclList { |
| if fn, ok := decl.(*syntax.FuncDecl); ok { |
| if fn.Name == nil { |
| continue |
| } |
| obj := a.info.Defs[fn.Name] |
| if obj == nil || a.dependentObj[obj] { |
| continue |
| } |
| |
| if a.hasBodyDependency(fn) { |
| a.dependentObj[obj] = true |
| changed = true |
| } |
| } |
| } |
| } |
| } |
| |
| return len(a.dependentObj) > 0 |
| } |
| |
| func (a *Analyzer) hasBodyDependency(fn *syntax.FuncDecl) bool { |
| if fn.Body == nil { |
| return false |
| } |
| // Walk the body and check identifiers |
| found := false |
| syntax.Inspect(fn.Body, func(n syntax.Node) bool { |
| if found { |
| return false |
| } |
| if id, ok := n.(*syntax.Name); ok { |
| obj := a.info.Uses[id] |
| if obj == nil { |
| obj = a.info.Defs[id] |
| } |
| if obj != nil { |
| if _, isFunc := obj.(*types2.Func); !isFunc { |
| if a.dependentObj[obj] { |
| found = true |
| return false |
| } |
| } else { |
| sig := obj.Type().(*types2.Signature) |
| if a.HasDependentSignature(sig) { |
| found = true |
| return false |
| } |
| } |
| if a.isDependentType(obj.Type()) { |
| found = true |
| return false |
| } |
| if isBaseSimdTypeObj(obj) { |
| found = true |
| return false |
| } |
| } |
| } |
| return true |
| }) |
| return found |
| } |
| |
| func (a *Analyzer) markIfDependent(obj types2.Object) bool { |
| if a.dependentObj[obj] { |
| return true |
| } |
| |
| isDep := false |
| switch obj := obj.(type) { |
| case *types2.Var: |
| if obj.Pkg() == a.pkg && obj.Parent() == a.pkg.Scope() { |
| isDep = a.isDependentType(obj.Type()) |
| } |
| case *types2.TypeName: |
| isDep = a.isDependentType(obj.Type()) |
| case *types2.Func: |
| sig := obj.Type().(*types2.Signature) |
| if a.HasDependentSignature(sig) { |
| // NOT dependent if it is a method of one of the base SIMD types. |
| // TODO: what about aliases of base SIMD types? |
| if rcv := sig.Recv(); rcv == nil { |
| isDep = true |
| } else if named, ok := rcv.Type().(*types2.Named); !ok || !isBaseSimdType(named) { |
| isDep = true |
| } |
| } |
| } |
| |
| // Also check if obj name is "simd.Type" (base case) |
| if isBaseSimdTypeObj(obj) { |
| isDep = true |
| } |
| |
| if isDep { |
| a.dependentObj[obj] = true |
| } |
| return isDep |
| } |
| |
| func (a *Analyzer) isDependentType(t types2.Type) bool { |
| return a.checkTypeRecursive(t) |
| } |
| |
| func (a *Analyzer) checkTypeRecursive(t types2.Type) bool { |
| if t == nil { |
| return false |
| } |
| if b, ok := a.visited[t]; ok { |
| return b // Break cycles |
| } |
| a.visited[t] = false |
| |
| memo := func(b bool) bool { |
| a.visited[t] = b |
| return b |
| } |
| |
| // Unwrap aliases |
| if named, ok := t.(*types2.Named); ok { |
| if isBaseSimdType(named) { |
| return memo(true) |
| } |
| if a.checkTypeRecursive(named.Underlying()) { |
| return memo(true) |
| } |
| } |
| |
| switch t := t.(type) { |
| case *types2.Basic: |
| return false |
| case *types2.Pointer: |
| return memo(a.checkTypeRecursive(t.Elem())) |
| case *types2.Slice: |
| return memo(a.checkTypeRecursive(t.Elem())) |
| case *types2.Array: |
| return memo(a.checkTypeRecursive(t.Elem())) |
| case *types2.Map: |
| return memo(a.checkTypeRecursive(t.Key()) || |
| a.checkTypeRecursive(t.Elem())) |
| case *types2.Chan: |
| return memo(a.checkTypeRecursive(t.Elem())) |
| case *types2.Struct: |
| for i := 0; i < t.NumFields(); i++ { |
| if a.checkTypeRecursive(t.Field(i).Type()) { |
| return memo(true) |
| } |
| } |
| case *types2.Signature: |
| return memo(a.HasDependentSignature(t)) |
| case *types2.Tuple: |
| for i := 0; i < t.Len(); i++ { |
| if a.checkTypeRecursive(t.At(i).Type()) { |
| return memo(true) |
| } |
| } |
| case *types2.Alias: |
| return memo(a.checkTypeRecursive(types2.Unalias(t))) |
| } |
| return false |
| } |
| |
| func isBaseSimdType(t *types2.Named) bool { |
| return isBaseSimdTypeObj(t.Obj()) |
| } |
| |
| func isBaseSimdTypeObj(obj types2.Object) bool { |
| if obj == nil || obj.Pkg() == nil { |
| return false |
| } |
| if obj.Pkg().Path() != simdPkg { |
| return false |
| } |
| return isSimdTypeName(obj.Name()) |
| } |
| |
| func (a *Analyzer) HasDependentSignature(sig *types2.Signature) bool { |
| // TODO what about type parameters? Need to invent a test that provokes that case. |
| return a.isDependentType(sig.Params()) || |
| a.isDependentType(sig.Results()) || |
| (sig.Recv() != nil && a.isDependentType(sig.Recv().Type())) |
| } |