Source file src/cmd/compile/internal/gc/symidx.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 gc
     6  
     7  import (
     8  	"maps"
     9  	"slices"
    10  
    11  	"cmd/compile/internal/base"
    12  	"cmd/compile/internal/ir"
    13  	"cmd/compile/internal/reflectdata"
    14  	"cmd/compile/internal/ssagen"
    15  	"cmd/compile/internal/typecheck"
    16  	"cmd/compile/internal/types"
    17  	"cmd/internal/goobj"
    18  	"cmd/internal/obj"
    19  )
    20  
    21  // preassignSymIdxs assigns a SymIdx to the symbols that will appear in export
    22  // data. This allows the export data to be emitted before the backend runs and NumberSyms is called.
    23  func preassignSymIdxs(symABIs *ssagen.SymABIs) {
    24  	// The visit is used to populate usedTypes, which is then used to filter additional
    25  	// symbols from types.LocalPkg.Syms that are not top level, but that need to be
    26  	// exported. For example, this includes a used type (meaning that it won't be removed by deadlocals)
    27  	// that's defined locally inside a top level function.
    28  	usedTypes := make(map[*types.Type]bool)
    29  	var visit func(*types.Type)
    30  	visit = func(t *types.Type) {
    31  		if t == nil || usedTypes[t] {
    32  			return
    33  		}
    34  		usedTypes[t] = true
    35  		switch t.Kind() {
    36  		case types.TPTR, types.TSLICE, types.TARRAY, types.TCHAN:
    37  			visit(t.Elem())
    38  		case types.TMAP:
    39  			visit(t.Key())
    40  			visit(t.Elem())
    41  		case types.TSTRUCT:
    42  			for _, f := range t.Fields() {
    43  				visit(f.Type)
    44  			}
    45  		case types.TFUNC:
    46  			for _, f := range t.RecvParamsResults() {
    47  				visit(f.Type)
    48  			}
    49  		case types.TINTER:
    50  			for _, m := range t.AllMethods() {
    51  				visit(m.Type)
    52  			}
    53  		}
    54  		if t.Sym() != nil && !t.IsInterface() {
    55  			for _, m := range t.Methods() {
    56  				visit(m.Type)
    57  			}
    58  		}
    59  	}
    60  
    61  	for _, n := range typecheck.Target.Externs {
    62  		if n.Op() == ir.ONAME && nameShouldBeIndexed(n) {
    63  			base.Ctxt.NumberSymEarly(n.Linksym())
    64  		}
    65  		if n.Op() == ir.OTYPE {
    66  			visit(n.Type())
    67  		}
    68  	}
    69  
    70  	for _, fn := range typecheck.Target.Funcs {
    71  		if funcShouldBeIndexed(fn, symABIs) {
    72  			base.Ctxt.NumberSymEarly(fn.Nname.Linksym())
    73  		}
    74  		for _, dcl := range fn.Dcl {
    75  			if dcl.Op() == ir.ONAME && dcl.Used() {
    76  				visit(dcl.Type())
    77  			}
    78  		}
    79  	}
    80  
    81  	reflectdata.ForEachRuntimeType(visit)
    82  
    83  	// Number any other types from types.LocalPkg.Syms that are reachable from the
    84  	// top level declarations or from types we know we will generate runtime data for.
    85  	for _, name := range slices.Sorted(maps.Keys(types.LocalPkg.Syms)) {
    86  		if n, ok := types.LocalPkg.Syms[name].Def.(*ir.Name); ok && usedTypes[n.Type()] {
    87  			if n.Op() == ir.OTYPE && !n.Alias() && typeShouldBeIndexed(n.Type()) {
    88  				base.Ctxt.NumberSymEarly(reflectdata.TypeLinksym(n.Type()))
    89  				base.Ctxt.NumberSymEarly(reflectdata.TypeLinksym(n.Type().PtrTo()))
    90  			}
    91  		}
    92  	}
    93  }
    94  
    95  // lsymShouldBeIndexed checks the common conditions where names, types, and funcs should be preassigned an index for export data:
    96  //   - We should not have already preassigned an index for the symbol (nothing to do)
    97  //   - It should not be content addressable (we don't necessarily know their content yet)
    98  //   - It should belong to the current package.
    99  func lsymShouldBeIndexed(lsym *obj.LSym) bool {
   100  	return lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed() && !lsym.ContentAddressable() && !base.Ctxt.IsNonPkgSym(lsym)
   101  }
   102  
   103  // nameShouldBeIndexed checks the conditions where names should be preassigned an index for export data:
   104  //   - They are global variable symbols (PEXTERN)
   105  //   - They belong to the current package (otherwise they should be numbered when that package is compiled)
   106  //   - They don't have a linkname (see IsNonPkgSym).
   107  //
   108  // TODO(matloob): Can we drop the linkname condition? Is it already checked by IsNonPkgSym?
   109  func nameShouldBeIndexed(name *ir.Name) bool {
   110  	return lsymShouldBeIndexed(name.Linksym()) &&
   111  		name.Class == ir.PEXTERN && name.Sym().Pkg == types.LocalPkg && name.Sym().Linkname == ""
   112  }
   113  
   114  // typeShouldBeIndexed checks the conditions where types should be preassigned an index for export data:
   115  //   - They belong to the current package
   116  //   - They are not DUPOK.
   117  func typeShouldBeIndexed(typ *types.Type) bool {
   118  	return typ.Sym() != nil && typ.Sym().Pkg == types.LocalPkg && !reflectdata.TypeCanBeDupok(typ) &&
   119  		lsymShouldBeIndexed(types.TypeSym(typ).Linksym())
   120  }
   121  
   122  // TODO(matloob): better document when functions should be preassigned an index for export data.
   123  func funcShouldBeIndexed(fn *ir.Func, symABIs *ssagen.SymABIs) bool {
   124  	name := fn.Nname
   125  	if fn.ABI != obj.ABIInternal || fn.Dupok() || fn.IsClosure() || ir.IsBlank(name) || name.Sym().Linkname != "" || !lsymShouldBeIndexed(name.Linksym()) {
   126  		return false
   127  	}
   128  	return len(fn.Body) != 0 || fn.WasmImport != nil || needsIntrinsicBody(fn, symABIs)
   129  }
   130  

View as plain text