Cygwin: access: Fix X_OK behaviour for backup operators and admins
[newlib-cygwin.git] / newlib / libc / machine / arc64 / strcmp.S
blobfc42f538ba3f9a083c8e52af29ce20a9391a256f
1 /*
2    Copyright (c) 2024, Synopsys, Inc. All rights reserved.
4    Redistribution and use in source and binary forms, with or without
5    modification, are permitted provided that the following conditions are met:
7    1) Redistributions of source code must retain the above copyright notice,
8    this list of conditions and the following disclaimer.
10    2) Redistributions in binary form must reproduce the above copyright notice,
11    this list of conditions and the following disclaimer in the documentation
12    and/or other materials provided with the distribution.
14    3) Neither the name of the Synopsys, Inc., nor the names of its contributors
15    may be used to endorse or promote products derived from this software
16    without specific prior written permission.
18    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28    POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/asm.h>
33 #if defined (__ARC64_ARCH32__)
35 ; 64 bit version has the same working principles, with slightly different
36 ; instructions, so it is more commented
38 ENTRY (strcmp)
39         xor     r12, r12, r12
41         mov     r8, NULL_32DT_1
43         asl     r9, r8, 7
45 .L_3_4B_comparison:
47         ld.ab   r6, [r0, +4]
49         ld.ab   r7, [r1, +4]
51 #if defined (__ARC64_LL64__)
53         ldd.ab  r2r3, [r0, +8]
55         ldd.ab  r4r5, [r1, +8]
57 #else
59         ld.ab   r2, [r0, +4]
60         ld.ab   r3, [r0, +4]
62         ld.ab   r4, [r1, +4]
63         ld.ab   r5, [r1, +4]
65 #endif
67         sub     r13, r6, r8
68         sub     r10, r2, r8
69         sub     r11, r3, r8
71         bic     r13, r13, r6
72         bic     r10, r10, r2
73         bic     r11, r11, r3
75         ; Look for difference
76         sub.f   0, r6, r7
77         bset.ne r12, r12, 3
79         sub.f   0, r2, r4
80         bset.ne r12, r12, 2
82         sub.f   0, r3, r5
83         bset.ne r12, r12, 1
86         ; Look for NULL byte
87         and.f   r13, r13, r9
88         bset.ne r12, r12, 3
90         and.f   r10, r10, r9
91         bset.ne r12, r12, 2
93         and.f   r11, r11, r9
94         bset.ne r12, r12, 1
96         breq    r12, 0, @.L_3_4B_comparison
98 ; Setup r0, r3 and r5 with the relevant loaded and intermediate values
99         mov r0, r11
100         mov     r3, r3
101         mov     r5, r5
103         asr.f   r12, r12, 3
105         mov.c   r0, r10
106         mov.c   r3, r2
107         mov.c   r5, r4
109         asr.f   r12, r12, 1
111         mov.c   r0, r13
112         mov.c   r3, r6
113         mov.c   r5, r7
116         ffs.f   r10, r0
117         xor     r12, r3, r5
119         mov.z   r10, 32
120         ffs r12, r12
122         xbfu    r10, r10, 0b0111000011
123         xbfu    r12, r12, 0b0111000011
126         sub.f   0, r10, r12
128         asl.ge  r12, r12, 3
130 ; Difference is first
131         lsr.ge  r3, r3, r12
132         lsr.ge  r5, r5, r12
134         bmsk    r3, r3, 7
135         bmsk    r5, r5, 7
137         j_s.d   [blink]
138         sub     r0, r3, r5
141 ENDFUNC(strcmp)
143 #else
145 ENTRY (strcmp)
147         xorl    r12, r12, r12
149 ; Setup byte detector (more information bellow) [1]
150         vpack2wl        r8, NULL_32DT_1, NULL_32DT_1
151 ; Set r9 as a copy of r8 for vectorized sub
152         asll    r9, r8, 7
154 .L_3_8B_comparison:
156         ldl.ab  r6, [r0, +8]
158         ldl.ab  r7, [r1, +8]
160 ; Using 128-bit memory operations
161 #if defined (__ARC64_M128__)
163         lddl.ab r2r3, [r0, +16]
165         lddl.ab r4r5, [r1, +16]
167 ; The 64-bit crunching implementation.
168 #elif defined (__ARC64_ARCH64__)
170         ldl.ab  r2, [r0, +8]
171         ldl.ab  r3, [r0, +8]
173         ldl.ab  r4, [r1, +8]
174         ldl.ab  r5, [r1, +8]
176 #else
177         # error Unknown configuration
178 #endif
180         subl    r13, r6, r8
181         subl    r10, r2, r8
182         subl    r11, r3, r8
184         bicl    r13, r13, r6
185         bicl    r10, r10, r2
186         bicl    r11, r11, r3
188 ; Look for difference
189         subl.f  0, r6, r7
190         bset.ne r12, r12, 3
192         subl.f  0, r2, r4
193         bset.ne r12, r12, 2
195         subl.f  0, r3, r5
196         bset.ne r12, r12, 1
198 ; Look for NULL byte
199         andl.f  r13, r13, r9
200         bset.ne r12, r12, 3
202         andl.f  r10, r10, r9
203         bset.ne r12, r12, 2
205         andl.f  r11, r11, r9
206         bset.ne r12, r12, 1
208         breq    r12, 0, @.L_3_8B_comparison
210 ; Setup r0, r3 and r5 with the relevant loaded and intermediate values [2]
211         ; [3]
212         movl    r0, r11
213         movl    r3, r3
214         movl    r5, r5
216         asr.f   r12, r12, 3
218         movl.c  r0, r10
219         movl.c  r3, r2
220         movl.c  r5, r4
222         asr.f   r12, r12, 1
224         movl.c  r0, r13
225         movl.c  r3, r6
226         movl.c  r5, r7
228         ffsl.f  r10, r0         ; [5]
229         xorl    r12, r3, r5
231         movl.z  r10, 64         ; [6]
232         ffsl    r12, r12        ; [8]
234         xbful   r10, r10, 0b0111000011  ; [7]
235         xbful   r12, r12, 0b0111000011
237 ; r12 contains position of difference and r10 the position of a NULL byte
238 ; r3 and r5 contain the differing 8 bytes
240 ; Is there a difference?
241         subl.f  0, r10, r12
242 ; Multiply the byte position by 8 to get bit shift
243         asll.ge r12, r12, 3
245         lsrl.ge r3, r3, r12
246         lsrl.ge r5, r5, r12
248 ; There is no difference. Up until the NULL byte which must be
250         bmskl   r3, r3, 7
251         bmskl   r5, r5, 7
253         j_s.d   [blink]
254         subl    r0, r3, r5
257 ENDFUNC (strcmp)
259 #endif
261 ;; One important thing to note, is that we look for the first byte difference on
262 ;; both strings but we only look for the NULL byte in one string.
263 ;; This is because if a NULL byte appears first, it will be the first different
264 ;; byte. If it doesnt, the difference is what matters either way. If there is no
265 ;; difference, the NULL bytes will coincide!
268 ;; This code uses a common technique for NULL byte detection inside a word.
269 ;; Details on this technique can be found in:
270 ;; (https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord)
272 ; In sum, this technique allows for detecting a NULL byte inside any given
273 ; amount of bits by performing the following operation
274 ;               DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080) [0]
276 ; The code above implements this by setting r8 to a 0x01010101... sequence and
277 ; r1 to a 0x80808080... sequence of appropriate length
278 ; As LIMM are 32 bit only, we need to perform MOVHL and ORL [1] operations to
279 ; have the appropriate 64 bit values in place
281 ;; Comparison is done 24 bytes at a time, either with 3 64 bit loads or 1 128 bit
282 ;; load and 1 64 bit.
283 ;; If either a NULL byte or a difference between the strings is found, r12 is
284 ;; used to know in which word the NULL/difference is found
286 ; With the carry bit from r12, we can use mov.c to only move the appropriate
287 ; registers into the ones we will operate on [2]. We can safely directly move
288 ; the last set of registers without looking at r12, because if they aren't the
289 ; appropriate ones, they will be rewritten afterwards. [3]
291 ;; Knowing the registers that contain the relevant information, we only need to
292 ;; look into where the difference and one of the zeros is.
293 ;; This is because, if the zeros are in different places, the difference will
294 ;; either be an earlier difference, or the first zero, so the actual zeros are
295 ;; irrelevant.
296 ;; Zero position is only relevant if there is no difference. And if there is no
297 ;; difference, the zeros have the same position.
299 ; So now comes the tricky part. In order to obtain the position of a "first
300 ; NULL byte", we need to understand the NULL byte detection operation.
301 ; It is explained in depth in the link above but in short, it works by first
302 ; setting the highest bit of each byte to 1, if the corresponding byte is either
303 ; 0 or more than 0x80
304 ; Then, it makes the highest bit of each byte 1, if the byte is less than 0x80.
305 ; The last step is to AND these two values (this operation is simplified with
306 ; the SUB, BIC and TST instructions).
308 ; This means that the evaluated equation result value has zeros for all non
309 ; zero bytes, except for the NULL bytes. Therefore, we can simply find the
310 ; first non zero bit (counting from bit 0) which will be inside the position of
311 ; the first NULL byte. [5]
313 ; One thing to note, is that ffs oddly returns 31/63 if no bit is found, setting
314 ; the zero flag. As there can be that no NULL byte is present on one or both
315 ; strings at this point, we must set r10 and r11 to 32/64 when appropriate. [6]
317 ; We can then convert the bit position into the last byte position by looking
318 ; into bits 3 to 5, and shifting 3 bits to the right. This can be combined into
319 ; a single xbful operation. The bottom 000011 represent shift by 3 and the top
320 ; 0111 represents the mask (3 to 5 shifted by 3 is 0 to 2). [7]
322 ; To obtain the position of the difference, all we need to do is xor the two
323 ; registers. This way, every equal byte cancels out and all we are left with
324 ; is gibberish in the differing bytes. We can use the same ffs and xbuf
325 ; operations to get the differing byte position.
327 ; Note that the order of the operations isnt the same as in this explanation,
328 ; to reduce register dependency between instructions
331 ; Unlike with r10, we dont need to check the zero flag for r12s' ffs because if
332 ; it is 0, it means there is no difference in the loaded data so any subtraction
333 ; operation will return 0 [8]
335 ; There is one optimization that is being overlooked, which is returning 0 if
336 ; there is no difference, but there are NULL bytes anywhere, right after the
337 ; main loop. The reason for this is because the only way this can happen is if
338 ; the strings have the same length AND either are a multiple of 16/8 bytes, or
339 ; the bytes that follow the NULL bytes also match. As this is extremely
340 ; unlikely, it isnt worth it to perform this optimization since it would require
341 ; an extra branch in all runs