[llvm] CodeGen: Add RegisterClass by HwMode (PR #158269)

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 12 04:12:36 PDT 2025


https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/158269

This is a generalization of the LookupPtrRegClass mechanism.
AMDGPU has several use cases for swapping the register class of
instruction operands based on the subtarget, but none of them
really fit into the box of being pointer-like.

The current system requires manual management of an arbitrary integer
ID. For the AMDGPU use case, this would end up being around 40 new
entries to manage.

This just introduces the base infrastructure. I have ports of all
the target specific usage of PointerLikeRegClass ready.

>From ba508ebb119852efd9f4e9571f1c218f0cfe48a3 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Mon, 25 Aug 2025 07:29:46 +0900
Subject: [PATCH] CodeGen: Add RegisterClass by HwMode

This is a generalization of the LookupPtrRegClass mechanism.
AMDGPU has several use cases for swapping the register class of
instruction operands based on the subtarget, but none of them
really fit into the box of being pointer-like.

The current system requires manual management of an arbitrary integer
ID. For the AMDGPU use case, this would end up being around 40 new
entries to manage.

This just introduces the base infrastructure. I have ports of all
the target specific usage of PointerLikeRegClass ready.
---
 llvm/include/llvm/CodeGen/TargetInstrInfo.h   |  24 +-
 llvm/include/llvm/MC/MCInstrDesc.h            |  20 +-
 llvm/include/llvm/MC/MCInstrInfo.h            |  28 +-
 llvm/include/llvm/MC/MCSubtargetInfo.h        |   1 +
 llvm/include/llvm/Target/Target.td            |  27 +-
 llvm/lib/CodeGen/TargetInstrInfo.cpp          |   7 +-
 llvm/lib/Target/AMDGPU/BUFInstructions.td     |   7 +-
 llvm/lib/Target/AMDGPU/SIInstrFormats.td      |   2 +-
 llvm/lib/Target/AMDGPU/SIInstrInfo.td         |   4 +-
 .../lib/Target/AVR/AsmParser/AVRAsmParser.cpp |   4 +-
 .../SystemZ/AsmParser/SystemZAsmParser.cpp    |   2 +-
 llvm/test/TableGen/RegClassByHwMode.td        | 429 ++++++++++++++++++
 llvm/utils/TableGen/AsmMatcherEmitter.cpp     | 156 ++++++-
 llvm/utils/TableGen/AsmWriterEmitter.cpp      |   2 +-
 .../TableGen/Common/CodeGenDAGPatterns.cpp    |  33 +-
 .../TableGen/Common/CodeGenInstAlias.cpp      |  14 +-
 .../TableGen/Common/CodeGenInstruction.cpp    |   2 +-
 llvm/utils/TableGen/Common/CodeGenTarget.h    |   8 +
 llvm/utils/TableGen/Common/InfoByHwMode.cpp   |  14 +
 llvm/utils/TableGen/Common/InfoByHwMode.h     |  12 +
 llvm/utils/TableGen/DAGISelMatcherGen.cpp     |   2 +-
 llvm/utils/TableGen/DecoderEmitter.cpp        | 111 ++++-
 llvm/utils/TableGen/GlobalISelEmitter.cpp     |  79 +++-
 llvm/utils/TableGen/InstrInfoEmitter.cpp      | 102 ++++-
 llvm/utils/TableGen/SubtargetEmitter.cpp      |   4 +-
 25 files changed, 1005 insertions(+), 89 deletions(-)
 create mode 100644 llvm/test/TableGen/RegClassByHwMode.td

diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
index 6a624a7052cdd..0212c8bde7d55 100644
--- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
@@ -113,9 +113,17 @@ struct ExtAddrMode {
 ///
 class LLVM_ABI TargetInstrInfo : public MCInstrInfo {
 protected:
+  /// Subtarget specific sub-array of MCInstrInfo's RegClassByHwModeTables.
+  /// This should be indexed by MCOperandInfo's RegClass field for
+  /// LookupRegClassByHwMode operands.
+  const int16_t *const RegClassByHwMode;
+
+public:
   TargetInstrInfo(unsigned CFSetupOpcode = ~0u, unsigned CFDestroyOpcode = ~0u,
-                  unsigned CatchRetOpcode = ~0u, unsigned ReturnOpcode = ~0u)
-      : CallFrameSetupOpcode(CFSetupOpcode),
+                  unsigned CatchRetOpcode = ~0u, unsigned ReturnOpcode = ~0u,
+                  const int16_t *const RegClassByHwModeTable = nullptr)
+      : RegClassByHwMode(RegClassByHwModeTable),
+        CallFrameSetupOpcode(CFSetupOpcode),
         CallFrameDestroyOpcode(CFDestroyOpcode), CatchRetOpcode(CatchRetOpcode),
         ReturnOpcode(ReturnOpcode) {}
 
@@ -133,6 +141,18 @@ class LLVM_ABI TargetInstrInfo : public MCInstrInfo {
            Opc <= TargetOpcode::GENERIC_ATOMICRMW_OP_END;
   }
 
+  /// Return the subtarget appropripate RegClassID for \p OpInfo
+  ///
+  /// Note this shadows a version of getOpRegClassID in MCInstrInfo which takes
+  /// an additional argument for the subtarget's HwMode, since TargetInstrInfo
+  /// is owned by a subtarget in CodeGen but MCInstrInfo is a TargetMachine
+  /// constant.
+  int16_t getOpRegClassID(const MCOperandInfo &OpInfo) const {
+    if (OpInfo.isLookupRegClassByHwMode())
+      return RegClassByHwMode[OpInfo.RegClass];
+    return OpInfo.RegClass;
+  }
+
   /// Given a machine instruction descriptor, returns the register
   /// class constraint for OpNum, or NULL.
   virtual const TargetRegisterClass *
diff --git a/llvm/include/llvm/MC/MCInstrDesc.h b/llvm/include/llvm/MC/MCInstrDesc.h
index 8c70925d4780e..292c40a8cb128 100644
--- a/llvm/include/llvm/MC/MCInstrDesc.h
+++ b/llvm/include/llvm/MC/MCInstrDesc.h
@@ -50,6 +50,7 @@ enum OperandConstraint {
 /// See the accessors for a description of what these are.
 enum OperandFlags {
   LookupPtrRegClass = 0,
+  LookupRegClassByHwMode,
   Predicate,
   OptionalDef,
   BranchTarget
@@ -85,10 +86,14 @@ enum OperandType {
 /// indicating the register class for register operands, etc.
 class MCOperandInfo {
 public:
-  /// This specifies the register class enumeration of the operand
-  /// if the operand is a register.  If isLookupPtrRegClass is set, then this is
-  /// an index that is passed to TargetRegisterInfo::getPointerRegClass(x) to
-  /// get a dynamic register class.
+  /// This specifies the register class enumeration of the operand if the
+  /// operand is a register. If LookupRegClassByHwMode is set, then this is an
+  /// index into a table in TargetInstrInfo or MCInstrInfo which contains the
+  /// real register class ID.
+  ///
+  /// If isLookupPtrRegClass is set, then this is / an index that is passed to
+  /// TargetRegisterInfo::getPointerRegClass(x) to / get a dynamic register
+  /// class.
   int16_t RegClass;
 
   /// These are flags from the MCOI::OperandFlags enum.
@@ -102,10 +107,17 @@ class MCOperandInfo {
 
   /// Set if this operand is a pointer value and it requires a callback
   /// to look up its register class.
+  // TODO: Deprecated in favor of isLookupRegClassByHwMode
   bool isLookupPtrRegClass() const {
     return Flags & (1 << MCOI::LookupPtrRegClass);
   }
 
+  /// Set if this operand is a value that requires the current hwmode to look up
+  /// its register class.
+  bool isLookupRegClassByHwMode() const {
+    return Flags & (1 << MCOI::LookupRegClassByHwMode);
+  }
+
   /// Set if this is one of the operands that made up of the predicate
   /// operand that controls an isPredicable() instruction.
   bool isPredicate() const { return Flags & (1 << MCOI::Predicate); }
diff --git a/llvm/include/llvm/MC/MCInstrInfo.h b/llvm/include/llvm/MC/MCInstrInfo.h
index 77ead222b9549..aaad450ef99c3 100644
--- a/llvm/include/llvm/MC/MCInstrInfo.h
+++ b/llvm/include/llvm/MC/MCInstrInfo.h
@@ -43,22 +43,48 @@ class MCInstrInfo {
   const ComplexDeprecationPredicate *ComplexDeprecationInfos;
   unsigned NumOpcodes;              // Number of entries in the desc array
 
+protected:
+  // Pointer to 2d array [NumHwModess][NumRegClassByHwModes]
+  const int16_t *RegClassByHwModeTables;
+  int16_t NumRegClassByHwModes;
+
 public:
   /// Initialize MCInstrInfo, called by TableGen auto-generated routines.
   /// *DO NOT USE*.
   void InitMCInstrInfo(const MCInstrDesc *D, const unsigned *NI, const char *ND,
                        const uint8_t *DF,
-                       const ComplexDeprecationPredicate *CDI, unsigned NO) {
+                       const ComplexDeprecationPredicate *CDI, unsigned NO,
+                       const int16_t *RCHWTables = nullptr,
+                       int16_t NumRegClassByHwMode = 0) {
     LastDesc = D + NO - 1;
     InstrNameIndices = NI;
     InstrNameData = ND;
     DeprecatedFeatures = DF;
     ComplexDeprecationInfos = CDI;
     NumOpcodes = NO;
+    RegClassByHwModeTables = RCHWTables;
+    NumRegClassByHwModes = NumRegClassByHwMode;
   }
 
   unsigned getNumOpcodes() const { return NumOpcodes; }
 
+  const int16_t *getRegClassByHwModeTable(unsigned ModeId) const {
+    assert(RegClassByHwModeTables);
+    assert(NumRegClassByHwModes != 0);
+    return &RegClassByHwModeTables[ModeId * NumRegClassByHwModes];
+  }
+
+  /// Return the ID of the register class to use for \p OpInfo, for the active
+  /// HwMode \p HwModeId. In general TargetInstrInfo's version which is already
+  /// specialized to the subtarget should be used.
+  int16_t getOpRegClassID(const MCOperandInfo &OpInfo,
+                          unsigned HwModeId) const {
+    int16_t RegClass = OpInfo.RegClass;
+    if (OpInfo.isLookupRegClassByHwMode())
+      RegClass = getRegClassByHwModeTable(HwModeId)[RegClass];
+    return RegClass;
+  }
+
   /// Return the machine instruction descriptor that corresponds to the
   /// specified instruction opcode.
   const MCInstrDesc &get(unsigned Opcode) const {
diff --git a/llvm/include/llvm/MC/MCSubtargetInfo.h b/llvm/include/llvm/MC/MCSubtargetInfo.h
index 44b4cb8fa17bd..5204c23897d67 100644
--- a/llvm/include/llvm/MC/MCSubtargetInfo.h
+++ b/llvm/include/llvm/MC/MCSubtargetInfo.h
@@ -259,6 +259,7 @@ class LLVM_ABI MCSubtargetInfo {
     HwMode_ValueType, // Return the HwMode ID that controls the ValueType.
     HwMode_RegInfo,   // Return the HwMode ID that controls the RegSizeInfo and
                       // SubRegRange.
+    HwMode_RegClass,  // Return the HwMode ID that controls the RegisterClass.
     HwMode_EncodingInfo // Return the HwMode ID that controls the EncodingInfo.
   };
 
diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td
index 6a7ecf78b2131..13d715582be0d 100644
--- a/llvm/include/llvm/Target/Target.td
+++ b/llvm/include/llvm/Target/Target.td
@@ -258,6 +258,12 @@ class DAGOperand {
   bit hasCompleteDecoder = true;
 }
 
+/// Abstract base class common to RegisterClass and
+/// RegClassByHwMode. This permits using RegClassByHwMode in
+/// RegisterOperand contexts without creating an artificial
+/// RegisterClass.
+class RegisterClassLike : DAGOperand;
+
 // RegisterClass - Now that all of the registers are defined, and aliases
 // between registers are defined, specify which registers belong to which
 // register classes. This also defines the default allocation order of
@@ -265,7 +271,7 @@ class DAGOperand {
 //
 class RegisterClass<string namespace, list<ValueType> regTypes, int alignment,
                     dag regList, RegAltNameIndex idx = NoRegAltName>
-  : DAGOperand {
+  : DAGOperand, RegisterClassLike {
   string Namespace = namespace;
 
   // The register size/alignment information, parameterized by a HW mode.
@@ -916,15 +922,30 @@ def decoder;
 /// derived from this. TableGen treats the register class as having a symbolic
 /// type that it doesn't know, and resolves the actual regclass to use by using
 /// the TargetRegisterInfo::getPointerRegClass() hook at codegen time.
+///
+/// This is deprecated in favor of RegClassByHwMode
 class PointerLikeRegClass<int Kind> {
   int RegClassKind = Kind;
 }
 
+/// RegClassByHwMode - Operands that change the register class based
+/// on the subtarget are derived from this derived from this. TableGen
+/// treats the register class as having a symbolic kind that it
+/// doesn't know, and resolves the actual regclass to use by using the
+/// a mapping in TargetInstrInfo at codegen time. This can be used to
+/// define operands which swap the register class with the pointer
+/// type.
+class RegClassByHwMode<list<HwMode> Modes,
+                       list<RegisterClass> RegClasses> :
+  HwModeSelect<Modes, !size(RegClasses)>, RegisterClassLike {
+  list<RegisterClass> Objects = RegClasses;
+}
 
 /// ptr_rc definition - Mark this operand as being a pointer value whose
 /// register class is resolved dynamically via a callback to TargetInstrInfo.
 /// FIXME: We should probably change this to a class which contain a list of
 /// flags. But currently we have but one flag.
+// Deprecated, use RegClassByHwMode instead.
 def ptr_rc : PointerLikeRegClass<0>;
 
 /// unknown definition - Mark this operand as being of unknown type, causing
@@ -1024,10 +1045,10 @@ class Operand<ValueType ty> : DAGOperand {
   AsmOperandClass ParserMatchClass = ImmAsmOperand;
 }
 
-class RegisterOperand<RegisterClass regclass, string pm = "printOperand">
+class RegisterOperand<RegisterClassLike regclass, string pm = "printOperand">
   : DAGOperand {
   // RegClass - The register class of the operand.
-  RegisterClass RegClass = regclass;
+  RegisterClassLike RegClass = regclass;
   // PrintMethod - The target method to call to print register operands of
   // this type. The method normally will just use an alt-name index to look
   // up the name to print. Default to the generic printOperand().
diff --git a/llvm/lib/CodeGen/TargetInstrInfo.cpp b/llvm/lib/CodeGen/TargetInstrInfo.cpp
index b0009560d3fcb..5be89b49fb6ba 100644
--- a/llvm/lib/CodeGen/TargetInstrInfo.cpp
+++ b/llvm/lib/CodeGen/TargetInstrInfo.cpp
@@ -64,8 +64,11 @@ TargetInstrInfo::getRegClass(const MCInstrDesc &MCID, unsigned OpNum,
   if (OpNum >= MCID.getNumOperands())
     return nullptr;
 
-  short RegClass = MCID.operands()[OpNum].RegClass;
-  if (MCID.operands()[OpNum].isLookupPtrRegClass())
+  const MCOperandInfo &OpInfo = MCID.operands()[OpNum];
+  int16_t RegClass = getOpRegClassID(OpInfo);
+
+  // TODO: Remove isLookupPtrRegClass in favor of isLookupRegClassByHwMode
+  if (OpInfo.isLookupPtrRegClass())
     return TRI->getPointerRegClass(RegClass);
 
   // Instructions like INSERT_SUBREG do not have fixed register classes.
diff --git a/llvm/lib/Target/AMDGPU/BUFInstructions.td b/llvm/lib/Target/AMDGPU/BUFInstructions.td
index f229298ba516b..09a66d785d5cf 100644
--- a/llvm/lib/Target/AMDGPU/BUFInstructions.td
+++ b/llvm/lib/Target/AMDGPU/BUFInstructions.td
@@ -411,11 +411,16 @@ class getBUFVDataRegisterOperand<int Size, bit isTFE> {
   RegisterOperand ret = !if(isTFE, tfeVDataOp, VDataOp);
 }
 
+class getBUFVDataRegisterOperandForOp<RegisterOperand Op, bit isTFE> {
+  defvar Size = !cast<RegisterClass>(Op.RegClass).Size;
+  RegisterOperand ret = getBUFVDataRegisterOperand<Size, isTFE>.ret;
+}
+
 class getMUBUFInsDA<list<RegisterOperand> vdataList,
                     list<RegisterClass> vaddrList, bit isTFE, bit hasRestrictedSOffset> {
   RegisterOperand vdataClass = !if(!empty(vdataList), ?, !head(vdataList));
   RegisterClass vaddrClass = !if(!empty(vaddrList), ?, !head(vaddrList));
-  RegisterOperand vdata_op = getBUFVDataRegisterOperand<vdataClass.RegClass.Size, isTFE>.ret;
+  RegisterOperand vdata_op = getBUFVDataRegisterOperandForOp<vdataClass, isTFE>.ret;
 
   dag SOffset = !if(hasRestrictedSOffset, (ins SReg_32:$soffset), (ins SCSrc_b32:$soffset));
   dag NonVaddrInputs = !con((ins SReg_128_XNULL:$srsrc), SOffset, (ins Offset:$offset, CPol_0:$cpol, i1imm_0:$swz));
diff --git a/llvm/lib/Target/AMDGPU/SIInstrFormats.td b/llvm/lib/Target/AMDGPU/SIInstrFormats.td
index 50964a94d6e58..de66c472be0ca 100644
--- a/llvm/lib/Target/AMDGPU/SIInstrFormats.td
+++ b/llvm/lib/Target/AMDGPU/SIInstrFormats.td
@@ -321,7 +321,7 @@ def CPolBit {
   int SCAL = 11;
 }
 
-class VOPDstOperand <RegisterClass rc> : RegisterOperand <rc, "printVOPDst">;
+class VOPDstOperand<RegisterClassLike rc> : RegisterOperand<rc, "printVOPDst">;
 
 def VOPDstOperand_t16 : VOPDstOperand <VGPR_16> {
   let EncoderMethod = "getMachineOpValueT16";
diff --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.td b/llvm/lib/Target/AMDGPU/SIInstrInfo.td
index c8231b470abae..e3c71d211faa0 100644
--- a/llvm/lib/Target/AMDGPU/SIInstrInfo.td
+++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.td
@@ -2635,7 +2635,7 @@ class getAlign2RegOp<RegisterOperand RC> {
 }
 
 class getEquivalentAGPROperand<RegisterOperand RC> {
-  defvar Size = RC.RegClass.Size;
+  defvar Size = !cast<RegisterClass>(RC.RegClass).Size;
   RegisterOperand ret =
     !cond(!eq(Size, 32)   : RegisterOperand<AGPR_32>,
           !eq(Size, 64)   : RegisterOperand<AReg_64>,
@@ -2646,7 +2646,7 @@ class getEquivalentAGPROperand<RegisterOperand RC> {
 }
 
 class getEquivalentVGPROperand<RegisterOperand RC> {
-  defvar Size = RC.RegClass.Size;
+  defvar Size = !cast<RegisterClass>(RC.RegClass).Size;
   RegisterOperand ret =
     !cond(!eq(Size, 32)   : RegisterOperand<VGPR_32>,
           !eq(Size, 64)   : RegisterOperand<VReg_64>,
diff --git a/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp b/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp
index a8650146e988a..fc794c4968b8c 100644
--- a/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp
+++ b/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp
@@ -764,7 +764,7 @@ unsigned AVRAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp,
       RegName << "r" << RegNum;
       if (MCRegister Reg = MatchRegisterName(RegName.str())) {
         Op.makeReg(Reg);
-        if (validateOperandClass(Op, Expected) == Match_Success) {
+        if (validateOperandClass(Op, Expected, *STI) == Match_Success) {
           return Match_Success;
         }
       }
@@ -780,7 +780,7 @@ unsigned AVRAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp,
 
       if (correspondingDREG) {
         Op.makeReg(correspondingDREG);
-        return validateOperandClass(Op, Expected);
+        return validateOperandClass(Op, Expected, *STI);
       }
     }
   }
diff --git a/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp b/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp
index 597ce8eabfeb2..4efb305b2d55a 100644
--- a/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp
+++ b/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp
@@ -1339,7 +1339,7 @@ bool SystemZAsmParser::parseDirectiveInsn(SMLoc L) {
     MatchClassKind Kind = Entry->OperandKinds[I];
 
     // Verify operand.
-    unsigned Res = validateOperandClass(Operand, Kind);
+    unsigned Res = validateOperandClass(Operand, Kind, *STI);
     if (Res != Match_Success)
       return Error(Operand.getStartLoc(), "unexpected operand type");
 
diff --git a/llvm/test/TableGen/RegClassByHwMode.td b/llvm/test/TableGen/RegClassByHwMode.td
new file mode 100644
index 0000000000000..3944c7eb79a7a
--- /dev/null
+++ b/llvm/test/TableGen/RegClassByHwMode.td
@@ -0,0 +1,429 @@
+// RUN: llvm-tblgen -gen-instr-info -I %p/../../include %s -o - | FileCheck -check-prefix=INSTRINFO %s
+// RUN: llvm-tblgen -gen-asm-matcher -I %p/../../include %s -o - | FileCheck -check-prefix=ASMMATCHER %s
+// RUN: llvm-tblgen -gen-disassembler -I %p/../../include %s -o - | FileCheck -check-prefix=DISASM %s
+// RUN: llvm-tblgen -gen-dag-isel -I %p/../../include %s -o - | FileCheck -check-prefix=ISEL-SDAG %s
+// RUN: llvm-tblgen -gen-global-isel -I %p/../../include %s -o - | FileCheck -check-prefix=ISEL-GISEL %s
+
+include "llvm/Target/Target.td"
+
+
+// INSTRINFO: #if defined(GET_INSTRINFO_MC_DESC) || defined(GET_INSTRINFO_CTOR_DTOR)
+// INSTRINFO-NEXT: namespace {
+// INSTRINFO-NEXT: enum RegClassByHwModeUses : uint16_t {
+// INSTRINFO-NEXT:   MyPtrRC,
+// INSTRINFO-NEXT:   XRegs_EvenIfRequired,
+// INSTRINFO-NEXT:   YRegs_EvenIfRequired,
+// INSTRINFO-NEXT: };
+// INSTRINFO-NEXT: }
+
+// INSTRINFO: { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 },
+// INSTRINFO: { MyTarget::XRegs_EvenRegClassID, 0, MCOI::OPERAND_REGISTER, 0 },
+// INSTRINFO: { YRegs_EvenIfRequired, 0|(1<<MCOI::LookupRegClassByHwMode), MCOI::OPERAND_REGISTER, 0 },
+// INSTRINFO: { XRegs_EvenIfRequired, 0|(1<<MCOI::LookupRegClassByHwMode), MCOI::OPERAND_REGISTER, 0 },
+// INSTRINFO: { XRegs_EvenIfRequired, 0|(1<<MCOI::LookupRegClassByHwMode), MCOI::OPERAND_REGISTER, 0 }, { MyPtrRC, 0|(1<<MCOI::LookupRegClassByHwMode), MCOI::OPERAND_UNKNOWN, 0 },
+// INSTRINFO: { YRegs_EvenIfRequired, 0|(1<<MCOI::LookupRegClassByHwMode), MCOI::OPERAND_REGISTER, 0 }, { XRegs_EvenIfRequired, 0|(1<<MCOI::LookupRegClassByHwMode), MCOI::OPERAND_REGISTER, 0 },
+
+
+
+// ASMMATCHER: enum MatchClassKind {
+// ASMMATCHER:   MCK_LAST_TOKEN = OptionalMatchClass,
+// ASMMATCHER-NEXT:   MCK_XRegs_Odd, // register class 'XRegs_Odd'
+// ASMMATCHER-NEXT:   MCK_PtrRegs32, // register class 'PtrRegs32'
+// ASMMATCHER-NEXT:   MCK_PtrRegs64, // register class 'PtrRegs64'
+// ASMMATCHER-NEXT:   MCK_XRegs_Even, // register class 'XRegs_Even'
+// ASMMATCHER-NEXT:   MCK_YRegs_Even, // register class 'YRegs_Even'
+// ASMMATCHER-NEXT:   MCK_XRegs, // register class 'XRegs'
+// ASMMATCHER-NEXT:   MCK_YRegs, // register class 'YRegs'
+// ASMMATCHER-NEXT:   MCK_LAST_REGISTER = MCK_YRegs,
+// ASMMATCHER-NEXT:   MCK_RegByHwMode_MyPtrRC, // register class by hwmode
+// ASMMATCHER-NEXT:   MCK_RegByHwMode_XRegs_EvenIfRequired, // register class by hwmode
+// ASMMATCHER-NEXT:   MCK_RegByHwMode_YRegs_EvenIfRequired, // register class by hwmode
+// ASMMATCHER-NEXT:   MCK_LAST_REGCLASS_BY_HWMODE = MCK_RegByHwMode_YRegs_EvenIfRequired,
+// ASMMATCHER-NEXT:   MCK_Imm, // user defined class 'ImmAsmOperand'
+
+// ASMMATCHER: static unsigned validateOperandClass(MCParsedAsmOperand &GOp, MatchClassKind Kind, const MCSubtargetInfo &STI) {
+// ASMMATCHER: if (Operand.isToken() && Kind <= MCK_LAST_TOKEN)
+
+// ASMMATCHER: switch (Kind) {
+
+// ASMMATCHER: if (Operand.isReg() && Kind > MCK_LAST_REGISTER && Kind <= MCK_LAST_REGCLASS_BY_HWMODE) {
+// ASMMATCHER-NEXT:    static constexpr MatchClassKind RegClassByHwModeMatchTable[4][3] = {
+// ASMMATCHER-NEXT:      { // DefaultMode
+// ASMMATCHER-NEXT:        MCK_PtrRegs32,
+// ASMMATCHER-NEXT:        MCK_XRegs,
+// ASMMATCHER-NEXT:        MCK_YRegs,
+// ASMMATCHER-NEXT:      },
+// ASMMATCHER-NEXT:      { // EvenMode
+// ASMMATCHER-NEXT:        InvalidMatchClass, // Missing mode
+// ASMMATCHER-NEXT:        MCK_XRegs_Even,
+// ASMMATCHER-NEXT:        MCK_YRegs_Even,
+// ASMMATCHER-NEXT:      },
+// ASMMATCHER-NEXT:      { // OddMode
+// ASMMATCHER-NEXT:        InvalidMatchClass, // Missing mode
+// ASMMATCHER-NEXT:        MCK_XRegs_Odd,
+// ASMMATCHER-NEXT:        InvalidMatchClass, // Missing mode
+// ASMMATCHER-NEXT:      },
+// ASMMATCHER-NEXT:      { // Ptr64
+// ASMMATCHER-NEXT:        MCK_PtrRegs64,
+// ASMMATCHER-NEXT:        InvalidMatchClass, // Missing mode
+// ASMMATCHER-NEXT:        InvalidMatchClass, // Missing mode
+// ASMMATCHER-NEXT:      },
+// ASMMATCHER-NEXT:    };
+// ASMMATCHER-EMPTY:
+// ASMMATCHER-NEXT:    static_assert(MCK_LAST_REGCLASS_BY_HWMODE - MCK_LAST_REGISTER == 3);
+// ASMMATCHER-NEXT:    const unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_RegClass);
+// ASMMATCHER-NEXT:    Kind = RegClassByHwModeMatchTable[HwMode][Kind - (MCK_LAST_REGISTER + 1)];
+// ASMMATCHER-NEXT:  }
+
+// ASMMATCHER: if (Operand.isReg()) {
+
+// ASMMATCHER: static const MatchEntry MatchTable0[] = {
+// ASMMATCHER: /* always_all */, MyTarget::ALWAYS_ALL, Convert__Reg1_0, AMFBS_None, { MCK_XRegs }, },
+// ASMMATCHER: /* always_even */, MyTarget::ALWAYS_EVEN, Convert__Reg1_0, AMFBS_None, { MCK_XRegs_Even }, },
+// ASMMATCHER: /* custom_decode */, MyTarget::CUSTOM_DECODE, Convert__RegByHwMode_YRegs_EvenIfRequired1_0, AMFBS_None, { MCK_RegByHwMode_YRegs_EvenIfRequired }, },
+// ASMMATCHER: /* even_if_mode */, MyTarget::EVEN_IF_MODE, Convert__RegByHwMode_XRegs_EvenIfRequired1_0, AMFBS_None, { MCK_RegByHwMode_XRegs_EvenIfRequired }, },
+// ASMMATCHER: /* my_mov */, MyTarget::MY_MOV, Convert__RegByHwMode_YRegs_EvenIfRequired1_0__RegByHwMode_XRegs_EvenIfRequired1_1, AMFBS_None, { MCK_RegByHwMode_YRegs_EvenIfRequired, MCK_RegByHwMode_XRegs_EvenIfRequired }, },
+
+
+
+// DISASM: {{\[\[}}maybe_unused{{\]\]}}
+// DISASM-NEXT: static DecodeStatus DecodeMyPtrRCRegClassByHwMode(MCInst &Inst, unsigned Imm, uint64_t Addr, const MCDisassembler *Decoder) {
+// DISASM-NEXT:   switch (Decoder->getSubtargetInfo().getHwMode(MCSubtargetInfo::HwMode_RegClass)) {
+// DISASM-NEXT:   case 0: // DefaultMode
+// DISASM-NEXT:     return DecodePtrRegs32RegisterClass(Inst, Imm, Addr, Decoder);
+// DISASM-NEXT:   case 3: // Ptr64
+// DISASM-NEXT:     return DecodePtrRegs64RegisterClass(Inst, Imm, Addr, Decoder);
+// DISASM-NEXT:   default:
+// DISASM-NEXT:     llvm_unreachable("no decoder for hwmode");
+// DISASM-NEXT:   }
+// DISASM-NEXT: }
+
+// DISASM: {{\[\[}}maybe_unused{{\]\]}}
+// DISASM-NEXT: static DecodeStatus DecodeXRegs_EvenIfRequiredRegClassByHwMode(MCInst &Inst, unsigned Imm, uint64_t Addr, const MCDisassembler *Decoder) {
+// DISASM-NEXT:  switch (Decoder->getSubtargetInfo().getHwMode(MCSubtargetInfo::HwMode_RegClass)) {
+// DISASM-NEXT:  case 0: // DefaultMode
+// DISASM-NEXT:    return DecodeXRegsRegisterClass(Inst, Imm, Addr, Decoder);
+// DISASM-NEXT:  case 1: // EvenMode
+// DISASM-NEXT:    return DecodeXRegs_EvenRegisterClass(Inst, Imm, Addr, Decoder);
+// DISASM-NEXT:  case 2: // OddMode
+// DISASM-NEXT:    return DecodeXRegs_OddRegisterClass(Inst, Imm, Addr, Decoder);
+// DISASM-NEXT:  default:
+// DISASM-NEXT:    llvm_unreachable("no decoder for hwmode");
+// DISASM-NEXT:  }
+// DISASM-NEXT:}
+// DISASM-EMPTY:
+// DISASM: {{\[\[}}maybe_unused{{\]\]}}
+// DISASM-NEXT: static DecodeStatus DecodeYRegs_EvenIfRequiredRegClassByHwMode(MCInst &Inst, unsigned Imm, uint64_t Addr, const MCDisassembler *Decoder) {
+// DISASM-NEXT:  switch (Decoder->getSubtargetInfo().getHwMode(MCSubtargetInfo::HwMode_RegClass)) {
+// DISASM-NEXT:  case 0: // DefaultMode
+// DISASM-NEXT:    return DecodeYRegsRegisterClass(Inst, Imm, Addr, Decoder);
+// DISASM-NEXT:  case 1: // EvenMode
+// DISASM-NEXT:    return DecodeYRegs_EvenRegisterClass(Inst, Imm, Addr, Decoder);
+// DISASM-NEXT:  default:
+// DISASM-NEXT:    llvm_unreachable("no decoder for hwmode");
+// DISASM-NEXT:  }
+// DISASM-NEXT:}
+
+// DISASM: static DecodeStatus decodeToMCInst(
+// DISASM:   switch (Idx) {
+// DISASM: case 0:
+// DISASM: if (!Check(S, DecodeYRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+// DISASM: if (!Check(S, DecodeXRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+
+// DISASM: case 1:
+// DISASM: if (!Check(S, DecodeXRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+
+// DISASM: case 2:
+// DISASM: if (!Check(S, DecodeXRegs_EvenRegisterClass(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+// DISASM: case 3:
+// DISASM: if (!Check(S, DecodeXRegsRegisterClass(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+
+// DISASM: case 4:
+// DISASM: if (!Check(S, YEvenIfRequiredCustomDecoder(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+
+
+// ISEL-SDAG: MatcherTable
+// ISEL-SDAG: OPC_SwitchOpcode /*2 cases */, {{[0-9]+}}, TARGET_VAL(ISD::STORE),
+// ISEL-SDAG: OPC_RecordMemRef,
+// ISEL-SDAG: OPC_RecordNode, // #0 = 'st' chained node
+// ISEL-SDAG: OPC_RecordChild1, // #1 = $val
+// ISEL-SDAG-NEXT: OPC_RecordChild2, // #2 = $src
+// ISEL-SDAG-NEXT: OPC_CheckChild2TypeI64,
+// ISEL-SDAG-NEXT: OPC_CheckPredicate2,  // Predicate_unindexedstore
+// ISEL-SDAG-NEXT: OPC_CheckPredicate3,  // Predicate_store
+// ISEL-SDAG: OPC_MorphNodeTo0, TARGET_VAL(MyTarget::MY_STORE), 0|OPFL_Chain|OPFL_MemRefs,
+
+// ISEL-SDAG: /*SwitchOpcode*/ {{[0-9]+}}, TARGET_VAL(ISD::LOAD),
+// ISEL-SDAG-NEXT: OPC_RecordMemRef,
+// ISEL-SDAG-NEXT: OPC_RecordNode, // #0 = 'ld' chained node
+// ISEL-SDAG-NEXT: OPC_RecordChild1, // #1 = $src
+// ISEL-SDAG-NEXT: OPC_CheckTypeI64,
+// ISEL-SDAG-NEXT: OPC_Scope, {{[0-9]+}}, /*->{{[0-9]+}}*/ // 2 children in Scope
+// ISEL-SDAG-NEXT: OPC_CheckChild1TypeI32,
+// ISEL-SDAG-NEXT: OPC_CheckPredicate0,  // Predicate_unindexedload
+// ISEL-SDAG-NEXT: OPC_CheckPredicate1,  // Predicate_load
+// ISEL-SDAG-NEXT: OPC_Scope, {{[0-9]+}}, /*->{{[0-9]+}}*/ // 3 children in Scope
+// ISEL-SDAG-NEXT: OPC_CheckPatternPredicate1, // (Subtarget->hasAlignedRegisters())
+// ISEL-SDAG-NEXT: OPC_EmitMergeInputChains1_0,
+// ISEL-SDAG-NEXT: OPC_MorphNodeTo1, TARGET_VAL(MyTarget::MY_LOAD), 0|OPFL_Chain|OPFL_MemRefs,
+
+// ISEL-SDAG: /*Scope*/
+// ISEL-SDAG: OPC_CheckPatternPredicate2, // (Subtarget->hasUnalignedRegisters())
+// ISEL-SDAG-NEXT: OPC_EmitMergeInputChains1_0,
+// ISEL-SDAG-NEXT: OPC_MorphNodeTo1, TARGET_VAL(MyTarget::MY_LOAD), 0|OPFL_Chain|OPFL_MemRefs,
+
+// ISEL-SDAG: /*Scope*/
+// ISEL-SDAG: OPC_CheckPatternPredicate3, // !((Subtarget->hasAlignedRegisters())) && !((Subtarget->hasUnalignedRegisters())) && !((Subtarget->isPtr64()))
+// ISEL-SDAG-NEXT: OPC_EmitMergeInputChains1_0,
+// ISEL-SDAG-NEXT: OPC_MorphNodeTo1, TARGET_VAL(MyTarget::MY_LOAD), 0|OPFL_Chain|OPFL_MemRefs,
+
+// ISEL-SDAG: /*Scope*/
+// ISEL-SDAG-NEXT: OPC_CheckChild1TypeI64,
+// ISEL-SDAG-NEXT: OPC_CheckPredicate0,  // Predicate_unindexedload
+// ISEL-SDAG-NEXT: OPC_CheckPredicate1,  // Predicate_load
+// ISEL-SDAG-NEXT: OPC_CheckPatternPredicate0, // (Subtarget->isPtr64())
+// ISEL-SDAG-NEXT: OPC_EmitMergeInputChains1_0,
+// ISEL-SDAG-NEXT: OPC_MorphNodeTo1, TARGET_VAL(MyTarget::MY_LOAD), 0|OPFL_Chain|OPFL_MemRefs,
+
+
+
+
+
+// ISEL-GISEL: GIM_Try, /*On fail goto*//*Label 3*/ GIMT_Encode4(148),
+// ISEL-GISEL-NEXT:   GIM_RootCheckType, /*Op*/0, /*Type*/GILLT_s64,
+// ISEL-GISEL-NEXT:   GIM_CheckAtomicOrdering, /*MI*/0, /*Order*/(uint8_t)AtomicOrdering::NotAtomic,
+// ISEL-GISEL-NEXT:   GIM_CheckMemorySizeEqualToLLT, /*MI*/0, /*MMO*/0, /*OpIdx*/0,
+// ISEL-GISEL-NEXT:   GIM_RootCheckRegBankForClass, /*Op*/0, /*RC*/GIMT_Encode2(MyTarget::XRegsRegClassID),
+// ISEL-GISEL-NEXT:   GIM_Try, /*On fail goto*//*Label 4*/ GIMT_Encode4(101),
+// ISEL-GISEL-NEXT:     GIM_CheckPointerToAny, /*MI*/0, /*Op*/1, /*SizeInBits*/32,
+
+// FIXME: This should be a direct check for regbank, not have an incorrect class
+
+// ISEL-GISEL-NEXT:     GIM_RootCheckRegBankForClass, /*Op*/1, /*RC*/GIMT_Encode2(MyTarget::PtrRegs32RegClassID),
+// ISEL-GISEL-NEXT:     GIM_Try, /*On fail goto*//*Label 5*/ GIMT_Encode4(85), // Rule ID 1 //
+// ISEL-GISEL-NEXT:       GIM_CheckFeatures, GIMT_Encode2(GIFBS_HwMode1),
+// ISEL-GISEL-NEXT:       // (ld:{ *:[i64] } PtrRegOperand:{ *:[i32] }:$src)<<P:Predicate_unindexedload>><<P:Predicate_load>>  =>  (MY_LOAD:{ *:[i64] } ?:{ *:[i32] }:$src)
+// ISEL-GISEL-NEXT:       GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::MY_LOAD),
+// ISEL-GISEL-NEXT:       GIR_RootConstrainSelectedInstOperands,
+// ISEL-GISEL-NEXT:       // GIR_Coverage, 1,
+// ISEL-GISEL-NEXT:       GIR_Done,
+// ISEL-GISEL-NEXT:     // Label 5: @85
+// ISEL-GISEL-NEXT:     GIM_Try, /*On fail goto*//*Label 6*/ GIMT_Encode4(100), // Rule ID 2 //
+// ISEL-GISEL-NEXT:       GIM_CheckFeatures, GIMT_Encode2(GIFBS_HwMode2),
+// ISEL-GISEL-NEXT:       // (ld:{ *:[i64] } PtrRegOperand:{ *:[i32] }:$src)<<P:Predicate_unindexedload>><<P:Predicate_load>>  =>  (MY_LOAD:{ *:[i64] } ?:{ *:[i32] }:$src)
+// ISEL-GISEL-NEXT:       GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::MY_LOAD),
+// ISEL-GISEL-NEXT:       GIR_RootConstrainSelectedInstOperands,
+// ISEL-GISEL-NEXT:       // GIR_Coverage, 2,
+// ISEL-GISEL-NEXT:       GIR_Done,
+// ISEL-GISEL-NEXT:     // Label 6: @100
+// ISEL-GISEL-NEXT:     GIM_Reject,
+// ISEL-GISEL-NEXT:   // Label 4: @101
+// ISEL-GISEL-NEXT:   GIM_Try, /*On fail goto*//*Label 7*/ GIMT_Encode4(124), // Rule ID 3 //
+// ISEL-GISEL-NEXT:     GIM_CheckFeatures, GIMT_Encode2(GIFBS_HwMode0),
+// ISEL-GISEL-NEXT:     // MIs[0] src
+// ISEL-GISEL-NEXT:     GIM_CheckPointerToAny, /*MI*/0, /*Op*/1, /*SizeInBits*/64,
+// ISEL-GISEL-NEXT:     GIM_RootCheckRegBankForClass, /*Op*/1, /*RC*/GIMT_Encode2(MyTarget::PtrRegs32RegClassID),
+// ISEL-GISEL-NEXT:     // (ld:{ *:[i64] } PtrRegOperand:{ *:[i64] }:$src)<<P:Predicate_unindexedload>><<P:Predicate_load>>  =>  (MY_LOAD:{ *:[i64] } ?:{ *:[i64] }:$src)
+// ISEL-GISEL-NEXT:     GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::MY_LOAD),
+// ISEL-GISEL-NEXT:     GIR_RootConstrainSelectedInstOperands,
+// ISEL-GISEL-NEXT:     // GIR_Coverage, 3,
+// ISEL-GISEL-NEXT:     GIR_Done,
+// ISEL-GISEL-NEXT:   // Label 7: @124
+// ISEL-GISEL-NEXT:   GIM_Try, /*On fail goto*//*Label 8*/ GIMT_Encode4(147), // Rule ID 4 //
+// ISEL-GISEL-NEXT:     GIM_CheckFeatures, GIMT_Encode2(GIFBS_HwMode3),
+// ISEL-GISEL-NEXT:     // MIs[0] src
+// ISEL-GISEL-NEXT:     GIM_CheckPointerToAny, /*MI*/0, /*Op*/1, /*SizeInBits*/32,
+// ISEL-GISEL-NEXT:     GIM_RootCheckRegBankForClass, /*Op*/1, /*RC*/GIMT_Encode2(MyTarget::PtrRegs32RegClassID),
+// ISEL-GISEL-NEXT:     // (ld:{ *:[i64] } PtrRegOperand:{ *:[i32] }:$src)<<P:Predicate_unindexedload>><<P:Predicate_load>>  =>  (MY_LOAD:{ *:[i64] } ?:{ *:[i32] }:$src)
+// ISEL-GISEL-NEXT:     GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::MY_LOAD),
+// ISEL-GISEL-NEXT:     GIR_RootConstrainSelectedInstOperands,
+// ISEL-GISEL-NEXT:     // GIR_Coverage, 4,
+// ISEL-GISEL-NEXT:     GIR_Done,
+// ISEL-GISEL-NEXT:   // Label 8: @147
+// ISEL-GISEL-NEXT:   GIM_Reject,
+// ISEL-GISEL-NEXT: // Label 3: @148
+// ISEL-GISEL-NEXT: GIM_Reject,
+// ISEL-GISEL-NEXT: // Label 1: @149
+// ISEL-GISEL-NEXT: GIM_Try, /*On fail goto*//*Label 9*/ GIMT_Encode4(186), // Rule ID 0 //
+// ISEL-GISEL-NEXT:   GIM_CheckFeatures, GIMT_Encode2(GIFBS_HwMode0),
+// ISEL-GISEL-NEXT:   GIM_RootCheckType, /*Op*/0, /*Type*/GILLT_s64,
+// ISEL-GISEL-NEXT:   GIM_CheckAtomicOrdering, /*MI*/0, /*Order*/(uint8_t)AtomicOrdering::NotAtomic,
+// ISEL-GISEL-NEXT:   GIM_CheckMemorySizeEqualToLLT, /*MI*/0, /*MMO*/0, /*OpIdx*/0,
+// ISEL-GISEL-NEXT:   GIM_RootCheckRegBankForClass, /*Op*/0, /*RC*/GIMT_Encode2(MyTarget::XRegsRegClassID),
+// ISEL-GISEL-NEXT:   // MIs[0] src
+// ISEL-GISEL-NEXT:   GIM_CheckPointerToAny, /*MI*/0, /*Op*/1, /*SizeInBits*/64,
+// ISEL-GISEL-NEXT:   GIM_RootCheckRegBankForClass, /*Op*/1, /*RC*/GIMT_Encode2(MyTarget::PtrRegs32RegClassID),
+// ISEL-GISEL-NEXT:   // (st XRegs_EvenIfRequired:{ *:[i64] }:$val, MyPtrRC:{ *:[i64] }:$src)<<P:Predicate_unindexedstore>><<P:Predicate_store>>  =>  (MY_STORE ?:{ *:[i64] }:$val, XRegs_EvenIfRequired:{ *:[i64] }:$src)
+// ISEL-GISEL-NEXT:   GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::MY_STORE),
+// ISEL-GISEL-NEXT:   GIR_RootConstrainSelectedInstOperands,
+
+
+
+def HasAlignedRegisters : Predicate<"Subtarget->hasAlignedRegisters()">;
+def HasUnalignedRegisters : Predicate<"Subtarget->hasUnalignedRegisters()">;
+def IsPtr64 : Predicate<"Subtarget->isPtr64()">;
+
+// FIXME: In reality these should be mutually exclusive and we need
+// the cross product of even mode / ptr size. i.e. EvenModePtr64,
+// OddMode32 etc. For the purposes of this test where we won't be
+// executing the code to compute a mode ID, it's simpler to pretend
+// these are orthogonal.
+def EvenMode : HwMode<[HasAlignedRegisters]>;
+def OddMode : HwMode<[HasUnalignedRegisters]>;
+def Ptr64 : HwMode<[IsPtr64]>;
+
+class MyReg<string n>
+  : Register<n> {
+  let Namespace = "MyTarget";
+}
+
+class MyClass<int size, list<ValueType> types, dag registers>
+  : RegisterClass<"MyTarget", types, size, registers> {
+  let Size = size;
+}
+
+def X0 : MyReg<"x0">;
+def X1 : MyReg<"x1">;
+def X2 : MyReg<"x2">;
+def X3 : MyReg<"x3">;
+def X4 : MyReg<"x4">;
+def X5 : MyReg<"x5">;
+def X6 : MyReg<"x6">;
+
+def Y0 : MyReg<"y0">;
+def Y1 : MyReg<"y1">;
+def Y2 : MyReg<"y2">;
+def Y3 : MyReg<"y3">;
+def Y4 : MyReg<"y4">;
+def Y5 : MyReg<"y5">;
+def Y6 : MyReg<"y6">;
+
+
+
+def P0_32 : MyReg<"p0">;
+def P1_32 : MyReg<"p1">;
+def P2_32 : MyReg<"p2">;
+def P3_32 : MyReg<"p3">;
+
+def P0_64 : MyReg<"p0_64">;
+def P1_64 : MyReg<"p1_64">;
+def P2_64 : MyReg<"p2_64">;
+def P3_64 : MyReg<"p3_64">;
+
+
+
+def XRegs : RegisterClass<"MyTarget", [i64], 64, (add X0, X1, X2, X3, X4, X5, X6)>;
+def XRegs_Odd : RegisterClass<"MyTarget", [i64], 64, (add X1, X3, X5)>;
+def XRegs_Even : RegisterClass<"MyTarget", [i64], 64, (add X0, X2, X4, X6)>;
+
+def XRegs_EvenIfRequired : RegClassByHwMode<[DefaultMode, EvenMode, OddMode],
+                                           [XRegs, XRegs_Even, XRegs_Odd]>;
+
+def YRegs : RegisterClass<"MyTarget", [i64], 64, (add Y0, Y1, Y2, Y3, Y4, Y5, Y6)>;
+def YRegs_Even : RegisterClass<"MyTarget", [i64], 64, (add Y0, Y2, Y4, Y6)>;
+
+def YRegs_EvenIfRequired : RegClassByHwMode<[DefaultMode, EvenMode],
+                                            [YRegs, YRegs_Even]>;
+
+
+def PtrRegs32 : RegisterClass<"MyTarget", [i32], 32, (add P0_32, P1_32, P2_32, P3_32)>;
+def PtrRegs64 : RegisterClass<"MyTarget", [i64], 64, (add P0_64, P1_64, P2_64, P3_64)>;
+
+def MyPtrRC : RegClassByHwMode<[DefaultMode, Ptr64],
+                               [PtrRegs32, PtrRegs64]>, Operand<iPTR>;
+
+
+def PtrRegOperand : RegisterOperand<MyPtrRC>, Operand<iPTR>;
+
+
+def CustomDecodeYEvenIfRequired : RegisterOperand<YRegs_EvenIfRequired> {
+  let DecoderMethod = "YEvenIfRequiredCustomDecoder";
+}
+
+class TestInstruction : Instruction {
+  let Size = 2;
+  let Namespace = "MyTarget";
+  let hasSideEffects = false;
+  let hasExtraSrcRegAllocReq = false;
+  let hasExtraDefRegAllocReq = false;
+
+  field bits<16> Inst;
+  bits<3> dst;
+  bits<3> src;
+  bits<3> opcode;
+
+  let Inst{2-0} = dst;
+  let Inst{5-3} = src;
+  let Inst{7-5} = opcode;
+}
+
+def SpecialOperand : RegisterOperand<XRegs_EvenIfRequired>;
+
+def MY_MOV : TestInstruction {
+  let OutOperandList = (outs YRegs_EvenIfRequired:$dst);
+  let InOperandList = (ins XRegs_EvenIfRequired:$src);
+  let AsmString = "my_mov $dst, $src";
+  let opcode = 0;
+}
+
+def EVEN_IF_MODE : TestInstruction {
+  let OutOperandList = (outs);
+  let InOperandList = (ins XRegs_EvenIfRequired:$src);
+  let AsmString = "even_if_mode $src";
+  let opcode = 1;
+}
+
+def ALWAYS_EVEN : TestInstruction {
+  let OutOperandList = (outs);
+  let InOperandList = (ins XRegs_Even:$src);
+  let AsmString = "always_even $src";
+  let opcode = 2;
+}
+
+def ALWAYS_ALL : TestInstruction {
+  let OutOperandList = (outs);
+  let InOperandList = (ins XRegs:$src);
+  let AsmString = "always_all $src";
+  let opcode = 3;
+}
+
+def CUSTOM_DECODE : TestInstruction {
+  let OutOperandList = (outs);
+  let InOperandList = (ins CustomDecodeYEvenIfRequired:$src);
+  let AsmString = "custom_decode $src";
+  let opcode = 4;
+}
+
+def MyTargetMov : SDNode<"MyTarget::MY_MOV", SDTUnaryOp, []>;
+
+// Test 2 different cases directly in the instruction
+def MY_STORE : TestInstruction {
+  let OutOperandList = (outs);
+  let InOperandList = (ins XRegs_EvenIfRequired:$src, MyPtrRC:$dst);
+  let AsmString = "my_store $src, $dst";
+  let opcode = 5;
+}
+
+// Test 2 different cases wrapped by RegisterOperand
+def MY_LOAD : TestInstruction {
+  let OutOperandList = (outs RegisterOperand<XRegs_EvenIfRequired>:$dst);
+  let InOperandList = (ins PtrRegOperand:$src);
+  let AsmString = "my_load $dst, $src";
+  let opcode = 6;
+}
+
+
+// Direct RegClassByHwMode usage
+def : Pat<
+  (store XRegs_EvenIfRequired:$val, MyPtrRC:$src),
+  (MY_STORE $val, XRegs_EvenIfRequired:$src)
+>;
+
+// Wrapped in RegisterOperand
+def : Pat<
+  (i64 (load PtrRegOperand:$src)),
+  (MY_LOAD $src)
+>;
+
+def MyTargetISA : InstrInfo;
+def MyTarget : Target { let InstructionSet = MyTargetISA; }
diff --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
index 29d26d5237695..ef41d93a09bc8 100644
--- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp
+++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
@@ -166,9 +166,14 @@ struct ClassInfo {
     /// RegisterClass0+1, and so on.
     RegisterClass0,
 
+    /// The (first) register class by hwmode, subsequent register classes by
+    /// hwmode are
+    /// RegisterClassByHwMode0+1, and so on.
+    RegisterClassByHwMode0 = 1 << 12,
+
     /// The (first) user defined class, subsequent user defined classes are
     /// UserClass0+1, and so on.
-    UserClass0 = 1 << 16
+    UserClass0 = 1 << 24
   };
 
   /// Kind - The class kind, which is either a predefined kind, or (UserClass0 +
@@ -224,7 +229,11 @@ struct ClassInfo {
 public:
   /// isRegisterClass() - Check if this is a register class.
   bool isRegisterClass() const {
-    return Kind >= RegisterClass0 && Kind < UserClass0;
+    return Kind >= RegisterClass0 && Kind < RegisterClassByHwMode0;
+  }
+
+  bool isRegisterClassByHwMode() const {
+    return Kind >= RegisterClassByHwMode0 && Kind < UserClass0;
   }
 
   /// isUserClass() - Check if this is a user defined class.
@@ -251,6 +260,9 @@ struct ClassInfo {
       return !Tmp.empty();
     }
 
+    if (isRegisterClassByHwMode() || RHS.isRegisterClassByHwMode())
+      return isRegisterClassByHwMode() == RHS.isRegisterClassByHwMode();
+
     // Otherwise we have two users operands; they are related if they are in the
     // same class hierarchy.
     //
@@ -315,7 +327,8 @@ struct ClassInfo {
       return false;
 
     // First, enforce the ordering between the three different types of class.
-    // Tokens sort before registers, which sort before user classes.
+    // Tokens sort before registers, which sort before regclass by hwmode, which
+    // sort before user classes.
     if (Kind == Token) {
       if (RHS.Kind != Token)
         return true;
@@ -323,9 +336,15 @@ struct ClassInfo {
     } else if (isRegisterClass()) {
       if (RHS.Kind == Token)
         return false;
-      else if (RHS.isUserClass())
+      else if (RHS.isUserClass() || RHS.isRegisterClassByHwMode())
         return true;
       assert(RHS.isRegisterClass());
+    } else if (isRegisterClassByHwMode()) {
+      if (RHS.Kind == Token || RHS.isRegisterClass())
+        return false;
+      else if (RHS.isUserClass())
+        return true;
+      assert(RHS.isRegisterClassByHwMode());
     } else if (isUserClass()) {
       if (!RHS.isUserClass())
         return false;
@@ -353,6 +372,10 @@ struct ClassInfo {
       // a set will always sort before all of it's strict supersets.
       if (Registers.size() != RHS.Registers.size())
         return Registers.size() < RHS.Registers.size();
+    } else if (isRegisterClassByHwMode()) {
+      // Ensure the MCK enum entries are in the same order as RegClassIDs. The
+      // lookup table to from RegByHwMode to concrete class relies on it.
+      return Kind < RHS.Kind;
     } else {
       llvm_unreachable("Unknown ClassInfoKind");
     }
@@ -1205,9 +1228,13 @@ ClassInfo *AsmMatcherInfo::getOperandClass(const Record *Rec, int SubOpIdx) {
       PrintFatalError(Rec->getLoc(),
                       "RegisterOperand `" + Rec->getName() +
                           "' has no associated register class!\n");
-    if (ClassInfo *CI = RegisterClassClasses[ClassRec])
-      return CI;
-    PrintFatalError(Rec->getLoc(), "register class has no class info!");
+
+    if (ClassRec->isSubClassOf("RegisterClassLike")) {
+      if (ClassInfo *CI = RegisterClassClasses[ClassRec])
+        return CI;
+
+      PrintFatalError(Rec->getLoc(), "register class has no class info!");
+    }
   }
 
   if (Rec->isSubClassOf("RegisterClass")) {
@@ -1216,13 +1243,19 @@ ClassInfo *AsmMatcherInfo::getOperandClass(const Record *Rec, int SubOpIdx) {
     PrintFatalError(Rec->getLoc(), "register class has no class info!");
   }
 
-  if (!Rec->isSubClassOf("Operand"))
+  if (Rec->isSubClassOf("Operand")) {
+    const Record *MatchClass = Rec->getValueAsDef("ParserMatchClass");
+    if (ClassInfo *CI = AsmOperandClasses[MatchClass])
+      return CI;
+  } else if (Rec->isSubClassOf("RegisterClassLike")) {
+    if (ClassInfo *CI = RegisterClassClasses[Rec])
+      return CI;
+    PrintFatalError(Rec->getLoc(), "register class has no class info!");
+  } else {
     PrintFatalError(Rec->getLoc(),
                     "Operand `" + Rec->getName() +
                         "' does not derive from class Operand!\n");
-  const Record *MatchClass = Rec->getValueAsDef("ParserMatchClass");
-  if (ClassInfo *CI = AsmOperandClasses[MatchClass])
-    return CI;
+  }
 
   PrintFatalError(Rec->getLoc(), "operand has no match class!");
 }
@@ -1307,6 +1340,7 @@ void AsmMatcherInfo::buildRegisterClasses(
     CI->DefaultMethod = ""; // unused
     RegisterSetClasses.try_emplace(RS, CI);
     ++Index;
+    assert(CI->isRegisterClass());
   }
 
   // Find the superclasses; we could compute only the subgroup lattice edges,
@@ -1348,6 +1382,32 @@ void AsmMatcherInfo::buildRegisterClasses(
       CI->DiagnosticType = RC.getName();
 
     RegisterClassClasses.try_emplace(Def, CI);
+    assert(CI->isRegisterClass());
+  }
+
+  unsigned RegClassByHwModeIndex = 0;
+  for (const Record *ClassByHwMode : Target.getAllRegClassByHwMode()) {
+    Classes.emplace_front();
+    ClassInfo *CI = &Classes.front();
+    CI->Kind = ClassInfo::RegisterClassByHwMode0 + RegClassByHwModeIndex;
+
+    CI->ClassName = "RegByHwMode_" + ClassByHwMode->getName().str();
+    CI->Name = "MCK_" + CI->ClassName;
+    CI->ValueName = ClassByHwMode->getName();
+    CI->PredicateMethod = ""; // unused
+    CI->RenderMethod = "addRegOperands";
+    // CI->Registers = RS;
+    //  FIXME: diagnostic type.
+    CI->DiagnosticType = "";
+    CI->IsOptional = false;
+    CI->DefaultMethod = ""; // unused
+    // RegisterSetClasses.try_emplace(RS, CI);
+
+    ++RegClassByHwModeIndex;
+
+    assert(CI->isRegisterClassByHwMode());
+
+    RegisterClassClasses.try_emplace(ClassByHwMode, CI);
   }
 
   // Populate the map for individual registers.
@@ -2367,10 +2427,14 @@ static void emitMatchClassEnumeration(CodeGenTarget &Target,
   for (const auto &CI : Infos) {
     if (LastKind == ClassInfo::Token && CI.Kind != ClassInfo::Token) {
       OS << "  MCK_LAST_TOKEN = " << LastName << ",\n";
+    } else if (LastKind < ClassInfo::RegisterClassByHwMode0 &&
+               CI.Kind >= ClassInfo::RegisterClassByHwMode0) {
+      OS << "  MCK_LAST_REGISTER = " << LastName << ",\n";
     } else if (LastKind < ClassInfo::UserClass0 &&
                CI.Kind >= ClassInfo::UserClass0) {
-      OS << "  MCK_LAST_REGISTER = " << LastName << ",\n";
+      OS << "  MCK_LAST_REGCLASS_BY_HWMODE = " << LastName << ",\n";
     }
+
     LastKind = (ClassInfo::ClassInfoKind)CI.Kind;
     LastName = CI.Name;
 
@@ -2382,6 +2446,8 @@ static void emitMatchClassEnumeration(CodeGenTarget &Target,
         OS << "register class '" << CI.ValueName << "'\n";
       else
         OS << "derived register class\n";
+    } else if (CI.isRegisterClassByHwMode()) {
+      OS << "register class by hwmode\n";
     } else {
       OS << "user defined class '" << CI.ValueName << "'\n";
     }
@@ -2454,7 +2520,7 @@ static void emitRegisterMatchErrorFunc(AsmMatcherInfo &Info, raw_ostream &OS) {
 static void emitValidateOperandClass(const CodeGenTarget &Target,
                                      AsmMatcherInfo &Info, raw_ostream &OS) {
   OS << "static unsigned validateOperandClass(MCParsedAsmOperand &GOp, "
-     << "MatchClassKind Kind) {\n";
+     << "MatchClassKind Kind, const MCSubtargetInfo &STI) {\n";
   OS << "  " << Info.Target.getName() << "Operand &Operand = ("
      << Info.Target.getName() << "Operand &)GOp;\n";
 
@@ -2494,8 +2560,68 @@ static void emitValidateOperandClass(const CodeGenTarget &Target,
   }
   OS << "  } // end switch (Kind)\n\n";
 
+  const CodeGenRegBank &RegBank = Target.getRegBank();
+  ArrayRef<const Record *> RegClassesByHwMode = Target.getAllRegClassByHwMode();
+  unsigned NumClassesByHwMode = RegClassesByHwMode.size();
+
+  if (!RegClassesByHwMode.empty()) {
+    OS << "  if (Operand.isReg() && Kind > MCK_LAST_REGISTER &&"
+          " Kind <= MCK_LAST_REGCLASS_BY_HWMODE) {\n";
+
+    const CodeGenHwModes &CGH = Target.getHwModes();
+    unsigned NumModes = CGH.getNumModeIds();
+
+    OS << indent(4)
+       << "static constexpr MatchClassKind RegClassByHwModeMatchTable["
+       << NumModes << "][" << RegClassesByHwMode.size() << "] = {\n";
+
+    // TODO: If the instruction predicates can statically resolve which hwmode,
+    // directly match the register class
+    for (unsigned M = 0; M < NumModes; ++M) {
+      OS << indent(6) << "{ // " << CGH.getModeName(M, /*IncludeDefault=*/true)
+         << '\n';
+      for (unsigned I = 0; I != NumClassesByHwMode; ++I) {
+        const Record *Class = RegClassesByHwMode[I];
+        const HwModeSelect &ModeSelect = CGH.getHwModeSelect(Class);
+
+        bool Seen = false;
+        for (const HwModeSelect::PairType &P : ModeSelect.Items) {
+          if (P.first == M) {
+            const CodeGenRegisterClass *RegClass =
+                RegBank.getRegClass(P.second);
+
+            const ClassInfo *CI =
+                Info.RegisterClassClasses.at(RegClass->getDef());
+
+            OS << indent(8) << CI->Name << ",\n";
+            Seen = true;
+            break;
+          }
+        }
+
+        if (!Seen)
+          OS << indent(8) << "InvalidMatchClass, // Missing mode\n";
+      }
+
+      OS << indent(6) << "},\n";
+    }
+
+    OS << indent(4) << "};\n\n";
+
+    OS << indent(4)
+       << "static_assert(MCK_LAST_REGCLASS_BY_HWMODE - MCK_LAST_REGISTER == "
+       << NumClassesByHwMode << ");\n";
+
+    OS << indent(4)
+       << "const unsigned HwMode = "
+          "STI.getHwMode(MCSubtargetInfo::HwMode_RegClass);\n"
+          "Kind = RegClassByHwModeMatchTable[HwMode][Kind - (MCK_LAST_REGISTER "
+          "+ 1)];\n"
+          "  }\n\n";
+  }
+
   // Check for register operands, including sub-classes.
-  const auto &Regs = Target.getRegBank().getRegisters();
+  const auto &Regs = RegBank.getRegisters();
   StringRef Namespace = Regs.front().TheDef->getValueAsString("Namespace");
   SmallVector<StringRef> Table(1 + Regs.size(), "InvalidMatchClass");
   for (const auto &RC : Info.RegisterClasses) {
@@ -3758,7 +3884,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
   }
   OS << "      }\n";
   OS << "      MCParsedAsmOperand &Actual = *Operands[ActualIdx];\n";
-  OS << "      unsigned Diag = validateOperandClass(Actual, Formal);\n";
+  OS << "      unsigned Diag = validateOperandClass(Actual, Formal, *STI);\n";
   OS << "      if (Diag == Match_Success) {\n";
   OS << "        DEBUG_WITH_TYPE(\"asm-matcher\",\n";
   OS << "                        dbgs() << \"match success using generic "
diff --git a/llvm/utils/TableGen/AsmWriterEmitter.cpp b/llvm/utils/TableGen/AsmWriterEmitter.cpp
index 9f32333f82100..c8c6c23bea014 100644
--- a/llvm/utils/TableGen/AsmWriterEmitter.cpp
+++ b/llvm/utils/TableGen/AsmWriterEmitter.cpp
@@ -945,7 +945,7 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
 
           if (Rec->isSubClassOf("RegisterOperand"))
             Rec = Rec->getValueAsDef("RegClass");
-          if (Rec->isSubClassOf("RegisterClass")) {
+          if (Rec->isSubClassOf("RegisterClassLike")) {
             if (!IAP.isOpMapped(ROName)) {
               IAP.addOperand(ROName, MIOpNum, PrintMethodIdx);
               const Record *R = CGA.ResultOperands[i].getRecord();
diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
index f1f7cd72ef9f2..909b4394a8755 100644
--- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
@@ -1789,6 +1789,23 @@ bool llvm::operator<(const SDTypeConstraint &LHS, const SDTypeConstraint &RHS) {
   return false;
 }
 
+/// RegClassByHwMode acts like ValueTypeByHwMode, taking the type of the
+/// register class from the active mode.
+static TypeSetByHwMode getTypeForRegClassByHwMode(const CodeGenTarget &T,
+                                                  const Record *R) {
+  TypeSetByHwMode TypeSet;
+  RegClassByHwMode Helper(R, T.getHwModes(), T);
+
+  for (auto [ModeID, RegClass] : Helper) {
+    ArrayRef<ValueTypeByHwMode> RegClassVTs = RegClass->getValueTypes();
+    MachineValueTypeSet &ModeTypeSet = TypeSet.getOrCreate(ModeID);
+    for (const ValueTypeByHwMode &VT : RegClassVTs)
+      ModeTypeSet.insert(VT.getType(ModeID));
+  }
+
+  return TypeSet;
+}
+
 // Update the node type to match an instruction operand or result as specified
 // in the ins or outs lists on the instruction definition. Return true if the
 // type was actually changed.
@@ -1814,13 +1831,16 @@ bool TreePatternNode::UpdateNodeTypeFromInst(unsigned ResNo,
   // Both RegisterClass and RegisterOperand operands derive their types from a
   // register class def.
   const Record *RC = nullptr;
-  if (Operand->isSubClassOf("RegisterClass"))
+  if (Operand->isSubClassOf("RegisterClassLike"))
     RC = Operand;
   else if (Operand->isSubClassOf("RegisterOperand"))
     RC = Operand->getValueAsDef("RegClass");
 
   assert(RC && "Unknown operand type");
   CodeGenTarget &Tgt = TP.getDAGPatterns().getTargetInfo();
+  if (RC->isSubClassOf("RegClassByHwMode"))
+    return UpdateNodeType(ResNo, getTypeForRegClassByHwMode(Tgt, RC), TP);
+
   return UpdateNodeType(ResNo, Tgt.getRegisterClass(RC).getValueTypes(), TP);
 }
 
@@ -2306,6 +2326,10 @@ static TypeSetByHwMode getImplicitType(const Record *R, unsigned ResNo,
       return TypeSetByHwMode(); // Unknown.
     const Record *RegClass = R->getValueAsDef("RegClass");
     const CodeGenTarget &T = TP.getDAGPatterns().getTargetInfo();
+
+    if (RegClass->isSubClassOf("RegClassByHwMode"))
+      return getTypeForRegClassByHwMode(T, RegClass);
+
     return TypeSetByHwMode(T.getRegisterClass(RegClass).getValueTypes());
   }
 
@@ -2325,6 +2349,11 @@ static TypeSetByHwMode getImplicitType(const Record *R, unsigned ResNo,
     return TypeSetByHwMode(T.getRegisterClass(R).getValueTypes());
   }
 
+  if (R->isSubClassOf("RegClassByHwMode")) {
+    const CodeGenTarget &T = CDP.getTargetInfo();
+    return getTypeForRegClassByHwMode(T, R);
+  }
+
   if (R->isSubClassOf("PatFrags")) {
     assert(ResNo == 0 && "FIXME: PatFrag with multiple results?");
     // Pattern fragment types will be resolved when they are inlined.
@@ -3577,7 +3606,7 @@ void CodeGenDAGPatterns::FindPatternInputsAndOutputs(
       continue;
     }
 
-    if (Val->getDef()->isSubClassOf("RegisterClass") ||
+    if (Val->getDef()->isSubClassOf("RegisterClassLike") ||
         Val->getDef()->isSubClassOf("ValueType") ||
         Val->getDef()->isSubClassOf("RegisterOperand") ||
         Val->getDef()->isSubClassOf("PointerLikeRegClass")) {
diff --git a/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp b/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp
index 54ea0f1bfad5a..fb1091015a78d 100644
--- a/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp
@@ -65,11 +65,15 @@ static Expected<ResultOperand> matchSimpleOperand(const Init *Arg,
 
       // Match 'RegClass:$name' or 'RegOp:$name'.
       if (const Record *ArgRC = getInitValueAsRegClass(Arg)) {
-        if (!T.getRegisterClass(OpRC).hasSubClass(&T.getRegisterClass(ArgRC)))
-          return createStringError(
-              "argument register class" + ArgRC->getName() +
-              " is not a subclass of operand register class " +
-              OpRC->getName());
+        if (OpRC->isSubClassOf("RegisterClass")) {
+          if (!T.getRegisterClass(OpRC).hasSubClass(&T.getRegisterClass(ArgRC)))
+            return createStringError(
+                "argument register class" + ArgRC->getName() +
+                " is not a subclass of operand register class " +
+                OpRC->getName());
+        }
+        // FIXME: Do some validation on RegClassByHwMode
+
         if (!ArgName)
           return createStringError("register class argument must have a name");
         return ResultOperand::createRecord(ArgName->getAsUnquotedString(),
diff --git a/llvm/utils/TableGen/Common/CodeGenInstruction.cpp b/llvm/utils/TableGen/Common/CodeGenInstruction.cpp
index ff70d50971b89..93d4f4bfe5d27 100644
--- a/llvm/utils/TableGen/Common/CodeGenInstruction.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenInstruction.cpp
@@ -118,7 +118,7 @@ CGIOperandList::CGIOperandList(const Record *R) : TheDef(R) {
         VariadicOuts = true;
       isVariadic = true;
       continue;
-    } else if (Rec->isSubClassOf("RegisterClass")) {
+    } else if (Rec->isSubClassOf("RegisterClassLike")) {
       OperandType = "OPERAND_REGISTER";
     } else if (!Rec->isSubClassOf("PointerLikeRegClass") &&
                !Rec->isSubClassOf("unknown_class")) {
diff --git a/llvm/utils/TableGen/Common/CodeGenTarget.h b/llvm/utils/TableGen/Common/CodeGenTarget.h
index ac138344c83c6..59a04e182c0cc 100644
--- a/llvm/utils/TableGen/Common/CodeGenTarget.h
+++ b/llvm/utils/TableGen/Common/CodeGenTarget.h
@@ -134,6 +134,14 @@ class CodeGenTarget {
 
   const CodeGenRegisterClass &getRegisterClass(const Record *R) const;
 
+  /// Convenience wrapper to avoid hardcoding the name of RegClassByHwMode
+  /// everywhere. This is here instead of CodeGenRegBank to avoid the fatal
+  /// error that occurs when no RegisterClasses are defined when constructing
+  /// the bank.
+  ArrayRef<const Record *> getAllRegClassByHwMode() const {
+    return Records.getAllDerivedDefinitions("RegClassByHwMode");
+  }
+
   /// getRegisterVTs - Find the union of all possible SimpleValueTypes for the
   /// specified physical register.
   std::vector<ValueTypeByHwMode> getRegisterVTs(const Record *R) const;
diff --git a/llvm/utils/TableGen/Common/InfoByHwMode.cpp b/llvm/utils/TableGen/Common/InfoByHwMode.cpp
index cb4f8876c648a..fd6494bc8cad1 100644
--- a/llvm/utils/TableGen/Common/InfoByHwMode.cpp
+++ b/llvm/utils/TableGen/Common/InfoByHwMode.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "InfoByHwMode.h"
+#include "CodeGenRegisters.h"
 #include "CodeGenTarget.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/Twine.h"
@@ -186,6 +187,19 @@ void RegSizeInfoByHwMode::writeToStream(raw_ostream &OS) const {
   OS << '}';
 }
 
+RegClassByHwMode::RegClassByHwMode(const Record *R, const CodeGenHwModes &CGH,
+                                   const CodeGenTarget &Target) {
+  const HwModeSelect &MS = CGH.getHwModeSelect(R);
+
+  for (const HwModeSelect::PairType &P : MS.Items) {
+    assert(P.second && P.second->isSubClassOf("RegisterClass") &&
+           "Register class must subclass RegisterClass");
+    const CodeGenRegisterClass &RegClass = Target.getRegisterClass(P.second);
+    if (!Map.try_emplace(P.first, &RegClass).second)
+      llvm_unreachable("duplicate entry");
+  }
+}
+
 SubRegRange::SubRegRange(const Record *R) {
   Size = R->getValueAsInt("Size");
   Offset = R->getValueAsInt("Offset");
diff --git a/llvm/utils/TableGen/Common/InfoByHwMode.h b/llvm/utils/TableGen/Common/InfoByHwMode.h
index 7925599a98a0c..1d3bc434ced4f 100644
--- a/llvm/utils/TableGen/Common/InfoByHwMode.h
+++ b/llvm/utils/TableGen/Common/InfoByHwMode.h
@@ -28,6 +28,8 @@
 
 namespace llvm {
 
+class CodeGenRegisterClass;
+class CodeGenTarget;
 class Record;
 class raw_ostream;
 
@@ -244,6 +246,16 @@ struct EncodingInfoByHwMode : public InfoByHwMode<const Record *> {
   EncodingInfoByHwMode() = default;
 };
 
+struct RegClassByHwMode : public InfoByHwMode<const CodeGenRegisterClass *> {
+public:
+  RegClassByHwMode(const Record *R, const CodeGenHwModes &CGH,
+                   const CodeGenTarget &Target);
+  RegClassByHwMode() = default;
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const RegSizeInfo &T);
+raw_ostream &operator<<(raw_ostream &OS, const RegClassByHwMode &T);
+
 } // namespace llvm
 
 #endif // LLVM_UTILS_TABLEGEN_COMMON_INFOBYHWMODE_H
diff --git a/llvm/utils/TableGen/DAGISelMatcherGen.cpp b/llvm/utils/TableGen/DAGISelMatcherGen.cpp
index 227311b0a3bc8..d84bfa8d0c92e 100644
--- a/llvm/utils/TableGen/DAGISelMatcherGen.cpp
+++ b/llvm/utils/TableGen/DAGISelMatcherGen.cpp
@@ -238,7 +238,7 @@ void MatcherGen::EmitLeafMatchCode(const TreePatternNode &N) {
   }
 
   if ( // Handle register references.  Nothing to do here, they always match.
-      LeafRec->isSubClassOf("RegisterClass") ||
+      LeafRec->isSubClassOf("RegisterClassLike") ||
       LeafRec->isSubClassOf("RegisterOperand") ||
       LeafRec->isSubClassOf("PointerLikeRegClass") ||
       LeafRec->isSubClassOf("SubRegIndex") ||
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 8747d02ac892b..6958f4e204a6d 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -13,6 +13,7 @@
 
 #include "Common/CodeGenHwModes.h"
 #include "Common/CodeGenInstruction.h"
+#include "Common/CodeGenRegisters.h"
 #include "Common/CodeGenTarget.h"
 #include "Common/InfoByHwMode.h"
 #include "Common/VarLenCodeEmitterGen.h"
@@ -209,7 +210,7 @@ class InstructionEncoding {
   SmallVector<OperandInfo, 16> Operands;
 
 public:
-  InstructionEncoding(const Record *EncodingDef,
+  InstructionEncoding(const CodeGenTarget &Target, const Record *EncodingDef,
                       const CodeGenInstruction *Inst);
 
   /// Returns the Record this encoding originates from.
@@ -259,8 +260,8 @@ class InstructionEncoding {
   void parseVarLenEncoding(const VarLenInst &VLI);
   void parseFixedLenEncoding(const BitsInit &RecordInstBits);
 
-  void parseVarLenOperands(const VarLenInst &VLI);
-  void parseFixedLenOperands(const BitsInit &Bits);
+  void parseVarLenOperands(const CodeGenTarget &Target, const VarLenInst &VLI);
+  void parseFixedLenOperands(const CodeGenTarget &Target, const BitsInit &Bits);
 };
 
 /// Sorting predicate to sort encoding IDs by encoding width.
@@ -371,6 +372,8 @@ class DecoderEmitter {
                          ArrayRef<unsigned> InstrLen) const;
   void emitPredicateFunction(formatted_raw_ostream &OS,
                              PredicateSet &Predicates) const;
+
+  void emitRegClassByHwModeDecoders(formatted_raw_ostream &OS) const;
   void emitDecoderFunction(formatted_raw_ostream &OS,
                            const DecoderSet &Decoders,
                            unsigned BucketBitWidth) const;
@@ -940,9 +943,66 @@ void DecoderEmitter::emitPredicateFunction(formatted_raw_ostream &OS,
   OS << "}\n\n";
 }
 
+static std::pair<std::string, bool>
+findOperandDecoderMethod(const CodeGenTarget &Target, const Record *Record);
+
+/// Emit a default implementation of a decoder for all RegClassByHwModes which
+/// do not have an explicit DecoderMethodSet, which dispatches over the decoder
+/// methods for the member classes.
+void DecoderEmitter::emitRegClassByHwModeDecoders(
+    formatted_raw_ostream &OS) const {
+  const CodeGenHwModes &CGH = Target.getHwModes();
+  if (CGH.getNumModeIds() == 1)
+    return;
+
+  ArrayRef<const Record *> RegClassByHwMode = Target.getAllRegClassByHwMode();
+  if (RegClassByHwMode.empty())
+    return;
+
+  const CodeGenRegBank &RegBank = Target.getRegBank();
+
+  for (const Record *ClassByHwMode : RegClassByHwMode) {
+    // Ignore cases that had an explicit DecoderMethod set.
+    if (!findOperandDecoderMethod(Target, ClassByHwMode).second)
+      continue;
+
+    const HwModeSelect &ModeSelect = CGH.getHwModeSelect(ClassByHwMode);
+
+    // Mips has a system where this is only used by compound operands with
+    // custom decoders, and we don't try to detect if this decoder is really
+    // needed.
+    OS << "[[maybe_unused]]\n";
+
+    OS << "static DecodeStatus Decode" << ClassByHwMode->getName()
+       << "RegClassByHwMode";
+    OS << R"((MCInst &Inst, unsigned Imm, uint64_t Addr, const MCDisassembler *Decoder) {
+  switch (Decoder->getSubtargetInfo().getHwMode(MCSubtargetInfo::HwMode_RegClass)) {
+)";
+    for (const HwModeSelect::PairType &P : ModeSelect.Items) {
+      const CodeGenRegisterClass *RegClass = RegBank.getRegClass(P.second);
+
+      OS << indent(2) << "case " << P.first << ": // "
+         << CGH.getModeName(P.first, /*IncludeDefault=*/true) << '\n'
+         << indent(4) << "return "
+         << findOperandDecoderMethod(Target, RegClass->getDef()).first
+         << "(Inst, Imm, Addr, Decoder);\n";
+    }
+    OS << indent(2) << R"(default:
+    llvm_unreachable("no decoder for hwmode");
+  }
+}
+
+)";
+  }
+
+  OS << '\n';
+}
+
 void DecoderEmitter::emitDecoderFunction(formatted_raw_ostream &OS,
                                          const DecoderSet &Decoders,
                                          unsigned BucketBitWidth) const {
+  emitRegClassByHwModeDecoders(OS);
+
   // The decoder function is just a big switch statement or a table of function
   // pointers based on the input decoder index.
 
@@ -1684,7 +1744,9 @@ void DecoderTableBuilder::emitTableEntries(const FilterChooser &FC) const {
   }
 }
 
-static std::string findOperandDecoderMethod(const Record *Record) {
+/// If this is explictly set value, return true for second.
+static std::pair<std::string, bool>
+findOperandDecoderMethod(const CodeGenTarget &Target, const Record *Record) {
   std::string Decoder;
 
   const RecordVal *DecoderString = Record->getValue("DecoderMethod");
@@ -1693,24 +1755,26 @@ static std::string findOperandDecoderMethod(const Record *Record) {
   if (String) {
     Decoder = String->getValue().str();
     if (!Decoder.empty())
-      return Decoder;
+      return {Decoder, false};
   }
 
   if (Record->isSubClassOf("RegisterOperand"))
     // Allows use of a DecoderMethod in referenced RegisterClass if set.
-    return findOperandDecoderMethod(Record->getValueAsDef("RegClass"));
+    return findOperandDecoderMethod(Target, Record->getValueAsDef("RegClass"));
 
   if (Record->isSubClassOf("RegisterClass")) {
     Decoder = "Decode" + Record->getName().str() + "RegisterClass";
+  } else if (Record->isSubClassOf("RegClassByHwMode")) {
+    Decoder = "Decode" + Record->getName().str() + "RegClassByHwMode";
   } else if (Record->isSubClassOf("PointerLikeRegClass")) {
     Decoder = "DecodePointerLikeRegClass" +
               utostr(Record->getValueAsInt("RegClassKind"));
   }
 
-  return Decoder;
+  return {Decoder, true};
 }
 
-OperandInfo getOpInfo(const Record *TypeRecord) {
+OperandInfo getOpInfo(const CodeGenTarget &Target, const Record *TypeRecord) {
   const RecordVal *HasCompleteDecoderVal =
       TypeRecord->getValue("hasCompleteDecoder");
   const BitInit *HasCompleteDecoderBit =
@@ -1720,7 +1784,8 @@ OperandInfo getOpInfo(const Record *TypeRecord) {
   bool HasCompleteDecoder =
       HasCompleteDecoderBit ? HasCompleteDecoderBit->getValue() : true;
 
-  return OperandInfo(findOperandDecoderMethod(TypeRecord), HasCompleteDecoder);
+  return OperandInfo(findOperandDecoderMethod(Target, TypeRecord).first,
+                     HasCompleteDecoder);
 }
 
 void InstructionEncoding::parseVarLenEncoding(const VarLenInst &VLI) {
@@ -1841,15 +1906,16 @@ void InstructionEncoding::parseFixedLenEncoding(
   }
 }
 
-void InstructionEncoding::parseVarLenOperands(const VarLenInst &VLI) {
+void InstructionEncoding::parseVarLenOperands(const CodeGenTarget &Target,
+                                              const VarLenInst &VLI) {
   SmallVector<int> TiedTo;
 
   for (const auto &[Idx, Op] : enumerate(Inst->Operands)) {
     if (Op.MIOperandInfo && Op.MIOperandInfo->getNumArgs() > 0)
       for (auto *Arg : Op.MIOperandInfo->getArgs())
-        Operands.push_back(getOpInfo(cast<DefInit>(Arg)->getDef()));
+        Operands.push_back(getOpInfo(Target, cast<DefInit>(Arg)->getDef()));
     else
-      Operands.push_back(getOpInfo(Op.Rec));
+      Operands.push_back(getOpInfo(Target, Op.Rec));
 
     int TiedReg = Op.getTiedRegister();
     TiedTo.push_back(-1);
@@ -1986,7 +2052,8 @@ static void addOneOperandFields(const Record *EncodingDef,
   }
 }
 
-void InstructionEncoding::parseFixedLenOperands(const BitsInit &Bits) {
+void InstructionEncoding::parseFixedLenOperands(const CodeGenTarget &Target,
+                                                const BitsInit &Bits) {
   // Search for tied operands, so that we can correctly instantiate
   // operands that are not explicitly represented in the encoding.
   std::map<StringRef, StringRef> TiedNames;
@@ -2012,7 +2079,7 @@ void InstructionEncoding::parseFixedLenOperands(const BitsInit &Bits) {
   for (const CGIOperandList::OperandInfo &Op : Inst->Operands) {
     // Lookup the decoder method and construct a new OperandInfo to hold our
     // result.
-    OperandInfo OpInfo = getOpInfo(Op.Rec);
+    OperandInfo OpInfo = getOpInfo(Target, Op.Rec);
 
     // If we have named sub-operands...
     if (Op.MIOperandInfo && !Op.SubOpNames[0].empty()) {
@@ -2031,7 +2098,7 @@ void InstructionEncoding::parseFixedLenOperands(const BitsInit &Bits) {
       for (auto [SubOpName, SubOp] :
            zip_equal(Op.SubOpNames, Op.MIOperandInfo->getArgs())) {
         const Record *SubOpRec = cast<DefInit>(SubOp)->getDef();
-        OperandInfo SubOpInfo = getOpInfo(SubOpRec);
+        OperandInfo SubOpInfo = getOpInfo(Target, SubOpRec);
         addOneOperandFields(EncodingDef, Bits, TiedNames, SubOpRec, SubOpName,
                             SubOpInfo);
         Operands.push_back(std::move(SubOpInfo));
@@ -2059,7 +2126,8 @@ void InstructionEncoding::parseFixedLenOperands(const BitsInit &Bits) {
   }
 }
 
-InstructionEncoding::InstructionEncoding(const Record *EncodingDef,
+InstructionEncoding::InstructionEncoding(const CodeGenTarget &Target,
+                                         const Record *EncodingDef,
                                          const CodeGenInstruction *Inst)
     : EncodingDef(EncodingDef), Inst(Inst) {
   const Record *InstDef = Inst->TheDef;
@@ -2080,13 +2148,13 @@ InstructionEncoding::InstructionEncoding(const Record *EncodingDef,
     parseVarLenEncoding(VLI);
     // If the encoding has a custom decoder, don't bother parsing the operands.
     if (DecoderMethod.empty())
-      parseVarLenOperands(VLI);
+      parseVarLenOperands(Target, VLI);
   } else {
     const auto *BI = cast<BitsInit>(InstField->getValue());
     parseFixedLenEncoding(*BI);
     // If the encoding has a custom decoder, don't bother parsing the operands.
     if (DecoderMethod.empty())
-      parseFixedLenOperands(*BI);
+      parseFixedLenOperands(Target, *BI);
   }
 
   if (DecoderMethod.empty()) {
@@ -2454,7 +2522,7 @@ void DecoderEmitter::parseInstructionEncodings() {
           continue;
         }
         unsigned EncodingID = Encodings.size();
-        Encodings.emplace_back(EncodingDef, Inst);
+        Encodings.emplace_back(Target, EncodingDef, Inst);
         EncodingIDsByHwMode[HwModeID].push_back(EncodingID);
       }
       continue; // Ignore encoding specified by Instruction itself.
@@ -2466,7 +2534,7 @@ void DecoderEmitter::parseInstructionEncodings() {
     }
 
     unsigned EncodingID = Encodings.size();
-    Encodings.emplace_back(InstDef, Inst);
+    Encodings.emplace_back(Target, InstDef, Inst);
 
     // This instruction is encoded the same on all HwModes.
     // According to user needs, add it to all, some, or only the default HwMode.
@@ -2489,7 +2557,8 @@ void DecoderEmitter::parseInstructionEncodings() {
       continue;
     }
     unsigned EncodingID = Encodings.size();
-    Encodings.emplace_back(EncodingDef, &Target.getInstruction(InstDef));
+    Encodings.emplace_back(Target, EncodingDef,
+                           &Target.getInstruction(InstDef));
     EncodingIDsByHwMode[DefaultMode].push_back(EncodingID);
   }
 
diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp
index 7ae6107b98554..359383f79b9bb 100644
--- a/llvm/utils/TableGen/GlobalISelEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp
@@ -267,13 +267,28 @@ static Error isTrivialOperatorNode(const TreePatternNode &N) {
   return failedImport(Explanation);
 }
 
-static const Record *getInitValueAsRegClass(const Init *V) {
+static const Record *getInitValueAsRegClass(const CodeGenTarget &Target,
+                                            const Init *V) {
   if (const DefInit *VDefInit = dyn_cast<DefInit>(V)) {
-    if (VDefInit->getDef()->isSubClassOf("RegisterOperand"))
-      return VDefInit->getDef()->getValueAsDef("RegClass");
-    if (VDefInit->getDef()->isSubClassOf("RegisterClass"))
-      return VDefInit->getDef();
+    const Record *RegClass = VDefInit->getDef();
+    if (RegClass->isSubClassOf("RegisterOperand"))
+      RegClass = RegClass->getValueAsDef("RegClass");
+
+    if (RegClass->isSubClassOf("RegisterClass"))
+      return RegClass;
+
+    // FIXME: We should figure out the hwmode and dispatch. But this interface
+    // is broken, we should be returning a register class. The expected uses
+    // will use the same RegBanks in all modes.
+    if (RegClass->isSubClassOf("RegClassByHwMode")) {
+      const HwModeSelect &ModeSelect =
+          Target.getHwModes().getHwModeSelect(RegClass);
+      if (ModeSelect.Items.empty())
+        return nullptr;
+      return ModeSelect.Items.front().second;
+    }
   }
+
   return nullptr;
 }
 
@@ -475,6 +490,9 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter {
   const CodeGenRegisterClass *
   inferRegClassFromPattern(const TreePatternNode &N) const;
 
+  const CodeGenRegisterClass *
+  inferRegClassFromRegisterClassLike(const Record *RecClassLike) const;
+
   const CodeGenRegisterClass *
   inferRegClassFromInstructionPattern(const TreePatternNode &N,
                                       unsigned ResIdx) const;
@@ -1141,9 +1159,10 @@ Error GlobalISelEmitter::importChildMatcher(
 
     // Check for register classes.
     if (ChildRec->isSubClassOf("RegisterClass") ||
-        ChildRec->isSubClassOf("RegisterOperand")) {
-      OM.addPredicate<RegisterBankOperandMatcher>(
-          Target.getRegisterClass(getInitValueAsRegClass(ChildDefInit)));
+        ChildRec->isSubClassOf("RegisterOperand") ||
+        ChildRec->isSubClassOf("RegClassByHwMode")) {
+      OM.addPredicate<RegisterBankOperandMatcher>(Target.getRegisterClass(
+          getInitValueAsRegClass(Target, ChildDefInit)));
       return Error::success();
     }
 
@@ -1285,7 +1304,7 @@ Error GlobalISelEmitter::importNamedNodeRenderer(
 
     // TODO: All special cases are handled above. Remove this check and add
     //   CopyRenderer unconditionally.
-    if (R->isSubClassOf("RegisterClass") ||
+    if (R->isSubClassOf("RegisterClassLike") ||
         R->isSubClassOf("RegisterOperand") || R->isSubClassOf("ValueType")) {
       MIBuilder.addRenderer<CopyRenderer>(NodeName);
       return Error::success();
@@ -1640,7 +1659,8 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderers(
     }
 
     // If this is a source operand, this is just a subregister copy.
-    const Record *RCDef = getInitValueAsRegClass(ValChild.getLeafValue());
+    const Record *RCDef =
+        getInitValueAsRegClass(Target, ValChild.getLeafValue());
     if (!RCDef)
       return failedImport("EXTRACT_SUBREG child #0 could not "
                           "be coerced to a register class");
@@ -1672,7 +1692,7 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderers(
       return failedImport("REG_SEQUENCE child #0 is not a leaf");
 
     const Record *RCDef =
-        getInitValueAsRegClass(Dst.getChild(0).getLeafValue());
+        getInitValueAsRegClass(Target, Dst.getChild(0).getLeafValue());
     if (!RCDef)
       return failedImport("REG_SEQUENCE child #0 could not "
                           "be coerced to a register class");
@@ -1791,7 +1811,7 @@ Error GlobalISelEmitter::constrainOperands(action_iterator InsertPt,
     // COPY_TO_REGCLASS does not provide operand constraints itself but the
     // result is constrained to the class given by the second child.
     const Record *DstIOpRec =
-        getInitValueAsRegClass(Dst.getChild(1).getLeafValue());
+        getInitValueAsRegClass(Target, Dst.getChild(1).getLeafValue());
 
     if (DstIOpRec == nullptr)
       return failedImport("COPY_TO_REGCLASS operand #1 isn't a register class");
@@ -1897,7 +1917,7 @@ Error GlobalISelEmitter::constrainOperands(action_iterator InsertPt,
 const CodeGenRegisterClass *
 GlobalISelEmitter::getRegClassFromLeaf(const TreePatternNode &Leaf) const {
   assert(Leaf.isLeaf() && "Expected leaf?");
-  const Record *RCRec = getInitValueAsRegClass(Leaf.getLeafValue());
+  const Record *RCRec = getInitValueAsRegClass(Target, Leaf.getLeafValue());
   if (!RCRec)
     return nullptr;
   return CGRegs.getRegClass(RCRec);
@@ -1926,6 +1946,26 @@ GlobalISelEmitter::inferRegClassFromPattern(const TreePatternNode &N) const {
   return inferRegClassFromInstructionPattern(N, /*ResIdx=*/0);
 }
 
+const CodeGenRegisterClass *
+GlobalISelEmitter::inferRegClassFromRegisterClassLike(
+    const Record *RegClassDef) const {
+  if (RegClassDef->isSubClassOf("RegClassByHwMode")) {
+    // TODO: We only are trying to match the regbank, which we assume is the
+    // same across modes, but we are just picking one class and assuming they
+    // all have the same bank.
+    //
+    // We should verify there is a common regbank among the possible classes,
+    // and return that instead of a concrete class.
+    const HwModeSelect &ModeSelect =
+        Target.getHwModes().getHwModeSelect(RegClassDef);
+    if (ModeSelect.Items.empty())
+      return nullptr;
+    return Target.getRegBank().getRegClass(ModeSelect.Items.front().second);
+  }
+
+  return &Target.getRegisterClass(RegClassDef);
+}
+
 const CodeGenRegisterClass *
 GlobalISelEmitter::inferRegClassFromInstructionPattern(const TreePatternNode &N,
                                                        unsigned ResIdx) const {
@@ -1997,8 +2037,14 @@ GlobalISelEmitter::inferRegClassFromInstructionPattern(const TreePatternNode &N,
   // from.
   const auto &DstIOperand = Inst.Operands[ResIdx];
   const Record *DstIOpRec = DstIOperand.Rec;
-  if (DstIOpRec->isSubClassOf("RegisterOperand"))
-    return &Target.getRegisterClass(DstIOpRec->getValueAsDef("RegClass"));
+
+  if (DstIOpRec->isSubClassOf("RegisterOperand")) {
+    const Record *RegClassDef = DstIOpRec->getValueAsDef("RegClass");
+    return inferRegClassFromRegisterClassLike(RegClassDef);
+  }
+
+  if (DstIOpRec->isSubClassOf("RegClassByHwMode"))
+    return inferRegClassFromRegisterClassLike(DstIOpRec);
 
   if (DstIOpRec->isSubClassOf("RegisterClass"))
     return &Target.getRegisterClass(DstIOpRec);
@@ -2111,7 +2157,8 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
   InstructionMatcher &InsnMatcher = InsnMatcherOrError.get();
 
   if (Dst.isLeaf()) {
-    if (const Record *RCDef = getInitValueAsRegClass(Dst.getLeafValue())) {
+    if (const Record *RCDef =
+            getInitValueAsRegClass(Target, Dst.getLeafValue())) {
       const CodeGenRegisterClass &RC = Target.getRegisterClass(RCDef);
 
       // We need to replace the def and all its uses with the specified
diff --git a/llvm/utils/TableGen/InstrInfoEmitter.cpp b/llvm/utils/TableGen/InstrInfoEmitter.cpp
index 0174475e70602..9ed81c6f6c9e6 100644
--- a/llvm/utils/TableGen/InstrInfoEmitter.cpp
+++ b/llvm/utils/TableGen/InstrInfoEmitter.cpp
@@ -14,6 +14,7 @@
 #include "Basic/SequenceToOffsetTable.h"
 #include "Common/CodeGenDAGPatterns.h"
 #include "Common/CodeGenInstruction.h"
+#include "Common/CodeGenRegisters.h"
 #include "Common/CodeGenSchedule.h"
 #include "Common/CodeGenTarget.h"
 #include "Common/PredicateExpander.h"
@@ -144,7 +145,11 @@ InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) {
 
       if (OpR->isSubClassOf("RegisterOperand"))
         OpR = OpR->getValueAsDef("RegClass");
-      if (OpR->isSubClassOf("RegisterClass"))
+
+      if (OpR->isSubClassOf("RegClassByHwMode")) {
+        Res += OpR->getName();
+        Res += ", ";
+      } else if (OpR->isSubClassOf("RegisterClass"))
         Res += getQualifiedName(OpR) + "RegClassID, ";
       else if (OpR->isSubClassOf("PointerLikeRegClass"))
         Res += utostr(OpR->getValueAsInt("RegClassKind")) + ", ";
@@ -155,9 +160,12 @@ InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) {
       // Fill in applicable flags.
       Res += "0";
 
-      // Ptr value whose register class is resolved via callback.
-      if (OpR->isSubClassOf("PointerLikeRegClass"))
+      if (OpR->isSubClassOf("RegClassByHwMode")) {
+        Res += "|(1<<MCOI::LookupRegClassByHwMode)";
+      } else if (OpR->isSubClassOf("PointerLikeRegClass")) {
+        // Ptr value whose register class is resolved via callback.
         Res += "|(1<<MCOI::LookupPtrRegClass)";
+      }
 
       // Predicate operands.  Check to see if the original unexpanded operand
       // was of type PredicateOp.
@@ -925,6 +933,14 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
 
   OS << "#if defined(GET_INSTRINFO_MC_DESC) || "
         "defined(GET_INSTRINFO_CTOR_DTOR)\n";
+
+  OS << "namespace {\n";
+  OS << "enum RegClassByHwModeUses : uint16_t {\n";
+  for (const Record *ClassByHwMode : Target.getAllRegClassByHwMode())
+    OS << "  " << ClassByHwMode->getName() << ",\n";
+  OS << "};\n";
+  OS << "}\n";
+
   OS << "namespace llvm {\n\n";
 
   OS << "struct " << TargetName << "InstrTable {\n";
@@ -986,6 +1002,16 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
   InstrNames.layout();
   InstrNames.emitStringLiteralDef(OS, Twine("extern const char ") + TargetName +
                                           "InstrNameData[]");
+  const CodeGenRegBank &RegBank = Target.getRegBank();
+  const CodeGenHwModes &CGH = Target.getHwModes();
+  unsigned NumModes = CGH.getNumModeIds();
+  ArrayRef<const Record *> RegClassByHwMode = Target.getAllRegClassByHwMode();
+  unsigned NumClassesByHwMode = RegClassByHwMode.size();
+
+  if (NumClassesByHwMode != 0) {
+    OS << "extern const int16_t " << TargetName << "RegClassByHwModeTables["
+       << NumModes << "][" << NumClassesByHwMode << "];\n";
+  }
 
   OS << "extern const unsigned " << TargetName << "InstrNameIndices[] = {";
   Num = 0;
@@ -1044,6 +1070,40 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
 
   // MCInstrInfo initialization routine.
   Timer.startTimer("Emit initialization routine");
+
+  if (NumClassesByHwMode != 0) {
+    OS << "const int16_t " << TargetName << "RegClassByHwModeTables["
+       << NumModes << "][" << NumClassesByHwMode << "] = {\n";
+
+    for (unsigned M = 0; M < NumModes; ++M) {
+      OS << "  { // " << CGH.getModeName(M, /*IncludeDefault=*/true) << '\n';
+      for (unsigned I = 0; I != NumClassesByHwMode; ++I) {
+        const Record *Class = RegClassByHwMode[I];
+        const HwModeSelect &ModeSelect = CGH.getHwModeSelect(Class);
+
+        bool Seen = false;
+        for (const HwModeSelect::PairType &P : ModeSelect.Items) {
+          if (P.first == M) {
+            const CodeGenRegisterClass *RegClass =
+                RegBank.getRegClass(P.second);
+            OS << indent(4) << RegClass->getQualifiedIdName() << ",\n";
+            Seen = true;
+            break;
+          }
+        }
+
+        // If a RegClassByHwMode doesn't have an entry corresponding to a mode,
+        // pad with default register class.
+        if (!Seen)
+          OS << indent(4) << "-1, // Missing mode entry\n";
+      }
+
+      OS << "  },\n";
+    }
+
+    OS << "};\n\n";
+  }
+
   OS << "static inline void Init" << TargetName
      << "MCInstrInfo(MCInstrInfo *II) {\n";
   OS << "  II->InitMCInstrInfo(" << TargetName << "Descs.Insts, " << TargetName
@@ -1056,7 +1116,15 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
     OS << TargetName << "InstrComplexDeprecationInfos, ";
   else
     OS << "nullptr, ";
-  OS << NumberedInstructions.size() << ");\n}\n\n";
+  OS << NumberedInstructions.size() << ", ";
+
+  if (NumClassesByHwMode != 0) {
+    OS << '&' << TargetName << "RegClassByHwModeTables[0][0], "
+       << NumClassesByHwMode;
+  } else
+    OS << "nullptr, 0";
+
+  OS << ");\n}\n\n";
 
   OS << "} // end namespace llvm\n";
 
@@ -1098,6 +1166,12 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
      << "Descs;\n";
   OS << "extern const unsigned " << TargetName << "InstrNameIndices[];\n";
   OS << "extern const char " << TargetName << "InstrNameData[];\n";
+
+  if (NumClassesByHwMode != 0) {
+    OS << "extern const int16_t " << TargetName << "RegClassByHwModeTables["
+       << NumModes << "][" << NumClassesByHwMode << "];\n";
+  }
+
   if (HasDeprecationFeatures)
     OS << "extern const uint8_t " << TargetName
        << "InstrDeprecationFeatures[];\n";
@@ -1108,7 +1182,13 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
      << "(const TargetSubtargetInfo &STI, unsigned CFSetupOpcode, unsigned "
         "CFDestroyOpcode, unsigned CatchRetOpcode, unsigned ReturnOpcode)\n"
      << "  : TargetInstrInfo(CFSetupOpcode, CFDestroyOpcode, CatchRetOpcode, "
-        "ReturnOpcode) {\n"
+        "ReturnOpcode";
+  if (NumClassesByHwMode != 0)
+    OS << ", " << TargetName
+       << "RegClassByHwModeTables[STI.getHwMode(MCSubtargetInfo::HwMode_"
+          "RegClass)]";
+
+  OS << ") {\n"
      << "  InitMCInstrInfo(" << TargetName << "Descs.Insts, " << TargetName
      << "InstrNameIndices, " << TargetName << "InstrNameData, ";
   if (HasDeprecationFeatures)
@@ -1119,8 +1199,16 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
     OS << TargetName << "InstrComplexDeprecationInfos, ";
   else
     OS << "nullptr, ";
-  OS << NumberedInstructions.size() << ");\n}\n";
-  OS << "} // end namespace llvm\n";
+  OS << NumberedInstructions.size();
+
+  if (NumClassesByHwMode != 0) {
+    OS << ", &" << TargetName << "RegClassByHwModeTables[0][0], "
+       << NumClassesByHwMode;
+  }
+
+  OS << ");\n"
+        "}\n"
+        "} // end namespace llvm\n";
 
   OS << "#endif // GET_INSTRINFO_CTOR_DTOR\n\n";
 
diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index e218927621dd3..b5ffbedc41b24 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -1792,7 +1792,8 @@ void SubtargetEmitter::emitHwModeCheck(const std::string &ClassName,
       if (P.second->isSubClassOf("ValueType")) {
         ValueTypeModes |= (1 << (P.first - 1));
       } else if (P.second->isSubClassOf("RegInfo") ||
-                 P.second->isSubClassOf("SubRegRange")) {
+                 P.second->isSubClassOf("SubRegRange") ||
+                 P.second->isSubClassOf("RegisterClassLike")) {
         RegInfoModes |= (1 << (P.first - 1));
       } else if (P.second->isSubClassOf("InstructionEncoding")) {
         EncodingInfoModes |= (1 << (P.first - 1));
@@ -1849,6 +1850,7 @@ void SubtargetEmitter::emitHwModeCheck(const std::string &ClassName,
   OS << "  case HwMode_Default:\n    return llvm::countr_zero(Modes) + 1;\n";
   HandlePerMode("ValueType", ValueTypeModes);
   HandlePerMode("RegInfo", RegInfoModes);
+  HandlePerMode("RegClass", RegInfoModes);
   HandlePerMode("EncodingInfo", EncodingInfoModes);
   OS << "  }\n";
   OS << "  llvm_unreachable(\"unexpected HwModeType\");\n"



More information about the llvm-commits mailing list