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 }