Skip to content

Commit ac153a8

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

1 file changed

Lines changed: 98 additions & 17 deletions

File tree

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

Lines changed: 98 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,92 @@ 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(), align);
2880
return nullptr;
2981
}
30-
const size_t real_size{size + MALLOC_REPLACER_SIZE_OFFSET};
31-
void* ptr{RuntimeAllocator::get().alloc_script_memory(real_size)};
3282

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

41110
inline void* calloc(size_t num, size_t size) noexcept {
@@ -47,10 +116,17 @@ inline void* calloc(size_t num, size_t size) noexcept {
47116
}
48117

49118
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));
119+
if (unlikely(ptr == nullptr)) {
120+
return;
53121
}
122+
123+
const size_t cb_size{sizeof(details::control_block)};
124+
const auto mem{reinterpret_cast<uint64_t>(ptr)};
125+
126+
const auto cb{details::control_block::from_raw(*reinterpret_cast<uint64_t*>(mem - cb_size))}; // NOLINT
127+
void* base{reinterpret_cast<void*>(mem - cb.base_offset)}; // NOLINT
128+
129+
RuntimeAllocator::get().free_script_memory(base, cb.size);
54130
}
55131

56132
inline void* realloc(void* ptr, size_t new_size) noexcept {
@@ -63,13 +139,18 @@ inline void* realloc(void* ptr, size_t new_size) noexcept {
63139
return nullptr;
64140
}
65141

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

69150
void* new_ptr{kphp::memory::script::alloc(new_size)};
70151
if (likely(new_ptr != nullptr)) {
71152
std::memcpy(new_ptr, ptr, std::min(new_size, old_size));
72-
RuntimeAllocator::get().free_script_memory(real_ptr, old_size);
153+
RuntimeAllocator::get().free_script_memory(old_base, old_size);
73154
}
74155
return new_ptr;
75156
}

0 commit comments

Comments
 (0)