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
45
46
47 nonNilValues := f.Cache.allocValueSlice(f.NumValues())
48 defer f.Cache.freeValueSlice(nonNilValues)
49
50
51 for _, b := range f.Blocks {
52 for _, v := range b.Values {
53
54
55
56
57
58
59 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 {
60 nonNilValues[v.ID] = v
61 }
62 }
63 }
64
65 for changed := true; changed; {
66 changed = false
67 for _, b := range f.Blocks {
68 for _, v := range b.Values {
69
70
71 if v.Op == OpPhi {
72 argsNonNil := true
73 for _, a := range v.Args {
74 if nonNilValues[a.ID] == nil {
75 argsNonNil = false
76 break
77 }
78 }
79 if argsNonNil {
80 if nonNilValues[v.ID] == nil {
81 changed = true
82 }
83 nonNilValues[v.ID] = v
84 }
85 }
86 }
87 }
88 }
89
90
91 sset := f.newSparseSet(f.NumValues())
92 defer f.retSparseSet(sset)
93 storeNumber := f.Cache.allocInt32Slice(f.NumValues())
94 defer f.Cache.freeInt32Slice(storeNumber)
95
96
97 for len(work) > 0 {
98 node := work[len(work)-1]
99 work = work[:len(work)-1]
100
101 switch node.op {
102 case Work:
103 b := node.block
104
105
106 if len(b.Preds) == 1 {
107 p := b.Preds[0].b
108 if p.Kind == BlockIf && p.Controls[0].Op == OpIsNonNil && p.Succs[0].b == b {
109 if ptr := p.Controls[0].Args[0]; nonNilValues[ptr.ID] == nil {
110 nonNilValues[ptr.ID] = ptr
111 work = append(work, bp{op: ClearPtr, ptr: ptr})
112 }
113 }
114 }
115
116
117 b.Values = storeOrder(b.Values, sset, storeNumber)
118
119 pendingLines := f.cachedLineStarts
120 pendingLines.clear()
121
122
123 for _, v := range b.Values {
124 switch v.Op {
125 case OpIsNonNil:
126 ptr := v.Args[0]
127 if nonNilValues[ptr.ID] != nil {
128 if v.Pos.IsStmt() == src.PosIsStmt {
129 pendingLines.add(v.Pos)
130 v.Pos = v.Pos.WithNotStmt()
131 }
132
133 v.reset(OpConstBool)
134 v.AuxInt = 1
135 }
136 case OpNilCheck:
137 ptr := v.Args[0]
138 if nilCheck := nonNilValues[ptr.ID]; nilCheck != nil {
139
140
141
142 if f.fe.Debug_checknil() && v.Pos.Line() > 1 {
143 f.Warnl(v.Pos, "removed nil check")
144 }
145 if v.Pos.IsStmt() == src.PosIsStmt {
146 pendingLines.add(v.Pos)
147 }
148 v.Op = OpCopy
149 v.SetArgs1(nilCheck)
150 continue
151 }
152
153
154 nonNilValues[ptr.ID] = v
155 work = append(work, bp{op: ClearPtr, ptr: ptr})
156 fallthrough
157 default:
158 if v.Pos.IsStmt() != src.PosNotStmt && !isPoorStatementOp(v.Op) && pendingLines.contains(v.Pos) {
159 v.Pos = v.Pos.WithIsStmt()
160 pendingLines.remove(v.Pos)
161 }
162 }
163 }
164
165 for j := range b.Values {
166 v := b.Values[j]
167 if v.Pos.IsStmt() != src.PosNotStmt && !isPoorStatementOp(v.Op) && pendingLines.contains(v.Pos) {
168 v.Pos = v.Pos.WithIsStmt()
169 pendingLines.remove(v.Pos)
170 }
171 }
172 if pendingLines.contains(b.Pos) {
173 b.Pos = b.Pos.WithIsStmt()
174 pendingLines.remove(b.Pos)
175 }
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] = nil
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 == 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))
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