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