#include "Mips.h"
#include "Mips16InstrInfo.h"
#include "MipsMachineFunction.h"
#include "MipsSubtarget.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Type.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <iterator>
#include <vector>
using namespace llvm;
#define DEBUG_TYPE "mips-constant-islands"
STATISTIC(NumCPEs, "Number of constpool entries");
STATISTIC(NumSplit, "Number of uncond branches inserted");
STATISTIC(NumCBrFixed, "Number of cond branches fixed");
STATISTIC(NumUBrFixed, "Number of uncond branches fixed");
static cl::opt<bool>
AlignConstantIslands("mips-align-constant-islands", cl::Hidden, cl::init(true),
cl::desc("Align constant islands in code"));
static cl::opt<int> ConstantIslandsSmallOffset(
"mips-constant-islands-small-offset",
cl::init(0),
cl::desc("Make small offsets be this amount for testing purposes"),
cl::Hidden);
static cl::opt<bool> NoLoadRelaxation(
"mips-constant-islands-no-load-relaxation",
cl::init(false),
cl::desc("Don't relax loads to long loads - for testing purposes"),
cl::Hidden);
static unsigned int branchTargetOperand(MachineInstr *MI) {
switch (MI->getOpcode()) {
case Mips::Bimm16:
case Mips::BimmX16:
case Mips::Bteqz16:
case Mips::BteqzX16:
case Mips::Btnez16:
case Mips::BtnezX16:
case Mips::JalB16:
return 0;
case Mips::BeqzRxImm16:
case Mips::BeqzRxImmX16:
case Mips::BnezRxImm16:
case Mips::BnezRxImmX16:
return 1;
}
llvm_unreachable("Unknown branch type");
}
static unsigned int longformBranchOpcode(unsigned int Opcode) {
switch (Opcode) {
case Mips::Bimm16:
case Mips::BimmX16:
return Mips::BimmX16;
case Mips::Bteqz16:
case Mips::BteqzX16:
return Mips::BteqzX16;
case Mips::Btnez16:
case Mips::BtnezX16:
return Mips::BtnezX16;
case Mips::JalB16:
return Mips::JalB16;
case Mips::BeqzRxImm16:
case Mips::BeqzRxImmX16:
return Mips::BeqzRxImmX16;
case Mips::BnezRxImm16:
case Mips::BnezRxImmX16:
return Mips::BnezRxImmX16;
}
llvm_unreachable("Unknown branch type");
}
static unsigned int branchMaxOffsets(unsigned int Opcode) {
unsigned Bits, Scale;
switch (Opcode) {
case Mips::Bimm16:
Bits = 11;
Scale = 2;
break;
case Mips::BimmX16:
Bits = 16;
Scale = 2;
break;
case Mips::BeqzRxImm16:
Bits = 8;
Scale = 2;
break;
case Mips::BeqzRxImmX16:
Bits = 16;
Scale = 2;
break;
case Mips::BnezRxImm16:
Bits = 8;
Scale = 2;
break;
case Mips::BnezRxImmX16:
Bits = 16;
Scale = 2;
break;
case Mips::Bteqz16:
Bits = 8;
Scale = 2;
break;
case Mips::BteqzX16:
Bits = 16;
Scale = 2;
break;
case Mips::Btnez16:
Bits = 8;
Scale = 2;
break;
case Mips::BtnezX16:
Bits = 16;
Scale = 2;
break;
default:
llvm_unreachable("Unknown branch type");
}
unsigned MaxOffs = ((1 << (Bits-1))-1) * Scale;
return MaxOffs;
}
namespace {
using Iter = MachineBasicBlock::iterator;
using ReverseIter = MachineBasicBlock::reverse_iterator;
class MipsConstantIslands : public MachineFunctionPass {
struct BasicBlockInfo {
unsigned Offset = 0;
unsigned Size = 0;
BasicBlockInfo() = default;
unsigned postOffset() const { return Offset + Size; }
};
std::vector<BasicBlockInfo> BBInfo;
std::vector<MachineBasicBlock*> WaterList;
SmallSet<MachineBasicBlock*, 4> NewWaterList;
using water_iterator = std::vector<MachineBasicBlock *>::iterator;
struct CPUser {
MachineInstr *MI;
MachineInstr *CPEMI;
MachineBasicBlock *HighWaterMark;
private:
unsigned MaxDisp;
unsigned LongFormMaxDisp; unsigned LongFormOpcode;
public:
bool NegOk;
CPUser(MachineInstr *mi, MachineInstr *cpemi, unsigned maxdisp,
bool neg,
unsigned longformmaxdisp, unsigned longformopcode)
: MI(mi), CPEMI(cpemi), MaxDisp(maxdisp),
LongFormMaxDisp(longformmaxdisp), LongFormOpcode(longformopcode),
NegOk(neg){
HighWaterMark = CPEMI->getParent();
}
unsigned getMaxDisp() const {
unsigned xMaxDisp = ConstantIslandsSmallOffset?
ConstantIslandsSmallOffset: MaxDisp;
return xMaxDisp;
}
void setMaxDisp(unsigned val) {
MaxDisp = val;
}
unsigned getLongFormMaxDisp() const {
return LongFormMaxDisp;
}
unsigned getLongFormOpcode() const {
return LongFormOpcode;
}
};
std::vector<CPUser> CPUsers;
struct CPEntry {
MachineInstr *CPEMI;
unsigned CPI;
unsigned RefCount;
CPEntry(MachineInstr *cpemi, unsigned cpi, unsigned rc = 0)
: CPEMI(cpemi), CPI(cpi), RefCount(rc) {}
};
std::vector<std::vector<CPEntry>> CPEntries;
struct ImmBranch {
MachineInstr *MI;
unsigned MaxDisp : 31;
bool isCond : 1;
int UncondBr;
ImmBranch(MachineInstr *mi, unsigned maxdisp, bool cond, int ubr)
: MI(mi), MaxDisp(maxdisp), isCond(cond), UncondBr(ubr) {}
};
std::vector<ImmBranch> ImmBranches;
bool HasFarJump;
const MipsSubtarget *STI = nullptr;
const Mips16InstrInfo *TII;
MipsFunctionInfo *MFI;
MachineFunction *MF = nullptr;
MachineConstantPool *MCP = nullptr;
unsigned PICLabelUId;
bool PrescannedForConstants = false;
void initPICLabelUId(unsigned UId) {
PICLabelUId = UId;
}
unsigned createPICLabelUId() {
return PICLabelUId++;
}
public:
static char ID;
MipsConstantIslands() : MachineFunctionPass(ID) {}
StringRef getPassName() const override { return "Mips Constant Islands"; }
bool runOnMachineFunction(MachineFunction &F) override;
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::NoVRegs);
}
void doInitialPlacement(std::vector<MachineInstr*> &CPEMIs);
CPEntry *findConstPoolEntry(unsigned CPI, const MachineInstr *CPEMI);
Align getCPEAlign(const MachineInstr &CPEMI);
void initializeFunctionInfo(const std::vector<MachineInstr*> &CPEMIs);
unsigned getOffsetOf(MachineInstr *MI) const;
unsigned getUserOffset(CPUser&) const;
void dumpBBs();
bool isOffsetInRange(unsigned UserOffset, unsigned TrialOffset,
unsigned Disp, bool NegativeOK);
bool isOffsetInRange(unsigned UserOffset, unsigned TrialOffset,
const CPUser &U);
void computeBlockSize(MachineBasicBlock *MBB);
MachineBasicBlock *splitBlockBeforeInstr(MachineInstr &MI);
void updateForInsertedWaterBlock(MachineBasicBlock *NewBB);
void adjustBBOffsetsAfter(MachineBasicBlock *BB);
bool decrementCPEReferenceCount(unsigned CPI, MachineInstr* CPEMI);
int findInRangeCPEntry(CPUser& U, unsigned UserOffset);
int findLongFormInRangeCPEntry(CPUser& U, unsigned UserOffset);
bool findAvailableWater(CPUser&U, unsigned UserOffset,
water_iterator &WaterIter);
void createNewWater(unsigned CPUserIndex, unsigned UserOffset,
MachineBasicBlock *&NewMBB);
bool handleConstantPoolUser(unsigned CPUserIndex);
void removeDeadCPEMI(MachineInstr *CPEMI);
bool removeUnusedCPEntries();
bool isCPEntryInRange(MachineInstr *MI, unsigned UserOffset,
MachineInstr *CPEMI, unsigned Disp, bool NegOk,
bool DoDump = false);
bool isWaterInRange(unsigned UserOffset, MachineBasicBlock *Water,
CPUser &U, unsigned &Growth);
bool isBBInRange(MachineInstr *MI, MachineBasicBlock *BB, unsigned Disp);
bool fixupImmediateBr(ImmBranch &Br);
bool fixupConditionalBr(ImmBranch &Br);
bool fixupUnconditionalBr(ImmBranch &Br);
void prescanForConstants();
};
}
char MipsConstantIslands::ID = 0;
bool MipsConstantIslands::isOffsetInRange
(unsigned UserOffset, unsigned TrialOffset,
const CPUser &U) {
return isOffsetInRange(UserOffset, TrialOffset,
U.getMaxDisp(), U.NegOk);
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void MipsConstantIslands::dumpBBs() {
for (unsigned J = 0, E = BBInfo.size(); J !=E; ++J) {
const BasicBlockInfo &BBI = BBInfo[J];
dbgs() << format("%08x %bb.%u\t", BBI.Offset, J)
<< format(" size=%#x\n", BBInfo[J].Size);
}
}
#endif
bool MipsConstantIslands::runOnMachineFunction(MachineFunction &mf) {
MF = &mf;
MCP = mf.getConstantPool();
STI = &mf.getSubtarget<MipsSubtarget>();
LLVM_DEBUG(dbgs() << "constant island machine function "
<< "\n");
if (!STI->inMips16Mode() || !MipsSubtarget::useConstantIslands()) {
return false;
}
TII = (const Mips16InstrInfo *)STI->getInstrInfo();
MFI = MF->getInfo<MipsFunctionInfo>();
LLVM_DEBUG(dbgs() << "constant island processing "
<< "\n");
if (!PrescannedForConstants) prescanForConstants();
HasFarJump = false;
MF->getRegInfo().invalidateLiveness();
MF->RenumberBlocks();
bool MadeChange = false;
std::vector<MachineInstr*> CPEMIs;
if (!MCP->isEmpty())
doInitialPlacement(CPEMIs);
initPICLabelUId(CPEMIs.size());
initializeFunctionInfo(CPEMIs);
CPEMIs.clear();
LLVM_DEBUG(dumpBBs());
MadeChange |= removeUnusedCPEntries();
unsigned NoCPIters = 0, NoBRIters = 0;
(void)NoBRIters;
while (true) {
LLVM_DEBUG(dbgs() << "Beginning CP iteration #" << NoCPIters << '\n');
bool CPChange = false;
for (unsigned i = 0, e = CPUsers.size(); i != e; ++i)
CPChange |= handleConstantPoolUser(i);
if (CPChange && ++NoCPIters > 30)
report_fatal_error("Constant Island pass failed to converge!");
LLVM_DEBUG(dumpBBs());
NewWaterList.clear();
LLVM_DEBUG(dbgs() << "Beginning BR iteration #" << NoBRIters << '\n');
bool BRChange = false;
for (unsigned i = 0, e = ImmBranches.size(); i != e; ++i)
BRChange |= fixupImmediateBr(ImmBranches[i]);
if (BRChange && ++NoBRIters > 30)
report_fatal_error("Branch Fix Up pass failed to converge!");
LLVM_DEBUG(dumpBBs());
if (!CPChange && !BRChange)
break;
MadeChange = true;
}
LLVM_DEBUG(dbgs() << '\n'; dumpBBs());
BBInfo.clear();
WaterList.clear();
CPUsers.clear();
CPEntries.clear();
ImmBranches.clear();
return MadeChange;
}
void
MipsConstantIslands::doInitialPlacement(std::vector<MachineInstr*> &CPEMIs) {
MachineBasicBlock *BB = MF->CreateMachineBasicBlock();
MF->push_back(BB);
const Align MaxAlign = MCP->getConstantPoolAlign();
BB->setAlignment(AlignConstantIslands ? MaxAlign : Align(4));
MF->ensureAlignment(BB->getAlignment());
SmallVector<MachineBasicBlock::iterator, 8> InsPoint(Log2(MaxAlign) + 1,
BB->end());
const std::vector<MachineConstantPoolEntry> &CPs = MCP->getConstants();
const DataLayout &TD = MF->getDataLayout();
for (unsigned i = 0, e = CPs.size(); i != e; ++i) {
unsigned Size = CPs[i].getSizeInBytes(TD);
assert(Size >= 4 && "Too small constant pool entry");
Align Alignment = CPs[i].getAlign();
assert(isAligned(Alignment, Size) && "CP Entry not multiple of 4 bytes!");
unsigned LogAlign = Log2(Alignment);
MachineBasicBlock::iterator InsAt = InsPoint[LogAlign];
MachineInstr *CPEMI =
BuildMI(*BB, InsAt, DebugLoc(), TII->get(Mips::CONSTPOOL_ENTRY))
.addImm(i).addConstantPoolIndex(i).addImm(Size);
CPEMIs.push_back(CPEMI);
for (unsigned a = LogAlign + 1; a <= Log2(MaxAlign); ++a)
if (InsPoint[a] == InsAt)
InsPoint[a] = CPEMI;
CPEntries.emplace_back(1, CPEntry(CPEMI, i));
++NumCPEs;
LLVM_DEBUG(dbgs() << "Moved CPI#" << i << " to end of function, size = "
<< Size << ", align = " << Alignment.value() << '\n');
}
LLVM_DEBUG(BB->dump());
}
static bool BBHasFallthrough(MachineBasicBlock *MBB) {
MachineFunction::iterator MBBI = MBB->getIterator();
if (std::next(MBBI) == MBB->getParent()->end())
return false;
MachineBasicBlock *NextBB = &*std::next(MBBI);
return llvm::is_contained(MBB->successors(), NextBB);
}
MipsConstantIslands::CPEntry
*MipsConstantIslands::findConstPoolEntry(unsigned CPI,
const MachineInstr *CPEMI) {
std::vector<CPEntry> &CPEs = CPEntries[CPI];
for (CPEntry &CPE : CPEs) {
if (CPE.CPEMI == CPEMI)
return &CPE;
}
return nullptr;
}
Align MipsConstantIslands::getCPEAlign(const MachineInstr &CPEMI) {
assert(CPEMI.getOpcode() == Mips::CONSTPOOL_ENTRY);
if (!AlignConstantIslands)
return Align(4);
unsigned CPI = CPEMI.getOperand(1).getIndex();
assert(CPI < MCP->getConstants().size() && "Invalid constant pool index.");
return MCP->getConstants()[CPI].getAlign();
}
void MipsConstantIslands::
initializeFunctionInfo(const std::vector<MachineInstr*> &CPEMIs) {
BBInfo.clear();
BBInfo.resize(MF->getNumBlockIDs());
for (MachineBasicBlock &MBB : *MF)
computeBlockSize(&MBB);
adjustBBOffsetsAfter(&MF->front());
for (MachineBasicBlock &MBB : *MF) {
if (!BBHasFallthrough(&MBB))
WaterList.push_back(&MBB);
for (MachineInstr &MI : MBB) {
if (MI.isDebugInstr())
continue;
int Opc = MI.getOpcode();
if (MI.isBranch()) {
bool isCond = false;
unsigned Bits = 0;
unsigned Scale = 1;
int UOpc = Opc;
switch (Opc) {
default:
continue; case Mips::Bimm16:
Bits = 11;
Scale = 2;
isCond = false;
break;
case Mips::BimmX16:
Bits = 16;
Scale = 2;
isCond = false;
break;
case Mips::BeqzRxImm16:
UOpc=Mips::Bimm16;
Bits = 8;
Scale = 2;
isCond = true;
break;
case Mips::BeqzRxImmX16:
UOpc=Mips::Bimm16;
Bits = 16;
Scale = 2;
isCond = true;
break;
case Mips::BnezRxImm16:
UOpc=Mips::Bimm16;
Bits = 8;
Scale = 2;
isCond = true;
break;
case Mips::BnezRxImmX16:
UOpc=Mips::Bimm16;
Bits = 16;
Scale = 2;
isCond = true;
break;
case Mips::Bteqz16:
UOpc=Mips::Bimm16;
Bits = 8;
Scale = 2;
isCond = true;
break;
case Mips::BteqzX16:
UOpc=Mips::Bimm16;
Bits = 16;
Scale = 2;
isCond = true;
break;
case Mips::Btnez16:
UOpc=Mips::Bimm16;
Bits = 8;
Scale = 2;
isCond = true;
break;
case Mips::BtnezX16:
UOpc=Mips::Bimm16;
Bits = 16;
Scale = 2;
isCond = true;
break;
}
unsigned MaxOffs = ((1 << (Bits-1))-1) * Scale;
ImmBranches.push_back(ImmBranch(&MI, MaxOffs, isCond, UOpc));
}
if (Opc == Mips::CONSTPOOL_ENTRY)
continue;
for (const MachineOperand &MO : MI.operands())
if (MO.isCPI()) {
unsigned Bits = 0;
unsigned Scale = 1;
bool NegOk = false;
unsigned LongFormBits = 0;
unsigned LongFormScale = 0;
unsigned LongFormOpcode = 0;
switch (Opc) {
default:
llvm_unreachable("Unknown addressing mode for CP reference!");
case Mips::LwRxPcTcp16:
Bits = 8;
Scale = 4;
LongFormOpcode = Mips::LwRxPcTcpX16;
LongFormBits = 14;
LongFormScale = 1;
break;
case Mips::LwRxPcTcpX16:
Bits = 14;
Scale = 1;
NegOk = true;
break;
}
unsigned CPI = MO.getIndex();
MachineInstr *CPEMI = CPEMIs[CPI];
unsigned MaxOffs = ((1 << Bits)-1) * Scale;
unsigned LongFormMaxOffs = ((1 << LongFormBits)-1) * LongFormScale;
CPUsers.push_back(CPUser(&MI, CPEMI, MaxOffs, NegOk, LongFormMaxOffs,
LongFormOpcode));
CPEntry *CPE = findConstPoolEntry(CPI, CPEMI);
assert(CPE && "Cannot find a corresponding CPEntry!");
CPE->RefCount++;
break;
}
}
}
}
void MipsConstantIslands::computeBlockSize(MachineBasicBlock *MBB) {
BasicBlockInfo &BBI = BBInfo[MBB->getNumber()];
BBI.Size = 0;
for (const MachineInstr &MI : *MBB)
BBI.Size += TII->getInstSizeInBytes(MI);
}
unsigned MipsConstantIslands::getOffsetOf(MachineInstr *MI) const {
MachineBasicBlock *MBB = MI->getParent();
unsigned Offset = BBInfo[MBB->getNumber()].Offset;
for (MachineBasicBlock::iterator I = MBB->begin(); &*I != MI; ++I) {
assert(I != MBB->end() && "Didn't find MI in its own basic block?");
Offset += TII->getInstSizeInBytes(*I);
}
return Offset;
}
static bool CompareMBBNumbers(const MachineBasicBlock *LHS,
const MachineBasicBlock *RHS) {
return LHS->getNumber() < RHS->getNumber();
}
void MipsConstantIslands::updateForInsertedWaterBlock
(MachineBasicBlock *NewBB) {
NewBB->getParent()->RenumberBlocks(NewBB);
BBInfo.insert(BBInfo.begin() + NewBB->getNumber(), BasicBlockInfo());
water_iterator IP = llvm::lower_bound(WaterList, NewBB, CompareMBBNumbers);
WaterList.insert(IP, NewBB);
}
unsigned MipsConstantIslands::getUserOffset(CPUser &U) const {
return getOffsetOf(U.MI);
}
MachineBasicBlock *
MipsConstantIslands::splitBlockBeforeInstr(MachineInstr &MI) {
MachineBasicBlock *OrigBB = MI.getParent();
MachineBasicBlock *NewBB =
MF->CreateMachineBasicBlock(OrigBB->getBasicBlock());
MachineFunction::iterator MBBI = ++OrigBB->getIterator();
MF->insert(MBBI, NewBB);
NewBB->splice(NewBB->end(), OrigBB, MI, OrigBB->end());
BuildMI(OrigBB, DebugLoc(), TII->get(Mips::Bimm16)).addMBB(NewBB);
++NumSplit;
NewBB->transferSuccessors(OrigBB);
OrigBB->addSuccessor(NewBB);
MF->RenumberBlocks(NewBB);
BBInfo.insert(BBInfo.begin() + NewBB->getNumber(), BasicBlockInfo());
water_iterator IP = llvm::lower_bound(WaterList, OrigBB, CompareMBBNumbers);
MachineBasicBlock* WaterBB = *IP;
if (WaterBB == OrigBB)
WaterList.insert(std::next(IP), NewBB);
else
WaterList.insert(IP, OrigBB);
NewWaterList.insert(OrigBB);
computeBlockSize(OrigBB);
computeBlockSize(NewBB);
adjustBBOffsetsAfter(OrigBB);
return NewBB;
}
bool MipsConstantIslands::isOffsetInRange(unsigned UserOffset,
unsigned TrialOffset, unsigned MaxDisp,
bool NegativeOK) {
if (UserOffset <= TrialOffset) {
if (TrialOffset - UserOffset <= MaxDisp)
return true;
} else if (NegativeOK) {
if (UserOffset - TrialOffset <= MaxDisp)
return true;
}
return false;
}
bool MipsConstantIslands::isWaterInRange(unsigned UserOffset,
MachineBasicBlock* Water, CPUser &U,
unsigned &Growth) {
unsigned CPEOffset = BBInfo[Water->getNumber()].postOffset();
unsigned NextBlockOffset;
Align NextBlockAlignment;
MachineFunction::const_iterator NextBlock = ++Water->getIterator();
if (NextBlock == MF->end()) {
NextBlockOffset = BBInfo[Water->getNumber()].postOffset();
NextBlockAlignment = Align(1);
} else {
NextBlockOffset = BBInfo[NextBlock->getNumber()].Offset;
NextBlockAlignment = NextBlock->getAlignment();
}
unsigned Size = U.CPEMI->getOperand(2).getImm();
unsigned CPEEnd = CPEOffset + Size;
if (CPEEnd > NextBlockOffset) {
Growth = CPEEnd - NextBlockOffset;
Growth += offsetToAlignment(CPEEnd, NextBlockAlignment);
if (CPEOffset < UserOffset)
UserOffset += Growth;
} else
Growth = 0;
return isOffsetInRange(UserOffset, CPEOffset, U);
}
bool MipsConstantIslands::isCPEntryInRange
(MachineInstr *MI, unsigned UserOffset,
MachineInstr *CPEMI, unsigned MaxDisp,
bool NegOk, bool DoDump) {
unsigned CPEOffset = getOffsetOf(CPEMI);
if (DoDump) {
LLVM_DEBUG({
unsigned Block = MI->getParent()->getNumber();
const BasicBlockInfo &BBI = BBInfo[Block];
dbgs() << "User of CPE#" << CPEMI->getOperand(0).getImm()
<< " max delta=" << MaxDisp
<< format(" insn address=%#x", UserOffset) << " in "
<< printMBBReference(*MI->getParent()) << ": "
<< format("%#x-%x\t", BBI.Offset, BBI.postOffset()) << *MI
<< format("CPE address=%#x offset=%+d: ", CPEOffset,
int(CPEOffset - UserOffset));
});
}
return isOffsetInRange(UserOffset, CPEOffset, MaxDisp, NegOk);
}
#ifndef NDEBUG
static bool BBIsJumpedOver(MachineBasicBlock *MBB) {
if (MBB->pred_size() != 1 || MBB->succ_size() != 1)
return false;
MachineBasicBlock *Succ = *MBB->succ_begin();
MachineBasicBlock *Pred = *MBB->pred_begin();
MachineInstr *PredMI = &Pred->back();
if (PredMI->getOpcode() == Mips::Bimm16)
return PredMI->getOperand(0).getMBB() == Succ;
return false;
}
#endif
void MipsConstantIslands::adjustBBOffsetsAfter(MachineBasicBlock *BB) {
unsigned BBNum = BB->getNumber();
for(unsigned i = BBNum + 1, e = MF->getNumBlockIDs(); i < e; ++i) {
unsigned Offset = BBInfo[i - 1].Offset + BBInfo[i - 1].Size;
BBInfo[i].Offset = Offset;
}
}
bool MipsConstantIslands::decrementCPEReferenceCount(unsigned CPI,
MachineInstr *CPEMI) {
CPEntry *CPE = findConstPoolEntry(CPI, CPEMI);
assert(CPE && "Unexpected!");
if (--CPE->RefCount == 0) {
removeDeadCPEMI(CPEMI);
CPE->CPEMI = nullptr;
--NumCPEs;
return true;
}
return false;
}
int MipsConstantIslands::findInRangeCPEntry(CPUser& U, unsigned UserOffset)
{
MachineInstr *UserMI = U.MI;
MachineInstr *CPEMI = U.CPEMI;
if (isCPEntryInRange(UserMI, UserOffset, CPEMI, U.getMaxDisp(), U.NegOk,
true)) {
LLVM_DEBUG(dbgs() << "In range\n");
return 1;
}
unsigned CPI = CPEMI->getOperand(1).getIndex();
std::vector<CPEntry> &CPEs = CPEntries[CPI];
for (CPEntry &CPE : CPEs) {
if (CPE.CPEMI == CPEMI)
continue;
if (CPE.CPEMI == nullptr)
continue;
if (isCPEntryInRange(UserMI, UserOffset, CPE.CPEMI, U.getMaxDisp(),
U.NegOk)) {
LLVM_DEBUG(dbgs() << "Replacing CPE#" << CPI << " with CPE#" << CPE.CPI
<< "\n");
U.CPEMI = CPE.CPEMI;
for (MachineOperand &MO : UserMI->operands())
if (MO.isCPI()) {
MO.setIndex(CPE.CPI);
break;
}
CPE.RefCount++;
return decrementCPEReferenceCount(CPI, CPEMI) ? 2 : 1;
}
}
return 0;
}
int MipsConstantIslands::findLongFormInRangeCPEntry
(CPUser& U, unsigned UserOffset)
{
MachineInstr *UserMI = U.MI;
MachineInstr *CPEMI = U.CPEMI;
if (isCPEntryInRange(UserMI, UserOffset, CPEMI,
U.getLongFormMaxDisp(), U.NegOk,
true)) {
LLVM_DEBUG(dbgs() << "In range\n");
UserMI->setDesc(TII->get(U.getLongFormOpcode()));
U.setMaxDisp(U.getLongFormMaxDisp());
return 2; }
unsigned CPI = CPEMI->getOperand(1).getIndex();
std::vector<CPEntry> &CPEs = CPEntries[CPI];
for (CPEntry &CPE : CPEs) {
if (CPE.CPEMI == CPEMI)
continue;
if (CPE.CPEMI == nullptr)
continue;
if (isCPEntryInRange(UserMI, UserOffset, CPE.CPEMI, U.getLongFormMaxDisp(),
U.NegOk)) {
LLVM_DEBUG(dbgs() << "Replacing CPE#" << CPI << " with CPE#" << CPE.CPI
<< "\n");
U.CPEMI = CPE.CPEMI;
for (MachineOperand &MO : UserMI->operands())
if (MO.isCPI()) {
MO.setIndex(CPE.CPI);
break;
}
CPE.RefCount++;
return decrementCPEReferenceCount(CPI, CPEMI) ? 2 : 1;
}
}
return 0;
}
static inline unsigned getUnconditionalBrDisp(int Opc) {
switch (Opc) {
case Mips::Bimm16:
return ((1<<10)-1)*2;
case Mips::BimmX16:
return ((1<<16)-1)*2;
default:
break;
}
return ((1<<16)-1)*2;
}
bool MipsConstantIslands::findAvailableWater(CPUser &U, unsigned UserOffset,
water_iterator &WaterIter) {
if (WaterList.empty())
return false;
unsigned BestGrowth = ~0u;
for (water_iterator IP = std::prev(WaterList.end()), B = WaterList.begin();;
--IP) {
MachineBasicBlock* WaterBB = *IP;
unsigned Growth;
if (isWaterInRange(UserOffset, WaterBB, U, Growth) &&
(WaterBB->getNumber() < U.HighWaterMark->getNumber() ||
NewWaterList.count(WaterBB)) && Growth < BestGrowth) {
BestGrowth = Growth;
WaterIter = IP;
LLVM_DEBUG(dbgs() << "Found water after " << printMBBReference(*WaterBB)
<< " Growth=" << Growth << '\n');
if (BestGrowth == 0)
return true;
}
if (IP == B)
break;
}
return BestGrowth != ~0u;
}
void MipsConstantIslands::createNewWater(unsigned CPUserIndex,
unsigned UserOffset,
MachineBasicBlock *&NewMBB) {
CPUser &U = CPUsers[CPUserIndex];
MachineInstr *UserMI = U.MI;
MachineInstr *CPEMI = U.CPEMI;
MachineBasicBlock *UserMBB = UserMI->getParent();
const BasicBlockInfo &UserBBI = BBInfo[UserMBB->getNumber()];
if (BBHasFallthrough(UserMBB)) {
unsigned Delta = 2;
unsigned CPEOffset = UserBBI.postOffset() + Delta;
if (isOffsetInRange(UserOffset, CPEOffset, U)) {
LLVM_DEBUG(dbgs() << "Split at end of " << printMBBReference(*UserMBB)
<< format(", expected CPE offset %#x\n", CPEOffset));
NewMBB = &*++UserMBB->getIterator();
int UncondBr = Mips::Bimm16;
BuildMI(UserMBB, DebugLoc(), TII->get(UncondBr)).addMBB(NewMBB);
unsigned MaxDisp = getUnconditionalBrDisp(UncondBr);
ImmBranches.push_back(ImmBranch(&UserMBB->back(),
MaxDisp, false, UncondBr));
BBInfo[UserMBB->getNumber()].Size += Delta;
adjustBBOffsetsAfter(UserMBB);
return;
}
}
const Align Align = MF->getAlignment();
unsigned BaseInsertOffset = UserOffset + U.getMaxDisp();
LLVM_DEBUG(dbgs() << format("Split in middle of big block before %#x",
BaseInsertOffset));
BaseInsertOffset -= 4;
LLVM_DEBUG(dbgs() << format(", adjusted to %#x", BaseInsertOffset)
<< " la=" << Log2(Align) << '\n');
if (BaseInsertOffset + 8 >= UserBBI.postOffset()) {
BaseInsertOffset = UserBBI.postOffset() - 8;
LLVM_DEBUG(dbgs() << format("Move inside block: %#x\n", BaseInsertOffset));
}
unsigned EndInsertOffset = BaseInsertOffset + 4 +
CPEMI->getOperand(2).getImm();
MachineBasicBlock::iterator MI = UserMI;
++MI;
unsigned CPUIndex = CPUserIndex+1;
unsigned NumCPUsers = CPUsers.size();
for (unsigned Offset = UserOffset + TII->getInstSizeInBytes(*UserMI);
Offset < BaseInsertOffset;
Offset += TII->getInstSizeInBytes(*MI), MI = std::next(MI)) {
assert(MI != UserMBB->end() && "Fell off end of block");
if (CPUIndex < NumCPUsers && CPUsers[CPUIndex].MI == MI) {
CPUser &U = CPUsers[CPUIndex];
if (!isOffsetInRange(Offset, EndInsertOffset, U)) {
BaseInsertOffset -= Align.value();
EndInsertOffset -= Align.value();
}
EndInsertOffset += U.CPEMI->getOperand(2).getImm();
CPUIndex++;
}
}
NewMBB = splitBlockBeforeInstr(*--MI);
}
bool MipsConstantIslands::handleConstantPoolUser(unsigned CPUserIndex) {
CPUser &U = CPUsers[CPUserIndex];
MachineInstr *UserMI = U.MI;
MachineInstr *CPEMI = U.CPEMI;
unsigned CPI = CPEMI->getOperand(1).getIndex();
unsigned Size = CPEMI->getOperand(2).getImm();
unsigned UserOffset = getUserOffset(U);
int result = findInRangeCPEntry(U, UserOffset);
if (result==1) return false;
else if (result==2) return true;
MachineBasicBlock *NewIsland = MF->CreateMachineBasicBlock();
MachineBasicBlock *NewMBB;
water_iterator IP;
if (findAvailableWater(U, UserOffset, IP)) {
LLVM_DEBUG(dbgs() << "Found water in range\n");
MachineBasicBlock *WaterBB = *IP;
if (NewWaterList.erase(WaterBB))
NewWaterList.insert(NewIsland);
NewMBB = &*++WaterBB->getIterator();
} else {
if (!NoLoadRelaxation) {
result = findLongFormInRangeCPEntry(U, UserOffset);
if (result != 0) return true;
}
LLVM_DEBUG(dbgs() << "No water found\n");
createNewWater(CPUserIndex, UserOffset, NewMBB);
MachineBasicBlock *WaterBB = &*--NewMBB->getIterator();
IP = llvm::find(WaterList, WaterBB);
if (IP != WaterList.end())
NewWaterList.erase(WaterBB);
NewWaterList.insert(NewIsland);
}
if (IP != WaterList.end())
WaterList.erase(IP);
MF->insert(NewMBB->getIterator(), NewIsland);
updateForInsertedWaterBlock(NewIsland);
decrementCPEReferenceCount(CPI, CPEMI);
unsigned ID = createPICLabelUId();
U.HighWaterMark = NewIsland;
U.CPEMI = BuildMI(NewIsland, DebugLoc(), TII->get(Mips::CONSTPOOL_ENTRY))
.addImm(ID).addConstantPoolIndex(CPI).addImm(Size);
CPEntries[CPI].push_back(CPEntry(U.CPEMI, ID, 1));
++NumCPEs;
NewIsland->setAlignment(getCPEAlign(*U.CPEMI));
BBInfo[NewIsland->getNumber()].Size += Size;
adjustBBOffsetsAfter(&*--NewIsland->getIterator());
for (MachineOperand &MO : UserMI->operands())
if (MO.isCPI()) {
MO.setIndex(ID);
break;
}
LLVM_DEBUG(
dbgs() << " Moved CPE to #" << ID << " CPI=" << CPI
<< format(" offset=%#x\n", BBInfo[NewIsland->getNumber()].Offset));
return true;
}
void MipsConstantIslands::removeDeadCPEMI(MachineInstr *CPEMI) {
MachineBasicBlock *CPEBB = CPEMI->getParent();
unsigned Size = CPEMI->getOperand(2).getImm();
CPEMI->eraseFromParent();
BBInfo[CPEBB->getNumber()].Size -= Size;
if (CPEBB->empty()) {
BBInfo[CPEBB->getNumber()].Size = 0;
CPEBB->setAlignment(Align(1));
} else {
CPEBB->setAlignment(getCPEAlign(*CPEBB->begin()));
}
adjustBBOffsetsAfter(CPEBB);
assert(!BBIsJumpedOver(CPEBB) && "How did this happen?");
}
bool MipsConstantIslands::removeUnusedCPEntries() {
unsigned MadeChange = false;
for (std::vector<CPEntry> &CPEs : CPEntries) {
for (CPEntry &CPE : CPEs) {
if (CPE.RefCount == 0 && CPE.CPEMI) {
removeDeadCPEMI(CPE.CPEMI);
CPE.CPEMI = nullptr;
MadeChange = true;
}
}
}
return MadeChange;
}
bool MipsConstantIslands::isBBInRange
(MachineInstr *MI,MachineBasicBlock *DestBB, unsigned MaxDisp) {
unsigned PCAdj = 4;
unsigned BrOffset = getOffsetOf(MI) + PCAdj;
unsigned DestOffset = BBInfo[DestBB->getNumber()].Offset;
LLVM_DEBUG(dbgs() << "Branch of destination " << printMBBReference(*DestBB)
<< " from " << printMBBReference(*MI->getParent())
<< " max delta=" << MaxDisp << " from " << getOffsetOf(MI)
<< " to " << DestOffset << " offset "
<< int(DestOffset - BrOffset) << "\t" << *MI);
if (BrOffset <= DestOffset) {
if (DestOffset-BrOffset <= MaxDisp)
return true;
} else {
if (BrOffset-DestOffset <= MaxDisp)
return true;
}
return false;
}
bool MipsConstantIslands::fixupImmediateBr(ImmBranch &Br) {
MachineInstr *MI = Br.MI;
unsigned TargetOperand = branchTargetOperand(MI);
MachineBasicBlock *DestBB = MI->getOperand(TargetOperand).getMBB();
if (isBBInRange(MI, DestBB, Br.MaxDisp))
return false;
if (!Br.isCond)
return fixupUnconditionalBr(Br);
return fixupConditionalBr(Br);
}
bool
MipsConstantIslands::fixupUnconditionalBr(ImmBranch &Br) {
MachineInstr *MI = Br.MI;
MachineBasicBlock *MBB = MI->getParent();
MachineBasicBlock *DestBB = MI->getOperand(0).getMBB();
unsigned BimmX16MaxDisp = ((1 << 16)-1) * 2;
if (isBBInRange(MI, DestBB, BimmX16MaxDisp)) {
Br.MaxDisp = BimmX16MaxDisp;
MI->setDesc(TII->get(Mips::BimmX16));
}
else {
DestBB->setAlignment(Align(4));
Br.MaxDisp = ((1<<24)-1) * 2;
MI->setDesc(TII->get(Mips::JalB16));
}
BBInfo[MBB->getNumber()].Size += 2;
adjustBBOffsetsAfter(MBB);
HasFarJump = true;
++NumUBrFixed;
LLVM_DEBUG(dbgs() << " Changed B to long jump " << *MI);
return true;
}
bool
MipsConstantIslands::fixupConditionalBr(ImmBranch &Br) {
MachineInstr *MI = Br.MI;
unsigned TargetOperand = branchTargetOperand(MI);
MachineBasicBlock *DestBB = MI->getOperand(TargetOperand).getMBB();
unsigned Opcode = MI->getOpcode();
unsigned LongFormOpcode = longformBranchOpcode(Opcode);
unsigned LongFormMaxOff = branchMaxOffsets(LongFormOpcode);
if (isBBInRange(MI, DestBB, LongFormMaxOff)) {
Br.MaxDisp = LongFormMaxOff;
MI->setDesc(TII->get(LongFormOpcode));
return true;
}
MachineBasicBlock *MBB = MI->getParent();
MachineInstr *BMI = &MBB->back();
bool NeedSplit = (BMI != MI) || !BBHasFallthrough(MBB);
unsigned OppositeBranchOpcode = TII->getOppositeBranchOpc(Opcode);
++NumCBrFixed;
if (BMI != MI) {
if (std::next(MachineBasicBlock::iterator(MI)) == std::prev(MBB->end()) &&
BMI->isUnconditionalBranch()) {
unsigned BMITargetOperand = branchTargetOperand(BMI);
MachineBasicBlock *NewDest =
BMI->getOperand(BMITargetOperand).getMBB();
if (isBBInRange(MI, NewDest, Br.MaxDisp)) {
LLVM_DEBUG(
dbgs() << " Invert Bcc condition and swap its destination with "
<< *BMI);
MI->setDesc(TII->get(OppositeBranchOpcode));
BMI->getOperand(BMITargetOperand).setMBB(DestBB);
MI->getOperand(TargetOperand).setMBB(NewDest);
return true;
}
}
}
if (NeedSplit) {
splitBlockBeforeInstr(*MI);
int delta = TII->getInstSizeInBytes(MBB->back());
BBInfo[MBB->getNumber()].Size -= delta;
MBB->back().eraseFromParent();
}
MachineBasicBlock *NextBB = &*++MBB->getIterator();
LLVM_DEBUG(dbgs() << " Insert B to " << printMBBReference(*DestBB)
<< " also invert condition and change dest. to "
<< printMBBReference(*NextBB) << "\n");
if (MI->getNumExplicitOperands() == 2) {
BuildMI(MBB, DebugLoc(), TII->get(OppositeBranchOpcode))
.addReg(MI->getOperand(0).getReg())
.addMBB(NextBB);
} else {
BuildMI(MBB, DebugLoc(), TII->get(OppositeBranchOpcode))
.addMBB(NextBB);
}
Br.MI = &MBB->back();
BBInfo[MBB->getNumber()].Size += TII->getInstSizeInBytes(MBB->back());
BuildMI(MBB, DebugLoc(), TII->get(Br.UncondBr)).addMBB(DestBB);
BBInfo[MBB->getNumber()].Size += TII->getInstSizeInBytes(MBB->back());
unsigned MaxDisp = getUnconditionalBrDisp(Br.UncondBr);
ImmBranches.push_back(ImmBranch(&MBB->back(), MaxDisp, false, Br.UncondBr));
BBInfo[MI->getParent()->getNumber()].Size -= TII->getInstSizeInBytes(*MI);
MI->eraseFromParent();
adjustBBOffsetsAfter(MBB);
return true;
}
void MipsConstantIslands::prescanForConstants() {
unsigned J = 0;
(void)J;
for (MachineBasicBlock &B : *MF) {
for (MachineBasicBlock::instr_iterator I = B.instr_begin(),
EB = B.instr_end();
I != EB; ++I) {
switch(I->getDesc().getOpcode()) {
case Mips::LwConstant32: {
PrescannedForConstants = true;
LLVM_DEBUG(dbgs() << "constant island constant " << *I << "\n");
J = I->getNumOperands();
LLVM_DEBUG(dbgs() << "num operands " << J << "\n");
MachineOperand& Literal = I->getOperand(1);
if (Literal.isImm()) {
int64_t V = Literal.getImm();
LLVM_DEBUG(dbgs() << "literal " << V << "\n");
Type *Int32Ty =
Type::getInt32Ty(MF->getFunction().getContext());
const Constant *C = ConstantInt::get(Int32Ty, V);
unsigned index = MCP->getConstantPoolIndex(C, Align(4));
I->getOperand(2).ChangeToImmediate(index);
LLVM_DEBUG(dbgs() << "constant island constant " << *I << "\n");
I->setDesc(TII->get(Mips::LwRxPcTcp16));
I->removeOperand(1);
I->removeOperand(1);
I->addOperand(MachineOperand::CreateCPI(index, 0));
I->addOperand(MachineOperand::CreateImm(4));
}
break;
}
default:
break;
}
}
}
}
FunctionPass *llvm::createMipsConstantIslandPass() {
return new MipsConstantIslands();
}