diff --git a/src/cmd/compile/fmt_test.go b/src/cmd/compile/fmt_test.go index 6d18d495c3..17716f82f2 100644 --- a/src/cmd/compile/fmt_test.go +++ b/src/cmd/compile/fmt_test.go @@ -229,7 +229,7 @@ func TestFormats(t *testing.T) { } } if mismatch { - t.Errorf("knownFormats is out of date; please run with -v to regenerate") + t.Errorf("knownFormats is out of date; please 'go test -v fmt_test.go > foo', then extract new definition of knownFormats from foo") } } @@ -683,6 +683,7 @@ var knownFormats = map[string]string{ "int32 %d": "", "int32 %v": "", "int32 %x": "", + "int64 %.5d": "", "int64 %+d": "", "int64 %-10d": "", "int64 %X": "", diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index c633ee4c93..69ed613412 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4606,22 +4606,31 @@ func genssa(f *ssa.Func, pp *Progs) { var buf bytes.Buffer buf.WriteString("") buf.WriteString("
") + filename := "" for p := pp.Text; p != nil; p = p.Link { + // Don't spam every line with the file name, which is often huge. + // Only print changes. + if f := p.FileName(); f != filename { + filename = f + buf.WriteString("
") + buf.WriteString(html.EscapeString("# " + filename)) + buf.WriteString("
") + } + buf.WriteString("
") if v, ok := progToValue[p]; ok { buf.WriteString(v.HTML()) } else if b, ok := progToBlock[p]; ok { - buf.WriteString(b.HTML()) + buf.WriteString("" + b.HTML() + "") } buf.WriteString("
") buf.WriteString("
") - buf.WriteString(html.EscapeString(p.String())) + buf.WriteString(fmt.Sprintf("%.5d (%s) %s", p.Pc, p.LineNumber(), html.EscapeString(p.InstructionString()))) buf.WriteString("
") - buf.WriteString("") } buf.WriteString("
") buf.WriteString("
") - f.HTMLWriter.WriteColumn("genssa", buf.String()) + f.HTMLWriter.WriteColumn("genssa", "ssa-prog", buf.String()) // pp.Text.Ctxt.LineHist.PrintFilenameOnly = saved } } diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go index bb87378bdd..47f37f2337 100644 --- a/src/cmd/compile/internal/ssa/html.go +++ b/src/cmd/compile/internal/ssa/html.go @@ -66,6 +66,11 @@ th, td { padding: 5px; } +td.ssa-prog { + width: 600px; + word-wrap: break-word; +} + li { list-style-type: none; } @@ -121,6 +126,11 @@ dd.ssa-prog { font-style: italic; } +.line-number { + font-style: italic; + font-size: 11px; +} + .highlight-yellow { background-color: yellow; } .highlight-aquamarine { background-color: aquamarine; } .highlight-coral { background-color: coral; } @@ -310,17 +320,21 @@ func (w *HTMLWriter) WriteFunc(title string, f *Func) { if w == nil { return // avoid generating HTML just to discard it } - w.WriteColumn(title, f.HTML()) + 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) { +func (w *HTMLWriter) WriteColumn(title, class, html string) { if w == nil { return } - w.WriteString("") + if class == "" { + w.WriteString("") + } else { + w.WriteString("") + } w.WriteString("

" + title + "

") w.WriteString(html) w.WriteString("") @@ -353,7 +367,14 @@ func (v *Value) LongHTML() string { // We already have visual noise in the form of punctuation // maybe we could replace some of that with formatting. s := fmt.Sprintf("", v.String()) - s += fmt.Sprintf("%s = %s", v.HTML(), v.Op.String()) + + linenumber := "(?)" + if v.Pos.IsKnown() { + linenumber = fmt.Sprintf("(%d)", v.Pos.Line()) + } + + s += fmt.Sprintf("%s %s = %s", v.HTML(), linenumber, v.Op.String()) + s += " <" + html.EscapeString(v.Type.String()) + ">" s += html.EscapeString(v.auxString()) for _, a := range v.Args { @@ -375,6 +396,7 @@ func (v *Value) LongHTML() string { if len(names) != 0 { s += " (" + strings.Join(names, ", ") + ")" } + s += "" return s } @@ -409,6 +431,11 @@ func (b *Block) LongHTML() string { case BranchLikely: s += " (likely)" } + 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. + s += fmt.Sprintf(" (line %d)", b.Pos.Line()) + } return s } diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go index bf2d209d7f..67c74c2f89 100644 --- a/src/cmd/internal/obj/util.go +++ b/src/cmd/internal/obj/util.go @@ -13,10 +13,34 @@ import ( const REG_NONE = 0 +// Line returns a string containing the filename and line number for p func (p *Prog) Line() string { return p.Ctxt.OutermostPos(p.Pos).Format(false) } +// LineNumber returns a string containing the line number for p's position +func (p *Prog) LineNumber() string { + pos := p.Ctxt.OutermostPos(p.Pos) + if !pos.IsKnown() { + return "?" + } + return fmt.Sprintf("%d", pos.Line()) +} + +// FileName returns a string containing the filename for p's position +func (p *Prog) FileName() string { + // TODO LineNumber and FileName cases don't handle full generality of positions, + // but because these are currently used only for GOSSAFUNC debugging output, that + // is okay. The intent is that "LineNumber()" yields the rapidly varying part, + // while "FileName()" yields the longer and slightly more constant material. + pos := p.Ctxt.OutermostPos(p.Pos) + if !pos.IsKnown() { + return "" + } + + return pos.Filename() +} + var armCondCode = []string{ ".EQ", ".NE", @@ -72,6 +96,18 @@ func (p *Prog) String() string { if p == nil { return "" } + if p.Ctxt == nil { + return "" + } + return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.Line(), p.InstructionString()) +} + +// InstructionString returns a string representation of the instruction without preceding +// program counter or file and line number. +func (p *Prog) InstructionString() string { + if p == nil { + return "" + } if p.Ctxt == nil { return "" @@ -81,7 +117,7 @@ func (p *Prog) String() string { var buf bytes.Buffer - fmt.Fprintf(&buf, "%.5d (%v)\t%v%s", p.Pc, p.Line(), p.As, sc) + fmt.Fprintf(&buf, "%v%s", p.As, sc) sep := "\t" if p.From.Type != TYPE_NONE {