JITLinkMemoryManager.cpp revision 360784
1//===--- JITLinkMemoryManager.cpp - JITLinkMemoryManager implementation ---===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
11#include "llvm/Support/Process.h"
12
13namespace llvm {
14namespace jitlink {
15
16JITLinkMemoryManager::~JITLinkMemoryManager() = default;
17JITLinkMemoryManager::Allocation::~Allocation() = default;
18
19Expected<std::unique_ptr<JITLinkMemoryManager::Allocation>>
20InProcessMemoryManager::allocate(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 reinterpret_cast<JITTargetAddress>(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
116    sys::MemoryBlock SegMem(SlabRemaining.base(), SegmentSize);
117    SlabRemaining = sys::MemoryBlock((char *)SlabRemaining.base() + SegmentSize,
118                                     SegmentSize);
119
120    // Zero out the zero-fill memory.
121    memset(static_cast<char *>(SegMem.base()) + Seg.getContentSize(), 0,
122           Seg.getZeroFillSize());
123
124    // Record the block for this segment.
125    Blocks[KV.first] = std::move(SegMem);
126  }
127  return std::unique_ptr<InProcessMemoryManager::Allocation>(
128      new IPMMAlloc(std::move(Blocks)));
129}
130
131} // end namespace jitlink
132} // end namespace llvm
133