root/arch/alpha/lib/stxcpy.S

/* [<][>][^][v][top][bottom][index][help] */
   1 /* SPDX-License-Identifier: GPL-2.0 */
   2 /*
   3  * arch/alpha/lib/stxcpy.S
   4  * Contributed by Richard Henderson (rth@tamu.edu)
   5  *
   6  * Copy a null-terminated string from SRC to DST.
   7  *
   8  * This is an internal routine used by strcpy, stpcpy, and strcat.
   9  * As such, it uses special linkage conventions to make implementation
  10  * of these public functions more efficient.
  11  *
  12  * On input:
  13  *      t9 = return address
  14  *      a0 = DST
  15  *      a1 = SRC
  16  *
  17  * On output:
  18  *      t12 = bitmask (with one bit set) indicating the last byte written
  19  *      a0  = unaligned address of the last *word* written
  20  *
  21  * Furthermore, v0, a3-a5, t11, and t12 are untouched.
  22  */
  23 
  24 #include <asm/regdef.h>
  25 
  26         .set noat
  27         .set noreorder
  28 
  29         .text
  30 
  31 /* There is a problem with either gdb (as of 4.16) or gas (as of 2.7) that
  32    doesn't like putting the entry point for a procedure somewhere in the
  33    middle of the procedure descriptor.  Work around this by putting the
  34    aligned copy in its own procedure descriptor */
  35 
  36         .ent stxcpy_aligned
  37         .align 3
  38 stxcpy_aligned:
  39         .frame sp, 0, t9
  40         .prologue 0
  41 
  42         /* On entry to this basic block:
  43            t0 == the first destination word for masking back in
  44            t1 == the first source word.  */
  45 
  46         /* Create the 1st output word and detect 0's in the 1st input word.  */
  47         lda     t2, -1          # e1    : build a mask against false zero
  48         mskqh   t2, a1, t2      # e0    :   detection in the src word
  49         mskqh   t1, a1, t3      # e0    :
  50         ornot   t1, t2, t2      # .. e1 :
  51         mskql   t0, a1, t0      # e0    : assemble the first output word
  52         cmpbge  zero, t2, t8    # .. e1 : bits set iff null found
  53         or      t0, t3, t1      # e0    :
  54         bne     t8, $a_eos      # .. e1 :
  55 
  56         /* On entry to this basic block:
  57            t0 == the first destination word for masking back in
  58            t1 == a source word not containing a null.  */
  59 
  60 $a_loop:
  61         stq_u   t1, 0(a0)       # e0    :
  62         addq    a0, 8, a0       # .. e1 :
  63         ldq_u   t1, 0(a1)       # e0    :
  64         addq    a1, 8, a1       # .. e1 :
  65         cmpbge  zero, t1, t8    # e0 (stall)
  66         beq     t8, $a_loop     # .. e1 (zdb)
  67 
  68         /* Take care of the final (partial) word store.
  69            On entry to this basic block we have:
  70            t1 == the source word containing the null
  71            t8 == the cmpbge mask that found it.  */
  72 $a_eos:
  73         negq    t8, t6          # e0    : find low bit set
  74         and     t8, t6, t12     # e1 (stall)
  75 
  76         /* For the sake of the cache, don't read a destination word
  77            if we're not going to need it.  */
  78         and     t12, 0x80, t6   # e0    :
  79         bne     t6, 1f          # .. e1 (zdb)
  80 
  81         /* We're doing a partial word store and so need to combine
  82            our source and original destination words.  */
  83         ldq_u   t0, 0(a0)       # e0    :
  84         subq    t12, 1, t6      # .. e1 :
  85         zapnot  t1, t6, t1      # e0    : clear src bytes >= null
  86         or      t12, t6, t8     # .. e1 :
  87         zap     t0, t8, t0      # e0    : clear dst bytes <= null
  88         or      t0, t1, t1      # e1    :
  89 
  90 1:      stq_u   t1, 0(a0)       # e0    :
  91         ret     (t9)            # .. e1 :
  92 
  93         .end stxcpy_aligned
  94 
  95         .align 3
  96         .ent __stxcpy
  97         .globl __stxcpy
  98 __stxcpy:
  99         .frame sp, 0, t9
 100         .prologue 0
 101 
 102         /* Are source and destination co-aligned?  */
 103         xor     a0, a1, t0      # e0    :
 104         unop                    #       :
 105         and     t0, 7, t0       # e0    :
 106         bne     t0, $unaligned  # .. e1 :
 107 
 108         /* We are co-aligned; take care of a partial first word.  */
 109         ldq_u   t1, 0(a1)       # e0    : load first src word
 110         and     a0, 7, t0       # .. e1 : take care not to load a word ...
 111         addq    a1, 8, a1               # e0    :
 112         beq     t0, stxcpy_aligned      # .. e1 : ... if we wont need it
 113         ldq_u   t0, 0(a0)       # e0    :
 114         br      stxcpy_aligned  # .. e1 :
 115 
 116 
 117 /* The source and destination are not co-aligned.  Align the destination
 118    and cope.  We have to be very careful about not reading too much and
 119    causing a SEGV.  */
 120 
 121         .align 3
 122 $u_head:
 123         /* We know just enough now to be able to assemble the first
 124            full source word.  We can still find a zero at the end of it
 125            that prevents us from outputting the whole thing.
 126 
 127            On entry to this basic block:
 128            t0 == the first dest word, for masking back in, if needed else 0
 129            t1 == the low bits of the first source word
 130            t6 == bytemask that is -1 in dest word bytes */
 131 
 132         ldq_u   t2, 8(a1)       # e0    :
 133         addq    a1, 8, a1       # .. e1 :
 134 
 135         extql   t1, a1, t1      # e0    :
 136         extqh   t2, a1, t4      # e0    :
 137         mskql   t0, a0, t0      # e0    :
 138         or      t1, t4, t1      # .. e1 :
 139         mskqh   t1, a0, t1      # e0    :
 140         or      t0, t1, t1      # e1    :
 141 
 142         or      t1, t6, t6      # e0    :
 143         cmpbge  zero, t6, t8    # .. e1 :
 144         lda     t6, -1          # e0    : for masking just below
 145         bne     t8, $u_final    # .. e1 :
 146 
 147         mskql   t6, a1, t6              # e0    : mask out the bits we have
 148         or      t6, t2, t2              # e1    :   already extracted before
 149         cmpbge  zero, t2, t8            # e0    :   testing eos
 150         bne     t8, $u_late_head_exit   # .. e1 (zdb)
 151 
 152         /* Finally, we've got all the stupid leading edge cases taken care
 153            of and we can set up to enter the main loop.  */
 154 
 155         stq_u   t1, 0(a0)       # e0    : store first output word
 156         addq    a0, 8, a0       # .. e1 :
 157         extql   t2, a1, t0      # e0    : position ho-bits of lo word
 158         ldq_u   t2, 8(a1)       # .. e1 : read next high-order source word
 159         addq    a1, 8, a1       # e0    :
 160         cmpbge  zero, t2, t8    # .. e1 :
 161         nop                     # e0    :
 162         bne     t8, $u_eos      # .. e1 :
 163 
 164         /* Unaligned copy main loop.  In order to avoid reading too much,
 165            the loop is structured to detect zeros in aligned source words.
 166            This has, unfortunately, effectively pulled half of a loop
 167            iteration out into the head and half into the tail, but it does
 168            prevent nastiness from accumulating in the very thing we want
 169            to run as fast as possible.
 170 
 171            On entry to this basic block:
 172            t0 == the shifted high-order bits from the previous source word
 173            t2 == the unshifted current source word
 174 
 175            We further know that t2 does not contain a null terminator.  */
 176 
 177         .align 3
 178 $u_loop:
 179         extqh   t2, a1, t1      # e0    : extract high bits for current word
 180         addq    a1, 8, a1       # .. e1 :
 181         extql   t2, a1, t3      # e0    : extract low bits for next time
 182         addq    a0, 8, a0       # .. e1 :
 183         or      t0, t1, t1      # e0    : current dst word now complete
 184         ldq_u   t2, 0(a1)       # .. e1 : load high word for next time
 185         stq_u   t1, -8(a0)      # e0    : save the current word
 186         mov     t3, t0          # .. e1 :
 187         cmpbge  zero, t2, t8    # e0    : test new word for eos
 188         beq     t8, $u_loop     # .. e1 :
 189 
 190         /* We've found a zero somewhere in the source word we just read.
 191            If it resides in the lower half, we have one (probably partial)
 192            word to write out, and if it resides in the upper half, we
 193            have one full and one partial word left to write out.
 194 
 195            On entry to this basic block:
 196            t0 == the shifted high-order bits from the previous source word
 197            t2 == the unshifted current source word.  */
 198 $u_eos:
 199         extqh   t2, a1, t1      # e0    :
 200         or      t0, t1, t1      # e1    : first (partial) source word complete
 201 
 202         cmpbge  zero, t1, t8    # e0    : is the null in this first bit?
 203         bne     t8, $u_final    # .. e1 (zdb)
 204 
 205 $u_late_head_exit:
 206         stq_u   t1, 0(a0)       # e0    : the null was in the high-order bits
 207         addq    a0, 8, a0       # .. e1 :
 208         extql   t2, a1, t1      # e0    :
 209         cmpbge  zero, t1, t8    # .. e1 :
 210 
 211         /* Take care of a final (probably partial) result word.
 212            On entry to this basic block:
 213            t1 == assembled source word
 214            t8 == cmpbge mask that found the null.  */
 215 $u_final:
 216         negq    t8, t6          # e0    : isolate low bit set
 217         and     t6, t8, t12     # e1    :
 218 
 219         and     t12, 0x80, t6   # e0    : avoid dest word load if we can
 220         bne     t6, 1f          # .. e1 (zdb)
 221 
 222         ldq_u   t0, 0(a0)       # e0    :
 223         subq    t12, 1, t6      # .. e1 :
 224         or      t6, t12, t8     # e0    :
 225         zapnot  t1, t6, t1      # .. e1 : kill source bytes >= null
 226         zap     t0, t8, t0      # e0    : kill dest bytes <= null
 227         or      t0, t1, t1      # e1    :
 228 
 229 1:      stq_u   t1, 0(a0)       # e0    :
 230         ret     (t9)            # .. e1 :
 231 
 232         /* Unaligned copy entry point.  */
 233         .align 3
 234 $unaligned:
 235 
 236         ldq_u   t1, 0(a1)       # e0    : load first source word
 237 
 238         and     a0, 7, t4       # .. e1 : find dest misalignment
 239         and     a1, 7, t5       # e0    : find src misalignment
 240 
 241         /* Conditionally load the first destination word and a bytemask
 242            with 0xff indicating that the destination byte is sacrosanct.  */
 243 
 244         mov     zero, t0        # .. e1 :
 245         mov     zero, t6        # e0    :
 246         beq     t4, 1f          # .. e1 :
 247         ldq_u   t0, 0(a0)       # e0    :
 248         lda     t6, -1          # .. e1 :
 249         mskql   t6, a0, t6      # e0    :
 250 1:
 251         subq    a1, t4, a1      # .. e1 : sub dest misalignment from src addr
 252 
 253         /* If source misalignment is larger than dest misalignment, we need
 254            extra startup checks to avoid SEGV.  */
 255 
 256         cmplt   t4, t5, t12     # e0    :
 257         beq     t12, $u_head    # .. e1 (zdb)
 258 
 259         lda     t2, -1          # e1    : mask out leading garbage in source
 260         mskqh   t2, t5, t2      # e0    :
 261         nop                     # e0    :
 262         ornot   t1, t2, t3      # .. e1 :
 263         cmpbge  zero, t3, t8    # e0    : is there a zero?
 264         beq     t8, $u_head     # .. e1 (zdb)
 265 
 266         /* At this point we've found a zero in the first partial word of
 267            the source.  We need to isolate the valid source data and mask
 268            it into the original destination data.  (Incidentally, we know
 269            that we'll need at least one byte of that original dest word.) */
 270 
 271         ldq_u   t0, 0(a0)       # e0    :
 272 
 273         negq    t8, t6          # .. e1 : build bitmask of bytes <= zero
 274         and     t6, t8, t12     # e0    :
 275         and     a1, 7, t5       # .. e1 :
 276         subq    t12, 1, t6      # e0    :
 277         or      t6, t12, t8     # e1    :
 278         srl     t12, t5, t12    # e0    : adjust final null return value
 279 
 280         zapnot  t2, t8, t2      # .. e1 : prepare source word; mirror changes
 281         and     t1, t2, t1      # e1    : to source validity mask
 282         extql   t2, a1, t2      # .. e0 :
 283         extql   t1, a1, t1      # e0    :
 284 
 285         andnot  t0, t2, t0      # .. e1 : zero place for source to reside
 286         or      t0, t1, t1      # e1    : and put it there
 287         stq_u   t1, 0(a0)       # .. e0 :
 288         ret     (t9)            # e1    :
 289 
 290         .end __stxcpy

/* [<][>][^][v][top][bottom][index][help] */