//===-- Speculation.h - Speculative Compilation --*- C++ -*-===//
 
//
 
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 
// See https://llvm.org/LICENSE.txt for license information.
 
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
//
 
//===----------------------------------------------------------------------===//
 
//
 
// Contains the definition to support speculative compilation when laziness is
 
// enabled.
 
//===----------------------------------------------------------------------===//
 
 
 
#ifndef LLVM_EXECUTIONENGINE_ORC_SPECULATION_H
 
#define LLVM_EXECUTIONENGINE_ORC_SPECULATION_H
 
 
 
#include "llvm/ADT/DenseMap.h"
 
#include "llvm/ExecutionEngine/Orc/Core.h"
 
#include "llvm/ExecutionEngine/Orc/DebugUtils.h"
 
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
 
#include "llvm/Support/Debug.h"
 
#include <mutex>
 
#include <type_traits>
 
#include <utility>
 
 
 
namespace llvm {
 
namespace orc {
 
 
 
class Speculator;
 
 
 
// Track the Impls (JITDylib,Symbols) of Symbols while lazy call through
 
// trampolines are created. Operations are guarded by locks tp ensure that Imap
 
// stays in consistent state after read/write
 
 
 
class ImplSymbolMap {
 
  friend class Speculator;
 
 
 
public:
 
  using AliaseeDetails = std::pair<SymbolStringPtr, JITDylib *>;
 
  using Alias = SymbolStringPtr;
 
  using ImapTy = DenseMap<Alias, AliaseeDetails>;
 
  void trackImpls(SymbolAliasMap ImplMaps, JITDylib *SrcJD);
 
 
 
private:
 
  // FIX ME: find a right way to distinguish the pre-compile Symbols, and update
 
  // the callsite
 
  std::optional<AliaseeDetails> getImplFor(const SymbolStringPtr &StubSymbol) {
 
    std::lock_guard<std::mutex> Lockit(ConcurrentAccess);
 
    auto Position = Maps.find(StubSymbol);
 
    if (Position != Maps.end())
 
      return Position->getSecond();
 
    else
 
      return std::nullopt;
 
  }
 
 
 
  std::mutex ConcurrentAccess;
 
  ImapTy Maps;
 
};
 
 
 
// Defines Speculator Concept,
 
class Speculator {
 
public:
 
  using TargetFAddr = JITTargetAddress;
 
  using FunctionCandidatesMap = DenseMap<SymbolStringPtr, SymbolNameSet>;
 
  using StubAddrLikelies = DenseMap<TargetFAddr, SymbolNameSet>;
 
 
 
private:
 
  void registerSymbolsWithAddr(TargetFAddr ImplAddr,
 
                               SymbolNameSet likelySymbols) {
 
    std::lock_guard<std::mutex> Lockit(ConcurrentAccess);
 
    GlobalSpecMap.insert({ImplAddr, std::move(likelySymbols)});
 
  }
 
 
 
  void launchCompile(JITTargetAddress FAddr) {
 
    SymbolNameSet CandidateSet;
 
    // Copy CandidateSet is necessary, to avoid unsynchronized access to
 
    // the datastructure.
 
    {
 
      std::lock_guard<std::mutex> Lockit(ConcurrentAccess);
 
      auto It = GlobalSpecMap.find(FAddr);
 
      if (It == GlobalSpecMap.end())
 
        return;
 
      CandidateSet = It->getSecond();
 
    }
 
 
 
    SymbolDependenceMap SpeculativeLookUpImpls;
 
 
 
    for (auto &Callee : CandidateSet) {
 
      auto ImplSymbol = AliaseeImplTable.getImplFor(Callee);
 
      // try to distinguish already compiled & library symbols
 
      if (!ImplSymbol)
 
        continue;
 
      const auto &ImplSymbolName = ImplSymbol->first;
 
      JITDylib *ImplJD = ImplSymbol->second;
 
      auto &SymbolsInJD = SpeculativeLookUpImpls[ImplJD];
 
      SymbolsInJD.insert(ImplSymbolName);
 
    }
 
 
 
    DEBUG_WITH_TYPE("orc", {
 
      for (auto &I : SpeculativeLookUpImpls) {
 
        llvm::dbgs() << "\n In " << I.first->getName() << " JITDylib ";
 
        for (auto &N : I.second)
 
          llvm::dbgs() << "\n Likely Symbol : " << N;
 
      }
 
    });
 
 
 
    // for a given symbol, there may be no symbol qualified for speculatively
 
    // compile try to fix this before jumping to this code if possible.
 
    for (auto &LookupPair : SpeculativeLookUpImpls)
 
      ES.lookup(
 
          LookupKind::Static,
 
          makeJITDylibSearchOrder(LookupPair.first,
 
                                  JITDylibLookupFlags::MatchAllSymbols),
 
          SymbolLookupSet(LookupPair.second), SymbolState::Ready,
 
          [this](Expected<SymbolMap> Result) {
 
            if (auto Err = Result.takeError())
 
              ES.reportError(std::move(Err));
 
          },
 
          NoDependenciesToRegister);
 
  }
 
 
 
public:
 
