Merge tag 'v5.15.57' into rpi-5.15.y
[platform/kernel/linux-rpi.git] / arch / arm / lib / memcmp_rpi.S
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 #include <linux/linkage.h>
30 #include "arm-mem.h"
31
32 /* Prevent the stack from becoming executable */
33 #if defined(__linux__) && defined(__ELF__)
34 .section .note.GNU-stack,"",%progbits
35 #endif
36
37     .text
38     .arch armv6
39     .object_arch armv4
40     .arm
41     .altmacro
42     .p2align 2
43
44 .macro memcmp_process_head  unaligned
45  .if unaligned
46         ldr     DAT0, [S_1], #4
47         ldr     DAT1, [S_1], #4
48         ldr     DAT2, [S_1], #4
49         ldr     DAT3, [S_1], #4
50  .else
51         ldmia   S_1!, {DAT0, DAT1, DAT2, DAT3}
52  .endif
53         ldmia   S_2!, {DAT4, DAT5, DAT6, DAT7}
54 .endm
55
56 .macro memcmp_process_tail
57         cmp     DAT0, DAT4
58         cmpeq   DAT1, DAT5
59         cmpeq   DAT2, DAT6
60         cmpeq   DAT3, DAT7
61         bne     200f
62 .endm
63
64 .macro memcmp_leading_31bytes
65         movs    DAT0, OFF, lsl #31
66         ldrmib  DAT0, [S_1], #1
67         ldrcsh  DAT1, [S_1], #2
68         ldrmib  DAT4, [S_2], #1
69         ldrcsh  DAT5, [S_2], #2
70         movpl   DAT0, #0
71         movcc   DAT1, #0
72         movpl   DAT4, #0
73         movcc   DAT5, #0
74         submi   N, N, #1
75         subcs   N, N, #2
76         cmp     DAT0, DAT4
77         cmpeq   DAT1, DAT5
78         bne     200f
79         movs    DAT0, OFF, lsl #29
80         ldrmi   DAT0, [S_1], #4
81         ldrcs   DAT1, [S_1], #4
82         ldrcs   DAT2, [S_1], #4
83         ldrmi   DAT4, [S_2], #4
84         ldmcsia S_2!, {DAT5, DAT6}
85         movpl   DAT0, #0
86         movcc   DAT1, #0
87         movcc   DAT2, #0
88         movpl   DAT4, #0
89         movcc   DAT5, #0
90         movcc   DAT6, #0
91         submi   N, N, #4
92         subcs   N, N, #8
93         cmp     DAT0, DAT4
94         cmpeq   DAT1, DAT5
95         cmpeq   DAT2, DAT6
96         bne     200f
97         tst     OFF, #16
98         beq     105f
99         memcmp_process_head  1
100         sub     N, N, #16
101         memcmp_process_tail
102 105:
103 .endm
104
105 .macro memcmp_trailing_15bytes  unaligned
106         movs    N, N, lsl #29
107  .if unaligned
108         ldrcs   DAT0, [S_1], #4
109         ldrcs   DAT1, [S_1], #4
110  .else
111         ldmcsia S_1!, {DAT0, DAT1}
112  .endif
113         ldrmi   DAT2, [S_1], #4
114         ldmcsia S_2!, {DAT4, DAT5}
115         ldrmi   DAT6, [S_2], #4
116         movcc   DAT0, #0
117         movcc   DAT1, #0
118         movpl   DAT2, #0
119         movcc   DAT4, #0
120         movcc   DAT5, #0
121         movpl   DAT6, #0
122         cmp     DAT0, DAT4
123         cmpeq   DAT1, DAT5
124         cmpeq   DAT2, DAT6
125         bne     200f
126         movs    N, N, lsl #2
127         ldrcsh  DAT0, [S_1], #2
128         ldrmib  DAT1, [S_1]
129         ldrcsh  DAT4, [S_2], #2
130         ldrmib  DAT5, [S_2]
131         movcc   DAT0, #0
132         movpl   DAT1, #0
133         movcc   DAT4, #0
134         movpl   DAT5, #0
135         cmp     DAT0, DAT4
136         cmpeq   DAT1, DAT5
137         bne     200f
138 .endm
139
140 .macro memcmp_long_inner_loop  unaligned
141 110:
142         memcmp_process_head  unaligned
143         pld     [S_2, #prefetch_distance*32 + 16]
144         memcmp_process_tail
145         memcmp_process_head  unaligned
146         pld     [S_1, OFF]
147         memcmp_process_tail
148         subs    N, N, #32
149         bhs     110b
150         /* Just before the final (prefetch_distance+1) 32-byte blocks,
151          * deal with final preloads */
152         preload_trailing  0, S_1, N, DAT0
153         preload_trailing  0, S_2, N, DAT0
154         add     N, N, #(prefetch_distance+2)*32 - 16
155 120:
156         memcmp_process_head  unaligned
157         memcmp_process_tail
158         subs    N, N, #16
159         bhs     120b
160         /* Trailing words and bytes */
161         tst     N, #15
162         beq     199f
163         memcmp_trailing_15bytes  unaligned
164 199:    /* Reached end without detecting a difference */
165         mov     a1, #0
166         setend  le
167         pop     {DAT1-DAT6, pc}
168 .endm
169
170 .macro memcmp_short_inner_loop  unaligned
171         subs    N, N, #16     /* simplifies inner loop termination */
172         blo     122f
173 120:
174         memcmp_process_head  unaligned
175         memcmp_process_tail
176         subs    N, N, #16
177         bhs     120b
178 122:    /* Trailing words and bytes */
179         tst     N, #15
180         beq     199f
181         memcmp_trailing_15bytes  unaligned
182 199:    /* Reached end without detecting a difference */
183         mov     a1, #0
184         setend  le
185         pop     {DAT1-DAT6, pc}
186 .endm
187
188 /*
189  * int memcmp(const void *s1, const void *s2, size_t n);
190  * On entry:
191  * a1 = pointer to buffer 1
192  * a2 = pointer to buffer 2
193  * a3 = number of bytes to compare (as unsigned chars)
194  * On exit:
195  * a1 = >0/=0/<0 if s1 >/=/< s2
196  */
197
198 .set prefetch_distance, 2
199
200 ENTRY(memcmp)
201         S_1     .req    a1
202         S_2     .req    a2
203         N       .req    a3
204         DAT0    .req    a4
205         DAT1    .req    v1
206         DAT2    .req    v2
207         DAT3    .req    v3
208         DAT4    .req    v4
209         DAT5    .req    v5
210         DAT6    .req    v6
211         DAT7    .req    ip
212         OFF     .req    lr
213
214         push    {DAT1-DAT6, lr}
215         setend  be /* lowest-addressed bytes are most significant */
216
217         /* To preload ahead as we go, we need at least (prefetch_distance+2) 32-byte blocks */
218         cmp     N, #(prefetch_distance+3)*32 - 1
219         blo     170f
220
221         /* Long case */
222         /* Adjust N so that the decrement instruction can also test for
223          * inner loop termination. We want it to stop when there are
224          * (prefetch_distance+1) complete blocks to go. */
225         sub     N, N, #(prefetch_distance+2)*32
226         preload_leading_step1  0, DAT0, S_1
227         preload_leading_step1  0, DAT1, S_2
228         tst     S_2, #31
229         beq     154f
230         rsb     OFF, S_2, #0 /* no need to AND with 15 here */
231         preload_leading_step2  0, DAT0, S_1, OFF, DAT2
232         preload_leading_step2  0, DAT1, S_2, OFF, DAT2
233         memcmp_leading_31bytes
234 154:    /* Second source now cacheline (32-byte) aligned; we have at
235          * least one prefetch to go. */
236         /* Prefetch offset is best selected such that it lies in the
237          * first 8 of each 32 bytes - but it's just as easy to aim for
238          * the first one */
239         and     OFF, S_1, #31
240         rsb     OFF, OFF, #32*prefetch_distance
241         tst     S_1, #3
242         bne     140f
243         memcmp_long_inner_loop  0
244 140:    memcmp_long_inner_loop  1
245
246 170:    /* Short case */
247         teq     N, #0
248         beq     199f
249         preload_all 0, 0, 0, S_1, N, DAT0, DAT1
250         preload_all 0, 0, 0, S_2, N, DAT0, DAT1
251         tst     S_2, #3
252         beq     174f
253 172:    subs    N, N, #1
254         blo     199f
255         ldrb    DAT0, [S_1], #1
256         ldrb    DAT4, [S_2], #1
257         cmp     DAT0, DAT4
258         bne     200f
259         tst     S_2, #3
260         bne     172b
261 174:    /* Second source now 4-byte aligned; we have 0 or more bytes to go */
262         tst     S_1, #3
263         bne     140f
264         memcmp_short_inner_loop  0
265 140:    memcmp_short_inner_loop  1
266
267 200:    /* Difference found: determine sign. */
268         movhi   a1, #1
269         movlo   a1, #-1
270         setend  le
271         pop     {DAT1-DAT6, pc}
272
273         .unreq  S_1
274         .unreq  S_2
275         .unreq  N
276         .unreq  DAT0
277         .unreq  DAT1
278         .unreq  DAT2
279         .unreq  DAT3
280         .unreq  DAT4
281         .unreq  DAT5
282         .unreq  DAT6
283         .unreq  DAT7
284         .unreq  OFF
285 ENDPROC(memcmp)