diff --git a/llvm/include/llvm/MC/MCDecoderOps.h b/llvm/include/llvm/MC/MCDecoderOps.h index 5afc0387f561f..43013640c9d78 100644 --- a/llvm/include/llvm/MC/MCDecoderOps.h +++ b/llvm/include/llvm/MC/MCDecoderOps.h @@ -13,18 +13,14 @@ namespace llvm::MCD { // Disassembler state machine opcodes. -// nts_t is either uint16_t or uint24_t based on whether large decoder table is -// enabled. enum DecoderOps { - OPC_Scope = 1, // OPC_Scope(nts_t NumToSkip) - OPC_ExtractField, // OPC_ExtractField(uleb128 Start, uint8_t Len) - OPC_FilterValueOrSkip, // OPC_FilterValueOrSkip(uleb128 Val, nts_t NumToSkip) - OPC_FilterValue, // OPC_FilterValue(uleb128 Val) - OPC_CheckField, // OPC_CheckField(uleb128 Start, uint8_t Len, - // uleb128 Val) - OPC_CheckPredicate, // OPC_CheckPredicate(uleb128 PIdx) - OPC_Decode, // OPC_Decode(uleb128 Opcode, uleb128 DIdx) - OPC_SoftFail, // OPC_SoftFail(uleb128 PMask, uleb128 NMask) + OPC_Scope = 1, // OPC_Scope(uleb128 Size) + OPC_SwitchField, // OPC_SwitchField(uleb128 Start, uint8_t Len, + // [uleb128 Val, uleb128 Size]...) + OPC_CheckField, // OPC_CheckField(uleb128 Start, uint8_t Len, uleb128 Val) + OPC_CheckPredicate, // OPC_CheckPredicate(uleb128 PIdx) + OPC_Decode, // OPC_Decode(uleb128 Opcode, uleb128 DIdx) + OPC_SoftFail, // OPC_SoftFail(uleb128 PMask, uleb128 NMask) }; } // namespace llvm::MCD diff --git a/llvm/test/TableGen/DecoderEmitter/DecoderEmitterBitwidthSpecialization.td b/llvm/test/TableGen/DecoderEmitter/DecoderEmitterBitwidthSpecialization.td index 71b0c99675baa..5f3e2a8d2d7df 100644 --- a/llvm/test/TableGen/DecoderEmitter/DecoderEmitterBitwidthSpecialization.td +++ b/llvm/test/TableGen/DecoderEmitter/DecoderEmitterBitwidthSpecialization.td @@ -84,14 +84,14 @@ def Inst3 : Instruction16Bit<3> { // In the default case, we emit a single decodeToMCinst function and DecodeIdx // is shared across all bitwidths. -// CHECK-DEFAULT-LABEL: DecoderTable8[25] -// CHECK-DEFAULT: DecodeIdx: 0 -// CHECK-DEFAULT: DecodeIdx: 1 +// CHECK-DEFAULT-LABEL: DecoderTable8 +// CHECK-DEFAULT: using decoder 0 +// CHECK-DEFAULT: using decoder 1 // CHECK-DEFAULT: }; -// CHECK-DEFAULT-LABEL: DecoderTable16[25] -// CHECK-DEFAULT: DecodeIdx: 2 -// CHECK-DEFAULT: DecodeIdx: 3 +// CHECK-DEFAULT-LABEL: DecoderTable16 +// CHECK-DEFAULT: using decoder 2 +// CHECK-DEFAULT: using decoder 3 // CHECK-DEFAULT: }; // CHECK-DEFAULT-LABEL: template @@ -105,10 +105,10 @@ def Inst3 : Instruction16Bit<3> { // When we specialize per bitwidth, we emit 2 decodeToMCInst functions and // DecodeIdx is assigned per bit width. -// CHECK-SPECIALIZE-NO-TABLE-LABEL: DecoderTable8[26] -// CHECK-SPECIALIZE-NO-TABLE: /* 0 */ 8, // Bitwidth 8 -// CHECK-SPECIALIZE-NO-TABLE: DecodeIdx: 0 -// CHECK-SPECIALIZE-NO-TABLE: DecodeIdx: 1 +// CHECK-SPECIALIZE-NO-TABLE-LABEL: DecoderTable8 +// CHECK-SPECIALIZE-NO-TABLE: 8, // 0: BitWidth 8 +// CHECK-SPECIALIZE-NO-TABLE: using decoder 0 +// CHECK-SPECIALIZE-NO-TABLE: using decoder 1 // CHECK-SPECIALIZE-NO-TABLE: }; // CHECK-SPECIALIZE-NO-TABLE-LABEL: template @@ -117,10 +117,10 @@ def Inst3 : Instruction16Bit<3> { // CHECK-SPECIALIZE-NO-TABLE: case 0 // CHECK-SPECIALIZE-NO-TABLE: case 1 -// CHECK-SPECIALIZE-NO-TABLE-LABEL: DecoderTable16[26] -// CHECK-SPECIALIZE-NO-TABLE: /* 0 */ 16, // Bitwidth 16 -// CHECK-SPECIALIZE-NO-TABLE: DecodeIdx: 0 -// CHECK-SPECIALIZE-NO-TABLE: DecodeIdx: 1 +// CHECK-SPECIALIZE-NO-TABLE-LABEL: DecoderTable16 +// CHECK-SPECIALIZE-NO-TABLE: 16, // 0: BitWidth 16 +// CHECK-SPECIALIZE-NO-TABLE: using decoder 0 +// CHECK-SPECIALIZE-NO-TABLE: using decoder 1 // CHECK-SPECIALIZE-NO-TABLE: }; // CHECK-SPECIALIZE-NO-TABLE-LABEL: template @@ -138,10 +138,10 @@ def Inst3 : Instruction16Bit<3> { // Per bitwidth specialization with function table. // 8 bit deccoder table, functions, and function table. -// CHECK-SPECIALIZE-TABLE-LABEL: DecoderTable8[26] -// CHECK-SPECIALIZE-TABLE: /* 0 */ 8, // Bitwidth 8 -// CHECK-SPECIALIZE-TABLE: DecodeIdx: 0 -// CHECK-SPECIALIZE-TABLE: DecodeIdx: 1 +// CHECK-SPECIALIZE-TABLE-LABEL: DecoderTable8 +// CHECK-SPECIALIZE-TABLE: 8, // 0: BitWidth 8 +// CHECK-SPECIALIZE-TABLE: using decoder 0 +// CHECK-SPECIALIZE-TABLE: using decoder 1 // CHECK-SPECIALIZE-TABLE: }; // CHECK-SPECIALIZE-TABLE-LABEL: template @@ -161,10 +161,10 @@ def Inst3 : Instruction16Bit<3> { // CHECK-SPECIALIZE-TABLE-NEXT: }; // 16 bit deccoder table, functions, and function table. -// CHECK-SPECIALIZE-TABLE-LABEL: DecoderTable16[26] -// CHECK-SPECIALIZE-TABLE: /* 0 */ 16, // Bitwidth 16 -// CHECK-SPECIALIZE-TABLE: DecodeIdx: 0 -// CHECK-SPECIALIZE-TABLE: DecodeIdx: 1 +// CHECK-SPECIALIZE-TABLE-LABEL: DecoderTable16 +// CHECK-SPECIALIZE-TABLE: 16, // 0: BitWidth 16 +// CHECK-SPECIALIZE-TABLE: using decoder 0 +// CHECK-SPECIALIZE-TABLE: using decoder 1 // CHECK-SPECIALIZE-TABLE: }; // CHECK-SPECIALIZE-TABLE-LABEL: template diff --git a/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td b/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td index d046c1a192111..faac5cba4579f 100644 --- a/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td +++ b/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td @@ -1,5 +1,4 @@ -// RUN: llvm-tblgen -gen-disassembler -I %p/../../../include %s | FileCheck %s --check-prefixes=CHECK,CHECK-SMALL -// RUN: llvm-tblgen -gen-disassembler --large-decoder-table -I %p/../../../include %s | FileCheck %s --check-prefixes=CHECK,CHECK-LARGE +// RUN: llvm-tblgen -gen-disassembler -I %p/../../../include %s | FileCheck %s --check-prefixes=CHECK include "llvm/Target/Target.td" @@ -53,19 +52,16 @@ def FOO32 : MyVarInst { // CHECK-NEXT: 43, // CHECK-NEXT: }; -// CHECK-SMALL: /* 0 */ OPC_ExtractField, 3, 5, // Field = Inst{7-3} -// CHECK-SMALL-NEXT: /* 3 */ OPC_FilterValueOrSkip, 8, 4, 0, // if Field != 0x8 skip to 11 -// CHECK-SMALL-NEXT: /* 7 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, // Opcode: FOO16 -// CHECK-SMALL-NEXT: /* 11 */ OPC_FilterValue, 9, // if Field != 0x9 pop scope -// CHECK-SMALL-NEXT: /* 13 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: FOO32 -// CHECK-SMALL-NEXT: }; - -// CHECK-LARGE: /* 0 */ OPC_ExtractField, 3, 5, // Field = Inst{7-3} -// CHECK-LARGE-NEXT: /* 3 */ OPC_FilterValueOrSkip, 8, 4, 0, 0, // if Field != 0x8 skip to 12 -// CHECK-LARGE-NEXT: /* 8 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, // Opcode: FOO16 -// CHECK-LARGE-NEXT: /* 12 */ OPC_FilterValue, 9, // if Field != 0x9 pop scope -// CHECK-LARGE-NEXT: /* 14 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: FOO32 -// CHECK-LARGE-NEXT: }; +// CHECK-LABEL: static const uint8_t DecoderTable43[15] = { +// CHECK-NEXT: OPC_SwitchField, 3, 5, // 0: switch Inst[7:3] { +// CHECK-NEXT: 8, 4, // 3: case 0x8: { +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 0, // 5: decode to FOO16 using decoder 0 +// CHECK-NEXT: // 5: } +// CHECK-NEXT: 9, 0, // 9: case 0x9: { +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 1, // 11: decode to FOO32 using decoder 1 +// CHECK-NEXT: // 11: } +// CHECK-NEXT: // 11: } // switch Inst[7:3] +// CHECK-NEXT: }; // CHECK: case 0: // CHECK-NEXT: tmp = fieldFromInstruction(insn, 8, 3); @@ -86,7 +82,7 @@ def FOO32 : MyVarInst { // CHECK-NEXT: MI.addOperand(MCOperand::createImm(tmp)); // CHECK-NEXT: return S; -// CHECK-LABEL: case OPC_ExtractField: { +// CHECK-LABEL: case OPC_SwitchField: { // CHECK: makeUp(insn, Start + Len); // CHECK-LABEL: case OPC_CheckField: { diff --git a/llvm/test/TableGen/DecoderEmitter/additional-encoding.td b/llvm/test/TableGen/DecoderEmitter/additional-encoding.td index e3ef572bfa7f8..2652407191a57 100644 --- a/llvm/test/TableGen/DecoderEmitter/additional-encoding.td +++ b/llvm/test/TableGen/DecoderEmitter/additional-encoding.td @@ -30,28 +30,42 @@ class I : Instruction { let OutOperandList = out_ops; } -// CHECK: /* 0 */ OPC_ExtractField, 12, 4, // Field = Inst{15-12} -// CHECK-NEXT: /* 3 */ OPC_FilterValueOrSkip, 0, 15, 0, // if Field != 0x0 skip to 22 -// CHECK-NEXT: /* 7 */ OPC_Scope, 8, 0, // end scope at 18 -// CHECK-NEXT: /* 10 */ OPC_CheckField, 6, 6, 0, // if Inst{11-6} != 0x0 -// CHECK-NEXT: /* 14 */ OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: {{.*}}:NOP, DecodeIdx: 0 -// CHECK-NEXT: /* 18 */ OPC_Decode, {{[0-9]+}}, 2, 1, // Opcode: SHIFT0, DecodeIdx: 1 -// CHECK-NEXT: /* 22 */ OPC_FilterValueOrSkip, 1, 15, 0, // if Field != 0x1 skip to 41 -// CHECK-NEXT: /* 26 */ OPC_Scope, 8, 0, // end scope at 37 -// CHECK-NEXT: /* 29 */ OPC_CheckField, 6, 6, 0, // if Inst{11-6} != 0x0 -// CHECK-NEXT: /* 33 */ OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: {{.*}}:NOP, DecodeIdx: 0 -// CHECK-NEXT: /* 37 */ OPC_Decode, {{[0-9]+}}, 2, 1, // Opcode: SHIFT1, DecodeIdx: 1 -// CHECK-NEXT: /* 41 */ OPC_FilterValueOrSkip, 2, 15, 0, // if Field != 0x2 skip to 60 -// CHECK-NEXT: /* 45 */ OPC_Scope, 8, 0, // end scope at 56 -// CHECK-NEXT: /* 48 */ OPC_CheckField, 6, 6, 0, // if Inst{11-6} != 0x0 -// CHECK-NEXT: /* 52 */ OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: {{.*}}:NOP, DecodeIdx: 0 -// CHECK-NEXT: /* 56 */ OPC_Decode, {{[0-9]+}}, 2, 1, // Opcode: SHIFT2, DecodeIdx: 1 -// CHECK-NEXT: /* 60 */ OPC_FilterValue, 3, // if Field != 0x3 -// CHECK-NEXT: /* 62 */ OPC_Scope, 8, 0, // end scope at 73 -// CHECK-NEXT: /* 65 */ OPC_CheckField, 6, 6, 0, // if Inst{11-6} != 0x0 -// CHECK-NEXT: /* 69 */ OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: {{.*}}:NOP, DecodeIdx: 0 -// CHECK-NEXT: /* 73 */ OPC_Decode, {{[0-9]+}}, 2, 1, // Opcode: SHIFT3, DecodeIdx: 1 - +// CHECK-LABEL: static const uint8_t DecoderTable16[67] = { +// CHECK-NEXT: OPC_SwitchField, 12, 4, // 0: switch Inst[15:12] { +// CHECK-NEXT: 0, 14, // 3: case 0x0: { +// CHECK-NEXT: OPC_Scope, 8, // 5: try { +// CHECK-NEXT: OPC_CheckField, 6, 6, 0, // 7: check Inst[11:6] == 0x0 +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 0, // 11: decode to NOP using decoder 0 +// CHECK-NEXT: // 11: } else try { +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 1, // 15: decode to SHIFT0 using decoder 1 +// CHECK-NEXT: // 15: } +// CHECK-NEXT: // 15: } +// CHECK-NEXT: 1, 14, // 19: case 0x1: { +// CHECK-NEXT: OPC_Scope, 8, // 21: try { +// CHECK-NEXT: OPC_CheckField, 6, 6, 0, // 23: check Inst[11:6] == 0x0 +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 0, // 27: decode to {{.*}}:NOP using decoder 0 +// CHECK-NEXT: // 27: } else try { +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 1, // 31: decode to SHIFT1 using decoder 1 +// CHECK-NEXT: // 31: } +// CHECK-NEXT: // 31: } +// CHECK-NEXT: 2, 14, // 35: case 0x2: { +// CHECK-NEXT: OPC_Scope, 8, // 37: try { +// CHECK-NEXT: OPC_CheckField, 6, 6, 0, // 39: check Inst[11:6] == 0x0 +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 0, // 43: decode to {{.*}}:NOP using decoder 0 +// CHECK-NEXT: // 43: } else try { +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 1, // 47: decode to SHIFT2 using decoder 1 +// CHECK-NEXT: // 47: } +// CHECK-NEXT: // 47: } +// CHECK-NEXT: 3, 0, // 51: case 0x3: { +// CHECK-NEXT: OPC_Scope, 8, // 53: try { +// CHECK-NEXT: OPC_CheckField, 6, 6, 0, // 55: check Inst[11:6] == 0x0 +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 0, // 59: decode to {{.*}}:NOP using decoder 0 +// CHECK-NEXT: // 59: } else try { +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 1, // 63: decode to SHIFT3 using decoder 1 +// CHECK-NEXT: // 63: } +// CHECK-NEXT: // 63: } +// CHECK-NEXT: // 63: } // switch Inst[15:12] +// CHECK-NEXT: }; class SHIFT opc> : I<(outs), (ins ShAmtOp:$shamt)>, EncSHIFT; def SHIFT0 : SHIFT<0>; diff --git a/llvm/test/TableGen/DecoderEmitter/big-filter.td b/llvm/test/TableGen/DecoderEmitter/big-filter.td index 87aa7f814c3f3..168692b6dd1ac 100644 --- a/llvm/test/TableGen/DecoderEmitter/big-filter.td +++ b/llvm/test/TableGen/DecoderEmitter/big-filter.td @@ -11,14 +11,18 @@ class I : Instruction { // Check that a 64-bit filter with all bits set does not confuse DecoderEmitter. // -// CHECK-LABEL: static const uint8_t DecoderTable128[34] = { -// CHECK-NEXT: /* 0 */ OPC_ExtractField, 0, 64, // Field = Inst{63-0} -// CHECK-NEXT: /* 3 */ OPC_FilterValueOrSkip, 1, 8, 0, // if Field != 0x1 skip to 15 -// CHECK-NEXT: /* 7 */ OPC_CheckField, 127, 1, 1, // if Inst{127} != 0x1 -// CHECK-NEXT: /* 11 */ OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: I2, DecodeIdx: 0 -// CHECK-NEXT: /* 15 */ OPC_FilterValue, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, // if Field != 0xffffffffffffffff -// CHECK-NEXT: /* 26 */ OPC_CheckField, 127, 1, 0, // if Inst{127} != 0x0 -// CHECK-NEXT: /* 30 */ OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: I1, DecodeIdx: 0 +// CHECK-LABEL: static const uint8_t DecoderTable128[32] = { +// CHECK-NEXT: OPC_SwitchField, 0, 64, // 0: switch Inst[63:0] { +// CHECK-NEXT: 1, 8, // 3: case 0x1: { +// CHECK-NEXT: OPC_CheckField, 127, 1, 1, // 5: check Inst[127] == 0x1 +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 0, // 9: decode to I2 using decoder 0 +// CHECK-NEXT: // 9: } +// CHECK-NEXT: 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 0, +// CHECK-NEXT: // 13: case 0xffffffffffffffff: { +// CHECK-NEXT: OPC_CheckField, 127, 1, 0, // 24: check Inst[127] == 0x0 +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 0, // 28: decode to I1 using decoder 0 +// CHECK-NEXT: // 28: } +// CHECK-NEXT: // 28: } // switch Inst[63:0] // CHECK-NEXT: }; def I1 : I { diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td index b04ba2b4a6f5b..a39f26a6e715a 100644 --- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td +++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td @@ -1,5 +1,4 @@ // RUN: llvm-tblgen -gen-disassembler -I %p/../../../include %s | FileCheck %s -// RUN: llvm-tblgen -gen-disassembler --large-decoder-table -I %p/../../../include %s | FileCheck %s --check-prefix=CHECK-LARGE // Check that if decoding of an instruction fails and the instruction does not // have a complete decoder method that can determine if the bitpattern is valid @@ -34,29 +33,14 @@ def InstB : TestInstruction { let hasCompleteDecoder = 0; } -// CHECK: /* 0 */ OPC_CheckField, 4, 4, 0, // if Inst{7-4} != 0x0 -// CHECK-NEXT: /* 4 */ OPC_Scope, 8, 0, // end scope at 15 -// CHECK-NEXT: /* 7 */ OPC_CheckField, 2, 2, 0, // if Inst{3-2} != 0x0 -// CHECK-NEXT: /* 11 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, // Opcode: InstB, DecodeIdx: 0 -// CHECK-NEXT: /* 15 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA, DecodeIdx: 1 +// CHECK-LABEL: static const uint8_t DecoderTable8[18] = { +// CHECK-NEXT: OPC_CheckField, 4, 4, 0, // 0: check Inst[7:4] == 0x0 +// CHECK-NEXT: OPC_Scope, 8, // 4: try { +// CHECK-NEXT: OPC_CheckField, 2, 2, 0, // 6: check Inst[3:2] == 0x0 +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 0, // 10: decode to InstB using decoder 0 +// CHECK-NEXT: // 10: } else try { +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 1, // 14: decode to InstA using decoder 1 +// CHECK-NEXT: // 14: } // CHECK-NEXT: }; // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } - -// CHECK: unsigned NumToSkip = *Ptr++; -// CHECK-NEXT: NumToSkip |= (*Ptr++) << 8; -// CHECK-NEXT: return NumToSkip; - -// CHECK-LARGE: /* 0 */ OPC_CheckField, 4, 4, 0, // if Inst{7-4} != 0x0 -// CHECK-LARGE-NEXT: /* 4 */ OPC_Scope, 8, 0, 0, // end scope at 16 -// CHECK-LARGE-NEXT: /* 8 */ OPC_CheckField, 2, 2, 0, // if Inst{3-2} != 0x0 -// CHECK-LARGE-NEXT: /* 12 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, // Opcode: InstB, DecodeIdx: 0 -// CHECK-LARGE-NEXT: /* 16 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA, DecodeIdx: 1 -// CHECK-LARGE-NEXT: }; - -// CHECK-LARGE: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } - -// CHECK-LARGE: unsigned NumToSkip = *Ptr++; -// CHECK-LARGE-NEXT: NumToSkip |= (*Ptr++) << 8; -// CHECK-LARGE-NEXT: NumToSkip |= (*Ptr++) << 16; -// CHECK-LARGE-NEXT: return NumToSkip; diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td index 7fd26fffd28b7..40d68061bfc0d 100644 --- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td +++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td @@ -1,5 +1,4 @@ // RUN: llvm-tblgen -gen-disassembler -I %p/../../../include %s | FileCheck %s -// RUN: llvm-tblgen -gen-disassembler --large-decoder-table -I %p/../../../include %s | FileCheck %s --check-prefix=CHECK-LARGE include "llvm/Target/Target.td" @@ -31,25 +30,17 @@ def InstB : TestInstruction { let hasCompleteDecoder = 0; } -// CHECK: /* 0 */ OPC_CheckField, 2, 1, 0, -// CHECK-NEXT: /* 4 */ OPC_CheckField, 5, 3, 0, -// CHECK-NEXT: /* 8 */ OPC_Scope, 8, 0, // end scope at 19 -// CHECK-NEXT: /* 11 */ OPC_CheckField, 0, 2, 3, -// CHECK-NEXT: /* 15 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, -// CHECK-NEXT: /* 19 */ OPC_CheckField, 3, 2, 0, -// CHECK-NEXT: /* 23 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, +// CHECK-LABEL: static const uint8_t DecoderTable8[26] = { +// CHECK-NEXT: OPC_CheckField, 2, 1, 0, // 0: check Inst[2] == 0x0 +// CHECK-NEXT: OPC_CheckField, 5, 3, 0, // 4: check Inst[7:5] == 0x0 +// CHECK-NEXT: OPC_Scope, 8, // 8: try { +// CHECK-NEXT: OPC_CheckField, 0, 2, 3, // 10: check Inst[1:0] == 0x3 +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 0, // 14: decode to InstB using decoder 0 +// CHECK-NEXT: // 14: } else try { +// CHECK-NEXT: OPC_CheckField, 3, 2, 0, // 18: check Inst[4:3] == 0x0 +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 1, // 22: decode to InstA using decoder 1 +// CHECK-NEXT: // 22: } +// CHECK-NEXT: }; // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } // CHECK: if (!Check(S, DecodeInstA(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } - -// CHECK-LARGE: /* 0 */ OPC_CheckField, 2, 1, 0, -// CHECK-LARGE-NEXT: /* 4 */ OPC_CheckField, 5, 3, 0, -// CHECK-LARGE-NEXT: /* 8 */ OPC_Scope, 8, 0, 0, // end scope at 20 -// CHECK-LARGE-NEXT: /* 12 */ OPC_CheckField, 0, 2, 3, -// CHECK-LARGE-NEXT: /* 16 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, -// CHECK-LARGE-NEXT: /* 20 */ OPC_CheckField, 3, 2, 0, -// CHECK-LARGE-NEXT: /* 24 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, -// CHECK-LARGE-NEXT: }; - -// CHECK-LARGE: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } -// CHECK-LARGE: if (!Check(S, DecodeInstA(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td index c884d6b8a93cc..2e2d1ddb8b8f6 100644 --- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td +++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td @@ -1,5 +1,4 @@ // RUN: llvm-tblgen -gen-disassembler -I %p/../../../include %s | FileCheck %s -// RUN: llvm-tblgen -gen-disassembler --large-decoder-table -I %p/../../../include %s | FileCheck %s --check-prefix=CHECK-LARGE include "llvm/Target/Target.td" @@ -35,20 +34,14 @@ def InstB : TestInstruction { let AsmString = "InstB"; } -// CHECK: /* 0 */ OPC_CheckField, 4, 4, 0, -// CHECK-NEXT: /* 4 */ OPC_Scope, 8, 0, // end scope at 15 -// CHECK-NEXT: /* 7 */ OPC_CheckField, 2, 2, 0, -// CHECK-NEXT: /* 11 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, -// CHECK-NEXT: /* 15 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA, DecodeIdx: 1 +// CHECK-LABEL: static const uint8_t DecoderTable8[18] = { +// CHECK-NEXT: OPC_CheckField, 4, 4, 0, // 0: check Inst[7:4] == 0x0 +// CHECK-NEXT: OPC_Scope, 8, // 4: try { +// CHECK-NEXT: OPC_CheckField, 2, 2, 0, // 6: check Inst[3:2] == 0x0 +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 0, // 10: decode to InstB using decoder 0 +// CHECK-NEXT: // 10: } else try { +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 1, // 14: decode to InstA using decoder 1 +// CHECK-NEXT: // 14: } // CHECK-NEXT: }; // CHECK: if (!Check(S, DecodeInstBOp(MI, tmp, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } - -// CHECK-LARGE: /* 0 */ OPC_CheckField, 4, 4, 0, -// CHECK-LARGE-NEXT: /* 4 */ OPC_Scope, 8, 0, 0, // end scope at 16 -// CHECK-LARGE-NEXT: /* 8 */ OPC_CheckField, 2, 2, 0, -// CHECK-LARGE-NEXT: /* 12 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, -// CHECK-LARGE-NEXT: /* 16 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA, DecodeIdx: 1 -// CHECK-LARGE-NEXT: }; - -// CHECK-LARGE: if (!Check(S, DecodeInstBOp(MI, tmp, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td index 2ff160cda2ec5..bb0f658a037e8 100644 --- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td +++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td @@ -1,5 +1,4 @@ // RUN: llvm-tblgen -gen-disassembler -I %p/../../../include %s | FileCheck %s -// RUN: llvm-tblgen -gen-disassembler --large-decoder-table -I %p/../../../include %s | FileCheck %s --check-prefix=CHECK-LARGE // Test for OPC_ExtractField/OPC_CheckField with start bit > 255. // These large start values may arise for architectures with long instruction @@ -33,21 +32,14 @@ def InstB : TestInstruction { let hasCompleteDecoder = 0; } -// CHECK: /* 0 */ OPC_CheckField, 250, 3, 4, 0, -// CHECK-NEXT: /* 5 */ OPC_Scope, 9, 0, // end scope at 17 -// CHECK-NEXT: /* 8 */ OPC_CheckField, 248, 3, 2, 0, -// CHECK-NEXT: /* 13 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, -// CHECK-NEXT: /* 17 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA, DecodeIdx: 1 +// CHECK-LABEL: static const uint8_t DecoderTable512[20] = { +// CHECK-NEXT: OPC_CheckField, 250, 3, 4, 0, // 0: check Inst[509:506] == 0x0 +// CHECK-NEXT: OPC_Scope, 9, // 5: try { +// CHECK-NEXT: OPC_CheckField, 248, 3, 2, 0, // 7: check Inst[505:504] == 0x0 +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 0, // 12: decode to InstB using decoder 0 +// CHECK-NEXT: // 12: } else try { +// CHECK-NEXT: OPC_Decode, {{[0-9, ]+}}, 1, // 16: decode to InstA using decoder 1 +// CHECK-NEXT: // 16: } // CHECK-NEXT: }; // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } - - -// CHECK-LARGE: /* 0 */ OPC_CheckField, 250, 3, 4, 0, -// CHECK-LARGE-NEXT: /* 5 */ OPC_Scope, 9, 0, 0, // end scope at 18 -// CHECK-LARGE-NEXT: /* 9 */ OPC_CheckField, 248, 3, 2, 0, -// CHECK-LARGE-NEXT: /* 14 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, -// CHECK-LARGE-NEXT: /* 18 */ OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA, DecodeIdx: 1 -// CHECK-LARGE-NEXT: }; - -// CHECK-LARGE: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } diff --git a/llvm/test/TableGen/DecoderEmitter/var-len-conflict-1.td b/llvm/test/TableGen/DecoderEmitter/var-len-conflict-1.td index b18d28a9f5136..ae585d7d2c62c 100644 --- a/llvm/test/TableGen/DecoderEmitter/var-len-conflict-1.td +++ b/llvm/test/TableGen/DecoderEmitter/var-len-conflict-1.td @@ -18,19 +18,12 @@ class I : Instruction { // 00000001 ________ I16_1 // 00000010 ________ I16_2 -// CHECK: /* 0 */ OPC_Scope, 17, 0, // end scope at 20 -// CHECK-NEXT: /* 3 */ OPC_ExtractField, 0, 1, // Field = Inst{0} -// CHECK-NEXT: /* 6 */ OPC_FilterValueOrSkip, 0, 4, 0, // if Field != 0x0 skip to 14 -// CHECK-NEXT: /* 10 */ OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: I8_0, DecodeIdx: 0 -// CHECK-NEXT: /* 14 */ OPC_FilterValue, 1, // if Field != 0x1 -// CHECK-NEXT: /* 16 */ OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: I8_1, DecodeIdx: 0 -// CHECK-NEXT: /* 20 */ OPC_ExtractField, 8, 8, // Field = Inst{15-8} -// CHECK-NEXT: /* 23 */ OPC_FilterValueOrSkip, 0, 4, 0, // if Field != 0x0 skip to 31 -// CHECK-NEXT: /* 27 */ OPC_Decode, {{[0-9]+}}, 2, 1, // Opcode: I16_0, DecodeIdx: 1 -// CHECK-NEXT: /* 31 */ OPC_FilterValueOrSkip, 1, 4, 0, // if Field != 0x1 skip to 39 -// CHECK-NEXT: /* 35 */ OPC_Decode, {{[0-9]+}}, 2, 1, // Opcode: I16_1, DecodeIdx: 1 -// CHECK-NEXT: /* 39 */ OPC_FilterValue, 2, // if Field != 0x2 -// CHECK-NEXT: /* 41 */ OPC_Decode, {{[0-9]+}}, 2, 1, // Opcode: I16_2, DecodeIdx: 1 +// CHECK: switch Inst[0] +// CHECK: decode to I8_0 +// CHECK: decode to I8_1 +// CHECK: switch Inst[15:8] +// CHECK: decode to I16_0 +// CHECK: decode to I16_1 def I8_0 : I { dag Inst = (descend (operand "$op", 7), 0b0); } def I8_1 : I { dag Inst = (descend (operand "$op", 7), 0b1); } diff --git a/llvm/test/TableGen/HwModeEncodeDecode.td b/llvm/test/TableGen/HwModeEncodeDecode.td index 6df5fd46297bf..7badc895d5f50 100644 --- a/llvm/test/TableGen/HwModeEncodeDecode.td +++ b/llvm/test/TableGen/HwModeEncodeDecode.td @@ -71,12 +71,12 @@ def baz : Instruction { } // DECODER-LABEL: DecoderTable_ModeA32 -// DECODER-DAG: Opcode: fooTypeEncA:foo -// DECODER-DAG: Opcode: bar +// DECODER-DAG: decode to fooTypeEncA:foo +// DECODER-DAG: decode to bar // DECODER-LABEL: DecoderTable_ModeB32 -// DECODER-DAG: Opcode: fooTypeEncB:foo -// DECODER-DAG: Opcode: fooTypeEncA:baz -// DECODER-DAG: Opcode: bar +// DECODER-DAG: decode to fooTypeEncA:baz +// DECODER-DAG: decode to bar +// DECODER-DAG: decode to fooTypeEncB:foo // ENCODER-LABEL: static const uint64_t InstBits_ModeA[] = { // ENCODER: UINT64_C(2), // bar diff --git a/llvm/test/TableGen/HwModeEncodeDecode2.td b/llvm/test/TableGen/HwModeEncodeDecode2.td index 6431a9bd69a55..e1a5ccffec722 100644 --- a/llvm/test/TableGen/HwModeEncodeDecode2.td +++ b/llvm/test/TableGen/HwModeEncodeDecode2.td @@ -94,25 +94,25 @@ let OutOperandList = (outs) in { } // DECODER-LABEL: DecoderTable_ModeA32 -// DECODER-DAG: Opcode: fooTypeEncA:foo -// DECODER-DAG: Opcode: bar +// DECODER-DAG: decode to fooTypeEncA:foo +// DECODER-DAG: decode to bar // DECODER-LABEL: DecoderTable_ModeB32 -// DECODER-DAG: Opcode: fooTypeEncB:foo -// DECODER-DAG: Opcode: fooTypeEncA:baz -// DECODER-DAG: Opcode: bar +// DECODER-DAG: decode to fooTypeEncB:foo +// DECODER-DAG: decode to fooTypeEncA:baz +// DECODER-DAG: decode to bar // DECODER-LABEL: DecoderTableAlt_ModeA32 -// DECODER-DAG: Opcode: unrelated +// DECODER-DAG: decode to unrelated // DECODER-LABEL: DecoderTableAlt_ModeB32 -// DECODER-DAG: Opcode: unrelated +// DECODER-DAG: decode to unrelated // DECODER-SUPPRESS-LABEL: DecoderTable32 -// DECODER-SUPPRESS-DAG: Opcode: bar +// DECODER-SUPPRESS-DAG: decode to bar // DECODER-SUPPRESS-LABEL: DecoderTable_ModeA32 -// DECODER-SUPPRESS-DAG: Opcode: fooTypeEncA:foo -// DECODER-SUPPRESS-NOT: Opcode: bar +// DECODER-SUPPRESS-DAG: decode to fooTypeEncA:foo +// DECODER-SUPPRESS-NOT: decode to bar // DECODER-SUPPRESS-LABEL: DecoderTable_ModeB32 -// DECODER-SUPPRESS-DAG: Opcode: fooTypeEncB:foo -// DECODER-SUPPRESS-DAG: Opcode: fooTypeEncA:baz -// DECODER-SUPPRESS-NOT: Opcode: bar +// DECODER-SUPPRESS-DAG: decode to fooTypeEncB:foo +// DECODER-SUPPRESS-DAG: decode to fooTypeEncA:baz +// DECODER-SUPPRESS-NOT: decode to bar // DECODER-SUPPRESS-LABEL: DecoderTableAlt32 -// DECODER-SUPPRESS-DAG: Opcode: unrelated +// DECODER-SUPPRESS-DAG: decode to unrelated diff --git a/llvm/test/TableGen/HwModeEncodeDecode3.td b/llvm/test/TableGen/HwModeEncodeDecode3.td index 3893216f70e01..36c65214d2719 100644 --- a/llvm/test/TableGen/HwModeEncodeDecode3.td +++ b/llvm/test/TableGen/HwModeEncodeDecode3.td @@ -117,46 +117,46 @@ def unrelated: Instruction { // ‘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-DAG: decode to bar // DECODER-LABEL: DecoderTable_ModeA32 -// DECODER-DAG: Opcode: fooTypeEncA:foo -// DECODER-DAG: Opcode: bar +// DECODER-DAG: decode to fooTypeEncA:foo +// DECODER-DAG: decode to bar // DECODER-LABEL: DecoderTable_ModeB32 -// DECODER-DAG: Opcode: fooTypeEncB:foo -// DECODER-DAG: Opcode: fooTypeEncA:baz -// DECODER-DAG: Opcode: bar +// DECODER-DAG: decode to fooTypeEncB:foo +// DECODER-DAG: decode to fooTypeEncA:baz +// DECODER-DAG: decode to bar // DECODER-LABEL: DecoderTable_ModeC32 -// DECODER-DAG: Opcode: fooTypeEncC:foo -// DECODER-DAG: Opcode: bar +// DECODER-DAG: decode to fooTypeEncC:foo +// DECODER-DAG: decode to bar // DECODER-LABEL: DecoderTableAlt32 -// DECODER-DAG: Opcode: unrelated +// DECODER-DAG: decode to unrelated // DECODER-LABEL: DecoderTableAlt_ModeA32 -// DECODER-DAG: Opcode: unrelated +// DECODER-DAG: decode to unrelated // DECODER-LABEL: DecoderTableAlt_ModeB32 -// DECODER-DAG: Opcode: unrelated +// DECODER-DAG: decode to unrelated // DECODER-LABEL: DecoderTableAlt_ModeC32 -// DECODER-DAG: Opcode: unrelated +// DECODER-DAG: decode to unrelated // DECODER-LABEL: DecoderTable64 -// DECODER-DAG: Opcode: fooTypeEncDefault:foo +// DECODER-DAG: decode to fooTypeEncDefault:foo // Under the 'O1' optimization level, unnecessary duplicate tables will be eliminated, // reducing the four ‘Alt’ tables down to just one. // DECODER-SUPPRESS-O1-LABEL: DecoderTable32 -// DECODER-SUPPRESS-O1-DAG: Opcode: bar +// DECODER-SUPPRESS-O1-DAG: decode to bar // DECODER-SUPPRESS-O1-LABEL: DecoderTable_ModeA32 -// DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncA:foo -// DECODER-SUPPRESS-O1-DAG: Opcode: bar +// DECODER-SUPPRESS-O1-DAG: decode to fooTypeEncA:foo +// DECODER-SUPPRESS-O1-DAG: decode to bar // DECODER-SUPPRESS-O1-LABEL: DecoderTable_ModeB32 -// DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncB:foo -// DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncA:baz -// DECODER-SUPPRESS-O1-DAG: Opcode: bar +// DECODER-SUPPRESS-O1-DAG: decode to fooTypeEncB:foo +// DECODER-SUPPRESS-O1-DAG: decode to fooTypeEncA:baz +// DECODER-SUPPRESS-O1-DAG: decode to bar // DECODER-SUPPRESS-O1-LABEL: DecoderTable_ModeC32 -// DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncC:foo -// DECODER-SUPPRESS-O1-DAG: Opcode: bar +// DECODER-SUPPRESS-O1-DAG: decode to fooTypeEncC:foo +// DECODER-SUPPRESS-O1-DAG: decode to bar // DECODER-SUPPRESS-O1-LABEL: DecoderTableAlt32 -// DECODER-SUPPRESS-O1-DAG: Opcode: unrelated +// DECODER-SUPPRESS-O1-DAG: decode to unrelated // DECODER-SUPPRESS-O1-LABEL: DecoderTable64 -// DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncDefault:foo +// DECODER-SUPPRESS-O1-DAG: decode to fooTypeEncDefault:foo // Under the 'O2' optimization condition, instructions possessing the 'EncodingByHwMode' // attribute will be extracted from their original DecoderNamespace and placed into their @@ -165,22 +165,22 @@ def unrelated: Instruction { // approach will significantly reduce instruction redundancy, but it necessitates users to thoroughly // consider the interplay between HwMode and DecoderNamespace for their instructions. // DECODER-SUPPRESS-O2-LABEL: DecoderTable32 -// DECODER-SUPPRESS-O2-DAG: Opcode: bar +// DECODER-SUPPRESS-O2-DAG: decode to bar // DECODER-SUPPRESS-O2-LABEL: DecoderTable_ModeA32 -// DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncA:foo -// DECODER-SUPPRESS-O2-NOT: Opcode: bar +// DECODER-SUPPRESS-O2-DAG: decode to fooTypeEncA:foo +// DECODER-SUPPRESS-O2-NOT: decode to bar // DECODER-SUPPRESS-O2-LABEL: DecoderTable_ModeB32 -// DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncB:foo -// DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncA:baz -// DECODER-SUPPRESS-O2-NOT: Opcode: bar +// DECODER-SUPPRESS-O2-DAG: decode to fooTypeEncB:foo +// DECODER-SUPPRESS-O2-DAG: decode to fooTypeEncA:baz +// DECODER-SUPPRESS-O2-NOT: decode to bar // DECODER-SUPPRESS-O2-LABEL: DecoderTable_ModeC32 -// DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncC:foo -// DECODER-SUPPRESS-O2-NOT: Opcode: bar +// DECODER-SUPPRESS-O2-DAG: decode to fooTypeEncC:foo +// DECODER-SUPPRESS-O2-NOT: decode to bar // DECODER-SUPPRESS-O2-LABEL: DecoderTableAlt32 -// DECODER-SUPPRESS-O2-DAG: Opcode: unrelated +// DECODER-SUPPRESS-O2-DAG: decode to unrelated // DECODER-SUPPRESS-O2-LABEL: DecoderTable64 -// DECODER-SUPPRESS-O2-NOT: Opcode: bar -// DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncDefault:foo +// DECODER-SUPPRESS-O2-NOT: decode to bar +// DECODER-SUPPRESS-O2-DAG: decode to fooTypeEncDefault:foo // For 'bar' and 'unrelated', we didn't assign any HwModes for them, // they should keep the same in the following four tables. diff --git a/llvm/utils/TableGen/CMakeLists.txt b/llvm/utils/TableGen/CMakeLists.txt index 67291214c14e6..e4784faaa2671 100644 --- a/llvm/utils/TableGen/CMakeLists.txt +++ b/llvm/utils/TableGen/CMakeLists.txt @@ -43,6 +43,8 @@ add_tablegen(llvm-tblgen LLVM DAGISelMatcherGen.cpp DAGISelMatcherOpt.cpp DecoderEmitter.cpp + DecoderTableEmitter.cpp + DecoderTree.cpp DFAEmitter.cpp DFAPacketizerEmitter.cpp DisassemblerEmitter.cpp diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp index 990bbb973e21d..e83df47d541c6 100644 --- a/llvm/utils/TableGen/DecoderEmitter.cpp +++ b/llvm/utils/TableGen/DecoderEmitter.cpp @@ -19,19 +19,18 @@ #include "Common/InstructionEncoding.h" #include "Common/SubtargetFeatureInfo.h" #include "Common/VarLenCodeEmitterGen.h" +#include "DecoderTableEmitter.h" +#include "DecoderTree.h" #include "TableGenBackends.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallBitVector.h" -#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" -#include "llvm/MC/MCDecoderOps.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -40,7 +39,6 @@ #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/KnownBits.h" -#include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TableGen/Error.h" @@ -57,7 +55,6 @@ #include using namespace llvm; -using namespace llvm::MCD; #define DEBUG_TYPE "decoder-emitter" @@ -85,12 +82,6 @@ static cl::opt DecoderEmitterSuppressDuplicates( "significantly reducing Table Duplications")), cl::init(SUPPRESSION_DISABLE), cl::cat(DisassemblerEmitterCat)); -static cl::opt LargeTable( - "large-decoder-table", - cl::desc("Use large decoder table format. This uses 24 bits for offset\n" - "in the table instead of the default 16 bits."), - cl::init(false), cl::cat(DisassemblerEmitterCat)); - static cl::opt UseFnTableInDecodeToMCInst( "use-fn-table-in-decode-to-mcinst", cl::desc( @@ -128,8 +119,6 @@ STATISTIC(NumInstructions, "Number of instructions considered"); STATISTIC(NumEncodingsSupported, "Number of encodings supported"); STATISTIC(NumEncodingsOmitted, "Number of encodings omitted"); -static unsigned getNumToSkipInBytes() { return LargeTable ? 3 : 2; } - /// Similar to KnownBits::print(), but allows you to specify a character to use /// to print unknown bits. static void printKnownBits(raw_ostream &OS, const KnownBits &Bits, @@ -161,96 +150,6 @@ class LessEncodingIDByWidth { } }; -using PredicateSet = SetVector; -using DecoderSet = SetVector; - -class DecoderTable { -public: - DecoderTable() { Data.reserve(16384); } - - void clear() { Data.clear(); } - size_t size() const { return Data.size(); } - const uint8_t *data() const { return Data.data(); } - - using const_iterator = std::vector::const_iterator; - const_iterator begin() const { return Data.begin(); } - const_iterator end() const { return Data.end(); } - - /// Inserts a state machine opcode into the table. - void insertOpcode(DecoderOps Opcode) { Data.push_back(Opcode); } - - /// Inserts a uint8 encoded value into the table. - void insertUInt8(unsigned Value) { - assert(isUInt<8>(Value)); - Data.push_back(Value); - } - - /// Inserts a ULEB128 encoded value into the table. - void insertULEB128(uint64_t Value) { - // Encode and emit the value to filter against. - uint8_t Buffer[16]; - unsigned Len = encodeULEB128(Value, Buffer); - Data.insert(Data.end(), Buffer, Buffer + Len); - } - - // Insert space for `NumToSkip` and return the position - // in the table for patching. - size_t insertNumToSkip() { - size_t Size = Data.size(); - Data.insert(Data.end(), getNumToSkipInBytes(), 0); - return Size; - } - - void patchNumToSkip(size_t FixupIdx, uint32_t DestIdx) { - // Calculate the distance from the byte following the fixup entry byte - // to the destination. The Target is calculated from after the - // `getNumToSkipInBytes()`-byte NumToSkip entry itself, so subtract - // `getNumToSkipInBytes()` from the displacement here to account for that. - assert(DestIdx >= FixupIdx + getNumToSkipInBytes() && - "Expecting a forward jump in the decoding table"); - uint32_t Delta = DestIdx - FixupIdx - getNumToSkipInBytes(); - if (!isUIntN(8 * getNumToSkipInBytes(), Delta)) - PrintFatalError( - "disassembler decoding table too large, try --large-decoder-table"); - - Data[FixupIdx] = static_cast(Delta); - Data[FixupIdx + 1] = static_cast(Delta >> 8); - if (getNumToSkipInBytes() == 3) - Data[FixupIdx + 2] = static_cast(Delta >> 16); - } - -private: - std::vector Data; -}; - -struct DecoderTableInfo { - DecoderTable Table; - PredicateSet Predicates; - DecoderSet Decoders; - bool HasCheckPredicate; - bool HasSoftFail; - - void insertPredicate(StringRef Predicate) { - Predicates.insert(CachedHashString(Predicate)); - } - - void insertDecoder(StringRef Decoder) { - Decoders.insert(CachedHashString(Decoder)); - } - - unsigned getPredicateIndex(StringRef Predicate) const { - auto I = find(Predicates, Predicate); - assert(I != Predicates.end()); - return std::distance(Predicates.begin(), I); - } - - unsigned getDecoderIndex(StringRef Decoder) const { - auto I = find(Decoders, Decoder); - assert(I != Decoders.end()); - return std::distance(Decoders.begin(), I); - } -}; - using NamespacesHwModesMap = std::map>; class DecoderEmitter { @@ -269,10 +168,6 @@ class DecoderEmitter { const CodeGenTarget &getTarget() const { return Target; } - // Emit the decoder state machine table. - void emitTable(formatted_raw_ostream &OS, DecoderTableInfo &TableInfo, - StringRef Namespace, unsigned HwModeID, unsigned BitWidth, - ArrayRef EncodingIDs) const; void emitInstrLenTable(formatted_raw_ostream &OS, ArrayRef InstrLen) const; void emitPredicateFunction(formatted_raw_ostream &OS, @@ -391,7 +286,7 @@ enum bitAttr_t { class FilterChooser { // TODO: Unfriend by providing the necessary accessors. - friend class DecoderTableBuilder; + friend class DecoderTreeBuilder; // Vector of encodings to choose our filter. ArrayRef Encodings; @@ -503,35 +398,6 @@ class FilterChooser { void dump() const; }; -class DecoderTableBuilder { - const CodeGenTarget &Target; - ArrayRef Encodings; - DecoderTableInfo &TableInfo; - -public: - DecoderTableBuilder(const CodeGenTarget &Target, - ArrayRef Encodings, - DecoderTableInfo &TableInfo) - : Target(Target), Encodings(Encodings), TableInfo(TableInfo) {} - - void buildTable(const FilterChooser &FC, unsigned BitWidth) const { - // When specializing decoders per bit width, each decoder table will begin - // with the bitwidth for that table. - if (SpecializeDecodersPerBitwidth) - TableInfo.Table.insertULEB128(BitWidth); - emitTableEntries(FC); - } - -private: - void emitPredicateTableEntry(unsigned EncodingID) const; - - void emitSoftFailTableEntry(unsigned EncodingID) const; - - void emitSingletonTableEntry(const FilterChooser &FC) const; - - void emitTableEntries(const FilterChooser &FC) const; -}; - } // end anonymous namespace /////////////////////////// @@ -608,219 +474,6 @@ unsigned Filter::usefulness() const { // // ////////////////////////////////// -static StringRef getDecoderOpName(DecoderOps Op) { -#define CASE(OP) \ - case OP: \ - return #OP - switch (Op) { - CASE(OPC_Scope); - CASE(OPC_ExtractField); - CASE(OPC_FilterValueOrSkip); - CASE(OPC_FilterValue); - CASE(OPC_CheckField); - CASE(OPC_CheckPredicate); - CASE(OPC_Decode); - CASE(OPC_SoftFail); - } -#undef CASE - llvm_unreachable("Unknown decoder op"); -} - -// Emit the decoder state machine table. -void DecoderEmitter::emitTable(formatted_raw_ostream &OS, - DecoderTableInfo &TableInfo, StringRef Namespace, - unsigned HwModeID, unsigned BitWidth, - ArrayRef EncodingIDs) const { - // We'll need to be able to map from a decoded opcode into the corresponding - // EncodingID for this specific combination of BitWidth and Namespace. This - // is used below to index into Encodings. - DenseMap OpcodeToEncodingID; - OpcodeToEncodingID.reserve(EncodingIDs.size()); - for (unsigned EncodingID : EncodingIDs) { - const Record *InstDef = Encodings[EncodingID].getInstruction()->TheDef; - OpcodeToEncodingID[Target.getInstrIntValue(InstDef)] = EncodingID; - } - - OS << "static const uint8_t DecoderTable" << Namespace; - if (HwModeID != DefaultMode) - OS << '_' << Target.getHwModes().getModeName(HwModeID); - OS << BitWidth << "[" << TableInfo.Table.size() << "] = {\n"; - - // Emit ULEB128 encoded value to OS, returning the number of bytes emitted. - auto EmitULEB128 = [](DecoderTable::const_iterator &I, - formatted_raw_ostream &OS) { - while (*I >= 128) - OS << (unsigned)*I++ << ", "; - OS << (unsigned)*I++ << ", "; - }; - - // Emit `getNumToSkipInBytes()`-byte numtoskip value to OS, returning the - // NumToSkip value. - auto EmitNumToSkip = [](DecoderTable::const_iterator &I, - formatted_raw_ostream &OS) { - uint8_t Byte = *I++; - uint32_t NumToSkip = Byte; - OS << (unsigned)Byte << ", "; - Byte = *I++; - OS << (unsigned)Byte << ", "; - NumToSkip |= Byte << 8; - if (getNumToSkipInBytes() == 3) { - Byte = *I++; - OS << (unsigned)(Byte) << ", "; - NumToSkip |= Byte << 16; - } - return NumToSkip; - }; - - // FIXME: We may be able to use the NumToSkip values to recover - // appropriate indentation levels. - DecoderTable &Table = TableInfo.Table; - DecoderTable::const_iterator I = Table.begin(); - DecoderTable::const_iterator E = Table.end(); - const uint8_t *const EndPtr = Table.data() + Table.size(); - - auto EmitPos = [&OS](uint32_t Pos) { - constexpr uint32_t StartColumn = 12; - OS << "/* " << Pos << " */"; - OS.PadToColumn(StartColumn); - }; - - auto StartComment = [&OS]() { - constexpr uint32_t CommentColumn = 52; - OS.PadToColumn(CommentColumn); - OS << "// "; - }; - - auto EmitNumToSkipComment = [&](uint32_t NumToSkip) { - uint32_t Index = (I - Table.begin()) + NumToSkip; - OS << "skip to " << Index; - }; - - // The first entry when specializing decoders per bitwidth is the bitwidth. - // This will be used for additional checks in `decodeInstruction`. - if (SpecializeDecodersPerBitwidth) { - EmitPos(0); - EmitULEB128(I, OS); - StartComment(); - OS << "Bitwidth " << BitWidth << '\n'; - } - - auto DecodeAndEmitULEB128 = [EndPtr, - &EmitULEB128](DecoderTable::const_iterator &I, - formatted_raw_ostream &OS) { - const char *ErrMsg = nullptr; - uint64_t Value = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg); - assert(ErrMsg == nullptr && "ULEB128 value too large!"); - - EmitULEB128(I, OS); - return Value; - }; - - while (I != E) { - assert(I < E && "incomplete decode table entry!"); - - uint32_t Pos = I - Table.begin(); - EmitPos(Pos); - const uint8_t DecoderOp = *I++; - OS << getDecoderOpName(static_cast(DecoderOp)) << ", "; - switch (DecoderOp) { - default: - PrintFatalError("Invalid decode table opcode: " + Twine((int)DecoderOp) + - " at index " + Twine(Pos)); - case OPC_Scope: { - uint32_t NumToSkip = EmitNumToSkip(I, OS); - StartComment(); - uint32_t Index = (I - Table.begin()) + NumToSkip; - OS << "end scope at " << Index; - break; - } - case OPC_ExtractField: { - // ULEB128 encoded start value. - unsigned Start = DecodeAndEmitULEB128(I, OS); - unsigned Len = *I++; - OS << Len << ','; - StartComment(); - OS << "Field = Inst{"; - if (Len > 1) - OS << (Start + Len - 1) << '-'; - OS << Start << '}'; - break; - } - case OPC_FilterValueOrSkip: { - // The filter value is ULEB128 encoded. - uint64_t FilterVal = DecodeAndEmitULEB128(I, OS); - uint32_t NumToSkip = EmitNumToSkip(I, OS); - StartComment(); - OS << "if Field != " << format_hex(FilterVal, 0) << ' '; - EmitNumToSkipComment(NumToSkip); - break; - } - case OPC_FilterValue: { - // The filter value is ULEB128 encoded. - uint64_t FilterVal = DecodeAndEmitULEB128(I, OS); - - StartComment(); - OS << "if Field != " << format_hex(FilterVal, 0) << " pop scope"; - break; - } - case OPC_CheckField: { - // ULEB128 encoded start value. - unsigned Start = DecodeAndEmitULEB128(I, OS); - - // 8-bit length. - unsigned Len = *I++; - OS << Len << ", "; - - // ULEB128 encoded field value. - uint64_t FieldVal = DecodeAndEmitULEB128(I, OS); - - StartComment(); - OS << "if Inst{"; - if (Len > 1) - OS << (Start + Len - 1) << '-'; - OS << Start << "} != " << format_hex(FieldVal, 0) << " pop scope"; - break; - } - case OPC_CheckPredicate: { - unsigned PIdx = DecodeAndEmitULEB128(I, OS); - StartComment(); - OS << "if !checkPredicate(" << PIdx << ") pop scope"; - break; - } - case OPC_Decode: { - // Decode the Opcode value. - unsigned Opc = DecodeAndEmitULEB128(I, OS); - - // Decoder index. - unsigned DecodeIdx = DecodeAndEmitULEB128(I, OS); - - auto EncI = OpcodeToEncodingID.find(Opc); - assert(EncI != OpcodeToEncodingID.end() && "no encoding entry"); - auto EncodingID = EncI->second; - - StartComment(); - OS << "Opcode: " << Encodings[EncodingID].getName() - << ", DecodeIdx: " << DecodeIdx; - break; - } - case OPC_SoftFail: { - // Decode the positive mask. - uint64_t PositiveMask = DecodeAndEmitULEB128(I, OS); - - // Decode the negative mask. - uint64_t NegativeMask = DecodeAndEmitULEB128(I, OS); - - StartComment(); - OS << "positive mask: " << format_hex(PositiveMask, 0) - << "negative mask: " << format_hex(NegativeMask, 0); - break; - } - } - OS << '\n'; - } - OS << "};\n\n"; -} - void DecoderEmitter::emitInstrLenTable(formatted_raw_ostream &OS, ArrayRef InstrLen) const { OS << "static const uint8_t InstrLenTable[] = {\n"; @@ -1129,81 +782,6 @@ static std::string getPredicateString(const InstructionEncoding &Encoding, return Predicate; } -void DecoderTableBuilder::emitPredicateTableEntry(unsigned EncodingID) const { - const InstructionEncoding &Encoding = Encodings[EncodingID]; - std::string Predicate = getPredicateString(Encoding, Target.getName()); - if (Predicate.empty()) - return; - - // Using the full predicate string as the key value here is a bit - // heavyweight, but is effective. If the string comparisons become a - // performance concern, we can implement a mangling of the predicate - // data easily enough with a map back to the actual string. That's - // overkill for now, though. - TableInfo.insertPredicate(Predicate); - unsigned PredicateIndex = TableInfo.getPredicateIndex(Predicate); - - TableInfo.Table.insertOpcode(OPC_CheckPredicate); - TableInfo.Table.insertULEB128(PredicateIndex); - TableInfo.HasCheckPredicate = true; -} - -void DecoderTableBuilder::emitSoftFailTableEntry(unsigned EncodingID) const { - const InstructionEncoding &Encoding = Encodings[EncodingID]; - const KnownBits &InstBits = Encoding.getInstBits(); - const APInt &SoftFailMask = Encoding.getSoftFailMask(); - - if (SoftFailMask.isZero()) - return; - - APInt PositiveMask = InstBits.Zero & SoftFailMask; - APInt NegativeMask = InstBits.One & SoftFailMask; - - TableInfo.Table.insertOpcode(OPC_SoftFail); - TableInfo.Table.insertULEB128(PositiveMask.getZExtValue()); - TableInfo.Table.insertULEB128(NegativeMask.getZExtValue()); - TableInfo.HasSoftFail = true; -} - -// Emits table entries to decode the singleton. -void DecoderTableBuilder::emitSingletonTableEntry( - const FilterChooser &FC) const { - unsigned EncodingID = *FC.SingletonEncodingID; - const InstructionEncoding &Encoding = Encodings[EncodingID]; - KnownBits EncodingBits = Encoding.getMandatoryBits(); - - // Look for islands of undecoded bits of the singleton. - std::vector Islands = getIslands(EncodingBits, FC.FilterBits); - - // Emit the predicate table entry if one is needed. - emitPredicateTableEntry(EncodingID); - - // Check any additional encoding fields needed. - for (const EncodingIsland &Island : reverse(Islands)) { - TableInfo.Table.insertOpcode(OPC_CheckField); - TableInfo.Table.insertULEB128(Island.StartBit); - TableInfo.Table.insertUInt8(Island.NumBits); - TableInfo.Table.insertULEB128(Island.FieldVal); - } - - // Check for soft failure of the match. - emitSoftFailTableEntry(EncodingID); - - // Using the full decoder string as the key value here is a bit - // heavyweight, but is effective. If the string comparisons become a - // performance concern, we can implement a mangling of the predicate - // data easily enough with a map back to the actual string. That's - // overkill for now, though. - std::string Decoder = getDecoderString(Encoding); - TableInfo.insertDecoder(Decoder); - unsigned DecoderIndex = TableInfo.getDecoderIndex(Decoder); - - TableInfo.Table.insertOpcode(MCD::OPC_Decode); - const Record *InstDef = Encodings[EncodingID].getInstruction()->TheDef; - TableInfo.Table.insertULEB128(Target.getInstrIntValue(InstDef)); - TableInfo.Table.insertULEB128(DecoderIndex); -} - std::unique_ptr FilterChooser::findBestFilter(ArrayRef BitAttrs, bool AllowMixed, bool Greedy) const { @@ -1480,83 +1058,11 @@ void FilterChooser::dump() const { } } -void DecoderTableBuilder::emitTableEntries(const FilterChooser &FC) const { - DecoderTable &Table = TableInfo.Table; - - // If there are other encodings that could match if those with all bits - // known don't, enter a scope so that they have a chance. - size_t FixupLoc = 0; - if (FC.VariableFC) { - Table.insertOpcode(OPC_Scope); - FixupLoc = Table.insertNumToSkip(); - } - - if (FC.SingletonEncodingID) { - assert(FC.FilterChooserMap.empty()); - // There is only one encoding in which all bits in the filtered range are - // fully defined, but we still need to check if the remaining (unfiltered) - // bits are valid for this encoding. We also need to check predicates etc. - emitSingletonTableEntry(FC); - } else if (FC.FilterChooserMap.size() == 1) { - // If there is only one possible field value, emit a combined OPC_CheckField - // instead of OPC_ExtractField + OPC_FilterValue. - const auto &[FilterVal, Delegate] = *FC.FilterChooserMap.begin(); - Table.insertOpcode(OPC_CheckField); - Table.insertULEB128(FC.StartBit); - Table.insertUInt8(FC.NumBits); - Table.insertULEB128(FilterVal); - - // Emit table entries for the only case. - emitTableEntries(*Delegate); - } else { - // The general case: emit a switch over the field value. - Table.insertOpcode(OPC_ExtractField); - Table.insertULEB128(FC.StartBit); - Table.insertUInt8(FC.NumBits); - - // Emit switch cases for all but the last element. - for (const auto &[FilterVal, Delegate] : drop_end(FC.FilterChooserMap)) { - Table.insertOpcode(OPC_FilterValueOrSkip); - Table.insertULEB128(FilterVal); - size_t FixupPos = Table.insertNumToSkip(); - - // Emit table entries for this case. - emitTableEntries(*Delegate); - - // Patch the previous FilterValueOrSkip to fall through to the next case. - Table.patchNumToSkip(FixupPos, Table.size()); - } - - // Emit a switch case for the last element. It never falls through; - // if it doesn't match, we leave the current scope. - const auto &[FilterVal, Delegate] = *FC.FilterChooserMap.rbegin(); - Table.insertOpcode(OPC_FilterValue); - Table.insertULEB128(FilterVal); - - // Emit table entries for the last case. - emitTableEntries(*Delegate); - } - - if (FC.VariableFC) { - Table.patchNumToSkip(FixupLoc, Table.size()); - emitTableEntries(*FC.VariableFC); - } -} - // emitDecodeInstruction - Emit the templated helper function // decodeInstruction(). static void emitDecodeInstruction(formatted_raw_ostream &OS, bool IsVarLenInst, const DecoderTableInfo &TableInfo) { OS << R"( -static unsigned decodeNumToSkip(const uint8_t *&Ptr) { - unsigned NumToSkip = *Ptr++; - NumToSkip |= (*Ptr++) << 8; -)"; - if (getNumToSkipInBytes() == 3) - OS << " NumToSkip |= (*Ptr++) << 16;\n"; - OS << R"( return NumToSkip; -} - template static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI, InsnType insn, uint64_t Address, @@ -1583,7 +1089,6 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI, OS << R"( SmallVector ScopeStack; - uint64_t CurFieldValue = 0; DecodeStatus S = MCDisassembler::Success; while (true) { ptrdiff_t Loc = Ptr - DecodeTable; @@ -1594,50 +1099,35 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI, << (int)DecoderOp << '\n'; return MCDisassembler::Fail; case OPC_Scope: { - unsigned NumToSkip = decodeNumToSkip(Ptr); + unsigned NumToSkip = decodeULEB128AndIncUnsafe(Ptr); const uint8_t *SkipTo = Ptr + NumToSkip; ScopeStack.push_back(SkipTo); LLVM_DEBUG(dbgs() << Loc << ": OPC_Scope(" << SkipTo - DecodeTable << ")\n"); continue; } - case OPC_ExtractField: { + case OPC_SwitchField: { // Decode the start value. unsigned Start = decodeULEB128AndIncUnsafe(Ptr); unsigned Len = *Ptr++;)"; if (IsVarLenInst) OS << "\n makeUp(insn, Start + Len);"; OS << R"( - CurFieldValue = fieldFromInstruction(insn, Start, Len); - LLVM_DEBUG(dbgs() << Loc << ": OPC_ExtractField(" << Start << ", " - << Len << "): " << CurFieldValue << "\n"); - continue; - } - case OPC_FilterValueOrSkip: { - // Decode the field value. - uint64_t Val = decodeULEB128AndIncUnsafe(Ptr); - bool Failed = Val != CurFieldValue; - unsigned NumToSkip = decodeNumToSkip(Ptr); - const uint8_t *SkipTo = Ptr + NumToSkip; - - LLVM_DEBUG(dbgs() << Loc << ": OPC_FilterValueOrSkip(" << Val << ", " - << SkipTo - DecodeTable << ") " - << (Failed ? "FAIL, " : "PASS\n")); - if (!Failed) - continue; - Ptr = SkipTo; - LLVM_DEBUG(dbgs() << "continuing at " << Ptr - DecodeTable << '\n'); - continue; - } - case OPC_FilterValue: { - // Decode the field value. - uint64_t Val = decodeULEB128AndIncUnsafe(Ptr); - bool Failed = Val != CurFieldValue; - - LLVM_DEBUG(dbgs() << Loc << ": OPC_FilterValue(" << Val << ") " - << (Failed ? "FAIL, " : "PASS\n")); - if (!Failed) + uint64_t FieldValue = fieldFromInstruction(insn, Start, Len); + uint64_t CaseValue; + unsigned CaseSize; + while (true) { + CaseValue = decodeULEB128AndIncUnsafe(Ptr); + CaseSize = decodeULEB128AndIncUnsafe(Ptr); + if (FieldValue == CaseValue || !CaseSize) + break; + Ptr += CaseSize; + } + if (FieldValue == CaseValue) { + LLVM_DEBUG(dbgs() << Loc << ": OPC_SwitchField(" << Start << ", " << Len + << "): " << FieldValue << '\n'); continue; + } break; } case OPC_CheckField: { @@ -1735,6 +1225,109 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI, )"; } +namespace { + +class DecoderTreeBuilder { + DecoderContext &Ctx; + const CodeGenTarget &Target; + ArrayRef Encodings; + +public: + DecoderTreeBuilder(DecoderContext &Ctx, const CodeGenTarget &Target, + ArrayRef Encodings) + : Ctx(Ctx), Target(Target), Encodings(Encodings) {} + + std::unique_ptr buildTree(ArrayRef EncodingIDs); + +private: + std::unique_ptr + convertSingleton(unsigned EncodingID, const KnownBits &FilterBits); + + std::unique_ptr convertFilterChooserMap( + unsigned StartBit, unsigned NumBits, + const std::map> &FCMap); + + std::unique_ptr + convertFilterChooser(const FilterChooser *FC); +}; + +} // namespace + +std::unique_ptr +DecoderTreeBuilder::convertSingleton(unsigned EncodingID, + const KnownBits &FilterBits) { + const InstructionEncoding &Encoding = Encodings[EncodingID]; + auto N = std::make_unique(); + + std::string Predicate = getPredicateString(Encoding, Target.getName()); + if (!Predicate.empty()) { + unsigned PredicateIndex = Ctx.getPredicateIndex(Predicate); + N->addChild(std::make_unique(PredicateIndex)); + } + + std::vector Islands = + getIslands(Encoding.getMandatoryBits(), FilterBits); + for (const EncodingIsland &Island : reverse(Islands)) { + N->addChild(std::make_unique( + Island.StartBit, Island.NumBits, Island.FieldVal)); + } + + const KnownBits &InstBits = Encoding.getInstBits(); + const APInt &SoftFailMask = Encoding.getSoftFailMask(); + if (!SoftFailMask.isZero()) { + APInt PositiveMask = InstBits.Zero & SoftFailMask; + APInt NegativeMask = InstBits.One & SoftFailMask; + N->addChild(std::make_unique(PositiveMask.getZExtValue(), + NegativeMask.getZExtValue())); + } + + unsigned DecoderIndex = Ctx.getDecoderIndex(getDecoderString(Encoding)); + N->addChild(std::make_unique( + Encoding.getName(), Encoding.getInstruction()->EnumVal, DecoderIndex)); + + return N; +} + +std::unique_ptr DecoderTreeBuilder::convertFilterChooserMap( + unsigned StartBit, unsigned NumBits, + const std::map> &FCMap) { + if (FCMap.size() == 1) { + const auto &[FieldVal, ChildFC] = *FCMap.begin(); + auto N = std::make_unique(); + N->addChild(std::make_unique(StartBit, NumBits, FieldVal)); + N->addChild(convertFilterChooser(ChildFC.get())); + return N; + } + auto N = std::make_unique(StartBit, NumBits); + for (const auto &[FieldVal, ChildFC] : FCMap) + N->addCase(FieldVal, convertFilterChooser(ChildFC.get())); + return N; +} + +std::unique_ptr +DecoderTreeBuilder::convertFilterChooser(const FilterChooser *FC) { + auto N = std::make_unique(); + + do { + if (FC->SingletonEncodingID) + N->addChild(convertSingleton(*FC->SingletonEncodingID, FC->FilterBits)); + else + N->addChild(convertFilterChooserMap(FC->StartBit, FC->NumBits, + FC->FilterChooserMap)); + FC = FC->VariableFC.get(); + } while (FC); + + return N; +} + +std::unique_ptr +DecoderTreeBuilder::buildTree(ArrayRef EncodingIDs) { + FilterChooser FC(Encodings, EncodingIDs); + if (FC.hasConflict()) + return nullptr; + return convertFilterChooser(&FC); +} + /// Collects all HwModes referenced by the target for encoding purposes. void DecoderEmitter::collectHwModesReferencedForEncodings( std::vector &HwModeIDs, @@ -1972,43 +1565,44 @@ template constexpr uint32_t InsnBitWidth = 0; PrintFatalError( "Cannot specialize decoders for variable length instuctions"); + DecoderContext Ctx; + DecoderTreeBuilder TreeBuilder(Ctx, Target, Encodings); + + DecoderTableInfo TableInfo; + DecoderTableEmitter TableEmitter(TableInfo, OS); + + // Emit a table for each (namespace, hwmode, bitwidth) combination. // Entries in `EncMap` are already sorted by bitwidth. So bucketing per // bitwidth can be done on-the-fly as we iterate over the map. - DecoderTableInfo TableInfo{}; - DecoderTableBuilder TableBuilder(Target, Encodings, TableInfo); - bool HasConflict = false; for (const auto &[BitWidth, BWMap] : EncMap) { for (const auto &[Key, EncodingIDs] : BWMap) { auto [DecoderNamespace, HwModeID] = Key; - // Emit the decoder for this (namespace, hwmode, width) combination. - FilterChooser FC(Encodings, EncodingIDs); - HasConflict |= FC.hasConflict(); - // Skip emitting table entries if a conflict has been detected. - if (HasConflict) + std::unique_ptr Tree = + TreeBuilder.buildTree(EncodingIDs); + + // Skip emitting the table if a conflict has been detected. + if (!Tree) { + HasConflict = true; continue; + } + + // Form the table name. + SmallString<32> TableName({"DecoderTable", DecoderNamespace}); + if (HwModeID != DefaultMode) + TableName.append({"_", Target.getHwModes().getModeName(HwModeID)}); + TableName.append(utostr(BitWidth)); - // The decode table is cleared for each top level decoder function. The - // predicates and decoders themselves, however, are shared across - // different decoders to give more opportunities for uniqueing. - // - If `SpecializeDecodersPerBitwidth` is enabled, decoders are shared - // across all decoder tables for a given bitwidth, else they are shared - // across all decoder tables. - // - predicates are shared across all decoder tables. - TableInfo.Table.clear(); - TableBuilder.buildTable(FC, BitWidth); - - // Print the table to the output stream. - emitTable(OS, TableInfo, DecoderNamespace, HwModeID, BitWidth, - EncodingIDs); + TableEmitter.emitTable( + TableName, SpecializeDecodersPerBitwidth ? BitWidth : 0, Tree.get()); } // Each BitWidth get's its own decoders and decoder function if // SpecializeDecodersPerBitwidth is enabled. if (SpecializeDecodersPerBitwidth) { - emitDecoderFunction(OS, TableInfo.Decoders, BitWidth); - TableInfo.Decoders.clear(); + emitDecoderFunction(OS, Ctx.Decoders, BitWidth); + Ctx.Decoders.clear(); } } @@ -2018,11 +1612,11 @@ template constexpr uint32_t InsnBitWidth = 0; // Emit the decoder function for the last bucket. This will also emit the // single decoder function if SpecializeDecodersPerBitwidth = false. if (!SpecializeDecodersPerBitwidth) - emitDecoderFunction(OS, TableInfo.Decoders, 0); + emitDecoderFunction(OS, Ctx.Decoders, 0); // Emit the predicate function. if (TableInfo.HasCheckPredicate) - emitPredicateFunction(OS, TableInfo.Predicates); + emitPredicateFunction(OS, Ctx.Predicates); // Emit the main entry point for the decoder, decodeInstruction(). emitDecodeInstruction(OS, IsVarLenInst, TableInfo); diff --git a/llvm/utils/TableGen/DecoderTableEmitter.cpp b/llvm/utils/TableGen/DecoderTableEmitter.cpp new file mode 100644 index 0000000000000..bd587faaa405b --- /dev/null +++ b/llvm/utils/TableGen/DecoderTableEmitter.cpp @@ -0,0 +1,326 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DecoderTableEmitter.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/LEB128.h" + +using namespace llvm; + +unsigned +DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const { + // To make the arithmetic below clearer. + static constexpr unsigned OpcodeSize = 1; + static constexpr unsigned FieldWidthSize = 1; + + switch (Node->getKind()) { + case DecoderTreeNode::CheckAny: { + const auto *N = static_cast(Node); + // Pretend the node was optimized. See the comment in emitCheckAnyNode. + if (range_size(N->children()) == 1) + return computeNodeSize(*N->child_begin()); + unsigned Size = 0; + // All children except the last one are preceded by OPC_Scope opcode and + // the size of the child. + for (const DecoderTreeNode *Child : drop_end(N->children())) { + unsigned ChildSize = computeNodeSize(Child); + Size += OpcodeSize + getULEB128Size(ChildSize) + ChildSize; + } + const DecoderTreeNode *Child = *std::prev(N->child_end()); + return Size + computeNodeSize(Child); + } + case DecoderTreeNode::CheckAll: { + const auto *N = static_cast(Node); + unsigned Size = 0; + for (const DecoderTreeNode *Child : N->children()) + Size += computeNodeSize(Child); + return Size; + } + case DecoderTreeNode::CheckField: { + const auto *N = static_cast(Node); + return OpcodeSize + getULEB128Size(N->getStartBit()) + FieldWidthSize + + getULEB128Size(N->getValue()); + } + case DecoderTreeNode::SwitchField: { + const auto *N = static_cast(Node); + unsigned Size = + OpcodeSize + getULEB128Size(N->getStartBit()) + FieldWidthSize; + + for (auto [Val, Child] : drop_end(N->cases())) { + unsigned ChildSize = computeNodeSize(Child); + Size += getULEB128Size(Val) + getULEB128Size(ChildSize) + ChildSize; + } + + // The last child is emitted with sentinel value 0 instead of the size. + // See the comment in emitSwitchFieldNode. + auto [Val, Child] = *std::prev(N->case_end()); + unsigned ChildSize = computeNodeSize(Child); + Size += getULEB128Size(Val) + getULEB128Size(0) + ChildSize; + return Size; + } + case DecoderTreeNode::CheckPredicate: { + const auto *N = static_cast(Node); + unsigned PredicateIndex = N->getPredicateIndex(); + return OpcodeSize + getULEB128Size(PredicateIndex); + } + case DecoderTreeNode::SoftFail: { + const auto *N = static_cast(Node); + return OpcodeSize + getULEB128Size(N->getPositiveMask()) + + getULEB128Size(N->getNegativeMask()); + } + case DecoderTreeNode::Decode: { + const auto *N = static_cast(Node); + return OpcodeSize + getULEB128Size(N->getInstOpcode()) + + getULEB128Size(N->getDecoderIndex()); + } + } + llvm_unreachable("Unknown node kind"); +} + +unsigned DecoderTableEmitter::computeTableSize(const DecoderTreeNode *Root, + unsigned BitWidth) const { + unsigned Size = 0; + if (BitWidth) + Size += getULEB128Size(BitWidth); + Size += computeNodeSize(Root); + return Size; +} + +void DecoderTableEmitter::emitStartLine() { + LineStartIndex = CurrentIndex; + OS.indent(2); +} + +void DecoderTableEmitter::emitOpcode(StringRef Name) { + emitStartLine(); + OS << Name << ", "; + ++CurrentIndex; +} + +void DecoderTableEmitter::emitByte(uint8_t Val) { + OS << static_cast(Val) << ", "; + ++CurrentIndex; +} + +void DecoderTableEmitter::emitUInt8(unsigned Val) { + assert(isUInt<8>(Val)); + emitByte(Val); +} + +void DecoderTableEmitter::emitULEB128(uint64_t Val) { + while (Val >= 0x80) { + emitByte((Val & 0x7F) | 0x80); + Val >>= 7; + } + emitByte(Val); +} + +raw_ostream &DecoderTableEmitter::emitComment(indent Indent) { + constexpr unsigned CommentColumn = 45; + if (OS.getColumn() > CommentColumn) + OS << '\n'; + OS.PadToColumn(CommentColumn); + OS << "// " << format_decimal(LineStartIndex, IndexWidth) << ": " << Indent; + return OS; +} + +namespace { + +/// Helper class for printing bit ranges. +struct BitRange { + unsigned MSB, LSB; + + friend raw_ostream &operator<<(raw_ostream &OS, BitRange R) { + if (R.MSB == R.LSB) + OS << '[' << R.LSB << ']'; + else + OS << '[' << R.MSB << ':' << R.LSB << ']'; + return OS; + } +}; + +} // namespace + +void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N, + indent Indent) { + // TODO: Single-child CheckAny node should be optimized out. For now, + // pretend this is the case and print the single child unindented. + if (range_size(N->children()) == 1) { + emitNode(*N->child_begin(), Indent); + return; + } + + ListSeparator LS("} else "); + for (const DecoderTreeNode *Child : drop_end(N->children())) { + emitOpcode("OPC_Scope"); + emitULEB128(computeNodeSize(Child)); + + emitComment(Indent) << LS << "try {\n"; + emitNode(Child, Indent + 1); + } + + // Don't emit OPC_Scope for the last child so that we leave the current scope + // if it fails. Otherwise, we would need some kind of OPC_LeaveScope opcode. + const DecoderTreeNode *Child = *std::prev(N->child_end()); + + emitComment(Indent) << LS << "try {\n"; + emitNode(Child, Indent + 1); + emitComment(Indent) << "}\n"; +} + +void DecoderTableEmitter::emitCheckAllNode(const CheckAllNode *N, + indent Indent) { + // TODO: Single-child CheckAll should be optimized out. + // TODO: Nested CheckAll nodes should be flattened. + // TODO: Sibling CheckAll and other Check* nodes should be merged together. + for (const DecoderTreeNode *Child : N->children()) + emitNode(Child, Indent); +} + +void DecoderTableEmitter::emitSwitchFieldNode(const SwitchFieldNode *N, + indent Indent) { + unsigned LSB = N->getStartBit(); + unsigned Width = N->getNumBits(); + unsigned MSB = LSB + Width - 1; + + emitOpcode("OPC_SwitchField"); + emitULEB128(LSB); + emitUInt8(Width); + + emitComment(Indent) << "switch Inst" << BitRange{MSB, LSB} << " {\n"; + + for (auto [Val, Child] : drop_end(N->cases())) { + emitStartLine(); + emitULEB128(Val); + emitULEB128(computeNodeSize(Child)); + + emitComment(Indent) << "case " << format_hex(Val, 0) << ": {\n"; + emitNode(Child, Indent + 1); + emitComment(Indent) << "}\n"; + } + + // Don't emit the size of the last child and instead emit a sentinel value, + // which tells the interpreter that this is the last case. The interpreter + // doesn't need to know its size because SwitchField node never falls through + // (we either successfully decode an instruction, or leave the current scope). + auto [Val, Child] = *std::prev(N->case_end()); + emitStartLine(); + emitULEB128(Val); + emitULEB128(0); + + emitComment(Indent) << "case " << format_hex(Val, 0) << ": {\n"; + emitNode(Child, Indent + 1); + emitComment(Indent) << "}\n"; + + emitComment(Indent) << "} // switch Inst" << BitRange{MSB, LSB} << "\n"; +} + +void DecoderTableEmitter::emitCheckFieldNode(const CheckFieldNode *N, + indent Indent) { + unsigned LSB = N->getStartBit(); + unsigned Width = N->getNumBits(); + unsigned MSB = LSB + Width - 1; + uint64_t Val = N->getValue(); + + emitOpcode("OPC_CheckField"); + emitULEB128(LSB); + emitUInt8(Width); + emitULEB128(Val); + + emitComment(Indent) << "check Inst" << BitRange{MSB, LSB} + << " == " << format_hex(Val, 0) << '\n'; +} + +void DecoderTableEmitter::emitCheckPredicateNode(const CheckPredicateNode *N, + indent Indent) { + unsigned PredicateIndex = N->getPredicateIndex(); + + emitOpcode("OPC_CheckPredicate"); + emitULEB128(PredicateIndex); + TableInfo.HasCheckPredicate = true; + + emitComment(Indent) << "check predicate " << PredicateIndex << "\n"; +} + +void DecoderTableEmitter::emitSoftFailNode(const SoftFailNode *N, + indent Indent) { + uint64_t PositiveMask = N->getPositiveMask(); + uint64_t NegativeMask = N->getNegativeMask(); + + emitOpcode("OPC_SoftFail"); + emitULEB128(PositiveMask); + emitULEB128(NegativeMask); + TableInfo.HasSoftFail = true; + + emitComment(Indent) << "softfail"; + OS << " pos=" << format_hex(PositiveMask, 10); + OS << " neg=" << format_hex(NegativeMask, 10) << '\n'; +} + +void DecoderTableEmitter::emitDecodeNode(const DecodeNode *N, indent Indent) { + StringRef EncodingName = N->getEncodingName(); + unsigned InstOpcode = N->getInstOpcode(); + unsigned DecoderIndex = N->getDecoderIndex(); + + emitOpcode("OPC_Decode"); + emitULEB128(InstOpcode); + emitULEB128(DecoderIndex); + + emitComment(Indent) << "decode to " << EncodingName << " using decoder " + << DecoderIndex << '\n'; +} + +void DecoderTableEmitter::emitNode(const DecoderTreeNode *N, indent Indent) { + switch (N->getKind()) { + case DecoderTreeNode::CheckAny: + return emitCheckAnyNode(static_cast(N), Indent); + case DecoderTreeNode::CheckAll: + return emitCheckAllNode(static_cast(N), Indent); + case DecoderTreeNode::SwitchField: + return emitSwitchFieldNode(static_cast(N), Indent); + case DecoderTreeNode::CheckField: + return emitCheckFieldNode(static_cast(N), Indent); + case DecoderTreeNode::CheckPredicate: + return emitCheckPredicateNode(static_cast(N), + Indent); + case DecoderTreeNode::SoftFail: + return emitSoftFailNode(static_cast(N), Indent); + case DecoderTreeNode::Decode: + return emitDecodeNode(static_cast(N), Indent); + } + llvm_unreachable("Unknown node kind"); +} + +void DecoderTableEmitter::emitTable(StringRef TableName, unsigned BitWidth, + const DecoderTreeNode *Root) { + unsigned TableSize = computeTableSize(Root, BitWidth); + OS << "static const uint8_t " << TableName << "[" << TableSize << "] = {\n"; + + // Calculate the number of decimal places for table indices. + // This is simply log10 of the table size. + IndexWidth = 1; + for (unsigned S = TableSize; S /= 10;) + ++IndexWidth; + + CurrentIndex = 0; + + // When specializing decoders per bit width, each decoder table will begin + // with the bitwidth for that table. + if (BitWidth) { + emitStartLine(); + emitULEB128(BitWidth); + emitComment(indent(0)) << "BitWidth " << BitWidth << '\n'; + } + + emitNode(Root, indent(0)); + assert(CurrentIndex == TableSize && + "The size of the emitted table differs from the calculated one"); + + OS << "};\n"; +} diff --git a/llvm/utils/TableGen/DecoderTableEmitter.h b/llvm/utils/TableGen/DecoderTableEmitter.h new file mode 100644 index 0000000000000..22e7f9042a6d9 --- /dev/null +++ b/llvm/utils/TableGen/DecoderTableEmitter.h @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_UTILS_TABLEGEN_DECODERTABLEEMITTER_H +#define LLVM_UTILS_TABLEGEN_DECODERTABLEEMITTER_H + +#include "DecoderTree.h" +#include "llvm/Support/FormattedStream.h" + +namespace llvm { + +struct DecoderTableInfo { + bool HasCheckPredicate = false; + bool HasSoftFail = false; +}; + +class DecoderTableEmitter { + DecoderTableInfo &TableInfo; + formatted_raw_ostream OS; + + /// The number of positions occupied by the index in the output. Used to + /// right-align indices and left-align the text that follows them. + unsigned IndexWidth; + + /// The current position in the output stream. After the table is emitted, + /// this is its size. + unsigned CurrentIndex; + + /// The index of the first byte of the table row. Used as a label in the + /// comment following the row. + unsigned LineStartIndex; + +public: + DecoderTableEmitter(DecoderTableInfo &TableInfo, raw_ostream &OS) + : TableInfo(TableInfo), OS(OS) {} + + void emitTable(StringRef TableName, unsigned BitWidth, + const DecoderTreeNode *Root); + +private: + unsigned computeNodeSize(const DecoderTreeNode *Node) const; + unsigned computeTableSize(const DecoderTreeNode *Root, + unsigned BitWidth) const; + + void emitStartLine(); + void emitOpcode(StringRef Name); + void emitByte(uint8_t Val); + void emitUInt8(unsigned Val); + void emitULEB128(uint64_t Val); + raw_ostream &emitComment(indent Indent); + + void emitCheckAnyNode(const CheckAnyNode *N, indent Indent); + void emitCheckAllNode(const CheckAllNode *N, indent Indent); + void emitSwitchFieldNode(const SwitchFieldNode *N, indent Indent); + void emitCheckFieldNode(const CheckFieldNode *N, indent Indent); + void emitCheckPredicateNode(const CheckPredicateNode *N, indent Indent); + void emitSoftFailNode(const SoftFailNode *N, indent Indent); + void emitDecodeNode(const DecodeNode *N, indent Indent); + void emitNode(const DecoderTreeNode *N, indent Indent); +}; + +} // namespace llvm + +#endif // LLVM_UTILS_TABLEGEN_DECODERTABLEEMITTER_H diff --git a/llvm/utils/TableGen/DecoderTree.cpp b/llvm/utils/TableGen/DecoderTree.cpp new file mode 100644 index 0000000000000..aa7389e6dd9f3 --- /dev/null +++ b/llvm/utils/TableGen/DecoderTree.cpp @@ -0,0 +1,14 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DecoderTree.h" + +using namespace llvm; + +// Pin the vtable to this file. +DecoderTreeNode::~DecoderTreeNode() = default; diff --git a/llvm/utils/TableGen/DecoderTree.h b/llvm/utils/TableGen/DecoderTree.h new file mode 100644 index 0000000000000..91d70a8393d86 --- /dev/null +++ b/llvm/utils/TableGen/DecoderTree.h @@ -0,0 +1,225 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_UTILS_TABLEGEN_DECODERTREE_H +#define LLVM_UTILS_TABLEGEN_DECODERTREE_H + +#include "llvm/ADT/CachedHashString.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include +#include + +namespace llvm { + +class InstructionEncoding; + +using PredicateSet = SetVector; +using DecoderSet = SetVector; + +/// Context shared across decoder trees. +/// Predicates and decoders are shared across decoder trees to provide more +/// opportunities for uniqueness. If SpecializeDecodersPerBitwidth is enabled, +/// decoders are shared across all trees for a given bitwidth, else they are +/// shared across all trees. Predicates are always shared across all trees. +struct DecoderContext { + PredicateSet Predicates; + DecoderSet Decoders; + + unsigned getPredicateIndex(StringRef Predicate) { + Predicates.insert(CachedHashString(Predicate)); + PredicateSet::const_iterator I = find(Predicates, Predicate); + return std::distance(Predicates.begin(), I); + } + + unsigned getDecoderIndex(StringRef Decoder) { + Decoders.insert(CachedHashString(Decoder)); + DecoderSet::const_iterator I = find(Decoders, Decoder); + return std::distance(Decoders.begin(), I); + } +}; + +class DecoderTreeNode { +public: + virtual ~DecoderTreeNode(); + + enum KindTy { + CheckAny, + CheckAll, + CheckField, + SwitchField, + CheckPredicate, + SoftFail, + Decode, + }; + + KindTy getKind() const { return Kind; } + +protected: + explicit DecoderTreeNode(KindTy Kind) : Kind(Kind) {} + +private: + KindTy Kind; +}; + +/// Common base class for nodes with multiple children. +class CheckManyNode : public DecoderTreeNode { + SmallVector, 0> Children; + + static const DecoderTreeNode * + mapElement(decltype(Children)::const_reference Element) { + return Element.get(); + } + +protected: + explicit CheckManyNode(KindTy Kind) : DecoderTreeNode(Kind) {} + +public: + void addChild(std::unique_ptr Child) { + Children.push_back(std::move(Child)); + } + + using child_iterator = mapped_iterator; + + child_iterator child_begin() const { + return child_iterator(Children.begin(), mapElement); + } + + child_iterator child_end() const { + return child_iterator(Children.end(), mapElement); + } + + iterator_range children() const { + return make_range(child_begin(), child_end()); + } +}; + +/// Executes child nodes one by one until one of them succeeds or all fail. +/// The node fails if all child nodes fail. It never succeeds, because if a +/// child node succeeds, it does not return. +class CheckAnyNode : public CheckManyNode { +public: + CheckAnyNode() : CheckManyNode(CheckAny) {} +}; + +/// Executes child nodes one by one until one of them fails all all succeed. +/// The node fails if any of the child nodes fails. +class CheckAllNode : public CheckManyNode { +public: + CheckAllNode() : CheckManyNode(CheckAll) {} +}; + +/// Checks the value of encoding bits in the specified range. +class CheckFieldNode : public DecoderTreeNode { + unsigned StartBit; + unsigned NumBits; + uint64_t Value; + +public: + CheckFieldNode(unsigned StartBit, unsigned NumBits, uint64_t Value) + : DecoderTreeNode(CheckField), StartBit(StartBit), NumBits(NumBits), + Value(Value) {} + + unsigned getStartBit() const { return StartBit; } + + unsigned getNumBits() const { return NumBits; } + + uint64_t getValue() const { return Value; } +}; + +/// Switch based on the value of encoding bits in the specified range. +/// If the value of the bits in the range doesn't match any of the cases, +/// the node fails. This is semantically equivalent to CheckAny node where +/// every child is a CheckField node, but is faster. +class SwitchFieldNode : public DecoderTreeNode { + unsigned StartBit; + unsigned NumBits; + std::map> Cases; + + static std::pair + mapElement(decltype(Cases)::const_reference Element) { + return std::pair(Element.first, Element.second.get()); + } + +public: + SwitchFieldNode(unsigned StartBit, unsigned NumBits) + : DecoderTreeNode(SwitchField), StartBit(StartBit), NumBits(NumBits) {} + + void addCase(uint64_t Value, std::unique_ptr N) { + Cases.try_emplace(Value, std::move(N)); + } + + unsigned getStartBit() const { return StartBit; } + + unsigned getNumBits() const { return NumBits; } + + using case_iterator = + mapped_iterator; + + case_iterator case_begin() const { + return case_iterator(Cases.begin(), mapElement); + } + + case_iterator case_end() const { + return case_iterator(Cases.end(), mapElement); + } + + iterator_range cases() const { + return make_range(case_begin(), case_end()); + } +}; + +/// Checks that the instruction to be decoded has its predicates satisfied. +class CheckPredicateNode : public DecoderTreeNode { + unsigned PredicateIndex; + +public: + explicit CheckPredicateNode(unsigned PredicateIndex) + : DecoderTreeNode(CheckPredicate), PredicateIndex(PredicateIndex) {} + + unsigned getPredicateIndex() const { return PredicateIndex; } +}; + +/// Checks if the encoding bits are correct w.r.t. SoftFail semantics. +/// This is the only node that can never fail. +class SoftFailNode : public DecoderTreeNode { + uint64_t PositiveMask, NegativeMask; + +public: + SoftFailNode(uint64_t PositiveMask, uint64_t NegativeMask) + : DecoderTreeNode(SoftFail), PositiveMask(PositiveMask), + NegativeMask(NegativeMask) {} + + uint64_t getPositiveMask() const { return PositiveMask; } + uint64_t getNegativeMask() const { return NegativeMask; } +}; + +/// Attempts to decode the specified encoding as the specified instruction. +class DecodeNode : public DecoderTreeNode { + StringRef EncodingName; + unsigned InstOpcode; + unsigned DecoderIndex; + +public: + DecodeNode(StringRef EncodingName, unsigned InstOpcode, unsigned DecoderIndex) + : DecoderTreeNode(Decode), EncodingName(EncodingName), + InstOpcode(InstOpcode), DecoderIndex(DecoderIndex) {} + + StringRef getEncodingName() const { return EncodingName; } + + unsigned getInstOpcode() const { return InstOpcode; } + + unsigned getDecoderIndex() const { return DecoderIndex; } +}; + +} // namespace llvm + +#endif // LLVM_UTILS_TABLEGEN_DECODERTREE_H