18
18
#include " src/__support/CPP/type_traits.h"
19
19
#include " src/__support/libc_assert.h"
20
20
#include " src/__support/macros/config.h"
21
+ #include " src/__support/math_extras.h"
21
22
22
23
#include < stdint.h>
23
24
@@ -40,24 +41,10 @@ LIBC_INLINE constexpr size_t align_down(size_t value, size_t alignment) {
40
41
return (value / alignment) * alignment;
41
42
}
42
43
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.
51
46
LIBC_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);
61
48
}
62
49
63
50
using ByteSpan = cpp::span<LIBC_NAMESPACE::cpp::byte>;
@@ -68,8 +55,8 @@ using cpp::optional;
68
55
// / The blocks store their offsets to the previous and next blocks. The latter
69
56
// / is also the block's size.
70
57
// /
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 .
73
60
// /
74
61
// / As an example, the diagram below represents two contiguous `Block`s. The
75
62
// / indices indicate byte offsets:
@@ -129,8 +116,9 @@ class Block {
129
116
Block (const Block &other) = delete ;
130
117
Block &operator =(const Block &other) = delete ;
131
118
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.
134
122
static optional<Block *> init (ByteSpan region);
135
123
136
124
// / @returns A pointer to a `Block`, given a pointer to the start of the
@@ -186,11 +174,19 @@ class Block {
186
174
}
187
175
188
176
// / @returns A pointer to the usable space inside this block.
177
+ // /
178
+ // / Aligned to some multiple of max_align_t.
189
179
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;
191
184
}
192
185
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;
194
190
}
195
191
196
192
// @returns The region of memory the block manages, including the header.
@@ -201,11 +197,12 @@ class Block {
201
197
// / Attempts to split this block.
202
198
// /
203
199
// / 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 ));
209
206
210
207
// / Merges this block with the one that comes after it.
211
208
bool merge_next ();
@@ -248,46 +245,56 @@ class Block {
248
245
// / nullptr.
249
246
LIBC_INLINE void mark_last () { next_ |= LAST_MASK; }
250
247
251
- LIBC_INLINE constexpr Block (size_t outer_size) : next_(outer_size) {
248
+ LIBC_INLINE Block (size_t outer_size) : next_(outer_size) {
252
249
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" );
253
252
}
254
253
255
254
LIBC_INLINE bool is_usable_space_aligned (size_t alignment) const {
256
255
return reinterpret_cast <uintptr_t >(usable_space ()) % alignment == 0 ;
257
256
}
258
257
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))
263
274
return 0 ;
264
275
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.
273
278
//
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.
274
285
//
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.
279
288
//
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;
285
296
}
286
297
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
-
291
298
// This is the return type for `allocate` which can split one block into up to
292
299
// three blocks.
293
300
struct BlockInfo {
@@ -309,21 +316,31 @@ class Block {
309
316
Block *next;
310
317
};
311
318
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 .
314
321
static BlockInfo allocate (Block *block, size_t alignment, size_t size);
315
322
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
+
316
334
private:
317
335
// / Construct a block to represent a span of bytes. Overwrites only enough
318
336
// / memory for the block header; the rest of the span is left alone.
319
337
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" );
320
341
return ::new (bytes.data ()) Block (bytes.size ());
321
342
}
322
343
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
-
327
344
// / Offset from this block to the previous block. 0 if this is the first
328
345
// / block. This field is only alive when the previous block is free;
329
346
// / otherwise, its memory is reused as part of the previous block's usable
@@ -343,81 +360,54 @@ class Block {
343
360
// / previous block is free.
344
361
// / * If the `last` flag is set, the block is the sentinel last block. It is
345
362
// / summarily considered used and has no next block.
346
- } __attribute__((packed, aligned(cpp::max( alignof ( max_align_t ), size_t { 4 })))) ;
363
+ };
347
364
348
365
inline constexpr size_t Block::BLOCK_OVERHEAD =
349
366
align_up (sizeof (Block), ALIGNMENT);
350
367
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
-
367
368
LIBC_INLINE
368
369
optional<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)
371
380
return {};
372
381
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)
376
384
return {};
377
385
378
- if (cpp::numeric_limits< size_t >:: max () < region. size () )
386
+ if (block_start + sizeof (Block) > last_start )
379
387
return {};
380
388
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)});
383
393
block->mark_free ();
384
394
last->mark_last ();
385
395
return block;
386
396
}
387
397
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
-
404
398
LIBC_INLINE
405
399
Block::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" );
410
402
411
403
BlockInfo info{block, /* prev=*/ nullptr , /* next=*/ nullptr };
412
404
413
405
if (!info.block ->is_usable_space_aligned (alignment)) {
414
406
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);
417
409
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" );
421
411
422
412
if (Block *prev = original->prev_free ()) {
423
413
// 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) {
441
431
}
442
432
443
433
LIBC_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" );
445
438
if (used ())
446
439
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 {};
457
440
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)
459
449
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" );
460
453
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 {};
463
457
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);
469
459
next_ &= ~SIZE_MASK;
470
- next_ |= outer_size1 ;
460
+ next_ |= new_outer_size ;
471
461
472
462
Block *new_block = as_block (new_region);
473
463
mark_free (); // Free status for this block is now stored in new_block.
474
464
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" );
475
468
return new_block;
476
469
}
477
470
0 commit comments