aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/ExecutionEngine/Orc/EPCIndirectionUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/ExecutionEngine/Orc/EPCIndirectionUtils.cpp')
-rw-r--r--llvm/lib/ExecutionEngine/Orc/EPCIndirectionUtils.cpp423
1 files changed, 423 insertions, 0 deletions
diff --git a/llvm/lib/ExecutionEngine/Orc/EPCIndirectionUtils.cpp b/llvm/lib/ExecutionEngine/Orc/EPCIndirectionUtils.cpp
new file mode 100644
index 000000000000..b9c70b0aeb3c
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/EPCIndirectionUtils.cpp
@@ -0,0 +1,423 @@
+//===------- EPCIndirectionUtils.cpp -- EPC based indirection APIs --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h"
+
+#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
+#include "llvm/Support/MathExtras.h"
+
+#include <future>
+
+using namespace llvm;
+using namespace llvm::orc;
+
+namespace llvm {
+namespace orc {
+
+class EPCIndirectionUtilsAccess {
+public:
+ using IndirectStubInfo = EPCIndirectionUtils::IndirectStubInfo;
+ using IndirectStubInfoVector = EPCIndirectionUtils::IndirectStubInfoVector;
+
+ static Expected<IndirectStubInfoVector>
+ getIndirectStubs(EPCIndirectionUtils &EPCIU, unsigned NumStubs) {
+ return EPCIU.getIndirectStubs(NumStubs);
+ };
+};
+
+} // end namespace orc
+} // end namespace llvm
+
+namespace {
+
+class EPCTrampolinePool : public TrampolinePool {
+public:
+ EPCTrampolinePool(EPCIndirectionUtils &EPCIU);
+ Error deallocatePool();
+
+protected:
+ Error grow() override;
+
+ using Allocation = jitlink::JITLinkMemoryManager::Allocation;
+
+ EPCIndirectionUtils &EPCIU;
+ unsigned TrampolineSize = 0;
+ unsigned TrampolinesPerPage = 0;
+ std::vector<std::unique_ptr<Allocation>> TrampolineBlocks;
+};
+
+class EPCIndirectStubsManager : public IndirectStubsManager,
+ private EPCIndirectionUtilsAccess {
+public:
+ EPCIndirectStubsManager(EPCIndirectionUtils &EPCIU) : EPCIU(EPCIU) {}
+
+ Error deallocateStubs();
+
+ Error createStub(StringRef StubName, JITTargetAddress StubAddr,
+ JITSymbolFlags StubFlags) override;
+
+ Error createStubs(const StubInitsMap &StubInits) override;
+
+ JITEvaluatedSymbol findStub(StringRef Name, bool ExportedStubsOnly) override;
+
+ JITEvaluatedSymbol findPointer(StringRef Name) override;
+
+ Error updatePointer(StringRef Name, JITTargetAddress NewAddr) override;
+
+private:
+ using StubInfo = std::pair<IndirectStubInfo, JITSymbolFlags>;
+
+ std::mutex ISMMutex;
+ EPCIndirectionUtils &EPCIU;
+ StringMap<StubInfo> StubInfos;
+};
+
+EPCTrampolinePool::EPCTrampolinePool(EPCIndirectionUtils &EPCIU)
+ : EPCIU(EPCIU) {
+ auto &EPC = EPCIU.getExecutorProcessControl();
+ auto &ABI = EPCIU.getABISupport();
+
+ TrampolineSize = ABI.getTrampolineSize();
+ TrampolinesPerPage =
+ (EPC.getPageSize() - ABI.getPointerSize()) / TrampolineSize;
+}
+
+Error EPCTrampolinePool::deallocatePool() {
+ Error Err = Error::success();
+ for (auto &Alloc : TrampolineBlocks)
+ Err = joinErrors(std::move(Err), Alloc->deallocate());
+ return Err;
+}
+
+Error EPCTrampolinePool::grow() {
+ assert(AvailableTrampolines.empty() &&
+ "Grow called with trampolines still available");
+
+ auto ResolverAddress = EPCIU.getResolverBlockAddress();
+ assert(ResolverAddress && "Resolver address can not be null");
+
+ auto &EPC = EPCIU.getExecutorProcessControl();
+ constexpr auto TrampolinePagePermissions =
+ static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
+ sys::Memory::MF_EXEC);
+ auto PageSize = EPC.getPageSize();
+ jitlink::JITLinkMemoryManager::SegmentsRequestMap Request;
+ Request[TrampolinePagePermissions] = {PageSize, static_cast<size_t>(PageSize),
+ 0};
+ auto Alloc = EPC.getMemMgr().allocate(nullptr, Request);
+
+ if (!Alloc)
+ return Alloc.takeError();
+
+ unsigned NumTrampolines = TrampolinesPerPage;
+
+ auto WorkingMemory = (*Alloc)->getWorkingMemory(TrampolinePagePermissions);
+ auto TargetAddress = (*Alloc)->getTargetMemory(TrampolinePagePermissions);
+
+ EPCIU.getABISupport().writeTrampolines(WorkingMemory.data(), TargetAddress,
+ ResolverAddress, NumTrampolines);
+
+ auto TargetAddr = (*Alloc)->getTargetMemory(TrampolinePagePermissions);
+ for (unsigned I = 0; I < NumTrampolines; ++I)
+ AvailableTrampolines.push_back(TargetAddr + (I * TrampolineSize));
+
+ if (auto Err = (*Alloc)->finalize())
+ return Err;
+
+ TrampolineBlocks.push_back(std::move(*Alloc));
+
+ return Error::success();
+}
+
+Error EPCIndirectStubsManager::createStub(StringRef StubName,
+ JITTargetAddress StubAddr,
+ JITSymbolFlags StubFlags) {
+ StubInitsMap SIM;
+ SIM[StubName] = std::make_pair(StubAddr, StubFlags);
+ return createStubs(SIM);
+}
+
+Error EPCIndirectStubsManager::createStubs(const StubInitsMap &StubInits) {
+ auto AvailableStubInfos = getIndirectStubs(EPCIU, StubInits.size());
+ if (!AvailableStubInfos)
+ return AvailableStubInfos.takeError();
+
+ {
+ std::lock_guard<std::mutex> Lock(ISMMutex);
+ unsigned ASIdx = 0;
+ for (auto &SI : StubInits) {
+ auto &A = (*AvailableStubInfos)[ASIdx++];
+ StubInfos[SI.first()] = std::make_pair(A, SI.second.second);
+ }
+ }
+
+ auto &MemAccess = EPCIU.getExecutorProcessControl().getMemoryAccess();
+ switch (EPCIU.getABISupport().getPointerSize()) {
+ case 4: {
+ unsigned ASIdx = 0;
+ std::vector<tpctypes::UInt32Write> PtrUpdates;
+ for (auto &SI : StubInits)
+ PtrUpdates.push_back({(*AvailableStubInfos)[ASIdx++].PointerAddress,
+ static_cast<uint32_t>(SI.second.first)});
+ return MemAccess.writeUInt32s(PtrUpdates);
+ }
+ case 8: {
+ unsigned ASIdx = 0;
+ std::vector<tpctypes::UInt64Write> PtrUpdates;
+ for (auto &SI : StubInits)
+ PtrUpdates.push_back({(*AvailableStubInfos)[ASIdx++].PointerAddress,
+ static_cast<uint64_t>(SI.second.first)});
+ return MemAccess.writeUInt64s(PtrUpdates);
+ }
+ default:
+ return make_error<StringError>("Unsupported pointer size",
+ inconvertibleErrorCode());
+ }
+}
+
+JITEvaluatedSymbol EPCIndirectStubsManager::findStub(StringRef Name,
+ bool ExportedStubsOnly) {
+ std::lock_guard<std::mutex> Lock(ISMMutex);
+ auto I = StubInfos.find(Name);
+ if (I == StubInfos.end())
+ return nullptr;
+ return {I->second.first.StubAddress, I->second.second};
+}
+
+JITEvaluatedSymbol EPCIndirectStubsManager::findPointer(StringRef Name) {
+ std::lock_guard<std::mutex> Lock(ISMMutex);
+ auto I = StubInfos.find(Name);
+ if (I == StubInfos.end())
+ return nullptr;
+ return {I->second.first.PointerAddress, I->second.second};
+}
+
+Error EPCIndirectStubsManager::updatePointer(StringRef Name,
+ JITTargetAddress NewAddr) {
+
+ JITTargetAddress PtrAddr = 0;
+ {
+ std::lock_guard<std::mutex> Lock(ISMMutex);
+ auto I = StubInfos.find(Name);
+ if (I == StubInfos.end())
+ return make_error<StringError>("Unknown stub name",
+ inconvertibleErrorCode());
+ PtrAddr = I->second.first.PointerAddress;
+ }
+
+ auto &MemAccess = EPCIU.getExecutorProcessControl().getMemoryAccess();
+ switch (EPCIU.getABISupport().getPointerSize()) {
+ case 4: {
+ tpctypes::UInt32Write PUpdate(PtrAddr, NewAddr);
+ return MemAccess.writeUInt32s(PUpdate);
+ }
+ case 8: {
+ tpctypes::UInt64Write PUpdate(PtrAddr, NewAddr);
+ return MemAccess.writeUInt64s(PUpdate);
+ }
+ default:
+ return make_error<StringError>("Unsupported pointer size",
+ inconvertibleErrorCode());
+ }
+}
+
+} // end anonymous namespace.
+
+namespace llvm {
+namespace orc {
+
+EPCIndirectionUtils::ABISupport::~ABISupport() {}
+
+Expected<std::unique_ptr<EPCIndirectionUtils>>
+EPCIndirectionUtils::Create(ExecutorProcessControl &EPC) {
+ const auto &TT = EPC.getTargetTriple();
+ switch (TT.getArch()) {
+ default:
+ return make_error<StringError>(
+ std::string("No EPCIndirectionUtils available for ") + TT.str(),
+ inconvertibleErrorCode());
+ case Triple::aarch64:
+ case Triple::aarch64_32:
+ return CreateWithABI<OrcAArch64>(EPC);
+
+ case Triple::x86:
+ return CreateWithABI<OrcI386>(EPC);
+
+ case Triple::mips:
+ return CreateWithABI<OrcMips32Be>(EPC);
+
+ case Triple::mipsel:
+ return CreateWithABI<OrcMips32Le>(EPC);
+
+ case Triple::mips64:
+ case Triple::mips64el:
+ return CreateWithABI<OrcMips64>(EPC);
+
+ case Triple::x86_64:
+ if (TT.getOS() == Triple::OSType::Win32)
+ return CreateWithABI<OrcX86_64_Win32>(EPC);
+ else
+ return CreateWithABI<OrcX86_64_SysV>(EPC);
+ }
+}
+
+Error EPCIndirectionUtils::cleanup() {
+ Error Err = Error::success();
+
+ for (auto &A : IndirectStubAllocs)
+ Err = joinErrors(std::move(Err), A->deallocate());
+
+ if (TP)
+ Err = joinErrors(std::move(Err),
+ static_cast<EPCTrampolinePool &>(*TP).deallocatePool());
+
+ if (ResolverBlock)
+ Err = joinErrors(std::move(Err), ResolverBlock->deallocate());
+
+ return Err;
+}
+
+Expected<JITTargetAddress>
+EPCIndirectionUtils::writeResolverBlock(JITTargetAddress ReentryFnAddr,
+ JITTargetAddress ReentryCtxAddr) {
+ assert(ABI && "ABI can not be null");
+ constexpr auto ResolverBlockPermissions =
+ static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
+ sys::Memory::MF_EXEC);
+ auto ResolverSize = ABI->getResolverCodeSize();
+
+ jitlink::JITLinkMemoryManager::SegmentsRequestMap Request;
+ Request[ResolverBlockPermissions] = {EPC.getPageSize(),
+ static_cast<size_t>(ResolverSize), 0};
+ auto Alloc = EPC.getMemMgr().allocate(nullptr, Request);
+ if (!Alloc)
+ return Alloc.takeError();
+
+ auto WorkingMemory = (*Alloc)->getWorkingMemory(ResolverBlockPermissions);
+ ResolverBlockAddr = (*Alloc)->getTargetMemory(ResolverBlockPermissions);
+ ABI->writeResolverCode(WorkingMemory.data(), ResolverBlockAddr, ReentryFnAddr,
+ ReentryCtxAddr);
+
+ if (auto Err = (*Alloc)->finalize())
+ return std::move(Err);
+
+ ResolverBlock = std::move(*Alloc);
+ return ResolverBlockAddr;
+}
+
+std::unique_ptr<IndirectStubsManager>
+EPCIndirectionUtils::createIndirectStubsManager() {
+ return std::make_unique<EPCIndirectStubsManager>(*this);
+}
+
+TrampolinePool &EPCIndirectionUtils::getTrampolinePool() {
+ if (!TP)
+ TP = std::make_unique<EPCTrampolinePool>(*this);
+ return *TP;
+}
+
+LazyCallThroughManager &EPCIndirectionUtils::createLazyCallThroughManager(
+ ExecutionSession &ES, JITTargetAddress ErrorHandlerAddr) {
+ assert(!LCTM &&
+ "createLazyCallThroughManager can not have been called before");
+ LCTM = std::make_unique<LazyCallThroughManager>(ES, ErrorHandlerAddr,
+ &getTrampolinePool());
+ return *LCTM;
+}
+
+EPCIndirectionUtils::EPCIndirectionUtils(ExecutorProcessControl &EPC,
+ std::unique_ptr<ABISupport> ABI)
+ : EPC(EPC), ABI(std::move(ABI)) {
+ assert(this->ABI && "ABI can not be null");
+
+ assert(EPC.getPageSize() > getABISupport().getStubSize() &&
+ "Stubs larger than one page are not supported");
+}
+
+Expected<EPCIndirectionUtils::IndirectStubInfoVector>
+EPCIndirectionUtils::getIndirectStubs(unsigned NumStubs) {
+
+ std::lock_guard<std::mutex> Lock(EPCUIMutex);
+
+ // If there aren't enough stubs available then allocate some more.
+ if (NumStubs > AvailableIndirectStubs.size()) {
+ auto NumStubsToAllocate = NumStubs;
+ auto PageSize = EPC.getPageSize();
+ auto StubBytes = alignTo(NumStubsToAllocate * ABI->getStubSize(), PageSize);
+ NumStubsToAllocate = StubBytes / ABI->getStubSize();
+ auto PointerBytes =
+ alignTo(NumStubsToAllocate * ABI->getPointerSize(), PageSize);
+
+ constexpr auto StubPagePermissions =
+ static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
+ sys::Memory::MF_EXEC);
+ constexpr auto PointerPagePermissions =
+ static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
+ sys::Memory::MF_WRITE);
+
+ jitlink::JITLinkMemoryManager::SegmentsRequestMap Request;
+ Request[StubPagePermissions] = {PageSize, static_cast<size_t>(StubBytes),
+ 0};
+ Request[PointerPagePermissions] = {PageSize, 0, PointerBytes};
+ auto Alloc = EPC.getMemMgr().allocate(nullptr, Request);
+ if (!Alloc)
+ return Alloc.takeError();
+
+ auto StubTargetAddr = (*Alloc)->getTargetMemory(StubPagePermissions);
+ auto PointerTargetAddr = (*Alloc)->getTargetMemory(PointerPagePermissions);
+
+ ABI->writeIndirectStubsBlock(
+ (*Alloc)->getWorkingMemory(StubPagePermissions).data(), StubTargetAddr,
+ PointerTargetAddr, NumStubsToAllocate);
+
+ if (auto Err = (*Alloc)->finalize())
+ return std::move(Err);
+
+ for (unsigned I = 0; I != NumStubsToAllocate; ++I) {
+ AvailableIndirectStubs.push_back(
+ IndirectStubInfo(StubTargetAddr, PointerTargetAddr));
+ StubTargetAddr += ABI->getStubSize();
+ PointerTargetAddr += ABI->getPointerSize();
+ }
+
+ IndirectStubAllocs.push_back(std::move(*Alloc));
+ }
+
+ assert(NumStubs <= AvailableIndirectStubs.size() &&
+ "Sufficient stubs should have been allocated above");
+
+ IndirectStubInfoVector Result;
+ while (NumStubs--) {
+ Result.push_back(AvailableIndirectStubs.back());
+ AvailableIndirectStubs.pop_back();
+ }
+
+ return std::move(Result);
+}
+
+static JITTargetAddress reentry(JITTargetAddress LCTMAddr,
+ JITTargetAddress TrampolineAddr) {
+ auto &LCTM = *jitTargetAddressToPointer<LazyCallThroughManager *>(LCTMAddr);
+ std::promise<JITTargetAddress> LandingAddrP;
+ auto LandingAddrF = LandingAddrP.get_future();
+ LCTM.resolveTrampolineLandingAddress(
+ TrampolineAddr,
+ [&](JITTargetAddress Addr) { LandingAddrP.set_value(Addr); });
+ return LandingAddrF.get();
+}
+
+Error setUpInProcessLCTMReentryViaEPCIU(EPCIndirectionUtils &EPCIU) {
+ auto &LCTM = EPCIU.getLazyCallThroughManager();
+ return EPCIU
+ .writeResolverBlock(pointerToJITTargetAddress(&reentry),
+ pointerToJITTargetAddress(&LCTM))
+ .takeError();
+}
+
+} // end namespace orc
+} // end namespace llvm