1
2
3
4
5 package ssa
6
7 import (
8 "cmd/compile/internal/ir"
9 "cmd/compile/internal/types"
10 "slices"
11 )
12
13
14
15 func pair(f *Func) {
16
17 switch f.Config.arch {
18 case "arm64":
19 default:
20 return
21 }
22 pairLoads(f)
23 pairStores(f)
24 }
25
26 type pairableLoadInfo struct {
27 width int64
28 pair Op
29 }
30
31
32
33 var pairableLoads = map[Op]pairableLoadInfo{
34 OpARM64MOVDload: {8, OpARM64LDP},
35 OpARM64MOVWUload: {4, OpARM64LDPW},
36 OpARM64MOVWload: {4, OpARM64LDPSW},
37
38
39 OpARM64FMOVDload: {8, OpARM64FLDPD},
40 OpARM64FMOVSload: {4, OpARM64FLDPS},
41 }
42
43 type pairableStoreInfo struct {
44 width int64
45 pair Op
46 }
47
48
49
50
51 var pairableStores = map[Op]pairableStoreInfo{
52 OpARM64MOVDstore: {8, OpARM64STP},
53 OpARM64MOVWstore: {4, OpARM64STPW},
54 OpARM64FMOVDstore: {8, OpARM64FSTPD},
55 OpARM64FMOVSstore: {4, OpARM64FSTPS},
56 }
57
58
59
60
61
62
63
64 func offsetOk(aux Aux, off, width int64) bool {
65 if true {
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 return true
90 }
91 if aux != nil {
92 if _, ok := aux.(*ir.Name); !ok {
93
94 return false
95 }
96
97
98
99
100
101
102
103 if off >= 0 {
104 off += 120
105 }
106
107 }
108 switch width {
109 case 4:
110 if off >= -256 && off <= 252 && off%4 == 0 {
111 return true
112 }
113 case 8:
114 if off >= -512 && off <= 504 && off%8 == 0 {
115 return true
116 }
117 }
118 return false
119 }
120
121 func pairLoads(f *Func) {
122 var loads []*Value
123
124
125 auxIDs := map[Aux]int{}
126 auxID := func(aux Aux) int {
127 id, ok := auxIDs[aux]
128 if !ok {
129 id = len(auxIDs)
130 auxIDs[aux] = id
131 }
132 return id
133 }
134
135 for _, b := range f.Blocks {
136
137 loads = loads[:0]
138 clear(auxIDs)
139 for _, v := range b.Values {
140 info := pairableLoads[v.Op]
141 if info.width == 0 {
142 continue
143 }
144 if !offsetOk(v.Aux, v.AuxInt, info.width) {
145 continue
146 }
147 loads = append(loads, v)
148 }
149 if len(loads) < 2 {
150 continue
151 }
152
153
154 slices.SortFunc(loads, func(x, y *Value) int {
155
156 if x.Op != y.Op {
157 return int(x.Op - y.Op)
158 }
159 if x.Args[0].ID != y.Args[0].ID {
160 return int(x.Args[0].ID - y.Args[0].ID)
161 }
162 if x.Args[1].ID != y.Args[1].ID {
163 return int(x.Args[1].ID - y.Args[1].ID)
164 }
165
166 if x.Aux != nil {
167 if y.Aux == nil {
168 return 1
169 }
170 a, b := auxID(x.Aux), auxID(y.Aux)
171 if a != b {
172 return a - b
173 }
174 } else if y.Aux != nil {
175 return -1
176 }
177
178 return int(x.AuxInt - y.AuxInt)
179 })
180
181
182 for i := 0; i < len(loads)-1; i++ {
183 x := loads[i]
184 y := loads[i+1]
185 if x.Op != y.Op || x.Args[0] != y.Args[0] || x.Args[1] != y.Args[1] {
186 continue
187 }
188 if x.Aux != y.Aux {
189 continue
190 }
191 if x.AuxInt+pairableLoads[x.Op].width != y.AuxInt {
192 continue
193 }
194
195
196
197
198 load := b.NewValue2IA(x.Pos, pairableLoads[x.Op].pair, types.NewTuple(x.Type, y.Type), x.AuxInt, x.Aux, x.Args[0], x.Args[1])
199
200
201 x.reset(OpSelect0)
202 x.SetArgs1(load)
203 y.reset(OpSelect1)
204 y.SetArgs1(load)
205
206 i++
207 }
208 }
209 }
210
211 func pairStores(f *Func) {
212 last := f.Cache.allocBoolSlice(f.NumValues())
213 defer f.Cache.freeBoolSlice(last)
214
215
216
217 prevStore := func(v *Value) *Value {
218 if v.Op == OpInitMem || v.Op == OpPhi {
219 return nil
220 }
221 m := v.MemoryArg()
222 if m.Block != v.Block {
223 return nil
224 }
225 return m
226 }
227
228 for _, b := range f.Blocks {
229
230
231
232
233 for _, v := range b.Values {
234 if v.Type.IsMemory() {
235 last[v.ID] = true
236 }
237 }
238 for _, v := range b.Values {
239 if v.Type.IsMemory() {
240 if m := prevStore(v); m != nil {
241 last[m.ID] = false
242 }
243 }
244 }
245 var lastMem *Value
246 for _, v := range b.Values {
247 if last[v.ID] {
248 lastMem = v
249 break
250 }
251 }
252
253
254 memCheck:
255 for v := lastMem; v != nil; v = prevStore(v) {
256 info := pairableStores[v.Op]
257 if info.width == 0 {
258 continue
259 }
260 if !offsetOk(v.Aux, v.AuxInt, info.width) {
261 continue
262 }
263 ptr := v.Args[0]
264 val := v.Args[1]
265 mem := v.Args[2]
266 off := v.AuxInt
267 aux := v.Aux
268
269
270 lowerOk := true
271 higherOk := true
272 count := 10
273 for w := prevStore(v); w != nil; w = prevStore(w) {
274 if w.Uses != 1 {
275
276
277
278
279
280 continue memCheck
281 }
282 if w.Op == v.Op &&
283 w.Args[0] == ptr &&
284 w.Aux == aux &&
285 (lowerOk && w.AuxInt == off-info.width || higherOk && w.AuxInt == off+info.width) {
286
287
288
289
290
291 args := []*Value{ptr, val, w.Args[1], mem}
292 if w.AuxInt == off-info.width {
293 args[1], args[2] = args[2], args[1]
294 off -= info.width
295 }
296 v.reset(info.pair)
297 v.AddArgs(args...)
298 v.Aux = aux
299 v.AuxInt = off
300 v.Pos = w.Pos
301
302
303 wmem := w.MemoryArg()
304 w.reset(OpCopy)
305 w.SetArgs1(wmem)
306 continue memCheck
307 }
308 if count--; count == 0 {
309
310
311
312
313
314 continue memCheck
315 }
316
317
318
319
320
321 var width int64
322 switch w.Op {
323 case OpARM64MOVDstore, OpARM64FMOVDstore:
324 width = 8
325 case OpARM64MOVWstore, OpARM64FMOVSstore:
326 width = 4
327 case OpARM64MOVHstore:
328 width = 2
329 case OpARM64MOVBstore:
330 width = 1
331 case OpCopy:
332 continue
333 default:
334
335
336 continue memCheck
337 }
338
339
340
341
342 if w.Args[0] != ptr || w.Aux != aux {
343 continue memCheck
344 }
345 if overlap(w.AuxInt, width, off-info.width, info.width) {
346
347 lowerOk = false
348 }
349 if overlap(w.AuxInt, width, off+info.width, info.width) {
350
351 higherOk = false
352 }
353 if !higherOk && !lowerOk {
354 continue memCheck
355 }
356 }
357 }
358 }
359 }
360
View as plain text