1 //===---- EPCGenericJITLinkMemoryManager.cpp -- Mem management via EPC ----===//
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/Orc/EPCGenericJITLinkMemoryManager.h"
10 #include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"
11 #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
12 
13 #include <limits>
14 
15 namespace llvm {
16 namespace orc {
17 
18 class EPCGenericJITLinkMemoryManager::Alloc
19     : public jitlink::JITLinkMemoryManager::Allocation {
20 public:
21   struct SegInfo {
22     char *WorkingMem = nullptr;
23     ExecutorAddress TargetAddr;
24     uint64_t ContentSize = 0;
25     uint64_t ZeroFillSize = 0;
26   };
27   using SegInfoMap = DenseMap<unsigned, SegInfo>;
28 
29   Alloc(EPCGenericJITLinkMemoryManager &Parent, ExecutorAddress TargetAddr,
30         uint64_t TargetSize, std::unique_ptr<char[]> WorkingBuffer,
31         SegInfoMap Segs)
32       : Parent(Parent), TargetAddr(TargetAddr), TargetSize(TargetSize),
33         WorkingBuffer(std::move(WorkingBuffer)), Segs(std::move(Segs)) {}
34 
35   MutableArrayRef<char> getWorkingMemory(ProtectionFlags Seg) override {
36     auto I = Segs.find(Seg);
37     assert(I != Segs.end() && "No allocation for seg");
38     assert(I->second.ContentSize <= std::numeric_limits<size_t>::max());
39     return {I->second.WorkingMem, static_cast<size_t>(I->second.ContentSize)};
40   }
41 
42   JITTargetAddress getTargetMemory(ProtectionFlags Seg) override {
43     auto I = Segs.find(Seg);
44     assert(I != Segs.end() && "No allocation for seg");
45     return I->second.TargetAddr.getValue();
46   }
47 
48   void finalizeAsync(FinalizeContinuation OnFinalize) override {
49     char *WorkingMem = WorkingBuffer.get();
50     tpctypes::FinalizeRequest FR;
51     for (auto &KV : Segs) {
52       assert(KV.second.ContentSize <= std::numeric_limits<size_t>::max());
53       FR.push_back(tpctypes::SegFinalizeRequest{
54           tpctypes::toWireProtectionFlags(
55               static_cast<sys::Memory::ProtectionFlags>(KV.first)),
56           KV.second.TargetAddr,
57           alignTo(KV.second.ContentSize + KV.second.ZeroFillSize,
58                   Parent.EPC.getPageSize()),
59           {WorkingMem, static_cast<size_t>(KV.second.ContentSize)}});
60       WorkingMem += KV.second.ContentSize;
61     }
62     Parent.EPC.callSPSWrapperAsync<rt::SPSMemoryFinalizeSignature>(
63         [OnFinalize = std::move(OnFinalize)](Error SerializationErr,
64                                              Error FinalizeErr) {
65           if (SerializationErr)
66             OnFinalize(std::move(SerializationErr));
67           else
68             OnFinalize(std::move(FinalizeErr));
69         },
70         Parent.FAs.Finalize.getValue(), std::move(FR));
71   }
72 
73   Error deallocate() override {
74     Error Err = Error::success();
75     if (auto E2 = Parent.EPC.callSPSWrapper<rt::SPSMemoryDeallocateSignature>(
76             Parent.FAs.Deallocate.getValue(), Err, TargetAddr, TargetSize))
77       return E2;
78     return Err;
79   }
80 
81 private:
82   EPCGenericJITLinkMemoryManager &Parent;
83   ExecutorAddress TargetAddr;
84   uint64_t TargetSize;
85   std::unique_ptr<char[]> WorkingBuffer;
86   SegInfoMap Segs;
87 };
88 
89 Expected<std::unique_ptr<jitlink::JITLinkMemoryManager::Allocation>>
90 EPCGenericJITLinkMemoryManager::allocate(const jitlink::JITLinkDylib *JD,
91                                          const SegmentsRequestMap &Request) {
92   Alloc::SegInfoMap Segs;
93   uint64_t AllocSize = 0;
94   size_t WorkingSize = 0;
95   for (auto &KV : Request) {
96     if (!isPowerOf2_64(KV.second.getAlignment()))
97       return make_error<StringError>("Alignment is not a power of two",
98                                      inconvertibleErrorCode());
99     if (KV.second.getAlignment() > EPC.getPageSize())
100       return make_error<StringError>("Alignment exceeds page size",
101                                      inconvertibleErrorCode());
102 
103     auto &Seg = Segs[KV.first];
104     Seg.ContentSize = KV.second.getContentSize();
105     Seg.ZeroFillSize = KV.second.getZeroFillSize();
106     AllocSize += alignTo(Seg.ContentSize + Seg.ZeroFillSize, EPC.getPageSize());
107     WorkingSize += Seg.ContentSize;
108   }
109 
110   std::unique_ptr<char[]> WorkingBuffer;
111   if (WorkingSize > 0)
112     WorkingBuffer = std::make_unique<char[]>(WorkingSize);
113   Expected<ExecutorAddress> TargetAllocAddr((ExecutorAddress()));
114   if (auto Err = EPC.callSPSWrapper<rt::SPSMemoryReserveSignature>(
115           FAs.Reserve.getValue(), TargetAllocAddr, AllocSize))
116     return std::move(Err);
117   if (!TargetAllocAddr)
118     return TargetAllocAddr.takeError();
119 
120   char *WorkingMem = WorkingBuffer.get();
121   JITTargetAddress SegAddr = TargetAllocAddr->getValue();
122   for (auto &KV : Segs) {
123     auto &Seg = KV.second;
124     Seg.TargetAddr.setValue(SegAddr);
125     SegAddr += alignTo(Seg.ContentSize + Seg.ZeroFillSize, EPC.getPageSize());
126     Seg.WorkingMem = WorkingMem;
127     WorkingMem += Seg.ContentSize;
128   }
129 
130   return std::make_unique<Alloc>(*this, *TargetAllocAddr, AllocSize,
131                                  std::move(WorkingBuffer), std::move(Segs));
132 }
133 
134 } // end namespace orc
135 } // end namespace llvm
136