1// UNREVIEWED 2 3// Copyright 2021 The Go Authors. All rights reserved. 4// Use of this source code is governed by a BSD-style 5// license that can be found in the LICENSE file. 6 7package noder 8 9import ( 10 "io" 11 12 "cmd/compile/internal/base" 13 "cmd/compile/internal/ir" 14 "cmd/compile/internal/reflectdata" 15 "cmd/compile/internal/types" 16 "cmd/internal/goobj" 17 "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 32type linker struct { 33 pw pkgEncoder 34 35 pkgs map[string]int 36 decls map[*types.Sym]int 37} 38 39func (l *linker) relocAll(pr *pkgReader, relocs []relocEnt) []relocEnt { 40 res := make([]relocEnt, len(relocs)) 41 for i, rent := range relocs { 42 rent.idx = l.relocIdx(pr, rent.kind, rent.idx) 43 res[i] = rent 44 } 45 return res 46} 47 48func (l *linker) relocIdx(pr *pkgReader, k reloc, idx int) int { 49 assert(pr != nil) 50 51 absIdx := pr.absIdx(k, idx) 52 53 if newidx := pr.newindex[absIdx]; newidx != 0 { 54 return ^newidx 55 } 56 57 var newidx int 58 switch k { 59 case relocString: 60 newidx = l.relocString(pr, idx) 61 case relocPkg: 62 newidx = l.relocPkg(pr, idx) 63 case relocObj: 64 newidx = l.relocObj(pr, idx) 65 66 default: 67 // Generic relocations. 68 // 69 // TODO(mdempsky): Deduplicate more sections? In fact, I think 70 // every section could be deduplicated. This would also be easier 71 // if we do external relocations. 72 73 w := l.pw.newEncoderRaw(k) 74 l.relocCommon(pr, &w, k, idx) 75 newidx = w.idx 76 } 77 78 pr.newindex[absIdx] = ^newidx 79 80 return newidx 81} 82 83func (l *linker) relocString(pr *pkgReader, idx int) int { 84 return l.pw.stringIdx(pr.stringIdx(idx)) 85} 86 87func (l *linker) relocPkg(pr *pkgReader, idx int) int { 88 path := pr.peekPkgPath(idx) 89 90 if newidx, ok := l.pkgs[path]; ok { 91 return newidx 92 } 93 94 r := pr.newDecoder(relocPkg, idx, syncPkgDef) 95 w := l.pw.newEncoder(relocPkg, syncPkgDef) 96 l.pkgs[path] = w.idx 97 98 // TODO(mdempsky): We end up leaving an empty string reference here 99 // from when the package was originally written as "". Probably not 100 // a big deal, but a little annoying. Maybe relocating 101 // cross-references in place is the way to go after all. 102 w.relocs = l.relocAll(pr, r.relocs) 103 104 _ = r.string() // original path 105 w.string(path) 106 107 io.Copy(&w.data, &r.data) 108 109 return w.flush() 110} 111 112func (l *linker) relocObj(pr *pkgReader, idx int) int { 113 path, name, tag := pr.peekObj(idx) 114 sym := types.NewPkg(path, "").Lookup(name) 115 116 if newidx, ok := l.decls[sym]; ok { 117 return newidx 118 } 119 120 if tag == objStub && path != "builtin" && path != "unsafe" { 121 pri, ok := objReader[sym] 122 if !ok { 123 base.Fatalf("missing reader for %q.%v", path, name) 124 } 125 assert(ok) 126 127 pr = pri.pr 128 idx = pri.idx 129 130 path2, name2, tag2 := pr.peekObj(idx) 131 sym2 := types.NewPkg(path2, "").Lookup(name2) 132 assert(sym == sym2) 133 assert(tag2 != objStub) 134 } 135 136 w := l.pw.newEncoderRaw(relocObj) 137 wext := l.pw.newEncoderRaw(relocObjExt) 138 wname := l.pw.newEncoderRaw(relocName) 139 wdict := l.pw.newEncoderRaw(relocObjDict) 140 141 l.decls[sym] = w.idx 142 assert(wext.idx == w.idx) 143 assert(wname.idx == w.idx) 144 assert(wdict.idx == w.idx) 145 146 l.relocCommon(pr, &w, relocObj, idx) 147 l.relocCommon(pr, &wname, relocName, idx) 148 l.relocCommon(pr, &wdict, relocObjDict, idx) 149 150 var obj *ir.Name 151 if path == "" { 152 var ok bool 153 obj, ok = sym.Def.(*ir.Name) 154 155 // Generic types and functions and declared constraint types won't 156 // have definitions. 157 // For now, just generically copy their extension data. 158 // TODO(mdempsky): Restore assertion. 159 if !ok && false { 160 base.Fatalf("missing definition for %v", sym) 161 } 162 } 163 164 if obj != nil { 165 wext.sync(syncObject1) 166 switch tag { 167 case objFunc: 168 l.relocFuncExt(&wext, obj) 169 case objType: 170 l.relocTypeExt(&wext, obj) 171 case objVar: 172 l.relocVarExt(&wext, obj) 173 } 174 wext.flush() 175 } else { 176 l.relocCommon(pr, &wext, relocObjExt, idx) 177 } 178 179 return w.idx 180} 181 182func (l *linker) relocCommon(pr *pkgReader, w *encoder, k reloc, idx int) { 183 r := pr.newDecoderRaw(k, idx) 184 w.relocs = l.relocAll(pr, r.relocs) 185 io.Copy(&w.data, &r.data) 186 w.flush() 187} 188 189func (l *linker) pragmaFlag(w *encoder, pragma ir.PragmaFlag) { 190 w.sync(syncPragma) 191 w.int(int(pragma)) 192} 193 194func (l *linker) relocFuncExt(w *encoder, name *ir.Name) { 195 w.sync(syncFuncExt) 196 197 l.pragmaFlag(w, name.Func.Pragma) 198 l.linkname(w, name) 199 200 // Relocated extension data. 201 w.bool(true) 202 203 // Record definition ABI so cross-ABI calls can be direct. 204 // This is important for the performance of calling some 205 // common functions implemented in assembly (e.g., bytealg). 206 w.uint64(uint64(name.Func.ABI)) 207 208 // Escape analysis. 209 for _, fs := range &types.RecvsParams { 210 for _, f := range fs(name.Type()).FieldSlice() { 211 w.string(f.Note) 212 } 213 } 214 215 if inl := name.Func.Inl; w.bool(inl != nil) { 216 w.len(int(inl.Cost)) 217 w.bool(inl.CanDelayResults) 218 219 pri, ok := bodyReader[name.Func] 220 assert(ok) 221 w.reloc(relocBody, l.relocIdx(pri.pr, relocBody, pri.idx)) 222 } 223 224 w.sync(syncEOF) 225} 226 227func (l *linker) relocTypeExt(w *encoder, name *ir.Name) { 228 w.sync(syncTypeExt) 229 230 typ := name.Type() 231 232 l.pragmaFlag(w, name.Pragma()) 233 234 // For type T, export the index of type descriptor symbols of T and *T. 235 l.lsymIdx(w, "", reflectdata.TypeLinksym(typ)) 236 l.lsymIdx(w, "", reflectdata.TypeLinksym(typ.PtrTo())) 237 238 if typ.Kind() != types.TINTER { 239 for _, method := range typ.Methods().Slice() { 240 l.relocFuncExt(w, method.Nname.(*ir.Name)) 241 } 242 } 243} 244 245func (l *linker) relocVarExt(w *encoder, name *ir.Name) { 246 w.sync(syncVarExt) 247 l.linkname(w, name) 248} 249 250func (l *linker) linkname(w *encoder, name *ir.Name) { 251 w.sync(syncLinkname) 252 253 linkname := name.Sym().Linkname 254 if !l.lsymIdx(w, linkname, name.Linksym()) { 255 w.string(linkname) 256 } 257} 258 259func (l *linker) lsymIdx(w *encoder, linkname string, lsym *obj.LSym) bool { 260 if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || linkname != "" { 261 w.int64(-1) 262 return false 263 } 264 265 // For a defined symbol, export its index. 266 // For re-exporting an imported symbol, pass its index through. 267 w.int64(int64(lsym.SymIdx)) 268 return true 269} 270 271// @@@ Helpers 272 273// TODO(mdempsky): These should probably be removed. I think they're a 274// smell that the export data format is not yet quite right. 275 276func (pr *pkgDecoder) peekPkgPath(idx int) string { 277 r := pr.newDecoder(relocPkg, idx, syncPkgDef) 278 path := r.string() 279 if path == "" { 280 path = pr.pkgPath 281 } 282 return path 283} 284 285func (pr *pkgDecoder) peekObj(idx int) (string, string, codeObj) { 286 r := pr.newDecoder(relocName, idx, syncObject1) 287 r.sync(syncSym) 288 r.sync(syncPkg) 289 path := pr.peekPkgPath(r.reloc(relocPkg)) 290 name := r.string() 291 assert(name != "") 292 293 tag := codeObj(r.code(syncCodeObj)) 294 295 return path, name, tag 296} 297