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
7package noder
8
9import (
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
32type linker struct {
33	pw pkgEncoder
34
35	pkgs  map[string]int
36	decls map[*types.Sym]int
37}
38
39func (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
48func (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
83func (l *linker) relocString(pr *pkgReader, idx int) int {
84	return l.pw.stringIdx(pr.stringIdx(idx))
85}
86
87func (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
112func (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
182func (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
189func (l *linker) pragmaFlag(w *encoder, pragma ir.PragmaFlag) {
190	w.sync(syncPragma)
191	w.int(int(pragma))
192}
193
194func (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
227func (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
245func (l *linker) relocVarExt(w *encoder, name *ir.Name) {
246	w.sync(syncVarExt)
247	l.linkname(w, name)
248}
249
250func (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
259func (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
276func (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
285func (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