[llvm] [TableGen] Introduce RegisterByHwMode (PR #175227)

Alexander Richardson via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 19 22:26:40 PST 2026


https://github.com/arichardson updated https://github.com/llvm/llvm-project/pull/175227

>From 6d24d45414c8342db11b3fbd892235f6654e8f49 Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Fri, 9 Jan 2026 11:37:57 -0800
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
 =?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1
---
 llvm/include/llvm/Target/Target.td            |  10 +
 .../TableGen/Common/RegisterByHwModeCommon.td |  92 +++++
 llvm/test/TableGen/RegClassByHwModeAlias.td   |  41 ++-
 .../TableGen/RegClassByHwModeCompressPat.td   |  58 +++-
 llvm/test/TableGen/RegClassByHwModeErrors.td  |   3 +-
 llvm/test/TableGen/RegisterByHwMode.td        | 316 ++++++++++++++++++
 llvm/test/TableGen/RegisterByHwModeErrors.td  |  69 ++++
 llvm/utils/TableGen/AsmMatcherEmitter.cpp     |  21 +-
 llvm/utils/TableGen/AsmWriterEmitter.cpp      |  45 ++-
 .../TableGen/Common/CodeGenDAGPatterns.cpp    |   2 +-
 .../TableGen/Common/CodeGenInstAlias.cpp      |   6 +-
 .../TableGen/Common/CodeGenRegisters.cpp      |  31 ++
 llvm/utils/TableGen/Common/CodeGenRegisters.h |   7 +
 llvm/utils/TableGen/Common/InfoByHwMode.cpp   |  53 ++-
 llvm/utils/TableGen/Common/InfoByHwMode.h     |  14 +-
 llvm/utils/TableGen/CompressInstEmitter.cpp   |  39 ++-
 llvm/utils/TableGen/DAGISelMatcherGen.cpp     |   4 +
 llvm/utils/TableGen/PseudoLoweringEmitter.cpp |  11 +-
 llvm/utils/TableGen/SubtargetEmitter.cpp      |   1 +
 19 files changed, 778 insertions(+), 45 deletions(-)
 create mode 100644 llvm/test/TableGen/Common/RegisterByHwModeCommon.td
 create mode 100644 llvm/test/TableGen/RegisterByHwMode.td
 create mode 100644 llvm/test/TableGen/RegisterByHwModeErrors.td

diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td
index 315de55b75510..61388c4902eb4 100644
--- a/llvm/include/llvm/Target/Target.td
+++ b/llvm/include/llvm/Target/Target.td
@@ -1078,6 +1078,16 @@ class RegisterOperand<RegisterClassLike regclass, string pm = "printOperand">
   Register GIZeroRegister = ?;
 }
 
