//===- 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 #include 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); } BinaryBasicBlock *FunctionFragment::front() const { return *begin(); } BinaryBasicBlock *FunctionFragment::back() const { return *std::prev(end()); } 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 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 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 Order) const { for (auto [Index, BB] : llvm::enumerate(Order)) BB->setLayoutIndex(Index); } bool FunctionLayout::update(const ArrayRef 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 OldBlockOrder) const { return ComputeEditDistance(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); }