diff options
Diffstat (limited to 'include/llvm/ExecutionEngine/Orc/RemoteObjectLayer.h')
| -rw-r--r-- | include/llvm/ExecutionEngine/Orc/RemoteObjectLayer.h | 538 | 
1 files changed, 538 insertions, 0 deletions
| diff --git a/include/llvm/ExecutionEngine/Orc/RemoteObjectLayer.h b/include/llvm/ExecutionEngine/Orc/RemoteObjectLayer.h new file mode 100644 index 000000000000..17255954a99f --- /dev/null +++ b/include/llvm/ExecutionEngine/Orc/RemoteObjectLayer.h @@ -0,0 +1,538 @@ +//===------ RemoteObjectLayer.h - Forwards objs to a remote -----*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Forwards objects to a remote object layer via RPC. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_REMOTEOBJECTLAYER_H +#define LLVM_EXECUTIONENGINE_ORC_REMOTEOBJECTLAYER_H + +#include "llvm/ExecutionEngine/Orc/OrcRemoteTargetRPCAPI.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/ExecutionEngine/Orc/LambdaResolver.h" +#include <map> + +namespace llvm { +namespace orc { + +/// RPC API needed by RemoteObjectClientLayer and RemoteObjectServerLayer. +class RemoteObjectLayerAPI { +public: + +  using ObjHandleT = remote::ResourceIdMgr::ResourceId; + +protected: + +  using RemoteSymbolId = remote::ResourceIdMgr::ResourceId; +  using RemoteSymbol = std::pair<RemoteSymbolId, JITSymbolFlags>; + +public: + +  using BadSymbolHandleError = remote::ResourceNotFound<RemoteSymbolId>; +  using BadObjectHandleError = remote::ResourceNotFound<ObjHandleT>; + +protected: + +  static const ObjHandleT InvalidObjectHandleId = 0; +  static const RemoteSymbolId NullSymbolId = 0; + +  class AddObject +    : public rpc::Function<AddObject, Expected<ObjHandleT>(std::string)> { +  public: +    static const char *getName() { return "AddObject"; } +  }; + +  class RemoveObject +    : public rpc::Function<RemoveObject, Error(ObjHandleT)> { +  public: +    static const char *getName() { return "RemoveObject"; } +  }; + +  class FindSymbol +    : public rpc::Function<FindSymbol, Expected<RemoteSymbol>(std::string, +                                                              bool)> { +  public: +    static const char *getName() { return "FindSymbol"; } +  }; + +  class FindSymbolIn +    : public rpc::Function<FindSymbolIn, +                           Expected<RemoteSymbol>(ObjHandleT, std::string, +                                                  bool)> { +  public: +    static const char *getName() { return "FindSymbolIn"; } +  }; + +  class EmitAndFinalize +    : public rpc::Function<EmitAndFinalize, +                           Error(ObjHandleT)> { +  public: +    static const char *getName() { return "EmitAndFinalize"; } +  }; + +  class Lookup +    : public rpc::Function<Lookup, +                           Expected<RemoteSymbol>(ObjHandleT, std::string)> { +  public: +    static const char *getName() { return "Lookup"; } +  }; + +  class LookupInLogicalDylib +    : public rpc::Function<LookupInLogicalDylib, +                           Expected<RemoteSymbol>(ObjHandleT, std::string)> { +  public: +    static const char *getName() { return "LookupInLogicalDylib"; } +  }; + +  class ReleaseRemoteSymbol +    : public rpc::Function<ReleaseRemoteSymbol, Error(RemoteSymbolId)> { +  public: +    static const char *getName() { return "ReleaseRemoteSymbol"; } +  }; + +  class MaterializeRemoteSymbol +    : public rpc::Function<MaterializeRemoteSymbol, +                           Expected<JITTargetAddress>(RemoteSymbolId)> { +  public: +    static const char *getName() { return "MaterializeRemoteSymbol"; } +  }; +}; + +/// Base class containing common utilities for RemoteObjectClientLayer and +/// RemoteObjectServerLayer. +template <typename RPCEndpoint> +class RemoteObjectLayer : public RemoteObjectLayerAPI { +public: + +  RemoteObjectLayer(RPCEndpoint &Remote, +                    std::function<void(Error)> ReportError) +      : Remote(Remote), ReportError(std::move(ReportError)), +        SymbolIdMgr(NullSymbolId + 1) { +    using ThisT = RemoteObjectLayer<RPCEndpoint>; +    Remote.template addHandler<ReleaseRemoteSymbol>( +             *this, &ThisT::handleReleaseRemoteSymbol); +    Remote.template addHandler<MaterializeRemoteSymbol>( +             *this, &ThisT::handleMaterializeRemoteSymbol); +  } + +protected: + +  /// This class is used as the symbol materializer for JITSymbols returned by +  /// RemoteObjectLayerClient/RemoteObjectLayerServer -- the materializer knows +  /// how to call back to the other RPC endpoint to get the address when +  /// requested. +  class RemoteSymbolMaterializer { +  public: + +    /// Construct a RemoteSymbolMaterializer for the given RemoteObjectLayer +    /// with the given Id. +    RemoteSymbolMaterializer(RemoteObjectLayer &C, +                             RemoteSymbolId Id) +      : C(C), Id(Id) {} + +    RemoteSymbolMaterializer(const RemoteSymbolMaterializer &Other) +      : C(Other.C), Id(Other.Id) { +      // FIXME: This is a horrible, auto_ptr-style, copy-as-move operation. +      //        It should be removed as soon as LLVM has C++14's generalized +      //        lambda capture (at which point the materializer can be moved +      //        into the lambda in remoteToJITSymbol below). +      const_cast<RemoteSymbolMaterializer&>(Other).Id = 0; +    } + +    RemoteSymbolMaterializer& +    operator=(const RemoteSymbolMaterializer&) = delete; + +    /// Release the remote symbol. +    ~RemoteSymbolMaterializer() { +      if (Id) +        C.releaseRemoteSymbol(Id); +    } + +    /// Materialize the symbol on the remote and get its address. +    Expected<JITTargetAddress> materialize() { +      auto Addr = C.materializeRemoteSymbol(Id); +      Id = 0; +      return Addr; +    } + +  private: +    RemoteObjectLayer &C; +    RemoteSymbolId Id; +  }; + +  /// Convenience function for getting a null remote symbol value. +  RemoteSymbol nullRemoteSymbol() { +    return RemoteSymbol(0, JITSymbolFlags()); +  } + +  /// Creates a StringError that contains a copy of Err's log message, then +  /// sends that StringError to ReportError. +  /// +  /// This allows us to locally log error messages for errors that will actually +  /// be delivered to the remote. +  Error teeLog(Error Err) { +    return handleErrors(std::move(Err), +                        [this](std::unique_ptr<ErrorInfoBase> EIB) { +                          ReportError(make_error<StringError>( +                                        EIB->message(), +                                        EIB->convertToErrorCode())); +                          return Error(std::move(EIB)); +                        }); +  } + +  Error badRemoteSymbolIdError(RemoteSymbolId Id) { +    return make_error<BadSymbolHandleError>(Id, "Remote JIT Symbol"); +  } + +  Error badObjectHandleError(ObjHandleT H) { +    return make_error<RemoteObjectLayerAPI::BadObjectHandleError>( +             H, "Bad object handle"); +  } + +  /// Create a RemoteSymbol wrapping the given JITSymbol. +  Expected<RemoteSymbol> jitSymbolToRemote(JITSymbol Sym) { +    if (Sym) { +      auto Id = SymbolIdMgr.getNext(); +      auto Flags = Sym.getFlags(); +      assert(!InUseSymbols.count(Id) && "Symbol id already in use"); +      InUseSymbols.insert(std::make_pair(Id, std::move(Sym))); +      return RemoteSymbol(Id, Flags); +    } else if (auto Err = Sym.takeError()) +      return teeLog(std::move(Err)); +    // else... +    return nullRemoteSymbol(); +  } + +  /// Convert an Expected<RemoteSymbol> to a JITSymbol. +  JITSymbol remoteToJITSymbol(Expected<RemoteSymbol> RemoteSymOrErr) { +    if (RemoteSymOrErr) { +      auto &RemoteSym = *RemoteSymOrErr; +      if (RemoteSym == nullRemoteSymbol()) +        return nullptr; +      // else... +      RemoteSymbolMaterializer RSM(*this, RemoteSym.first); +      auto Sym = +        JITSymbol([RSM]() mutable { return RSM.materialize(); }, +                  RemoteSym.second); +      return Sym; +    } else +      return RemoteSymOrErr.takeError(); +  } + +  RPCEndpoint &Remote; +  std::function<void(Error)> ReportError; + +private: + +  /// Notify the remote to release the given JITSymbol. +  void releaseRemoteSymbol(RemoteSymbolId Id) { +    if (auto Err = Remote.template callB<ReleaseRemoteSymbol>(Id)) +      ReportError(std::move(Err)); +  } + +  /// Notify the remote to materialize the JITSymbol with the given Id and +  /// return its address. +  Expected<JITTargetAddress> materializeRemoteSymbol(RemoteSymbolId Id) { +    return Remote.template callB<MaterializeRemoteSymbol>(Id); +  } + +  /// Release the JITSymbol with the given Id. +  Error handleReleaseRemoteSymbol(RemoteSymbolId Id) { +    auto SI = InUseSymbols.find(Id); +    if (SI != InUseSymbols.end()) { +      InUseSymbols.erase(SI); +      return Error::success(); +    } else +      return teeLog(badRemoteSymbolIdError(Id)); +  } + +  /// Run the materializer for the JITSymbol with the given Id and return its +  /// address. +  Expected<JITTargetAddress> handleMaterializeRemoteSymbol(RemoteSymbolId Id) { +    auto SI = InUseSymbols.find(Id); +    if (SI != InUseSymbols.end()) { +      auto AddrOrErr = SI->second.getAddress(); +      InUseSymbols.erase(SI); +      SymbolIdMgr.release(Id); +      if (AddrOrErr) +        return *AddrOrErr; +      else +        return teeLog(AddrOrErr.takeError()); +    } else { +      return teeLog(badRemoteSymbolIdError(Id)); +    } +  } + +  remote::ResourceIdMgr SymbolIdMgr; +  std::map<RemoteSymbolId, JITSymbol> InUseSymbols; +}; + +/// RemoteObjectClientLayer forwards the ORC Object Layer API over an RPC +/// connection. +/// +/// This class can be used as the base layer of a JIT stack on the client and +/// will forward operations to a corresponding RemoteObjectServerLayer on the +/// server (which can be composed on top of a "real" object layer like +/// RTDyldObjectLinkingLayer to actually carry out the operations). +/// +/// Sending relocatable objects to the server (rather than fully relocated +/// bits) allows JIT'd code to be cached on the server side and re-used in +/// subsequent JIT sessions. +template <typename RPCEndpoint> +class RemoteObjectClientLayer : public RemoteObjectLayer<RPCEndpoint> { +private: + +  using AddObject = RemoteObjectLayerAPI::AddObject; +  using RemoveObject = RemoteObjectLayerAPI::RemoveObject; +  using FindSymbol = RemoteObjectLayerAPI::FindSymbol; +  using FindSymbolIn = RemoteObjectLayerAPI::FindSymbolIn; +  using EmitAndFinalize = RemoteObjectLayerAPI::EmitAndFinalize; +  using Lookup = RemoteObjectLayerAPI::Lookup; +  using LookupInLogicalDylib = RemoteObjectLayerAPI::LookupInLogicalDylib; + +  using RemoteObjectLayer<RPCEndpoint>::teeLog; +  using RemoteObjectLayer<RPCEndpoint>::badObjectHandleError; +  using RemoteObjectLayer<RPCEndpoint>::remoteToJITSymbol; + +public: + +  using ObjHandleT = RemoteObjectLayerAPI::ObjHandleT; +  using RemoteSymbol = RemoteObjectLayerAPI::RemoteSymbol; + +  using ObjectPtr = +    std::shared_ptr<object::OwningBinary<object::ObjectFile>>; + +  /// Create a RemoteObjectClientLayer that communicates with a +  /// RemoteObjectServerLayer instance via the given RPCEndpoint. +  /// +  /// The ReportError functor can be used locally log errors that are intended +  /// to be sent  sent +  RemoteObjectClientLayer(RPCEndpoint &Remote, +                          std::function<void(Error)> ReportError) +      : RemoteObjectLayer<RPCEndpoint>(Remote, std::move(ReportError)) { +    using ThisT = RemoteObjectClientLayer<RPCEndpoint>; +    Remote.template addHandler<Lookup>(*this, &ThisT::lookup); +    Remote.template addHandler<LookupInLogicalDylib>( +            *this, &ThisT::lookupInLogicalDylib); +  } + +  /// @brief Add an object to the JIT. +  /// +  /// @return A handle that can be used to refer to the loaded object (for +  ///         symbol searching, finalization, freeing memory, etc.). +  Expected<ObjHandleT> +  addObject(ObjectPtr Object, std::shared_ptr<JITSymbolResolver> Resolver) { +    StringRef ObjBuffer = Object->getBinary()->getData(); +    if (auto HandleOrErr = +          this->Remote.template callB<AddObject>(ObjBuffer)) { +      auto &Handle = *HandleOrErr; +      // FIXME: Return an error for this: +      assert(!Resolvers.count(Handle) && "Handle already in use?"); +      Resolvers[Handle] = std::move(Resolver); +      return Handle; +    } else +      return HandleOrErr.takeError(); +  } + +  /// @brief Remove the given object from the JIT. +  Error removeObject(ObjHandleT H) { +    return this->Remote.template callB<RemoveObject>(H); +  } + +  /// @brief Search for the given named symbol. +  JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) { +    return remoteToJITSymbol( +             this->Remote.template callB<FindSymbol>(Name, +                                                     ExportedSymbolsOnly)); +  } + +  /// @brief Search for the given named symbol within the given context. +  JITSymbol findSymbolIn(ObjHandleT H, StringRef Name, bool ExportedSymbolsOnly) { +    return remoteToJITSymbol( +             this->Remote.template callB<FindSymbolIn>(H, Name, +                                                       ExportedSymbolsOnly)); +  } + +  /// @brief Immediately emit and finalize the object with the given handle. +  Error emitAndFinalize(ObjHandleT H) { +    return this->Remote.template callB<EmitAndFinalize>(H); +  } + +private: + +  Expected<RemoteSymbol> lookup(ObjHandleT H, const std::string &Name) { +    auto RI = Resolvers.find(H); +    if (RI != Resolvers.end()) { +      return this->jitSymbolToRemote(RI->second->findSymbol(Name)); +    } else +      return teeLog(badObjectHandleError(H)); +  } + +  Expected<RemoteSymbol> lookupInLogicalDylib(ObjHandleT H, +                                              const std::string &Name) { +    auto RI = Resolvers.find(H); +    if (RI != Resolvers.end()) +      return this->jitSymbolToRemote( +               RI->second->findSymbolInLogicalDylib(Name)); +    else +      return teeLog(badObjectHandleError(H)); +  } + +  std::map<remote::ResourceIdMgr::ResourceId, +           std::shared_ptr<JITSymbolResolver>> Resolvers; +}; + +/// RemoteObjectServerLayer acts as a server and handling RPC calls for the +/// object layer API from the given RPC connection. +/// +/// This class can be composed on top of a 'real' object layer (e.g. +/// RTDyldObjectLinkingLayer) to do the actual work of relocating objects +/// and making them executable. +template <typename BaseLayerT, typename RPCEndpoint> +class RemoteObjectServerLayer : public RemoteObjectLayer<RPCEndpoint> { +private: + +  using ObjHandleT = RemoteObjectLayerAPI::ObjHandleT; +  using RemoteSymbol = RemoteObjectLayerAPI::RemoteSymbol; + +  using AddObject = RemoteObjectLayerAPI::AddObject; +  using RemoveObject = RemoteObjectLayerAPI::RemoveObject; +  using FindSymbol = RemoteObjectLayerAPI::FindSymbol; +  using FindSymbolIn = RemoteObjectLayerAPI::FindSymbolIn; +  using EmitAndFinalize = RemoteObjectLayerAPI::EmitAndFinalize; +  using Lookup = RemoteObjectLayerAPI::Lookup; +  using LookupInLogicalDylib = RemoteObjectLayerAPI::LookupInLogicalDylib; + +  using RemoteObjectLayer<RPCEndpoint>::teeLog; +  using RemoteObjectLayer<RPCEndpoint>::badObjectHandleError; +  using RemoteObjectLayer<RPCEndpoint>::remoteToJITSymbol; + +public: + +  /// Create a RemoteObjectServerLayer with the given base layer (which must be +  /// an object layer), RPC endpoint, and error reporter function. +  RemoteObjectServerLayer(BaseLayerT &BaseLayer, +                          RPCEndpoint &Remote, +                          std::function<void(Error)> ReportError) +    : RemoteObjectLayer<RPCEndpoint>(Remote, std::move(ReportError)), +      BaseLayer(BaseLayer), HandleIdMgr(1) { +    using ThisT = RemoteObjectServerLayer<BaseLayerT, RPCEndpoint>; + +    Remote.template addHandler<AddObject>(*this, &ThisT::addObject); +    Remote.template addHandler<RemoveObject>(*this, &ThisT::removeObject); +    Remote.template addHandler<FindSymbol>(*this, &ThisT::findSymbol); +    Remote.template addHandler<FindSymbolIn>(*this, &ThisT::findSymbolIn); +    Remote.template addHandler<EmitAndFinalize>(*this, &ThisT::emitAndFinalize); +  } + +private: + +  class StringMemoryBuffer : public MemoryBuffer { +  public: +    StringMemoryBuffer(std::string Buffer) +      : Buffer(std::move(Buffer)) { +      init(this->Buffer.data(), this->Buffer.data() + this->Buffer.size(), +           false); +    } + +    BufferKind getBufferKind() const override { return MemoryBuffer_Malloc; } +  private: +    std::string Buffer; +  }; + +  JITSymbol lookup(ObjHandleT Id, const std::string &Name) { +    return remoteToJITSymbol( +             this->Remote.template callB<Lookup>(Id, Name)); +  } + +  JITSymbol lookupInLogicalDylib(ObjHandleT Id, const std::string &Name) { +    return remoteToJITSymbol( +             this->Remote.template callB<LookupInLogicalDylib>(Id, Name)); +  } + +  Expected<ObjHandleT> addObject(std::string ObjBuffer) { +    auto Buffer = llvm::make_unique<StringMemoryBuffer>(std::move(ObjBuffer)); +    if (auto ObjectOrErr = +          object::ObjectFile::createObjectFile(Buffer->getMemBufferRef())) { +      auto Object = +        std::make_shared<object::OwningBinary<object::ObjectFile>>( +          std::move(*ObjectOrErr), std::move(Buffer)); + +      auto Id = HandleIdMgr.getNext(); +      assert(!BaseLayerHandles.count(Id) && "Id already in use?"); + +      auto Resolver = +        createLambdaResolver( +          [this, Id](const std::string &Name) { return lookup(Id, Name); }, +          [this, Id](const std::string &Name) { +            return lookupInLogicalDylib(Id, Name); +          }); + +      if (auto HandleOrErr = +          BaseLayer.addObject(std::move(Object), std::move(Resolver))) { +        BaseLayerHandles[Id] = std::move(*HandleOrErr); +        return Id; +      } else +        return teeLog(HandleOrErr.takeError()); +    } else +      return teeLog(ObjectOrErr.takeError()); +  } + +  Error removeObject(ObjHandleT H) { +    auto HI = BaseLayerHandles.find(H); +    if (HI != BaseLayerHandles.end()) { +      if (auto Err = BaseLayer.removeObject(HI->second)) +        return teeLog(std::move(Err)); +      return Error::success(); +    } else +      return teeLog(badObjectHandleError(H)); +  } + +  Expected<RemoteSymbol> findSymbol(const std::string &Name, +                                    bool ExportedSymbolsOnly) { +    if (auto Sym = BaseLayer.findSymbol(Name, ExportedSymbolsOnly)) +      return this->jitSymbolToRemote(std::move(Sym)); +    else if (auto Err = Sym.takeError()) +      return teeLog(std::move(Err)); +    return this->nullRemoteSymbol(); +  } + +  Expected<RemoteSymbol> findSymbolIn(ObjHandleT H, const std::string &Name, +                                      bool ExportedSymbolsOnly) { +    auto HI = BaseLayerHandles.find(H); +    if (HI != BaseLayerHandles.end()) { +      if (auto Sym = BaseLayer.findSymbolIn(HI->second, Name, ExportedSymbolsOnly)) +        return this->jitSymbolToRemote(std::move(Sym)); +      else if (auto Err = Sym.takeError()) +        return teeLog(std::move(Err)); +      return this->nullRemoteSymbol(); +    } else +      return teeLog(badObjectHandleError(H)); +  } + +  Error emitAndFinalize(ObjHandleT H) { +    auto HI = BaseLayerHandles.find(H); +    if (HI != BaseLayerHandles.end()) { +      if (auto Err = BaseLayer.emitAndFinalize(HI->second)) +        return teeLog(std::move(Err)); +      return Error::success(); +    } else +      return teeLog(badObjectHandleError(H)); +  } + +  BaseLayerT &BaseLayer; +  remote::ResourceIdMgr HandleIdMgr; +  std::map<ObjHandleT, typename BaseLayerT::ObjHandleT> BaseLayerHandles; +}; + +} // end namespace orc +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_REMOTEOBJECTLAYER_H | 
