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