github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/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: "late deadcode", fn: deadcode}, 274 {name: "critical", fn: critical, required: true}, // remove critical edges 275 {name: "likelyadjust", fn: likelyadjust}, 276 {name: "layout", fn: layout, required: true}, // schedule blocks 277 {name: "schedule", fn: schedule, required: true}, // schedule values 278 {name: "flagalloc", fn: flagalloc, required: true}, // allocate flags register 279 {name: "regalloc", fn: regalloc, required: true}, // allocate int & float registers + stack slots 280 {name: "trim", fn: trim}, // remove empty blocks 281 } 282 283 // Double-check phase ordering constraints. 284 // This code is intended to document the ordering requirements 285 // between different phases. It does not override the passes 286 // list above. 287 type constraint struct { 288 a, b string // a must come before b 289 } 290 291 var passOrder = [...]constraint{ 292 // prove reliese on common-subexpression elimination for maximum benefits. 293 {"generic cse", "prove"}, 294 // deadcode after prove to eliminate all new dead blocks. 295 {"prove", "generic deadcode"}, 296 // common-subexpression before dead-store elim, so that we recognize 297 // when two address expressions are the same. 298 {"generic cse", "dse"}, 299 // cse substantially improves nilcheckelim efficacy 300 {"generic cse", "nilcheckelim"}, 301 // allow deadcode to clean up after nilcheckelim 302 {"nilcheckelim", "generic deadcode"}, 303 // nilcheckelim generates sequences of plain basic blocks 304 {"nilcheckelim", "fuse"}, 305 // nilcheckelim relies on opt to rewrite user nil checks 306 {"opt", "nilcheckelim"}, 307 // tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET 308 {"tighten", "lower"}, 309 // cse, phiopt, nilcheckelim, prove and loopbce share idom. 310 {"generic domtree", "generic cse"}, 311 {"generic domtree", "phiopt"}, 312 {"generic domtree", "nilcheckelim"}, 313 {"generic domtree", "prove"}, 314 {"generic domtree", "loopbce"}, 315 // tighten will be most effective when as many values have been removed as possible 316 {"generic deadcode", "tighten"}, 317 {"generic cse", "tighten"}, 318 // checkbce needs the values removed 319 {"generic deadcode", "check bce"}, 320 // don't run optimization pass until we've decomposed builtin objects 321 {"decompose builtin", "late opt"}, 322 // don't layout blocks until critical edges have been removed 323 {"critical", "layout"}, 324 // regalloc requires the removal of all critical edges 325 {"critical", "regalloc"}, 326 // regalloc requires all the values in a block to be scheduled 327 {"schedule", "regalloc"}, 328 // checkLower must run after lowering & subsequent dead code elim 329 {"lower", "checkLower"}, 330 {"lowered deadcode", "checkLower"}, 331 // flagalloc needs instructions to be scheduled. 332 {"schedule", "flagalloc"}, 333 // regalloc needs flags to be allocated first. 334 {"flagalloc", "regalloc"}, 335 // trim needs regalloc to be done first. 336 {"regalloc", "trim"}, 337 } 338 339 func init() { 340 for _, c := range passOrder { 341 a, b := c.a, c.b 342 i := -1 343 j := -1 344 for k, p := range passes { 345 if p.name == a { 346 i = k 347 } 348 if p.name == b { 349 j = k 350 } 351 } 352 if i < 0 { 353 log.Panicf("pass %s not found", a) 354 } 355 if j < 0 { 356 log.Panicf("pass %s not found", b) 357 } 358 if i >= j { 359 log.Panicf("passes %s and %s out of order", a, b) 360 } 361 } 362 }