[llvm] AMDGPU: VALU data fast-forwarding needs no s_delay (PR #205481)

Jay Foad via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 28 08:22:01 PDT 2026


================
@@ -346,6 +352,205 @@ class AMDGPUInsertDelayAlu {
     return (Imm & 0x780) ? nullptr : DelayAlu;
   }
 
+  static bool isFastForwardProducer(const MachineInstr &MI,
+                                    const MachineOperand &MO, Register VccReg,
+                                    Register ExecReg) {
+    if (!MO.isReg() || !MO.isDef())
+      return false;
+
+    if (!SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/false))
+      return false;
+
+    const TargetRegisterInfo *TRI =
+        MI.getMF()->getSubtarget().getRegisterInfo();
+    Register Reg = MO.getReg();
+    if (TRI->isSubRegisterEq(Reg, VccReg)) {
+      switch (MI.getOpcode()) {
+      // VOP2 carry-out producers (implicit VCC)
+      case AMDGPU::V_ADD_CO_U32_e32:
+      case AMDGPU::V_SUB_CO_U32_e32:
+      case AMDGPU::V_SUBREV_CO_U32_e32:
+      case AMDGPU::V_ADDC_U32_e32:
+      case AMDGPU::V_SUBB_U32_e32:
+      case AMDGPU::V_SUBBREV_U32_e32:
+      // VOP3 carry-out producers (explicit VCC)
+      case AMDGPU::V_ADD_CO_U32_e64:
+      case AMDGPU::V_SUB_CO_U32_e64:
+      case AMDGPU::V_SUBREV_CO_U32_e64:
+      case AMDGPU::V_ADDC_U32_e64:
+      case AMDGPU::V_SUBB_U32_e64:
+      case AMDGPU::V_SUBBREV_U32_e64:
+        return true;
+      default:
+        // V_CMP* produces condition masks (excluding V_CMPX)
+        if (MI.isCompare() && !AMDGPU::isVCMPX(MI.getOpcode())) {
+          // VOPC: implicit VCC def
+          return true;
+        }
+      }
+    } else if (TRI->isSubRegisterEq(Reg, ExecReg)) {
+      // V_CMPX writes EXEC (implicit)
+      if (AMDGPU::isVCMPX(MI.getOpcode()))
+        return true;
+    } else if (AMDGPU::isSGPR(Reg, TRI)) {
+      switch (MI.getOpcode()) {
+      // VOP3 carry-out producers (explicit SGPR)
+      case AMDGPU::V_ADD_CO_U32_e64:
+      case AMDGPU::V_SUB_CO_U32_e64:
+      case AMDGPU::V_SUBREV_CO_U32_e64:
+      case AMDGPU::V_ADDC_U32_e64:
+      case AMDGPU::V_SUBB_U32_e64:
+      case AMDGPU::V_SUBBREV_U32_e64:
+      case AMDGPU::V_DIV_SCALE_F32_e64:
+      case AMDGPU::V_DIV_SCALE_F64_e64:
+        return true;
+      default:
+        // V_CMP* produces condition masks (excluding V_CMPX)
+        if (MI.isCompare() && !AMDGPU::isVCMPX(MI.getOpcode())) {
+          // VOP3: explicit SGPR def (operand 0)
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  static unsigned int getVOPDComponentOpCode(const MachineInstr &MI,
+                                             unsigned OpNo,
+                                             const MachineOperand &MO) {
+    Register Reg = MO.getReg();
+    auto MIOpCode = MI.getOpcode();
+    // Get the component instruction descriptors for VOPD.
+    auto [OpX, OpY] = AMDGPU::getVOPDComponents(MIOpCode);
+    const MCInstrInfo *MCII = MI.getMF()->getTarget().getMCInstrInfo();
+
+    if (MO.isImplicit()) {
+      const TargetRegisterInfo *TRI =
+          MI.getMF()->getSubtarget().getRegisterInfo();
+      for (unsigned CompOp : {OpX, OpY}) {
+        const MCInstrDesc &CompDesc = MCII->get(CompOp);
+        bool UsesReg = any_of(CompDesc.implicit_uses(), [&](MCPhysReg R) {
+          return TRI->regsOverlap(R, Reg);
+        });
+        if (UsesReg) {
+          MIOpCode = CompOp;
+          break;
+        }
+      }
+    } else {
+      // Explicit source: identify which component/source THIS operand is by
+      // matching its operand index (OpNo). The same register can appear in
+      // multiple slots, so matching by register value is not reliable.
+      const auto &InstInfo = AMDGPU::getVOPDInstInfo(MIOpCode, MCII);
+
+      // Map a parsed source index (0/1/2) to the matching named operand for a
+      // standalone component opcode.
+      static constexpr AMDGPU::OpName SrcNames[] = {
+          AMDGPU::OpName::src0, AMDGPU::OpName::src1, AMDGPU::OpName::src2};
+
+      bool Found = false;
+      for (auto CompIdx : AMDGPU::VOPD::COMPONENTS) {
+        const auto &CInfo = InstInfo[CompIdx];
+        unsigned CompSrcOperandsNum = CInfo.getCompParsedSrcOperandsNum();
+        for (unsigned CompSrcIdx = 0; CompSrcIdx < CompSrcOperandsNum;
+             ++CompSrcIdx) {
+          if (CInfo.getIndexOfSrcInParsedOperands(CompSrcIdx) != OpNo)
+            continue;
+
+          // Re-target analysis to this component's standalone opcode.
+          MIOpCode = (CompIdx == AMDGPU::VOPD::X) ? OpX : OpY;
+
+          // Translate the parsed src index into MIOpCode's operand-index
+          assert(CompSrcIdx < std::size(SrcNames) &&
+                 "unexpected VOPD src index");
+          int NamedIdx =
+              AMDGPU::getNamedOperandIdx(MIOpCode, SrcNames[CompSrcIdx]);
+          if (NamedIdx >= 0)
+            OpNo = static_cast<unsigned>(NamedIdx);
+          Found = true;
+          break;
+        }
+        if (Found)
+          break;
+      }
+    }
+    return MIOpCode;
+  }
+
+  static bool isFastForwardConsumer(const MachineInstr &MI,
+                                    const MachineOperand &MO, Register VccReg,
+                                    Register ExecReg, unsigned OpNo) {
+    if (!MO.isReg() || !MO.isUse())
----------------
jayfoad wrote:

This is checked in the caller. Either remove this or change it to an assert.

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


More information about the llvm-commits mailing list