Skip to content

Commit ffd59bf

Browse files
committed
Auto merge of #75687 - TimDiekmann:realloc-align, r=Amanieu
Allow reallocation to different alignment in `AllocRef` The allocator-wg [has decided](rust-lang/wg-allocators#5 (comment)) to support reallocating to a different alignment in `AllocRef`. For more details please see the linked issue. r? @Amanieu closes rust-lang/wg-allocators#5
2 parents 2fe9a33 + 46b547c commit ffd59bf

File tree

5 files changed

+185
-162
lines changed

5 files changed

+185
-162
lines changed

library/alloc/src/alloc.rs

+55-34
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![stable(feature = "alloc_module", since = "1.28.0")]
44

55
use core::intrinsics::{self, min_align_of_val, size_of_val};
6-
use core::ptr::{NonNull, Unique};
6+
use core::ptr::{self, NonNull, Unique};
77

88
#[stable(feature = "alloc_module", since = "1.28.0")]
99
#[doc(inline)]
@@ -162,36 +162,45 @@ impl Global {
162162
unsafe fn grow_impl(
163163
&mut self,
164164
ptr: NonNull<u8>,
165-
layout: Layout,
166-
new_size: usize,
165+
old_layout: Layout,
166+
new_layout: Layout,
167167
zeroed: bool,
168168
) -> Result<NonNull<[u8]>, AllocErr> {
169169
debug_assert!(
170-
new_size >= layout.size(),
171-
"`new_size` must be greater than or equal to `layout.size()`"
170+
new_layout.size() >= old_layout.size(),
171+
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
172172
);
173173

174-
match layout.size() {
175-
// SAFETY: the caller must ensure that the `new_size` does not overflow.
176-
// `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
177-
0 => unsafe {
178-
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
179-
self.alloc_impl(new_layout, zeroed)
180-
},
174+
match old_layout.size() {
175+
0 => self.alloc_impl(new_layout, zeroed),
181176

182177
// SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size`
183178
// as required by safety conditions. Other conditions must be upheld by the caller
184-
old_size => unsafe {
185-
// `realloc` probably checks for `new_size >= size` or something similar.
186-
intrinsics::assume(new_size >= layout.size());
179+
old_size if old_layout.align() == new_layout.align() => unsafe {
180+
let new_size = new_layout.size();
181+
182+
// `realloc` probably checks for `new_size >= old_layout.size()` or something similar.
183+
intrinsics::assume(new_size >= old_layout.size());
187184

188-
let raw_ptr = realloc(ptr.as_ptr(), layout, new_size);
185+
let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size);
189186
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
190187
if zeroed {
191188
raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
192189
}
193190
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
194191
},
192+
193+
// SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`,
194+
// both the old and new memory allocation are valid for reads and writes for `old_size`
195+
// bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
196+
// `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
197+
// for `dealloc` must be upheld by the caller.
198+
old_size => unsafe {
199+
let new_ptr = self.alloc_impl(new_layout, zeroed)?;
200+
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size);
201+
self.dealloc(ptr, old_layout);
202+
Ok(new_ptr)
203+
},
195204
}
196205
}
197206
}
@@ -221,52 +230,64 @@ unsafe impl AllocRef for Global {
221230
unsafe fn grow(
222231
&mut self,
223232
ptr: NonNull<u8>,
224-
layout: Layout,
225-
new_size: usize,
233+
old_layout: Layout,
234+
new_layout: Layout,
226235
) -> Result<NonNull<[u8]>, AllocErr> {
227236
// SAFETY: all conditions must be upheld by the caller
228-
unsafe { self.grow_impl(ptr, layout, new_size, false) }
237+
unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
229238
}
230239

231240
#[inline]
232241
unsafe fn grow_zeroed(
233242
&mut self,
234243
ptr: NonNull<u8>,
235-
layout: Layout,
236-
new_size: usize,
244+
old_layout: Layout,
245+
new_layout: Layout,
237246
) -> Result<NonNull<[u8]>, AllocErr> {
238247
// SAFETY: all conditions must be upheld by the caller
239-
unsafe { self.grow_impl(ptr, layout, new_size, true) }
248+
unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
240249
}
241250

242251
#[inline]
243252
unsafe fn shrink(
244253
&mut self,
245254
ptr: NonNull<u8>,
246-
layout: Layout,
247-
new_size: usize,
255+
old_layout: Layout,
256+
new_layout: Layout,
248257
) -> Result<NonNull<[u8]>, AllocErr> {
249258
debug_assert!(
250-
new_size <= layout.size(),
251-
"`new_size` must be smaller than or equal to `layout.size()`"
259+
new_layout.size() <= old_layout.size(),
260+
"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
252261
);
253262

254-
match new_size {
263+
match new_layout.size() {
255264
// SAFETY: conditions must be upheld by the caller
256265
0 => unsafe {
257-
self.dealloc(ptr, layout);
258-
Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0))
266+
self.dealloc(ptr, old_layout);
267+
Ok(NonNull::slice_from_raw_parts(new_layout.dangling(), 0))
259268
},
260269

261270
// SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller
262-
new_size => unsafe {
263-
// `realloc` probably checks for `new_size <= size` or something similar.
264-
intrinsics::assume(new_size <= layout.size());
271+
new_size if old_layout.align() == new_layout.align() => unsafe {
272+
// `realloc` probably checks for `new_size <= old_layout.size()` or something similar.
273+
intrinsics::assume(new_size <= old_layout.size());
265274

266-
let raw_ptr = realloc(ptr.as_ptr(), layout, new_size);
275+
let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size);
267276
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
268277
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
269278
},
279+
280+
// SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`,
281+
// both the old and new memory allocation are valid for reads and writes for `new_size`
282+
// bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
283+
// `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
284+
// for `dealloc` must be upheld by the caller.
285+
new_size => unsafe {
286+
let new_ptr = self.alloc(new_layout)?;
287+
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size);
288+
self.dealloc(ptr, old_layout);
289+
Ok(new_ptr)
290+
},
270291
}
271292
}
272293
}
@@ -279,7 +300,7 @@ unsafe impl AllocRef for Global {
279300
unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
280301
let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
281302
match Global.alloc(layout) {
282-
Ok(ptr) => ptr.as_non_null_ptr().as_ptr(),
303+
Ok(ptr) => ptr.as_mut_ptr(),
283304
Err(_) => handle_alloc_error(layout),
284305
}
285306
}

