xref: /llvm-project/bolt/include/bolt/Passes/DataflowAnalysis.h (revision f40d25dd8d3ad7bcfa8f5e8f74f245ab1a7675df)
1 //===- bolt/Passes/DataflowAnalysis.h ---------------------------*- 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 #ifndef BOLT_PASSES_DATAFLOWANALYSIS_H
10 #define BOLT_PASSES_DATAFLOWANALYSIS_H
11 
12 #include "bolt/Core/BinaryContext.h"
13 #include "bolt/Core/BinaryFunction.h"
14 #include "llvm/Support/Errc.h"
15 #include <optional>
16 #include <queue>
17 
18 namespace llvm {
19 namespace bolt {
20 
21 /// Represents a given program point as viewed by a dataflow analysis. This
22 /// point is a location that may be either an instruction or a basic block.
23 ///  Example:
24 ///
25 ///    BB1:    --> ProgramPoint 1  (stored as bb *)
26 ///      add   --> ProgramPoint 2  (stored as inst *)
27 ///      sub   --> ProgramPoint 3  (stored as inst *)
28 ///      jmp   --> ProgramPoint 4  (stored as inst *)
29 ///
30 /// ProgramPoints allow us to attach a state to any location in the program
31 /// and is a core concept used in the dataflow analysis engine.
32 ///
33 /// A dataflow analysis will associate a state with a program point. In
34 /// analyses whose direction is forward, this state tracks what happened after
35 /// the execution of an instruction, and the BB tracks the state of what
36 /// happened before the execution of the first instruction in this BB. For
37 /// backwards dataflow analyses, state tracks what happened before the
38 /// execution of a given instruction, while the state associated with a BB
39 /// tracks what happened after the execution of the last instruction of a BB.
40 class ProgramPoint {
41   enum IDTy : bool { BB = 0, Inst } ID;
42 
43   union DataU {
44     BinaryBasicBlock *BB;
45     MCInst *Inst;
DataU(BinaryBasicBlock * BB)46     DataU(BinaryBasicBlock *BB) : BB(BB) {}
DataU(MCInst * Inst)47     DataU(MCInst *Inst) : Inst(Inst) {}
48   } Data;
49 
50 public:
ProgramPoint()51   ProgramPoint() : ID(IDTy::BB), Data((MCInst *)nullptr) {}
ProgramPoint(BinaryBasicBlock * BB)52   ProgramPoint(BinaryBasicBlock *BB) : ID(IDTy::BB), Data(BB) {}
ProgramPoint(MCInst * Inst)53   ProgramPoint(MCInst *Inst) : ID(IDTy::Inst), Data(Inst) {}
54 
55   /// Convenience function to access the last program point of a basic block,
56   /// which is equal to its last instruction. If it is empty, it is equal to
57   /// itself.
getLastPointAt(BinaryBasicBlock & BB)58   static ProgramPoint getLastPointAt(BinaryBasicBlock &BB) {
59     auto Last = BB.rbegin();
60     if (Last != BB.rend())
61       return ProgramPoint(&*Last);
62     return ProgramPoint(&BB);
63   }
64 
65   /// Similar to getLastPointAt.
getFirstPointAt(BinaryBasicBlock & BB)66   static ProgramPoint getFirstPointAt(BinaryBasicBlock &BB) {
67     auto First = BB.begin();
68     if (First != BB.end())
69       return ProgramPoint(&*First);
70     return ProgramPoint(&BB);
71   }
72 
73   bool operator<(const ProgramPoint &PP) const { return Data.BB < PP.Data.BB; }
74   bool operator==(const ProgramPoint &PP) const {
75     return Data.BB == PP.Data.BB;
76   }
77 
isBB()78   bool isBB() const { return ID == IDTy::BB; }
isInst()79   bool isInst() const { return ID == IDTy::Inst; }
80 
getBB()81   BinaryBasicBlock *getBB() const {
82     assert(isBB());
83     return Data.BB;
84   }
getInst()85   MCInst *getInst() const {
86     assert(isInst());
87     return Data.Inst;
88   }
89 
90   friend DenseMapInfo<ProgramPoint>;
91 };
92 
93 /// Convenience function to operate on all predecessors of a BB, as viewed
94 /// by a dataflow analysis. This includes throw sites if it is a landing pad.
95 void doForAllPreds(const BinaryBasicBlock &BB,
96                    std::function<void(ProgramPoint)> Task);
97 
98 /// Operates on all successors of a basic block.
99 void doForAllSuccs(const BinaryBasicBlock &BB,
100                    std::function<void(ProgramPoint)> Task);
101 
102 /// Default printer for State data.
103 template <typename StateTy> class StatePrinter {
104 public:
print(raw_ostream & OS,const StateTy & State)105   void print(raw_ostream &OS, const StateTy &State) const { OS << State; }
StatePrinter(const BinaryContext &)106   explicit StatePrinter(const BinaryContext &) {}
107 };
108 
109 /// Printer for State data that is a BitVector of registers.
110 class RegStatePrinter {
111 public:
112   void print(raw_ostream &OS, const BitVector &State) const;
RegStatePrinter(const BinaryContext & BC)113   explicit RegStatePrinter(const BinaryContext &BC) : BC(BC) {}
114 
115 private:
116   const BinaryContext &BC;
117 };
118 
119 /// Base class for dataflow analyses. Depends on the type of whatever object is
120 /// stored as the state (StateTy) at each program point. The dataflow then
121 /// updates the state at each program point depending on the instruction being
122 /// processed, iterating until all points converge and agree on a state value.
123 /// Remember that depending on how you formulate your dataflow equation, this
124 /// may not converge and will loop indefinitely.
125 /// /p Backward indicates the direction of the dataflow. If false, direction is
126 /// forward.
127 ///
128 /// Example: Compute the set of live registers at each program point.
129 ///
130 ///   Modelling:
131 ///     Let State be the set of registers that are live. The kill set of a
132 ///     point is the set of all registers clobbered by the instruction at this
133 ///     program point. The gen set is the set of all registers read by it.
134 ///
135 ///       out{b} = Union (s E succs{b}) {in{s}}
136 ///       in{b}  = (out{b} - kill{b}) U gen{b}
137 ///
138 ///   Template parameters:
139 ///     StateTy = BitVector, where each index corresponds to a machine register
140 ///     Backward = true   (live reg operates in reverse order)
141 ///
142 ///   Subclass implementation notes:
143 ///     Confluence operator = union  (if a reg is alive in any succ, it is alive
144 ///     in the current block).
145 ///
146 template <typename Derived, typename StateTy, bool Backward = false,
147           typename StatePrinterTy = StatePrinter<StateTy>>
148 class DataflowAnalysis {
149   /// CRTP convenience methods
derived()150   Derived &derived() { return *static_cast<Derived *>(this); }
151 
const_derived()152   const Derived &const_derived() const {
153     return *static_cast<const Derived *>(this);
154   }
155 
156   mutable std::optional<unsigned> AnnotationIndex;
157 
158 protected:
159   const BinaryContext &BC;
160   /// Reference to the function being analysed
161   BinaryFunction &Func;
162 
163   /// The id of the annotation allocator to be used
164   MCPlusBuilder::AllocatorIdTy AllocatorId = 0;
165 
166   /// Tracks the state at basic block start (end) if direction of the dataflow
167   /// is forward (backward).
168   std::unordered_map<const BinaryBasicBlock *, StateTy> StateAtBBEntry;
169   /// Map a point to its previous (succeeding) point if the direction of the
170   /// dataflow is forward (backward). This is used to support convenience
171   /// methods to access the resulting state before (after) a given instruction,
172   /// otherwise our clients need to keep "prev" pointers themselves.
173   DenseMap<const MCInst *, ProgramPoint> PrevPoint;
174 
175   /// Perform any bookkeeping before dataflow starts
preflight()176   void preflight() { llvm_unreachable("Unimplemented method"); }
177 
178   /// Sets initial state for each BB
getStartingStateAtBB(const BinaryBasicBlock & BB)179   StateTy getStartingStateAtBB(const BinaryBasicBlock &BB) {
180     llvm_unreachable("Unimplemented method");
181   }
182 
183   /// Sets initial state for each instruction (out set)
getStartingStateAtPoint(const MCInst & Point)184   StateTy getStartingStateAtPoint(const MCInst &Point) {
185     llvm_unreachable("Unimplemented method");
186   }
187 
188   /// Computes the in set for the first instruction in a BB by applying the
189   /// confluence operator to the out sets of the last instruction of each pred
190   /// (in case of a backwards dataflow, we will operate on the in sets of each
191   /// successor to determine the starting state of the last instruction of the
192   /// current BB)
doConfluence(StateTy & StateOut,const StateTy & StateIn)193   void doConfluence(StateTy &StateOut, const StateTy &StateIn) {
194     llvm_unreachable("Unimplemented method");
195   }
196 
197   /// In case of a forwards dataflow, compute the in set for the first
198   /// instruction in a Landing Pad considering all out sets for associated
199   /// throw sites.
200   /// In case of a backwards dataflow, compute the in set of a invoke
201   /// instruction considering in sets for the first instructions of its
202   /// landing pads.
doConfluenceWithLP(StateTy & StateOut,const StateTy & StateIn,const MCInst & Invoke)203   void doConfluenceWithLP(StateTy &StateOut, const StateTy &StateIn,
204                           const MCInst &Invoke) {
205     return derived().doConfluence(StateOut, StateIn);
206   }
207 
208   /// Returns the out set of an instruction given its in set.
209   /// If backwards, computes the in set given its out set.
computeNext(const MCInst & Point,const StateTy & Cur)210   StateTy computeNext(const MCInst &Point, const StateTy &Cur) {
211     llvm_unreachable("Unimplemented method");
212     return StateTy();
213   }
214 
215   /// Returns the MCAnnotation name
getAnnotationName()216   StringRef getAnnotationName() const {
217     llvm_unreachable("Unimplemented method");
218     return StringRef("");
219   }
220 
getAnnotationIndex()221   unsigned getAnnotationIndex() const {
222     if (AnnotationIndex)
223       return *AnnotationIndex;
224     AnnotationIndex =
225         BC.MIB->getOrCreateAnnotationIndex(const_derived().getAnnotationName());
226     return *AnnotationIndex;
227   }
228 
229   /// Private getter methods accessing state in a read-write fashion
getOrCreateStateAt(const BinaryBasicBlock & BB)230   StateTy &getOrCreateStateAt(const BinaryBasicBlock &BB) {
231     return StateAtBBEntry[&BB];
232   }
233 
getOrCreateStateAt(MCInst & Point)234   StateTy &getOrCreateStateAt(MCInst &Point) {
235     return BC.MIB->getOrCreateAnnotationAs<StateTy>(
236         Point, derived().getAnnotationIndex(), AllocatorId);
237   }
238 
getOrCreateStateAt(ProgramPoint Point)239   StateTy &getOrCreateStateAt(ProgramPoint Point) {
240     if (Point.isBB())
241       return getOrCreateStateAt(*Point.getBB());
242     return getOrCreateStateAt(*Point.getInst());
243   }
244 
245 public:
246   /// Return the allocator id
getAllocatorId()247   unsigned getAllocatorId() { return AllocatorId; }
248 
249   /// If the direction of the dataflow is forward, operates on the last
250   /// instruction of all predecessors when performing an iteration of the
251   /// dataflow equation for the start of this BB.  If backwards, operates on
252   /// the first instruction of all successors.
doForAllSuccsOrPreds(const BinaryBasicBlock & BB,std::function<void (ProgramPoint)> Task)253   void doForAllSuccsOrPreds(const BinaryBasicBlock &BB,
254                             std::function<void(ProgramPoint)> Task) {
255     if (!Backward)
256       return doForAllPreds(BB, Task);
257     return doForAllSuccs(BB, Task);
258   }
259 
260   /// We need the current binary context and the function that will be processed
261   /// in this dataflow analysis.
262   DataflowAnalysis(BinaryFunction &BF,
263                    MCPlusBuilder::AllocatorIdTy AllocatorId = 0)
264       : BC(BF.getBinaryContext()), Func(BF), AllocatorId(AllocatorId) {}
265 
~DataflowAnalysis()266   virtual ~DataflowAnalysis() { cleanAnnotations(); }
267 
268   /// Track the state at basic block start (end) if direction of the dataflow
269   /// is forward (backward).
getStateAt(const BinaryBasicBlock & BB)270   ErrorOr<const StateTy &> getStateAt(const BinaryBasicBlock &BB) const {
271     auto Iter = StateAtBBEntry.find(&BB);
272     if (Iter == StateAtBBEntry.end())
273       return make_error_code(errc::result_out_of_range);
274     return Iter->second;
275   }
276 
277   /// Track the state at the end (start) of each MCInst in this function if
278   /// the direction of the dataflow is forward (backward).
getStateAt(const MCInst & Point)279   ErrorOr<const StateTy &> getStateAt(const MCInst &Point) const {
280     return BC.MIB->tryGetAnnotationAs<StateTy>(
281         Point, const_derived().getAnnotationIndex());
282   }
283 
284   /// Return the out set (in set) of a given program point if the direction of
285   /// the dataflow is forward (backward).
getStateAt(ProgramPoint Point)286   ErrorOr<const StateTy &> getStateAt(ProgramPoint Point) const {
287     if (Point.isBB())
288       return getStateAt(*Point.getBB());
289     return getStateAt(*Point.getInst());
290   }
291 
292   /// Relies on a ptr map to fetch the previous instruction and then retrieve
293   /// state. WARNING: Watch out for invalidated pointers. Do not use this
294   /// function if you invalidated pointers after the analysis has been completed
getStateBefore(const MCInst & Point)295   ErrorOr<const StateTy &> getStateBefore(const MCInst &Point) {
296     return getStateAt(PrevPoint[&Point]);
297   }
298 
getStateBefore(ProgramPoint Point)299   ErrorOr<const StateTy &> getStateBefore(ProgramPoint Point) {
300     if (Point.isBB())
301       return getStateAt(*Point.getBB());
302     return getStateAt(PrevPoint[Point.getInst()]);
303   }
304 
305   /// Remove any state annotations left by this analysis
cleanAnnotations()306   void cleanAnnotations() {
307     for (BinaryBasicBlock &BB : Func) {
308       for (MCInst &Inst : BB) {
309         BC.MIB->removeAnnotation(Inst, derived().getAnnotationIndex());
310       }
311     }
312   }
313 
314   /// Public entry point that will perform the entire analysis form start to
315   /// end.
run()316   void run() {
317     derived().preflight();
318 
319     if (Func.begin() == Func.end())
320       return;
321     // Initialize state for all points of the function
322     for (BinaryBasicBlock &BB : Func) {
323       StateTy &St = getOrCreateStateAt(BB);
324       St = derived().getStartingStateAtBB(BB);
325       for (MCInst &Inst : BB) {
326         StateTy &St = getOrCreateStateAt(Inst);
327         St = derived().getStartingStateAtPoint(Inst);
328       }
329     }
330 
331     std::queue<BinaryBasicBlock *> Worklist;
332     // TODO: Pushing this in a DFS ordering will greatly speed up the dataflow
333     // performance.
334     if (!Backward) {
335       for (BinaryBasicBlock &BB : Func) {
336         Worklist.push(&BB);
337         MCInst *Prev = nullptr;
338         for (MCInst &Inst : BB) {
339           PrevPoint[&Inst] = Prev ? ProgramPoint(Prev) : ProgramPoint(&BB);
340           Prev = &Inst;
341         }
342       }
343     } else {
344       for (BinaryBasicBlock &BB : llvm::reverse(Func)) {
345         Worklist.push(&BB);
346         MCInst *Prev = nullptr;
347         for (MCInst &Inst : llvm::reverse(BB)) {
348           PrevPoint[&Inst] = Prev ? ProgramPoint(Prev) : ProgramPoint(&BB);
349           Prev = &Inst;
350         }
351       }
352     }
353 
354     // Main dataflow loop
355     while (!Worklist.empty()) {
356       BinaryBasicBlock *BB = Worklist.front();
357       Worklist.pop();
358 
359       // Calculate state at the entry of first instruction in BB
360       StateTy StateAtEntry = getOrCreateStateAt(*BB);
361       if (BB->isLandingPad()) {
362         doForAllSuccsOrPreds(*BB, [&](ProgramPoint P) {
363           if (P.isInst() && BC.MIB->isInvoke(*P.getInst()))
364             derived().doConfluenceWithLP(StateAtEntry, *getStateAt(P),
365                                          *P.getInst());
366           else
367             derived().doConfluence(StateAtEntry, *getStateAt(P));
368         });
369       } else {
370         doForAllSuccsOrPreds(*BB, [&](ProgramPoint P) {
371           derived().doConfluence(StateAtEntry, *getStateAt(P));
372         });
373       }
374 
375       bool Changed = false;
376       StateTy &St = getOrCreateStateAt(*BB);
377       if (St != StateAtEntry) {
378         Changed = true;
379         St = std::move(StateAtEntry);
380       }
381 
382       // Propagate information from first instruction down to the last one
383       StateTy *PrevState = &St;
384       const MCInst *LAST = nullptr;
385       if (!Backward)
386         LAST = &*BB->rbegin();
387       else
388         LAST = &*BB->begin();
389 
390       auto doNext = [&](MCInst &Inst, const BinaryBasicBlock &BB) {
391         StateTy CurState = derived().computeNext(Inst, *PrevState);
392 
393         if (Backward && BC.MIB->isInvoke(Inst)) {
394           BinaryBasicBlock *LBB = Func.getLandingPadBBFor(BB, Inst);
395           if (LBB) {
396             auto First = LBB->begin();
397             if (First != LBB->end())
398               derived().doConfluenceWithLP(CurState,
399                                            getOrCreateStateAt(&*First), Inst);
400             else
401               derived().doConfluenceWithLP(CurState, getOrCreateStateAt(LBB),
402                                            Inst);
403           }
404         }
405 
406         StateTy &St = getOrCreateStateAt(Inst);
407         if (St != CurState) {
408           St = CurState;
409           if (&Inst == LAST)
410             Changed = true;
411         }
412         PrevState = &St;
413       };
414 
415       if (!Backward)
416         for (MCInst &Inst : *BB)
417           doNext(Inst, *BB);
418       else
419         for (MCInst &Inst : llvm::reverse(*BB))
420           doNext(Inst, *BB);
421 
422       if (Changed) {
423         if (!Backward) {
424           for (BinaryBasicBlock *Succ : BB->successors())
425             Worklist.push(Succ);
426           for (BinaryBasicBlock *LandingPad : BB->landing_pads())
427             Worklist.push(LandingPad);
428         } else {
429           for (BinaryBasicBlock *Pred : BB->predecessors())
430             Worklist.push(Pred);
431           for (BinaryBasicBlock *Thrower : BB->throwers())
432             Worklist.push(Thrower);
433         }
434       }
435     } // end while (!Worklist.empty())
436   }
437 };
438 
439 /// Define an iterator for navigating the expressions calculated by a
440 /// dataflow analysis at each program point, when they are backed by a
441 /// BitVector.
442 class ExprIterator {
443   const BitVector *BV;
444   const std::vector<MCInst *> &Expressions;
445   int Idx;
446 
447 public:
448   using iterator_category = std::forward_iterator_tag;
449   using value_type = const MCInst *;
450   using difference_type = std::ptrdiff_t;
451   using pointer = value_type *;
452   using reference = value_type &;
453 
454   ExprIterator &operator++() {
455     assert(Idx != -1 && "Iterator already at the end");
456     Idx = BV->find_next(Idx);
457     return *this;
458   }
459   ExprIterator operator++(int) {
460     assert(Idx != -1 && "Iterator already at the end");
461     ExprIterator Ret = *this;
462     ++(*this);
463     return Ret;
464   }
465   bool operator==(const ExprIterator &Other) const { return Idx == Other.Idx; }
466   bool operator!=(const ExprIterator &Other) const { return Idx != Other.Idx; }
467   MCInst *operator*() {
468     assert(Idx != -1 && "Invalid access to end iterator");
469     return Expressions[Idx];
470   }
ExprIterator(const BitVector * BV,const std::vector<MCInst * > & Exprs)471   ExprIterator(const BitVector *BV, const std::vector<MCInst *> &Exprs)
472       : BV(BV), Expressions(Exprs) {
473     Idx = BV->find_first();
474   }
ExprIterator(const BitVector * BV,const std::vector<MCInst * > & Exprs,int Idx)475   ExprIterator(const BitVector *BV, const std::vector<MCInst *> &Exprs, int Idx)
476       : BV(BV), Expressions(Exprs), Idx(Idx) {}
477 
getBitVectorIndex()478   int getBitVectorIndex() const { return Idx; }
479 };
480 
481 /// Specialization of DataflowAnalysis whose state specifically stores
482 /// a set of instructions.
483 template <typename Derived, bool Backward = false,
484           typename StatePrinterTy = StatePrinter<BitVector>>
485 class InstrsDataflowAnalysis
486     : public DataflowAnalysis<Derived, BitVector, Backward, StatePrinterTy> {
487 public:
488   /// These iterator functions offer access to the set of pointers to
489   /// instructions in a given program point
expr_begin(const T & Point)490   template <typename T> ExprIterator expr_begin(const T &Point) const {
491     if (auto State = this->getStateAt(Point))
492       return ExprIterator(&*State, Expressions);
493     return expr_end();
494   }
expr_begin(const BitVector & BV)495   ExprIterator expr_begin(const BitVector &BV) const {
496     return ExprIterator(&BV, Expressions);
497   }
expr_end()498   ExprIterator expr_end() const {
499     return ExprIterator(nullptr, Expressions, -1);
500   }
501 
502   /// Used to size the set of expressions/definitions being tracked by the
503   /// dataflow analysis
504   uint64_t NumInstrs{0};
505   /// We put every MCInst we want to track (which one representing an
506   /// expression/def) into a vector because we need to associate them with
507   /// small numbers. They will be tracked via BitVectors throughout the
508   /// dataflow analysis.
509   std::vector<MCInst *> Expressions;
510   /// Maps expressions defs (MCInsts) to its index in the Expressions vector
511   std::unordered_map<const MCInst *, uint64_t> ExprToIdx;
512 
513   /// Return whether \p Expr is in the state set at \p Point
count(ProgramPoint Point,const MCInst & Expr)514   bool count(ProgramPoint Point, const MCInst &Expr) const {
515     auto IdxIter = ExprToIdx.find(&Expr);
516     assert(IdxIter != ExprToIdx.end() && "Invalid Expr");
517     return (*this->getStateAt(Point))[IdxIter->second];
518   }
519 
count(const MCInst & Point,const MCInst & Expr)520   bool count(const MCInst &Point, const MCInst &Expr) const {
521     auto IdxIter = ExprToIdx.find(&Expr);
522     assert(IdxIter != ExprToIdx.end() && "Invalid Expr");
523     return (*this->getStateAt(Point))[IdxIter->second];
524   }
525 
526   /// Return whether \p Expr is in the state set at the instr of index
527   /// \p PointIdx
count(unsigned PointIdx,const MCInst & Expr)528   bool count(unsigned PointIdx, const MCInst &Expr) const {
529     return count(*Expressions[PointIdx], Expr);
530   }
531 
532   InstrsDataflowAnalysis(BinaryFunction &BF,
533                          MCPlusBuilder::AllocatorIdTy AllocId = 0)
534       : DataflowAnalysis<Derived, BitVector, Backward, StatePrinterTy>(
535             BF, AllocId) {}
~InstrsDataflowAnalysis()536   virtual ~InstrsDataflowAnalysis() {}
537 };
538 
539 } // namespace bolt
540 
541 /// DenseMapInfo allows us to use the DenseMap LLVM data structure to store
542 /// ProgramPoints.
543 template <> struct DenseMapInfo<bolt::ProgramPoint> {
544   static inline bolt::ProgramPoint getEmptyKey() {
545     uintptr_t Val = static_cast<uintptr_t>(-1);
546     Val <<= PointerLikeTypeTraits<MCInst *>::NumLowBitsAvailable;
547     return bolt::ProgramPoint(reinterpret_cast<MCInst *>(Val));
548   }
549   static inline bolt::ProgramPoint getTombstoneKey() {
550     uintptr_t Val = static_cast<uintptr_t>(-2);
551     Val <<= PointerLikeTypeTraits<MCInst *>::NumLowBitsAvailable;
552     return bolt::ProgramPoint(reinterpret_cast<MCInst *>(Val));
553   }
554   static unsigned getHashValue(const bolt::ProgramPoint &PP) {
555     return (unsigned((uintptr_t)PP.Data.BB) >> 4) ^
556            (unsigned((uintptr_t)PP.Data.BB) >> 9);
557   }
558   static bool isEqual(const bolt::ProgramPoint &LHS,
559                       const bolt::ProgramPoint &RHS) {
560     return LHS.Data.BB == RHS.Data.BB;
561   }
562 };
563 
564 raw_ostream &operator<<(raw_ostream &OS, const BitVector &Val);
565 
566 } // namespace llvm
567 
568 #endif
569