+/// RegisterByHwMode - Useful for InstAliases with a hardcoded register operand
+/// where the operand type is a RegClassByHwMode.
+class RegisterByHwMode<RegisterClassLike RegClass, list<HwMode> Modes,
+                       list<Register> Registers>
+    : HwModeSelect<Modes, !size(Registers)>, RegisterOperand<RegClass> {
+  list<Register> Objects = Registers;
+  // Note: No assertions to validate the registers against the
+  // register class here, this is done inside llvm-tblgen.
+}
+
 let OperandType = "OPERAND_IMMEDIATE" in {
 def i1imm  : Operand<i1>;
 def i8imm  : Operand<i8>;
diff --git a/llvm/test/TableGen/Common/RegisterByHwModeCommon.td b/llvm/test/TableGen/Common/RegisterByHwModeCommon.td
new file mode 100644
index 0000000000000..57ec5b2d616c8
--- /dev/null
+++ b/llvm/test/TableGen/Common/RegisterByHwModeCommon.td
@@ -0,0 +1,92 @@
+include "llvm/Target/Target.td"
+
+/// A minimal reduced version of the RISC-V RVY register variants where pointers
+/// use either Xn or Xn_Y registers depending on CapMode and 64-bit predicates.
+/// Define HWModes for the full cross-product here since we can only match one
+/// HWMode at any given time.
+def Is32Bit : Predicate<"!Subtarget->is64Bit()">;
+def Is64Bit : Predicate<"Subtarget->is64Bit()">;
+def UseYRegForPtr : Predicate<"Subtarget->useYRegForPtr()">;
+def UseXRegForPtr : Predicate<"!Subtarget->useYRegForPtr()">;
+defvar XPtr32 = DefaultMode;
+def XPtr64 : HwMode<[Is64Bit, UseXRegForPtr]>;
+def YPtr32 : HwMode<[Is32Bit, UseYRegForPtr]>;
+def YPtr64 : HwMode<[Is64Bit, UseYRegForPtr]>;
+
+class MyReg<string n> : Register<n> {
+  let Namespace = "MyTarget";
+}
+
+def X0 : MyReg<"x0">;
+def X1 : MyReg<"x1">;
+def X2 : MyReg<"x2">;
+def X3 : MyReg<"x3">;
+
+def Y0 : MyReg<"y0">;
+def Y1 : MyReg<"y1">;
+def Y2 : MyReg<"y2">;
+def Y3 : MyReg<"y3">;
+
+def XLenVT : ValueTypeByHwMode<[XPtr32, XPtr64, YPtr32, YPtr64],
+                               [i32,    i64,    i32,    i64]>;
+def YLenVT : ValueTypeByHwMode<[XPtr32, XPtr64, YPtr32, YPtr64],
+                               [c64,    c128,   c64,    c128]>;
+def PtrVT : ValueTypeByHwMode<[XPtr32, XPtr64, YPtr32, YPtr64],
+                              [XLenVT, XLenVT, YLenVT, YLenVT]>;
+defvar RegInfo32 = RegInfo<32,32,32>;
+defvar RegInfo64 = RegInfo<64,64,64>;
+def XLenRI : RegInfoByHwMode<[XPtr32,    XPtr64,    YPtr32,    YPtr64],
+                             [RegInfo32, RegInfo32, RegInfo64, RegInfo64]>;
+def XRegs : RegisterClass<"MyTarget", [XLenVT], 32, (add X0, X1, X2, X3)> {
+  let RegInfos = XLenRI;  // Needed to determine size of registers
+}
+defvar RegInfo128 = RegInfo<128,128,128>;
+def YLenRI : RegInfoByHwMode<[XPtr32,    XPtr64,    YPtr32,     YPtr64],
+                             [RegInfo64, RegInfo64, RegInfo128, RegInfo128]>;
+
+def YRegs : RegisterClass<"MyTarget", [YLenVT], 64, (add Y0, Y1, Y2, Y3)> {
+  let RegInfos = YLenRI;  // Needed to determine size of registers
+}
+def PtrRC : RegClassByHwMode<[XPtr32, XPtr64, YPtr32, YPtr64],
+                             [XRegs,  XRegs,  YRegs,  YRegs]>;
+
+def PtrRegOperand : RegisterOperand<PtrRC>;
+
+def NullReg : RegisterByHwMode<PtrRC, [XPtr32, XPtr64, YPtr32, YPtr64],
+                                      [X0,     X0,     Y0,     Y0]>;
+
+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 TEST_XREG : TestInstruction {
+  let OutOperandList = (outs XRegs:$dst);
+  let InOperandList = (ins XRegs:$src);
+  let AsmString = "test_x $dst, $src";
+  let opcode = 0;
+}
+def TEST_YREG : TestInstruction {
+  let OutOperandList = (outs YRegs:$dst);
+  let InOperandList = (ins YRegs:$src);
+  let AsmString = "test_y $dst, $src";
+  let opcode = 1;
+}
+def TEST_PTRREG : TestInstruction {
+  let OutOperandList = (outs PtrRegOperand:$dst);
+  let InOperandList = (ins PtrRegOperand:$src);
+  let AsmString = "test_ptr $dst, $src";
+  let opcode = 2;
+}
\ No newline at end of file
diff --git a/llvm/test/TableGen/RegClassByHwModeAlias.td b/llvm/test/TableGen/RegClassByHwModeAlias.td
index 726bd3a0a5a49..09d57974a70b3 100644
--- a/llvm/test/TableGen/RegClassByHwModeAlias.td
+++ b/llvm/test/TableGen/RegClassByHwModeAlias.td
@@ -11,6 +11,7 @@ def EvenXRegs : RegisterClass<"MyTarget", [i64], 64, (add X0, X2, X4, X6)>;
 def EvenYRegs : RegisterClass<"MyTarget", [i64], 64, (add Y0, Y2, Y4, Y6)>;
 def PtrRC : RegClassByHwMode<[PtrX, PtrY], [XRegs, YRegs]>;
 def EvenPtrRC : RegClassByHwMode<[PtrX, PtrY], [EvenXRegs, EvenYRegs]>;
+def NullReg : RegisterByHwMode<PtrRC, [PtrX, PtrY], [X0, Y0]>;
 
 def TEST_XREG : TestInstruction {
   let OutOperandList = (outs XRegs:$dst);
@@ -28,21 +29,49 @@ def TEST_PTR : TestInstruction {
 def MY_T_X : InstAlias<"t_x $src", (TEST_XREG X0, XRegs:$src)>;
 def MY_T_X_EVEN : InstAlias<"t_x.even $src", (TEST_XREG EvenXRegs:$dst, EvenXRegs:$src)>;
 
-// TODO: Can't use a fixed register for this instruction, would need RegisterByHwMode.
-// def MY_T_PTR : InstAlias<"t_ptr $src", (TEST_PTR X0, XRegs:$src)>;
+def MY_T_PTR : InstAlias<"t_ptr $src", (TEST_PTR NullReg, PtrRC:$src)>;
 def MY_T_PTR_EVEN : InstAlias<"t_ptr.even $src", (TEST_PTR EvenPtrRC:$dst, EvenPtrRC:$src)>;
 
 // CHECK-LABEL: static const AliasPatternCond Conds[] = {
-// CHECK-NEXT:    // (TEST_PTR EvenPtrRC:$dst, EvenPtrRC:$src) - 0
+// CHECK-NEXT:    // (TEST_PTR NullReg, PtrRC:$src) - 0
+// CHECK-NEXT:    {AliasPatternCond::K_Custom, 1/*NullReg*/},
+// CHECK-NEXT:    {AliasPatternCond::K_RegClassByHwMode, MyTarget::PtrRC},
+// CHECK-NEXT:    // (TEST_PTR EvenPtrRC:$dst, EvenPtrRC:$src) - 2
 // CHECK-NEXT:    {AliasPatternCond::K_RegClassByHwMode, MyTarget::EvenPtrRC},
 // CHECK-NEXT:    {AliasPatternCond::K_RegClassByHwMode, MyTarget::EvenPtrRC},
-// CHECK-NEXT:    // (TEST_XREG X0, XRegs:$src) - 2
+// CHECK-NEXT:    // (TEST_XREG X0, XRegs:$src) - 4
 // CHECK-NEXT:    {AliasPatternCond::K_Reg, MyTarget::X0},
 // CHECK-NEXT:    {AliasPatternCond::K_RegClass, MyTarget::XRegsRegClassID},
-// CHECK-NEXT:    // (TEST_XREG EvenXRegs:$dst, EvenXRegs:$src) - 4
+// CHECK-NEXT:    // (TEST_XREG EvenXRegs:$dst, EvenXRegs:$src) - 6
 // CHECK-NEXT:    {AliasPatternCond::K_RegClass, MyTarget::EvenXRegsRegClassID},
 // CHECK-NEXT:    {AliasPatternCond::K_RegClass, MyTarget::EvenXRegsRegClassID},
 // CHECK-NEXT:  };
 
+// CHECK-LABEL: static bool MyTargetInstPrinterValidateMCOperand(const MCOperand &MCOp,
+// CHECK-NEXT:                    const MCSubtargetInfo &STI,
+// CHECK-NEXT:                    unsigned PredicateIndex) {
+// CHECK-NEXT:    switch (PredicateIndex) {
+// CHECK-NEXT:    default:
+// CHECK-NEXT:      llvm_unreachable("Unknown MCOperandPredicate kind");
+// CHECK-NEXT:      break;
+// CHECK-NEXT:    case 1: {
+// CHECK-NEXT:      auto getNullReg = [](unsigned HwMode) {
+// CHECK-NEXT:        switch (HwMode) {
+// CHECK-NEXT:        case 0: return MyTarget::X0; // DefaultMode
+// CHECK-NEXT:        case 1: return MyTarget::Y0; // PtrY
+// CHECK-NEXT:        default: llvm_unreachable("Unhandled HwMode for Register NullReg");
+// CHECK-NEXT:        }
+// CHECK-NEXT:      };
+// CHECK-NEXT:      return MCOp.isReg() && MCOp.getReg() == getNullReg(STI.getHwMode(MCSubtargetInfo::HwMode_RegInfo));
+// CHECK-NEXT:    }
+// CHECK-NEXT:    }
+// CHECK-NEXT:  }
+
 def MyTargetISA : InstrInfo;
-def MyTarget : Target { let InstructionSet = MyTargetISA; }
+def MyTargetAsmWriter : AsmWriter {
+  int PassSubtarget = 1;
+}
+def MyTarget : Target {
+  let InstructionSet = MyTargetISA;
+  let AssemblyWriters = [MyTargetAsmWriter];
+}
diff --git a/llvm/test/TableGen/RegClassByHwModeCompressPat.td b/llvm/test/TableGen/RegClassByHwModeCompressPat.td
index d642584e52b37..f2b12aac9e008 100644
--- a/llvm/test/TableGen/RegClassByHwModeCompressPat.td
+++ b/llvm/test/TableGen/RegClassByHwModeCompressPat.td
@@ -6,7 +6,7 @@ def IsPtr64 : Predicate<"Subtarget->isPtr64()">;
 defvar Ptr32 = DefaultMode;
 def Ptr64 : HwMode<[IsPtr64]>;
 def PtrRC : RegClassByHwMode<[Ptr32, Ptr64], [XRegs, YRegs]>;
-
+def NullReg : RegisterByHwMode<PtrRC, [Ptr32, Ptr64], [X0, Y0]>;
 
 def X_MOV : TestInstruction {
   let OutOperandList = (outs XRegs:$dst);
@@ -74,9 +74,8 @@ def : CompressPat<(X_MOV XRegs:$dst, XRegs:$dst),
                   (X_MOV_TIED XRegs:$dst)>;
 def : CompressPat<(X_MOV XRegs:$dst, XRegs:$src),
                   (X_MOV_SMALL XRegs:$dst, XRegs:$src)>;
-// TODO: Should also be able to use a fixed register with RegClassByHwMode
-// def : CompressPat<(PTR_MOV PtrRC:$dst, X0),
-//                   (PTR_MOV_ZERO PtrRC:$dst)>;
+def : CompressPat<(PTR_MOV PtrRC:$dst, NullReg),
+                  (PTR_MOV_ZERO PtrRC:$dst)>;
 def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$dst),
                   (PTR_MOV_TIED PtrRC:$dst)>;
 def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$src),
@@ -89,6 +88,23 @@ def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$src),
 // CHECK-NEXT:   switch (MI.getOpcode()) {
 // CHECK-NEXT:   default: return false;
 // CHECK-NEXT:   case MyTarget::PTR_MOV: {
+// CHECK-NEXT:     if (MI.getOperand(1).isReg() &&
+// CHECK-NEXT:         (MI.getOperand(1).getReg() == [](unsigned HwMode) {
+// CHECK-NEXT:           switch (HwMode)
+// CHECK-NEXT:           case 0: return MyTarget::X0; // DefaultMode
+// CHECK-NEXT:           case 1: return MyTarget::Y0; // Ptr64
+// CHECK-NEXT:           default: llvm_unreachable("Unhandled HwMode for Register NullReg");
+// CHECK-NEXT:           }
+// CHECK-NEXT:         }(HwModeId)) &&
+// CHECK-NEXT:         MI.getOperand(0).isReg() &&
+// CHECK-NEXT:         MyTargetMCRegisterClasses[MyTargetRegClassByHwModeTables[HwModeId][MyTarget::PtrRC]].contains(MI.getOperand(0).getReg())) {
+// CHECK-NEXT:       // ptr_mov.zero $dst
+// CHECK-NEXT:       OutInst.setOpcode(MyTarget::PTR_MOV_ZERO);
+// CHECK-NEXT:       // Operand: dst
+// CHECK-NEXT:       OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT:       OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT:       return true;
+// CHECK-NEXT:     } // if
 // CHECK-NEXT:     if (MI.getOperand(1).isReg() && MI.getOperand(0).isReg() &&
 // CHECK-NEXT:         (MI.getOperand(1).getReg() == MI.getOperand(0).getReg()) &&
 // CHECK-NEXT:         MI.getOperand(1).isReg() &&
@@ -199,6 +215,26 @@ def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$src),
 // CHECK-NEXT:     } // if
 // CHECK-NEXT:     break;
 // CHECK-NEXT:   } // case PTR_MOV_TIED
+// CHECK-NEXT:   case MyTarget::PTR_MOV_ZERO: {
+// CHECK-NEXT:     if (MI.getOperand(0).isReg() &&
+// CHECK-NEXT:         MyTargetMCRegisterClasses[MyTargetRegClassByHwModeTables[HwModeId][MyTarget::PtrRC]].contains(MI.getOperand(0).getReg())) {
+// CHECK-NEXT:       // ptr_mov $dst, $src
+// CHECK-NEXT:       OutInst.setOpcode(MyTarget::PTR_MOV);
+// CHECK-NEXT:       // Operand: dst
+// CHECK-NEXT:       OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT:       // Operand: src
+// CHECK-NEXT:       OutInst.addOperand(MCOperand::createReg([](unsigned HwMode) {
+// CHECK-NEXT:           switch (HwMode) {
+// CHECK-NEXT:           case 0: return MyTarget::X0; // DefaultMode
+// CHECK-NEXT:           case 1: return MyTarget::Y0; // Ptr64
+// CHECK-NEXT:           default: llvm_unreachable("Unhandled HwMode for Register NullReg");
+// CHECK-NEXT:           }
+// CHECK-NEXT:         }(HwModeId)));
+// CHECK-NEXT:       OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT:       return true;
+// CHECK-NEXT:     } // if
+// CHECK-NEXT:     break;
+// CHECK-NEXT:   } // case PTR_MOV_ZERO
 // CHECK-NEXT:   case MyTarget::X_MOV_SMALL: {
 // CHECK-NEXT:     if (MI.getOperand(0).isReg() &&
 // CHECK-NEXT:         MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(0).getReg()) &&
@@ -273,6 +309,20 @@ def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$src),
 // CHECK-NEXT:       // Operand: src
 // CHECK-NEXT:       return true;
 // CHECK-NEXT:     } // if
