[llvm] [ARM] Port Redundant Copy Elimination to ARM (PR #203706)

via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 13 08:18:34 PDT 2026


https://github.com/mike-goutokuji updated https://github.com/llvm/llvm-project/pull/203706

>From b879284384ac07d87762ae3108f1e62d02290e61 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 |  15 +-
 llvm/lib/Target/ARM/ARMPassRegistry.def       |   1 +
 .../ARM/ARMRedundantCopyElimination.cpp       | 615 ++++++++++++++++++
 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, 853 insertions(+), 40 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..d19e84fe4abcf 100644
--- a/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp
+++ b/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp
@@ -1861,6 +1861,19 @@ bool ARMConstantIslands::optimizeThumb2Instructions() {
   return MadeChange;
 }
 
+static bool isCPSRDeadOnCondBranchExit(const MachineInstr &Br) {
+  if (Br.killsRegister(ARM::CPSR, /*TRI=*/nullptr))
+    return true;
+
+  // Kill markers may have been cleared (e.g. by arm-copyelim).  The fold is
+  // safe when CPSR is not live into any successor.
+  const MachineBasicBlock *MBB = Br.getParent();
+  for (const MachineBasicBlock *Succ : MBB->successors()) {
+    if (Succ->isLiveIn(ARM::CPSR))
+      return false;
+  }
+  return true;
+}
 
 bool ARMConstantIslands::optimizeThumb2Branches() {
 
@@ -1910,7 +1923,7 @@ 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))
+    if (!isCPSRDeadOnCondBranchExit(*Br.MI))
       return false;
 
     Register PredReg;
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..b045b764a0cdc
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMRedundantCopyElimination.cpp
@@ -0,0 +1,615 @@
+//=- 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.
+// 2. If a CPSR-setting instruction defines a GPR, we can remove a redundant
+//    zero move on the appropriate conditional path.
+// 3. If the CPSR-setting instruction is a comparison against a constant, we can
+//    remove a redundant move immediate on the appropriate path.
+// 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.
+//
+// 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.");
+
+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) {}
+};
+
+namespace {
+class ARMRedundantCopyEliminationImpl {
+public:
+  bool run(MachineFunction &MF);
+
+private:
+  const MachineRegisterInfo *MRI;
+  const TargetRegisterInfo *TRI;
+  const TargetInstrInfo *TII;
+
+  LiveRegUnits DomBBClobberedRegs, DomBBUsedRegs;
+  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", 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));
+  }
+}
+
+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;
+
+  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;
+}
+
+bool ARMRedundantCopyEliminationImpl::knownRegValInBlock(
+    MachineInstr &CondBr, MachineBasicBlock *MBB,
+    SmallVectorImpl<RegImm> &KnownRegs,
+    SmallVectorImpl<RegEqual> &KnownEqualRegs,
+    MachineBasicBlock::iterator &FirstUse) {
+  if (!isCondBranchOpcode(CondBr.getOpcode()))
+    return false;
+
+  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;
+
+  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())) {
+    MCPhysReg SrcReg;
+    int32_t KnownImm;
+    if (isCompareImm(PredI, SrcReg, KnownImm)) {
+      Register PredReg;
+      if (getInstrPredicate(PredI, PredReg) != ARMCC::AL)
+        return false;
+      bool Res = false;
+      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 (DomBBClobberedRegs.available(Rn) &&
+          DomBBClobberedRegs.available(Rm)) {
+        FirstUse = PredI;
+        KnownEqualRegs.push_back(RegEqual(Rn, Rm));
+        return true;
+      }
+      return false;
+    }
+
+    if (isFlagSettingAluWithDest(PredI)) {
+      Register PredReg;
+      if (getInstrPredicate(PredI, PredReg) != ARMCC::AL)
+        return false;
+      MCPhysReg DstReg = PredI.getOperand(0).getReg();
+      if (!DomBBClobberedRegs.available(DstReg))
+        return false;
+      FirstUse = PredI;
+      KnownRegs.push_back(RegImm(DstReg, 0));
+      return true;
+    }
+
+    if (PredI.definesRegister(ARM::CPSR, /*TRI=*/nullptr))
+      return false;
+
+    LiveRegUnits::accumulateUsedDefed(PredI, DomBBClobberedRegs, DomBBUsedRegs,
+                                      TRI);
+  }
+  return false;
+}
+
+bool ARMRedundantCopyEliminationImpl::optimizeBlock(MachineBasicBlock *MBB) {
+  if (MBB->pred_size() != 1)
+    return false;
+
+  MachineBasicBlock *PredMBB = *MBB->pred_begin();
+  if (PredMBB->succ_size() != 2)
+    return false;
+
+  MachineBasicBlock::iterator FirstUse;
+  bool SeenFirstUse = false;
+  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;
+
+    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;
+  }
+
+  if (KnownRegs.empty() && KnownEqualRegs.empty())
+    return false;
+
+  OptBBClobberedRegs.clear();
+  OptBBUsedRegs.clear();
+
+  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 (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;
+        }
+      }
+      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;
+      }
+    }
+
+    if (PredI == PredMBB->begin())
+      break;
+
+    LiveRegUnits::accumulateUsedDefed(*PredI, OptBBClobberedRegs, OptBBUsedRegs,
+                                      TRI);
+    if (shouldStopPredScan(KnownRegs, KnownEqualRegs, OptBBClobberedRegs))
+      break;
+  }
+
+  if (KnownRegs.empty() && KnownEqualRegs.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();
+    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) {
+            int64_t ImmVal;
+            if (getMoveImmediateValue(*MI, ImmVal) && KnownReg.Imm == ImmVal)
+              ShouldRemove = true;
+          }
+
+          if (!ShouldRemove)
+            continue;
+
+          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;
+        }
+      }
+    }
+
+    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;
+      }
+    }
+
+    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;
+      }
+    }
+
+    if (KnownRegs.empty() && KnownEqualRegs.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: ");
+  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();
+
+  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