go.tools/go/ssa: use bytes.Buffer instead of io.Writer.

This is (a) more efficient and (b) avoids the need for
constant error handling, since buffer writes can't fail.

Also:
- added WriteFunction and WritePackage functions,
  similar to types.WriteFoo.
- *Function and *Package now implement io.WriterTo.

LGTM=gri
R=gri
CC=golang-codereviews
https://golang.org/cl/57930044
diff --git a/go/pointer/gen.go b/go/pointer/gen.go
index bfb3bf9..a9e1842 100644
--- a/go/pointer/gen.go
+++ b/go/pointer/gen.go
@@ -1141,9 +1141,9 @@
 			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 0253b35..64cfbeb 100644
--- a/go/ssa/blockopt.go
+++ b/go/ssa/blockopt.go
@@ -152,7 +152,7 @@
 		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 4abbad2..81f0d66 100644
--- a/go/ssa/create.go
+++ b/go/ssa/create.go
@@ -232,7 +232,7 @@
 	}
 
 	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 33d1cfa..12ef430 100644
--- a/go/ssa/dom.go
+++ b/go/ssa/dom.go
@@ -18,8 +18,8 @@
 // to avoid the need for buckets of size > 1.
 
 import (
+	"bytes"
 	"fmt"
-	"io"
 	"math/big"
 	"os"
 	"sort"
@@ -310,32 +310,32 @@
 // 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 985a521..71ec90c 100644
--- a/go/ssa/example_test.go
+++ b/go/ssa/example_test.go
@@ -64,14 +64,14 @@
 	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 fea2bf3..6af67a3 100644
--- a/go/ssa/func.go
+++ b/go/ssa/func.go
@@ -330,7 +330,7 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	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, "<deleted>")
+				buf.WriteString("<deleted>")
 			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 46e2593..eb87b00 100644
--- a/go/ssa/interp/value.go
+++ b/go/ssa/interp/value.go
@@ -352,109 +352,109 @@
 // 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, "<nil>")
+			buf.WriteString("<nil>")
 		} 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 3067250..e2d9174 100644
--- a/go/ssa/print.go
+++ b/go/ssa/print.go
@@ -7,6 +7,9 @@
 // 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 @@
 	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 @@
 	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 5368242..4b0fb7e 100644
--- a/go/ssa/sanity.go
+++ b/go/ssa/sanity.go
@@ -43,7 +43,7 @@
 //
 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 83e50ce..2f3bcc9 100644
--- a/go/ssa/source_test.go
+++ b/go/ssa/source_test.go
@@ -213,7 +213,7 @@
 		// 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 0802223..fb330ac 100644
--- a/go/ssa/testmain.go
+++ b/go/ssa/testmain.go
@@ -128,7 +128,7 @@
 	testmain.Members["main"] = main
 
 	if prog.mode&LogPackages != 0 {
-		testmain.DumpTo(os.Stderr)
+		testmain.WriteTo(os.Stderr)
 	}
 
 	if prog.mode&SanityCheckFunctions != 0 {