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 uid, _ := unnecessary.get(v.Args[0].ID)
225 u := b.Values[uid]
226 if !u.Type.IsMemory() && !u.Pos.SameFileAndLine(v.Pos) {
227 if u.Pos.IsStmt() == src.PosIsStmt {
228 pendingLines.add(u.Pos)
229 }
230 u.Pos = v.Pos
231 } else if v.Pos.IsStmt() == src.PosIsStmt {
232 pendingLines.add(v.Pos)
233 }
234
235 v.reset(OpUnknown)
236 firstToRemove = i
237 continue
238 }
239 if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
240 if v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(*ir.Name).Type().HasPointers()) {
241
242 continue
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
268 unnecessary.clear()
269 }
270
271
272 var ptrstore [2]*Value
273 ptrs := ptrstore[:0]
274 if opcodeTable[v.Op].faultOnNilArg0 && (faultOnLoad || v.Type.IsMemory()) {
275
276 ptrs = append(ptrs, v.Args[0])
277 }
278 if opcodeTable[v.Op].faultOnNilArg1 && (faultOnLoad || (v.Type.IsMemory() && v.Op != OpPPC64LoweredMove)) {
279
280
281 ptrs = append(ptrs, v.Args[1])
282 }
283
284 for _, ptr := range ptrs {
285
286 switch opcodeTable[v.Op].auxType {
287 case auxSym:
288 if v.Aux != nil {
289 continue
290 }
291 case auxSymOff:
292 if v.Aux != nil || v.AuxInt < 0 || v.AuxInt >= minZeroPage {
293 continue
294 }
295 case auxSymValAndOff:
296 off := ValAndOff(v.AuxInt).Off()
297 if v.Aux != nil || off < 0 || off >= minZeroPage {
298 continue
299 }
300 case auxInt32:
301
302 case auxInt64:
303
304
305 case auxNone:
306
307 default:
308 v.Fatalf("can't handle aux %s (type %d) yet\n", v.auxString(), int(opcodeTable[v.Op].auxType))
309 }
310
311
312 unnecessary.set(ptr.ID, int32(i))
313 }
314 }
315
316 i := firstToRemove
317 for j := i; j < len(b.Values); j++ {
318 v := b.Values[j]
319 if v.Op != OpUnknown {
320 if !notStmtBoundary(v.Op) && pendingLines.contains(v.Pos) {
321 v.Pos = v.Pos.WithIsStmt()
322 pendingLines.remove(v.Pos)
323 }
324 b.Values[i] = v
325 i++
326 }
327 }
328
329 if pendingLines.contains(b.Pos) {
330 b.Pos = b.Pos.WithIsStmt()
331 }
332
333 b.truncateValues(i)
334
335
336
337 }
338 }
339
View as plain text