blob: d9779571a78b20f79ea2f329f6da0d4d07f68a39 [file] [edit]
// 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()))
}