Retro68/gcc/newlib/libc/machine/sh/strncpy.S
2012-03-27 01:51:53 +02:00

210 lines
4.8 KiB
ArmAsm

/* Copyright 2003 SuperH Ltd. */
#include "asm.h"
#ifdef __SH5__
#if __SHMEDIA__
#ifdef __LITTLE_ENDIAN__
#define ZPAD_MASK(src, dst) addi src, -1, dst
#else
#define ZPAD_MASK(src, dst) \
byterev src, dst; addi dst, -1, dst; byterev dst, dst
#endif
/* We assume that the destination is not in the first 16 bytes of memory.
A typical linker script will put the text section first, and as
this code is longer that 16 bytes, you have to get out of your way
to put data there. */
ENTRY(strncpy)
pt L_small, tr2
ldlo.q r3, 0, r0
shlli r3, 3, r19
mcmpeq.b r0, r63, r1
SHHI r1, r19, r7
add r2, r4, r20
addi r20, -8, r5
/* If the size is greater than 8, we know we can read beyond the first
(possibly partial) quadword, and write out a full first and last
(possibly unaligned and/or overlapping) quadword. */
bge/u r2, r5, tr2 // L_small
pt L_found0, tr0
addi r2, 8, r22
bnei/u r7, 0, tr0 // L_found0
ori r3, -8, r38
pt L_end_early, tr1
sub r2, r38, r22
stlo.q r2, 0, r0
sthi.q r2, 7, r0
sub r3, r2, r6
ldx.q r22, r6, r0
/* Before each iteration, check that we can store in full the next quad we
are about to fetch. */
addi r5, -8, r36
bgtu/u r22, r36, tr1 // L_end_early
pt L_scan0, tr1
L_scan0:
addi r22, 8, r22
mcmpeq.b r0, r63, r1
stlo.q r22, -8, r0
bnei/u r1, 0, tr0 // L_found0
sthi.q r22, -1, r0
ldx.q r22, r6, r0
bgeu/l r36, r22, tr1 // L_scan0
L_end:
// At end; we might re-read a few bytes when we fetch the last quad.
// branch mispredict, so load is ready now.
mcmpeq.b r0, r63, r1
addi r22, 8, r22
bnei/u r1, 0, tr0 // L_found0
add r3, r4, r7
ldlo.q r7, -8, r1
ldhi.q r7, -1, r7
ptabs r18, tr0
stlo.q r22, -8, r0
or r1, r7, r1
mcmpeq.b r1, r63, r7
sthi.q r22, -1, r0
ZPAD_MASK (r7, r7)
and r1, r7, r1 // mask out non-zero bytes after first zero byte
stlo.q r20, -8, r1
sthi.q r20, -1, r1
blink tr0, r63
L_end_early:
/* Check if we can store the current quad in full. */
pt L_end, tr1
add r3, r4, r7
bgtu/u r5, r22, tr1 // L_end // Not really unlikely, but gap is short.
/* If not, that means we can just proceed to process the last quad.
Two pipeline stalls are unavoidable, as we don't have enough ILP. */
ldlo.q r7, -8, r1
ldhi.q r7, -1, r7
ptabs r18, tr0
or r1, r7, r1
mcmpeq.b r1, r63, r7
ZPAD_MASK (r7, r7)
and r1, r7, r1 // mask out non-zero bytes after first zero byte
stlo.q r20, -8, r1
sthi.q r20, -1, r1
blink tr0, r63
L_found0:
// r0: string to store, not yet zero-padding normalized.
// r1: result of mcmpeq.b r0, r63, r1.
// r22: store address plus 8. I.e. address where zero padding beyond the
// string in r0 goes.
// r20: store end address.
// r5: store end address minus 8.
pt L_write0_multiquad, tr0
ZPAD_MASK (r1, r1)
and r0, r1, r0 // mask out non-zero bytes after first zero byte
stlo.q r22, -8, r0
sthi.q r22, -1, r0
andi r22, -8, r1 // Check if zeros to write fit in one quad word.
bgtu/l r5, r1, tr0 // L_write0_multiquad
ptabs r18, tr1
sub r20, r22, r1
shlli r1, 2, r1 // Do shift in two steps so that 64 bit case is
SHLO r0, r1, r0 // handled correctly.
SHLO r0, r1, r0
sthi.q r20, -1, r0
blink tr1, r63
L_write0_multiquad:
pt L_write0_loop, tr0
ptabs r18, tr1
stlo.q r22, 0, r63
sthi.q r20, -1, r63
addi r1, 8, r1
bgeu/l r5, r1, tr0 // L_write0_loop
blink tr1, r63
L_write0_loop:
st.q r1, 0 ,r63
addi r1, 8, r1
bgeu/l r5, r1, tr0 // L_write0_loop
blink tr1, r63
L_small:
// r0: string to store, not yet zero-padding normalized.
// r1: result of mcmpeq.b r0, r63, r1.
// r7: nonzero indicates relevant zero found r0.
// r2: store address.
// r3: read address.
// r4: size, max 8
// r20: store end address.
// r5: store end address minus 8.
pt L_nohi, tr0
pt L_small_storelong, tr1
ptabs r18, tr2
sub r63, r4, r23
bnei/u r7, 0, tr0 // L_nohi
ori r3, -8, r7
bge/l r23, r7, tr0 // L_nohi
ldhi.q r3, 7, r1
or r0, r1, r0
mcmpeq.b r0, r63, r1
L_nohi:
ZPAD_MASK (r1, r1)
and r0, r1, r0
movi 4, r19
bge/u r4, r19, tr1 // L_small_storelong
pt L_small_end, tr0
#ifndef __LITTLE_ENDIAN__
byterev r0, r0
#endif
beqi/u r4, 0, tr0 // L_small_end
st.b r2, 0, r0
beqi/u r4, 1, tr0 // L_small_end
shlri r0, 8, r0
st.b r2, 1, r0
beqi/u r4, 2, tr0 // L_small_end
shlri r0, 8, r0
st.b r2, 2, r0
L_small_end:
blink tr2, r63
L_small_storelong:
shlli r23, 3, r7
SHHI r0, r7, r1
#ifdef __LITTLE_ENDIAN__
shlri r1, 32, r1
#else
shlri r0, 32, r0
#endif
stlo.l r2, 0, r0
sthi.l r2, 3, r0
stlo.l r20, -4, r1
sthi.l r20, -1, r1
blink tr2, r63
#else /* SHcompact */
/* This code is optimized for size. Instruction selection is SH5 specific.
SH4 should use a different version. */
ENTRY(strncpy)
mov #0, r6
cmp/eq r4, r6
bt return
mov r2, r5
add #-1, r5
add r5, r4
loop:
bt/s found0
add #1, r5
mov.b @r3+, r1
found0:
cmp/eq r5,r4
mov.b r1, @r5
bf/s loop
cmp/eq r1, r6
return:
rts
nop
#endif /* SHcompact */
#endif /* __SH5__ */