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 v.Op != OpLocalAddr || c != 0 {
185 return c
186 }
187
188 vm := v.Args[1]
189 wm := w.Args[1]
190 if vm == wm {
191 return 0
192 }
193
194
195
196 if vm.Block != v.Block {
197 return -1
198 }
199 if wm.Block != w.Block {
200 return +1
201 }
202
203 vs := storeOrdering(vm, o)
204 ws := storeOrdering(wm, o)
205 if vs <= 0 {
206 f.Fatalf("unable to determine the order of %s", vm.LongString())
207 }
208 if ws <= 0 {
209 f.Fatalf("unable to determine the order of %s", wm.LongString())
210 }
211 return cmp.Compare(vs, ws)
212 })
213
214 for i := 0; i < len(e)-1; i++ {
215
216 v := e[i]
217 if v == nil {
218 continue
219 }
220
221 e[i] = nil
222
223 for j := i + 1; j < len(e); j++ {
224 w := e[j]
225 if w == nil {
226 continue
227 }
228 if sdom.IsAncestorEq(v.Block, w.Block) {
229 rewrite[w.ID] = v
230 e[j] = nil
231 } else {
232
233 break
234 }
235 }
236 }
237 }
238
239 rewrites := int64(0)
240
241
242 for _, b := range f.Blocks {
243 for _, v := range b.Values {
244 for i, w := range v.Args {
245 if x := rewrite[w.ID]; x != nil {
246 if w.Pos.IsStmt() == src.PosIsStmt {
247
248
249
250 if w.Block == v.Block && w.Pos.Line() == v.Pos.Line() {
251 v.Pos = v.Pos.WithIsStmt()
252 w.Pos = w.Pos.WithNotStmt()
253 }
254 }
255 v.SetArg(i, x)
256 rewrites++
257 }
258 }
259 }
260 for i, v := range b.ControlValues() {
261 if x := rewrite[v.ID]; x != nil {
262 if v.Op == OpNilCheck {
263
264
265 continue
266 }
267 b.ReplaceControl(i, x)
268 }
269 }
270 }
271
272 if f.pass.stats > 0 {
273 f.LogStat("CSE REWRITES", rewrites)
274 }
275 }
276
277
278
279
280
281 func storeOrdering(v *Value, cache []int32) int32 {
282 const minScore int32 = 1
283 score := minScore
284 w := v
285 for {
286 if s := cache[w.ID]; s >= minScore {
287 score += s
288 break
289 }
290 if w.Op == OpPhi || w.Op == OpInitMem {
291 break
292 }
293 a := w.MemoryArg()
294 if a.Block != w.Block {
295 break
296 }
297 w = a
298 score++
299 }
300 w = v
301 for cache[w.ID] == 0 {
302 cache[w.ID] = score
303 if score == minScore {
304 break
305 }
306 w = w.MemoryArg()
307 score--
308 }
309 return cache[v.ID]
310 }
311
312
313
314
315 type eqclass []*Value
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332 func partitionValues(a []*Value, auxIDs auxmap) []eqclass {
333 slices.SortFunc(a, func(v, w *Value) int {
334 switch cmpVal(v, w, auxIDs) {
335 case types.CMPlt:
336 return -1
337 case types.CMPgt:
338 return +1
339 default:
340
341 return cmp.Compare(v.ID, w.ID)
342 }
343 })
344
345 var partition []eqclass
346 for len(a) > 0 {
347 v := a[0]
348 j := 1
349 for ; j < len(a); j++ {
350 w := a[j]
351 if cmpVal(v, w, auxIDs) != types.CMPeq {
352 break
353 }
354 }
355 if j > 1 {
356 partition = append(partition, a[:j])
357 }
358 a = a[j:]
359 }
360
361 return partition
362 }
363 func lt2Cmp(isLt bool) types.Cmp {
364 if isLt {
365 return types.CMPlt
366 }
367 return types.CMPgt
368 }
369
370 type auxmap map[Aux]int32
371
372 func cmpVal(v, w *Value, auxIDs auxmap) types.Cmp {
373
374 if v.Op != w.Op {
375 return lt2Cmp(v.Op < w.Op)
376 }
377 if v.AuxInt != w.AuxInt {
378 return lt2Cmp(v.AuxInt < w.AuxInt)
379 }
380 if len(v.Args) != len(w.Args) {
381 return lt2Cmp(len(v.Args) < len(w.Args))
382 }
383 if v.Op == OpPhi && v.Block != w.Block {
384 return lt2Cmp(v.Block.ID < w.Block.ID)
385 }
386 if v.Type.IsMemory() {
387
388
389 return lt2Cmp(v.ID < w.ID)
390 }
391
392
393
394 if v.Op != OpSelect0 && v.Op != OpSelect1 && v.Op != OpSelectN {
395 if tc := v.Type.Compare(w.Type); tc != types.CMPeq {
396 return tc
397 }
398 }
399
400 if v.Aux != w.Aux {
401 if v.Aux == nil {
402 return types.CMPlt
403 }
404 if w.Aux == nil {
405 return types.CMPgt
406 }
407 return lt2Cmp(auxIDs[v.Aux] < auxIDs[w.Aux])
408 }
409
410 return types.CMPeq
411 }
412
View as plain text