[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
diff --git a/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp b/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp
new file mode 100644
index 0000000..c327005
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp
@@ -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.