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 0000000000000..17255954a99f4 --- /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 |