diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h index 6a624a7052cdd..f2ad5ee249b46 100644 --- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h +++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h @@ -113,9 +113,16 @@ struct ExtAddrMode { /// class LLVM_ABI TargetInstrInfo : public MCInstrInfo { protected: + /// Subtarget specific sub-array of MCInstrInfo's RegClassByHwModeTables + /// (i.e. the table for the active HwMode). This should be indexed by + /// MCOperandInfo's RegClass field for LookupRegClassByHwMode operands. + const int16_t *const RegClassByHwMode; + TargetInstrInfo(unsigned CFSetupOpcode = ~0u, unsigned CFDestroyOpcode = ~0u, - unsigned CatchRetOpcode = ~0u, unsigned ReturnOpcode = ~0u) - : CallFrameSetupOpcode(CFSetupOpcode), + unsigned CatchRetOpcode = ~0u, unsigned ReturnOpcode = ~0u, + const int16_t *const RegClassByHwModeTable = nullptr) + : RegClassByHwMode(RegClassByHwModeTable), + CallFrameSetupOpcode(CFSetupOpcode), CallFrameDestroyOpcode(CFDestroyOpcode), CatchRetOpcode(CatchRetOpcode), ReturnOpcode(ReturnOpcode) {} @@ -133,6 +140,18 @@ class LLVM_ABI TargetInstrInfo : public MCInstrInfo { Opc <= TargetOpcode::GENERIC_ATOMICRMW_OP_END; } + /// \returns the subtarget appropriate RegClassID for \p OpInfo + /// + /// Note this shadows a version of getOpRegClassID in MCInstrInfo which takes + /// an additional argument for the subtarget's HwMode, since TargetInstrInfo + /// is owned by a subtarget in CodeGen but MCInstrInfo is a TargetMachine + /// constant. + int16_t getOpRegClassID(const MCOperandInfo &OpInfo) const { + if (OpInfo.isLookupRegClassByHwMode()) + return RegClassByHwMode[OpInfo.RegClass]; + return OpInfo.RegClass; + } + /// Given a machine instruction descriptor, returns the register /// class constraint for OpNum, or NULL. virtual const TargetRegisterClass * diff --git a/llvm/include/llvm/MC/MCInstrDesc.h b/llvm/include/llvm/MC/MCInstrDesc.h index 8c70925d4780e..0a4bd17e20738 100644 --- a/llvm/include/llvm/MC/MCInstrDesc.h +++ b/llvm/include/llvm/MC/MCInstrDesc.h @@ -50,6 +50,7 @@ enum OperandConstraint { /// See the accessors for a description of what these are. enum OperandFlags { LookupPtrRegClass = 0, + LookupRegClassByHwMode, Predicate, OptionalDef, BranchTarget @@ -85,10 +86,13 @@ enum OperandType { /// indicating the register class for register operands, etc. class MCOperandInfo { public: - /// This specifies the register class enumeration of the operand - /// if the operand is a register. If isLookupPtrRegClass is set, then this is - /// an index that is passed to TargetRegisterInfo::getPointerRegClass(x) to - /// get a dynamic register class. + /// This specifies the register class enumeration of the operand if the + /// operand is a register. If LookupRegClassByHwMode is set, then this is an + /// index into a table in TargetInstrInfo or MCInstrInfo which contains the + /// real register class ID. + /// + /// If isLookupPtrRegClass is set, then this is an index that is passed to + /// TargetRegisterInfo::getPointerRegClass(x) to get a dynamic register class. int16_t RegClass; /// These are flags from the MCOI::OperandFlags enum. @@ -102,10 +106,17 @@ class MCOperandInfo { /// Set if this operand is a pointer value and it requires a callback /// to look up its register class. + // TODO: Deprecated in favor of isLookupRegClassByHwMode bool isLookupPtrRegClass() const { return Flags & (1 << MCOI::LookupPtrRegClass); } + /// Set if this operand is a value that requires the current hwmode to look up + /// its register class. + bool isLookupRegClassByHwMode() const { + return Flags & (1 << MCOI::LookupRegClassByHwMode); + } + /// Set if this is one of the operands that made up of the predicate /// operand that controls an isPredicable() instruction. bool isPredicate() const { return Flags & (1 << MCOI::Predicate); } diff --git a/llvm/include/llvm/MC/MCInstrInfo.h b/llvm/include/llvm/MC/MCInstrInfo.h index 77ead222b9549..97d5e7a52d1a8 100644 --- a/llvm/include/llvm/MC/MCInstrInfo.h +++ b/llvm/include/llvm/MC/MCInstrInfo.h @@ -43,22 +43,48 @@ class MCInstrInfo { const ComplexDeprecationPredicate *ComplexDeprecationInfos; unsigned NumOpcodes; // Number of entries in the desc array +protected: + // Pointer to 2d array [NumHwModes][NumRegClassByHwModes] + const int16_t *RegClassByHwModeTables; + int16_t NumRegClassByHwModes; + public: /// Initialize MCInstrInfo, called by TableGen auto-generated routines. /// *DO NOT USE*. void InitMCInstrInfo(const MCInstrDesc *D, const unsigned *NI, const char *ND, const uint8_t *DF, - const ComplexDeprecationPredicate *CDI, unsigned NO) { + const ComplexDeprecationPredicate *CDI, unsigned NO, + const int16_t *RCHWTables = nullptr, + int16_t NumRegClassByHwMode = 0) { LastDesc = D + NO - 1; InstrNameIndices = NI; InstrNameData = ND; DeprecatedFeatures = DF; ComplexDeprecationInfos = CDI; NumOpcodes = NO; + RegClassByHwModeTables = RCHWTables; + NumRegClassByHwModes = NumRegClassByHwMode; } unsigned getNumOpcodes() const { return NumOpcodes; } + const int16_t *getRegClassByHwModeTable(unsigned ModeId) const { + assert(RegClassByHwModeTables && NumRegClassByHwModes != 0 && + "MCInstrInfo not properly initialized"); + return &RegClassByHwModeTables[ModeId * NumRegClassByHwModes]; + } + + /// Return the ID of the register class to use for \p OpInfo, for the active + /// HwMode \p HwModeId. In general TargetInstrInfo's version which is already + /// specialized to the subtarget should be used. + int16_t getOpRegClassID(const MCOperandInfo &OpInfo, + unsigned HwModeId) const { + int16_t RegClass = OpInfo.RegClass; + if (OpInfo.isLookupRegClassByHwMode()) + RegClass = getRegClassByHwModeTable(HwModeId)[RegClass]; + return RegClass; + } + /// Return the machine instruction descriptor that corresponds to the /// specified instruction opcode. const MCInstrDesc &get(unsigned Opcode) const { diff --git a/llvm/include/llvm/MC/MCSubtargetInfo.h b/llvm/include/llvm/MC/MCSubtargetInfo.h index 44b4cb8fa17bd..03ff85f6a8f47 100644 --- a/llvm/include/llvm/MC/MCSubtargetInfo.h +++ b/llvm/include/llvm/MC/MCSubtargetInfo.h @@ -255,10 +255,10 @@ class LLVM_ABI MCSubtargetInfo { /// this method also supports controlling multiple attributes with a single /// HwMode ID, just as was done previously. enum HwModeType { - HwMode_Default, // Return the smallest HwMode ID of current subtarget. - HwMode_ValueType, // Return the HwMode ID that controls the ValueType. - HwMode_RegInfo, // Return the HwMode ID that controls the RegSizeInfo and - // SubRegRange. + HwMode_Default, // Return the smallest HwMode ID of current subtarget. + HwMode_ValueType, // Return the HwMode ID that controls the ValueType. + HwMode_RegInfo, // Return the HwMode ID that controls the RegSizeInfo, + // SubRegRange, and RegisterClass. HwMode_EncodingInfo // Return the HwMode ID that controls the EncodingInfo. }; diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td index e664e20896db4..13175177edd3e 100644 --- a/llvm/include/llvm/Target/Target.td +++ b/llvm/include/llvm/Target/Target.td @@ -258,6 +258,12 @@ class DAGOperand { bit hasCompleteDecoder = true; } +/// Abstract base class common to RegisterClass and +/// RegClassByHwMode. This permits using RegClassByHwMode in +/// RegisterOperand contexts without creating an artificial +/// RegisterClass. +class RegisterClassLike : DAGOperand; + // RegisterClass - Now that all of the registers are defined, and aliases // between registers are defined, specify which registers belong to which // register classes. This also defines the default allocation order of @@ -265,7 +271,7 @@ class DAGOperand { // class RegisterClass regTypes, int alignment, dag regList, RegAltNameIndex idx = NoRegAltName> - : DAGOperand { + : RegisterClassLike { string Namespace = namespace; // The register size/alignment information, parameterized by a HW mode. @@ -916,15 +922,30 @@ def decoder; /// derived from this. TableGen treats the register class as having a symbolic /// type that it doesn't know, and resolves the actual regclass to use by using /// the TargetRegisterInfo::getPointerRegClass() hook at codegen time. +/// +/// This is deprecated in favor of RegClassByHwMode. class PointerLikeRegClass { int RegClassKind = Kind; } +/// RegClassByHwMode - Operands that change the register class based +/// on the subtarget are derived from this. TableGen +/// treats the register class as having a symbolic kind that it +/// doesn't know, and resolves the actual regclass to use by using the +/// a mapping in TargetInstrInfo at codegen time. This can be used to +/// define operands which swap the register class with the pointer +/// type. +class RegClassByHwMode Modes, + list RegClasses> + : HwModeSelect, RegisterClassLike { + list Objects = RegClasses; +} /// ptr_rc definition - Mark this operand as being a pointer value whose /// register class is resolved dynamically via a callback to TargetInstrInfo. /// FIXME: We should probably change this to a class which contain a list of /// flags. But currently we have but one flag. +// Deprecated, use RegClassByHwMode instead. def ptr_rc : PointerLikeRegClass<0>; /// unknown definition - Mark this operand as being of unknown type, causing @@ -1024,10 +1045,10 @@ class Operand : DAGOperand { AsmOperandClass ParserMatchClass = ImmAsmOperand; } -class RegisterOperand +class RegisterOperand : DAGOperand { // RegClass - The register class of the operand. - RegisterClass RegClass = regclass; + RegisterClassLike RegClass = regclass; // PrintMethod - The target method to call to print register operands of // this type. The method normally will just use an alt-name index to look // up the name to print. Default to the generic printOperand(). diff --git a/llvm/lib/CodeGen/TargetInstrInfo.cpp b/llvm/lib/CodeGen/TargetInstrInfo.cpp index b0009560d3fcb..5be89b49fb6ba 100644 --- a/llvm/lib/CodeGen/TargetInstrInfo.cpp +++ b/llvm/lib/CodeGen/TargetInstrInfo.cpp @@ -64,8 +64,11 @@ TargetInstrInfo::getRegClass(const MCInstrDesc &MCID, unsigned OpNum, if (OpNum >= MCID.getNumOperands()) return nullptr; - short RegClass = MCID.operands()[OpNum].RegClass; - if (MCID.operands()[OpNum].isLookupPtrRegClass()) + const MCOperandInfo &OpInfo = MCID.operands()[OpNum]; + int16_t RegClass = getOpRegClassID(OpInfo); + + // TODO: Remove isLookupPtrRegClass in favor of isLookupRegClassByHwMode + if (OpInfo.isLookupPtrRegClass()) return TRI->getPointerRegClass(RegClass); // Instructions like INSERT_SUBREG do not have fixed register classes. diff --git a/llvm/lib/Target/AMDGPU/BUFInstructions.td b/llvm/lib/Target/AMDGPU/BUFInstructions.td index f229298ba516b..09a66d785d5cf 100644 --- a/llvm/lib/Target/AMDGPU/BUFInstructions.td +++ b/llvm/lib/Target/AMDGPU/BUFInstructions.td @@ -411,11 +411,16 @@ class getBUFVDataRegisterOperand { RegisterOperand ret = !if(isTFE, tfeVDataOp, VDataOp); } +class getBUFVDataRegisterOperandForOp { + defvar Size = !cast(Op.RegClass).Size; + RegisterOperand ret = getBUFVDataRegisterOperand.ret; +} + class getMUBUFInsDA vdataList, list vaddrList, bit isTFE, bit hasRestrictedSOffset> { RegisterOperand vdataClass = !if(!empty(vdataList), ?, !head(vdataList)); RegisterClass vaddrClass = !if(!empty(vaddrList), ?, !head(vaddrList)); - RegisterOperand vdata_op = getBUFVDataRegisterOperand.ret; + RegisterOperand vdata_op = getBUFVDataRegisterOperandForOp.ret; dag SOffset = !if(hasRestrictedSOffset, (ins SReg_32:$soffset), (ins SCSrc_b32:$soffset)); dag NonVaddrInputs = !con((ins SReg_128_XNULL:$srsrc), SOffset, (ins Offset:$offset, CPol_0:$cpol, i1imm_0:$swz)); diff --git a/llvm/lib/Target/AMDGPU/SIInstrFormats.td b/llvm/lib/Target/AMDGPU/SIInstrFormats.td index 50964a94d6e58..de66c472be0ca 100644 --- a/llvm/lib/Target/AMDGPU/SIInstrFormats.td +++ b/llvm/lib/Target/AMDGPU/SIInstrFormats.td @@ -321,7 +321,7 @@ def CPolBit { int SCAL = 11; } -class VOPDstOperand : RegisterOperand ; +class VOPDstOperand : RegisterOperand; def VOPDstOperand_t16 : VOPDstOperand { let EncoderMethod = "getMachineOpValueT16"; diff --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.td b/llvm/lib/Target/AMDGPU/SIInstrInfo.td index 18fae6cfc7ed9..fb2cd04b364d7 100644 --- a/llvm/lib/Target/AMDGPU/SIInstrInfo.td +++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.td @@ -2636,7 +2636,7 @@ class getAlign2RegOp { } class getEquivalentAGPROperand { - defvar Size = RC.RegClass.Size; + defvar Size = !cast(RC.RegClass).Size; RegisterOperand ret = !cond(!eq(Size, 32) : RegisterOperand, !eq(Size, 64) : RegisterOperand, @@ -2647,7 +2647,7 @@ class getEquivalentAGPROperand { } class getEquivalentVGPROperand { - defvar Size = RC.RegClass.Size; + defvar Size = !cast(RC.RegClass).Size; RegisterOperand ret = !cond(!eq(Size, 32) : RegisterOperand, !eq(Size, 64) : RegisterOperand, diff --git a/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp b/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp index a8650146e988a..fc794c4968b8c 100644 --- a/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp +++ b/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp @@ -764,7 +764,7 @@ unsigned AVRAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp, RegName << "r" << RegNum; if (MCRegister Reg = MatchRegisterName(RegName.str())) { Op.makeReg(Reg); - if (validateOperandClass(Op, Expected) == Match_Success) { + if (validateOperandClass(Op, Expected, *STI) == Match_Success) { return Match_Success; } } @@ -780,7 +780,7 @@ unsigned AVRAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp, if (correspondingDREG) { Op.makeReg(correspondingDREG); - return validateOperandClass(Op, Expected); + return validateOperandClass(Op, Expected, *STI); } } } diff --git a/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp b/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp index 597ce8eabfeb2..4efb305b2d55a 100644 --- a/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp +++ b/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp @@ -1339,7 +1339,7 @@ bool SystemZAsmParser::parseDirectiveInsn(SMLoc L) { MatchClassKind Kind = Entry->OperandKinds[I]; // Verify operand. - unsigned Res = validateOperandClass(Operand, Kind); + unsigned Res = validateOperandClass(Operand, Kind, *STI); if (Res != Match_Success) return Error(Operand.getStartLoc(), "unexpected operand type"); diff --git a/llvm/test/TableGen/RegClassByHwMode.td b/llvm/test/TableGen/RegClassByHwMode.td new file mode 100644 index 0000000000000..5d813d2bfc83a --- /dev/null +++ b/llvm/test/TableGen/RegClassByHwMode.td @@ -0,0 +1,467 @@ +// RUN: llvm-tblgen -gen-instr-info -I %p/../../include %s -o - | FileCheck -check-prefix=INSTRINFO %s +// RUN: llvm-tblgen -gen-asm-matcher -I %p/../../include %s -o - | FileCheck -check-prefix=ASMMATCHER %s +// RUN: llvm-tblgen -gen-disassembler -I %p/../../include %s -o - | FileCheck -check-prefix=DISASM %s +// RUN: llvm-tblgen -gen-dag-isel -I %p/../../include %s -o - | FileCheck -check-prefix=ISEL-SDAG %s +// RUN: llvm-tblgen -gen-global-isel -I %p/../../include %s -o - | FileCheck -check-prefix=ISEL-GISEL %s + +include "llvm/Target/Target.td" + +// INSTRINFO: #ifdef GET_INSTRINFO_ENUM +// INSTRINFO-NEXT: #undef GET_INSTRINFO_ENUM +// INSTRINFO-NEXT: namespace llvm::MyTarget { +// INSTRINFO-NEXT: enum { +// INSTRINFO-NEXT: PHI +// INSTRINFO: }; +// INSTRINFO: enum RegClassByHwModeUses : uint16_t { +// INSTRINFO-NEXT: MyPtrRC, +// INSTRINFO-NEXT: XRegs_EvenIfRequired, +// INSTRINFO-NEXT: YRegs_EvenIfRequired, +// INSTRINFO-NEXT: }; +// INSTRINFO-NEXT: } + +// INSTRINFO: { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 }, +// INSTRINFO: { MyTarget::XRegs_EvenRegClassID, 0, MCOI::OPERAND_REGISTER, 0 }, +// INSTRINFO: { MyTarget::YRegs_EvenIfRequired, 0|(1<InitMCInstrInfo(MyTargetDescs.Insts, MyTargetInstrNameIndices, MyTargetInstrNameData, nullptr, nullptr, 321, &MyTargetRegClassByHwModeTables[0][0], 3); + + + +// ASMMATCHER: enum MatchClassKind { +// ASMMATCHER: MCK_LAST_TOKEN = OptionalMatchClass, +// ASMMATCHER-NEXT: MCK_XRegs_Odd, // register class 'XRegs_Odd' +// ASMMATCHER-NEXT: MCK_PtrRegs32, // register class 'PtrRegs32' +// ASMMATCHER-NEXT: MCK_PtrRegs64, // register class 'PtrRegs64' +// ASMMATCHER-NEXT: MCK_XRegs_Even, // register class 'XRegs_Even' +// ASMMATCHER-NEXT: MCK_YRegs_Even, // register class 'YRegs_Even' +// 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_MyPtrRC, // register class by hwmode +// ASMMATCHER-NEXT: MCK_RegByHwMode_XRegs_EvenIfRequired, // register class by hwmode +// ASMMATCHER-NEXT: MCK_RegByHwMode_YRegs_EvenIfRequired, // register class by hwmode +// ASMMATCHER-NEXT: MCK_LAST_REGCLASS_BY_HWMODE = MCK_RegByHwMode_YRegs_EvenIfRequired, +// ASMMATCHER-NEXT: MCK_Imm, // user defined class 'ImmAsmOperand' + +// ASMMATCHER: static unsigned validateOperandClass(MCParsedAsmOperand &GOp, MatchClassKind Kind, const MCSubtargetInfo &STI) { +// ASMMATCHER: if (Operand.isToken() && Kind <= MCK_LAST_TOKEN) + +// ASMMATCHER: switch (Kind) { + +// ASMMATCHER: if (Operand.isReg() && Kind > MCK_LAST_REGISTER && Kind <= MCK_LAST_REGCLASS_BY_HWMODE) { +// ASMMATCHER-NEXT: static constexpr MatchClassKind RegClassByHwModeMatchTable[4][3] = { +// ASMMATCHER-NEXT: { // DefaultMode +// ASMMATCHER-NEXT: MCK_PtrRegs32, +// ASMMATCHER-NEXT: MCK_XRegs, +// ASMMATCHER-NEXT: MCK_YRegs, +// ASMMATCHER-NEXT: }, +// ASMMATCHER-NEXT: { // EvenMode +// ASMMATCHER-NEXT: InvalidMatchClass, // Missing mode +// ASMMATCHER-NEXT: MCK_XRegs_Even, +// ASMMATCHER-NEXT: MCK_YRegs_Even, +// ASMMATCHER-NEXT: }, +// ASMMATCHER-NEXT: { // OddMode +// ASMMATCHER-NEXT: InvalidMatchClass, // Missing mode +// ASMMATCHER-NEXT: MCK_XRegs_Odd, +// ASMMATCHER-NEXT: InvalidMatchClass, // Missing mode +// ASMMATCHER-NEXT: }, +// ASMMATCHER-NEXT: { // Ptr64 +// ASMMATCHER-NEXT: MCK_PtrRegs64, +// ASMMATCHER-NEXT: InvalidMatchClass, // Missing mode +// ASMMATCHER-NEXT: InvalidMatchClass, // Missing mode +// ASMMATCHER-NEXT: }, +// ASMMATCHER-NEXT: }; +// ASMMATCHER-EMPTY: +// ASMMATCHER-NEXT: static_assert(MCK_LAST_REGCLASS_BY_HWMODE - MCK_LAST_REGISTER == 3); +// ASMMATCHER-NEXT: const unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_RegInfo); +// ASMMATCHER-NEXT: Kind = RegClassByHwModeMatchTable[HwMode][Kind - (MCK_LAST_REGISTER + 1)]; +// ASMMATCHER-NEXT: } + +// ASMMATCHER: if (Operand.isReg()) { + +// ASMMATCHER: static const MatchEntry MatchTable0[] = { +// ASMMATCHER: /* also_my_load_1 */, MyTarget::MY_LOAD, Convert__RegByHwMode_XRegs_EvenIfRequired1_0__RegByHwMode_MyPtrRC1_1, AMFBS_None, { MCK_RegByHwMode_XRegs_EvenIfRequired, MCK_RegByHwMode_MyPtrRC }, }, +// ASMMATCHER: /* also_my_load_2 */, MyTarget::MY_LOAD, Convert__RegByHwMode_XRegs_EvenIfRequired1_0__RegByHwMode_MyPtrRC1_1, AMFBS_None, { MCK_RegByHwMode_XRegs_EvenIfRequired, MCK_RegByHwMode_MyPtrRC }, }, +// ASMMATCHER: /* always_all */, MyTarget::ALWAYS_ALL, Convert__Reg1_0, AMFBS_None, { MCK_XRegs }, }, +// ASMMATCHER: /* always_even */, MyTarget::ALWAYS_EVEN, Convert__Reg1_0, AMFBS_None, { MCK_XRegs_Even }, }, +// ASMMATCHER: /* custom_decode */, MyTarget::CUSTOM_DECODE, Convert__RegByHwMode_YRegs_EvenIfRequired1_0, AMFBS_None, { MCK_RegByHwMode_YRegs_EvenIfRequired }, }, +// ASMMATCHER: /* even_if_mode */, MyTarget::EVEN_IF_MODE, Convert__RegByHwMode_XRegs_EvenIfRequired1_0, AMFBS_None, { MCK_RegByHwMode_XRegs_EvenIfRequired }, }, +// ASMMATCHER: /* my_mov */, MyTarget::MY_MOV, Convert__RegByHwMode_YRegs_EvenIfRequired1_0__RegByHwMode_XRegs_EvenIfRequired1_1, AMFBS_None, { MCK_RegByHwMode_YRegs_EvenIfRequired, MCK_RegByHwMode_XRegs_EvenIfRequired }, }, + + + +// DISASM{LITERAL}: [[maybe_unused]] +// DISASM-NEXT: static DecodeStatus DecodeMyPtrRCRegClassByHwMode(MCInst &Inst, unsigned Imm, uint64_t Addr, const MCDisassembler *Decoder) { +// DISASM-NEXT: switch (Decoder->getSubtargetInfo().getHwMode(MCSubtargetInfo::HwMode_RegInfo)) { +// DISASM-NEXT: case 0: // DefaultMode +// DISASM-NEXT: return DecodePtrRegs32RegisterClass(Inst, Imm, Addr, Decoder); +// DISASM-NEXT: case 3: // Ptr64 +// DISASM-NEXT: return DecodePtrRegs64RegisterClass(Inst, Imm, Addr, Decoder); +// DISASM-NEXT: default: +// DISASM-NEXT: llvm_unreachable("no decoder for hwmode"); +// DISASM-NEXT: } +// DISASM-NEXT: } + +// DISASM{LITERAL}: [[maybe_unused]] +// DISASM-NEXT: static DecodeStatus DecodeXRegs_EvenIfRequiredRegClassByHwMode(MCInst &Inst, unsigned Imm, uint64_t Addr, const MCDisassembler *Decoder) { +// DISASM-NEXT: switch (Decoder->getSubtargetInfo().getHwMode(MCSubtargetInfo::HwMode_RegInfo)) { +// DISASM-NEXT: case 0: // DefaultMode +// DISASM-NEXT: return DecodeXRegsRegisterClass(Inst, Imm, Addr, Decoder); +// DISASM-NEXT: case 1: // EvenMode +// DISASM-NEXT: return DecodeXRegs_EvenRegisterClass(Inst, Imm, Addr, Decoder); +// DISASM-NEXT: case 2: // OddMode +// DISASM-NEXT: return DecodeXRegs_OddRegisterClass(Inst, Imm, Addr, Decoder); +// DISASM-NEXT: default: +// DISASM-NEXT: llvm_unreachable("no decoder for hwmode"); +// DISASM-NEXT: } +// DISASM-NEXT:} +// DISASM-EMPTY: +// DISASM{LITERAL}: [[maybe_unused]] +// DISASM-NEXT: static DecodeStatus DecodeYRegs_EvenIfRequiredRegClassByHwMode(MCInst &Inst, unsigned Imm, uint64_t Addr, const MCDisassembler *Decoder) { +// DISASM-NEXT: switch (Decoder->getSubtargetInfo().getHwMode(MCSubtargetInfo::HwMode_RegInfo)) { +// DISASM-NEXT: case 0: // DefaultMode +// DISASM-NEXT: return DecodeYRegsRegisterClass(Inst, Imm, Addr, Decoder); +// DISASM-NEXT: case 1: // EvenMode +// DISASM-NEXT: return DecodeYRegs_EvenRegisterClass(Inst, Imm, Addr, Decoder); +// DISASM-NEXT: default: +// DISASM-NEXT: llvm_unreachable("no decoder for hwmode"); +// DISASM-NEXT: } +// DISASM-NEXT:} + +// DISASM: static DecodeStatus decodeToMCInst( +// DISASM: switch (Idx) { +// DISASM: case 0: +// DISASM: if (!Check(S, DecodeYRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; } +// DISASM: if (!Check(S, DecodeXRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; } + +// DISASM: case 1: +// DISASM: if (!Check(S, DecodeXRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; } + +// DISASM: case 2: +// DISASM: if (!Check(S, DecodeXRegs_EvenRegisterClass(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; } +// DISASM: case 3: +// DISASM: if (!Check(S, DecodeXRegsRegisterClass(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; } + +// DISASM: case 4: +// DISASM: if (!Check(S, YEvenIfRequiredCustomDecoder(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; } + + +// ISEL-SDAG: MatcherTable +// ISEL-SDAG: OPC_SwitchOpcode /*2 cases */, {{[0-9]+}}, TARGET_VAL(ISD::STORE), +// ISEL-SDAG: OPC_RecordMemRef, +// ISEL-SDAG: OPC_RecordNode, // #0 = 'st' chained node +// ISEL-SDAG: OPC_RecordChild1, // #1 = $val +// ISEL-SDAG-NEXT: OPC_RecordChild2, // #2 = $src +// ISEL-SDAG-NEXT: OPC_CheckChild2TypeI64, +// ISEL-SDAG-NEXT: OPC_CheckPredicate2, // Predicate_unindexedstore +// ISEL-SDAG-NEXT: OPC_CheckPredicate3, // Predicate_store +// ISEL-SDAG: OPC_MorphNodeTo0, TARGET_VAL(MyTarget::MY_STORE), 0|OPFL_Chain|OPFL_MemRefs, + +// ISEL-SDAG: /*SwitchOpcode*/ {{[0-9]+}}, TARGET_VAL(ISD::LOAD), +// ISEL-SDAG-NEXT: OPC_RecordMemRef, +// ISEL-SDAG-NEXT: OPC_RecordNode, // #0 = 'ld' chained node +// ISEL-SDAG-NEXT: OPC_RecordChild1, // #1 = $src +// ISEL-SDAG-NEXT: OPC_CheckTypeI64, +// ISEL-SDAG-NEXT: OPC_Scope, {{[0-9]+}}, /*->{{[0-9]+}}*/ // 2 children in Scope +// ISEL-SDAG-NEXT: OPC_CheckChild1TypeI32, +// ISEL-SDAG-NEXT: OPC_CheckPredicate0, // Predicate_unindexedload +// ISEL-SDAG-NEXT: OPC_CheckPredicate1, // Predicate_load +// ISEL-SDAG-NEXT: OPC_Scope, {{[0-9]+}}, /*->{{[0-9]+}}*/ // 3 children in Scope +// ISEL-SDAG-NEXT: OPC_CheckPatternPredicate1, // (Subtarget->hasAlignedRegisters()) +// ISEL-SDAG-NEXT: OPC_EmitMergeInputChains1_0, +// ISEL-SDAG-NEXT: OPC_MorphNodeTo1, TARGET_VAL(MyTarget::MY_LOAD), 0|OPFL_Chain|OPFL_MemRefs, + +// ISEL-SDAG: /*Scope*/ +// ISEL-SDAG: OPC_CheckPatternPredicate2, // (Subtarget->hasUnalignedRegisters()) +// ISEL-SDAG-NEXT: OPC_EmitMergeInputChains1_0, +// ISEL-SDAG-NEXT: OPC_MorphNodeTo1, TARGET_VAL(MyTarget::MY_LOAD), 0|OPFL_Chain|OPFL_MemRefs, + +// ISEL-SDAG: /*Scope*/ +// ISEL-SDAG: OPC_CheckPatternPredicate3, // !((Subtarget->hasAlignedRegisters())) && !((Subtarget->hasUnalignedRegisters())) && !((Subtarget->isPtr64())) +// ISEL-SDAG-NEXT: OPC_EmitMergeInputChains1_0, +// ISEL-SDAG-NEXT: OPC_MorphNodeTo1, TARGET_VAL(MyTarget::MY_LOAD), 0|OPFL_Chain|OPFL_MemRefs, + +// ISEL-SDAG: /*Scope*/ +// ISEL-SDAG-NEXT: OPC_CheckChild1TypeI64, +// ISEL-SDAG-NEXT: OPC_CheckPredicate0, // Predicate_unindexedload +// ISEL-SDAG-NEXT: OPC_CheckPredicate1, // Predicate_load +// ISEL-SDAG-NEXT: OPC_CheckPatternPredicate0, // (Subtarget->isPtr64()) +// ISEL-SDAG-NEXT: OPC_EmitMergeInputChains1_0, +// ISEL-SDAG-NEXT: OPC_MorphNodeTo1, TARGET_VAL(MyTarget::MY_LOAD), 0|OPFL_Chain|OPFL_MemRefs, + + + + + +// ISEL-GISEL: GIM_Try, /*On fail goto*//*Label 3*/ GIMT_Encode4(148), +// ISEL-GISEL-NEXT: GIM_RootCheckType, /*Op*/0, /*Type*/GILLT_s64, +// ISEL-GISEL-NEXT: GIM_CheckAtomicOrdering, /*MI*/0, /*Order*/(uint8_t)AtomicOrdering::NotAtomic, +// ISEL-GISEL-NEXT: GIM_CheckMemorySizeEqualToLLT, /*MI*/0, /*MMO*/0, /*OpIdx*/0, +// ISEL-GISEL-NEXT: GIM_RootCheckRegBankForClass, /*Op*/0, /*RC*/GIMT_Encode2(MyTarget::XRegsRegClassID), +// ISEL-GISEL-NEXT: GIM_Try, /*On fail goto*//*Label 4*/ GIMT_Encode4(101), +// ISEL-GISEL-NEXT: GIM_CheckPointerToAny, /*MI*/0, /*Op*/1, /*SizeInBits*/32, + +// FIXME: This should be a direct check for regbank, not have an incorrect class + +// ISEL-GISEL-NEXT: GIM_RootCheckRegBankForClass, /*Op*/1, /*RC*/GIMT_Encode2(MyTarget::PtrRegs32RegClassID), +// ISEL-GISEL-NEXT: GIM_Try, /*On fail goto*//*Label 5*/ GIMT_Encode4(85), // Rule ID 1 // +// ISEL-GISEL-NEXT: GIM_CheckFeatures, GIMT_Encode2(GIFBS_HwMode1), +// ISEL-GISEL-NEXT: // (ld:{ *:[i64] } PtrRegOperand:{ *:[i32] }:$src)<><> => (MY_LOAD:{ *:[i64] } ?:{ *:[i32] }:$src) +// ISEL-GISEL-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::MY_LOAD), +// ISEL-GISEL-NEXT: GIR_RootConstrainSelectedInstOperands, +// ISEL-GISEL-NEXT: // GIR_Coverage, 1, +// ISEL-GISEL-NEXT: GIR_Done, +// ISEL-GISEL-NEXT: // Label 5: @85 +// ISEL-GISEL-NEXT: GIM_Try, /*On fail goto*//*Label 6*/ GIMT_Encode4(100), // Rule ID 2 // +// ISEL-GISEL-NEXT: GIM_CheckFeatures, GIMT_Encode2(GIFBS_HwMode2), +// ISEL-GISEL-NEXT: // (ld:{ *:[i64] } PtrRegOperand:{ *:[i32] }:$src)<><> => (MY_LOAD:{ *:[i64] } ?:{ *:[i32] }:$src) +// ISEL-GISEL-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::MY_LOAD), +// ISEL-GISEL-NEXT: GIR_RootConstrainSelectedInstOperands, +// ISEL-GISEL-NEXT: // GIR_Coverage, 2, +// ISEL-GISEL-NEXT: GIR_Done, +// ISEL-GISEL-NEXT: // Label 6: @100 +// ISEL-GISEL-NEXT: GIM_Reject, +// ISEL-GISEL-NEXT: // Label 4: @101 +// ISEL-GISEL-NEXT: GIM_Try, /*On fail goto*//*Label 7*/ GIMT_Encode4(124), // Rule ID 3 // +// ISEL-GISEL-NEXT: GIM_CheckFeatures, GIMT_Encode2(GIFBS_HwMode0), +// ISEL-GISEL-NEXT: // MIs[0] src +// ISEL-GISEL-NEXT: GIM_CheckPointerToAny, /*MI*/0, /*Op*/1, /*SizeInBits*/64, +// ISEL-GISEL-NEXT: GIM_RootCheckRegBankForClass, /*Op*/1, /*RC*/GIMT_Encode2(MyTarget::PtrRegs32RegClassID), +// ISEL-GISEL-NEXT: // (ld:{ *:[i64] } PtrRegOperand:{ *:[i64] }:$src)<><> => (MY_LOAD:{ *:[i64] } ?:{ *:[i64] }:$src) +// ISEL-GISEL-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::MY_LOAD), +// ISEL-GISEL-NEXT: GIR_RootConstrainSelectedInstOperands, +// ISEL-GISEL-NEXT: // GIR_Coverage, 3, +// ISEL-GISEL-NEXT: GIR_Done, +// ISEL-GISEL-NEXT: // Label 7: @124 +// ISEL-GISEL-NEXT: GIM_Try, /*On fail goto*//*Label 8*/ GIMT_Encode4(147), // Rule ID 4 // +// ISEL-GISEL-NEXT: GIM_CheckFeatures, GIMT_Encode2(GIFBS_HwMode3), +// ISEL-GISEL-NEXT: // MIs[0] src +// ISEL-GISEL-NEXT: GIM_CheckPointerToAny, /*MI*/0, /*Op*/1, /*SizeInBits*/32, +// ISEL-GISEL-NEXT: GIM_RootCheckRegBankForClass, /*Op*/1, /*RC*/GIMT_Encode2(MyTarget::PtrRegs32RegClassID), +// ISEL-GISEL-NEXT: // (ld:{ *:[i64] } PtrRegOperand:{ *:[i32] }:$src)<><> => (MY_LOAD:{ *:[i64] } ?:{ *:[i32] }:$src) +// ISEL-GISEL-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::MY_LOAD), +// ISEL-GISEL-NEXT: GIR_RootConstrainSelectedInstOperands, +// ISEL-GISEL-NEXT: // GIR_Coverage, 4, +// ISEL-GISEL-NEXT: GIR_Done, +// ISEL-GISEL-NEXT: // Label 8: @147 +// ISEL-GISEL-NEXT: GIM_Reject, +// ISEL-GISEL-NEXT: // Label 3: @148 +// ISEL-GISEL-NEXT: GIM_Reject, +// ISEL-GISEL-NEXT: // Label 1: @149 +// ISEL-GISEL-NEXT: GIM_Try, /*On fail goto*//*Label 9*/ GIMT_Encode4(186), // Rule ID 0 // +// ISEL-GISEL-NEXT: GIM_CheckFeatures, GIMT_Encode2(GIFBS_HwMode0), +// ISEL-GISEL-NEXT: GIM_RootCheckType, /*Op*/0, /*Type*/GILLT_s64, +// ISEL-GISEL-NEXT: GIM_CheckAtomicOrdering, /*MI*/0, /*Order*/(uint8_t)AtomicOrdering::NotAtomic, +// ISEL-GISEL-NEXT: GIM_CheckMemorySizeEqualToLLT, /*MI*/0, /*MMO*/0, /*OpIdx*/0, +// ISEL-GISEL-NEXT: GIM_RootCheckRegBankForClass, /*Op*/0, /*RC*/GIMT_Encode2(MyTarget::XRegsRegClassID), +// ISEL-GISEL-NEXT: // MIs[0] src +// ISEL-GISEL-NEXT: GIM_CheckPointerToAny, /*MI*/0, /*Op*/1, /*SizeInBits*/64, +// ISEL-GISEL-NEXT: GIM_RootCheckRegBankForClass, /*Op*/1, /*RC*/GIMT_Encode2(MyTarget::PtrRegs32RegClassID), +// ISEL-GISEL-NEXT: // (st XRegs_EvenIfRequired:{ *:[i64] }:$val, MyPtrRC:{ *:[i64] }:$src)<><> => (MY_STORE ?:{ *:[i64] }:$val, XRegs_EvenIfRequired:{ *:[i64] }:$src) +// ISEL-GISEL-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::MY_STORE), +// ISEL-GISEL-NEXT: GIR_RootConstrainSelectedInstOperands, + + + +def HasAlignedRegisters : Predicate<"Subtarget->hasAlignedRegisters()">; +def HasUnalignedRegisters : Predicate<"Subtarget->hasUnalignedRegisters()">; +def IsPtr64 : Predicate<"Subtarget->isPtr64()">; + +// FIXME: In reality these should be mutually exclusive and we need +// the cross product of even mode / ptr size. i.e. EvenModePtr64, +// OddMode32 etc. For the purposes of this test where we won't be +// executing the code to compute a mode ID, it's simpler to pretend +// these are orthogonal. +def EvenMode : HwMode<[HasAlignedRegisters]>; +def OddMode : HwMode<[HasUnalignedRegisters]>; +def Ptr64 : HwMode<[IsPtr64]>; + +class MyReg + : Register { + let Namespace = "MyTarget"; +} + +class MyClass types, dag registers> + : RegisterClass<"MyTarget", types, size, registers> { + let Size = size; +} + +def X0 : MyReg<"x0">; +def X1 : MyReg<"x1">; +def X2 : MyReg<"x2">; +def X3 : MyReg<"x3">; +def X4 : MyReg<"x4">; +def X5 : MyReg<"x5">; +def X6 : MyReg<"x6">; + +def Y0 : MyReg<"y0">; +def Y1 : MyReg<"y1">; +def Y2 : MyReg<"y2">; +def Y3 : MyReg<"y3">; +def Y4 : MyReg<"y4">; +def Y5 : MyReg<"y5">; +def Y6 : MyReg<"y6">; + + + +def P0_32 : MyReg<"p0">; +def P1_32 : MyReg<"p1">; +def P2_32 : MyReg<"p2">; +def P3_32 : MyReg<"p3">; + +def P0_64 : MyReg<"p0_64">; +def P1_64 : MyReg<"p1_64">; +def P2_64 : MyReg<"p2_64">; +def P3_64 : MyReg<"p3_64">; + + + +def XRegs : RegisterClass<"MyTarget", [i64], 64, (add X0, X1, X2, X3, X4, X5, X6)>; +def XRegs_Odd : RegisterClass<"MyTarget", [i64], 64, (add X1, X3, X5)>; +def XRegs_Even : RegisterClass<"MyTarget", [i64], 64, (add X0, X2, X4, X6)>; + +def XRegs_EvenIfRequired : RegClassByHwMode<[DefaultMode, EvenMode, OddMode], + [XRegs, XRegs_Even, XRegs_Odd]>; + +def YRegs : RegisterClass<"MyTarget", [i64], 64, (add Y0, Y1, Y2, Y3, Y4, Y5, Y6)>; +def YRegs_Even : RegisterClass<"MyTarget", [i64], 64, (add Y0, Y2, Y4, Y6)>; + +def YRegs_EvenIfRequired : RegClassByHwMode<[DefaultMode, EvenMode], + [YRegs, YRegs_Even]>; + + +def PtrRegs32 : RegisterClass<"MyTarget", [i32], 32, (add P0_32, P1_32, P2_32, P3_32)>; +def PtrRegs64 : RegisterClass<"MyTarget", [i64], 64, (add P0_64, P1_64, P2_64, P3_64)>; + +def MyPtrRC : RegClassByHwMode<[DefaultMode, Ptr64], + [PtrRegs32, PtrRegs64]>; + + +def PtrRegOperand : RegisterOperand; + + +def CustomDecodeYEvenIfRequired : RegisterOperand { + let DecoderMethod = "YEvenIfRequiredCustomDecoder"; +} + +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 SpecialOperand : RegisterOperand; + +def MY_MOV : TestInstruction { + let OutOperandList = (outs YRegs_EvenIfRequired:$dst); + let InOperandList = (ins XRegs_EvenIfRequired:$src); + let AsmString = "my_mov $dst, $src"; + let opcode = 0; +} + +def EVEN_IF_MODE : TestInstruction { + let OutOperandList = (outs); + let InOperandList = (ins XRegs_EvenIfRequired:$src); + let AsmString = "even_if_mode $src"; + let opcode = 1; +} + +def ALWAYS_EVEN : TestInstruction { + let OutOperandList = (outs); + let InOperandList = (ins XRegs_Even:$src); + let AsmString = "always_even $src"; + let opcode = 2; +} + +def ALWAYS_ALL : TestInstruction { + let OutOperandList = (outs); + let InOperandList = (ins XRegs:$src); + let AsmString = "always_all $src"; + let opcode = 3; +} + +def CUSTOM_DECODE : TestInstruction { + let OutOperandList = (outs); + let InOperandList = (ins CustomDecodeYEvenIfRequired:$src); + let AsmString = "custom_decode $src"; + let opcode = 4; +} + +def MyTargetMov : SDNode<"MyTarget::MY_MOV", SDTUnaryOp, []>; + +// Test 2 different cases directly in the instruction +def MY_STORE : TestInstruction { + let OutOperandList = (outs); + let InOperandList = (ins XRegs_EvenIfRequired:$src, MyPtrRC:$dst); + let AsmString = "my_store $src, $dst"; + let opcode = 5; +} + +// Test 2 different cases wrapped by RegisterOperand +def MY_LOAD : TestInstruction { + let OutOperandList = (outs RegisterOperand:$dst); + let InOperandList = (ins PtrRegOperand:$src); + let AsmString = "my_load $dst, $src"; + let opcode = 6; +} + +// Direct RegClassByHwMode reference +def MY_LOAD_ALIAS1 : InstAlias<"also_my_load_1 $dst, $src", + (MY_LOAD XRegs_EvenIfRequired:$dst, MyPtrRC:$src)>; + +// RegClassByHwMode wrapped in RegisterOperand +def MY_LOAD_ALIAS2 : InstAlias<"also_my_load_2 $dst, $src", + (MY_LOAD RegisterOperand:$dst, PtrRegOperand:$src)>; + +// Direct RegClassByHwMode usage +def : Pat< + (store XRegs_EvenIfRequired:$val, MyPtrRC:$src), + (MY_STORE $val, XRegs_EvenIfRequired:$src) +>; + +// Wrapped in RegisterOperand +def : Pat< + (i64 (load PtrRegOperand:$src)), + (MY_LOAD $src) +>; + +def MyTargetISA : InstrInfo; +def MyTarget : Target { let InstructionSet = MyTargetISA; } diff --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp index 29d26d5237695..e1f2f06d755f1 100644 --- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp +++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp @@ -166,14 +166,18 @@ struct ClassInfo { /// RegisterClass0+1, and so on. RegisterClass0, + /// The (first) register class by hwmode, subsequent register classes by + /// hwmode are RegisterClassByHwMode0+1, and so on. + RegisterClassByHwMode0 = 1 << 12, + /// The (first) user defined class, subsequent user defined classes are /// UserClass0+1, and so on. - UserClass0 = 1 << 16 + UserClass0 = 1 << 24 }; /// Kind - The class kind, which is either a predefined kind, or (UserClass0 + /// N) for the Nth user defined class. - unsigned Kind; + unsigned Kind = 0; /// SuperClasses - The super classes of this class. Note that for simplicities /// sake user operands only record their immediate super class, while register @@ -215,7 +219,7 @@ struct ClassInfo { std::string DiagnosticString; /// Is this operand optional and not always required. - bool IsOptional; + bool IsOptional = false; /// DefaultMethod - The name of the method that returns the default operand /// for optional operand @@ -224,7 +228,11 @@ struct ClassInfo { public: /// isRegisterClass() - Check if this is a register class. bool isRegisterClass() const { - return Kind >= RegisterClass0 && Kind < UserClass0; + return Kind >= RegisterClass0 && Kind < RegisterClassByHwMode0; + } + + bool isRegisterClassByHwMode() const { + return Kind >= RegisterClassByHwMode0 && Kind < UserClass0; } /// isUserClass() - Check if this is a user defined class. @@ -251,6 +259,9 @@ struct ClassInfo { return !Tmp.empty(); } + if (isRegisterClassByHwMode() || RHS.isRegisterClassByHwMode()) + return isRegisterClassByHwMode() == RHS.isRegisterClassByHwMode(); + // Otherwise we have two users operands; they are related if they are in the // same class hierarchy. // @@ -315,7 +326,8 @@ struct ClassInfo { return false; // First, enforce the ordering between the three different types of class. - // Tokens sort before registers, which sort before user classes. + // Tokens sort before registers, which sort before regclass by hwmode, which + // sort before user classes. if (Kind == Token) { if (RHS.Kind != Token) return true; @@ -323,9 +335,15 @@ struct ClassInfo { } else if (isRegisterClass()) { if (RHS.Kind == Token) return false; - else if (RHS.isUserClass()) + else if (RHS.isUserClass() || RHS.isRegisterClassByHwMode()) return true; assert(RHS.isRegisterClass()); + } else if (isRegisterClassByHwMode()) { + if (RHS.Kind == Token || RHS.isRegisterClass()) + return false; + else if (RHS.isUserClass()) + return true; + assert(RHS.isRegisterClassByHwMode()); } else if (isUserClass()) { if (!RHS.isUserClass()) return false; @@ -353,6 +371,10 @@ struct ClassInfo { // a set will always sort before all of it's strict supersets. if (Registers.size() != RHS.Registers.size()) return Registers.size() < RHS.Registers.size(); + } else if (isRegisterClassByHwMode()) { + // Ensure the MCK enum entries are in the same order as RegClassIDs. The + // lookup table to from RegByHwMode to concrete class relies on it. + return Kind < RHS.Kind; } else { llvm_unreachable("Unknown ClassInfoKind"); } @@ -1205,9 +1227,13 @@ ClassInfo *AsmMatcherInfo::getOperandClass(const Record *Rec, int SubOpIdx) { PrintFatalError(Rec->getLoc(), "RegisterOperand `" + Rec->getName() + "' has no associated register class!\n"); - if (ClassInfo *CI = RegisterClassClasses[ClassRec]) - return CI; - PrintFatalError(Rec->getLoc(), "register class has no class info!"); + + if (ClassRec->isSubClassOf("RegisterClassLike")) { + if (ClassInfo *CI = RegisterClassClasses[ClassRec]) + return CI; + + PrintFatalError(Rec->getLoc(), "register class has no class info!"); + } } if (Rec->isSubClassOf("RegisterClass")) { @@ -1216,13 +1242,19 @@ ClassInfo *AsmMatcherInfo::getOperandClass(const Record *Rec, int SubOpIdx) { PrintFatalError(Rec->getLoc(), "register class has no class info!"); } - if (!Rec->isSubClassOf("Operand")) + if (Rec->isSubClassOf("Operand")) { + const Record *MatchClass = Rec->getValueAsDef("ParserMatchClass"); + if (ClassInfo *CI = AsmOperandClasses[MatchClass]) + return CI; + } else if (Rec->isSubClassOf("RegisterClassLike")) { + if (ClassInfo *CI = RegisterClassClasses[Rec]) + return CI; + PrintFatalError(Rec->getLoc(), "register class has no class info!"); + } else { PrintFatalError(Rec->getLoc(), "Operand `" + Rec->getName() + "' does not derive from class Operand!\n"); - const Record *MatchClass = Rec->getValueAsDef("ParserMatchClass"); - if (ClassInfo *CI = AsmOperandClasses[MatchClass]) - return CI; + } PrintFatalError(Rec->getLoc(), "operand has no match class!"); } @@ -1307,6 +1339,7 @@ void AsmMatcherInfo::buildRegisterClasses( CI->DefaultMethod = ""; // unused RegisterSetClasses.try_emplace(RS, CI); ++Index; + assert(CI->isRegisterClass()); } // Find the superclasses; we could compute only the subgroup lattice edges, @@ -1348,6 +1381,24 @@ void AsmMatcherInfo::buildRegisterClasses( CI->DiagnosticType = RC.getName(); RegisterClassClasses.try_emplace(Def, CI); + assert(CI->isRegisterClass()); + } + + unsigned RegClassByHwModeIndex = 0; + for (const Record *ClassByHwMode : Target.getAllRegClassByHwMode()) { + ClassInfo &CI = Classes.emplace_front(); + CI.Kind = ClassInfo::RegisterClassByHwMode0 + RegClassByHwModeIndex; + + CI.ClassName = "RegByHwMode_" + ClassByHwMode->getName().str(); + CI.Name = "MCK_" + CI.ClassName; + CI.ValueName = ClassByHwMode->getName(); + CI.RenderMethod = "addRegOperands"; + // FIXME: Set diagnostic type. + ++RegClassByHwModeIndex; + + assert(CI.isRegisterClassByHwMode()); + + RegisterClassClasses.try_emplace(ClassByHwMode, &CI); } // Populate the map for individual registers. @@ -2367,10 +2418,14 @@ static void emitMatchClassEnumeration(CodeGenTarget &Target, for (const auto &CI : Infos) { if (LastKind == ClassInfo::Token && CI.Kind != ClassInfo::Token) { OS << " MCK_LAST_TOKEN = " << LastName << ",\n"; + } else if (LastKind < ClassInfo::RegisterClassByHwMode0 && + CI.Kind >= ClassInfo::RegisterClassByHwMode0) { + OS << " MCK_LAST_REGISTER = " << LastName << ",\n"; } else if (LastKind < ClassInfo::UserClass0 && CI.Kind >= ClassInfo::UserClass0) { - OS << " MCK_LAST_REGISTER = " << LastName << ",\n"; + OS << " MCK_LAST_REGCLASS_BY_HWMODE = " << LastName << ",\n"; } + LastKind = (ClassInfo::ClassInfoKind)CI.Kind; LastName = CI.Name; @@ -2382,6 +2437,8 @@ static void emitMatchClassEnumeration(CodeGenTarget &Target, OS << "register class '" << CI.ValueName << "'\n"; else OS << "derived register class\n"; + } else if (CI.isRegisterClassByHwMode()) { + OS << "register class by hwmode\n"; } else { OS << "user defined class '" << CI.ValueName << "'\n"; } @@ -2454,7 +2511,7 @@ static void emitRegisterMatchErrorFunc(AsmMatcherInfo &Info, raw_ostream &OS) { static void emitValidateOperandClass(const CodeGenTarget &Target, AsmMatcherInfo &Info, raw_ostream &OS) { OS << "static unsigned validateOperandClass(MCParsedAsmOperand &GOp, " - << "MatchClassKind Kind) {\n"; + << "MatchClassKind Kind, const MCSubtargetInfo &STI) {\n"; OS << " " << Info.Target.getName() << "Operand &Operand = (" << Info.Target.getName() << "Operand &)GOp;\n"; @@ -2494,8 +2551,65 @@ static void emitValidateOperandClass(const CodeGenTarget &Target, } OS << " } // end switch (Kind)\n\n"; + const CodeGenRegBank &RegBank = Target.getRegBank(); + ArrayRef RegClassesByHwMode = Target.getAllRegClassByHwMode(); + unsigned NumClassesByHwMode = RegClassesByHwMode.size(); + + if (!RegClassesByHwMode.empty()) { + OS << " if (Operand.isReg() && Kind > MCK_LAST_REGISTER &&" + " Kind <= MCK_LAST_REGCLASS_BY_HWMODE) {\n"; + + const CodeGenHwModes &CGH = Target.getHwModes(); + unsigned NumModes = CGH.getNumModeIds(); + + OS << indent(4) + << "static constexpr MatchClassKind RegClassByHwModeMatchTable[" + << NumModes << "][" << RegClassesByHwMode.size() << "] = {\n"; + + // TODO: If the instruction predicates can statically resolve which hwmode, + // directly match the register class + for (unsigned M = 0; M < NumModes; ++M) { + OS << indent(6) << "{ // " << CGH.getModeName(M, /*IncludeDefault=*/true) + << '\n'; + for (unsigned I = 0; I != NumClassesByHwMode; ++I) { + const Record *Class = RegClassesByHwMode[I]; + const HwModeSelect &ModeSelect = CGH.getHwModeSelect(Class); + + auto FoundMode = + find_if(ModeSelect.Items, [=](const HwModeSelect::PairType P) { + return P.first == M; + }); + + if (FoundMode == ModeSelect.Items.end()) { + OS << indent(8) << "InvalidMatchClass, // Missing mode\n"; + } else { + const CodeGenRegisterClass *RegClass = + RegBank.getRegClass(FoundMode->second); + const ClassInfo *CI = + Info.RegisterClassClasses.at(RegClass->getDef()); + OS << indent(8) << CI->Name << ",\n"; + } + } + + OS << indent(6) << "},\n"; + } + + OS << indent(4) << "};\n\n"; + + OS << indent(4) + << "static_assert(MCK_LAST_REGCLASS_BY_HWMODE - MCK_LAST_REGISTER == " + << NumClassesByHwMode << ");\n"; + + OS << indent(4) + << "const unsigned HwMode = " + "STI.getHwMode(MCSubtargetInfo::HwMode_RegInfo);\n" + "Kind = RegClassByHwModeMatchTable[HwMode][Kind - (MCK_LAST_REGISTER " + "+ 1)];\n" + " }\n\n"; + } + // Check for register operands, including sub-classes. - const auto &Regs = Target.getRegBank().getRegisters(); + const auto &Regs = RegBank.getRegisters(); StringRef Namespace = Regs.front().TheDef->getValueAsString("Namespace"); SmallVector Table(1 + Regs.size(), "InvalidMatchClass"); for (const auto &RC : Info.RegisterClasses) { @@ -3758,7 +3872,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { } OS << " }\n"; OS << " MCParsedAsmOperand &Actual = *Operands[ActualIdx];\n"; - OS << " unsigned Diag = validateOperandClass(Actual, Formal);\n"; + OS << " unsigned Diag = validateOperandClass(Actual, Formal, *STI);\n"; OS << " if (Diag == Match_Success) {\n"; OS << " DEBUG_WITH_TYPE(\"asm-matcher\",\n"; OS << " dbgs() << \"match success using generic " diff --git a/llvm/utils/TableGen/AsmWriterEmitter.cpp b/llvm/utils/TableGen/AsmWriterEmitter.cpp index 9f32333f82100..c8c6c23bea014 100644 --- a/llvm/utils/TableGen/AsmWriterEmitter.cpp +++ b/llvm/utils/TableGen/AsmWriterEmitter.cpp @@ -945,7 +945,7 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { if (Rec->isSubClassOf("RegisterOperand")) Rec = Rec->getValueAsDef("RegClass"); - if (Rec->isSubClassOf("RegisterClass")) { + if (Rec->isSubClassOf("RegisterClassLike")) { if (!IAP.isOpMapped(ROName)) { IAP.addOperand(ROName, MIOpNum, PrintMethodIdx); const Record *R = CGA.ResultOperands[i].getRecord(); diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp index 37844663dfc33..af75e44f63e48 100644 --- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp +++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp @@ -1789,6 +1789,23 @@ bool llvm::operator<(const SDTypeConstraint &LHS, const SDTypeConstraint &RHS) { return false; } +/// RegClassByHwMode acts like ValueTypeByHwMode, taking the type of the +/// register class from the active mode. +static TypeSetByHwMode getTypeForRegClassByHwMode(const CodeGenTarget &T, + const Record *R) { + TypeSetByHwMode TypeSet; + RegClassByHwMode Helper(R, T.getHwModes(), T.getRegBank()); + + for (auto [ModeID, RegClass] : Helper) { + ArrayRef RegClassVTs = RegClass->getValueTypes(); + MachineValueTypeSet &ModeTypeSet = TypeSet.getOrCreate(ModeID); + for (const ValueTypeByHwMode &VT : RegClassVTs) + ModeTypeSet.insert(VT.getType(ModeID)); + } + + return TypeSet; +} + // Update the node type to match an instruction operand or result as specified // in the ins or outs lists on the instruction definition. Return true if the // type was actually changed. @@ -1814,13 +1831,16 @@ bool TreePatternNode::UpdateNodeTypeFromInst(unsigned ResNo, // Both RegisterClass and RegisterOperand operands derive their types from a // register class def. const Record *RC = nullptr; - if (Operand->isSubClassOf("RegisterClass")) + if (Operand->isSubClassOf("RegisterClassLike")) RC = Operand; else if (Operand->isSubClassOf("RegisterOperand")) RC = Operand->getValueAsDef("RegClass"); assert(RC && "Unknown operand type"); CodeGenTarget &Tgt = TP.getDAGPatterns().getTargetInfo(); + if (RC->isSubClassOf("RegClassByHwMode")) + return UpdateNodeType(ResNo, getTypeForRegClassByHwMode(Tgt, RC), TP); + return UpdateNodeType(ResNo, Tgt.getRegisterClass(RC).getValueTypes(), TP); } @@ -2306,6 +2326,10 @@ static TypeSetByHwMode getImplicitType(const Record *R, unsigned ResNo, return TypeSetByHwMode(); // Unknown. const Record *RegClass = R->getValueAsDef("RegClass"); const CodeGenTarget &T = TP.getDAGPatterns().getTargetInfo(); + + if (RegClass->isSubClassOf("RegClassByHwMode")) + return getTypeForRegClassByHwMode(T, RegClass); + return TypeSetByHwMode(T.getRegisterClass(RegClass).getValueTypes()); } @@ -2325,6 +2349,11 @@ static TypeSetByHwMode getImplicitType(const Record *R, unsigned ResNo, return TypeSetByHwMode(T.getRegisterClass(R).getValueTypes()); } + if (R->isSubClassOf("RegClassByHwMode")) { + const CodeGenTarget &T = CDP.getTargetInfo(); + return getTypeForRegClassByHwMode(T, R); + } + if (R->isSubClassOf("PatFrags")) { assert(ResNo == 0 && "FIXME: PatFrag with multiple results?"); // Pattern fragment types will be resolved when they are inlined. @@ -3581,7 +3610,7 @@ void CodeGenDAGPatterns::FindPatternInputsAndOutputs( continue; } - if (Val->getDef()->isSubClassOf("RegisterClass") || + if (Val->getDef()->isSubClassOf("RegisterClassLike") || Val->getDef()->isSubClassOf("ValueType") || Val->getDef()->isSubClassOf("RegisterOperand") || Val->getDef()->isSubClassOf("PointerLikeRegClass")) { diff --git a/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp b/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp index 54ea0f1bfad5a..6c1a3e9977c28 100644 --- a/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp +++ b/llvm/utils/TableGen/Common/CodeGenInstAlias.cpp @@ -38,17 +38,6 @@ unsigned CodeGenInstAlias::ResultOperand::getMINumOperands() const { return MIOpInfo->getNumArgs(); } -static const Record *getInitValueAsRegClass(const Init *V) { - if (const auto *VDefInit = dyn_cast(V)) { - const Record *R = VDefInit->getDef(); - if (R->isSubClassOf("RegisterClass")) - return R; - if (R->isSubClassOf("RegisterOperand")) - return R->getValueAsDef("RegClass"); - } - return nullptr; -} - using ResultOperand = CodeGenInstAlias::ResultOperand; static Expected matchSimpleOperand(const Init *Arg, @@ -64,14 +53,20 @@ static Expected matchSimpleOperand(const Init *Arg, const Record *ArgRec = ArgDef->getDef(); // Match 'RegClass:$name' or 'RegOp:$name'. - if (const Record *ArgRC = getInitValueAsRegClass(Arg)) { - if (!T.getRegisterClass(OpRC).hasSubClass(&T.getRegisterClass(ArgRC))) - return createStringError( - "argument register class" + ArgRC->getName() + - " is not a subclass of operand register class " + - OpRC->getName()); - if (!ArgName) - return createStringError("register class argument must have a name"); + if (const Record *ArgRC = T.getInitValueAsRegClassLike(Arg)) { + if (ArgRC->isSubClassOf("RegisterClass")) { + if (!T.getRegisterClass(OpRC).hasSubClass(&T.getRegisterClass(ArgRC))) + return createStringError( + "argument register class" + ArgRC->getName() + + " is not a subclass of operand register class " + + OpRC->getName()); + if (!ArgName) + return createStringError( + "register class argument must have a name"); + } + + // TODO: Verify RegClassByHwMode usage + return ResultOperand::createRecord(ArgName->getAsUnquotedString(), ArgRec); } diff --git a/llvm/utils/TableGen/Common/CodeGenInstruction.cpp b/llvm/utils/TableGen/Common/CodeGenInstruction.cpp index ff70d50971b89..93d4f4bfe5d27 100644 --- a/llvm/utils/TableGen/Common/CodeGenInstruction.cpp +++ b/llvm/utils/TableGen/Common/CodeGenInstruction.cpp @@ -118,7 +118,7 @@ CGIOperandList::CGIOperandList(const Record *R) : TheDef(R) { VariadicOuts = true; isVariadic = true; continue; - } else if (Rec->isSubClassOf("RegisterClass")) { + } else if (Rec->isSubClassOf("RegisterClassLike")) { OperandType = "OPERAND_REGISTER"; } else if (!Rec->isSubClassOf("PointerLikeRegClass") && !Rec->isSubClassOf("unknown_class")) { diff --git a/llvm/utils/TableGen/Common/CodeGenTarget.cpp b/llvm/utils/TableGen/Common/CodeGenTarget.cpp index 8982927eeefcf..3db0d07eec88f 100644 --- a/llvm/utils/TableGen/Common/CodeGenTarget.cpp +++ b/llvm/utils/TableGen/Common/CodeGenTarget.cpp @@ -197,6 +197,38 @@ void CodeGenTarget::ReadLegalValueTypes() const { LegalValueTypes.erase(llvm::unique(LegalValueTypes), LegalValueTypes.end()); } +const Record *CodeGenTarget::getInitValueAsRegClass( + const Init *V, bool AssumeRegClassByHwModeIsDefault) const { + const Record *RegClassLike = getInitValueAsRegClassLike(V); + if (!RegClassLike || RegClassLike->isSubClassOf("RegisterClass")) + return RegClassLike; + + // FIXME: We should figure out the hwmode and dispatch. But this interface + // is broken, we should be returning a register class. The expected uses + // will use the same RegBanks in all modes. + if (AssumeRegClassByHwModeIsDefault && + RegClassLike->isSubClassOf("RegClassByHwMode")) { + const HwModeSelect &ModeSelect = getHwModes().getHwModeSelect(RegClassLike); + if (ModeSelect.Items.empty()) + return nullptr; + return ModeSelect.Items.front().second; + } + + return nullptr; +} + +const Record *CodeGenTarget::getInitValueAsRegClassLike(const Init *V) const { + const DefInit *VDefInit = dyn_cast(V); + if (!VDefInit) + return nullptr; + + const Record *RegClass = VDefInit->getDef(); + if (RegClass->isSubClassOf("RegisterOperand")) + return RegClass->getValueAsDef("RegClass"); + + return RegClass->isSubClassOf("RegisterClassLike") ? RegClass : nullptr; +} + CodeGenSchedModels &CodeGenTarget::getSchedModels() const { if (!SchedModels) SchedModels = std::make_unique(Records, *this); diff --git a/llvm/utils/TableGen/Common/CodeGenTarget.h b/llvm/utils/TableGen/Common/CodeGenTarget.h index ac138344c83c6..b6f9d4209a13e 100644 --- a/llvm/utils/TableGen/Common/CodeGenTarget.h +++ b/llvm/utils/TableGen/Common/CodeGenTarget.h @@ -134,6 +134,14 @@ class CodeGenTarget { const CodeGenRegisterClass &getRegisterClass(const Record *R) const; + /// Convenience wrapper to avoid hardcoding the name of RegClassByHwMode + /// everywhere. This is here instead of CodeGenRegBank to avoid the fatal + /// error that occurs when no RegisterClasses are defined when constructing + /// the bank. + ArrayRef getAllRegClassByHwMode() const { + return Records.getAllDerivedDefinitions("RegClassByHwMode"); + } + /// getRegisterVTs - Find the union of all possible SimpleValueTypes for the /// specified physical register. std::vector getRegisterVTs(const Record *R) const; @@ -144,6 +152,21 @@ class CodeGenTarget { return LegalValueTypes; } + /// If \p V is a DefInit that can be interpreted as a RegisterClass (e.g., + /// it's a RegisterOperand, or a direct RegisterClass reference), return the + /// Record for that RegisterClass. + /// + /// AssumeRegClassByHwModeIsDefault is a hack which should be removed. It only + /// happens to be adequate for the current GlobalISel usage. + const Record * + getInitValueAsRegClass(const Init *V, + bool AssumeRegClassByHwModeIsDefault = false) const; + + /// If \p V is a DefInit that can be interpreted as a RegisterClassLike, + /// return the Record. This is used as a convenience function to handle direct + /// RegisterClass references, or those wrapped in a RegisterOperand. + const Record *getInitValueAsRegClassLike(const Init *V) const; + CodeGenSchedModels &getSchedModels() const; const CodeGenHwModes &getHwModes() const { return CGH; } diff --git a/llvm/utils/TableGen/Common/InfoByHwMode.cpp b/llvm/utils/TableGen/Common/InfoByHwMode.cpp index cb4f8876c648a..a6e2fc415f350 100644 --- a/llvm/utils/TableGen/Common/InfoByHwMode.cpp +++ b/llvm/utils/TableGen/Common/InfoByHwMode.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "InfoByHwMode.h" +#include "CodeGenRegisters.h" #include "CodeGenTarget.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Twine.h" @@ -186,6 +187,19 @@ void RegSizeInfoByHwMode::writeToStream(raw_ostream &OS) const { OS << '}'; } +RegClassByHwMode::RegClassByHwMode(const Record *R, const CodeGenHwModes &CGH, + const CodeGenRegBank &RegBank) { + const HwModeSelect &MS = CGH.getHwModeSelect(R); + + for (auto [ModeID, RegClassRec] : MS.Items) { + assert(RegClassRec && RegClassRec->isSubClassOf("RegisterClass") && + "Register class must subclass RegisterClass"); + const CodeGenRegisterClass *RegClass = RegBank.getRegClass(RegClassRec); + if (!Map.try_emplace(ModeID, RegClass).second) + llvm_unreachable("duplicate entry"); + } +} + SubRegRange::SubRegRange(const Record *R) { Size = R->getValueAsInt("Size"); Offset = R->getValueAsInt("Offset"); diff --git a/llvm/utils/TableGen/Common/InfoByHwMode.h b/llvm/utils/TableGen/Common/InfoByHwMode.h index 7925599a98a0c..c730b7397c173 100644 --- a/llvm/utils/TableGen/Common/InfoByHwMode.h +++ b/llvm/utils/TableGen/Common/InfoByHwMode.h @@ -28,6 +28,8 @@ namespace llvm { +class CodeGenRegBank; +class CodeGenRegisterClass; class Record; class raw_ostream; @@ -244,6 +246,13 @@ struct EncodingInfoByHwMode : public InfoByHwMode { EncodingInfoByHwMode() = default; }; +struct RegClassByHwMode : public InfoByHwMode { +public: + RegClassByHwMode(const Record *R, const CodeGenHwModes &CGH, + const CodeGenRegBank &RegBank); + RegClassByHwMode() = default; +}; + } // namespace llvm #endif // LLVM_UTILS_TABLEGEN_COMMON_INFOBYHWMODE_H diff --git a/llvm/utils/TableGen/Common/InstructionEncoding.cpp b/llvm/utils/TableGen/Common/InstructionEncoding.cpp index 22163e1898333..c6c006b527b05 100644 --- a/llvm/utils/TableGen/Common/InstructionEncoding.cpp +++ b/llvm/utils/TableGen/Common/InstructionEncoding.cpp @@ -14,7 +14,9 @@ using namespace llvm; -static std::string findOperandDecoderMethod(const Record *Record) { +std::pair +InstructionEncoding::findOperandDecoderMethod(const CodeGenTarget &Target, + const Record *Record) { std::string Decoder; const RecordVal *DecoderString = Record->getValue("DecoderMethod"); @@ -23,24 +25,27 @@ static std::string findOperandDecoderMethod(const Record *Record) { if (String) { Decoder = String->getValue().str(); if (!Decoder.empty()) - return Decoder; + return {Decoder, false}; } if (Record->isSubClassOf("RegisterOperand")) // Allows use of a DecoderMethod in referenced RegisterClass if set. - return findOperandDecoderMethod(Record->getValueAsDef("RegClass")); + return findOperandDecoderMethod(Target, Record->getValueAsDef("RegClass")); if (Record->isSubClassOf("RegisterClass")) { Decoder = "Decode" + Record->getName().str() + "RegisterClass"; + } else if (Record->isSubClassOf("RegClassByHwMode")) { + Decoder = "Decode" + Record->getName().str() + "RegClassByHwMode"; } else if (Record->isSubClassOf("PointerLikeRegClass")) { Decoder = "DecodePointerLikeRegClass" + utostr(Record->getValueAsInt("RegClassKind")); } - return Decoder; + return {Decoder, true}; } -static OperandInfo getOpInfo(const Record *TypeRecord) { +OperandInfo InstructionEncoding::getOpInfo(const CodeGenTarget &Target, + const Record *TypeRecord) { const RecordVal *HasCompleteDecoderVal = TypeRecord->getValue("hasCompleteDecoder"); const BitInit *HasCompleteDecoderBit = @@ -50,7 +55,8 @@ static OperandInfo getOpInfo(const Record *TypeRecord) { bool HasCompleteDecoder = HasCompleteDecoderBit ? HasCompleteDecoderBit->getValue() : true; - return OperandInfo(findOperandDecoderMethod(TypeRecord), HasCompleteDecoder); + return OperandInfo(findOperandDecoderMethod(Target, TypeRecord).first, + HasCompleteDecoder); } void InstructionEncoding::parseVarLenEncoding(const VarLenInst &VLI) { @@ -171,15 +177,16 @@ void InstructionEncoding::parseFixedLenEncoding( } } -void InstructionEncoding::parseVarLenOperands(const VarLenInst &VLI) { +void InstructionEncoding::parseVarLenOperands(const CodeGenTarget &Target, + const VarLenInst &VLI) { SmallVector TiedTo; for (const auto &[Idx, Op] : enumerate(Inst->Operands)) { if (Op.MIOperandInfo && Op.MIOperandInfo->getNumArgs() > 0) for (auto *Arg : Op.MIOperandInfo->getArgs()) - Operands.push_back(getOpInfo(cast(Arg)->getDef())); + Operands.push_back(getOpInfo(Target, cast(Arg)->getDef())); else - Operands.push_back(getOpInfo(Op.Rec)); + Operands.push_back(getOpInfo(Target, Op.Rec)); int TiedReg = Op.getTiedRegister(); TiedTo.push_back(-1); @@ -314,7 +321,8 @@ static void addOneOperandFields(const Record *EncodingDef, } } -void InstructionEncoding::parseFixedLenOperands(const BitsInit &Bits) { +void InstructionEncoding::parseFixedLenOperands(const CodeGenTarget &Target, + const BitsInit &Bits) { // Search for tied operands, so that we can correctly instantiate // operands that are not explicitly represented in the encoding. std::map TiedNames; @@ -340,7 +348,7 @@ void InstructionEncoding::parseFixedLenOperands(const BitsInit &Bits) { for (const CGIOperandList::OperandInfo &Op : Inst->Operands) { // Lookup the decoder method and construct a new OperandInfo to hold our // result. - OperandInfo OpInfo = getOpInfo(Op.Rec); + OperandInfo OpInfo = getOpInfo(Target, Op.Rec); // If we have named sub-operands... if (Op.MIOperandInfo && !Op.SubOpNames[0].empty()) { @@ -359,7 +367,7 @@ void InstructionEncoding::parseFixedLenOperands(const BitsInit &Bits) { for (auto [SubOpName, SubOp] : zip_equal(Op.SubOpNames, Op.MIOperandInfo->getArgs())) { const Record *SubOpRec = cast(SubOp)->getDef(); - OperandInfo SubOpInfo = getOpInfo(SubOpRec); + OperandInfo SubOpInfo = getOpInfo(Target, SubOpRec); addOneOperandFields(EncodingDef, Bits, TiedNames, SubOpRec, SubOpName, SubOpInfo); Operands.push_back(std::move(SubOpInfo)); @@ -387,7 +395,8 @@ void InstructionEncoding::parseFixedLenOperands(const BitsInit &Bits) { } } -InstructionEncoding::InstructionEncoding(const Record *EncodingDef, +InstructionEncoding::InstructionEncoding(const CodeGenTarget &Target, + const Record *EncodingDef, const CodeGenInstruction *Inst) : EncodingDef(EncodingDef), Inst(Inst) { const Record *InstDef = Inst->TheDef; @@ -408,13 +417,13 @@ InstructionEncoding::InstructionEncoding(const Record *EncodingDef, parseVarLenEncoding(VLI); // If the encoding has a custom decoder, don't bother parsing the operands. if (DecoderMethod.empty()) - parseVarLenOperands(VLI); + parseVarLenOperands(Target, VLI); } else { const auto *BI = cast(InstField->getValue()); parseFixedLenEncoding(*BI); // If the encoding has a custom decoder, don't bother parsing the operands. if (DecoderMethod.empty()) - parseFixedLenOperands(*BI); + parseFixedLenOperands(Target, *BI); } if (DecoderMethod.empty()) { diff --git a/llvm/utils/TableGen/Common/InstructionEncoding.h b/llvm/utils/TableGen/Common/InstructionEncoding.h index 40c89dd4c6f2d..412f93402170c 100644 --- a/llvm/utils/TableGen/Common/InstructionEncoding.h +++ b/llvm/utils/TableGen/Common/InstructionEncoding.h @@ -21,6 +21,7 @@ namespace llvm { class BitsInit; class CodeGenInstruction; +class CodeGenTarget; class Record; class RecordVal; class VarLenInst; @@ -91,7 +92,7 @@ class InstructionEncoding { SmallVector Operands; public: - InstructionEncoding(const Record *EncodingDef, + InstructionEncoding(const CodeGenTarget &Target, const Record *EncodingDef, const CodeGenInstruction *Inst); /// Returns the Record this encoding originates from. @@ -137,12 +138,20 @@ class InstructionEncoding { /// Returns information about the operands' contribution to this encoding. ArrayRef getOperands() const { return Operands; } + /// \returns the effective value of the DecoderMethod field. If DecoderMethod + /// is an explictly set value, return false for second. + static std::pair + findOperandDecoderMethod(const CodeGenTarget &Target, const Record *Record); + + static OperandInfo getOpInfo(const CodeGenTarget &Target, + const Record *TypeRecord); + private: void parseVarLenEncoding(const VarLenInst &VLI); void parseFixedLenEncoding(const BitsInit &RecordInstBits); - void parseVarLenOperands(const VarLenInst &VLI); - void parseFixedLenOperands(const BitsInit &Bits); + void parseVarLenOperands(const CodeGenTarget &Target, const VarLenInst &VLI); + void parseFixedLenOperands(const CodeGenTarget &Target, const BitsInit &Bits); }; } // namespace llvm diff --git a/llvm/utils/TableGen/DAGISelMatcherGen.cpp b/llvm/utils/TableGen/DAGISelMatcherGen.cpp index 227311b0a3bc8..d84bfa8d0c92e 100644 --- a/llvm/utils/TableGen/DAGISelMatcherGen.cpp +++ b/llvm/utils/TableGen/DAGISelMatcherGen.cpp @@ -238,7 +238,7 @@ void MatcherGen::EmitLeafMatchCode(const TreePatternNode &N) { } if ( // Handle register references. Nothing to do here, they always match. - LeafRec->isSubClassOf("RegisterClass") || + LeafRec->isSubClassOf("RegisterClassLike") || LeafRec->isSubClassOf("RegisterOperand") || LeafRec->isSubClassOf("PointerLikeRegClass") || LeafRec->isSubClassOf("SubRegIndex") || diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp index 083d78c4986f0..10863343d4625 100644 --- a/llvm/utils/TableGen/DecoderEmitter.cpp +++ b/llvm/utils/TableGen/DecoderEmitter.cpp @@ -13,6 +13,7 @@ #include "Common/CodeGenHwModes.h" #include "Common/CodeGenInstruction.h" +#include "Common/CodeGenRegisters.h" #include "Common/CodeGenTarget.h" #include "Common/InfoByHwMode.h" #include "Common/InstructionEncoding.h" @@ -276,6 +277,8 @@ class DecoderEmitter { ArrayRef InstrLen) const; void emitPredicateFunction(formatted_raw_ostream &OS, const PredicateSet &Predicates) const; + + void emitRegClassByHwModeDecoders(formatted_raw_ostream &OS) const; void emitDecoderFunction(formatted_raw_ostream &OS, const DecoderSet &Decoders, unsigned BucketBitWidth) const; @@ -842,6 +845,56 @@ void DecoderEmitter::emitPredicateFunction( OS << "}\n\n"; } +/// Emit a default implementation of a decoder for all RegClassByHwModes which +/// do not have an explicit DecoderMethodSet, which dispatches over the decoder +/// methods for the member classes. +void DecoderEmitter::emitRegClassByHwModeDecoders( + formatted_raw_ostream &OS) const { + const CodeGenHwModes &CGH = Target.getHwModes(); + if (CGH.getNumModeIds() == 1) + return; + + ArrayRef RegClassByHwMode = Target.getAllRegClassByHwMode(); + if (RegClassByHwMode.empty()) + return; + + for (const Record *ClassByHwMode : RegClassByHwMode) { + // Ignore cases that had an explicit DecoderMethod set. + if (!InstructionEncoding::findOperandDecoderMethod(Target, ClassByHwMode) + .second) + continue; + + const HwModeSelect &ModeSelect = CGH.getHwModeSelect(ClassByHwMode); + + // Mips has a system where this is only used by compound operands with + // custom decoders, and we don't try to detect if this decoder is really + // needed. + OS << "[[maybe_unused]]\n"; + + OS << "static DecodeStatus Decode" << ClassByHwMode->getName() + << "RegClassByHwMode"; + OS << R"((MCInst &Inst, unsigned Imm, uint64_t Addr, const MCDisassembler *Decoder) { + switch (Decoder->getSubtargetInfo().getHwMode(MCSubtargetInfo::HwMode_RegInfo)) { +)"; + for (auto [ModeID, RegClassRec] : ModeSelect.Items) { + OS << indent(2) << "case " << ModeID << ": // " + << CGH.getModeName(ModeID, /*IncludeDefault=*/true) << '\n' + << indent(4) << "return " + << InstructionEncoding::findOperandDecoderMethod(Target, RegClassRec) + .first + << "(Inst, Imm, Addr, Decoder);\n"; + } + OS << indent(2) << R"(default: + llvm_unreachable("no decoder for hwmode"); + } +} + +)"; + } + + OS << '\n'; +} + void DecoderEmitter::emitDecoderFunction(formatted_raw_ostream &OS, const DecoderSet &Decoders, unsigned BucketBitWidth) const { @@ -1800,7 +1853,7 @@ void DecoderEmitter::parseInstructionEncodings() { continue; } unsigned EncodingID = Encodings.size(); - Encodings.emplace_back(EncodingDef, Inst); + Encodings.emplace_back(Target, EncodingDef, Inst); EncodingIDsByHwMode[HwModeID].push_back(EncodingID); } continue; // Ignore encoding specified by Instruction itself. @@ -1812,7 +1865,7 @@ void DecoderEmitter::parseInstructionEncodings() { } unsigned EncodingID = Encodings.size(); - Encodings.emplace_back(InstDef, Inst); + Encodings.emplace_back(Target, InstDef, Inst); // This instruction is encoded the same on all HwModes. // According to user needs, add it to all, some, or only the default HwMode. @@ -1835,7 +1888,8 @@ void DecoderEmitter::parseInstructionEncodings() { continue; } unsigned EncodingID = Encodings.size(); - Encodings.emplace_back(EncodingDef, &Target.getInstruction(InstDef)); + Encodings.emplace_back(Target, EncodingDef, + &Target.getInstruction(InstDef)); EncodingIDsByHwMode[DefaultMode].push_back(EncodingID); } @@ -1892,6 +1946,8 @@ template constexpr uint32_t InsnBitWidth = 0; emitInstrLenTable(OS, InstrLen); } + emitRegClassByHwModeDecoders(OS); + // Map of (bitwidth, namespace, hwmode) tuple to encoding IDs. // Its organized as a nested map, with the (namespace, hwmode) as the key for // the inner map and bitwidth as the key for the outer map. We use std::map diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index 7ae6107b98554..f7993ce0b845d 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -267,16 +267,6 @@ static Error isTrivialOperatorNode(const TreePatternNode &N) { return failedImport(Explanation); } -static const Record *getInitValueAsRegClass(const Init *V) { - if (const DefInit *VDefInit = dyn_cast(V)) { - if (VDefInit->getDef()->isSubClassOf("RegisterOperand")) - return VDefInit->getDef()->getValueAsDef("RegClass"); - if (VDefInit->getDef()->isSubClassOf("RegisterClass")) - return VDefInit->getDef(); - } - return nullptr; -} - static std::string getScopedName(unsigned Scope, const std::string &Name) { return ("pred:" + Twine(Scope) + ":" + Name).str(); } @@ -475,6 +465,9 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter { const CodeGenRegisterClass * inferRegClassFromPattern(const TreePatternNode &N) const; + const CodeGenRegisterClass * + inferRegClassFromRegisterClassLike(const Record *RecClassLike) const; + const CodeGenRegisterClass * inferRegClassFromInstructionPattern(const TreePatternNode &N, unsigned ResIdx) const; @@ -1140,10 +1133,12 @@ Error GlobalISelEmitter::importChildMatcher( auto *ChildRec = ChildDefInit->getDef(); // Check for register classes. - if (ChildRec->isSubClassOf("RegisterClass") || + if (ChildRec->isSubClassOf("RegisterClassLike") || ChildRec->isSubClassOf("RegisterOperand")) { OM.addPredicate( - Target.getRegisterClass(getInitValueAsRegClass(ChildDefInit))); + Target.getRegisterClass(Target.getInitValueAsRegClass( + ChildDefInit, + /*AssumeRegClassByHwModeIsDefault=*/true))); return Error::success(); } @@ -1285,7 +1280,7 @@ Error GlobalISelEmitter::importNamedNodeRenderer( // TODO: All special cases are handled above. Remove this check and add // CopyRenderer unconditionally. - if (R->isSubClassOf("RegisterClass") || + if (R->isSubClassOf("RegisterClassLike") || R->isSubClassOf("RegisterOperand") || R->isSubClassOf("ValueType")) { MIBuilder.addRenderer(NodeName); return Error::success(); @@ -1640,7 +1635,8 @@ Expected GlobalISelEmitter::importExplicitUseRenderers( } // If this is a source operand, this is just a subregister copy. - const Record *RCDef = getInitValueAsRegClass(ValChild.getLeafValue()); + const Record *RCDef = + Target.getInitValueAsRegClass(ValChild.getLeafValue()); if (!RCDef) return failedImport("EXTRACT_SUBREG child #0 could not " "be coerced to a register class"); @@ -1672,7 +1668,7 @@ Expected GlobalISelEmitter::importExplicitUseRenderers( return failedImport("REG_SEQUENCE child #0 is not a leaf"); const Record *RCDef = - getInitValueAsRegClass(Dst.getChild(0).getLeafValue()); + Target.getInitValueAsRegClass(Dst.getChild(0).getLeafValue()); if (!RCDef) return failedImport("REG_SEQUENCE child #0 could not " "be coerced to a register class"); @@ -1791,7 +1787,7 @@ Error GlobalISelEmitter::constrainOperands(action_iterator InsertPt, // COPY_TO_REGCLASS does not provide operand constraints itself but the // result is constrained to the class given by the second child. const Record *DstIOpRec = - getInitValueAsRegClass(Dst.getChild(1).getLeafValue()); + Target.getInitValueAsRegClass(Dst.getChild(1).getLeafValue()); if (DstIOpRec == nullptr) return failedImport("COPY_TO_REGCLASS operand #1 isn't a register class"); @@ -1897,7 +1893,7 @@ Error GlobalISelEmitter::constrainOperands(action_iterator InsertPt, const CodeGenRegisterClass * GlobalISelEmitter::getRegClassFromLeaf(const TreePatternNode &Leaf) const { assert(Leaf.isLeaf() && "Expected leaf?"); - const Record *RCRec = getInitValueAsRegClass(Leaf.getLeafValue()); + const Record *RCRec = Target.getInitValueAsRegClass(Leaf.getLeafValue()); if (!RCRec) return nullptr; return CGRegs.getRegClass(RCRec); @@ -1926,6 +1922,26 @@ GlobalISelEmitter::inferRegClassFromPattern(const TreePatternNode &N) const { return inferRegClassFromInstructionPattern(N, /*ResIdx=*/0); } +const CodeGenRegisterClass * +GlobalISelEmitter::inferRegClassFromRegisterClassLike( + const Record *RegClassDef) const { + if (RegClassDef->isSubClassOf("RegClassByHwMode")) { + // TODO: We only are trying to match the regbank, which we assume is the + // same across modes, but we are just picking one class and assuming they + // all have the same bank. + // + // We should verify there is a common regbank among the possible classes, + // and return that instead of a concrete class. + const HwModeSelect &ModeSelect = + Target.getHwModes().getHwModeSelect(RegClassDef); + if (ModeSelect.Items.empty()) + return nullptr; + return Target.getRegBank().getRegClass(ModeSelect.Items.front().second); + } + + return &Target.getRegisterClass(RegClassDef); +} + const CodeGenRegisterClass * GlobalISelEmitter::inferRegClassFromInstructionPattern(const TreePatternNode &N, unsigned ResIdx) const { @@ -1997,8 +2013,14 @@ GlobalISelEmitter::inferRegClassFromInstructionPattern(const TreePatternNode &N, // from. const auto &DstIOperand = Inst.Operands[ResIdx]; const Record *DstIOpRec = DstIOperand.Rec; - if (DstIOpRec->isSubClassOf("RegisterOperand")) - return &Target.getRegisterClass(DstIOpRec->getValueAsDef("RegClass")); + + if (DstIOpRec->isSubClassOf("RegisterOperand")) { + const Record *RegClassDef = DstIOpRec->getValueAsDef("RegClass"); + return inferRegClassFromRegisterClassLike(RegClassDef); + } + + if (DstIOpRec->isSubClassOf("RegClassByHwMode")) + return inferRegClassFromRegisterClassLike(DstIOpRec); if (DstIOpRec->isSubClassOf("RegisterClass")) return &Target.getRegisterClass(DstIOpRec); @@ -2111,7 +2133,8 @@ Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { InstructionMatcher &InsnMatcher = InsnMatcherOrError.get(); if (Dst.isLeaf()) { - if (const Record *RCDef = getInitValueAsRegClass(Dst.getLeafValue())) { + if (const Record *RCDef = + Target.getInitValueAsRegClass(Dst.getLeafValue())) { const CodeGenRegisterClass &RC = Target.getRegisterClass(RCDef); // We need to replace the def and all its uses with the specified diff --git a/llvm/utils/TableGen/InstrInfoEmitter.cpp b/llvm/utils/TableGen/InstrInfoEmitter.cpp index 0174475e70602..176e4b250b82a 100644 --- a/llvm/utils/TableGen/InstrInfoEmitter.cpp +++ b/llvm/utils/TableGen/InstrInfoEmitter.cpp @@ -14,6 +14,7 @@ #include "Basic/SequenceToOffsetTable.h" #include "Common/CodeGenDAGPatterns.h" #include "Common/CodeGenInstruction.h" +#include "Common/CodeGenRegisters.h" #include "Common/CodeGenSchedule.h" #include "Common/CodeGenTarget.h" #include "Common/PredicateExpander.h" @@ -114,6 +115,7 @@ class InstrInfoEmitter { InstrInfoEmitter::OperandInfoTy InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) { OperandInfoTy Result; + StringRef Namespace = CDP.getTargetInfo().getInstNamespace(); for (auto &Op : Inst.Operands) { // Handle aggregate operands and normal operands the same way by expanding @@ -144,7 +146,13 @@ InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) { if (OpR->isSubClassOf("RegisterOperand")) OpR = OpR->getValueAsDef("RegClass"); - if (OpR->isSubClassOf("RegisterClass")) + + if (OpR->isSubClassOf("RegClassByHwMode")) { + Res += Namespace; + Res += "::"; + Res += OpR->getName(); + Res += ", "; + } else if (OpR->isSubClassOf("RegisterClass")) Res += getQualifiedName(OpR) + "RegClassID, "; else if (OpR->isSubClassOf("PointerLikeRegClass")) Res += utostr(OpR->getValueAsInt("RegClassKind")) + ", "; @@ -155,9 +163,12 @@ InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) { // Fill in applicable flags. Res += "0"; - // Ptr value whose register class is resolved via callback. - if (OpR->isSubClassOf("PointerLikeRegClass")) + if (OpR->isSubClassOf("RegClassByHwMode")) { + Res += "|(1<isSubClassOf("PointerLikeRegClass")) { + // Ptr value whose register class is resolved via callback. Res += "|(1< RegClassByHwMode = Target.getAllRegClassByHwMode(); + unsigned NumClassesByHwMode = RegClassByHwMode.size(); OS << "extern const unsigned " << TargetName << "InstrNameIndices[] = {"; Num = 0; @@ -1044,6 +1061,39 @@ void InstrInfoEmitter::run(raw_ostream &OS) { // MCInstrInfo initialization routine. Timer.startTimer("Emit initialization routine"); + + if (NumClassesByHwMode != 0) { + OS << "extern const int16_t " << TargetName << "RegClassByHwModeTables[" + << NumModes << "][" << NumClassesByHwMode << "] = {\n"; + + for (unsigned M = 0; M < NumModes; ++M) { + OS << " { // " << CGH.getModeName(M, /*IncludeDefault=*/true) << '\n'; + for (unsigned I = 0; I != NumClassesByHwMode; ++I) { + const Record *Class = RegClassByHwMode[I]; + const HwModeSelect &ModeSelect = CGH.getHwModeSelect(Class); + + auto FoundMode = + find_if(ModeSelect.Items, [=](const HwModeSelect::PairType P) { + return P.first == M; + }); + + if (FoundMode == ModeSelect.Items.end()) { + // If a RegClassByHwMode doesn't have an entry corresponding to a + // mode, pad with default register class. + OS << indent(4) << "-1, // Missing mode entry\n"; + } else { + const CodeGenRegisterClass *RegClass = + RegBank.getRegClass(FoundMode->second); + OS << indent(4) << RegClass->getQualifiedIdName() << ",\n"; + } + } + + OS << " },\n"; + } + + OS << "};\n\n"; + } + OS << "static inline void Init" << TargetName << "MCInstrInfo(MCInstrInfo *II) {\n"; OS << " II->InitMCInstrInfo(" << TargetName << "Descs.Insts, " << TargetName @@ -1056,7 +1106,15 @@ void InstrInfoEmitter::run(raw_ostream &OS) { OS << TargetName << "InstrComplexDeprecationInfos, "; else OS << "nullptr, "; - OS << NumberedInstructions.size() << ");\n}\n\n"; + OS << NumberedInstructions.size() << ", "; + + if (NumClassesByHwMode != 0) { + OS << '&' << TargetName << "RegClassByHwModeTables[0][0], " + << NumClassesByHwMode; + } else + OS << "nullptr, 0"; + + OS << ");\n}\n\n"; OS << "} // end namespace llvm\n"; @@ -1098,6 +1156,12 @@ void InstrInfoEmitter::run(raw_ostream &OS) { << "Descs;\n"; OS << "extern const unsigned " << TargetName << "InstrNameIndices[];\n"; OS << "extern const char " << TargetName << "InstrNameData[];\n"; + + if (NumClassesByHwMode != 0) { + OS << "extern const int16_t " << TargetName << "RegClassByHwModeTables[" + << NumModes << "][" << NumClassesByHwMode << "];\n"; + } + if (HasDeprecationFeatures) OS << "extern const uint8_t " << TargetName << "InstrDeprecationFeatures[];\n"; @@ -1108,7 +1172,13 @@ void InstrInfoEmitter::run(raw_ostream &OS) { << "(const TargetSubtargetInfo &STI, unsigned CFSetupOpcode, unsigned " "CFDestroyOpcode, unsigned CatchRetOpcode, unsigned ReturnOpcode)\n" << " : TargetInstrInfo(CFSetupOpcode, CFDestroyOpcode, CatchRetOpcode, " - "ReturnOpcode) {\n" + "ReturnOpcode"; + if (NumClassesByHwMode != 0) + OS << ", " << TargetName + << "RegClassByHwModeTables[STI.getHwMode(MCSubtargetInfo::HwMode_" + "RegInfo)]"; + + OS << ") {\n" << " InitMCInstrInfo(" << TargetName << "Descs.Insts, " << TargetName << "InstrNameIndices, " << TargetName << "InstrNameData, "; if (HasDeprecationFeatures) @@ -1119,8 +1189,16 @@ void InstrInfoEmitter::run(raw_ostream &OS) { OS << TargetName << "InstrComplexDeprecationInfos, "; else OS << "nullptr, "; - OS << NumberedInstructions.size() << ");\n}\n"; - OS << "} // end namespace llvm\n"; + OS << NumberedInstructions.size(); + + if (NumClassesByHwMode != 0) { + OS << ", &" << TargetName << "RegClassByHwModeTables[0][0], " + << NumClassesByHwMode; + } + + OS << ");\n" + "}\n" + "} // end namespace llvm\n"; OS << "#endif // GET_INSTRINFO_CTOR_DTOR\n\n"; @@ -1316,6 +1394,15 @@ void InstrInfoEmitter::emitEnums( } OS << " INSTRUCTION_LIST_END = " << NumberedInstructions.size() << '\n'; OS << " };\n"; + + ArrayRef RegClassesByHwMode = Target.getAllRegClassByHwMode(); + if (!RegClassesByHwMode.empty()) { + OS << " enum RegClassByHwModeUses : uint16_t {\n"; + for (const Record *ClassByHwMode : RegClassesByHwMode) + OS << indent(4) << ClassByHwMode->getName() << ",\n"; + OS << " };\n"; + } + OS << "} // end namespace llvm::" << Namespace << '\n'; OS << "#endif // GET_INSTRINFO_ENUM\n\n"; diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp index e218927621dd3..0f42d49a6bea1 100644 --- a/llvm/utils/TableGen/SubtargetEmitter.cpp +++ b/llvm/utils/TableGen/SubtargetEmitter.cpp @@ -1792,7 +1792,8 @@ 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("SubRegRange")) { + P.second->isSubClassOf("SubRegRange") || + P.second->isSubClassOf("RegisterClassLike")) { RegInfoModes |= (1 << (P.first - 1)); } else if (P.second->isSubClassOf("InstructionEncoding")) { EncodingInfoModes |= (1 << (P.first - 1));