github.com/bir3/gocompiler@v0.9.2202/src/cmd/compile/internal/noder/linker.go (about)

     1  // Copyright 2021 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 noder
     6  
     7  import (
     8  	"github.com/bir3/gocompiler/src/internal/buildcfg"
     9  	"github.com/bir3/gocompiler/src/internal/pkgbits"
    10  	"io"
    11  
    12  	"github.com/bir3/gocompiler/src/cmd/compile/internal/base"
    13  	"github.com/bir3/gocompiler/src/cmd/compile/internal/ir"
    14  	"github.com/bir3/gocompiler/src/cmd/compile/internal/reflectdata"
    15  	"github.com/bir3/gocompiler/src/cmd/compile/internal/types"
    16  	"github.com/bir3/gocompiler/src/cmd/internal/goobj"
    17  	"github.com/bir3/gocompiler/src/cmd/internal/obj"
    18  )
    19  
    20  // This file implements the unified IR linker, which combines the
    21  // local package's stub data with imported package data to produce a
    22  // complete export data file. It also rewrites the compiler's
    23  // extension data sections based on the results of compilation (e.g.,
    24  // the function inlining cost and linker symbol index assignments).
    25  //
    26  // TODO(mdempsky): Using the name "linker" here is confusing, because
    27  // readers are likely to mistake references to it for cmd/link. But
    28  // there's a shortage of good names for "something that combines
    29  // multiple parts into a cohesive whole"... e.g., "assembler" and
    30  // "compiler" are also already taken.
    31  
    32  // TODO(mdempsky): Should linker go into pkgbits? Probably the
    33  // low-level linking details can be moved there, but the logic for
    34  // handling extension data needs to stay in the compiler.
    35  
    36  // A linker combines a package's stub export data with any referenced
    37  // elements from imported packages into a single, self-contained
    38  // export data file.
    39  type linker struct {
    40  	pw	pkgbits.PkgEncoder
    41  
    42  	pkgs	map[string]pkgbits.Index
    43  	decls	map[*types.Sym]pkgbits.Index
    44  	bodies	map[*types.Sym]pkgbits.Index
    45  }
    46  
    47  // relocAll ensures that all elements specified by pr and relocs are
    48  // copied into the output export data file, and returns the
    49  // corresponding indices in the output.
    50  func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.RelocEnt {
    51  	res := make([]pkgbits.RelocEnt, len(relocs))
    52  	for i, rent := range relocs {
    53  		rent.Idx = l.relocIdx(pr, rent.Kind, rent.Idx)
    54  		res[i] = rent
    55  	}
    56  	return res
    57  }
    58  
    59  // relocIdx ensures a single element is copied into the output export
    60  // data file, and returns the corresponding index in the output.
    61  func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx pkgbits.Index) pkgbits.Index {
    62  	assert(pr != nil)
    63  
    64  	absIdx := pr.AbsIdx(k, idx)
    65  
    66  	if newidx := pr.newindex[absIdx]; newidx != 0 {
    67  		return ^newidx
    68  	}
    69  
    70  	var newidx pkgbits.Index
    71  	switch k {
    72  	case pkgbits.RelocString:
    73  		newidx = l.relocString(pr, idx)
    74  	case pkgbits.RelocPkg:
    75  		newidx = l.relocPkg(pr, idx)
    76  	case pkgbits.RelocObj:
    77  		newidx = l.relocObj(pr, idx)
    78  
    79  	default:
    80  		// Generic relocations.
    81  		//
    82  		// TODO(mdempsky): Deduplicate more sections? In fact, I think
    83  		// every section could be deduplicated. This would also be easier
    84  		// if we do external relocations.
    85  
    86  		w := l.pw.NewEncoderRaw(k)
    87  		l.relocCommon(pr, &w, k, idx)
    88  		newidx = w.Idx
    89  	}
    90  
    91  	pr.newindex[absIdx] = ^newidx
    92  
    93  	return newidx
    94  }
    95  
    96  // relocString copies the specified string from pr into the output
    97  // export data file, deduplicating it against other strings.
    98  func (l *linker) relocString(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
    99  	return l.pw.StringIdx(pr.StringIdx(idx))
   100  }
   101  
   102  // relocPkg copies the specified package from pr into the output
   103  // export data file, rewriting its import path to match how it was
   104  // imported.
   105  //
   106  // TODO(mdempsky): Since CL 391014, we already have the compilation
   107  // unit's import path, so there should be no need to rewrite packages
   108  // anymore.
   109  func (l *linker) relocPkg(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
   110  	path := pr.PeekPkgPath(idx)
   111  
   112  	if newidx, ok := l.pkgs[path]; ok {
   113  		return newidx
   114  	}
   115  
   116  	r := pr.NewDecoder(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef)
   117  	w := l.pw.NewEncoder(pkgbits.RelocPkg, pkgbits.SyncPkgDef)
   118  	l.pkgs[path] = w.Idx
   119  
   120  	// TODO(mdempsky): We end up leaving an empty string reference here
   121  	// from when the package was originally written as "". Probably not
   122  	// a big deal, but a little annoying. Maybe relocating
   123  	// cross-references in place is the way to go after all.
   124  	w.Relocs = l.relocAll(pr, r.Relocs)
   125  
   126  	_ = r.String()	// original path
   127  	w.String(path)
   128  
   129  	io.Copy(&w.Data, &r.Data)
   130  
   131  	return w.Flush()
   132  }
   133  
   134  // relocObj copies the specified object from pr into the output export
   135  // data file, rewriting its compiler-private extension data (e.g.,
   136  // adding inlining cost and escape analysis results for functions).
   137  func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
   138  	path, name, tag := pr.PeekObj(idx)
   139  	sym := types.NewPkg(path, "").Lookup(name)
   140  
   141  	if newidx, ok := l.decls[sym]; ok {
   142  		return newidx
   143  	}
   144  
   145  	if tag == pkgbits.ObjStub && path != "builtin" && path != "unsafe" {
   146  		pri, ok := objReader[sym]
   147  		if !ok {
   148  			base.Fatalf("missing reader for %q.%v", path, name)
   149  		}
   150  		assert(ok)
   151  
   152  		pr = pri.pr
   153  		idx = pri.idx
   154  
   155  		path2, name2, tag2 := pr.PeekObj(idx)
   156  		sym2 := types.NewPkg(path2, "").Lookup(name2)
   157  		assert(sym == sym2)
   158  		assert(tag2 != pkgbits.ObjStub)
   159  	}
   160  
   161  	w := l.pw.NewEncoderRaw(pkgbits.RelocObj)
   162  	wext := l.pw.NewEncoderRaw(pkgbits.RelocObjExt)
   163  	wname := l.pw.NewEncoderRaw(pkgbits.RelocName)
   164  	wdict := l.pw.NewEncoderRaw(pkgbits.RelocObjDict)
   165  
   166  	l.decls[sym] = w.Idx
   167  	assert(wext.Idx == w.Idx)
   168  	assert(wname.Idx == w.Idx)
   169  	assert(wdict.Idx == w.Idx)
   170  
   171  	l.relocCommon(pr, &w, pkgbits.RelocObj, idx)
   172  	l.relocCommon(pr, &wname, pkgbits.RelocName, idx)
   173  	l.relocCommon(pr, &wdict, pkgbits.RelocObjDict, idx)
   174  
   175  	// Generic types and functions won't have definitions, and imported
   176  	// objects may not either.
   177  	obj, _ := sym.Def.(*ir.Name)
   178  	local := sym.Pkg == types.LocalPkg
   179  
   180  	if local && obj != nil {
   181  		wext.Sync(pkgbits.SyncObject1)
   182  		switch tag {
   183  		case pkgbits.ObjFunc:
   184  			l.relocFuncExt(&wext, obj)
   185  		case pkgbits.ObjType:
   186  			l.relocTypeExt(&wext, obj)
   187  		case pkgbits.ObjVar:
   188  			l.relocVarExt(&wext, obj)
   189  		}
   190  		wext.Flush()
   191  	} else {
   192  		l.relocCommon(pr, &wext, pkgbits.RelocObjExt, idx)
   193  	}
   194  
   195  	// Check if we need to export the inline bodies for functions and
   196  	// methods.
   197  	if obj != nil {
   198  		if obj.Op() == ir.ONAME && obj.Class == ir.PFUNC {
   199  			l.exportBody(obj, local)
   200  		}
   201  
   202  		if obj.Op() == ir.OTYPE && !obj.Alias() {
   203  			if typ := obj.Type(); !typ.IsInterface() {
   204  				for _, method := range typ.Methods() {
   205  					l.exportBody(method.Nname.(*ir.Name), local)
   206  				}
   207  			}
   208  		}
   209  	}
   210  
   211  	return w.Idx
   212  }
   213  
   214  // exportBody exports the given function or method's body, if
   215  // appropriate. local indicates whether it's a local function or
   216  // method available on a locally declared type. (Due to cross-package
   217  // type aliases, a method may be imported, but still available on a
   218  // locally declared type.)
   219  func (l *linker) exportBody(obj *ir.Name, local bool) {
   220  	assert(obj.Op() == ir.ONAME && obj.Class == ir.PFUNC)
   221  
   222  	fn := obj.Func
   223  	if fn.Inl == nil {
   224  		return	// not inlinable anyway
   225  	}
   226  
   227  	// As a simple heuristic, if the function was declared in this
   228  	// package or we inlined it somewhere in this package, then we'll
   229  	// (re)export the function body. This isn't perfect, but seems
   230  	// reasonable in practice. In particular, it has the nice property
   231  	// that in the worst case, adding a blank import ensures the
   232  	// function body is available for inlining.
   233  	//
   234  	// TODO(mdempsky): Reimplement the reachable method crawling logic
   235  	// from typecheck/crawler.go.
   236  	exportBody := local || fn.Inl.HaveDcl
   237  	if !exportBody {
   238  		return
   239  	}
   240  
   241  	sym := obj.Sym()
   242  	if _, ok := l.bodies[sym]; ok {
   243  		// Due to type aliases, we might visit methods multiple times.
   244  		base.AssertfAt(obj.Type().Recv() != nil, obj.Pos(), "expected method: %v", obj)
   245  		return
   246  	}
   247  
   248  	pri, ok := bodyReaderFor(fn)
   249  	assert(ok)
   250  	l.bodies[sym] = l.relocIdx(pri.pr, pkgbits.RelocBody, pri.idx)
   251  }
   252  
   253  // relocCommon copies the specified element from pr into w,
   254  // recursively relocating any referenced elements as well.
   255  func (l *linker) relocCommon(pr *pkgReader, w *pkgbits.Encoder, k pkgbits.RelocKind, idx pkgbits.Index) {
   256  	r := pr.NewDecoderRaw(k, idx)
   257  	w.Relocs = l.relocAll(pr, r.Relocs)
   258  	io.Copy(&w.Data, &r.Data)
   259  	w.Flush()
   260  }
   261  
   262  func (l *linker) pragmaFlag(w *pkgbits.Encoder, pragma ir.PragmaFlag) {
   263  	w.Sync(pkgbits.SyncPragma)
   264  	w.Int(int(pragma))
   265  }
   266  
   267  func (l *linker) relocFuncExt(w *pkgbits.Encoder, name *ir.Name) {
   268  	w.Sync(pkgbits.SyncFuncExt)
   269  
   270  	l.pragmaFlag(w, name.Func.Pragma)
   271  	l.linkname(w, name)
   272  
   273  	if buildcfg.GOARCH == "wasm" {
   274  		if name.Func.WasmImport != nil {
   275  			w.String(name.Func.WasmImport.Module)
   276  			w.String(name.Func.WasmImport.Name)
   277  		} else {
   278  			w.String("")
   279  			w.String("")
   280  		}
   281  	}
   282  
   283  	// Relocated extension data.
   284  	w.Bool(true)
   285  
   286  	// Record definition ABI so cross-ABI calls can be direct.
   287  	// This is important for the performance of calling some
   288  	// common functions implemented in assembly (e.g., bytealg).
   289  	w.Uint64(uint64(name.Func.ABI))
   290  
   291  	// Escape analysis.
   292  	for _, f := range name.Type().RecvParams() {
   293  		w.String(f.Note)
   294  	}
   295  
   296  	if inl := name.Func.Inl; w.Bool(inl != nil) {
   297  		w.Len(int(inl.Cost))
   298  		w.Bool(inl.CanDelayResults)
   299  		if buildcfg.Experiment.NewInliner {
   300  			w.String(inl.Properties)
   301  		}
   302  	}
   303  
   304  	w.Sync(pkgbits.SyncEOF)
   305  }
   306  
   307  func (l *linker) relocTypeExt(w *pkgbits.Encoder, name *ir.Name) {
   308  	w.Sync(pkgbits.SyncTypeExt)
   309  
   310  	typ := name.Type()
   311  
   312  	l.pragmaFlag(w, name.Pragma())
   313  
   314  	// For type T, export the index of type descriptor symbols of T and *T.
   315  	l.lsymIdx(w, "", reflectdata.TypeLinksym(typ))
   316  	l.lsymIdx(w, "", reflectdata.TypeLinksym(typ.PtrTo()))
   317  
   318  	if typ.Kind() != types.TINTER {
   319  		for _, method := range typ.Methods() {
   320  			l.relocFuncExt(w, method.Nname.(*ir.Name))
   321  		}
   322  	}
   323  }
   324  
   325  func (l *linker) relocVarExt(w *pkgbits.Encoder, name *ir.Name) {
   326  	w.Sync(pkgbits.SyncVarExt)
   327  	l.linkname(w, name)
   328  }
   329  
   330  func (l *linker) linkname(w *pkgbits.Encoder, name *ir.Name) {
   331  	w.Sync(pkgbits.SyncLinkname)
   332  
   333  	linkname := name.Sym().Linkname
   334  	if !l.lsymIdx(w, linkname, name.Linksym()) {
   335  		w.String(linkname)
   336  	}
   337  }
   338  
   339  func (l *linker) lsymIdx(w *pkgbits.Encoder, linkname string, lsym *obj.LSym) bool {
   340  	if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || linkname != "" {
   341  		w.Int64(-1)
   342  		return false
   343  	}
   344  
   345  	// For a defined symbol, export its index.
   346  	// For re-exporting an imported symbol, pass its index through.
   347  	w.Int64(int64(lsym.SymIdx))
   348  	return true
   349  }