]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/internal/obj: rework gclocals handling
authorJosh Bleecher Snyder <josharian@gmail.com>
Fri, 14 Apr 2017 13:35:53 +0000 (06:35 -0700)
committerJosh Bleecher Snyder <josharian@gmail.com>
Tue, 18 Apr 2017 02:13:32 +0000 (02:13 +0000)
The compiler handled gcargs and gclocals LSyms unusually.
It generated placeholder symbols (makefuncdatasym),
filled them in, and then renamed them for content-addressability.
This is an important binary size optimization;
the same locals information occurs over and over.

This CL continues to treat these LSyms unusually,
but in a slightly more explicit way,
and importantly for concurrent compilation,
in a way that does not require concurrent
modification of Ctxt.Hash.

Instead of creating gcargs and gclocals in the usual way,
by creating a types.Sym and then an obj.LSym,
we add them directly to obj.FuncInfo,
initialize them in obj.InitTextSym,
and deduplicate and add them to ctxt.Data at the end.
Then the backend's job is simply to fill them in
and rename them appropriately.

Updates #15756

name       old alloc/op      new alloc/op      delta
Template        38.8MB ± 0%       38.7MB ± 0%  -0.22%  (p=0.016 n=5+5)
Unicode         29.8MB ± 0%       29.8MB ± 0%    ~     (p=0.690 n=5+5)
GoTypes          113MB ± 0%        113MB ± 0%  -0.24%  (p=0.008 n=5+5)
SSA             1.25GB ± 0%       1.24GB ± 0%  -0.39%  (p=0.008 n=5+5)
Flate           25.3MB ± 0%       25.2MB ± 0%  -0.43%  (p=0.008 n=5+5)
GoParser        31.7MB ± 0%       31.7MB ± 0%  -0.22%  (p=0.008 n=5+5)
Reflect         78.2MB ± 0%       77.6MB ± 0%  -0.80%  (p=0.008 n=5+5)
Tar             26.6MB ± 0%       26.3MB ± 0%  -0.85%  (p=0.008 n=5+5)
XML             42.4MB ± 0%       41.9MB ± 0%  -1.04%  (p=0.008 n=5+5)

name       old allocs/op     new allocs/op     delta
Template          378k ± 0%         377k ± 1%    ~     (p=0.151 n=5+5)
Unicode           321k ± 1%         321k ± 0%    ~     (p=0.841 n=5+5)
GoTypes          1.14M ± 0%        1.14M ± 0%  -0.47%  (p=0.016 n=5+5)
SSA              9.71M ± 0%        9.67M ± 0%  -0.33%  (p=0.008 n=5+5)
Flate             233k ± 1%         232k ± 1%    ~     (p=0.151 n=5+5)
GoParser          316k ± 0%         315k ± 0%  -0.49%  (p=0.016 n=5+5)
Reflect           979k ± 0%         972k ± 0%  -0.75%  (p=0.008 n=5+5)
Tar               250k ± 0%         247k ± 1%  -0.92%  (p=0.008 n=5+5)
XML               392k ± 1%         389k ± 0%  -0.67%  (p=0.008 n=5+5)

Change-Id: Idc36186ca9d2f8214b5f7720bbc27b6bb22fdc48
Reviewed-on: https://go-review.googlesource.com/40697
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
src/cmd/compile/fmt_test.go
src/cmd/compile/internal/gc/gsubr.go
src/cmd/compile/internal/gc/obj.go
src/cmd/compile/internal/gc/pgen.go
src/cmd/compile/internal/gc/plive.go
src/cmd/compile/internal/gc/ssa.go
src/cmd/internal/obj/link.go
src/cmd/internal/obj/plist.go
src/cmd/link/internal/ld/objfile.go

