Skip to content

Commit afe67fa

Browse files
committed
Auto merge of #116370 - nnethercote:more-arena-stuff, r=cjgillot
Remove the `TypedArena::alloc_from_iter` specialization. It was added in #78569. It's complicated and doesn't actually help performance. r? `@cjgillot`
2 parents 2bbb619 + a2051dd commit afe67fa

File tree

6 files changed

+74
-128
lines changed

6 files changed

+74
-128
lines changed

compiler/rustc_arena/src/lib.rs

+45-90
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
#![feature(dropck_eyepatch)]
1616
#![feature(new_uninit)]
1717
#![feature(maybe_uninit_slice)]
18-
#![feature(min_specialization)]
1918
#![feature(decl_macro)]
2019
#![feature(pointer_byte_offsets)]
2120
#![feature(rustc_attrs)]
@@ -44,23 +43,6 @@ fn outline<F: FnOnce() -> R, R>(f: F) -> R {
4443
f()
4544
}
4645

47-
/// An arena that can hold objects of only one type.
48-
pub struct TypedArena<T> {
49-
/// A pointer to the next object to be allocated.
50-
ptr: Cell<*mut T>,
51-
52-
/// A pointer to the end of the allocated area. When this pointer is
53-
/// reached, a new chunk is allocated.
54-
end: Cell<*mut T>,
55-
56-
/// A vector of arena chunks.
57-
chunks: RefCell<Vec<ArenaChunk<T>>>,
58-
59-
/// Marker indicating that dropping the arena causes its owned
60-
/// instances of `T` to be dropped.
61-
_own: PhantomData<T>,
62-
}
63-
6446
struct ArenaChunk<T = u8> {
6547
/// The raw storage for the arena chunk.
6648
storage: NonNull<[MaybeUninit<T>]>,
@@ -130,6 +112,23 @@ impl<T> ArenaChunk<T> {
130112
const PAGE: usize = 4096;
131113
const HUGE_PAGE: usize = 2 * 1024 * 1024;
132114

115+
/// An arena that can hold objects of only one type.
116+
pub struct TypedArena<T> {
117+
/// A pointer to the next object to be allocated.
118+
ptr: Cell<*mut T>,
119+
120+
/// A pointer to the end of the allocated area. When this pointer is
121+
/// reached, a new chunk is allocated.
122+
end: Cell<*mut T>,
123+
124+
/// A vector of arena chunks.
125+
chunks: RefCell<Vec<ArenaChunk<T>>>,
126+
127+
/// Marker indicating that dropping the arena causes its owned
128+
/// instances of `T` to be dropped.
129+
_own: PhantomData<T>,
130+
}
131+
133132
impl<T> Default for TypedArena<T> {
134133
/// Creates a new `TypedArena`.
135134
fn default() -> TypedArena<T> {
@@ -144,77 +143,6 @@ impl<T> Default for TypedArena<T> {
144143
}
145144
}
146145

147-
trait IterExt<T> {
148-
fn alloc_from_iter(self, arena: &TypedArena<T>) -> &mut [T];
149-
}
150-
151-
impl<I, T> IterExt<T> for I
152-
where
153-
I: IntoIterator<Item = T>,
154-
{
155-
// This default collects into a `SmallVec` and then allocates by copying
156-
// from it. The specializations below for types like `Vec` are more
157-
// efficient, copying directly without the intermediate collecting step.
158-
// This default could be made more efficient, like
159-
// `DroplessArena::alloc_from_iter`, but it's not hot enough to bother.
160-
#[inline]
161-
default fn alloc_from_iter(self, arena: &TypedArena<T>) -> &mut [T] {
162-
let vec: SmallVec<[_; 8]> = self.into_iter().collect();
163-
vec.alloc_from_iter(arena)
164-
}
165-
}
166-
167-
impl<T, const N: usize> IterExt<T> for std::array::IntoIter<T, N> {
168-
#[inline]
169-
fn alloc_from_iter(self, arena: &TypedArena<T>) -> &mut [T] {
170-
let len = self.len();
171-
if len == 0 {
172-
return &mut [];
173-
}
174-
// Move the content to the arena by copying and then forgetting it.
175-
let start_ptr = arena.alloc_raw_slice(len);
176-
unsafe {
177-
self.as_slice().as_ptr().copy_to_nonoverlapping(start_ptr, len);
178-
mem::forget(self);
179-
slice::from_raw_parts_mut(start_ptr, len)
180-
}
181-
}
182-
}
183-
184-
impl<T> IterExt<T> for Vec<T> {
185-
#[inline]
186-
fn alloc_from_iter(mut self, arena: &TypedArena<T>) -> &mut [T] {
187-
let len = self.len();
188-
if len == 0 {
189-
return &mut [];
190-
}
191-
// Move the content to the arena by copying and then forgetting it.
192-
let start_ptr = arena.alloc_raw_slice(len);
193-
unsafe {
194-
self.as_ptr().copy_to_nonoverlapping(start_ptr, len);
195-
self.set_len(0);
196-
slice::from_raw_parts_mut(start_ptr, len)
197-
}
198-
}
199-
}
200-
201-
impl<A: smallvec::Array> IterExt<A::Item> for SmallVec<A> {
202-
#[inline]
203-
fn alloc_from_iter(mut self, arena: &TypedArena<A::Item>) -> &mut [A::Item] {
204-
let len = self.len();
205-
if len == 0 {
206-
return &mut [];
207-
}
208-
// Move the content to the arena by copying and then forgetting it.
209-
let start_ptr = arena.alloc_raw_slice(len);
210-
unsafe {
211-
self.as_ptr().copy_to_nonoverlapping(start_ptr, len);
212-
self.set_len(0);
213-
slice::from_raw_parts_mut(start_ptr, len)
214-
}
215-
}
216-
}
217-
218146
impl<T> TypedArena<T> {
219147
/// Allocates an object in the `TypedArena`, returning a reference to it.
220148
#[inline]
@@ -270,8 +198,35 @@ impl<T> TypedArena<T> {
270198

271199
#[inline]
272200
pub fn alloc_from_iter<I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
201+
// This implementation is entirely separate to
202+
// `DroplessIterator::alloc_from_iter`, even though conceptually they
203+
// are the same.
204+
//
205+
// `DroplessIterator` (in the fast case) writes elements from the
206+
// iterator one at a time into the allocated memory. That's easy
207+
// because the elements don't implement `Drop`. But for `TypedArena`
208+
// they do implement `Drop`, which means that if the iterator panics we
209+
// could end up with some allocated-but-uninitialized elements, which
210+
// will then cause UB in `TypedArena::drop`.
211+
//
212+
// Instead we use an approach where any iterator panic will occur
213+
// before the memory is allocated. This function is much less hot than
214+
// `DroplessArena::alloc_from_iter`, so it doesn't need to be
215+
// hyper-optimized.
273216
assert!(mem::size_of::<T>() != 0);
274-
iter.alloc_from_iter(self)
217+
218+
let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect();
219+
if vec.is_empty() {
220+
return &mut [];
221+
}
222+
// Move the content to the arena by copying and then forgetting it.
223+
let len = vec.len();
224+
let start_ptr = self.alloc_raw_slice(len);
225+
unsafe {
226+
vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
227+
vec.set_len(0);
228+
slice::from_raw_parts_mut(start_ptr, len)
229+
}
275230
}
276231

277232
/// Grows the arena.

compiler/rustc_ast_lowering/src/format.rs

+18-29
Original file line numberDiff line numberDiff line change
@@ -410,15 +410,11 @@ fn expand_format_args<'hir>(
410410
let format_options = use_format_options.then(|| {
411411
// Generate:
412412
// &[format_spec_0, format_spec_1, format_spec_2]
413-
let elements: Vec<_> = fmt
414-
.template
415-
.iter()
416-
.filter_map(|piece| {
417-
let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
418-
Some(make_format_spec(ctx, macsp, placeholder, &mut argmap))
419-
})
420-
.collect();
421-
ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
413+
let elements = ctx.arena.alloc_from_iter(fmt.template.iter().filter_map(|piece| {
414+
let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
415+
Some(make_format_spec(ctx, macsp, placeholder, &mut argmap))
416+
}));
417+
ctx.expr_array_ref(macsp, elements)
422418
});
423419

424420
let arguments = fmt.arguments.all_args();
@@ -477,10 +473,8 @@ fn expand_format_args<'hir>(
477473
// <core::fmt::Argument>::new_debug(&arg2),
478474
// …
479475
// ]
480-
let elements: Vec<_> = arguments
481-
.iter()
482-
.zip(argmap)
483-
.map(|(arg, ((_, ty), placeholder_span))| {
476+
let elements = ctx.arena.alloc_from_iter(arguments.iter().zip(argmap).map(
477+
|(arg, ((_, ty), placeholder_span))| {
484478
let placeholder_span =
485479
placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
486480
let arg_span = match arg.kind {
@@ -493,9 +487,9 @@ fn expand_format_args<'hir>(
493487
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
494488
));
495489
make_argument(ctx, placeholder_span, ref_arg, ty)
496-
})
497-
.collect();
498-
ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
490+
},
491+
));
492+
ctx.expr_array_ref(macsp, elements)
499493
} else {
500494
// Generate:
501495
// &match (&arg0, &arg1, &…) {
@@ -528,19 +522,14 @@ fn expand_format_args<'hir>(
528522
make_argument(ctx, placeholder_span, arg, ty)
529523
},
530524
));
531-
let elements: Vec<_> = arguments
532-
.iter()
533-
.map(|arg| {
534-
let arg_expr = ctx.lower_expr(&arg.expr);
535-
ctx.expr(
536-
arg.expr.span.with_ctxt(macsp.ctxt()),
537-
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
538-
)
539-
})
540-
.collect();
541-
let args_tuple = ctx
542-
.arena
543-
.alloc(ctx.expr(macsp, hir::ExprKind::Tup(ctx.arena.alloc_from_iter(elements))));
525+
let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
526+
let arg_expr = ctx.lower_expr(&arg.expr);
527+
ctx.expr(
528+
arg.expr.span.with_ctxt(macsp.ctxt()),
529+
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
530+
)
531+
}));
532+
let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
544533
let array = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
545534
let match_arms = ctx.arena.alloc_from_iter([ctx.arm(args_pat, array)]);
546535
let match_expr = ctx.arena.alloc(ctx.expr_match(

compiler/rustc_hir_analysis/src/variance/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -192,5 +192,5 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
192192
}
193193
}
194194
}
195-
tcx.arena.alloc_from_iter(collector.variances.into_iter())
195+
tcx.arena.alloc_from_iter(collector.variances)
196196
}

