Demonstration code:
fn main() {
let mut buf = magic_buffer::MagicBuffer::new(4096).unwrap();
let a = buf[0..4096][0];
buf[1..4097][4095] = 1;
let b = buf[0..4096][0];
println!("a = {a}");
println!("b = {b}");
let c = buf[0..4096][0];
println!("c = {c}");
}
Output in debug mode:
Output in release mode:
The key here is that we're violating LLVM's memory model (and from what I understand from @RalfJung, probably gcc's memory model as well) by aliasing the physical memory in virtual memory. Seen post-optimisation, we're basically doing this:
let a = *buf.ptr;
*buf.ptr.add(4096) = 1;
let b = *buf.ptr;
As far as LLVM is concerned, *buf.ptr and *buf.ptr.add(4096) don't alias, so this code is equivalent:
let a = *buf.ptr;
*buf.ptr.add(4096) = 1;
let b = a;
(and as for why c shows up correctly: print is heavy enough to clobber all the registers, so LLVM chooses to reload)
Demonstration code:
Output in debug mode:
Output in release mode:
The key here is that we're violating LLVM's memory model (and from what I understand from @RalfJung, probably gcc's memory model as well) by aliasing the physical memory in virtual memory. Seen post-optimisation, we're basically doing this:
As far as LLVM is concerned,
*buf.ptrand*buf.ptr.add(4096)don't alias, so this code is equivalent:(and as for why
cshows up correctly: print is heavy enough to clobber all the registers, so LLVM chooses to reload)