Skip to content

Commit 294ffea

Browse files
committed
refactor(utils): rework rlsf for const fn compatibility (wip)
1 parent efb6e7e commit 294ffea

File tree

7 files changed

+306
-140
lines changed

7 files changed

+306
-140
lines changed

src/r3_core/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@
66
#![feature(const_ptr_offset)]
77
#![feature(const_swap)]
88
#![feature(const_slice_first_last)]
9+
#![feature(const_replace)]
10+
#![feature(const_intrinsic_copy)]
911
#![feature(maybe_uninit_slice)]
1012
#![feature(const_mut_refs)]
1113
#![feature(const_slice_from_raw_parts)]
1214
#![feature(const_option)]
15+
#![feature(const_option_ext)]
1316
#![feature(const_trait_impl)]
1417
#![feature(const_refs_to_cell)]
18+
#![feature(const_ptr_as_ref)]
1519
#![feature(exhaustive_patterns)] // `let Ok(()) = Ok::<(), !>(())`
1620
#![feature(decl_macro)]
1721
#![feature(set_ptr_value)] // `<*const T>::set_ptr_value`

src/r3_core/src/utils/alloc/rlsf/flex.rs

Lines changed: 101 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub unsafe trait FlexSource {
2020
/// `min_size` must be a multiple of [`GRANULARITY`]. `min_size` must not
2121
/// be zero.
2222
#[inline]
23+
#[default_method_body_is_const]
2324
unsafe fn alloc(&mut self, min_size: usize) -> Option<NonNull<[u8]>> {
2425
let _ = min_size;
2526
None
@@ -34,6 +35,7 @@ pub unsafe trait FlexSource {
3435
/// `ptr` must be an existing allocation made by this
3536
/// allocator. `min_new_len` must be greater than or equal to `ptr.len()`.
3637
#[inline]
38+
#[default_method_body_is_const]
3739
unsafe fn realloc_inplace_grow(
3840
&mut self,
3941
ptr: NonNull<[u8]>,
@@ -61,6 +63,7 @@ pub unsafe trait FlexSource {
6163
///
6264
/// The returned value must be constant for a particular instance of `Self`.
6365
#[inline]
66+
#[default_method_body_is_const]
6467
fn supports_dealloc(&self) -> bool {
6568
false
6669
}
@@ -73,6 +76,7 @@ pub unsafe trait FlexSource {
7376
///
7477
/// The returned value must be constant for a particular instance of `Self`.
7578
#[inline]
79+
#[default_method_body_is_const]
7680
fn supports_realloc_inplace_grow(&self) -> bool {
7781
false
7882
}
@@ -92,6 +96,7 @@ pub unsafe trait FlexSource {
9296
///
9397
/// The returned value must be constant for a particular instance of `Self`.
9498
#[inline]
99+
#[default_method_body_is_const]
95100
fn is_contiguous_growable(&self) -> bool {
96101
false
97102
}
@@ -102,12 +107,17 @@ pub unsafe trait FlexSource {
102107
///
103108
/// The returned value must be constant for a particular instance of `Self`.
104109
#[inline]
110+
#[default_method_body_is_const]
105111
fn min_align(&self) -> usize {
106112
1
107113
}
108114
}
109115

110116
trait FlexSourceExt: FlexSource {
117+
fn use_growable_pool(&self) -> bool;
118+
}
119+
120+
impl<T: ~const FlexSource> const FlexSourceExt for T {
111121
#[inline]
112122
fn use_growable_pool(&self) -> bool {
113123
// `growable_pool` is used for deallocation and pool growth.
@@ -117,8 +127,6 @@ trait FlexSourceExt: FlexSource {
117127
}
118128
}
119129

120-
impl<T: FlexSource> FlexSourceExt for T {}
121-
122130
/// Wraps [`core::alloc::GlobalAlloc`] to implement the [`FlexSource`] trait.
123131
///
124132
/// Since this type does not implement [`FlexSource::realloc_inplace_grow`],
@@ -179,8 +187,8 @@ unsafe impl<T: core::alloc::GlobalAlloc, const ALIGN: usize> FlexSource
179187
/// A wrapper of [`Tlsf`] that automatically acquires fresh memory pools from
180188
/// [`FlexSource`].
181189
#[derive(Debug)]
182-
pub struct FlexTlsf<Source: FlexSource, FLBitmap, SLBitmap, const FLLEN: usize, const SLLEN: usize>
183-
{
190+
#[must_use = "call `destroy` to drop it cleanly"]
191+
pub struct FlexTlsf<Source, FLBitmap, SLBitmap, const FLLEN: usize, const SLLEN: usize> {
184192
/// The lastly created memory pool.
185193
growable_pool: Option<Pool>,
186194
source: Source,
@@ -222,7 +230,7 @@ const _: () = if core::mem::size_of::<PoolFtr>() != GRANULARITY / 2 {
222230
impl PoolFtr {
223231
/// Get a pointer to `PoolFtr` for a given allocation.
224232
#[inline]
225-
fn get_for_alloc(alloc: NonNull<[u8]>, alloc_align: usize) -> *mut Self {
233+
const fn get_for_alloc(alloc: NonNull<[u8]>, alloc_align: usize) -> *mut Self {
226234
let alloc_end = nonnull_slice_end(alloc);
227235
let mut ptr = alloc_end.wrapping_sub(core::mem::size_of::<Self>());
228236
// If `alloc_end` is not well-aligned, we need to adjust the location
@@ -236,12 +244,12 @@ impl PoolFtr {
236244

237245
/// Initialization with a [`FlexSource`] provided by [`Default::default`]
238246
impl<
239-
Source: FlexSource + Default,
247+
Source: FlexSource + ~const Default,
240248
FLBitmap: BinInteger,
241249
SLBitmap: BinInteger,
242250
const FLLEN: usize,
243251
const SLLEN: usize,
244-
> Default for FlexTlsf<Source, FLBitmap, SLBitmap, FLLEN, SLLEN>
252+
> const Default for FlexTlsf<Source, FLBitmap, SLBitmap, FLLEN, SLLEN>
245253
{
246254
#[inline]
247255
fn default() -> Self {
@@ -271,6 +279,8 @@ impl<
271279
};
272280
}
273281

282+
// FIXME: `~const` bounds can't appear on any `impl`s but `impl const Trait for
283+
// Ty` (This is why the `~const` bounds are applied on each method.)
274284
impl<
275285
Source: FlexSource,
276286
FLBitmap: BinInteger,
@@ -281,7 +291,7 @@ impl<
281291
{
282292
/// Construct a new `FlexTlsf` object.
283293
#[inline]
284-
pub fn new(source: Source) -> Self {
294+
pub const fn new(source: Source) -> Self {
285295
Self {
286296
source,
287297
tlsf: Tlsf::INIT,
@@ -291,7 +301,7 @@ impl<
291301

292302
/// Borrow the contained `Source`.
293303
#[inline]
294-
pub fn source_ref(&self) -> &Source {
304+
pub const fn source_ref(&self) -> &Source {
295305
&self.source
296306
}
297307

@@ -302,7 +312,7 @@ impl<
302312
/// The caller must not replace the `Source` with another one or modify
303313
/// any existing allocations in the `Source`.
304314
#[inline]
305-
pub unsafe fn source_mut_unchecked(&mut self) -> &mut Source {
315+
pub const unsafe fn source_mut_unchecked(&mut self) -> &mut Source {
306316
&mut self.source
307317
}
308318

@@ -316,14 +326,21 @@ impl<
316326
/// This method will complete in constant time (assuming `Source`'s methods
317327
/// do so as well).
318328
#[cfg_attr(target_arch = "wasm32", inline(never))]
319-
pub fn allocate(&mut self, layout: Layout) -> Option<NonNull<u8>> {
329+
pub const fn allocate(&mut self, layout: Layout) -> Option<NonNull<u8>>
330+
where
331+
Source: ~const FlexSource,
332+
FLBitmap: ~const BinInteger,
333+
SLBitmap: ~const BinInteger,
334+
{
320335
if let Some(x) = self.tlsf.allocate(layout) {
321336
return Some(x);
322337
}
323338

324-
self.increase_pool_to_contain_allocation(layout)?;
339+
const_try!(self.increase_pool_to_contain_allocation(layout));
325340

326-
self.tlsf.allocate(layout).or_else(|| {
341+
let result = self.tlsf.allocate(layout);
342+
343+
if result.is_none() {
327344
// Not a hard error, but it's still unexpected because
328345
// `increase_pool_to_contain_allocation` was supposed to make this
329346
// allocation possible
@@ -332,31 +349,37 @@ impl<
332349
"the allocation failed despite the effort by \
333350
`increase_pool_to_contain_allocation`"
334351
);
335-
None
336-
})
352+
}
353+
354+
result
337355
}
338356

339357
/// Increase the amount of memory pool to guarantee the success of the
340358
/// given allocation. Returns `Some(())` on success.
341359
#[inline]
342-
fn increase_pool_to_contain_allocation(&mut self, layout: Layout) -> Option<()> {
360+
const fn increase_pool_to_contain_allocation(&mut self, layout: Layout) -> Option<()>
361+
where
362+
Source: ~const FlexSource,
363+
FLBitmap: ~const BinInteger,
364+
SLBitmap: ~const BinInteger,
365+
{
343366
let use_growable_pool = self.source.use_growable_pool();
344367

345368
// How many extra bytes we need to get from the source for the
346369
// allocation to success?
347-
let extra_bytes_well_aligned =
370+
let extra_bytes_well_aligned = const_try!(
348371
Tlsf::<'static, FLBitmap, SLBitmap, FLLEN, SLLEN>::pool_size_to_contain_allocation(
349372
layout,
350-
)?;
373+
)
374+
);
351375

352376
// The sentinel block + the block to store the allocation
353377
debug_assert!(extra_bytes_well_aligned >= GRANULARITY * 2);
354378

355-
if let Some(growable_pool) = self.growable_pool.filter(|_| use_growable_pool) {
379+
if let (Some(growable_pool), true) = (self.growable_pool, use_growable_pool) {
356380
// Try to extend an existing memory pool first.
357-
let new_pool_len_desired = growable_pool
358-
.pool_len
359-
.checked_add(extra_bytes_well_aligned)?;
381+
let new_pool_len_desired =
382+
const_try!(growable_pool.pool_len.checked_add(extra_bytes_well_aligned));
360383

361384
// The following assertion should not trip because...
362385
// - `extra_bytes_well_aligned` returns a value that is at least
@@ -459,13 +482,13 @@ impl<
459482
// ╰───┬───╯
460483
// GRANULARITY
461484
//
462-
extra_bytes_well_aligned.checked_add(GRANULARITY)?
485+
const_try!(extra_bytes_well_aligned.checked_add(GRANULARITY))
463486
} else {
464487
extra_bytes_well_aligned
465488
};
466489

467490
// Safety: `extra_bytes` is non-zero and aligned to `GRANULARITY` bytes
468-
let alloc = unsafe { self.source.alloc(extra_bytes)? };
491+
let alloc = const_try!(unsafe { self.source.alloc(extra_bytes) });
469492

470493
let is_well_aligned = self.source.min_align() >= super::GRANULARITY;
471494

@@ -477,21 +500,26 @@ impl<
477500
} else {
478501
self.tlsf.insert_free_block_ptr(alloc)
479502
}
480-
}
481-
.unwrap_or_else(|| unsafe {
482-
debug_assert!(false, "`pool_size_to_contain_allocation` is an impostor");
483-
// Safety: It's unreachable
484-
core::hint::unreachable_unchecked()
485-
})
486-
.get();
503+
};
504+
let pool_len = if let Some(pool_len) = pool_len {
505+
pool_len.get()
506+
} else {
507+
unsafe {
508+
debug_assert!(false, "`pool_size_to_contain_allocation` is an impostor");
509+
// Safety: It's unreachable
510+
core::hint::unreachable_unchecked()
511+
}
512+
};
487513

488514
if self.source.supports_dealloc() {
489515
// Link the new memory pool's `PoolFtr::prev_alloc_end` to the
490516
// previous pool (`self.growable_pool`).
491517
let pool_ftr = PoolFtr::get_for_alloc(alloc, self.source.min_align());
492-
let prev_alloc = self
493-
.growable_pool
494-
.map(|p| nonnull_slice_from_raw_parts(p.alloc_start, p.alloc_len));
518+
let prev_alloc = if let Some(p) = self.growable_pool {
519+
Some(nonnull_slice_from_raw_parts(p.alloc_start, p.alloc_len))
520+
} else {
521+
None
522+
};
495523
// Safety: `(*pool_ftr).prev_alloc` is within a pool footer
496524
// we control
497525
unsafe { (*pool_ftr).prev_alloc = prev_alloc };
@@ -522,7 +550,12 @@ impl<
522550
/// ([`Layout::align`]) as `align`.
523551
///
524552
#[cfg_attr(target_arch = "wasm32", inline(never))]
525-
pub unsafe fn deallocate(&mut self, ptr: NonNull<u8>, align: usize) {
553+
pub const unsafe fn deallocate(&mut self, ptr: NonNull<u8>, align: usize)
554+
where
555+
Source: ~const FlexSource,
556+
FLBitmap: ~const BinInteger,
557+
SLBitmap: ~const BinInteger,
558+
{
526559
// Safety: Upheld by the caller
527560
self.tlsf.deallocate(ptr, align)
528561
}
@@ -541,7 +574,12 @@ impl<
541574
///
542575
/// - `ptr` must denote a memory block previously allocated via `self`.
543576
///
544-
pub(crate) unsafe fn deallocate_unknown_align(&mut self, ptr: NonNull<u8>) {
577+
pub(crate) const unsafe fn deallocate_unknown_align(&mut self, ptr: NonNull<u8>)
578+
where
579+
Source: ~const FlexSource,
580+
FLBitmap: ~const BinInteger,
581+
SLBitmap: ~const BinInteger,
582+
{
545583
// Safety: Upheld by the caller
546584
self.tlsf.deallocate_unknown_align(ptr)
547585
}
@@ -562,11 +600,16 @@ impl<
562600
/// - The memory block must have been allocated with the same alignment
563601
/// ([`Layout::align`]) as `new_layout`.
564602
///
565-
pub unsafe fn reallocate(
603+
pub const unsafe fn reallocate(
566604
&mut self,
567605
ptr: NonNull<u8>,
568606
new_layout: Layout,
569-
) -> Option<NonNull<u8>> {
607+
) -> Option<NonNull<u8>>
608+
where
609+
Source: ~const FlexSource,
610+
FLBitmap: ~const BinInteger,
611+
SLBitmap: ~const BinInteger,
612+
{
570613
// Do this early so that the compiler can de-duplicate the evaluation of
571614
// `size_of_allocation`, which is done here as well as in
572615
// `Tlsf::reallocate`.
@@ -584,7 +627,7 @@ impl<
584627
// the same as the one in `Tlsf::reallocate`, but `self.allocation`
585628
// here refers to `FlexTlsf::allocate`, which inserts new meory pools
586629
// as necessary.
587-
let new_ptr = self.allocate(new_layout)?;
630+
let new_ptr = const_try!(self.allocate(new_layout));
588631

589632
// Move the existing data into the new location
590633
debug_assert!(new_layout.size() >= old_size);
@@ -605,24 +648,36 @@ impl<
605648
/// - `ptr` must denote a memory block previously allocated via `Self`.
606649
///
607650
#[inline]
608-
pub(crate) unsafe fn size_of_allocation_unknown_align(ptr: NonNull<u8>) -> usize {
651+
pub(crate) const unsafe fn size_of_allocation_unknown_align(ptr: NonNull<u8>) -> usize {
609652
// Safety: Upheld by the caller
610653
Tlsf::<'static, FLBitmap, SLBitmap, FLLEN, SLLEN>::size_of_allocation_unknown_align(ptr)
611654
}
612655
}
613656

614-
impl<Source: FlexSource, FLBitmap, SLBitmap, const FLLEN: usize, const SLLEN: usize> Drop
615-
for FlexTlsf<Source, FLBitmap, SLBitmap, FLLEN, SLLEN>
657+
// FIXME: There isn't a way to add `~const` to a type definition, so this
658+
// `destroy` cannot be `Drop::drop`
659+
// FIXME: `~const` bounds can't appear on any `impl`s but
660+
// `impl const Trait for Ty`
661+
impl<Source: FlexSource, FLBitmap, SLBitmap, const FLLEN: usize, const SLLEN: usize>
662+
FlexTlsf<Source, FLBitmap, SLBitmap, FLLEN, SLLEN>
616663
{
617-
fn drop(&mut self) {
664+
/// Deallocate all memory blocks and destroy `self`.
665+
pub const fn destroy(mut self)
666+
where
667+
Source: ~const FlexSource + ~const Drop,
668+
FLBitmap: ~const Drop,
669+
SLBitmap: ~const Drop,
670+
{
618671
if self.source.supports_dealloc() {
619672
debug_assert!(self.source.use_growable_pool());
620673

621674
// Deallocate all memory pools
622675
let align = self.source.min_align();
623-
let mut cur_alloc_or_none = self
624-
.growable_pool
625-
.map(|p| nonnull_slice_from_raw_parts(p.alloc_start, p.alloc_len));
676+
let mut cur_alloc_or_none = if let Some(p) = self.growable_pool {
677+
Some(nonnull_slice_from_raw_parts(p.alloc_start, p.alloc_len))
678+
} else {
679+
None
680+
};
626681

627682
while let Some(cur_alloc) = cur_alloc_or_none {
628683
// Safety: We control the referenced pool footer

0 commit comments

Comments
 (0)