Context: Rust issue in today's 1.97 release, rust-lang/rust#159035
Rust repro with opt pipeline, https://rust.godbolt.org/z/dTPGY3rY1
Take this LLVM IR, particularly noting that %0 can be -1, 0, or 1 as conveyed by the !range attribute
define { i32, i32 } @"example[aafd2ff9cfc6cff8]::run"(ptr dead_on_return noalias nofree noundef readonly align 8 captures(none) dereferenceable(24) %c) unnamed_addr {
start:
%0 = load i64, ptr %c, align 8, !range !4, !noundef !5
%.not = icmp ne i64 %0, -1
%_3.sroa.5.0.c.sroa_idx = getelementptr inbounds nuw i8, ptr %c, i64 12
%_3.sroa.5.0.copyload = load i32, ptr %_3.sroa.5.0.c.sroa_idx, align 4
%1 = trunc nuw i64 %0 to i1
%2 = getelementptr inbounds nuw i8, ptr %c, i64 16
%_3.sroa.6.0.copyload = load i32, ptr %2, align 8
%_4.sroa.0.1 = select i1 %1, i32 %_3.sroa.6.0.copyload, i32 %_3.sroa.5.0.copyload
%_0.sroa.3.0 = select i1 %.not, i32 %_4.sroa.0.1, i32 undef
%_0.sroa.0.0 = zext i1 %.not to i32
%3 = insertvalue { i32, i32 } poison, i32 %_0.sroa.0.0, 0
%4 = insertvalue { i32, i32 } %3, i32 %_0.sroa.3.0, 1
ret { i32, i32 } %4
}
!4 = !{i64 -1, i64 2}
!5 = !{}
Today on llc trunk that compiles to the following: https://llvm.godbolt.org/z/TcY99vhjE
"example[aafd2ff9cfc6cff8]::run":
mov rcx, qword ptr [rdi]
xor eax, eax
cmp rcx, -1
setne al
mov ecx, ecx
mov edx, dword ptr [rdi + 4*rcx + 12]
ret
rcx there maps to %0, so it can be -1, 0, or 1 (as i64).
If it stayed that way, that would be fine, as the [rdi + 4*rcx + 12] would read 4 bytes from rdi + 8, rdi + 12, or rdi + 16, all of which are within the dereferenceable(24).
However, that mov ecx, ecx changes rcx to instead be 0x00000000FFFFFFFF, 0, or 1 (as i64), and thus the following mov reads massively out of bounds, causing a segfault.
Apologies in advance if I got the logic chain wrong here somewhere. I'm reasonably good at middle-end IR but worse at assembly and completely unfamiliar with backend IR.
I think this is x86-specific because it was reported that neither aarch64 nor riscv64gc hit a problem here (Rust Zulip #t-compiler > Miscompilation in 1.97.0 with new Option::None discriminant @ 💬).
Context: Rust issue in today's 1.97 release, rust-lang/rust#159035
Rust repro with opt pipeline, https://rust.godbolt.org/z/dTPGY3rY1
Take this LLVM IR, particularly noting that
%0can be-1,0, or1as conveyed by the!rangeattributeToday on llc trunk that compiles to the following: https://llvm.godbolt.org/z/TcY99vhjE
rcxthere maps to%0, so it can be-1,0, or1(as i64).If it stayed that way, that would be fine, as the
[rdi + 4*rcx + 12]would read 4 bytes fromrdi + 8,rdi + 12, orrdi + 16, all of which are within thedereferenceable(24).However, that
mov ecx, ecxchangesrcxto instead be0x00000000FFFFFFFF,0, or1(as i64), and thus the followingmovreads massively out of bounds, causing a segfault.Apologies in advance if I got the logic chain wrong here somewhere. I'm reasonably good at middle-end IR but worse at assembly and completely unfamiliar with backend IR.
I think this is x86-specific because it was reported that neither aarch64 nor riscv64gc hit a problem here (Rust Zulip #t-compiler > Miscompilation in 1.97.0 with new Option::None discriminant @ 💬).