[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 t := n.Type
if t.IsInterface() { if t.IsInterface() {
w.typeExt(t)
break break
} }
@ -496,6 +497,7 @@ func (p *iexporter) doDecl(n *Node) {
w.signature(m.Type) w.signature(m.Type)
} }
w.typeExt(t)
for _, m := range ms.Slice() { for _, m := range ms.Slice() {
w.methExt(m) 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. // Inline bodies.
func (w *exportWriter) stmtList(list Nodes) { func (w *exportWriter) stmtList(list Nodes) {

View file

@ -316,6 +316,7 @@ func (r *importReader) doDecl(n *Node) {
resumecheckwidth() resumecheckwidth()
if underlying.IsInterface() { if underlying.IsInterface() {
r.typeExt(t)
break break
} }
@ -346,6 +347,7 @@ func (r *importReader) doDecl(n *Node) {
} }
t.Methods().Set(ms) t.Methods().Set(ms)
r.typeExt(t)
for _, m := range ms { for _, m := range ms {
r.methExt(m) 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) { func (r *importReader) doInline(n *Node) {
if len(n.Func.Inl.Body) != 0 { if len(n.Func.Inl.Body) != 0 {
Fatalf("%v already has inline body", n) 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 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 // named types from other files are defined only by those files
if tbase.Sym != nil && tbase.Sym.Pkg != localpkg { 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 return lsym
} }
// TODO(mdempsky): Investigate whether this can happen. // TODO(mdempsky): Investigate whether this can happen.

View file

@ -41,11 +41,12 @@ import (
// DwarfFiles [...]string // DwarfFiles [...]string
// //
// SymbolDefs [...]struct { // SymbolDefs [...]struct {
// Name string // Name string
// ABI uint16 // ABI uint16
// Type uint8 // Type uint8
// Flag uint8 // Flag uint8
// Size uint32 // Flag2 uint8
// Size uint32
// } // }
// Hashed64Defs [...]struct { // short hashed (content-addressable) symbol definitions // Hashed64Defs [...]struct { // short hashed (content-addressable) symbol definitions
// ... // same as SymbolDefs // ... // same as SymbolDefs
@ -60,6 +61,12 @@ import (
// ... // same as SymbolDefs // ... // same as SymbolDefs
// } // }
// //
// RefFlags [...]struct { // referenced symbol flags
// Sym symRef
// Flag uint8
// Flag2 uint8
// }
//
// Hash64 [...][8]byte // Hash64 [...][8]byte
// Hash [...][N]byte // Hash [...][N]byte
// //
@ -176,6 +183,7 @@ const (
BlkHasheddef BlkHasheddef
BlkNonpkgdef BlkNonpkgdef
BlkNonpkgref BlkNonpkgref
BlkRefFlags
BlkHash64 BlkHash64
BlkHash BlkHash
BlkRelocIdx BlkRelocIdx
@ -431,6 +439,33 @@ func (a *Aux) Write(w *Writer) { w.Bytes(a[:]) }
// for testing // for testing
func (a *Aux) fromBytes(b []byte) { copy(a[:], b) } 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. // Referenced symbol name.
// //
// Serialized format: // Serialized format:
@ -689,6 +724,18 @@ func (r *Reader) Sym(i uint32) *Sym {
return (*Sym)(unsafe.Pointer(&r.b[off])) 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. // Hash64 returns the i-th short hashed symbol's hash.
// Note: here i is the index of short hashed symbols, not all symbols // Note: here i is the index of short hashed symbols, not all symbols
// (unlike other accessors). // (unlike other accessors).

View file

@ -105,6 +105,10 @@ func WriteObjFile(ctxt *Link, b *bio.Writer) {
w.Sym(s) w.Sym(s)
} }
// Referenced package symbol flags
h.Offsets[goobj2.BlkRefFlags] = w.Offset()
w.refFlags()
// Hashes // Hashes
h.Offsets[goobj2.BlkHash64] = w.Offset() h.Offsets[goobj2.BlkHash64] = w.Offset()
for _, s := range ctxt.hashed64defs { 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) // Emits flags of referenced indexed symbols.
// only. func (w *writer) refFlags() {
func (w *writer) refNames() { seen := make(map[*LSym]bool)
seen := make(map[goobj2.SymRef]bool)
w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
switch rs.PkgIdx { switch rs.PkgIdx {
case goobj2.PkgIdxNone, goobj2.PkgIdxHashed64, goobj2.PkgIdxHashed, goobj2.PkgIdxBuiltin, goobj2.PkgIdxSelf: // not an external indexed reference 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: case goobj2.PkgIdxInvalid:
panic("unindexed symbol reference") panic("unindexed symbol reference")
} }
symref := makeSymRef(rs) if seen[rs] {
if seen[symref] {
return 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 var o goobj2.RefName
o.SetSym(symref) o.SetSym(symref)
o.SetName(rs.Name, w.Writer) 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) { func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) {
// load non-package refs
ndef := uint32(r.NAlldef()) ndef := uint32(r.NAlldef())
needNameExpansion := r.NeedNameExpansion() needNameExpansion := r.NeedNameExpansion()
for i, n := uint32(0), uint32(r.NNonpkgref()); i < n; i++ { 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) 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 { func abiToVer(abi uint16, localSymVersion int) int {