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;
 | |
| }
 |