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.)
Faded out values and blocks are dead code that has not been eliminated.
Values printed in italics have a dependency cycle.
CFG: 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.
`)
w.WriteString("
")
w.WriteString("
")
}
func (w *HTMLWriter) Close() {
if w == nil {
return
}
io.WriteString(w.w, "
")
io.WriteString(w.w, "
")
io.WriteString(w.w, "")
io.WriteString(w.w, "")
w.w.Close()
fmt.Printf("dumped IR to %v\n", w.path)
}
// WriteFunc writes f in a column headed by title.
// phase is used for collapsing columns and should be unique across the table.
func (w *HTMLWriter) WriteFunc(phase, title string, f *Function) {
if w == nil {
return
}
w.WriteColumn(phase, title, "", funcHTML(f, phase, w.dot))
}
// WriteColumn writes raw HTML in a column headed by title.
// It is intended for pre- and post-compilation log output.
func (w *HTMLWriter) WriteColumn(phase, title, class, html string) {
if w == nil {
return
}
id := strings.Replace(phase, " ", "-", -1)
// collapsed column
w.Printf("
%v
", id, phase)
if class == "" {
w.Printf("
", id)
} else {
w.Printf("
", id, class)
}
w.WriteString("
" + title + "
")
w.WriteString(html)
w.WriteString("
")
}
func (w *HTMLWriter) Printf(msg string, v ...interface{}) {
if _, err := fmt.Fprintf(w.w, msg, v...); err != nil {
log.Fatalf("%v", err)
}
}
func (w *HTMLWriter) WriteString(s string) {
if _, err := io.WriteString(w.w, s); err != nil {
log.Fatalf("%v", err)
}
}
func valueHTML(v Node) string {
if v == nil {
return "<nil>"
}
// 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.
class := fmt.Sprintf("t%d", v.ID())
var label string
switch v := v.(type) {
case *Function:
label = v.RelString(nil)
case *Builtin:
label = v.Name()
default:
label = class
}
return fmt.Sprintf("%s", class, label)
}
func valueLongHTML(v Node) 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("", v.ID())
linenumber := "(?)"
if v.Pos().IsValid() {
line := v.Parent().Prog.Fset.Position(v.Pos()).Line
linenumber = fmt.Sprintf("(%d)", line, line)
}
s += fmt.Sprintf("%s %s = %s", valueHTML(v), linenumber, opName(v))
if v, ok := v.(Value); ok {
s += " <" + html.EscapeString(v.Type().String()) + ">"
}
switch v := v.(type) {
case *Parameter:
s += fmt.Sprintf(" {%s}", html.EscapeString(v.name))
case *BinOp:
s += fmt.Sprintf(" {%s}", html.EscapeString(v.Op.String()))
case *UnOp:
s += fmt.Sprintf(" {%s}", html.EscapeString(v.Op.String()))
case *Extract:
name := v.Tuple.Type().(*types.Tuple).At(v.Index).Name()
s += fmt.Sprintf(" [%d] (%s)", v.Index, name)
case *Field:
st := v.X.Type().Underlying().(*types.Struct)
// Be robust against a bad index.
name := "?"
if 0 <= v.Field && v.Field < st.NumFields() {
name = st.Field(v.Field).Name()
}
s += fmt.Sprintf(" [%d] (%s)", v.Field, name)
case *FieldAddr:
st := deref(v.X.Type()).Underlying().(*types.Struct)
// Be robust against a bad index.
name := "?"
if 0 <= v.Field && v.Field < st.NumFields() {
name = st.Field(v.Field).Name()
}
s += fmt.Sprintf(" [%d] (%s)", v.Field, name)
case *Recv:
s += fmt.Sprintf(" {%t}", v.CommaOk)
case *Call:
if v.Common().IsInvoke() {
s += fmt.Sprintf(" {%s}", html.EscapeString(v.Common().Method.FullName()))
}
case *Const:
if v.Value == nil {
s += " {<nil>}"
} else {
s += fmt.Sprintf(" {%s}", html.EscapeString(v.Value.String()))
}
case *Sigma:
s += fmt.Sprintf(" [#%s]", v.From)
}
for _, a := range v.Operands(nil) {
s += fmt.Sprintf(" %s", valueHTML(*a))
}
// OPT(dh): we're calling namedValues many times on the same function.
allNames := namedValues(v.Parent())
var names []string
for name, values := range allNames {
for _, value := range values {
if v == value {
names = append(names, name.Name())
break
}
}
}
if len(names) != 0 {
s += " (" + strings.Join(names, ", ") + ")"
}
s += ""
return s
}
func blockHTML(b *BasicBlock) 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.
s := html.EscapeString(b.String())
return fmt.Sprintf("%s", s, s)
}
func blockLongHTML(b *BasicBlock) string {
var kind string
var term Instruction
if len(b.Instrs) > 0 {
term = b.Control()
kind = opName(term)
}
// TODO: improve this for HTML?
s := fmt.Sprintf("%s", b.Index, kind)
if term != nil {
ops := term.Operands(nil)
if len(ops) > 0 {
var ss []string
for _, op := range ops {
ss = append(ss, valueHTML(*op))
}
s += " " + strings.Join(ss, ", ")
}
}
if len(b.Succs) > 0 {
s += " →" // right arrow
for _, c := range b.Succs {
s += " " + blockHTML(c)
}
}
return s
}
func funcHTML(f *Function, phase string, dot *dotWriter) string {
buf := new(bytes.Buffer)
if dot != nil {
dot.writeFuncSVG(buf, phase, f)
}
fmt.Fprint(buf, "")
p := htmlFuncPrinter{w: buf}
fprintFunc(p, f)
// fprintFunc(&buf, f) // TODO: HTML, not text, for line breaks, etc.
fmt.Fprint(buf, "")
return buf.String()
}
type htmlFuncPrinter struct {
w io.Writer
}
func (p htmlFuncPrinter) startBlock(b *BasicBlock, reachable bool) {
var dead string
if !reachable {
dead = "dead-block"
}
fmt.Fprintf(p.w, "
", b, dead)
fmt.Fprintf(p.w, "
%s:", blockHTML(b))
if len(b.Preds) > 0 {
io.WriteString(p.w, " ←") // left arrow
for _, pred := range b.Preds {
fmt.Fprintf(p.w, " %s", blockHTML(pred))
}
}
if len(b.Instrs) > 0 {
io.WriteString(p.w, ``)
}
io.WriteString(p.w, "
")
if len(b.Instrs) > 0 { // start list of values
io.WriteString(p.w, "
")
io.WriteString(p.w, "
")
}
}
func (p htmlFuncPrinter) endBlock(b *BasicBlock) {
if len(b.Instrs) > 0 { // end list of values
io.WriteString(p.w, "