Skip to content

Commit

Permalink
Use GC_memalign for larger allocations
Browse files Browse the repository at this point in the history
When a layout's alignment is larger than its size, we must allocate
using Boehm's `memalign` function instead of `malloc` in order to mirror
the existing behaviour in rustc.

This is checked in rustc with various assertions on pointer read and
write intrinsics, and was discovered when they failed with large
alloctions in lrlex using a debug build of alloy.
  • Loading branch information
jacob-hughes committed Nov 21, 2023
1 parent 6138ad6 commit 88a482e
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 11 deletions.
62 changes: 51 additions & 11 deletions library/alloc/src/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};

Expand All @@ -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<NonNull<[u8]>, 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))
},
}
}

Expand Down
2 changes: 2 additions & 0 deletions library/boehm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 88a482e

Please sign in to comment.