[llvm] [ARM] Port ConditionOptimizer to ARM (PR #151187)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 29 10:00:48 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-arm

Author: AZero13 (AZero13)

<details>
<summary>Changes</summary>

Following the same principles, we can optimize conditions to make it better for the CSE.

---

Patch is 126.20 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/151187.diff


7 Files Affected:

- (modified) llvm/lib/Target/ARM/ARM.h (+2) 
- (added) llvm/lib/Target/ARM/ARMConditionOptimizer.cpp (+514) 
- (modified) llvm/lib/Target/ARM/ARMTargetMachine.cpp (+12) 
- (modified) llvm/lib/Target/ARM/CMakeLists.txt (+1) 
- (modified) llvm/test/CodeGen/ARM/O3-pipeline.ll (+1) 
- (added) llvm/test/CodeGen/ARM/combine-comparisons-by-cse.ll (+3184) 
- (modified) llvm/test/CodeGen/ARM/fpclamptosat.ll (+6-6) 


``````````diff
diff --git a/llvm/lib/Target/ARM/ARM.h b/llvm/lib/Target/ARM/ARM.h
index 3847f4e966afe..dd5cfb7a8a062 100644
--- a/llvm/lib/Target/ARM/ARM.h
+++ b/llvm/lib/Target/ARM/ARM.h
@@ -35,6 +35,7 @@ Pass *createMVETailPredicationPass();
 FunctionPass *createARMLowOverheadLoopsPass();
 FunctionPass *createARMBlockPlacementPass();
 Pass *createARMParallelDSPPass();
+FunctionPass *createARMConditionOptimizerPass();
 FunctionPass *createARMISelDag(ARMBaseTargetMachine &TM,
                                CodeGenOptLevel OptLevel);
 FunctionPass *createA15SDOptimizerPass();
@@ -64,6 +65,7 @@ void LowerARMMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI,
 void initializeARMAsmPrinterPass(PassRegistry &);
 void initializeARMBlockPlacementPass(PassRegistry &);
 void initializeARMBranchTargetsPass(PassRegistry &);
+void initializeARMConditionOptimizerPass(PassRegistry &);
 void initializeARMConstantIslandsPass(PassRegistry &);
 void initializeARMDAGToDAGISelLegacyPass(PassRegistry &);
 void initializeARMExpandPseudoPass(PassRegistry &);
diff --git a/llvm/lib/Target/ARM/ARMConditionOptimizer.cpp b/llvm/lib/Target/ARM/ARMConditionOptimizer.cpp
new file mode 100644
index 0000000000000..461c392d46b0d
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMConditionOptimizer.cpp
@@ -0,0 +1,514 @@
+//=- ARMConditionOptimizer.cpp - Remove useless comparisons 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 tries to make consecutive compares of values use same operands to
+// allow CSE pass to remove duplicated instructions.  For this it analyzes
+// branches and adjusts comparisons with immediate values by converting:
+//  * GE -> GT
+//  * GT -> GE
+//  * LT -> LE
+//  * LE -> LT
+// and adjusting immediate values appropriately.  It basically corrects two
+// immediate values towards each other to make them equal.
+//
+// Consider the following example in C:
+//
+//   if ((a < 5 && ...) || (a > 5 && ...)) {
+//        ~~~~~             ~~~~~
+//          ^                 ^
+//          x                 y
+//
+// Here both "x" and "y" expressions compare "a" with "5".  When "x" evaluates
+// to "false", "y" can just check flags set by the first comparison.  As a
+// result of the canonicalization employed by
+// SelectionDAGBuilder::visitSwitchCase, DAGCombine, and other target-specific
+// code, assembly ends up in the form that is not CSE friendly:
+//
+//     ...
+//     cmp      r8, #4
+//     bgt     .LBB0_3
+//     ...
+//   .LBB0_3:
+//     cmp      r8, #6
+//     blt     .LBB0_6
+//     ...
+//
+// Same assembly after the pass:
+//
+//     ...
+//     cmp      r8, #5
+//     bge     .LBB0_3
+//     ...
+//   .LBB0_3:
+//     cmp      r8, #5     // <-- CSE pass removes this instruction
+//     ble     .LBB0_6
+//     ...
+//
+// Currently only CMP and CMN followed by branches are supported.
+//
+// TODO: maybe deal with predicated instructions
+//
+//===----------------------------------------------------------------------===//
+
+#include "ARM.h"
+#include "ARMSubtarget.h"
+#include "MCTargetDesc/ARMAddressingModes.h"
+#include "Utils/ARMBaseInfo.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DepthFirstIterator.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/CodeGen/MachineDominators.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/MachineOperand.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetInstrInfo.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cstdlib>
+#include <tuple>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "arm-condopt"
+
+STATISTIC(NumConditionsAdjusted, "Number of conditions adjusted");
+
+namespace {
+
+class ARMConditionOptimizer : public MachineFunctionPass {
+  const TargetInstrInfo *TII;
+  MachineDominatorTree *DomTree;
+  const MachineRegisterInfo *MRI;
+
+public:
+  // Stores immediate, compare instruction opcode and branch condition (in this
+  // order) of adjusted comparison.
+  using CmpInfo = std::tuple<int, unsigned, ARMCC::CondCodes>;
+
+  static char ID;
+
+  ARMConditionOptimizer() : MachineFunctionPass(ID) {}
+
+  void getAnalysisUsage(AnalysisUsage &AU) const override;
+  MachineInstr *findSuitableCompare(MachineBasicBlock *MBB);
+  CmpInfo adjustCmp(MachineInstr *CmpMI, ARMCC::CondCodes Cmp);
+  void modifyCmp(MachineInstr *CmpMI, const CmpInfo &Info);
+  bool adjustTo(MachineInstr *CmpMI, ARMCC::CondCodes Cmp, MachineInstr *To,
+                int ToImm);
+  bool runOnMachineFunction(MachineFunction &MF) override;
+
+  StringRef getPassName() const override { return "ARM Condition Optimizer"; }
+};
+
+} // end anonymous namespace
+
+char ARMConditionOptimizer::ID = 0;
+
+INITIALIZE_PASS_BEGIN(ARMConditionOptimizer, "ARM-condopt", "ARM CondOpt Pass",
+                      false, false)
+INITIALIZE_PASS_DEPENDENCY(MachineDominatorTreeWrapperPass)
+INITIALIZE_PASS_END(ARMConditionOptimizer, "ARM-condopt", "ARM CondOpt Pass",
+                    false, false)
+
+FunctionPass *llvm::createARMConditionOptimizerPass() {
+  return new ARMConditionOptimizer();
+}
+
+void ARMConditionOptimizer::getAnalysisUsage(AnalysisUsage &AU) const {
+  AU.addRequired<MachineDominatorTreeWrapperPass>();
+  AU.addPreserved<MachineDominatorTreeWrapperPass>();
+  MachineFunctionPass::getAnalysisUsage(AU);
+}
+
+// Finds compare instruction that corresponds to supported types of branching.
+// Returns the instruction or nullptr on failures or detecting unsupported
+// instructions.
+MachineInstr *
+ARMConditionOptimizer::findSuitableCompare(MachineBasicBlock *MBB) {
+  MachineBasicBlock::iterator Term = MBB->getFirstTerminator();
+  if (Term == MBB->end())
+    return nullptr;
+
+  // Accept ARM, Thumb, and Thumb2 conditional branches
+  if (Term->getOpcode() != ARM::Bcc && Term->getOpcode() != ARM::tBcc &&
+      Term->getOpcode() != ARM::t2Bcc)
+    return nullptr;
+
+  // Since we may modify cmp of this MBB, make sure NZCV does not live out.
+  for (auto *SuccBB : MBB->successors())
+    if (SuccBB->isLiveIn(ARM::CPSR))
+      return nullptr;
+
+  // Now find the instruction controlling the terminator.
+  for (MachineBasicBlock::iterator B = MBB->begin(), It = Term; It != B;) {
+    It = prev_nodbg(It, B);
+    MachineInstr &I = *It;
+    assert(!I.isTerminator() && "Spurious terminator");
+    // Check if there is any use of CPSR between CMP and Bcc.
+    if (I.readsRegister(ARM::CPSR, /*TRI=*/nullptr))
+      return nullptr;
+    switch (I.getOpcode()) {
+    // Thumb-1, Thumb-2, and ARM CMP instructions - immediate variants only
+    case ARM::tCMPi8:
+    case ARM::t2CMPri:
+    case ARM::t2CMNri:
+    case ARM::CMPri:
+    case ARM::CMNri:
+      break;
+    default:
+      return nullptr;
+    }
+
+    // Only handle unpredicated CMP/CMN instructions
+    // ARM and Thumb2 instructions can be predicated, Thumb-1 cannot.
+    if (I.getOpcode() != ARM::tCMPi8) {
+      int PIdx = I.findFirstPredOperandIdx();
+      if (PIdx != -1 && I.getOperand(PIdx).getImm() != (int64_t)ARMCC::AL) {
+        LLVM_DEBUG(dbgs() << "Skipping predicated instruction: " << I << '\n');
+        return nullptr;
+      }
+    }
+
+    // Check that the immediate operand is valid
+    if (!I.getOperand(1).isImm()) {
+      LLVM_DEBUG(dbgs() << "Immediate of cmp/cmn is symbolic, " << I << '\n');
+      return nullptr;
+    }
+    return &I;
+  }
+  LLVM_DEBUG(dbgs() << "Flags not defined in " << printMBBReference(*MBB)
+                    << '\n');
+  return nullptr;
+}
+
+// Changes opcode cmp <-> cmn considering register operand width.
+static int getComplementOpc(int Opc) {
+  switch (Opc) {
+  // ARM CMN/CMP immediate instructions
+  case ARM::CMNri:
+    return ARM::CMPri;
+  case ARM::CMPri:
+    return ARM::CMNri;
+  // Thumb CMP immediate instructions - NOTE: Thumb1 doesn't have CMN!
+  case ARM::tCMPi8:
+    return ARM::INSTRUCTION_LIST_END; // No complement for Thumb1
+  // Thumb2 CMN/CMP immediate instructions
+  case ARM::t2CMPri:
+    return ARM::t2CMNri;
+  case ARM::t2CMNri:
+    return ARM::t2CMPri;
+  default:
+    llvm_unreachable("Unexpected opcode");
+  }
+}
+
+// Changes form of comparison inclusive <-> exclusive.
+static ARMCC::CondCodes getAdjustedCmp(ARMCC::CondCodes Cmp) {
+  switch (Cmp) {
+  case ARMCC::GT:
+    return ARMCC::GE;
+  case ARMCC::GE:
+    return ARMCC::GT;
+  case ARMCC::LT:
+    return ARMCC::LE;
+  case ARMCC::LE:
+    return ARMCC::LT;
+  case ARMCC::HI:
+    return ARMCC::HS;
+  case ARMCC::HS:
+    return ARMCC::HI;
+  case ARMCC::LO:
+    return ARMCC::LS;
+  case ARMCC::LS:
+    return ARMCC::LO;
+  default:
+    llvm_unreachable("Unexpected condition code");
+  }
+}
+
+// Transforms GT -> GE, GE -> GT, LT -> LE, LE -> LT by updating comparison
+// operator and condition code.
+ARMConditionOptimizer::CmpInfo
+ARMConditionOptimizer::adjustCmp(MachineInstr *CmpMI, ARMCC::CondCodes Cmp) {
+  unsigned Opc = CmpMI->getOpcode();
+  unsigned OldOpc = Opc;
+
+  bool IsSigned = Cmp == ARMCC::GT || Cmp == ARMCC::GE || Cmp == ARMCC::LT ||
+                  Cmp == ARMCC::LE;
+
+  // CMN (compare with negative immediate) is an alias to ADDS (as
+  // "operand - negative" == "operand + positive")
+  bool Negative = (Opc == ARM::CMNri || Opc == ARM::t2CMNri);
+
+  int Correction = (Cmp == ARMCC::GT || Cmp == ARMCC::HI) ? 1 : -1;
+  // Negate Correction value for comparison with negative immediate (CMN).
+  if (Negative) {
+    Correction = -Correction;
+  }
+
+  const int OldImm = (int)CmpMI->getOperand(1).getImm();
+  const int NewImm = std::abs(OldImm + Correction);
+
+  // Handle cmn 1 -> cmp 0, transitions by adjusting compare instruction opcode.
+  if (OldImm == 1 && Negative && Correction == -1) {
+    // If we are adjusting from -1 to 0, we need to change the opcode.
+    Opc = getComplementOpc(Opc);
+  }
+
+  // Handle +0 -> -1 transitions by adjusting compare instruction opcode.
+  if (OldImm == 0 && Correction == -1) {
+    Opc = getComplementOpc(Opc);
+  }
+
+  // If we change opcodes, this means we did an unsigned wrap, so return the old
+  // cmp for unsigned comparisons.
+
+  // If we have an invalid value, return the old cmp
+  if (Opc == ARM::INSTRUCTION_LIST_END || (!IsSigned && Opc != OldOpc))
+    return CmpInfo(OldImm, OldOpc, Cmp);
+
+  return CmpInfo(NewImm, Opc, getAdjustedCmp(Cmp));
+}
+
+// Applies changes to comparison instruction suggested by adjustCmp().
+void ARMConditionOptimizer::modifyCmp(MachineInstr *CmpMI,
+                                      const CmpInfo &Info) {
+  int Imm;
+  unsigned Opc;
+  ARMCC::CondCodes Cmp;
+  std::tie(Imm, Opc, Cmp) = Info;
+  if (Imm == 0) {
+    if (Cmp == ARMCC::GE)
+      Cmp = ARMCC::PL;
+    if (Cmp == ARMCC::LT)
+      Cmp = ARMCC::MI;
+  }
+
+  MachineBasicBlock *const MBB = CmpMI->getParent();
+
+  // Build the new instruction with the correct format for the target opcode.
+  MachineInstrBuilder MIB = BuildMI(*MBB, CmpMI, CmpMI->getDebugLoc(),
+                                    TII->get(Opc))
+                                .add(CmpMI->getOperand(0)) // Rn
+                                .addImm(Imm);              // Immediate
+
+  // Add predicate operands for all CMP/CMN instructions.
+  // Even Thumb-1 CMP instructions have predicate operands.
+  MIB.add(predOps(ARMCC::AL));
+
+  CmpMI->eraseFromParent();
+
+  // The fact that this comparison was picked ensures that it's related to the
+  // first terminator instruction.
+  MachineInstr &BrMI = *MBB->getFirstTerminator();
+
+  // Change condition in branch instruction.
+  // Rebuild the branch instruction correctly for all subtargets.
+  unsigned BranchOpc = BrMI.getOpcode();
+  MachineInstrBuilder BranchMIB =
+      BuildMI(*MBB, BrMI, BrMI.getDebugLoc(), TII->get(BranchOpc))
+          .add(BrMI.getOperand(0)); // Target MBB
+
+  // Add the new condition code.
+  BranchMIB.addImm(Cmp);
+
+  // Add the predicate register operand for all branch types.
+  // All ARM/Thumb/Thumb2 conditional branches need this.
+  BranchMIB.add(BrMI.getOperand(2));
+
+  BrMI.eraseFromParent();
+
+  ++NumConditionsAdjusted;
+}
+
+// Parse a condition code returned by analyzeBranch, and compute the CondCode
+// corresponding to TBB.
+// Returns true if parsing was successful, otherwise false is returned.
+static bool parseCond(ArrayRef<MachineOperand> Cond, ARMCC::CondCodes &CC) {
+  // A normal br.cond simply has the condition code (size == 2 for ARM/Thumb)
+  if (Cond.size() == 2 && Cond[0].isImm()) {
+    CC = (ARMCC::CondCodes)(int)Cond[0].getImm();
+    return true;
+  }
+  return false;
+}
+
+// Adjusts one cmp instruction to another one if result of adjustment will allow
+// CSE.  Returns true if compare instruction was changed, otherwise false is
+// returned.
+bool ARMConditionOptimizer::adjustTo(MachineInstr *CmpMI, ARMCC::CondCodes Cmp,
+                                     MachineInstr *To, int ToImm) {
+  CmpInfo Info = adjustCmp(CmpMI, Cmp);
+  if (std::get<0>(Info) == ToImm && std::get<1>(Info) == To->getOpcode()) {
+    modifyCmp(CmpMI, Info);
+    return true;
+  }
+  return false;
+}
+
+static bool isGreaterThan(ARMCC::CondCodes Cmp) {
+  return Cmp == ARMCC::GT || Cmp == ARMCC::HI;
+}
+
+static bool isLessThan(ARMCC::CondCodes Cmp) {
+  return Cmp == ARMCC::LT || Cmp == ARMCC::LO;
+}
+
+bool ARMConditionOptimizer::runOnMachineFunction(MachineFunction &MF) {
+  LLVM_DEBUG(dbgs() << "********** ARM Conditional Compares **********\n"
+                    << "********** Function: " << MF.getName() << '\n');
+  if (skipFunction(MF.getFunction()))
+    return false;
+
+  TII = MF.getSubtarget<ARMSubtarget>().getInstrInfo();
+  DomTree = &getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
+  MRI = &MF.getRegInfo();
+
+  bool Changed = false;
+
+  // Visit blocks in dominator tree pre-order. The pre-order enables multiple
+  // cmp-conversions from the same head block.
+  // Note that updateDomTree() modifies the children of the DomTree node
+  // currently being visited. The df_iterator supports that; it doesn't look at
+  // child_begin() / child_end() until after a node has been visited.
+  for (MachineDomTreeNode *I : depth_first(DomTree)) {
+    MachineBasicBlock *HBB = I->getBlock();
+
+    SmallVector<MachineOperand, 4> HeadCond;
+    MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
+    if (TII->analyzeBranch(*HBB, TBB, FBB, HeadCond)) {
+      continue;
+    }
+
+    // Equivalence check is to skip loops.
+    if (!TBB || TBB == HBB) {
+      continue;
+    }
+
+    SmallVector<MachineOperand, 4> TrueCond;
+    MachineBasicBlock *TBB_TBB = nullptr, *TBB_FBB = nullptr;
+    if (TII->analyzeBranch(*TBB, TBB_TBB, TBB_FBB, TrueCond)) {
+      continue;
+    }
+
+    MachineInstr *HeadCmpMI = findSuitableCompare(HBB);
+    if (!HeadCmpMI) {
+      continue;
+    }
+
+    ARMCC::CondCodes HeadCmp;
+    if (HeadCond.empty() || !parseCond(HeadCond, HeadCmp)) {
+      continue;
+    }
+
+    ARMCC::CondCodes TrueCmp;
+    if (TrueCond.empty() || !parseCond(TrueCond, TrueCmp)) {
+      continue;
+    }
+
+    const int HeadImm = (int)HeadCmpMI->getOperand(1).getImm();
+
+    // Convert PL/MI to GE/LT for comparisons with 0.
+    if (HeadImm == 0) {
+      if (HeadCmp == ARMCC::PL)
+        HeadCmp = ARMCC::GE;
+      if (HeadCmp == ARMCC::MI)
+        HeadCmp = ARMCC::LT;
+    }
+
+    int HeadImmTrueValue = HeadImm;
+
+    unsigned HeadOpc = HeadCmpMI->getOpcode();
+    if (HeadOpc == ARM::CMNri || HeadOpc == ARM::t2CMNri)
+      HeadImmTrueValue = -HeadImmTrueValue;
+
+    // Try to find a suitable compare in TBB, but don't require it yet
+    MachineInstr *TrueCmpMI = findSuitableCompare(TBB);
+
+    // If we have a suitable compare in TBB, try the optimization
+    if (TrueCmpMI) {
+      const int TrueImm = (int)TrueCmpMI->getOperand(1).getImm();
+
+      int TrueImmTrueValue = TrueImm;
+
+      // Special Case 0.
+      if (TrueImm == 0) {
+        if (TrueCmp == ARMCC::PL)
+          TrueCmp = ARMCC::GE;
+        if (TrueCmp == ARMCC::MI)
+          TrueCmp = ARMCC::LT;
+      }
+
+      unsigned TrueOpc = TrueCmpMI->getOpcode();
+      if (TrueOpc == ARM::CMNri || TrueOpc == ARM::t2CMNri)
+        TrueImmTrueValue = -TrueImmTrueValue;
+
+      if (((isGreaterThan(HeadCmp) && isLessThan(TrueCmp)) ||
+           (isLessThan(HeadCmp) && isGreaterThan(TrueCmp))) &&
+          std::abs(TrueImmTrueValue - HeadImmTrueValue) == 2) {
+        // This branch transforms machine instructions that correspond to
+        //
+        // 1) (a > {TrueImm} && ...) || (a < {HeadImm} && ...)
+        // 2) (a < {TrueImm} && ...) || (a > {HeadImm} && ...)
+        //
+        // into
+        //
+        // 1) (a >= {NewImm} && ...) || (a <= {NewImm} && ...)
+        // 2) (a <= {NewImm} && ...) || (a >= {NewImm} && ...)
+
+        CmpInfo HeadCmpInfo = adjustCmp(HeadCmpMI, HeadCmp);
+        CmpInfo TrueCmpInfo = adjustCmp(TrueCmpMI, TrueCmp);
+        if (std::get<0>(HeadCmpInfo) == std::get<0>(TrueCmpInfo) &&
+            std::get<1>(HeadCmpInfo) == std::get<1>(TrueCmpInfo)) {
+          modifyCmp(HeadCmpMI, HeadCmpInfo);
+          modifyCmp(TrueCmpMI, TrueCmpInfo);
+          Changed = true;
+        }
+      } else if (((isGreaterThan(HeadCmp) && isGreaterThan(TrueCmp)) ||
+                  (isLessThan(HeadCmp) && isLessThan(TrueCmp))) &&
+                 std::abs(TrueImmTrueValue - HeadImmTrueValue) == 1) {
+        // This branch transforms machine instructions that correspond to
+        //
+        // 1) (a > {TrueImm} && ...) || (a > {HeadImm} && ...)
+        // 2) (a < {TrueImm} && ...) || (a < {HeadImm} && ...)
+        //
+        // into
+        //
+        // 1) (a <= {NewImm} && ...) || (a >  {NewImm} && ...)
+        // 2) (a <  {NewImm} && ...) || (a >= {NewImm} && ...)
+
+        // GT -> GE transformation increases immediate value, so picking the
+        // smaller one; LT -> LE decreases immediate value so invert the choice.
+        bool adjustHeadCond = (HeadImmTrueValue < TrueImmTrueValue);
+        if (isLessThan(HeadCmp)) {
+          adjustHeadCond = !adjustHeadCond;
+        }
+
+        if (adjustHeadCond) {
+          Changed |= adjustTo(HeadCmpMI, HeadCmp, TrueCmpMI, TrueImm);
+        } else {
+          Changed |= adjustTo(TrueCmpMI, TrueCmp, HeadCmpMI, HeadImm);
+        }
+      }
+    }
+    // Other transformation cases almost never occur due to generation of < or >
+    // comparisons instead of <= and >=.
+  }
+
+  return Changed;
+}
diff --git a/llvm/lib/Target/ARM/ARMTargetMachine.cpp b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
index e8d0d35080775..4f817db555c98 100644
--- a/llvm/lib/Target/ARM/ARMTargetMachine.cpp
+++ b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
@@ -79,6 +79,11 @@ static cl::opt<cl::boolOrDefault>
 EnableGlobalMerge("arm-global-merge", cl::Hidden,
                   cl::desc("Enable the global merge pass"));
 
+static cl::opt<bool>
+    EnableCondOpt("arm-enable-condopt",
+                  cl::desc("Enable the condition optimizer pass"),
+                  cl::init(true), cl::Hidden);
+
 namespace llvm {
   void initializeARMExecutionDomainFixPass(PassRegistry&);
 }
@@ -360,6 +365,7 @@ class ARMPassConfig : public TargetPassConfig {
   void addIRPasses() override;
   void addCodeGenPrepare() override;
   bool addPreISel() override;
+  bool addILPOpts() override;
   bool addInstSelector() override;
   bool addIRTranslator() override;
   bool addLegalizeMachineIR() override;
@@ -484,6 +490,12 @@ bool ARMPassConfig::addPreISel() {
   return false;
 }
 
+bool ARMPassConfig::addILPOpts() {
+  if (EnableCondOpt)
+    addPass(createARMConditionOptimizerPass());
+  return false;
+}
+
 bool ARMPassConfig::addInstSelector() {
   addPass(createARMISelDag(getARMTargetMachine(), getOptLevel()));
   return false;
diff --git a/llvm/lib/Target/ARM/CMakeList...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/151187


More information about the llvm-commits mailing list