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