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"
|
[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 {
|
|
|
|
|
Logger
|
2017-04-22 18:59:11 -07:00
|
|
|
w io.WriteCloser
|
2015-08-10 12:15:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewHTMLWriter(path string, logger Logger, funcname string) *HTMLWriter {
|
|
|
|
|
out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
|
|
|
|
if err != nil {
|
2016-12-15 17:17:01 -08:00
|
|
|
logger.Fatalf(src.NoXPos, "%v", err)
|
2015-08-10 12:15:52 -07:00
|
|
|
}
|
2017-04-22 18:59:11 -07:00
|
|
|
html := HTMLWriter{w: out, Logger: logger}
|
2015-08-10 12:15:52 -07:00
|
|
|
html.start(funcname)
|
|
|
|
|
return &html
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (w *HTMLWriter) start(name string) {
|
|
|
|
|
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>
|
|
|
|
|
|
|
|
|
|
#helplink {
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
display: block;
|
|
|
|
|
margin-top: -15px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#help {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-04 17:33:56 -07:00
|
|
|
.stats {
|
|
|
|
|
font-size: 60%;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-10 12:15:52 -07:00
|
|
|
table {
|
|
|
|
|
border: 1px solid black;
|
|
|
|
|
table-layout: fixed;
|
|
|
|
|
width: 300px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
th, td {
|
|
|
|
|
border: 1px solid black;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
width: 400px;
|
|
|
|
|
vertical-align: top;
|
|
|
|
|
padding: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
li {
|
|
|
|
|
list-style-type: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
li.ssa-long-value {
|
|
|
|
|
text-indent: -2em; /* 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dl.ssa-gen {
|
|
|
|
|
padding-left: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dt.ssa-prog-src {
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
float: left;
|
|
|
|
|
width: 4em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dd.ssa-prog {
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin-right: 0;
|
|
|
|
|
margin-left: 4em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dead-value {
|
|
|
|
|
color: gray;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dead-block {
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.depcycle {
|
|
|
|
|
font-style: italic;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.highlight-yellow { background-color: yellow; }
|
|
|
|
|
.highlight-aquamarine { background-color: aquamarine; }
|
|
|
|
|
.highlight-coral { background-color: coral; }
|
|
|
|
|
.highlight-lightpink { background-color: lightpink; }
|
|
|
|
|
.highlight-lightsteelblue { background-color: lightsteelblue; }
|
|
|
|
|
.highlight-palegreen { background-color: palegreen; }
|
|
|
|
|
.highlight-powderblue { background-color: powderblue; }
|
|
|
|
|
.highlight-lightgray { background-color: lightgray; }
|
|
|
|
|
|
|
|
|
|
.outline-blue { outline: blue solid 2px; }
|
|
|
|
|
.outline-red { outline: red solid 2px; }
|
|
|
|
|
.outline-blueviolet { outline: blueviolet solid 2px; }
|
|
|
|
|
.outline-darkolivegreen { outline: darkolivegreen solid 2px; }
|
|
|
|
|
.outline-fuchsia { outline: fuchsia solid 2px; }
|
|
|
|
|
.outline-sienna { outline: sienna solid 2px; }
|
|
|
|
|
.outline-gold { outline: gold solid 2px; }
|
|
|
|
|
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
|
|
|
// ordered list of all available highlight colors
|
|
|
|
|
var highlights = [
|
|
|
|
|
"highlight-aquamarine",
|
|
|
|
|
"highlight-coral",
|
|
|
|
|
"highlight-lightpink",
|
|
|
|
|
"highlight-lightsteelblue",
|
|
|
|
|
"highlight-palegreen",
|
2017-05-13 08:29:47 -07:00
|
|
|
"highlight-lightgray",
|
|
|
|
|
"highlight-yellow"
|
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",
|
|
|
|
|
"outline-gold"
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// state: which value is outlined this color?
|
|
|
|
|
var outlined = {};
|
|
|
|
|
for (var i = 0; i < outlines.length; i++) {
|
|
|
|
|
outlined[outlines[i]] = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window.onload = function() {
|
|
|
|
|
var ssaElemClicked = function(elem, event, selections, selected) {
|
|
|
|
|
event.stopPropagation()
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ssaBlockClicked = function(event) {
|
|
|
|
|
ssaElemClicked(this, event, outlines, outlined);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function toggle_visibility(id) {
|
|
|
|
|
var e = document.getElementById(id);
|
|
|
|
|
if(e.style.display == 'block')
|
|
|
|
|
e.style.display = 'none';
|
|
|
|
|
else
|
|
|
|
|
e.style.display = 'block';
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
</head>`)
|
|
|
|
|
w.WriteString("<body>")
|
|
|
|
|
w.WriteString("<h1>")
|
|
|
|
|
w.WriteString(html.EscapeString(name))
|
|
|
|
|
w.WriteString("</h1>")
|
|
|
|
|
w.WriteString(`
|
|
|
|
|
<a href="#" onclick="toggle_visibility('help');" id="helplink">help</a>
|
|
|
|
|
<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>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
`)
|
|
|
|
|
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()
|
2015-08-10 12:15:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WriteFunc writes f in a column headed by title.
|
|
|
|
|
func (w *HTMLWriter) WriteFunc(title string, f *Func) {
|
|
|
|
|
if w == nil {
|
|
|
|
|
return // avoid generating HTML just to discard it
|
|
|
|
|
}
|
|
|
|
|
w.WriteColumn(title, f.HTML())
|
|
|
|
|
// TODO: Add visual representation of f's CFG.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WriteColumn writes raw HTML in a column headed by title.
|
|
|
|
|
// It is intended for pre- and post-compilation log output.
|
|
|
|
|
func (w *HTMLWriter) WriteColumn(title string, html string) {
|
|
|
|
|
if w == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
w.WriteString("<td>")
|
|
|
|
|
w.WriteString("<h2>" + title + "</h2>")
|
|
|
|
|
w.WriteString(html)
|
|
|
|
|
w.WriteString("</td>")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
2016-12-15 17:17:01 -08:00
|
|
|
w.Fatalf(src.NoXPos, "%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 {
|
2016-12-15 17:17:01 -08:00
|
|
|
w.Fatalf(src.NoXPos, "%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())
|
|
|
|
|
s += fmt.Sprintf("%s = %s", v.HTML(), v.Op.String())
|
|
|
|
|
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, ", ") + ")"
|
|
|
|
|
}
|
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))
|
|
|
|
|
}
|
2015-08-10 12:15:52 -07:00
|
|
|
if b.Control != nil {
|
|
|
|
|
s += fmt.Sprintf(" %s", b.Control.HTML())
|
|
|
|
|
}
|
|
|
|
|
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)"
|
|
|
|
|
}
|
2015-08-10 12:15:52 -07:00
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *Func) HTML() string {
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
fmt.Fprint(&buf, "<code>")
|
|
|
|
|
p := htmlFuncPrinter{w: &buf}
|
|
|
|
|
fprintFunc(p, f)
|
|
|
|
|
|
|
|
|
|
// fprintFunc(&buf, f) // TODO: HTML, not text, <br /> for line breaks, etc.
|
|
|
|
|
fmt.Fprint(&buf, "</code>")
|
|
|
|
|
return buf.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type htmlFuncPrinter struct {
|
|
|
|
|
w io.Writer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p htmlFuncPrinter) header(f *Func) {}
|
|
|
|
|
|
|
|
|
|
func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) {
|
|
|
|
|
// TODO: Make blocks collapsable?
|
|
|
|
|
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())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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>")
|
|
|
|
|
// io.WriteString(p.w, "</span>")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
}
|