- codegen: an arm64 sincos pairing miscompile is fixed (piano_render/audio_mix match rustc again); `&mut` field loops promote to registers; the domain base and `thread_local!` key-table reads are inline loads, not host calls - rwbench times from the moment the JIT reports optimised code settled - check: delayed bugs are internal failures only when nothing else was reported; the ahead helper backs off instead of printing a panic; suite crash rows 58 -> 20 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
111 lines
3.3 KiB
Rust
111 lines
3.3 KiB
Rust
// Fields of a `&mut` parameter that a loop loads and stores each iteration (a cursor index, a bit
|
|
// buffer) are kept in registers for the loop and written back on every way out: the normal exit,
|
|
// each early exit, the panic that unwinds out of the loop. Output matches rustc.
|
|
use std::hint::black_box;
|
|
use std::panic::{catch_unwind, AssertUnwindSafe};
|
|
|
|
struct Cursor<'a> {
|
|
chars: &'a [u32],
|
|
index: usize,
|
|
seen: u64,
|
|
wide: u8,
|
|
}
|
|
|
|
impl<'a> Cursor<'a> {
|
|
fn peek(&self) -> u32 {
|
|
self.chars.get(self.index).copied().unwrap_or(0)
|
|
}
|
|
fn skip_while(&mut self, limit: u32) -> usize {
|
|
let start = self.index;
|
|
while self.peek() != 0 && self.peek() < limit {
|
|
self.seen = self.seen.wrapping_mul(31).wrapping_add(self.peek() as u64);
|
|
self.index += 1;
|
|
}
|
|
self.index - start
|
|
}
|
|
// an early exit and a narrow field
|
|
fn skip_until(&mut self, stop: u32) -> bool {
|
|
loop {
|
|
let c = self.peek();
|
|
if c == 0 {
|
|
return false;
|
|
}
|
|
self.wide = self.wide.wrapping_add(c as u8);
|
|
if c == stop {
|
|
return true;
|
|
}
|
|
self.index += 1;
|
|
}
|
|
}
|
|
// a loop that panics in the middle: the index must be current when it unwinds
|
|
fn count_to_nine(&mut self) {
|
|
loop {
|
|
let c = self.chars[self.index];
|
|
assert!(c != 9, "nine at {}", self.index);
|
|
self.index += 1;
|
|
}
|
|
}
|
|
// an access through a pointer based on the parameter: no promotion, still right
|
|
fn through_pointer(&mut self, n: usize) {
|
|
let p: *mut usize = &mut self.index;
|
|
for _ in 0..n {
|
|
unsafe {
|
|
*p += 2;
|
|
}
|
|
self.index += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Pair {
|
|
a: f32,
|
|
n: u32,
|
|
}
|
|
|
|
fn accumulate(p: &mut Pair, xs: &[f32]) {
|
|
for x in xs {
|
|
p.a += *x * 0.5;
|
|
p.n += 1;
|
|
}
|
|
}
|
|
|
|
fn run() {
|
|
let text: Vec<u32> = (0..64).map(|i| if i == 63 { 0 } else { 1 + (i * 7) % 11 }).collect();
|
|
let mut c = Cursor { chars: &text, index: 0, seen: 0, wide: 0 };
|
|
let a = c.skip_while(9);
|
|
println!("{} {} {}", a, c.index, c.seen);
|
|
let b = c.skip_until(10);
|
|
println!("{} {} {}", b, c.index, c.wide);
|
|
let b = c.skip_until(77);
|
|
println!("{} {} {}", b, c.index, c.wide);
|
|
let mut c = Cursor { chars: &text, index: 0, seen: 0, wide: 0 };
|
|
let r = catch_unwind(AssertUnwindSafe(|| c.count_to_nine()));
|
|
println!("{} {}", r.is_err(), c.index);
|
|
let mut c = Cursor { chars: &text, index: 3, seen: 0, wide: 0 };
|
|
c.through_pointer(black_box(5));
|
|
println!("{}", c.index);
|
|
let mut p = Pair { a: 1.0, n: 0 };
|
|
accumulate(&mut p, &[1.0, 2.0, 3.5, black_box(4.0)]);
|
|
println!("{} {}", p.a, p.n);
|
|
// enough calls to be compiled by the optimiser
|
|
let mut total = 0u64;
|
|
for round in 0..3000 {
|
|
let mut c = Cursor { chars: &text, index: black_box(round % 5), seen: 0, wide: 0 };
|
|
total += c.skip_while(black_box(10)) as u64;
|
|
c.skip_until(black_box(4));
|
|
total += c.index as u64 + c.wide as u64;
|
|
let mut p = Pair { a: 0.0, n: 0 };
|
|
accumulate(&mut p, &[1.0, 2.0]);
|
|
total += p.n as u64;
|
|
}
|
|
println!("{}", total);
|
|
}
|
|
|
|
#[test]
|
|
fn loop_fields_stay_correct() {
|
|
run();
|
|
}
|
|
|
|
fn main() {
|
|
run();
|
|
}
|