Repository: llvm/llvm-project
Pull request #132236: [RISCV][VLOPT] Look through PHI instructions
Author: login: michaelmaitland
Labels: name: backend:RISC-V
Created: 2025-03-20T15:16:43Z | Merged: 2025-03-24T13:26:09Z

Similar to what we do for copies. We may reduce one of the PHI operands and not the other, and thats perfectly okay.


Diff:
diff --git a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
index c36a1e9adccb0..9ed2ba274bc53 100644
--- a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
+++ b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
@@ -1323,6 +1323,7 @@ std::optional<MachineOperand>
 RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
   std::optional<MachineOperand> CommonVL;
   SmallSetVector<MachineOperand *, 8> Worklist;
+  SmallPtrSet<const MachineInstr *, 4> PHISeen;
   for (auto &UserOp : MRI->use_operands(MI.getOperand(0).getReg()))
     Worklist.insert(&UserOp);
 
@@ -1340,6 +1341,16 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
       continue;
     }
 
+    if (UserMI.isPHI()) {
+      // Don't follow PHI cycles
+      if (!PHISeen.insert(&UserMI).second)
+        continue;
+      LLVM_DEBUG(dbgs() << "    Peeking through uses of PHI\n");
+      for (auto &PhiUse : MRI->use_operands(UserMI.getOperand(0).getReg()))
+        Worklist.insert(&PhiUse);
+      continue;
+    }
+
     auto VLOp = getMinimumVLForUser(UserOp);
     if (!VLOp)
       return std::nullopt;
diff --git a/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir b/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir
index d42feeca9dbcc..6343afc6bac62 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir
+++ b/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir
@@ -368,3 +368,134 @@ body: |
     %y:vr = COPY %x
     %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 4 /* e16 */, 0 /* tu, mu */
 ...
