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