1 //===- bolt/Core/FunctionLayout.h - Fragmented Function Layout --*- 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 // This file contains the declaration of the FunctionLayout class. The layout of
10 // a function is the order of basic blocks, in which we will arrange them in the
11 // new binary. Normally, when not optimizing for code layout, the blocks of a
12 // function are contiguous. However, we can split the layout into multiple
13 // fragments. The blocks within a fragment are contiguous, but the fragments
14 // itself are disjoint. Fragments could be used to enhance code layout, e.g. to
15 // separate the blocks into hot and cold sections.
16 //
17 //===----------------------------------------------------------------------===//
18 
19 #ifndef BOLT_CORE_FUNCTION_LAYOUT_H
20 #define BOLT_CORE_FUNCTION_LAYOUT_H
21 
22 #include "llvm/ADT/ArrayRef.h"
23 #include "llvm/ADT/DenseSet.h"
24 #include "llvm/ADT/SmallVector.h"
25 #include "llvm/ADT/iterator.h"
26 
27 namespace llvm {
28 namespace bolt {
29 
30 class BinaryFunction;
31 class BinaryBasicBlock;
32 class FunctionLayout;
33 
34 class FragmentNum {
35   unsigned Value{0};
36 
37 public:
38   constexpr FragmentNum() = default;
FragmentNum(unsigned Value)39   constexpr explicit FragmentNum(unsigned Value) : Value(Value) {}
get()40   constexpr unsigned get() const { return Value; }
41   constexpr bool operator==(const FragmentNum Other) const {
42     return Value == Other.Value;
43   }
44   constexpr bool operator!=(const FragmentNum Other) const {
45     return !(*this == Other);
46   }
47 
hot()48   static constexpr FragmentNum hot() { return FragmentNum(0); }
cold()49   static constexpr FragmentNum cold() { return FragmentNum(1); }
50 };
51 
52 /// A freestanding subset of contiguous blocks of a function.
53 class FunctionFragment {
54   using BasicBlockListType = SmallVector<BinaryBasicBlock *, 0>;
55   using FragmentListType = SmallVector<unsigned, 0>;
56 
57 public:
58   using const_iterator = BasicBlockListType::const_iterator;
59 
60 private:
61   FragmentNum Num;
62   const FunctionLayout &Layout;
63 
FunctionFragment(FragmentNum Num,const FunctionLayout & Layout)64   FunctionFragment(FragmentNum Num, const FunctionLayout &Layout)
65       : Num(Num), Layout(Layout) {}
66 
67 public:
68   unsigned size() const;
69   bool empty() const;
70   const_iterator begin() const;
71   const_iterator end() const;
72   BinaryBasicBlock *front() const;
73 
74   friend class FunctionLayout;
75   friend class FragmentIterator;
76 };
77 
78 /// The function layout represents the fragments we split a function into and
79 /// the order of basic blocks within each fragment.
80 ///
81 /// Internally, the function layout stores blocks across fragments contiguously.
82 /// This is necessary to retain compatibility with existing code and tests that
83 /// iterate  over all blocks of the layout and depend on that order. When
84 /// writing new code, avoid iterating using FunctionLayout::blocks() by
85 /// iterating either over fragments or over BinaryFunction::begin()..end().
86 class FunctionLayout {
87 private:
88   using BasicBlockListType = SmallVector<BinaryBasicBlock *, 0>;
89   using block_iterator = BasicBlockListType::iterator;
90   using FragmentListType = SmallVector<unsigned, 0>;
91 
92 public:
93   class FragmentIterator;
94 
95   class FragmentIterator
96       : public iterator_facade_base<
97             FragmentIterator, std::bidirectional_iterator_tag, FunctionFragment,
98             std::ptrdiff_t, FunctionFragment *, FunctionFragment> {
99     FragmentNum Num;
100     const FunctionLayout *Layout;
101 
FragmentIterator(FragmentNum Num,const FunctionLayout * Layout)102     FragmentIterator(FragmentNum Num, const FunctionLayout *Layout)
103         : Num(Num), Layout(Layout) {}
104 
105   public:
106     bool operator==(const FragmentIterator &Other) const {
107       return Num == Other.Num;
108     }
109 
110     FunctionFragment operator*() const {
111       return FunctionFragment(Num, *Layout);
112     }
113 
114     FragmentIterator &operator++() {
115       Num = FragmentNum(Num.get() + 1);
116       return *this;
117     }
118 
119     FragmentIterator &operator--() {
120       Num = FragmentNum(Num.get() - 1);
121       return *this;
122     }
123 
124     friend class FunctionLayout;
125   };
126 
127   using const_iterator = FragmentIterator;
128   using block_const_iterator = BasicBlockListType::const_iterator;
129   using block_const_reverse_iterator =
130       BasicBlockListType::const_reverse_iterator;
131 
132 private:
133   BasicBlockListType Blocks;
134   /// List of indices dividing block list into fragments. To simplify iteration,
135   /// we have `Fragments.back()` equals `Blocks.size()`. Hence,
136   /// `Fragments.size()` equals `this->size() + 1`.
137   FragmentListType Fragments = {0};
138   BasicBlockListType PreviousBlocks;
139   FragmentListType PreviousFragments;
140 
141 public:
142   /// Add an empty fragment.
143   FunctionFragment addFragment();
144 
145   /// Return the fragment identified by Num.
146   FunctionFragment getFragment(FragmentNum Num) const;
147 
148   /// Find the fragment that contains BB.
149   FunctionFragment findFragment(const BinaryBasicBlock *BB) const;
150 
151   /// Add BB to the end of the last fragment.
152   void addBasicBlock(BinaryBasicBlock *BB);
153 
154   /// Insert range of basic blocks after InsertAfter. If InsertAfter is nullptr,
155   /// the blocks will be inserted at the start of the function.
156   void insertBasicBlocks(BinaryBasicBlock *InsertAfter,
157                          ArrayRef<BinaryBasicBlock *> NewBlocks);
158 
159   /// Erase all blocks from the layout that are in ToErase.
160   void eraseBasicBlocks(const DenseSet<const BinaryBasicBlock *> ToErase);
161 
162   /// Make sure fragments' and basic blocks' indices match the current layout.
163   void updateLayoutIndices() const;
164 
165   /// Replace the current layout with NewLayout. Uses the block's
166   /// self-identifying fragment number to assign blocks to infer function
167   /// fragments.
168   void update(const ArrayRef<BinaryBasicBlock *> NewLayout);
169 
170   /// Return true if the layout has been changed by basic block reordering,
171   /// false otherwise.
hasLayoutChanged()172   bool hasLayoutChanged() const { return !PreviousBlocks.empty(); }
173 
174   /// Clear layout releasing memory.
175   void clear();
176 
getBlock(unsigned Index)177   BinaryBasicBlock *getBlock(unsigned Index) const { return Blocks[Index]; }
178 
179   /// Returns the basic block after the given basic block in the layout or
180   /// nullptr if the last basic block is given.
181   BinaryBasicBlock *getBasicBlockAfter(const BinaryBasicBlock *BB,
182                                        bool IgnoreSplits = true) const;
183 
184   /// True if the layout contains at least two non-empty fragments.
185   bool isSplit() const;
186 
187   /// Get the edit distance of the new layout with respect to the previous
188   /// layout after basic block reordering.
189   uint64_t getEditDistance() const;
190 
size()191   size_t size() const { return Fragments.size() - 1; }
empty()192   bool empty() const { return Fragments.size() == 1; }
begin()193   const_iterator begin() const { return {FragmentNum(0), this}; }
end()194   const_iterator end() const { return {FragmentNum(size()), this}; }
front()195   FunctionFragment front() const { return *begin(); }
back()196   FunctionFragment back() const { return *std::prev(end()); }
197   FunctionFragment operator[](const FragmentNum Num) const {
198     return getFragment(Num);
199   }
200 
block_size()201   size_t block_size() const { return Blocks.size(); }
block_empty()202   bool block_empty() const { return Blocks.empty(); }
block_front()203   BinaryBasicBlock *block_front() const { return Blocks.front(); }
block_back()204   BinaryBasicBlock *block_back() const { return Blocks.back(); }
block_begin()205   block_const_iterator block_begin() const { return Blocks.begin(); }
block_end()206   block_const_iterator block_end() const { return Blocks.end(); }
blocks()207   iterator_range<block_const_iterator> blocks() const {
208     return {block_begin(), block_end()};
209   }
block_rbegin()210   block_const_reverse_iterator block_rbegin() const { return Blocks.rbegin(); }
block_rend()211   block_const_reverse_iterator block_rend() const { return Blocks.rend(); }
rblocks()212   iterator_range<block_const_reverse_iterator> rblocks() const {
213     return {block_rbegin(), block_rend()};
214   }
215 
216 private:
217   block_const_iterator findBasicBlockPos(const BinaryBasicBlock *BB) const;
218   block_iterator findBasicBlockPos(const BinaryBasicBlock *BB);
219 
220   friend class FunctionFragment;
221 };
222 
223 } // namespace bolt
224 } // namespace llvm
225 
226 #endif
227