1
2
3
4
5 package ssa
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 import (
42 "fmt"
43 "go/token"
44 "math/big"
45 "os"
46
47 "golang.org/x/tools/internal/typeparams"
48 )
49
50
51
52 const debugLifting = false
53
54
55
56
57
58
59
60
61
62
63
64
65 type domFrontier [][]*BasicBlock
66
67 func (df domFrontier) add(u, v *BasicBlock) {
68 p := &df[u.Index]
69 *p = append(*p, v)
70 }
71
72
73
74
75
76
77
78 func (df domFrontier) build(u *BasicBlock) {
79
80 for _, child := range u.dom.children {
81 df.build(child)
82 }
83 for _, vb := range u.Succs {
84 if v := vb.dom; v.idom != u {
85 df.add(u, vb)
86 }
87 }
88 for _, w := range u.dom.children {
89 for _, vb := range df[w.Index] {
90
91 if v := vb.dom; v.idom != u {
92 df.add(u, vb)
93 }
94 }
95 }
96 }
97
98 func buildDomFrontier(fn *Function) domFrontier {
99 df := make(domFrontier, len(fn.Blocks))
100 df.build(fn.Blocks[0])
101 if fn.Recover != nil {
102 df.build(fn.Recover)
103 }
104 return df
105 }
106
107 func removeInstr(refs []Instruction, instr Instruction) []Instruction {
108 return removeInstrsIf(refs, func(i Instruction) bool { return i == instr })
109 }
110
111 func removeInstrsIf(refs []Instruction, p func(Instruction) bool) []Instruction {
112
113 i := 0
114 for _, ref := range refs {
115 if p(ref) {
116 continue
117 }
118 refs[i] = ref
119 i++
120 }
121 for j := i; j != len(refs); j++ {
122 refs[j] = nil
123 }
124 return refs[:i]
125 }
126
127
128
129
130
131
132
133
134
135 func lift(fn *Function) {
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154 df := buildDomFrontier(fn)
155
156 if debugLifting {
157 title := false
158 for i, blocks := range df {
159 if blocks != nil {
160 if !title {
161 fmt.Fprintf(os.Stderr, "Dominance frontier of %s:\n", fn)
162 title = true
163 }
164 fmt.Fprintf(os.Stderr, "\t%s: %s\n", fn.Blocks[i], blocks)
165 }
166 }
167 }
168
169 newPhis := make(newPhiMap)
170
171
172
173
174
175
176
177
178
179
180 usesDefer := false
181
182
183
184
185 fresh := 1000
186
187
188
189 numAllocs := 0
190 for _, b := range fn.Blocks {
191 b.gaps = 0
192 b.rundefers = 0
193 for _, instr := range b.Instrs {
194 switch instr := instr.(type) {
195 case *Alloc:
196 index := -1
197 if liftAlloc(df, instr, newPhis, &fresh) {
198 index = numAllocs
199 numAllocs++
200 }
201 instr.index = index
202 case *Defer:
203 usesDefer = true
204 case *RunDefers:
205 b.rundefers++
206 }
207 }
208 }
209
210
211
212
213
214
215 renaming := make([]Value, numAllocs)
216
217
218 rename(fn.Blocks[0], renaming, newPhis)
219
220
221 removeDeadPhis(fn.Blocks, newPhis)
222
223
224 for _, b := range fn.Blocks {
225 nps := newPhis[b]
226 j := len(nps)
227
228 rundefersToKill := b.rundefers
229 if usesDefer {
230 rundefersToKill = 0
231 }
232
233 if j+b.gaps+rundefersToKill == 0 {
234 continue
235 }
236
237
238
239
240 dst := make([]Instruction, len(b.Instrs)+j-b.gaps-rundefersToKill)
241 for i, np := range nps {
242 dst[i] = np.phi
243 }
244 for _, instr := range b.Instrs {
245 if instr == nil {
246 continue
247 }
248 if !usesDefer {
249 if _, ok := instr.(*RunDefers); ok {
250 continue
251 }
252 }
253 dst[j] = instr
254 j++
255 }
256 b.Instrs = dst
257 }
258
259
260 j := 0
261 for _, l := range fn.Locals {
262 if l.index < 0 {
263 fn.Locals[j] = l
264 j++
265 }
266 }
267
268 for i := j; i < len(fn.Locals); i++ {
269 fn.Locals[i] = nil
270 }
271 fn.Locals = fn.Locals[:j]
272 }
273
274
275
276 func removeDeadPhis(blocks []*BasicBlock, newPhis newPhiMap) {
277
278
279
280
281
282
283
284 livePhis := make(map[*Phi]bool)
285 for _, npList := range newPhis {
286 for _, np := range npList {
287 phi := np.phi
288 if !livePhis[phi] && phiHasDirectReferrer(phi) {
289 markLivePhi(livePhis, phi)
290 }
291 }
292 }
293
294
295
296 for _, b := range blocks {
297 for _, phi := range b.phis() {
298 markLivePhi(livePhis, phi.(*Phi))
299 }
300 }
301
302
303 for block, npList := range newPhis {
304 j := 0
305 for _, np := range npList {
306 if livePhis[np.phi] {
307 npList[j] = np
308 j++
309 } else {
310
311 for _, val := range np.phi.Edges {
312 if refs := val.Referrers(); refs != nil {
313 *refs = removeInstr(*refs, np.phi)
314 }
315 }
316 np.phi.block = nil
317 }
318 }
319 newPhis[block] = npList[:j]
320 }
321 }
322
323
324
325 func markLivePhi(livePhis map[*Phi]bool, phi *Phi) {
326 livePhis[phi] = true
327 for _, rand := range phi.Operands(nil) {
328 if q, ok := (*rand).(*Phi); ok {
329 if !livePhis[q] {
330 markLivePhi(livePhis, q)
331 }
332 }
333 }
334 }
335
336
337
338
339 func phiHasDirectReferrer(phi *Phi) bool {
340 for _, instr := range *phi.Referrers() {
341 if _, ok := instr.(*Phi); !ok {
342 return true
343 }
344 }
345 return false
346 }
347
348 type blockSet struct{ big.Int }
349
350
351 func (s *blockSet) add(b *BasicBlock) bool {
352 i := b.Index
353 if s.Bit(i) != 0 {
354 return false
355 }
356 s.SetBit(&s.Int, i, 1)
357 return true
358 }
359
360
361
362 func (s *blockSet) take() int {
363 l := s.BitLen()
364 for i := 0; i < l; i++ {
365 if s.Bit(i) == 1 {
366 s.SetBit(&s.Int, i, 0)
367 return i
368 }
369 }
370 return -1
371 }
372
373
374
375 type newPhi struct {
376 phi *Phi
377 alloc *Alloc
378 }
379
380
381
382 type newPhiMap map[*BasicBlock][]newPhi
383
384
385
386
387
388
389 func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) bool {
390
391
392 if fn := alloc.Parent(); fn.Recover != nil {
393 for _, nr := range fn.namedResults {
394 if nr == alloc {
395 return false
396 }
397 }
398 }
399
400
401
402 var defblocks blockSet
403 for _, instr := range *alloc.Referrers() {
404
405
406
407 switch instr := instr.(type) {
408 case *Store:
409 if instr.Val == alloc {
410 return false
411 }
412 if instr.Addr != alloc {
413 panic("Alloc.Referrers is inconsistent")
414 }
415 defblocks.add(instr.Block())
416 case *UnOp:
417 if instr.Op != token.MUL {
418 return false
419 }
420 if instr.X != alloc {
421 panic("Alloc.Referrers is inconsistent")
422 }
423 case *DebugRef:
424
425 default:
426 return false
427 }
428 }
429
430 defblocks.add(alloc.Block())
431
432 if debugLifting {
433 fmt.Fprintln(os.Stderr, "\tlifting ", alloc, alloc.Name())
434 }
435
436 fn := alloc.Parent()
437
438
439
440
441
442
443
444
445
446
447 var hasAlready blockSet
448
449
450 var work blockSet = defblocks
451 var W blockSet
452 W.Set(&defblocks.Int)
453
454
455 for i := W.take(); i != -1; i = W.take() {
456 u := fn.Blocks[i]
457 for _, v := range df[u.Index] {
458 if hasAlready.add(v) {
459
460
461 phi := &Phi{
462 Edges: make([]Value, len(v.Preds)),
463 Comment: alloc.Comment,
464 }
465
466 phi.setNum(*fresh)
467 *fresh++
468
469 phi.pos = alloc.Pos()
470 phi.setType(typeparams.MustDeref(alloc.Type()))
471 phi.block = v
472 if debugLifting {
473 fmt.Fprintf(os.Stderr, "\tplace %s = %s at block %s\n", phi.Name(), phi, v)
474 }
475 newPhis[v] = append(newPhis[v], newPhi{phi, alloc})
476
477 if work.add(v) {
478 W.add(v)
479 }
480 }
481 }
482 }
483
484 return true
485 }
486
487
488
489
490 func replaceAll(x, y Value) {
491 var rands []*Value
492 pxrefs := x.Referrers()
493 pyrefs := y.Referrers()
494 for _, instr := range *pxrefs {
495 rands = instr.Operands(rands[:0])
496 for _, rand := range rands {
497 if *rand != nil {
498 if *rand == x {
499 *rand = y
500 }
501 }
502 }
503 if pyrefs != nil {
504 *pyrefs = append(*pyrefs, instr)
505 }
506 }
507 *pxrefs = nil
508 }
509
510
511
512 func renamed(renaming []Value, alloc *Alloc) Value {
513 v := renaming[alloc.index]
514 if v == nil {
515 v = zeroConst(typeparams.MustDeref(alloc.Type()))
516 renaming[alloc.index] = v
517 }
518 return v
519 }
520
521
522
523
524
525
526
527
528
529
530 func rename(u *BasicBlock, renaming []Value, newPhis newPhiMap) {
531
532 for _, np := range newPhis[u] {
533 phi := np.phi
534 alloc := np.alloc
535 renaming[alloc.index] = phi
536 }
537
538
539 for i, instr := range u.Instrs {
540 switch instr := instr.(type) {
541 case *Alloc:
542 if instr.index >= 0 {
543
544 renaming[instr.index] = nil
545 if debugLifting {
546 fmt.Fprintf(os.Stderr, "\tkill alloc %s\n", instr)
547 }
548
549 u.Instrs[i] = nil
550 u.gaps++
551 }
552
553 case *Store:
554 if alloc, ok := instr.Addr.(*Alloc); ok && alloc.index >= 0 {
555
556 renaming[alloc.index] = instr.Val
557 if debugLifting {
558 fmt.Fprintf(os.Stderr, "\tkill store %s; new value: %s\n",
559 instr, instr.Val.Name())
560 }
561
562 if refs := instr.Val.Referrers(); refs != nil {
563 *refs = removeInstr(*refs, instr)
564 }
565
566 u.Instrs[i] = nil
567 u.gaps++
568 }
569
570 case *UnOp:
571 if instr.Op == token.MUL {
572 if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 {
573 newval := renamed(renaming, alloc)
574 if debugLifting {
575 fmt.Fprintf(os.Stderr, "\tupdate load %s = %s with %s\n",
576 instr.Name(), instr, newval.Name())
577 }
578
579
580
581 replaceAll(instr, newval)
582
583 u.Instrs[i] = nil
584 u.gaps++
585 }
586 }
587
588 case *DebugRef:
589 if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 {
590 if instr.IsAddr {
591 instr.X = renamed(renaming, alloc)
592 instr.IsAddr = false
593
594
595 if refs := instr.X.Referrers(); refs != nil {
596 *refs = append(*refs, instr)
597 }
598 } else {
599
600
601 instr.X = nil
602
603
604 u.Instrs[i] = nil
605 u.gaps++
606 }
607 }
608 }
609 }
610
611
612 for _, v := range u.Succs {
613 phis := newPhis[v]
614 if len(phis) == 0 {
615 continue
616 }
617 i := v.predIndex(u)
618 for _, np := range phis {
619 phi := np.phi
620 alloc := np.alloc
621 newval := renamed(renaming, alloc)
622 if debugLifting {
623 fmt.Fprintf(os.Stderr, "\tsetphi %s edge %s -> %s (#%d) (alloc=%s) := %s\n",
624 phi.Name(), u, v, i, alloc.Name(), newval.Name())
625 }
626 phi.Edges[i] = newval
627 if prefs := newval.Referrers(); prefs != nil {
628 *prefs = append(*prefs, phi)
629 }
630 }
631 }
632
633
634
635 for i, v := range u.dom.children {
636 r := renaming
637 if i < len(u.dom.children)-1 {
638
639
640 r = make([]Value, len(renaming))
641 copy(r, renaming)
642 }
643 rename(v, r, newPhis)
644 }
645
646 }
647
View as plain text