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.
7 // nilcheckelim eliminates unnecessary nil checks.
8 // runs on machine-independent code.
9 func nilcheckelim(f *Func) {
10 // A nil check is redundant if the same nil check was successful in a
11 // dominating block. The efficacy of this pass depends heavily on the
12 // efficacy of the cse pass.
15 // TODO: Eliminate more nil checks.
16 // We can recursively remove any chain of fixed offset calculations,
17 // i.e. struct fields and array elements, even with non-constant
18 // indices: x is non-nil iff x.a.b[i].c is.
22 Work walkState = iota // process nil checks and traverse to dominees
23 ClearPtr // forget the fact that ptr is nil
27 block *Block // block, or nil in ClearPtr state
28 ptr *Value // if non-nil, ptr that is to be cleared in ClearPtr state
32 work := make([]bp, 0, 256)
33 work = append(work, bp{block: f.Entry})
35 // map from value ID to bool indicating if value is known to be non-nil
36 // in the current dominator path being walked. This slice is updated by
37 // walkStates to maintain the known non-nil values.
38 nonNilValues := make([]bool, f.NumValues())
40 // make an initial pass identifying any non-nil values
41 for _, b := range f.Blocks {
42 // a value resulting from taking the address of a
43 // value, or a value constructed from an offset of a
44 // non-nil ptr (OpAddPtr) implies it is non-nil
45 for _, v := range b.Values {
46 if v.Op == OpAddr || v.Op == OpAddPtr {
47 nonNilValues[v.ID] = true
48 } else if v.Op == OpPhi {
49 // phis whose arguments are all non-nil
52 for _, a := range v.Args {
53 if !nonNilValues[a.ID] {
58 nonNilValues[v.ID] = true
64 // perform a depth first walk of the dominee tree
66 node := work[len(work)-1]
67 work = work[:len(work)-1]
73 // First, see if we're dominated by an explicit nil check.
74 if len(b.Preds) == 1 {
76 if p.Kind == BlockIf && p.Control.Op == OpIsNonNil && p.Succs[0].b == b {
77 ptr := p.Control.Args[0]
78 if !nonNilValues[ptr.ID] {
79 nonNilValues[ptr.ID] = true
80 work = append(work, bp{op: ClearPtr, ptr: ptr})
85 // Next, eliminate any redundant nil checks in this block.
87 for _, v := range b.Values {
93 if nonNilValues[ptr.ID] {
94 // This is a redundant explicit nil check.
100 if nonNilValues[ptr.ID] {
101 // This is a redundant implicit nil check.
102 // Logging in the style of the former compiler -- and omit line 1,
103 // which is usually in generated code.
104 if f.Config.Debug_checknil() && v.Line > 1 {
105 f.Config.Warnl(v.Line, "removed nil check")
108 // TODO: f.freeValue(v)
114 for j := i; j < len(b.Values); j++ {
117 b.Values = b.Values[:i]
119 // Finally, find redundant nil checks for subsequent blocks.
120 // Note that we can't add these until the loop above is done, as the
121 // values in the block are not ordered in any way when this pass runs.
122 // This was the cause of issue #18725.
123 for _, v := range b.Values {
124 if v.Op != OpNilCheck {
128 // Record the fact that we know ptr is non nil, and remember to
129 // undo that information when this dominator subtree is done.
130 nonNilValues[ptr.ID] = true
131 work = append(work, bp{op: ClearPtr, ptr: ptr})
134 // Add all dominated blocks to the work list.
135 for w := sdom[node.block.ID].child; w != nil; w = sdom[w.ID].sibling {
136 work = append(work, bp{op: Work, block: w})
140 nonNilValues[node.ptr.ID] = false
146 // All platforms are guaranteed to fault if we load/store to anything smaller than this address.
148 // This should agree with minLegalPointer in the runtime.
149 const minZeroPage = 4096
151 // nilcheckelim2 eliminates unnecessary nil checks.
152 // Runs after lowering and scheduling.
153 func nilcheckelim2(f *Func) {
154 unnecessary := f.newSparseSet(f.NumValues())
155 defer f.retSparseSet(unnecessary)
156 for _, b := range f.Blocks {
157 // Walk the block backwards. Find instructions that will fault if their
158 // input pointer is nil. Remove nil checks on those pointers, as the
159 // faulting instruction effectively does the nil check for free.
161 for i := len(b.Values) - 1; i >= 0; i-- {
163 if opcodeTable[v.Op].nilCheck && unnecessary.contains(v.Args[0].ID) {
164 if f.Config.Debug_checknil() && int(v.Line) > 1 {
165 f.Config.Warnl(v.Line, "removed nil check")
170 if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
171 if v.Op == OpVarDef || v.Op == OpVarKill || v.Op == OpVarLive {
172 // These ops don't really change memory.
175 // This op changes memory. Any faulting instruction after v that
176 // we've recorded in the unnecessary map is now obsolete.
180 // Find any pointers that this op is guaranteed to fault on if nil.
181 var ptrstore [2]*Value
183 if opcodeTable[v.Op].faultOnNilArg0 {
184 ptrs = append(ptrs, v.Args[0])
186 if opcodeTable[v.Op].faultOnNilArg1 {
187 ptrs = append(ptrs, v.Args[1])
189 for _, ptr := range ptrs {
190 // Check to make sure the offset is small.
191 switch opcodeTable[v.Op].auxType {
193 if v.Aux != nil || v.AuxInt < 0 || v.AuxInt >= minZeroPage {
196 case auxSymValAndOff:
197 off := ValAndOff(v.AuxInt).Off()
198 if v.Aux != nil || off < 0 || off >= minZeroPage {
202 // Mips uses this auxType for atomic add constant. It does not affect the effective address.
204 // ARM uses this auxType for duffcopy/duffzero/alignment info.
205 // It does not affect the effective address.
209 v.Fatalf("can't handle aux %s (type %d) yet\n", v.auxString(), int(opcodeTable[v.Op].auxType))
211 // This instruction is guaranteed to fault if ptr is nil.
212 // Any previous nil check op is unnecessary.
213 unnecessary.add(ptr.ID)
216 // Remove values we've clobbered with OpUnknown.
218 for _, v := range b.Values {
219 if v.Op != OpUnknown {
224 for j := i; j < len(b.Values); j++ {
227 b.Values = b.Values[:i]
229 // TODO: if b.Kind == BlockPlain, start the analysis in the subsequent block to find
230 // more unnecessary nil checks. Would fix test/nilptr3_ssa.go:157.