Skip to content

Commit 2bc4751

Browse files
committed
Add alloc_align
Signed-off-by: Petr Shumilov <p.shumilov@vkteam.ru>
1 parent 0a30b53 commit 2bc4751

1 file changed

Lines changed: 99 additions & 17 deletions

File tree

runtime-common/core/allocator/script-malloc-interface.h

Lines changed: 99 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,93 @@ namespace memory {
1919

2020
namespace script {
2121

22-
constexpr int64_t MALLOC_REPLACER_SIZE_OFFSET = sizeof(size_t);
23-
constexpr uint64_t MALLOC_REPLACER_MAX_ALLOC = 0xFFFFFF00;
22+
constexpr uint64_t MALLOC_REPLACER_MAX_ALLOC = 0xFFFFFF00; // 4GiB
23+
24+
namespace details {
25+
struct control_block {
26+
static constexpr auto SIZE_FIELD_BITSIZE{48};
27+
static constexpr auto BASE_OFFSET_FIELD_BITSIZE{16};
28+
static constexpr uint64_t BLOCK_SIZE_MASK{(1UL << SIZE_FIELD_BITSIZE) - 1};
29+
static constexpr uint64_t BASE_OFFSET_MASK{(1UL << BASE_OFFSET_FIELD_BITSIZE) - 1};
30+
31+
static constexpr uint64_t max_size() noexcept {
32+
return 1UL << SIZE_FIELD_BITSIZE;
33+
}
34+
35+
static constexpr uint64_t max_alignment() noexcept {
36+
return 1UL << BASE_OFFSET_FIELD_BITSIZE;
37+
}
38+
39+
uint64_t raw() const noexcept {
40+
return (static_cast<std::uint64_t>(base_offset) << SIZE_FIELD_BITSIZE) | (static_cast<std::uint64_t>(size) & BLOCK_SIZE_MASK);
41+
}
42+
43+
static control_block from_raw(std::uint64_t raw) noexcept {
44+
return control_block{.size = raw & BLOCK_SIZE_MASK, .base_offset = static_cast<uint16_t>((raw >> SIZE_FIELD_BITSIZE) & BASE_OFFSET_MASK)};
45+
}
46+
47+
uint64_t size : SIZE_FIELD_BITSIZE;
48+
uint16_t base_offset : BASE_OFFSET_FIELD_BITSIZE;
49+
};
50+
51+
inline bool is_power_of_2(uint64_t v) noexcept {
52+
return v && !(v & (v - 1));
53+
}
54+
55+
static_assert(sizeof(control_block) == sizeof(uint64_t), "Control block's size must be equal to uint64");
56+
57+
} // namespace details
2458

2559
inline void* alloc(size_t size) noexcept {
26-
if (unlikely(size > MALLOC_REPLACER_MAX_ALLOC - MALLOC_REPLACER_SIZE_OFFSET)) {
27-
php_warning("attempt to allocate too much memory by malloc replacer : %lu", size);
60+
const size_t cb_size{sizeof(details::control_block)};
61+
if (unlikely(size > std::min(details::control_block::max_size(), MALLOC_REPLACER_MAX_ALLOC) - cb_size)) {
62+
php_warning("attempt to allocate too much memory by malloc replacer, requested : %lu", size);
63+
return nullptr;
64+
}
65+
const size_t total_size{size + cb_size};
66+
void* base{RuntimeAllocator::get().alloc_script_memory(total_size)};
67+
if (unlikely(base == nullptr)) {
68+
php_warning("not enough script memory to allocate, requested : %lu, actual requested: %lu", size, total_size);
69+
return base;
70+
}
71+
*(reinterpret_cast<std::uint64_t*>(base)) = details::control_block{.size = total_size, .base_offset = cb_size}.raw();
72+
return reinterpret_cast<void*>(reinterpret_cast<uint64_t>(base) + cb_size); // NOLINT
73+
}
74+
75+
inline void* alloc_aligned(size_t size, std::align_val_t alignment) noexcept {
76+
// Check that provided alignment is power of two
77+
const size_t align{static_cast<uint64_t>(alignment)};
78+
if (unlikely(align == 0 || !details::is_power_of_2(align) || align >= details::control_block::max_alignment())) {
79+
php_warning("allocation alignment have to be non-zero power of two and not greater than %" PRIi64 ", got : %lu", details::control_block::max_alignment(),
80+
align);
2881
return nullptr;
2982
}
30-
const size_t real_size{size + MALLOC_REPLACER_SIZE_OFFSET};
31-
void* ptr{RuntimeAllocator::get().alloc_script_memory(real_size)};
3283

33-
if (unlikely(ptr == nullptr)) {
34-
php_warning("not enough script memory to allocate: %lu", size);
35-
return ptr;
84+
// Check that memory is enough
85+
const size_t cb_size{sizeof(details::control_block)};
86+
if (unlikely(size > std::min(details::control_block::max_size(), MALLOC_REPLACER_MAX_ALLOC) - (align - 1) - cb_size)) {
87+
php_warning("attempt to allocate too much memory by malloc replacer, requested : %lu", size);
88+
return nullptr;
89+
}
90+
91+
// Request mem from underlying memory manager
92+
const size_t total_size{size + (align - 1) + cb_size};
93+
void* base{RuntimeAllocator::get().alloc_script_memory(total_size)};
94+
if (unlikely(base == nullptr)) {
95+
php_warning("not enough script memory to allocate, requested : %lu, actual requested: %lu", size, total_size);
96+
return base;
3697
}
37-
*static_cast<size_t*>(ptr) = real_size;
38-
return static_cast<std::byte*>(ptr) + MALLOC_REPLACER_SIZE_OFFSET;
98+
99+
const uint64_t base_u{reinterpret_cast<uint64_t>(base)};
100+
// The smallest multiple of `align` greater than or equal to requested memory
101+
const uint64_t aligned_u{((base_u + cb_size) + (align - 1)) & ~(align - 1)};
102+
const uint64_t base_offset_u{aligned_u - base_u};
103+
104+
// Save control block
105+
*(reinterpret_cast<std::uint64_t*>(aligned_u - cb_size)) = // NOLINT
106+
details::control_block{.size = total_size, .base_offset = static_cast<std::uint16_t>(base_offset_u)}.raw();
107+
108+
return reinterpret_cast<void*>(aligned_u); // NOLINT
39109
}
40110

41111
inline void* calloc(size_t num, size_t size) noexcept {
@@ -47,10 +117,17 @@ inline void* calloc(size_t num, size_t size) noexcept {
47117
}
48118

49119
inline void free(void* ptr) noexcept {
50-
if (likely(ptr != nullptr)) {
51-
void* real_ptr{static_cast<std::byte*>(ptr) - MALLOC_REPLACER_SIZE_OFFSET};
52-
RuntimeAllocator::get().free_script_memory(real_ptr, *static_cast<size_t*>(real_ptr));
120+
if (unlikely(ptr == nullptr)) {
121+
return;
53122
}
123+
124+
const size_t cb_size{sizeof(details::control_block)};
125+
const auto mem{reinterpret_cast<uint64_t>(ptr)};
126+
127+
const auto cb{details::control_block::from_raw(*reinterpret_cast<uint64_t*>(mem - cb_size))}; // NOLINT
128+
void* base{reinterpret_cast<void*>(mem - cb.base_offset)}; // NOLINT
129+
130+
RuntimeAllocator::get().free_script_memory(base, cb.size);
54131
}
55132

56133
inline void* realloc(void* ptr, size_t new_size) noexcept {
@@ -63,13 +140,18 @@ inline void* realloc(void* ptr, size_t new_size) noexcept {
63140
return nullptr;
64141
}
65142

66-
void* real_ptr{static_cast<std::byte*>(ptr) - sizeof(size_t)};
67-
const size_t old_size{*static_cast<size_t*>(real_ptr)};
143+
const size_t cb_size{sizeof(details::control_block)};
144+
const auto mem{reinterpret_cast<uint64_t>(ptr)};
145+
146+
const auto cb{details::control_block::from_raw(*reinterpret_cast<uint64_t*>(mem - cb_size))}; // NOLINT
147+
148+
void* old_base{reinterpret_cast<void*>(mem - cb.base_offset)}; // NOLINT
149+
const size_t old_size{cb.size};
68150

69151
void* new_ptr{kphp::memory::script::alloc(new_size)};
70152
if (likely(new_ptr != nullptr)) {
71153
std::memcpy(new_ptr, ptr, std::min(new_size, old_size));
72-
RuntimeAllocator::get().free_script_memory(real_ptr, old_size);
154+
RuntimeAllocator::get().free_script_memory(old_base, old_size);
73155
}
74156
return new_ptr;
75157
}

0 commit comments

Comments
 (0)