[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/unittests/ExecutionEngine/Orc/CMakeLists.txt b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
index f0fd589..c18c936 100644
--- a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
+++ b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
@@ -14,6 +14,7 @@
   CoreAPIsTest.cpp
   IndirectionUtilsTest.cpp
   GlobalMappingLayerTest.cpp
+  LazyCallThroughAndReexportsTest.cpp
   LazyEmittingLayerTest.cpp
   LegacyAPIInteropTest.cpp
   ObjectTransformLayerTest.cpp
diff --git a/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp b/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp
index b3cd4b5..d0e4eff 100644
--- a/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp
@@ -22,44 +22,6 @@
 
 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) {
   bool OnResolutionRun = false;
   bool OnReadyRun = false;
diff --git a/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp b/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp
new file mode 100644
index 0000000..7caaa76
--- /dev/null
+++ b/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp
@@ -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";
+}
diff --git a/llvm/unittests/ExecutionEngine/Orc/OrcTestCommon.h b/llvm/unittests/ExecutionEngine/Orc/OrcTestCommon.h
index acc0e5b..23ca74c 100644
--- a/llvm/unittests/ExecutionEngine/Orc/OrcTestCommon.h
+++ b/llvm/unittests/ExecutionEngine/Orc/OrcTestCommon.h
@@ -85,6 +85,44 @@
   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.
 class OrcExecutionTest {
 public: