[llvm] [NextUseAnalysis] Add updateInstrIds() method. (PR #209954)

Konstantina Mitropoulou via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 15 21:00:50 PDT 2026


https://github.com/kmitropoulou created https://github.com/llvm/llvm-project/pull/209954

During spilling, we emit spill/restore instructions. Hence, the ID of the instructions change. For example, let's assume that we calculate the distance from a restore to its use. The restore will get a new ID, but the use will not get a new ID because it is already in the map.

The updateInstrIds() update the instruction IDs of the basic block where the new instruction is added.

It should be called every time we emit a spill or restore instruction.
This method is similar to insertMachineInstrInMaps() from slot indexes.


>From 05fd7e746698fd7e76d079de69b30a3ccbe27203 Mon Sep 17 00:00:00 2001
From: Konstantina Mitropoulou <KonstantinaMitropoulou at amd.com>
Date: Wed, 15 Jul 2026 15:01:29 -0700
Subject: [PATCH 1/2] Add a new test

---
 .../AMDGPU/AMDGPUNextUseAnalysisTest.cpp      | 227 ++++++++++++++++++
 llvm/unittests/Target/AMDGPU/CMakeLists.txt   |   1 +
 2 files changed, 228 insertions(+)
 create mode 100644 llvm/unittests/Target/AMDGPU/AMDGPUNextUseAnalysisTest.cpp

diff --git a/llvm/unittests/Target/AMDGPU/AMDGPUNextUseAnalysisTest.cpp b/llvm/unittests/Target/AMDGPU/AMDGPUNextUseAnalysisTest.cpp
new file mode 100644
index 0000000000000..cba74d0eb5d37
--- /dev/null
+++ b/llvm/unittests/Target/AMDGPU/AMDGPUNextUseAnalysisTest.cpp
@@ -0,0 +1,227 @@
+//===- AMDGPUNextUseAnalysisTest.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Unit tests for AMDGPUNextUseAnalysis.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AMDGPUNextUseAnalysis.h"
+#include "AMDGPUUnitTests.h"
+#include "llvm/CodeGen/MIRParser/MIRParser.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+class AMDGPUNextUseAnalysisTest : public AMDGPUCodeGenTestBase {
+public:
+  void SetUp() override { setUpImpl("amdgcn-amd-amdhsa", "gfx1310", ""); }
+};
+
+// Test that distance calculation works correctly when multiple instructions
+// are inserted at different points in the basic block.
+TEST_F(AMDGPUNextUseAnalysisTest, DistanceAfterInstructionInsertion) {
+  StringRef MIR = R"(
+name:            DistanceAfterInstructionInsertion
+tracksRegLiveness: true
+machineFunctionInfo:
+  isEntryFunction: true
+body:             |
+  bb.0:
+    liveins: $vgpr0
+    %0:vgpr_32 = COPY $vgpr0
+    %1:vgpr_32 = V_MOV_B32_e32 1, implicit $exec
+    %2:vgpr_32 = V_MOV_B32_e32 2, implicit $exec
+    %3:vgpr_32 = V_ADD_U32_e32 %0, %1, implicit $exec
+    %4:vgpr_32 = V_ADD_U32_e32 %3, %2, implicit $exec
+    S_NOP 0, implicit %4
+    S_ENDPGM 0
+...
+)";
+  EXPECT_TRUE(parseMIR(MIR));
+  MachineFunction &MF = getMF("DistanceAfterInstructionInsertion");
+  AMDGPUNextUseAnalysis &NUA = MFAM.getResult<AMDGPUNextUseAnalysisPass>(MF);
+
+  MachineBasicBlock &MBB = *MF.begin();
+  const auto &TII = *MF.getSubtarget().getInstrInfo();
+
+  auto It = MBB.begin();
+  std::advance(It, 3);
+  MachineInstr &Add1 = *It++;
+  MachineInstr &Add2 = *It++;
+  MachineInstr &Nop1 = *It;
+
+  Register Add1DefReg = Add1.getOperand(0).getReg();
+
+  // Add a new multiplication before the first ADD.
+  Register NewReg1 =
+      MF.getRegInfo().createVirtualRegister(&AMDGPU::VGPR_32RegClass);
+  MachineInstr *NewMul1 = BuildMI(MBB, Add1.getIterator(), DebugLoc(),
+                                  TII.get(AMDGPU::V_MUL_U32_U24_e64), NewReg1)
+                              .addImm(0)
+                              .addImm(1)
+                              .addImm(2)
+                              .getInstr();
+
+  // Add the destination register of the first multiplication as an input
+  // operand of the fist ADD.
+  Add1.getOperand(1).setReg(NewReg1);
+
+  // Add a second multiplication before the first ADD
+  Register NewReg2 =
+      MF.getRegInfo().createVirtualRegister(&AMDGPU::VGPR_32RegClass);
+  MachineInstr *NewMul2 = BuildMI(MBB, Add1.getIterator(), DebugLoc(),
+                                  TII.get(AMDGPU::V_MUL_U32_U24_e64), NewReg2)
+                              .addImm(0)
+                              .addImm(3)
+                              .addImm(4)
+                              .getInstr();
+
+  // Add the destination register of the second multiplication as an input
+  // operand of the second ADD.
+  Add2.getOperand(1).setReg(NewReg2);
+
+  Register Add2DefReg = Add2.getOperand(0).getReg();
+
+  // Add a third multiplication before the second ADD
+  Register NewReg3 =
+      MF.getRegInfo().createVirtualRegister(&AMDGPU::VGPR_32RegClass);
+  MachineInstr *NewMul3 = BuildMI(MBB, Add2.getIterator(), DebugLoc(),
+                                  TII.get(AMDGPU::V_MUL_U32_U24_e64), NewReg3)
+                              .addImm(0)
+                              .addImm(5)
+                              .addImm(6)
+                              .getInstr();
+
+  BuildMI(MBB, MBB.back().getIterator(), DebugLoc(), TII.get(AMDGPU::S_NOP))
+      .addImm(0)
+      .addReg(Add1DefReg, RegState::Implicit)
+      .addReg(Add2DefReg, RegState::Implicit);
+
+  // Add the destination register of the third multiplication as an input
+  // operand of the first NOP.
+  for (MachineOperand &MO : Nop1.operands()) {
+    if (MO.isReg() && MO.isUse())
+      MO.setReg(NewReg3);
+  }
+
+  // Instruction order after the insertion of the new instructions.
+  // %0:vgpr_32 = COPY $vgpr0
+  // %1:vgpr_32 = V_MOV_B32_e32 1, implicit $exec
+  // %2:vgpr_32 = V_MOV_B32_e32 2, implicit $exec
+  // %5:vgpr_32 = V_MUL_U32_U24_e64 0, 1, 2, implicit $exec
+  // %6:vgpr_32 = V_MUL_U32_U24_e64 0, 3, 4, implicit $exec
+  // %3:vgpr_32 = V_ADD_U32_e32 %5:vgpr_32, %1:vgpr_32, implicit $exec
+  // %7:vgpr_32 = V_MUL_U32_U24_e64 0, 5, 6, implicit $exec
+  // %4:vgpr_32 = V_ADD_U32_e32 %6:vgpr_32, %2:vgpr_32, implicit $exec
+  // S_NOP 0, implicit %7:vgpr_32
+  // S_NOP 0, implicit %3:vgpr_32, implicit %4:vgpr_32
+  // S_ENDPGM 0
+
+  MachineRegisterInfo &MRI = MF.getRegInfo();
+  SmallVector<const MachineOperand *> Add1Uses;
+  for (MachineInstr &UseMI : MRI.use_nodbg_instructions(Add1DefReg))
+    for (const MachineOperand &MO : UseMI.uses())
+      if (MO.isReg() && MO.getReg() == Add1DefReg)
+        Add1Uses.push_back(&MO);
+
+  SmallVector<const MachineOperand *> Add2Uses;
+  for (MachineInstr &UseMI : MRI.use_nodbg_instructions(Add2DefReg))
+    for (const MachineOperand &MO : UseMI.uses())
+      if (MO.isReg() && MO.getReg() == Add2DefReg)
+        Add2Uses.push_back(&MO);
+
+  SmallVector<const MachineOperand *> Mul1Uses;
+  for (MachineInstr &UseMI : MRI.use_nodbg_instructions(NewReg1))
+    for (const MachineOperand &MO : UseMI.uses())
+      if (MO.isReg() && MO.getReg() == NewReg1)
+        Mul1Uses.push_back(&MO);
+
+  SmallVector<const MachineOperand *> Mul2Uses;
+  for (MachineInstr &UseMI : MRI.use_nodbg_instructions(NewReg2))
+    for (const MachineOperand &MO : UseMI.uses())
+      if (MO.isReg() && MO.getReg() == NewReg2)
+        Mul2Uses.push_back(&MO);
+
+  SmallVector<const MachineOperand *> Mul3Uses;
+  for (MachineInstr &UseMI : MRI.use_nodbg_instructions(NewReg3))
+    for (const MachineOperand &MO : UseMI.uses())
+      if (MO.isReg() && MO.getReg() == NewReg3)
+        Mul3Uses.push_back(&MO);
+
+  NextUseDistance DistNewMul1 =
+      NUA.getShortestDistance(NewReg1, *NewMul1, Mul1Uses);
+  EXPECT_EQ(DistNewMul1.getRawValue(), 0);
+
+  NextUseDistance DistNewMul2 =
+      NUA.getShortestDistance(NewReg2, *NewMul2, Mul2Uses);
+  EXPECT_EQ(DistNewMul2.getRawValue(), 3);
+
+  NextUseDistance DistAdd1 =
+      NUA.getShortestDistance(Add1DefReg, Add1, Add1Uses);
+  EXPECT_EQ(DistAdd1.getRawValue(), 4);
+
+  NextUseDistance DistNewMul3 =
+      NUA.getShortestDistance(NewReg3, *NewMul3, Mul3Uses);
+  EXPECT_EQ(DistNewMul3.getRawValue(), 2);
+
+  NextUseDistance DistAdd2 =
+      NUA.getShortestDistance(Add2DefReg, Add2, Add2Uses);
+  EXPECT_EQ(DistAdd2.getRawValue(), 2);
+
+  // Add a fourth multiplication after the second ADD.
+  Register NewReg4 =
+      MF.getRegInfo().createVirtualRegister(&AMDGPU::VGPR_32RegClass);
+  MachineInstr *NewMul4 = BuildMI(MBB, Nop1.getIterator(), DebugLoc(),
+                                  TII.get(AMDGPU::V_MUL_U32_U24_e64), NewReg4)
+                              .addImm(0)
+                              .addImm(7)
+                              .addImm(8)
+                              .getInstr();
+
+  [[maybe_unused]] MachineInstr *Nop3 =
+      BuildMI(MBB, MBB.back().getIterator(), DebugLoc(), TII.get(AMDGPU::S_NOP))
+          .addImm(0)
+          .addReg(NewReg4, RegState::Implicit)
+          .getInstr();
+
+  // Instructions after emitting more instructions.
+  // %0:vgpr_32 = COPY $vgpr0
+  // %1:vgpr_32 = V_MOV_B32_e32 1, implicit $exec
+  // %2:vgpr_32 = V_MOV_B32_e32 2, implicit $exec
+  // %5:vgpr_32 = V_MUL_U32_U24_e64 0, 1, 2, implicit $exec
+  // %6:vgpr_32 = V_MUL_U32_U24_e64 0, 3, 4, implicit $exec
+  // %3:vgpr_32 = V_ADD_U32_e32 %5:vgpr_32, %1:vgpr_32, implicit $exec
+  // %7:vgpr_32 = V_MUL_U32_U24_e64 0, 5, 6, implicit $exec
+  // %4:vgpr_32 = V_ADD_U32_e32 %6:vgpr_32, %2:vgpr_32, implicit $exec
+  // %8:vgpr_32 = V_MUL_U32_U24_e64 0, 7, 8, implicit $exec
+  // S_NOP 0, implicit %7:vgpr_32
+  // S_NOP 0, implicit %3:vgpr_32, implicit %4:vgpr_32
+  // S_NOP 0, implicit %8:vgpr_32
+  // S_ENDPGM 0
+
+  SmallVector<const MachineOperand *> Mul4Uses;
+  for (MachineInstr &UseMI : MRI.use_nodbg_instructions(NewReg4))
+    for (const MachineOperand &MO : UseMI.uses())
+      if (MO.isReg() && MO.getReg() == NewReg4)
+        Mul4Uses.push_back(&MO);
+
+  NextUseDistance DistAdd1b =
+      NUA.getShortestDistance(Add1DefReg, Add1, Add1Uses);
+  EXPECT_EQ(DistAdd1b.getRawValue(), 4);
+
+  NextUseDistance DistAdd2b =
+      NUA.getShortestDistance(Add2DefReg, Add2, Add2Uses);
+  EXPECT_EQ(DistAdd2b.getRawValue(), 2);
+
+  NextUseDistance DistNewMul4 =
+      NUA.getShortestDistance(NewReg4, *NewMul4, Mul4Uses);
+  EXPECT_EQ(DistNewMul4.getRawValue(), 3);
+}
diff --git a/llvm/unittests/Target/AMDGPU/CMakeLists.txt b/llvm/unittests/Target/AMDGPU/CMakeLists.txt
index 77335a57520bc..af35c7ccb1131 100644
--- a/llvm/unittests/Target/AMDGPU/CMakeLists.txt
+++ b/llvm/unittests/Target/AMDGPU/CMakeLists.txt
@@ -24,6 +24,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_llvm_target_unittest(AMDGPUTests
   AMDGPUMCExprTest.cpp
+  AMDGPUNextUseAnalysisTest.cpp
   AMDGPUUnitTests.cpp
   CSETest.cpp
   DwarfRegMappings.cpp

>From a07dbbc1a98ad387144eb32065d43d79c594069f Mon Sep 17 00:00:00 2001
From: Konstantina Mitropoulou <KonstantinaMitropoulou at amd.com>
Date: Wed, 15 Jul 2026 20:41:30 -0700
Subject: [PATCH 2/2] [NextUseAnalysis] Add updateInstrIds() method.

During spilling, we emit spill/restore instructions. Hence, the ID of the
instructions change. For example, let's assume that we calculate the distance
from a restore to its use. The restore will get a new ID, but the use will not
get a new ID because it is already in the map.

The updateInstrIds() update the instruction IDs of the basic block where the new
instruction is added.

It should be called every time we emit a spill or restore instruction.
This method is similar to insertMachineInstrInMaps() from slot indexes.
---
 .../Target/AMDGPU/AMDGPUNextUseAnalysis.cpp   |  9 ++++++
 .../lib/Target/AMDGPU/AMDGPUNextUseAnalysis.h |  2 ++
 .../AMDGPU/AMDGPUNextUseAnalysisTest.cpp      | 28 +++++++++++++------
 3 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUNextUseAnalysis.cpp b/llvm/lib/Target/AMDGPU/AMDGPUNextUseAnalysis.cpp
index cad1513f280fb..0f3abadc9446d 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUNextUseAnalysis.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUNextUseAnalysis.cpp
@@ -2106,6 +2106,11 @@ class llvm::AMDGPUNextUseAnalysisImpl {
     return getShortestDistance(LiveReg, LaneBitmask::getAll(), FromMI, Uses,
                                nullptr, nullptr, nullptr);
   }
+
+  void updateInstrIds(const MachineInstr *MI) const {
+    auto &MutableInstrToId = const_cast<InstrToIdMap &>(InstrToId);
+    calcInstrIds(MI->getParent(), MutableInstrToId);
+  }
 };
 
 AMDGPUNextUseAnalysisImpl::AMDGPUNextUseAnalysisImpl(
@@ -2265,6 +2270,10 @@ void AMDGPUNextUseAnalysis::getReachableUses(
   return Impl->getReachableUses(LiveReg, LaneMask, MI, Uses);
 }
 
+void AMDGPUNextUseAnalysis::updateInstrIds(const MachineInstr *MI) const {
+  return Impl->updateInstrIds(MI);
+}
+
 //==============================================================================
 // AMDGPUNextUseAnalysisLegacyPass
 //==============================================================================
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUNextUseAnalysis.h b/llvm/lib/Target/AMDGPU/AMDGPUNextUseAnalysis.h
index 1c6e5b1d8cbce..da0b15f23d668 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUNextUseAnalysis.h
+++ b/llvm/lib/Target/AMDGPU/AMDGPUNextUseAnalysis.h
@@ -310,6 +310,8 @@ class AMDGPUNextUseAnalysis {
                            UseDistancePair *FurthestSubreg = nullptr,
                            DenseMap<const MachineOperand *, UseDistancePair>
                                *RelevantUses = nullptr) const;
+
+  void updateInstrIds(const MachineInstr *MI) const;
 };
 
 //==============================================================================
