Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions libc/fuzzing/__support/freelist_heap_fuzz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t remainder) {

// Perform allocation.
void *ptr = nullptr;
size_t alignment = alignof(max_align_t);
size_t alignment = Block::MIN_ALIGN;
switch (alloc_type) {
case AllocType::MALLOC:
ptr = heap.allocate(alloc_size);
Expand All @@ -172,7 +172,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t remainder) {
alloc_size - alloc.size);
alloc.ptr = ptr;
alloc.size = alloc_size;
alloc.alignment = alignof(max_align_t);
alloc.alignment = Block::MIN_ALIGN;
}
break;
}
Expand All @@ -194,8 +194,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t remainder) {

if (ptr) {
// aligned_allocate should automatically apply a minimum alignment.
if (alignment < alignof(max_align_t))
alignment = alignof(max_align_t);
if (alignment < Block::MIN_ALIGN)
alignment = Block::MIN_ALIGN;
// Check alignment.
if (reinterpret_cast<uintptr_t>(ptr) % alignment)
__builtin_trap();
Expand Down
70 changes: 35 additions & 35 deletions libc/src/__support/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ using cpp::optional;
/// The blocks store their offsets to the previous and next blocks. The latter
/// is also the block's size.
///
/// All blocks have their usable space aligned to some multiple of max_align_t.
/// This also implies that block outer sizes are aligned to max_align_t.
/// All blocks have their usable space aligned to some multiple of MIN_ALIGN.
/// This also implies that block outer sizes are aligned to MIN_ALIGN.
///
/// As an example, the diagram below represents two contiguous `Block`s. The
/// indices indicate byte offsets:
Expand Down Expand Up @@ -97,13 +97,17 @@ class Block {
static constexpr size_t SIZE_MASK = ~(PREV_FREE_MASK | LAST_MASK);

public:
// To ensure block sizes have two lower unused bits, ensure usable space is
// always aligned to at least 4 bytes. (The distances between usable spaces,
// the outer size, is then always also 4-aligned.)
static constexpr size_t MIN_ALIGN = cpp::max(size_t{4}, alignof(max_align_t));
// No copy or move.
Block(const Block &other) = delete;
Block &operator=(const Block &other) = delete;

/// Initializes a given memory region into a first block and a sentinel last
/// block. Returns the first block, which has its usable space aligned to
/// max_align_t.
/// MIN_ALIGN.
static optional<Block *> init(ByteSpan region);

/// @returns A pointer to a `Block`, given a pointer to the start of the
Expand Down Expand Up @@ -160,17 +164,17 @@ class Block {

/// @returns A pointer to the usable space inside this block.
///
/// Aligned to some multiple of max_align_t.
/// Aligned to some multiple of MIN_ALIGN.
LIBC_INLINE cpp::byte *usable_space() {
auto *s = reinterpret_cast<cpp::byte *>(this) + sizeof(Block);
LIBC_ASSERT(reinterpret_cast<uintptr_t>(s) % alignof(max_align_t) == 0 &&
"usable space must be aligned to a multiple of max_align_t");
LIBC_ASSERT(reinterpret_cast<uintptr_t>(s) % MIN_ALIGN == 0 &&
"usable space must be aligned to MIN_ALIGN");
return s;
}
LIBC_INLINE const cpp::byte *usable_space() const {
const auto *s = reinterpret_cast<const cpp::byte *>(this) + sizeof(Block);
LIBC_ASSERT(reinterpret_cast<uintptr_t>(s) % alignof(max_align_t) == 0 &&
"usable space must be aligned to a multiple of max_align_t");
LIBC_ASSERT(reinterpret_cast<uintptr_t>(s) % MIN_ALIGN == 0 &&
"usable space must be aligned to MIN_ALIGN");
return s;
}

Expand All @@ -185,9 +189,9 @@ class Block {
/// `new_inner_size`. The remaining space will be returned as a new block,
/// with usable space aligned to `usable_space_alignment`. Note that the prev_
/// field of the next block counts as part of the inner size of the block.
/// `usable_space_alignment` must be a multiple of max_align_t.
/// `usable_space_alignment` must be a multiple of MIN_ALIGN.
optional<Block *> split(size_t new_inner_size,
size_t usable_space_alignment = alignof(max_align_t));
size_t usable_space_alignment = MIN_ALIGN);

/// Merges this block with the one that comes after it.
bool merge_next();
Expand Down Expand Up @@ -228,13 +232,11 @@ class Block {

LIBC_INLINE Block(size_t outer_size, bool is_last) : next_(outer_size) {
// Last blocks are not usable, so they need not have sizes aligned to
// max_align_t. Their lower bits must still be free, so they must be aligned
// to Block.
LIBC_ASSERT(
outer_size % (is_last ? alignof(Block) : alignof(max_align_t)) == 0 &&
"block sizes must be aligned");
LIBC_ASSERT(is_usable_space_aligned(alignof(max_align_t)) &&
"usable space must be aligned to a multiple of max_align_t");
// MIN_ALIGN.
LIBC_ASSERT(outer_size % (is_last ? alignof(Block) : MIN_ALIGN) == 0 &&
"block sizes must be aligned");
LIBC_ASSERT(is_usable_space_aligned(MIN_ALIGN) &&
"usable space must be aligned to a multiple of MIN_ALIGN");
if (is_last)
next_ |= LAST_MASK;
}
Expand All @@ -249,11 +251,10 @@ class Block {
// Returns 0 if there is no such size.
LIBC_INLINE static size_t min_size_for_allocation(size_t alignment,
size_t size) {
LIBC_ASSERT(alignment >= alignof(max_align_t) &&
alignment % alignof(max_align_t) == 0 &&
"alignment must be multiple of max_align_t");
LIBC_ASSERT(alignment >= MIN_ALIGN && alignment % MIN_ALIGN == 0 &&
"alignment must be multiple of MIN_ALIGN");

if (alignment == alignof(max_align_t))
if (alignment == MIN_ALIGN)
return size;

// We must create a new block inside this one (splitting). This requires a
Expand All @@ -274,7 +275,7 @@ class Block {
// So the maximum distance would be G - L. As a special case, if L is 1
// (unaligned), the max distance is G - 1.
//
// This block's usable space is aligned to max_align_t >= Block. With zero
// This block's usable space is aligned to MIN_ALIGN >= Block. With zero
// padding, the next block's usable space is sizeof(Block) past it, which is
// a point aligned to Block. Thus the max padding needed is alignment -
// alignof(Block).
Expand Down Expand Up @@ -309,13 +310,15 @@ class Block {
static BlockInfo allocate(Block *block, size_t alignment, size_t size);

// These two functions may wrap around.
LIBC_INLINE static uintptr_t next_possible_block_start(
uintptr_t ptr, size_t usable_space_alignment = alignof(max_align_t)) {
LIBC_INLINE static uintptr_t
next_possible_block_start(uintptr_t ptr,
size_t usable_space_alignment = MIN_ALIGN) {
return align_up(ptr + sizeof(Block), usable_space_alignment) -
sizeof(Block);
}
LIBC_INLINE static uintptr_t prev_possible_block_start(
uintptr_t ptr, size_t usable_space_alignment = alignof(max_align_t)) {
LIBC_INLINE static uintptr_t
prev_possible_block_start(uintptr_t ptr,
size_t usable_space_alignment = MIN_ALIGN) {
return align_down(ptr, usable_space_alignment) - sizeof(Block);
}

Expand Down Expand Up @@ -360,9 +363,6 @@ class Block {
static constexpr size_t PREV_FIELD_SIZE = sizeof(prev_);
};

static_assert(alignof(Block) >= 4,
"at least 2 bits must be available in block sizes for flags");

LIBC_INLINE
optional<Block *> Block::init(ByteSpan region) {
if (!region.data())
Expand Down Expand Up @@ -394,8 +394,8 @@ optional<Block *> Block::init(ByteSpan region) {

LIBC_INLINE
Block::BlockInfo Block::allocate(Block *block, size_t alignment, size_t size) {
LIBC_ASSERT(alignment % alignof(max_align_t) == 0 &&
"alignment must be a multiple of max_align_t");
LIBC_ASSERT(alignment % MIN_ALIGN == 0 &&
"alignment must be a multiple of MIN_ALIGN");

BlockInfo info{block, /*prev=*/nullptr, /*next=*/nullptr};

Expand Down Expand Up @@ -430,8 +430,8 @@ Block::BlockInfo Block::allocate(Block *block, size_t alignment, size_t size) {
LIBC_INLINE
optional<Block *> Block::split(size_t new_inner_size,
size_t usable_space_alignment) {
LIBC_ASSERT(usable_space_alignment % alignof(max_align_t) == 0 &&
"alignment must be a multiple of max_align_t");
LIBC_ASSERT(usable_space_alignment % MIN_ALIGN == 0 &&
"alignment must be a multiple of MIN_ALIGN");
if (used())
return {};

Expand All @@ -445,8 +445,8 @@ optional<Block *> Block::split(size_t new_inner_size,
if (next_block_start < start)
return {};
size_t new_outer_size = next_block_start - start;
LIBC_ASSERT(new_outer_size % alignof(max_align_t) == 0 &&
"new size must be aligned to max_align_t");
LIBC_ASSERT(new_outer_size % MIN_ALIGN == 0 &&
"new size must be aligned to MIN_ALIGN");

if (outer_size() < new_outer_size ||
outer_size() - new_outer_size < sizeof(Block))
Expand Down
6 changes: 3 additions & 3 deletions libc/src/__support/freelist_heap.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ LIBC_INLINE void *FreeListHeap::allocate_impl(size_t alignment, size_t size) {
}

LIBC_INLINE void *FreeListHeap::allocate(size_t size) {
return allocate_impl(alignof(max_align_t), size);
return allocate_impl(Block::MIN_ALIGN, size);
}

LIBC_INLINE void *FreeListHeap::aligned_allocate(size_t alignment,
Expand All @@ -121,8 +121,8 @@ LIBC_INLINE void *FreeListHeap::aligned_allocate(size_t alignment,
if (size % alignment != 0)
return nullptr;

// The minimum alignment supported by Block is max_align_t.
alignment = cpp::max(alignment, alignof(max_align_t));
// The minimum alignment supported by Block is MIN_ALIGN.
alignment = cpp::max(alignment, Block::MIN_ALIGN);

return allocate_impl(alignment, size);
}
Expand Down
9 changes: 4 additions & 5 deletions libc/src/__support/freestore.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ class FreeStore {

private:
static constexpr size_t MIN_OUTER_SIZE =
align_up(sizeof(Block) + sizeof(FreeList::Node), alignof(max_align_t));
align_up(sizeof(Block) + sizeof(FreeList::Node), Block::MIN_ALIGN);
static constexpr size_t MIN_LARGE_OUTER_SIZE =
align_up(sizeof(Block) + sizeof(FreeTrie::Node), alignof(max_align_t));
align_up(sizeof(Block) + sizeof(FreeTrie::Node), Block::MIN_ALIGN);
static constexpr size_t NUM_SMALL_SIZES =
(MIN_LARGE_OUTER_SIZE - MIN_OUTER_SIZE) / alignof(max_align_t);
(MIN_LARGE_OUTER_SIZE - MIN_OUTER_SIZE) / Block::MIN_ALIGN;

LIBC_INLINE static bool too_small(Block *block) {
return block->outer_size() < MIN_OUTER_SIZE;
Expand Down Expand Up @@ -98,8 +98,7 @@ LIBC_INLINE Block *FreeStore::remove_best_fit(size_t size) {

LIBC_INLINE FreeList &FreeStore::small_list(Block *block) {
LIBC_ASSERT(is_small(block) && "only legal for small blocks");
return small_lists[(block->outer_size() - MIN_OUTER_SIZE) /
alignof(max_align_t)];
return small_lists[(block->outer_size() - MIN_OUTER_SIZE) / Block::MIN_ALIGN];
}

LIBC_INLINE FreeList *FreeStore::find_best_small_fit(size_t size) {
Expand Down
38 changes: 19 additions & 19 deletions libc/test/src/__support/block_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ using LIBC_NAMESPACE::cpp::span;

TEST(LlvmLibcBlockTest, CanCreateSingleAlignedBlock) {
constexpr size_t kN = 1024;
alignas(max_align_t) array<byte, kN> bytes;
alignas(Block::MIN_ALIGN) array<byte, kN> bytes;

auto result = Block::init(bytes);
ASSERT_TRUE(result.has_value());
Block *block = *result;

EXPECT_EQ(reinterpret_cast<uintptr_t>(block) % alignof(Block), size_t{0});
EXPECT_TRUE(block->is_usable_space_aligned(alignof(max_align_t)));
EXPECT_TRUE(block->is_usable_space_aligned(Block::MIN_ALIGN));

Block *last = block->next();
ASSERT_NE(last, static_cast<Block *>(nullptr));
Expand All @@ -52,15 +52,15 @@ TEST(LlvmLibcBlockTest, CanCreateUnalignedSingleBlock) {
constexpr size_t kN = 1024;

// Force alignment, so we can un-force it below
alignas(max_align_t) array<byte, kN> bytes;
alignas(Block::MIN_ALIGN) array<byte, kN> bytes;
span<byte> aligned(bytes);

auto result = Block::init(aligned.subspan(1));
EXPECT_TRUE(result.has_value());

Block *block = *result;
EXPECT_EQ(reinterpret_cast<uintptr_t>(block) % alignof(Block), size_t{0});
EXPECT_TRUE(block->is_usable_space_aligned(alignof(max_align_t)));
EXPECT_TRUE(block->is_usable_space_aligned(Block::MIN_ALIGN));

Block *last = block->next();
ASSERT_NE(last, static_cast<Block *>(nullptr));
Expand Down Expand Up @@ -98,7 +98,7 @@ TEST(LlvmLibcBlockTest, CanSplitBlock) {
EXPECT_EQ(block2->outer_size(), orig_size - block1->outer_size());
EXPECT_FALSE(block2->used());
EXPECT_EQ(reinterpret_cast<uintptr_t>(block2) % alignof(Block), size_t{0});
EXPECT_TRUE(block2->is_usable_space_aligned(alignof(max_align_t)));
EXPECT_TRUE(block2->is_usable_space_aligned(Block::MIN_ALIGN));

EXPECT_EQ(block1->next(), block2);
EXPECT_EQ(block2->prev_free(), block1);
Expand All @@ -124,7 +124,7 @@ TEST(LlvmLibcBlockTest, CanSplitBlockUnaligned) {
EXPECT_EQ(block2->outer_size(), orig_size - block1->outer_size());
EXPECT_FALSE(block2->used());
EXPECT_EQ(reinterpret_cast<uintptr_t>(block2) % alignof(Block), size_t{0});
EXPECT_TRUE(block2->is_usable_space_aligned(alignof(max_align_t)));
EXPECT_TRUE(block2->is_usable_space_aligned(Block::MIN_ALIGN));

EXPECT_EQ(block1->next(), block2);
EXPECT_EQ(block2->prev_free(), block1);
Expand Down Expand Up @@ -211,7 +211,7 @@ TEST(LlvmLibcBlockTest, CanMakeMinimalSizeFirstBlock) {

result = block->split(0);
ASSERT_TRUE(result.has_value());
EXPECT_LE(block->outer_size(), sizeof(Block) + alignof(max_align_t));
EXPECT_LE(block->outer_size(), sizeof(Block) + Block::MIN_ALIGN);
}

TEST(LlvmLibcBlockTest, CanMakeMinimalSizeSecondBlock) {
Expand All @@ -228,7 +228,7 @@ TEST(LlvmLibcBlockTest, CanMakeMinimalSizeSecondBlock) {
reinterpret_cast<uintptr_t>(block1->usable_space()) +
Block::PREV_FIELD_SIZE);
ASSERT_TRUE(result.has_value());
EXPECT_LE((*result)->outer_size(), sizeof(Block) + alignof(max_align_t));
EXPECT_LE((*result)->outer_size(), sizeof(Block) + Block::MIN_ALIGN);
}

TEST(LlvmLibcBlockTest, CanMarkBlockUsed) {
Expand Down Expand Up @@ -361,18 +361,18 @@ TEST(LlvmLibcBlockTest, Allocate) {
if (i > block->inner_size())
continue;

auto info = Block::allocate(block, alignof(max_align_t), i);
auto info = Block::allocate(block, Block::MIN_ALIGN, i);
EXPECT_NE(info.block, static_cast<Block *>(nullptr));
}

// Ensure we can allocate a byte at every guaranteeable alignment.
for (size_t i = 1; i < kN / alignof(max_align_t); ++i) {
for (size_t i = 1; i < kN / Block::MIN_ALIGN; ++i) {
array<byte, kN> bytes;
auto result = Block::init(bytes);
ASSERT_TRUE(result.has_value());
Block *block = *result;

size_t alignment = i * alignof(max_align_t);
size_t alignment = i * Block::MIN_ALIGN;
if (Block::min_size_for_allocation(alignment, 1) > block->inner_size())
continue;

Expand All @@ -393,14 +393,14 @@ TEST(LlvmLibcBlockTest, AllocateAlreadyAligned) {
constexpr size_t SIZE = Block::PREV_FIELD_SIZE + 1;

auto [aligned_block, prev, next] =
Block::allocate(block, alignof(max_align_t), SIZE);
Block::allocate(block, Block::MIN_ALIGN, SIZE);

// Since this is already aligned, there should be no previous block.
EXPECT_EQ(prev, static_cast<Block *>(nullptr));

// Ensure we the block is aligned and large enough.
EXPECT_NE(aligned_block, static_cast<Block *>(nullptr));
EXPECT_TRUE(aligned_block->is_usable_space_aligned(alignof(max_align_t)));
EXPECT_TRUE(aligned_block->is_usable_space_aligned(Block::MIN_ALIGN));
EXPECT_GE(aligned_block->inner_size(), SIZE);

// Check the next block.
Expand All @@ -422,9 +422,9 @@ TEST(LlvmLibcBlockTest, AllocateNeedsAlignment) {
// Now pick an alignment such that the usable space is not already aligned to
// it. We want to explicitly test that the block will split into one before
// it.
size_t alignment = alignof(max_align_t);
size_t alignment = Block::MIN_ALIGN;
while (block->is_usable_space_aligned(alignment))
alignment += alignof(max_align_t);
alignment += Block::MIN_ALIGN;

auto [aligned_block, prev, next] = Block::allocate(block, alignment, 10);

Expand Down Expand Up @@ -464,9 +464,9 @@ TEST(LlvmLibcBlockTest, PreviousBlockMergedIfNotFirst) {
// Now pick an alignment such that the usable space is not already aligned to
// it. We want to explicitly test that the block will split into one before
// it.
size_t alignment = alignof(max_align_t);
size_t alignment = Block::MIN_ALIGN;
while (newblock->is_usable_space_aligned(alignment))
alignment += alignof(max_align_t);
alignment += Block::MIN_ALIGN;

// Ensure we can allocate in the new block.
auto [aligned_block, prev, next] = Block::allocate(newblock, alignment, 1);
Expand Down Expand Up @@ -501,9 +501,9 @@ TEST(LlvmLibcBlockTest, CanRemergeBlockAllocations) {
// Now pick an alignment such that the usable space is not already aligned to
// it. We want to explicitly test that the block will split into one before
// it.
size_t alignment = alignof(max_align_t);
size_t alignment = Block::MIN_ALIGN;
while (block->is_usable_space_aligned(alignment))
alignment += alignof(max_align_t);
alignment += Block::MIN_ALIGN;

auto [aligned_block, prev, next] = Block::allocate(block, alignment, 1);

Expand Down
Loading
Loading