Change:
<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>

Discussion:
The following code: Generates two subsequent labels and an unnecessary instruction: When compiled with using rustc . Godbolt: P.S. For context, I am not too familiar with asm. I did not find any similar results while searching through the rustc and llvm issue trackers for "double jump". Please let me know how I can improve the quality of my issue reporting. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
label -needs-triage +C-optimization +T-compiler +A-codegen +A-LLVM
Here are multiple solutions to this problem. It can be resolved by directly optimizing in IR () or during the code generation phase (). I believe the code generation phase is the key point, and we can use to eliminate this branch. However, I am more familiar with IR-related aspects, so let me resolve it here now.
Fixed by label +llvm-fixed-upstream claim
Confirmed fixed by , needs codegen test.
I just stumbled past this and am confused by the comment I would expect this to overflow if and ()
You're right, I made a logical error.

Consider the following functions (): These functions have identical behavior: they map to . In the former case, LLVM generates a nice lookup table for us, but in the latter, it emits an extra branch. The only difference is that I've used to avoid needing to write an unreachable-by-optimization branch. If we look at the generated IR (after ): The problem is clear: LLVM does not seem to realize that it can trivially transform to here, by observing that the default in the is only taken when . I suspect this is more LLVM bug than Rust bug, but it feels fixable by a MIR peephole optimization? Unclear. The code I wrote is an attractive nuisance that I imagine other people writing, too, so perhaps there is value to seeing if this optimization can be made before LLVM. This bug is also present in Clang, in case someone wants to file an LLVM bug: It's unclear to me if this is the sort of optimization Clang would do in the frontend instead of in LLVM; could go either-or here, tbh. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Upstream issue: claim
label llvm-fixed-upstream
It looks like this is fixed since 1.75, but I don't know what fixed it:
I don't think it has been fixed. It looks like the function is not submitted: Bisected to cc
You need to stick or on the function otherwise when CE compiles the crate the function is only lowered to MIR as if it has the attribute.
Normally I would do this. Maybe we should mention this somewhere to avoid submitting invalid code to godbolt?
Oops, sorry. I saw that one function was generated and assumed the other one got merged...
Seems like this has been reverted :/, look at the generated assembly with
Can you explain why you think that? I don't see any changes: BTW, I can reland the upstream patch now.

I think that it's been reverted because the assembly output contains this: The branch still has some branching (this can also be seen on the LLVM IR, has 12 lines and 2 jumps, while has four lines and no branches)
Ah, I think you're saying that this optimization is still in a missing state, right?
Yep, I thought you meant that you implemented the optimization, is it not implemented? :sweat_smile:
Yes, I have implemented it, but due to the compilation time issue mentioned in , I had to revert the commit. Now I have relanded it: label +llvm-fixed-upstream
Confirmed fixed by , needs codegen test.

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, !