[llvm] r321838 - [ORC] Add new core ORC APIs (Core.h/Core.cpp): VSO, AsynchronousSymbolQuery and

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 4 16:04:16 PST 2018


Author: lhames
Date: Thu Jan  4 16:04:16 2018
New Revision: 321838

URL: http://llvm.org/viewvc/llvm-project?rev=321838&view=rev
Log:
[ORC] Add new core ORC APIs (Core.h/Core.cpp): VSO, AsynchronousSymbolQuery and
SymbolSource.

These new APIs are a first stab at tackling some current shortcomings of ORC,
especially in performance and threading support.

VSO (Virtual Shared Object) is a symbol table representing the symbol
definitions of a set of modules that behave as if they had been statically
linked together into a shared object or dylib. Symbol definitions, either
pre-defined addresses or lazy definitions, can be added and queries for symbol
addresses made. The table applies the same linkage strength rules that static
linkers do when constructing a dylib or shared object: duplicate definitions
result in errors, strong definitions override weak or common ones. This class
should improve symbol lookup speed by providing centralized symbol tables (as
compared to the findSymbol implementation in the in-tree ORC layers, which
maintain one symbol table per object file / module added).

AsynchronousSymbolQuery is a query for the addresses of a set of symbols.
Query results are returned via a callback once they become available. Querying
for a set of symbols, rather than one symbol at a time (as the current lookup
scheme does) the JIT has the opportunity to make better use of available
resources (e.g. by spawning multiple jobs to materialize the requested symbols
if possible). Returning results via a callback makes queries asynchronous, so
queries from multiple threads of JIT'd code can proceed simultaneously.

SymbolSource represents a source of symbol definitions. It is used when
adding lazy symbol definitions to a VSO. Symbol definitions can be materialized
when needed or discarded if a stronger definition is found. Materializing on
demand via SymbolSources should (eventually) allow us to remove the lazy
materializers from JITSymbol, which will in turn allow the removal of many
current error checks and reduce the number of RPC round-trips involved in
materializing remote symbols. Adding a discard function allows sources to
discard symbol definitions (or mark them as available_externally), reducing the
amount of redundant code generated by the JIT for ODR symbols.

Added:
    llvm/trunk/include/llvm/ExecutionEngine/Orc/Core.h
    llvm/trunk/lib/ExecutionEngine/Orc/Core.cpp
    llvm/trunk/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp
Modified:
    llvm/trunk/include/llvm/ExecutionEngine/JITSymbol.h
    llvm/trunk/include/llvm/ExecutionEngine/Orc/OrcError.h
    llvm/trunk/lib/ExecutionEngine/Orc/CMakeLists.txt
    llvm/trunk/lib/ExecutionEngine/Orc/OrcError.cpp
    llvm/trunk/unittests/ExecutionEngine/Orc/CMakeLists.txt

Modified: llvm/trunk/include/llvm/ExecutionEngine/JITSymbol.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ExecutionEngine/JITSymbol.h?rev=321838&r1=321837&r2=321838&view=diff
==============================================================================
--- llvm/trunk/include/llvm/ExecutionEngine/JITSymbol.h (original)
+++ llvm/trunk/include/llvm/ExecutionEngine/JITSymbol.h Thu Jan  4 16:04:16 2018
@@ -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;