diff --git a/llvm/unittests/Target/AMDGPU/AMDGPUNextUseAnalysisTest.cpp b/llvm/unittests/Target/AMDGPU/AMDGPUNextUseAnalysisTest.cpp
index cba74d0eb5d37..e268647e0a1fc 100644
--- a/llvm/unittests/Target/AMDGPU/AMDGPUNextUseAnalysisTest.cpp
+++ b/llvm/unittests/Target/AMDGPU/AMDGPUNextUseAnalysisTest.cpp
@@ -70,6 +70,8 @@ body:             |
                               .addImm(2)
                               .getInstr();
 
+  NUA.updateInstrIds(NewMul1);
+
   // Add the destination register of the first multiplication as an input
   // operand of the fist ADD.
   Add1.getOperand(1).setReg(NewReg1);
@@ -84,6 +86,8 @@ body:             |
                               .addImm(4)
                               .getInstr();
 
+  NUA.updateInstrIds(NewMul2);
+
   // Add the destination register of the second multiplication as an input
   // operand of the second ADD.
   Add2.getOperand(1).setReg(NewReg2);
@@ -100,10 +104,15 @@ body:             |
                               .addImm(6)
                               .getInstr();
 
-  BuildMI(MBB, MBB.back().getIterator(), DebugLoc(), TII.get(AMDGPU::S_NOP))
-      .addImm(0)
-      .addReg(Add1DefReg, RegState::Implicit)
-      .addReg(Add2DefReg, RegState::Implicit);
+  NUA.updateInstrIds(NewMul3);
+
+  MachineInstr *Nop2 =
+      BuildMI(MBB, MBB.back().getIterator(), DebugLoc(), TII.get(AMDGPU::S_NOP))
+          .addImm(0)
+          .addReg(Add1DefReg, RegState::Implicit)
+          .addReg(Add2DefReg, RegState::Implicit)
+          .getInstr();
+  NUA.updateInstrIds(Nop2);
 
   // Add the destination register of the third multiplication as an input
   // operand of the first NOP.
