Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:43:53

0001 //===------ LazyReexports.h -- Utilities for lazy reexports -----*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 // Lazy re-exports are similar to normal re-exports, except that for callable
0010 // symbols the definitions are replaced with trampolines that will look up and
0011 // call through to the re-exported symbol at runtime. This can be used to
0012 // enable lazy compilation.
0013 //
0014 //===----------------------------------------------------------------------===//
0015 
0016 #ifndef LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
0017 #define LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
0018 
0019 #include "llvm/ADT/STLExtras.h"
0020 #include "llvm/ExecutionEngine/Orc/Core.h"
0021 #include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
0022 #include "llvm/ExecutionEngine/Orc/RedirectionManager.h"
0023 #include "llvm/ExecutionEngine/Orc/Speculation.h"
0024 
0025 namespace llvm {
0026 
0027 class Triple;
0028 
0029 namespace orc {
0030 
0031 /// Manages a set of 'lazy call-through' trampolines. These are compiler
0032 /// re-entry trampolines that are pre-bound to look up a given symbol in a given
0033 /// JITDylib, then jump to that address. Since compilation of symbols is
0034 /// triggered on first lookup, these call-through trampolines can be used to
0035 /// implement lazy compilation.
0036 ///
0037 /// The easiest way to construct these call-throughs is using the lazyReexport
0038 /// function.
0039 class LazyCallThroughManager {
0040 public:
0041   using NotifyResolvedFunction =
0042       unique_function<Error(ExecutorAddr ResolvedAddr)>;
0043 
0044   LazyCallThroughManager(ExecutionSession &ES, ExecutorAddr ErrorHandlerAddr,
0045                          TrampolinePool *TP);
0046 
0047   // Return a free call-through trampoline and bind it to look up and call
0048   // through to the given symbol.
0049   Expected<ExecutorAddr>
0050   getCallThroughTrampoline(JITDylib &SourceJD, SymbolStringPtr SymbolName,
0051                            NotifyResolvedFunction NotifyResolved);
0052 
0053   void resolveTrampolineLandingAddress(
0054       ExecutorAddr TrampolineAddr,
0055       TrampolinePool::NotifyLandingResolvedFunction NotifyLandingResolved);
0056 
0057   virtual ~LazyCallThroughManager() = default;
0058 
0059 protected:
0060   using NotifyLandingResolvedFunction =
0061       TrampolinePool::NotifyLandingResolvedFunction;
0062 
0063   struct ReexportsEntry {
0064     JITDylib *SourceJD;
0065     SymbolStringPtr SymbolName;
0066   };
0067 
0068   ExecutorAddr reportCallThroughError(Error Err);
0069   Expected<ReexportsEntry> findReexport(ExecutorAddr TrampolineAddr);
0070   Error notifyResolved(ExecutorAddr TrampolineAddr, ExecutorAddr ResolvedAddr);
0071   void setTrampolinePool(TrampolinePool &TP) { this->TP = &TP; }
0072 
0073 private:
0074   using ReexportsMap = std::map<ExecutorAddr, ReexportsEntry>;
0075 
0076   using NotifiersMap = std::map<ExecutorAddr, NotifyResolvedFunction>;
0077 
0078   std::mutex LCTMMutex;
0079   ExecutionSession &ES;
0080   ExecutorAddr ErrorHandlerAddr;
0081   TrampolinePool *TP = nullptr;
0082   ReexportsMap Reexports;
0083   NotifiersMap Notifiers;
0084 };
0085 
0086 /// A lazy call-through manager that builds trampolines in the current process.
0087 class LocalLazyCallThroughManager : public LazyCallThroughManager {
0088 private:
0089   using NotifyTargetResolved = unique_function<void(ExecutorAddr)>;
0090 
0091   LocalLazyCallThroughManager(ExecutionSession &ES,
0092                               ExecutorAddr ErrorHandlerAddr)
0093       : LazyCallThroughManager(ES, ErrorHandlerAddr, nullptr) {}
0094 
0095   template <typename ORCABI> Error init() {
0096     auto TP = LocalTrampolinePool<ORCABI>::Create(
0097         [this](ExecutorAddr TrampolineAddr,
0098                TrampolinePool::NotifyLandingResolvedFunction
0099                    NotifyLandingResolved) {
0100           resolveTrampolineLandingAddress(TrampolineAddr,
0101                                           std::move(NotifyLandingResolved));
0102         });
0103 
0104     if (!TP)
0105       return TP.takeError();
0106 
0107     this->TP = std::move(*TP);
0108     setTrampolinePool(*this->TP);
0109     return Error::success();
0110   }
0111 
0112   std::unique_ptr<TrampolinePool> TP;
0113 
0114 public:
0115   /// Create a LocalLazyCallThroughManager using the given ABI. See
0116   /// createLocalLazyCallThroughManager.
0117   template <typename ORCABI>
0118   static Expected<std::unique_ptr<LocalLazyCallThroughManager>>
0119   Create(ExecutionSession &ES, ExecutorAddr ErrorHandlerAddr) {
0120     auto LLCTM = std::unique_ptr<LocalLazyCallThroughManager>(
0121         new LocalLazyCallThroughManager(ES, ErrorHandlerAddr));
0122 
0123     if (auto Err = LLCTM->init<ORCABI>())
0124       return std::move(Err);
0125 
0126     return std::move(LLCTM);
0127   }
0128 };
0129 
0130 /// Create a LocalLazyCallThroughManager from the given triple and execution
0131 /// session.
0132 Expected<std::unique_ptr<LazyCallThroughManager>>
0133 createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES,
0134                                   ExecutorAddr ErrorHandlerAddr);
0135 
0136 /// A materialization unit that builds lazy re-exports. These are callable
0137 /// entry points that call through to the given symbols.
0138 /// Unlike a 'true' re-export, the address of the lazy re-export will not
0139 /// match the address of the re-exported symbol, but calling it will behave
0140 /// the same as calling the re-exported symbol.
0141 class LazyReexportsMaterializationUnit : public MaterializationUnit {
0142 public:
0143   LazyReexportsMaterializationUnit(LazyCallThroughManager &LCTManager,
0144                                    RedirectableSymbolManager &RSManager,
0145                                    JITDylib &SourceJD,
0146                                    SymbolAliasMap CallableAliases,
0147                                    ImplSymbolMap *SrcJDLoc);
0148 
0149   StringRef getName() const override;
0150 
0151 private:
0152   void materialize(std::unique_ptr<MaterializationResponsibility> R) override;
0153   void discard(const JITDylib &JD, const SymbolStringPtr &Name) override;
0154   static MaterializationUnit::Interface
0155   extractFlags(const SymbolAliasMap &Aliases);
0156 
0157   LazyCallThroughManager &LCTManager;
0158   RedirectableSymbolManager &RSManager;
0159   JITDylib &SourceJD;
0160   SymbolAliasMap CallableAliases;
0161   ImplSymbolMap *AliaseeTable;
0162 };
0163 
0164 /// Define lazy-reexports based on the given SymbolAliasMap. Each lazy re-export
0165 /// is a callable symbol that will look up and dispatch to the given aliasee on
0166 /// first call. All subsequent calls will go directly to the aliasee.
0167 inline std::unique_ptr<LazyReexportsMaterializationUnit>
0168 lazyReexports(LazyCallThroughManager &LCTManager,
0169               RedirectableSymbolManager &RSManager, JITDylib &SourceJD,
0170               SymbolAliasMap CallableAliases,
0171               ImplSymbolMap *SrcJDLoc = nullptr) {
0172   return std::make_unique<LazyReexportsMaterializationUnit>(
0173       LCTManager, RSManager, SourceJD, std::move(CallableAliases), SrcJDLoc);
0174 }
0175 
0176 class LazyReexportsManager : public ResourceManager {
0177 
0178   friend std::unique_ptr<MaterializationUnit>
0179   lazyReexports(LazyReexportsManager &, SymbolAliasMap);
0180 
0181 public:
0182   struct CallThroughInfo {
0183     JITDylibSP JD;
0184     SymbolStringPtr Name;
0185     SymbolStringPtr BodyName;
0186   };
0187 
0188   class Listener {
0189   public:
0190     using CallThroughInfo = LazyReexportsManager::CallThroughInfo;
0191 
0192     virtual ~Listener();
0193 
0194     /// Called under the session lock when new lazy reexports are created.
0195     virtual void onLazyReexportsCreated(JITDylib &JD, ResourceKey K,
0196                                         const SymbolAliasMap &Reexports) = 0;
0197 
0198     /// Called under the session lock when lazy reexports have their ownership
0199     /// transferred to a new ResourceKey.
0200     virtual void onLazyReexportsTransfered(JITDylib &JD, ResourceKey DstK,
0201                                            ResourceKey SrcK) = 0;
0202 
0203     /// Called under the session lock when lazy reexports are removed.
0204     virtual Error onLazyReexportsRemoved(JITDylib &JD, ResourceKey K) = 0;
0205 
0206     /// Called outside the session lock when a lazy reexport is called.
0207     /// NOTE: Since this is called outside the session lock there is a chance
0208     ///       that the reexport referred to has already been removed. Listeners
0209     ///       must be prepared to handle requests for stale reexports.
0210     virtual void onLazyReexportCalled(const CallThroughInfo &CTI) = 0;
0211   };
0212 
0213   using OnTrampolinesReadyFn = unique_function<void(
0214       Expected<std::vector<ExecutorSymbolDef>> EntryAddrs)>;
0215   using EmitTrampolinesFn =
0216       unique_function<void(ResourceTrackerSP RT, size_t NumTrampolines,
0217                            OnTrampolinesReadyFn OnTrampolinesReady)>;
0218 
0219   /// Create a LazyReexportsManager that uses the ORC runtime for reentry.
0220   /// This will work both in-process and out-of-process.
0221   static Expected<std::unique_ptr<LazyReexportsManager>>
0222   Create(EmitTrampolinesFn EmitTrampolines, RedirectableSymbolManager &RSMgr,
0223          JITDylib &PlatformJD, Listener *L = nullptr);
0224 
0225   LazyReexportsManager(LazyReexportsManager &&) = delete;
0226   LazyReexportsManager &operator=(LazyReexportsManager &&) = delete;
0227 
0228   Error handleRemoveResources(JITDylib &JD, ResourceKey K) override;
0229   void handleTransferResources(JITDylib &JD, ResourceKey DstK,
0230                                ResourceKey SrcK) override;
0231 
0232 private:
0233   class MU;
0234   class Plugin;
0235 
0236   using ResolveSendResultFn =
0237       unique_function<void(Expected<ExecutorSymbolDef>)>;
0238 
0239   LazyReexportsManager(EmitTrampolinesFn EmitTrampolines,
0240                        RedirectableSymbolManager &RSMgr, JITDylib &PlatformJD,
0241                        Listener *L, Error &Err);
0242 
0243   std::unique_ptr<MaterializationUnit>
0244   createLazyReexports(SymbolAliasMap Reexports);
0245 
0246   void emitReentryTrampolines(std::unique_ptr<MaterializationResponsibility> MR,
0247                               SymbolAliasMap Reexports);
0248   void emitRedirectableSymbols(
0249       std::unique_ptr<MaterializationResponsibility> MR,
0250       SymbolAliasMap Reexports,
0251       Expected<std::vector<ExecutorSymbolDef>> ReentryPoints);
0252   void resolve(ResolveSendResultFn SendResult, ExecutorAddr ReentryStubAddr);
0253 
0254   ExecutionSession &ES;
0255   EmitTrampolinesFn EmitTrampolines;
0256   RedirectableSymbolManager &RSMgr;
0257   Listener *L;
0258 
0259   DenseMap<ResourceKey, std::vector<ExecutorAddr>> KeyToReentryAddrs;
0260   DenseMap<ExecutorAddr, CallThroughInfo> CallThroughs;
0261 };
0262 
0263 /// Define lazy-reexports based on the given SymbolAliasMap. Each lazy re-export
0264 /// is a callable symbol that will look up and dispatch to the given aliasee on
0265 /// first call. All subsequent calls will go directly to the aliasee.
0266 inline std::unique_ptr<MaterializationUnit>
0267 lazyReexports(LazyReexportsManager &LRM, SymbolAliasMap Reexports) {
0268   return LRM.createLazyReexports(std::move(Reexports));
0269 }
0270 
0271 class SimpleLazyReexportsSpeculator : public LazyReexportsManager::Listener {
0272 public:
0273   using RecordExecutionFunction =
0274       unique_function<void(const CallThroughInfo &CTI)>;
0275 
0276   static std::shared_ptr<SimpleLazyReexportsSpeculator>
0277   Create(ExecutionSession &ES, RecordExecutionFunction RecordExec = {}) {
0278     class make_shared_helper : public SimpleLazyReexportsSpeculator {
0279     public:
0280       make_shared_helper(ExecutionSession &ES,
0281                          RecordExecutionFunction RecordExec)
0282           : SimpleLazyReexportsSpeculator(ES, std::move(RecordExec)) {}
0283     };
0284 
0285     auto Instance =
0286         std::make_shared<make_shared_helper>(ES, std::move(RecordExec));
0287     Instance->WeakThis = Instance;
0288     return Instance;
0289   }
0290 
0291   SimpleLazyReexportsSpeculator(SimpleLazyReexportsSpeculator &&) = delete;
0292   SimpleLazyReexportsSpeculator &
0293   operator=(SimpleLazyReexportsSpeculator &&) = delete;
0294   ~SimpleLazyReexportsSpeculator() override;
0295 
0296   void onLazyReexportsCreated(JITDylib &JD, ResourceKey K,
0297                               const SymbolAliasMap &Reexports) override;
0298 
0299   void onLazyReexportsTransfered(JITDylib &JD, ResourceKey DstK,
0300                                  ResourceKey SrcK) override;
0301 
0302   Error onLazyReexportsRemoved(JITDylib &JD, ResourceKey K) override;
0303 
0304   void onLazyReexportCalled(const CallThroughInfo &CTI) override;
0305 
0306   void addSpeculationSuggestions(
0307       std::vector<std::pair<std::string, SymbolStringPtr>> NewSuggestions);
0308 
0309 private:
0310   SimpleLazyReexportsSpeculator(ExecutionSession &ES,
0311                                 RecordExecutionFunction RecordExec)
0312       : ES(ES), RecordExec(std::move(RecordExec)) {}
0313 
0314   bool doNextSpeculativeLookup();
0315 
0316   class SpeculateTask;
0317 
0318   using KeyToFunctionBodiesMap =
0319       DenseMap<ResourceKey, std::vector<SymbolStringPtr>>;
0320 
0321   ExecutionSession &ES;
0322   RecordExecutionFunction RecordExec;
0323   std::weak_ptr<SimpleLazyReexportsSpeculator> WeakThis;
0324   DenseMap<JITDylib *, KeyToFunctionBodiesMap> LazyReexports;
0325   std::deque<std::pair<std::string, SymbolStringPtr>> SpeculateSuggestions;
0326   bool SpeculateTaskActive = false;
0327 };
0328 
0329 } // End namespace orc
0330 } // End namespace llvm
0331 
0332 #endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H