compiler/rustc_middle/src/ty/codec.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -348,9 +348,10 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::Const<'tcx> {
348348

349349
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for [ty::ValTree<'tcx>] {
350350
fn decode(decoder: &mut D) -> &'tcx Self {
351-
decoder.interner().arena.alloc_from_iter(
352-
(0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::<Vec<_>>(),
353-
)
351+
decoder
352+
.interner()
353+
.arena
354+
.alloc_from_iter((0..decoder.read_usize()).map(|_| Decodable::decode(decoder)))
354355
}
355356
}
356357

@@ -368,9 +369,10 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for AdtDef<'tcx> {
368369

369370
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for [(ty::Clause<'tcx>, Span)] {
370371
fn decode(decoder: &mut D) -> &'tcx Self {
371-
decoder.interner().arena.alloc_from_iter(
372-
(0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::<Vec<_>>(),
373-
)
372+
decoder
373+
.interner()
374+
.arena
375+
.alloc_from_iter((0..decoder.read_usize()).map(|_| Decodable::decode(decoder)))
374376
}
375377
}
376378

compiler/rustc_trait_selection/src/traits/vtable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ fn vtable_entries<'tcx>(
316316
dump_vtable_entries(tcx, sp, trait_ref, &entries);
317317
}
318318

319-
tcx.arena.alloc_from_iter(entries.into_iter())
319+
tcx.arena.alloc_from_iter(entries)
320320
}
321321

322322
/// Find slot base for trait methods within vtable entries of another trait

compiler/rustc_ty_utils/src/consts.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ pub(crate) fn destructure_const<'tcx>(
7171
_ => bug!("cannot destructure constant {:?}", const_),
7272
};
7373

74-
let fields = tcx.arena.alloc_from_iter(fields.into_iter());
74+
let fields = tcx.arena.alloc_from_iter(fields);
7575

7676
ty::DestructuredConst { variant, fields }
7777
}

0 commit comments

Comments
 (0)