Skip to content

Commit 9ab93f2

Browse files
committed
Specialize arena alloc for cloned slices
1 parent 18ec4a9 commit 9ab93f2

File tree

1 file changed

+52
-24
lines changed
  • compiler/rustc_arena/src

1 file changed

+52
-24
lines changed

compiler/rustc_arena/src/lib.rs

+52-24
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,50 @@
1919
#![cfg_attr(bootstrap, feature(min_const_generics))]
2020
#![feature(min_specialization)]
2121
#![cfg_attr(test, feature(test))]
22+
#![feature(rustc_attrs)]
2223

2324
use smallvec::SmallVec;
2425

2526
use std::alloc::Layout;
2627
use std::cell::{Cell, RefCell};
2728
use std::cmp;
29+
use std::iter::Cloned;
2830
use std::marker::{PhantomData, Send};
2931
use std::mem::{self, MaybeUninit};
3032
use std::ptr;
31-
use std::slice;
33+
use std::slice::{self, Iter};
3234

3335
#[inline(never)]
3436
#[cold]
3537
pub fn cold_path<F: FnOnce() -> R, R>(f: F) -> R {
3638
f()
3739
}
3840

41+
/// Move the contents of an iterator to a contiguous memory region.
42+
///
43+
/// SAFETY: the caller must ensure that the destination memory is free and large enough to hold
44+
/// the contents of the iterator. Must not be called for iterators that may allocate on the same
45+
/// arena.
46+
#[inline]
47+
unsafe fn write_from_iter<'a, T, I: Iterator<Item = T>>(
48+
mut iter: I,
49+
len: usize,
50+
mem: *mut T,
51+
) -> &'a mut [T] {
52+
let mut l = len;
53+
for i in 0..len {
54+
if let Some(value) = iter.next() {
55+
std::ptr::write(mem.add(i), value);
56+
} else {
57+
l = i;
58+
break;
59+
}
60+
}
61+
// We only return as many items as the iterator gave us, even
62+
// though it was supposed to give us `len`
63+
return std::slice::from_raw_parts_mut(mem, l);
64+
}
65+
3966
/// An arena that can hold objects of only one type.
4067
pub struct TypedArena<T> {
4168
/// A pointer to the next object to be allocated.
@@ -133,6 +160,10 @@ where
133160
}
134161
}
135162

163+
#[rustc_unsafe_specialization_marker]
164+
trait Clonable: Clone {}
165+
impl<T: Clone> Clonable for T {}
166+
136167
impl<T, const N: usize> IterExt<T> for std::array::IntoIter<T, N> {
137168
#[inline]
138169
fn alloc_from_iter(self, arena: &TypedArena<T>) -> &mut [T] {
@@ -167,6 +198,25 @@ impl<T> IterExt<T> for Vec<T> {
167198
}
168199
}
169200

201+
impl<'a, T: 'a> IterExt<T> for Cloned<Iter<'a, T>>
202+
where
203+
T: Clonable,
204+
{
205+
#[inline]
206+
fn alloc_from_iter(self, arena: &TypedArena<T>) -> &mut [T] {
207+
let len = self.len();
208+
if len == 0 {
209+
return &mut [];
210+
}
211+
// Move the content to the arena by copying and then forgetting it
212+
unsafe {
213+
let len = self.len();
214+
let start_ptr = arena.alloc_raw_slice(len);
215+
write_from_iter(self, len, start_ptr)
216+
}
217+
}
218+
}
219+
170220
impl<A: smallvec::Array> IterExt<A::Item> for SmallVec<A> {
171221
#[inline]
172222
fn alloc_from_iter(mut self, arena: &TypedArena<A::Item>) -> &mut [A::Item] {
@@ -489,28 +539,6 @@ impl DroplessArena {
489539
}
490540
}
491541

492-
#[inline]
493-
unsafe fn write_from_iter<T, I: Iterator<Item = T>>(
494-
&self,
495-
mut iter: I,
496-
len: usize,
497-
mem: *mut T,
498-
) -> &mut [T] {
499-
let mut i = 0;
500-
// Use a manual loop since LLVM manages to optimize it better for
501-
// slice iterators
502-
loop {
503-
let value = iter.next();
504-
if i >= len || value.is_none() {
505-
// We only return as many items as the iterator gave us, even
506-
// though it was supposed to give us `len`
507-
return slice::from_raw_parts_mut(mem, i);
508-
}
509-
ptr::write(mem.add(i), value.unwrap());
510-
i += 1;
511-
}
512-
}
513-
514542
#[inline]
515543
pub fn alloc_from_iter<T, I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
516544
let iter = iter.into_iter();
@@ -529,7 +557,7 @@ impl DroplessArena {
529557
}
530558

531559
let mem = self.alloc_raw(Layout::array::<T>(len).unwrap()) as *mut T;
532-
unsafe { self.write_from_iter(iter, len, mem) }
560+
unsafe { write_from_iter(iter, len, mem) }
533561
}
534562
(_, _) => {
535563
cold_path(move || -> &mut [T] {

0 commit comments

Comments
 (0)