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
5package noder
6
7import (
8	"internal/buildcfg"
9	"internal/pkgbits"
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// 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.
39type 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.
50func (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.
61func (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.
98func (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.
109func (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).
137func (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.)
219func (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.
255func (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
262func (l *linker) pragmaFlag(w *pkgbits.Encoder, pragma ir.PragmaFlag) {
263	w.Sync(pkgbits.SyncPragma)
264	w.Int(int(pragma))
265}
266
267func (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
307func (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
325func (l *linker) relocVarExt(w *pkgbits.Encoder, name *ir.Name) {
326	w.Sync(pkgbits.SyncVarExt)
327	l.linkname(w, name)
328}
329
330func (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
339func (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}
350