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