#include "AArch64.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/CodeGen/LiveRegUnits.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/Support/Debug.h"
using namespace llvm;
#define DEBUG_TYPE "aarch64-copyelim"
STATISTIC(NumCopiesRemoved, "Number of copies removed.");
namespace {
class AArch64RedundantCopyElimination : public MachineFunctionPass {
const MachineRegisterInfo *MRI;
const TargetRegisterInfo *TRI;
LiveRegUnits DomBBClobberedRegs, DomBBUsedRegs;
LiveRegUnits OptBBClobberedRegs, OptBBUsedRegs;
public:
static char ID;
AArch64RedundantCopyElimination() : MachineFunctionPass(ID) {
initializeAArch64RedundantCopyEliminationPass(
*PassRegistry::getPassRegistry());
}
struct RegImm {
MCPhysReg Reg;
int32_t Imm;
RegImm(MCPhysReg Reg, int32_t Imm) : Reg(Reg), Imm(Imm) {}
};
bool knownRegValInBlock(MachineInstr &CondBr, MachineBasicBlock *MBB,
SmallVectorImpl<RegImm> &KnownRegs,
MachineBasicBlock::iterator &FirstUse);
bool optimizeBlock(MachineBasicBlock *MBB);
bool runOnMachineFunction(MachineFunction &MF) override;
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::NoVRegs);
}
StringRef getPassName() const override {
return "AArch64 Redundant Copy Elimination";
}
};
char AArch64RedundantCopyElimination::ID = 0;
}
INITIALIZE_PASS(AArch64RedundantCopyElimination, "aarch64-copyelim",
"AArch64 redundant copy elimination pass", false, false)
bool AArch64RedundantCopyElimination::knownRegValInBlock(
MachineInstr &CondBr, MachineBasicBlock *MBB,
SmallVectorImpl<RegImm> &KnownRegs, MachineBasicBlock::iterator &FirstUse) {
unsigned Opc = CondBr.getOpcode();
if (((Opc == AArch64::CBZW || Opc == AArch64::CBZX) &&
MBB == CondBr.getOperand(1).getMBB()) ||
((Opc == AArch64::CBNZW || Opc == AArch64::CBNZX) &&
MBB != CondBr.getOperand(1).getMBB())) {
FirstUse = CondBr;
KnownRegs.push_back(RegImm(CondBr.getOperand(0).getReg(), 0));
return true;
}
if (Opc != AArch64::Bcc)
return false;
AArch64CC::CondCode CC = (AArch64CC::CondCode)CondBr.getOperand(0).getImm();
if (CC != AArch64CC::EQ && CC != AArch64CC::NE)
return false;
MachineBasicBlock *BrTarget = CondBr.getOperand(1).getMBB();
if ((CC == AArch64CC::EQ && BrTarget != MBB) ||
(CC == AArch64CC::NE && BrTarget == MBB))
return false;
MachineBasicBlock *PredMBB = *MBB->pred_begin();
assert(PredMBB == CondBr.getParent() &&
"Conditional branch not in predecessor block!");
if (CondBr == PredMBB->begin())
return false;
DomBBClobberedRegs.clear();
DomBBUsedRegs.clear();
MachineBasicBlock::reverse_iterator RIt = CondBr.getReverseIterator();
for (MachineInstr &PredI : make_range(std::next(RIt), PredMBB->rend())) {
bool IsCMN = false;
switch (PredI.getOpcode()) {
default:
break;
case AArch64::ADDSWri:
case AArch64::ADDSXri:
IsCMN = true;
LLVM_FALLTHROUGH;
case AArch64::SUBSWri:
case AArch64::SUBSXri: {
if (!PredI.getOperand(1).isReg())
return false;
MCPhysReg DstReg = PredI.getOperand(0).getReg();
MCPhysReg SrcReg = PredI.getOperand(1).getReg();
bool Res = false;
if (PredI.getOperand(2).isImm() && DomBBClobberedRegs.available(SrcReg) &&
SrcReg != DstReg) {
int32_t KnownImm = PredI.getOperand(2).getImm();
int32_t Shift = PredI.getOperand(3).getImm();
KnownImm <<= Shift;
if (IsCMN)
KnownImm = -KnownImm;
FirstUse = PredI;
KnownRegs.push_back(RegImm(SrcReg, KnownImm));
Res = true;
}
if (DstReg == AArch64::WZR || DstReg == AArch64::XZR)
return Res;
if (!DomBBClobberedRegs.available(DstReg))
return Res;
FirstUse = PredI;
KnownRegs.push_back(RegImm(DstReg, 0));
return true;
}
case AArch64::ADCSWr:
case AArch64::ADCSXr:
case AArch64::ADDSWrr:
case AArch64::ADDSWrs:
case AArch64::ADDSWrx:
case AArch64::ADDSXrr:
case AArch64::ADDSXrs:
case AArch64::ADDSXrx:
case AArch64::ADDSXrx64:
case AArch64::ANDSWri:
case AArch64::ANDSWrr:
case AArch64::ANDSWrs:
case AArch64::ANDSXri:
case AArch64::ANDSXrr:
case AArch64::ANDSXrs:
case AArch64::BICSWrr:
case AArch64::BICSWrs:
case AArch64::BICSXrs:
case AArch64::BICSXrr:
case AArch64::SBCSWr:
case AArch64::SBCSXr:
case AArch64::SUBSWrr:
case AArch64::SUBSWrs:
case AArch64::SUBSWrx:
case AArch64::SUBSXrr:
case AArch64::SUBSXrs:
case AArch64::SUBSXrx:
case AArch64::SUBSXrx64: {
MCPhysReg DstReg = PredI.getOperand(0).getReg();
if (DstReg == AArch64::WZR || DstReg == AArch64::XZR)
return false;
if (!DomBBClobberedRegs.available(DstReg))
return false;
FirstUse = PredI;
KnownRegs.push_back(RegImm(DstReg, 0));
return true;
}
}
if (PredI.definesRegister(AArch64::NZCV))
return false;
LiveRegUnits::accumulateUsedDefed(PredI, DomBBClobberedRegs, DomBBUsedRegs,
TRI);
}
return false;
}
bool AArch64RedundantCopyElimination::optimizeBlock(MachineBasicBlock *MBB) {
if (MBB->pred_size() != 1)
return false;
MachineBasicBlock *PredMBB = *MBB->pred_begin();
if (PredMBB->succ_size() != 2)
return false;
MachineBasicBlock::iterator CondBr = PredMBB->getLastNonDebugInstr();
if (CondBr == PredMBB->end())
return false;
MachineBasicBlock::iterator FirstUse;
bool SeenFirstUse = false;
SmallVector<RegImm, 4> KnownRegs;
MachineBasicBlock::iterator Itr = std::next(CondBr);
do {
--Itr;
if (!knownRegValInBlock(*Itr, MBB, KnownRegs, FirstUse))
continue;
OptBBClobberedRegs.clear();
OptBBUsedRegs.clear();
for (auto PredI = Itr;; --PredI) {
if (FirstUse == PredI)
SeenFirstUse = true;
if (PredI->isCopy()) {
MCPhysReg CopyDstReg = PredI->getOperand(0).getReg();
MCPhysReg CopySrcReg = PredI->getOperand(1).getReg();
for (auto &KnownReg : KnownRegs) {
if (!OptBBClobberedRegs.available(KnownReg.Reg))
continue;
if (CopySrcReg == KnownReg.Reg &&
OptBBClobberedRegs.available(CopyDstReg)) {
KnownRegs.push_back(RegImm(CopyDstReg, KnownReg.Imm));
if (SeenFirstUse)
FirstUse = PredI;
break;
}
if (CopyDstReg == KnownReg.Reg &&
OptBBClobberedRegs.available(CopySrcReg)) {
KnownRegs.push_back(RegImm(CopySrcReg, KnownReg.Imm));
if (SeenFirstUse)
FirstUse = PredI;
break;
}
}
}
if (PredI == PredMBB->begin())
break;
LiveRegUnits::accumulateUsedDefed(*PredI, OptBBClobberedRegs,
OptBBUsedRegs, TRI);
if (all_of(KnownRegs, [&](RegImm KnownReg) {
return !OptBBClobberedRegs.available(KnownReg.Reg);
}))
break;
}
break;
} while (Itr != PredMBB->begin() && Itr->isTerminator());
if (KnownRegs.empty())
return false;
bool Changed = false;
SmallSetVector<unsigned, 4> UsedKnownRegs;
MachineBasicBlock::iterator LastChange = MBB->begin();
for (MachineBasicBlock::iterator I = MBB->begin(), E = MBB->end(); I != E;) {
MachineInstr *MI = &*I;
++I;
bool RemovedMI = false;
bool IsCopy = MI->isCopy();
bool IsMoveImm = MI->isMoveImmediate();
if (IsCopy || IsMoveImm) {
Register DefReg = MI->getOperand(0).getReg();
Register SrcReg = IsCopy ? MI->getOperand(1).getReg() : Register();
int64_t SrcImm = IsMoveImm ? MI->getOperand(1).getImm() : 0;
if (!MRI->isReserved(DefReg) &&
((IsCopy && (SrcReg == AArch64::XZR || SrcReg == AArch64::WZR)) ||
IsMoveImm)) {
for (RegImm &KnownReg : KnownRegs) {
if (KnownReg.Reg != DefReg &&
!TRI->isSuperRegister(DefReg, KnownReg.Reg))
continue;
if (IsCopy && KnownReg.Imm != 0)
continue;
if (IsMoveImm) {
if (KnownReg.Imm != SrcImm)
continue;
MCPhysReg CmpReg = KnownReg.Reg;
if (any_of(MI->implicit_operands(), [CmpReg](MachineOperand &O) {
return !O.isDead() && O.isReg() && O.isDef() &&
O.getReg() != CmpReg;
}))
continue;
if (TRI->isSuperRegister(DefReg, KnownReg.Reg) && KnownReg.Imm < 0)
continue;
}
if (IsCopy)
LLVM_DEBUG(dbgs() << "Remove redundant Copy : " << *MI);
else
LLVM_DEBUG(dbgs() << "Remove redundant Move : " << *MI);
MI->eraseFromParent();
Changed = true;
LastChange = I;
NumCopiesRemoved++;
UsedKnownRegs.insert(KnownReg.Reg);
RemovedMI = true;
break;
}
}
}
if (RemovedMI)
continue;
for (unsigned RI = 0; RI < KnownRegs.size();)
if (MI->modifiesRegister(KnownRegs[RI].Reg, TRI)) {
std::swap(KnownRegs[RI], KnownRegs[KnownRegs.size() - 1]);
KnownRegs.pop_back();
} else {
++RI;
}
if (KnownRegs.empty())
break;
}
if (!Changed)
return false;
for (MCPhysReg KnownReg : UsedKnownRegs)
if (!MBB->isLiveIn(KnownReg))
MBB->addLiveIn(KnownReg);
LLVM_DEBUG(dbgs() << "Clearing kill flags.\n\tFirstUse: " << *FirstUse
<< "\tLastChange: " << *LastChange);
for (MachineInstr &MMI : make_range(FirstUse, PredMBB->end()))
MMI.clearKillInfo();
for (MachineInstr &MMI : make_range(MBB->begin(), LastChange))
MMI.clearKillInfo();
return true;
}
bool AArch64RedundantCopyElimination::runOnMachineFunction(
MachineFunction &MF) {
if (skipFunction(MF.getFunction()))
return false;
TRI = MF.getSubtarget().getRegisterInfo();
MRI = &MF.getRegInfo();
DomBBClobberedRegs.init(*TRI);
DomBBUsedRegs.init(*TRI);
OptBBClobberedRegs.init(*TRI);
OptBBUsedRegs.init(*TRI);
bool Changed = false;
for (MachineBasicBlock &MBB : MF)
Changed |= optimizeBlock(&MBB);
return Changed;
}
FunctionPass *llvm::createAArch64RedundantCopyEliminationPass() {
return new AArch64RedundantCopyElimination();
}