memory.cpp (6159B)
1 #include "memory.hpp" 2 3 #if defined(_WIN32) 4 # include <windows.h> 5 # include <memoryapi.h> 6 namespace { 7 uint8_t* Win32PageAlloc(size_t size, size_t alignment) 8 { 9 assert(Test::Utility::IsPowerOfTwo(alignment)); 10 assert(Test::Memory::MEM_ALIGN_4_KIB <= alignment); 11 12 uintptr_t aligned_size = Test::Utility::AlignNext(size, alignment); 13 14 DWORD prot = PAGE_READWRITE; 15 DWORD flags = MEM_RESERVE | MEM_COMMIT; 16 LPVOID ptr = VirtualAlloc(nullptr, aligned_size, flags, prot); 17 18 return (uint8_t*) ptr; 19 } 20 21 Test::Memory::MirrorAllocResult Win32MirrorAlloc(size_t size, size_t mirrors) 22 { 23 assert(Test::Utility::IsAlignedTo(size, Test::Memory::MEM_ALIGN_4_KIB)); 24 25 Test::Memory::MirrorAllocResult result{}; 26 27 (void) size; 28 (void) mirrors; 29 30 return result; 31 } 32 } 33 34 #elif defined(__linux__) 35 # define _GNU_SOURCE 1 36 # include <sys/mman.h> 37 # include <unistd.h> 38 namespace { 39 uint8_t* LinuxPageAlloc(size_t size, size_t alignment) 40 { 41 assert(Test::Utility::IsPowerOfTwo(alignment)); 42 assert(Test::Memory::MEM_ALIGN_4_KIB <= alignment); 43 44 int prot = PROT_READ | PROT_WRITE; 45 int flags = MAP_PRIVATE | MAP_ANONYMOUS; 46 47 uintptr_t aligned_size = Test::Utility::AlignNext(size, alignment); 48 void *unaligned_ptr = mmap(nullptr, aligned_size, PROT_NONE, flags, -1, 0); 49 if (unaligned_ptr == MAP_FAILED) 50 return nullptr; 51 52 uintptr_t aligned_base = Test::Utility::AlignNext((uintptr_t) unaligned_ptr, alignment); 53 void *aligned_ptr = mmap((void *) aligned_base, size, prot, flags | MAP_FIXED, -1, 0); 54 assert(aligned_ptr != MAP_FAILED); 55 56 if (Test::Utility::IsAlignedTo(aligned_base, Test::Memory::MEM_ALIGN_2_MIB)) 57 madvise(aligned_ptr, size, MADV_HUGEPAGE); 58 59 munmap(unaligned_ptr, aligned_base - (uintptr_t) aligned_ptr); 60 61 return (uint8_t*) aligned_ptr; 62 } 63 64 Test::Memory::MirrorAllocResult LinuxMirrorAlloc(size_t size, size_t mirrors) 65 { 66 assert(Test::Utility::IsAlignedTo(size, Test::Memory::MEM_ALIGN_4_KIB)); 67 68 Test::Memory::MirrorAllocResult result{}; 69 70 result.len = size; 71 result.cap = size * mirrors; 72 73 int prot = PROT_READ | PROT_WRITE; 74 int buffer_flags = MAP_PRIVATE | MAP_ANONYMOUS; 75 int mirror_flags = MAP_SHARED | MAP_FIXED; 76 77 result.memfd = memfd_create("linux mirror alloc", MFD_CLOEXEC); 78 if (result.memfd < 0) { 79 goto error; 80 } 81 82 ftruncate(result.memfd, result.cap); 83 84 result.ptr = (uint8_t*) mmap(nullptr, result.cap, PROT_NONE, buffer_flags, -1, 0); 85 if (result.ptr == MAP_FAILED) { 86 close(result.memfd); 87 goto error; 88 } 89 90 for (size_t i = 0; i < mirrors; i++) { 91 uint8_t* base = result.ptr + (result.len * i); 92 void *mirror = mmap(base, result.len, prot, mirror_flags, result.memfd, 0); 93 if (mirror == MAP_FAILED) { 94 munmap(result.ptr, result.cap); 95 close(result.memfd); 96 goto error; 97 } 98 } 99 100 return result; 101 102 error: 103 result.memfd = -1; 104 result.ptr = nullptr; 105 result.len = result.cap = 0; 106 107 return result; 108 } 109 } 110 111 #else 112 # error "Unknown platform, must be one of: windows linux" 113 #endif 114 115 namespace Test::Memory { 116 uint8_t *GenAlloc(size_t size, size_t alignment) { 117 assert(Utility::IsPowerOfTwo(alignment)); 118 119 size_t upper_bound = size + (alignment - 1); 120 121 uintptr_t ptr = (uintptr_t) malloc(upper_bound); 122 uintptr_t res = Utility::AlignNext(ptr, alignment); 123 124 return (uint8_t *) res; 125 } 126 127 uint8_t *PageAlloc(size_t size, size_t alignment) { 128 if (alignment < MEM_ALIGN_4_KIB) 129 alignment = MEM_ALIGN_4_KIB; 130 131 #if defined(_WIN32) 132 return Win32PageAlloc(size, alignment); 133 #elif defined(__linux__) 134 return LinuxPageAlloc(size, alignment); 135 #else 136 return nullptr; 137 #endif 138 } 139 140 MirrorAllocResult MirrorAlloc(size_t size, size_t mirrors) { 141 #if defined(_WIN32) 142 return Win32MirrorAlloc(size, mirrors); 143 #elif defined(__linux__) 144 return LinuxMirrorAlloc(size, mirrors); 145 #else 146 return nullptr; 147 #endif 148 } 149 150 void LinearAlloc::Init(uint8_t *buf, size_t cap) 151 { 152 ptr = buf; 153 this->cap = cap; 154 len = 0; 155 } 156 157 void LinearAlloc::Reset() 158 { 159 len = 0; 160 } 161 162 uint8_t *LinearAlloc::AllocRaw(size_t size, size_t alignment) 163 { 164 assert(size); 165 assert(alignment); 166 assert(Utility::IsPowerOfTwo(alignment)); 167 168 uintptr_t currentPtr = (uintptr_t) ptr + len; 169 uintptr_t endPtr = (uintptr_t) ptr + cap; 170 171 uintptr_t alignedPtr = Utility::AlignNext(currentPtr, alignment); 172 if (endPtr < alignedPtr + size) 173 return nullptr; 174 175 len = (alignedPtr + size) - (uintptr_t) ptr; 176 177 return (uint8_t *) alignedPtr; 178 } 179 180 void BlockAlloc::Init(uint8_t *buf, size_t cap, size_t block_size, size_t block_alignment) 181 { 182 assert(Utility::IsPowerOfTwo(block_alignment)); 183 assert(Utility::IsAlignedTo((uintptr_t) buf, block_alignment)); 184 185 assert(sizeof(FreeListNode) <= block_size); 186 assert(alignof(FreeListNode) <= block_alignment); 187 188 ptr = buf; 189 this->cap = cap; 190 this->block_size = block_size; 191 this->block_alignment = block_alignment; 192 193 Reset(); 194 } 195 196 void BlockAlloc::Reset() 197 { 198 freelist = nullptr; 199 200 for (size_t i = 0; i < cap / block_size; i++) { 201 uint8_t *block = ptr + (block_size * i); 202 203 FreeListNode *node = (FreeListNode *) block; 204 node->next = freelist; 205 freelist = node; 206 } 207 } 208 209 uint8_t *BlockAlloc::AllocRaw() 210 { 211 if (freelist == nullptr) 212 return nullptr; 213 214 FreeListNode *node = freelist; 215 freelist = node->next; 216 217 return (uint8_t *) node; 218 } 219 220 void BlockAlloc::Free(uint8_t *ptr) 221 { 222 FreeListNode *node = (FreeListNode *) ptr; 223 node->next = freelist; 224 freelist = node; 225 } 226 227 void StackAlloc::Init(uint8_t *buf, size_t cap) 228 { 229 ptr = buf; 230 this->cap = cap; 231 len = 0; 232 } 233 234 void StackAlloc::Reset() 235 { 236 len = 0; 237 } 238 239 StackAlloc::Result StackAlloc::AllocRaw(size_t size, size_t alignment) 240 { 241 assert(size); 242 assert(alignment); 243 assert(Utility::IsPowerOfTwo(alignment)); 244 245 uintptr_t currentPtr = (uintptr_t) ptr + len; 246 uintptr_t endPtr = (uintptr_t) ptr + cap; 247 248 uintptr_t alignedPtr = Utility::AlignNext(currentPtr, alignment); 249 if (endPtr < alignedPtr + size) { 250 Result result{}; 251 return result; 252 } 253 254 len = (alignedPtr + size) - (uintptr_t) ptr; 255 256 Result result{}; 257 result.ptr = (uint8_t *) alignedPtr; 258 result.len = size; 259 result.saveptr = (uint8_t *) currentPtr; 260 return result; 261 } 262 263 void StackAlloc::Free(Result region) 264 { 265 len = region.saveptr - ptr; 266 } 267 }