From c6e11fe03765e3fe1fc68bd794625ca0ecd833be Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 6 Apr 2016 12:01:40 -0700 Subject: cmd: add new common architecture representation Information about CPU architectures (e.g., name, family, byte ordering, pointer and register size) is currently redundantly scattered around the source tree. Instead consolidate the basic information into a single new package cmd/internal/sys. Also, introduce new sys.I386, sys.AMD64, etc. names for the constants '8', '6', etc. and replace most uses of the latter. The notable exceptions are a couple of error messages that still refer to the old char-based toolchain names and function reltype in cmd/link. Passes toolstash/buildall. Change-Id: I8a6f0cbd49577ec1672a98addebc45f767e36461 Reviewed-on: https://go-review.googlesource.com/21623 Reviewed-by: Michael Hudson-Doyle Reviewed-by: Brad Fitzpatrick Run-TryBot: Matthew Dempsky TryBot-Result: Gobot Gobot --- src/cmd/link/internal/ld/deadcode.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/cmd/link/internal/ld/deadcode.go') diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index 56c4370bcc..b17b96001e 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -6,6 +6,7 @@ package ld import ( "cmd/internal/obj" + "cmd/internal/sys" "fmt" "strings" "unicode" @@ -227,7 +228,7 @@ func (d *deadcodepass) markMethod(m methodref) { func (d *deadcodepass) init() { var names []string - if Thearch.Thechar == '5' { + if SysArch.Family == sys.ARM { // mark some functions that are only referenced after linker code editing if d.ctxt.Goarm == 5 { names = append(names, "_sfloat") -- cgit v1.3 From bce9747ed00c53e7ddeea102e87aede1b3ec9bd3 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Sat, 9 Apr 2016 15:04:45 +1000 Subject: cmd: remove unused code Generated with honnef.co/go/unused There is a large amount of unused code in cmd/internal/obj/s390x but that can wait til the s390x port is merged. There is some unused code in cmd/internal/unvendor/golang.org/x/arch/arm/armasm but that should be addressed upstream and a new revision imported. Change-Id: I252c0f9ea8c5bb1a0b530a374ef13a0a20ea56aa Reviewed-on: https://go-review.googlesource.com/21782 Reviewed-by: Brad Fitzpatrick Run-TryBot: Dave Cheney --- src/cmd/internal/goobj/read.go | 7 ++---- src/cmd/internal/obj/mips/asm0.go | 4 ---- src/cmd/internal/obj/x86/asm6.go | 5 ----- src/cmd/internal/obj/x86/obj6_test.go | 1 - src/cmd/internal/objfile/pe.go | 2 -- src/cmd/link/internal/amd64/asm.go | 6 ----- src/cmd/link/internal/arm/asm.go | 5 ----- src/cmd/link/internal/arm64/asm.go | 4 ---- src/cmd/link/internal/ld/data.go | 4 ---- src/cmd/link/internal/ld/deadcode.go | 4 +--- src/cmd/link/internal/ld/decodesym.go | 18 --------------- src/cmd/link/internal/ld/go.go | 30 +------------------------ src/cmd/link/internal/ld/macho.go | 7 ++---- src/cmd/link/internal/ld/macho_combine_dwarf.go | 4 +--- src/cmd/link/internal/mips64/asm.go | 4 ---- src/cmd/link/internal/ppc64/asm.go | 4 ---- src/cmd/link/internal/x86/asm.go | 4 ---- 17 files changed, 7 insertions(+), 106 deletions(-) (limited to 'src/cmd/link/internal/ld/deadcode.go') diff --git a/src/cmd/internal/goobj/read.go b/src/cmd/internal/goobj/read.go index 5434661384..698d58efe0 100644 --- a/src/cmd/internal/goobj/read.go +++ b/src/cmd/internal/goobj/read.go @@ -229,11 +229,8 @@ var ( errCorruptArchive = errors.New("corrupt archive") errTruncatedArchive = errors.New("truncated archive") - errNotArchive = errors.New("unrecognized archive format") - - errCorruptObject = errors.New("corrupt object file") - errTruncatedObject = errors.New("truncated object file") - errNotObject = errors.New("unrecognized object file format") + errCorruptObject = errors.New("corrupt object file") + errNotObject = errors.New("unrecognized object file format") ) // An objReader is an object file reader. diff --git a/src/cmd/internal/obj/mips/asm0.go b/src/cmd/internal/obj/mips/asm0.go index 521cb66dec..5cb5d1cfd9 100644 --- a/src/cmd/internal/obj/mips/asm0.go +++ b/src/cmd/internal/obj/mips/asm0.go @@ -974,10 +974,6 @@ func OP_JMP(op uint32, i uint32) uint32 { return op | i&0x3FFFFFF } -func oclass(a *obj.Addr) int { - return int(a.Class) - 1 -} - func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) { o1 := uint32(0) o2 := uint32(0) diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go index b940094b8b..c15b59b5e8 100644 --- a/src/cmd/internal/obj/x86/asm6.go +++ b/src/cmd/internal/obj/x86/asm6.go @@ -884,11 +884,6 @@ var yvex_vpbroadcast = []ytab{ {Yxm, Ynone, Yyr, Zvex_rm_v_r, 2}, } -var yvex_xxmyxm = []ytab{ - {Yxr, Ynone, Yxm, Zvex_r_v_rm, 2}, - {Yyr, Ynone, Yxm, Zvex_r_v_rm, 2}, -} - var ymmxmm0f38 = []ytab{ {Ymm, Ynone, Ymr, Zlitm_r, 3}, {Yxm, Ynone, Yxr, Zlitm_r, 5}, diff --git a/src/cmd/internal/obj/x86/obj6_test.go b/src/cmd/internal/obj/x86/obj6_test.go index a5c80cea3b..fe1f95cc0d 100644 --- a/src/cmd/internal/obj/x86/obj6_test.go +++ b/src/cmd/internal/obj/x86/obj6_test.go @@ -76,7 +76,6 @@ func parseTestData(t *testing.T) *ParsedTestData { } var spaces_re *regexp.Regexp = regexp.MustCompile("\\s+") -var marker_re *regexp.Regexp = regexp.MustCompile("MOVQ \\$([0-9]+), AX") func normalize(s string) string { return spaces_re.ReplaceAllLiteralString(strings.TrimSpace(s), " ") diff --git a/src/cmd/internal/objfile/pe.go b/src/cmd/internal/objfile/pe.go index 1b319941ac..c024762371 100644 --- a/src/cmd/internal/objfile/pe.go +++ b/src/cmd/internal/objfile/pe.go @@ -69,8 +69,6 @@ func (f *peFile) symbols() ([]Sym, error) { text = 0x20 data = 0x40 bss = 0x80 - permX = 0x20000000 - permR = 0x40000000 permW = 0x80000000 ) ch := sect.Characteristics diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index 8cecd422e1..a6dce6c2c9 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -99,12 +99,6 @@ func gentext() { ld.Addaddr(ld.Ctxt, initarray_entry, initfunc) } -func adddynrela(rela *ld.LSym, s *ld.LSym, r *ld.Reloc) { - ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off)) - ld.Adduint64(ld.Ctxt, rela, ld.R_X86_64_RELATIVE) - ld.Addaddrplus(ld.Ctxt, rela, r.Sym, r.Add) // Addend -} - func adddynrel(s *ld.LSym, r *ld.Reloc) { targ := r.Sym ld.Ctxt.Cursym = s diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index b89cb20bdf..1188615716 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -114,11 +114,6 @@ func braddoff(a int32, b int32) int32 { return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b)) } -func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { - ld.Addaddrplus(ld.Ctxt, rel, s, int64(r.Off)) - ld.Adduint32(ld.Ctxt, rel, ld.R_ARM_RELATIVE) -} - func adddynrel(s *ld.LSym, r *ld.Reloc) { targ := r.Sym ld.Ctxt.Cursym = s diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index fd8929dd99..d3ba5ff3f3 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -91,10 +91,6 @@ func gentext() { ld.Addaddr(ld.Ctxt, initarray_entry, initfunc) } -func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { - log.Fatalf("adddynrela not implemented") -} - func adddynrel(s *ld.LSym, r *ld.Reloc) { log.Fatalf("adddynrel not implemented") } diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index cd910b54c0..2c8cc9ca4f 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -236,10 +236,6 @@ func addaddrplus4(ctxt *Link, s *LSym, t *LSym, add int64) int64 { * Used for the data block. */ -func listnextp(s *LSym) **LSym { - return &s.Next -} - func listsubp(s *LSym) **LSym { return &s.Sub } diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index b17b96001e..8b2d0d447e 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -170,9 +170,7 @@ type methodref struct { r [3]*Reloc // R_METHOD relocations to fields of runtime.method } -func (m methodref) mtyp() *LSym { return m.r[0].Sym } -func (m methodref) ifn() *LSym { return m.r[1].Sym } -func (m methodref) tfn() *LSym { return m.r[2].Sym } +func (m methodref) ifn() *LSym { return m.r[1].Sym } func (m methodref) isExported() bool { for _, r := range m.m { diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index bc29938590..1066d220f7 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -56,11 +56,6 @@ func decodetype_kind(s *LSym) uint8 { return uint8(s.P[2*SysArch.PtrSize+7] & obj.KindMask) // 0x13 / 0x1f } -// Type.commonType.kind -func decodetype_noptr(s *LSym) uint8 { - return uint8(s.P[2*SysArch.PtrSize+7] & obj.KindNoPointers) // 0x13 / 0x1f -} - // Type.commonType.kind func decodetype_usegcprog(s *LSym) uint8 { return uint8(s.P[2*SysArch.PtrSize+7] & obj.KindGCProg) // 0x13 / 0x1f @@ -216,19 +211,6 @@ func decodetype_structfieldarrayoff(s *LSym, i int) int { return off } -func decodetype_stringptr(s *LSym, off int) string { - s = decode_reloc_sym(s, int32(off)) - if s == nil { - return "" - } - r := decode_reloc(s, 0) // s has a pointer to the string data at offset 0 - if r == nil { // shouldn't happen. - return "" - } - strlen := int64(decode_inuxi(s.P[SysArch.PtrSize:], SysArch.IntSize)) - return string(r.Sym.P[r.Add : r.Add+strlen]) -} - // decodetype_name decodes the name from a reflect.name. func decodetype_name(s *LSym, off int) string { r := decode_reloc(s, int32(off)) diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go index 5dad90dae6..3af5f7a046 100644 --- a/src/cmd/link/internal/ld/go.go +++ b/src/cmd/link/internal/ld/go.go @@ -419,35 +419,7 @@ type Pkg struct { impby []*Pkg } -var ( - // pkgmap records the imported-by relationship between packages. - // Entries are keyed by package path (e.g., "runtime" or "net/url"). - pkgmap = map[string]*Pkg{} - - pkgall []*Pkg -) - -func lookupPkg(path string) *Pkg { - if p, ok := pkgmap[path]; ok { - return p - } - p := &Pkg{path: path} - pkgmap[path] = p - pkgall = append(pkgall, p) - return p -} - -// imported records that package pkg imports package imp. -func imported(pkg, imp string) { - // everyone imports runtime, even runtime. - if imp == "runtime" { - return - } - - p := lookupPkg(pkg) - i := lookupPkg(imp) - i.impby = append(i.impby, p) -} +var pkgall []*Pkg func (p *Pkg) cycle() *Pkg { if p.checked { diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index 25d48fbf22..1d9a1a9324 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -586,11 +586,8 @@ func Asmbmacho() { // and we can assume OS X. // // See golang.org/issues/12941. - const ( - LC_VERSION_MIN_MACOSX = 0x24 - LC_VERSION_MIN_IPHONEOS = 0x25 - LC_VERSION_MIN_WATCHOS = 0x30 - ) + const LC_VERSION_MIN_MACOSX = 0x24 + ml := newMachoLoad(LC_VERSION_MIN_MACOSX, 2) ml.data[0] = 10<<16 | 7<<8 | 0<<0 // OS X version 10.7.0 ml.data[1] = 10<<16 | 7<<8 | 0<<0 // SDK 10.7.0 diff --git a/src/cmd/link/internal/ld/macho_combine_dwarf.go b/src/cmd/link/internal/ld/macho_combine_dwarf.go index b5a5a8d429..dcc371ec05 100644 --- a/src/cmd/link/internal/ld/macho_combine_dwarf.go +++ b/src/cmd/link/internal/ld/macho_combine_dwarf.go @@ -15,11 +15,9 @@ import ( "unsafe" ) -var fakedwarf, realdwarf, linkseg *macho.Segment +var realdwarf, linkseg *macho.Segment var dwarfstart, linkstart int64 var linkoffset uint32 -var machHeader *macho.FileHeader -var mappedHeader []byte const ( LC_ID_DYLIB = 0xd diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go index 027736cc11..ad6a1f7524 100644 --- a/src/cmd/link/internal/mips64/asm.go +++ b/src/cmd/link/internal/mips64/asm.go @@ -41,10 +41,6 @@ import ( func gentext() {} -func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { - log.Fatalf("adddynrela not implemented") -} - func adddynrel(s *ld.LSym, r *ld.Reloc) { log.Fatalf("adddynrel not implemented") } diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 13d80545c7..3970f3c5f9 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -265,10 +265,6 @@ func gencallstub(abicase int, stub *ld.LSym, targ *ld.LSym) { ld.Adduint32(ld.Ctxt, stub, 0x4e800420) // bctr } -func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { - log.Fatalf("adddynrela not implemented") -} - func adddynrel(s *ld.LSym, r *ld.Reloc) { targ := r.Sym ld.Ctxt.Cursym = s diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index a786ba5a48..19a8917ec8 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -139,10 +139,6 @@ func gentext() { ld.Addaddr(ld.Ctxt, initarray_entry, initfunc) } -func adddynrela(rela *ld.LSym, s *ld.LSym, r *ld.Reloc) { - log.Fatalf("adddynrela not implemented") -} - func adddynrel(s *ld.LSym, r *ld.Reloc) { targ := r.Sym ld.Ctxt.Cursym = s -- cgit v1.3 From b0cbe158da10aac1876680e825a902d58a9d1bac Mon Sep 17 00:00:00 2001 From: Shahar Kohanim Date: Mon, 11 Apr 2016 22:19:34 +0300 Subject: cmd/link: move function only lsym fields to pcln struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit name old secs new secs delta LinkCmdGo 0.53 ± 9% 0.53 ±10% -1.30% (p=0.022 n=100+99) name old MaxRSS new MaxRSS delta LinkCmdGo 151k ± 4% 142k ± 6% -5.92% (p=0.000 n=98+100) Change-Id: Ic30e63a948f8e626b3396f458a0163f7234810c1 Reviewed-on: https://go-review.googlesource.com/21920 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/cmd/link/internal/ld/deadcode.go | 7 +++++-- src/cmd/link/internal/ld/dwarf.go | 2 +- src/cmd/link/internal/ld/lib.go | 17 ++++++++++++++--- src/cmd/link/internal/ld/link.go | 6 +++--- src/cmd/link/internal/ld/objfile.go | 13 +++++++------ src/cmd/link/internal/ld/pcln.go | 6 +++++- 6 files changed, 35 insertions(+), 16 deletions(-) (limited to 'src/cmd/link/internal/ld/deadcode.go') diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index 8b2d0d447e..83e4cdc077 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -272,9 +272,12 @@ func (d *deadcodepass) flood() { if Debug['v'] > 1 { fmt.Fprintf(d.ctxt.Bso, "marktext %s\n", s.Name) } - for _, a := range s.Autom { - d.mark(a.Gotype, s) + if s.Pcln != nil { + for _, a := range s.Pcln.Autom { + d.mark(a.Gotype, s) + } } + } if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' { diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index a3a931f94c..82689988c5 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -1556,7 +1556,7 @@ func writelines(prev *LSym) *LSym { dt, da int offs int64 ) - for _, a := range s.Autom { + for _, a := range s.Pcln.Autom { switch a.Name { case obj.A_AUTO: dt = DW_ABRV_AUTO diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 1f2df8b9c5..db34e68404 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1747,7 +1747,11 @@ func stkcheck(up *Chain, depth int) int { return 0 } // Raise limit to allow frame. - limit = int(obj.StackLimit+s.Locals) + int(Ctxt.FixedFrameSize()) + locals := int32(0) + if s.Pcln != nil { + locals = s.Pcln.Locals + } + limit = int(obj.StackLimit+locals) + int(Ctxt.FixedFrameSize()) } // Walk through sp adjustments in function, consuming relocs. @@ -1978,10 +1982,17 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { for s := Ctxt.Textp; s != nil; s = s.Next { put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), s.Gotype) + locals := int32(0) + if s.Pcln != nil { + locals = s.Pcln.Locals + } // NOTE(ality): acid can't produce a stack trace without .frame symbols - put(nil, ".frame", 'm', int64(s.Locals)+int64(SysArch.PtrSize), 0, 0, nil) + put(nil, ".frame", 'm', int64(locals)+int64(SysArch.PtrSize), 0, 0, nil) - for _, a := range s.Autom { + if s.Pcln == nil { + continue + } + for _, a := range s.Pcln.Autom { // Emit a or p according to actual offset, even if label is wrong. // This avoids negative offsets, which cannot be encoded. if a.Name != obj.A_AUTO && a.Name != obj.A_PARAM { diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index 52b52f1cc0..93454fb4b2 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -50,8 +50,6 @@ type LSym struct { Align int32 Elfsym int32 LocalElfsym int32 - Args int32 - Locals int32 Value int64 Size int64 // ElfType is set for symbols read from shared libraries by ldshlibsyms. It @@ -67,7 +65,6 @@ type LSym struct { Dynimplib string Dynimpvers string Sect *Section - Autom []Auto Pcln *Pcln P []byte R []Reloc @@ -221,6 +218,9 @@ type Library struct { } type Pcln struct { + Args int32 + Locals int32 + Autom []Auto Pcsp Pcdata Pcfile Pcdata Pcline Pcdata diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go index 578afd4c74..eacccb59fb 100644 --- a/src/cmd/link/internal/ld/objfile.go +++ b/src/cmd/link/internal/ld/objfile.go @@ -331,8 +331,11 @@ overwrite: } if s.Type == obj.STEXT { - s.Args = r.readInt32() - s.Locals = r.readInt32() + s.Pcln = new(Pcln) + pc := s.Pcln + + pc.Args = r.readInt32() + pc.Locals = r.readInt32() if r.readUint8() != 0 { s.Attr |= AttrNoSplit } @@ -341,13 +344,13 @@ overwrite: s.Attr |= AttrReflectMethod } n := r.readInt() - s.Autom = r.autom[:n:n] + pc.Autom = r.autom[:n:n] if !isdup { r.autom = r.autom[n:] } for i := 0; i < n; i++ { - s.Autom[i] = Auto{ + pc.Autom[i] = Auto{ Asym: r.readSymIndex(), Aoffset: r.readInt32(), Name: r.readInt16(), @@ -355,8 +358,6 @@ overwrite: } } - s.Pcln = new(Pcln) - pc := s.Pcln pc.Pcsp.P = r.readData() pc.Pcfile.P = r.readData() pc.Pcline.P = r.readData() diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go index 9a947c7c07..3ef52444af 100644 --- a/src/cmd/link/internal/ld/pcln.go +++ b/src/cmd/link/internal/ld/pcln.go @@ -293,7 +293,11 @@ func pclntab() { // args int32 // TODO: Move into funcinfo. - off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args))) + args := uint32(0) + if Ctxt.Cursym.Pcln != nil { + args = uint32(Ctxt.Cursym.Pcln.Args) + } + off = int32(setuint32(Ctxt, ftab, int64(off), args)) // frame int32 // This has been removed (it was never set quite correctly anyway). -- cgit v1.3 From 61b7a9c57bb6b9c259360239001b2d5be4876abd Mon Sep 17 00:00:00 2001 From: Shahar Kohanim Date: Tue, 12 Apr 2016 23:18:47 +0300 Subject: cmd/link: rename Pcln to FuncInfo After non pcln fields were added to it in a previous commit. Change-Id: Icf92c0774d157c61399a6fc2a3c4d2cd47a634d2 Reviewed-on: https://go-review.googlesource.com/21921 Run-TryBot: Shahar Kohanim TryBot-Result: Gobot Gobot Reviewed-by: David Crawshaw --- src/cmd/link/internal/ld/deadcode.go | 10 +++++----- src/cmd/link/internal/ld/dwarf.go | 16 ++++++++-------- src/cmd/link/internal/ld/lib.go | 16 ++++++++-------- src/cmd/link/internal/ld/link.go | 4 ++-- src/cmd/link/internal/ld/objfile.go | 4 ++-- src/cmd/link/internal/ld/pcln.go | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) (limited to 'src/cmd/link/internal/ld/deadcode.go') diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index 83e4cdc077..51fae02ef0 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -272,8 +272,8 @@ func (d *deadcodepass) flood() { if Debug['v'] > 1 { fmt.Fprintf(d.ctxt.Bso, "marktext %s\n", s.Name) } - if s.Pcln != nil { - for _, a := range s.Pcln.Autom { + if s.FuncInfo != nil { + for _, a := range s.FuncInfo.Autom { d.mark(a.Gotype, s) } } @@ -335,9 +335,9 @@ func (d *deadcodepass) flood() { d.markableMethods = append(d.markableMethods, methods...) } - if s.Pcln != nil { - for i := range s.Pcln.Funcdata { - d.mark(s.Pcln.Funcdata[i], s) + if s.FuncInfo != nil { + for i := range s.FuncInfo.Funcdata { + d.mark(s.FuncInfo.Funcdata[i], s) } } d.mark(s.Gotype, s) diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 82689988c5..b1208b63a8 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -1345,8 +1345,8 @@ func finddebugruntimepath(s *LSym) { return } - for i := range s.Pcln.File { - f := s.Pcln.File[i] + for i := range s.FuncInfo.File { + f := s.FuncInfo.File[i] if i := strings.Index(f.Name, "runtime/runtime.go"); i >= 0 { gdbscript = f.Name[:i] + "runtime/runtime-gdb.py" break @@ -1514,14 +1514,14 @@ func writelines(prev *LSym) *LSym { newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0) } - if s.Pcln == nil { + if s.FuncInfo == nil { continue } finddebugruntimepath(s) - pciterinit(Ctxt, &pcfile, &s.Pcln.Pcfile) - pciterinit(Ctxt, &pcline, &s.Pcln.Pcline) + pciterinit(Ctxt, &pcfile, &s.FuncInfo.Pcfile) + pciterinit(Ctxt, &pcline, &s.FuncInfo.Pcline) epc = pc for pcfile.done == 0 && pcline.done == 0 { if epc-s.Value >= int64(pcfile.nextpc) { @@ -1556,7 +1556,7 @@ func writelines(prev *LSym) *LSym { dt, da int offs int64 ) - for _, a := range s.Pcln.Autom { + for _, a := range s.FuncInfo.Autom { switch a.Name { case obj.A_AUTO: dt = DW_ABRV_AUTO @@ -1698,14 +1698,14 @@ func writeframes(prev *LSym) *LSym { var pcsp Pciter for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { s := Ctxt.Cursym - if s.Pcln == nil { + if s.FuncInfo == nil { continue } // Emit a FDE, Section 6.4.1. // First build the section contents into a byte buffer. deltaBuf = deltaBuf[:0] - for pciterinit(Ctxt, &pcsp, &s.Pcln.Pcsp); pcsp.done == 0; pciternext(&pcsp) { + for pciterinit(Ctxt, &pcsp, &s.FuncInfo.Pcsp); pcsp.done == 0; pciternext(&pcsp) { nextpc := pcsp.nextpc // pciterinit goes up to the end of the function, diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index db34e68404..bdcc84a129 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1710,7 +1710,7 @@ func stkcheck(up *Chain, depth int) int { return -1 } - if s.Attr.External() || s.Pcln == nil { + if s.Attr.External() || s.FuncInfo == nil { // external function. // should never be called directly. // only diagnose the direct caller. @@ -1748,8 +1748,8 @@ func stkcheck(up *Chain, depth int) int { } // Raise limit to allow frame. locals := int32(0) - if s.Pcln != nil { - locals = s.Pcln.Locals + if s.FuncInfo != nil { + locals = s.FuncInfo.Locals } limit = int(obj.StackLimit+locals) + int(Ctxt.FixedFrameSize()) } @@ -1761,7 +1761,7 @@ func stkcheck(up *Chain, depth int) int { var ch1 Chain var pcsp Pciter var r *Reloc - for pciterinit(Ctxt, &pcsp, &s.Pcln.Pcsp); pcsp.done == 0; pciternext(&pcsp) { + for pciterinit(Ctxt, &pcsp, &s.FuncInfo.Pcsp); pcsp.done == 0; pciternext(&pcsp) { // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc). // Check stack size in effect for this span. @@ -1983,16 +1983,16 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), s.Gotype) locals := int32(0) - if s.Pcln != nil { - locals = s.Pcln.Locals + if s.FuncInfo != nil { + locals = s.FuncInfo.Locals } // NOTE(ality): acid can't produce a stack trace without .frame symbols put(nil, ".frame", 'm', int64(locals)+int64(SysArch.PtrSize), 0, 0, nil) - if s.Pcln == nil { + if s.FuncInfo == nil { continue } - for _, a := range s.Pcln.Autom { + for _, a := range s.FuncInfo.Autom { // Emit a or p according to actual offset, even if label is wrong. // This avoids negative offsets, which cannot be encoded. if a.Name != obj.A_AUTO && a.Name != obj.A_PARAM { diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index 93454fb4b2..b0bca4300f 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -65,7 +65,7 @@ type LSym struct { Dynimplib string Dynimpvers string Sect *Section - Pcln *Pcln + FuncInfo *FuncInfo P []byte R []Reloc } @@ -217,7 +217,7 @@ type Library struct { hash []byte } -type Pcln struct { +type FuncInfo struct { Args int32 Locals int32 Autom []Auto diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go index eacccb59fb..6826737cae 100644 --- a/src/cmd/link/internal/ld/objfile.go +++ b/src/cmd/link/internal/ld/objfile.go @@ -331,8 +331,8 @@ overwrite: } if s.Type == obj.STEXT { - s.Pcln = new(Pcln) - pc := s.Pcln + s.FuncInfo = new(FuncInfo) + pc := s.FuncInfo pc.Args = r.readInt32() pc.Locals = r.readInt32() diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go index 3ef52444af..74ef8c2929 100644 --- a/src/cmd/link/internal/ld/pcln.go +++ b/src/cmd/link/internal/ld/pcln.go @@ -204,7 +204,7 @@ func container(s *LSym) int { // pclntab initializes the pclntab symbol with // runtime function and file name information. -var pclntab_zpcln Pcln +var pclntab_zpcln FuncInfo // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab. var pclntabNfunc int32 @@ -255,13 +255,13 @@ func pclntab() { var i int32 var it Pciter var off int32 - var pcln *Pcln + var pcln *FuncInfo for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { last = Ctxt.Cursym if container(Ctxt.Cursym) != 0 { continue } - pcln = Ctxt.Cursym.Pcln + pcln = Ctxt.Cursym.FuncInfo if pcln == nil { pcln = &pclntab_zpcln } @@ -294,8 +294,8 @@ func pclntab() { // args int32 // TODO: Move into funcinfo. args := uint32(0) - if Ctxt.Cursym.Pcln != nil { - args = uint32(Ctxt.Cursym.Pcln.Args) + if Ctxt.Cursym.FuncInfo != nil { + args = uint32(Ctxt.Cursym.FuncInfo.Args) } off = int32(setuint32(Ctxt, ftab, int64(off), args)) -- cgit v1.3 From 7d469179e6e3dafe16700b7fc1cf8683ad9453fa Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Mon, 28 Mar 2016 10:32:27 -0400 Subject: cmd/compile, etc: store method tables as offsets This CL introduces the typeOff type and a lookup method of the same name that can turn a typeOff offset into an *rtype. In a typical Go binary (built with buildmode=exe, pie, c-archive, or c-shared), there is one moduledata and all typeOff values are offsets relative to firstmoduledata.types. This makes computing the pointer cheap in typical programs. With buildmode=shared (and one day, buildmode=plugin) there are multiple modules whose relative offset is determined at runtime. We identify a type in the general case by the pair of the original *rtype that references it and its typeOff value. We determine the module from the original pointer, and then use the typeOff from there to compute the final *rtype. To ensure there is only one *rtype representing each type, the runtime initializes a typemap for each module, using any identical type from an earlier module when resolving that offset. This means that types computed from an offset match the type mapped by the pointer dynamic relocations. A series of followup CLs will replace other *rtype values with typeOff (and name/*string with nameOff). For types created at runtime by reflect, type offsets are treated as global IDs and reference into a reflect offset map kept by the runtime. darwin/amd64: cmd/go: -57KB (0.6%) jujud: -557KB (0.8%) linux/amd64 PIE: cmd/go: -361KB (3.0%) jujud: -3.5MB (4.2%) For #6853. Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96 Reviewed-on: https://go-review.googlesource.com/21285 Reviewed-by: Ian Lance Taylor Run-TryBot: David Crawshaw TryBot-Result: Gobot Gobot --- src/cmd/compile/internal/gc/reflect.go | 75 +++++--- src/cmd/internal/obj/link.go | 15 +- src/cmd/link/internal/ld/deadcode.go | 14 +- src/cmd/link/internal/ld/decodesym.go | 22 +-- src/reflect/export_test.go | 2 +- src/reflect/type.go | 267 +++++++++++++++++++++------- src/reflect/value.go | 15 +- src/runtime/iface.go | 10 +- src/runtime/proc.go | 3 +- src/runtime/runtime1.go | 33 ++++ src/runtime/symtab.go | 2 + src/runtime/type.go | 307 ++++++++++++++++++++++++++++++++- 12 files changed, 637 insertions(+), 128 deletions(-) (limited to 'src/cmd/link/internal/ld/deadcode.go') diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index ea67634260..2bd50b4665 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -75,7 +75,7 @@ func uncommonSize(t *Type) int { // Sizeof(runtime.uncommontype{}) if t.Sym == nil && len(methods(t)) == 0 { return 0 } - return 2*Widthptr + 2*Widthint + return 2 * Widthptr } func makefield(name string, t *Type) *Field { @@ -580,13 +580,23 @@ func dextratype(s *Sym, ot int, t *Type, dataAdd int) int { ot = dgopkgpath(s, ot, typePkg(t)) - // slice header - ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint+dataAdd) - - n := len(m) - ot = duintxx(s, ot, uint64(n), Widthint) - ot = duintxx(s, ot, uint64(n), Widthint) + dataAdd += Widthptr + 2 + 2 + if Widthptr == 8 { + dataAdd += 4 + } + mcount := len(m) + if mcount != int(uint16(mcount)) { + Fatalf("too many methods on %s: %d", t, mcount) + } + if dataAdd != int(uint16(dataAdd)) { + Fatalf("methods are too far away on %s: %d", t, dataAdd) + } + ot = duint16(s, ot, uint16(mcount)) + ot = duint16(s, ot, uint16(dataAdd)) + if Widthptr == 8 { + ot = duint32(s, ot, 0) // align for following pointers + } return ot } @@ -609,6 +619,7 @@ func typePkg(t *Type) *Pkg { // dextratypeData dumps the backing array for the []method field of // runtime.uncommontype. func dextratypeData(s *Sym, ot int, t *Type) int { + lsym := Linksym(s) for _, a := range methods(t) { // ../../../../runtime/type.go:/method exported := exportname(a.name) @@ -617,21 +628,24 @@ func dextratypeData(s *Sym, ot int, t *Type) int { pkg = a.pkg } ot = dname(s, ot, a.name, "", pkg, exported) - ot = dmethodptr(s, ot, dtypesym(a.mtype)) - ot = dmethodptr(s, ot, a.isym) - ot = dmethodptr(s, ot, a.tsym) + ot = dmethodptrOffLSym(lsym, ot, Linksym(dtypesym(a.mtype))) + ot = dmethodptrOffLSym(lsym, ot, Linksym(a.isym)) + ot = dmethodptrOffLSym(lsym, ot, Linksym(a.tsym)) + if Widthptr == 8 { + ot = duintxxLSym(lsym, ot, 0, 4) // pad to reflect.method size + } } return ot } -func dmethodptr(s *Sym, off int, x *Sym) int { - duintptr(s, off, 0) - r := obj.Addrel(Linksym(s)) - r.Off = int32(off) - r.Siz = uint8(Widthptr) - r.Sym = Linksym(x) - r.Type = obj.R_METHOD - return off + Widthptr +func dmethodptrOffLSym(s *obj.LSym, ot int, x *obj.LSym) int { + duintxxLSym(s, ot, 0, 4) + r := obj.Addrel(s) + r.Off = int32(ot) + r.Siz = 4 + r.Sym = x + r.Type = obj.R_METHODOFF + return ot + 4 } var kinds = []int{ @@ -1286,18 +1300,29 @@ ok: 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 reflect can find them. - // We only need the link for unnamed composites that - // we want be able to find. - if t.Sym == nil { + // types in the binary, so the runtime can find them. + // + // When buildmode=shared, all types are in typelinks so the + // runtime can deduplicate type pointers. + keep := Ctxt.Flag_dynlink + if !keep && t.Sym == nil { + // For an unnamed type, we only need the link if the type can + // be created at run time by reflect.PtrTo and similar + // functions. If the type exists in the program, those + // functions must return the existing type structure rather + // than creating a new one. switch t.Etype { case TPTR32, TPTR64, TARRAY, TCHAN, TFUNC, TMAP, TSTRUCT: - slink := typelinkLSym(t) - dsymptrOffLSym(slink, 0, Linksym(s), 0) - ggloblLSym(slink, 4, int16(dupok|obj.RODATA)) + keep = true } } + if keep { + slink := typelinkLSym(t) + dsymptrOffLSym(slink, 0, Linksym(s), 0) + ggloblLSym(slink, 4, int16(dupok|obj.RODATA)) + } return s } diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index 42aaa5f4f0..55c9f4f9e2 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -457,8 +457,8 @@ const ( // R_ADDRMIPS (only used on mips64) resolves to a 32-bit external address, // by loading the address into a register with two instructions (lui, ori). R_ADDRMIPS - // R_ADDROFF resolves to an offset from the beginning of the section holding - // the data being relocated to the referenced symbol. + // R_ADDROFF resolves to a 32-bit offset from the beginning of the section + // holding the data being relocated to the referenced symbol. R_ADDROFF R_SIZE R_CALL @@ -492,11 +492,12 @@ const ( // should be linked into the final binary, even if there are no other // direct references. (This is used for types reachable by reflection.) R_USETYPE - // R_METHOD resolves to an *rtype for a method. - // It is used when linking from the uncommonType of another *rtype, and - // may be set to zero by the linker if it determines the method text is - // unreachable by the linked program. - R_METHOD + // R_METHODOFF resolves to a 32-bit offset from the beginning of the section + // holding the data being relocated to the referenced symbol. + // It is a variant of R_ADDROFF used when linking from the uncommonType of a + // *rtype, and may be set to zero by the linker if it determines the method + // text is unreachable by the linked program. + R_METHODOFF R_POWER_TOC R_GOTPCREL // R_JMPMIPS (only used on mips64) resolves to non-PC-relative target address diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index 51fae02ef0..c83a104a54 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -19,7 +19,7 @@ import ( // // This flood fill is wrapped in logic for pruning unused methods. // All methods are mentioned by relocations on their receiver's *rtype. -// These relocations are specially defined as R_METHOD by the compiler +// These relocations are specially defined as R_METHODOFF by the compiler // so we can detect and manipulated them here. // // There are three ways a method of a reachable type can be invoked: @@ -100,7 +100,7 @@ func deadcode(ctxt *Link) { d.flood() } - // Remove all remaining unreached R_METHOD relocations. + // Remove all remaining unreached R_METHODOFF relocations. for _, m := range d.markableMethods { for _, r := range m.r { d.cleanupReloc(r) @@ -167,7 +167,7 @@ var markextra = []string{ type methodref struct { m methodsig src *LSym // receiver type symbol - r [3]*Reloc // R_METHOD relocations to fields of runtime.method + r [3]*Reloc // R_METHODOFF relocations to fields of runtime.method } func (m methodref) ifn() *LSym { return m.r[1].Sym } @@ -190,7 +190,7 @@ type deadcodepass struct { func (d *deadcodepass) cleanupReloc(r *Reloc) { if r.Sym.Attr.Reachable() { - r.Type = obj.R_ADDR + r.Type = obj.R_ADDROFF } else { if Debug['v'] > 1 { fmt.Fprintf(d.ctxt.Bso, "removing method %s\n", r.Sym.Name) @@ -217,7 +217,7 @@ func (d *deadcodepass) mark(s, parent *LSym) { func (d *deadcodepass) markMethod(m methodref) { for _, r := range m.r { d.mark(r.Sym, m.src) - r.Type = obj.R_ADDR + r.Type = obj.R_ADDROFF } } @@ -291,14 +291,14 @@ func (d *deadcodepass) flood() { } } - mpos := 0 // 0-3, the R_METHOD relocs of runtime.uncommontype + mpos := 0 // 0-3, the R_METHODOFF relocs of runtime.uncommontype var methods []methodref for i := 0; i < len(s.R); i++ { r := &s.R[i] if r.Sym == nil { continue } - if r.Type != obj.R_METHOD { + if r.Type != obj.R_METHODOFF { d.mark(r.Sym, s) continue } diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index 7daa8bc812..5fa8b4c81f 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -47,9 +47,9 @@ func decode_inuxi(p []byte, sz int) uint64 { } } -func commonsize() int { return 6*SysArch.PtrSize + 8 } // runtime._type -func structfieldSize() int { return 3 * SysArch.PtrSize } // runtime.structfield -func uncommonSize() int { return 2*SysArch.PtrSize + 2*SysArch.IntSize } // runtime.uncommontype +func commonsize() int { return 6*SysArch.PtrSize + 8 } // runtime._type +func structfieldSize() int { return 3 * SysArch.PtrSize } // runtime.structfield +func uncommonSize() int { return 2 * SysArch.PtrSize } // runtime.uncommontype // Type.commonType.kind func decodetype_kind(s *LSym) uint8 { @@ -341,12 +341,14 @@ func decodetype_methods(s *LSym) []methodsig { // just Sizeof(rtype) } - numMethods := int(decode_inuxi(s.P[off+2*SysArch.PtrSize:], SysArch.IntSize)) - r := decode_reloc(s, int32(off+SysArch.PtrSize)) - if r.Sym != s { - panic(fmt.Sprintf("method slice pointer in %s leads to a different symbol %s", s, r.Sym)) + mcount := int(decode_inuxi(s.P[off+SysArch.PtrSize:], 2)) + moff := int(decode_inuxi(s.P[off+SysArch.PtrSize+2:], 2)) + off += moff // offset to array of reflect.method values + var sizeofMethod int // sizeof reflect.method in program + if SysArch.PtrSize == 4 { + sizeofMethod = 4 * SysArch.PtrSize + } else { + sizeofMethod = 3 * SysArch.PtrSize } - off = int(r.Add) // array of reflect.method values - sizeofMethod := 4 * SysArch.PtrSize // sizeof reflect.method in program - return decode_methodsig(s, off, sizeofMethod, numMethods) + return decode_methodsig(s, off, sizeofMethod, mcount) } diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go index 037c953718..2769e0db40 100644 --- a/src/reflect/export_test.go +++ b/src/reflect/export_test.go @@ -90,7 +90,7 @@ func FirstMethodNameBytes(t Type) *byte { if ut == nil { panic("type has no methods") } - m := ut.methods[0] + m := ut.methods()[0] if *m.name.data(0)&(1<<2) == 0 { panic("method name does not have pkgPath *string") } diff --git a/src/reflect/type.go b/src/reflect/type.go index 7104fde60a..c7ed402be2 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -288,10 +288,10 @@ type typeAlg struct { // Method on non-interface type type method struct { - name name // name of method - mtyp *rtype // method type (without receiver) - ifn unsafe.Pointer // fn used in interface call (one-word receiver) - tfn unsafe.Pointer // fn used for normal method call + name name // name of method + mtyp typeOff // method type (without receiver) + ifn textOff // fn used in interface call (one-word receiver) + tfn textOff // fn used for normal method call } // uncommonType is present only for types with names or methods @@ -299,8 +299,9 @@ type method struct { // Using a pointer to this struct reduces the overall size required // to describe an unnamed type with no methods. type uncommonType struct { - pkgPath *string // import path; nil for built-in types like int, string - methods []method // methods associated with type + pkgPath *string // import path; nil for built-in types like int, string + mcount uint16 // number of methods + moff uint16 // offset from this uncommontype to [mcount]method } // ChanDir represents a channel type's direction. @@ -589,6 +590,10 @@ var kindNames = []string{ UnsafePointer: "unsafe.Pointer", } +func (t *uncommonType) methods() []method { + return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.moff)))[:t.mcount:t.mcount] +} + func (t *uncommonType) PkgPath() string { if t == nil || t.pkgPath == nil { return "" @@ -596,13 +601,55 @@ func (t *uncommonType) PkgPath() string { return *t.pkgPath } +// resolveTypeOff resolves an *rtype offset from a base type. +// The (*rtype).typeOff method is a convenience wrapper for this function. +// Implemented in the runtime package. +func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer + +// resolveTextOff resolves an function pointer offset from a base type. +// The (*rtype).textOff method is a convenience wrapper for this function. +// Implemented in the runtime package. +func resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer + +// addReflectOff adds a pointer to the reflection lookup map in the runtime. +// It returns a new ID that can be used as a typeOff or textOff, and will +// be resolved correctly. Implemented in the runtime package. +func addReflectOff(ptr unsafe.Pointer) int32 + +// resolveReflectType adds a *rtype to the reflection lookup map in the runtime. +// It returns a new typeOff that can be used to refer to the pointer. +func resolveReflectType(t *rtype) typeOff { + return typeOff(addReflectOff(unsafe.Pointer(t))) +} + +// resolveReflectText adds a function pointer to the reflection lookup map in +// the runtime. It returns a new textOff that can be used to refer to the +// pointer. +func resolveReflectText(ptr unsafe.Pointer) textOff { + return textOff(addReflectOff(ptr)) +} + +type typeOff int32 // offset to an *rtype +type textOff int32 // offset from top of text section + +func (t *rtype) typeOff(off typeOff) *rtype { + if off == 0 { + return nil + } + return (*rtype)(resolveTypeOff(unsafe.Pointer(t), int32(off))) +} + +func (t *rtype) textOff(off textOff) unsafe.Pointer { + return resolveTextOff(unsafe.Pointer(t), int32(off)) +} + func (t *rtype) uncommon() *uncommonType { if t.tflag&tflagUncommon == 0 { return nil } switch t.Kind() { case Struct: - return &(*structTypeWithMethods)(unsafe.Pointer(t)).u + return &(*structTypeUncommon)(unsafe.Pointer(t)).u case Ptr: type u struct { ptrType @@ -688,7 +735,7 @@ func (t *rtype) NumMethod() int { if ut == nil { return 0 } - return len(ut.methods) + return int(ut.mcount) } func (t *rtype) Method(i int) (m Method) { @@ -698,10 +745,10 @@ func (t *rtype) Method(i int) (m Method) { } ut := t.uncommon() - if ut == nil || i < 0 || i >= len(ut.methods) { + if ut == nil || i < 0 || i >= int(ut.mcount) { panic("reflect: Method index out of range") } - p := &ut.methods[i] + p := ut.methods()[i] m.Name = p.name.name() fl := flag(Func) if !p.name.isExported() { @@ -712,8 +759,9 @@ func (t *rtype) Method(i int) (m Method) { m.PkgPath = *pkgPath fl |= flagStickyRO } - if p.mtyp != nil { - ft := (*funcType)(unsafe.Pointer(p.mtyp)) + if p.mtyp != 0 { + mtyp := t.typeOff(p.mtyp) + ft := (*funcType)(unsafe.Pointer(mtyp)) in := make([]Type, 0, 1+len(ft.in())) in = append(in, t) for _, arg := range ft.in() { @@ -723,9 +771,10 @@ func (t *rtype) Method(i int) (m Method) { for _, ret := range ft.out() { out = append(out, ret) } - mt := FuncOf(in, out, p.mtyp.IsVariadic()) + mt := FuncOf(in, out, ft.IsVariadic()) m.Type = mt - fn := unsafe.Pointer(&p.tfn) + tfn := t.textOff(p.tfn) + fn := unsafe.Pointer(&tfn) m.Func = Value{mt.(*rtype), fn, fl} } m.Index = i @@ -741,8 +790,9 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) { if ut == nil { return Method{}, false } - for i := range ut.methods { - p := &ut.methods[i] + utmethods := ut.methods() + for i := 0; i < int(ut.mcount); i++ { + p := utmethods[i] if p.name.name() == name { return t.Method(i), true } @@ -1430,10 +1480,11 @@ func implements(T, V *rtype) bool { return false } i := 0 - for j := 0; j < len(v.methods); j++ { + vmethods := v.methods() + for j := 0; j < int(v.mcount); j++ { tm := &t.methods[i] - vm := &v.methods[j] - if vm.name.name() == tm.name.name() && vm.mtyp == tm.typ { + vm := vmethods[j] + if vm.name.name() == tm.name.name() && V.typeOff(vm.mtyp) == tm.typ { if i++; i >= len(t.methods) { return true } @@ -2161,21 +2212,55 @@ func SliceOf(t Type) Type { return cachePut(ckey, &slice.rtype) } -// structTypeWithMethods is a structType created at runtime with StructOf. -// It is needed to pin the []method slice from its associated uncommonType struct. -// Keep in sync with the memory layout of structType. -type structTypeWithMethods struct { - structType - u uncommonType -} - // The structLookupCache caches StructOf lookups. // StructOf does not share the common lookupCache since we need to pin -// the *structType and its associated *uncommonType (especially the -// []method slice field of that uncommonType.) +// the memory associated with *structTypeFixedN. var structLookupCache struct { sync.RWMutex - m map[uint32][]*structTypeWithMethods // keyed by hash calculated in StructOf + m map[uint32][]interface { + common() *rtype + } // keyed by hash calculated in StructOf +} + +type structTypeUncommon struct { + structType + u uncommonType +} + +// A *rtype representing a struct is followed directly in memory by an +// array of method objects representing the methods attached to the +// struct. To get the same layout for a run time generated type, we +// need an array directly following the uncommonType memory. The types +// structTypeFixed4, ...structTypeFixedN are used to do this. +// +// A similar strategy is used for funcTypeFixed4, ...funcTypeFixedN. + +// TODO(crawshaw): as these structTypeFixedN and funcTypeFixedN structs +// have no methods, they could be defined at runtime using the StructOf +// function. + +type structTypeFixed4 struct { + structType + u uncommonType + m [4]method +} + +type structTypeFixed8 struct { + structType + u uncommonType + m [8]method +} + +type structTypeFixed16 struct { + structType + u uncommonType + m [16]method +} + +type structTypeFixed32 struct { + structType + u uncommonType + m [32]method } // StructOf returns the struct type containing fields. @@ -2192,7 +2277,7 @@ func StructOf(fields []StructField) Type { typalign uint8 comparable = true hashable = true - typ = new(structTypeWithMethods) + methods []method fs = make([]structField, len(fields)) repr = make([]byte, 0, 64) @@ -2269,7 +2354,6 @@ func StructOf(fields []StructField) Type { } return recv.Field(ifield).Method(imethod).Call(args) }) - } else { tfn = MakeFunc(m.typ, func(in []Value) []Value { var args []Value @@ -2287,47 +2371,59 @@ func StructOf(fields []StructField) Type { } return recv.Field(ifield).Method(imethod).Call(args) }) - } - typ.u.methods = append( - typ.u.methods, - method{ - name: m.name, - mtyp: m.typ, - ifn: unsafe.Pointer(&ifn), - tfn: unsafe.Pointer(&tfn), - }, - ) + methods = append(methods, method{ + name: m.name, + mtyp: resolveReflectType(m.typ), + ifn: resolveReflectText(unsafe.Pointer(&ifn)), + tfn: resolveReflectText(unsafe.Pointer(&tfn)), + }) } case Ptr: ptr := (*ptrType)(unsafe.Pointer(ft)) if unt := ptr.uncommon(); unt != nil { - for _, m := range unt.methods { + for _, m := range unt.methods() { if m.name.pkgPath() != nil { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } - typ.u.methods = append(typ.u.methods, m) + methods = append(methods, method{ + name: m.name, + mtyp: resolveReflectType(ptr.typeOff(m.mtyp)), + ifn: resolveReflectText(ptr.textOff(m.ifn)), + tfn: resolveReflectText(ptr.textOff(m.tfn)), + }) } } if unt := ptr.elem.uncommon(); unt != nil { - for _, m := range unt.methods { + for _, m := range unt.methods() { if m.name.pkgPath() != nil { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } - typ.u.methods = append(typ.u.methods, m) + methods = append(methods, method{ + name: m.name, + mtyp: resolveReflectType(ptr.elem.typeOff(m.mtyp)), + ifn: resolveReflectText(ptr.elem.textOff(m.ifn)), + tfn: resolveReflectText(ptr.elem.textOff(m.tfn)), + }) } } default: if unt := ft.uncommon(); unt != nil { - for _, m := range unt.methods { + for _, m := range unt.methods() { if m.name.pkgPath() != nil { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } - typ.u.methods = append(typ.u.methods, m) + methods = append(methods, method{ + name: m.name, + mtyp: resolveReflectType(ft.typeOff(m.mtyp)), + ifn: resolveReflectText(ft.textOff(m.ifn)), + tfn: resolveReflectText(ft.textOff(m.tfn)), + }) + } } } @@ -2359,6 +2455,49 @@ func StructOf(fields []StructField) Type { fs[i] = f } + + var typ *structType + var ut *uncommonType + var typPin interface { + common() *rtype + } // structTypeFixedN + + switch { + case len(methods) == 0: + t := new(structTypeUncommon) + typ = &t.structType + ut = &t.u + typPin = t + case len(methods) <= 4: + t := new(structTypeFixed4) + typ = &t.structType + ut = &t.u + copy(t.m[:], methods) + typPin = t + case len(methods) <= 8: + t := new(structTypeFixed8) + typ = &t.structType + ut = &t.u + copy(t.m[:], methods) + typPin = t + case len(methods) <= 16: + t := new(structTypeFixed16) + typ = &t.structType + ut = &t.u + copy(t.m[:], methods) + typPin = t + case len(methods) <= 32: + t := new(structTypeFixed32) + typ = &t.structType + ut = &t.u + copy(t.m[:], methods) + typPin = t + default: + panic("reflect.StructOf: too many methods") + } + ut.mcount = uint16(len(methods)) + ut.moff = uint16(unsafe.Sizeof(uncommonType{})) + if len(fs) > 0 { repr = append(repr, ' ') } @@ -2372,15 +2511,16 @@ func StructOf(fields []StructField) Type { // Make the struct type. var istruct interface{} = struct{}{} prototype := *(**structType)(unsafe.Pointer(&istruct)) - typ.structType = *prototype - typ.structType.fields = fs + *typ = *prototype + typ.fields = fs // Look in cache structLookupCache.RLock() - for _, t := range structLookupCache.m[hash] { - if haveIdenticalUnderlyingType(&typ.rtype, &t.rtype) { + for _, st := range structLookupCache.m[hash] { + t := st.common() + if haveIdenticalUnderlyingType(&typ.rtype, t) { structLookupCache.RUnlock() - return &t.rtype + return t } } structLookupCache.RUnlock() @@ -2389,11 +2529,14 @@ func StructOf(fields []StructField) Type { structLookupCache.Lock() defer structLookupCache.Unlock() if structLookupCache.m == nil { - structLookupCache.m = make(map[uint32][]*structTypeWithMethods) + structLookupCache.m = make(map[uint32][]interface { + common() *rtype + }) } - for _, t := range structLookupCache.m[hash] { - if haveIdenticalUnderlyingType(&typ.rtype, &t.rtype) { - return &t.rtype + for _, st := range structLookupCache.m[hash] { + t := st.common() + if haveIdenticalUnderlyingType(&typ.rtype, t) { + return t } } @@ -2403,9 +2546,8 @@ func StructOf(fields []StructField) Type { // even if 't' wasn't a structType with methods, we should be ok // as the 'u uncommonType' field won't be accessed except when // tflag&tflagUncommon is set. - tt := (*structTypeWithMethods)(unsafe.Pointer(t)) - structLookupCache.m[hash] = append(structLookupCache.m[hash], tt) - return &tt.rtype + structLookupCache.m[hash] = append(structLookupCache.m[hash], t) + return t } } @@ -2414,7 +2556,7 @@ func StructOf(fields []StructField) Type { typ.size = size typ.align = typalign typ.fieldAlign = typalign - if len(typ.u.methods) > 0 { + if len(methods) > 0 { typ.tflag |= tflagUncommon } if !hasPtr { @@ -2514,7 +2656,7 @@ func StructOf(fields []StructField) Type { typ.kind &^= kindDirectIface } - structLookupCache.m[hash] = append(structLookupCache.m[hash], typ) + structLookupCache.m[hash] = append(structLookupCache.m[hash], typPin) return &typ.rtype } @@ -2533,6 +2675,7 @@ func runtimeStructField(field StructField) structField { } } + _ = resolveReflectType(field.Type.common()) return structField{ name: newName(field.Name, string(field.Tag), field.PkgPath, exported), typ: field.Type.common(), diff --git a/src/reflect/value.go b/src/reflect/value.go index 262545d973..d72c14e9e1 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -566,15 +566,16 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn } else { rcvrtype = v.typ ut := v.typ.uncommon() - if ut == nil || uint(i) >= uint(len(ut.methods)) { + if ut == nil || uint(i) >= uint(ut.mcount) { panic("reflect: internal error: invalid method index") } - m := &ut.methods[i] + m := ut.methods()[i] if !m.name.isExported() { panic("reflect: " + op + " of unexported method") } - fn = unsafe.Pointer(&m.ifn) - t = m.mtyp + ifn := v.typ.textOff(m.ifn) + fn = unsafe.Pointer(&ifn) + t = v.typ.typeOff(m.mtyp) } return } @@ -1687,11 +1688,11 @@ func (v Value) Type() Type { } // Method on concrete type. ut := v.typ.uncommon() - if ut == nil || uint(i) >= uint(len(ut.methods)) { + if ut == nil || uint(i) >= uint(ut.mcount) { panic("reflect: internal error: invalid method index") } - m := &ut.methods[i] - return m.mtyp + m := ut.methods()[i] + return v.typ.typeOff(m.mtyp) } // Uint returns v's underlying value, as a uint64. diff --git a/src/runtime/iface.go b/src/runtime/iface.go index a4c962fb7a..700bdc2f48 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -93,7 +93,8 @@ func additab(m *itab, locked, canfail bool) { // so can iterate over both in lock step; // the loop is O(ni+nt) not O(ni*nt). ni := len(inter.mhdr) - nt := len(x.mhdr) + nt := int(x.mcount) + xmhdr := (*[1 << 16]method)(add(unsafe.Pointer(x), uintptr(x.moff)))[:nt:nt] j := 0 for k := 0; k < ni; k++ { i := &inter.mhdr[k] @@ -104,15 +105,16 @@ func additab(m *itab, locked, canfail bool) { ipkg = inter.pkgpath } for ; j < nt; j++ { - t := &x.mhdr[j] - if t.mtyp == itype && t.name.name() == iname { + t := &xmhdr[j] + if typ.typeOff(t.mtyp) == itype && t.name.name() == iname { pkgPath := t.name.pkgPath() if pkgPath == nil { pkgPath = x.pkgpath } if t.name.isExported() || pkgPath == ipkg { if m != nil { - *(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*sys.PtrSize)) = t.ifn + ifn := typ.textOff(t.ifn) + *(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*sys.PtrSize)) = ifn } goto nextimethod } diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 1a9dbd6c53..98a986cd63 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -435,9 +435,10 @@ func schedinit() { tracebackinit() moduledataverify() stackinit() - itabsinit() mallocinit() mcommoninit(_g_.m) + typelinksinit() + itabsinit() msigsave(_g_.m) initSigmask = _g_.m.sigmask diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go index e1956569fd..02aeedaf75 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go @@ -486,3 +486,36 @@ func reflect_typelinks() ([]unsafe.Pointer, [][]int32) { } return sections, ret } + +// reflect_resolveTypeOff resolves an *rtype offset from a base type. +//go:linkname reflect_resolveTypeOff reflect.resolveTypeOff +func reflect_resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer { + return unsafe.Pointer((*_type)(rtype).typeOff(typeOff(off))) +} + +// reflect_resolveTextOff resolves an function pointer offset from a base type. +//go:linkname reflect_resolveTextOff reflect.resolveTextOff +func reflect_resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer { + return (*_type)(rtype).textOff(textOff(off)) + +} + +// reflect_addReflectOff adds a pointer to the reflection offset lookup map. +//go:linkname reflect_addReflectOff reflect.addReflectOff +func reflect_addReflectOff(ptr unsafe.Pointer) int32 { + lock(&reflectOffs.lock) + if reflectOffs.m == nil { + reflectOffs.m = make(map[int32]unsafe.Pointer) + reflectOffs.minv = make(map[unsafe.Pointer]int32) + reflectOffs.next = -1 + } + id, found := reflectOffs.minv[ptr] + if !found { + id = reflectOffs.next + reflectOffs.next-- // use negative offsets as IDs to aid debugging + reflectOffs.m[id] = ptr + reflectOffs.minv[ptr] = id + } + unlock(&reflectOffs.lock) + return id +} diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index 8c70f22c1f..2df390253a 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -137,6 +137,8 @@ type moduledata struct { gcdatamask, gcbssmask bitvector + typemap map[typeOff]*_type // offset to *_rtype in previous module + next *moduledata } diff --git a/src/runtime/type.go b/src/runtime/type.go index fbf6f9973c..86131d3ff3 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -131,6 +131,92 @@ func (t *_type) name() string { return t._string[i+1:] } +// reflectOffs holds type offsets defined at run time by the reflect package. +// +// When a type is defined at run time, its *rtype data lives on the heap. +// There are a wide range of possible addresses the heap may use, that +// may not be representable as a 32-bit offset. Moreover the GC may +// one day start moving heap memory, in which case there is no stable +// offset that can be defined. +// +// To provide stable offsets, we add pin *rtype objects in a global map +// and treat the offset as an identifier. We use negative offsets that +// do not overlap with any compile-time module offsets. +// +// Entries are created by reflect.addReflectOff. +var reflectOffs struct { + lock mutex + next int32 + m map[int32]unsafe.Pointer + minv map[unsafe.Pointer]int32 +} + +func (t *_type) typeOff(off typeOff) *_type { + if off == 0 { + return nil + } + base := uintptr(unsafe.Pointer(t)) + var md *moduledata + for next := &firstmoduledata; next != nil; next = next.next { + if base >= next.types && base < next.etypes { + md = next + break + } + } + if md == nil { + lock(&reflectOffs.lock) + res := reflectOffs.m[int32(off)] + unlock(&reflectOffs.lock) + if res == nil { + println("runtime: typeOff", hex(off), "base", hex(base), "not in ranges:") + for next := &firstmoduledata; next != nil; next = next.next { + println("\ttypes", hex(next.types), "etypes", hex(next.etypes)) + } + throw("runtime: type offset base pointer out of range") + } + return (*_type)(res) + } + if t := md.typemap[off]; t != nil { + return t + } + res := md.types + uintptr(off) + if res > md.etypes { + println("runtime: typeOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes)) + throw("runtime: type offset out of range") + } + return (*_type)(unsafe.Pointer(res)) +} + +func (t *_type) textOff(off textOff) unsafe.Pointer { + base := uintptr(unsafe.Pointer(t)) + var md *moduledata + for next := &firstmoduledata; next != nil; next = next.next { + if base >= next.types && base < next.etypes { + md = next + break + } + } + if md == nil { + lock(&reflectOffs.lock) + res := reflectOffs.m[int32(off)] + unlock(&reflectOffs.lock) + if res == nil { + println("runtime: textOff", hex(off), "base", hex(base), "not in ranges:") + for next := &firstmoduledata; next != nil; next = next.next { + println("\ttypes", hex(next.types), "etypes", hex(next.etypes)) + } + throw("runtime: text offset base pointer out of range") + } + return res + } + res := md.text + uintptr(off) + if res > md.etext { + println("runtime: textOff", hex(off), "out of range", hex(md.text), "-", hex(md.etext)) + throw("runtime: text offset out of range") + } + return unsafe.Pointer(res) +} + func (t *functype) in() []*_type { // See funcType in reflect/type.go for details on data layout. uadd := uintptr(unsafe.Sizeof(functype{})) @@ -154,16 +240,20 @@ func (t *functype) dotdotdot() bool { return t.outCount&(1<<15) != 0 } +type typeOff int32 +type textOff int32 + type method struct { name name - mtyp *_type - ifn unsafe.Pointer - tfn unsafe.Pointer + mtyp typeOff + ifn textOff + tfn textOff } type uncommontype struct { pkgpath *string - mhdr []method + mcount uint16 // number of methods + moff uint16 // offset from this uncommontype to [mcount]method } type imethod struct { @@ -270,6 +360,18 @@ func (n *name) name() (s string) { return s } +func (n *name) tag() (s string) { + tl := n.tagLen() + if tl == 0 { + return "" + } + nl := n.nameLen() + hdr := (*stringStruct)(unsafe.Pointer(&s)) + hdr.str = unsafe.Pointer(n.data(3 + nl + 2)) + hdr.len = tl + return s +} + func (n *name) pkgPath() *string { if *n.data(0)&(1<<2) == 0 { return nil @@ -281,3 +383,200 @@ func (n *name) pkgPath() *string { off = int(round(uintptr(off), sys.PtrSize)) return *(**string)(unsafe.Pointer(n.data(off))) } + +// typelinksinit scans the types from extra modules and builds the +// moduledata typemap used to de-duplicate type pointers. +func typelinksinit() { + if firstmoduledata.next == nil { + return + } + typehash := make(map[uint32][]*_type) + + modules := []*moduledata{} + for md := &firstmoduledata; md != nil; md = md.next { + modules = append(modules, md) + } + prev, modules := modules[len(modules)-1], modules[:len(modules)-1] + for len(modules) > 0 { + // Collect types from the previous module into typehash. + collect: + for _, tl := range prev.typelinks { + var t *_type + if prev.typemap == nil { + t = (*_type)(unsafe.Pointer(prev.types + uintptr(tl))) + } else { + t = prev.typemap[typeOff(tl)] + } + // Add to typehash if not seen before. + tlist := typehash[t.hash] + for _, tcur := range tlist { + if tcur == t { + continue collect + } + } + typehash[t.hash] = append(tlist, t) + } + + // If any of this module's typelinks match a type from a + // prior module, prefer that prior type by adding the offset + // to this module's typemap. + md := modules[len(modules)-1] + md.typemap = make(map[typeOff]*_type, len(md.typelinks)) + for _, tl := range md.typelinks { + t := (*_type)(unsafe.Pointer(md.types + uintptr(tl))) + for _, candidate := range typehash[t.hash] { + if typesEqual(t, candidate) { + t = candidate + break + } + } + md.typemap[typeOff(tl)] = t + } + + prev, modules = md, modules[:len(modules)-1] + } +} + +// typesEqual reports whether two types are equal. +// +// Everywhere in the runtime and reflect packages, it is assumed that +// there is exactly one *_type per Go type, so that pointer equality +// can be used to test if types are equal. There is one place that +// breaks this assumption: buildmode=shared. In this case a type can +// appear as two different pieces of memory. This is hidden from the +// runtime and reflect package by the per-module typemap built in +// typelinksinit. It uses typesEqual to map types from later modules +// back into earlier ones. +// +// Only typelinksinit needs this function. +func typesEqual(t, v *_type) bool { + if t == v { + return true + } + kind := t.kind & kindMask + if kind != v.kind&kindMask { + return false + } + if t._string != v._string { + return false + } + ut := t.uncommon() + uv := v.uncommon() + if ut != nil || uv != nil { + if ut == nil || uv == nil { + return false + } + if !pkgPathEqual(ut.pkgpath, uv.pkgpath) { + return false + } + } + if kindBool <= kind && kind <= kindComplex128 { + return true + } + switch kind { + case kindString, kindUnsafePointer: + return true + case kindArray: + at := (*arraytype)(unsafe.Pointer(t)) + av := (*arraytype)(unsafe.Pointer(v)) + return typesEqual(at.elem, av.elem) && at.len == av.len + case kindChan: + ct := (*chantype)(unsafe.Pointer(t)) + cv := (*chantype)(unsafe.Pointer(v)) + return ct.dir == cv.dir && typesEqual(ct.elem, cv.elem) + case kindFunc: + ft := (*functype)(unsafe.Pointer(t)) + fv := (*functype)(unsafe.Pointer(v)) + if ft.outCount != fv.outCount || ft.inCount != fv.inCount { + return false + } + tin, vin := ft.in(), fv.in() + for i := 0; i < len(tin); i++ { + if !typesEqual(tin[i], vin[i]) { + return false + } + } + tout, vout := ft.out(), fv.out() + for i := 0; i < len(tout); i++ { + if !typesEqual(tout[i], vout[i]) { + return false + } + } + return true + case kindInterface: + it := (*interfacetype)(unsafe.Pointer(t)) + iv := (*interfacetype)(unsafe.Pointer(v)) + if !pkgPathEqual(it.pkgpath, iv.pkgpath) { + return false + } + if len(it.mhdr) != len(iv.mhdr) { + return false + } + for i := range it.mhdr { + tm := &it.mhdr[i] + vm := &iv.mhdr[i] + if tm.name.name() != vm.name.name() { + return false + } + if !pkgPathEqual(tm.name.pkgPath(), vm.name.pkgPath()) { + return false + } + if !typesEqual(tm._type, vm._type) { + return false + } + } + return true + case kindMap: + mt := (*maptype)(unsafe.Pointer(t)) + mv := (*maptype)(unsafe.Pointer(v)) + return typesEqual(mt.key, mv.key) && typesEqual(mt.elem, mv.elem) + case kindPtr: + pt := (*ptrtype)(unsafe.Pointer(t)) + pv := (*ptrtype)(unsafe.Pointer(v)) + return typesEqual(pt.elem, pv.elem) + case kindSlice: + st := (*slicetype)(unsafe.Pointer(t)) + sv := (*slicetype)(unsafe.Pointer(v)) + return typesEqual(st.elem, sv.elem) + case kindStruct: + st := (*structtype)(unsafe.Pointer(t)) + sv := (*structtype)(unsafe.Pointer(v)) + if len(st.fields) != len(sv.fields) { + return false + } + for i := range st.fields { + tf := &st.fields[i] + vf := &sv.fields[i] + if tf.name.name() != vf.name.name() { + return false + } + if !pkgPathEqual(tf.name.pkgPath(), vf.name.pkgPath()) { + return false + } + if !typesEqual(tf.typ, vf.typ) { + return false + } + if tf.name.tag() != vf.name.tag() { + return false + } + if tf.offset != vf.offset { + return false + } + } + return true + default: + println("runtime: impossible type kind", kind) + throw("runtime: impossible type kind") + return false + } +} + +func pkgPathEqual(p, q *string) bool { + if p == q { + return true + } + if p == nil || q == nil { + return false + } + return *p == *q +} -- cgit v1.3 From 7d56215bcbc5a2ef5e59805271b0ca6a4fd56e4d Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Tue, 19 Apr 2016 14:02:21 -0400 Subject: cmd/link: convert textp into a slice Updates #15374 Change-Id: I3ea715735862fe9550b88d7a29def6cb9d4419a6 Reviewed-on: https://go-review.googlesource.com/22243 Run-TryBot: David Crawshaw TryBot-Result: Gobot Gobot Reviewed-by: Michael Hudson-Doyle --- src/cmd/link/internal/amd64/asm.go | 7 +----- src/cmd/link/internal/arm/asm.go | 7 +----- src/cmd/link/internal/arm64/asm.go | 7 +----- src/cmd/link/internal/ld/data.go | 15 +++++------ src/cmd/link/internal/ld/deadcode.go | 22 ++++------------ src/cmd/link/internal/ld/dwarf.go | 8 +++--- src/cmd/link/internal/ld/elf.go | 2 +- src/cmd/link/internal/ld/ldelf.go | 10 ++------ src/cmd/link/internal/ld/ldmacho.go | 10 ++------ src/cmd/link/internal/ld/ldpe.go | 10 ++------ src/cmd/link/internal/ld/lib.go | 38 ++++++++-------------------- src/cmd/link/internal/ld/link.go | 3 +-- src/cmd/link/internal/ld/macho.go | 2 +- src/cmd/link/internal/ld/objfile.go | 7 +----- src/cmd/link/internal/ld/pcln.go | 49 +++++++++++++++++------------------- src/cmd/link/internal/ld/pe.go | 2 +- src/cmd/link/internal/ppc64/asm.go | 23 +++-------------- src/cmd/link/internal/s390x/asm.go | 7 +----- src/cmd/link/internal/x86/asm.go | 10 ++------ 19 files changed, 71 insertions(+), 168 deletions(-) (limited to 'src/cmd/link/internal/ld/deadcode.go') diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index ab96a59151..cdb0354579 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -86,12 +86,7 @@ func gentext() { Addcall(ld.Ctxt, initfunc, addmoduledata) // c: c3 retq o(0xc3) - if ld.Ctxt.Etextp != nil { - ld.Ctxt.Etextp.Next = initfunc - } else { - ld.Ctxt.Textp = initfunc - } - ld.Ctxt.Etextp = initfunc + ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc) initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0) initarray_entry.Attr |= ld.AttrReachable initarray_entry.Attr |= ld.AttrLocal diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index 69e1d8f317..069812fcef 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -95,12 +95,7 @@ func gentext() { rel.Type = obj.R_PCREL rel.Add = 4 - if ld.Ctxt.Etextp != nil { - ld.Ctxt.Etextp.Next = initfunc - } else { - ld.Ctxt.Textp = initfunc - } - ld.Ctxt.Etextp = initfunc + ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc) initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0) initarray_entry.Attr |= ld.AttrReachable initarray_entry.Attr |= ld.AttrLocal diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index d8ffffa157..97803c9d03 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -78,12 +78,7 @@ func gentext() { rel.Sym = ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0) rel.Type = obj.R_CALLARM64 // Really should be R_AARCH64_JUMP26 but doesn't seem to make any difference - if ld.Ctxt.Etextp != nil { - ld.Ctxt.Etextp.Next = initfunc - } else { - ld.Ctxt.Textp = initfunc - } - ld.Ctxt.Etextp = initfunc + ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc) initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0) initarray_entry.Attr |= ld.AttrReachable initarray_entry.Attr |= ld.AttrLocal diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 71af0e4730..8d20096dcf 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -648,7 +648,7 @@ func reloc() { } Bso.Flush() - for s := Ctxt.Textp; s != nil; s = s.Next { + for _, s := range Ctxt.Textp { relocsym(s) } for _, sym := range datap { @@ -724,7 +724,7 @@ func dynreloc(data *[obj.SXREF][]*LSym) { } Bso.Flush() - for s := Ctxt.Textp; s != nil; s = s.Next { + for _, s := range Ctxt.Textp { dynrelocsym(s) } for _, syms := range data { @@ -791,7 +791,7 @@ func Codeblk(addr int64, size int64) { fmt.Fprintf(Bso, "codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) } - blk(Ctxt.Textp, addr, size) + blkSlice(Ctxt.Textp, addr, size) /* again for printing */ if Debug['a'] == 0 { @@ -799,7 +799,7 @@ func Codeblk(addr int64, size int64) { } var sym *LSym - for sym = Ctxt.Textp; sym != nil; sym = sym.Next { + for _, sym = range Ctxt.Textp { if !sym.Attr.Reachable() { continue } @@ -1893,8 +1893,9 @@ func textbuildid() { sym.P = []byte(data) sym.Size = int64(len(sym.P)) - sym.Next = Ctxt.Textp - Ctxt.Textp = sym + Ctxt.Textp = append(Ctxt.Textp, nil) + copy(Ctxt.Textp[1:], Ctxt.Textp) + Ctxt.Textp[0] = sym } // assign addresses to text @@ -1914,7 +1915,7 @@ func textaddress() { } va := uint64(INITTEXT) sect.Vaddr = va - for sym := Ctxt.Textp; sym != nil; sym = sym.Next { + for _, sym := range Ctxt.Textp { sym.Sect = sect if sym.Type&obj.SSUB != 0 { continue diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index c83a104a54..6a70ff581f 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -119,25 +119,13 @@ func deadcode(ctxt *Link) { } // Remove dead text but keep file information (z symbols). - var last *LSym - for s := ctxt.Textp; s != nil; s = s.Next { - if !s.Attr.Reachable() { - continue + textp := make([]*LSym, 0, len(ctxt.Textp)) + for _, s := range ctxt.Textp { + if s.Attr.Reachable() { + textp = append(textp, s) } - if last == nil { - ctxt.Textp = s - } else { - last.Next = s - } - last = s - } - if last == nil { - ctxt.Textp = nil - ctxt.Etextp = nil - } else { - last.Next = nil - ctxt.Etextp = last } + ctxt.Textp = textp } var markextra = []string{ diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index bec9946ec5..a5e26b49f2 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -1432,7 +1432,7 @@ func writelines(prev *LSym) *LSym { lang := DW_LANG_Go - s := Ctxt.Textp + s := Ctxt.Textp[0] dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, "go", 0) newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT, int64(lang), 0) @@ -1502,8 +1502,8 @@ func writelines(prev *LSym) *LSym { var pcfile Pciter var pcline Pciter - for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { - s = Ctxt.Cursym + for _, Ctxt.Cursym = range Ctxt.Textp { + s := Ctxt.Cursym dwfunc := newdie(dwinfo, DW_ABRV_FUNCTION, s.Name, int(s.Version)) newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s) @@ -1696,7 +1696,7 @@ func writeframes(prev *LSym) *LSym { var deltaBuf []byte var pcsp Pciter - for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { + for _, Ctxt.Cursym = range Ctxt.Textp { s := Ctxt.Cursym if s.FuncInfo == nil { continue diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index 84aa58e7c7..15b8d7af93 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -1727,7 +1727,7 @@ func Elfemitreloc() { Cput(0) } - elfrelocsect(Segtext.Sect, list2slice(Ctxt.Textp)) + elfrelocsect(Segtext.Sect, Ctxt.Textp) for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next { elfrelocsect(sect, datap) } diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go index 59e71f4dd4..af60a5c85b 100644 --- a/src/cmd/link/internal/ld/ldelf.go +++ b/src/cmd/link/internal/ld/ldelf.go @@ -842,19 +842,13 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList - if Ctxt.Etextp != nil { - Ctxt.Etextp.Next = s - } else { - Ctxt.Textp = s - } - Ctxt.Etextp = s + Ctxt.Textp = append(Ctxt.Textp, s) for s = s.Sub; s != nil; s = s.Sub { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList - Ctxt.Etextp.Next = s - Ctxt.Etextp = s + Ctxt.Textp = append(Ctxt.Textp, s) } } } diff --git a/src/cmd/link/internal/ld/ldmacho.go b/src/cmd/link/internal/ld/ldmacho.go index 105fc137f9..a10124907c 100644 --- a/src/cmd/link/internal/ld/ldmacho.go +++ b/src/cmd/link/internal/ld/ldmacho.go @@ -707,19 +707,13 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList - if Ctxt.Etextp != nil { - Ctxt.Etextp.Next = s - } else { - Ctxt.Textp = s - } - Ctxt.Etextp = s + Ctxt.Textp = append(Ctxt.Textp, s) for s1 = s.Sub; s1 != nil; s1 = s1.Sub { if s1.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s1.Name) } s1.Attr |= AttrOnList - Ctxt.Etextp.Next = s1 - Ctxt.Etextp = s1 + Ctxt.Textp = append(Ctxt.Textp, s1) } } } diff --git a/src/cmd/link/internal/ld/ldpe.go b/src/cmd/link/internal/ld/ldpe.go index c51479fb4e..7eb26bcbe8 100644 --- a/src/cmd/link/internal/ld/ldpe.go +++ b/src/cmd/link/internal/ld/ldpe.go @@ -435,19 +435,13 @@ func ldpe(f *bio.Reader, pkg string, length int64, pn string) { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList - if Ctxt.Etextp != nil { - Ctxt.Etextp.Next = s - } else { - Ctxt.Textp = s - } - Ctxt.Etextp = s + Ctxt.Textp = append(Ctxt.Textp, s) for s = s.Sub; s != nil; s = s.Sub { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList - Ctxt.Etextp.Next = s - Ctxt.Etextp = s + Ctxt.Textp = append(Ctxt.Textp, s) } } } diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index d728dda5b6..77db672bfd 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1545,30 +1545,14 @@ func ldshlibsyms(shlib string) { // We might have overwritten some functions above (this tends to happen for the // autogenerated type equality/hashing functions) and we don't want to generated - // pcln table entries for these any more so unstitch them from the Textp linked - // list. - var last *LSym - - for s := Ctxt.Textp; s != nil; s = s.Next { - if s.Type == obj.SDYNIMPORT { - continue - } - - if last == nil { - Ctxt.Textp = s - } else { - last.Next = s + // pcln table entries for these any more so remove them from Textp. + textp := make([]*LSym, 0, len(Ctxt.Textp)) + for _, s := range Ctxt.Textp { + if s.Type != obj.SDYNIMPORT { + textp = append(textp, s) } - last = s - } - - if last == nil { - Ctxt.Textp = nil - Ctxt.Etextp = nil - } else { - last.Next = nil - Ctxt.Etextp = last } + Ctxt.Textp = textp Ctxt.Shlibs = append(Ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f, gcdata_addresses: gcdata_addresses}) } @@ -1682,7 +1666,7 @@ func dostkcheck() { // Check every function, but do the nosplit functions in a first pass, // to make the printed failure chains as short as possible. - for s := Ctxt.Textp; s != nil; s = s.Next { + for _, s := range Ctxt.Textp { // runtime.racesymbolizethunk is called from gcc-compiled C // code running on the operating system thread stack. // It uses more than the usual amount of stack but that's okay. @@ -1697,7 +1681,7 @@ func dostkcheck() { } } - for s := Ctxt.Textp; s != nil; s = s.Next { + for _, s := range Ctxt.Textp { if !s.Attr.NoSplit() { Ctxt.Cursym = s ch.sym = s @@ -1995,7 +1979,7 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { } var off int32 - for s := Ctxt.Textp; s != nil; s = s.Next { + for _, s := range Ctxt.Textp { put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), s.Gotype) locals := int32(0) @@ -2105,7 +2089,7 @@ func undefsym(s *LSym) { } func undef() { - for s := Ctxt.Textp; s != nil; s = s.Next { + for _, s := range Ctxt.Textp { undefsym(s) } for _, s := range datap { @@ -2123,7 +2107,7 @@ func callgraph() { var i int var r *Reloc - for s := Ctxt.Textp; s != nil; s = s.Next { + for _, s := range Ctxt.Textp { for i = 0; i < len(s.R); i++ { r = &s.R[i] if r.Sym == nil { diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index b0bca4300f..a3b8e57ee5 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -178,8 +178,7 @@ type Link struct { Diag func(string, ...interface{}) Cursym *LSym Version int - Textp *LSym - Etextp *LSym + Textp []*LSym Nhistfile int32 Filesyms *LSym Moduledata *LSym diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index 5b2906ee27..310435e49e 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -852,7 +852,7 @@ func Machoemitreloc() { Cput(0) } - machorelocsect(Segtext.Sect, list2slice(Ctxt.Textp)) + machorelocsect(Segtext.Sect, Ctxt.Textp) for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next { machorelocsect(sect, datap) } diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go index 566c949040..bcfe52585f 100644 --- a/src/cmd/link/internal/ld/objfile.go +++ b/src/cmd/link/internal/ld/objfile.go @@ -398,12 +398,7 @@ overwrite: log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList - if r.ctxt.Etextp != nil { - r.ctxt.Etextp.Next = s - } else { - r.ctxt.Textp = s - } - r.ctxt.Etextp = s + r.ctxt.Textp = append(r.ctxt.Textp, s) } } } diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go index e1c1d2d318..7d1858c95e 100644 --- a/src/cmd/link/internal/ld/pcln.go +++ b/src/cmd/link/internal/ld/pcln.go @@ -224,14 +224,14 @@ func pclntab() { nfunc := int32(0) // Find container symbols, mark them with SCONTAINER - for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { - if Ctxt.Cursym.Outer != nil { - Ctxt.Cursym.Outer.Type |= obj.SCONTAINER + for _, s := range Ctxt.Textp { + if s.Outer != nil { + s.Outer.Type |= obj.SCONTAINER } } - for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { - if container(Ctxt.Cursym) == 0 { + for _, s := range Ctxt.Textp { + if container(s) == 0 { nfunc++ } } @@ -246,7 +246,7 @@ func pclntab() { nfunc = 0 var last *LSym - for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { + for _, Ctxt.Cursym = range Ctxt.Textp { last = Ctxt.Cursym if container(Ctxt.Cursym) != 0 { continue @@ -401,10 +401,9 @@ func findfunctab() { t.Attr |= AttrLocal // find min and max address - min := Ctxt.Textp.Value - + min := Ctxt.Textp[0].Value max := int64(0) - for s := Ctxt.Textp; s != nil; s = s.Next { + for _, s := range Ctxt.Textp { max = s.Value + s.Size } @@ -417,34 +416,34 @@ func findfunctab() { indexes[i] = NOIDX } idx := int32(0) - var e *LSym - var i int32 - var p int64 - var q int64 - for s := Ctxt.Textp; s != nil; s = s.Next { + for i, s := range Ctxt.Textp { if container(s) != 0 { continue } - p = s.Value - e = s.Next - for container(e) != 0 { - e = e.Next + p := s.Value + var e *LSym + i++ + if i < len(Ctxt.Textp) { + e = Ctxt.Textp[i] + } + for container(e) != 0 && i < len(Ctxt.Textp) { + e = Ctxt.Textp[i] + i++ } + q := max if e != nil { q = e.Value - } else { - q = max } //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); for ; p < q; p += SUBBUCKETSIZE { - i = int32((p - min) / SUBBUCKETSIZE) + i = int((p - min) / SUBBUCKETSIZE) if indexes[i] > idx { indexes[i] = idx } } - i = int32((q - 1 - min) / SUBBUCKETSIZE) + i = int((q - 1 - min) / SUBBUCKETSIZE) if indexes[i] > idx { indexes[i] = idx } @@ -457,15 +456,13 @@ func findfunctab() { Symgrow(Ctxt, t, 4*int64(nbuckets)+int64(n)) // fill in table - var base int32 - var j int32 for i := int32(0); i < nbuckets; i++ { - base = indexes[i*SUBBUCKETS] + base := indexes[i*SUBBUCKETS] if base == NOIDX { Diag("hole in findfunctab") } setuint32(Ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base)) - for j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { + for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { idx = indexes[i*SUBBUCKETS+j] if idx == NOIDX { Diag("hole in findfunctab") diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go index c0df07d359..839aa6cca7 100644 --- a/src/cmd/link/internal/ld/pe.go +++ b/src/cmd/link/internal/ld/pe.go @@ -831,7 +831,7 @@ func peemitreloc(text, data, ctors *IMAGE_SECTION_HEADER) { Lputl(0) Wputl(0) - n := perelocsect(Segtext.Sect, list2slice(Ctxt.Textp)) + 1 + n := perelocsect(Segtext.Sect, Ctxt.Textp) + 1 for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next { n += perelocsect(sect, datap) } diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 17ee25608b..dbf5fac0de 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -87,9 +87,7 @@ func genplt() { // // This assumes "case 1" from the ABI, where the caller needs // us to save and restore the TOC pointer. - pprevtextp := &ld.Ctxt.Textp - - for s := *pprevtextp; s != nil; pprevtextp, s = &s.Next, s.Next { + for _, s := range ld.Ctxt.Textp { for i := range s.R { r := &s.R[i] if r.Type != 256+ld.R_PPC64_REL24 || r.Sym.Type != obj.SDYNIMPORT { @@ -110,15 +108,7 @@ func genplt() { if stub.Size == 0 { // Need outer to resolve .TOC. stub.Outer = s - - // Link in to textp before s (we could - // do it after, but would have to skip - // the subsymbols) - *pprevtextp = stub - - stub.Next = s - pprevtextp = &stub.Next - + ld.Ctxt.Textp = append(ld.Ctxt.Textp, stub) gencallstub(1, stub, r.Sym) } @@ -131,7 +121,6 @@ func genplt() { ld.Ctxt.Arch.ByteOrder.PutUint32(s.P[r.Off+4:], o1) } } - } func genaddmoduledata() { @@ -187,13 +176,7 @@ func genaddmoduledata() { // blr o(0x4e800020) - if ld.Ctxt.Etextp != nil { - ld.Ctxt.Etextp.Next = initfunc - } else { - ld.Ctxt.Textp = initfunc - } - ld.Ctxt.Etextp = initfunc - + ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc) initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0) initarray_entry.Attr |= ld.AttrReachable initarray_entry.Attr |= ld.AttrLocal diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go index 30b1e5a3e1..7c2e3358ff 100644 --- a/src/cmd/link/internal/s390x/asm.go +++ b/src/cmd/link/internal/s390x/asm.go @@ -90,12 +90,7 @@ func gentext() { // undef (for debugging) ld.Adduint32(ld.Ctxt, initfunc, 0) - if ld.Ctxt.Etextp != nil { - ld.Ctxt.Etextp.Next = initfunc - } else { - ld.Ctxt.Textp = initfunc - } - ld.Ctxt.Etextp = initfunc + ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc) initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0) initarray_entry.Attr |= ld.AttrLocal initarray_entry.Attr |= ld.AttrReachable diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index 5231ad1f6c..4a55b535ac 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -69,12 +69,7 @@ func gentext() { // c3 ret o(0xc3) - if ld.Ctxt.Etextp != nil { - ld.Ctxt.Etextp.Next = thunkfunc - } else { - ld.Ctxt.Textp = thunkfunc - } - ld.Ctxt.Etextp = thunkfunc + ld.Ctxt.Textp = append(ld.Ctxt.Textp, thunkfunc) addmoduledata := ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0) if addmoduledata.Type == obj.STEXT { @@ -130,8 +125,7 @@ func gentext() { o(0xc3) - ld.Ctxt.Etextp.Next = initfunc - ld.Ctxt.Etextp = initfunc + ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc) initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0) initarray_entry.Attr |= ld.AttrReachable initarray_entry.Attr |= ld.AttrLocal -- cgit v1.3 From d224e98d9ac969e733f5578dce3e1831c5c84f45 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Fri, 22 Apr 2016 18:49:59 -0700 Subject: cmd/link: add -dumpdep flag to dump linker dependency graph This is what led to https://golang.org/cl/20763 and https://golang.org/cl/20765 to shrink binary sizes. Change-Id: Id360d474e6153cfe32a525b0a720810fd113195b Reviewed-on: https://go-review.googlesource.com/22392 Reviewed-by: David Crawshaw --- src/cmd/link/internal/ld/deadcode.go | 7 +++++++ src/cmd/link/internal/ld/lib.go | 1 + src/cmd/link/internal/ld/pobj.go | 1 + 3 files changed, 9 insertions(+) (limited to 'src/cmd/link/internal/ld/deadcode.go') diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index 6a70ff581f..aaed6cde21 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -196,6 +196,13 @@ func (d *deadcodepass) mark(s, parent *LSym) { if s.Attr.ReflectMethod() { d.reflectMethod = true } + if flag_dumpdep { + p := "_" + if parent != nil { + p = parent.Name + } + fmt.Printf("%s -> %s\n", p, s.Name) + } s.Attr |= AttrReachable s.Reachparent = parent d.markQueue = append(d.markQueue, s) diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 77db672bfd..f7b9b79c2f 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -196,6 +196,7 @@ var ( Funcalign int iscgo bool elfglobalsymndx int + flag_dumpdep bool flag_installsuffix string flag_race int flag_msan int diff --git a/src/cmd/link/internal/ld/pobj.go b/src/cmd/link/internal/ld/pobj.go index f4fb4d4845..b64bb5deaf 100644 --- a/src/cmd/link/internal/ld/pobj.go +++ b/src/cmd/link/internal/ld/pobj.go @@ -90,6 +90,7 @@ func Ldmain() { flag.Var(&Buildmode, "buildmode", "set build `mode`") obj.Flagcount("c", "dump call graph", &Debug['c']) obj.Flagcount("d", "disable dynamic executable", &Debug['d']) + flag.BoolVar(&flag_dumpdep, "dumpdep", false, "dump symbol dependency graph") obj.Flagstr("extar", "archive program for buildmode=c-archive", &extar) obj.Flagstr("extld", "use `linker` when linking in external mode", &extld) obj.Flagstr("extldflags", "pass `flags` to external linker", &extldflags) -- cgit v1.3