strncmp.S 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. /* Copyright (C) 2013-2026 Free Software Foundation, Inc.
  2. This file is part of the GNU C Library.
  3. The GNU C Library is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public
  5. License as published by the Free Software Foundation; either
  6. version 2.1 of the License, or (at your option) any later version.
  7. The GNU C Library is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public
  12. License along with the GNU C Library. If not, see
  13. <https://www.gnu.org/licenses/>. */
  14. #include <sysdep.h>
  15. /* Assumptions:
  16. *
  17. * ARMv8-a, AArch64
  18. */
  19. #define REP8_01 0x0101010101010101
  20. #define REP8_7f 0x7f7f7f7f7f7f7f7f
  21. /* Parameters and result. */
  22. #define src1 x0
  23. #define src2 x1
  24. #define limit x2
  25. #define result x0
  26. /* Internal variables. */
  27. #define data1 x3
  28. #define data1w w3
  29. #define data2 x4
  30. #define data2w w4
  31. #define has_nul x5
  32. #define diff x6
  33. #define syndrome x7
  34. #define tmp1 x8
  35. #define tmp2 x9
  36. #define tmp3 x10
  37. #define zeroones x11
  38. #define pos x12
  39. #define mask x13
  40. #define endloop x14
  41. #define count mask
  42. #define offset pos
  43. #define neg_offset x15
  44. /* Define endian dependent shift operations.
  45. On big-endian early bytes are at MSB and on little-endian LSB.
  46. LS_FW means shifting towards early bytes.
  47. LS_BK means shifting towards later bytes.
  48. */
  49. #ifdef __AARCH64EB__
  50. #define LS_FW lsl
  51. #define LS_BK lsr
  52. #else
  53. #define LS_FW lsr
  54. #define LS_BK lsl
  55. #endif
  56. .text
  57. .p2align 6
  58. .rep 9
  59. nop /* Pad so that the loop below fits a cache line. */
  60. .endr
  61. ENTRY_ALIGN (strncmp, 0)
  62. cbz limit, L(ret0)
  63. eor tmp1, src1, src2
  64. mov zeroones, #REP8_01
  65. tst tmp1, #7
  66. and count, src1, #7
  67. b.ne L(misaligned8)
  68. cbnz count, L(mutual_align)
  69. /* NUL detection works on the principle that (X - 1) & (~X) & 0x80
  70. (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and
  71. can be done in parallel across the entire word. */
  72. /* Start of performance-critical section -- one 64B cache line. */
  73. L(loop_aligned):
  74. ldr data1, [src1], #8
  75. ldr data2, [src2], #8
  76. L(start_realigned):
  77. subs limit, limit, #8
  78. sub tmp1, data1, zeroones
  79. orr tmp2, data1, #REP8_7f
  80. eor diff, data1, data2 /* Non-zero if differences found. */
  81. csinv endloop, diff, xzr, hi /* Last Dword or differences. */
  82. bics has_nul, tmp1, tmp2 /* Non-zero if NUL terminator. */
  83. ccmp endloop, #0, #0, eq
  84. b.eq L(loop_aligned)
  85. /* End of performance-critical section -- one 64B cache line. */
  86. L(full_check):
  87. #ifndef __AARCH64EB__
  88. orr syndrome, diff, has_nul
  89. add limit, limit, 8 /* Rewind limit to before last subs. */
  90. L(syndrome_check):
  91. /* Limit was reached. Check if the NUL byte or the difference
  92. is before the limit. */
  93. rev syndrome, syndrome
  94. rev data1, data1
  95. clz pos, syndrome
  96. rev data2, data2
  97. lsl data1, data1, pos
  98. cmp limit, pos, lsr #3
  99. lsl data2, data2, pos
  100. /* But we need to zero-extend (char is unsigned) the value and then
  101. perform a signed 32-bit subtraction. */
  102. lsr data1, data1, #56
  103. sub result, data1, data2, lsr #56
  104. csel result, result, xzr, hi
  105. ret
  106. #else
  107. /* Not reached the limit, must have found the end or a diff. */
  108. tbz limit, #63, L(not_limit)
  109. add tmp1, limit, 8
  110. cbz limit, L(not_limit)
  111. lsl limit, tmp1, #3 /* Bits -> bytes. */
  112. mov mask, #~0
  113. lsr mask, mask, limit
  114. bic data1, data1, mask
  115. bic data2, data2, mask
  116. /* Make sure that the NUL byte is marked in the syndrome. */
  117. orr has_nul, has_nul, mask
  118. L(not_limit):
  119. /* For big-endian we cannot use the trick with the syndrome value
  120. as carry-propagation can corrupt the upper bits if the trailing
  121. bytes in the string contain 0x01. */
  122. /* However, if there is no NUL byte in the dword, we can generate
  123. the result directly. We can't just subtract the bytes as the
  124. MSB might be significant. */
  125. cbnz has_nul, 1f
  126. cmp data1, data2
  127. cset result, ne
  128. cneg result, result, lo
  129. ret
  130. 1:
  131. /* Re-compute the NUL-byte detection, using a byte-reversed value. */
  132. rev tmp3, data1
  133. sub tmp1, tmp3, zeroones
  134. orr tmp2, tmp3, #REP8_7f
  135. bic has_nul, tmp1, tmp2
  136. rev has_nul, has_nul
  137. orr syndrome, diff, has_nul
  138. clz pos, syndrome
  139. /* The most-significant-non-zero bit of the syndrome marks either the
  140. first bit that is different, or the top bit of the first zero byte.
  141. Shifting left now will bring the critical information into the
  142. top bits. */
  143. L(end_quick):
  144. lsl data1, data1, pos
  145. lsl data2, data2, pos
  146. /* But we need to zero-extend (char is unsigned) the value and then
  147. perform a signed 32-bit subtraction. */
  148. lsr data1, data1, #56
  149. sub result, data1, data2, lsr #56
  150. ret
  151. #endif
  152. L(mutual_align):
  153. /* Sources are mutually aligned, but are not currently at an
  154. alignment boundary. Round down the addresses and then mask off
  155. the bytes that precede the start point.
  156. We also need to adjust the limit calculations, but without
  157. overflowing if the limit is near ULONG_MAX. */
  158. bic src1, src1, #7
  159. bic src2, src2, #7
  160. ldr data1, [src1], #8
  161. neg tmp3, count, lsl #3 /* 64 - bits(bytes beyond align). */
  162. ldr data2, [src2], #8
  163. mov tmp2, #~0
  164. LS_FW tmp2, tmp2, tmp3 /* Shift (count & 63). */
  165. /* Adjust the limit and ensure it doesn't overflow. */
  166. adds limit, limit, count
  167. csinv limit, limit, xzr, lo
  168. orr data1, data1, tmp2
  169. orr data2, data2, tmp2
  170. b L(start_realigned)
  171. .p2align 6
  172. /* Don't bother with dwords for up to 16 bytes. */
  173. L(misaligned8):
  174. cmp limit, #16
  175. b.hs L(try_misaligned_words)
  176. L(byte_loop):
  177. /* Perhaps we can do better than this. */
  178. ldrb data1w, [src1], #1
  179. ldrb data2w, [src2], #1
  180. subs limit, limit, #1
  181. ccmp data1w, #1, #0, hi /* NZCV = 0b0000. */
  182. ccmp data1w, data2w, #0, cs /* NZCV = 0b0000. */
  183. b.eq L(byte_loop)
  184. L(done):
  185. sub result, data1, data2
  186. ret
  187. /* Align the SRC1 to a dword by doing a bytewise compare and then do
  188. the dword loop. */
  189. L(try_misaligned_words):
  190. cbz count, L(src1_aligned)
  191. neg count, count
  192. and count, count, #7
  193. sub limit, limit, count
  194. L(page_end_loop):
  195. ldrb data1w, [src1], #1
  196. ldrb data2w, [src2], #1
  197. cmp data1w, #1
  198. ccmp data1w, data2w, #0, cs /* NZCV = 0b0000. */
  199. b.ne L(done)
  200. subs count, count, #1
  201. b.hi L(page_end_loop)
  202. /* The following diagram explains the comparison of misaligned strings.
  203. The bytes are shown in natural order. For little-endian, it is
  204. reversed in the registers. The "x" bytes are before the string.
  205. The "|" separates data that is loaded at one time.
  206. src1 | a a a a a a a a | b b b c c c c c | . . .
  207. src2 | x x x x x a a a a a a a a b b b | c c c c c . . .
  208. After shifting in each step, the data looks like this:
  209. STEP_A STEP_B STEP_C
  210. data1 a a a a a a a a b b b c c c c c b b b c c c c c
  211. data2 a a a a a a a a b b b 0 0 0 0 0 0 0 0 c c c c c
  212. The bytes with "0" are eliminated from the syndrome via mask.
  213. Align SRC2 down to 16 bytes. This way we can read 16 bytes at a
  214. time from SRC2. The comparison happens in 3 steps. After each step
  215. the loop can exit, or read from SRC1 or SRC2. */
  216. L(src1_aligned):
  217. /* Calculate offset from 8 byte alignment to string start in bits. No
  218. need to mask offset since shifts are ignoring upper bits. */
  219. lsl offset, src2, #3
  220. bic src2, src2, #0xf
  221. mov mask, -1
  222. neg neg_offset, offset
  223. ldr data1, [src1], #8
  224. ldp tmp1, tmp2, [src2], #16
  225. LS_BK mask, mask, neg_offset
  226. and neg_offset, neg_offset, #63 /* Need actual value for cmp later. */
  227. /* Skip the first compare if data in tmp1 is irrelevant. */
  228. tbnz offset, 6, L(misaligned_mid_loop)
  229. L(loop_misaligned):
  230. /* STEP_A: Compare full 8 bytes when there is enough data from SRC2.*/
  231. LS_FW data2, tmp1, offset
  232. LS_BK tmp1, tmp2, neg_offset
  233. subs limit, limit, #8
  234. orr data2, data2, tmp1 /* 8 bytes from SRC2 combined from two regs.*/
  235. sub has_nul, data1, zeroones
  236. eor diff, data1, data2 /* Non-zero if differences found. */
  237. orr tmp3, data1, #REP8_7f
  238. csinv endloop, diff, xzr, hi /* If limit, set to all ones. */
  239. bic has_nul, has_nul, tmp3 /* Non-zero if NUL byte found in SRC1. */
  240. orr tmp3, endloop, has_nul
  241. cbnz tmp3, L(full_check)
  242. ldr data1, [src1], #8
  243. L(misaligned_mid_loop):
  244. /* STEP_B: Compare first part of data1 to second part of tmp2. */
  245. LS_FW data2, tmp2, offset
  246. #ifdef __AARCH64EB__
  247. /* For big-endian we do a byte reverse to avoid carry-propagation
  248. problem described above. This way we can reuse the has_nul in the
  249. next step and also use syndrome value trick at the end. */
  250. rev tmp3, data1
  251. #define data1_fixed tmp3
  252. #else
  253. #define data1_fixed data1
  254. #endif
  255. sub has_nul, data1_fixed, zeroones
  256. orr tmp3, data1_fixed, #REP8_7f
  257. eor diff, data2, data1 /* Non-zero if differences found. */
  258. bic has_nul, has_nul, tmp3 /* Non-zero if NUL terminator. */
  259. #ifdef __AARCH64EB__
  260. rev has_nul, has_nul
  261. #endif
  262. cmp limit, neg_offset, lsr #3
  263. orr syndrome, diff, has_nul
  264. bic syndrome, syndrome, mask /* Ignore later bytes. */
  265. csinv tmp3, syndrome, xzr, hi /* If limit, set to all ones. */
  266. cbnz tmp3, L(syndrome_check)
  267. /* STEP_C: Compare second part of data1 to first part of tmp1. */
  268. ldp tmp1, tmp2, [src2], #16
  269. cmp limit, #8
  270. LS_BK data2, tmp1, neg_offset
  271. eor diff, data2, data1 /* Non-zero if differences found. */
  272. orr syndrome, diff, has_nul
  273. and syndrome, syndrome, mask /* Ignore earlier bytes. */
  274. csinv tmp3, syndrome, xzr, hi /* If limit, set to all ones. */
  275. cbnz tmp3, L(syndrome_check)
  276. ldr data1, [src1], #8
  277. sub limit, limit, #8
  278. b L(loop_misaligned)
  279. #ifdef __AARCH64EB__
  280. L(syndrome_check):
  281. clz pos, syndrome
  282. cmp pos, limit, lsl #3
  283. b.lo L(end_quick)
  284. #endif
  285. L(ret0):
  286. mov result, #0
  287. ret
  288. END (strncmp)
  289. libc_hidden_builtin_def (strncmp)