1
2
3
4
5 package ssa
6
7 import (
8 "cmd/compile/internal/ir"
9 "cmd/internal/src"
10 "internal/buildcfg"
11 )
12
13
14
15 func nilcheckelim(f *Func) {
16
17
18
19 sdom := f.Sdom()
20
21
22
23
24
25
26 type walkState int
27 const (
28 Work walkState = iota
29 ClearPtr
30 )
31
32 type bp struct {
33 block *Block
34 ptr *Value
35 op walkState
36 }
37
38 work := make([]bp, 0, 256)
39 work = append(work, bp{block: f.Entry})
40
41
42
43
44 nonNilValues := make([]bool, f.NumValues())
45
46
47 for _, b := range f.Blocks {
48 for _, v := range b.Values {
49
50
51
52
53
54
55 if v.Op == OpAddr || v.Op == OpLocalAddr || v.Op == OpAddPtr || v.Op == OpOffPtr || v.Op == OpAdd32 || v.Op == OpAdd64 || v.Op == OpSub32 || v.Op == OpSub64 || v.Op == OpSlicePtr {
56 nonNilValues[v.ID] = true
57 }
58 }
59 }
60
61 for changed := true; changed; {
62 changed = false
63 for _, b := range f.Blocks {
64 for _, v := range b.Values {
65
66
67 if v.Op == OpPhi {
68 argsNonNil := true
69 for _, a := range v.Args {
70 if !nonNilValues[a.ID] {
71 argsNonNil = false
72 break
73 }
74 }
75 if argsNonNil {
76 if !nonNilValues[v.ID] {
77 changed = true
78 }
79 nonNilValues[v.ID] = true
80 }
81 }
82 }
83 }
84 }
85
86
87 sset := f.newSparseSet(f.NumValues())
88 defer f.retSparseSet(sset)
89 storeNumber := make([]int32, f.NumValues())
90
91
92 for len(work) > 0 {
93 node := work[len(work)-1]
94 work = work[:len(work)-1]
95
96 switch node.op {
97 case Work:
98 b := node.block
99
100
101 if len(b.Preds) == 1 {
102 p := b.Preds[0].b
103 if p.Kind == BlockIf && p.Controls[0].Op == OpIsNonNil && p.Succs[0].b == b {
104 if ptr := p.Controls[0].Args[0]; !nonNilValues[ptr.ID] {
105 nonNilValues[ptr.ID] = true
106 work = append(work, bp{op: ClearPtr, ptr: ptr})
107 }
108 }
109 }
110
111
112 b.Values = storeOrder(b.Values, sset, storeNumber)
113
114 pendingLines := f.cachedLineStarts
115 pendingLines.clear()
116
117
118 i := 0
119 for _, v := range b.Values {
120 b.Values[i] = v
121 i++
122 switch v.Op {
123 case OpIsNonNil:
124 ptr := v.Args[0]
125 if nonNilValues[ptr.ID] {
126 if v.Pos.IsStmt() == src.PosIsStmt {
127 pendingLines.add(v.Pos)
128 v.Pos = v.Pos.WithNotStmt()
129 }
130
131 v.reset(OpConstBool)
132 v.AuxInt = 1
133 }
134 case OpNilCheck:
135 ptr := v.Args[0]
136 if nonNilValues[ptr.ID] {
137
138
139
140 if f.fe.Debug_checknil() && v.Pos.Line() > 1 {
141 f.Warnl(v.Pos, "removed nil check")
142 }
143 if v.Pos.IsStmt() == src.PosIsStmt {
144 pendingLines.add(v.Pos)
145 }
146 v.reset(OpUnknown)
147 f.freeValue(v)
148 i--
149 continue
150 }
151
152
153 nonNilValues[ptr.ID] = true
154 work = append(work, bp{op: ClearPtr, ptr: ptr})
155 fallthrough
156 default:
157 if v.Pos.IsStmt() != src.PosNotStmt && !isPoorStatementOp(v.Op) && pendingLines.contains(v.Pos) {
158 v.Pos = v.Pos.WithIsStmt()
159 pendingLines.remove(v.Pos)
160 }
161 }
162 }
163
164 for j := 0; j < i; j++ {
165 v := b.Values[j]
166 if v.Pos.IsStmt() != src.PosNotStmt && !isPoorStatementOp(v.Op) && pendingLines.contains(v.Pos) {
167 v.Pos = v.Pos.WithIsStmt()
168 pendingLines.remove(v.Pos)
169 }
170 }
171 if pendingLines.contains(b.Pos) {
172 b.Pos = b.Pos.WithIsStmt()
173 pendingLines.remove(b.Pos)
174 }
175 b.truncateValues(i)
176
177
178 for w := sdom[node.block.ID].child; w != nil; w = sdom[w.ID].sibling {
179 work = append(work, bp{op: Work, block: w})
180 }
181
182 case ClearPtr:
183 nonNilValues[node.ptr.ID] = false
184 continue
185 }
186 }
187 }
188
189
190
191
192 const minZeroPage = 4096
193
194
195 var faultOnLoad = buildcfg.GOOS != "aix"
196
197
198
199 func nilcheckelim2(f *Func) {
200 unnecessary := f.newSparseMap(f.NumValues())
201 defer f.retSparseMap(unnecessary)
202
203 pendingLines := f.cachedLineStarts
204
205 for _, b := range f.Blocks {
206
207
208
209 unnecessary.clear()
210 pendingLines.clear()
211
212 firstToRemove := len(b.Values)
213 for i := len(b.Values) - 1; i >= 0; i-- {
214 v := b.Values[i]
215 if opcodeTable[v.Op].nilCheck && unnecessary.contains(v.Args[0].ID) {
216 if f.fe.Debug_checknil() && v.Pos.Line() > 1 {
217 f.Warnl(v.Pos, "removed nil check")
218 }
219
220
221
222
223
224 u := b.Values[unnecessary.get(v.Args[0].ID)]
225 if !u.Pos.SameFileAndLine(v.Pos) {
226 if u.Pos.IsStmt() == src.PosIsStmt {
227 pendingLines.add(u.Pos)
228 }
229 u.Pos = v.Pos
230 } else if v.Pos.IsStmt() == src.PosIsStmt {
231 pendingLines.add(v.Pos)
232 }
233
234 v.reset(OpUnknown)
235 firstToRemove = i
236 continue
237 }
238 if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
239 if v.Op == OpVarKill || v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(*ir.Name).Type().HasPointers()) {
240
241 continue
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264 }
265
266
267 unnecessary.clear()
268 }
269
270
271 var ptrstore [2]*Value
272 ptrs := ptrstore[:0]
273 if opcodeTable[v.Op].faultOnNilArg0 && (faultOnLoad || v.Type.IsMemory()) {
274
275 ptrs = append(ptrs, v.Args[0])
276 }
277 if opcodeTable[v.Op].faultOnNilArg1 && (faultOnLoad || (v.Type.IsMemory() && v.Op != OpPPC64LoweredMove)) {
278
279
280 ptrs = append(ptrs, v.Args[1])
281 }
282
283 for _, ptr := range ptrs {
284
285 switch opcodeTable[v.Op].auxType {
286 case auxSym:
287 if v.Aux != nil {
288 continue
289 }
290 case auxSymOff:
291 if v.Aux != nil || v.AuxInt < 0 || v.AuxInt >= minZeroPage {
292 continue
293 }
294 case auxSymValAndOff:
295 off := ValAndOff(v.AuxInt).Off()
296 if v.Aux != nil || off < 0 || off >= minZeroPage {
297 continue
298 }
299 case auxInt32:
300
301 case auxInt64:
302
303
304 case auxNone:
305
306 default:
307 v.Fatalf("can't handle aux %s (type %d) yet\n", v.auxString(), int(opcodeTable[v.Op].auxType))
308 }
309
310
311 unnecessary.set(ptr.ID, int32(i), src.NoXPos)
312 }
313 }
314
315 i := firstToRemove
316 for j := i; j < len(b.Values); j++ {
317 v := b.Values[j]
318 if v.Op != OpUnknown {
319 if !notStmtBoundary(v.Op) && pendingLines.contains(v.Pos) {
320 v.Pos = v.Pos.WithIsStmt()
321 pendingLines.remove(v.Pos)
322 }
323 b.Values[i] = v
324 i++
325 }
326 }
327
328 if pendingLines.contains(b.Pos) {
329 b.Pos = b.Pos.WithIsStmt()
330 }
331
332 b.truncateValues(i)
333
334
335
336 }
337 }
338
View as plain text