[TableGen] Introduce RegisterByHwMode

This is useful for `InstAlias` where a fixed register may depend on the
HwMode. The motivating use case for this is the RISC-V RVY ISA where
certain instructions mnemonics are remapped to take a different
register class depending on the HwMode and can be used as follows:
```
def NullReg : RegisterByHwMode<PtrRC, [RV32I, RV64I, RV64Y, RV64Y],
                                      [X0,    X0,    X0_Y,  X0_Y]>;
```

Pull Request: https://github.com/llvm/llvm-project/pull/175227
This commit is contained in:
Alexander Richardson 2026-02-18 17:23:10 -08:00 committed by GitHub
parent f9e002158c
commit 3459bb4f27
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 813 additions and 47 deletions

View File

@ -108,8 +108,8 @@ private:
// namespace scope.
class NamespaceEmitter {
public:
NamespaceEmitter(raw_ostream &OS, StringRef NameUntrimmed)
: Name(trim(NameUntrimmed).str()), OS(OS) {
NamespaceEmitter(raw_ostream &OS, const Twine &NameUntrimmed)
: Name(trim(NameUntrimmed.str()).str()), OS(OS) {
if (!Name.empty())
OS << "namespace " << Name << " {\n\n";
}

View File

@ -1160,6 +1160,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>;

View File

@ -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;
}

View File

@ -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,42 @@ 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: return MCOp.isReg() && MCOp.getReg() == MyTarget::RegisterByHwMode::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];
}

View File

