github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/ssa/nilcheck.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  // nilcheckelim eliminates unnecessary nil checks.
     8  func nilcheckelim(f *Func) {
     9  	// A nil check is redundant if the same nil check was successful in a
    10  	// dominating block. The efficacy of this pass depends heavily on the
    11  	// efficacy of the cse pass.
    12  	idom := f.idom
    13  	domTree := make([][]*Block, f.NumBlocks())
    14  
    15  	// Create a block ID -> [dominees] mapping
    16  	for _, b := range f.Blocks {
    17  		if dom := idom[b.ID]; dom != nil {
    18  			domTree[dom.ID] = append(domTree[dom.ID], b)
    19  		}
    20  	}
    21  
    22  	// TODO: Eliminate more nil checks.
    23  	// We can recursively remove any chain of fixed offset calculations,
    24  	// i.e. struct fields and array elements, even with non-constant
    25  	// indices: x is non-nil iff x.a.b[i].c is.
    26  
    27  	type walkState int
    28  	const (
    29  		Work   walkState = iota // clear nil check if we should and traverse to dominees regardless
    30  		RecPtr                  // record the pointer as being nil checked
    31  		ClearPtr
    32  	)
    33  
    34  	type bp struct {
    35  		block *Block // block, or nil in RecPtr/ClearPtr state
    36  		ptr   *Value // if non-nil, ptr that is to be set/cleared in RecPtr/ClearPtr state
    37  		op    walkState
    38  	}
    39  
    40  	work := make([]bp, 0, 256)
    41  	work = append(work, bp{block: f.Entry})
    42  
    43  	// map from value ID to bool indicating if value is known to be non-nil
    44  	// in the current dominator path being walked. This slice is updated by
    45  	// walkStates to maintain the known non-nil values.
    46  	nonNilValues := make([]bool, f.NumValues())
    47  
    48  	// make an initial pass identifying any non-nil values
    49  	for _, b := range f.Blocks {
    50  		// a value resulting from taking the address of a
    51  		// value, or a value constructed from an offset of a
    52  		// non-nil ptr (OpAddPtr) implies it is non-nil
    53  		for _, v := range b.Values {
    54  			if v.Op == OpAddr || v.Op == OpAddPtr {
    55  				nonNilValues[v.ID] = true
    56  			} else if v.Op == OpPhi {
    57  				// phis whose arguments are all non-nil
    58  				// are non-nil
    59  				argsNonNil := true
    60  				for _, a := range v.Args {
    61  					if !nonNilValues[a.ID] {
    62  						argsNonNil = false
    63  					}
    64  				}
    65  				if argsNonNil {
    66  					nonNilValues[v.ID] = true
    67  				}
    68  			}
    69  		}
    70  	}
    71  
    72  	// perform a depth first walk of the dominee tree
    73  	for len(work) > 0 {
    74  		node := work[len(work)-1]
    75  		work = work[:len(work)-1]
    76  
    77  		switch node.op {
    78  		case Work:
    79  			checked := checkedptr(node.block) // ptr being checked for nil/non-nil
    80  			nonnil := nonnilptr(node.block)   // ptr that is non-nil due to this blocks pred
    81  
    82  			if checked != nil {
    83  				// already have a nilcheck in the dominator path, or this block is a success
    84  				// block for the same value it is checking
    85  				if nonNilValues[checked.ID] || checked == nonnil {
    86  					// Eliminate the nil check.
    87  					// The deadcode pass will remove vestigial values,
    88  					// and the fuse pass will join this block with its successor.
    89  
    90  					// Logging in the style of the former compiler -- and omit line 1,
    91  					// which is usually in generated code.
    92  					if f.Config.Debug_checknil() && node.block.Control.Line > 1 {
    93  						f.Config.Warnl(node.block.Control.Line, "removed nil check")
    94  					}
    95  
    96  					switch node.block.Kind {
    97  					case BlockIf:
    98  						node.block.Kind = BlockFirst
    99  						node.block.SetControl(nil)
   100  					case BlockCheck:
   101  						node.block.Kind = BlockPlain
   102  						node.block.SetControl(nil)
   103  					default:
   104  						f.Fatalf("bad block kind in nilcheck %s", node.block.Kind)
   105  					}
   106  				}
   107  			}
   108  
   109  			if nonnil != nil && !nonNilValues[nonnil.ID] {
   110  				// this is a new nilcheck so add a ClearPtr node to clear the
   111  				// ptr from the map of nil checks once we traverse
   112  				// back up the tree
   113  				work = append(work, bp{op: ClearPtr, ptr: nonnil})
   114  			}
   115  
   116  			// add all dominated blocks to the work list
   117  			for _, w := range domTree[node.block.ID] {
   118  				work = append(work, bp{block: w})
   119  			}
   120  
   121  			if nonnil != nil && !nonNilValues[nonnil.ID] {
   122  				work = append(work, bp{op: RecPtr, ptr: nonnil})
   123  			}
   124  		case RecPtr:
   125  			nonNilValues[node.ptr.ID] = true
   126  			continue
   127  		case ClearPtr:
   128  			nonNilValues[node.ptr.ID] = false
   129  			continue
   130  		}
   131  	}
   132  }
   133  
   134  // checkedptr returns the Value, if any,
   135  // that is used in a nil check in b's Control op.
   136  func checkedptr(b *Block) *Value {
   137  	if b.Kind == BlockCheck {
   138  		return b.Control.Args[0]
   139  	}
   140  	if b.Kind == BlockIf && b.Control.Op == OpIsNonNil {
   141  		return b.Control.Args[0]
   142  	}
   143  	return nil
   144  }
   145  
   146  // nonnilptr returns the Value, if any,
   147  // that is non-nil due to b being the successor block
   148  // of an OpIsNonNil or OpNilCheck block for the value and having a single
   149  // predecessor.
   150  func nonnilptr(b *Block) *Value {
   151  	if len(b.Preds) == 1 {
   152  		bp := b.Preds[0].b
   153  		if bp.Kind == BlockCheck {
   154  			return bp.Control.Args[0]
   155  		}
   156  		if bp.Kind == BlockIf && bp.Control.Op == OpIsNonNil && bp.Succs[0].b == b {
   157  			return bp.Control.Args[0]
   158  		}
   159  	}
   160  	return nil
   161  }