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

View as plain text