| //===------ LazyReexports.h -- Utilities for lazy reexports -----*- C++ -*-===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Lazy re-exports are similar to normal re-exports, except that for callable |
| // symbols the definitions are replaced with trampolines that will look up and |
| // call through to the re-exported symbol at runtime. This can be used to |
| // enable lazy compilation. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H |
| #define LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H |
| |
| #include "llvm/ExecutionEngine/Orc/Core.h" |
| #include "llvm/ExecutionEngine/Orc/IndirectionUtils.h" |
| #include "llvm/ExecutionEngine/Orc/Speculation.h" |
| |
| namespace llvm { |
| |
| class Triple; |
| |
| namespace orc { |
| |
| /// Manages a set of 'lazy call-through' trampolines. These are compiler |
| /// re-entry trampolines that are pre-bound to look up a given symbol in a given |
| /// JITDylib, then jump to that address. Since compilation of symbols is |
| /// triggered on first lookup, these call-through trampolines can be used to |
| /// implement lazy compilation. |
| /// |
| /// The easiest way to construct these call-throughs is using the lazyReexport |
| /// function. |
| class LazyCallThroughManager { |
| public: |
| /// Clients will want to take some action on first resolution, e.g. updating |
| /// a stub pointer. Instances of this class can be used to implement this. |
| class NotifyResolvedFunction { |
| public: |
| virtual ~NotifyResolvedFunction() {} |
| |
| /// Called the first time a lazy call through is executed and the target |
| /// symbol resolved. |
| virtual Error operator()(JITDylib &SourceJD, |
| const SymbolStringPtr &SymbolName, |
| JITTargetAddress ResolvedAddr) = 0; |
| |
| private: |
| virtual void anchor(); |
| }; |
| |
| template <typename NotifyResolvedImpl> |
| class NotifyResolvedFunctionImpl : public NotifyResolvedFunction { |
| public: |
| NotifyResolvedFunctionImpl(NotifyResolvedImpl NotifyResolved) |
| : NotifyResolved(std::move(NotifyResolved)) {} |
| Error operator()(JITDylib &SourceJD, const SymbolStringPtr &SymbolName, |
| JITTargetAddress ResolvedAddr) { |
| return NotifyResolved(SourceJD, SymbolName, ResolvedAddr); |
| } |
| |
| private: |
| NotifyResolvedImpl NotifyResolved; |
| }; |
| |
| /// Create a shared NotifyResolvedFunction from a given type that is |
| /// callable with the correct signature. |
| template <typename NotifyResolvedImpl> |
| static std::unique_ptr<NotifyResolvedFunction> |
| createNotifyResolvedFunction(NotifyResolvedImpl NotifyResolved) { |
| return std::make_unique<NotifyResolvedFunctionImpl<NotifyResolvedImpl>>( |
| std::move(NotifyResolved)); |
| } |
| |
| // Return a free call-through trampoline and bind it to look up and call |
| // through to the given symbol. |
| Expected<JITTargetAddress> getCallThroughTrampoline( |
| JITDylib &SourceJD, SymbolStringPtr SymbolName, |
| std::shared_ptr<NotifyResolvedFunction> NotifyResolved); |
| |
| protected: |
| LazyCallThroughManager(ExecutionSession &ES, |
| JITTargetAddress ErrorHandlerAddr, |
| std::unique_ptr<TrampolinePool> TP); |
| |
| JITTargetAddress callThroughToSymbol(JITTargetAddress TrampolineAddr); |
| |
| void setTrampolinePool(std::unique_ptr<TrampolinePool> TP) { |
| this->TP = std::move(TP); |
| } |
| |
| private: |
| using ReexportsMap = |
| std::map<JITTargetAddress, std::pair<JITDylib *, SymbolStringPtr>>; |
| |
| using NotifiersMap = |
| std::map<JITTargetAddress, std::shared_ptr<NotifyResolvedFunction>>; |
| |
| std::mutex LCTMMutex; |
| ExecutionSession &ES; |
| JITTargetAddress ErrorHandlerAddr; |
| std::unique_ptr<TrampolinePool> TP; |
| ReexportsMap Reexports; |
| NotifiersMap Notifiers; |
| }; |
| |
| /// A lazy call-through manager that builds trampolines in the current process. |
| class LocalLazyCallThroughManager : public LazyCallThroughManager { |
| private: |
| LocalLazyCallThroughManager(ExecutionSession &ES, |
| JITTargetAddress ErrorHandlerAddr) |
| : LazyCallThroughManager(ES, ErrorHandlerAddr, nullptr) {} |
| |
| template <typename ORCABI> Error init() { |
| auto TP = LocalTrampolinePool<ORCABI>::Create( |
| [this](JITTargetAddress TrampolineAddr) { |
| return callThroughToSymbol(TrampolineAddr); |
| }); |
| |
| if (!TP) |
| return TP.takeError(); |
| |
| setTrampolinePool(std::move(*TP)); |
| return Error::success(); |
| } |
| |
| public: |
| /// Create a LocalLazyCallThroughManager using the given ABI. See |
| /// createLocalLazyCallThroughManager. |
| template <typename ORCABI> |
| static Expected<std::unique_ptr<LocalLazyCallThroughManager>> |
| Create(ExecutionSession &ES, JITTargetAddress ErrorHandlerAddr) { |
| auto LLCTM = std::unique_ptr<LocalLazyCallThroughManager>( |
| new LocalLazyCallThroughManager(ES, ErrorHandlerAddr)); |
| |
| if (auto Err = LLCTM->init<ORCABI>()) |
| return std::move(Err); |
| |
| return std::move(LLCTM); |
| } |
| }; |
| |
| /// Create a LocalLazyCallThroughManager from the given triple and execution |
| /// session. |
| Expected<std::unique_ptr<LazyCallThroughManager>> |
| createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES, |
| JITTargetAddress ErrorHandlerAddr); |
| |
| /// A materialization unit that builds lazy re-exports. These are callable |
| /// entry points that call through to the given symbols. |
| /// Unlike a 'true' re-export, the address of the lazy re-export will not |
| /// match the address of the re-exported symbol, but calling it will behave |
| /// the same as calling the re-exported symbol. |
| class LazyReexportsMaterializationUnit : public MaterializationUnit { |
| public: |
| LazyReexportsMaterializationUnit(LazyCallThroughManager &LCTManager, |
| IndirectStubsManager &ISManager, |
| JITDylib &SourceJD, |
| SymbolAliasMap CallableAliases, |
| ImplSymbolMap *SrcJDLoc, VModuleKey K); |
| |
| StringRef getName() const override; |
| |
| private: |
| void materialize(MaterializationResponsibility R) override; |
| void discard(const JITDylib &JD, const SymbolStringPtr &Name) override; |
| static SymbolFlagsMap extractFlags(const SymbolAliasMap &Aliases); |
| |
| LazyCallThroughManager &LCTManager; |
| IndirectStubsManager &ISManager; |
| JITDylib &SourceJD; |
| SymbolAliasMap CallableAliases; |
| std::shared_ptr<LazyCallThroughManager::NotifyResolvedFunction> |
| NotifyResolved; |
| ImplSymbolMap *AliaseeTable; |
| }; |
| |
| /// Define lazy-reexports based on the given SymbolAliasMap. Each lazy re-export |
| /// is a callable symbol that will look up and dispatch to the given aliasee on |
| /// first call. All subsequent calls will go directly to the aliasee. |
| inline std::unique_ptr<LazyReexportsMaterializationUnit> |
| lazyReexports(LazyCallThroughManager &LCTManager, |
| IndirectStubsManager &ISManager, JITDylib &SourceJD, |
| SymbolAliasMap CallableAliases, ImplSymbolMap *SrcJDLoc = nullptr, |
| VModuleKey K = VModuleKey()) { |
| return std::make_unique<LazyReexportsMaterializationUnit>( |
| LCTManager, ISManager, SourceJD, std::move(CallableAliases), SrcJDLoc, |
| std::move(K)); |
| } |
| |
| } // End namespace orc |
| } // End namespace llvm |
| |
| #endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H |