github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/src/cmd/compile/internal/ssa/html.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ssa 6 7 import ( 8 "bytes" 9 "cmd/internal/src" 10 "fmt" 11 "html" 12 "io" 13 "os" 14 ) 15 16 type HTMLWriter struct { 17 Logger 18 *os.File 19 } 20 21 func NewHTMLWriter(path string, logger Logger, funcname string) *HTMLWriter { 22 out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 23 if err != nil { 24 logger.Fatalf(src.NoXPos, "%v", err) 25 } 26 html := HTMLWriter{File: out, Logger: logger} 27 html.start(funcname) 28 return &html 29 } 30 31 func (w *HTMLWriter) start(name string) { 32 if w == nil { 33 return 34 } 35 w.WriteString("<html>") 36 w.WriteString(`<head> 37 <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> 38 <style> 39 40 #helplink { 41 margin-bottom: 15px; 42 display: block; 43 margin-top: -15px; 44 } 45 46 #help { 47 display: none; 48 } 49 50 .stats { 51 font-size: 60%; 52 } 53 54 table { 55 border: 1px solid black; 56 table-layout: fixed; 57 width: 300px; 58 } 59 60 th, td { 61 border: 1px solid black; 62 overflow: hidden; 63 width: 400px; 64 vertical-align: top; 65 padding: 5px; 66 } 67 68 li { 69 list-style-type: none; 70 } 71 72 li.ssa-long-value { 73 text-indent: -2em; /* indent wrapped lines */ 74 } 75 76 li.ssa-value-list { 77 display: inline; 78 } 79 80 li.ssa-start-block { 81 padding: 0; 82 margin: 0; 83 } 84 85 li.ssa-end-block { 86 padding: 0; 87 margin: 0; 88 } 89 90 ul.ssa-print-func { 91 padding-left: 0; 92 } 93 94 dl.ssa-gen { 95 padding-left: 0; 96 } 97 98 dt.ssa-prog-src { 99 padding: 0; 100 margin: 0; 101 float: left; 102 width: 4em; 103 } 104 105 dd.ssa-prog { 106 padding: 0; 107 margin-right: 0; 108 margin-left: 4em; 109 } 110 111 .dead-value { 112 color: gray; 113 } 114 115 .dead-block { 116 opacity: 0.5; 117 } 118 119 .depcycle { 120 font-style: italic; 121 } 122 123 .highlight-yellow { background-color: yellow; } 124 .highlight-aquamarine { background-color: aquamarine; } 125 .highlight-coral { background-color: coral; } 126 .highlight-lightpink { background-color: lightpink; } 127 .highlight-lightsteelblue { background-color: lightsteelblue; } 128 .highlight-palegreen { background-color: palegreen; } 129 .highlight-powderblue { background-color: powderblue; } 130 .highlight-lightgray { background-color: lightgray; } 131 132 .outline-blue { outline: blue solid 2px; } 133 .outline-red { outline: red solid 2px; } 134 .outline-blueviolet { outline: blueviolet solid 2px; } 135 .outline-darkolivegreen { outline: darkolivegreen solid 2px; } 136 .outline-fuchsia { outline: fuchsia solid 2px; } 137 .outline-sienna { outline: sienna solid 2px; } 138 .outline-gold { outline: gold solid 2px; } 139 140 </style> 141 142 <script type="text/javascript"> 143 // ordered list of all available highlight colors 144 var highlights = [ 145 "highlight-yellow", 146 "highlight-aquamarine", 147 "highlight-coral", 148 "highlight-lightpink", 149 "highlight-lightsteelblue", 150 "highlight-palegreen", 151 "highlight-lightgray" 152 ]; 153 154 // state: which value is highlighted this color? 155 var highlighted = {}; 156 for (var i = 0; i < highlights.length; i++) { 157 highlighted[highlights[i]] = ""; 158 } 159 160 // ordered list of all available outline colors 161 var outlines = [ 162 "outline-blue", 163 "outline-red", 164 "outline-blueviolet", 165 "outline-darkolivegreen", 166 "outline-fuchsia", 167 "outline-sienna", 168 "outline-gold" 169 ]; 170 171 // state: which value is outlined this color? 172 var outlined = {}; 173 for (var i = 0; i < outlines.length; i++) { 174 outlined[outlines[i]] = ""; 175 } 176 177 window.onload = function() { 178 var ssaElemClicked = function(elem, event, selections, selected) { 179 event.stopPropagation() 180 181 // TODO: pushState with updated state and read it on page load, 182 // so that state can survive across reloads 183 184 // find all values with the same name 185 var c = elem.classList.item(0); 186 var x = document.getElementsByClassName(c); 187 188 // if selected, remove selections from all of them 189 // otherwise, attempt to add 190 191 var remove = ""; 192 for (var i = 0; i < selections.length; i++) { 193 var color = selections[i]; 194 if (selected[color] == c) { 195 remove = color; 196 break; 197 } 198 } 199 200 if (remove != "") { 201 for (var i = 0; i < x.length; i++) { 202 x[i].classList.remove(remove); 203 } 204 selected[remove] = ""; 205 return; 206 } 207 208 // we're adding a selection 209 // find first available color 210 var avail = ""; 211 for (var i = 0; i < selections.length; i++) { 212 var color = selections[i]; 213 if (selected[color] == "") { 214 avail = color; 215 break; 216 } 217 } 218 if (avail == "") { 219 alert("out of selection colors; go add more"); 220 return; 221 } 222 223 // set that as the selection 224 for (var i = 0; i < x.length; i++) { 225 x[i].classList.add(avail); 226 } 227 selected[avail] = c; 228 }; 229 230 var ssaValueClicked = function(event) { 231 ssaElemClicked(this, event, highlights, highlighted); 232 } 233 234 var ssaBlockClicked = function(event) { 235 ssaElemClicked(this, event, outlines, outlined); 236 } 237 238 var ssavalues = document.getElementsByClassName("ssa-value"); 239 for (var i = 0; i < ssavalues.length; i++) { 240 ssavalues[i].addEventListener('click', ssaValueClicked); 241 } 242 243 var ssalongvalues = document.getElementsByClassName("ssa-long-value"); 244 for (var i = 0; i < ssalongvalues.length; i++) { 245 // don't attach listeners to li nodes, just the spans they contain 246 if (ssalongvalues[i].nodeName == "SPAN") { 247 ssalongvalues[i].addEventListener('click', ssaValueClicked); 248 } 249 } 250 251 var ssablocks = document.getElementsByClassName("ssa-block"); 252 for (var i = 0; i < ssablocks.length; i++) { 253 ssablocks[i].addEventListener('click', ssaBlockClicked); 254 } 255 }; 256 257 function toggle_visibility(id) { 258 var e = document.getElementById(id); 259 if(e.style.display == 'block') 260 e.style.display = 'none'; 261 else 262 e.style.display = 'block'; 263 } 264 </script> 265 266 </head>`) 267 // TODO: Add javascript click handlers for blocks 268 // to outline that block across all phases 269 w.WriteString("<body>") 270 w.WriteString("<h1>") 271 w.WriteString(html.EscapeString(name)) 272 w.WriteString("</h1>") 273 w.WriteString(` 274 <a href="#" onclick="toggle_visibility('help');" id="helplink">help</a> 275 <div id="help"> 276 277 <p> 278 Click on a value or block to toggle highlighting of that value/block 279 and its uses. (Values and blocks are highlighted by ID, and IDs of 280 dead items may be reused, so not all highlights necessarily correspond 281 to the clicked item.) 282 </p> 283 284 <p> 285 Faded out values and blocks are dead code that has not been eliminated. 286 </p> 287 288 <p> 289 Values printed in italics have a dependency cycle. 290 </p> 291 292 </div> 293 `) 294 w.WriteString("<table>") 295 w.WriteString("<tr>") 296 } 297 298 func (w *HTMLWriter) Close() { 299 if w == nil { 300 return 301 } 302 w.WriteString("</tr>") 303 w.WriteString("</table>") 304 w.WriteString("</body>") 305 w.WriteString("</html>") 306 w.File.Close() 307 } 308 309 // WriteFunc writes f in a column headed by title. 310 func (w *HTMLWriter) WriteFunc(title string, f *Func) { 311 if w == nil { 312 return // avoid generating HTML just to discard it 313 } 314 w.WriteColumn(title, f.HTML()) 315 // TODO: Add visual representation of f's CFG. 316 } 317 318 // WriteColumn writes raw HTML in a column headed by title. 319 // It is intended for pre- and post-compilation log output. 320 func (w *HTMLWriter) WriteColumn(title string, html string) { 321 if w == nil { 322 return 323 } 324 w.WriteString("<td>") 325 w.WriteString("<h2>" + title + "</h2>") 326 w.WriteString(html) 327 w.WriteString("</td>") 328 } 329 330 func (w *HTMLWriter) Printf(msg string, v ...interface{}) { 331 if _, err := fmt.Fprintf(w.File, msg, v...); err != nil { 332 w.Fatalf(src.NoXPos, "%v", err) 333 } 334 } 335 336 func (w *HTMLWriter) WriteString(s string) { 337 if _, err := w.File.WriteString(s); err != nil { 338 w.Fatalf(src.NoXPos, "%v", err) 339 } 340 } 341 342 func (v *Value) HTML() string { 343 // TODO: Using the value ID as the class ignores the fact 344 // that value IDs get recycled and that some values 345 // are transmuted into other values. 346 s := v.String() 347 return fmt.Sprintf("<span class=\"%s ssa-value\">%s</span>", s, s) 348 } 349 350 func (v *Value) LongHTML() string { 351 // TODO: Any intra-value formatting? 352 // I'm wary of adding too much visual noise, 353 // but a little bit might be valuable. 354 // We already have visual noise in the form of punctuation 355 // maybe we could replace some of that with formatting. 356 s := fmt.Sprintf("<span class=\"%s ssa-long-value\">", v.String()) 357 s += fmt.Sprintf("%s = %s", v.HTML(), v.Op.String()) 358 s += " <" + html.EscapeString(v.Type.String()) + ">" 359 s += html.EscapeString(v.auxString()) 360 for _, a := range v.Args { 361 s += fmt.Sprintf(" %s", a.HTML()) 362 } 363 r := v.Block.Func.RegAlloc 364 if int(v.ID) < len(r) && r[v.ID] != nil { 365 s += " : " + html.EscapeString(r[v.ID].Name()) 366 } 367 s += "</span>" 368 return s 369 } 370 371 func (b *Block) HTML() string { 372 // TODO: Using the value ID as the class ignores the fact 373 // that value IDs get recycled and that some values 374 // are transmuted into other values. 375 s := html.EscapeString(b.String()) 376 return fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", s, s) 377 } 378 379 func (b *Block) LongHTML() string { 380 // TODO: improve this for HTML? 381 s := fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", html.EscapeString(b.String()), html.EscapeString(b.Kind.String())) 382 if b.Aux != nil { 383 s += html.EscapeString(fmt.Sprintf(" {%v}", b.Aux)) 384 } 385 if b.Control != nil { 386 s += fmt.Sprintf(" %s", b.Control.HTML()) 387 } 388 if len(b.Succs) > 0 { 389 s += " →" // right arrow 390 for _, e := range b.Succs { 391 c := e.b 392 s += " " + c.HTML() 393 } 394 } 395 switch b.Likely { 396 case BranchUnlikely: 397 s += " (unlikely)" 398 case BranchLikely: 399 s += " (likely)" 400 } 401 return s 402 } 403 404 func (f *Func) HTML() string { 405 var buf bytes.Buffer 406 fmt.Fprint(&buf, "<code>") 407 p := htmlFuncPrinter{w: &buf} 408 fprintFunc(p, f) 409 410 // fprintFunc(&buf, f) // TODO: HTML, not text, <br /> for line breaks, etc. 411 fmt.Fprint(&buf, "</code>") 412 return buf.String() 413 } 414 415 type htmlFuncPrinter struct { 416 w io.Writer 417 } 418 419 func (p htmlFuncPrinter) header(f *Func) {} 420 421 func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) { 422 // TODO: Make blocks collapsable? 423 var dead string 424 if !reachable { 425 dead = "dead-block" 426 } 427 fmt.Fprintf(p.w, "<ul class=\"%s ssa-print-func %s\">", b, dead) 428 fmt.Fprintf(p.w, "<li class=\"ssa-start-block\">%s:", b.HTML()) 429 if len(b.Preds) > 0 { 430 io.WriteString(p.w, " ←") // left arrow 431 for _, e := range b.Preds { 432 pred := e.b 433 fmt.Fprintf(p.w, " %s", pred.HTML()) 434 } 435 } 436 io.WriteString(p.w, "</li>") 437 if len(b.Values) > 0 { // start list of values 438 io.WriteString(p.w, "<li class=\"ssa-value-list\">") 439 io.WriteString(p.w, "<ul>") 440 } 441 } 442 443 func (p htmlFuncPrinter) endBlock(b *Block) { 444 if len(b.Values) > 0 { // end list of values 445 io.WriteString(p.w, "</ul>") 446 io.WriteString(p.w, "</li>") 447 } 448 io.WriteString(p.w, "<li class=\"ssa-end-block\">") 449 fmt.Fprint(p.w, b.LongHTML()) 450 io.WriteString(p.w, "</li>") 451 io.WriteString(p.w, "</ul>") 452 // io.WriteString(p.w, "</span>") 453 } 454 455 func (p htmlFuncPrinter) value(v *Value, live bool) { 456 var dead string 457 if !live { 458 dead = "dead-value" 459 } 460 fmt.Fprintf(p.w, "<li class=\"ssa-long-value %s\">", dead) 461 fmt.Fprint(p.w, v.LongHTML()) 462 io.WriteString(p.w, "</li>") 463 } 464 465 func (p htmlFuncPrinter) startDepCycle() { 466 fmt.Fprintln(p.w, "<span class=\"depcycle\">") 467 } 468 469 func (p htmlFuncPrinter) endDepCycle() { 470 fmt.Fprintln(p.w, "</span>") 471 } 472 473 func (p htmlFuncPrinter) named(n LocalSlot, vals []*Value) { 474 // TODO 475 }