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 
12 #include <limits>
13 
14 namespace llvm {
15 namespace orc {
16 
17 class EPCGenericJITLinkMemoryManager::Alloc
18     : public jitlink::JITLinkMemoryManager::Allocation {
19 public:
20   struct SegInfo {
21     char *WorkingMem = nullptr;
22     ExecutorAddress TargetAddr;
23     uint64_t ContentSize = 0;
24     uint64_t ZeroFillSize = 0;
25   };
26   using SegInfoMap = DenseMap<unsigned, SegInfo>;
27 
28   Alloc(EPCGenericJITLinkMemoryManager &Parent, ExecutorAddress TargetAddr,
29         uint64_t TargetSize, std::unique_ptr<char[]> WorkingBuffer,
30         SegInfoMap Segs)
31       : Parent(Parent), TargetAddr(TargetAddr), TargetSize(TargetSize),
32         WorkingBuffer(std::move(WorkingBuffer)), Segs(std::move(Segs)) {}
33 
34   MutableArrayRef<char> getWorkingMemory(ProtectionFlags Seg) override {
35     auto I = Segs.find(Seg);
36     assert(I != Segs.end() && "No allocation for seg");
37     assert(I->second.ContentSize <= std::numeric_limits<size_t>::max());
38     return {I->second.WorkingMem, static_cast<size_t>(I->second.ContentSize)};
39   }
40 
41   JITTargetAddress getTargetMemory(ProtectionFlags Seg) override {
42     auto I = Segs.find(Seg);
43     assert(I != Segs.end() && "No allocation for seg");
44     return I->second.TargetAddr.getValue();
45   }
46 
47   void finalizeAsync(FinalizeContinuation OnFinalize) override {
48     char *WorkingMem = WorkingBuffer.get();
49     tpctypes::FinalizeRequest FR;
50     for (auto &KV : Segs) {
51       assert(KV.second.ContentSize <= std::numeric_limits<size_t>::max());
52       FR.push_back(tpctypes::SegFinalizeRequest{
53           tpctypes::toWireProtectionFlags(
54               static_cast<sys::Memory::ProtectionFlags>(KV.first)),
55           KV.second.TargetAddr,
56           alignTo(KV.second.ContentSize + KV.second.ZeroFillSize,
57                   Parent.EPC.getPageSize()),
58           {WorkingMem, static_cast<size_t>(KV.second.ContentSize)}});
59       WorkingMem += KV.second.ContentSize;
60     }
61     Parent.EPC.callSPSWrapperAsync<shared::SPSOrcTargetProcessFinalize>(
62         [OnFinalize = std::move(OnFinalize)](Error SerializationErr,
63                                              Error FinalizeErr) {
64           if (SerializationErr)
65             OnFinalize(std::move(SerializationErr));
66           else
67             OnFinalize(std::move(FinalizeErr));
68         },
69         Parent.FAs.Finalize.getValue(), std::move(FR));
70   }
71 
72   Error deallocate() override {
73     Error Err = Error::success();
74     if (auto E2 =
75             Parent.EPC.callSPSWrapper<shared::SPSOrcTargetProcessDeallocate>(
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 /// Create from a ExecutorProcessControl instance.
90 Expected<std::unique_ptr<EPCGenericJITLinkMemoryManager>>
91 EPCGenericJITLinkMemoryManager::CreateUsingOrcRTFuncs(ExecutionSession &ES,
92                                                       JITDylib &OrcRuntimeJD) {
93 
94   StringRef GlobalPrefix = "";
95   if (ES.getExecutorProcessControl().getTargetTriple().isOSBinFormatMachO())
96     GlobalPrefix = "_";
97 
98   FuncAddrs FAs;
99   if (auto Err = lookupAndRecordAddrs(
100           ES, LookupKind::Static, makeJITDylibSearchOrder(&OrcRuntimeJD),
101           {{ES.intern((GlobalPrefix + "__orc_rt_reserve").str()), &FAs.Reserve},
102            {ES.intern((GlobalPrefix + "__orc_rt_finalize").str()),
103             &FAs.Finalize},
104            {ES.intern((GlobalPrefix + "__orc_rt_deallocate").str()),
105             &FAs.Deallocate}}))
106     return std::move(Err);
107 
108   return std::make_unique<EPCGenericJITLinkMemoryManager>(
109       ES.getExecutorProcessControl(), FAs);
110 }
111 
112 Expected<std::unique_ptr<jitlink::JITLinkMemoryManager::Allocation>>
113 EPCGenericJITLinkMemoryManager::allocate(const jitlink::JITLinkDylib *JD,
114                                          const SegmentsRequestMap &Request) {
115   Alloc::SegInfoMap Segs;
116   uint64_t AllocSize = 0;
117   size_t WorkingSize = 0;
118   for (auto &KV : Request) {
119     if (!isPowerOf2_64(KV.second.getAlignment()))
120       return make_error<StringError>("Alignment is not a power of two",
121                                      inconvertibleErrorCode());
122     if (KV.second.getAlignment() > EPC.getPageSize())
123       return make_error<StringError>("Alignment exceeds page size",
124                                      inconvertibleErrorCode());
125 
126     auto &Seg = Segs[KV.first];
127     Seg.ContentSize = KV.second.getContentSize();
128     Seg.ZeroFillSize = KV.second.getZeroFillSize();
129     AllocSize += alignTo(Seg.ContentSize + Seg.ZeroFillSize, EPC.getPageSize());
130     WorkingSize += Seg.ContentSize;
131   }
132 
133   std::unique_ptr<char[]> WorkingBuffer;
134   if (WorkingSize > 0)
135     WorkingBuffer = std::make_unique<char[]>(WorkingSize);
136   Expected<ExecutorAddress> TargetAllocAddr((ExecutorAddress()));
137   if (auto Err = EPC.callSPSWrapper<shared::SPSOrcTargetProcessAllocate>(
138           FAs.Reserve.getValue(), TargetAllocAddr, AllocSize))
139     return std::move(Err);
140   if (!TargetAllocAddr)
141     return TargetAllocAddr.takeError();
142 
143   char *WorkingMem = WorkingBuffer.get();
144   JITTargetAddress SegAddr = TargetAllocAddr->getValue();
145   for (auto &KV : Segs) {
146     auto &Seg = KV.second;
147     Seg.TargetAddr.setValue(SegAddr);
148     SegAddr += alignTo(Seg.ContentSize + Seg.ZeroFillSize, EPC.getPageSize());
149     Seg.WorkingMem = WorkingMem;
150     WorkingMem += Seg.ContentSize;
151   }
152 
153   return std::make_unique<Alloc>(*this, *TargetAllocAddr, AllocSize,
154                                  std::move(WorkingBuffer), std::move(Segs));
155 }
156 
157 } // end namespace orc
158 } // end namespace llvm
159