+---
+name: phi
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: phi
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.2(0x40000000), %bb.1(0x40000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %w:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT:   BNE $noreg, $noreg, %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   %y:vr = PHI %w, %bb.0, %x, %bb.1
+  ; CHECK-NEXT:   %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  bb.0:
+    %w:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+    BNE $noreg, $noreg, %bb.2
+  bb.1:
+    %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+  bb.2:
+    %y:vr = PHI %w, %bb.0, %x, %bb.1
+    %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+...
+---
+name: phi_user_invalid_sew
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: phi_user_invalid_sew
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.2(0x40000000), %bb.1(0x40000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %w:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT:   BNE $noreg, $noreg, %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   %y:vr = PHI %w, %bb.0, %x, %bb.1
+  ; CHECK-NEXT:   %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 4 /* e16 */, 0 /* tu, mu */
+  bb.0:
+    %w:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+    BNE $noreg, $noreg, %bb.2
+  bb.1:
+    %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+  bb.2:
+    %y:vr = PHI %w, %bb.0, %x, %bb.1
+    %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 4 /* e16 */, 0 /* tu, mu */
+...
+---
+name: phi_different_incoming_sew
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: phi_different_incoming_sew
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.2(0x40000000), %bb.1(0x40000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %w:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT:   BNE $noreg, $noreg, %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 4 /* e16 */, 0 /* tu, mu */
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   %y:vr = PHI %w, %bb.0, %x, %bb.1
+  ; CHECK-NEXT:   %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  bb.0:
+    %w:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+    BNE $noreg, $noreg, %bb.2
+  bb.1:
+    %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 4 /* e16 */, 0 /* tu, mu */
+  bb.2:
+    %y:vr = PHI %w, %bb.0, %x, %bb.1
+    %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+...
+---
+name: phi_cycle_direct
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: phi_cycle_direct
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %y:vr = PHI %x, %bb.0, %y, %bb.1
+  ; CHECK-NEXT:   %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT:   PseudoBR %bb.1
+  bb.0:
+    %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+  bb.1:
+    %y:vr = PHI %x, %bb.0, %y, %bb.1
+    %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+    PseudoBR %bb.1
+...
+---
+name: phi_cycle_indirect
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: phi_cycle_indirect
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %y:vr = PHI %x, %bb.0, %z, %bb.1
+  ; CHECK-NEXT:   %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT:   PseudoBR %bb.1
+  bb.0:
+    %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+  bb.1:
+    %y:vr = PHI %x, %bb.0, %z, %bb.1
+    %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+    PseudoBR %bb.1
+...
+


Reviews:
user: login: topperc
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-20T15:27:53Z
user: login: lukel97
body: Nice, is there a specific workload you're seeing that motivates this?
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T15:31:35Z
user: login: lukel97
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T15:46:00Z
user: login: topperc
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-20T17:52:03Z
user: login: michaelmaitland
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T17:54:50Z
user: login: topperc
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-20T17:58:28Z
user: login: michaelmaitland
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T20:02:08Z
user: login: lukel97
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T11:21:18Z
user: login: michaelmaitland
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T13:10:32Z
user: login: lukel97
body: LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T13:21:46Z

Review comments:
diff_hunk: @@ -1340,6 +1340,13 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
       continue;
     }
 
+    if (UserMI.isPHI()) {
+      LLVM_DEBUG(dbgs() << "    Peeking through uses of PHI\n");
+      for (auto &PhiUse : MRI->use_operands(UserMI.getOperand(0).getReg()))
path: llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
user: login: topperc
body: Do we need to do anything to not go through a phi cycle indefinitely?
created_at: 2025-03-20T15:27:53Z
author_association: COLLABORATOR
diff_hunk: @@ -1340,6 +1340,13 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
       continue;
     }
 
+    if (UserMI.isPHI()) {
+      LLVM_DEBUG(dbgs() << "    Peeking through uses of PHI\n");
+      for (auto &PhiUse : MRI->use_operands(UserMI.getOperand(0).getReg()))
+        Worklist.insert(&PhiUse);
path: llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
user: login: lukel97
body: Do you have a test for a recurrence phi? I think locally I needed a "seen" set of pointers otherwise you ended up with infinite loops: https://github.com/lukel97/llvm-project/blob/a82b6466715ae03e5698a323cb2daae45dd2bded/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp#L1331-L1353
created_at: 2025-03-20T15:29:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1340,6 +1340,13 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
       continue;
     }
 
+    if (UserMI.isPHI()) {
+      LLVM_DEBUG(dbgs() << "    Peeking through uses of PHI\n");
+      for (auto &PhiUse : MRI->use_operands(UserMI.getOperand(0).getReg()))
+        Worklist.insert(&PhiUse);
path: llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
user: login: lukel97
body: Oops I forgot to add context to that link. That branch is a WIP patch I have that generalizes the RISCVVLOptimizer pass to a dataflow analysis. I originally added it because I thought we needed to handle recurrences when removing vp intrinsics. But I didn't pursue it any further because it didn't have any impact on SPEC.
created_at: 2025-03-20T15:46:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1340,6 +1341,17 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
       continue;
     }
 
+    if (UserMI.isPHI() && PHISeen.insert(&UserMI).second) {
+      LLVM_DEBUG(dbgs() << "    Peeking through uses of PHI\n");
+      for (auto &PhiUse : MRI->use_operands(UserMI.getOperand(0).getReg())) {
+        // If UserMI has a PHI cycle, don't analyze it.
+        if (PhiUse.getParent() == &UserMI)
path: llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
user: login: topperc
body: You can have a cycle without the phi directly using itself. The phi using itself is a more unusual case. The common case is there being some operation modifying the phi and then the result of that being used by the phi.

Example:

preheader:
  ...
loop:
  a = phi [preheader, 0], [loop, b]
  b = add a, 1
  ...
  branch to loop
created_at: 2025-03-20T17:52:03Z
author_association: COLLABORATOR
diff_hunk: @@ -1340,6 +1341,17 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
       continue;
     }
 
+    if (UserMI.isPHI() && PHISeen.insert(&UserMI).second) {
+      LLVM_DEBUG(dbgs() << "    Peeking through uses of PHI\n");
+      for (auto &PhiUse : MRI->use_operands(UserMI.getOperand(0).getReg())) {
+        // If UserMI has a PHI cycle, don't analyze it.
+        if (PhiUse.getParent() == &UserMI)
path: llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
user: login: michaelmaitland
body: I think I handle that case too in the if statement with the `insert` check. I added a test for it.
created_at: 2025-03-20T17:54:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1340,6 +1341,17 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
       continue;
     }
 
+    if (UserMI.isPHI() && PHISeen.insert(&UserMI).second) {
+      LLVM_DEBUG(dbgs() << "    Peeking through uses of PHI\n");
+      for (auto &PhiUse : MRI->use_operands(UserMI.getOperand(0).getReg())) {
+        // If UserMI has a PHI cycle, don't analyze it.
+        if (PhiUse.getParent() == &UserMI)
path: llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
user: login: topperc
body: Oh sorry. I didn't see the PHISeen check. I only saw the comment.
created_at: 2025-03-20T17:58:28Z
author_association: COLLABORATOR
diff_hunk: @@ -1340,6 +1340,13 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
       continue;
     }
 
+    if (UserMI.isPHI()) {
+      LLVM_DEBUG(dbgs() << "    Peeking through uses of PHI\n");
+      for (auto &PhiUse : MRI->use_operands(UserMI.getOperand(0).getReg()))
+        Worklist.insert(&PhiUse);
path: llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
user: login: michaelmaitland
body: Test added.
created_at: 2025-03-20T20:02:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1340,6 +1341,17 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
       continue;
     }
 
+    if (UserMI.isPHI() && PHISeen.insert(&UserMI).second) {
+      LLVM_DEBUG(dbgs() << "    Peeking through uses of PHI\n");
+      for (auto &PhiUse : MRI->use_operands(UserMI.getOperand(0).getReg())) {
+        // If UserMI has a PHI cycle, don't analyze it.
+        if (PhiUse.getParent() == &UserMI)
path: llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
user: login: lukel97
body: Do we need this `PhiUse.getParent() == &UserMI` check here? Does the PHISeen.insert check above already take care of the direct cycle case
created_at: 2025-03-24T11:20:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1340,6 +1341,17 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
       continue;
     }
 
+    if (UserMI.isPHI() && PHISeen.insert(&UserMI).second) {
+      LLVM_DEBUG(dbgs() << "    Peeking through uses of PHI\n");
+      for (auto &PhiUse : MRI->use_operands(UserMI.getOperand(0).getReg())) {
+        // If UserMI has a PHI cycle, don't analyze it.
+        if (PhiUse.getParent() == &UserMI)
path: llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
user: login: michaelmaitland
body: Thanks for calling this out. I revised the logic.
created_at: 2025-03-24T13:10:32Z
author_association: CONTRIBUTOR

PR comments:
user: login: llvmbot
created_at: 2025-03-20T15:17:07Z
body: 
<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-risc-v

Author: Michael Maitland (michaelmaitland)

<details>
<summary>Changes</summary>

Similar to what we do for copies. We may reduce one of the PHI operands and not the other, and thats perfectly okay.

---
Full diff: https://github.com/llvm/llvm-project/pull/132236.diff


2 Files Affected:

- (modified) llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp (+7) 
- (modified) llvm/test/CodeGen/RISCV/rvv/vl-opt.mir (+84) 


``````````diff
diff --git a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
index c36a1e9adccb0..61f43e2e72b13 100644
--- a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
+++ b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
@@ -1340,6 +1340,13 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
       continue;
     }
 
+    if (UserMI.isPHI()) {
+      LLVM_DEBUG(dbgs() << "    Peeking through uses of PHI\n");
+        for (auto &PhiUse : MRI->use_operands(UserMI.getOperand(0).getReg()))
+          Worklist.insert(&PhiUse);
+      continue;
+    }
+
     auto VLOp = getMinimumVLForUser(UserOp);
     if (!VLOp)
       return std::nullopt;
diff --git a/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir b/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir
index d42feeca9dbcc..dfc4dd84c72b2 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir
+++ b/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir
@@ -368,3 +368,87 @@ body: |
     %y:vr = COPY %x
     %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 4 /* e16 */, 0 /* tu, mu */
 ...
+---
+name: phi
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: phi
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.2(0x40000000), %bb.1(0x40000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %w:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT:   BNE $noreg, $noreg, %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   %y:vr = PHI %w, %bb.0, %x, %bb.1
+  ; CHECK-NEXT:   %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  bb.0:
+    %w:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+    BNE $noreg, $noreg, %bb.2
+  bb.1:
+    %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+  bb.2:
+    %y:vr = PHI %w, %bb.0, %x, %bb.1
+    %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+...
+---
+name: phi_user_invalid_sew
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: phi_user_invalid_sew
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.2(0x40000000), %bb.1(0x40000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %w:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT:   BNE $noreg, $noreg, %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   %y:vr = PHI %w, %bb.0, %x, %bb.1
+  ; CHECK-NEXT:   %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 4 /* e16 */, 0 /* tu, mu */
+  bb.0:
+    %w:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+    BNE $noreg, $noreg, %bb.2
+  bb.1:
+    %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+  bb.2:
+    %y:vr = PHI %w, %bb.0, %x, %bb.1
+    %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 4 /* e16 */, 0 /* tu, mu */
+...
+---
+name: phi_different_incoming_sew
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: phi_different_incoming_sew
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.2(0x40000000), %bb.1(0x40000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %w:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  ; CHECK-NEXT:   BNE $noreg, $noreg, %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 4 /* e16 */, 0 /* tu, mu */
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   %y:vr = PHI %w, %bb.0, %x, %bb.1
+  ; CHECK-NEXT:   %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+  bb.0:
+    %w:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
+    BNE $noreg, $noreg, %bb.2
+  bb.1:
+    %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 4 /* e16 */, 0 /* tu, mu */
+  bb.2:
+    %y:vr = PHI %w, %bb.0, %x, %bb.1
+    %z:vr = PseudoVADD_VV_M1 $noreg, %y, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+...

``````````

</details>

author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-03-20T15:20:19Z
body: <!--LLVM CODE FORMAT COMMENT: {clang-format}-->

:white_check_mark: With the latest revision this PR passed the C/C++ code formatter.
author_association: NONE
user: login: preames
created_at: 2025-03-20T15:26:59Z
body: > Similar to what we do for copies. We may reduce one of the PHI operands and not the other, and thats perfectly okay.

Unlike COPY, PHI allows the existence of cycles in the def/use graph.  Usually, when traversing phis we need some kind of visited set to bailout if we get trapped in a cycle.  Is there some non-obvious invariant in the code here which side steps that?

Classic test case would be something like:
```
%V = some_vector_instruction
%phi = phi [%V, %loop_header], [%phi, %loop]
```

author_association: COLLABORATOR
user: login: michaelmaitland
created_at: 2025-03-20T15:36:23Z
body: > Nice, is there a specific workload you're seeing that motivates this?

We were experimenting with not using vp intrinsics and saw it in x264.
author_association: CONTRIBUTOR
user: login: michaelmaitland
created_at: 2025-03-20T15:43:12Z
body: @preames @topperc @lukel97 thanks for pointing this out. I've updated the patch to avoid an infinite loop. I think it is conservative in that case since the user in the loop body is causing us not to optimize. Maybe we can handle that in a follow up.

@lukel97 thanks for sharing your patch, I''ll take a closer look now.
author_association: CONTRIBUTOR