Skip to content

Commit cf5c718

Browse files
committed
[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 <[email protected]>
1 parent c951514 commit cf5c718

4 files changed

Lines changed: 93 additions & 59 deletions

File tree

src/cmd/internal/goobj2/objfile.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,15 @@ func (r *Reloc2) Sym() SymRef {
321321
return SymRef{binary.LittleEndian.Uint32(r[14:]), binary.LittleEndian.Uint32(r[18:])}
322322
}
323323

324+
func (r *Reloc2) Set(off int32, size uint8, typ uint8, add int64, sym SymRef) {
325+
binary.LittleEndian.PutUint32(r[:], uint32(off))
326+
r[4] = size
327+
r[5] = typ
328+
binary.LittleEndian.PutUint64(r[6:], uint64(add))
329+
binary.LittleEndian.PutUint32(r[14:], sym.PkgIdx)
330+
binary.LittleEndian.PutUint32(r[18:], sym.SymIdx)
331+
}
332+
324333
// Aux symbol info.
325334
type Aux struct {
326335
Type uint8

src/cmd/link/internal/ld/deadcode2.go

Lines changed: 30 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ type deadcodePass2 struct {
3737
ctxt *Link
3838
ldr *loader.Loader
3939
wq workQueue
40-
rtmp []loader.Reloc
4140

4241
ifaceMethod map[methodsig]bool // methods declared in reached interfaces
4342
markableMethods []methodref2 // methods of reached types
@@ -86,9 +85,9 @@ func (d *deadcodePass2) init() {
8685
// but we do keep the symbols it refers to.
8786
exportsIdx := d.ldr.Lookup("go.plugin.exports", 0)
8887
if exportsIdx != 0 {
89-
d.ReadRelocSyms(exportsIdx)
90-
for i := 0; i < len(d.rtmp); i++ {
91-
d.mark(d.rtmp[i].Sym, 0)
88+
relocs := d.ldr.Relocs(exportsIdx)
89+
for i := 0; i < relocs.Count; i++ {
90+
d.mark(relocs.At2(i).Sym(), 0)
9291
}
9392
}
9493
}
@@ -119,7 +118,6 @@ func (d *deadcodePass2) init() {
119118
}
120119

121120
func (d *deadcodePass2) flood() {
122-
symRelocs := []loader.Reloc{}
123121
auxSyms := []loader.Sym{}
124122
for !d.wq.empty() {
125123
symIdx := d.wq.pop()
@@ -128,22 +126,11 @@ func (d *deadcodePass2) flood() {
128126

129127
isgotype := d.ldr.IsGoType(symIdx)
130128
relocs := d.ldr.Relocs(symIdx)
131-
// For non-type symbols, we only need the target and the reloc
132-
// type, so don't read other fields.
133-
// For type symbols we may need all fields for interface
134-
// satisfaction check.
135-
// TODO: we don't even need the reloc type for non-type non-dwarf
136-
// symbols.
137-
if isgotype {
138-
symRelocs = relocs.ReadAll(symRelocs)
139-
} else {
140-
symRelocs = relocs.ReadSyms(symRelocs)
141-
}
142129

143130
if isgotype {
144131
p := d.ldr.Data(symIdx)
145132
if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface {
146-
for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs) {
133+
for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, &relocs) {
147134
if d.ctxt.Debugvlog > 1 {
148135
d.ctxt.Logf("reached iface method: %s\n", sig)
149136
}
@@ -154,25 +141,26 @@ func (d *deadcodePass2) flood() {
154141

155142
var methods []methodref2
156143
for i := 0; i < relocs.Count; i++ {
157-
r := symRelocs[i]
158-
if r.Type == objabi.R_WEAKADDROFF {
144+
r := relocs.At2(i)
145+
t := r.Type()
146+
if t == objabi.R_WEAKADDROFF {
159147
continue
160148
}
161-
if r.Type == objabi.R_METHODOFF {
149+
if t == objabi.R_METHODOFF {
162150
if i+2 >= relocs.Count {
163151
panic("expect three consecutive R_METHODOFF relocs")
164152
}
165153
methods = append(methods, methodref2{src: symIdx, r: i})
166154
i += 2
167155
continue
168156
}
169-
if r.Type == objabi.R_USETYPE {
157+
if t == objabi.R_USETYPE {
170158
// type symbol used for DWARF. we need to load the symbol but it may not
171159
// be otherwise reachable in the program.
172160
// do nothing for now as we still load all type symbols.
173161
continue
174162
}
175-
d.mark(r.Sym, symIdx)
163+
d.mark(r.Sym(), symIdx)
176164
}
177165
auxSyms = d.ldr.ReadAuxSyms(symIdx, auxSyms)
178166
for i := 0; i < len(auxSyms); i++ {
@@ -194,7 +182,7 @@ func (d *deadcodePass2) flood() {
194182
// Decode runtime type information for type methods
195183
// to help work out which methods can be called
196184
// dynamically via interfaces.
197-
methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs)
185+
methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, &relocs)
198186
if len(methods) != len(methodsigs) {
199187
panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs)))
200188
}
@@ -227,10 +215,10 @@ func (d *deadcodePass2) mark(symIdx, parent loader.Sym) {
227215
}
228216

229217
func (d *deadcodePass2) markMethod(m methodref2) {
230-
d.ReadRelocSyms(m.src)
231-
d.mark(d.rtmp[m.r].Sym, m.src)
232-
d.mark(d.rtmp[m.r+1].Sym, m.src)
233-
d.mark(d.rtmp[m.r+2].Sym, m.src)
218+
relocs := d.ldr.Relocs(m.src)
219+
d.mark(relocs.At2(m.r).Sym(), m.src)
220+
d.mark(relocs.At2(m.r+1).Sym(), m.src)
221+
d.mark(relocs.At2(m.r+2).Sym(), m.src)
234222
}
235223

236224
func deadcode2(ctxt *Link) {
@@ -313,15 +301,15 @@ func (m methodref2) isExported() bool {
313301
// the function type.
314302
//
315303
// Conveniently this is the layout of both runtime.method and runtime.imethod.
316-
func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, off, size, count int) []methodsig {
304+
func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, off, size, count int) []methodsig {
317305
var buf bytes.Buffer
318306
var methods []methodsig
319307
for i := 0; i < count; i++ {
320-
buf.WriteString(decodetypeName2(ldr, symIdx, symRelocs, off))
321-
mtypSym := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off+4))
308+
buf.WriteString(decodetypeName3(ldr, symIdx, relocs, off))
309+
mtypSym := decodeRelocSym3(ldr, symIdx, relocs, int32(off+4))
322310
// FIXME: add some sort of caching here, since we may see some of the
323311
// same symbols over time for param types.
324-
d.ReadRelocs(mtypSym)
312+
mrelocs := ldr.Relocs(mtypSym)
325313
mp := ldr.Data(mtypSym)
326314

327315
buf.WriteRune('(')
@@ -330,7 +318,7 @@ func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, sym
330318
if i > 0 {
331319
buf.WriteString(", ")
332320
}
333-
a := decodetypeFuncInType2(ldr, arch, mtypSym, d.rtmp, i)
321+
a := decodetypeFuncInType3(ldr, arch, mtypSym, &mrelocs, i)
334322
buf.WriteString(ldr.SymName(a))
335323
}
336324
buf.WriteString(") (")
@@ -339,7 +327,7 @@ func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, sym
339327
if i > 0 {
340328
buf.WriteString(", ")
341329
}
342-
a := decodetypeFuncOutType2(ldr, arch, mtypSym, d.rtmp, i)
330+
a := decodetypeFuncOutType3(ldr, arch, mtypSym, &mrelocs, i)
343331
buf.WriteString(ldr.SymName(a))
344332
}
345333
buf.WriteRune(')')
@@ -351,25 +339,26 @@ func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, sym
351339
return methods
352340
}
353341

354-
func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig {
342+
func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
355343
p := ldr.Data(symIdx)
356344
if decodetypeKind(arch, p)&kindMask != kindInterface {
357345
panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx)))
358346
}
359-
rel := decodeReloc2(ldr, symIdx, symRelocs, int32(commonsize(arch)+arch.PtrSize))
360-
if rel.Sym == 0 {
347+
rel := decodeReloc3(ldr, symIdx, relocs, int32(commonsize(arch)+arch.PtrSize))
348+
s := rel.Sym()
349+
if s == 0 {
361350
return nil
362351
}
363-
if rel.Sym != symIdx {
352+
if s != symIdx {
364353
panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", ldr.SymName(symIdx)))
365354
}
366-
off := int(rel.Add) // array of reflect.imethod values
355+
off := int(rel.Add()) // array of reflect.imethod values
367356
numMethods := int(decodetypeIfaceMethodCount(arch, p))
368357
sizeofIMethod := 4 + 4
369-
return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofIMethod, numMethods)
358+
return d.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofIMethod, numMethods)
370359
}
371360

372-
func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig {
361+
func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
373362
p := ldr.Data(symIdx)
374363
if !decodetypeHasUncommon(arch, p) {
375364
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
400389
moff := int(decodeInuxi(arch, p[off+4+2+2:], 4))
401390
off += moff // offset to array of reflect.method values
402391
const sizeofMethod = 4 * 4 // sizeof reflect.method in program
403-
return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofMethod, mcount)
404-
}
405-
406-
// readRelocs reads the relocations for the specified symbol into the
407-
// deadcode relocs work array. Use with care, since the work array
408-
// is a singleton.
409-
func (d *deadcodePass2) ReadRelocs(symIdx loader.Sym) {
410-
relocs := d.ldr.Relocs(symIdx)
411-
d.rtmp = relocs.ReadAll(d.rtmp)
412-
}
413-
414-
// Like ReadRelocs, but only reads target symbols.
415-
func (d *deadcodePass2) ReadRelocSyms(symIdx loader.Sym) {
416-
relocs := d.ldr.Relocs(symIdx)
417-
d.rtmp = relocs.ReadSyms(d.rtmp)
392+
return d.decodeMethodSig2(ldr, arch, symIdx, relocs, off, sizeofMethod, mcount)
418393
}

src/cmd/link/internal/ld/decodesym2.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,24 @@ func decodeReloc2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Relo
2525
return loader.Reloc{}
2626
}
2727

28+
func decodeReloc3(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Reloc2 {
29+
for j := 0; j < relocs.Count; j++ {
30+
rel := relocs.At2(j)
31+
if rel.Off() == off {
32+
return rel
33+
}
34+
}
35+
return loader.Reloc2{}
36+
}
37+
2838
func decodeRelocSym2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Sym {
2939
return decodeReloc2(ldr, symIdx, symRelocs, off).Sym
3040
}
3141

42+
func decodeRelocSym3(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int32) loader.Sym {
43+
return decodeReloc3(ldr, symIdx, relocs, off).Sym()
44+
}
45+
3246
// decodetypeName2 decodes the name from a reflect.name.
3347
func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int) string {
3448
r := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off))
@@ -41,6 +55,17 @@ func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.R
4155
return string(data[3 : 3+namelen])
4256
}
4357

58+
func decodetypeName3(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) string {
59+
r := decodeRelocSym3(ldr, symIdx, relocs, int32(off))
60+
if r == 0 {
61+
return ""
62+
}
63+
64+
data := ldr.Data(r)
65+
namelen := int(uint16(data[1])<<8 | uint16(data[2]))
66+
return string(data[3 : 3+namelen])
67+
}
68+
4469
func decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym {
4570
uadd := commonsize(arch) + 4
4671
if arch.PtrSize == 8 {
@@ -52,10 +77,25 @@ func decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym
5277
return decodeRelocSym2(ldr, symIdx, symRelocs, int32(uadd+i*arch.PtrSize))
5378
}
5479

80+
func decodetypeFuncInType3(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym {
81+
uadd := commonsize(arch) + 4
82+
if arch.PtrSize == 8 {
83+
uadd += 4
84+
}
85+
if decodetypeHasUncommon(arch, ldr.Data(symIdx)) {
86+
uadd += uncommonSize()
87+
}
88+
return decodeRelocSym3(ldr, symIdx, relocs, int32(uadd+i*arch.PtrSize))
89+
}
90+
5591
func decodetypeFuncOutType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym {
5692
return decodetypeFuncInType2(ldr, arch, symIdx, symRelocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx)))
5793
}
5894

95+
func decodetypeFuncOutType3(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym {
96+
return decodetypeFuncInType3(ldr, arch, symIdx, relocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx)))
97+
}
98+
5999
func decodetypeArrayElem2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
60100
// FIXME: it's inefficient to read the relocations each time. Add some
61101
// sort of cache here, or pass in the relocs. Alternatively we could

src/cmd/link/internal/loader/loader.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,12 @@ func (l *Loader) resolve(r *oReader, s goobj2.SymRef) Sym {
520520
var rr *oReader
521521
switch p := s.PkgIdx; p {
522522
case goobj2.PkgIdxInvalid:
523+
// {0, X} with non-zero X is never a valid sym reference from a Go object.
524+
// We steal this space for symbol references from external objects.
525+
// In this case, X is just the global index.
526+
if l.isExtReader(r) {
527+
return Sym(s.SymIdx)
528+
}
523529
if s.SymIdx != 0 {
524530
panic("bad sym ref")
525531
}
@@ -1448,10 +1454,14 @@ func (relocs *Relocs) At(j int) Reloc {
14481454

14491455
func (relocs *Relocs) At2(j int) Reloc2 {
14501456
if relocs.l.isExtReader(relocs.r) {
1451-
// TODO: implement this. How? Maybe we can construct the reloc
1452-
// data for external symbols in the same byte form as the one
1453-
// in the object file?
1454-
panic("not implemented")
1457+
pp := relocs.l.payloads[relocs.li]
1458+
r := pp.relocs[j]
1459+
// XXX populate a goobj2.Reloc from external reloc record.
1460+
// Ugly. Maybe we just want to use this format to store the
1461+
// reloc record in the first place?
1462+
var b goobj2.Reloc2
1463+
b.Set(r.Off, r.Size, uint8(r.Type), r.Add, goobj2.SymRef{PkgIdx: 0, SymIdx: uint32(r.Sym)})
1464+
return Reloc2{&b, relocs.r, relocs.l}
14551465
}
14561466
return Reloc2{relocs.r.Reloc2(relocs.li, j), relocs.r, relocs.l}
14571467
}

0 commit comments

Comments
 (0)