Skip to content

core: use the platform's memchr#159090

Draft
joboet wants to merge 1 commit into
rust-lang:mainfrom
joboet:memchr-compiler-builtins
Draft

core: use the platform's memchr#159090
joboet wants to merge 1 commit into
rust-lang:mainfrom
joboet:memchr-compiler-builtins

Conversation

@joboet

@joboet joboet commented Jul 10, 2026

Copy link
Copy Markdown
Member

The custom, SWAR-based memchr implementation is in many cases slower than the platform's own implementation. This is especially the case on GNU/Linux as glibc will use IFUNCs to select the most efficient implementation at link time. Thus, this PR makes core unconditionally use the platform memchr.

Although one could also imagine including the memchr crate into core, platforms are in my opinion in a much better place to optimise their implementation – even some don't – since they can e.g. update their dynamically-linked libc to take advantage of new target features and know better than us whether to optimise for binary-size or performance.

Just like #94079 did, this PR adds a new symbol to the ones required by core. Given that memchr is in the C standard just like strlen is, I expect every platform that already provides the other symbols will also provide memchr. This PR also adds a reasonably optimised memchr implementation to compiler_builtins so that all targets that use the memory routines contained therein will continue to work.

r? @tgross35

@rustbot

rustbot commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

compiler-builtins is developed in its own repository. If possible, consider making this change to rust-lang/compiler-builtins instead.

cc @tgross35

⚠️ #[rustc_allow_const_fn_unstable] needs careful audit to avoid accidentally exposing unstable
implementation details on stable.

cc @rust-lang/wg-const-eval

@rustbot rustbot added A-compiler-builtins Area: compiler-builtins (https://github.com/rust-lang/compiler-builtins) S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jul 10, 2026
@rustbot

rustbot commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

tgross35 is currently at their maximum review capacity.
They may take a while to respond.

// describe a valid memory region. Since the reference is a `&[u8]`,
// every byte contained therein is interpretable as an initialized
// byte.
let res = unsafe { memchr(text.as_ptr().cast(), x as c_int, text.len()) };

@RalfJung RalfJung Jul 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the slice is empty, it might legally be a no-provenance pointer. C requires the memchr pointer to always be valid, even if the length is 0. In that case, this call would cause UB (that Miri would flag).

For some of the other operations, we assume that they work on arbitrary pointers if the length is 0, and there are recent proposals to the C standard to officially bless this. Do those proposals cover memchr as well?

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have though the validity requirement was only for null pointers, which cannot occur here since the pointer comes from a slice. In any case, N3322 covers memchr.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have though the validity requirement was only for null pointers, which cannot occur here since the pointer comes from a slice.

In C it is UB to even create a pointer that doesn't point to an allocation (except for null), so I see no way to argue that it would be allowed to pass such a pointer when even the much-more-well-defined null pointer is disallowed.

In any case, N3322 covers memchr.

Okay, good.
Still, please add this to the libcore crate-level docs as an assumption we make, since it's not yet in a published standard.

And we have to figure out what to do with Miri. Currently, if you directly invoke memcpy or any of the others in Miri, it still enforces the C23 rules. Only if you directly invoke Rust's intrinsics do we allow arbitrary dangling pointers for size 0. For memchr we thus have to either also introduce an intrinsic, or we have to make Miri implement the rules of https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3322.pdf in the hopes that the committee will include them in the next standard. Do you have any idea how far along that proposal is through the process? (Cc @nikic)

@rust-log-analyzer

Copy link
Copy Markdown
Collaborator

The job x86_64-gnu-next-trait-solver-polonius failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
[RUSTC-TIMING] build_script_build test:false 0.255
error[E0425]: cannot find value `ptr` in this scope
  --> library/core/src/slice/memchr.rs:63:63
   |
63 |                 let index = unsafe { res.offset_from_unsigned(ptr) };
   |                                                               ^^^ not found in this scope

For more information about this error, try `rustc --explain E0425`.
[RUSTC-TIMING] core test:false 24.808
error: could not compile `core` (lib) due to 1 previous error

Important

For more information how to resolve CI failures of this job, visit this link.

@tgross35 tgross35 Jul 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The c-b part should land as a PR to rust-lang/compiler-builtins first. We're not running all its checks and benchmarks in r-l/r

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I think this would be good to add regardless of whether or not we use it, if only for the purpose of helping no-std builds)

