github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/internal/cmd/irdump/main.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  // irdump: a tool for displaying the IR form of Go programs.
     6  package main
     7  
     8  import (
     9  	"flag"
    10  	"fmt"
    11  	"go/build"
    12  	"os"
    13  	"runtime/pprof"
    14  
    15  	"github.com/amarpal/go-tools/go/ir"
    16  	"github.com/amarpal/go-tools/go/ir/irutil"
    17  
    18  	"golang.org/x/tools/go/buildutil"
    19  	"golang.org/x/tools/go/packages"
    20  )
    21  
    22  // flags
    23  var (
    24  	mode       = ir.BuilderMode(ir.PrintPackages | ir.PrintFunctions)
    25  	testFlag   = flag.Bool("test", false, "include implicit test packages and executables")
    26  	cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
    27  	dot        bool
    28  	html       string
    29  )
    30  
    31  func init() {
    32  	flag.Var(&mode, "build", ir.BuilderModeDoc)
    33  	flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
    34  	flag.BoolVar(&dot, "dot", false, "Print Graphviz dot of CFG")
    35  	flag.StringVar(&html, "html", "", "Print HTML for 'function'")
    36  }
    37  
    38  const usage = `IR builder.
    39  Usage: irdump [-build=[DBCSNFL]] [-test] [-arg=...] package...
    40  Use -help flag to display options.
    41  
    42  Examples:
    43  % irdump -build=F hello.go              # dump IR form of a single package
    44  % irdump -build=F -test fmt             # dump IR form of a package and its tests
    45  `
    46  
    47  func main() {
    48  	if err := doMain(); err != nil {
    49  		fmt.Fprintf(os.Stderr, "irdump: %s\n", err)
    50  		os.Exit(1)
    51  	}
    52  }
    53  
    54  func doMain() error {
    55  	flag.Parse()
    56  	if len(flag.Args()) == 0 {
    57  		fmt.Fprint(os.Stderr, usage)
    58  		os.Exit(1)
    59  	}
    60  
    61  	cfg := &packages.Config{
    62  		Mode:  packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo,
    63  		Tests: *testFlag,
    64  	}
    65  
    66  	// Profiling support.
    67  	if *cpuprofile != "" {
    68  		f, err := os.Create(*cpuprofile)
    69  		if err != nil {
    70  			fmt.Fprintln(os.Stderr, err)
    71  			os.Exit(1)
    72  		}
    73  		pprof.StartCPUProfile(f)
    74  		defer pprof.StopCPUProfile()
    75  	}
    76  
    77  	// Load, parse and type-check the initial packages.
    78  	initial, err := packages.Load(cfg, flag.Args()...)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	if len(initial) == 0 {
    83  		return fmt.Errorf("no packages")
    84  	}
    85  	if packages.PrintErrors(initial) > 0 {
    86  		return fmt.Errorf("packages contain errors")
    87  	}
    88  
    89  	// Create IR-form program representation.
    90  	_, pkgs := irutil.Packages(initial, mode, &irutil.Options{PrintFunc: html})
    91  
    92  	for i, p := range pkgs {
    93  		if p == nil {
    94  			return fmt.Errorf("cannot build IR for package %s", initial[i])
    95  		}
    96  	}
    97  
    98  	// Build and display only the initial packages
    99  	// (and synthetic wrappers).
   100  	for _, p := range pkgs {
   101  		p.Build()
   102  	}
   103  
   104  	if dot {
   105  		for _, p := range pkgs {
   106  			for _, m := range p.Members {
   107  				if fn, ok := m.(*ir.Function); ok {
   108  					fmt.Println("digraph{")
   109  					fmt.Printf("label = %q;\n", fn.Name())
   110  					for _, b := range fn.Blocks {
   111  						fmt.Printf("n%d [label=\"%d: %s\"]\n", b.Index, b.Index, b.Comment)
   112  						for _, succ := range b.Succs {
   113  							fmt.Printf("n%d -> n%d\n", b.Index, succ.Index)
   114  						}
   115  					}
   116  					fmt.Println("}")
   117  				}
   118  			}
   119  		}
   120  	}
   121  	return nil
   122  }