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 if f.auxmap == nil {
39 f.auxmap = auxmap{}
40 }
41 for _, b := range f.Blocks {
42 for _, v := range b.Values {
43 if v.Type.IsMemory() {
44 continue
45 }
46 if f.auxmap[v.Aux] == 0 {
47 f.auxmap[v.Aux] = int32(len(f.auxmap)) + 1
48 }
49 a = append(a, v)
50 }
51 }
52 partition := partitionValues(a, f.auxmap)
53
54
55 valueEqClass := f.Cache.allocIDSlice(f.NumValues())
56 defer f.Cache.freeIDSlice(valueEqClass)
57 for _, b := range f.Blocks {
58 for _, v := range b.Values {
59
60 valueEqClass[v.ID] = -v.ID
61 }
62 }
63 var pNum ID = 1
64 for _, e := range partition {
65 if f.pass.debug > 1 && len(e) > 500 {
66 fmt.Printf("CSE.large partition (%d): ", len(e))
67 for j := 0; j < 3; j++ {
68 fmt.Printf("%s ", e[j].LongString())
69 }
70 fmt.Println()
71 }
72
73 for _, v := range e {
74 valueEqClass[v.ID] = pNum
75 }
76 if f.pass.debug > 2 && len(e) > 1 {
77 fmt.Printf("CSE.partition #%d:", pNum)
78 for _, v := range e {
79 fmt.Printf(" %s", v.String())
80 }
81 fmt.Printf("\n")
82 }
83 pNum++
84 }
85
86
87
88
89 var splitPoints []int
90 for {
91 changed := false
92
93
94
95 for i := 0; i < len(partition); i++ {
96 e := partition[i]
97
98 if opcodeTable[e[0].Op].commutative {
99
100 for _, v := range e {
101 if valueEqClass[v.Args[0].ID] > valueEqClass[v.Args[1].ID] {
102 v.Args[0], v.Args[1] = v.Args[1], v.Args[0]
103 }
104 }
105 }
106
107
108 slices.SortFunc(e, func(v, w *Value) int {
109 for i, a := range v.Args {
110 b := w.Args[i]
111 if valueEqClass[a.ID] < valueEqClass[b.ID] {
112 return -1
113 }
114 if valueEqClass[a.ID] > valueEqClass[b.ID] {
115 return +1
116 }
117 }
118 return 0
119 })
120
121
122 splitPoints = append(splitPoints[:0], 0)
123 for j := 1; j < len(e); j++ {
124 v, w := e[j-1], e[j]
125
126 eqArgs := true
127 for k, a := range v.Args {
128 b := w.Args[k]
129 if valueEqClass[a.ID] != valueEqClass[b.ID] {
130 eqArgs = false
131 break
132 }
133 }
134 if !eqArgs {
135 splitPoints = append(splitPoints, j)
136 }
137 }
138 if len(splitPoints) == 1 {
139 continue
140 }
141
142
143 partition[i] = partition[len(partition)-1]
144 partition = partition[:len(partition)-1]
145 i--
146
147
148 splitPoints = append(splitPoints, len(e))
149 for j := 0; j < len(splitPoints)-1; j++ {
150 f := e[splitPoints[j]:splitPoints[j+1]]
151 if len(f) == 1 {
152
153 valueEqClass[f[0].ID] = -f[0].ID
154 continue
155 }
156 for _, v := range f {
157 valueEqClass[v.ID] = pNum
158 }
159 pNum++
160 partition = append(partition, f)
161 }
162 changed = true
163 }
164
165 if !changed {
166 break
167 }
168 }
169
170 sdom := f.Sdom()
171
172
173
174 rewrite := f.Cache.allocValueSlice(f.NumValues())
175 defer f.Cache.freeValueSlice(rewrite)
176 for _, e := range partition {
177 slices.SortFunc(e, func(v, w *Value) int {
178 return cmp.Compare(sdom.domorder(v.Block), sdom.domorder(w.Block))
179 })
180
181 for i := 0; i < len(e)-1; i++ {
182
183 v := e[i]
184 if v == nil {
185 continue
186 }
187
188 e[i] = nil
189
190 for j := i + 1; j < len(e); j++ {
191 w := e[j]
192 if w == nil {
193 continue
194 }
195 if sdom.IsAncestorEq(v.Block, w.Block) {
196 rewrite[w.ID] = v
197 e[j] = nil
198 } else {
199
200 break
201 }
202 }
203 }
204 }
205
206 rewrites := int64(0)
207
208
209 for _, b := range f.Blocks {
210 for _, v := range b.Values {
211 for i, w := range v.Args {
212 if x := rewrite[w.ID]; x != nil {
213 if w.Pos.IsStmt() == src.PosIsStmt {
214
215
216
217 if w.Block == v.Block && w.Pos.Line() == v.Pos.Line() {
218 v.Pos = v.Pos.WithIsStmt()
219 w.Pos = w.Pos.WithNotStmt()
220 }
221 }
222 v.SetArg(i, x)
223 rewrites++
224 }
225 }
226 }
227 for i, v := range b.ControlValues() {
228 if x := rewrite[v.ID]; x != nil {
229 if v.Op == OpNilCheck {
230
231
232 continue
233 }
234 b.ReplaceControl(i, x)
235 }
236 }
237 }
238
239 if f.pass.stats > 0 {
240 f.LogStat("CSE REWRITES", rewrites)
241 }
242 }
243
244
245
246
247 type eqclass []*Value
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264 func partitionValues(a []*Value, auxIDs auxmap) []eqclass {
265 slices.SortFunc(a, func(v, w *Value) int {
266 switch cmpVal(v, w, auxIDs) {
267 case types.CMPlt:
268 return -1
269 case types.CMPgt:
270 return +1
271 default:
272
273 return cmp.Compare(v.ID, w.ID)
274 }
275 })
276
277 var partition []eqclass
278 for len(a) > 0 {
279 v := a[0]
280 j := 1
281 for ; j < len(a); j++ {
282 w := a[j]
283 if cmpVal(v, w, auxIDs) != types.CMPeq {
284 break
285 }
286 }
287 if j > 1 {
288 partition = append(partition, a[:j])
289 }
290 a = a[j:]
291 }
292
293 return partition
294 }
295 func lt2Cmp(isLt bool) types.Cmp {
296 if isLt {
297 return types.CMPlt
298 }
299 return types.CMPgt
300 }
301
302 type auxmap map[Aux]int32
303
304 func cmpVal(v, w *Value, auxIDs auxmap) types.Cmp {
305
306 if v.Op != w.Op {
307 return lt2Cmp(v.Op < w.Op)
308 }
309 if v.AuxInt != w.AuxInt {
310 return lt2Cmp(v.AuxInt < w.AuxInt)
311 }
312 if len(v.Args) != len(w.Args) {
313 return lt2Cmp(len(v.Args) < len(w.Args))
314 }
315 if v.Op == OpPhi && v.Block != w.Block {
316 return lt2Cmp(v.Block.ID < w.Block.ID)
317 }
318 if v.Type.IsMemory() {
319
320
321 return lt2Cmp(v.ID < w.ID)
322 }
323
324
325
326 if v.Op != OpSelect0 && v.Op != OpSelect1 && v.Op != OpSelectN {
327 if tc := v.Type.Compare(w.Type); tc != types.CMPeq {
328 return tc
329 }
330 }
331
332 if v.Aux != w.Aux {
333 if v.Aux == nil {
334 return types.CMPlt
335 }
336 if w.Aux == nil {
337 return types.CMPgt
338 }
339 return lt2Cmp(auxIDs[v.Aux] < auxIDs[w.Aux])
340 }
341
342 return types.CMPeq
343 }
344
View as plain text