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