[llvm] [ARM] Port Redundant Copy Elimination to ARM (PR #203706)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jun 13 10:09:25 PDT 2026
https://github.com/mike-goutokuji updated https://github.com/llvm/llvm-project/pull/203706
>From 353b9f29117df559645fdc87f535c6b5e770b053 Mon Sep 17 00:00:00 2001
From: Mike-Goutokuji <gfunni234 at gmail.com>
Date: Sat, 13 Jun 2026 11:11:02 -0400
Subject: [PATCH] [ARM] Port Redundant Copy Elimination to ARM
Based off AArch64's and RISC-V's version. I had to adjust constandisland to avoid a regression related to kill markers.
---
llvm/lib/Target/ARM/ARM.h | 9 +
llvm/lib/Target/ARM/ARMConstantIslandPass.cpp | 28 +-
llvm/lib/Target/ARM/ARMPassRegistry.def | 1 +
.../ARM/ARMRedundantCopyElimination.cpp | 731 ++++++++++++++++++
llvm/lib/Target/ARM/ARMTargetMachine.cpp | 11 +
llvm/lib/Target/ARM/CMakeLists.txt | 1 +
llvm/test/CodeGen/ARM/O3-pipeline.ll | 1 +
llvm/test/CodeGen/ARM/arm-shrink-wrapping.ll | 4 -
llvm/test/CodeGen/ARM/code-placement.ll | 4 +-
llvm/test/CodeGen/ARM/machine-copy-remove.mir | 186 +++++
llvm/test/CodeGen/ARM/v8m-tail-call.ll | 1 -
.../CodeGen/Thumb/thumb-shrink-wrapping.ll | 4 -
.../Thumb2/LowOverheadLoops/reductions.ll | 1 -
.../test/CodeGen/Thumb2/mve-blockplacement.ll | 15 +-
llvm/test/CodeGen/Thumb2/mve-memtp-branch.ll | 24 +-
.../outlined-fn-may-clobber-lr-in-caller.ll | 1 -
16 files changed, 981 insertions(+), 41 deletions(-)
create mode 100644 llvm/lib/Target/ARM/ARMRedundantCopyElimination.cpp
create mode 100644 llvm/test/CodeGen/ARM/machine-copy-remove.mir
diff --git a/llvm/lib/Target/ARM/ARM.h b/llvm/lib/Target/ARM/ARM.h
index 08caef8419d12..59533ba3a941e 100644
--- a/llvm/lib/Target/ARM/ARM.h
+++ b/llvm/lib/Target/ARM/ARM.h
@@ -58,6 +58,7 @@ FunctionPass *createARMSLSHardeningPass();
FunctionPass *createARMIndirectThunks();
Pass *createMVELaneInterleavingPass();
FunctionPass *createARMFixCortexA57AES1742098Pass();
+FunctionPass *createARMRedundantCopyEliminationPass();
void LowerARMMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI,
ARMAsmPrinter &AP);
@@ -81,6 +82,7 @@ void initializeMVETailPredicationPass(PassRegistry &);
void initializeMVEVPTBlockPass(PassRegistry &);
void initializeThumb2ITBlockPass(PassRegistry &);
void initializeThumb2SizeReducePass(PassRegistry &);
+void initializeARMRedundantCopyEliminationPass(PassRegistry &);
class ARMPreAllocLoadStoreOptPass
: public OptionalPassInfoMixin<ARMPreAllocLoadStoreOptPass> {
@@ -95,6 +97,13 @@ class ARMLoadStoreOptPass : public OptionalPassInfoMixin<ARMLoadStoreOptPass> {
MachineFunctionAnalysisManager &MFAM);
};
+class ARMRedundantCopyEliminationPass
+ : public OptionalPassInfoMixin<ARMRedundantCopyEliminationPass> {
+public:
+ PreservedAnalyses run(MachineFunction &MF,
+ MachineFunctionAnalysisManager &MFAM);
+};
+
} // end namespace llvm
#endif // LLVM_LIB_TARGET_ARM_ARM_H
diff --git a/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp b/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp
index 97db51129cff2..107786a278614 100644
--- a/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp
+++ b/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp
@@ -27,6 +27,7 @@
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/CodeGen/LivePhysRegs.h"
+#include "llvm/CodeGen/LiveRegUnits.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineDominators.h"
@@ -1861,6 +1862,29 @@ bool ARMConstantIslands::optimizeThumb2Instructions() {
return MadeChange;
}
+/// isCPSRDeadOnCondBranchExit - Return whether CPSR is dead when a conditional
+/// branch instruction is executed. Used to verify that folding tCMPi8+t2Bcc
+/// into tCBZ/tCBNZ is safe.
+static bool isCPSRDeadOnCondBranchExit(const MachineInstr &Br,
+ const TargetRegisterInfo *TRI) {
+ if (Br.killsRegister(ARM::CPSR, TRI))
+ return true;
+
+ // Kill markers may have been cleared (e.g. by arm-copyelim). Compute
+ // liveness information for CPSR to make the decision by walking backwards
+ // from the end of the block.
+ const MachineBasicBlock *MBB = Br.getParent();
+ LiveRegUnits LRU(*TRI);
+ LRU.addLiveOuts(*MBB);
+ for (auto I = MBB->rbegin(); I != MBB->rend(); ++I) {
+ if (&*I == &Br)
+ break;
+ LRU.stepBackward(*I);
+ }
+
+ // If CPSR is available, it is dead on exit from the branch.
+ return LRU.available(ARM::CPSR);
+}
bool ARMConstantIslands::optimizeThumb2Branches() {
@@ -1910,7 +1934,8 @@ bool ARMConstantIslands::optimizeThumb2Branches() {
// If the conditional branch doesn't kill CPSR, then CPSR can be liveout
// so this transformation is not safe.
- if (!Br.MI->killsRegister(ARM::CPSR, /*TRI=*/nullptr))
+ auto *TRI = STI->getRegisterInfo();
+ if (!isCPSRDeadOnCondBranchExit(*Br.MI, TRI))
return false;
Register PredReg;
@@ -1932,7 +1957,6 @@ bool ARMConstantIslands::optimizeThumb2Branches() {
return false;
// Search backwards to find a tCMPi8
- auto *TRI = STI->getRegisterInfo();
MachineInstr *CmpMI = findCMPToFoldIntoCBZ(Br.MI, TRI);
if (!CmpMI || CmpMI->getOpcode() != ARM::tCMPi8)
return false;
diff --git a/llvm/lib/Target/ARM/ARMPassRegistry.def b/llvm/lib/Target/ARM/ARMPassRegistry.def
index 968411531182b..51bf43c8f9b2f 100644
--- a/llvm/lib/Target/ARM/ARMPassRegistry.def
+++ b/llvm/lib/Target/ARM/ARMPassRegistry.def
@@ -28,4 +28,5 @@
#endif
MACHINE_FUNCTION_PASS("arm-ldst-opt", ARMLoadStoreOptPass())
MACHINE_FUNCTION_PASS("arm-prera-ldst-opt", ARMPreAllocLoadStoreOptPass())
+MACHINE_FUNCTION_PASS("arm-copyelim", ARMRedundantCopyEliminationPass())
#undef MACHINE_FUNCTION_PASS
diff --git a/llvm/lib/Target/ARM/ARMRedundantCopyElimination.cpp b/llvm/lib/Target/ARM/ARMRedundantCopyElimination.cpp
new file mode 100644
index 0000000000000..37e1440515826
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMRedundantCopyElimination.cpp
@@ -0,0 +1,731 @@
+//=- ARMRedundantCopyElimination.cpp - Remove useless copy for ARM -=//
+//
+// 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 copies/moves in BBs based on a dominating
+// condition.
+//
+// We handle four cases:
+// 1. For BBs that are targets of tCBZ/tCBNZ instructions, we know the value of
+// the source register is zero on the taken/not-taken path. For instance, the
+// move instruction in the code below can be removed because the tCBZ jumps
+// to %bb.2 when r0 is zero.
+//
+// %bb.1:
+// tCBZ $r0, %bb.2
+// %bb.2:
+// $r0 = t2MOVi 0 ; <-- redundant
+//
+// 2. If the CPSR-setting instruction defines a GPR, we can remove a redundant
+// zero move in some cases.
+//
+// %bb.0:
+// $r0 = t2SUBrr $r1, $r2, def $cpsr
+// t2Bcc %bb.2, ne, killed $cpsr
+// %bb.1:
+// $r0 = t2MOVi 0 ; <-- redundant
+// %bb.2:
+//
+// 3. If the CPSR-setting instruction is a comparison against a constant, we
+// can remove a redundant move immediate in some cases.
+//
+// %bb.0:
+// t2CMPri $r0, 1, implicit-def $cpsr
+// t2Bcc %bb.1, eq, killed $cpsr
+// %bb.1:
+// $r0 = t2MOVi 1 ; <-- redundant
+//
+// 4. If the CPSR-setting instruction is a register comparison, we can remove a
+// redundant copy/move between the compared registers on the EQ path.
+//
+// %bb.0:
+// t2CMPrr $r0, $r1, implicit-def $cpsr
+// t2Bcc %bb.1, eq, killed $cpsr
+// %bb.1:
+// $r0 = t2MOVr $r1 ; <-- redundant
+//
+// This pass should be run after register allocation.
+//
+// FIXME: This could also be extended to check the whole dominance subtree below
+// the comparison if the compile time regression is acceptable.
+//
+//===----------------------------------------------------------------------===//
+#include "ARM.h"
+#include "ARMBaseInstrInfo.h"
+#include "Utils/ARMBaseInfo.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/IR/Function.h"
+#include "llvm/Support/Debug.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "arm-copyelim"
+
+STATISTIC(NumCopiesRemoved, "Number of copies removed.");
+
+namespace {
+
+struct RegImm {
+ MCPhysReg Reg;
+ int32_t Imm;
+ RegImm(MCPhysReg Reg, int32_t Imm) : Reg(Reg), Imm(Imm) {}
+};
+
+struct RegEqual {
+ MCPhysReg Reg1;
+ MCPhysReg Reg2;
+ RegEqual(MCPhysReg Reg1, MCPhysReg Reg2) : Reg1(Reg1), Reg2(Reg2) {}
+};
+
+class ARMRedundantCopyEliminationImpl {
+public:
+ bool run(MachineFunction &MF);
+
+private:
+ const MachineRegisterInfo *MRI;
+ const TargetRegisterInfo *TRI;
+ const TargetInstrInfo *TII;
+
+ // DomBBClobberedRegs is used when computing known values in the dominating
+ // BB.
+ LiveRegUnits DomBBClobberedRegs, DomBBUsedRegs;
+
+ // OptBBClobberedRegs is used when optimizing away redundant copies/moves.
+ LiveRegUnits OptBBClobberedRegs, OptBBUsedRegs;
+
+ bool getCBZKnownRegs(MachineInstr &CondBr, MachineBasicBlock *MBB,
+ SmallVectorImpl<RegImm> &KnownRegs,
+ MachineBasicBlock::iterator &FirstUse);
+ bool knownRegValInBlock(MachineInstr &CondBr, MachineBasicBlock *MBB,
+ SmallVectorImpl<RegImm> &KnownRegs,
+ SmallVectorImpl<RegEqual> &KnownEqualRegs,
+ MachineBasicBlock::iterator &FirstUse);
+ bool optimizeBlock(MachineBasicBlock *MBB);
+};
+
+class ARMRedundantCopyElimination : public MachineFunctionPass {
+public:
+ static char ID;
+ ARMRedundantCopyElimination() : MachineFunctionPass(ID) {}
+
+ bool runOnMachineFunction(MachineFunction &MF) override;
+ MachineFunctionProperties getRequiredProperties() const override {
+ return MachineFunctionProperties().setNoVRegs();
+ }
+ StringRef getPassName() const override {
+ return "ARM Redundant Copy Elimination";
+ }
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesCFG();
+ MachineFunctionPass::getAnalysisUsage(AU);
+ }
+};
+
+char ARMRedundantCopyElimination::ID = 0;
+
+} // end anonymous namespace
+
+INITIALIZE_PASS(ARMRedundantCopyElimination, "arm-copyelim",
+ "ARM redundant copy elimination pass", false, false)
+
+static bool isCompareImm(const MachineInstr &MI, MCPhysReg &SrcReg,
+ int32_t &Imm) {
+ switch (MI.getOpcode()) {
+ case ARM::CMPri:
+ case ARM::t2CMPri:
+ case ARM::tCMPi8:
+ SrcReg = MI.getOperand(0).getReg();
+ Imm = MI.getOperand(1).getImm();
+ return true;
+ case ARM::CMNri:
+ case ARM::t2CMNri:
+ SrcReg = MI.getOperand(0).getReg();
+ Imm = -MI.getOperand(1).getImm();
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool isCompareReg(const MachineInstr &MI, MCPhysReg &Rn, MCPhysReg &Rm) {
+ switch (MI.getOpcode()) {
+ case ARM::CMPrr:
+ case ARM::t2CMPrr:
+ case ARM::tCMPr:
+ case ARM::tCMPhir:
+ Rn = MI.getOperand(0).getReg();
+ Rm = MI.getOperand(1).getReg();
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool isFlagSettingAluWithDest(const MachineInstr &MI) {
+ if (!MI.definesRegister(ARM::CPSR, /*TRI=*/nullptr))
+ return false;
+ if (MI.getNumOperands() == 0 || !MI.getOperand(0).isReg())
+ return false;
+ return MI.getOperand(0).isDef();
+}
+
+static bool isKnownZeroSource(const MachineInstr &MI,
+ ArrayRef<RegImm> KnownRegs) {
+ if (MI.getNumOperands() < 2 || !MI.getOperand(1).isReg())
+ return false;
+ MCPhysReg SrcReg = MI.getOperand(1).getReg();
+ for (const RegImm &KnownReg : KnownRegs) {
+ if (KnownReg.Imm == 0 && KnownReg.Reg == SrcReg)
+ return true;
+ }
+ return false;
+}
+
+static bool getMoveImmediateValue(const MachineInstr &MI, int64_t &ImmVal) {
+ for (const MachineOperand &MO : MI.operands()) {
+ if (MO.isImm()) {
+ ImmVal = MO.getImm();
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool isRedundantZeroAssign(const MachineInstr &MI, MCPhysReg DefReg,
+ ArrayRef<RegImm> KnownRegs) {
+ for (const RegImm &KnownReg : KnownRegs) {
+ if (KnownReg.Imm != 0)
+ continue;
+ if (KnownReg.Reg != DefReg)
+ continue;
+ int64_t ImmVal;
+ if (MI.isMoveImmediate() && getMoveImmediateValue(MI, ImmVal) &&
+ ImmVal == 0)
+ return true;
+ if (MI.isCopy() && isKnownZeroSource(MI, KnownRegs))
+ return true;
+ switch (MI.getOpcode()) {
+ case ARM::MOVr:
+ case ARM::t2MOVr:
+ case ARM::tMOVr:
+ if (isKnownZeroSource(MI, KnownRegs))
+ return true;
+ break;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+static bool isRedundantEqualRegAssign(const MachineInstr &MI,
+ ArrayRef<RegEqual> KnownEqualRegs) {
+ if (MI.getNumOperands() < 2 || !MI.getOperand(0).isReg() ||
+ !MI.getOperand(1).isReg())
+ return false;
+ MCPhysReg DefReg = MI.getOperand(0).getReg();
+ MCPhysReg SrcReg = MI.getOperand(1).getReg();
+ for (const RegEqual &Eq : KnownEqualRegs) {
+ if ((DefReg == Eq.Reg1 && SrcReg == Eq.Reg2) ||
+ (DefReg == Eq.Reg2 && SrcReg == Eq.Reg1))
+ return true;
+ }
+ return false;
+}
+
+static bool shouldStopPredScan(ArrayRef<RegImm> KnownRegs,
+ ArrayRef<RegEqual> KnownEqualRegs,
+ const LiveRegUnits &Clobbered) {
+ bool AnyKnownReg = any_of(
+ KnownRegs, [&](const RegImm &K) { return Clobbered.available(K.Reg); });
+ bool AnyKnownEqual = any_of(KnownEqualRegs, [&](const RegEqual &Eq) {
+ return Clobbered.available(Eq.Reg1) && Clobbered.available(Eq.Reg2);
+ });
+ return !AnyKnownReg && !AnyKnownEqual;
+}
+
+static void propagateEqualRegThroughCopy(MCPhysReg CopyDstReg,
+ MCPhysReg CopySrcReg,
+ ArrayRef<RegEqual> KnownEqualRegs,
+ SmallVectorImpl<RegEqual> &Out) {
+ Out.push_back(RegEqual(CopyDstReg, CopySrcReg));
+ for (const RegEqual &Eq : KnownEqualRegs) {
+ if (CopySrcReg == Eq.Reg1)
+ Out.push_back(RegEqual(CopyDstReg, Eq.Reg2));
+ else if (CopySrcReg == Eq.Reg2)
+ Out.push_back(RegEqual(CopyDstReg, Eq.Reg1));
+ else if (CopyDstReg == Eq.Reg1)
+ Out.push_back(RegEqual(CopySrcReg, Eq.Reg2));
+ else if (CopyDstReg == Eq.Reg2)
+ Out.push_back(RegEqual(CopySrcReg, Eq.Reg1));
+ }
+}
+
+/// It's possible to determine the value of a register based on a dominating
+/// condition. To do so, this function checks to see if the basic block \p MBB
+/// is the target of a conditional branch \p CondBr with an equality comparison.
+/// If the branch is a tCBZ/tCBNZ, we know the value of its source operand is
+/// zero in \p MBB for some cases.
+bool ARMRedundantCopyEliminationImpl::getCBZKnownRegs(
+ MachineInstr &CondBr, MachineBasicBlock *MBB,
+ SmallVectorImpl<RegImm> &KnownRegs, MachineBasicBlock::iterator &FirstUse) {
+ unsigned Opc = CondBr.getOpcode();
+ if (Opc != ARM::tCBZ && Opc != ARM::tCBNZ)
+ return false;
+
+ // Check if the current basic block is the target block to which the
+ // tCBZ/tCBNZ instruction jumps when its register is zero.
+ MCPhysReg Reg = CondBr.getOperand(0).getReg();
+ MachineBasicBlock *Target = CondBr.getOperand(1).getMBB();
+ if ((Opc == ARM::tCBZ && Target == MBB) ||
+ (Opc == ARM::tCBNZ && Target != MBB)) {
+ FirstUse = CondBr;
+ KnownRegs.push_back(RegImm(Reg, 0));
+ return true;
+ }
+ return false;
+}
+
+/// It's possible to determine the value of a register based on a dominating
+/// condition. To do so, this function checks to see if the basic block \p MBB
+/// is the target of a conditional branch \p CondBr with an equality comparison.
+/// If the branch is a tCBZ/tCBNZ, getCBZKnownRegs handles that case. Otherwise,
+/// we find and inspect the CPSR-setting instruction (e.g., CMP, CMN, SUBS). If
+/// this instruction defines a GPR, we know the value of the destination
+/// register is zero in \p MBB for some cases. In addition, if the CPSR-setting
+/// instruction is comparing against a constant we know the source register is
+/// equal to the constant in \p MBB for some cases. If the CPSR-setting
+/// instruction is a register comparison, we know the compared registers are
+/// equal in \p MBB for some cases.
+///
+/// If we find any known constant values, push a physical register and constant
+/// value pair onto the KnownRegs vector. If we find any known equal registers,
+/// push them onto KnownEqualRegs. Return true if we find any known values;
+/// otherwise, return false.
+bool ARMRedundantCopyEliminationImpl::knownRegValInBlock(
+ MachineInstr &CondBr, MachineBasicBlock *MBB,
+ SmallVectorImpl<RegImm> &KnownRegs,
+ SmallVectorImpl<RegEqual> &KnownEqualRegs,
+ MachineBasicBlock::iterator &FirstUse) {
+ // Otherwise, must be a conditional branch.
+ if (!isCondBranchOpcode(CondBr.getOpcode()))
+ return false;
+
+ // Must be an equality check (i.e., == or !=).
+ ARMCC::CondCodes CC = (ARMCC::CondCodes)CondBr.getOperand(1).getImm();
+ if (CC != ARMCC::EQ && CC != ARMCC::NE)
+ return false;
+
+ MachineBasicBlock *BrTarget = CondBr.getOperand(0).getMBB();
+ if ((CC == ARMCC::EQ && BrTarget != MBB) ||
+ (CC == ARMCC::NE && BrTarget == MBB))
+ return false;
+
+ // Stop if we get to the beginning of PredMBB.
+ MachineBasicBlock *PredMBB = *MBB->pred_begin();
+ assert(PredMBB == CondBr.getParent() &&
+ "Conditional branch not in predecessor block!");
+ // Stop if we get to the beginning of PredMBB.
+ if (CondBr == PredMBB->begin())
+ return false;
+
+ // Registers clobbered in PredMBB between CondBr instruction and current
+ // instruction being checked in loop.
+ DomBBClobberedRegs.clear();
+ DomBBUsedRegs.clear();
+
+ // Find compare instruction that sets CPSR used by CondBr.
+ MachineBasicBlock::reverse_iterator RIt = CondBr.getReverseIterator();
+ for (MachineInstr &PredI : make_range(std::next(RIt), PredMBB->rend())) {
+ MCPhysReg SrcReg;
+ int32_t KnownImm;
+ if (isCompareImm(PredI, SrcReg, KnownImm)) {
+ Register PredReg;
+ if (getInstrPredicate(PredI, PredReg) != ARMCC::AL)
+ return false;
+ bool Res = false;
+ // If we're comparing against a non-symbolic immediate and the source
+ // register of the compare is not modified (including a self-clobbering
+ // compare) between the compare and conditional branch we know the value
+ // of the 1st source operand.
+ if (DomBBClobberedRegs.available(SrcReg)) {
+ FirstUse = PredI;
+ KnownRegs.push_back(RegImm(SrcReg, KnownImm));
+ Res = true;
+ }
+ return Res;
+ }
+
+ MCPhysReg Rn;
+ MCPhysReg Rm;
+ if (isCompareReg(PredI, Rn, Rm)) {
+ Register PredReg;
+ if (getInstrPredicate(PredI, PredReg) != ARMCC::AL)
+ return false;
+ // If we're comparing two registers and neither are modified between the
+ // compare and the conditional branch we know they are equal on the EQ
+ // path.
+ if (DomBBClobberedRegs.available(Rn) &&
+ DomBBClobberedRegs.available(Rm)) {
+ FirstUse = PredI;
+ KnownEqualRegs.push_back(RegEqual(Rn, Rm));
+ return true;
+ }
+ return false;
+ }
+
+ // Look for CPSR-setting instructions that define a GPR.
+ if (isFlagSettingAluWithDest(PredI)) {
+ Register PredReg;
+ if (getInstrPredicate(PredI, PredReg) != ARMCC::AL)
+ return false;
+ MCPhysReg DstReg = PredI.getOperand(0).getReg();
+
+ // The destination register must not be modified between the CPSR setting
+ // instruction and the conditional branch.
+ if (!DomBBClobberedRegs.available(DstReg))
+ return false;
+
+ // We've found the instruction that sets CPSR whose DstReg == 0.
+ FirstUse = PredI;
+ KnownRegs.push_back(RegImm(DstReg, 0));
+ return true;
+ }
+
+ // Bail if we see an instruction that defines CPSR that we don't handle.
+ if (PredI.definesRegister(ARM::CPSR, /*TRI=*/nullptr))
+ return false;
+
+ // Track clobbered and used registers.
+ LiveRegUnits::accumulateUsedDefed(PredI, DomBBClobberedRegs, DomBBUsedRegs,
+ TRI);
+ }
+ return false;
+}
+
+bool ARMRedundantCopyEliminationImpl::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;
+
+ // Keep track of the earliest point in the PredMBB block where kill markers
+ // need to be removed if a COPY is removed.
+ MachineBasicBlock::iterator FirstUse;
+ // After calling getCBZKnownRegs or knownRegValInBlock, FirstUse will either
+ // point to a tCBZ/tCBNZ or a compare (e.g., CMP). In the latter case, we
+ // must take care when updating FirstUse when scanning for COPY instructions.
+ // In particular, if there's a COPY in between the compare and branch the COPY
+ // should not update FirstUse.
+ bool SeenFirstUse = false;
+ // Registers that contain a known value at the start of MBB.
+ SmallVector<RegImm, 4> KnownRegs;
+ SmallVector<RegEqual, 2> KnownEqualRegs;
+ MachineBasicBlock::iterator CondBrIt = PredMBB->end();
+ for (MachineInstr &Term : PredMBB->terminators()) {
+ if (Term.getOpcode() == ARM::tCBZ || Term.getOpcode() == ARM::tCBNZ) {
+ CondBrIt = Term.getIterator();
+ if (getCBZKnownRegs(Term, MBB, KnownRegs, FirstUse))
+ break;
+ KnownRegs.clear();
+ CondBrIt = PredMBB->end();
+ }
+ }
+
+ if (KnownRegs.empty() && KnownEqualRegs.empty()) {
+ MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
+ SmallVector<MachineOperand, 4> Cond;
+ if (TII->analyzeBranch(*PredMBB, TBB, FBB, Cond, /*AllowModify*/ false) ||
+ Cond.size() != 2)
+ return false;
+
+ // Must be a conditional branch.
+ for (MachineInstr &MI : reverse(PredMBB->instrs())) {
+ if (!isCondBranchOpcode(MI.getOpcode()))
+ continue;
+ CondBrIt = MI.getIterator();
+ if (!knownRegValInBlock(MI, MBB, KnownRegs, KnownEqualRegs, FirstUse))
+ return false;
+ break;
+ }
+ if (CondBrIt == PredMBB->end())
+ return false;
+ }
+
+ // We've not found registers with a known value, time to bail out.
+ if (KnownRegs.empty() && KnownEqualRegs.empty())
+ return false;
+
+ // Reset the clobbered and used register units.
+ OptBBClobberedRegs.clear();
+ OptBBUsedRegs.clear();
+
+ // Look backward in PredMBB for COPYs from the known reg to find other
+ // registers that are known to be a constant value.
+ for (auto PredI = CondBrIt;; --PredI) {
+ if (FirstUse == PredI)
+ SeenFirstUse = true;
+
+ if (PredI->isCopy()) {
+ MCPhysReg CopyDstReg = PredI->getOperand(0).getReg();
+ MCPhysReg CopySrcReg = PredI->getOperand(1).getReg();
+ for (const RegImm &KnownReg : KnownRegs) {
+ if (!OptBBClobberedRegs.available(KnownReg.Reg))
+ continue;
+ // If a known register is copied, the copy destination also has the
+ // known value.
+ if (CopySrcReg == KnownReg.Reg &&
+ OptBBClobberedRegs.available(CopyDstReg)) {
+ KnownRegs.push_back(RegImm(CopyDstReg, KnownReg.Imm));
+ if (SeenFirstUse)
+ FirstUse = PredI;
+ break;
+ }
+ // If we have X = COPY Y, and X is known to be zero, then now Y is
+ // known to be zero.
+ if (CopyDstReg == KnownReg.Reg &&
+ OptBBClobberedRegs.available(CopySrcReg)) {
+ KnownRegs.push_back(RegImm(CopySrcReg, KnownReg.Imm));
+ if (SeenFirstUse)
+ FirstUse = PredI;
+ break;
+ }
+ }
+ SmallVector<RegEqual, 2> NewEqual;
+ if (OptBBClobberedRegs.available(CopyDstReg) &&
+ OptBBClobberedRegs.available(CopySrcReg)) {
+ propagateEqualRegThroughCopy(CopyDstReg, CopySrcReg, KnownEqualRegs,
+ NewEqual);
+ }
+ if (!NewEqual.empty()) {
+ KnownEqualRegs.append(NewEqual.begin(), NewEqual.end());
+ if (SeenFirstUse)
+ FirstUse = PredI;
+ }
+ }
+
+ // Stop if we get to the beginning of PredMBB.
+ if (PredI == PredMBB->begin())
+ break;
+
+ LiveRegUnits::accumulateUsedDefed(*PredI, OptBBClobberedRegs, OptBBUsedRegs,
+ TRI);
+ // Stop if all of the known regs have been clobbered.
+ if (shouldStopPredScan(KnownRegs, KnownEqualRegs, OptBBClobberedRegs))
+ break;
+ }
+
+ if (KnownRegs.empty() && KnownEqualRegs.empty())
+ return false;
+
+ bool Changed = false;
+ // UsedKnownRegs is the set of KnownRegs that have had uses added to MBB.
+ SmallSetVector<unsigned, 4> UsedKnownRegs;
+ MachineBasicBlock::iterator LastChange = MBB->begin();
+ // Remove redundant copy/move instructions unless KnownReg is modified.
+ 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();
+ bool IsRegMov = false;
+ switch (MI->getOpcode()) {
+ case ARM::MOVr:
+ case ARM::t2MOVr:
+ case ARM::tMOVr:
+ IsRegMov = true;
+ break;
+ }
+ if (IsCopy || IsMoveImm || IsRegMov) {
+ Register DefReg = MI->getOperand(0).getReg();
+ if (!MRI->isReserved(DefReg)) {
+ if ((IsCopy || IsRegMov) && !KnownEqualRegs.empty() &&
+ isRedundantEqualRegAssign(*MI, KnownEqualRegs)) {
+ // Don't remove an instruction that has other live defs.
+ if (any_of(MI->operands(), [DefReg](MachineOperand &O) {
+ return O.isReg() && O.isDef() && !O.isDead() &&
+ O.getReg() != DefReg;
+ }))
+ continue;
+
+ for (const RegEqual &Eq : KnownEqualRegs) {
+ if (MI->getOperand(0).getReg() == Eq.Reg1 ||
+ MI->getOperand(0).getReg() == Eq.Reg2 ||
+ MI->getOperand(1).getReg() == Eq.Reg1 ||
+ MI->getOperand(1).getReg() == Eq.Reg2) {
+ UsedKnownRegs.insert(Eq.Reg1);
+ UsedKnownRegs.insert(Eq.Reg2);
+ }
+ }
+ LLVM_DEBUG(dbgs() << "Remove redundant equal-reg move: " << *MI);
+ MI->eraseFromParent();
+ Changed = true;
+ LastChange = I;
+ ++NumCopiesRemoved;
+ RemovedMI = true;
+ }
+
+ for (RegImm &KnownReg : KnownRegs) {
+ if (RemovedMI)
+ break;
+ if (KnownReg.Reg != DefReg)
+ continue;
+
+ bool ShouldRemove = false;
+ if (KnownReg.Imm == 0 &&
+ isRedundantZeroAssign(*MI, DefReg, KnownRegs))
+ ShouldRemove = true;
+ else if (IsMoveImm) {
+ // For a move immediate, the known immediate must match the source
+ // immediate.
+ int64_t ImmVal;
+ if (getMoveImmediateValue(*MI, ImmVal) && KnownReg.Imm == ImmVal)
+ ShouldRemove = true;
+ }
+
+ if (!ShouldRemove)
+ continue;
+
+ // Don't remove an instruction that has other live defs.
+ MCPhysReg CmpReg = KnownReg.Reg;
+ if (any_of(MI->operands(), [CmpReg](MachineOperand &O) {
+ return O.isReg() && O.isDef() && !O.isDead() &&
+ O.getReg() != CmpReg;
+ }))
+ 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;
+ }
+ }
+ }
+
+ // Skip to the next instruction if we removed the COPY/MovImm.
+ if (RemovedMI)
+ continue;
+
+ // Remove any regs the MI clobbers from the KnownRegs set.
+ 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();
+ // Don't increment RI since we need to now check the swapped-in
+ // KnownRegs[RI].
+ } else {
+ ++RI;
+ }
+ }
+
+ // Remove any equal-reg pairs the MI clobbers from the KnownEqualRegs set.
+ for (unsigned RI = 0; RI < KnownEqualRegs.size();) {
+ if (MI->modifiesRegister(KnownEqualRegs[RI].Reg1, TRI) ||
+ MI->modifiesRegister(KnownEqualRegs[RI].Reg2, TRI)) {
+ std::swap(KnownEqualRegs[RI],
+ KnownEqualRegs[KnownEqualRegs.size() - 1]);
+ KnownEqualRegs.pop_back();
+ } else {
+ ++RI;
+ }
+ }
+
+ // Continue until both known sets are empty.
+ if (KnownRegs.empty() && KnownEqualRegs.empty())
+ break;
+ }
+
+ if (!Changed)
+ return false;
+
+ // Add newly used regs to the block's live-in list if they aren't there
+ // already.
+ for (MCPhysReg KnownReg : UsedKnownRegs)
+ if (!MBB->isLiveIn(KnownReg))
+ MBB->addLiveIn(KnownReg);
+
+ LLVM_DEBUG(dbgs() << "Clearing kill flags.\n\tFirstUse: " << *FirstUse
+ << "\tLastChange: ");
+ if (LastChange == MBB->end())
+ LLVM_DEBUG(dbgs() << "<end>\n");
+ else
+ LLVM_DEBUG(dbgs() << *LastChange);
+ // Clear kills in the range where changes were made. This is conservative,
+ // but should be okay since kill markers are being phased out.
+ for (MachineInstr &MMI : make_range(FirstUse, PredMBB->end()))
+ MMI.clearKillInfo();
+ for (MachineInstr &MMI : make_range(MBB->begin(), LastChange))
+ MMI.clearKillInfo();
+
+ return true;
+}
+
+bool ARMRedundantCopyEliminationImpl::run(MachineFunction &MF) {
+ if (MF.getFunction().hasOptNone())
+ return false;
+ if (!MF.getProperties().hasNoVRegs())
+ return false;
+
+ TII = MF.getSubtarget().getInstrInfo();
+ TRI = MF.getSubtarget().getRegisterInfo();
+ MRI = &MF.getRegInfo();
+
+ // Resize the clobbered and used register unit trackers. We do this once per
+ // function.
+ DomBBClobberedRegs.init(*TRI);
+ DomBBUsedRegs.init(*TRI);
+ OptBBClobberedRegs.init(*TRI);
+ OptBBUsedRegs.init(*TRI);
+
+ bool Changed = false;
+ for (MachineBasicBlock &MBB : MF)
+ Changed |= optimizeBlock(&MBB);
+ return Changed;
+}
+
+bool ARMRedundantCopyElimination::runOnMachineFunction(MachineFunction &MF) {
+ if (skipFunction(MF.getFunction()))
+ return false;
+ return ARMRedundantCopyEliminationImpl().run(MF);
+}
+
+PreservedAnalyses
+ARMRedundantCopyEliminationPass::run(MachineFunction &MF,
+ MachineFunctionAnalysisManager &MFAM) {
+ if (MF.getFunction().hasOptNone())
+ return PreservedAnalyses::all();
+ if (!MF.getProperties().hasNoVRegs())
+ return PreservedAnalyses::all();
+
+ const bool Changed = ARMRedundantCopyEliminationImpl().run(MF);
+ if (!Changed)
+ return PreservedAnalyses::all();
+ PreservedAnalyses PA = getMachineFunctionPassPreservedAnalyses();
+ PA.preserveSet<CFGAnalyses>();
+ return PA;
+}
+
+FunctionPass *llvm::createARMRedundantCopyEliminationPass() {
+ return new ARMRedundantCopyElimination();
+}
diff --git a/llvm/lib/Target/ARM/ARMTargetMachine.cpp b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
index ad6ceacb9933e..e1be789ffc876 100644
--- a/llvm/lib/Target/ARM/ARMTargetMachine.cpp
+++ b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
@@ -75,6 +75,10 @@ EnableARMLoadStoreOpt("arm-load-store-opt", cl::Hidden,
cl::desc("Enable ARM load/store optimization pass"),
cl::init(true));
+static cl::opt<bool> EnableRedundantCopyElimination(
+ "arm-copy-elim", cl::Hidden,
+ cl::desc("Enable ARM redundant copy elimination pass"), cl::init(true));
+
// FIXME: Unify control over GlobalMerge.
static cl::opt<cl::boolOrDefault>
EnableGlobalMerge("arm-global-merge", cl::Hidden,
@@ -113,6 +117,7 @@ extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeARMTarget() {
initializeARMFixCortexA57AES1742098Pass(Registry);
initializeARMDAGToDAGISelLegacyPass(Registry);
initializeMachineKCFILegacyPass(Registry);
+ initializeARMRedundantCopyEliminationPass(Registry);
}
static std::unique_ptr<TargetLoweringObjectFile> createTLOF(const Triple &TT) {
@@ -312,6 +317,7 @@ class ARMPassConfig : public TargetPassConfig {
bool addRegBankSelect() override;
bool addGlobalInstructionSelect() override;
void addPreRegAlloc() override;
+ void addPostRegAlloc() override;
void addPreSched2() override;
void addPreEmitPass() override;
void addPreEmitPass2() override;
@@ -477,6 +483,11 @@ void ARMPassConfig::addPreRegAlloc() {
}
}
+void ARMPassConfig::addPostRegAlloc() {
+ if (getOptLevel() != CodeGenOptLevel::None && EnableRedundantCopyElimination)
+ addPass(createARMRedundantCopyEliminationPass());
+}
+
void ARMPassConfig::addPreSched2() {
if (getOptLevel() != CodeGenOptLevel::None) {
if (EnableARMLoadStoreOpt)
diff --git a/llvm/lib/Target/ARM/CMakeLists.txt b/llvm/lib/Target/ARM/CMakeLists.txt
index d99368e1d3b2b..4596f2bfe54a7 100644
--- a/llvm/lib/Target/ARM/CMakeLists.txt
+++ b/llvm/lib/Target/ARM/CMakeLists.txt
@@ -48,6 +48,7 @@ add_llvm_target(ARMCodeGen
ARMMCInstLower.cpp
ARMMachineFunctionInfo.cpp
ARMMacroFusion.cpp
+ ARMRedundantCopyElimination.cpp
ARMRegisterInfo.cpp
ARMOptimizeBarriersPass.cpp
ARMRegisterBankInfo.cpp
diff --git a/llvm/test/CodeGen/ARM/O3-pipeline.ll b/llvm/test/CodeGen/ARM/O3-pipeline.ll
index 9f4d70531a3f7..ad3e5a1d0b7c2 100644
--- a/llvm/test/CodeGen/ARM/O3-pipeline.ll
+++ b/llvm/test/CodeGen/ARM/O3-pipeline.ll
@@ -142,6 +142,7 @@
; CHECK-NEXT: Stack Slot Coloring
; CHECK-NEXT: Machine Copy Propagation Pass
; CHECK-NEXT: Machine Loop Invariant Code Motion
+; CHECK-NEXT: ARM Redundant Copy Elimination
; CHECK-NEXT: Remove Redundant DEBUG_VALUE analysis
; CHECK-NEXT: Fixup Statepoint Caller Saved
; CHECK-NEXT: PostRA Machine Sink
diff --git a/llvm/test/CodeGen/ARM/arm-shrink-wrapping.ll b/llvm/test/CodeGen/ARM/arm-shrink-wrapping.ll
index 6bcffa29678eb..dd9f22d94e0a4 100644
--- a/llvm/test/CodeGen/ARM/arm-shrink-wrapping.ll
+++ b/llvm/test/CodeGen/ARM/arm-shrink-wrapping.ll
@@ -1029,7 +1029,6 @@ define i32 @inlineAsm(i32 %cond, i32 %N) "frame-pointer"="all" {
; ARM-ENABLE-NEXT: @ InlineAsm End
; ARM-ENABLE-NEXT: bne LBB6_2
; ARM-ENABLE-NEXT: @ %bb.3: @ %for.exit
-; ARM-ENABLE-NEXT: mov r0, #0
; ARM-ENABLE-NEXT: @ InlineAsm Start
; ARM-ENABLE-NEXT: nop
; ARM-ENABLE-NEXT: @ InlineAsm End
@@ -1059,7 +1058,6 @@ define i32 @inlineAsm(i32 %cond, i32 %N) "frame-pointer"="all" {
; ARM-DISABLE-NEXT: @ InlineAsm End
; ARM-DISABLE-NEXT: bne LBB6_2
; ARM-DISABLE-NEXT: @ %bb.3: @ %for.exit
-; ARM-DISABLE-NEXT: mov r0, #0
; ARM-DISABLE-NEXT: @ InlineAsm Start
; ARM-DISABLE-NEXT: nop
; ARM-DISABLE-NEXT: @ InlineAsm End
@@ -1088,7 +1086,6 @@ define i32 @inlineAsm(i32 %cond, i32 %N) "frame-pointer"="all" {
; THUMB-ENABLE-NEXT: @ InlineAsm End
; THUMB-ENABLE-NEXT: bne LBB6_2
; THUMB-ENABLE-NEXT: @ %bb.3: @ %for.exit
-; THUMB-ENABLE-NEXT: movs r0, #0
; THUMB-ENABLE-NEXT: @ InlineAsm Start
; THUMB-ENABLE-NEXT: nop
; THUMB-ENABLE-NEXT: @ InlineAsm End
@@ -1117,7 +1114,6 @@ define i32 @inlineAsm(i32 %cond, i32 %N) "frame-pointer"="all" {
; THUMB-DISABLE-NEXT: @ InlineAsm End
; THUMB-DISABLE-NEXT: bne LBB6_2
; THUMB-DISABLE-NEXT: @ %bb.3: @ %for.exit
-; THUMB-DISABLE-NEXT: movs r0, #0
; THUMB-DISABLE-NEXT: @ InlineAsm Start
; THUMB-DISABLE-NEXT: nop
; THUMB-DISABLE-NEXT: @ InlineAsm End
diff --git a/llvm/test/CodeGen/ARM/code-placement.ll b/llvm/test/CodeGen/ARM/code-placement.ll
index c8bb10cd104cf..dd6752a878f49 100644
--- a/llvm/test/CodeGen/ARM/code-placement.ll
+++ b/llvm/test/CodeGen/ARM/code-placement.ll
@@ -11,8 +11,8 @@ entry:
br i1 %0, label %bb2, label %bb
bb:
-; CHECK: LBB0_[[LABEL:[0-9]]]:
-; CHECK: bne LBB0_[[LABEL]]
+; CHECK: @ =>This Inner Loop Header: Depth=1
+; CHECK: bne LBB0_[[LABEL:[0-9]+]]
; CHECK-NOT: b LBB0_[[LABEL]]
; CHECK: bx lr
%list_addr.05 = phi ptr [ %2, %bb ], [ %list, %entry ]
diff --git a/llvm/test/CodeGen/ARM/machine-copy-remove.mir b/llvm/test/CodeGen/ARM/machine-copy-remove.mir
new file mode 100644
index 0000000000000..baed2677b3c6e
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/machine-copy-remove.mir
@@ -0,0 +1,186 @@
+# RUN: llc -mtriple=thumbv7-none-eabi -run-pass=arm-copyelim %s -verify-machineinstrs -o - | FileCheck %s
+# RUN: llc -mtriple=thumbv7-none-eabi -passes=arm-copyelim %s -verify-machineinstrs -o - | FileCheck %s
+
+# CHECK-LABEL: name: cbz_zero
+# CHECK: tCBZ
+# CHECK: bb.1:
+# CHECK-NOT: t2MOVi
+---
+name: cbz_zero
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $r0, $r1
+ tCBZ $r0, %bb.1
+ t2B %bb.2, 14, $noreg
+
+ bb.1:
+ liveins: $r1
+ $r0 = t2MOVi 0, 14, $noreg, $noreg
+ STRi12 killed $r0, $r1, 0, 14, $noreg
+
+ bb.2:
+ tBX_RET 14, $noreg
+
+...
+# CHECK-LABEL: name: cmp_imm
+# CHECK: t2CMPri
+# CHECK: bb.1:
+# CHECK-NOT: t2MOVi
+---
+name: cmp_imm
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $r0, $r1
+ t2CMPri $r0, 1, 14, $noreg, implicit-def $cpsr
+ t2Bcc %bb.1, 0, killed $cpsr
+ t2B %bb.2, 14, $noreg
+
+ bb.1:
+ liveins: $r1
+ $r0 = t2MOVi 1, 14, $noreg, $noreg
+ STRi12 killed $r0, $r1, 0, 14, $noreg
+
+ bb.2:
+ tBX_RET 14, $noreg
+
+...
+# CHECK-LABEL: name: subs_zero
+# CHECK: t2SUBrr
+# CHECK: bb.1:
+# CHECK-NOT: t2MOVi
+---
+name: subs_zero
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $r0, $r1, $r2
+ $r0 = t2SUBrr $r1, $r2, 14, $noreg, def $cpsr
+ STRi12 killed $r0, $r1, 0, 14, $noreg
+ t2Bcc %bb.2, 1, killed $cpsr
+ t2B %bb.1, 14, $noreg
+
+ bb.1:
+ liveins: $r1
+ $r0 = t2MOVi 0, 14, $noreg, $noreg
+ STRi12 killed $r0, $r1, 4, 14, $noreg
+
+ bb.2:
+ tBX_RET 14, $noreg
+
+...
+# CHECK-LABEL: name: cmp_rr
+# CHECK: t2CMPrr
+# CHECK: bb.1:
+# CHECK-NOT: t2MOVr
+---
+name: cmp_rr
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $r0, $r1, $r2
+ t2CMPrr $r0, $r1, 14, $noreg, implicit-def $cpsr
+ t2Bcc %bb.1, 0, killed $cpsr
+ t2B %bb.2, 14, $noreg
+
+ bb.1:
+ liveins: $r1, $r2
+ $r0 = t2MOVr $r1, 14, $noreg, $noreg
+ STRi12 killed $r0, $r2, 0, 14, $noreg
+
+ bb.2:
+ tBX_RET 14, $noreg
+
+...
+# CHECK-LABEL: name: cmp_copy_prop
+# CHECK: t2CMPrr
+# CHECK: bb.1:
+# CHECK-NOT: t2MOVr
+---
+name: cmp_copy_prop
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $r0, $r1, $r2
+ $r2 = COPY $r0
+ t2CMPrr $r0, $r1, 14, $noreg, implicit-def $cpsr
+ t2Bcc %bb.1, 0, killed $cpsr
+ t2B %bb.2, 14, $noreg
+
+ bb.1:
+ liveins: $r1, $r2
+ $r0 = t2MOVr $r2, 14, $noreg, $noreg
+ STRi12 killed $r0, $r2, 0, 14, $noreg
+
+ bb.2:
+ tBX_RET 14, $noreg
+
+...
+# CHECK-LABEL: name: tmovi_dead_cpsr
+# CHECK: tCBZ
+# CHECK: bb.1:
+# CHECK-NOT: tMOVi8
+---
+name: tmovi_dead_cpsr
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $r0, $r1
+ tCBZ $r0, %bb.1
+ t2B %bb.2, 14, $noreg
+
+ bb.1:
+ liveins: $r1
+ $r0, dead $cpsr = tMOVi8 0, 14, $noreg
+ STRi12 killed $r0, $r1, 0, 14, $noreg
+
+ bb.2:
+ tBX_RET 14, $noreg
+
+...
+# CHECK-LABEL: name: tmovi_live_cpsr
+# CHECK: tCBZ
+# CHECK: bb.1:
+# CHECK: tMOVi8
+# CHECK: t2Bcc
+---
+name: tmovi_live_cpsr
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $r0, $r1, $r2
+ tCBZ $r0, %bb.1
+ t2B %bb.2, 14, $noreg
+
+ bb.1:
+ liveins: $r1, $r2
+ $r0, $cpsr = tMOVi8 0, 14, $noreg
+ STRi12 killed $r0, $r1, 0, 14, $noreg
+ t2Bcc %bb.2, 1, killed $cpsr
+
+ bb.2:
+ tBX_RET 14, $noreg
+
+...
+# CHECK-LABEL: name: cmp_reg_predicated
+# CHECK: t2CMPrr
+# CHECK: bb.1:
+# CHECK: t2MOVr
+---
+name: cmp_reg_predicated
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $r0, $r1, $r2, $cpsr
+ t2CMPrr $r0, $r1, 1, $cpsr, implicit-def $cpsr
+ t2Bcc %bb.1, 0, killed $cpsr
+ t2B %bb.2, 14, $noreg
+
+ bb.1:
+ liveins: $r1, $r2
+ $r0 = t2MOVr $r1, 14, $noreg, $noreg
+ STRi12 killed $r0, $r2, 0, 14, $noreg
+
+ bb.2:
+ tBX_RET 14, $noreg
diff --git a/llvm/test/CodeGen/ARM/v8m-tail-call.ll b/llvm/test/CodeGen/ARM/v8m-tail-call.ll
index 4bad4ba0daf36..efef3d3c53aae 100644
--- a/llvm/test/CodeGen/ARM/v8m-tail-call.ll
+++ b/llvm/test/CodeGen/ARM/v8m-tail-call.ll
@@ -61,7 +61,6 @@ define hidden i32 @f2(i32, i32, i32, i32, i32) {
; CHECK-NEXT: add sp, #4
; CHECK-NEXT: b h2
; CHECK-NEXT: .LBB2_2:
-; CHECK-NEXT: movs r0, #0
; CHECK-NEXT: mvns r0, r0
; CHECK-NEXT: add sp, #4
; CHECK-NEXT: pop {r4, r5, r6, r7, pc}
diff --git a/llvm/test/CodeGen/Thumb/thumb-shrink-wrapping.ll b/llvm/test/CodeGen/Thumb/thumb-shrink-wrapping.ll
index 27720a4200b11..ae0c48dfec688 100644
--- a/llvm/test/CodeGen/Thumb/thumb-shrink-wrapping.ll
+++ b/llvm/test/CodeGen/Thumb/thumb-shrink-wrapping.ll
@@ -931,7 +931,6 @@ define i32 @inlineAsm(i32 %cond, i32 %N) {
; ENABLE-V4T-NEXT: @ InlineAsm Start
; ENABLE-V4T-NEXT: mov r8, r8
; ENABLE-V4T-NEXT: @ InlineAsm End
-; ENABLE-V4T-NEXT: movs r0, #0
; ENABLE-V4T-NEXT: pop {r4}
; ENABLE-V4T-NEXT: pop {r1}
; ENABLE-V4T-NEXT: bx r1
@@ -963,7 +962,6 @@ define i32 @inlineAsm(i32 %cond, i32 %N) {
; ENABLE-V5T-NEXT: @ InlineAsm Start
; ENABLE-V5T-NEXT: mov r8, r8
; ENABLE-V5T-NEXT: @ InlineAsm End
-; ENABLE-V5T-NEXT: movs r0, #0
; ENABLE-V5T-NEXT: pop {r4, pc}
; ENABLE-V5T-NEXT: LBB7_4: @ %if.else
; ENABLE-V5T-NEXT: lsls r0, r1, #1
@@ -994,7 +992,6 @@ define i32 @inlineAsm(i32 %cond, i32 %N) {
; DISABLE-V4T-NEXT: @ InlineAsm Start
; DISABLE-V4T-NEXT: mov r8, r8
; DISABLE-V4T-NEXT: @ InlineAsm End
-; DISABLE-V4T-NEXT: movs r0, #0
; DISABLE-V4T-NEXT: b LBB7_5
; DISABLE-V4T-NEXT: LBB7_4: @ %if.else
; DISABLE-V4T-NEXT: lsls r0, r1, #1
@@ -1027,7 +1024,6 @@ define i32 @inlineAsm(i32 %cond, i32 %N) {
; DISABLE-V5T-NEXT: @ InlineAsm Start
; DISABLE-V5T-NEXT: mov r8, r8
; DISABLE-V5T-NEXT: @ InlineAsm End
-; DISABLE-V5T-NEXT: movs r0, #0
; DISABLE-V5T-NEXT: pop {r4, pc}
; DISABLE-V5T-NEXT: LBB7_4: @ %if.else
; DISABLE-V5T-NEXT: lsls r0, r1, #1
diff --git a/llvm/test/CodeGen/Thumb2/LowOverheadLoops/reductions.ll b/llvm/test/CodeGen/Thumb2/LowOverheadLoops/reductions.ll
index c418038b751d7..42a77037b1aa8 100644
--- a/llvm/test/CodeGen/Thumb2/LowOverheadLoops/reductions.ll
+++ b/llvm/test/CodeGen/Thumb2/LowOverheadLoops/reductions.ll
@@ -571,7 +571,6 @@ define dso_local arm_aapcs_vfpcc void @two_reductions_mul_add_v8i16(ptr nocaptur
; CHECK-NEXT: vaddv.u16 r2, q0
; CHECK-NEXT: b .LBB7_5
; CHECK-NEXT: .LBB7_4:
-; CHECK-NEXT: movs r2, #0
; CHECK-NEXT: movs r4, #0
; CHECK-NEXT: .LBB7_5: @ %for.cond.cleanup
; CHECK-NEXT: strb r2, [r0]
diff --git a/llvm/test/CodeGen/Thumb2/mve-blockplacement.ll b/llvm/test/CodeGen/Thumb2/mve-blockplacement.ll
index 706a7c34c3df5..1b9199f365d50 100644
--- a/llvm/test/CodeGen/Thumb2/mve-blockplacement.ll
+++ b/llvm/test/CodeGen/Thumb2/mve-blockplacement.ll
@@ -69,9 +69,8 @@ define i32 @test(i8 zeroext %var_2, i16 signext %var_15, ptr %arr_60) {
; CHECK-NEXT: ldrb r6, [r3], #2
; CHECK-NEXT: adds r4, #8
; CHECK-NEXT: cmp r6, #0
-; CHECK-NEXT: ite ne
+; CHECK-NEXT: it ne
; CHECK-NEXT: sxthne r6, r1
-; CHECK-NEXT: moveq r6, #0
; CHECK-NEXT: cmp r6, #0
; CHECK-NEXT: cset r6, ne
; CHECK-NEXT: strb r6, [r5]
@@ -92,9 +91,8 @@ define i32 @test(i8 zeroext %var_2, i16 signext %var_15, ptr %arr_60) {
; CHECK-NEXT: ldrb r6, [r3, #-1]
; CHECK-NEXT: add.w r2, r2, #792
; CHECK-NEXT: cmp r6, #0
-; CHECK-NEXT: ite ne
+; CHECK-NEXT: it ne
; CHECK-NEXT: sxthne r6, r1
-; CHECK-NEXT: moveq r6, #0
; CHECK-NEXT: cmp r6, #0
; CHECK-NEXT: str r7, [r4]
; CHECK-NEXT: cset r6, ne
@@ -102,9 +100,8 @@ define i32 @test(i8 zeroext %var_2, i16 signext %var_15, ptr %arr_60) {
; CHECK-NEXT: strb r6, [r5]
; CHECK-NEXT: ldrb r6, [r3], #2
; CHECK-NEXT: cmp r6, #0
-; CHECK-NEXT: ite ne
+; CHECK-NEXT: it ne
; CHECK-NEXT: sxthne r6, r1
-; CHECK-NEXT: moveq r6, #0
; CHECK-NEXT: cmp r6, #0
; CHECK-NEXT: cset r6, ne
; CHECK-NEXT: strb r6, [r5]
@@ -124,9 +121,8 @@ define i32 @test(i8 zeroext %var_2, i16 signext %var_15, ptr %arr_60) {
; CHECK-NEXT: ldrb r4, [r3, #-1]
; CHECK-NEXT: add.w r2, r2, #792
; CHECK-NEXT: cmp r4, #0
-; CHECK-NEXT: ite ne
+; CHECK-NEXT: it ne
; CHECK-NEXT: sxthne r4, r1
-; CHECK-NEXT: moveq r4, #0
; CHECK-NEXT: cmp r4, #0
; CHECK-NEXT: str.w r6, [r11]
; CHECK-NEXT: cset r4, ne
@@ -134,9 +130,8 @@ define i32 @test(i8 zeroext %var_2, i16 signext %var_15, ptr %arr_60) {
; CHECK-NEXT: strb r4, [r5]
; CHECK-NEXT: ldrb r4, [r3], #2
; CHECK-NEXT: cmp r4, #0
-; CHECK-NEXT: ite ne
+; CHECK-NEXT: it ne
; CHECK-NEXT: sxthne r4, r1
-; CHECK-NEXT: moveq r4, #0
; CHECK-NEXT: cmp r4, #0
; CHECK-NEXT: cset r4, ne
; CHECK-NEXT: strb r4, [r5]
diff --git a/llvm/test/CodeGen/Thumb2/mve-memtp-branch.ll b/llvm/test/CodeGen/Thumb2/mve-memtp-branch.ll
index b5c9b903e1841..71689f649c49a 100644
--- a/llvm/test/CodeGen/Thumb2/mve-memtp-branch.ll
+++ b/llvm/test/CodeGen/Thumb2/mve-memtp-branch.ll
@@ -34,9 +34,8 @@ define i32 @a(i8 zeroext %b, ptr nocapture readonly %c, ptr nocapture readonly %
; CHECK-NEXT: @ Child Loop BB0_11 Depth 2
; CHECK-NEXT: ldr.w r0, [r2, r3, lsl #2]
; CHECK-NEXT: cmp r0, #0
-; CHECK-NEXT: ite ne
+; CHECK-NEXT: it ne
; CHECK-NEXT: ldrbne r0, [r1, r3]
-; CHECK-NEXT: moveq r0, #0
; CHECK-NEXT: mla r3, r3, r12, r5
; CHECK-NEXT: add r3, r0
; CHECK-NEXT: rsb.w r0, r0, #108
@@ -49,9 +48,8 @@ define i32 @a(i8 zeroext %b, ptr nocapture readonly %c, ptr nocapture readonly %
; CHECK-NEXT: @ in Loop: Header=BB0_3 Depth=1
; CHECK-NEXT: ldr r0, [r2, #4]
; CHECK-NEXT: cmp r0, #0
-; CHECK-NEXT: ite ne
+; CHECK-NEXT: it ne
; CHECK-NEXT: ldrbne r0, [r1, #1]
-; CHECK-NEXT: moveq r0, #0
; CHECK-NEXT: adds r3, r5, r0
; CHECK-NEXT: rsb.w r0, r0, #108
; CHECK-NEXT: adds r3, #19
@@ -64,9 +62,8 @@ define i32 @a(i8 zeroext %b, ptr nocapture readonly %c, ptr nocapture readonly %
; CHECK-NEXT: @ in Loop: Header=BB0_3 Depth=1
; CHECK-NEXT: ldr r0, [r2, #4]
; CHECK-NEXT: cmp r0, #0
-; CHECK-NEXT: ite ne
+; CHECK-NEXT: it ne
; CHECK-NEXT: ldrbne r0, [r1, #1]
-; CHECK-NEXT: moveq r0, #0
; CHECK-NEXT: adds r3, r5, r0
; CHECK-NEXT: rsb.w r0, r0, #108
; CHECK-NEXT: adds r3, #19
@@ -79,9 +76,8 @@ define i32 @a(i8 zeroext %b, ptr nocapture readonly %c, ptr nocapture readonly %
; CHECK-NEXT: @ in Loop: Header=BB0_3 Depth=1
; CHECK-NEXT: ldr r0, [r2, #4]
; CHECK-NEXT: cmp r0, #0
-; CHECK-NEXT: ite ne
+; CHECK-NEXT: it ne
; CHECK-NEXT: ldrbne r0, [r1, #1]
-; CHECK-NEXT: moveq r0, #0
; CHECK-NEXT: adds r3, r5, r0
; CHECK-NEXT: rsb.w r0, r0, #108
; CHECK-NEXT: add.w r4, r0, #15
@@ -109,9 +105,8 @@ define i32 @a(i8 zeroext %b, ptr nocapture readonly %c, ptr nocapture readonly %
; CHECK-NEXT: @ in Loop: Header=BB0_14 Depth=1
; CHECK-NEXT: ldr r3, [r2, #4]
; CHECK-NEXT: cmp r3, #0
-; CHECK-NEXT: ite ne
+; CHECK-NEXT: it ne
; CHECK-NEXT: ldrbne r3, [r1, #1]
-; CHECK-NEXT: moveq r3, #0
; CHECK-NEXT: add.w r5, r12, r3
; CHECK-NEXT: rsb.w r3, r3, #108
; CHECK-NEXT: add.w r4, r5, #19
@@ -129,9 +124,8 @@ define i32 @a(i8 zeroext %b, ptr nocapture readonly %c, ptr nocapture readonly %
; CHECK-NEXT: @ in Loop: Header=BB0_14 Depth=1
; CHECK-NEXT: ldr r3, [r2, #4]
; CHECK-NEXT: cmp r3, #0
-; CHECK-NEXT: ite ne
+; CHECK-NEXT: it ne
; CHECK-NEXT: ldrbne r3, [r1, #1]
-; CHECK-NEXT: moveq r3, #0
; CHECK-NEXT: add.w r5, r12, r3
; CHECK-NEXT: rsb.w r3, r3, #108
; CHECK-NEXT: add.w r4, r5, #19
@@ -148,9 +142,8 @@ define i32 @a(i8 zeroext %b, ptr nocapture readonly %c, ptr nocapture readonly %
; CHECK-NEXT: @ in Loop: Header=BB0_14 Depth=1
; CHECK-NEXT: ldr r3, [r2, #4]
; CHECK-NEXT: cmp r3, #0
-; CHECK-NEXT: ite ne
+; CHECK-NEXT: it ne
; CHECK-NEXT: ldrbne r3, [r1, #1]
-; CHECK-NEXT: moveq r3, #0
; CHECK-NEXT: add.w r5, r12, r3
; CHECK-NEXT: rsb.w r3, r3, #108
; CHECK-NEXT: add.w r4, r5, #19
@@ -167,9 +160,8 @@ define i32 @a(i8 zeroext %b, ptr nocapture readonly %c, ptr nocapture readonly %
; CHECK-NEXT: @ in Loop: Header=BB0_14 Depth=1
; CHECK-NEXT: ldr r3, [r2, #4]
; CHECK-NEXT: cmp r3, #0
-; CHECK-NEXT: ite ne
+; CHECK-NEXT: it ne
; CHECK-NEXT: ldrbne r3, [r1, #1]
-; CHECK-NEXT: moveq r3, #0
; CHECK-NEXT: add.w r5, r12, r3
; CHECK-NEXT: rsb.w r3, r3, #108
; CHECK-NEXT: add.w r4, r5, #19
diff --git a/llvm/test/CodeGen/Thumb2/outlined-fn-may-clobber-lr-in-caller.ll b/llvm/test/CodeGen/Thumb2/outlined-fn-may-clobber-lr-in-caller.ll
index 1dbb21f40a761..b181fbc6d0fc3 100644
--- a/llvm/test/CodeGen/Thumb2/outlined-fn-may-clobber-lr-in-caller.ll
+++ b/llvm/test/CodeGen/Thumb2/outlined-fn-may-clobber-lr-in-caller.ll
@@ -20,7 +20,6 @@ define void @test(ptr nocapture noundef writeonly %arg, i32 noundef %arg1, i8 no
; CHECK-NEXT: cmp r1, #1
; CHECK-NEXT: bne .LBB0_5
; CHECK-NEXT: @ %bb.2: @ %bb4
-; CHECK-NEXT: movs r1, #1
; CHECK-NEXT: strb.w r1, [r0, #36]
; CHECK-NEXT: movs r1, #30
; CHECK-NEXT: strb.w r1, [r0, #34]
More information about the llvm-commits
mailing list