diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td index 38c3b6064d267..4551321e2bbbd 100644 --- a/llvm/include/llvm/Target/Target.td +++ b/llvm/include/llvm/Target/Target.td @@ -1154,6 +1154,17 @@ class InstrInfo { // // This option is a temporary migration help. It will go away. bit guessInstructionProperties = true; + + // Lists of decoder namespaces to coalesce. Each list should consist of one + // or more names, and the meaning is that each namespace is coalesced with + // the first namespace in the list. And the order of the namespaces in the + // list is the attempt order. As an example, if we have + // [ "", "X", "Y" ] + // Then namespaces X and Y are coalesced with "", and the decoding is + // attempted in the order "", then X and then Y. + // FIXME: Does this need to be done per HWmode, since the decoder tables + // are generated per HW mode. + list> CoalesceDecoderNamespaces = [[]]; } // Standard Pseudo Instructions. diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp index a5cedadd30981..532f08926a9e4 100644 --- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp +++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp @@ -58,9 +58,6 @@ #include #include #include -namespace llvm { -class Argument; -} using namespace llvm; diff --git a/llvm/lib/Target/AArch64/AArch64.td b/llvm/lib/Target/AArch64/AArch64.td index 86f95488e6bb7..fe63757dc0098 100644 --- a/llvm/lib/Target/AArch64/AArch64.td +++ b/llvm/lib/Target/AArch64/AArch64.td @@ -40,7 +40,10 @@ include "AArch64SchedPredExynos.td" include "AArch64SchedPredNeoverse.td" include "AArch64Combine.td" -def AArch64InstrInfo : InstrInfo; +def AArch64InstrInfo : InstrInfo { + // Coalesce "Fallback" and default "" namespaces. + let CoalesceDecoderNamespaces = [["", "Fallback" ]]; +} //===----------------------------------------------------------------------===// // Named operands for MRS/MSR/TLBI/... diff --git a/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp b/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp index 8c1e9f61693fb..a3ab01449893b 100644 --- a/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp +++ b/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp @@ -1608,7 +1608,7 @@ DecodeStatus AArch64Disassembler::getInstruction(MCInst &MI, uint64_t &Size, uint32_t Insn = (Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | (Bytes[0] << 0); - const uint8_t *Tables[] = {DecoderTable32, DecoderTableFallback32}; + const uint8_t *Tables[] = {DecoderTable32}; for (const auto *Table : Tables) { DecodeStatus Result = diff --git a/llvm/lib/Target/AMDGPU/AMDGPU.td b/llvm/lib/Target/AMDGPU/AMDGPU.td index a366db1c580ba..02d29318d0fdb 100644 --- a/llvm/lib/Target/AMDGPU/AMDGPU.td +++ b/llvm/lib/Target/AMDGPU/AMDGPU.td @@ -2115,6 +2115,11 @@ def FeatureISAVersion12_Generic: FeatureSet< def AMDGPUInstrInfo : InstrInfo { let guessInstructionProperties = 1; + let CoalesceDecoderNamespaces = [ + ["GFX11", "GFX11_FAKE16"], + ["GFX12", "GFX12_FAKE16"], + ["GFX1250", "GFX1250_FAKE16"], + ]; } def AMDGPUAsmParser : AsmParser { diff --git a/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp b/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp index b50b2a2e6e23c..f457085e42eb4 100644 --- a/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp +++ b/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp @@ -617,18 +617,15 @@ DecodeStatus AMDGPUDisassembler::getInstruction(MCInst &MI, uint64_t &Size, std::bitset<96> DecW = eat12Bytes(Bytes); if (isGFX11() && - tryDecodeInst(DecoderTableGFX1196, DecoderTableGFX11_FAKE1696, MI, - DecW, Address, CS)) + tryDecodeInst(DecoderTableGFX1196, MI, DecW, Address, CS)) break; if (isGFX1250() && - tryDecodeInst(DecoderTableGFX125096, DecoderTableGFX1250_FAKE1696, MI, - DecW, Address, CS)) + tryDecodeInst(DecoderTableGFX125096, MI, DecW, Address, CS)) break; if (isGFX12() && - tryDecodeInst(DecoderTableGFX1296, DecoderTableGFX12_FAKE1696, MI, - DecW, Address, CS)) + tryDecodeInst(DecoderTableGFX1296, MI, DecW, Address, CS)) break; if (isGFX12() && @@ -698,18 +695,13 @@ DecodeStatus AMDGPUDisassembler::getInstruction(MCInst &MI, uint64_t &Size, break; if (isGFX1250() && - tryDecodeInst(DecoderTableGFX125064, DecoderTableGFX1250_FAKE1664, MI, - QW, Address, CS)) + tryDecodeInst(DecoderTableGFX125064, MI, QW, Address, CS)) break; - if (isGFX12() && - tryDecodeInst(DecoderTableGFX1264, DecoderTableGFX12_FAKE1664, MI, QW, - Address, CS)) + if (isGFX12() && tryDecodeInst(DecoderTableGFX1264, MI, QW, Address, CS)) break; - if (isGFX11() && - tryDecodeInst(DecoderTableGFX1164, DecoderTableGFX11_FAKE1664, MI, QW, - Address, CS)) + if (isGFX11() && tryDecodeInst(DecoderTableGFX1164, MI, QW, Address, CS)) break; if (isGFX11() && @@ -753,19 +745,14 @@ DecodeStatus AMDGPUDisassembler::getInstruction(MCInst &MI, uint64_t &Size, if (isGFX10() && tryDecodeInst(DecoderTableGFX1032, MI, DW, Address, CS)) break; - if (isGFX11() && - tryDecodeInst(DecoderTableGFX1132, DecoderTableGFX11_FAKE1632, MI, DW, - Address, CS)) + if (isGFX11() && tryDecodeInst(DecoderTableGFX1132, MI, DW, Address, CS)) break; if (isGFX1250() && - tryDecodeInst(DecoderTableGFX125032, DecoderTableGFX1250_FAKE1632, MI, - DW, Address, CS)) + tryDecodeInst(DecoderTableGFX125032, MI, DW, Address, CS)) break; - if (isGFX12() && - tryDecodeInst(DecoderTableGFX1232, DecoderTableGFX12_FAKE1632, MI, DW, - Address, CS)) + if (isGFX12() && tryDecodeInst(DecoderTableGFX1232, MI, DW, Address, CS)) break; } diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp index 89df9d82f8780..86ea7bfb5b592 100644 --- a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp +++ b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp @@ -691,9 +691,9 @@ static constexpr DecoderListEntry DecoderList32[]{ {DecoderTableXSMT32, XSMTGroup, "SpacemiT extensions"}, // Standard Extensions {DecoderTable32, {}, "standard 32-bit instructions"}, - {DecoderTableRV32Only32, {}, "RV32-only standard 32-bit instructions"}, - {DecoderTableZfinx32, {}, "Zfinx (Float in Integer)"}, - {DecoderTableZdinxRV32Only32, {}, "RV32-only Zdinx (Double in Integer)"}, + //{DecoderTableRV32Only32, {}, "RV32-only standard 32-bit instructions"}, + //{DecoderTableZfinx32, {}, "Zfinx (Float in Integer)"}, + //{DecoderTableZdinxRV32Only32, {}, "RV32-only Zdinx (Double in Integer)"}, }; namespace { @@ -743,7 +743,7 @@ static constexpr DecoderListEntry DecoderList16[]{ // DecoderTableZicfiss16 must be checked before DecoderTable16. {DecoderTableZicfiss16, {}, "Zicfiss (Shadow Stack 16-bit)"}, {DecoderTable16, {}, "standard 16-bit instructions"}, - {DecoderTableRV32Only16, {}, "RV32-only 16-bit instructions"}, + //{DecoderTableRV32Only16, {}, "RV32-only 16-bit instructions"}, // Zc* instructions incompatible with Zcf or Zcd {DecoderTableZcOverlap16, {}, diff --git a/llvm/lib/Target/RISCV/RISCV.td b/llvm/lib/Target/RISCV/RISCV.td index b24d8637cb27f..e005878dc1274 100644 --- a/llvm/lib/Target/RISCV/RISCV.td +++ b/llvm/lib/Target/RISCV/RISCV.td @@ -85,6 +85,11 @@ include "RISCVPfmCounters.td" def RISCVInstrInfo : InstrInfo { let guessInstructionProperties = 0; + let CoalesceDecoderNamespaces = [ + ["", "RV32Only", "Zfinx", "ZdinxRV32Only"] + // ["Zicfiss", "", "RV32Only", "ZcOverlap"], // Need to handle RV32Only mappings. + ]; + } def RISCVAsmParser : AsmParser { diff --git a/llvm/test/TableGen/FixedLenDecoderEmitter/additional-encoding.td b/llvm/test/TableGen/FixedLenDecoderEmitter/additional-encoding.td index ec7e35e1ecac7..baa607c6fbf60 100644 --- a/llvm/test/TableGen/FixedLenDecoderEmitter/additional-encoding.td +++ b/llvm/test/TableGen/FixedLenDecoderEmitter/additional-encoding.td @@ -31,22 +31,22 @@ class I : Instruction { } // CHECK: /* 0 */ MCD::OPC_ExtractField, 12, 4, // Inst{15-12} ... -// CHECK-NEXT: /* 3 */ MCD::OPC_FilterValueOrSkip, 0, 15, 0, // Skip to: 22 +// CHECK-NEXT: /* 3 */ MCD::OPC_FilterValueOrSkip, 0, 15, 0, // FilterVal = 0x0, Skip to: 22 // CHECK-NEXT: /* 7 */ MCD::OPC_Scope, 8, 0, // Skip to: 18 // CHECK-NEXT: /* 10 */ MCD::OPC_CheckField, 6, 6, 0, // CHECK-NEXT: /* 14 */ MCD::OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: {{.*}}:NOP, DecodeIdx: 0 // CHECK-NEXT: /* 18 */ MCD::OPC_TryDecode, 187, 2, 1, -// CHECK-NEXT: /* 22 */ MCD::OPC_FilterValueOrSkip, 1, 15, 0, // Skip to: 41 +// CHECK-NEXT: /* 22 */ MCD::OPC_FilterValueOrSkip, 1, 15, 0, // FilterVal = 0x1, Skip to: 41 // CHECK-NEXT: /* 26 */ MCD::OPC_Scope, 8, 0, // Skip to: 37 // CHECK-NEXT: /* 29 */ MCD::OPC_CheckField, 6, 6, 0, // CHECK-NEXT: /* 33 */ MCD::OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: {{.*}}:NOP, DecodeIdx: 0 // CHECK-NEXT: /* 37 */ MCD::OPC_TryDecode, 188, 2, 1, -// CHECK-NEXT: /* 41 */ MCD::OPC_FilterValueOrSkip, 2, 15, 0, // Skip to: 60 +// CHECK-NEXT: /* 41 */ MCD::OPC_FilterValueOrSkip, 2, 15, 0, // FilterVal = 0x2, Skip to: 60 // CHECK-NEXT: /* 45 */ MCD::OPC_Scope, 8, 0, // Skip to: 56 // CHECK-NEXT: /* 48 */ MCD::OPC_CheckField, 6, 6, 0, // CHECK-NEXT: /* 52 */ MCD::OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: {{.*}}:NOP, DecodeIdx: 0 // CHECK-NEXT: /* 56 */ MCD::OPC_TryDecode, 189, 2, 1, -// CHECK-NEXT: /* 60 */ MCD::OPC_FilterValue, 3, +// CHECK-NEXT: /* 60 */ MCD::OPC_FilterValue, 3, // FilterVal = 0x3 // CHECK-NEXT: /* 62 */ MCD::OPC_Scope, 8, 0, // Skip to: 73 // CHECK-NEXT: /* 65 */ MCD::OPC_CheckField, 6, 6, 0, // CHECK-NEXT: /* 69 */ MCD::OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: {{.*}}:NOP, DecodeIdx: 0 diff --git a/llvm/test/TableGen/FixedLenDecoderEmitter/big-filter.td b/llvm/test/TableGen/FixedLenDecoderEmitter/big-filter.td index 28762bfa1ec24..d0c2918ef8042 100644 --- a/llvm/test/TableGen/FixedLenDecoderEmitter/big-filter.td +++ b/llvm/test/TableGen/FixedLenDecoderEmitter/big-filter.td @@ -13,10 +13,10 @@ class I : Instruction { // // CHECK-LABEL: static const uint8_t DecoderTable128[34] = { // CHECK-NEXT: /* 0 */ MCD::OPC_ExtractField, 0, 64, // Inst{63-0} ... -// CHECK-NEXT: /* 3 */ MCD::OPC_FilterValueOrSkip, 1, 8, 0, // Skip to: 15 +// CHECK-NEXT: /* 3 */ MCD::OPC_FilterValueOrSkip, 1, 8, 0, // FilterVal = 0x1, Skip to: 15 // CHECK-NEXT: /* 7 */ MCD::OPC_CheckField, 127, 1, 1, // CHECK-NEXT: /* 11 */ MCD::OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: I2, DecodeIdx: 0 -// CHECK-NEXT: /* 15 */ MCD::OPC_FilterValue, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, +// CHECK-NEXT: /* 15 */ MCD::OPC_FilterValue, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, // FilterVal = 0xffffffffffffffff // CHECK-NEXT: /* 26 */ MCD::OPC_CheckField, 127, 1, 0, // CHECK-NEXT: /* 30 */ MCD::OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: I1, DecodeIdx: 0 // CHECK-NEXT: }; diff --git a/llvm/test/TableGen/FixedLenDecoderEmitter/conflict.td b/llvm/test/TableGen/FixedLenDecoderEmitter/conflict.td index 853a68d22d1d9..c33c1bf0342b5 100644 --- a/llvm/test/TableGen/FixedLenDecoderEmitter/conflict.td +++ b/llvm/test/TableGen/FixedLenDecoderEmitter/conflict.td @@ -9,27 +9,44 @@ def MyTarget : Target { let InstructionSet = MyTargetISA; } def R0 : Register<"r0"> { let Namespace = "MyTarget"; } def GPR32 : RegisterClass<"MyTarget", [i32], 32, (add R0)>; +def X0 : Register<"x0"> { let Namespace = "MyTarget"; } +def GPR64 : RegisterClass<"MyTarget", [i64], 64, (add X0)>; + class I Pat> : Instruction { let Namespace = "MyTarget"; let OutOperandList = OOps; let InOperandList = IOps; let Pattern = Pat; + let Size = 4; bits<32> Inst; bits<32> SoftFail; } +// Assume there isa 2 bit encoding for the dst and src register. def A : I<(outs GPR32:$dst), (ins GPR32:$src1), []> { - let Size = 4; - let Inst{31...0} = 0; + bits<2> dst; + bits<2> src1; + let Inst{31...4} = 0; + let Inst{1...0} = dst; + let Inst{3...2} = src1; } + def B : I<(outs GPR32:$dst), (ins GPR32:$src1), []> { - let Size = 4; - let Inst{31...0} = 0; + bits<2> dst; + bits<2> src1; + let Inst{31...4} = 0; + let Inst{1...0} = dst; + let Inst{3...2} = src1; +} + +def C : I<(outs), (ins), []> { + let Inst{31...4} = 1; + let Inst{3...0} = 0; } // CHECK: Decoding Conflict: // CHECK: ................................ -// CHECK: 00000000000000000000000000000000 -// CHECK: 00000000000000000000000000000000 A -// CHECK: 00000000000000000000000000000000 B +// CHECK: 0000000000000000000000000000.... +// CHECK: 0000000000000000000000000000____ A +// CHECK: 0000000000000000000000000000____ B diff --git a/llvm/test/TableGen/FixedLenDecoderEmitter/var-len-conflict-1.td b/llvm/test/TableGen/FixedLenDecoderEmitter/var-len-conflict-1.td index 8afcf786f9c73..8021e3917d744 100644 --- a/llvm/test/TableGen/FixedLenDecoderEmitter/var-len-conflict-1.td +++ b/llvm/test/TableGen/FixedLenDecoderEmitter/var-len-conflict-1.td @@ -20,16 +20,16 @@ class I : Instruction { // CHECK: /* 0 */ MCD::OPC_Scope, 17, 0, // Skip to: 20 // CHECK-NEXT: /* 3 */ MCD::OPC_ExtractField, 0, 1, // Inst{0} ... -// CHECK-NEXT: /* 6 */ MCD::OPC_FilterValueOrSkip, 0, 4, 0, // Skip to: 14 +// CHECK-NEXT: /* 6 */ MCD::OPC_FilterValueOrSkip, 0, 4, 0, // FilterVal = 0x0, Skip to: 14 // CHECK-NEXT: /* 10 */ MCD::OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: I8_0, DecodeIdx: 0 -// CHECK-NEXT: /* 14 */ MCD::OPC_FilterValue, 1, +// CHECK-NEXT: /* 14 */ MCD::OPC_FilterValue, 1, // FilterVal = 0x1 // CHECK-NEXT: /* 16 */ MCD::OPC_Decode, {{[0-9]+}}, 2, 0, // Opcode: I8_1, DecodeIdx: 0 // CHECK-NEXT: /* 20 */ MCD::OPC_ExtractField, 8, 8, // Inst{15-8} ... -// CHECK-NEXT: /* 23 */ MCD::OPC_FilterValueOrSkip, 0, 4, 0, // Skip to: 31 +// CHECK-NEXT: /* 23 */ MCD::OPC_FilterValueOrSkip, 0, 4, 0, // FilterVal = 0x0, Skip to: 31 // CHECK-NEXT: /* 27 */ MCD::OPC_Decode, {{[0-9]+}}, 2, 1, // Opcode: I16_0, DecodeIdx: 1 -// CHECK-NEXT: /* 31 */ MCD::OPC_FilterValueOrSkip, 1, 4, 0, // Skip to: 39 +// CHECK-NEXT: /* 31 */ MCD::OPC_FilterValueOrSkip, 1, 4, 0, // FilterVal = 0x1, Skip to: 39 // CHECK-NEXT: /* 35 */ MCD::OPC_Decode, {{[0-9]+}}, 2, 1, // Opcode: I16_1, DecodeIdx: 1 -// CHECK-NEXT: /* 39 */ MCD::OPC_FilterValue, 2, +// CHECK-NEXT: /* 39 */ MCD::OPC_FilterValue, 2, // FilterVal = 0x2 // CHECK-NEXT: /* 41 */ MCD::OPC_Decode, {{[0-9]+}}, 2, 1, // Opcode: I16_2, DecodeIdx: 1 def I8_0 : I { dag Inst = (descend (operand "$op", 7), 0b0); } diff --git a/llvm/test/TableGen/VarLenDecoder.td b/llvm/test/TableGen/VarLenDecoder.td index 7eda1e6e47431..4ad9484a974c2 100644 --- a/llvm/test/TableGen/VarLenDecoder.td +++ b/llvm/test/TableGen/VarLenDecoder.td @@ -54,16 +54,16 @@ def FOO32 : MyVarInst { // CHECK-NEXT: }; // CHECK-SMALL: /* 0 */ MCD::OPC_ExtractField, 3, 5, // Inst{7-3} ... -// CHECK-SMALL-NEXT: /* 3 */ MCD::OPC_FilterValueOrSkip, 8, 4, 0, // Skip to: 11 +// CHECK-SMALL-NEXT: /* 3 */ MCD::OPC_FilterValueOrSkip, 8, 4, 0, // FilterVal = 0x8, Skip to: 11 // CHECK-SMALL-NEXT: /* 7 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, // Opcode: FOO16 -// CHECK-SMALL-NEXT: /* 11 */ MCD::OPC_FilterValue, 9, +// CHECK-SMALL-NEXT: /* 11 */ MCD::OPC_FilterValue, 9, // FilterVal = 0x9 // CHECK-SMALL-NEXT: /* 13 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: FOO32 // CHECK-SMALL-NEXT: }; // CHECK-LARGE: /* 0 */ MCD::OPC_ExtractField, 3, 5, // Inst{7-3} ... -// CHECK-LARGE-NEXT: /* 3 */ MCD::OPC_FilterValueOrSkip, 8, 4, 0, 0, // Skip to: 12 +// CHECK-LARGE-NEXT: /* 3 */ MCD::OPC_FilterValueOrSkip, 8, 4, 0, 0, // FilterVal = 0x8, Skip to: 12 // CHECK-LARGE-NEXT: /* 8 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, // Opcode: FOO16 -// CHECK-LARGE-NEXT: /* 12 */ MCD::OPC_FilterValue, 9, +// CHECK-LARGE-NEXT: /* 12 */ MCD::OPC_FilterValue, 9, // FilterVal = 0x9 // CHECK-LARGE-NEXT: /* 14 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: FOO32 // CHECK-LARGE-NEXT: }; diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp index 95e7408ea88c7..ba37e1dc9e27b 100644 --- a/llvm/utils/TableGen/DecoderEmitter.cpp +++ b/llvm/utils/TableGen/DecoderEmitter.cpp @@ -28,6 +28,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/MC/MCDecoderOps.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" @@ -116,6 +117,12 @@ static cl::opt IgnoreFullyDefinedOperands( "Do not automatically decode operands with no '?' in their encoding."), cl::init(false), cl::cat(DisassemblerEmitterCat)); +static cl::opt ResolveConflictsByTryingAll( + "resolve-conflicts-try-all", + cl::desc( + "Resolve decoding conflicts by trying all conflciting instructions."), + cl::init(false), cl::cat(DisassemblerEmitterCat)); + STATISTIC(NumEncodings, "Number of encodings considered"); STATISTIC(NumEncodingsLackingDisasm, "Number of encodings without disassembler info"); @@ -174,6 +181,36 @@ struct OperandInfo { ArrayRef fields() const { return Fields; } }; +class CoalescedNamespaces { + // Map from a namespace to its coalesced namespace, order info. + StringMap> NSMap; + +public: + CoalescedNamespaces() {} + void addNamespaces(ArrayRef NSList) { + if (NSList.size() <= 1) + return; + StringRef Head = NSList.front(); + for (const auto &[Idx, NS] : enumerate(NSList)) { + if (NSMap.contains(NS)) + PrintFatalError("Namespace " + NS + " already coalesced"); + NSMap[NS] = std::make_pair(Head, (unsigned)Idx); + } + } + StringRef getEffectiveNS(StringRef NS) const { + auto II = NSMap.find(NS); + if (II == NSMap.end()) + return NS; + return II->second.first; + } + unsigned getNSOrder(StringRef NS) const { + auto II = NSMap.find(NS); + if (II == NSMap.end()) + return 0; + return II->second.second; + } +}; + /// Represents a parsed InstructionEncoding record or a record derived from it. class InstructionEncoding { /// The Record this encoding originates from. @@ -187,6 +224,7 @@ class InstructionEncoding { /// The namespace in which this encoding exists. StringRef DecoderNamespace; + StringRef EffectiveDecoderNamespace; /// Known bits of this encoding. This is the value of the `Inst` field /// with any variable references replaced with '?'. @@ -209,8 +247,8 @@ class InstructionEncoding { SmallVector Operands; public: - InstructionEncoding(const Record *EncodingDef, - const CodeGenInstruction *Inst); + InstructionEncoding(const Record *EncodingDef, const CodeGenInstruction *Inst, + const CoalescedNamespaces &NSInfo); /// Returns the Record this encoding originates from. const Record *getRecord() const { return EncodingDef; } @@ -221,8 +259,12 @@ class InstructionEncoding { /// Returns the name of this encoding, for debugging purposes. StringRef getName() const { return Name; } - /// Returns the namespace in which this encoding exists. + /// Returns the namespace in which this encoding exists. This always returns + /// the effective namespace when coalescing is enabled. StringRef getDecoderNamespace() const { return DecoderNamespace; } + StringRef getEffectiveDecoderNamespace() const { + return EffectiveDecoderNamespace; + } /// Returns the size of this encoding, in bits. unsigned getBitWidth() const { return InstBits.getBitWidth(); } @@ -263,16 +305,26 @@ class InstructionEncoding { void parseFixedLenOperands(const BitsInit &Bits); }; -/// Sorting predicate to sort encoding IDs by encoding width. +/// Sorting predicate to sort encoding IDs by encoding width. Withing the +/// same width, sort the decode namespace order. class LessEncodingIDByWidth { ArrayRef Encodings; + const CoalescedNamespaces &NSInfo; public: - explicit LessEncodingIDByWidth(ArrayRef Encodings) - : Encodings(Encodings) {} + explicit LessEncodingIDByWidth(ArrayRef Encodings, + const CoalescedNamespaces &NSInfo) + : Encodings(Encodings), NSInfo(NSInfo) {} bool operator()(unsigned ID1, unsigned ID2) const { - return Encodings[ID1].getBitWidth() < Encodings[ID2].getBitWidth(); + const InstructionEncoding &E1 = Encodings[ID1]; + const InstructionEncoding &E2 = Encodings[ID2]; + + unsigned NSOrder1 = NSInfo.getNSOrder(E1.getDecoderNamespace()); + unsigned NSOrder2 = NSInfo.getNSOrder(E2.getDecoderNamespace()); + + return std::tuple(E1.getBitWidth(), NSOrder1) < + std::tuple(E2.getBitWidth(), NSOrder2); } }; @@ -357,6 +409,8 @@ class DecoderEmitter { /// Encodings IDs for each HwMode. An ID is an index into Encodings. SmallDenseMap> EncodingIDsByHwMode; + CoalescedNamespaces NSInfo; + public: explicit DecoderEmitter(const RecordKeeper &RK); @@ -504,6 +558,11 @@ class FilterChooser { /// Also used when there is only one encoding. std::optional SingletonEncodingID; + // AttemptAll = true means we either have > 1 NamespaceChoosers or + // we sequentially attempt to decode witth the EncodingIDs. + bool AttemptAll = false; + std::vector> NamespaceChoosers; + /// If the selected filter matches multiple encodings, and there is /// *at least one* encoding in which all bits are known in the filtered range, /// then this is the FilterChooser created for the subset of encodings that @@ -517,6 +576,8 @@ class FilterChooser { /// The "field value" here refers to the encoding bits in the filtered range. std::map> FilterChooserMap; + const CoalescedNamespaces &NSInfo; + struct Island { unsigned StartBit; unsigned NumBits; @@ -526,10 +587,12 @@ class FilterChooser { public: /// Constructs a top-level filter chooser. FilterChooser(ArrayRef Encodings, - ArrayRef EncodingIDs) - : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(nullptr) { + ArrayRef EncodingIDs, + const CoalescedNamespaces &NSInfo) + : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(nullptr), + NSInfo(NSInfo) { // Sort encoding IDs once. - stable_sort(this->EncodingIDs, LessEncodingIDByWidth(Encodings)); + stable_sort(this->EncodingIDs, LessEncodingIDByWidth(Encodings, NSInfo)); // Filter width is the width of the smallest encoding. unsigned FilterWidth = Encodings[this->EncodingIDs.front()].getBitWidth(); FilterBits = KnownBits(FilterWidth); @@ -540,9 +603,10 @@ class FilterChooser { FilterChooser(ArrayRef Encodings, ArrayRef EncodingIDs, const KnownBits &FilterBits, const FilterChooser &Parent) - : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(&Parent) { + : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(&Parent), + NSInfo(Parent.NSInfo) { // Inferior filter choosers are created from sorted array of encoding IDs. - assert(is_sorted(EncodingIDs, LessEncodingIDByWidth(Encodings))); + assert(is_sorted(EncodingIDs, LessEncodingIDByWidth(Encodings, NSInfo))); assert(!FilterBits.hasConflict() && "Broken filter"); // Filter width is the width of the smallest encoding. unsigned FilterWidth = Encodings[EncodingIDs.front()].getBitWidth(); @@ -564,6 +628,8 @@ class FilterChooser { /// works with, creating inferior FilterChoosers as necessary. void applyFilter(const Filter &F); + void splitPerNamespace(); + /// dumpStack - dumpStack traverses the filter chooser chain and calls /// dumpFilterArray on each filter chooser up to the top level one. void dumpStack(raw_ostream &OS, indent Indent, unsigned PadToWidth) const; @@ -591,6 +657,8 @@ class FilterChooser { // dump the conflict set to the standard error. void doFilter(); + bool hasMultipleNamespaces() const; + public: void dump() const; }; @@ -679,6 +747,31 @@ void FilterChooser::applyFilter(const Filter &F) { NumBits = F.NumBits; assert(FilterBits.extractBits(NumBits, StartBit).isUnknown()); + // If we have a mix of fixed and variable encodings, and they have different + // namespaces, then split this filter into multiple ones. If its just fixed or + // just variable IDs, they should already be ordered in their namespace order. + // If we have both, then if we do not split, we may generate a decoding order + // as : f0, f1, f2, v0, v1, v2 + // whereas the intent was: f0, v0, f1, v1, f2, v2. So achieve this, split + // This into sub-filters and apply. If there is just fixed IDs or just + // variable IDs. we assume they are sorted in the right order. + if (!F.VariableIDs.empty() && !F.FilteredIDs.empty()) { + StringSet<> Namespaces; + for (unsigned ID : F.VariableIDs) + Namespaces.insert(Encodings[ID].getDecoderNamespace()); + for (const auto &[_, InferiorEncodingIDs] : F.FilteredIDs) { + for (unsigned ID : InferiorEncodingIDs) + Namespaces.insert(Encodings[ID].getDecoderNamespace()); + } + if (Namespaces.size() > 1) { + errs() << "Found multiple namespaces with non-empty variable IDs\n"; + splitPerNamespace(); + return; + } + } + + // Check if we have a mix of namespaces. If so, we need to attempt to decode + // them in the order of the namespaces if (!F.VariableIDs.empty()) { // Delegates to an inferior filter chooser for further processing on this // group of instructions whose segment values are variable. @@ -822,28 +915,55 @@ unsigned DecoderEmitter::emitTable(formatted_raw_ostream &OS, case MCD::OPC_FilterValueOrSkip: { OS << " MCD::OPC_FilterValueOrSkip, "; // The filter value is ULEB128 encoded. + const char *ErrMsg = nullptr; + uint64_t FilterVal = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg); + assert(ErrMsg == nullptr && "ULEB128 value too large!"); emitULEB128(I, OS); + uint32_t NumToSkip = emitNumToSkip(I, OS); - emitNumToSkipComment(NumToSkip); + + OS << "// FilterVal = 0x"; + OS.write_hex(FilterVal); + emitNumToSkipComment(NumToSkip, /*InComment=*/true); OS << '\n'; break; } case MCD::OPC_FilterValue: { OS << " MCD::OPC_FilterValue, "; + const char *ErrMsg = nullptr; + uint64_t FilterVal = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg); + assert(ErrMsg == nullptr && "ULEB128 value too large!"); // The filter value is ULEB128 encoded. emitULEB128(I, OS); + OS << "// FilterVal = 0x"; + OS.write_hex(FilterVal); OS << '\n'; break; } case MCD::OPC_CheckField: { OS << " MCD::OPC_CheckField, "; + + const char *ErrMsg = nullptr; + unsigned Start = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg); + assert(ErrMsg == nullptr && "ULEB128 value too large!"); // ULEB128 encoded start value. emitULEB128(I, OS); + // 8-bit length. unsigned Len = *I++; OS << Len << ", "; + + uint64_t FieldVal = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg); + assert(ErrMsg == nullptr && "ULEB128 value too large!"); + // ULEB128 encoded field value. emitULEB128(I, OS); + + OS << " // Inst{"; + if (Len > 1) + OS << (Start + Len - 1) << "-"; + OS << Start << "} == 0x"; + OS.write_hex(FieldVal); OS << '\n'; break; } @@ -873,11 +993,8 @@ unsigned DecoderEmitter::emitTable(formatted_raw_ostream &OS, assert(EncI != OpcodeToEncodingID.end() && "no encoding entry"); auto EncodingID = EncI->second; - if (!IsTry) { - OS << "// Opcode: " << Encodings[EncodingID].getName() - << ", DecodeIdx: " << DecodeIdx << '\n'; - break; - } + OS << "// Opcode: " << Encodings[EncodingID].getName() + << ", DecodeIdx: " << DecodeIdx; OS << '\n'; break; } @@ -1331,6 +1448,7 @@ void DecoderTableBuilder::emitSingletonTableEntry( // can decode it. const MCD::DecoderOps DecoderOp = Encoding.hasCompleteDecoder() ? MCD::OPC_Decode : MCD::OPC_TryDecode; + TableInfo.Table.insertOpcode(DecoderOp); const Record *InstDef = Encodings[EncodingID].getInstruction()->TheDef; TableInfo.Table.insertULEB128(Target.getInstrIntValue(InstDef)); @@ -1571,6 +1689,40 @@ std::unique_ptr FilterChooser::findBestFilter() const { return nullptr; } +bool FilterChooser::hasMultipleNamespaces() const { + if (EncodingIDs.size() <= 1) + return false; + const InstructionEncoding &First = Encodings[EncodingIDs.front()]; + const InstructionEncoding &Last = Encodings[EncodingIDs.back()]; + return First.getDecoderNamespace() != Last.getDecoderNamespace(); +} + +void FilterChooser::splitPerNamespace() { + if (!hasMultipleNamespaces()) + PrintFatalError("Expected use of > 1 decoder namespaces"); + + auto GetNSOrder = [&](unsigned EncodingID) -> unsigned { + StringRef NS = Encodings[EncodingID].getDecoderNamespace(); + return NSInfo.getNSOrder(NS); + }; + + // This splits a filter into per-namespace filter. + + unsigned LastNSOrder = GetNSOrder(EncodingIDs.back()); + std::vector Counts(LastNSOrder + 1, 0); + for (unsigned ID : EncodingIDs) + ++Counts[GetNSOrder(ID)]; + unsigned Start = 0; + for (unsigned Count : Counts) { + if (Count == 0) + continue; + ArrayRef NSEncodings = ArrayRef(EncodingIDs).slice(Start, Count); + NamespaceChoosers.push_back(std::make_unique( + Encodings, NSEncodings, FilterBits, *this)); + Start += Count; + } +} + // Decides on the best configuration of filter(s) to use in order to decode // the instructions. A conflict of instructions may occur, in which case we // dump the conflict set to the standard error. @@ -1589,6 +1741,16 @@ void FilterChooser::doFilter() { return; } + if (hasMultipleNamespaces()) { + splitPerNamespace(); + return; + } + + if (ResolveConflictsByTryingAll) { + AttemptAll = true; + return; + } + // Print out useful conflict information for postmortem analysis. errs() << "Decoding Conflict:\n"; dump(); @@ -1617,10 +1779,14 @@ void DecoderTableBuilder::emitTableEntries(const FilterChooser &FC) const { // 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; + // Note: If the same bit patten can match a known encoding as well as variable + // encoding, we first let the fixed encoding take priority over the variable + // ones. This is a sort of conflict, call it a soft-conflict which is always + // resolved by giving preference to the fixed encoding. + size_t VarScopeFixupLoc = 0; if (FC.VariableFC) { Table.insertOpcode(MCD::OPC_Scope); - FixupLoc = Table.insertNumToSkip(); + VarScopeFixupLoc = Table.insertNumToSkip(); } if (FC.SingletonEncodingID) { @@ -1640,7 +1806,7 @@ void DecoderTableBuilder::emitTableEntries(const FilterChooser &FC) const { // Emit table entries for the only case. emitTableEntries(*Delegate); - } else { + } else if (FC.FilterChooserMap.size() > 1) { // The general case: emit a switch over the field value. Table.insertOpcode(MCD::OPC_ExtractField); Table.insertULEB128(FC.StartBit); @@ -1667,10 +1833,35 @@ void DecoderTableBuilder::emitTableEntries(const FilterChooser &FC) const { // Emit table entries for the last case. emitTableEntries(*Delegate); + } else if (!FC.NamespaceChoosers.empty()) { + errs() << "Attempting NS Choosers " << Table.size() << '\n'; + FC.dump(); + for (const auto &Delegate : drop_end(FC.NamespaceChoosers)) { + Table.insertOpcode(MCD::OPC_Scope); + unsigned FixupLoc = Table.insertNumToSkip(); + emitTableEntries(*Delegate); + Table.patchNumToSkip(FixupLoc, Table.size()); + } + emitTableEntries(*FC.NamespaceChoosers.back()); + } else if (FC.AttemptAll) { + errs() << "Attempting All " << Table.size() << '\n'; + FC.dump(); + for (const auto ID : drop_end(FC.EncodingIDs)) { + Table.insertOpcode(MCD::OPC_Scope); + unsigned FixupLoc = Table.insertNumToSkip(); + FilterChooser SingletonFC(FC.Encodings, ID, FC.FilterBits, *FC.Parent); + emitSingletonTableEntry(SingletonFC); + Table.patchNumToSkip(FixupLoc, Table.size()); + } + FilterChooser LastSingletonFC(FC.Encodings, FC.EncodingIDs.back(), + FC.FilterBits, *FC.Parent); + emitSingletonTableEntry(LastSingletonFC); + } else { + llvm_unreachable("Invalid FilterChooser"); } if (FC.VariableFC) { - Table.patchNumToSkip(FixupLoc, Table.size()); + Table.patchNumToSkip(VarScopeFixupLoc, Table.size()); emitTableEntries(*FC.VariableFC); } } @@ -2051,7 +2242,8 @@ void InstructionEncoding::parseFixedLenOperands(const BitsInit &Bits) { } InstructionEncoding::InstructionEncoding(const Record *EncodingDef, - const CodeGenInstruction *Inst) + const CodeGenInstruction *Inst, + const CoalescedNamespaces &NSInfo) : EncodingDef(EncodingDef), Inst(Inst) { const Record *InstDef = Inst->TheDef; @@ -2061,6 +2253,7 @@ InstructionEncoding::InstructionEncoding(const Record *EncodingDef, Name.append(InstDef->getName()); DecoderNamespace = EncodingDef->getValueAsString("DecoderNamespace"); + EffectiveDecoderNamespace = NSInfo.getEffectiveNS(DecoderNamespace); DecoderMethod = EncodingDef->getValueAsString("DecoderMethod"); if (!DecoderMethod.empty()) HasCompleteDecoder = EncodingDef->getValueAsBit("hasCompleteDecoder"); @@ -2268,9 +2461,11 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI, S = decodeToMCInst(DecodeIdx, S, insn, MI, Address, DisAsm, DecodeComplete); assert(DecodeComplete); - LLVM_DEBUG(dbgs() << Loc << ": OPC_Decode: opcode " << Opc - << ", using decoder " << DecodeIdx << ": " - << (S != MCDisassembler::Fail ? "PASS\n" : "FAIL\n")); + LLVM_DEBUG({ + dbgs() << Loc << ": OPC_Decode : opcode " << Opc + << ", using decoder " << DecodeIdx << ": " + << (S != MCDisassembler::Fail ? "PASS\n" : "FAIL\n"); + }); return S; })"; if (HasTryDecode) { @@ -2295,15 +2490,15 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI, return S; } assert(S == MCDisassembler::Fail); + // Reset decode status. This also drops a SoftFail status that could be + // set before the decode attempt. + S = MCDisassembler::Success; if (ScopeStack.empty()) { LLVM_DEBUG(dbgs() << "FAIL, returning FAIL\n"); return MCDisassembler::Fail; } Ptr = ScopeStack.pop_back_val(); LLVM_DEBUG(dbgs() << "FAIL, continuing at " << Ptr - DecodeTable << '\n'); - // Reset decode status. This also drops a SoftFail status that could be - // set before the decode attempt. - S = MCDisassembler::Success; break; })"; } @@ -2339,6 +2534,7 @@ void DecoderEmitter::collectHwModesReferencedForEncodings( if (EncodingDef->isSubClassOf("InstructionEncoding")) { StringRef DecoderNamespace = EncodingDef->getValueAsString("DecoderNamespace"); + DecoderNamespace = NSInfo.getEffectiveNS(DecoderNamespace); NamespacesWithHwModes[DecoderNamespace].insert(HwModeID); BV.set(HwModeID); } @@ -2360,7 +2556,8 @@ void DecoderEmitter::handleHwModesUnrelatedEncodings( break; } case SUPPRESSION_LEVEL1: { - StringRef DecoderNamespace = Encodings[EncodingID].getDecoderNamespace(); + StringRef DecoderNamespace = + Encodings[EncodingID].getEffectiveDecoderNamespace(); auto It = NamespacesWithHwModes.find(DecoderNamespace); if (It != NamespacesWithHwModes.end()) { for (unsigned HwModeID : It->second) @@ -2445,7 +2642,7 @@ void DecoderEmitter::parseInstructionEncodings() { continue; } unsigned EncodingID = Encodings.size(); - Encodings.emplace_back(EncodingDef, Inst); + Encodings.emplace_back(EncodingDef, Inst, NSInfo); EncodingIDsByHwMode[HwModeID].push_back(EncodingID); } continue; // Ignore encoding specified by Instruction itself. @@ -2457,7 +2654,7 @@ void DecoderEmitter::parseInstructionEncodings() { } unsigned EncodingID = Encodings.size(); - Encodings.emplace_back(InstDef, Inst); + Encodings.emplace_back(InstDef, Inst, NSInfo); // This instruction is encoded the same on all HwModes. // According to user needs, add it to all, some, or only the default HwMode. @@ -2480,7 +2677,8 @@ void DecoderEmitter::parseInstructionEncodings() { continue; } unsigned EncodingID = Encodings.size(); - Encodings.emplace_back(EncodingDef, &Target.getInstruction(InstDef)); + Encodings.emplace_back(EncodingDef, &Target.getInstruction(InstDef), + NSInfo); EncodingIDsByHwMode[DefaultMode].push_back(EncodingID); } @@ -2492,6 +2690,25 @@ void DecoderEmitter::parseInstructionEncodings() { DecoderEmitter::DecoderEmitter(const RecordKeeper &RK) : RK(RK), Target(RK), CGH(Target.getHwModes()) { + // First parse information about coalesced namespaces. + const ListInit *LI = Target.getInstructionSet()->getValueAsListInit( + "CoalesceDecoderNamespaces"); + for (const Init *LE : LI->getElements()) { + const ListInit *InnerLI = dyn_cast(LE); + if (!InnerLI) + PrintFatalError( + "CoalesceDecoderNamespaces expected to be a list of list of strings"); + std::vector Strings; + for (const Init *I : InnerLI->getElements()) { + if (const auto *SI = dyn_cast(I)) + Strings.push_back(SI->getValue()); + else + PrintFatalError("CoalesceDecoderNamespaces expected to be a list of " + "list of strings"); + } + NSInfo.addNamespaces(Strings); + } + Target.reverseBitsForLittleEndianEncoding(); parseInstructionEncodings(); } @@ -2551,7 +2768,9 @@ template constexpr uint32_t InsnBitWidth = 0; const InstructionEncoding &Encoding = Encodings[EncodingID]; const unsigned BitWidth = IsVarLenInst ? MaxInstLen : Encoding.getBitWidth(); - StringRef DecoderNamespace = Encoding.getDecoderNamespace(); + // For the purpose of bucketing, map the decoder namespace to its + // effective one. + StringRef DecoderNamespace = Encoding.getEffectiveDecoderNamespace(); EncMap[BitWidth][{DecoderNamespace, HwModeID}].push_back(EncodingID); } } @@ -2575,7 +2794,7 @@ template constexpr uint32_t InsnBitWidth = 0; auto [DecoderNamespace, HwModeID] = Key; // Emit the decoder for this (namespace, hwmode, width) combination. - FilterChooser FC(Encodings, EncodingIDs); + FilterChooser FC(Encodings, EncodingIDs, NSInfo); // The decode table is cleared for each top level decoder function. The // predicates and decoders themselves, however, are shared across