Added: llvm/trunk/include/llvm/ExecutionEngine/Orc/Core.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ExecutionEngine/Orc/Core.h?rev=321838&view=auto
==============================================================================
--- llvm/trunk/include/llvm/ExecutionEngine/Orc/Core.h (added)
+++ llvm/trunk/include/llvm/ExecutionEngine/Orc/Core.h Thu Jan  4 16:04:16 2018
@@ -0,0 +1,234 @@
+//===------ 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 <deque>
+#include <map>
+#include <memory>
+#include <set>
+#include <vector>
+
+namespace llvm {
+namespace orc {
+
+class VSO;
+
+/// @brief A set of symbol names (represented by SymbolStringPtrs for
+//         efficiency).
+using SymbolNameSet = std::set<SymbolStringPtr>;
+
+/// @brief A map from symbol names (as SymbolStringPtrs) to JITSymbols
+///        (address/flags pairs).
+using SymbolMap = std::map<SymbolStringPtr, JITSymbol>;
+
+/// @brief A map from symbol names (as SymbolStringPtrs) to JITSymbolFlags.
+using SymbolFlagsMap = std::map<SymbolStringPtr, JITSymbolFlags>;
+
+/// @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<void(Expected<SymbolMap>)>;
+
+  /// @brief Callback to notify client that symbols are ready for execution.
+  using SymbolsReadyCallback = std::function<void(Error)>;
+
+  /// @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;
+};
+
+/// @brief Represents a source of symbol definitions which may be materialized
+///        (turned into data / code through some materialization process) or
+///        discarded (if the definition is overridden by a stronger one).
+///
+/// SymbolSources are used when providing lazy definitions of symbols to VSOs.
+/// The VSO will call materialize when the address of a symbol is requested via
+/// the lookup method. The VSO will call discard if a stronger definition is
+/// added or already present.
+class SymbolSource {
+public:
+  virtual ~SymbolSource() {}
+
+  /// @brief Implementations of this method should materialize the given
+  ///        symbols (plus any additional symbols required) by adding a
+  ///        Materializer to the ExecutionSession's MaterializationQueue.
+  virtual Error materialize(VSO &V, SymbolNameSet Symbols) = 0;
+
+  /// @brief Implementations of this method should discard the given symbol
+  ///        from the source (e.g. if the source is an LLVM IR Module and the
+  ///        symbol is a function, delete the function body or mark it available
+  ///        externally).
+  virtual void discard(VSO &V, SymbolStringPtr Name) = 0;
+private:
+  virtual void anchor();
+};
+
+/// @brief Represents a dynamic linkage unit in a JIT process.
+///
+/// VSO acts as a symbol table (symbol definitions can be set and the dylib
+/// queried to find symbol addresses) and as a key for tracking resources
+/// (since a VSO's address is fixed).
+class VSO {
+  friend class ExecutionSession;
+public:
+
+  /// @brief 
+  enum RelativeLinkageStrength {
+    NewDefinitionIsStronger,
+    DuplicateDefinition,
+    ExistingDefinitionIsStronger
+  };
+
+  using SetDefinitionsResult =
+    std::map<SymbolStringPtr, RelativeLinkageStrength>;
+  using SourceWorkMap = std::map<SymbolSource*, SymbolNameSet>;
+  
+  struct LookupResult {
+    SourceWorkMap MaterializationWork;
+    SymbolNameSet UnresolvedSymbols;
+  };
+
+  VSO() = default;
+
+  VSO(const VSO&) = delete;
+  VSO& operator=(const VSO&) = delete;
+  VSO(VSO&&) = delete;
+  VSO& operator=(VSO&&) = delete;
+
+  /// @brief Compare new linkage with existing linkage.
+  static RelativeLinkageStrength
+  compareLinkage(Optional<JITSymbolFlags> OldFlags,
+                 JITSymbolFlags NewFlags);
+
+  /// @brief Compare new linkage with an existing symbol's linkage.
+  RelativeLinkageStrength compareLinkage(SymbolStringPtr Name,
+                                         JITSymbolFlags NewFlags) const;
+
+  /// @brief Adds the given symbols to the mapping as resolved, finalized
+  ///        symbols.
+  ///
+  /// FIXME: We can take this by const-ref once symbol-based laziness is
+  ///        removed.
+  Error define(SymbolMap NewSymbols);
+
+  /// @brief Adds the given symbols to the mapping as lazy symbols.
+  Error defineLazy(const SymbolFlagsMap &NewSymbols, SymbolSource &Source);
+
+  /// @brief Add the given symbol/address mappings to the dylib, but do not
+  ///        mark the symbols as finalized yet.
+  void resolve(SymbolMap SymbolValues);
+
+  /// @brief Finalize the given symbols.
+  void finalize(SymbolNameSet SymbolsToFinalize);
+
+  /// @brief Apply the given query to the given symbols in this VSO.
+  ///
+  /// For symbols in this VSO that have already been materialized, their address
+  /// will be set in the query immediately.
+  ///
+  /// For symbols in this VSO that have not been materialized, the query will be
+  /// recorded and the source for those symbols (plus the set of symbols to be
+  /// materialized by that source) will be returned as the MaterializationWork
+  /// field of the LookupResult.
+  ///
+  /// Any symbols not found in this VSO will be returned in the
+  /// UnresolvedSymbols field of the LookupResult.
+  LookupResult lookup(AsynchronousSymbolQuery &Query, SymbolNameSet Symbols);
+
+private:
+
+  class MaterializationInfo {
+  public:
+    MaterializationInfo(JITSymbolFlags Flags, AsynchronousSymbolQuery &Query);
+    JITSymbolFlags getFlags() const;
+    JITTargetAddress getAddress() const;
+    void query(SymbolStringPtr Name, AsynchronousSymbolQuery &Query);
+    void resolve(SymbolStringPtr Name, JITSymbol Sym);
+    void finalize();
+  private:
+    JITSymbolFlags Flags;
+    JITTargetAddress Address = 0;
+    std::vector<AsynchronousSymbolQuery*> PendingResolution;
+    std::vector<AsynchronousSymbolQuery*> PendingFinalization;
+  };
+
+  class SymbolTableEntry {
+  public:
+    SymbolTableEntry(JITSymbolFlags Flags, SymbolSource &Source);
+    SymbolTableEntry(JITSymbol Sym);
+    SymbolTableEntry(SymbolTableEntry &&Other);
+    ~SymbolTableEntry();
+    JITSymbolFlags getFlags() const;
+    void replaceWithSource(VSO &V, SymbolStringPtr Name, JITSymbolFlags Flags,
+                           SymbolSource &NewSource);
+    SymbolSource* query(SymbolStringPtr Name, AsynchronousSymbolQuery &Query);
+    void resolve(VSO &V, SymbolStringPtr Name, JITSymbol Sym);
+    void finalize();
+  private:
+    JITSymbolFlags Flags;
+    union {
+      JITTargetAddress Address;
+      SymbolSource *Source;
+      std::unique_ptr<MaterializationInfo> MatInfo;
+    };
+  };
+
+  std::map<SymbolStringPtr, SymbolTableEntry> Symbols;
+};
+
+} // End namespace orc
+} // End namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_ORC_CORE_H

