225 lines
7.8 KiB
C++
225 lines
7.8 KiB
C++
//===--- HexagonBranchRelaxation.cpp - Identify and relax long jumps ------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "hexagon-brelax"
|
|
|
|
#include "Hexagon.h"
|
|
#include "HexagonInstrInfo.h"
|
|
#include "HexagonSubtarget.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/CodeGen/MachineBasicBlock.h"
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
|
#include "llvm/CodeGen/MachineFunctionPass.h"
|
|
#include "llvm/CodeGen/MachineInstr.h"
|
|
#include "llvm/CodeGen/MachineOperand.h"
|
|
#include "llvm/CodeGen/Passes.h"
|
|
#include "llvm/Pass.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <iterator>
|
|
|
|
using namespace llvm;
|
|
|
|
// Since we have no exact knowledge of code layout, allow some safety buffer
|
|
// for jump target. This is measured in bytes.
|
|
static cl::opt<uint32_t> BranchRelaxSafetyBuffer("branch-relax-safety-buffer",
|
|
cl::init(200), cl::Hidden, cl::ZeroOrMore, cl::desc("safety buffer size"));
|
|
|
|
namespace llvm {
|
|
|
|
FunctionPass *createHexagonBranchRelaxation();
|
|
void initializeHexagonBranchRelaxationPass(PassRegistry&);
|
|
|
|
} // end namespace llvm
|
|
|
|
namespace {
|
|
|
|
struct HexagonBranchRelaxation : public MachineFunctionPass {
|
|
public:
|
|
static char ID;
|
|
|
|
HexagonBranchRelaxation() : MachineFunctionPass(ID) {
|
|
initializeHexagonBranchRelaxationPass(*PassRegistry::getPassRegistry());
|
|
}
|
|
|
|
bool runOnMachineFunction(MachineFunction &MF) override;
|
|
|
|
StringRef getPassName() const override {
|
|
return "Hexagon Branch Relaxation";
|
|
}
|
|
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
|
AU.setPreservesCFG();
|
|
MachineFunctionPass::getAnalysisUsage(AU);
|
|
}
|
|
|
|
private:
|
|
const HexagonInstrInfo *HII;
|
|
const HexagonRegisterInfo *HRI;
|
|
|
|
bool relaxBranches(MachineFunction &MF);
|
|
void computeOffset(MachineFunction &MF,
|
|
DenseMap<MachineBasicBlock*, unsigned> &BlockToInstOffset);
|
|
bool reGenerateBranch(MachineFunction &MF,
|
|
DenseMap<MachineBasicBlock*, unsigned> &BlockToInstOffset);
|
|
bool isJumpOutOfRange(MachineInstr &MI,
|
|
DenseMap<MachineBasicBlock*, unsigned> &BlockToInstOffset);
|
|
};
|
|
|
|
char HexagonBranchRelaxation::ID = 0;
|
|
|
|
} // end anonymous namespace
|
|
|
|
INITIALIZE_PASS(HexagonBranchRelaxation, "hexagon-brelax",
|
|
"Hexagon Branch Relaxation", false, false)
|
|
|
|
FunctionPass *llvm::createHexagonBranchRelaxation() {
|
|
return new HexagonBranchRelaxation();
|
|
}
|
|
|
|
bool HexagonBranchRelaxation::runOnMachineFunction(MachineFunction &MF) {
|
|
LLVM_DEBUG(dbgs() << "****** Hexagon Branch Relaxation ******\n");
|
|
|
|
auto &HST = MF.getSubtarget<HexagonSubtarget>();
|
|
HII = HST.getInstrInfo();
|
|
HRI = HST.getRegisterInfo();
|
|
|
|
bool Changed = false;
|
|
Changed = relaxBranches(MF);
|
|
return Changed;
|
|
}
|
|
|
|
void HexagonBranchRelaxation::computeOffset(MachineFunction &MF,
|
|
DenseMap<MachineBasicBlock*, unsigned> &OffsetMap) {
|
|
// offset of the current instruction from the start.
|
|
unsigned InstOffset = 0;
|
|
for (auto &B : MF) {
|
|
if (B.getAlignment() != Align(1)) {
|
|
// Although we don't know the exact layout of the final code, we need
|
|
// to account for alignment padding somehow. This heuristic pads each
|
|
// aligned basic block according to the alignment value.
|
|
InstOffset = alignTo(InstOffset, B.getAlignment());
|
|
}
|
|
OffsetMap[&B] = InstOffset;
|
|
for (auto &MI : B.instrs()) {
|
|
InstOffset += HII->getSize(MI);
|
|
// Assume that all extendable branches will be extended.
|
|
if (MI.isBranch() && HII->isExtendable(MI))
|
|
InstOffset += HEXAGON_INSTR_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// relaxBranches - For Hexagon, if the jump target/loop label is too far from
|
|
/// the jump/loop instruction then, we need to make sure that we have constant
|
|
/// extenders set for jumps and loops.
|
|
|
|
/// There are six iterations in this phase. It's self explanatory below.
|
|
bool HexagonBranchRelaxation::relaxBranches(MachineFunction &MF) {
|
|
// Compute the offset of each basic block
|
|
// offset of the current instruction from the start.
|
|
// map for each instruction to the beginning of the function
|
|
DenseMap<MachineBasicBlock*, unsigned> BlockToInstOffset;
|
|
computeOffset(MF, BlockToInstOffset);
|
|
|
|
return reGenerateBranch(MF, BlockToInstOffset);
|
|
}
|
|
|
|
/// Check if a given instruction is:
|
|
/// - a jump to a distant target
|
|
/// - that exceeds its immediate range
|
|
/// If both conditions are true, it requires constant extension.
|
|
bool HexagonBranchRelaxation::isJumpOutOfRange(MachineInstr &MI,
|
|
DenseMap<MachineBasicBlock*, unsigned> &BlockToInstOffset) {
|
|
MachineBasicBlock &B = *MI.getParent();
|
|
auto FirstTerm = B.getFirstInstrTerminator();
|
|
if (FirstTerm == B.instr_end())
|
|
return false;
|
|
|
|
if (HII->isExtended(MI))
|
|
return false;
|
|
|
|
unsigned InstOffset = BlockToInstOffset[&B];
|
|
unsigned Distance = 0;
|
|
|
|
// To save time, estimate exact position of a branch instruction
|
|
// as one at the end of the MBB.
|
|
// Number of instructions times typical instruction size.
|
|
InstOffset += HII->nonDbgBBSize(&B) * HEXAGON_INSTR_SIZE;
|
|
|
|
MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
|
|
SmallVector<MachineOperand, 4> Cond;
|
|
|
|
// Try to analyze this branch.
|
|
if (HII->analyzeBranch(B, TBB, FBB, Cond, false)) {
|
|
// Could not analyze it. See if this is something we can recognize.
|
|
// If it is a NVJ, it should always have its target in
|
|
// a fixed location.
|
|
if (HII->isNewValueJump(*FirstTerm))
|
|
TBB = FirstTerm->getOperand(HII->getCExtOpNum(*FirstTerm)).getMBB();
|
|
}
|
|
if (TBB && &MI == &*FirstTerm) {
|
|
Distance = std::abs((long long)InstOffset - BlockToInstOffset[TBB])
|
|
+ BranchRelaxSafetyBuffer;
|
|
return !HII->isJumpWithinBranchRange(*FirstTerm, Distance);
|
|
}
|
|
if (FBB) {
|
|
// Look for second terminator.
|
|
auto SecondTerm = std::next(FirstTerm);
|
|
assert(SecondTerm != B.instr_end() &&
|
|
(SecondTerm->isBranch() || SecondTerm->isCall()) &&
|
|
"Bad second terminator");
|
|
if (&MI != &*SecondTerm)
|
|
return false;
|
|
// Analyze the second branch in the BB.
|
|
Distance = std::abs((long long)InstOffset - BlockToInstOffset[FBB])
|
|
+ BranchRelaxSafetyBuffer;
|
|
return !HII->isJumpWithinBranchRange(*SecondTerm, Distance);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool HexagonBranchRelaxation::reGenerateBranch(MachineFunction &MF,
|
|
DenseMap<MachineBasicBlock*, unsigned> &BlockToInstOffset) {
|
|
bool Changed = false;
|
|
|
|
for (auto &B : MF) {
|
|
for (auto &MI : B) {
|
|
if (!MI.isBranch() || !isJumpOutOfRange(MI, BlockToInstOffset))
|
|
continue;
|
|
LLVM_DEBUG(dbgs() << "Long distance jump. isExtendable("
|
|
<< HII->isExtendable(MI) << ") isConstExtended("
|
|
<< HII->isConstExtended(MI) << ") " << MI);
|
|
|
|
// Since we have not merged HW loops relaxation into
|
|
// this code (yet), soften our approach for the moment.
|
|
if (!HII->isExtendable(MI) && !HII->isExtended(MI)) {
|
|
LLVM_DEBUG(dbgs() << "\tUnderimplemented relax branch instruction.\n");
|
|
} else {
|
|
// Find which operand is expandable.
|
|
int ExtOpNum = HII->getCExtOpNum(MI);
|
|
MachineOperand &MO = MI.getOperand(ExtOpNum);
|
|
// This need to be something we understand. So far we assume all
|
|
// branches have only MBB address as expandable field.
|
|
// If it changes, this will need to be expanded.
|
|
assert(MO.isMBB() && "Branch with unknown expandable field type");
|
|
// Mark given operand as extended.
|
|
MO.addTargetFlag(HexagonII::HMOTF_ConstExtended);
|
|
Changed = true;
|
|
}
|
|
}
|
|
}
|
|
return Changed;
|
|
}
|