From 69748f0ce4faf850a2e25641aed23e646556d616 Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Mon, 27 Jul 2020 14:46:16 -0400 Subject: [PATCH] [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 --- src/cmd/compile/internal/gc/iexport.go | 13 ++++++ src/cmd/compile/internal/gc/iimport.go | 13 ++++++ src/cmd/compile/internal/gc/reflect.go | 9 ++++ src/cmd/internal/goobj2/objfile.go | 57 +++++++++++++++++++++++--- src/cmd/internal/obj/objfile2.go | 41 ++++++++++++++++-- src/cmd/link/internal/loader/loader.go | 10 +++++ 6 files changed, 134 insertions(+), 9 deletions(-) diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go index 328260fc59..7a7dbdeac1 100644 --- a/src/cmd/compile/internal/gc/iexport.go +++ b/src/cmd/compile/internal/gc/iexport.go @@ -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) { diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go index 15a660cf55..29bb1e57a4 100644 --- a/src/cmd/compile/internal/gc/iimport.go +++ b/src/cmd/compile/internal/gc/iimport.go @@ -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) diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 05e64a5a9a..7758097db8 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -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. diff --git a/src/cmd/internal/goobj2/objfile.go b/src/cmd/internal/goobj2/objfile.go index eae9b5587c..c3d00122e7 100644 --- a/src/cmd/internal/goobj2/objfile.go +++ b/src/cmd/internal/goobj2/objfile.go @@ -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). diff --git a/src/cmd/internal/obj/objfile2.go b/src/cmd/internal/obj/objfile2.go index 988ecdf543..6740f42220 100644 --- a/src/cmd/internal/obj/objfile2.go +++ b/src/cmd/internal/obj/objfile2.go @@ -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,39 @@ func (w *writer) Aux(s *LSym) { } } +// 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 + return + case goobj2.PkgIdxInvalid: + panic("unindexed symbol reference") + } + if seen[rs] { + return + } + 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[goobj2.SymRef]bool) + 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 +512,11 @@ 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) var o goobj2.RefName o.SetSym(symref) o.SetName(rs.Name, w.Writer) diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 45085f56c1..d34e6fdf6b 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -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 { -- 2.50.0