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