Skip to content

Commit 78d5ed4

Browse files
committed
Auto merge of rust-lang#133662 - paolobarbolini:vec-extend-with-via-repeatn, r=<try>
Use `iter::repeat_n` to implement `Vec::extend_with` This replaces the `Vec::extend_with` manual implementation, which is used by `Vec::resize` and `Vec` `SpecFromElem`, with `iter::repeat_n`. I've compared the codegen output between: 1. the current `Vec::resize` impl 2. this branch 3. this branch + rust-lang#130887 3 gives the closest codegen output to 1, with some output improvements. 2 doesn't look good: https://rust.godbolt.org/z/Yrc83EhjY. May also help rust-lang#120050? --- WARNING: DO NOT MERGE - in order to run the perf run in rust-lang#133662 (comment) this PR currently also contains commits from rust-lang#130887
2 parents 6c76ed5 + b080e18 commit 78d5ed4

File tree

3 files changed

+47
-112
lines changed

3 files changed

+47
-112
lines changed

library/alloc/src/vec/mod.rs

+1-25
Original file line numberDiff line numberDiff line change
@@ -3112,31 +3112,7 @@ impl<T: Clone, A: Allocator> Vec<T, A> {
31123112
#[track_caller]
31133113
/// Extend the vector by `n` clones of value.
31143114
fn extend_with(&mut self, n: usize, value: T) {
3115-
self.reserve(n);
3116-
3117-
unsafe {
3118-
let mut ptr = self.as_mut_ptr().add(self.len());
3119-
// Use SetLenOnDrop to work around bug where compiler
3120-
// might not realize the store through `ptr` through self.set_len()
3121-
// don't alias.
3122-
let mut local_len = SetLenOnDrop::new(&mut self.len);
3123-
3124-
// Write all elements except the last one
3125-
for _ in 1..n {
3126-
ptr::write(ptr, value.clone());
3127-
ptr = ptr.add(1);
3128-
// Increment the length in every step in case clone() panics
3129-
local_len.increment_len(1);
3130-
}
3131-
3132-
if n > 0 {
3133-
// We can write the last element directly without cloning needlessly
3134-
ptr::write(ptr, value);
3135-
local_len.increment_len(1);
3136-
}
3137-
3138-
// len set by scope guard
3139-
}
3115+
self.extend_trusted(iter::repeat_n(value, n));
31403116
}
31413117
}
31423118

+45-86
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::fmt;
22
use crate::iter::{FusedIterator, TrustedLen, UncheckedIterator};
3-
use crate::mem::{self, MaybeUninit};
43
use crate::num::NonZero;
54

65
/// Creates a new iterator that repeats a single element a given number of times.
@@ -57,78 +56,49 @@ use crate::num::NonZero;
5756
#[inline]
5857
#[stable(feature = "iter_repeat_n", since = "1.82.0")]
5958
pub fn repeat_n<T: Clone>(element: T, count: usize) -> RepeatN<T> {
60-
let element = if count == 0 {
61-
// `element` gets dropped eagerly.
62-
MaybeUninit::uninit()
63-
} else {
64-
MaybeUninit::new(element)
65-
};
66-
67-
RepeatN { element, count }
59+
RepeatN { inner: RepeatNInner::new(element, count) }
60+
}
61+
62+
#[derive(Clone, Copy)]
63+
#[repr(C)] // keep the layout consistent for codegen tests
64+
struct RepeatNInner<T> {
65+
count: NonZero<usize>,
66+
element: T,
67+
}
68+
69+
impl<T> RepeatNInner<T> {
70+
fn new(element: T, count: usize) -> Option<Self> {
71+
let count = NonZero::<usize>::new(count)?;
72+
Some(Self { element, count })
73+
}
6874
}
6975

7076
/// An iterator that repeats an element an exact number of times.
7177
///
7278
/// This `struct` is created by the [`repeat_n()`] function.
7379
/// See its documentation for more.
7480
#[stable(feature = "iter_repeat_n", since = "1.82.0")]
81+
#[derive(Clone)]
7582
pub struct RepeatN<A> {
76-
count: usize,
77-
// Invariant: uninit iff count == 0.
78-
element: MaybeUninit<A>,
83+
inner: Option<RepeatNInner<A>>,
7984
}
8085

8186
impl<A> RepeatN<A> {
82-
/// Returns the element if it hasn't been dropped already.
83-
fn element_ref(&self) -> Option<&A> {
84-
if self.count > 0 {
85-
// SAFETY: The count is non-zero, so it must be initialized.
86-
Some(unsafe { self.element.assume_init_ref() })
87-
} else {
88-
None
89-
}
90-
}
9187
/// If we haven't already dropped the element, return it in an option.
92-
///
93-
/// Clears the count so it won't be dropped again later.
9488
#[inline]
9589
fn take_element(&mut self) -> Option<A> {
96-
if self.count > 0 {
97-
self.count = 0;
98-
let element = mem::replace(&mut self.element, MaybeUninit::uninit());
99-
// SAFETY: We just set count to zero so it won't be dropped again,
100-
// and it used to be non-zero so it hasn't already been dropped.
101-
unsafe { Some(element.assume_init()) }
102-
} else {
103-
None
104-
}
105-
}
106-
}
107-
108-
#[stable(feature = "iter_repeat_n", since = "1.82.0")]
109-
impl<A: Clone> Clone for RepeatN<A> {
110-
fn clone(&self) -> RepeatN<A> {
111-
RepeatN {
112-
count: self.count,
113-
element: self.element_ref().cloned().map_or_else(MaybeUninit::uninit, MaybeUninit::new),
114-
}
90+
self.inner.take().map(|inner| inner.element)
11591
}
11692
}
11793