  Speculator(ImplSymbolMap &Impl, ExecutionSession &ref)
 
      : AliaseeImplTable(Impl), ES(ref), GlobalSpecMap(0) {}
 
  Speculator(const Speculator &) = delete;
 
  Speculator(Speculator &&) = delete;
 
  Speculator &operator=(const Speculator &) = delete;
 
  Speculator &operator=(Speculator &&) = delete;
 
 
 
  /// Define symbols for this Speculator object (__orc_speculator) and the
 
  /// speculation runtime entry point symbol (__orc_speculate_for) in the
 
  /// given JITDylib.
 
  Error addSpeculationRuntime(JITDylib &JD, MangleAndInterner &Mangle);
 
 
 
  // Speculatively compile likely functions for the given Stub Address.
 
  // destination of __orc_speculate_for jump
 
  void speculateFor(TargetFAddr StubAddr) { launchCompile(StubAddr); }
 
 
 
  // FIXME : Register with Stub Address, after JITLink Fix.
 
  void registerSymbols(FunctionCandidatesMap Candidates, JITDylib *JD) {
 
    for (auto &SymPair : Candidates) {
 
      auto Target = SymPair.first;
 
      auto Likely = SymPair.second;
 
 
 
      auto OnReadyFixUp = [Likely, Target,
 
                           this](Expected<SymbolMap> ReadySymbol) {
 
        if (ReadySymbol) {
 
          auto RAddr = (*ReadySymbol)[Target].getAddress();
 
          registerSymbolsWithAddr(RAddr, std::move(Likely));
 
        } else
 
          this->getES().reportError(ReadySymbol.takeError());
 
      };
 
      // Include non-exported symbols also.
 
      ES.lookup(
 
          LookupKind::Static,
 
          makeJITDylibSearchOrder(JD, JITDylibLookupFlags::MatchAllSymbols),
 
          SymbolLookupSet(Target, SymbolLookupFlags::WeaklyReferencedSymbol),
 
          SymbolState::Ready, OnReadyFixUp, NoDependenciesToRegister);
 
    }
 
  }
 
 
 
  ExecutionSession &getES() { return ES; }
 
 
 
private:
 
  static void speculateForEntryPoint(Speculator *Ptr, uint64_t StubId);
 
  std::mutex ConcurrentAccess;
 
  ImplSymbolMap &AliaseeImplTable;
 
  ExecutionSession &ES;
 
  StubAddrLikelies GlobalSpecMap;
 
};
 
 
 
class IRSpeculationLayer : public IRLayer {
 
public:
 
  using IRlikiesStrRef =
 
      std::optional<DenseMap<StringRef, DenseSet<StringRef>>>;
 
  using ResultEval = std::function<IRlikiesStrRef(Function &)>;
 
  using TargetAndLikelies = DenseMap<SymbolStringPtr, SymbolNameSet>;
 
 
 
  IRSpeculationLayer(ExecutionSession &ES, IRLayer &BaseLayer, Speculator &Spec,
 
                     MangleAndInterner &Mangle, ResultEval Interpreter)
 
      : IRLayer(ES, BaseLayer.getManglingOptions()), NextLayer(BaseLayer),
 
        S(Spec), Mangle(Mangle), QueryAnalysis(Interpreter) {}
 
 
 
  void emit(std::unique_ptr<MaterializationResponsibility> R,
 
            ThreadSafeModule TSM) override;
 
 
 
private:
 
  TargetAndLikelies
 
  internToJITSymbols(DenseMap<StringRef, DenseSet<StringRef>> IRNames) {
 
    assert(!IRNames.empty() && "No IRNames received to Intern?");
 
    TargetAndLikelies InternedNames;
 
    for (auto &NamePair : IRNames) {
 
      DenseSet<SymbolStringPtr> TargetJITNames;
 
      for (auto &TargetNames : NamePair.second)
 
        TargetJITNames.insert(Mangle(TargetNames));
 
      InternedNames[Mangle(NamePair.first)] = std::move(TargetJITNames);
 
    }
 
    return InternedNames;
 
  }
 
 
 
  IRLayer &NextLayer;
 
  Speculator &S;
 
  MangleAndInterner &Mangle;
 
  ResultEval QueryAnalysis;
 
};
 
 
 
} // namespace orc
 
} // namespace llvm
 
 
 
#endif // LLVM_EXECUTIONENGINE_ORC_SPECULATION_H