+// CHECK-NEXT:     if (MI.getOperand(1).isReg() &&
+// CHECK-NEXT:         (MI.getOperand(1).getReg() == [](unsigned HwMode) {
+// CHECK-NEXT:           switch (HwMode)
+// CHECK-NEXT:           case 0: return MyTarget::X0; // DefaultMode
+// CHECK-NEXT:           case 1: return MyTarget::Y0; // Ptr64
+// CHECK-NEXT:           default: llvm_unreachable("Unhandled HwMode for Register NullReg");
+// CHECK-NEXT:           }
+// CHECK-NEXT:         }(HwModeId)) &&
+// CHECK-NEXT:         MI.getOperand(0).isReg() && MI.getOperand(0).getReg().isPhysical() &&
+// CHECK-NEXT:         MyTargetMCRegisterClasses[MyTargetRegClassByHwModeTables[HwModeId][MyTarget::PtrRC]].contains(MI.getOperand(0).getReg())) {
+// CHECK-NEXT:       // ptr_mov.zero $dst
+// CHECK-NEXT:       // Operand: dst
+// CHECK-NEXT:       return true;
+// CHECK-NEXT:     } // if
 // CHECK-NEXT:     break;
 // CHECK-NEXT:   } // case PTR_MOV
 // CHECK-NEXT:   case MyTarget::X_MOV: {
