[llvm] [AMDGPU] Post-RA Peephole for Two-Address Instructions (PR #207731)

Lukas Sommer via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 15 02:58:18 PDT 2026


https://github.com/sommerlukas updated https://github.com/llvm/llvm-project/pull/207731

>From 62c946210fa51ac24a74d00bef2f44c4cb1b5221 Mon Sep 17 00:00:00 2001
From: Lukas Sommer <lukas.sommer at amd.com>
Date: Fri, 3 Jul 2026 11:19:06 -0500
Subject: [PATCH 1/6] Initial implementation of post-RA peephole

Signed-off-by: Lukas Sommer <lukas.sommer at amd.com>
---
 llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp | 179 ++++++++++++++++++-
 1 file changed, 175 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp b/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
index a496c9a4daa71..22c6aeec437f8 100644
--- a/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
+++ b/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
@@ -26,6 +26,7 @@
 #include "llvm/CodeGen/MachineFunctionPass.h"
 #include "llvm/CodeGen/MachineLoopInfo.h"
 #include "llvm/CodeGen/MachinePostDominators.h"
+#include "llvm/CodeGen/ReachingDefAnalysis.h"
 #include "llvm/CodeGen/TargetSchedule.h"
 #include "llvm/Support/BranchProbability.h"
 using namespace llvm;
@@ -39,6 +40,7 @@ class SIPreEmitPeephole {
   const SIInstrInfo *TII = nullptr;
   const SIRegisterInfo *TRI = nullptr;
   MachineLoopInfo *MLI = nullptr;
+  const ReachingDefInfo *RDI = nullptr;
 
   bool optimizeVccBranch(MachineInstr &MI) const;
   void updateMLIBeforeRemovingEdge(MachineBasicBlock *From,
@@ -81,8 +83,34 @@ class SIPreEmitPeephole {
   void addOperandAndMods(MachineInstrBuilder &NewMI, unsigned SrcMods,
                          bool IsHiBits, const MachineOperand &SrcMO);
 
+  // Delayed post-RA conversion from a two-address instruction to a
+  // three-address instruction. The copy instructions become redundant after
+  // retargeting the converted instruction to NewDst.
+  struct ThreeAddressCandidate {
+    MachineInstr *TwoAddrMI;
+    Register NewDst;
+    SmallVector<MachineInstr *, 2> CopiesToRemove;
+
+    ThreeAddressCandidate(MachineInstr &TwoAddrMI, Register NewDst,
+                          ArrayRef<MachineInstr *> Copies)
+        : TwoAddrMI(&TwoAddrMI), NewDst(NewDst), CopiesToRemove(Copies) {}
+  };
+  // Check if \p MaybeCopy is a simple copy of \p Src. If it is a supported
+  // copy, return the destination register of the copy.
+  std::optional<Register> checkCopy(MachineInstr &TwoAddress,
+                                    MachineInstr &MaybeCopy, Register Src);
+  // Check whether the two-address instruction should be replaced with a
+  // three-address instruction to avoid copies of the output registers. If the
+  // replacement should take place, returns the destination register for the
+  // replacement and fills \p Copies with the copy instructions to remove after
+  // the replacement.
+  Register
+  shouldReplaceWithThreeAddress(MachineInstr &MI,
+                                SmallVectorImpl<MachineInstr *> &Copies);
+
 public:
-  bool run(MachineFunction &MF, MachineLoopInfo *MLI);
+  bool run(MachineFunction &MF, MachineLoopInfo *MLI,
+           const ReachingDefInfo *RDI);
 };
 
 class SIPreEmitPeepholeLegacy : public MachineFunctionPass {
@@ -94,13 +122,16 @@ class SIPreEmitPeepholeLegacy : public MachineFunctionPass {
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.addUsedIfAvailable<MachineLoopInfoWrapperPass>();
     AU.addPreserved<MachineLoopInfoWrapperPass>();
+    AU.addRequired<ReachingDefInfoWrapperPass>();
     MachineFunctionPass::getAnalysisUsage(AU);
   }
 
   bool runOnMachineFunction(MachineFunction &MF) override {
     auto *MLIWrapper = getAnalysisIfAvailable<MachineLoopInfoWrapperPass>();
     MachineLoopInfo *MLI = MLIWrapper ? &MLIWrapper->getLI() : nullptr;
-    return SIPreEmitPeephole().run(MF, MLI);
+    const ReachingDefInfo *RDI =
+        &getAnalysis<ReachingDefInfoWrapperPass>().getRDI();
+    return SIPreEmitPeephole().run(MF, MLI, RDI);
   }
 };
 
@@ -763,13 +794,99 @@ MachineInstrBuilder SIPreEmitPeephole::createUnpackedMI(MachineInstr &I,
   return NewMI;
 }
 
+std::optional<Register> SIPreEmitPeephole::checkCopy(MachineInstr &TwoAddress,
+                                                     MachineInstr &MaybeCopy,
+                                                     Register Src) {
+  if (MaybeCopy.isBundle())
+    return std::nullopt;
+
+  switch (MaybeCopy.getOpcode()) {
+  case AMDGPU::COPY:
+  case AMDGPU::V_MOV_B32_e32:
+    break;
+  default:
+    return std::nullopt;
+  }
+
+  MachineOperand CopySrc = MaybeCopy.getOperand(1);
+  if (!CopySrc.isReg() || CopySrc.getReg() != Src)
+    return std::nullopt;
+
+  MachineOperand Dst = MaybeCopy.getOperand(0);
+  if (!Dst.isReg())
+    return std::nullopt;
+
+  Register Reg = Dst.getReg();
+  SmallPtrSet<MachineInstr *, 1> Ignore{&TwoAddress, &MaybeCopy};
+  if (!RDI->isSafeToDefRegAt(&TwoAddress, Reg, Ignore))
+    return std::nullopt;
+
+  if (!RDI->hasSameReachingDef(&TwoAddress, &MaybeCopy, AMDGPU::EXEC))
+    return std::nullopt;
+
+  return Reg;
+}
+
+Register SIPreEmitPeephole::shouldReplaceWithThreeAddress(
+    MachineInstr &MI, SmallVectorImpl<MachineInstr *> &Copies) {
+  MachineOperand *Dst = TII->getNamedOperand(MI, AMDGPU::OpName::vdst);
+  if (!Dst || !Dst->isReg())
+    return Register();
+
+  SmallVector<Register, 2> DestRegs;
+  Register Sub0 = TRI->getSubReg(Dst->getReg(), AMDGPU::sub0);
+  Register Sub1 = TRI->getSubReg(Dst->getReg(), AMDGPU::sub1);
+  if (Sub0 && Sub1) {
+    DestRegs.push_back(Sub0);
+    DestRegs.push_back(Sub1);
+  } else
+    DestRegs.push_back(Dst->getReg());
+
+  SmallVector<Register, 2> Replacements;
+  for (Register SubReg : DestRegs) {
+    if (!SubReg.isPhysical())
+      return Register();
+    if (RDI->isReachingDefLiveOut(&MI, SubReg))
+      return Register();
+
+    SmallPtrSet<MachineInstr *, 1> Uses;
+    RDI->getReachingLocalUses(&MI, SubReg, Uses);
+    if (Uses.size() != 1)
+      return Register();
+
+    MachineInstr &Use = **Uses.begin();
+    std::optional<Register> MaybeRegister = checkCopy(MI, Use, SubReg);
+
+    if (!MaybeRegister)
+      return Register();
+    Replacements.push_back(*MaybeRegister);
+    Copies.push_back(&Use);
+  }
+
+  if (Replacements.size() > 2)
+    return Register();
+
+  if (Replacements.size() == 1)
+    return Replacements.front();
+
+  Register Tuple = TRI->getMatchingSuperReg(Replacements.front(), AMDGPU::sub0,
+                                            TRI->getVGPR64Class());
+  if (!Tuple)
+    return Register();
+
+  if (TRI->getSubReg(Tuple, AMDGPU::sub1) != Replacements[1].asMCReg())
+    return Register();
+  return Tuple;
+}
+
 PreservedAnalyses
 llvm::SIPreEmitPeepholePass::run(MachineFunction &MF,
                                  MachineFunctionAnalysisManager &MFAM) {
   auto *MLI = MFAM.getCachedResult<MachineLoopAnalysis>(MF);
+  const ReachingDefInfo &RDI = MFAM.getResult<ReachingDefAnalysis>(MF);
   SIPreEmitPeephole Impl;
 
-  if (Impl.run(MF, MLI)) {
+  if (Impl.run(MF, MLI, &RDI)) {
     auto PA = getMachineFunctionPassPreservedAnalyses();
     PA.preserve<MachineLoopAnalysis>();
     return PA;
@@ -778,11 +895,13 @@ llvm::SIPreEmitPeepholePass::run(MachineFunction &MF,
   return PreservedAnalyses::all();
 }
 
-bool SIPreEmitPeephole::run(MachineFunction &MF, MachineLoopInfo *LoopInfo) {
+bool SIPreEmitPeephole::run(MachineFunction &MF, MachineLoopInfo *LoopInfo,
+                            const ReachingDefInfo *ReachingDefInfo) {
   const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
   TII = ST.getInstrInfo();
   TRI = &TII->getRegisterInfo();
   MLI = LoopInfo;
+  RDI = ReachingDefInfo;
   bool Changed = false;
 
   MF.RenumberBlocks();
@@ -803,6 +922,58 @@ bool SIPreEmitPeephole::run(MachineFunction &MF, MachineLoopInfo *LoopInfo) {
       }
     }
 
+    // Delayed optimization to replace two-address instructions followed by
+    // copies with a three-address instruction that directly writes to the
+    // copied registers instead. The pattern we seek to optimize is:
+    // $vgpr2_vgpr3 = V_FMAC_F64 ..., $vgpr_2_vgpr3
+    // $vgpr0 = V_MOV_B32 $vgrp2
+    // $vgpr1 = V_MOV_B32 $vgrp3
+    //
+    // This can be optimized to:
+    // $vgpr0_vgpr1 = V_FMA_F64 ..., $vgpr2_vgpr3
+    //
+    // The TwoAddressInstruction pass handles some cases, but bails in more
+    // complex cases.
+    SmallVector<ThreeAddressCandidate> Candidates;
+    // First, collect the candidates. We can't perform the transformation right
+    // away, it would invalidate the iterator.
+    for (auto &MI : make_early_inc_range(MBB.instrs())) {
+      if (MI.isBundle())
+        continue;
+
+      unsigned Opc = MI.getOpcode();
+      if (Opc != AMDGPU::V_FMAC_F64_e64 && Opc != AMDGPU::V_FMAC_F32_e64 &&
+          Opc != AMDGPU::V_FMAC_F64_e32 && Opc != AMDGPU::V_FMAC_F32_e32)
+        continue;
+
+      SmallVector<MachineInstr *, 2> Copies;
+      if (Register Dst = shouldReplaceWithThreeAddress(MI, Copies))
+        Candidates.emplace_back(MI, Dst, Copies);
+    }
+
+    for (ThreeAddressCandidate &Cand : Candidates) {
+      MachineInstr *ThreeAddrsInst =
+          TII->convertToThreeAddress(*Cand.TwoAddrMI, nullptr, nullptr);
+      if (!ThreeAddrsInst)
+        continue;
+      MachineOperand &NewDst = ThreeAddrsInst->getOperand(0);
+      assert(NewDst.isReg());
+
+      const TargetRegisterClass *DstRC =
+          TII->getRegClass(ThreeAddrsInst->getDesc(), NewDst.getOperandNo());
+      if (DstRC && !DstRC->contains(Cand.NewDst.asMCReg())) {
+        ThreeAddrsInst->eraseFromParent();
+        continue;
+      }
+
+      NewDst.setReg(Cand.NewDst);
+      NewDst.setIsRenamable(false);
+      Cand.TwoAddrMI->eraseFromParent();
+      llvm::for_each(Cand.CopiesToRemove,
+                     [](MachineInstr *Copy) { Copy->eraseFromParent(); });
+      Changed = true;
+    }
+
     if (!ST.hasVGPRIndexMode())
       continue;
 

>From 650ab3de0d5b2e96328c4c33f4c8f8f35c9021d4 Mon Sep 17 00:00:00 2001
From: Lukas Sommer <lukas.sommer at amd.com>
Date: Mon, 6 Jul 2026 07:00:27 -0500
Subject: [PATCH 2/6] Add and update tests

Signed-off-by: Lukas Sommer <lukas.sommer at amd.com>
---
 .../CodeGen/AMDGPU/hazard-pass-ordering.mir   |   3 +
 .../AMDGPU/insert-handle-flat-vmem-ds.mir     |   3 +
 .../CodeGen/AMDGPU/insert-skip-from-vcc.mir   |  49 ++++++
 .../CodeGen/AMDGPU/insert-skips-gfx10.mir     |   7 +
 .../CodeGen/AMDGPU/insert-skips-gfx12.mir     |  20 +++
 .../CodeGen/AMDGPU/insert-skips-gfx1250.mir   |   2 +
 llvm/test/CodeGen/AMDGPU/insert-skips-gws.mir |   2 +
 .../AMDGPU/insert-skips-ignored-insts.mir     |   1 +
 llvm/test/CodeGen/AMDGPU/llc-pipeline.ll      |   4 +
 ...hort-exec-branch-on-unconditional-jump.mir |   1 +
 ...emove-short-exec-branches-gpr-idx-mode.mir |   4 +
 ...ort-exec-branches-special-instructions.mir |  10 ++
 .../CodeGen/AMDGPU/set-gpr-idx-peephole.mir   | 164 +++++++++++++++---
 .../AMDGPU/si-pre-emit-peephole-fmac.mir      |  38 ++++
 ...i-pre-emit-peephole-preserve-loop-info.mir |   9 +
 .../AMDGPU/skip-branch-taildup-ret.mir        |   2 +
 16 files changed, 295 insertions(+), 24 deletions(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir

diff --git a/llvm/test/CodeGen/AMDGPU/hazard-pass-ordering.mir b/llvm/test/CodeGen/AMDGPU/hazard-pass-ordering.mir
index aedd20ee15283..656bd9363458b 100644
--- a/llvm/test/CodeGen/AMDGPU/hazard-pass-ordering.mir
+++ b/llvm/test/CodeGen/AMDGPU/hazard-pass-ordering.mir
@@ -10,8 +10,11 @@
 # GCN-NEXT: s_nop
 # GCN-NEXT: flat_load_dword
 name:            mai_hazard_pass_ordering_optimize_vcc_branch
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $agpr0, $vgpr1
+
     $vgpr0 = V_MOV_B32_e32 1, implicit $exec
     $vgpr2 = V_ACCVGPR_READ_B32_e64 killed $agpr0, implicit $exec
     $sgpr8_sgpr9 = S_MOV_B64 -1
diff --git a/llvm/test/CodeGen/AMDGPU/insert-handle-flat-vmem-ds.mir b/llvm/test/CodeGen/AMDGPU/insert-handle-flat-vmem-ds.mir
index 22f1013f207e9..b3426c08c8b58 100644
--- a/llvm/test/CodeGen/AMDGPU/insert-handle-flat-vmem-ds.mir
+++ b/llvm/test/CodeGen/AMDGPU/insert-handle-flat-vmem-ds.mir
@@ -5,6 +5,7 @@
 ---
 
 name: skip_execz_flat
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_execz_flat
   ; CHECK: bb.0:
@@ -34,6 +35,7 @@ body: |
 ---
 
 name: skip_execz_mubuf
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_execz_mubuf
   ; CHECK: bb.0:
@@ -63,6 +65,7 @@ body: |
 ---
 
 name: skip_execz_ds
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_execz_ds
   ; CHECK: bb.0:
diff --git a/llvm/test/CodeGen/AMDGPU/insert-skip-from-vcc.mir b/llvm/test/CodeGen/AMDGPU/insert-skip-from-vcc.mir
index 27f84ca8e3b5c..518aef23205a3 100644
--- a/llvm/test/CodeGen/AMDGPU/insert-skip-from-vcc.mir
+++ b/llvm/test/CodeGen/AMDGPU/insert-skip-from-vcc.mir
@@ -8,6 +8,7 @@
 # GCN-NOT: S_AND_
 # GCN: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            and_execz_mov_vccz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -26,6 +27,7 @@ body:             |
 # GCN-NOT: S_AND_
 # GCN: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            and_execz_imm_vccz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -43,6 +45,7 @@ body:             |
 # GCN-NOT: S_AND_
 # GCN: S_CBRANCH_EXECNZ %bb.1, implicit $exec
 name:            and_execnz_imm_vccnz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -60,6 +63,7 @@ body:             |
 # GCN: $vcc = S_AND_B64 $exec, -1, implicit-def $scc
 # GCN: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            and_execz_imm_vccz_live_scc
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -78,6 +82,7 @@ body:             |
 # GCN: $vcc = S_AND_B64 $exec, -1, implicit-def $scc
 # GCN: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            and_execz_mov_vccz_live_scc
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -96,6 +101,7 @@ body:             |
 # GCN:      $sgpr0_sgpr1 = S_MOV_B64 -1
 # GCN-NEXT: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            and_execz_mov_vccz_live_sreg
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -114,6 +120,7 @@ body:             |
 # GCN:      $sgpr0_sgpr1 = S_MOV_B64 -1
 # GCN-NEXT: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            and_execz_mov_vccz_live_sreg_commute
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -133,6 +140,7 @@ body:             |
 # GCN: $vcc = S_AND_B64 $exec, -1, implicit-def $scc
 # GCN: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            and_execz_mov_vccz_live_scc_commute
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -152,6 +160,7 @@ body:             |
 # GCN-NOT: S_AND_
 # GCN: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            and_execz_mov_vccz_commute
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -170,6 +179,7 @@ body:             |
 # GCN:      $exec = S_MOV_B64 -1
 # GCN-NEXT: S_ENDPGM 0
 name:            and_execz_mov_exec_vccz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -188,6 +198,7 @@ body:             |
 # GCN:      $exec = S_MOV_B64 -1
 # GCN-NEXT: S_BRANCH %bb.1{{$}}
 name:            and_execz_mov_exec_vccnz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -207,6 +218,7 @@ body:             |
 # GCN-NEXT: $sgpr2 = S_MOV_B32 $sgpr1
 # GCN-NEXT: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            and_execz_mov_vccz_reads_sreg_early
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -227,6 +239,7 @@ body:             |
 # GCN-NEXT: $sgpr2 = S_MOV_B32 $sgpr1
 # GCN-NEXT: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            and_execz_mov_vccz_reads_sreg_late
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -247,6 +260,7 @@ body:             |
 # GCN-NEXT: $vcc = S_AND_B64 $exec, killed $sgpr0_sgpr1, implicit-def dead $scc
 # GCN-NEXT: S_CBRANCH_VCCZ %bb.1, implicit killed $vcc
 name:            and_execz_mov_vccz_reads_writes_sreg_early
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -267,6 +281,7 @@ body:             |
 # GCN-NEXT: $sgpr2 = S_MOV_B32 $vcc_lo
 # GCN-NEXT: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            and_execz_mov_vccz_reads_cond
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -288,6 +303,7 @@ body:             |
 # GCN-NEXT: $vcc = S_AND_B64 $exec, killed $sgpr0_sgpr1, implicit-def dead $scc
 # GCN-NEXT: S_CBRANCH_VCCZ %bb.1, implicit killed $vcc
 name:            and_execz_mov_vccz_modifies_sreg
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -308,6 +324,7 @@ body:             |
 # GCN-NEXT: S_CBRANCH_EXECZ %bb.1, implicit $exec
 # GCN-NEXT: S_ENDPGM 0, implicit $scc
 name:            and_execz_imm_vccz_liveout_scc
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -326,6 +343,7 @@ body:             |
 # W32-NOT: S_AND_
 # W32: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            and_execz_mov_vccz_w32
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -345,6 +363,7 @@ body:             |
 # GCN-NOT: S_ANDN2_
 # GCN: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            andn2_execz_mov_vccz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -364,6 +383,7 @@ body:             |
 # GCN-NOT: S_ANDN2_
 # GCN: S_BRANCH %bb.1
 name:            andn2_branch_mov_vccz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -383,6 +403,7 @@ body:             |
 # GCN-NOT: S_ANDN2_
 # GCN: S_CBRANCH_EXECNZ %bb.1, implicit $exec
 name:            andn2_execnz_mov_vccnz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -403,6 +424,7 @@ body:             |
 # GCN-NOT: S_CBRANCH
 # GCN-NOT: S_BRANCH
 name:            andn2_no_branch_mov_vccnz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -423,6 +445,7 @@ body:             |
 # GCN: $vcc = S_MOV_B64 0
 # GCN-NEXT: S_BRANCH %bb.1
 name:            and_0_mov
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -443,6 +466,7 @@ body:             |
 # GCN: $vcc = S_MOV_B64 0
 # GCN-NEXT: S_BRANCH %bb.1
 name:            andn2_m1_mov
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -463,6 +487,7 @@ body:             |
 # GCN: $vcc = S_MOV_B64 $exec
 # GCN-NEXT: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            and_m1_mov
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -483,6 +508,7 @@ body:             |
 # GCN: $vcc = S_MOV_B64 $exec
 # GCN-NEXT: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            andn2_0_mov
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -503,6 +529,7 @@ body:             |
 # GCN: S_AND_
 # GCN-NEXT: S_BRANCH %bb.1
 name:            and_0_scc_req
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -523,6 +550,7 @@ body:             |
 # GCN: S_ANDN2_
 # GCN-NEXT: S_BRANCH %bb.1
 name:            andn2_m1_scc_req
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -542,6 +570,7 @@ body:             |
 # GCN-NOT: S_AND_
 # GCN: S_CBRANCH_VCCZ %bb.1, implicit killed $vcc
 name:            and_cmp_vccz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -550,6 +579,8 @@ body:             |
     S_NOP 0
 
   bb.2:
+    liveins: $vgpr0, $sgpr0, $sgpr0_sgpr1
+
     V_CMP_EQ_U32_e32 0, killed $vgpr0, implicit-def $vcc, implicit $exec
     $vcc = S_AND_B64 $exec, $vcc, implicit-def dead $scc
     S_CBRANCH_VCCZ %bb.1, implicit killed $vcc
@@ -561,6 +592,7 @@ body:             |
 # GCN-NOT: S_AND_
 # GCN: S_CBRANCH_VCCNZ %bb.1, implicit killed $vcc
 name:            and_cmp_vccnz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -569,6 +601,8 @@ body:             |
     S_NOP 0
 
   bb.2:
+    liveins: $vgpr0, $sgpr0, $sgpr0_sgpr1
+
     V_CMP_EQ_U32_e32 0, killed $vgpr0, implicit-def $vcc, implicit $exec
     $vcc = S_AND_B64 $exec, $vcc, implicit-def dead $scc
     S_CBRANCH_VCCNZ %bb.1, implicit killed $vcc
@@ -580,6 +614,7 @@ body:             |
 # GCN: $vcc = S_ANDN2_B64 $exec, $vcc, implicit-def dead $scc
 # GCN: S_CBRANCH_VCCZ %bb.1, implicit killed $vcc
 name:            andn2_cmp_vccz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -588,6 +623,8 @@ body:             |
     S_NOP 0
 
   bb.2:
+    liveins: $vgpr0, $sgpr0, $sgpr0_sgpr1
+
     V_CMP_EQ_U32_e32 0, killed $vgpr0, implicit-def $vcc, implicit $exec
     $vcc = S_ANDN2_B64 $exec, $vcc, implicit-def dead $scc
     S_CBRANCH_VCCZ %bb.1, implicit killed $vcc
@@ -599,6 +636,7 @@ body:             |
 # GCN-NOT: S_AND_
 # GCN: S_CBRANCH_VCCZ %bb.1, implicit killed $vcc
 name:            and_cmpclass_vccz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -607,6 +645,8 @@ body:             |
     S_NOP 0
 
   bb.2:
+    liveins: $vgpr0, $sgpr0, $sgpr0_sgpr1
+
     V_CMP_CLASS_F32_e32 killed $sgpr0, killed $vgpr0, implicit-def $vcc, implicit $exec
     $vcc = S_AND_B64 $exec, $vcc, implicit-def dead $scc
     S_CBRANCH_VCCZ %bb.1, implicit killed $vcc
@@ -618,6 +658,7 @@ body:             |
 # GCN-NOT: S_AND_
 # GCN: S_CBRANCH_VCCZ %bb.1, implicit killed $vcc
 name:            and_cmpx_vccz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -626,6 +667,8 @@ body:             |
     S_NOP 0
 
   bb.2:
+    liveins: $vgpr0, $sgpr0, $sgpr0_sgpr1
+
     V_CMPX_EQ_U32_e32 0, killed $vgpr0, implicit-def $vcc, implicit-def $exec, implicit $exec
     $vcc = S_AND_B64 $exec, $vcc, implicit-def dead $scc
     S_CBRANCH_VCCZ %bb.1, implicit killed $vcc
@@ -638,6 +681,7 @@ body:             |
 # GCN: $vcc = S_AND_B64 $exec, $vcc, implicit-def dead $scc
 # GCN: S_CBRANCH_VCCZ %bb.1, implicit killed $vcc
 name:            and_or_cmp_vccz
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -646,6 +690,8 @@ body:             |
     S_NOP 0
 
   bb.2:
+    liveins: $vgpr0, $sgpr0, $sgpr0_sgpr1
+
     V_CMP_EQ_U32_e32 0, killed $vgpr0, implicit-def $vcc, implicit $exec
     $exec = S_OR_B64 $exec, $sgpr0_sgpr1, implicit-def dead $scc
     $vcc = S_AND_B64 $exec, $vcc, implicit-def dead $scc
@@ -659,6 +705,7 @@ body:             |
 # GCN-NOT: S_
 # GCN: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            issue176578_src0_nonreg
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -678,6 +725,7 @@ body:             |
 # GCN-NOT: S_
 # GCN: S_CBRANCH_EXECZ %bb.1, implicit $exec
 name:            issue176578_src1_nonreg
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
@@ -696,6 +744,7 @@ body:             |
 # GCN: $vcc = S_AND_B64
 # GCN-NEXT: S_CBRANCH_VCCZ %bb.1
 name:            issue176578_nonregs
+tracksRegLiveness: true
 body:             |
   bb.0:
     S_NOP 0
diff --git a/llvm/test/CodeGen/AMDGPU/insert-skips-gfx10.mir b/llvm/test/CodeGen/AMDGPU/insert-skips-gfx10.mir
index dcf99afd64a54..77e11a45ef910 100644
--- a/llvm/test/CodeGen/AMDGPU/insert-skips-gfx10.mir
+++ b/llvm/test/CodeGen/AMDGPU/insert-skips-gfx10.mir
@@ -3,6 +3,7 @@
 
 ---
 name: skip_waitcnt_vscnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_waitcnt_vscnt
   ; CHECK: bb.0:
@@ -33,6 +34,7 @@ body: |
 
 ---
 name: skip_waitcnt_expcnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_waitcnt_expcnt
   ; CHECK: bb.0:
@@ -63,6 +65,7 @@ body: |
 
 ---
 name: skip_waitcnt_vmcnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_waitcnt_vmcnt
   ; CHECK: bb.0:
@@ -93,6 +96,7 @@ body: |
 
 ---
 name: skip_waitcnt_lgkmcnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_waitcnt_lgkmcnt
   ; CHECK: bb.0:
@@ -123,6 +127,7 @@ body: |
 
 ---
 name: skip_wait_idle
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_idle
   ; CHECK: bb.0:
@@ -153,6 +158,7 @@ body: |
 
 ---
 name: skip_bvh
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_bvh
   ; CHECK: bb.0:
@@ -187,6 +193,7 @@ body: |
 
 ---
 name: skip_barrier
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_barrier
   ; CHECK: bb.0:
diff --git a/llvm/test/CodeGen/AMDGPU/insert-skips-gfx12.mir b/llvm/test/CodeGen/AMDGPU/insert-skips-gfx12.mir
index 921a4c03ee09b..516c05873b474 100644
--- a/llvm/test/CodeGen/AMDGPU/insert-skips-gfx12.mir
+++ b/llvm/test/CodeGen/AMDGPU/insert-skips-gfx12.mir
@@ -3,6 +3,7 @@
 
 ---
 name: skip_wait_loadcnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_loadcnt
   ; CHECK: bb.0:
@@ -33,6 +34,7 @@ body: |
 
 ---
 name: skip_wait_loadcnt_dscnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_loadcnt_dscnt
   ; CHECK: bb.0:
@@ -63,6 +65,7 @@ body: |
 
 ---
 name: skip_wait_storecnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_storecnt
   ; CHECK: bb.0:
@@ -93,6 +96,7 @@ body: |
 
 ---
 name: skip_wait_storecnt_dscnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_storecnt_dscnt
   ; CHECK: bb.0:
@@ -123,6 +127,7 @@ body: |
 
 ---
 name: skip_wait_samplecnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_samplecnt
   ; CHECK: bb.0:
@@ -153,6 +158,7 @@ body: |
 
 ---
 name: skip_wait_bvhcnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_bvhcnt
   ; CHECK: bb.0:
@@ -183,6 +189,7 @@ body: |
 
 ---
 name: skip_wait_expcnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_expcnt
   ; CHECK: bb.0:
@@ -213,6 +220,7 @@ body: |
 
 ---
 name: skip_wait_dscnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_dscnt
   ; CHECK: bb.0:
@@ -243,6 +251,7 @@ body: |
 
 ---
 name: skip_wait_kmcnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_kmcnt
   ; CHECK: bb.0:
@@ -273,6 +282,7 @@ body: |
 
 ---
 name: skip_wait_idle
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_idle
   ; CHECK: bb.0:
@@ -303,6 +313,7 @@ body: |
 
 ---
 name: skip_wait_event
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_event
   ; CHECK: bb.0:
@@ -333,6 +344,7 @@ body: |
 
 ---
 name: skip_barrier_signal_imm
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_barrier_signal_imm
   ; CHECK: bb.0:
@@ -363,6 +375,7 @@ body: |
 
 ---
 name: skip_barrier_signal_isfirst_imm
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_barrier_signal_isfirst_imm
   ; CHECK: bb.0:
@@ -395,6 +408,7 @@ body: |
 
 ---
 name: skip_barrier_signal_m0
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_barrier_signal_m0
   ; CHECK: bb.0:
@@ -427,6 +441,7 @@ body: |
 
 ---
 name: skip_barrier_signal_isfirst_m0
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_barrier_signal_isfirst_m0
   ; CHECK: bb.0:
@@ -461,6 +476,7 @@ body: |
 
 ---
 name: skip_barrier_wait
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_barrier_wait
   ; CHECK: bb.0:
@@ -491,6 +507,7 @@ body: |
 
 ---
 name: skip_barrier_init_imm
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_barrier_init_imm
   ; CHECK: bb.0:
@@ -523,6 +540,7 @@ body: |
 
 ---
 name: skip_barrier_init_m0
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_barrier_init_m0
   ; CHECK: bb.0:
@@ -555,6 +573,7 @@ body: |
 
 ---
 name: skip_barrier_join_imm
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_barrier_join_imm
   ; CHECK: bb.0:
@@ -585,6 +604,7 @@ body: |
 
 ---
 name: skip_barrier_leave
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_barrier_leave
   ; CHECK: bb.0:
diff --git a/llvm/test/CodeGen/AMDGPU/insert-skips-gfx1250.mir b/llvm/test/CodeGen/AMDGPU/insert-skips-gfx1250.mir
index 542af92191f61..195d17d6b05b8 100644
--- a/llvm/test/CodeGen/AMDGPU/insert-skips-gfx1250.mir
+++ b/llvm/test/CodeGen/AMDGPU/insert-skips-gfx1250.mir
@@ -3,6 +3,7 @@
 
 ---
 name: skip_wait_xcnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_xcnt
   ; CHECK: bb.0:
@@ -33,6 +34,7 @@ body: |
 
 ---
 name: skip_wait_asynccnt
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_wait_asynccnt
   ; CHECK: bb.0:
diff --git a/llvm/test/CodeGen/AMDGPU/insert-skips-gws.mir b/llvm/test/CodeGen/AMDGPU/insert-skips-gws.mir
index a28521c73f247..583fa9c552348 100644
--- a/llvm/test/CodeGen/AMDGPU/insert-skips-gws.mir
+++ b/llvm/test/CodeGen/AMDGPU/insert-skips-gws.mir
@@ -5,6 +5,7 @@
 ---
 
 name: skip_gws_init
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_gws_init
   ; CHECK: bb.0:
@@ -36,6 +37,7 @@ body: |
 ---
 
 name: skip_gws_barrier
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: skip_gws_barrier
   ; CHECK: bb.0:
diff --git a/llvm/test/CodeGen/AMDGPU/insert-skips-ignored-insts.mir b/llvm/test/CodeGen/AMDGPU/insert-skips-ignored-insts.mir
index e5f0d01a664e3..7c17eaf0d6275 100644
--- a/llvm/test/CodeGen/AMDGPU/insert-skips-ignored-insts.mir
+++ b/llvm/test/CodeGen/AMDGPU/insert-skips-ignored-insts.mir
@@ -4,6 +4,7 @@
 ---
 
 name: no_count_dbg_value
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: no_count_dbg_value
   ; CHECK: bb.0:
diff --git a/llvm/test/CodeGen/AMDGPU/llc-pipeline.ll b/llvm/test/CodeGen/AMDGPU/llc-pipeline.ll
index d10df2ed1d11f..912a8c8b017b0 100644
--- a/llvm/test/CodeGen/AMDGPU/llc-pipeline.ll
+++ b/llvm/test/CodeGen/AMDGPU/llc-pipeline.ll
@@ -443,6 +443,7 @@
 ; GCN-O1-NEXT:        Insert required mode register values
 ; GCN-O1-NEXT:        SI Insert Hard Clauses
 ; GCN-O1-NEXT:        SI Final Branch Preparation
+; GCN-O1-NEXT:        Reaching Definitions Analysis
 ; GCN-O1-NEXT:        SI peephole optimizations
 ; GCN-O1-NEXT:        Post RA hazard recognizer
 ; GCN-O1-NEXT:        AMDGPU Insert waits for SGPR read hazards
@@ -765,6 +766,7 @@
 ; GCN-O1-OPTS-NEXT:        Insert required mode register values
 ; GCN-O1-OPTS-NEXT:        SI Insert Hard Clauses
 ; GCN-O1-OPTS-NEXT:        SI Final Branch Preparation
+; GCN-O1-OPTS-NEXT:        Reaching Definitions Analysis
 ; GCN-O1-OPTS-NEXT:        SI peephole optimizations
 ; GCN-O1-OPTS-NEXT:        Post RA hazard recognizer
 ; GCN-O1-OPTS-NEXT:        AMDGPU Insert waits for SGPR read hazards
@@ -1092,6 +1094,7 @@
 ; GCN-O2-NEXT:        Insert required mode register values
 ; GCN-O2-NEXT:        SI Insert Hard Clauses
 ; GCN-O2-NEXT:        SI Final Branch Preparation
+; GCN-O2-NEXT:        Reaching Definitions Analysis
 ; GCN-O2-NEXT:        SI peephole optimizations
 ; GCN-O2-NEXT:        Post RA hazard recognizer
 ; GCN-O2-NEXT:        AMDGPU Insert waits for SGPR read hazards
@@ -1432,6 +1435,7 @@
 ; GCN-O3-NEXT:        Insert required mode register values
 ; GCN-O3-NEXT:        SI Insert Hard Clauses
 ; GCN-O3-NEXT:        SI Final Branch Preparation
+; GCN-O3-NEXT:        Reaching Definitions Analysis
 ; GCN-O3-NEXT:        SI peephole optimizations
 ; GCN-O3-NEXT:        Post RA hazard recognizer
 ; GCN-O3-NEXT:        AMDGPU Insert waits for SGPR read hazards
diff --git a/llvm/test/CodeGen/AMDGPU/remove-not-short-exec-branch-on-unconditional-jump.mir b/llvm/test/CodeGen/AMDGPU/remove-not-short-exec-branch-on-unconditional-jump.mir
index 9a2a8b39f3c02..1defd9b09834e 100644
--- a/llvm/test/CodeGen/AMDGPU/remove-not-short-exec-branch-on-unconditional-jump.mir
+++ b/llvm/test/CodeGen/AMDGPU/remove-not-short-exec-branch-on-unconditional-jump.mir
@@ -5,6 +5,7 @@
 
 ---
 name:            test
+tracksRegLiveness: true
 body:             |
   ; CHECK-LABEL: name: test
   ; CHECK: bb.0:
diff --git a/llvm/test/CodeGen/AMDGPU/remove-short-exec-branches-gpr-idx-mode.mir b/llvm/test/CodeGen/AMDGPU/remove-short-exec-branches-gpr-idx-mode.mir
index 492a0dd2bafe9..b9dab105ab44f 100644
--- a/llvm/test/CodeGen/AMDGPU/remove-short-exec-branches-gpr-idx-mode.mir
+++ b/llvm/test/CodeGen/AMDGPU/remove-short-exec-branches-gpr-idx-mode.mir
@@ -5,6 +5,7 @@
 ---
 
 name: need_skip_gpr_idx_mode
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: need_skip_gpr_idx_mode
   ; CHECK: bb.0:
@@ -34,6 +35,7 @@ body: |
 ---
 
 name: need_skip_gpr_idx_on
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: need_skip_gpr_idx_on
   ; CHECK: bb.0:
@@ -67,6 +69,7 @@ body: |
 ---
 
 name: need_skip_gpr_idx_off
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: need_skip_gpr_idx_off
   ; CHECK: bb.0:
@@ -96,6 +99,7 @@ body: |
 ---
 
 name: need_skip_gpr_idx_idx
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: need_skip_gpr_idx_idx
   ; CHECK: bb.0:
diff --git a/llvm/test/CodeGen/AMDGPU/remove-short-exec-branches-special-instructions.mir b/llvm/test/CodeGen/AMDGPU/remove-short-exec-branches-special-instructions.mir
index 096cc33a07c28..f75e512766caf 100644
--- a/llvm/test/CodeGen/AMDGPU/remove-short-exec-branches-special-instructions.mir
+++ b/llvm/test/CodeGen/AMDGPU/remove-short-exec-branches-special-instructions.mir
@@ -6,6 +6,7 @@
 ---
 
 name: need_skip_setreg_imm32_b32
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: need_skip_setreg_imm32_b32
   ; CHECK: bb.0:
@@ -37,6 +38,7 @@ body: |
 ---
 
 name: need_skip_setreg_b32
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: need_skip_setreg_b32
   ; CHECK: bb.0:
@@ -71,6 +73,7 @@ body: |
 ---
 
 name: need_skip_denorm_mode
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: need_skip_denorm_mode
   ; CHECK: bb.0:
@@ -101,6 +104,7 @@ body: |
 ---
 
 name: need_skip_round_mode
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: need_skip_round_mode
   ; CHECK: bb.0:
@@ -131,15 +135,18 @@ body: |
 ---
 
 name: need_skip_writelane_b32
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: need_skip_writelane_b32
   ; CHECK: bb.0:
   ; CHECK-NEXT:   successors: %bb.1(0x40000000), %bb.2(0x40000000)
+  ; CHECK-NEXT:   liveins: $vgpr0
   ; CHECK-NEXT: {{  $}}
   ; CHECK-NEXT:   S_CBRANCH_EXECZ %bb.2, implicit $exec
   ; CHECK-NEXT: {{  $}}
   ; CHECK-NEXT: bb.1:
   ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT:   liveins: $vgpr0
   ; CHECK-NEXT: {{  $}}
   ; CHECK-NEXT:   $sgpr0 = IMPLICIT_DEF
   ; CHECK-NEXT:   $vgpr0 = V_WRITELANE_B32 $sgpr0, 0, $vgpr0
@@ -147,10 +154,12 @@ body: |
   ; CHECK-NEXT: bb.2:
   ; CHECK-NEXT:   S_ENDPGM 0
   bb.0:
+    liveins: $vgpr0
     successors: %bb.1, %bb.2
     S_CBRANCH_EXECZ %bb.2, implicit $exec
 
   bb.1:
+    liveins: $vgpr0
     successors: %bb.2
     $sgpr0 = IMPLICIT_DEF
     $vgpr0 = V_WRITELANE_B32 $sgpr0, 0, $vgpr0
@@ -161,6 +170,7 @@ body: |
 
 ---
 name: need_skip_readlane_b32
+tracksRegLiveness: true
 body: |
   ; CHECK-LABEL: name: need_skip_readlane_b32
   ; CHECK: bb.0:
diff --git a/llvm/test/CodeGen/AMDGPU/set-gpr-idx-peephole.mir b/llvm/test/CodeGen/AMDGPU/set-gpr-idx-peephole.mir
index 76a7303d9a068..06e4c412d35d4 100644
--- a/llvm/test/CodeGen/AMDGPU/set-gpr-idx-peephole.mir
+++ b/llvm/test/CodeGen/AMDGPU/set-gpr-idx-peephole.mir
@@ -5,11 +5,16 @@
 
 ---
 name:            simple
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: simple
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $mode, implicit-def $m0, implicit $mode, implicit undef $m0
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $mode, implicit-def $m0, implicit $mode, implicit undef $m0
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: $vgpr15 = V_MOV_B32_indirect_read undef $vgpr0, implicit $exec, implicit $m0, implicit killed $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
@@ -23,11 +28,16 @@ body:             |
 
 ---
 name:            salu_in_between
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: salu_in_between
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: $sgpr0 = S_MOV_B32 $sgpr2
     ; GCN-NEXT: $vgpr15 = V_MOV_B32_indirect_read undef $vgpr0, implicit $exec, implicit $m0, implicit killed $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
@@ -43,10 +53,15 @@ body:             |
 
 ---
 name:            meta_in_between
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
     ; GCN-LABEL: name: meta_in_between
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: KILL $sgpr0
     ; GCN-NEXT: $sgpr0 = IMPLICIT_DEF
@@ -64,11 +79,16 @@ body:             |
 
 ---
 name:            valu_write_in_between
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: valu_write_in_between
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
     ; GCN-NEXT: $vgpr20 = V_MOV_B32_indirect_read 1, implicit $exec, implicit $m0
@@ -86,11 +106,16 @@ body:             |
 
 ---
 name:            valu_read_in_between
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: valu_read_in_between
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
     ; GCN-NEXT: V_NOP_e32 implicit $exec, implicit $vgpr0
@@ -108,11 +133,16 @@ body:             |
 
 ---
 name:            changed_index
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: changed_index
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
     ; GCN-NEXT: $sgpr2 = S_MOV_B32 1
@@ -130,11 +160,16 @@ body:             |
 
 ---
 name:            implicitly_changed_index
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: implicitly_changed_index
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
     ; GCN-NEXT: S_NOP 0, implicit-def $sgpr2
@@ -152,11 +187,16 @@ body:             |
 
 ---
 name:            changed_m0
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: changed_m0
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
     ; GCN-NEXT: $m0 = S_MOV_B32 1
@@ -174,11 +214,16 @@ body:             |
 
 ---
 name:            implicitly_changed_m0
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: implicitly_changed_m0
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
     ; GCN-NEXT: S_NOP 0, implicit-def $m0
@@ -196,11 +241,16 @@ body:             |
 
 ---
 name:            same_imm_index
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: same_imm_index
-    ; GCN: S_SET_GPR_IDX_ON 1, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON 1, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: $vgpr15 = V_MOV_B32_indirect_read undef $vgpr0, implicit $exec, implicit $m0, implicit killed $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
@@ -214,11 +264,16 @@ body:             |
 
 ---
 name:            different_imm_index
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: different_imm_index
-    ; GCN: S_SET_GPR_IDX_ON 1, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON 1, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
     ; GCN-NEXT: S_SET_GPR_IDX_ON 2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
@@ -234,11 +289,16 @@ body:             |
 
 ---
 name:            different_gpr_index
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: different_gpr_index
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
     ; GCN-NEXT: S_SET_GPR_IDX_ON killed $sgpr1, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
@@ -254,11 +314,16 @@ body:             |
 
 ---
 name:            different_gpr_index_then_same_index
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: different_gpr_index_then_same_index
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
     ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr1, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
@@ -280,11 +345,16 @@ body:             |
 
 ---
 name:            use_m0_with_idx_off
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: use_m0_with_idx_off
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_ON killed $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
@@ -300,20 +370,23 @@ body:             |
 
 ---
 name:            three_in_a_row
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: three_in_a_row
     ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
-    ; GCN-NEXT: $vgpr17 = V_MOV_B32_indirect_read undef $vgpr0, implicit $exec, implicit $m0, implicit killed $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
+    ; GCN-NEXT: $vgpr17 = V_MOV_B32_indirect_read undef $vgpr0, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: $vgpr18 = V_MOV_B32_indirect_read undef $vgpr0, implicit $exec, implicit $m0, implicit killed $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
   S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
   $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
   S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
-  S_SET_GPR_IDX_ON killed $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
-  $vgpr17 = V_MOV_B32_indirect_read undef $vgpr0, implicit $exec, implicit $m0, implicit killed $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
+  S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+  $vgpr17 = V_MOV_B32_indirect_read undef $vgpr0, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
   S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
   S_SET_GPR_IDX_ON killed $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
   $vgpr18 = V_MOV_B32_indirect_read undef $vgpr0, implicit $exec, implicit $m0, implicit killed $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
@@ -322,11 +395,16 @@ body:             |
 
 ---
 name:            different_gpr_index_then_two_same_indexes
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: different_gpr_index_then_two_same_indexes
-    ; GCN: S_SET_GPR_IDX_ON $sgpr1, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr1, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
     ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
@@ -346,11 +424,16 @@ body:             |
 
 ---
 name:            two_same_indexes_then_different
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: two_same_indexes_then_different
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
@@ -370,11 +453,16 @@ body:             |
 
 ---
 name:            indirect_mov
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
 
     ; GCN-LABEL: name: indirect_mov
-    ; GCN: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $m0, implicit-def $mode, implicit undef $m0, implicit $mode
     ; GCN-NEXT: $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: V_MOV_B32_indirect_write undef $vgpr0, undef $vgpr3, implicit $exec, implicit $m0, implicit-def $vgpr0_vgpr1_vgpr2_vgpr3, implicit killed $vgpr0_vgpr1_vgpr2_vgpr3(tied-def 4)
     ; GCN-NEXT: V_MOV_B32_indirect_write undef $vgpr0, undef $vgpr3, implicit $exec, implicit $m0, implicit-def $vgpr0_vgpr1_vgpr2_vgpr3, implicit killed $vgpr0_vgpr1_vgpr2_vgpr3(tied-def 4)
@@ -390,10 +478,15 @@ body:             |
 
 ---
 name:            simple_bundle
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
     ; GCN-LABEL: name: simple_bundle
-    ; GCN: BUNDLE implicit-def $m0, implicit-def $m0_lo16, implicit-def $m0_hi16, implicit-def $mode, implicit-def $vgpr16, implicit-def $vgpr16_lo16, implicit-def $vgpr16_hi16, implicit $sgpr2, implicit $m0, implicit $mode, implicit undef $vgpr1, implicit $exec, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15 {
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: BUNDLE implicit-def $m0, implicit-def $m0_lo16, implicit-def $m0_hi16, implicit-def $mode, implicit-def $vgpr16, implicit-def $vgpr16_lo16, implicit-def $vgpr16_hi16, implicit $sgpr2, implicit $m0, implicit $mode, implicit undef $vgpr1, implicit $exec, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15 {
     ; GCN-NEXT:   S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $mode, implicit-def $m0, implicit $mode, implicit undef $m0
     ; GCN-NEXT:   $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: }
@@ -415,10 +508,15 @@ body:             |
 
 ---
 name:            salu_in_between_bundle
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
     ; GCN-LABEL: name: salu_in_between_bundle
-    ; GCN: BUNDLE implicit-def $m0, implicit-def $m0_lo16, implicit-def $m0_hi16, implicit-def $mode, implicit-def $vgpr16, implicit-def $vgpr16_lo16, implicit-def $vgpr16_hi16, implicit $sgpr2, implicit $m0, implicit $mode, implicit undef $vgpr1, implicit $exec, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15 {
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: BUNDLE implicit-def $m0, implicit-def $m0_lo16, implicit-def $m0_hi16, implicit-def $mode, implicit-def $vgpr16, implicit-def $vgpr16_lo16, implicit-def $vgpr16_hi16, implicit $sgpr2, implicit $m0, implicit $mode, implicit undef $vgpr1, implicit $exec, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15 {
     ; GCN-NEXT:   S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $mode, implicit-def $m0, implicit $mode, implicit undef $m0
     ; GCN-NEXT:   $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT: }
@@ -442,10 +540,15 @@ body:             |
 
 ---
 name:            valu_in_between_bundle
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
     ; GCN-LABEL: name: valu_in_between_bundle
-    ; GCN: BUNDLE implicit-def $m0, implicit-def $m0_lo16, implicit-def $m0_hi16, implicit-def $mode, implicit-def $vgpr16, implicit-def $vgpr16_lo16, implicit-def $vgpr16_hi16, implicit $sgpr2, implicit $m0, implicit $mode, implicit undef $vgpr1, implicit $exec, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15 {
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: BUNDLE implicit-def $m0, implicit-def $m0_lo16, implicit-def $m0_hi16, implicit-def $mode, implicit-def $vgpr16, implicit-def $vgpr16_lo16, implicit-def $vgpr16_hi16, implicit $sgpr2, implicit $m0, implicit $mode, implicit undef $vgpr1, implicit $exec, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15 {
     ; GCN-NEXT:   S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $mode, implicit-def $m0, implicit $mode, implicit undef $m0
     ; GCN-NEXT:   $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT:   S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
@@ -471,10 +574,15 @@ body:             |
 
 ---
 name:            changed_index_bundle
+tracksRegLiveness: true
 body:             |
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
     ; GCN-LABEL: name: changed_index_bundle
-    ; GCN: BUNDLE implicit-def $m0, implicit-def $m0_lo16, implicit-def $m0_hi16, implicit-def $mode, implicit-def $vgpr16, implicit-def $vgpr16_lo16, implicit-def $vgpr16_hi16, implicit $sgpr2, implicit $m0, implicit $mode, implicit undef $vgpr1, implicit $exec, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15 {
+    ; GCN: liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: BUNDLE implicit-def $m0, implicit-def $m0_lo16, implicit-def $m0_hi16, implicit-def $mode, implicit-def $vgpr16, implicit-def $vgpr16_lo16, implicit-def $vgpr16_hi16, implicit $sgpr2, implicit $m0, implicit $mode, implicit undef $vgpr1, implicit $exec, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15 {
     ; GCN-NEXT:   S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $mode, implicit-def $m0, implicit $mode, implicit undef $m0
     ; GCN-NEXT:   $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
     ; GCN-NEXT:   S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
@@ -500,10 +608,12 @@ body:             |
 
 ---
 name:            simple_cbranch_vccz
+tracksRegLiveness: true
 body:             |
   ; GCN-LABEL: name: simple_cbranch_vccz
   ; GCN: bb.0:
   ; GCN-NEXT:   successors: %bb.1(0x80000000)
+  ; GCN-NEXT:   liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
   ; GCN-NEXT: {{  $}}
   ; GCN-NEXT:   S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $mode, implicit-def $m0, implicit $mode, implicit undef $m0
   ; GCN-NEXT:   $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
@@ -513,6 +623,8 @@ body:             |
   ; GCN-NEXT: {{  $}}
   ; GCN-NEXT: bb.1:
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
   S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $mode, implicit-def $m0, implicit $mode, implicit undef $m0
   $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
   S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
@@ -525,10 +637,12 @@ body:             |
 
 ---
 name:            simple_cbranch_execz
+tracksRegLiveness: true
 body:             |
   ; GCN-LABEL: name: simple_cbranch_execz
   ; GCN: bb.0:
   ; GCN-NEXT:   successors:
+  ; GCN-NEXT:   liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
   ; GCN-NEXT: {{  $}}
   ; GCN-NEXT:   S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $mode, implicit-def $m0, implicit $mode, implicit undef $m0
   ; GCN-NEXT:   $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
@@ -537,6 +651,8 @@ body:             |
   ; GCN-NEXT: {{  $}}
   ; GCN-NEXT: bb.1:
   bb.0:
+    liveins: $sgpr0, $sgpr1, $sgpr2, $vgpr0, $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15, $vcc
+
   S_SET_GPR_IDX_ON $sgpr2, 1, implicit-def $mode, implicit-def $m0, implicit $mode, implicit undef $m0
   $vgpr16 = V_MOV_B32_indirect_read undef $vgpr1, implicit $exec, implicit $m0, implicit $vgpr0_vgpr1_vgpr2_vgpr3_vgpr4_vgpr5_vgpr6_vgpr7_vgpr8_vgpr9_vgpr10_vgpr11_vgpr12_vgpr13_vgpr14_vgpr15
   S_SET_GPR_IDX_OFF implicit-def $mode, implicit $mode
diff --git a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
new file mode 100644
index 0000000000000..7f94f55237621
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
@@ -0,0 +1,38 @@
+# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx90a -run-pass=si-pre-emit-peephole -verify-machineinstrs -o - %s | FileCheck %s
+
+---
+name: fmac_f64_e32_return_copy
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1
+
+    ; CHECK-LABEL: name: fmac_f64_e32_return_copy
+    ; CHECK: $vgpr0_vgpr1 = nofpexcept V_FMA_F64_e64 0, killed $vgpr4_vgpr5, 0, killed $vgpr0_vgpr1, 0, killed $vgpr2_vgpr3, 0, 0, implicit $mode, implicit $exec
+    ; CHECK-NEXT: SI_RETURN implicit killed $vgpr0, implicit killed $vgpr1
+    renamable $vgpr2_vgpr3 = nofpexcept V_RSQ_F64_e32 $vgpr0_vgpr1, implicit $mode, implicit $exec
+    renamable $vgpr4_vgpr5 = nofpexcept V_MUL_F64_e64 0, $vgpr2_vgpr3, 0, $vgpr0_vgpr1, 0, 0, implicit $mode, implicit $exec
+    renamable $vgpr0_vgpr1 = nofpexcept V_FMA_F64_e64 0, killed $vgpr0_vgpr1, 0, $vgpr2_vgpr3, 0, 4602678819172646912, 0, 0, implicit $mode, implicit $exec
+    renamable $vgpr2_vgpr3 = nofpexcept V_FMAC_F64_e32 killed renamable $vgpr4_vgpr5, killed renamable $vgpr0_vgpr1, killed renamable $vgpr2_vgpr3, implicit $mode, implicit $exec
+    $vgpr0 = V_MOV_B32_e32 killed $vgpr2, implicit $exec, implicit $exec
+    $vgpr1 = V_MOV_B32_e32 killed $vgpr3, implicit $exec, implicit $exec
+    SI_RETURN implicit killed $vgpr0, implicit killed $vgpr1
+...
+
+---
+name: fmac_f32_e32_return_copy
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0
+
+    ; CHECK-LABEL: name: fmac_f32_e32_return_copy
+    ; CHECK: $vgpr0 = nofpexcept V_FMA_F32_e64 0, killed $vgpr4, 0, killed $vgpr0, 0, killed $vgpr2, 0, 0, implicit $mode, implicit $exec
+    ; CHECK-NEXT: SI_RETURN implicit killed $vgpr0
+    renamable $vgpr2 = nofpexcept V_RSQ_F32_e32 $vgpr0, implicit $exec, implicit $mode
+    renamable $vgpr4 = nofpexcept V_MUL_F32_e64 0, $vgpr2, 0, $vgpr0, 0, 0, implicit $mode, implicit $exec
+    renamable $vgpr0 = nofpexcept V_FMA_F32_e64 0, killed $vgpr0, 0, $vgpr2, 0, 1056964608, 0, 0, implicit $mode, implicit $exec
+    renamable $vgpr2 = nofpexcept V_FMAC_F32_e32 killed renamable $vgpr4, killed renamable $vgpr0, killed renamable $vgpr2, implicit $mode, implicit $exec
+    $vgpr0 = V_MOV_B32_e32 killed $vgpr2, implicit $exec, implicit $exec
+    SI_RETURN implicit killed $vgpr0
+...
diff --git a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-preserve-loop-info.mir b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-preserve-loop-info.mir
index 12eff8da0e477..23a3a862696e4 100644
--- a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-preserve-loop-info.mir
+++ b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-preserve-loop-info.mir
@@ -3,7 +3,9 @@
 # CHECK: Running analysis: MachineLoopAnalysis on vcc_and_removal_preserves_mli
 # CHECK-NEXT: Running analysis: MachineDominatorTreeAnalysis on vcc_and_removal_preserves_mli
 # CHECK-NEXT: Running pass: SIPreEmitPeepholePass on vcc_and_removal_preserves_mli
+# CHECK-NEXT: Running analysis: ReachingDefAnalysis on vcc_and_removal_preserves_mli
 # CHECK-NEXT: Invalidating analysis: MachineDominatorTreeAnalysis on vcc_and_removal_preserves_mli
+# CHECK-NEXT: Invalidating analysis: ReachingDefAnalysis on vcc_and_removal_preserves_mli
 # CHECK-NEXT: Running pass: MachineLoopPrinterPass on vcc_and_removal_preserves_mli
 # CHECK-NEXT: Machine loop info for machine function 'vcc_and_removal_preserves_mli':
 # CHECK-NOT: Running analysis: MachineLoopAnalysis on vcc_and_removal_preserves_mli
@@ -11,12 +13,17 @@
 
 ---
 name: vcc_and_removal_preserves_mli
+tracksRegLiveness: true
 body: |
   bb.0:
+    liveins: $vgpr0
+
     S_BRANCH %bb.1
 
   ; S_AND gets removed
   bb.1:
+    liveins: $vgpr0
+
     V_CMP_EQ_U32_e32 0, $vgpr0, implicit-def $vcc, implicit $exec
     $vcc = S_AND_B64 $exec, $vcc, implicit-def dead $scc
     S_CBRANCH_VCCNZ %bb.1, implicit $vcc
@@ -27,12 +34,14 @@ body: |
 ...
 
 # CHECK-LABEL: Running pass: SIPreEmitPeepholePass on vcc_branch_destroys_loop
+# CHECK-NEXT: Running analysis: ReachingDefAnalysis on vcc_branch_destroys_loop
 # CHECK-NOT: Running analysis: MachineLoopAnalysis on vcc_branch_destroys_loop
 # CHECK: Machine loop info for machine function 'vcc_branch_destroys_loop':
 # CHECK-NOT: Loop at depth
 
 ---
 name: vcc_branch_destroys_loop
+tracksRegLiveness: true
 body: |
   bb.0:
     S_BRANCH %bb.1
diff --git a/llvm/test/CodeGen/AMDGPU/skip-branch-taildup-ret.mir b/llvm/test/CodeGen/AMDGPU/skip-branch-taildup-ret.mir
index 4b5d05167d53a..306a5e56df2f8 100644
--- a/llvm/test/CodeGen/AMDGPU/skip-branch-taildup-ret.mir
+++ b/llvm/test/CodeGen/AMDGPU/skip-branch-taildup-ret.mir
@@ -3,6 +3,7 @@
 
 ---
 name: skip_branch_taildup_endpgm
+tracksRegLiveness: true
 machineFunctionInfo:
   isEntryFunction: true
 body:             |
@@ -124,6 +125,7 @@ body:             |
 
 ---
 name: skip_branch_taildup_ret
+tracksRegLiveness: true
 body:             |
   ; CHECK-LABEL: name: skip_branch_taildup_ret
   ; CHECK: bb.0:

>From c5731f246aa11f16a05630b922e66eda987123cc Mon Sep 17 00:00:00 2001
From: Lukas Sommer <lukas.sommer at amd.com>
Date: Mon, 6 Jul 2026 08:07:42 -0500
Subject: [PATCH 3/6] Add documentation

Signed-off-by: Lukas Sommer <lukas.sommer at amd.com>
---
 llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp  | 15 ++++++++---
 .../AMDGPU/si-pre-emit-peephole-fmac.mir      | 25 +++++++++++++++++++
 2 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp b/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
index 22c6aeec437f8..734ccbad4eb35 100644
--- a/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
+++ b/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
@@ -83,9 +83,10 @@ class SIPreEmitPeephole {
   void addOperandAndMods(MachineInstrBuilder &NewMI, unsigned SrcMods,
                          bool IsHiBits, const MachineOperand &SrcMO);
 
-  // Delayed post-RA conversion from a two-address instruction to a
-  // three-address instruction. The copy instructions become redundant after
-  // retargeting the converted instruction to NewDst.
+  // Hold information about a candidate for delayed post-RA conversion from a
+  // two-address instruction to a three-address instruction. The copy
+  // instructions become redundant after retargeting the converted instruction
+  // to NewDst.
   struct ThreeAddressCandidate {
     MachineInstr *TwoAddrMI;
     Register NewDst;
@@ -817,10 +818,14 @@ std::optional<Register> SIPreEmitPeephole::checkCopy(MachineInstr &TwoAddress,
     return std::nullopt;
 
   Register Reg = Dst.getReg();
+  // Check that it is safe to define the copy destination register at the
+  // current position of the two-address instruction.
   SmallPtrSet<MachineInstr *, 1> Ignore{&TwoAddress, &MaybeCopy};
   if (!RDI->isSafeToDefRegAt(&TwoAddress, Reg, Ignore))
     return std::nullopt;
 
+  // Check that the two-address instruction and the copy have the same exec
+  // mask.
   if (!RDI->hasSameReachingDef(&TwoAddress, &MaybeCopy, AMDGPU::EXEC))
     return std::nullopt;
 
@@ -846,7 +851,7 @@ Register SIPreEmitPeephole::shouldReplaceWithThreeAddress(
   for (Register SubReg : DestRegs) {
     if (!SubReg.isPhysical())
       return Register();
-    if (RDI->isReachingDefLiveOut(&MI, SubReg))
+    if (RDI->getLocalLiveOutMIDef(MI.getParent(), SubReg) == &MI)
       return Register();
 
     SmallPtrSet<MachineInstr *, 1> Uses;
@@ -869,6 +874,8 @@ Register SIPreEmitPeephole::shouldReplaceWithThreeAddress(
   if (Replacements.size() == 1)
     return Replacements.front();
 
+  // Try to identify a matching super-register/tuple for the f64 case and abort
+  // the transformation if there is none.
   Register Tuple = TRI->getMatchingSuperReg(Replacements.front(), AMDGPU::sub0,
                                             TRI->getVGPR64Class());
   if (!Tuple)
diff --git a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
index 7f94f55237621..fc8e9552bbf18 100644
--- a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
+++ b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
@@ -36,3 +36,28 @@ body: |
     $vgpr0 = V_MOV_B32_e32 killed $vgpr2, implicit $exec, implicit $exec
     SI_RETURN implicit killed $vgpr0
 ...
+
+---
+name: fmac_f32_e32_copy_live_out
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr2, $vgpr4
+    successors: %bb.1
+
+    ; CHECK-LABEL: name: fmac_f32_e32_copy_live_out
+    ; CHECK: renamable $vgpr2 = nofpexcept V_FMAC_F32_e32 killed $vgpr4, killed $vgpr0, killed $vgpr2, implicit $mode, implicit $exec
+    ; CHECK-NEXT: $vgpr0 = V_MOV_B32_e32 $vgpr2, implicit $exec, implicit $exec
+    ; CHECK-NEXT: S_BRANCH %bb.1
+    renamable $vgpr2 = nofpexcept V_FMAC_F32_e32 killed renamable $vgpr4, killed renamable $vgpr0, killed renamable $vgpr2, implicit $mode, implicit $exec
+    $vgpr0 = V_MOV_B32_e32 $vgpr2, implicit $exec, implicit $exec
+    S_BRANCH %bb.1
+
+  bb.1:
+    liveins: $vgpr2
+
+    ; CHECK: bb.1:
+    ; CHECK-NEXT: liveins: $vgpr2
+    ; CHECK: SI_RETURN implicit killed $vgpr2
+    SI_RETURN implicit killed $vgpr2
+...

>From 6ce398b91f60122c252e3061a5f6059039bbfce4 Mon Sep 17 00:00:00 2001
From: Lukas Sommer <lukas.sommer at amd.com>
Date: Tue, 7 Jul 2026 07:56:44 -0500
Subject: [PATCH 4/6] Auto-generate test checks

Signed-off-by: Lukas Sommer <lukas.sommer at amd.com>
---
 .../AMDGPU/si-pre-emit-peephole-fmac.mir      | 39 ++++++++++++++-----
 1 file changed, 29 insertions(+), 10 deletions(-)

diff --git a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
index fc8e9552bbf18..d3391e2cda4e3 100644
--- a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
+++ b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
@@ -1,4 +1,5 @@
-# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx90a -run-pass=si-pre-emit-peephole -verify-machineinstrs -o - %s | FileCheck %s
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx90a -run-pass=si-pre-emit-peephole -o - %s | FileCheck %s
 
 ---
 name: fmac_f64_e32_return_copy
@@ -8,7 +9,12 @@ body: |
     liveins: $vgpr0, $vgpr1
 
     ; CHECK-LABEL: name: fmac_f64_e32_return_copy
-    ; CHECK: $vgpr0_vgpr1 = nofpexcept V_FMA_F64_e64 0, killed $vgpr4_vgpr5, 0, killed $vgpr0_vgpr1, 0, killed $vgpr2_vgpr3, 0, 0, implicit $mode, implicit $exec
+    ; CHECK: liveins: $vgpr0, $vgpr1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: renamable $vgpr2_vgpr3 = nofpexcept V_RSQ_F64_e32 $vgpr0_vgpr1, implicit $mode, implicit $exec
+    ; CHECK-NEXT: renamable $vgpr4_vgpr5 = nofpexcept V_MUL_F64_e64 0, $vgpr2_vgpr3, 0, $vgpr0_vgpr1, 0, 0, implicit $mode, implicit $exec
+    ; CHECK-NEXT: renamable $vgpr0_vgpr1 = nofpexcept V_FMA_F64_e64 0, killed $vgpr0_vgpr1, 0, $vgpr2_vgpr3, 0, 4602678819172646912, 0, 0, implicit $mode, implicit $exec
+    ; CHECK-NEXT: $vgpr0_vgpr1 = nofpexcept V_FMA_F64_e64 0, killed $vgpr4_vgpr5, 0, killed $vgpr0_vgpr1, 0, killed $vgpr2_vgpr3, 0, 0, implicit $mode, implicit $exec
     ; CHECK-NEXT: SI_RETURN implicit killed $vgpr0, implicit killed $vgpr1
     renamable $vgpr2_vgpr3 = nofpexcept V_RSQ_F64_e32 $vgpr0_vgpr1, implicit $mode, implicit $exec
     renamable $vgpr4_vgpr5 = nofpexcept V_MUL_F64_e64 0, $vgpr2_vgpr3, 0, $vgpr0_vgpr1, 0, 0, implicit $mode, implicit $exec
@@ -27,7 +33,12 @@ body: |
     liveins: $vgpr0
 
     ; CHECK-LABEL: name: fmac_f32_e32_return_copy
-    ; CHECK: $vgpr0 = nofpexcept V_FMA_F32_e64 0, killed $vgpr4, 0, killed $vgpr0, 0, killed $vgpr2, 0, 0, implicit $mode, implicit $exec
+    ; CHECK: liveins: $vgpr0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: renamable $vgpr2 = nofpexcept V_RSQ_F32_e32 $vgpr0, implicit $exec, implicit $mode
+    ; CHECK-NEXT: renamable $vgpr4 = nofpexcept V_MUL_F32_e64 0, $vgpr2, 0, $vgpr0, 0, 0, implicit $mode, implicit $exec
+    ; CHECK-NEXT: renamable $vgpr0 = nofpexcept V_FMA_F32_e64 0, killed $vgpr0, 0, $vgpr2, 0, 1056964608, 0, 0, implicit $mode, implicit $exec
+    ; CHECK-NEXT: $vgpr0 = nofpexcept V_FMA_F32_e64 0, killed $vgpr4, 0, killed $vgpr0, 0, killed $vgpr2, 0, 0, implicit $mode, implicit $exec
     ; CHECK-NEXT: SI_RETURN implicit killed $vgpr0
     renamable $vgpr2 = nofpexcept V_RSQ_F32_e32 $vgpr0, implicit $exec, implicit $mode
     renamable $vgpr4 = nofpexcept V_MUL_F32_e64 0, $vgpr2, 0, $vgpr0, 0, 0, implicit $mode, implicit $exec
@@ -41,14 +52,25 @@ body: |
 name: fmac_f32_e32_copy_live_out
 tracksRegLiveness: true
 body: |
+  ; CHECK-LABEL: name: fmac_f32_e32_copy_live_out
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT:   liveins: $vgpr0, $vgpr2, $vgpr4
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   renamable $vgpr2 = nofpexcept V_FMAC_F32_e32 killed $vgpr4, killed $vgpr0, killed $vgpr2, implicit $mode, implicit $exec
+  ; CHECK-NEXT:   $vgpr0 = V_MOV_B32_e32 $vgpr2, implicit $exec, implicit $exec
+  ; CHECK-NEXT:   S_BRANCH %bb.1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   liveins: $vgpr2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   SI_RETURN implicit killed $vgpr2
+  ; Negative test to check the peephole doesn't apply if the original
+  ; destination register is live out of the block.
   bb.0:
     liveins: $vgpr0, $vgpr2, $vgpr4
     successors: %bb.1
 
-    ; CHECK-LABEL: name: fmac_f32_e32_copy_live_out
-    ; CHECK: renamable $vgpr2 = nofpexcept V_FMAC_F32_e32 killed $vgpr4, killed $vgpr0, killed $vgpr2, implicit $mode, implicit $exec
-    ; CHECK-NEXT: $vgpr0 = V_MOV_B32_e32 $vgpr2, implicit $exec, implicit $exec
-    ; CHECK-NEXT: S_BRANCH %bb.1
     renamable $vgpr2 = nofpexcept V_FMAC_F32_e32 killed renamable $vgpr4, killed renamable $vgpr0, killed renamable $vgpr2, implicit $mode, implicit $exec
     $vgpr0 = V_MOV_B32_e32 $vgpr2, implicit $exec, implicit $exec
     S_BRANCH %bb.1
@@ -56,8 +78,5 @@ body: |
   bb.1:
     liveins: $vgpr2
 
-    ; CHECK: bb.1:
-    ; CHECK-NEXT: liveins: $vgpr2
-    ; CHECK: SI_RETURN implicit killed $vgpr2
     SI_RETURN implicit killed $vgpr2
 ...

>From 63d75ff5a6732d728ebe964ce5f1273055a8639a Mon Sep 17 00:00:00 2001
From: Lukas Sommer <lukas.sommer at amd.com>
Date: Wed, 8 Jul 2026 11:22:02 -0500
Subject: [PATCH 5/6] Extend to more copies and opcodes

Signed-off-by: Lukas Sommer <lukas.sommer at amd.com>
---
 llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp  | 213 ++++++++++++------
 llvm/test/CodeGen/AMDGPU/mad-mix.ll           |   9 +-
 .../AMDGPU/si-pre-emit-peephole-fmac-f16.mir  |  41 ++++
 .../AMDGPU/si-pre-emit-peephole-fmac.mir      |  42 ++++
 4 files changed, 225 insertions(+), 80 deletions(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac-f16.mir

diff --git a/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp b/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
index 734ccbad4eb35..ebd5b89e3154f 100644
--- a/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
+++ b/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
@@ -96,19 +96,26 @@ class SIPreEmitPeephole {
                           ArrayRef<MachineInstr *> Copies)
         : TwoAddrMI(&TwoAddrMI), NewDst(NewDst), CopiesToRemove(Copies) {}
   };
-  // Check if \p MaybeCopy is a simple copy of \p Src. If it is a supported
+  // Return true if MI is a candidate for late conversion to a three-address
+  // instruction to avoid copies.
+  bool isLateThreeAddrPeepholeCandidate(const MachineInstr &MI) const;
+  // Check if MaybeCopy is a simple copy of Src. If it is a supported
   // copy, return the destination register of the copy.
-  std::optional<Register> checkCopy(MachineInstr &TwoAddress,
-                                    MachineInstr &MaybeCopy, Register Src);
+  Register checkCopy(MachineInstr &TwoAddress, MachineInstr &MaybeCopy,
+                     Register Src, const TargetRegisterClass *NewDstRC);
   // Check whether the two-address instruction should be replaced with a
   // three-address instruction to avoid copies of the output registers. If the
   // replacement should take place, returns the destination register for the
-  // replacement and fills \p Copies with the copy instructions to remove after
+  // replacement and fills Copies with the copy instructions to remove after
   // the replacement.
   Register
   shouldReplaceWithThreeAddress(MachineInstr &MI,
                                 SmallVectorImpl<MachineInstr *> &Copies);
 
+  // Convert two-adress instruction in Cand to its three-address equivalent,
+  // targeting the register given in Cand.
+  bool convertToThreeAddressInstr(ThreeAddressCandidate &Cand);
+
 public:
   bool run(MachineFunction &MF, MachineLoopInfo *MLI,
            const ReachingDefInfo *RDI);
@@ -795,41 +802,79 @@ MachineInstrBuilder SIPreEmitPeephole::createUnpackedMI(MachineInstr &I,
   return NewMI;
 }
 
-std::optional<Register> SIPreEmitPeephole::checkCopy(MachineInstr &TwoAddress,
-                                                     MachineInstr &MaybeCopy,
-                                                     Register Src) {
-  if (MaybeCopy.isBundle())
-    return std::nullopt;
-
-  switch (MaybeCopy.getOpcode()) {
-  case AMDGPU::COPY:
-  case AMDGPU::V_MOV_B32_e32:
-    break;
+bool SIPreEmitPeephole::isLateThreeAddrPeepholeCandidate(
+    const MachineInstr &MI) const {
+  // This is a subset of the opcodes that are convertible to three-address
+  // instructions according to . This is intended. For some cases,
+  // convertToThreeAddress could early-clobber, which would require us to roll
+  // back the transformation. However, rollback is not side-effect free for some
+  // cases in convertToThreeAddress that fold immediates. Therefore, we limit
+  // this peephole to only the instructions for which rollback will not be
+  // necessary.
+  switch (MI.getOpcode()) {
+  case AMDGPU::V_MAC_F16_e32:
+  case AMDGPU::V_MAC_F16_e64:
+  case AMDGPU::V_MAC_F32_e32:
+  case AMDGPU::V_MAC_F32_e64:
+  case AMDGPU::V_MAC_LEGACY_F32_e32:
+  case AMDGPU::V_MAC_LEGACY_F32_e64:
+  case AMDGPU::V_FMAC_F16_e32:
+  case AMDGPU::V_FMAC_F16_e64:
+  case AMDGPU::V_FMAC_F16_t16_e64:
+  case AMDGPU::V_FMAC_F16_fake16_e64:
+  case AMDGPU::V_FMAC_F32_e32:
+  case AMDGPU::V_FMAC_F32_e64:
+  case AMDGPU::V_FMAC_LEGACY_F32_e32:
+  case AMDGPU::V_FMAC_LEGACY_F32_e64:
+  case AMDGPU::V_FMAC_F64_e32:
+  case AMDGPU::V_FMAC_F64_e64:
+    return true;
   default:
-    return std::nullopt;
+    return false;
   }
+}
+
+Register SIPreEmitPeephole::checkCopy(MachineInstr &TwoAddress,
+                                      MachineInstr &MaybeCopy, Register Src,
+                                      const TargetRegisterClass *NewDstRC) {
+  if (MaybeCopy.isBundle())
+    return Register();
+
+  if (!TII->isFoldableCopy(MaybeCopy) ||
+      MaybeCopy.getOpcode() == AMDGPU::WWM_COPY)
+    return Register();
 
-  MachineOperand CopySrc = MaybeCopy.getOperand(1);
-  if (!CopySrc.isReg() || CopySrc.getReg() != Src)
-    return std::nullopt;
+  if (TII->hasAnyModifiersSet(MaybeCopy))
+    return Register();
+
+  auto CopySrc = MaybeCopy.getOperand(TII->getFoldableCopySrcIdx(MaybeCopy));
+  if (!CopySrc.isReg() || CopySrc.getReg() != Src ||
+      CopySrc.getSubReg() != AMDGPU::NoSubRegister)
+    return Register();
 
   MachineOperand Dst = MaybeCopy.getOperand(0);
-  if (!Dst.isReg())
-    return std::nullopt;
+  if (!Dst.isReg() || Dst.getSubReg() != AMDGPU::NoSubRegister)
+    return Register();
+
+  Register DstReg = Dst.getReg();
+  if (TRI->getPhysRegBaseClass(DstReg) != TRI->getPhysRegBaseClass(Src))
+    return Register();
+
+  if (NewDstRC && !NewDstRC->contains(DstReg.asMCReg()))
+    return Register();
 
-  Register Reg = Dst.getReg();
   // Check that it is safe to define the copy destination register at the
   // current position of the two-address instruction.
   SmallPtrSet<MachineInstr *, 1> Ignore{&TwoAddress, &MaybeCopy};
-  if (!RDI->isSafeToDefRegAt(&TwoAddress, Reg, Ignore))
-    return std::nullopt;
+  if (!RDI->isSafeToDefRegAt(&TwoAddress, DstReg, Ignore))
+    return Register();
 
   // Check that the two-address instruction and the copy have the same exec
   // mask.
   if (!RDI->hasSameReachingDef(&TwoAddress, &MaybeCopy, AMDGPU::EXEC))
-    return std::nullopt;
+    return Register();
 
-  return Reg;
+  return DstReg;
 }
 
 Register SIPreEmitPeephole::shouldReplaceWithThreeAddress(
@@ -838,54 +883,96 @@ Register SIPreEmitPeephole::shouldReplaceWithThreeAddress(
   if (!Dst || !Dst->isReg())
     return Register();
 
-  SmallVector<Register, 2> DestRegs;
-  Register Sub0 = TRI->getSubReg(Dst->getReg(), AMDGPU::sub0);
-  Register Sub1 = TRI->getSubReg(Dst->getReg(), AMDGPU::sub1);
-  if (Sub0 && Sub1) {
-    DestRegs.push_back(Sub0);
-    DestRegs.push_back(Sub1);
-  } else
-    DestRegs.push_back(Dst->getReg());
-
-  SmallVector<Register, 2> Replacements;
-  for (Register SubReg : DestRegs) {
-    if (!SubReg.isPhysical())
+  const TargetRegisterClass *NewDstRC =
+      TII->getRegClass(MI.getDesc(), Dst->getOperandNo());
+
+  auto CheckRegisterCopies =
+      [&](Register Reg, const TargetRegisterClass *ReplacementRC) -> Register {
+    if (!Reg.isPhysical())
       return Register();
-    if (RDI->getLocalLiveOutMIDef(MI.getParent(), SubReg) == &MI)
+    if (RDI->getLocalLiveOutMIDef(MI.getParent(), Reg) == &MI)
       return Register();
 
     SmallPtrSet<MachineInstr *, 1> Uses;
-    RDI->getReachingLocalUses(&MI, SubReg, Uses);
+    RDI->getReachingLocalUses(&MI, Reg, Uses);
     if (Uses.size() != 1)
       return Register();
 
     MachineInstr &Use = **Uses.begin();
-    std::optional<Register> MaybeRegister = checkCopy(MI, Use, SubReg);
+    Register ReplacementReg = checkCopy(MI, Use, Reg, ReplacementRC);
 
-    if (!MaybeRegister)
+    if (!ReplacementReg)
       return Register();
-    Replacements.push_back(*MaybeRegister);
+
     Copies.push_back(&Use);
-  }
+    return ReplacementReg;
+  };
+
+  Register DstReg = Dst->getReg();
+  if (Register Replacement = CheckRegisterCopies(DstReg, NewDstRC))
+    return Replacement;
 
-  if (Replacements.size() > 2)
+  // If we didn't find a copy for the full register, we might be dealing with a
+  // 64-bit register and copies for the individual parts. Check both
+  // subregisters for matching copies.
+  Register Sub0 = TRI->getSubReg(Dst->getReg(), AMDGPU::sub0);
+  Register Sub1 = TRI->getSubReg(Dst->getReg(), AMDGPU::sub1);
+  if (!Sub0 || !Sub1)
     return Register();
 
-  if (Replacements.size() == 1)
-    return Replacements.front();
+  auto Sub0Replacment = CheckRegisterCopies(Sub0, nullptr);
+  auto Sub1Replacement = CheckRegisterCopies(Sub1, nullptr);
+  if (!Sub0Replacment || !Sub1Replacement)
+    return Register();
 
   // Try to identify a matching super-register/tuple for the f64 case and abort
   // the transformation if there is none.
-  Register Tuple = TRI->getMatchingSuperReg(Replacements.front(), AMDGPU::sub0,
-                                            TRI->getVGPR64Class());
+  if (!NewDstRC)
+    return Register();
+  Register Tuple =
+      TRI->getMatchingSuperReg(Sub0Replacment, AMDGPU::sub0, NewDstRC);
   if (!Tuple)
     return Register();
 
-  if (TRI->getSubReg(Tuple, AMDGPU::sub1) != Replacements[1].asMCReg())
+  if (TRI->getSubReg(Tuple, AMDGPU::sub1) != Sub1Replacement.asMCReg())
     return Register();
   return Tuple;
 }
 
+bool SIPreEmitPeephole::convertToThreeAddressInstr(
+    ThreeAddressCandidate &Cand) {
+  MachineInstr *ThreeAddrsInst =
+      TII->convertToThreeAddress(*Cand.TwoAddrMI, nullptr, nullptr);
+  if (!ThreeAddrsInst)
+    return false;
+  MachineOperand &NewDst = ThreeAddrsInst->getOperand(0);
+  assert(NewDst.isReg());
+
+  // The two asserts are defensive for the case that the implementation of
+  // convertToThreeAddress changes in a manner incompatible with this peephole.
+  const TargetRegisterClass *DstRC =
+      TII->getRegClass(ThreeAddrsInst->getDesc(), NewDst.getOperandNo());
+  assert((!DstRC || DstRC->contains(Cand.NewDst.asMCReg())) &&
+         "candidate prechecks should guarantee a legal destination class");
+
+  auto RegistersOverlap = [&](MachineOperand &MO) {
+    if (!MO.isReg())
+      return false;
+    return TRI->regsOverlap(Cand.NewDst, MO.getReg());
+  };
+  assert((!NewDst.isEarlyClobber() ||
+          llvm::none_of(ThreeAddrsInst->uses(), RegistersOverlap)) &&
+         "three-address instruction must not early-clobber the new destination "
+         "register");
+
+  NewDst.setReg(Cand.NewDst);
+  NewDst.setIsRenamable(false);
+  Cand.TwoAddrMI->eraseFromParent();
+  llvm::for_each(Cand.CopiesToRemove,
+                 [](MachineInstr *Copy) { Copy->eraseFromParent(); });
+  return true;
+}
+
 PreservedAnalyses
 llvm::SIPreEmitPeepholePass::run(MachineFunction &MF,
                                  MachineFunctionAnalysisManager &MFAM) {
@@ -948,9 +1035,7 @@ bool SIPreEmitPeephole::run(MachineFunction &MF, MachineLoopInfo *LoopInfo,
       if (MI.isBundle())
         continue;
 
-      unsigned Opc = MI.getOpcode();
-      if (Opc != AMDGPU::V_FMAC_F64_e64 && Opc != AMDGPU::V_FMAC_F32_e64 &&
-          Opc != AMDGPU::V_FMAC_F64_e32 && Opc != AMDGPU::V_FMAC_F32_e32)
+      if (!isLateThreeAddrPeepholeCandidate(MI))
         continue;
 
       SmallVector<MachineInstr *, 2> Copies;
@@ -958,28 +1043,8 @@ bool SIPreEmitPeephole::run(MachineFunction &MF, MachineLoopInfo *LoopInfo,
         Candidates.emplace_back(MI, Dst, Copies);
     }
 
-    for (ThreeAddressCandidate &Cand : Candidates) {
-      MachineInstr *ThreeAddrsInst =
-          TII->convertToThreeAddress(*Cand.TwoAddrMI, nullptr, nullptr);
-      if (!ThreeAddrsInst)
-        continue;
-      MachineOperand &NewDst = ThreeAddrsInst->getOperand(0);
-      assert(NewDst.isReg());
-
-      const TargetRegisterClass *DstRC =
-          TII->getRegClass(ThreeAddrsInst->getDesc(), NewDst.getOperandNo());
-      if (DstRC && !DstRC->contains(Cand.NewDst.asMCReg())) {
-        ThreeAddrsInst->eraseFromParent();
-        continue;
-      }
-
-      NewDst.setReg(Cand.NewDst);
-      NewDst.setIsRenamable(false);
-      Cand.TwoAddrMI->eraseFromParent();
-      llvm::for_each(Cand.CopiesToRemove,
-                     [](MachineInstr *Copy) { Copy->eraseFromParent(); });
-      Changed = true;
-    }
+    for (ThreeAddressCandidate &Cand : Candidates)
+      Changed |= convertToThreeAddressInstr(Cand);
 
     if (!ST.hasVGPRIndexMode())
       continue;
diff --git a/llvm/test/CodeGen/AMDGPU/mad-mix.ll b/llvm/test/CodeGen/AMDGPU/mad-mix.ll
index 4e1acf114da5d..e308216402cc6 100644
--- a/llvm/test/CodeGen/AMDGPU/mad-mix.ll
+++ b/llvm/test/CodeGen/AMDGPU/mad-mix.ll
@@ -299,8 +299,7 @@ define <2 x float> @v_mad_mix_v2f32_shuffle(<2 x half> %src0, <2 x half> %src1,
 ; GFX9GEN-NEXT:    v_cvt_f32_f16_sdwa v2, v2 dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:WORD_1
 ; GFX9GEN-NEXT:    v_cvt_f32_f16_sdwa v1, v1 dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:WORD_1
 ; GFX9GEN-NEXT:    v_mad_f32 v0, v3, v0, v2
-; GFX9GEN-NEXT:    v_mac_f32_e32 v2, v4, v1
-; GFX9GEN-NEXT:    v_mov_b32_e32 v1, v2
+; GFX9GEN-NEXT:    v_mad_f32 v1, v4, v1, v2
 ; GFX9GEN-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; VI-LABEL: v_mad_mix_v2f32_shuffle:
@@ -312,8 +311,7 @@ define <2 x float> @v_mad_mix_v2f32_shuffle(<2 x half> %src0, <2 x half> %src1,
 ; VI-NEXT:    v_cvt_f32_f16_sdwa v2, v2 dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:WORD_1
 ; VI-NEXT:    v_cvt_f32_f16_sdwa v1, v1 dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:WORD_1
 ; VI-NEXT:    v_mad_f32 v0, v3, v0, v2
-; VI-NEXT:    v_mac_f32_e32 v2, v4, v1
-; VI-NEXT:    v_mov_b32_e32 v1, v2
+; VI-NEXT:    v_mad_f32 v1, v4, v1, v2
 ; VI-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; CI-LABEL: v_mad_mix_v2f32_shuffle:
@@ -445,8 +443,7 @@ define float @v_mad_mix_f32_absf16lo_f16lo_f16lo(half %src0, half %src1, half %s
 ; SDAG-CI-NEXT:    v_cvt_f32_f16_e32 v3, v1
 ; SDAG-CI-NEXT:    v_cvt_f32_f16_e32 v1, v2
 ; SDAG-CI-NEXT:    v_cvt_f32_f16_e64 v0, |v0|
-; SDAG-CI-NEXT:    v_mac_f32_e32 v1, v0, v3
-; SDAG-CI-NEXT:    v_mov_b32_e32 v0, v1
+; SDAG-CI-NEXT:    v_mad_f32 v0, v0, v3, v1
 ; SDAG-CI-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GISEL-CI-LABEL: v_mad_mix_f32_absf16lo_f16lo_f16lo:
diff --git a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac-f16.mir b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac-f16.mir
new file mode 100644
index 0000000000000..6d49eefb14c32
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac-f16.mir
@@ -0,0 +1,41 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1100 -run-pass=si-pre-emit-peephole -o - %s | FileCheck %s
+
+---
+name: fmac_f16_t16_e64_return_copy
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr1_lo16, $vgpr2_lo16, $vgpr4_lo16
+
+    ; CHECK-LABEL: name: fmac_f16_t16_e64_return_copy
+    ; CHECK: liveins: $vgpr1_lo16, $vgpr2_lo16, $vgpr4_lo16
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: $vgpr0_lo16 = V_MOV_B16_t16_e64 0, $vgpr1_lo16, 0, implicit $exec
+    ; CHECK-NEXT: $vgpr0_lo16 = nofpexcept V_FMA_F16_gfx9_t16_e64 0, $vgpr4_lo16, 0, $vgpr1_lo16, 0, killed $vgpr2_lo16, 0, 0, 0, implicit $mode, implicit $exec
+    ; CHECK-NEXT: S_ENDPGM 0, implicit killed $vgpr0_lo16
+    $vgpr0_lo16 = V_MOV_B16_t16_e64 0, $vgpr1_lo16, 0, implicit $exec
+    $vgpr2_lo16 = nofpexcept V_FMAC_F16_t16_e64 0, $vgpr4_lo16, 0, $vgpr1_lo16, 0, killed $vgpr2_lo16, 0, 0, 0, implicit $mode, implicit $exec
+    $vgpr0_lo16 = V_MOV_B16_t16_e64 0, killed $vgpr2_lo16, 0, implicit $exec
+    S_ENDPGM 0, implicit killed $vgpr0_lo16
+...
+
+---
+name: fmac_f16_t16_e64_return_copy_with_modifier
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr1_lo16, $vgpr2_lo16, $vgpr4_lo16
+
+    ; CHECK-LABEL: name: fmac_f16_t16_e64_return_copy_with_modifier
+    ; CHECK: liveins: $vgpr1_lo16, $vgpr2_lo16, $vgpr4_lo16
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: $vgpr0_lo16 = V_MOV_B16_t16_e64 0, $vgpr1_lo16, 0, implicit $exec
+    ; CHECK-NEXT: $vgpr2_lo16 = nofpexcept V_FMAC_F16_t16_e64 0, $vgpr4_lo16, 0, $vgpr1_lo16, 0, killed $vgpr2_lo16, 0, 0, 0, implicit $mode, implicit $exec
+    ; CHECK-NEXT: $vgpr0_lo16 = V_MOV_B16_t16_e64 1, killed $vgpr2_lo16, 0, implicit $exec
+    ; CHECK-NEXT: S_ENDPGM 0, implicit killed $vgpr0_lo16
+    $vgpr0_lo16 = V_MOV_B16_t16_e64 0, $vgpr1_lo16, 0, implicit $exec
+    $vgpr2_lo16 = nofpexcept V_FMAC_F16_t16_e64 0, $vgpr4_lo16, 0, $vgpr1_lo16, 0, killed $vgpr2_lo16, 0, 0, 0, implicit $mode, implicit $exec
+    $vgpr0_lo16 = V_MOV_B16_t16_e64 1, killed $vgpr2_lo16, 0, implicit $exec
+    S_ENDPGM 0, implicit killed $vgpr0_lo16
+...
diff --git a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
index d3391e2cda4e3..d900d3f1fa8e1 100644
--- a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
+++ b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
@@ -25,6 +25,29 @@ body: |
     SI_RETURN implicit killed $vgpr0, implicit killed $vgpr1
 ...
 
+---
+name: fmac_f64_e32_return_full_copy
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1
+
+    ; CHECK-LABEL: name: fmac_f64_e32_return_full_copy
+    ; CHECK: liveins: $vgpr0, $vgpr1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: renamable $vgpr2_vgpr3 = nofpexcept V_RSQ_F64_e32 $vgpr0_vgpr1, implicit $mode, implicit $exec
+    ; CHECK-NEXT: renamable $vgpr4_vgpr5 = nofpexcept V_MUL_F64_e64 0, $vgpr2_vgpr3, 0, $vgpr0_vgpr1, 0, 0, implicit $mode, implicit $exec
+    ; CHECK-NEXT: renamable $vgpr0_vgpr1 = nofpexcept V_FMA_F64_e64 0, killed $vgpr0_vgpr1, 0, $vgpr2_vgpr3, 0, 4602678819172646912, 0, 0, implicit $mode, implicit $exec
+    ; CHECK-NEXT: $vgpr0_vgpr1 = nofpexcept V_FMA_F64_e64 0, killed $vgpr4_vgpr5, 0, killed $vgpr0_vgpr1, 0, killed $vgpr2_vgpr3, 0, 0, implicit $mode, implicit $exec
+    ; CHECK-NEXT: SI_RETURN implicit killed $vgpr0, implicit killed $vgpr1
+    renamable $vgpr2_vgpr3 = nofpexcept V_RSQ_F64_e32 $vgpr0_vgpr1, implicit $mode, implicit $exec
+    renamable $vgpr4_vgpr5 = nofpexcept V_MUL_F64_e64 0, $vgpr2_vgpr3, 0, $vgpr0_vgpr1, 0, 0, implicit $mode, implicit $exec
+    renamable $vgpr0_vgpr1 = nofpexcept V_FMA_F64_e64 0, killed $vgpr0_vgpr1, 0, $vgpr2_vgpr3, 0, 4602678819172646912, 0, 0, implicit $mode, implicit $exec
+    renamable $vgpr2_vgpr3 = nofpexcept V_FMAC_F64_e32 killed renamable $vgpr4_vgpr5, killed renamable $vgpr0_vgpr1, killed renamable $vgpr2_vgpr3, implicit $mode, implicit $exec
+    $vgpr0_vgpr1 = V_MOV_B64_e32 killed $vgpr2_vgpr3, implicit $exec, implicit $exec
+    SI_RETURN implicit killed $vgpr0, implicit killed $vgpr1
+...
+
 ---
 name: fmac_f32_e32_return_copy
 tracksRegLiveness: true
@@ -48,6 +71,25 @@ body: |
     SI_RETURN implicit killed $vgpr0
 ...
 
+---
+name: mac_f32_e32_return_copy
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr2, $vgpr4
+
+    ; CHECK-LABEL: name: mac_f32_e32_return_copy
+    ; CHECK: liveins: $vgpr0, $vgpr2, $vgpr4
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: $vgpr1 = V_MOV_B32_e32 $vgpr0, implicit $exec
+    ; CHECK-NEXT: $vgpr1 = nofpexcept V_MAD_F32_e64 0, killed $vgpr4, 0, killed $vgpr0, 0, killed $vgpr2, 0, 0, implicit $mode, implicit $exec
+    ; CHECK-NEXT: S_ENDPGM 0, implicit killed $vgpr1
+    $vgpr1 = V_MOV_B32_e32 $vgpr0, implicit $exec
+    $vgpr2 = nofpexcept V_MAC_F32_e32 killed $vgpr4, killed $vgpr0, killed $vgpr2, implicit $mode, implicit $exec
+    $vgpr1 = V_MOV_B32_e32 killed $vgpr2, implicit $exec
+    S_ENDPGM 0, implicit killed $vgpr1
+...
+
 ---
 name: fmac_f32_e32_copy_live_out
 tracksRegLiveness: true

>From 81dc2f44f2417796d37445ea221d20e34242f841 Mon Sep 17 00:00:00 2001
From: Lukas Sommer <lukas.sommer at amd.com>
Date: Wed, 15 Jul 2026 04:56:57 -0500
Subject: [PATCH 6/6] Address PR feedback

Signed-off-by: Lukas Sommer <lukas.sommer at amd.com>
---
 llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp  | 49 +++++++++++--------
 .../AMDGPU/si-pre-emit-peephole-fmac.mir      | 43 ++++++++++++++++
 2 files changed, 71 insertions(+), 21 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp b/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
index ebd5b89e3154f..c13139b656cc8 100644
--- a/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
+++ b/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
@@ -96,13 +96,19 @@ class SIPreEmitPeephole {
                           ArrayRef<MachineInstr *> Copies)
         : TwoAddrMI(&TwoAddrMI), NewDst(NewDst), CopiesToRemove(Copies) {}
   };
+
   // Return true if MI is a candidate for late conversion to a three-address
   // instruction to avoid copies.
   bool isLateThreeAddrPeepholeCandidate(const MachineInstr &MI) const;
-  // Check if MaybeCopy is a simple copy of Src. If it is a supported
-  // copy, return the destination register of the copy.
+
+  // Check if MaybeCopy is a simple copy of Src. If NewDstRC is non-null, also
+  // require the copy destination to belong to that register class. The class
+  // check is omitted for copies of individual subregisters; their reconstructed
+  // super-register is checked against the destination class by the caller.
+  // Return the destination register of a supported copy.
   Register checkCopy(MachineInstr &TwoAddress, MachineInstr &MaybeCopy,
                      Register Src, const TargetRegisterClass *NewDstRC);
+
   // Check whether the two-address instruction should be replaced with a
   // three-address instruction to avoid copies of the output registers. If the
   // replacement should take place, returns the destination register for the
@@ -804,13 +810,13 @@ MachineInstrBuilder SIPreEmitPeephole::createUnpackedMI(MachineInstr &I,
 
 bool SIPreEmitPeephole::isLateThreeAddrPeepholeCandidate(
     const MachineInstr &MI) const {
-  // This is a subset of the opcodes that are convertible to three-address
-  // instructions according to . This is intended. For some cases,
-  // convertToThreeAddress could early-clobber, which would require us to roll
-  // back the transformation. However, rollback is not side-effect free for some
-  // cases in convertToThreeAddress that fold immediates. Therefore, we limit
-  // this peephole to only the instructions for which rollback will not be
-  // necessary.
+  // This is only a subset of the opcodes that are convertible to three-address
+  // instructions according to MachineInstr::isConvertibleTo3Addr. This is
+  // intended. For some cases, convertToThreeAddress could early-clobber, which
+  // would require us to roll back the transformation. However, rollback is not
+  // side-effect free for some cases in convertToThreeAddress that fold
+  // immediates. Therefore, we limit this peephole to only the instructions for
+  // which rollback will not be necessary.
   switch (MI.getOpcode()) {
   case AMDGPU::V_MAC_F16_e32:
   case AMDGPU::V_MAC_F16_e64:
@@ -847,12 +853,13 @@ Register SIPreEmitPeephole::checkCopy(MachineInstr &TwoAddress,
   if (TII->hasAnyModifiersSet(MaybeCopy))
     return Register();
 
-  auto CopySrc = MaybeCopy.getOperand(TII->getFoldableCopySrcIdx(MaybeCopy));
+  MachineOperand &CopySrc =
+      MaybeCopy.getOperand(TII->getFoldableCopySrcIdx(MaybeCopy));
   if (!CopySrc.isReg() || CopySrc.getReg() != Src ||
       CopySrc.getSubReg() != AMDGPU::NoSubRegister)
     return Register();
 
-  MachineOperand Dst = MaybeCopy.getOperand(0);
+  MachineOperand &Dst = MaybeCopy.getOperand(0);
   if (!Dst.isReg() || Dst.getSubReg() != AMDGPU::NoSubRegister)
     return Register();
 
@@ -860,18 +867,18 @@ Register SIPreEmitPeephole::checkCopy(MachineInstr &TwoAddress,
   if (TRI->getPhysRegBaseClass(DstReg) != TRI->getPhysRegBaseClass(Src))
     return Register();
 
-  if (NewDstRC && !NewDstRC->contains(DstReg.asMCReg()))
+  if (NewDstRC && !NewDstRC->contains(DstReg))
     return Register();
 
   // Check that it is safe to define the copy destination register at the
   // current position of the two-address instruction.
-  SmallPtrSet<MachineInstr *, 1> Ignore{&TwoAddress, &MaybeCopy};
+  SmallPtrSet<MachineInstr *, 2> Ignore{&TwoAddress, &MaybeCopy};
   if (!RDI->isSafeToDefRegAt(&TwoAddress, DstReg, Ignore))
     return Register();
 
   // Check that the two-address instruction and the copy have the same exec
   // mask.
-  if (!RDI->hasSameReachingDef(&TwoAddress, &MaybeCopy, AMDGPU::EXEC))
+  if (!RDI->hasSameReachingDef(&TwoAddress, &MaybeCopy, TRI->getExec()))
     return Register();
 
   return DstReg;
@@ -920,9 +927,9 @@ Register SIPreEmitPeephole::shouldReplaceWithThreeAddress(
   if (!Sub0 || !Sub1)
     return Register();
 
-  auto Sub0Replacment = CheckRegisterCopies(Sub0, nullptr);
-  auto Sub1Replacement = CheckRegisterCopies(Sub1, nullptr);
-  if (!Sub0Replacment || !Sub1Replacement)
+  Register Sub0Replacement = CheckRegisterCopies(Sub0, nullptr);
+  Register Sub1Replacement = CheckRegisterCopies(Sub1, nullptr);
+  if (!Sub0Replacement || !Sub1Replacement)
     return Register();
 
   // Try to identify a matching super-register/tuple for the f64 case and abort
@@ -930,11 +937,11 @@ Register SIPreEmitPeephole::shouldReplaceWithThreeAddress(
   if (!NewDstRC)
     return Register();
   Register Tuple =
-      TRI->getMatchingSuperReg(Sub0Replacment, AMDGPU::sub0, NewDstRC);
+      TRI->getMatchingSuperReg(Sub0Replacement, AMDGPU::sub0, NewDstRC);
   if (!Tuple)
     return Register();
 
-  if (TRI->getSubReg(Tuple, AMDGPU::sub1) != Sub1Replacement.asMCReg())
+  if (Register(TRI->getSubReg(Tuple, AMDGPU::sub1)) != Sub1Replacement)
     return Register();
   return Tuple;
 }
@@ -952,7 +959,7 @@ bool SIPreEmitPeephole::convertToThreeAddressInstr(
   // convertToThreeAddress changes in a manner incompatible with this peephole.
   const TargetRegisterClass *DstRC =
       TII->getRegClass(ThreeAddrsInst->getDesc(), NewDst.getOperandNo());
-  assert((!DstRC || DstRC->contains(Cand.NewDst.asMCReg())) &&
+  assert((!DstRC || DstRC->contains(Cand.NewDst)) &&
          "candidate prechecks should guarantee a legal destination class");
 
   auto RegistersOverlap = [&](MachineOperand &MO) {
@@ -1031,7 +1038,7 @@ bool SIPreEmitPeephole::run(MachineFunction &MF, MachineLoopInfo *LoopInfo,
     SmallVector<ThreeAddressCandidate> Candidates;
     // First, collect the candidates. We can't perform the transformation right
     // away, it would invalidate the iterator.
-    for (auto &MI : make_early_inc_range(MBB.instrs())) {
+    for (MachineInstr &MI : MBB.instrs()) {
       if (MI.isBundle())
         continue;
 
diff --git a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
index d900d3f1fa8e1..f68d89db6f987 100644
--- a/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
+++ b/llvm/test/CodeGen/AMDGPU/si-pre-emit-peephole-fmac.mir
@@ -48,6 +48,49 @@ body: |
     SI_RETURN implicit killed $vgpr0, implicit killed $vgpr1
 ...
 
+---
+name: fmac_f64_e32_return_copy_odd_destination
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr4, $vgpr5, $vgpr6, $vgpr7, $vgpr8, $vgpr9
+
+    ; The copied halves form an odd-start tuple, which is not in the aligned
+    ; destination register class.
+    ; CHECK-LABEL: name: fmac_f64_e32_return_copy_odd_destination
+    ; CHECK: liveins: $vgpr4, $vgpr5, $vgpr6, $vgpr7, $vgpr8, $vgpr9
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: renamable $vgpr6_vgpr7 = nofpexcept V_FMAC_F64_e32 killed $vgpr4_vgpr5, killed $vgpr8_vgpr9, killed $vgpr6_vgpr7, implicit $mode, implicit $exec
+    ; CHECK-NEXT: $vgpr1 = V_MOV_B32_e32 killed $vgpr6, implicit $exec, implicit $exec
+    ; CHECK-NEXT: $vgpr2 = V_MOV_B32_e32 killed $vgpr7, implicit $exec, implicit $exec
+    ; CHECK-NEXT: SI_RETURN implicit killed $vgpr1, implicit killed $vgpr2
+    renamable $vgpr6_vgpr7 = nofpexcept V_FMAC_F64_e32 killed renamable $vgpr4_vgpr5, killed renamable $vgpr8_vgpr9, killed renamable $vgpr6_vgpr7, implicit $mode, implicit $exec
+    $vgpr1 = V_MOV_B32_e32 killed $vgpr6, implicit $exec, implicit $exec
+    $vgpr2 = V_MOV_B32_e32 killed $vgpr7, implicit $exec, implicit $exec
+    SI_RETURN implicit killed $vgpr1, implicit killed $vgpr2
+...
+
+---
+name: fmac_f64_e32_return_copy_non_tuple
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr4, $vgpr5, $vgpr6, $vgpr7, $vgpr8, $vgpr9
+
+    ; The copied halves are not subregisters of the same 64-bit tuple.
+    ; CHECK-LABEL: name: fmac_f64_e32_return_copy_non_tuple
+    ; CHECK: liveins: $vgpr4, $vgpr5, $vgpr6, $vgpr7, $vgpr8, $vgpr9
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: renamable $vgpr6_vgpr7 = nofpexcept V_FMAC_F64_e32 killed $vgpr4_vgpr5, killed $vgpr8_vgpr9, killed $vgpr6_vgpr7, implicit $mode, implicit $exec
+    ; CHECK-NEXT: $vgpr1 = V_MOV_B32_e32 killed $vgpr6, implicit $exec, implicit $exec
+    ; CHECK-NEXT: $vgpr3 = V_MOV_B32_e32 killed $vgpr7, implicit $exec, implicit $exec
+    ; CHECK-NEXT: SI_RETURN implicit killed $vgpr1, implicit killed $vgpr3
+    renamable $vgpr6_vgpr7 = nofpexcept V_FMAC_F64_e32 killed renamable $vgpr4_vgpr5, killed renamable $vgpr8_vgpr9, killed renamable $vgpr6_vgpr7, implicit $mode, implicit $exec
+    $vgpr1 = V_MOV_B32_e32 killed $vgpr6, implicit $exec, implicit $exec
+    $vgpr3 = V_MOV_B32_e32 killed $vgpr7, implicit $exec, implicit $exec
+    SI_RETURN implicit killed $vgpr1, implicit killed $vgpr3
+...
+
 ---
 name: fmac_f32_e32_return_copy
 tracksRegLiveness: true



More information about the llvm-commits mailing list