forked from OSchip/llvm-project
372 lines
15 KiB
C++
372 lines
15 KiB
C++
//===- MCCodePadder.cpp - Target MC Code Padder ---------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/MC/MCAsmLayout.h"
|
|
#include "llvm/MC/MCCodePadder.h"
|
|
#include "llvm/MC/MCObjectStreamer.h"
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <numeric>
|
|
|
|
using namespace llvm;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// MCCodePadder
|
|
//
|
|
|
|
MCCodePadder::~MCCodePadder() {
|
|
for (auto *Policy : CodePaddingPolicies)
|
|
delete Policy;
|
|
}
|
|
|
|
bool MCCodePadder::addPolicy(MCCodePaddingPolicy *Policy) {
|
|
assert(Policy && "Policy must be valid");
|
|
return CodePaddingPolicies.insert(Policy).second;
|
|
}
|
|
|
|
void MCCodePadder::handleBasicBlockStart(MCObjectStreamer *OS,
|
|
const MCCodePaddingContext &Context) {
|
|
assert(OS != nullptr && "OS must be valid");
|
|
assert(this->OS == nullptr && "Still handling another basic block");
|
|
this->OS = OS;
|
|
|
|
ArePoliciesActive = usePoliciesForBasicBlock(Context);
|
|
|
|
bool InsertionPoint = basicBlockRequiresInsertionPoint(Context);
|
|
assert((!InsertionPoint ||
|
|
OS->getCurrentFragment()->getKind() != MCFragment::FT_Align) &&
|
|
"Cannot insert padding nops right after an alignment fragment as it "
|
|
"will ruin the alignment");
|
|
|
|
uint64_t PoliciesMask = MCPaddingFragment::PFK_None;
|
|
if (ArePoliciesActive) {
|
|
PoliciesMask = std::accumulate(
|
|
CodePaddingPolicies.begin(), CodePaddingPolicies.end(),
|
|
MCPaddingFragment::PFK_None,
|
|
[&Context](uint64_t Mask,
|
|
const MCCodePaddingPolicy *Policy) -> uint64_t {
|
|
return Policy->basicBlockRequiresPaddingFragment(Context)
|
|
? (Mask | Policy->getKindMask())
|
|
: Mask;
|
|
});
|
|
}
|
|
|
|
if (InsertionPoint || PoliciesMask != MCPaddingFragment::PFK_None) {
|
|
MCPaddingFragment *PaddingFragment = OS->getOrCreatePaddingFragment();
|
|
if (InsertionPoint)
|
|
PaddingFragment->setAsInsertionPoint();
|
|
PaddingFragment->setPaddingPoliciesMask(
|
|
PaddingFragment->getPaddingPoliciesMask() | PoliciesMask);
|
|
}
|
|
}
|
|
|
|
void MCCodePadder::handleBasicBlockEnd(const MCCodePaddingContext &Context) {
|
|
assert(this->OS != nullptr && "Not handling a basic block");
|
|
OS = nullptr;
|
|
}
|
|
|
|
void MCCodePadder::handleInstructionBegin(const MCInst &Inst) {
|
|
if (!OS)
|
|
return; // instruction was emitted outside a function
|
|
|
|
assert(CurrHandledInstFragment == nullptr && "Can't start handling an "
|
|
"instruction while still "
|
|
"handling another instruction");
|
|
|
|
bool InsertionPoint = instructionRequiresInsertionPoint(Inst);
|
|
assert((!InsertionPoint ||
|
|
OS->getCurrentFragment()->getKind() != MCFragment::FT_Align) &&
|
|
"Cannot insert padding nops right after an alignment fragment as it "
|
|
"will ruin the alignment");
|
|
|
|
uint64_t PoliciesMask = MCPaddingFragment::PFK_None;
|
|
if (ArePoliciesActive) {
|
|
PoliciesMask = std::accumulate(
|
|
CodePaddingPolicies.begin(), CodePaddingPolicies.end(),
|
|
MCPaddingFragment::PFK_None,
|
|
[&Inst](uint64_t Mask, const MCCodePaddingPolicy *Policy) -> uint64_t {
|
|
return Policy->instructionRequiresPaddingFragment(Inst)
|
|
? (Mask | Policy->getKindMask())
|
|
: Mask;
|
|
});
|
|
}
|
|
MCFragment *CurrFragment = OS->getCurrentFragment();
|
|
// CurrFragment can be a previously created MCPaddingFragment. If so, let's
|
|
// update it with the information we have, such as the instruction that it
|
|
// should point to.
|
|
bool needToUpdateCurrFragment =
|
|
CurrFragment != nullptr &&
|
|
CurrFragment->getKind() == MCFragment::FT_Padding;
|
|
if (InsertionPoint || PoliciesMask != MCPaddingFragment::PFK_None ||
|
|
needToUpdateCurrFragment) {
|
|
// temporarily holding the fragment as CurrHandledInstFragment, to be
|
|
// updated after the instruction will be written
|
|
CurrHandledInstFragment = OS->getOrCreatePaddingFragment();
|
|
if (InsertionPoint)
|
|
CurrHandledInstFragment->setAsInsertionPoint();
|
|
CurrHandledInstFragment->setPaddingPoliciesMask(
|
|
CurrHandledInstFragment->getPaddingPoliciesMask() | PoliciesMask);
|
|
}
|
|
}
|
|
|
|
void MCCodePadder::handleInstructionEnd(const MCInst &Inst) {
|
|
if (!OS)
|
|
return; // instruction was emitted outside a function
|
|
if (CurrHandledInstFragment == nullptr)
|
|
return;
|
|
|
|
MCFragment *InstFragment = OS->getCurrentFragment();
|
|
if (MCDataFragment *InstDataFragment =
|
|
dyn_cast_or_null<MCDataFragment>(InstFragment))
|
|
// Inst is a fixed size instruction and was encoded into a MCDataFragment.
|
|
// Let the fragment hold it and its size. Its size is the current size of
|
|
// the data fragment, as the padding fragment was inserted right before it
|
|
// and nothing was written yet except Inst
|
|
CurrHandledInstFragment->setInstAndInstSize(
|
|
Inst, InstDataFragment->getContents().size());
|
|
else if (MCRelaxableFragment *InstRelaxableFragment =
|
|
dyn_cast_or_null<MCRelaxableFragment>(InstFragment))
|
|
// Inst may be relaxed and its size may vary.
|
|
// Let the fragment hold the instruction and the MCRelaxableFragment
|
|
// that's holding it.
|
|
CurrHandledInstFragment->setInstAndInstFragment(Inst,
|
|
InstRelaxableFragment);
|
|
else
|
|
llvm_unreachable("After encoding an instruction current fragment must be "
|
|
"either a MCDataFragment or a MCRelaxableFragment");
|
|
|
|
CurrHandledInstFragment = nullptr;
|
|
}
|
|
|
|
MCPFRange &MCCodePadder::getJurisdiction(MCPaddingFragment *Fragment,
|
|
MCAsmLayout &Layout) {
|
|
auto JurisdictionLocation = FragmentToJurisdiction.find(Fragment);
|
|
if (JurisdictionLocation != FragmentToJurisdiction.end())
|
|
return JurisdictionLocation->second;
|
|
|
|
MCPFRange Jurisdiction;
|
|
|
|
// Forward scanning the fragments in this section, starting from the given
|
|
// fragments, and adding relevant MCPaddingFragments to the Jurisdiction
|
|
for (MCFragment *CurrFragment = Fragment; CurrFragment != nullptr;
|
|
CurrFragment = CurrFragment->getNextNode()) {
|
|
|
|
MCPaddingFragment *CurrPaddingFragment =
|
|
dyn_cast<MCPaddingFragment>(CurrFragment);
|
|
if (CurrPaddingFragment == nullptr)
|
|
continue;
|
|
|
|
if (CurrPaddingFragment != Fragment &&
|
|
CurrPaddingFragment->isInsertionPoint())
|
|
// Found next insertion point Fragment. From now on it's its jurisdiction.
|
|
break;
|
|
for (const auto *Policy : CodePaddingPolicies) {
|
|
if (CurrPaddingFragment->hasPaddingPolicy(Policy->getKindMask())) {
|
|
Jurisdiction.push_back(CurrPaddingFragment);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto InsertionResult =
|
|
FragmentToJurisdiction.insert(std::make_pair(Fragment, Jurisdiction));
|
|
assert(InsertionResult.second &&
|
|
"Insertion to FragmentToJurisdiction failed");
|
|
return InsertionResult.first->second;
|
|
}
|
|
|
|
uint64_t MCCodePadder::getMaxWindowSize(MCPaddingFragment *Fragment,
|
|
MCAsmLayout &Layout) {
|
|
auto MaxFragmentSizeLocation = FragmentToMaxWindowSize.find(Fragment);
|
|
if (MaxFragmentSizeLocation != FragmentToMaxWindowSize.end())
|
|
return MaxFragmentSizeLocation->second;
|
|
|
|
MCPFRange &Jurisdiction = getJurisdiction(Fragment, Layout);
|
|
uint64_t JurisdictionMask = MCPaddingFragment::PFK_None;
|
|
for (const auto *Protege : Jurisdiction)
|
|
JurisdictionMask |= Protege->getPaddingPoliciesMask();
|
|
|
|
uint64_t MaxFragmentSize = UINT64_C(0);
|
|
for (const auto *Policy : CodePaddingPolicies)
|
|
if ((JurisdictionMask & Policy->getKindMask()) !=
|
|
MCPaddingFragment::PFK_None)
|
|
MaxFragmentSize = std::max(MaxFragmentSize, Policy->getWindowSize());
|
|
|
|
auto InsertionResult =
|
|
FragmentToMaxWindowSize.insert(std::make_pair(Fragment, MaxFragmentSize));
|
|
assert(InsertionResult.second &&
|
|
"Insertion to FragmentToMaxWindowSize failed");
|
|
return InsertionResult.first->second;
|
|
}
|
|
|
|
bool MCCodePadder::relaxFragment(MCPaddingFragment *Fragment,
|
|
MCAsmLayout &Layout) {
|
|
if (!Fragment->isInsertionPoint())
|
|
return false;
|
|
uint64_t OldSize = Fragment->getSize();
|
|
|
|
uint64_t MaxWindowSize = getMaxWindowSize(Fragment, Layout);
|
|
if (MaxWindowSize == UINT64_C(0))
|
|
return false;
|
|
assert(isPowerOf2_64(MaxWindowSize) &&
|
|
"MaxWindowSize must be an integer power of 2");
|
|
uint64_t SectionAlignment = Fragment->getParent()->getAlignment();
|
|
assert(isPowerOf2_64(SectionAlignment) &&
|
|
"SectionAlignment must be an integer power of 2");
|
|
|
|
MCPFRange &Jurisdiction = getJurisdiction(Fragment, Layout);
|
|
uint64_t OptimalSize = UINT64_C(0);
|
|
double OptimalWeight = std::numeric_limits<double>::max();
|
|
uint64_t MaxFragmentSize = MaxWindowSize - UINT16_C(1);
|
|
for (uint64_t Size = UINT64_C(0); Size <= MaxFragmentSize; ++Size) {
|
|
Fragment->setSize(Size);
|
|
Layout.invalidateFragmentsFrom(Fragment);
|
|
double SizeWeight = 0.0;
|
|
// The section is guaranteed to be aligned to SectionAlignment, but that
|
|
// doesn't guarantee the exact section offset w.r.t. the policies window
|
|
// size.
|
|
// As a concrete example, the section could be aligned to 16B, but a
|
|
// policy's window size can be 32B. That means that the section actual start
|
|
// address can either be 0mod32 or 16mod32. The said policy will act
|
|
// differently for each case, so we need to take both into consideration.
|
|
for (uint64_t Offset = UINT64_C(0); Offset < MaxWindowSize;
|
|
Offset += SectionAlignment) {
|
|
double OffsetWeight = std::accumulate(
|
|
CodePaddingPolicies.begin(), CodePaddingPolicies.end(), 0.0,
|
|
[&Jurisdiction, &Offset, &Layout](
|
|
double Weight, const MCCodePaddingPolicy *Policy) -> double {
|
|
double PolicyWeight =
|
|
Policy->computeRangePenaltyWeight(Jurisdiction, Offset, Layout);
|
|
assert(PolicyWeight >= 0.0 && "A penalty weight must be positive");
|
|
return Weight + PolicyWeight;
|
|
});
|
|
SizeWeight = std::max(SizeWeight, OffsetWeight);
|
|
}
|
|
if (SizeWeight < OptimalWeight) {
|
|
OptimalWeight = SizeWeight;
|
|
OptimalSize = Size;
|
|
}
|
|
if (OptimalWeight == 0.0)
|
|
break;
|
|
}
|
|
|
|
Fragment->setSize(OptimalSize);
|
|
Layout.invalidateFragmentsFrom(Fragment);
|
|
return OldSize != OptimalSize;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// MCCodePaddingPolicy
|
|
//
|
|
|
|
uint64_t MCCodePaddingPolicy::getNextFragmentOffset(const MCFragment *Fragment,
|
|
const MCAsmLayout &Layout) {
|
|
assert(Fragment != nullptr && "Fragment cannot be null");
|
|
MCFragment const *NextFragment = Fragment->getNextNode();
|
|
return NextFragment == nullptr
|
|
? Layout.getSectionAddressSize(Fragment->getParent())
|
|
: Layout.getFragmentOffset(NextFragment);
|
|
}
|
|
|
|
uint64_t
|
|
MCCodePaddingPolicy::getFragmentInstByte(const MCPaddingFragment *Fragment,
|
|
MCAsmLayout &Layout) const {
|
|
uint64_t InstByte = getNextFragmentOffset(Fragment, Layout);
|
|
if (InstByteIsLastByte)
|
|
InstByte += Fragment->getInstSize() - UINT64_C(1);
|
|
return InstByte;
|
|
}
|
|
|
|
uint64_t
|
|
MCCodePaddingPolicy::computeWindowEndAddress(const MCPaddingFragment *Fragment,
|
|
uint64_t Offset,
|
|
MCAsmLayout &Layout) const {
|
|
uint64_t InstByte = getFragmentInstByte(Fragment, Layout);
|
|
return alignTo(InstByte + UINT64_C(1) + Offset, WindowSize) - Offset;
|
|
}
|
|
|
|
double MCCodePaddingPolicy::computeRangePenaltyWeight(
|
|
const MCPFRange &Range, uint64_t Offset, MCAsmLayout &Layout) const {
|
|
|
|
SmallVector<MCPFRange, 8> Windows;
|
|
SmallVector<MCPFRange, 8>::iterator CurrWindowLocation = Windows.end();
|
|
for (const MCPaddingFragment *Fragment : Range) {
|
|
if (!Fragment->hasPaddingPolicy(getKindMask()))
|
|
continue;
|
|
uint64_t FragmentWindowEndAddress =
|
|
computeWindowEndAddress(Fragment, Offset, Layout);
|
|
if (CurrWindowLocation == Windows.end() ||
|
|
FragmentWindowEndAddress !=
|
|
computeWindowEndAddress(*CurrWindowLocation->begin(), Offset,
|
|
Layout)) {
|
|
// next window is starting
|
|
Windows.push_back(MCPFRange());
|
|
CurrWindowLocation = Windows.end() - 1;
|
|
}
|
|
CurrWindowLocation->push_back(Fragment);
|
|
}
|
|
|
|
if (Windows.empty())
|
|
return 0.0;
|
|
|
|
double RangeWeight = 0.0;
|
|
SmallVector<MCPFRange, 8>::iterator I = Windows.begin();
|
|
RangeWeight += computeFirstWindowPenaltyWeight(*I, Offset, Layout);
|
|
++I;
|
|
RangeWeight += std::accumulate(
|
|
I, Windows.end(), 0.0,
|
|
[this, &Layout, &Offset](double Weight, MCPFRange &Window) -> double {
|
|
return Weight += computeWindowPenaltyWeight(Window, Offset, Layout);
|
|
});
|
|
return RangeWeight;
|
|
}
|
|
|
|
double MCCodePaddingPolicy::computeFirstWindowPenaltyWeight(
|
|
const MCPFRange &Window, uint64_t Offset, MCAsmLayout &Layout) const {
|
|
if (Window.empty())
|
|
return 0.0;
|
|
uint64_t WindowEndAddress =
|
|
computeWindowEndAddress(*Window.begin(), Offset, Layout);
|
|
|
|
MCPFRange FullWindowFirstPart; // will hold all the fragments that are in the
|
|
// same window as the fragments in the given
|
|
// window but their penalty weight should not
|
|
// be added
|
|
for (const MCFragment *Fragment = (*Window.begin())->getPrevNode();
|
|
Fragment != nullptr; Fragment = Fragment->getPrevNode()) {
|
|
const MCPaddingFragment *PaddingNopFragment =
|
|
dyn_cast<MCPaddingFragment>(Fragment);
|
|
if (PaddingNopFragment == nullptr ||
|
|
!PaddingNopFragment->hasPaddingPolicy(getKindMask()))
|
|
continue;
|
|
if (WindowEndAddress !=
|
|
computeWindowEndAddress(PaddingNopFragment, Offset, Layout))
|
|
break;
|
|
|
|
FullWindowFirstPart.push_back(PaddingNopFragment);
|
|
}
|
|
|
|
std::reverse(FullWindowFirstPart.begin(), FullWindowFirstPart.end());
|
|
double FullWindowFirstPartWeight =
|
|
computeWindowPenaltyWeight(FullWindowFirstPart, Offset, Layout);
|
|
|
|
MCPFRange FullWindow(
|
|
FullWindowFirstPart); // will hold all the fragments that are in the
|
|
// same window as the fragments in the given
|
|
// window, whether their weight should be added
|
|
// or not
|
|
FullWindow.append(Window.begin(), Window.end());
|
|
double FullWindowWeight =
|
|
computeWindowPenaltyWeight(FullWindow, Offset, Layout);
|
|
|
|
assert(FullWindowWeight >= FullWindowFirstPartWeight &&
|
|
"More fragments necessarily means bigger weight");
|
|
return FullWindowWeight - FullWindowFirstPartWeight;
|
|
}
|