llvm-project/bolt/lib/Core/FunctionLayout.cpp
shaw young 629b6f4eb1
[BOLT][NFC] Extend updateLayoutIndices (#93861)
Make FunctionLayout::updateLayoutIndices const and add an overloaded
function that updates LayoutIndices given an Order parameter.
2024-05-30 14:35:29 -07:00

262 lines
8.0 KiB
C++

//===- bolt/Core/FunctionLayout.cpp - 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
//
//===----------------------------------------------------------------------===//
#include "bolt/Core/FunctionLayout.h"
#include "bolt/Core/BinaryBasicBlock.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/edit_distance.h"
#include <algorithm>
#include <iterator>
using namespace llvm;
using namespace bolt;
FunctionFragment::FunctionFragment(FunctionLayout &Layout,
const FragmentNum Num)
: Layout(&Layout), Num(Num), StartIndex(Layout.block_size()) {}
FunctionFragment::iterator FunctionFragment::begin() {
return iterator(Layout->block_begin() + StartIndex);
}
FunctionFragment::const_iterator FunctionFragment::begin() const {
return const_iterator(Layout->block_begin() + StartIndex);
}
FunctionFragment::iterator FunctionFragment::end() {
return iterator(Layout->block_begin() + StartIndex + Size);
}
FunctionFragment::const_iterator FunctionFragment::end() const {
return const_iterator(Layout->block_begin() + StartIndex + Size);
}
const BinaryBasicBlock *FunctionFragment::front() const { return *begin(); }
FunctionLayout::FunctionLayout() { addFragment(); }
FunctionLayout::FunctionLayout(const FunctionLayout &Other)
: Blocks(Other.Blocks) {
for (FunctionFragment *const FF : Other.Fragments) {
auto *Copy = new FunctionFragment(*FF);
Copy->Layout = this;
Fragments.emplace_back(Copy);
}
}
FunctionLayout::FunctionLayout(FunctionLayout &&Other)
: Fragments(std::move(Other.Fragments)), Blocks(std::move(Other.Blocks)) {
for (FunctionFragment *const F : Fragments)
F->Layout = this;
}
FunctionLayout &FunctionLayout::operator=(const FunctionLayout &Other) {
Blocks = Other.Blocks;
for (FunctionFragment *const FF : Other.Fragments) {
auto *const Copy = new FunctionFragment(*FF);
Copy->Layout = this;
Fragments.emplace_back(Copy);
}
return *this;
}
FunctionLayout &FunctionLayout::operator=(FunctionLayout &&Other) {
Fragments = std::move(Other.Fragments);
Blocks = std::move(Other.Blocks);
for (FunctionFragment *const FF : Fragments)
FF->Layout = this;
return *this;
}
FunctionLayout::~FunctionLayout() {
for (FunctionFragment *const F : Fragments) {
delete F;
}
}
FunctionFragment &FunctionLayout::addFragment() {
FunctionFragment *const FF =
new FunctionFragment(*this, FragmentNum(Fragments.size()));
Fragments.emplace_back(FF);
return *FF;
}
FunctionFragment &FunctionLayout::getFragment(FragmentNum Num) {
return *Fragments[Num.get()];
}
const FunctionFragment &FunctionLayout::getFragment(FragmentNum Num) const {
return *Fragments[Num.get()];
}
const FunctionFragment &
FunctionLayout::findFragment(const BinaryBasicBlock *const BB) const {
return getFragment(BB->getFragmentNum());
}
void FunctionLayout::addBasicBlock(BinaryBasicBlock *const BB) {
BB->setLayoutIndex(Blocks.size());
Blocks.emplace_back(BB);
Fragments.back()->Size++;
}
void FunctionLayout::insertBasicBlocks(
const BinaryBasicBlock *const InsertAfter,
const ArrayRef<BinaryBasicBlock *> NewBlocks) {
block_iterator InsertBeforePos = Blocks.begin();
FragmentNum InsertFragmentNum = FragmentNum::main();
unsigned LayoutIndex = 0;
if (InsertAfter) {
InsertBeforePos = std::next(findBasicBlockPos(InsertAfter));
InsertFragmentNum = InsertAfter->getFragmentNum();
LayoutIndex = InsertAfter->getLayoutIndex();
}
llvm::copy(NewBlocks, std::inserter(Blocks, InsertBeforePos));
for (BinaryBasicBlock *const BB : NewBlocks) {
BB->setFragmentNum(InsertFragmentNum);
BB->setLayoutIndex(LayoutIndex++);
}
const fragment_iterator InsertFragment =
fragment_begin() + InsertFragmentNum.get();
InsertFragment->Size += NewBlocks.size();
const fragment_iterator TailBegin = std::next(InsertFragment);
auto const UpdateFragment = [&](FunctionFragment &FF) {
FF.StartIndex += NewBlocks.size();
for (BinaryBasicBlock *const BB : FF)
BB->setLayoutIndex(LayoutIndex++);
};
std::for_each(TailBegin, fragment_end(), UpdateFragment);
}
void FunctionLayout::eraseBasicBlocks(
const DenseSet<const BinaryBasicBlock *> ToErase) {
const auto IsErased = [&](const BinaryBasicBlock *const BB) {
return ToErase.contains(BB);
};
unsigned TotalErased = 0;
for (FunctionFragment &FF : fragments()) {
unsigned Erased = count_if(FF, IsErased);
FF.Size -= Erased;
FF.StartIndex -= TotalErased;
TotalErased += Erased;
}
llvm::erase_if(Blocks, IsErased);
// Remove empty fragments at the end
const auto IsEmpty = [](const FunctionFragment *const FF) {
return FF->empty();
};
const FragmentListType::iterator EmptyTailBegin =
llvm::find_if_not(reverse(Fragments), IsEmpty).base();
for (FunctionFragment *const FF :
llvm::make_range(EmptyTailBegin, Fragments.end()))
delete FF;
Fragments.erase(EmptyTailBegin, Fragments.end());
updateLayoutIndices();
}
void FunctionLayout::updateLayoutIndices() const {
unsigned BlockIndex = 0;
for (const FunctionFragment &FF : fragments()) {
for (BinaryBasicBlock *const BB : FF) {
BB->setLayoutIndex(BlockIndex++);
BB->setFragmentNum(FF.getFragmentNum());
}
}
}
void FunctionLayout::updateLayoutIndices(
ArrayRef<BinaryBasicBlock *> Order) const {
for (auto [Index, BB] : llvm::enumerate(Order))
BB->setLayoutIndex(Index);
}
bool FunctionLayout::update(const ArrayRef<BinaryBasicBlock *> NewLayout) {
const bool EqualBlockOrder = llvm::equal(Blocks, NewLayout);
if (EqualBlockOrder) {
const bool EqualPartitioning =
llvm::all_of(fragments(), [](const FunctionFragment &FF) {
return llvm::all_of(FF, [&](const BinaryBasicBlock *const BB) {
return FF.Num == BB->getFragmentNum();
});
});
if (EqualPartitioning)
return false;
}
clear();
// Generate fragments
for (BinaryBasicBlock *const BB : NewLayout) {
FragmentNum Num = BB->getFragmentNum();
// Add empty fragments if necessary
while (Fragments.back()->getFragmentNum() < Num)
addFragment();
// Set the next fragment to point one past the current BB
addBasicBlock(BB);
}
return true;
}
void FunctionLayout::clear() {
Blocks = BasicBlockListType();
// If the binary does not have relocations and is not split, the function will
// be written to the output stream at its original file offset (see
// `RewriteInstance::rewriteFile`). Hence, when the layout is cleared, retain
// the main fragment, so that this information is not lost.
for (FunctionFragment *const FF : llvm::drop_begin(Fragments))
delete FF;
Fragments = FragmentListType{Fragments.front()};
getMainFragment().Size = 0;
}
const BinaryBasicBlock *
FunctionLayout::getBasicBlockAfter(const BinaryBasicBlock *BB,
bool IgnoreSplits) const {
const block_const_iterator BBPos = find(blocks(), BB);
if (BBPos == block_end())
return nullptr;
const block_const_iterator BlockAfter = std::next(BBPos);
if (BlockAfter == block_end())
return nullptr;
if (!IgnoreSplits)
if (BlockAfter == getFragment(BB->getFragmentNum()).end())
return nullptr;
return *BlockAfter;
}
bool FunctionLayout::isSplit() const {
const unsigned NonEmptyFragCount = llvm::count_if(
fragments(), [](const FunctionFragment &FF) { return !FF.empty(); });
return NonEmptyFragCount >= 2;
}
uint64_t FunctionLayout::getEditDistance(
const ArrayRef<const BinaryBasicBlock *> OldBlockOrder) const {
return ComputeEditDistance<const BinaryBasicBlock *>(OldBlockOrder, Blocks);
}
FunctionLayout::block_const_iterator
FunctionLayout::findBasicBlockPos(const BinaryBasicBlock *BB) const {
return block_const_iterator(find(Blocks, BB));
}
FunctionLayout::block_iterator
FunctionLayout::findBasicBlockPos(const BinaryBasicBlock *BB) {
return find(Blocks, BB);
}