Source file src/cmd/compile/internal/noder/linker.go

     1  // UNREVIEWED
     2  
     3  // Copyright 2021 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  package noder
     8  
     9  import (
    10  	"io"
    11  
    12  	"cmd/compile/internal/base"
    13  	"cmd/compile/internal/ir"
    14  	"cmd/compile/internal/reflectdata"
    15  	"cmd/compile/internal/types"
    16  	"cmd/internal/goobj"
    17  	"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  type linker struct {
    33  	pw pkgEncoder
    34  
    35  	pkgs  map[string]int
    36  	decls map[*types.Sym]int
    37  }
    38  
    39  func (l *linker) relocAll(pr *pkgReader, relocs []relocEnt) []relocEnt {
    40  	res := make([]relocEnt, len(relocs))
    41  	for i, rent := range relocs {
    42  		rent.idx = l.relocIdx(pr, rent.kind, rent.idx)
    43  		res[i] = rent
    44  	}
    45  	return res
    46  }
    47  
    48  func (l *linker) relocIdx(pr *pkgReader, k reloc, idx int) int {
    49  	assert(pr != nil)
    50  
    51  	absIdx := pr.absIdx(k, idx)
    52  
    53  	if newidx := pr.newindex[absIdx]; newidx != 0 {
    54  		return ^newidx
    55  	}
    56  
    57  	var newidx int
    58  	switch k {
    59  	case relocString:
    60  		newidx = l.relocString(pr, idx)
    61  	case relocPkg:
    62  		newidx = l.relocPkg(pr, idx)
    63  	case relocObj:
    64  		newidx = l.relocObj(pr, idx)
    65  
    66  	default:
    67  		// Generic relocations.
    68  		//
    69  		// TODO(mdempsky): Deduplicate more sections? In fact, I think
    70  		// every section could be deduplicated. This would also be easier
    71  		// if we do external relocations.
    72  
    73  		w := l.pw.newEncoderRaw(k)
    74  		l.relocCommon(pr, &w, k, idx)
    75  		newidx = w.idx
    76  	}
    77  
    78  	pr.newindex[absIdx] = ^newidx
    79  
    80  	return newidx
    81  }
    82  
    83  func (l *linker) relocString(pr *pkgReader, idx int) int {
    84  	return l.pw.stringIdx(pr.stringIdx(idx))
    85  }
    86  
    87  func (l *linker) relocPkg(pr *pkgReader, idx int) int {
    88  	path := pr.peekPkgPath(idx)
    89  
    90  	if newidx, ok := l.pkgs[path]; ok {
    91  		return newidx
    92  	}
    93  
    94  	r := pr.newDecoder(relocPkg, idx, syncPkgDef)
    95  	w := l.pw.newEncoder(relocPkg, syncPkgDef)
    96  	l.pkgs[path] = w.idx
    97  
    98  	// TODO(mdempsky): We end up leaving an empty string reference here
    99  	// from when the package was originally written as "". Probably not
   100  	// a big deal, but a little annoying. Maybe relocating
   101  	// cross-references in place is the way to go after all.
   102  	w.relocs = l.relocAll(pr, r.relocs)
   103  
   104  	_ = r.string() // original path
   105  	w.string(path)
   106  
   107  	io.Copy(&w.data, &r.data)
   108  
   109  	return w.flush()
   110  }
   111  
   112  func (l *linker) relocObj(pr *pkgReader, idx int) int {
   113  	path, name, tag := pr.peekObj(idx)
   114  	sym := types.NewPkg(path, "").Lookup(name)
   115  
   116  	if newidx, ok := l.decls[sym]; ok {
   117  		return newidx
   118  	}
   119  
   120  	if tag == objStub && path != "builtin" && path != "unsafe" {
   121  		pri, ok := objReader[sym]
   122  		if !ok {
   123  			base.Fatalf("missing reader for %q.%v", path, name)
   124  		}
   125  		assert(ok)
   126  
   127  		pr = pri.pr
   128  		idx = pri.idx
   129  
   130  		path2, name2, tag2 := pr.peekObj(idx)
   131  		sym2 := types.NewPkg(path2, "").Lookup(name2)
   132  		assert(sym == sym2)
   133  		assert(tag2 != objStub)
   134  	}
   135  
   136  	w := l.pw.newEncoderRaw(relocObj)
   137  	wext := l.pw.newEncoderRaw(relocObjExt)
   138  	wname := l.pw.newEncoderRaw(relocName)
   139  	wdict := l.pw.newEncoderRaw(relocObjDict)
   140  
   141  	l.decls[sym] = w.idx
   142  	assert(wext.idx == w.idx)
   143  	assert(wname.idx == w.idx)
   144  	assert(wdict.idx == w.idx)
   145  
   146  	l.relocCommon(pr, &w, relocObj, idx)
   147  	l.relocCommon(pr, &wname, relocName, idx)
   148  	l.relocCommon(pr, &wdict, relocObjDict, idx)
   149  
   150  	var obj *ir.Name
   151  	if path == "" {
   152  		var ok bool
   153  		obj, ok = sym.Def.(*ir.Name)
   154  
   155  		// Generic types and functions and declared constraint types won't
   156  		// have definitions.
   157  		// For now, just generically copy their extension data.
   158  		// TODO(mdempsky): Restore assertion.
   159  		if !ok && false {
   160  			base.Fatalf("missing definition for %v", sym)
   161  		}
   162  	}
   163  
   164  	if obj != nil {
   165  		wext.sync(syncObject1)
   166  		switch tag {
   167  		case objFunc:
   168  			l.relocFuncExt(&wext, obj)
   169  		case objType:
   170  			l.relocTypeExt(&wext, obj)
   171  		case objVar:
   172  			l.relocVarExt(&wext, obj)
   173  		}
   174  		wext.flush()
   175  	} else {
   176  		l.relocCommon(pr, &wext, relocObjExt, idx)
   177  	}
   178  
   179  	return w.idx
   180  }
   181  
   182  func (l *linker) relocCommon(pr *pkgReader, w *encoder, k reloc, idx int) {
   183  	r := pr.newDecoderRaw(k, idx)
   184  	w.relocs = l.relocAll(pr, r.relocs)
   185  	io.Copy(&w.data, &r.data)
   186  	w.flush()
   187  }
   188  
   189  func (l *linker) pragmaFlag(w *encoder, pragma ir.PragmaFlag) {
   190  	w.sync(syncPragma)
   191  	w.int(int(pragma))
   192  }
   193  
   194  func (l *linker) relocFuncExt(w *encoder, name *ir.Name) {
   195  	w.sync(syncFuncExt)
   196  
   197  	l.pragmaFlag(w, name.Func.Pragma)
   198  	l.linkname(w, name)
   199  
   200  	// Relocated extension data.
   201  	w.bool(true)
   202  
   203  	// Record definition ABI so cross-ABI calls can be direct.
   204  	// This is important for the performance of calling some
   205  	// common functions implemented in assembly (e.g., bytealg).
   206  	w.uint64(uint64(name.Func.ABI))
   207  
   208  	// Escape analysis.
   209  	for _, fs := range &types.RecvsParams {
   210  		for _, f := range fs(name.Type()).FieldSlice() {
   211  			w.string(f.Note)
   212  		}
   213  	}
   214  
   215  	if inl := name.Func.Inl; w.bool(inl != nil) {
   216  		w.len(int(inl.Cost))
   217  		w.bool(inl.CanDelayResults)
   218  
   219  		pri, ok := bodyReader[name.Func]
   220  		assert(ok)
   221  		w.reloc(relocBody, l.relocIdx(pri.pr, relocBody, pri.idx))
   222  	}
   223  
   224  	w.sync(syncEOF)
   225  }
   226  
   227  func (l *linker) relocTypeExt(w *encoder, name *ir.Name) {
   228  	w.sync(syncTypeExt)
   229  
   230  	typ := name.Type()
   231  
   232  	l.pragmaFlag(w, name.Pragma())
   233  
   234  	// For type T, export the index of type descriptor symbols of T and *T.
   235  	l.lsymIdx(w, "", reflectdata.TypeLinksym(typ))
   236  	l.lsymIdx(w, "", reflectdata.TypeLinksym(typ.PtrTo()))
   237  
   238  	if typ.Kind() != types.TINTER {
   239  		for _, method := range typ.Methods().Slice() {
   240  			l.relocFuncExt(w, method.Nname.(*ir.Name))
   241  		}
   242  	}
   243  }
   244  
   245  func (l *linker) relocVarExt(w *encoder, name *ir.Name) {
   246  	w.sync(syncVarExt)
   247  	l.linkname(w, name)
   248  }
   249  
   250  func (l *linker) linkname(w *encoder, name *ir.Name) {
   251  	w.sync(syncLinkname)
   252  
   253  	linkname := name.Sym().Linkname
   254  	if !l.lsymIdx(w, linkname, name.Linksym()) {
   255  		w.string(linkname)
   256  	}
   257  }
   258  
   259  func (l *linker) lsymIdx(w *encoder, linkname string, lsym *obj.LSym) bool {
   260  	if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || linkname != "" {
   261  		w.int64(-1)
   262  		return false
   263  	}
   264  
   265  	// For a defined symbol, export its index.
   266  	// For re-exporting an imported symbol, pass its index through.
   267  	w.int64(int64(lsym.SymIdx))
   268  	return true
   269  }
   270  
   271  // @@@ Helpers
   272  
   273  // TODO(mdempsky): These should probably be removed. I think they're a
   274  // smell that the export data format is not yet quite right.
   275  
   276  func (pr *pkgDecoder) peekPkgPath(idx int) string {
   277  	r := pr.newDecoder(relocPkg, idx, syncPkgDef)
   278  	path := r.string()
   279  	if path == "" {
   280  		path = pr.pkgPath
   281  	}
   282  	return path
   283  }
   284  
   285  func (pr *pkgDecoder) peekObj(idx int) (string, string, codeObj) {
   286  	r := pr.newDecoder(relocName, idx, syncObject1)
   287  	r.sync(syncSym)
   288  	r.sync(syncPkg)
   289  	path := pr.peekPkgPath(r.reloc(relocPkg))
   290  	name := r.string()
   291  	assert(name != "")
   292  
   293  	tag := codeObj(r.code(syncCodeObj))
   294  
   295  	return path, name, tag
   296  }
   297  

View as plain text