diff options
Diffstat (limited to 'llvm/lib/Target/RISCV/RISCVRedundantCopyElimination.cpp')
| -rw-r--r-- | llvm/lib/Target/RISCV/RISCVRedundantCopyElimination.cpp | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/llvm/lib/Target/RISCV/RISCVRedundantCopyElimination.cpp b/llvm/lib/Target/RISCV/RISCVRedundantCopyElimination.cpp new file mode 100644 index 000000000000..3c4a60b81d8e --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVRedundantCopyElimination.cpp @@ -0,0 +1,179 @@ +//=- RISCVRedundantCopyElimination.cpp - Remove useless copy for RISCV ------=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass removes unnecessary zero copies in BBs that are targets of +// beqz/bnez instructions. For instance, the copy instruction in the code below +// can be removed because the beqz jumps to BB#2 when a0 is zero. +// BB#1: +// beqz %a0, <BB#2> +// BB#2: +// %a0 = COPY %x0 +// This pass should be run after register allocation. +// +// This pass is based on the earliest versions of +// AArch64RedundantCopyElimination. +// +// FIXME: Support compares with constants other than zero? This is harder to +// do on RISC-V since branches can't have immediates. +// +//===----------------------------------------------------------------------===// + +#include "RISCV.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Support/Debug.h" + +using namespace llvm; + +#define DEBUG_TYPE "riscv-copyelim" + +STATISTIC(NumCopiesRemoved, "Number of copies removed."); + +namespace { +class RISCVRedundantCopyElimination : public MachineFunctionPass { + const MachineRegisterInfo *MRI; + const TargetRegisterInfo *TRI; + +public: + static char ID; + RISCVRedundantCopyElimination() : MachineFunctionPass(ID) { + initializeRISCVRedundantCopyEliminationPass( + *PassRegistry::getPassRegistry()); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + + StringRef getPassName() const override { + return "RISCV Redundant Copy Elimination"; + } + +private: + bool optimizeBlock(MachineBasicBlock &MBB); +}; + +} // end anonymous namespace + +char RISCVRedundantCopyElimination::ID = 0; + +INITIALIZE_PASS(RISCVRedundantCopyElimination, "riscv-copyelim", + "RISCV redundant copy elimination pass", false, false) + +static bool guaranteesZeroRegInBlock(const MachineInstr &MI, + const MachineBasicBlock &MBB) { + unsigned Opc = MI.getOpcode(); + if (Opc == RISCV::BEQ && MI.getOperand(1).getReg() == RISCV::X0 && + &MBB == MI.getOperand(2).getMBB()) + return true; + if (Opc == RISCV::BNE && MI.getOperand(1).getReg() == RISCV::X0 && + &MBB != MI.getOperand(2).getMBB()) + return true; + + return false; +} + +bool RISCVRedundantCopyElimination::optimizeBlock(MachineBasicBlock &MBB) { + // Check if the current basic block has a single predecessor. + if (MBB.pred_size() != 1) + return false; + + // Check if the predecessor has two successors, implying the block ends in a + // conditional branch. + MachineBasicBlock *PredMBB = *MBB.pred_begin(); + if (PredMBB->succ_size() != 2) + return false; + + MachineBasicBlock::iterator CondBr = PredMBB->getLastNonDebugInstr(); + if (CondBr == PredMBB->end()) + return false; + + while (true) { + // If we run out of terminators, give up. + if (!CondBr->isTerminator()) + return false; + // If we found a branch with X0, stop searching and try to remove copies. + // TODO: Handle multiple branches with different registers. + if (guaranteesZeroRegInBlock(*CondBr, MBB)) + break; + // If we reached the beginning of the basic block, give up. + if (CondBr == PredMBB->begin()) + return false; + --CondBr; + } + + Register TargetReg = CondBr->getOperand(0).getReg(); + if (!TargetReg) + return false; + + bool Changed = false; + MachineBasicBlock::iterator LastChange = MBB.begin(); + // Remove redundant Copy instructions unless TargetReg is modified. + for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); I != E;) { + MachineInstr *MI = &*I; + ++I; + if (MI->isCopy() && MI->getOperand(0).isReg() && + MI->getOperand(1).isReg()) { + Register DefReg = MI->getOperand(0).getReg(); + Register SrcReg = MI->getOperand(1).getReg(); + + if (SrcReg == RISCV::X0 && !MRI->isReserved(DefReg) && + TargetReg == DefReg) { + LLVM_DEBUG(dbgs() << "Remove redundant Copy : "); + LLVM_DEBUG(MI->print(dbgs())); + + MI->eraseFromParent(); + Changed = true; + LastChange = I; + ++NumCopiesRemoved; + continue; + } + } + + if (MI->modifiesRegister(TargetReg, TRI)) + break; + } + + if (!Changed) + return false; + + // Otherwise, we have to fixup the use-def chain, starting with the + // BEQ/BNE. Conservatively mark as much as we can live. + CondBr->clearRegisterKills(TargetReg, TRI); + + // Add newly used reg to the block's live-in list if it isn't there already. + if (!MBB.isLiveIn(TargetReg)) + MBB.addLiveIn(TargetReg); + + // Clear any kills of TargetReg between CondBr and the last removed COPY. + for (MachineInstr &MMI : make_range(MBB.begin(), LastChange)) + MMI.clearRegisterKills(TargetReg, TRI); + + return true; +} + +bool RISCVRedundantCopyElimination::runOnMachineFunction(MachineFunction &MF) { + if (skipFunction(MF.getFunction())) + return false; + + TRI = MF.getSubtarget().getRegisterInfo(); + MRI = &MF.getRegInfo(); + + bool Changed = false; + for (MachineBasicBlock &MBB : MF) + Changed |= optimizeBlock(MBB); + + return Changed; +} + +FunctionPass *llvm::createRISCVRedundantCopyEliminationPass() { + return new RISCVRedundantCopyElimination(); +} |
