| // Copyright 2015 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package ssa | 
 |  | 
 | import ( | 
 | 	"cmd/internal/src" | 
 | 	"fmt" | 
 | 	"hash/crc32" | 
 | 	"internal/buildcfg" | 
 | 	"io" | 
 | 	"log" | 
 | 	"math/rand" | 
 | 	"os" | 
 | 	"path/filepath" | 
 | 	"regexp" | 
 | 	"runtime" | 
 | 	"sort" | 
 | 	"strings" | 
 | 	"time" | 
 | ) | 
 |  | 
 | // Compile is the main entry point for this package. | 
 | // Compile modifies f so that on return: | 
 | //   - all Values in f map to 0 or 1 assembly instructions of the target architecture | 
 | //   - the order of f.Blocks is the order to emit the Blocks | 
 | //   - the order of b.Values is the order to emit the Values in each Block | 
 | //   - f has a non-nil regAlloc field | 
 | func Compile(f *Func) { | 
 | 	// TODO: debugging - set flags to control verbosity of compiler, | 
 | 	// which phases to dump IR before/after, etc. | 
 | 	if f.Log() { | 
 | 		f.Logf("compiling %s\n", f.Name) | 
 | 	} | 
 |  | 
 | 	var rnd *rand.Rand | 
 | 	if checkEnabled { | 
 | 		seed := int64(crc32.ChecksumIEEE(([]byte)(f.Name))) ^ int64(checkRandSeed) | 
 | 		rnd = rand.New(rand.NewSource(seed)) | 
 | 	} | 
 |  | 
 | 	// hook to print function & phase if panic happens | 
 | 	phaseName := "init" | 
 | 	defer func() { | 
 | 		if phaseName != "" { | 
 | 			err := recover() | 
 | 			stack := make([]byte, 16384) | 
 | 			n := runtime.Stack(stack, false) | 
 | 			stack = stack[:n] | 
 | 			if f.HTMLWriter != nil { | 
 | 				f.HTMLWriter.flushPhases() | 
 | 			} | 
 | 			f.Fatalf("panic during %s while compiling %s:\n\n%v\n\n%s\n", phaseName, f.Name, err, stack) | 
 | 		} | 
 | 	}() | 
 |  | 
 | 	// Run all the passes | 
 | 	if f.Log() { | 
 | 		printFunc(f) | 
 | 	} | 
 | 	f.HTMLWriter.WritePhase("start", "start") | 
 | 	if BuildDump[f.Name] { | 
 | 		f.dumpFile("build") | 
 | 	} | 
 | 	if checkEnabled { | 
 | 		checkFunc(f) | 
 | 	} | 
 | 	const logMemStats = false | 
 | 	for _, p := range passes { | 
 | 		if !f.Config.optimize && !p.required || p.disabled { | 
 | 			continue | 
 | 		} | 
 | 		f.pass = &p | 
 | 		phaseName = p.name | 
 | 		if f.Log() { | 
 | 			f.Logf("  pass %s begin\n", p.name) | 
 | 		} | 
 | 		// TODO: capture logging during this pass, add it to the HTML | 
 | 		var mStart runtime.MemStats | 
 | 		if logMemStats || p.mem { | 
 | 			runtime.ReadMemStats(&mStart) | 
 | 		} | 
 |  | 
 | 		if checkEnabled && !f.scheduled { | 
 | 			// Test that we don't depend on the value order, by randomizing | 
 | 			// the order of values in each block. See issue 18169. | 
 | 			for _, b := range f.Blocks { | 
 | 				for i := 0; i < len(b.Values)-1; i++ { | 
 | 					j := i + rnd.Intn(len(b.Values)-i) | 
 | 					b.Values[i], b.Values[j] = b.Values[j], b.Values[i] | 
 | 				} | 
 | 			} | 
 | 		} | 
 |  | 
 | 		tStart := time.Now() | 
 | 		p.fn(f) | 
 | 		tEnd := time.Now() | 
 |  | 
 | 		// Need something less crude than "Log the whole intermediate result". | 
 | 		if f.Log() || f.HTMLWriter != nil { | 
 | 			time := tEnd.Sub(tStart).Nanoseconds() | 
 | 			var stats string | 
 | 			if logMemStats { | 
 | 				var mEnd runtime.MemStats | 
 | 				runtime.ReadMemStats(&mEnd) | 
 | 				nBytes := mEnd.TotalAlloc - mStart.TotalAlloc | 
 | 				nAllocs := mEnd.Mallocs - mStart.Mallocs | 
 | 				stats = fmt.Sprintf("[%d ns %d allocs %d bytes]", time, nAllocs, nBytes) | 
 | 			} else { | 
 | 				stats = fmt.Sprintf("[%d ns]", time) | 
 | 			} | 
 |  | 
 | 			if f.Log() { | 
 | 				f.Logf("  pass %s end %s\n", p.name, stats) | 
 | 				printFunc(f) | 
 | 			} | 
 | 			f.HTMLWriter.WritePhase(phaseName, fmt.Sprintf("%s <span class=\"stats\">%s</span>", phaseName, stats)) | 
 | 		} | 
 | 		if p.time || p.mem { | 
 | 			// Surround timing information w/ enough context to allow comparisons. | 
 | 			time := tEnd.Sub(tStart).Nanoseconds() | 
 | 			if p.time { | 
 | 				f.LogStat("TIME(ns)", time) | 
 | 			} | 
 | 			if p.mem { | 
 | 				var mEnd runtime.MemStats | 
 | 				runtime.ReadMemStats(&mEnd) | 
 | 				nBytes := mEnd.TotalAlloc - mStart.TotalAlloc | 
 | 				nAllocs := mEnd.Mallocs - mStart.Mallocs | 
 | 				f.LogStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs) | 
 | 			} | 
 | 		} | 
 | 		if p.dump != nil && p.dump[f.Name] { | 
 | 			// Dump function to appropriately named file | 
 | 			f.dumpFile(phaseName) | 
 | 		} | 
 | 		if checkEnabled { | 
 | 			checkFunc(f) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if f.HTMLWriter != nil { | 
 | 		// Ensure we write any pending phases to the html | 
 | 		f.HTMLWriter.flushPhases() | 
 | 	} | 
 |  | 
 | 	if f.ruleMatches != nil { | 
 | 		var keys []string | 
 | 		for key := range f.ruleMatches { | 
 | 			keys = append(keys, key) | 
 | 		} | 
 | 		sort.Strings(keys) | 
 | 		buf := new(strings.Builder) | 
 | 		fmt.Fprintf(buf, "%s: ", f.Name) | 
 | 		for _, key := range keys { | 
 | 			fmt.Fprintf(buf, "%s=%d ", key, f.ruleMatches[key]) | 
 | 		} | 
 | 		fmt.Fprint(buf, "\n") | 
 | 		fmt.Print(buf.String()) | 
 | 	} | 
 |  | 
 | 	// Squash error printing defer | 
 | 	phaseName = "" | 
 | } | 
 |  | 
 | // DumpFileForPhase creates a file from the function name and phase name, | 
 | // warning and returning nil if this is not possible. | 
 | func (f *Func) DumpFileForPhase(phaseName string) io.WriteCloser { | 
 | 	f.dumpFileSeq++ | 
 | 	fname := fmt.Sprintf("%s_%02d__%s.dump", f.Name, int(f.dumpFileSeq), phaseName) | 
 | 	fname = strings.Replace(fname, " ", "_", -1) | 
 | 	fname = strings.Replace(fname, "/", "_", -1) | 
 | 	fname = strings.Replace(fname, ":", "_", -1) | 
 |  | 
 | 	if ssaDir := os.Getenv("GOSSADIR"); ssaDir != "" { | 
 | 		fname = filepath.Join(ssaDir, fname) | 
 | 	} | 
 |  | 
 | 	fi, err := os.Create(fname) | 
 | 	if err != nil { | 
 | 		f.Warnl(src.NoXPos, "Unable to create after-phase dump file %s", fname) | 
 | 		return nil | 
 | 	} | 
 | 	return fi | 
 | } | 
 |  | 
 | // dumpFile creates a file from the phase name and function name | 
 | // Dumping is done to files to avoid buffering huge strings before | 
 | // output. | 
 | func (f *Func) dumpFile(phaseName string) { | 
 | 	fi := f.DumpFileForPhase(phaseName) | 
 | 	if fi != nil { | 
 | 		p := stringFuncPrinter{w: fi} | 
 | 		fprintFunc(p, f) | 
 | 		fi.Close() | 
 | 	} | 
 | } | 
 |  | 
 | type pass struct { | 
 | 	name     string | 
 | 	fn       func(*Func) | 
 | 	required bool | 
 | 	disabled bool | 
 | 	time     bool            // report time to run pass | 
 | 	mem      bool            // report mem stats to run pass | 
 | 	stats    int             // pass reports own "stats" (e.g., branches removed) | 
 | 	debug    int             // pass performs some debugging. =1 should be in error-testing-friendly Warnl format. | 
 | 	test     int             // pass-specific ad-hoc option, perhaps useful in development | 
 | 	dump     map[string]bool // dump if function name matches | 
 | } | 
 |  | 
 | func (p *pass) addDump(s string) { | 
 | 	if p.dump == nil { | 
 | 		p.dump = make(map[string]bool) | 
 | 	} | 
 | 	p.dump[s] = true | 
 | } | 
 |  | 
 | func (p *pass) String() string { | 
 | 	if p == nil { | 
 | 		return "nil pass" | 
 | 	} | 
 | 	return p.name | 
 | } | 
 |  | 
 | // Run consistency checker between each phase | 
 | var ( | 
 | 	checkEnabled  = false | 
 | 	checkRandSeed = 0 | 
 | ) | 
 |  | 
 | // Debug output | 
 | var IntrinsicsDebug int | 
 | var IntrinsicsDisable bool | 
 |  | 
 | var BuildDebug int | 
 | var BuildTest int | 
 | var BuildStats int | 
 | var BuildDump map[string]bool = make(map[string]bool) // names of functions to dump after initial build of ssa | 
 |  | 
 | var GenssaDump map[string]bool = make(map[string]bool) // names of functions to dump after ssa has been converted to asm | 
 |  | 
 | // PhaseOption sets the specified flag in the specified ssa phase, | 
 | // returning empty string if this was successful or a string explaining | 
 | // the error if it was not. | 
 | // A version of the phase name with "_" replaced by " " is also checked for a match. | 
 | // If the phase name begins a '~' then the rest of the underscores-replaced-with-blanks | 
 | // version is used as a regular expression to match the phase name(s). | 
 | // | 
 | // Special cases that have turned out to be useful: | 
 | //   - ssa/check/on enables checking after each phase | 
 | //   - ssa/all/time enables time reporting for all phases | 
 | // | 
 | // See gc/lex.go for dissection of the option string. | 
 | // Example uses: | 
 | // | 
 | // GO_GCFLAGS=-d=ssa/generic_cse/time,ssa/generic_cse/stats,ssa/generic_cse/debug=3 ./make.bash | 
 | // | 
 | // BOOT_GO_GCFLAGS=-d='ssa/~^.*scc$/off' GO_GCFLAGS='-d=ssa/~^.*scc$/off' ./make.bash | 
 | func PhaseOption(phase, flag string, val int, valString string) string { | 
 | 	switch phase { | 
 | 	case "", "help": | 
 | 		lastcr := 0 | 
 | 		phasenames := "    check, all, build, intrinsics, genssa" | 
 | 		for _, p := range passes { | 
 | 			pn := strings.Replace(p.name, " ", "_", -1) | 
 | 			if len(pn)+len(phasenames)-lastcr > 70 { | 
 | 				phasenames += "\n    " | 
 | 				lastcr = len(phasenames) | 
 | 				phasenames += pn | 
 | 			} else { | 
 | 				phasenames += ", " + pn | 
 | 			} | 
 | 		} | 
 | 		return `PhaseOptions usage: | 
 |  | 
 |     go tool compile -d=ssa/<phase>/<flag>[=<value>|<function_name>] | 
 |  | 
 | where: | 
 |  | 
 | - <phase> is one of: | 
 | ` + phasenames + ` | 
 |  | 
 | - <flag> is one of: | 
 |     on, off, debug, mem, time, test, stats, dump, seed | 
 |  | 
 | - <value> defaults to 1 | 
 |  | 
 | - <function_name> is required for the "dump" flag, and specifies the | 
 |   name of function to dump after <phase> | 
 |  | 
 | Phase "all" supports flags "time", "mem", and "dump". | 
 | Phase "intrinsics" supports flags "on", "off", and "debug". | 
 | Phase "genssa" (assembly generation) supports the flag "dump". | 
 |  | 
 | If the "dump" flag is specified, the output is written on a file named | 
 | <phase>__<function_name>_<seq>.dump; otherwise it is directed to stdout. | 
 |  | 
 | Examples: | 
 |  | 
 |     -d=ssa/check/on | 
 | enables checking after each phase | 
 |  | 
 | 	-d=ssa/check/seed=1234 | 
 | enables checking after each phase, using 1234 to seed the PRNG | 
 | used for value order randomization | 
 |  | 
 |     -d=ssa/all/time | 
 | enables time reporting for all phases | 
 |  | 
 |     -d=ssa/prove/debug=2 | 
 | sets debugging level to 2 in the prove pass | 
 |  | 
 | Be aware that when "/debug=X" is applied to a pass, some passes | 
 | will emit debug output for all functions, and other passes will | 
 | only emit debug output for functions that match the current | 
 | GOSSAFUNC value. | 
 |  | 
 | Multiple flags can be passed at once, by separating them with | 
 | commas. For example: | 
 |  | 
 |     -d=ssa/check/on,ssa/all/time | 
 | ` | 
 | 	} | 
 |  | 
 | 	if phase == "check" { | 
 | 		switch flag { | 
 | 		case "on": | 
 | 			checkEnabled = val != 0 | 
 | 			debugPoset = checkEnabled // also turn on advanced self-checking in prove's data structure | 
 | 			return "" | 
 | 		case "off": | 
 | 			checkEnabled = val == 0 | 
 | 			debugPoset = checkEnabled | 
 | 			return "" | 
 | 		case "seed": | 
 | 			checkEnabled = true | 
 | 			checkRandSeed = val | 
 | 			debugPoset = checkEnabled | 
 | 			return "" | 
 | 		} | 
 | 	} | 
 |  | 
 | 	alltime := false | 
 | 	allmem := false | 
 | 	alldump := false | 
 | 	if phase == "all" { | 
 | 		switch flag { | 
 | 		case "time": | 
 | 			alltime = val != 0 | 
 | 		case "mem": | 
 | 			allmem = val != 0 | 
 | 		case "dump": | 
 | 			alldump = val != 0 | 
 | 			if alldump { | 
 | 				BuildDump[valString] = true | 
 | 				GenssaDump[valString] = true | 
 | 			} | 
 | 		default: | 
 | 			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) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if phase == "intrinsics" { | 
 | 		switch flag { | 
 | 		case "on": | 
 | 			IntrinsicsDisable = val == 0 | 
 | 		case "off": | 
 | 			IntrinsicsDisable = val != 0 | 
 | 		case "debug": | 
 | 			IntrinsicsDebug = val | 
 | 		default: | 
 | 			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/intrinsics/{on,off,debug})", flag, phase) | 
 | 		} | 
 | 		return "" | 
 | 	} | 
 | 	if phase == "build" { | 
 | 		switch flag { | 
 | 		case "debug": | 
 | 			BuildDebug = val | 
 | 		case "test": | 
 | 			BuildTest = val | 
 | 		case "stats": | 
 | 			BuildStats = val | 
 | 		case "dump": | 
 | 			BuildDump[valString] = true | 
 | 		default: | 
 | 			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) | 
 | 		} | 
 | 		return "" | 
 | 	} | 
 | 	if phase == "genssa" { | 
 | 		switch flag { | 
 | 		case "dump": | 
 | 			GenssaDump[valString] = true | 
 | 		default: | 
 | 			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/genssa/dump=function_name)", flag, phase) | 
 | 		} | 
 | 		return "" | 
 | 	} | 
 |  | 
 | 	underphase := strings.Replace(phase, "_", " ", -1) | 
 | 	var re *regexp.Regexp | 
 | 	if phase[0] == '~' { | 
 | 		r, ok := regexp.Compile(underphase[1:]) | 
 | 		if ok != nil { | 
 | 			return fmt.Sprintf("Error %s in regexp for phase %s, flag %s", ok.Error(), phase, flag) | 
 | 		} | 
 | 		re = r | 
 | 	} | 
 | 	matchedOne := false | 
 | 	for i, p := range passes { | 
 | 		if phase == "all" { | 
 | 			p.time = alltime | 
 | 			p.mem = allmem | 
 | 			if alldump { | 
 | 				p.addDump(valString) | 
 | 			} | 
 | 			passes[i] = p | 
 | 			matchedOne = true | 
 | 		} else if p.name == phase || p.name == underphase || re != nil && re.MatchString(p.name) { | 
 | 			switch flag { | 
 | 			case "on": | 
 | 				p.disabled = val == 0 | 
 | 			case "off": | 
 | 				p.disabled = val != 0 | 
 | 			case "time": | 
 | 				p.time = val != 0 | 
 | 			case "mem": | 
 | 				p.mem = val != 0 | 
 | 			case "debug": | 
 | 				p.debug = val | 
 | 			case "stats": | 
 | 				p.stats = val | 
 | 			case "test": | 
 | 				p.test = val | 
 | 			case "dump": | 
 | 				p.addDump(valString) | 
 | 			default: | 
 | 				return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) | 
 | 			} | 
 | 			if p.disabled && p.required { | 
 | 				return fmt.Sprintf("Cannot disable required SSA phase %s using -d=ssa/%s debug option", phase, phase) | 
 | 			} | 
 | 			passes[i] = p | 
 | 			matchedOne = true | 
 | 		} | 
 | 	} | 
 | 	if matchedOne { | 
 | 		return "" | 
 | 	} | 
 | 	return fmt.Sprintf("Did not find a phase matching %s in -d=ssa/... debug option", phase) | 
 | } | 
 |  | 
 | // list of passes for the compiler | 
 | var passes = [...]pass{ | 
 | 	// TODO: combine phielim and copyelim into a single pass? | 
 | 	{name: "number lines", fn: numberLines, required: true}, | 
 | 	{name: "early phielim", fn: phielim}, | 
 | 	{name: "early copyelim", fn: copyelim}, | 
 | 	{name: "early deadcode", fn: deadcode}, // remove generated dead code to avoid doing pointless work during opt | 
 | 	{name: "short circuit", fn: shortcircuit}, | 
 | 	{name: "decompose user", fn: decomposeUser, required: true}, | 
 | 	{name: "pre-opt deadcode", fn: deadcode}, | 
 | 	{name: "opt", fn: opt, required: true},               // NB: some generic rules know the name of the opt pass. TODO: split required rules and optimizing rules | 
 | 	{name: "zero arg cse", fn: zcse, required: true},     // required to merge OpSB values | 
 | 	{name: "opt deadcode", fn: deadcode, required: true}, // remove any blocks orphaned during opt | 
 | 	{name: "generic cse", fn: cse}, | 
 | 	{name: "phiopt", fn: phiopt}, | 
 | 	{name: "gcse deadcode", fn: deadcode, required: true}, // clean out after cse and phiopt | 
 | 	{name: "nilcheckelim", fn: nilcheckelim}, | 
 | 	{name: "prove", fn: prove}, | 
 | 	{name: "early fuse", fn: fuseEarly}, | 
 | 	{name: "expand calls", fn: expandCalls, required: true}, | 
 | 	{name: "decompose builtin", fn: postExpandCallsDecompose, required: true}, | 
 | 	{name: "softfloat", fn: softfloat, required: true}, | 
 | 	{name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules | 
 | 	{name: "dead auto elim", fn: elimDeadAutosGeneric}, | 
 | 	{name: "sccp", fn: sccp}, | 
 | 	{name: "generic deadcode", fn: deadcode, required: true}, // remove dead stores, which otherwise mess up store chain | 
 | 	{name: "check bce", fn: checkbce}, | 
 | 	{name: "branchelim", fn: branchelim}, | 
 | 	{name: "late fuse", fn: fuseLate}, | 
 | 	{name: "dse", fn: dse}, | 
 | 	{name: "memcombine", fn: memcombine}, | 
 | 	{name: "writebarrier", fn: writebarrier, required: true}, // expand write barrier ops | 
 | 	{name: "insert resched checks", fn: insertLoopReschedChecks, | 
 | 		disabled: !buildcfg.Experiment.PreemptibleLoops}, // insert resched checks in loops. | 
 | 	{name: "lower", fn: lower, required: true}, | 
 | 	{name: "addressing modes", fn: addressingModes, required: false}, | 
 | 	{name: "late lower", fn: lateLower, required: true}, | 
 | 	{name: "lowered deadcode for cse", fn: deadcode}, // deadcode immediately before CSE avoids CSE making dead values live again | 
 | 	{name: "lowered cse", fn: cse}, | 
 | 	{name: "elim unread autos", fn: elimUnreadAutos}, | 
 | 	{name: "tighten tuple selectors", fn: tightenTupleSelectors, required: true}, | 
 | 	{name: "lowered deadcode", fn: deadcode, required: true}, | 
 | 	{name: "checkLower", fn: checkLower, required: true}, | 
 | 	{name: "late phielim", fn: phielim}, | 
 | 	{name: "late copyelim", fn: copyelim}, | 
 | 	{name: "tighten", fn: tighten, required: true}, // move values closer to their uses | 
 | 	{name: "late deadcode", fn: deadcode}, | 
 | 	{name: "critical", fn: critical, required: true}, // remove critical edges | 
 | 	{name: "phi tighten", fn: phiTighten},            // place rematerializable phi args near uses to reduce value lifetimes | 
 | 	{name: "likelyadjust", fn: likelyadjust}, | 
 | 	{name: "layout", fn: layout, required: true},     // schedule blocks | 
 | 	{name: "schedule", fn: schedule, required: true}, // schedule values | 
 | 	{name: "late nilcheck", fn: nilcheckelim2}, | 
 | 	{name: "flagalloc", fn: flagalloc, required: true}, // allocate flags register | 
 | 	{name: "regalloc", fn: regalloc, required: true},   // allocate int & float registers + stack slots | 
 | 	{name: "loop rotate", fn: loopRotate}, | 
 | 	{name: "trim", fn: trim}, // remove empty blocks | 
 | } | 
 |  | 
 | // Double-check phase ordering constraints. | 
 | // This code is intended to document the ordering requirements | 
 | // between different phases. It does not override the passes | 
 | // list above. | 
 | type constraint struct { | 
 | 	a, b string // a must come before b | 
 | } | 
 |  | 
 | var passOrder = [...]constraint{ | 
 | 	// "insert resched checks" uses mem, better to clean out stores first. | 
 | 	{"dse", "insert resched checks"}, | 
 | 	// insert resched checks adds new blocks containing generic instructions | 
 | 	{"insert resched checks", "lower"}, | 
 | 	{"insert resched checks", "tighten"}, | 
 |  | 
 | 	// prove relies on common-subexpression elimination for maximum benefits. | 
 | 	{"generic cse", "prove"}, | 
 | 	// deadcode after prove to eliminate all new dead blocks. | 
 | 	{"prove", "generic deadcode"}, | 
 | 	// common-subexpression before dead-store elim, so that we recognize | 
 | 	// when two address expressions are the same. | 
 | 	{"generic cse", "dse"}, | 
 | 	// cse substantially improves nilcheckelim efficacy | 
 | 	{"generic cse", "nilcheckelim"}, | 
 | 	// allow deadcode to clean up after nilcheckelim | 
 | 	{"nilcheckelim", "generic deadcode"}, | 
 | 	// nilcheckelim generates sequences of plain basic blocks | 
 | 	{"nilcheckelim", "late fuse"}, | 
 | 	// nilcheckelim relies on opt to rewrite user nil checks | 
 | 	{"opt", "nilcheckelim"}, | 
 | 	// tighten will be most effective when as many values have been removed as possible | 
 | 	{"generic deadcode", "tighten"}, | 
 | 	{"generic cse", "tighten"}, | 
 | 	// checkbce needs the values removed | 
 | 	{"generic deadcode", "check bce"}, | 
 | 	// decompose builtin now also cleans up after expand calls | 
 | 	{"expand calls", "decompose builtin"}, | 
 | 	// don't run optimization pass until we've decomposed builtin objects | 
 | 	{"decompose builtin", "late opt"}, | 
 | 	// decompose builtin is the last pass that may introduce new float ops, so run softfloat after it | 
 | 	{"decompose builtin", "softfloat"}, | 
 | 	// tuple selectors must be tightened to generators and de-duplicated before scheduling | 
 | 	{"tighten tuple selectors", "schedule"}, | 
 | 	// remove critical edges before phi tighten, so that phi args get better placement | 
 | 	{"critical", "phi tighten"}, | 
 | 	// don't layout blocks until critical edges have been removed | 
 | 	{"critical", "layout"}, | 
 | 	// regalloc requires the removal of all critical edges | 
 | 	{"critical", "regalloc"}, | 
 | 	// regalloc requires all the values in a block to be scheduled | 
 | 	{"schedule", "regalloc"}, | 
 | 	// the rules in late lower run after the general rules. | 
 | 	{"lower", "late lower"}, | 
 | 	// late lower may generate some values that need to be CSEed. | 
 | 	{"late lower", "lowered cse"}, | 
 | 	// checkLower must run after lowering & subsequent dead code elim | 
 | 	{"lower", "checkLower"}, | 
 | 	{"lowered deadcode", "checkLower"}, | 
 | 	{"late lower", "checkLower"}, | 
 | 	// late nilcheck needs instructions to be scheduled. | 
 | 	{"schedule", "late nilcheck"}, | 
 | 	// flagalloc needs instructions to be scheduled. | 
 | 	{"schedule", "flagalloc"}, | 
 | 	// regalloc needs flags to be allocated first. | 
 | 	{"flagalloc", "regalloc"}, | 
 | 	// loopRotate will confuse regalloc. | 
 | 	{"regalloc", "loop rotate"}, | 
 | 	// trim needs regalloc to be done first. | 
 | 	{"regalloc", "trim"}, | 
 | 	// memcombine works better if fuse happens first, to help merge stores. | 
 | 	{"late fuse", "memcombine"}, | 
 | 	// memcombine is a arch-independent pass. | 
 | 	{"memcombine", "lower"}, | 
 | } | 
 |  | 
 | func init() { | 
 | 	for _, c := range passOrder { | 
 | 		a, b := c.a, c.b | 
 | 		i := -1 | 
 | 		j := -1 | 
 | 		for k, p := range passes { | 
 | 			if p.name == a { | 
 | 				i = k | 
 | 			} | 
 | 			if p.name == b { | 
 | 				j = k | 
 | 			} | 
 | 		} | 
 | 		if i < 0 { | 
 | 			log.Panicf("pass %s not found", a) | 
 | 		} | 
 | 		if j < 0 { | 
 | 			log.Panicf("pass %s not found", b) | 
 | 		} | 
 | 		if i >= j { | 
 | 			log.Panicf("passes %s and %s out of order", a, b) | 
 | 		} | 
 | 	} | 
 | } |