[llvm] [AMDGPU] Merge 32-bit VGPR copies into 64-bit copies (PR #193270)

Christudasan Devadasan via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 22 22:17:34 PDT 2026


================
@@ -0,0 +1,196 @@
+//===-- SIMergeVGPRCopies.cpp - Merge adjacent VGPR copies ----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// Merge adjacent 32-bit copy-to-VGPR pairs into a single 64-bit copy
+/// (V_PK_MOV_B32 or V_MOV_B64).
+///
+/// Both sources and destinations must be consecutive and even-aligned, but the
+/// source and destination pairs can be in either order. The sources can be
+/// SGPRs if the target supports V_MOV_B64, but not if the merged copy needs to
+/// use V_PK_MOV_B32 (e.g. if the source pairs are inversed relative to the
+/// destinations).
+///
+/// This runs after register allocation, when all registers are physical and
+/// before ExpandPostRAPseudos lowers each copy individually.
+///
+//===----------------------------------------------------------------------===//
+
+#include "SIMergeVGPRCopies.h"
+#include "AMDGPU.h"
+#include "GCNSubtarget.h"
+#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "si-merge-vgpr-copies"
+
+namespace {
+
+class SIMergeVGPRCopiesLegacy : public MachineFunctionPass {
+public:
+  static char ID;
+
+  SIMergeVGPRCopiesLegacy() : MachineFunctionPass(ID) {}
+
+  void getAnalysisUsage(AnalysisUsage &AU) const override {
+    AU.setPreservesAll();
+    MachineFunctionPass::getAnalysisUsage(AU);
+  }
+
+  bool runOnMachineFunction(MachineFunction &MF) override;
+
+  StringRef getPassName() const override { return "SI Merge VGPR copies"; }
+};
+
+class SIMergeVGPRCopies {
+public:
+  bool run(MachineFunction &MF);
+};
+
+} // End anonymous namespace.
+
+INITIALIZE_PASS(SIMergeVGPRCopiesLegacy, DEBUG_TYPE, "SI Merge VGPR copies",
+                false, false)
+
+char SIMergeVGPRCopiesLegacy::ID = 0;
+
+char &llvm::SIMergeVGPRCopiesID = SIMergeVGPRCopiesLegacy::ID;
+
+PreservedAnalyses SIMergeVGPRCopiesPass::run(MachineFunction &MF,
+                                             MachineFunctionAnalysisManager &) {
+  SIMergeVGPRCopies().run(MF);
+  return PreservedAnalyses::all();
+}
+
+bool SIMergeVGPRCopiesLegacy::runOnMachineFunction(MachineFunction &MF) {
+  return SIMergeVGPRCopies().run(MF);
+}
+
+static bool getBaseIfConsecutive(const Register &Reg1, const Register &Reg2,
+                                 const SIRegisterInfo *TRI, Register &Base) {
+  long Dist =
+      static_cast<long>(TRI->getHWRegIndex(Reg2)) - TRI->getHWRegIndex(Reg1);
+  if (std::abs(Dist) != 1)
+    return false;
+  Base = (Dist == 1) ? Reg1 : Reg2;
+  return true;
+}
+
+bool SIMergeVGPRCopies::run(MachineFunction &MF) {
+  const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
+  // Only merge when the target has a 64-bit move instruction. Skip targets
+  // with VOPD: GCNCreateVOPD runs later and can pair 32-bit moves with
+  // unrelated instructions, which is potentially better than v_mov_b64.
+  if ((!ST.hasPkMovB32() && !ST.hasMovB64()) || AMDGPU::hasVOPD(ST))
+    return false;
+
+  const SIRegisterInfo *TRI = ST.getRegisterInfo();
+  const SIInstrInfo *TII = ST.getInstrInfo();
+  const TargetRegisterClass *VGPR64RC = TRI->getVGPR64Class();
+  const TargetRegisterClass *SGPR64RC = &AMDGPU::SReg_64RegClass;
+  bool Changed = false;
+
+  for (MachineBasicBlock &MBB : MF) {
+    for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); I != E;) {
+      MachineInstr &MI = *I;
+
+      I = next_nodbg(I, E);
----------------
cdevadas wrote:

What @arsenm meant was to use something similar to:
`for (MachineInstr &MI : make_early_inc_range(MBB)) {
 ...
}`
This creates a wrapper range that allows the MachineInstr to be safely modified during iteration. It would achieve the same thing you're doing with the early increment.
If you want to skip the dbg instructions, try using something like the following (or a similar approach)
`for (MachineInstr &MI : make_early_inc_range(instructionsWithoutDebug(MBB.instr_begin(), MBB.instr_end()))`

https://github.com/llvm/llvm-project/pull/193270


More information about the llvm-commits mailing list