1
2
3
4
5 package ssa
6
7 import (
8 "cmd/internal/src"
9 "fmt"
10 "hash/crc32"
11 "internal/buildcfg"
12 "io"
13 "log"
14 "math/rand"
15 "os"
16 "path/filepath"
17 "regexp"
18 "runtime"
19 "sort"
20 "strings"
21 "time"
22 )
23
24
25
26
27
28
29
30 func Compile(f *Func) {
31
32
33 if f.Log() {
34 f.Logf("compiling %s\n", f.Name)
35 }
36
37 var rnd *rand.Rand
38 if checkEnabled {
39 seed := int64(crc32.ChecksumIEEE(([]byte)(f.Name))) ^ int64(checkRandSeed)
40 rnd = rand.New(rand.NewSource(seed))
41 }
42
43
44 phaseName := "init"
45 defer func() {
46 if phaseName != "" {
47 err := recover()
48 stack := make([]byte, 16384)
49 n := runtime.Stack(stack, false)
50 stack = stack[:n]
51 if f.HTMLWriter != nil {
52 f.HTMLWriter.flushPhases()
53 }
54 f.Fatalf("panic during %s while compiling %s:\n\n%v\n\n%s\n", phaseName, f.Name, err, stack)
55 }
56 }()
57
58
59 if f.Log() {
60 printFunc(f)
61 }
62 f.HTMLWriter.WritePhase("start", "start")
63 if BuildDump[f.Name] {
64 f.dumpFile("build")
65 }
66 if checkEnabled {
67 checkFunc(f)
68 }
69 const logMemStats = false
70 for _, p := range passes {
71 if !f.Config.optimize && !p.required || p.disabled {
72 continue
73 }
74 f.pass = &p
75 phaseName = p.name
76 if f.Log() {
77 f.Logf(" pass %s begin\n", p.name)
78 }
79
80 var mStart runtime.MemStats
81 if logMemStats || p.mem {
82 runtime.ReadMemStats(&mStart)
83 }
84
85 if checkEnabled && !f.scheduled {
86
87
88 for _, b := range f.Blocks {
89 for i := 0; i < len(b.Values)-1; i++ {
90 j := i + rnd.Intn(len(b.Values)-i)
91 b.Values[i], b.Values[j] = b.Values[j], b.Values[i]
92 }
93 }
94 }
95
96 tStart := time.Now()
97 p.fn(f)
98 tEnd := time.Now()
99
100
101 if f.Log() || f.HTMLWriter != nil {
102 time := tEnd.Sub(tStart).Nanoseconds()
103 var stats string
104 if logMemStats {
105 var mEnd runtime.MemStats
106 runtime.ReadMemStats(&mEnd)
107 nBytes := mEnd.TotalAlloc - mStart.TotalAlloc
108 nAllocs := mEnd.Mallocs - mStart.Mallocs
109 stats = fmt.Sprintf("[%d ns %d allocs %d bytes]", time, nAllocs, nBytes)
110 } else {
111 stats = fmt.Sprintf("[%d ns]", time)
112 }
113
114 if f.Log() {
115 f.Logf(" pass %s end %s\n", p.name, stats)
116 printFunc(f)
117 }
118 f.HTMLWriter.WritePhase(phaseName, fmt.Sprintf("%s <span class=\"stats\">%s</span>", phaseName, stats))
119 }
120 if p.time || p.mem {
121
122 time := tEnd.Sub(tStart).Nanoseconds()
123 if p.time {
124 f.LogStat("TIME(ns)", time)
125 }
126 if p.mem {
127 var mEnd runtime.MemStats
128 runtime.ReadMemStats(&mEnd)
129 nBytes := mEnd.TotalAlloc - mStart.TotalAlloc
130 nAllocs := mEnd.Mallocs - mStart.Mallocs
131 f.LogStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs)
132 }
133 }
134 if p.dump != nil && p.dump[f.Name] {
135
136 f.dumpFile(phaseName)
137 }
138 if checkEnabled {
139 checkFunc(f)
140 }
141 }
142
143 if f.HTMLWriter != nil {
144
145 f.HTMLWriter.flushPhases()
146 }
147
148 if f.ruleMatches != nil {
149 var keys []string
150 for key := range f.ruleMatches {
151 keys = append(keys, key)
152 }
153 sort.Strings(keys)
154 buf := new(strings.Builder)
155 fmt.Fprintf(buf, "%s: ", f.Name)
156 for _, key := range keys {
157 fmt.Fprintf(buf, "%s=%d ", key, f.ruleMatches[key])
158 }
159 fmt.Fprint(buf, "\n")
160 fmt.Print(buf.String())
161 }
162
163
164 phaseName = ""
165 }
166
167
168
169 func (f *Func) DumpFileForPhase(phaseName string) io.WriteCloser {
170 f.dumpFileSeq++
171 fname := fmt.Sprintf("%s_%02d__%s.dump", f.Name, int(f.dumpFileSeq), phaseName)
172 fname = strings.Replace(fname, " ", "_", -1)
173 fname = strings.Replace(fname, "/", "_", -1)
174 fname = strings.Replace(fname, ":", "_", -1)
175
176 if ssaDir := os.Getenv("GOSSADIR"); ssaDir != "" {
177 fname = filepath.Join(ssaDir, fname)
178 }
179
180 fi, err := os.Create(fname)
181 if err != nil {
182 f.Warnl(src.NoXPos, "Unable to create after-phase dump file %s", fname)
183 return nil
184 }
185 return fi
186 }
187
188
189
190
191 func (f *Func) dumpFile(phaseName string) {
192 fi := f.DumpFileForPhase(phaseName)
193 if fi != nil {
194 p := stringFuncPrinter{w: fi}
195 fprintFunc(p, f)
196 fi.Close()
197 }
198 }
199
200 type pass struct {
201 name string
202 fn func(*Func)
203 required bool
204 disabled bool
205 time bool
206 mem bool
207 stats int
208 debug int
209 test int
210 dump map[string]bool
211 }
212
213 func (p *pass) addDump(s string) {
214 if p.dump == nil {
215 p.dump = make(map[string]bool)
216 }
217 p.dump[s] = true
218 }
219
220 func (p *pass) String() string {
221 if p == nil {
222 return "nil pass"
223 }
224 return p.name
225 }
226
227
228 var (
229 checkEnabled = false
230 checkRandSeed = 0
231 )
232
233
234 var IntrinsicsDebug int
235 var IntrinsicsDisable bool
236
237 var BuildDebug int
238 var BuildTest int
239 var BuildStats int
240 var BuildDump map[string]bool = make(map[string]bool)
241
242 var GenssaDump map[string]bool = make(map[string]bool)
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261 func PhaseOption(phase, flag string, val int, valString string) string {
262 switch phase {
263 case "", "help":
264 lastcr := 0
265 phasenames := " check, all, build, intrinsics, genssa"
266 for _, p := range passes {
267 pn := strings.Replace(p.name, " ", "_", -1)
268 if len(pn)+len(phasenames)-lastcr > 70 {
269 phasenames += "\n "
270 lastcr = len(phasenames)
271 phasenames += pn
272 } else {
273 phasenames += ", " + pn
274 }
275 }
276 return `PhaseOptions usage:
277
278 go tool compile -d=ssa/<phase>/<flag>[=<value>|<function_name>]
279
280 where:
281
282 - <phase> is one of:
283 ` + phasenames + `
284
285 - <flag> is one of:
286 on, off, debug, mem, time, test, stats, dump, seed
287
288 - <value> defaults to 1
289
290 - <function_name> is required for the "dump" flag, and specifies the
291 name of function to dump after <phase>
292
293 Phase "all" supports flags "time", "mem", and "dump".
294 Phase "intrinsics" supports flags "on", "off", and "debug".
295 Phase "genssa" (assembly generation) supports the flag "dump".
296
297 If the "dump" flag is specified, the output is written on a file named
298 <phase>__<function_name>_<seq>.dump; otherwise it is directed to stdout.
299
300 Examples:
301
302 -d=ssa/check/on
303 enables checking after each phase
304
305 -d=ssa/check/seed=1234
306 enables checking after each phase, using 1234 to seed the PRNG
307 used for value order randomization
308
309 -d=ssa/all/time
310 enables time reporting for all phases
311
312 -d=ssa/prove/debug=2
313 sets debugging level to 2 in the prove pass
314
315 Be aware that when "/debug=X" is applied to a pass, some passes
316 will emit debug output for all functions, and other passes will
317 only emit debug output for functions that match the current
318 GOSSAFUNC value.
319
320 Multiple flags can be passed at once, by separating them with
321 commas. For example:
322
323 -d=ssa/check/on,ssa/all/time
324 `
325 }
326
327 if phase == "check" {
328 switch flag {
329 case "on":
330 checkEnabled = val != 0
331 debugPoset = checkEnabled
332 return ""
333 case "off":
334 checkEnabled = val == 0
335 debugPoset = checkEnabled
336 return ""
337 case "seed":
338 checkEnabled = true
339 checkRandSeed = val
340 debugPoset = checkEnabled
341 return ""
342 }
343 }
344
345 alltime := false
346 allmem := false
347 alldump := false
348 if phase == "all" {
349 switch flag {
350 case "time":
351 alltime = val != 0
352 case "mem":
353 allmem = val != 0
354 case "dump":
355 alldump = val != 0
356 if alldump {
357 BuildDump[valString] = true
358 GenssaDump[valString] = true
359 }
360 default:
361 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)
362 }
363 }
364
365 if phase == "intrinsics" {
366 switch flag {
367 case "on":
368 IntrinsicsDisable = val == 0
369 case "off":
370 IntrinsicsDisable = val != 0
371 case "debug":
372 IntrinsicsDebug = val
373 default:
374 return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/intrinsics/{on,off,debug})", flag, phase)
375 }
376 return ""
377 }
378 if phase == "build" {
379 switch flag {
380 case "debug":
381 BuildDebug = val
382 case "test":
383 BuildTest = val
384 case "stats":
385 BuildStats = val
386 case "dump":
387 BuildDump[valString] = true
388 default:
389 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)
390 }
391 return ""
392 }
393 if phase == "genssa" {
394 switch flag {
395 case "dump":
396 GenssaDump[valString] = true
397 default:
398 return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/genssa/dump=function_name)", flag, phase)
399 }
400 return ""
401 }
402
403 underphase := strings.Replace(phase, "_", " ", -1)
404 var re *regexp.Regexp
405 if phase[0] == '~' {
406 r, ok := regexp.Compile(underphase[1:])
407 if ok != nil {
408 return fmt.Sprintf("Error %s in regexp for phase %s, flag %s", ok.Error(), phase, flag)
409 }
410 re = r
411 }
412 matchedOne := false
413 for i, p := range passes {
414 if phase == "all" {
415 p.time = alltime
416 p.mem = allmem
417 if alldump {
418 p.addDump(valString)
419 }
420 passes[i] = p
421 matchedOne = true
422 } else if p.name == phase || p.name == underphase || re != nil && re.MatchString(p.name) {
423 switch flag {
424 case "on":
425 p.disabled = val == 0
426 case "off":
427 p.disabled = val != 0
428 case "time":
429 p.time = val != 0
430 case "mem":
431 p.mem = val != 0
432 case "debug":
433 p.debug = val
434 case "stats":
435 p.stats = val
436 case "test":
437 p.test = val
438 case "dump":
439 p.addDump(valString)
440 default:
441 return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
442 }
443 if p.disabled && p.required {
444 return fmt.Sprintf("Cannot disable required SSA phase %s using -d=ssa/%s debug option", phase, phase)
445 }
446 passes[i] = p
447 matchedOne = true
448 }
449 }
450 if matchedOne {
451 return ""
452 }
453 return fmt.Sprintf("Did not find a phase matching %s in -d=ssa/... debug option", phase)
454 }
455
456
457 var passes = [...]pass{
458 {name: "number lines", fn: numberLines, required: true},
459 {name: "early phielim and copyelim", fn: copyelim},
460 {name: "early deadcode", fn: deadcode},
461 {name: "short circuit", fn: shortcircuit},
462 {name: "decompose user", fn: decomposeUser, required: true},
463 {name: "pre-opt deadcode", fn: deadcode},
464 {name: "opt", fn: opt, required: true},
465 {name: "zero arg cse", fn: zcse, required: true},
466 {name: "opt deadcode", fn: deadcode, required: true},
467 {name: "generic cse", fn: cse},
468 {name: "phiopt", fn: phiopt},
469 {name: "gcse deadcode", fn: deadcode, required: true},
470 {name: "nilcheckelim", fn: nilcheckelim},
471 {name: "prove", fn: prove},
472 {name: "early fuse", fn: fuseEarly},
473 {name: "expand calls", fn: expandCalls, required: true},
474 {name: "decompose builtin", fn: postExpandCallsDecompose, required: true},
475 {name: "softfloat", fn: softfloat, required: true},
476 {name: "late opt", fn: opt, required: true},
477 {name: "dead auto elim", fn: elimDeadAutosGeneric},
478 {name: "sccp", fn: sccp},
479 {name: "generic deadcode", fn: deadcode, required: true},
480 {name: "branchelim", fn: branchelim},
481 {name: "late fuse", fn: fuseLate},
482 {name: "check bce", fn: checkbce},
483 {name: "dse", fn: dse},
484 {name: "memcombine", fn: memcombine},
485 {name: "writebarrier", fn: writebarrier, required: true},
486 {name: "insert resched checks", fn: insertLoopReschedChecks,
487 disabled: !buildcfg.Experiment.PreemptibleLoops},
488 {name: "lower", fn: lower, required: true},
489 {name: "addressing modes", fn: addressingModes, required: false},
490 {name: "late lower", fn: lateLower, required: true},
491 {name: "lowered deadcode for cse", fn: deadcode},
492 {name: "lowered cse", fn: cse},
493 {name: "elim unread autos", fn: elimUnreadAutos},
494 {name: "tighten tuple selectors", fn: tightenTupleSelectors, required: true},
495 {name: "lowered deadcode", fn: deadcode, required: true},
496 {name: "checkLower", fn: checkLower, required: true},
497 {name: "late phielim and copyelim", fn: copyelim},
498 {name: "tighten", fn: tighten, required: true},
499 {name: "late deadcode", fn: deadcode},
500 {name: "critical", fn: critical, required: true},
501 {name: "phi tighten", fn: phiTighten},
502 {name: "likelyadjust", fn: likelyadjust},
503 {name: "layout", fn: layout, required: true},
504 {name: "schedule", fn: schedule, required: true},
505 {name: "late nilcheck", fn: nilcheckelim2},
506 {name: "flagalloc", fn: flagalloc, required: true},
507 {name: "regalloc", fn: regalloc, required: true},
508 {name: "loop rotate", fn: loopRotate},
509 {name: "trim", fn: trim},
510 }
511
512
513
514
515
516 type constraint struct {
517 a, b string
518 }
519
520 var passOrder = [...]constraint{
521
522 {"dse", "insert resched checks"},
523
524 {"insert resched checks", "lower"},
525 {"insert resched checks", "tighten"},
526
527
528 {"generic cse", "prove"},
529
530 {"prove", "generic deadcode"},
531
532
533 {"generic cse", "dse"},
534
535 {"generic cse", "nilcheckelim"},
536
537 {"nilcheckelim", "generic deadcode"},
538
539 {"nilcheckelim", "late fuse"},
540
541 {"opt", "nilcheckelim"},
542
543 {"generic deadcode", "tighten"},
544 {"generic cse", "tighten"},
545
546 {"generic deadcode", "check bce"},
547
548 {"expand calls", "decompose builtin"},
549
550 {"decompose builtin", "late opt"},
551
552 {"decompose builtin", "softfloat"},
553
554 {"tighten tuple selectors", "schedule"},
555
556 {"critical", "phi tighten"},
557
558 {"critical", "layout"},
559
560 {"critical", "regalloc"},
561
562 {"schedule", "regalloc"},
563
564 {"lower", "late lower"},
565
566 {"late lower", "lowered cse"},
567
568 {"lower", "checkLower"},
569 {"lowered deadcode", "checkLower"},
570 {"late lower", "checkLower"},
571
572 {"schedule", "late nilcheck"},
573
574 {"schedule", "flagalloc"},
575
576 {"flagalloc", "regalloc"},
577
578 {"regalloc", "loop rotate"},
579
580 {"regalloc", "trim"},
581
582 {"late fuse", "memcombine"},
583
584 {"memcombine", "lower"},
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