//===------ 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 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; public: using BadSymbolHandleError = remote::ResourceNotFound; using BadObjectHandleError = remote::ResourceNotFound; protected: static const ObjHandleT InvalidObjectHandleId = 0; static const RemoteSymbolId NullSymbolId = 0; class AddObject : public rpc::Function(std::string)> { public: static const char *getName() { return "AddObject"; } }; class RemoveObject : public rpc::Function { public: static const char *getName() { return "RemoveObject"; } }; class FindSymbol : public rpc::Function(std::string, bool)> { public: static const char *getName() { return "FindSymbol"; } }; class FindSymbolIn : public rpc::Function(ObjHandleT, std::string, bool)> { public: static const char *getName() { return "FindSymbolIn"; } }; class EmitAndFinalize : public rpc::Function { public: static const char *getName() { return "EmitAndFinalize"; } }; class Lookup : public rpc::Function(ObjHandleT, std::string)> { public: static const char *getName() { return "Lookup"; } }; class LookupInLogicalDylib : public rpc::Function(ObjHandleT, std::string)> { public: static const char *getName() { return "LookupInLogicalDylib"; } }; class ReleaseRemoteSymbol : public rpc::Function { public: static const char *getName() { return "ReleaseRemoteSymbol"; } }; class MaterializeRemoteSymbol : public rpc::Function(RemoteSymbolId)> { public: static const char *getName() { return "MaterializeRemoteSymbol"; } }; }; /// Base class containing common utilities for RemoteObjectClientLayer and /// RemoteObjectServerLayer. template class RemoteObjectLayer : public RemoteObjectLayerAPI { public: RemoteObjectLayer(RPCEndpoint &Remote, std::function ReportError) : Remote(Remote), ReportError(std::move(ReportError)), SymbolIdMgr(NullSymbolId + 1) { using ThisT = RemoteObjectLayer; Remote.template addHandler( *this, &ThisT::handleReleaseRemoteSymbol); Remote.template addHandler( *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(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 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 EIB) { ReportError(make_error( EIB->message(), EIB->convertToErrorCode())); return Error(std::move(EIB)); }); } Error badRemoteSymbolIdError(RemoteSymbolId Id) { return make_error(Id, "Remote JIT Symbol"); } Error badObjectHandleError(ObjHandleT H) { return make_error( H, "Bad object handle"); } /// Create a RemoteSymbol wrapping the given JITSymbol. Expected 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 to a JITSymbol. JITSymbol remoteToJITSymbol(Expected 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 ReportError; private: /// Notify the remote to release the given JITSymbol. void releaseRemoteSymbol(RemoteSymbolId Id) { if (auto Err = Remote.template callB(Id)) ReportError(std::move(Err)); } /// Notify the remote to materialize the JITSymbol with the given Id and /// return its address. Expected materializeRemoteSymbol(RemoteSymbolId Id) { return Remote.template callB(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 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 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 class RemoteObjectClientLayer : public RemoteObjectLayer { 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::teeLog; using RemoteObjectLayer::badObjectHandleError; using RemoteObjectLayer::remoteToJITSymbol; public: using ObjHandleT = RemoteObjectLayerAPI::ObjHandleT; using RemoteSymbol = RemoteObjectLayerAPI::RemoteSymbol; using ObjectPtr = std::unique_ptr; /// 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 ReportError) : RemoteObjectLayer(Remote, std::move(ReportError)) { using ThisT = RemoteObjectClientLayer; Remote.template addHandler(*this, &ThisT::lookup); Remote.template addHandler( *this, &ThisT::lookupInLogicalDylib); } /// 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 addObject(ObjectPtr ObjBuffer, std::shared_ptr Resolver) { if (auto HandleOrErr = this->Remote.template callB(ObjBuffer->getBuffer())) { 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(); } /// Remove the given object from the JIT. Error removeObject(ObjHandleT H) { return this->Remote.template callB(H); } /// Search for the given named symbol. JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) { return remoteToJITSymbol( this->Remote.template callB(Name, ExportedSymbolsOnly)); } /// Search for the given named symbol within the given context. JITSymbol findSymbolIn(ObjHandleT H, StringRef Name, bool ExportedSymbolsOnly) { return remoteToJITSymbol( this->Remote.template callB(H, Name, ExportedSymbolsOnly)); } /// Immediately emit and finalize the object with the given handle. Error emitAndFinalize(ObjHandleT H) { return this->Remote.template callB(H); } private: Expected 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 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> 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 class RemoteObjectServerLayer : public RemoteObjectLayer { 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::teeLog; using RemoteObjectLayer::badObjectHandleError; using RemoteObjectLayer::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 ReportError) : RemoteObjectLayer(Remote, std::move(ReportError)), BaseLayer(BaseLayer), HandleIdMgr(1) { using ThisT = RemoteObjectServerLayer; Remote.template addHandler(*this, &ThisT::addObject); Remote.template addHandler(*this, &ThisT::removeObject); Remote.template addHandler(*this, &ThisT::findSymbol); Remote.template addHandler(*this, &ThisT::findSymbolIn); Remote.template addHandler(*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(Id, Name)); } JITSymbol lookupInLogicalDylib(ObjHandleT Id, const std::string &Name) { return remoteToJITSymbol( this->Remote.template callB(Id, Name)); } Expected addObject(std::string ObjBuffer) { auto Buffer = llvm::make_unique(std::move(ObjBuffer)); 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(Buffer), std::move(Resolver))) { BaseLayerHandles[Id] = std::move(*HandleOrErr); return Id; } else return teeLog(HandleOrErr.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 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 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 BaseLayerHandles; }; } // end namespace orc } // end namespace llvm #endif // LLVM_EXECUTIONENGINE_ORC_REMOTEOBJECTLAYER_H