Merge tag 'v5.15.57' into rpi-5.15.y
[platform/kernel/linux-rpi.git] / arch / arm / lib / memcpymove.h
1 /*
2 Copyright (c) 2013, Raspberry Pi Foundation
3 Copyright (c) 2013, RISC OS Open Ltd
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8     * Redistributions of source code must retain the above copyright
9       notice, this list of conditions and the following disclaimer.
10     * Redistributions in binary form must reproduce the above copyright
11       notice, this list of conditions and the following disclaimer in the
12       documentation and/or other materials provided with the distribution.
13     * Neither the name of the copyright holder nor the
14       names of its contributors may be used to endorse or promote products
15       derived from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
21 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 .macro unaligned_words  backwards, align, use_pld, words, r0, r1, r2, r3, r4, r5, r6, r7, r8
30  .if words == 1
31   .if backwards
32         mov     r1, r0, lsl #32-align*8
33         ldr     r0, [S, #-4]!
34         orr     r1, r1, r0, lsr #align*8
35         str     r1, [D, #-4]!
36   .else
37         mov     r0, r1, lsr #align*8
38         ldr     r1, [S, #4]!
39         orr     r0, r0, r1, lsl #32-align*8
40         str     r0, [D], #4
41   .endif
42  .elseif words == 2
43   .if backwards
44         ldr     r1, [S, #-4]!
45         mov     r2, r0, lsl #32-align*8
46         ldr     r0, [S, #-4]!
47         orr     r2, r2, r1, lsr #align*8
48         mov     r1, r1, lsl #32-align*8
49         orr     r1, r1, r0, lsr #align*8
50         stmdb   D!, {r1, r2}
51   .else
52         ldr     r1, [S, #4]!
53         mov     r0, r2, lsr #align*8
54         ldr     r2, [S, #4]!
55         orr     r0, r0, r1, lsl #32-align*8
56         mov     r1, r1, lsr #align*8
57         orr     r1, r1, r2, lsl #32-align*8
58         stmia   D!, {r0, r1}
59   .endif
60  .elseif words == 4
61   .if backwards
62         ldmdb   S!, {r2, r3}
63         mov     r4, r0, lsl #32-align*8
64         ldmdb   S!, {r0, r1}
65         orr     r4, r4, r3, lsr #align*8
66         mov     r3, r3, lsl #32-align*8
67         orr     r3, r3, r2, lsr #align*8
68         mov     r2, r2, lsl #32-align*8
69         orr     r2, r2, r1, lsr #align*8
70         mov     r1, r1, lsl #32-align*8
71         orr     r1, r1, r0, lsr #align*8
72         stmdb   D!, {r1, r2, r3, r4}
73   .else
74         ldmib   S!, {r1, r2}
75         mov     r0, r4, lsr #align*8
76         ldmib   S!, {r3, r4}
77         orr     r0, r0, r1, lsl #32-align*8
78         mov     r1, r1, lsr #align*8
79         orr     r1, r1, r2, lsl #32-align*8
80         mov     r2, r2, lsr #align*8
81         orr     r2, r2, r3, lsl #32-align*8
82         mov     r3, r3, lsr #align*8
83         orr     r3, r3, r4, lsl #32-align*8
84         stmia   D!, {r0, r1, r2, r3}
85   .endif
86  .elseif words == 8
87   .if backwards
88         ldmdb   S!, {r4, r5, r6, r7}
89         mov     r8, r0, lsl #32-align*8
90         ldmdb   S!, {r0, r1, r2, r3}
91    .if use_pld
92         pld     [S, OFF]
93    .endif
94         orr     r8, r8, r7, lsr #align*8
95         mov     r7, r7, lsl #32-align*8
96         orr     r7, r7, r6, lsr #align*8
97         mov     r6, r6, lsl #32-align*8
98         orr     r6, r6, r5, lsr #align*8
99         mov     r5, r5, lsl #32-align*8
100         orr     r5, r5, r4, lsr #align*8
101         mov     r4, r4, lsl #32-align*8
102         orr     r4, r4, r3, lsr #align*8
103         mov     r3, r3, lsl #32-align*8
104         orr     r3, r3, r2, lsr #align*8
105         mov     r2, r2, lsl #32-align*8
106         orr     r2, r2, r1, lsr #align*8
107         mov     r1, r1, lsl #32-align*8
108         orr     r1, r1, r0, lsr #align*8
109         stmdb   D!, {r5, r6, r7, r8}
110         stmdb   D!, {r1, r2, r3, r4}
111   .else
112         ldmib   S!, {r1, r2, r3, r4}
113         mov     r0, r8, lsr #align*8
114         ldmib   S!, {r5, r6, r7, r8}
115    .if use_pld
116         pld     [S, OFF]
117    .endif
118         orr     r0, r0, r1, lsl #32-align*8
119         mov     r1, r1, lsr #align*8
120         orr     r1, r1, r2, lsl #32-align*8
121         mov     r2, r2, lsr #align*8
122         orr     r2, r2, r3, lsl #32-align*8
123         mov     r3, r3, lsr #align*8
124         orr     r3, r3, r4, lsl #32-align*8
125         mov     r4, r4, lsr #align*8
126         orr     r4, r4, r5, lsl #32-align*8
127         mov     r5, r5, lsr #align*8
128         orr     r5, r5, r6, lsl #32-align*8
129         mov     r6, r6, lsr #align*8
130         orr     r6, r6, r7, lsl #32-align*8
131         mov     r7, r7, lsr #align*8
132         orr     r7, r7, r8, lsl #32-align*8
133         stmia   D!, {r0, r1, r2, r3}
134         stmia   D!, {r4, r5, r6, r7}
135   .endif
136  .endif
137 .endm
138
139 .macro memcpy_leading_15bytes  backwards, align
140         movs    DAT1, DAT2, lsl #31
141         sub     N, N, DAT2
142  .if backwards
143         ldrmib  DAT0, [S, #-1]!
144         ldrcsh  DAT1, [S, #-2]!
145         strmib  DAT0, [D, #-1]!
146         strcsh  DAT1, [D, #-2]!
147  .else
148         ldrmib  DAT0, [S], #1
149         ldrcsh  DAT1, [S], #2
150         strmib  DAT0, [D], #1
151         strcsh  DAT1, [D], #2
152  .endif
153         movs    DAT1, DAT2, lsl #29
154  .if backwards
155         ldrmi   DAT0, [S, #-4]!
156   .if align == 0
157         ldmcsdb S!, {DAT1, DAT2}
158   .else
159         ldrcs   DAT2, [S, #-4]!
160         ldrcs   DAT1, [S, #-4]!
161   .endif
162         strmi   DAT0, [D, #-4]!
163         stmcsdb D!, {DAT1, DAT2}
164  .else
165         ldrmi   DAT0, [S], #4
166   .if align == 0
167         ldmcsia S!, {DAT1, DAT2}
168   .else
169         ldrcs   DAT1, [S], #4
170         ldrcs   DAT2, [S], #4
171   .endif
172         strmi   DAT0, [D], #4
173         stmcsia D!, {DAT1, DAT2}
174  .endif
175 .endm
176
177 .macro memcpy_trailing_15bytes  backwards, align
178         movs    N, N, lsl #29
179  .if backwards
180   .if align == 0
181         ldmcsdb S!, {DAT0, DAT1}
182   .else
183         ldrcs   DAT1, [S, #-4]!
184         ldrcs   DAT0, [S, #-4]!
185   .endif
186         ldrmi   DAT2, [S, #-4]!
187         stmcsdb D!, {DAT0, DAT1}
188         strmi   DAT2, [D, #-4]!
189  .else
190   .if align == 0
191         ldmcsia S!, {DAT0, DAT1}
192   .else
193         ldrcs   DAT0, [S], #4
194         ldrcs   DAT1, [S], #4
195   .endif
196         ldrmi   DAT2, [S], #4
197         stmcsia D!, {DAT0, DAT1}
198         strmi   DAT2, [D], #4
199  .endif
200         movs    N, N, lsl #2
201  .if backwards
202         ldrcsh  DAT0, [S, #-2]!
203         ldrmib  DAT1, [S, #-1]
204         strcsh  DAT0, [D, #-2]!
205         strmib  DAT1, [D, #-1]
206  .else
207         ldrcsh  DAT0, [S], #2
208         ldrmib  DAT1, [S]
209         strcsh  DAT0, [D], #2
210         strmib  DAT1, [D]
211  .endif
212 .endm
213
214 .macro memcpy_long_inner_loop  backwards, align
215  .if align != 0
216   .if backwards
217         ldr     DAT0, [S, #-align]!
218   .else
219         ldr     LAST, [S, #-align]!
220   .endif
221  .endif
222 110:
223  .if align == 0
224   .if backwards
225         ldmdb   S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST}
226         pld     [S, OFF]
227         stmdb   D!, {DAT4, DAT5, DAT6, LAST}
228         stmdb   D!, {DAT0, DAT1, DAT2, DAT3}
229   .else
230         ldmia   S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST}
231         pld     [S, OFF]
232         stmia   D!, {DAT0, DAT1, DAT2, DAT3}
233         stmia   D!, {DAT4, DAT5, DAT6, LAST}
234   .endif
235  .else
236         unaligned_words  backwards, align, 1, 8, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7, LAST
237  .endif
238         subs    N, N, #32
239         bhs     110b
240         /* Just before the final (prefetch_distance+1) 32-byte blocks, deal with final preloads */
241         preload_trailing  backwards, S, N, OFF
242         add     N, N, #(prefetch_distance+2)*32 - 32
243 120:
244  .if align == 0
245   .if backwards
246         ldmdb   S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST}
247         stmdb   D!, {DAT4, DAT5, DAT6, LAST}
248         stmdb   D!, {DAT0, DAT1, DAT2, DAT3}
249   .else
250         ldmia   S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST}
251         stmia   D!, {DAT0, DAT1, DAT2, DAT3}
252         stmia   D!, {DAT4, DAT5, DAT6, LAST}
253   .endif
254  .else
255         unaligned_words  backwards, align, 0, 8, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7, LAST
256  .endif
257         subs    N, N, #32
258         bhs     120b
259         tst     N, #16
260  .if align == 0
261   .if backwards
262         ldmnedb S!, {DAT0, DAT1, DAT2, LAST}
263         stmnedb D!, {DAT0, DAT1, DAT2, LAST}
264   .else
265         ldmneia S!, {DAT0, DAT1, DAT2, LAST}
266         stmneia D!, {DAT0, DAT1, DAT2, LAST}
267   .endif
268  .else
269         beq     130f
270         unaligned_words  backwards, align, 0, 4, DAT0, DAT1, DAT2, DAT3, LAST
271 130:
272  .endif
273         /* Trailing words and bytes */
274         tst      N, #15
275         beq      199f
276  .if align != 0
277         add     S, S, #align
278  .endif
279         memcpy_trailing_15bytes  backwards, align
280 199:
281         pop     {DAT3, DAT4, DAT5, DAT6, DAT7}
282         pop     {D, DAT1, DAT2, pc}
283 .endm
284
285 .macro memcpy_medium_inner_loop  backwards, align
286 120:
287  .if backwards
288   .if align == 0
289         ldmdb   S!, {DAT0, DAT1, DAT2, LAST}
290   .else
291         ldr     LAST, [S, #-4]!
292         ldr     DAT2, [S, #-4]!
293         ldr     DAT1, [S, #-4]!
294         ldr     DAT0, [S, #-4]!
295   .endif
296         stmdb   D!, {DAT0, DAT1, DAT2, LAST}
297  .else
298   .if align == 0
299         ldmia   S!, {DAT0, DAT1, DAT2, LAST}
300   .else
301         ldr     DAT0, [S], #4
302         ldr     DAT1, [S], #4
303         ldr     DAT2, [S], #4
304         ldr     LAST, [S], #4
305   .endif
306         stmia   D!, {DAT0, DAT1, DAT2, LAST}
307  .endif
308         subs     N, N, #16
309         bhs      120b
310         /* Trailing words and bytes */
311         tst      N, #15
312         beq      199f
313         memcpy_trailing_15bytes  backwards, align
314 199:
315         pop     {D, DAT1, DAT2, pc}
316 .endm
317
318 .macro memcpy_short_inner_loop  backwards, align
319         tst     N, #16
320  .if backwards
321   .if align == 0
322         ldmnedb S!, {DAT0, DAT1, DAT2, LAST}
323   .else
324         ldrne   LAST, [S, #-4]!
325         ldrne   DAT2, [S, #-4]!
326         ldrne   DAT1, [S, #-4]!
327         ldrne   DAT0, [S, #-4]!
328   .endif
329         stmnedb D!, {DAT0, DAT1, DAT2, LAST}
330  .else
331   .if align == 0
332         ldmneia S!, {DAT0, DAT1, DAT2, LAST}
333   .else
334         ldrne   DAT0, [S], #4
335         ldrne   DAT1, [S], #4
336         ldrne   DAT2, [S], #4
337         ldrne   LAST, [S], #4
338   .endif
339         stmneia D!, {DAT0, DAT1, DAT2, LAST}
340  .endif
341         memcpy_trailing_15bytes  backwards, align
342 199:
343         pop     {D, DAT1, DAT2, pc}
344 .endm
345
346 .macro memcpy backwards
347         D       .req    a1
348         S       .req    a2
349         N       .req    a3
350         DAT0    .req    a4
351         DAT1    .req    v1
352         DAT2    .req    v2
353         DAT3    .req    v3
354         DAT4    .req    v4
355         DAT5    .req    v5
356         DAT6    .req    v6
357         DAT7    .req    sl
358         LAST    .req    ip
359         OFF     .req    lr
360
361         UNWIND( .fnstart )
362
363         push    {D, DAT1, DAT2, lr}
364         UNWIND( .fnend )
365
366         UNWIND( .fnstart )
367         UNWIND( .save {D, DAT1, DAT2, lr} )
368
369  .if backwards
370         add     D, D, N
371         add     S, S, N
372  .endif
373
374         /* See if we're guaranteed to have at least one 16-byte aligned 16-byte write */
375         cmp     N, #31
376         blo     170f
377         /* To preload ahead as we go, we need at least (prefetch_distance+2) 32-byte blocks */
378         cmp     N, #(prefetch_distance+3)*32 - 1
379         blo     160f
380
381         /* Long case */
382         push    {DAT3, DAT4, DAT5, DAT6, DAT7}
383         UNWIND( .fnend )
384
385         UNWIND( .fnstart )
386         UNWIND( .save {D, DAT1, DAT2, lr} )
387         UNWIND( .save {DAT3, DAT4, DAT5, DAT6, DAT7} )
388
389         /* Adjust N so that the decrement instruction can also test for
390          * inner loop termination. We want it to stop when there are
391          * (prefetch_distance+1) complete blocks to go. */
392         sub     N, N, #(prefetch_distance+2)*32
393         preload_leading_step1  backwards, DAT0, S
394  .if backwards
395         /* Bug in GAS: it accepts, but mis-assembles the instruction
396          * ands    DAT2, D, #60, 2
397          * which sets DAT2 to the number of leading bytes until destination is aligned and also clears C (sets borrow)
398          */
399         .word   0xE210513C
400         beq     154f
401  .else
402         ands    DAT2, D, #15
403         beq     154f
404         rsb     DAT2, DAT2, #16 /* number of leading bytes until destination aligned */
405  .endif
406         preload_leading_step2  backwards, DAT0, S, DAT2, OFF
407         memcpy_leading_15bytes backwards, 1
408 154:    /* Destination now 16-byte aligned; we have at least one prefetch as well as at least one 16-byte output block */
409         /* Prefetch offset is best selected such that it lies in the first 8 of each 32 bytes - but it's just as easy to aim for the first one */
410  .if backwards
411         rsb     OFF, S, #3
412         and     OFF, OFF, #28
413         sub     OFF, OFF, #32*(prefetch_distance+1)
414  .else
415         and     OFF, S, #28
416         rsb     OFF, OFF, #32*prefetch_distance
417  .endif
418         movs    DAT0, S, lsl #31
419         bhi     157f
420         bcs     156f
421         bmi     155f
422         memcpy_long_inner_loop  backwards, 0
423 155:    memcpy_long_inner_loop  backwards, 1
424 156:    memcpy_long_inner_loop  backwards, 2
425 157:    memcpy_long_inner_loop  backwards, 3
426
427         UNWIND( .fnend )
428
429         UNWIND( .fnstart )
430         UNWIND( .save {D, DAT1, DAT2, lr} )
431
432 160:    /* Medium case */
433         preload_all  backwards, 0, 0, S, N, DAT2, OFF
434         sub     N, N, #16     /* simplifies inner loop termination */
435  .if backwards
436         ands    DAT2, D, #15
437         beq     164f
438  .else
439         ands    DAT2, D, #15
440         beq     164f
441         rsb     DAT2, DAT2, #16
442  .endif
443         memcpy_leading_15bytes backwards, align
444 164:    /* Destination now 16-byte aligned; we have at least one 16-byte output block */
445         tst     S, #3
446         bne     140f
447         memcpy_medium_inner_loop  backwards, 0
448 140:    memcpy_medium_inner_loop  backwards, 1
449
450 170:    /* Short case, less than 31 bytes, so no guarantee of at least one 16-byte block */
451         teq     N, #0
452         beq     199f
453         preload_all  backwards, 1, 0, S, N, DAT2, LAST
454         tst     D, #3
455         beq     174f
456 172:    subs    N, N, #1
457         blo     199f
458  .if backwards
459         ldrb    DAT0, [S, #-1]!
460         strb    DAT0, [D, #-1]!
461  .else
462         ldrb    DAT0, [S], #1
463         strb    DAT0, [D], #1
464  .endif
465         tst     D, #3
466         bne     172b
467 174:    /* Destination now 4-byte aligned; we have 0 or more output bytes to go */
468         tst     S, #3
469         bne     140f
470         memcpy_short_inner_loop  backwards, 0
471 140:    memcpy_short_inner_loop  backwards, 1
472
473         UNWIND( .fnend )
474
475         .unreq  D
476         .unreq  S
477         .unreq  N
478         .unreq  DAT0
479         .unreq  DAT1
480         .unreq  DAT2
481         .unreq  DAT3
482         .unreq  DAT4
483         .unreq  DAT5
484         .unreq  DAT6
485         .unreq  DAT7
486         .unreq  LAST
487         .unreq  OFF
488 .endm