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 <linux/export.h> 7#include <asm/ppc_asm.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 CFUNC(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 CFUNC(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 1201: 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 2012: 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 244BEGIN_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 248END_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 3101: 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 4363: 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 4624: 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 4705: 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 4886: 489 addi r3,r3,16 490 addi r4,r4,16 491 4927: 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 533BEGIN_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 537END_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 638EXPORT_SYMBOL(memcmp) 639