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