From: shaharko Date: Wed, 19 Oct 2016 04:33:16 +0000 (+0300) Subject: cmd/compile, cmd/link: more efficient typelink generation X-Git-Tag: go1.8beta1~638 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=d8d445280a6a7e5f3535e8b49e9ae248f2627759;p=gostls13.git cmd/compile, cmd/link: more efficient typelink generation Instead of generating typelink symbols in the compiler mark types that should have typelinks with a flag. The linker detects this flag and adds the marked types to the typelink table. name old s/op new s/op delta LinkCmdCompile 0.27 ± 6% 0.25 ± 6% -6.93% (p=0.000 n=97+98) LinkCmdGo 0.30 ± 5% 0.29 ±10% -4.22% (p=0.000 n=97+99) name old MaxRSS new MaxRSS delta LinkCmdCompile 112k ± 3% 106k ± 2% -4.85% (p=0.000 n=100+100) LinkCmdGo 107k ± 3% 103k ± 3% -3.00% (p=0.000 n=100+100) Change-Id: Ic95dd4b0101e90c1fa262c9c6c03a2028d6b3623 Reviewed-on: https://go-review.googlesource.com/31772 Run-TryBot: Shahar Kohanim TryBot-Result: Gobot Gobot Reviewed-by: David Crawshaw --- diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 32909490b6..1c887ced25 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -937,11 +937,6 @@ func tracksym(t *Type, f *Field) *Sym { return Pkglookup(t.tconv(FmtLeft)+"."+f.Sym.Name, trackpkg) } -func typelinkLSym(t *Type) *obj.LSym { - name := "go.typelink." + t.tconv(FmtLeft) // complete, unambiguous type name - return obj.Linklookup(Ctxt, name, 0) -} - func typesymprefix(prefix string, t *Type) *Sym { p := prefix + "." + t.tconv(FmtLeft) s := Pkglookup(p, typepkg) @@ -1338,8 +1333,6 @@ ok: ot = dextratypeData(s, ot, t) ggloblsym(s, int32(ot), int16(dupok|obj.RODATA)) - // generate typelink.foo pointing at s = type.foo. - // // The linker will leave a table of all the typelinks for // types in the binary, so the runtime can find them. // @@ -1357,11 +1350,7 @@ ok: keep = true } } - if keep { - slink := typelinkLSym(t) - dsymptrOffLSym(slink, 0, Linksym(s), 0) - ggloblLSym(slink, 4, int16(dupok|obj.RODATA)) - } + s.Lsym.MakeTypelink = keep return s } diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index 46d249f88a..aa2bbf2b28 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -330,6 +330,9 @@ type LSym struct { Seenglobl bool Onlist bool + // MakeTypelink means that the type should have an entry in the typelink table. + MakeTypelink bool + // ReflectMethod means the function may call reflect.Type.Method or // reflect.Type.MethodByName. Matching is imprecise (as reflect.Type // can be used through a custom interface), so ReflectMethod may be diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index f8d61cd1c2..0a269afdca 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -52,7 +52,9 @@ // - type [int] // - name & version [symref index] // - flags [int] -// 1 dupok +// 1<<0 dupok +// 1<<1 local +// 1<<2 add to typelink table // - size [int] // - gotype [symref index] // - p [data block] @@ -395,6 +397,9 @@ func (w *objWriter) writeSym(s *LSym) { if s.Local { flags |= 1 << 1 } + if s.MakeTypelink { + flags |= 1 << 2 + } w.writeInt(flags) w.writeInt(s.Size) w.writeRefIndex(s.Gotype) diff --git a/src/cmd/internal/obj/sizeof_test.go b/src/cmd/internal/obj/sizeof_test.go index 65028ab4b9..391ed37efb 100644 --- a/src/cmd/internal/obj/sizeof_test.go +++ b/src/cmd/internal/obj/sizeof_test.go @@ -23,7 +23,7 @@ func TestSizeof(t *testing.T) { _64bit uintptr // size on 64bit platforms }{ {Addr{}, 40, 64}, - {LSym{}, 80, 136}, + {LSym{}, 84, 136}, {Prog{}, 144, 224}, } diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 5460b76c99..73c0daa77c 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1717,15 +1717,10 @@ func (ctxt *Link) dodata() { sect.Align = dataMaxAlign[obj.STYPELINK] datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) - ctxt.Syms.Lookup("runtime.typelink", 0).Sect = sect - ctxt.Syms.Lookup("runtime.etypelink", 0).Sect = sect - for _, s := range data[obj.STYPELINK] { - datsize = aligndatsize(datsize, s) - s.Sect = sect - s.Type = obj.SRODATA - s.Value = int64(uint64(datsize) - sect.Vaddr) - datsize += s.Size - } + typelink := ctxt.Syms.Lookup("runtime.typelink", 0) + typelink.Sect = sect + typelink.Type = obj.RODATA + datsize += typelink.Size checkdatsize(ctxt, datsize, obj.STYPELINK) sect.Length = uint64(datsize) - sect.Vaddr @@ -1909,10 +1904,6 @@ func dodataSect(ctxt *Link, symn obj.SymKind, syms []*Symbol) (result []*Symbol, // we skip size comparison and fall through to the name // comparison (conveniently, .got sorts before .toc). key.size = 0 - case obj.STYPELINK: - // Sort typelinks by the rtype.string field so the reflect - // package can binary search type links. - key.name = string(decodetypeStr(s.R[0].Sym)) } symsSort = append(symsSort, key) @@ -2235,7 +2226,6 @@ func (ctxt *Link) address() { var ( text = Segtext.Sect rodata = ctxt.Syms.Lookup("runtime.rodata", 0).Sect - typelink = ctxt.Syms.Lookup("runtime.typelink", 0).Sect itablink = ctxt.Syms.Lookup("runtime.itablink", 0).Sect symtab = ctxt.Syms.Lookup("runtime.symtab", 0).Sect pclntab = ctxt.Syms.Lookup("runtime.pclntab", 0).Sect @@ -2291,8 +2281,6 @@ func (ctxt *Link) address() { ctxt.xdefine("runtime.erodata", obj.SRODATA, int64(rodata.Vaddr+rodata.Length)) ctxt.xdefine("runtime.types", obj.SRODATA, int64(types.Vaddr)) ctxt.xdefine("runtime.etypes", obj.SRODATA, int64(types.Vaddr+types.Length)) - ctxt.xdefine("runtime.typelink", obj.SRODATA, int64(typelink.Vaddr)) - ctxt.xdefine("runtime.etypelink", obj.SRODATA, int64(typelink.Vaddr+typelink.Length)) ctxt.xdefine("runtime.itablink", obj.SRODATA, int64(itablink.Vaddr)) ctxt.xdefine("runtime.eitablink", obj.SRODATA, int64(itablink.Vaddr+itablink.Length)) diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index 696a55849c..7ecc5b1ec2 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -110,11 +110,10 @@ func deadcode(ctxt *Link) { } if Buildmode != BuildmodeShared { - // Keep a typelink or itablink if the symbol it points at is being kept. - // (When BuildmodeShared, always keep typelinks and itablinks.) + // Keep a itablink if the symbol it points at is being kept. + // (When BuildmodeShared, always keep itablinks.) for _, s := range ctxt.Syms.Allsym { - if strings.HasPrefix(s.Name, "go.typelink.") || - strings.HasPrefix(s.Name, "go.itablink.") { + if strings.HasPrefix(s.Name, "go.itablink.") { s.Attr.Set(AttrReachable, len(s.R) == 1 && s.R[0].Sym.Attr.Reachable()) } } diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index b6bde4cdfb..ab7e49b51f 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -105,6 +105,7 @@ const ( AttrOnList AttrLocal AttrReflectMethod + AttrMakeTypelink ) func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 } @@ -119,6 +120,7 @@ func (a Attribute) Hidden() bool { return a&AttrHidden != 0 } func (a Attribute) OnList() bool { return a&AttrOnList != 0 } func (a Attribute) Local() bool { return a&AttrLocal != 0 } func (a Attribute) ReflectMethod() bool { return a&AttrReflectMethod != 0 } +func (a Attribute) MakeTypelink() bool { return a&AttrMakeTypelink != 0 } func (a Attribute) CgoExport() bool { return a.CgoExportDynamic() || a.CgoExportStatic() diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 40adf96f71..85af07d5af 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -201,6 +201,7 @@ func Main() { ctxt.textaddress() ctxt.pclntab() ctxt.findfunctab() + ctxt.typelink() ctxt.symtab() ctxt.dodata() ctxt.address() diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go index ce666dc57b..7626a4fbc1 100644 --- a/src/cmd/link/internal/ld/objfile.go +++ b/src/cmd/link/internal/ld/objfile.go @@ -54,7 +54,9 @@ package ld // - type [int] // - name & version [symref index] // - flags [int] -// 1 dupok +// 1<<0 dupok +// 1<<1 local +// 1<<2 add to typelink table // - size [int] // - gotype [symref index] // - p [data block] @@ -264,6 +266,7 @@ func (r *objReader) readSym() { flags := r.readInt() dupok := flags&1 != 0 local := flags&2 != 0 + makeTypelink := flags&4 != 0 size := r.readInt() typ := r.readSymIndex() data := r.readData() @@ -315,6 +318,7 @@ overwrite: s.Size = int64(size) } s.Attr.Set(AttrLocal, local) + s.Attr.Set(AttrMakeTypelink, makeTypelink) if typ != nil { s.Gotype = typ } diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 97c6c2db72..74d2d3d93a 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -366,8 +366,6 @@ func (ctxt *Link) symtab() { ctxt.xdefine("runtime.text", obj.STEXT, 0) ctxt.xdefine("runtime.etext", obj.STEXT, 0) - ctxt.xdefine("runtime.typelink", obj.SRODATA, 0) - ctxt.xdefine("runtime.etypelink", obj.SRODATA, 0) ctxt.xdefine("runtime.itablink", obj.SRODATA, 0) ctxt.xdefine("runtime.eitablink", obj.SRODATA, 0) ctxt.xdefine("runtime.rodata", obj.SRODATA, 0) @@ -449,9 +447,6 @@ func (ctxt *Link) symtab() { } } - symtypelink := ctxt.Syms.Lookup("runtime.typelink", 0) - symtypelink.Type = obj.STYPELINK - symitablink := ctxt.Syms.Lookup("runtime.itablink", 0) symitablink.Type = obj.SITABLINK @@ -461,7 +456,6 @@ func (ctxt *Link) symtab() { symt.Size = 0 symt.Attr |= AttrReachable - ntypelinks := 0 nitablinks := 0 // assign specific types so that they sort together. @@ -491,12 +485,6 @@ func (ctxt *Link) symtab() { // names, as they can be referred to by a section offset. s.Type = obj.STYPERELRO - case strings.HasPrefix(s.Name, "go.typelink."): - ntypelinks++ - s.Type = obj.STYPELINK - s.Attr |= AttrHidden - s.Outer = symtypelink - case strings.HasPrefix(s.Name, "go.itablink."): nitablinks++ s.Type = obj.SITABLINK @@ -590,9 +578,11 @@ func (ctxt *Link) symtab() { adduint(ctxt, moduledata, uint64(nsections)) // The typelinks slice - Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.typelink", 0)) - adduint(ctxt, moduledata, uint64(ntypelinks)) - adduint(ctxt, moduledata, uint64(ntypelinks)) + typelinkSym := ctxt.Syms.Lookup("runtime.typelink", 0) + ntypelinks := uint64(typelinkSym.Size) / 4 + Addaddr(ctxt, moduledata, typelinkSym) + adduint(ctxt, moduledata, ntypelinks) + adduint(ctxt, moduledata, ntypelinks) // The itablinks slice Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.itablink", 0)) adduint(ctxt, moduledata, uint64(nitablinks)) diff --git a/src/cmd/link/internal/ld/typelink.go b/src/cmd/link/internal/ld/typelink.go new file mode 100644 index 0000000000..48a1104998 --- /dev/null +++ b/src/cmd/link/internal/ld/typelink.go @@ -0,0 +1,49 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "sort" + + "cmd/internal/obj" +) + +type byTypeStr []typelinkSortKey + +type typelinkSortKey struct { + TypeStr string + Type *Symbol +} + +func (s byTypeStr) Less(i, j int) bool { return s[i].TypeStr < s[j].TypeStr } +func (s byTypeStr) Len() int { return len(s) } +func (s byTypeStr) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// typelink generates the typelink table which is used by reflect.typelinks(). +// Types that should be added to the typelinks table are marked with the +// MakeTypelink attribute by the compiler. +func (ctxt *Link) typelink() { + typelinks := byTypeStr{} + for _, s := range ctxt.Syms.Allsym { + if s.Attr.Reachable() && s.Attr.MakeTypelink() { + typelinks = append(typelinks, typelinkSortKey{decodetypeStr(s), s}) + } + } + sort.Sort(typelinks) + + tl := ctxt.Syms.Lookup("runtime.typelink", 0) + tl.Type = obj.STYPELINK + tl.Attr |= AttrReachable | AttrLocal + tl.Size = int64(4 * len(typelinks)) + tl.P = make([]byte, tl.Size) + tl.R = make([]Reloc, len(typelinks)) + for i, s := range typelinks { + r := &tl.R[i] + r.Sym = s.Type + r.Off = int32(i * 4) + r.Siz = 4 + r.Type = obj.R_ADDROFF + } +}