Change:
<!-- Thank you for filing a regression report! A regression is something that changed between versions of Rust but was not supposed to. Please provide a short summary of the regression, along with any information you feel is relevant to replicate it. --I tried this code: I expected to see this happen: same codegen or similar in performance Instead, this happened: array has way worse codegen <details<summaryAssembly</summary<p</p</details<!-- Provide the most recent version this worked on, for example: It most recently worked on: Rust 1.47 --It most recently worked on: 1.64 <!-- Provide the version you are using that has the regression. --65 till now. Regressed in nightly-2022-08-13, maybe LLVM 15 ?: : <!-- Did the compiler crash? If so, please provide a backtrace. --<!-- Include a backtrace in the code block by setting in your environment. E.g. . --<details<summaryBacktrace</summary<p</p</details<!-- If you know when this regression occurred, please add a line like below, replacing with one of stable, beta, or nightly. -- modify labels: +regression-from-stable-to-stable -regression-untriaged <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
WG-prioritization assigning priority (). cc since this regressed in the LLVM 15 update label -I-prioritize +P-medium +T-compiler
I've seen llvm doing these strange 1-byte-at-a-time "vectorizations" in other places too. This might be a more general problem. In this case we can probably paper over it by implementing TrustedRandomAccess for the array iter.
Unfortunately, the array iterator is fundamentally worse right now, because indexing into it -- a fundamental part of being a by-value array iterator -- keeps it from SRoAing and thus doesn't optimize well. This is why when I was making better I ended up making to not have to use . Sadly, for the foreseeable future you're better off using the "weaker" slice iterator when you can.
My guess is that the SROA is missing something in the opaque pointer mode. claim
It's not about vectorization. This optimization can be restored by using . But highly unrecommended. Upstream issue:
The upstream issue is closed. claim

Discussion:
use crate::{ fmt, intrinsics::transmute_unchecked, <del> iter::{self, ExactSizeIterator, FusedIterator, TrustedLen}, </del> <ins> iter::{self, ExactSizeIterator, FusedIterator, TrustedLen, TrustedRandomAccessNoCoerce}, </ins> mem::MaybeUninit, ops::{IndexRange, Range}, ptr,

NonZeroUsize::new(remaining).map_or(Ok(()), Err) } <ins> #[inline] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { // SAFETY: The caller must provide an idx that is in bound of the remainder. unsafe { self.data.as_ptr().add(self.alive.start()).add(idx).cast::<T>().read() } } </ins> } #[stable(feature = "array_value_iter_impls", since = "1.40.0")]

#[stable(feature = "array_value_iter_impls", since = "1.40.0")] unsafe impl<T, const N: usize> TrustedLen for IntoIter<T, N> {} <ins> #[doc(hidden)] #[unstable(issue = "none", feature = "std_internals")] #[rustc_unsafe_specialization_marker] pub trait NonDrop {} // T: Copy as approximation for !Drop since get_unchecked does not advance self.alive // and thus we can't implement drop-handling #[unstable(issue = "none", feature = "std_internals")] impl<T: Copy> NonDrop for T {} #[doc(hidden)] #[unstable(issue = "none", feature = "std_internals")] unsafe impl<T, const N: usize> TrustedRandomAccessNoCoerce for IntoIter<T, N> where T: NonDrop, { const MAY_HAVE_SIDE_EFFECT: bool = false; } </ins> #[stable(feature = "array_value_iter_impls", since = "1.40.0")] impl<T: Clone, const N: usize> Clone for IntoIter<T, N> { fn clone(&self) -> Self {

} #[inline] <ins> fn fold<Acc, F>(self, init: Acc, f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { ZipImpl::fold(self, init, f) } #[inline] </ins> unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item where Self: TrustedRandomAccessNoCoerce,

where A: DoubleEndedIterator + ExactSizeIterator, B: DoubleEndedIterator + ExactSizeIterator; <ins> fn fold<Acc, F>(self, init: Acc, f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc; </ins> // This has the same safety requirements as `Iterator::__iterator_get_unchecked` unsafe fn get_unchecked(&mut self, idx: usize) -> <Self as Iterator>::Item where

{ unreachable!("Always specialized"); } <ins> #[inline] default fn fold<Acc, F>(self, init: Acc, f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { SpecFold::spec_fold(self, init, f) } </ins> } #[doc(hidden)]

// `Iterator::__iterator_get_unchecked`. unsafe { (self.a.__iterator_get_unchecked(idx), self.b.__iterator_get_unchecked(idx)) } } <ins> #[inline] fn fold<Acc, F>(mut self, init: Acc, mut f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { let mut accum = init; let len = ZipImpl::size_hint(&self).0; for i in 0..len { // SAFETY: since Self: TrustedRandomAccessNoCoerce we can trust the size-hint to // calculate the length and then use that to do unchecked iteration. // fold consumes the iterator so we don't need to fixup any state. unsafe { accum = f(accum, self.get_unchecked(i)); } } accum } </ins> } #[doc(hidden)]

unsafe { self.__iterator_get_unchecked(index) } } } <ins> trait SpecFold: Iterator { fn spec_fold<B, F>(self, init: B, f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B; } impl<A: Iterator, B: Iterator> SpecFold for Zip<A, B> { // Adapted from default impl from the Iterator trait #[inline] default fn spec_fold<Acc, F>(mut self, init: Acc, mut f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { let mut accum = init; while let Some(x) = ZipImpl::next(&mut self) { accum = f(accum, x); } accum } } impl<A: TrustedLen, B: TrustedLen> SpecFold for Zip<A, B> { #[inline] fn spec_fold<Acc, F>(mut self, init: Acc, mut f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { let mut accum = init; loop { let (upper, more) = if let Some(upper) = ZipImpl::size_hint(&self).1 { (upper, false) } else { // Per TrustedLen contract a None upper bound means more than usize::MAX items (usize::MAX, true) }; for _ in 0..upper { let pair = // SAFETY: TrustedLen guarantees that at least `upper` many items are available // therefore we know they can't be None unsafe { (self.a.next().unwrap_unchecked(), self.b.next().unwrap_unchecked()) }; accum = f(accum, pair); } if !more { break; } } accum } } </ins>

let it = xs.iter_mut().map(|x| *x = 1).enumerate().zip(&ys); it.count(); } <del> assert_eq!(&xs, &[1, 1, 1, 1, 1, 0]); </del> <ins> let length_aware =  &xs == &[1, 1, 1, 1, 0, 0]; let probe_first = &xs == &[1, 1, 1, 1, 1, 0]; // either implementation is valid according to zip documentation assert!(length_aware || probe_first); </ins> } #[test]

<ins> // assembly-output: emit-asm // # zen3 previously exhibited odd vectorization // compile-flags: --crate-type=lib -Ctarget-cpu=znver3 -O // only-x86_64 // ignore-sgx use std::iter; // previously this produced a long chain of // 56:  vpextrb $6, %xmm0, %ecx // 57:  orb %cl, 22(%rsi) // 58:  vpextrb $7, %xmm0, %ecx // 59:  orb %cl, 23(%rsi) // [...] // CHECK-LABEL: zip_arrays: #[no_mangle] pub fn zip_arrays(mut a: [u8; 32], b: [u8; 32]) -> [u8; 32] { // CHECK-NOT: vpextrb // CHECK-NOT: orb %cl // CHECK: vorps iter::zip(&mut a, b).for_each(|(a, b)| *a |= b); // CHECK: retq a } </ins>