Commit 5b59b32c authored by Josh Bleecher Snyder's avatar Josh Bleecher Snyder

cmd/compile: teach assemblers to accept a Prog allocator

The existing bulk Prog allocator is not concurrency-safe.
To allow for concurrency-safe bulk allocation of Progs,
I want to move Prog allocation and caching upstream,
to the clients of cmd/internal/obj.

This is a preliminary enabling refactoring.
After this CL, instead of calling Ctxt.NewProg
throughout the assemblers, we thread through
a newprog function that returns a new Prog.

That function is set up to be Ctxt.NewProg,
so there are no real changes in this CL;
this CL only establishes the plumbing.

Passes toolstash-check -all.
Negligible compiler performance impact.

Updates #15756

name        old time/op     new time/op     delta
Template        213ms ± 3%      214ms ± 4%    ~     (p=0.574 n=49+47)
Unicode        90.1ms ± 5%     89.9ms ± 4%    ~     (p=0.417 n=50+49)
GoTypes         585ms ± 4%      584ms ± 3%    ~     (p=0.466 n=49+49)
SSA             6.50s ± 3%      6.52s ± 2%    ~     (p=0.251 n=49+49)
Flate           128ms ± 4%      128ms ± 4%    ~     (p=0.673 n=49+50)
GoParser        152ms ± 3%      152ms ± 3%    ~     (p=0.810 n=48+49)
Reflect         372ms ± 4%      372ms ± 5%    ~     (p=0.778 n=49+50)
Tar             113ms ± 5%      111ms ± 4%  -0.98%  (p=0.016 n=50+49)
XML             208ms ± 3%      208ms ± 2%    ~     (p=0.483 n=47+49)
[Geo mean]      285ms           285ms       -0.17%

name        old user-ns/op  new user-ns/op  delta
Template         253M ± 8%       254M ± 9%    ~     (p=0.899 n=50+50)
Unicode          106M ± 9%       106M ±11%    ~     (p=0.642 n=50+50)
GoTypes          736M ± 4%       740M ± 4%    ~     (p=0.121 n=50+49)
SSA             8.82G ± 3%      8.88G ± 2%  +0.65%  (p=0.006 n=49+48)
Flate            147M ± 4%       147M ± 5%    ~     (p=0.844 n=47+48)
GoParser         179M ± 4%       178M ± 6%    ~     (p=0.785 n=50+50)
Reflect          443M ± 6%       441M ± 5%    ~     (p=0.850 n=48+47)
Tar              126M ± 5%       126M ± 5%    ~     (p=0.734 n=50+50)
XML              244M ± 5%       244M ± 5%    ~     (p=0.594 n=49+50)
[Geo mean]       341M            341M       +0.11%

