diff options
Diffstat (limited to 'src/cmd/compile/internal/ssa/html.go')
| -rw-r--r-- | src/cmd/compile/internal/ssa/html.go | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go new file mode 100644 index 0000000000..bb88a3ebde --- /dev/null +++ b/src/cmd/compile/internal/ssa/html.go @@ -0,0 +1,478 @@ +// 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" + "fmt" + "html" + "io" + "os" +) + +type HTMLWriter struct { + Logger + *os.File +} + +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 { + logger.Fatalf(0, "%v", err) + } + html := HTMLWriter{File: out, Logger: logger} + html.start(funcname) + return &html +} + +func (w *HTMLWriter) start(name string) { + if w == nil { + return + } + w.WriteString("<html>") + w.WriteString(`<head> +<style> + +#helplink { + margin-bottom: 15px; + display: block; + margin-top: -15px; +} + +#help { + display: none; +} + +.stats { + font-size: 60%; +} + +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-yellow", + "highlight-aquamarine", + "highlight-coral", + "highlight-lightpink", + "highlight-lightsteelblue", + "highlight-palegreen", + "highlight-lightgray" +]; + +// 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>`) + // TODO: Add javascript click handlers for blocks + // to outline that block across all phases + 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> +Click on a value or block to toggle highlighting of that value/block and its uses. +Values and blocks are highlighted by ID, which may vary across passes. +(TODO: Fix this.) +</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 + } + w.WriteString("</tr>") + w.WriteString("</table>") + w.WriteString("</body>") + w.WriteString("</html>") + w.File.Close() +} + +// 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{}) { + if _, err := fmt.Fprintf(w.File, msg, v...); err != nil { + w.Fatalf(0, "%v", err) + } +} + +func (w *HTMLWriter) WriteString(s string) { + if _, err := w.File.WriteString(s); err != nil { + w.Fatalf(0, "%v", err) + } +} + +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. + return fmt.Sprintf("<span class=\"%[1]s ssa-value\">%[1]s</span>", v.String()) +} + +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()) + ">" + if v.AuxInt != 0 { + s += fmt.Sprintf(" [%d]", v.AuxInt) + } + if v.Aux != nil { + if _, ok := v.Aux.(string); ok { + s += html.EscapeString(fmt.Sprintf(" {%q}", v.Aux)) + } else { + s += html.EscapeString(fmt.Sprintf(" {%v}", v.Aux)) + } + } + for _, a := range v.Args { + s += fmt.Sprintf(" %s", a.HTML()) + } + r := v.Block.Func.RegAlloc + if int(v.ID) < len(r) && r[v.ID] != nil { + s += " : " + r[v.ID].Name() + } + + 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. + return fmt.Sprintf("<span class=\"%[1]s ssa-block\">%[1]s</span>", html.EscapeString(b.String())) +} + +func (b *Block) LongHTML() string { + // TODO: improve this for HTML? + s := fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", html.EscapeString(b.String()), html.EscapeString(b.Kind.String())) + if b.Aux != nil { + s += html.EscapeString(fmt.Sprintf(" {%v}", b.Aux)) + } + if b.Control != nil { + s += fmt.Sprintf(" %s", b.Control.HTML()) + } + if len(b.Succs) > 0 { + s += " →" // right arrow + for _, c := range b.Succs { + s += " " + c.HTML() + } + } + switch b.Likely { + case BranchUnlikely: + s += " (unlikely)" + case BranchLikely: + s += " (likely)" + } + 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 + for _, pred := range b.Preds { + 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>") +} + +func (p htmlFuncPrinter) named(n LocalSlot, vals []*Value) { + // TODO +} |
