diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index feb3c8556a..fb5a413b84 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -893,6 +893,10 @@ func mkinlcall1(n, fn *Node, maxCost int32) *Node { fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n) } + if ssaDump != "" && ssaDump == Curfn.funcname() { + ssaDumpInlined = append(ssaDumpInlined, fn) + } + ninit := n.Ninit // Make temp names to use instead of the originals. diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 2abd9448d4..bbd2a668a5 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -27,6 +27,9 @@ var ssaDump string // early copy of $GOSSAFUNC; the func name to dump output var ssaDumpStdout bool // whether to dump to stdout const ssaDumpFile = "ssa.html" +// ssaDumpInlined holds all inlined functions when ssaDump contains a function name. +var ssaDumpInlined []*Node + func initssaconfig() { types_ := ssa.NewTypes() @@ -147,27 +150,7 @@ func buildssa(fn *Node, worker int) *ssa.Func { if printssa { s.f.HTMLWriter = ssa.NewHTMLWriter(ssaDumpFile, s.f.Frontend(), name) // TODO: generate and print a mapping from nodes to values and blocks - - // Read sources for a function fn and format into a column. - fname := Ctxt.PosTable.Pos(fn.Pos).Filename() - f, err := os.Open(fname) - if err != nil { - s.f.HTMLWriter.Logger.Logf("skipping sources column: %v", err) - } else { - defer f.Close() - firstLn := fn.Pos.Line() - 1 - lastLn := fn.Func.Endlineno.Line() - var lines []string - ln := uint(0) - scanner := bufio.NewScanner(f) - for scanner.Scan() && ln < lastLn { - if ln >= firstLn { - lines = append(lines, scanner.Text()) - } - ln++ - } - s.f.HTMLWriter.WriteSources("sources", fname, firstLn+1, lines) - } + dumpSourcesColumn(s.f.HTMLWriter, fn) } // Allocate starting block @@ -239,6 +222,59 @@ func buildssa(fn *Node, worker int) *ssa.Func { return s.f } +func dumpSourcesColumn(writer *ssa.HTMLWriter, fn *Node) { + // Read sources of target function fn. + fname := Ctxt.PosTable.Pos(fn.Pos).Filename() + targetFn, err := readFuncLines(fname, fn.Pos.Line(), fn.Func.Endlineno.Line()) + if err != nil { + writer.Logger.Logf("cannot read sources for function %v: %v", fn, err) + } + + // Read sources of inlined functions. + var inlFns []*ssa.FuncLines + for _, fi := range ssaDumpInlined { + var elno src.XPos + if fi.Name.Defn == nil { + // Endlineno is filled from exported data. + elno = fi.Func.Endlineno + } else { + elno = fi.Name.Defn.Func.Endlineno + } + fname := Ctxt.PosTable.Pos(fi.Pos).Filename() + fnLines, err := readFuncLines(fname, fi.Pos.Line(), elno.Line()) + if err != nil { + writer.Logger.Logf("cannot read sources for function %v: %v", fi, err) + continue + } + inlFns = append(inlFns, fnLines) + } + + sort.Sort(ssa.ByTopo(inlFns)) + if targetFn != nil { + inlFns = append([]*ssa.FuncLines{targetFn}, inlFns...) + } + + writer.WriteSources("sources", inlFns) +} + +func readFuncLines(file string, start, end uint) (*ssa.FuncLines, error) { + f, err := os.Open(os.ExpandEnv(file)) + if err != nil { + return nil, err + } + defer f.Close() + var lines []string + ln := uint(1) + scanner := bufio.NewScanner(f) + for scanner.Scan() && ln <= end { + if ln >= start { + lines = append(lines, scanner.Text()) + } + ln++ + } + return &ssa.FuncLines{Filename: file, StartLineno: start, Lines: lines}, nil +} + // updateUnsetPredPos propagates the earliest-value position information for b // towards all of b's predecessors that need a position, and recurs on that // predecessor if its position is updated. B should have a non-empty position. diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go index 2e48e8105b..6943e5ef40 100644 --- a/src/cmd/compile/internal/ssa/html.go +++ b/src/cmd/compile/internal/ssa/html.go @@ -458,25 +458,70 @@ func (w *HTMLWriter) WriteFunc(phase, title string, f *Func) { // TODO: Add visual representation of f's CFG. } +// FuncLines contains source code for a function to be displayed +// in sources column. +type FuncLines struct { + Filename string + StartLineno uint + Lines []string +} + +// ByTopo sorts topologically: target function is on top, +// followed by inlined functions sorted by filename and line numbers. +type ByTopo []*FuncLines + +func (x ByTopo) Len() int { return len(x) } +func (x ByTopo) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x ByTopo) Less(i, j int) bool { + a := x[i] + b := x[j] + if a.Filename == a.Filename { + return a.StartLineno < b.StartLineno + } + return a.Filename < b.Filename +} + // WriteSources writes lines as source code in a column headed by title. // phase is used for collapsing columns and should be unique across the table. -func (w *HTMLWriter) WriteSources(phase, title string, firstLineno uint, lines []string) { +func (w *HTMLWriter) WriteSources(phase string, all []*FuncLines) { if w == nil { return // avoid generating HTML just to discard it } var buf bytes.Buffer fmt.Fprint(&buf, "
") - for i, _ := range lines { - ln := int(firstLineno) + i - fmt.Fprintf(&buf, "
%v
", ln, ln) + filename := "" + for _, fl := range all { + fmt.Fprint(&buf, "
 
") + if filename != fl.Filename { + fmt.Fprint(&buf, "
 
") + filename = fl.Filename + } + for i := range fl.Lines { + ln := int(fl.StartLineno) + i + fmt.Fprintf(&buf, "
%v
", ln, ln) + } } fmt.Fprint(&buf, "
")
-	for i, l := range lines {
-		ln := int(firstLineno) + i
-		fmt.Fprintf(&buf, "
%v
", ln, html.EscapeString(l)) + filename = "" + for _, fl := range all { + fmt.Fprint(&buf, "
 
") + if filename != fl.Filename { + fmt.Fprintf(&buf, "
%v
", fl.Filename) + filename = fl.Filename + } + for i, line := range fl.Lines { + ln := int(fl.StartLineno) + i + var escaped string + if strings.TrimSpace(line) == "" { + escaped = " " + } else { + escaped = html.EscapeString(line) + } + fmt.Fprintf(&buf, "
%v
", ln, escaped) + } } fmt.Fprint(&buf, "
") - w.WriteColumn(phase, title, "", buf.String()) + w.WriteColumn(phase, phase, "", buf.String()) } // WriteColumn writes raw HTML in a column headed by title.