github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/noder/unified.go (about) 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 "github.com/bir3/gocompiler/src/internal/goversion" 10 "github.com/bir3/gocompiler/src/internal/pkgbits" 11 "io" 12 "runtime" 13 "sort" 14 "strings" 15 16 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 17 "github.com/bir3/gocompiler/src/cmd/compile/internal/inline" 18 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 19 "github.com/bir3/gocompiler/src/cmd/compile/internal/typecheck" 20 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 21 "github.com/bir3/gocompiler/src/cmd/compile/internal/types2" 22 "github.com/bir3/gocompiler/src/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, false) 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 // 127 // If duringInlining is true, then the inline.InlineDecls is called as 128 // necessary on instantiations of imported generic functions, so their 129 // inlining costs can be computed. 130 func readBodies(target *ir.Package, duringInlining bool) { 131 var inlDecls []ir.Node 132 133 // Don't use range--bodyIdx can add closures to todoBodies. 134 for { 135 // The order we expand dictionaries and bodies doesn't matter, so 136 // pop from the end to reduce todoBodies reallocations if it grows 137 // further. 138 // 139 // However, we do at least need to flush any pending dictionaries 140 // before reading bodies, because bodies might reference the 141 // dictionaries. 142 143 if len(todoDicts) > 0 { 144 fn := todoDicts[len(todoDicts)-1] 145 todoDicts = todoDicts[:len(todoDicts)-1] 146 fn() 147 continue 148 } 149 150 if len(todoBodies) > 0 { 151 fn := todoBodies[len(todoBodies)-1] 152 todoBodies = todoBodies[:len(todoBodies)-1] 153 154 pri, ok := bodyReader[fn] 155 assert(ok) 156 pri.funcBody(fn) 157 158 // Instantiated generic function: add to Decls for typechecking 159 // and compilation. 160 if fn.OClosure == nil && len(pri.dict.targs) != 0 { 161 // cmd/link does not support a type symbol referencing a method symbol 162 // across DSO boundary, so force re-compiling methods on a generic type 163 // even it was seen from imported package in linkshared mode, see #58966. 164 canSkipNonGenericMethod := !(base.Ctxt.Flag_linkshared && ir.IsMethod(fn)) 165 if duringInlining && canSkipNonGenericMethod { 166 inlDecls = append(inlDecls, fn) 167 } else { 168 target.Decls = append(target.Decls, fn) 169 } 170 } 171 172 continue 173 } 174 175 break 176 } 177 178 todoDicts = nil 179 todoBodies = nil 180 181 if len(inlDecls) != 0 { 182 // If we instantiated any generic functions during inlining, we need 183 // to call CanInline on them so they'll be transitively inlined 184 // correctly (#56280). 185 // 186 // We know these functions were already compiled in an imported 187 // package though, so we don't need to actually apply InlineCalls or 188 // save the function bodies any further than this. 189 // 190 // We can also lower the -m flag to 0, to suppress duplicate "can 191 // inline" diagnostics reported against the imported package. Again, 192 // we already reported those diagnostics in the original package, so 193 // it's pointless repeating them here. 194 195 oldLowerM := base.Flag.LowerM 196 base.Flag.LowerM = 0 197 inline.InlineDecls(nil, inlDecls, false) 198 base.Flag.LowerM = oldLowerM 199 200 for _, fn := range inlDecls { 201 fn.(*ir.Func).Body = nil // free memory 202 } 203 } 204 } 205 206 // writePkgStub type checks the given parsed source files, 207 // writes an export data package stub representing them, 208 // and returns the result. 209 func writePkgStub(noders []*noder) string { 210 m, pkg, info := checkFiles(noders) 211 212 pw := newPkgWriter(m, pkg, info) 213 214 pw.collectDecls(noders) 215 216 publicRootWriter := pw.newWriter(pkgbits.RelocMeta, pkgbits.SyncPublic) 217 privateRootWriter := pw.newWriter(pkgbits.RelocMeta, pkgbits.SyncPrivate) 218 219 assert(publicRootWriter.Idx == pkgbits.PublicRootIdx) 220 assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx) 221 222 { 223 w := publicRootWriter 224 w.pkg(pkg) 225 w.Bool(false) // TODO(mdempsky): Remove; was "has init" 226 227 scope := pkg.Scope() 228 names := scope.Names() 229 w.Len(len(names)) 230 for _, name := range names { 231 w.obj(scope.Lookup(name), nil) 232 } 233 234 w.Sync(pkgbits.SyncEOF) 235 w.Flush() 236 } 237 238 { 239 w := privateRootWriter 240 w.pkgInit(noders) 241 w.Flush() 242 } 243 244 var sb strings.Builder 245 pw.DumpTo(&sb) 246 247 // At this point, we're done with types2. Make sure the package is 248 // garbage collected. 249 freePackage(pkg) 250 251 return sb.String() 252 } 253 254 // freePackage ensures the given package is garbage collected. 255 func freePackage(pkg *types2.Package) { 256 // The GC test below relies on a precise GC that runs finalizers as 257 // soon as objects are unreachable. Our implementation provides 258 // this, but other/older implementations may not (e.g., Go 1.4 does 259 // not because of #22350). To avoid imposing unnecessary 260 // restrictions on the GOROOT_BOOTSTRAP toolchain, we skip the test 261 // during bootstrapping. 262 if base.CompilerBootstrap || base.Debug.GCCheck == 0 { 263 *pkg = types2.Package{} 264 return 265 } 266 267 // Set a finalizer on pkg so we can detect if/when it's collected. 268 done := make(chan struct{}) 269 runtime.SetFinalizer(pkg, func(*types2.Package) { close(done) }) 270 271 // Important: objects involved in cycles are not finalized, so zero 272 // out pkg to break its cycles and allow the finalizer to run. 273 *pkg = types2.Package{} 274 275 // It typically takes just 1 or 2 cycles to release pkg, but it 276 // doesn't hurt to try a few more times. 277 for i := 0; i < 10; i++ { 278 select { 279 case <-done: 280 return 281 default: 282 runtime.GC() 283 } 284 } 285 286 base.Fatalf("package never finalized") 287 } 288 289 // readPackage reads package export data from pr to populate 290 // importpkg. 291 // 292 // localStub indicates whether pr is reading the stub export data for 293 // the local package, as opposed to relocated export data for an 294 // import. 295 func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) { 296 { 297 r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) 298 299 pkg := r.pkg() 300 base.Assertf(pkg == importpkg, "have package %q (%p), want package %q (%p)", pkg.Path, pkg, importpkg.Path, importpkg) 301 302 r.Bool() // TODO(mdempsky): Remove; was "has init" 303 304 for i, n := 0, r.Len(); i < n; i++ { 305 r.Sync(pkgbits.SyncObject) 306 assert(!r.Bool()) 307 idx := r.Reloc(pkgbits.RelocObj) 308 assert(r.Len() == 0) 309 310 path, name, code := r.p.PeekObj(idx) 311 if code != pkgbits.ObjStub { 312 objReader[types.NewPkg(path, "").Lookup(name)] = pkgReaderIndex{pr, idx, nil, nil, nil} 313 } 314 } 315 316 r.Sync(pkgbits.SyncEOF) 317 } 318 319 if !localStub { 320 r := pr.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate) 321 322 if r.Bool() { 323 sym := importpkg.Lookup(".inittask") 324 task := ir.NewNameAt(src.NoXPos, sym) 325 task.Class = ir.PEXTERN 326 sym.Def = task 327 } 328 329 for i, n := 0, r.Len(); i < n; i++ { 330 path := r.String() 331 name := r.String() 332 idx := r.Reloc(pkgbits.RelocBody) 333 334 sym := types.NewPkg(path, "").Lookup(name) 335 if _, ok := importBodyReader[sym]; !ok { 336 importBodyReader[sym] = pkgReaderIndex{pr, idx, nil, nil, nil} 337 } 338 } 339 340 r.Sync(pkgbits.SyncEOF) 341 } 342 } 343 344 // writeUnifiedExport writes to `out` the finalized, self-contained 345 // Unified IR export data file for the current compilation unit. 346 func writeUnifiedExport(out io.Writer) { 347 l := linker{ 348 pw: pkgbits.NewPkgEncoder(base.Debug.SyncFrames), 349 350 pkgs: make(map[string]pkgbits.Index), 351 decls: make(map[*types.Sym]pkgbits.Index), 352 bodies: make(map[*types.Sym]pkgbits.Index), 353 } 354 355 publicRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPublic) 356 privateRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPrivate) 357 assert(publicRootWriter.Idx == pkgbits.PublicRootIdx) 358 assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx) 359 360 var selfPkgIdx pkgbits.Index 361 362 { 363 pr := localPkgReader 364 r := pr.NewDecoder(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) 365 366 r.Sync(pkgbits.SyncPkg) 367 selfPkgIdx = l.relocIdx(pr, pkgbits.RelocPkg, r.Reloc(pkgbits.RelocPkg)) 368 369 r.Bool() // TODO(mdempsky): Remove; was "has init" 370 371 for i, n := 0, r.Len(); i < n; i++ { 372 r.Sync(pkgbits.SyncObject) 373 assert(!r.Bool()) 374 idx := r.Reloc(pkgbits.RelocObj) 375 assert(r.Len() == 0) 376 377 xpath, xname, xtag := pr.PeekObj(idx) 378 assert(xpath == pr.PkgPath()) 379 assert(xtag != pkgbits.ObjStub) 380 381 if types.IsExported(xname) { 382 l.relocIdx(pr, pkgbits.RelocObj, idx) 383 } 384 } 385 386 r.Sync(pkgbits.SyncEOF) 387 } 388 389 { 390 var idxs []pkgbits.Index 391 for _, idx := range l.decls { 392 idxs = append(idxs, idx) 393 } 394 sort.Slice(idxs, func(i, j int) bool { return idxs[i] < idxs[j] }) 395 396 w := publicRootWriter 397 398 w.Sync(pkgbits.SyncPkg) 399 w.Reloc(pkgbits.RelocPkg, selfPkgIdx) 400 w.Bool(false) // TODO(mdempsky): Remove; was "has init" 401 402 w.Len(len(idxs)) 403 for _, idx := range idxs { 404 w.Sync(pkgbits.SyncObject) 405 w.Bool(false) 406 w.Reloc(pkgbits.RelocObj, idx) 407 w.Len(0) 408 } 409 410 w.Sync(pkgbits.SyncEOF) 411 w.Flush() 412 } 413 414 { 415 type symIdx struct { 416 sym *types.Sym 417 idx pkgbits.Index 418 } 419 var bodies []symIdx 420 for sym, idx := range l.bodies { 421 bodies = append(bodies, symIdx{sym, idx}) 422 } 423 sort.Slice(bodies, func(i, j int) bool { return bodies[i].idx < bodies[j].idx }) 424 425 w := privateRootWriter 426 427 w.Bool(typecheck.Lookup(".inittask").Def != nil) 428 429 w.Len(len(bodies)) 430 for _, body := range bodies { 431 w.String(body.sym.Pkg.Path) 432 w.String(body.sym.Name) 433 w.Reloc(pkgbits.RelocBody, body.idx) 434 } 435 436 w.Sync(pkgbits.SyncEOF) 437 w.Flush() 438 } 439 440 base.Ctxt.Fingerprint = l.pw.DumpTo(out) 441 }