Speculation.h revision 360784
1//===-- Speculation.h - Speculative Compilation --*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Contains the definition to support speculative compilation when laziness is
10// enabled.
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_EXECUTIONENGINE_ORC_SPECULATION_H
14#define LLVM_EXECUTIONENGINE_ORC_SPECULATION_H
15
16#include "llvm/ADT/ArrayRef.h"
17#include "llvm/ADT/DenseMap.h"
18#include "llvm/ADT/Optional.h"
19#include "llvm/ExecutionEngine/Orc/Core.h"
20#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
21#include "llvm/IR/PassManager.h"
22#include "llvm/Passes/PassBuilder.h"
23#include "llvm/Support/Debug.h"
24
25#include <mutex>
26#include <type_traits>
27#include <utility>
28#include <vector>
29
30namespace llvm {
31namespace orc {
32
33class Speculator;
34
35// Track the Impls (JITDylib,Symbols) of Symbols while lazy call through
36// trampolines are created. Operations are guarded by locks tp ensure that Imap
37// stays in consistent state after read/write
38
39class ImplSymbolMap {
40  friend class Speculator;
41
42public:
43  using AliaseeDetails = std::pair<SymbolStringPtr, JITDylib *>;
44  using Alias = SymbolStringPtr;
45  using ImapTy = DenseMap<Alias, AliaseeDetails>;
46  void trackImpls(SymbolAliasMap ImplMaps, JITDylib *SrcJD);
47
48private:
49  // FIX ME: find a right way to distinguish the pre-compile Symbols, and update
50  // the callsite
51  Optional<AliaseeDetails> getImplFor(const SymbolStringPtr &StubSymbol) {
52    std::lock_guard<std::mutex> Lockit(ConcurrentAccess);
53    auto Position = Maps.find(StubSymbol);
54    if (Position != Maps.end())
55      return Position->getSecond();
56    else
57      return None;
58  }
59
60  std::mutex ConcurrentAccess;
61  ImapTy Maps;
62};
63
64// Defines Speculator Concept,
65class Speculator {
66public:
67  using TargetFAddr = JITTargetAddress;
68  using FunctionCandidatesMap = DenseMap<SymbolStringPtr, SymbolNameSet>;
69  using StubAddrLikelies = DenseMap<TargetFAddr, SymbolNameSet>;
70
71private:
72  void registerSymbolsWithAddr(TargetFAddr ImplAddr,
73                               SymbolNameSet likelySymbols) {
74    std::lock_guard<std::mutex> Lockit(ConcurrentAccess);
75    GlobalSpecMap.insert({ImplAddr, std::move(likelySymbols)});
76  }
77
78  void launchCompile(JITTargetAddress FAddr) {
79    SymbolNameSet CandidateSet;
80    // Copy CandidateSet is necessary, to avoid unsynchronized access to
81    // the datastructure.
82    {
83      std::lock_guard<std::mutex> Lockit(ConcurrentAccess);
84      auto It = GlobalSpecMap.find(FAddr);
85      if (It == GlobalSpecMap.end())
86        return;
87      CandidateSet = It->getSecond();
88    }
89
90    SymbolDependenceMap SpeculativeLookUpImpls;
91
92    for (auto &Callee : CandidateSet) {
93      auto ImplSymbol = AliaseeImplTable.getImplFor(Callee);
94      // try to distinguish already compiled & library symbols
95      if (!ImplSymbol.hasValue())
96        continue;
97      const auto &ImplSymbolName = ImplSymbol.getPointer()->first;
98      JITDylib *ImplJD = ImplSymbol.getPointer()->second;
99      auto &SymbolsInJD = SpeculativeLookUpImpls[ImplJD];
100      SymbolsInJD.insert(ImplSymbolName);
101    }
102
103    DEBUG_WITH_TYPE("orc", {
104      for (auto &I : SpeculativeLookUpImpls) {
105        llvm::dbgs() << "\n In " << I.first->getName() << " JITDylib ";
106        for (auto &N : I.second)
107          llvm::dbgs() << "\n Likely Symbol : " << N;
108      }
109    });
110
111    // for a given symbol, there may be no symbol qualified for speculatively
112    // compile try to fix this before jumping to this code if possible.
113    for (auto &LookupPair : SpeculativeLookUpImpls)
114      ES.lookup(
115          LookupKind::Static,
116          makeJITDylibSearchOrder(LookupPair.first,
117                                  JITDylibLookupFlags::MatchAllSymbols),
118          SymbolLookupSet(LookupPair.second), SymbolState::Ready,
119          [this](Expected<SymbolMap> Result) {
120            if (auto Err = Result.takeError())
121              ES.reportError(std::move(Err));
122          },
123          NoDependenciesToRegister);
124  }
125
126public:
127  Speculator(ImplSymbolMap &Impl, ExecutionSession &ref)
128      : AliaseeImplTable(Impl), ES(ref), GlobalSpecMap(0) {}
129  Speculator(const Speculator &) = delete;
130  Speculator(Speculator &&) = delete;
131  Speculator &operator=(const Speculator &) = delete;
132  Speculator &operator=(Speculator &&) = delete;
133
134  /// Define symbols for this Speculator object (__orc_speculator) and the
135  /// speculation runtime entry point symbol (__orc_speculate_for) in the
136  /// given JITDylib.
137  Error addSpeculationRuntime(JITDylib &JD, MangleAndInterner &Mangle);
138
139  // Speculatively compile likely functions for the given Stub Address.
140  // destination of __orc_speculate_for jump
141  void speculateFor(TargetFAddr StubAddr) { launchCompile(StubAddr); }
142
143  // FIXME : Register with Stub Address, after JITLink Fix.
144  void registerSymbols(FunctionCandidatesMap Candidates, JITDylib *JD) {
145    for (auto &SymPair : Candidates) {
146      auto Target = SymPair.first;
147      auto Likely = SymPair.second;
148
149      auto OnReadyFixUp = [Likely, Target,
150                           this](Expected<SymbolMap> ReadySymbol) {
151        if (ReadySymbol) {
152          auto RAddr = (*ReadySymbol)[Target].getAddress();
153          registerSymbolsWithAddr(RAddr, std::move(Likely));
154        } else
155          this->getES().reportError(ReadySymbol.takeError());
156      };
157      // Include non-exported symbols also.
158      ES.lookup(
159          LookupKind::Static,
160          makeJITDylibSearchOrder(JD, JITDylibLookupFlags::MatchAllSymbols),
161          SymbolLookupSet(Target, SymbolLookupFlags::WeaklyReferencedSymbol),
162          SymbolState::Ready, OnReadyFixUp, NoDependenciesToRegister);
163    }
164  }
165
166  ExecutionSession &getES() { return ES; }
167
168private:
169  static void speculateForEntryPoint(Speculator *Ptr, uint64_t StubId);
170  std::mutex ConcurrentAccess;
171  ImplSymbolMap &AliaseeImplTable;
172  ExecutionSession &ES;
173  StubAddrLikelies GlobalSpecMap;
174};
175
176class IRSpeculationLayer : public IRLayer {
177public:
178  using IRlikiesStrRef = Optional<DenseMap<StringRef, DenseSet<StringRef>>>;
179  using ResultEval = std::function<IRlikiesStrRef(Function &)>;
180  using TargetAndLikelies = DenseMap<SymbolStringPtr, SymbolNameSet>;
181
182  IRSpeculationLayer(ExecutionSession &ES, IRCompileLayer &BaseLayer,
183                     Speculator &Spec, MangleAndInterner &Mangle,
184                     ResultEval Interpreter)
185      : IRLayer(ES, BaseLayer.getManglingOptions()), NextLayer(BaseLayer),
186        S(Spec), Mangle(Mangle), QueryAnalysis(Interpreter) {}
187
188  void emit(MaterializationResponsibility R, ThreadSafeModule TSM);
189
190private:
191  TargetAndLikelies
192  internToJITSymbols(DenseMap<StringRef, DenseSet<StringRef>> IRNames) {
193    assert(!IRNames.empty() && "No IRNames received to Intern?");
194    TargetAndLikelies InternedNames;
195    DenseSet<SymbolStringPtr> TargetJITNames;
196    for (auto &NamePair : IRNames) {
197      for (auto &TargetNames : NamePair.second)
198        TargetJITNames.insert(Mangle(TargetNames));
199
200      InternedNames[Mangle(NamePair.first)] = std::move(TargetJITNames);
201    }
202    return InternedNames;
203  }
204
205  IRCompileLayer &NextLayer;
206  Speculator &S;
207  MangleAndInterner &Mangle;
208  ResultEval QueryAnalysis;
209};
210
211} // namespace orc
212} // namespace llvm
213
214#endif // LLVM_EXECUTIONENGINE_ORC_SPECULATION_H
215