[dev.link] cmd/compile, cmd/link: reference type symbol of defined type by index

The type descriptor symbol of a defined (named) type (and pointer
to it) is defined only in the package that defines the type. It
is not dupOK, unlike other type descriptors. So it can be
referenced by index. Currently it is referenced by name for
cross-package references, because the index is not exported and
so not known to the referencing package.

This CL passes the index through the export data, so the symbol
can be referenced by index, and does not need to be looked up by
name. This also makes such symbol references consistent: it is
referenced by index within the defining package and also cross-
package, which makes it easier for content hashing (in later CLs).

One complication is that we need to set flags on referenced
symbols (specifically, the UsedInIface flag). Before, they are
non-package refs, which naturally carry flags in the object file.
For indexed refs, we currently don't put their flags in the
object file. Introduce a new block for this.

Change-Id: I8126f8e318ac4e6609eb2ac136201fd6c264c256
Reviewed-on: https://go-review.googlesource.com/c/go/+/245718
Reviewed-by: Jeremy Faller <jeremy@golang.org>
This commit is contained in:
Cherry Zhang 2020-07-27 14:46:16 -04:00
parent e4e1c6a7af
commit 69748f0ce4
6 changed files with 137 additions and 12 deletions

View file

@ -484,6 +484,7 @@ func (p *iexporter) doDecl(n *Node) {
t := n.Type
if t.IsInterface() {
w.typeExt(t)
break
}
@ -496,6 +497,7 @@ func (p *iexporter) doDecl(n *Node) {
w.signature(m.Type)
}
w.typeExt(t)
for _, m := range ms.Slice() {
w.methExt(m)
}
@ -1014,6 +1016,17 @@ func (w *exportWriter) symIdx(s *types.Sym) {
}
}
func (w *exportWriter) typeExt(t *types.Type) {
// For type T, export the index of type descriptor symbols of T and *T.
if i, ok := typeSymIdx[t]; ok {
w.int64(i[0])
w.int64(i[1])
return
}
w.symIdx(typesym(t))
w.symIdx(typesym(t.PtrTo()))
}
// Inline bodies.
func (w *exportWriter) stmtList(list Nodes) {

View file

@ -316,6 +316,7 @@ func (r *importReader) doDecl(n *Node) {
resumecheckwidth()
if underlying.IsInterface() {
r.typeExt(t)
break
}
@ -346,6 +347,7 @@ func (r *importReader) doDecl(n *Node) {
}
t.Methods().Set(ms)
r.typeExt(t)
for _, m := range ms {
r.methExt(m)
}
@ -708,6 +710,17 @@ func (r *importReader) symIdx(s *types.Sym) {
}
}
func (r *importReader) typeExt(t *types.Type) {
i, pi := r.int64(), r.int64()
if i != -1 && pi != -1 {
typeSymIdx[t] = [2]int64{i, pi}
}
}
// Map imported type T to the index of type descriptor symbols of T and *T,
// so we can use index to reference the symbol.
var typeSymIdx = make(map[*types.Type][2]int64)
func (r *importReader) doInline(n *Node) {
if len(n.Func.Inl.Body) != 0 {
Fatalf("%v already has inline body", n)

View file

@ -1168,6 +1168,15 @@ func dtypesym(t *types.Type) *obj.LSym {
if myimportpath != "runtime" || (tbase != types.Types[tbase.Etype] && tbase != types.Bytetype && tbase != types.Runetype && tbase != types.Errortype) { // int, float, etc
// named types from other files are defined only by those files
if tbase.Sym != nil && tbase.Sym.Pkg != localpkg {
if i, ok := typeSymIdx[tbase]; ok {
lsym.Pkg = tbase.Sym.Pkg.Prefix
if t != tbase {
lsym.SymIdx = int32(i[1])
} else {
lsym.SymIdx = int32(i[0])
}
lsym.Set(obj.AttrIndexed, true)
}
return lsym
}
// TODO(mdempsky): Investigate whether this can happen.

View file

@ -41,11 +41,12 @@ import (
// DwarfFiles [...]string
//
// SymbolDefs [...]struct {
// Name string
// ABI uint16
// Type uint8
// Flag uint8
// Size uint32
// Name string
// ABI uint16
// Type uint8
// Flag uint8
// Flag2 uint8
// Size uint32
// }
// Hashed64Defs [...]struct { // short hashed (content-addressable) symbol definitions
// ... // same as SymbolDefs
@ -60,6 +61,12 @@ import (
// ... // same as SymbolDefs
// }
//
// RefFlags [...]struct { // referenced symbol flags
// Sym symRef
// Flag uint8
// Flag2 uint8
// }
//
// Hash64 [...][8]byte
// Hash [...][N]byte
//
@ -176,6 +183,7 @@ const (
BlkHasheddef
BlkNonpkgdef
BlkNonpkgref
BlkRefFlags
BlkHash64
BlkHash
BlkRelocIdx
@ -431,6 +439,33 @@ func (a *Aux) Write(w *Writer) { w.Bytes(a[:]) }
// for testing
func (a *Aux) fromBytes(b []byte) { copy(a[:], b) }
// Referenced symbol flags.
//
// Serialized format:
// RefFlags struct {
// Sym symRef
// Flag uint8
// Flag2 uint8
// }
type RefFlags [RefFlagsSize]byte
const RefFlagsSize = 8 + 1 + 1
func (r *RefFlags) Sym() SymRef {
return SymRef{binary.LittleEndian.Uint32(r[:]), binary.LittleEndian.Uint32(r[4:])}
}
func (r *RefFlags) Flag() uint8 { return r[8] }
func (r *RefFlags) Flag2() uint8 { return r[9] }
func (r *RefFlags) SetSym(x SymRef) {
binary.LittleEndian.PutUint32(r[:], x.PkgIdx)
binary.LittleEndian.PutUint32(r[4:], x.SymIdx)
}
func (r *RefFlags) SetFlag(x uint8) { r[8] = x }
func (r *RefFlags) SetFlag2(x uint8) { r[9] = x }
func (r *RefFlags) Write(w *Writer) { w.Bytes(r[:]) }
// Referenced symbol name.
//
// Serialized format:
@ -689,6 +724,18 @@ func (r *Reader) Sym(i uint32) *Sym {
return (*Sym)(unsafe.Pointer(&r.b[off]))
}
// NRefFlags returns the number of referenced symbol flags.
func (r *Reader) NRefFlags() int {
return int(r.h.Offsets[BlkRefFlags+1]-r.h.Offsets[BlkRefFlags]) / RefFlagsSize
}
// RefFlags returns a pointer to the i-th referenced symbol flags.
// Note: here i is not a local symbol index, just a counter.
func (r *Reader) RefFlags(i int) *RefFlags {
off := r.h.Offsets[BlkRefFlags] + uint32(i*RefFlagsSize)
return (*RefFlags)(unsafe.Pointer(&r.b[off]))
}
// Hash64 returns the i-th short hashed symbol's hash.
// Note: here i is the index of short hashed symbols, not all symbols
// (unlike other accessors).

View file

@ -105,6 +105,10 @@ func WriteObjFile(ctxt *Link, b *bio.Writer) {
w.Sym(s)
}
// Referenced package symbol flags
h.Offsets[goobj2.BlkRefFlags] = w.Offset()
w.refFlags()
// Hashes
h.Offsets[goobj2.BlkHash64] = w.Offset()
for _, s := range ctxt.hashed64defs {
@ -468,10 +472,9 @@ func (w *writer) Aux(s *LSym) {
}
}
// Emits names of referenced indexed symbols, used by tools (objdump, nm)
// only.
func (w *writer) refNames() {
seen := make(map[goobj2.SymRef]bool)
// Emits flags of referenced indexed symbols.
func (w *writer) refFlags() {
seen := make(map[*LSym]bool)
w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
switch rs.PkgIdx {
case goobj2.PkgIdxNone, goobj2.PkgIdxHashed64, goobj2.PkgIdxHashed, goobj2.PkgIdxBuiltin, goobj2.PkgIdxSelf: // not an external indexed reference
@ -479,11 +482,41 @@ func (w *writer) refNames() {
case goobj2.PkgIdxInvalid:
panic("unindexed symbol reference")
}
symref := makeSymRef(rs)
if seen[symref] {
if seen[rs] {
return
}
seen[symref] = true
seen[rs] = true
symref := makeSymRef(rs)
flag2 := uint8(0)
if rs.UsedInIface() {
flag2 |= goobj2.SymFlagUsedInIface
}
if flag2 == 0 {
return // no need to write zero flags
}
var o goobj2.RefFlags
o.SetSym(symref)
o.SetFlag2(flag2)
o.Write(w.Writer)
})
}
// Emits names of referenced indexed symbols, used by tools (objdump, nm)
// only.
func (w *writer) refNames() {
seen := make(map[*LSym]bool)
w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
switch rs.PkgIdx {
case goobj2.PkgIdxNone, goobj2.PkgIdxHashed64, goobj2.PkgIdxHashed, goobj2.PkgIdxBuiltin, goobj2.PkgIdxSelf: // not an external indexed reference
return
case goobj2.PkgIdxInvalid:
panic("unindexed symbol reference")
}
if seen[rs] {
return
}
seen[rs] = true
symref := makeSymRef(rs)
var o goobj2.RefName
o.SetSym(symref)
o.SetName(rs.Name, w.Writer)

View file

@ -2149,6 +2149,7 @@ func (l *Loader) LoadNonpkgSyms(arch *sys.Arch) {
}
func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) {
// load non-package refs
ndef := uint32(r.NAlldef())
needNameExpansion := r.NeedNameExpansion()
for i, n := uint32(0), uint32(r.NNonpkgref()); i < n; i++ {
@ -2167,6 +2168,15 @@ func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) {
l.SetAttrUsedInIface(gi, true)
}
}
// load flags of package refs
for i, n := 0, r.NRefFlags(); i < n; i++ {
rf := r.RefFlags(i)
gi := l.resolve(r, rf.Sym())
if rf.Flag2()&goobj2.SymFlagUsedInIface != 0 {
l.SetAttrUsedInIface(gi, true)
}
}
}
func abiToVer(abi uint16, localSymVersion int) int {