[llvm] fd7b0ee - [AMDGPU][MC][GFX11] Add VOPD VGPR bank access validation

Dmitry Preobrazhensky via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 7 05:53:24 PDT 2022


Author: Dmitry Preobrazhensky
Date: 2022-10-07T15:52:59+03:00
New Revision: fd7b0eeaf6512d03262397ed2c4f2d1e51bd8d80

URL: https://github.com/llvm/llvm-project/commit/fd7b0eeaf6512d03262397ed2c4f2d1e51bd8d80
DIFF: https://github.com/llvm/llvm-project/commit/fd7b0eeaf6512d03262397ed2c4f2d1e51bd8d80.diff

LOG: [AMDGPU][MC][GFX11] Add VOPD VGPR bank access validation

Differential Revision: https://reviews.llvm.org/D134960

Added: 
    

Modified: 
    llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
    llvm/lib/Target/AMDGPU/SIInstrInfo.td
    llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp
    llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.h
    llvm/test/MC/AMDGPU/gfx11_asm_vopd_errs.s
    llvm/test/MC/AMDGPU/gfx11_asm_vopd_features.s

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
index 3741053d60336..db2e331ddd373 100644
--- a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
+++ b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
@@ -1664,6 +1664,8 @@ class AMDGPUAsmParser : public MCTargetAsmParser {
   bool validateSMEMOffset(const MCInst &Inst, const OperandVector &Operands);
   bool validateSOPLiteral(const MCInst &Inst) const;
   bool validateConstantBusLimitations(const MCInst &Inst, const OperandVector &Operands);
+  bool validateVOPDRegBankConstraints(const MCInst &Inst,
+                                      const OperandVector &Operands);
   bool validateIntClampSupported(const MCInst &Inst);
   bool validateMIMGAtomicDMask(const MCInst &Inst);
   bool validateMIMGGatherDMask(const MCInst &Inst);
@@ -3575,6 +3577,44 @@ bool AMDGPUAsmParser::validateConstantBusLimitations(
   return false;
 }
 
+bool AMDGPUAsmParser::validateVOPDRegBankConstraints(
+    const MCInst &Inst, const OperandVector &Operands) {
+
+  const unsigned Opcode = Inst.getOpcode();
+  if (!isVOPD(Opcode))
+    return true;
+
+  const MCRegisterInfo *TRI = getContext().getRegisterInfo();
+
+  auto getVRegIdx = [&](unsigned, unsigned OperandIdx) {
+    const MCOperand &Opr = Inst.getOperand(OperandIdx);
+    return (Opr.isReg() && !isSGPR(mc2PseudoReg(Opr.getReg()), TRI))
+               ? Opr.getReg()
+               : MCRegister::NoRegister;
+  };
+
+  auto InstInfo = getVOPDInstInfo(Opcode, &MII);
+  auto InvalidOperandInfo = InstInfo.getInvalidOperandIndex(getVRegIdx);
+  if (!InvalidOperandInfo)
+    return true;
+
+  auto OprIdx = *InvalidOperandInfo;
+  auto ParsedIdx = std::max(InstInfo[VOPD::X].getParsedOperandIndex(OprIdx),
+                            InstInfo[VOPD::Y].getParsedOperandIndex(OprIdx));
+  assert(ParsedIdx > 0 && ParsedIdx < Operands.size());
+
+  auto Loc = ((AMDGPUOperand &)*Operands[ParsedIdx]).getStartLoc();
+  if (OprIdx == VOPD::Component::DST) {
+    Error(Loc, "one dst register must be even and the other odd");
+  } else {
+    auto SrcIdx = OprIdx - VOPD::Component::DST_NUM;
+    Error(Loc, Twine("src") + Twine(SrcIdx) +
+                   " operands must use 
diff erent VGPR banks");
+  }
+
+  return false;
+}
+
 bool AMDGPUAsmParser::validateIntClampSupported(const MCInst &Inst) {
 
   const unsigned Opc = Inst.getOpcode();
@@ -4626,6 +4666,9 @@ bool AMDGPUAsmParser::validateInstruction(const MCInst &Inst,
   if (!validateConstantBusLimitations(Inst, Operands)) {
     return false;
   }
+  if (!validateVOPDRegBankConstraints(Inst, Operands)) {
+    return false;
+  }
   if (!validateIntClampSupported(Inst)) {
     Error(getImmLoc(AMDGPUOperand::ImmTyClampSI, Operands),
       "integer clamping is not supported on this GPU");
@@ -8479,14 +8522,6 @@ OperandMatchResultTy AMDGPUAsmParser::parseVOPD(OperandVector &Operands) {
 }
 
 // Create VOPD MCInst operands using parsed assembler operands.
-// Parsed VOPD operands are ordered as follows:
-//   OpXMnemo dstX src0X [vsrc1X|imm vsrc1X|vsrc1X imm] '::'
-//   OpYMnemo dstY src0Y [vsrc1Y|imm vsrc1Y|vsrc1Y imm]
-// If both OpX and OpY have an imm, the first imm has a 
diff erent name:
-//   OpXMnemo dstX src0X [vsrc1X|immDeferred vsrc1X|vsrc1X immDeferred] '::'
-//   OpYMnemo dstY src0Y [vsrc1Y|imm vsrc1Y|vsrc1Y imm]
-// MCInst operands have the following order:
-//   dstX, dstY, src0X [, other OpX operands], src0Y [, other OpY operands]
 void AMDGPUAsmParser::cvtVOPD(MCInst &Inst, const OperandVector &Operands) {
   auto addOp = [&](uint16_t i) { // NOLINT:function pointer
     AMDGPUOperand &Op = ((AMDGPUOperand &)*Operands[i]);
@@ -8498,71 +8533,23 @@ void AMDGPUAsmParser::cvtVOPD(MCInst &Inst, const OperandVector &Operands) {
       Op.addImmOperands(Inst, 1);
       return;
     }
-    // Handle tokens like 'offen' which are sometimes hard-coded into the
-    // asm string.  There are no MCInst operands for these.
-    if (Op.isToken()) {
-      return;
-    }
     llvm_unreachable("Unhandled operand type in cvtVOPD");
   };
 
-  // Indices into MCInst.Operands
-  const auto FmamkOpXImmMCIndex = 3; // dstX, dstY, src0X, imm, ...
-  const auto FmaakOpXImmMCIndex = 4; // dstX, dstY, src0X, src1X, imm, ...
-  const auto MinOpYImmMCIndex = 4;   // dstX, dstY, src0X, src0Y, imm, ...
+  auto InstInfo = getVOPDInstInfo(Inst.getOpcode(), &MII);
 
-  unsigned Opc = Inst.getOpcode();
-  bool HasVsrc1X =
-      AMDGPU::getNamedOperandIdx(Opc, AMDGPU::OpName::vsrc1X) != -1;
-  bool HasImmX =
-      AMDGPU::getNamedOperandIdx(Opc, AMDGPU::OpName::immDeferred) != -1 ||
-      (HasVsrc1X && (AMDGPU::getNamedOperandIdx(Opc, AMDGPU::OpName::imm) ==
-                         FmamkOpXImmMCIndex ||
-                     AMDGPU::getNamedOperandIdx(Opc, AMDGPU::OpName::imm) ==
-                         FmaakOpXImmMCIndex));
-
-  bool HasVsrc1Y =
-      AMDGPU::getNamedOperandIdx(Opc, AMDGPU::OpName::vsrc1Y) != -1;
-  bool HasImmY =
-      AMDGPU::getNamedOperandIdx(Opc, AMDGPU::OpName::immDeferred) != -1 ||
-      AMDGPU::getNamedOperandIdx(Opc, AMDGPU::OpName::imm) >=
-          MinOpYImmMCIndex + HasVsrc1X;
-
-  // Indices of parsed operands relative to dst
-  const auto DstIdx = 0;
-  const auto Src0Idx = 1;
-  const auto Vsrc1OrImmIdx = 2;
-
-  const auto OpXOperandsSize = 2 + HasImmX + HasVsrc1X;
-  const auto BridgeTokensSize = 2; // Special VOPD tokens ('::' and OpYMnemo)
-
-  // Offsets into parsed operands
-  const auto OpXFirstOperandOffset = 1;
-  const auto OpYFirstOperandOffset =
-      OpXFirstOperandOffset + OpXOperandsSize + BridgeTokensSize;
-
-  // Order of addOp calls determines MC operand order
-  addOp(OpXFirstOperandOffset + DstIdx); // vdstX
-  addOp(OpYFirstOperandOffset + DstIdx); // vdstY
-
-  addOp(OpXFirstOperandOffset + Src0Idx); // src0X
-  if (HasImmX) {
-    // immX then vsrc1X for fmamk, vsrc1X then immX for fmaak
-    addOp(OpXFirstOperandOffset + Vsrc1OrImmIdx);
-    addOp(OpXFirstOperandOffset + Vsrc1OrImmIdx + 1);
-  } else {
-    if (HasVsrc1X) // all except v_mov
-      addOp(OpXFirstOperandOffset + Vsrc1OrImmIdx); // vsrc1X
+  // MCInst operands are ordered as follows:
+  //   dstX, dstY, src0X [, other OpX operands], src0Y [, other OpY operands]
+
+  for (auto CompIdx : VOPD::COMPONENTS) {
+    addOp(InstInfo[CompIdx].getParsedDstIndex());
   }
 
-  addOp(OpYFirstOperandOffset + Src0Idx); // src0Y
-  if (HasImmY) {
-    // immY then vsrc1Y for fmamk, vsrc1Y then immY for fmaak
-    addOp(OpYFirstOperandOffset + Vsrc1OrImmIdx);
-    addOp(OpYFirstOperandOffset + Vsrc1OrImmIdx + 1);
-  } else {
-    if (HasVsrc1Y) // all except v_mov
-      addOp(OpYFirstOperandOffset + Vsrc1OrImmIdx); // vsrc1Y
+  for (auto CompIdx : VOPD::COMPONENTS) {
+    auto SrcOperandsNum = InstInfo[CompIdx].getSrcOperandsNum();
+    for (unsigned SrcIdx = 0; SrcIdx < SrcOperandsNum; ++SrcIdx) {
+      addOp(InstInfo[CompIdx].getParsedSrcIndex(SrcIdx));
+    }
   }
 }
 

diff  --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.td b/llvm/lib/Target/AMDGPU/SIInstrInfo.td
index cf1aa5b25f587..6a0ac73a962db 100644
--- a/llvm/lib/Target/AMDGPU/SIInstrInfo.td
+++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.td
@@ -3040,6 +3040,11 @@ def VOPDComponentTable : GenericTable {
   let PrimaryKeyName = "getVOPDComponentHelper";
 }
 
+def getVOPDBaseFromComponent : SearchIndex {
+  let Table = VOPDComponentTable;
+  let Key = ["VOPDOp"];
+}
+
 def VOPDPairs : GenericTable {
   let FilterClass = "VOPD_Base";
   let CppTypeName = "VOPDInfo";

diff  --git a/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp b/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp
index 054937512d29e..020c75f80dd3c 100644
--- a/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp
@@ -460,6 +460,108 @@ int getVOPDFull(unsigned OpX, unsigned OpY) {
   return Info ? Info->Opcode : -1;
 }
 
+std::pair<unsigned, unsigned> getVOPDComponents(unsigned VOPDOpcode) {
+  const VOPDInfo *Info = getVOPDOpcodeHelper(VOPDOpcode);
+  assert(Info);
+  auto OpX = getVOPDBaseFromComponent(Info->OpX);
+  auto OpY = getVOPDBaseFromComponent(Info->OpY);
+  assert(OpX && OpY);
+  return {OpX->BaseVOP, OpY->BaseVOP};
+}
+
+namespace VOPD {
+
+ComponentProps::ComponentProps(const MCInstrDesc &OpDesc) {
+  assert(OpDesc.getNumDefs() == Component::DST_NUM);
+
+  assert(OpDesc.getOperandConstraint(Component::SRC0, MCOI::TIED_TO) == -1);
+  assert(OpDesc.getOperandConstraint(Component::SRC1, MCOI::TIED_TO) == -1);
+  auto TiedIdx = OpDesc.getOperandConstraint(Component::SRC2, MCOI::TIED_TO);
+  assert(TiedIdx == -1 || TiedIdx == Component::DST);
+  HasSrc2Acc = TiedIdx != -1;
+
+  SrcOperandsNum = OpDesc.getNumOperands() - OpDesc.getNumDefs() - HasSrc2Acc;
+  assert(SrcOperandsNum <= Component::MAX_SRC_NUM);
+
+  auto OperandsNum = OpDesc.getNumOperands() - HasSrc2Acc;
+  for (unsigned OprIdx = Component::SRC1; OprIdx < OperandsNum; ++OprIdx) {
+    if (OpDesc.OpInfo[OprIdx].OperandType == AMDGPU::OPERAND_KIMM32) {
+      MandatoryLiteralIdx = OprIdx;
+      break;
+    }
+  }
+}
+
+unsigned ComponentInfo::getParsedOperandIndex(unsigned OprIdx) const {
+  assert(OprIdx < Component::MAX_OPR_NUM);
+
+  if (OprIdx == Component::DST)
+    return getParsedDstIndex();
+
+  auto SrcIdx = OprIdx - Component::DST_NUM;
+  if (SrcIdx < getSrcOperandsNum())
+    return getParsedSrcIndex(SrcIdx);
+
+  // The specified operand does not exist.
+  return 0;
+}
+
+Optional<unsigned> InstInfo::getInvalidOperandIndex(
+    std::function<unsigned(unsigned, unsigned)> GetRegIdx) const {
+
+  auto OpXRegs = getRegIndices(ComponentIndex::X, GetRegIdx);
+  auto OpYRegs = getRegIndices(ComponentIndex::Y, GetRegIdx);
+
+  for (unsigned OprIdx = 0; OprIdx < Component::MAX_OPR_NUM; ++OprIdx) {
+    unsigned BanksNum = BANKS_NUM[OprIdx];
+    if (OpXRegs[OprIdx] && OpYRegs[OprIdx] &&
+        (OpXRegs[OprIdx] % BanksNum == OpYRegs[OprIdx] % BanksNum))
+      return OprIdx;
+  }
+
+  return {};
+}
+
+InstInfo::RegIndices InstInfo::getRegIndices(
+    unsigned ComponentIdx,
+    std::function<unsigned(unsigned, unsigned)> GetRegIdx) const {
+  assert(ComponentIdx < COMPONENTS_NUM);
+
+  auto Comp = CompInfo[ComponentIdx];
+
+  unsigned DstReg = GetRegIdx(ComponentIdx, Comp.getDstIndex());
+  unsigned Src0Reg = GetRegIdx(ComponentIdx, Comp.getSrcIndex(0));
+
+  unsigned Src1Reg = 0;
+  if (Comp.hasRegularSrcOperand(1))
+    Src1Reg = GetRegIdx(ComponentIdx, Comp.getSrcIndex(1));
+
+  unsigned Src2Reg = 0;
+  if (Comp.hasRegularSrcOperand(2))
+    Src2Reg = GetRegIdx(ComponentIdx, Comp.getSrcIndex(2));
+  else if (Comp.hasSrc2Acc())
+    Src2Reg = DstReg;
+
+  return {DstReg, Src0Reg, Src1Reg, Src2Reg};
+}
+
+} // namespace VOPD
+
+VOPD::InstInfo getVOPDInstInfo(const MCInstrDesc &OpX, const MCInstrDesc &OpY) {
+  return VOPD::InstInfo(OpX, OpY);
+}
+
+VOPD::InstInfo getVOPDInstInfo(unsigned VOPDOpcode,
+                               const MCInstrInfo *InstrInfo) {
+  auto [OpX, OpY] = getVOPDComponents(VOPDOpcode);
+  const auto &OpXDesc = InstrInfo->get(OpX);
+  const auto &OpYDesc = InstrInfo->get(OpY);
+  VOPD::ComponentInfo OpXInfo(OpXDesc, VOPD::ComponentKind::COMPONENT_X);
+  VOPD::ComponentInfo OpYInfo(OpYDesc, VOPD::ComponentKind::COMPONENT_Y,
+                              OpXInfo.getSrcOperandsNum());
+  return VOPD::InstInfo(OpXInfo, OpYInfo);
+}
+
 namespace IsaInfo {
 
 AMDGPUTargetID::AMDGPUTargetID(const MCSubtargetInfo &STI)

diff  --git a/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.h b/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.h
index 11a89962676ec..bfab25815c16c 100644
--- a/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.h
+++ b/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.h
@@ -10,8 +10,12 @@
 #define LLVM_LIB_TARGET_AMDGPU_UTILS_AMDGPUBASEINFO_H
 
 #include "SIDefines.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/IR/CallingConv.h"
 #include "llvm/Support/Alignment.h"
+#include <array>
+#include <functional>
+#include <utility>
 
 struct amd_kernel_code_t;
 
@@ -22,6 +26,7 @@ class Argument;
 class Function;
 class GCNSubtarget;
 class GlobalValue;
+class MCInstrInfo;
 class MCRegisterClass;
 class MCRegisterInfo;
 class MCSubtargetInfo;
@@ -499,6 +504,180 @@ int getVOPDFull(unsigned OpX, unsigned OpY);
 LLVM_READONLY
 bool isVOPD(unsigned Opc);
 
+namespace VOPD {
+
+enum Component : unsigned {
+  DST = 0,
+  SRC0,
+  SRC1,
+  SRC2,
+
+  DST_NUM = 1,
+  MAX_SRC_NUM = 3,
+  MAX_OPR_NUM = DST_NUM + MAX_SRC_NUM
+};
+
+// Number of VGPR banks per VOPD component operand.
+constexpr unsigned BANKS_NUM[] = {2, 4, 4, 2};
+
+enum ComponentIndex : unsigned { X = 0, Y = 1 };
+constexpr unsigned COMPONENTS[] = {ComponentIndex::X, ComponentIndex::Y};
+constexpr unsigned COMPONENTS_NUM = 2;
+
+enum ComponentKind : unsigned {
+  SINGLE = 0,  // A single VOP1 or VOP2 instruction which may be used in VOPD.
+  COMPONENT_X, // A VOPD instruction, X component.
+  COMPONENT_Y, // A VOPD instruction, Y component.
+  MAX = COMPONENT_Y
+};
+
+// Location of operands in a MachineInstr/MCInst
+// and position of operands in parsed operands array.
+class ComponentLayout {
+private:
+  // Regular MachineInstr/MCInst operands are ordered as follows:
+  //   dst, src0 [, other src operands]
+  // VOPD MachineInstr/MCInst operands are ordered as follows:
+  //   dstX, dstY, src0X [, other OpX operands], src0Y [, other OpY operands]
+  // Each ComponentKind has operand indices defined below.
+  static constexpr unsigned MC_DST_IDX[] = {0, 0, 1};
+  static constexpr unsigned FIRST_MC_SRC_IDX[] = {1, 2, 2 /* + OpXSrcNum */};
+
+  // Parsed operands of regular instructions are ordered as follows:
+  //   Mnemo dst src0 [vsrc1 ...]
+  // Parsed VOPD operands are ordered as follows:
+  //   OpXMnemo dstX src0X [vsrc1X|imm vsrc1X|vsrc1X imm] '::'
+  //   OpYMnemo dstY src0Y [vsrc1Y|imm vsrc1Y|vsrc1Y imm]
+  // Each ComponentKind has operand indices defined below.
+  static constexpr unsigned PARSED_DST_IDX[] = {1, 1, 4 /* + OpXSrcNum */};
+  static constexpr unsigned FIRST_PARSED_SRC_IDX[] = {2, 2,
+                                                      5 /* + OpXSrcNum */};
+
+private:
+  ComponentKind Kind;
+  unsigned OpXSrcNum;
+
+public:
+  ComponentLayout(ComponentKind Kind_ = ComponentKind::SINGLE,
+                  unsigned OpXSrcNum_ = 0)
+      : Kind(Kind_), OpXSrcNum(OpXSrcNum_) {
+    assert(Kind <= ComponentKind::MAX);
+    assert((Kind == ComponentKind::COMPONENT_Y) == (OpXSrcNum > 0));
+  }
+
+public:
+  unsigned getDstIndex() const { return MC_DST_IDX[Kind]; }
+  unsigned getSrcIndex(unsigned SrcIdx) const {
+    assert(SrcIdx < Component::MAX_SRC_NUM);
+    return FIRST_MC_SRC_IDX[Kind] + OpXSrcNum + SrcIdx;
+  }
+
+  unsigned getParsedDstIndex() const {
+    return PARSED_DST_IDX[Kind] + OpXSrcNum;
+  }
+  unsigned getParsedSrcIndex(unsigned SrcIdx) const {
+    assert(SrcIdx < Component::MAX_SRC_NUM);
+    return FIRST_PARSED_SRC_IDX[Kind] + OpXSrcNum + SrcIdx;
+  }
+};
+
+// Properties of VOPD components.
+class ComponentProps {
+private:
+  unsigned SrcOperandsNum;
+  Optional<unsigned> MandatoryLiteralIdx;
+  bool HasSrc2Acc;
+
+public:
+  ComponentProps(const MCInstrDesc &OpDesc);
+
+  unsigned getSrcOperandsNum() const { return SrcOperandsNum; }
+  bool hasMandatoryLiteral() const { return MandatoryLiteralIdx.has_value(); }
+  unsigned getMandatoryLiteralIndex() const {
+    assert(hasMandatoryLiteral());
+    return *MandatoryLiteralIdx;
+  }
+  bool hasRegularSrcOperand(unsigned SrcIdx) const {
+    assert(SrcIdx < Component::MAX_SRC_NUM);
+    return SrcOperandsNum > SrcIdx && !hasMandatoryLiteralAt(SrcIdx);
+  }
+  bool hasSrc2Acc() const { return HasSrc2Acc; }
+
+private:
+  bool hasMandatoryLiteralAt(unsigned SrcIdx) const {
+    assert(SrcIdx < Component::MAX_SRC_NUM);
+    return hasMandatoryLiteral() &&
+           *MandatoryLiteralIdx == Component::DST_NUM + SrcIdx;
+  }
+};
+
+// Layout and properties of VOPD components.
+class ComponentInfo : public ComponentLayout, public ComponentProps {
+public:
+  ComponentInfo(const MCInstrDesc &OpDesc,
+                ComponentKind Kind = ComponentKind::SINGLE,
+                unsigned OpXSrcNum = 0)
+      : ComponentLayout(Kind, OpXSrcNum), ComponentProps(OpDesc) {}
+
+  // Map MC operand index to parsed operand index.
+  // Return 0 if the specified operand does not exist.
+  unsigned getParsedOperandIndex(unsigned OprIdx) const;
+};
+
+// Properties of VOPD instructions.
+class InstInfo {
+private:
+  const ComponentInfo CompInfo[COMPONENTS_NUM];
+
+public:
+  using RegIndices = std::array<unsigned, Component::MAX_OPR_NUM>;
+
+  InstInfo(const MCInstrDesc &OpX, const MCInstrDesc &OpY)
+      : CompInfo{OpX, OpY} {}
+
+  InstInfo(const ComponentInfo &OprInfoX, const ComponentInfo &OprInfoY)
+      : CompInfo{OprInfoX, OprInfoY} {}
+
+  const ComponentInfo &operator[](size_t ComponentIdx) const {
+    assert(ComponentIdx < COMPONENTS_NUM);
+    return CompInfo[ComponentIdx];
+  }
+
+  // Check VOPD operands constraints.
+  // GetRegIdx(Component, OperandIdx) must return a VGPR register index
+  // for the specified component and operand. The callback must return 0
+  // if the operand is not a register or not a VGPR.
+  bool hasInvalidOperand(
+      std::function<unsigned(unsigned, unsigned)> GetRegIdx) const {
+    return getInvalidOperandIndex(GetRegIdx).has_value();
+  }
+
+  // Check VOPD operands constraints.
+  // Return the index of an invalid component operand, if any.
+  Optional<unsigned> getInvalidOperandIndex(
+      std::function<unsigned(unsigned, unsigned)> GetRegIdx) const;
+
+private:
+  RegIndices
+  getRegIndices(unsigned ComponentIdx,
+                std::function<unsigned(unsigned, unsigned)> GetRegIdx) const;
+};
+
+} // namespace VOPD
+
+LLVM_READONLY
+std::pair<unsigned, unsigned> getVOPDComponents(unsigned VOPDOpcode);
+
+LLVM_READONLY
+// Get properties of 2 single VOP1/VOP2 instructions
+// used as components to create a VOPD instruction.
+VOPD::InstInfo getVOPDInstInfo(const MCInstrDesc &OpX, const MCInstrDesc &OpY);
+
+LLVM_READONLY
+// Get properties of VOPD X and Y components.
+VOPD::InstInfo
+getVOPDInstInfo(unsigned VOPDOpcode, const MCInstrInfo *InstrInfo);
+
 LLVM_READONLY
 bool isTrue16Inst(unsigned Opc);
 

diff  --git a/llvm/test/MC/AMDGPU/gfx11_asm_vopd_errs.s b/llvm/test/MC/AMDGPU/gfx11_asm_vopd_errs.s
index 88df32924493b..f1682c5636ef0 100644
--- a/llvm/test/MC/AMDGPU/gfx11_asm_vopd_errs.s
+++ b/llvm/test/MC/AMDGPU/gfx11_asm_vopd_errs.s
@@ -74,7 +74,7 @@ v_dual_fmamk_f32    v122, s0, 0xdeadbeef, v161   ::  v_dual_fmamk_f32  v123, s0,
 // GFX11-NEXT:{{^}}                                                                                 ^
 
 //===----------------------------------------------------------------------===//
-// A VOPD instruction cannot use more than 2 scalar operands
+// A VOPD instruction cannot use more than 2 scalar operands.
 //===----------------------------------------------------------------------===//
 
 // 2 
diff erent SGPRs + LITERAL
@@ -141,3 +141,129 @@ v_dual_add_f32      v255, vcc_lo, v2             ::  v_dual_cndmask_b32   v6, s1
 // GFX11: error: invalid operand (violates constant bus restrictions)
 // GFX11-NEXT:{{^}}v_dual_add_f32      v255, vcc_lo, v2             ::  v_dual_cndmask_b32   v6, s1, v3
 // GFX11-NEXT:{{^}}                                                                              ^
+
+//===----------------------------------------------------------------------===//
+// One dst register must be even and the other odd.
+//===----------------------------------------------------------------------===//
+
+v_dual_add_f32      v0, v4, v2                   ::  v_dual_add_f32       v2, v1, v3
+// GFX11: error: one dst register must be even and the other odd
+// GFX11-NEXT:{{^}}v_dual_add_f32      v0, v4, v2                   ::  v_dual_add_f32       v2, v1, v3
+// GFX11-NEXT:{{^}}                                                                          ^
+
+v_dual_mov_b32      v1, v4                       ::  v_dual_add_f32       v5, v1, v3
+// GFX11: error: one dst register must be even and the other odd
+// GFX11-NEXT:{{^}}v_dual_mov_b32      v1, v4                       ::  v_dual_add_f32       v5, v1, v3
+// GFX11-NEXT:{{^}}                                                                          ^
+
+v_dual_cndmask_b32  v2, v4, v5                   ::  v_dual_add_f32       v8, v5, v6
+// GFX11: error: one dst register must be even and the other odd
+// GFX11-NEXT:{{^}}v_dual_cndmask_b32  v2, v4, v5                   ::  v_dual_add_f32       v8, v5, v6
+// GFX11-NEXT:{{^}}                                                                          ^
+
+v_dual_fmac_f32     v3, v4, v5                   ::  v_dual_add_f32       v9, v5, v6
+// GFX11: error: one dst register must be even and the other odd
+// GFX11-NEXT:{{^}}v_dual_fmac_f32     v3, v4, v5                   ::  v_dual_add_f32       v9, v5, v6
+// GFX11-NEXT:{{^}}                                                                          ^
+
+v_dual_fmaak_f32    v4, v4, v5, 0xaf123456       ::  v_dual_add_f32       v0, v5, v6
+// GFX11: error: one dst register must be even and the other odd
+// GFX11-NEXT:{{^}}v_dual_fmaak_f32    v4, v4, v5, 0xaf123456       ::  v_dual_add_f32       v0, v5, v6
+// GFX11-NEXT:{{^}}                                                                          ^
+
+v_dual_fmamk_f32    v5, v4, 0xaf123456, v6       ::  v_dual_add_f32       v1, v5, v6
+// GFX11: error: one dst register must be even and the other odd
+// GFX11-NEXT:{{^}}v_dual_fmamk_f32    v5, v4, 0xaf123456, v6       ::  v_dual_add_f32       v1, v5, v6
+// GFX11-NEXT:{{^}}                                                                          ^
+
+//===----------------------------------------------------------------------===//
+// Src0 operands must use 
diff erent VGPR banks.
+//===----------------------------------------------------------------------===//
+
+v_dual_add_f32      v1, v1, v5                   ::  v_dual_mov_b32       v2, v1
+// GFX11: error: src0 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_add_f32      v1, v1, v5                   ::  v_dual_mov_b32       v2, v1
+// GFX11-NEXT:{{^}}                                                                              ^
+
+v_dual_mov_b32      v1, v2                       ::  v_dual_add_f32       v2, v6, v6
+// GFX11: error: src0 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_mov_b32      v1, v2                       ::  v_dual_add_f32       v2, v6, v6
+// GFX11-NEXT:{{^}}                                                                              ^
+
+v_dual_cndmask_b32  v1, v3, v5                   ::  v_dual_add_f32       v2, v11, v6
+// GFX11: error: src0 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_cndmask_b32  v1, v3, v5                   ::  v_dual_add_f32       v2, v11, v6
+// GFX11-NEXT:{{^}}                                                                              ^
+
+v_dual_fmac_f32     v1, v4, v5                   ::  v_dual_add_f32       v2, v44, v6
+// GFX11: error: src0 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_fmac_f32     v1, v4, v5                   ::  v_dual_add_f32       v2, v44, v6
+// GFX11-NEXT:{{^}}                                                                              ^
+
+v_dual_fmaak_f32    v1, v5, v5, 0xaf123456       ::  v_dual_add_f32       v2, v25, v6
+// GFX11: error: src0 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_fmaak_f32    v1, v5, v5, 0xaf123456       ::  v_dual_add_f32       v2, v25, v6
+// GFX11-NEXT:{{^}}                                                                              ^
+
+v_dual_fmamk_f32    v1, v6, 0xaf123456, v6       ::  v_dual_add_f32       v2, v2, v6
+// GFX11: error: src0 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_fmamk_f32    v1, v6, 0xaf123456, v6       ::  v_dual_add_f32       v2, v2, v6
+// GFX11-NEXT:{{^}}                                                                              ^
+
+//===----------------------------------------------------------------------===//
+// Src1 operands must use 
diff erent VGPR banks.
+//===----------------------------------------------------------------------===//
+
+v_dual_add_f32      v1, v4, v0                   ::  v_dual_add_f32       v2, v5, v4
+// GFX11: error: src1 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_add_f32      v1, v4, v0                   ::  v_dual_add_f32       v2, v5, v4
+// GFX11-NEXT:{{^}}                                                                                  ^
+
+v_dual_cndmask_b32  v1, v4, v1                   ::  v_dual_add_f32       v2, v5, v9
+// GFX11: error: src1 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_cndmask_b32  v1, v4, v1                   ::  v_dual_add_f32       v2, v5, v9
+// GFX11-NEXT:{{^}}                                                                                  ^
+
+v_dual_fmac_f32     v1, v4, v2                   ::  v_dual_add_f32       v2, v5, v14
+// GFX11: error: src1 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_fmac_f32     v1, v4, v2                   ::  v_dual_add_f32       v2, v5, v14
+// GFX11-NEXT:{{^}}                                                                                  ^
+
+v_dual_fmaak_f32    v1, v4, v3, 0xaf123456       ::  v_dual_add_f32       v2, v5, v23
+// GFX11: error: src1 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_fmaak_f32    v1, v4, v3, 0xaf123456       ::  v_dual_add_f32       v2, v5, v23
+// GFX11-NEXT:{{^}}                                                                                  ^
+
+v_dual_add_f32      v2, v4, v4                   ::  v_dual_cndmask_b32   v1, v5, v0
+// GFX11: error: src1 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_add_f32      v2, v4, v4                   ::  v_dual_cndmask_b32   v1, v5, v0
+// GFX11-NEXT:{{^}}                                                                                  ^
+
+v_dual_add_f32      v2, v4, v5                   ::  v_dual_fmac_f32      v1, v5, v1
+// GFX11: error: src1 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_add_f32      v2, v4, v5                   ::  v_dual_fmac_f32      v1, v5, v1
+// GFX11-NEXT:{{^}}                                                                                  ^
+
+v_dual_fmaak_f32    v1, v4, v3, 0xaf123456       ::  v_dual_fmaak_f32     v2, v5, v23, 0xaf123456
+// GFX11: error: src1 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_fmaak_f32    v1, v4, v3, 0xaf123456       ::  v_dual_fmaak_f32     v2, v5, v23, 0xaf123456
+// GFX11-NEXT:{{^}}                                                                                  ^
+
+//===----------------------------------------------------------------------===//
+// Src2 operands must use 
diff erent VGPR banks.
+//===----------------------------------------------------------------------===//
+
+v_dual_fmamk_f32    v6, v1, 0xaf123456, v3       :: v_dual_fmamk_f32      v5, v2, 0xaf123456, v5
+// GFX11: error: src2 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_fmamk_f32    v6, v1, 0xaf123456, v3       :: v_dual_fmamk_f32      v5, v2, 0xaf123456, v5
+// GFX11-NEXT:{{^}}                                                                                              ^
+
+v_dual_fmac_f32     v7, v1, v2                   :: v_dual_fmamk_f32      v6, v2, 0xaf123456, v3
+// GFX11: error: src2 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_fmac_f32     v7, v1, v2                   :: v_dual_fmamk_f32      v6, v2, 0xaf123456, v3
+// GFX11-NEXT:{{^}}                                                                                              ^
+
+v_dual_fmamk_f32    v6, v1, 0xaf123456, v3       :: v_dual_fmac_f32       v5, v2, v3
+// GFX11: error: src2 operands must use 
diff erent VGPR banks
+// GFX11-NEXT:{{^}}v_dual_fmamk_f32    v6, v1, 0xaf123456, v3       :: v_dual_fmac_f32       v5, v2, v3
+// GFX11-NEXT:{{^}}                                        ^

diff  --git a/llvm/test/MC/AMDGPU/gfx11_asm_vopd_features.s b/llvm/test/MC/AMDGPU/gfx11_asm_vopd_features.s
index 8d01ca7a5968e..fd8569bc0a9b3 100644
--- a/llvm/test/MC/AMDGPU/gfx11_asm_vopd_features.s
+++ b/llvm/test/MC/AMDGPU/gfx11_asm_vopd_features.s
@@ -102,3 +102,79 @@ v_dual_cndmask_b32  v255, s1, v2                 ::  v_dual_cndmask_b32   v6, s1
 
 v_dual_add_f32      v255, vcc_lo, v2             ::  v_dual_cndmask_b32   v6, v1, v3
 // GFX11: encoding: [0x6a,0x04,0x12,0xc9,0x01,0x07,0x06,0xff]
+
+//===----------------------------------------------------------------------===//
+// One dst register must be even and the other odd.
+//===----------------------------------------------------------------------===//
+
+v_dual_mul_f32      v0, v10, v20                 ::  v_dual_mul_f32      v1, v11, v21
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x0b,0x2b,0x00,0x00]
+
+v_dual_mul_f32      v1, v10, v20                 ::  v_dual_mul_f32      v0, v11, v21
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x0b,0x2b,0x00,0x01]
+
+//===----------------------------------------------------------------------===//
+// srcX0 and srcY0 must use 
diff erent VGPR banks.
+//===----------------------------------------------------------------------===//
+
+v_dual_mul_f32      v0, v10, v20                 ::  v_dual_mul_f32      v1, v11, v21
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x0b,0x2b,0x00,0x00]
+
+v_dual_mul_f32      v0, v10, v20                 ::  v_dual_mul_f32      v1, v12, v21
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x0c,0x2b,0x00,0x00]
+
+v_dual_mul_f32      v0, v10, v20                 ::  v_dual_mul_f32      v1, v13, v21
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x0d,0x2b,0x00,0x00]
+
+v_dual_mul_f32      v0, v10, v20                 ::  v_dual_mul_f32      v1, v15, v21
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x0f,0x2b,0x00,0x00]
+
+v_dual_mul_f32      v0, v10, v20                 ::  v_dual_mul_f32      v1, v16, v21
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x10,0x2b,0x00,0x00]
+
+v_dual_mul_f32      v0, v10, v20                 ::  v_dual_mul_f32      v1, v17, v21
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x11,0x2b,0x00,0x00]
+
+//===----------------------------------------------------------------------===//
+// srcX1 and srcY1 must use 
diff erent VGPR banks.
+//===----------------------------------------------------------------------===//
+
+v_dual_mul_f32      v0, v10, v20                 ::  v_dual_mul_f32      v1, v11, v21
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x0b,0x2b,0x00,0x00]
+
+v_dual_mul_f32      v0, v10, v20                 ::  v_dual_mul_f32      v1, v11, v22
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x0b,0x2d,0x00,0x00]
+
+v_dual_mul_f32      v0, v10, v20                 ::  v_dual_mul_f32      v1, v11, v23
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x0b,0x2f,0x00,0x00]
+
+v_dual_mul_f32      v0, v10, v20                 ::  v_dual_mul_f32      v1, v11, v25
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x0b,0x33,0x00,0x00]
+
+v_dual_mul_f32      v0, v10, v20                 ::  v_dual_mul_f32      v1, v11, v26
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x0b,0x35,0x00,0x00]
+
+v_dual_mul_f32      v0, v10, v20                 ::  v_dual_mul_f32      v1, v11, v27
+// GFX11: encoding: [0x0a,0x29,0xc6,0xc8,0x0b,0x37,0x00,0x00]
+
+//===----------------------------------------------------------------------===//
+// srcX2 and srcY2 must use 
diff erent VGPR banks.
+//===----------------------------------------------------------------------===//
+
+v_dual_fmamk_f32    v6, v1, 0xaf123456, v0       :: v_dual_fmamk_f32      v5, v2, 0xaf123456, v1
+// GFX11: encoding: [0x01,0x01,0x84,0xc8,0x02,0x03,0x04,0x06,0x56,0x34,0x12,0xaf]
+
+v_dual_fmamk_f32    v6, v1, 0xaf123456, v1       :: v_dual_fmamk_f32      v5, v2, 0xaf123456, v0
+// GFX11: encoding: [0x01,0x03,0x84,0xc8,0x02,0x01,0x04,0x06,0x56,0x34,0x12,0xaf]
+
+v_dual_fmac_f32     v6, v1, v2                   :: v_dual_fmamk_f32      v7, v2, 0xaf123456, v7
+// GFX11: encoding: [0x01,0x05,0x04,0xc8,0x02,0x0f,0x06,0x06,0x56,0x34,0x12,0xaf]
+
+v_dual_fmac_f32     v7, v1, v2                   :: v_dual_fmamk_f32      v6, v2, 0xaf123456, v6
+// GFX11: encoding: [0x01,0x05,0x04,0xc8,0x02,0x0d,0x06,0x07,0x56,0x34,0x12,0xaf]
+
+v_dual_fmamk_f32    v5, v1, 0xaf123456, v5       :: v_dual_fmac_f32       v6, v2, v3
+// GFX11: encoding: [0x01,0x0b,0x80,0xc8,0x02,0x07,0x06,0x05,0x56,0x34,0x12,0xaf]
+
+v_dual_fmamk_f32    v6, v1, 0xaf123456, v6       :: v_dual_fmac_f32       v5, v2, v3
+// GFX11: encoding: [0x01,0x0d,0x80,0xc8,0x02,0x07,0x04,0x06,0x56,0x34,0x12,0xaf]


        


More information about the llvm-commits mailing list