[llvm] [AArch64] ConditionOptimizer: replace per-block DenseMap with ScopedHashTable traversal (PR #196746)
Hussam Alhassan via llvm-commits
llvm-commits at lists.llvm.org
Wed May 13 02:30:57 PDT 2026
https://github.com/hussam-alhassan updated https://github.com/llvm/llvm-project/pull/196746
>From 06b80fa4065c9c2b6d86356534a717d8d8c08158 Mon Sep 17 00:00:00 2001
From: Hussam Alhassan <hsm.link at proton.me>
Date: Mon, 13 Apr 2026 00:59:30 +0100
Subject: [PATCH 1/2] [AArch64] ConditionOptimizer: replace intra-block
DenseMap with ScopedHashTable traversal
The intra-block path used a DenseMap cleared at each block boundary, so
pairs from dominating blocks were never visible to descendants. Replace
it and the separate cross-block path with a unified recursive domtree
walk using a ScopedHashTable. Any dominating block's pair is now a
candidate, not just pairs within the same block.
Rename optimizeIntraBlock to optimizeBlock and remove dead code
---
.../AArch64/AArch64ConditionOptimizer.cpp | 242 ++++--------------
.../aarch64-condopt-cross-block-domtree.mir | 118 +++++++++
2 files changed, 167 insertions(+), 193 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/aarch64-condopt-cross-block-domtree.mir
diff --git a/llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp b/llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp
index c5dcaa880ae07..9e110629e9ccd 100644
--- a/llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp
@@ -6,17 +6,28 @@
//
//===----------------------------------------------------------------------===//
//
-//
// This pass tries to make consecutive comparisons of values use the same
-// operands to allow the CSE pass to remove duplicate instructions. It adjusts
-// comparisons with immediate values by converting between inclusive and
-// exclusive forms (GE <-> GT, LE <-> LT) and correcting immediate values to
-// make them equal.
+// operands to allow the CSE pass to remove duplicate instructions.
+//
+// For two producer-consumer pairs P1 = (v, N1, CC1) and P2 = (v, N2, CC2) over
+// F where:
+// - v is a shared operand
+// - Nx are the immediates being compared such that 1 <= |N1 - N2| <= 2
+// - CCx are ordered conditions tested by the consumers (GT/LT/GE/LE)
+// - (Nx, CCx) are predicates over v
+// - F is a flag register, implicitly defined by the producers and tested by the
+// consumers
+// - No instruction between P1 and P2 reads or writes F
//
-// The pass handles:
-// * Cross-block: SUBS/ADDS followed by conditional branches
-// * Intra-block: Select-family conditional instructions
+// Apply the following transformation:
+// 1. Find an immediate N' reachable from N1 and N2 via +-1 adjustment
+// 2. Derive CC1' and CC2' such that P1' = (v, N', CC1') and P2' = (v, N', CC2')
+// are
+// semantically equivalent to the originals
+// 3. Modify the producers to compare against N'
+// 4. Modify the consumers to test CC1' and CC2', respectively
//
+// This enables CSE to eliminate one of the compares.
//
// Consider the following example in C:
//
@@ -51,12 +62,9 @@
// b.le .LBB0_6
// ...
//
-// See optimizeCrossBlock() and optimizeIntraBlock() for implementation details.
+// See optimizeBlock() for implementation details.
//
// TODO: maybe handle TBNZ/TBZ the same way as CMP when used instead for "a < 0"
-// TODO: For cross-block:
-// - handle other conditional instructions (e.g. CSET)
-// - allow second branching to be anything if it doesn't require adjusting
//
//===----------------------------------------------------------------------===//
@@ -64,9 +72,7 @@
#include "AArch64Subtarget.h"
#include "MCTargetDesc/AArch64AddressingModes.h"
#include "Utils/AArch64BaseInfo.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/DepthFirstIterator.h"
-#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/ScopedHashTable.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineDominators.h"
@@ -105,37 +111,36 @@ class AArch64ConditionOptimizerImpl {
/// Represents a comparison instruction paired with its consuming
/// conditional instruction
struct CmpCondPair {
- MachineInstr *CmpMI;
- MachineInstr *CondMI;
- AArch64CC::CondCode CC;
+ MachineInstr *CmpMI = nullptr;
+ MachineInstr *CondMI = nullptr;
+ AArch64CC::CondCode CC = AArch64CC::Invalid;
int getImm() const { return CmpMI->getOperand(2).getImm(); }
unsigned getOpc() const { return CmpMI->getOpcode(); }
};
+ using PairSHT = ScopedHashTable<Register, CmpCondPair>;
+
const AArch64InstrInfo *TII;
const TargetRegisterInfo *TRI;
MachineDominatorTree *DomTree;
const MachineRegisterInfo *MRI;
+ PairSHT BlockPairs;
public:
bool run(MachineFunction &MF, MachineDominatorTree &MDT);
private:
bool canAdjustCmp(MachineInstr &CmpMI);
- bool registersMatch(MachineInstr *FirstMI, MachineInstr *SecondMI);
bool nzcvLivesOut(MachineBasicBlock *MBB);
- MachineInstr *getBccTerminator(MachineBasicBlock *MBB);
- MachineInstr *findAdjustableCmp(MachineInstr *CondMI);
CmpInfo getAdjustedCmpInfo(MachineInstr *CmpMI, AArch64CC::CondCode Cmp);
void updateCmpInstr(MachineInstr *CmpMI, int NewImm, unsigned NewOpc);
void updateCondInstr(MachineInstr *CondMI, AArch64CC::CondCode NewCC);
void applyCmpAdjustment(CmpCondPair &Pair, const CmpInfo &Info);
- bool commitPendingPair(std::optional<CmpCondPair> &PendingPair,
- SmallDenseMap<Register, CmpCondPair> &PairsByReg);
+ bool commitPendingPair(std::optional<CmpCondPair> &PendingPair);
bool tryOptimizePair(CmpCondPair &First, CmpCondPair &Second);
- bool optimizeIntraBlock(MachineBasicBlock &MBB);
- bool optimizeCrossBlock(MachineBasicBlock &HBB);
+ bool optimizeBlock(MachineBasicBlock &MBB);
+ void visitBlock(MachineDomTreeNode *Node, bool &Changed);
};
class AArch64ConditionOptimizerLegacy : public MachineFunctionPass {
@@ -191,23 +196,6 @@ bool AArch64ConditionOptimizerImpl::canAdjustCmp(MachineInstr &CmpMI) {
return true;
}
-// Ensure both compare MIs use the same register, tracing through copies.
-bool AArch64ConditionOptimizerImpl::registersMatch(MachineInstr *FirstMI,
- MachineInstr *SecondMI) {
- Register FirstReg = FirstMI->getOperand(1).getReg();
- Register SecondReg = SecondMI->getOperand(1).getReg();
- Register FirstCmpReg =
- FirstReg.isVirtual() ? TRI->lookThruCopyLike(FirstReg, MRI) : FirstReg;
- Register SecondCmpReg =
- SecondReg.isVirtual() ? TRI->lookThruCopyLike(SecondReg, MRI) : SecondReg;
- if (FirstCmpReg != SecondCmpReg) {
- LLVM_DEBUG(dbgs() << "CMPs compare different registers\n");
- return false;
- }
-
- return true;
-}
-
// Check if NZCV lives out to any successor block.
bool AArch64ConditionOptimizerImpl::nzcvLivesOut(MachineBasicBlock *MBB) {
for (auto *SuccBB : MBB->successors()) {
@@ -236,61 +224,6 @@ static bool isCmpInstruction(unsigned Opc) {
}
}
-// Returns the Bcc terminator if present, otherwise nullptr.
-MachineInstr *
-AArch64ConditionOptimizerImpl::getBccTerminator(MachineBasicBlock *MBB) {
- MachineBasicBlock::iterator Term = MBB->getFirstTerminator();
- if (Term == MBB->end()) {
- LLVM_DEBUG(dbgs() << "No terminator in " << printMBBReference(*MBB)
- << '\n');
- return nullptr;
- }
-
- if (Term->getOpcode() != AArch64::Bcc) {
- LLVM_DEBUG(dbgs() << "Non-Bcc terminator in " << printMBBReference(*MBB)
- << ": " << *Term);
- return nullptr;
- }
-
- return &*Term;
-}
-
-// Find the CMP instruction controlling the given conditional instruction and
-// ensure it can be adjusted for CSE optimization. Searches backward from
-// CondMI, ensuring no NZCV interference. Returns nullptr if no suitable CMP
-// is found or if adjustments are not safe.
-MachineInstr *
-AArch64ConditionOptimizerImpl::findAdjustableCmp(MachineInstr *CondMI) {
- assert(CondMI && "CondMI cannot be null");
- MachineBasicBlock *MBB = CondMI->getParent();
-
- // Search backward from the conditional to find the instruction controlling
- // it.
- for (MachineBasicBlock::iterator B = MBB->begin(),
- It = MachineBasicBlock::iterator(CondMI);
- It != B;) {
- It = prev_nodbg(It, B);
- MachineInstr &I = *It;
- assert(!I.isTerminator() && "Spurious terminator");
- // Ensure there is no use of NZCV between CMP and conditional.
- if (I.readsRegister(AArch64::NZCV, /*TRI=*/nullptr))
- return nullptr;
-
- if (isCmpInstruction(I.getOpcode())) {
- if (!canAdjustCmp(I)) {
- return nullptr;
- }
- return &I;
- }
-
- if (I.modifiesRegister(AArch64::NZCV, /*TRI=*/nullptr))
- return nullptr;
- }
- LLVM_DEBUG(dbgs() << "Flags not defined in " << printMBBReference(*MBB)
- << '\n');
- return nullptr;
-}
-
// Changes opcode adds <-> subs considering register operand width.
static int getComplementOpc(int Opc) {
switch (Opc) {
@@ -394,18 +327,6 @@ void AArch64ConditionOptimizerImpl::applyCmpAdjustment(CmpCondPair &Pair,
Pair.CC = Info.CC;
}
-// Extracts the condition code from the result of analyzeBranch.
-// Returns the CondCode or Invalid if the format is not a simple br.cond.
-static AArch64CC::CondCode parseCondCode(ArrayRef<MachineOperand> Cond) {
- assert(!Cond.empty() && "Expected non-empty condition from analyzeBranch");
- // A normal br.cond simply has the condition code.
- if (Cond[0].getImm() != -1) {
- assert(Cond.size() == 1 && "Unknown Cond array format");
- return (AArch64CC::CondCode)(int)Cond[0].getImm();
- }
- return AArch64CC::CondCode::Invalid;
-}
-
static bool isGreaterThan(AArch64CC::CondCode Cmp) {
return Cmp == AArch64CC::GT || Cmp == AArch64CC::HI;
}
@@ -507,19 +428,17 @@ bool AArch64ConditionOptimizerImpl::tryOptimizePair(CmpCondPair &First,
}
bool AArch64ConditionOptimizerImpl::commitPendingPair(
- std::optional<CmpCondPair> &PendingPair,
- SmallDenseMap<Register, CmpCondPair> &PairsByReg) {
+ std::optional<CmpCondPair> &PendingPair) {
if (!PendingPair)
return false;
Register Reg = PendingPair->CmpMI->getOperand(1).getReg();
Register Key = Reg.isVirtual() ? TRI->lookThruCopyLike(Reg, MRI) : Reg;
- auto MatchingPair = PairsByReg.find(Key);
- bool Changed = MatchingPair != PairsByReg.end() &&
- tryOptimizePair(MatchingPair->second, *PendingPair);
+ CmpCondPair Prior = BlockPairs.lookup(Key);
+ bool Changed = Prior.CmpMI && tryOptimizePair(Prior, *PendingPair);
- PairsByReg[Key] = *PendingPair;
+ BlockPairs.insert(Key, *PendingPair);
PendingPair = std::nullopt;
return Changed;
}
@@ -544,8 +463,7 @@ bool AArch64ConditionOptimizerImpl::commitPendingPair(
// cmp w8, #10 ; <- CSE can remove the redundant cmp
// csinc w10, w0, w1, ge ; w10 = (w8 >= 10) ? w0 : w1+1
//
-bool AArch64ConditionOptimizerImpl::optimizeIntraBlock(MachineBasicBlock &MBB) {
- SmallDenseMap<Register, CmpCondPair> PairsByReg;
+bool AArch64ConditionOptimizerImpl::optimizeBlock(MachineBasicBlock &MBB) {
std::optional<CmpCondPair> PendingPair;
MachineInstr *ActiveCmp = nullptr;
bool Changed = false;
@@ -555,22 +473,19 @@ bool AArch64ConditionOptimizerImpl::optimizeIntraBlock(MachineBasicBlock &MBB) {
continue;
if (isCmpInstruction(MI.getOpcode()) && canAdjustCmp(MI)) {
- Changed |= commitPendingPair(PendingPair, PairsByReg);
+ Changed |= commitPendingPair(PendingPair);
ActiveCmp = &MI;
continue;
}
if (MI.modifiesRegister(AArch64::NZCV, /*TRI=*/nullptr)) {
- // Non-CMP clobber: commit any pending pair and reset all state, since
- // unknown flag state at this point invalidates all prior pairs
- Changed |= commitPendingPair(PendingPair, PairsByReg);
+ // Non-CMP clobber: commit any pending pair and reset local state.
+ Changed |= commitPendingPair(PendingPair);
ActiveCmp = nullptr;
- PairsByReg.clear();
continue;
}
- if (AArch64InstrInfo::findCondCodeUseOperandIdxForBranchOrSelect(MI) >= 0 &&
- !MI.isBranch()) {
+ if (AArch64InstrInfo::findCondCodeUseOperandIdxForBranchOrSelect(MI) >= 0) {
if (PendingPair) {
// A second conditional consuming the same CMP would invalidate any
// optimization: modifying the CMP would silently change what both
@@ -598,67 +513,11 @@ bool AArch64ConditionOptimizerImpl::optimizeIntraBlock(MachineBasicBlock &MBB) {
// Only commit the final pending pair if NZCV doesn't live out: a cross-block
// consumer would be affected by any CMP adjustment we make.
if (!nzcvLivesOut(&MBB))
- Changed |= commitPendingPair(PendingPair, PairsByReg);
+ Changed |= commitPendingPair(PendingPair);
return Changed;
}
-// Optimizes CMP+Bcc pairs across two basic blocks in the dominator tree.
-bool AArch64ConditionOptimizerImpl::optimizeCrossBlock(MachineBasicBlock &HBB) {
- SmallVector<MachineOperand, 4> HeadCondOperands;
- MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
- if (TII->analyzeBranch(HBB, TBB, FBB, HeadCondOperands)) {
- return false;
- }
-
- // Equivalence check is to skip loops.
- if (!TBB || TBB == &HBB) {
- return false;
- }
-
- SmallVector<MachineOperand, 4> TrueCondOperands;
- MachineBasicBlock *TBB_TBB = nullptr, *TBB_FBB = nullptr;
- if (TII->analyzeBranch(*TBB, TBB_TBB, TBB_FBB, TrueCondOperands)) {
- return false;
- }
-
- MachineInstr *HeadBrMI = getBccTerminator(&HBB);
- MachineInstr *TrueBrMI = getBccTerminator(TBB);
- if (!HeadBrMI || !TrueBrMI)
- return false;
-
- // Since we may modify cmps in these blocks, make sure NZCV does not live out.
- if (nzcvLivesOut(&HBB) || nzcvLivesOut(TBB))
- return false;
-
- // Find the CMPs controlling each branch
- MachineInstr *HeadCmpMI = findAdjustableCmp(HeadBrMI);
- MachineInstr *TrueCmpMI = findAdjustableCmp(TrueBrMI);
- if (!HeadCmpMI || !TrueCmpMI)
- return false;
-
- if (!registersMatch(HeadCmpMI, TrueCmpMI))
- return false;
-
- AArch64CC::CondCode HeadCondCode = parseCondCode(HeadCondOperands);
- AArch64CC::CondCode TrueCondCode = parseCondCode(TrueCondOperands);
- if (HeadCondCode == AArch64CC::CondCode::Invalid ||
- TrueCondCode == AArch64CC::CondCode::Invalid) {
- return false;
- }
-
- LLVM_DEBUG(dbgs() << "Checking cross-block pair: "
- << AArch64CC::getCondCodeName(HeadCondCode) << " #"
- << HeadCmpMI->getOperand(2).getImm() << ", "
- << AArch64CC::getCondCodeName(TrueCondCode) << " #"
- << TrueCmpMI->getOperand(2).getImm() << '\n');
-
- CmpCondPair Head{HeadCmpMI, HeadBrMI, HeadCondCode};
- CmpCondPair True{TrueCmpMI, TrueBrMI, TrueCondCode};
-
- return tryOptimizePair(Head, True);
-}
-
bool AArch64ConditionOptimizerLegacy::runOnMachineFunction(
MachineFunction &MF) {
if (skipFunction(MF.getFunction()))
@@ -668,6 +527,14 @@ bool AArch64ConditionOptimizerLegacy::runOnMachineFunction(
return AArch64ConditionOptimizerImpl().run(MF, MDT);
}
+void AArch64ConditionOptimizerImpl::visitBlock(MachineDomTreeNode *Node,
+ bool &Changed) {
+ ScopedHashTableScope<Register, CmpCondPair> Scope(BlockPairs);
+ Changed |= optimizeBlock(*Node->getBlock());
+ for (auto *Child : Node->children())
+ visitBlock(Child, Changed);
+}
+
bool AArch64ConditionOptimizerImpl::run(MachineFunction &MF,
MachineDominatorTree &MDT) {
LLVM_DEBUG(dbgs() << "********** AArch64 Conditional Compares **********\n"
@@ -679,18 +546,7 @@ bool AArch64ConditionOptimizerImpl::run(MachineFunction &MF,
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();
- Changed |= optimizeIntraBlock(*HBB);
- Changed |= optimizeCrossBlock(*HBB);
- }
-
+ visitBlock(DomTree->getRootNode(), Changed);
return Changed;
}
diff --git a/llvm/test/CodeGen/AArch64/aarch64-condopt-cross-block-domtree.mir b/llvm/test/CodeGen/AArch64/aarch64-condopt-cross-block-domtree.mir
new file mode 100644
index 0000000000000..65b54e8c46d94
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/aarch64-condopt-cross-block-domtree.mir
@@ -0,0 +1,118 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=aarch64-linux-gnu -run-pass=aarch64-condopt -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=aarch64-linux-gnu -passes=aarch64-condopt %s -o - | FileCheck %s
+
+---
+# Non-adjacent dominator pair: bb.1 sits between bb.0 and bb.2 in the CFG, but
+# bb.0 dominates bb.2, so the SHT makes bb.0's pair visible when processing bb.2.
+#
+# Input: bb.0: CMP #9 GT; bb.2: CMP #11 LT
+# Output: bb.0: CMP #10 GE; bb.2: CMP #10 LE
+name: non_adjacent_dominator_pair
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gpr32sp }
+body: |
+ ; CHECK-LABEL: name: non_adjacent_dominator_pair
+ ; CHECK: bb.0:
+ ; CHECK-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000)
+ ; CHECK-NEXT: liveins: $w0
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32sp = COPY $w0
+ ; CHECK-NEXT: dead $wzr = SUBSWri [[COPY]], 10, 0, implicit-def $nzcv
+ ; CHECK-NEXT: Bcc 10, %bb.1, implicit $nzcv
+ ; CHECK-NEXT: B %bb.2
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.1:
+ ; CHECK-NEXT: successors: %bb.2(0x80000000)
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: B %bb.2
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.2:
+ ; CHECK-NEXT: successors: %bb.3(0x40000000), %bb.4(0x40000000)
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: dead $wzr = SUBSWri [[COPY]], 10, 0, implicit-def $nzcv
+ ; CHECK-NEXT: Bcc 13, %bb.3, implicit $nzcv
+ ; CHECK-NEXT: B %bb.4
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.3:
+ ; CHECK-NEXT: $w0 = MOVi32imm 1
+ ; CHECK-NEXT: RET_ReallyLR implicit $w0
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.4:
+ ; CHECK-NEXT: $w0 = MOVi32imm 0
+ ; CHECK-NEXT: RET_ReallyLR implicit $w0
+ bb.0:
+ liveins: $w0
+ successors: %bb.1, %bb.2
+
+ %0:gpr32sp = COPY $w0
+ dead $wzr = SUBSWri %0, 9, 0, implicit-def $nzcv
+ Bcc 12, %bb.1, implicit $nzcv
+ B %bb.2
+
+ bb.1:
+ successors: %bb.2
+ B %bb.2
+
+ bb.2:
+ successors: %bb.3, %bb.4
+
+ dead $wzr = SUBSWri %0, 11, 0, implicit-def $nzcv
+ Bcc 11, %bb.3, implicit $nzcv
+ B %bb.4
+
+ bb.3:
+ $w0 = MOVi32imm 1
+ RET_ReallyLR implicit $w0
+
+ bb.4:
+ $w0 = MOVi32imm 0
+ RET_ReallyLR implicit $w0
+...
+---
+# Cross-block select-pair: bb.0 dominates bb.1, so the SHT makes bb.0's pair
+# visible when processing bb.1.
+#
+# Input: bb.0: CMP #9 GT CSINC; bb.1: CMP #11 LT CSINC
+# Output: bb.0: CMP #10 GE CSINC; bb.1: CMP #10 LE CSINC
+name: cross_block_select_pair
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gpr32sp }
+ - { id: 1, class: gpr32 }
+ - { id: 2, class: gpr32 }
+ - { id: 3, class: gpr32 }
+body: |
+ ; CHECK-LABEL: name: cross_block_select_pair
+ ; CHECK: bb.0:
+ ; CHECK-NEXT: successors: %bb.1(0x80000000)
+ ; CHECK-NEXT: liveins: $w0, $w1, $w2
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32sp = COPY $w0
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32 = COPY $w1
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr32 = COPY $w2
+ ; CHECK-NEXT: dead $wzr = SUBSWri [[COPY]], 10, 0, implicit-def $nzcv
+ ; CHECK-NEXT: [[CSINCWr:%[0-9]+]]:gpr32 = CSINCWr [[COPY1]], [[COPY2]], 10, implicit $nzcv
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.1:
+ ; CHECK-NEXT: dead $wzr = SUBSWri [[COPY]], 10, 0, implicit-def $nzcv
+ ; CHECK-NEXT: [[CSINCWr1:%[0-9]+]]:gpr32 = CSINCWr [[CSINCWr]], [[COPY2]], 13, implicit $nzcv
+ ; CHECK-NEXT: $w0 = COPY [[CSINCWr1]]
+ ; CHECK-NEXT: RET_ReallyLR implicit $w0
+ bb.0:
+ liveins: $w0, $w1, $w2
+ successors: %bb.1
+
+ %0:gpr32sp = COPY $w0
+ %1:gpr32 = COPY $w1
+ %2:gpr32 = COPY $w2
+ dead $wzr = SUBSWri %0, 9, 0, implicit-def $nzcv
+ %3:gpr32 = CSINCWr %1, %2, 12, implicit $nzcv
+
+ bb.1:
+ dead $wzr = SUBSWri %0, 11, 0, implicit-def $nzcv
+ %4:gpr32 = CSINCWr %3, %2, 11, implicit $nzcv
+ $w0 = COPY %4
+ RET_ReallyLR implicit $w0
+...
>From b2139e40662927a94d5ec26ea6a26114262463dd Mon Sep 17 00:00:00 2001
From: Hussam Alhassan <hsm.link at proton.me>
Date: Wed, 13 May 2026 10:28:26 +0100
Subject: [PATCH 2/2] Fix misaligned comment
---
llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp b/llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp
index 9e110629e9ccd..c59efedaab613 100644
--- a/llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp
@@ -22,8 +22,7 @@
// Apply the following transformation:
// 1. Find an immediate N' reachable from N1 and N2 via +-1 adjustment
// 2. Derive CC1' and CC2' such that P1' = (v, N', CC1') and P2' = (v, N', CC2')
-// are
-// semantically equivalent to the originals
+// are semantically equivalent to the originals
// 3. Modify the producers to compare against N'
// 4. Modify the consumers to test CC1' and CC2', respectively
//
More information about the llvm-commits
mailing list