#include "PPC.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachinePostDominators.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/TargetFrameLowering.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/Debug.h"
using namespace llvm;
#define DEBUG_TYPE "ppc-branch-coalescing"
STATISTIC(NumBlocksCoalesced, "Number of blocks coalesced");
STATISTIC(NumPHINotMoved, "Number of PHI Nodes that cannot be merged");
STATISTIC(NumBlocksNotCoalesced, "Number of blocks not coalesced");
namespace {
class PPCBranchCoalescing : public MachineFunctionPass {
struct CoalescingCandidateInfo {
MachineBasicBlock *BranchBlock; MachineBasicBlock *BranchTargetBlock; MachineBasicBlock *FallThroughBlock; SmallVector<MachineOperand, 4> Cond;
bool MustMoveDown;
bool MustMoveUp;
CoalescingCandidateInfo();
void clear();
};
MachineDominatorTree *MDT;
MachinePostDominatorTree *MPDT;
const TargetInstrInfo *TII;
MachineRegisterInfo *MRI;
void initialize(MachineFunction &F);
bool canCoalesceBranch(CoalescingCandidateInfo &Cand);
bool identicalOperands(ArrayRef<MachineOperand> OperandList1,
ArrayRef<MachineOperand> OperandList2) const;
bool validateCandidates(CoalescingCandidateInfo &SourceRegion,
CoalescingCandidateInfo &TargetRegion) const;
public:
static char ID;
PPCBranchCoalescing() : MachineFunctionPass(ID) {
initializePPCBranchCoalescingPass(*PassRegistry::getPassRegistry());
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<MachineDominatorTree>();
AU.addRequired<MachinePostDominatorTree>();
MachineFunctionPass::getAnalysisUsage(AU);
}
StringRef getPassName() const override { return "Branch Coalescing"; }
bool mergeCandidates(CoalescingCandidateInfo &SourceRegion,
CoalescingCandidateInfo &TargetRegion);
bool canMoveToBeginning(const MachineInstr &MI,
const MachineBasicBlock &MBB) const;
bool canMoveToEnd(const MachineInstr &MI,
const MachineBasicBlock &MBB) const;
bool canMerge(CoalescingCandidateInfo &SourceRegion,
CoalescingCandidateInfo &TargetRegion) const;
void moveAndUpdatePHIs(MachineBasicBlock *SourceRegionMBB,
MachineBasicBlock *TargetRegionMBB);
bool runOnMachineFunction(MachineFunction &MF) override;
};
}
char PPCBranchCoalescing::ID = 0;
FunctionPass *llvm::createPPCBranchCoalescingPass() {
return new PPCBranchCoalescing();
}
INITIALIZE_PASS_BEGIN(PPCBranchCoalescing, DEBUG_TYPE,
"Branch Coalescing", false, false)
INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree)
INITIALIZE_PASS_DEPENDENCY(MachinePostDominatorTree)
INITIALIZE_PASS_END(PPCBranchCoalescing, DEBUG_TYPE, "Branch Coalescing",
false, false)
PPCBranchCoalescing::CoalescingCandidateInfo::CoalescingCandidateInfo()
: BranchBlock(nullptr), BranchTargetBlock(nullptr),
FallThroughBlock(nullptr), MustMoveDown(false), MustMoveUp(false) {}
void PPCBranchCoalescing::CoalescingCandidateInfo::clear() {
BranchBlock = nullptr;
BranchTargetBlock = nullptr;
FallThroughBlock = nullptr;
Cond.clear();
MustMoveDown = false;
MustMoveUp = false;
}
void PPCBranchCoalescing::initialize(MachineFunction &MF) {
MDT = &getAnalysis<MachineDominatorTree>();
MPDT = &getAnalysis<MachinePostDominatorTree>();
TII = MF.getSubtarget().getInstrInfo();
MRI = &MF.getRegInfo();
}
bool PPCBranchCoalescing::canCoalesceBranch(CoalescingCandidateInfo &Cand) {
LLVM_DEBUG(dbgs() << "Determine if branch block "
<< Cand.BranchBlock->getNumber() << " can be coalesced:");
MachineBasicBlock *FalseMBB = nullptr;
if (TII->analyzeBranch(*Cand.BranchBlock, Cand.BranchTargetBlock, FalseMBB,
Cand.Cond)) {
LLVM_DEBUG(dbgs() << "TII unable to Analyze Branch - skip\n");
return false;
}
for (auto &I : Cand.BranchBlock->terminators()) {
LLVM_DEBUG(dbgs() << "Looking at terminator : " << I << "\n");
if (!I.isBranch())
continue;
if (I.getNumOperands() != I.getNumExplicitOperands()) {
LLVM_DEBUG(dbgs() << "Terminator contains implicit operands - skip : "
<< I << "\n");
return false;
}
}
if (Cand.BranchBlock->isEHPad() || Cand.BranchBlock->hasEHPadSuccessor()) {
LLVM_DEBUG(dbgs() << "EH Pad - skip\n");
return false;
}
if (Cand.BranchBlock->mayHaveInlineAsmBr()) {
LLVM_DEBUG(dbgs() << "Inline Asm Br - skip\n");
return false;
}
if (!Cand.BranchTargetBlock || FalseMBB ||
!Cand.BranchBlock->isSuccessor(Cand.BranchTargetBlock)) {
LLVM_DEBUG(dbgs() << "Does not form a triangle - skip\n");
return false;
}
if (Cand.BranchBlock->succ_size() != 2) {
LLVM_DEBUG(dbgs() << "Does not have 2 successors - skip\n");
return false;
}
assert(Cand.BranchBlock->canFallThrough() &&
"Expecting the block to fall through!");
MachineBasicBlock *Succ =
(*Cand.BranchBlock->succ_begin() == Cand.BranchTargetBlock)
? *Cand.BranchBlock->succ_rbegin()
: *Cand.BranchBlock->succ_begin();
assert(Succ && "Expecting a valid fall-through block\n");
if (!Succ->empty()) {
LLVM_DEBUG(dbgs() << "Fall-through block contains code -- skip\n");
return false;
}
if (!Succ->isSuccessor(Cand.BranchTargetBlock)) {
LLVM_DEBUG(
dbgs()
<< "Successor of fall through block is not branch taken block\n");
return false;
}
Cand.FallThroughBlock = Succ;
LLVM_DEBUG(dbgs() << "Valid Candidate\n");
return true;
}
bool PPCBranchCoalescing::identicalOperands(
ArrayRef<MachineOperand> OpList1, ArrayRef<MachineOperand> OpList2) const {
if (OpList1.size() != OpList2.size()) {
LLVM_DEBUG(dbgs() << "Operand list is different size\n");
return false;
}
for (unsigned i = 0; i < OpList1.size(); ++i) {
const MachineOperand &Op1 = OpList1[i];
const MachineOperand &Op2 = OpList2[i];
LLVM_DEBUG(dbgs() << "Op1: " << Op1 << "\n"
<< "Op2: " << Op2 << "\n");
if (Op1.isIdenticalTo(Op2)) {
if (Op1.isReg() &&
Register::isPhysicalRegister(Op1.getReg())
&& !(Op1.isUse() && MRI->isConstantPhysReg(Op1.getReg()))) {
LLVM_DEBUG(dbgs() << "The operands are not provably identical.\n");
return false;
}
LLVM_DEBUG(dbgs() << "Op1 and Op2 are identical!\n");
continue;
}
if (Op1.isReg() && Op2.isReg() &&
Register::isVirtualRegister(Op1.getReg()) &&
Register::isVirtualRegister(Op2.getReg())) {
MachineInstr *Op1Def = MRI->getVRegDef(Op1.getReg());
MachineInstr *Op2Def = MRI->getVRegDef(Op2.getReg());
if (TII->produceSameValue(*Op1Def, *Op2Def, MRI)) {
LLVM_DEBUG(dbgs() << "Op1Def: " << *Op1Def << " and " << *Op2Def
<< " produce the same value!\n");
} else {
LLVM_DEBUG(dbgs() << "Operands produce different values\n");
return false;
}
} else {
LLVM_DEBUG(dbgs() << "The operands are not provably identical.\n");
return false;
}
}
return true;
}
void PPCBranchCoalescing::moveAndUpdatePHIs(MachineBasicBlock *SourceMBB,
MachineBasicBlock *TargetMBB) {
MachineBasicBlock::iterator MI = SourceMBB->begin();
MachineBasicBlock::iterator ME = SourceMBB->getFirstNonPHI();
if (MI == ME) {
LLVM_DEBUG(dbgs() << "SourceMBB contains no PHI instructions.\n");
return;
}
for (MachineBasicBlock::iterator Iter = MI; Iter != ME; Iter++) {
MachineInstr &PHIInst = *Iter;
for (unsigned i = 2, e = PHIInst.getNumOperands() + 1; i != e; i += 2) {
MachineOperand &MO = PHIInst.getOperand(i);
if (MO.getMBB() == SourceMBB)
MO.setMBB(TargetMBB);
}
}
TargetMBB->splice(TargetMBB->begin(), SourceMBB, MI, ME);
}
bool PPCBranchCoalescing::canMoveToBeginning(const MachineInstr &MI,
const MachineBasicBlock &TargetMBB
) const {
LLVM_DEBUG(dbgs() << "Checking if " << MI << " can move to beginning of "
<< TargetMBB.getNumber() << "\n");
for (auto &Def : MI.defs()) { for (auto &Use : MRI->use_instructions(Def.getReg())) {
if (Use.isPHI() && Use.getParent() == &TargetMBB) {
LLVM_DEBUG(dbgs() << " *** used in a PHI -- cannot move ***\n");
return false;
}
}
}
LLVM_DEBUG(dbgs() << " Safe to move to the beginning.\n");
return true;
}
bool PPCBranchCoalescing::canMoveToEnd(const MachineInstr &MI,
const MachineBasicBlock &TargetMBB
) const {
LLVM_DEBUG(dbgs() << "Checking if " << MI << " can move to end of "
<< TargetMBB.getNumber() << "\n");
for (auto &Use : MI.uses()) {
if (Use.isReg() && Register::isVirtualRegister(Use.getReg())) {
MachineInstr *DefInst = MRI->getVRegDef(Use.getReg());
if (DefInst->isPHI() && DefInst->getParent() == MI.getParent()) {
LLVM_DEBUG(dbgs() << " *** Cannot move this instruction ***\n");
return false;
} else {
LLVM_DEBUG(
dbgs() << " *** def is in another block -- safe to move!\n");
}
}
}
LLVM_DEBUG(dbgs() << " Safe to move to the end.\n");
return true;
}
bool PPCBranchCoalescing::validateCandidates(
CoalescingCandidateInfo &SourceRegion,
CoalescingCandidateInfo &TargetRegion) const {
if (TargetRegion.BranchTargetBlock != SourceRegion.BranchBlock)
llvm_unreachable("Expecting SourceRegion to immediately follow TargetRegion");
else if (!MDT->dominates(TargetRegion.BranchBlock, SourceRegion.BranchBlock))
llvm_unreachable("Expecting TargetRegion to dominate SourceRegion");
else if (!MPDT->dominates(SourceRegion.BranchBlock, TargetRegion.BranchBlock))
llvm_unreachable("Expecting SourceRegion to post-dominate TargetRegion");
else if (!TargetRegion.FallThroughBlock->empty() ||
!SourceRegion.FallThroughBlock->empty())
llvm_unreachable("Expecting fall-through blocks to be empty");
return true;
}
bool PPCBranchCoalescing::canMerge(CoalescingCandidateInfo &SourceRegion,
CoalescingCandidateInfo &TargetRegion) const {
if (!validateCandidates(SourceRegion, TargetRegion))
return false;
for (MachineBasicBlock::iterator
I = SourceRegion.BranchBlock->instr_begin(),
E = SourceRegion.BranchBlock->getFirstNonPHI();
I != E; ++I) {
for (auto &Def : I->defs())
for (auto &Use : MRI->use_instructions(Def.getReg())) {
if (Use.isPHI() && Use.getParent() == SourceRegion.BranchTargetBlock) {
LLVM_DEBUG(dbgs()
<< "PHI " << *I
<< " defines register used in another "
"PHI within branch target block -- can't merge\n");
NumPHINotMoved++;
return false;
}
if (Use.getParent() == SourceRegion.BranchBlock) {
LLVM_DEBUG(dbgs() << "PHI " << *I
<< " defines register used in this "
"block -- all must move down\n");
SourceRegion.MustMoveDown = true;
}
}
}
for (MachineBasicBlock::iterator
I = SourceRegion.BranchBlock->getFirstNonPHI(),
E = SourceRegion.BranchBlock->end();
I != E; ++I) {
if (!canMoveToBeginning(*I, *SourceRegion.BranchTargetBlock)) {
LLVM_DEBUG(dbgs() << "Instruction " << *I
<< " cannot move down - must move up!\n");
SourceRegion.MustMoveUp = true;
}
if (!canMoveToEnd(*I, *TargetRegion.BranchBlock)) {
LLVM_DEBUG(dbgs() << "Instruction " << *I
<< " cannot move up - must move down!\n");
SourceRegion.MustMoveDown = true;
}
}
return (SourceRegion.MustMoveUp && SourceRegion.MustMoveDown) ? false : true;
}
bool PPCBranchCoalescing::mergeCandidates(CoalescingCandidateInfo &SourceRegion,
CoalescingCandidateInfo &TargetRegion) {
if (SourceRegion.MustMoveUp && SourceRegion.MustMoveDown) {
llvm_unreachable("Cannot have both MustMoveDown and MustMoveUp set!");
return false;
}
if (!validateCandidates(SourceRegion, TargetRegion))
return false;
moveAndUpdatePHIs(SourceRegion.BranchBlock, SourceRegion.BranchTargetBlock);
MachineBasicBlock::iterator firstInstr =
SourceRegion.BranchBlock->getFirstNonPHI();
MachineBasicBlock::iterator lastInstr =
SourceRegion.BranchBlock->getFirstTerminator();
MachineBasicBlock *Source = SourceRegion.MustMoveDown
? SourceRegion.BranchTargetBlock
: TargetRegion.BranchBlock;
MachineBasicBlock::iterator Target =
SourceRegion.MustMoveDown
? SourceRegion.BranchTargetBlock->getFirstNonPHI()
: TargetRegion.BranchBlock->getFirstTerminator();
Source->splice(Target, SourceRegion.BranchBlock, firstInstr, lastInstr);
SourceRegion.BranchBlock->removeSuccessor(SourceRegion.FallThroughBlock);
TargetRegion.BranchBlock->transferSuccessorsAndUpdatePHIs(
SourceRegion.BranchBlock);
TargetRegion.BranchBlock->ReplaceUsesOfBlockWith(
SourceRegion.BranchBlock, SourceRegion.BranchTargetBlock);
MachineBasicBlock::iterator I =
SourceRegion.BranchBlock->terminators().begin();
while (I != SourceRegion.BranchBlock->terminators().end()) {
MachineInstr &CurrInst = *I;
++I;
if (CurrInst.isBranch())
CurrInst.eraseFromParent();
}
assert(TargetRegion.FallThroughBlock->empty() &&
"FallThroughBlocks should be empty!");
TargetRegion.FallThroughBlock->transferSuccessorsAndUpdatePHIs(
SourceRegion.FallThroughBlock);
TargetRegion.FallThroughBlock->removeSuccessor(SourceRegion.BranchBlock);
assert(SourceRegion.BranchBlock->empty() &&
"Expecting branch block to be empty!");
SourceRegion.BranchBlock->eraseFromParent();
assert(SourceRegion.FallThroughBlock->empty() &&
"Expecting fall-through block to be empty!\n");
SourceRegion.FallThroughBlock->eraseFromParent();
NumBlocksCoalesced++;
return true;
}
bool PPCBranchCoalescing::runOnMachineFunction(MachineFunction &MF) {
if (skipFunction(MF.getFunction()) || MF.empty())
return false;
bool didSomething = false;
LLVM_DEBUG(dbgs() << "******** Branch Coalescing ********\n");
initialize(MF);
LLVM_DEBUG(dbgs() << "Function: "; MF.dump(); dbgs() << "\n");
CoalescingCandidateInfo Cand1, Cand2;
for (MachineBasicBlock &MBB : MF) {
bool MergedCandidates = false;
do {
MergedCandidates = false;
Cand1.clear();
Cand2.clear();
Cand1.BranchBlock = &MBB;
if (!canCoalesceBranch(Cand1))
break;
Cand2.BranchBlock = Cand1.BranchTargetBlock;
if (!canCoalesceBranch(Cand2))
break;
assert(MPDT->dominates(Cand2.BranchTargetBlock, Cand1.BranchBlock) &&
"Branch-taken block should post-dominate first candidate");
if (!identicalOperands(Cand1.Cond, Cand2.Cond)) {
LLVM_DEBUG(dbgs() << "Blocks " << Cand1.BranchBlock->getNumber()
<< " and " << Cand2.BranchBlock->getNumber()
<< " have different branches\n");
break;
}
if (!canMerge(Cand2, Cand1)) {
LLVM_DEBUG(dbgs() << "Cannot merge blocks "
<< Cand1.BranchBlock->getNumber() << " and "
<< Cand2.BranchBlock->getNumber() << "\n");
NumBlocksNotCoalesced++;
continue;
}
LLVM_DEBUG(dbgs() << "Merging blocks " << Cand1.BranchBlock->getNumber()
<< " and " << Cand1.BranchTargetBlock->getNumber()
<< "\n");
MergedCandidates = mergeCandidates(Cand2, Cand1);
if (MergedCandidates)
didSomething = true;
LLVM_DEBUG(dbgs() << "Function after merging: "; MF.dump();
dbgs() << "\n");
} while (MergedCandidates);
}
#ifndef NDEBUG
if (didSomething)
MF.verify(nullptr, "Error in code produced by branch coalescing");
#endif
LLVM_DEBUG(dbgs() << "Finished Branch Coalescing\n");
return didSomething;
}