#include "BPF.h"
#include "BPFInstrInfo.h"
#include "BPFTargetMachine.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/Support/Debug.h"
#include <set>
using namespace llvm;
#define DEBUG_TYPE "bpf-mi-zext-elim"
STATISTIC(ZExtElemNum, "Number of zero extension shifts eliminated");
namespace {
struct BPFMIPeephole : public MachineFunctionPass {
static char ID;
const BPFInstrInfo *TII;
MachineFunction *MF;
MachineRegisterInfo *MRI;
BPFMIPeephole() : MachineFunctionPass(ID) {
initializeBPFMIPeepholePass(*PassRegistry::getPassRegistry());
}
private:
void initialize(MachineFunction &MFParm);
bool isCopyFrom32Def(MachineInstr *CopyMI);
bool isInsnFrom32Def(MachineInstr *DefInsn);
bool isPhiFrom32Def(MachineInstr *MovMI);
bool isMovFrom32Def(MachineInstr *MovMI);
bool eliminateZExtSeq();
bool eliminateZExt();
std::set<MachineInstr *> PhiInsns;
public:
bool runOnMachineFunction(MachineFunction &MF) override {
if (skipFunction(MF.getFunction()))
return false;
initialize(MF);
bool ZExtSeqExist, ZExtExist;
ZExtSeqExist = eliminateZExtSeq();
ZExtExist = eliminateZExt();
return ZExtSeqExist || ZExtExist;
}
};
void BPFMIPeephole::initialize(MachineFunction &MFParm) {
MF = &MFParm;
MRI = &MF->getRegInfo();
TII = MF->getSubtarget<BPFSubtarget>().getInstrInfo();
LLVM_DEBUG(dbgs() << "*** BPF MachineSSA ZEXT Elim peephole pass ***\n\n");
}
bool BPFMIPeephole::isCopyFrom32Def(MachineInstr *CopyMI)
{
MachineOperand &opnd = CopyMI->getOperand(1);
if (!opnd.isReg())
return false;
Register Reg = opnd.getReg();
if (!Register::isVirtualRegister(Reg))
return false;
if (MRI->getRegClass(Reg) == &BPF::GPRRegClass)
return false;
MachineInstr *DefInsn = MRI->getVRegDef(Reg);
if (!isInsnFrom32Def(DefInsn))
return false;
return true;
}
bool BPFMIPeephole::isPhiFrom32Def(MachineInstr *PhiMI)
{
for (unsigned i = 1, e = PhiMI->getNumOperands(); i < e; i += 2) {
MachineOperand &opnd = PhiMI->getOperand(i);
if (!opnd.isReg())
return false;
MachineInstr *PhiDef = MRI->getVRegDef(opnd.getReg());
if (!PhiDef)
return false;
if (PhiDef->isPHI()) {
if (!PhiInsns.insert(PhiDef).second)
return false;
if (!isPhiFrom32Def(PhiDef))
return false;
}
if (PhiDef->getOpcode() == BPF::COPY && !isCopyFrom32Def(PhiDef))
return false;
}
return true;
}
bool BPFMIPeephole::isInsnFrom32Def(MachineInstr *DefInsn)
{
if (!DefInsn)
return false;
if (DefInsn->isPHI()) {
if (!PhiInsns.insert(DefInsn).second)
return false;
if (!isPhiFrom32Def(DefInsn))
return false;
} else if (DefInsn->getOpcode() == BPF::COPY) {
if (!isCopyFrom32Def(DefInsn))
return false;
}
return true;
}
bool BPFMIPeephole::isMovFrom32Def(MachineInstr *MovMI)
{
MachineInstr *DefInsn = MRI->getVRegDef(MovMI->getOperand(1).getReg());
LLVM_DEBUG(dbgs() << " Def of Mov Src:");
LLVM_DEBUG(DefInsn->dump());
PhiInsns.clear();
if (!isInsnFrom32Def(DefInsn))
return false;
LLVM_DEBUG(dbgs() << " One ZExt elim sequence identified.\n");
return true;
}
bool BPFMIPeephole::eliminateZExtSeq() {
MachineInstr* ToErase = nullptr;
bool Eliminated = false;
for (MachineBasicBlock &MBB : *MF) {
for (MachineInstr &MI : MBB) {
if (ToErase) {
ToErase->eraseFromParent();
ToErase = nullptr;
}
if (MI.getOpcode() == BPF::SRL_ri &&
MI.getOperand(2).getImm() == 32) {
Register DstReg = MI.getOperand(0).getReg();
Register ShfReg = MI.getOperand(1).getReg();
MachineInstr *SllMI = MRI->getVRegDef(ShfReg);
LLVM_DEBUG(dbgs() << "Starting SRL found:");
LLVM_DEBUG(MI.dump());
if (!SllMI ||
SllMI->isPHI() ||
SllMI->getOpcode() != BPF::SLL_ri ||
SllMI->getOperand(2).getImm() != 32)
continue;
LLVM_DEBUG(dbgs() << " SLL found:");
LLVM_DEBUG(SllMI->dump());
MachineInstr *MovMI = MRI->getVRegDef(SllMI->getOperand(1).getReg());
if (!MovMI ||
MovMI->isPHI() ||
MovMI->getOpcode() != BPF::MOV_32_64)
continue;
LLVM_DEBUG(dbgs() << " Type cast Mov found:");
LLVM_DEBUG(MovMI->dump());
Register SubReg = MovMI->getOperand(1).getReg();
if (!isMovFrom32Def(MovMI)) {
LLVM_DEBUG(dbgs()
<< " One ZExt elim sequence failed qualifying elim.\n");
continue;
}
BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(BPF::SUBREG_TO_REG), DstReg)
.addImm(0).addReg(SubReg).addImm(BPF::sub_32);
SllMI->eraseFromParent();
MovMI->eraseFromParent();
ToErase = &MI;
ZExtElemNum++;
Eliminated = true;
}
}
}
return Eliminated;
}
bool BPFMIPeephole::eliminateZExt() {
MachineInstr* ToErase = nullptr;
bool Eliminated = false;
for (MachineBasicBlock &MBB : *MF) {
for (MachineInstr &MI : MBB) {
if (ToErase) {
ToErase->eraseFromParent();
ToErase = nullptr;
}
if (MI.getOpcode() != BPF::MOV_32_64)
continue;
LLVM_DEBUG(dbgs() << "Candidate MOV_32_64 instruction:");
LLVM_DEBUG(MI.dump());
if (!isMovFrom32Def(&MI))
continue;
LLVM_DEBUG(dbgs() << "Removing the MOV_32_64 instruction\n");
Register dst = MI.getOperand(0).getReg();
Register src = MI.getOperand(1).getReg();
BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(BPF::SUBREG_TO_REG), dst)
.addImm(0).addReg(src).addImm(BPF::sub_32);
ToErase = &MI;
Eliminated = true;
}
}
return Eliminated;
}
}
INITIALIZE_PASS(BPFMIPeephole, DEBUG_TYPE,
"BPF MachineSSA Peephole Optimization For ZEXT Eliminate",
false, false)
char BPFMIPeephole::ID = 0;
FunctionPass* llvm::createBPFMIPeepholePass() { return new BPFMIPeephole(); }
STATISTIC(RedundantMovElemNum, "Number of redundant moves eliminated");
namespace {
struct BPFMIPreEmitPeephole : public MachineFunctionPass {
static char ID;
MachineFunction *MF;
const TargetRegisterInfo *TRI;
BPFMIPreEmitPeephole() : MachineFunctionPass(ID) {
initializeBPFMIPreEmitPeepholePass(*PassRegistry::getPassRegistry());
}
private:
void initialize(MachineFunction &MFParm);
bool eliminateRedundantMov();
public:
bool runOnMachineFunction(MachineFunction &MF) override {
if (skipFunction(MF.getFunction()))
return false;
initialize(MF);
return eliminateRedundantMov();
}
};
void BPFMIPreEmitPeephole::initialize(MachineFunction &MFParm) {
MF = &MFParm;
TRI = MF->getSubtarget<BPFSubtarget>().getRegisterInfo();
LLVM_DEBUG(dbgs() << "*** BPF PreEmit peephole pass ***\n\n");
}
bool BPFMIPreEmitPeephole::eliminateRedundantMov() {
MachineInstr* ToErase = nullptr;
bool Eliminated = false;
for (MachineBasicBlock &MBB : *MF) {
for (MachineInstr &MI : MBB) {
if (ToErase) {
LLVM_DEBUG(dbgs() << " Redundant Mov Eliminated:");
LLVM_DEBUG(ToErase->dump());
ToErase->eraseFromParent();
ToErase = nullptr;
}
unsigned Opcode = MI.getOpcode();
if (Opcode == BPF::MOV_rr) {
Register dst = MI.getOperand(0).getReg();
Register src = MI.getOperand(1).getReg();
if (dst != src)
continue;
ToErase = &MI;
RedundantMovElemNum++;
Eliminated = true;
}
}
}
return Eliminated;
}
}
INITIALIZE_PASS(BPFMIPreEmitPeephole, "bpf-mi-pemit-peephole",
"BPF PreEmit Peephole Optimization", false, false)
char BPFMIPreEmitPeephole::ID = 0;
FunctionPass* llvm::createBPFMIPreEmitPeepholePass()
{
return new BPFMIPreEmitPeephole();
}
STATISTIC(TruncElemNum, "Number of truncation eliminated");
namespace {
struct BPFMIPeepholeTruncElim : public MachineFunctionPass {
static char ID;
const BPFInstrInfo *TII;
MachineFunction *MF;
MachineRegisterInfo *MRI;
BPFMIPeepholeTruncElim() : MachineFunctionPass(ID) {
initializeBPFMIPeepholeTruncElimPass(*PassRegistry::getPassRegistry());
}
private:
void initialize(MachineFunction &MFParm);
bool eliminateTruncSeq();
public:
bool runOnMachineFunction(MachineFunction &MF) override {
if (skipFunction(MF.getFunction()))
return false;
initialize(MF);
return eliminateTruncSeq();
}
};
static bool TruncSizeCompatible(int TruncSize, unsigned opcode)
{
if (TruncSize == 1)
return opcode == BPF::LDB || opcode == BPF::LDB32;
if (TruncSize == 2)
return opcode == BPF::LDH || opcode == BPF::LDH32;
if (TruncSize == 4)
return opcode == BPF::LDW || opcode == BPF::LDW32;
return false;
}
void BPFMIPeepholeTruncElim::initialize(MachineFunction &MFParm) {
MF = &MFParm;
MRI = &MF->getRegInfo();
TII = MF->getSubtarget<BPFSubtarget>().getInstrInfo();
LLVM_DEBUG(dbgs() << "*** BPF MachineSSA TRUNC Elim peephole pass ***\n\n");
}
bool BPFMIPeepholeTruncElim::eliminateTruncSeq() {
MachineInstr* ToErase = nullptr;
bool Eliminated = false;
for (MachineBasicBlock &MBB : *MF) {
for (MachineInstr &MI : MBB) {
MachineInstr *MI2 = nullptr;
Register DstReg, SrcReg;
MachineInstr *DefMI;
int TruncSize = -1;
if (ToErase) {
ToErase->eraseFromParent();
ToErase = nullptr;
}
if (MI.getOpcode() == BPF::SRL_ri &&
MI.getOperand(2).getImm() == 32) {
SrcReg = MI.getOperand(1).getReg();
if (!MRI->hasOneNonDBGUse(SrcReg))
continue;
MI2 = MRI->getVRegDef(SrcReg);
DstReg = MI.getOperand(0).getReg();
if (!MI2 ||
MI2->getOpcode() != BPF::SLL_ri ||
MI2->getOperand(2).getImm() != 32)
continue;
SrcReg = MI2->getOperand(1).getReg();
DefMI = MRI->getVRegDef(SrcReg);
if (DefMI)
TruncSize = 4;
} else if (MI.getOpcode() == BPF::AND_ri ||
MI.getOpcode() == BPF::AND_ri_32) {
SrcReg = MI.getOperand(1).getReg();
DstReg = MI.getOperand(0).getReg();
DefMI = MRI->getVRegDef(SrcReg);
if (!DefMI)
continue;
int64_t imm = MI.getOperand(2).getImm();
if (imm == 0xff)
TruncSize = 1;
else if (imm == 0xffff)
TruncSize = 2;
}
if (TruncSize == -1)
continue;
if (DefMI->isPHI()) {
bool CheckFail = false;
for (unsigned i = 1, e = DefMI->getNumOperands(); i < e; i += 2) {
MachineOperand &opnd = DefMI->getOperand(i);
if (!opnd.isReg()) {
CheckFail = true;
break;
}
MachineInstr *PhiDef = MRI->getVRegDef(opnd.getReg());
if (!PhiDef || PhiDef->isPHI() ||
!TruncSizeCompatible(TruncSize, PhiDef->getOpcode())) {
CheckFail = true;
break;
}
}
if (CheckFail)
continue;
} else if (!TruncSizeCompatible(TruncSize, DefMI->getOpcode())) {
continue;
}
BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(BPF::MOV_rr), DstReg)
.addReg(SrcReg);
if (MI2)
MI2->eraseFromParent();
ToErase = &MI;
TruncElemNum++;
Eliminated = true;
}
}
return Eliminated;
}
}
INITIALIZE_PASS(BPFMIPeepholeTruncElim, "bpf-mi-trunc-elim",
"BPF MachineSSA Peephole Optimization For TRUNC Eliminate",
false, false)
char BPFMIPeepholeTruncElim::ID = 0;
FunctionPass* llvm::createBPFMIPeepholeTruncElimPass()
{
return new BPFMIPeepholeTruncElim();
}