@joboet joboet marked this pull request as draft July 10, 2026 14:59
@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 10, 2026
@tgross35

Copy link
Copy Markdown
Contributor

Nominating for @rust-lang/libs because there are some tradeoffs here. Notably in #t-libs > Using the platform's memchr, the possibility of using the memchr crate as a submodule was brought up instead.

The top post says:

Although one could also imagine including the memchr crate into core, platforms are in my opinion in a much better place to optimise their implementation – even some don't – since they can e.g. update their dynamically-linked libc to take advantage of new target features and know better than us whether to optimise for binary-size or performance.

We could also use the memchr crate now and switch to the platform versions if/when they catch up. Using the crate just seems like an easier way to give everyone the same performance - if there are platforms that have gone the past decade with an underoptimized memchr, I'm not sure there's really any reason to expect that to change.

@rustbot label +I-libs-nominated

@rustbot rustbot added the I-libs-nominated Nominated for discussion during a libs team meeting. label Jul 10, 2026
@BurntSushi

Copy link
Copy Markdown
Member

IMO, the memchr crate is the superior option here. I'm biased of course, but I think this PR is probably understating how little the platform memchr can be optimized. e.g., I think musl is just limited to the SWAR approach. And I don't know what Windows does.

In contrast, the memchr crate should have a great implementation on more platforms. That includes x86-64, aarch64 and wasm32. For other platforms, it falls back to SWAR.

Here are some benchmarks comparing the memchr crate on x86-64:

[andrew@duff memchr]$ rebar cmp ./benchmarks/record/aarch64/2023-12-29.csv -e 'libc/memchr' -e 'rust/memchr/memchr/oneshot' -e 'rust/memchr/memchr/prebuilt'
benchmark                          libc/memchr/oneshot  rust/memchr/memchr/oneshot  rust/memchr/memchr/prebuilt
---------                          -------------------  --------------------------  ---------------------------
memchr/sherlock/common/huge1       1200.7 MB/s (1.33x)  1490.7 MB/s (1.07x)         1594.1 MB/s (1.00x)
memchr/sherlock/common/small1      2.1 GB/s (1.17x)     2.5 GB/s (1.00x)            2.5 GB/s (1.00x)
memchr/sherlock/common/tiny1       1566.8 MB/s (1.02x)  1566.8 MB/s (1.02x)         1605.0 MB/s (1.00x)
memchr/sherlock/never/huge1        44.3 GB/s (1.67x)    73.9 GB/s (1.00x)           73.9 GB/s (1.00x)
memchr/sherlock/never/small1       15.1 GB/s (41.00x)   618.4 GB/s (1.00x)          618.4 GB/s (1.00x)
memchr/sherlock/never/tiny1        64.3 GB/s (1.00x)    64.3 GB/s (1.00x)           64.3 GB/s (1.00x)
memchr/sherlock/never/empty1       1.00ns (1.00x)       1.00ns (1.00x)              1.00ns (1.00x)
memchr/sherlock/rare/huge1         38.0 GB/s (1.63x)    61.6 GB/s (1.00x)           61.8 GB/s (1.00x)
memchr/sherlock/rare/small1        15.1 GB/s (41.00x)   618.4 GB/s (1.00x)          618.4 GB/s (1.00x)
memchr/sherlock/rare/tiny1         64.3 GB/s (1.00x)    64.3 GB/s (1.00x)           64.3 GB/s (1.00x)
memchr/sherlock/uncommon/huge1     5.5 GB/s (1.21x)     4.9 GB/s (1.36x)            6.6 GB/s (1.00x)
memchr/sherlock/uncommon/small1    14.7 GB/s (1.02x)    14.7 GB/s (1.02x)           15.1 GB/s (1.00x)
memchr/sherlock/uncommon/tiny1     64.3 GB/s (1.00x)    64.3 GB/s (1.00x)           64.3 GB/s (1.00x)
memchr/sherlock/verycommon/huge1   535.3 MB/s (1.81x)   881.4 MB/s (1.10x)          970.7 MB/s (1.00x)
memchr/sherlock/verycommon/small1  844.3 MB/s (1.29x)   950.8 MB/s (1.14x)          1086.2 MB/s (1.00x)

