162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * strlen() for PPC32 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2018 Christophe Leroy CS Systemes d'Information. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Inspired from glibc implementation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <asm/ppc_asm.h> 1162306a36Sopenharmony_ci#include <asm/cache.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci .text 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* 1662306a36Sopenharmony_ci * Algorithm: 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * 1) Given a word 'x', we can test to see if it contains any 0 bytes 1962306a36Sopenharmony_ci * by subtracting 0x01010101, and seeing if any of the high bits of each 2062306a36Sopenharmony_ci * byte changed from 0 to 1. This works because the least significant 2162306a36Sopenharmony_ci * 0 byte must have had no incoming carry (otherwise it's not the least 2262306a36Sopenharmony_ci * significant), so it is 0x00 - 0x01 == 0xff. For all other 2362306a36Sopenharmony_ci * byte values, either they have the high bit set initially, or when 2462306a36Sopenharmony_ci * 1 is subtracted you get a value in the range 0x00-0x7f, none of which 2562306a36Sopenharmony_ci * have their high bit set. The expression here is 2662306a36Sopenharmony_ci * (x - 0x01010101) & ~x & 0x80808080), which gives 0x00000000 when 2762306a36Sopenharmony_ci * there were no 0x00 bytes in the word. You get 0x80 in bytes that 2862306a36Sopenharmony_ci * match, but possibly false 0x80 matches in the next more significant 2962306a36Sopenharmony_ci * byte to a true match due to carries. For little-endian this is 3062306a36Sopenharmony_ci * of no consequence since the least significant match is the one 3162306a36Sopenharmony_ci * we're interested in, but big-endian needs method 2 to find which 3262306a36Sopenharmony_ci * byte matches. 3362306a36Sopenharmony_ci * 2) Given a word 'x', we can test to see _which_ byte was zero by 3462306a36Sopenharmony_ci * calculating ~(((x & ~0x80808080) - 0x80808080 - 1) | x | ~0x80808080). 3562306a36Sopenharmony_ci * This produces 0x80 in each byte that was zero, and 0x00 in all 3662306a36Sopenharmony_ci * the other bytes. The '| ~0x80808080' clears the low 7 bits in each 3762306a36Sopenharmony_ci * byte, and the '| x' part ensures that bytes with the high bit set 3862306a36Sopenharmony_ci * produce 0x00. The addition will carry into the high bit of each byte 3962306a36Sopenharmony_ci * iff that byte had one of its low 7 bits set. We can then just see 4062306a36Sopenharmony_ci * which was the most significant bit set and divide by 8 to find how 4162306a36Sopenharmony_ci * many to add to the index. 4262306a36Sopenharmony_ci * This is from the book 'The PowerPC Compiler Writer's Guide', 4362306a36Sopenharmony_ci * by Steve Hoxey, Faraydon Karim, Bill Hay and Hank Warren. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci_GLOBAL(strlen) 4762306a36Sopenharmony_ci andi. r0, r3, 3 4862306a36Sopenharmony_ci lis r7, 0x0101 4962306a36Sopenharmony_ci addi r10, r3, -4 5062306a36Sopenharmony_ci addic r7, r7, 0x0101 /* r7 = 0x01010101 (lomagic) & clear XER[CA] */ 5162306a36Sopenharmony_ci rotlwi r6, r7, 31 /* r6 = 0x80808080 (himagic) */ 5262306a36Sopenharmony_ci bne- 3f 5362306a36Sopenharmony_ci .balign IFETCH_ALIGN_BYTES 5462306a36Sopenharmony_ci1: lwzu r9, 4(r10) 5562306a36Sopenharmony_ci2: subf r8, r7, r9 5662306a36Sopenharmony_ci and. r8, r8, r6 5762306a36Sopenharmony_ci beq+ 1b 5862306a36Sopenharmony_ci andc. r8, r8, r9 5962306a36Sopenharmony_ci beq+ 1b 6062306a36Sopenharmony_ci andc r8, r9, r6 6162306a36Sopenharmony_ci orc r9, r9, r6 6262306a36Sopenharmony_ci subfe r8, r6, r8 6362306a36Sopenharmony_ci nor r8, r8, r9 6462306a36Sopenharmony_ci cntlzw r8, r8 6562306a36Sopenharmony_ci subf r3, r3, r10 6662306a36Sopenharmony_ci srwi r8, r8, 3 6762306a36Sopenharmony_ci add r3, r3, r8 6862306a36Sopenharmony_ci blr 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Missaligned string: make sure bytes before string are seen not 0 */ 7162306a36Sopenharmony_ci3: xor r10, r10, r0 7262306a36Sopenharmony_ci orc r8, r8, r8 7362306a36Sopenharmony_ci lwzu r9, 4(r10) 7462306a36Sopenharmony_ci slwi r0, r0, 3 7562306a36Sopenharmony_ci srw r8, r8, r0 7662306a36Sopenharmony_ci orc r9, r9, r8 7762306a36Sopenharmony_ci b 2b 7862306a36Sopenharmony_ciEXPORT_SYMBOL(strlen) 79