[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