diff --git a/llvm/test/TableGen/RegClassByHwModeErrors.td b/llvm/test/TableGen/RegClassByHwModeErrors.td
index c7731312e28a6..833224074ac34 100644
--- a/llvm/test/TableGen/RegClassByHwModeErrors.td
+++ b/llvm/test/TableGen/RegClassByHwModeErrors.td
@@ -66,8 +66,7 @@ def PTR_ZERO_SMALL : TestInstruction {
 /// This should fail since X0 is not necessarily part of PtrRC.
 def : CompressPat<(PTR_MOV PtrRC:$dst, X0),
                   (PTR_ZERO_SMALL PtrRC:$dst)>;
-// CHECK: [[#@LINE-2]]:1: error: cannot resolve HwMode for PtrRC
-// CHECK: Common.td:7:5: note: PtrRC defined here
+// CHECK: [[#@LINE-2]]:1: error: Error in Dag '(PTR_MOV PtrRC:$dst, X0)': Register 'X0' is not in register class 'PtrRC'
 def MyTargetISA : InstrInfo;
 def MyTarget : Target { let InstructionSet = MyTargetISA; }
 
diff --git a/llvm/test/TableGen/RegisterByHwMode.td b/llvm/test/TableGen/RegisterByHwMode.td
new file mode 100644
index 0000000000000..5a128a3b6d849
--- /dev/null
+++ b/llvm/test/TableGen/RegisterByHwMode.td
@@ -0,0 +1,316 @@
+// RUN: llvm-tblgen --gen-asm-matcher -I %p/../../include -I %S %s -o - | FileCheck --check-prefix=ASMMATCHER %s
+// RUN: llvm-tblgen --gen-pseudo-lowering -I %p/../../include -I %S %s -o - | FileCheck --check-prefix=PSEUDO %s
+// RUN: llvm-tblgen --gen-subtarget -I %p/../../include -I %S %s -o - | FileCheck --check-prefix=SUBTARGET %s
+// RUN: llvm-tblgen --gen-instr-info -I %p/../../include -I %S %s -o - | FileCheck --check-prefix=INSTRINFO %s
+// RUN: llvm-tblgen --gen-asm-writer -I %p/../../include -I %S %s -o - | FileCheck --check-prefix=ASMWRITER %s
+/// Note: No impact on disassembler (handled by the alias expansion), so not tested here
+/// Note: DAGIsel is not supported yet
+// RUNTODO: llvm-tblgen --gen-dag-isel -I %p/../../include -I %S %s -o -
+// RUNTODO: llvm-tblgen --gen-global-isel -I %p/../../include -I %S %s -o -
+
+
+// SUBTARGET-LABEL:  enum class MyTargetHwModeBits : unsigned {
+// SUBTARGET-NEXT:    DefaultMode = 0,
+// SUBTARGET-NEXT:    XPtr64 = (1 << 0),
+// SUBTARGET-NEXT:    YPtr32 = (1 << 1),
+// SUBTARGET-NEXT:    YPtr64 = (1 << 2),
+// SUBTARGET-EMPTY:
+// SUBTARGET-NEXT:    LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/YPtr64),
+// SUBTARGET-NEXT:  };
+// SUBTARGET-NEXT:  unsigned getHwModeSet() const override;
+// SUBTARGET-NEXT:  unsigned getHwMode(enum HwModeType type = HwMode_Default) const override;
+
+// SUBTARGET-LABEL: unsigned MyTargetGenSubtargetInfo::getHwModeSet() const {
+// SUBTARGET{LITERAL}:[[maybe_unused]] const auto *Subtarget =
+// SUBTARGET-NEXT:        static_cast<const MyTargetSubtarget *>(this);
+// SUBTARGET-NEXT:  // Collect HwModes and store them as a bit set.
+// SUBTARGET-NEXT:  unsigned Modes = 0;
+// SUBTARGET-NEXT:  if ((Subtarget->is64Bit()) && (!Subtarget->useYRegForPtr())) Modes |= (1 << 0);
+// SUBTARGET-NEXT:  if ((!Subtarget->is64Bit()) && (Subtarget->useYRegForPtr())) Modes |= (1 << 1);
+// SUBTARGET-NEXT:  if ((Subtarget->is64Bit()) && (Subtarget->useYRegForPtr())) Modes |= (1 << 2);
+// SUBTARGET-NEXT:  return Modes;
+// SUBTARGET-NEXT:}
+
+// INSTRINFO-LABEL: extern const MyTargetInstrTable MyTargetDescs
+// INSTRINFO:      { MyTarget::EvenPtrRC, 0|(1<<MCOI::LookupRegClassByHwMode), MCOI::OPERAND_REGISTER, 0 },
+// INSTRINFO-NEXT: { MyTarget::EvenXRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 },
+// INSTRINFO-NEXT: { MyTarget::PtrRC, 0|(1<<MCOI::LookupRegClassByHwMode), MCOI::OPERAND_REGISTER, 0 }, { MyTarget::PtrRC, 0|(1<<MCOI::LookupRegClassByHwMode), MCOI::OPERAND_REGISTER, 0 },
+// INSTRINFO-NEXT: { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 }, { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 },
+// INSTRINFO-NEXT: { MyTarget::YRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 }, { MyTarget::YRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 },
+
+// INSTRINFO-LABEL: extern const int16_t MyTargetRegClassByHwModeTables[4][2] = {
+// INSTRINFO-NEXT:   { // DefaultMode
+// INSTRINFO-NEXT:     MyTarget::EvenXRegsRegClassID,
+// INSTRINFO-NEXT:     MyTarget::XRegsRegClassID,
+// INSTRINFO-NEXT:   },
+// INSTRINFO-NEXT:   { // XPtr64
+// INSTRINFO-NEXT:     MyTarget::EvenXRegsRegClassID,
+// INSTRINFO-NEXT:     MyTarget::XRegsRegClassID,
+// INSTRINFO-NEXT:   },
+// INSTRINFO-NEXT:   { // YPtr32
+// INSTRINFO-NEXT:     MyTarget::EvenYRegsRegClassID,
+// INSTRINFO-NEXT:     MyTarget::YRegsRegClassID,
+// INSTRINFO-NEXT:   },
+// INSTRINFO-NEXT:   { // YPtr64
+// INSTRINFO-NEXT:     MyTarget::EvenYRegsRegClassID,
+// INSTRINFO-NEXT:     MyTarget::YRegsRegClassID,
+// INSTRINFO-NEXT:   },
+// INSTRINFO-NEXT: };
+
+// ASMWRITER-LABEL: static const AliasPatternCond Conds[] = {
+// ASMWRITER-NEXT:    // (TEST_PTRREG PtrRC:$dst, PtrRC:$src) - 0
+// ASMWRITER-NEXT:    {AliasPatternCond::K_RegClassByHwMode, MyTarget::PtrRC},
+// ASMWRITER-NEXT:    {AliasPatternCond::K_RegClassByHwMode, MyTarget::PtrRC},
+// ASMWRITER-NEXT:    // (TEST_PTRREG EvenPtrRC:$dst, EvenPtrRC:$src) - 2
+// ASMWRITER-NEXT:    {AliasPatternCond::K_RegClassByHwMode, MyTarget::EvenPtrRC},
+// ASMWRITER-NEXT:    {AliasPatternCond::K_RegClassByHwMode, MyTarget::EvenPtrRC},
+// ASMWRITER-NEXT:    // (TEST_PTRREG NullReg, PtrRC:$src) - 4
+// ASMWRITER-NEXT:    {AliasPatternCond::K_Custom, 1/*NullReg*/},
+// ASMWRITER-NEXT:    {AliasPatternCond::K_RegClassByHwMode, MyTarget::PtrRC},
+// ASMWRITER-NEXT:    // (TEST_PTRREG NullReg, EvenPtrRC:$src) - 6
+// ASMWRITER-NEXT:    {AliasPatternCond::K_Custom, 1/*NullReg*/},
+// ASMWRITER-NEXT:    {AliasPatternCond::K_RegClassByHwMode, MyTarget::EvenPtrRC},
+// ASMWRITER-NEXT:    // (TEST_XREG X0, XRegs:$src) - 8
+// ASMWRITER-NEXT:    {AliasPatternCond::K_Reg, MyTarget::X0},
+// ASMWRITER-NEXT:    {AliasPatternCond::K_RegClass, MyTarget::XRegsRegClassID},
+// ASMWRITER-NEXT:    // (TEST_XREG X0, EvenXRegs:$src) - 10
+// ASMWRITER-NEXT:    {AliasPatternCond::K_Reg, MyTarget::X0},
+// ASMWRITER-NEXT:    {AliasPatternCond::K_RegClass, MyTarget::EvenXRegsRegClassID},
+// ASMWRITER-NEXT:    // (TEST_XREG ModeCountReg, XRegs:$src) - 12
+// ASMWRITER-NEXT:    {AliasPatternCond::K_Custom, 2/*ModeCountReg*/},
+// ASMWRITER-NEXT:    {AliasPatternCond::K_RegClass, MyTarget::XRegsRegClassID},
+// ASMWRITER-NEXT:    // (TEST_YREG Y0, YRegs:$src) - 14
+// ASMWRITER-NEXT:    {AliasPatternCond::K_Reg, MyTarget::Y0},
+// ASMWRITER-NEXT:    {AliasPatternCond::K_RegClass, MyTarget::YRegsRegClassID},
+// ASMWRITER-NEXT:    // (TEST_YREG Y0, EvenYRegs:$src) - 16
+// ASMWRITER-NEXT:    {AliasPatternCond::K_Reg, MyTarget::Y0},
+// ASMWRITER-NEXT:    {AliasPatternCond::K_RegClass, MyTarget::EvenYRegsRegClassID},
+// ASMWRITER-NEXT:  };
+
+// ASMWRITER-LABEL: static bool MyTargetInstPrinterValidateMCOperand(const MCOperand &MCOp,
+// ASMWRITER-NEXT:                    const MCSubtargetInfo &STI,
+// ASMWRITER-NEXT:                    unsigned PredicateIndex) {
+// ASMWRITER-NEXT:    switch (PredicateIndex) {
+// ASMWRITER-NEXT:    default:
+// ASMWRITER-NEXT:      llvm_unreachable("Unknown MCOperandPredicate kind");
+// ASMWRITER-NEXT:      break;
+// ASMWRITER-NEXT:    case 1: {
+// ASMWRITER-NEXT:      auto getNullReg = [](unsigned HwMode) {
+// ASMWRITER-NEXT:        switch (HwMode) {
+// ASMWRITER-NEXT:        case 0: return MyTarget::X0; // DefaultMode
+// ASMWRITER-NEXT:        case 1: return MyTarget::X0; // XPtr64
+// ASMWRITER-NEXT:        case 2: return MyTarget::Y0; // YPtr32
+// ASMWRITER-NEXT:        case 3: return MyTarget::Y0; // YPtr64
+// ASMWRITER-NEXT:        default: llvm_unreachable("Unhandled HwMode for Register NullReg");
+// ASMWRITER-NEXT:        }
+// ASMWRITER-NEXT:      };
+// ASMWRITER-NEXT:      return MCOp.isReg() && MCOp.getReg() == getNullReg(STI.getHwMode(MCSubtargetInfo::HwMode_RegInfo));
+// ASMWRITER-NEXT:    }
+// ASMWRITER-NEXT:    case 2: {
+// ASMWRITER-NEXT:      auto getModeCountReg = [](unsigned HwMode) {
+// ASMWRITER-NEXT:        switch (HwMode) {
+// ASMWRITER-NEXT:        case 0: return MyTarget::X0; // DefaultMode
+// ASMWRITER-NEXT:        case 1: return MyTarget::X1; // XPtr64
+// ASMWRITER-NEXT:        case 2: return MyTarget::X2; // YPtr32
+// ASMWRITER-NEXT:        case 3: return MyTarget::X3; // YPtr64
+// ASMWRITER-NEXT:        default: llvm_unreachable("Unhandled HwMode for Register ModeCountReg");
+// ASMWRITER-NEXT:        }
+// ASMWRITER-NEXT:      };
+// ASMWRITER-NEXT:      return MCOp.isReg() && MCOp.getReg() == getModeCountReg(STI.getHwMode(MCSubtargetInfo::HwMode_RegInfo));
+// ASMWRITER-NEXT:    }
+// ASMWRITER-NEXT:    }
+// ASMWRITER-NEXT:  }
+
+
+
+// ASMMATCHER-LABEL: enum InstructionConversionKind {
+// ASMMATCHER-NEXT:    Convert__regModeCountReg__Reg1_0,
+// ASMMATCHER-NEXT:    Convert__regNullReg__RegByHwMode_PtrRC1_0,
+// ASMMATCHER-NEXT:    Convert__regNullReg__RegByHwMode_EvenPtrRC1_0,
+// ASMMATCHER-NEXT:    Convert__regX0__Reg1_0,
+// ASMMATCHER-NEXT:    Convert__regY0__Reg1_0,
+// ASMMATCHER-NEXT:    Convert__RegByHwMode_PtrRC1_0__RegByHwMode_PtrRC1_1,
+// ASMMATCHER-NEXT:    Convert__RegByHwMode_EvenPtrRC1_0__RegByHwMode_EvenPtrRC1_1,
+// ASMMATCHER-NEXT:    Convert__Reg1_0__Reg1_1,
+// ASMMATCHER-NEXT:    CVT_NUM_SIGNATURES
+// ASMMATCHER-NEXT:  };
+
+// ASMMATCHER-LABEL: static const uint8_t ConversionTable[CVT_NUM_SIGNATURES][5] = {
+// ASMMATCHER-NEXT:    // Convert__regModeCountReg__Reg1_0
+// ASMMATCHER-NEXT:    { CVT_regModeCountReg, 0, CVT_95_Reg, 1, CVT_Done },
+// ASMMATCHER-NEXT:    // Convert__regNullReg__RegByHwMode_PtrRC1_0
+// ASMMATCHER-NEXT:    { CVT_regNullReg, 0, CVT_95_addRegOperands, 1, CVT_Done },
+// ASMMATCHER-NEXT:    // Convert__regNullReg__RegByHwMode_EvenPtrRC1_0
+// ASMMATCHER-NEXT:    { CVT_regNullReg, 0, CVT_95_addRegOperands, 1, CVT_Done },
+// ASMMATCHER-NEXT:    // Convert__regX0__Reg1_0
+// ASMMATCHER-NEXT:    { CVT_regX0, 0, CVT_95_Reg, 1, CVT_Done },
+// ASMMATCHER-NEXT:    // Convert__regY0__Reg1_0
+// ASMMATCHER-NEXT:    { CVT_regY0, 0, CVT_95_Reg, 1, CVT_Done },
+// ASMMATCHER-NEXT:    // Convert__RegByHwMode_PtrRC1_0__RegByHwMode_PtrRC1_1
+// ASMMATCHER-NEXT:    { CVT_95_addRegOperands, 1, CVT_95_addRegOperands, 2, CVT_Done },
+// ASMMATCHER-NEXT:    // Convert__RegByHwMode_EvenPtrRC1_0__RegByHwMode_EvenPtrRC1_1
+// ASMMATCHER-NEXT:    { CVT_95_addRegOperands, 1, CVT_95_addRegOperands, 2, CVT_Done },
+// ASMMATCHER-NEXT:    // Convert__Reg1_0__Reg1_1
+// ASMMATCHER-NEXT:    { CVT_95_Reg, 1, CVT_95_Reg, 2, CVT_Done },
+// ASMMATCHER-NEXT:  };
+
+// ASMMATCHER-LABEL: convertToMCInst(unsigned Kind, MCInst &Inst, unsigned Opcode,
+// ASMMATCHER-LABEL:  case CVT_regModeCountReg:
+// ASMMATCHER-NEXT:     Inst.addOperand(MCOperand::createReg([](unsigned HwMode) {
+// ASMMATCHER-NEXT:       switch (HwMode) {
+// ASMMATCHER-NEXT:       case 0: return MyTarget::X0; // DefaultMode
+// ASMMATCHER-NEXT:       case 1: return MyTarget::X1; // XPtr64
+// ASMMATCHER-NEXT:       case 2: return MyTarget::X2; // YPtr32
+// ASMMATCHER-NEXT:       case 3: return MyTarget::X3; // YPtr64
+// ASMMATCHER-NEXT:       default: llvm_unreachable("Unhandled HwMode for Register ModeCountReg");
+// ASMMATCHER-NEXT:       }
+// ASMMATCHER-NEXT:     }(STI->getHwMode(MCSubtargetInfo::HwMode_RegInfo))));
+// ASMMATCHER-NEXT:     break;
+// ASMMATCHER-NEXT:   case CVT_95_Reg:
+// ASMMATCHER-NEXT:     static_cast<MyTargetOperand &>(*Operands[OpIdx]).addRegOperands(Inst, 1);
+// ASMMATCHER-NEXT:     break;
+// ASMMATCHER-NEXT:   case CVT_regNullReg:
+// ASMMATCHER-NEXT:     Inst.addOperand(MCOperand::createReg([](unsigned HwMode) {
+// ASMMATCHER-NEXT:       switch (HwMode) {
+// ASMMATCHER-NEXT:       case 0: return MyTarget::X0; // DefaultMode
+// ASMMATCHER-NEXT:       case 1: return MyTarget::X0; // XPtr64
+// ASMMATCHER-NEXT:       case 2: return MyTarget::Y0; // YPtr32
+// ASMMATCHER-NEXT:       case 3: return MyTarget::Y0; // YPtr64
+// ASMMATCHER-NEXT:       default: llvm_unreachable("Unhandled HwMode for Register NullReg");
+// ASMMATCHER-NEXT:       }
+// ASMMATCHER-NEXT:     }(STI->getHwMode(MCSubtargetInfo::HwMode_RegInfo))));
+// ASMMATCHER-NEXT:     break;
+// ASMMATCHER-NEXT:   case CVT_95_addRegOperands:
+// ASMMATCHER-NEXT:     static_cast<MyTargetOperand &>(*Operands[OpIdx]).addRegOperands(Inst, 1);
+// ASMMATCHER-NEXT:     break;
+// ASMMATCHER-NEXT:   case CVT_regX0:
+// ASMMATCHER-NEXT:     Inst.addOperand(MCOperand::createReg(MyTarget::X0));
+// ASMMATCHER-NEXT:     break;
+// ASMMATCHER-NEXT:   case CVT_regY0:
+// ASMMATCHER-NEXT:     Inst.addOperand(MCOperand::createReg(MyTarget::Y0));
+// ASMMATCHER-NEXT:     break;
+// ASMMATCHER-NEXT:   }
+
+// ASMMATCHER-LABEL: enum MatchClassKind {
+// ASMMATCHER-NEXT:    InvalidMatchClass = 0,
+// ASMMATCHER-NEXT:    OptionalMatchClass = 1,
+// ASMMATCHER-NEXT:    MCK_LAST_TOKEN = OptionalMatchClass,
+// ASMMATCHER-NEXT:    MCK_EvenXRegs, // register class 'EvenXRegs'
+// ASMMATCHER-NEXT:    MCK_EvenYRegs, // register class 'EvenYRegs'
+// 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_EvenPtrRC, // register class by hwmode
+// ASMMATCHER-NEXT:    MCK_RegByHwMode_PtrRC, // register class by hwmode
+// ASMMATCHER-NEXT:    MCK_LAST_REGCLASS_BY_HWMODE = MCK_RegByHwMode_PtrRC,
+// ASMMATCHER-NEXT:    MCK_Imm, // user defined class 'ImmAsmOperand'
+// ASMMATCHER-NEXT:    NumMatchClassKinds
+// ASMMATCHER-NEXT:  };
+
+
+// ASMMATCHER-LABEL: static const MatchEntry MatchTable0[] = {
+// ASMMATCHER-NEXT:    /* mode_count */, MyTarget::TEST_XREG, Convert__regModeCountReg__Reg1_0, AMFBS_None, { MCK_XRegs }, },
+// ASMMATCHER-NEXT:    /* t_ptr */, MyTarget::TEST_PTRREG, Convert__regNullReg__RegByHwMode_PtrRC1_0, AMFBS_None, { MCK_RegByHwMode_PtrRC }, },
+// ASMMATCHER-NEXT:    /* t_ptr.even */, MyTarget::TEST_PTRREG, Convert__regNullReg__RegByHwMode_EvenPtrRC1_0, AMFBS_None, { MCK_RegByHwMode_EvenPtrRC }, },
+// ASMMATCHER-NEXT:    /* t_x */, MyTarget::TEST_XREG, Convert__regX0__Reg1_0, AMFBS_None, { MCK_XRegs }, },
+// ASMMATCHER-NEXT:    /* t_x.even */, MyTarget::TEST_XREG, Convert__regX0__Reg1_0, AMFBS_None, { MCK_EvenXRegs }, },
+// ASMMATCHER-NEXT:    /* t_y */, MyTarget::TEST_YREG, Convert__regY0__Reg1_0, AMFBS_None, { MCK_YRegs }, },
+// ASMMATCHER-NEXT:    /* t_y.even */, MyTarget::TEST_YREG, Convert__regY0__Reg1_0, AMFBS_None, { MCK_EvenYRegs }, },
+// ASMMATCHER-NEXT:    /* test_alias */, MyTarget::TEST_PTRREG, Convert__RegByHwMode_PtrRC1_0__RegByHwMode_PtrRC1_1, AMFBS_None, { MCK_RegByHwMode_PtrRC, MCK_RegByHwMode_PtrRC }, },
+// ASMMATCHER-NEXT:    /* test_alias.even */, MyTarget::TEST_PTRREG, Convert__RegByHwMode_EvenPtrRC1_0__RegByHwMode_EvenPtrRC1_1, AMFBS_None, { MCK_RegByHwMode_EvenPtrRC, MCK_RegByHwMode_EvenPtrRC }, },
+// ASMMATCHER-NEXT:    /* test_ptr */, MyTarget::TEST_PTRREG, Convert__RegByHwMode_PtrRC1_0__RegByHwMode_PtrRC1_1, AMFBS_None, { MCK_RegByHwMode_PtrRC, MCK_RegByHwMode_PtrRC }, },
+// ASMMATCHER-NEXT:    /* test_x */, MyTarget::TEST_XREG, Convert__Reg1_0__Reg1_1, AMFBS_None, { MCK_XRegs, MCK_XRegs }, },
+// ASMMATCHER-NEXT:    /* test_y */, MyTarget::TEST_YREG, Convert__Reg1_0__Reg1_1, AMFBS_None, { MCK_YRegs, MCK_YRegs }, },
+// ASMMATCHER-NEXT: };
+
+include "Common/RegisterByHwModeCommon.td"
+
+// Define more restrictive subset classes to check that those are handled.
+def EvenXRegs : RegisterClass<"MyTarget", [XLenVT], 32, (add X0, X2)> {
+  let RegInfos = XLenRI;  // Needed to determine size of registers
+}
+def EvenYRegs : RegisterClass<"MyTarget", [YLenVT], 64, (add Y0, Y2)> {
+  let RegInfos = YLenRI;  // Needed to determine size of registers
+}
+def EvenPtrRC : RegClassByHwMode<[XPtr32,    XPtr64,    YPtr32,    YPtr64],
+                                 [EvenXRegs, EvenXRegs, EvenYRegs, EvenYRegs]>;
+
+def MY_TEST_ALIAS : InstAlias<"test_alias $dst, $src", (TEST_PTRREG PtrRC:$dst, PtrRC:$src)>;
+def MY_TEST_ALIAS_EVEN : InstAlias<"test_alias.even $dst, $src", (TEST_PTRREG EvenPtrRC:$dst, EvenPtrRC:$src)>;
+def MY_T_X : InstAlias<"t_x $src", (TEST_XREG X0, XRegs:$src)>;
+def MY_T_X_EVEN : InstAlias<"t_x.even $src", (TEST_XREG X0, EvenXRegs:$src)>;
+def MY_T_Y : InstAlias<"t_y $src", (TEST_YREG Y0, YRegs:$src)>;
+def MY_T_Y_EVEN : InstAlias<"t_y.even $src", (TEST_YREG Y0, EvenYRegs:$src)>;
+def MY_T_PTR : InstAlias<"t_ptr $src", (TEST_PTRREG NullReg, PtrRC:$src)>;
+def MY_T_PTR_EVEN : InstAlias<"t_ptr.even $src", (TEST_PTRREG NullReg, EvenPtrRC:$src)>;
+
+// Add another test where the register number varies, but the regclass doesn't
+def ModeCountReg : RegisterByHwMode<XRegs, [XPtr32, XPtr64, YPtr32, YPtr64],
+                                           [X0,     X1,     X2,     X3]>;
+def TEST_MODE_COUNT : InstAlias<"mode_count $src", (TEST_XREG ModeCountReg, XRegs:$src)>;
+
+/// Include a test for -gen-pseudo-lowering:
+class Pseudo<dag outs, dag ins> : TestInstruction {
+  let OutOperandList = outs;
+  let InOperandList = ins;
+  let isPseudo = 1;
+  let isCodeGenOnly = 1;
+}
+// PSEUDO-LABEL: lowerPseudoInstExpansion(const MachineInstr *MI, MCInst &Inst)
+// PSEUDO-LABEL: case MyTarget::PseudoPtr: {
+// PSEUDO-NEXT:    MCOperand MCOp;
+// PSEUDO-NEXT:    Inst.setOpcode(MyTarget::TEST_PTRREG);
+// PSEUDO-NEXT:    // Operand: dst
+// PSEUDO-NEXT:    Inst.addOperand(MCOperand::createReg([](unsigned HwMode) {
+// PSEUDO-NEXT:        switch (HwMode) {
+// PSEUDO-NEXT:        case 0: return MyTarget::X0; // DefaultMode
+// PSEUDO-NEXT:        case 1: return MyTarget::X0; // XPtr64
+// PSEUDO-NEXT:        case 2: return MyTarget::Y0; // YPtr32
+// PSEUDO-NEXT:        case 3: return MyTarget::Y0; // YPtr64
+// PSEUDO-NEXT:        default: llvm_unreachable("Unhandled HwMode for Register NullReg");
+// PSEUDO-NEXT:        }
+// PSEUDO-NEXT:      }(STI->getHwMode(MCSubtargetInfo::HwMode_RegInfo))));
+// PSEUDO-NEXT:    // Operand: src
+// PSEUDO-NEXT:    lowerOperand(MI->getOperand(0), MCOp);
+// PSEUDO-NEXT:    Inst.addOperand(MCOp);
+// PSEUDO-NEXT:    break;
+// PSEUDO-NEXT:  }
+// PSEUDO-NEXT:  case MyTarget::PseudoX: {
+// PSEUDO-NEXT:    MCOperand MCOp;
+// PSEUDO-NEXT:    Inst.setOpcode(MyTarget::TEST_XREG);
+// PSEUDO-NEXT:    // Operand: dst
+// PSEUDO-NEXT:    Inst.addOperand(MCOperand::createReg([](unsigned HwMode) {
+// PSEUDO-NEXT:        switch (HwMode) {
+// PSEUDO-NEXT:        case 0: return MyTarget::X0; // DefaultMode
+// PSEUDO-NEXT:        case 1: return MyTarget::X1; // XPtr64
+// PSEUDO-NEXT:        case 2: return MyTarget::X2; // YPtr32
+// PSEUDO-NEXT:        case 3: return MyTarget::X3; // YPtr64
+// PSEUDO-NEXT:        default: llvm_unreachable("Unhandled HwMode for Register ModeCountReg");
+// PSEUDO-NEXT:        }
+// PSEUDO-NEXT:      }(STI->getHwMode(MCSubtargetInfo::HwMode_RegInfo))));
+// PSEUDO-NEXT:    // Operand: src
+// PSEUDO-NEXT:    lowerOperand(MI->getOperand(0), MCOp);
+// PSEUDO-NEXT:    Inst.addOperand(MCOp);
+// PSEUDO-NEXT:    break;
+// PSEUDO-NEXT:  }
+
+def PseudoX : Pseudo<(outs), (ins EvenXRegs:$rs1)>,
+    PseudoInstExpansion<(TEST_XREG ModeCountReg, XRegs:$rs1)>;
+def PseudoPtr : Pseudo<(outs), (ins EvenPtrRC:$rs1)>,
+    PseudoInstExpansion<(TEST_PTRREG NullReg, PtrRegOperand:$rs1)>;
+
+// TODO: Add DAGISel support
+def int_with_mode_reg : Intrinsic<[llvm_i64_ty], [], [IntrNoMem, IntrWillReturn]>;
+def int_with_null_reg : Intrinsic<[llvm_i64_ty], [], [IntrNoMem, IntrWillReturn]>;
+def : Pat<(int_with_mode_reg), (TEST_XREG ModeCountReg)>;
+def : Pat<(int_with_mode_reg), (TEST_PTRREG NullReg)>;
+
+defm : RemapAllTargetPseudoPointerOperands<PtrRC>;
+def MyTargetISA : InstrInfo;
+def MyTargetAsmWriter : AsmWriter {
+  int PassSubtarget = 1;
+}
+def MyTarget : Target {
+  let InstructionSet = MyTargetISA;
+  let AssemblyWriters = [MyTargetAsmWriter];
+}
diff --git a/llvm/test/TableGen/RegisterByHwModeErrors.td b/llvm/test/TableGen/RegisterByHwModeErrors.td
new file mode 100644
index 0000000000000..a1b7b4cc6e8b7
--- /dev/null
+++ b/llvm/test/TableGen/RegisterByHwModeErrors.td
@@ -0,0 +1,69 @@
+// RUN: rm -rf %t && split-file %s %t
+// RUN: not llvm-tblgen --gen-asm-matcher -I %t -I %p/../../include -I %S \
+// RUN:    %t/bad-regclass.td -o /dev/null 2>&1 | FileCheck %t/bad-regclass.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen --gen-asm-matcher -I %t -I %p/../../include -I %S \
+// RUN:    %t/bad-regclass-by-mode.td -o /dev/null 2>&1 | FileCheck %t/bad-regclass-by-mode.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen --gen-asm-matcher -I %t -I %p/../../include -I %S \
+// RUN:    %t/bad-not-regclass.td -o /dev/null 2>&1 | FileCheck %t/bad-not-regclass.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen --gen-asm-matcher -I %t -I %p/../../include -I %S \
+// RUN:    %t/duplicate-entry.td -o /dev/null 2>&1 | FileCheck %t/duplicate-entry.td --implicit-check-not="error:"
+// RUN: not llvm-tblgen --gen-dag-isel -I %t -I %p/../../include -I %S \
+// RUN:    %t/isel-not-supported-yet.td -o /dev/null 2>&1 | FileCheck %t/isel-not-supported-yet.td --implicit-check-not="error:"
+
+//--- bad-regclass.td
+include "Common/RegisterByHwModeCommon.td"
+def BadReg : RegisterByHwMode<XRegs, [XPtr32, XPtr64, YPtr32, YPtr64], [X0, X0, X0, Y0]>;
+// CHECK: [[#@LINE-1]]:5: error: Register Y0 for HwMode YPtr64 is not a member of register class XRegs
+
+// Need to define an instruction that uses BadReg to get the diagnostic
+def : InstAlias<"test $src", (TEST_XREG BadReg, XRegs:$src)>;
+def MyTargetISA : InstrInfo;
+def MyTarget : Target { let InstructionSet = MyTargetISA; }
+
+
+//--- bad-regclass-by-mode.td
+include "Common/RegisterByHwModeCommon.td"
+def BadReg : RegisterByHwMode<PtrRC, [XPtr32, XPtr64, YPtr32, YPtr64], [X0, X0, X0, X0]>;
+// CHECK: [[#@LINE-1]]:5: error: Register X0 for HwMode YPtr32 is not a member of register class YRegs
+
+// Need to define an instruction that uses BadReg to get the diagnostic
+def : InstAlias<"test $src", (TEST_PTRREG BadReg, PtrRegOperand:$src)>;
+def MyTargetISA : InstrInfo;
+def MyTarget : Target { let InstructionSet = MyTargetISA; }
+
+
+//--- bad-not-regclass.td
+include "Common/RegisterByHwModeCommon.td"
+def FakeRegClass : RegisterClassLike;
+def BadReg : RegisterByHwMode<FakeRegClass, [XPtr32, XPtr64, YPtr32, YPtr64], [X0, X0, X0, X0]>;
+// CHECK: [[#@LINE-1]]:5: error: FakeRegClass is not a known RegisterClass!
+// CHECK: [[#@LINE-3]]:5: note: FakeRegClass defined here
+
+// Need to define an instruction that uses BadReg to get the diagnostic
+def : InstAlias<"test $src", (TEST_XREG BadReg, XRegs:$src)>;
+def MyTargetISA : InstrInfo;
+def MyTarget : Target { let InstructionSet = MyTargetISA; }
+
+
+//--- duplicate-entry.td
+include "Common/RegisterByHwModeCommon.td"
+/// We should get an error if we accidentally use the same mode twice:
+def BadReg : RegisterByHwMode<XRegs, [XPtr32, XPtr64, XPtr32, YPtr64], [X0, X0, X0, Y0]>;
+// CHECK: [[#@LINE-1]]:5: error: duplicate Register for HwMode DefaultMode: X0
+
+// Need to define an instruction that uses BadReg to get the diagnostic
+def : InstAlias<"test $src", (TEST_XREG BadReg, XRegs:$src)>;
+def MyTargetISA : InstrInfo;
+def MyTarget : Target { let InstructionSet = MyTargetISA; }
+
+//--- isel-not-supported-yet.td
+include "Common/RegisterByHwModeCommon.td"
+/// Output for -gen-dag-isel is not supported yet, check that we get an
+/// error instead of crashing.
+def int_with_null_reg : Intrinsic<[llvm_i64_ty], [], [IntrNoMem, IntrWillReturn]>;
+def : Pat<(int_with_null_reg), (TEST_PTRREG NullReg)>;
+// TODO: We don't track source locations of the DAG nodes, so the error location is bad.
+// CHECK: RegisterByHwModeCommon.td:55:5: error: RegisterByHwMode in SelectionDAG patterns not yet supported!
+
+def MyTargetISA : InstrInfo;
+def MyTarget : Target { let InstructionSet = MyTargetISA; }
diff --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
index e6085af5aa91e..4049a1a28a3da 100644
--- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp
+++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
@@ -451,8 +451,9 @@ struct MatchableInfo {
       /// the operand.
       ImmOperand,
 
-      /// RegOperand - This represents a fixed register that is dumped in.
-      RegOperand
+      /// RegOperand - This represents a fixed register (potentially depending
+      /// on the HwMode) that is dumped in.
+      RegOperand,
     } Kind;
 
     /// Tuple containing the index of the (earlier) result operand that should
@@ -2263,12 +2264,14 @@ emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName,
       }
       case MatchableInfo::ResOperand::RegOperand: {
         std::string Reg, Name;
+        bool IsRegByHwMode = false;
         if (!OpInfo.Register) {
           Name = "reg0";
           Reg = "0";
         } else {
           Reg = getQualifiedName(OpInfo.Register);
           Name = "reg" + OpInfo.Register->getName().str();
+          IsRegByHwMode = OpInfo.Register->isSubClassOf("RegisterByHwMode");
         }
         Signature += "__" + Name;
         Name = "CVT_" + Name;
@@ -2281,10 +2284,18 @@ emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName,
 
         if (!IsNewConverter)
           break;
-        CvtOS << "    case " << Name << ":\n"
-              << "      Inst.addOperand(MCOperand::createReg(" << Reg << "));\n"
-              << "      break;\n";
 
+        CvtOS << indent(4) << "case " << Name << ":\n"
+              << indent(6) << "Inst.addOperand(MCOperand::createReg(";
+        if (IsRegByHwMode) {
+          RegisterByHwMode(OpInfo.Register, Target.getRegBank())
+                  .generateResolverLambda(CvtOS, Target.getHwModes(),
+                                          /*Indent=*/6)
+              << "(STI->getHwMode(MCSubtargetInfo::HwMode_RegInfo))";
+        } else {
+          CvtOS << Reg;
+        }
+        CvtOS << "));\n" << indent(6) << "break;\n";
         OpOS << "    case " << Name << ":\n"
              << "      Operands[*(p + 1)]->setMCOperandNum(NumMCOperands);\n"
              << "      Operands[*(p + 1)]->setConstraint(\"m\");\n"
diff --git a/llvm/utils/TableGen/AsmWriterEmitter.cpp b/llvm/utils/TableGen/AsmWriterEmitter.cpp
index edca87504a2f7..9c72464b77c59 100644
--- a/llvm/utils/TableGen/AsmWriterEmitter.cpp
+++ b/llvm/utils/TableGen/AsmWriterEmitter.cpp
@@ -1001,9 +1001,22 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
             break;
           }
 
