| /* | |
| * arch/alpha/lib/ev67-strlen_user.S | |
| * 21264 version contributed by Rick Gorton <rick.gorton@api-networks.com> | |
| * | |
| * Return the length of the string including the NULL terminator | |
| * (strlen+1) or zero if an error occurred. | |
| * | |
| * In places where it is critical to limit the processing time, | |
| * and the data is not trusted, strnlen_user() should be used. | |
| * It will return a value greater than its second argument if | |
| * that limit would be exceeded. This implementation is allowed | |
| * to access memory beyond the limit, but will not cross a page | |
| * boundary when doing so. | |
| * | |
| * Much of the information about 21264 scheduling/coding comes from: | |
| * Compiler Writer's Guide for the Alpha 21264 | |
| * abbreviated as 'CWG' in other comments here | |
| * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | |
| * Scheduling notation: | |
| * E - either cluster | |
| * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | |
| * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | |
| * Try not to change the actual algorithm if possible for consistency. | |
| */ | |
| #include <asm/regdef.h> | |
| /* Allow an exception for an insn; exit if we get one. */ | |
| #define EX(x,y...) \ | |
| 99: x,##y; \ | |
| .section __ex_table,"a"; \ | |
| .long 99b - .; \ | |
| lda v0, $exception-99b(zero); \ | |
| .previous | |
| .set noreorder | |
| .set noat | |
| .text | |
| .globl __strlen_user | |
| .ent __strlen_user | |
| .frame sp, 0, ra | |
| .align 4 | |
| __strlen_user: | |
| ldah a1, 32767(zero) # do not use plain strlen_user() for strings | |
| # that might be almost 2 GB long; you should | |
| # be using strnlen_user() instead | |
| nop | |
| nop | |
| nop | |
| .globl __strnlen_user | |
| .align 4 | |
| __strnlen_user: | |
| .prologue 0 | |
| EX( ldq_u t0, 0(a0) ) # L : load first quadword (a0 may be misaligned) | |
| lda t1, -1(zero) # E : | |
| insqh t1, a0, t1 # U : | |
| andnot a0, 7, v0 # E : | |
| or t1, t0, t0 # E : | |
| subq a0, 1, a0 # E : get our +1 for the return | |
| cmpbge zero, t0, t1 # E : t1 <- bitmask: bit i == 1 <==> i-th byte == 0 | |
| subq a1, 7, t2 # E : | |
| subq a0, v0, t0 # E : | |
| bne t1, $found # U : | |
| addq t2, t0, t2 # E : | |
| addq a1, 1, a1 # E : | |
| nop # E : | |
| nop # E : | |
| .align 4 | |
| $loop: ble t2, $limit # U : | |
| EX( ldq t0, 8(v0) ) # L : | |
| nop # E : | |
| nop # E : | |
| cmpbge zero, t0, t1 # E : | |
| subq t2, 8, t2 # E : | |
| addq v0, 8, v0 # E : addr += 8 | |
| beq t1, $loop # U : | |
| $found: cttz t1, t2 # U0 : | |
| addq v0, t2, v0 # E : | |
| subq v0, a0, v0 # E : | |
| ret # L0 : | |
| $exception: | |
| nop | |
| nop | |
| nop | |
| ret | |
| .align 4 # currently redundant | |
| $limit: | |
| nop | |
| nop | |
| subq a1, t2, v0 | |
| ret | |
| .end __strlen_user |