Change:
Update: per , this will be fixed with LLVM 19 (Context: this is a minimized example of the behaviour I hit while working on ) I tried this code: expected to see this happen: the code would read and bump the pointer in the iterator without checking the end, something like Instead, this happened: it still checks the iterator ending condition Dunno where the problem is here -- could be LLVM, could be how the functions are written, could be how MIR handles things... LLVM after optimizing: Looks like CSE figured out that it's assuming the same thing as the condition in , but nothing moved it up to even though that block dominates the assume? If it had, then I think the the branch and compare might have properly disappeared? <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
It's not sufficient for the block to dominate, we also need the assume to post-dominate and there to be no implicit control flow. I think from the LLVM side, the better way to look at this is that the edge to the end block is dead because it would result in assume(false). One could interpret this is a degenerate jump threading optimization.
Hi, I'm pretty new to rust, so please forgive me if this sounds ignorant, but how did you get the asm equivalent of your code?
there are various ways to do that for small snippets you can use or pass to rustc extract that via from a binary or use some other disassembler
thanks! Just tried out and my mind is blown lol. Thanks again
Upstream patch: claim
Upstream issue: label llvm-fixed-upstream
LLVM 19 no longer seems to solve this problem, and I suspect it has something to do with inline changes. Still keeping llvm-fixed-upstream, I'll look into that this week.
Bisected to: New upstream issue: label -llvm-fixed-upstream
label +llvm-fixed-upstream
Confirmed fixed by , needs codegen test.
We can finally close it.
I appreciate your perseverance here, !

Discussion:
<ins> //@ compile-flags: -O //@ min-llvm-version: 19 // Test for #107681. // Make sure we don't create `br` or `select` instructions. #![crate_type = "lib"] use std::iter::Copied; use std::slice::Iter; #[no_mangle] pub unsafe fn foo(x: &mut Copied<Iter<'_, u32>>) -> u32 { // CHECK-LABEL: @foo( // CHECK-NOT: br // CHECK-NOT: select // CHECK: [[RET:%.*]] = load i32, ptr // CHECK-NEXT: ret i32 [[RET]] x.next().unwrap_unchecked() } </ins>

<ins> //@ compile-flags: -O //@ min-llvm-version: 19 //@ only-x86_64 // Test for #118306. // Make sure we don't create `br` or `select` instructions. #![crate_type = "lib"] #[no_mangle] pub fn branchy(input: u64) -> u64 { // CHECK-LABEL: @branchy( // CHECK-NEXT:  start: // CHECK-NEXT:    [[_2:%.*]] = and i64 [[INPUT:%.*]], 3 // CHECK-NEXT:    [[SWITCH_GEP:%.*]] = getelementptr inbounds [4 x i64], ptr @switch.table.branchy, i64 0, i64 [[_2]] // CHECK-NEXT:    [[SWITCH_LOAD:%.*]] = load i64, ptr [[SWITCH_GEP]] // CHECK-NEXT:    ret i64 [[SWITCH_LOAD]] match input % 4 { 1 | 2 => 1, 3 => 2, _ => 0, } } </ins>

<ins> //@ compile-flags: -Copt-level=s //@ min-llvm-version: 19 //@ only-x86_64 // Test for #126585. // Ensure that this IR doesn't have extra undef phi input, which also guarantees that this asm // doesn't have subsequent labels and unnecessary `jmp` instructions. #![crate_type = "lib"] #[no_mangle] fn checked_div_round(a: u64, b: u64) -> Option<u64> { // CHECK-LABEL: @checked_div_round // CHECK: phi // CHECK-NOT: undef // CHECK: phi // CHECK-NOT: undef match b { 0 => None, 1 => Some(a), // `a / b` is computable and `(a % b) * 2` can not overflow since `b >= 2`. b => Some(a / b + if (a % b) * 2 >= b { 1 } else { 0 }), } } </ins>