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