Change:
<ins> // run-pass // min-llvm-version: 13.0 // compiler-flags: -O // Regression test for issue #80309 pub fn zero(x: usize) -> usize { std::ptr::null::<i8>().wrapping_add(x) as usize - x } pub fn qux(x: &[i8]) -> i8 { x[zero(x.as_ptr() as usize)] } fn main() { let z = vec![42, 43]; println!("{}", qux(&z)); } </ins>

Discussion:
. The following code leads to in release mode. (It works fine, printing in debug mode.) Apparently, leaving the object with a , then going back into the object with and dereferencing the resulting pointer is supposed to be safe (although there is still an open issue () about properly documenting that this is safe). As discussed in the linked IRLO thread, what’s probably happening here is that LLVM realizes that the first evaluates to the null pointer, and then considers the code equivalent to something like which is then detected as UB (dereferencing some integer offset of the null pointer), hence the illegal instruction. () Errors:
Here’s a similar miscompilation, using only safe Rust code (admitted, I was playing around with this until I got some safe code to miscompile, I’m not entirely sure anymore as to why exactly this particular example gets turned into UB by the optimizer) () modify labels: C-bug, T-compiler prioritize
After a bit of godbolt testing, this seems to happen since 1.16.0, when was stabilized
Reduced:
LLVM bug report:
Simplified the example:
Assigning as discussed as part of the and removing .
As per documentation of and the expectation is that provenance of the pointers is retained, so the setting the provenance of the resulting pointer to something else (to that of the null pointer in this case) than the provenance of the original pointer seems like the bug here. Given that reasoning the fix that has landed () to LLVM smells like it could be partial and we might still be prone to the same issue if it happens through some other contrived sample code? We could potentially work-around this in Rust as well by implementing these wrapping operations in a way that discards provenance entirely for now (probably via --)?
Agreed. AFAIK, the entire reason that exist is to not have to do that.
Well, temporarily_ work around. I suspect fixing the issue at the root in LLVM might take some time.
Note that was reverted in the underlying issue is tracked at
This was later fixed by
So can we cherry-pick that into the rustc LLVM fork, or what is the usual process?
I believe this is resolved by the bump to LLVM 12.
Reopening this as it does not appear to be fixed yet :(
is the last changeset that is necessary to resolve this fully.

Given that promotion to LLVM12 happened fairly recently, we may want to backport the commit.
Hm, but that optimization doesn't sound wrong, it just sounds bad. Some other actually wrong optimization would be needed to turn this into a miscompilation.
Well, it effectively manifests a constant pointer, much like the instsimplify fixed by nikic. always has its own provenance, so this optimisation is only "correct" in absence of intermediate null pointers. In this specific example such a null pointer does manifest itself: To complete the original reproducer we then have these two instructions: So I guess if we call anything else wrong, its probably going to be .
Ah... that is interesting. It sounds potentially problematic. Usually, when I start with a pointer , if I cast it to an integer and back, that pointer should have a provenance that allows "at least as much" as the original. It should always be correct to add an integer roundtrip. That is the one thing one can rely on with ptr-int-casts, or so I thought. What you are saying is that if the pointer has physical address 0, then this is not true.
This is fixed on nightly by the LLVM 13 upgrade. Probably worth adding a test for this.
Dropping priority as the issue is fixed.