diff options
Diffstat (limited to 'llvm')
| -rw-r--r-- | llvm/include/llvm/ExecutionEngine/Orc/Core.h | 13 | ||||
| -rw-r--r-- | llvm/lib/ExecutionEngine/Orc/Core.cpp | 191 | ||||
| -rw-r--r-- | llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp | 54 | 
3 files changed, 139 insertions, 119 deletions
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Core.h b/llvm/include/llvm/ExecutionEngine/Orc/Core.h index 67f7b013194..2f06deef0c9 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/Core.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Core.h @@ -430,6 +430,7 @@ enum class SymbolState : uint8_t {    NeverSearched, /// Added to the symbol table, never queried.    Materializing, /// Queried, materialization begun.    Resolved,      /// Assigned address, still materializing. +  Emitted,       /// Emitted to memory, but waiting on transitive dependencies.    Ready = 0x3f   /// Ready and safe for clients to access.  }; @@ -637,7 +638,6 @@ private:    struct MaterializingInfo {      SymbolDependenceMap Dependants;      SymbolDependenceMap UnemittedDependencies; -    bool IsEmitted = false;      void addQuery(std::shared_ptr<AsynchronousSymbolQuery> Q);      void removeQuery(const AsynchronousSymbolQuery &Q); @@ -736,13 +736,6 @@ private:    SymbolNameSet getRequestedSymbols(const SymbolFlagsMap &SymbolFlags) const; -  // Move a symbol to the failure state. -  // Detaches the symbol from all dependencies, moves all dependants to the -  // error state (but does not fail them), deletes the MaterializingInfo for -  // the symbol (if present) and returns the set of queries that need to be -  // notified of the failure. -  AsynchronousSymbolQuerySet failSymbol(const SymbolStringPtr &Name); -    void addDependencies(const SymbolStringPtr &Name,                         const SymbolDependenceMap &Dependants); @@ -750,7 +743,9 @@ private:    Error emit(const SymbolFlagsMap &Emitted); -  void notifyFailed(const SymbolFlagsMap &FailedSymbols); +  using FailedSymbolsWorklist = +      std::vector<std::pair<JITDylib *, SymbolStringPtr>>; +  static void notifyFailed(FailedSymbolsWorklist FailedSymbols);    ExecutionSession &ES;    std::string JITDylibName; diff --git a/llvm/lib/ExecutionEngine/Orc/Core.cpp b/llvm/lib/ExecutionEngine/Orc/Core.cpp index 123e5679ae2..436fd55cd8d 100644 --- a/llvm/lib/ExecutionEngine/Orc/Core.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Core.cpp @@ -240,6 +240,8 @@ raw_ostream &operator<<(raw_ostream &OS, const SymbolState &S) {      return OS << "Materializing";    case SymbolState::Resolved:      return OS << "Resolved"; +  case SymbolState::Emitted: +    return OS << "Emitted";    case SymbolState::Ready:      return OS << "Ready";    } @@ -423,8 +425,13 @@ void MaterializationResponsibility::failMaterialization() {             << SymbolFlags << "\n";    }); -  JD.notifyFailed(SymbolFlags); +  JITDylib::FailedSymbolsWorklist Worklist; + +  for (auto &KV : SymbolFlags) +    Worklist.push_back(std::make_pair(&JD, KV.first));    SymbolFlags.clear(); + +  JD.notifyFailed(std::move(Worklist));  }  void MaterializationResponsibility::replace( @@ -841,70 +848,6 @@ JITDylib::getRequestedSymbols(const SymbolFlagsMap &SymbolFlags) const {    });  } -JITDylib::AsynchronousSymbolQuerySet -JITDylib::failSymbol(const SymbolStringPtr &Name) { -  assert(Symbols.count(Name) && "Name not in symbol table"); -  assert(Symbols[Name].getFlags().hasError() && -         "Failing symbol not in the error state"); - -  auto MII = MaterializingInfos.find(Name); -  if (MII == MaterializingInfos.end()) -    return AsynchronousSymbolQuerySet(); - -  auto &MI = MII->second; - -  // Visit all dependants. -  for (auto &KV : MI.Dependants) { -    auto &DependantJD = *KV.first; -    for (auto &DependantName : KV.second) { -      assert(DependantJD.Symbols.count(DependantName) && -             "No symbol with DependantName in DependantJD"); -      auto &DependantSymTabEntry = DependantJD.Symbols[DependantName]; -      DependantSymTabEntry.setFlags(DependantSymTabEntry.getFlags() | -                                    JITSymbolFlags::HasError); - -      assert(DependantJD.MaterializingInfos.count(DependantName) && -             "Dependant symbol does not have MaterializingInfo?"); -      auto &DependantMI = DependantJD.MaterializingInfos[DependantName]; - -      assert(DependantMI.UnemittedDependencies.count(this) && -             "No unemitted dependency recorded for this JD?"); -      auto UnemittedDepsI = DependantMI.UnemittedDependencies.find(this); -      assert(UnemittedDepsI != DependantMI.UnemittedDependencies.end() && -             "No unemitted dependency on this JD"); -      assert(UnemittedDepsI->second.count(Name) && -             "No unemitted dependency on symbol Name in this JD"); - -      UnemittedDepsI->second.erase(Name); -      if (UnemittedDepsI->second.empty()) -        DependantMI.UnemittedDependencies.erase(UnemittedDepsI); -    } -  } - -  // Visit all unemitted dependencies and disconnect from them. -  for (auto &KV : MI.UnemittedDependencies) { -    auto &DependencyJD = *KV.first; -    for (auto &DependencyName : KV.second) { -      assert(DependencyJD.MaterializingInfos.count(DependencyName) && -             "Dependency does not have MaterializingInfo"); -      auto &DependencyMI = DependencyJD.MaterializingInfos[DependencyName]; -      auto DependantsI = DependencyMI.Dependants.find(this); -      assert(DependantsI != DependencyMI.Dependants.end() && -             "No dependnts entry recorded for this JD"); -      assert(DependantsI->second.count(Name) && -             "No dependants entry recorded for Name"); -      DependantsI->second.erase(Name); -      if (DependantsI->second.empty()) -        DependencyMI.Dependants.erase(DependantsI); -    } -  } - -  AsynchronousSymbolQuerySet QueriesToFail; -  for (auto &Q : MI.takeAllPendingQueries()) -    QueriesToFail.insert(std::move(Q)); -  return QueriesToFail; -} -  void JITDylib::addDependencies(const SymbolStringPtr &Name,                                 const SymbolDependenceMap &Dependencies) {    assert(Symbols.count(Name) && "Name not in symbol table"); @@ -916,7 +859,8 @@ void JITDylib::addDependencies(const SymbolStringPtr &Name,      return;    auto &MI = MaterializingInfos[Name]; -  assert(!MI.IsEmitted && "Can not add dependencies to an emitted symbol"); +  assert(Symbols[Name].getState() != SymbolState::Emitted && +         "Can not add dependencies to an emitted symbol");    bool DependsOnSymbolInErrorState = false; @@ -930,18 +874,21 @@ void JITDylib::addDependencies(const SymbolStringPtr &Name,      for (auto &OtherSymbol : KV.second) {        // Check the sym entry for the dependency. -      auto SymI = OtherJITDylib.Symbols.find(OtherSymbol); +      auto OtherSymI = OtherJITDylib.Symbols.find(OtherSymbol);  #ifndef NDEBUG -      // Assert that this symbol exists and has not been emitted already. -      assert(SymI != OtherJITDylib.Symbols.end() && -             (SymI->second.getState() != SymbolState::Ready && -              "Dependency on emitted symbol")); +      // Assert that this symbol exists and has not reached the ready state +      // already. +      assert(OtherSymI != OtherJITDylib.Symbols.end() && +             (OtherSymI->second.getState() != SymbolState::Ready && +              "Dependency on emitted/ready symbol"));  #endif +      auto &OtherSymEntry = OtherSymI->second; +        // If the dependency is in an error state then note this and continue,        // we will move this symbol to the error state below. -      if (SymI->second.getFlags().hasError()) { +      if (OtherSymEntry.getFlags().hasError()) {          DependsOnSymbolInErrorState = true;          continue;        } @@ -952,7 +899,7 @@ void JITDylib::addDependencies(const SymbolStringPtr &Name,               "No MaterializingInfo for dependency");        auto &OtherMI = OtherJITDylib.MaterializingInfos[OtherSymbol]; -      if (OtherMI.IsEmitted) +      if (OtherSymEntry.getState() == SymbolState::Emitted)          transferEmittedNodeDependencies(MI, Name, OtherMI);        else if (&OtherJITDylib != this || OtherSymbol != Name) {          OtherMI.Dependants[this].insert(Name); @@ -1087,6 +1034,12 @@ Error JITDylib::emit(const SymbolFlagsMap &Emitted) {        Worklist.pop_back();        auto &Name = SymI->first; +      auto &SymEntry = SymI->second; + +      // Move symbol to the emitted state. +      assert(SymEntry.getState() == SymbolState::Resolved && +             "Emitting from state other than Resolved"); +      SymEntry.setState(SymbolState::Emitted);        auto MII = MaterializingInfos.find(Name);        assert(MII != MaterializingInfos.end() && @@ -1121,20 +1074,22 @@ Error JITDylib::emit(const SymbolFlagsMap &Emitted) {            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 (DependantMI.IsEmitted && +          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. -            auto DependantSymI = DependantJD.Symbols.find(DependantName); -            assert(DependantSymI != DependantJD.Symbols.end() && -                   "Dependant has no entry in the Symbols table"); -            DependantSymI->second.setState(SymbolState::Ready); +            DependantSymEntry.setState(SymbolState::Ready);              for (auto &Q : DependantMI.takeQueriesMeeting(SymbolState::Ready)) {                Q->notifySymbolMetRequiredState( @@ -1148,9 +1103,8 @@ Error JITDylib::emit(const SymbolFlagsMap &Emitted) {            }          }        } -      MI.Dependants.clear(); -      MI.IsEmitted = true; +      MI.Dependants.clear();        if (MI.UnemittedDependencies.empty()) {          SymI->second.setState(SymbolState::Ready);          for (auto &Q : MI.takeQueriesMeeting(SymbolState::Ready)) { @@ -1183,17 +1137,27 @@ Error JITDylib::emit(const SymbolFlagsMap &Emitted) {    return Error::success();  } -void JITDylib::notifyFailed(const SymbolFlagsMap &FailedSymbols) { +void JITDylib::notifyFailed(FailedSymbolsWorklist Worklist) {    AsynchronousSymbolQuerySet FailedQueries; +  auto FailedSymbolsMap = std::make_shared<SymbolDependenceMap>(); + +  // Failing no symbols is a no-op. +  if (Worklist.empty()) +    return; + +  auto &ES = Worklist.front().first->getExecutionSession();    ES.runSessionLocked([&]() { -    std::vector<const SymbolStringPtr *> MaterializerNamesToFail; +    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(); -    for (auto &KV : FailedSymbols) { -      auto &Name = KV.first; +      (*FailedSymbolsMap)[&JD].insert(Name); -      assert(Symbols.count(Name) && "No symbol table entry for Name"); -      auto &Sym = Symbols[Name]; +      assert(JD.Symbols.count(Name) && "No symbol table entry for Name"); +      auto &Sym = JD.Symbols[Name];        // Move the symbol into the error state.        // Note that this may be redundant: The symbol might already have been @@ -1203,13 +1167,12 @@ void JITDylib::notifyFailed(const SymbolFlagsMap &FailedSymbols) {        // 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 = MaterializingInfos.find(Name); +      auto MII = JD.MaterializingInfos.find(Name); -      if (MII == MaterializingInfos.end()) +      if (MII == JD.MaterializingInfos.end())          continue;        auto &MI = MII->second; -      MaterializerNamesToFail.push_back(&KV.first);        // Move all dependants to the error state and disconnect from them.        for (auto &KV : MI.Dependants) { @@ -1225,7 +1188,7 @@ void JITDylib::notifyFailed(const SymbolFlagsMap &FailedSymbols) {                   "No MaterializingInfo for dependant");            auto &DependantMI = DependantJD.MaterializingInfos[DependantName]; -          auto UnemittedDepI = DependantMI.UnemittedDependencies.find(this); +          auto UnemittedDepI = DependantMI.UnemittedDependencies.find(&JD);            assert(UnemittedDepI != DependantMI.UnemittedDependencies.end() &&                   "No UnemittedDependencies entry for this JITDylib");            assert(UnemittedDepI->second.count(Name) && @@ -1233,8 +1196,18 @@ void JITDylib::notifyFailed(const SymbolFlagsMap &FailedSymbols) {            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) { @@ -1244,35 +1217,35 @@ void JITDylib::notifyFailed(const SymbolFlagsMap &FailedSymbols) {                UnemittedDepJD.MaterializingInfos.find(UnemittedDepName);            assert(UnemittedDepMII != UnemittedDepJD.MaterializingInfos.end() &&                   "Missing MII for unemitted dependency"); -          assert(UnemittedDepMII->second.Dependants.count(this) && +          assert(UnemittedDepMII->second.Dependants.count(&JD) &&                   "JD not listed as a dependant of unemitted dependency"); -          assert(UnemittedDepMII->second.Dependants[this].count(Name) && +          assert(UnemittedDepMII->second.Dependants[&JD].count(Name) &&                   "Name is not listed as a dependant of unemitted dependency"); -          UnemittedDepMII->second.Dependants[this].erase(Name); -          if (UnemittedDepMII->second.Dependants[this].empty()) -            UnemittedDepMII->second.Dependants.erase(this); +          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. -      for (auto &Q : MII->second.pendingQueries()) +      for (auto &Q : MII->second.pendingQueries()) { +        // Add the query to the list to be failed and detach it.          FailedQueries.insert(Q); -    } - -    // Detach failed queries. -    for (auto &Q : FailedQueries) -      Q->detach(); +        Q->detach(); +      } -    // Remove the MaterializingInfos. -    while (!MaterializerNamesToFail.empty()) { -      MaterializingInfos.erase(*MaterializerNamesToFail.back()); -      MaterializerNamesToFail.pop_back(); +      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);      }    }); -  auto FailedSymbolsMap = std::make_shared<SymbolDependenceMap>(); -  for (auto &KV : FailedSymbols) -    (*FailedSymbolsMap)[this].insert(KV.first);    for (auto &Q : FailedQueries)      Q->handleFailed(make_error<FailedToMaterialize>(FailedSymbolsMap));  } @@ -1701,8 +1674,6 @@ void JITDylib::dump(raw_ostream &OS) {        OS << "  MaterializingInfos entries:\n";      for (auto &KV : MaterializingInfos) {        OS << "    \"" << *KV.first << "\":\n" -         << "      IsEmitted = " << (KV.second.IsEmitted ? "true" : "false") -         << "\n"           << "      " << KV.second.pendingQueries().size()           << " pending queries: { ";        for (const auto &Q : KV.second.pendingQueries()) diff --git a/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp b/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp index 0f04099c707..6bb0175fd74 100644 --- a/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp +++ b/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp @@ -718,6 +718,60 @@ TEST_F(CoreAPIsStandardTest, AddDependencyOnFailedSymbol) {        << "Lookup on failed symbol should fail";  } +TEST_F(CoreAPIsStandardTest, FailAfterMaterialization) { +  Optional<MaterializationResponsibility> FooR; +  Optional<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()}}), +      [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); }); + +  auto BarMU = std::make_unique<SimpleMaterializationUnit>( +      SymbolFlagsMap({{Bar, BarSym.getFlags()}}), +      [&](MaterializationResponsibility R) { BarR.emplace(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(JITDylibSearchList({{&JD, false}}), {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(JITDylibSearchList({{&JD, false}}), {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."; + +  // Fail bar. +  BarR->failMaterialization(); + +  // Verify that both queries failed. +  EXPECT_TRUE(OnFooReadyRun) << "Query for Foo did not run"; +  EXPECT_TRUE(OnBarReadyRun) << "Query for Bar did not run"; +} +  TEST_F(CoreAPIsStandardTest, FailMaterializerWithUnqueriedSymbols) {    // Make sure that symbols with no queries aganist them still    // fail correctly.  | 

