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

Discussion:
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.

<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. --I tried this code: I expected to see this happen: all three versions of the loop body have no overflow checks and are optimized out. Instead, this happened: has no overflow checks whereas the two other versions do have overflow checks on 1.64.0 and below, and on beta/nightly all versions have overflow checks. Also, uncommenting line , which makes bound one smaller, makes all three versions have no overflow checks on both 1.64.0 and nightly, which is extra weird because the bounds are satisfied with either value.
IR test case: I'd generally expect CVP to handle this, but: Apparently, LVI fails to determine the range of a value that is used inside a loop. Sinking the umin into does fold. There's two possible fixes here. One would be to support this in LVI. The other would be to add with.overflow support to SCCP.
Upstream patch:
Fixed by the LLVM 16 upgrade.

<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. --I tried this code: I expected to see this happen: The loop should be inlined because is const-known. For example, here is the generated assembly on Rust 1.63.0 () and Rust Beta (): Instead, this happened: On nightly, the loop is inlined. However, the entire function is not computed despite the stable and beta Rust inlining it. Here is the generated assembly on Nightly (): n/a - disclosed at usages Keywords: const inline loop, const loop, const loop not evaluated Relevant discord thread:
label regression-from-stable-to-nightly
label I-slow
WG-prioritization assigning priority (). label -I-prioritize +P-high t-compiler
Partial fix: This at least allows the loop to be unrolled, though it only gets fully constant folded in the backend.
Oh my, if it isn't yet another casualty of LLVM's dumb typed GEP representation. This might get fixed by as well.
This turns out to be surprisingly tricky due to phase ordering issues. Even with canonical GEPs, the loop bounds only become known after full unrolling, by which point it is too late. I think the best shot at this would be to generalize LICM scalar promotion to perform load promotion in the presence of potentially aliasing loads. is preparation to move in that direction.
for improved LICM promotion. Still needs to be combined with canonical GEPs to work.
what are your thoughts on this ticket? Your was merged, but the other LLVM changes have not. Meanwhile, it seems like has stalled, but nikic r+'ed it apart from wanting a FIXME removed, does that sound right? (I went ahead and a commit to remove the FIXME.) And its not clear whether would actually resolve this, is that correct?
We need both and to fully resolve this, as far as I remember. We should probably land ASAP to give us a chance to address regressions from it in LLVM 16.
Fixed by the LLVM 16 upgrade.

I tried this code: expected to see this happen: Instead, this happened: There are two unnecessary instructions: It most recently worked on: Rust 1.63, Rust beta : modify labels: +regression-from-stable-to-nightly -regression-untriaged
Related (maybe):
The "canonical" code produces identical (and, since it has early exit, much more performant in most cases) assemblies between versions.
But constant-time functions can not early exit. The "fold bool" variant should also be optimized when compiled with nighly. So I think it's regression.
Upstream issue:
Aside: I think our labels here are a bit too general; while the text for "regression-from-stable-to-nightly" label itself says "Performance or correctness regression from stable to nightly", I think it should probably be revised to be restricted to "correctness or severe performance regression ..." label: -regression-from-stable-to-nightly
I'm putting back the label for now; I want to get more consensus amongst the team about what the labels should be used for and what they denote (and how to get a narrower view when one wants to filter out cases like this) label: +regression-from-stable-to-nightly
Fixed upstream and will be pulled in with the next LLVM major version upgrade. I'm downgrading the priority as there's no point in revisiting this in triage until that time.
Fixed by the LLVM 16 upgrade.

The following code (contrived for demonstration purposes) correctly checks for and avoids a division overflow condition ( input coerced to output) but a regression in rustc 1.56.0 (still in latest nightly and latest 1.62.0 release) causes the optimizer to no longer skip the checked division and panic branch: label +regression-from-stable-to-stable +A-codegen +A-llvm +I-heavy +I-slow +T-compiler Edit: It might be worth adding some regression tests for this because it's happened and then was fixed a few times before (e.g. introduced in 1.47.0 and fixed in 1.48.0).
This bisects to something between 2021-08-21 and 2021-08-22 in the range of to , which includes (the upgrade to llvm 13), which is the most likely culprit.
The only difference in our unoptimized LLVMIR is that we now emit noundef, which enables other optimizations. Typically we do not add regression tests to Rust's test suite based purely on LLVM optimizations, and this regression does seem to be due to that. Even if we wanted one here, there's so many valid code emissions and optimizations for division, I'm not seeing a simple path forward for a regression test here. But we will be upgrading LLVM soon (work has commenced in ).
Thanks for clarifying - I wasn't sure what was in-scope and what would purely be considered an upstream issue without any workarounds on our end. I should have checked and included that info in my original bug report - I've made a mental note to do that in the future. I look forward to seeing if the migration to LLVM 15 will address this. In the meantime, I suppose this is what was made for!
Still fails to optimize on current LLVM: It does work if the first logical and is replaced with a bitwise and, so something is missing logical and handling.
It's a regression, so I see some value in tracking it on our issue tracker, I just don't see much value in a specific test for it because for the optimized forms of the LLVMIR, I thiiiink the only thing we could really do is check that bb6 is not present:
Even this simple case doesn't get optimized:
WG-prioritization assigning priority ().

label -I-prioritize +P-low
Fixed since 1.68, though I don't know specifically what fixed it.
thanks! Tentatively closing the issue. Please report back if you still experience the issue
Thanks for the update and the codegen regression tests.

I'm trying to figure out why certain iterator-based algorithms get const-optimized, while others don't and I stumbled upon this case. (updated 2021.03.05) Compiling results in the expected machine code, but has extra stack reserve/free instructions that are not necessary, since the function's contents are optimized away completely. The stack size equals the size of the array.
Two observations: in this case optimize code better than Just like , adding to array in fn2 also make it optimizable.
This would very likely be fixed by
Fixed by
There should be codegen test for this issue.
Yeah, right. I can add those for the issues I closed.
Actually, I'm reopening this one as the issue stands on ARM:
Looks like a phase ordering problem. The remaining IR would be eliminated by GVN + InstCombine. Currently it only gets eliminated by DAGCombine, thus you still see the prologue/epilogue on ARM.
The problem here is that this loop only gets unrolled during full loop unrolling, while we need it to be unrolled during simple unrolling to still have a chance to optimize. For the IR at that point, SCEV cannot determine the loop trip count:
Fixed by the LLVM 16 upgrade.