1#[cfg(target_arch = "x86_64")]
12mod fastmem_x86 {
13 use core::sync::atomic::{AtomicU8, Ordering};
14
15 const UNKNOWN: u8 = 0;
16 const NO: u8 = 1;
17 const YES: u8 = 2;
18
19 static AVX2: AtomicU8 = AtomicU8::new(UNKNOWN);
20 static ERMS: AtomicU8 = AtomicU8::new(UNKNOWN);
21
22 #[inline]
23 fn xgetbv0() -> u64 {
24 let lo: u32;
25 let hi: u32;
26 unsafe {
27 core::arch::asm!("xgetbv", in("ecx") 0u32, out("eax") lo, out("edx") hi,
28 options(nostack, nomem));
29 }
30 ((hi as u64) << 32) | lo as u64
31 }
32
33 pub fn avx2() -> bool {
34 match AVX2.load(Ordering::Relaxed) {
35 YES => return true,
36 NO => return false,
37 _ => {}
38 }
39 let ok = {
40 let max = core::arch::x86_64::__cpuid(0).eax;
41 if max < 7 {
42 false
43 } else {
44 let c1 = core::arch::x86_64::__cpuid(1);
45 let osxsave = c1.ecx & (1 << 27) != 0;
46 let avx = c1.ecx & (1 << 28) != 0;
47 osxsave
48 && avx
49 && (xgetbv0() & 0x6) == 0x6
50 && core::arch::x86_64::__cpuid_count(7, 0).ebx & (1 << 5) != 0
51 }
52 };
53 AVX2.store(if ok { YES } else { NO }, Ordering::Relaxed);
54 ok
55 }
56
57 pub fn erms() -> bool {
58 match ERMS.load(Ordering::Relaxed) {
59 YES => return true,
60 NO => return false,
61 _ => {}
62 }
63 let ok = {
64 let max = core::arch::x86_64::__cpuid(0).eax;
65 max >= 7 && core::arch::x86_64::__cpuid_count(7, 0).ebx & (1 << 9) != 0
66 };
67 ERMS.store(if ok { YES } else { NO }, Ordering::Relaxed);
68 ok
69 }
70}
71
72#[cfg(target_arch = "x86_64")]
73use core::arch::x86_64::*;
74
75#[cfg(target_arch = "x86_64")]
80unsafe fn strlen_sse2(s: *const u8) -> usize {
81 let start = s as usize;
82 let mut p = start & !15usize;
83 let zero = _mm_setzero_si128();
84 let first = _mm_load_si128(p as *const __m128i);
85 let mut mask = _mm_movemask_epi8(_mm_cmpeq_epi8(first, zero)) as u32;
86 mask &= !0u32 << (start - p);
87 if mask != 0 {
88 return p - start + mask.trailing_zeros() as usize;
89 }
90 p += 16;
91 loop {
92 let a = _mm_load_si128(p as *const __m128i);
93 let ma = _mm_movemask_epi8(_mm_cmpeq_epi8(a, zero)) as u32;
94 if ma != 0 {
95 return p + ma.trailing_zeros() as usize - start;
96 }
97 let b = _mm_load_si128((p + 16) as *const __m128i);
98 let mb = _mm_movemask_epi8(_mm_cmpeq_epi8(b, zero)) as u32;
99 if mb != 0 {
100 return p + 16 + mb.trailing_zeros() as usize - start;
101 }
102 p += 32;
103 }
104}
105
106#[cfg(target_arch = "x86_64")]
107#[target_feature(enable = "avx2")]
108unsafe fn strlen_avx2(s: *const u8) -> usize {
109 let start = s as usize;
110 let mut p = start & !31usize;
111 let zero = _mm256_setzero_si256();
112 let first = _mm256_load_si256(p as *const __m256i);
113 let mut mask = _mm256_movemask_epi8(_mm256_cmpeq_epi8(first, zero)) as u32;
114 mask &= !0u32 << (start - p);
115 if mask != 0 {
116 return p - start + mask.trailing_zeros() as usize;
117 }
118 p += 32;
119 loop {
120 let a = _mm256_load_si256(p as *const __m256i);
121 let ma = _mm256_movemask_epi8(_mm256_cmpeq_epi8(a, zero)) as u32;
122 if ma != 0 {
123 return p + ma.trailing_zeros() as usize - start;
124 }
125 let b = _mm256_load_si256((p + 32) as *const __m256i);
126 let mb = _mm256_movemask_epi8(_mm256_cmpeq_epi8(b, zero)) as u32;
127 if mb != 0 {
128 return p + 32 + mb.trailing_zeros() as usize - start;
129 }
130 p += 64;
131 }
132}
133
134#[cfg(not(target_arch = "x86_64"))]
135unsafe fn strlen_words(s: *const u8) -> usize {
136 let start = s as usize;
137 let mut p = s;
138 while (p as usize) & 7 != 0 {
139 if *p == 0 {
140 return p as usize - start;
141 }
142 p = p.add(1);
143 }
144 const ONES: u64 = 0x0101_0101_0101_0101;
145 const HIGHS: u64 = 0x8080_8080_8080_8080;
146 loop {
149 let w = core::ptr::read(p as *const u64);
150 let z = w.wrapping_sub(ONES) & !w & HIGHS;
151 if z != 0 {
152 return p as usize - start + (z.trailing_zeros() as usize / 8);
153 }
154 p = p.add(8);
155 }
156}
157
158#[no_mangle]
159pub unsafe extern "C" fn strlen(s: *const c_char) -> usize {
160 #[cfg(target_arch = "x86_64")]
161 {
162 if fastmem_x86::avx2() {
163 return strlen_avx2(s as *const u8);
164 }
165 strlen_sse2(s as *const u8)
166 }
167 #[cfg(not(target_arch = "x86_64"))]
168 {
169 strlen_words(s as *const u8)
170 }
171}
172
173#[cfg(target_arch = "x86_64")]
178#[inline]
179unsafe fn store16(dst: *mut u8, src: *const u8) {
180 let v = _mm_loadu_si128(src as *const __m128i);
181 _mm_storeu_si128(dst as *mut __m128i, v);
182}
183
184#[cfg(target_arch = "riscv64")]
189#[inline(always)]
190unsafe fn load_u64(p: *const u8) -> u64 {
191 let v: u64;
192 core::arch::asm!("ld {}, 0({})", out(reg) v, in(reg) p, options(nostack));
193 v
194}
195
196#[cfg(not(target_arch = "riscv64"))]
197#[inline(always)]
198unsafe fn load_u64(p: *const u8) -> u64 {
199 core::ptr::read_unaligned(p as *const u64)
200}
201
202#[cfg(target_arch = "riscv64")]
203#[inline(always)]
204unsafe fn store_u64(p: *mut u8, v: u64) {
205 core::arch::asm!("sd {}, 0({})", in(reg) v, in(reg) p, options(nostack));
206}
207
208#[cfg(not(target_arch = "riscv64"))]
209#[inline(always)]
210unsafe fn store_u64(p: *mut u8, v: u64) {
211 core::ptr::write_unaligned(p as *mut u64, v);
212}
213
214#[cfg(target_arch = "riscv64")]
215#[inline(always)]
216unsafe fn copy32(d: *mut u8, s: *const u8) {
217 core::arch::asm!(
218 "ld t0, 0({s})", "ld t1, 8({s})", "ld t2, 16({s})", "ld t3, 24({s})",
219 "sd t0, 0({d})", "sd t1, 8({d})", "sd t2, 16({d})", "sd t3, 24({d})",
220 s = in(reg) s, d = in(reg) d,
221 out("t0") _, out("t1") _, out("t2") _, out("t3") _,
222 options(nostack),
223 );
224}
225
226#[cfg(target_arch = "riscv64")]
227#[inline(always)]
228unsafe fn store32(d: *mut u8, v: u64) {
229 core::arch::asm!(
230 "sd {v}, 0({d})", "sd {v}, 8({d})", "sd {v}, 16({d})", "sd {v}, 24({d})",
231 v = in(reg) v, d = in(reg) d,
232 options(nostack),
233 );
234}
235
236#[inline]
237unsafe fn copy8(dst: *mut u8, src: *const u8) {
238 store_u64(dst, load_u64(src));
239}
240
241#[cfg(target_arch = "x86_64")]
242unsafe fn memcpy_sse2(dst: *mut u8, src: *const u8, n: usize) {
243 if n >= 16 {
244 let mut i = 0usize;
245 while i + 16 <= n {
246 store16(dst.add(i), src.add(i));
247 i += 16;
248 }
249 if i < n {
250 store16(dst.add(n - 16), src.add(n - 16));
251 }
252 } else if n >= 8 {
253 copy8(dst, src);
254 copy8(dst.add(n - 8), src.add(n - 8));
255 } else if n >= 4 {
256 core::ptr::write_unaligned(
257 dst as *mut u32,
258 core::ptr::read_unaligned(src as *const u32),
259 );
260 core::ptr::write_unaligned(
261 dst.add(n - 4) as *mut u32,
262 core::ptr::read_unaligned(src.add(n - 4) as *const u32),
263 );
264 } else if n >= 2 {
265 core::ptr::write_unaligned(
266 dst as *mut u16,
267 core::ptr::read_unaligned(src as *const u16),
268 );
269 core::ptr::write_unaligned(
270 dst.add(n - 2) as *mut u16,
271 core::ptr::read_unaligned(src.add(n - 2) as *const u16),
272 );
273 } else if n == 1 {
274 *dst = *src;
275 }
276}
277
278#[cfg(target_arch = "x86_64")]
279#[target_feature(enable = "avx2")]
280unsafe fn memcpy_avx2(dst: *mut u8, src: *const u8, n: usize) {
281 let mut i = 0usize;
282 let head = (32 - (dst as usize & 31)) & 31;
283 while i < head {
284 *dst.add(i) = *src.add(i);
285 i += 1;
286 }
287 while i + 128 <= n {
288 let a = _mm256_loadu_si256(src.add(i) as *const __m256i);
289 let b = _mm256_loadu_si256(src.add(i + 32) as *const __m256i);
290 let c = _mm256_loadu_si256(src.add(i + 64) as *const __m256i);
291 let d = _mm256_loadu_si256(src.add(i + 96) as *const __m256i);
292 _mm256_store_si256(dst.add(i) as *mut __m256i, a);
293 _mm256_store_si256(dst.add(i + 32) as *mut __m256i, b);
294 _mm256_store_si256(dst.add(i + 64) as *mut __m256i, c);
295 _mm256_store_si256(dst.add(i + 96) as *mut __m256i, d);
296 i += 128;
297 }
298 while i + 32 <= n {
299 let a = _mm256_loadu_si256(src.add(i) as *const __m256i);
300 _mm256_store_si256(dst.add(i) as *mut __m256i, a);
301 i += 32;
302 }
303 if i < n {
304 let off = n - 32;
305 let a = _mm256_loadu_si256(src.add(off) as *const __m256i);
306 _mm256_storeu_si256(dst.add(off) as *mut __m256i, a);
307 }
308 _mm256_zeroupper();
309}
310
311#[cfg(target_arch = "x86_64")]
312unsafe fn memcpy_rep(dst: *mut u8, src: *const u8, n: usize) {
313 core::arch::asm!("rep movsb", inout("rdi") dst => _, inout("rsi") src => _,
314 inout("rcx") n => _, options(nostack, preserves_flags));
315}
316
317#[cfg(not(target_arch = "x86_64"))]
318unsafe fn memcpy_words(dst: *mut u8, src: *const u8, n: usize) {
319 let mut d = dst;
320 let mut s = src;
321 let all = dst.add(n);
322 while d < all && (d as usize) & 7 != 0 {
323 *d = *s;
324 d = d.add(1);
325 s = s.add(1);
326 }
327 let mut end = d.add((all as usize - d as usize) & !31);
328 while d < end {
329 #[cfg(target_arch = "riscv64")]
330 copy32(d, s);
331 #[cfg(not(target_arch = "riscv64"))]
332 {
333 copy8(d, s);
334 copy8(d.add(8), s.add(8));
335 copy8(d.add(16), s.add(16));
336 copy8(d.add(24), s.add(24));
337 }
338 d = d.add(32);
339 s = s.add(32);
340 }
341 end = d.add((all as usize - d as usize) & !7);
342 while d < end {
343 copy8(d, s);
344 d = d.add(8);
345 s = s.add(8);
346 }
347 while d < all {
348 *d = *s;
349 d = d.add(1);
350 s = s.add(1);
351 }
352}
353
354#[no_mangle]
355pub unsafe extern "C" fn memcpy(dst: *mut c_void, src: *const c_void, n: usize) -> *mut c_void {
356 let d = dst as *mut u8;
357 let s = src as *const u8;
358 if n != 0 {
359 #[cfg(target_arch = "x86_64")]
360 {
361 if n >= 128 {
362 if fastmem_x86::avx2() {
363 memcpy_avx2(d, s, n);
364 } else if fastmem_x86::erms() {
365 memcpy_rep(d, s, n);
366 } else {
367 memcpy_sse2(d, s, n);
368 }
369 } else {
370 memcpy_sse2(d, s, n);
371 }
372 }
373 #[cfg(not(target_arch = "x86_64"))]
374 memcpy_words(d, s, n);
375 }
376 dst
377}
378
379#[cfg(target_arch = "x86_64")]
384unsafe fn memset_sse2(dst: *mut u8, c: u8, n: usize) {
385 let v16 = _mm_set1_epi8(c as i8);
386 if n >= 16 {
387 let mut i = 0usize;
388 while i + 16 <= n {
389 _mm_storeu_si128(dst.add(i) as *mut __m128i, v16);
390 i += 16;
391 }
392 if i < n {
393 _mm_storeu_si128(dst.add(n - 16) as *mut __m128i, v16);
394 }
395 } else if n >= 8 {
396 let v64 = 0x0101_0101_0101_0101u64 * c as u64;
397 core::ptr::write_unaligned(dst as *mut u64, v64);
398 core::ptr::write_unaligned(dst.add(n - 8) as *mut u64, v64);
399 } else if n >= 4 {
400 let v32 = 0x0101_0101u32 * c as u32;
401 core::ptr::write_unaligned(dst as *mut u32, v32);
402 core::ptr::write_unaligned(dst.add(n - 4) as *mut u32, v32);
403 } else if n >= 2 {
404 let v16 = 0x0101u16 * c as u16;
405 core::ptr::write_unaligned(dst as *mut u16, v16);
406 core::ptr::write_unaligned(dst.add(n - 2) as *mut u16, v16);
407 } else if n == 1 {
408 *dst = c;
409 }
410}
411
412#[cfg(target_arch = "x86_64")]
413#[target_feature(enable = "avx2")]
414unsafe fn memset_avx2(dst: *mut u8, c: u8, n: usize) {
415 let v = _mm256_set1_epi8(c as i8);
416 let mut i = 0usize;
417 let head = (32 - (dst as usize & 31)) & 31;
418 while i < head {
419 *dst.add(i) = c;
420 i += 1;
421 }
422 while i + 128 <= n {
423 _mm256_store_si256(dst.add(i) as *mut __m256i, v);
424 _mm256_store_si256(dst.add(i + 32) as *mut __m256i, v);
425 _mm256_store_si256(dst.add(i + 64) as *mut __m256i, v);
426 _mm256_store_si256(dst.add(i + 96) as *mut __m256i, v);
427 i += 128;
428 }
429 while i + 32 <= n {
430 _mm256_store_si256(dst.add(i) as *mut __m256i, v);
431 i += 32;
432 }
433 if i < n {
434 _mm256_storeu_si256(dst.add(n - 32) as *mut __m256i, v);
435 }
436 _mm256_zeroupper();
437}
438
439#[cfg(not(target_arch = "x86_64"))]
440unsafe fn memset_words(dst: *mut u8, c: u8, n: usize) {
441 let word = 0x0101_0101_0101_0101u64 * c as u64;
442 let all = dst.add(n);
443 let mut d = dst;
444 while d < all && (d as usize) & 7 != 0 {
447 core::ptr::write_volatile(d, c);
448 d = d.add(1);
449 }
450 let mut end = d.add((all as usize - d as usize) & !31);
451 while d < end {
452 #[cfg(target_arch = "riscv64")]
453 store32(d, word);
454 #[cfg(not(target_arch = "riscv64"))]
455 {
456 store_u64(d, word);
457 store_u64(d.add(8), word);
458 store_u64(d.add(16), word);
459 store_u64(d.add(24), word);
460 }
461 d = d.add(32);
462 }
463 end = d.add((all as usize - d as usize) & !7);
464 while d < end {
465 store_u64(d, word);
466 d = d.add(8);
467 }
468 while d < all {
469 core::ptr::write_volatile(d, c);
470 d = d.add(1);
471 }
472}
473
474#[no_mangle]
475pub unsafe extern "C" fn memset(s: *mut c_void, c: c_int, n: usize) -> *mut c_void {
476 let d = s as *mut u8;
477 let b = c as u8;
478 if n != 0 {
479 #[cfg(target_arch = "x86_64")]
480 {
481 if n >= 128 && fastmem_x86::avx2() {
482 memset_avx2(d, b, n);
483 } else {
484 memset_sse2(d, b, n);
485 }
486 }
487 #[cfg(not(target_arch = "x86_64"))]
488 memset_words(d, b, n);
489 }
490 s
491}
492
493#[no_mangle]
498pub unsafe extern "C" fn memmove(dst: *mut c_void, src: *const c_void, n: usize) -> *mut c_void {
499 let d = dst as *mut u8;
500 let s = src as *const u8;
501 let du = d as usize;
502 let su = s as usize;
503 if n == 0 || du == su {
504 return dst;
505 }
506 if du < su {
507 if du + n <= su {
508 return memcpy(dst, src, n);
509 }
510 let delta = su - du;
514 let mut i = 0usize;
515 if delta >= 32 {
516 while i + 32 <= n {
517 copy8(d.add(i), s.add(i));
518 copy8(d.add(i + 8), s.add(i + 8));
519 copy8(d.add(i + 16), s.add(i + 16));
520 copy8(d.add(i + 24), s.add(i + 24));
521 i += 32;
522 }
523 }
524 if delta >= 8 {
525 while i + 8 <= n {
526 copy8(d.add(i), s.add(i));
527 i += 8;
528 }
529 }
530 while i < n {
531 *d.add(i) = *s.add(i);
532 i += 1;
533 }
534 } else {
535 if su + n <= du {
536 return memcpy(dst, src, n);
537 }
538 let mut i = n;
542 while i >= 32 {
543 i -= 32;
544 copy8(d.add(i + 24), s.add(i + 24));
545 copy8(d.add(i + 16), s.add(i + 16));
546 copy8(d.add(i + 8), s.add(i + 8));
547 copy8(d.add(i), s.add(i));
548 }
549 while i >= 8 {
550 i -= 8;
551 copy8(d.add(i), s.add(i));
552 }
553 while i > 0 {
554 i -= 1;
555 *d.add(i) = *s.add(i);
556 }
557 }
558 dst
559}
560
561#[cfg(target_arch = "x86_64")]
566#[target_feature(enable = "avx2")]
567unsafe fn memcmp_avx2(a: *const u8, b: *const u8, n: usize) -> c_int {
568 let mut i = 0usize;
569 while i + 32 <= n {
570 let va = _mm256_loadu_si256(a.add(i) as *const __m256i);
571 let vb = _mm256_loadu_si256(b.add(i) as *const __m256i);
572 let mask = _mm256_movemask_epi8(_mm256_cmpeq_epi8(va, vb)) as u32;
573 if mask != u32::MAX {
574 let k = (!mask).trailing_zeros() as usize;
575 _mm256_zeroupper();
576 return *a.add(i + k) as c_int - *b.add(i + k) as c_int;
577 }
578 i += 32;
579 }
580 _mm256_zeroupper();
581 while i + 8 <= n {
582 let wa = load_u64(a.add(i));
583 let wb = load_u64(b.add(i));
584 if wa != wb {
585 break;
586 }
587 i += 8;
588 }
589 while i < n {
590 let x = *a.add(i);
591 let y = *b.add(i);
592 if x != y {
593 return x as c_int - y as c_int;
594 }
595 i += 1;
596 }
597 0
598}
599
600#[no_mangle]
601pub unsafe extern "C" fn memcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int {
602 let a = s1 as *const u8;
603 let b = s2 as *const u8;
604 if n == 0 {
605 return 0;
606 }
607 if *a != *b {
610 return *a as c_int - *b as c_int;
611 }
612 #[cfg(target_arch = "x86_64")]
613 {
614 if n >= 32 && fastmem_x86::avx2() {
615 return memcmp_avx2(a, b, n);
616 }
617 }
618 let mut i = 0usize;
619 while i + 8 <= n {
620 let wa = core::ptr::read_unaligned(a.add(i) as *const u64);
621 let wb = core::ptr::read_unaligned(b.add(i) as *const u64);
622 if wa != wb {
623 break;
624 }
625 i += 8;
626 }
627 while i < n {
628 let x = *a.add(i);
629 let y = *b.add(i);
630 if x != y {
631 return x as c_int - y as c_int;
632 }
633 i += 1;
634 }
635 0
636}
637
638#[no_mangle]
643pub unsafe extern "C" fn strcmp(s1: *const u8, s2: *const u8) -> c_int {
644 let mut a = s1;
645 let mut b = s2;
646 const ONES: u64 = 0x0101_0101_0101_0101;
647 const HIGHS: u64 = 0x8080_8080_8080_8080;
648 loop {
649 let room_a = 4096 - ((a as usize) & 4095);
653 let room_b = 4096 - ((b as usize) & 4095);
654 let room = if room_a < room_b { room_a } else { room_b };
655 let words_end = a.add(room & !7usize);
656 while a < words_end {
657 let wa = load_u64(a);
658 let wb = load_u64(b);
659 if wa == wb {
660 if wa.wrapping_sub(ONES) & !wa & HIGHS != 0 {
661 return 0;
662 }
663 a = a.add(8);
664 b = b.add(8);
665 continue;
666 }
667 let mut i = 0usize;
668 while i < 8 {
669 let x = *a.add(i);
670 let y = *b.add(i);
671 if x != y {
672 return x as c_int - y as c_int;
673 }
674 if x == 0 {
675 return 0;
676 }
677 i += 1;
678 }
679 }
680 let tail = room & 7;
681 let mut i = 0usize;
682 while i < tail {
683 let x = *a;
684 let y = *b;
685 if x != y {
686 return x as c_int - y as c_int;
687 }
688 if x == 0 {
689 return 0;
690 }
691 a = a.add(1);
692 b = b.add(1);
693 i += 1;
694 }
695 }
696}