mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 20:51:52 +01:00
[ORC] Add a "lazy call-through" utility based on the same underlying trampoline
implementation as lazy compile callbacks, and a "lazy re-exports" utility that builds lazy call-throughs. Lazy call-throughs are similar to lazy compile callbacks (and are based on the same underlying state saving/restoring trampolines) but resolve their targets by performing a standard ORC lookup rather than invoking a user supplied compiler callback. This allows them to inherit the thread-safety of ORC lookups while blocking only the calling thread (whereas compile callbacks also block one compile thread). Lazy re-exports provide a simple way of building lazy call-throughs. Unlike a regular re-export, a lazy re-export generates a new address (a stub entry point) that will act like the re-exported symbol when called. The first call via a lazy re-export will trigger compilation of the re-exported symbol before calling through to it. llvm-svn: 343061
This commit is contained in:
parent
4a600aef42
commit
310a62559f
191
include/llvm/ExecutionEngine/Orc/LazyReexports.h
Normal file
191
include/llvm/ExecutionEngine/Orc/LazyReexports.h
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
//===------ LazyReexports.h -- Utilities for lazy reexports -----*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Lazy re-exports are similar to normal re-exports, except that for callable
|
||||||
|
// symbols the definitions are replaced with trampolines that will look up and
|
||||||
|
// call through to the re-exported symbol at runtime. This can be used to
|
||||||
|
// enable lazy compilation.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
|
||||||
|
#define LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
|
||||||
|
|
||||||
|
#include "llvm/ExecutionEngine/Orc/Core.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
|
||||||
|
class Triple;
|
||||||
|
|
||||||
|
namespace orc {
|
||||||
|
|
||||||
|
/// Manages a set of 'lazy call-through' trampolines. These are compiler
|
||||||
|
/// re-entry trampolines that are pre-bound to look up a given symbol in a given
|
||||||
|
/// JITDylib, then jump to that address. Since compilation of symbols is
|
||||||
|
/// triggered on first lookup, these call-through trampolines can be used to
|
||||||
|
/// implement lazy compilation.
|
||||||
|
///
|
||||||
|
/// The easiest way to construct these call-throughs is using the lazyReexport
|
||||||
|
/// function.
|
||||||
|
class LazyCallThroughManager {
|
||||||
|
public:
|
||||||
|
/// Clients will want to take some action on first resolution, e.g. updating
|
||||||
|
/// a stub pointer. Instances of this class can be used to implement this.
|
||||||
|
class NotifyResolvedFunction {
|
||||||
|
public:
|
||||||
|
virtual ~NotifyResolvedFunction() {}
|
||||||
|
|
||||||
|
/// Called the first time a lazy call through is executed and the target
|
||||||
|
/// symbol resolved.
|
||||||
|
virtual Error operator()(JITDylib &SourceJD,
|
||||||
|
const SymbolStringPtr &SymbolName,
|
||||||
|
JITTargetAddress ResolvedAddr) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void anchor();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename NotifyResolvedImpl>
|
||||||
|
class NotifyResolvedFunctionImpl : public NotifyResolvedFunction {
|
||||||
|
public:
|
||||||
|
NotifyResolvedFunctionImpl(NotifyResolvedImpl NotifyResolved)
|
||||||
|
: NotifyResolved(std::move(NotifyResolved)) {}
|
||||||
|
Error operator()(JITDylib &SourceJD, const SymbolStringPtr &SymbolName,
|
||||||
|
JITTargetAddress ResolvedAddr) {
|
||||||
|
return NotifyResolved(SourceJD, SymbolName, ResolvedAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
NotifyResolvedImpl NotifyResolved;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Create a shared NotifyResolvedFunction from a given type that is
|
||||||
|
/// callable with the correct signature.
|
||||||
|
template <typename NotifyResolvedImpl>
|
||||||
|
static std::unique_ptr<NotifyResolvedFunction>
|
||||||
|
createNotifyResolvedFunction(NotifyResolvedImpl NotifyResolved) {
|
||||||
|
return llvm::make_unique<NotifyResolvedFunctionImpl<NotifyResolvedImpl>>(
|
||||||
|
std::move(NotifyResolved));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return a free call-through trampoline and bind it to look up and call
|
||||||
|
// through to the given symbol.
|
||||||
|
Expected<JITTargetAddress> getCallThroughTrampoline(
|
||||||
|
JITDylib &SourceJD, SymbolStringPtr SymbolName,
|
||||||
|
std::shared_ptr<NotifyResolvedFunction> NotifyResolved);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
LazyCallThroughManager(ExecutionSession &ES,
|
||||||
|
JITTargetAddress ErrorHandlerAddr,
|
||||||
|
std::unique_ptr<TrampolinePool> TP);
|
||||||
|
|
||||||
|
JITTargetAddress callThroughToSymbol(JITTargetAddress TrampolineAddr);
|
||||||
|
|
||||||
|
void setTrampolinePool(std::unique_ptr<TrampolinePool> TP) {
|
||||||
|
this->TP = std::move(TP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using ReexportsMap =
|
||||||
|
std::map<JITTargetAddress, std::pair<JITDylib *, SymbolStringPtr>>;
|
||||||
|
|
||||||
|
using NotifiersMap =
|
||||||
|
std::map<JITTargetAddress, std::shared_ptr<NotifyResolvedFunction>>;
|
||||||
|
|
||||||
|
std::mutex LCTMMutex;
|
||||||
|
ExecutionSession &ES;
|
||||||
|
JITTargetAddress ErrorHandlerAddr;
|
||||||
|
std::unique_ptr<TrampolinePool> TP;
|
||||||
|
ReexportsMap Reexports;
|
||||||
|
NotifiersMap Notifiers;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A lazy call-through manager that builds trampolines in the current process.
|
||||||
|
class LocalLazyCallThroughManager : public LazyCallThroughManager {
|
||||||
|
private:
|
||||||
|
LocalLazyCallThroughManager(ExecutionSession &ES,
|
||||||
|
JITTargetAddress ErrorHandlerAddr)
|
||||||
|
: LazyCallThroughManager(ES, ErrorHandlerAddr, nullptr) {}
|
||||||
|
|
||||||
|
template <typename ORCABI> Error init() {
|
||||||
|
auto TP = LocalTrampolinePool<ORCABI>::Create(
|
||||||
|
[this](JITTargetAddress TrampolineAddr) {
|
||||||
|
return callThroughToSymbol(TrampolineAddr);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!TP)
|
||||||
|
return TP.takeError();
|
||||||
|
|
||||||
|
setTrampolinePool(std::move(*TP));
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Create a LocalLazyCallThroughManager using the given ABI. See
|
||||||
|
/// createLocalLazyCallThroughManager.
|
||||||
|
template <typename ORCABI>
|
||||||
|
static Expected<std::unique_ptr<LocalLazyCallThroughManager>>
|
||||||
|
Create(ExecutionSession &ES, JITTargetAddress ErrorHandlerAddr) {
|
||||||
|
auto LLCTM = std::unique_ptr<LocalLazyCallThroughManager>(
|
||||||
|
new LocalLazyCallThroughManager(ES, ErrorHandlerAddr));
|
||||||
|
|
||||||
|
if (auto Err = LLCTM->init<ORCABI>())
|
||||||
|
return std::move(Err);
|
||||||
|
|
||||||
|
return std::move(LLCTM);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Create a LocalLazyCallThroughManager from the given triple and execution
|
||||||
|
/// session.
|
||||||
|
Expected<std::unique_ptr<LazyCallThroughManager>>
|
||||||
|
createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES,
|
||||||
|
JITTargetAddress ErrorHandlerAddr);
|
||||||
|
|
||||||
|
/// A materialization unit that builds lazy re-exports. These are callable
|
||||||
|
/// entry points that call through to the given symbols.
|
||||||
|
/// Unlike a 'true' re-export, the address of the lazy re-export will not
|
||||||
|
/// match the address of the re-exported symbol, but calling it will behave
|
||||||
|
/// the same as calling the re-exported symbol.
|
||||||
|
class LazyReexportsMaterializationUnit : public MaterializationUnit {
|
||||||
|
public:
|
||||||
|
LazyReexportsMaterializationUnit(LazyCallThroughManager &LCTManager,
|
||||||
|
IndirectStubsManager &ISManager,
|
||||||
|
JITDylib &SourceJD,
|
||||||
|
SymbolAliasMap CallableAliases);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void materialize(MaterializationResponsibility R) override;
|
||||||
|
void discard(const JITDylib &JD, SymbolStringPtr Name) override;
|
||||||
|
static SymbolFlagsMap extractFlags(const SymbolAliasMap &Aliases);
|
||||||
|
|
||||||
|
LazyCallThroughManager &LCTManager;
|
||||||
|
IndirectStubsManager &ISManager;
|
||||||
|
JITDylib &SourceJD;
|
||||||
|
SymbolAliasMap CallableAliases;
|
||||||
|
std::shared_ptr<LazyCallThroughManager::NotifyResolvedFunction>
|
||||||
|
NotifyResolved;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Define lazy-reexports based on the given SymbolAliasMap. Each lazy re-export
|
||||||
|
/// is a callable symbol that will look up and dispatch to the given aliasee on
|
||||||
|
/// first call. All subsequent calls will go directly to the aliasee.
|
||||||
|
inline std::unique_ptr<LazyReexportsMaterializationUnit>
|
||||||
|
lazyReexports(LazyCallThroughManager &LCTManager,
|
||||||
|
IndirectStubsManager &ISManager, JITDylib &SourceJD,
|
||||||
|
SymbolAliasMap CallableAliases) {
|
||||||
|
return llvm::make_unique<LazyReexportsMaterializationUnit>(
|
||||||
|
LCTManager, ISManager, SourceJD, std::move(CallableAliases));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End namespace orc
|
||||||
|
} // End namespace llvm
|
||||||
|
|
||||||
|
#endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
|
@ -5,6 +5,7 @@ add_llvm_library(LLVMOrcJIT
|
|||||||
IndirectionUtils.cpp
|
IndirectionUtils.cpp
|
||||||
IRCompileLayer.cpp
|
IRCompileLayer.cpp
|
||||||
IRTransformLayer.cpp
|
IRTransformLayer.cpp
|
||||||
|
LazyReexports.cpp
|
||||||
Legacy.cpp
|
Legacy.cpp
|
||||||
Layer.cpp
|
Layer.cpp
|
||||||
LLJIT.cpp
|
LLJIT.cpp
|
||||||
|
204
lib/ExecutionEngine/Orc/LazyReexports.cpp
Normal file
204
lib/ExecutionEngine/Orc/LazyReexports.cpp
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
//===---------- LazyReexports.cpp - Utilities for lazy reexports ----------===//
|
||||||
|
//
|
||||||
|
// 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/LazyReexports.h"
|
||||||
|
|
||||||
|
#include "llvm/ADT/Triple.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/OrcABISupport.h"
|
||||||
|
|
||||||
|
#define DEBUG_TYPE "orc"
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
namespace orc {
|
||||||
|
|
||||||
|
void LazyCallThroughManager::NotifyResolvedFunction::anchor() {}
|
||||||
|
|
||||||
|
LazyCallThroughManager::LazyCallThroughManager(
|
||||||
|
ExecutionSession &ES, JITTargetAddress ErrorHandlerAddr,
|
||||||
|
std::unique_ptr<TrampolinePool> TP)
|
||||||
|
: ES(ES), ErrorHandlerAddr(ErrorHandlerAddr), TP(std::move(TP)) {}
|
||||||
|
|
||||||
|
Expected<JITTargetAddress> LazyCallThroughManager::getCallThroughTrampoline(
|
||||||
|
JITDylib &SourceJD, SymbolStringPtr SymbolName,
|
||||||
|
std::shared_ptr<NotifyResolvedFunction> NotifyResolved) {
|
||||||
|
std::lock_guard<std::mutex> Lock(LCTMMutex);
|
||||||
|
auto Trampoline = TP->getTrampoline();
|
||||||
|
|
||||||
|
if (!Trampoline)
|
||||||
|
return Trampoline.takeError();
|
||||||
|
|
||||||
|
Reexports[*Trampoline] = std::make_pair(&SourceJD, std::move(SymbolName));
|
||||||
|
Notifiers[*Trampoline] = std::move(NotifyResolved);
|
||||||
|
return *Trampoline;
|
||||||
|
}
|
||||||
|
|
||||||
|
JITTargetAddress
|
||||||
|
LazyCallThroughManager::callThroughToSymbol(JITTargetAddress TrampolineAddr) {
|
||||||
|
JITDylib *SourceJD = nullptr;
|
||||||
|
SymbolStringPtr SymbolName;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> Lock(LCTMMutex);
|
||||||
|
auto I = Reexports.find(TrampolineAddr);
|
||||||
|
if (I == Reexports.end())
|
||||||
|
return ErrorHandlerAddr;
|
||||||
|
SourceJD = I->second.first;
|
||||||
|
SymbolName = I->second.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto LookupResult =
|
||||||
|
ES.lookup({SourceJD}, {SymbolName}, NoDependenciesToRegister);
|
||||||
|
|
||||||
|
if (!LookupResult) {
|
||||||
|
ES.reportError(LookupResult.takeError());
|
||||||
|
return ErrorHandlerAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(LookupResult->size() == 1 && "Unexpected number of results");
|
||||||
|
assert(LookupResult->count(SymbolName) && "Unexpected result");
|
||||||
|
|
||||||
|
auto ResolvedAddr = LookupResult->begin()->second.getAddress();
|
||||||
|
|
||||||
|
std::shared_ptr<NotifyResolvedFunction> NotifyResolved = nullptr;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> Lock(LCTMMutex);
|
||||||
|
auto I = Notifiers.find(TrampolineAddr);
|
||||||
|
if (I != Notifiers.end()) {
|
||||||
|
NotifyResolved = I->second;
|
||||||
|
Notifiers.erase(I);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NotifyResolved) {
|
||||||
|
if (auto Err = (*NotifyResolved)(*SourceJD, SymbolName, ResolvedAddr)) {
|
||||||
|
ES.reportError(std::move(Err));
|
||||||
|
return ErrorHandlerAddr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResolvedAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<std::unique_ptr<LazyCallThroughManager>>
|
||||||
|
createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES,
|
||||||
|
JITTargetAddress ErrorHandlerAddr) {
|
||||||
|
switch (T.getArch()) {
|
||||||
|
default:
|
||||||
|
return make_error<StringError>(
|
||||||
|
std::string("No callback manager available for ") + T.str(),
|
||||||
|
inconvertibleErrorCode());
|
||||||
|
|
||||||
|
case Triple::aarch64:
|
||||||
|
return LocalLazyCallThroughManager::Create<OrcAArch64>(ES,
|
||||||
|
ErrorHandlerAddr);
|
||||||
|
|
||||||
|
case Triple::x86:
|
||||||
|
return LocalLazyCallThroughManager::Create<OrcI386>(ES, ErrorHandlerAddr);
|
||||||
|
|
||||||
|
case Triple::mips:
|
||||||
|
return LocalLazyCallThroughManager::Create<OrcMips32Be>(ES,
|
||||||
|
ErrorHandlerAddr);
|
||||||
|
|
||||||
|
case Triple::mipsel:
|
||||||
|
return LocalLazyCallThroughManager::Create<OrcMips32Le>(ES,
|
||||||
|
ErrorHandlerAddr);
|
||||||
|
|
||||||
|
case Triple::mips64:
|
||||||
|
case Triple::mips64el:
|
||||||
|
return LocalLazyCallThroughManager::Create<OrcMips64>(ES, ErrorHandlerAddr);
|
||||||
|
|
||||||
|
case Triple::x86_64:
|
||||||
|
if (T.getOS() == Triple::OSType::Win32)
|
||||||
|
return LocalLazyCallThroughManager::Create<OrcX86_64_Win32>(
|
||||||
|
ES, ErrorHandlerAddr);
|
||||||
|
else
|
||||||
|
return LocalLazyCallThroughManager::Create<OrcX86_64_SysV>(
|
||||||
|
ES, ErrorHandlerAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyReexportsMaterializationUnit::LazyReexportsMaterializationUnit(
|
||||||
|
LazyCallThroughManager &LCTManager, IndirectStubsManager &ISManager,
|
||||||
|
JITDylib &SourceJD, SymbolAliasMap CallableAliases)
|
||||||
|
: MaterializationUnit(extractFlags(CallableAliases)),
|
||||||
|
LCTManager(LCTManager), ISManager(ISManager), SourceJD(SourceJD),
|
||||||
|
CallableAliases(std::move(CallableAliases)),
|
||||||
|
NotifyResolved(LazyCallThroughManager::createNotifyResolvedFunction(
|
||||||
|
[&ISManager](JITDylib &JD, const SymbolStringPtr &SymbolName,
|
||||||
|
JITTargetAddress ResolvedAddr) {
|
||||||
|
return ISManager.updatePointer(*SymbolName, ResolvedAddr);
|
||||||
|
})) {}
|
||||||
|
|
||||||
|
void LazyReexportsMaterializationUnit::materialize(
|
||||||
|
MaterializationResponsibility R) {
|
||||||
|
auto RequestedSymbols = R.getRequestedSymbols();
|
||||||
|
|
||||||
|
SymbolAliasMap RequestedAliases;
|
||||||
|
for (auto &RequestedSymbol : RequestedSymbols) {
|
||||||
|
auto I = CallableAliases.find(RequestedSymbol);
|
||||||
|
assert(I != CallableAliases.end() && "Symbol not found in alias map?");
|
||||||
|
RequestedAliases[I->first] = std::move(I->second);
|
||||||
|
CallableAliases.erase(I);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CallableAliases.empty())
|
||||||
|
R.replace(lazyReexports(LCTManager, ISManager, SourceJD,
|
||||||
|
std::move(CallableAliases)));
|
||||||
|
|
||||||
|
IndirectStubsManager::StubInitsMap StubInits;
|
||||||
|
for (auto &Alias : RequestedAliases) {
|
||||||
|
|
||||||
|
auto CallThroughTrampoline = LCTManager.getCallThroughTrampoline(
|
||||||
|
SourceJD, Alias.second.Aliasee, NotifyResolved);
|
||||||
|
|
||||||
|
if (!CallThroughTrampoline) {
|
||||||
|
SourceJD.getExecutionSession().reportError(
|
||||||
|
CallThroughTrampoline.takeError());
|
||||||
|
R.failMaterialization();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StubInits[*Alias.first] =
|
||||||
|
std::make_pair(*CallThroughTrampoline, Alias.second.AliasFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto Err = ISManager.createStubs(StubInits)) {
|
||||||
|
SourceJD.getExecutionSession().reportError(std::move(Err));
|
||||||
|
R.failMaterialization();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SymbolMap Stubs;
|
||||||
|
for (auto &Alias : RequestedAliases)
|
||||||
|
Stubs[Alias.first] = ISManager.findStub(*Alias.first, false);
|
||||||
|
|
||||||
|
R.resolve(Stubs);
|
||||||
|
R.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LazyReexportsMaterializationUnit::discard(const JITDylib &JD,
|
||||||
|
SymbolStringPtr Name) {
|
||||||
|
assert(CallableAliases.count(Name) &&
|
||||||
|
"Symbol not covered by this MaterializationUnit");
|
||||||
|
CallableAliases.erase(Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
SymbolFlagsMap
|
||||||
|
LazyReexportsMaterializationUnit::extractFlags(const SymbolAliasMap &Aliases) {
|
||||||
|
SymbolFlagsMap SymbolFlags;
|
||||||
|
for (auto &KV : Aliases) {
|
||||||
|
assert(KV.second.AliasFlags.isCallable() &&
|
||||||
|
"Lazy re-exports must be callable symbols");
|
||||||
|
SymbolFlags[KV.first] = KV.second.AliasFlags;
|
||||||
|
}
|
||||||
|
return SymbolFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End namespace orc.
|
||||||
|
} // End namespace llvm.
|
@ -14,6 +14,7 @@ add_llvm_unittest(OrcJITTests
|
|||||||
CoreAPIsTest.cpp
|
CoreAPIsTest.cpp
|
||||||
IndirectionUtilsTest.cpp
|
IndirectionUtilsTest.cpp
|
||||||
GlobalMappingLayerTest.cpp
|
GlobalMappingLayerTest.cpp
|
||||||
|
LazyCallThroughAndReexportsTest.cpp
|
||||||
LazyEmittingLayerTest.cpp
|
LazyEmittingLayerTest.cpp
|
||||||
LegacyAPIInteropTest.cpp
|
LegacyAPIInteropTest.cpp
|
||||||
ObjectTransformLayerTest.cpp
|
ObjectTransformLayerTest.cpp
|
||||||
|
@ -22,44 +22,6 @@ class CoreAPIsStandardTest : public CoreAPIsBasedStandardTest {};
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class SimpleMaterializationUnit : public MaterializationUnit {
|
|
||||||
public:
|
|
||||||
using MaterializeFunction =
|
|
||||||
std::function<void(MaterializationResponsibility)>;
|
|
||||||
using DiscardFunction =
|
|
||||||
std::function<void(const JITDylib &, SymbolStringPtr)>;
|
|
||||||
using DestructorFunction = std::function<void()>;
|
|
||||||
|
|
||||||
SimpleMaterializationUnit(
|
|
||||||
SymbolFlagsMap SymbolFlags, MaterializeFunction Materialize,
|
|
||||||
DiscardFunction Discard = DiscardFunction(),
|
|
||||||
DestructorFunction Destructor = DestructorFunction())
|
|
||||||
: MaterializationUnit(std::move(SymbolFlags)),
|
|
||||||
Materialize(std::move(Materialize)), Discard(std::move(Discard)),
|
|
||||||
Destructor(std::move(Destructor)) {}
|
|
||||||
|
|
||||||
~SimpleMaterializationUnit() override {
|
|
||||||
if (Destructor)
|
|
||||||
Destructor();
|
|
||||||
}
|
|
||||||
|
|
||||||
void materialize(MaterializationResponsibility R) override {
|
|
||||||
Materialize(std::move(R));
|
|
||||||
}
|
|
||||||
|
|
||||||
void discard(const JITDylib &JD, SymbolStringPtr Name) override {
|
|
||||||
if (Discard)
|
|
||||||
Discard(JD, std::move(Name));
|
|
||||||
else
|
|
||||||
llvm_unreachable("Discard not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
MaterializeFunction Materialize;
|
|
||||||
DiscardFunction Discard;
|
|
||||||
DestructorFunction Destructor;
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_F(CoreAPIsStandardTest, BasicSuccessfulLookup) {
|
TEST_F(CoreAPIsStandardTest, BasicSuccessfulLookup) {
|
||||||
bool OnResolutionRun = false;
|
bool OnResolutionRun = false;
|
||||||
bool OnReadyRun = false;
|
bool OnReadyRun = false;
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
#include "OrcTestCommon.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/LazyReexports.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
using namespace llvm::orc;
|
||||||
|
|
||||||
|
class LazyReexportsTest : public CoreAPIsBasedStandardTest {};
|
||||||
|
|
||||||
|
static int dummyTarget() { return 42; }
|
||||||
|
|
||||||
|
TEST_F(LazyReexportsTest, BasicLocalCallThroughManagerOperation) {
|
||||||
|
// Create a callthrough manager for the host (if possible) and verify that
|
||||||
|
// a call to the lazy call-through:
|
||||||
|
// (1) Materializes the MU. This verifies that the symbol was looked up, and
|
||||||
|
// that we didn't arrive at the target via some other path
|
||||||
|
// (2) Returns the expected value (which we take as proof that the call
|
||||||
|
// reached the target).
|
||||||
|
|
||||||
|
auto JTMB = JITTargetMachineBuilder::detectHost();
|
||||||
|
|
||||||
|
// Bail out if we can not detect the host.
|
||||||
|
if (!JTMB) {
|
||||||
|
consumeError(JTMB.takeError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bail out if we can not build a local call-through manager.
|
||||||
|
auto LCTM = createLocalLazyCallThroughManager(JTMB->getTargetTriple(), ES, 0);
|
||||||
|
if (!LCTM) {
|
||||||
|
consumeError(LCTM.takeError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto DummyTarget = ES.getSymbolStringPool().intern("DummyTarget");
|
||||||
|
|
||||||
|
bool DummyTargetMaterialized = false;
|
||||||
|
|
||||||
|
cantFail(JD.define(llvm::make_unique<SimpleMaterializationUnit>(
|
||||||
|
SymbolFlagsMap({{DummyTarget, JITSymbolFlags::Exported}}),
|
||||||
|
[&](MaterializationResponsibility R) {
|
||||||
|
DummyTargetMaterialized = true;
|
||||||
|
R.resolve(
|
||||||
|
{{DummyTarget,
|
||||||
|
JITEvaluatedSymbol(static_cast<JITTargetAddress>(
|
||||||
|
reinterpret_cast<uintptr_t>(&dummyTarget)),
|
||||||
|
JITSymbolFlags::Exported)}});
|
||||||
|
R.emit();
|
||||||
|
})));
|
||||||
|
|
||||||
|
unsigned NotifyResolvedCount = 0;
|
||||||
|
auto NotifyResolved = LazyCallThroughManager::createNotifyResolvedFunction(
|
||||||
|
[&](JITDylib &JD, const SymbolStringPtr &SymbolName,
|
||||||
|
JITTargetAddress ResolvedAddr) {
|
||||||
|
++NotifyResolvedCount;
|
||||||
|
return Error::success();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto CallThroughTrampoline = cantFail((*LCTM)->getCallThroughTrampoline(
|
||||||
|
JD, DummyTarget, std::move(NotifyResolved)));
|
||||||
|
|
||||||
|
auto CTTPtr = reinterpret_cast<int (*)()>(
|
||||||
|
static_cast<uintptr_t>(CallThroughTrampoline));
|
||||||
|
|
||||||
|
// Call twice to verify nothing unexpected happens on redundant calls.
|
||||||
|
auto Result = CTTPtr();
|
||||||
|
(void)CTTPtr();
|
||||||
|
|
||||||
|
EXPECT_TRUE(DummyTargetMaterialized)
|
||||||
|
<< "CallThrough did not materialize target";
|
||||||
|
EXPECT_EQ(NotifyResolvedCount, 1U)
|
||||||
|
<< "CallThrough should have generated exactly one 'NotifyResolved' call";
|
||||||
|
EXPECT_EQ(Result, 42) << "Failed to call through to target";
|
||||||
|
}
|
@ -85,6 +85,44 @@ private:
|
|||||||
static bool NativeTargetInitialized;
|
static bool NativeTargetInitialized;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SimpleMaterializationUnit : public orc::MaterializationUnit {
|
||||||
|
public:
|
||||||
|
using MaterializeFunction =
|
||||||
|
std::function<void(orc::MaterializationResponsibility)>;
|
||||||
|
using DiscardFunction =
|
||||||
|
std::function<void(const orc::JITDylib &, orc::SymbolStringPtr)>;
|
||||||
|
using DestructorFunction = std::function<void()>;
|
||||||
|
|
||||||
|
SimpleMaterializationUnit(
|
||||||
|
orc::SymbolFlagsMap SymbolFlags, MaterializeFunction Materialize,
|
||||||
|
DiscardFunction Discard = DiscardFunction(),
|
||||||
|
DestructorFunction Destructor = DestructorFunction())
|
||||||
|
: MaterializationUnit(std::move(SymbolFlags)),
|
||||||
|
Materialize(std::move(Materialize)), Discard(std::move(Discard)),
|
||||||
|
Destructor(std::move(Destructor)) {}
|
||||||
|
|
||||||
|
~SimpleMaterializationUnit() override {
|
||||||
|
if (Destructor)
|
||||||
|
Destructor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void materialize(orc::MaterializationResponsibility R) override {
|
||||||
|
Materialize(std::move(R));
|
||||||
|
}
|
||||||
|
|
||||||
|
void discard(const orc::JITDylib &JD, orc::SymbolStringPtr Name) override {
|
||||||
|
if (Discard)
|
||||||
|
Discard(JD, std::move(Name));
|
||||||
|
else
|
||||||
|
llvm_unreachable("Discard not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
MaterializeFunction Materialize;
|
||||||
|
DiscardFunction Discard;
|
||||||
|
DestructorFunction Destructor;
|
||||||
|
};
|
||||||
|
|
||||||
// Base class for Orc tests that will execute code.
|
// Base class for Orc tests that will execute code.
|
||||||
class OrcExecutionTest {
|
class OrcExecutionTest {
|
||||||
public:
|
public:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user