...
Source file src/pkg/cmd/compile/internal/ssa/nilcheck.go
1
2
3
4
5 package ssa
6
7 import (
8 "cmd/internal/objabi"
9 "cmd/internal/src"
10 )
11
12
13
14 func nilcheckelim(f *Func) {
15
16
17
18 sdom := f.sdom()
19
20
21
22
23
24
25 type walkState int
26 const (
27 Work walkState = iota
28 ClearPtr
29 )
30
31 type bp struct {
32 block *Block
33 ptr *Value
34 op walkState
35 }
36
37 work := make([]bp, 0, 256)
38 work = append(work, bp{block: f.Entry})
39
40
41
42
43 nonNilValues := make([]bool, f.NumValues())
44
45
46 for _, b := range f.Blocks {
47 for _, v := range b.Values {
48
49
50
51
52
53
54 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 {
55 nonNilValues[v.ID] = true
56 }
57 }
58 }
59
60 for changed := true; changed; {
61 changed = false
62 for _, b := range f.Blocks {
63 for _, v := range b.Values {
64
65
66 if v.Op == OpPhi {
67 argsNonNil := true
68 for _, a := range v.Args {
69 if !nonNilValues[a.ID] {
70 argsNonNil = false
71 break
72 }
73 }
74 if argsNonNil {
75 if !nonNilValues[v.ID] {
76 changed = true
77 }
78 nonNilValues[v.ID] = true
79 }
80 }
81 }
82 }
83 }
84
85
86 sset := f.newSparseSet(f.NumValues())
87 defer f.retSparseSet(sset)
88 storeNumber := make([]int32, f.NumValues())
89
90
91 for len(work) > 0 {
92 node := work[len(work)-1]
93 work = work[:len(work)-1]
94
95 switch node.op {
96 case Work:
97 b := node.block
98
99
100 if len(b.Preds) == 1 {
101 p := b.Preds[0].b
102 if p.Kind == BlockIf && p.Control.Op == OpIsNonNil && p.Succs[0].b == b {
103 ptr := p.Control.Args[0]
104 if !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 pendingLines.contains(v.Pos) && v.Pos.IsStmt() != src.PosNotStmt {
158 v.Pos = v.Pos.WithIsStmt()
159 pendingLines.remove(v.Pos)
160 }
161 }
162 }
163 if pendingLines.contains(b.Pos) {
164 b.Pos = b.Pos.WithIsStmt()
165 pendingLines.remove(b.Pos)
166 }
167 for j := i; j < len(b.Values); j++ {
168 b.Values[j] = nil
169 }
170 b.Values = b.Values[:i]
171
172
173 for w := sdom[node.block.ID].child; w != nil; w = sdom[w.ID].sibling {
174 work = append(work, bp{op: Work, block: w})
175 }
176
177 case ClearPtr:
178 nonNilValues[node.ptr.ID] = false
179 continue
180 }
181 }
182 }
183
184
185
186
187 const minZeroPage = 4096
188
189
190 var faultOnLoad = objabi.GOOS != "aix"
191
192
193
194 func nilcheckelim2(f *Func) {
195 unnecessary := f.newSparseSet(f.NumValues())
196 defer f.retSparseSet(unnecessary)
197
198 pendingLines := f.cachedLineStarts
199
200 for _, b := range f.Blocks {
201
202
203
204 unnecessary.clear()
205 pendingLines.clear()
206
207 firstToRemove := len(b.Values)
208 for i := len(b.Values) - 1; i >= 0; i-- {
209 v := b.Values[i]
210 if opcodeTable[v.Op].nilCheck && unnecessary.contains(v.Args[0].ID) {
211 if f.fe.Debug_checknil() && v.Pos.Line() > 1 {
212 f.Warnl(v.Pos, "removed nil check")
213 }
214 if v.Pos.IsStmt() == src.PosIsStmt {
215 pendingLines.add(v.Pos)
216 }
217 v.reset(OpUnknown)
218 firstToRemove = i
219 continue
220 }
221 if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
222 if v.Op == OpVarKill || v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(GCNode).Typ().HasHeapPointer()) {
223
224 continue
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247 }
248
249
250 unnecessary.clear()
251 }
252
253
254 var ptrstore [2]*Value
255 ptrs := ptrstore[:0]
256 if opcodeTable[v.Op].faultOnNilArg0 && (faultOnLoad || v.Type.IsMemory()) {
257
258 ptrs = append(ptrs, v.Args[0])
259 }
260 if opcodeTable[v.Op].faultOnNilArg1 && (faultOnLoad || (v.Type.IsMemory() && v.Op != OpPPC64LoweredMove)) {
261
262
263 ptrs = append(ptrs, v.Args[1])
264 }
265
266 for _, ptr := range ptrs {
267
268 switch opcodeTable[v.Op].auxType {
269 case auxSymOff:
270 if v.Aux != nil || v.AuxInt < 0 || v.AuxInt >= minZeroPage {
271 continue
272 }
273 case auxSymValAndOff:
274 off := ValAndOff(v.AuxInt).Off()
275 if v.Aux != nil || off < 0 || off >= minZeroPage {
276 continue
277 }
278 case auxInt32:
279
280 case auxInt64:
281
282
283 case auxNone:
284
285 default:
286 v.Fatalf("can't handle aux %s (type %d) yet\n", v.auxString(), int(opcodeTable[v.Op].auxType))
287 }
288
289
290 unnecessary.add(ptr.ID)
291 }
292 }
293
294 i := firstToRemove
295 for j := i; j < len(b.Values); j++ {
296 v := b.Values[j]
297 if v.Op != OpUnknown {
298 if v.Pos.IsStmt() != src.PosNotStmt && pendingLines.contains(v.Pos) {
299 v.Pos = v.Pos.WithIsStmt()
300 pendingLines.remove(v.Pos)
301 }
302 b.Values[i] = v
303 i++
304 }
305 }
306
307 if pendingLines.contains(b.Pos) {
308 b.Pos = b.Pos.WithIsStmt()
309 }
310
311 for j := i; j < len(b.Values); j++ {
312 b.Values[j] = nil
313 }
314 b.Values = b.Values[:i]
315
316
317
318 }
319 }
320
View as plain text