index 204e611e9e669a4ba2fc389c6aa2a9a4bffd51f1..01c522c7041392cbbcfbd8592d7bd50e35c7648a 100644 (file)
@@ -654,7 +654,6 @@ var knownFormats = map[string]string{
        "cmd/compile/internal/types.EType %v":             "",
        "cmd/internal/src.Pos %s":                         "",
        "cmd/internal/src.Pos %v":                         "",
-       "cmd/internal/src.XPos %v":                        "",
        "error %v":                                        "",
        "float64 %.2f":                                    "",
        "float64 %.3f":                                    "",
index bf70cebb75431617e338a60123cef6b42d6f938f..96d47a526c2c2f60e648ea55d25a305ad24ea7b2 100644 (file)
@@ -145,13 +145,29 @@ func (pp *Progs) settext(fn *Node) {
                Fatalf("Progs.settext called twice")
        }
        ptxt := pp.Prog(obj.ATEXT)
-       if fn.Func.lsym != nil {
-               fn.Func.lsym.Text = ptxt
-               ptxt.From.Type = obj.TYPE_MEM
-               ptxt.From.Name = obj.NAME_EXTERN
-               ptxt.From.Sym = fn.Func.lsym
-       }
        pp.Text = ptxt
+
+       if fn.Func.lsym == nil {
+               // func _() { }
+               return
+       }
+
+       fn.Func.lsym.Text = ptxt
+       ptxt.From.Type = obj.TYPE_MEM
+       ptxt.From.Name = obj.NAME_EXTERN
+       ptxt.From.Sym = fn.Func.lsym
+
+       p := pp.Prog(obj.AFUNCDATA)
+       Addrconst(&p.From, obj.FUNCDATA_ArgsPointerMaps)
+       p.To.Type = obj.TYPE_MEM
+       p.To.Name = obj.NAME_EXTERN
+       p.To.Sym = &fn.Func.lsym.FuncInfo.GCArgs
+
+       p = pp.Prog(obj.AFUNCDATA)
+       Addrconst(&p.From, obj.FUNCDATA_LocalsPointerMaps)
+       p.To.Type = obj.TYPE_MEM
+       p.To.Name = obj.NAME_EXTERN
+       p.To.Sym = &fn.Func.lsym.FuncInfo.GCLocals
 }
 
 func (f *Func) initLSym() {
index 184cccb965f260d53779b4086a5159d0cd3c260d..43da48a404ba5176ad5469c855102162f0753233 100644 (file)
@@ -152,6 +152,8 @@ func dumpobj1(outfile string, mode int) {
                ggloblsym(zero, int32(zerosize), obj.DUPOK|obj.RODATA)
        }
 
+       addGCLocals()
+
        obj.WriteObjFile(Ctxt, bout.Writer)
 
        if writearchive {
@@ -227,6 +229,27 @@ func dumpglobls() {
        funcsyms = nil
 }
 
+// addGCLocals adds gcargs and gclocals symbols to Ctxt.Data.
+// It takes care not to add any duplicates.
+// Though the object file format handles duplicates efficiently,
+// storing only a single copy of the data,
+// failure to remove these duplicates adds a few percent to object file size.
+func addGCLocals() {
+       seen := make(map[string]bool)
+       for _, s := range Ctxt.Text {
+               if s.FuncInfo == nil {
+                       continue
+               }
+               for _, gcsym := range []*obj.LSym{&s.FuncInfo.GCArgs, &s.FuncInfo.GCLocals} {
+                       if seen[gcsym.Name] {
+                               continue
+                       }
+                       Ctxt.Data = append(Ctxt.Data, gcsym)
+                       seen[gcsym.Name] = true
+               }
+       }
+}
+
 func linksymname(s *types.Sym) string {
        if isblanksym(s) {
                return "_"
@@ -279,11 +302,19 @@ func duintptr(s *types.Sym, off int, v uint64) int {
        return duintxx(s, off, v, Widthptr)
 }
 
-func dbvec(s *types.Sym, off int, bv bvec) int {
+func duint8LSym(s *obj.LSym, off int, v uint8) int {
+       return duintxxLSym(s, off, uint64(v), 1)
+}
+
+func duint32LSym(s *obj.LSym, off int, v uint32) int {
+       return duintxxLSym(s, off, uint64(v), 4)
+}
+
+func dbvecLSym(s *obj.LSym, off int, bv bvec) int {
        // Runtime reads the bitmaps as byte arrays. Oblige.
        for j := 0; int32(j) < bv.n; j += 8 {
                word := bv.b[j/32]
-               off = duint8(s, off, uint8(word>>(uint(j)%32)))
+               off = duint8LSym(s, off, uint8(word>>(uint(j)%32)))
        }
        return off
 }
index 3a993e55d58795562030b8997c3f028233863798..c5519f6c3b35b2008da23ca70d8362d59b926f03 100644 (file)
@@ -17,39 +17,6 @@ import (
 
 // "Portable" code generation.
 
-func makefuncdatasym(pp *Progs, nameprefix string, funcdatakind int64, curfn *Node) *types.Sym {
-       // This symbol requires a unique, reproducible name;
-       // unique to avoid duplicate symbols,
-       // and reproducible for reproducible builds and toolstash.
-       // The function name will usually suffice.
-       suffix := curfn.Func.Nname.Sym.Name
-       if suffix == "_" {
-               // It is possible to have multiple functions called _,
-               // so in this rare case, use instead the function's position.
-               // This formatted string will be meaningless gibberish, but that's ok.
-               // It will be unique and reproducible, and it is rare anyway.
-               // Note that we can't just always use the position;
-               // it is possible to have multiple autogenerated functions at the same position.
-               // Fortunately, no autogenerated functions are called _.
-               if curfn.Pos == autogeneratedPos {
-                       Fatalf("autogenerated func _")
-               }
-               suffix = fmt.Sprintf("%v", curfn.Pos)
-       }
-       // Add in the package path as well.
-       // When generating wrappers, we can end up compiling a function belonging
-       // to other packages, which might have a name that collides with one in our package.
-       symname := nameprefix + curfn.Func.Nname.Sym.Pkg.Path + "." + suffix
-
-       sym := lookup(symname)
-       p := pp.Prog(obj.AFUNCDATA)
-       Addrconst(&p.From, funcdatakind)
-       p.To.Type = obj.TYPE_MEM
-       p.To.Name = obj.NAME_EXTERN
-       p.To.Sym = Linksym(sym)
-       return sym
-}
-
 // TODO(mdempsky): Update to reference OpVar{Def,Kill,Live} instead
 // and move to plive.go.
 
@@ -113,6 +80,7 @@ func emitptrargsmap() {
                return
        }
        sym := lookup(fmt.Sprintf("%s.args_stackmap", Curfn.Func.Nname.Sym.Name))
+       lsym := Linksym(sym)
 
        nptr := int(Curfn.Type.ArgWidth() / int64(Widthptr))
        bv := bvalloc(int32(nptr) * 2)
@@ -120,8 +88,8 @@ func emitptrargsmap() {
        if Curfn.Type.Results().NumFields() > 0 {
                nbitmap = 2
        }
-       off := duint32(sym, 0, uint32(nbitmap))
-       off = duint32(sym, off, uint32(bv.n))
+       off := duint32LSym(lsym, 0, uint32(nbitmap))
+       off = duint32LSym(lsym, off, uint32(bv.n))
        var xoffset int64
        if Curfn.IsMethod() {
                xoffset = 0
@@ -133,14 +101,14 @@ func emitptrargsmap() {
                onebitwalktype1(Curfn.Type.Params(), &xoffset, bv)
        }
 
-       off = dbvec(sym, off, bv)
+       off = dbvecLSym(lsym, off, bv)
        if Curfn.Type.Results().NumFields() > 0 {
                xoffset = 0
                onebitwalktype1(Curfn.Type.Results(), &xoffset, bv)
-               off = dbvec(sym, off, bv)
+               off = dbvecLSym(lsym, off, bv)
        }
 
-       ggloblsym(sym, int32(off), obj.RODATA|obj.LOCAL)
+       ggloblLSym(lsym, int32(off), obj.RODATA|obj.LOCAL)
 }
 
 // cmpstackvarlt reports whether the stack variable a sorts before b.
index 7d5387eb84d7d928fd665ed88729a9682cc0a506..b667510b083f756c026b2e375c34930a5d70d021 100644 (file)
@@ -1051,32 +1051,18 @@ func livenessprintdebug(lv *Liveness) {
        fmt.Printf("\n")
 }
 
-func finishgclocals(sym *types.Sym) {
-       ls := Linksym(sym)
-       ls.Name = fmt.Sprintf("gclocals·%x", md5.Sum(ls.P))
-       ls.Set(obj.AttrDuplicateOK, true)
-       sv := obj.SymVer{Name: ls.Name, Version: 0}
-       ls2, ok := Ctxt.Hash[sv]
-       if ok {
-               sym.Lsym = ls2
-       } else {
-               Ctxt.Hash[sv] = ls
-               ggloblsym(sym, int32(ls.Size), obj.RODATA)
-       }
-}
-
 // Dumps a slice of bitmaps to a symbol as a sequence of uint32 values. The
 // first word dumped is the total number of bitmaps. The second word is the
 // length of the bitmaps. All bitmaps are assumed to be of equal length. The
 // remaining bytes are the raw bitmaps.
-func livenessemit(lv *Liveness, argssym, livesym *types.Sym) {
+func livenessemit(lv *Liveness, argssym, livesym *obj.LSym) {
        args := bvalloc(argswords(lv))
-       aoff := duint32(argssym, 0, uint32(len(lv.livevars))) // number of bitmaps
-       aoff = duint32(argssym, aoff, uint32(args.n))         // number of bits in each bitmap
+       aoff := duint32LSym(argssym, 0, uint32(len(lv.livevars))) // number of bitmaps
+       aoff = duint32LSym(argssym, aoff, uint32(args.n))         // number of bits in each bitmap
 
        locals := bvalloc(localswords(lv))
-       loff := duint32(livesym, 0, uint32(len(lv.livevars))) // number of bitmaps
-       loff = duint32(livesym, loff, uint32(locals.n))       // number of bits in each bitmap
+       loff := duint32LSym(livesym, 0, uint32(len(lv.livevars))) // number of bitmaps
+       loff = duint32LSym(livesym, loff, uint32(locals.n))       // number of bits in each bitmap
 
        for _, live := range lv.livevars {
                args.Clear()
@@ -1084,19 +1070,24 @@ func livenessemit(lv *Liveness, argssym, livesym *types.Sym) {
 
                onebitlivepointermap(lv, live, lv.vars, args, locals)
 
-               aoff = dbvec(argssym, aoff, args)
-               loff = dbvec(livesym, loff, locals)
+               aoff = dbvecLSym(argssym, aoff, args)
+               loff = dbvecLSym(livesym, loff, locals)
        }
 
-       finishgclocals(livesym)
-       finishgclocals(argssym)
+       // Give these LSyms content-addressable names,
+       // so that they can be de-duplicated.
+       // This provides significant binary size savings.
+       // It is safe to rename these LSyms because
+       // they are tracked separately from ctxt.Hash.
+       argssym.Name = fmt.Sprintf("gclocals·%x", md5.Sum(argssym.P))
+       livesym.Name = fmt.Sprintf("gclocals·%x", md5.Sum(livesym.P))
 }
 
 // Entry pointer for liveness analysis. Solves for the liveness of
 // pointer variables in the function and emits a runtime data
 // structure read by the garbage collector.
 // Returns a map from GC safe points to their corresponding stack map index.
-func liveness(e *ssafn, f *ssa.Func, argssym, livesym *types.Sym) map[*ssa.Value]int {
+func liveness(e *ssafn, f *ssa.Func) map[*ssa.Value]int {
        // Construct the global liveness state.
        vars := getvariables(e.curfn)
        lv := newliveness(e.curfn, f, vars, e.stkptrsize)
@@ -1111,6 +1102,8 @@ func liveness(e *ssafn, f *ssa.Func, argssym, livesym *types.Sym) map[*ssa.Value
        }
 
        // Emit the live pointer map data structures
-       livenessemit(lv, argssym, livesym)
+       if ls := e.curfn.Func.lsym; ls != nil {
+               livenessemit(lv, &ls.FuncInfo.GCArgs, &ls.FuncInfo.GCLocals)
+       }
        return lv.stackMapIndex
 }
index 0b9af124a23a6e0a463f89c4fa22632195c3dfee..b54e0bca454f82e7ceb872336ed8dcb49e0cc293 100644 (file)
@@ -4326,9 +4326,7 @@ func genssa(f *ssa.Func, pp *Progs) {
        e := f.Frontend().(*ssafn)
 
        // Generate GC bitmaps.
-       gcargs := makefuncdatasym(pp, "gcargs·", obj.FUNCDATA_ArgsPointerMaps, e.curfn)
-       gclocals := makefuncdatasym(pp, "gclocals·", obj.FUNCDATA_LocalsPointerMaps, e.curfn)
-       s.stackMapIndex = liveness(e, f, gcargs, gclocals)
+       s.stackMapIndex = liveness(e, f)
 
        // Remember where each block starts.
        s.bstart = make([]*obj.Prog, f.NumBlocks())
index 893ccf674a7a257598c94d8460233b96e5b0e86a..cc59d1f5b16bdf20d44e9026788a564f13ecd86d 100644 (file)
@@ -330,6 +330,8 @@ type FuncInfo struct {
        Autom    []*Auto
        Pcln     Pcln
        dwarfSym *LSym
+       GCArgs   LSym
+       GCLocals LSym
 }
 
 // Attribute is a set of symbol attributes.
index 7dc5b485b178f07344238499b544cdf140d7f408..71fa1e47d8b793b02f549df1f8d6c27922d994b2 100644 (file)
@@ -139,6 +139,15 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
        dsym.Type = SDWARFINFO
        dsym.Set(AttrDuplicateOK, s.DuplicateOK())
        ctxt.Data = append(ctxt.Data, dsym)
+
+       // Set up the function's gcargs and gclocals.
+       // They will be filled in later if needed.
+       gcargs := &s.FuncInfo.GCArgs
+       gcargs.Set(AttrDuplicateOK, true)
+       gcargs.Type = SRODATA
+       gclocals := &s.FuncInfo.GCLocals
+       gclocals.Set(AttrDuplicateOK, true)
+       gclocals.Type = SRODATA
 }
 
 func (ctxt *Link) Globl(s *LSym, size int64, flag int) {
index 2124e3f814e1a0debbe09d81da33b0219e9eb945..0ad4c6a404998315050a10627d54f132851656cf 100644 (file)
@@ -479,7 +479,7 @@ func (r *objReader) readRef() {
        name := r.readSymName()
        v := r.readInt()
        if v != 0 && v != 1 {
-               log.Fatalf("invalid symbol version %d", v)
+               log.Fatalf("invalid symbol version for %q: %d", name, v)
        }
        if v == 1 {
                v = r.localSymVersion