github.com/euank/go@v0.0.0-20160829210321-495514729181/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  var BuildDebug int
   128  var BuildTest int
   129  var BuildStats int
   130  
   131  // PhaseOption sets the specified flag in the specified ssa phase,
   132  // returning empty string if this was successful or a string explaining
   133  // the error if it was not.
   134  // A version of the phase name with "_" replaced by " " is also checked for a match.
   135  // If the phase name begins a '~' then the rest of the underscores-replaced-with-blanks
   136  // version is used as a regular expression to match the phase name(s).
   137  //
   138  // Special cases that have turned out to be useful:
   139  //  ssa/check/on enables checking after each phase
   140  //  ssa/all/time enables time reporting for all phases
   141  //
   142  // See gc/lex.go for dissection of the option string.
   143  // Example uses:
   144  //
   145  // GO_GCFLAGS=-d=ssa/generic_cse/time,ssa/generic_cse/stats,ssa/generic_cse/debug=3 ./make.bash
   146  //
   147  // BOOT_GO_GCFLAGS=-d='ssa/~^.*scc$/off' GO_GCFLAGS='-d=ssa/~^.*scc$/off' ./make.bash
   148  //
   149  func PhaseOption(phase, flag string, val int) string {
   150  	if phase == "check" && flag == "on" {
   151  		checkEnabled = val != 0
   152  		return ""
   153  	}
   154  	if phase == "check" && flag == "off" {
   155  		checkEnabled = val == 0
   156  		return ""
   157  	}
   158  
   159  	alltime := false
   160  	if phase == "all" {
   161  		if flag == "time" {
   162  			alltime = val != 0
   163  		} else {
   164  			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
   165  		}
   166  	}
   167  
   168  	if phase == "intrinsics" {
   169  		switch flag {
   170  		case "on":
   171  			IntrinsicsDisable = val == 0
   172  		case "off":
   173  			IntrinsicsDisable = val != 0
   174  		case "debug":
   175  			IntrinsicsDebug = val
   176  		default:
   177  			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
   178  		}
   179  		return ""
   180  	}
   181  	if phase == "build" {
   182  		switch flag {
   183  		case "debug":
   184  			BuildDebug = val
   185  		case "test":
   186  			BuildTest = val
   187  		case "stats":
   188  			BuildStats = val
   189  		default:
   190  			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
   191  		}
   192  		return ""
   193  	}
   194  
   195  	underphase := strings.Replace(phase, "_", " ", -1)
   196  	var re *regexp.Regexp
   197  	if phase[0] == '~' {
   198  		r, ok := regexp.Compile(underphase[1:])
   199  		if ok != nil {
   200  			return fmt.Sprintf("Error %s in regexp for phase %s, flag %s", ok.Error(), phase, flag)
   201  		}
   202  		re = r
   203  	}
   204  	matchedOne := false
   205  	for i, p := range passes {
   206  		if phase == "all" {
   207  			p.time = alltime
   208  			passes[i] = p
   209  			matchedOne = true
   210  		} else if p.name == phase || p.name == underphase || re != nil && re.MatchString(p.name) {
   211  			switch flag {
   212  			case "on":
   213  				p.disabled = val == 0
   214  			case "off":
   215  				p.disabled = val != 0
   216  			case "time":
   217  				p.time = val != 0
   218  			case "mem":
   219  				p.mem = val != 0
   220  			case "debug":
   221  				p.debug = val
   222  			case "stats":
   223  				p.stats = val
   224  			case "test":
   225  				p.test = val
   226  			default:
   227  				return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
   228  			}
   229  			if p.disabled && p.required {
   230  				return fmt.Sprintf("Cannot disable required SSA phase %s using -d=ssa/%s debug option", phase, phase)
   231  			}
   232  			passes[i] = p
   233  			matchedOne = true
   234  		}
   235  	}
   236  	if matchedOne {
   237  		return ""
   238  	}
   239  	return fmt.Sprintf("Did not find a phase matching %s in -d=ssa/... debug option", phase)
   240  }
   241  
   242  // list of passes for the compiler
   243  var passes = [...]pass{
   244  	// TODO: combine phielim and copyelim into a single pass?
   245  	{name: "early phielim", fn: phielim},
   246  	{name: "early copyelim", fn: copyelim},
   247  	{name: "early deadcode", fn: deadcode}, // remove generated dead code to avoid doing pointless work during opt
   248  	{name: "short circuit", fn: shortcircuit},
   249  	{name: "decompose user", fn: decomposeUser, required: true},
   250  	{name: "opt", fn: opt, required: true},               // TODO: split required rules and optimizing rules
   251  	{name: "zero arg cse", fn: zcse, required: true},     // required to merge OpSB values
   252  	{name: "opt deadcode", fn: deadcode, required: true}, // remove any blocks orphaned during opt
   253  	{name: "generic domtree", fn: domTree},
   254  	{name: "generic cse", fn: cse},
   255  	{name: "phiopt", fn: phiopt},
   256  	{name: "nilcheckelim", fn: nilcheckelim},
   257  	{name: "prove", fn: prove},
   258  	{name: "loopbce", fn: loopbce},
   259  	{name: "decompose builtin", fn: decomposeBuiltIn, required: true},
   260  	{name: "dec", fn: dec, required: true},
   261  	{name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules
   262  	{name: "generic deadcode", fn: deadcode},
   263  	{name: "check bce", fn: checkbce},
   264  	{name: "fuse", fn: fuse},
   265  	{name: "dse", fn: dse},
   266  	{name: "tighten", fn: tighten}, // move values closer to their uses
   267  	{name: "lower", fn: lower, required: true},
   268  	{name: "lowered cse", fn: cse},
   269  	{name: "lowered deadcode", fn: deadcode, required: true},
   270  	{name: "checkLower", fn: checkLower, required: true},
   271  	{name: "late phielim", fn: phielim},
   272  	{name: "late copyelim", fn: copyelim},
   273  	{name: "phi tighten", fn: phiTighten},
   274  	{name: "late deadcode", fn: deadcode},
   275  	{name: "critical", fn: critical, required: true}, // remove critical edges
   276  	{name: "likelyadjust", fn: likelyadjust},
   277  	{name: "layout", fn: layout, required: true},       // schedule blocks
   278  	{name: "schedule", fn: schedule, required: true},   // schedule values
   279  	{name: "flagalloc", fn: flagalloc, required: true}, // allocate flags register
   280  	{name: "regalloc", fn: regalloc, required: true},   // allocate int & float registers + stack slots
   281  	{name: "trim", fn: trim},                           // remove empty blocks
   282  }
   283  
   284  // Double-check phase ordering constraints.
   285  // This code is intended to document the ordering requirements
   286  // between different phases. It does not override the passes
   287  // list above.
   288  type constraint struct {
   289  	a, b string // a must come before b
   290  }
   291  
   292  var passOrder = [...]constraint{
   293  	// prove reliese on common-subexpression elimination for maximum benefits.
   294  	{"generic cse", "prove"},
   295  	// deadcode after prove to eliminate all new dead blocks.
   296  	{"prove", "generic deadcode"},
   297  	// common-subexpression before dead-store elim, so that we recognize
   298  	// when two address expressions are the same.
   299  	{"generic cse", "dse"},
   300  	// cse substantially improves nilcheckelim efficacy
   301  	{"generic cse", "nilcheckelim"},
   302  	// allow deadcode to clean up after nilcheckelim
   303  	{"nilcheckelim", "generic deadcode"},
   304  	// nilcheckelim generates sequences of plain basic blocks
   305  	{"nilcheckelim", "fuse"},
   306  	// nilcheckelim relies on opt to rewrite user nil checks
   307  	{"opt", "nilcheckelim"},
   308  	// tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET
   309  	{"tighten", "lower"},
   310  	// cse, phiopt, nilcheckelim, prove and loopbce share idom.
   311  	{"generic domtree", "generic cse"},
   312  	{"generic domtree", "phiopt"},
   313  	{"generic domtree", "nilcheckelim"},
   314  	{"generic domtree", "prove"},
   315  	{"generic domtree", "loopbce"},
   316  	// tighten will be most effective when as many values have been removed as possible
   317  	{"generic deadcode", "tighten"},
   318  	{"generic cse", "tighten"},
   319  	// checkbce needs the values removed
   320  	{"generic deadcode", "check bce"},
   321  	// don't run optimization pass until we've decomposed builtin objects
   322  	{"decompose builtin", "late opt"},
   323  	// don't layout blocks until critical edges have been removed
   324  	{"critical", "layout"},
   325  	// regalloc requires the removal of all critical edges
   326  	{"critical", "regalloc"},
   327  	// regalloc requires all the values in a block to be scheduled
   328  	{"schedule", "regalloc"},
   329  	// checkLower must run after lowering & subsequent dead code elim
   330  	{"lower", "checkLower"},
   331  	{"lowered deadcode", "checkLower"},
   332  	// flagalloc needs instructions to be scheduled.
   333  	{"schedule", "flagalloc"},
   334  	// regalloc needs flags to be allocated first.
   335  	{"flagalloc", "regalloc"},
   336  	// trim needs regalloc to be done first.
   337  	{"regalloc", "trim"},
   338  }
   339  
   340  func init() {
   341  	for _, c := range passOrder {
   342  		a, b := c.a, c.b
   343  		i := -1
   344  		j := -1
   345  		for k, p := range passes {
   346  			if p.name == a {
   347  				i = k
   348  			}
   349  			if p.name == b {
   350  				j = k
   351  			}
   352  		}
   353  		if i < 0 {
   354  			log.Panicf("pass %s not found", a)
   355  		}
   356  		if j < 0 {
   357  			log.Panicf("pass %s not found", b)
   358  		}
   359  		if i >= j {
   360  			log.Panicf("passes %s and %s out of order", a, b)
   361  		}
   362  	}
   363  }