[BOLT] Add main fragment to function layout

Functions that do not contain any code still have to be emitted. This
occurs on AArch64 where functions can consist only of a constant island.
To support fragment semantics in code emission, this commits adds a
guaranteed main fragment to function layout. This fragment might be
empty, but allows us omit checks whether the function is empty in most
places.

Reviewed By: rafauler

Differential Revision: https://reviews.llvm.org/D130051
This commit is contained in:
Fabian Parzefall 2022-08-17 14:51:29 -07:00
parent 7ed3d81333
commit 0f8412c19c
4 changed files with 51 additions and 38 deletions

View File

@ -23,6 +23,7 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
namespace llvm {
namespace bolt {
@ -100,7 +101,10 @@ public:
const FunctionLayout *Layout;
FragmentIterator(FragmentNum Num, const FunctionLayout *Layout)
: Num(Num), Layout(Layout) {}
: Num(Num), Layout(Layout) {
assert(Num.get() <= Layout->fragment_size() &&
"Initializing iterator out of bounds");
}
public:
bool operator==(const FragmentIterator &Other) const {
@ -108,15 +112,20 @@ public:
}
FunctionFragment operator*() const {
assert(Num.get() < Layout->fragment_size() &&
"Dereferencing end() iterator (or past it)");
return FunctionFragment(Num, *Layout);
}
FragmentIterator &operator++() {
assert(Num.get() < Layout->fragment_size() &&
"Incrementing iterator past end()");
Num = FragmentNum(Num.get() + 1);
return *this;
}
FragmentIterator &operator--() {
assert(Num.get() > 0 && "Decrementing iterator past begin()");
Num = FragmentNum(Num.get() - 1);
return *this;
}
@ -133,8 +142,9 @@ private:
BasicBlockListType Blocks;
/// List of indices dividing block list into fragments. To simplify iteration,
/// we have `Fragments.back()` equals `Blocks.size()`. Hence,
/// `Fragments.size()` equals `this->size() + 1`.
FragmentListType Fragments = {0};
/// `Fragments.size()` equals `this->size() + 1`. Always contains at least one
/// fragment.
FragmentListType Fragments = {0, 0};
BasicBlockListType PreviousBlocks;
FragmentListType PreviousFragments;
@ -188,14 +198,23 @@ public:
/// layout after basic block reordering.
uint64_t getEditDistance() const;
size_t size() const { return Fragments.size() - 1; }
bool empty() const { return Fragments.size() == 1; }
const_iterator begin() const { return {FragmentNum(0), this}; }
const_iterator end() const { return {FragmentNum(size()), this}; }
FunctionFragment front() const { return *begin(); }
FunctionFragment back() const { return *std::prev(end()); }
FunctionFragment operator[](const FragmentNum Num) const {
return getFragment(Num);
/// 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() >= 2 &&
"Layout should have at least one fragment.");
return Fragments.size() - 1;
}
bool fragment_empty() const { return Fragments.size() == 1; }
const_iterator fragment_begin() const { return {FragmentNum(0), this}; }
const_iterator fragment_end() const {
return {FragmentNum(fragment_size()), this};
}
iterator_range<const_iterator> fragments() const {
return {fragment_begin(), fragment_end()};
}
size_t block_size() const { return Blocks.size(); }

View File

@ -15,6 +15,7 @@
#include "bolt/Core/BinaryContext.h"
#include "bolt/Core/BinaryFunction.h"
#include "bolt/Core/DebugData.h"
#include "bolt/Core/FunctionLayout.h"
#include "bolt/Utils/CommandLineOpts.h"
#include "bolt/Utils/Utils.h"
#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"
@ -396,12 +397,12 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, bool EmitColdPart,
if (!EmitCodeOnly && EmitColdPart && BF.hasConstantIsland())
BF.duplicateConstantIslands();
const FunctionFragment FF = BF.getLayout().getFragment(
EmitColdPart ? FragmentNum::cold() : FragmentNum::hot());
// Track the first emitted instruction with debug info.
bool FirstInstr = true;
for (BinaryBasicBlock *BB : BF.getLayout().blocks()) {
if (EmitColdPart != BB->isCold())
continue;
for (BinaryBasicBlock *const BB : FF) {
if ((opts::AlignBlocks || opts::PreserveBlocksAlignment) &&
BB->getAlignment() > 1)
Streamer.emitCodeAlignment(BB->getAlignment(), &*BC.STI,

View File

@ -503,10 +503,10 @@ void BinaryFunction::print(raw_ostream &OS, std::string Annotation,
}
StringRef SplitPointMsg = "";
for (const FunctionFragment F : Layout) {
for (const FunctionFragment FF : Layout.fragments()) {
OS << SplitPointMsg;
SplitPointMsg = "------- HOT-COLD SPLIT POINT -------\n\n";
for (const BinaryBasicBlock *BB : F) {
for (const BinaryBasicBlock *BB : FF) {
OS << BB->getName() << " (" << BB->size()
<< " instructions, align : " << BB->getAlignment() << ")\n";
@ -2782,12 +2782,12 @@ bool BinaryFunction::finalizeCFIState() {
const char *Sep = "";
(void)Sep;
for (const FunctionFragment F : Layout) {
for (const FunctionFragment FF : Layout.fragments()) {
// Hot-cold border: at start of each region (with a different FDE) we need
// to reset the CFI state.
int32_t State = 0;
for (BinaryBasicBlock *BB : F) {
for (BinaryBasicBlock *BB : FF) {
const int32_t CFIStateAtExit = BB->getCFIStateAtExit();
// We need to recover the correct state if it doesn't match expected

View File

@ -20,7 +20,7 @@ BinaryBasicBlock *FunctionFragment::front() const { return *begin(); }
FunctionFragment FunctionLayout::addFragment() {
Fragments.emplace_back(Blocks.size());
return back();
return getFragment(FragmentNum(Blocks.size() - 1));
}
FunctionFragment FunctionLayout::getFragment(FragmentNum Num) const {
@ -33,21 +33,14 @@ FunctionLayout::findFragment(const BinaryBasicBlock *BB) const {
}
void FunctionLayout::addBasicBlock(BinaryBasicBlock *BB) {
if (empty())
addFragment();
BB->setLayoutIndex(Blocks.size());
Blocks.emplace_back(BB);
++Fragments.back();
assert(Fragments.back() == Blocks.size());
}
void FunctionLayout::insertBasicBlocks(BinaryBasicBlock *InsertAfter,
ArrayRef<BinaryBasicBlock *> NewBlocks) {
if (empty())
addFragment();
const block_iterator InsertBeforePos =
InsertAfter ? std::next(findBasicBlockPos(InsertAfter)) : Blocks.begin();
Blocks.insert(InsertBeforePos, NewBlocks.begin(), NewBlocks.end());
@ -66,9 +59,9 @@ void FunctionLayout::eraseBasicBlocks(
};
FragmentListType NewFragments;
NewFragments.emplace_back(0);
for (const FunctionFragment F : *this) {
unsigned ErasedBlocks = count_if(F, IsErased);
unsigned NewFragment = NewFragments.back() + F.size() - ErasedBlocks;
for (const FunctionFragment FF : fragments()) {
unsigned ErasedBlocks = count_if(FF, IsErased);
unsigned NewFragment = NewFragments.back() + FF.size() - ErasedBlocks;
NewFragments.emplace_back(NewFragment);
}
llvm::erase_if(Blocks, IsErased);
@ -77,8 +70,8 @@ void FunctionLayout::eraseBasicBlocks(
void FunctionLayout::updateLayoutIndices() const {
unsigned BlockIndex = 0;
for (const FunctionFragment F : *this) {
for (BinaryBasicBlock *const BB : F)
for (const FunctionFragment FF : fragments()) {
for (BinaryBasicBlock *const BB : FF)
BB->setLayoutIndex(BlockIndex++);
}
}
@ -88,7 +81,7 @@ void FunctionLayout::update(const ArrayRef<BinaryBasicBlock *> NewLayout) {
PreviousFragments = std::move(Fragments);
Blocks.clear();
Fragments = {0};
Fragments = {0, 0};
if (NewLayout.empty())
return;
@ -99,12 +92,12 @@ void FunctionLayout::update(const ArrayRef<BinaryBasicBlock *> NewLayout) {
for (const auto &BB : enumerate(Blocks)) {
unsigned FragmentNum = BB.value()->getFragmentNum().get();
assert(FragmentNum + 1 >= size() &&
assert(FragmentNum >= fragment_size() - 1 &&
"Blocks must be arranged such that fragments are monotonically "
"increasing.");
// Add empty fragments if necessary
for (unsigned I = size(); I <= FragmentNum; ++I) {
for (unsigned I = fragment_size(); I <= FragmentNum; ++I) {
addFragment();
Fragments[I] = BB.index();
}
@ -123,9 +116,9 @@ void FunctionLayout::update(const ArrayRef<BinaryBasicBlock *> NewLayout) {
void FunctionLayout::clear() {
Blocks = {};
Fragments = {0};
Fragments = {0, 0};
PreviousBlocks = {};
PreviousFragments = {0};
PreviousFragments = {0, 0};
}
BinaryBasicBlock *FunctionLayout::getBasicBlockAfter(const BinaryBasicBlock *BB,
@ -147,7 +140,7 @@ BinaryBasicBlock *FunctionLayout::getBasicBlockAfter(const BinaryBasicBlock *BB,
bool FunctionLayout::isSplit() const {
unsigned NonEmptyFragCount = llvm::count_if(
*this, [](const FunctionFragment &F) { return !F.empty(); });
fragments(), [](const FunctionFragment &FF) { return !FF.empty(); });
return NonEmptyFragCount >= 2;
}