11894
#[stable(feature = "iter_repeat_n", since = "1.82.0")]
11995
impl<A: fmt::Debug> fmt::Debug for RepeatN<A> {
12096
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121-
f.debug_struct("RepeatN")
122-
.field("count", &self.count)
123-
.field("element", &self.element_ref())
124-
.finish()
125-
}
126-
}
127-
128-
#[stable(feature = "iter_repeat_n", since = "1.82.0")]
129-
impl<A> Drop for RepeatN<A> {
130-
fn drop(&mut self) {
131-
self.take_element();
97+
let (count, element) = match self.inner.as_ref() {
98+
Some(inner) => (inner.count.get(), Some(&inner.element)),
99+
None => (0, None),
100+
};
101+
f.debug_struct("RepeatN").field("count", &count).field("element", &element).finish()
132102
}
133103
}
134104

@@ -138,12 +108,17 @@ impl<A: Clone> Iterator for RepeatN<A> {
138108

139109
#[inline]
140110
fn next(&mut self) -> Option<A> {
141-
if self.count > 0 {
142-
// SAFETY: Just checked it's not empty
143-
unsafe { Some(self.next_unchecked()) }
144-
} else {
145-
None
111+
let inner = self.inner.as_mut()?;
112+
let count = inner.count.get();
113+
114+
if let Some(decremented) = NonZero::<usize>::new(count - 1) {
115+
// Order of these is important for optimization
116+
let tmp = inner.element.clone();
117+
inner.count = decremented;
118+
return Some(tmp);
146119
}
120+
121+
return self.take_element();
147122
}
148123

149124
#[inline]
@@ -154,19 +129,19 @@ impl<A: Clone> Iterator for RepeatN<A> {
154129

155130
#[inline]
156131
fn advance_by(&mut self, skip: usize) -> Result<(), NonZero<usize>> {
157-
let len = self.count;
132+
let Some(inner) = self.inner.as_mut() else {
133+
return NonZero::<usize>::new(skip).map(Err).unwrap_or(Ok(()));
134+
};
158135

159-
if skip >= len {
160-
self.take_element();
161-
}
136+
let len = inner.count.get();
162137

163-
if skip > len {
164-
// SAFETY: we just checked that the difference is positive
165-
Err(unsafe { NonZero::new_unchecked(skip - len) })
166-
} else {
167-
self.count = len - skip;
168-
Ok(())
138+
if let Some(new_len) = len.checked_sub(skip).and_then(NonZero::<usize>::new) {
139+
inner.count = new_len;
140+
return Ok(());
169141
}
142+
143+
self.inner = None;
144+
return NonZero::<usize>::new(skip - len).map(Err).unwrap_or(Ok(()));
170145
}
171146

172147
#[inline]
@@ -183,7 +158,7 @@ impl<A: Clone> Iterator for RepeatN<A> {
183158
#[stable(feature = "iter_repeat_n", since = "1.82.0")]
184159
impl<A: Clone> ExactSizeIterator for RepeatN<A> {
185160
fn len(&self) -> usize {
186-
self.count
161+
self.inner.as_ref().map(|inner| inner.count.get()).unwrap_or(0)
187162
}
188163
}
189164

@@ -211,20 +186,4 @@ impl<A: Clone> FusedIterator for RepeatN<A> {}
211186
#[unstable(feature = "trusted_len", issue = "37572")]
212187
unsafe impl<A: Clone> TrustedLen for RepeatN<A> {}
213188
#[stable(feature = "iter_repeat_n", since = "1.82.0")]
214-
impl<A: Clone> UncheckedIterator for RepeatN<A> {
215-
#[inline]
216-
unsafe fn next_unchecked(&mut self) -> Self::Item {
217-
// SAFETY: The caller promised the iterator isn't empty
218-
self.count = unsafe { self.count.unchecked_sub(1) };
219-
if self.count == 0 {
220-
// SAFETY: the check above ensured that the count used to be non-zero,
221-
// so element hasn't been dropped yet, and we just lowered the count to
222-
// zero so it won't be dropped later, and thus it's okay to take it here.
223-
unsafe { mem::replace(&mut self.element, MaybeUninit::uninit()).assume_init() }
224-
} else {
225-
// SAFETY: the count is non-zero, so it must have not been dropped yet.
226-
let element = unsafe { self.element.assume_init_ref() };
227-
A::clone(element)
228-
}
229-
}
230-
}
189+
impl<A: Clone> UncheckedIterator for RepeatN<A> {}

tests/codegen/iter-repeat-n-trivial-drop.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ pub fn iter_repeat_n_next(it: &mut std::iter::RepeatN<NotCopy>) -> Option<NotCop
2626

2727
// CHECK: [[NOT_EMPTY]]:
2828
// CHECK-NEXT: %[[DEC:.+]] = add i64 %[[COUNT]], -1
29-
// CHECK-NEXT: store i64 %[[DEC]]
3029
// CHECK-NOT: br
3130
// CHECK: %[[VAL:.+]] = load i16
31+
// CHECK-NEXT: store i64 %[[DEC]]
3232
// CHECK-NEXT: br label %[[EMPTY]]
3333

3434
// CHECK: [[EMPTY]]:

0 commit comments

Comments
 (0)