diff --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h --- a/llvm/include/llvm/Support/Allocator.h +++ b/llvm/include/llvm/Support/Allocator.h @@ -140,6 +140,9 @@ // This method is *not* marked noalias, because // SpecificBumpPtrAllocator::DestroyAll() loops over all allocations, and // that loop is not based on the Allocate() return value. + // + // Allocate(0, N) is valid, it returns a non-null pointer (which should not + // be dereferenced). LLVM_ATTRIBUTE_RETURNS_NONNULL void *Allocate(size_t Size, Align Alignment) { // Keep track of how many bytes we've allocated. BytesAllocated += Size; @@ -154,7 +157,9 @@ #endif // Check if we have enough space. - if (Adjustment + SizeToAllocate <= size_t(End - CurPtr)) { + if (Adjustment + SizeToAllocate <= size_t(End - CurPtr) + // We can't return nullptr even for a zero-sized allocation! + && CurPtr != nullptr) { char *AlignedPtr = CurPtr + Adjustment; CurPtr = AlignedPtr + SizeToAllocate; // Update the allocation point of this memory block in MemorySanitizer. diff --git a/llvm/unittests/Support/AllocatorTest.cpp b/llvm/unittests/Support/AllocatorTest.cpp --- a/llvm/unittests/Support/AllocatorTest.cpp +++ b/llvm/unittests/Support/AllocatorTest.cpp @@ -99,6 +99,31 @@ EXPECT_EQ(0U, a & 127); } +// Test zero-sized allocations. +// In general we don't need to allocate memory for these. +// However Allocate never returns null, so if the first allocation is zero-sized +// we end up creating a slab for it. +TEST(AllocatorTest, TestZero) { + BumpPtrAllocator Alloc; + EXPECT_EQ(0u, Alloc.GetNumSlabs()); + EXPECT_EQ(0u, Alloc.getBytesAllocated()); + + void *Empty = Alloc.Allocate(0, 1); + EXPECT_NE(Empty, nullptr) << "Allocate is __attribute__((returns_nonnull))"; + EXPECT_EQ(1u, Alloc.GetNumSlabs()) << "Allocated a slab to point to"; + EXPECT_EQ(0u, Alloc.getBytesAllocated()); + + void *Large = Alloc.Allocate(4096, 1); + EXPECT_EQ(1u, Alloc.GetNumSlabs()); + EXPECT_EQ(4096u, Alloc.getBytesAllocated()); + EXPECT_EQ(Empty, Large); + + void *Empty2 = Alloc.Allocate(0, 1); + EXPECT_NE(Empty2, nullptr); + EXPECT_EQ(1u, Alloc.GetNumSlabs()); + EXPECT_EQ(4096u, Alloc.getBytesAllocated()); +} + // Test allocating just over the slab size. This tests a bug where before the // allocator incorrectly calculated the buffer end pointer. TEST(AllocatorTest, TestOverflow) {