1
2
3
4
5 package ssacompile
6
7 import (
8 "cmp"
9 "fmt"
10 "slices"
11
12 "cmd/compile/internal/ssa"
13 "cmd/compile/internal/ssa/ssaop"
14 "cmd/compile/internal/types"
15 "cmd/internal/src"
16 )
17
18
19
20
21 func cse(f *ssa.Func) {
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 a := f.Cache.AllocValueSlice(f.NumValues())
39 defer func() { f.Cache.FreeValueSlice(a) }()
40 a = a[:0]
41 o := f.Cache.AllocInt32Slice(f.NumValues())
42 defer func() { f.Cache.FreeInt32Slice(o) }()
43 if f.Auxmap == nil {
44 f.Auxmap = ssa.AuxMap{}
45 }
46 for _, b := range f.Blocks {
47 for _, v := range b.Values {
48 if v.Type.IsMemory() {
49 continue
50 }
51 if f.Auxmap[v.Aux] == 0 {
52 f.Auxmap[v.Aux] = int32(len(f.Auxmap)) + 1
53 }
54 a = append(a, v)
55 }
56 }
57 partition := partitionValues(a, f.Auxmap)
58
59
60 valueEqClass := f.Cache.AllocIDSlice(f.NumValues())
61 defer f.Cache.FreeIDSlice(valueEqClass)
62 for _, b := range f.Blocks {
63 for _, v := range b.Values {
64
65 valueEqClass[v.ID] = -v.ID
66 }
67 }
68 var pNum ssa.ID = 1
69 for _, e := range partition {
70 if f.Pass.Debug > 1 && len(e) > 500 {
71 fmt.Printf("CSE.large partition (%d): ", len(e))
72 for j := 0; j < 3; j++ {
73 fmt.Printf("%s ", e[j].LongString())
74 }
75 fmt.Println()
76 }
77
78 for _, v := range e {
79 valueEqClass[v.ID] = pNum
80 }
81 if f.Pass.Debug > 2 && len(e) > 1 {
82 fmt.Printf("CSE.partition #%d:", pNum)
83 for _, v := range e {
84 fmt.Printf(" %s", v.String())
85 }
86 fmt.Printf("\n")
87 }
88 pNum++
89 }
90
91
92
93 memTable := f.Cache.AllocInt32Slice(f.NumValues())
94 defer f.Cache.FreeInt32Slice(memTable)
95
96
97
98
99 var splitPoints []int
100 for {
101 changed := false
102
103
104
105 for i := 0; i < len(partition); i++ {
106 e := partition[i]
107
108 if ssaop.OpcodeTable[e[0].Op].Commutative {
109
110 for _, v := range e {
111 if valueEqClass[v.Args[0].ID] > valueEqClass[v.Args[1].ID] {
112 v.Args[0], v.Args[1] = v.Args[1], v.Args[0]
113 }
114 }
115 }
116
117
118 slices.SortFunc(e, func(v, w *ssa.Value) int {
119 _, idxMem, _, _ := isMemUser(v)
120 for i, a := range v.Args {
121 var aId, bId ssa.ID
122 if i != idxMem {
123 b := w.Args[i]
124 aId = a.ID
125 bId = b.ID
126 } else {
127
128
129 aId, _ = getEffectiveMemoryArg(memTable, v)
130 bId, _ = getEffectiveMemoryArg(memTable, w)
131 }
132 if valueEqClass[aId] < valueEqClass[bId] {
133 return -1
134 }
135 if valueEqClass[aId] > valueEqClass[bId] {
136 return +1
137 }
138 }
139 return 0
140 })
141
142
143 splitPoints = append(splitPoints[:0], 0)
144 for j := 1; j < len(e); j++ {
145 v, w := e[j-1], e[j]
146
147 eqArgs := true
148 _, idxMem, _, _ := isMemUser(v)
149 for k, a := range v.Args {
150 if v.Op == ssaop.OpLocalAddr && k == 1 {
151 continue
152 }
153 var aId, bId ssa.ID
154 if k != idxMem {
155 b := w.Args[k]
156 aId = a.ID
157 bId = b.ID
158 } else {
159
160
161 aId, _ = getEffectiveMemoryArg(memTable, v)
162 bId, _ = getEffectiveMemoryArg(memTable, w)
163 }
164 if valueEqClass[aId] != valueEqClass[bId] {
165 eqArgs = false
166 break
167 }
168 }
169 if !eqArgs {
170 splitPoints = append(splitPoints, j)
171 }
172 }
173 if len(splitPoints) == 1 {
174 continue
175 }
176
177
178 partition[i] = partition[len(partition)-1]
179 partition = partition[:len(partition)-1]
180 i--
181
182
183 splitPoints = append(splitPoints, len(e))
184 for j := 0; j < len(splitPoints)-1; j++ {
185 f := e[splitPoints[j]:splitPoints[j+1]]
186 if len(f) == 1 {
187
188 valueEqClass[f[0].ID] = -f[0].ID
189 continue
190 }
191 for _, v := range f {
192 valueEqClass[v.ID] = pNum
193 }
194 pNum++
195 partition = append(partition, f)
196 }
197 changed = true
198 }
199
200 if !changed {
201 break
202 }
203 }
204
205 sdom := f.Sdom()
206
207
208
209 rewrite := f.Cache.AllocValueSlice(f.NumValues())
210 defer f.Cache.FreeValueSlice(rewrite)
211 for _, e := range partition {
212 slices.SortFunc(e, func(v, w *ssa.Value) int {
213 if c := cmp.Compare(sdom.DomOrder(v.Block), sdom.DomOrder(w.Block)); c != 0 {
214 return c
215 }
216 if _, _, _, ok := isMemUser(v); ok {
217
218
219
220 _, vSkips := getEffectiveMemoryArg(memTable, v)
221 _, wSkips := getEffectiveMemoryArg(memTable, w)
222 if c := cmp.Compare(vSkips, wSkips); c != 0 {
223 return c
224 }
225 }
226 if v.Op == ssaop.OpLocalAddr {
227
228 vm := v.Args[1]
229 wm := w.Args[1]
230 if vm == wm {
231 return 0
232 }
233
234
235
236 if vm.Block != v.Block {
237 return -1
238 }
239 if wm.Block != w.Block {
240 return +1
241 }
242
243 vs := storeOrdering(vm, o)
244 ws := storeOrdering(wm, o)
245 if vs <= 0 {
246 f.Fatalf("unable to determine the order of %s", vm.LongString())
247 }
248 if ws <= 0 {
249 f.Fatalf("unable to determine the order of %s", wm.LongString())
250 }
251 return cmp.Compare(vs, ws)
252 }
253 vStmt := v.Pos.IsStmt() == src.PosIsStmt
254 wStmt := w.Pos.IsStmt() == src.PosIsStmt
255 if vStmt != wStmt {
256 if vStmt {
257 return -1
258 }
259 return +1
260 }
261 return 0
262 })
263
264 for i := 0; i < len(e)-1; i++ {
265
266 v := e[i]
267 if v == nil {
268 continue
269 }
270
271 e[i] = nil
272
273 for j := i + 1; j < len(e); j++ {
274 w := e[j]
275 if w == nil {
276 continue
277 }
278 if sdom.IsAncestorEq(v.Block, w.Block) {
279 rewrite[w.ID] = v
280 e[j] = nil
281 } else {
282
283 break
284 }
285 }
286 }
287 }
288
289 rewrites := int64(0)
290
291
292 for _, b := range f.Blocks {
293 for _, v := range b.Values {
294 for i, w := range v.Args {
295 if x := rewrite[w.ID]; x != nil {
296 if w.Pos.IsStmt() == src.PosIsStmt && w.Op != ssaop.OpNilCheck {
297
298
299
300 if w.Block == v.Block && w.Pos.Line() == v.Pos.Line() {
301 v.Pos = v.Pos.WithIsStmt()
302 w.Pos = w.Pos.WithNotStmt()
303 }
304 }
305 v.SetArg(i, x)
306 rewrites++
307 }
308 }
309 }
310 for i, v := range b.ControlValues() {
311 if x := rewrite[v.ID]; x != nil {
312 if v.Op == ssaop.OpNilCheck {
313
314
315 continue
316 }
317 b.ReplaceControl(i, x)
318 }
319 }
320 }
321
322 if f.Pass.Stats > 0 {
323 f.LogStat("CSE REWRITES", rewrites)
324 }
325 }
326
327
328
329
330
331 func storeOrdering(v *ssa.Value, cache []int32) int32 {
332 const minScore int32 = 1
333 score := minScore
334 w := v
335 for {
336 if s := cache[w.ID]; s >= minScore {
337 score += s
338 break
339 }
340 if w.Op == ssaop.OpPhi || w.Op == ssaop.OpInitMem {
341 break
342 }
343 a := w.MemoryArg()
344 if a.Block != w.Block {
345 break
346 }
347 w = a
348 score++
349 }
350 w = v
351 for cache[w.ID] == 0 {
352 cache[w.ID] = score
353 if score == minScore {
354 break
355 }
356 w = w.MemoryArg()
357 score--
358 }
359 return cache[v.ID]
360 }
361
362
363
364
365 type eqclass []*ssa.Value
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382 func partitionValues(a []*ssa.Value, auxIDs ssa.AuxMap) []eqclass {
383 slices.SortFunc(a, func(v, w *ssa.Value) int {
384 switch cmpVal(v, w, auxIDs) {
385 case types.CMPlt:
386 return -1
387 case types.CMPgt:
388 return +1
389 default:
390
391 return cmp.Compare(v.ID, w.ID)
392 }
393 })
394
395 var partition []eqclass
396 for len(a) > 0 {
397 v := a[0]
398 j := 1
399 for ; j < len(a); j++ {
400 w := a[j]
401 if cmpVal(v, w, auxIDs) != types.CMPeq {
402 break
403 }
404 }
405 if j > 1 {
406 partition = append(partition, a[:j])
407 }
408 a = a[j:]
409 }
410
411 return partition
412 }
413 func lt2Cmp(isLt bool) types.Cmp {
414 if isLt {
415 return types.CMPlt
416 }
417 return types.CMPgt
418 }
419
420 func cmpVal(v, w *ssa.Value, auxIDs ssa.AuxMap) types.Cmp {
421
422 if v.Op != w.Op {
423 return lt2Cmp(v.Op < w.Op)
424 }
425 if v.AuxInt != w.AuxInt {
426 return lt2Cmp(v.AuxInt < w.AuxInt)
427 }
428 if len(v.Args) != len(w.Args) {
429 return lt2Cmp(len(v.Args) < len(w.Args))
430 }
431 if v.Op == ssaop.OpPhi && v.Block != w.Block {
432 return lt2Cmp(v.Block.ID < w.Block.ID)
433 }
434 if v.Type.IsMemory() {
435
436
437 return lt2Cmp(v.ID < w.ID)
438 }
439
440
441
442 if v.Op != ssaop.OpSelect0 && v.Op != ssaop.OpSelect1 && v.Op != ssaop.OpSelectN {
443 if tc := v.Type.Compare(w.Type); tc != types.CMPeq {
444 return tc
445 }
446 }
447
448 if v.Aux != w.Aux {
449 if v.Aux == nil {
450 return types.CMPlt
451 }
452 if w.Aux == nil {
453 return types.CMPgt
454 }
455 return lt2Cmp(auxIDs[v.Aux] < auxIDs[w.Aux])
456 }
457
458 return types.CMPeq
459 }
460
461
462
463 func isMemUser(v *ssa.Value) (int, int, int64, bool) {
464 switch v.Op {
465 case ssaop.OpLoad:
466 return 0, 1, v.Type.Size(), true
467 case ssaop.OpNilCheck:
468 return 0, 1, 0, true
469 default:
470 return -1, -1, 0, false
471 }
472 }
473
474
475
476
477 func isMemDef(v *ssa.Value) (int, int, int64, bool) {
478 switch v.Op {
479 case ssaop.OpStore:
480 return 0, 2, ssa.AuxToType(v.Aux).Size(), true
481 case ssaop.OpVarDef:
482 return -1, 0, 0, true
483 case ssaop.OpZero:
484 return 0, 1, v.AuxInt, true
485 default:
486 return -1, -1, 0, false
487 }
488 }
489
490
491
492 const memTableSkipBits = 8
493
494
495 const maxId = ssa.ID(1<<(31-memTableSkipBits)) - 1
496
497
498 func getEffectiveMemoryArg(memTable []int32, v *ssa.Value) (ssa.ID, uint32) {
499 if code := uint32(memTable[v.ID]); code != 0 {
500 return ssa.ID(code >> memTableSkipBits), code & ((1 << memTableSkipBits) - 1)
501 }
502 if idxPtr, idxMem, width, ok := isMemUser(v); ok {
503
504 memId := v.Args[idxMem].ID
505 if memId > maxId {
506 return memId, 0
507 }
508 mem, skips := skipDisjointMemDefs(v, idxPtr, idxMem, width)
509 if mem.ID <= maxId {
510 memId = mem.ID
511 } else {
512 skips = 0
513 }
514 memTable[v.ID] = int32(memId<<memTableSkipBits) | int32(skips)
515 return memId, skips
516 } else {
517 v.Block.Func.Fatalf("expected memory user instruction: %v", v.LongString())
518 }
519 return 0, 0
520 }
521
522
523
524 func skipDisjointMemDefs(user *ssa.Value, idxUserPtr, idxUserMem int, useWidth int64) (*ssa.Value, uint32) {
525 usePtr, mem := user.Args[idxUserPtr], user.Args[idxUserMem]
526 const maxSkips = (1 << memTableSkipBits) - 1
527 var skips uint32
528 for skips = 0; skips < maxSkips; skips++ {
529 if idxPtr, idxMem, width, ok := isMemDef(mem); ok {
530 if mem.Args[idxMem].Uses > 50 {
531
532 break
533 }
534 if width == 0 {
535 mem = mem.Args[idxMem]
536 continue
537 }
538 defPtr := mem.Args[idxPtr]
539 if ssa.Disjoint1(defPtr, width, usePtr, useWidth) {
540 mem = mem.Args[idxMem]
541 continue
542 }
543 }
544 break
545 }
546 return mem, skips
547 }
548
View as plain text