diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td index 315de55b75510..61388c4902eb4 100644 --- a/llvm/include/llvm/Target/Target.td +++ b/llvm/include/llvm/Target/Target.td @@ -1078,6 +1078,16 @@ class RegisterOperand Register GIZeroRegister = ?; } +/// RegisterByHwMode - Useful for InstAliases with a hardcoded register operand +/// where the operand type is a RegClassByHwMode. +class RegisterByHwMode Modes, + list Registers> + : HwModeSelect, RegisterOperand { + list 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; def i8imm : Operand; diff --git a/llvm/test/TableGen/Common/RegisterByHwModeCommon.td b/llvm/test/TableGen/Common/RegisterByHwModeCommon.td new file mode 100644 index 0000000000000..0fb775aa53565 --- /dev/null +++ b/llvm/test/TableGen/Common/RegisterByHwModeCommon.td @@ -0,0 +1,92 @@ +include "llvm/Target/Target.td" + +/// A minimal reduced version of the RISC-V RVY register variants where pointers +/// use either Xn or Xn_Y registers depending on CapMode and 64-bit predicates. +/// Define HWModes for the full cross-product here since we can only match one +/// HWMode at any given time. +def Is32Bit : Predicate<"!Subtarget->is64Bit()">; +def Is64Bit : Predicate<"Subtarget->is64Bit()">; +def UseYRegForPtr : Predicate<"Subtarget->useYRegForPtr()">; +def UseXRegForPtr : Predicate<"!Subtarget->useYRegForPtr()">; +defvar XPtr32 = DefaultMode; +def XPtr64 : HwMode<[Is64Bit, UseXRegForPtr]>; +def YPtr32 : HwMode<[Is32Bit, UseYRegForPtr]>; +def YPtr64 : HwMode<[Is64Bit, UseYRegForPtr]>; + +class MyReg : Register { + 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; + +def NullReg : RegisterByHwMode; + +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; +} diff --git a/llvm/test/TableGen/RegClassByHwModeAlias.td b/llvm/test/TableGen/RegClassByHwModeAlias.td index 726bd3a0a5a49..2b83e40ff4a78 100644 --- a/llvm/test/TableGen/RegClassByHwModeAlias.td +++ b/llvm/test/TableGen/RegClassByHwModeAlias.td @@ -11,6 +11,7 @@ def EvenXRegs : RegisterClass<"MyTarget", [i64], 64, (add X0, X2, X4, X6)>; def EvenYRegs : RegisterClass<"MyTarget", [i64], 64, (add Y0, Y2, Y4, Y6)>; def PtrRC : RegClassByHwMode<[PtrX, PtrY], [XRegs, YRegs]>; def EvenPtrRC : RegClassByHwMode<[PtrX, PtrY], [EvenXRegs, EvenYRegs]>; +def NullReg : RegisterByHwMode; 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]; +} diff --git a/llvm/test/TableGen/RegClassByHwModeCompressPat.td b/llvm/test/TableGen/RegClassByHwModeCompressPat.td index d642584e52b37..cbe686848c4c4 100644 --- a/llvm/test/TableGen/RegClassByHwModeCompressPat.td +++ b/llvm/test/TableGen/RegClassByHwModeCompressPat.td @@ -6,7 +6,7 @@ def IsPtr64 : Predicate<"Subtarget->isPtr64()">; defvar Ptr32 = DefaultMode; def Ptr64 : HwMode<[IsPtr64]>; def PtrRC : RegClassByHwMode<[Ptr32, Ptr64], [XRegs, YRegs]>; - +def NullReg : RegisterByHwMode; 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: { diff --git a/llvm/test/TableGen/RegClassByHwModeErrors.td b/llvm/test/TableGen/RegClassByHwModeErrors.td index c7731312e28a6..833224074ac34 100644 --- a/llvm/test/TableGen/RegClassByHwModeErrors.td +++ b/llvm/test/TableGen/RegClassByHwModeErrors.td @@ -66,8 +66,7 @@ def PTR_ZERO_SMALL : TestInstruction { /// This should fail since X0 is not necessarily part of PtrRC. def : CompressPat<(PTR_MOV PtrRC:$dst, X0), (PTR_ZERO_SMALL PtrRC:$dst)>; -// CHECK: [[#@LINE-2]]:1: error: cannot resolve HwMode for PtrRC -// CHECK: Common.td:7:5: note: PtrRC defined here +// CHECK: [[#@LINE-2]]:1: error: Error in Dag '(PTR_MOV PtrRC:$dst, X0)': Register 'X0' is not in register class 'PtrRC' def MyTargetISA : InstrInfo; def MyTarget : Target { let InstructionSet = MyTargetISA; } diff --git a/llvm/test/TableGen/RegisterByHwMode.td b/llvm/test/TableGen/RegisterByHwMode.td new file mode 100644 index 0000000000000..51f8717c70060 --- /dev/null +++ b/llvm/test/TableGen/RegisterByHwMode.td @@ -0,0 +1,344 @@ +// 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-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-NEXT: } // end namespace MyTarget::RegisterByHwMode +// REGINFO-HEADER-EMPTY: +// REGINFO-HEADER-NEXT: } // end namespace llvm + +// REGINFO-MCDESC-LABEL: // Registers by HwMode +// REGINFO-MCDESC-NEXT: namespace MyTarget::RegisterByHwMode { +// 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-NEXT: } // end namespace MyTarget::RegisterByHwMode +// REGINFO-MCDESC-EMPTY: +// REGINFO-MCDESC-NEXT: } // end 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 override; +// SUBTARGET-NEXT: unsigned getHwMode(enum HwModeType type = HwMode_Default) const override; + +// SUBTARGET-LABEL: unsigned MyTargetGenSubtargetInfo::getHwModeSet() const { +// SUBTARGET{LITERAL}:[[maybe_unused]] const auto *Subtarget = +// SUBTARGET-NEXT: static_cast(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<getHwMode(MCSubtargetInfo::HwMode_RegInfo)))); +// ASMMATCHER-NEXT: break; +// ASMMATCHER-NEXT: case CVT_95_Reg: +// ASMMATCHER-NEXT: static_cast(*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(*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; +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; +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 : 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; +def MyTargetISA : InstrInfo; +def MyTargetAsmWriter : AsmWriter { + int PassSubtarget = 1; +} +def MyTarget : Target { + let InstructionSet = MyTargetISA; + let AssemblyWriters = [MyTargetAsmWriter]; +} diff --git a/llvm/test/TableGen/RegisterByHwModeErrors.td b/llvm/test/TableGen/RegisterByHwModeErrors.td new file mode 100644 index 0000000000000..a1b7b4cc6e8b7 --- /dev/null +++ b/llvm/test/TableGen/RegisterByHwModeErrors.td @@ -0,0 +1,69 @@ +// RUN: rm -rf %t && split-file %s %t +// RUN: not llvm-tblgen --gen-asm-matcher -I %t -I %p/../../include -I %S \ +// RUN: %t/bad-regclass.td -o /dev/null 2>&1 | FileCheck %t/bad-regclass.td --implicit-check-not="error:" +// RUN: not llvm-tblgen --gen-asm-matcher -I %t -I %p/../../include -I %S \ +// RUN: %t/bad-regclass-by-mode.td -o /dev/null 2>&1 | FileCheck %t/bad-regclass-by-mode.td --implicit-check-not="error:" +// RUN: not llvm-tblgen --gen-asm-matcher -I %t -I %p/../../include -I %S \ +// RUN: %t/bad-not-regclass.td -o /dev/null 2>&1 | FileCheck %t/bad-not-regclass.td --implicit-check-not="error:" +// RUN: not llvm-tblgen --gen-asm-matcher -I %t -I %p/../../include -I %S \ +// RUN: %t/duplicate-entry.td -o /dev/null 2>&1 | FileCheck %t/duplicate-entry.td --implicit-check-not="error:" +// RUN: not llvm-tblgen --gen-dag-isel -I %t -I %p/../../include -I %S \ +// RUN: %t/isel-not-supported-yet.td -o /dev/null 2>&1 | FileCheck %t/isel-not-supported-yet.td --implicit-check-not="error:" + +//--- bad-regclass.td +include "Common/RegisterByHwModeCommon.td" +def BadReg : RegisterByHwMode; +// 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; +// 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; +// 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; +// CHECK: [[#@LINE-1]]:5: error: duplicate Register for HwMode DefaultMode: X0 + +// Need to define an instruction that uses BadReg to get the diagnostic +def : InstAlias<"test $src", (TEST_XREG BadReg, XRegs:$src)>; +def MyTargetISA : InstrInfo; +def MyTarget : Target { let InstructionSet = MyTargetISA; } + +//--- isel-not-supported-yet.td +include "Common/RegisterByHwModeCommon.td" +/// Output for -gen-dag-isel is not supported yet, check that we get an +/// error instead of crashing. +def int_with_null_reg : Intrinsic<[llvm_i64_ty], [], [IntrNoMem, IntrWillReturn]>; +def : Pat<(int_with_null_reg), (TEST_PTRREG NullReg)>; +// TODO: We don't track source locations of the DAG nodes, so the error location is bad. +// CHECK: RegisterByHwModeCommon.td:55:5: error: RegisterByHwMode in SelectionDAG patterns not yet supported! + +def MyTargetISA : InstrInfo; +def MyTarget : Target { let InstructionSet = MyTargetISA; } diff --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp index e6085af5aa91e..1c14de996d2d7 100644 --- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp +++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp @@ -451,8 +451,9 @@ struct MatchableInfo { /// the operand. ImmOperand, - /// RegOperand - This represents a fixed register that is dumped in. - RegOperand + /// RegOperand - This represents a fixed register (potentially depending + /// on the HwMode) that is dumped in. + RegOperand, } Kind; /// Tuple containing the index of the (earlier) result operand that should @@ -2263,12 +2264,14 @@ emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, } case MatchableInfo::ResOperand::RegOperand: { std::string Reg, Name; + bool IsRegByHwMode = false; if (!OpInfo.Register) { Name = "reg0"; Reg = "0"; } else { Reg = getQualifiedName(OpInfo.Register); Name = "reg" + OpInfo.Register->getName().str(); + IsRegByHwMode = OpInfo.Register->isSubClassOf("RegisterByHwMode"); } Signature += "__" + Name; Name = "CVT_" + Name; @@ -2281,10 +2284,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" diff --git a/llvm/utils/TableGen/AsmWriterEmitter.cpp b/llvm/utils/TableGen/AsmWriterEmitter.cpp index edca87504a2f7..690987ab1dd83 100644 --- a/llvm/utils/TableGen/AsmWriterEmitter.cpp +++ b/llvm/utils/TableGen/AsmWriterEmitter.cpp @@ -1001,9 +1001,22 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { break; } - StringRef Reg = CGA.ResultOperands[i].getRegister()->getName(); - IAP.addCond(std::string( - formatv("AliasPatternCond::K_Reg, {}::{}", Namespace, Reg))); + const Record *Rec = CGA.ResultOperands[i].getRegister(); + StringRef Reg = Rec->getName(); + if (Rec->isSubClassOf("RegisterByHwMode")) { + // Use a custom predicate to handle RegisterByHwMode since there + // is no way to handle this in the generic code. + unsigned &Entry = MCOpPredicateMap[Rec]; + if (!Entry) { + MCOpPredicates.push_back(Rec); + Entry = MCOpPredicates.size(); + } + IAP.addCond(std::string( + formatv("AliasPatternCond::K_Custom, {}/*{}*/", Entry, Reg))); + } else { + IAP.addCond(std::string( + formatv("AliasPatternCond::K_Reg, {}::{}", Namespace, Reg))); + } break; } @@ -1297,12 +1310,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"; diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp index 3ed5fe5d806ae..bf85493e3f731 100644 --- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp +++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp @@ -1802,7 +1802,7 @@ static TypeSetByHwMode getTypeForRegClassByHwMode(const CodeGenTarget &T, const Record *R, ArrayRef Loc) { TypeSetByHwMode TypeSet; - RegClassByHwMode Helper(R, T.getHwModes(), T.getRegBank()); + RegClassByHwMode Helper(R, T.getRegBank()); for (auto [ModeID, RegClass] : Helper) { ArrayRef RegClassVTs = RegClass->getValueTypes(); diff --git a/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp b/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp index d3050c0553114..407cd7826ff48 100644 --- a/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp +++ b/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp @@ -47,8 +47,12 @@ matchSimpleOperand(const Init *Arg, const StringInit *ArgName, const Record *Op, if (const auto *ArgDef = dyn_cast(Arg)) { const Record *ArgRec = ArgDef->getDef(); - // Match 'RegClass:$name' or 'RegOp:$name'. + // Match 'RegClass:$name', 'RegOp:$name', or RegisterByHwMode. if (const Record *ArgRC = T.getInitValueAsRegClassLike(Arg)) { + if (ArgRec->isSubClassOf("RegisterByHwMode")) { + // Note: constraints are validated in RegisterByHwMode ctor later. + return ResultOperand::createRegister(ArgRec); + } if (ArgRC->isSubClassOf("RegisterClass")) { if (!OpRC->isSubClassOf("RegisterClass") || !T.getRegisterClass(OpRC, Loc).hasSubClass( diff --git a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp index 65a2594859e69..aece50ff33ecf 100644 --- a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp +++ b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp @@ -2548,6 +2548,37 @@ CodeGenRegBank::getRegClassForRegister(const Record *R) { return FoundRC; } +bool CodeGenRegBank::regClassContainsReg(const Record *RegClassDef, + const Record *RegDef, + ArrayRef Loc) { + // Check all four combinations of Register[ByHwMode] X RegClass[ByHwMode], + // starting with the two RegClassByHwMode cases. + unsigned NumModes = CGH.getNumModeIds(); + std::optional RegByMode; + CodeGenRegister *Reg = nullptr; + if (RegDef->isSubClassOf("RegisterByHwMode")) + RegByMode = RegisterByHwMode(RegDef, *this); + else + Reg = getReg(RegDef); + if (RegClassDef->isSubClassOf("RegClassByHwMode")) { + RegClassByHwMode RC(RegClassDef, *this); + for (unsigned M = 0; M < NumModes; ++M) { + if (RC.hasMode(M) && !RC.get(M)->contains(Reg ? Reg : RegByMode->get(M))) + return false; + } + return true; + } + // Otherwise we have a plain register class, check Register[ByHwMode] + CodeGenRegisterClass *RC = getRegClass(RegClassDef, Loc); + if (Reg) + return RC->contains(Reg); + for (unsigned M = 0; M < NumModes; ++M) { + if (RegByMode->hasMode(M) && !RC->contains(RegByMode->get(M))) + return false; + } + return true; // RegByMode contained for all possible modes. +} + const CodeGenRegisterClass * CodeGenRegBank::getMinimalPhysRegClass(const Record *RegRecord, ValueTypeByHwMode *VT) { diff --git a/llvm/utils/TableGen/Common/CodeGenRegisters.h b/llvm/utils/TableGen/Common/CodeGenRegisters.h index a3ad0b797a704..eaabe48a70d25 100644 --- a/llvm/utils/TableGen/Common/CodeGenRegisters.h +++ b/llvm/utils/TableGen/Common/CodeGenRegisters.h @@ -825,6 +825,13 @@ class CodeGenRegBank { /// return the superclass. Otherwise return null. const CodeGenRegisterClass *getRegClassForRegister(const Record *R); + /// Returns whether \p RegClass contains register \p Reg, handling + /// RegClassByHwMode and RegisterByHwMode correctly. + /// This should be preferred instead of + /// `RegBank.getRegClass(RC).contains(RegBank.getReg(R))`. + bool regClassContainsReg(const Record *RegClass, const Record *RegDef, + ArrayRef Loc = {}); + // Analog of TargetRegisterInfo::getMinimalPhysRegClass. Unlike // getRegClassForRegister, this tries to find the smallest class containing // the physical register. If \p VT is specified, it will only find classes diff --git a/llvm/utils/TableGen/Common/InfoByHwMode.cpp b/llvm/utils/TableGen/Common/InfoByHwMode.cpp index 539c24a530a7d..a666148481d36 100644 --- a/llvm/utils/TableGen/Common/InfoByHwMode.cpp +++ b/llvm/utils/TableGen/Common/InfoByHwMode.cpp @@ -180,9 +180,10 @@ void RegSizeInfoByHwMode::writeToStream(raw_ostream &OS) const { OS << '}'; } -RegClassByHwMode::RegClassByHwMode(const Record *R, const CodeGenHwModes &CGH, +RegClassByHwMode::RegClassByHwMode(const Record *R, const CodeGenRegBank &RegBank) : InfoByHwMode(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(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 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; diff --git a/llvm/utils/TableGen/Common/InfoByHwMode.h b/llvm/utils/TableGen/Common/InfoByHwMode.h index 4126cb9bc585a..2a2e5c23d3721 100644 --- a/llvm/utils/TableGen/Common/InfoByHwMode.h +++ b/llvm/utils/TableGen/Common/InfoByHwMode.h @@ -28,6 +28,7 @@ namespace llvm { class CodeGenRegBank; +class CodeGenRegister; class CodeGenRegisterClass; class Record; class raw_ostream; @@ -254,11 +255,20 @@ struct EncodingInfoByHwMode : public InfoByHwMode { struct RegClassByHwMode : public InfoByHwMode { public: - RegClassByHwMode(const Record *R, const CodeGenHwModes &CGH, - const CodeGenRegBank &RegBank); + RegClassByHwMode(const Record *R, const CodeGenRegBank &RegBank); RegClassByHwMode() = default; }; +struct RegisterByHwMode : public InfoByHwMode { + RegisterByHwMode(const Record *R, CodeGenRegBank &RegBank); + RegisterByHwMode() = default; + /// Resolve the register by calling ::RegByHwMode::get(HwMode). + void emitResolverCall(raw_ostream &OS, const Twine &HwMode) const; + +private: + StringRef Namespace; +}; + } // namespace llvm #endif // LLVM_UTILS_TABLEGEN_COMMON_INFOBYHWMODE_H diff --git a/llvm/utils/TableGen/CompressInstEmitter.cpp b/llvm/utils/TableGen/CompressInstEmitter.cpp index 1dce0baa5f898..1908df85dd3f7 100644 --- a/llvm/utils/TableGen/CompressInstEmitter.cpp +++ b/llvm/utils/TableGen/CompressInstEmitter.cpp @@ -165,13 +165,12 @@ class CompressInstEmitter { bool CompressInstEmitter::validateRegister(const Record *Reg, const Record *RegClass, ArrayRef Loc) { - assert(Reg->isSubClassOf("Register") && "Reg record should be a Register"); + assert((Reg->isSubClassOf("Register") || + Reg->isSubClassOf("RegisterByHwMode")) && + "Reg record should be a Register"); assert(RegClass->isSubClassOf("RegisterClassLike") && "RegClass record should be RegisterClassLike"); - const CodeGenRegisterClass &RC = Target.getRegisterClass(RegClass, Loc); - const CodeGenRegister *R = Target.getRegBank().getReg(Reg); - assert(R != nullptr && "Register not defined!!"); - return RC.contains(R); + return Target.getRegBank().regClassContainsReg(RegClass, Reg, Loc); } bool CompressInstEmitter::validateTypes(const Record *DagOpType, @@ -255,12 +254,13 @@ void CompressInstEmitter::addDagOperandMapping(const Record *Rec, "' and Dag operand count mismatch"); if (const auto *DI = dyn_cast(Dag->getArg(DAGOpNo))) { - if (DI->getDef()->isSubClassOf("Register")) { + if (DI->getDef()->isSubClassOf("Register") || + DI->getDef()->isSubClassOf("RegisterByHwMode")) { // Check if the fixed register belongs to the Register class. if (!validateRegister(DI->getDef(), OpndRec, Rec->getLoc())) PrintFatalError(Rec->getLoc(), "Error in Dag '" + Dag->getAsString() + - "'Register: '" + DI->getDef()->getName() + + "': Register '" + DI->getDef()->getName() + "' is not in register class '" + OpndRec->getName() + "'"); OperandMap[OpNo].Kind = OpData::Reg; @@ -763,8 +763,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; } } @@ -881,9 +887,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; } diff --git a/llvm/utils/TableGen/DAGISelMatcherGen.cpp b/llvm/utils/TableGen/DAGISelMatcherGen.cpp index e8f146264b1ec..003a732234fad 100644 --- a/llvm/utils/TableGen/DAGISelMatcherGen.cpp +++ b/llvm/utils/TableGen/DAGISelMatcherGen.cpp @@ -675,6 +675,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") { diff --git a/llvm/utils/TableGen/PseudoLoweringEmitter.cpp b/llvm/utils/TableGen/PseudoLoweringEmitter.cpp index 3cefdca1f1a14..76806c32033d5 100644 --- a/llvm/utils/TableGen/PseudoLoweringEmitter.cpp +++ b/llvm/utils/TableGen/PseudoLoweringEmitter.cpp @@ -76,6 +76,7 @@ void PseudoLoweringEmitter::addOperandMapping( // Physical register reference. Explicit check for the special case // "zero_reg" definition. if (DI->getDef()->isSubClassOf("Register") || + DI->getDef()->isSubClassOf("RegisterByHwMode") || DI->getDef()->getName() == "zero_reg") { auto &Entry = OperandMap[MIOpNo]; Entry.Kind = OpData::Reg; @@ -256,10 +257,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; } diff --git a/llvm/utils/TableGen/RegisterInfoEmitter.cpp b/llvm/utils/TableGen/RegisterInfoEmitter.cpp index 02fd8648302f1..8d010d3235b2b 100644 --- a/llvm/utils/TableGen/RegisterInfoEmitter.cpp +++ b/llvm/utils/TableGen/RegisterInfoEmitter.cpp @@ -204,6 +204,27 @@ void RegisterInfoEmitter::runEnums(raw_ostream &OS, raw_ostream &MainOS, OS << "} // end namespace " << Namespace << '\n'; OS << '\n'; + // Note: While these functions are not enums, we need to define them in the + // same place as ::, so that the assembly parser can use them + // without having to include RegisterInfo.h, which may not be + // possible due to build system structure. + ArrayRef RegisterByHwModeRecords = + Records.getAllDerivedDefinitions("RegisterByHwMode"); + if (!RegisterByHwModeRecords.empty()) { + OS << "// Registers by HwMode\n"; + OS << "class MCRegister;\n"; + OS << "namespace " << RegisterClasses.front().Namespace + << "::RegisterByHwMode {\n"; + // 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"; + } + OS << "} // end namespace " << RegisterClasses.front().Namespace + << "::RegisterByHwMode\n\n"; + } + OS << "} // end namespace llvm\n\n"; } @@ -1139,6 +1160,37 @@ void RegisterInfoEmitter::runMCDesc(raw_ostream &OS, raw_ostream &MainOS, OS << "}\n\n"; + // Emit the register by HwMode (if present). + ArrayRef RegisterByHwModeRecords = + Records.getAllDerivedDefinitions("RegisterByHwMode"); + if (!RegisterByHwModeRecords.empty()) { + OS << "// Registers by HwMode\n" + << "namespace " << RegisterClasses.front().Namespace + << "::RegisterByHwMode {\n"; + 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"; + } + OS << "} // end namespace " << RegisterClasses.front().Namespace + << "::RegisterByHwMode\n\n"; + } + OS << "} // end namespace llvm\n\n"; } diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp index 27299fdda3e0b..882f882c2100d 100644 --- a/llvm/utils/TableGen/SubtargetEmitter.cpp +++ b/llvm/utils/TableGen/SubtargetEmitter.cpp @@ -1856,6 +1856,7 @@ void SubtargetEmitter::emitHwModeCheck(const std::string &ClassName, if (P.second->isSubClassOf("ValueType")) { ValueTypeModes |= (1 << (P.first - 1)); } else if (P.second->isSubClassOf("RegInfo") || + P.second->isSubClassOf("Register") || P.second->isSubClassOf("SubRegRange") || P.second->isSubClassOf("RegisterClassLike")) { RegInfoModes |= (1 << (P.first - 1));