And aarch64:

[andrew@duff memchr]$ rebar cmp ./benchmarks/record/x86_64/2025-09-25.csv -e 'libc/memchr' -e 'rust/memchr/memchr/oneshot' -e 'rust/memchr/memchr/prebuilt'
benchmark                          libc/memchr/oneshot  rust/memchr/memchr/oneshot  rust/memchr/memchr/prebuilt
---------                          -------------------  --------------------------  ---------------------------
memchr/sherlock/common/huge1       3.2 GB/s (1.04x)     3.1 GB/s (1.07x)            3.3 GB/s (1.00x)
memchr/sherlock/common/small1      3.4 GB/s (1.10x)     3.4 GB/s (1.10x)            3.7 GB/s (1.00x)
memchr/sherlock/common/tiny1       1241.6 MB/s (1.04x)  1218.6 MB/s (1.06x)         1290.3 MB/s (1.00x)
memchr/sherlock/never/huge1        133.8 GB/s (1.01x)   135.1 GB/s (1.00x)          135.1 GB/s (1.00x)
memchr/sherlock/never/small1       44.2 GB/s (1.00x)    41.2 GB/s (1.07x)           41.2 GB/s (1.07x)
memchr/sherlock/never/tiny1        5.4 GB/s (1.00x)     4.9 GB/s (1.08x)            4.9 GB/s (1.08x)
memchr/sherlock/never/empty1       11.00ns (1.00x)      11.00ns (1.00x)             12.00ns (1.09x)
memchr/sherlock/rare/huge1         109.9 GB/s (1.04x)   112.8 GB/s (1.01x)          114.5 GB/s (1.00x)
memchr/sherlock/rare/small1        32.5 GB/s (1.06x)    32.5 GB/s (1.06x)           34.4 GB/s (1.00x)
memchr/sherlock/rare/tiny1         4.3 GB/s (1.00x)     4.3 GB/s (1.00x)            4.3 GB/s (1.00x)
memchr/sherlock/uncommon/huge1     16.2 GB/s (1.00x)    11.5 GB/s (1.41x)           11.6 GB/s (1.40x)
memchr/sherlock/uncommon/small1    13.4 GB/s (1.05x)    13.4 GB/s (1.05x)           14.1 GB/s (1.00x)
memchr/sherlock/uncommon/tiny1     2.2 GB/s (1.00x)     2.0 GB/s (1.10x)            2.1 GB/s (1.03x)
memchr/sherlock/verycommon/huge1   1436.4 MB/s (1.11x)  1451.5 MB/s (1.10x)         1599.3 MB/s (1.00x)
memchr/sherlock/verycommon/small1  1469.2 MB/s (1.09x)  1455.7 MB/s (1.10x)         1607.2 MB/s (1.00x)

@Kobzol

Kobzol commented Jul 11, 2026

Copy link
Copy Markdown
Member

@bors try @rust-timer queue

@rust-timer

Copy link
Copy Markdown
Collaborator

Awaiting bors try build completion.

@rustbot label: +S-waiting-on-perf

@rust-bors

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Jul 11, 2026
rust-bors Bot pushed a commit that referenced this pull request Jul 11, 2026
@rust-bors

rust-bors Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

💔 Test for bb887d8 failed: CI. Failed job:

@rust-log-analyzer

Copy link
Copy Markdown
Collaborator

