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 {
445 *d = c;
446 d = d.add(1);
447 }
448 let mut end = d.add((all as usize - d as usize) & !31);
449 while d < end {
450 #[cfg(target_arch = "riscv64")]
451 store32(d, word);
452 #[cfg(not(target_arch = "riscv64"))]
453 {
454 store_u64(d, word);
455 store_u64(d.add(8), word);
456 store_u64(d.add(16), word);
457 store_u64(d.add(24), word);
458 }
459 d = d.add(32);
460 }
461 end = d.add((all as usize - d as usize) & !7);
462 while d < end {
463 store_u64(d, word);
464 d = d.add(8);
465 }
466 while d < all {
467 *d = c;
468 d = d.add(1);
469 }
470}
471
472#[no_mangle]
473pub unsafe extern "C" fn memset(s: *mut c_void, c: c_int, n: usize) -> *mut c_void {
474 let d = s as *mut u8;
475 let b = c as u8;
476 if n != 0 {
477 #[cfg(target_arch = "x86_64")]
478 {
479 if n >= 128 && fastmem_x86::avx2() {
480 memset_avx2(d, b, n);
481 } else {
482 memset_sse2(d, b, n);
483 }
484 }
485 #[cfg(not(target_arch = "x86_64"))]
486 memset_words(d, b, n);
487 }
488 s
489}
490
491#[no_mangle]
496pub unsafe extern "C" fn memmove(dst: *mut c_void, src: *const c_void, n: usize) -> *mut c_void {
497 let d = dst as *mut u8;
498 let s = src as *const u8;
499 let du = d as usize;
500 let su = s as usize;
501 if n == 0 || du == su {
502 return dst;
503 }
504 if du < su {
505 if du + n <= su {
506 return memcpy(dst, src, n);
507 }
508 let delta = su - du;
512 let mut i = 0usize;
513 if delta >= 32 {
514 while i + 32 <= n {
515 copy8(d.add(i), s.add(i));
516 copy8(d.add(i + 8), s.add(i + 8));
517 copy8(d.add(i + 16), s.add(i + 16));
518 copy8(d.add(i + 24), s.add(i + 24));
519 i += 32;
520 }
521 }
522 if delta >= 8 {
523 while i + 8 <= n {
524 copy8(d.add(i), s.add(i));
525 i += 8;
526 }
527 }
528 while i < n {
529 *d.add(i) = *s.add(i);
530 i += 1;
531 }
532 } else {
533 if su + n <= du {
534 return memcpy(dst, src, n);
535 }
536 let mut i = n;
540 while i >= 32 {
541 i -= 32;
542 copy8(d.add(i + 24), s.add(i + 24));
543 copy8(d.add(i + 16), s.add(i + 16));
544 copy8(d.add(i + 8), s.add(i + 8));
545 copy8(d.add(i), s.add(i));
546 }
547 while i >= 8 {
548 i -= 8;
549 copy8(d.add(i), s.add(i));
550 }
551 while i > 0 {
552 i -= 1;
553 *d.add(i) = *s.add(i);
554 }
555 }
556 dst
557}
558
559#[cfg(target_arch = "x86_64")]
564#[target_feature(enable = "avx2")]
565unsafe fn memcmp_avx2(a: *const u8, b: *const u8, n: usize) -> c_int {
566 let mut i = 0usize;
567 while i + 32 <= n {
568 let va = _mm256_loadu_si256(a.add(i) as *const __m256i);
569 let vb = _mm256_loadu_si256(b.add(i) as *const __m256i);
570 let mask = _mm256_movemask_epi8(_mm256_cmpeq_epi8(va, vb)) as u32;
571 if mask != u32::MAX {
572 let k = (!mask).trailing_zeros() as usize;
573 _mm256_zeroupper();
574 return *a.add(i + k) as c_int - *b.add(i + k) as c_int;
575 }
576 i += 32;
577 }
578 _mm256_zeroupper();
579 while i + 8 <= n {
580 let wa = load_u64(a.add(i));
581 let wb = load_u64(b.add(i));
582 if wa != wb {
583 break;
584 }
585 i += 8;
586 }
587 while i < n {
588 let x = *a.add(i);
589 let y = *b.add(i);
590 if x != y {
591 return x as c_int - y as c_int;
592 }
593 i += 1;
594 }
595 0
596}
597
598#[no_mangle]
599pub unsafe extern "C" fn memcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int {
600 let a = s1 as *const u8;
601 let b = s2 as *const u8;
602 if n == 0 {
603 return 0;
604 }
605 if *a != *b {
608 return *a as c_int - *b as c_int;
609 }
610 #[cfg(target_arch = "x86_64")]
611 {
612 if n >= 32 && fastmem_x86::avx2() {
613 return memcmp_avx2(a, b, n);
614 }
615 }
616 let mut i = 0usize;
617 while i + 8 <= n {
618 let wa = core::ptr::read_unaligned(a.add(i) as *const u64);
619 let wb = core::ptr::read_unaligned(b.add(i) as *const u64);
620 if wa != wb {
621 break;
622 }
623 i += 8;
624 }
625 while i < n {
626 let x = *a.add(i);
627 let y = *b.add(i);
628 if x != y {
629 return x as c_int - y as c_int;
630 }
631 i += 1;
632 }
633 0
634}
635
636#[no_mangle]
641pub unsafe extern "C" fn strcmp(s1: *const u8, s2: *const u8) -> c_int {
642 let mut a = s1;
643 let mut b = s2;
644 const ONES: u64 = 0x0101_0101_0101_0101;
645 const HIGHS: u64 = 0x8080_8080_8080_8080;
646 loop {
647 let room_a = 4096 - ((a as usize) & 4095);
651 let room_b = 4096 - ((b as usize) & 4095);
652 let room = if room_a < room_b { room_a } else { room_b };
653 let words_end = a.add(room & !7usize);
654 while a < words_end {
655 let wa = load_u64(a);
656 let wb = load_u64(b);
657 if wa == wb {
658 if wa.wrapping_sub(ONES) & !wa & HIGHS != 0 {
659 return 0;
660 }
661 a = a.add(8);
662 b = b.add(8);
663 continue;
664 }
665 let mut i = 0usize;
666 while i < 8 {
667 let x = *a.add(i);
668 let y = *b.add(i);
669 if x != y {
670 return x as c_int - y as c_int;
671 }
672 if x == 0 {
673 return 0;
674 }
675 i += 1;
676 }
677 }
678 let tail = room & 7;
679 let mut i = 0usize;
680 while i < tail {
681 let x = *a;
682 let y = *b;
683 if x != y {
684 return x as c_int - y as c_int;
685 }
686 if x == 0 {
687 return 0;
688 }
689 a = a.add(1);
690 b = b.add(1);
691 i += 1;
692 }
693 }
694}