1111 html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/" ,
1212 test( no_crate_inject, attr( deny( warnings) ) )
1313) ]
14+ #![ feature( core_intrinsics) ]
1415#![ feature( dropck_eyepatch) ]
1516#![ feature( new_uninit) ]
1617#![ feature( maybe_uninit_slice) ]
@@ -30,11 +31,11 @@ use smallvec::SmallVec;
3031
3132use std:: alloc:: Layout ;
3233use std:: cell:: { Cell , RefCell } ;
33- use std:: cmp;
3434use std:: marker:: PhantomData ;
3535use std:: mem:: { self , MaybeUninit } ;
3636use std:: ptr:: { self , NonNull } ;
3737use std:: slice;
38+ use std:: { cmp, intrinsics} ;
3839
3940#[ inline( never) ]
4041#[ cold]
@@ -363,6 +364,22 @@ unsafe impl<#[may_dangle] T> Drop for TypedArena<T> {
363364
364365unsafe impl < T : Send > Send for TypedArena < T > { }
365366
367+ #[ inline( always) ]
368+ fn align_down ( val : usize , align : usize ) -> usize {
369+ debug_assert ! ( align. is_power_of_two( ) ) ;
370+ val & !( align - 1 )
371+ }
372+
373+ #[ inline( always) ]
374+ fn align_up ( val : usize , align : usize ) -> usize {
375+ debug_assert ! ( align. is_power_of_two( ) ) ;
376+ ( val + align - 1 ) & !( align - 1 )
377+ }
378+
379+ // Pointer alignment is common in compiler types, so keep `DroplessArena` aligned to them
380+ // to optimize away alignment code.
381+ const DROPLESS_ALIGNMENT : usize = mem:: align_of :: < usize > ( ) ;
382+
366383/// An arena that can hold objects of multiple different types that impl `Copy`
367384/// and/or satisfy `!mem::needs_drop`.
368385pub struct DroplessArena {
@@ -375,6 +392,8 @@ pub struct DroplessArena {
375392 /// start. (This is slightly simpler and faster than allocating upwards,
376393 /// see <https://fitzgeraldnick.com/2019/11/01/always-bump-downwards.html>.)
377394 /// When this pointer crosses the start pointer, a new chunk is allocated.
395+ ///
396+ /// This is kept aligned to DROPLESS_ALIGNMENT.
378397 end : Cell < * mut u8 > ,
379398
380399 /// A vector of arena chunks.
@@ -395,9 +414,11 @@ impl Default for DroplessArena {
395414}
396415
397416impl DroplessArena {
398- #[ inline( never) ]
399- #[ cold]
400- fn grow ( & self , additional : usize ) {
417+ fn grow ( & self , layout : Layout ) {
418+ // Add some padding so we can align `self.end` while
419+ // stilling fitting in a `layout` allocation.
420+ let additional = layout. size ( ) + cmp:: max ( DROPLESS_ALIGNMENT , layout. align ( ) ) - 1 ;
421+
401422 unsafe {
402423 let mut chunks = self . chunks . borrow_mut ( ) ;
403424 let mut new_cap;
@@ -416,13 +437,35 @@ impl DroplessArena {
416437 // Also ensure that this chunk can fit `additional`.
417438 new_cap = cmp:: max ( additional, new_cap) ;
418439
419- let mut chunk = ArenaChunk :: new ( new_cap) ;
440+ let mut chunk = ArenaChunk :: new ( align_up ( new_cap, PAGE ) ) ;
420441 self . start . set ( chunk. start ( ) ) ;
421- self . end . set ( chunk. end ( ) ) ;
442+
443+ // Align the end to DROPLESS_ALIGNMENT
444+ let end = align_down ( chunk. end ( ) . addr ( ) , DROPLESS_ALIGNMENT ) ;
445+
446+ // Make sure we don't go past `start`. This should not happen since the allocation
447+ // should be at least DROPLESS_ALIGNMENT - 1 bytes.
448+ debug_assert ! ( chunk. start( ) . addr( ) <= end) ;
449+
450+ self . end . set ( chunk. end ( ) . with_addr ( end) ) ;
451+
422452 chunks. push ( chunk) ;
423453 }
424454 }
425455
456+ #[ inline( never) ]
457+ #[ cold]
458+ fn grow_and_alloc_raw ( & self , layout : Layout ) -> * mut u8 {
459+ self . grow ( layout) ;
460+ self . alloc_raw_without_grow ( layout) . unwrap ( )
461+ }
462+
463+ #[ inline( never) ]
464+ #[ cold]
465+ fn grow_and_alloc < T > ( & self ) -> * mut u8 {
466+ self . grow_and_alloc_raw ( Layout :: new :: < T > ( ) )
467+ }
468+
426469 /// Allocates a byte slice with specified layout from the current memory
427470 /// chunk. Returns `None` if there is no free space left to satisfy the
428471 /// request.
@@ -432,12 +475,17 @@ impl DroplessArena {
432475 let old_end = self . end . get ( ) ;
433476 let end = old_end. addr ( ) ;
434477
435- let align = layout. align ( ) ;
436- let bytes = layout. size ( ) ;
478+ // Align allocated bytes so that `self.end` stays aligned to DROPLESS_ALIGNMENT
479+ let bytes = align_up ( layout. size ( ) , DROPLESS_ALIGNMENT ) ;
480+
481+ // Tell LLVM that `end` is aligned to DROPLESS_ALIGNMENT
482+ unsafe { intrinsics:: assume ( end == align_down ( end, DROPLESS_ALIGNMENT ) ) } ;
437483
438- let new_end = end. checked_sub ( bytes) ? & ! ( align - 1 ) ;
484+ let new_end = align_down ( end. checked_sub ( bytes) ?, layout . align ( ) ) ;
439485 if start <= new_end {
440486 let new_end = old_end. with_addr ( new_end) ;
487+ // `new_end` is aligned to DROPLESS_ALIGNMENT as `align_down` preserves alignment
488+ // as both `end` and `bytes` are already aligned to DROPLESS_ALIGNMENT.
441489 self . end . set ( new_end) ;
442490 Some ( new_end)
443491 } else {
@@ -448,21 +496,26 @@ impl DroplessArena {
448496 #[ inline]
449497 pub fn alloc_raw ( & self , layout : Layout ) -> * mut u8 {
450498 assert ! ( layout. size( ) != 0 ) ;
451- loop {
452- if let Some ( a) = self . alloc_raw_without_grow ( layout) {
453- break a;
454- }
455- // No free space left. Allocate a new chunk to satisfy the request.
456- // On failure the grow will panic or abort.
457- self . grow ( layout. size ( ) ) ;
499+ if let Some ( a) = self . alloc_raw_without_grow ( layout) {
500+ return a;
458501 }
502+ // No free space left. Allocate a new chunk to satisfy the request.
503+ // On failure the grow will panic or abort.
504+ self . grow_and_alloc_raw ( layout)
459505 }
460506
461507 #[ inline]
462508 pub fn alloc < T > ( & self , object : T ) -> & mut T {
463509 assert ! ( !mem:: needs_drop:: <T >( ) ) ;
510+ assert ! ( mem:: size_of:: <T >( ) != 0 ) ;
464511
465- let mem = self . alloc_raw ( Layout :: for_value :: < T > ( & object) ) as * mut T ;
512+ let mem = if let Some ( a) = self . alloc_raw_without_grow ( Layout :: for_value :: < T > ( & object) ) {
513+ a
514+ } else {
515+ // No free space left. Allocate a new chunk to satisfy the request.
516+ // On failure the grow will panic or abort.
517+ self . grow_and_alloc :: < T > ( )
518+ } as * mut T ;
466519
467520 unsafe {
468521 // Write into uninitialized memory.
0 commit comments