[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