blob: ee4dd689b8dd647580f5ae4f4572e84e8968f31a [file] [log] [blame]
//===- bolt/Core/FunctionLayout.h - Fragmented Function Layout --*- 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
//
//===----------------------------------------------------------------------===//
//
// This file contains the declaration of the FunctionLayout class. The layout of
// a function is the order of basic blocks, in which we will arrange them in the
// new binary. Normally, when not optimizing for code layout, the blocks of a
// function are contiguous. However, we can split the layout into multiple
// fragments. The blocks within a fragment are contiguous, but the fragments
// itself are disjoint. Fragments could be used to enhance code layout, e.g. to
// separate the blocks into hot and cold sections.
//
//===----------------------------------------------------------------------===//
#ifndef BOLT_CORE_FUNCTION_LAYOUT_H
#define BOLT_CORE_FUNCTION_LAYOUT_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include <iterator>
namespace llvm {
namespace bolt {
class BinaryFunction;
class BinaryBasicBlock;
class FunctionLayout;
class FragmentNum {
unsigned Value{0};
public:
constexpr FragmentNum() = default;
constexpr explicit FragmentNum(unsigned Value) : Value(Value) {}
constexpr unsigned get() const { return Value; }
constexpr bool operator==(const FragmentNum Other) const {
return Value == Other.Value;
}
constexpr bool operator!=(const FragmentNum Other) const {
return Value != Other.Value;
}
constexpr bool operator<(const FragmentNum Other) const {
return Value < Other.Value;
}
constexpr bool operator<=(const FragmentNum Other) const {
return Value <= Other.Value;
}
constexpr bool operator>=(const FragmentNum Other) const {
return Value >= Other.Value;
}
constexpr bool operator>(const FragmentNum Other) const {
return Value > Other.Value;
}
static constexpr FragmentNum main() { return FragmentNum(0); }
static constexpr FragmentNum cold() { return FragmentNum(1); }
static constexpr FragmentNum warm() { return FragmentNum(2); }
};
/// A freestanding subset of contiguous blocks of a function.
class FunctionFragment {
using BasicBlockListType = SmallVector<BinaryBasicBlock *, 0>;
using FragmentListType = SmallVector<unsigned, 0>;
public:
using iterator = BasicBlockListType::iterator;
using const_iterator = BasicBlockListType::const_iterator;
private:
FunctionLayout *Layout;
FragmentNum Num;
unsigned StartIndex;
unsigned Size = 0;
/// Output address for the fragment.
uint64_t Address = 0;
/// The address for the code for this fragment in codegen memory. Used for
/// functions that are emitted in a dedicated section with a fixed address,
/// e.g. for functions that are overwritten in-place.
uint64_t ImageAddress = 0;
/// The size of the code in memory.
uint64_t ImageSize = 0;
/// Offset in the file.
uint64_t FileOffset = 0;
FunctionFragment(FunctionLayout &Layout, FragmentNum Num);
FunctionFragment(const FunctionFragment &) = default;
FunctionFragment(FunctionFragment &&) = default;
FunctionFragment &operator=(const FunctionFragment &) = default;
FunctionFragment &operator=(FunctionFragment &&) = default;
~FunctionFragment() = default;
public:
FragmentNum getFragmentNum() const { return Num; }
bool isMainFragment() const {
return getFragmentNum() == FragmentNum::main();
}
bool isSplitFragment() const { return !isMainFragment(); }
uint64_t getAddress() const { return Address; }
void setAddress(uint64_t Value) { Address = Value; }
uint64_t getImageAddress() const { return ImageAddress; }
void setImageAddress(uint64_t Address) { ImageAddress = Address; }
uint64_t getImageSize() const { return ImageSize; }
void setImageSize(uint64_t Size) { ImageSize = Size; }
uint64_t getFileOffset() const { return FileOffset; }
void setFileOffset(uint64_t Offset) { FileOffset = Offset; }
unsigned size() const { return Size; };
bool empty() const { return size() == 0; };
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
BinaryBasicBlock *front() const;
BinaryBasicBlock *back() const;
friend class FunctionLayout;
};
/// The function layout represents the fragments we split a function into and
/// the order of basic blocks within each fragment.
///
/// Internally, the function layout stores blocks across fragments contiguously.
/// This is necessary to retain compatibility with existing code and tests that
/// iterate over all blocks of the layout and depend on that order. When
/// writing new code, avoid iterating using FunctionLayout::blocks() by
/// iterating either over fragments or over BinaryFunction::begin()..end().
class FunctionLayout {
private:
using FragmentListType = SmallVector<FunctionFragment *, 0>;
using BasicBlockListType = SmallVector<BinaryBasicBlock *, 0>;
public:
using fragment_iterator = pointee_iterator<FragmentListType::const_iterator>;
using fragment_const_iterator =
pointee_iterator<FragmentListType::const_iterator,
const FunctionFragment>;
using block_iterator = BasicBlockListType::iterator;
using block_const_iterator = BasicBlockListType::const_iterator;
using block_reverse_iterator = std::reverse_iterator<block_iterator>;
using block_const_reverse_iterator =
std::reverse_iterator<block_const_iterator>;
private:
FragmentListType Fragments;
BasicBlockListType Blocks;
public:
FunctionLayout();
FunctionLayout(const FunctionLayout &Other);
FunctionLayout(FunctionLayout &&Other);
FunctionLayout &operator=(const FunctionLayout &Other);
FunctionLayout &operator=(FunctionLayout &&Other);
~FunctionLayout();
/// Add an empty fragment.
FunctionFragment &addFragment();
/// Return the fragment identified by Num.
FunctionFragment &getFragment(FragmentNum Num);
/// Return the fragment identified by Num.
const FunctionFragment &getFragment(FragmentNum Num) const;
/// Get the fragment that contains all entry blocks and other blocks that
/// cannot be split.
FunctionFragment &getMainFragment() {
return getFragment(FragmentNum::main());
}
/// Get the fragment that contains all entry blocks and other blocks that
/// cannot be split.
const FunctionFragment &getMainFragment() const {
return getFragment(FragmentNum::main());
}
/// Get the fragment that contains all entry blocks and other blocks that
/// cannot be split.
iterator_range<fragment_iterator> getSplitFragments() {
return {++fragment_begin(), fragment_end()};
}
/// Get the fragment that contains all entry blocks and other blocks that
/// cannot be split.
iterator_range<fragment_const_iterator> getSplitFragments() const {
return {++fragment_begin(), fragment_end()};
}
/// Find the fragment that contains BB.
const FunctionFragment &findFragment(const BinaryBasicBlock *BB) const;
/// Add BB to the end of the last fragment.
void addBasicBlock(BinaryBasicBlock *BB);
/// Insert range of basic blocks after InsertAfter. If InsertAfter is nullptr,
/// the blocks will be inserted at the start of the function.
void insertBasicBlocks(const BinaryBasicBlock *InsertAfter,
ArrayRef<BinaryBasicBlock *> NewBlocks);
/// Erase all blocks from the layout that are in ToErase. If this method
/// erases all blocks of a fragment, it will be removed as well.
void eraseBasicBlocks(const DenseSet<const BinaryBasicBlock *> ToErase);
/// Make sure fragments' and basic blocks' indices match the current layout.
void updateLayoutIndices() const;
void updateLayoutIndices(ArrayRef<BinaryBasicBlock *> Order) const;
/// Replace the current layout with NewLayout. Uses the block's
/// self-identifying fragment number to assign blocks to infer function
/// fragments. Returns `true` if the new layout is different from the current
/// layout.
bool update(ArrayRef<BinaryBasicBlock *> NewLayout);
/// Clear layout releasing memory.
void clear();
BinaryBasicBlock *getBlock(unsigned Index) { return Blocks[Index]; }
const BinaryBasicBlock *getBlock(unsigned Index) const {
return Blocks[Index];
}
/// Returns the basic block after the given basic block in the layout or
/// nullptr if the last basic block is given.
BinaryBasicBlock *getBasicBlockAfter(const BinaryBasicBlock *const BB,
const bool IgnoreSplits = true) {
return const_cast<BinaryBasicBlock *>(
static_cast<const FunctionLayout &>(*this).getBasicBlockAfter(
BB, IgnoreSplits));
}
/// Returns the basic block after the given basic block in the layout or
/// nullptr if the last basic block is given.
const BinaryBasicBlock *getBasicBlockAfter(const BinaryBasicBlock *BB,
bool IgnoreSplits = true) const;
/// True if the layout contains at least two non-empty fragments.
bool isSplit() const;
/// Get the edit distance of the new layout with respect to the previous
/// layout after basic block reordering.
uint64_t
getEditDistance(ArrayRef<const BinaryBasicBlock *> OldBlockOrder) const;
/// True if the function is split into at most 2 fragments. Mostly used for
/// checking whether a function can be processed in places that do not support
/// multiple fragments yet.
bool isHotColdSplit() const { return fragment_size() <= 2; }
size_t fragment_size() const {
assert(Fragments.size() >= 1 &&
"Layout should have at least one fragment.");
return Fragments.size();
}
bool fragment_empty() const { return fragment_size() == 0; }
fragment_iterator fragment_begin() { return Fragments.begin(); }
fragment_const_iterator fragment_begin() const { return Fragments.begin(); }
fragment_iterator fragment_end() { return Fragments.end(); }
fragment_const_iterator fragment_end() const { return Fragments.end(); }
iterator_range<fragment_iterator> fragments() {
return {fragment_begin(), fragment_end()};
}
iterator_range<fragment_const_iterator> fragments() const {
return {fragment_begin(), fragment_end()};
}
size_t block_size() const { return Blocks.size(); }
bool block_empty() const { return Blocks.empty(); }
/// Required to return non-const qualified `BinaryBasicBlock *` for graph
/// traits.
BinaryBasicBlock *block_front() const { return Blocks.front(); }
const BinaryBasicBlock *block_back() const { return Blocks.back(); }
block_iterator block_begin() { return Blocks.begin(); }
block_const_iterator block_begin() const {
return block_const_iterator(Blocks.begin());
}
block_iterator block_end() { return Blocks.end(); }
block_const_iterator block_end() const {
return block_const_iterator(Blocks.end());
}
iterator_range<block_iterator> blocks() {
return {block_begin(), block_end()};
}
iterator_range<block_const_iterator> blocks() const {
return {block_begin(), block_end()};
}
block_reverse_iterator block_rbegin() {
return block_reverse_iterator(Blocks.rbegin());
}
block_const_reverse_iterator block_rbegin() const {
return block_const_reverse_iterator(
std::make_reverse_iterator(block_end()));
}
block_reverse_iterator block_rend() {
return block_reverse_iterator(Blocks.rend());
}
block_const_reverse_iterator block_rend() const {
return block_const_reverse_iterator(
std::make_reverse_iterator(block_begin()));
}
iterator_range<block_const_reverse_iterator> rblocks() const {
return {block_rbegin(), block_rend()};
}
private:
block_const_iterator findBasicBlockPos(const BinaryBasicBlock *BB) const;
block_iterator findBasicBlockPos(const BinaryBasicBlock *BB);
friend class FunctionFragment;
};
} // namespace bolt
} // namespace llvm
#endif