Modified: llvm/trunk/include/llvm/ExecutionEngine/Orc/OrcError.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ExecutionEngine/Orc/OrcError.h?rev=321838&r1=321837&r2=321838&view=diff
==============================================================================
--- llvm/trunk/include/llvm/ExecutionEngine/Orc/OrcError.h (original)
+++ llvm/trunk/include/llvm/ExecutionEngine/Orc/OrcError.h Thu Jan  4 16:04:16 2018
@@ -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<DuplicateDefinition> {
+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<JITSymbolNotFound> {
 public:
   static char ID;

Modified: llvm/trunk/lib/ExecutionEngine/Orc/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/Orc/CMakeLists.txt?rev=321838&r1=321837&r2=321838&view=diff
==============================================================================
--- llvm/trunk/lib/ExecutionEngine/Orc/CMakeLists.txt (original)
+++ llvm/trunk/lib/ExecutionEngine/Orc/CMakeLists.txt Thu Jan  4 16:04:16 2018
@@ -1,4 +1,5 @@
 add_llvm_library(LLVMOrcJIT
+  Core.cpp
   ExecutionUtils.cpp
   IndirectionUtils.cpp
   NullResolver.cpp

Added: llvm/trunk/lib/ExecutionEngine/Orc/Core.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/Orc/Core.cpp?rev=321838&view=auto
==============================================================================
--- llvm/trunk/lib/ExecutionEngine/Orc/Core.cpp (added)
+++ llvm/trunk/lib/ExecutionEngine/Orc/Core.cpp Thu Jan  4 16:04:16 2018
@@ -0,0 +1,322 @@
+//===--------- 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 {
+
+void SymbolSource::anchor() {}
+
+AsynchronousSymbolQuery::AsynchronousSymbolQuery(
+                                  const SymbolNameSet &Symbols,
+                                  SymbolsResolvedCallback NotifySymbolsResolved,
+                                  SymbolsReadyCallback NotifySymbolsReady)
+    : NotifySymbolsResolved(std::move(NotifySymbolsResolved)),
+      NotifySymbolsReady(std::move(NotifySymbolsReady)) {
+  assert(NotifySymbolsResolved && "Symbols resolved callback must be set");
+  assert(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());
+}
+
+VSO::MaterializationInfo::MaterializationInfo(JITSymbolFlags Flags,
+                                              AsynchronousSymbolQuery &Query)
+  : Flags(std::move(Flags)), PendingResolution({ &Query }) {}
+
+JITSymbolFlags VSO::MaterializationInfo::getFlags() const {
+  return Flags;
+}
+
+JITTargetAddress VSO::MaterializationInfo::getAddress() const {
+  return Address;
+}
+
+void VSO::MaterializationInfo::query(SymbolStringPtr Name,
+                                     AsynchronousSymbolQuery &Query) {
+  if (Address != 0) {
+    Query.setDefinition(Name, JITSymbol(Address, Flags));
+    PendingFinalization.push_back(&Query);
+  } else
+    PendingResolution.push_back(&Query);
+}
+
+void VSO::MaterializationInfo::resolve(SymbolStringPtr Name, JITSymbol Sym) {
+  // FIXME: Sanity check flags?
+  Flags = Sym.getFlags();
+  Address = cantFail(Sym.getAddress());
+  for (auto *Query : PendingResolution) {
+    Query->setDefinition(Name, std::move(Sym));
+    PendingFinalization.push_back(Query);
+  }
+  PendingResolution = {};
+}
+
+void VSO::MaterializationInfo::finalize() {
+  for (auto *Query : PendingFinalization)
+    Query->notifySymbolFinalized();
+  PendingFinalization = {};
+}
+
+VSO::SymbolTableEntry::SymbolTableEntry(JITSymbolFlags Flags, SymbolSource &Source)
+  : Flags(JITSymbolFlags::FlagNames(Flags | JITSymbolFlags::NotMaterialized)),
+    Source(&Source) {
+  // FIXME: Assert flag sanity.
+}
+
+VSO::SymbolTableEntry::SymbolTableEntry(JITSymbol Sym)
+  : Flags(Sym.getFlags()), Address(cantFail(Sym.getAddress())) {
+  // FIXME: Assert flag sanity.
+}
+
+VSO::SymbolTableEntry::SymbolTableEntry(SymbolTableEntry &&Other)
+  : Flags(Other.Flags), Address(0) {
+  if (Flags.isMaterializing())
+    MatInfo = std::move(Other.MatInfo);
+  else
+    Source = Other.Source;
+}
+
+VSO::SymbolTableEntry::~SymbolTableEntry() {
+  assert(!Flags.isMaterializing() &&
+         "Symbol table entry destroyed while symbol was being materialized");
+}
+
+JITSymbolFlags VSO::SymbolTableEntry::getFlags() const { return Flags; }
+
+void VSO::SymbolTableEntry::replaceWithSource(VSO &V,
+                                              SymbolStringPtr Name,
+                                              JITSymbolFlags Flags,
+                                              SymbolSource &NewSource) {
+  assert(!this->Flags.isMaterializing() &&
+         "Attempted to replace symbol with lazy definition during "
+         "materialization");
+  if (!this->Flags.isMaterialized())
+    Source->discard(V, Name);
+  this->Flags = Flags;
+  this->Source = &NewSource;
+}
+
+SymbolSource*
+VSO::SymbolTableEntry::query(SymbolStringPtr Name,
+                             AsynchronousSymbolQuery &Query) {
+  if (Flags.isMaterializing()) {
+    MatInfo->query(std::move(Name), Query);
+    return nullptr;
+  } else if (Flags.isMaterialized()) {
+    Query.setDefinition(std::move(Name), JITSymbol(Address, Flags));
+    Query.notifySymbolFinalized();
+    return nullptr;
+  }
+  SymbolSource *S = Source;
+  new (&MatInfo) std::unique_ptr<MaterializationInfo>(
+    llvm::make_unique<MaterializationInfo>(Flags, Query));
+  Flags |= JITSymbolFlags::Materializing;
+  return S;
+}
+
+void VSO::SymbolTableEntry::resolve(VSO &V, SymbolStringPtr Name,
+                                      JITSymbol Sym) {
+  if (Flags.isMaterializing())
+    MatInfo->resolve(std::move(Name), std::move(Sym));
+  else {
+    // If there's a layer for this symbol.
+    if (!Flags.isMaterialized())
+      Source->discard(V, Name);
+
+    // FIXME: Should we assert flag state here (flags must match except for
+    //        materialization state, overrides must be legal) or in the caller
+    //        in VSO?
+    Flags = Sym.getFlags();
+    Address = cantFail(Sym.getAddress());
+  }
+}
+
+void VSO::SymbolTableEntry::finalize() {
+  if (Flags.isMaterializing()) {
+    auto TmpMatInfo = std::move(MatInfo);
+    MatInfo.std::unique_ptr<MaterializationInfo>::~unique_ptr();
+    // FIXME: Assert flag sanity?
+    Flags = TmpMatInfo->getFlags();
+    Address = TmpMatInfo->getAddress();
+    TmpMatInfo->finalize();
+  }
+  assert(Flags.isMaterialized() && "Trying to finalize not-emitted symbol");
+}
+
+VSO::RelativeLinkageStrength
+VSO::compareLinkage(Optional<JITSymbolFlags> Old, JITSymbolFlags New) {
+  if (Old == None)
+    return llvm::orc::VSO::NewDefinitionIsStronger;
+
+  if (Old->isStrongDefinition()) {
+    if (New.isStrongDefinition())
+      return llvm::orc::VSO::DuplicateDefinition;
+    else
+      return llvm::orc::VSO::ExistingDefinitionIsStronger;
+  } else {
+    if (New.isStrongDefinition())
+      return llvm::orc::VSO::NewDefinitionIsStronger;
+    else
+      return llvm::orc::VSO::ExistingDefinitionIsStronger;
+  }
+}
+
+VSO::RelativeLinkageStrength
+VSO::compareLinkage(SymbolStringPtr Name, JITSymbolFlags NewFlags) const {
+  auto I = Symbols.find(Name);
+  return compareLinkage(I == Symbols.end()
+                        ? None
+                        : Optional<JITSymbolFlags>(I->second.getFlags()),
+                        NewFlags);
+}
+
+Error VSO::define(SymbolMap NewSymbols) {
+  Error Err = Error::success();
+  for (auto &KV : NewSymbols) {
+    auto I = Symbols.find(KV.first);
+    auto LinkageResult =
+      compareLinkage(I == Symbols.end()
+                     ? None
+                     : Optional<JITSymbolFlags>(I->second.getFlags()),
+                     KV.second.getFlags());
+
+    // Silently discard weaker definitions.
+    if (LinkageResult == ExistingDefinitionIsStronger)
+      continue;
+
+    // Report duplicate definition errors.
+    if (LinkageResult == DuplicateDefinition) {
+      Err = joinErrors(std::move(Err),
+                       make_error<orc::DuplicateDefinition>(*KV.first));
+      continue;
+    }
+
+    if (I != Symbols.end()) {
+      I->second.resolve(*this, KV.first, std::move(KV.second));
+      I->second.finalize();
+    } else
+      Symbols.insert(std::make_pair(KV.first, std::move(KV.second)));
+  }
+  return Err;
+}
+
+Error VSO::defineLazy(const SymbolFlagsMap &NewSymbols, SymbolSource &Source) {
+  Error Err = Error::success();
+  for (auto &KV : NewSymbols) {
+    auto I = Symbols.find(KV.first);
+
+    auto LinkageResult =
+      compareLinkage(I == Symbols.end()
+                     ? None
+                     : Optional<JITSymbolFlags>(I->second.getFlags()),
+                     KV.second);
+
+    // Discard weaker definitions.
+    if (LinkageResult == ExistingDefinitionIsStronger)
+      Source.discard(*this, KV.first);
+
+    // Report duplicate definition errors.
+    if (LinkageResult == DuplicateDefinition) {
+      Err = joinErrors(std::move(Err),
+                       make_error<orc::DuplicateDefinition>(*KV.first));
+      continue;
+    }
+
+    if (I != Symbols.end())
+      I->second.replaceWithSource(*this, KV.first, KV.second, Source);
+    else
+      Symbols.emplace(std::make_pair(KV.first, 
+                                    SymbolTableEntry(KV.second, Source)));
+  }
+  return Err;
+}
+
+void VSO::resolve(SymbolMap SymbolValues) {
+  for (auto &KV : SymbolValues) {
+    auto I = Symbols.find(KV.first);
+    assert(I != Symbols.end() && "Resolving symbol not present in this dylib");
+    I->second.resolve(*this, KV.first, std::move(KV.second));
+  }
+}
+
+void VSO::finalize(SymbolNameSet SymbolsToFinalize) {
+  for (auto &S : SymbolsToFinalize) {
+    auto I = Symbols.find(S);
+    assert(I != Symbols.end() && "Finalizing symbol not present in this dylib");
+    I->second.finalize();
+  }
+}
+
+VSO::LookupResult VSO::lookup(AsynchronousSymbolQuery &Query,
+                              SymbolNameSet Names) {
+  SourceWorkMap MaterializationWork;
+
+  for (SymbolNameSet::iterator I = Names.begin(), E = Names.end(); I != E;) {
+    auto Tmp = I;
+    ++I;
+    auto SymI = Symbols.find(*Tmp);
+
+    // If the symbol isn't in this dylib then just continue.
+    // If it is, erase it from Names and proceed.
+    if (SymI == Symbols.end())
+      continue;
+    else
+      Names.erase(Tmp);
+
+    // Forward the query to the given SymbolTableEntry, and if it return a
+    // layer to perform materialization with, add that to the
+    // MaterializationWork map.
+    if (auto *Source = SymI->second.query(SymI->first, Query))
+      MaterializationWork[Source].insert(SymI->first);
+  }
+
+  return { std::move(MaterializationWork), std::move(Names) };
+}
+
+} // End namespace orc.
+} // End namespace llvm.

Modified: llvm/trunk/lib/ExecutionEngine/Orc/OrcError.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/Orc/OrcError.cpp?rev=321838&r1=321837&r2=321838&view=diff
==============================================================================
--- llvm/trunk/lib/ExecutionEngine/Orc/OrcError.cpp (original)
+++ llvm/trunk/lib/ExecutionEngine/Orc/OrcError.cpp Thu Jan  4 16:04:16 2018
@@ -29,6 +29,10 @@ public:
 
   std::string message(int condition) const override {
     switch (static_cast<OrcErrorCode>(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<OrcErrorCategory> O
 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 Er
   return std::error_code(static_cast<UT>(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)) {}
 

Modified: llvm/trunk/unittests/ExecutionEngine/Orc/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ExecutionEngine/Orc/CMakeLists.txt?rev=321838&r1=321837&r2=321838&view=diff
==============================================================================
--- llvm/trunk/unittests/ExecutionEngine/Orc/CMakeLists.txt (original)
+++ llvm/trunk/unittests/ExecutionEngine/Orc/CMakeLists.txt Thu Jan  4 16:04:16 2018
@@ -11,6 +11,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_llvm_unittest(OrcJITTests
   CompileOnDemandLayerTest.cpp
+  CoreAPIsTest.cpp
   IndirectionUtilsTest.cpp
   GlobalMappingLayerTest.cpp
   LazyEmittingLayerTest.cpp

Added: llvm/trunk/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp?rev=321838&view=auto
==============================================================================
--- llvm/trunk/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp (added)
+++ llvm/trunk/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp Thu Jan  4 16:04:16 2018
@@ -0,0 +1,226 @@
+//===----------- 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"
+
+#include <set>
+
+using namespace llvm;
+using namespace llvm::orc;
+
+namespace {
+
+class SimpleSource : public SymbolSource {
+public:
+  using MaterializeFunction = std::function<Error(VSO&, SymbolNameSet)>;
+  using DiscardFunction = std::function<void(VSO&, SymbolStringPtr)>;
+
+  SimpleSource(MaterializeFunction Materialize, DiscardFunction Discard)
+    : Materialize(std::move(Materialize)), Discard(std::move(Discard)) {}
+
+  Error materialize(VSO &V, SymbolNameSet Symbols) override {
+    return Materialize(V, std::move(Symbols));
+  }
+
+  void discard(VSO &V, SymbolStringPtr Name) override {
+    Discard(V, std::move(Name));
+  }
+
+private:
+  MaterializeFunction Materialize;
+  DiscardFunction Discard;
+};
+
+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<SymbolMap> 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";
+}
+
+TEST(CoreAPIsTest, AsynchronousSymbolQueryResolutionErrorOnly) {
+  SymbolStringPool SP;
+  auto Foo = SP.intern("foo");
+  SymbolNameSet Names({Foo});
+
+  bool OnResolutionRun = false;
+  bool OnReadyRun = false;
+
+  auto OnResolution =
+    [&](Expected<SymbolMap> Result) {
+      EXPECT_FALSE(!!Result) << "Resolution unexpectedly returned success";
+      auto Msg = toString(Result.takeError());
+      EXPECT_EQ(Msg, "xyz")
+        << "Resolution returned incorrect result";
+      OnResolutionRun = true;
+    };
+  auto OnReady =
+    [&](Error Err) {
+      cantFail(std::move(Err));
+      OnReadyRun = true;
+    };
+
+  AsynchronousSymbolQuery Q(Names, OnResolution, OnReady);
+
+  Q.setFailed(make_error<StringError>("xyz", inconvertibleErrorCode()));
+
+  EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
+  EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run";
+}
+
+TEST(CoreAPIsTest, SimpleAsynchronousSymbolQueryAgainstVSO) {
+  SymbolStringPool SP;
+  auto Foo = SP.intern("foo");
+  constexpr JITTargetAddress FakeAddr = 0xdeadbeef;
+  SymbolNameSet Names({Foo});
+
+  bool OnResolutionRun = false;
+  bool OnReadyRun = false;
+
+  auto OnResolution =
+    [&](Expected<SymbolMap> Result) {
+      EXPECT_TRUE(!!Result) << "Query 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));
+      OnReadyRun = true;
+    };
+
+  AsynchronousSymbolQuery Q(Names, OnResolution, OnReady);
+  VSO V;
+
+  SymbolMap Defs;
+  Defs.insert(
+    std::make_pair(Foo, JITSymbol(FakeAddr, JITSymbolFlags::Exported)));
+  cantFail(V.define(std::move(Defs)));
+  V.lookup(Q, Names);
+
+  EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
+  EXPECT_TRUE(OnReadyRun) << "OnReady was not run";
+}
+
+TEST(CoreAPIsTest, AddAndMaterializeLazySymbol) {
+
+  constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef;
+  constexpr JITTargetAddress FakeBarAddr = 0xcafef00d;
+
+  SymbolStringPool SP;
+  auto Foo = SP.intern("foo");
+  auto Bar = SP.intern("bar");
+  
+  bool FooMaterialized = false;
+  bool BarDiscarded = false;
+
+  VSO V;
+
+  auto Source =
+    std::make_shared<SimpleSource>(
+      [&](VSO &V, SymbolNameSet Symbols) {
+        EXPECT_EQ(Symbols.size(), 1U)
+          << "Expected Symbols set size to be 1 ({ Foo })";
+        EXPECT_EQ(*Symbols.begin(), Foo)
+          << "Expected Symbols == { Foo }";
+
+        SymbolMap SymbolsToResolve;
+        SymbolsToResolve.insert(
+          std::make_pair(Foo, JITSymbol(FakeFooAddr,
+                                        JITSymbolFlags::Exported)));
+        V.resolve(std::move(SymbolsToResolve));
+        SymbolNameSet SymbolsToFinalize;
+        SymbolsToFinalize.insert(Foo);
+        V.finalize(SymbolsToFinalize);
+        FooMaterialized = true;
+        return Error::success();
+      },
+      [&](VSO &V, SymbolStringPtr Name) {
+        EXPECT_EQ(Name, Bar) << "Expected Name to be Bar";
+        BarDiscarded = true;
+      });
+
+  SymbolFlagsMap InitialSymbols({
+      {Foo, JITSymbolFlags::Exported},
+      {Bar, static_cast<JITSymbolFlags::FlagNames>(JITSymbolFlags::Exported |
+                                                   JITSymbolFlags::Weak)}
+    });
+  cantFail(V.defineLazy(InitialSymbols, *Source));
+
+  SymbolMap BarOverride;
+  BarOverride.insert(
+    std::make_pair(Bar, JITSymbol(FakeBarAddr, JITSymbolFlags::Exported)));
+  cantFail(V.define(std::move(BarOverride)));
+
+  SymbolNameSet Names({Foo});
+
+  bool OnResolutionRun = false;
+  bool OnReadyRun = false;
+
+  auto OnResolution =
+    [&](Expected<SymbolMap> 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()), FakeFooAddr)
+        << "Resolution returned incorrect result";
+      OnResolutionRun = true;
+    };
+
+  auto OnReady =
+    [&](Error Err) {
+      cantFail(std::move(Err));
+      OnReadyRun = true;
+    };
+
+  AsynchronousSymbolQuery Q(Names, OnResolution, OnReady);
+
+  auto LR = V.lookup(Q, Names);
+
+  for (auto &SWKV : LR.MaterializationWork)
+    cantFail(SWKV.first->materialize(V, std::move(SWKV.second)));
+
+  EXPECT_TRUE(LR.UnresolvedSymbols.empty()) << "Could not find Foo in dylib";
+  EXPECT_TRUE(FooMaterialized) << "Foo was not materialized";
+  EXPECT_TRUE(BarDiscarded) << "Bar was not discarded";
+  EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
+  EXPECT_TRUE(OnReadyRun) << "OnReady was not run";
+}
+
+}




More information about the llvm-commits mailing list