Change-Id: Ice962f61eb3a524c2db00a166cb582c22caa7d68
Reviewed-on: https://go-review.googlesource.com/39633
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarKeith Randall <khr@golang.org>
parent 7e068895
......@@ -279,7 +279,7 @@ var deferreturn *obj.LSym
// p->pc if extra padding is necessary.
// In rare cases, asmoutnacl might split p into two instructions.
// origPC is the PC for this Prog (no padding is taken into account).
func asmoutnacl(ctxt *obj.Link, origPC int32, p *obj.Prog, o *Optab, out []uint32) int {
func asmoutnacl(ctxt *obj.Link, newprog obj.ProgAlloc, origPC int32, p *obj.Prog, o *Optab, out []uint32) int {
size := int(o.size)
// instruction specific
......@@ -406,7 +406,7 @@ func asmoutnacl(ctxt *obj.Link, origPC int32, p *obj.Prog, o *Optab, out []uint3
// split it into two instructions:
// ADD $-100004, R13
// MOVW R14, 0(R13)
q := ctxt.NewProg()
q := newprog()
p.Scond &^= C_WBIT
*q = *p
......@@ -486,7 +486,7 @@ func asmoutnacl(ctxt *obj.Link, origPC int32, p *obj.Prog, o *Optab, out []uint3
if p.Scond&(C_PBIT|C_WBIT) != 0 {
ctxt.Diag("unsupported instruction (.P/.W): %v", p)
}
q := ctxt.NewProg()
q := newprog()
*q = *p
var a2 *obj.Addr
if p.To.Type == obj.TYPE_MEM {
......@@ -547,7 +547,7 @@ func asmoutnacl(ctxt *obj.Link, origPC int32, p *obj.Prog, o *Optab, out []uint3
return size
}
func span5(ctxt *obj.Link, cursym *obj.LSym) {
func span5(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
var p *obj.Prog
var op *obj.Prog
......@@ -572,7 +572,7 @@ func span5(ctxt *obj.Link, cursym *obj.LSym) {
var o *Optab
for ; p != nil || ctxt.Blitrl != nil; op, p = p, p.Link {
if p == nil {
if checkpool(ctxt, op, 0) {
if checkpool(ctxt, newprog, op, 0) {
p = op
continue
}
......@@ -588,7 +588,7 @@ func span5(ctxt *obj.Link, cursym *obj.LSym) {
if ctxt.Headtype != obj.Hnacl {
m = int(o.size)
} else {
m = asmoutnacl(ctxt, c, p, o, nil)
m = asmoutnacl(ctxt, newprog, c, p, o, nil)
c = int32(p.Pc) // asmoutnacl might change pc for alignment
o = oplook(ctxt, p) // asmoutnacl might change p in rare cases
}
......@@ -600,7 +600,7 @@ func span5(ctxt *obj.Link, cursym *obj.LSym) {
// must check literal pool here in case p generates many instructions
if ctxt.Blitrl != nil {
i = m
if checkpool(ctxt, op, i) {
if checkpool(ctxt, newprog, op, i) {
p = op
continue
}
......@@ -613,19 +613,19 @@ func span5(ctxt *obj.Link, cursym *obj.LSym) {
switch o.flag & (LFROM | LTO | LPOOL) {
case LFROM:
addpool(ctxt, p, &p.From)
addpool(ctxt, newprog, p, &p.From)
case LTO:
addpool(ctxt, p, &p.To)
addpool(ctxt, newprog, p, &p.To)
case LPOOL:
if p.Scond&C_SCOND == C_SCOND_NONE {
flushpool(ctxt, p, 0, 0)
flushpool(ctxt, newprog, p, 0, 0)
}
}
if p.As == AMOVW && p.To.Type == obj.TYPE_REG && p.To.Reg == REGPC && p.Scond&C_SCOND == C_SCOND_NONE {
flushpool(ctxt, p, 0, 0)
flushpool(ctxt, newprog, p, 0, 0)
}
c += int32(m)
}
......@@ -685,7 +685,7 @@ func span5(ctxt *obj.Link, cursym *obj.LSym) {
if ctxt.Headtype != obj.Hnacl {
m = int(o.size)
} else {
m = asmoutnacl(ctxt, c, p, o, nil)
m = asmoutnacl(ctxt, newprog, c, p, o, nil)
}
if p.Pc != int64(opc) {
bflag = 1
......@@ -746,7 +746,7 @@ func span5(ctxt *obj.Link, cursym *obj.LSym) {
asmout(ctxt, p, o, out[:])
m = int(o.size)
} else {
m = asmoutnacl(ctxt, c, p, o, out[:])
m = asmoutnacl(ctxt, newprog, c, p, o, out[:])
if int64(opc) != p.Pc {
ctxt.Diag("asmoutnacl broken: pc changed (%d->%d) in last stage: %v", opc, int32(p.Pc), p)
}
......@@ -795,22 +795,22 @@ func span5(ctxt *obj.Link, cursym *obj.LSym) {
* drop the pool now, and branch round it.
* this happens only in extended basic blocks that exceed 4k.
*/
func checkpool(ctxt *obj.Link, p *obj.Prog, sz int) bool {
func checkpool(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog, sz int) bool {
if pool.size >= 0xff0 || immaddr(int32((p.Pc+int64(sz)+4)+4+int64(12+pool.size)-int64(pool.start+8))) == 0 {
return flushpool(ctxt, p, 1, 0)
return flushpool(ctxt, newprog, p, 1, 0)
} else if p.Link == nil {
return flushpool(ctxt, p, 2, 0)
return flushpool(ctxt, newprog, p, 2, 0)
}
return false
}
func flushpool(ctxt *obj.Link, p *obj.Prog, skip int, force int) bool {
func flushpool(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog, skip int, force int) bool {
if ctxt.Blitrl != nil {
if skip != 0 {
if false && skip == 1 {
fmt.Printf("note: flush literal pool at %x: len=%d ref=%x\n", uint64(p.Pc+4), pool.size, pool.start)
}
q := ctxt.NewProg()
q := newprog()
q.As = AB
q.To.Type = obj.TYPE_BRANCH
q.Pcond = p.Link
......@@ -822,7 +822,7 @@ func flushpool(ctxt *obj.Link, p *obj.Prog, skip int, force int) bool {
}
if ctxt.Headtype == obj.Hnacl && pool.size%16 != 0 {
// if pool is not multiple of 16 bytes, add an alignment marker
q := ctxt.NewProg()
q := newprog()
q.As = ADATABUNDLEEND
ctxt.Elitrl.Link = q
......@@ -850,7 +850,7 @@ func flushpool(ctxt *obj.Link, p *obj.Prog, skip int, force int) bool {
return false
}
func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
func addpool(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog, a *obj.Addr) {
var t obj.Prog
c := aclass(ctxt, a)
......@@ -894,7 +894,7 @@ func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
if ctxt.Headtype == obj.Hnacl && pool.size%16 == 0 {
// start a new data bundle
q := ctxt.NewProg()
q := newprog()
q.As = ADATABUNDLE
q.Pc = int64(pool.size)
pool.size += 4
......@@ -908,7 +908,7 @@ func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
ctxt.Elitrl = q
}
q := ctxt.NewProg()
q := newprog()
*q = t
q.Pc = int64(pool.size)
......
This diff is collapsed.
......@@ -524,7 +524,7 @@ var pool struct {
size uint32
}
func span7(ctxt *obj.Link, cursym *obj.LSym) {
func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p := cursym.Text
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
return
......@@ -557,19 +557,19 @@ func span7(ctxt *obj.Link, cursym *obj.LSym) {
switch o.flag & (LFROM | LTO) {
case LFROM:
addpool(ctxt, p, &p.From)
addpool(ctxt, newprog, p, &p.From)
case LTO:
addpool(ctxt, p, &p.To)
addpool(ctxt, newprog, p, &p.To)
break
}
if p.As == AB || p.As == obj.ARET || p.As == AERET { /* TODO: other unconditional operations */
checkpool(ctxt, p, 0)
checkpool(ctxt, newprog, p, 0)
}
c += int64(m)
if ctxt.Blitrl != nil {
checkpool(ctxt, p, 1)
checkpool(ctxt, newprog, p, 1)
}
}
......@@ -598,14 +598,14 @@ func span7(ctxt *obj.Link, cursym *obj.LSym) {
if (o.type_ == 7 || o.type_ == 39) && p.Pcond != nil { // 7: BEQ and like, 39: CBZ and like
otxt := p.Pcond.Pc - c
if otxt <= -(1<<18)+10 || otxt >= (1<<18)-10 {
q := ctxt.NewProg()
q := newprog()
q.Link = p.Link
p.Link = q
q.As = AB
q.To.Type = obj.TYPE_BRANCH
q.Pcond = p.Pcond
p.Pcond = q
q = ctxt.NewProg()
q = newprog()
q.Link = p.Link
p.Link = q
q.As = AB
......@@ -670,21 +670,21 @@ func span7(ctxt *obj.Link, cursym *obj.LSym) {
* to go out of range of a 1Mb PC-relative offset
* drop the pool now, and branch round it.
*/
func checkpool(ctxt *obj.Link, p *obj.Prog, skip int) {
func checkpool(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog, skip int) {
if pool.size >= 0xffff0 || !ispcdisp(int32(p.Pc+4+int64(pool.size)-int64(pool.start)+8)) {
flushpool(ctxt, p, skip)
flushpool(ctxt, newprog, p, skip)
} else if p.Link == nil {
flushpool(ctxt, p, 2)
flushpool(ctxt, newprog, p, 2)
}
}
func flushpool(ctxt *obj.Link, p *obj.Prog, skip int) {
func flushpool(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog, skip int) {
if ctxt.Blitrl != nil {
if skip != 0 {
if ctxt.Debugvlog && skip == 1 {
fmt.Printf("note: flush literal pool at %#x: len=%d ref=%x\n", uint64(p.Pc+4), pool.size, pool.start)
}
q := ctxt.NewProg()
q := newprog()
q.As = AB
q.To.Type = obj.TYPE_BRANCH
q.Pcond = p.Link
......@@ -715,10 +715,10 @@ func flushpool(ctxt *obj.Link, p *obj.Prog, skip int) {
/*
* TODO: hash
*/
func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
func addpool(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog, a *obj.Addr) {
c := aclass(ctxt, a)
lit := ctxt.Instoffset
t := *ctxt.NewProg()
t := *newprog()
t.As = AWORD
sz := 4
......@@ -789,7 +789,7 @@ func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
}
}
q := ctxt.NewProg()
q := newprog()
*q = t
q.Pc = int64(pool.size)
if ctxt.Blitrl == nil {
......
This diff is collapsed.
......@@ -76,8 +76,8 @@ func mkfwd(sym *LSym) {
}
}
func Appendp(ctxt *Link, q *Prog) *Prog {
p := ctxt.NewProg()
func Appendp(q *Prog, newprog ProgAlloc) *Prog {
p := newprog()
p.Link = q.Link
q.Link = p
p.Pos = q.Pos
......
......@@ -224,7 +224,8 @@ const (
// Each Prog is charged to a specific source line in the debug information,
// specified by Pos.Line().
// Every Prog has a Ctxt field that defines its context.
// Progs should be allocated using ctxt.NewProg(), not new(Prog).
// For performance reasons, Progs usually are usually bulk allocated, cached, and reused;
// those bulk allocators should always be used, rather than new(Prog).
//
// The other fields not yet mentioned are for use by the back ends and should
// be left zeroed by creators of Prog lists.
......@@ -789,9 +790,9 @@ type SymVer struct {
// LinkArch is the definition of a single architecture.
type LinkArch struct {
*sys.Arch
Preprocess func(*Link, *LSym)
Assemble func(*Link, *LSym)
Progedit func(*Link, *Prog)
Preprocess func(*Link, *LSym, ProgAlloc)
Assemble func(*Link, *LSym, ProgAlloc)
Progedit func(*Link, *Prog, ProgAlloc)
UnaryDst map[As]bool // Instruction takes one operand, a destination.
}
......
......@@ -373,7 +373,7 @@ var oprange [ALAST & obj.AMask][]Optab
var xcmp [C_NCLASS][C_NCLASS]bool
func span0(ctxt *obj.Link, cursym *obj.LSym) {
func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p := cursym.Text
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
return
......@@ -430,7 +430,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym) {
if o.type_ == 6 && p.Pcond != nil {
otxt = p.Pcond.Pc - c
if otxt < -(1<<17)+10 || otxt >= (1<<17)-10 {
q = ctxt.NewProg()
q = newprog()
q.Link = p.Link
p.Link = q
q.As = AJMP
......@@ -438,7 +438,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym) {
q.To.Type = obj.TYPE_BRANCH
q.Pcond = p.Pcond
p.Pcond = q
q = ctxt.NewProg()
q = newprog()
q.Link = p.Link
p.Link = q
q.As = AJMP
......@@ -446,8 +446,8 @@ func span0(ctxt *obj.Link, cursym *obj.LSym) {
q.To.Type = obj.TYPE_BRANCH
q.Pcond = q.Link.Link
addnop(ctxt, p.Link)
addnop(ctxt, p)
addnop(ctxt, p.Link, newprog)
addnop(ctxt, p, newprog)
bflag = 1
}
}
......
This diff is collapsed.
......@@ -117,7 +117,7 @@ func checkaddr(ctxt *Link, p *Prog, a *Addr) {
ctxt.Diag("invalid encoding for argument %v", p)
}
func linkpatch(ctxt *Link, sym *LSym) {
func linkpatch(ctxt *Link, sym *LSym, newprog ProgAlloc) {
var c int32
var name string
var q *Prog
......@@ -130,7 +130,7 @@ func linkpatch(ctxt *Link, sym *LSym) {
checkaddr(ctxt, p, &p.To)
if ctxt.Arch.Progedit != nil {
ctxt.Arch.Progedit(ctxt, p)
ctxt.Arch.Progedit(ctxt, p, newprog)
}
if p.To.Type != TYPE_BRANCH {
continue
......
......@@ -15,6 +15,10 @@ type Plist struct {
Curfn interface{} // holds a *gc.Node, if non-nil
}
// ProgAlloc is a function that allocates Progs.
// It is used to provide access to cached/bulk-allocated Progs to the assemblers.
type ProgAlloc func() *Prog
func Flushplist(ctxt *Link, plist *Plist) {
flushplist(ctxt, plist, !ctxt.Debugasm)
}
......@@ -97,6 +101,8 @@ func flushplist(ctxt *Link, plist *Plist, freeProgs bool) {
etext = p
}
newprog := ProgAlloc(ctxt.NewProg)
// Add reference to Go arguments for C or assembly functions without them.
for _, s := range text {
if !strings.HasPrefix(s.Name, "\"\".") {
......@@ -111,7 +117,7 @@ func flushplist(ctxt *Link, plist *Plist, freeProgs bool) {
}
if !found {
p := Appendp(ctxt, s.Text)
p := Appendp(s.Text, newprog)
p.As = AFUNCDATA
p.From.Type = TYPE_CONST
p.From.Offset = FUNCDATA_ArgsPointerMaps
......@@ -124,9 +130,9 @@ func flushplist(ctxt *Link, plist *Plist, freeProgs bool) {
// Turn functions into machine code images.
for _, s := range text {
mkfwd(s)
linkpatch(ctxt, s)
ctxt.Arch.Preprocess(ctxt, s)
ctxt.Arch.Assemble(ctxt, s)
linkpatch(ctxt, s, newprog)
ctxt.Arch.Preprocess(ctxt, s, newprog)
ctxt.Arch.Assemble(ctxt, s, newprog)
linkpcln(ctxt, s)
makeFuncDebugEntry(ctxt, plist.Curfn, s)
if freeProgs {
......
......@@ -552,7 +552,7 @@ var oprange [ALAST & obj.AMask][]Optab
var xcmp [C_NCLASS][C_NCLASS]bool
func span9(ctxt *obj.Link, cursym *obj.LSym) {
func span9(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p := cursym.Text
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
return
......@@ -609,14 +609,14 @@ func span9(ctxt *obj.Link, cursym *obj.LSym) {
if (o.type_ == 16 || o.type_ == 17) && p.Pcond != nil {
otxt = p.Pcond.Pc - c
if otxt < -(1<<15)+10 || otxt >= (1<<15)-10 {
q = ctxt.NewProg()
q = newprog()
q.Link = p.Link
p.Link = q
q.As = ABR
q.To.Type = obj.TYPE_BRANCH
q.Pcond = p.Pcond
p.Pcond = q
q = ctxt.NewProg()
q = newprog()
q.Link = p.Link
p.Link = q
q.As = ABR
......
This diff is collapsed.
......@@ -385,7 +385,7 @@ var oprange [ALAST & obj.AMask][]Optab
var xcmp [C_NCLASS][C_NCLASS]bool
func spanz(ctxt *obj.Link, cursym *obj.LSym) {
func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p := cursym.Text
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
return
......
This diff is collapsed.
......@@ -1762,7 +1762,7 @@ func spadjop(ctxt *obj.Link, p *obj.Prog, l, q obj.As) obj.As {
return q
}
func span6(ctxt *obj.Link, s *obj.LSym) {
func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
if s.P != nil {
return
}
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment