[llvm] [ORC] Merge MaterializationResponsibility notifyEmitted and addDepend… (PR #79952)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 29 23:05:40 PST 2024


https://github.com/lhames created https://github.com/llvm/llvm-project/pull/79952

…encies.

Removes the MaterializationResponsibility::addDependencies and addDependenciesForAll methods, and transfers dependency registration to the notifyEmitted operation. The new dependency registration allows dependencies to be specified for arbitrary subsets of the MaterializationResponsibility's symbols (rather than just single symbols or all symbols) via an array of SymbolDependenceGroups (pairs of symbol sets and corresponding dependencies for that set).

This patch aims to both improve emission performance and simplify dependence tracking. By eliminating some states (e.g. symbols having registered dependencies but not yet being resolved or emitted) we make some errors impossible by construction, and reduce the number of error cases that we need to check. NonOwningSymbolStringPtrs are used for dependence tracking under the session lock, which should reduce ref-counting operations, and intra-emit dependencies are resolved outside the session lock, which should provide better performance when JITing concurrently (since some dependence tracking can happen in parallel).

The Orc C API is updated to account for this change, with the LLVMOrcMaterializationResponsibilityNotifyEmitted API being modified and the LLVMOrcMaterializationResponsibilityAddDependencies and LLVMOrcMaterializationResponsibilityAddDependenciesForAll operations being removed.

>From f6aa995bbfd430fd417b3e8afdea3ea4afbdf32d Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Tue, 23 Jan 2024 12:42:12 -0800
Subject: [PATCH] [ORC] Merge MaterializationResponsibility notifyEmitted and
 addDependencies.

Removes the MaterializationResponsibility::addDependencies and
addDependenciesForAll methods, and transfers dependency registration to the
notifyEmitted operation. The new dependency registration allows dependencies to
be specified for arbitrary subsets of the MaterializationResponsibility's
symbols (rather than just single symbols or all symbols) via an array of
SymbolDependenceGroups (pairs of symbol sets and corresponding dependencies for
that set).

This patch aims to both improve emission performance and simplify dependence
tracking. By eliminating some states (e.g. symbols having registered
dependencies but not yet being resolved or emitted) we make some errors
impossible by construction, and reduce the number of error cases that we need
to check. NonOwningSymbolStringPtrs are used for dependence tracking under the
session lock, which should reduce ref-counting operations, and intra-emit
dependencies are resolved outside the session lock, which should provide better
performance when JITing concurrently (since some dependence tracking can happen
in parallel).

The Orc C API is updated to account for this change, with the
LLVMOrcMaterializationResponsibilityNotifyEmitted API being modified and the
LLVMOrcMaterializationResponsibilityAddDependencies and
LLVMOrcMaterializationResponsibilityAddDependenciesForAll operations being
removed.
---
 llvm/include/llvm-c/Orc.h                     |   57 +-
 llvm/include/llvm/ExecutionEngine/Orc/Core.h  |  128 +-
 .../llvm/ExecutionEngine/Orc/DebugUtils.h     |    3 +
 .../Orc/RTDyldObjectLinkingLayer.h            |    2 +-
 llvm/lib/ExecutionEngine/Orc/Core.cpp         | 1210 +++++++++++------
 llvm/lib/ExecutionEngine/Orc/DebugUtils.cpp   |    4 +
 .../ExecutionEngine/Orc/IndirectionUtils.cpp  |    2 +-
 .../lib/ExecutionEngine/Orc/LazyReexports.cpp |    2 +-
 .../Orc/ObjectLinkingLayer.cpp                |  134 +-
 .../ExecutionEngine/Orc/OrcV2CBindings.cpp    |   44 +-
 .../Orc/RTDyldObjectLinkingLayer.cpp          |   34 +-
 .../x86-64/LocalDependencyPropagation.s       |    9 +-
 .../ExecutionEngine/Orc/CoreAPIsTest.cpp      |  168 +--
 .../Orc/LazyCallThroughAndReexportsTest.cpp   |    2 +-
 .../ExecutionEngine/Orc/OrcCAPITest.cpp       |   26 +-
 .../Orc/ResourceTrackerTest.cpp               |   12 +-
 16 files changed, 1085 insertions(+), 752 deletions(-)

diff --git a/llvm/include/llvm-c/Orc.h b/llvm/include/llvm-c/Orc.h
index 304833cca2d43..8609a8a6d9e9d 100644
--- a/llvm/include/llvm-c/Orc.h
+++ b/llvm/include/llvm-c/Orc.h
@@ -181,6 +181,15 @@ typedef struct {
  */
 typedef LLVMOrcCDependenceMapPair *LLVMOrcCDependenceMapPairs;
 
+/**
+ * A set of symbols that share dependencies.
+ */
+typedef struct {
+  LLVMOrcCSymbolsList Symbols;
+  LLVMOrcCDependenceMapPairs Dependencies;
+  size_t NumDependencies;
+} LLVMOrcCSymbolDependenceGroup;
+
 /**
  * Lookup kind. This can be used by definition generators when deciding whether
  * to produce a definition for a requested symbol.
@@ -808,6 +817,19 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyResolved(
  * that all symbols covered by this MaterializationResponsibility instance
  * have been emitted.
  *
+ * This function takes ownership of the symbols in the Dependencies struct.
+ * This allows the following pattern...
+ *
+ *   LLVMOrcSymbolStringPoolEntryRef Names[] = {...};
+ *   LLVMOrcCDependenceMapPair Dependence = {JD, {Names, sizeof(Names)}}
+ *   LLVMOrcMaterializationResponsibilityAddDependencies(JD, Name, &Dependence,
+ * 1);
+ *
+ * ... without requiring cleanup of the elements of the Names array afterwards.
+ *
+ * The client is still responsible for deleting the Dependencies.Names arrays,
+ * and the Dependencies array itself.
+ *
  * This method will return an error if any symbols being resolved have been
  * moved to the error state due to the failure of a dependency. If this
  * method returns an error then clients should log it and call
@@ -817,7 +839,8 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyResolved(
  * LLVMErrorSuccess.
  */
 LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyEmitted(
-    LLVMOrcMaterializationResponsibilityRef MR);
+    LLVMOrcMaterializationResponsibilityRef MR,
+    LLVMOrcCSymbolDependenceGroup *SymbolDepGroups, size_t NumSymbolDepGroups);
 
 /**
  * Attempt to claim responsibility for new definitions. This method can be
@@ -870,38 +893,6 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityDelegate(
     LLVMOrcSymbolStringPoolEntryRef *Symbols, size_t NumSymbols,
     LLVMOrcMaterializationResponsibilityRef *Result);
 
-/**
- * Adds dependencies to a symbol that the MaterializationResponsibility is
- * responsible for.
- *
- * This function takes ownership of Dependencies struct. The Names
- * array have been retained for this function. This allows the following
- * pattern...
- *
- *   LLVMOrcSymbolStringPoolEntryRef Names[] = {...};
- *   LLVMOrcCDependenceMapPair Dependence = {JD, {Names, sizeof(Names)}}
- *   LLVMOrcMaterializationResponsibilityAddDependencies(JD, Name, &Dependence,
- * 1);
- *
- * ... without requiring cleanup of the elements of the Names array afterwards.
- *
- * The client is still responsible for deleting the Dependencies.Names array
- * itself.
- */
-void LLVMOrcMaterializationResponsibilityAddDependencies(
-    LLVMOrcMaterializationResponsibilityRef MR,
-    LLVMOrcSymbolStringPoolEntryRef Name,
-    LLVMOrcCDependenceMapPairs Dependencies, size_t NumPairs);
-
-/**
- * Adds dependencies to all symbols that the MaterializationResponsibility is
- * responsible for. See LLVMOrcMaterializationResponsibilityAddDependencies for
- * notes about memory responsibility.
- */
-void LLVMOrcMaterializationResponsibilityAddDependenciesForAll(
-    LLVMOrcMaterializationResponsibilityRef MR,
-    LLVMOrcCDependenceMapPairs Dependencies, size_t NumPairs);
-
 /**
  * Create a "bare" JITDylib.
  *
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Core.h b/llvm/include/llvm/ExecutionEngine/Orc/Core.h
index 6a9bcf712169d..80a9613c20ceb 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Core.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Core.h
@@ -436,6 +436,27 @@ class FailedToMaterialize : public ErrorInfo<FailedToMaterialize> {
   std::shared_ptr<SymbolDependenceMap> Symbols;
 };
 
+/// Used to report failure due to unsatisfiable symbol dependencies.
+class UnsatisfiedSymbolDependencies
+    : public ErrorInfo<UnsatisfiedSymbolDependencies> {
+public:
+  static char ID;
+
+  UnsatisfiedSymbolDependencies(std::shared_ptr<SymbolStringPool> SSP,
+                                JITDylibSP JD, SymbolNameSet FailedSymbols,
+                                SymbolDependenceMap BadDeps,
+                                std::string Explanation);
+  std::error_code convertToErrorCode() const override;
+  void log(raw_ostream &OS) const override;
+
+private:
+  std::shared_ptr<SymbolStringPool> SSP;
+  JITDylibSP JD;
+  SymbolNameSet FailedSymbols;
+  SymbolDependenceMap BadDeps;
+  std::string Explanation;
+};
+
 /// Used to notify clients when symbols can not be found during a lookup.
 class SymbolsNotFound : public ErrorInfo<SymbolsNotFound> {
 public:
@@ -517,6 +538,13 @@ class UnexpectedSymbolDefinitions : public ErrorInfo<UnexpectedSymbolDefinitions
   SymbolNameVector Symbols;
 };
 
+/// A set of symbols and the their dependencies. Used to describe dependencies
+/// for the MaterializationResponsibility::notifyEmitted operation.
+struct SymbolDependenceGroup {
+  SymbolNameSet Symbols;
+  SymbolDependenceMap Dependencies;
+};
+
 /// Tracks responsibility for materialization, and mediates interactions between
 /// MaterializationUnits and JDs.
 ///
@@ -587,13 +615,22 @@ class MaterializationResponsibility {
   /// that all symbols covered by this MaterializationResponsibility instance
   /// have been emitted.
   ///
+  /// The DepGroups array describes the dependencies of symbols being emitted on
+  /// symbols that are outside this MaterializationResponsibility object. Each
+  /// group consists of a pair of a set of symbols and a SymbolDependenceMap
+  /// that describes the dependencies for the symbols in the first set. The
+  /// elements of DepGroups must be non-overlapping (no symbol should appear in
+  /// more than one of hte symbol sets), but do not have to be exhaustive. Any
+  /// symbol in this MaterializationResponsibility object that is not covered
+  /// by an entry will be take to have no dependencies.
+  ///
   /// This method will return an error if any symbols being resolved have been
   /// moved to the error state due to the failure of a dependency. If this
   /// method returns an error then clients should log it and call
   /// failMaterialize. If no dependencies have been registered for the
   /// symbols covered by this MaterializationResponsibility then this method
   /// is guaranteed to return Error::success() and can be wrapped with cantFail.
-  Error notifyEmitted();
+  Error notifyEmitted(ArrayRef<SymbolDependenceGroup> DepGroups);
 
   /// Attempt to claim responsibility for new definitions. This method can be
   /// used to claim responsibility for symbols that are added to a
@@ -628,12 +665,6 @@ class MaterializationResponsibility {
   Expected<std::unique_ptr<MaterializationResponsibility>>
   delegate(const SymbolNameSet &Symbols);
 
-  void addDependencies(const SymbolStringPtr &Name,
-                       const SymbolDependenceMap &Dependencies);
-
-  /// Add dependencies that apply to all symbols covered by this instance.
-  void addDependenciesForAll(const SymbolDependenceMap &Dependencies);
-
 private:
   /// Create a MaterializationResponsibility for the given JITDylib and
   ///        initial symbols.
@@ -1185,9 +1216,29 @@ class JITDylib : public ThreadSafeRefCountedBase<JITDylib>,
   using UnmaterializedInfosList =
       std::vector<std::shared_ptr<UnmaterializedInfo>>;
 
+  struct EmissionDepUnit {
+    EmissionDepUnit(JITDylib &JD) : JD(&JD) {}
+
+    JITDylib *JD = nullptr;
+    DenseMap<NonOwningSymbolStringPtr, JITSymbolFlags> Symbols;
+    DenseMap<JITDylib *, DenseSet<NonOwningSymbolStringPtr>> Dependencies;
+  };
+
+  struct EmissionDepUnitInfo {
+    std::shared_ptr<EmissionDepUnit> EDU;
+    DenseSet<EmissionDepUnit *> IntraEmitUsers;
+    DenseMap<JITDylib *, DenseSet<NonOwningSymbolStringPtr>> NewDeps;
+  };
+
+  // Information about not-yet-ready symbol.
+  // * DefiningEDU will point to the EmissionDepUnit that defines the symbol.
+  // * DependantEDUs will hold pointers to any EmissionDepUnits currently
+  //   waiting on this symbol.
+  // * Pending queries holds any not-yet-completed queries that include this
+  //   symbol.
   struct MaterializingInfo {
-    SymbolDependenceMap Dependants;
-    SymbolDependenceMap UnemittedDependencies;
+    std::shared_ptr<EmissionDepUnit> DefiningEDU;
+    DenseSet<EmissionDepUnit *> DependantEDUs;
 
     void addQuery(std::shared_ptr<AsynchronousSymbolQuery> Q);
     void removeQuery(const AsynchronousSymbolQuery &Q);
@@ -1278,17 +1329,8 @@ class JITDylib : public ThreadSafeRefCountedBase<JITDylib>,
 
   Error resolve(MaterializationResponsibility &MR, const SymbolMap &Resolved);
 
-  Error emit(MaterializationResponsibility &MR, const SymbolFlagsMap &Emitted);
-
   void unlinkMaterializationResponsibility(MaterializationResponsibility &MR);
 
-  using FailedSymbolsWorklist =
-      std::vector<std::pair<JITDylib *, SymbolStringPtr>>;
-
-  static std::pair<AsynchronousSymbolQuerySet,
-                   std::shared_ptr<SymbolDependenceMap>>
-      failSymbols(FailedSymbolsWorklist);
-
   ExecutionSession &ES;
   enum { Open, Closing, Closed } State = Open;
   std::mutex GeneratorsMutex;
@@ -1767,19 +1809,42 @@ class ExecutionSession {
   SymbolNameSet OL_getRequestedSymbols(const MaterializationResponsibility &MR);
   Error OL_notifyResolved(MaterializationResponsibility &MR,
                           const SymbolMap &Symbols);
-  Error OL_notifyEmitted(MaterializationResponsibility &MR);
+
+  using EDUInfosMap =
+      DenseMap<JITDylib::EmissionDepUnit *, JITDylib::EmissionDepUnitInfo>;
+  void propagateExtraEmitDeps(std::deque<JITDylib::EmissionDepUnit *> Worklist,
+                              EDUInfosMap &EDUInfos);
+  EDUInfosMap simplifyDepGroups(MaterializationResponsibility &MR,
+                                ArrayRef<SymbolDependenceGroup> EmittedDeps);
+  void IL_makeEDUReady(std::shared_ptr<JITDylib::EmissionDepUnit> EDU,
+                       JITDylib::AsynchronousSymbolQuerySet &Queries);
+  void IL_makeEDUEmitted(std::shared_ptr<JITDylib::EmissionDepUnit> EDU,
+                         JITDylib::AsynchronousSymbolQuerySet &Queries);
+  bool IL_removeEDUDependence(JITDylib::EmissionDepUnit &EDU, JITDylib &DepJD,
+                              NonOwningSymbolStringPtr DepSym,
+                              EDUInfosMap &EDUInfos);
+
+  static Error makeJDClosedError(JITDylib::EmissionDepUnit &EDU,
+                                 JITDylib &ClosedJD);
+  static Error makeUnsatisfiedDepsError(JITDylib::EmissionDepUnit &EDU,
+                                        JITDylib &BadJD, SymbolNameSet BadDeps);
+
+  Expected<JITDylib::AsynchronousSymbolQuerySet>
+  IL_emit(MaterializationResponsibility &MR, EDUInfosMap EDUInfos);
+  Error OL_notifyEmitted(MaterializationResponsibility &MR,
+                         ArrayRef<SymbolDependenceGroup> EmittedDeps);
+
   Error OL_defineMaterializing(MaterializationResponsibility &MR,
                                SymbolFlagsMap SymbolFlags);
+
+  std::pair<JITDylib::AsynchronousSymbolQuerySet,
+            std::shared_ptr<SymbolDependenceMap>>
+  IL_failSymbols(JITDylib &JD, const SymbolNameVector &SymbolsToFail);
   void OL_notifyFailed(MaterializationResponsibility &MR);
   Error OL_replace(MaterializationResponsibility &MR,
                    std::unique_ptr<MaterializationUnit> MU);
   Expected<std::unique_ptr<MaterializationResponsibility>>
   OL_delegate(MaterializationResponsibility &MR, const SymbolNameSet &Symbols);
-  void OL_addDependencies(MaterializationResponsibility &MR,
-                          const SymbolStringPtr &Name,
-                          const SymbolDependenceMap &Dependencies);
-  void OL_addDependenciesForAll(MaterializationResponsibility &MR,
-                                const SymbolDependenceMap &Dependencies);
 
 #ifndef NDEBUG
   void dumpDispatchInfo(Task &T);
@@ -1965,8 +2030,9 @@ inline Error MaterializationResponsibility::notifyResolved(
   return getExecutionSession().OL_notifyResolved(*this, Symbols);
 }
 
-inline Error MaterializationResponsibility::notifyEmitted() {
-  return getExecutionSession().OL_notifyEmitted(*this);
+inline Error MaterializationResponsibility::notifyEmitted(
+    ArrayRef<SymbolDependenceGroup> EmittedDeps) {
+  return getExecutionSession().OL_notifyEmitted(*this, EmittedDeps);
 }
 
 inline Error MaterializationResponsibility::defineMaterializing(
@@ -1989,16 +2055,6 @@ MaterializationResponsibility::delegate(const SymbolNameSet &Symbols) {
   return getExecutionSession().OL_delegate(*this, Symbols);
 }
 
-inline void MaterializationResponsibility::addDependencies(
-    const SymbolStringPtr &Name, const SymbolDependenceMap &Dependencies) {
-  getExecutionSession().OL_addDependencies(*this, Name, Dependencies);
-}
-
-inline void MaterializationResponsibility::addDependenciesForAll(
-    const SymbolDependenceMap &Dependencies) {
-  getExecutionSession().OL_addDependenciesForAll(*this, Dependencies);
-}
-
 } // End namespace orc
 } // End namespace llvm
 
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/DebugUtils.h b/llvm/include/llvm/ExecutionEngine/Orc/DebugUtils.h
index 53e089613dd45..035139578e08f 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/DebugUtils.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/DebugUtils.h
@@ -32,6 +32,9 @@ namespace orc {
 /// Render a SymbolStringPtr.
 raw_ostream &operator<<(raw_ostream &OS, const SymbolStringPtr &Sym);
 
+/// Render a NonOwningSymbolStringPtr.
+raw_ostream &operator<<(raw_ostream &OS, NonOwningSymbolStringPtr Sym);
+
 /// Render a SymbolNameSet.
 raw_ostream &operator<<(raw_ostream &OS, const SymbolNameSet &Symbols);
 
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h b/llvm/include/llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h
index 93ff17e346d29..b45e287c2b1ac 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h
@@ -137,7 +137,7 @@ class RTDyldObjectLinkingLayer
                  object::OwningBinary<object::ObjectFile> O,
                  std::unique_ptr<RuntimeDyld::MemoryManager> MemMgr,
                  std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo,
-                 Error Err);
+                 std::unique_ptr<SymbolDependenceMap> Deps, Error Err);
 
   Error handleRemoveResources(JITDylib &JD, ResourceKey K) override;
   void handleTransferResources(JITDylib &JD, ResourceKey DstKey,
diff --git a/llvm/lib/ExecutionEngine/Orc/Core.cpp b/llvm/lib/ExecutionEngine/Orc/Core.cpp
index 56838e9bc86d6..c9cea9288417c 100644
--- a/llvm/lib/ExecutionEngine/Orc/Core.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/Core.cpp
@@ -30,6 +30,7 @@ char SymbolsNotFound::ID = 0;
 char SymbolsCouldNotBeRemoved::ID = 0;
 char MissingSymbolDefinitions::ID = 0;
 char UnexpectedSymbolDefinitions::ID = 0;
+char UnsatisfiedSymbolDependencies::ID = 0;
 char MaterializationTask::ID = 0;
 char LookupTask::ID = 0;
 
@@ -103,6 +104,25 @@ void FailedToMaterialize::log(raw_ostream &OS) const {
   OS << "Failed to materialize symbols: " << *Symbols;
 }
 
+UnsatisfiedSymbolDependencies::UnsatisfiedSymbolDependencies(
+    std::shared_ptr<SymbolStringPool> SSP, JITDylibSP JD,
+    SymbolNameSet FailedSymbols, SymbolDependenceMap BadDeps,
+    std::string Explanation)
+    : SSP(std::move(SSP)), JD(std::move(JD)),
+      FailedSymbols(std::move(FailedSymbols)), BadDeps(std::move(BadDeps)),
+      Explanation(std::move(Explanation)) {}
+
+std::error_code UnsatisfiedSymbolDependencies::convertToErrorCode() const {
+  return orcError(OrcErrorCode::UnknownORCError);
+}
+
+void UnsatisfiedSymbolDependencies::log(raw_ostream &OS) const {
+  OS << "In " << JD->getName() << ", failed to materialize " << FailedSymbols
+     << ", due to unsatisfied dependencies " << BadDeps;
+  if (!Explanation.empty())
+    OS << " (" << Explanation << ")";
+}
+
 SymbolsNotFound::SymbolsNotFound(std::shared_ptr<SymbolStringPool> SSP,
                                  SymbolNameSet Symbols)
     : SSP(std::move(SSP)) {
@@ -276,7 +296,7 @@ void AbsoluteSymbolsMaterializationUnit::materialize(
     R->failMaterialization();
     return;
   }
-  if (auto Err = R->notifyEmitted()) {
+  if (auto Err = R->notifyEmitted({})) {
     R->getExecutionSession().reportError(std::move(Err));
     R->failMaterialization();
     return;
@@ -358,6 +378,7 @@ void ReExportsMaterializationUnit::materialize(
 
     std::unique_ptr<MaterializationResponsibility> R;
     SymbolAliasMap Aliases;
+    std::vector<SymbolDependenceGroup> SDGs;
   };
 
   // Build a list of queries to issue. In each round we build a query for the
@@ -427,14 +448,10 @@ void ReExportsMaterializationUnit::materialize(
              "Unexpected dependencies for reexports");
 
       auto &SrcJDDeps = Deps.find(&SrcJD)->second;
-      SymbolDependenceMap PerAliasDepsMap;
-      auto &PerAliasDeps = PerAliasDepsMap[&SrcJD];
 
-      for (auto &KV : QueryInfo->Aliases)
-        if (SrcJDDeps.count(KV.second.Aliasee)) {
-          PerAliasDeps = {KV.second.Aliasee};
-          QueryInfo->R->addDependencies(KV.first, PerAliasDepsMap);
-        }
+      for (auto &[Alias, AliasInfo] : QueryInfo->Aliases)
+        if (SrcJDDeps.count(AliasInfo.Aliasee))
+          QueryInfo->SDGs.push_back({{Alias}, {{&SrcJD, {AliasInfo.Aliasee}}}});
     };
 
     auto OnComplete = [QueryInfo](Expected<SymbolMap> Result) {
@@ -457,7 +474,7 @@ void ReExportsMaterializationUnit::materialize(
           QueryInfo->R->failMaterialization();
           return;
         }
-        if (auto Err = QueryInfo->R->notifyEmitted()) {
+        if (auto Err = QueryInfo->R->notifyEmitted(QueryInfo->SDGs)) {
           ES.reportError(std::move(Err));
           QueryInfo->R->failMaterialization();
           return;
@@ -874,83 +891,6 @@ JITDylib::getRequestedSymbols(const SymbolFlagsMap &SymbolFlags) const {
   });
 }
 
-void JITDylib::addDependencies(const SymbolStringPtr &Name,
-                               const SymbolDependenceMap &Dependencies) {
-  ES.runSessionLocked([&]() {
-    assert(Symbols.count(Name) && "Name not in symbol table");
-    assert(Symbols[Name].getState() < SymbolState::Emitted &&
-           "Can not add dependencies for a symbol that is not materializing");
-
-    LLVM_DEBUG({
-      dbgs() << "In " << getName() << " adding dependencies for " << *Name
-             << ": " << Dependencies << "\n";
-    });
-
-    // If Name is already in an error state then just bail out.
-    if (Symbols[Name].getFlags().hasError())
-      return;
-
-    auto &MI = MaterializingInfos[Name];
-    assert(Symbols[Name].getState() != SymbolState::Emitted &&
-           "Can not add dependencies to an emitted symbol");
-
-    bool DependsOnSymbolInErrorState = false;
-
-    // Register dependencies, record whether any depenendency is in the error
-    // state.
-    for (auto &KV : Dependencies) {
-      assert(KV.first && "Null JITDylib in dependency?");
-      auto &OtherJITDylib = *KV.first;
-      auto &DepsOnOtherJITDylib = MI.UnemittedDependencies[&OtherJITDylib];
-
-      for (auto &OtherSymbol : KV.second) {
-
-        // Check the sym entry for the dependency.
-        auto OtherSymI = OtherJITDylib.Symbols.find(OtherSymbol);
-
-        // Assert that this symbol exists and has not reached the ready state
-        // already.
-        assert(OtherSymI != OtherJITDylib.Symbols.end() &&
-               "Dependency on unknown symbol");
-
-        auto &OtherSymEntry = OtherSymI->second;
-
-        // If the other symbol is already in the Ready state then there's no
-        // dependency to add.
-        if (OtherSymEntry.getState() == SymbolState::Ready)
-          continue;
-
-        // If the dependency is in an error state then note this and continue,
-        // we will move this symbol to the error state below.
-        if (OtherSymEntry.getFlags().hasError()) {
-          DependsOnSymbolInErrorState = true;
-          continue;
-        }
-
-        // If the dependency was not in the error state then add it to
-        // our list of dependencies.
-        auto &OtherMI = OtherJITDylib.MaterializingInfos[OtherSymbol];
-
-        if (OtherSymEntry.getState() == SymbolState::Emitted)
-          transferEmittedNodeDependencies(MI, Name, OtherMI);
-        else if (&OtherJITDylib != this || OtherSymbol != Name) {
-          OtherMI.Dependants[this].insert(Name);
-          DepsOnOtherJITDylib.insert(OtherSymbol);
-        }
-      }
-
-      if (DepsOnOtherJITDylib.empty())
-        MI.UnemittedDependencies.erase(&OtherJITDylib);
-    }
-
-    // If this symbol dependended on any symbols in the error state then move
-    // this symbol to the error state too.
-    if (DependsOnSymbolInErrorState)
-      Symbols[Name].setFlags(Symbols[Name].getFlags() |
-                             JITSymbolFlags::HasError);
-  });
-}
-
 Error JITDylib::resolve(MaterializationResponsibility &MR,
                         const SymbolMap &Resolved) {
   AsynchronousSymbolQuerySet CompletedQueries;
@@ -1050,161 +990,6 @@ Error JITDylib::resolve(MaterializationResponsibility &MR,
   return Error::success();
 }
 
-Error JITDylib::emit(MaterializationResponsibility &MR,
-                     const SymbolFlagsMap &Emitted) {
-  AsynchronousSymbolQuerySet CompletedQueries;
-  DenseMap<JITDylib *, SymbolNameVector> ReadySymbols;
-
-  if (auto Err = ES.runSessionLocked([&, this]() -> Error {
-        if (MR.RT->isDefunct())
-          return make_error<ResourceTrackerDefunct>(MR.RT);
-
-        if (State != Open)
-          return make_error<StringError>("JITDylib " + getName() +
-                                             " is defunct",
-                                         inconvertibleErrorCode());
-
-        SymbolNameSet SymbolsInErrorState;
-        std::vector<SymbolTable::iterator> Worklist;
-
-        // Scan to build worklist, record any symbols in the erorr state.
-        for (const auto &KV : Emitted) {
-          auto &Name = KV.first;
-
-          auto SymI = Symbols.find(Name);
-          assert(SymI != Symbols.end() && "No symbol table entry for Name");
-
-          if (SymI->second.getFlags().hasError())
-            SymbolsInErrorState.insert(Name);
-          else
-            Worklist.push_back(SymI);
-        }
-
-        // If any symbols were in the error state then bail out.
-        if (!SymbolsInErrorState.empty()) {
-          auto FailedSymbolsDepMap = std::make_shared<SymbolDependenceMap>();
-          (*FailedSymbolsDepMap)[this] = std::move(SymbolsInErrorState);
-          return make_error<FailedToMaterialize>(
-              getExecutionSession().getSymbolStringPool(),
-              std::move(FailedSymbolsDepMap));
-        }
-
-        // Otherwise update dependencies and move to the emitted state.
-        while (!Worklist.empty()) {
-          auto SymI = Worklist.back();
-          Worklist.pop_back();
-
-          auto &Name = SymI->first;
-          auto &SymEntry = SymI->second;
-
-          // Move symbol to the emitted state.
-          assert(((SymEntry.getFlags().hasMaterializationSideEffectsOnly() &&
-                   SymEntry.getState() == SymbolState::Materializing) ||
-                  SymEntry.getState() == SymbolState::Resolved) &&
-                 "Emitting from state other than Resolved");
-          SymEntry.setState(SymbolState::Emitted);
-
-          auto MII = MaterializingInfos.find(Name);
-
-          // If this symbol has no MaterializingInfo then it's trivially ready.
-          // Update its state and continue.
-          if (MII == MaterializingInfos.end()) {
-            SymEntry.setState(SymbolState::Ready);
-            continue;
-          }
-
-          auto &MI = MII->second;
-
-          // For each dependant, transfer this node's emitted dependencies to
-          // it. If the dependant node is ready (i.e. has no unemitted
-          // dependencies) then notify any pending queries.
-          for (auto &KV : MI.Dependants) {
-            auto &DependantJD = *KV.first;
-            auto &DependantJDReadySymbols = ReadySymbols[&DependantJD];
-            for (auto &DependantName : KV.second) {
-              auto DependantMII =
-                  DependantJD.MaterializingInfos.find(DependantName);
-              assert(DependantMII != DependantJD.MaterializingInfos.end() &&
-                     "Dependant should have MaterializingInfo");
-
-              auto &DependantMI = DependantMII->second;
-
-              // Remove the dependant's dependency on this node.
-              assert(DependantMI.UnemittedDependencies.count(this) &&
-                     "Dependant does not have an unemitted dependencies record "
-                     "for "
-                     "this JITDylib");
-              assert(DependantMI.UnemittedDependencies[this].count(Name) &&
-                     "Dependant does not count this symbol as a dependency?");
-
-              DependantMI.UnemittedDependencies[this].erase(Name);
-              if (DependantMI.UnemittedDependencies[this].empty())
-                DependantMI.UnemittedDependencies.erase(this);
-
-              // Transfer unemitted dependencies from this node to the
-              // dependant.
-              DependantJD.transferEmittedNodeDependencies(DependantMI,
-                                                          DependantName, MI);
-
-              auto DependantSymI = DependantJD.Symbols.find(DependantName);
-              assert(DependantSymI != DependantJD.Symbols.end() &&
-                     "Dependant has no entry in the Symbols table");
-              auto &DependantSymEntry = DependantSymI->second;
-
-              // If the dependant is emitted and this node was the last of its
-              // unemitted dependencies then the dependant node is now ready, so
-              // notify any pending queries on the dependant node.
-              if (DependantSymEntry.getState() == SymbolState::Emitted &&
-                  DependantMI.UnemittedDependencies.empty()) {
-                assert(DependantMI.Dependants.empty() &&
-                       "Dependants should be empty by now");
-
-                // Since this dependant is now ready, we erase its
-                // MaterializingInfo and update its materializing state.
-                DependantSymEntry.setState(SymbolState::Ready);
-                DependantJDReadySymbols.push_back(DependantName);
-
-                for (auto &Q :
-                     DependantMI.takeQueriesMeeting(SymbolState::Ready)) {
-                  Q->notifySymbolMetRequiredState(
-                      DependantName, DependantSymI->second.getSymbol());
-                  if (Q->isComplete())
-                    CompletedQueries.insert(Q);
-                  Q->removeQueryDependence(DependantJD, DependantName);
-                }
-                DependantJD.MaterializingInfos.erase(DependantMII);
-              }
-            }
-          }
-
-          auto &ThisJDReadySymbols = ReadySymbols[this];
-          MI.Dependants.clear();
-          if (MI.UnemittedDependencies.empty()) {
-            SymI->second.setState(SymbolState::Ready);
-            ThisJDReadySymbols.push_back(Name);
-            for (auto &Q : MI.takeQueriesMeeting(SymbolState::Ready)) {
-              Q->notifySymbolMetRequiredState(Name, SymI->second.getSymbol());
-              if (Q->isComplete())
-                CompletedQueries.insert(Q);
-              Q->removeQueryDependence(*this, Name);
-            }
-            MaterializingInfos.erase(MII);
-          }
-        }
-
-        return Error::success();
-      }))
-    return Err;
-
-  // Otherwise notify all the completed queries.
-  for (auto &Q : CompletedQueries) {
-    assert(Q->isComplete() && "Q is not complete");
-    Q->handleComplete(ES);
-  }
-
-  return Error::success();
-}
-
 void JITDylib::unlinkMaterializationResponsibility(
     MaterializationResponsibility &MR) {
   ES.runSessionLocked([&]() {
@@ -1217,123 +1002,6 @@ void JITDylib::unlinkMaterializationResponsibility(
   });
 }
 
-std::pair<JITDylib::AsynchronousSymbolQuerySet,
-          std::shared_ptr<SymbolDependenceMap>>
-JITDylib::failSymbols(FailedSymbolsWorklist Worklist) {
-  AsynchronousSymbolQuerySet FailedQueries;
-  auto FailedSymbolsMap = std::make_shared<SymbolDependenceMap>();
-
-  while (!Worklist.empty()) {
-    assert(Worklist.back().first && "Failed JITDylib can not be null");
-    auto &JD = *Worklist.back().first;
-    auto Name = std::move(Worklist.back().second);
-    Worklist.pop_back();
-
-    (*FailedSymbolsMap)[&JD].insert(Name);
-
-    // Look up the symbol to fail.
-    auto SymI = JD.Symbols.find(Name);
-
-    // It's possible that this symbol has already been removed, e.g. if a
-    // materialization failure happens concurrently with a ResourceTracker or
-    // JITDylib removal. In that case we can safely skip this symbol and
-    // continue.
-    if (SymI == JD.Symbols.end())
-      continue;
-    auto &Sym = SymI->second;
-
-    // Move the symbol into the error state.
-    // Note that this may be redundant: The symbol might already have been
-    // moved to this state in response to the failure of a dependence.
-    Sym.setFlags(Sym.getFlags() | JITSymbolFlags::HasError);
-
-    // FIXME: Come up with a sane mapping of state to
-    // presence-of-MaterializingInfo so that we can assert presence / absence
-    // here, rather than testing it.
-    auto MII = JD.MaterializingInfos.find(Name);
-
-    if (MII == JD.MaterializingInfos.end())
-      continue;
-
-    auto &MI = MII->second;
-
-    // Move all dependants to the error state and disconnect from them.
-    for (auto &KV : MI.Dependants) {
-      auto &DependantJD = *KV.first;
-      for (auto &DependantName : KV.second) {
-        assert(DependantJD.Symbols.count(DependantName) &&
-               "No symbol table entry for DependantName");
-        auto &DependantSym = DependantJD.Symbols[DependantName];
-        DependantSym.setFlags(DependantSym.getFlags() |
-                              JITSymbolFlags::HasError);
-
-        assert(DependantJD.MaterializingInfos.count(DependantName) &&
-               "No MaterializingInfo for dependant");
-        auto &DependantMI = DependantJD.MaterializingInfos[DependantName];
-
-        auto UnemittedDepI = DependantMI.UnemittedDependencies.find(&JD);
-        assert(UnemittedDepI != DependantMI.UnemittedDependencies.end() &&
-               "No UnemittedDependencies entry for this JITDylib");
-        assert(UnemittedDepI->second.count(Name) &&
-               "No UnemittedDependencies entry for this symbol");
-        UnemittedDepI->second.erase(Name);
-        if (UnemittedDepI->second.empty())
-          DependantMI.UnemittedDependencies.erase(UnemittedDepI);
-
-        // If this symbol is already in the emitted state then we need to
-        // take responsibility for failing its queries, so add it to the
-        // worklist.
-        if (DependantSym.getState() == SymbolState::Emitted) {
-          assert(DependantMI.Dependants.empty() &&
-                 "Emitted symbol should not have dependants");
-          Worklist.push_back(std::make_pair(&DependantJD, DependantName));
-        }
-      }
-    }
-    MI.Dependants.clear();
-
-    // Disconnect from all unemitted depenencies.
-    for (auto &KV : MI.UnemittedDependencies) {
-      auto &UnemittedDepJD = *KV.first;
-      for (auto &UnemittedDepName : KV.second) {
-        auto UnemittedDepMII =
-            UnemittedDepJD.MaterializingInfos.find(UnemittedDepName);
-        assert(UnemittedDepMII != UnemittedDepJD.MaterializingInfos.end() &&
-               "Missing MII for unemitted dependency");
-        assert(UnemittedDepMII->second.Dependants.count(&JD) &&
-               "JD not listed as a dependant of unemitted dependency");
-        assert(UnemittedDepMII->second.Dependants[&JD].count(Name) &&
-               "Name is not listed as a dependant of unemitted dependency");
-        UnemittedDepMII->second.Dependants[&JD].erase(Name);
-        if (UnemittedDepMII->second.Dependants[&JD].empty())
-          UnemittedDepMII->second.Dependants.erase(&JD);
-      }
-    }
-    MI.UnemittedDependencies.clear();
-
-    // Collect queries to be failed for this MII.
-    AsynchronousSymbolQueryList ToDetach;
-    for (auto &Q : MII->second.pendingQueries()) {
-      // Add the query to the list to be failed and detach it.
-      FailedQueries.insert(Q);
-      ToDetach.push_back(Q);
-    }
-    for (auto &Q : ToDetach)
-      Q->detach();
-
-    assert(MI.Dependants.empty() &&
-           "Can not delete MaterializingInfo with dependants still attached");
-    assert(MI.UnemittedDependencies.empty() &&
-           "Can not delete MaterializingInfo with unemitted dependencies "
-           "still attached");
-    assert(!MI.hasQueriesPending() &&
-           "Can not delete MaterializingInfo with queries pending");
-    JD.MaterializingInfos.erase(MII);
-  }
-
-  return std::make_pair(std::move(FailedQueries), std::move(FailedSymbolsMap));
-}
-
 void JITDylib::setLinkOrder(JITDylibSearchOrder NewLinkOrder,
                             bool LinkAgainstThisJITDylibFirst) {
   ES.runSessionLocked([&]() {
@@ -1504,16 +1172,38 @@ void JITDylib::dump(raw_ostream &OS) {
          << " pending queries: { ";
       for (const auto &Q : KV.second.pendingQueries())
         OS << Q.get() << " (" << Q->getRequiredState() << ") ";
-      OS << "}\n      Dependants:\n";
-      for (auto &KV2 : KV.second.Dependants)
-        OS << "        " << KV2.first->getName() << ": " << KV2.second << "\n";
-      OS << "      Unemitted Dependencies:\n";
-      for (auto &KV2 : KV.second.UnemittedDependencies)
-        OS << "        " << KV2.first->getName() << ": " << KV2.second << "\n";
+      OS << "}\n      Defining EDU: ";
+      if (KV.second.DefiningEDU) {
+        OS << KV.second.DefiningEDU.get() << " { ";
+        for (auto &[Name, Flags] : KV.second.DefiningEDU->Symbols)
+          OS << Name << " ";
+        OS << "}\n";
+        OS << "        Dependencies:\n";
+        if (!KV.second.DefiningEDU->Dependencies.empty()) {
+          for (auto &[DepJD, Deps] : KV.second.DefiningEDU->Dependencies) {
+            OS << "          " << DepJD->getName() << ": [ ";
+            for (auto &Dep : Deps)
+              OS << Dep << " ";
+            OS << "]\n";
+          }
+        } else
+          OS << "          none\n";
+      } else
+        OS << "none\n";
+      OS << "      Dependant EDUs:\n";
+      if (!KV.second.DependantEDUs.empty()) {
+        for (auto &DependantEDU : KV.second.DependantEDUs) {
+          OS << "        " << DependantEDU << ": "
+             << DependantEDU->JD->getName() << " { ";
+          for (auto &[Name, Flags] : DependantEDU->Symbols)
+            OS << Name << " ";
+          OS << "}\n";
+        }
+      } else
+        OS << "        none\n";
       assert((Symbols[KV.first].getState() != SymbolState::Ready ||
-              !KV.second.pendingQueries().empty() ||
-              !KV.second.Dependants.empty() ||
-              !KV.second.UnemittedDependencies.empty()) &&
+              (KV.second.pendingQueries().empty() && !KV.second.DefiningEDU &&
+               !KV.second.DependantEDUs.empty())) &&
              "Stale materializing info entry");
     }
   });
@@ -1568,7 +1258,7 @@ JITDylib::removeTracker(ResourceTracker &RT) {
   assert(State != Closed && "JD is defunct");
 
   SymbolNameVector SymbolsToRemove;
-  std::vector<std::pair<JITDylib *, SymbolStringPtr>> SymbolsToFail;
+  SymbolNameVector SymbolsToFail;
 
   if (&RT == DefaultTracker.get()) {
     SymbolNameSet TrackedSymbols;
@@ -1599,11 +1289,12 @@ JITDylib::removeTracker(ResourceTracker &RT) {
     // If there is a MaterializingInfo then collect any queries to fail.
     auto MII = MaterializingInfos.find(Sym);
     if (MII != MaterializingInfos.end())
-      SymbolsToFail.push_back({this, Sym});
+      SymbolsToFail.push_back(Sym);
   }
 
   AsynchronousSymbolQuerySet QueriesToFail;
-  auto Result = failSymbols(std::move(SymbolsToFail));
+  auto Result = ES.runSessionLocked(
+      [&]() { return ES.IL_failSymbols(*this, std::move(SymbolsToFail)); });
 
   // Removed symbols should be taken out of the table altogether.
   for (auto &Sym : SymbolsToRemove) {
@@ -1793,32 +1484,6 @@ void JITDylib::detachQueryHelper(AsynchronousSymbolQuery &Q,
   }
 }
 
-void JITDylib::transferEmittedNodeDependencies(
-    MaterializingInfo &DependantMI, const SymbolStringPtr &DependantName,
-    MaterializingInfo &EmittedMI) {
-  for (auto &KV : EmittedMI.UnemittedDependencies) {
-    auto &DependencyJD = *KV.first;
-    SymbolNameSet *UnemittedDependenciesOnDependencyJD = nullptr;
-
-    for (auto &DependencyName : KV.second) {
-      auto &DependencyMI = DependencyJD.MaterializingInfos[DependencyName];
-
-      // Do not add self dependencies.
-      if (&DependencyMI == &DependantMI)
-        continue;
-
-      // If we haven't looked up the dependencies for DependencyJD yet, do it
-      // now and cache the result.
-      if (!UnemittedDependenciesOnDependencyJD)
-        UnemittedDependenciesOnDependencyJD =
-            &DependantMI.UnemittedDependencies[&DependencyJD];
-
-      DependencyMI.Dependants[this].insert(DependantName);
-      UnemittedDependenciesOnDependencyJD->insert(DependencyName);
-    }
-  }
-}
-
 Platform::~Platform() = default;
 
 Expected<DenseMap<JITDylib *, SymbolMap>> Platform::lookupInitSymbols(
@@ -3034,16 +2699,583 @@ Error ExecutionSession::OL_notifyResolved(MaterializationResponsibility &MR,
   return MR.JD.resolve(MR, Symbols);
 }
 
-Error ExecutionSession::OL_notifyEmitted(MaterializationResponsibility &MR) {
+void ExecutionSession::propagateExtraEmitDeps(
+    std::deque<JITDylib::EmissionDepUnit *> Worklist, EDUInfosMap &EDUInfos) {
+
+  // Iterate to a fixed-poient to propagate extra-emit dependencies through the
+  // EDU graph.
+  while (!Worklist.empty()) {
+    auto &EDU = *Worklist.front();
+    Worklist.pop_front();
+
+    assert(EDUInfos.count(&EDU) && "No info entry for EDU");
+    auto &EDUInfo = EDUInfos[&EDU];
+
+    // Propagate new dependencies to users.
+    for (auto *UserEDU : EDUInfo.IntraEmitUsers) {
+
+      // UserEDUInfo only present if UserEDU has its own users.
+      JITDylib::EmissionDepUnitInfo *UserEDUInfo = nullptr;
+      {
+        auto UserEDUInfoItr = EDUInfos.find(UserEDU);
+        if (UserEDUInfoItr != EDUInfos.end())
+          UserEDUInfo = &UserEDUInfoItr->second;
+      }
+
+      for (auto &[DepJD, Deps] : EDUInfo.NewDeps) {
+        auto &UserEDUDepsForJD = UserEDU->Dependencies[DepJD];
+        DenseSet<NonOwningSymbolStringPtr> *UserEDUNewDepsForJD = nullptr;
+        for (auto Dep : Deps) {
+          if (UserEDUDepsForJD.insert(Dep).second) {
+            if (UserEDUInfo) {
+              if (!UserEDUNewDepsForJD) {
+                // If UserEDU has no new deps then it's not in the worklist
+                // yet, so add it.
+                if (UserEDUInfo->NewDeps.empty())
+                  Worklist.push_back(UserEDU);
+                UserEDUNewDepsForJD = &UserEDUInfo->NewDeps[DepJD];
+              }
+              // Add (DepJD, Dep) to NewDeps.
+              UserEDUNewDepsForJD->insert(Dep);
+            }
+          }
+        }
+      }
+    }
+
+    EDUInfo.NewDeps.clear();
+  }
+}
+
+// Note: This method modifies the emitted set.
+ExecutionSession::EDUInfosMap ExecutionSession::simplifyDepGroups(
+    MaterializationResponsibility &MR,
+    ArrayRef<SymbolDependenceGroup> EmittedDeps) {
+
+  auto &TargetJD = MR.getTargetJITDylib();
+
+  // 1. Build initial EmissionDepUnit -> EmissionDepUnitInfo and
+  //    Symbol -> EmissionDepUnit mappings.
+  DenseMap<JITDylib::EmissionDepUnit *, JITDylib::EmissionDepUnitInfo> EDUInfos;
+  EDUInfos.reserve(EmittedDeps.size());
+  DenseMap<NonOwningSymbolStringPtr, JITDylib::EmissionDepUnit *> EDUForSymbol;
+  for (auto &DG : EmittedDeps) {
+    assert(!DG.Symbols.empty() && "DepGroup does not cover any symbols");
+
+    // Skip empty EDUs.
+    if (DG.Dependencies.empty())
+      continue;
+
+    auto TmpEDU = std::make_shared<JITDylib::EmissionDepUnit>(TargetJD);
+    auto &EDUInfo = EDUInfos[TmpEDU.get()];
+    EDUInfo.EDU = std::move(TmpEDU);
+    for (const auto &Symbol : DG.Symbols) {
+      NonOwningSymbolStringPtr NonOwningSymbol(Symbol);
+      assert(!EDUForSymbol.count(NonOwningSymbol) &&
+             "Symbol should not appear in more than one SymbolDependenceGroup");
+      assert(MR.getSymbols().count(Symbol) &&
+             "Symbol in DepGroups not in the emitted set");
+      auto NewlyEmittedItr = MR.getSymbols().find(Symbol);
+      EDUInfo.EDU->Symbols[NonOwningSymbol] = NewlyEmittedItr->second;
+      EDUForSymbol[NonOwningSymbol] = EDUInfo.EDU.get();
+    }
+  }
+
+  // 2. Build a "residual" EDU to cover all symbols that have no dependencies.
+  {
+    DenseMap<NonOwningSymbolStringPtr, JITSymbolFlags> ResidualSymbolFlags;
+    for (auto &[Sym, Flags] : MR.getSymbols()) {
+      if (!EDUForSymbol.count(NonOwningSymbolStringPtr(Sym)))
+        ResidualSymbolFlags[NonOwningSymbolStringPtr(Sym)] = Flags;
+    }
+    if (!ResidualSymbolFlags.empty()) {
+      auto ResidualEDU = std::make_shared<JITDylib::EmissionDepUnit>(TargetJD);
+      ResidualEDU->Symbols = std::move(ResidualSymbolFlags);
+      auto &ResidualEDUInfo = EDUInfos[ResidualEDU.get()];
+      ResidualEDUInfo.EDU = std::move(ResidualEDU);
+
+      // If the residual EDU is the only one then bail out early.
+      if (EDUInfos.size() == 1)
+        return EDUInfos;
+
+      // Otherwise add the residual EDU to the EDUForSymbol map.
+      for (auto &[Sym, Flags] : ResidualEDUInfo.EDU->Symbols)
+        EDUForSymbol[Sym] = ResidualEDUInfo.EDU.get();
+    }
+  }
+
+#ifndef NDEBUG
+  assert(EDUForSymbol.size() == MR.getSymbols().size() &&
+         "MR symbols not fully covered by EDUs?");
+  for (auto &[Sym, Flags] : MR.getSymbols()) {
+    assert(EDUForSymbol.count(NonOwningSymbolStringPtr(Sym)) &&
+           "Sym in MR not covered by EDU");
+  }
+#endif // NDEBUG
+
+  // 3. Use the DegGroups DepGroups array to build a graph of dependencies
+  //    between EmissionDepUnits in this finalization. We want to remove these
+  //    intra-finalization uses, propagating dependencies on symbols outside
+  //    this finalization. Add EDUs to the worklist.
+  for (auto &DG : EmittedDeps) {
+
+    // Skip SymbolDependenceGroups with no dependencies.
+    if (DG.Dependencies.empty())
+      continue;
+
+    assert(EDUForSymbol.count(NonOwningSymbolStringPtr(*DG.Symbols.begin())) &&
+           "No EDU for DG");
+    auto &EDU =
+        *EDUForSymbol.find(NonOwningSymbolStringPtr(*DG.Symbols.begin()))
+             ->second;
+
+    for (auto &[DepJD, Deps] : DG.Dependencies) {
+      DenseSet<NonOwningSymbolStringPtr> NewDepsForJD;
+
+      assert(!Deps.empty() && "Dependence set for DepJD is empty");
+
+      if (DepJD != &TargetJD) {
+        // DepJD is some other JITDylib.There can't be any intra-finalization
+        // edges here, so just skip.
+        for (auto &Dep : Deps)
+          NewDepsForJD.insert(NonOwningSymbolStringPtr(Dep));
+      } else {
+        // DepJD is the Target JITDylib. Check for intra-finaliztaion edges,
+        // skipping any and recording the intra-finalization use instead.
+        for (auto &Dep : Deps) {
+          NonOwningSymbolStringPtr NonOwningDep(Dep);
+          auto I = EDUForSymbol.find(NonOwningDep);
+          if (I == EDUForSymbol.end()) {
+            if (!MR.getSymbols().count(Dep))
+              NewDepsForJD.insert(NonOwningDep);
+            continue;
+          }
+
+          if (I->second != &EDU)
+            EDUInfos[I->second].IntraEmitUsers.insert(&EDU);
+        }
+      }
+
+      if (!NewDepsForJD.empty())
+        EDU.Dependencies[DepJD] = std::move(NewDepsForJD);
+    }
+  }
+
+  // 4. Build the worklist.
+  std::deque<JITDylib::EmissionDepUnit *> Worklist;
+  for (auto &[EDU, EDUInfo] : EDUInfos) {
+    // If this EDU has extra-finalization dependencies and intra-finalization
+    // users then add it to the worklist.
+    if (!EDU->Dependencies.empty()) {
+      auto I = EDUInfos.find(EDU);
+      if (I != EDUInfos.end()) {
+        auto &EDUInfo = I->second;
+        if (!EDUInfo.IntraEmitUsers.empty()) {
+          EDUInfo.NewDeps = EDU->Dependencies;
+          Worklist.push_back(EDU);
+        }
+      }
+    }
+  }
+
+  // 4. Propagate dependencies through the EDU graph.
+  propagateExtraEmitDeps(Worklist, EDUInfos);
+
+  return EDUInfos;
+}
+
+void ExecutionSession::IL_makeEDUReady(
+    std::shared_ptr<JITDylib::EmissionDepUnit> EDU,
+    JITDylib::AsynchronousSymbolQuerySet &Queries) {
+
+  // The symbols for this EDU are ready.
+  auto &JD = *EDU->JD;
+
+  for (auto &[Sym, Flags] : EDU->Symbols) {
+    assert(JD.Symbols.count(SymbolStringPtr(Sym)) &&
+           "JD does not have an entry for Sym");
+    auto &Entry = JD.Symbols[SymbolStringPtr(Sym)];
+
+    assert(((Entry.getFlags().hasMaterializationSideEffectsOnly() &&
+             Entry.getState() == SymbolState::Materializing) ||
+            Entry.getState() == SymbolState::Resolved ||
+            Entry.getState() == SymbolState::Emitted) &&
+           "Emitting from state other than Resolved");
+
+    Entry.setState(SymbolState::Ready);
+
+    auto MII = JD.MaterializingInfos.find(SymbolStringPtr(Sym));
+
+    // Check for pending queries.
+    if (MII == JD.MaterializingInfos.end())
+      continue;
+    auto &MI = MII->second;
+
+    for (auto &Q : MI.takeQueriesMeeting(SymbolState::Ready)) {
+      Q->notifySymbolMetRequiredState(SymbolStringPtr(Sym), Entry.getSymbol());
+      if (Q->isComplete())
+        Queries.insert(Q);
+      Q->removeQueryDependence(JD, SymbolStringPtr(Sym));
+    }
+
+    JD.MaterializingInfos.erase(MII);
+  }
+}
+
+void ExecutionSession::IL_makeEDUEmitted(
+    std::shared_ptr<JITDylib::EmissionDepUnit> EDU,
+    JITDylib::AsynchronousSymbolQuerySet &Queries) {
+
+  // The symbols for this EDU are emitted, but not ready.
+  auto &JD = *EDU->JD;
+
+  for (auto &[Sym, Flags] : EDU->Symbols) {
+    assert(JD.Symbols.count(SymbolStringPtr(Sym)) &&
+           "JD does not have an entry for Sym");
+    auto &Entry = JD.Symbols[SymbolStringPtr(Sym)];
+
+    assert(((Entry.getFlags().hasMaterializationSideEffectsOnly() &&
+             Entry.getState() == SymbolState::Materializing) ||
+            Entry.getState() == SymbolState::Resolved ||
+            Entry.getState() == SymbolState::Emitted) &&
+           "Emitting from state other than Resolved");
+
+    if (Entry.getState() == SymbolState::Emitted) {
+      // This was already emitted, so we can skip the rest of this loop.
+#ifndef NDEBUG
+      for (auto &[Sym, Flags] : EDU->Symbols) {
+        assert(JD.Symbols.count(SymbolStringPtr(Sym)) &&
+               "JD does not have an entry for Sym");
+        auto &Entry = JD.Symbols[SymbolStringPtr(Sym)];
+        assert(Entry.getState() == SymbolState::Emitted &&
+               "Symbols for EDU in inconsistent state");
+        assert(JD.MaterializingInfos.count(SymbolStringPtr(Sym)) &&
+               "Emitted symbol has no MI");
+        auto MI = JD.MaterializingInfos[SymbolStringPtr(Sym)];
+        assert(MI.takeQueriesMeeting(SymbolState::Emitted).empty() &&
+               "Already-emitted symbol has waiting-on-emitted queries");
+      }
+#endif // NDEBUG
+      break;
+    }
+
+    Entry.setState(SymbolState::Emitted);
+    auto &MI = JD.MaterializingInfos[SymbolStringPtr(Sym)];
+    MI.DefiningEDU = EDU;
+
+    for (auto &Q : MI.takeQueriesMeeting(SymbolState::Emitted)) {
+      Q->notifySymbolMetRequiredState(SymbolStringPtr(Sym), Entry.getSymbol());
+      if (Q->isComplete())
+        Queries.insert(Q);
+      Q->removeQueryDependence(JD, SymbolStringPtr(Sym));
+    }
+  }
+
+  for (auto &[DepJD, Deps] : EDU->Dependencies) {
+    for (auto &Dep : Deps)
+      DepJD->MaterializingInfos[SymbolStringPtr(Dep)].DependantEDUs.insert(
+          EDU.get());
+  }
+}
+
+/// Removes the given dependence from EDU. If EDU's dependence set becomes
+/// empty then this function adds an entry for it to the EDUInfos map.
+/// Returns true if a new EDUInfosMap entry is added.
+bool ExecutionSession::IL_removeEDUDependence(JITDylib::EmissionDepUnit &EDU,
+                                              JITDylib &DepJD,
+                                              NonOwningSymbolStringPtr DepSym,
+                                              EDUInfosMap &EDUInfos) {
+  assert(EDU.Dependencies.count(&DepJD) &&
+         "JD does not appear in Dependencies of DependantEDU");
+  assert(EDU.Dependencies[&DepJD].count(DepSym) &&
+         "Symbol does not appear in Dependencies of DependantEDU");
+  auto &JDDeps = EDU.Dependencies[&DepJD];
+  JDDeps.erase(DepSym);
+  if (JDDeps.empty()) {
+    EDU.Dependencies.erase(&DepJD);
+    if (EDU.Dependencies.empty()) {
+      // If the dependencies set has become empty then EDU _may_ be ready
+      // (we won't know for sure until we've propagated the extra-emit deps).
+      // Create an EDUInfo for it (if it doesn't have one already) so that
+      // it'll be visited after propagation.
+      auto &DepEDUInfo = EDUInfos[&EDU];
+      if (!DepEDUInfo.EDU) {
+        assert(EDU.JD->Symbols.count(
+                   SymbolStringPtr(EDU.Symbols.begin()->first)) &&
+               "Missing symbol entry for first symbol in EDU");
+        auto DepEDUFirstMI = EDU.JD->MaterializingInfos.find(
+            SymbolStringPtr(EDU.Symbols.begin()->first));
+        assert(DepEDUFirstMI != EDU.JD->MaterializingInfos.end() &&
+               "Missing MI for first symbol in DependantEDU");
+        DepEDUInfo.EDU = DepEDUFirstMI->second.DefiningEDU;
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+Error ExecutionSession::makeJDClosedError(JITDylib::EmissionDepUnit &EDU,
+                                          JITDylib &ClosedJD) {
+  SymbolNameSet FailedSymbols;
+  for (auto &[Sym, Flags] : EDU.Symbols)
+    FailedSymbols.insert(SymbolStringPtr(Sym));
+  SymbolDependenceMap BadDeps;
+  for (auto &Dep : EDU.Dependencies[&ClosedJD])
+    BadDeps[&ClosedJD].insert(SymbolStringPtr(Dep));
+  return make_error<UnsatisfiedSymbolDependencies>(
+      ClosedJD.getExecutionSession().getSymbolStringPool(), EDU.JD,
+      std::move(FailedSymbols), std::move(BadDeps),
+      ClosedJD.getName() + " is closed");
+}
+
+Error ExecutionSession::makeUnsatisfiedDepsError(JITDylib::EmissionDepUnit &EDU,
+                                                 JITDylib &BadJD,
+                                                 SymbolNameSet BadDeps) {
+  SymbolNameSet FailedSymbols;
+  for (auto &[Sym, Flags] : EDU.Symbols)
+    FailedSymbols.insert(SymbolStringPtr(Sym));
+  SymbolDependenceMap BadDepsMap;
+  BadDepsMap[&BadJD] = std::move(BadDeps);
+  return make_error<UnsatisfiedSymbolDependencies>(
+      BadJD.getExecutionSession().getSymbolStringPool(), &BadJD,
+      std::move(FailedSymbols), std::move(BadDepsMap),
+      "dependencies removed or in error state");
+}
+
+Expected<JITDylib::AsynchronousSymbolQuerySet>
+ExecutionSession::IL_emit(MaterializationResponsibility &MR,
+                          EDUInfosMap EDUInfos) {
+
+  if (MR.RT->isDefunct())
+    return make_error<ResourceTrackerDefunct>(MR.RT);
+
+  auto &TargetJD = MR.getTargetJITDylib();
+  if (TargetJD.State != JITDylib::Open)
+    return make_error<StringError>("JITDylib " + TargetJD.getName() +
+                                       " is defunct",
+                                   inconvertibleErrorCode());
+
+  // Walk all EDUs:
+  // 1. Verifying that dependencies are available (not removed or in the error
+  //    state.
+  // 2. Removing any dependencies that are already Ready.
+  // 3. Lifting any EDUs for Emitted symbols into the EDUInfos map.
+  // 4. Finding any dependant EDUs and lifting them into the EDUInfos map.
+  std::deque<JITDylib::EmissionDepUnit *> Worklist;
+  for (auto &[EDU, _] : EDUInfos)
+    Worklist.push_back(EDU);
+
+  for (auto *EDU : Worklist) {
+    auto *EDUInfo = &EDUInfos[EDU];
+
+    SmallVector<JITDylib *> DepJDsToRemove;
+    for (auto &[DepJD, Deps] : EDU->Dependencies) {
+      if (DepJD->State != JITDylib::Open)
+        return makeJDClosedError(*EDU, *DepJD);
+
+      SymbolNameSet BadDeps;
+      SmallVector<NonOwningSymbolStringPtr> DepsToRemove;
+      for (auto &Dep : Deps) {
+        auto DepEntryItr = DepJD->Symbols.find(SymbolStringPtr(Dep));
+
+        // If this dep has been removed or moved to the error state then add it
+        // to the bad deps set. We aggregate these bad deps for more
+        // comprehensive error messages.
+        if (DepEntryItr == DepJD->Symbols.end() ||
+            DepEntryItr->second.getFlags().hasError()) {
+          BadDeps.insert(SymbolStringPtr(Dep));
+          continue;
+        }
+
+        // If this dep isn't emitted yet then just add it to the NewDeps set to
+        // be propagated.
+        auto &DepEntry = DepEntryItr->second;
+        if (DepEntry.getState() < SymbolState::Emitted) {
+          EDUInfo->NewDeps[DepJD].insert(Dep);
+          continue;
+        }
+
+        // This dep has been emitted, so add it to the list to be removed from
+        // EDU.
+        DepsToRemove.push_back(Dep);
+
+        // If Dep is Ready then there's nothing further to do.
+        if (DepEntry.getState() == SymbolState::Ready) {
+          assert(!DepJD->MaterializingInfos.count(SymbolStringPtr(Dep)) &&
+                 "Unexpected MaterializationInfo attached to ready symbol");
+          continue;
+        }
+
+        // If we get here thene Dep is Emitted. We need to look up its defining
+        // EDU and add this EDU to the defining EDU's list of users (this means
+        // creating an EDUInfos entry if the defining EDU doesn't have one
+        // already).
+        assert(DepJD->MaterializingInfos.count(SymbolStringPtr(Dep)) &&
+               "Expected MaterializationInfo for emitted dependency");
+        auto &DepMI = DepJD->MaterializingInfos[SymbolStringPtr(Dep)];
+        assert(DepMI.DefiningEDU &&
+               "Emitted symbol does not have a defining EDU");
+        assert(!DepMI.DefiningEDU->Dependencies.empty() &&
+               "Emitted symbol has empty dependencies (should be ready)");
+        assert(DepMI.DependantEDUs.empty() &&
+               "Already-emitted symbol has dependant EDUs?");
+        auto &DepEDUInfo = EDUInfos[DepMI.DefiningEDU.get()];
+        if (!DepEDUInfo.EDU) {
+          // No EDUInfo yet -- build initial entry, and reset the EDUInfo
+          // pointer, which we will have invalidated.
+          EDUInfo = &EDUInfos[EDU];
+          DepEDUInfo.EDU = DepMI.DefiningEDU;
+          for (auto &[DepDepJD, DepDeps] : DepEDUInfo.EDU->Dependencies) {
+            if (DepDepJD == &TargetJD) {
+              for (auto &DepDep : DepDeps)
+                if (!MR.getSymbols().count(SymbolStringPtr(DepDep)))
+                  DepEDUInfo.NewDeps[DepDepJD].insert(DepDep);
+            } else
+              DepEDUInfo.NewDeps[DepDepJD] = DepDeps;
+          }
+        }
+        DepEDUInfo.IntraEmitUsers.insert(EDU);
+      }
+
+      // Some dependencies were removed or in an error state -- error out.
+      if (!BadDeps.empty())
+        return makeUnsatisfiedDepsError(*EDU, *DepJD, std::move(BadDeps));
+
+      // Remove the emitted / ready deps from DepJD.
+      for (auto &Dep : DepsToRemove)
+        Deps.erase(Dep);
+
+      // If there are no further deps in DepJD then flag it for removal too.
+      if (Deps.empty())
+        DepJDsToRemove.push_back(DepJD);
+    }
+
+    // Remove any JDs whose dependence sets have become empty.
+    for (auto &DepJD : DepJDsToRemove) {
+      assert(EDU->Dependencies.count(DepJD) &&
+             "Trying to remove non-existent dep entries");
+      EDU->Dependencies.erase(DepJD);
+    }
+
+    // Now look for users of this EDU.
+    DenseSet<JITDylib::EmissionDepUnit *> *EDUUsers = nullptr;
+    for (auto &[Sym, Flags] : EDU->Symbols) {
+      assert(TargetJD.Symbols.count(SymbolStringPtr(Sym)) &&
+             "Sym not present in symbol table");
+      assert((TargetJD.Symbols[SymbolStringPtr(Sym)].getState() ==
+                  SymbolState::Resolved ||
+              TargetJD.Symbols[SymbolStringPtr(Sym)]
+                  .getFlags()
+                  .hasMaterializationSideEffectsOnly()) &&
+             "Emitting symbol not in the resolved state");
+      assert(!TargetJD.Symbols[SymbolStringPtr(Sym)].getFlags().hasError() &&
+             "Symbol is already in an error state");
+
+      auto MII = TargetJD.MaterializingInfos.find(SymbolStringPtr(Sym));
+      if (MII == TargetJD.MaterializingInfos.end() ||
+          MII->second.DependantEDUs.empty())
+        continue;
+
+      if (!EDUUsers)
+        EDUUsers = &EDUInfo->IntraEmitUsers;
+      for (auto &DependantEDU : MII->second.DependantEDUs) {
+        if (IL_removeEDUDependence(*DependantEDU, TargetJD, Sym, EDUInfos)) {
+          EDUInfo = &EDUInfos[EDU];
+          EDUUsers = &EDUInfo->IntraEmitUsers;
+        }
+        EDUUsers->insert(DependantEDU);
+      }
+      MII->second.DependantEDUs.clear();
+    }
+  }
+
+  Worklist.clear();
+  for (auto &[EDU, EDUInfo] : EDUInfos) {
+    if (!EDUInfo.IntraEmitUsers.empty() && !EDU->Dependencies.empty()) {
+      if (EDUInfo.NewDeps.empty())
+        EDUInfo.NewDeps = EDU->Dependencies;
+      Worklist.push_back(EDU);
+    }
+  }
+
+  propagateExtraEmitDeps(Worklist, EDUInfos);
+
+  JITDylib::AsynchronousSymbolQuerySet CompletedQueries;
+
+  // Extract completed queries and lodge not-yet-ready EDUs in the
+  // session.
+  for (auto &[EDU, EDUInfo] : EDUInfos) {
+    if (EDU->Dependencies.empty())
+      IL_makeEDUReady(std::move(EDUInfo.EDU), CompletedQueries);
+    else
+      IL_makeEDUEmitted(std::move(EDUInfo.EDU), CompletedQueries);
+  }
+
+  return std::move(CompletedQueries);
+}
+
+Error ExecutionSession::OL_notifyEmitted(
+    MaterializationResponsibility &MR,
+    ArrayRef<SymbolDependenceGroup> DepGroups) {
   LLVM_DEBUG({
     dbgs() << "In " << MR.JD.getName() << " emitting " << MR.SymbolFlags
            << "\n";
+    if (!DepGroups.empty()) {
+      dbgs() << "  Initial dependencies:\n";
+      for (auto &SDG : DepGroups) {
+        dbgs() << "    Symbols: " << SDG.Symbols
+               << ", Dependencies: " << SDG.Dependencies << "\n";
+      }
+    }
   });
 
-  if (auto Err = MR.JD.emit(MR, MR.SymbolFlags))
-    return Err;
+#ifndef NDEBUG
+  SymbolNameSet Visited;
+  for (auto &DG : DepGroups) {
+    for (auto &Sym : DG.Symbols) {
+      assert(MR.SymbolFlags.count(Sym) &&
+             "DG contains dependence for symbol outside this MR");
+      assert(Visited.insert(Sym).second &&
+             "DG contains duplicate entries for Name");
+    }
+  }
+#endif // NDEBUG
+
+  auto EDUInfos = simplifyDepGroups(MR, DepGroups);
+
+  LLVM_DEBUG({
+    dbgs() << "  Simplified dependencies:\n";
+    for (auto &[EDU, EDUInfo] : EDUInfos) {
+      dbgs() << "    Symbols: { ";
+      for (auto &[Sym, Flags] : EDU->Symbols)
+        dbgs() << Sym << " ";
+      dbgs() << "}, Dependencies: { ";
+      for (auto &[DepJD, Deps] : EDU->Dependencies) {
+        dbgs() << "(" << DepJD->getName() << ", { ";
+        for (auto &Dep : Deps)
+          dbgs() << Dep << " ";
+        dbgs() << "}) ";
+      }
+      dbgs() << "}\n";
+    }
+  });
+
+  auto CompletedQueries =
+      runSessionLocked([&]() { return IL_emit(MR, EDUInfos); });
+
+  // On error bail out.
+  if (!CompletedQueries)
+    return CompletedQueries.takeError();
 
   MR.SymbolFlags.clear();
+
+  // Otherwise notify all the completed queries.
+  for (auto &Q : *CompletedQueries) {
+    assert(Q->isComplete() && "Q is not complete");
+    Q->handleComplete(*this);
+  }
+
   return Error::success();
 }
 
@@ -3064,6 +3296,143 @@ Error ExecutionSession::OL_defineMaterializing(
     return AcceptedDefs.takeError();
 }
 
+std::pair<JITDylib::AsynchronousSymbolQuerySet,
+          std::shared_ptr<SymbolDependenceMap>>
+ExecutionSession::IL_failSymbols(JITDylib &JD,
+                                 const SymbolNameVector &SymbolsToFail) {
+  JITDylib::AsynchronousSymbolQuerySet FailedQueries;
+  auto FailedSymbolsMap = std::make_shared<SymbolDependenceMap>();
+  auto ExtractFailedQueries = [&](JITDylib::MaterializingInfo &MI) {
+    JITDylib::AsynchronousSymbolQueryList ToDetach;
+    for (auto &Q : MI.pendingQueries()) {
+      // Add the query to the list to be failed and detach it.
+      FailedQueries.insert(Q);
+      ToDetach.push_back(Q);
+    }
+    for (auto &Q : ToDetach)
+      Q->detach();
+    assert(!MI.hasQueriesPending() && "Queries still pending after detach");
+  };
+
+  for (auto &Name : SymbolsToFail) {
+    (*FailedSymbolsMap)[&JD].insert(Name);
+
+    // Look up the symbol to fail.
+    auto SymI = JD.Symbols.find(Name);
+
+    // FIXME: Revisit this. We should be able to assert sequencing between
+    //        ResourceTracker removal and symbol failure.
+    //
+    // It's possible that this symbol has already been removed, e.g. if a
+    // materialization failure happens concurrently with a ResourceTracker or
+    // JITDylib removal. In that case we can safely skip this symbol and
+    // continue.
+    if (SymI == JD.Symbols.end())
+      continue;
+    auto &Sym = SymI->second;
+
+    // If the symbol is already in the error state then we must have visited
+    // it earlier.
+    if (Sym.getFlags().hasError()) {
+      assert(!JD.MaterializingInfos.count(Name) &&
+             "Symbol in error state still has MaterializingInfo");
+      continue;
+    }
+
+    // Move the symbol into the error state.
+    Sym.setFlags(Sym.getFlags() | JITSymbolFlags::HasError);
+
+    // FIXME: Come up with a sane mapping of state to
+    // presence-of-MaterializingInfo so that we can assert presence / absence
+    // here, rather than testing it.
+    auto MII = JD.MaterializingInfos.find(Name);
+    if (MII == JD.MaterializingInfos.end())
+      continue;
+
+    auto &MI = MII->second;
+
+    // Collect queries to be failed for this MII.
+    ExtractFailedQueries(MI);
+
+    if (MI.DefiningEDU) {
+      // If there is a DefiningEDU for this symbol then remove this
+      // symbol from it.
+      assert(MI.DependantEDUs.empty() &&
+             "Symbol with DefiningEDU should not have DependantEDUs");
+      assert(Sym.getState() >= SymbolState::Emitted &&
+             "Symbol has EDU, should have been emitted");
+      assert(MI.DefiningEDU->Symbols.count(NonOwningSymbolStringPtr(Name)) &&
+             "Symbol does not appear in its DefiningEDU");
+      MI.DefiningEDU->Symbols.erase(NonOwningSymbolStringPtr(Name));
+      MI.DefiningEDU = nullptr;
+    } else {
+      // Otherwise if there are any EDUs waiting on this symbol then move
+      // those symbols to the error state too, and deregister them from the
+      // symbols that they depend on.
+      // Note: We use a copy of DependantEDUs here since we'll be removing
+      // from the original set as we go.
+      for (auto &DependantEDU : MI.DependantEDUs) {
+
+        // Remove DependantEDU from all of its users DependantEDUs lists.
+        for (auto &[JD, Syms] : DependantEDU->Dependencies) {
+          for (auto Sym : Syms) {
+            assert(JD->Symbols.count(SymbolStringPtr(Sym)) && "Sym not in JD?");
+            assert(JD->MaterializingInfos.count(SymbolStringPtr(Sym)) &&
+                   "DependantEDU not registered with symbol it depends on");
+            auto SymMI = JD->MaterializingInfos[SymbolStringPtr(Sym)];
+            assert(SymMI.DependantEDUs.count(DependantEDU) &&
+                   "DependantEDU missing from DependantEDUs list");
+            SymMI.DependantEDUs.erase(DependantEDU);
+          }
+        }
+
+        // Move any symbols defined by DependantEDU into the error state and
+        // fail any queries waiting on them.
+        auto &DepJD = *DependantEDU->JD;
+        auto DepEDUSymbols = std::move(DependantEDU->Symbols);
+        for (auto &[DepName, Flags] : DepEDUSymbols) {
+          auto DepSymItr = DepJD.Symbols.find(SymbolStringPtr(DepName));
+          assert(DepSymItr != DepJD.Symbols.end() &&
+                 "Symbol not present in table");
+          auto &DepSym = DepSymItr->second;
+
+          assert(DepSym.getState() >= SymbolState::Emitted &&
+                 "Symbol has EDU, should have been emitted");
+          assert(!DepSym.getFlags().hasError() &&
+                 "Symbol is already in the error state?");
+          DepSym.setFlags(DepSym.getFlags() | JITSymbolFlags::HasError);
+          (*FailedSymbolsMap)[&DepJD].insert(SymbolStringPtr(DepName));
+
+          // This symbol has a defining EDU so its MaterializingInfo object must
+          // exist.
+          auto DepMIItr =
+              DepJD.MaterializingInfos.find(SymbolStringPtr(DepName));
+          assert(DepMIItr != DepJD.MaterializingInfos.end() &&
+                 "Symbol has defining EDU but not MaterializingInfo");
+          auto &DepMI = DepMIItr->second;
+          assert(DepMI.DefiningEDU.get() == DependantEDU &&
+                 "Bad EDU dependence edge");
+          assert(DepMI.DependantEDUs.empty() &&
+                 "Symbol was emitted, should not have any DependantEDUs");
+          ExtractFailedQueries(DepMI);
+          DepJD.MaterializingInfos.erase(SymbolStringPtr(DepName));
+        }
+      }
+
+      MI.DependantEDUs.clear();
+    }
+
+    assert(!MI.DefiningEDU && "DefiningEDU should have been reset");
+    assert(MI.DependantEDUs.empty() &&
+           "DependantEDUs should have been removed above");
+    assert(!MI.hasQueriesPending() &&
+           "Can not delete MaterializingInfo with queries pending");
+    JD.MaterializingInfos.erase(Name);
+  }
+
+  return std::make_pair(std::move(FailedQueries), std::move(FailedSymbolsMap));
+}
+
 void ExecutionSession::OL_notifyFailed(MaterializationResponsibility &MR) {
 
   LLVM_DEBUG({
@@ -3071,25 +3440,23 @@ void ExecutionSession::OL_notifyFailed(MaterializationResponsibility &MR) {
            << MR.SymbolFlags << "\n";
   });
 
-  JITDylib::FailedSymbolsWorklist Worklist;
+  if (MR.SymbolFlags.empty())
+    return;
 
-  for (auto &KV : MR.SymbolFlags)
-    Worklist.push_back(std::make_pair(&MR.JD, KV.first));
+  SymbolNameVector SymbolsToFail;
+  for (auto &[Name, Flags] : MR.SymbolFlags)
+    SymbolsToFail.push_back(Name);
   MR.SymbolFlags.clear();
 
-  if (Worklist.empty())
-    return;
-
   JITDylib::AsynchronousSymbolQuerySet FailedQueries;
   std::shared_ptr<SymbolDependenceMap> FailedSymbols;
 
-  runSessionLocked([&]() {
+  std::tie(FailedQueries, FailedSymbols) = runSessionLocked([&]() {
     // If the tracker is defunct then there's nothing to do here.
     if (MR.RT->isDefunct())
-      return;
-
-    std::tie(FailedQueries, FailedSymbols) =
-        JITDylib::failSymbols(std::move(Worklist));
+      return std::pair<JITDylib::AsynchronousSymbolQuerySet,
+                       std::shared_ptr<SymbolDependenceMap>>();
+    return IL_failSymbols(MR.getTargetJITDylib(), SymbolsToFail);
   });
 
   for (auto &Q : FailedQueries)
@@ -3140,29 +3507,6 @@ ExecutionSession::OL_delegate(MaterializationResponsibility &MR,
                         std::move(DelegatedInitSymbol));
 }
 
-void ExecutionSession::OL_addDependencies(
-    MaterializationResponsibility &MR, const SymbolStringPtr &Name,
-    const SymbolDependenceMap &Dependencies) {
-  LLVM_DEBUG({
-    dbgs() << "Adding dependencies for " << Name << ": " << Dependencies
-           << "\n";
-  });
-  assert(MR.SymbolFlags.count(Name) &&
-         "Symbol not covered by this MaterializationResponsibility instance");
-  MR.JD.addDependencies(Name, Dependencies);
-}
-
-void ExecutionSession::OL_addDependenciesForAll(
-    MaterializationResponsibility &MR,
-    const SymbolDependenceMap &Dependencies) {
-  LLVM_DEBUG({
-    dbgs() << "Adding dependencies for all symbols in " << MR.SymbolFlags << ": "
-           << Dependencies << "\n";
-  });
-  for (auto &KV : MR.SymbolFlags)
-    MR.JD.addDependencies(KV.first, Dependencies);
-}
-
 #ifndef NDEBUG
 void ExecutionSession::dumpDispatchInfo(Task &T) {
   runSessionLocked([&]() {
diff --git a/llvm/lib/ExecutionEngine/Orc/DebugUtils.cpp b/llvm/lib/ExecutionEngine/Orc/DebugUtils.cpp
index aca4576422125..0f6923a7633f3 100644
--- a/llvm/lib/ExecutionEngine/Orc/DebugUtils.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/DebugUtils.cpp
@@ -142,6 +142,10 @@ raw_ostream &operator<<(raw_ostream &OS, const SymbolStringPtr &Sym) {
   return OS << *Sym;
 }
 
+raw_ostream &operator<<(raw_ostream &OS, NonOwningSymbolStringPtr Sym) {
+  return OS << *Sym;
+}
+
 raw_ostream &operator<<(raw_ostream &OS, const SymbolNameSet &Symbols) {
   return OS << printSequence(Symbols, '{', '}', PrintAll<SymbolStringPtr>());
 }
diff --git a/llvm/lib/ExecutionEngine/Orc/IndirectionUtils.cpp b/llvm/lib/ExecutionEngine/Orc/IndirectionUtils.cpp
index f9efff148df9c..119a1aa66adfc 100644
--- a/llvm/lib/ExecutionEngine/Orc/IndirectionUtils.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/IndirectionUtils.cpp
@@ -43,7 +43,7 @@ class CompileCallbackMaterializationUnit : public orc::MaterializationUnit {
     Result[Name] = {Compile(), JITSymbolFlags::Exported};
     // No dependencies, so these calls cannot fail.
     cantFail(R->notifyResolved(Result));
-    cantFail(R->notifyEmitted());
+    cantFail(R->notifyEmitted({}));
   }
 
   void discard(const JITDylib &JD, const SymbolStringPtr &Name) override {
diff --git a/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp b/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp
index d95a642934f11..693a3f33f868a 100644
--- a/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp
@@ -218,7 +218,7 @@ void LazyReexportsMaterializationUnit::materialize(
 
   // No registered dependencies, so these calls cannot fail.
   cantFail(R->notifyResolved(Stubs));
-  cantFail(R->notifyEmitted());
+  cantFail(R->notifyEmitted({}));
 }
 
 void LazyReexportsMaterializationUnit::discard(const JITDylib &JD,
diff --git a/llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp b/llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp
index b8282948034ec..fffa95ee72b71 100644
--- a/llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp
@@ -10,6 +10,7 @@
 #include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h"
 #include "llvm/ExecutionEngine/JITLink/aarch32.h"
 #include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
+#include "llvm/ExecutionEngine/Orc/DebugUtils.h"
 #include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h"
 #include "llvm/ExecutionEngine/Orc/Shared/ObjectFormats.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -216,16 +217,13 @@ class ObjectLinkingLayerJITLinkContext final : public JITLinkContext {
       }
     };
 
-    for (auto &KV : InternalNamedSymbolDeps) {
-      SymbolDependenceMap InternalDeps;
-      InternalDeps[&MR->getTargetJITDylib()] = std::move(KV.second);
-      MR->addDependencies(KV.first, InternalDeps);
-    }
-
     ES.lookup(LookupKind::Static, LinkOrder, std::move(LookupSet),
               SymbolState::Resolved, std::move(OnResolve),
               [this](const SymbolDependenceMap &Deps) {
-                registerDependencies(Deps);
+                // Translate LookupDeps map to SymbolSourceJD.
+                for (auto &[DepJD, Deps] : Deps)
+                  for (auto &DepSym : Deps)
+                    SymbolSourceJDs[NonOwningSymbolStringPtr(DepSym)] = DepJD;
               });
   }
 
@@ -329,7 +327,7 @@ class ObjectLinkingLayerJITLinkContext final : public JITLinkContext {
       MR->failMaterialization();
       return;
     }
-    if (auto Err = MR->notifyEmitted()) {
+    if (auto Err = MR->notifyEmitted(SymbolDepGroups)) {
       Layer.getExecutionSession().reportError(std::move(Err));
       MR->failMaterialization();
     }
@@ -348,8 +346,8 @@ class ObjectLinkingLayerJITLinkContext final : public JITLinkContext {
 
     Layer.modifyPassConfig(*MR, LG, Config);
 
-    Config.PostPrunePasses.push_back(
-        [this](LinkGraph &G) { return computeNamedSymbolDependencies(G); });
+    Config.PreFixupPasses.push_back(
+        [this](LinkGraph &G) { return registerDependencies(G); });
 
     return Error::success();
   }
@@ -413,9 +411,10 @@ class ObjectLinkingLayerJITLinkContext final : public JITLinkContext {
       for (auto &E : B.edges()) {
         auto &Tgt = E.getTarget();
         if (Tgt.getScope() != Scope::Local) {
-          if (Tgt.isExternal())
-            BIDCacheVal.External.insert(getInternedName(Tgt));
-          else
+          if (Tgt.isExternal()) {
+            if (Tgt.getAddress() || !Tgt.isWeaklyReferenced())
+              BIDCacheVal.External.insert(getInternedName(Tgt));
+          } else
             BIDCacheVal.Internal.insert(getInternedName(Tgt));
         }
       }
@@ -481,58 +480,92 @@ class ObjectLinkingLayerJITLinkContext final : public JITLinkContext {
     return Error::success();
   }
 
-  Error computeNamedSymbolDependencies(LinkGraph &G) {
-    auto &ES = MR->getTargetJITDylib().getExecutionSession();
+  Error registerDependencies(LinkGraph &G) {
+    auto &TargetJD = MR->getTargetJITDylib();
+    auto &ES = TargetJD.getExecutionSession();
     auto BlockDeps = computeBlockNonLocalDeps(G);
 
+    DenseSet<Block *> BlockDepsProcessed;
+    DenseMap<Block *, SymbolDependenceGroup> DepGroupForBlock;
+
     // Compute dependencies for symbols defined in the JITLink graph.
     for (auto *Sym : G.defined_symbols()) {
 
-      // Skip local symbols: we do not track dependencies for these.
+      // Skip local symbols.
       if (Sym->getScope() == Scope::Local)
         continue;
       assert(Sym->hasName() &&
              "Defined non-local jitlink::Symbol should have a name");
 
-      auto &SymDeps = BlockDeps[Sym->getBlock()];
-      if (SymDeps.External.empty() && SymDeps.Internal.empty())
+      auto &BDeps = BlockDeps[Sym->getBlock()];
+
+      // Skip symbols in blocks that don't depend on anything.
+      if (BDeps.Internal.empty() && BDeps.External.empty())
         continue;
 
-      auto SymName = ES.intern(Sym->getName());
-      if (!SymDeps.External.empty())
-        ExternalNamedSymbolDeps[SymName] = SymDeps.External;
-      if (!SymDeps.Internal.empty())
-        InternalNamedSymbolDeps[SymName] = SymDeps.Internal;
+      SymbolDependenceGroup &SDG = DepGroupForBlock[&Sym->getBlock()];
+      SDG.Symbols.insert(ES.intern(Sym->getName()));
+
+      if (!BlockDepsProcessed.count(&Sym->getBlock())) {
+        BlockDepsProcessed.insert(&Sym->getBlock());
+
+        if (!BDeps.Internal.empty())
+          SDG.Dependencies[&TargetJD] = BDeps.Internal;
+        for (auto &Dep : BDeps.External) {
+          auto DepSrcItr = SymbolSourceJDs.find(NonOwningSymbolStringPtr(Dep));
+          if (DepSrcItr != SymbolSourceJDs.end())
+            SDG.Dependencies[DepSrcItr->second].insert(Dep);
+        }
+      }
     }
 
+    SymbolDependenceGroup SynthSDG;
+
     for (auto &P : Layer.Plugins) {
       auto SynthDeps = P->getSyntheticSymbolDependencies(*MR);
       if (SynthDeps.empty())
         continue;
 
       DenseSet<Block *> BlockVisited;
-      for (auto &KV : SynthDeps) {
-        auto &Name = KV.first;
-        auto &DepsForName = KV.second;
-        for (auto *Sym : DepsForName) {
+      for (auto &[Name, DepSyms] : SynthDeps) {
+        SynthSDG.Symbols.insert(Name);
+        for (auto *Sym : DepSyms) {
           if (Sym->getScope() == Scope::Local) {
             auto &BDeps = BlockDeps[Sym->getBlock()];
             for (auto &S : BDeps.Internal)
-              InternalNamedSymbolDeps[Name].insert(S);
-            for (auto &S : BDeps.External)
-              ExternalNamedSymbolDeps[Name].insert(S);
+              SynthSDG.Dependencies[&TargetJD].insert(S);
+            for (auto &S : BDeps.External) {
+              auto DepSrcItr =
+                  SymbolSourceJDs.find(NonOwningSymbolStringPtr(S));
+              if (DepSrcItr != SymbolSourceJDs.end())
+                SynthSDG.Dependencies[DepSrcItr->second].insert(S);
+            }
           } else {
-            if (Sym->isExternal())
-              ExternalNamedSymbolDeps[Name].insert(
-                  BlockDeps.getInternedName(*Sym));
-            else
-              InternalNamedSymbolDeps[Name].insert(
-                  BlockDeps.getInternedName(*Sym));
+            auto SymName = ES.intern(Sym->getName());
+            if (Sym->isExternal()) {
+              assert(SymbolSourceJDs.count(NonOwningSymbolStringPtr(SymName)) &&
+                     "External symbol source entry missing");
+              SynthSDG
+                  .Dependencies[SymbolSourceJDs[NonOwningSymbolStringPtr(
+                      SymName)]]
+                  .insert(SymName);
+            } else
+              SynthSDG.Dependencies[&TargetJD].insert(SymName);
           }
         }
       }
     }
 
+    // Transfer SDGs to SymbolDepGroups.
+    DepGroupForBlock.reserve(DepGroupForBlock.size() + 1);
+    for (auto &[B, SDG] : DepGroupForBlock) {
+      assert(!SDG.Symbols.empty() && "SymbolDependenceGroup covers no symbols");
+      if (!SDG.Dependencies.empty())
+        SymbolDepGroups.push_back(std::move(SDG));
+    }
+    if (!SynthSDG.Symbols.empty() && !SynthSDG.Dependencies.empty())
+      SymbolDepGroups.push_back(std::move(SynthSDG));
+
     return Error::success();
   }
 
@@ -601,34 +634,13 @@ class ObjectLinkingLayerJITLinkContext final : public JITLinkContext {
                                 std::move(BlockDeps));
   }
 
-  void registerDependencies(const SymbolDependenceMap &QueryDeps) {
-    for (auto &NamedDepsEntry : ExternalNamedSymbolDeps) {
-      auto &Name = NamedDepsEntry.first;
-      auto &NameDeps = NamedDepsEntry.second;
-      SymbolDependenceMap SymbolDeps;
-
-      for (const auto &QueryDepsEntry : QueryDeps) {
-        JITDylib &SourceJD = *QueryDepsEntry.first;
-        const SymbolNameSet &Symbols = QueryDepsEntry.second;
-        auto &DepsForJD = SymbolDeps[&SourceJD];
-
-        for (const auto &S : Symbols)
-          if (NameDeps.count(S))
-            DepsForJD.insert(S);
-
-        if (DepsForJD.empty())
-          SymbolDeps.erase(&SourceJD);
-      }
-
-      MR->addDependencies(Name, SymbolDeps);
-    }
-  }
-
   ObjectLinkingLayer &Layer;
   std::unique_ptr<MaterializationResponsibility> MR;
   std::unique_ptr<MemoryBuffer> ObjBuffer;
-  DenseMap<SymbolStringPtr, SymbolNameSet> ExternalNamedSymbolDeps;
-  DenseMap<SymbolStringPtr, SymbolNameSet> InternalNamedSymbolDeps;
+  DenseMap<Block *, SymbolNameSet> ExternalBlockDeps;
+  DenseMap<Block *, SymbolNameSet> InternalBlockDeps;
+  DenseMap<NonOwningSymbolStringPtr, JITDylib *> SymbolSourceJDs;
+  std::vector<SymbolDependenceGroup> SymbolDepGroups;
 };
 
 ObjectLinkingLayer::Plugin::~Plugin() = default;
diff --git a/llvm/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp b/llvm/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp
index 72314cceedf33..453b8f86868ad 100644
--- a/llvm/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp
@@ -150,6 +150,14 @@ static LLVMJITSymbolFlags fromJITSymbolFlags(JITSymbolFlags JSF) {
   return F;
 }
 
+static SymbolNameSet toSymbolNameSet(LLVMOrcCSymbolsList Symbols) {
+  SymbolNameSet Result;
+  Result.reserve(Symbols.Length);
+  for (size_t I = 0; I != Symbols.Length; ++I)
+    Result.insert(unwrap(Symbols.Symbols[I]).moveToSymbolStringPtr());
+  return Result;
+}
+
 static SymbolMap toSymbolMap(LLVMOrcCSymbolMapPairs Syms, size_t NumPairs) {
   SymbolMap SM;
   for (size_t I = 0; I != NumPairs; ++I) {
@@ -522,14 +530,24 @@ void LLVMOrcDisposeSymbols(LLVMOrcSymbolStringPoolEntryRef *Symbols) {
 
 LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyResolved(
     LLVMOrcMaterializationResponsibilityRef MR, LLVMOrcCSymbolMapPairs Symbols,
-    size_t NumPairs) {
-  SymbolMap SM = toSymbolMap(Symbols, NumPairs);
+    size_t NumSymbols) {
+  SymbolMap SM = toSymbolMap(Symbols, NumSymbols);
   return wrap(unwrap(MR)->notifyResolved(std::move(SM)));
 }
 
 LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyEmitted(
-    LLVMOrcMaterializationResponsibilityRef MR) {
-  return wrap(unwrap(MR)->notifyEmitted());
+    LLVMOrcMaterializationResponsibilityRef MR,
+    LLVMOrcCSymbolDependenceGroup *SymbolDepGroups, size_t NumSymbolDepGroups) {
+  std::vector<SymbolDependenceGroup> SDGs;
+  SDGs.reserve(NumSymbolDepGroups);
+  for (size_t I = 0; I != NumSymbolDepGroups; ++I) {
+    SDGs.push_back(SymbolDependenceGroup());
+    auto &SDG = SDGs.back();
+    SDG.Symbols = toSymbolNameSet(SymbolDepGroups[I].Symbols);
+    SDG.Dependencies = toSymbolDependenceMap(
+        SymbolDepGroups[I].Dependencies, SymbolDepGroups[I].NumDependencies);
+  }
+  return wrap(unwrap(MR)->notifyEmitted(SDGs));
 }
 
 LLVMErrorRef LLVMOrcMaterializationResponsibilityDefineMaterializing(
@@ -567,24 +585,6 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityDelegate(
   return LLVMErrorSuccess;
 }
 
-void LLVMOrcMaterializationResponsibilityAddDependencies(
-    LLVMOrcMaterializationResponsibilityRef MR,
-    LLVMOrcSymbolStringPoolEntryRef Name,
-    LLVMOrcCDependenceMapPairs Dependencies, size_t NumPairs) {
-
-  SymbolDependenceMap SDM = toSymbolDependenceMap(Dependencies, NumPairs);
-  auto Sym = unwrap(Name).moveToSymbolStringPtr();
-  unwrap(MR)->addDependencies(Sym, SDM);
-}
-
-void LLVMOrcMaterializationResponsibilityAddDependenciesForAll(
-    LLVMOrcMaterializationResponsibilityRef MR,
-    LLVMOrcCDependenceMapPairs Dependencies, size_t NumPairs) {
-
-  SymbolDependenceMap SDM = toSymbolDependenceMap(Dependencies, NumPairs);
-  unwrap(MR)->addDependenciesForAll(SDM);
-}
-
 void LLVMOrcMaterializationResponsibilityFailMaterialization(
     LLVMOrcMaterializationResponsibilityRef MR) {
   unwrap(MR)->failMaterialization();
diff --git a/llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp b/llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp
index f9630161b95ed..0d48c34354018 100644
--- a/llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp
@@ -16,7 +16,9 @@ using namespace llvm::orc;
 
 class JITDylibSearchOrderResolver : public JITSymbolResolver {
 public:
-  JITDylibSearchOrderResolver(MaterializationResponsibility &MR) : MR(MR) {}
+  JITDylibSearchOrderResolver(MaterializationResponsibility &MR,
+                              SymbolDependenceMap &Deps)
+      : MR(MR), Deps(Deps) {}
 
   void lookup(const LookupSet &Symbols, OnResolvedFunction OnResolved) override {
     auto &ES = MR.getTargetJITDylib().getExecutionSession();
@@ -43,17 +45,13 @@ class JITDylibSearchOrderResolver : public JITSymbolResolver {
           OnResolved(Result);
         };
 
-    // Register dependencies for all symbols contained in this set.
-    auto RegisterDependencies = [&](const SymbolDependenceMap &Deps) {
-      MR.addDependenciesForAll(Deps);
-    };
-
     JITDylibSearchOrder LinkOrder;
     MR.getTargetJITDylib().withLinkOrderDo(
         [&](const JITDylibSearchOrder &LO) { LinkOrder = LO; });
-    ES.lookup(LookupKind::Static, LinkOrder, InternedSymbols,
-              SymbolState::Resolved, std::move(OnResolvedWithUnwrap),
-              RegisterDependencies);
+    ES.lookup(
+        LookupKind::Static, LinkOrder, InternedSymbols, SymbolState::Resolved,
+        std::move(OnResolvedWithUnwrap),
+        [this](const SymbolDependenceMap &LookupDeps) { Deps = LookupDeps; });
   }
 
   Expected<LookupSet> getResponsibilitySet(const LookupSet &Symbols) override {
@@ -69,6 +67,7 @@ class JITDylibSearchOrderResolver : public JITSymbolResolver {
 
 private:
   MaterializationResponsibility &MR;
+  SymbolDependenceMap &Deps;
 };
 
 } // end anonymous namespace
@@ -183,8 +182,9 @@ void RTDyldObjectLinkingLayer::emit(
   // Switch to shared ownership of MR so that it can be captured by both
   // lambdas below.
   std::shared_ptr<MaterializationResponsibility> SharedR(std::move(R));
+  auto Deps = std::make_unique<SymbolDependenceMap>();
 
-  JITDylibSearchOrderResolver Resolver(*SharedR);
+  JITDylibSearchOrderResolver Resolver(*SharedR, *Deps);
 
   jitLinkForORC(
       object::OwningBinary<object::ObjectFile>(std::move(*Obj), std::move(O)),
@@ -196,12 +196,12 @@ void RTDyldObjectLinkingLayer::emit(
         return onObjLoad(*SharedR, Obj, MemMgrRef, LoadedObjInfo,
                          ResolvedSymbols, *InternalSymbols);
       },
-      [this, SharedR, MemMgr = std::move(MemMgr)](
+      [this, SharedR, MemMgr = std::move(MemMgr), Deps = std::move(Deps)](
           object::OwningBinary<object::ObjectFile> Obj,
           std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo,
           Error Err) mutable {
         onObjEmit(*SharedR, std::move(Obj), std::move(MemMgr),
-                  std::move(LoadedObjInfo), std::move(Err));
+                  std::move(LoadedObjInfo), std::move(Deps), std::move(Err));
       });
 }
 
@@ -356,14 +356,20 @@ void RTDyldObjectLinkingLayer::onObjEmit(
     MaterializationResponsibility &R,
     object::OwningBinary<object::ObjectFile> O,
     std::unique_ptr<RuntimeDyld::MemoryManager> MemMgr,
-    std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo, Error Err) {
+    std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo,
+    std::unique_ptr<SymbolDependenceMap> Deps, Error Err) {
   if (Err) {
     getExecutionSession().reportError(std::move(Err));
     R.failMaterialization();
     return;
   }
 
-  if (auto Err = R.notifyEmitted()) {
+  SymbolDependenceGroup SDG;
+  for (auto &[Sym, Flags] : R.getSymbols())
+    SDG.Symbols.insert(Sym);
+  SDG.Dependencies = std::move(*Deps);
+
+  if (auto Err = R.notifyEmitted(SDG)) {
     getExecutionSession().reportError(std::move(Err));
     R.failMaterialization();
     return;
diff --git a/llvm/test/ExecutionEngine/JITLink/x86-64/LocalDependencyPropagation.s b/llvm/test/ExecutionEngine/JITLink/x86-64/LocalDependencyPropagation.s
index d70cdfa077517..ee7f1f72079f5 100644
--- a/llvm/test/ExecutionEngine/JITLink/x86-64/LocalDependencyPropagation.s
+++ b/llvm/test/ExecutionEngine/JITLink/x86-64/LocalDependencyPropagation.s
@@ -7,8 +7,13 @@
 # symbols: _baz depends on _foo indirectly via the local symbol _bar. We expect
 # _baz to depend on _foo, and _foo on _external_func.
 
-# CHECK-DAG: In main adding dependencies for _foo: { (main, { _external_func }) }
-# CHECK-DAG: In main adding dependencies for _baz: { (main, { _foo }) }
+# CHECK: In main emitting {{.*}}_foo{{.*}}
+# CHECK-NEXT: Initial dependencies:
+# CHECK-DAG: Symbols: { _foo }, Dependencies: { (main, { _external_func }) }
+# CHECK-DAG: Symbols: { _baz }, Dependencies: { (main, { _foo }) }
+# CHECK: Simplified dependencies:
+# CHECK-DAG: Symbols: { _foo }, Dependencies: { (main, { _external_func }) }
+# CHECK-DAG: Symbols: { _baz }, Dependencies: { (main, { _external_func }) }
 
         .section	__TEXT,__text,regular,pure_instructions
 
diff --git a/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp b/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp
index 14934e000d205..9e18b062b2f13 100644
--- a/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp
@@ -78,7 +78,7 @@ TEST_F(CoreAPIsStandardTest, BasicSuccessfulLookup) {
 
   EXPECT_FALSE(OnCompletionRun) << "Should not be ready yet";
 
-  cantFail(FooMR->notifyEmitted());
+  cantFail(FooMR->notifyEmitted({}));
 
   EXPECT_TRUE(OnCompletionRun) << "Should have been marked ready";
 }
@@ -107,7 +107,7 @@ TEST_F(CoreAPIsStandardTest, ResolveUnrequestedSymbol) {
       SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),
       [this](std::unique_ptr<MaterializationResponsibility> R) {
         cantFail(R->notifyResolved({{Foo, FooSym}, {Bar, BarSym}}));
-        cantFail(R->notifyEmitted());
+        cantFail(R->notifyEmitted({}));
       })));
 
   auto Result =
@@ -148,7 +148,7 @@ TEST_F(CoreAPIsStandardTest, MaterializationSideEffctsOnlyBasic) {
 
   EXPECT_FALSE(Result) << "Lookup returned unexpectedly";
   EXPECT_TRUE(FooR) << "Lookup failed to trigger materialization";
-  EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())
+  EXPECT_THAT_ERROR(FooR->notifyEmitted({}), Succeeded())
       << "Emission of materialization-side-effects-only symbol failed";
 
   EXPECT_TRUE(Result) << "Lookup failed to return";
@@ -195,7 +195,7 @@ TEST_F(CoreAPIsStandardTest, RemoveSymbolsTest) {
       [this](std::unique_ptr<MaterializationResponsibility> R) {
         ADD_FAILURE() << "Unexpected materialization of \"Bar\"";
         cantFail(R->notifyResolved({{Bar, BarSym}}));
-        cantFail(R->notifyEmitted());
+        cantFail(R->notifyEmitted({}));
       },
       nullptr,
       [&](const JITDylib &JD, const SymbolStringPtr &Name) {
@@ -247,7 +247,7 @@ TEST_F(CoreAPIsStandardTest, RemoveSymbolsTest) {
   }
 
   cantFail(BazR->notifyResolved({{Baz, BazSym}}));
-  cantFail(BazR->notifyEmitted());
+  cantFail(BazR->notifyEmitted({}));
   {
     // Attempt 3: Search now that all symbols are fully materialized
     // (Foo, Baz), or not yet materialized (Bar).
@@ -419,7 +419,7 @@ TEST_F(CoreAPIsStandardTest, TestThatReExportsDontUnnecessarilyMaterialize) {
       [&](std::unique_ptr<MaterializationResponsibility> R) {
         BarMaterialized = true;
         cantFail(R->notifyResolved({{Bar, BarSym}}));
-        cantFail(R->notifyEmitted());
+        cantFail(R->notifyEmitted({}));
       });
 
   cantFail(JD.define(BarMU));
@@ -481,10 +481,10 @@ TEST_F(CoreAPIsStandardTest, TestTrivialCircularDependency) {
             SymbolLookupSet({Foo}), SymbolState::Ready, OnCompletion,
             NoDependenciesToRegister);
 
-  FooR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});
   EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Succeeded())
       << "No symbols marked failed, but Foo failed to resolve";
-  EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())
+  SymbolDependenceGroup SDG({{Foo}, {{&JD, SymbolNameSet({Foo})}}});
+  EXPECT_THAT_ERROR(FooR->notifyEmitted(SDG), Succeeded())
       << "No symbols marked failed, but Foo failed to emit";
 
   EXPECT_TRUE(FooReady)
@@ -590,17 +590,6 @@ TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneJITDylib) {
             SymbolLookupSet(Baz), SymbolState::Ready, std::move(OnBazReady),
             NoDependenciesToRegister);
 
-  // Add a circular dependency: Foo -> Bar, Bar -> Baz, Baz -> Foo.
-  FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
-  BarR->addDependenciesForAll({{&JD, SymbolNameSet({Baz})}});
-  BazR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});
-
-  // Add self-dependencies for good measure. This tests that the implementation
-  // of addDependencies filters these out.
-  FooR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});
-  BarR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
-  BazR->addDependenciesForAll({{&JD, SymbolNameSet({Baz})}});
-
   // Check that nothing has been resolved yet.
   EXPECT_FALSE(FooResolved) << "\"Foo\" should not be resolved yet";
   EXPECT_FALSE(BarResolved) << "\"Bar\" should not be resolved yet";
@@ -624,10 +613,15 @@ TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneJITDylib) {
   EXPECT_FALSE(BazReady) << "\"Baz\" should not be ready yet";
 
   // Emit two of the symbols.
-  EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())
-      << "No symbols failed, but Foo failed to emit";
-  EXPECT_THAT_ERROR(BarR->notifyEmitted(), Succeeded())
-      << "No symbols failed, but Bar failed to emit";
+  {
+    SymbolDependenceGroup FooDeps({{Foo}, {{&JD, {Foo, Bar}}}});
+    EXPECT_THAT_ERROR(FooR->notifyEmitted(FooDeps), Succeeded())
+        << "No symbols failed, but Foo failed to emit";
+
+    SymbolDependenceGroup BarDeps({{Bar}, {{&JD, {Bar, Baz}}}});
+    EXPECT_THAT_ERROR(BarR->notifyEmitted(BarDeps), Succeeded())
+        << "No symbols failed, but Bar failed to emit";
+  }
 
   // Verify that nothing is ready until the circular dependence is resolved.
   EXPECT_FALSE(FooReady) << "\"Foo\" still should not be ready";
@@ -635,8 +629,11 @@ TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneJITDylib) {
   EXPECT_FALSE(BazReady) << "\"Baz\" still should not be ready";
 
   // Emit the last symbol.
-  EXPECT_THAT_ERROR(BazR->notifyEmitted(), Succeeded())
-      << "No symbols failed, but Baz failed to emit";
+  {
+    SymbolDependenceGroup BazDeps({{Baz}, {{&JD, {Baz, Foo}}}});
+    EXPECT_THAT_ERROR(BazR->notifyEmitted(BazDeps), Succeeded())
+        << "No symbols failed, but Baz failed to emit";
+  }
 
   // Verify that everything becomes ready once the circular dependence resolved.
   EXPECT_TRUE(FooReady) << "\"Foo\" should be ready now";
@@ -686,9 +683,6 @@ TEST_F(CoreAPIsStandardTest, FailureInDependency) {
             SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnBarReady),
             NoDependenciesToRegister);
 
-  // Add a dependency by Foo on Bar.
-  FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
-
   // Fail bar.
   BarR->failMaterialization();
 
@@ -697,77 +691,7 @@ TEST_F(CoreAPIsStandardTest, FailureInDependency) {
   EXPECT_FALSE(OnFooReadyRun) << "Query for \"Foo\" was run unexpectedly";
 
   // Check that we can still resolve Foo (even though it has been failed).
-  EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Failed())
-      << "Expected resolution for \"Foo\" to fail.";
-
-  FooR->failMaterialization();
-
-  // Verify that queries on Foo have now failed.
-  EXPECT_TRUE(OnFooReadyRun) << "Query for \"Foo\" was not run";
-
-  // Verify that subsequent lookups on Bar and Foo fail.
-  EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Bar}), Failed())
-      << "Lookup on failed symbol should fail";
-
-  EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())
-      << "Lookup on failed symbol should fail";
-}
-
-TEST_F(CoreAPIsStandardTest, FailureInCircularDependency) {
-  std::unique_ptr<MaterializationResponsibility> FooR;
-  std::unique_ptr<MaterializationResponsibility> BarR;
-
-  // Create a MaterializationUnit for each symbol that moves the
-  // MaterializationResponsibility into one of the locals above.
-  auto FooMU = std::make_unique<SimpleMaterializationUnit>(
-      SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
-      [&](std::unique_ptr<MaterializationResponsibility> R) {
-        FooR = std::move(R);
-      });
-
-  auto BarMU = std::make_unique<SimpleMaterializationUnit>(
-      SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
-      [&](std::unique_ptr<MaterializationResponsibility> R) {
-        BarR = std::move(R);
-      });
-
-  // Define the symbols.
-  cantFail(JD.define(FooMU));
-  cantFail(JD.define(BarMU));
-
-  bool OnFooReadyRun = false;
-  auto OnFooReady = [&](Expected<SymbolMap> Result) {
-    EXPECT_THAT_EXPECTED(std::move(Result), Failed());
-    OnFooReadyRun = true;
-  };
-
-  ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
-            SymbolLookupSet(Foo), SymbolState::Ready, std::move(OnFooReady),
-            NoDependenciesToRegister);
-
-  bool OnBarReadyRun = false;
-  auto OnBarReady = [&](Expected<SymbolMap> Result) {
-    EXPECT_THAT_EXPECTED(std::move(Result), Failed());
-    OnBarReadyRun = true;
-  };
-
-  ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
-            SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnBarReady),
-            NoDependenciesToRegister);
-
-  // Add a dependency by Foo on Bar and vice-versa.
-  FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
-  BarR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});
-
-  // Fail bar.
-  BarR->failMaterialization();
-
-  // Verify that queries on Bar failed, but queries on Foo have not yet.
-  EXPECT_TRUE(OnBarReadyRun) << "Query for \"Bar\" was not run";
-  EXPECT_FALSE(OnFooReadyRun) << "Query for \"Foo\" was run unexpectedly";
-
-  // Verify that trying to resolve Foo fails.
-  EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Failed())
+  EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Succeeded())
       << "Expected resolution for \"Foo\" to fail.";
 
   FooR->failMaterialization();
@@ -833,12 +757,14 @@ TEST_F(CoreAPIsStandardTest, AddDependencyOnFailedSymbol) {
   EXPECT_TRUE(OnBarReadyRun) << "Query for \"Bar\" was not run";
   EXPECT_FALSE(OnFooReadyRun) << "Query for \"Foo\" should not have run yet";
 
-  // Add dependency of Foo on Bar.
-  FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
+  EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Succeeded())
+      << "Expected resolution for \"Foo\" to succeed.";
 
-  // Check that we can still resolve Foo (even though it has been failed).
-  EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Failed())
-      << "Expected resolution for \"Foo\" to fail.";
+  // Check that emission of Foo fails.
+  {
+    SymbolDependenceGroup FooDeps({{Foo}, {{&JD, {Bar}}}});
+    EXPECT_THAT_ERROR(FooR->notifyEmitted(FooDeps), Failed());
+  }
 
   FooR->failMaterialization();
 
@@ -895,15 +821,14 @@ TEST_F(CoreAPIsStandardTest, FailAfterMaterialization) {
             SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnBarReady),
             NoDependenciesToRegister);
 
-  // Add a dependency by Foo on Bar and vice-versa.
-  FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
-  BarR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});
-
   // Materialize Foo.
   EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Succeeded())
       << "Expected resolution for \"Foo\" to succeed.";
-  EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())
-      << "Expected emission for \"Foo\" to succeed.";
+  {
+    SymbolDependenceGroup FooDeps({{Foo}, {{&JD, {Bar}}}});
+    EXPECT_THAT_ERROR(FooR->notifyEmitted(FooDeps), Succeeded())
+        << "Expected emission for \"Foo\" to succeed.";
+  }
 
   // Fail bar.
   BarR->failMaterialization();
@@ -985,7 +910,7 @@ TEST_F(CoreAPIsStandardTest, AddAndMaterializeLazySymbol) {
       [&](std::unique_ptr<MaterializationResponsibility> R) {
         assert(BarDiscarded && "Bar should have been discarded by this point");
         cantFail(R->notifyResolved(SymbolMap({{Foo, FooSym}})));
-        cantFail(R->notifyEmitted());
+        cantFail(R->notifyEmitted({}));
         FooMaterialized = true;
       },
       nullptr,
@@ -1026,7 +951,7 @@ TEST_F(CoreAPIsStandardTest, TestBasicWeakSymbolMaterialization) {
       SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),
       [&](std::unique_ptr<MaterializationResponsibility> R) {
         cantFail(R->notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})));
-        cantFail(R->notifyEmitted());
+        cantFail(R->notifyEmitted({}));
         BarMaterialized = true;
       });
 
@@ -1092,7 +1017,7 @@ TEST_F(CoreAPIsStandardTest, DefineMaterializingSymbol) {
         cantFail(
             R->defineMaterializing(SymbolFlagsMap({{Bar, BarSym.getFlags()}})));
         cantFail(R->notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})));
-        cantFail(R->notifyEmitted());
+        cantFail(R->notifyEmitted({}));
       });
 
   cantFail(JD.define(MU));
@@ -1371,7 +1296,6 @@ TEST_F(CoreAPIsStandardTest, FailEmissionAfterResolution) {
       SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),
       [&](std::unique_ptr<MaterializationResponsibility> R) {
         cantFail(R->notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})));
-
         ES.lookup(
             LookupKind::Static, makeJITDylibSearchOrder(&JD),
             SymbolLookupSet({Baz}), SymbolState::Resolved,
@@ -1384,9 +1308,7 @@ TEST_F(CoreAPIsStandardTest, FailEmissionAfterResolution) {
               cantFail(std::move(Result));
               R->failMaterialization();
             },
-            [&](const SymbolDependenceMap &Deps) {
-              R->addDependenciesForAll(Deps);
-            });
+            NoDependenciesToRegister);
       });
 
   cantFail(JD.define(MU));
@@ -1463,7 +1385,7 @@ TEST_F(CoreAPIsStandardTest, TestLookupWithUnthreadedMaterialization) {
       SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}),
       [&](std::unique_ptr<MaterializationResponsibility> R) {
         cantFail(R->notifyResolved({{Foo, FooSym}}));
-        cantFail(R->notifyEmitted());
+        cantFail(R->notifyEmitted({}));
       });
 
   cantFail(JD.define(MU));
@@ -1527,14 +1449,14 @@ TEST_F(CoreAPIsStandardTest, TestGetRequestedSymbolsAndReplace) {
             SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
             [&](std::unique_ptr<MaterializationResponsibility> R2) {
               cantFail(R2->notifyResolved(SymbolMap({{Bar, BarSym}})));
-              cantFail(R2->notifyEmitted());
+              cantFail(R2->notifyEmitted({}));
               BarMaterialized = true;
             });
 
         cantFail(R->replace(std::move(NewMU)));
 
         cantFail(R->notifyResolved(SymbolMap({{Foo, FooSym}})));
-        cantFail(R->notifyEmitted());
+        cantFail(R->notifyEmitted({}));
 
         FooMaterialized = true;
       });
@@ -1564,9 +1486,9 @@ TEST_F(CoreAPIsStandardTest, TestMaterializationResponsibilityDelegation) {
         auto R2 = cantFail(R->delegate({Bar}));
 
         cantFail(R->notifyResolved({{Foo, FooSym}}));
-        cantFail(R->notifyEmitted());
+        cantFail(R->notifyEmitted({}));
         cantFail(R2->notifyResolved({{Bar, BarSym}}));
-        cantFail(R2->notifyEmitted());
+        cantFail(R2->notifyEmitted({}));
       });
 
   cantFail(JD.define(MU));
@@ -1619,7 +1541,7 @@ TEST_F(CoreAPIsStandardTest, TestMaterializeWeakSymbol) {
 
   // No dependencies registered, can't fail:
   cantFail(FooR->notifyResolved(SymbolMap({{Foo, FooSym}})));
-  cantFail(FooR->notifyEmitted());
+  cantFail(FooR->notifyEmitted({}));
 }
 
 static bool linkOrdersEqual(const std::vector<JITDylibSP> &LHS,
diff --git a/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp b/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp
index 22762b7ad5904..7f367cfd58739 100644
--- a/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp
@@ -46,7 +46,7 @@ TEST_F(LazyReexportsTest, BasicLocalCallThroughManagerOperation) {
         cantFail(R->notifyResolved({{DummyTarget,
                                      {ExecutorAddr::fromPtr(&dummyTarget),
                                       JITSymbolFlags::Exported}}}));
-        cantFail(R->notifyEmitted());
+        cantFail(R->notifyEmitted({}));
       })));
 
   unsigned NotifyResolvedCount = 0;
diff --git a/llvm/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp b/llvm/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp
index 061db56850ef5..bbb16231d08a4 100644
--- a/llvm/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp
@@ -681,24 +681,7 @@ void Materialize(void *Ctx, LLVMOrcMaterializationResponsibilityRef MR) {
   LLVMOrcDisposeMaterializationResponsibility(OtherMR);
 
   // FIXME: Implement async lookup
-  // A real test of the dependence tracking in the success case would require
-  // async lookups. You could:
-  // 1. Materialize foo, making foo depend on other.
-  // 2. In the caller, verify that the lookup callback for foo has not run (due
-  // to the dependence)
-  // 3. Materialize other by looking it up.
-  // 4. In the caller, verify that the lookup callback for foo has now run.
-
-  LLVMOrcRetainSymbolStringPoolEntry(TargetSym.Name);
-  LLVMOrcRetainSymbolStringPoolEntry(DependencySymbol);
-  LLVMOrcCDependenceMapPair Dependency = {JD, {&DependencySymbol, 1}};
-  LLVMOrcMaterializationResponsibilityAddDependencies(MR, TargetSym.Name,
-                                                      &Dependency, 1);
-
   LLVMOrcRetainSymbolStringPoolEntry(DependencySymbol);
-  LLVMOrcMaterializationResponsibilityAddDependenciesForAll(MR, &Dependency, 1);
-
-  // See FIXME above
   LLVMOrcCSymbolMapPair Pair = {DependencySymbol, Sym};
   LLVMOrcMaterializationResponsibilityNotifyResolved(MR, &Pair, 1);
   // DependencySymbol no longer owned by us
@@ -706,7 +689,14 @@ void Materialize(void *Ctx, LLVMOrcMaterializationResponsibilityRef MR) {
   Pair = {TargetSym.Name, Sym};
   LLVMOrcMaterializationResponsibilityNotifyResolved(MR, &Pair, 1);
 
-  LLVMOrcMaterializationResponsibilityNotifyEmitted(MR);
+  LLVMOrcRetainSymbolStringPoolEntry(TargetSym.Name);
+  LLVMOrcCDependenceMapPair Dependency = {JD, {&DependencySymbol, 1}};
+  LLVMOrcCSymbolDependenceGroup DependenceSet = {
+      .Symbols = {.Symbols = &TargetSym.Name, .Length = 1},
+      .Dependencies = &Dependency,
+      .NumDependencies = 1};
+
+  LLVMOrcMaterializationResponsibilityNotifyEmitted(MR, &DependenceSet, 1);
   LLVMOrcDisposeMaterializationResponsibility(MR);
 }
 
diff --git a/llvm/unittests/ExecutionEngine/Orc/ResourceTrackerTest.cpp b/llvm/unittests/ExecutionEngine/Orc/ResourceTrackerTest.cpp
index 9f13167f8878e..0e5d151714fcc 100644
--- a/llvm/unittests/ExecutionEngine/Orc/ResourceTrackerTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/ResourceTrackerTest.cpp
@@ -170,7 +170,7 @@ TEST_F(ResourceTrackerStandardTest, BasicDefineAndRemoveAllAfterMaterializing) {
         cantFail(R->withResourceKeyDo(
             [&](ResourceKey K) { SRM.recordResource(K); }));
         cantFail(R->notifyResolved({{Foo, FooSym}}));
-        cantFail(R->notifyEmitted());
+        cantFail(R->notifyEmitted({}));
       });
 
   auto RT = JD.createResourceTracker();
@@ -252,7 +252,7 @@ TEST_F(ResourceTrackerStandardTest, JITDylibClear) {
         cantFail(R->withResourceKeyDo(
             [&](ResourceKey K) { ++SRM.getRecordedResources()[K]; }));
         cantFail(R->notifyResolved({{Foo, FooSym}}));
-        cantFail(R->notifyEmitted());
+        cantFail(R->notifyEmitted({}));
       })));
 
   // Add materializer for Bar.
@@ -262,7 +262,7 @@ TEST_F(ResourceTrackerStandardTest, JITDylibClear) {
         cantFail(R->withResourceKeyDo(
             [&](ResourceKey K) { ++SRM.getRecordedResources()[K]; }));
         cantFail(R->notifyResolved({{Bar, BarSym}}));
-        cantFail(R->notifyEmitted());
+        cantFail(R->notifyEmitted({}));
       })));
 
   EXPECT_TRUE(SRM.getRecordedResources().empty())
@@ -305,7 +305,7 @@ TEST_F(ResourceTrackerStandardTest,
           cantFail(R->withResourceKeyDo(
               [&](ResourceKey K) { SRM.recordResource(K); }));
           cantFail(R->notifyResolved({{Name, Sym}}));
-          cantFail(R->notifyEmitted());
+          cantFail(R->notifyEmitted({}));
         });
   };
 
@@ -355,7 +355,7 @@ TEST_F(ResourceTrackerStandardTest,
           cantFail(R->withResourceKeyDo(
               [&](ResourceKey K) { SRM.recordResource(K, 1); }));
           cantFail(R->notifyResolved({{Name, Sym}}));
-          cantFail(R->notifyEmitted());
+          cantFail(R->notifyEmitted({}));
         });
   };
 
@@ -446,7 +446,7 @@ TEST_F(ResourceTrackerStandardTest,
       << "Expected Resource value for BarRT to be '2' here";
 
   cantFail(FooMR->notifyResolved({{Foo, FooSym}}));
-  cantFail(FooMR->notifyEmitted());
+  cantFail(FooMR->notifyEmitted({}));
 }
 
 } // namespace



More information about the llvm-commits mailing list