github.com/goplus/llgo@v0.8.3/chore/ssadump/ssadump.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // ssadump: a tool for displaying and interpreting the SSA form of Go programs. 6 package main // import "golang.org/x/tools/cmd/ssadump" 7 8 import ( 9 "flag" 10 "fmt" 11 "go/build" 12 "go/types" 13 "os" 14 "runtime" 15 "runtime/pprof" 16 17 "golang.org/x/tools/go/buildutil" 18 "golang.org/x/tools/go/packages" 19 "golang.org/x/tools/go/ssa" 20 "golang.org/x/tools/go/ssa/interp" 21 "golang.org/x/tools/go/ssa/ssautil" 22 ) 23 24 const ( 25 loadFiles = packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles 26 loadImports = loadFiles | packages.NeedImports 27 loadTypes = loadImports | packages.NeedTypes | packages.NeedTypesSizes 28 loadSyntax = loadTypes | packages.NeedSyntax | packages.NeedTypesInfo 29 ) 30 31 // flags 32 var ( 33 mode = ssa.BuilderMode(0) 34 35 testFlag = flag.Bool("test", false, "include implicit test packages and executables") 36 37 runFlag = flag.Bool("run", false, "interpret the SSA program") 38 39 interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter. 40 The value is a sequence of zero or more more of these letters: 41 R disable [R]ecover() from panic; show interpreter crash instead. 42 T [T]race execution of the program. Best for single-threaded programs! 43 `) 44 45 cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") 46 47 args stringListValue 48 ) 49 50 func init() { 51 flag.Var(&mode, "build", ssa.BuilderModeDoc) 52 flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) 53 flag.Var(&args, "arg", "add argument to interpreted program") 54 } 55 56 const usage = `SSA builder and interpreter. 57 Usage: ssadump [-build=[DBCSNFLG]] [-test] [-run] [-interp=[TR]] [-arg=...] package... 58 Use -help flag to display options. 59 60 Examples: 61 % ssadump -build=F hello.go # dump SSA form of a single package 62 % ssadump -build=F -test fmt # dump SSA form of a package and its tests 63 % ssadump -run -interp=T hello.go # interpret a program, with tracing 64 65 The -run flag causes ssadump to build the code in a runnable form and run the first 66 package named main. 67 68 Interpretation of the standard "testing" package is no longer supported. 69 ` 70 71 func main() { 72 if err := doMain(); err != nil { 73 fmt.Fprintf(os.Stderr, "ssadump: %s\n", err) 74 os.Exit(1) 75 } 76 } 77 78 func doMain() error { 79 flag.Parse() 80 if len(flag.Args()) == 0 { 81 fmt.Fprint(os.Stderr, usage) 82 os.Exit(1) 83 } 84 85 cfg := &packages.Config{ 86 Mode: loadSyntax, 87 Tests: *testFlag, 88 } 89 90 // Choose types.Sizes from conf.Build. 91 // TODO(adonovan): remove this when go/packages provides a better way. 92 var wordSize int64 = 8 93 switch build.Default.GOARCH { 94 case "386", "arm": 95 wordSize = 4 96 } 97 sizes := &types.StdSizes{ 98 MaxAlign: 8, 99 WordSize: wordSize, 100 } 101 102 var interpMode interp.Mode 103 for _, c := range *interpFlag { 104 switch c { 105 case 'T': 106 interpMode |= interp.EnableTracing 107 case 'R': 108 interpMode |= interp.DisableRecover 109 default: 110 return fmt.Errorf("unknown -interp option: '%c'", c) 111 } 112 } 113 114 // Profiling support. 115 if *cpuprofile != "" { 116 f, err := os.Create(*cpuprofile) 117 if err != nil { 118 fmt.Fprintln(os.Stderr, err) 119 os.Exit(1) 120 } 121 pprof.StartCPUProfile(f) 122 defer pprof.StopCPUProfile() 123 } 124 125 // Load, parse and type-check the initial packages, 126 // and, if -run, their dependencies. 127 if *runFlag { 128 cfg.Mode = loadSyntax | packages.NeedDeps 129 } 130 initial, err := packages.Load(cfg, flag.Args()...) 131 if err != nil { 132 return err 133 } 134 if len(initial) == 0 { 135 return fmt.Errorf("no packages") 136 } 137 if packages.PrintErrors(initial) > 0 { 138 return fmt.Errorf("packages contain errors") 139 } 140 141 // Turn on instantiating generics during build if the program will be run. 142 if *runFlag { 143 mode |= ssa.InstantiateGenerics 144 } 145 146 // Create SSA-form program representation. 147 prog, pkgs := ssautil.AllPackages(initial, mode) 148 149 for i, p := range pkgs { 150 if p == nil { 151 return fmt.Errorf("cannot build SSA for package %s", initial[i]) 152 } 153 } 154 155 if !*runFlag { 156 // Build and display only the initial packages 157 // (and synthetic wrappers). 158 for _, p := range pkgs { 159 p.Build() 160 } 161 162 } else { 163 // Run the interpreter. 164 // Build SSA for all packages. 165 prog.Build() 166 167 // Earlier versions of the interpreter needed the runtime 168 // package; however, interp cannot handle unsafe constructs 169 // used during runtime's package initialization at the moment. 170 // The key construct blocking support is: 171 // *((*T)(unsafe.Pointer(p))) 172 // Unfortunately, this means only trivial programs can be 173 // interpreted by ssadump. 174 if prog.ImportedPackage("runtime") != nil { 175 return fmt.Errorf("-run: program depends on runtime package (interpreter can run only trivial programs)") 176 } 177 178 if runtime.GOARCH != build.Default.GOARCH { 179 return fmt.Errorf("cross-interpretation is not supported (target has GOARCH %s, interpreter has %s)", 180 build.Default.GOARCH, runtime.GOARCH) 181 } 182 183 // Run first main package. 184 for _, main := range ssautil.MainPackages(pkgs) { 185 fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path()) 186 os.Exit(interp.Interpret(main, interpMode, sizes, main.Pkg.Path(), args)) 187 } 188 return fmt.Errorf("no main package") 189 } 190 return nil 191 } 192 193 // stringListValue is a flag.Value that accumulates strings. 194 // e.g. --flag=one --flag=two would produce []string{"one", "two"}. 195 type stringListValue []string 196 197 func (ss *stringListValue) Get() interface{} { return []string(*ss) } 198 199 func (ss *stringListValue) String() string { return fmt.Sprintf("%q", *ss) } 200 201 func (ss *stringListValue) Set(s string) error { *ss = append(*ss, s); return nil }