The job dist-x86_64-linux-quick failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 1`
##[endgroup]
[2026-07-11T13:26:43.881Z INFO  opt_dist::timer] Section `Stage 1 (Rustc PGO)` starts
[2026-07-11T13:26:43.881Z INFO  opt_dist::timer] Section `Stage 1 (Rustc PGO) > Build PGO instrumented rustc and LLVM` starts
[2026-07-11T13:26:43.881Z INFO  opt_dist::exec] Executing `RUST_BACKTRACE=full python3 /checkout/x.py build --target x86_64-unknown-linux-gnu --host x86_64-unknown-linux-gnu --stage 2 library/std --set rust.llvm-bitcode-linker=false --set build.extended=false --set rust.codegen-backends=['llvm'] --set rust.deny-warnings=false --set pgo.rustc.generate="/tmp/tmp-multistage/opt-artifacts/rustc-pgo" --set llvm.thin-lto=false --set llvm.link-shared=true [at /checkout/obj]`
##[group]Building bootstrap
    Finished `dev` profile [unoptimized] target(s) in 0.06s
##[endgroup]
[TIMING:start] compile::Assemble { target_compiler: Compiler { stage: 2, host: x86_64-unknown-linux-gnu, forced_compiler: false } }
[TIMING:start] builder::Libdir { compiler: Compiler { stage: 2, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu }
---
[RUSTC-TIMING] build_script_build test:false 0.273
error[E0425]: cannot find value `ptr` in this scope
  --> /rustc/bb887d8ddb734d012807b54778bfbed7a92abfee/library/core/src/slice/memchr.rs:63:63
   |
63 |                 let index = unsafe { res.offset_from_unsigned(ptr) };
   |                                                               ^^^ not found in this scope

[RUSTC-TIMING] profiler_builtins test:false 0.022
For more information about this error, try `rustc --explain E0425`.
[RUSTC-TIMING] core test:false 14.265
error: could not compile `core` (lib) due to 1 previous error
Bootstrap failed while executing `build --target x86_64-unknown-linux-gnu --host x86_64-unknown-linux-gnu --stage 2 library/std --set rust.llvm-bitcode-linker=false --set build.extended=false --set rust.codegen-backends=['llvm'] --set rust.deny-warnings=false --set pgo.rustc.generate="/tmp/tmp-multistage/opt-artifacts/rustc-pgo" --set llvm.thin-lto=false --set llvm.link-shared=true`
Currently active steps:
compile::Assemble { target_compiler: Compiler { stage: 2, host: x86_64-unknown-linux-gnu, forced_compiler: false } } at src/bootstrap/src/core/build_steps/compile.rs:142
compile::Rustc { target: x86_64-unknown-linux-gnu, build_compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu, forced_compiler: false }, crates: [] } at src/bootstrap/src/core/build_steps/compile.rs:2332
compile::Std { target: x86_64-unknown-linux-gnu, build_compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu, forced_compiler: false }, crates: [], force_recompile: false, extra_rust_args: [], is_for_mir_opt_tests: false } at src/bootstrap/src/core/build_steps/compile.rs:1080
Build completed unsuccessfully in 0:09:56
---
[2026-07-11T13:36:40.309Z INFO  opt_dist::utils] Free disk space: 1.27 TiB out of total 2.51 TiB (49.21% used)
Error: Optimized build pipeline has failed

Caused by:
    Command RUST_BACKTRACE=full python3 /checkout/x.py build --target x86_64-unknown-linux-gnu --host x86_64-unknown-linux-gnu --stage 2 library/std --set rust.llvm-bitcode-linker=false --set build.extended=false --set rust.codegen-backends=['llvm'] --set rust.deny-warnings=false --set pgo.rustc.generate="/tmp/tmp-multistage/opt-artifacts/rustc-pgo" --set llvm.thin-lto=false --set llvm.link-shared=true [at /checkout/obj] has failed with exit code Some(1)

Stack backtrace:
   0: <anyhow::Error>::msg::<alloc::string::String>
             at /rust/deps/anyhow-1.0.102/src/backtrace.rs:10:14
   1: <opt_dist::exec::CmdBuilder>::run
             at /rustc/bb887d8ddb734d012807b54778bfbed7a92abfee/src/tools/opt-dist/src/exec.rs:81:17
   2: <opt_dist::exec::Bootstrap>::run
             at /rustc/bb887d8ddb734d012807b54778bfbed7a92abfee/src/tools/opt-dist/src/exec.rs:209:18
   3: opt_dist::execute_pipeline::{closure#1}::{closure#0}
             at /rustc/bb887d8ddb734d012807b54778bfbed7a92abfee/src/tools/opt-dist/src/main.rs:256:21
   4: <opt_dist::timer::TimerSection>::section::<opt_dist::execute_pipeline::{closure#1}::{closure#0}, ()>
             at /rustc/bb887d8ddb734d012807b54778bfbed7a92abfee/src/tools/opt-dist/src/timer.rs:111:22
   5: opt_dist::execute_pipeline::{closure#1}
             at /rustc/bb887d8ddb734d012807b54778bfbed7a92abfee/src/tools/opt-dist/src/main.rs:245:15
   6: <opt_dist::timer::TimerSection>::section::<opt_dist::execute_pipeline::{closure#1}, opt_dist::training::RustcPGOProfile>
             at /rustc/bb887d8ddb734d012807b54778bfbed7a92abfee/src/tools/opt-dist/src/timer.rs:111:22
   7: opt_dist::execute_pipeline
             at /rustc/bb887d8ddb734d012807b54778bfbed7a92abfee/src/tools/opt-dist/src/main.rs:242:35
   8: opt_dist::main
             at /rustc/bb887d8ddb734d012807b54778bfbed7a92abfee/src/tools/opt-dist/src/main.rs:469:18
   9: <fn() -> core::result::Result<(), anyhow::Error> as core::ops::function::FnOnce<()>>::call_once
             at /rustc/0417c25868d6dfbd1c291dfeae950504faa6f790/library/core/src/ops/function.rs:250:5
  10: std::sys::backtrace::__rust_begin_short_backtrace::<fn() -> core::result::Result<(), anyhow::Error>, core::result::Result<(), anyhow::Error>>
             at /rustc/0417c25868d6dfbd1c291dfeae950504faa6f790/library/std/src/sys/backtrace.rs:166:18
  11: std::rt::lang_start::<core::result::Result<(), anyhow::Error>>::{closure#0}
             at /rustc/0417c25868d6dfbd1c291dfeae950504faa6f790/library/std/src/rt.rs:206:18
  12: <&dyn core::ops::function::Fn<(), Output = i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe as core::ops::function::FnOnce<()>>::call_once
             at /rustc/0417c25868d6dfbd1c291dfeae950504faa6f790/library/core/src/ops/function.rs:287:21
  13: std::panicking::catch_unwind::do_call::<&dyn core::ops::function::Fn<(), Output = i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe, i32>
             at /rustc/0417c25868d6dfbd1c291dfeae950504faa6f790/library/std/src/panicking.rs:581:40
  14: std::panicking::catch_unwind::<i32, &dyn core::ops::function::Fn<(), Output = i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe>
             at /rustc/0417c25868d6dfbd1c291dfeae950504faa6f790/library/std/src/panicking.rs:544:19
  15: std::panic::catch_unwind::<&dyn core::ops::function::Fn<(), Output = i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe, i32>
             at /rustc/0417c25868d6dfbd1c291dfeae950504faa6f790/library/std/src/panic.rs:359:14
  16: std::rt::lang_start_internal::{closure#0}
             at /rustc/0417c25868d6dfbd1c291dfeae950504faa6f790/library/std/src/rt.rs:175:24
  17: std::panicking::catch_unwind::do_call::<std::rt::lang_start_internal::{closure#0}, isize>
             at /rustc/0417c25868d6dfbd1c291dfeae950504faa6f790/library/std/src/panicking.rs:581:40

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-compiler-builtins Area: compiler-builtins (https://github.com/rust-lang/compiler-builtins) I-libs-nominated Nominated for discussion during a libs team meeting. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. S-waiting-on-perf Status: Waiting on a perf run to be completed. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants