main.go raw

   1  // mxssadump exercises the mxssa builder on a real package.
   2  // It loads a package via the moxie loader, runs the native type checker (B1),
   3  // then builds the mxssa IR (B2) and prints a summary.
   4  //
   5  // Usage: mxssadump <import-path|.>
   6  package main
   7  
   8  import (
   9  	"fmt"
  10  	"go/token"
  11  	"go/types"
  12  	"os"
  13  	"runtime"
  14  	"sort"
  15  	"strings"
  16  
  17  	"moxie/compileopts"
  18  	"moxie/goenv"
  19  	"moxie/loader"
  20  	"moxie/mxssa"
  21  	"moxie/syntax"
  22  	"moxie/typecheck"
  23  	"moxie/typecheck/bridge"
  24  )
  25  
  26  func main() {
  27  	if len(os.Args) < 2 {
  28  		fmt.Fprintln(os.Stderr, "usage: mxssadump <import-path|.>")
  29  		os.Exit(1)
  30  	}
  31  	target := os.Args[1]
  32  
  33  	opts := &compileopts.Options{GOOS: "linux", GOARCH: "amd64"}
  34  	cfg, err := newConfig(opts)
  35  	if err != nil {
  36  		fatalf("config: %v", err)
  37  	}
  38  
  39  	typeCfg := types.Config{Error: func(e error) {}}
  40  	prog, err := loader.Load(cfg, target, typeCfg)
  41  	if err != nil {
  42  		fatalf("load: %v", err)
  43  	}
  44  	prog.EnableSyntaxFiles()
  45  	if err := prog.Parse(); err != nil {
  46  		if !isMainNameError(err.Error()) {
  47  			fatalf("parse: %v", err)
  48  		}
  49  	}
  50  
  51  	mainPkg := prog.MainPkg()
  52  	if mainPkg == nil || len(mainPkg.SyntaxFiles) == 0 {
  53  		fatalf("no syntax files for %s", target)
  54  	}
  55  
  56  	// Build bridge importer from the go/types package graph.
  57  	pkgMap := make(map[string]*types.Package, len(prog.Packages))
  58  	for path, p := range prog.Packages {
  59  		if p.Pkg != nil {
  60  			pkgMap[path] = p.Pkg
  61  		}
  62  	}
  63  	imp := bridge.New(pkgMap)
  64  
  65  	// Run the native checker (B1).
  66  	tcPkg, _, err := typecheck.Check(mainPkg.ImportPath, mainPkg.SyntaxFiles, imp)
  67  	if err != nil {
  68  		fmt.Fprintf(os.Stderr, "warn: typecheck: %v\n", err)
  69  	}
  70  
  71  	// Build the mxssa IR (B2).
  72  	fset := token.NewFileSet()
  73  	ssaProg := mxssa.NewProgram(fset)
  74  
  75  	if tcPkg == nil {
  76  		fatalf("typecheck returned nil package")
  77  	}
  78  
  79  	// Collect typecheck.Info from the checker.
  80  	_, info, _ := typecheck.Check(mainPkg.ImportPath, mainPkg.SyntaxFiles, imp)
  81  
  82  	ssaPkg := ssaProg.CreatePackage(tcPkg, mainPkg.SyntaxFiles, info)
  83  
  84  	// Print summary.
  85  	fmt.Printf("package %s\n", mainPkg.ImportPath)
  86  	fmt.Printf("  syntax files:  %d\n", len(mainPkg.SyntaxFiles))
  87  	fmt.Printf("  ssa members:   %d\n", len(ssaPkg.Members))
  88  
  89  	var funcs, globals, types_, consts int
  90  	var funcNames []string
  91  	for name, m := range ssaPkg.Members {
  92  		switch m.(type) {
  93  		case *mxssa.Function:
  94  			funcs++
  95  			funcNames = append(funcNames, name)
  96  		case *mxssa.Global:
  97  			globals++
  98  		case *mxssa.Type_:
  99  			types_++
 100  		case *mxssa.NamedConst:
 101  			consts++
 102  		}
 103  	}
 104  	sort.Strings(funcNames)
 105  	fmt.Printf("  functions:     %d\n", funcs)
 106  	fmt.Printf("  globals:       %d\n", globals)
 107  	fmt.Printf("  types:         %d\n", types_)
 108  	fmt.Printf("  consts:        %d\n", consts)
 109  
 110  	// Print function block counts.
 111  	fmt.Println("\nfunctions:")
 112  	for _, name := range funcNames {
 113  		fn := ssaPkg.Members[name].(*mxssa.Function)
 114  		fmt.Printf("  %-30s  blocks=%d params=%d\n", name, len(fn.Blocks), len(fn.Params))
 115  	}
 116  }
 117  
 118  func fatalf(format string, args ...interface{}) {
 119  	fmt.Fprintf(os.Stderr, "mxssadump: "+format+"\n", args...)
 120  	os.Exit(1)
 121  }
 122  
 123  func newConfig(options *compileopts.Options) (*compileopts.Config, error) {
 124  	spec, err := compileopts.LoadTarget(options)
 125  	if err != nil {
 126  		return nil, err
 127  	}
 128  	_, gorootMinor, err := goenv.GetGorootVersion()
 129  	if err != nil {
 130  		return nil, err
 131  	}
 132  	_, buildMinor, _, err := goenv.Parse(runtime.Version())
 133  	if err != nil {
 134  		return nil, err
 135  	}
 136  	if buildMinor < gorootMinor {
 137  		return nil, fmt.Errorf("go toolchain mismatch: built with %s, running %d.%d",
 138  			runtime.Version(), 1, gorootMinor)
 139  	}
 140  	return &compileopts.Config{
 141  		Options:        options,
 142  		Target:         spec,
 143  		GoMinorVersion: gorootMinor,
 144  		TestConfig:     options.TestConfig,
 145  	}, nil
 146  }
 147  
 148  func isMainNameError(msg string) bool {
 149  	return strings.Contains(msg, "expected main package to have name")
 150  }
 151  
 152  var _ []*syntax.File
 153