github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/compile/internal/ssa/compile.go (about)

     1  // Copyright 2015 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  package ssa
     6  
     7  import (
     8  	"fmt"
     9  	"log"
    10  	"regexp"
    11  	"runtime"
    12  	"strings"
    13  	"time"
    14  )
    15  
    16  // Compile is the main entry point for this package.
    17  // Compile modifies f so that on return:
    18  //   · all Values in f map to 0 or 1 assembly instructions of the target architecture
    19  //   · the order of f.Blocks is the order to emit the Blocks
    20  //   · the order of b.Values is the order to emit the Values in each Block
    21  //   · f has a non-nil regAlloc field
    22  func Compile(f *Func) {
    23  	// TODO: debugging - set flags to control verbosity of compiler,
    24  	// which phases to dump IR before/after, etc.
    25  	if f.Log() {
    26  		f.Logf("compiling %s\n", f.Name)
    27  	}
    28  
    29  	// hook to print function & phase if panic happens
    30  	phaseName := "init"
    31  	defer func() {
    32  		if phaseName != "" {
    33  			err := recover()
    34  			stack := make([]byte, 16384)
    35  			n := runtime.Stack(stack, false)
    36  			stack = stack[:n]
    37  			f.Fatalf("panic during %s while compiling %s:\n\n%v\n\n%s\n", phaseName, f.Name, err, stack)
    38  		}
    39  	}()
    40  
    41  	// Run all the passes
    42  	printFunc(f)
    43  	f.Config.HTML.WriteFunc("start", f)
    44  	if checkEnabled {
    45  		checkFunc(f)
    46  	}
    47  	const logMemStats = false
    48  	for _, p := range passes {
    49  		if !f.Config.optimize && !p.required || p.disabled {
    50  			continue
    51  		}
    52  		f.pass = &p
    53  		phaseName = p.name
    54  		if f.Log() {
    55  			f.Logf("  pass %s begin\n", p.name)
    56  		}
    57  		// TODO: capture logging during this pass, add it to the HTML
    58  		var mStart runtime.MemStats
    59  		if logMemStats || p.mem {
    60  			runtime.ReadMemStats(&mStart)
    61  		}
    62  
    63  		tStart := time.Now()
    64  		p.fn(f)
    65  		tEnd := time.Now()
    66  
    67  		// Need something less crude than "Log the whole intermediate result".
    68  		if f.Log() || f.Config.HTML != nil {
    69  			time := tEnd.Sub(tStart).Nanoseconds()
    70  			var stats string
    71  			if logMemStats {
    72  				var mEnd runtime.MemStats
    73  				runtime.ReadMemStats(&mEnd)
    74  				nBytes := mEnd.TotalAlloc - mStart.TotalAlloc
    75  				nAllocs := mEnd.Mallocs - mStart.Mallocs
    76  				stats = fmt.Sprintf("[%d ns %d allocs %d bytes]", time, nAllocs, nBytes)
    77  			} else {
    78  				stats = fmt.Sprintf("[%d ns]", time)
    79  			}
    80  
    81  			f.Logf("  pass %s end %s\n", p.name, stats)
    82  			printFunc(f)
    83  			f.Config.HTML.WriteFunc(fmt.Sprintf("after %s <span class=\"stats\">%s</span>", phaseName, stats), f)
    84  		}
    85  		if p.time || p.mem {
    86  			// Surround timing information w/ enough context to allow comparisons.
    87  			time := tEnd.Sub(tStart).Nanoseconds()
    88  			if p.time {
    89  				f.logStat("TIME(ns)", time)
    90  			}
    91  			if p.mem {
    92  				var mEnd runtime.MemStats
    93  				runtime.ReadMemStats(&mEnd)
    94  				nBytes := mEnd.TotalAlloc - mStart.TotalAlloc
    95  				nAllocs := mEnd.Mallocs - mStart.Mallocs
    96  				f.logStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs)
    97  			}
    98  		}
    99  		if checkEnabled {
   100  			checkFunc(f)
   101  		}
   102  	}
   103  
   104  	// Squash error printing defer
   105  	phaseName = ""
   106  }
   107  
   108  type pass struct {
   109  	name     string
   110  	fn       func(*Func)
   111  	required bool
   112  	disabled bool
   113  	time     bool // report time to run pass
   114  	mem      bool // report mem stats to run pass
   115  	stats    int  // pass reports own "stats" (e.g., branches removed)
   116  	debug    int  // pass performs some debugging. =1 should be in error-testing-friendly Warnl format.
   117  	test     int  // pass-specific ad-hoc option, perhaps useful in development
   118  }
   119  
   120  // Run consistency checker between each phase
   121  var checkEnabled = false
   122  
   123  // Debug output
   124  var IntrinsicsDebug int
   125  var IntrinsicsDisable bool
   126  
   127  // PhaseOption sets the specified flag in the specified ssa phase,
   128  // returning empty string if this was successful or a string explaining
   129  // the error if it was not.
   130  // A version of the phase name with "_" replaced by " " is also checked for a match.
   131  // If the phase name begins a '~' then the rest of the underscores-replaced-with-blanks
   132  // version is used as a regular expression to match the phase name(s).
   133  //
   134  // Special cases that have turned out to be useful:
   135  //  ssa/check/on enables checking after each phase
   136  //  ssa/all/time enables time reporting for all phases
   137  //
   138  // See gc/lex.go for dissection of the option string.
   139  // Example uses:
   140  //
   141  // GO_GCFLAGS=-d=ssa/generic_cse/time,ssa/generic_cse/stats,ssa/generic_cse/debug=3 ./make.bash
   142  //
   143  // BOOT_GO_GCFLAGS=-d='ssa/~^.*scc$/off' GO_GCFLAGS='-d=ssa/~^.*scc$/off' ./make.bash
   144  //
   145  func PhaseOption(phase, flag string, val int) string {
   146  	if phase == "check" && flag == "on" {
   147  		checkEnabled = val != 0
   148  		return ""
   149  	}
   150  	if phase == "check" && flag == "off" {
   151  		checkEnabled = val == 0
   152  		return ""
   153  	}
   154  
   155  	alltime := false
   156  	if phase == "all" {
   157  		if flag == "time" {
   158  			alltime = val != 0
   159  		} else {
   160  			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
   161  		}
   162  	}
   163  
   164  	if phase == "intrinsics" {
   165  		switch flag {
   166  		case "on":
   167  			IntrinsicsDisable = val == 0
   168  		case "off":
   169  			IntrinsicsDisable = val != 0
   170  		case "debug":
   171  			IntrinsicsDebug = val
   172  		default:
   173  			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
   174  		}
   175  		return ""
   176  	}
   177  
   178  	underphase := strings.Replace(phase, "_", " ", -1)
   179  	var re *regexp.Regexp
   180  	if phase[0] == '~' {
   181  		r, ok := regexp.Compile(underphase[1:])
   182  		if ok != nil {
   183  			return fmt.Sprintf("Error %s in regexp for phase %s, flag %s", ok.Error(), phase, flag)
   184  		}
   185  		re = r
   186  	}
   187  	matchedOne := false
   188  	for i, p := range passes {
   189  		if phase == "all" {
   190  			p.time = alltime
   191  			passes[i] = p
   192  			matchedOne = true
   193  		} else if p.name == phase || p.name == underphase || re != nil && re.MatchString(p.name) {
   194  			switch flag {
   195  			case "on":
   196  				p.disabled = val == 0
   197  			case "off":
   198  				p.disabled = val != 0
   199  			case "time":
   200  				p.time = val != 0
   201  			case "mem":
   202  				p.mem = val != 0
   203  			case "debug":
   204  				p.debug = val
   205  			case "stats":
   206  				p.stats = val
   207  			case "test":
   208  				p.test = val
   209  			default:
   210  				return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
   211  			}
   212  			if p.disabled && p.required {
   213  				return fmt.Sprintf("Cannot disable required SSA phase %s using -d=ssa/%s debug option", phase, phase)
   214  			}
   215  			passes[i] = p
   216  			matchedOne = true
   217  		}
   218  	}
   219  	if matchedOne {
   220  		return ""
   221  	}
   222  	return fmt.Sprintf("Did not find a phase matching %s in -d=ssa/... debug option", phase)
   223  }
   224  
   225  // list of passes for the compiler
   226  var passes = [...]pass{
   227  	// TODO: combine phielim and copyelim into a single pass?
   228  	{name: "early phielim", fn: phielim},
   229  	{name: "early copyelim", fn: copyelim},
   230  	{name: "early deadcode", fn: deadcode}, // remove generated dead code to avoid doing pointless work during opt
   231  	{name: "short circuit", fn: shortcircuit},
   232  	{name: "decompose user", fn: decomposeUser, required: true},
   233  	{name: "opt", fn: opt, required: true},           // TODO: split required rules and optimizing rules
   234  	{name: "zero arg cse", fn: zcse, required: true}, // required to merge OpSB values
   235  	{name: "opt deadcode", fn: deadcode},             // remove any blocks orphaned during opt
   236  	{name: "generic cse", fn: cse},
   237  	{name: "phiopt", fn: phiopt},
   238  	{name: "nilcheckelim", fn: nilcheckelim},
   239  	{name: "prove", fn: prove},
   240  	{name: "loopbce", fn: loopbce},
   241  	{name: "decompose builtin", fn: decomposeBuiltIn, required: true},
   242  	{name: "dec", fn: dec, required: true},
   243  	{name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules
   244  	{name: "generic deadcode", fn: deadcode},
   245  	{name: "check bce", fn: checkbce},
   246  	{name: "fuse", fn: fuse},
   247  	{name: "dse", fn: dse},
   248  	{name: "tighten", fn: tighten}, // move values closer to their uses
   249  	{name: "lower", fn: lower, required: true},
   250  	{name: "lowered cse", fn: cse},
   251  	{name: "lowered deadcode", fn: deadcode, required: true},
   252  	{name: "checkLower", fn: checkLower, required: true},
   253  	{name: "late phielim", fn: phielim},
   254  	{name: "late copyelim", fn: copyelim},
   255  	{name: "late deadcode", fn: deadcode},
   256  	{name: "critical", fn: critical, required: true}, // remove critical edges
   257  	{name: "likelyadjust", fn: likelyadjust},
   258  	{name: "layout", fn: layout, required: true},       // schedule blocks
   259  	{name: "schedule", fn: schedule, required: true},   // schedule values
   260  	{name: "flagalloc", fn: flagalloc, required: true}, // allocate flags register
   261  	{name: "regalloc", fn: regalloc, required: true},   // allocate int & float registers + stack slots
   262  	{name: "trim", fn: trim},                           // remove empty blocks
   263  }
   264  
   265  // Double-check phase ordering constraints.
   266  // This code is intended to document the ordering requirements
   267  // between different phases. It does not override the passes
   268  // list above.
   269  type constraint struct {
   270  	a, b string // a must come before b
   271  }
   272  
   273  var passOrder = [...]constraint{
   274  	// prove reliese on common-subexpression elimination for maximum benefits.
   275  	{"generic cse", "prove"},
   276  	// deadcode after prove to eliminate all new dead blocks.
   277  	{"prove", "generic deadcode"},
   278  	// common-subexpression before dead-store elim, so that we recognize
   279  	// when two address expressions are the same.
   280  	{"generic cse", "dse"},
   281  	// cse substantially improves nilcheckelim efficacy
   282  	{"generic cse", "nilcheckelim"},
   283  	// allow deadcode to clean up after nilcheckelim
   284  	{"nilcheckelim", "generic deadcode"},
   285  	// nilcheckelim generates sequences of plain basic blocks
   286  	{"nilcheckelim", "fuse"},
   287  	// nilcheckelim relies on opt to rewrite user nil checks
   288  	{"opt", "nilcheckelim"},
   289  	// tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET
   290  	{"tighten", "lower"},
   291  	// tighten will be most effective when as many values have been removed as possible
   292  	{"generic deadcode", "tighten"},
   293  	{"generic cse", "tighten"},
   294  	// checkbce needs the values removed
   295  	{"generic deadcode", "check bce"},
   296  	// don't run optimization pass until we've decomposed builtin objects
   297  	{"decompose builtin", "late opt"},
   298  	// don't layout blocks until critical edges have been removed
   299  	{"critical", "layout"},
   300  	// regalloc requires the removal of all critical edges
   301  	{"critical", "regalloc"},
   302  	// regalloc requires all the values in a block to be scheduled
   303  	{"schedule", "regalloc"},
   304  	// checkLower must run after lowering & subsequent dead code elim
   305  	{"lower", "checkLower"},
   306  	{"lowered deadcode", "checkLower"},
   307  	// flagalloc needs instructions to be scheduled.
   308  	{"schedule", "flagalloc"},
   309  	// regalloc needs flags to be allocated first.
   310  	{"flagalloc", "regalloc"},
   311  	// trim needs regalloc to be done first.
   312  	{"regalloc", "trim"},
   313  }
   314  
   315  func init() {
   316  	for _, c := range passOrder {
   317  		a, b := c.a, c.b
   318  		i := -1
   319  		j := -1
   320  		for k, p := range passes {
   321  			if p.name == a {
   322  				i = k
   323  			}
   324  			if p.name == b {
   325  				j = k
   326  			}
   327  		}
   328  		if i < 0 {
   329  			log.Panicf("pass %s not found", a)
   330  		}
   331  		if j < 0 {
   332  			log.Panicf("pass %s not found", b)
   333  		}
   334  		if i >= j {
   335  			log.Panicf("passes %s and %s out of order", a, b)
   336  		}
   337  	}
   338  }