Source file src/cmd/compile/internal/midway/analysis.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 midway
     6  
     7  import (
     8  	"cmd/compile/internal/syntax"
     9  	"cmd/compile/internal/types2"
    10  )
    11  
    12  // Analyzer holds the state for SIMD dependency analysis
    13  type Analyzer struct {
    14  	pkg          *types2.Package
    15  	info         *types2.Info
    16  	dependentObj map[types2.Object]bool
    17  	visited      map[types2.Type]bool
    18  	inSimd       bool
    19  }
    20  
    21  func NewAnalyzer(pkg *types2.Package, info *types2.Info) *Analyzer {
    22  	return &Analyzer{
    23  		pkg:          pkg,
    24  		info:         info,
    25  		dependentObj: make(map[types2.Object]bool),
    26  		visited:      make(map[types2.Type]bool),
    27  		inSimd:       pkg.Path() == simdPkg,
    28  	}
    29  }
    30  
    31  // Analyze builds the set of SIMD-dependent objects
    32  func (a *Analyzer) Analyze(files []*syntax.File) bool {
    33  	// Phase 1: Seed dependence from types and signatures
    34  	for _, obj := range a.info.Defs {
    35  		if obj != nil {
    36  			a.markIfDependent(obj)
    37  		}
    38  	}
    39  	for _, obj := range a.info.Uses {
    40  		if obj != nil {
    41  			a.markIfDependent(obj)
    42  		}
    43  	}
    44  
    45  	// Phase 2: Transitive closure via function bodies
    46  	changed := true
    47  	for changed {
    48  		changed = false
    49  		for _, file := range files {
    50  			for _, decl := range file.DeclList {
    51  				if fn, ok := decl.(*syntax.FuncDecl); ok {
    52  					if fn.Name == nil {
    53  						continue
    54  					}
    55  					obj := a.info.Defs[fn.Name]
    56  					if obj == nil || a.dependentObj[obj] {
    57  						continue
    58  					}
    59  
    60  					if a.hasBodyDependency(fn) {
    61  						a.dependentObj[obj] = true
    62  						changed = true
    63  					}
    64  				}
    65  			}
    66  		}
    67  	}
    68  
    69  	return len(a.dependentObj) > 0
    70  }
    71  
    72  func (a *Analyzer) hasBodyDependency(fn *syntax.FuncDecl) bool {
    73  	if fn.Body == nil {
    74  		return false
    75  	}
    76  	// Walk the body and check identifiers
    77  	found := false
    78  	syntax.Inspect(fn.Body, func(n syntax.Node) bool {
    79  		if found {
    80  			return false
    81  		}
    82  		if id, ok := n.(*syntax.Name); ok {
    83  			obj := a.info.Uses[id]
    84  			if obj == nil {
    85  				obj = a.info.Defs[id]
    86  			}
    87  			if obj != nil {
    88  				if _, isFunc := obj.(*types2.Func); !isFunc {
    89  					if a.dependentObj[obj] {
    90  						found = true
    91  						return false
    92  					}
    93  				} else {
    94  					sig := obj.Type().(*types2.Signature)
    95  					if a.HasDependentSignature(sig) {
    96  						found = true
    97  						return false
    98  					}
    99  				}
   100  				if a.isDependentType(obj.Type()) {
   101  					found = true
   102  					return false
   103  				}
   104  				if isBaseSimdTypeObj(obj) {
   105  					found = true
   106  					return false
   107  				}
   108  			}
   109  		}
   110  		return true
   111  	})
   112  	return found
   113  }
   114  
   115  func (a *Analyzer) markIfDependent(obj types2.Object) bool {
   116  	if a.dependentObj[obj] {
   117  		return true
   118  	}
   119  
   120  	isDep := false
   121  	switch obj := obj.(type) {
   122  	case *types2.Var:
   123  		if obj.Pkg() == a.pkg && obj.Parent() == a.pkg.Scope() {
   124  			isDep = a.isDependentType(obj.Type())
   125  		}
   126  	case *types2.TypeName:
   127  		isDep = a.isDependentType(obj.Type())
   128  	case *types2.Func:
   129  		sig := obj.Type().(*types2.Signature)
   130  		if a.HasDependentSignature(sig) {
   131  			// NOT dependent if it is a method of one of the base SIMD types.
   132  			// TODO: what about aliases of base SIMD types?
   133  			if rcv := sig.Recv(); rcv == nil {
   134  				isDep = true
   135  			} else if named, ok := rcv.Type().(*types2.Named); !ok || !isBaseSimdType(named) {
   136  				isDep = true
   137  			}
   138  		}
   139  	}
   140  
   141  	// Also check if obj name is "simd.Type" (base case)
   142  	if isBaseSimdTypeObj(obj) {
   143  		isDep = true
   144  	}
   145  
   146  	if isDep {
   147  		a.dependentObj[obj] = true
   148  	}
   149  	return isDep
   150  }
   151  
   152  func (a *Analyzer) isDependentType(t types2.Type) bool {
   153  	return a.checkTypeRecursive(t)
   154  }
   155  
   156  func (a *Analyzer) checkTypeRecursive(t types2.Type) bool {
   157  	if t == nil {
   158  		return false
   159  	}
   160  	if b, ok := a.visited[t]; ok {
   161  		return b // Break cycles
   162  	}
   163  	a.visited[t] = false
   164  
   165  	memo := func(b bool) bool {
   166  		a.visited[t] = b
   167  		return b
   168  	}
   169  
   170  	// Unwrap aliases
   171  	if named, ok := t.(*types2.Named); ok {
   172  		if isBaseSimdType(named) {
   173  			return memo(true)
   174  		}
   175  		if a.checkTypeRecursive(named.Underlying()) {
   176  			return memo(true)
   177  		}
   178  	}
   179  
   180  	switch t := t.(type) {
   181  	case *types2.Basic:
   182  		return false
   183  	case *types2.Pointer:
   184  		return memo(a.checkTypeRecursive(t.Elem()))
   185  	case *types2.Slice:
   186  		return memo(a.checkTypeRecursive(t.Elem()))
   187  	case *types2.Array:
   188  		return memo(a.checkTypeRecursive(t.Elem()))
   189  	case *types2.Map:
   190  		return memo(a.checkTypeRecursive(t.Key()) ||
   191  			a.checkTypeRecursive(t.Elem()))
   192  	case *types2.Chan:
   193  		return memo(a.checkTypeRecursive(t.Elem()))
   194  	case *types2.Struct:
   195  		for i := 0; i < t.NumFields(); i++ {
   196  			if a.checkTypeRecursive(t.Field(i).Type()) {
   197  				return memo(true)
   198  			}
   199  		}
   200  	case *types2.Signature:
   201  		return memo(a.HasDependentSignature(t))
   202  	case *types2.Tuple:
   203  		for i := 0; i < t.Len(); i++ {
   204  			if a.checkTypeRecursive(t.At(i).Type()) {
   205  				return memo(true)
   206  			}
   207  		}
   208  	case *types2.Alias:
   209  		return memo(a.checkTypeRecursive(types2.Unalias(t)))
   210  	}
   211  	return false
   212  }
   213  
   214  func isBaseSimdType(t *types2.Named) bool {
   215  	return isBaseSimdTypeObj(t.Obj())
   216  }
   217  
   218  func isBaseSimdTypeObj(obj types2.Object) bool {
   219  	if obj == nil || obj.Pkg() == nil {
   220  		return false
   221  	}
   222  	if obj.Pkg().Path() != simdPkg {
   223  		return false
   224  	}
   225  	return isSimdTypeName(obj.Name())
   226  }
   227  
   228  func (a *Analyzer) HasDependentSignature(sig *types2.Signature) bool {
   229  	// TODO what about type parameters?  Need to invent a test that provokes that case.
   230  	return a.isDependentType(sig.Params()) ||
   231  		a.isDependentType(sig.Results()) ||
   232  		(sig.Recv() != nil && a.isDependentType(sig.Recv().Type()))
   233  }
   234  

View as plain text