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