mirror of
https://github.com/golang/go
synced 2024-11-23 15:00:03 -07:00
cmd/compile: avoid duplicate GC bitmap symbols
Currently, liveness produces a distinct obj.LSym for each GC bitmap for each function. These are then named by content hash and only ultimately deduplicated by WriteObjFile. For various reasons (see next commit), we want to remove this deduplication behavior from WriteObjFile. Furthermore, it's inefficient to produce these duplicate symbols in the first place. GC bitmaps are the only source of duplicate symbols in the compiler. This commit eliminates these duplicate symbols by declaring them in the Ctxt symbol hash just like every other obj.LSym. As a result, all GC bitmaps with the same content now refer to the same obj.LSym. The next commit will remove deduplication from WriteObjFile. For #27539. Change-Id: I4f15e3d99530122cdf473b7a838c69ef5f79db59 Reviewed-on: https://go-review.googlesource.com/c/146557 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
parent
441cb988b4
commit
15265ec421
@ -185,24 +185,6 @@ func (pp *Progs) settext(fn *Node) {
|
|||||||
ptxt.From.Type = obj.TYPE_MEM
|
ptxt.From.Type = obj.TYPE_MEM
|
||||||
ptxt.From.Name = obj.NAME_EXTERN
|
ptxt.From.Name = obj.NAME_EXTERN
|
||||||
ptxt.From.Sym = fn.Func.lsym
|
ptxt.From.Sym = fn.Func.lsym
|
||||||
|
|
||||||
p := pp.Prog(obj.AFUNCDATA)
|
|
||||||
Addrconst(&p.From, objabi.FUNCDATA_ArgsPointerMaps)
|
|
||||||
p.To.Type = obj.TYPE_MEM
|
|
||||||
p.To.Name = obj.NAME_EXTERN
|
|
||||||
p.To.Sym = &fn.Func.lsym.Func.GCArgs
|
|
||||||
|
|
||||||
p = pp.Prog(obj.AFUNCDATA)
|
|
||||||
Addrconst(&p.From, objabi.FUNCDATA_LocalsPointerMaps)
|
|
||||||
p.To.Type = obj.TYPE_MEM
|
|
||||||
p.To.Name = obj.NAME_EXTERN
|
|
||||||
p.To.Sym = &fn.Func.lsym.Func.GCLocals
|
|
||||||
|
|
||||||
p = pp.Prog(obj.AFUNCDATA)
|
|
||||||
Addrconst(&p.From, objabi.FUNCDATA_RegPointerMaps)
|
|
||||||
p.To.Type = obj.TYPE_MEM
|
|
||||||
p.To.Name = obj.NAME_EXTERN
|
|
||||||
p.To.Sym = &fn.Func.lsym.Func.GCRegs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Func) initLSym() {
|
func (f *Func) initLSym() {
|
||||||
|
@ -277,18 +277,18 @@ func dumpglobls() {
|
|||||||
// Though the object file format handles duplicates efficiently,
|
// Though the object file format handles duplicates efficiently,
|
||||||
// storing only a single copy of the data,
|
// storing only a single copy of the data,
|
||||||
// failure to remove these duplicates adds a few percent to object file size.
|
// failure to remove these duplicates adds a few percent to object file size.
|
||||||
|
//
|
||||||
|
// This is done during the sequential phase after compilation, since
|
||||||
|
// global symbols can't be declared during parallel compilation.
|
||||||
func addGCLocals() {
|
func addGCLocals() {
|
||||||
seen := make(map[string]bool)
|
|
||||||
for _, s := range Ctxt.Text {
|
for _, s := range Ctxt.Text {
|
||||||
if s.Func == nil {
|
if s.Func == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, gcsym := range []*obj.LSym{&s.Func.GCArgs, &s.Func.GCLocals, &s.Func.GCRegs} {
|
for _, gcsym := range []*obj.LSym{s.Func.GCArgs, s.Func.GCLocals, s.Func.GCRegs} {
|
||||||
if seen[gcsym.Name] {
|
if gcsym != nil && !gcsym.OnList() {
|
||||||
continue
|
ggloblsym(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK)
|
||||||
}
|
}
|
||||||
Ctxt.Data = append(Ctxt.Data, gcsym)
|
|
||||||
seen[gcsym.Name] = true
|
|
||||||
}
|
}
|
||||||
if x := s.Func.StackObjects; x != nil {
|
if x := s.Func.StackObjects; x != nil {
|
||||||
ggloblsym(x, int32(len(x.P)), obj.RODATA|obj.LOCAL)
|
ggloblsym(x, int32(len(x.P)), obj.RODATA|obj.LOCAL)
|
||||||
|
@ -1461,7 +1461,7 @@ func (lv *Liveness) printDebug() {
|
|||||||
// first word dumped is the total number of bitmaps. The second word is 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
|
// length of the bitmaps. All bitmaps are assumed to be of equal length. The
|
||||||
// remaining bytes are the raw bitmaps.
|
// remaining bytes are the raw bitmaps.
|
||||||
func (lv *Liveness) emit(argssym, livesym, regssym *obj.LSym) {
|
func (lv *Liveness) emit() (argsSym, liveSym, regsSym *obj.LSym) {
|
||||||
// Size args bitmaps to be just large enough to hold the largest pointer.
|
// Size args bitmaps to be just large enough to hold the largest pointer.
|
||||||
// First, find the largest Xoffset node we care about.
|
// First, find the largest Xoffset node we care about.
|
||||||
// (Nodes without pointers aren't in lv.vars; see livenessShouldTrack.)
|
// (Nodes without pointers aren't in lv.vars; see livenessShouldTrack.)
|
||||||
@ -1489,13 +1489,16 @@ func (lv *Liveness) emit(argssym, livesym, regssym *obj.LSym) {
|
|||||||
// This would require shifting all bitmaps.
|
// This would require shifting all bitmaps.
|
||||||
maxLocals := lv.stkptrsize
|
maxLocals := lv.stkptrsize
|
||||||
|
|
||||||
|
// Temporary symbols for encoding bitmaps.
|
||||||
|
var argsSymTmp, liveSymTmp, regsSymTmp obj.LSym
|
||||||
|
|
||||||
args := bvalloc(int32(maxArgs / int64(Widthptr)))
|
args := bvalloc(int32(maxArgs / int64(Widthptr)))
|
||||||
aoff := duint32(argssym, 0, uint32(len(lv.stackMaps))) // number of bitmaps
|
aoff := duint32(&argsSymTmp, 0, uint32(len(lv.stackMaps))) // number of bitmaps
|
||||||
aoff = duint32(argssym, aoff, uint32(args.n)) // number of bits in each bitmap
|
aoff = duint32(&argsSymTmp, aoff, uint32(args.n)) // number of bits in each bitmap
|
||||||
|
|
||||||
locals := bvalloc(int32(maxLocals / int64(Widthptr)))
|
locals := bvalloc(int32(maxLocals / int64(Widthptr)))
|
||||||
loff := duint32(livesym, 0, uint32(len(lv.stackMaps))) // number of bitmaps
|
loff := duint32(&liveSymTmp, 0, uint32(len(lv.stackMaps))) // number of bitmaps
|
||||||
loff = duint32(livesym, loff, uint32(locals.n)) // number of bits in each bitmap
|
loff = duint32(&liveSymTmp, loff, uint32(locals.n)) // number of bits in each bitmap
|
||||||
|
|
||||||
for _, live := range lv.stackMaps {
|
for _, live := range lv.stackMaps {
|
||||||
args.Clear()
|
args.Clear()
|
||||||
@ -1503,13 +1506,13 @@ func (lv *Liveness) emit(argssym, livesym, regssym *obj.LSym) {
|
|||||||
|
|
||||||
lv.pointerMap(live, lv.vars, args, locals)
|
lv.pointerMap(live, lv.vars, args, locals)
|
||||||
|
|
||||||
aoff = dbvec(argssym, aoff, args)
|
aoff = dbvec(&argsSymTmp, aoff, args)
|
||||||
loff = dbvec(livesym, loff, locals)
|
loff = dbvec(&liveSymTmp, loff, locals)
|
||||||
}
|
}
|
||||||
|
|
||||||
regs := bvalloc(lv.usedRegs())
|
regs := bvalloc(lv.usedRegs())
|
||||||
roff := duint32(regssym, 0, uint32(len(lv.regMaps))) // number of bitmaps
|
roff := duint32(®sSymTmp, 0, uint32(len(lv.regMaps))) // number of bitmaps
|
||||||
roff = duint32(regssym, roff, uint32(regs.n)) // number of bits in each bitmap
|
roff = duint32(®sSymTmp, roff, uint32(regs.n)) // number of bits in each bitmap
|
||||||
if regs.n > 32 {
|
if regs.n > 32 {
|
||||||
// Our uint32 conversion below won't work.
|
// Our uint32 conversion below won't work.
|
||||||
Fatalf("GP registers overflow uint32")
|
Fatalf("GP registers overflow uint32")
|
||||||
@ -1519,25 +1522,29 @@ func (lv *Liveness) emit(argssym, livesym, regssym *obj.LSym) {
|
|||||||
for _, live := range lv.regMaps {
|
for _, live := range lv.regMaps {
|
||||||
regs.Clear()
|
regs.Clear()
|
||||||
regs.b[0] = uint32(live)
|
regs.b[0] = uint32(live)
|
||||||
roff = dbvec(regssym, roff, regs)
|
roff = dbvec(®sSymTmp, roff, regs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give these LSyms content-addressable names,
|
// Give these LSyms content-addressable names,
|
||||||
// so that they can be de-duplicated.
|
// so that they can be de-duplicated.
|
||||||
// This provides significant binary size savings.
|
// This provides significant binary size savings.
|
||||||
// It is safe to rename these LSyms because
|
//
|
||||||
// they are tracked separately from ctxt.hash.
|
// These symbols will be added to Ctxt.Data by addGCLocals
|
||||||
argssym.Name = fmt.Sprintf("gclocals·%x", md5.Sum(argssym.P))
|
// after parallel compilation is done.
|
||||||
livesym.Name = fmt.Sprintf("gclocals·%x", md5.Sum(livesym.P))
|
makeSym := func(tmpSym *obj.LSym) *obj.LSym {
|
||||||
regssym.Name = fmt.Sprintf("gclocals·%x", md5.Sum(regssym.P))
|
return Ctxt.LookupInit(fmt.Sprintf("gclocals·%x", md5.Sum(tmpSym.P)), func(lsym *obj.LSym) {
|
||||||
|
lsym.P = tmpSym.P
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return makeSym(&argsSymTmp), makeSym(&liveSymTmp), makeSym(®sSymTmp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entry pointer for liveness analysis. Solves for the liveness of
|
// Entry pointer for liveness analysis. Solves for the liveness of
|
||||||
// pointer variables in the function and emits a runtime data
|
// pointer variables in the function and emits a runtime data
|
||||||
// structure read by the garbage collector.
|
// structure read by the garbage collector.
|
||||||
// Returns a map from GC safe points to their corresponding stack map index.
|
// Returns a map from GC safe points to their corresponding stack map index.
|
||||||
func liveness(e *ssafn, f *ssa.Func) LivenessMap {
|
func liveness(e *ssafn, f *ssa.Func, pp *Progs) LivenessMap {
|
||||||
// Construct the global liveness state.
|
// Construct the global liveness state.
|
||||||
vars, idx := getvariables(e.curfn)
|
vars, idx := getvariables(e.curfn)
|
||||||
lv := newliveness(e.curfn, f, vars, idx, e.stkptrsize)
|
lv := newliveness(e.curfn, f, vars, idx, e.stkptrsize)
|
||||||
@ -1577,7 +1584,25 @@ func liveness(e *ssafn, f *ssa.Func) LivenessMap {
|
|||||||
|
|
||||||
// Emit the live pointer map data structures
|
// Emit the live pointer map data structures
|
||||||
if ls := e.curfn.Func.lsym; ls != nil {
|
if ls := e.curfn.Func.lsym; ls != nil {
|
||||||
lv.emit(&ls.Func.GCArgs, &ls.Func.GCLocals, &ls.Func.GCRegs)
|
ls.Func.GCArgs, ls.Func.GCLocals, ls.Func.GCRegs = lv.emit()
|
||||||
|
|
||||||
|
p := pp.Prog(obj.AFUNCDATA)
|
||||||
|
Addrconst(&p.From, objabi.FUNCDATA_ArgsPointerMaps)
|
||||||
|
p.To.Type = obj.TYPE_MEM
|
||||||
|
p.To.Name = obj.NAME_EXTERN
|
||||||
|
p.To.Sym = ls.Func.GCArgs
|
||||||
|
|
||||||
|
p = pp.Prog(obj.AFUNCDATA)
|
||||||
|
Addrconst(&p.From, objabi.FUNCDATA_LocalsPointerMaps)
|
||||||
|
p.To.Type = obj.TYPE_MEM
|
||||||
|
p.To.Name = obj.NAME_EXTERN
|
||||||
|
p.To.Sym = ls.Func.GCLocals
|
||||||
|
|
||||||
|
p = pp.Prog(obj.AFUNCDATA)
|
||||||
|
Addrconst(&p.From, objabi.FUNCDATA_RegPointerMaps)
|
||||||
|
p.To.Type = obj.TYPE_MEM
|
||||||
|
p.To.Name = obj.NAME_EXTERN
|
||||||
|
p.To.Sym = ls.Func.GCRegs
|
||||||
}
|
}
|
||||||
return lv.livenessMap
|
return lv.livenessMap
|
||||||
}
|
}
|
||||||
|
@ -5051,7 +5051,7 @@ func genssa(f *ssa.Func, pp *Progs) {
|
|||||||
|
|
||||||
e := f.Frontend().(*ssafn)
|
e := f.Frontend().(*ssafn)
|
||||||
|
|
||||||
s.livenessMap = liveness(e, f)
|
s.livenessMap = liveness(e, f, pp)
|
||||||
emitStackObjects(e, pp)
|
emitStackObjects(e, pp)
|
||||||
|
|
||||||
// Remember where each block starts.
|
// Remember where each block starts.
|
||||||
|
@ -403,9 +403,9 @@ type FuncInfo struct {
|
|||||||
dwarfAbsFnSym *LSym
|
dwarfAbsFnSym *LSym
|
||||||
dwarfIsStmtSym *LSym
|
dwarfIsStmtSym *LSym
|
||||||
|
|
||||||
GCArgs LSym
|
GCArgs *LSym
|
||||||
GCLocals LSym
|
GCLocals *LSym
|
||||||
GCRegs LSym
|
GCRegs *LSym
|
||||||
StackObjects *LSym
|
StackObjects *LSym
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,18 +147,6 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
|
|||||||
isstmt.Type = objabi.SDWARFMISC
|
isstmt.Type = objabi.SDWARFMISC
|
||||||
isstmt.Set(AttrDuplicateOK, s.DuplicateOK())
|
isstmt.Set(AttrDuplicateOK, s.DuplicateOK())
|
||||||
ctxt.Data = append(ctxt.Data, isstmt)
|
ctxt.Data = append(ctxt.Data, isstmt)
|
||||||
|
|
||||||
// Set up the function's gcargs and gclocals.
|
|
||||||
// They will be filled in later if needed.
|
|
||||||
gcargs := &s.Func.GCArgs
|
|
||||||
gcargs.Set(AttrDuplicateOK, true)
|
|
||||||
gcargs.Type = objabi.SRODATA
|
|
||||||
gclocals := &s.Func.GCLocals
|
|
||||||
gclocals.Set(AttrDuplicateOK, true)
|
|
||||||
gclocals.Type = objabi.SRODATA
|
|
||||||
gcregs := &s.Func.GCRegs
|
|
||||||
gcregs.Set(AttrDuplicateOK, true)
|
|
||||||
gcregs.Type = objabi.SRODATA
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctxt *Link) Globl(s *LSym, size int64, flag int) {
|
func (ctxt *Link) Globl(s *LSym, size int64, flag int) {
|
||||||
|
Loading…
Reference in New Issue
Block a user