github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/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 }