mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: speed up compiling with -S
Compiling with -S was not implemented with performance in mind. It allocates profligately. Compiling with -S is ~58% slower, allocates ~47% more memory, and does ~183% more allocations. compilecmp now uses -S to do finer-grained comparisons between compiler versions, so I now care about its performance. This change picks some of the lowest hanging fruit, mostly by modifying printing routines to print directly to a writer, rather than constructing a string first. I have confirmed that compiling std+cmd with "-gcflags=all=-S -p=1" and CGO_ENABLED=0 yields identical results before/after this change. (-p=1 makes package compilation order deterministic. CGO_ENABLED=0 prevents cgo temp workdirs from showing up in filenames.) Using the -S flag, the compiler performance impact is: name old time/op new time/op delta Template 344ms ± 2% 301ms ± 2% -12.45% (p=0.000 n=22+24) Unicode 136ms ± 3% 121ms ± 3% -11.40% (p=0.000 n=24+25) GoTypes 1.24s ± 5% 1.09s ± 3% -12.58% (p=0.000 n=25+25) Compiler 5.66s ± 4% 5.06s ± 2% -10.56% (p=0.000 n=25+20) SSA 19.9s ± 3% 17.2s ± 4% -13.64% (p=0.000 n=25+25) Flate 212ms ± 2% 188ms ± 2% -11.33% (p=0.000 n=25+24) GoParser 278ms ± 3% 242ms ± 1% -12.84% (p=0.000 n=23+24) Reflect 743ms ± 3% 657ms ± 5% -11.56% (p=0.000 n=24+25) Tar 295ms ± 2% 263ms ± 2% -10.78% (p=0.000 n=25+25) XML 409ms ± 2% 360ms ± 3% -12.03% (p=0.000 n=24+25) [Geo mean] 714ms 629ms -11.92% name old user-time/op new user-time/op delta Template 430ms ± 5% 388ms ± 3% -9.76% (p=0.000 n=21+24) Unicode 202ms ±12% 171ms ± 5% -15.21% (p=0.000 n=25+23) GoTypes 1.58s ± 3% 1.42s ± 3% -9.58% (p=0.000 n=24+24) Compiler 7.42s ± 3% 6.68s ± 8% -9.93% (p=0.000 n=25+25) SSA 26.9s ± 3% 22.9s ± 3% -14.85% (p=0.000 n=25+25) Flate 260ms ± 6% 234ms ± 3% -9.69% (p=0.000 n=23+25) GoParser 354ms ± 1% 296ms ± 3% -16.46% (p=0.000 n=23+25) Reflect 953ms ± 2% 865ms ± 4% -9.14% (p=0.000 n=24+24) Tar 380ms ± 2% 348ms ± 2% -8.28% (p=0.000 n=25+22) XML 530ms ± 3% 451ms ± 3% -15.01% (p=0.000 n=24+23) [Geo mean] 929ms 819ms -11.84% name old alloc/op new alloc/op delta Template 54.1MB ± 0% 44.3MB ± 0% -18.24% (p=0.000 n=24+24) Unicode 33.5MB ± 0% 30.6MB ± 0% -8.57% (p=0.000 n=25+25) GoTypes 189MB ± 0% 152MB ± 0% -19.55% (p=0.000 n=25+23) Compiler 875MB ± 0% 703MB ± 0% -19.70% (p=0.000 n=25+25) SSA 3.19GB ± 0% 2.51GB ± 0% -21.50% (p=0.000 n=25+25) Flate 32.9MB ± 0% 27.3MB ± 0% -17.04% (p=0.000 n=25+25) GoParser 43.9MB ± 0% 35.1MB ± 0% -20.19% (p=0.000 n=25+25) Reflect 117MB ± 0% 96MB ± 0% -18.22% (p=0.000 n=24+23) Tar 48.6MB ± 0% 40.6MB ± 0% -16.39% (p=0.000 n=25+24) XML 65.7MB ± 0% 53.9MB ± 0% -17.93% (p=0.000 n=25+23) [Geo mean] 118MB 97MB -17.80% name old allocs/op new allocs/op delta Template 1.07M ± 0% 0.60M ± 0% -43.90% (p=0.000 n=25+24) Unicode 539k ± 0% 398k ± 0% -26.20% (p=0.000 n=23+25) GoTypes 3.97M ± 0% 2.19M ± 0% -44.90% (p=0.000 n=25+24) Compiler 17.6M ± 0% 9.5M ± 0% -46.39% (p=0.000 n=22+23) SSA 66.1M ± 0% 34.1M ± 0% -48.41% (p=0.000 n=25+22) Flate 629k ± 0% 365k ± 0% -41.95% (p=0.000 n=25+25) GoParser 929k ± 0% 500k ± 0% -46.11% (p=0.000 n=25+25) Reflect 2.49M ± 0% 1.47M ± 0% -41.00% (p=0.000 n=24+25) Tar 919k ± 0% 534k ± 0% -41.94% (p=0.000 n=25+24) XML 1.28M ± 0% 0.71M ± 0% -44.72% (p=0.000 n=25+24) [Geo mean] 2.32M 1.33M -42.82% This change also speeds up cmd/objdump a modest amount, ~4%. Change-Id: I7c7aa2b365688bc44b3ef6e1d03bcf934699cabc Reviewed-on: https://go-review.googlesource.com/c/go/+/216857 Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
04040ec9f9
commit
1dcf34f2ec
4 changed files with 134 additions and 94 deletions
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"cmd/internal/objabi"
|
"cmd/internal/objabi"
|
||||||
"cmd/internal/sys"
|
"cmd/internal/sys"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
@ -262,13 +263,13 @@ func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
|
||||||
fmt.Fprintf(ctxt.Bso, "\n")
|
fmt.Fprintf(ctxt.Bso, "\n")
|
||||||
if s.Type == objabi.STEXT {
|
if s.Type == objabi.STEXT {
|
||||||
for p := s.Func.Text; p != nil; p = p.Link {
|
for p := s.Func.Text; p != nil; p = p.Link {
|
||||||
var s string
|
fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc)))
|
||||||
if ctxt.Debugasm > 1 {
|
if ctxt.Debugasm > 1 {
|
||||||
s = p.String()
|
io.WriteString(ctxt.Bso, p.String())
|
||||||
} else {
|
} else {
|
||||||
s = p.InnermostString()
|
p.InnermostString(ctxt.Bso)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(ctxt.Bso, "\t%#04x %s\n", uint(int(p.Pc)), s)
|
fmt.Fprintln(ctxt.Bso)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < len(s.P); i += 16 {
|
for i := 0; i < len(s.P); i += 16 {
|
||||||
|
|
@ -283,11 +284,11 @@ func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
|
||||||
fmt.Fprintf(ctxt.Bso, " ")
|
fmt.Fprintf(ctxt.Bso, " ")
|
||||||
for j = i; j < i+16 && j < len(s.P); j++ {
|
for j = i; j < i+16 && j < len(s.P); j++ {
|
||||||
c := int(s.P[j])
|
c := int(s.P[j])
|
||||||
|
b := byte('.')
|
||||||
if ' ' <= c && c <= 0x7e {
|
if ' ' <= c && c <= 0x7e {
|
||||||
fmt.Fprintf(ctxt.Bso, "%c", c)
|
b = byte(c)
|
||||||
} else {
|
|
||||||
fmt.Fprintf(ctxt.Bso, ".")
|
|
||||||
}
|
}
|
||||||
|
ctxt.Bso.WriteByte(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(ctxt.Bso, "\n")
|
fmt.Fprintf(ctxt.Bso, "\n")
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"cmd/internal/objabi"
|
"cmd/internal/objabi"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -17,8 +18,8 @@ const REG_NONE = 0
|
||||||
func (p *Prog) Line() string {
|
func (p *Prog) Line() string {
|
||||||
return p.Ctxt.OutermostPos(p.Pos).Format(false, true)
|
return p.Ctxt.OutermostPos(p.Pos).Format(false, true)
|
||||||
}
|
}
|
||||||
func (p *Prog) InnermostLine() string {
|
func (p *Prog) InnermostLine(w io.Writer) {
|
||||||
return p.Ctxt.InnermostPos(p.Pos).Format(false, true)
|
p.Ctxt.InnermostPos(p.Pos).WriteTo(w, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InnermostLineNumber returns a string containing the line number for the
|
// InnermostLineNumber returns a string containing the line number for the
|
||||||
|
|
@ -121,45 +122,61 @@ func (p *Prog) String() string {
|
||||||
return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.Line(), p.InstructionString())
|
return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.Line(), p.InstructionString())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Prog) InnermostString() string {
|
func (p *Prog) InnermostString(w io.Writer) {
|
||||||
if p == nil {
|
if p == nil {
|
||||||
return "<nil Prog>"
|
io.WriteString(w, "<nil Prog>")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if p.Ctxt == nil {
|
if p.Ctxt == nil {
|
||||||
return "<Prog without ctxt>"
|
io.WriteString(w, "<Prog without ctxt>")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.InnermostLine(), p.InstructionString())
|
fmt.Fprintf(w, "%.5d (", p.Pc)
|
||||||
|
p.InnermostLine(w)
|
||||||
|
io.WriteString(w, ")\t")
|
||||||
|
p.WriteInstructionString(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstructionString returns a string representation of the instruction without preceding
|
// InstructionString returns a string representation of the instruction without preceding
|
||||||
// program counter or file and line number.
|
// program counter or file and line number.
|
||||||
func (p *Prog) InstructionString() string {
|
func (p *Prog) InstructionString() string {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
p.WriteInstructionString(buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteInstructionString writes a string representation of the instruction without preceding
|
||||||
|
// program counter or file and line number.
|
||||||
|
func (p *Prog) WriteInstructionString(w io.Writer) {
|
||||||
if p == nil {
|
if p == nil {
|
||||||
return "<nil Prog>"
|
io.WriteString(w, "<nil Prog>")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.Ctxt == nil {
|
if p.Ctxt == nil {
|
||||||
return "<Prog without ctxt>"
|
io.WriteString(w, "<Prog without ctxt>")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sc := CConv(p.Scond)
|
sc := CConv(p.Scond)
|
||||||
|
|
||||||
var buf bytes.Buffer
|
io.WriteString(w, p.As.String())
|
||||||
|
io.WriteString(w, sc)
|
||||||
fmt.Fprintf(&buf, "%v%s", p.As, sc)
|
|
||||||
sep := "\t"
|
sep := "\t"
|
||||||
|
|
||||||
if p.From.Type != TYPE_NONE {
|
if p.From.Type != TYPE_NONE {
|
||||||
fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.From))
|
io.WriteString(w, sep)
|
||||||
|
WriteDconv(w, p, &p.From)
|
||||||
sep = ", "
|
sep = ", "
|
||||||
}
|
}
|
||||||
if p.Reg != REG_NONE {
|
if p.Reg != REG_NONE {
|
||||||
// Should not happen but might as well show it if it does.
|
// Should not happen but might as well show it if it does.
|
||||||
fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.Reg)))
|
fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.Reg)))
|
||||||
sep = ", "
|
sep = ", "
|
||||||
}
|
}
|
||||||
for i := range p.RestArgs {
|
for i := range p.RestArgs {
|
||||||
fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.RestArgs[i]))
|
io.WriteString(w, sep)
|
||||||
|
WriteDconv(w, p, &p.RestArgs[i])
|
||||||
sep = ", "
|
sep = ", "
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,17 +187,17 @@ func (p *Prog) InstructionString() string {
|
||||||
// TEXT foo(SB), $0
|
// TEXT foo(SB), $0
|
||||||
s := p.From.Sym.Attribute.TextAttrString()
|
s := p.From.Sym.Attribute.TextAttrString()
|
||||||
if s != "" {
|
if s != "" {
|
||||||
fmt.Fprintf(&buf, "%s%s", sep, s)
|
fmt.Fprintf(w, "%s%s", sep, s)
|
||||||
sep = ", "
|
sep = ", "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if p.To.Type != TYPE_NONE {
|
if p.To.Type != TYPE_NONE {
|
||||||
fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To))
|
io.WriteString(w, sep)
|
||||||
|
WriteDconv(w, p, &p.To)
|
||||||
}
|
}
|
||||||
if p.RegTo2 != REG_NONE {
|
if p.RegTo2 != REG_NONE {
|
||||||
fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.RegTo2)))
|
fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.RegTo2)))
|
||||||
}
|
}
|
||||||
return buf.String()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctxt *Link) NewProg() *Prog {
|
func (ctxt *Link) NewProg() *Prog {
|
||||||
|
|
@ -194,16 +211,20 @@ func (ctxt *Link) CanReuseProgs() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Dconv(p *Prog, a *Addr) string {
|
func Dconv(p *Prog, a *Addr) string {
|
||||||
var str string
|
buf := new(bytes.Buffer)
|
||||||
|
WriteDconv(buf, p, a)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteDconv(w io.Writer, p *Prog, a *Addr) {
|
||||||
switch a.Type {
|
switch a.Type {
|
||||||
default:
|
default:
|
||||||
str = fmt.Sprintf("type=%d", a.Type)
|
fmt.Fprintf(w, "type=%d", a.Type)
|
||||||
|
|
||||||
case TYPE_NONE:
|
case TYPE_NONE:
|
||||||
str = ""
|
|
||||||
if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil {
|
if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil {
|
||||||
str = fmt.Sprintf("%v(%v)(NONE)", Mconv(a), Rconv(int(a.Reg)))
|
a.WriteNameTo(w)
|
||||||
|
fmt.Fprintf(w, "(%v)(NONE)", Rconv(int(a.Reg)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case TYPE_REG:
|
case TYPE_REG:
|
||||||
|
|
@ -212,71 +233,75 @@ func Dconv(p *Prog, a *Addr) string {
|
||||||
// where the $1 is included in the p->to Addr.
|
// where the $1 is included in the p->to Addr.
|
||||||
// Move into a new field.
|
// Move into a new field.
|
||||||
if a.Offset != 0 && (a.Reg < RBaseARM64 || a.Reg >= RBaseMIPS) {
|
if a.Offset != 0 && (a.Reg < RBaseARM64 || a.Reg >= RBaseMIPS) {
|
||||||
str = fmt.Sprintf("$%d,%v", a.Offset, Rconv(int(a.Reg)))
|
fmt.Fprintf(w, "$%d,%v", a.Offset, Rconv(int(a.Reg)))
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
str = Rconv(int(a.Reg))
|
|
||||||
if a.Name != NAME_NONE || a.Sym != nil {
|
if a.Name != NAME_NONE || a.Sym != nil {
|
||||||
str = fmt.Sprintf("%v(%v)(REG)", Mconv(a), Rconv(int(a.Reg)))
|
a.WriteNameTo(w)
|
||||||
|
fmt.Fprintf(w, "(%v)(REG)", Rconv(int(a.Reg)))
|
||||||
|
} else {
|
||||||
|
io.WriteString(w, Rconv(int(a.Reg)))
|
||||||
}
|
}
|
||||||
if (RBaseARM64+1<<10+1<<9) /* arm64.REG_ELEM */ <= a.Reg &&
|
if (RBaseARM64+1<<10+1<<9) /* arm64.REG_ELEM */ <= a.Reg &&
|
||||||
a.Reg < (RBaseARM64+1<<11) /* arm64.REG_ELEM_END */ {
|
a.Reg < (RBaseARM64+1<<11) /* arm64.REG_ELEM_END */ {
|
||||||
str += fmt.Sprintf("[%d]", a.Index)
|
fmt.Fprintf(w, "[%d]", a.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
case TYPE_BRANCH:
|
case TYPE_BRANCH:
|
||||||
if a.Sym != nil {
|
if a.Sym != nil {
|
||||||
str = fmt.Sprintf("%s(SB)", a.Sym.Name)
|
fmt.Fprintf(w, "%s(SB)", a.Sym.Name)
|
||||||
} else if p != nil && p.Pcond != nil {
|
} else if p != nil && p.Pcond != nil {
|
||||||
str = fmt.Sprint(p.Pcond.Pc)
|
fmt.Fprint(w, p.Pcond.Pc)
|
||||||
} else if a.Val != nil {
|
} else if a.Val != nil {
|
||||||
str = fmt.Sprint(a.Val.(*Prog).Pc)
|
fmt.Fprint(w, a.Val.(*Prog).Pc)
|
||||||
} else {
|
} else {
|
||||||
str = fmt.Sprintf("%d(PC)", a.Offset)
|
fmt.Fprintf(w, "%d(PC)", a.Offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
case TYPE_INDIR:
|
case TYPE_INDIR:
|
||||||
str = fmt.Sprintf("*%s", Mconv(a))
|
io.WriteString(w, "*")
|
||||||
|
a.WriteNameTo(w)
|
||||||
|
|
||||||
case TYPE_MEM:
|
case TYPE_MEM:
|
||||||
str = Mconv(a)
|
a.WriteNameTo(w)
|
||||||
if a.Index != REG_NONE {
|
if a.Index != REG_NONE {
|
||||||
if a.Scale == 0 {
|
if a.Scale == 0 {
|
||||||
// arm64 shifted or extended register offset, scale = 0.
|
// arm64 shifted or extended register offset, scale = 0.
|
||||||
str += fmt.Sprintf("(%v)", Rconv(int(a.Index)))
|
fmt.Fprintf(w, "(%v)", Rconv(int(a.Index)))
|
||||||
} else {
|
} else {
|
||||||
str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
|
fmt.Fprintf(w, "(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case TYPE_CONST:
|
case TYPE_CONST:
|
||||||
|
io.WriteString(w, "$")
|
||||||
|
a.WriteNameTo(w)
|
||||||
if a.Reg != 0 {
|
if a.Reg != 0 {
|
||||||
str = fmt.Sprintf("$%v(%v)", Mconv(a), Rconv(int(a.Reg)))
|
fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
|
||||||
} else {
|
|
||||||
str = fmt.Sprintf("$%v", Mconv(a))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case TYPE_TEXTSIZE:
|
case TYPE_TEXTSIZE:
|
||||||
if a.Val.(int32) == objabi.ArgsSizeUnknown {
|
if a.Val.(int32) == objabi.ArgsSizeUnknown {
|
||||||
str = fmt.Sprintf("$%d", a.Offset)
|
fmt.Fprintf(w, "$%d", a.Offset)
|
||||||
} else {
|
} else {
|
||||||
str = fmt.Sprintf("$%d-%d", a.Offset, a.Val.(int32))
|
fmt.Fprintf(w, "$%d-%d", a.Offset, a.Val.(int32))
|
||||||
}
|
}
|
||||||
|
|
||||||
case TYPE_FCONST:
|
case TYPE_FCONST:
|
||||||
str = fmt.Sprintf("%.17g", a.Val.(float64))
|
str := fmt.Sprintf("%.17g", a.Val.(float64))
|
||||||
// Make sure 1 prints as 1.0
|
// Make sure 1 prints as 1.0
|
||||||
if !strings.ContainsAny(str, ".e") {
|
if !strings.ContainsAny(str, ".e") {
|
||||||
str += ".0"
|
str += ".0"
|
||||||
}
|
}
|
||||||
str = fmt.Sprintf("$(%s)", str)
|
fmt.Fprintf(w, "$(%s)", str)
|
||||||
|
|
||||||
case TYPE_SCONST:
|
case TYPE_SCONST:
|
||||||
str = fmt.Sprintf("$%q", a.Val.(string))
|
fmt.Fprintf(w, "$%q", a.Val.(string))
|
||||||
|
|
||||||
case TYPE_ADDR:
|
case TYPE_ADDR:
|
||||||
str = fmt.Sprintf("$%s", Mconv(a))
|
io.WriteString(w, "$")
|
||||||
|
a.WriteNameTo(w)
|
||||||
|
|
||||||
case TYPE_SHIFT:
|
case TYPE_SHIFT:
|
||||||
v := int(a.Offset)
|
v := int(a.Offset)
|
||||||
|
|
@ -285,49 +310,45 @@ func Dconv(p *Prog, a *Addr) string {
|
||||||
case "arm":
|
case "arm":
|
||||||
op := ops[((v>>5)&3)<<1:]
|
op := ops[((v>>5)&3)<<1:]
|
||||||
if v&(1<<4) != 0 {
|
if v&(1<<4) != 0 {
|
||||||
str = fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
|
fmt.Fprintf(w, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
|
||||||
} else {
|
} else {
|
||||||
str = fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
|
fmt.Fprintf(w, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
|
||||||
}
|
}
|
||||||
if a.Reg != 0 {
|
if a.Reg != 0 {
|
||||||
str += fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
|
fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
|
||||||
}
|
}
|
||||||
case "arm64":
|
case "arm64":
|
||||||
op := ops[((v>>22)&3)<<1:]
|
op := ops[((v>>22)&3)<<1:]
|
||||||
r := (v >> 16) & 31
|
r := (v >> 16) & 31
|
||||||
str = fmt.Sprintf("%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63)
|
fmt.Fprintf(w, "%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63)
|
||||||
default:
|
default:
|
||||||
panic("TYPE_SHIFT is not supported on " + objabi.GOARCH)
|
panic("TYPE_SHIFT is not supported on " + objabi.GOARCH)
|
||||||
}
|
}
|
||||||
|
|
||||||
case TYPE_REGREG:
|
case TYPE_REGREG:
|
||||||
str = fmt.Sprintf("(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
|
fmt.Fprintf(w, "(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
|
||||||
|
|
||||||
case TYPE_REGREG2:
|
case TYPE_REGREG2:
|
||||||
str = fmt.Sprintf("%v, %v", Rconv(int(a.Offset)), Rconv(int(a.Reg)))
|
fmt.Fprintf(w, "%v, %v", Rconv(int(a.Offset)), Rconv(int(a.Reg)))
|
||||||
|
|
||||||
case TYPE_REGLIST:
|
case TYPE_REGLIST:
|
||||||
str = RLconv(a.Offset)
|
io.WriteString(w, RLconv(a.Offset))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return str
|
func (a *Addr) WriteNameTo(w io.Writer) {
|
||||||
}
|
|
||||||
|
|
||||||
func Mconv(a *Addr) string {
|
|
||||||
var str string
|
|
||||||
|
|
||||||
switch a.Name {
|
switch a.Name {
|
||||||
default:
|
default:
|
||||||
str = fmt.Sprintf("name=%d", a.Name)
|
fmt.Fprintf(w, "name=%d", a.Name)
|
||||||
|
|
||||||
case NAME_NONE:
|
case NAME_NONE:
|
||||||
switch {
|
switch {
|
||||||
case a.Reg == REG_NONE:
|
case a.Reg == REG_NONE:
|
||||||
str = fmt.Sprint(a.Offset)
|
fmt.Fprint(w, a.Offset)
|
||||||
case a.Offset == 0:
|
case a.Offset == 0:
|
||||||
str = fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
|
fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
|
||||||
case a.Offset != 0:
|
case a.Offset != 0:
|
||||||
str = fmt.Sprintf("%d(%v)", a.Offset, Rconv(int(a.Reg)))
|
fmt.Fprintf(w, "%d(%v)", a.Offset, Rconv(int(a.Reg)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type.
|
// Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type.
|
||||||
|
|
@ -337,9 +358,9 @@ func Mconv(a *Addr) string {
|
||||||
reg = Rconv(int(a.Reg))
|
reg = Rconv(int(a.Reg))
|
||||||
}
|
}
|
||||||
if a.Sym != nil {
|
if a.Sym != nil {
|
||||||
str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
|
fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
|
||||||
} else {
|
} else {
|
||||||
str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg)
|
fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
case NAME_GOTREF:
|
case NAME_GOTREF:
|
||||||
|
|
@ -348,9 +369,9 @@ func Mconv(a *Addr) string {
|
||||||
reg = Rconv(int(a.Reg))
|
reg = Rconv(int(a.Reg))
|
||||||
}
|
}
|
||||||
if a.Sym != nil {
|
if a.Sym != nil {
|
||||||
str = fmt.Sprintf("%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg)
|
fmt.Fprintf(w, "%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg)
|
||||||
} else {
|
} else {
|
||||||
str = fmt.Sprintf("%s@GOT(%s)", offConv(a.Offset), reg)
|
fmt.Fprintf(w, "%s@GOT(%s)", offConv(a.Offset), reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
case NAME_STATIC:
|
case NAME_STATIC:
|
||||||
|
|
@ -359,9 +380,9 @@ func Mconv(a *Addr) string {
|
||||||
reg = Rconv(int(a.Reg))
|
reg = Rconv(int(a.Reg))
|
||||||
}
|
}
|
||||||
if a.Sym != nil {
|
if a.Sym != nil {
|
||||||
str = fmt.Sprintf("%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
|
fmt.Fprintf(w, "%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
|
||||||
} else {
|
} else {
|
||||||
str = fmt.Sprintf("<>%s(%s)", offConv(a.Offset), reg)
|
fmt.Fprintf(w, "<>%s(%s)", offConv(a.Offset), reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
case NAME_AUTO:
|
case NAME_AUTO:
|
||||||
|
|
@ -370,9 +391,9 @@ func Mconv(a *Addr) string {
|
||||||
reg = Rconv(int(a.Reg))
|
reg = Rconv(int(a.Reg))
|
||||||
}
|
}
|
||||||
if a.Sym != nil {
|
if a.Sym != nil {
|
||||||
str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
|
fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
|
||||||
} else {
|
} else {
|
||||||
str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg)
|
fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
case NAME_PARAM:
|
case NAME_PARAM:
|
||||||
|
|
@ -381,9 +402,9 @@ func Mconv(a *Addr) string {
|
||||||
reg = Rconv(int(a.Reg))
|
reg = Rconv(int(a.Reg))
|
||||||
}
|
}
|
||||||
if a.Sym != nil {
|
if a.Sym != nil {
|
||||||
str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
|
fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
|
||||||
} else {
|
} else {
|
||||||
str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg)
|
fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
|
||||||
}
|
}
|
||||||
case NAME_TOCREF:
|
case NAME_TOCREF:
|
||||||
reg := "SB"
|
reg := "SB"
|
||||||
|
|
@ -391,13 +412,11 @@ func Mconv(a *Addr) string {
|
||||||
reg = Rconv(int(a.Reg))
|
reg = Rconv(int(a.Reg))
|
||||||
}
|
}
|
||||||
if a.Sym != nil {
|
if a.Sym != nil {
|
||||||
str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
|
fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
|
||||||
} else {
|
} else {
|
||||||
str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg)
|
fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return str
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func offConv(off int64) string {
|
func offConv(off int64) string {
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@
|
||||||
package src
|
package src
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Pos encodes a source position consisting of a (line, column) number pair
|
// A Pos encodes a source position consisting of a (line, column) number pair
|
||||||
|
|
@ -129,13 +130,22 @@ func (p Pos) String() string {
|
||||||
// shown as well, as in "filename:line[origfile:origline:origcolumn] if
|
// shown as well, as in "filename:line[origfile:origline:origcolumn] if
|
||||||
// showOrig is set.
|
// showOrig is set.
|
||||||
func (p Pos) Format(showCol, showOrig bool) string {
|
func (p Pos) Format(showCol, showOrig bool) string {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
p.WriteTo(buf, showCol, showOrig)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo a position to w, formatted as Format does.
|
||||||
|
func (p Pos) WriteTo(w io.Writer, showCol, showOrig bool) {
|
||||||
if !p.IsKnown() {
|
if !p.IsKnown() {
|
||||||
return "<unknown line number>"
|
io.WriteString(w, "<unknown line number>")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if b := p.base; b == b.Pos().base {
|
if b := p.base; b == b.Pos().base {
|
||||||
// base is file base (incl. nil)
|
// base is file base (incl. nil)
|
||||||
return format(p.Filename(), p.Line(), p.Col(), showCol)
|
format(w, p.Filename(), p.Line(), p.Col(), showCol)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// base is relative
|
// base is relative
|
||||||
|
|
@ -146,22 +156,32 @@ func (p Pos) Format(showCol, showOrig bool) string {
|
||||||
// that's provided via a line directive).
|
// that's provided via a line directive).
|
||||||
// TODO(gri) This may not be true if we have an inlining base.
|
// TODO(gri) This may not be true if we have an inlining base.
|
||||||
// We may want to differentiate at some point.
|
// We may want to differentiate at some point.
|
||||||
s := format(p.RelFilename(), p.RelLine(), p.RelCol(), showCol)
|
format(w, p.RelFilename(), p.RelLine(), p.RelCol(), showCol)
|
||||||
if showOrig {
|
if showOrig {
|
||||||
s += "[" + format(p.Filename(), p.Line(), p.Col(), showCol) + "]"
|
io.WriteString(w, "[")
|
||||||
|
format(w, p.Filename(), p.Line(), p.Col(), showCol)
|
||||||
|
io.WriteString(w, "]")
|
||||||
}
|
}
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// format formats a (filename, line, col) tuple as "filename:line" (showCol
|
// format formats a (filename, line, col) tuple as "filename:line" (showCol
|
||||||
// is false or col == 0) or "filename:line:column" (showCol is true and col != 0).
|
// is false or col == 0) or "filename:line:column" (showCol is true and col != 0).
|
||||||
func format(filename string, line, col uint, showCol bool) string {
|
func format(w io.Writer, filename string, line, col uint, showCol bool) {
|
||||||
s := filename + ":" + strconv.FormatUint(uint64(line), 10)
|
io.WriteString(w, filename)
|
||||||
|
io.WriteString(w, ":")
|
||||||
|
fmt.Fprint(w, line)
|
||||||
// col == 0 and col == colMax are interpreted as unknown column values
|
// col == 0 and col == colMax are interpreted as unknown column values
|
||||||
if showCol && 0 < col && col < colMax {
|
if showCol && 0 < col && col < colMax {
|
||||||
s += ":" + strconv.FormatUint(uint64(col), 10)
|
io.WriteString(w, ":")
|
||||||
|
fmt.Fprint(w, col)
|
||||||
}
|
}
|
||||||
return s
|
}
|
||||||
|
|
||||||
|
// formatstr wraps format to return a string.
|
||||||
|
func formatstr(filename string, line, col uint, showCol bool) string {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
format(buf, filename, line, col, showCol)
|
||||||
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ func TestLico(t *testing.T) {
|
||||||
{makeLico(lineMax+1, colMax+1), fmt.Sprintf(":%d", lineMax), lineMax, 0},
|
{makeLico(lineMax+1, colMax+1), fmt.Sprintf(":%d", lineMax), lineMax, 0},
|
||||||
} {
|
} {
|
||||||
x := test.x
|
x := test.x
|
||||||
if got := format("", x.Line(), x.Col(), true); got != test.string {
|
if got := formatstr("", x.Line(), x.Col(), true); got != test.string {
|
||||||
t.Errorf("%s: got %q", test.string, got)
|
t.Errorf("%s: got %q", test.string, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -179,7 +179,7 @@ func TestIsStmt(t *testing.T) {
|
||||||
{makeLico(lineMax+1, colMax+1).withNotStmt(), fmt.Sprintf(":%d", lineMax) + not, lineMax, 0},
|
{makeLico(lineMax+1, colMax+1).withNotStmt(), fmt.Sprintf(":%d", lineMax) + not, lineMax, 0},
|
||||||
} {
|
} {
|
||||||
x := test.x
|
x := test.x
|
||||||
if got := format("", x.Line(), x.Col(), true) + fmt.Sprintf(":%d", x.IsStmt()); got != test.string {
|
if got := formatstr("", x.Line(), x.Col(), true) + fmt.Sprintf(":%d", x.IsStmt()); got != test.string {
|
||||||
t.Errorf("%s: got %q", test.string, got)
|
t.Errorf("%s: got %q", test.string, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -219,7 +219,7 @@ func TestLogue(t *testing.T) {
|
||||||
{makeLico(lineMax, 1).withXlogue(PosEpilogueBegin), fmt.Sprintf(":%d:1", lineMax) + defs + epi, lineMax, 1},
|
{makeLico(lineMax, 1).withXlogue(PosEpilogueBegin), fmt.Sprintf(":%d:1", lineMax) + defs + epi, lineMax, 1},
|
||||||
} {
|
} {
|
||||||
x := test.x
|
x := test.x
|
||||||
if got := format("", x.Line(), x.Col(), true) + fmt.Sprintf(":%d:%d", x.IsStmt(), x.Xlogue()); got != test.string {
|
if got := formatstr("", x.Line(), x.Col(), true) + fmt.Sprintf(":%d:%d", x.IsStmt(), x.Xlogue()); got != test.string {
|
||||||
t.Errorf("%d: %s: got %q", i, test.string, got)
|
t.Errorf("%d: %s: got %q", i, test.string, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue