[llvm] [AMDGPU] merge 16bit mov pairs in post-RA peephole (PR #208625)

Domenic Nutile via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 13 10:56:30 PDT 2026


================
@@ -763,6 +766,292 @@ MachineInstrBuilder SIPreEmitPeephole::createUnpackedMI(MachineInstr &I,
   return NewMI;
 }
 
+// Helper: extract the src operand and whether it is from the hi16 half.
+// Post-RA, both V_MOV_B16_t16_e32 and V_MOV_B16_t16_e64 use VGPR_16 physical
+// registers whose encoding already encodes hi/lo (IS_HI16 bit).
+// Returns false if the source is not a physical VGPR_16 or immediate zero.
+//
+// Operand layouts (post-RA, physical registers):
+//   V_MOV_B16_t16_e32: dst(0), src0(1)
+//   V_MOV_B16_t16_e64: dst(0), src0_mods(1), src0(2), op_sel(3)
+static bool getMovB16Info(const MachineInstr &MI, const SIRegisterInfo *TRI,
+                          MCRegister &SrcReg32, bool &SrcIsHi, bool &SrcIsImm,
+                          int64_t &ImmVal) {
+  SrcIsImm = false;
+  SrcIsHi = false;
+  SrcReg32 = MCRegister();
+
+  unsigned Opc = MI.getOpcode();
+  const MachineOperand *SrcOp = nullptr;
+
+  if (Opc == AMDGPU::V_MOV_B16_t16_e64)
+    SrcOp = &MI.getOperand(2);
+  else if (Opc == AMDGPU::V_MOV_B16_t16_e32)
+    SrcOp = &MI.getOperand(1);
+  else
+    return false;
+
+  if (SrcOp->isImm()) {
+    SrcIsImm = true;
+    ImmVal = SrcOp->getImm();
+    return true;
+  }
+
+  if (!SrcOp->isReg() || !SrcOp->getReg().isPhysical())
+    return false;
+
+  MCRegister SrcReg = SrcOp->getReg().asMCReg();
+
+  // We require the source to be a 16-bit VGPR so we can determine hi/lo.
+  if (!AMDGPU::VGPR_16RegClass.contains(SrcReg))
+    return false;
+
+  SrcIsHi = AMDGPU::isHi16Reg(SrcReg, *TRI);
+  SrcReg32 = TRI->get32BitRegister(SrcReg);
+  return SrcReg32.isValid();
+}
+
+// clang-format off
+// Try to merge a pair of v_mov_b16 instructions targeting the lo16 and hi16
+// halves of the same VGPR into a single 32-bit instruction.
+//
+// Patterns:
+//   v_mov_b16 v0.h, 0        v_mov_b16 v0.l, v2.l  => v_and_b32  v0,0xffff,v2
+//   v_mov_b16 v0.h, 0        v_mov_b16 v0.l, v2.h  => v_lshrrev_b32 v0,16,v2
+//   v_mov_b16 v0.l, 0        v_mov_b16 v0.h, v2.l  => v_lshlrev_b32 v0,16,v2
+//   v_mov_b16 v0.l, 0        v_mov_b16 v0.h, v2.h  => v_and_b32  v0,0xffff0000,v2
+//   v_mov_b16 v0.l, v2.l     v_mov_b16 v0.h, v3.l  => v_perm_b32 v0,v2,v3,0x05040100
+//   v_mov_b16 v0.l, v2.l     v_mov_b16 v0.h, v3.h  => v_bfi_b32  v0,0x0000ffff,v2,v3
+//   v_mov_b16 v0.l, v2.h     v_mov_b16 v0.h, v3.l  => v_alignbit_b32 v0,v3,v2,16
----------------
saxlungs wrote:

Unless I'm missing something, should both of these be achievable with the same pattern, but just with v2 and v3 swapped in operand order to decide which one is the .h and which one is the .l? Might be able to simplify the code a bit.

Not sure if one of `v_bfi_b32` or `v_alignbit_b32` is seen as better, but the code you have below for building the `v_bfi_b32` looks simpler so maybe you could use that one for both?

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


More information about the llvm-commits mailing list