//===--- EPCIndirectionUtils.h - EPC based indirection utils ----*- 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
 
//
 
//===----------------------------------------------------------------------===//
 
//
 
// Indirection utilities (stubs, trampolines, lazy call-throughs) that use the
 
// ExecutorProcessControl API to interact with the executor process.
 
//
 
//===----------------------------------------------------------------------===//
 
 
 
#ifndef LLVM_EXECUTIONENGINE_ORC_EPCINDIRECTIONUTILS_H
 
#define LLVM_EXECUTIONENGINE_ORC_EPCINDIRECTIONUTILS_H
 
 
 
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
 
#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
 
#include "llvm/ExecutionEngine/Orc/LazyReexports.h"
 
 
 
#include <mutex>
 
 
 
namespace llvm {
 
namespace orc {
 
 
 
class ExecutorProcessControl;
 
 
 
/// Provides ExecutorProcessControl based indirect stubs, trampoline pool and
 
/// lazy call through manager.
 
class EPCIndirectionUtils {
 
  friend class EPCIndirectionUtilsAccess;
 
 
 
public:
 
  /// ABI support base class. Used to write resolver, stub, and trampoline
 
  /// blocks.
 
  class ABISupport {
 
  protected:
 
    ABISupport(unsigned PointerSize, unsigned TrampolineSize, unsigned StubSize,
 
               unsigned StubToPointerMaxDisplacement, unsigned ResolverCodeSize)
 
        : PointerSize(PointerSize), TrampolineSize(TrampolineSize),
 
          StubSize(StubSize),
 
          StubToPointerMaxDisplacement(StubToPointerMaxDisplacement),
 
          ResolverCodeSize(ResolverCodeSize) {}
 
 
 
  public:
 
    virtual ~ABISupport();
 
 
 
    unsigned getPointerSize() const { return PointerSize; }
 
    unsigned getTrampolineSize() const { return TrampolineSize; }
 
    unsigned getStubSize() const { return StubSize; }
 
    unsigned getStubToPointerMaxDisplacement() const {
 
      return StubToPointerMaxDisplacement;
 
    }
 
    unsigned getResolverCodeSize() const { return ResolverCodeSize; }
 
 
 
    virtual void writeResolverCode(char *ResolverWorkingMem,
 
                                   JITTargetAddress ResolverTargetAddr,
 
                                   JITTargetAddress ReentryFnAddr,
 
                                   JITTargetAddress ReentryCtxAddr) const = 0;
 
 
 
    virtual void writeTrampolines(char *TrampolineBlockWorkingMem,
 
                                  JITTargetAddress TrampolineBlockTragetAddr,
 
                                  JITTargetAddress ResolverAddr,
 
                                  unsigned NumTrampolines) const = 0;
 
 
 
    virtual void
 
    writeIndirectStubsBlock(char *StubsBlockWorkingMem,
 
                            JITTargetAddress StubsBlockTargetAddress,
 
                            JITTargetAddress PointersBlockTargetAddress,
 
                            unsigned NumStubs) const = 0;
 
 
 
  private:
 
    unsigned PointerSize = 0;
 
    unsigned TrampolineSize = 0;
 
    unsigned StubSize = 0;
 
    unsigned StubToPointerMaxDisplacement = 0;
 
    unsigned ResolverCodeSize = 0;
 
  };
 
 
 
  /// Create using the given ABI class.
 
  template <typename ORCABI>
 
  static std::unique_ptr<EPCIndirectionUtils>
 
  CreateWithABI(ExecutorProcessControl &EPC);
 
 
 
  /// Create based on the ExecutorProcessControl triple.
 
  static Expected<std::unique_ptr<EPCIndirectionUtils>>
 
  Create(ExecutorProcessControl &EPC);
 
 
 
  /// Return a reference to the ExecutorProcessControl object.
 
  ExecutorProcessControl &getExecutorProcessControl() const { return EPC; }
 
 
 
  /// Return a reference to the ABISupport object for this instance.
 
  ABISupport &getABISupport() const { return *ABI; }
 
 
 
  /// Release memory for resources held by this instance. This *must* be called
 
  /// prior to destruction of the class.
 
  Error cleanup();
 
 
 
  /// Write resolver code to the executor process and return its address.
 
  /// This must be called before any call to createTrampolinePool or
 
  /// createLazyCallThroughManager.
 
  Expected<JITTargetAddress>
 
  writeResolverBlock(JITTargetAddress ReentryFnAddr,
 
                     JITTargetAddress ReentryCtxAddr);
 
 
 
  /// Returns the address of the Resolver block. Returns zero if the
 
  /// writeResolverBlock method has not previously been called.
 
  JITTargetAddress getResolverBlockAddress() const { return ResolverBlockAddr; }
 
 
 
  /// Create an IndirectStubsManager for the executor process.
 
  std::unique_ptr<IndirectStubsManager> createIndirectStubsManager();
 
 
 
  /// Create a TrampolinePool for the executor process.
 
  TrampolinePool &getTrampolinePool();
 
 
 
  /// Create a LazyCallThroughManager.
 
