#include "SystemZ.h"
#include "SystemZInstrInfo.h"
#include "SystemZTargetMachine.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.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/MachineInstrBuilder.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
#include <cstdint>
using namespace llvm;
#define DEBUG_TYPE "systemz-long-branch"
STATISTIC(LongBranches, "Number of long branches.");
namespace {
struct MBBInfo {
uint64_t Address = 0;
uint64_t Size = 0;
Align Alignment;
unsigned NumTerminators = 0;
MBBInfo() = default;
};
struct TerminatorInfo {
MachineInstr *Branch = nullptr;
uint64_t Address = 0;
uint64_t Size = 0;
unsigned TargetBlock = 0;
unsigned ExtraRelaxSize = 0;
TerminatorInfo() = default;
};
struct BlockPosition {
uint64_t Address = 0;
unsigned KnownBits;
BlockPosition(unsigned InitialLogAlignment)
: KnownBits(InitialLogAlignment) {}
};
class SystemZLongBranch : public MachineFunctionPass {
public:
static char ID;
SystemZLongBranch() : MachineFunctionPass(ID) {
initializeSystemZLongBranchPass(*PassRegistry::getPassRegistry());
}
bool runOnMachineFunction(MachineFunction &F) override;
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::NoVRegs);
}
private:
void skipNonTerminators(BlockPosition &Position, MBBInfo &Block);
void skipTerminator(BlockPosition &Position, TerminatorInfo &Terminator,
bool AssumeRelaxed);
TerminatorInfo describeTerminator(MachineInstr &MI);
uint64_t initMBBInfo();
bool mustRelaxBranch(const TerminatorInfo &Terminator, uint64_t Address);
bool mustRelaxABranch();
void setWorstCaseAddresses();
void splitBranchOnCount(MachineInstr *MI, unsigned AddOpcode);
void splitCompareBranch(MachineInstr *MI, unsigned CompareOpcode);
void relaxBranch(TerminatorInfo &Terminator);
void relaxBranches();
const SystemZInstrInfo *TII = nullptr;
MachineFunction *MF = nullptr;
SmallVector<MBBInfo, 16> MBBs;
SmallVector<TerminatorInfo, 16> Terminators;
};
char SystemZLongBranch::ID = 0;
const uint64_t MaxBackwardRange = 0x10000;
const uint64_t MaxForwardRange = 0xfffe;
}
INITIALIZE_PASS(SystemZLongBranch, DEBUG_TYPE, "SystemZ Long Branch", false,
false)
void SystemZLongBranch::skipNonTerminators(BlockPosition &Position,
MBBInfo &Block) {
if (Log2(Block.Alignment) > Position.KnownBits) {
Position.Address +=
(Block.Alignment.value() - (uint64_t(1) << Position.KnownBits));
Position.KnownBits = Log2(Block.Alignment);
}
Position.Address = alignTo(Position.Address, Block.Alignment);
Block.Address = Position.Address;
Position.Address += Block.Size;
}
void SystemZLongBranch::skipTerminator(BlockPosition &Position,
TerminatorInfo &Terminator,
bool AssumeRelaxed) {
Terminator.Address = Position.Address;
Position.Address += Terminator.Size;
if (AssumeRelaxed)
Position.Address += Terminator.ExtraRelaxSize;
}
static unsigned getInstSizeInBytes(const MachineInstr &MI,
const SystemZInstrInfo *TII) {
unsigned Size = TII->getInstSizeInBytes(MI);
assert((Size ||
MI.isDebugOrPseudoInstr() || MI.isPosition() || MI.isKill() ||
MI.isImplicitDef() || MI.getOpcode() == SystemZ::MemBarrier ||
MI.isInlineAsm() || MI.getOpcode() == SystemZ::STACKMAP ||
MI.getOpcode() == SystemZ::PATCHPOINT) &&
"Missing size value for instruction.");
return Size;
}
TerminatorInfo SystemZLongBranch::describeTerminator(MachineInstr &MI) {
TerminatorInfo Terminator;
Terminator.Size = getInstSizeInBytes(MI, TII);
if (MI.isConditionalBranch() || MI.isUnconditionalBranch()) {
switch (MI.getOpcode()) {
case SystemZ::J:
Terminator.ExtraRelaxSize = 2;
break;
case SystemZ::BRC:
Terminator.ExtraRelaxSize = 2;
break;
case SystemZ::BRCT:
case SystemZ::BRCTG:
Terminator.ExtraRelaxSize = 6;
break;
case SystemZ::BRCTH:
Terminator.ExtraRelaxSize = 0;
break;
case SystemZ::CRJ:
case SystemZ::CLRJ:
Terminator.ExtraRelaxSize = 2;
break;
case SystemZ::CGRJ:
case SystemZ::CLGRJ:
Terminator.ExtraRelaxSize = 4;
break;
case SystemZ::CIJ:
case SystemZ::CGIJ:
Terminator.ExtraRelaxSize = 4;
break;
case SystemZ::CLIJ:
case SystemZ::CLGIJ:
Terminator.ExtraRelaxSize = 6;
break;
default:
llvm_unreachable("Unrecognized branch instruction");
}
Terminator.Branch = &MI;
Terminator.TargetBlock =
TII->getBranchInfo(MI).getMBBTarget()->getNumber();
}
return Terminator;
}
uint64_t SystemZLongBranch::initMBBInfo() {
MF->RenumberBlocks();
unsigned NumBlocks = MF->size();
MBBs.clear();
MBBs.resize(NumBlocks);
Terminators.clear();
Terminators.reserve(NumBlocks);
BlockPosition Position(Log2(MF->getAlignment()));
for (unsigned I = 0; I < NumBlocks; ++I) {
MachineBasicBlock *MBB = MF->getBlockNumbered(I);
MBBInfo &Block = MBBs[I];
Block.Alignment = MBB->getAlignment();
MachineBasicBlock::iterator MI = MBB->begin();
MachineBasicBlock::iterator End = MBB->end();
while (MI != End && !MI->isTerminator()) {
Block.Size += getInstSizeInBytes(*MI, TII);
++MI;
}
skipNonTerminators(Position, Block);
while (MI != End) {
if (!MI->isDebugInstr()) {
assert(MI->isTerminator() && "Terminator followed by non-terminator");
Terminators.push_back(describeTerminator(*MI));
skipTerminator(Position, Terminators.back(), false);
++Block.NumTerminators;
}
++MI;
}
}
return Position.Address;
}
bool SystemZLongBranch::mustRelaxBranch(const TerminatorInfo &Terminator,
uint64_t Address) {
if (!Terminator.Branch || Terminator.ExtraRelaxSize == 0)
return false;
const MBBInfo &Target = MBBs[Terminator.TargetBlock];
if (Address >= Target.Address) {
if (Address - Target.Address <= MaxBackwardRange)
return false;
} else {
if (Target.Address - Address <= MaxForwardRange)
return false;
}
return true;
}
bool SystemZLongBranch::mustRelaxABranch() {
for (auto &Terminator : Terminators)
if (mustRelaxBranch(Terminator, Terminator.Address))
return true;
return false;
}
void SystemZLongBranch::setWorstCaseAddresses() {
SmallVector<TerminatorInfo, 16>::iterator TI = Terminators.begin();
BlockPosition Position(Log2(MF->getAlignment()));
for (auto &Block : MBBs) {
skipNonTerminators(Position, Block);
for (unsigned BTI = 0, BTE = Block.NumTerminators; BTI != BTE; ++BTI) {
skipTerminator(Position, *TI, true);
++TI;
}
}
}
void SystemZLongBranch::splitBranchOnCount(MachineInstr *MI,
unsigned AddOpcode) {
MachineBasicBlock *MBB = MI->getParent();
DebugLoc DL = MI->getDebugLoc();
BuildMI(*MBB, MI, DL, TII->get(AddOpcode))
.add(MI->getOperand(0))
.add(MI->getOperand(1))
.addImm(-1);
MachineInstr *BRCL = BuildMI(*MBB, MI, DL, TII->get(SystemZ::BRCL))
.addImm(SystemZ::CCMASK_ICMP)
.addImm(SystemZ::CCMASK_CMP_NE)
.add(MI->getOperand(2));
BRCL->addRegisterKilled(SystemZ::CC, &TII->getRegisterInfo());
MI->eraseFromParent();
}
void SystemZLongBranch::splitCompareBranch(MachineInstr *MI,
unsigned CompareOpcode) {
MachineBasicBlock *MBB = MI->getParent();
DebugLoc DL = MI->getDebugLoc();
BuildMI(*MBB, MI, DL, TII->get(CompareOpcode))
.add(MI->getOperand(0))
.add(MI->getOperand(1));
MachineInstr *BRCL = BuildMI(*MBB, MI, DL, TII->get(SystemZ::BRCL))
.addImm(SystemZ::CCMASK_ICMP)
.add(MI->getOperand(2))
.add(MI->getOperand(3));
BRCL->addRegisterKilled(SystemZ::CC, &TII->getRegisterInfo());
MI->eraseFromParent();
}
void SystemZLongBranch::relaxBranch(TerminatorInfo &Terminator) {
MachineInstr *Branch = Terminator.Branch;
switch (Branch->getOpcode()) {
case SystemZ::J:
Branch->setDesc(TII->get(SystemZ::JG));
break;
case SystemZ::BRC:
Branch->setDesc(TII->get(SystemZ::BRCL));
break;
case SystemZ::BRCT:
splitBranchOnCount(Branch, SystemZ::AHI);
break;
case SystemZ::BRCTG:
splitBranchOnCount(Branch, SystemZ::AGHI);
break;
case SystemZ::CRJ:
splitCompareBranch(Branch, SystemZ::CR);
break;
case SystemZ::CGRJ:
splitCompareBranch(Branch, SystemZ::CGR);
break;
case SystemZ::CIJ:
splitCompareBranch(Branch, SystemZ::CHI);
break;
case SystemZ::CGIJ:
splitCompareBranch(Branch, SystemZ::CGHI);
break;
case SystemZ::CLRJ:
splitCompareBranch(Branch, SystemZ::CLR);
break;
case SystemZ::CLGRJ:
splitCompareBranch(Branch, SystemZ::CLGR);
break;
case SystemZ::CLIJ:
splitCompareBranch(Branch, SystemZ::CLFI);
break;
case SystemZ::CLGIJ:
splitCompareBranch(Branch, SystemZ::CLGFI);
break;
default:
llvm_unreachable("Unrecognized branch");
}
Terminator.Size += Terminator.ExtraRelaxSize;
Terminator.ExtraRelaxSize = 0;
Terminator.Branch = nullptr;
++LongBranches;
}
void SystemZLongBranch::relaxBranches() {
SmallVector<TerminatorInfo, 16>::iterator TI = Terminators.begin();
BlockPosition Position(Log2(MF->getAlignment()));
for (auto &Block : MBBs) {
skipNonTerminators(Position, Block);
for (unsigned BTI = 0, BTE = Block.NumTerminators; BTI != BTE; ++BTI) {
assert(Position.Address <= TI->Address &&
"Addresses shouldn't go forwards");
if (mustRelaxBranch(*TI, Position.Address))
relaxBranch(*TI);
skipTerminator(Position, *TI, false);
++TI;
}
}
}
bool SystemZLongBranch::runOnMachineFunction(MachineFunction &F) {
TII = static_cast<const SystemZInstrInfo *>(F.getSubtarget().getInstrInfo());
MF = &F;
uint64_t Size = initMBBInfo();
if (Size <= MaxForwardRange || !mustRelaxABranch())
return false;
setWorstCaseAddresses();
relaxBranches();
return true;
}
FunctionPass *llvm::createSystemZLongBranchPass(SystemZTargetMachine &TM) {
return new SystemZLongBranch();
}