2
* arch/alpha/lib/stxcpy.S
3
* Contributed by Richard Henderson (rth@tamu.edu)
5
* Copy a null-terminated string from SRC to DST.
7
* This is an internal routine used by strcpy, stpcpy, and strcat.
8
* As such, it uses special linkage conventions to make implementation
9
* of these public functions more efficient.
17
* t12 = bitmask (with one bit set) indicating the last byte written
18
* a0 = unaligned address of the last *word* written
20
* Furthermore, v0, a3-a5, t11, and t12 are untouched.
23
#include <asm/regdef.h>
30
/* There is a problem with either gdb (as of 4.16) or gas (as of 2.7) that
31
doesn't like putting the entry point for a procedure somewhere in the
32
middle of the procedure descriptor. Work around this by putting the
33
aligned copy in its own procedure descriptor */
41
/* On entry to this basic block:
42
t0 == the first destination word for masking back in
43
t1 == the first source word. */
45
/* Create the 1st output word and detect 0's in the 1st input word. */
46
lda t2, -1 # e1 : build a mask against false zero
47
mskqh t2, a1, t2 # e0 : detection in the src word
48
mskqh t1, a1, t3 # e0 :
49
ornot t1, t2, t2 # .. e1 :
50
mskql t0, a1, t0 # e0 : assemble the first output word
51
cmpbge zero, t2, t8 # .. e1 : bits set iff null found
53
bne t8, $a_eos # .. e1 :
55
/* On entry to this basic block:
56
t0 == the first destination word for masking back in
57
t1 == a source word not containing a null. */
60
stq_u t1, 0(a0) # e0 :
61
addq a0, 8, a0 # .. e1 :
62
ldq_u t1, 0(a1) # e0 :
63
addq a1, 8, a1 # .. e1 :
64
cmpbge zero, t1, t8 # e0 (stall)
65
beq t8, $a_loop # .. e1 (zdb)
67
/* Take care of the final (partial) word store.
68
On entry to this basic block we have:
69
t1 == the source word containing the null
70
t8 == the cmpbge mask that found it. */
72
negq t8, t6 # e0 : find low bit set
73
and t8, t6, t12 # e1 (stall)
75
/* For the sake of the cache, don't read a destination word
76
if we're not going to need it. */
77
and t12, 0x80, t6 # e0 :
78
bne t6, 1f # .. e1 (zdb)
80
/* We're doing a partial word store and so need to combine
81
our source and original destination words. */
82
ldq_u t0, 0(a0) # e0 :
83
subq t12, 1, t6 # .. e1 :
84
zapnot t1, t6, t1 # e0 : clear src bytes >= null
85
or t12, t6, t8 # .. e1 :
86
zap t0, t8, t0 # e0 : clear dst bytes <= null
89
1: stq_u t1, 0(a0) # e0 :
101
/* Are source and destination co-aligned? */
102
xor a0, a1, t0 # e0 :
105
bne t0, $unaligned # .. e1 :
107
/* We are co-aligned; take care of a partial first word. */
108
ldq_u t1, 0(a1) # e0 : load first src word
109
and a0, 7, t0 # .. e1 : take care not to load a word ...
110
addq a1, 8, a1 # e0 :
111
beq t0, stxcpy_aligned # .. e1 : ... if we wont need it
112
ldq_u t0, 0(a0) # e0 :
113
br stxcpy_aligned # .. e1 :
116
/* The source and destination are not co-aligned. Align the destination
117
and cope. We have to be very careful about not reading too much and
122
/* We know just enough now to be able to assemble the first
123
full source word. We can still find a zero at the end of it
124
that prevents us from outputting the whole thing.
126
On entry to this basic block:
127
t0 == the first dest word, for masking back in, if needed else 0
128
t1 == the low bits of the first source word
129
t6 == bytemask that is -1 in dest word bytes */
131
ldq_u t2, 8(a1) # e0 :
132
addq a1, 8, a1 # .. e1 :
134
extql t1, a1, t1 # e0 :
135
extqh t2, a1, t4 # e0 :
136
mskql t0, a0, t0 # e0 :
137
or t1, t4, t1 # .. e1 :
138
mskqh t1, a0, t1 # e0 :
142
cmpbge zero, t6, t8 # .. e1 :
143
lda t6, -1 # e0 : for masking just below
144
bne t8, $u_final # .. e1 :
146
mskql t6, a1, t6 # e0 : mask out the bits we have
147
or t6, t2, t2 # e1 : already extracted before
148
cmpbge zero, t2, t8 # e0 : testing eos
149
bne t8, $u_late_head_exit # .. e1 (zdb)
151
/* Finally, we've got all the stupid leading edge cases taken care
152
of and we can set up to enter the main loop. */
154
stq_u t1, 0(a0) # e0 : store first output word
155
addq a0, 8, a0 # .. e1 :
156
extql t2, a1, t0 # e0 : position ho-bits of lo word
157
ldq_u t2, 8(a1) # .. e1 : read next high-order source word
158
addq a1, 8, a1 # e0 :
159
cmpbge zero, t2, t8 # .. e1 :
161
bne t8, $u_eos # .. e1 :
163
/* Unaligned copy main loop. In order to avoid reading too much,
164
the loop is structured to detect zeros in aligned source words.
165
This has, unfortunately, effectively pulled half of a loop
166
iteration out into the head and half into the tail, but it does
167
prevent nastiness from accumulating in the very thing we want
168
to run as fast as possible.
170
On entry to this basic block:
171
t0 == the shifted high-order bits from the previous source word
172
t2 == the unshifted current source word
174
We further know that t2 does not contain a null terminator. */
178
extqh t2, a1, t1 # e0 : extract high bits for current word
179
addq a1, 8, a1 # .. e1 :
180
extql t2, a1, t3 # e0 : extract low bits for next time
181
addq a0, 8, a0 # .. e1 :
182
or t0, t1, t1 # e0 : current dst word now complete
183
ldq_u t2, 0(a1) # .. e1 : load high word for next time
184
stq_u t1, -8(a0) # e0 : save the current word
186
cmpbge zero, t2, t8 # e0 : test new word for eos
187
beq t8, $u_loop # .. e1 :
189
/* We've found a zero somewhere in the source word we just read.
190
If it resides in the lower half, we have one (probably partial)
191
word to write out, and if it resides in the upper half, we
192
have one full and one partial word left to write out.
194
On entry to this basic block:
195
t0 == the shifted high-order bits from the previous source word
196
t2 == the unshifted current source word. */
198
extqh t2, a1, t1 # e0 :
199
or t0, t1, t1 # e1 : first (partial) source word complete
201
cmpbge zero, t1, t8 # e0 : is the null in this first bit?
202
bne t8, $u_final # .. e1 (zdb)
205
stq_u t1, 0(a0) # e0 : the null was in the high-order bits
206
addq a0, 8, a0 # .. e1 :
207
extql t2, a1, t1 # e0 :
208
cmpbge zero, t1, t8 # .. e1 :
210
/* Take care of a final (probably partial) result word.
211
On entry to this basic block:
212
t1 == assembled source word
213
t8 == cmpbge mask that found the null. */
215
negq t8, t6 # e0 : isolate low bit set
216
and t6, t8, t12 # e1 :
218
and t12, 0x80, t6 # e0 : avoid dest word load if we can
219
bne t6, 1f # .. e1 (zdb)
221
ldq_u t0, 0(a0) # e0 :
222
subq t12, 1, t6 # .. e1 :
223
or t6, t12, t8 # e0 :
224
zapnot t1, t6, t1 # .. e1 : kill source bytes >= null
225
zap t0, t8, t0 # e0 : kill dest bytes <= null
228
1: stq_u t1, 0(a0) # e0 :
231
/* Unaligned copy entry point. */
235
ldq_u t1, 0(a1) # e0 : load first source word
237
and a0, 7, t4 # .. e1 : find dest misalignment
238
and a1, 7, t5 # e0 : find src misalignment
240
/* Conditionally load the first destination word and a bytemask
241
with 0xff indicating that the destination byte is sacrosanct. */
243
mov zero, t0 # .. e1 :
246
ldq_u t0, 0(a0) # e0 :
248
mskql t6, a0, t6 # e0 :
250
subq a1, t4, a1 # .. e1 : sub dest misalignment from src addr
252
/* If source misalignment is larger than dest misalignment, we need
253
extra startup checks to avoid SEGV. */
255
cmplt t4, t5, t12 # e0 :
256
beq t12, $u_head # .. e1 (zdb)
258
lda t2, -1 # e1 : mask out leading garbage in source
259
mskqh t2, t5, t2 # e0 :
261
ornot t1, t2, t3 # .. e1 :
262
cmpbge zero, t3, t8 # e0 : is there a zero?
263
beq t8, $u_head # .. e1 (zdb)
265
/* At this point we've found a zero in the first partial word of
266
the source. We need to isolate the valid source data and mask
267
it into the original destination data. (Incidentally, we know
268
that we'll need at least one byte of that original dest word.) */
270
ldq_u t0, 0(a0) # e0 :
272
negq t8, t6 # .. e1 : build bitmask of bytes <= zero
273
and t6, t8, t12 # e0 :
274
and a1, 7, t5 # .. e1 :
275
subq t12, 1, t6 # e0 :
276
or t6, t12, t8 # e1 :
277
srl t12, t5, t12 # e0 : adjust final null return value
279
zapnot t2, t8, t2 # .. e1 : prepare source word; mirror changes
280
and t1, t2, t1 # e1 : to source validity mask
281
extql t2, a1, t2 # .. e0 :
282
extql t1, a1, t1 # e0 :
284
andnot t0, t2, t0 # .. e1 : zero place for source to reside
285
or t0, t1, t1 # e1 : and put it there
286
stq_u t1, 0(a0) # .. e0 :