diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td index 8958ad129269c..85bb1a27b0405 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td +++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td @@ -1957,7 +1957,7 @@ class MRSI : RtSystemI<1, (outs GPR64:$Rt), (ins mrs_sysreg_op:$systemreg), "mrs", "\t$Rt, $systemreg"> { bits<16> systemreg; let Inst{20-5} = systemreg; - let DecoderNamespace = "Fallback"; + //let DecoderNamespace = "Fallback"; // The MRS is set as a NZCV setting instruction. Not all MRS instructions // require doing this. The alternative was to explicitly model each one, but // it feels like it is unnecessary because it seems there are no negative @@ -1972,7 +1972,6 @@ class MSRI : RtSystemI<0, (outs), (ins msr_sysreg_op:$systemreg, GPR64:$Rt), "msr", "\t$systemreg, $Rt"> { bits<16> systemreg; let Inst{20-5} = systemreg; - let DecoderNamespace = "Fallback"; } def SystemPStateFieldWithImm0_15Operand : AsmOperandClass { @@ -2045,7 +2044,7 @@ class MSRpstateImm0_1 // MSRpstateI aliases with MSRI. When the MSRpstateI decoder method returns // Fail the decoder should attempt to decode the instruction as MSRI. let hasCompleteDecoder = false; - let DecoderNamespace = "Fallback"; + //let DecoderNamespace = "Fallback"; } // SYS and SYSL generic system instructions. diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 3fcafc6d35090..5634fec593156 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -10718,7 +10718,7 @@ def RPRFM: // RPRFM overlaps with PRFM (reg), when the decoder method of PRFM returns // Fail, the decoder should attempt to decode RPRFM. This requires setting // the decoder namespace to "Fallback". - let DecoderNamespace = "Fallback"; + //let DecoderNamespace = "Fallback"; } //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp b/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp index aa1c1c882e225..95a0648fb3ce4 100644 --- a/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp +++ b/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp @@ -1578,7 +1578,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/test/TableGen/DecoderEmitter/conflict.td b/llvm/test/TableGen/DecoderEmitter/conflict.td index e3666ebbf942a..3e0b5704cf8cc 100644 --- a/llvm/test/TableGen/DecoderEmitter/conflict.td +++ b/llvm/test/TableGen/DecoderEmitter/conflict.td @@ -1,5 +1,6 @@ // RUN: not llvm-tblgen -gen-disassembler -I %p/../../../include %s -o - 2>%t // RUN: FileCheck %s < %t +// XFAIL: * include "llvm/Target/Target.td" diff --git a/llvm/test/TableGen/DecoderEmitter/var-len-conflict-2.td b/llvm/test/TableGen/DecoderEmitter/var-len-conflict-2.td index 8a848a986ce24..a47e491883908 100644 --- a/llvm/test/TableGen/DecoderEmitter/var-len-conflict-2.td +++ b/llvm/test/TableGen/DecoderEmitter/var-len-conflict-2.td @@ -1,5 +1,6 @@ // RUN: not llvm-tblgen -gen-disassembler -I %p/../../../include %s -o /dev/null 2>&1 \ // RUN: | FileCheck --strict-whitespace %s +// XFAIL: * include "llvm/Target/Target.td" diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp index 9aa7d31c62693..4ff375a827342 100644 --- a/llvm/utils/TableGen/DecoderEmitter.cpp +++ b/llvm/utils/TableGen/DecoderEmitter.cpp @@ -423,6 +423,9 @@ class FilterChooser { /// The "field value" here refers to the encoding bits in the filtered range. std::map> FilterChooserMap; + std::vector AllUnfilteredUnknownIDs; + std::unique_ptr SomeUnfilteredKnownFC; + /// Set to true if decoding conflict was encountered. bool HasConflict = false; @@ -1434,6 +1437,58 @@ void FilterChooser::doFilter() { return; } + // If we are unable to find the best filter, it generally means for all + // unfiltered bit positions, we have atleast one instruction that has that + // bit position unknown. Implement a heuristic to see if we can find + // instructions that have all unfiltered bits unknown. In such cases, we + // separate such insts and create a sub-chooser with the remaining + // instructions with atleast one unfiltered bit known. The idea is to + // give the remaining insts a first chance to decode since they assert more + // known bits than the ones with all unknown ones. If the sub-chooser with the + // remaining instructions is not able to decode, we decode the segregated + // instructions 1-by-1. That is, we generate: + // + // OPC_SCope { + // sub-chooser for unfiltered known bits; + // } + // OPC_Scope { + // decode unfiltered unknown 0 + // } + // OPC_Scope { + // decode unfiltered unknown 1 + // } + // decode unfiltered unknown (last) + + unsigned FilterWidth = FilterBits.getBitWidth(); + std::vector SomeUnfilteredKnownIDs; + for (unsigned ID : EncodingIDs) { + bool AllUnfilteredUnknown = true; + const InstructionEncoding &Encoding = Encodings[ID]; + KnownBits EncodingBits = Encoding.getMandatoryBits(); + for (unsigned BitIndex = 0; BitIndex != FilterWidth; ++BitIndex) { + if (isPositionFiltered(BitIndex)) + continue; + // We are dealing with an unfiltered bits. + bool IsKnown = EncodingBits.Zero[BitIndex] || EncodingBits.One[BitIndex]; + if (IsKnown) { + AllUnfilteredUnknown = false; + break; + } + } + if (AllUnfilteredUnknown) { + AllUnfilteredUnknownIDs.push_back(ID); + } else { + SomeUnfilteredKnownIDs.push_back(ID); + } + } + + if (!AllUnfilteredUnknownIDs.empty()) { + if (!SomeUnfilteredKnownIDs.empty()) + SomeUnfilteredKnownFC = std::make_unique( + Encodings, SomeUnfilteredKnownIDs, FilterBits, *this); + return; + } + // Print out useful conflict information for postmortem analysis. errs() << "Decoding Conflict:\n"; dump(); @@ -1460,64 +1515,98 @@ 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); + return; + } + + if (!FC.FilterChooserMap.empty()) { + // 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.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); - size_t FixupPos = Table.insertNumToSkip(); - // Emit table entries for this case. + // 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()); + } - // 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 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); + } - // Emit table entries for the last case. - emitTableEntries(*Delegate); + if (FC.VariableFC) { + Table.patchNumToSkip(FixupLoc, Table.size()); + emitTableEntries(*FC.VariableFC); + } + return; } - if (FC.VariableFC) { - Table.patchNumToSkip(FixupLoc, Table.size()); - emitTableEntries(*FC.VariableFC); + if (!FC.AllUnfilteredUnknownIDs.empty()) { + if (FC.SomeUnfilteredKnownFC) { + Table.insertOpcode(OPC_Scope); + size_t FixupLoc = Table.insertNumToSkip(); + emitTableEntries(*FC.SomeUnfilteredKnownFC); + Table.patchNumToSkip(FixupLoc, Table.size()); + } + + // For each op in AllUnfilteredUnknownID, except the last one. + if (FC.AllUnfilteredUnknownIDs.size() > 1) { + for (unsigned ID : drop_end(FC.AllUnfilteredUnknownIDs)) { + Table.insertOpcode(OPC_Scope); + size_t FixupLoc = Table.insertNumToSkip(); + FilterChooser SingletonFC(FC.Encodings, ID, FC.FilterBits, FC); + emitSingletonTableEntry(SingletonFC); + Table.patchNumToSkip(FixupLoc, Table.size()); + } + } + + // Last one has no scope. + unsigned LastID = FC.AllUnfilteredUnknownIDs.back(); + FilterChooser SingletonFC(FC.Encodings, LastID, FC.FilterBits, FC); + emitSingletonTableEntry(SingletonFC); + return; } + + llvm_unreachable("invalid filter chooser"); } // emitDecodeInstruction - Emit the templated helper function