  /// This function should only be called once.
 
  LazyCallThroughManager &
 
  createLazyCallThroughManager(ExecutionSession &ES,
 
                               JITTargetAddress ErrorHandlerAddr);
 
 
 
  /// Create a LazyCallThroughManager for the executor process.
 
  LazyCallThroughManager &getLazyCallThroughManager() {
 
    assert(LCTM && "createLazyCallThroughManager must be called first");
 
    return *LCTM;
 
  }
 
 
 
private:
 
  using FinalizedAlloc = jitlink::JITLinkMemoryManager::FinalizedAlloc;
 
 
 
  struct IndirectStubInfo {
 
    IndirectStubInfo() = default;
 
    IndirectStubInfo(JITTargetAddress StubAddress,
 
                     JITTargetAddress PointerAddress)
 
        : StubAddress(StubAddress), PointerAddress(PointerAddress) {}
 
    JITTargetAddress StubAddress = 0;
 
    JITTargetAddress PointerAddress = 0;
 
  };
 
 
 
  using IndirectStubInfoVector = std::vector<IndirectStubInfo>;
 
 
 
  /// Create an EPCIndirectionUtils instance.
 
  EPCIndirectionUtils(ExecutorProcessControl &EPC,
 
                      std::unique_ptr<ABISupport> ABI);
 
 
 
  Expected<IndirectStubInfoVector> getIndirectStubs(unsigned NumStubs);
 
 
 
  std::mutex EPCUIMutex;
 
  ExecutorProcessControl &EPC;
 
  std::unique_ptr<ABISupport> ABI;
 
  JITTargetAddress ResolverBlockAddr = 0;
 
  FinalizedAlloc ResolverBlock;
 
  std::unique_ptr<TrampolinePool> TP;
 
  std::unique_ptr<LazyCallThroughManager> LCTM;
 
 
 
  std::vector<IndirectStubInfo> AvailableIndirectStubs;
 
  std::vector<FinalizedAlloc> IndirectStubAllocs;
 
};
 
 
 
/// This will call writeResolver on the given EPCIndirectionUtils instance
 
/// to set up re-entry via a function that will directly return the trampoline
 
/// landing address.
 
///
 
/// The EPCIndirectionUtils' LazyCallThroughManager must have been previously
 
/// created via EPCIndirectionUtils::createLazyCallThroughManager.
 
///
 
/// The EPCIndirectionUtils' writeResolver method must not have been previously
 
/// called.
 
///
 
/// This function is experimental and likely subject to revision.
 
Error setUpInProcessLCTMReentryViaEPCIU(EPCIndirectionUtils &EPCIU);
 
 
 
namespace detail {
 
 
 
template <typename ORCABI>
 
class ABISupportImpl : public EPCIndirectionUtils::ABISupport {
 
public:
 
  ABISupportImpl()
 
      : ABISupport(ORCABI::PointerSize, ORCABI::TrampolineSize,
 
                   ORCABI::StubSize, ORCABI::StubToPointerMaxDisplacement,
 
                   ORCABI::ResolverCodeSize) {}
 
 
 
  void writeResolverCode(char *ResolverWorkingMem,
 
                         JITTargetAddress ResolverTargetAddr,
 
                         JITTargetAddress ReentryFnAddr,
 
                         JITTargetAddress ReentryCtxAddr) const override {
 
    ORCABI::writeResolverCode(ResolverWorkingMem, ResolverTargetAddr,
 
                              ReentryFnAddr, ReentryCtxAddr);
 
  }
 
 
 
  void writeTrampolines(char *TrampolineBlockWorkingMem,
 
                        JITTargetAddress TrampolineBlockTargetAddr,
 
                        JITTargetAddress ResolverAddr,
 
                        unsigned NumTrampolines) const override {
 
    ORCABI::writeTrampolines(TrampolineBlockWorkingMem,
 
                             TrampolineBlockTargetAddr, ResolverAddr,
 
                             NumTrampolines);
 
  }
 
 
 
  void writeIndirectStubsBlock(char *StubsBlockWorkingMem,
 
                               JITTargetAddress StubsBlockTargetAddress,
 
                               JITTargetAddress PointersBlockTargetAddress,
 
                               unsigned NumStubs) const override {
 
    ORCABI::writeIndirectStubsBlock(StubsBlockWorkingMem,
 
                                    StubsBlockTargetAddress,
 
                                    PointersBlockTargetAddress, NumStubs);
 
  }
 
};
 
 
 
} // end namespace detail
 
 
 
template <typename ORCABI>
 
std::unique_ptr<EPCIndirectionUtils>
 
EPCIndirectionUtils::CreateWithABI(ExecutorProcessControl &EPC) {
 
  return std::unique_ptr<EPCIndirectionUtils>(new EPCIndirectionUtils(
 
      EPC, std::make_unique<detail::ABISupportImpl<ORCABI>>()));
 
}
 
 
 
} // end namespace orc
 
} // end namespace llvm
 
 
 
#endif // LLVM_EXECUTIONENGINE_ORC_EPCINDIRECTIONUTILS_H