]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/noder/unified.go
cmd/compile: use HaveInlineBody for unified IR
[gostls13.git] / src / cmd / compile / internal / noder / unified.go
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         "fmt"
9         "internal/goversion"
10         "internal/pkgbits"
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/typecheck"
20         "cmd/compile/internal/types"
21         "cmd/compile/internal/types2"
22         "cmd/internal/src"
23 )
24
25 // localPkgReader holds the package reader used for reading the local
26 // package. It exists so the unified IR linker can refer back to it
27 // later.
28 var localPkgReader *pkgReader
29
30 // unified constructs the local package's Internal Representation (IR)
31 // from its syntax tree (AST).
32 //
33 // The pipeline contains 2 steps:
34 //
35 //  1. Generate the export data "stub".
36 //
37 //  2. Generate the IR from the export data above.
38 //
39 // The package data "stub" at step (1) contains everything from the local package,
40 // but nothing that has been imported. When we're actually writing out export data
41 // to the output files (see writeNewExport), we run the "linker", which:
42 //
43 //   - Updates compiler extensions data (e.g. inlining cost, escape analysis results).
44 //
45 //   - Handles re-exporting any transitive dependencies.
46 //
47 //   - Prunes out any unnecessary details (e.g. non-inlineable functions, because any
48 //     downstream importers only care about inlinable functions).
49 //
50 // The source files are typechecked twice: once before writing the export data
51 // using types2, and again after reading the export data using gc/typecheck.
52 // The duplication of work will go away once we only use the types2 type checker,
53 // removing the gc/typecheck step. For now, it is kept because:
54 //
55 //   - It reduces the engineering costs in maintaining a fork of typecheck
56 //     (e.g. no need to backport fixes like CL 327651).
57 //
58 //   - It makes it easier to pass toolstash -cmp.
59 //
60 //   - Historically, we would always re-run the typechecker after importing a package,
61 //     even though we know the imported data is valid. It's not ideal, but it's
62 //     not causing any problems either.
63 //
64 //   - gc/typecheck is still in charge of some transformations, such as rewriting
65 //     multi-valued function calls or transforming ir.OINDEX to ir.OINDEXMAP.
66 //
67 // Using the syntax tree with types2, which has a complete representation of generics,
68 // the unified IR has the full typed AST needed for introspection during step (1).
69 // In other words, we have all the necessary information to build the generic IR form
70 // (see writer.captureVars for an example).
71 func unified(noders []*noder) {
72         inline.InlineCall = unifiedInlineCall
73         typecheck.HaveInlineBody = unifiedHaveInlineBody
74
75         data := writePkgStub(noders)
76
77         // We already passed base.Flag.Lang to types2 to handle validating
78         // the user's source code. Bump it up now to the current version and
79         // re-parse, so typecheck doesn't complain if we construct IR that
80         // utilizes newer Go features.
81         base.Flag.Lang = fmt.Sprintf("go1.%d", goversion.Version)
82         types.ParseLangFlag()
83
84         target := typecheck.Target
85
86         typecheck.TypecheckAllowed = true
87
88         localPkgReader = newPkgReader(pkgbits.NewPkgDecoder(types.LocalPkg.Path, data))
89         readPackage(localPkgReader, types.LocalPkg, true)
90
91         r := localPkgReader.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate)
92         r.pkgInit(types.LocalPkg, target)
93
94         // Type-check any top-level assignments. We ignore non-assignments
95         // here because other declarations are typechecked as they're
96         // constructed.
97         for i, ndecls := 0, len(target.Decls); i < ndecls; i++ {
98                 switch n := target.Decls[i]; n.Op() {
99                 case ir.OAS, ir.OAS2:
100                         target.Decls[i] = typecheck.Stmt(n)
101                 }
102         }
103
104         readBodies(target)
105
106         // Check that nothing snuck past typechecking.
107         for _, n := range target.Decls {
108                 if n.Typecheck() == 0 {
109                         base.FatalfAt(n.Pos(), "missed typecheck: %v", n)
110                 }
111
112                 // For functions, check that at least their first statement (if
113                 // any) was typechecked too.
114                 if fn, ok := n.(*ir.Func); ok && len(fn.Body) != 0 {
115                         if stmt := fn.Body[0]; stmt.Typecheck() == 0 {
116                                 base.FatalfAt(stmt.Pos(), "missed typecheck: %v", stmt)
117                         }
118                 }
119         }
120
121         base.ExitIfErrors() // just in case
122 }
123
124 // readBodies iteratively expands all pending dictionaries and
125 // function bodies.
126 func readBodies(target *ir.Package) {
127         // Don't use range--bodyIdx can add closures to todoBodies.
128         for {
129                 // The order we expand dictionaries and bodies doesn't matter, so
130                 // pop from the end to reduce todoBodies reallocations if it grows
131                 // further.
132                 //
133                 // However, we do at least need to flush any pending dictionaries
134                 // before reading bodies, because bodies might reference the
135                 // dictionaries.
136
137                 if len(todoDicts) > 0 {
138                         fn := todoDicts[len(todoDicts)-1]
139                         todoDicts = todoDicts[:len(todoDicts)-1]
140                         fn()
141                         continue
142                 }
143
144                 if len(todoBodies) > 0 {
145                         fn := todoBodies[len(todoBodies)-1]
146                         todoBodies = todoBodies[:len(todoBodies)-1]
147
148                         pri, ok := bodyReader[fn]
149                         assert(ok)
150                         pri.funcBody(fn)
151
152                         // Instantiated generic function: add to Decls for typechecking
153                         // and compilation.
154                         if fn.OClosure == nil && len(pri.dict.targs) != 0 {
155                                 target.Decls = append(target.Decls, fn)
156                         }
157
158                         continue
159                 }
160
161                 break
162         }
163
164         todoDicts = nil
165         todoBodies = nil
166 }
167
168 // writePkgStub type checks the given parsed source files,
169 // writes an export data package stub representing them,
170 // and returns the result.
171 func writePkgStub(noders []*noder) string {
172         m, pkg, info := checkFiles(noders)
173
174         pw := newPkgWriter(m, pkg, info)
175
176         pw.collectDecls(noders)
177
178         publicRootWriter := pw.newWriter(pkgbits.RelocMeta, pkgbits.SyncPublic)
179         privateRootWriter := pw.newWriter(pkgbits.RelocMeta, pkgbits.SyncPrivate)
180
181         assert(publicRootWriter.Idx == pkgbits.PublicRootIdx)
182         assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx)
183
184         {
185                 w := publicRootWriter
186                 w.pkg(pkg)
187                 w.Bool(false) // TODO(mdempsky): Remove; was "has init"
188
189                 scope := pkg.Scope()
190                 names := scope.Names()
191                 w.Len(len(names))
192                 for _, name := range names {
193                         w.obj(scope.Lookup(name), nil)
194                 }
195
196                 w.Sync(pkgbits.SyncEOF)
197                 w.Flush()
198         }
199
200         {
201                 w := privateRootWriter
202                 w.pkgInit(noders)
203                 w.Flush()
204         }
205
206         var sb strings.Builder
207         pw.DumpTo(&sb)
208
209         // At this point, we're done with types2. Make sure the package is
210         // garbage collected.
211         freePackage(pkg)
212
213         return sb.String()
214 }
215
216 // freePackage ensures the given package is garbage collected.
217 func freePackage(pkg *types2.Package) {
218         // The GC test below relies on a precise GC that runs finalizers as
219         // soon as objects are unreachable. Our implementation provides
220         // this, but other/older implementations may not (e.g., Go 1.4 does
221         // not because of #22350). To avoid imposing unnecessary
222         // restrictions on the GOROOT_BOOTSTRAP toolchain, we skip the test
223         // during bootstrapping.
224         if base.CompilerBootstrap {
225                 return
226         }
227
228         // Set a finalizer on pkg so we can detect if/when it's collected.
229         done := make(chan struct{})
230         runtime.SetFinalizer(pkg, func(*types2.Package) { close(done) })
231
232         // Important: objects involved in cycles are not finalized, so zero
233         // out pkg to break its cycles and allow the finalizer to run.
234         *pkg = types2.Package{}
235
236         // It typically takes just 1 or 2 cycles to release pkg, but it
237         // doesn't hurt to try a few more times.
238         for i := 0; i < 10; i++ {
239                 select {
240                 case <-done:
241                         return
242                 default:
243                         runtime.GC()
244                 }
245         }
246
247         base.Fatalf("package never finalized")
248 }
249
250 // readPackage reads package export data from pr to populate
251 // importpkg.
252 //
253 // localStub indicates whether pr is reading the stub export data for
254 // the local package, as opposed to relocated export data for an
255 // import.
256 func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) {
257         {
258                 r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
259
260                 pkg := r.pkg()
261                 base.Assertf(pkg == importpkg, "have package %q (%p), want package %q (%p)", pkg.Path, pkg, importpkg.Path, importpkg)
262
263                 r.Bool() // TODO(mdempsky): Remove; was "has init"
264
265                 for i, n := 0, r.Len(); i < n; i++ {
266                         r.Sync(pkgbits.SyncObject)
267                         assert(!r.Bool())
268                         idx := r.Reloc(pkgbits.RelocObj)
269                         assert(r.Len() == 0)
270
271                         path, name, code := r.p.PeekObj(idx)
272                         if code != pkgbits.ObjStub {
273                                 objReader[types.NewPkg(path, "").Lookup(name)] = pkgReaderIndex{pr, idx, nil, nil, nil}
274                         }
275                 }
276
277                 r.Sync(pkgbits.SyncEOF)
278         }
279
280         if !localStub {
281                 r := pr.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate)
282
283                 if r.Bool() {
284                         sym := importpkg.Lookup(".inittask")
285                         task := ir.NewNameAt(src.NoXPos, sym)
286                         task.Class = ir.PEXTERN
287                         sym.Def = task
288                 }
289
290                 for i, n := 0, r.Len(); i < n; i++ {
291                         path := r.String()
292                         name := r.String()
293                         idx := r.Reloc(pkgbits.RelocBody)
294
295                         sym := types.NewPkg(path, "").Lookup(name)
296                         if _, ok := importBodyReader[sym]; !ok {
297                                 importBodyReader[sym] = pkgReaderIndex{pr, idx, nil, nil, nil}
298                         }
299                 }
300
301                 r.Sync(pkgbits.SyncEOF)
302         }
303 }
304
305 // writeUnifiedExport writes to `out` the finalized, self-contained
306 // Unified IR export data file for the current compilation unit.
307 func writeUnifiedExport(out io.Writer) {
308         l := linker{
309                 pw: pkgbits.NewPkgEncoder(base.Debug.SyncFrames),
310
311                 pkgs:   make(map[string]pkgbits.Index),
312                 decls:  make(map[*types.Sym]pkgbits.Index),
313                 bodies: make(map[*types.Sym]pkgbits.Index),
314         }
315
316         publicRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPublic)
317         privateRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPrivate)
318         assert(publicRootWriter.Idx == pkgbits.PublicRootIdx)
319         assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx)
320
321         var selfPkgIdx pkgbits.Index
322
323         {
324                 pr := localPkgReader
325                 r := pr.NewDecoder(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
326
327                 r.Sync(pkgbits.SyncPkg)
328                 selfPkgIdx = l.relocIdx(pr, pkgbits.RelocPkg, r.Reloc(pkgbits.RelocPkg))
329
330                 r.Bool() // TODO(mdempsky): Remove; was "has init"
331
332                 for i, n := 0, r.Len(); i < n; i++ {
333                         r.Sync(pkgbits.SyncObject)
334                         assert(!r.Bool())
335                         idx := r.Reloc(pkgbits.RelocObj)
336                         assert(r.Len() == 0)
337
338                         xpath, xname, xtag := pr.PeekObj(idx)
339                         assert(xpath == pr.PkgPath())
340                         assert(xtag != pkgbits.ObjStub)
341
342                         if types.IsExported(xname) {
343                                 l.relocIdx(pr, pkgbits.RelocObj, idx)
344                         }
345                 }
346
347                 r.Sync(pkgbits.SyncEOF)
348         }
349
350         {
351                 var idxs []pkgbits.Index
352                 for _, idx := range l.decls {
353                         idxs = append(idxs, idx)
354                 }
355                 sort.Slice(idxs, func(i, j int) bool { return idxs[i] < idxs[j] })
356
357                 w := publicRootWriter
358
359                 w.Sync(pkgbits.SyncPkg)
360                 w.Reloc(pkgbits.RelocPkg, selfPkgIdx)
361                 w.Bool(false) // TODO(mdempsky): Remove; was "has init"
362
363                 w.Len(len(idxs))
364                 for _, idx := range idxs {
365                         w.Sync(pkgbits.SyncObject)
366                         w.Bool(false)
367                         w.Reloc(pkgbits.RelocObj, idx)
368                         w.Len(0)
369                 }
370
371                 w.Sync(pkgbits.SyncEOF)
372                 w.Flush()
373         }
374
375         {
376                 type symIdx struct {
377                         sym *types.Sym
378                         idx pkgbits.Index
379                 }
380                 var bodies []symIdx
381                 for sym, idx := range l.bodies {
382                         bodies = append(bodies, symIdx{sym, idx})
383                 }
384                 sort.Slice(bodies, func(i, j int) bool { return bodies[i].idx < bodies[j].idx })
385
386                 w := privateRootWriter
387
388                 w.Bool(typecheck.Lookup(".inittask").Def != nil)
389
390                 w.Len(len(bodies))
391                 for _, body := range bodies {
392                         w.String(body.sym.Pkg.Path)
393                         w.String(body.sym.Name)
394                         w.Reloc(pkgbits.RelocBody, body.idx)
395                 }
396
397                 w.Sync(pkgbits.SyncEOF)
398                 w.Flush()
399         }
400
401         base.Ctxt.Fingerprint = l.pw.DumpTo(out)
402 }