Source file src/cmd/compile/internal/ssacompile/compile.go

     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 ssacompile
     6  
     7  import (
     8  	"fmt"
     9  	"hash/crc32"
    10  	"internal/buildcfg"
    11  	"log"
    12  	"math/rand"
    13  	"regexp"
    14  	"runtime"
    15  	"sort"
    16  	"strconv"
    17  	"strings"
    18  	"time"
    19  
    20  	"cmd/compile/internal/base"
    21  	"cmd/compile/internal/ssa"
    22  	"cmd/compile/internal/ssa/ssaconfig"
    23  )
    24  
    25  // Compiler satisfies the ssacore.Compiler interface.
    26  type Compiler struct{}
    27  
    28  func (_ Compiler) Passes() []ssa.Pass {
    29  	return passes[:]
    30  }
    31  
    32  // Compile is the main entry point for this package.
    33  // Compile modifies f so that on return:
    34  //   - all Values in f map to 0 or 1 assembly instructions of the target architecture
    35  //   - the order of f.Blocks is the order to emit the Blocks
    36  //   - the order of b.Values is the order to emit the Values in each Block
    37  //   - f has a non-nil regAlloc field
    38  func (_ Compiler) Compile(f *ssa.Func, htmlWriter ssa.HTMLWriter) {
    39  	// TODO: debugging - set flags to control verbosity of compiler,
    40  	// which phases to dump IR before/after, etc.
    41  	if f.Log() {
    42  		f.Logf("compiling %s\n", f.Name)
    43  	}
    44  
    45  	var rnd *rand.Rand
    46  	if checkEnabled {
    47  		seed := int64(crc32.ChecksumIEEE(([]byte)(f.Name))) ^ int64(checkRandSeed)
    48  		rnd = rand.New(rand.NewSource(seed))
    49  	}
    50  
    51  	// hook to print function & phase if panic happens
    52  	phaseName := "init"
    53  	defer func() {
    54  		if phaseName != "" {
    55  			err := recover()
    56  			stack := make([]byte, 16384)
    57  			n := runtime.Stack(stack, false)
    58  			stack = stack[:n]
    59  			if htmlWriter != nil {
    60  				htmlWriter.FlushPhases()
    61  			}
    62  			f.Fatalf("panic during %s while compiling %s:\n\n%v\n\n%s\n", phaseName, f.Name, err, stack)
    63  		}
    64  	}()
    65  
    66  	// Run all the passes
    67  	if f.Log() {
    68  		ssa.PrintFunc(f)
    69  	}
    70  	htmlWriter.WritePhase("start", "start")
    71  	if ssaconfig.BuildDump[f.Name] {
    72  		f.DumpFile("build")
    73  	}
    74  	if checkEnabled {
    75  		checkFunc(f)
    76  	}
    77  	const logMemStats = false
    78  	for _, p := range passes {
    79  		if !f.Config.Optimize && !p.Required || p.Disabled {
    80  			continue
    81  		}
    82  		f.Pass = &p
    83  		phaseName = p.Name
    84  		if f.Log() {
    85  			f.Logf("  pass %s begin\n", p.Name)
    86  		}
    87  		// TODO: capture logging during this pass, add it to the HTML
    88  		var mStart runtime.MemStats
    89  		if logMemStats || p.Mem {
    90  			runtime.ReadMemStats(&mStart)
    91  		}
    92  
    93  		if checkEnabled && !f.Scheduled {
    94  			// Test that we don't depend on the value order, by randomizing
    95  			// the order of values in each block. See issue 18169.
    96  			for _, b := range f.Blocks {
    97  				for i := 0; i < len(b.Values)-1; i++ {
    98  					j := i + rnd.Intn(len(b.Values)-i)
    99  					b.Values[i], b.Values[j] = b.Values[j], b.Values[i]
   100  				}
   101  			}
   102  		}
   103  
   104  		tStart := time.Now()
   105  		p.Fn(f)
   106  		tEnd := time.Now()
   107  
   108  		// Need something less crude than "Log the whole intermediate result".
   109  		if f.Log() || htmlWriter != nil {
   110  			time := tEnd.Sub(tStart).Nanoseconds()
   111  			var stats string
   112  			if logMemStats {
   113  				var mEnd runtime.MemStats
   114  				runtime.ReadMemStats(&mEnd)
   115  				nBytes := mEnd.TotalAlloc - mStart.TotalAlloc
   116  				nAllocs := mEnd.Mallocs - mStart.Mallocs
   117  				stats = fmt.Sprintf("[%d ns %d allocs %d bytes]", time, nAllocs, nBytes)
   118  			} else {
   119  				stats = fmt.Sprintf("[%d ns]", time)
   120  			}
   121  
   122  			if f.Log() {
   123  				f.Logf("  pass %s end %s\n", p.Name, stats)
   124  				ssa.PrintFunc(f)
   125  			}
   126  			htmlWriter.WritePhase(phaseName, fmt.Sprintf("%s <span class=\"stats\">%s</span>", phaseName, stats))
   127  		}
   128  		if p.Time || p.Mem {
   129  			// Surround timing information w/ enough context to allow comparisons.
   130  			time := tEnd.Sub(tStart).Nanoseconds()
   131  			if p.Time {
   132  				f.LogStat("TIME(ns)", time)
   133  			}
   134  			if p.Mem {
   135  				var mEnd runtime.MemStats
   136  				runtime.ReadMemStats(&mEnd)
   137  				nBytes := mEnd.TotalAlloc - mStart.TotalAlloc
   138  				nAllocs := mEnd.Mallocs - mStart.Mallocs
   139  				f.LogStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs)
   140  			}
   141  		}
   142  		if p.Dump != nil && p.Dump[f.Name] {
   143  			// Dump function to appropriately named file
   144  			f.DumpFile(phaseName)
   145  		}
   146  		if checkEnabled {
   147  			checkFunc(f)
   148  		}
   149  	}
   150  
   151  	if htmlWriter != nil {
   152  		// Ensure we write any pending phases to the html
   153  		htmlWriter.FlushPhases()
   154  	}
   155  
   156  	if f.RuleMatches != nil {
   157  		var keys []string
   158  		for key := range f.RuleMatches {
   159  			keys = append(keys, key)
   160  		}
   161  		sort.Strings(keys)
   162  		buf := new(strings.Builder)
   163  		fmt.Fprintf(buf, "%s: ", f.Name)
   164  		for _, key := range keys {
   165  			fmt.Fprintf(buf, "%s=%d ", key, f.RuleMatches[key])
   166  		}
   167  		fmt.Fprint(buf, "\n")
   168  		fmt.Print(buf.String())
   169  	}
   170  
   171  	// Squash error printing defer
   172  	phaseName = ""
   173  }
   174  
   175  // Run consistency checker between each phase
   176  var (
   177  	checkEnabled  = false
   178  	checkRandSeed = 0
   179  )
   180  
   181  // PhaseOption sets the specified flag in the specified ssa phase,
   182  // returning empty string if this was successful or a string explaining
   183  // the error if it was not.
   184  // A version of the phase name with "_" replaced by " " is also checked for a match.
   185  // If the phase name begins a '~' then the rest of the underscores-replaced-with-blanks
   186  // version is used as a regular expression to match the phase name(s).
   187  //
   188  // Special cases that have turned out to be useful:
   189  //   - ssa/check/on enables checking after each phase
   190  //   - ssa/all/time enables time reporting for all phases
   191  //
   192  // See gc/lex.go for dissection of the option string.
   193  // Example uses:
   194  //
   195  // GO_GCFLAGS=-d=ssa/generic_cse/time,ssa/generic_cse/stats,ssa/generic_cse/debug=3 ./make.bash
   196  //
   197  // BOOT_GO_GCFLAGS=-d='ssa/~^.*scc$/off' GO_GCFLAGS='-d=ssa/~^.*scc$/off' ./make.bash
   198  func PhaseOption(phase, flag string, val int, valString string) string {
   199  	switch phase {
   200  	case "", "help":
   201  		lastcr := 0
   202  		phasenames := "    check, all, build, intrinsics, genssa"
   203  		for _, p := range passes {
   204  			pn := strings.ReplaceAll(p.Name, " ", "_")
   205  			if len(pn)+len(phasenames)-lastcr > 70 {
   206  				phasenames += "\n    "
   207  				lastcr = len(phasenames)
   208  				phasenames += pn
   209  			} else {
   210  				phasenames += ", " + pn
   211  			}
   212  		}
   213  		return `PhaseOptions usage:
   214  
   215      go tool compile -d=ssa/<phase>/<flag>[=<value>|<function_name>]
   216  
   217  where:
   218  
   219  - <phase> is one of:
   220  ` + phasenames + `
   221  
   222  - <flag> is one of:
   223      on, off, debug, mem, time, test, stats, dump, seed, @<keyword>
   224  
   225  - <value> defaults to 1
   226  
   227  - <function_name> is required for the "dump" flag, and specifies the
   228    name of function to dump after <phase>
   229  
   230  Phase "all" supports flags "time", "mem", and "dump".
   231  Phase "intrinsics" supports flags "on", "off", and "debug".
   232  Phase "genssa" (assembly generation) supports the flag "dump".
   233  
   234  If the "dump" flag is specified, the output is written on a file named
   235  <phase>__<function_name>_<seq>.dump; otherwise it is directed to stdout.
   236  
   237  Examples:
   238  
   239      -d=ssa/check/on
   240  enables checking after each phase
   241  
   242  	-d=ssa/check/seed=1234
   243  enables checking after each phase, using 1234 to seed the PRNG
   244  used for value order randomization
   245  
   246      -d=ssa/all/time
   247  enables time reporting for all phases
   248  
   249      -d=ssa/prove/debug=2
   250  sets debugging level to 2 in the prove pass
   251  
   252  Be aware that when "/debug=X" is applied to a pass, some passes
   253  will emit debug output for all functions, and other passes will
   254  only emit debug output for functions that match the current
   255  GOSSAFUNC value.
   256  
   257  Multiple flags can be passed at once, by separating them with
   258  commas. For example:
   259  
   260      -d=ssa/check/on,ssa/all/time
   261  `
   262  	}
   263  
   264  	if phase == "check" {
   265  		switch flag {
   266  		case "on":
   267  			checkEnabled = val != 0
   268  			ssa.DebugPoset = checkEnabled // also turn on advanced self-checking in prove's data structure
   269  			return ""
   270  		case "off":
   271  			checkEnabled = val == 0
   272  			ssa.DebugPoset = checkEnabled
   273  			return ""
   274  		case "seed":
   275  			checkEnabled = true
   276  			checkRandSeed = val
   277  			ssa.DebugPoset = checkEnabled
   278  			return ""
   279  		}
   280  	}
   281  
   282  	alltime := false
   283  	allmem := false
   284  	alldump := false
   285  	if phase == "all" {
   286  		switch flag {
   287  		case "time":
   288  			alltime = val != 0
   289  		case "mem":
   290  			allmem = val != 0
   291  		case "dump":
   292  			alldump = val != 0
   293  			if alldump {
   294  				ssaconfig.BuildDump[valString] = true
   295  				ssaconfig.GenssaDump[valString] = true
   296  			}
   297  		default:
   298  			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/all/{time,mem,dump=function_name})", flag, phase)
   299  		}
   300  	}
   301  
   302  	if phase == "intrinsics" {
   303  		switch flag {
   304  		case "on":
   305  			ssaconfig.IntrinsicsDisable = val == 0
   306  		case "off":
   307  			ssaconfig.IntrinsicsDisable = val != 0
   308  		case "debug":
   309  			ssaconfig.IntrinsicsDebug = val
   310  		default:
   311  			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/intrinsics/{on,off,debug})", flag, phase)
   312  		}
   313  		return ""
   314  	}
   315  	if phase == "build" {
   316  		switch flag {
   317  		case "debug":
   318  			ssaconfig.BuildDebug = val
   319  		case "test":
   320  			ssaconfig.BuildTest = val
   321  		case "stats":
   322  			ssaconfig.BuildStats = val
   323  		case "dump":
   324  			ssaconfig.BuildDump[valString] = true
   325  		default:
   326  			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/build/{debug,test,stats,dump=function_name})", flag, phase)
   327  		}
   328  		return ""
   329  	}
   330  	if phase == "genssa" {
   331  		switch flag {
   332  		case "dump":
   333  			ssaconfig.GenssaDump[valString] = true
   334  		default:
   335  			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/genssa/dump=function_name)", flag, phase)
   336  		}
   337  		return ""
   338  	}
   339  
   340  	underphase := strings.ReplaceAll(phase, "_", " ")
   341  	var re *regexp.Regexp
   342  	if phase[0] == '~' {
   343  		r, ok := regexp.Compile(underphase[1:])
   344  		if ok != nil {
   345  			return fmt.Sprintf("Error %s in regexp for phase %s, flag %s", ok.Error(), phase, flag)
   346  		}
   347  		re = r
   348  	}
   349  	matchedOne := false
   350  	for i, p := range passes {
   351  		if phase == "all" {
   352  			p.Time = alltime
   353  			p.Mem = allmem
   354  			if alldump {
   355  				p.AddDump(valString)
   356  			}
   357  			passes[i] = p
   358  			matchedOne = true
   359  		} else if p.Name == phase || p.Name == underphase || re != nil && re.MatchString(p.Name) {
   360  			switch flag {
   361  			case "on":
   362  				p.Disabled = val == 0
   363  			case "off":
   364  				p.Disabled = val != 0
   365  			case "time":
   366  				p.Time = val != 0
   367  			case "mem":
   368  				p.Mem = val != 0
   369  			case "debug":
   370  				p.Debug = val
   371  			case "stats":
   372  				p.Stats = val
   373  			case "test":
   374  				p.Test = val
   375  			case "dump":
   376  				p.AddDump(valString)
   377  			default:
   378  				if flag != "" && flag[0] == '@' {
   379  					if p.Keywords == nil {
   380  						p.Keywords = make(map[string]int64)
   381  						p.UsedKW = make(map[string]bool)
   382  					}
   383  					val64, err := strconv.ParseInt(valString, 10, 64)
   384  					if err != nil {
   385  						return fmt.Sprintf("Failed to parse %s as integer value in -d=ssa/%s/%s=%s option", valString, phase, flag, valString)
   386  					}
   387  					p.Keywords[flag[1:]] = int64(val64)
   388  				} else {
   389  					return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
   390  				}
   391  			}
   392  			if p.Disabled && p.Required {
   393  				return fmt.Sprintf("Cannot disable required SSA phase %s using -d=ssa/%s debug option", phase, phase)
   394  			}
   395  			passes[i] = p
   396  			matchedOne = true
   397  		}
   398  	}
   399  	if matchedOne {
   400  		return ""
   401  	}
   402  	return fmt.Sprintf("Did not find a phase matching %s in -d=ssa/... debug option", phase)
   403  }
   404  
   405  // list of passes for the compiler
   406  var passes = [...]ssa.Pass{
   407  	{Name: "number lines", Fn: numberLines, Required: true},
   408  	{Name: "early phielim and copyelim", Fn: copyelim},
   409  	{Name: "early deadcode", Fn: deadcode}, // remove generated dead code to avoid doing pointless work during opt
   410  	{Name: "short circuit", Fn: shortcircuit},
   411  	{Name: "decompose user", Fn: decomposeUser, Required: true},
   412  	{Name: "pre-opt deadcode", Fn: deadcode},
   413  	{Name: "opt", Fn: opt, Required: true},
   414  	{Name: "zero arg cse", Fn: zcse, Required: true},     // required to merge OpSB values
   415  	{Name: "opt deadcode", Fn: deadcode, Required: true}, // remove any blocks orphaned during opt
   416  	{Name: "generic cse", Fn: cse},
   417  	{Name: "phiopt", Fn: phiopt},
   418  	{Name: "gcse deadcode", Fn: deadcode, Required: true}, // clean out after cse and phiopt
   419  	{Name: "nilcheckelim", Fn: nilcheckelim},
   420  	{Name: "prove", Fn: prove},
   421  	{Name: "divisible", Fn: divisiblePass, Required: true},
   422  	{Name: "divmod", Fn: divmodPass, Required: true},
   423  	{Name: "middle opt", Fn: opt, Required: true},
   424  	{Name: "known bits", Fn: ssa.KnownBits},
   425  	{Name: "early fuse", Fn: fuseEarly},
   426  	{Name: "expand calls", Fn: expandCalls, Required: true},
   427  	{Name: "decompose builtin", Fn: postExpandCallsDecompose, Required: true},
   428  	{Name: "softfloat", Fn: softfloat, Required: true},
   429  	{Name: "branchelim", Fn: branchelim},
   430  	{Name: "late opt", Fn: opt, Required: true},
   431  	{Name: "dead auto elim", Fn: elimDeadAutosGeneric},
   432  	{Name: "sccp", Fn: sccp},
   433  	{Name: "generic deadcode", Fn: deadcode, Required: true}, // remove dead stores, which otherwise mess up store chain
   434  	{Name: "late fuse", Fn: fuseLate},
   435  	{Name: "check bce", Fn: checkbce},
   436  	{Name: "dse", Fn: dse},
   437  	{Name: "memcombine", Fn: memcombine},
   438  	{Name: "writebarrier", Fn: writebarrier, Required: true}, // expand write barrier ops
   439  	{Name: "insert resched checks", Fn: insertLoopReschedChecks,
   440  		Disabled: !buildcfg.Experiment.PreemptibleLoops}, // insert resched checks in loops.
   441  	{Name: "cpufeatures", Fn: cpufeatures, Required: buildcfg.Experiment.SIMD, Disabled: !buildcfg.Experiment.SIMD},
   442  	{Name: "rewrite tern", Fn: rewriteTern, Required: false, Disabled: !buildcfg.Experiment.SIMD},
   443  	{Name: "lower", Fn: lower, Required: true},
   444  	{Name: "addressing modes", Fn: addressingModes, Required: false},
   445  	{Name: "late lower", Fn: lateLower, Required: true},
   446  	{Name: "pair", Fn: pair},
   447  	{Name: "lowered deadcode for cse", Fn: deadcode}, // deadcode immediately before CSE avoids CSE making dead values live again
   448  	{Name: "lowered cse", Fn: cse},
   449  	{Name: "elim unread autos", Fn: elimUnreadAutos},
   450  	{Name: "tighten tuple selectors", Fn: tightenTupleSelectors, Required: true},
   451  	{Name: "lowered deadcode", Fn: deadcode, Required: true},
   452  	{Name: "checkLower", Fn: checkLower, Required: true},
   453  	{Name: "loop invariant", Fn: licm},
   454  	{Name: "late phielim and copyelim", Fn: copyelim},
   455  	{Name: "tighten", Fn: tighten, Required: true}, // move values closer to their uses
   456  	// TODO: fix 80102 and re-enable.
   457  	//{name: "merge conditional branches", fn: mergeConditionalBranches}, // generate conditional comparison instructions on ARM64 architecture
   458  	{Name: "late deadcode", Fn: deadcode},
   459  	{Name: "critical", Fn: critical, Required: true}, // remove critical edges
   460  	{Name: "phi tighten", Fn: phiTighten},            // place rematerializable phi args near uses to reduce value lifetimes
   461  	{Name: "likelyadjust", Fn: likelyadjust},
   462  	{Name: "layout", Fn: layout, Required: true},     // schedule blocks
   463  	{Name: "schedule", Fn: schedule, Required: true}, // schedule values
   464  	{Name: "late nilcheck", Fn: nilcheckelim2},
   465  	{Name: "flagalloc", Fn: flagalloc, Required: true}, // allocate flags register
   466  	{Name: "regalloc", Fn: regalloc, Required: true},   // allocate int & float registers + stack slots
   467  	{Name: "loop rotate", Fn: loopRotate},
   468  	{Name: "trim", Fn: trim}, // remove empty blocks
   469  }
   470  
   471  // Double-check phase ordering constraints.
   472  // This code is intended to document the ordering requirements
   473  // between different phases. It does not override the passes
   474  // list above.
   475  type constraint struct {
   476  	a, b string // a must come before b
   477  }
   478  
   479  var passOrder = [...]constraint{
   480  	// "insert resched checks" uses mem, better to clean out stores first.
   481  	{"dse", "insert resched checks"},
   482  	// insert resched checks adds new blocks containing generic instructions
   483  	{"insert resched checks", "lower"},
   484  	{"insert resched checks", "tighten"},
   485  
   486  	// prove relies on common-subexpression elimination for maximum benefits.
   487  	{"generic cse", "prove"},
   488  	// deadcode after prove to eliminate all new dead blocks.
   489  	{"prove", "generic deadcode"},
   490  	// divisible after prove to let prove analyze div and mod
   491  	{"prove", "divisible"},
   492  	// divmod after divisible to avoid rewriting subexpressions of ones divisible will handle
   493  	{"divisible", "divmod"},
   494  	// divmod before decompose builtin to handle 64-bit on 32-bit systems
   495  	{"divmod", "decompose builtin"},
   496  	// common-subexpression before dead-store elim, so that we recognize
   497  	// when two address expressions are the same.
   498  	{"generic cse", "dse"},
   499  	// cse substantially improves nilcheckelim efficacy
   500  	{"generic cse", "nilcheckelim"},
   501  	// allow deadcode to clean up after nilcheckelim
   502  	{"nilcheckelim", "generic deadcode"},
   503  	// nilcheckelim generates sequences of plain basic blocks
   504  	{"nilcheckelim", "late fuse"},
   505  	// nilcheckelim relies on the first opt to rewrite user nil checks
   506  	{"opt", "nilcheckelim"},
   507  	// tighten will be most effective when as many values have been removed as possible
   508  	{"generic deadcode", "tighten"},
   509  	{"generic cse", "tighten"},
   510  	// checkbce needs the values removed
   511  	{"generic deadcode", "check bce"},
   512  	// decompose builtin now also cleans up after expand calls
   513  	{"expand calls", "decompose builtin"},
   514  	// don't run optimization pass until we've decomposed builtin objects
   515  	{"decompose builtin", "late opt"},
   516  	// decompose builtin is the last pass that may introduce new float ops, so run softfloat after it
   517  	{"decompose builtin", "softfloat"},
   518  	// tuple selectors must be tightened to generators and de-duplicated before scheduling
   519  	{"tighten tuple selectors", "schedule"},
   520  	// remove critical edges before phi tighten, so that phi args get better placement
   521  	{"critical", "phi tighten"},
   522  	// don't layout blocks until critical edges have been removed
   523  	{"critical", "layout"},
   524  	// regalloc requires the removal of all critical edges
   525  	{"critical", "regalloc"},
   526  	// regalloc requires all the values in a block to be scheduled
   527  	{"schedule", "regalloc"},
   528  	// the rules in late lower run after the general rules.
   529  	{"lower", "late lower"},
   530  	// late lower may generate some values that need to be CSEed.
   531  	{"late lower", "lowered cse"},
   532  	// checkLower must run after lowering & subsequent dead code elim
   533  	{"lower", "checkLower"},
   534  	{"lowered deadcode", "checkLower"},
   535  	{"late lower", "checkLower"},
   536  	// late nilcheck needs instructions to be scheduled.
   537  	{"schedule", "late nilcheck"},
   538  	// flagalloc needs instructions to be scheduled.
   539  	{"schedule", "flagalloc"},
   540  	// regalloc needs flags to be allocated first.
   541  	{"flagalloc", "regalloc"},
   542  	// loopRotate will confuse regalloc.
   543  	{"regalloc", "loop rotate"},
   544  	// trim needs regalloc to be done first.
   545  	{"regalloc", "trim"},
   546  	// memcombine works better if fuse happens first, to help merge stores.
   547  	{"late fuse", "memcombine"},
   548  	// memcombine is a arch-independent pass.
   549  	{"memcombine", "lower"},
   550  	// late opt transform some CondSelects into math.
   551  	{"branchelim", "late opt"},
   552  	// branchelim is an arch-independent pass.
   553  	{"branchelim", "lower"},
   554  	// lower needs cpu feature information (for SIMD)
   555  	{"cpufeatures", "lower"},
   556  	// known bits is an arch-independent pass.
   557  	{"known bits", "lower"},
   558  	// known bits does very little except some fancy constant folding and we need opt to clean it up.
   559  	{"known bits", "late opt"},
   560  	// known bits does a better job once prove cleaned up some always taken and never taken branches.
   561  	// known bits also relies on the output to be mostly topo-sorted (for recursion limit purposes) which prove does.
   562  	{"prove", "known bits"},
   563  }
   564  
   565  func PostCompile() {
   566  	for _, c := range passes {
   567  		if c.Keywords != nil {
   568  			for k := range c.Keywords {
   569  				if !c.UsedKW[k] {
   570  					// If someone specified a debugging keyword that was not
   571  					// consumed, they might want to know about this.
   572  					base.Warn("Keyword %s for pass %s was not used", k, c.Name)
   573  				}
   574  			}
   575  		}
   576  	}
   577  }
   578  
   579  func init() {
   580  	for _, c := range passOrder {
   581  		a, b := c.a, c.b
   582  		i := -1
   583  		j := -1
   584  		for k, p := range passes {
   585  			if p.Name == a {
   586  				i = k
   587  			}
   588  			if p.Name == b {
   589  				j = k
   590  			}
   591  		}
   592  		if i < 0 {
   593  			log.Panicf("pass %s not found", a)
   594  		}
   595  		if j < 0 {
   596  			log.Panicf("pass %s not found", b)
   597  		}
   598  		if i >= j {
   599  			log.Panicf("passes %s and %s out of order", a, b)
   600  		}
   601  	}
   602  }
   603  

View as plain text