LEFT | RIGHT |
(no file at all) | |
1 // +build ignore | 1 // +build ignore |
2 | 2 |
3 package main | 3 package main |
4 | 4 |
5 // ssadump: a tool for displaying and interpreting the SSA form of Go programs. | 5 // ssadump: a tool for displaying and interpreting the SSA form of Go programs. |
6 | 6 |
7 import ( | 7 import ( |
8 "exp/ssa" | 8 "exp/ssa" |
9 "exp/ssa/interp" | 9 "exp/ssa/interp" |
10 "flag" | 10 "flag" |
11 "fmt" | 11 "fmt" |
| 12 "go/ast" |
12 "log" | 13 "log" |
13 "os" | 14 "os" |
14 "runtime/pprof" | 15 "runtime/pprof" |
15 "strings" | 16 "strings" |
16 ) | 17 ) |
17 | 18 |
18 // TODO(adonovan): perhaps these should each be separate flags? | 19 // TODO(adonovan): perhaps these should each be separate flags? |
19 var buildFlag = flag.String("build", "", `Options controlling the SSA builder. | 20 var buildFlag = flag.String("build", "", `Options controlling the SSA builder. |
20 The value is a sequence of zero or more of these letters: | 21 The value is a sequence of zero or more of these letters: |
21 C perform sanity [C]hecking of the SSA form. | 22 C perform sanity [C]hecking of the SSA form. |
22 P log [P]ackage inventory. | 23 P log [P]ackage inventory. |
23 F log [F]unction SSA code. | 24 F log [F]unction SSA code. |
24 S log [S]ource locations as SSA builder progresses. | 25 S log [S]ource locations as SSA builder progresses. |
25 G use binary object files from gc to provide imports (no code). | 26 G use binary object files from gc to provide imports (no code). |
26 L build distinct packages seria[L]ly instead of in parallel. | 27 L build distinct packages seria[L]ly instead of in parallel. |
27 N build [N]aive SSA form: don't replace local loads/stores with registers. | 28 N build [N]aive SSA form: don't replace local loads/stores with registers. |
28 `) | 29 `) |
29 | 30 |
30 var runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the progra
m.") | 31 var runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the progra
m.") |
31 | 32 |
32 var interpFlag = flag.String("interp", "", `Options controlling the SSA test int
erpreter. | 33 var interpFlag = flag.String("interp", "", `Options controlling the SSA test int
erpreter. |
33 The value is a sequence of zero or more more of these letters: | 34 The value is a sequence of zero or more more of these letters: |
34 R disable [R]ecover() from panic; show interpreter crash instead. | 35 R disable [R]ecover() from panic; show interpreter crash instead. |
35 T [T]race execution of the program. Best for single-threaded programs! | 36 T [T]race execution of the program. Best for single-threaded programs! |
36 `) | 37 `) |
37 | 38 |
38 const usage = `SSA builder and interpreter. | 39 const usage = `SSA builder and interpreter. |
39 Usage: ssadump [<flag> ...] <file.go> ... | 40 Usage: ssadump [<flag> ...] [<file.go> ...] [<arg> ...] |
| 41 ssadump [<flag> ...] <import/path> [<arg> ...] |
40 Use -help flag to display options. | 42 Use -help flag to display options. |
41 | 43 |
42 Examples: | 44 Examples: |
43 % ssadump -run -interp=T hello.go # interpret a program, with tracing | 45 % ssadump -run -interp=T hello.go # interpret a program, with tracing |
44 % ssadump -build=FPG hello.go # quickly dump SSA form of a single packag
e | 46 % ssadump -build=FPG hello.go # quickly dump SSA form of a single packag
e |
45 ` | 47 ` |
46 | 48 |
47 var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") | 49 var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") |
48 | 50 |
49 func main() { | 51 func main() { |
50 flag.Parse() | 52 flag.Parse() |
51 args := flag.Args() | 53 args := flag.Args() |
52 | 54 |
53 // TODO(adonovan): perhaps we need a more extensible option | 55 // TODO(adonovan): perhaps we need a more extensible option |
54 // API than a bitset, e.g. a struct with a sane zero value? | 56 // API than a bitset, e.g. a struct with a sane zero value? |
55 var mode ssa.BuilderMode | 57 var mode ssa.BuilderMode |
56 for _, c := range *buildFlag { | 58 for _, c := range *buildFlag { |
57 switch c { | 59 switch c { |
58 case 'P': | 60 case 'P': |
59 » » » mode |= ssa.LogPackages | 61 » » » mode |= ssa.LogPackages | ssa.BuildSerially |
60 case 'F': | 62 case 'F': |
61 » » » mode |= ssa.LogFunctions | 63 » » » mode |= ssa.LogFunctions | ssa.BuildSerially |
62 case 'S': | 64 case 'S': |
63 » » » mode |= ssa.LogSource | 65 » » » mode |= ssa.LogSource | ssa.BuildSerially |
64 case 'C': | 66 case 'C': |
65 mode |= ssa.SanityCheckFunctions | 67 mode |= ssa.SanityCheckFunctions |
66 case 'N': | 68 case 'N': |
67 mode |= ssa.NaiveForm | 69 mode |= ssa.NaiveForm |
68 case 'G': | 70 case 'G': |
69 mode |= ssa.UseGCImporter | 71 mode |= ssa.UseGCImporter |
70 case 'L': | 72 case 'L': |
71 mode |= ssa.BuildSerially | 73 mode |= ssa.BuildSerially |
72 default: | 74 default: |
73 log.Fatalf("Unknown -build option: '%c'.", c) | 75 log.Fatalf("Unknown -build option: '%c'.", c) |
(...skipping 10 matching lines...) Expand all Loading... |
84 default: | 86 default: |
85 log.Fatalf("Unknown -interp option: '%c'.", c) | 87 log.Fatalf("Unknown -interp option: '%c'.", c) |
86 } | 88 } |
87 } | 89 } |
88 | 90 |
89 if len(args) == 0 { | 91 if len(args) == 0 { |
90 fmt.Fprint(os.Stderr, usage) | 92 fmt.Fprint(os.Stderr, usage) |
91 os.Exit(1) | 93 os.Exit(1) |
92 } | 94 } |
93 | 95 |
94 // Treat all leading consecutive "*.go" arguments as a single package. | |
95 // | |
96 // TODO(gri): make it a typechecker error for there to be | |
97 // duplicate (e.g.) main functions in the same package. | |
98 var gofiles []string | |
99 for len(args) > 0 && strings.HasSuffix(args[0], ".go") { | |
100 gofiles = append(gofiles, args[0]) | |
101 args = args[1:] | |
102 } | |
103 if gofiles == nil { | |
104 log.Fatal("No *.go source files specified.") | |
105 } | |
106 | |
107 // Profiling support. | 96 // Profiling support. |
108 if *cpuprofile != "" { | 97 if *cpuprofile != "" { |
109 f, err := os.Create(*cpuprofile) | 98 f, err := os.Create(*cpuprofile) |
110 if err != nil { | 99 if err != nil { |
111 log.Fatal(err) | 100 log.Fatal(err) |
112 } | 101 } |
113 pprof.StartCPUProfile(f) | 102 pprof.StartCPUProfile(f) |
114 defer pprof.StopCPUProfile() | 103 defer pprof.StopCPUProfile() |
115 } | 104 } |
116 | 105 |
117 // TODO(adonovan): permit naming a package directly instead of | |
118 // a list of .go files. | |
119 | |
120 // TODO(adonovan/gri): the cascade of errors is confusing due | 106 // TODO(adonovan/gri): the cascade of errors is confusing due |
121 // to reentrant control flow. Disable for now and re-think. | 107 // to reentrant control flow. Disable for now and re-think. |
122 var errh func(error) | 108 var errh func(error) |
123 // errh = func(err error) { fmt.Println(err.Error()) } | 109 // errh = func(err error) { fmt.Println(err.Error()) } |
124 | 110 |
125 » b := ssa.NewBuilder(mode, ssa.GorootLoader, errh) | 111 » loader := ssa.GorootLoader |
126 » files, err := ssa.ParseFiles(b.Prog.Files, ".", gofiles...) | 112 » b := ssa.NewBuilder(mode, loader, errh) |
| 113 |
| 114 » var pkgname string |
| 115 » var files []*ast.File |
| 116 » var err error |
| 117 |
| 118 » switch { |
| 119 » case len(args) == 0: |
| 120 » » log.Fatal("No *.go source files nor package name was specified."
) |
| 121 |
| 122 » case strings.HasSuffix(args[0], ".go"): |
| 123 » » // % ssadump a.go b.go ... |
| 124 » » // Leading consecutive *.go arguments constitute main package. |
| 125 » » i := 1 |
| 126 » » for ; i < len(args) && strings.HasSuffix(args[i], ".go"); i++ { |
| 127 » » } |
| 128 » » files, err = ssa.ParseFiles(b.Prog.Files, ".", args[:i]...) |
| 129 » » pkgname = "main" |
| 130 » » args = args[i:] |
| 131 |
| 132 » default: |
| 133 » » // % ssadump my/package ... |
| 134 » » // First argument is import path of main package. |
| 135 » » pkgname = args[0] |
| 136 » » args = args[1:] |
| 137 » » files, err = loader(b.Prog.Files, pkgname) |
| 138 » } |
127 if err != nil { | 139 if err != nil { |
128 log.Fatalf(err.Error()) | 140 log.Fatalf(err.Error()) |
129 } | 141 } |
130 » mainpkg, err := b.CreatePackage("main", files) | 142 |
| 143 » // TODO(gri): make it a typechecker error for there to be |
| 144 » // duplicate (e.g.) main functions in the same package. |
| 145 » mainpkg, err := b.CreatePackage(pkgname, files) |
131 if err != nil { | 146 if err != nil { |
132 log.Fatalf(err.Error()) | 147 log.Fatalf(err.Error()) |
133 } | 148 } |
134 b.BuildAllPackages() | 149 b.BuildAllPackages() |
135 b = nil // discard Builder | 150 b = nil // discard Builder |
136 | 151 |
137 if *runFlag { | 152 if *runFlag { |
138 » » interp.Interpret(mainpkg, interpMode, gofiles[0], args) | 153 » » interp.Interpret(mainpkg, interpMode, pkgname, args) |
139 } | 154 } |
140 } | 155 } |
LEFT | RIGHT |