1818#include " src/__support/CPP/type_traits.h"
1919#include " src/__support/libc_assert.h"
2020#include " src/__support/macros/config.h"
21+ #include " src/__support/math_extras.h"
2122
2223#include < stdint.h>
2324
@@ -40,24 +41,10 @@ LIBC_INLINE constexpr size_t align_down(size_t value, size_t alignment) {
4041 return (value / alignment) * alignment;
4142}
4243
43- // / Returns the value rounded down to the nearest multiple of alignment.
44- template <typename T>
45- LIBC_INLINE constexpr T *align_down (T *value, size_t alignment) {
46- return reinterpret_cast <T *>(
47- align_down (reinterpret_cast <size_t >(value), alignment));
48- }
49-
50- // / Returns the value rounded up to the nearest multiple of alignment.
44+ // / Returns the value rounded up to the nearest multiple of alignment. May wrap
45+ // / around.
5146LIBC_INLINE constexpr size_t align_up (size_t value, size_t alignment) {
52- __builtin_add_overflow (value, alignment - 1 , &value);
53- return align_down (value, alignment);
54- }
55-
56- // / Returns the value rounded up to the nearest multiple of alignment.
57- template <typename T>
58- LIBC_INLINE constexpr T *align_up (T *value, size_t alignment) {
59- return reinterpret_cast <T *>(
60- align_up (reinterpret_cast <size_t >(value), alignment));
47+ return align_down (value + alignment - 1 , alignment);
6148}
6249
6350using ByteSpan = cpp::span<LIBC_NAMESPACE::cpp::byte>;
@@ -68,8 +55,8 @@ using cpp::optional;
6855// / The blocks store their offsets to the previous and next blocks. The latter
6956// / is also the block's size.
7057// /
71- // / Blocks will always be aligned to a `ALIGNMENT` boundary. Block sizes will
72- // / always be rounded up to a multiple of `ALIGNMENT` .
58+ // / All blocks have their usable space aligned to some multiple of max_align_t.
59+ // / This also implies that block outer sizes are aligned to max_align_t .
7360// /
7461// / As an example, the diagram below represents two contiguous `Block`s. The
7562// / indices indicate byte offsets:
@@ -129,8 +116,9 @@ class Block {
129116 Block (const Block &other) = delete ;
130117 Block &operator =(const Block &other) = delete ;
131118
132- // / Creates the first block for a given memory region, followed by a sentinel
133- // / last block. Returns the first block.
119+ // / Initializes a given memory region into a first block and a sentinel last
120+ // / block. Returns the first block, which has its usable space aligned to
121+ // / max_align_t.
134122 static optional<Block *> init (ByteSpan region);
135123
136124 // / @returns A pointer to a `Block`, given a pointer to the start of the
@@ -186,11 +174,19 @@ class Block {
186174 }
187175
188176 // / @returns A pointer to the usable space inside this block.
177+ // /
178+ // / Aligned to some multiple of max_align_t.
189179 LIBC_INLINE cpp::byte *usable_space () {
190- return reinterpret_cast <cpp::byte *>(this ) + BLOCK_OVERHEAD;
180+ auto *s = reinterpret_cast <cpp::byte *>(this ) + BLOCK_OVERHEAD;
181+ LIBC_ASSERT (reinterpret_cast <uintptr_t >(s) % alignof (max_align_t ) == 0 &&
182+ " usable space must be aligned to a multiple of max_align_t" );
183+ return s;
191184 }
192185 LIBC_INLINE const cpp::byte *usable_space () const {
193- return reinterpret_cast <const cpp::byte *>(this ) + BLOCK_OVERHEAD;
186+ const auto *s = reinterpret_cast <const cpp::byte *>(this ) + BLOCK_OVERHEAD;
187+ LIBC_ASSERT (reinterpret_cast <uintptr_t >(s) % alignof (max_align_t ) == 0 &&
188+ " usable space must be aligned to a multiple of max_align_t" );
189+ return s;
194190 }
195191
196192 // @returns The region of memory the block manages, including the header.
@@ -201,11 +197,12 @@ class Block {
201197 // / Attempts to split this block.
202198 // /
203199 // / If successful, the block will have an inner size of at least
204- // / `new_inner_size`, rounded to ensure that the split point is on an
205- // / ALIGNMENT boundary. The remaining space will be returned as a new block.
206- // / Note that the prev_ field of the next block counts as part of the inner
207- // / size of the returnd block.
208- optional<Block *> split (size_t new_inner_size);
200+ // / `new_inner_size`. The remaining space will be returned as a new block,
201+ // / with usable space aligned to `usable_space_alignment`. Note that the prev_
202+ // / field of the next block counts as part of the inner size of the block.
203+ // / `usable_space_alignment` must be a multiple of max_align_t.
204+ optional<Block *> split (size_t new_inner_size,
205+ size_t usable_space_alignment = alignof (max_align_t ));
209206
210207 // / Merges this block with the one that comes after it.
211208 bool merge_next ();
@@ -248,46 +245,56 @@ class Block {
248245 // / nullptr.
249246 LIBC_INLINE void mark_last () { next_ |= LAST_MASK; }
250247
251- LIBC_INLINE constexpr Block (size_t outer_size) : next_(outer_size) {
248+ LIBC_INLINE Block (size_t outer_size) : next_(outer_size) {
252249 LIBC_ASSERT (outer_size % ALIGNMENT == 0 && " block sizes must be aligned" );
250+ LIBC_ASSERT (is_usable_space_aligned (alignof (max_align_t )) &&
251+ " usable space must be aligned to a multiple of max_align_t" );
253252 }
254253
255254 LIBC_INLINE bool is_usable_space_aligned (size_t alignment) const {
256255 return reinterpret_cast <uintptr_t >(usable_space ()) % alignment == 0 ;
257256 }
258257
259- // / @returns The new inner size of this block that would give the usable
260- // / space of the next block the given alignment.
261- LIBC_INLINE size_t padding_for_alignment (size_t alignment) const {
262- if (is_usable_space_aligned (alignment))
258+ // Returns the minimum inner size necessary for a block of that size to
259+ // always be able to allocate at the given size and alignment.
260+ //
261+ // Returns 0 if there is no such size.
262+ LIBC_INLINE static size_t min_size_for_allocation (size_t alignment,
263+ size_t size) {
264+ LIBC_ASSERT (alignment >= alignof (max_align_t ) &&
265+ alignment % alignof (max_align_t ) == 0 &&
266+ " alignment must be multiple of max_align_t" );
267+
268+ if (alignment == alignof (max_align_t ))
269+ return size;
270+
271+ // We must create a new block inside this one (splitting). This requires a
272+ // block header in addition to the requested size.
273+ if (add_overflow (size, sizeof (Block), size))
263274 return 0 ;
264275
265- // We need to ensure we can always split this block into a "padding" block
266- // and the aligned block. To do this, we need enough extra space for at
267- // least one block.
268- //
269- // |block |usable_space |
270- // |........|......................................|
271- // ^
272- // Alignment requirement
276+ // Beyond that, padding space may need to remain in this block to ensure
277+ // that the usable space of the next block is aligned.
273278 //
279+ // Consider a position P of some lesser alignment, L, with maximal distance
280+ // to the next position of some greater alignment, G, where G is a multiple
281+ // of L. P must be one L unit past a G-aligned point. If it were one L-unit
282+ // earlier, its distance would be zero. If it were one L-unit later, its
283+ // distance would not be maximal. If it were not some integral number of L
284+ // units away, it would not be L-aligned.
274285 //
275- // |block |space |block |usable_space |
276- // |........|........|........|....................|
277- // ^
278- // Alignment requirement
286+ // So the maximum distance would be G - L. As a special case, if L is 1
287+ // (unaligned), the max distance is G - 1.
279288 //
280- alignment = cpp::max (alignment, ALIGNMENT);
281- uintptr_t start = reinterpret_cast <uintptr_t >(usable_space ());
282- uintptr_t next_usable_space = align_up (start + BLOCK_OVERHEAD, alignment);
283- uintptr_t next_block = next_usable_space - BLOCK_OVERHEAD;
284- return next_block - start + sizeof (prev_);
289+ // This block's usable space is aligned to max_align_t >= Block. With zero
290+ // padding, the next block's usable space is sizeof(Block) past it, which is
291+ // a point aligned to Block. Thus the max padding needed is alignment -
292+ // alignof(Block).
293+ if (add_overflow (size, alignment - alignof (Block), size))
294+ return 0 ;
295+ return size;
285296 }
286297
287- // Check that we can `allocate` a block with a given alignment and size from
288- // this existing block.
289- bool can_allocate (size_t alignment, size_t size) const ;
290-
291298 // This is the return type for `allocate` which can split one block into up to
292299 // three blocks.
293300 struct BlockInfo {
@@ -309,21 +316,31 @@ class Block {
309316 Block *next;
310317 };
311318
312- // Divide a block into up to 3 blocks according to `BlockInfo`. This should
313- // only be called if `can_allocate` returns true .
319+ // Divide a block into up to 3 blocks according to `BlockInfo`. Behavior is
320+ // undefined if allocation is not possible for the given size and alignment .
314321 static BlockInfo allocate (Block *block, size_t alignment, size_t size);
315322
323+ // These two functions may wrap around.
324+ LIBC_INLINE static uintptr_t next_possible_block_start (
325+ uintptr_t ptr, size_t usable_space_alignment = alignof (max_align_t )) {
326+ return align_up (ptr + sizeof (Block), usable_space_alignment) -
327+ sizeof (Block);
328+ }
329+ LIBC_INLINE static uintptr_t prev_possible_block_start (
330+ uintptr_t ptr, size_t usable_space_alignment = alignof (max_align_t )) {
331+ return align_down (ptr, usable_space_alignment) - sizeof (Block);
332+ }
333+
316334private:
317335 // / Construct a block to represent a span of bytes. Overwrites only enough
318336 // / memory for the block header; the rest of the span is left alone.
319337 LIBC_INLINE static Block *as_block (ByteSpan bytes) {
338+ LIBC_ASSERT (reinterpret_cast <uintptr_t >(bytes.data ()) % alignof (Block) ==
339+ 0 &&
340+ " block start must be suitably aligned" );
320341 return ::new (bytes.data ()) Block (bytes.size ());
321342 }
322343
323- // / Like `split`, but assumes the caller has already checked to parameters to
324- // / ensure the split will succeed.
325- Block *split_impl (size_t new_inner_size);
326-
327344 // / Offset from this block to the previous block. 0 if this is the first
328345 // / block. This field is only alive when the previous block is free;
329346 // / otherwise, its memory is reused as part of the previous block's usable
@@ -343,81 +360,54 @@ class Block {
343360 // / previous block is free.
344361 // / * If the `last` flag is set, the block is the sentinel last block. It is
345362 // / summarily considered used and has no next block.
346- } __attribute__((packed, aligned(cpp::max( alignof ( max_align_t ), size_t { 4 })))) ;
363+ };
347364
348365inline constexpr size_t Block::BLOCK_OVERHEAD =
349366 align_up (sizeof (Block), ALIGNMENT);
350367
351- LIBC_INLINE ByteSpan get_aligned_subspan (ByteSpan bytes, size_t alignment) {
352- if (bytes.data () == nullptr )
353- return ByteSpan ();
354-
355- auto unaligned_start = reinterpret_cast <uintptr_t >(bytes.data ());
356- auto aligned_start = align_up (unaligned_start, alignment);
357- auto unaligned_end = unaligned_start + bytes.size ();
358- auto aligned_end = align_down (unaligned_end, alignment);
359-
360- if (aligned_end <= aligned_start)
361- return ByteSpan ();
362-
363- return bytes.subspan (aligned_start - unaligned_start,
364- aligned_end - aligned_start);
365- }
366-
367368LIBC_INLINE
368369optional<Block *> Block::init (ByteSpan region) {
369- optional<ByteSpan> result = get_aligned_subspan (region, ALIGNMENT);
370- if (!result)
370+ if (!region.data ())
371+ return {};
372+
373+ uintptr_t start = reinterpret_cast <uintptr_t >(region.data ());
374+ uintptr_t end = start + region.size ();
375+ if (end < start)
376+ return {};
377+
378+ uintptr_t block_start = next_possible_block_start (start);
379+ if (block_start < start)
371380 return {};
372381
373- region = result.value ();
374- // Two blocks are allocated: a free block and a sentinel last block.
375- if (region.size () < 2 * BLOCK_OVERHEAD)
382+ uintptr_t last_start = prev_possible_block_start (end);
383+ if (last_start >= end)
376384 return {};
377385
378- if (cpp::numeric_limits< size_t >:: max () < region. size () )
386+ if (block_start + sizeof (Block) > last_start )
379387 return {};
380388
381- Block *block = as_block (region.first (region.size () - BLOCK_OVERHEAD));
382- Block *last = as_block (region.last (BLOCK_OVERHEAD));
389+ auto *last_start_ptr = reinterpret_cast <cpp::byte *>(last_start);
390+ Block *block =
391+ as_block ({reinterpret_cast <cpp::byte *>(block_start), last_start_ptr});
392+ Block *last = as_block ({last_start_ptr, sizeof (Block)});
383393 block->mark_free ();
384394 last->mark_last ();
385395 return block;
386396}
387397
388- LIBC_INLINE
389- bool Block::can_allocate (size_t alignment, size_t size) const {
390- if (inner_size () < size)
391- return false ;
392- if (is_usable_space_aligned (alignment))
393- return true ;
394-
395- // Alignment isn't met, so a padding block is needed. Determine amount of
396- // inner_size() consumed by the padding block.
397- size_t padding_size = padding_for_alignment (alignment) - sizeof (prev_);
398-
399- // Check that there is room for the allocation in the following aligned block.
400- size_t aligned_inner_size = inner_size () - padding_size - BLOCK_OVERHEAD;
401- return size <= aligned_inner_size;
402- }
403-
404398LIBC_INLINE
405399Block::BlockInfo Block::allocate (Block *block, size_t alignment, size_t size) {
406- LIBC_ASSERT (
407- block->can_allocate (alignment, size) &&
408- " Calls to this function for a given alignment and size should only be "
409- " done if `can_allocate` for these parameters returns true." );
400+ LIBC_ASSERT (alignment % alignof (max_align_t ) == 0 &&
401+ " alignment must be a multiple of max_align_t" );
410402
411403 BlockInfo info{block, /* prev=*/ nullptr , /* next=*/ nullptr };
412404
413405 if (!info.block ->is_usable_space_aligned (alignment)) {
414406 Block *original = info.block ;
415- optional<Block *> maybe_aligned_block =
416- original->split (info. block -> padding_for_alignment ( alignment) );
407+ // The padding block has no minimum size requirement.
408+ optional<Block *> maybe_aligned_block = original->split (0 , alignment);
417409 LIBC_ASSERT (maybe_aligned_block.has_value () &&
418- " This split should always result in a new block. The check in "
419- " `can_allocate` ensures that we have enough space here to make "
420- " two blocks." );
410+ " it should always be possible to split for alignment" );
421411
422412 if (Block *prev = original->prev_free ()) {
423413 // If there is a free block before this, we can merge the current one with
@@ -441,37 +431,40 @@ Block::BlockInfo Block::allocate(Block *block, size_t alignment, size_t size) {
441431}
442432
443433LIBC_INLINE
444- optional<Block *> Block::split (size_t new_inner_size) {
434+ optional<Block *> Block::split (size_t new_inner_size,
435+ size_t usable_space_alignment) {
436+ LIBC_ASSERT (usable_space_alignment % alignof (max_align_t ) == 0 &&
437+ " alignment must be a multiple of max_align_t" );
445438 if (used ())
446439 return {};
447- // The prev_ field of the next block is always available, so there is a
448- // minimum size to a block created through splitting.
449- if (new_inner_size < sizeof (prev_))
450- new_inner_size = sizeof (prev_);
451-
452- size_t old_inner_size = inner_size ();
453- new_inner_size =
454- align_up (new_inner_size - sizeof (prev_), ALIGNMENT) + sizeof (prev_);
455- if (old_inner_size < new_inner_size)
456- return {};
457440
458- if (old_inner_size - new_inner_size < BLOCK_OVERHEAD)
441+ // Compute the minimum outer size that produces a block of at least
442+ // `new_inner_size`.
443+ size_t min_outer_size = outer_size (cpp::max (new_inner_size, sizeof (prev_)));
444+
445+ uintptr_t start = reinterpret_cast <uintptr_t >(this );
446+ uintptr_t next_block_start =
447+ next_possible_block_start (start + min_outer_size, usable_space_alignment);
448+ if (next_block_start < start)
459449 return {};
450+ size_t new_outer_size = next_block_start - start;
451+ LIBC_ASSERT (new_outer_size % alignof (max_align_t ) == 0 &&
452+ " new size must be aligned to max_align_t" );
460453
461- return split_impl (new_inner_size);
462- }
454+ if (outer_size () < new_outer_size ||
455+ outer_size () - new_outer_size < sizeof (Block))
456+ return {};
463457
464- LIBC_INLINE
465- Block *Block::split_impl (size_t new_inner_size) {
466- size_t outer_size1 = outer_size (new_inner_size);
467- LIBC_ASSERT (outer_size1 % ALIGNMENT == 0 && " new size must be aligned" );
468- ByteSpan new_region = region ().subspan (outer_size1);
458+ ByteSpan new_region = region ().subspan (new_outer_size);
469459 next_ &= ~SIZE_MASK;
470- next_ |= outer_size1 ;
460+ next_ |= new_outer_size ;
471461
472462 Block *new_block = as_block (new_region);
473463 mark_free (); // Free status for this block is now stored in new_block.
474464 new_block->next ()->prev_ = new_region.size ();
465+
466+ LIBC_ASSERT (new_block->is_usable_space_aligned (usable_space_alignment) &&
467+ " usable space must have requested alignment" );
475468 return new_block;
476469}
477470
0 commit comments