xref: /llvm-project/bolt/include/bolt/Core/FunctionLayout.h (revision 49ee6069db372ce326bc36678e745459868c3771)
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 #include "llvm/ADT/iterator_range.h"
27 #include <iterator>
28 
29 namespace llvm {
30 namespace bolt {
31 
32 class BinaryFunction;
33 class BinaryBasicBlock;
34 class FunctionLayout;
35 
36 class FragmentNum {
37   unsigned Value{0};
38 
39 public:
40   constexpr FragmentNum() = default;
41   constexpr explicit FragmentNum(unsigned Value) : Value(Value) {}
42   constexpr unsigned get() const { return Value; }
43 
44   constexpr bool operator==(const FragmentNum Other) const {
45     return Value == Other.Value;
46   }
47   constexpr bool operator!=(const FragmentNum Other) const {
48     return Value != Other.Value;
49   }
50   constexpr bool operator<(const FragmentNum Other) const {
51     return Value < Other.Value;
52   }
53   constexpr bool operator<=(const FragmentNum Other) const {
54     return Value <= Other.Value;
55   }
56   constexpr bool operator>=(const FragmentNum Other) const {
57     return Value >= Other.Value;
58   }
59   constexpr bool operator>(const FragmentNum Other) const {
60     return Value > Other.Value;
61   }
62 
63   static constexpr FragmentNum main() { return FragmentNum(0); }
64   static constexpr FragmentNum cold() { return FragmentNum(1); }
65   static constexpr FragmentNum warm() { return FragmentNum(2); }
66 };
67 
68 /// A freestanding subset of contiguous blocks of a function.
69 class FunctionFragment {
70   using BasicBlockListType = SmallVector<BinaryBasicBlock *, 0>;
71   using FragmentListType = SmallVector<unsigned, 0>;
72 
73 public:
74   using iterator = BasicBlockListType::iterator;
75   using const_iterator = BasicBlockListType::const_iterator;
76 
77 private:
78   FunctionLayout *Layout;
79   FragmentNum Num;
80   unsigned StartIndex;
81   unsigned Size = 0;
82 
83   /// Output address for the fragment.
84   uint64_t Address = 0;
85 
86   /// The address for the code for this fragment in codegen memory. Used for
87   /// functions that are emitted in a dedicated section with a fixed address,
88   /// e.g. for functions that are overwritten in-place.
89   uint64_t ImageAddress = 0;
90 
91   /// The size of the code in memory.
92   uint64_t ImageSize = 0;
93 
94   /// Offset in the file.
95   uint64_t FileOffset = 0;
96 
97   FunctionFragment(FunctionLayout &Layout, FragmentNum Num);
98   FunctionFragment(const FunctionFragment &) = default;
99   FunctionFragment(FunctionFragment &&) = default;
100   FunctionFragment &operator=(const FunctionFragment &) = default;
101   FunctionFragment &operator=(FunctionFragment &&) = default;
102   ~FunctionFragment() = default;
103 
104 public:
105   FragmentNum getFragmentNum() const { return Num; }
106   bool isMainFragment() const {
107     return getFragmentNum() == FragmentNum::main();
108   }
109   bool isSplitFragment() const { return !isMainFragment(); }
110 
111   uint64_t getAddress() const { return Address; }
112   void setAddress(uint64_t Value) { Address = Value; }
113   uint64_t getImageAddress() const { return ImageAddress; }
114   void setImageAddress(uint64_t Address) { ImageAddress = Address; }
115   uint64_t getImageSize() const { return ImageSize; }
116   void setImageSize(uint64_t Size) { ImageSize = Size; }
117   uint64_t getFileOffset() const { return FileOffset; }
118   void setFileOffset(uint64_t Offset) { FileOffset = Offset; }
119 
120   unsigned size() const { return Size; };
121   bool empty() const { return size() == 0; };
122   iterator begin();
123   const_iterator begin() const;
124   iterator end();
125   const_iterator end() const;
126   BinaryBasicBlock *front() const;
127   BinaryBasicBlock *back() const;
128 
129   friend class FunctionLayout;
130 };
131 
132 /// The function layout represents the fragments we split a function into and
133 /// the order of basic blocks within each fragment.
134 ///
135 /// Internally, the function layout stores blocks across fragments contiguously.
136 /// This is necessary to retain compatibility with existing code and tests that
137 /// iterate  over all blocks of the layout and depend on that order. When
138 /// writing new code, avoid iterating using FunctionLayout::blocks() by
139 /// iterating either over fragments or over BinaryFunction::begin()..end().
140 class FunctionLayout {
141 private:
142   using FragmentListType = SmallVector<FunctionFragment *, 0>;
143   using BasicBlockListType = SmallVector<BinaryBasicBlock *, 0>;
144 
145 public:
146   using fragment_iterator = pointee_iterator<FragmentListType::const_iterator>;
147   using fragment_const_iterator =
148       pointee_iterator<FragmentListType::const_iterator,
149                        const FunctionFragment>;
150   using block_iterator = BasicBlockListType::iterator;
151   using block_const_iterator = BasicBlockListType::const_iterator;
152   using block_reverse_iterator = std::reverse_iterator<block_iterator>;
153   using block_const_reverse_iterator =
154       std::reverse_iterator<block_const_iterator>;
155 
156 private:
157   FragmentListType Fragments;
158   BasicBlockListType Blocks;
159 
160 public:
161   FunctionLayout();
162   FunctionLayout(const FunctionLayout &Other);
163   FunctionLayout(FunctionLayout &&Other);
164   FunctionLayout &operator=(const FunctionLayout &Other);
165   FunctionLayout &operator=(FunctionLayout &&Other);
166   ~FunctionLayout();
167 
168   /// Add an empty fragment.
169   FunctionFragment &addFragment();
170 
171   /// Return the fragment identified by Num.
172   FunctionFragment &getFragment(FragmentNum Num);
173 
174   /// Return the fragment identified by Num.
175   const FunctionFragment &getFragment(FragmentNum Num) const;
176 
177   /// Get the fragment that contains all entry blocks and other blocks that
178   /// cannot be split.
179   FunctionFragment &getMainFragment() {
180     return getFragment(FragmentNum::main());
181   }
182 
183   /// Get the fragment that contains all entry blocks and other blocks that
184   /// cannot be split.
185   const FunctionFragment &getMainFragment() const {
186     return getFragment(FragmentNum::main());
187   }
188 
189   /// Get the fragment that contains all entry blocks and other blocks that
190   /// cannot be split.
191   iterator_range<fragment_iterator> getSplitFragments() {
192     return {++fragment_begin(), fragment_end()};
193   }
194 
195   /// Get the fragment that contains all entry blocks and other blocks that
196   /// cannot be split.
197   iterator_range<fragment_const_iterator> getSplitFragments() const {
198     return {++fragment_begin(), fragment_end()};
199   }
200 
201   /// Find the fragment that contains BB.
202   const FunctionFragment &findFragment(const BinaryBasicBlock *BB) const;
203 
204   /// Add BB to the end of the last fragment.
205   void addBasicBlock(BinaryBasicBlock *BB);
206 
207   /// Insert range of basic blocks after InsertAfter. If InsertAfter is nullptr,
208   /// the blocks will be inserted at the start of the function.
209   void insertBasicBlocks(const BinaryBasicBlock *InsertAfter,
210                          ArrayRef<BinaryBasicBlock *> NewBlocks);
211 
212   /// Erase all blocks from the layout that are in ToErase. If this method
213   /// erases all blocks of a fragment, it will be removed as well.
214   void eraseBasicBlocks(const DenseSet<const BinaryBasicBlock *> ToErase);
215 
216   /// Make sure fragments' and basic blocks' indices match the current layout.
217   void updateLayoutIndices() const;
218   void updateLayoutIndices(ArrayRef<BinaryBasicBlock *> Order) const;
219 
220   /// Replace the current layout with NewLayout. Uses the block's
221   /// self-identifying fragment number to assign blocks to infer function
222   /// fragments. Returns `true` if the new layout is different from the current
223   /// layout.
224   bool update(ArrayRef<BinaryBasicBlock *> NewLayout);
225 
226   /// Clear layout releasing memory.
227   void clear();
228 
229   BinaryBasicBlock *getBlock(unsigned Index) { return Blocks[Index]; }
230 
231   const BinaryBasicBlock *getBlock(unsigned Index) const {
232     return Blocks[Index];
233   }
234 
235   /// Returns the basic block after the given basic block in the layout or
236   /// nullptr if the last basic block is given.
237   BinaryBasicBlock *getBasicBlockAfter(const BinaryBasicBlock *const BB,
238                                        const bool IgnoreSplits = true) {
239     return const_cast<BinaryBasicBlock *>(
240         static_cast<const FunctionLayout &>(*this).getBasicBlockAfter(
241             BB, IgnoreSplits));
242   }
243 
244   /// Returns the basic block after the given basic block in the layout or
245   /// nullptr if the last basic block is given.
246   const BinaryBasicBlock *getBasicBlockAfter(const BinaryBasicBlock *BB,
247                                              bool IgnoreSplits = true) const;
248 
249   /// True if the layout contains at least two non-empty fragments.
250   bool isSplit() const;
251 
252   /// Get the edit distance of the new layout with respect to the previous
253   /// layout after basic block reordering.
254   uint64_t
255   getEditDistance(ArrayRef<const BinaryBasicBlock *> OldBlockOrder) const;
256 
257   /// True if the function is split into at most 2 fragments. Mostly used for
258   /// checking whether a function can be processed in places that do not support
259   /// multiple fragments yet.
260   bool isHotColdSplit() const { return fragment_size() <= 2; }
261 
262   size_t fragment_size() const {
263     assert(Fragments.size() >= 1 &&
264            "Layout should have at least one fragment.");
265     return Fragments.size();
266   }
267   bool fragment_empty() const { return fragment_size() == 0; }
268 
269   fragment_iterator fragment_begin() { return Fragments.begin(); }
270   fragment_const_iterator fragment_begin() const { return Fragments.begin(); }
271   fragment_iterator fragment_end() { return Fragments.end(); }
272   fragment_const_iterator fragment_end() const { return Fragments.end(); }
273   iterator_range<fragment_iterator> fragments() {
274     return {fragment_begin(), fragment_end()};
275   }
276   iterator_range<fragment_const_iterator> fragments() const {
277     return {fragment_begin(), fragment_end()};
278   }
279 
280   size_t block_size() const { return Blocks.size(); }
281   bool block_empty() const { return Blocks.empty(); }
282 
283   /// Required to return non-const qualified `BinaryBasicBlock *` for graph
284   /// traits.
285   BinaryBasicBlock *block_front() const { return Blocks.front(); }
286   const BinaryBasicBlock *block_back() const { return Blocks.back(); }
287 
288   block_iterator block_begin() { return Blocks.begin(); }
289   block_const_iterator block_begin() const {
290     return block_const_iterator(Blocks.begin());
291   }
292   block_iterator block_end() { return Blocks.end(); }
293   block_const_iterator block_end() const {
294     return block_const_iterator(Blocks.end());
295   }
296   iterator_range<block_iterator> blocks() {
297     return {block_begin(), block_end()};
298   }
299   iterator_range<block_const_iterator> blocks() const {
300     return {block_begin(), block_end()};
301   }
302   block_reverse_iterator block_rbegin() {
303     return block_reverse_iterator(Blocks.rbegin());
304   }
305   block_const_reverse_iterator block_rbegin() const {
306     return block_const_reverse_iterator(
307         std::make_reverse_iterator(block_end()));
308   }
309   block_reverse_iterator block_rend() {
310     return block_reverse_iterator(Blocks.rend());
311   }
312   block_const_reverse_iterator block_rend() const {
313     return block_const_reverse_iterator(
314         std::make_reverse_iterator(block_begin()));
315   }
316   iterator_range<block_const_reverse_iterator> rblocks() const {
317     return {block_rbegin(), block_rend()};
318   }
319 
320 private:
321   block_const_iterator findBasicBlockPos(const BinaryBasicBlock *BB) const;
322   block_iterator findBasicBlockPos(const BinaryBasicBlock *BB);
323 
324   friend class FunctionFragment;
325 };
326 
327 } // namespace bolt
328 } // namespace llvm
329 
330 #endif
331