github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/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 "cmd/internal/obj" 9 "fmt" 10 "log" 11 "os" 12 "regexp" 13 "runtime" 14 "strings" 15 "time" 16 ) 17 18 // Compile is the main entry point for this package. 19 // Compile modifies f so that on return: 20 // · all Values in f map to 0 or 1 assembly instructions of the target architecture 21 // · the order of f.Blocks is the order to emit the Blocks 22 // · the order of b.Values is the order to emit the Values in each Block 23 // · f has a non-nil regAlloc field 24 func Compile(f *Func) { 25 // TODO: debugging - set flags to control verbosity of compiler, 26 // which phases to dump IR before/after, etc. 27 if f.Log() { 28 f.Logf("compiling %s\n", f.Name) 29 } 30 31 // hook to print function & phase if panic happens 32 phaseName := "init" 33 defer func() { 34 if phaseName != "" { 35 err := recover() 36 stack := make([]byte, 16384) 37 n := runtime.Stack(stack, false) 38 stack = stack[:n] 39 f.Fatalf("panic during %s while compiling %s:\n\n%v\n\n%s\n", phaseName, f.Name, err, stack) 40 } 41 }() 42 43 // Run all the passes 44 printFunc(f) 45 f.Config.HTML.WriteFunc("start", f) 46 if BuildDump != "" && BuildDump == f.Name { 47 f.dumpFile("build") 48 } 49 if checkEnabled { 50 checkFunc(f) 51 } 52 const logMemStats = false 53 for _, p := range passes { 54 if !f.Config.optimize && !p.required || p.disabled { 55 continue 56 } 57 f.pass = &p 58 phaseName = p.name 59 if f.Log() { 60 f.Logf(" pass %s begin\n", p.name) 61 } 62 // TODO: capture logging during this pass, add it to the HTML 63 var mStart runtime.MemStats 64 if logMemStats || p.mem { 65 runtime.ReadMemStats(&mStart) 66 } 67 68 tStart := time.Now() 69 p.fn(f) 70 tEnd := time.Now() 71 72 // Need something less crude than "Log the whole intermediate result". 73 if f.Log() || f.Config.HTML != nil { 74 time := tEnd.Sub(tStart).Nanoseconds() 75 var stats string 76 if logMemStats { 77 var mEnd runtime.MemStats 78 runtime.ReadMemStats(&mEnd) 79 nBytes := mEnd.TotalAlloc - mStart.TotalAlloc 80 nAllocs := mEnd.Mallocs - mStart.Mallocs 81 stats = fmt.Sprintf("[%d ns %d allocs %d bytes]", time, nAllocs, nBytes) 82 } else { 83 stats = fmt.Sprintf("[%d ns]", time) 84 } 85 86 f.Logf(" pass %s end %s\n", p.name, stats) 87 printFunc(f) 88 f.Config.HTML.WriteFunc(fmt.Sprintf("after %s <span class=\"stats\">%s</span>", phaseName, stats), f) 89 } 90 if p.time || p.mem { 91 // Surround timing information w/ enough context to allow comparisons. 92 time := tEnd.Sub(tStart).Nanoseconds() 93 if p.time { 94 f.LogStat("TIME(ns)", time) 95 } 96 if p.mem { 97 var mEnd runtime.MemStats 98 runtime.ReadMemStats(&mEnd) 99 nBytes := mEnd.TotalAlloc - mStart.TotalAlloc 100 nAllocs := mEnd.Mallocs - mStart.Mallocs 101 f.LogStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs) 102 } 103 } 104 if p.dump != nil && p.dump[f.Name] { 105 // Dump function to appropriately named file 106 f.dumpFile(phaseName) 107 } 108 if checkEnabled { 109 checkFunc(f) 110 } 111 } 112 113 // Squash error printing defer 114 phaseName = "" 115 } 116 117 // TODO: should be a config field 118 var dumpFileSeq int 119 120 // dumpFile creates a file from the phase name and function name 121 // Dumping is done to files to avoid buffering huge strings before 122 // output. 123 func (f *Func) dumpFile(phaseName string) { 124 dumpFileSeq++ 125 fname := fmt.Sprintf("%s__%s_%d.dump", phaseName, f.Name, dumpFileSeq) 126 fname = strings.Replace(fname, " ", "_", -1) 127 fname = strings.Replace(fname, "/", "_", -1) 128 fname = strings.Replace(fname, ":", "_", -1) 129 130 fi, err := os.Create(fname) 131 if err != nil { 132 f.Config.Warnl(0, "Unable to create after-phase dump file %s", fname) 133 return 134 } 135 136 p := stringFuncPrinter{w: fi} 137 fprintFunc(p, f) 138 fi.Close() 139 } 140 141 type pass struct { 142 name string 143 fn func(*Func) 144 required bool 145 disabled bool 146 time bool // report time to run pass 147 mem bool // report mem stats to run pass 148 stats int // pass reports own "stats" (e.g., branches removed) 149 debug int // pass performs some debugging. =1 should be in error-testing-friendly Warnl format. 150 test int // pass-specific ad-hoc option, perhaps useful in development 151 dump map[string]bool // dump if function name matches 152 } 153 154 func (p *pass) addDump(s string) { 155 if p.dump == nil { 156 p.dump = make(map[string]bool) 157 } 158 p.dump[s] = true 159 } 160 161 // Run consistency checker between each phase 162 var checkEnabled = false 163 164 // Debug output 165 var IntrinsicsDebug int 166 var IntrinsicsDisable bool 167 168 var BuildDebug int 169 var BuildTest int 170 var BuildStats int 171 var BuildDump string // name of function to dump after initial build of ssa 172 173 // PhaseOption sets the specified flag in the specified ssa phase, 174 // returning empty string if this was successful or a string explaining 175 // the error if it was not. 176 // A version of the phase name with "_" replaced by " " is also checked for a match. 177 // If the phase name begins a '~' then the rest of the underscores-replaced-with-blanks 178 // version is used as a regular expression to match the phase name(s). 179 // 180 // Special cases that have turned out to be useful: 181 // ssa/check/on enables checking after each phase 182 // ssa/all/time enables time reporting for all phases 183 // 184 // See gc/lex.go for dissection of the option string. 185 // Example uses: 186 // 187 // GO_GCFLAGS=-d=ssa/generic_cse/time,ssa/generic_cse/stats,ssa/generic_cse/debug=3 ./make.bash 188 // 189 // BOOT_GO_GCFLAGS=-d='ssa/~^.*scc$/off' GO_GCFLAGS='-d=ssa/~^.*scc$/off' ./make.bash 190 // 191 func PhaseOption(phase, flag string, val int, valString string) string { 192 if phase == "help" { 193 lastcr := 0 194 phasenames := "check, all, build, intrinsics" 195 for _, p := range passes { 196 pn := strings.Replace(p.name, " ", "_", -1) 197 if len(pn)+len(phasenames)-lastcr > 70 { 198 phasenames += "\n" 199 lastcr = len(phasenames) 200 phasenames += pn 201 } else { 202 phasenames += ", " + pn 203 } 204 } 205 return "" + 206 `GcFlag -d=ssa/<phase>/<flag>[=<value>]|[:<function_name>] 207 <phase> is one of: 208 ` + phasenames + ` 209 <flag> is one of on, off, debug, mem, time, test, stats, dump 210 <value> defaults to 1 211 <function_name> is required for "dump", specifies name of function to dump after <phase> 212 Except for dump, output is directed to standard out; dump appears in a file. 213 Phase "all" supports flags "time", "mem", and "dump". 214 Phases "intrinsics" supports flags "on", "off", and "debug". 215 Interpretation of the "debug" value depends on the phase. 216 Dump files are named <phase>__<function_name>_<seq>.dump. 217 ` 218 } 219 220 if phase == "check" && flag == "on" { 221 checkEnabled = val != 0 222 return "" 223 } 224 if phase == "check" && flag == "off" { 225 checkEnabled = val == 0 226 return "" 227 } 228 229 alltime := false 230 allmem := false 231 alldump := false 232 if phase == "all" { 233 if flag == "time" { 234 alltime = val != 0 235 } else if flag == "mem" { 236 allmem = val != 0 237 } else if flag == "dump" { 238 alldump = val != 0 239 if alldump { 240 BuildDump = valString 241 } 242 } else { 243 return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) 244 } 245 } 246 247 if phase == "intrinsics" { 248 switch flag { 249 case "on": 250 IntrinsicsDisable = val == 0 251 case "off": 252 IntrinsicsDisable = val != 0 253 case "debug": 254 IntrinsicsDebug = val 255 default: 256 return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) 257 } 258 return "" 259 } 260 if phase == "build" { 261 switch flag { 262 case "debug": 263 BuildDebug = val 264 case "test": 265 BuildTest = val 266 case "stats": 267 BuildStats = val 268 case "dump": 269 BuildDump = valString 270 default: 271 return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) 272 } 273 return "" 274 } 275 276 underphase := strings.Replace(phase, "_", " ", -1) 277 var re *regexp.Regexp 278 if phase[0] == '~' { 279 r, ok := regexp.Compile(underphase[1:]) 280 if ok != nil { 281 return fmt.Sprintf("Error %s in regexp for phase %s, flag %s", ok.Error(), phase, flag) 282 } 283 re = r 284 } 285 matchedOne := false 286 for i, p := range passes { 287 if phase == "all" { 288 p.time = alltime 289 p.mem = allmem 290 if alldump { 291 p.addDump(valString) 292 } 293 passes[i] = p 294 matchedOne = true 295 } else if p.name == phase || p.name == underphase || re != nil && re.MatchString(p.name) { 296 switch flag { 297 case "on": 298 p.disabled = val == 0 299 case "off": 300 p.disabled = val != 0 301 case "time": 302 p.time = val != 0 303 case "mem": 304 p.mem = val != 0 305 case "debug": 306 p.debug = val 307 case "stats": 308 p.stats = val 309 case "test": 310 p.test = val 311 case "dump": 312 p.addDump(valString) 313 default: 314 return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) 315 } 316 if p.disabled && p.required { 317 return fmt.Sprintf("Cannot disable required SSA phase %s using -d=ssa/%s debug option", phase, phase) 318 } 319 passes[i] = p 320 matchedOne = true 321 } 322 } 323 if matchedOne { 324 return "" 325 } 326 return fmt.Sprintf("Did not find a phase matching %s in -d=ssa/... debug option", phase) 327 } 328 329 // list of passes for the compiler 330 var passes = [...]pass{ 331 // TODO: combine phielim and copyelim into a single pass? 332 {name: "early phielim", fn: phielim}, 333 {name: "early copyelim", fn: copyelim}, 334 {name: "early deadcode", fn: deadcode}, // remove generated dead code to avoid doing pointless work during opt 335 {name: "short circuit", fn: shortcircuit}, 336 {name: "decompose user", fn: decomposeUser, required: true}, 337 {name: "opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules 338 {name: "zero arg cse", fn: zcse, required: true}, // required to merge OpSB values 339 {name: "opt deadcode", fn: deadcode, required: true}, // remove any blocks orphaned during opt 340 {name: "generic cse", fn: cse}, 341 {name: "phiopt", fn: phiopt}, 342 {name: "nilcheckelim", fn: nilcheckelim}, 343 {name: "prove", fn: prove}, 344 {name: "loopbce", fn: loopbce}, 345 {name: "decompose builtin", fn: decomposeBuiltIn, required: true}, 346 {name: "dec", fn: dec, required: true}, 347 {name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules 348 {name: "generic deadcode", fn: deadcode}, 349 {name: "check bce", fn: checkbce}, 350 {name: "writebarrier", fn: writebarrier, required: true}, // expand write barrier ops 351 {name: "fuse", fn: fuse}, 352 {name: "dse", fn: dse}, 353 {name: "insert resched checks", fn: insertLoopReschedChecks, 354 disabled: obj.Preemptibleloops_enabled == 0}, // insert resched checks in loops. 355 {name: "tighten", fn: tighten}, // move values closer to their uses 356 {name: "lower", fn: lower, required: true}, 357 {name: "lowered cse", fn: cse}, 358 {name: "lowered deadcode", fn: deadcode, required: true}, 359 {name: "checkLower", fn: checkLower, required: true}, 360 {name: "late phielim", fn: phielim}, 361 {name: "late copyelim", fn: copyelim}, 362 {name: "phi tighten", fn: phiTighten}, 363 {name: "late deadcode", fn: deadcode}, 364 {name: "critical", fn: critical, required: true}, // remove critical edges 365 {name: "likelyadjust", fn: likelyadjust}, 366 {name: "layout", fn: layout, required: true}, // schedule blocks 367 {name: "schedule", fn: schedule, required: true}, // schedule values 368 {name: "late nilcheck", fn: nilcheckelim2}, 369 {name: "flagalloc", fn: flagalloc, required: true}, // allocate flags register 370 {name: "regalloc", fn: regalloc, required: true}, // allocate int & float registers + stack slots 371 {name: "stackframe", fn: stackframe, required: true}, 372 {name: "trim", fn: trim}, // remove empty blocks 373 } 374 375 // Double-check phase ordering constraints. 376 // This code is intended to document the ordering requirements 377 // between different phases. It does not override the passes 378 // list above. 379 type constraint struct { 380 a, b string // a must come before b 381 } 382 383 var passOrder = [...]constraint{ 384 // "insert resched checks" uses mem, better to clean out stores first. 385 {"dse", "insert resched checks"}, 386 // insert resched checks adds new blocks containing generic instructions 387 {"insert resched checks", "lower"}, 388 {"insert resched checks", "tighten"}, 389 390 // prove relies on common-subexpression elimination for maximum benefits. 391 {"generic cse", "prove"}, 392 // deadcode after prove to eliminate all new dead blocks. 393 {"prove", "generic deadcode"}, 394 // common-subexpression before dead-store elim, so that we recognize 395 // when two address expressions are the same. 396 {"generic cse", "dse"}, 397 // cse substantially improves nilcheckelim efficacy 398 {"generic cse", "nilcheckelim"}, 399 // allow deadcode to clean up after nilcheckelim 400 {"nilcheckelim", "generic deadcode"}, 401 // nilcheckelim generates sequences of plain basic blocks 402 {"nilcheckelim", "fuse"}, 403 // nilcheckelim relies on opt to rewrite user nil checks 404 {"opt", "nilcheckelim"}, 405 // tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET 406 {"tighten", "lower"}, 407 // tighten will be most effective when as many values have been removed as possible 408 {"generic deadcode", "tighten"}, 409 {"generic cse", "tighten"}, 410 // checkbce needs the values removed 411 {"generic deadcode", "check bce"}, 412 // don't run optimization pass until we've decomposed builtin objects 413 {"decompose builtin", "late opt"}, 414 // don't layout blocks until critical edges have been removed 415 {"critical", "layout"}, 416 // regalloc requires the removal of all critical edges 417 {"critical", "regalloc"}, 418 // regalloc requires all the values in a block to be scheduled 419 {"schedule", "regalloc"}, 420 // checkLower must run after lowering & subsequent dead code elim 421 {"lower", "checkLower"}, 422 {"lowered deadcode", "checkLower"}, 423 // late nilcheck needs instructions to be scheduled. 424 {"schedule", "late nilcheck"}, 425 // flagalloc needs instructions to be scheduled. 426 {"schedule", "flagalloc"}, 427 // regalloc needs flags to be allocated first. 428 {"flagalloc", "regalloc"}, 429 // stackframe needs to know about spilled registers. 430 {"regalloc", "stackframe"}, 431 // trim needs regalloc to be done first. 432 {"regalloc", "trim"}, 433 } 434 435 func init() { 436 for _, c := range passOrder { 437 a, b := c.a, c.b 438 i := -1 439 j := -1 440 for k, p := range passes { 441 if p.name == a { 442 i = k 443 } 444 if p.name == b { 445 j = k 446 } 447 } 448 if i < 0 { 449 log.Panicf("pass %s not found", a) 450 } 451 if j < 0 { 452 log.Panicf("pass %s not found", b) 453 } 454 if i >= j { 455 log.Panicf("passes %s and %s out of order", a, b) 456 } 457 } 458 }