xref: /llvm-project/llvm/lib/ExecutionEngine/JITLink/JITLinkMemoryManager.cpp (revision c874dd53628db8170d4c5ba3878817abc385a695)
1 //===--- JITLinkMemoryManager.cpp - JITLinkMemoryManager implementation ---===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
10 #include "llvm/Support/Process.h"
11 
12 namespace llvm {
13 namespace jitlink {
14 
15 JITLinkMemoryManager::~JITLinkMemoryManager() = default;
16 JITLinkMemoryManager::Allocation::~Allocation() = default;
17 
18 Expected<std::unique_ptr<JITLinkMemoryManager::Allocation>>
19 InProcessMemoryManager::allocate(const JITLinkDylib *JD,
20                                  const SegmentsRequestMap &Request) {
21 
22   using AllocationMap = DenseMap<unsigned, sys::MemoryBlock>;
23 
24   // Local class for allocation.
25   class IPMMAlloc : public Allocation {
26   public:
27     IPMMAlloc(AllocationMap SegBlocks) : SegBlocks(std::move(SegBlocks)) {}
28     MutableArrayRef<char> getWorkingMemory(ProtectionFlags Seg) override {
29       assert(SegBlocks.count(Seg) && "No allocation for segment");
30       return {static_cast<char *>(SegBlocks[Seg].base()),
31               SegBlocks[Seg].allocatedSize()};
32     }
33     JITTargetAddress getTargetMemory(ProtectionFlags Seg) override {
34       assert(SegBlocks.count(Seg) && "No allocation for segment");
35       return pointerToJITTargetAddress(SegBlocks[Seg].base());
36     }
37     void finalizeAsync(FinalizeContinuation OnFinalize) override {
38       OnFinalize(applyProtections());
39     }
40     Error deallocate() override {
41       if (SegBlocks.empty())
42         return Error::success();
43       void *SlabStart = SegBlocks.begin()->second.base();
44       char *SlabEnd = (char *)SlabStart;
45       for (auto &KV : SegBlocks) {
46         SlabStart = std::min(SlabStart, KV.second.base());
47         SlabEnd = std::max(SlabEnd, (char *)(KV.second.base()) +
48                                         KV.second.allocatedSize());
49       }
50       size_t SlabSize = SlabEnd - (char *)SlabStart;
51       assert((SlabSize % sys::Process::getPageSizeEstimate()) == 0 &&
52              "Slab size is not a multiple of page size");
53       sys::MemoryBlock Slab(SlabStart, SlabSize);
54       if (auto EC = sys::Memory::releaseMappedMemory(Slab))
55         return errorCodeToError(EC);
56       return Error::success();
57     }
58 
59   private:
60     Error applyProtections() {
61       for (auto &KV : SegBlocks) {
62         auto &Prot = KV.first;
63         auto &Block = KV.second;
64         if (auto EC = sys::Memory::protectMappedMemory(Block, Prot))
65           return errorCodeToError(EC);
66         if (Prot & sys::Memory::MF_EXEC)
67           sys::Memory::InvalidateInstructionCache(Block.base(),
68                                                   Block.allocatedSize());
69       }
70       return Error::success();
71     }
72 
73     AllocationMap SegBlocks;
74   };
75 
76   if (!isPowerOf2_64((uint64_t)sys::Process::getPageSizeEstimate()))
77     return make_error<StringError>("Page size is not a power of 2",
78                                    inconvertibleErrorCode());
79 
80   AllocationMap Blocks;
81   const sys::Memory::ProtectionFlags ReadWrite =
82       static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
83                                                 sys::Memory::MF_WRITE);
84 
85   // Compute the total number of pages to allocate.
86   size_t TotalSize = 0;
87   for (auto &KV : Request) {
88     const auto &Seg = KV.second;
89 
90     if (Seg.getAlignment() > sys::Process::getPageSizeEstimate())
91       return make_error<StringError>("Cannot request higher than page "
92                                      "alignment",
93                                      inconvertibleErrorCode());
94 
95     TotalSize = alignTo(TotalSize, sys::Process::getPageSizeEstimate());
96     TotalSize += Seg.getContentSize();
97     TotalSize += Seg.getZeroFillSize();
98   }
99 
100   // Allocate one slab to cover all the segments.
101   std::error_code EC;
102   auto SlabRemaining =
103       sys::Memory::allocateMappedMemory(TotalSize, nullptr, ReadWrite, EC);
104 
105   if (EC)
106     return errorCodeToError(EC);
107 
108   // Allocate segment memory from the slab.
109   for (auto &KV : Request) {
110 
111     const auto &Seg = KV.second;
112 
113     uint64_t SegmentSize = alignTo(Seg.getContentSize() + Seg.getZeroFillSize(),
114                                    sys::Process::getPageSizeEstimate());
115     assert(SlabRemaining.allocatedSize() >= SegmentSize &&
116            "Mapping exceeds allocation");
117 
118     sys::MemoryBlock SegMem(SlabRemaining.base(), SegmentSize);
119     SlabRemaining = sys::MemoryBlock((char *)SlabRemaining.base() + SegmentSize,
120                                      SlabRemaining.allocatedSize() - SegmentSize);
121 
122     // Zero out the zero-fill memory.
123     memset(static_cast<char *>(SegMem.base()) + Seg.getContentSize(), 0,
124            Seg.getZeroFillSize());
125 
126     // Record the block for this segment.
127     Blocks[KV.first] = std::move(SegMem);
128   }
129 
130   return std::unique_ptr<InProcessMemoryManager::Allocation>(
131       new IPMMAlloc(std::move(Blocks)));
132 }
133 
134 } // end namespace jitlink
135 } // end namespace llvm
136