github.com/bir3/gocompiler@v0.9.2202/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  import (
     8  	"github.com/bir3/gocompiler/src/cmd/internal/src"
     9  )
    10  
    11  // findlive returns the reachable blocks and live values in f.
    12  // The caller should call f.Cache.freeBoolSlice(live) when it is done with it.
    13  func findlive(f *Func) (reachable []bool, live []bool) {
    14  	reachable = ReachableBlocks(f)
    15  	var order []*Value
    16  	live, order = liveValues(f, reachable)
    17  	f.Cache.freeValueSlice(order)
    18  	return
    19  }
    20  
    21  // ReachableBlocks returns the reachable blocks in f.
    22  func ReachableBlocks(f *Func) []bool {
    23  	reachable := make([]bool, f.NumBlocks())
    24  	reachable[f.Entry.ID] = true
    25  	p := make([]*Block, 0, 64)	// stack-like worklist
    26  	p = append(p, f.Entry)
    27  	for len(p) > 0 {
    28  		// Pop a reachable block
    29  		b := p[len(p)-1]
    30  		p = p[:len(p)-1]
    31  		// Mark successors as reachable
    32  		s := b.Succs
    33  		if b.Kind == BlockFirst {
    34  			s = s[:1]
    35  		}
    36  		for _, e := range s {
    37  			c := e.b
    38  			if int(c.ID) >= len(reachable) {
    39  				f.Fatalf("block %s >= f.NumBlocks()=%d?", c, len(reachable))
    40  			}
    41  			if !reachable[c.ID] {
    42  				reachable[c.ID] = true
    43  				p = append(p, c)	// push
    44  			}
    45  		}
    46  	}
    47  	return reachable
    48  }
    49  
    50  // liveValues returns the live values in f and a list of values that are eligible
    51  // to be statements in reversed data flow order.
    52  // The second result is used to help conserve statement boundaries for debugging.
    53  // reachable is a map from block ID to whether the block is reachable.
    54  // The caller should call f.Cache.freeBoolSlice(live) and f.Cache.freeValueSlice(liveOrderStmts).
    55  // when they are done with the return values.
    56  func liveValues(f *Func, reachable []bool) (live []bool, liveOrderStmts []*Value) {
    57  	live = f.Cache.allocBoolSlice(f.NumValues())
    58  	liveOrderStmts = f.Cache.allocValueSlice(f.NumValues())[:0]
    59  
    60  	// After regalloc, consider all values to be live.
    61  	// See the comment at the top of regalloc.go and in deadcode for details.
    62  	if f.RegAlloc != nil {
    63  		for i := range live {
    64  			live[i] = true
    65  		}
    66  		return
    67  	}
    68  
    69  	// Record all the inline indexes we need
    70  	var liveInlIdx map[int]bool
    71  	pt := f.Config.ctxt.PosTable
    72  	for _, b := range f.Blocks {
    73  		for _, v := range b.Values {
    74  			i := pt.Pos(v.Pos).Base().InliningIndex()
    75  			if i < 0 {
    76  				continue
    77  			}
    78  			if liveInlIdx == nil {
    79  				liveInlIdx = map[int]bool{}
    80  			}
    81  			liveInlIdx[i] = true
    82  		}
    83  		i := pt.Pos(b.Pos).Base().InliningIndex()
    84  		if i < 0 {
    85  			continue
    86  		}
    87  		if liveInlIdx == nil {
    88  			liveInlIdx = map[int]bool{}
    89  		}
    90  		liveInlIdx[i] = true
    91  	}
    92  
    93  	// Find all live values
    94  	q := f.Cache.allocValueSlice(f.NumValues())[:0]
    95  	defer f.Cache.freeValueSlice(q)
    96  
    97  	// Starting set: all control values of reachable blocks are live.
    98  	// Calls are live (because callee can observe the memory state).
    99  	for _, b := range f.Blocks {
   100  		if !reachable[b.ID] {
   101  			continue
   102  		}
   103  		for _, v := range b.ControlValues() {
   104  			if !live[v.ID] {
   105  				live[v.ID] = true
   106  				q = append(q, v)
   107  				if v.Pos.IsStmt() != src.PosNotStmt {
   108  					liveOrderStmts = append(liveOrderStmts, v)
   109  				}
   110  			}
   111  		}
   112  		for _, v := range b.Values {
   113  			if (opcodeTable[v.Op].call || opcodeTable[v.Op].hasSideEffects || opcodeTable[v.Op].nilCheck) && !live[v.ID] {
   114  				live[v.ID] = true
   115  				q = append(q, v)
   116  				if v.Pos.IsStmt() != src.PosNotStmt {
   117  					liveOrderStmts = append(liveOrderStmts, v)
   118  				}
   119  			}
   120  			if v.Op == OpInlMark {
   121  				if !liveInlIdx[int(v.AuxInt)] {
   122  					// We don't need marks for bodies that
   123  					// have been completely optimized away.
   124  					// TODO: save marks only for bodies which
   125  					// have a faulting instruction or a call?
   126  					continue
   127  				}
   128  				live[v.ID] = true
   129  				q = append(q, v)
   130  				if v.Pos.IsStmt() != src.PosNotStmt {
   131  					liveOrderStmts = append(liveOrderStmts, v)
   132  				}
   133  			}
   134  		}
   135  	}
   136  
   137  	// Compute transitive closure of live values.
   138  	for len(q) > 0 {
   139  		// pop a reachable value
   140  		v := q[len(q)-1]
   141  		q[len(q)-1] = nil
   142  		q = q[:len(q)-1]
   143  		for i, x := range v.Args {
   144  			if v.Op == OpPhi && !reachable[v.Block.Preds[i].b.ID] {
   145  				continue
   146  			}
   147  			if !live[x.ID] {
   148  				live[x.ID] = true
   149  				q = append(q, x)	// push
   150  				if x.Pos.IsStmt() != src.PosNotStmt {
   151  					liveOrderStmts = append(liveOrderStmts, x)
   152  				}
   153  			}
   154  		}
   155  	}
   156  
   157  	return
   158  }
   159  
   160  // deadcode removes dead code from f.
   161  func deadcode(f *Func) {
   162  	// deadcode after regalloc is forbidden for now. Regalloc
   163  	// doesn't quite generate legal SSA which will lead to some
   164  	// required moves being eliminated. See the comment at the
   165  	// top of regalloc.go for details.
   166  	if f.RegAlloc != nil {
   167  		f.Fatalf("deadcode after regalloc")
   168  	}
   169  
   170  	// Find reachable blocks.
   171  	reachable := ReachableBlocks(f)
   172  
   173  	// Get rid of edges from dead to live code.
   174  	for _, b := range f.Blocks {
   175  		if reachable[b.ID] {
   176  			continue
   177  		}
   178  		for i := 0; i < len(b.Succs); {
   179  			e := b.Succs[i]
   180  			if reachable[e.b.ID] {
   181  				b.removeEdge(i)
   182  			} else {
   183  				i++
   184  			}
   185  		}
   186  	}
   187  
   188  	// Get rid of dead edges from live code.
   189  	for _, b := range f.Blocks {
   190  		if !reachable[b.ID] {
   191  			continue
   192  		}
   193  		if b.Kind != BlockFirst {
   194  			continue
   195  		}
   196  		b.removeEdge(1)
   197  		b.Kind = BlockPlain
   198  		b.Likely = BranchUnknown
   199  	}
   200  
   201  	// Splice out any copies introduced during dead block removal.
   202  	copyelim(f)
   203  
   204  	// Find live values.
   205  	live, order := liveValues(f, reachable)
   206  	defer func() { f.Cache.freeBoolSlice(live) }()
   207  	defer func() { f.Cache.freeValueSlice(order) }()
   208  
   209  	// Remove dead & duplicate entries from namedValues map.
   210  	s := f.newSparseSet(f.NumValues())
   211  	defer f.retSparseSet(s)
   212  	i := 0
   213  	for _, name := range f.Names {
   214  		j := 0
   215  		s.clear()
   216  		values := f.NamedValues[*name]
   217  		for _, v := range values {
   218  			if live[v.ID] && !s.contains(v.ID) {
   219  				values[j] = v
   220  				j++
   221  				s.add(v.ID)
   222  			}
   223  		}
   224  		if j == 0 {
   225  			delete(f.NamedValues, *name)
   226  		} else {
   227  			f.Names[i] = name
   228  			i++
   229  			for k := len(values) - 1; k >= j; k-- {
   230  				values[k] = nil
   231  			}
   232  			f.NamedValues[*name] = values[:j]
   233  		}
   234  	}
   235  	clearNames := f.Names[i:]
   236  	for j := range clearNames {
   237  		clearNames[j] = nil
   238  	}
   239  	f.Names = f.Names[:i]
   240  
   241  	pendingLines := f.cachedLineStarts	// Holds statement boundaries that need to be moved to a new value/block
   242  	pendingLines.clear()
   243  
   244  	// Unlink values and conserve statement boundaries
   245  	for i, b := range f.Blocks {
   246  		if !reachable[b.ID] {
   247  			// TODO what if control is statement boundary? Too late here.
   248  			b.ResetControls()
   249  		}
   250  		for _, v := range b.Values {
   251  			if !live[v.ID] {
   252  				v.resetArgs()
   253  				if v.Pos.IsStmt() == src.PosIsStmt && reachable[b.ID] {
   254  					pendingLines.set(v.Pos, int32(i))	// TODO could be more than one pos for a line
   255  				}
   256  			}
   257  		}
   258  	}
   259  
   260  	// Find new homes for lost lines -- require earliest in data flow with same line that is also in same block
   261  	for i := len(order) - 1; i >= 0; i-- {
   262  		w := order[i]
   263  		if j := pendingLines.get(w.Pos); j > -1 && f.Blocks[j] == w.Block {
   264  			w.Pos = w.Pos.WithIsStmt()
   265  			pendingLines.remove(w.Pos)
   266  		}
   267  	}
   268  
   269  	// Any boundary that failed to match a live value can move to a block end
   270  	pendingLines.foreachEntry(func(j int32, l uint, bi int32) {
   271  		b := f.Blocks[bi]
   272  		if b.Pos.Line() == l && b.Pos.FileIndex() == j {
   273  			b.Pos = b.Pos.WithIsStmt()
   274  		}
   275  	})
   276  
   277  	// Remove dead values from blocks' value list. Return dead
   278  	// values to the allocator.
   279  	for _, b := range f.Blocks {
   280  		i := 0
   281  		for _, v := range b.Values {
   282  			if live[v.ID] {
   283  				b.Values[i] = v
   284  				i++
   285  			} else {
   286  				f.freeValue(v)
   287  			}
   288  		}
   289  		b.truncateValues(i)
   290  	}
   291  
   292  	// Remove unreachable blocks. Return dead blocks to allocator.
   293  	i = 0
   294  	for _, b := range f.Blocks {
   295  		if reachable[b.ID] {
   296  			f.Blocks[i] = b
   297  			i++
   298  		} else {
   299  			if len(b.Values) > 0 {
   300  				b.Fatalf("live values in unreachable block %v: %v", b, b.Values)
   301  			}
   302  			f.freeBlock(b)
   303  		}
   304  	}
   305  	// zero remainder to help GC
   306  	tail := f.Blocks[i:]
   307  	for j := range tail {
   308  		tail[j] = nil
   309  	}
   310  	f.Blocks = f.Blocks[:i]
   311  }
   312  
   313  // removeEdge removes the i'th outgoing edge from b (and
   314  // the corresponding incoming edge from b.Succs[i].b).
   315  // Note that this potentially reorders successors of b, so it
   316  // must be used very carefully.
   317  func (b *Block) removeEdge(i int) {
   318  	e := b.Succs[i]
   319  	c := e.b
   320  	j := e.i
   321  
   322  	// Adjust b.Succs
   323  	b.removeSucc(i)
   324  
   325  	// Adjust c.Preds
   326  	c.removePred(j)
   327  
   328  	// Remove phi args from c's phis.
   329  	for _, v := range c.Values {
   330  		if v.Op != OpPhi {
   331  			continue
   332  		}
   333  		c.removePhiArg(v, j)
   334  		// Note: this is trickier than it looks. Replacing
   335  		// a Phi with a Copy can in general cause problems because
   336  		// Phi and Copy don't have exactly the same semantics.
   337  		// Phi arguments always come from a predecessor block,
   338  		// whereas copies don't. This matters in loops like:
   339  		// 1: x = (Phi y)
   340  		//    y = (Add x 1)
   341  		//    goto 1
   342  		// If we replace Phi->Copy, we get
   343  		// 1: x = (Copy y)
   344  		//    y = (Add x 1)
   345  		//    goto 1
   346  		// (Phi y) refers to the *previous* value of y, whereas
   347  		// (Copy y) refers to the *current* value of y.
   348  		// The modified code has a cycle and the scheduler
   349  		// will barf on it.
   350  		//
   351  		// Fortunately, this situation can only happen for dead
   352  		// code loops. We know the code we're working with is
   353  		// not dead, so we're ok.
   354  		// Proof: If we have a potential bad cycle, we have a
   355  		// situation like this:
   356  		//   x = (Phi z)
   357  		//   y = (op1 x ...)
   358  		//   z = (op2 y ...)
   359  		// Where opX are not Phi ops. But such a situation
   360  		// implies a cycle in the dominator graph. In the
   361  		// example, x.Block dominates y.Block, y.Block dominates
   362  		// z.Block, and z.Block dominates x.Block (treating
   363  		// "dominates" as reflexive).  Cycles in the dominator
   364  		// graph can only happen in an unreachable cycle.
   365  	}
   366  }