Change:
Consider this code: This compiles to just: () However, changing any of the to breaks this optimization and a bunch of cmps are generated as well as the addition, which seems unnecessary in this case, because / shouldn't make any difference as short circuit can't be observed. () This works fine with Doesn't seem like an LLVM issue (to me?). : correctly gets optimized to this with () <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
claim
label +T-compiler +A-codegen +I-slow
Seems like I misunderstood what the "Build LLVM IR" button on the Rust playground does (I thought it shows the LLVM IR that rustc emits and not the resulting, final IR after optimization passes). It looks like LLVM started recognizing this pattern in version 13 (I'm assuming opt 13.0.0 on godbolt means it uses LLVM version 13): Though that still doesn't explain why this doesn't happen when compiled with rust. I'm on 1.66-nightly and that uses LLVM version 15.0.2 as far as I can tell (), so presumably it should be able to recognize this pattern and further reduce it to like it does with opt 13. Does do additional passes that rustc doesn't do that could prevent this?
This pattern is currently only handled by IPSCCP, which only runs very early. CVP doesn't handle it, because it currently only optimizes conditions with a constant operand. In principle, it can be extended to handle this with some extensions to LVI. The alternative is to get the IR into the necessary form prior to IPSCCP. I believe in this case the problem is that early SimplifyCFG runs before SROA, and we need SROA to simplify the conditions stored into variables first. Swapping the passes would probably fix this, but I vaguely remember there being some complication with doing that.
The reason this doesn't work is that we currently rely on SimplifyCFG not optimizing branches into selects this early, so IPSCCP still sees proper branches. Would have to add an extra option to disables this in the early SimplifyCFG run.
Upstream patch:
Fixed by LLVM 16 upgrade.

Discussion:
<ins> // compile-flags: -O // min-llvm-version: 16 #![crate_type = "lib"] #[no_mangle] pub fn all_zero(data: &[u64]) -> bool { // CHECK-LABEL: @all_zero( // CHECK: [[PHI:%.*]] = phi i1 // CHECK-NOT: phi i8 // CHECK-NOT: zext data.iter().copied().fold(true, |acc, x| acc & (x == 0)) } </ins>

<ins> // compile-flags: -O // min-llvm-version: 16 // ignore-debug: the debug assertions get in the way #![crate_type = "lib"] #[no_mangle] pub fn test() -> usize { // CHECK-LABEL: @test( // CHECK: ret {{i64|i32}} 165 let values = [23, 16, 54, 3, 60, 9]; let mut acc = 0; for item in values { acc += item; } acc } </ins>

<ins> // compile-flags: -O // min-llvm-version: 16 // ignore-debug: the debug assertions get in the way #![crate_type = "lib"] #[no_mangle] pub fn test(a: [i32; 10]) -> i32 { // CHECK-LABEL: @test( // CHECK: [[L1:%.+]] = load i32 // CHECK: [[L2:%.+]] = load i32 // CHECK: [[R:%.+]] = add i32 [[L1]], [[L2]] // CHECK: ret i32 [[R]] let mut sum = 0; for v in a.iter().skip(8) { sum += v; } sum } </ins>

<ins> // compile-flags: -O -C overflow-checks // min-llvm-version: 16 #![crate_type = "lib"] #[no_mangle] pub fn test(arr: &[u8], weight: u32) { // CHECK-LABEL: @test( // CHECK-NOT: panic let weight = weight.min(256 * 256 * 256); for x in arr { assert!(weight <= 256 * 256 * 256); let result = *x as u32 * weight; } } </ins>

<ins> // compile-flags: -O // min-llvm-version: 16 #![crate_type = "lib"] #[no_mangle] pub fn test(a: i32, b: i32) -> bool { // CHECK-LABEL: @test( // CHECK: ret i1 true let c1 = (a >= 0) && (a <= 10); let c2 = (b >= 0) && (b <= 20); if c1 & c2 { a + 100 != b } else { true } } </ins>

<ins> // compile-flags: -O // min-llvm-version: 16 #![crate_type = "lib"] #[no_mangle] pub fn test() -> u32 { // CHECK-LABEL: @test( // CHECK: ret i32 13 let s = [1, 2, 3, 4, 5, 6, 7]; let mut iter = s.iter(); let mut sum = 0; while let Some(_) = iter.next() { sum += iter.next().map_or(1, |&x| x) } sum } </ins>

<ins> // compile-flags: -O // min-llvm-version: 16 #![crate_type = "lib"] #[no_mangle] pub fn test(dividend: i64, divisor: i64) -> Option<i64> { // CHECK-LABEL: @test( // CHECK-NOT: panic if dividend > i64::min_value() && divisor != 0 { Some(dividend / divisor) } else { None } } </ins>