From: Cherry Zhang Date: Thu, 5 Mar 2020 21:43:37 +0000 (-0500) Subject: [dev.link] cmd/link: experiment new reloc accessors in deadcode pass X-Git-Tag: go1.15beta1~679^2~72 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=cf5c718cbaa479da9257fa8f16bb081dfc89fd6b;p=gostls13.git [dev.link] cmd/link: experiment new reloc accessors in deadcode pass There is a small speedup: (linking cmd/compile) name old time/op new time/op delta Deadcode 57.1ms ± 1% 53.5ms ± 1% -6.44% (p=0.008 n=5+5) With this, we don't need a slice to read the relocations, reduce some allocations. name old alloc/op new alloc/op delta Deadcode 4.16MB ± 0% 3.84MB ± 0% -7.85% (p=0.008 n=5+5) Change-Id: Icd41c05682ba3f293a8cb9d2fe818e39d7276e5a Reviewed-on: https://go-review.googlesource.com/c/go/+/222244 Reviewed-by: Than McIntosh --- diff --git a/src/cmd/internal/goobj2/objfile.go b/src/cmd/internal/goobj2/objfile.go index 6d9b0f9e8d..d5a9b4aa8c 100644 --- a/src/cmd/internal/goobj2/objfile.go +++ b/src/cmd/internal/goobj2/objfile.go @@ -321,6 +321,15 @@ func (r *Reloc2) Sym() SymRef { return SymRef{binary.LittleEndian.Uint32(r[14:]), binary.LittleEndian.Uint32(r[18:])} } +func (r *Reloc2) Set(off int32, size uint8, typ uint8, add int64, sym SymRef) { + binary.LittleEndian.PutUint32(r[:], uint32(off)) + r[4] = size + r[5] = typ + binary.LittleEndian.PutUint64(r[6:], uint64(add)) + binary.LittleEndian.PutUint32(r[14:], sym.PkgIdx) + binary.LittleEndian.PutUint32(r[18:], sym.SymIdx) +} + // Aux symbol info. type Aux struct { Type uint8 diff --git a/src/cmd/link/internal/ld/deadcode2.go b/src/cmd/link/internal/ld/deadcode2.go index 4733f47446..a7a41e3e16 100644 --- a/src/cmd/link/internal/ld/deadcode2.go +++ b/src/cmd/link/internal/ld/deadcode2.go @@ -37,7 +37,6 @@ type deadcodePass2 struct { ctxt *Link ldr *loader.Loader wq workQueue - rtmp []loader.Reloc ifaceMethod map[methodsig]bool // methods declared in reached interfaces markableMethods []methodref2 // methods of reached types @@ -86,9 +85,9 @@ func (d *deadcodePass2) init() { // but we do keep the symbols it refers to. exportsIdx := d.ldr.Lookup("go.plugin.exports", 0) if exportsIdx != 0 { - d.ReadRelocSyms(exportsIdx) - for i := 0; i < len(d.rtmp); i++ { - d.mark(d.rtmp[i].Sym, 0) + relocs := d.ldr.Relocs(exportsIdx) + for i := 0; i < relocs.Count; i++ { + d.mark(relocs.At2(i).Sym(), 0) } } } @@ -119,7 +118,6 @@ func (d *deadcodePass2) init() { } func (d *deadcodePass2) flood() { - symRelocs := []loader.Reloc{} auxSyms := []loader.Sym{} for !d.wq.empty() { symIdx := d.wq.pop() @@ -128,22 +126,11 @@ func (d *deadcodePass2) flood() { isgotype := d.ldr.IsGoType(symIdx) relocs := d.ldr.Relocs(symIdx) - // For non-type symbols, we only need the target and the reloc - // type, so don't read other fields. - // For type symbols we may need all fields for interface - // satisfaction check. - // TODO: we don't even need the reloc type for non-type non-dwarf - // symbols. - if isgotype { - symRelocs = relocs.ReadAll(symRelocs) - } else { - symRelocs = relocs.ReadSyms(symRelocs) - } if isgotype { p := d.ldr.Data(symIdx) if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface { - for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs) { + for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, &relocs) { if d.ctxt.Debugvlog > 1 { d.ctxt.Logf("reached iface method: %s\n", sig) } @@ -154,11 +141,12 @@ func (d *deadcodePass2) flood() { var methods []methodref2 for i := 0; i < relocs.Count; i++ { - r := symRelocs[i] - if r.Type == objabi.R_WEAKADDROFF { + r := relocs.At2(i) + t := r.Type() + if t == objabi.R_WEAKADDROFF { continue } - if r.Type == objabi.R_METHODOFF { + if t == objabi.R_METHODOFF { if i+2 >= relocs.Count { panic("expect three consecutive R_METHODOFF relocs") } @@ -166,13 +154,13 @@ func (d *deadcodePass2) flood() { i += 2 continue } - if r.Type == objabi.R_USETYPE { + if t == objabi.R_USETYPE { // type symbol used for DWARF. we need to load the symbol but it may not // be otherwise reachable in the program. // do nothing for now as we still load all type symbols. continue } - d.mark(r.Sym, symIdx) + d.mark(r.Sym(), symIdx) } auxSyms = d.ldr.ReadAuxSyms(symIdx, auxSyms) for i := 0; i < len(auxSyms); i++ { @@ -194,7 +182,7 @@ func (d *deadcodePass2) flood() { // Decode runtime type information for type methods // to help work out which methods can be called // dynamically via interfaces. - methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs) + methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, &relocs) if len(methods) != len(methodsigs) { panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs))) } @@ -227,10 +215,10 @@ func (d *deadcodePass2) mark(symIdx, parent loader.Sym) { } func (d *deadcodePass2) markMethod(m methodref2) { - d.ReadRelocSyms(m.src) - d.mark(d.rtmp[m.r].Sym, m.src) - d.mark(d.rtmp[m.r+1].Sym, m.src) - d.mark(d.rtmp[m.r+2].Sym, m.src) + relocs := d.ldr.Relocs(m.src) + d.mark(relocs.At2(m.r).Sym(), m.src) + d.mark(relocs.At2(m.r+1).Sym(), m.src) + d.mark(relocs.At2(m.r+2).Sym(), m.src) } func deadcode2(ctxt *Link) { @@ -313,15 +301,15 @@ func (m methodref2) isExported() bool { // the function type. // // Conveniently this is the layout of both runtime.method and runtime.imethod. -func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, off, size, count int) []methodsig { +func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, off, size, count int) []methodsig { var buf bytes.Buffer var methods []methodsig for i := 0; i < count; i++ { - buf.WriteString(decodetypeName2(ldr, symIdx, symRelocs, off)) - mtypSym := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off+4)) + buf.WriteString(decodetypeName3(ldr, symIdx, relocs, off)) + mtypSym := decodeRelocSym3(ldr, symIdx, relocs, int32(off+4)) // FIXME: add some sort of caching here, since we may see some of the // same symbols over time for param types. - d.ReadRelocs(mtypSym) + mrelocs := ldr.Relocs(mtypSym) mp := ldr.Data(mtypSym) buf.WriteRune('(') @@ -330,7 +318,7 @@ func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, sym if i > 0 { buf.WriteString(", ") } - a := decodetypeFuncInType2(ldr, arch, mtypSym, d.rtmp, i) + a := decodetypeFuncInType3(ldr, arch, mtypSym, &mrelocs, i) buf.WriteString(ldr.SymName(a)) } buf.WriteString(") (") @@ -339,7 +327,7 @@ func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, sym if i > 0 { buf.WriteString(", ") } - a := decodetypeFuncOutType2(ldr, arch, mtypSym, d.rtmp, i) + a := decodetypeFuncOutType3(ldr, arch, mtypSym, &mrelocs, i) buf.WriteString(ldr.SymName(a)) } buf.WriteRune(')') @@ -351,25 +339,26 @@ func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, sym return methods } -func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig { +func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig { p := ldr.Data(symIdx) if decodetypeKind(arch, p)&kindMask != kindInterface { panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx))) } - rel := decodeReloc2(ldr, symIdx, symRelocs, int32(commonsize(arch)+arch.PtrSize)) - if rel.Sym == 0 { + rel := decodeReloc3(ldr, symIdx, relocs, int32(commonsize(arch)+arch.PtrSize)) + s := rel.Sym() + if s == 0 { return nil } - if rel.Sym != symIdx { + if s != symIdx { panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", ldr.SymName(symIdx))) } - off := int(rel.Add) // array of reflect.imethod values + off := int(rel.Add()) // array of reflect.imethod values numMethods := int(decodetypeIfaceMethodCount(arch, p)) sizeofIMethod := 4 + 4 - return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofIMethod, numMethods) + return d.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofIMethod, numMethods) } -func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig { +func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig { p := ldr.Data(symIdx) if !decodetypeHasUncommon(arch, p) { panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx))) @@ -400,19 +389,5 @@ func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, s moff := int(decodeInuxi(arch, p[off+4+2+2:], 4)) off += moff // offset to array of reflect.method values const sizeofMethod = 4 * 4 // sizeof reflect.method in program - return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofMethod, mcount) -} - -// readRelocs reads the relocations for the specified symbol into the -// deadcode relocs work array. Use with care, since the work array -// is a singleton. -func (d *deadcodePass2) ReadRelocs(symIdx loader.Sym) { - relocs := d.ldr.Relocs(symIdx) - d.rtmp = relocs.ReadAll(d.rtmp) -} - -// Like ReadRelocs, but only reads target symbols. -func (d *deadcodePass2) ReadRelocSyms(symIdx loader.Sym) { - relocs := d.ldr.Relocs(symIdx) - d.rtmp = relocs.ReadSyms(d.rtmp) + return d.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofMethod, mcount) } diff --git a/src/cmd/link/internal/ld/decodesym2.go b/src/cmd/link/internal/ld/decodesym2.go index 78967406bf..e93cc91a9f 100644 --- a/src/cmd/link/internal/ld/decodesym2.go +++ b/src/cmd/link/internal/ld/decodesym2.go @@ -25,10 +25,24 @@ func decodeReloc2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Relo return loader.Reloc{} } +func decodeReloc3(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Reloc2 { + for j := 0; j < relocs.Count; j++ { + rel := relocs.At2(j) + if rel.Off() == off { + return rel + } + } + return loader.Reloc2{} +} + func decodeRelocSym2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Sym { return decodeReloc2(ldr, symIdx, symRelocs, off).Sym } +func decodeRelocSym3(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Sym { + return decodeReloc3(ldr, symIdx, relocs, off).Sym() +} + // decodetypeName2 decodes the name from a reflect.name. func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int) string { r := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off)) @@ -41,6 +55,17 @@ func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.R return string(data[3 : 3+namelen]) } +func decodetypeName3(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) string { + r := decodeRelocSym3(ldr, symIdx, relocs, int32(off)) + if r == 0 { + return "" + } + + data := ldr.Data(r) + namelen := int(uint16(data[1])<<8 | uint16(data[2])) + return string(data[3 : 3+namelen]) +} + func decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym { uadd := commonsize(arch) + 4 if arch.PtrSize == 8 { @@ -52,10 +77,25 @@ func decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym return decodeRelocSym2(ldr, symIdx, symRelocs, int32(uadd+i*arch.PtrSize)) } +func decodetypeFuncInType3(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym { + uadd := commonsize(arch) + 4 + if arch.PtrSize == 8 { + uadd += 4 + } + if decodetypeHasUncommon(arch, ldr.Data(symIdx)) { + uadd += uncommonSize() + } + return decodeRelocSym3(ldr, symIdx, relocs, int32(uadd+i*arch.PtrSize)) +} + func decodetypeFuncOutType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym { return decodetypeFuncInType2(ldr, arch, symIdx, symRelocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx))) } +func decodetypeFuncOutType3(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym { + return decodetypeFuncInType3(ldr, arch, symIdx, relocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx))) +} + func decodetypeArrayElem2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym { // FIXME: it's inefficient to read the relocations each time. Add some // sort of cache here, or pass in the relocs. Alternatively we could diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 979d94402e..102fee5a41 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -520,6 +520,12 @@ func (l *Loader) resolve(r *oReader, s goobj2.SymRef) Sym { var rr *oReader switch p := s.PkgIdx; p { case goobj2.PkgIdxInvalid: + // {0, X} with non-zero X is never a valid sym reference from a Go object. + // We steal this space for symbol references from external objects. + // In this case, X is just the global index. + if l.isExtReader(r) { + return Sym(s.SymIdx) + } if s.SymIdx != 0 { panic("bad sym ref") } @@ -1448,10 +1454,14 @@ func (relocs *Relocs) At(j int) Reloc { func (relocs *Relocs) At2(j int) Reloc2 { if relocs.l.isExtReader(relocs.r) { - // TODO: implement this. How? Maybe we can construct the reloc - // data for external symbols in the same byte form as the one - // in the object file? - panic("not implemented") + pp := relocs.l.payloads[relocs.li] + r := pp.relocs[j] + // XXX populate a goobj2.Reloc from external reloc record. + // Ugly. Maybe we just want to use this format to store the + // reloc record in the first place? + var b goobj2.Reloc2 + b.Set(r.Off, r.Size, uint8(r.Type), r.Add, goobj2.SymRef{PkgIdx: 0, SymIdx: uint32(r.Sym)}) + return Reloc2{&b, relocs.r, relocs.l} } return Reloc2{relocs.r.Reloc2(relocs.li, j), relocs.r, relocs.l} }