[llvm] [AMDGPU] PreRA Peephole: EliminateSaveRestore (PR #191583)

Zeng Wu via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 10 17:59:10 PDT 2026


https://github.com/zwu-2025 created https://github.com/llvm/llvm-project/pull/191583

A new method is added in pre-ra-peephole  and it tries to find the pattern on GFX950
```
v_mov tmp, orig         ; save
orig = op0(tmp)   ...    ; modify (overwrites orig, reads tmp)
op1(orig, )              ; uses of orig
v_mov orig, tmp         ; restore
```
is changed to:
```
tmp = op0(orig, )        ; write to tmp instead
op1(tmp)               ; replace uses of orig with tmp
save and restore movs are dead
```


>From 62cd0f385cccba7e68451507754a43fc1fc5a80f Mon Sep 17 00:00:00 2001
From: zwu-2025 <Zeng.Wu2 at amd.com>
Date: Thu, 2 Apr 2026 21:55:03 +0000
Subject: [PATCH] [AMDGPU] PreRA EliminateSaveRestore

---
 .../Target/AMDGPU/GCNPreRAOptimizations.cpp   | 270 ++++++++++++++++--
 .../AMDGPU/amdgcn-eliminate-save-restore.mir  | 181 ++++++++++++
 2 files changed, 424 insertions(+), 27 deletions(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/amdgcn-eliminate-save-restore.mir

diff --git a/llvm/lib/Target/AMDGPU/GCNPreRAOptimizations.cpp b/llvm/lib/Target/AMDGPU/GCNPreRAOptimizations.cpp
index cd56887fd46a8..681294f953cf7 100644
--- a/llvm/lib/Target/AMDGPU/GCNPreRAOptimizations.cpp
+++ b/llvm/lib/Target/AMDGPU/GCNPreRAOptimizations.cpp
@@ -36,7 +36,9 @@
 #include "MCTargetDesc/AMDGPUMCTargetDesc.h"
 #include "SIRegisterInfo.h"
 #include "llvm/CodeGen/LiveIntervals.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
 #include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineInstr.h"
 #include "llvm/InitializePasses.h"
 
 using namespace llvm;
@@ -57,6 +59,7 @@ class GCNPreRAOptimizationsImpl {
 public:
   GCNPreRAOptimizationsImpl(LiveIntervals *LS) : LIS(LS) {}
   bool run(MachineFunction &MF);
+  bool eliminateSaveRestore(MachineBasicBlock &);
 };
 
 class GCNPreRAOptimizationsLegacy : public MachineFunctionPass {
@@ -238,6 +241,214 @@ GCNPreRAOptimizationsPass::run(MachineFunction &MF,
   return PreservedAnalyses::all();
 }
 
+using InstrIt = MachineBasicBlock::iterator;
+
+static InstrIt findModifyInstr(const SIRegisterInfo *TRI, InstrIt Start,
+                               InstrIt End, Register OrigIn, Register Tmp,
+                               Register &OrigMod) {
+  InstrIt Next = Start;
+  while (Next != End) {
+    if (Next->isDebugOrPseudoInstr()) {
+      ++Next;
+      continue;
+    }
+    // Mask is changed in the current block and bail out for now.
+    if (Next->modifiesRegister(AMDGPU::EXEC, TRI)) {
+      return End;
+    }
+
+    // def/use are tied which make swap complex, skip for now.
+    for (size_t Idx = 0; Idx < Next->getNumDefs(); Idx++)
+      if (Next->isRegTiedToUseOperand(Idx))
+        return End;
+
+    for (size_t Idx = Next->getNumDefs(); Idx < Next->getNumOperands(); Idx++)
+      if (Next->isRegTiedToDefOperand(Idx))
+        return End;
+
+    // If the value of Tmp is changed, bail out.
+    if (Next->modifiesRegister(Tmp, TRI))
+      return End;
+
+    // If Next reads register Tmp, check this the `modify`` instruction.
+    if (Next->readsRegister(Tmp, TRI)) {
+      // check the new virtual register it defines
+      for (const MachineOperand &MO : Next->defs()) {
+        if (MO.isReg() && MO.getReg().isVirtual()) {
+          // `Modify` insturction is found and store the renamed Orig register
+          OrigMod = MO.getReg();
+          return Next;
+        }
+      }
+
+      // Tmp is read but no OrigMod is found, bail out.
+      return End;
+    }
+
+    ++Next;
+  }
+
+  return Next;
+};
+
+static bool isVMov(InstrIt I) {
+  return I->getOpcode() == AMDGPU::V_MOV_B32_e32 ||
+         I->getOpcode() == AMDGPU::V_MOV_B32_e64;
+};
+
+static InstrIt findRestoreInstr(const SIRegisterInfo *TRI, InstrIt Start,
+                                InstrIt End, Register Orig, Register Tmp,
+                                llvm::SmallVector<InstrIt, 4> &Candidates) {
+  InstrIt Next = Start;
+  while (Next != End) {
+    if (Next->isDebugOrPseudoInstr()) {
+      ++Next;
+      continue;
+    }
+
+    if (Next->modifiesRegister(AMDGPU::EXEC, TRI))
+      return End;
+
+    // Tmp should not be changed or else `Restore` will not make sense.
+    if (Next->modifiesRegister(Tmp, TRI))
+      return End;
+
+    // To check it is the restore instruction: read-only Tmp
+    if (isVMov(Next) && Next->getNumOperands() >= 2 &&
+        Next->getOperand(1).isReg() && Next->getOperand(1).getReg() == Tmp) {
+      return Next;
+    }
+
+    if (Next->readsRegister(Orig, TRI))
+      Candidates.push_back(Next);
+
+    ++Next;
+  }
+  return Next;
+}
+
+// Eliminate save-modify-restore.
+// This pass is after PHIElimination and before ReaAlloc
+// v_mov tmp, orig         ; save
+// orig = op0(tmp)   ...    ; modify (overwrites orig, reads tmp)
+// op1(orig, )              ; uses of orig
+// v_mov orig, tmp         ; restore
+// is changed to:
+// tmp = op0(orig, )        ; write to tmp instead
+// op1(tmp)               ; replace uses of orig with tmp
+// save and restore movs are dead
+bool GCNPreRAOptimizationsImpl::eliminateSaveRestore(MachineBasicBlock &MBB) {
+  if (MBB.empty())
+    return false;
+
+  bool Updated = true;
+  bool Changed = false;
+  while (Updated) {
+    Updated = false;
+    InstrIt I = MBB.begin();
+    InstrIt E = MBB.end();
+    while (I != E) {
+      // find the start V_MOV tmp, orig
+      if (!isVMov(I) || !I->getOperand(1).isReg() ||
+          I->isDebugOrPseudoInstr()) {
+        ++I;
+        continue;
+      }
+
+      Register Orig = I->getOperand(1).getReg();
+      Register Tmp = I->getOperand(0).getReg();
+
+      // Skip if the operands are physical reigsters.
+      if (!Tmp.isVirtual() || !Orig.isVirtual()) {
+        ++I;
+        continue;
+      }
+
+      InstrIt Start = I;
+      Register OrigMod;
+      InstrIt ModifyInst =
+          findModifyInstr(TRI, std::next(Start), E, Orig, Tmp, OrigMod);
+      if (ModifyInst == E) {
+        // There is no instrution which write to orig and read tmp for V_MOV
+        // tmp, orig. Try again from the next V_MOV.
+        I = std::next(Start);
+        continue;
+      }
+
+      // Find the restore op and collect all instructions read-only OrigMod and
+      // no-write to Tmp between it and the NewCopy
+      llvm::SmallVector<InstrIt, 4> Candidates;
+      InstrIt RestoreInstr = findRestoreInstr(TRI, std::next(ModifyInst), E,
+                                              OrigMod, Tmp, Candidates);
+      if (RestoreInstr == E) {
+        // there is no restore op V_MOV orig, tmp
+        // try again from the next V_MOV.
+        I = std::next(Start);
+        continue;
+      }
+
+      Changed = true;
+      I = std::next(RestoreInstr);
+      LLVM_DEBUG(
+          dbgs() << "Pre-RA Eliminate Save Resotre, Modify Instruction is "
+                 << *ModifyInst << " Restore Instruction is " << *RestoreInstr
+                 << " \n");
+
+      Register OrigRestored = RestoreInstr->getOperand(0).getReg();
+
+      // swap the operands on the Modify instruction
+      for (unsigned Idx = 0; Idx < ModifyInst->getNumOperands(); ++Idx) {
+        MachineOperand &MO = ModifyInst->getOperand(Idx);
+        if (!MO.isReg())
+          continue;
+        if (MO.isDef() && MO.getReg() == OrigMod)
+          MO.setReg(Tmp);
+        if (MO.isUse() && MO.getReg() == Tmp)
+          MO.setReg(Orig);
+      }
+
+      // replace OrigMod, from `Modify` instruction with Tmp
+      for (auto &Candidate : Candidates) {
+        for (MachineOperand &MO : Candidate->operands()) {
+          if (!MO.isImplicit() && MO.isReg() && MO.isUse() &&
+              MO.getReg() == OrigMod) {
+            MO.setReg(Tmp);
+          }
+        }
+      }
+
+      // globally replace any remaining uses of OrigRestored, from the `Restore`
+      // instruction with Tmp
+      MRI->replaceRegWith(OrigRestored, Tmp);
+
+      LIS->RemoveMachineInstrFromMaps(*Start);
+      LIS->RemoveMachineInstrFromMaps(*RestoreInstr);
+
+      Start->eraseFromParent();
+      RestoreInstr->eraseFromParent();
+
+      if (LIS->hasInterval(Orig)) {
+        LIS->removeInterval(Orig);
+        LIS->createAndComputeVirtRegInterval(Orig);
+      }
+
+      if (LIS->hasInterval(Tmp)) {
+        LIS->removeInterval(Tmp);
+        LIS->createAndComputeVirtRegInterval(Tmp);
+      }
+
+      if (LIS->hasInterval(OrigMod))
+        LIS->removeInterval(OrigMod);
+      if (LIS->hasInterval(OrigRestored))
+        LIS->removeInterval(OrigRestored);
+
+      Updated = true;
+    }
+  }
+
+  return Changed;
+}
+
 bool GCNPreRAOptimizationsImpl::run(MachineFunction &MF) {
   const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
   TII = ST.getInstrInfo();
@@ -258,34 +469,39 @@ bool GCNPreRAOptimizationsImpl::run(MachineFunction &MF) {
     Changed |= processReg(Reg);
   }
 
-  if (!ST.useRealTrue16Insts())
-    return Changed;
-
-  // Add RA hints to improve True16 COPY elimination.
-  for (const MachineBasicBlock &MBB : MF) {
-    for (const MachineInstr &MI : MBB) {
-      if (MI.getOpcode() != AMDGPU::COPY)
-        continue;
-      Register Dst = MI.getOperand(0).getReg();
-      Register Src = MI.getOperand(1).getReg();
-      const TargetRegisterClass *DstRC = TRI->getRegClassForReg(*MRI, Dst);
-      bool IsDst16Bit = AMDGPU::VGPR_16RegClass.hasSubClassEq(DstRC);
-      if (Dst.isVirtual() && IsDst16Bit && Src.isPhysical() &&
-          TRI->getRegClassForReg(*MRI, Src) == &AMDGPU::VGPR_32RegClass)
-        MRI->setRegAllocationHint(Dst, 0, TRI->getSubReg(Src, AMDGPU::lo16));
-      if (Src.isVirtual() &&
-          MRI->getRegClass(Src) == &AMDGPU::VGPR_16RegClass &&
-          Dst.isPhysical() && DstRC == &AMDGPU::VGPR_32RegClass)
-        MRI->setRegAllocationHint(Src, 0, TRI->getSubReg(Dst, AMDGPU::lo16));
-      if (!Dst.isVirtual() || !Src.isVirtual())
-        continue;
-      if (MRI->getRegClass(Dst) == &AMDGPU::VGPR_32RegClass &&
-          MRI->getRegClass(Src) == &AMDGPU::VGPR_16RegClass) {
-        MRI->setRegAllocationHint(Dst, AMDGPURI::Size32, Src);
-        MRI->setRegAllocationHint(Src, AMDGPURI::Size16, Dst);
+  if (ST.useRealTrue16Insts()) {
+    // Add RA hints to improve True16 COPY elimination.
+    for (const MachineBasicBlock &MBB : MF) {
+      for (const MachineInstr &MI : MBB) {
+        if (MI.getOpcode() != AMDGPU::COPY)
+          continue;
+        Register Dst = MI.getOperand(0).getReg();
+        Register Src = MI.getOperand(1).getReg();
+        const TargetRegisterClass *DstRC = TRI->getRegClassForReg(*MRI, Dst);
+        bool IsDst16Bit = AMDGPU::VGPR_16RegClass.hasSubClassEq(DstRC);
+        if (Dst.isVirtual() && IsDst16Bit && Src.isPhysical() &&
+            TRI->getRegClassForReg(*MRI, Src) == &AMDGPU::VGPR_32RegClass)
+          MRI->setRegAllocationHint(Dst, 0, TRI->getSubReg(Src, AMDGPU::lo16));
+        if (Src.isVirtual() &&
+            MRI->getRegClass(Src) == &AMDGPU::VGPR_16RegClass &&
+            Dst.isPhysical() && DstRC == &AMDGPU::VGPR_32RegClass)
+          MRI->setRegAllocationHint(Src, 0, TRI->getSubReg(Dst, AMDGPU::lo16));
+        if (!Dst.isVirtual() || !Src.isVirtual())
+          continue;
+        if (MRI->getRegClass(Dst) == &AMDGPU::VGPR_32RegClass &&
+            MRI->getRegClass(Src) == &AMDGPU::VGPR_16RegClass) {
+          MRI->setRegAllocationHint(Dst, AMDGPURI::Size32, Src);
+          MRI->setRegAllocationHint(Src, AMDGPURI::Size16, Dst);
+        }
+        if (IsDst16Bit && MRI->getRegClass(Src) == &AMDGPU::VGPR_32RegClass)
+          MRI->setRegAllocationHint(Dst, AMDGPURI::Size16, Src);
       }
-      if (IsDst16Bit && MRI->getRegClass(Src) == &AMDGPU::VGPR_32RegClass)
-        MRI->setRegAllocationHint(Dst, AMDGPURI::Size16, Src);
+    }
+  }
+
+  if (ST.hasGFX950Insts()) {
+    for (MachineBasicBlock &MBB : MF) {
+      Changed |= eliminateSaveRestore(MBB);
     }
   }
 
diff --git a/llvm/test/CodeGen/AMDGPU/amdgcn-eliminate-save-restore.mir b/llvm/test/CodeGen/AMDGPU/amdgcn-eliminate-save-restore.mir
new file mode 100644
index 0000000000000..91b63f93dfab4
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/amdgcn-eliminate-save-restore.mir
@@ -0,0 +1,181 @@
+# RUN: llc -march=amdgcn -mcpu=gfx950 -run-pass=amdgpu-pre-ra-optimizations --verify-machineinstrs %s -o - | FileCheck %s
+
+# 1. basic case
+name:            test_basic_case
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    %orig:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+
+    ; CHECK-LABEL: name: test_basic_case    
+    ; CHECK: %5:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    ; CHECK-NEXT: %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+    ; CHECK-NEXT: %tmp:vgpr_32 = V_ADD_U32_e32 %5, %other, implicit $exec
+    ; CHECK-NEXT: dead %3:vgpr_32 = V_MUL_LO_U32_e64 %tmp, %tmp, implicit $exec
+    ; CHECK-NEXT: S_ENDPGM 0
+
+    %tmp:vgpr_32 = V_MOV_B32_e32 %orig, implicit $exec
+    %orig:vgpr_32 = V_ADD_U32_e32 %tmp, %other, implicit $exec
+    %2:vgpr_32 = V_MUL_LO_U32_e64 %orig, %orig, implicit $exec
+    %orig:vgpr_32 = V_MOV_B32_e32 %tmp, implicit $exec
+    S_ENDPGM 0
+...
+
+---
+name:            test_orig_clobbered_before_restore
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    ; CHECK-LABEL: name: test_orig_clobbered_before_restore
+    ; CHECK: %5:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    ; CHECK-NEXT: %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+    ; CHECK-NEXT: dead %tmp:vgpr_32 = V_ADD_U32_e32 %5, %other, implicit $exec
+    ; CHECK-NEXT: dead %3:vgpr_32 = V_MOV_B32_e32 88, implicit $exec
+    ; CHECK-NEXT: S_ENDPGM 0
+
+    %orig:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+    %tmp:vgpr_32 = V_MOV_B32_e32 %orig, implicit $exec
+    %orig:vgpr_32 = V_ADD_U32_e32 %tmp, %other, implicit $exec
+    ; clobbered before the following v_mov, it is renamed to %3 here, no impact on the changes.
+    %orig:vgpr_32 = V_MOV_B32_e32 88, implicit $exec
+    %orig:vgpr_32 = V_MOV_B32_e32 %tmp, implicit $exec
+    S_ENDPGM 0
+...
+
+
+name:            test_tmp_clobbered_before_restore
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    ; CHECK-LABEL: name: test_tmp_clobbered_before_restore
+    ; CHECK: %4:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    ; CHECK-NEXT: %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+    ; CHECK-NEXT: %tmp:vgpr_32 = V_MOV_B32_e32 %4, implicit $exec
+    ; CHECK-NEXT: dead %3:vgpr_32 = V_ADD_U32_e32 %tmp, %other, implicit $exec
+    ; CHECK-NEXT: %tmp:vgpr_32 = V_MAC_F32_e32 %other, %other, %tmp, implicit $exec, implicit $mode
+    ; CHECK-NEXT: dead %orig:vgpr_32 = V_MOV_B32_e32 %tmp, implicit $exec
+    ; CHECK-NEXT: S_ENDPGM 0
+  
+    %orig:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+    %tmp:vgpr_32 = V_MOV_B32_e32 %orig, implicit $exec
+    %orig:vgpr_32 = V_ADD_U32_e32 %tmp, %other, implicit $exec
+    ; %tmp is clobbered so no changes should be applied.
+    %tmp:vgpr_32 = V_MAC_F32_e32 %other, %other, %tmp, implicit $exec, implicit $mode
+    %orig:vgpr_32 = V_MOV_B32_e32 %tmp, implicit $exec
+    S_ENDPGM 0
+
+---
+name:            test_phys_reg_bailout
+# $vgpr0 is for %orig and $vgpr1 is for %tmp
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $vgpr0, $vgpr1
+    %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+
+    ; CHECK-LABEL: name: test_phys_reg_bailout
+    ; CHECK: $vgpr1 = V_MOV_B32_e32 $vgpr0
+    ; CHECK: $vgpr0 = V_ADD_U32_e32 $vgpr1, %other
+    ; CHECK: $vgpr0 = V_MOV_B32_e32 $vgpr1
+
+    $vgpr1 = V_MOV_B32_e32 $vgpr0, implicit $exec
+    $vgpr0 = V_ADD_U32_e32 $vgpr1, %other, implicit $exec
+    $vgpr0 = V_MOV_B32_e32 $vgpr1, implicit $exec
+    S_ENDPGM 0
+...
+
+---
+name:            test_exec_redefined
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    %orig:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+
+    ; CHECK-LABEL: name: test_exec_redefined
+    ; CHECK: %4:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    ; CHECK-NEXT: %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+    ; CHECK-NEXT: %tmp:vgpr_32 = V_MOV_B32_e32 %4, implicit $exec
+    ; CHECK-NEXT: $exec = S_MOV_B64 -1
+    ; CHECK-NEXT: dead %3:vgpr_32 = V_ADD_U32_e32 %tmp, %other, implicit $exec
+    ; CHECK-NEXT: dead %orig:vgpr_32 = V_MOV_B32_e32 %tmp, implicit $exec
+    ; CHECK-NEXT: S_ENDPGM 0
+
+    %tmp:vgpr_32 = V_MOV_B32_e32 %orig, implicit $exec
+    $exec = S_MOV_B64 -1
+    %1:vgpr_32 = V_ADD_U32_e32 %tmp, %other, implicit $exec
+    %orig:vgpr_32 = V_MOV_B32_e32 %tmp, implicit $exec
+    S_ENDPGM 0
+...
+
+---
+name:            test_tied_operand
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    ; CHECK-LABEL: name: test_tied_operand
+    ; CHECK: %orig:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    ; CHECK-NEXT: %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+    ; CHECK-NEXT: %3:vgpr_32 = V_MOV_B32_e32 %orig, implicit $exec
+    ; CHECK-NEXT: %orig:vgpr_32 = V_MAC_F32_e32 %3, %other, %orig, implicit $exec, implicit $mode
+    ; CHECK-NEXT: dead %tmp:vgpr_32 = V_MOV_B32_e32 %orig, implicit $exec
+    ; CHECK-NEXT: S_ENDPGM 0
+
+    %orig:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+
+    %tmp:vgpr_32 = V_MOV_B32_e32 %orig, implicit $exec
+    %orig:vgpr_32 = V_MAC_F32_e32 %tmp, %other, %orig, implicit $exec, implicit $mode
+    %tmp:vgpr_32 = V_MOV_B32_e32 %orig, implicit $exec
+    S_ENDPGM 0
+...
+
+---
+name:            test_tmp_modified_early
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    ; CHECK-LABEL: name: test_tmp_modified_early
+    ; CHECK: %4:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    ; CHECK-NEXT: %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+    ; CHECK-NEXT: dead %5:vgpr_32 = V_MOV_B32_e32 %4, implicit $exec
+    ; CHECK-NEXT: %tmp:vgpr_32 = V_MOV_B32_e32 99, implicit $exec
+    ; CHECK-NEXT: dead %3:vgpr_32 = V_ADD_U32_e32 %tmp, %other, implicit $exec
+    ; CHECK-NEXT: dead %orig:vgpr_32 = V_MOV_B32_e32 %tmp, implicit $exec
+    ; CHECK-NEXT: S_ENDPGM 0
+
+    %orig:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+
+    %tmp:vgpr_32 = V_MOV_B32_e32 %orig, implicit $exec
+    %tmp:vgpr_32 = V_MOV_B32_e32 99, implicit $exec
+    %0:vgpr_32 = V_ADD_U32_e32 %tmp, %other, implicit $exec
+    %orig:vgpr_32 = V_MOV_B32_e32 %tmp, implicit $exec
+    S_ENDPGM 0
+...
+
+---
+name:            test_exec_changed_before_restore
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    ; CHECK-LABEL: name: test_exec_changed_before_restore
+    ; CHECK: %4:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    ; CHECK-NEXT: %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+    ; CHECK-NEXT: %tmp:vgpr_32 = V_MOV_B32_e32 %4, implicit $exec
+    ; CHECK-NEXT: dead %3:vgpr_32 = V_ADD_U32_e32 %tmp, %other, implicit $exec
+    ; CHECK-NEXT: $exec = S_MOV_B64 -1
+    ; CHECK-NEXT: dead %orig:vgpr_32 = V_MOV_B32_e32 %tmp, implicit $exec
+    ; CHECK-NEXT: S_ENDPGM 0
+
+    %orig:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+    %other:vgpr_32 = V_MOV_B32_e32 42, implicit $exec
+    %tmp:vgpr_32 = V_MOV_B32_e32 %orig, implicit $exec
+    %orig:vgpr_32 = V_ADD_U32_e32 %tmp, %other, implicit $exec
+    $exec = S_MOV_B64 -1
+    %orig:vgpr_32 = V_MOV_B32_e32 %tmp, implicit $exec
+    S_ENDPGM 0
+...



More information about the llvm-commits mailing list