xref: /netbsd-src/external/apache2/llvm/dist/llvm/lib/ExecutionEngine/SectionMemoryManager.cpp (revision 82d56013d7b633d116a93943de88e08335357a7c)
17330f729Sjoerg //===- SectionMemoryManager.cpp - Memory manager for MCJIT/RtDyld *- C++ -*-==//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This file implements the section-based memory manager used by the MCJIT
107330f729Sjoerg // execution engine and RuntimeDyld
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg 
147330f729Sjoerg #include "llvm/ExecutionEngine/SectionMemoryManager.h"
157330f729Sjoerg #include "llvm/Config/config.h"
167330f729Sjoerg #include "llvm/Support/MathExtras.h"
177330f729Sjoerg #include "llvm/Support/Process.h"
187330f729Sjoerg 
197330f729Sjoerg namespace llvm {
207330f729Sjoerg 
allocateDataSection(uintptr_t Size,unsigned Alignment,unsigned SectionID,StringRef SectionName,bool IsReadOnly)217330f729Sjoerg uint8_t *SectionMemoryManager::allocateDataSection(uintptr_t Size,
227330f729Sjoerg                                                    unsigned Alignment,
237330f729Sjoerg                                                    unsigned SectionID,
247330f729Sjoerg                                                    StringRef SectionName,
257330f729Sjoerg                                                    bool IsReadOnly) {
267330f729Sjoerg   if (IsReadOnly)
277330f729Sjoerg     return allocateSection(SectionMemoryManager::AllocationPurpose::ROData,
287330f729Sjoerg                            Size, Alignment);
297330f729Sjoerg   return allocateSection(SectionMemoryManager::AllocationPurpose::RWData, Size,
307330f729Sjoerg                          Alignment);
317330f729Sjoerg }
327330f729Sjoerg 
allocateCodeSection(uintptr_t Size,unsigned Alignment,unsigned SectionID,StringRef SectionName)337330f729Sjoerg uint8_t *SectionMemoryManager::allocateCodeSection(uintptr_t Size,
347330f729Sjoerg                                                    unsigned Alignment,
357330f729Sjoerg                                                    unsigned SectionID,
367330f729Sjoerg                                                    StringRef SectionName) {
377330f729Sjoerg   return allocateSection(SectionMemoryManager::AllocationPurpose::Code, Size,
387330f729Sjoerg                          Alignment);
397330f729Sjoerg }
407330f729Sjoerg 
allocateSection(SectionMemoryManager::AllocationPurpose Purpose,uintptr_t Size,unsigned Alignment)417330f729Sjoerg uint8_t *SectionMemoryManager::allocateSection(
427330f729Sjoerg     SectionMemoryManager::AllocationPurpose Purpose, uintptr_t Size,
437330f729Sjoerg     unsigned Alignment) {
447330f729Sjoerg   if (!Alignment)
457330f729Sjoerg     Alignment = 16;
467330f729Sjoerg 
477330f729Sjoerg   assert(!(Alignment & (Alignment - 1)) && "Alignment must be a power of two.");
487330f729Sjoerg 
497330f729Sjoerg   uintptr_t RequiredSize = Alignment * ((Size + Alignment - 1) / Alignment + 1);
507330f729Sjoerg   uintptr_t Addr = 0;
517330f729Sjoerg 
527330f729Sjoerg   MemoryGroup &MemGroup = [&]() -> MemoryGroup & {
537330f729Sjoerg     switch (Purpose) {
547330f729Sjoerg     case AllocationPurpose::Code:
557330f729Sjoerg       return CodeMem;
567330f729Sjoerg     case AllocationPurpose::ROData:
577330f729Sjoerg       return RODataMem;
587330f729Sjoerg     case AllocationPurpose::RWData:
597330f729Sjoerg       return RWDataMem;
607330f729Sjoerg     }
617330f729Sjoerg     llvm_unreachable("Unknown SectionMemoryManager::AllocationPurpose");
627330f729Sjoerg   }();
637330f729Sjoerg 
647330f729Sjoerg   // Look in the list of free memory regions and use a block there if one
657330f729Sjoerg   // is available.
667330f729Sjoerg   for (FreeMemBlock &FreeMB : MemGroup.FreeMem) {
677330f729Sjoerg     if (FreeMB.Free.allocatedSize() >= RequiredSize) {
687330f729Sjoerg       Addr = (uintptr_t)FreeMB.Free.base();
697330f729Sjoerg       uintptr_t EndOfBlock = Addr + FreeMB.Free.allocatedSize();
707330f729Sjoerg       // Align the address.
717330f729Sjoerg       Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);
727330f729Sjoerg 
737330f729Sjoerg       if (FreeMB.PendingPrefixIndex == (unsigned)-1) {
747330f729Sjoerg         // The part of the block we're giving out to the user is now pending
757330f729Sjoerg         MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size));
767330f729Sjoerg 
777330f729Sjoerg         // Remember this pending block, such that future allocations can just
787330f729Sjoerg         // modify it rather than creating a new one
797330f729Sjoerg         FreeMB.PendingPrefixIndex = MemGroup.PendingMem.size() - 1;
807330f729Sjoerg       } else {
817330f729Sjoerg         sys::MemoryBlock &PendingMB =
827330f729Sjoerg             MemGroup.PendingMem[FreeMB.PendingPrefixIndex];
837330f729Sjoerg         PendingMB = sys::MemoryBlock(PendingMB.base(),
847330f729Sjoerg                                      Addr + Size - (uintptr_t)PendingMB.base());
857330f729Sjoerg       }
867330f729Sjoerg 
877330f729Sjoerg       // Remember how much free space is now left in this block
887330f729Sjoerg       FreeMB.Free =
897330f729Sjoerg           sys::MemoryBlock((void *)(Addr + Size), EndOfBlock - Addr - Size);
907330f729Sjoerg       return (uint8_t *)Addr;
917330f729Sjoerg     }
927330f729Sjoerg   }
937330f729Sjoerg 
947330f729Sjoerg   // No pre-allocated free block was large enough. Allocate a new memory region.
957330f729Sjoerg   // Note that all sections get allocated as read-write.  The permissions will
967330f729Sjoerg   // be updated later based on memory group.
977330f729Sjoerg   //
987330f729Sjoerg   // FIXME: It would be useful to define a default allocation size (or add
997330f729Sjoerg   // it as a constructor parameter) to minimize the number of allocations.
1007330f729Sjoerg   //
1017330f729Sjoerg   // FIXME: Initialize the Near member for each memory group to avoid
1027330f729Sjoerg   // interleaving.
1037330f729Sjoerg   std::error_code ec;
1047330f729Sjoerg   sys::MemoryBlock MB = MMapper.allocateMappedMemory(
1057330f729Sjoerg       Purpose, RequiredSize, &MemGroup.Near,
1067330f729Sjoerg       sys::Memory::MF_READ | sys::Memory::MF_WRITE, ec);
1077330f729Sjoerg   if (ec) {
1087330f729Sjoerg     // FIXME: Add error propagation to the interface.
1097330f729Sjoerg     return nullptr;
1107330f729Sjoerg   }
1117330f729Sjoerg 
1127330f729Sjoerg   // Save this address as the basis for our next request
1137330f729Sjoerg   MemGroup.Near = MB;
1147330f729Sjoerg 
115*82d56013Sjoerg   // Copy the address to all the other groups, if they have not
116*82d56013Sjoerg   // been initialized.
117*82d56013Sjoerg   if (CodeMem.Near.base() == 0)
118*82d56013Sjoerg     CodeMem.Near = MB;
119*82d56013Sjoerg   if (RODataMem.Near.base() == 0)
120*82d56013Sjoerg     RODataMem.Near = MB;
121*82d56013Sjoerg   if (RWDataMem.Near.base() == 0)
122*82d56013Sjoerg     RWDataMem.Near = MB;
123*82d56013Sjoerg 
1247330f729Sjoerg   // Remember that we allocated this memory
1257330f729Sjoerg   MemGroup.AllocatedMem.push_back(MB);
1267330f729Sjoerg   Addr = (uintptr_t)MB.base();
1277330f729Sjoerg   uintptr_t EndOfBlock = Addr + MB.allocatedSize();
1287330f729Sjoerg 
1297330f729Sjoerg   // Align the address.
1307330f729Sjoerg   Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);
1317330f729Sjoerg 
1327330f729Sjoerg   // The part of the block we're giving out to the user is now pending
1337330f729Sjoerg   MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size));
1347330f729Sjoerg 
1357330f729Sjoerg   // The allocateMappedMemory may allocate much more memory than we need. In
1367330f729Sjoerg   // this case, we store the unused memory as a free memory block.
1377330f729Sjoerg   unsigned FreeSize = EndOfBlock - Addr - Size;
1387330f729Sjoerg   if (FreeSize > 16) {
1397330f729Sjoerg     FreeMemBlock FreeMB;
1407330f729Sjoerg     FreeMB.Free = sys::MemoryBlock((void *)(Addr + Size), FreeSize);
1417330f729Sjoerg     FreeMB.PendingPrefixIndex = (unsigned)-1;
1427330f729Sjoerg     MemGroup.FreeMem.push_back(FreeMB);
1437330f729Sjoerg   }
1447330f729Sjoerg 
1457330f729Sjoerg   // Return aligned address
1467330f729Sjoerg   return (uint8_t *)Addr;
1477330f729Sjoerg }
1487330f729Sjoerg 
finalizeMemory(std::string * ErrMsg)1497330f729Sjoerg bool SectionMemoryManager::finalizeMemory(std::string *ErrMsg) {
1507330f729Sjoerg   // FIXME: Should in-progress permissions be reverted if an error occurs?
1517330f729Sjoerg   std::error_code ec;
1527330f729Sjoerg 
1537330f729Sjoerg   // Make code memory executable.
1547330f729Sjoerg   ec = applyMemoryGroupPermissions(CodeMem,
1557330f729Sjoerg                                    sys::Memory::MF_READ | sys::Memory::MF_EXEC);
1567330f729Sjoerg   if (ec) {
1577330f729Sjoerg     if (ErrMsg) {
1587330f729Sjoerg       *ErrMsg = ec.message();
1597330f729Sjoerg     }
1607330f729Sjoerg     return true;
1617330f729Sjoerg   }
1627330f729Sjoerg 
1637330f729Sjoerg   // Make read-only data memory read-only.
164*82d56013Sjoerg   ec = applyMemoryGroupPermissions(RODataMem, sys::Memory::MF_READ);
1657330f729Sjoerg   if (ec) {
1667330f729Sjoerg     if (ErrMsg) {
1677330f729Sjoerg       *ErrMsg = ec.message();
1687330f729Sjoerg     }
1697330f729Sjoerg     return true;
1707330f729Sjoerg   }
1717330f729Sjoerg 
1727330f729Sjoerg   // Read-write data memory already has the correct permissions
1737330f729Sjoerg 
1747330f729Sjoerg   // Some platforms with separate data cache and instruction cache require
1757330f729Sjoerg   // explicit cache flush, otherwise JIT code manipulations (like resolved
1767330f729Sjoerg   // relocations) will get to the data cache but not to the instruction cache.
1777330f729Sjoerg   invalidateInstructionCache();
1787330f729Sjoerg 
1797330f729Sjoerg   return false;
1807330f729Sjoerg }
1817330f729Sjoerg 
trimBlockToPageSize(sys::MemoryBlock M)1827330f729Sjoerg static sys::MemoryBlock trimBlockToPageSize(sys::MemoryBlock M) {
1837330f729Sjoerg   static const size_t PageSize = sys::Process::getPageSizeEstimate();
1847330f729Sjoerg 
1857330f729Sjoerg   size_t StartOverlap =
1867330f729Sjoerg       (PageSize - ((uintptr_t)M.base() % PageSize)) % PageSize;
1877330f729Sjoerg 
1887330f729Sjoerg   size_t TrimmedSize = M.allocatedSize();
1897330f729Sjoerg   TrimmedSize -= StartOverlap;
1907330f729Sjoerg   TrimmedSize -= TrimmedSize % PageSize;
1917330f729Sjoerg 
1927330f729Sjoerg   sys::MemoryBlock Trimmed((void *)((uintptr_t)M.base() + StartOverlap),
1937330f729Sjoerg                            TrimmedSize);
1947330f729Sjoerg 
1957330f729Sjoerg   assert(((uintptr_t)Trimmed.base() % PageSize) == 0);
1967330f729Sjoerg   assert((Trimmed.allocatedSize() % PageSize) == 0);
1977330f729Sjoerg   assert(M.base() <= Trimmed.base() &&
1987330f729Sjoerg          Trimmed.allocatedSize() <= M.allocatedSize());
1997330f729Sjoerg 
2007330f729Sjoerg   return Trimmed;
2017330f729Sjoerg }
2027330f729Sjoerg 
2037330f729Sjoerg std::error_code
applyMemoryGroupPermissions(MemoryGroup & MemGroup,unsigned Permissions)2047330f729Sjoerg SectionMemoryManager::applyMemoryGroupPermissions(MemoryGroup &MemGroup,
2057330f729Sjoerg                                                   unsigned Permissions) {
2067330f729Sjoerg   for (sys::MemoryBlock &MB : MemGroup.PendingMem)
2077330f729Sjoerg     if (std::error_code EC = MMapper.protectMappedMemory(MB, Permissions))
2087330f729Sjoerg       return EC;
2097330f729Sjoerg 
2107330f729Sjoerg   MemGroup.PendingMem.clear();
2117330f729Sjoerg 
2127330f729Sjoerg   // Now go through free blocks and trim any of them that don't span the entire
2137330f729Sjoerg   // page because one of the pending blocks may have overlapped it.
2147330f729Sjoerg   for (FreeMemBlock &FreeMB : MemGroup.FreeMem) {
2157330f729Sjoerg     FreeMB.Free = trimBlockToPageSize(FreeMB.Free);
2167330f729Sjoerg     // We cleared the PendingMem list, so all these pointers are now invalid
2177330f729Sjoerg     FreeMB.PendingPrefixIndex = (unsigned)-1;
2187330f729Sjoerg   }
2197330f729Sjoerg 
2207330f729Sjoerg   // Remove all blocks which are now empty
221*82d56013Sjoerg   erase_if(MemGroup.FreeMem, [](FreeMemBlock &FreeMB) {
2227330f729Sjoerg     return FreeMB.Free.allocatedSize() == 0;
223*82d56013Sjoerg   });
2247330f729Sjoerg 
2257330f729Sjoerg   return std::error_code();
2267330f729Sjoerg }
2277330f729Sjoerg 
invalidateInstructionCache()2287330f729Sjoerg void SectionMemoryManager::invalidateInstructionCache() {
2297330f729Sjoerg   for (sys::MemoryBlock &Block : CodeMem.PendingMem)
2307330f729Sjoerg     sys::Memory::InvalidateInstructionCache(Block.base(),
2317330f729Sjoerg                                             Block.allocatedSize());
2327330f729Sjoerg }
2337330f729Sjoerg 
~SectionMemoryManager()2347330f729Sjoerg SectionMemoryManager::~SectionMemoryManager() {
2357330f729Sjoerg   for (MemoryGroup *Group : {&CodeMem, &RWDataMem, &RODataMem}) {
2367330f729Sjoerg     for (sys::MemoryBlock &Block : Group->AllocatedMem)
2377330f729Sjoerg       MMapper.releaseMappedMemory(Block);
2387330f729Sjoerg   }
2397330f729Sjoerg }
2407330f729Sjoerg 
~MemoryMapper()2417330f729Sjoerg SectionMemoryManager::MemoryMapper::~MemoryMapper() {}
2427330f729Sjoerg 
anchor()2437330f729Sjoerg void SectionMemoryManager::anchor() {}
2447330f729Sjoerg 
2457330f729Sjoerg namespace {
2467330f729Sjoerg // Trivial implementation of SectionMemoryManager::MemoryMapper that just calls
2477330f729Sjoerg // into sys::Memory.
2487330f729Sjoerg class DefaultMMapper final : public SectionMemoryManager::MemoryMapper {
2497330f729Sjoerg public:
2507330f729Sjoerg   sys::MemoryBlock
allocateMappedMemory(SectionMemoryManager::AllocationPurpose Purpose,size_t NumBytes,const sys::MemoryBlock * const NearBlock,unsigned Flags,std::error_code & EC)2517330f729Sjoerg   allocateMappedMemory(SectionMemoryManager::AllocationPurpose Purpose,
2527330f729Sjoerg                        size_t NumBytes, const sys::MemoryBlock *const NearBlock,
2537330f729Sjoerg                        unsigned Flags, std::error_code &EC) override {
2547330f729Sjoerg     return sys::Memory::allocateMappedMemory(NumBytes, NearBlock, Flags, EC);
2557330f729Sjoerg   }
2567330f729Sjoerg 
protectMappedMemory(const sys::MemoryBlock & Block,unsigned Flags)2577330f729Sjoerg   std::error_code protectMappedMemory(const sys::MemoryBlock &Block,
2587330f729Sjoerg                                       unsigned Flags) override {
2597330f729Sjoerg     return sys::Memory::protectMappedMemory(Block, Flags);
2607330f729Sjoerg   }
2617330f729Sjoerg 
releaseMappedMemory(sys::MemoryBlock & M)2627330f729Sjoerg   std::error_code releaseMappedMemory(sys::MemoryBlock &M) override {
2637330f729Sjoerg     return sys::Memory::releaseMappedMemory(M);
2647330f729Sjoerg   }
2657330f729Sjoerg };
2667330f729Sjoerg 
2677330f729Sjoerg DefaultMMapper DefaultMMapperInstance;
2687330f729Sjoerg } // namespace
2697330f729Sjoerg 
SectionMemoryManager(MemoryMapper * MM)2707330f729Sjoerg SectionMemoryManager::SectionMemoryManager(MemoryMapper *MM)
2717330f729Sjoerg     : MMapper(MM ? *MM : DefaultMMapperInstance) {}
2727330f729Sjoerg 
2737330f729Sjoerg } // namespace llvm
274