From 113d6d30b12ec59097f6cd4608fb159b474c4f75 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Fri, 17 May 2013 17:33:09 -0400 Subject: [PATCH] code.google.com/p/go.tools/ssa: include ssa.Package and init() function in example output. Also: - remove redundant text in doc.go. - fix (yet more) cases of missing parens in Printf, fallout from go/types accessors refactoring. - don't mix spaces and tabs within lines printed by ssa.Function.DumpTo: it makes it too hard to constructed expected outputs for tests. (Tabs may appear at line start though.) Sadly godoc -play won't run this program; it complains it can't import "code.google.com/p/go.exp/ssa". Any idea why? R=gri CC=golang-dev https://golang.org/cl/9481044 --- ssa/doc.go | 51 +------------------------------------------- ssa/example_test.go | 52 +++++++++++++++++++++++++++++++++++++++------ ssa/func.go | 8 +++++-- ssa/print.go | 4 ++-- 4 files changed, 55 insertions(+), 60 deletions(-) diff --git a/ssa/doc.go b/ssa/doc.go index 1ebbedc5..71c04dc4 100644 --- a/ssa/doc.go +++ b/ssa/doc.go @@ -107,56 +107,6 @@ // either accurate or unambiguous. The public API exposes a number of // name-based maps for client convenience. // -// Given a Go source package such as this: -// -// package main -// -// import "fmt" -// -// const message = "Hello, World!" -// -// func main() { -// fmt.Println(message) -// } -// -// The SSA Builder creates a *Program containing a main *Package such -// as this: -// -// Package (Name: "main") -// Members: -// "message": *Constant (Type: untyped string, Value: "Hello, World!") -// "init·guard": *Global (Type: *bool) -// "main": *Function (Type: func()) -// Init: *Function (Type: func()) -// -// The printed representation of the function main.main is shown -// below. Within the function listing, the name of each BasicBlock -// such as ".0.entry" is printed left-aligned, followed by the block's -// Instructions. -// For each instruction that defines an SSA virtual register -// (i.e. implements Value), the type of that value is shown in the -// right column. -// -// # Name: main.main -// # Declared at hello.go:7:6 -// func main(): -// .0.entry: -// t0 = new [1]interface{} *[1]interface{} -// t1 = &t0[0:untyped integer] *interface{} -// t2 = make interface interface{} <- string ("Hello, World!":string) interface{} -// *t1 = t2 -// t3 = slice t0[:] []interface{} -// t4 = fmt.Println(t3) (n int, err error) -// ret -// -// -// The ssadump utility is an example of an application that loads and -// dumps the SSA form of a Go program, whether a single package or a -// whole program. -// -// TODO(adonovan): demonstrate more features in the example: -// parameters and control flow at the least. -// // TODO(adonovan): Consider the exceptional control-flow implications // of defer and recover(). // @@ -174,4 +124,5 @@ // flexibility. For example, analysis tools may wish to construct a // fake ssa.Function for the root of the callgraph, a fake "reflect" // package, etc. +// package ssa diff --git a/ssa/example_test.go b/ssa/example_test.go index a7c66292..4fa9ba70 100644 --- a/ssa/example_test.go +++ b/ssa/example_test.go @@ -8,15 +8,31 @@ import ( "os" ) -// This example demonstrates the SSA builder. +// This program demonstrates how to run the SSA builder on a "Hello, +// World!" program and shows the printed representation of packages, +// functions and instructions. +// +// Within the function listing, the name of each BasicBlock such as +// ".0.entry" is printed left-aligned, followed by the block's +// Instructions. +// +// For each instruction that defines an SSA virtual register +// (i.e. implements Value), the type of that value is shown in the +// right column. +// +// Build and run the ssadump.go program in this package if you want a +// standalone tool with similar functionality. +// func Example() { const hello = ` package main import "fmt" +const message = "Hello, World!" + func main() { - fmt.Println("Hello, World!") + fmt.Println(message) } ` @@ -26,21 +42,26 @@ func main() { // Parse the input file. file, err := parser.ParseFile(builder.Prog.Files, "hello.go", hello, parser.DeclarationErrors) if err != nil { - fmt.Printf("Parsing failed: %s\n", err.Error()) + fmt.Printf(err.Error()) // parse error return } // Create a "main" package containing one file. mainPkg, err := builder.CreatePackage("main", []*ast.File{file}) if err != nil { - fmt.Printf("Type-checking failed: %s\n", err.Error()) + fmt.Printf(err.Error()) // type error return } + // Print out the package. + mainPkg.DumpTo(os.Stdout) + fmt.Println() + // Build SSA code for bodies of functions in mainPkg. builder.BuildPackage(mainPkg) // Print out the package-level functions. + mainPkg.Init.DumpTo(os.Stdout) for _, mem := range mainPkg.Members { if fn, ok := mem.(*ssa.Function); ok { fn.DumpTo(os.Stdout) @@ -48,10 +69,29 @@ func main() { } // Output: + // + // Package main: + // var init·guard *bool + // func main func() + // const message message = "Hello, World!":untyped string + // + // # Name: main.init + // # Declared at - + // func init(): + // .0.entry: P:0 S:2 + // t0 = *init·guard bool + // if t0 goto 2.init.done else 1.init.start + // .1.init.start: P:1 S:1 + // *init·guard = true:bool + // t1 = fmt.init() () + // jump 2.init.done + // .2.init.done: P:2 S:0 + // ret + // // # Name: main.main - // # Declared at hello.go:6:6 + // # Declared at hello.go:8:6 // func main(): - // .0.entry: P:0 S:0 + // .0.entry: P:0 S:0 // a0 = new [1]interface{} *[1]interface{} // t0 = &a0[0:untyped integer] *interface{} // t1 = make interface interface{} <- string ("Hello, World!":string) interface{} diff --git a/ssa/func.go b/ssa/func.go index d16dd119..0b2c8213 100644 --- a/ssa/func.go +++ b/ssa/func.go @@ -554,13 +554,17 @@ func (f *Function) DumpTo(w io.Writer) { io.WriteString(w, "\t(external)\n") } + // NB. column calculations are confused by non-ASCII characters. + const punchcard = 80 // for old time's sake. for _, b := range f.Blocks { if b == nil { // Corrupt CFG. fmt.Fprintf(w, ".nil:\n") continue } - fmt.Fprintf(w, ".%s:\t\t\t\t\t\t\t P:%d S:%d\n", b, len(b.Preds), len(b.Succs)) + 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)) + if false { // CFG debugging fmt.Fprintf(w, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs) } @@ -568,7 +572,7 @@ func (f *Function) DumpTo(w io.Writer) { io.WriteString(w, "\t") switch v := instr.(type) { case Value: - l := 80 // for old time's sake. + l := punchcard // Left-align the instruction. if name := v.Name(); name != "" { n, _ := fmt.Fprintf(w, "%s = ", name) diff --git a/ssa/print.go b/ssa/print.go index f92add1b..2a61e413 100644 --- a/ssa/print.go +++ b/ssa/print.go @@ -17,7 +17,7 @@ func (id Id) String() string { if id.Pkg == nil { return id.Name } - return fmt.Sprintf("%s/%s", id.Pkg.Path, id.Name) + return fmt.Sprintf("%s/%s", id.Pkg.Path(), id.Name) } // relName returns the name of v relative to i. @@ -360,7 +360,7 @@ func (p *Package) String() string { } func (p *Package) DumpTo(w io.Writer) { - fmt.Fprintf(w, "Package %s:\n", p.Types.Path) + fmt.Fprintf(w, "Package %s:\n", p.Types.Path()) var names []string maxname := 0