// 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/base" "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 isDependentObj map[types2.Object]bool // does an Object depend on a simd type in some way? isDependentMethod map[types2.Object]bool // is this dependent Object also a method? (methods are not renamed, their types are) hasDependentMethod map[types2.Type]bool // is this a type that has a dependent method? visited map[types2.Type]bool // if in map, type has been visited, and value is whether type is dependent inSimd bool // true if the current package is the simd package (which is a special case) } func NewAnalyzer(pkg *types2.Package, info *types2.Info) *Analyzer { return &Analyzer{ pkg: pkg, info: info, isDependentObj: make(map[types2.Object]bool), isDependentMethod: make(map[types2.Object]bool), hasDependentMethod: make(map[types2.Type]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 hdmsize := len(a.hasDependentMethod) for { for _, obj := range a.info.Defs { if obj != nil { a.markIfDependent(obj) } } for _, obj := range a.info.Uses { if obj != nil { a.markIfDependent(obj) } } if hdmsize == len(a.hasDependentMethod) { break } if base.Debug.Simd > 0 { base.Warn("hasDependentMethod increased from %d to %d", hdmsize, len(a.hasDependentMethod)) } hdmsize = len(a.hasDependentMethod) clear(a.visited) } // 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.isDependentObj[obj] { continue } if a.hasBodyDependency(fn) { a.isDependentObj[obj] = true changed = true } } } } } return len(a.isDependentObj) > 0 } func (a *Analyzer) hasBodyDependency(fn *syntax.FuncDecl) bool { if fn.Body == nil { return false } // Walk the body and check identifiers // This will also note any variable references that are dependent. found := false syntax.Inspect(fn.Body, func(n syntax.Node) bool { 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.isDependentObj[obj] { found = true return false } } else { sig := obj.Type().(*types2.Signature) if a.HasDependentSignature(sig) { found = true return false } } if a.isDependentType(obj.Type()) { // Whatever this is, it makes the outer object dependent. // If this is a package variable with dependent type, mark the // variable as dependent, so that references to it become dependent. if obj, ok := obj.(*types2.Var); ok && obj.Kind() == types2.PackageVar { // everything else is nested within a dependent function/struct/scope // and does not need its own renaming a.isDependentObj[obj] = true } 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.isDependentObj[obj] { return true } isDep := false isDepMeth := 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 t := rcv.Type() if !a.isDependentType(t) { a.markHasMethod(t) } isDepMeth = true } } } // Also check if obj name is "simd.Type" (base case) if isBaseSimdTypeObj(obj) { isDep = true } if isDep { if base.Debug.Simd > 0 { base.Warn("%s: %v is simd-dependent", obj.Pos().String(), obj) } a.isDependentObj[obj] = true } if isDepMeth { if base.Debug.Simd > 0 { base.Warn("%s: %v is simd-dependent method", obj.Pos().String(), obj) } a.isDependentMethod[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 a.hasDependentMethod[t] { a.visited[t] = true } 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 } // This attempts to mark types that are not otherwise dependent // as being dependent, if they have a method with a dependent // signature. func (a *Analyzer) markHasMethod(t types2.Type) { if t == nil { return } if a.hasDependentMethod[t] { return } a.hasDependentMethod[t] = true switch t := t.(type) { case *types2.Pointer: a.markHasMethod(t.Elem()) case *types2.Alias: a.markHasMethod(t.Rhs()) } return } 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())) }