PostgreSQL Source Code git master
Loading...
Searching...
No Matches
SectionMemoryManager.cpp
Go to the documentation of this file.
1/*
2 * This file is from LLVM 22 (originally pull request #71968), with minor
3 * modifications to avoid name clash and work with older LLVM versions. It
4 * replaces llvm::SectionMemoryManager, and is injected into llvm::RuntimeDyld
5 * to fix a memory layout bug on large memory ARM systems on LLVM < 22.
6 *
7 * We can remove this code (.cpp, .h, .LICENSE) once LLVM 22 is our minimum
8 * supported version or we've switched to JITLink for at least Aarch64.
9 *
10 * This file is a modified copy of a part of the LLVM source code that
11 * we would normally access from the LLVM library. It is therefore
12 * covered by the license at https://llvm.org/LICENSE.txt, reproduced
13 * verbatim in SectionMemoryManager.LICENSE in fulfillment of clause
14 * 4a. The bugfix changes from the pull request are also covered, per
15 * clause 5.
16 */
17
18//===- SectionMemoryManager.cpp - Memory manager for MCJIT/RtDyld *- C++ -*-==//
19//
20// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
21// See https://llvm.org/LICENSE.txt for license information.
22// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
23//
24//===----------------------------------------------------------------------===//
25//
26// This file implements the section-based memory manager used by the MCJIT
27// execution engine and RuntimeDyld
28//
29//===----------------------------------------------------------------------===//
30
32
33#ifdef USE_LLVM_BACKPORT_SECTION_MEMORY_MANAGER
34
36#include <llvm/Support/MathExtras.h>
37#include <llvm/Support/Process.h>
38
39namespace llvm {
40namespace backport {
41
42bool SectionMemoryManager::hasSpace(const MemoryGroup &MemGroup,
43 uintptr_t Size) const {
44 for (const FreeMemBlock &FreeMB : MemGroup.FreeMem) {
45 if (FreeMB.Free.allocatedSize() >= Size)
46 return true;
47 }
48 return false;
49}
50
51#if LLVM_VERSION_MAJOR < 16
61#else
65#endif
66 if (CodeSize == 0 && RODataSize == 0 && RWDataSize == 0)
67 return;
68
69 static const size_t PageSize = sys::Process::getPageSizeEstimate();
70
71 // Code alignment needs to be at least the stub alignment - however, we
72 // don't have an easy way to get that here so as a workaround, we assume
73 // it's 8, which is the largest value I observed across all platforms.
74 constexpr uint64_t StubAlign = 8;
75 CodeAlign = Align(std::max(CodeAlign.value(), StubAlign));
76 RODataAlign = Align(std::max(RODataAlign.value(), StubAlign));
77 RWDataAlign = Align(std::max(RWDataAlign.value(), StubAlign));
78
79 // Get space required for each section. Use the same calculation as
80 // allocateSection because we need to be able to satisfy it.
86
90 // Sufficient space in contiguous block already available.
91 return;
92 }
93
94 // MemoryManager does not have functions for releasing memory after it's
95 // allocated. Normally it tries to use any excess blocks that were allocated
96 // due to page alignment, but if we have insufficient free memory for the
97 // request this can lead to allocating disparate memory that can violate the
98 // ARM ABI. Clear free memory so only the new allocations are used, but do
99 // not release allocated memory as it may still be in-use.
100 CodeMem.FreeMem.clear();
101 RODataMem.FreeMem.clear();
102 RWDataMem.FreeMem.clear();
103
104 // Round up to the nearest page size. Blocks must be page-aligned.
110
111 std::error_code ec;
112 sys::MemoryBlock MB = MMapper->allocateMappedMemory(
114 sys::Memory::MF_READ | sys::Memory::MF_WRITE, ec);
115 if (ec) {
116 return;
117 }
118 // CodeMem will arbitrarily own this MemoryBlock to handle cleanup.
119 CodeMem.AllocatedMem.push_back(MB);
120 uintptr_t Addr = (uintptr_t)MB.base();
121 FreeMemBlock FreeMB;
122 FreeMB.PendingPrefixIndex = (unsigned)-1;
123
124 if (CodeSize > 0) {
126 FreeMB.Free = sys::MemoryBlock((void *)Addr, RequiredCodeSize);
127 CodeMem.FreeMem.push_back(FreeMB);
129 }
130
131 if (RODataSize > 0) {
133 FreeMB.Free = sys::MemoryBlock((void *)Addr, RequiredRODataSize);
134 RODataMem.FreeMem.push_back(FreeMB);
136 }
137
138 if (RWDataSize > 0) {
140 FreeMB.Free = sys::MemoryBlock((void *)Addr, RequiredRWDataSize);
141 RWDataMem.FreeMem.push_back(FreeMB);
142 }
143}
144
146 unsigned Alignment,
147 unsigned SectionID,
149 bool IsReadOnly) {
150 if (IsReadOnly)
152 Size, Alignment);
154 Alignment);
155}
156
158 unsigned Alignment,
159 unsigned SectionID,
162 Alignment);
163}
164
167 unsigned Alignment) {
168 if (!Alignment)
169 Alignment = 16;
170
171 assert(!(Alignment & (Alignment - 1)) && "Alignment must be a power of two.");
172
174 uintptr_t Addr = 0;
175
176 MemoryGroup &MemGroup = [&]() -> MemoryGroup & {
177 switch (Purpose) {
179 return CodeMem;
181 return RODataMem;
183 return RWDataMem;
184 }
185 llvm_unreachable("Unknown SectionMemoryManager::AllocationPurpose");
186 }();
187
188 // Look in the list of free memory regions and use a block there if one
189 // is available.
190 for (FreeMemBlock &FreeMB : MemGroup.FreeMem) {
191 if (FreeMB.Free.allocatedSize() >= RequiredSize) {
192 Addr = (uintptr_t)FreeMB.Free.base();
193 uintptr_t EndOfBlock = Addr + FreeMB.Free.allocatedSize();
194 // Align the address.
195 Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);
196
197 if (FreeMB.PendingPrefixIndex == (unsigned)-1) {
198 // The part of the block we're giving out to the user is now pending
199 MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size));
200
201 // Remember this pending block, such that future allocations can just
202 // modify it rather than creating a new one
203 FreeMB.PendingPrefixIndex = MemGroup.PendingMem.size() - 1;
204 } else {
205 sys::MemoryBlock &PendingMB =
206 MemGroup.PendingMem[FreeMB.PendingPrefixIndex];
207 PendingMB = sys::MemoryBlock(PendingMB.base(),
208 Addr + Size - (uintptr_t)PendingMB.base());
209 }
210
211 // Remember how much free space is now left in this block
212 FreeMB.Free =
213 sys::MemoryBlock((void *)(Addr + Size), EndOfBlock - Addr - Size);
214 return (uint8_t *)Addr;
215 }
216 }
217
218 // No pre-allocated free block was large enough. Allocate a new memory region.
219 // Note that all sections get allocated as read-write. The permissions will
220 // be updated later based on memory group.
221 //
222 // FIXME: It would be useful to define a default allocation size (or add
223 // it as a constructor parameter) to minimize the number of allocations.
224 //
225 // FIXME: Initialize the Near member for each memory group to avoid
226 // interleaving.
227 std::error_code ec;
228 sys::MemoryBlock MB = MMapper->allocateMappedMemory(
230 sys::Memory::MF_READ | sys::Memory::MF_WRITE, ec);
231 if (ec) {
232 // FIXME: Add error propagation to the interface.
233 return nullptr;
234 }
235
236 // Save this address as the basis for our next request
237 MemGroup.Near = MB;
238
239 // Copy the address to all the other groups, if they have not
240 // been initialized.
241 if (CodeMem.Near.base() == nullptr)
242 CodeMem.Near = MB;
243 if (RODataMem.Near.base() == nullptr)
244 RODataMem.Near = MB;
245 if (RWDataMem.Near.base() == nullptr)
246 RWDataMem.Near = MB;
247
248 // Remember that we allocated this memory
249 MemGroup.AllocatedMem.push_back(MB);
250 Addr = (uintptr_t)MB.base();
251 uintptr_t EndOfBlock = Addr + MB.allocatedSize();
252
253 // Align the address.
254 Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);
255
256 // The part of the block we're giving out to the user is now pending
257 MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size));
258
259 // The allocateMappedMemory may allocate much more memory than we need. In
260 // this case, we store the unused memory as a free memory block.
261 unsigned FreeSize = EndOfBlock - Addr - Size;
262 if (FreeSize > 16) {
263 FreeMemBlock FreeMB;
264 FreeMB.Free = sys::MemoryBlock((void *)(Addr + Size), FreeSize);
265 FreeMB.PendingPrefixIndex = (unsigned)-1;
266 MemGroup.FreeMem.push_back(FreeMB);
267 }
268
269 // Return aligned address
270 return (uint8_t *)Addr;
271}
272
274 // FIXME: Should in-progress permissions be reverted if an error occurs?
275 std::error_code ec;
276
277 // Make code memory executable.
279 sys::Memory::MF_READ | sys::Memory::MF_EXEC);
280 if (ec) {
281 if (ErrMsg) {
282 *ErrMsg = ec.message();
283 }
284 return true;
285 }
286
287 // Make read-only data memory read-only.
288 ec = applyMemoryGroupPermissions(RODataMem, sys::Memory::MF_READ);
289 if (ec) {
290 if (ErrMsg) {
291 *ErrMsg = ec.message();
292 }
293 return true;
294 }
295
296 // Read-write data memory already has the correct permissions
297
298 // Some platforms with separate data cache and instruction cache require
299 // explicit cache flush, otherwise JIT code manipulations (like resolved
300 // relocations) will get to the data cache but not to the instruction cache.
302
303 return false;
304}
305
306static sys::MemoryBlock trimBlockToPageSize(sys::MemoryBlock M) {
307 static const size_t PageSize = sys::Process::getPageSizeEstimate();
308
309 size_t StartOverlap =
310 (PageSize - ((uintptr_t)M.base() % PageSize)) % PageSize;
311
312 size_t TrimmedSize = M.allocatedSize();
315
316 sys::MemoryBlock Trimmed((void *)((uintptr_t)M.base() + StartOverlap),
318
319 assert(((uintptr_t)Trimmed.base() % PageSize) == 0);
320 assert((Trimmed.allocatedSize() % PageSize) == 0);
321 assert(M.base() <= Trimmed.base() &&
322 Trimmed.allocatedSize() <= M.allocatedSize());
323
324 return Trimmed;
325}
326
327std::error_code
329 unsigned Permissions) {
330 for (sys::MemoryBlock &MB : MemGroup.PendingMem)
331 if (std::error_code EC = MMapper->protectMappedMemory(MB, Permissions))
332 return EC;
333
334 MemGroup.PendingMem.clear();
335
336 // Now go through free blocks and trim any of them that don't span the entire
337 // page because one of the pending blocks may have overlapped it.
338 for (FreeMemBlock &FreeMB : MemGroup.FreeMem) {
339 FreeMB.Free = trimBlockToPageSize(FreeMB.Free);
340 // We cleared the PendingMem list, so all these pointers are now invalid
341 FreeMB.PendingPrefixIndex = (unsigned)-1;
342 }
343
344 // Remove all blocks which are now empty
345 erase_if(MemGroup.FreeMem, [](FreeMemBlock &FreeMB) {
346 return FreeMB.Free.allocatedSize() == 0;
347 });
348
349 return std::error_code();
350}
351
353 for (sys::MemoryBlock &Block : CodeMem.PendingMem)
356}
357
359 for (MemoryGroup *Group : {&CodeMem, &RWDataMem, &RODataMem}) {
360 for (sys::MemoryBlock &Block : Group->AllocatedMem)
361 MMapper->releaseMappedMemory(Block);
362 }
363}
364
366
368
369namespace {
370// Trivial implementation of SectionMemoryManager::MemoryMapper that just calls
371// into sys::Memory.
372class DefaultMMapper final : public SectionMemoryManager::MemoryMapper {
373public:
374 sys::MemoryBlock
375 allocateMappedMemory(SectionMemoryManager::AllocationPurpose Purpose,
376 size_t NumBytes, const sys::MemoryBlock *const NearBlock,
377 unsigned Flags, std::error_code &EC) override {
378 return sys::Memory::allocateMappedMemory(NumBytes, NearBlock, Flags, EC);
379 }
380
381 std::error_code protectMappedMemory(const sys::MemoryBlock &Block,
382 unsigned Flags) override {
383 return sys::Memory::protectMappedMemory(Block, Flags);
384 }
385
386 std::error_code releaseMappedMemory(sys::MemoryBlock &M) override {
387 return sys::Memory::releaseMappedMemory(M);
388 }
389};
390} // namespace
391
393 bool ReserveAlloc)
396 if (!MMapper) {
397 OwnedMMapper = std::make_unique<DefaultMMapper>();
398 MMapper = OwnedMMapper.get();
399 }
400}
401
402} // namespace backport
403} // namespace llvm
404
405#endif
void * Block
Definition bufmgr.h:26
size_t Size
Definition c.h:689
virtual sys::MemoryBlock allocateMappedMemory(AllocationPurpose Purpose, size_t NumBytes, const sys::MemoryBlock *const NearBlock, unsigned Flags, std::error_code &EC)=0
std::unique_ptr< MemoryMapper > OwnedMMapper
SectionMemoryManager(MemoryMapper *MM=nullptr, bool ReserveAlloc=false)
virtual void reserveAllocationSpace(uintptr_t CodeSize, uint32_t CodeAlign, uintptr_t RODataSize, uint32_t RODataAlign, uintptr_t RWDataSize, uint32_t RWDataAlign) override
bool finalizeMemory(std::string *ErrMsg=nullptr) override
uint8_t * allocateSection(AllocationPurpose Purpose, uintptr_t Size, unsigned Alignment)
bool hasSpace(const MemoryGroup &MemGroup, uintptr_t Size) const
uint8_t * allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName, bool IsReadOnly) override
std::error_code applyMemoryGroupPermissions(MemoryGroup &MemGroup, unsigned Permissions)
uint8_t * allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName) override
if(enc< 0)
#define final(a, b, c)
Definition hashfn.c:116
static int fb(int x)
#define assert(x)
Definition regcustom.h:57