[llvm] [X86][APX] Fix EFLAGS reuse across cycles (PR #223613)

Feng Zou via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 15 00:24:44 PDT 2026


https://github.com/fzou1 created https://github.com/llvm/llvm-project/pull/223613

optimizeCompareInstr's multi-predecessor NF reuse (added in ab39c491430e) did not scan the below-CmpInstr region of CmpMBB when CmpMBB was on a CFG cycle: that region is on the back-edge path but is scanned by neither the caller nor the helper's predecessor walk. A non-NF-convertible EFLAGS clobber there (e.g. an unrelated CMP feeding a JCC, as in the reproducer below with VUCOMISS/CTEST/COPY-\$eflags) still let the helper delete the redundant compare and mark \$eflags live-in on CmpMBB. That broke the reused EFLAGS on iteration 2+ and tripped X86FlagsCopyLowering's SuccMBB == TestMBB self-loop check with a fatal report_fatal_error when a preexisting COPY \$eflags save/restore sat in the cycle.

Reproducer: https://godbolt.org/z/s58j5zxqd

Detect CmpMBB on a cycle via a forward-reachability BFS on its successors and, when so, scan from just after CmpInstr to the block's end: bail on any non-NF-convertible EFLAGS clobber, stage NF-convertible ones. The rest of the helper is unchanged.

Tests exercise the bail paths (selfloop_shadow_below, twoblock_cycle_shadow_below, threeblock_cycle_shadow_below, selfloop_nfconvertible_below_cap), the positive stage-and-succeed path (selfloop_nfconvertible_below), and the CmpMBB-not-on-cycle skip path (multipred_above_cmpmbb).

Fixes: https://github.com/llvm/llvm-project/issues/223282

Assisted by Claude Code.

>From bdb8e8ee267c9b54708fb120470c34accb0858d9 Mon Sep 17 00:00:00 2001
From: Feng Zou <feng.zou at intel.com>
Date: Mon, 14 Sep 2026 11:21:09 +0800
Subject: [PATCH] [X86][APX] Fix EFLAGS reuse across cycles in
 findDominatingRedundantFlagInstr

optimizeCompareInstr's multi-predecessor NF reuse (added in ab39c491430e)
did not scan the below-CmpInstr region of CmpMBB when CmpMBB was on a CFG
cycle: that region is on the back-edge path but is scanned by neither the
caller nor the helper's predecessor walk. A non-NF-convertible EFLAGS
clobber there (e.g. an unrelated CMP feeding a JCC, as in the reproducer
below with VUCOMISS/CTEST/COPY-\$eflags) still let the helper delete the
redundant compare and mark \$eflags live-in on CmpMBB. That broke the
reused EFLAGS on iteration 2+ and tripped X86FlagsCopyLowering's
SuccMBB == TestMBB self-loop check with a fatal report_fatal_error when
a preexisting COPY \$eflags save/restore sat in the cycle.

Reproducer: https://godbolt.org/z/s58j5zxqd

Detect CmpMBB on a cycle via a forward-reachability BFS on its
successors and, when so, scan from just after CmpInstr to the block's
end: bail on any non-NF-convertible EFLAGS clobber, stage NF-convertible
ones. The rest of the helper is unchanged.

Tests exercise the bail paths (selfloop_shadow_below,
twoblock_cycle_shadow_below, threeblock_cycle_shadow_below,
selfloop_nfconvertible_below_cap), the positive stage-and-succeed path
(selfloop_nfconvertible_below), and the CmpMBB-not-on-cycle skip path
(multipred_above_cmpmbb).

Fixes: https://github.com/llvm/llvm-project/issues/223282

Assisted by Claude Code.
---
 llvm/lib/Target/X86/X86InstrInfo.cpp          |  46 ++-
 .../X86/apx/optimize-compare-multipred.mir    | 320 ++++++++++++++++++
 2 files changed, 360 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Target/X86/X86InstrInfo.cpp b/llvm/lib/Target/X86/X86InstrInfo.cpp
index ca864d02b3399..16966bed1f531 100644
--- a/llvm/lib/Target/X86/X86InstrInfo.cpp
+++ b/llvm/lib/Target/X86/X86InstrInfo.cpp
@@ -5355,19 +5355,53 @@ MachineInstr *X86InstrInfo::findDominatingRedundantFlagInstr(
   // bailing on any other clobber (it would shadow the producer's flags from
   // CmpInstr).
   //
-  // Clobbers are staged in Pending and committed only on success. Visited
-  // (seeded with MultiPredMBB) stops the walk from revisiting a block or
-  // re-entering the single-predecessor chain, so none is collected twice.
+  // Both scans stage clobbers in Pending and only commit on success. The
+  // predecessor walk seeds Visited with MultiPredMBB so it doesn't re-scan
+  // the single-predecessor chain the caller already staged.
+  //
+  // If CmpMBB is on a CFG cycle, the region below CmpInstr executes on the
+  // back-edge before the next iteration's CmpInstr and is scanned by neither
+  // the caller nor the predecessor walk. Handle it here: bail on any non-
+  // NF-convertible EFLAGS clobber, stage NF-convertible ones.
   //
   // Each NF conversion trades a compact legacy/EVEX-compressed encoding for a
   // wider EVEX (often NDD three-operand) one, growing code size, while the
   // reuse only removes a single compare. Cap the total number of conversions
-  // (those the caller already collected on the single-predecessor chain plus
-  // those the walk stages) so the reuse cannot bloat code just to delete one
-  // compare.
+  // (caller chain + below-scan + predecessor walk) so the reuse cannot bloat
+  // code just to delete one compare.
   MachineInstr *Sub = nullptr;
   MachineBasicBlock *SubMBB = nullptr;
   SmallVector<std::pair<MachineInstr *, unsigned>, 4> Pending;
+
+  MachineBasicBlock *CmpMBB = CmpInstr.getParent();
+  auto IsCmpMBBOnCycle = [&] {
+    SmallPtrSet<MachineBasicBlock *, 8> Seen;
+    SmallVector<MachineBasicBlock *, 8> Stack(CmpMBB->succ_begin(),
+                                              CmpMBB->succ_end());
+    while (!Stack.empty()) {
+      MachineBasicBlock *Succ = Stack.pop_back_val();
+      if (Succ == CmpMBB)
+        return true;
+      if (!Seen.insert(Succ).second)
+        continue;
+      Stack.append(Succ->succ_begin(), Succ->succ_end());
+    }
+    return false;
+  };
+  if (IsCmpMBBOnCycle()) {
+    for (MachineInstr &Inst : make_range(
+             std::next(MachineBasicBlock::iterator(CmpInstr)), CmpMBB->end())) {
+      if (!Inst.modifiesRegister(X86::EFLAGS, TRI))
+        continue;
+      unsigned NewOpc = X86::getNFVariantIfClobberRemovable(Inst, TRI);
+      if (!NewOpc)
+        return nullptr;
+      if (InstsToUpdate.size() + Pending.size() >= MaxNFConversions)
+        return nullptr;
+      Pending.push_back(std::make_pair(&Inst, NewOpc));
+    }
+  }
+
   SmallPtrSet<MachineBasicBlock *, 8> Visited;
   SmallVector<MachineBasicBlock *, 8> Worklist;
   Visited.insert(MultiPredMBB);
diff --git a/llvm/test/CodeGen/X86/apx/optimize-compare-multipred.mir b/llvm/test/CodeGen/X86/apx/optimize-compare-multipred.mir
index a6ff8900a0349..9dcbf7358771e 100644
--- a/llvm/test/CodeGen/X86/apx/optimize-compare-multipred.mir
+++ b/llvm/test/CodeGen/X86/apx/optimize-compare-multipred.mir
@@ -352,3 +352,323 @@ body: |
     $al = SETCCr 15, implicit $eflags
     RET 0, $al
 ...
+
+# Self-loop: bb.1 is its own predecessor. A live CMP below the redundant
+# compare in bb.1 shadows bb.0's producer on iteration 2+. The upfront
+# below-CmpInstr scan spots the clobber and keeps the compare.
+---
+name: selfloop_shadow_below
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: selfloop_shadow_below
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT:   liveins: $edi, $esi
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $edi
+  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gr32 = COPY $esi
+  ; CHECK-NEXT:   CMP32ri [[COPY]], 2, implicit-def $eflags
+  ; CHECK-NEXT:   JMP_1 %bb.1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.1(0x40000000), %bb.2(0x40000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   CMP32ri [[COPY]], 2, implicit-def $eflags
+  ; CHECK-NEXT:   CMP32ri [[COPY1]], 5, implicit-def $eflags
+  ; CHECK-NEXT:   JCC_1 %bb.1, 5, implicit $eflags
+  ; CHECK-NEXT:   JMP_1 %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   liveins: $eflags
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   $al = SETCCr 15, implicit $eflags
+  ; CHECK-NEXT:   RET 0, $al
+  bb.0:
+    liveins: $edi, $esi
+    %0:gr32 = COPY $edi
+    %1:gr32 = COPY $esi
+    CMP32ri %0, 2, implicit-def $eflags
+    JMP_1 %bb.1
+
+  bb.1:
+    CMP32ri %0, 2, implicit-def $eflags
+    CMP32ri %1, 5, implicit-def $eflags
+    JCC_1 %bb.1, 5, implicit $eflags
+    JMP_1 %bb.2
+
+  bb.2:
+    liveins: $eflags
+    $al = SETCCr 15, implicit $eflags
+    RET 0, $al
+...
+
+# Two-block cycle bb.1 -> bb.2 -> bb.1. A live CMP below the redundant compare
+# in bb.1 shadows bb.0's producer on the back-edge. The upfront below-CmpInstr
+# scan spots the clobber and keeps the compare. Guards against a self-loop-only
+# cycle check that would miss this shape.
+---
+name: twoblock_cycle_shadow_below
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: twoblock_cycle_shadow_below
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT:   liveins: $edi, $esi
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $edi
+  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gr32 = COPY $esi
+  ; CHECK-NEXT:   CMP32ri [[COPY]], 2, implicit-def $eflags
+  ; CHECK-NEXT:   JMP_1 %bb.1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   CMP32ri [[COPY]], 2, implicit-def $eflags
+  ; CHECK-NEXT:   CMP32ri [[COPY1]], 5, implicit-def $eflags
+  ; CHECK-NEXT:   JMP_1 %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   successors: %bb.1(0x40000000), %bb.3(0x40000000)
+  ; CHECK-NEXT:   liveins: $eflags
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   JCC_1 %bb.1, 5, implicit $eflags
+  ; CHECK-NEXT:   JMP_1 %bb.3
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.3:
+  ; CHECK-NEXT:   liveins: $eflags
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   $al = SETCCr 15, implicit $eflags
+  ; CHECK-NEXT:   RET 0, $al
+  bb.0:
+    liveins: $edi, $esi
+    %0:gr32 = COPY $edi
+    %1:gr32 = COPY $esi
+    CMP32ri %0, 2, implicit-def $eflags
+    JMP_1 %bb.1
+
+  bb.1:
+    CMP32ri %0, 2, implicit-def $eflags
+    CMP32ri %1, 5, implicit-def $eflags
+    JMP_1 %bb.2
+
+  bb.2:
+    liveins: $eflags
+    JCC_1 %bb.1, 5, implicit $eflags
+    JMP_1 %bb.3
+
+  bb.3:
+    liveins: $eflags
+    $al = SETCCr 15, implicit $eflags
+    RET 0, $al
+...
+
+# Self-loop where the below-CmpInstr region holds an NF-convertible dead clobber.
+# The upfront below-CmpInstr scan stages the IMUL, the backward walk finds
+# bb.0's producer, and the reuse succeeds: the redundant compare is removed
+# and the IMUL is rewritten to its NF variant. Exercises the positive path of
+# the new scan (the shadow_below cases only exercise the bail path).
+---
+name: selfloop_nfconvertible_below
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: selfloop_nfconvertible_below
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT:   liveins: $edi, $esi
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $edi
+  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gr32 = COPY $esi
+  ; CHECK-NEXT:   CMP32ri [[COPY]], 2, implicit-def $eflags
+  ; CHECK-NEXT:   JMP_1 %bb.1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT:   liveins: $eflags
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[IMUL32rr_NF:%[0-9]+]]:gr32 = nuw nsw IMUL32rr_NF [[COPY]], [[COPY1]]
+  ; CHECK-NEXT:   JMP_1 %bb.1
+  bb.0:
+    liveins: $edi, $esi
+    %0:gr32 = COPY $edi
+    %1:gr32 = COPY $esi
+    CMP32ri %0, 2, implicit-def $eflags
+    JMP_1 %bb.1
+
+  bb.1:
+    CMP32ri %0, 2, implicit-def $eflags
+    %2:gr32 = nuw nsw IMUL32rr %0, %1, implicit-def dead $eflags
+    JMP_1 %bb.1
+...
+
+# Multi-predecessor block sits above CmpMBB in the caller's single-predecessor
+# chain: bb.3 (CmpMBB) <- bb.2 <- bb.1 (MultiPredMBB, self-loop). bb.3 has no
+# back-edge so the below-CmpInstr scan is skipped; the walk finds bb.0's
+# producer through bb.1's non-self predecessor. Guards against a regression
+# where a revisit of MultiPredMBB would double-scan the caller's chain.
+---
+name: multipred_above_cmpmbb
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: multipred_above_cmpmbb
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT:   liveins: $edi, $esi
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $edi
+  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gr32 = COPY $esi
+  ; CHECK-NEXT:   CMP32ri [[COPY]], 2, implicit-def $eflags
+  ; CHECK-NEXT:   JMP_1 %bb.1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.1(0x40000000), %bb.2(0x40000000)
+  ; CHECK-NEXT:   liveins: $eflags
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[IMUL32rr_NF:%[0-9]+]]:gr32 = nuw nsw IMUL32rr_NF [[COPY]], [[COPY1]]
+  ; CHECK-NEXT:   JCC_1 %bb.1, 5, implicit undef $eflags
+  ; CHECK-NEXT:   JMP_1 %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   successors: %bb.3(0x80000000)
+  ; CHECK-NEXT:   liveins: $eflags
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   JMP_1 %bb.3
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.3:
+  ; CHECK-NEXT:   liveins: $eflags
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   $al = SETCCr 15, implicit $eflags
+  ; CHECK-NEXT:   RET 0, $al
+  bb.0:
+    liveins: $edi, $esi
+    %0:gr32 = COPY $edi
+    %1:gr32 = COPY $esi
+    CMP32ri %0, 2, implicit-def $eflags
+    JMP_1 %bb.1
+
+  bb.1:
+    %2:gr32 = nuw nsw IMUL32rr %0, %1, implicit-def dead $eflags
+    JCC_1 %bb.1, 5, implicit undef $eflags
+    JMP_1 %bb.2
+
+  bb.2:
+    JMP_1 %bb.3
+
+  bb.3:
+    CMP32ri %0, 2, implicit-def $eflags
+    $al = SETCCr 15, implicit $eflags
+    RET 0, $al
+...
+
+# Three-block cycle bb.2 (CmpMBB) -> bb.3 -> bb.1 (MultiPredMBB) -> bb.2.
+# Exercises the below-CmpInstr scan when MultiPredMBB is a distinct block on
+# the cycle.
+---
+name: threeblock_cycle_shadow_below
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: threeblock_cycle_shadow_below
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT:   liveins: $edi, $esi
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $edi
+  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gr32 = COPY $esi
+  ; CHECK-NEXT:   CMP32ri [[COPY]], 2, implicit-def $eflags
+  ; CHECK-NEXT:   JMP_1 %bb.1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   JMP_1 %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   successors: %bb.4(0x40000000), %bb.3(0x40000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   CMP32ri [[COPY]], 2, implicit-def $eflags
+  ; CHECK-NEXT:   CMP32ri [[COPY1]], 5, implicit-def $eflags
+  ; CHECK-NEXT:   JCC_1 %bb.4, 5, implicit $eflags
+  ; CHECK-NEXT:   JMP_1 %bb.3
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.3:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   JMP_1 %bb.1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.4:
+  ; CHECK-NEXT:   liveins: $eflags
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   $al = SETCCr 15, implicit $eflags
+  ; CHECK-NEXT:   RET 0, $al
+  bb.0:
+    liveins: $edi, $esi
+    %0:gr32 = COPY $edi
+    %1:gr32 = COPY $esi
+    CMP32ri %0, 2, implicit-def $eflags
+    JMP_1 %bb.1
+
+  bb.1:
+    JMP_1 %bb.2
+
+  bb.2:
+    CMP32ri %0, 2, implicit-def $eflags
+    CMP32ri %1, 5, implicit-def $eflags
+    JCC_1 %bb.4, 5, implicit $eflags
+    JMP_1 %bb.3
+
+  bb.3:
+    JMP_1 %bb.1
+
+  bb.4:
+    liveins: $eflags
+    $al = SETCCr 15, implicit $eflags
+    RET 0, $al
+...
+
+# Self-loop where the below-CmpInstr region holds 7 NF-convertible dead clobbers,
+# exceeding the default MaxNFConversions cap of 6. The below-scan declines
+# staging and the redundant compare is kept. Exercises the cap-bail branch of
+# the below-scan.
+---
+name: selfloop_nfconvertible_below_cap
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: selfloop_nfconvertible_below_cap
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT:   liveins: $edi, $esi
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $edi
+  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gr32 = COPY $esi
+  ; CHECK-NEXT:   CMP32ri [[COPY]], 2, implicit-def $eflags
+  ; CHECK-NEXT:   JMP_1 %bb.1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   CMP32ri [[COPY]], 2, implicit-def $eflags
+  ; CHECK-NEXT:   [[IMUL32rr:%[0-9]+]]:gr32 = nuw nsw IMUL32rr [[COPY]], [[COPY1]], implicit-def dead $eflags
+  ; CHECK-NEXT:   [[IMUL32rr1:%[0-9]+]]:gr32 = nuw nsw IMUL32rr [[IMUL32rr]], [[COPY1]], implicit-def dead $eflags
+  ; CHECK-NEXT:   [[IMUL32rr2:%[0-9]+]]:gr32 = nuw nsw IMUL32rr [[IMUL32rr1]], [[COPY1]], implicit-def dead $eflags
+  ; CHECK-NEXT:   [[IMUL32rr3:%[0-9]+]]:gr32 = nuw nsw IMUL32rr [[IMUL32rr2]], [[COPY1]], implicit-def dead $eflags
+  ; CHECK-NEXT:   [[IMUL32rr4:%[0-9]+]]:gr32 = nuw nsw IMUL32rr [[IMUL32rr3]], [[COPY1]], implicit-def dead $eflags
+  ; CHECK-NEXT:   [[IMUL32rr5:%[0-9]+]]:gr32 = nuw nsw IMUL32rr [[IMUL32rr4]], [[COPY1]], implicit-def dead $eflags
+  ; CHECK-NEXT:   [[IMUL32rr6:%[0-9]+]]:gr32 = nuw nsw IMUL32rr [[IMUL32rr5]], [[COPY1]], implicit-def dead $eflags
+  ; CHECK-NEXT:   JMP_1 %bb.1
+  bb.0:
+    liveins: $edi, $esi
+    %0:gr32 = COPY $edi
+    %1:gr32 = COPY $esi
+    CMP32ri %0, 2, implicit-def $eflags
+    JMP_1 %bb.1
+
+  bb.1:
+    CMP32ri %0, 2, implicit-def $eflags
+    %2:gr32 = nuw nsw IMUL32rr %0, %1, implicit-def dead $eflags
+    %3:gr32 = nuw nsw IMUL32rr %2, %1, implicit-def dead $eflags
+    %4:gr32 = nuw nsw IMUL32rr %3, %1, implicit-def dead $eflags
+    %5:gr32 = nuw nsw IMUL32rr %4, %1, implicit-def dead $eflags
+    %6:gr32 = nuw nsw IMUL32rr %5, %1, implicit-def dead $eflags
+    %7:gr32 = nuw nsw IMUL32rr %6, %1, implicit-def dead $eflags
+    %8:gr32 = nuw nsw IMUL32rr %7, %1, implicit-def dead $eflags
+    JMP_1 %bb.1
+...



More information about the llvm-commits mailing list