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