Skip to content
Draft

New POC #159201

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions llvm/lib/Target/AArch64/AArch64InstrFormats.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Target/AArch64/AArch64InstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}

//===----------------------------------------------------------------------===//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
1 change: 1 addition & 0 deletions llvm/test/TableGen/DecoderEmitter/conflict.td
Original file line number Diff line number Diff line change
@@ -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"

Expand Down
1 change: 1 addition & 0 deletions llvm/test/TableGen/DecoderEmitter/var-len-conflict-2.td
Original file line number Diff line number Diff line change
@@ -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"

Expand Down
175 changes: 132 additions & 43 deletions llvm/utils/TableGen/DecoderEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,9 @@ class FilterChooser {
/// The "field value" here refers to the encoding bits in the filtered range.
std::map<uint64_t, std::unique_ptr<const FilterChooser>> FilterChooserMap;

std::vector<unsigned> AllUnfilteredUnknownIDs;
std::unique_ptr<const FilterChooser> SomeUnfilteredKnownFC;

/// Set to true if decoding conflict was encountered.
bool HasConflict = false;

Expand Down Expand Up @@ -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<unsigned> 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<FilterChooser>(
Encodings, SomeUnfilteredKnownIDs, FilterBits, *this);
return;
}

// Print out useful conflict information for postmortem analysis.
errs() << "Decoding Conflict:\n";
dump();
Expand All @@ -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
Expand Down