#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>
#define DEBUG_TYPE "hexagon-brelax"
using namespace llvm;
static cl::opt<uint32_t>
BranchRelaxSafetyBuffer("branch-relax-safety-buffer", cl::init(200),
cl::Hidden, cl::desc("safety buffer size"));
namespace llvm {
FunctionPass *createHexagonBranchRelaxation();
void initializeHexagonBranchRelaxationPass(PassRegistry&);
}
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;
}
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) {
unsigned InstOffset = 0;
for (auto &B : MF) {
if (B.getAlignment() != Align(1)) {
InstOffset = alignTo(InstOffset, B.getAlignment());
}
OffsetMap[&B] = InstOffset;
for (auto &MI : B.instrs()) {
InstOffset += HII->getSize(MI);
if (MI.isBranch() && HII->isExtendable(MI))
InstOffset += HEXAGON_INSTR_SIZE;
}
}
}
bool HexagonBranchRelaxation::relaxBranches(MachineFunction &MF) {
DenseMap<MachineBasicBlock*, unsigned> BlockToInstOffset;
computeOffset(MF, BlockToInstOffset);
return reGenerateBranch(MF, BlockToInstOffset);
}
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;
InstOffset += HII->nonDbgBBSize(&B) * HEXAGON_INSTR_SIZE;
MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
SmallVector<MachineOperand, 4> Cond;
if (HII->analyzeBranch(B, TBB, FBB, Cond, false)) {
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) {
auto SecondTerm = std::next(FirstTerm);
assert(SecondTerm != B.instr_end() &&
(SecondTerm->isBranch() || SecondTerm->isCall()) &&
"Bad second terminator");
if (&MI != &*SecondTerm)
return false;
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);
if (!HII->isExtendable(MI) && !HII->isExtended(MI)) {
LLVM_DEBUG(dbgs() << "\tUnderimplemented relax branch instruction.\n");
} else {
int ExtOpNum = HII->getCExtOpNum(MI);
MachineOperand &MO = MI.getOperand(ExtOpNum);
assert(MO.isMBB() && "Branch with unknown expandable field type");
MO.addTargetFlag(HexagonII::HMOTF_ConstExtended);
Changed = true;
}
}
}
return Changed;
}