@@ -158,7 +167,7 @@ body:             |
 
   NextUseDistance DistNewMul1 =
       NUA.getShortestDistance(NewReg1, *NewMul1, Mul1Uses);
-  EXPECT_EQ(DistNewMul1.getRawValue(), 0);
+  EXPECT_EQ(DistNewMul1.getRawValue(), 2);
 
   NextUseDistance DistNewMul2 =
       NUA.getShortestDistance(NewReg2, *NewMul2, Mul2Uses);
@@ -186,11 +195,14 @@ body:             |
                               .addImm(8)
                               .getInstr();
 
-  [[maybe_unused]] MachineInstr *Nop3 =
+  NUA.updateInstrIds(NewMul4);
+
+  MachineInstr *Nop3 =
       BuildMI(MBB, MBB.back().getIterator(), DebugLoc(), TII.get(AMDGPU::S_NOP))
           .addImm(0)
           .addReg(NewReg4, RegState::Implicit)
           .getInstr();
+  NUA.updateInstrIds(Nop3);
 
   // Instructions after emitting more instructions.
   // %0:vgpr_32 = COPY $vgpr0
@@ -215,11 +227,11 @@ body:             |
 
   NextUseDistance DistAdd1b =
       NUA.getShortestDistance(Add1DefReg, Add1, Add1Uses);
-  EXPECT_EQ(DistAdd1b.getRawValue(), 4);
+  EXPECT_EQ(DistAdd1b.getRawValue(), 5);
 
   NextUseDistance DistAdd2b =
       NUA.getShortestDistance(Add2DefReg, Add2, Add2Uses);
-  EXPECT_EQ(DistAdd2b.getRawValue(), 2);
+  EXPECT_EQ(DistAdd2b.getRawValue(), 3);
 
   NextUseDistance DistNewMul4 =
       NUA.getShortestDistance(NewReg4, *NewMul4, Mul4Uses);



More information about the llvm-commits mailing list