github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/ssa/deadcode.go (about)

     1  // Copyright 2015 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 ssa
     6  
     7  // findlive returns the reachable blocks and live values in f.
     8  func findlive(f *Func) (reachable []bool, live []bool) {
     9  	reachable = reachableBlocks(f)
    10  	live = liveValues(f, reachable)
    11  	return
    12  }
    13  
    14  // reachableBlocks returns the reachable blocks in f.
    15  func reachableBlocks(f *Func) []bool {
    16  	reachable := make([]bool, f.NumBlocks())
    17  	reachable[f.Entry.ID] = true
    18  	p := []*Block{f.Entry} // stack-like worklist
    19  	for len(p) > 0 {
    20  		// Pop a reachable block
    21  		b := p[len(p)-1]
    22  		p = p[:len(p)-1]
    23  		// Mark successors as reachable
    24  		s := b.Succs
    25  		if b.Kind == BlockFirst {
    26  			s = s[:1]
    27  		}
    28  		for _, e := range s {
    29  			c := e.b
    30  			if !reachable[c.ID] {
    31  				reachable[c.ID] = true
    32  				p = append(p, c) // push
    33  			}
    34  		}
    35  	}
    36  	return reachable
    37  }
    38  
    39  // liveValues returns the live values in f.
    40  // reachable is a map from block ID to whether the block is reachable.
    41  func liveValues(f *Func, reachable []bool) []bool {
    42  	live := make([]bool, f.NumValues())
    43  
    44  	// After regalloc, consider all values to be live.
    45  	// See the comment at the top of regalloc.go and in deadcode for details.
    46  	if f.RegAlloc != nil {
    47  		for i := range live {
    48  			live[i] = true
    49  		}
    50  		return live
    51  	}
    52  
    53  	// Find all live values
    54  	var q []*Value // stack-like worklist of unscanned values
    55  
    56  	// Starting set: all control values of reachable blocks are live.
    57  	for _, b := range f.Blocks {
    58  		if !reachable[b.ID] {
    59  			continue
    60  		}
    61  		if v := b.Control; v != nil && !live[v.ID] {
    62  			live[v.ID] = true
    63  			q = append(q, v)
    64  		}
    65  	}
    66  
    67  	// Compute transitive closure of live values.
    68  	for len(q) > 0 {
    69  		// pop a reachable value
    70  		v := q[len(q)-1]
    71  		q = q[:len(q)-1]
    72  		for i, x := range v.Args {
    73  			if v.Op == OpPhi && !reachable[v.Block.Preds[i].b.ID] {
    74  				continue
    75  			}
    76  			if !live[x.ID] {
    77  				live[x.ID] = true
    78  				q = append(q, x) // push
    79  			}
    80  		}
    81  	}
    82  
    83  	return live
    84  }
    85  
    86  // deadcode removes dead code from f.
    87  func deadcode(f *Func) {
    88  	// deadcode after regalloc is forbidden for now. Regalloc
    89  	// doesn't quite generate legal SSA which will lead to some
    90  	// required moves being eliminated. See the comment at the
    91  	// top of regalloc.go for details.
    92  	if f.RegAlloc != nil {
    93  		f.Fatalf("deadcode after regalloc")
    94  	}
    95  
    96  	// Find reachable blocks.
    97  	reachable := reachableBlocks(f)
    98  
    99  	// Get rid of edges from dead to live code.
   100  	for _, b := range f.Blocks {
   101  		if reachable[b.ID] {
   102  			continue
   103  		}
   104  		for i := 0; i < len(b.Succs); {
   105  			e := b.Succs[i]
   106  			if reachable[e.b.ID] {
   107  				b.removeEdge(i)
   108  			} else {
   109  				i++
   110  			}
   111  		}
   112  	}
   113  
   114  	// Get rid of dead edges from live code.
   115  	for _, b := range f.Blocks {
   116  		if !reachable[b.ID] {
   117  			continue
   118  		}
   119  		if b.Kind != BlockFirst {
   120  			continue
   121  		}
   122  		b.removeEdge(1)
   123  		b.Kind = BlockPlain
   124  		b.Likely = BranchUnknown
   125  	}
   126  
   127  	// Splice out any copies introduced during dead block removal.
   128  	copyelim(f)
   129  
   130  	// Find live values.
   131  	live := liveValues(f, reachable)
   132  
   133  	// Remove dead & duplicate entries from namedValues map.
   134  	s := f.newSparseSet(f.NumValues())
   135  	defer f.retSparseSet(s)
   136  	i := 0
   137  	for _, name := range f.Names {
   138  		j := 0
   139  		s.clear()
   140  		values := f.NamedValues[name]
   141  		for _, v := range values {
   142  			if live[v.ID] && !s.contains(v.ID) {
   143  				values[j] = v
   144  				j++
   145  				s.add(v.ID)
   146  			}
   147  		}
   148  		if j == 0 {
   149  			delete(f.NamedValues, name)
   150  		} else {
   151  			f.Names[i] = name
   152  			i++
   153  			for k := len(values) - 1; k >= j; k-- {
   154  				values[k] = nil
   155  			}
   156  			f.NamedValues[name] = values[:j]
   157  		}
   158  	}
   159  	for k := len(f.Names) - 1; k >= i; k-- {
   160  		f.Names[k] = LocalSlot{}
   161  	}
   162  	f.Names = f.Names[:i]
   163  
   164  	// Unlink values.
   165  	for _, b := range f.Blocks {
   166  		if !reachable[b.ID] {
   167  			b.SetControl(nil)
   168  		}
   169  		for _, v := range b.Values {
   170  			if !live[v.ID] {
   171  				v.resetArgs()
   172  			}
   173  		}
   174  	}
   175  
   176  	// Remove dead values from blocks' value list. Return dead
   177  	// values to the allocator.
   178  	for _, b := range f.Blocks {
   179  		i := 0
   180  		for _, v := range b.Values {
   181  			if live[v.ID] {
   182  				b.Values[i] = v
   183  				i++
   184  			} else {
   185  				f.freeValue(v)
   186  			}
   187  		}
   188  		// aid GC
   189  		tail := b.Values[i:]
   190  		for j := range tail {
   191  			tail[j] = nil
   192  		}
   193  		b.Values = b.Values[:i]
   194  	}
   195  
   196  	// Remove unreachable blocks. Return dead blocks to allocator.
   197  	i = 0
   198  	for _, b := range f.Blocks {
   199  		if reachable[b.ID] {
   200  			f.Blocks[i] = b
   201  			i++
   202  		} else {
   203  			if len(b.Values) > 0 {
   204  				b.Fatalf("live values in unreachable block %v: %v", b, b.Values)
   205  			}
   206  			f.freeBlock(b)
   207  		}
   208  	}
   209  	// zero remainder to help GC
   210  	tail := f.Blocks[i:]
   211  	for j := range tail {
   212  		tail[j] = nil
   213  	}
   214  	f.Blocks = f.Blocks[:i]
   215  }
   216  
   217  // removeEdge removes the i'th outgoing edge from b (and
   218  // the corresponding incoming edge from b.Succs[i].b).
   219  func (b *Block) removeEdge(i int) {
   220  	e := b.Succs[i]
   221  	c := e.b
   222  	j := e.i
   223  
   224  	// Adjust b.Succs
   225  	b.removeSucc(i)
   226  
   227  	// Adjust c.Preds
   228  	c.removePred(j)
   229  
   230  	// Remove phi args from c's phis.
   231  	n := len(c.Preds)
   232  	for _, v := range c.Values {
   233  		if v.Op != OpPhi {
   234  			continue
   235  		}
   236  		v.Args[j].Uses--
   237  		v.Args[j] = v.Args[n]
   238  		v.Args[n] = nil
   239  		v.Args = v.Args[:n]
   240  		phielimValue(v)
   241  		// Note: this is trickier than it looks. Replacing
   242  		// a Phi with a Copy can in general cause problems because
   243  		// Phi and Copy don't have exactly the same semantics.
   244  		// Phi arguments always come from a predecessor block,
   245  		// whereas copies don't. This matters in loops like:
   246  		// 1: x = (Phi y)
   247  		//    y = (Add x 1)
   248  		//    goto 1
   249  		// If we replace Phi->Copy, we get
   250  		// 1: x = (Copy y)
   251  		//    y = (Add x 1)
   252  		//    goto 1
   253  		// (Phi y) refers to the *previous* value of y, whereas
   254  		// (Copy y) refers to the *current* value of y.
   255  		// The modified code has a cycle and the scheduler
   256  		// will barf on it.
   257  		//
   258  		// Fortunately, this situation can only happen for dead
   259  		// code loops. We know the code we're working with is
   260  		// not dead, so we're ok.
   261  		// Proof: If we have a potential bad cycle, we have a
   262  		// situation like this:
   263  		//   x = (Phi z)
   264  		//   y = (op1 x ...)
   265  		//   z = (op2 y ...)
   266  		// Where opX are not Phi ops. But such a situation
   267  		// implies a cycle in the dominator graph. In the
   268  		// example, x.Block dominates y.Block, y.Block dominates
   269  		// z.Block, and z.Block dominates x.Block (treating
   270  		// "dominates" as reflexive).  Cycles in the dominator
   271  		// graph can only happen in an unreachable cycle.
   272  	}
   273  }