1
2
3
4
5 package ssa
6
7 import (
8 "cmd/compile/internal/types"
9 "cmd/internal/src"
10 "cmp"
11 "fmt"
12 "slices"
13 )
14
15
16
17
18 func cse(f *Func) {
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 a := f.Cache.allocValueSlice(f.NumValues())
36 defer func() { f.Cache.freeValueSlice(a) }()
37 a = a[:0]
38 o := f.Cache.allocInt32Slice(f.NumValues())
39 defer func() { f.Cache.freeInt32Slice(o) }()
40 if f.auxmap == nil {
41 f.auxmap = auxmap{}
42 }
43 for _, b := range f.Blocks {
44 for _, v := range b.Values {
45 if v.Type.IsMemory() {
46 continue
47 }
48 if f.auxmap[v.Aux] == 0 {
49 f.auxmap[v.Aux] = int32(len(f.auxmap)) + 1
50 }
51 a = append(a, v)
52 }
53 }
54 partition := partitionValues(a, f.auxmap)
55
56
57 valueEqClass := f.Cache.allocIDSlice(f.NumValues())
58 defer f.Cache.freeIDSlice(valueEqClass)
59 for _, b := range f.Blocks {
60 for _, v := range b.Values {
61
62 valueEqClass[v.ID] = -v.ID
63 }
64 }
65 var pNum ID = 1
66 for _, e := range partition {
67 if f.pass.debug > 1 && len(e) > 500 {
68 fmt.Printf("CSE.large partition (%d): ", len(e))
69 for j := 0; j < 3; j++ {
70 fmt.Printf("%s ", e[j].LongString())
71 }
72 fmt.Println()
73 }
74
75 for _, v := range e {
76 valueEqClass[v.ID] = pNum
77 }
78 if f.pass.debug > 2 && len(e) > 1 {
79 fmt.Printf("CSE.partition #%d:", pNum)
80 for _, v := range e {
81 fmt.Printf(" %s", v.String())
82 }
83 fmt.Printf("\n")
84 }
85 pNum++
86 }
87
88
89
90
91 var splitPoints []int
92 for {
93 changed := false
94
95
96
97 for i := 0; i < len(partition); i++ {
98 e := partition[i]
99
100 if opcodeTable[e[0].Op].commutative {
101
102 for _, v := range e {
103 if valueEqClass[v.Args[0].ID] > valueEqClass[v.Args[1].ID] {
104 v.Args[0], v.Args[1] = v.Args[1], v.Args[0]
105 }
106 }
107 }
108
109
110 slices.SortFunc(e, func(v, w *Value) int {
111 for i, a := range v.Args {
112 b := w.Args[i]
113 if valueEqClass[a.ID] < valueEqClass[b.ID] {
114 return -1
115 }
116 if valueEqClass[a.ID] > valueEqClass[b.ID] {
117 return +1
118 }
119 }
120 return 0
121 })
122
123
124 splitPoints = append(splitPoints[:0], 0)
125 for j := 1; j < len(e); j++ {
126 v, w := e[j-1], e[j]
127
128 eqArgs := true
129 for k, a := range v.Args {
130 if v.Op == OpLocalAddr && k == 1 {
131 continue
132 }
133 b := w.Args[k]
134 if valueEqClass[a.ID] != valueEqClass[b.ID] {
135 eqArgs = false
136 break
137 }
138 }
139 if !eqArgs {
140 splitPoints = append(splitPoints, j)
141 }
142 }
143 if len(splitPoints) == 1 {
144 continue
145 }
146
147
148 partition[i] = partition[len(partition)-1]
149 partition = partition[:len(partition)-1]
150 i--
151
152
153 splitPoints = append(splitPoints, len(e))
154 for j := 0; j < len(splitPoints)-1; j++ {
155 f := e[splitPoints[j]:splitPoints[j+1]]
156 if len(f) == 1 {
157
158 valueEqClass[f[0].ID] = -f[0].ID
159 continue
160 }
161 for _, v := range f {
162 valueEqClass[v.ID] = pNum
163 }
164 pNum++
165 partition = append(partition, f)
166 }
167 changed = true
168 }
169
170 if !changed {
171 break
172 }
173 }
174
175 sdom := f.Sdom()
176
177
178
179 rewrite := f.Cache.allocValueSlice(f.NumValues())
180 defer f.Cache.freeValueSlice(rewrite)
181 for _, e := range partition {
182 slices.SortFunc(e, func(v, w *Value) int {
183 c := cmp.Compare(sdom.domorder(v.Block), sdom.domorder(w.Block))
184 if c != 0 {
185 return c
186 }
187 if v.Op == OpLocalAddr {
188
189 vm := v.Args[1]
190 wm := w.Args[1]
191 if vm == wm {
192 return 0
193 }
194
195
196
197 if vm.Block != v.Block {
198 return -1
199 }
200 if wm.Block != w.Block {
201 return +1
202 }
203
204 vs := storeOrdering(vm, o)
205 ws := storeOrdering(wm, o)
206 if vs <= 0 {
207 f.Fatalf("unable to determine the order of %s", vm.LongString())
208 }
209 if ws <= 0 {
210 f.Fatalf("unable to determine the order of %s", wm.LongString())
211 }
212 return cmp.Compare(vs, ws)
213 }
214 vStmt := v.Pos.IsStmt() == src.PosIsStmt
215 wStmt := w.Pos.IsStmt() == src.PosIsStmt
216 if vStmt != wStmt {
217 if vStmt {
218 return -1
219 }
220 return +1
221 }
222 return 0
223 })
224
225 for i := 0; i < len(e)-1; i++ {
226
227 v := e[i]
228 if v == nil {
229 continue
230 }
231
232 e[i] = nil
233
234 for j := i + 1; j < len(e); j++ {
235 w := e[j]
236 if w == nil {
237 continue
238 }
239 if sdom.IsAncestorEq(v.Block, w.Block) {
240 rewrite[w.ID] = v
241 e[j] = nil
242 } else {
243
244 break
245 }
246 }
247 }
248 }
249
250 rewrites := int64(0)
251
252
253 for _, b := range f.Blocks {
254 for _, v := range b.Values {
255 for i, w := range v.Args {
256 if x := rewrite[w.ID]; x != nil {
257 if w.Pos.IsStmt() == src.PosIsStmt {
258
259
260
261 if w.Block == v.Block && w.Pos.Line() == v.Pos.Line() {
262 v.Pos = v.Pos.WithIsStmt()
263 w.Pos = w.Pos.WithNotStmt()
264 }
265 }
266 v.SetArg(i, x)
267 rewrites++
268 }
269 }
270 }
271 for i, v := range b.ControlValues() {
272 if x := rewrite[v.ID]; x != nil {
273 if v.Op == OpNilCheck {
274
275
276 continue
277 }
278 b.ReplaceControl(i, x)
279 }
280 }
281 }
282
283 if f.pass.stats > 0 {
284 f.LogStat("CSE REWRITES", rewrites)
285 }
286 }
287
288
289
290
291
292 func storeOrdering(v *Value, cache []int32) int32 {
293 const minScore int32 = 1
294 score := minScore
295 w := v
296 for {
297 if s := cache[w.ID]; s >= minScore {
298 score += s
299 break
300 }
301 if w.Op == OpPhi || w.Op == OpInitMem {
302 break
303 }
304 a := w.MemoryArg()
305 if a.Block != w.Block {
306 break
307 }
308 w = a
309 score++
310 }
311 w = v
312 for cache[w.ID] == 0 {
313 cache[w.ID] = score
314 if score == minScore {
315 break
316 }
317 w = w.MemoryArg()
318 score--
319 }
320 return cache[v.ID]
321 }
322
323
324
325
326 type eqclass []*Value
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343 func partitionValues(a []*Value, auxIDs auxmap) []eqclass {
344 slices.SortFunc(a, func(v, w *Value) int {
345 switch cmpVal(v, w, auxIDs) {
346 case types.CMPlt:
347 return -1
348 case types.CMPgt:
349 return +1
350 default:
351
352 return cmp.Compare(v.ID, w.ID)
353 }
354 })
355
356 var partition []eqclass
357 for len(a) > 0 {
358 v := a[0]
359 j := 1
360 for ; j < len(a); j++ {
361 w := a[j]
362 if cmpVal(v, w, auxIDs) != types.CMPeq {
363 break
364 }
365 }
366 if j > 1 {
367 partition = append(partition, a[:j])
368 }
369 a = a[j:]
370 }
371
372 return partition
373 }
374 func lt2Cmp(isLt bool) types.Cmp {
375 if isLt {
376 return types.CMPlt
377 }
378 return types.CMPgt
379 }
380
381 type auxmap map[Aux]int32
382
383 func cmpVal(v, w *Value, auxIDs auxmap) types.Cmp {
384
385 if v.Op != w.Op {
386 return lt2Cmp(v.Op < w.Op)
387 }
388 if v.AuxInt != w.AuxInt {
389 return lt2Cmp(v.AuxInt < w.AuxInt)
390 }
391 if len(v.Args) != len(w.Args) {
392 return lt2Cmp(len(v.Args) < len(w.Args))
393 }
394 if v.Op == OpPhi && v.Block != w.Block {
395 return lt2Cmp(v.Block.ID < w.Block.ID)
396 }
397 if v.Type.IsMemory() {
398
399
400 return lt2Cmp(v.ID < w.ID)
401 }
402
403
404
405 if v.Op != OpSelect0 && v.Op != OpSelect1 && v.Op != OpSelectN {
406 if tc := v.Type.Compare(w.Type); tc != types.CMPeq {
407 return tc
408 }
409 }
410
411 if v.Aux != w.Aux {
412 if v.Aux == nil {
413 return types.CMPlt
414 }
415 if w.Aux == nil {
416 return types.CMPgt
417 }
418 return lt2Cmp(auxIDs[v.Aux] < auxIDs[w.Aux])
419 }
420
421 return types.CMPeq
422 }
423
View as plain text