-          StringRef Reg = CGA.ResultOperands[i].getRegister()->getName();
-          IAP.addCond(std::string(
-              formatv("AliasPatternCond::K_Reg, {}::{}", Namespace, Reg)));
+          const Record *Rec = CGA.ResultOperands[i].getRegister();
+          StringRef Reg = Rec->getName();
+          if (Rec->isSubClassOf("RegisterByHwMode")) {
+            // Use a custom predicate to handle RegisterByHwMode since there
+            // is no way to handle this in the generic code.
+            unsigned &Entry = MCOpPredicateMap[Rec];
+            if (!Entry) {
+              MCOpPredicates.push_back(Rec);
+              Entry = MCOpPredicates.size();
+            }
+            IAP.addCond(std::string(
+                formatv("AliasPatternCond::K_Custom, {}/*{}*/", Entry, Reg)));
+          } else {
+            IAP.addCond(std::string(
+                formatv("AliasPatternCond::K_Reg, {}::{}", Namespace, Reg)));
+          }
           break;
         }
 
@@ -1297,12 +1310,26 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
       << "    llvm_unreachable(\"Unknown MCOperandPredicate kind\");\n"
       << "    break;\n";
 
-    for (unsigned i = 0; i < MCOpPredicates.size(); ++i) {
-      StringRef MCOpPred =
-          MCOpPredicates[i]->getValueAsString("MCOperandPredicate");
-      O << "  case " << i + 1 << ": {\n"
-        << MCOpPred.data() << "\n"
-        << "    }\n";
+    for (auto [I, Rec] : enumerate(MCOpPredicates)) {
+      O << "  case " << I + 1 << ": {\n";
+      // We have to handle RegClassByHwMode predicates here since there is no
+      // special case opcode for them.
+      if (Rec->isSubClassOf("RegisterByHwMode")) {
+        if (!PassSubtarget)
+          PrintFatalError(Target.getAsmWriter()->getLoc(),
+                          "PassSubtarget must be set in "
+                          "AsmWriter to handle RegisterByHwMode");
+        O << "    auto get" << Rec->getName() << " = ";
+        RegisterByHwMode(Rec, Target.getRegBank())
+                .generateResolverLambda(O, Target.getHwModes(), /*Indent=*/4)
+            << ";\n";
+        O << "    return MCOp.isReg() && MCOp.getReg() == get" << Rec->getName()
+          << "(STI.getHwMode(MCSubtargetInfo::HwMode_RegInfo));\n";
+      } else {
+        // Normal MCOperandPredicate code snippet, emit verbatim.
+        O << Rec->getValueAsString("MCOperandPredicate") << "\n";
+      }
+      O << "  }\n";
     }
     O << "  }\n"
       << "}\n\n";
diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
index c3e07979467de..541a0378c4411 100644
--- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
@@ -1801,7 +1801,7 @@ static TypeSetByHwMode getTypeForRegClassByHwMode(const CodeGenTarget &T,
                                                   const Record *R,
                                                   ArrayRef<SMLoc> Loc) {
   TypeSetByHwMode TypeSet;
-  RegClassByHwMode Helper(R, T.getHwModes(), T.getRegBank());
+  RegClassByHwMode Helper(R, T.getRegBank());
 
   for (auto [ModeID, RegClass] : Helper) {
     ArrayRef<ValueTypeByHwMode> RegClassVTs = RegClass->getValueTypes();
diff --git a/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp b/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp
index d3050c0553114..407cd7826ff48 100644
--- a/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp
@@ -47,8 +47,12 @@ matchSimpleOperand(const Init *Arg, const StringInit *ArgName, const Record *Op,
     if (const auto *ArgDef = dyn_cast<DefInit>(Arg)) {
       const Record *ArgRec = ArgDef->getDef();
 
-      // Match 'RegClass:$name' or 'RegOp:$name'.
+      // Match 'RegClass:$name', 'RegOp:$name', or RegisterByHwMode.
       if (const Record *ArgRC = T.getInitValueAsRegClassLike(Arg)) {
+        if (ArgRec->isSubClassOf("RegisterByHwMode")) {
+          // Note: constraints are validated in RegisterByHwMode ctor later.
+          return ResultOperand::createRegister(ArgRec);
+        }
         if (ArgRC->isSubClassOf("RegisterClass")) {
           if (!OpRC->isSubClassOf("RegisterClass") ||
               !T.getRegisterClass(OpRC, Loc).hasSubClass(
diff --git a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
index 65a2594859e69..aece50ff33ecf 100644
--- a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
@@ -2548,6 +2548,37 @@ CodeGenRegBank::getRegClassForRegister(const Record *R) {
   return FoundRC;
 }
 
+bool CodeGenRegBank::regClassContainsReg(const Record *RegClassDef,
+                                         const Record *RegDef,
+                                         ArrayRef<SMLoc> Loc) {
+  // Check all four combinations of Register[ByHwMode] X RegClass[ByHwMode],
+  // starting with the two RegClassByHwMode cases.
+  unsigned NumModes = CGH.getNumModeIds();
+  std::optional<RegisterByHwMode> RegByMode;
+  CodeGenRegister *Reg = nullptr;
+  if (RegDef->isSubClassOf("RegisterByHwMode"))
+    RegByMode = RegisterByHwMode(RegDef, *this);
+  else
+    Reg = getReg(RegDef);
+  if (RegClassDef->isSubClassOf("RegClassByHwMode")) {
+    RegClassByHwMode RC(RegClassDef, *this);
+    for (unsigned M = 0; M < NumModes; ++M) {
+      if (RC.hasMode(M) && !RC.get(M)->contains(Reg ? Reg : RegByMode->get(M)))
+        return false;
+    }
+    return true;
+  }
+  // Otherwise we have a plain register class, check Register[ByHwMode]
+  CodeGenRegisterClass *RC = getRegClass(RegClassDef, Loc);
+  if (Reg)
+    return RC->contains(Reg);
+  for (unsigned M = 0; M < NumModes; ++M) {
+    if (RegByMode->hasMode(M) && !RC->contains(RegByMode->get(M)))
+      return false;
+  }
+  return true; // RegByMode contained for all possible modes.
+}
+
 const CodeGenRegisterClass *
 CodeGenRegBank::getMinimalPhysRegClass(const Record *RegRecord,
                                        ValueTypeByHwMode *VT) {
diff --git a/llvm/utils/TableGen/Common/CodeGenRegisters.h b/llvm/utils/TableGen/Common/CodeGenRegisters.h
index a3ad0b797a704..eaabe48a70d25 100644
--- a/llvm/utils/TableGen/Common/CodeGenRegisters.h
+++ b/llvm/utils/TableGen/Common/CodeGenRegisters.h
@@ -825,6 +825,13 @@ class CodeGenRegBank {
   /// return the superclass.  Otherwise return null.
   const CodeGenRegisterClass *getRegClassForRegister(const Record *R);
 
+  /// Returns whether \p RegClass contains register \p Reg, handling
+  /// RegClassByHwMode and RegisterByHwMode correctly.
+  /// This should be preferred instead of
+  /// `RegBank.getRegClass(RC).contains(RegBank.getReg(R))`.
+  bool regClassContainsReg(const Record *RegClass, const Record *RegDef,
+                           ArrayRef<SMLoc> Loc = {});
+
   // Analog of TargetRegisterInfo::getMinimalPhysRegClass. Unlike
   // getRegClassForRegister, this tries to find the smallest class containing
   // the physical register. If \p VT is specified, it will only find classes
diff --git a/llvm/utils/TableGen/Common/InfoByHwMode.cpp b/llvm/utils/TableGen/Common/InfoByHwMode.cpp
index 539c24a530a7d..453405ae7b93c 100644
--- a/llvm/utils/TableGen/Common/InfoByHwMode.cpp
+++ b/llvm/utils/TableGen/Common/InfoByHwMode.cpp
@@ -180,9 +180,10 @@ void RegSizeInfoByHwMode::writeToStream(raw_ostream &OS) const {
   OS << '}';
 }
 
-RegClassByHwMode::RegClassByHwMode(const Record *R, const CodeGenHwModes &CGH,
+RegClassByHwMode::RegClassByHwMode(const Record *R,
                                    const CodeGenRegBank &RegBank)
     : InfoByHwMode<const llvm::CodeGenRegisterClass *>(R) {
+  const CodeGenHwModes &CGH = RegBank.getHwModes();
   const HwModeSelect &MS = CGH.getHwModeSelect(R);
 
   for (auto [ModeID, RegClassRec] : MS.Items) {
@@ -229,6 +230,56 @@ EncodingInfoByHwMode::EncodingInfoByHwMode(const Record *R,
   }
 }
 
+RegisterByHwMode::RegisterByHwMode(const Record *R, CodeGenRegBank &RegBank)
+    : InfoByHwMode<const llvm::CodeGenRegister *>(R) {
+  const CodeGenHwModes &CGH = RegBank.getHwModes();
+  const HwModeSelect &MS = CGH.getHwModeSelect(R);
+  const Record *RCDef = R->getValueAsDef("RegClass");
+  std::optional<RegClassByHwMode> RegClassByMode;
+  if (RCDef->isSubClassOf("RegClassByHwMode"))
+    RegClassByMode = RegClassByHwMode(RCDef, RegBank);
+  for (auto [ModeID, RegRecord] : MS.Items) {
+    assert(RegRecord && RegRecord->isSubClassOf("Register") &&
+           "Register value must subclass Register");
+    CodeGenRegister *Reg = RegBank.getReg(RegRecord);
+    const CodeGenRegisterClass *RC =
+        RegClassByMode ? RegClassByMode->get(ModeID)
+                       : RegBank.getRegClass(RCDef, R->getLoc());
+    if (!RC->contains(Reg))
+      PrintFatalError(R->getLoc(), "Register " + Reg->getName() +
+                                       " for HwMode " +
+                                       CGH.getModeName(ModeID, true) +
+                                       " is not a member of register class " +
+                                       RC->getName());
+    if (!Map.try_emplace(ModeID, Reg).second)
+      PrintFatalError(R->getLoc(), "duplicate Register for HwMode " +
+                                       CGH.getModeName(ModeID, true) + ": " +
+                                       Reg->getName());
+  }
+}
+
+raw_ostream &RegisterByHwMode::generateResolverLambda(raw_ostream &OS,
+                                                      const CodeGenHwModes &CGH,
+                                                      unsigned Indent) const {
+  unsigned NumModes = CGH.getNumModeIds();
+  OS << "[](unsigned HwMode) {\n"
+     << indent(Indent + 2) << "switch (HwMode) {\n";
+  for (unsigned M = 0; M < NumModes; ++M) {
+    if (hasMode(M)) {
+      const CodeGenRegister *R = get(M);
+      OS << indent(Indent + 2) << "case " << M << ": return "
+         << getQualifiedName(R->TheDef) << "; // " << CGH.getModeName(M, true)
+         << "\n";
+    }
+  }
+  OS << indent(Indent + 2)
+     << "default: llvm_unreachable(\"Unhandled HwMode for Register "
+     << Def->getName() << "\");\n"
+     << indent(Indent + 2) << "}\n"
+     << indent(Indent) << "}";
+  return OS;
+}
+
 raw_ostream &llvm::operator<<(raw_ostream &OS, const ValueTypeByHwMode &T) {
   T.writeToStream(OS);
   return OS;
diff --git a/llvm/utils/TableGen/Common/InfoByHwMode.h b/llvm/utils/TableGen/Common/InfoByHwMode.h
index 4126cb9bc585a..b57952c4ee702 100644
--- a/llvm/utils/TableGen/Common/InfoByHwMode.h
+++ b/llvm/utils/TableGen/Common/InfoByHwMode.h
@@ -28,6 +28,7 @@
 namespace llvm {
 
 class CodeGenRegBank;
+class CodeGenRegister;
 class CodeGenRegisterClass;
 class Record;
 class raw_ostream;
@@ -254,11 +255,20 @@ struct EncodingInfoByHwMode : public InfoByHwMode<const Record *> {
 
 struct RegClassByHwMode : public InfoByHwMode<const CodeGenRegisterClass *> {
 public:
-  RegClassByHwMode(const Record *R, const CodeGenHwModes &CGH,
-                   const CodeGenRegBank &RegBank);
+  RegClassByHwMode(const Record *R, const CodeGenRegBank &RegBank);
   RegClassByHwMode() = default;
 };
 
+struct RegisterByHwMode : public InfoByHwMode<const CodeGenRegister *> {
+  RegisterByHwMode(const Record *R, CodeGenRegBank &RegBank);
+  RegisterByHwMode() = default;
+  // TODO: Should probably emit this register selection code somewhere shared
+  // instead of emitting it every time.
+  raw_ostream &generateResolverLambda(raw_ostream &OS,
+                                      const CodeGenHwModes &CGH,
+                                      unsigned Indent) const;
+};
+
 } // namespace llvm
 
 #endif // LLVM_UTILS_TABLEGEN_COMMON_INFOBYHWMODE_H
diff --git a/llvm/utils/TableGen/CompressInstEmitter.cpp b/llvm/utils/TableGen/CompressInstEmitter.cpp
index 1dce0baa5f898..f474823ca8804 100644
--- a/llvm/utils/TableGen/CompressInstEmitter.cpp
+++ b/llvm/utils/TableGen/CompressInstEmitter.cpp
@@ -165,13 +165,12 @@ class CompressInstEmitter {
 bool CompressInstEmitter::validateRegister(const Record *Reg,
                                            const Record *RegClass,
                                            ArrayRef<SMLoc> Loc) {
-  assert(Reg->isSubClassOf("Register") && "Reg record should be a Register");
+  assert((Reg->isSubClassOf("Register") ||
+          Reg->isSubClassOf("RegisterByHwMode")) &&
+         "Reg record should be a Register");
   assert(RegClass->isSubClassOf("RegisterClassLike") &&
          "RegClass record should be RegisterClassLike");
-  const CodeGenRegisterClass &RC = Target.getRegisterClass(RegClass, Loc);
-  const CodeGenRegister *R = Target.getRegBank().getReg(Reg);
-  assert(R != nullptr && "Register not defined!!");
-  return RC.contains(R);
+  return Target.getRegBank().regClassContainsReg(RegClass, Reg, Loc);
 }
 
 bool CompressInstEmitter::validateTypes(const Record *DagOpType,
@@ -255,12 +254,13 @@ void CompressInstEmitter::addDagOperandMapping(const Record *Rec,
                                            "' and Dag operand count mismatch");
 
       if (const auto *DI = dyn_cast<DefInit>(Dag->getArg(DAGOpNo))) {
-        if (DI->getDef()->isSubClassOf("Register")) {
+        if (DI->getDef()->isSubClassOf("Register") ||
+            DI->getDef()->isSubClassOf("RegisterByHwMode")) {
           // Check if the fixed register belongs to the Register class.
           if (!validateRegister(DI->getDef(), OpndRec, Rec->getLoc()))
             PrintFatalError(Rec->getLoc(),
                             "Error in Dag '" + Dag->getAsString() +
-                                "'Register: '" + DI->getDef()->getName() +
+                                "': Register '" + DI->getDef()->getName() +
                                 "' is not in register class '" +
                                 OpndRec->getName() + "'");
           OperandMap[OpNo].Kind = OpData::Reg;
@@ -763,8 +763,16 @@ void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &OS,
           const Record *Reg = SourceOperandMap[OpNo].RegRec;
           CondStream << CondSep << "MI.getOperand(" << OpNo << ").isReg()"
                      << CondSep << "(MI.getOperand(" << OpNo
-                     << ").getReg() == " << TargetName << "::" << Reg->getName()
-                     << ")";
+                     << ").getReg() == ";
+          if (Reg->isSubClassOf("RegisterByHwMode")) {
+            RegisterByHwMode(Reg, Target.getRegBank())
+                    .generateResolverLambda(CondStream, Target.getHwModes(),
+                                            /*Indent=*/8)
+                << "(HwModeId)";
+          } else {
+            CondStream << TargetName << "::" << Reg->getName();
+          }
+          CondStream << ")";
           break;
         }
         }
@@ -881,9 +889,16 @@ void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &OS,
           if (CompressOrUncompress) {
             // Fixed register has been validated at pattern validation time.
             const Record *Reg = DestOperandMap[OpNo].RegRec;
-            CodeStream.indent(6)
-                << "OutInst.addOperand(MCOperand::createReg(" << TargetName
-                << "::" << Reg->getName() << "));\n";
+            CodeStream.indent(6) << "OutInst.addOperand(MCOperand::createReg(";
+            if (Reg->isSubClassOf("RegisterByHwMode")) {
+              RegisterByHwMode(Reg, Target.getRegBank())
+                      .generateResolverLambda(CodeStream, Target.getHwModes(),
+                                              /*Indent=*/8)
+                  << "(HwModeId)";
+            } else {
+              CodeStream << TargetName << "::" << Reg->getName();
+            }
+            CodeStream << "));\n";
           }
         } break;
         }
diff --git a/llvm/utils/TableGen/DAGISelMatcherGen.cpp b/llvm/utils/TableGen/DAGISelMatcherGen.cpp
index dd4aefad02af7..af29c7e88fe5c 100644
--- a/llvm/utils/TableGen/DAGISelMatcherGen.cpp
+++ b/llvm/utils/TableGen/DAGISelMatcherGen.cpp
@@ -675,6 +675,10 @@ void MatcherGen::EmitResultLeafAsOperand(const TreePatternNode &N,
                                          NextRecordedOperandNo));
       ResultOps.push_back(NextRecordedOperandNo++);
       return;
+    } else if (Def->isSubClassOf("RegisterByHwMode")) {
+      PrintFatalError(Def->getLoc() /* TODO: N.getLoc() */,
+                      "RegisterByHwMode in SelectionDAG patterns "
+                      "not yet supported!");
     }
 
     if (Def->getName() == "zero_reg") {
diff --git a/llvm/utils/TableGen/PseudoLoweringEmitter.cpp b/llvm/utils/TableGen/PseudoLoweringEmitter.cpp
index 3cefdca1f1a14..23ad8e5ab1ec9 100644
--- a/llvm/utils/TableGen/PseudoLoweringEmitter.cpp
+++ b/llvm/utils/TableGen/PseudoLoweringEmitter.cpp
@@ -76,6 +76,7 @@ void PseudoLoweringEmitter::addOperandMapping(
     // Physical register reference. Explicit check for the special case
     // "zero_reg" definition.
     if (DI->getDef()->isSubClassOf("Register") ||
+        DI->getDef()->isSubClassOf("RegisterByHwMode") ||
         DI->getDef()->getName() == "zero_reg") {
       auto &Entry = OperandMap[MIOpNo];
       Entry.Kind = OpData::Reg;
@@ -256,10 +257,16 @@ void PseudoLoweringEmitter::emitLoweringEmitter(raw_ostream &o) {
             const Record *Reg = Expansion.OperandMap[MIOpNo + i].RegRec;
             o << "    Inst.addOperand(MCOperand::createReg(";
             // "zero_reg" is special.
-            if (Reg->getName() == "zero_reg")
+            if (Reg->getName() == "zero_reg") {
               o << "0";
-            else
+            } else if (Reg->isSubClassOf("RegisterByHwMode")) {
+              RegisterByHwMode(Reg, Target.getRegBank())
+                      .generateResolverLambda(o, Target.getHwModes(),
+                                              /*Indent=*/6)
+                  << "(STI->getHwMode(MCSubtargetInfo::HwMode_RegInfo))";
+            } else {
               o << Reg->getValueAsString("Namespace") << "::" << Reg->getName();
+            }
             o << "));\n";
             break;
           }
diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index 27299fdda3e0b..882f882c2100d 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -1856,6 +1856,7 @@ 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("Register") ||
                  P.second->isSubClassOf("SubRegRange") ||
                  P.second->isSubClassOf("RegisterClassLike")) {
         RegInfoModes |= (1 << (P.first - 1));



More information about the llvm-commits mailing list