diff --git a/library/alloc/src/gc.rs b/library/alloc/src/gc.rs index 83cb2fdf724e8..3efe75164a0ef 100644 --- a/library/alloc/src/gc.rs +++ b/library/alloc/src/gc.rs @@ -11,13 +11,14 @@ use std::boxed::Box; use core::{ alloc::{AllocError, Allocator, GlobalAlloc, Layout}, any::Any, - borrow, + borrow, cmp, cmp::Ordering, fmt, hash::{Hash, Hasher}, marker::{FinalizerSafe, PhantomData, Unsize}, mem::MaybeUninit, ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver}, + ptr, ptr::{drop_in_place, null_mut, NonNull}, }; @@ -27,35 +28,74 @@ use core::sync::atomic::{self, AtomicU64}; #[cfg(not(no_global_oom_handling))] use core::gc::ReferenceFree; +// Fast-path for low alignment values +pub const MIN_ALIGN: usize = 8; + #[derive(Debug)] pub struct GcAllocator; unsafe impl GlobalAlloc for GcAllocator { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - unsafe { boehm::GC_malloc(layout.size()) as *mut u8 } + unsafe { gc_malloc(layout) } } #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _: Layout) { - unsafe { - boehm::GC_free(ptr); - } + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + unsafe { gc_free(ptr, layout) } } #[inline] - unsafe fn realloc(&self, ptr: *mut u8, _: Layout, new_size: usize) -> *mut u8 { + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + unsafe { gc_realloc(ptr, layout, new_size) } + } +} + +#[inline] +unsafe fn gc_malloc(layout: Layout) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + unsafe { boehm::GC_malloc(layout.size()) as *mut u8 } + } else { + unsafe { boehm::GC_memalign(layout.align(), layout.size()) as *mut u8 } + } +} + +#[inline] +unsafe fn gc_realloc(ptr: *mut u8, old_layout: Layout, new_size: usize) -> *mut u8 { + if old_layout.align() <= MIN_ALIGN && old_layout.align() <= new_size { unsafe { boehm::GC_realloc(ptr, new_size) as *mut u8 } + } else { + unsafe { + let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); + + let new_ptr = gc_malloc(new_layout); + if !new_ptr.is_null() { + let size = cmp::min(old_layout.size(), new_size); + ptr::copy_nonoverlapping(ptr, new_ptr, size); + gc_free(ptr, old_layout); + } + new_ptr + } + } +} + +#[inline] +unsafe fn gc_free(ptr: *mut u8, _: Layout) { + unsafe { + boehm::GC_free(ptr); } } unsafe impl Allocator for GcAllocator { #[inline] fn allocate(&self, layout: Layout) -> Result, AllocError> { - unsafe { - let ptr = boehm::GC_malloc(layout.size()) as *mut u8; - let ptr = NonNull::new_unchecked(ptr); - Ok(NonNull::slice_from_raw_parts(ptr, layout.size())) + match layout.size() { + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), + size => unsafe { + let ptr = gc_malloc(layout); + let ptr = NonNull::new(ptr).ok_or(AllocError)?; + Ok(NonNull::slice_from_raw_parts(ptr, size)) + }, } } diff --git a/library/alloc/tests/gc.rs b/library/alloc/tests/gc.rs new file mode 100644 index 0000000000000..be25f93d38a4a --- /dev/null +++ b/library/alloc/tests/gc.rs @@ -0,0 +1,12 @@ +use std::gc::GcAllocator; + +#[repr(align(1024))] +struct S(u8); + +#[test] +fn large_alignment() { + let x = Box::new_in(S(123), GcAllocator); + let ptr = Box::into_raw(x); + assert!(!ptr.is_null()); + assert!(ptr.is_aligned()); +} diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 2529c8384764a..1bc2b7dc08e8f 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -56,6 +56,7 @@ mod c_str; mod const_fns; mod cow_str; mod fmt; +mod gc; mod heap; mod linked_list; mod rc; diff --git a/library/boehm/src/lib.rs b/library/boehm/src/lib.rs index c63a02864b4ec..50f46b354505c 100644 --- a/library/boehm/src/lib.rs +++ b/library/boehm/src/lib.rs @@ -34,6 +34,8 @@ pub struct ProfileStats { extern "C" { pub fn GC_malloc(nbytes: usize) -> *mut u8; + pub fn GC_memalign(align: usize, nbytes: usize) -> *mut u8; + pub fn GC_realloc(old: *mut u8, new_size: usize) -> *mut u8; pub fn GC_free(dead: *mut u8);