Change:
I was looking at the generated asm for to see what happens to the array, and was surprised to see a large function with 5(!) call sites for . The generated code doesn't seem to vary with the number of elements in the array. That got me to compare with the functionally equivalent code: which doesn't yield the same assembly at all. The first difference being that the latter doesn't even store the array (only the strs) while the former does. That got me even further in comparing with equivalent loops, up to the extreme (and stupid): vs. The latter generates the simplest code possible: The former not so much:
Documenting my thinking in the hopes that it will be useful, or at least that someone will correct me via Campbell's Law. We know that the ASM outputs are different, so let's go back a few steps until we can find where in the toolchain we want to add our improvement. I compiled this on 1.19 with , but the important thing to note is that while iterany is quite bloated, it looks like the for loop has already been optimized: So is just as trivial in LLVM bytecode as it is in assembler - whatever optimizations we needed to do have already happened. We need to go back farther. . Notice that the tables have turned! is more concise than , so the code we need to add should change the MIR output. Note that . So the problem here seems to be that we aren't inlining before we generate LLVM bytecode. But that answer doesn't satisfy me either, because neither the nor the has any calls to , and in fact . So... one of these things is happening: 're failing to inline , and we haven't noticed because LLVM's been inlining this code for us. LLVM doesn't have as much knowledge as we do, so it can't do as good a job as we theoretically could. 've been looking at unoptimized MIR this whole time. This hypothesis seems to be borne out by the fact that when I rerun rustc without optimizations, I get exactly the same MIR output. But , and ? It's possible that both of these hypotheses are correct; maybe this is unoptimized MIR, because the MIR optimizations are doing absolutely nothing to this file! I'm still cloning rustc, but once I do I'll comment out the MIR optimizations and see where that gets us.
Well, I can oblige that in one aspect :) You put far too much hope into MIR optimizations. They do very little right now&mdash;inlining, for example, isn't even enabled by default (only with ). Even in the future, when all the MIR optimizations we've ever wanted are implemented, they will only cover a small subset of what LLVM does (there's no point in duplicating all the work that went into LLVM, and that would be impractical anyway). This code looks sufficiently messy that I don't expect MIR optimizations to ever fully optimize it away. For example, that would probably require loop unrolling, which hasn't ever been proposed AFAIK (and if it was, I'd be skeptical of it). In any case, the more interesting question is why LLVM can't optimize the iterator version as well as the alternative. Whatever goes awry in optimizing this, it happens in one of LLVM's many passes.
The LLVM IR you posted is very weird. It has multiple branches on constants, and presumably dead code consequently. There's also several branches on values?!? These should all have been cleaned up. After running it through again, I get this IR (names are all messed up cause I did it on , which insists on applying C++ name demangling): <details</detailsThat's... less code, but still very stupid. Why are the adjacent loads and stores not folded away?! A third pass (just at this point) gets rid of them, constant folds the rest, and winds up with . Looks like a nasty phase ordering issue. But what about this IR confuses LLVM so much?
Neat discovery. Should we move this discussion to LLVM's bug tracker and close this task, then?
Not necessarily, no. In the past people have tweaked the definitions of iterator methods to help the optimizer. rustc might also be able to help by changing the pass pipeline or by generating better/different IR in the first place.
This did optimize out in LLVM 3.9, and was broken by LLVM 4.0.
In more detail, LLVM 3.9 generates the following small code for ( metadata stripped - it doesn't matter, with this function substituted in both 3.9 and 4.0 manage to optimize everything out): While LLVM 4.0 manages to screw up and generate this monstrosity: I think the somehow prevents SROA.
Going closer to the root: LLVM 3.9 optimizes the closure in to this: While LLVM 4.0 for some reason generates this:
caused by
The initial nonequivalence in the filing of the bug report is due to the slice iterator's explicit manual unrolling by 4 in its implementation of any. That might seem bad but as soon as llvm learns to loop optimize a loop with two exits (either the element was found or the iterator reached the end), the explicit unrolling can be removed.
is the PR that fixed this intended to be a permanent solution, or is it just a workaround? IOW, should we open a new bug to track reevaluating this fix when LLVM is resolved?
Hmmm, mentioned this bug, but you're right, this is older so me "fixing it" is probably more coincidental than anything else.
That LLVM bug is now tracked at and appears to have had no motion on it in the past 5 years. Assembly in the initial comparison cases remains identical per No other changes, as far as I can tell.
This was nominally resolved on our end, but was left open in the possibility that the LLVM issue was going to be resolved, so as to remove the "temporary" workaround. However, if we wish to track LLVM-side bugs that are not currently an impact on us, but may allow upgrades later, then I believe we may wish to consider tracking them in another form than an issue on the GH tracker, as they are "not an issue", so to speak. Alternatively, if we want to track them on this tracker, we may wish to track them in a more organized fashion (e.g. a metabug). I am going to close this issue. Do feel free to reopen this if it regresses again, or it becomes otherwise currently actionable, or if there is a particular reason we must have an open issue on our end to track this LLVM issue for potential improvements as opposed to any of the other currently-open LLVM issues we could be potentially waiting on in order to make an improvement.
The LLVM issue has long since been fixed, so it's possible that the workaround may be removed.
I think any workarounds here are long gone, since moved to internal iteration way back in

Discussion:
discr, variants }, <del> // FIXME(eddyb): using `FieldPlacement::Arbitrary` here results // in lost optimizations, specifically around allocations, see // `test/codegen/{alloc-optimisation,vec-optimizes-away}.rs`. fields: FieldPlacement::Union(1), </del> <ins> fields: FieldPlacement::Arbitrary { offsets: vec![Size::from_bytes(0)], memory_index: vec![0] }, </ins> abi, align, size

A(i32), B(i32), } <del> // CHECK: %Enum4 = type { [2 x i32] } </del> <ins> // CHECK: %Enum4 = type { [0 x i32], i32, [1 x i32] } // CHECK: %"Enum4::A" = type { [1 x i32], i32, [0 x i32] } </ins> pub enum Enum64 { A(Align64), B(i32), } <del> // CHECK: %Enum64 = type { [16 x i64] } </del> <ins> // CHECK: %Enum64 = type { [0 x i32], i32, [31 x i32] } </ins> // CHECK: %"Enum64::A" = type { [8 x i64], %Align64, [0 x i64] } // CHECK-LABEL: @align64