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