diff --git a/llvm/include/llvm/MC/MCSubtargetInfo.h b/llvm/include/llvm/MC/MCSubtargetInfo.h index f172a799aa333..ff76435d60843 100644 --- a/llvm/include/llvm/MC/MCSubtargetInfo.h +++ b/llvm/include/llvm/MC/MCSubtargetInfo.h @@ -240,7 +240,32 @@ class MCSubtargetInfo { return ProcFeatures; } - virtual unsigned getHwMode() const { return 0; } + /// HwMode IDs are stored and accessed in a bit set format, enabling + /// users to efficiently retrieve specific IDs, such as the RegInfo + /// HwMode ID, from the set as required. Using this approach, various + /// types of HwMode IDs can be added to a subtarget to manage different + /// attributes within that subtarget, significantly enhancing the + /// scalability and usability of HwMode. Moreover, to ensure compatibility, + /// 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_EncodingInfo // Return the HwMode ID that controls the EncodingInfo. + }; + + /// Return a bit set containing all HwMode IDs of the current subtarget. + virtual unsigned getHwModeSet() const { return 0; } + + /// HwMode ID corresponding to the 'type' parameter is retrieved from the + /// HwMode bit set of the current subtarget. It’s important to note that if + /// the current subtarget possesses two HwMode IDs and both control a single + /// attribute (such as RegInfo), this interface will result in an error. + virtual unsigned getHwMode(enum HwModeType type = HwMode_Default) const { + return 0; + } /// Return the cache size in bytes for the given level of cache. /// Level is zero-based, so a value of zero means the first level of diff --git a/llvm/test/TableGen/HwModeBitSet.td b/llvm/test/TableGen/HwModeBitSet.td new file mode 100644 index 0000000000000..b2de6e8e012c5 --- /dev/null +++ b/llvm/test/TableGen/HwModeBitSet.td @@ -0,0 +1,162 @@ +// This is to test the scenario where different HwMode attributes coexist. +// RUN: llvm-tblgen -gen-register-info -register-info-debug -I %p/../../include %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-REG +// RUN: llvm-tblgen -gen-subtarget -I %p/../../include %s -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SUBTARGET + + +include "llvm/Target/Target.td" + +def TestTargetInstrInfo : InstrInfo; + +def TestTarget : Target { + let InstructionSet = TestTargetInstrInfo; +} + +def TestMode : HwMode<"+feat", []>; +def TestMode1 : HwMode<"+feat1", []>; +def TestMode2 : HwMode<"+feat2", []>; + +class MyReg + : Register { + let Namespace = "Test"; +} + +class MyClass types, dag registers> + : RegisterClass<"Test", 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 X7 : MyReg<"x7">; +def X8 : MyReg<"x8">; +def X9 : MyReg<"x9">; +def X10 : MyReg<"x10">; +def X11 : MyReg<"x11">; +def X12 : MyReg<"x12">; +def X13 : MyReg<"x13">; +def X14 : MyReg<"x14">; +def X15 : MyReg<"x15">; + +def ValueModeVT : ValueTypeByHwMode<[DefaultMode, TestMode, TestMode1], + [i32, i64, f32]>; + +let RegInfos = RegInfoByHwMode<[DefaultMode, TestMode], + [RegInfo<32,32,32>, RegInfo<64,64,64>]> in +def XRegs : MyClass<32, [ValueModeVT], (sequence "X%u", 0, 15)>; + +def sub_even : SubRegIndex<32> { + let SubRegRanges = SubRegRangeByHwMode<[DefaultMode, TestMode], + [SubRegRange<32>, SubRegRange<64>]>; +} +def sub_odd : SubRegIndex<32, 32> { + let SubRegRanges = SubRegRangeByHwMode<[DefaultMode, TestMode], + [SubRegRange<32, 32>, SubRegRange<64, 64>]>; +} + +def XPairs : RegisterTuples<[sub_even, sub_odd], + [(decimate (rotl XRegs, 0), 2), + (decimate (rotl XRegs, 1), 2)]>; + +let RegInfos = RegInfoByHwMode<[DefaultMode, TestMode], + [RegInfo<64,64,32>, RegInfo<128,128,64>]> in +def XPairsClass : MyClass<64, [untyped], (add XPairs)>; + +// Modes who are not controlling Register related features will be manipulated +// the same as DefaultMode. +// CHECK-REG-LABEL: RegisterClass XRegs: +// CHECK-REG: SpillSize: { Default:32 TestMode:64 TestMode1:32 TestMode2:32 } +// CHECK-REG: SpillAlignment: { Default:32 TestMode:64 TestMode1:32 TestMode2:32 } +// CHECK-REG: Regs: X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + +// CHECK-REG-LABEL: RegisterClass XPairsClass: +// CHECK-REG: SpillSize: { Default:64 TestMode:128 TestMode1:64 TestMode2:64 } +// CHECK-REG: SpillAlignment: { Default:32 TestMode:64 TestMode1:32 TestMode2:32 } +// CHECK-REG: CoveredBySubRegs: 1 +// CHECK-REG: Regs: X0_X1 X2_X3 X4_X5 X6_X7 X8_X9 X10_X11 X12_X13 X14_X15 + +// CHECK-REG-LABEL: SubRegIndex sub_even: +// CHECK-REG: Offset: { Default:0 TestMode:0 TestMode1:0 TestMode2:0 } +// CHECK-REG: Size: { Default:32 TestMode:64 TestMode1:32 TestMode2:32 } +// CHECK-REG-LABEL: SubRegIndex sub_odd: +// CHECK-REG: Offset: { Default:32 TestMode:64 TestMode1:32 TestMode2:32 } +// CHECK-REG: Size: { Default:32 TestMode:64 TestMode1:32 TestMode2:32 } + +//============================================================================// +//--------------------- Encoding/Decoding parts ------------------------------// +//============================================================================// +def fooTypeEncDefault : InstructionEncoding { + let Size = 8; + field bits<64> SoftFail = 0; + bits<64> Inst; + bits<8> factor; + let Inst{7...0} = factor; + let Inst{3...2} = 0b10; + let Inst{1...0} = 0b00; +} + +def fooTypeEncA : InstructionEncoding { + let Size = 4; + field bits<32> SoftFail = 0; + bits<32> Inst; + bits<8> factor; + let Inst{7...0} = factor; + let Inst{3...2} = 0b11; + let Inst{1...0} = 0b00; +} + + +def foo : Instruction { + bits<32> Inst; + let OutOperandList = (outs); + let InOperandList = (ins i32imm:$factor); + let EncodingInfos = EncodingByHwMode< + [TestMode2, DefaultMode], [fooTypeEncA, fooTypeEncDefault] + >; + let AsmString = "foo $factor"; +} + +// CHECK-SUBTARGET-LABEL: unsigned TestTargetGenSubtargetInfo::getHwModeSet() const { +// CHECK-SUBTARGET: unsigned Modes = 0; +// CHECK-SUBTARGET: if (checkFeatures("+feat")) Modes |= (1 << 0); +// CHECK-SUBTARGET: if (checkFeatures("+feat1")) Modes |= (1 << 1); +// CHECK-SUBTARGET: if (checkFeatures("+feat2")) Modes |= (1 << 2); +// CHECK-SUBTARGET: return Modes; +// CHECK-SUBTARGET: } +// CHECK-SUBTARGET-LABEL: unsigned TestTargetGenSubtargetInfo::getHwMode(enum HwModeType type) const { +// CHECK-SUBTARGET: unsigned Modes = getHwModeSet(); +// CHECK-SUBTARGET: if (!Modes) +// CHECK-SUBTARGET: return Modes; +// CHECK-SUBTARGET: switch (type) { +// CHECK-SUBTARGET: case HwMode_Default: +// CHECK-SUBTARGET: return llvm::countr_zero(Modes) + 1; +// CHECK-SUBTARGET: case HwMode_ValueType: +// CHECK-SUBTARGET: Modes &= 3; +// CHECK-SUBTARGET: if (!Modes) +// CHECK-SUBTARGET: return Modes; +// CHECK-SUBTARGET: if (!llvm::has_single_bit(Modes)) +// CHECK-SUBTARGET: llvm_unreachable("Two or more HwModes for ValueType were found!"); +// CHECK-SUBTARGET: return llvm::countr_zero(Modes) + 1; +// CHECK-SUBTARGET: case HwMode_RegInfo: +// CHECK-SUBTARGET: Modes &= 1; +// CHECK-SUBTARGET: if (!Modes) +// CHECK-SUBTARGET: return Modes; +// CHECK-SUBTARGET: if (!llvm::has_single_bit(Modes)) +// CHECK-SUBTARGET: llvm_unreachable("Two or more HwModes for RegInfo were found!"); +// CHECK-SUBTARGET: return llvm::countr_zero(Modes) + 1; +// CHECK-SUBTARGET: case HwMode_EncodingInfo: +// CHECK-SUBTARGET: Modes &= 4; +// CHECK-SUBTARGET: if (!Modes) +// CHECK-SUBTARGET: return Modes; +// CHECK-SUBTARGET: if (!llvm::has_single_bit(Modes)) +// CHECK-SUBTARGET: llvm_unreachable("Two or more HwModes for EncodingInfo were found!"); +// CHECK-SUBTARGET: return llvm::countr_zero(Modes) + 1; +// CHECK-SUBTARGET: } +// CHECK-SUBTARGET: llvm_unreachable("unexpected HwModeType"); +// CHECK-SUBTARGET: return 0; // should not get here +// CHECK-SUBTARGET: } + diff --git a/llvm/test/TableGen/HwModeEncodeAPInt.td b/llvm/test/TableGen/HwModeEncodeAPInt.td new file mode 100644 index 0000000000000..43ca5edd952a6 --- /dev/null +++ b/llvm/test/TableGen/HwModeEncodeAPInt.td @@ -0,0 +1,241 @@ +// This testcase is to test the correctness of HwMode encoding under the 'APInt' Mode. +// RUN: llvm-tblgen -gen-emitter -I %p/../../include %s | \ +// RUN: FileCheck %s --check-prefix=ENCODER + +include "llvm/Target/Target.td" + +def archInstrInfo : InstrInfo { } + +def arch : Target { + let InstructionSet = archInstrInfo; +} + +def Myi32 : Operand { + let DecoderMethod = "DecodeMyi32"; +} + +def HasA : Predicate<"Subtarget->hasA()">; +def HasB : Predicate<"Subtarget->hasB()">; + +def ModeA : HwMode<"+a", [HasA]>; // Mode 1 +def ModeB : HwMode<"+b", [HasB]>; // Mode 2 +def ModeC : HwMode<"+c", []>; // Mode 3 + + +def fooTypeEncDefault : InstructionEncoding { + let Size = 16; + field bits<128> SoftFail = 0; + bits<128> Inst; + bits<8> factor; + let Inst{127...120} = factor; + let Inst{3...2} = 0b10; + let Inst{1...0} = 0b00; +} + +def fooTypeEncA : InstructionEncoding { + let Size = 16; + field bits<128> SoftFail = 0; + bits<128> Inst; + bits<8> factor; + let Inst{119...112} = factor; + let Inst{3...2} = 0b11; + let Inst{1...0} = 0b00; +} + +def fooTypeEncB : InstructionEncoding { + let Size = 16; + field bits<128> SoftFail = 0; + bits<128> Inst; + bits<8> factor; + let Inst{119...112} = factor; + let Inst{111...110} = 0b11; +} + +def fooTypeEncC : InstructionEncoding { + let Size = 16; + field bits<128> SoftFail = 0; + bits<128> Inst; + bits<8> factor; + let Inst{31...24} = factor; + let Inst{23...21} = 0b110; + let Inst{1...0} = 0b11; +} + +// Test for DefaultMode as a selector. +def foo : Instruction { + bits<128> Inst; + let OutOperandList = (outs); + let InOperandList = (ins i32imm:$factor); + let EncodingInfos = EncodingByHwMode< + [ModeC, ModeA, ModeB, DefaultMode], + [fooTypeEncC, fooTypeEncA, fooTypeEncB, fooTypeEncDefault]>; + let AsmString = "foo $factor"; +} + +def bar: Instruction { + let OutOperandList = (outs); + let InOperandList = (ins i32imm:$factor); + let Size = 4; + bits<32> Inst; + bits<32> SoftFail; + bits<8> factor; + let Inst{31...24} = factor; + let Inst{1...0} = 0b10; + let AsmString = "bar $factor"; +} + +def baz : Instruction { + let OutOperandList = (outs); + let InOperandList = (ins i32imm:$factor); + bits<32> Inst; + let EncodingInfos = EncodingByHwMode< + [ModeB], [fooTypeEncA] + >; + let AsmString = "foo $factor"; +} + +def unrelated: Instruction { + let OutOperandList = (outs); + let DecoderNamespace = "Alt"; + let InOperandList = (ins i32imm:$factor); + let Size = 4; + bits<32> Inst; + bits<32> SoftFail; + bits<8> factor; + let Inst{31...24} = factor; + let Inst{1...0} = 0b10; + let AsmString = "unrelated $factor"; +} + +// For 'bar' and 'unrelated', we didn't assign any HwModes for them, +// they should keep the same in the following four tables. +// For 'foo' we assigned four HwModes( includes 'DefaultMode' ), +// it's encodings should be different in the following four tables. +// For 'baz' we only assigned ModeB for it, so it will be presented +// as '0' in the tables of ModeA, ModeC and Default Mode. +// ENCODER-LABEL: static const uint64_t InstBits[] = { +// ENCODER: UINT64_C(2), UINT64_C(0), // bar +// ENCODER: UINT64_C(0), UINT64_C(0), // baz +// ENCODER: UINT64_C(8), UINT64_C(0), // foo +// ENCODER: UINT64_C(2), UINT64_C(0), // unrelated +// ENCODER-LABEL: static const uint64_t InstBits_ModeA[] = { +// ENCODER: UINT64_C(2), UINT64_C(0), // bar +// ENCODER: UINT64_C(0), UINT64_C(0), // baz +// ENCODER: UINT64_C(12), UINT64_C(0), // foo +// ENCODER: UINT64_C(2), UINT64_C(0), // unrelated +// ENCODER-LABEL: static const uint64_t InstBits_ModeB[] = { +// ENCODER: UINT64_C(2), UINT64_C(0), // bar +// ENCODER: UINT64_C(12), UINT64_C(0), // baz +// ENCODER: UINT64_C(0), UINT64_C(211106232532992), // foo +// ENCODER: UINT64_C(2), UINT64_C(0), // unrelated +// ENCODER-LABEL: static const uint64_t InstBits_ModeC[] = { +// ENCODER: UINT64_C(2), UINT64_C(0), // bar +// ENCODER: UINT64_C(0), UINT64_C(0), // baz +// ENCODER: UINT64_C(12582915), UINT64_C(0), // foo +// ENCODER: UINT64_C(2), UINT64_C(0), // unrelated + + +// ENCODER: const uint64_t *InstBitsByHw; +// ENCODER: const unsigned opcode = MI.getOpcode(); +// ENCODER: if (Scratch.getBitWidth() != 128) +// ENCODER: Scratch = Scratch.zext(128); +// ENCODER: Inst = APInt(128, ArrayRef(InstBits + opcode * 2, 2)); +// ENCODER: APInt &Value = Inst; +// ENCODER: APInt &op = Scratch; +// ENCODER: switch (opcode) { +// ENCODER-LABEL: case ::bar: +// ENCODER-LABEL: case ::unrelated: +// ENCODER-NOT: getHwMode +// ENCODER-LABEL: case ::foo: { +// ENCODER: unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo); +// ENCODER: switch (HwMode) { +// ENCODER: default: llvm_unreachable("Unknown hardware mode!"); break; +// ENCODER: case 0: InstBitsByHw = InstBits; break; +// ENCODER: case 1: InstBitsByHw = InstBits_ModeA; break; +// ENCODER: case 2: InstBitsByHw = InstBits_ModeB; break; +// ENCODER: case 3: InstBitsByHw = InstBits_ModeC; break; +// ENCODER: }; +// ENCODER: Inst = APInt(128, ArrayRef(InstBitsByHw + opcode * 2, 2)); +// ENCODER: Value = Inst; +// ENCODER: switch (HwMode) { +// ENCODER: default: llvm_unreachable("Unhandled HwMode"); +// ENCODER: case 0: { +// ENCODER: op.clearAllBits(); +// ENCODER: getMachineOpValue(MI, MI.getOperand(0), op, Fixups, STI); +// ENCODER: Value.insertBits(op.extractBitsAsZExtValue(8, 0), 120, 8); +// ENCODER: break; +// ENCODER: } +// ENCODER: case 1: { +// ENCODER: op.clearAllBits(); +// ENCODER: getMachineOpValue(MI, MI.getOperand(0), op, Fixups, STI); +// ENCODER: Value.insertBits(op.extractBitsAsZExtValue(8, 0), 112, 8); +// ENCODER: break; +// ENCODER: } +// ENCODER: case 2: { +// ENCODER: op.clearAllBits(); +// ENCODER: getMachineOpValue(MI, MI.getOperand(0), op, Fixups, STI); +// ENCODER: Value.insertBits(op.extractBitsAsZExtValue(8, 0), 112, 8); +// ENCODER: break; +// ENCODER: } +// ENCODER: case 3: { +// ENCODER: op.clearAllBits(); +// ENCODER: getMachineOpValue(MI, MI.getOperand(0), op, Fixups, STI); +// ENCODER: Value.insertBits(op.extractBitsAsZExtValue(8, 0), 24, 8); +// ENCODER: break; +// ENCODER: } +// ENCODER-LABEL: case ::baz: { +// ENCODER: unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo); +// ENCODER: switch (HwMode) { +// ENCODER: default: llvm_unreachable("Unknown hardware mode!"); break; +// ENCODER: case 2: InstBitsByHw = InstBits_ModeB; break; +// ENCODER: }; +// ENCODER: Inst = APInt(128, ArrayRef(InstBitsByHw + opcode * 2, 2)); +// ENCODER: Value = Inst; +// ENCODER: switch (HwMode) { +// ENCODER: default: llvm_unreachable("Unhandled HwMode"); +// ENCODER: case 2: { +// ENCODER: getMachineOpValue(MI, MI.getOperand(0), op, Fixups, STI); +// ENCODER: Value.insertBits(op.extractBitsAsZExtValue(8, 0), 112, 8); +// ENCODER: break; +// ENCODER: } + +// ENCODER-LABEL: uint32_t archMCCodeEmitter::getOperandBitOffset +// ENCODER: switch (MI.getOpcode()) { +// ENCODER-LABEL: case ::bar: +// ENCODER-LABEL: case ::unrelated: { +// ENCODER-NOT: getHwMode +// ENCODER-LABEL: case ::foo: { +// ENCODER: unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo); +// ENCODER: switch (HwMode) { +// ENCODER: default: llvm_unreachable("Unhandled HwMode"); +// ENCODER: case 0: { +// ENCODER: switch (OpNum) { +// ENCODER: case 0: +// ENCODER: return 120; +// ENCODER: } +// ENCODER: break; +// ENCODER: } +// ENCODER: case 1: { +// ENCODER: switch (OpNum) { +// ENCODER: case 0: +// ENCODER: return 112; +// ENCODER: } +// ENCODER: break; +// ENCODER: } +// ENCODER: case 2: { +// ENCODER: switch (OpNum) { +// ENCODER: case 0: +// ENCODER: return 112; +// ENCODER: } +// ENCODER: break; +// ENCODER: } +// ENCODER: case 3: { +// ENCODER: switch (OpNum) { +// ENCODER: case 0: +// ENCODER: return 24; +// ENCODER: } +// ENCODER: break; +// ENCODER: } +// ENCODER: } +// ENCODER: break; +// ENCODER: } diff --git a/llvm/test/TableGen/HwModeEncodeDecode3.td b/llvm/test/TableGen/HwModeEncodeDecode3.td index 8e0266b2c55af..c4d488d9d5f8f 100644 --- a/llvm/test/TableGen/HwModeEncodeDecode3.td +++ b/llvm/test/TableGen/HwModeEncodeDecode3.td @@ -22,8 +22,9 @@ def Myi32 : Operand { def HasA : Predicate<"Subtarget->hasA()">; def HasB : Predicate<"Subtarget->hasB()">; -def ModeA : HwMode<"+a", [HasA]>; -def ModeB : HwMode<"+b", [HasB]>; +def ModeA : HwMode<"+a", [HasA]>; // Mode 1 +def ModeB : HwMode<"+b", [HasB]>; // Mode 2 +def ModeC : HwMode<"+c", []>; // Mode 3 def fooTypeEncDefault : InstructionEncoding { @@ -55,13 +56,23 @@ def fooTypeEncB : InstructionEncoding { let Inst{1...0} = 0b11; } +def fooTypeEncC : InstructionEncoding { + let Size = 4; + field bits<32> SoftFail = 0; + bits<32> Inst; + bits<8> factor; + let Inst{31...24} = factor; + let Inst{23...21} = 0b110; + let Inst{1...0} = 0b11; +} + // Test for DefaultMode as a selector. def foo : Instruction { let OutOperandList = (outs); let InOperandList = (ins i32imm:$factor); let EncodingInfos = EncodingByHwMode< - [ModeA, ModeB, DefaultMode], [fooTypeEncA, fooTypeEncB, fooTypeEncDefault] - >; + [ModeC, ModeA, ModeB, DefaultMode], + [fooTypeEncC, fooTypeEncA, fooTypeEncB, fooTypeEncDefault]>; let AsmString = "foo $factor"; } @@ -102,9 +113,9 @@ def unrelated: Instruction { // Under default settings, using 'HwMode' to dictate instruction encodings results in -// significant duplication of DecoderTables. The three tables ‘DecoderTableAlt32’, -// ‘DecoderTableAlt_ModeA32’, and ‘DecoderTableAlt_ModeB32’ are exact duplicates and -// could effectively be merged into one. +// significant duplication of DecoderTables. The four tables ‘DecoderTableAlt32’, +// ‘DecoderTableAlt_ModeA32’, ‘DecoderTableAlt_ModeB32’ and 'DecoderTable_ModeC32' are +// exact duplicates and could effectively be merged into one. // DECODER-LABEL: DecoderTable32[] = // DECODER-DAG: Opcode: bar // DECODER-LABEL: DecoderTable64[] = @@ -115,6 +126,8 @@ def unrelated: Instruction { // DECODER-DAG: Opcode: unrelated // DECODER-LABEL: DecoderTableAlt_ModeB32[] = // DECODER-DAG: Opcode: unrelated +// DECODER-LABEL: DecoderTableAlt_ModeC32[] = +// DECODER-DAG: Opcode: unrelated // DECODER-LABEL: DecoderTable_ModeA32[] = // DECODER-DAG: Opcode: fooTypeEncA:foo // DECODER-DAG: Opcode: bar @@ -122,9 +135,12 @@ def unrelated: Instruction { // DECODER-DAG: Opcode: fooTypeEncB:foo // DECODER-DAG: Opcode: fooTypeEncA:baz // DECODER-DAG: Opcode: bar +// DECODER-LABEL: DecoderTable_ModeC32[] = +// DECODER-DAG: Opcode: fooTypeEncC:foo +// DECODER-DAG: Opcode: bar // Under the 'O1' optimization level, unnecessary duplicate tables will be eliminated, -// reducing the three ‘Alt’ tables down to just one. +// reducing the four ‘Alt’ tables down to just one. // DECODER-SUPPRESS-O1-LABEL: DecoderTable32[] = // DECODER-SUPPRESS-O1-DAG: Opcode: bar // DECODER-SUPPRESS-O1-LABEL: DecoderTable64[] = @@ -138,6 +154,9 @@ def unrelated: Instruction { // DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncB:foo // DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncA:baz // DECODER-SUPPRESS-O1-DAG: Opcode: bar +// DECODER-SUPPRESS-O1-LABEL: DecoderTable_ModeC32[] = +// DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncC:foo +// DECODER-SUPPRESS-O1-DAG: Opcode: bar // Under the 'O2' optimization condition, instructions possessing the 'EncodingByHwMode' // attribute will be extracted from their original DecoderNamespace and placed into their @@ -159,37 +178,90 @@ def unrelated: Instruction { // DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncB:foo // DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncA:baz // DECODER-SUPPRESS-O2-NOT: Opcode: bar +// DECODER-SUPPRESS-O2-LABEL: DecoderTable_ModeC32[] = +// DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncC:foo +// DECODER-SUPPRESS-O2-NOT: Opcode: bar -// ENCODER-LABEL: static const uint64_t InstBits_DefaultMode[] = { +// For 'bar' and 'unrelated', we didn't assign any HwModes for them, +// they should keep the same in the following four tables. +// For 'foo' we assigned four HwModes( includes 'DefaultMode' ), +// it's encodings should be different in the following four tables. +// For 'baz' we only assigned ModeB for it, so it will be presented +// as '0' in the tables of ModeA, ModeC and Default Mode. +// ENCODER-LABEL: static const uint64_t InstBits[] = { // ENCODER: UINT64_C(2), // bar // ENCODER: UINT64_C(0), // baz // ENCODER: UINT64_C(8), // foo // ENCODER: UINT64_C(2), // unrelated - // ENCODER-LABEL: static const uint64_t InstBits_ModeA[] = { // ENCODER: UINT64_C(2), // bar // ENCODER: UINT64_C(0), // baz // ENCODER: UINT64_C(12), // foo // ENCODER: UINT64_C(2), // unrelated - // ENCODER-LABEL: static const uint64_t InstBits_ModeB[] = { // ENCODER: UINT64_C(2), // bar // ENCODER: UINT64_C(12), // baz // ENCODER: UINT64_C(3), // foo // ENCODER: UINT64_C(2), // unrelated +// ENCODER-LABEL: static const uint64_t InstBits_ModeC[] = { +// ENCODER: UINT64_C(2), // bar +// ENCODER: UINT64_C(0), // baz +// ENCODER: UINT64_C(12582915), // foo +// ENCODER: UINT64_C(2), // unrelated -// ENCODER: unsigned HwMode = STI.getHwMode(); -// ENCODER: switch (HwMode) { -// ENCODER: default: llvm_unreachable("Unknown hardware mode!"); break; -// ENCODER: case 0: InstBits = InstBits_DefaultMode; break; -// ENCODER: case 1: InstBits = InstBits_ModeA; break; -// ENCODER: case 2: InstBits = InstBits_ModeB; break; -// ENCODER: }; - -// ENCODER: case ::foo: { -// ENCODER: switch (HwMode) { -// ENCODER: default: llvm_unreachable("Unhandled HwMode"); -// ENCODER: case 0: { -// ENCODER: case 1: { -// ENCODER: case 2: { - +// ENCODER-LABEL: case ::bar: +// ENCODER-LABEL: case ::unrelated: +// ENCODER-NOT: getHwMode +// ENCODER-LABEL: case ::foo: { +// ENCODER: unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo); +// ENCODER: switch (HwMode) { +// ENCODER: default: llvm_unreachable("Unknown hardware mode!"); break; +// ENCODER: case 0: InstBitsByHw = InstBits; break; +// ENCODER: case 1: InstBitsByHw = InstBits_ModeA; break; +// ENCODER: case 2: InstBitsByHw = InstBits_ModeB; break; +// ENCODER: case 3: InstBitsByHw = InstBits_ModeC; break; +// ENCODER: }; +// ENCODER: Value = InstBitsByHw[opcode]; +// ENCODER: switch (HwMode) { +// ENCODER: default: llvm_unreachable("Unhandled HwMode"); +// ENCODER: case 0: { +// ENCODER: op = getMachineOpValue(MI, MI.getOperand(0), Fixups, STI); +// ENCODER: op &= UINT64_C(240); +// ENCODER: Value |= op; +// ENCODER: break; +// ENCODER: } +// ENCODER: case 1: { +// ENCODER: op = getMachineOpValue(MI, MI.getOperand(0), Fixups, STI); +// ENCODER: op &= UINT64_C(240); +// ENCODER: Value |= op; +// ENCODER: break; +// ENCODER: } +// ENCODER: case 2: { +// ENCODER: op = getMachineOpValue(MI, MI.getOperand(0), Fixups, STI); +// ENCODER: op &= UINT64_C(255); +// ENCODER: op <<= 8; +// ENCODER: Value |= op; +// ENCODER: break; +// ENCODER: } +// ENCODER: case 3: { +// ENCODER: op = getMachineOpValue(MI, MI.getOperand(0), Fixups, STI); +// ENCODER: op &= UINT64_C(255); +// ENCODER: op <<= 24; +// ENCODER: Value |= op; +// ENCODER: break; +// ENCODER: } +// ENCODER-LABEL: case ::baz: { +// ENCODER: unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo); +// ENCODER: switch (HwMode) { +// ENCODER: default: llvm_unreachable("Unknown hardware mode!"); break; +// ENCODER: case 2: InstBitsByHw = InstBits_ModeB; break; +// ENCODER: }; +// ENCODER: Value = InstBitsByHw[opcode]; +// ENCODER: switch (HwMode) { +// ENCODER: default: llvm_unreachable("Unhandled HwMode"); +// ENCODER: case 2: { +// ENCODER: op = getMachineOpValue(MI, MI.getOperand(0), Fixups, STI); +// ENCODER: op &= UINT64_C(240); +// ENCODER: Value |= op; +// ENCODER: break; +// ENCODER: } diff --git a/llvm/utils/TableGen/CodeEmitterGen.cpp b/llvm/utils/TableGen/CodeEmitterGen.cpp index a57885f22d7e3..755b819e748fd 100644 --- a/llvm/utils/TableGen/CodeEmitterGen.cpp +++ b/llvm/utils/TableGen/CodeEmitterGen.cpp @@ -68,7 +68,7 @@ class CodeEmitterGen { void emitInstructionBaseValues( raw_ostream &o, ArrayRef NumberedInstructions, - CodeGenTarget &Target, int HwMode = -1); + CodeGenTarget &Target, unsigned HwMode = DefaultMode); void emitCaseMap(raw_ostream &o, const std::map> &CaseMap); @@ -281,7 +281,7 @@ std::pair CodeEmitterGen::getInstructionCases(Record *R, CodeGenTarget &Target) { std::string Case, BitOffsetCase; - auto append = [&](const char *S) { + auto append = [&](const std::string &S) { Case += S; BitOffsetCase += S; }; @@ -290,11 +290,45 @@ CodeEmitterGen::getInstructionCases(Record *R, CodeGenTarget &Target) { if (auto *DI = dyn_cast_or_null(RV->getValue())) { const CodeGenHwModes &HWM = Target.getHwModes(); EncodingInfoByHwMode EBM(DI->getDef(), HWM); + + // Invoke the interface to obtain the HwMode ID controlling the + // EncodingInfo for the current subtarget. This interface will + // mask off irrelevant HwMode IDs. + append(" unsigned HwMode = " + "STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo);\n"); + Case += " switch (HwMode) {\n"; + Case += " default: llvm_unreachable(\"Unknown hardware mode!\"); " + "break;\n"; + for (auto &[ModeId, Encoding] : EBM) { + if (ModeId == DefaultMode) { + Case += + " case " + itostr(DefaultMode) + ": InstBitsByHw = InstBits"; + } else { + Case += " case " + itostr(ModeId) + + ": InstBitsByHw = InstBits_" + + std::string(HWM.getMode(ModeId).Name); + } + Case += "; break;\n"; + } + Case += " };\n"; + + // We need to remodify the 'Inst' value from the table we found above. + if (UseAPInt) { + int NumWords = APInt::getNumWords(BitWidth); + Case += " Inst = APInt(" + itostr(BitWidth); + Case += ", ArrayRef(InstBitsByHw + opcode * " + itostr(NumWords) + + ", " + itostr(NumWords); + Case += "));\n"; + Case += " Value = Inst;\n"; + } else { + Case += " Value = InstBitsByHw[opcode];\n"; + } + append(" switch (HwMode) {\n"); append(" default: llvm_unreachable(\"Unhandled HwMode\");\n"); - for (auto &KV : EBM) { - append((" case " + itostr(KV.first) + ": {\n").c_str()); - addInstructionCasesForEncoding(R, KV.second, Target, Case, + for (auto &[ModeId, Encoding] : EBM) { + append(" case " + itostr(ModeId) + ": {\n"); + addInstructionCasesForEncoding(R, Encoding, Target, Case, BitOffsetCase); append(" break;\n"); append(" }\n"); @@ -360,9 +394,9 @@ static void emitInstBits(raw_ostream &OS, const APInt &Bits) { void CodeEmitterGen::emitInstructionBaseValues( raw_ostream &o, ArrayRef NumberedInstructions, - CodeGenTarget &Target, int HwMode) { + CodeGenTarget &Target, unsigned HwMode) { const CodeGenHwModes &HWM = Target.getHwModes(); - if (HwMode == -1) + if (HwMode == DefaultMode) o << " static const uint64_t InstBits[] = {\n"; else o << " static const uint64_t InstBits_" @@ -383,8 +417,17 @@ void CodeEmitterGen::emitInstructionBaseValues( if (const RecordVal *RV = R->getValue("EncodingInfos")) { if (auto *DI = dyn_cast_or_null(RV->getValue())) { EncodingInfoByHwMode EBM(DI->getDef(), HWM); - if (EBM.hasMode(HwMode)) + if (EBM.hasMode(HwMode)) { EncodingDef = EBM.get(HwMode); + } else { + // If the HwMode does not match, then Encoding '0' + // should be generated. + APInt Value(BitWidth, 0); + o << " "; + emitInstBits(o, Value); + o << "," << '\t' << "// " << R->getName() << "\n"; + continue; + } } } BitsInit *BI = EncodingDef->getValueAsBitsInit("Inst"); @@ -479,23 +522,17 @@ void CodeEmitterGen::run(raw_ostream &o) { } // Emit instruction base values - if (HwModes.empty()) { - emitInstructionBaseValues(o, NumberedInstructions, Target, -1); - } else { - for (unsigned HwMode : HwModes) - emitInstructionBaseValues(o, NumberedInstructions, Target, (int)HwMode); - } - + emitInstructionBaseValues(o, NumberedInstructions, Target, DefaultMode); if (!HwModes.empty()) { - o << " const uint64_t *InstBits;\n"; - o << " unsigned HwMode = STI.getHwMode();\n"; - o << " switch (HwMode) {\n"; - o << " default: llvm_unreachable(\"Unknown hardware mode!\"); break;\n"; - for (unsigned I : HwModes) { - o << " case " << I << ": InstBits = InstBits_" - << HWM.getModeName(I, /*IncludeDefault=*/true) << "; break;\n"; + // Emit table for instrs whose encodings are controlled by HwModes. + for (unsigned HwMode : HwModes) { + if (HwMode == DefaultMode) + continue; + emitInstructionBaseValues(o, NumberedInstructions, Target, HwMode); } - o << " };\n"; + + // This pointer will be assigned to the HwMode table later. + o << " const uint64_t *InstBitsByHw;\n"; } // Map to accumulate all the cases. diff --git a/llvm/utils/TableGen/Common/CodeGenHwModes.cpp b/llvm/utils/TableGen/Common/CodeGenHwModes.cpp index fec74d29c8bbb..124cfbaf4fb74 100644 --- a/llvm/utils/TableGen/Common/CodeGenHwModes.cpp +++ b/llvm/utils/TableGen/Common/CodeGenHwModes.cpp @@ -74,6 +74,8 @@ CodeGenHwModes::CodeGenHwModes(RecordKeeper &RK) : Records(RK) { ModeIds.insert(std::pair(R, Modes.size())); } + assert(Modes.size() <= 32 && "number of HwModes exceeds maximum of 32"); + for (Record *R : Records.getAllDerivedDefinitions("HwModeSelect")) { auto P = ModeSelects.emplace(std::pair(R, HwModeSelect(R, *this))); assert(P.second); diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp index 2e2c57b802ee5..b6b7641cfb929 100644 --- a/llvm/utils/TableGen/SubtargetEmitter.cpp +++ b/llvm/utils/TableGen/SubtargetEmitter.cpp @@ -1781,13 +1781,62 @@ void SubtargetEmitter::EmitHwModeCheck(const std::string &ClassName, if (CGH.getNumModeIds() == 1) return; - OS << "unsigned " << ClassName << "::getHwMode() const {\n"; + // Collect all HwModes and related features defined in the TD files, + // and store them as a bit set. + unsigned ValueTypeModes = 0; + unsigned RegInfoModes = 0; + unsigned EncodingInfoModes = 0; + for (const auto &MS : CGH.getHwModeSelects()) { + for (const HwModeSelect::PairType &P : MS.second.Items) { + if (P.first == DefaultMode) + continue; + if (P.second->isSubClassOf("ValueType")) { + ValueTypeModes |= (1 << (P.first - 1)); + } else if (P.second->isSubClassOf("RegInfo") || + P.second->isSubClassOf("SubRegRange")) { + RegInfoModes |= (1 << (P.first - 1)); + } else if (P.second->isSubClassOf("InstructionEncoding")) { + EncodingInfoModes |= (1 << (P.first - 1)); + } + } + } + + // Start emitting for getHwModeSet(). + OS << "unsigned " << ClassName << "::getHwModeSet() const {\n"; + OS << " // Collect HwModes and store them as a bit set.\n"; + OS << " unsigned Modes = 0;\n"; for (unsigned M = 1, NumModes = CGH.getNumModeIds(); M != NumModes; ++M) { const HwMode &HM = CGH.getMode(M); - OS << " if (checkFeatures(\"" << HM.Features << "\")) return " << M - << ";\n"; + OS << " if (checkFeatures(\"" << HM.Features << "\")) Modes |= (1 << " + << (M - 1) << ");\n"; } - OS << " return 0;\n}\n"; + OS << " return Modes;\n}\n"; + // End emitting for getHwModeSet(). + + auto handlePerMode = [&](std::string ModeType, unsigned ModeInBitSet) { + OS << " case HwMode_" << ModeType << ":\n" + << " Modes &= " << ModeInBitSet << ";\n" + << " if (!Modes)\n return Modes;\n" + << " if (!llvm::has_single_bit(Modes))\n" + << " llvm_unreachable(\"Two or more HwModes for " << ModeType + << " were found!\");\n" + << " return llvm::countr_zero(Modes) + 1;\n"; + }; + + // Start emitting for getHwMode(). + OS << "unsigned " << ClassName + << "::getHwMode(enum HwModeType type) const {\n"; + OS << " unsigned Modes = getHwModeSet();\n\n"; + OS << " if (!Modes)\n return Modes;\n\n"; + OS << " switch (type) {\n"; + OS << " case HwMode_Default:\n return llvm::countr_zero(Modes) + 1;\n"; + handlePerMode("ValueType", ValueTypeModes); + handlePerMode("RegInfo", RegInfoModes); + handlePerMode("EncodingInfo", EncodingInfoModes); + OS << " }\n"; + OS << " llvm_unreachable(\"unexpected HwModeType\");\n" + << " return 0; // should not get here\n}\n"; + // End emitting for getHwMode(). } void SubtargetEmitter::emitGetMacroFusions(const std::string &ClassName, @@ -1876,8 +1925,11 @@ void SubtargetEmitter::emitGenMCSubtargetInfo(raw_ostream &OS) { << " return " << Target << "_MC" << "::resolveVariantSchedClassImpl(SchedClass, MI, MCII, CPUID);\n"; OS << " }\n"; - if (TGT.getHwModes().getNumModeIds() > 1) - OS << " unsigned getHwMode() const override;\n"; + if (TGT.getHwModes().getNumModeIds() > 1) { + OS << " unsigned getHwModeSet() const override;\n"; + OS << " unsigned getHwMode(enum HwModeType type = HwMode_Default) const " + "override;\n"; + } OS << "};\n"; EmitHwModeCheck(Target + "GenMCSubtargetInfo", OS); } @@ -2004,8 +2056,11 @@ void SubtargetEmitter::run(raw_ostream &OS) { << " unsigned CPUID) const override;\n" << " DFAPacketizer *createDFAPacketizer(const InstrItineraryData *IID)" << " const;\n"; - if (TGT.getHwModes().getNumModeIds() > 1) - OS << " unsigned getHwMode() const override;\n"; + if (TGT.getHwModes().getNumModeIds() > 1) { + OS << " unsigned getHwModeSet() const override;\n"; + OS << " unsigned getHwMode(enum HwModeType type = HwMode_Default) const " + "override;\n"; + } if (TGT.hasMacroFusion()) OS << " std::vector getMacroFusions() const " "override;\n";