github.com/bir3/gocompiler@v0.9.2202/src/cmd/compile/internal/noder/linker.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 "github.com/bir3/gocompiler/src/internal/buildcfg" 9 "github.com/bir3/gocompiler/src/internal/pkgbits" 10 "io" 11 12 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 13 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 14 "github.com/bir3/gocompiler/src/cmd/compile/internal/reflectdata" 15 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 16 "github.com/bir3/gocompiler/src/cmd/internal/goobj" 17 "github.com/bir3/gocompiler/src/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. 39 type 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. 50 func (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. 61 func (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. 98 func (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. 109 func (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). 137 func (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.) 219 func (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. 255 func (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 262 func (l *linker) pragmaFlag(w *pkgbits.Encoder, pragma ir.PragmaFlag) { 263 w.Sync(pkgbits.SyncPragma) 264 w.Int(int(pragma)) 265 } 266 267 func (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 307 func (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 325 func (l *linker) relocVarExt(w *pkgbits.Encoder, name *ir.Name) { 326 w.Sync(pkgbits.SyncVarExt) 327 l.linkname(w, name) 328 } 329 330 func (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 339 func (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 }