Merge tag 'x86_urgent_for_v5.15_rc5' of git://git.kernel.org/pub/scm/linux/kernel...
[sfrench/cifs-2.6.git] / arch / powerpc / lib / memcmp_64.S
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * Author: Anton Blanchard <anton@au.ibm.com>
4  * Copyright 2015 IBM Corporation.
5  */
6 #include <asm/ppc_asm.h>
7 #include <asm/export.h>
8 #include <asm/ppc-opcode.h>
9
10 #define off8    r6
11 #define off16   r7
12 #define off24   r8
13
14 #define rA      r9
15 #define rB      r10
16 #define rC      r11
17 #define rD      r27
18 #define rE      r28
19 #define rF      r29
20 #define rG      r30
21 #define rH      r31
22
23 #ifdef __LITTLE_ENDIAN__
24 #define LH      lhbrx
25 #define LW      lwbrx
26 #define LD      ldbrx
27 #define LVS     lvsr
28 #define VPERM(_VRT,_VRA,_VRB,_VRC) \
29         vperm _VRT,_VRB,_VRA,_VRC
30 #else
31 #define LH      lhzx
32 #define LW      lwzx
33 #define LD      ldx
34 #define LVS     lvsl
35 #define VPERM(_VRT,_VRA,_VRB,_VRC) \
36         vperm _VRT,_VRA,_VRB,_VRC
37 #endif
38
39 #define VMX_THRESH 4096
40 #define ENTER_VMX_OPS   \
41         mflr    r0;     \
42         std     r3,-STACKFRAMESIZE+STK_REG(R31)(r1); \
43         std     r4,-STACKFRAMESIZE+STK_REG(R30)(r1); \
44         std     r5,-STACKFRAMESIZE+STK_REG(R29)(r1); \
45         std     r0,16(r1); \
46         stdu    r1,-STACKFRAMESIZE(r1); \
47         bl      enter_vmx_ops; \
48         cmpwi   cr1,r3,0; \
49         ld      r0,STACKFRAMESIZE+16(r1); \
50         ld      r3,STK_REG(R31)(r1); \
51         ld      r4,STK_REG(R30)(r1); \
52         ld      r5,STK_REG(R29)(r1); \
53         addi    r1,r1,STACKFRAMESIZE; \
54         mtlr    r0
55
56 #define EXIT_VMX_OPS \
57         mflr    r0; \
58         std     r3,-STACKFRAMESIZE+STK_REG(R31)(r1); \
59         std     r4,-STACKFRAMESIZE+STK_REG(R30)(r1); \
60         std     r5,-STACKFRAMESIZE+STK_REG(R29)(r1); \
61         std     r0,16(r1); \
62         stdu    r1,-STACKFRAMESIZE(r1); \
63         bl      exit_vmx_ops; \
64         ld      r0,STACKFRAMESIZE+16(r1); \
65         ld      r3,STK_REG(R31)(r1); \
66         ld      r4,STK_REG(R30)(r1); \
67         ld      r5,STK_REG(R29)(r1); \
68         addi    r1,r1,STACKFRAMESIZE; \
69         mtlr    r0
70
71 /*
72  * LD_VSR_CROSS16B load the 2nd 16 bytes for _vaddr which is unaligned with
73  * 16 bytes boundary and permute the result with the 1st 16 bytes.
74
75  *    |  y y y y y y y y y y y y y 0 1 2 | 3 4 5 6 7 8 9 a b c d e f z z z |
76  *    ^                                  ^                                 ^
77  * 0xbbbb10                          0xbbbb20                          0xbbb30
78  *                                 ^
79  *                                _vaddr
80  *
81  *
82  * _vmask is the mask generated by LVS
83  * _v1st_qw is the 1st aligned QW of current addr which is already loaded.
84  *   for example: 0xyyyyyyyyyyyyy012 for big endian
85  * _v2nd_qw is the 2nd aligned QW of cur _vaddr to be loaded.
86  *   for example: 0x3456789abcdefzzz for big endian
87  * The permute result is saved in _v_res.
88  *   for example: 0x0123456789abcdef for big endian.
89  */
90 #define LD_VSR_CROSS16B(_vaddr,_vmask,_v1st_qw,_v2nd_qw,_v_res) \
91         lvx     _v2nd_qw,_vaddr,off16; \
92         VPERM(_v_res,_v1st_qw,_v2nd_qw,_vmask)
93
94 /*
95  * There are 2 categories for memcmp:
96  * 1) src/dst has the same offset to the 8 bytes boundary. The handlers
97  * are named like .Lsameoffset_xxxx
98  * 2) src/dst has different offset to the 8 bytes boundary. The handlers
99  * are named like .Ldiffoffset_xxxx
100  */
101 _GLOBAL_TOC(memcmp)
102         cmpdi   cr1,r5,0
103
104         /* Use the short loop if the src/dst addresses are not
105          * with the same offset of 8 bytes align boundary.
106          */
107         xor     r6,r3,r4
108         andi.   r6,r6,7
109
110         /* Fall back to short loop if compare at aligned addrs
111          * with less than 8 bytes.
112          */
113         cmpdi   cr6,r5,7
114
115         beq     cr1,.Lzero
116         bgt     cr6,.Lno_short
117
118 .Lshort:
119         mtctr   r5
120 1:      lbz     rA,0(r3)
121         lbz     rB,0(r4)
122         subf.   rC,rB,rA
123         bne     .Lnon_zero
124         bdz     .Lzero
125
126         lbz     rA,1(r3)
127         lbz     rB,1(r4)
128         subf.   rC,rB,rA
129         bne     .Lnon_zero
130         bdz     .Lzero
131
132         lbz     rA,2(r3)
133         lbz     rB,2(r4)
134         subf.   rC,rB,rA
135         bne     .Lnon_zero
136         bdz     .Lzero
137
138         lbz     rA,3(r3)
139         lbz     rB,3(r4)
140         subf.   rC,rB,rA
141         bne     .Lnon_zero
142
143         addi    r3,r3,4
144         addi    r4,r4,4
145
146         bdnz    1b
147
148 .Lzero:
149         li      r3,0
150         blr
151
152 .Lno_short:
153         dcbt    0,r3
154         dcbt    0,r4
155         bne     .Ldiffoffset_8bytes_make_align_start
156
157
158 .Lsameoffset_8bytes_make_align_start:
159         /* attempt to compare bytes not aligned with 8 bytes so that
160          * rest comparison can run based on 8 bytes alignment.
161          */
162         andi.   r6,r3,7
163
164         /* Try to compare the first double word which is not 8 bytes aligned:
165          * load the first double word at (src & ~7UL) and shift left appropriate
166          * bits before comparision.
167          */
168         rlwinm  r6,r3,3,26,28
169         beq     .Lsameoffset_8bytes_aligned
170         clrrdi  r3,r3,3
171         clrrdi  r4,r4,3
172         LD      rA,0,r3
173         LD      rB,0,r4
174         sld     rA,rA,r6
175         sld     rB,rB,r6
176         cmpld   cr0,rA,rB
177         srwi    r6,r6,3
178         bne     cr0,.LcmpAB_lightweight
179         subfic  r6,r6,8
180         subf.   r5,r6,r5
181         addi    r3,r3,8
182         addi    r4,r4,8
183         beq     .Lzero
184
185 .Lsameoffset_8bytes_aligned:
186         /* now we are aligned with 8 bytes.
187          * Use .Llong loop if left cmp bytes are equal or greater than 32B.
188          */
189         cmpdi   cr6,r5,31
190         bgt     cr6,.Llong
191
192 .Lcmp_lt32bytes:
193         /* compare 1 ~ 31 bytes, at least r3 addr is 8 bytes aligned now */
194         cmpdi   cr5,r5,7
195         srdi    r0,r5,3
196         ble     cr5,.Lcmp_rest_lt8bytes
197
198         /* handle 8 ~ 31 bytes */
199         clrldi  r5,r5,61
200         mtctr   r0
201 2:
202         LD      rA,0,r3
203         LD      rB,0,r4
204         cmpld   cr0,rA,rB
205         addi    r3,r3,8
206         addi    r4,r4,8
207         bne     cr0,.LcmpAB_lightweight
208         bdnz    2b
209
210         cmpwi   r5,0
211         beq     .Lzero
212
213 .Lcmp_rest_lt8bytes:
214         /*
215          * Here we have less than 8 bytes to compare. At least s1 is aligned to
216          * 8 bytes, but s2 may not be. We must make sure s2 + 7 doesn't cross a
217          * page boundary, otherwise we might read past the end of the buffer and
218          * trigger a page fault. We use 4K as the conservative minimum page
219          * size. If we detect that case we go to the byte-by-byte loop.
220          *
221          * Otherwise the next double word is loaded from s1 and s2, and shifted
222          * right to compare the appropriate bits.
223          */
224         clrldi  r6,r4,(64-12)   // r6 = r4 & 0xfff
225         cmpdi   r6,0xff8
226         bgt     .Lshort
227
228         subfic  r6,r5,8
229         slwi    r6,r6,3
230         LD      rA,0,r3
231         LD      rB,0,r4
232         srd     rA,rA,r6
233         srd     rB,rB,r6
234         cmpld   cr0,rA,rB
235         bne     cr0,.LcmpAB_lightweight
236         b       .Lzero
237
238 .Lnon_zero:
239         mr      r3,rC
240         blr
241
242 .Llong:
243 #ifdef CONFIG_ALTIVEC
244 BEGIN_FTR_SECTION
245         /* Try to use vmx loop if length is equal or greater than 4K */
246         cmpldi  cr6,r5,VMX_THRESH
247         bge     cr6,.Lsameoffset_vmx_cmp
248 END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
249
250 .Llong_novmx_cmp:
251 #endif
252         /* At least s1 addr is aligned with 8 bytes */
253         li      off8,8
254         li      off16,16
255         li      off24,24
256
257         std     r31,-8(r1)
258         std     r30,-16(r1)
259         std     r29,-24(r1)
260         std     r28,-32(r1)
261         std     r27,-40(r1)
262
263         srdi    r0,r5,5
264         mtctr   r0
265         andi.   r5,r5,31
266
267         LD      rA,0,r3
268         LD      rB,0,r4
269
270         LD      rC,off8,r3
271         LD      rD,off8,r4
272
273         LD      rE,off16,r3
274         LD      rF,off16,r4
275
276         LD      rG,off24,r3
277         LD      rH,off24,r4
278         cmpld   cr0,rA,rB
279
280         addi    r3,r3,32
281         addi    r4,r4,32
282
283         bdz     .Lfirst32
284
285         LD      rA,0,r3
286         LD      rB,0,r4
287         cmpld   cr1,rC,rD
288
289         LD      rC,off8,r3
290         LD      rD,off8,r4
291         cmpld   cr6,rE,rF
292
293         LD      rE,off16,r3
294         LD      rF,off16,r4
295         cmpld   cr7,rG,rH
296         bne     cr0,.LcmpAB
297
298         LD      rG,off24,r3
299         LD      rH,off24,r4
300         cmpld   cr0,rA,rB
301         bne     cr1,.LcmpCD
302
303         addi    r3,r3,32
304         addi    r4,r4,32
305
306         bdz     .Lsecond32
307
308         .balign 16
309
310 1:      LD      rA,0,r3
311         LD      rB,0,r4
312         cmpld   cr1,rC,rD
313         bne     cr6,.LcmpEF
314
315         LD      rC,off8,r3
316         LD      rD,off8,r4
317         cmpld   cr6,rE,rF
318         bne     cr7,.LcmpGH
319
320         LD      rE,off16,r3
321         LD      rF,off16,r4
322         cmpld   cr7,rG,rH
323         bne     cr0,.LcmpAB
324
325         LD      rG,off24,r3
326         LD      rH,off24,r4
327         cmpld   cr0,rA,rB
328         bne     cr1,.LcmpCD
329
330         addi    r3,r3,32
331         addi    r4,r4,32
332
333         bdnz    1b
334
335 .Lsecond32:
336         cmpld   cr1,rC,rD
337         bne     cr6,.LcmpEF
338
339         cmpld   cr6,rE,rF
340         bne     cr7,.LcmpGH
341
342         cmpld   cr7,rG,rH
343         bne     cr0,.LcmpAB
344
345         bne     cr1,.LcmpCD
346         bne     cr6,.LcmpEF
347         bne     cr7,.LcmpGH
348
349 .Ltail:
350         ld      r31,-8(r1)
351         ld      r30,-16(r1)
352         ld      r29,-24(r1)
353         ld      r28,-32(r1)
354         ld      r27,-40(r1)
355
356         cmpdi   r5,0
357         beq     .Lzero
358         b       .Lshort
359
360 .Lfirst32:
361         cmpld   cr1,rC,rD
362         cmpld   cr6,rE,rF
363         cmpld   cr7,rG,rH
364
365         bne     cr0,.LcmpAB
366         bne     cr1,.LcmpCD
367         bne     cr6,.LcmpEF
368         bne     cr7,.LcmpGH
369
370         b       .Ltail
371
372 .LcmpAB:
373         li      r3,1
374         bgt     cr0,.Lout
375         li      r3,-1
376         b       .Lout
377
378 .LcmpCD:
379         li      r3,1
380         bgt     cr1,.Lout
381         li      r3,-1
382         b       .Lout
383
384 .LcmpEF:
385         li      r3,1
386         bgt     cr6,.Lout
387         li      r3,-1
388         b       .Lout
389
390 .LcmpGH:
391         li      r3,1
392         bgt     cr7,.Lout
393         li      r3,-1
394
395 .Lout:
396         ld      r31,-8(r1)
397         ld      r30,-16(r1)
398         ld      r29,-24(r1)
399         ld      r28,-32(r1)
400         ld      r27,-40(r1)
401         blr
402
403 .LcmpAB_lightweight:   /* skip NV GPRS restore */
404         li      r3,1
405         bgtlr
406         li      r3,-1
407         blr
408
409 #ifdef CONFIG_ALTIVEC
410 .Lsameoffset_vmx_cmp:
411         /* Enter with src/dst addrs has the same offset with 8 bytes
412          * align boundary.
413          *
414          * There is an optimization based on following fact: memcmp()
415          * prones to fail early at the first 32 bytes.
416          * Before applying VMX instructions which will lead to 32x128bits
417          * VMX regs load/restore penalty, we compare the first 32 bytes
418          * so that we can catch the ~80% fail cases.
419          */
420
421         li      r0,4
422         mtctr   r0
423 .Lsameoffset_prechk_32B_loop:
424         LD      rA,0,r3
425         LD      rB,0,r4
426         cmpld   cr0,rA,rB
427         addi    r3,r3,8
428         addi    r4,r4,8
429         bne     cr0,.LcmpAB_lightweight
430         addi    r5,r5,-8
431         bdnz    .Lsameoffset_prechk_32B_loop
432
433         ENTER_VMX_OPS
434         beq     cr1,.Llong_novmx_cmp
435
436 3:
437         /* need to check whether r4 has the same offset with r3
438          * for 16 bytes boundary.
439          */
440         xor     r0,r3,r4
441         andi.   r0,r0,0xf
442         bne     .Ldiffoffset_vmx_cmp_start
443
444         /* len is no less than 4KB. Need to align with 16 bytes further.
445          */
446         andi.   rA,r3,8
447         LD      rA,0,r3
448         beq     4f
449         LD      rB,0,r4
450         cmpld   cr0,rA,rB
451         addi    r3,r3,8
452         addi    r4,r4,8
453         addi    r5,r5,-8
454
455         beq     cr0,4f
456         /* save and restore cr0 */
457         mfocrf  r5,128
458         EXIT_VMX_OPS
459         mtocrf  128,r5
460         b       .LcmpAB_lightweight
461
462 4:
463         /* compare 32 bytes for each loop */
464         srdi    r0,r5,5
465         mtctr   r0
466         clrldi  r5,r5,59
467         li      off16,16
468
469 .balign 16
470 5:
471         lvx     v0,0,r3
472         lvx     v1,0,r4
473         VCMPEQUD_RC(v0,v0,v1)
474         bnl     cr6,7f
475         lvx     v0,off16,r3
476         lvx     v1,off16,r4
477         VCMPEQUD_RC(v0,v0,v1)
478         bnl     cr6,6f
479         addi    r3,r3,32
480         addi    r4,r4,32
481         bdnz    5b
482
483         EXIT_VMX_OPS
484         cmpdi   r5,0
485         beq     .Lzero
486         b       .Lcmp_lt32bytes
487
488 6:
489         addi    r3,r3,16
490         addi    r4,r4,16
491
492 7:
493         /* diff the last 16 bytes */
494         EXIT_VMX_OPS
495         LD      rA,0,r3
496         LD      rB,0,r4
497         cmpld   cr0,rA,rB
498         li      off8,8
499         bne     cr0,.LcmpAB_lightweight
500
501         LD      rA,off8,r3
502         LD      rB,off8,r4
503         cmpld   cr0,rA,rB
504         bne     cr0,.LcmpAB_lightweight
505         b       .Lzero
506 #endif
507
508 .Ldiffoffset_8bytes_make_align_start:
509         /* now try to align s1 with 8 bytes */
510         rlwinm  r6,r3,3,26,28
511         beq     .Ldiffoffset_align_s1_8bytes
512
513         clrrdi  r3,r3,3
514         LD      rA,0,r3
515         LD      rB,0,r4  /* unaligned load */
516         sld     rA,rA,r6
517         srd     rA,rA,r6
518         srd     rB,rB,r6
519         cmpld   cr0,rA,rB
520         srwi    r6,r6,3
521         bne     cr0,.LcmpAB_lightweight
522
523         subfic  r6,r6,8
524         subf.   r5,r6,r5
525         addi    r3,r3,8
526         add     r4,r4,r6
527
528         beq     .Lzero
529
530 .Ldiffoffset_align_s1_8bytes:
531         /* now s1 is aligned with 8 bytes. */
532 #ifdef CONFIG_ALTIVEC
533 BEGIN_FTR_SECTION
534         /* only do vmx ops when the size equal or greater than 4K bytes */
535         cmpdi   cr5,r5,VMX_THRESH
536         bge     cr5,.Ldiffoffset_vmx_cmp
537 END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
538
539 .Ldiffoffset_novmx_cmp:
540 #endif
541
542
543         cmpdi   cr5,r5,31
544         ble     cr5,.Lcmp_lt32bytes
545
546 #ifdef CONFIG_ALTIVEC
547         b       .Llong_novmx_cmp
548 #else
549         b       .Llong
550 #endif
551
552 #ifdef CONFIG_ALTIVEC
553 .Ldiffoffset_vmx_cmp:
554         /* perform a 32 bytes pre-checking before
555          * enable VMX operations.
556          */
557         li      r0,4
558         mtctr   r0
559 .Ldiffoffset_prechk_32B_loop:
560         LD      rA,0,r3
561         LD      rB,0,r4
562         cmpld   cr0,rA,rB
563         addi    r3,r3,8
564         addi    r4,r4,8
565         bne     cr0,.LcmpAB_lightweight
566         addi    r5,r5,-8
567         bdnz    .Ldiffoffset_prechk_32B_loop
568
569         ENTER_VMX_OPS
570         beq     cr1,.Ldiffoffset_novmx_cmp
571
572 .Ldiffoffset_vmx_cmp_start:
573         /* Firstly try to align r3 with 16 bytes */
574         andi.   r6,r3,0xf
575         li      off16,16
576         beq     .Ldiffoffset_vmx_s1_16bytes_align
577
578         LVS     v3,0,r3
579         LVS     v4,0,r4
580
581         lvx     v5,0,r3
582         lvx     v6,0,r4
583         LD_VSR_CROSS16B(r3,v3,v5,v7,v9)
584         LD_VSR_CROSS16B(r4,v4,v6,v8,v10)
585
586         VCMPEQUB_RC(v7,v9,v10)
587         bnl     cr6,.Ldiffoffset_vmx_diff_found
588
589         subfic  r6,r6,16
590         subf    r5,r6,r5
591         add     r3,r3,r6
592         add     r4,r4,r6
593
594 .Ldiffoffset_vmx_s1_16bytes_align:
595         /* now s1 is aligned with 16 bytes */
596         lvx     v6,0,r4
597         LVS     v4,0,r4
598         srdi    r6,r5,5  /* loop for 32 bytes each */
599         clrldi  r5,r5,59
600         mtctr   r6
601
602 .balign 16
603 .Ldiffoffset_vmx_32bytesloop:
604         /* the first qw of r4 was saved in v6 */
605         lvx     v9,0,r3
606         LD_VSR_CROSS16B(r4,v4,v6,v8,v10)
607         VCMPEQUB_RC(v7,v9,v10)
608         vor     v6,v8,v8
609         bnl     cr6,.Ldiffoffset_vmx_diff_found
610
611         addi    r3,r3,16
612         addi    r4,r4,16
613
614         lvx     v9,0,r3
615         LD_VSR_CROSS16B(r4,v4,v6,v8,v10)
616         VCMPEQUB_RC(v7,v9,v10)
617         vor     v6,v8,v8
618         bnl     cr6,.Ldiffoffset_vmx_diff_found
619
620         addi    r3,r3,16
621         addi    r4,r4,16
622
623         bdnz    .Ldiffoffset_vmx_32bytesloop
624
625         EXIT_VMX_OPS
626
627         cmpdi   r5,0
628         beq     .Lzero
629         b       .Lcmp_lt32bytes
630
631 .Ldiffoffset_vmx_diff_found:
632         EXIT_VMX_OPS
633         /* anyway, the diff will appear in next 16 bytes */
634         li      r5,16
635         b       .Lcmp_lt32bytes
636
637 #endif
638 EXPORT_SYMBOL(memcmp)