@ -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,17 @@ 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() == MyTarget::RegisterByHwMode::getNullReg(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 +209,20 @@ 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(MyTarget::RegisterByHwMode::getNullReg(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 +297,14 @@ 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() == MyTarget::RegisterByHwMode::getNullReg(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: {

View File

@ -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; }

View File

@ -0,0 +1,348 @@
// 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-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-asm-writer -I %p/../../include -I %S %s -o - | FileCheck --check-prefix=ASMWRITER %s
/// The RegInfo output is split over multiple files:
// RUN: llvm-tblgen --gen-register-info -I %p/../../include -I %S %s -o %t.inc
// RUN: FileCheck %s --input-file=%tEnums.inc --check-prefix=REGINFO-HEADER
// RUN: FileCheck %s --input-file=%tMCDesc.inc --check-prefix=REGINFO-MCDESC
/// 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 -
// REGINFO-HEADER-LABEL: // Registers by HwMode
// REGINFO-HEADER-NEXT: class MCRegister;
// REGINFO-HEADER-NEXT: namespace MyTarget::RegisterByHwMode {
// REGINFO-HEADER-EMPTY:
// REGINFO-HEADER-NEXT: LLVM_READONLY MCRegister getModeCountReg(unsigned HwMode);
// REGINFO-HEADER-NEXT: LLVM_READONLY MCRegister getNullReg(unsigned HwMode);
// REGINFO-HEADER-NEXT: LLVM_READONLY MCRegister getPtrRegFor64BitModesOnly(unsigned HwMode);
// REGINFO-HEADER-EMPTY:
// REGINFO-HEADER-NEXT: } // namespace MyTarget::RegisterByHwMode
// REGINFO-HEADER-EMPTY:
// REGINFO-HEADER-NEXT: } // namespace llvm
// REGINFO-MCDESC-LABEL: // Registers by HwMode
// REGINFO-MCDESC-NEXT: namespace MyTarget::RegisterByHwMode {
// REGINFO-MCDESC-EMPTY:
// REGINFO-MCDESC-NEXT: LLVM_READONLY MCRegister getModeCountReg(unsigned HwMode) {
// REGINFO-MCDESC-NEXT: switch (HwMode) {
// REGINFO-MCDESC-NEXT: case 0: return MyTarget::X0; // DefaultMode
// REGINFO-MCDESC-NEXT: case 1: return MyTarget::X1; // XPtr64
// REGINFO-MCDESC-NEXT: case 2: return MyTarget::X2; // YPtr32
// REGINFO-MCDESC-NEXT: case 3: return MyTarget::X3; // YPtr64
// REGINFO-MCDESC-NEXT: default: llvm_unreachable("Unhandled HwMode for Register ModeCountReg");
// REGINFO-MCDESC-NEXT: }
// REGINFO-MCDESC-NEXT: }
// REGINFO-MCDESC-NEXT: LLVM_READONLY MCRegister getNullReg(unsigned HwMode) {
// REGINFO-MCDESC-NEXT: switch (HwMode) {
// REGINFO-MCDESC-NEXT: case 0: return MyTarget::X0; // DefaultMode
// REGINFO-MCDESC-NEXT: case 1: return MyTarget::X0; // XPtr64
// REGINFO-MCDESC-NEXT: case 2: return MyTarget::Y0; // YPtr32
// REGINFO-MCDESC-NEXT: case 3: return MyTarget::Y0; // YPtr64
// REGINFO-MCDESC-NEXT: default: llvm_unreachable("Unhandled HwMode for Register NullReg");
// REGINFO-MCDESC-NEXT: }
// REGINFO-MCDESC-NEXT: }
// REGINFO-MCDESC-NEXT: LLVM_READONLY MCRegister getPtrRegFor64BitModesOnly(unsigned HwMode) {
// REGINFO-MCDESC-NEXT: switch (HwMode) {
// REGINFO-MCDESC-NEXT: case 1: return MyTarget::X1; // XPtr64
// REGINFO-MCDESC-NEXT: case 3: return MyTarget::Y1; // YPtr64
// REGINFO-MCDESC-NEXT: default: llvm_unreachable("Unhandled HwMode for Register PtrRegFor64BitModesOnly");
// REGINFO-MCDESC-NEXT: }
// REGINFO-MCDESC-NEXT: }
// REGINFO-MCDESC-EMPTY:
// REGINFO-MCDESC-NEXT: } // namespace MyTarget::RegisterByHwMode
// REGINFO-MCDESC-EMPTY:
// REGINFO-MCDESC-NEXT: } // namespace llvm
// 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 final;
// SUBTARGET-NEXT: unsigned getHwMode(enum HwModeType type = HwMode_Default) const final;
// 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) -
// ASMWRITER-NEXT: {AliasPatternCond::K_RegClassByHwMode, MyTarget::PtrRC},
// ASMWRITER-NEXT: {AliasPatternCond::K_RegClassByHwMode, MyTarget::PtrRC},
// ASMWRITER-NEXT: // (TEST_PTRREG EvenPtrRC:$dst, EvenPtrRC:$src) -
// ASMWRITER-NEXT: {AliasPatternCond::K_RegClassByHwMode, MyTarget::EvenPtrRC},
// ASMWRITER-NEXT: {AliasPatternCond::K_RegClassByHwMode, MyTarget::EvenPtrRC},
// ASMWRITER-NEXT: // (TEST_PTRREG NullReg, PtrRC:$src) -
// ASMWRITER-NEXT: {AliasPatternCond::K_Custom, 1/*NullReg*/},
// ASMWRITER-NEXT: {AliasPatternCond::K_RegClassByHwMode, MyTarget::PtrRC},
// ASMWRITER-NEXT: // (TEST_PTRREG NullReg, EvenPtrRC:$src) -
// ASMWRITER-NEXT: {AliasPatternCond::K_Custom, 1/*NullReg*/},
// ASMWRITER-NEXT: {AliasPatternCond::K_RegClassByHwMode, MyTarget::EvenPtrRC},
// ASMWRITER-NEXT: // (TEST_PTRREG PtrRegFor64BitModesOnly, PtrRC:$src) - 8
// ASMWRITER-NEXT: {AliasPatternCond::K_Custom, 2/*PtrRegFor64BitModesOnly*/},
// ASMWRITER-NEXT: {AliasPatternCond::K_RegClassByHwMode, MyTarget::PtrRC},
// ASMWRITER-NEXT: // (TEST_XREG X0, XRegs:$src) -
// ASMWRITER-NEXT: {AliasPatternCond::K_Reg, MyTarget::X0},
// ASMWRITER-NEXT: {AliasPatternCond::K_RegClass, MyTarget::XRegsRegClassID},
// ASMWRITER-NEXT: // (TEST_XREG X0, EvenXRegs:$src) -
// ASMWRITER-NEXT: {AliasPatternCond::K_Reg, MyTarget::X0},
// ASMWRITER-NEXT: {AliasPatternCond::K_RegClass, MyTarget::EvenXRegsRegClassID},
// ASMWRITER-NEXT: // (TEST_XREG ModeCountReg, XRegs:$src) -
// ASMWRITER-NEXT: {AliasPatternCond::K_Custom, 3/*ModeCountReg*/},
// ASMWRITER-NEXT: {AliasPatternCond::K_RegClass, MyTarget::XRegsRegClassID},
// ASMWRITER-NEXT: // (TEST_YREG Y0, YRegs:$src) -
// ASMWRITER-NEXT: {AliasPatternCond::K_Reg, MyTarget::Y0},
// ASMWRITER-NEXT: {AliasPatternCond::K_RegClass, MyTarget::YRegsRegClassID},
// ASMWRITER-NEXT: // (TEST_YREG Y0, EvenYRegs:$src) -
// 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: return MCOp.isReg() && MCOp.getReg() == MyTarget::RegisterByHwMode::getNullReg(STI.getHwMode(MCSubtargetInfo::HwMode_RegInfo));
// ASMWRITER-NEXT: }
// ASMWRITER-NEXT: case 2: {
// ASMWRITER-NEXT: return MCOp.isReg() && MCOp.getReg() == MyTarget::RegisterByHwMode::getPtrRegFor64BitModesOnly(STI.getHwMode(MCSubtargetInfo::HwMode_RegInfo));
// ASMWRITER-NEXT: }
// ASMWRITER-NEXT: case 3: {
// ASMWRITER-NEXT: return MCOp.isReg() && MCOp.getReg() == MyTarget::RegisterByHwMode::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__regPtrRegFor64BitModesOnly__RegByHwMode_PtrRC1_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__regPtrRegFor64BitModesOnly__RegByHwMode_PtrRC1_0
// ASMMATCHER-NEXT: { CVT_regPtrRegFor64BitModesOnly, 0, CVT_95_addRegOperands, 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(MyTarget::RegisterByHwMode::getModeCountReg(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(MyTarget::RegisterByHwMode::getNullReg(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: case CVT_regPtrRegFor64BitModesOnly:
// ASMMATCHER-NEXT: Inst.addOperand(MCOperand::createReg(MyTarget::RegisterByHwMode::getPtrRegFor64BitModesOnly(STI->getHwMode(MCSubtargetInfo::HwMode_RegInfo))));
// 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_64_only */, MyTarget::TEST_PTRREG, Convert__regPtrRegFor64BitModesOnly__RegByHwMode_PtrRC1_0, AMFBS_None, { MCK_RegByHwMode_PtrRC }, },
// 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)>;
/// Test where we don't have valid values for RegisterByHwMode cases:
def PtrRegFor64BitModesOnly : RegisterByHwMode<PtrRC, [XPtr64, YPtr64], [X1, Y1]>;
let Predicates = [Is64Bit] in
def TEST_64_ONLY : InstAlias<"test_64_only $src", (TEST_PTRREG PtrRegFor64BitModesOnly, PtrRC:$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-NEXT: Inst.clear();
// PSEUDO-NEXT: switch (MI->getOpcode()) {
// PSEUDO-NEXT: default: return false;
// PSEUDO-NEXT: case MyTarget::Pseudo64Only: {
// PSEUDO-NEXT: MCOperand MCOp;
// PSEUDO-NEXT: Inst.setOpcode(MyTarget::TEST_PTRREG);
// PSEUDO-NEXT: // Operand: dst
// PSEUDO-NEXT: Inst.addOperand(MCOperand::createReg(MyTarget::RegisterByHwMode::getPtrRegFor64BitModesOnly(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::PseudoPtr: {
// PSEUDO-NEXT: MCOperand MCOp;
// PSEUDO-NEXT: Inst.setOpcode(MyTarget::TEST_PTRREG);
// PSEUDO-NEXT: // Operand: dst
// PSEUDO-NEXT: Inst.addOperand(MCOperand::createReg(MyTarget::RegisterByHwMode::getNullReg(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(MyTarget::RegisterByHwMode::getModeCountReg(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)>;
let Predicates = [Is64Bit] in
def Pseudo64Only : Pseudo<(outs), (ins PtrRC:$rs1)>,
PseudoInstExpansion<(TEST_PTRREG PtrRegFor64BitModesOnly, 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];
}

View File

@ -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; }

View File

@ -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,17 @@ 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())
.emitResolverCall(
CvtOS, "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"

View File

@ -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,25 @@ 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 << " return MCOp.isReg() && MCOp.getReg() == ";
RegisterByHwMode(Rec, Target.getRegBank())
.emitResolverCall(O,
"STI.getHwMode(MCSubtargetInfo::HwMode_RegInfo)");
O << ";\n";
} else {
// Normal MCOperandPredicate code snippet, emit verbatim.
O << Rec->getValueAsString("MCOperandPredicate") << "\n";
}
O << " }\n";
}
O << " }\n"
<< "}\n\n";

View File

@ -1802,7 +1802,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();

View File

@ -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(

View File

@ -2650,6 +2650,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) {

View File

@ -837,6 +837,13 @@ public:
/// 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

View File

@ -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,41 @@ 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");
Namespace = RegBank.getRegClasses().front().Namespace;
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());
}
}
void RegisterByHwMode::emitResolverCall(raw_ostream &OS,
const Twine &HwMode) const {
OS << Namespace << "::RegisterByHwMode::get" << Def->getName() << "("
<< HwMode << ")";
}
raw_ostream &llvm::operator<<(raw_ostream &OS, const ValueTypeByHwMode &T) {
T.writeToStream(OS);
return OS;

View File

@ -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;
/// Resolve the register by calling <target>::RegByHwMode::get<name>(HwMode).
void emitResolverCall(raw_ostream &OS, const Twine &HwMode) const;
private:
StringRef Namespace;
};
} // namespace llvm
#endif // LLVM_UTILS_TABLEGEN_COMMON_INFOBYHWMODE_H

