Thanks for using Compiler Explorer
Sponsors
Jakt
C++
Ada
Analysis
Android Java
Android Kotlin
Assembly
C
C3
Carbon
C++ (Circle)
CIRCT
Clean
CMake
CMakeScript
COBOL
C++ for OpenCL
MLIR
Cppx
Cppx-Blue
Cppx-Gold
Cpp2-cppfront
Crystal
C#
CUDA C++
D
Dart
Elixir
Erlang
Fortran
F#
GLSL
Go
Haskell
HLSL
Hook
Hylo
IL
ispc
Java
Julia
Kotlin
LLVM IR
LLVM MIR
Modula-2
Nim
Objective-C
Objective-C++
OCaml
Odin
OpenCL C
Pascal
Pony
Python
Racket
Ruby
Rust
Snowball
Scala
Slang
Solidity
Spice
SPIR-V
Swift
LLVM TableGen
Toit
TypeScript Native
V
Vala
Visual Basic
Vyper
WASM
Zig
Javascript
GIMPLE
Ygen
rust source #1
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
mrustc (master)
rustc 1.0.0
rustc 1.1.0
rustc 1.10.0
rustc 1.11.0
rustc 1.12.0
rustc 1.13.0
rustc 1.14.0
rustc 1.15.1
rustc 1.16.0
rustc 1.17.0
rustc 1.18.0
rustc 1.19.0
rustc 1.2.0
rustc 1.20.0
rustc 1.21.0
rustc 1.22.0
rustc 1.23.0
rustc 1.24.0
rustc 1.25.0
rustc 1.26.0
rustc 1.27.0
rustc 1.27.1
rustc 1.28.0
rustc 1.29.0
rustc 1.3.0
rustc 1.30.0
rustc 1.31.0
rustc 1.32.0
rustc 1.33.0
rustc 1.34.0
rustc 1.35.0
rustc 1.36.0
rustc 1.37.0
rustc 1.38.0
rustc 1.39.0
rustc 1.4.0
rustc 1.40.0
rustc 1.41.0
rustc 1.42.0
rustc 1.43.0
rustc 1.44.0
rustc 1.45.0
rustc 1.45.2
rustc 1.46.0
rustc 1.47.0
rustc 1.48.0
rustc 1.49.0
rustc 1.5.0
rustc 1.50.0
rustc 1.51.0
rustc 1.52.0
rustc 1.53.0
rustc 1.54.0
rustc 1.55.0
rustc 1.56.0
rustc 1.57.0
rustc 1.58.0
rustc 1.59.0
rustc 1.6.0
rustc 1.60.0
rustc 1.61.0
rustc 1.62.0
rustc 1.63.0
rustc 1.64.0
rustc 1.65.0
rustc 1.66.0
rustc 1.67.0
rustc 1.68.0
rustc 1.69.0
rustc 1.7.0
rustc 1.70.0
rustc 1.71.0
rustc 1.72.0
rustc 1.73.0
rustc 1.74.0
rustc 1.75.0
rustc 1.76.0
rustc 1.77.0
rustc 1.78.0
rustc 1.79.0
rustc 1.8.0
rustc 1.80.0
rustc 1.81.0
rustc 1.82.0
rustc 1.83.0
rustc 1.9.0
rustc beta
rustc nightly
rustc-cg-gcc (master)
x86-64 GCCRS (GCC master)
x86-64 GCCRS (GCCRS master)
x86-64 GCCRS 14.1 (GCC assertions)
x86-64 GCCRS 14.1 (GCC)
x86-64 GCCRS 14.2 (GCC assertions)
x86-64 GCCRS 14.2 (GCC)
Options
Source code
#![feature(pointer_is_aligned_to)] #![feature(const_raw_ptr_comparison)] #![feature(const_pointer_is_aligned)] #![feature(const_align_offset)] use core::mem; /// Returns `true` if any byte in the word `v` is nonascii (>= 128). Snarfed /// from `../str/mod.rs`, which does something similar for utf8 validation. #[inline] const fn contains_nonascii(v: usize) -> bool { const NONASCII_MASK: usize = usize::from_ne_bytes([0x80; mem::size_of::<usize>()]); (NONASCII_MASK & v) != 0 } /// ASCII test *without* the chunk-at-a-time optimizations. /// /// This is carefully structured to produce nice small code -- it's smaller in /// `-O` than what the "obvious" ways produces under `-C opt-level=s`. If you /// touch it, be sure to run (and update if needed) the assembly test. #[inline] const fn is_ascii_simple(mut bytes: &[u8]) -> bool { while let [rest @ .., last] = bytes { if !last.is_ascii() { break; } bytes = rest; } bytes.is_empty() } /// Optimized ASCII test that will use usize-at-a-time operations instead of /// byte-at-a-time operations (when possible). /// /// The algorithm we use here is pretty simple. If `s` is too short, we just /// check each byte and be done with it. Otherwise: /// /// - Read the first word with an unaligned load. /// - Align the pointer, read subsequent words until end with aligned loads. /// - Read the last `usize` from `s` with an unaligned load. /// /// If any of these loads produces something for which `contains_nonascii` /// (above) returns true, then we know the answer is false. #[no_mangle] pub const fn is_ascii(s: &[u8]) -> bool { const USIZE_SIZE: usize = mem::size_of::<usize>(); let len = s.len(); let align_offset = s.as_ptr().align_offset(USIZE_SIZE); // If we wouldn't gain anything from the word-at-a-time implementation, fall // back to a scalar loop. // // We also do this for architectures where `size_of::<usize>()` isn't // sufficient alignment for `usize`, because it's a weird edge case. if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::<usize>() { return is_ascii_simple(s); } // We always read the first word unaligned, which means `align_offset` is // 0, we'd read the same value again for the aligned read. let offset_to_aligned = if align_offset == 0 { USIZE_SIZE } else { align_offset }; let start = s.as_ptr(); // SAFETY: We verify `len < USIZE_SIZE` above. let first_word = unsafe { (start as *const usize).read_unaligned() }; if contains_nonascii(first_word) { return false; } // We checked this above, somewhat implicitly. Note that `offset_to_aligned` // is either `align_offset` or `USIZE_SIZE`, both of are explicitly checked // above. debug_assert!(offset_to_aligned <= len); // SAFETY: word_ptr is the (properly aligned) usize ptr we use to read the // middle chunk of the slice. let mut word_ptr = unsafe { start.add(offset_to_aligned) as *const usize }; // `byte_pos` is the byte index of `word_ptr`, used for loop end checks. let mut byte_pos = offset_to_aligned; // Paranoia check about alignment, since we're about to do a bunch of // unaligned loads. In practice this should be impossible barring a bug in // `align_offset` though. // While this method is allowed to spuriously fail in CTFE, if it doesn't // have alignment information it should have given a `usize::MAX` for // `align_offset` earlier, sending things through the scalar path instead of // this one, so this check should pass if it's reachable. debug_assert!(word_ptr.is_aligned_to(mem::align_of::<usize>())); // Read subsequent words until the last aligned word, excluding the last // aligned word by itself to be done in tail check later, to ensure that // tail is always one `usize` at most to extra branch `byte_pos == len`. while byte_pos < len - USIZE_SIZE { // Sanity check that the read is in bounds debug_assert!(byte_pos + USIZE_SIZE <= len); // And that our assumptions about `byte_pos` hold. debug_assert!(matches!( word_ptr.cast::<u8>().guaranteed_eq(start.wrapping_add(byte_pos)), // These are from the same allocation, so will hopefully always be // known to match even in CTFE, but if it refuses to compare them // that's ok since it's just a debug check anyway. None | Some(true), )); // SAFETY: We know `word_ptr` is properly aligned (because of // `align_offset`), and we know that we have enough bytes between `word_ptr` and the end let word = unsafe { word_ptr.read() }; if contains_nonascii(word) { return false; } byte_pos += USIZE_SIZE; // SAFETY: We know that `byte_pos <= len - USIZE_SIZE`, which means that // after this `add`, `word_ptr` will be at most one-past-the-end. word_ptr = unsafe { word_ptr.add(1) }; } // Sanity check to ensure there really is only one `usize` left. This should // be guaranteed by our loop condition. debug_assert!(byte_pos <= len && len - byte_pos <= USIZE_SIZE); // SAFETY: This relies on `len >= USIZE_SIZE`, which we check at the start. let last_word = unsafe { (start.add(len - USIZE_SIZE) as *const usize).read_unaligned() }; !contains_nonascii(last_word) }
rust source #2
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
mrustc (master)
rustc 1.0.0
rustc 1.1.0
rustc 1.10.0
rustc 1.11.0
rustc 1.12.0
rustc 1.13.0
rustc 1.14.0
rustc 1.15.1
rustc 1.16.0
rustc 1.17.0
rustc 1.18.0
rustc 1.19.0
rustc 1.2.0
rustc 1.20.0
rustc 1.21.0
rustc 1.22.0
rustc 1.23.0
rustc 1.24.0
rustc 1.25.0
rustc 1.26.0
rustc 1.27.0
rustc 1.27.1
rustc 1.28.0
rustc 1.29.0
rustc 1.3.0
rustc 1.30.0
rustc 1.31.0
rustc 1.32.0
rustc 1.33.0
rustc 1.34.0
rustc 1.35.0
rustc 1.36.0
rustc 1.37.0
rustc 1.38.0
rustc 1.39.0
rustc 1.4.0
rustc 1.40.0
rustc 1.41.0
rustc 1.42.0
rustc 1.43.0
rustc 1.44.0
rustc 1.45.0
rustc 1.45.2
rustc 1.46.0
rustc 1.47.0
rustc 1.48.0
rustc 1.49.0
rustc 1.5.0
rustc 1.50.0
rustc 1.51.0
rustc 1.52.0
rustc 1.53.0
rustc 1.54.0
rustc 1.55.0
rustc 1.56.0
rustc 1.57.0
rustc 1.58.0
rustc 1.59.0
rustc 1.6.0
rustc 1.60.0
rustc 1.61.0
rustc 1.62.0
rustc 1.63.0
rustc 1.64.0
rustc 1.65.0
rustc 1.66.0
rustc 1.67.0
rustc 1.68.0
rustc 1.69.0
rustc 1.7.0
rustc 1.70.0
rustc 1.71.0
rustc 1.72.0
rustc 1.73.0
rustc 1.74.0
rustc 1.75.0
rustc 1.76.0
rustc 1.77.0
rustc 1.78.0
rustc 1.79.0
rustc 1.8.0
rustc 1.80.0
rustc 1.81.0
rustc 1.82.0
rustc 1.83.0
rustc 1.9.0
rustc beta
rustc nightly
rustc-cg-gcc (master)
x86-64 GCCRS (GCC master)
x86-64 GCCRS (GCCRS master)
x86-64 GCCRS 14.1 (GCC assertions)
x86-64 GCCRS 14.1 (GCC)
x86-64 GCCRS 14.2 (GCC assertions)
x86-64 GCCRS 14.2 (GCC)
Options
Source code
#[no_mangle] pub const fn is_ascii(bytes: &[u8]) -> bool { // Constant chosen to enable `pmovmskb` instruction on x86-64 const N: usize = 32; let mut i = 0; while i + N <= bytes.len() { let chunk_end = i + N; // Get LLVM to produce a `pmovmskb` instruction on x86-64 which // creates a mask from the most significant bit of each byte. // ASCII bytes are less than 128 (0x80), so their most significant // bit is unset. Thus, detecting non-ASCII bytes can be done in one // instruction. let mut count = 0; while i < chunk_end { count += (bytes[i] <= 127) as u8; i += 1; } // All bytes should be <= 127 so count is equal to chunk size. if count != N as u8 { return false; } } // Process the remaining `bytes.len() % N` bytes. let mut is_ascii = true; while i < bytes.len() { is_ascii &= bytes[i] <= 127; i += 1; } is_ascii }
Become a Patron
Sponsor on GitHub
Donate via PayPal
Source on GitHub
Mailing list
Installed libraries
Wiki
Report an issue
How it works
Contact the author
CE on Mastodon
CE on Bluesky
About the author
Statistics
Changelog
Version tree