[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 <thanm@google.com>
diff --git a/src/cmd/internal/goobj2/objfile.go b/src/cmd/internal/goobj2/objfile.go
index 6d9b0f9..d5a9b4a 100644
--- a/src/cmd/internal/goobj2/objfile.go
+++ b/src/cmd/internal/goobj2/objfile.go
@@ -321,6 +321,15 @@
 	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 4733f47..a7a41e3 100644
--- a/src/cmd/link/internal/ld/deadcode2.go
+++ b/src/cmd/link/internal/ld/deadcode2.go
@@ -37,7 +37,6 @@
 	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 @@
 			// 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) flood() {
-	symRelocs := []loader.Reloc{}
 	auxSyms := []loader.Sym{}
 	for !d.wq.empty() {
 		symIdx := d.wq.pop()
@@ -128,22 +126,11 @@
 
 		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 @@
 
 		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 @@
 				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 @@
 			// 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) 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 @@
 // 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 @@
 			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 @@
 			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 @@
 	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 @@
 	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 7896740..e93cc91 100644
--- a/src/cmd/link/internal/ld/decodesym2.go
+++ b/src/cmd/link/internal/ld/decodesym2.go
@@ -25,10 +25,24 @@
 	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 @@
 	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 @@
 	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 979d944..102fee5 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -520,6 +520,12 @@
 	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) 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}
 }