[ORC] Add a pair of ORC layers that forward object-layer operations via RPC.

This patch introduces RemoteObjectClientLayer and RemoteObjectServerLayer,
which can be used to forward ORC object-layer operations from a JIT stack in
the client to a JIT stack (consisting only of object-layers) in the server.

This is a new way to support remote-JITing in LLVM. The previous approach
(supported by OrcRemoteTargetClient and OrcRemoteTargetServer) used a
remote-mapping memory manager that sat "beneath" the JIT stack and sent
fully-relocated binary blobs to the server. The main advantage of the new
approach is that relocatable objects can be cached on the server and re-used
(if the code that they represent hasn't changed), whereas fully-relocated blobs
can not (since the addresses they have been permanently bound to will change
from run to run).

llvm-svn: 312511
This commit is contained in:
Lang Hames 2017-09-05 03:34:09 +00:00
parent f9c9455d3f
commit 617fc35637
7 changed files with 1142 additions and 1 deletions

View File

@ -89,9 +89,15 @@ public:
/// @brief Implicitly convert to the underlying flags type.
operator UnderlyingType&() { return Flags; }
/// @brief Implicitly convert to the underlying flags type.
operator const UnderlyingType&() const { return Flags; }
/// @brief Return a reference to the target-specific flags.
TargetFlagsType& getTargetFlags() { return TargetFlags; }
/// @brief Return a reference to the target-specific flags.
const TargetFlagsType& getTargetFlags() const { return TargetFlags; }
/// Construct a JITSymbolFlags value based on the flags of the given global
/// value.
static JITSymbolFlags fromGlobalValue(const GlobalValue &GV);

View File

@ -33,7 +33,8 @@ enum class OrcErrorCode : int {
RPCResponseAbandoned,
UnexpectedRPCCall,
UnexpectedRPCResponse,
UnknownErrorCodeFromRemote
UnknownErrorCodeFromRemote,
UnknownResourceHandle
};
std::error_code orcError(OrcErrorCode ErrCode);

View File

@ -25,6 +25,37 @@ namespace orc {
namespace remote {
/// Template error for missing resources.
template <typename ResourceIdT>
class ResourceNotFound
: public ErrorInfo<ResourceNotFound<ResourceIdT>> {
public:
static char ID;
ResourceNotFound(ResourceIdT ResourceId,
std::string ResourceDescription = "")
: ResourceId(std::move(ResourceId)),
ResourceDescription(std::move(ResourceDescription)) {}
std::error_code convertToErrorCode() const override {
return orcError(OrcErrorCode::UnknownResourceHandle);
}
void log(raw_ostream &OS) const override {
OS << (ResourceDescription.empty()
? "Remote resource with id "
: ResourceDescription)
<< " " << ResourceId << " not found";
}
private:
ResourceIdT ResourceId;
std::string ResourceDescription;
};
template <typename ResourceIdT>
char ResourceNotFound<ResourceIdT>::ID = 0;
class DirectBufferWriter {
public:
DirectBufferWriter() = default;
@ -45,6 +76,32 @@ private:
namespace rpc {
template <>
class RPCTypeName<JITSymbolFlags> {
public:
static const char *getName() { return "JITSymbolFlags"; }
};
template <typename ChannelT>
class SerializationTraits<ChannelT, JITSymbolFlags> {
public:
static Error serialize(ChannelT &C, const JITSymbolFlags &Flags) {
return serializeSeq(C, static_cast<JITSymbolFlags::UnderlyingType>(Flags),
Flags.getTargetFlags());
}
static Error deserialize(ChannelT &C, JITSymbolFlags &Flags) {
JITSymbolFlags::UnderlyingType JITFlags;
JITSymbolFlags::TargetFlagsType TargetFlags;
if (auto Err = deserializeSeq(C, JITFlags, TargetFlags))
return Err;
Flags = JITSymbolFlags(static_cast<JITSymbolFlags::FlagNames>(JITFlags),
TargetFlags);
return Error::success();
}
};
template <> class RPCTypeName<remote::DirectBufferWriter> {
public:
static const char *getName() { return "DirectBufferWriter"; }

View File

@ -0,0 +1,498 @@
//===------ 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:
class RemoteSymbolMaterializer {
public:
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;
~RemoteSymbolMaterializer() {
if (Id)
C.releaseRemoteSymbol(Id);
}
Expected<JITTargetAddress> materialize() {
auto Addr = C.materializeRemoteSymbol(Id);
Id = 0;
return Addr;
}
private:
RemoteObjectLayer &C;
RemoteSymbolId Id;
};
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");
}
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();
}
JITSymbol remoteToJITSymbol(Expected<RemoteSymbol> RemoteSymOrErr) {
if (RemoteSymOrErr) {
auto &RemoteSym = *RemoteSymOrErr;
RemoteSymbolMaterializer RSM(*this, RemoteSym.first);
auto Sym =
JITSymbol([RSM]() mutable { return RSM.materialize(); },
RemoteSym.second);
return Sym;
} else
return RemoteSymOrErr.takeError();
}
template <typename Func, typename... ArgTs>
using CallBResult = decltype(foldRemoteERror(
std::declval<RPCEndpoint>()
.template callB<Func>(
std::declval<const ArgTs&>()...)));
/// API checked callB function.
template <typename Func, typename... ArgTs>
CallBResult<Func> callB(const ArgTs &... Args) {
return foldRemoteError(Remote.template callB<Func>(Args...));
}
RPCEndpoint &Remote;
std::function<void(Error)> ReportError;
private:
void releaseRemoteSymbol(RemoteSymbolId Id) {
if (auto Err = Remote.template callB<ReleaseRemoteSymbol>(Id))
ReportError(std::move(Err));
}
Expected<JITTargetAddress> materializeRemoteSymbol(RemoteSymbolId Id) {
return Remote.template callB<MaterializeRemoteSymbol>(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));
}
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;
};
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>>;
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);
}
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();
}
Error removeObject(ObjHandleT H) {
return this->Remote.template callB<RemoveObject>(H);
}
JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) {
return remoteToJITSymbol(
this->Remote.template callB<FindSymbol>(Name,
ExportedSymbolsOnly));
}
JITSymbol findSymbolIn(ObjHandleT H, StringRef Name, bool ExportedSymbolsOnly) {
return remoteToJITSymbol(
this->Remote.template callB<FindSymbolIn>(H, Name,
ExportedSymbolsOnly));
}
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;
};
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:
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

