diff --git a/go/pointer/gen.go b/go/pointer/gen.go index bfb3bf99..a9e18429 100644 --- a/go/pointer/gen.go +++ b/go/pointer/gen.go @@ -1141,9 +1141,9 @@ func (a *analysis) genFunc(cgn *cgnode) { fn2 := *cgn.fn // copy fn2.Locals = nil fn2.Blocks = nil - fn2.DumpTo(a.log) + fn2.WriteTo(a.log) } else { - cgn.fn.DumpTo(a.log) + cgn.fn.WriteTo(a.log) } } diff --git a/go/ssa/blockopt.go b/go/ssa/blockopt.go index 0253b350..64cfbeb8 100644 --- a/go/ssa/blockopt.go +++ b/go/ssa/blockopt.go @@ -152,7 +152,7 @@ func optimizeBlocks(f *Function) { changed = false if debugBlockOpt { - f.DumpTo(os.Stderr) + f.WriteTo(os.Stderr) mustSanityCheck(f, nil) } diff --git a/go/ssa/create.go b/go/ssa/create.go index 4abbad2e..81f0d668 100644 --- a/go/ssa/create.go +++ b/go/ssa/create.go @@ -232,7 +232,7 @@ func (prog *Program) CreatePackage(info *loader.PackageInfo) *Package { } if prog.mode&LogPackages != 0 { - p.DumpTo(os.Stderr) + p.WriteTo(os.Stderr) } if info.Importable { diff --git a/go/ssa/dom.go b/go/ssa/dom.go index 33d1cfae..12ef4308 100644 --- a/go/ssa/dom.go +++ b/go/ssa/dom.go @@ -18,8 +18,8 @@ package ssa // to avoid the need for buckets of size > 1. import ( + "bytes" "fmt" - "io" "math/big" "os" "sort" @@ -310,32 +310,32 @@ func sanityCheckDomTree(f *Function) { // Printing functions ---------------------------------------- // printDomTree prints the dominator tree as text, using indentation. -func printDomTreeText(w io.Writer, v *BasicBlock, indent int) { - fmt.Fprintf(w, "%*s%s\n", 4*indent, "", v) +func printDomTreeText(buf *bytes.Buffer, v *BasicBlock, indent int) { + fmt.Fprintf(buf, "%*s%s\n", 4*indent, "", v) for _, child := range v.dom.children { - printDomTreeText(w, child, indent+1) + printDomTreeText(buf, child, indent+1) } } // printDomTreeDot prints the dominator tree of f in AT&T GraphViz // (.dot) format. -func printDomTreeDot(w io.Writer, f *Function) { - fmt.Fprintln(w, "//", f) - fmt.Fprintln(w, "digraph domtree {") +func printDomTreeDot(buf *bytes.Buffer, f *Function) { + fmt.Fprintln(buf, "//", f) + fmt.Fprintln(buf, "digraph domtree {") for i, b := range f.Blocks { v := b.dom - fmt.Fprintf(w, "\tn%d [label=\"%s (%d, %d)\",shape=\"rectangle\"];\n", v.pre, b, v.pre, v.post) + fmt.Fprintf(buf, "\tn%d [label=\"%s (%d, %d)\",shape=\"rectangle\"];\n", v.pre, b, v.pre, v.post) // TODO(adonovan): improve appearance of edges // belonging to both dominator tree and CFG. // Dominator tree edge. if i != 0 { - fmt.Fprintf(w, "\tn%d -> n%d [style=\"solid\",weight=100];\n", v.idom.dom.pre, v.pre) + fmt.Fprintf(buf, "\tn%d -> n%d [style=\"solid\",weight=100];\n", v.idom.dom.pre, v.pre) } // CFG edges. for _, pred := range b.Preds { - fmt.Fprintf(w, "\tn%d -> n%d [style=\"dotted\",weight=0];\n", pred.dom.pre, v.pre) + fmt.Fprintf(buf, "\tn%d -> n%d [style=\"dotted\",weight=0];\n", pred.dom.pre, v.pre) } } - fmt.Fprintln(w, "}") + fmt.Fprintln(buf, "}") } diff --git a/go/ssa/example_test.go b/go/ssa/example_test.go index 985a5219..71ec90ca 100644 --- a/go/ssa/example_test.go +++ b/go/ssa/example_test.go @@ -64,14 +64,14 @@ func main() { mainPkg := prog.Package(iprog.Created[0].Pkg) // Print out the package. - mainPkg.DumpTo(os.Stdout) + mainPkg.WriteTo(os.Stdout) // Build SSA code for bodies of functions in mainPkg. mainPkg.Build() // Print out the package-level functions. - mainPkg.Func("init").DumpTo(os.Stdout) - mainPkg.Func("main").DumpTo(os.Stdout) + mainPkg.Func("init").WriteTo(os.Stdout) + mainPkg.Func("main").WriteTo(os.Stdout) // Output: // diff --git a/go/ssa/func.go b/go/ssa/func.go index fea2bf3b..6af67a32 100644 --- a/go/ssa/func.go +++ b/go/ssa/func.go @@ -330,7 +330,7 @@ func (f *Function) finishBody() { if f.Prog.mode&NaiveForm == 0 { // For debugging pre-state of lifting pass: // numberRegisters(f) - // f.DumpTo(os.Stderr) + // f.WriteTo(os.Stderr) lift(f) } @@ -339,7 +339,7 @@ func (f *Function) finishBody() { numberRegisters(f) if f.Prog.mode&LogFunctions != 0 { - f.DumpTo(os.Stderr) + f.WriteTo(os.Stderr) } if f.Prog.mode&SanityCheckFunctions != 0 { @@ -498,22 +498,20 @@ func (f *Function) RelString(from *types.Package) string { return f.name } -// writeSignature writes to w the signature sig in declaration syntax. -func writeSignature(w io.Writer, pkg *types.Package, name string, sig *types.Signature, params []*Parameter) { - io.WriteString(w, "func ") +// writeSignature writes to buf the signature sig in declaration syntax. +func writeSignature(buf *bytes.Buffer, pkg *types.Package, name string, sig *types.Signature, params []*Parameter) { + buf.WriteString("func ") if recv := sig.Recv(); recv != nil { - io.WriteString(w, "(") + buf.WriteString("(") if n := params[0].Name(); n != "" { - io.WriteString(w, n) - io.WriteString(w, " ") + buf.WriteString(n) + buf.WriteString(" ") } - io.WriteString(w, relType(params[0].Type(), pkg)) - io.WriteString(w, ") ") + buf.WriteString(relType(params[0].Type(), pkg)) + buf.WriteString(") ") } - io.WriteString(w, name) - var sigbuf bytes.Buffer - types.WriteSignature(&sigbuf, pkg, sig) - w.Write(sigbuf.Bytes()) + buf.WriteString(name) + types.WriteSignature(buf, pkg, sig) } func (f *Function) pkgobj() *types.Package { @@ -523,49 +521,56 @@ func (f *Function) pkgobj() *types.Package { return nil } -// DumpTo prints to w a human readable "disassembly" of the SSA code of -// all basic blocks of function f. -// -func (f *Function) DumpTo(w io.Writer) { - fmt.Fprintf(w, "# Name: %s\n", f.String()) +var _ io.WriterTo = (*Function)(nil) // *Function implements io.Writer + +func (f *Function) WriteTo(w io.Writer) (int64, error) { + var buf bytes.Buffer + WriteFunction(&buf, f) + n, err := w.Write(buf.Bytes()) + return int64(n), err +} + +// WriteFunction writes to buf a human-readable "disassembly" of f. +func WriteFunction(buf *bytes.Buffer, f *Function) { + fmt.Fprintf(buf, "# Name: %s\n", f.String()) if f.Pkg != nil { - fmt.Fprintf(w, "# Package: %s\n", f.Pkg.Object.Path()) + fmt.Fprintf(buf, "# Package: %s\n", f.Pkg.Object.Path()) } if syn := f.Synthetic; syn != "" { - fmt.Fprintln(w, "# Synthetic:", syn) + fmt.Fprintln(buf, "# Synthetic:", syn) } if pos := f.Pos(); pos.IsValid() { - fmt.Fprintf(w, "# Location: %s\n", f.Prog.Fset.Position(pos)) + fmt.Fprintf(buf, "# Location: %s\n", f.Prog.Fset.Position(pos)) } if f.Enclosing != nil { - fmt.Fprintf(w, "# Parent: %s\n", f.Enclosing.Name()) + fmt.Fprintf(buf, "# Parent: %s\n", f.Enclosing.Name()) } if f.Recover != nil { - fmt.Fprintf(w, "# Recover: %s\n", f.Recover) + fmt.Fprintf(buf, "# Recover: %s\n", f.Recover) } pkgobj := f.pkgobj() if f.FreeVars != nil { - io.WriteString(w, "# Free variables:\n") + buf.WriteString("# Free variables:\n") for i, fv := range f.FreeVars { - fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), pkgobj)) + fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), pkgobj)) } } if len(f.Locals) > 0 { - io.WriteString(w, "# Locals:\n") + buf.WriteString("# Locals:\n") for i, l := range f.Locals { - fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), pkgobj)) + fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), pkgobj)) } } - writeSignature(w, pkgobj, f.Name(), f.Signature, f.Params) - io.WriteString(w, ":\n") + writeSignature(buf, pkgobj, f.Name(), f.Signature, f.Params) + buf.WriteString(":\n") if f.Blocks == nil { - io.WriteString(w, "\t(external)\n") + buf.WriteString("\t(external)\n") } // NB. column calculations are confused by non-ASCII characters. @@ -573,42 +578,42 @@ func (f *Function) DumpTo(w io.Writer) { for _, b := range f.Blocks { if b == nil { // Corrupt CFG. - fmt.Fprintf(w, ".nil:\n") + fmt.Fprintf(buf, ".nil:\n") continue } - n, _ := fmt.Fprintf(w, ".%s:", b) - fmt.Fprintf(w, "%*sP:%d S:%d\n", punchcard-1-n-len("P:n S:n"), "", len(b.Preds), len(b.Succs)) + n, _ := fmt.Fprintf(buf, ".%s:", b) + fmt.Fprintf(buf, "%*sP:%d S:%d\n", punchcard-1-n-len("P:n S:n"), "", len(b.Preds), len(b.Succs)) if false { // CFG debugging - fmt.Fprintf(w, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs) + fmt.Fprintf(buf, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs) } for _, instr := range b.Instrs { - io.WriteString(w, "\t") + buf.WriteString("\t") switch v := instr.(type) { case Value: l := punchcard // Left-align the instruction. if name := v.Name(); name != "" { - n, _ := fmt.Fprintf(w, "%s = ", name) + n, _ := fmt.Fprintf(buf, "%s = ", name) l -= n } // TODO(adonovan): append instructions directly to w. - n, _ := io.WriteString(w, instr.String()) + n, _ := buf.WriteString(instr.String()) l -= n // Right-align the type. if t := v.Type(); t != nil { - fmt.Fprintf(w, " %*s", l-10, relType(t, pkgobj)) + fmt.Fprintf(buf, " %*s", l-10, relType(t, pkgobj)) } case nil: // Be robust against bad transforms. - io.WriteString(w, "") + buf.WriteString("") default: - io.WriteString(w, instr.String()) + buf.WriteString(instr.String()) } - io.WriteString(w, "\n") + buf.WriteString("\n") } } - fmt.Fprintf(w, "\n") + fmt.Fprintf(buf, "\n") } // newBasicBlock adds to f a new basic block and returns it. It does diff --git a/go/ssa/interp/value.go b/go/ssa/interp/value.go index 46e2593d..eb87b008 100644 --- a/go/ssa/interp/value.go +++ b/go/ssa/interp/value.go @@ -352,109 +352,109 @@ func copyVal(v value) value { // Prints in the style of built-in println. // (More or less; in gc println is actually a compiler intrinsic and // can distinguish println(1) from println(interface{}(1)).) -func toWriter(w io.Writer, v value) { +func writeValue(buf *bytes.Buffer, v value) { switch v := v.(type) { case nil, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, complex64, complex128, string: - fmt.Fprintf(w, "%v", v) + fmt.Fprintf(buf, "%v", v) case map[value]value: - io.WriteString(w, "map[") + buf.WriteString("map[") sep := "" for k, e := range v { - io.WriteString(w, sep) + buf.WriteString(sep) sep = " " - toWriter(w, k) - io.WriteString(w, ":") - toWriter(w, e) + writeValue(buf, k) + buf.WriteString(":") + writeValue(buf, e) } - io.WriteString(w, "]") + buf.WriteString("]") case *hashmap: - io.WriteString(w, "map[") + buf.WriteString("map[") sep := " " for _, e := range v.table { for e != nil { - io.WriteString(w, sep) + buf.WriteString(sep) sep = " " - toWriter(w, e.key) - io.WriteString(w, ":") - toWriter(w, e.value) + writeValue(buf, e.key) + buf.WriteString(":") + writeValue(buf, e.value) e = e.next } } - io.WriteString(w, "]") + buf.WriteString("]") case chan value: - fmt.Fprintf(w, "%v", v) // (an address) + fmt.Fprintf(buf, "%v", v) // (an address) case *value: if v == nil { - io.WriteString(w, "") + buf.WriteString("") } else { - fmt.Fprintf(w, "%p", v) + fmt.Fprintf(buf, "%p", v) } case iface: - fmt.Fprintf(w, "(%s, ", v.t) - toWriter(w, v.v) - io.WriteString(w, ")") + fmt.Fprintf(buf, "(%s, ", v.t) + writeValue(buf, v.v) + buf.WriteString(")") case structure: - io.WriteString(w, "{") + buf.WriteString("{") for i, e := range v { if i > 0 { - io.WriteString(w, " ") + buf.WriteString(" ") } - toWriter(w, e) + writeValue(buf, e) } - io.WriteString(w, "}") + buf.WriteString("}") case array: - io.WriteString(w, "[") + buf.WriteString("[") for i, e := range v { if i > 0 { - io.WriteString(w, " ") + buf.WriteString(" ") } - toWriter(w, e) + writeValue(buf, e) } - io.WriteString(w, "]") + buf.WriteString("]") case []value: - io.WriteString(w, "[") + buf.WriteString("[") for i, e := range v { if i > 0 { - io.WriteString(w, " ") + buf.WriteString(" ") } - toWriter(w, e) + writeValue(buf, e) } - io.WriteString(w, "]") + buf.WriteString("]") case *ssa.Function, *ssa.Builtin, *closure: - fmt.Fprintf(w, "%p", v) // (an address) + fmt.Fprintf(buf, "%p", v) // (an address) case rtype: - io.WriteString(w, v.t.String()) + buf.WriteString(v.t.String()) case tuple: // Unreachable in well-formed Go programs - io.WriteString(w, "(") + buf.WriteString("(") for i, e := range v { if i > 0 { - io.WriteString(w, ", ") + buf.WriteString(", ") } - toWriter(w, e) + writeValue(buf, e) } - io.WriteString(w, ")") + buf.WriteString(")") default: - fmt.Fprintf(w, "<%T>", v) + fmt.Fprintf(buf, "<%T>", v) } } // Implements printing of Go values in the style of built-in println. func toString(v value) string { var b bytes.Buffer - toWriter(&b, v) + writeValue(&b, v) return b.String() } diff --git a/go/ssa/print.go b/go/ssa/print.go index 3067250f..e2d91742 100644 --- a/go/ssa/print.go +++ b/go/ssa/print.go @@ -7,6 +7,9 @@ package ssa // This file implements the String() methods for all Value and // Instruction types. +// TODO(adonovan): define WriteValue(*bytes.Buffer) and avoid creation +// of garbage. + import ( "bytes" "fmt" @@ -364,8 +367,18 @@ func (p *Package) String() string { return "package " + p.Object.Path() } -func (p *Package) DumpTo(w io.Writer) { - fmt.Fprintf(w, "%s:\n", p) +var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer + +func (p *Package) WriteTo(w io.Writer) (int64, error) { + var buf bytes.Buffer + WritePackage(&buf, p) + n, err := w.Write(buf.Bytes()) + return int64(n), err +} + +// WritePackage writes to buf a human-readable summary of p. +func WritePackage(buf *bytes.Buffer, p *Package) { + fmt.Fprintf(buf, "%s:\n", p) var names []string maxname := 0 @@ -380,27 +393,27 @@ func (p *Package) DumpTo(w io.Writer) { for _, name := range names { switch mem := p.Members[name].(type) { case *NamedConst: - fmt.Fprintf(w, " const %-*s %s = %s\n", + fmt.Fprintf(buf, " const %-*s %s = %s\n", maxname, name, mem.Name(), mem.Value.RelString(p.Object)) case *Function: - fmt.Fprintf(w, " func %-*s %s\n", + fmt.Fprintf(buf, " func %-*s %s\n", maxname, name, types.TypeString(p.Object, mem.Type())) case *Type: - fmt.Fprintf(w, " type %-*s %s\n", + fmt.Fprintf(buf, " type %-*s %s\n", maxname, name, types.TypeString(p.Object, mem.Type().Underlying())) for _, meth := range IntuitiveMethodSet(mem.Type()) { - fmt.Fprintf(w, " %s\n", types.SelectionString(p.Object, meth)) + fmt.Fprintf(buf, " %s\n", types.SelectionString(p.Object, meth)) } case *Global: - fmt.Fprintf(w, " var %-*s %s\n", + fmt.Fprintf(buf, " var %-*s %s\n", maxname, name, types.TypeString(p.Object, mem.Type().(*types.Pointer).Elem())) } } - fmt.Fprintf(w, "\n") + fmt.Fprintf(buf, "\n") } // IntuitiveMethodSet returns the intuitive method set of a type, T. diff --git a/go/ssa/sanity.go b/go/ssa/sanity.go index 5368242d..4b0fb7e3 100644 --- a/go/ssa/sanity.go +++ b/go/ssa/sanity.go @@ -43,7 +43,7 @@ func sanityCheck(fn *Function, reporter io.Writer) bool { // func mustSanityCheck(fn *Function, reporter io.Writer) { if !sanityCheck(fn, reporter) { - fn.DumpTo(os.Stderr) + fn.WriteTo(os.Stderr) panic("SanityCheck failed") } } diff --git a/go/ssa/source_test.go b/go/ssa/source_test.go index 83e50cea..2f3bcc9a 100644 --- a/go/ssa/source_test.go +++ b/go/ssa/source_test.go @@ -213,7 +213,7 @@ func TestValueForExpr(t *testing.T) { // debugging for _, mem := range mainPkg.Members { if fn, ok := mem.(*ssa.Function); ok { - fn.DumpTo(os.Stderr) + fn.WriteTo(os.Stderr) } } } diff --git a/go/ssa/testmain.go b/go/ssa/testmain.go index 0802223c..fb330acc 100644 --- a/go/ssa/testmain.go +++ b/go/ssa/testmain.go @@ -128,7 +128,7 @@ func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package { testmain.Members["main"] = main if prog.mode&LogPackages != 0 { - testmain.DumpTo(os.Stderr) + testmain.WriteTo(os.Stderr) } if prog.mode&SanityCheckFunctions != 0 {