2015-08-10 12:15:52 -07:00
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
import (
"bytes"
2016-12-07 16:02:42 -08:00
"cmd/internal/src"
2015-08-10 12:15:52 -07:00
"fmt"
"html"
"io"
"os"
2018-10-16 13:03:35 +03:00
"os/exec"
2018-07-24 10:39:00 +03:00
"path/filepath"
2018-07-31 18:13:05 +03:00
"strconv"
[dev.debug] cmd/compile: better DWARF with optimizations on
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.
After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.
There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:
type myint int
var a int
b := myint(int)
and
b := (*uint64)(unsafe.Pointer(a))
don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.
Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.
A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.
TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.
go build -toolexec 'toolstash -cmp' -a std succeeds.
With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after: 06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean /tmp/220352263 /tmp/621364410
completed 15 of 15, estimated time remaining 0s (eta 3:52PM)
name old time/op new time/op delta
Template 199ms ± 3% 198ms ± 2% ~ (p=0.400 n=15+14)
Unicode 96.6ms ± 5% 96.4ms ± 5% ~ (p=0.838 n=15+15)
GoTypes 653ms ± 2% 647ms ± 2% ~ (p=0.102 n=15+14)
Flate 133ms ± 6% 129ms ± 3% -2.62% (p=0.041 n=15+15)
GoParser 164ms ± 5% 159ms ± 3% -3.05% (p=0.000 n=15+15)
Reflect 428ms ± 4% 422ms ± 3% ~ (p=0.156 n=15+13)
Tar 123ms ±10% 124ms ± 8% ~ (p=0.461 n=15+15)
XML 228ms ± 3% 224ms ± 3% -1.57% (p=0.045 n=15+15)
[Geo mean] 206ms 377ms +82.86%
name old user-time/op new user-time/op delta
Template 292ms ±10% 301ms ±12% ~ (p=0.189 n=15+15)
Unicode 166ms ±37% 158ms ±14% ~ (p=0.418 n=15+14)
GoTypes 962ms ± 6% 963ms ± 7% ~ (p=0.976 n=15+15)
Flate 207ms ±19% 200ms ±14% ~ (p=0.345 n=14+15)
GoParser 246ms ±22% 240ms ±15% ~ (p=0.587 n=15+15)
Reflect 611ms ±13% 587ms ±14% ~ (p=0.085 n=15+13)
Tar 211ms ±12% 217ms ±14% ~ (p=0.355 n=14+15)
XML 335ms ±15% 320ms ±18% ~ (p=0.169 n=15+15)
[Geo mean] 317ms 583ms +83.72%
name old alloc/op new alloc/op delta
Template 40.2MB ± 0% 40.2MB ± 0% -0.15% (p=0.000 n=14+15)
Unicode 29.2MB ± 0% 29.3MB ± 0% ~ (p=0.624 n=15+15)
GoTypes 114MB ± 0% 114MB ± 0% -0.15% (p=0.000 n=15+14)
Flate 25.7MB ± 0% 25.6MB ± 0% -0.18% (p=0.000 n=13+15)
GoParser 32.2MB ± 0% 32.2MB ± 0% -0.14% (p=0.003 n=15+15)
Reflect 77.8MB ± 0% 77.9MB ± 0% ~ (p=0.061 n=15+15)
Tar 27.1MB ± 0% 27.0MB ± 0% -0.11% (p=0.029 n=15+15)
XML 42.7MB ± 0% 42.5MB ± 0% -0.29% (p=0.000 n=15+15)
[Geo mean] 42.1MB 75.0MB +78.05%
name old allocs/op new allocs/op delta
Template 402k ± 1% 398k ± 0% -0.91% (p=0.000 n=15+15)
Unicode 344k ± 1% 344k ± 0% ~ (p=0.715 n=15+14)
GoTypes 1.18M ± 0% 1.17M ± 0% -0.91% (p=0.000 n=15+14)
Flate 243k ± 0% 240k ± 1% -1.05% (p=0.000 n=13+15)
GoParser 327k ± 1% 324k ± 1% -0.96% (p=0.000 n=15+15)
Reflect 984k ± 1% 982k ± 0% ~ (p=0.050 n=15+15)
Tar 261k ± 1% 259k ± 1% -0.77% (p=0.000 n=15+15)
XML 411k ± 0% 404k ± 1% -1.55% (p=0.000 n=15+15)
[Geo mean] 439k 755k +72.01%
name old text-bytes new text-bytes delta
HelloSize 694kB ± 0% 694kB ± 0% -0.00% (p=0.000 n=15+15)
name old data-bytes new data-bytes delta
HelloSize 5.55kB ± 0% 5.55kB ± 0% ~ (all equal)
name old bss-bytes new bss-bytes delta
HelloSize 133kB ± 0% 133kB ± 0% ~ (all equal)
name old exe-bytes new exe-bytes delta
HelloSize 1.04MB ± 0% 1.04MB ± 0% ~ (all equal)
Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2017-07-21 18:30:19 -04:00
"strings"
2015-08-10 12:15:52 -07:00
)
type HTMLWriter struct {
2020-03-29 13:17:46 -06:00
w io . WriteCloser
2020-04-05 12:44:02 -06:00
Func * Func
2020-03-29 13:17:46 -06:00
path string
dot * dotWriter
prevHash [ ] byte
pendingPhases [ ] string
pendingTitles [ ] string
2015-08-10 12:15:52 -07:00
}
2020-04-05 12:44:02 -06:00
func NewHTMLWriter ( path string , f * Func , cfgMask string ) * HTMLWriter {
2015-08-10 12:15:52 -07:00
out , err := os . OpenFile ( path , os . O_WRONLY | os . O_CREATE | os . O_TRUNC , 0644 )
if err != nil {
2020-04-05 12:44:02 -06:00
f . Fatalf ( "%v" , err )
2015-08-10 12:15:52 -07:00
}
2018-07-24 10:39:00 +03:00
pwd , err := os . Getwd ( )
if err != nil {
2020-04-05 12:44:02 -06:00
f . Fatalf ( "%v" , err )
2018-07-24 10:39:00 +03:00
}
2020-04-05 12:44:02 -06:00
html := HTMLWriter {
w : out ,
Func : f ,
path : filepath . Join ( pwd , path ) ,
dot : newDotWriter ( cfgMask ) ,
}
html . start ( )
2015-08-10 12:15:52 -07:00
return & html
}
2020-04-05 12:44:02 -06:00
// Fatalf reports an error and exits.
func ( w * HTMLWriter ) Fatalf ( msg string , args ... interface { } ) {
fe := w . Func . Frontend ( )
fe . Fatalf ( src . NoXPos , msg , args ... )
}
// Logf calls the (w *HTMLWriter).Func's Logf method passing along a msg and args.
func ( w * HTMLWriter ) Logf ( msg string , args ... interface { } ) {
w . Func . Logf ( msg , args ... )
}
func ( w * HTMLWriter ) start ( ) {
2015-08-10 12:15:52 -07:00
if w == nil {
return
}
w . WriteString ( "<html>" )
w . WriteString ( ` < head >
2016-04-30 11:13:29 +02:00
< meta http - equiv = "Content-Type" content = "text/html;charset=UTF-8" >
2015-08-10 12:15:52 -07:00
< style >
2018-06-08 00:25:12 +03:00
body {
font - size : 14 px ;
font - family : Arial , sans - serif ;
}
2018-10-25 13:52:50 +03:00
h1 {
font - size : 18 px ;
display : inline - block ;
margin : 0 1 em .5 em 0 ;
}
2015-08-10 12:15:52 -07:00
# helplink {
2018-10-25 13:52:50 +03:00
display : inline - block ;
2015-08-10 12:15:52 -07:00
}
# help {
display : none ;
}
2015-09-04 17:33:56 -07:00
. stats {
2018-06-14 18:20:03 +03:00
font - size : 60 % ;
2015-09-04 17:33:56 -07:00
}
2015-08-10 12:15:52 -07:00
table {
border : 1 px solid black ;
table - layout : fixed ;
width : 300 px ;
}
th , td {
border : 1 px solid black ;
overflow : hidden ;
width : 400 px ;
vertical - align : top ;
padding : 5 px ;
}
2018-06-08 00:25:12 +03:00
td > h2 {
cursor : pointer ;
font - size : 120 % ;
2020-03-29 13:17:46 -06:00
margin : 5 px 0 px 5 px 0 px ;
2018-06-08 00:25:12 +03:00
}
td . collapsed {
font - size : 12 px ;
width : 12 px ;
2020-02-20 09:07:48 -07:00
border : 1 px solid white ;
2020-03-29 13:17:46 -06:00
padding : 2 px ;
2018-06-08 00:25:12 +03:00
cursor : pointer ;
background : # fafafa ;
}
2020-03-29 13:17:46 -06:00
td . collapsed div {
/* TODO: Flip the direction of the phase's title 90 degrees on a collapsed column. */
writing - mode : vertical - lr ;
white - space : pre ;
2018-06-08 00:25:12 +03:00
}
2018-07-31 18:13:05 +03:00
code , pre , . lines , . ast {
2018-06-14 18:20:03 +03:00
font - family : Menlo , monospace ;
font - size : 12 px ;
}
2018-10-17 10:40:44 +03:00
pre {
- moz - tab - size : 4 ;
- o - tab - size : 4 ;
tab - size : 4 ;
}
2018-07-31 18:13:05 +03:00
. allow - x - scroll {
overflow - x : scroll ;
}
2018-06-14 18:20:03 +03:00
. lines {
float : left ;
overflow : hidden ;
text - align : right ;
}
. lines div {
padding - right : 10 px ;
color : gray ;
}
div . line - number {
font - size : 12 px ;
}
2018-07-31 18:13:05 +03:00
. ast {
white - space : nowrap ;
}
cmd/compile: add line numbers to values & blocks in ssa.html
In order to improve the line numbering for debuggers,
it's necessary to trace lines through compilation.
This makes it (much) easier to follow.
The format of the last column of the ssa.html output was
also changed to reduce the spamminess of the file name,
which is usually the same and makes it far harder to read
instructions and line numbers, and to make it wider and also
able to break words when wrapping (long path names still
can push off the end otherwise; side-to-side scrolling was
tried but was more annoying than the occasional wrapped
line).
Sample output now, where [...] is elision for sake of making
the CL character-counter happy -- and the (##) line numbers
are rendered in italics and a smaller font (11 point) under
control of a CSS class "line-number".
genssa
# /Users/drchase/[...]/ssa/testdata/hist.go
00000 (35) TEXT "".main(SB)
00001 (35) FUNCDATA $0, gclocals·7be4bb[...]1e8b(SB)
00002 (35) FUNCDATA $1, gclocals·9ab98a[...]4568(SB)
v920 00003 (36) LEAQ ""..autotmp_31-640(SP), DI
v858 00004 (36) XORPS X0, X0
v6 00005 (36) LEAQ -48(DI), DI
v6 00006 (36) DUFFZERO $277
v576 00007 (36) LEAQ ""..autotmp_31-640(SP), AX
v10 00008 (36) TESTB AX, (AX)
b1 00009 (36) JMP 10
and from an earlier phase:
b18: ← b17
v242 (47) = Copy <mem> v238
v243 (47) = VarKill <mem> {.autotmp_16} v242
v244 (48) = Addr <**bufio.Scanner> {scanner} v2
v245 (48) = Load <*bufio.Scanner> v244 v243
[...]
v279 (49) = Store <mem> {int64} v277 v276 v278
v280 (49) = Addr <*error> {.autotmp_18} v2
v281 (49) = Load <error> v280 v279
v282 (49) = Addr <*error> {err} v2
v283 (49) = VarDef <mem> {err} v279
v284 (49) = Store <mem> {error} v282 v281 v283
v285 (47) = VarKill <mem> {.autotmp_18} v284
v286 (47) = VarKill <mem> {.autotmp_17} v285
v287 (50) = Addr <*error> {err} v2
v288 (50) = Load <error> v287 v286
v289 (50) = NeqInter <bool> v288 v51
If v289 → b21 b22 (line 50)
Change-Id: I3f46310918f965761f59e6f03ea53067237c28a8
Reviewed-on: https://go-review.googlesource.com/69591
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2017-10-10 14:44:15 -04:00
td . ssa - prog {
width : 600 px ;
word - wrap : break - word ;
}
2015-08-10 12:15:52 -07:00
li {
list - style - type : none ;
}
li . ssa - long - value {
text - indent : - 2 em ; /* indent wrapped lines */
}
li . ssa - value - list {
display : inline ;
}
li . ssa - start - block {
padding : 0 ;
margin : 0 ;
}
li . ssa - end - block {
padding : 0 ;
margin : 0 ;
}
ul . ssa - print - func {
padding - left : 0 ;
}
2018-10-25 13:33:09 +03:00
li . ssa - start - block button {
padding : 0 1 em ;
margin : 0 ;
border : none ;
display : inline ;
font - size : 14 px ;
float : right ;
}
button : hover {
background - color : # eee ;
cursor : pointer ;
}
2015-08-10 12:15:52 -07:00
dl . ssa - gen {
padding - left : 0 ;
}
dt . ssa - prog - src {
padding : 0 ;
margin : 0 ;
float : left ;
width : 4 em ;
}
dd . ssa - prog {
padding : 0 ;
margin - right : 0 ;
margin - left : 4 em ;
}
. dead - value {
color : gray ;
}
. dead - block {
opacity : 0.5 ;
}
. depcycle {
font - style : italic ;
}
cmd/compile: add line numbers to values & blocks in ssa.html
In order to improve the line numbering for debuggers,
it's necessary to trace lines through compilation.
This makes it (much) easier to follow.
The format of the last column of the ssa.html output was
also changed to reduce the spamminess of the file name,
which is usually the same and makes it far harder to read
instructions and line numbers, and to make it wider and also
able to break words when wrapping (long path names still
can push off the end otherwise; side-to-side scrolling was
tried but was more annoying than the occasional wrapped
line).
Sample output now, where [...] is elision for sake of making
the CL character-counter happy -- and the (##) line numbers
are rendered in italics and a smaller font (11 point) under
control of a CSS class "line-number".
genssa
# /Users/drchase/[...]/ssa/testdata/hist.go
00000 (35) TEXT "".main(SB)
00001 (35) FUNCDATA $0, gclocals·7be4bb[...]1e8b(SB)
00002 (35) FUNCDATA $1, gclocals·9ab98a[...]4568(SB)
v920 00003 (36) LEAQ ""..autotmp_31-640(SP), DI
v858 00004 (36) XORPS X0, X0
v6 00005 (36) LEAQ -48(DI), DI
v6 00006 (36) DUFFZERO $277
v576 00007 (36) LEAQ ""..autotmp_31-640(SP), AX
v10 00008 (36) TESTB AX, (AX)
b1 00009 (36) JMP 10
and from an earlier phase:
b18: ← b17
v242 (47) = Copy <mem> v238
v243 (47) = VarKill <mem> {.autotmp_16} v242
v244 (48) = Addr <**bufio.Scanner> {scanner} v2
v245 (48) = Load <*bufio.Scanner> v244 v243
[...]
v279 (49) = Store <mem> {int64} v277 v276 v278
v280 (49) = Addr <*error> {.autotmp_18} v2
v281 (49) = Load <error> v280 v279
v282 (49) = Addr <*error> {err} v2
v283 (49) = VarDef <mem> {err} v279
v284 (49) = Store <mem> {error} v282 v281 v283
v285 (47) = VarKill <mem> {.autotmp_18} v284
v286 (47) = VarKill <mem> {.autotmp_17} v285
v287 (50) = Addr <*error> {err} v2
v288 (50) = Load <error> v287 v286
v289 (50) = NeqInter <bool> v288 v51
If v289 → b21 b22 (line 50)
Change-Id: I3f46310918f965761f59e6f03ea53067237c28a8
Reviewed-on: https://go-review.googlesource.com/69591
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2017-10-10 14:44:15 -04:00
. line - number {
font - size : 11 px ;
}
2018-06-14 18:20:03 +03:00
. no - line - number {
font - size : 11 px ;
color : gray ;
}
2018-10-16 13:03:35 +03:00
. zoom {
position : absolute ;
float : left ;
white - space : nowrap ;
background - color : # eee ;
}
. zoom a : link , . zoom a : visited {
text - decoration : none ;
color : blue ;
font - size : 16 px ;
padding : 4 px 2 px ;
}
svg {
cursor : default ;
outline : 1 px solid # eee ;
2020-03-07 13:36:36 -08:00
width : 100 % ;
2018-10-16 13:03:35 +03:00
}
2020-02-20 09:07:48 -07:00
body . darkmode {
background - color : rgb ( 21 , 21 , 21 ) ;
color : rgb ( 230 , 255 , 255 ) ;
opacity : 100 % ;
}
td . darkmode {
background - color : rgb ( 21 , 21 , 21 ) ;
border : 1 px solid gray ;
}
body . darkmode table , th {
border : 1 px solid gray ;
}
2020-03-10 21:26:42 -06:00
body . darkmode text {
fill : white ;
}
body . darkmode svg polygon : first - child {
fill : rgb ( 21 , 21 , 21 ) ;
}
2020-02-20 09:07:48 -07:00
. highlight - aquamarine { background - color : aquamarine ; color : black ; }
. highlight - coral { background - color : coral ; color : black ; }
. highlight - lightpink { background - color : lightpink ; color : black ; }
. highlight - lightsteelblue { background - color : lightsteelblue ; color : black ; }
. highlight - palegreen { background - color : palegreen ; color : black ; }
. highlight - skyblue { background - color : skyblue ; color : black ; }
. highlight - lightgray { background - color : lightgray ; color : black ; }
. highlight - yellow { background - color : yellow ; color : black ; }
. highlight - lime { background - color : lime ; color : black ; }
. highlight - khaki { background - color : khaki ; color : black ; }
. highlight - aqua { background - color : aqua ; color : black ; }
. highlight - salmon { background - color : salmon ; color : black ; }
/* Ensure all dead values/blocks continue to have gray font color in dark mode with highlights */
. dead - value span . highlight - aquamarine ,
. dead - block . highlight - aquamarine ,
. dead - value span . highlight - coral ,
. dead - block . highlight - coral ,
. dead - value span . highlight - lightpink ,
. dead - block . highlight - lightpink ,
. dead - value span . highlight - lightsteelblue ,
. dead - block . highlight - lightsteelblue ,
. dead - value span . highlight - palegreen ,
. dead - block . highlight - palegreen ,
. dead - value span . highlight - skyblue ,
. dead - block . highlight - skyblue ,
. dead - value span . highlight - lightgray ,
. dead - block . highlight - lightgray ,
. dead - value span . highlight - yellow ,
. dead - block . highlight - yellow ,
. dead - value span . highlight - lime ,
. dead - block . highlight - lime ,
. dead - value span . highlight - khaki ,
. dead - block . highlight - khaki ,
. dead - value span . highlight - aqua ,
. dead - block . highlight - aqua ,
. dead - value span . highlight - salmon ,
. dead - block . highlight - salmon {
color : gray ;
}
2015-08-10 12:15:52 -07:00
2020-03-10 21:26:42 -06:00
. outline - blue { outline : # 2893 ff solid 2 px ; }
2015-08-10 12:15:52 -07:00
. outline - red { outline : red solid 2 px ; }
. outline - blueviolet { outline : blueviolet solid 2 px ; }
. outline - darkolivegreen { outline : darkolivegreen solid 2 px ; }
. outline - fuchsia { outline : fuchsia solid 2 px ; }
. outline - sienna { outline : sienna solid 2 px ; }
. outline - gold { outline : gold solid 2 px ; }
2018-06-16 16:02:48 +03:00
. outline - orangered { outline : orangered solid 2 px ; }
. outline - teal { outline : teal solid 2 px ; }
. outline - maroon { outline : maroon solid 2 px ; }
. outline - black { outline : black solid 2 px ; }
2015-08-10 12:15:52 -07:00
2020-03-10 21:26:42 -06:00
ellipse . outline - blue { stroke - width : 2 px ; stroke : # 2893 ff ; }
2018-10-16 13:03:35 +03:00
ellipse . outline - red { stroke - width : 2 px ; stroke : red ; }
ellipse . outline - blueviolet { stroke - width : 2 px ; stroke : blueviolet ; }
ellipse . outline - darkolivegreen { stroke - width : 2 px ; stroke : darkolivegreen ; }
ellipse . outline - fuchsia { stroke - width : 2 px ; stroke : fuchsia ; }
ellipse . outline - sienna { stroke - width : 2 px ; stroke : sienna ; }
ellipse . outline - gold { stroke - width : 2 px ; stroke : gold ; }
ellipse . outline - orangered { stroke - width : 2 px ; stroke : orangered ; }
ellipse . outline - teal { stroke - width : 2 px ; stroke : teal ; }
ellipse . outline - maroon { stroke - width : 2 px ; stroke : maroon ; }
ellipse . outline - black { stroke - width : 2 px ; stroke : black ; }
2020-02-20 09:07:48 -07:00
/* Capture alternative for outline-black and ellipse.outline-black when in dark mode */
body . darkmode . outline - black { outline : gray solid 2 px ; }
body . darkmode ellipse . outline - black { outline : gray solid 2 px ; }
2015-08-10 12:15:52 -07:00
< / style >
< script type = "text/javascript" >
// ordered list of all available highlight colors
var highlights = [
"highlight-aquamarine" ,
"highlight-coral" ,
"highlight-lightpink" ,
"highlight-lightsteelblue" ,
"highlight-palegreen" ,
2018-06-16 16:02:48 +03:00
"highlight-skyblue" ,
2017-05-13 08:29:47 -07:00
"highlight-lightgray" ,
2018-06-16 16:02:48 +03:00
"highlight-yellow" ,
"highlight-lime" ,
"highlight-khaki" ,
"highlight-aqua" ,
"highlight-salmon"
2015-08-10 12:15:52 -07:00
] ;
// state: which value is highlighted this color?
var highlighted = { } ;
for ( var i = 0 ; i < highlights . length ; i ++ ) {
highlighted [ highlights [ i ] ] = "" ;
}
// ordered list of all available outline colors
var outlines = [
"outline-blue" ,
"outline-red" ,
"outline-blueviolet" ,
"outline-darkolivegreen" ,
"outline-fuchsia" ,
"outline-sienna" ,
2018-06-16 16:02:48 +03:00
"outline-gold" ,
"outline-orangered" ,
"outline-teal" ,
"outline-maroon" ,
"outline-black"
2015-08-10 12:15:52 -07:00
] ;
// state: which value is outlined this color?
var outlined = { } ;
for ( var i = 0 ; i < outlines . length ; i ++ ) {
outlined [ outlines [ i ] ] = "" ;
}
window . onload = function ( ) {
2020-02-20 09:07:48 -07:00
if ( window . matchMedia && window . matchMedia ( "(prefers-color-scheme: dark)" ) . matches ) {
toggleDarkMode ( ) ;
document . getElementById ( "dark-mode-button" ) . checked = true ;
}
2015-08-10 12:15:52 -07:00
var ssaElemClicked = function ( elem , event , selections , selected ) {
2018-06-14 18:20:03 +03:00
event . stopPropagation ( ) ;
2015-08-10 12:15:52 -07:00
// TODO: pushState with updated state and read it on page load,
// so that state can survive across reloads
// find all values with the same name
var c = elem . classList . item ( 0 ) ;
var x = document . getElementsByClassName ( c ) ;
// if selected, remove selections from all of them
// otherwise, attempt to add
var remove = "" ;
for ( var i = 0 ; i < selections . length ; i ++ ) {
var color = selections [ i ] ;
if ( selected [ color ] == c ) {
remove = color ;
break ;
}
}
if ( remove != "" ) {
for ( var i = 0 ; i < x . length ; i ++ ) {
x [ i ] . classList . remove ( remove ) ;
}
selected [ remove ] = "" ;
return ;
}
// we're adding a selection
// find first available color
var avail = "" ;
for ( var i = 0 ; i < selections . length ; i ++ ) {
var color = selections [ i ] ;
if ( selected [ color ] == "" ) {
avail = color ;
break ;
}
}
if ( avail == "" ) {
alert ( "out of selection colors; go add more" ) ;
return ;
}
// set that as the selection
for ( var i = 0 ; i < x . length ; i ++ ) {
x [ i ] . classList . add ( avail ) ;
}
selected [ avail ] = c ;
} ;
var ssaValueClicked = function ( event ) {
ssaElemClicked ( this , event , highlights , highlighted ) ;
2018-06-14 18:20:03 +03:00
} ;
2015-08-10 12:15:52 -07:00
var ssaBlockClicked = function ( event ) {
ssaElemClicked ( this , event , outlines , outlined ) ;
2018-06-14 18:20:03 +03:00
} ;
2015-08-10 12:15:52 -07:00
var ssavalues = document . getElementsByClassName ( "ssa-value" ) ;
for ( var i = 0 ; i < ssavalues . length ; i ++ ) {
ssavalues [ i ] . addEventListener ( ' click ' , ssaValueClicked ) ;
}
var ssalongvalues = document . getElementsByClassName ( "ssa-long-value" ) ;
for ( var i = 0 ; i < ssalongvalues . length ; i ++ ) {
// don't attach listeners to li nodes, just the spans they contain
if ( ssalongvalues [ i ] . nodeName == "SPAN" ) {
ssalongvalues [ i ] . addEventListener ( ' click ' , ssaValueClicked ) ;
}
}
var ssablocks = document . getElementsByClassName ( "ssa-block" ) ;
for ( var i = 0 ; i < ssablocks . length ; i ++ ) {
ssablocks [ i ] . addEventListener ( ' click ' , ssaBlockClicked ) ;
}
2018-06-14 18:20:03 +03:00
var lines = document . getElementsByClassName ( "line-number" ) ;
for ( var i = 0 ; i < lines . length ; i ++ ) {
lines [ i ] . addEventListener ( ' click ' , ssaValueClicked ) ;
}
// Contains phase names which are expanded by default. Other columns are collapsed.
var expandedDefault = [
2018-06-08 00:25:12 +03:00
"start" ,
"deadcode" ,
"opt" ,
"lower" ,
2020-03-29 13:17:46 -06:00
"late-deadcode" ,
2018-06-08 00:25:12 +03:00
"regalloc" ,
"genssa" ,
2018-06-14 18:20:03 +03:00
] ;
2018-06-08 00:25:12 +03:00
function toggler ( phase ) {
return function ( ) {
toggle_cell ( phase + ' - col ' ) ;
toggle_cell ( phase + ' - exp ' ) ;
} ;
}
2018-06-14 18:20:03 +03:00
2018-06-08 00:25:12 +03:00
function toggle_cell ( id ) {
2018-06-14 18:20:03 +03:00
var e = document . getElementById ( id ) ;
if ( e . style . display == ' table - cell ' ) {
e . style . display = ' none ' ;
} else {
e . style . display = ' table - cell ' ;
}
2018-06-08 00:25:12 +03:00
}
2018-06-14 18:20:03 +03:00
// Go through all columns and collapse needed phases.
2020-03-29 13:17:46 -06:00
const td = document . getElementsByTagName ( "td" ) ;
for ( let i = 0 ; i < td . length ; i ++ ) {
const id = td [ i ] . id ;
const phase = id . substr ( 0 , id . length - 4 ) ;
let show = expandedDefault . indexOf ( phase ) != = - 1
// If show == false, check to see if this is a combined column (multiple phases).
// If combined, check each of the phases to see if they are in our expandedDefaults.
// If any are found, that entire combined column gets shown.
if ( ! show ) {
const combined = phase . split ( ' -- + -- ' ) ;
const len = combined . length ;
if ( len > 1 ) {
for ( let i = 0 ; i < len ; i ++ ) {
if ( expandedDefault . indexOf ( combined [ i ] ) != = - 1 ) {
show = true ;
break ;
}
}
}
}
2018-06-08 00:25:12 +03:00
if ( id . endsWith ( "-exp" ) ) {
2020-03-29 13:17:46 -06:00
const h2Els = td [ i ] . getElementsByTagName ( "h2" ) ;
const len = h2Els . length ;
if ( len > 0 ) {
for ( let i = 0 ; i < len ; i ++ ) {
h2Els [ i ] . addEventListener ( ' click ' , toggler ( phase ) ) ;
}
2018-06-08 00:25:12 +03:00
}
} else {
2018-06-14 18:20:03 +03:00
td [ i ] . addEventListener ( ' click ' , toggler ( phase ) ) ;
2018-06-08 00:25:12 +03:00
}
2018-06-14 18:20:03 +03:00
if ( id . endsWith ( "-col" ) && show || id . endsWith ( "-exp" ) && ! show ) {
td [ i ] . style . display = ' none ' ;
continue ;
2018-06-08 00:25:12 +03:00
}
td [ i ] . style . display = ' table - cell ' ;
}
2018-10-16 13:03:35 +03:00
// find all svg block nodes, add their block classes
var nodes = document . querySelectorAll ( ' * [ id ^ = "graph_node_" ] ' ) ;
for ( var i = 0 ; i < nodes . length ; i ++ ) {
var node = nodes [ i ] ;
var name = node . id . toString ( ) ;
var block = name . substring ( name . lastIndexOf ( "_" ) + 1 ) ;
node . classList . remove ( "node" ) ;
node . classList . add ( block ) ;
node . addEventListener ( ' click ' , ssaBlockClicked ) ;
var ellipse = node . getElementsByTagName ( ' ellipse ' ) [ 0 ] ;
ellipse . classList . add ( block ) ;
ellipse . addEventListener ( ' click ' , ssaBlockClicked ) ;
}
// make big graphs smaller
var targetScale = 0.5 ;
var nodes = document . querySelectorAll ( ' * [ id ^ = "svg_graph_" ] ' ) ;
// TODO: Implement smarter auto-zoom using the viewBox attribute
// and in case of big graphs set the width and height of the svg graph to
// maximum allowed.
for ( var i = 0 ; i < nodes . length ; i ++ ) {
var node = nodes [ i ] ;
var name = node . id . toString ( ) ;
var phase = name . substring ( name . lastIndexOf ( "_" ) + 1 ) ;
var gNode = document . getElementById ( "g_graph_" + phase ) ;
var scale = gNode . transform . baseVal . getItem ( 0 ) . matrix . a ;
if ( scale > targetScale ) {
node . width . baseVal . value *= targetScale / scale ;
node . height . baseVal . value *= targetScale / scale ;
}
}
2015-08-10 12:15:52 -07:00
} ;
function toggle_visibility ( id ) {
2018-06-14 18:20:03 +03:00
var e = document . getElementById ( id ) ;
if ( e . style . display == ' block ' ) {
e . style . display = ' none ' ;
} else {
e . style . display = ' block ' ;
}
2015-08-10 12:15:52 -07:00
}
2018-10-16 13:03:35 +03:00
2018-10-25 13:33:09 +03:00
function hideBlock ( el ) {
var es = el . parentNode . parentNode . getElementsByClassName ( "ssa-value-list" ) ;
if ( es . length == = 0 )
return ;
var e = es [ 0 ] ;
if ( e . style . display == = ' block ' || e . style . display == = ' ' ) {
e . style . display = ' none ' ;
el . innerHTML = '+' ;
} else {
e . style . display = ' block ' ;
el . innerHTML = '-' ;
}
}
2018-10-16 13:03:35 +03:00
// TODO: scale the graph with the viewBox attribute.
function graphReduce ( id ) {
var node = document . getElementById ( id ) ;
if ( node ) {
node . width . baseVal . value *= 0.9 ;
node . height . baseVal . value *= 0.9 ;
}
return false ;
}
function graphEnlarge ( id ) {
var node = document . getElementById ( id ) ;
if ( node ) {
node . width . baseVal . value *= 1.1 ;
node . height . baseVal . value *= 1.1 ;
}
return false ;
}
function makeDraggable ( event ) {
var svg = event . target ;
if ( window . PointerEvent ) {
svg . addEventListener ( ' pointerdown ' , startDrag ) ;
svg . addEventListener ( ' pointermove ' , drag ) ;
svg . addEventListener ( ' pointerup ' , endDrag ) ;
svg . addEventListener ( ' pointerleave ' , endDrag ) ;
} else {
svg . addEventListener ( ' mousedown ' , startDrag ) ;
svg . addEventListener ( ' mousemove ' , drag ) ;
svg . addEventListener ( ' mouseup ' , endDrag ) ;
svg . addEventListener ( ' mouseleave ' , endDrag ) ;
}
var point = svg . createSVGPoint ( ) ;
var isPointerDown = false ;
var pointerOrigin ;
var viewBox = svg . viewBox . baseVal ;
function getPointFromEvent ( event ) {
point . x = event . clientX ;
point . y = event . clientY ;
// We get the current transformation matrix of the SVG and we inverse it
var invertedSVGMatrix = svg . getScreenCTM ( ) . inverse ( ) ;
return point . matrixTransform ( invertedSVGMatrix ) ;
}
function startDrag ( event ) {
isPointerDown = true ;
pointerOrigin = getPointFromEvent ( event ) ;
}
function drag ( event ) {
if ( ! isPointerDown ) {
return ;
}
event . preventDefault ( ) ;
var pointerPosition = getPointFromEvent ( event ) ;
viewBox . x -= ( pointerPosition . x - pointerOrigin . x ) ;
viewBox . y -= ( pointerPosition . y - pointerOrigin . y ) ;
}
function endDrag ( event ) {
isPointerDown = false ;
}
2020-02-20 09:07:48 -07:00
}
function toggleDarkMode ( ) {
document . body . classList . toggle ( ' darkmode ' ) ;
2020-03-10 21:26:42 -06:00
// Collect all of the "collapsed" elements and apply dark mode on each collapsed column
2020-02-20 09:07:48 -07:00
const collapsedEls = document . getElementsByClassName ( ' collapsed ' ) ;
const len = collapsedEls . length ;
for ( let i = 0 ; i < len ; i ++ ) {
collapsedEls [ i ] . classList . toggle ( ' darkmode ' ) ;
}
2020-03-10 21:26:42 -06:00
// Collect and spread the appropriate elements from all of the svgs on the page into one array
const svgParts = [
... document . querySelectorAll ( ' path ' ) ,
... document . querySelectorAll ( ' ellipse ' ) ,
... document . querySelectorAll ( ' polygon ' ) ,
] ;
// Iterate over the svgParts specifically looking for white and black fill/stroke to be toggled.
// The verbose conditional is intentional here so that we do not mutate any svg path, ellipse, or polygon that is of any color other than white or black.
svgParts . forEach ( el = > {
if ( el . attributes . stroke . value == = ' white ' ) {
el . attributes . stroke . value = ' black ' ;
} else if ( el . attributes . stroke . value == = ' black ' ) {
el . attributes . stroke . value = ' white ' ;
}
if ( el . attributes . fill . value == = ' white ' ) {
el . attributes . fill . value = ' black ' ;
} else if ( el . attributes . fill . value == = ' black ' ) {
el . attributes . fill . value = ' white ' ;
}
} ) ;
2020-02-20 09:07:48 -07:00
}
< / script >
2015-08-10 12:15:52 -07:00
< / head > ` )
w . WriteString ( "<body>" )
w . WriteString ( "<h1>" )
2020-04-05 12:44:02 -06:00
w . WriteString ( html . EscapeString ( w . Func . Name ) )
2015-08-10 12:15:52 -07:00
w . WriteString ( "</h1>" )
w . WriteString ( `
2018-10-16 13:03:35 +03:00
< a href = "#" onclick = "toggle_visibility('help');return false;" id = "helplink" > help < / a >
2015-08-10 12:15:52 -07:00
< div id = "help" >
< p >
2016-10-11 13:50:44 -07:00
Click on a value or block to toggle highlighting of that value / block
and its uses . ( Values and blocks are highlighted by ID , and IDs of
dead items may be reused , so not all highlights necessarily correspond
to the clicked item . )
2015-08-10 12:15:52 -07:00
< / p >
< p >
Faded out values and blocks are dead code that has not been eliminated .
< / p >
< p >
Values printed in italics have a dependency cycle .
< / p >
2018-10-16 13:03:35 +03:00
< p >
< b > CFG < / b > : Dashed edge is for unlikely branches . Blue color is for backward edges .
Edge with a dot means that this edge follows the order in which blocks were laidout .
< / p >
2015-08-10 12:15:52 -07:00
< / div >
2020-02-20 09:07:48 -07:00
< label for = "dark-mode-button" style = "margin-left: 15px; cursor: pointer;" > darkmode < / label >
< input type = "checkbox" onclick = "toggleDarkMode();" id = "dark-mode-button" style = "cursor: pointer" / >
2015-08-10 12:15:52 -07:00
` )
w . WriteString ( "<table>" )
w . WriteString ( "<tr>" )
}
func ( w * HTMLWriter ) Close ( ) {
if w == nil {
return
}
2017-04-22 18:59:11 -07:00
io . WriteString ( w . w , "</tr>" )
io . WriteString ( w . w , "</table>" )
io . WriteString ( w . w , "</body>" )
io . WriteString ( w . w , "</html>" )
w . w . Close ( )
2018-07-24 10:39:00 +03:00
fmt . Printf ( "dumped SSA to %v\n" , w . path )
2015-08-10 12:15:52 -07:00
}
2020-04-05 12:44:02 -06:00
// WritePhase writes f in a column headed by title.
2018-06-14 18:20:03 +03:00
// phase is used for collapsing columns and should be unique across the table.
2020-04-05 12:44:02 -06:00
func ( w * HTMLWriter ) WritePhase ( phase , title string ) {
2015-08-10 12:15:52 -07:00
if w == nil {
return // avoid generating HTML just to discard it
}
2020-04-05 12:44:02 -06:00
hash := hashFunc ( w . Func )
2020-03-29 13:17:46 -06:00
w . pendingPhases = append ( w . pendingPhases , phase )
w . pendingTitles = append ( w . pendingTitles , title )
if ! bytes . Equal ( hash , w . prevHash ) {
2020-04-05 16:12:05 -06:00
w . flushPhases ( )
2020-03-29 13:17:46 -06:00
}
w . prevHash = hash
2015-08-10 12:15:52 -07:00
}
2020-04-05 16:12:05 -06:00
// flushPhases collects any pending phases and titles, writes them to the html, and resets the pending slices.
func ( w * HTMLWriter ) flushPhases ( ) {
phaseLen := len ( w . pendingPhases )
if phaseLen == 0 {
return
}
phases := strings . Join ( w . pendingPhases , " + " )
w . WriteMultiTitleColumn (
phases ,
w . pendingTitles ,
fmt . Sprintf ( "hash-%x" , w . prevHash ) ,
w . Func . HTML ( w . pendingPhases [ phaseLen - 1 ] , w . dot ) ,
)
w . pendingPhases = w . pendingPhases [ : 0 ]
w . pendingTitles = w . pendingTitles [ : 0 ]
}
2018-07-26 12:51:06 +03:00
// FuncLines contains source code for a function to be displayed
// in sources column.
type FuncLines struct {
Filename string
StartLineno uint
Lines [ ] string
}
// ByTopo sorts topologically: target function is on top,
// followed by inlined functions sorted by filename and line numbers.
type ByTopo [ ] * FuncLines
func ( x ByTopo ) Len ( ) int { return len ( x ) }
func ( x ByTopo ) Swap ( i , j int ) { x [ i ] , x [ j ] = x [ j ] , x [ i ] }
func ( x ByTopo ) Less ( i , j int ) bool {
a := x [ i ]
b := x [ j ]
2018-09-18 01:28:58 +03:00
if a . Filename == b . Filename {
2018-07-26 12:51:06 +03:00
return a . StartLineno < b . StartLineno
}
return a . Filename < b . Filename
}
2018-06-14 18:20:03 +03:00
// WriteSources writes lines as source code in a column headed by title.
// phase is used for collapsing columns and should be unique across the table.
2018-07-26 12:51:06 +03:00
func ( w * HTMLWriter ) WriteSources ( phase string , all [ ] * FuncLines ) {
2018-06-14 18:20:03 +03:00
if w == nil {
return // avoid generating HTML just to discard it
}
var buf bytes . Buffer
fmt . Fprint ( & buf , "<div class=\"lines\" style=\"width: 8%\">" )
2018-07-26 12:51:06 +03:00
filename := ""
for _ , fl := range all {
fmt . Fprint ( & buf , "<div> </div>" )
if filename != fl . Filename {
fmt . Fprint ( & buf , "<div> </div>" )
filename = fl . Filename
}
for i := range fl . Lines {
ln := int ( fl . StartLineno ) + i
fmt . Fprintf ( & buf , "<div class=\"l%v line-number\">%v</div>" , ln , ln )
}
2018-06-14 18:20:03 +03:00
}
fmt . Fprint ( & buf , "</div><div style=\"width: 92%\"><pre>" )
2018-07-26 12:51:06 +03:00
filename = ""
for _ , fl := range all {
fmt . Fprint ( & buf , "<div> </div>" )
if filename != fl . Filename {
fmt . Fprintf ( & buf , "<div><strong>%v</strong></div>" , fl . Filename )
filename = fl . Filename
}
for i , line := range fl . Lines {
ln := int ( fl . StartLineno ) + i
var escaped string
if strings . TrimSpace ( line ) == "" {
escaped = " "
} else {
escaped = html . EscapeString ( line )
}
fmt . Fprintf ( & buf , "<div class=\"l%v line-number\">%v</div>" , ln , escaped )
}
2018-06-14 18:20:03 +03:00
}
fmt . Fprint ( & buf , "</pre></div>" )
2018-07-31 18:13:05 +03:00
w . WriteColumn ( phase , phase , "allow-x-scroll" , buf . String ( ) )
}
func ( w * HTMLWriter ) WriteAST ( phase string , buf * bytes . Buffer ) {
if w == nil {
return // avoid generating HTML just to discard it
}
lines := strings . Split ( buf . String ( ) , "\n" )
var out bytes . Buffer
fmt . Fprint ( & out , "<div>" )
for _ , l := range lines {
l = strings . TrimSpace ( l )
var escaped string
var lineNo string
if l == "" {
escaped = " "
} else {
if strings . HasPrefix ( l , "buildssa" ) {
escaped = fmt . Sprintf ( "<b>%v</b>" , l )
} else {
// Parse the line number from the format l(123).
idx := strings . Index ( l , " l(" )
if idx != - 1 {
subl := l [ idx + 3 : ]
idxEnd := strings . Index ( subl , ")" )
if idxEnd != - 1 {
if _ , err := strconv . Atoi ( subl [ : idxEnd ] ) ; err == nil {
lineNo = subl [ : idxEnd ]
}
}
}
escaped = html . EscapeString ( l )
}
}
if lineNo != "" {
fmt . Fprintf ( & out , "<div class=\"l%v line-number ast\">%v</div>" , lineNo , escaped )
} else {
fmt . Fprintf ( & out , "<div class=\"ast\">%v</div>" , escaped )
}
}
fmt . Fprint ( & out , "</div>" )
w . WriteColumn ( phase , phase , "allow-x-scroll" , out . String ( ) )
2018-06-14 18:20:03 +03:00
}
2015-08-10 12:15:52 -07:00
// WriteColumn writes raw HTML in a column headed by title.
// It is intended for pre- and post-compilation log output.
2018-06-08 00:25:12 +03:00
func ( w * HTMLWriter ) WriteColumn ( phase , title , class , html string ) {
2020-03-29 13:17:46 -06:00
w . WriteMultiTitleColumn ( phase , [ ] string { title } , class , html )
}
func ( w * HTMLWriter ) WriteMultiTitleColumn ( phase string , titles [ ] string , class , html string ) {
2015-08-10 12:15:52 -07:00
if w == nil {
return
}
2018-06-08 00:25:12 +03:00
id := strings . Replace ( phase , " " , "-" , - 1 )
// collapsed column
w . Printf ( "<td id=\"%v-col\" class=\"collapsed\"><div>%v</div></td>" , id , phase )
cmd/compile: add line numbers to values & blocks in ssa.html
In order to improve the line numbering for debuggers,
it's necessary to trace lines through compilation.
This makes it (much) easier to follow.
The format of the last column of the ssa.html output was
also changed to reduce the spamminess of the file name,
which is usually the same and makes it far harder to read
instructions and line numbers, and to make it wider and also
able to break words when wrapping (long path names still
can push off the end otherwise; side-to-side scrolling was
tried but was more annoying than the occasional wrapped
line).
Sample output now, where [...] is elision for sake of making
the CL character-counter happy -- and the (##) line numbers
are rendered in italics and a smaller font (11 point) under
control of a CSS class "line-number".
genssa
# /Users/drchase/[...]/ssa/testdata/hist.go
00000 (35) TEXT "".main(SB)
00001 (35) FUNCDATA $0, gclocals·7be4bb[...]1e8b(SB)
00002 (35) FUNCDATA $1, gclocals·9ab98a[...]4568(SB)
v920 00003 (36) LEAQ ""..autotmp_31-640(SP), DI
v858 00004 (36) XORPS X0, X0
v6 00005 (36) LEAQ -48(DI), DI
v6 00006 (36) DUFFZERO $277
v576 00007 (36) LEAQ ""..autotmp_31-640(SP), AX
v10 00008 (36) TESTB AX, (AX)
b1 00009 (36) JMP 10
and from an earlier phase:
b18: ← b17
v242 (47) = Copy <mem> v238
v243 (47) = VarKill <mem> {.autotmp_16} v242
v244 (48) = Addr <**bufio.Scanner> {scanner} v2
v245 (48) = Load <*bufio.Scanner> v244 v243
[...]
v279 (49) = Store <mem> {int64} v277 v276 v278
v280 (49) = Addr <*error> {.autotmp_18} v2
v281 (49) = Load <error> v280 v279
v282 (49) = Addr <*error> {err} v2
v283 (49) = VarDef <mem> {err} v279
v284 (49) = Store <mem> {error} v282 v281 v283
v285 (47) = VarKill <mem> {.autotmp_18} v284
v286 (47) = VarKill <mem> {.autotmp_17} v285
v287 (50) = Addr <*error> {err} v2
v288 (50) = Load <error> v287 v286
v289 (50) = NeqInter <bool> v288 v51
If v289 → b21 b22 (line 50)
Change-Id: I3f46310918f965761f59e6f03ea53067237c28a8
Reviewed-on: https://go-review.googlesource.com/69591
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2017-10-10 14:44:15 -04:00
if class == "" {
2018-06-08 00:25:12 +03:00
w . Printf ( "<td id=\"%v-exp\">" , id )
cmd/compile: add line numbers to values & blocks in ssa.html
In order to improve the line numbering for debuggers,
it's necessary to trace lines through compilation.
This makes it (much) easier to follow.
The format of the last column of the ssa.html output was
also changed to reduce the spamminess of the file name,
which is usually the same and makes it far harder to read
instructions and line numbers, and to make it wider and also
able to break words when wrapping (long path names still
can push off the end otherwise; side-to-side scrolling was
tried but was more annoying than the occasional wrapped
line).
Sample output now, where [...] is elision for sake of making
the CL character-counter happy -- and the (##) line numbers
are rendered in italics and a smaller font (11 point) under
control of a CSS class "line-number".
genssa
# /Users/drchase/[...]/ssa/testdata/hist.go
00000 (35) TEXT "".main(SB)
00001 (35) FUNCDATA $0, gclocals·7be4bb[...]1e8b(SB)
00002 (35) FUNCDATA $1, gclocals·9ab98a[...]4568(SB)
v920 00003 (36) LEAQ ""..autotmp_31-640(SP), DI
v858 00004 (36) XORPS X0, X0
v6 00005 (36) LEAQ -48(DI), DI
v6 00006 (36) DUFFZERO $277
v576 00007 (36) LEAQ ""..autotmp_31-640(SP), AX
v10 00008 (36) TESTB AX, (AX)
b1 00009 (36) JMP 10
and from an earlier phase:
b18: ← b17
v242 (47) = Copy <mem> v238
v243 (47) = VarKill <mem> {.autotmp_16} v242
v244 (48) = Addr <**bufio.Scanner> {scanner} v2
v245 (48) = Load <*bufio.Scanner> v244 v243
[...]
v279 (49) = Store <mem> {int64} v277 v276 v278
v280 (49) = Addr <*error> {.autotmp_18} v2
v281 (49) = Load <error> v280 v279
v282 (49) = Addr <*error> {err} v2
v283 (49) = VarDef <mem> {err} v279
v284 (49) = Store <mem> {error} v282 v281 v283
v285 (47) = VarKill <mem> {.autotmp_18} v284
v286 (47) = VarKill <mem> {.autotmp_17} v285
v287 (50) = Addr <*error> {err} v2
v288 (50) = Load <error> v287 v286
v289 (50) = NeqInter <bool> v288 v51
If v289 → b21 b22 (line 50)
Change-Id: I3f46310918f965761f59e6f03ea53067237c28a8
Reviewed-on: https://go-review.googlesource.com/69591
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2017-10-10 14:44:15 -04:00
} else {
2018-06-08 00:25:12 +03:00
w . Printf ( "<td id=\"%v-exp\" class=\"%v\">" , id , class )
cmd/compile: add line numbers to values & blocks in ssa.html
In order to improve the line numbering for debuggers,
it's necessary to trace lines through compilation.
This makes it (much) easier to follow.
The format of the last column of the ssa.html output was
also changed to reduce the spamminess of the file name,
which is usually the same and makes it far harder to read
instructions and line numbers, and to make it wider and also
able to break words when wrapping (long path names still
can push off the end otherwise; side-to-side scrolling was
tried but was more annoying than the occasional wrapped
line).
Sample output now, where [...] is elision for sake of making
the CL character-counter happy -- and the (##) line numbers
are rendered in italics and a smaller font (11 point) under
control of a CSS class "line-number".
genssa
# /Users/drchase/[...]/ssa/testdata/hist.go
00000 (35) TEXT "".main(SB)
00001 (35) FUNCDATA $0, gclocals·7be4bb[...]1e8b(SB)
00002 (35) FUNCDATA $1, gclocals·9ab98a[...]4568(SB)
v920 00003 (36) LEAQ ""..autotmp_31-640(SP), DI
v858 00004 (36) XORPS X0, X0
v6 00005 (36) LEAQ -48(DI), DI
v6 00006 (36) DUFFZERO $277
v576 00007 (36) LEAQ ""..autotmp_31-640(SP), AX
v10 00008 (36) TESTB AX, (AX)
b1 00009 (36) JMP 10
and from an earlier phase:
b18: ← b17
v242 (47) = Copy <mem> v238
v243 (47) = VarKill <mem> {.autotmp_16} v242
v244 (48) = Addr <**bufio.Scanner> {scanner} v2
v245 (48) = Load <*bufio.Scanner> v244 v243
[...]
v279 (49) = Store <mem> {int64} v277 v276 v278
v280 (49) = Addr <*error> {.autotmp_18} v2
v281 (49) = Load <error> v280 v279
v282 (49) = Addr <*error> {err} v2
v283 (49) = VarDef <mem> {err} v279
v284 (49) = Store <mem> {error} v282 v281 v283
v285 (47) = VarKill <mem> {.autotmp_18} v284
v286 (47) = VarKill <mem> {.autotmp_17} v285
v287 (50) = Addr <*error> {err} v2
v288 (50) = Load <error> v287 v286
v289 (50) = NeqInter <bool> v288 v51
If v289 → b21 b22 (line 50)
Change-Id: I3f46310918f965761f59e6f03ea53067237c28a8
Reviewed-on: https://go-review.googlesource.com/69591
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2017-10-10 14:44:15 -04:00
}
2020-03-29 13:17:46 -06:00
for _ , title := range titles {
w . WriteString ( "<h2>" + title + "</h2>" )
}
2015-08-10 12:15:52 -07:00
w . WriteString ( html )
2020-03-29 13:17:46 -06:00
w . WriteString ( "</td>\n" )
2015-08-10 12:15:52 -07:00
}
func ( w * HTMLWriter ) Printf ( msg string , v ... interface { } ) {
2017-04-22 18:59:11 -07:00
if _ , err := fmt . Fprintf ( w . w , msg , v ... ) ; err != nil {
2020-04-05 12:44:02 -06:00
w . Fatalf ( "%v" , err )
2015-08-10 12:15:52 -07:00
}
}
func ( w * HTMLWriter ) WriteString ( s string ) {
2017-04-22 18:59:11 -07:00
if _ , err := io . WriteString ( w . w , s ) ; err != nil {
2020-04-05 12:44:02 -06:00
w . Fatalf ( "%v" , err )
2015-08-10 12:15:52 -07:00
}
}
func ( v * Value ) HTML ( ) string {
// TODO: Using the value ID as the class ignores the fact
// that value IDs get recycled and that some values
// are transmuted into other values.
2016-09-09 11:29:33 -07:00
s := v . String ( )
return fmt . Sprintf ( "<span class=\"%s ssa-value\">%s</span>" , s , s )
2015-08-10 12:15:52 -07:00
}
func ( v * Value ) LongHTML ( ) string {
// TODO: Any intra-value formatting?
// I'm wary of adding too much visual noise,
// but a little bit might be valuable.
// We already have visual noise in the form of punctuation
// maybe we could replace some of that with formatting.
s := fmt . Sprintf ( "<span class=\"%s ssa-long-value\">" , v . String ( ) )
cmd/compile: add line numbers to values & blocks in ssa.html
In order to improve the line numbering for debuggers,
it's necessary to trace lines through compilation.
This makes it (much) easier to follow.
The format of the last column of the ssa.html output was
also changed to reduce the spamminess of the file name,
which is usually the same and makes it far harder to read
instructions and line numbers, and to make it wider and also
able to break words when wrapping (long path names still
can push off the end otherwise; side-to-side scrolling was
tried but was more annoying than the occasional wrapped
line).
Sample output now, where [...] is elision for sake of making
the CL character-counter happy -- and the (##) line numbers
are rendered in italics and a smaller font (11 point) under
control of a CSS class "line-number".
genssa
# /Users/drchase/[...]/ssa/testdata/hist.go
00000 (35) TEXT "".main(SB)
00001 (35) FUNCDATA $0, gclocals·7be4bb[...]1e8b(SB)
00002 (35) FUNCDATA $1, gclocals·9ab98a[...]4568(SB)
v920 00003 (36) LEAQ ""..autotmp_31-640(SP), DI
v858 00004 (36) XORPS X0, X0
v6 00005 (36) LEAQ -48(DI), DI
v6 00006 (36) DUFFZERO $277
v576 00007 (36) LEAQ ""..autotmp_31-640(SP), AX
v10 00008 (36) TESTB AX, (AX)
b1 00009 (36) JMP 10
and from an earlier phase:
b18: ← b17
v242 (47) = Copy <mem> v238
v243 (47) = VarKill <mem> {.autotmp_16} v242
v244 (48) = Addr <**bufio.Scanner> {scanner} v2
v245 (48) = Load <*bufio.Scanner> v244 v243
[...]
v279 (49) = Store <mem> {int64} v277 v276 v278
v280 (49) = Addr <*error> {.autotmp_18} v2
v281 (49) = Load <error> v280 v279
v282 (49) = Addr <*error> {err} v2
v283 (49) = VarDef <mem> {err} v279
v284 (49) = Store <mem> {error} v282 v281 v283
v285 (47) = VarKill <mem> {.autotmp_18} v284
v286 (47) = VarKill <mem> {.autotmp_17} v285
v287 (50) = Addr <*error> {err} v2
v288 (50) = Load <error> v287 v286
v289 (50) = NeqInter <bool> v288 v51
If v289 → b21 b22 (line 50)
Change-Id: I3f46310918f965761f59e6f03ea53067237c28a8
Reviewed-on: https://go-review.googlesource.com/69591
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2017-10-10 14:44:15 -04:00
2018-06-14 18:20:03 +03:00
linenumber := "<span class=\"no-line-number\">(?)</span>"
cmd/compile: add line numbers to values & blocks in ssa.html
In order to improve the line numbering for debuggers,
it's necessary to trace lines through compilation.
This makes it (much) easier to follow.
The format of the last column of the ssa.html output was
also changed to reduce the spamminess of the file name,
which is usually the same and makes it far harder to read
instructions and line numbers, and to make it wider and also
able to break words when wrapping (long path names still
can push off the end otherwise; side-to-side scrolling was
tried but was more annoying than the occasional wrapped
line).
Sample output now, where [...] is elision for sake of making
the CL character-counter happy -- and the (##) line numbers
are rendered in italics and a smaller font (11 point) under
control of a CSS class "line-number".
genssa
# /Users/drchase/[...]/ssa/testdata/hist.go
00000 (35) TEXT "".main(SB)
00001 (35) FUNCDATA $0, gclocals·7be4bb[...]1e8b(SB)
00002 (35) FUNCDATA $1, gclocals·9ab98a[...]4568(SB)
v920 00003 (36) LEAQ ""..autotmp_31-640(SP), DI
v858 00004 (36) XORPS X0, X0
v6 00005 (36) LEAQ -48(DI), DI
v6 00006 (36) DUFFZERO $277
v576 00007 (36) LEAQ ""..autotmp_31-640(SP), AX
v10 00008 (36) TESTB AX, (AX)
b1 00009 (36) JMP 10
and from an earlier phase:
b18: ← b17
v242 (47) = Copy <mem> v238
v243 (47) = VarKill <mem> {.autotmp_16} v242
v244 (48) = Addr <**bufio.Scanner> {scanner} v2
v245 (48) = Load <*bufio.Scanner> v244 v243
[...]
v279 (49) = Store <mem> {int64} v277 v276 v278
v280 (49) = Addr <*error> {.autotmp_18} v2
v281 (49) = Load <error> v280 v279
v282 (49) = Addr <*error> {err} v2
v283 (49) = VarDef <mem> {err} v279
v284 (49) = Store <mem> {error} v282 v281 v283
v285 (47) = VarKill <mem> {.autotmp_18} v284
v286 (47) = VarKill <mem> {.autotmp_17} v285
v287 (50) = Addr <*error> {err} v2
v288 (50) = Load <error> v287 v286
v289 (50) = NeqInter <bool> v288 v51
If v289 → b21 b22 (line 50)
Change-Id: I3f46310918f965761f59e6f03ea53067237c28a8
Reviewed-on: https://go-review.googlesource.com/69591
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2017-10-10 14:44:15 -04:00
if v . Pos . IsKnown ( ) {
2018-06-14 18:20:03 +03:00
linenumber = fmt . Sprintf ( "<span class=\"l%v line-number\">(%s)</span>" , v . Pos . LineNumber ( ) , v . Pos . LineNumberHTML ( ) )
cmd/compile: add line numbers to values & blocks in ssa.html
In order to improve the line numbering for debuggers,
it's necessary to trace lines through compilation.
This makes it (much) easier to follow.
The format of the last column of the ssa.html output was
also changed to reduce the spamminess of the file name,
which is usually the same and makes it far harder to read
instructions and line numbers, and to make it wider and also
able to break words when wrapping (long path names still
can push off the end otherwise; side-to-side scrolling was
tried but was more annoying than the occasional wrapped
line).
Sample output now, where [...] is elision for sake of making
the CL character-counter happy -- and the (##) line numbers
are rendered in italics and a smaller font (11 point) under
control of a CSS class "line-number".
genssa
# /Users/drchase/[...]/ssa/testdata/hist.go
00000 (35) TEXT "".main(SB)
00001 (35) FUNCDATA $0, gclocals·7be4bb[...]1e8b(SB)
00002 (35) FUNCDATA $1, gclocals·9ab98a[...]4568(SB)
v920 00003 (36) LEAQ ""..autotmp_31-640(SP), DI
v858 00004 (36) XORPS X0, X0
v6 00005 (36) LEAQ -48(DI), DI
v6 00006 (36) DUFFZERO $277
v576 00007 (36) LEAQ ""..autotmp_31-640(SP), AX
v10 00008 (36) TESTB AX, (AX)
b1 00009 (36) JMP 10
and from an earlier phase:
b18: ← b17
v242 (47) = Copy <mem> v238
v243 (47) = VarKill <mem> {.autotmp_16} v242
v244 (48) = Addr <**bufio.Scanner> {scanner} v2
v245 (48) = Load <*bufio.Scanner> v244 v243
[...]
v279 (49) = Store <mem> {int64} v277 v276 v278
v280 (49) = Addr <*error> {.autotmp_18} v2
v281 (49) = Load <error> v280 v279
v282 (49) = Addr <*error> {err} v2
v283 (49) = VarDef <mem> {err} v279
v284 (49) = Store <mem> {error} v282 v281 v283
v285 (47) = VarKill <mem> {.autotmp_18} v284
v286 (47) = VarKill <mem> {.autotmp_17} v285
v287 (50) = Addr <*error> {err} v2
v288 (50) = Load <error> v287 v286
v289 (50) = NeqInter <bool> v288 v51
If v289 → b21 b22 (line 50)
Change-Id: I3f46310918f965761f59e6f03ea53067237c28a8
Reviewed-on: https://go-review.googlesource.com/69591
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2017-10-10 14:44:15 -04:00
}
s += fmt . Sprintf ( "%s %s = %s" , v . HTML ( ) , linenumber , v . Op . String ( ) )
2015-08-10 12:15:52 -07:00
s += " <" + html . EscapeString ( v . Type . String ( ) ) + ">"
2016-04-30 11:13:29 +02:00
s += html . EscapeString ( v . auxString ( ) )
2015-08-10 12:15:52 -07:00
for _ , a := range v . Args {
s += fmt . Sprintf ( " %s" , a . HTML ( ) )
}
r := v . Block . Func . RegAlloc
2015-08-11 12:51:33 -07:00
if int ( v . ID ) < len ( r ) && r [ v . ID ] != nil {
2017-08-17 12:23:34 -07:00
s += " : " + html . EscapeString ( r [ v . ID ] . String ( ) )
2015-08-10 12:15:52 -07:00
}
[dev.debug] cmd/compile: better DWARF with optimizations on
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.
After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.
There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:
type myint int
var a int
b := myint(int)
and
b := (*uint64)(unsafe.Pointer(a))
don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.
Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.
A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.
TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.
go build -toolexec 'toolstash -cmp' -a std succeeds.
With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after: 06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean /tmp/220352263 /tmp/621364410
completed 15 of 15, estimated time remaining 0s (eta 3:52PM)
name old time/op new time/op delta
Template 199ms ± 3% 198ms ± 2% ~ (p=0.400 n=15+14)
Unicode 96.6ms ± 5% 96.4ms ± 5% ~ (p=0.838 n=15+15)
GoTypes 653ms ± 2% 647ms ± 2% ~ (p=0.102 n=15+14)
Flate 133ms ± 6% 129ms ± 3% -2.62% (p=0.041 n=15+15)
GoParser 164ms ± 5% 159ms ± 3% -3.05% (p=0.000 n=15+15)
Reflect 428ms ± 4% 422ms ± 3% ~ (p=0.156 n=15+13)
Tar 123ms ±10% 124ms ± 8% ~ (p=0.461 n=15+15)
XML 228ms ± 3% 224ms ± 3% -1.57% (p=0.045 n=15+15)
[Geo mean] 206ms 377ms +82.86%
name old user-time/op new user-time/op delta
Template 292ms ±10% 301ms ±12% ~ (p=0.189 n=15+15)
Unicode 166ms ±37% 158ms ±14% ~ (p=0.418 n=15+14)
GoTypes 962ms ± 6% 963ms ± 7% ~ (p=0.976 n=15+15)
Flate 207ms ±19% 200ms ±14% ~ (p=0.345 n=14+15)
GoParser 246ms ±22% 240ms ±15% ~ (p=0.587 n=15+15)
Reflect 611ms ±13% 587ms ±14% ~ (p=0.085 n=15+13)
Tar 211ms ±12% 217ms ±14% ~ (p=0.355 n=14+15)
XML 335ms ±15% 320ms ±18% ~ (p=0.169 n=15+15)
[Geo mean] 317ms 583ms +83.72%
name old alloc/op new alloc/op delta
Template 40.2MB ± 0% 40.2MB ± 0% -0.15% (p=0.000 n=14+15)
Unicode 29.2MB ± 0% 29.3MB ± 0% ~ (p=0.624 n=15+15)
GoTypes 114MB ± 0% 114MB ± 0% -0.15% (p=0.000 n=15+14)
Flate 25.7MB ± 0% 25.6MB ± 0% -0.18% (p=0.000 n=13+15)
GoParser 32.2MB ± 0% 32.2MB ± 0% -0.14% (p=0.003 n=15+15)
Reflect 77.8MB ± 0% 77.9MB ± 0% ~ (p=0.061 n=15+15)
Tar 27.1MB ± 0% 27.0MB ± 0% -0.11% (p=0.029 n=15+15)
XML 42.7MB ± 0% 42.5MB ± 0% -0.29% (p=0.000 n=15+15)
[Geo mean] 42.1MB 75.0MB +78.05%
name old allocs/op new allocs/op delta
Template 402k ± 1% 398k ± 0% -0.91% (p=0.000 n=15+15)
Unicode 344k ± 1% 344k ± 0% ~ (p=0.715 n=15+14)
GoTypes 1.18M ± 0% 1.17M ± 0% -0.91% (p=0.000 n=15+14)
Flate 243k ± 0% 240k ± 1% -1.05% (p=0.000 n=13+15)
GoParser 327k ± 1% 324k ± 1% -0.96% (p=0.000 n=15+15)
Reflect 984k ± 1% 982k ± 0% ~ (p=0.050 n=15+15)
Tar 261k ± 1% 259k ± 1% -0.77% (p=0.000 n=15+15)
XML 411k ± 0% 404k ± 1% -1.55% (p=0.000 n=15+15)
[Geo mean] 439k 755k +72.01%
name old text-bytes new text-bytes delta
HelloSize 694kB ± 0% 694kB ± 0% -0.00% (p=0.000 n=15+15)
name old data-bytes new data-bytes delta
HelloSize 5.55kB ± 0% 5.55kB ± 0% ~ (all equal)
name old bss-bytes new bss-bytes delta
HelloSize 133kB ± 0% 133kB ± 0% ~ (all equal)
name old exe-bytes new exe-bytes delta
HelloSize 1.04MB ± 0% 1.04MB ± 0% ~ (all equal)
Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2017-07-21 18:30:19 -04:00
var names [ ] string
for name , values := range v . Block . Func . NamedValues {
for _ , value := range values {
if value == v {
2017-08-17 12:23:34 -07:00
names = append ( names , name . String ( ) )
[dev.debug] cmd/compile: better DWARF with optimizations on
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.
After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.
There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:
type myint int
var a int
b := myint(int)
and
b := (*uint64)(unsafe.Pointer(a))
don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.
Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.
A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.
TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.
go build -toolexec 'toolstash -cmp' -a std succeeds.
With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after: 06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean /tmp/220352263 /tmp/621364410
completed 15 of 15, estimated time remaining 0s (eta 3:52PM)
name old time/op new time/op delta
Template 199ms ± 3% 198ms ± 2% ~ (p=0.400 n=15+14)
Unicode 96.6ms ± 5% 96.4ms ± 5% ~ (p=0.838 n=15+15)
GoTypes 653ms ± 2% 647ms ± 2% ~ (p=0.102 n=15+14)
Flate 133ms ± 6% 129ms ± 3% -2.62% (p=0.041 n=15+15)
GoParser 164ms ± 5% 159ms ± 3% -3.05% (p=0.000 n=15+15)
Reflect 428ms ± 4% 422ms ± 3% ~ (p=0.156 n=15+13)
Tar 123ms ±10% 124ms ± 8% ~ (p=0.461 n=15+15)
XML 228ms ± 3% 224ms ± 3% -1.57% (p=0.045 n=15+15)
[Geo mean] 206ms 377ms +82.86%
name old user-time/op new user-time/op delta
Template 292ms ±10% 301ms ±12% ~ (p=0.189 n=15+15)
Unicode 166ms ±37% 158ms ±14% ~ (p=0.418 n=15+14)
GoTypes 962ms ± 6% 963ms ± 7% ~ (p=0.976 n=15+15)
Flate 207ms ±19% 200ms ±14% ~ (p=0.345 n=14+15)
GoParser 246ms ±22% 240ms ±15% ~ (p=0.587 n=15+15)
Reflect 611ms ±13% 587ms ±14% ~ (p=0.085 n=15+13)
Tar 211ms ±12% 217ms ±14% ~ (p=0.355 n=14+15)
XML 335ms ±15% 320ms ±18% ~ (p=0.169 n=15+15)
[Geo mean] 317ms 583ms +83.72%
name old alloc/op new alloc/op delta
Template 40.2MB ± 0% 40.2MB ± 0% -0.15% (p=0.000 n=14+15)
Unicode 29.2MB ± 0% 29.3MB ± 0% ~ (p=0.624 n=15+15)
GoTypes 114MB ± 0% 114MB ± 0% -0.15% (p=0.000 n=15+14)
Flate 25.7MB ± 0% 25.6MB ± 0% -0.18% (p=0.000 n=13+15)
GoParser 32.2MB ± 0% 32.2MB ± 0% -0.14% (p=0.003 n=15+15)
Reflect 77.8MB ± 0% 77.9MB ± 0% ~ (p=0.061 n=15+15)
Tar 27.1MB ± 0% 27.0MB ± 0% -0.11% (p=0.029 n=15+15)
XML 42.7MB ± 0% 42.5MB ± 0% -0.29% (p=0.000 n=15+15)
[Geo mean] 42.1MB 75.0MB +78.05%
name old allocs/op new allocs/op delta
Template 402k ± 1% 398k ± 0% -0.91% (p=0.000 n=15+15)
Unicode 344k ± 1% 344k ± 0% ~ (p=0.715 n=15+14)
GoTypes 1.18M ± 0% 1.17M ± 0% -0.91% (p=0.000 n=15+14)
Flate 243k ± 0% 240k ± 1% -1.05% (p=0.000 n=13+15)
GoParser 327k ± 1% 324k ± 1% -0.96% (p=0.000 n=15+15)
Reflect 984k ± 1% 982k ± 0% ~ (p=0.050 n=15+15)
Tar 261k ± 1% 259k ± 1% -0.77% (p=0.000 n=15+15)
XML 411k ± 0% 404k ± 1% -1.55% (p=0.000 n=15+15)
[Geo mean] 439k 755k +72.01%
name old text-bytes new text-bytes delta
HelloSize 694kB ± 0% 694kB ± 0% -0.00% (p=0.000 n=15+15)
name old data-bytes new data-bytes delta
HelloSize 5.55kB ± 0% 5.55kB ± 0% ~ (all equal)
name old bss-bytes new bss-bytes delta
HelloSize 133kB ± 0% 133kB ± 0% ~ (all equal)
name old exe-bytes new exe-bytes delta
HelloSize 1.04MB ± 0% 1.04MB ± 0% ~ (all equal)
Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2017-07-21 18:30:19 -04:00
break // drop duplicates.
}
}
}
if len ( names ) != 0 {
s += " (" + strings . Join ( names , ", " ) + ")"
}
cmd/compile: add line numbers to values & blocks in ssa.html
In order to improve the line numbering for debuggers,
it's necessary to trace lines through compilation.
This makes it (much) easier to follow.
The format of the last column of the ssa.html output was
also changed to reduce the spamminess of the file name,
which is usually the same and makes it far harder to read
instructions and line numbers, and to make it wider and also
able to break words when wrapping (long path names still
can push off the end otherwise; side-to-side scrolling was
tried but was more annoying than the occasional wrapped
line).
Sample output now, where [...] is elision for sake of making
the CL character-counter happy -- and the (##) line numbers
are rendered in italics and a smaller font (11 point) under
control of a CSS class "line-number".
genssa
# /Users/drchase/[...]/ssa/testdata/hist.go
00000 (35) TEXT "".main(SB)
00001 (35) FUNCDATA $0, gclocals·7be4bb[...]1e8b(SB)
00002 (35) FUNCDATA $1, gclocals·9ab98a[...]4568(SB)
v920 00003 (36) LEAQ ""..autotmp_31-640(SP), DI
v858 00004 (36) XORPS X0, X0
v6 00005 (36) LEAQ -48(DI), DI
v6 00006 (36) DUFFZERO $277
v576 00007 (36) LEAQ ""..autotmp_31-640(SP), AX
v10 00008 (36) TESTB AX, (AX)
b1 00009 (36) JMP 10
and from an earlier phase:
b18: ← b17
v242 (47) = Copy <mem> v238
v243 (47) = VarKill <mem> {.autotmp_16} v242
v244 (48) = Addr <**bufio.Scanner> {scanner} v2
v245 (48) = Load <*bufio.Scanner> v244 v243
[...]
v279 (49) = Store <mem> {int64} v277 v276 v278
v280 (49) = Addr <*error> {.autotmp_18} v2
v281 (49) = Load <error> v280 v279
v282 (49) = Addr <*error> {err} v2
v283 (49) = VarDef <mem> {err} v279
v284 (49) = Store <mem> {error} v282 v281 v283
v285 (47) = VarKill <mem> {.autotmp_18} v284
v286 (47) = VarKill <mem> {.autotmp_17} v285
v287 (50) = Addr <*error> {err} v2
v288 (50) = Load <error> v287 v286
v289 (50) = NeqInter <bool> v288 v51
If v289 → b21 b22 (line 50)
Change-Id: I3f46310918f965761f59e6f03ea53067237c28a8
Reviewed-on: https://go-review.googlesource.com/69591
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2017-10-10 14:44:15 -04:00
2015-08-10 12:15:52 -07:00
s += "</span>"
return s
}
func ( b * Block ) HTML ( ) string {
// TODO: Using the value ID as the class ignores the fact
// that value IDs get recycled and that some values
// are transmuted into other values.
2016-09-09 11:29:33 -07:00
s := html . EscapeString ( b . String ( ) )
return fmt . Sprintf ( "<span class=\"%s ssa-block\">%s</span>" , s , s )
2015-08-10 12:15:52 -07:00
}
func ( b * Block ) LongHTML ( ) string {
// TODO: improve this for HTML?
2015-08-27 10:29:01 -07:00
s := fmt . Sprintf ( "<span class=\"%s ssa-block\">%s</span>" , html . EscapeString ( b . String ( ) ) , html . EscapeString ( b . Kind . String ( ) ) )
2015-09-08 21:28:44 -07:00
if b . Aux != nil {
s += html . EscapeString ( fmt . Sprintf ( " {%v}" , b . Aux ) )
}
2019-08-12 20:19:58 +01:00
for _ , c := range b . ControlValues ( ) {
s += fmt . Sprintf ( " %s" , c . HTML ( ) )
2015-08-10 12:15:52 -07:00
}
if len ( b . Succs ) > 0 {
s += " →" // right arrow
2016-04-28 16:52:47 -07:00
for _ , e := range b . Succs {
c := e . b
2015-08-10 12:15:52 -07:00
s += " " + c . HTML ( )
}
}
2015-08-17 00:29:56 -07:00
switch b . Likely {
case BranchUnlikely :
s += " (unlikely)"
case BranchLikely :
s += " (likely)"
}
cmd/compile: add line numbers to values & blocks in ssa.html
In order to improve the line numbering for debuggers,
it's necessary to trace lines through compilation.
This makes it (much) easier to follow.
The format of the last column of the ssa.html output was
also changed to reduce the spamminess of the file name,
which is usually the same and makes it far harder to read
instructions and line numbers, and to make it wider and also
able to break words when wrapping (long path names still
can push off the end otherwise; side-to-side scrolling was
tried but was more annoying than the occasional wrapped
line).
Sample output now, where [...] is elision for sake of making
the CL character-counter happy -- and the (##) line numbers
are rendered in italics and a smaller font (11 point) under
control of a CSS class "line-number".
genssa
# /Users/drchase/[...]/ssa/testdata/hist.go
00000 (35) TEXT "".main(SB)
00001 (35) FUNCDATA $0, gclocals·7be4bb[...]1e8b(SB)
00002 (35) FUNCDATA $1, gclocals·9ab98a[...]4568(SB)
v920 00003 (36) LEAQ ""..autotmp_31-640(SP), DI
v858 00004 (36) XORPS X0, X0
v6 00005 (36) LEAQ -48(DI), DI
v6 00006 (36) DUFFZERO $277
v576 00007 (36) LEAQ ""..autotmp_31-640(SP), AX
v10 00008 (36) TESTB AX, (AX)
b1 00009 (36) JMP 10
and from an earlier phase:
b18: ← b17
v242 (47) = Copy <mem> v238
v243 (47) = VarKill <mem> {.autotmp_16} v242
v244 (48) = Addr <**bufio.Scanner> {scanner} v2
v245 (48) = Load <*bufio.Scanner> v244 v243
[...]
v279 (49) = Store <mem> {int64} v277 v276 v278
v280 (49) = Addr <*error> {.autotmp_18} v2
v281 (49) = Load <error> v280 v279
v282 (49) = Addr <*error> {err} v2
v283 (49) = VarDef <mem> {err} v279
v284 (49) = Store <mem> {error} v282 v281 v283
v285 (47) = VarKill <mem> {.autotmp_18} v284
v286 (47) = VarKill <mem> {.autotmp_17} v285
v287 (50) = Addr <*error> {err} v2
v288 (50) = Load <error> v287 v286
v289 (50) = NeqInter <bool> v288 v51
If v289 → b21 b22 (line 50)
Change-Id: I3f46310918f965761f59e6f03ea53067237c28a8
Reviewed-on: https://go-review.googlesource.com/69591
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2017-10-10 14:44:15 -04:00
if b . Pos . IsKnown ( ) {
// TODO does not begin to deal with the full complexity of line numbers.
// Maybe we want a string/slice instead, of outer-inner when inlining.
2018-06-14 18:20:03 +03:00
s += fmt . Sprintf ( " <span class=\"l%v line-number\">(%s)</span>" , b . Pos . LineNumber ( ) , b . Pos . LineNumberHTML ( ) )
cmd/compile: add line numbers to values & blocks in ssa.html
In order to improve the line numbering for debuggers,
it's necessary to trace lines through compilation.
This makes it (much) easier to follow.
The format of the last column of the ssa.html output was
also changed to reduce the spamminess of the file name,
which is usually the same and makes it far harder to read
instructions and line numbers, and to make it wider and also
able to break words when wrapping (long path names still
can push off the end otherwise; side-to-side scrolling was
tried but was more annoying than the occasional wrapped
line).
Sample output now, where [...] is elision for sake of making
the CL character-counter happy -- and the (##) line numbers
are rendered in italics and a smaller font (11 point) under
control of a CSS class "line-number".
genssa
# /Users/drchase/[...]/ssa/testdata/hist.go
00000 (35) TEXT "".main(SB)
00001 (35) FUNCDATA $0, gclocals·7be4bb[...]1e8b(SB)
00002 (35) FUNCDATA $1, gclocals·9ab98a[...]4568(SB)
v920 00003 (36) LEAQ ""..autotmp_31-640(SP), DI
v858 00004 (36) XORPS X0, X0
v6 00005 (36) LEAQ -48(DI), DI
v6 00006 (36) DUFFZERO $277
v576 00007 (36) LEAQ ""..autotmp_31-640(SP), AX
v10 00008 (36) TESTB AX, (AX)
b1 00009 (36) JMP 10
and from an earlier phase:
b18: ← b17
v242 (47) = Copy <mem> v238
v243 (47) = VarKill <mem> {.autotmp_16} v242
v244 (48) = Addr <**bufio.Scanner> {scanner} v2
v245 (48) = Load <*bufio.Scanner> v244 v243
[...]
v279 (49) = Store <mem> {int64} v277 v276 v278
v280 (49) = Addr <*error> {.autotmp_18} v2
v281 (49) = Load <error> v280 v279
v282 (49) = Addr <*error> {err} v2
v283 (49) = VarDef <mem> {err} v279
v284 (49) = Store <mem> {error} v282 v281 v283
v285 (47) = VarKill <mem> {.autotmp_18} v284
v286 (47) = VarKill <mem> {.autotmp_17} v285
v287 (50) = Addr <*error> {err} v2
v288 (50) = Load <error> v287 v286
v289 (50) = NeqInter <bool> v288 v51
If v289 → b21 b22 (line 50)
Change-Id: I3f46310918f965761f59e6f03ea53067237c28a8
Reviewed-on: https://go-review.googlesource.com/69591
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2017-10-10 14:44:15 -04:00
}
2015-08-10 12:15:52 -07:00
return s
}
2018-10-16 13:03:35 +03:00
func ( f * Func ) HTML ( phase string , dot * dotWriter ) string {
buf := new ( bytes . Buffer )
if dot != nil {
dot . writeFuncSVG ( buf , phase , f )
}
fmt . Fprint ( buf , "<code>" )
p := htmlFuncPrinter { w : buf }
2015-08-10 12:15:52 -07:00
fprintFunc ( p , f )
// fprintFunc(&buf, f) // TODO: HTML, not text, <br /> for line breaks, etc.
2018-10-16 13:03:35 +03:00
fmt . Fprint ( buf , "</code>" )
2015-08-10 12:15:52 -07:00
return buf . String ( )
}
2018-10-16 13:03:35 +03:00
func ( d * dotWriter ) writeFuncSVG ( w io . Writer , phase string , f * Func ) {
if d . broken {
return
}
if _ , ok := d . phases [ phase ] ; ! ok {
return
}
cmd := exec . Command ( d . path , "-Tsvg" )
pipe , err := cmd . StdinPipe ( )
if err != nil {
d . broken = true
fmt . Println ( err )
return
}
buf := new ( bytes . Buffer )
cmd . Stdout = buf
bufErr := new ( bytes . Buffer )
cmd . Stderr = bufErr
err = cmd . Start ( )
if err != nil {
d . broken = true
fmt . Println ( err )
return
}
2020-03-07 13:36:36 -08:00
fmt . Fprint ( pipe , ` digraph "" { margin=0; ranksep=.2; ` )
2018-10-16 13:03:35 +03:00
id := strings . Replace ( phase , " " , "-" , - 1 )
fmt . Fprintf ( pipe , ` id="g_graph_%s"; ` , id )
fmt . Fprintf ( pipe , ` node [style=filled,fillcolor=white,fontsize=16,fontname="Menlo,Times,serif",margin="0.01,0.03"]; ` )
fmt . Fprintf ( pipe , ` edge [fontsize=16,fontname="Menlo,Times,serif"]; ` )
for i , b := range f . Blocks {
if b . Kind == BlockInvalid {
continue
}
layout := ""
if f . laidout {
layout = fmt . Sprintf ( " #%d" , i )
}
2018-11-21 19:06:28 +02:00
fmt . Fprintf ( pipe , ` %v [label="%v%s\n%v",id="graph_node_%v_%v",tooltip="%v"]; ` , b , b , layout , b . Kind . String ( ) , id , b , b . LongString ( ) )
2018-10-16 13:03:35 +03:00
}
indexOf := make ( [ ] int , f . NumBlocks ( ) )
for i , b := range f . Blocks {
indexOf [ b . ID ] = i
}
layoutDrawn := make ( [ ] bool , f . NumBlocks ( ) )
ponums := make ( [ ] int32 , f . NumBlocks ( ) )
_ = postorderWithNumbering ( f , ponums )
isBackEdge := func ( from , to ID ) bool {
return ponums [ from ] <= ponums [ to ]
}
for _ , b := range f . Blocks {
for i , s := range b . Succs {
style := "solid"
color := "black"
arrow := "vee"
if b . unlikelyIndex ( ) == i {
style = "dashed"
}
if f . laidout && indexOf [ s . b . ID ] == indexOf [ b . ID ] + 1 {
// Red color means ordered edge. It overrides other colors.
arrow = "dotvee"
layoutDrawn [ s . b . ID ] = true
} else if isBackEdge ( b . ID , s . b . ID ) {
2020-03-10 21:26:42 -06:00
color = "#2893ff"
2018-10-16 13:03:35 +03:00
}
fmt . Fprintf ( pipe , ` %v -> %v [label=" %d ",style="%s",color="%s",arrowhead="%s"]; ` , b , s . b , i , style , color , arrow )
}
}
if f . laidout {
fmt . Fprintln ( pipe , ` edge[constraint=false,color=gray,style=solid,arrowhead=dot]; ` )
colors := [ ... ] string { "#eea24f" , "#f38385" , "#f4d164" , "#ca89fc" , "gray" }
ci := 0
for i := 1 ; i < len ( f . Blocks ) ; i ++ {
if layoutDrawn [ f . Blocks [ i ] . ID ] {
continue
}
fmt . Fprintf ( pipe , ` %s -> %s [color="%s"]; ` , f . Blocks [ i - 1 ] , f . Blocks [ i ] , colors [ ci ] )
ci = ( ci + 1 ) % len ( colors )
}
}
fmt . Fprint ( pipe , "}" )
pipe . Close ( )
err = cmd . Wait ( )
if err != nil {
d . broken = true
2018-11-21 19:06:28 +02:00
fmt . Printf ( "dot: %v\n%v\n" , err , bufErr . String ( ) )
2018-10-16 13:03:35 +03:00
return
}
svgID := "svg_graph_" + id
fmt . Fprintf ( w , ` <div class="zoom"><button onclick="return graphReduce('%s');">-</button> <button onclick="return graphEnlarge('%s');">+</button></div> ` , svgID , svgID )
// For now, an awful hack: edit the html as it passes through
// our fingers, finding '<svg ' and injecting needed attributes after it.
err = d . copyUntil ( w , buf , ` <svg ` )
if err != nil {
2018-11-21 19:06:28 +02:00
fmt . Printf ( "injecting attributes: %v\n" , err )
2018-10-16 13:03:35 +03:00
return
}
fmt . Fprintf ( w , ` id="%s" onload="makeDraggable(evt)" ` , svgID )
io . Copy ( w , buf )
}
func ( b * Block ) unlikelyIndex ( ) int {
switch b . Likely {
case BranchLikely :
return 1
case BranchUnlikely :
return 0
}
return - 1
}
func ( d * dotWriter ) copyUntil ( w io . Writer , buf * bytes . Buffer , sep string ) error {
i := bytes . Index ( buf . Bytes ( ) , [ ] byte ( sep ) )
if i == - 1 {
return fmt . Errorf ( "couldn't find dot sep %q" , sep )
}
_ , err := io . CopyN ( w , buf , int64 ( i + len ( sep ) ) )
return err
}
2015-08-10 12:15:52 -07:00
type htmlFuncPrinter struct {
w io . Writer
}
func ( p htmlFuncPrinter ) header ( f * Func ) { }
func ( p htmlFuncPrinter ) startBlock ( b * Block , reachable bool ) {
var dead string
if ! reachable {
dead = "dead-block"
}
fmt . Fprintf ( p . w , "<ul class=\"%s ssa-print-func %s\">" , b , dead )
fmt . Fprintf ( p . w , "<li class=\"ssa-start-block\">%s:" , b . HTML ( ) )
if len ( b . Preds ) > 0 {
io . WriteString ( p . w , " ←" ) // left arrow
2016-04-28 16:52:47 -07:00
for _ , e := range b . Preds {
pred := e . b
2015-08-10 12:15:52 -07:00
fmt . Fprintf ( p . w , " %s" , pred . HTML ( ) )
}
}
2018-10-25 13:33:09 +03:00
if len ( b . Values ) > 0 {
io . WriteString ( p . w , ` <button onclick="hideBlock(this)">-</button> ` )
}
2015-08-10 12:15:52 -07:00
io . WriteString ( p . w , "</li>" )
if len ( b . Values ) > 0 { // start list of values
io . WriteString ( p . w , "<li class=\"ssa-value-list\">" )
io . WriteString ( p . w , "<ul>" )
}
}
func ( p htmlFuncPrinter ) endBlock ( b * Block ) {
if len ( b . Values ) > 0 { // end list of values
io . WriteString ( p . w , "</ul>" )
io . WriteString ( p . w , "</li>" )
}
io . WriteString ( p . w , "<li class=\"ssa-end-block\">" )
fmt . Fprint ( p . w , b . LongHTML ( ) )
io . WriteString ( p . w , "</li>" )
io . WriteString ( p . w , "</ul>" )
}
func ( p htmlFuncPrinter ) value ( v * Value , live bool ) {
var dead string
if ! live {
dead = "dead-value"
}
fmt . Fprintf ( p . w , "<li class=\"ssa-long-value %s\">" , dead )
fmt . Fprint ( p . w , v . LongHTML ( ) )
io . WriteString ( p . w , "</li>" )
}
func ( p htmlFuncPrinter ) startDepCycle ( ) {
fmt . Fprintln ( p . w , "<span class=\"depcycle\">" )
}
func ( p htmlFuncPrinter ) endDepCycle ( ) {
fmt . Fprintln ( p . w , "</span>" )
}
2015-11-02 08:10:26 -08:00
func ( p htmlFuncPrinter ) named ( n LocalSlot , vals [ ] * Value ) {
2017-08-17 12:23:34 -07:00
fmt . Fprintf ( p . w , "<li>name %s: " , n )
2017-02-07 15:49:43 -05:00
for _ , val := range vals {
fmt . Fprintf ( p . w , "%s " , val . HTML ( ) )
}
fmt . Fprintf ( p . w , "</li>" )
2015-11-02 08:10:26 -08:00
}
2018-10-16 13:03:35 +03:00
type dotWriter struct {
path string
broken bool
phases map [ string ] bool // keys specify phases with CFGs
}
// newDotWriter returns non-nil value when mask is valid.
2019-02-22 15:53:52 +00:00
// dotWriter will generate SVGs only for the phases specified in the mask.
2018-10-16 13:03:35 +03:00
// mask can contain following patterns and combinations of them:
// * - all of them;
// x-y - x through y, inclusive;
// x,y - x and y, but not the passes between.
func newDotWriter ( mask string ) * dotWriter {
if mask == "" {
return nil
}
// User can specify phase name with _ instead of spaces.
mask = strings . Replace ( mask , "_" , " " , - 1 )
ph := make ( map [ string ] bool )
ranges := strings . Split ( mask , "," )
for _ , r := range ranges {
spl := strings . Split ( r , "-" )
if len ( spl ) > 2 {
fmt . Printf ( "range is not valid: %v\n" , mask )
return nil
}
var first , last int
if mask == "*" {
first = 0
last = len ( passes ) - 1
} else {
first = passIdxByName ( spl [ 0 ] )
last = passIdxByName ( spl [ len ( spl ) - 1 ] )
}
if first < 0 || last < 0 || first > last {
fmt . Printf ( "range is not valid: %v\n" , r )
return nil
}
for p := first ; p <= last ; p ++ {
ph [ passes [ p ] . name ] = true
}
}
path , err := exec . LookPath ( "dot" )
if err != nil {
fmt . Println ( err )
return nil
}
return & dotWriter { path : path , phases : ph }
}
func passIdxByName ( name string ) int {
for i , p := range passes {
if p . name == name {
return i
}
}
return - 1
}