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