-
-
Notifications
You must be signed in to change notification settings - Fork 14.5k
Description
Code
Code (MCVE)
Build with
cargo rustc --release -- --emit=llvm-irThis will likely fail on linking step, but will still generate the relevant LLVM-IR output. To pass the linking step a no_std target can be passed as in
cargo rustc --release --target=riscv32imc-unknown-none-elf -- --emit=llvm-irCargo.toml
[package]
name = "test_disp"
version = "0.1.0"
edition = "2024"
[dependencies]
[profile.release]
panic = "abort"
lto = "fat"
opt-level = "z"
debug = true
codegen-units = 1src/main.rs
#![no_std]
#![no_main]
use core::fmt::{Arguments, Write};
use core::ptr;
pub struct Writer {
cout: *mut char,
}
const WRITE_INST: Writer = Writer {
// Just for illustration
cout: 0x1234 as *mut char,
};
impl Write for Writer {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
for c in s.chars() {
unsafe { ptr::write_volatile(self.cout, c); }
}
Ok(())
}
}
macro_rules! print {
($($arg:tt)*) => ($crate::_print(format_args!($($arg)*)));
}
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}
pub fn _print(args: Arguments) {
let _ = WRITE_INST.write_fmt(args);
}
#[unsafe(no_mangle)]
pub fn foo(x: usize) {
print!("{:#X?}", x);
}
#[unsafe(no_mangle)]
unsafe extern "C" fn _start() -> ! {
let mut x: usize = 0;
loop {
foo(x);
x = x+1;
}
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}Version it worked on
This code compiles without generating any panicking machinery on v1.92.0 and the nightly, including toolchain built from main (79a1e77).
Versions with regression
Stable 1.93.0 and beta :
rustc +beta --version --verbose:
rustc 1.94.0-beta.2 (23a44d3c7 2026-01-25)
binary: rustc
commit-hash: 23a44d3c70448c08dc6a2fc13c1afceab49f2bb9
commit-date: 2026-01-25
host: x86_64-unknown-linux-gnu
release: 1.94.0-beta.2
LLVM version: 21.1.8
rustc +stable --version --verbose:
rustc 1.93.0 (254b59607 2026-01-19)
binary: rustc
commit-hash: 254b59607d4417e9dffbc307138ae5c86280fe4c
commit-date: 2026-01-19
host: x86_64-unknown-linux-gnu
release: 1.93.0
LLVM version: 21.1.8
Resulting LLVM-IR
Note the portion in beta.txt which does not present in nightly.txt
.preheader: ; preds = %.preheader.preheader, %61
%24 = phi i64 [ %27, %61 ], [ 20, %.preheader.preheader ]
%25 = phi i64 [ %65, %61 ], [ %19, %.preheader.preheader ]
%26 = icmp ne i64 %24, 0, !dbg !191
tail call void @llvm.assume(i1 %26), !dbg !192
%27 = add nsw i64 %24, -4, !dbg !197
%28 = icmp ult i64 %27, 20, !dbg !183
br i1 %28, label %61, label %60, !dbg !183
//.........
60: ; preds = %.preheader
; call core::panicking::panic_bounds_check
tail call fastcc void @_ZN4core9panicking18panic_bounds_check17hb0c8fc4be015a51bE(i64 noundef -4) #13, !dbg !183
unreachable, !dbg !183
The offending code is unchanged between the versions:
rust/library/core/src/fmt/num.rs
Lines 198 to 217 in 79a1e77
| while size_of::<Self>() > 1 && remain > 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)") { | |
| // SAFETY: All of the decimals fit in buf due to MAX_DEC_N | |
| // and the while condition ensures at least 4 more decimals. | |
| unsafe { core::hint::assert_unchecked(offset >= 4) } | |
| // SAFETY: The offset counts down from its initial buf.len() | |
| // without underflow due to the previous precondition. | |
| unsafe { core::hint::assert_unchecked(offset <= buf.len()) } | |
| offset -= 4; | |
| // pull two pairs | |
| let scale: Self = 1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)"); | |
| let quad = remain % scale; | |
| remain /= scale; | |
| let pair1 = (quad / 100) as usize; | |
| let pair2 = (quad % 100) as usize; | |
| buf[offset + 0].write(DECIMAL_PAIRS[pair1 * 2 + 0]); | |
| buf[offset + 1].write(DECIMAL_PAIRS[pair1 * 2 + 1]); | |
| buf[offset + 2].write(DECIMAL_PAIRS[pair2 * 2 + 0]); | |
| buf[offset + 3].write(DECIMAL_PAIRS[pair2 * 2 + 1]); | |
| } |
If replacing the buf[offset + ..] with buf.get_unchecked_mut(offset + ...) the issue goes away.
Update:
Removing lto="fat" from Cargo.toml also changes this behavior.