library/alloc/src/raw_vec.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use core::alloc::LayoutErr;
55
use core::cmp;
6+
use core::intrinsics;
67
use core::mem::{self, ManuallyDrop, MaybeUninit};
78
use core::ops::Drop;
89
use core::ptr::{NonNull, Unique};
@@ -465,8 +466,9 @@ impl<T, A: AllocRef> RawVec<T, A> {
465466
let new_size = amount * mem::size_of::<T>();
466467

467468
let ptr = unsafe {
468-
self.alloc.shrink(ptr, layout, new_size).map_err(|_| TryReserveError::AllocError {
469-
layout: Layout::from_size_align_unchecked(new_size, layout.align()),
469+
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
470+
self.alloc.shrink(ptr, layout, new_layout).map_err(|_| TryReserveError::AllocError {
471+
layout: new_layout,
470472
non_exhaustive: (),
471473
})?
472474
};
@@ -494,13 +496,16 @@ where
494496

495497
let memory = if let Some((ptr, old_layout)) = current_memory {
496498
debug_assert_eq!(old_layout.align(), new_layout.align());
497-
unsafe { alloc.grow(ptr, old_layout, new_layout.size()) }
499+
unsafe {
500+
// The allocator checks for alignment equality
501+
intrinsics::assume(old_layout.align() == new_layout.align());
502+
alloc.grow(ptr, old_layout, new_layout)
503+
}
498504
} else {
499505
alloc.alloc(new_layout)
500-
}
501-
.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })?;
506+
};
502507

503-
Ok(memory)
508+
memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })
504509
}
505510

506511
unsafe impl<#[may_dangle] T, A: AllocRef> Drop for RawVec<T, A> {

0 commit comments

Comments
 (0)