blob: 22e3fcdcd4ad2606f0656c6e632b6fe6bb2b1e1c [file] [edit]
//===- LookupAndApplyTest.cpp - Test lookupAndApply -----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/LookupAndApply.h"
#include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h"
#include "llvm/ExecutionEngine/Orc/SelfExecutorProcessControl.h"
#include "llvm/Support/MSVCErrorWorkarounds.h"
#include "llvm/Testing/Support/Error.h"
#include <future>
#include "gtest/gtest.h"
using namespace llvm;
using namespace llvm::orc;
namespace {
// Two arbitrary, distinct addresses to resolve symbols to.
constexpr uint64_t AddrAValue = 0x1000;
constexpr uint64_t AddrBValue = 0x2000;
// Define Name -> Addr in JD as an exported absolute symbol.
static void defineAddr(JITDylib &JD, StringRef Name, ExecutorAddr Addr) {
auto &ES = JD.getExecutionSession();
cantFail(JD.define(
absoluteSymbols({{ES.intern(Name), {Addr, JITSymbolFlags::Exported}}})));
}
} // namespace
// An empty PrepareFns list trivially succeeds, even with an empty
// SearchOrder: there's nothing to look up, so no ExecutionSession is needed.
TEST(LookupAndApplyTest, EmptyPrepareFnsTriviallySucceeds) {
EXPECT_THAT_ERROR(lookupAndApply(LookupKind::Static, {}, {}), Succeeded());
}
// An empty SearchOrder fails outright, even for a symbol that is only weakly
// referenced (which would otherwise resolve to null rather than failing the
// lookup, see RecordAddrWeaklyReferencedAbsent below): there is no
// ExecutionSession to intern the symbol's name with, so the prepare function
// is never run, and so the lookup never gets far enough to see that the
// symbol was only weakly referenced.
TEST(LookupAndApplyTest, EmptySearchOrderFailsEvenForWeakReference) {
ExecutorAddr A(AddrAValue);
EXPECT_THAT_ERROR(
lookupAndApply(LookupKind::Static, {},
{recordAddr(SymbolNameSpec::verbatim("addr_a"), &A,
SymbolLookupFlags::WeaklyReferencedSymbol)}),
Failed());
EXPECT_EQ(A, ExecutorAddr(AddrAValue));
}
// recordAddr writes the resolved address of a required symbol.
TEST(LookupAndApplyTest, RecordAddr) {
ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
auto &JD = ES.getBootstrapJITDylib();
defineAddr(JD, "addr_a", ExecutorAddr(AddrAValue));
ExecutorAddr A;
cantFail(
lookupAndApply(JD, {recordAddr(SymbolNameSpec::verbatim("addr_a"), &A)}));
EXPECT_EQ(A, ExecutorAddr(AddrAValue));
cantFail(ES.endSession());
}
// A required symbol that is missing fails the lookup.
TEST(LookupAndApplyTest, RecordAddrRequiredAbsentFails) {
ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
ExecutorAddr A(AddrAValue);
EXPECT_THAT_ERROR(
lookupAndApply(ES.getBootstrapJITDylib(),
{recordAddr(SymbolNameSpec::verbatim("absent"), &A)}),
Failed());
cantFail(ES.endSession());
}
// A weakly-referenced symbol that is missing records a null address, rather
// than failing the lookup.
TEST(LookupAndApplyTest, RecordAddrWeaklyReferencedAbsent) {
ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
ExecutorAddr A(AddrAValue);
cantFail(
lookupAndApply(ES.getBootstrapJITDylib(),
{recordAddr(SymbolNameSpec::verbatim("absent"), &A,
SymbolLookupFlags::WeaklyReferencedSymbol)}));
EXPECT_EQ(A, ExecutorAddr());
cantFail(ES.endSession());
}
// The SymbolStringPtr overload behaves like the StringRef overload for a
// present, required symbol -- the caller just does the interning itself.
TEST(LookupAndApplyTest, RecordAddrSymbolStringPtr) {
ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
auto &JD = ES.getBootstrapJITDylib();
defineAddr(JD, "addr_a", ExecutorAddr(AddrAValue));
ExecutorAddr A;
cantFail(lookupAndApply(JD, {recordAddr(ES.intern("addr_a"), &A)}));
EXPECT_EQ(A, ExecutorAddr(AddrAValue));
cantFail(ES.endSession());
}
// As above, but for a weakly-referenced symbol that is missing: records a
// null address rather than failing the lookup.
TEST(LookupAndApplyTest, RecordAddrSymbolStringPtrWeaklyReferencedAbsent) {
ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
ExecutorAddr A(AddrAValue);
cantFail(
lookupAndApply(ES.getBootstrapJITDylib(),
{recordAddr(ES.intern("absent"), &A,
SymbolLookupFlags::WeaklyReferencedSymbol)}));
EXPECT_EQ(A, ExecutorAddr());
cantFail(ES.endSession());
}
// Several prepare functions in one call are all applied, and a single one may
// contribute more than one symbol.
TEST(LookupAndApplyTest, MultiplePrepareFns) {
ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
auto &JD = ES.getBootstrapJITDylib();
defineAddr(JD, "addr_a", ExecutorAddr(AddrAValue));
defineAddr(JD, "addr_b", ExecutorAddr(AddrBValue));
ExecutorAddr A, B, C, D;
// A composite prepare fn: contributes both names, records both results.
auto RecordBoth = [&C, &D](SymbolLookupSet &LS, ExecutionSession &ES,
const Mangler &) -> LookupApplyFn {
auto NA = ES.intern("addr_a");
auto NB = ES.intern("addr_b");
LS.add(NA);
LS.add(NB);
return
[&C, &D, NA = std::move(NA), NB = std::move(NB)](const SymbolMap &M) {
C = M.lookup(NA).getAddress();
D = M.lookup(NB).getAddress();
};
};
cantFail(lookupAndApply(
JD, {recordAddr(SymbolNameSpec::verbatim("addr_a"), &A),
recordAddr(SymbolNameSpec::verbatim("addr_b"), &B), RecordBoth}));
EXPECT_EQ(A, ExecutorAddr(AddrAValue));
EXPECT_EQ(B, ExecutorAddr(AddrBValue));
EXPECT_EQ(C, ExecutorAddr(AddrAValue));
EXPECT_EQ(D, ExecutorAddr(AddrBValue));
cantFail(ES.endSession());
}
// If the lookup fails then no applicator runs: a failed lookup must not
// leave some variables written and others not.
TEST(LookupAndApplyTest, NoApplyOnLookupFailure) {
ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
auto &JD = ES.getBootstrapJITDylib();
defineAddr(JD, "addr_a", ExecutorAddr(AddrAValue));
ExecutorAddr A, B;
// "addr_a" resolves, "absent" does not, so the whole lookup fails.
EXPECT_THAT_ERROR(
lookupAndApply(JD, {recordAddr(SymbolNameSpec::verbatim("addr_a"), &A),
recordAddr(SymbolNameSpec::verbatim("absent"), &B)}),
Failed());
EXPECT_EQ(A, ExecutorAddr());
EXPECT_EQ(B, ExecutorAddr());
cantFail(ES.endSession());
}
// The asynchronous form delivers success once every applicator has run.
TEST(LookupAndApplyTest, Async) {
ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
auto &JD = ES.getBootstrapJITDylib();
defineAddr(JD, "addr_a", ExecutorAddr(AddrAValue));
ExecutorAddr A;
std::promise<MSVCPError> P;
auto F = P.get_future();
lookupAndApply([&](Error Err) { P.set_value(std::move(Err)); }, JD,
{recordAddr(SymbolNameSpec::verbatim("addr_a"), &A)});
EXPECT_THAT_ERROR(F.get(), Succeeded());
EXPECT_EQ(A, ExecutorAddr(AddrAValue));
cantFail(ES.endSession());
}
// A lookup failure is delivered through the callback rather than returned.
TEST(LookupAndApplyTest, AsyncFailure) {
ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
auto &JD = ES.getBootstrapJITDylib();
ExecutorAddr A(AddrAValue);
std::promise<MSVCPError> P;
auto F = P.get_future();
lookupAndApply([&](Error Err) { P.set_value(std::move(Err)); }, JD,
{recordAddr(SymbolNameSpec::verbatim("absent"), &A)});
EXPECT_THAT_ERROR(F.get(), Failed());
EXPECT_EQ(A, ExecutorAddr(AddrAValue));
cantFail(ES.endSession());
}
// Weak references are resolved per symbol: within one lookup a symbol that is
// found is recorded, and one that is not is left null.
TEST(LookupAndApplyTest, WeaklyReferencedMixed) {
ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
auto &JD = ES.getBootstrapJITDylib();
defineAddr(JD, "addr_a", ExecutorAddr(AddrAValue));
ExecutorAddr A, B(AddrBValue);
cantFail(lookupAndApply(
JD, {recordAddr(SymbolNameSpec::verbatim("addr_a"), &A,
SymbolLookupFlags::WeaklyReferencedSymbol),
recordAddr(SymbolNameSpec::verbatim("absent"), &B,
SymbolLookupFlags::WeaklyReferencedSymbol)}));
EXPECT_EQ(A, ExecutorAddr(AddrAValue));
EXPECT_EQ(B, ExecutorAddr());
cantFail(ES.endSession());
}