| //===----- JITLinkReentryTrampolines.cpp -- JITLink-based trampoline- -----===// |
| // |
| // 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/JITLinkReentryTrampolines.h" |
| |
| #include "llvm/ExecutionEngine/JITLink/aarch64.h" |
| #include "llvm/ExecutionEngine/JITLink/x86_64.h" |
| #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" |
| |
| #include <memory> |
| |
| #define DEBUG_TYPE "orc" |
| |
| using namespace llvm; |
| using namespace llvm::jitlink; |
| |
| namespace { |
| constexpr StringRef ReentryFnName = "__orc_rt_reenter"; |
| constexpr StringRef ReentrySectionName = "__orc_stubs"; |
| } // namespace |
| |
| namespace llvm::orc { |
| |
| class JITLinkReentryTrampolines::TrampolineAddrScraperPlugin |
| : public ObjectLinkingLayer::Plugin { |
| public: |
| void modifyPassConfig(MaterializationResponsibility &MR, |
| jitlink::LinkGraph &G, |
| jitlink::PassConfiguration &Config) override { |
| Config.PreFixupPasses.push_back( |
| [this](LinkGraph &G) { return recordTrampolineAddrs(G); }); |
| } |
| |
| Error notifyFailed(MaterializationResponsibility &MR) override { |
| return Error::success(); |
| } |
| |
| Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override { |
| return Error::success(); |
| } |
| |
| void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, |
| ResourceKey SrcKey) override {} |
| |
| void registerGraph(LinkGraph &G, |
| std::shared_ptr<std::vector<ExecutorSymbolDef>> Addrs) { |
| std::lock_guard<std::mutex> Lock(M); |
| assert(!PendingAddrs.count(&G) && "Duplicate registration"); |
| PendingAddrs[&G] = std::move(Addrs); |
| } |
| |
| Error recordTrampolineAddrs(LinkGraph &G) { |
| std::shared_ptr<std::vector<ExecutorSymbolDef>> Addrs; |
| { |
| std::lock_guard<std::mutex> Lock(M); |
| auto I = PendingAddrs.find(&G); |
| if (I == PendingAddrs.end()) |
| return Error::success(); |
| Addrs = std::move(I->second); |
| PendingAddrs.erase(I); |
| } |
| |
| auto *Sec = G.findSectionByName(ReentrySectionName); |
| assert(Sec && "Reentry graph missing reentry section"); |
| assert(!Sec->empty() && "Reentry graph is empty"); |
| |
| for (auto *Sym : Sec->symbols()) |
| if (!Sym->hasName()) |
| Addrs->push_back({Sym->getAddress(), JITSymbolFlags()}); |
| |
| return Error::success(); |
| } |
| |
| private: |
| std::mutex M; |
| DenseMap<LinkGraph *, std::shared_ptr<std::vector<ExecutorSymbolDef>>> |
| PendingAddrs; |
| }; |
| |
| Expected<std::unique_ptr<JITLinkReentryTrampolines>> |
| JITLinkReentryTrampolines::Create(ObjectLinkingLayer &ObjLinkingLayer) { |
| |
| EmitTrampolineFn EmitTrampoline; |
| |
| const auto &TT = ObjLinkingLayer.getExecutionSession().getTargetTriple(); |
| switch (TT.getArch()) { |
| case Triple::aarch64: |
| EmitTrampoline = aarch64::createAnonymousReentryTrampoline; |
| break; |
| case Triple::x86_64: |
| EmitTrampoline = x86_64::createAnonymousReentryTrampoline; |
| break; |
| default: |
| return make_error<StringError>("JITLinkReentryTrampolines: architecture " + |
| TT.getArchName() + " not supported", |
| inconvertibleErrorCode()); |
| } |
| |
| return std::make_unique<JITLinkReentryTrampolines>(ObjLinkingLayer, |
| std::move(EmitTrampoline)); |
| } |
| |
| JITLinkReentryTrampolines::JITLinkReentryTrampolines( |
| ObjectLinkingLayer &ObjLinkingLayer, EmitTrampolineFn EmitTrampoline) |
| : ObjLinkingLayer(ObjLinkingLayer), |
| EmitTrampoline(std::move(EmitTrampoline)) { |
| auto TAS = std::make_shared<TrampolineAddrScraperPlugin>(); |
| TrampolineAddrScraper = TAS.get(); |
| ObjLinkingLayer.addPlugin(std::move(TAS)); |
| } |
| |
| void JITLinkReentryTrampolines::emit(ResourceTrackerSP RT, |
| size_t NumTrampolines, |
| OnTrampolinesReadyFn OnTrampolinesReady) { |
| |
| if (NumTrampolines == 0) |
| return OnTrampolinesReady(std::vector<ExecutorSymbolDef>()); |
| |
| JITDylibSP JD(&RT->getJITDylib()); |
| auto &ES = ObjLinkingLayer.getExecutionSession(); |
| |
| auto ReentryGraphSym = |
| ES.intern(("__orc_reentry_graph_#" + Twine(++ReentryGraphIdx)).str()); |
| |
| auto G = std::make_unique<jitlink::LinkGraph>( |
| (*ReentryGraphSym).str(), ES.getSymbolStringPool(), ES.getTargetTriple(), |
| SubtargetFeatures(), jitlink::getGenericEdgeKindName); |
| |
| auto &ReentryFnSym = G->addExternalSymbol(ReentryFnName, 0, false); |
| |
| auto &ReentrySection = |
| G->createSection(ReentrySectionName, MemProt::Exec | MemProt::Read); |
| |
| for (size_t I = 0; I != NumTrampolines; ++I) |
| EmitTrampoline(*G, ReentrySection, ReentryFnSym).setLive(true); |
| |
| auto &FirstBlock = **ReentrySection.blocks().begin(); |
| G->addDefinedSymbol(FirstBlock, 0, *ReentryGraphSym, FirstBlock.getSize(), |
| Linkage::Strong, Scope::SideEffectsOnly, true, true); |
| |
| auto TrampolineAddrs = std::make_shared<std::vector<ExecutorSymbolDef>>(); |
| TrampolineAddrScraper->registerGraph(*G, TrampolineAddrs); |
| |
| // Add Graph via object linking layer. |
| if (auto Err = ObjLinkingLayer.add(std::move(RT), std::move(G))) |
| return OnTrampolinesReady(std::move(Err)); |
| |
| // Trigger graph emission. |
| ES.lookup( |
| LookupKind::Static, {{JD.get(), JITDylibLookupFlags::MatchAllSymbols}}, |
| SymbolLookupSet(ReentryGraphSym, |
| SymbolLookupFlags::WeaklyReferencedSymbol), |
| SymbolState::Ready, |
| [OnTrampolinesReady = std::move(OnTrampolinesReady), |
| TrampolineAddrs = |
| std::move(TrampolineAddrs)](Expected<SymbolMap> Result) mutable { |
| if (Result) |
| OnTrampolinesReady(std::move(*TrampolineAddrs)); |
| else |
| OnTrampolinesReady(Result.takeError()); |
| }, |
| NoDependenciesToRegister); |
| } |
| |
| Expected<std::unique_ptr<LazyReexportsManager>> |
| createJITLinkLazyReexportsManager(ObjectLinkingLayer &ObjLinkingLayer, |
| RedirectableSymbolManager &RSMgr, |
| JITDylib &PlatformJD, |
| LazyReexportsManager::Listener *L) { |
| auto JLT = JITLinkReentryTrampolines::Create(ObjLinkingLayer); |
| if (!JLT) |
| return JLT.takeError(); |
| |
| return LazyReexportsManager::Create( |
| [JLT = std::move(*JLT)](ResourceTrackerSP RT, size_t NumTrampolines, |
| LazyReexportsManager::OnTrampolinesReadyFn |
| OnTrampolinesReady) mutable { |
| JLT->emit(std::move(RT), NumTrampolines, std::move(OnTrampolinesReady)); |
| }, |
| RSMgr, PlatformJD, L); |
| } |
| |
| } // namespace llvm::orc |