mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
AK: Adopt mimalloc v2 as main allocator
Use mimalloc for Ladybird-owned allocations without overriding malloc(). Route kmalloc(), kcalloc(), krealloc(), and kfree() through mimalloc, and put the embedded Rust crates on the same allocator via a shared shim in AK/kmalloc.cpp. This also lets us drop kfree_sized(), since it no longer used its size argument. StringData, Utf16StringData, JS object storage, Rust error strings, and the CoreAudio playback helpers can all free their AK-backed storage with plain kfree(). Sanitizer builds still use the system allocator. LeakSanitizer does not reliably trace references stored in mimalloc-managed AK containers, so static caches and other long-lived roots can look leaked. Pass the old size into the Rust realloc shim so aligned fallback reallocations can move posix_memalign-backed blocks safely. Static builds still need a little linker help. macOS app binaries need the Rust allocator entry points forced in from liblagom-ak.a, while static ELF links can pull in identical allocator shim definitions from multiple Rust staticlibs. Keep the Apple -u flags and allow those duplicate shim symbols for LibJS and LibRegex links on Linux and BSD.
This commit is contained in:
committed by
Andreas Kling
parent
648ececa62
commit
b23aa38546
Notes:
github-actions[bot]
2026-04-08 07:58:49 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/b23aa385467 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8752 Reviewed-by: https://github.com/trflynn89
@@ -50,7 +50,7 @@ public:
|
||||
Bitmap& operator=(Bitmap&& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
kfree_sized(m_data, size_in_bytes());
|
||||
kfree(m_data);
|
||||
m_data = exchange(other.m_data, nullptr);
|
||||
m_size = exchange(other.m_size, 0);
|
||||
}
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
~Bitmap()
|
||||
{
|
||||
if (m_is_owning) {
|
||||
kfree_sized(m_data, size_in_bytes());
|
||||
kfree(m_data);
|
||||
}
|
||||
m_data = nullptr;
|
||||
}
|
||||
@@ -96,7 +96,7 @@ public:
|
||||
__builtin_memcpy(m_data, previous_data, previous_size_bytes);
|
||||
if ((previous_size % 8) != 0)
|
||||
set_range(previous_size, 8 - previous_size % 8, default_value);
|
||||
kfree_sized(previous_data, previous_size_bytes);
|
||||
kfree(previous_data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
munmap((void*)chunk, m_chunk_size);
|
||||
#endif
|
||||
} else {
|
||||
kfree_sized((void*)chunk, m_chunk_size);
|
||||
kfree((void*)chunk);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
{
|
||||
if (this != &other) {
|
||||
if (!m_inline)
|
||||
kfree_sized(m_outline_buffer, m_outline_capacity);
|
||||
kfree(m_outline_buffer);
|
||||
move_from(move(other));
|
||||
}
|
||||
return *this;
|
||||
@@ -176,7 +176,7 @@ public:
|
||||
void clear()
|
||||
{
|
||||
if (!m_inline) {
|
||||
kfree_sized(m_outline_buffer, m_outline_capacity);
|
||||
kfree(m_outline_buffer);
|
||||
m_inline = true;
|
||||
}
|
||||
m_size = 0;
|
||||
@@ -353,10 +353,9 @@ private:
|
||||
{
|
||||
// m_inline_buffer and m_outline_buffer are part of a union, so save the pointer
|
||||
auto* outline_buffer = m_outline_buffer;
|
||||
auto outline_capacity = m_outline_capacity;
|
||||
if (!may_discard_existing_data)
|
||||
__builtin_memcpy(m_inline_buffer, outline_buffer, size);
|
||||
kfree_sized(outline_buffer, outline_capacity);
|
||||
kfree(outline_buffer);
|
||||
m_inline = true;
|
||||
}
|
||||
|
||||
@@ -377,7 +376,7 @@ private:
|
||||
__builtin_memcpy(new_buffer, data(), m_size);
|
||||
} else if (m_outline_buffer) {
|
||||
__builtin_memcpy(new_buffer, m_outline_buffer, min(new_capacity, m_outline_capacity));
|
||||
kfree_sized(m_outline_buffer, m_outline_capacity);
|
||||
kfree(m_outline_buffer);
|
||||
}
|
||||
|
||||
m_outline_buffer = new_buffer;
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
|
||||
void operator delete(void* ptr)
|
||||
{
|
||||
kfree_sized(ptr, allocation_size_for_stringimpl(static_cast<ByteStringImpl*>(ptr)->m_length));
|
||||
kfree(ptr);
|
||||
}
|
||||
|
||||
static ByteStringImpl& the_empty_stringimpl();
|
||||
|
||||
@@ -77,12 +77,22 @@ target_link_libraries(AK PRIVATE FastFloat::fast_float)
|
||||
find_package(fmt CONFIG REQUIRED)
|
||||
target_link_libraries(AK PRIVATE fmt::fmt)
|
||||
|
||||
set(MIMALLOC_VISIBILITY PRIVATE)
|
||||
if (NOT BUILD_SHARED_LIBS)
|
||||
set(MIMALLOC_VISIBILITY PUBLIC)
|
||||
endif()
|
||||
target_link_libraries(AK ${MIMALLOC_VISIBILITY} mimalloc)
|
||||
|
||||
# FIXME: Make this generic for all imported shared library dependencies and apply globally
|
||||
if (BUILD_SHARED_LIBS AND NOT CMAKE_SKIP_INSTALL_RULES AND NOT "${VCPKG_INSTALLED_DIR}" STREQUAL "")
|
||||
install(IMPORTED_RUNTIME_ARTIFACTS simdutf::simdutf
|
||||
LIBRARY COMPONENT Lagom_Runtime NAMELINK_COMPONENT Lagom_Development
|
||||
FRAMEWORK COMPONENT Lagom_Runtime
|
||||
)
|
||||
install(IMPORTED_RUNTIME_ARTIFACTS mimalloc
|
||||
LIBRARY COMPONENT Lagom_Runtime NAMELINK_COMPONENT Lagom_Development
|
||||
FRAMEWORK COMPONENT Lagom_Runtime
|
||||
)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
@@ -108,6 +118,14 @@ elseif (APPLE)
|
||||
set(ASSERTION_HANDLER_VISIBILITY INTERFACE)
|
||||
endif()
|
||||
target_link_options(AK ${ASSERTION_HANDLER_VISIBILITY} LINKER:-U,_ak_assertion_handler)
|
||||
if (NOT BUILD_SHARED_LIBS)
|
||||
target_link_options(AK INTERFACE
|
||||
LINKER:-u,_ladybird_rust_alloc
|
||||
LINKER:-u,_ladybird_rust_alloc_zeroed
|
||||
LINKER:-u,_ladybird_rust_dealloc
|
||||
LINKER:-u,_ladybird_rust_realloc
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Manually install AK headers
|
||||
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
return;
|
||||
for (size_t i = 0; i < m_size; ++i)
|
||||
m_elements[i].~T();
|
||||
kfree_sized(m_elements, storage_allocation_size(m_size));
|
||||
kfree(m_elements);
|
||||
m_elements = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
kfree_sized(m_buckets, size_in_bytes(capacity()));
|
||||
kfree(m_buckets);
|
||||
}
|
||||
|
||||
HashTable(HashTable const& other)
|
||||
@@ -622,7 +622,6 @@ private:
|
||||
VERIFY(new_capacity >= size());
|
||||
|
||||
auto* old_buckets = m_buckets;
|
||||
auto old_buckets_size = size_in_bytes(capacity());
|
||||
Iterator old_iter = begin();
|
||||
|
||||
auto* new_buckets = kcalloc(1, size_in_bytes(new_capacity));
|
||||
@@ -644,7 +643,7 @@ private:
|
||||
it->~T();
|
||||
}
|
||||
|
||||
kfree_sized(old_buckets, old_buckets_size);
|
||||
kfree(old_buckets);
|
||||
return {};
|
||||
}
|
||||
void rehash(size_t new_capacity)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/kmalloc.h>
|
||||
|
||||
namespace AK::Detail {
|
||||
|
||||
@@ -26,7 +27,7 @@ public:
|
||||
VERIFY(byte_count);
|
||||
|
||||
auto capacity = allocation_size_for_string_data(byte_count);
|
||||
void* slot = malloc(capacity);
|
||||
void* slot = kmalloc(capacity);
|
||||
if (!slot)
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
@@ -52,7 +53,7 @@ public:
|
||||
VERIFY(byte_count > MAX_SHORT_STRING_BYTE_COUNT);
|
||||
|
||||
auto capacity = sizeof(StringData) + sizeof(StringData::SubstringData);
|
||||
void* slot = malloc(capacity);
|
||||
void* slot = kmalloc(capacity);
|
||||
if (!slot)
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
@@ -66,7 +67,7 @@ public:
|
||||
|
||||
void operator delete(void* ptr)
|
||||
{
|
||||
free(ptr);
|
||||
kfree(ptr);
|
||||
}
|
||||
|
||||
~StringData()
|
||||
|
||||
@@ -20,11 +20,9 @@ namespace AK::Detail {
|
||||
|
||||
NonnullRefPtr<Utf16StringData> Utf16StringData::create_uninitialized(StorageType storage_type, size_t code_unit_length)
|
||||
{
|
||||
auto allocation_size = storage_type == Utf16StringData::StorageType::ASCII
|
||||
? sizeof(Utf16StringData) + (sizeof(char) * code_unit_length)
|
||||
: sizeof(Utf16StringData) + (sizeof(char16_t) * code_unit_length);
|
||||
auto allocation_size = allocation_size_for_string_data(storage_type == Utf16StringData::StorageType::ASCII, code_unit_length);
|
||||
|
||||
void* slot = malloc(allocation_size);
|
||||
void* slot = kmalloc(allocation_size);
|
||||
VERIFY(slot);
|
||||
|
||||
return adopt_ref(*new (slot) Utf16StringData(storage_type, code_unit_length));
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Utf16View.h>
|
||||
#include <AK/kmalloc.h>
|
||||
|
||||
namespace AK::Detail {
|
||||
|
||||
@@ -52,7 +53,7 @@ public:
|
||||
|
||||
void operator delete(void* ptr)
|
||||
{
|
||||
free(ptr);
|
||||
kfree(ptr);
|
||||
}
|
||||
|
||||
[[nodiscard]] ALWAYS_INLINE bool operator==(Utf16StringData const& other) const
|
||||
@@ -130,6 +131,13 @@ private:
|
||||
template<typename ViewType>
|
||||
static NonnullRefPtr<Utf16StringData> create_from_code_point_iterable(ViewType const&);
|
||||
|
||||
[[nodiscard]] static constexpr size_t allocation_size_for_string_data(bool has_ascii_storage, size_t code_unit_length)
|
||||
{
|
||||
return has_ascii_storage
|
||||
? sizeof(Utf16StringData) + (sizeof(char) * code_unit_length)
|
||||
: sizeof(Utf16StringData) + (sizeof(char16_t) * code_unit_length);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t calculate_code_point_length() const;
|
||||
|
||||
// We store whether this string has ASCII or UTF-16 storage by setting the most significant bit of m_length_in_code_units
|
||||
|
||||
@@ -481,7 +481,7 @@ public:
|
||||
{
|
||||
clear_with_capacity();
|
||||
if (m_metadata.outline_buffer) {
|
||||
kfree_sized(m_metadata.outline_buffer, m_capacity * sizeof(StorageType));
|
||||
kfree(m_metadata.outline_buffer);
|
||||
m_metadata.outline_buffer = nullptr;
|
||||
}
|
||||
reset_capacity();
|
||||
@@ -862,7 +862,7 @@ public:
|
||||
}
|
||||
}
|
||||
if (m_metadata.outline_buffer)
|
||||
kfree_sized(m_metadata.outline_buffer, m_capacity * sizeof(StorageType));
|
||||
kfree(m_metadata.outline_buffer);
|
||||
m_metadata.outline_buffer = new_buffer;
|
||||
m_capacity = new_capacity;
|
||||
update_metadata(); // We have *some* space, we just allocated it.
|
||||
|
||||
158
AK/kmalloc.cpp
158
AK/kmalloc.cpp
@@ -68,4 +68,162 @@ nothrow_t const nothrow;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
# include <cstddef>
|
||||
# include <cstring>
|
||||
|
||||
# if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
// LeakSanitizer does not reliably trace references stored in mimalloc-managed
|
||||
// AK containers, so sanitizer builds fall back to the system allocator.
|
||||
# define AK_USE_SYSTEM_ALLOCATOR_INSTRUMENTED 1
|
||||
# else
|
||||
# include <mimalloc.h>
|
||||
# endif
|
||||
|
||||
static bool allocation_needs_explicit_alignment(size_t alignment)
|
||||
{
|
||||
return alignment > alignof(std::max_align_t);
|
||||
}
|
||||
|
||||
# ifdef AK_USE_SYSTEM_ALLOCATOR_INSTRUMENTED
|
||||
|
||||
static void* aligned_alloc_with_system_allocator(size_t size, size_t alignment, bool zeroed)
|
||||
{
|
||||
void* ptr = nullptr;
|
||||
auto actual_size = size == 0 ? static_cast<size_t>(1) : size;
|
||||
if (auto result = posix_memalign(&ptr, alignment, actual_size); result != 0)
|
||||
return nullptr;
|
||||
if (zeroed)
|
||||
__builtin_memset(ptr, 0, actual_size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* ak_kcalloc(size_t count, size_t size)
|
||||
{
|
||||
return calloc(count, size);
|
||||
}
|
||||
|
||||
void* ak_kmalloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void* ak_krealloc(void* ptr, size_t size)
|
||||
{
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
|
||||
size_t ak_kmalloc_good_size(size_t size)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
void ak_kfree(void* ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void* ladybird_rust_alloc(size_t size, size_t alignment);
|
||||
void* ladybird_rust_alloc_zeroed(size_t size, size_t alignment);
|
||||
void ladybird_rust_dealloc(void* ptr, size_t alignment);
|
||||
void* ladybird_rust_realloc(void* ptr, size_t old_size, size_t new_size, size_t alignment);
|
||||
}
|
||||
|
||||
extern "C" void* ladybird_rust_alloc(size_t size, size_t alignment)
|
||||
{
|
||||
if (allocation_needs_explicit_alignment(alignment))
|
||||
return aligned_alloc_with_system_allocator(size, alignment, false);
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
extern "C" void* ladybird_rust_alloc_zeroed(size_t size, size_t alignment)
|
||||
{
|
||||
if (allocation_needs_explicit_alignment(alignment))
|
||||
return aligned_alloc_with_system_allocator(size, alignment, true);
|
||||
return calloc(1, size);
|
||||
}
|
||||
|
||||
extern "C" void ladybird_rust_dealloc(void* ptr, size_t)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
extern "C" void* ladybird_rust_realloc(void* ptr, size_t old_size, size_t new_size, size_t alignment)
|
||||
{
|
||||
if (!allocation_needs_explicit_alignment(alignment))
|
||||
return realloc(ptr, new_size);
|
||||
|
||||
auto* new_ptr = aligned_alloc_with_system_allocator(new_size, alignment, false);
|
||||
if (!new_ptr)
|
||||
return nullptr;
|
||||
if (ptr)
|
||||
__builtin_memcpy(new_ptr, ptr, old_size < new_size ? old_size : new_size);
|
||||
free(ptr);
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
# else
|
||||
|
||||
void* ak_kcalloc(size_t count, size_t size)
|
||||
{
|
||||
return mi_calloc(count, size);
|
||||
}
|
||||
|
||||
void* ak_kmalloc(size_t size)
|
||||
{
|
||||
return mi_malloc(size);
|
||||
}
|
||||
|
||||
void* ak_krealloc(void* ptr, size_t size)
|
||||
{
|
||||
return mi_realloc(ptr, size);
|
||||
}
|
||||
|
||||
size_t ak_kmalloc_good_size(size_t size)
|
||||
{
|
||||
return mi_good_size(size);
|
||||
}
|
||||
|
||||
void ak_kfree(void* ptr)
|
||||
{
|
||||
mi_free(ptr);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void* ladybird_rust_alloc(size_t size, size_t alignment);
|
||||
void* ladybird_rust_alloc_zeroed(size_t size, size_t alignment);
|
||||
void ladybird_rust_dealloc(void* ptr, size_t alignment);
|
||||
void* ladybird_rust_realloc(void* ptr, size_t old_size, size_t new_size, size_t alignment);
|
||||
}
|
||||
|
||||
extern "C" void* ladybird_rust_alloc(size_t size, size_t alignment)
|
||||
{
|
||||
if (allocation_needs_explicit_alignment(alignment))
|
||||
return mi_malloc_aligned(size, alignment);
|
||||
return mi_malloc(size);
|
||||
}
|
||||
|
||||
extern "C" void* ladybird_rust_alloc_zeroed(size_t size, size_t alignment)
|
||||
{
|
||||
if (allocation_needs_explicit_alignment(alignment))
|
||||
return mi_zalloc_aligned(size, alignment);
|
||||
return mi_zalloc(size);
|
||||
}
|
||||
|
||||
extern "C" void ladybird_rust_dealloc(void* ptr, size_t)
|
||||
{
|
||||
mi_free(ptr);
|
||||
}
|
||||
|
||||
extern "C" void* ladybird_rust_realloc(void* ptr, size_t, size_t new_size, size_t alignment)
|
||||
{
|
||||
if (allocation_needs_explicit_alignment(alignment))
|
||||
return mi_realloc_aligned(ptr, new_size, alignment);
|
||||
return mi_realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
46
AK/kmalloc.h
46
AK/kmalloc.h
@@ -12,25 +12,43 @@
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define kcalloc calloc
|
||||
#define kmalloc malloc
|
||||
#define kmalloc_good_size malloc_good_size
|
||||
#if defined(AK_OS_SERENITY)
|
||||
# define kcalloc calloc
|
||||
# define kfree free
|
||||
# define kmalloc malloc
|
||||
# define krealloc realloc
|
||||
# define kmalloc_good_size malloc_good_size
|
||||
#else
|
||||
[[nodiscard]] void* ak_kcalloc(size_t count, size_t size);
|
||||
void ak_kfree(void* ptr);
|
||||
[[nodiscard]] void* ak_kmalloc(size_t size);
|
||||
[[nodiscard]] void* ak_krealloc(void* ptr, size_t size);
|
||||
[[nodiscard]] size_t ak_kmalloc_good_size(size_t size);
|
||||
|
||||
inline void kfree_sized(void* ptr, size_t)
|
||||
[[nodiscard]] inline void* kcalloc(size_t count, size_t size)
|
||||
{
|
||||
free(ptr);
|
||||
return ak_kcalloc(count, size);
|
||||
}
|
||||
|
||||
#ifndef AK_OS_SERENITY
|
||||
# include <AK/Types.h>
|
||||
|
||||
# ifndef AK_OS_MACOS
|
||||
extern "C" {
|
||||
inline size_t malloc_good_size(size_t size) { return size; }
|
||||
inline void kfree(void* ptr)
|
||||
{
|
||||
ak_kfree(ptr);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline void* kmalloc(size_t size)
|
||||
{
|
||||
return ak_kmalloc(size);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline void* krealloc(void* ptr, size_t size)
|
||||
{
|
||||
return ak_krealloc(ptr, size);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline size_t kmalloc_good_size(size_t size)
|
||||
{
|
||||
return ak_kmalloc_good_size(size);
|
||||
}
|
||||
# else
|
||||
# include <malloc/malloc.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
using std::nothrow;
|
||||
|
||||
Reference in New Issue
Block a user