184d9c625SLionel Sambuc/*- 284d9c625SLionel Sambuc * Copyright (c) 2012 The NetBSD Foundation, Inc. 384d9c625SLionel Sambuc * All rights reserved. 484d9c625SLionel Sambuc * 584d9c625SLionel Sambuc * This code is derived from software contributed to The NetBSD Foundation 684d9c625SLionel Sambuc * by Matt Thomas of 3am Software Foundry. 784d9c625SLionel Sambuc * 884d9c625SLionel Sambuc * Redistribution and use in source and binary forms, with or without 984d9c625SLionel Sambuc * modification, are permitted provided that the following conditions 1084d9c625SLionel Sambuc * are met: 1184d9c625SLionel Sambuc * 1. Redistributions of source code must retain the above copyright 1284d9c625SLionel Sambuc * notice, this list of conditions and the following disclaimer. 1384d9c625SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright 1484d9c625SLionel Sambuc * notice, this list of conditions and the following disclaimer in the 1584d9c625SLionel Sambuc * documentation and/or other materials provided with the distribution. 1684d9c625SLionel Sambuc * 1784d9c625SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 1884d9c625SLionel Sambuc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 1984d9c625SLionel Sambuc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2084d9c625SLionel Sambuc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2184d9c625SLionel Sambuc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2284d9c625SLionel Sambuc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2384d9c625SLionel Sambuc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2484d9c625SLionel Sambuc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2584d9c625SLionel Sambuc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2684d9c625SLionel Sambuc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2784d9c625SLionel Sambuc * POSSIBILITY OF SUCH DAMAGE. 2884d9c625SLionel Sambuc */ 2984d9c625SLionel Sambuc 3084d9c625SLionel Sambuc#include <machine/asm.h> 3184d9c625SLionel Sambuc 32*0a6a1f1dSLionel SambucRCSID("$NetBSD: strlen_arm.S,v 1.9 2014/05/06 16:02:11 joerg Exp $") 3384d9c625SLionel Sambuc 3484d9c625SLionel Sambuc#if defined(__thumb__) && !defined(_ARM_ARCH_T2) 3584d9c625SLionel Sambuc#error Only Thumb2 or ARM supported 3684d9c625SLionel Sambuc#endif 3784d9c625SLionel Sambuc 3884d9c625SLionel Sambuc#ifdef __ARMEL__ 3984d9c625SLionel Sambuc#define BYTE0 0x000000ff 4084d9c625SLionel Sambuc#define BYTE1 0x0000ff00 4184d9c625SLionel Sambuc#define BYTE2 0x00ff0000 4284d9c625SLionel Sambuc#define BYTE3 0xff000000 4384d9c625SLionel Sambuc#else 4484d9c625SLionel Sambuc#define BYTE0 0xff000000 4584d9c625SLionel Sambuc#define BYTE1 0x00ff0000 4684d9c625SLionel Sambuc#define BYTE2 0x0000ff00 4784d9c625SLionel Sambuc#define BYTE3 0x000000ff 4884d9c625SLionel Sambuc#endif 4984d9c625SLionel Sambuc 5084d9c625SLionel Sambuc#ifdef STRNLEN 5184d9c625SLionel Sambuc#define FUNCNAME strnlen 5284d9c625SLionel Sambuc#else 5384d9c625SLionel Sambuc#define FUNCNAME strlen 5484d9c625SLionel Sambuc#endif 5584d9c625SLionel Sambuc 5684d9c625SLionel Sambuc .text 5784d9c625SLionel SambucENTRY(FUNCNAME) 5884d9c625SLionel Sambuc#if defined(__ARM_EABI__) && defined(__UNWIND_TABLES__) 59*0a6a1f1dSLionel Sambuc# if !defined(__ARM_DWARF_EH__) 6084d9c625SLionel Sambuc .fnstart 61*0a6a1f1dSLionel Sambuc# endif 6284d9c625SLionel Sambuc .cfi_startproc 6384d9c625SLionel Sambuc#endif 6484d9c625SLionel Sambuc#ifdef STRNLEN 6584d9c625SLionel Sambuc push {r4,r5} /* save some registers */ 6684d9c625SLionel Sambuc#if defined(__ARM_EABI__) && defined(__UNWIND_TABLES__) 67*0a6a1f1dSLionel Sambuc# if !defined(__ARM_DWARF_EH__) 6884d9c625SLionel Sambuc .save {r4,r5} 69*0a6a1f1dSLionel Sambuc# endif 7084d9c625SLionel Sambuc .cfi_def_cfa_offset 8 7184d9c625SLionel Sambuc .cfi_offset 5, -4 7284d9c625SLionel Sambuc .cfi_offset 4, -8 7384d9c625SLionel Sambuc#endif 7484d9c625SLionel Sambuc adds r5, r0, r1 /* get ptr to end of string */ 7584d9c625SLionel Sambuc mov r4, r1 /* save maxlen */ 7684d9c625SLionel Sambuc#endif 7784d9c625SLionel Sambuc adds r2, r0, #4 /* for the final post-inc */ 7884d9c625SLionel Sambuc1: tst r0, #3 /* test for word alignment */ 7984d9c625SLionel Sambuc beq .Lpre_main_loop /* finally word aligned */ 8084d9c625SLionel Sambuc#ifdef STRNLEN 8184d9c625SLionel Sambuc cmp r0, r5 /* have we gone too far? */ 8284d9c625SLionel Sambuc beq .Lmaxed_out /* yes, return maxlen */ 8384d9c625SLionel Sambuc#endif 8484d9c625SLionel Sambuc ldrb r3, [r0], #1 /* load a byte */ 8584d9c625SLionel Sambuc cmp r3, #0 /* is it 0? */ 8684d9c625SLionel Sambuc bne 1b /* no, try next byte */ 8784d9c625SLionel Sambuc subs r2, r2, #3 /* subtract (4 - the NUL) */ 8884d9c625SLionel Sambuc subs r0, r0, r2 /* subtract start */ 8984d9c625SLionel Sambuc#ifdef STRNLEN 9084d9c625SLionel Sambuc pop {r4, r5} /* restore registers */ 9184d9c625SLionel Sambuc#endif 9284d9c625SLionel Sambuc RET /* return */ 9384d9c625SLionel Sambuc.Lpre_main_loop: 9484d9c625SLionel Sambuc#if defined(_ARM_ARCH_7) 9584d9c625SLionel Sambuc movw r1, #0xfefe /* magic constant; 254 in each byte */ 9684d9c625SLionel Sambuc movt r1, #0xfefe /* magic constant; 254 in each byte */ 9784d9c625SLionel Sambuc#elif defined(_ARM_ARCH_6) 9884d9c625SLionel Sambuc mov r1, #0xfe /* put 254 in low byte */ 9984d9c625SLionel Sambuc orr r1, r1, r1, lsl #8 /* move to next byte */ 10084d9c625SLionel Sambuc orr r1, r1, r1, lsl #16 /* move to next halfword */ 10184d9c625SLionel Sambuc#endif /* _ARM_ARCH_6 */ 10284d9c625SLionel Sambuc.Lmain_loop: 10384d9c625SLionel Sambuc#ifdef STRNLEN 10484d9c625SLionel Sambuc cmp r0, r5 /* gone too far? */ 10584d9c625SLionel Sambuc bge .Lmaxed_out /* yes, return maxlen */ 10684d9c625SLionel Sambuc#endif 10784d9c625SLionel Sambuc ldr r3, [r0], #4 /* load next word */ 10884d9c625SLionel Sambuc#if defined(_ARM_ARCH_6) 10984d9c625SLionel Sambuc /* 11084d9c625SLionel Sambuc * Add 254 to each byte using the UQADD8 (unsigned saturating add 8) 11184d9c625SLionel Sambuc * instruction. For every non-NUL byte, the result for that byte will 11284d9c625SLionel Sambuc * become 255. For NUL, it will be 254. When we complement the 11384d9c625SLionel Sambuc * result, if the result is non-0 then we must have encountered a NUL. 11484d9c625SLionel Sambuc */ 11584d9c625SLionel Sambuc uqadd8 r3, r3, r1 /* magic happens here */ 11684d9c625SLionel Sambuc mvns r3, r3 /* is the complemented result non-0? */ 11784d9c625SLionel Sambuc beq .Lmain_loop /* no, then we encountered no NULs */ 11884d9c625SLionel Sambuc#else 11984d9c625SLionel Sambuc /* 12084d9c625SLionel Sambuc * No fancy shortcuts so just test each byte lane for a NUL. 12184d9c625SLionel Sambuc * (other tests for NULs in a word take more instructions/cycles). 12284d9c625SLionel Sambuc */ 12384d9c625SLionel Sambuc tst r3, #BYTE0 /* is this byte 0? */ 12484d9c625SLionel Sambuc tstne r3, #BYTE1 /* no, is this byte 0? */ 12584d9c625SLionel Sambuc tstne r3, #BYTE2 /* no, is this byte 0? */ 12684d9c625SLionel Sambuc tstne r3, #BYTE3 /* no, is this byte 0? */ 12784d9c625SLionel Sambuc bne .Lmain_loop /* no, then get next word */ 12884d9c625SLionel Sambuc#endif 12984d9c625SLionel Sambuc#if defined(_ARM_ARCH_6) 13084d9c625SLionel Sambuc /* 13184d9c625SLionel Sambuc * We encountered a NUL. Find out where by doing a CLZ and then 13284d9c625SLionel Sambuc * shifting right by 3. That will be the number of non-NUL bytes. 13384d9c625SLionel Sambuc */ 13484d9c625SLionel Sambuc#ifdef __ARMEL__ 13584d9c625SLionel Sambuc rev r3, r3 /* we want this in BE for the CLZ */ 13684d9c625SLionel Sambuc#endif 13784d9c625SLionel Sambuc clz r3, r3 /* count how many leading zeros */ 13884d9c625SLionel Sambuc#ifdef __thumb__ 13984d9c625SLionel Sambuc lsrs r3, r3, #3 14084d9c625SLionel Sambuc adds r0, r0, r3 /* divide that by 8 and add to count */ 14184d9c625SLionel Sambuc#else 14284d9c625SLionel Sambuc add r0, r0, r3, lsr #3 /* divide that by 8 and add to count */ 14384d9c625SLionel Sambuc#endif 14484d9c625SLionel Sambuc#else 14584d9c625SLionel Sambuc /* 14684d9c625SLionel Sambuc * We encountered a NUL. 14784d9c625SLionel Sambuc */ 14884d9c625SLionel Sambuc tst r3, #BYTE0 /* 1st byte was NUL? */ 14984d9c625SLionel Sambuc beq 1f /* yes, done adding */ 15084d9c625SLionel Sambuc add r0, r0, #1 /* we have one more non-NUL byte */ 15184d9c625SLionel Sambuc tst r3, #BYTE1 /* 2nd byte was NUL? */ 15284d9c625SLionel Sambuc beq 1f /* yes, done adding */ 15384d9c625SLionel Sambuc add r0, r0, #1 /* we have one more non-NUL byte */ 15484d9c625SLionel Sambuc tst r3, #BYTE2 /* 3rd byte was NUL? */ 15584d9c625SLionel Sambuc addne r0, r0, #1 /* no, we have one more non-NUL byte */ 15684d9c625SLionel Sambuc1: 15784d9c625SLionel Sambuc#endif /* _ARM_ARCH_6 */ 15884d9c625SLionel Sambuc /* 15984d9c625SLionel Sambuc * r0 now points to 4 past the NUL due to the post-inc. Subtract the 16084d9c625SLionel Sambuc * start of the string (which also has 4 added to it to compensate for 16184d9c625SLionel Sambuc * the post-inc. 16284d9c625SLionel Sambuc */ 16384d9c625SLionel Sambuc subs r0, r0, r2 /* subtract start to get length */ 16484d9c625SLionel Sambuc#ifdef STRNLEN 16584d9c625SLionel Sambuc cmp r0, r4 /* is it larger than maxlen? */ 16684d9c625SLionel Sambuc#ifdef __thumb__ 16784d9c625SLionel Sambuc it gt 16884d9c625SLionel Sambuc#endif 16984d9c625SLionel Sambuc movgt r0, r4 /* yes, return maxlen */ 17084d9c625SLionel Sambuc pop {r4, r5} /* restore registers */ 17184d9c625SLionel Sambuc#endif 17284d9c625SLionel Sambuc RET /* return */ 17384d9c625SLionel Sambuc 17484d9c625SLionel Sambuc#ifdef STRNLEN 17584d9c625SLionel Sambuc.Lmaxed_out: 17684d9c625SLionel Sambuc mov r0, r4 /* return maxlen */ 17784d9c625SLionel Sambuc pop {r4, r5} /* restore registers */ 17884d9c625SLionel Sambuc RET /* return */ 17984d9c625SLionel Sambuc#endif 18084d9c625SLionel Sambuc#if defined(__ARM_EABI__) && defined(__UNWIND_TABLES__) 18184d9c625SLionel Sambuc .cfi_endproc 182*0a6a1f1dSLionel Sambuc# if !defined(__ARM_DWARF_EH__) 18384d9c625SLionel Sambuc .fnend 18484d9c625SLionel Sambuc# endif 185*0a6a1f1dSLionel Sambuc#endif 18684d9c625SLionel SambucEND(FUNCNAME) 187