View File

@ -54,6 +54,8 @@ public:
case OrcErrorCode::UnknownErrorCodeFromRemote:
return "Unknown error returned from remote RPC function "
"(Use StringError to get error message)";
case OrcErrorCode::UnknownResourceHandle:
return "Unknown resource handle";
}
llvm_unreachable("Unhandled error code");
}

View File

@ -18,6 +18,7 @@ add_llvm_unittest(OrcJITTests
OrcCAPITest.cpp
OrcTestCommon.cpp
QueueChannel.cpp
RemoteObjectLayerTest.cpp
RPCUtilsTest.cpp
RTDyldObjectLinkingLayerTest.cpp
)

View File

@ -0,0 +1,576 @@
//===---------------------- RemoteObjectLayerTest.cpp ---------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/ExecutionEngine/Orc/NullResolver.h"
#include "llvm/ExecutionEngine/Orc/RemoteObjectLayer.h"
#include "OrcTestCommon.h"
#include "QueueChannel.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace llvm::orc;
namespace {
class MockObjectLayer {
public:
using ObjHandleT = uint64_t;
using ObjectPtr =
std::shared_ptr<object::OwningBinary<object::ObjectFile>>;
using LookupFn = std::function<JITSymbol(StringRef, bool)>;
using SymbolLookupTable = std::map<ObjHandleT, LookupFn>;
using AddObjectFtor =
std::function<Expected<ObjHandleT>(ObjectPtr, SymbolLookupTable&)>;
class ObjectNotFound : public remote::ResourceNotFound<ObjHandleT> {
public:
ObjectNotFound(ObjHandleT H) : ResourceNotFound(H, "Object handle") {}
};
MockObjectLayer(AddObjectFtor AddObject)
: AddObject(std::move(AddObject)) {}
Expected<ObjHandleT> addObject(ObjectPtr Obj,
std::shared_ptr<JITSymbolResolver> Resolver) {
return AddObject(Obj, SymTab);
}
Error removeObject(ObjHandleT H) {
if (SymTab.count(H))
return Error::success();
else
return make_error<ObjectNotFound>(H);
}
JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) {
for (auto KV : SymTab) {
if (auto Sym = KV.second(Name, ExportedSymbolsOnly))
return Sym;
else if (auto Err = Sym.takeError())
return std::move(Err);
}
return JITSymbol(nullptr);
}
JITSymbol findSymbolIn(ObjHandleT H, StringRef Name,
bool ExportedSymbolsOnly) {
auto LI = SymTab.find(H);
if (LI != SymTab.end())
return LI->second(Name, ExportedSymbolsOnly);
else
return make_error<ObjectNotFound>(H);
}
Error emitAndFinalize(ObjHandleT H) {
if (SymTab.count(H))
return Error::success();
else
return make_error<ObjectNotFound>(H);
}
private:
AddObjectFtor AddObject;
SymbolLookupTable SymTab;
};
using RPCEndpoint = rpc::SingleThreadedRPCEndpoint<rpc::RawByteChannel>;
MockObjectLayer::ObjectPtr createTestObject() {
OrcNativeTarget::initialize();
auto TM = std::unique_ptr<TargetMachine>(EngineBuilder().selectTarget());
if (!TM)
return nullptr;
LLVMContext Ctx;
ModuleBuilder MB(Ctx, TM->getTargetTriple().str(), "TestModule");
MB.getModule()->setDataLayout(TM->createDataLayout());
auto *Main = MB.createFunctionDecl<void(int, char**)>("main");
Main->getBasicBlockList().push_back(BasicBlock::Create(Ctx));
IRBuilder<> B(&Main->back());
B.CreateRet(ConstantInt::getSigned(Type::getInt32Ty(Ctx), 42));
SimpleCompiler IRCompiler(*TM);
return std::make_shared<object::OwningBinary<object::ObjectFile>>(
IRCompiler(*MB.getModule()));
}
TEST(RemoteObjectLayer, AddObject) {
llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
auto TestObject = createTestObject();
if (!TestObject)
return;
auto Channels = createPairedQueueChannels();
auto ReportError =
[](Error Err) {
logAllUnhandledErrors(std::move(Err), llvm::errs(), "");
};
// Copy the bytes out of the test object: the copy will be used to verify
// that the original is correctly transmitted over RPC to the mock layer.
StringRef ObjBytes = TestObject->getBinary()->getData();
std::vector<char> ObjContents(ObjBytes.size());
std::copy(ObjBytes.begin(), ObjBytes.end(), ObjContents.begin());
RPCEndpoint ClientEP(*Channels.first, true);
RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
RPCEndpoint ServerEP(*Channels.second, true);
MockObjectLayer BaseLayer(
[&ObjContents](MockObjectLayer::ObjectPtr Obj,
MockObjectLayer::SymbolLookupTable &SymTab) {
// Check that the received object file content matches the original.
StringRef RPCObjContents = Obj->getBinary()->getData();
EXPECT_EQ(RPCObjContents.size(), ObjContents.size())
<< "RPC'd object file has incorrect size";
EXPECT_TRUE(std::equal(RPCObjContents.begin(), RPCObjContents.end(),
ObjContents.begin()))
<< "RPC'd object file content does not match original content";
return 1;
});
RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
ServerEP,
ReportError);
bool Finished = false;
ServerEP.addHandler<remote::utils::TerminateSession>(
[&]() { Finished = true; }
);
auto ServerThread =
std::thread([&]() {
while (!Finished)
cantFail(ServerEP.handleOne());
});
cantFail(Client.addObject(std::move(TestObject),
std::make_shared<NullResolver>()));
cantFail(ClientEP.callB<remote::utils::TerminateSession>());
ServerThread.join();
}
TEST(RemoteObjectLayer, AddObjectFailure) {
llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
auto TestObject = createTestObject();
if (!TestObject)
return;
auto Channels = createPairedQueueChannels();
auto ReportError =
[](Error Err) {
auto ErrMsg = toString(std::move(Err));
EXPECT_EQ(ErrMsg, "AddObjectFailure - Test Message")
<< "Expected error string to be \"AddObjectFailure - Test Message\"";
};
RPCEndpoint ClientEP(*Channels.first, true);
RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
RPCEndpoint ServerEP(*Channels.second, true);
MockObjectLayer BaseLayer(
[](MockObjectLayer::ObjectPtr Obj,
MockObjectLayer::SymbolLookupTable &SymTab)
-> Expected<MockObjectLayer::ObjHandleT> {
return make_error<StringError>("AddObjectFailure - Test Message",
inconvertibleErrorCode());
});
RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
ServerEP,
ReportError);
bool Finished = false;
ServerEP.addHandler<remote::utils::TerminateSession>(
[&]() { Finished = true; }
);
auto ServerThread =
std::thread([&]() {
while (!Finished)
cantFail(ServerEP.handleOne());
});
auto HandleOrErr =
Client.addObject(std::move(TestObject), std::make_shared<NullResolver>());
EXPECT_FALSE(HandleOrErr) << "Expected error from addObject";
auto ErrMsg = toString(HandleOrErr.takeError());
EXPECT_EQ(ErrMsg, "AddObjectFailure - Test Message")
<< "Expected error string to be \"AddObjectFailure - Test Message\"";
cantFail(ClientEP.callB<remote::utils::TerminateSession>());
ServerThread.join();
}
TEST(RemoteObjectLayer, RemoveObject) {
llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
auto TestObject = createTestObject();
if (!TestObject)
return;
auto Channels = createPairedQueueChannels();
auto ReportError =
[](Error Err) {
logAllUnhandledErrors(std::move(Err), llvm::errs(), "");
};
RPCEndpoint ClientEP(*Channels.first, true);
RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
RPCEndpoint ServerEP(*Channels.second, true);
MockObjectLayer BaseLayer(
[](MockObjectLayer::ObjectPtr Obj,
MockObjectLayer::SymbolLookupTable &SymTab) {
SymTab[1] = MockObjectLayer::LookupFn();
return 1;
});
RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
ServerEP,
ReportError);
bool Finished = false;
ServerEP.addHandler<remote::utils::TerminateSession>(
[&]() { Finished = true; }
);
auto ServerThread =
std::thread([&]() {
while (!Finished)
cantFail(ServerEP.handleOne());
});
auto H = cantFail(Client.addObject(std::move(TestObject),
std::make_shared<NullResolver>()));
cantFail(Client.removeObject(H));
cantFail(ClientEP.callB<remote::utils::TerminateSession>());
ServerThread.join();
}
TEST(RemoteObjectLayer, RemoveObjectFailure) {
llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
auto TestObject = createTestObject();
if (!TestObject)
return;
auto Channels = createPairedQueueChannels();
auto ReportError =
[](Error Err) {
auto ErrMsg = toString(std::move(Err));
EXPECT_EQ(ErrMsg, "Object handle 42 not found")
<< "Expected error string to be \"Object handle 42 not found\"";
};
RPCEndpoint ClientEP(*Channels.first, true);
RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
RPCEndpoint ServerEP(*Channels.second, true);
// AddObject lambda does not update symbol table, so removeObject will treat
// this as a bad object handle.
MockObjectLayer BaseLayer(
[](MockObjectLayer::ObjectPtr Obj,
MockObjectLayer::SymbolLookupTable &SymTab) {
return 42;
});
RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
ServerEP,
ReportError);
bool Finished = false;
ServerEP.addHandler<remote::utils::TerminateSession>(
[&]() { Finished = true; }
);
auto ServerThread =
std::thread([&]() {
while (!Finished)
cantFail(ServerEP.handleOne());
});
auto H = cantFail(Client.addObject(std::move(TestObject),
std::make_shared<NullResolver>()));
auto Err = Client.removeObject(H);
EXPECT_TRUE(!!Err) << "Expected error from removeObject";
auto ErrMsg = toString(std::move(Err));
EXPECT_EQ(ErrMsg, "Object handle 42 not found")
<< "Expected error string to be \"Object handle 42 not found\"";
cantFail(ClientEP.callB<remote::utils::TerminateSession>());
ServerThread.join();
}
TEST(RemoteObjectLayer, FindSymbol) {
llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
auto TestObject = createTestObject();
if (!TestObject)
return;
auto Channels = createPairedQueueChannels();
auto ReportError =
[](Error Err) {
auto ErrMsg = toString(std::move(Err));
EXPECT_EQ(ErrMsg, "Could not find symbol 'barbaz'")
<< "Expected error string to be \"Object handle 42 not found\"";
};
RPCEndpoint ClientEP(*Channels.first, true);
RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
RPCEndpoint ServerEP(*Channels.second, true);
// AddObject lambda does not update symbol table, so removeObject will treat
// this as a bad object handle.
MockObjectLayer BaseLayer(
[](MockObjectLayer::ObjectPtr Obj,
MockObjectLayer::SymbolLookupTable &SymTab) {
SymTab[42] =
[](StringRef Name, bool ExportedSymbolsOnly) -> JITSymbol {
if (Name == "foobar")
return JITSymbol(0x12348765, JITSymbolFlags::Exported);
return make_error<JITSymbolNotFound>(Name);
};
return 42;
});
RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
ServerEP,
ReportError);
bool Finished = false;
ServerEP.addHandler<remote::utils::TerminateSession>(
[&]() { Finished = true; }
);
auto ServerThread =
std::thread([&]() {
while (!Finished)
cantFail(ServerEP.handleOne());
});
cantFail(Client.addObject(std::move(TestObject),
std::make_shared<NullResolver>()));
auto Sym1 = Client.findSymbol("foobar", true);
EXPECT_TRUE(!!Sym1) << "Symbol 'foobar' should be findable";
EXPECT_EQ(cantFail(Sym1.getAddress()), 0x12348765ULL)
<< "Symbol 'foobar' does not return the correct address";
auto Sym2 = Client.findSymbol("barbaz", true);
EXPECT_FALSE(!!Sym2) << "Symbol 'barbaz' should not be findable";
auto Err = Sym2.takeError();
EXPECT_TRUE(!!Err) << "Sym2 should contain an error value";
auto ErrMsg = toString(std::move(Err));
EXPECT_EQ(ErrMsg, "Could not find symbol 'barbaz'")
<< "Expected symbol-not-found error for Sym2";
cantFail(ClientEP.callB<remote::utils::TerminateSession>());
ServerThread.join();
}
TEST(RemoteObjectLayer, FindSymbolIn) {
llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
auto TestObject = createTestObject();
if (!TestObject)
return;
auto Channels = createPairedQueueChannels();
auto ReportError =
[](Error Err) {
auto ErrMsg = toString(std::move(Err));
EXPECT_EQ(ErrMsg, "Could not find symbol 'barbaz'")
<< "Expected error string to be \"Object handle 42 not found\"";
};
RPCEndpoint ClientEP(*Channels.first, true);
RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
RPCEndpoint ServerEP(*Channels.second, true);
// AddObject lambda does not update symbol table, so removeObject will treat
// this as a bad object handle.
MockObjectLayer BaseLayer(
[](MockObjectLayer::ObjectPtr Obj,
MockObjectLayer::SymbolLookupTable &SymTab) {
SymTab[42] =
[](StringRef Name, bool ExportedSymbolsOnly) -> JITSymbol {
if (Name == "foobar")
return JITSymbol(0x12348765, JITSymbolFlags::Exported);
return make_error<JITSymbolNotFound>(Name);
};
// Dummy symbol table entry - this should not be visible to
// findSymbolIn.
SymTab[43] =
[](StringRef Name, bool ExportedSymbolsOnly) -> JITSymbol {
if (Name == "barbaz")
return JITSymbol(0xdeadbeef, JITSymbolFlags::Exported);
return make_error<JITSymbolNotFound>(Name);
};
return 42;
});
RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
ServerEP,
ReportError);
bool Finished = false;
ServerEP.addHandler<remote::utils::TerminateSession>(
[&]() { Finished = true; }
);
auto ServerThread =
std::thread([&]() {
while (!Finished)
cantFail(ServerEP.handleOne());
});
auto H = cantFail(Client.addObject(std::move(TestObject),
std::make_shared<NullResolver>()));
auto Sym1 = Client.findSymbolIn(H, "foobar", true);
EXPECT_TRUE(!!Sym1) << "Symbol 'foobar' should be findable";
EXPECT_EQ(cantFail(Sym1.getAddress()), 0x12348765ULL)
<< "Symbol 'foobar' does not return the correct address";
auto Sym2 = Client.findSymbolIn(H, "barbaz", true);
EXPECT_FALSE(!!Sym2) << "Symbol 'barbaz' should not be findable";
auto Err = Sym2.takeError();
EXPECT_TRUE(!!Err) << "Sym2 should contain an error value";
auto ErrMsg = toString(std::move(Err));
EXPECT_EQ(ErrMsg, "Could not find symbol 'barbaz'")
<< "Expected symbol-not-found error for Sym2";
cantFail(ClientEP.callB<remote::utils::TerminateSession>());
ServerThread.join();
}
TEST(RemoteObjectLayer, EmitAndFinalize) {
llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
auto TestObject = createTestObject();
if (!TestObject)
return;
auto Channels = createPairedQueueChannels();
auto ReportError =
[](Error Err) {
logAllUnhandledErrors(std::move(Err), llvm::errs(), "");
};
RPCEndpoint ClientEP(*Channels.first, true);
RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
RPCEndpoint ServerEP(*Channels.second, true);
MockObjectLayer BaseLayer(
[](MockObjectLayer::ObjectPtr Obj,
MockObjectLayer::SymbolLookupTable &SymTab) {
SymTab[1] = MockObjectLayer::LookupFn();
return 1;
});
RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
ServerEP,
ReportError);
bool Finished = false;
ServerEP.addHandler<remote::utils::TerminateSession>(
[&]() { Finished = true; }
);
auto ServerThread =
std::thread([&]() {
while (!Finished)
cantFail(ServerEP.handleOne());
});
auto H = cantFail(Client.addObject(std::move(TestObject),
std::make_shared<NullResolver>()));
auto Err = Client.emitAndFinalize(H);
EXPECT_FALSE(!!Err) << "emitAndFinalize should work";
cantFail(ClientEP.callB<remote::utils::TerminateSession>());
ServerThread.join();
}
TEST(RemoteObjectLayer, EmitAndFinalizeFailure) {
llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
auto TestObject = createTestObject();
if (!TestObject)
return;
auto Channels = createPairedQueueChannels();
auto ReportError =
[](Error Err) {
auto ErrMsg = toString(std::move(Err));
EXPECT_EQ(ErrMsg, "Object handle 1 not found")
<< "Expected bad handle error";
};
RPCEndpoint ClientEP(*Channels.first, true);
RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
RPCEndpoint ServerEP(*Channels.second, true);
MockObjectLayer BaseLayer(
[](MockObjectLayer::ObjectPtr Obj,
MockObjectLayer::SymbolLookupTable &SymTab) {
return 1;
});
RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
ServerEP,
ReportError);
bool Finished = false;
ServerEP.addHandler<remote::utils::TerminateSession>(
[&]() { Finished = true; }
);
auto ServerThread =
std::thread([&]() {
while (!Finished)
cantFail(ServerEP.handleOne());
});
auto H = cantFail(Client.addObject(std::move(TestObject),
std::make_shared<NullResolver>()));
auto Err = Client.emitAndFinalize(H);
EXPECT_TRUE(!!Err) << "emitAndFinalize should work";
auto ErrMsg = toString(std::move(Err));
EXPECT_EQ(ErrMsg, "Object handle 1 not found")
<< "emitAndFinalize returned incorrect error";
cantFail(ClientEP.callB<remote::utils::TerminateSession>());
ServerThread.join();
}
}