From 0439f4ef09af50b440af245a0f2173442fbcd567 Mon Sep 17 00:00:00 2001 From: SomeFlyingThing <306498559+SomeFlyingThing@users.noreply.github.com> Date: Thu, 23 Jul 2026 18:32:21 +0100 Subject: [PATCH 1/5] Hint that memchr returns an in-bounds index --- library/core/src/slice/memchr.rs | 7 ++++++- library/coretests/tests/slice.rs | 11 +++++++++++ .../codegen-llvm/lib-optimizations/memchr-result.rs | 13 +++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/codegen-llvm/lib-optimizations/memchr-result.rs diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index 1e1053583a617..6762015181d85 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -28,7 +28,12 @@ pub const fn memchr(x: u8, text: &[u8]) -> Option { return memchr_naive(x, text); } - memchr_aligned(x, text) + let result = memchr_aligned(x, text); + if let Some(index) = result { + // SAFETY: `memchr_aligned` only returns the index of a matching byte in `text`. + unsafe { crate::hint::assert_unchecked(index < text.len()) }; + } + result } #[inline] diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index a4db7304fff90..b05f54d4df0a2 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -1781,6 +1781,17 @@ pub mod memchr { assert_eq!(None, memchr(b'a', b"xyz")); } + #[test] + fn each_alignment() { + let mut data = [1u8; 64]; + let needle = 2; + let pos = 40; + data[pos] = needle; + for start in 0..16 { + assert_eq!(Some(pos - start), memchr(needle, &data[start..])); + } + } + #[test] fn matches_one_reversed() { assert_eq!(Some(0), memrchr(b'a', b"a")); diff --git a/tests/codegen-llvm/lib-optimizations/memchr-result.rs b/tests/codegen-llvm/lib-optimizations/memchr-result.rs new file mode 100644 index 0000000000000..fbdbdcc3fe9f3 --- /dev/null +++ b/tests/codegen-llvm/lib-optimizations/memchr-result.rs @@ -0,0 +1,13 @@ +// Ensure `memchr` communicates that a returned index is in bounds. +//@ compile-flags: -Copt-level=3 -Zinline-mir=false +//@ only-64bit + +#![crate_type = "lib"] + +// CHECK-LABEL: @find_char +#[no_mangle] +pub fn find_char(haystack: &str, needle: char) -> Option { + // CHECK-NOT: phi { i64, i64 } + // CHECK: ret { i64, i64 } + haystack.find(needle) +} From c1f36d5f4bde0f955e9d0cbdec406d22b5044360 Mon Sep 17 00:00:00 2001 From: SomeFlyingThing <306498559+SomeFlyingThing@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:29:23 +0100 Subject: [PATCH 2/5] Hint that memrchr returns an in-bounds index --- library/core/src/slice/memchr.rs | 10 ++++++++++ .../codegen-llvm/lib-optimizations/memchr-result.rs | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index 6762015181d85..c83e8b218da08 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -112,8 +112,18 @@ const fn memchr_aligned(x: u8, text: &[u8]) -> Option { } /// Returns the last index matching the byte `x` in `text`. +#[inline] #[must_use] pub fn memrchr(x: u8, text: &[u8]) -> Option { + let result = memrchr_aligned(x, text); + if let Some(index) = result { + // SAFETY: `memrchr_aligned` only returns the index of a matching byte in `text`. + unsafe { crate::hint::assert_unchecked(index < text.len()) }; + } + result +} + +fn memrchr_aligned(x: u8, text: &[u8]) -> Option { // Scan for a single byte value by reading two `usize` words at a time. // // Split `text` in three parts: diff --git a/tests/codegen-llvm/lib-optimizations/memchr-result.rs b/tests/codegen-llvm/lib-optimizations/memchr-result.rs index fbdbdcc3fe9f3..f18335075451c 100644 --- a/tests/codegen-llvm/lib-optimizations/memchr-result.rs +++ b/tests/codegen-llvm/lib-optimizations/memchr-result.rs @@ -3,6 +3,11 @@ //@ only-64bit #![crate_type = "lib"] +#![feature(slice_internals)] + +extern crate core; + +use core::slice::memchr::memrchr; // CHECK-LABEL: @find_char #[no_mangle] @@ -11,3 +16,11 @@ pub fn find_char(haystack: &str, needle: char) -> Option { // CHECK: ret { i64, i64 } haystack.find(needle) } + +// CHECK-LABEL: @rfind_byte +#[no_mangle] +pub fn rfind_byte(haystack: &[u8], needle: u8) -> Option { + // CHECK-NOT: panic_bounds_check + // CHECK: ret { i1, i8 } + memrchr(needle, haystack).map(|index| haystack[index]) +} From 844c01e43be5782643646d73a6f65539db046a33 Mon Sep 17 00:00:00 2001 From: SomeFlyingThing <306498559+SomeFlyingThing@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:14:14 +0000 Subject: [PATCH 3/5] Cover memchr fast path with bounds assertion --- library/core/src/slice/memchr.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index c83e8b218da08..017661f0448c2 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -24,13 +24,13 @@ const fn contains_zero_byte(x: usize) -> bool { #[must_use] pub const fn memchr(x: u8, text: &[u8]) -> Option { // Fast path for small slices. - if text.len() < 2 * USIZE_BYTES { - return memchr_naive(x, text); - } - - let result = memchr_aligned(x, text); + let result = if text.len() < 2 * USIZE_BYTES { + memchr_naive(x, text) + } else { + memchr_aligned(x, text) + }; if let Some(index) = result { - // SAFETY: `memchr_aligned` only returns the index of a matching byte in `text`. + // SAFETY: Both implementations only return an index from within `text`. unsafe { crate::hint::assert_unchecked(index < text.len()) }; } result From 49c1f02279a37b85fcd9448dc7b87e20923f57dd Mon Sep 17 00:00:00 2001 From: SomeFlyingThing <306498559+SomeFlyingThing@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:32:44 +0000 Subject: [PATCH 4/5] Fix memchr result CI checks --- library/core/src/slice/memchr.rs | 7 ++----- tests/codegen-llvm/lib-optimizations/memchr-result.rs | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index 017661f0448c2..fb99e86139d7e 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -24,11 +24,8 @@ const fn contains_zero_byte(x: usize) -> bool { #[must_use] pub const fn memchr(x: u8, text: &[u8]) -> Option { // Fast path for small slices. - let result = if text.len() < 2 * USIZE_BYTES { - memchr_naive(x, text) - } else { - memchr_aligned(x, text) - }; + let result = + if text.len() < 2 * USIZE_BYTES { memchr_naive(x, text) } else { memchr_aligned(x, text) }; if let Some(index) = result { // SAFETY: Both implementations only return an index from within `text`. unsafe { crate::hint::assert_unchecked(index < text.len()) }; diff --git a/tests/codegen-llvm/lib-optimizations/memchr-result.rs b/tests/codegen-llvm/lib-optimizations/memchr-result.rs index f18335075451c..77abc33adde83 100644 --- a/tests/codegen-llvm/lib-optimizations/memchr-result.rs +++ b/tests/codegen-llvm/lib-optimizations/memchr-result.rs @@ -1,6 +1,6 @@ // Ensure `memchr` communicates that a returned index is in bounds. //@ compile-flags: -Copt-level=3 -Zinline-mir=false -//@ only-64bit +//@ only-x86_64 #![crate_type = "lib"] #![feature(slice_internals)] From 807750a1fcdb31f4bf527089ff44cf95ac199046 Mon Sep 17 00:00:00 2001 From: SomeFlyingThing <306498559+SomeFlyingThing@users.noreply.github.com> Date: Tue, 28 Jul 2026 20:57:45 +0000 Subject: [PATCH 5/5] Preserve memchr codegen on LLVM 21 --- library/core/src/slice/memchr.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index fb99e86139d7e..68826ecac31f3 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -24,10 +24,18 @@ const fn contains_zero_byte(x: usize) -> bool { #[must_use] pub const fn memchr(x: u8, text: &[u8]) -> Option { // Fast path for small slices. - let result = - if text.len() < 2 * USIZE_BYTES { memchr_naive(x, text) } else { memchr_aligned(x, text) }; + if text.len() < 2 * USIZE_BYTES { + let result = memchr_naive(x, text); + if let Some(index) = result { + // SAFETY: `memchr_naive` only returns an index from within `text`. + unsafe { crate::hint::assert_unchecked(index < text.len()) }; + } + return result; + } + + let result = memchr_aligned(x, text); if let Some(index) = result { - // SAFETY: Both implementations only return an index from within `text`. + // SAFETY: `memchr_aligned` only returns an index from within `text`. unsafe { crate::hint::assert_unchecked(index < text.len()) }; } result