Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.0.1-2020.1.5 / ir / lift.go
1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package ir
6
7 // This file defines the lifting pass which tries to "lift" Alloc
8 // cells (new/local variables) into SSA registers, replacing loads
9 // with the dominating stored value, eliminating loads and stores, and
10 // inserting φ- and σ-nodes as needed.
11
12 // Cited papers and resources:
13 //
14 // Ron Cytron et al. 1991. Efficiently computing SSA form...
15 // http://doi.acm.org/10.1145/115372.115320
16 //
17 // Cooper, Harvey, Kennedy.  2001.  A Simple, Fast Dominance Algorithm.
18 // Software Practice and Experience 2001, 4:1-10.
19 // http://www.hipersoft.rice.edu/grads/publications/dom14.pdf
20 //
21 // Daniel Berlin, llvmdev mailing list, 2012.
22 // http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-January/046638.html
23 // (Be sure to expand the whole thread.)
24 //
25 // C. Scott Ananian. 1997. The static single information form.
26 //
27 // Jeremy Singer. 2006. Static program analysis based on virtual register renaming.
28
29 // TODO(adonovan): opt: there are many optimizations worth evaluating, and
30 // the conventional wisdom for SSA construction is that a simple
31 // algorithm well engineered often beats those of better asymptotic
32 // complexity on all but the most egregious inputs.
33 //
34 // Danny Berlin suggests that the Cooper et al. algorithm for
35 // computing the dominance frontier is superior to Cytron et al.
36 // Furthermore he recommends that rather than computing the DF for the
37 // whole function then renaming all alloc cells, it may be cheaper to
38 // compute the DF for each alloc cell separately and throw it away.
39 //
40 // Consider exploiting liveness information to avoid creating dead
41 // φ-nodes which we then immediately remove.
42 //
43 // Also see many other "TODO: opt" suggestions in the code.
44
45 import (
46         "fmt"
47         "go/types"
48         "os"
49 )
50
51 // If true, show diagnostic information at each step of lifting.
52 // Very verbose.
53 const debugLifting = false
54
55 // domFrontier maps each block to the set of blocks in its dominance
56 // frontier.  The outer slice is conceptually a map keyed by
57 // Block.Index.  The inner slice is conceptually a set, possibly
58 // containing duplicates.
59 //
60 // TODO(adonovan): opt: measure impact of dups; consider a packed bit
61 // representation, e.g. big.Int, and bitwise parallel operations for
62 // the union step in the Children loop.
63 //
64 // domFrontier's methods mutate the slice's elements but not its
65 // length, so their receivers needn't be pointers.
66 //
67 type domFrontier [][]*BasicBlock
68
69 func (df domFrontier) add(u, v *BasicBlock) {
70         df[u.Index] = append(df[u.Index], v)
71 }
72
73 // build builds the dominance frontier df for the dominator tree of
74 // fn, using the algorithm found in A Simple, Fast Dominance
75 // Algorithm, Figure 5.
76 //
77 // TODO(adonovan): opt: consider Berlin approach, computing pruned SSA
78 // by pruning the entire IDF computation, rather than merely pruning
79 // the DF -> IDF step.
80 func (df domFrontier) build(fn *Function) {
81         for _, b := range fn.Blocks {
82                 if len(b.Preds) >= 2 {
83                         for _, p := range b.Preds {
84                                 runner := p
85                                 for runner != b.dom.idom {
86                                         df.add(runner, b)
87                                         runner = runner.dom.idom
88                                 }
89                         }
90                 }
91         }
92 }
93
94 func buildDomFrontier(fn *Function) domFrontier {
95         df := make(domFrontier, len(fn.Blocks))
96         df.build(fn)
97         return df
98 }
99
100 type postDomFrontier [][]*BasicBlock
101
102 func (rdf postDomFrontier) add(u, v *BasicBlock) {
103         rdf[u.Index] = append(rdf[u.Index], v)
104 }
105
106 func (rdf postDomFrontier) build(fn *Function) {
107         for _, b := range fn.Blocks {
108                 if len(b.Succs) >= 2 {
109                         for _, s := range b.Succs {
110                                 runner := s
111                                 for runner != b.pdom.idom {
112                                         rdf.add(runner, b)
113                                         runner = runner.pdom.idom
114                                 }
115                         }
116                 }
117         }
118 }
119
120 func buildPostDomFrontier(fn *Function) postDomFrontier {
121         rdf := make(postDomFrontier, len(fn.Blocks))
122         rdf.build(fn)
123         return rdf
124 }
125
126 func removeInstr(refs []Instruction, instr Instruction) []Instruction {
127         i := 0
128         for _, ref := range refs {
129                 if ref == instr {
130                         continue
131                 }
132                 refs[i] = ref
133                 i++
134         }
135         for j := i; j != len(refs); j++ {
136                 refs[j] = nil // aid GC
137         }
138         return refs[:i]
139 }
140
141 func clearInstrs(instrs []Instruction) {
142         for i := range instrs {
143                 instrs[i] = nil
144         }
145 }
146
147 // lift replaces local and new Allocs accessed only with
148 // load/store by IR registers, inserting φ- and σ-nodes where necessary.
149 // The result is a program in pruned SSI form.
150 //
151 // Preconditions:
152 // - fn has no dead blocks (blockopt has run).
153 // - Def/use info (Operands and Referrers) is up-to-date.
154 // - The dominator tree is up-to-date.
155 //
156 func lift(fn *Function) {
157         // TODO(adonovan): opt: lots of little optimizations may be
158         // worthwhile here, especially if they cause us to avoid
159         // buildDomFrontier.  For example:
160         //
161         // - Alloc never loaded?  Eliminate.
162         // - Alloc never stored?  Replace all loads with a zero constant.
163         // - Alloc stored once?  Replace loads with dominating store;
164         //   don't forget that an Alloc is itself an effective store
165         //   of zero.
166         // - Alloc used only within a single block?
167         //   Use degenerate algorithm avoiding φ-nodes.
168         // - Consider synergy with scalar replacement of aggregates (SRA).
169         //   e.g. *(&x.f) where x is an Alloc.
170         //   Perhaps we'd get better results if we generated this as x.f
171         //   i.e. Field(x, .f) instead of Load(FieldIndex(x, .f)).
172         //   Unclear.
173         //
174         // But we will start with the simplest correct code.
175         var df domFrontier
176         var rdf postDomFrontier
177         var closure *closure
178         var newPhis newPhiMap
179         var newSigmas newSigmaMap
180
181         // During this pass we will replace some BasicBlock.Instrs
182         // (allocs, loads and stores) with nil, keeping a count in
183         // BasicBlock.gaps.  At the end we will reset Instrs to the
184         // concatenation of all non-dead newPhis and non-nil Instrs
185         // for the block, reusing the original array if space permits.
186
187         // While we're here, we also eliminate 'rundefers'
188         // instructions in functions that contain no 'defer'
189         // instructions.
190         usesDefer := false
191
192         // Determine which allocs we can lift and number them densely.
193         // The renaming phase uses this numbering for compact maps.
194         numAllocs := 0
195         for _, b := range fn.Blocks {
196                 b.gaps = 0
197                 b.rundefers = 0
198                 for _, instr := range b.Instrs {
199                         switch instr := instr.(type) {
200                         case *Alloc:
201                                 if !liftable(instr) {
202                                         instr.index = -1
203                                         continue
204                                 }
205                                 index := -1
206                                 if numAllocs == 0 {
207                                         df = buildDomFrontier(fn)
208                                         rdf = buildPostDomFrontier(fn)
209                                         if len(fn.Blocks) > 2 {
210                                                 closure = transitiveClosure(fn)
211                                         }
212                                         newPhis = make(newPhiMap, len(fn.Blocks))
213                                         newSigmas = make(newSigmaMap, len(fn.Blocks))
214
215                                         if debugLifting {
216                                                 title := false
217                                                 for i, blocks := range df {
218                                                         if blocks != nil {
219                                                                 if !title {
220                                                                         fmt.Fprintf(os.Stderr, "Dominance frontier of %s:\n", fn)
221                                                                         title = true
222                                                                 }
223                                                                 fmt.Fprintf(os.Stderr, "\t%s: %s\n", fn.Blocks[i], blocks)
224                                                         }
225                                                 }
226                                         }
227                                 }
228                                 liftAlloc(closure, df, rdf, instr, newPhis, newSigmas)
229                                 index = numAllocs
230                                 numAllocs++
231                                 instr.index = index
232                         case *Defer:
233                                 usesDefer = true
234                         case *RunDefers:
235                                 b.rundefers++
236                         }
237                 }
238         }
239
240         if numAllocs > 0 {
241                 // renaming maps an alloc (keyed by index) to its replacement
242                 // value.  Initially the renaming contains nil, signifying the
243                 // zero constant of the appropriate type; we construct the
244                 // Const lazily at most once on each path through the domtree.
245                 // TODO(adonovan): opt: cache per-function not per subtree.
246                 renaming := make([]Value, numAllocs)
247
248                 // Renaming.
249                 rename(fn.Blocks[0], renaming, newPhis, newSigmas)
250
251                 simplifyPhis(newPhis)
252
253                 // Eliminate dead φ- and σ-nodes.
254                 markLiveNodes(fn.Blocks, newPhis, newSigmas)
255         }
256
257         // Prepend remaining live φ-nodes to each block and possibly kill rundefers.
258         for _, b := range fn.Blocks {
259                 var head []Instruction
260                 if numAllocs > 0 {
261                         nps := newPhis[b.Index]
262                         head = make([]Instruction, 0, len(nps))
263                         for _, pred := range b.Preds {
264                                 nss := newSigmas[pred.Index]
265                                 idx := pred.succIndex(b)
266                                 for _, newSigma := range nss {
267                                         if sigma := newSigma.sigmas[idx]; sigma != nil && sigma.live {
268                                                 head = append(head, sigma)
269
270                                                 // we didn't populate referrers before, as most
271                                                 // sigma nodes will be killed
272                                                 if refs := sigma.X.Referrers(); refs != nil {
273                                                         *refs = append(*refs, sigma)
274                                                 }
275                                         } else if sigma != nil {
276                                                 sigma.block = nil
277                                         }
278                                 }
279                         }
280                         for _, np := range nps {
281                                 if np.phi.live {
282                                         head = append(head, np.phi)
283                                 } else {
284                                         for _, edge := range np.phi.Edges {
285                                                 if refs := edge.Referrers(); refs != nil {
286                                                         *refs = removeInstr(*refs, np.phi)
287                                                 }
288                                         }
289                                         np.phi.block = nil
290                                 }
291                         }
292                 }
293
294                 rundefersToKill := b.rundefers
295                 if usesDefer {
296                         rundefersToKill = 0
297                 }
298
299                 j := len(head)
300                 if j+b.gaps+rundefersToKill == 0 {
301                         continue // fast path: no new phis or gaps
302                 }
303
304                 // We could do straight copies instead of element-wise copies
305                 // when both b.gaps and rundefersToKill are zero. However,
306                 // that seems to only be the case ~1% of the time, which
307                 // doesn't seem worth the extra branch.
308
309                 // Remove dead instructions, add phis and sigmas
310                 ns := len(b.Instrs) + j - b.gaps - rundefersToKill
311                 if ns <= cap(b.Instrs) {
312                         // b.Instrs has enough capacity to store all instructions
313
314                         // OPT(dh): check cap vs the actually required space; if
315                         // there is a big enough difference, it may be worth
316                         // allocating a new slice, to avoid pinning memory.
317                         dst := b.Instrs[:cap(b.Instrs)]
318                         i := len(dst) - 1
319                         for n := len(b.Instrs) - 1; n >= 0; n-- {
320                                 instr := dst[n]
321                                 if instr == nil {
322                                         continue
323                                 }
324                                 if !usesDefer {
325                                         if _, ok := instr.(*RunDefers); ok {
326                                                 continue
327                                         }
328                                 }
329                                 dst[i] = instr
330                                 i--
331                         }
332                         off := i + 1 - len(head)
333                         // aid GC
334                         clearInstrs(dst[:off])
335                         dst = dst[off:]
336                         copy(dst, head)
337                         b.Instrs = dst
338                 } else {
339                         // not enough space, so allocate a new slice and copy
340                         // over.
341                         dst := make([]Instruction, ns)
342                         copy(dst, head)
343
344                         for _, instr := range b.Instrs {
345                                 if instr == nil {
346                                         continue
347                                 }
348                                 if !usesDefer {
349                                         if _, ok := instr.(*RunDefers); ok {
350                                                 continue
351                                         }
352                                 }
353                                 dst[j] = instr
354                                 j++
355                         }
356                         b.Instrs = dst
357                 }
358         }
359
360         // Remove any fn.Locals that were lifted.
361         j := 0
362         for _, l := range fn.Locals {
363                 if l.index < 0 {
364                         fn.Locals[j] = l
365                         j++
366                 }
367         }
368         // Nil out fn.Locals[j:] to aid GC.
369         for i := j; i < len(fn.Locals); i++ {
370                 fn.Locals[i] = nil
371         }
372         fn.Locals = fn.Locals[:j]
373 }
374
375 func hasDirectReferrer(instr Instruction) bool {
376         for _, instr := range *instr.Referrers() {
377                 switch instr.(type) {
378                 case *Phi, *Sigma:
379                         // ignore
380                 default:
381                         return true
382                 }
383         }
384         return false
385 }
386
387 func markLiveNodes(blocks []*BasicBlock, newPhis newPhiMap, newSigmas newSigmaMap) {
388         // Phi and sigma nodes are considered live if a non-phi, non-sigma
389         // node uses them. Once we find a node that is live, we mark all
390         // of its operands as used, too.
391         for _, npList := range newPhis {
392                 for _, np := range npList {
393                         phi := np.phi
394                         if !phi.live && hasDirectReferrer(phi) {
395                                 markLivePhi(phi)
396                         }
397                 }
398         }
399         for _, npList := range newSigmas {
400                 for _, np := range npList {
401                         for _, sigma := range np.sigmas {
402                                 if sigma != nil && !sigma.live && hasDirectReferrer(sigma) {
403                                         markLiveSigma(sigma)
404                                 }
405                         }
406                 }
407         }
408         // Existing φ-nodes due to && and || operators
409         // are all considered live (see Go issue 19622).
410         for _, b := range blocks {
411                 for _, phi := range b.phis() {
412                         markLivePhi(phi.(*Phi))
413                 }
414         }
415 }
416
417 func markLivePhi(phi *Phi) {
418         phi.live = true
419         for _, rand := range phi.Edges {
420                 switch rand := rand.(type) {
421                 case *Phi:
422                         if !rand.live {
423                                 markLivePhi(rand)
424                         }
425                 case *Sigma:
426                         if !rand.live {
427                                 markLiveSigma(rand)
428                         }
429                 }
430         }
431 }
432
433 func markLiveSigma(sigma *Sigma) {
434         sigma.live = true
435         switch rand := sigma.X.(type) {
436         case *Phi:
437                 if !rand.live {
438                         markLivePhi(rand)
439                 }
440         case *Sigma:
441                 if !rand.live {
442                         markLiveSigma(rand)
443                 }
444         }
445 }
446
447 // simplifyPhis replaces trivial phis with non-phi alternatives. Phi
448 // nodes where all edges are identical, or consist of only the phi
449 // itself and one other value, may be replaced with the value.
450 func simplifyPhis(newPhis newPhiMap) {
451         // find all phis that are trivial and can be replaced with a
452         // non-phi value. run until we reach a fixpoint, because replacing
453         // a phi may make other phis trivial.
454         for changed := true; changed; {
455                 changed = false
456                 for _, npList := range newPhis {
457                         for _, np := range npList {
458                                 if np.phi.live {
459                                         // we're reusing 'live' to mean 'dead' in the context of simplifyPhis
460                                         continue
461                                 }
462                                 if r, ok := isUselessPhi(np.phi); ok {
463                                         // useless phi, replace its uses with the
464                                         // replacement value. the dead phi pass will clean
465                                         // up the phi afterwards.
466                                         replaceAll(np.phi, r)
467                                         np.phi.live = true
468                                         changed = true
469                                 }
470                         }
471                 }
472         }
473
474         for _, npList := range newPhis {
475                 for _, np := range npList {
476                         np.phi.live = false
477                 }
478         }
479 }
480
481 type BlockSet struct {
482         idx    int
483         values []bool
484         count  int
485 }
486
487 func NewBlockSet(size int) *BlockSet {
488         return &BlockSet{values: make([]bool, size)}
489 }
490
491 func (s *BlockSet) Set(s2 *BlockSet) {
492         copy(s.values, s2.values)
493         s.count = 0
494         for _, v := range s.values {
495                 if v {
496                         s.count++
497                 }
498         }
499 }
500
501 func (s *BlockSet) Num() int {
502         return s.count
503 }
504
505 func (s *BlockSet) Has(b *BasicBlock) bool {
506         if b.Index >= len(s.values) {
507                 return false
508         }
509         return s.values[b.Index]
510 }
511
512 // add adds b to the set and returns true if the set changed.
513 func (s *BlockSet) Add(b *BasicBlock) bool {
514         if s.values[b.Index] {
515                 return false
516         }
517         s.count++
518         s.values[b.Index] = true
519         s.idx = b.Index
520
521         return true
522 }
523
524 func (s *BlockSet) Clear() {
525         for j := range s.values {
526                 s.values[j] = false
527         }
528         s.count = 0
529 }
530
531 // take removes an arbitrary element from a set s and
532 // returns its index, or returns -1 if empty.
533 func (s *BlockSet) Take() int {
534         // [i, end]
535         for i := s.idx; i < len(s.values); i++ {
536                 if s.values[i] {
537                         s.values[i] = false
538                         s.idx = i
539                         s.count--
540                         return i
541                 }
542         }
543
544         // [start, i)
545         for i := 0; i < s.idx; i++ {
546                 if s.values[i] {
547                         s.values[i] = false
548                         s.idx = i
549                         s.count--
550                         return i
551                 }
552         }
553
554         return -1
555 }
556
557 type closure struct {
558         span       []uint32
559         reachables []interval
560 }
561
562 type interval uint32
563
564 const (
565         flagMask   = 1 << 31
566         numBits    = 20
567         lengthBits = 32 - numBits - 1
568         lengthMask = (1<<lengthBits - 1) << numBits
569         numMask    = 1<<numBits - 1
570 )
571
572 func (c closure) has(s, v *BasicBlock) bool {
573         idx := uint32(v.Index)
574         if idx == 1 || s.Dominates(v) {
575                 return true
576         }
577         r := c.reachable(s.Index)
578         for i := 0; i < len(r); i++ {
579                 inv := r[i]
580                 var start, end uint32
581                 if inv&flagMask == 0 {
582                         // small interval
583                         start = uint32(inv & numMask)
584                         end = start + uint32(inv&lengthMask)>>numBits
585                 } else {
586                         // large interval
587                         i++
588                         start = uint32(inv & numMask)
589                         end = uint32(r[i])
590                 }
591                 if idx >= start && idx <= end {
592                         return true
593                 }
594         }
595         return false
596 }
597
598 func (c closure) reachable(id int) []interval {
599         return c.reachables[c.span[id]:c.span[id+1]]
600 }
601
602 func (c closure) walk(current *BasicBlock, b *BasicBlock, visited []bool) {
603         visited[b.Index] = true
604         for _, succ := range b.Succs {
605                 if visited[succ.Index] {
606                         continue
607                 }
608                 visited[succ.Index] = true
609                 c.walk(current, succ, visited)
610         }
611 }
612
613 func transitiveClosure(fn *Function) *closure {
614         reachable := make([]bool, len(fn.Blocks))
615         c := &closure{}
616         c.span = make([]uint32, len(fn.Blocks)+1)
617
618         addInterval := func(start, end uint32) {
619                 if l := end - start; l <= 1<<lengthBits-1 {
620                         n := interval(l<<numBits | start)
621                         c.reachables = append(c.reachables, n)
622                 } else {
623                         n1 := interval(1<<31 | start)
624                         n2 := interval(end)
625                         c.reachables = append(c.reachables, n1, n2)
626                 }
627         }
628
629         for i, b := range fn.Blocks[1:] {
630                 for i := range reachable {
631                         reachable[i] = false
632                 }
633
634                 c.walk(b, b, reachable)
635                 start := ^uint32(0)
636                 for id, isReachable := range reachable {
637                         if !isReachable {
638                                 if start != ^uint32(0) {
639                                         end := uint32(id) - 1
640                                         addInterval(start, end)
641                                         start = ^uint32(0)
642                                 }
643                                 continue
644                         } else if start == ^uint32(0) {
645                                 start = uint32(id)
646                         }
647                 }
648                 if start != ^uint32(0) {
649                         addInterval(start, uint32(len(reachable))-1)
650                 }
651
652                 c.span[i+2] = uint32(len(c.reachables))
653         }
654
655         return c
656 }
657
658 // newPhi is a pair of a newly introduced φ-node and the lifted Alloc
659 // it replaces.
660 type newPhi struct {
661         phi   *Phi
662         alloc *Alloc
663 }
664
665 type newSigma struct {
666         alloc  *Alloc
667         sigmas []*Sigma
668 }
669
670 // newPhiMap records for each basic block, the set of newPhis that
671 // must be prepended to the block.
672 type newPhiMap [][]newPhi
673 type newSigmaMap [][]newSigma
674
675 func liftable(alloc *Alloc) bool {
676         // Don't lift aggregates into registers, because we don't have
677         // a way to express their zero-constants.
678         switch deref(alloc.Type()).Underlying().(type) {
679         case *types.Array, *types.Struct:
680                 return false
681         }
682
683         fn := alloc.Parent()
684         // Don't lift named return values in functions that defer
685         // calls that may recover from panic.
686         if fn.hasDefer {
687                 for _, nr := range fn.namedResults {
688                         if nr == alloc {
689                                 return false
690                         }
691                 }
692         }
693
694         for _, instr := range *alloc.Referrers() {
695                 switch instr := instr.(type) {
696                 case *Store:
697                         if instr.Val == alloc {
698                                 return false // address used as value
699                         }
700                         if instr.Addr != alloc {
701                                 panic("Alloc.Referrers is inconsistent")
702                         }
703                 case *Load:
704                         if instr.X != alloc {
705                                 panic("Alloc.Referrers is inconsistent")
706                         }
707
708                 case *DebugRef:
709                         // ok
710                 default:
711                         return false
712                 }
713         }
714
715         return true
716 }
717
718 // liftAlloc determines whether alloc can be lifted into registers,
719 // and if so, it populates newPhis with all the φ-nodes it may require
720 // and returns true.
721 func liftAlloc(closure *closure, df domFrontier, rdf postDomFrontier, alloc *Alloc, newPhis newPhiMap, newSigmas newSigmaMap) {
722         fn := alloc.Parent()
723
724         defblocks := fn.blockset(0)
725         useblocks := fn.blockset(1)
726         Aphi := fn.blockset(2)
727         Asigma := fn.blockset(3)
728         W := fn.blockset(4)
729
730         // Compute defblocks, the set of blocks containing a
731         // definition of the alloc cell.
732         for _, instr := range *alloc.Referrers() {
733                 // Bail out if we discover the alloc is not liftable;
734                 // the only operations permitted to use the alloc are
735                 // loads/stores into the cell, and DebugRef.
736                 switch instr := instr.(type) {
737                 case *Store:
738                         defblocks.Add(instr.Block())
739                 case *Load:
740                         useblocks.Add(instr.Block())
741                         for _, ref := range *instr.Referrers() {
742                                 useblocks.Add(ref.Block())
743                         }
744                 }
745         }
746         // The Alloc itself counts as a (zero) definition of the cell.
747         defblocks.Add(alloc.Block())
748
749         if debugLifting {
750                 fmt.Fprintln(os.Stderr, "\tlifting ", alloc, alloc.Name())
751         }
752
753         // Φ-insertion.
754         //
755         // What follows is the body of the main loop of the insert-φ
756         // function described by Cytron et al, but instead of using
757         // counter tricks, we just reset the 'hasAlready' and 'work'
758         // sets each iteration.  These are bitmaps so it's pretty cheap.
759
760         // Initialize W and work to defblocks.
761
762         for change := true; change; {
763                 change = false
764                 {
765                         // Traverse iterated dominance frontier, inserting φ-nodes.
766                         W.Set(defblocks)
767
768                         for i := W.Take(); i != -1; i = W.Take() {
769                                 n := fn.Blocks[i]
770                                 for _, y := range df[n.Index] {
771                                         if Aphi.Add(y) {
772                                                 if len(*alloc.Referrers()) == 0 {
773                                                         continue
774                                                 }
775                                                 live := false
776                                                 if closure == nil {
777                                                         live = true
778                                                 } else {
779                                                         for _, ref := range *alloc.Referrers() {
780                                                                 if _, ok := ref.(*Load); ok {
781                                                                         if closure.has(y, ref.Block()) {
782                                                                                 live = true
783                                                                                 break
784                                                                         }
785                                                                 }
786                                                         }
787                                                 }
788                                                 if !live {
789                                                         continue
790                                                 }
791
792                                                 // Create φ-node.
793                                                 // It will be prepended to v.Instrs later, if needed.
794                                                 phi := &Phi{
795                                                         Edges: make([]Value, len(y.Preds)),
796                                                 }
797
798                                                 phi.source = alloc.source
799                                                 phi.setType(deref(alloc.Type()))
800                                                 phi.block = y
801                                                 if debugLifting {
802                                                         fmt.Fprintf(os.Stderr, "\tplace %s = %s at block %s\n", phi.Name(), phi, y)
803                                                 }
804                                                 newPhis[y.Index] = append(newPhis[y.Index], newPhi{phi, alloc})
805
806                                                 for _, p := range y.Preds {
807                                                         useblocks.Add(p)
808                                                 }
809                                                 change = true
810                                                 if defblocks.Add(y) {
811                                                         W.Add(y)
812                                                 }
813                                         }
814                                 }
815                         }
816                 }
817
818                 {
819                         W.Set(useblocks)
820                         for i := W.Take(); i != -1; i = W.Take() {
821                                 n := fn.Blocks[i]
822                                 for _, y := range rdf[n.Index] {
823                                         if Asigma.Add(y) {
824                                                 sigmas := make([]*Sigma, 0, len(y.Succs))
825                                                 anyLive := false
826                                                 for _, succ := range y.Succs {
827                                                         live := false
828                                                         for _, ref := range *alloc.Referrers() {
829                                                                 if closure == nil || closure.has(succ, ref.Block()) {
830                                                                         live = true
831                                                                         anyLive = true
832                                                                         break
833                                                                 }
834                                                         }
835                                                         if live {
836                                                                 sigma := &Sigma{
837                                                                         From: y,
838                                                                         X:    alloc,
839                                                                 }
840                                                                 sigma.source = alloc.source
841                                                                 sigma.setType(deref(alloc.Type()))
842                                                                 sigma.block = succ
843                                                                 sigmas = append(sigmas, sigma)
844                                                         } else {
845                                                                 sigmas = append(sigmas, nil)
846                                                         }
847                                                 }
848
849                                                 if anyLive {
850                                                         newSigmas[y.Index] = append(newSigmas[y.Index], newSigma{alloc, sigmas})
851                                                         for _, s := range y.Succs {
852                                                                 defblocks.Add(s)
853                                                         }
854                                                         change = true
855                                                         if useblocks.Add(y) {
856                                                                 W.Add(y)
857                                                         }
858                                                 }
859                                         }
860                                 }
861                         }
862                 }
863         }
864 }
865
866 // replaceAll replaces all intraprocedural uses of x with y,
867 // updating x.Referrers and y.Referrers.
868 // Precondition: x.Referrers() != nil, i.e. x must be local to some function.
869 //
870 func replaceAll(x, y Value) {
871         var rands []*Value
872         pxrefs := x.Referrers()
873         pyrefs := y.Referrers()
874         for _, instr := range *pxrefs {
875                 rands = instr.Operands(rands[:0]) // recycle storage
876                 for _, rand := range rands {
877                         if *rand != nil {
878                                 if *rand == x {
879                                         *rand = y
880                                 }
881                         }
882                 }
883                 if pyrefs != nil {
884                         *pyrefs = append(*pyrefs, instr) // dups ok
885                 }
886         }
887         *pxrefs = nil // x is now unreferenced
888 }
889
890 // renamed returns the value to which alloc is being renamed,
891 // constructing it lazily if it's the implicit zero initialization.
892 //
893 func renamed(fn *Function, renaming []Value, alloc *Alloc) Value {
894         v := renaming[alloc.index]
895         if v == nil {
896                 v = emitConst(fn, zeroConst(deref(alloc.Type())))
897                 renaming[alloc.index] = v
898         }
899         return v
900 }
901
902 // rename implements the Cytron et al-based SSI renaming algorithm, a
903 // preorder traversal of the dominator tree replacing all loads of
904 // Alloc cells with the value stored to that cell by the dominating
905 // store instruction.
906 //
907 // renaming is a map from *Alloc (keyed by index number) to its
908 // dominating stored value; newPhis[x] is the set of new φ-nodes to be
909 // prepended to block x.
910 //
911 func rename(u *BasicBlock, renaming []Value, newPhis newPhiMap, newSigmas newSigmaMap) {
912         // Each φ-node becomes the new name for its associated Alloc.
913         for _, np := range newPhis[u.Index] {
914                 phi := np.phi
915                 alloc := np.alloc
916                 renaming[alloc.index] = phi
917         }
918
919         // Rename loads and stores of allocs.
920         for i, instr := range u.Instrs {
921                 switch instr := instr.(type) {
922                 case *Alloc:
923                         if instr.index >= 0 { // store of zero to Alloc cell
924                                 // Replace dominated loads by the zero value.
925                                 renaming[instr.index] = nil
926                                 if debugLifting {
927                                         fmt.Fprintf(os.Stderr, "\tkill alloc %s\n", instr)
928                                 }
929                                 // Delete the Alloc.
930                                 u.Instrs[i] = nil
931                                 u.gaps++
932                         }
933
934                 case *Store:
935                         if alloc, ok := instr.Addr.(*Alloc); ok && alloc.index >= 0 { // store to Alloc cell
936                                 // Replace dominated loads by the stored value.
937                                 renaming[alloc.index] = instr.Val
938                                 if debugLifting {
939                                         fmt.Fprintf(os.Stderr, "\tkill store %s; new value: %s\n",
940                                                 instr, instr.Val.Name())
941                                 }
942                                 if refs := instr.Addr.Referrers(); refs != nil {
943                                         *refs = removeInstr(*refs, instr)
944                                 }
945                                 if refs := instr.Val.Referrers(); refs != nil {
946                                         *refs = removeInstr(*refs, instr)
947                                 }
948                                 // Delete the Store.
949                                 u.Instrs[i] = nil
950                                 u.gaps++
951                         }
952
953                 case *Load:
954                         if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // load of Alloc cell
955                                 // In theory, we wouldn't be able to replace loads
956                                 // directly, because a loaded value could be used in
957                                 // different branches, in which case it should be
958                                 // replaced with different sigma nodes. But we can't
959                                 // simply defer replacement, either, because then
960                                 // later stores might incorrectly affect this load.
961                                 //
962                                 // To avoid doing renaming on _all_ values (instead of
963                                 // just loads and stores like we're doing), we make
964                                 // sure during code generation that each load is only
965                                 // used in one block. For example, in constant switch
966                                 // statements, where the tag is only evaluated once,
967                                 // we store it in a temporary and load it for each
968                                 // comparison, so that we have individual loads to
969                                 // replace.
970                                 newval := renamed(u.Parent(), renaming, alloc)
971                                 if debugLifting {
972                                         fmt.Fprintf(os.Stderr, "\tupdate load %s = %s with %s\n",
973                                                 instr.Name(), instr, newval)
974                                 }
975                                 replaceAll(instr, newval)
976                                 u.Instrs[i] = nil
977                                 u.gaps++
978                         }
979
980                 case *DebugRef:
981                         if x, ok := instr.X.(*Alloc); ok && x.index >= 0 {
982                                 if instr.IsAddr {
983                                         instr.X = renamed(u.Parent(), renaming, x)
984                                         instr.IsAddr = false
985
986                                         // Add DebugRef to instr.X's referrers.
987                                         if refs := instr.X.Referrers(); refs != nil {
988                                                 *refs = append(*refs, instr)
989                                         }
990                                 } else {
991                                         // A source expression denotes the address
992                                         // of an Alloc that was optimized away.
993                                         instr.X = nil
994
995                                         // Delete the DebugRef.
996                                         u.Instrs[i] = nil
997                                         u.gaps++
998                                 }
999                         }
1000                 }
1001         }
1002
1003         // update all outgoing sigma nodes with the dominating store
1004         for _, sigmas := range newSigmas[u.Index] {
1005                 for _, sigma := range sigmas.sigmas {
1006                         if sigma == nil {
1007                                 continue
1008                         }
1009                         sigma.X = renamed(u.Parent(), renaming, sigmas.alloc)
1010                 }
1011         }
1012
1013         // For each φ-node in a CFG successor, rename the edge.
1014         for succi, v := range u.Succs {
1015                 phis := newPhis[v.Index]
1016                 if len(phis) == 0 {
1017                         continue
1018                 }
1019                 i := v.predIndex(u)
1020                 for _, np := range phis {
1021                         phi := np.phi
1022                         alloc := np.alloc
1023                         // if there's a sigma node, use it, else use the dominating value
1024                         var newval Value
1025                         for _, sigmas := range newSigmas[u.Index] {
1026                                 if sigmas.alloc == alloc && sigmas.sigmas[succi] != nil {
1027                                         newval = sigmas.sigmas[succi]
1028                                         break
1029                                 }
1030                         }
1031                         if newval == nil {
1032                                 newval = renamed(u.Parent(), renaming, alloc)
1033                         }
1034                         if debugLifting {
1035                                 fmt.Fprintf(os.Stderr, "\tsetphi %s edge %s -> %s (#%d) (alloc=%s) := %s\n",
1036                                         phi.Name(), u, v, i, alloc.Name(), newval.Name())
1037                         }
1038                         phi.Edges[i] = newval
1039                         if prefs := newval.Referrers(); prefs != nil {
1040                                 *prefs = append(*prefs, phi)
1041                         }
1042                 }
1043         }
1044
1045         // Continue depth-first recursion over domtree, pushing a
1046         // fresh copy of the renaming map for each subtree.
1047         r := make([]Value, len(renaming))
1048         for _, v := range u.dom.children {
1049                 // XXX add debugging
1050                 copy(r, renaming)
1051
1052                 // on entry to a block, the incoming sigma nodes become the new values for their alloc
1053                 if idx := u.succIndex(v); idx != -1 {
1054                         for _, sigma := range newSigmas[u.Index] {
1055                                 if sigma.sigmas[idx] != nil {
1056                                         r[sigma.alloc.index] = sigma.sigmas[idx]
1057                                 }
1058                         }
1059                 }
1060                 rename(v, r, newPhis, newSigmas)
1061         }
1062
1063 }