mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
This adds FUNCDATA and PCDATA that records the register maps much like the existing live arguments maps and live locals maps. The register map is indexed independently from the argument and locals maps since changes in register liveness tend not to correlate with changes to argument and local liveness. This is the final CL toward adding safe-points everywhere. The following CLs will optimize liveness analysis to bring down the cost. The effect of this CL is: name old time/op new time/op delta Template 195ms ± 2% 197ms ± 1% ~ (p=0.136 n=9+9) Unicode 98.4ms ± 2% 99.7ms ± 1% +1.39% (p=0.004 n=10+10) GoTypes 685ms ± 1% 700ms ± 1% +2.06% (p=0.000 n=9+9) Compiler 3.28s ± 1% 3.34s ± 0% +1.71% (p=0.000 n=9+8) SSA 7.79s ± 1% 7.91s ± 1% +1.55% (p=0.000 n=10+9) Flate 133ms ± 2% 133ms ± 2% ~ (p=0.190 n=10+10) GoParser 161ms ± 2% 164ms ± 3% +1.83% (p=0.015 n=10+10) Reflect 450ms ± 1% 457ms ± 1% +1.62% (p=0.000 n=10+10) Tar 183ms ± 2% 185ms ± 1% +0.91% (p=0.008 n=9+10) XML 234ms ± 1% 238ms ± 1% +1.60% (p=0.000 n=9+9) [Geo mean] 411ms 417ms +1.40% name old exe-bytes new exe-bytes delta HelloSize 1.47M ± 0% 1.51M ± 0% +2.79% (p=0.000 n=10+10) Compared to just before "cmd/internal/obj: consolidate emitting entry stack map", the cumulative effect of adding stack maps everywhere and register maps is: name old time/op new time/op delta Template 185ms ± 2% 197ms ± 1% +6.42% (p=0.000 n=10+9) Unicode 96.3ms ± 3% 99.7ms ± 1% +3.60% (p=0.000 n=10+10) GoTypes 658ms ± 0% 700ms ± 1% +6.37% (p=0.000 n=10+9) Compiler 3.14s ± 1% 3.34s ± 0% +6.53% (p=0.000 n=9+8) SSA 7.41s ± 2% 7.91s ± 1% +6.71% (p=0.000 n=9+9) Flate 126ms ± 1% 133ms ± 2% +6.15% (p=0.000 n=10+10) GoParser 153ms ± 1% 164ms ± 3% +6.89% (p=0.000 n=10+10) Reflect 437ms ± 1% 457ms ± 1% +4.59% (p=0.000 n=10+10) Tar 178ms ± 1% 185ms ± 1% +4.18% (p=0.000 n=10+10) XML 223ms ± 1% 238ms ± 1% +6.39% (p=0.000 n=10+9) [Geo mean] 394ms 417ms +5.78% name old alloc/op new alloc/op delta Template 34.5MB ± 0% 38.0MB ± 0% +10.19% (p=0.000 n=10+10) Unicode 29.3MB ± 0% 30.3MB ± 0% +3.56% (p=0.000 n=8+9) GoTypes 113MB ± 0% 125MB ± 0% +10.89% (p=0.000 n=10+10) Compiler 510MB ± 0% 575MB ± 0% +12.79% (p=0.000 n=10+10) SSA 1.46GB ± 0% 1.64GB ± 0% +12.40% (p=0.000 n=10+10) Flate 23.9MB ± 0% 25.9MB ± 0% +8.56% (p=0.000 n=10+10) GoParser 28.0MB ± 0% 30.8MB ± 0% +10.08% (p=0.000 n=10+10) Reflect 77.6MB ± 0% 84.3MB ± 0% +8.63% (p=0.000 n=10+10) Tar 34.1MB ± 0% 37.0MB ± 0% +8.44% (p=0.000 n=10+10) XML 42.7MB ± 0% 47.2MB ± 0% +10.75% (p=0.000 n=10+10) [Geo mean] 76.0MB 83.3MB +9.60% name old allocs/op new allocs/op delta Template 321k ± 0% 337k ± 0% +4.98% (p=0.000 n=10+10) Unicode 337k ± 0% 340k ± 0% +1.04% (p=0.000 n=10+9) GoTypes 1.13M ± 0% 1.18M ± 0% +4.85% (p=0.000 n=10+10) Compiler 4.67M ± 0% 4.96M ± 0% +6.25% (p=0.000 n=10+10) SSA 11.7M ± 0% 12.3M ± 0% +5.69% (p=0.000 n=10+10) Flate 216k ± 0% 226k ± 0% +4.52% (p=0.000 n=10+9) GoParser 271k ± 0% 283k ± 0% +4.52% (p=0.000 n=10+10) Reflect 927k ± 0% 972k ± 0% +4.78% (p=0.000 n=10+10) Tar 318k ± 0% 333k ± 0% +4.56% (p=0.000 n=10+10) XML 376k ± 0% 395k ± 0% +5.04% (p=0.000 n=10+10) [Geo mean] 730k 764k +4.61% name old exe-bytes new exe-bytes delta HelloSize 1.46M ± 0% 1.51M ± 0% +3.66% (p=0.000 n=10+10) For #24543. Change-Id: I91e003dc64151916b384274884bf02a2d6862547 Reviewed-on: https://go-review.googlesource.com/109353 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
295 lines
7.6 KiB
Go
295 lines
7.6 KiB
Go
// Derived from Inferno utils/6c/txt.c
|
|
// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/txt.c
|
|
//
|
|
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
|
|
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
|
|
// Portions Copyright © 1997-1999 Vita Nuova Limited
|
|
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
|
|
// Portions Copyright © 2004,2006 Bruce Ellis
|
|
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
|
|
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
|
|
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
package gc
|
|
|
|
import (
|
|
"cmd/compile/internal/ssa"
|
|
"cmd/compile/internal/types"
|
|
"cmd/internal/obj"
|
|
"cmd/internal/objabi"
|
|
"cmd/internal/src"
|
|
)
|
|
|
|
var sharedProgArray = new([10000]obj.Prog) // *T instead of T to work around issue 19839
|
|
|
|
// Progs accumulates Progs for a function and converts them into machine code.
|
|
type Progs struct {
|
|
Text *obj.Prog // ATEXT Prog for this function
|
|
next *obj.Prog // next Prog
|
|
pc int64 // virtual PC; count of Progs
|
|
pos src.XPos // position to use for new Progs
|
|
curfn *Node // fn these Progs are for
|
|
progcache []obj.Prog // local progcache
|
|
cacheidx int // first free element of progcache
|
|
|
|
nextLive LivenessIndex // liveness index for the next Prog
|
|
prevLive LivenessIndex // last emitted liveness index
|
|
}
|
|
|
|
// newProgs returns a new Progs for fn.
|
|
// worker indicates which of the backend workers will use the Progs.
|
|
func newProgs(fn *Node, worker int) *Progs {
|
|
pp := new(Progs)
|
|
if Ctxt.CanReuseProgs() {
|
|
sz := len(sharedProgArray) / nBackendWorkers
|
|
pp.progcache = sharedProgArray[sz*worker : sz*(worker+1)]
|
|
}
|
|
pp.curfn = fn
|
|
|
|
// prime the pump
|
|
pp.next = pp.NewProg()
|
|
pp.clearp(pp.next)
|
|
|
|
pp.pos = fn.Pos
|
|
pp.settext(fn)
|
|
pp.nextLive = LivenessInvalid
|
|
pp.prevLive = LivenessInvalid
|
|
return pp
|
|
}
|
|
|
|
func (pp *Progs) NewProg() *obj.Prog {
|
|
var p *obj.Prog
|
|
if pp.cacheidx < len(pp.progcache) {
|
|
p = &pp.progcache[pp.cacheidx]
|
|
pp.cacheidx++
|
|
} else {
|
|
p = new(obj.Prog)
|
|
}
|
|
p.Ctxt = Ctxt
|
|
return p
|
|
}
|
|
|
|
// Flush converts from pp to machine code.
|
|
func (pp *Progs) Flush() {
|
|
plist := &obj.Plist{Firstpc: pp.Text, Curfn: pp.curfn}
|
|
obj.Flushplist(Ctxt, plist, pp.NewProg, myimportpath)
|
|
}
|
|
|
|
// Free clears pp and any associated resources.
|
|
func (pp *Progs) Free() {
|
|
if Ctxt.CanReuseProgs() {
|
|
// Clear progs to enable GC and avoid abuse.
|
|
s := pp.progcache[:pp.cacheidx]
|
|
for i := range s {
|
|
s[i] = obj.Prog{}
|
|
}
|
|
}
|
|
// Clear pp to avoid abuse.
|
|
*pp = Progs{}
|
|
}
|
|
|
|
// Prog adds a Prog with instruction As to pp.
|
|
func (pp *Progs) Prog(as obj.As) *obj.Prog {
|
|
if pp.nextLive.stackMapIndex != pp.prevLive.stackMapIndex {
|
|
// Emit stack map index change.
|
|
idx := pp.nextLive.stackMapIndex
|
|
pp.prevLive.stackMapIndex = idx
|
|
p := pp.Prog(obj.APCDATA)
|
|
Addrconst(&p.From, objabi.PCDATA_StackMapIndex)
|
|
Addrconst(&p.To, int64(idx))
|
|
}
|
|
if pp.nextLive.regMapIndex != pp.prevLive.regMapIndex {
|
|
// Emit register map index change.
|
|
idx := pp.nextLive.regMapIndex
|
|
pp.prevLive.regMapIndex = idx
|
|
p := pp.Prog(obj.APCDATA)
|
|
Addrconst(&p.From, objabi.PCDATA_RegMapIndex)
|
|
Addrconst(&p.To, int64(idx))
|
|
}
|
|
|
|
p := pp.next
|
|
pp.next = pp.NewProg()
|
|
pp.clearp(pp.next)
|
|
p.Link = pp.next
|
|
|
|
if !pp.pos.IsKnown() && Debug['K'] != 0 {
|
|
Warn("prog: unknown position (line 0)")
|
|
}
|
|
|
|
p.As = as
|
|
p.Pos = pp.pos
|
|
if pp.pos.IsStmt() == src.PosIsStmt {
|
|
// Clear IsStmt for later Progs at this pos provided that as can be marked as a stmt
|
|
if ssa.LosesStmtMark(as) {
|
|
return p
|
|
}
|
|
pp.pos = pp.pos.WithNotStmt()
|
|
}
|
|
return p
|
|
}
|
|
|
|
func (pp *Progs) clearp(p *obj.Prog) {
|
|
obj.Nopout(p)
|
|
p.As = obj.AEND
|
|
p.Pc = pp.pc
|
|
pp.pc++
|
|
}
|
|
|
|
func (pp *Progs) Appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog {
|
|
q := pp.NewProg()
|
|
pp.clearp(q)
|
|
q.As = as
|
|
q.Pos = p.Pos
|
|
q.From.Type = ftype
|
|
q.From.Reg = freg
|
|
q.From.Offset = foffset
|
|
q.To.Type = ttype
|
|
q.To.Reg = treg
|
|
q.To.Offset = toffset
|
|
q.Link = p.Link
|
|
p.Link = q
|
|
return q
|
|
}
|
|
|
|
func (pp *Progs) settext(fn *Node) {
|
|
if pp.Text != nil {
|
|
Fatalf("Progs.settext called twice")
|
|
}
|
|
ptxt := pp.Prog(obj.ATEXT)
|
|
pp.Text = ptxt
|
|
|
|
if fn.Func.lsym == nil {
|
|
// func _() { }
|
|
return
|
|
}
|
|
|
|
fn.Func.lsym.Func.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, 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() {
|
|
if f.lsym != nil {
|
|
Fatalf("Func.initLSym called twice")
|
|
}
|
|
|
|
if nam := f.Nname; !nam.isBlank() {
|
|
f.lsym = nam.Sym.Linksym()
|
|
if f.Pragma&Systemstack != 0 {
|
|
f.lsym.Set(obj.AttrCFunc, true)
|
|
}
|
|
}
|
|
|
|
var flag int
|
|
if f.Dupok() {
|
|
flag |= obj.DUPOK
|
|
}
|
|
if f.Wrapper() {
|
|
flag |= obj.WRAPPER
|
|
}
|
|
if f.Needctxt() {
|
|
flag |= obj.NEEDCTXT
|
|
}
|
|
if f.Pragma&Nosplit != 0 {
|
|
flag |= obj.NOSPLIT
|
|
}
|
|
if f.ReflectMethod() {
|
|
flag |= obj.REFLECTMETHOD
|
|
}
|
|
|
|
// Clumsy but important.
|
|
// See test/recover.go for test cases and src/reflect/value.go
|
|
// for the actual functions being considered.
|
|
if myimportpath == "reflect" {
|
|
switch f.Nname.Sym.Name {
|
|
case "callReflect", "callMethod":
|
|
flag |= obj.WRAPPER
|
|
}
|
|
}
|
|
|
|
Ctxt.InitTextSym(f.lsym, flag)
|
|
}
|
|
|
|
func ggloblnod(nam *Node) {
|
|
s := nam.Sym.Linksym()
|
|
s.Gotype = ngotype(nam).Linksym()
|
|
flags := 0
|
|
if nam.Name.Readonly() {
|
|
flags = obj.RODATA
|
|
}
|
|
if nam.Type != nil && !types.Haspointers(nam.Type) {
|
|
flags |= obj.NOPTR
|
|
}
|
|
Ctxt.Globl(s, nam.Type.Width, flags)
|
|
}
|
|
|
|
func ggloblsym(s *obj.LSym, width int32, flags int16) {
|
|
if flags&obj.LOCAL != 0 {
|
|
s.Set(obj.AttrLocal, true)
|
|
flags &^= obj.LOCAL
|
|
}
|
|
Ctxt.Globl(s, int64(width), int(flags))
|
|
}
|
|
|
|
func isfat(t *types.Type) bool {
|
|
if t != nil {
|
|
switch t.Etype {
|
|
case TSTRUCT, TARRAY, TSLICE, TSTRING,
|
|
TINTER: // maybe remove later
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func Addrconst(a *obj.Addr, v int64) {
|
|
a.Sym = nil
|
|
a.Type = obj.TYPE_CONST
|
|
a.Offset = v
|
|
}
|
|
|
|
func Patch(p *obj.Prog, to *obj.Prog) {
|
|
if p.To.Type != obj.TYPE_BRANCH {
|
|
Fatalf("patch: not a branch")
|
|
}
|
|
p.To.Val = to
|
|
p.To.Offset = to.Pc
|
|
}
|