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	"fmt"
9	"internal/pkgbits"
10	"internal/types/errors"
11	"io"
12	"runtime"
13	"sort"
14	"strings"
15
16	"cmd/compile/internal/base"
17	"cmd/compile/internal/inline"
18	"cmd/compile/internal/ir"
19	"cmd/compile/internal/pgoir"
20	"cmd/compile/internal/typecheck"
21	"cmd/compile/internal/types"
22	"cmd/compile/internal/types2"
23	"cmd/internal/src"
24)
25
26// localPkgReader holds the package reader used for reading the local
27// package. It exists so the unified IR linker can refer back to it
28// later.
29var localPkgReader *pkgReader
30
31// LookupFunc returns the ir.Func for an arbitrary full symbol name if
32// that function exists in the set of available export data.
33//
34// This allows lookup of arbitrary functions and methods that aren't otherwise
35// referenced by the local package and thus haven't been read yet.
36//
37// TODO(prattmic): Does not handle instantiation of generic types. Currently
38// profiles don't contain the original type arguments, so we won't be able to
39// create the runtime dictionaries.
40//
41// TODO(prattmic): Hit rate of this function is usually fairly low, and errors
42// are only used when debug logging is enabled. Consider constructing cheaper
43// errors by default.
44func LookupFunc(fullName string) (*ir.Func, error) {
45	pkgPath, symName, err := ir.ParseLinkFuncName(fullName)
46	if err != nil {
47		return nil, fmt.Errorf("error parsing symbol name %q: %v", fullName, err)
48	}
49
50	pkg, ok := types.PkgMap()[pkgPath]
51	if !ok {
52		return nil, fmt.Errorf("pkg %s doesn't exist in %v", pkgPath, types.PkgMap())
53	}
54
55	// Symbol naming is ambiguous. We can't necessarily distinguish between
56	// a method and a closure. e.g., is foo.Bar.func1 a closure defined in
57	// function Bar, or a method on type Bar? Thus we must simply attempt
58	// to lookup both.
59
60	fn, err := lookupFunction(pkg, symName)
61	if err == nil {
62		return fn, nil
63	}
64
65	fn, mErr := lookupMethod(pkg, symName)
66	if mErr == nil {
67		return fn, nil
68	}
69
70	return nil, fmt.Errorf("%s is not a function (%v) or method (%v)", fullName, err, mErr)
71}
72
73// PostLookupCleanup performs cleanup operations needed
74// after a series of calls to LookupFunc, specifically invoking
75// readBodies to post-process any funcs on the "todoBodies" list
76// that were added as a result of the lookup operations.
77func PostLookupCleanup() {
78	readBodies(typecheck.Target, false)
79}
80
81func lookupFunction(pkg *types.Pkg, symName string) (*ir.Func, error) {
82	sym := pkg.Lookup(symName)
83
84	// TODO(prattmic): Enclosed functions (e.g., foo.Bar.func1) are not
85	// present in objReader, only as OCLOSURE nodes in the enclosing
86	// function.
87	pri, ok := objReader[sym]
88	if !ok {
89		return nil, fmt.Errorf("func sym %v missing objReader", sym)
90	}
91
92	node, err := pri.pr.objIdxMayFail(pri.idx, nil, nil, false)
93	if err != nil {
94		return nil, fmt.Errorf("func sym %v lookup error: %w", sym, err)
95	}
96	name := node.(*ir.Name)
97	if name.Op() != ir.ONAME || name.Class != ir.PFUNC {
98		return nil, fmt.Errorf("func sym %v refers to non-function name: %v", sym, name)
99	}
100	return name.Func, nil
101}
102
103func lookupMethod(pkg *types.Pkg, symName string) (*ir.Func, error) {
104	// N.B. readPackage creates a Sym for every object in the package to
105	// initialize objReader and importBodyReader, even if the object isn't
106	// read.
107	//
108	// However, objReader is only initialized for top-level objects, so we
109	// must first lookup the type and use that to find the method rather
110	// than looking for the method directly.
111	typ, meth, err := ir.LookupMethodSelector(pkg, symName)
112	if err != nil {
113		return nil, fmt.Errorf("error looking up method symbol %q: %v", symName, err)
114	}
115
116	pri, ok := objReader[typ]
117	if !ok {
118		return nil, fmt.Errorf("type sym %v missing objReader", typ)
119	}
120
121	node, err := pri.pr.objIdxMayFail(pri.idx, nil, nil, false)
122	if err != nil {
123		return nil, fmt.Errorf("func sym %v lookup error: %w", typ, err)
124	}
125	name := node.(*ir.Name)
126	if name.Op() != ir.OTYPE {
127		return nil, fmt.Errorf("type sym %v refers to non-type name: %v", typ, name)
128	}
129	if name.Alias() {
130		return nil, fmt.Errorf("type sym %v refers to alias", typ)
131	}
132	if name.Type().IsInterface() {
133		return nil, fmt.Errorf("type sym %v refers to interface type", typ)
134	}
135
136	for _, m := range name.Type().Methods() {
137		if m.Sym == meth {
138			fn := m.Nname.(*ir.Name).Func
139			return fn, nil
140		}
141	}
142
143	return nil, fmt.Errorf("method %s missing from method set of %v", symName, typ)
144}
145
146// unified constructs the local package's Internal Representation (IR)
147// from its syntax tree (AST).
148//
149// The pipeline contains 2 steps:
150//
151//  1. Generate the export data "stub".
152//
153//  2. Generate the IR from the export data above.
154//
155// The package data "stub" at step (1) contains everything from the local package,
156// but nothing that has been imported. When we're actually writing out export data
157// to the output files (see writeNewExport), we run the "linker", which:
158//
159//   - Updates compiler extensions data (e.g. inlining cost, escape analysis results).
160//
161//   - Handles re-exporting any transitive dependencies.
162//
163//   - Prunes out any unnecessary details (e.g. non-inlineable functions, because any
164//     downstream importers only care about inlinable functions).
165//
166// The source files are typechecked twice: once before writing the export data
167// using types2, and again after reading the export data using gc/typecheck.
168// The duplication of work will go away once we only use the types2 type checker,
169// removing the gc/typecheck step. For now, it is kept because:
170//
171//   - It reduces the engineering costs in maintaining a fork of typecheck
172//     (e.g. no need to backport fixes like CL 327651).
173//
174//   - It makes it easier to pass toolstash -cmp.
175//
176//   - Historically, we would always re-run the typechecker after importing a package,
177//     even though we know the imported data is valid. It's not ideal, but it's
178//     not causing any problems either.
179//
180//   - gc/typecheck is still in charge of some transformations, such as rewriting
181//     multi-valued function calls or transforming ir.OINDEX to ir.OINDEXMAP.
182//
183// Using the syntax tree with types2, which has a complete representation of generics,
184// the unified IR has the full typed AST needed for introspection during step (1).
185// In other words, we have all the necessary information to build the generic IR form
186// (see writer.captureVars for an example).
187func unified(m posMap, noders []*noder) {
188	inline.InlineCall = unifiedInlineCall
189	typecheck.HaveInlineBody = unifiedHaveInlineBody
190	pgoir.LookupFunc = LookupFunc
191	pgoir.PostLookupCleanup = PostLookupCleanup
192
193	data := writePkgStub(m, noders)
194
195	target := typecheck.Target
196
197	localPkgReader = newPkgReader(pkgbits.NewPkgDecoder(types.LocalPkg.Path, data))
198	readPackage(localPkgReader, types.LocalPkg, true)
199
200	r := localPkgReader.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate)
201	r.pkgInit(types.LocalPkg, target)
202
203	readBodies(target, false)
204
205	// Check that nothing snuck past typechecking.
206	for _, fn := range target.Funcs {
207		if fn.Typecheck() == 0 {
208			base.FatalfAt(fn.Pos(), "missed typecheck: %v", fn)
209		}
210
211		// For functions, check that at least their first statement (if
212		// any) was typechecked too.
213		if len(fn.Body) != 0 {
214			if stmt := fn.Body[0]; stmt.Typecheck() == 0 {
215				base.FatalfAt(stmt.Pos(), "missed typecheck: %v", stmt)
216			}
217		}
218	}
219
220	// For functions originally came from package runtime,
221	// mark as norace to prevent instrumenting, see issue #60439.
222	for _, fn := range target.Funcs {
223		if !base.Flag.CompilingRuntime && types.RuntimeSymName(fn.Sym()) != "" {
224			fn.Pragma |= ir.Norace
225		}
226	}
227
228	base.ExitIfErrors() // just in case
229}
230
231// readBodies iteratively expands all pending dictionaries and
232// function bodies.
233//
234// If duringInlining is true, then the inline.InlineDecls is called as
235// necessary on instantiations of imported generic functions, so their
236// inlining costs can be computed.
237func readBodies(target *ir.Package, duringInlining bool) {
238	var inlDecls []*ir.Func
239
240	// Don't use range--bodyIdx can add closures to todoBodies.
241	for {
242		// The order we expand dictionaries and bodies doesn't matter, so
243		// pop from the end to reduce todoBodies reallocations if it grows
244		// further.
245		//
246		// However, we do at least need to flush any pending dictionaries
247		// before reading bodies, because bodies might reference the
248		// dictionaries.
249
250		if len(todoDicts) > 0 {
251			fn := todoDicts[len(todoDicts)-1]
252			todoDicts = todoDicts[:len(todoDicts)-1]
253			fn()
254			continue
255		}
256
257		if len(todoBodies) > 0 {
258			fn := todoBodies[len(todoBodies)-1]
259			todoBodies = todoBodies[:len(todoBodies)-1]
260
261			pri, ok := bodyReader[fn]
262			assert(ok)
263			pri.funcBody(fn)
264
265			// Instantiated generic function: add to Decls for typechecking
266			// and compilation.
267			if fn.OClosure == nil && len(pri.dict.targs) != 0 {
268				// cmd/link does not support a type symbol referencing a method symbol
269				// across DSO boundary, so force re-compiling methods on a generic type
270				// even it was seen from imported package in linkshared mode, see #58966.
271				canSkipNonGenericMethod := !(base.Ctxt.Flag_linkshared && ir.IsMethod(fn))
272				if duringInlining && canSkipNonGenericMethod {
273					inlDecls = append(inlDecls, fn)
274				} else {
275					target.Funcs = append(target.Funcs, fn)
276				}
277			}
278
279			continue
280		}
281
282		break
283	}
284
285	todoDicts = nil
286	todoBodies = nil
287
288	if len(inlDecls) != 0 {
289		// If we instantiated any generic functions during inlining, we need
290		// to call CanInline on them so they'll be transitively inlined
291		// correctly (#56280).
292		//
293		// We know these functions were already compiled in an imported
294		// package though, so we don't need to actually apply InlineCalls or
295		// save the function bodies any further than this.
296		//
297		// We can also lower the -m flag to 0, to suppress duplicate "can
298		// inline" diagnostics reported against the imported package. Again,
299		// we already reported those diagnostics in the original package, so
300		// it's pointless repeating them here.
301
302		oldLowerM := base.Flag.LowerM
303		base.Flag.LowerM = 0
304		inline.CanInlineFuncs(inlDecls, nil)
305		base.Flag.LowerM = oldLowerM
306
307		for _, fn := range inlDecls {
308			fn.Body = nil // free memory
309		}
310	}
311}
312
313// writePkgStub type checks the given parsed source files,
314// writes an export data package stub representing them,
315// and returns the result.
316func writePkgStub(m posMap, noders []*noder) string {
317	pkg, info, otherInfo := checkFiles(m, noders)
318
319	pw := newPkgWriter(m, pkg, info, otherInfo)
320
321	pw.collectDecls(noders)
322
323	publicRootWriter := pw.newWriter(pkgbits.RelocMeta, pkgbits.SyncPublic)
324	privateRootWriter := pw.newWriter(pkgbits.RelocMeta, pkgbits.SyncPrivate)
325
326	assert(publicRootWriter.Idx == pkgbits.PublicRootIdx)
327	assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx)
328
329	{
330		w := publicRootWriter
331		w.pkg(pkg)
332		w.Bool(false) // TODO(mdempsky): Remove; was "has init"
333
334		scope := pkg.Scope()
335		names := scope.Names()
336		w.Len(len(names))
337		for _, name := range names {
338			w.obj(scope.Lookup(name), nil)
339		}
340
341		w.Sync(pkgbits.SyncEOF)
342		w.Flush()
343	}
344
345	{
346		w := privateRootWriter
347		w.pkgInit(noders)
348		w.Flush()
349	}
350
351	var sb strings.Builder
352	pw.DumpTo(&sb)
353
354	// At this point, we're done with types2. Make sure the package is
355	// garbage collected.
356	freePackage(pkg)
357
358	return sb.String()
359}
360
361// freePackage ensures the given package is garbage collected.
362func freePackage(pkg *types2.Package) {
363	// The GC test below relies on a precise GC that runs finalizers as
364	// soon as objects are unreachable. Our implementation provides
365	// this, but other/older implementations may not (e.g., Go 1.4 does
366	// not because of #22350). To avoid imposing unnecessary
367	// restrictions on the GOROOT_BOOTSTRAP toolchain, we skip the test
368	// during bootstrapping.
369	if base.CompilerBootstrap || base.Debug.GCCheck == 0 {
370		*pkg = types2.Package{}
371		return
372	}
373
374	// Set a finalizer on pkg so we can detect if/when it's collected.
375	done := make(chan struct{})
376	runtime.SetFinalizer(pkg, func(*types2.Package) { close(done) })
377
378	// Important: objects involved in cycles are not finalized, so zero
379	// out pkg to break its cycles and allow the finalizer to run.
380	*pkg = types2.Package{}
381
382	// It typically takes just 1 or 2 cycles to release pkg, but it
383	// doesn't hurt to try a few more times.
384	for i := 0; i < 10; i++ {
385		select {
386		case <-done:
387			return
388		default:
389			runtime.GC()
390		}
391	}
392
393	base.Fatalf("package never finalized")
394}
395
396// readPackage reads package export data from pr to populate
397// importpkg.
398//
399// localStub indicates whether pr is reading the stub export data for
400// the local package, as opposed to relocated export data for an
401// import.
402func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) {
403	{
404		r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
405
406		pkg := r.pkg()
407		if pkg != importpkg {
408			base.ErrorfAt(base.AutogeneratedPos, errors.BadImportPath, "mismatched import path, have %q (%p), want %q (%p)", pkg.Path, pkg, importpkg.Path, importpkg)
409			base.ErrorExit()
410		}
411
412		r.Bool() // TODO(mdempsky): Remove; was "has init"
413
414		for i, n := 0, r.Len(); i < n; i++ {
415			r.Sync(pkgbits.SyncObject)
416			assert(!r.Bool())
417			idx := r.Reloc(pkgbits.RelocObj)
418			assert(r.Len() == 0)
419
420			path, name, code := r.p.PeekObj(idx)
421			if code != pkgbits.ObjStub {
422				objReader[types.NewPkg(path, "").Lookup(name)] = pkgReaderIndex{pr, idx, nil, nil, nil}
423			}
424		}
425
426		r.Sync(pkgbits.SyncEOF)
427	}
428
429	if !localStub {
430		r := pr.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate)
431
432		if r.Bool() {
433			sym := importpkg.Lookup(".inittask")
434			task := ir.NewNameAt(src.NoXPos, sym, nil)
435			task.Class = ir.PEXTERN
436			sym.Def = task
437		}
438
439		for i, n := 0, r.Len(); i < n; i++ {
440			path := r.String()
441			name := r.String()
442			idx := r.Reloc(pkgbits.RelocBody)
443
444			sym := types.NewPkg(path, "").Lookup(name)
445			if _, ok := importBodyReader[sym]; !ok {
446				importBodyReader[sym] = pkgReaderIndex{pr, idx, nil, nil, nil}
447			}
448		}
449
450		r.Sync(pkgbits.SyncEOF)
451	}
452}
453
454// writeUnifiedExport writes to `out` the finalized, self-contained
455// Unified IR export data file for the current compilation unit.
456func writeUnifiedExport(out io.Writer) {
457	l := linker{
458		pw: pkgbits.NewPkgEncoder(base.Debug.SyncFrames),
459
460		pkgs:   make(map[string]pkgbits.Index),
461		decls:  make(map[*types.Sym]pkgbits.Index),
462		bodies: make(map[*types.Sym]pkgbits.Index),
463	}
464
465	publicRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPublic)
466	privateRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPrivate)
467	assert(publicRootWriter.Idx == pkgbits.PublicRootIdx)
468	assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx)
469
470	var selfPkgIdx pkgbits.Index
471
472	{
473		pr := localPkgReader
474		r := pr.NewDecoder(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
475
476		r.Sync(pkgbits.SyncPkg)
477		selfPkgIdx = l.relocIdx(pr, pkgbits.RelocPkg, r.Reloc(pkgbits.RelocPkg))
478
479		r.Bool() // TODO(mdempsky): Remove; was "has init"
480
481		for i, n := 0, r.Len(); i < n; i++ {
482			r.Sync(pkgbits.SyncObject)
483			assert(!r.Bool())
484			idx := r.Reloc(pkgbits.RelocObj)
485			assert(r.Len() == 0)
486
487			xpath, xname, xtag := pr.PeekObj(idx)
488			assert(xpath == pr.PkgPath())
489			assert(xtag != pkgbits.ObjStub)
490
491			if types.IsExported(xname) {
492				l.relocIdx(pr, pkgbits.RelocObj, idx)
493			}
494		}
495
496		r.Sync(pkgbits.SyncEOF)
497	}
498
499	{
500		var idxs []pkgbits.Index
501		for _, idx := range l.decls {
502			idxs = append(idxs, idx)
503		}
504		sort.Slice(idxs, func(i, j int) bool { return idxs[i] < idxs[j] })
505
506		w := publicRootWriter
507
508		w.Sync(pkgbits.SyncPkg)
509		w.Reloc(pkgbits.RelocPkg, selfPkgIdx)
510		w.Bool(false) // TODO(mdempsky): Remove; was "has init"
511
512		w.Len(len(idxs))
513		for _, idx := range idxs {
514			w.Sync(pkgbits.SyncObject)
515			w.Bool(false)
516			w.Reloc(pkgbits.RelocObj, idx)
517			w.Len(0)
518		}
519
520		w.Sync(pkgbits.SyncEOF)
521		w.Flush()
522	}
523
524	{
525		type symIdx struct {
526			sym *types.Sym
527			idx pkgbits.Index
528		}
529		var bodies []symIdx
530		for sym, idx := range l.bodies {
531			bodies = append(bodies, symIdx{sym, idx})
532		}
533		sort.Slice(bodies, func(i, j int) bool { return bodies[i].idx < bodies[j].idx })
534
535		w := privateRootWriter
536
537		w.Bool(typecheck.Lookup(".inittask").Def != nil)
538
539		w.Len(len(bodies))
540		for _, body := range bodies {
541			w.String(body.sym.Pkg.Path)
542			w.String(body.sym.Name)
543			w.Reloc(pkgbits.RelocBody, body.idx)
544		}
545
546		w.Sync(pkgbits.SyncEOF)
547		w.Flush()
548	}
549
550	base.Ctxt.Fingerprint = l.pw.DumpTo(out)
551}
552