1*84d9c625SLionel Sambuc/*- 2*84d9c625SLionel Sambuc * Copyright (c) 2012 The NetBSD Foundation, Inc. 3*84d9c625SLionel Sambuc * All rights reserved. 4*84d9c625SLionel Sambuc * 5*84d9c625SLionel Sambuc * This code is derived from software contributed to The NetBSD Foundation 6*84d9c625SLionel Sambuc * by Matt Thomas of 3am Software Foundry. 7*84d9c625SLionel Sambuc * 8*84d9c625SLionel Sambuc * Redistribution and use in source and binary forms, with or without 9*84d9c625SLionel Sambuc * modification, are permitted provided that the following conditions 10*84d9c625SLionel Sambuc * are met: 11*84d9c625SLionel Sambuc * 1. Redistributions of source code must retain the above copyright 12*84d9c625SLionel Sambuc * notice, this list of conditions and the following disclaimer. 13*84d9c625SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright 14*84d9c625SLionel Sambuc * notice, this list of conditions and the following disclaimer in the 15*84d9c625SLionel Sambuc * documentation and/or other materials provided with the distribution. 16*84d9c625SLionel Sambuc * 17*84d9c625SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18*84d9c625SLionel Sambuc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19*84d9c625SLionel Sambuc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20*84d9c625SLionel Sambuc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21*84d9c625SLionel Sambuc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22*84d9c625SLionel Sambuc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23*84d9c625SLionel Sambuc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24*84d9c625SLionel Sambuc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25*84d9c625SLionel Sambuc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26*84d9c625SLionel Sambuc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27*84d9c625SLionel Sambuc * POSSIBILITY OF SUCH DAMAGE. 28*84d9c625SLionel Sambuc */ 29*84d9c625SLionel Sambuc 30*84d9c625SLionel Sambuc#include <machine/asm.h> 31*84d9c625SLionel Sambuc 32*84d9c625SLionel SambucRCSID("$NetBSD: strlen_arm.S,v 1.8 2013/09/05 05:15:47 matt Exp $") 33*84d9c625SLionel Sambuc 34*84d9c625SLionel Sambuc#if defined(__thumb__) && !defined(_ARM_ARCH_T2) 35*84d9c625SLionel Sambuc#error Only Thumb2 or ARM supported 36*84d9c625SLionel Sambuc#endif 37*84d9c625SLionel Sambuc 38*84d9c625SLionel Sambuc#ifdef __ARMEL__ 39*84d9c625SLionel Sambuc#define BYTE0 0x000000ff 40*84d9c625SLionel Sambuc#define BYTE1 0x0000ff00 41*84d9c625SLionel Sambuc#define BYTE2 0x00ff0000 42*84d9c625SLionel Sambuc#define BYTE3 0xff000000 43*84d9c625SLionel Sambuc#else 44*84d9c625SLionel Sambuc#define BYTE0 0xff000000 45*84d9c625SLionel Sambuc#define BYTE1 0x00ff0000 46*84d9c625SLionel Sambuc#define BYTE2 0x0000ff00 47*84d9c625SLionel Sambuc#define BYTE3 0x000000ff 48*84d9c625SLionel Sambuc#endif 49*84d9c625SLionel Sambuc 50*84d9c625SLionel Sambuc#ifdef STRNLEN 51*84d9c625SLionel Sambuc#define FUNCNAME strnlen 52*84d9c625SLionel Sambuc#else 53*84d9c625SLionel Sambuc#define FUNCNAME strlen 54*84d9c625SLionel Sambuc#endif 55*84d9c625SLionel Sambuc 56*84d9c625SLionel Sambuc .text 57*84d9c625SLionel SambucENTRY(FUNCNAME) 58*84d9c625SLionel Sambuc#if defined(__ARM_EABI__) && defined(__UNWIND_TABLES__) 59*84d9c625SLionel Sambuc .fnstart 60*84d9c625SLionel Sambuc .cfi_startproc 61*84d9c625SLionel Sambuc#endif 62*84d9c625SLionel Sambuc#ifdef STRNLEN 63*84d9c625SLionel Sambuc push {r4,r5} /* save some registers */ 64*84d9c625SLionel Sambuc#if defined(__ARM_EABI__) && defined(__UNWIND_TABLES__) 65*84d9c625SLionel Sambuc .save {r4,r5} 66*84d9c625SLionel Sambuc .cfi_def_cfa_offset 8 67*84d9c625SLionel Sambuc .cfi_offset 5, -4 68*84d9c625SLionel Sambuc .cfi_offset 4, -8 69*84d9c625SLionel Sambuc#endif 70*84d9c625SLionel Sambuc adds r5, r0, r1 /* get ptr to end of string */ 71*84d9c625SLionel Sambuc mov r4, r1 /* save maxlen */ 72*84d9c625SLionel Sambuc#endif 73*84d9c625SLionel Sambuc adds r2, r0, #4 /* for the final post-inc */ 74*84d9c625SLionel Sambuc1: tst r0, #3 /* test for word alignment */ 75*84d9c625SLionel Sambuc beq .Lpre_main_loop /* finally word aligned */ 76*84d9c625SLionel Sambuc#ifdef STRNLEN 77*84d9c625SLionel Sambuc cmp r0, r5 /* have we gone too far? */ 78*84d9c625SLionel Sambuc beq .Lmaxed_out /* yes, return maxlen */ 79*84d9c625SLionel Sambuc#endif 80*84d9c625SLionel Sambuc ldrb r3, [r0], #1 /* load a byte */ 81*84d9c625SLionel Sambuc cmp r3, #0 /* is it 0? */ 82*84d9c625SLionel Sambuc bne 1b /* no, try next byte */ 83*84d9c625SLionel Sambuc subs r2, r2, #3 /* subtract (4 - the NUL) */ 84*84d9c625SLionel Sambuc subs r0, r0, r2 /* subtract start */ 85*84d9c625SLionel Sambuc#ifdef STRNLEN 86*84d9c625SLionel Sambuc pop {r4, r5} /* restore registers */ 87*84d9c625SLionel Sambuc#endif 88*84d9c625SLionel Sambuc RET /* return */ 89*84d9c625SLionel Sambuc.Lpre_main_loop: 90*84d9c625SLionel Sambuc#if defined(_ARM_ARCH_7) 91*84d9c625SLionel Sambuc movw r1, #0xfefe /* magic constant; 254 in each byte */ 92*84d9c625SLionel Sambuc movt r1, #0xfefe /* magic constant; 254 in each byte */ 93*84d9c625SLionel Sambuc#elif defined(_ARM_ARCH_6) 94*84d9c625SLionel Sambuc mov r1, #0xfe /* put 254 in low byte */ 95*84d9c625SLionel Sambuc orr r1, r1, r1, lsl #8 /* move to next byte */ 96*84d9c625SLionel Sambuc orr r1, r1, r1, lsl #16 /* move to next halfword */ 97*84d9c625SLionel Sambuc#endif /* _ARM_ARCH_6 */ 98*84d9c625SLionel Sambuc.Lmain_loop: 99*84d9c625SLionel Sambuc#ifdef STRNLEN 100*84d9c625SLionel Sambuc cmp r0, r5 /* gone too far? */ 101*84d9c625SLionel Sambuc bge .Lmaxed_out /* yes, return maxlen */ 102*84d9c625SLionel Sambuc#endif 103*84d9c625SLionel Sambuc ldr r3, [r0], #4 /* load next word */ 104*84d9c625SLionel Sambuc#if defined(_ARM_ARCH_6) 105*84d9c625SLionel Sambuc /* 106*84d9c625SLionel Sambuc * Add 254 to each byte using the UQADD8 (unsigned saturating add 8) 107*84d9c625SLionel Sambuc * instruction. For every non-NUL byte, the result for that byte will 108*84d9c625SLionel Sambuc * become 255. For NUL, it will be 254. When we complement the 109*84d9c625SLionel Sambuc * result, if the result is non-0 then we must have encountered a NUL. 110*84d9c625SLionel Sambuc */ 111*84d9c625SLionel Sambuc uqadd8 r3, r3, r1 /* magic happens here */ 112*84d9c625SLionel Sambuc mvns r3, r3 /* is the complemented result non-0? */ 113*84d9c625SLionel Sambuc beq .Lmain_loop /* no, then we encountered no NULs */ 114*84d9c625SLionel Sambuc#else 115*84d9c625SLionel Sambuc /* 116*84d9c625SLionel Sambuc * No fancy shortcuts so just test each byte lane for a NUL. 117*84d9c625SLionel Sambuc * (other tests for NULs in a word take more instructions/cycles). 118*84d9c625SLionel Sambuc */ 119*84d9c625SLionel Sambuc tst r3, #BYTE0 /* is this byte 0? */ 120*84d9c625SLionel Sambuc tstne r3, #BYTE1 /* no, is this byte 0? */ 121*84d9c625SLionel Sambuc tstne r3, #BYTE2 /* no, is this byte 0? */ 122*84d9c625SLionel Sambuc tstne r3, #BYTE3 /* no, is this byte 0? */ 123*84d9c625SLionel Sambuc bne .Lmain_loop /* no, then get next word */ 124*84d9c625SLionel Sambuc#endif 125*84d9c625SLionel Sambuc#if defined(_ARM_ARCH_6) 126*84d9c625SLionel Sambuc /* 127*84d9c625SLionel Sambuc * We encountered a NUL. Find out where by doing a CLZ and then 128*84d9c625SLionel Sambuc * shifting right by 3. That will be the number of non-NUL bytes. 129*84d9c625SLionel Sambuc */ 130*84d9c625SLionel Sambuc#ifdef __ARMEL__ 131*84d9c625SLionel Sambuc rev r3, r3 /* we want this in BE for the CLZ */ 132*84d9c625SLionel Sambuc#endif 133*84d9c625SLionel Sambuc clz r3, r3 /* count how many leading zeros */ 134*84d9c625SLionel Sambuc#ifdef __thumb__ 135*84d9c625SLionel Sambuc lsrs r3, r3, #3 136*84d9c625SLionel Sambuc adds r0, r0, r3 /* divide that by 8 and add to count */ 137*84d9c625SLionel Sambuc#else 138*84d9c625SLionel Sambuc add r0, r0, r3, lsr #3 /* divide that by 8 and add to count */ 139*84d9c625SLionel Sambuc#endif 140*84d9c625SLionel Sambuc#else 141*84d9c625SLionel Sambuc /* 142*84d9c625SLionel Sambuc * We encountered a NUL. 143*84d9c625SLionel Sambuc */ 144*84d9c625SLionel Sambuc tst r3, #BYTE0 /* 1st byte was NUL? */ 145*84d9c625SLionel Sambuc beq 1f /* yes, done adding */ 146*84d9c625SLionel Sambuc add r0, r0, #1 /* we have one more non-NUL byte */ 147*84d9c625SLionel Sambuc tst r3, #BYTE1 /* 2nd byte was NUL? */ 148*84d9c625SLionel Sambuc beq 1f /* yes, done adding */ 149*84d9c625SLionel Sambuc add r0, r0, #1 /* we have one more non-NUL byte */ 150*84d9c625SLionel Sambuc tst r3, #BYTE2 /* 3rd byte was NUL? */ 151*84d9c625SLionel Sambuc addne r0, r0, #1 /* no, we have one more non-NUL byte */ 152*84d9c625SLionel Sambuc1: 153*84d9c625SLionel Sambuc#endif /* _ARM_ARCH_6 */ 154*84d9c625SLionel Sambuc /* 155*84d9c625SLionel Sambuc * r0 now points to 4 past the NUL due to the post-inc. Subtract the 156*84d9c625SLionel Sambuc * start of the string (which also has 4 added to it to compensate for 157*84d9c625SLionel Sambuc * the post-inc. 158*84d9c625SLionel Sambuc */ 159*84d9c625SLionel Sambuc subs r0, r0, r2 /* subtract start to get length */ 160*84d9c625SLionel Sambuc#ifdef STRNLEN 161*84d9c625SLionel Sambuc cmp r0, r4 /* is it larger than maxlen? */ 162*84d9c625SLionel Sambuc#ifdef __thumb__ 163*84d9c625SLionel Sambuc it gt 164*84d9c625SLionel Sambuc#endif 165*84d9c625SLionel Sambuc movgt r0, r4 /* yes, return maxlen */ 166*84d9c625SLionel Sambuc pop {r4, r5} /* restore registers */ 167*84d9c625SLionel Sambuc#endif 168*84d9c625SLionel Sambuc RET /* return */ 169*84d9c625SLionel Sambuc 170*84d9c625SLionel Sambuc#ifdef STRNLEN 171*84d9c625SLionel Sambuc.Lmaxed_out: 172*84d9c625SLionel Sambuc mov r0, r4 /* return maxlen */ 173*84d9c625SLionel Sambuc pop {r4, r5} /* restore registers */ 174*84d9c625SLionel Sambuc RET /* return */ 175*84d9c625SLionel Sambuc#endif 176*84d9c625SLionel Sambuc#if defined(__ARM_EABI__) && defined(__UNWIND_TABLES__) 177*84d9c625SLionel Sambuc .cfi_endproc 178*84d9c625SLionel Sambuc .fnend 179*84d9c625SLionel Sambuc#endif 180*84d9c625SLionel SambucEND(FUNCNAME) 181