[X86][APX] Reuse EFLAGS across multi-predecessor blocks via NF in optimizeCompareInstr#208184
[X86][APX] Reuse EFLAGS across multi-predecessor blocks via NF in optimizeCompareInstr#208184fzou1 wants to merge 2 commits into
Conversation
…CompareInstr Extend optimizeCompareInstr to reuse EFLAGS produced in predecessor blocks (including via NF variants) when a compare is redundant, covering multi-predecessor, dominating, and cyclic CFGs. Share the NF-clobber removal helper with FlagsCopyLowering, correctly handle swapped and immediate-adjusted flag reuse, and simplify EFLAGS live-in marking. Adds MIR tests exercising multi-predecessor reuse over cyclic and dominating CFGs, including a multi-level idom walk. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@llvm/pr-subscribers-backend-x86 Author: Feng Zou (fzou1) ChangesExtend optimizeCompareInstr to reuse EFLAGS produced in predecessor blocks (including via NF variants) when a compare is redundant, covering multi-predecessor, dominating, and cyclic CFGs. Share the NF-clobber removal helper with FlagsCopyLowering, correctly handle swapped and immediate-adjusted flag reuse, and simplify EFLAGS live-in marking. Adds MIR tests exercising multi-predecessor reuse over cyclic and dominating CFGs, including a multi-level idom walk. Assisted-By: Claude Opus 4.8 Patch is 37.99 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/208184.diff 7 Files Affected:
diff --git a/llvm/lib/Target/X86/X86FlagsCopyLowering.cpp b/llvm/lib/Target/X86/X86FlagsCopyLowering.cpp
index 36b17cab03ee9..48c148f882456 100644
--- a/llvm/lib/Target/X86/X86FlagsCopyLowering.cpp
+++ b/llvm/lib/Target/X86/X86FlagsCopyLowering.cpp
@@ -68,8 +68,6 @@ STATISTIC(NumTestsInserted, "Number of test instructions inserted");
STATISTIC(NumAddsInserted, "Number of adds instructions inserted");
STATISTIC(NumNFsConvertedTo, "Number of NF instructions converted to");
-extern cl::opt<bool> X86EnableAPXForRelocation;
-
namespace {
// Convenient array type for storing registers associated with each condition.
@@ -254,14 +252,7 @@ static EFLAGSClobber getClobberType(const MachineInstr &MI) {
if (!FlagDef)
return NoClobber;
- // For the instructions are ADDrm/ADDmr with relocation, we'll skip the
- // optimization for replacing non-NF with NF. This is to keep backward
- // compatiblity with old version of linkers without APX relocation type
- // support on Linux OS.
- bool IsWithReloc =
- X86EnableAPXForRelocation ? false : isAddMemInstrWithRelocation(MI);
-
- if (FlagDef->isDead() && X86::getNFVariant(MI.getOpcode()) && !IsWithReloc)
+ if (X86::getNFVariantIfClobberRemovable(MI))
return EvitableClobber;
return InevitableClobber;
diff --git a/llvm/lib/Target/X86/X86InstrInfo.cpp b/llvm/lib/Target/X86/X86InstrInfo.cpp
index 7ff2400d06d1d..e7410ddabee94 100644
--- a/llvm/lib/Target/X86/X86InstrInfo.cpp
+++ b/llvm/lib/Target/X86/X86InstrInfo.cpp
@@ -3290,6 +3290,21 @@ unsigned X86::getNFVariant(unsigned Opc) {
return getNewOpcFromTable(X86NFTransformTable, Opc);
}
+unsigned X86::getNFVariantIfClobberRemovable(const MachineInstr &MI,
+ const TargetRegisterInfo *TRI) {
+ if (!MI.registerDefIsDead(X86::EFLAGS, TRI))
+ return 0;
+ // For the instructions are ADDrm/ADDmr with relocation, we'll skip the
+ // optimization for replacing non-NF with NF. This is to keep backward
+ // compatiblity with old version of linkers without APX relocation type
+ // support on Linux OS.
+ bool IsWithReloc =
+ X86EnableAPXForRelocation ? false : isAddMemInstrWithRelocation(MI);
+ if (IsWithReloc)
+ return 0;
+ return X86::getNFVariant(MI.getOpcode());
+}
+
unsigned X86::getNonNDVariant(unsigned Opc) {
#if defined(EXPENSIVE_CHECKS) && !defined(NDEBUG)
// Make sure the tables are sorted.
@@ -5285,6 +5300,104 @@ static std::pair<X86::CondCode, unsigned> isUseDefConvertible(const MachineInstr
}
}
+MachineInstr *X86InstrInfo::findDominatingRedundantFlagInstr(
+ MachineInstr &CmpInstr, Register SrcReg, Register SrcReg2, int64_t CmpMask,
+ int64_t CmpValue, MachineBasicBlock *MultiPredMBB, bool &IsSwapped,
+ int64_t &ImmDelta,
+ SmallVectorImpl<std::pair<MachineInstr *, unsigned>> &InstsToUpdate) const {
+ assert(Subtarget.hasNF() && "NF feature required");
+ MachineBasicBlock &CmpMBB = *CmpInstr.getParent();
+ MachineFunction &MF = *CmpMBB.getParent();
+ const TargetRegisterInfo *TRI = &getRegisterInfo();
+
+ // The caller already scanned MultiPredMBB (and any single-predecessor blocks
+ // between it and CmpMBB) without finding the producer, so the producer must
+ // live in a block that strictly dominates MultiPredMBB. Walk up the
+ // immediate-dominator chain. In each dominating block scan backward: the
+ // redundant producer ends the search; an NF-convertible clobber is stepped
+ // over (it will be collected by the path walk below) so the producer can
+ // still be found earlier in the same block or further up; any other EFLAGS
+ // clobber shadows the producer, so its flags cannot reach CmpInstr.
+ MachineDominatorTree MDT(MF);
+ MachineDomTreeNode *Node = MDT.getNode(MultiPredMBB);
+ MachineInstr *Sub = nullptr;
+ for (Node = Node ? Node->getIDom() : nullptr; Node && !Sub;
+ Node = Node->getIDom()) {
+ MachineBasicBlock *DomMBB = Node->getBlock();
+ for (MachineInstr &Inst : reverse(*DomMBB)) {
+ if (!Inst.modifiesRegister(X86::EFLAGS, TRI))
+ continue;
+ if (isRedundantFlagInstr(CmpInstr, SrcReg, SrcReg2, CmpMask, CmpValue,
+ Inst, &IsSwapped, &ImmDelta)) {
+ // Found the producer.
+ Sub = &Inst;
+ break;
+ }
+ if (!X86::getNFVariantIfClobberRemovable(Inst, TRI))
+ // A non-convertible clobber shadows any producer further up.
+ return nullptr;
+ // NF-convertible clobber: keep scanning earlier in this block.
+ }
+ // No producer in this block: keep walking up the dominator chain.
+ }
+ if (!Sub)
+ return nullptr;
+
+ // The forward condition-code fixup in the caller (OpsToUpdate) only rewrites
+ // EFLAGS users within CmpMBB. When the producer's flags require a condition
+ // swap or an immediate adjustment, EFLAGS users elsewhere in the dominated
+ // region or in CmpMBB's successors (when EFLAGS is live-out) would also need
+ // rewriting, which is not handled here. Restrict the multi-predecessor case
+ // to producers that yield identical flags.
+ if (IsSwapped || ImmDelta != 0)
+ return nullptr;
+ MachineBasicBlock *SubMBB = Sub->getParent();
+
+ // Verify that on every path from the producer to CmpInstr all EFLAGS clobbers
+ // are NF-convertible, collecting them. The caller already handled CmpMBB, the
+ // single-predecessor chain, and MultiPredMBB itself, so start from
+ // MultiPredMBB's predecessors and walk the CFG backward up to (and including
+ // the post-producer range of) SubMBB. Because SubMBB dominates MultiPredMBB,
+ // every block that can reach MultiPredMBB is dominated by SubMBB, so the walk
+ // reaches SubMBB on every path and never escapes above it.
+ //
+ // The walk never revisits a block (Visited) and never reaches a block the
+ // caller already scanned: MultiPredMBB is seeded into Visited, the
+ // single-predecessor chain blocks (CmpMBB..MultiPredMBB) each have exactly one
+ // predecessor by construction (the caller only advanced through such blocks),
+ // so none can be re-entered from the dominated region, and CmpMBB lies in the
+ // successor direction. No instruction is therefore collected twice.
+ SmallPtrSet<MachineBasicBlock *, 8> Visited;
+ SmallVector<MachineBasicBlock *, 8> Worklist;
+ Visited.insert(MultiPredMBB);
+ for (MachineBasicBlock *Pred : MultiPredMBB->predecessors())
+ if (Visited.insert(Pred).second)
+ Worklist.push_back(Pred);
+ while (!Worklist.empty()) {
+ MachineBasicBlock *MBB = Worklist.pop_back_val();
+ // For SubMBB only the range after the producer is relevant.
+ MachineBasicBlock::iterator I =
+ (MBB == SubMBB) ? std::next(MachineBasicBlock::iterator(Sub))
+ : MBB->begin();
+ for (MachineBasicBlock::iterator E = MBB->end(); I != E; ++I) {
+ MachineInstr &MI = *I;
+ if (!MI.modifiesRegister(X86::EFLAGS, TRI))
+ continue;
+ unsigned NewOpc = X86::getNFVariantIfClobberRemovable(MI, TRI);
+ if (!NewOpc)
+ return nullptr;
+ InstsToUpdate.push_back(std::make_pair(&MI, NewOpc));
+ }
+ if (MBB == SubMBB)
+ continue;
+ for (MachineBasicBlock *Pred : MBB->predecessors())
+ if (Visited.insert(Pred).second)
+ Worklist.push_back(Pred);
+ }
+
+ return Sub;
+}
+
/// Check if there exists an earlier instruction that
/// operates on the same source operands and sets flags in the same way as
/// Compare; remove Compare if possible.
@@ -5448,17 +5561,9 @@ bool X86InstrInfo::optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg,
continue;
}
- // For the instructions are ADDrm/ADDmr with relocation, we'll skip the
- // optimization for replacing non-NF with NF. This is to keep backward
- // compatiblity with old version of linkers without APX relocation type
- // support on Linux OS.
- bool IsWithReloc = X86EnableAPXForRelocation
- ? false
- : isAddMemInstrWithRelocation(Inst);
-
// Try to replace non-NF with NF instructions.
- if (HasNF && Inst.registerDefIsDead(X86::EFLAGS, TRI) && !IsWithReloc) {
- unsigned NewOp = X86::getNFVariant(Inst.getOpcode());
+ if (HasNF) {
+ unsigned NewOp = X86::getNFVariantIfClobberRemovable(Inst, TRI);
if (!NewOp)
return false;
@@ -5474,10 +5579,28 @@ bool X86InstrInfo::optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg,
if (MI || Sub)
break;
- // Reached begin of basic block. Continue in predecessor if there is
- // exactly one.
- if (MBB->pred_size() != 1)
- return false;
+ // Reached the begin of the basic block. If it has exactly one predecessor,
+ // continue the backward scan there. Otherwise (multiple predecessors), try
+ // to reuse EFLAGS from a dominating producer (handled below).
+ if (MBB->pred_size() != 1) {
+ // The block has multiple predecessors. We can still reuse EFLAGS from an
+ // equivalent flag producer that dominates CmpInstr, provided every path
+ // from that producer to CmpInstr only clobbers EFLAGS via instructions
+ // that have an NF (no-flags) variant (which requires APX). This handles
+ // patterns like (CMP duplicated by CodeGenPrepare across a diamond):
+ // entry: cmp %x, C ; br
+ // bb1: imul ... ; clobbers EFLAGS -> {nf} imul
+ // bb2: ...
+ // bb3: cmp %x, C ; <-- redundant, reuse EFLAGS from entry
+ // cmovcc ...
+ if (HasNF)
+ Sub = findDominatingRedundantFlagInstr(CmpInstr, SrcReg, SrcReg2,
+ CmpMask, CmpValue, MBB, IsSwapped,
+ ImmDelta, InstsToUpdate);
+ if (!Sub)
+ return false;
+ break;
+ }
MBB = *MBB->pred_begin();
From = MBB->rbegin();
}
@@ -5683,11 +5806,25 @@ bool X86InstrInfo::optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg,
.setImm(Op.second);
}
// Add EFLAGS to block live-ins between CmpBB and block of flags producer.
- for (MachineBasicBlock *MBB = &CmpMBB; MBB != SubBB;
- MBB = *MBB->pred_begin()) {
- assert(MBB->pred_size() == 1 && "Expected exactly one predecessor");
+ // Walk the CFG backward from CmpMBB up to (but excluding) SubBB, marking
+ // EFLAGS live-in on every block in between. SubBB dominates CmpMBB (whether
+ // the producer was found by the single-predecessor backward walk or the
+ // multi-predecessor dominator search), so the walk reaches SubBB on every
+ // path and never escapes above it. A single-predecessor chain is just the
+ // degenerate case where every block has exactly one predecessor.
+ SmallPtrSet<MachineBasicBlock *, 8> Visited;
+ SmallVector<MachineBasicBlock *, 8> Worklist(1, &CmpMBB);
+ Visited.insert(&CmpMBB);
+ while (!Worklist.empty()) {
+ MachineBasicBlock *MBB = Worklist.pop_back_val();
+ // EFLAGS is produced inside SubBB, so it is not live-in there.
+ if (MBB == SubBB)
+ continue;
if (!MBB->isLiveIn(X86::EFLAGS))
MBB->addLiveIn(X86::EFLAGS);
+ for (MachineBasicBlock *Pred : MBB->predecessors())
+ if (Visited.insert(Pred).second)
+ Worklist.push_back(Pred);
}
return true;
}
diff --git a/llvm/lib/Target/X86/X86InstrInfo.h b/llvm/lib/Target/X86/X86InstrInfo.h
index 2db0731da3c56..df0c74e1c7b9e 100644
--- a/llvm/lib/Target/X86/X86InstrInfo.h
+++ b/llvm/lib/Target/X86/X86InstrInfo.h
@@ -83,6 +83,14 @@ int getCCMPCondFlagsFromCondCode(CondCode CC);
// Get the opcode of corresponding NF variant.
unsigned getNFVariant(unsigned Opc);
+// If \p MI clobbers EFLAGS and that clobber can be removed by rewriting the
+// instruction to its NF (no-flags) variant, return that NF opcode; otherwise
+// return 0. The clobber is removable only if the EFLAGS def is dead, an NF
+// variant exists, and (for linker backward-compat) it is not an ADDrm/ADDmr
+// with relocation.
+unsigned getNFVariantIfClobberRemovable(const MachineInstr &MI,
+ const TargetRegisterInfo *TRI = nullptr);
+
// Get the opcode of corresponding NonND variant.
unsigned getNonNDVariant(unsigned Opc);
@@ -765,6 +773,24 @@ class X86InstrInfo final : public X86GenInstrInfo {
const MachineInstr &OI, bool *IsSwapped,
int64_t *ImmDelta) const;
+ /// Used by optimizeCompareInstr when the backward search for an equivalent
+ /// flag producer reaches a block (\p MultiPredMBB) with multiple
+ /// predecessors. Searches the dominator tree for an instruction that produces
+ /// the same flags as the compare \p CmpInstr and dominates it, such that on
+ /// every path from that producer to \p CmpInstr all EFLAGS clobbers can be
+ /// converted to their NF (no-flags) variants. Those clobbers are appended to
+ /// \p InstsToUpdate as (instruction, NF opcode) pairs for the caller to
+ /// rewrite. Restricted to producers that yield identical flags: it succeeds
+ /// only when isRedundantFlagInstr reports no operand swap and no immediate
+ /// delta, so on success \p IsSwapped is false and \p ImmDelta is 0. Returns
+ /// the flag producer on success, or nullptr otherwise. Requires the NF
+ /// feature (APX).
+ MachineInstr *findDominatingRedundantFlagInstr(
+ MachineInstr &CmpInstr, Register SrcReg, Register SrcReg2,
+ int64_t CmpMask, int64_t CmpValue, MachineBasicBlock *MultiPredMBB,
+ bool &IsSwapped, int64_t &ImmDelta,
+ SmallVectorImpl<std::pair<MachineInstr *, unsigned>> &InstsToUpdate) const;
+
/// Commute operands of \p MI for memory fold.
///
/// \param Idx1 the index of operand to be commuted.
diff --git a/llvm/test/CodeGen/X86/apx/add.ll b/llvm/test/CodeGen/X86/apx/add.ll
index d7c5635b617c1..8b1e1ceb832a3 100644
--- a/llvm/test/CodeGen/X86/apx/add.ll
+++ b/llvm/test/CodeGen/X86/apx/add.ll
@@ -1221,26 +1221,24 @@ define i32 @two_address_no_subreg(i32 %arg0, ptr %arg1, i1 %arg2) nounwind {
; NF-NEXT: jne .LBB50_2 # encoding: [0x75,A]
; NF-NEXT: # fixup A - offset: 1, value: .LBB50_2, kind: FK_PCRel_1
; NF-NEXT: # %bb.1: # %bb2
-; NF-NEXT: xorl %r12d, %r12d # encoding: [0x45,0x31,0xe4]
+; NF-NEXT: movl $0, %r12d # encoding: [0x41,0xbc,0x00,0x00,0x00,0x00]
; NF-NEXT: .LBB50_2: # %bb1
-; NF-NEXT: movl %r13d, %esi # encoding: [0x44,0x89,0xee]
-; NF-NEXT: addl (%rsp), %esi # 4-byte Folded Reload
-; NF-NEXT: # encoding: [0x03,0x34,0x24]
-; NF-NEXT: {nf} imull %ebx, %ebx, %r13d # EVEX TO EVEX Compression encoding: [0x62,0xf4,0x14,0x1c,0xaf,0xdb]
-; NF-NEXT: testb $1, %bpl # encoding: [0x40,0xf6,0xc5,0x01]
+; NF-NEXT: movq (%rsp), %rax # 8-byte Reload
+; NF-NEXT: # encoding: [0x48,0x8b,0x04,0x24]
+; NF-NEXT: {nf} addl %eax, %r13d, %ebp # encoding: [0x62,0xd4,0x54,0x1c,0x01,0xc5]
+; NF-NEXT: {nf} imull %ebx, %ebx, %r13d # encoding: [0x62,0xf4,0x14,0x1c,0xaf,0xdb]
; NF-NEXT: jne .LBB50_4 # encoding: [0x75,A]
; NF-NEXT: # fixup A - offset: 1, value: .LBB50_4, kind: FK_PCRel_1
; NF-NEXT: # %bb.3: # %bb4
-; NF-NEXT: xorl %ebp, %ebp # encoding: [0x31,0xed]
+; NF-NEXT: xorl %eax, %eax # encoding: [0x31,0xc0]
; NF-NEXT: movq %r14, %rdi # encoding: [0x4c,0x89,0xf7]
-; NF-NEXT: movl %esi, %r14d # encoding: [0x41,0x89,0xf6]
-; NF-NEXT: callq *%rbp # encoding: [0xff,0xd5]
+; NF-NEXT: callq *%rax # encoding: [0xff,0xd0]
+; NF-NEXT: xorl %eax, %eax # encoding: [0x31,0xc0]
; NF-NEXT: {nf} incl %r12d, %r15d # EVEX TO EVEX Compression encoding: [0x62,0xd4,0x04,0x1c,0xff,0xc4]
; NF-NEXT: xorl %edi, %edi # encoding: [0x31,0xff]
-; NF-NEXT: callq *%rbp # encoding: [0xff,0xd5]
-; NF-NEXT: movl %r14d, %esi # encoding: [0x44,0x89,0xf6]
+; NF-NEXT: callq *%rax # encoding: [0xff,0xd0]
; NF-NEXT: .LBB50_4: # %bb3
-; NF-NEXT: orl %r13d, %esi # EVEX TO LEGACY Compression encoding: [0x44,0x09,0xee]
+; NF-NEXT: {nf} orl %r13d, %ebp, %esi # EVEX TO EVEX Compression encoding: [0x62,0x74,0x4c,0x1c,0x09,0xed]
; NF-NEXT: orl %r15d, %esi # EVEX TO LEGACY Compression encoding: [0x44,0x09,0xfe]
; NF-NEXT: xorl %eax, %eax # encoding: [0x31,0xc0]
; NF-NEXT: movl %ebx, %edi # encoding: [0x89,0xdf]
diff --git a/llvm/test/CodeGen/X86/apx/flags-copy-lowering.ll b/llvm/test/CodeGen/X86/apx/flags-copy-lowering.ll
index 0dcda8efdbc78..357126bff417d 100644
--- a/llvm/test/CodeGen/X86/apx/flags-copy-lowering.ll
+++ b/llvm/test/CodeGen/X86/apx/flags-copy-lowering.ll
@@ -51,7 +51,6 @@ define <2 x i128> @flag_copy_2(<2 x i128> %x, <2 x i128> %y) nounwind {
ret <2 x i128> %z
}
-; TODO: Remove the 2nd cmpl by using NF imul.
define void @flag_copy_3(i32 %x, i32 %y, ptr %pa, ptr %pb, ptr %pc) nounwind {
; CHECK-LABEL: flag_copy_3:
; CHECK: # %bb.0: # %entry
@@ -60,14 +59,13 @@ define void @flag_copy_3(i32 %x, i32 %y, ptr %pa, ptr %pb, ptr %pc) nounwind {
; CHECK-NEXT: jl .LBB2_2
; CHECK-NEXT: # %bb.1: # %bb1
; CHECK-NEXT: movl %edi, %eax
-; CHECK-NEXT: imull %esi, %eax
+; CHECK-NEXT: {nf} imull %esi, %eax
; CHECK-NEXT: movl %eax, (%rdx)
; CHECK-NEXT: jmp .LBB2_3
; CHECK-NEXT: .LBB2_2: # %bb2
; CHECK-NEXT: leal -2(%rsi), %eax
; CHECK-NEXT: movl %eax, (%rcx)
; CHECK-NEXT: .LBB2_3: # %bb3
-; CHECK-NEXT: cmpl $2, %edi
; CHECK-NEXT: cmovgel %edi, %esi
; CHECK-NEXT: movl %esi, (%r8)
; CHECK-NEXT: retq
diff --git a/llvm/test/CodeGen/X86/apx/optimize-compare-multipred.ll b/llvm/test/CodeGen/X86/apx/optimize-compare-multipred.ll
new file mode 100644
index 0000000000000..0cf2469f78ba3
--- /dev/null
+++ b/llvm/test/CodeGen/X86/apx/optimize-compare-multipred.ll
@@ -0,0 +1,483 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+nf | FileCheck %s --check-prefixes=NF
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown | FileCheck %s --check-prefixes=NONF
+
+; Tests for optimizeCompareInstr reusing EFLAGS across a block with multiple
+; predecessors via findDominatingRedundantFlagInstr (requires APX NF feature).
+; A compare duplicated by CodeGenPrepare across a diamond can be removed when
+; every path from the dominating producer to the redundant compare only
+; clobbers EFLAGS through NF-convertible instructions.
+
+declare void @ext()
+@g = external global i32
+
+; Positive: the entry compare dominates the redundant compare in bb3; the only
+; EFLAGS clobber on the path (imul on bb1) is NF-convertible, so the second
+; compare is removed and the imul becomes {nf} imul.
+define void @diamond_one_arm(i32 %x, i32 %y, ptr %pa, ptr %pb, ptr %pc) nounwind {
+; NF-LABEL: diamond_one_arm:
+; NF: # %bb.0: # %entry
+; NF-NEXT: # kill: def $esi killed $esi def $rsi
+; NF-NEXT: cmpl $2, %edi
+; NF-NEXT: jl .LBB0_2
+; NF-NEXT: # %bb.1: # %bb1
+; NF-NEXT: movl %edi, %eax
+; NF-NEXT: {nf} imull %esi, %eax
+; NF-NEXT: movl %eax, (%rdx)
+; NF-NEXT: jmp .LBB0_3
+; NF-NEXT: .LBB0_2: # %bb2
+; NF-NEXT: leal -2(%rsi), %eax
+; NF-NEXT: movl %eax, (%rcx)
+; NF-NEXT: .LBB0_3: # %bb3
+; NF-NEXT: cmovgel %edi, %esi
+; NF-NEXT: movl %esi, (%r8)
+; NF-NEXT: retq
+;
+; NONF-LABEL: diamond_one_arm:
+; NONF: # %bb.0: # %entry
+; NONF-NEXT: # kill: def $esi killed $esi def $rsi
+; NONF-NEXT: cmpl $2, %edi
+; NONF-NEXT: jl .LBB0_2
+; NONF-NEXT: # %bb.1: # %bb1
+; NONF-NEXT: movl %edi, %eax
+; NONF-NEXT: imull %esi, %eax
+; NONF-NEXT: movl %eax, (%rdx)
+; NONF-NEXT: jmp .LBB0_3
+; NONF-NEXT: .LBB0_2: # %bb2
+; NONF-NEXT: leal -2(%rsi), %eax
+; NONF-NEXT: movl %eax, (%rcx)
+; NONF-NEXT: .LBB0_3: # %bb3
+; NONF-NEXT: cmpl $2, %edi
+; NONF-NEXT: cmovgel %edi, %esi
+; NONF-NEXT: movl %esi, (%r8)
+; NONF-NEXT: retq
+entry:
+ %cmp = icmp sgt i32 %x, 1
+ br i1 %cmp, label %bb1, label %bb2
+bb1:
+ %mul = mul nuw nsw i32 %x, %y
+ store i32 %mul, ptr %pa
+ br label %bb3
+bb2:
+ %sub = sub nuw nsw i32 %y, 2
+ store i32 %sub, ptr %pb
+ br label %bb3
+bb3:
+ %s = select i1 %cmp, i32 %x, i32 %y
+ store i32 %s, ptr %pc
+ ret void
+}
+
+; Positive: NF-convertible EFLAGS clobbers on BOTH diamond arms. The backward
+; CFG walk collects both, converts both to {nf}, and removes the redundant
+; compare.
+define void @diamond_both_arms(i3...
[truncated]
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
| // support on Linux OS. | ||
| bool IsWithReloc = | ||
| X86EnableAPXForRelocation ? false : isAddMemInstrWithRelocation(MI); | ||
| if (IsWithReloc) |
There was a problem hiding this comment.
| if (IsWithReloc) | |
| if (!X86EnableAPXForRelocation && isAddMemInstrWithRelocation(MI)) |
| MachineDominatorTree MDT(MF); | ||
| MachineDomTreeNode *Node = MDT.getNode(MultiPredMBB); | ||
| MachineInstr *Sub = nullptr; | ||
| for (Node = Node ? Node->getIDom() : nullptr; Node && !Sub; |
There was a problem hiding this comment.
if (!Node)
return nullptr;
Extend optimizeCompareInstr to reuse EFLAGS produced in predecessor blocks (including via NF variants) when a compare is redundant, covering multi-predecessor, dominating, and cyclic CFGs. Share the NF-clobber removal helper with FlagsCopyLowering, correctly handle swapped and immediate-adjusted flag reuse, and simplify EFLAGS live-in marking.
Adds MIR tests exercising multi-predecessor reuse over cyclic and dominating CFGs, including a multi-level idom walk.
Assisted-By: Claude Opus 4.8