github.com/bir3/gocompiler@v0.3.205/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/pkgbits" 9 "io" 10 11 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 12 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 13 "github.com/bir3/gocompiler/src/cmd/compile/internal/reflectdata" 14 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 15 "github.com/bir3/gocompiler/src/cmd/internal/goobj" 16 "github.com/bir3/gocompiler/src/cmd/internal/obj" 17 ) 18 19 // This file implements the unified IR linker, which combines the 20 // local package's stub data with imported package data to produce a 21 // complete export data file. It also rewrites the compiler's 22 // extension data sections based on the results of compilation (e.g., 23 // the function inlining cost and linker symbol index assignments). 24 // 25 // TODO(mdempsky): Using the name "linker" here is confusing, because 26 // readers are likely to mistake references to it for cmd/link. But 27 // there's a shortage of good names for "something that combines 28 // multiple parts into a cohesive whole"... e.g., "assembler" and 29 // "compiler" are also already taken. 30 31 // TODO(mdempsky): Should linker go into pkgbits? Probably the 32 // low-level linking details can be moved there, but the logic for 33 // handling extension data needs to stay in the compiler. 34 35 // A linker combines a package's stub export data with any referenced 36 // elements from imported packages into a single, self-contained 37 // export data file. 38 type linker struct { 39 pw pkgbits.PkgEncoder 40 41 pkgs map[string]pkgbits.Index 42 decls map[*types.Sym]pkgbits.Index 43 bodies map[*types.Sym]pkgbits.Index 44 } 45 46 // relocAll ensures that all elements specified by pr and relocs are 47 // copied into the output export data file, and returns the 48 // corresponding indices in the output. 49 func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.RelocEnt { 50 res := make([]pkgbits.RelocEnt, len(relocs)) 51 for i, rent := range relocs { 52 rent.Idx = l.relocIdx(pr, rent.Kind, rent.Idx) 53 res[i] = rent 54 } 55 return res 56 } 57 58 // relocIdx ensures a single element is copied into the output export 59 // data file, and returns the corresponding index in the output. 60 func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx pkgbits.Index) pkgbits.Index { 61 assert(pr != nil) 62 63 absIdx := pr.AbsIdx(k, idx) 64 65 if newidx := pr.newindex[absIdx]; newidx != 0 { 66 return ^newidx 67 } 68 69 var newidx pkgbits.Index 70 switch k { 71 case pkgbits.RelocString: 72 newidx = l.relocString(pr, idx) 73 case pkgbits.RelocPkg: 74 newidx = l.relocPkg(pr, idx) 75 case pkgbits.RelocObj: 76 newidx = l.relocObj(pr, idx) 77 78 default: 79 // Generic relocations. 80 // 81 // TODO(mdempsky): Deduplicate more sections? In fact, I think 82 // every section could be deduplicated. This would also be easier 83 // if we do external relocations. 84 85 w := l.pw.NewEncoderRaw(k) 86 l.relocCommon(pr, &w, k, idx) 87 newidx = w.Idx 88 } 89 90 pr.newindex[absIdx] = ^newidx 91 92 return newidx 93 } 94 95 // relocString copies the specified string from pr into the output 96 // export data file, deduplicating it against other strings. 97 func (l *linker) relocString(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { 98 return l.pw.StringIdx(pr.StringIdx(idx)) 99 } 100 101 // relocPkg copies the specified package from pr into the output 102 // export data file, rewriting its import path to match how it was 103 // imported. 104 // 105 // TODO(mdempsky): Since CL 391014, we already have the compilation 106 // unit's import path, so there should be no need to rewrite packages 107 // anymore. 108 func (l *linker) relocPkg(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { 109 path := pr.PeekPkgPath(idx) 110 111 if newidx, ok := l.pkgs[path]; ok { 112 return newidx 113 } 114 115 r := pr.NewDecoder(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef) 116 w := l.pw.NewEncoder(pkgbits.RelocPkg, pkgbits.SyncPkgDef) 117 l.pkgs[path] = w.Idx 118 119 // TODO(mdempsky): We end up leaving an empty string reference here 120 // from when the package was originally written as "". Probably not 121 // a big deal, but a little annoying. Maybe relocating 122 // cross-references in place is the way to go after all. 123 w.Relocs = l.relocAll(pr, r.Relocs) 124 125 _ = r.String() // original path 126 w.String(path) 127 128 io.Copy(&w.Data, &r.Data) 129 130 return w.Flush() 131 } 132 133 // relocObj copies the specified object from pr into the output export 134 // data file, rewriting its compiler-private extension data (e.g., 135 // adding inlining cost and escape analysis results for functions). 136 func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { 137 path, name, tag := pr.PeekObj(idx) 138 sym := types.NewPkg(path, "").Lookup(name) 139 140 if newidx, ok := l.decls[sym]; ok { 141 return newidx 142 } 143 144 if tag == pkgbits.ObjStub && path != "builtin" && path != "unsafe" { 145 pri, ok := objReader[sym] 146 if !ok { 147 base.Fatalf("missing reader for %q.%v", path, name) 148 } 149 assert(ok) 150 151 pr = pri.pr 152 idx = pri.idx 153 154 path2, name2, tag2 := pr.PeekObj(idx) 155 sym2 := types.NewPkg(path2, "").Lookup(name2) 156 assert(sym == sym2) 157 assert(tag2 != pkgbits.ObjStub) 158 } 159 160 w := l.pw.NewEncoderRaw(pkgbits.RelocObj) 161 wext := l.pw.NewEncoderRaw(pkgbits.RelocObjExt) 162 wname := l.pw.NewEncoderRaw(pkgbits.RelocName) 163 wdict := l.pw.NewEncoderRaw(pkgbits.RelocObjDict) 164 165 l.decls[sym] = w.Idx 166 assert(wext.Idx == w.Idx) 167 assert(wname.Idx == w.Idx) 168 assert(wdict.Idx == w.Idx) 169 170 l.relocCommon(pr, &w, pkgbits.RelocObj, idx) 171 l.relocCommon(pr, &wname, pkgbits.RelocName, idx) 172 l.relocCommon(pr, &wdict, pkgbits.RelocObjDict, idx) 173 174 // Generic types and functions won't have definitions, and imported 175 // objects may not either. 176 obj, _ := sym.Def.(*ir.Name) 177 local := sym.Pkg == types.LocalPkg 178 179 if local && obj != nil { 180 wext.Sync(pkgbits.SyncObject1) 181 switch tag { 182 case pkgbits.ObjFunc: 183 l.relocFuncExt(&wext, obj) 184 case pkgbits.ObjType: 185 l.relocTypeExt(&wext, obj) 186 case pkgbits.ObjVar: 187 l.relocVarExt(&wext, obj) 188 } 189 wext.Flush() 190 } else { 191 l.relocCommon(pr, &wext, pkgbits.RelocObjExt, idx) 192 } 193 194 // Check if we need to export the inline bodies for functions and 195 // methods. 196 if obj != nil { 197 if obj.Op() == ir.ONAME && obj.Class == ir.PFUNC { 198 l.exportBody(obj, local) 199 } 200 201 if obj.Op() == ir.OTYPE && !obj.Alias() { 202 if typ := obj.Type(); !typ.IsInterface() { 203 for _, method := range typ.Methods().Slice() { 204 l.exportBody(method.Nname.(*ir.Name), local) 205 } 206 } 207 } 208 } 209 210 return w.Idx 211 } 212 213 // exportBody exports the given function or method's body, if 214 // appropriate. local indicates whether it's a local function or 215 // method available on a locally declared type. (Due to cross-package 216 // type aliases, a method may be imported, but still available on a 217 // locally declared type.) 218 func (l *linker) exportBody(obj *ir.Name, local bool) { 219 assert(obj.Op() == ir.ONAME && obj.Class == ir.PFUNC) 220 221 fn := obj.Func 222 if fn.Inl == nil { 223 return // not inlinable anyway 224 } 225 226 // As a simple heuristic, if the function was declared in this 227 // package or we inlined it somewhere in this package, then we'll 228 // (re)export the function body. This isn't perfect, but seems 229 // reasonable in practice. In particular, it has the nice property 230 // that in the worst case, adding a blank import ensures the 231 // function body is available for inlining. 232 // 233 // TODO(mdempsky): Reimplement the reachable method crawling logic 234 // from typecheck/crawler.go. 235 exportBody := local || fn.Inl.Body != nil 236 if !exportBody { 237 return 238 } 239 240 sym := obj.Sym() 241 if _, ok := l.bodies[sym]; ok { 242 // Due to type aliases, we might visit methods multiple times. 243 base.AssertfAt(obj.Type().Recv() != nil, obj.Pos(), "expected method: %v", obj) 244 return 245 } 246 247 pri, ok := bodyReaderFor(fn) 248 assert(ok) 249 l.bodies[sym] = l.relocIdx(pri.pr, pkgbits.RelocBody, pri.idx) 250 } 251 252 // relocCommon copies the specified element from pr into w, 253 // recursively relocating any referenced elements as well. 254 func (l *linker) relocCommon(pr *pkgReader, w *pkgbits.Encoder, k pkgbits.RelocKind, idx pkgbits.Index) { 255 r := pr.NewDecoderRaw(k, idx) 256 w.Relocs = l.relocAll(pr, r.Relocs) 257 io.Copy(&w.Data, &r.Data) 258 w.Flush() 259 } 260 261 func (l *linker) pragmaFlag(w *pkgbits.Encoder, pragma ir.PragmaFlag) { 262 w.Sync(pkgbits.SyncPragma) 263 w.Int(int(pragma)) 264 } 265 266 func (l *linker) relocFuncExt(w *pkgbits.Encoder, name *ir.Name) { 267 w.Sync(pkgbits.SyncFuncExt) 268 269 l.pragmaFlag(w, name.Func.Pragma) 270 l.linkname(w, name) 271 272 // Relocated extension data. 273 w.Bool(true) 274 275 // Record definition ABI so cross-ABI calls can be direct. 276 // This is important for the performance of calling some 277 // common functions implemented in assembly (e.g., bytealg). 278 w.Uint64(uint64(name.Func.ABI)) 279 280 // Escape analysis. 281 for _, fs := range &types.RecvsParams { 282 for _, f := range fs(name.Type()).FieldSlice() { 283 w.String(f.Note) 284 } 285 } 286 287 if inl := name.Func.Inl; w.Bool(inl != nil) { 288 w.Len(int(inl.Cost)) 289 w.Bool(inl.CanDelayResults) 290 } 291 292 w.Sync(pkgbits.SyncEOF) 293 } 294 295 func (l *linker) relocTypeExt(w *pkgbits.Encoder, name *ir.Name) { 296 w.Sync(pkgbits.SyncTypeExt) 297 298 typ := name.Type() 299 300 l.pragmaFlag(w, name.Pragma()) 301 302 // For type T, export the index of type descriptor symbols of T and *T. 303 l.lsymIdx(w, "", reflectdata.TypeLinksym(typ)) 304 l.lsymIdx(w, "", reflectdata.TypeLinksym(typ.PtrTo())) 305 306 if typ.Kind() != types.TINTER { 307 for _, method := range typ.Methods().Slice() { 308 l.relocFuncExt(w, method.Nname.(*ir.Name)) 309 } 310 } 311 } 312 313 func (l *linker) relocVarExt(w *pkgbits.Encoder, name *ir.Name) { 314 w.Sync(pkgbits.SyncVarExt) 315 l.linkname(w, name) 316 } 317 318 func (l *linker) linkname(w *pkgbits.Encoder, name *ir.Name) { 319 w.Sync(pkgbits.SyncLinkname) 320 321 linkname := name.Sym().Linkname 322 if !l.lsymIdx(w, linkname, name.Linksym()) { 323 w.String(linkname) 324 } 325 } 326 327 func (l *linker) lsymIdx(w *pkgbits.Encoder, linkname string, lsym *obj.LSym) bool { 328 if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || linkname != "" { 329 w.Int64(-1) 330 return false 331 } 332 333 // For a defined symbol, export its index. 334 // For re-exporting an imported symbol, pass its index through. 335 w.Int64(int64(lsym.SymIdx)) 336 return true 337 }