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
This commit is contained in:
parent
87334f402b
commit
113d6d30b1
51
ssa/doc.go
51
ssa/doc.go
|
@ -107,56 +107,6 @@
|
||||||
// either accurate or unambiguous. The public API exposes a number of
|
// either accurate or unambiguous. The public API exposes a number of
|
||||||
// name-based maps for client convenience.
|
// 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
|
// TODO(adonovan): Consider the exceptional control-flow implications
|
||||||
// of defer and recover().
|
// of defer and recover().
|
||||||
//
|
//
|
||||||
|
@ -174,4 +124,5 @@
|
||||||
// flexibility. For example, analysis tools may wish to construct a
|
// flexibility. For example, analysis tools may wish to construct a
|
||||||
// fake ssa.Function for the root of the callgraph, a fake "reflect"
|
// fake ssa.Function for the root of the callgraph, a fake "reflect"
|
||||||
// package, etc.
|
// package, etc.
|
||||||
|
//
|
||||||
package ssa
|
package ssa
|
||||||
|
|
|
@ -8,15 +8,31 @@ import (
|
||||||
"os"
|
"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() {
|
func Example() {
|
||||||
const hello = `
|
const hello = `
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
|
const message = "Hello, World!"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("Hello, World!")
|
fmt.Println(message)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -26,21 +42,26 @@ func main() {
|
||||||
// Parse the input file.
|
// Parse the input file.
|
||||||
file, err := parser.ParseFile(builder.Prog.Files, "hello.go", hello, parser.DeclarationErrors)
|
file, err := parser.ParseFile(builder.Prog.Files, "hello.go", hello, parser.DeclarationErrors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Parsing failed: %s\n", err.Error())
|
fmt.Printf(err.Error()) // parse error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a "main" package containing one file.
|
// Create a "main" package containing one file.
|
||||||
mainPkg, err := builder.CreatePackage("main", []*ast.File{file})
|
mainPkg, err := builder.CreatePackage("main", []*ast.File{file})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Type-checking failed: %s\n", err.Error())
|
fmt.Printf(err.Error()) // type error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Print out the package.
|
||||||
|
mainPkg.DumpTo(os.Stdout)
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
// Build SSA code for bodies of functions in mainPkg.
|
// Build SSA code for bodies of functions in mainPkg.
|
||||||
builder.BuildPackage(mainPkg)
|
builder.BuildPackage(mainPkg)
|
||||||
|
|
||||||
// Print out the package-level functions.
|
// Print out the package-level functions.
|
||||||
|
mainPkg.Init.DumpTo(os.Stdout)
|
||||||
for _, mem := range mainPkg.Members {
|
for _, mem := range mainPkg.Members {
|
||||||
if fn, ok := mem.(*ssa.Function); ok {
|
if fn, ok := mem.(*ssa.Function); ok {
|
||||||
fn.DumpTo(os.Stdout)
|
fn.DumpTo(os.Stdout)
|
||||||
|
@ -48,8 +69,27 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output:
|
// 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
|
// # Name: main.main
|
||||||
// # Declared at hello.go:6:6
|
// # Declared at hello.go:8:6
|
||||||
// func main():
|
// func main():
|
||||||
// .0.entry: P:0 S:0
|
// .0.entry: P:0 S:0
|
||||||
// a0 = new [1]interface{} *[1]interface{}
|
// a0 = new [1]interface{} *[1]interface{}
|
||||||
|
|
|
@ -554,13 +554,17 @@ func (f *Function) DumpTo(w io.Writer) {
|
||||||
io.WriteString(w, "\t(external)\n")
|
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 {
|
for _, b := range f.Blocks {
|
||||||
if b == nil {
|
if b == nil {
|
||||||
// Corrupt CFG.
|
// Corrupt CFG.
|
||||||
fmt.Fprintf(w, ".nil:\n")
|
fmt.Fprintf(w, ".nil:\n")
|
||||||
continue
|
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
|
if false { // CFG debugging
|
||||||
fmt.Fprintf(w, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs)
|
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")
|
io.WriteString(w, "\t")
|
||||||
switch v := instr.(type) {
|
switch v := instr.(type) {
|
||||||
case Value:
|
case Value:
|
||||||
l := 80 // for old time's sake.
|
l := punchcard
|
||||||
// Left-align the instruction.
|
// Left-align the instruction.
|
||||||
if name := v.Name(); name != "" {
|
if name := v.Name(); name != "" {
|
||||||
n, _ := fmt.Fprintf(w, "%s = ", name)
|
n, _ := fmt.Fprintf(w, "%s = ", name)
|
||||||
|
|
|
@ -17,7 +17,7 @@ func (id Id) String() string {
|
||||||
if id.Pkg == nil {
|
if id.Pkg == nil {
|
||||||
return id.Name
|
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.
|
// 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) {
|
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
|
var names []string
|
||||||
maxname := 0
|
maxname := 0
|
||||||
|
|
Loading…
Reference in New Issue