View File

@ -166,13 +166,12 @@ public:
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,
@ -256,12 +255,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;
@ -762,8 +762,14 @@ 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())
.emitResolverCall(CondStream, "HwModeId");
} else {
CondStream << TargetName << "::" << Reg->getName();
}
CondStream << ")";
break;
}
}
@ -880,9 +886,14 @@ 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())
.emitResolverCall(CodeStream, "HwModeId");
} else {
CodeStream << TargetName << "::" << Reg->getName();
}
CodeStream << "));\n";
}
} break;
}

View File

@ -671,6 +671,10 @@ void MatcherGen::EmitResultLeafAsOperand(const TreePatternNode &N,
new EmitRegisterMatcher(Reg, N.getType(0), 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") {

View File

@ -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,15 @@ 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())
.emitResolverCall(
o, "STI->getHwMode(MCSubtargetInfo::HwMode_RegInfo)");
} else {
o << Reg->getValueAsString("Namespace") << "::" << Reg->getName();
}
o << "));\n";
break;
}

View File

@ -193,6 +193,24 @@ void RegisterInfoEmitter::runEnums(raw_ostream &OS, raw_ostream &MainOS,
}
OS << "};\n";
}
// Note: While these functions are not enums, we need to define them in the
// same place as <TARGET>::<REG>, so that the assembly parser can use them
// without having to include <TARGETT>RegisterInfo.h, which may not be
// possible due to build system structure.
ArrayRef<const Record *> RegisterByHwModeRecords =
Records.getAllDerivedDefinitions("RegisterByHwMode");
if (!RegisterByHwModeRecords.empty()) {
OS << "// Registers by HwMode\n";
OS << "class MCRegister;\n";
NamespaceEmitter RegClassNS(OS, Namespace + "::RegisterByHwMode");
// Define the getters for the RegisterByHwMode in one globally accessible
// location so they can be reused by all callers.
for (const Record *Rec : RegisterByHwModeRecords) {
OS << "LLVM_READONLY MCRegister get" << Rec->getName()
<< "(unsigned HwMode);\n";
}
}
}
static void printInt(raw_ostream &OS, int Val) { OS << Val; }
@ -1148,6 +1166,36 @@ void RegisterInfoEmitter::runMCDesc(raw_ostream &OS, raw_ostream &MainOS,
EmitRegMapping(OS, Regs, false);
OS << "}\n\n";
// Emit the register by HwMode (if present).
ArrayRef<const Record *> RegisterByHwModeRecords =
Records.getAllDerivedDefinitions("RegisterByHwMode");
if (!RegisterByHwModeRecords.empty()) {
OS << "// Registers by HwMode\n";
NamespaceEmitter RegClassNS(OS, RegisterClasses.front().Namespace +
"::RegisterByHwMode");
unsigned NumModes = Target.getHwModes().getNumModeIds();
for (const Record *Rec : RegisterByHwModeRecords) {
RegisterByHwMode RegByMode(Rec, RegBank);
OS << "LLVM_READONLY MCRegister get" << Rec->getName()
<< "(unsigned HwMode) {\n";
OS << indent(2) << "switch (HwMode) {\n";
for (unsigned M = 0; M < NumModes; ++M) {
if (RegByMode.hasMode(M)) {
const CodeGenRegister *R = RegByMode.get(M);
OS << indent(2) << "case " << M << ": return "
<< getQualifiedName(R->TheDef) << "; // "
<< Target.getHwModes().getModeName(M, true) << "\n";
}
}
OS << indent(2)
<< "default: llvm_unreachable(\"Unhandled HwMode for Register "
<< Rec->getName() << "\");\n"
<< indent(2) << "}\n"
<< "}\n";
}
}
}
void RegisterInfoEmitter::runTargetHeader(raw_ostream &OS, raw_ostream &MainOS,

View File

@ -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));