[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:
parent
f9e002158c
commit
3459bb4f27
@ -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";
|
||||
}
|
||||
|
||||
@ -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>;
|
||||
|
||||
92
llvm/test/TableGen/Common/RegisterByHwModeCommon.td
Normal file
92
llvm/test/TableGen/Common/RegisterByHwModeCommon.td
Normal 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;
|
||||
}
|
||||
@ -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];
|
||||
}
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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; }
|
||||
|
||||
|
||||
348
llvm/test/TableGen/RegisterByHwMode.td
Normal file
348
llvm/test/TableGen/RegisterByHwMode.td
Normal 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];
|
||||
}
|
||||
69
llvm/test/TableGen/RegisterByHwModeErrors.td
Normal file
69
llvm/test/TableGen/RegisterByHwModeErrors.td
Normal 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; }
|
||||
@ -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"
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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") {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user