diff --git a/llvm/include/llvm/ExecutionEngine/JITSymbol.h b/llvm/include/llvm/ExecutionEngine/JITSymbol.h index 933b3ea8e13d..daf8c0ac4c74 100644 --- a/llvm/include/llvm/ExecutionEngine/JITSymbol.h +++ b/llvm/include/llvm/ExecutionEngine/JITSymbol.h @@ -48,7 +48,9 @@ public: Weak = 1U << 1, Common = 1U << 2, Absolute = 1U << 3, - Exported = 1U << 4 + Exported = 1U << 4, + NotMaterialized = 1U << 5, + Materializing = 1U << 6 }; /// @brief Default-construct a JITSymbolFlags instance. @@ -67,6 +69,15 @@ public: return (Flags & HasError) == HasError; } + /// @brief Returns true if this symbol has been fully materialized (i.e. is + /// callable). + bool isMaterialized() const { return !(Flags & NotMaterialized); } + + /// @brief Returns true if this symbol is in the process of being + /// materialized. This is generally only of interest as an + /// implementation detail to JIT infrastructure. + bool isMaterializing() const { return Flags & Materializing; } + /// @brief Returns true if the Weak flag is set. bool isWeak() const { return (Flags & Weak) == Weak; diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Core.h b/llvm/include/llvm/ExecutionEngine/Orc/Core.h new file mode 100644 index 000000000000..b1dc00c3fab9 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/Core.h @@ -0,0 +1,88 @@ +//===------ Core.h -- Core ORC APIs (Layer, JITDylib, etc.) -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Contains core ORC APIs. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_CORE_H +#define LLVM_EXECUTIONENGINE_ORC_CORE_H + +#include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h" + +#include +#include +#include + +namespace llvm { +namespace orc { + +/// @brief A set of symbol names (represented by SymbolStringPtrs for +// efficiency). +using SymbolNameSet = std::set; + +/// @brief A map from symbol names (as SymbolStringPtrs) to JITSymbols +/// (address/flags pairs). +using SymbolMap = std::map; + +/// @brief A symbol query that returns results via a callback when results are +/// ready. +/// +/// makes a callback when all symbols are available. +class AsynchronousSymbolQuery { +public: + + /// @brief Callback to notify client that symbols have been resolved. + using SymbolsResolvedCallback = std::function)>; + + /// @brief Callback to notify client that symbols are ready for execution. + using SymbolsReadyCallback = std::function; + + /// @brief Create a query for the given symbols, notify-resolved and + /// notify-ready callbacks. + AsynchronousSymbolQuery(const SymbolNameSet &Symbols, + SymbolsResolvedCallback NotifySymbolsResolved, + SymbolsReadyCallback NotifySymbolsReady); + + /// @brief Notify client that the query failed. + /// + /// If the notify-resolved callback has not been made yet, then it is called + /// with the given error, and the notify-finalized callback is never made. + /// + /// If the notify-resolved callback has already been made then then the + /// notify-finalized callback is called with the given error. + /// + /// It is illegal to call setFailed after both callbacks have been made. + void setFailed(Error Err); + + /// @brief Set the resolved symbol information for the given symbol name. + /// + /// If this symbol was the last one not resolved, this will trigger a call to + /// the notify-finalized callback passing the completed sybol map. + void setDefinition(SymbolStringPtr Name, JITSymbol Sym); + + /// @brief Notify the query that a requested symbol is ready for execution. + /// + /// This decrements the query's internal count of not-yet-ready symbols. If + /// this call to notifySymbolFinalized sets the counter to zero, it will call + /// the notify-finalized callback with Error::success as the value. + void notifySymbolFinalized(); +private: + SymbolMap Symbols; + size_t OutstandingResolutions = 0; + size_t OutstandingFinalizations = 0; + SymbolsResolvedCallback NotifySymbolsResolved; + SymbolsReadyCallback NotifySymbolsReady; +}; + +} // End namespace orc +} // End namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_CORE_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/OrcError.h b/llvm/include/llvm/ExecutionEngine/Orc/OrcError.h index e1ac87075ac0..c2ff41e421e7 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/OrcError.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/OrcError.h @@ -22,7 +22,8 @@ namespace orc { enum class OrcErrorCode : int { // RPC Errors - JITSymbolNotFound = 1, + DuplicateDefinition = 1, + JITSymbolNotFound, RemoteAllocatorDoesNotExist, RemoteAllocatorIdAlreadyInUse, RemoteMProtectAddrUnrecognized, @@ -39,6 +40,18 @@ enum class OrcErrorCode : int { std::error_code orcError(OrcErrorCode ErrCode); +class DuplicateDefinition : public ErrorInfo { +public: + static char ID; + + DuplicateDefinition(std::string SymbolName); + std::error_code convertToErrorCode() const override; + void log(raw_ostream &OS) const override; + const std::string &getSymbolName() const; +private: + std::string SymbolName; +}; + class JITSymbolNotFound : public ErrorInfo { public: static char ID; diff --git a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt index f83e002c758f..ee64990dfb49 100644 --- a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt @@ -1,4 +1,5 @@ add_llvm_library(LLVMOrcJIT + Core.cpp ExecutionUtils.cpp IndirectionUtils.cpp NullResolver.cpp diff --git a/llvm/lib/ExecutionEngine/Orc/Core.cpp b/llvm/lib/ExecutionEngine/Orc/Core.cpp new file mode 100644 index 000000000000..4c1ed18d4d1d --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/Core.cpp @@ -0,0 +1,67 @@ +//===--------- Core.cpp - Core ORC APIs (SymbolSource, VSO, etc.) ---------===// +// +// 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/Core.h" +#include "llvm/ExecutionEngine/Orc/OrcError.h" + +namespace llvm { +namespace orc { + +AsynchronousSymbolQuery::AsynchronousSymbolQuery( + const SymbolNameSet &Symbols, + SymbolsResolvedCallback NotifySymbolsResolved, + SymbolsReadyCallback NotifySymbolsReady) + : NotifySymbolsResolved(std::move(NotifySymbolsResolved)), + NotifySymbolsReady(std::move(NotifySymbolsReady)) { + assert(this->NotifySymbolsResolved && + "Symbols resolved callback must be set"); + assert(this->NotifySymbolsReady && "Symbols ready callback must be set"); + OutstandingResolutions = OutstandingFinalizations = Symbols.size(); +} + +void AsynchronousSymbolQuery::setFailed(Error Err) { + OutstandingResolutions = OutstandingFinalizations = 0; + if (NotifySymbolsResolved) + NotifySymbolsResolved(std::move(Err)); + else + NotifySymbolsReady(std::move(Err)); +} + +void AsynchronousSymbolQuery::setDefinition(SymbolStringPtr Name, + JITSymbol Sym) { + // If OutstandingResolutions is zero we must have errored out already. Just + // ignore this. + if (OutstandingResolutions == 0) + return; + + assert(!Symbols.count(Name) && + "Symbol has already been assigned an address"); + Symbols.insert(std::make_pair(std::move(Name), std::move(Sym))); + --OutstandingResolutions; + if (OutstandingResolutions == 0) { + NotifySymbolsResolved(std::move(Symbols)); + // Null out NotifySymbolsResolved to indicate that we've already called it. + NotifySymbolsResolved = {}; + } +} + +void AsynchronousSymbolQuery::notifySymbolFinalized() { + // If OutstandingFinalizations is zero we must have errored out already. Just + // ignore this. + if (OutstandingFinalizations == 0) + return; + + assert(OutstandingFinalizations > 0 && "All symbols already finalized"); + --OutstandingFinalizations; + if (OutstandingFinalizations == 0) + NotifySymbolsReady(Error::success()); +} + +} // End namespace orc. +} // End namespace llvm. diff --git a/llvm/lib/ExecutionEngine/Orc/OrcError.cpp b/llvm/lib/ExecutionEngine/Orc/OrcError.cpp index c218cb9a523c..f0bfed8ddb8a 100644 --- a/llvm/lib/ExecutionEngine/Orc/OrcError.cpp +++ b/llvm/lib/ExecutionEngine/Orc/OrcError.cpp @@ -29,6 +29,10 @@ public: std::string message(int condition) const override { switch (static_cast(condition)) { + case OrcErrorCode::DuplicateDefinition: + return "Duplicate symbol definition"; + case OrcErrorCode::JITSymbolNotFound: + return "JIT symbol not found"; case OrcErrorCode::RemoteAllocatorDoesNotExist: return "Remote allocator does not exist"; case OrcErrorCode::RemoteAllocatorIdAlreadyInUse: @@ -45,8 +49,6 @@ public: return "Could not negotiate RPC function"; case OrcErrorCode::RPCResponseAbandoned: return "RPC response abandoned"; - case OrcErrorCode::JITSymbolNotFound: - return "JIT symbol not found"; case OrcErrorCode::UnexpectedRPCCall: return "Unexpected RPC call"; case OrcErrorCode::UnexpectedRPCResponse: @@ -67,6 +69,7 @@ static ManagedStatic OrcErrCat; namespace llvm { namespace orc { +char DuplicateDefinition::ID = 0; char JITSymbolNotFound::ID = 0; std::error_code orcError(OrcErrorCode ErrCode) { @@ -74,6 +77,22 @@ std::error_code orcError(OrcErrorCode ErrCode) { return std::error_code(static_cast(ErrCode), *OrcErrCat); } + +DuplicateDefinition::DuplicateDefinition(std::string SymbolName) + : SymbolName(std::move(SymbolName)) {} + +std::error_code DuplicateDefinition::convertToErrorCode() const { + return orcError(OrcErrorCode::DuplicateDefinition); +} + +void DuplicateDefinition::log(raw_ostream &OS) const { + OS << "Duplicate definition of symbol '" << SymbolName << "'"; +} + +const std::string &DuplicateDefinition::getSymbolName() const { + return SymbolName; +} + JITSymbolNotFound::JITSymbolNotFound(std::string SymbolName) : SymbolName(std::move(SymbolName)) {} diff --git a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt index dd0281d0b73d..25697300e33b 100644 --- a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt +++ b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt @@ -11,6 +11,7 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(OrcJITTests CompileOnDemandLayerTest.cpp + CoreAPIsTest.cpp IndirectionUtilsTest.cpp GlobalMappingLayerTest.cpp LazyEmittingLayerTest.cpp diff --git a/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp b/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp new file mode 100644 index 000000000000..6c33abbc811f --- /dev/null +++ b/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp @@ -0,0 +1,50 @@ +//===----------- CoreAPIsTest.cpp - Unit tests for Core ORC APIs ----------===// +// +// 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/Core.h" +#include "OrcTestCommon.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::orc; + +namespace { + +TEST(CoreAPIsTest, AsynchronousSymbolQuerySuccessfulResolutionOnly) { + SymbolStringPool SP; + auto Foo = SP.intern("foo"); + constexpr JITTargetAddress FakeAddr = 0xdeadbeef; + SymbolNameSet Names({Foo}); + + bool OnResolutionRun = false; + bool OnReadyRun = false; + auto OnResolution = + [&](Expected Result) { + EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error"; + auto I = Result->find(Foo); + EXPECT_NE(I, Result->end()) << "Could not find symbol definition"; + EXPECT_EQ(cantFail(I->second.getAddress()), FakeAddr) + << "Resolution returned incorrect result"; + OnResolutionRun = true; + }; + auto OnReady = + [&](Error Err) { + cantFail(std::move(Err)); + OnResolutionRun = true; + }; + + AsynchronousSymbolQuery Q(Names, OnResolution, OnReady); + + Q.setDefinition(Foo, JITSymbol(FakeAddr, JITSymbolFlags::Exported)); + + EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run"; + EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run"; +} + +}