aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Target/RISCV/RISCVRedundantCopyElimination.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Target/RISCV/RISCVRedundantCopyElimination.cpp')
-rw-r--r--llvm/lib/Target/RISCV/RISCVRedundantCopyElimination.cpp179
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();
+}