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

View as plain text