[llvm] [TableGen][DecoderEmitter] Rework table construction/emission (PR #155889)
Sergei Barannikov via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 15 18:00:18 PDT 2025
https://github.com/s-barannikov updated https://github.com/llvm/llvm-project/pull/155889
>From 542049e43de9913e1d33bf481b68a5e26741cee0 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Sun, 14 Sep 2025 03:19:24 +0300
Subject: [PATCH 1/3] tmp
---
llvm/include/llvm/MC/MCDecoderOps.h | 20 +-
llvm/utils/TableGen/DecoderEmitter.cpp | 1127 +++++++++++++-----------
2 files changed, 636 insertions(+), 511 deletions(-)
diff --git a/llvm/include/llvm/MC/MCDecoderOps.h b/llvm/include/llvm/MC/MCDecoderOps.h
index 790ff3eb4f333..4e06deb0eacee 100644
--- a/llvm/include/llvm/MC/MCDecoderOps.h
+++ b/llvm/include/llvm/MC/MCDecoderOps.h
@@ -13,19 +13,15 @@
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_TryDecode, // OPC_TryDecode(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_TryDecode, // OPC_TryDecode(uleb128 Opcode, uleb128 DIdx)
+ OPC_SoftFail, // OPC_SoftFail(uleb128 PMask, uleb128 NMask)
};
} // namespace llvm::MCD
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 8ff1c89f3699c..9780e29435d84 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -24,12 +24,10 @@
#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"
@@ -82,12 +80,6 @@ static cl::opt<SuppressLevel> DecoderEmitterSuppressDuplicates(
"significantly reducing Table Duplications")),
cl::init(SUPPRESSION_DISABLE), cl::cat(DisassemblerEmitterCat));
-static cl::opt<bool> 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<bool> UseFnTableInDecodeToMCInst(
"use-fn-table-in-decode-to-mcinst",
cl::desc(
@@ -125,8 +117,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,
@@ -158,70 +148,185 @@ class LessEncodingIDByWidth {
}
};
-typedef SmallSetVector<CachedHashString, 16> PredicateSet;
-typedef SmallSetVector<CachedHashString, 16> DecoderSet;
+class DecoderTreeNode {
+public:
+ virtual ~DecoderTreeNode() = default;
+
+ enum KindTy {
+ AnyOf,
+ AllOf,
+ CheckField,
+ SwitchField,
+ CheckPredicate,
+ SoftFail,
+ Decode,
+ };
+
+ KindTy getKind() const { return Kind; }
+
+protected:
+ explicit DecoderTreeNode(KindTy Kind) : Kind(Kind) {}
+
+private:
+ KindTy Kind;
+};
+
+class AllOfNode : public DecoderTreeNode {
+ SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
+
+ static const DecoderTreeNode *
+ mapElement(decltype(Children)::const_reference Element) {
+ return Element.get();
+ }
-class DecoderTable {
public:
- DecoderTable() { Data.reserve(16384); }
+ AllOfNode() : DecoderTreeNode(AllOf) {}
+
+ void addChild(std::unique_ptr<DecoderTreeNode> Child) {
+ Children.push_back(std::move(Child));
+ }
- void clear() { Data.clear(); }
- size_t size() const { return Data.size(); }
- const uint8_t *data() const { return Data.data(); }
+ using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
+ decltype(&mapElement)>;
- using const_iterator = std::vector<uint8_t>::const_iterator;
- const_iterator begin() const { return Data.begin(); }
- const_iterator end() const { return Data.end(); }
+ child_iterator child_begin() const {
+ return child_iterator(Children.begin(), mapElement);
+ }
- /// Inserts a state machine opcode into the table.
- void insertOpcode(MCD::DecoderOps Opcode) { Data.push_back(Opcode); }
+ child_iterator child_end() const {
+ return child_iterator(Children.end(), mapElement);
+ }
- /// Inserts a uint8 encoded value into the table.
- void insertUInt8(unsigned Value) {
- assert(isUInt<8>(Value));
- Data.push_back(Value);
+ iterator_range<child_iterator> children() const {
+ return make_range(child_begin(), child_end());
}
+};
- /// 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);
+class AnyOfNode : public DecoderTreeNode {
+ SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
+
+ static const DecoderTreeNode *
+ mapElement(decltype(Children)::const_reference Element) {
+ return Element.get();
}
- // 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;
+public:
+ AnyOfNode() : DecoderTreeNode(AnyOf) {}
+
+ void addChild(std::unique_ptr<DecoderTreeNode> N) {
+ Children.push_back(std::move(N));
}
- 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");
+ using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
+ decltype(&mapElement)>;
- Data[FixupIdx] = static_cast<uint8_t>(Delta);
- Data[FixupIdx + 1] = static_cast<uint8_t>(Delta >> 8);
- if (getNumToSkipInBytes() == 3)
- Data[FixupIdx + 2] = static_cast<uint8_t>(Delta >> 16);
+ child_iterator child_begin() const {
+ return child_iterator(Children.begin(), mapElement);
}
-private:
- std::vector<uint8_t> Data;
+ child_iterator child_end() const {
+ return child_iterator(Children.end(), mapElement);
+ }
+
+ iterator_range<child_iterator> children() const {
+ return make_range(child_begin(), child_end());
+ }
+};
+
+class SwitchFieldNode : public DecoderTreeNode {
+ unsigned StartBit;
+ unsigned NumBits;
+ std::map<uint64_t, std::unique_ptr<DecoderTreeNode>> Cases;
+
+ static std::pair<uint64_t, const DecoderTreeNode *>
+ 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<DecoderTreeNode> N) {
+ Cases.try_emplace(Value, std::move(N));
+ }
+
+ unsigned getStartBit() const { return StartBit; }
+
+ unsigned getNumBits() const { return NumBits; }
+
+ using case_iterator =
+ mapped_iterator<decltype(Cases)::const_iterator, decltype(&mapElement)>;
+
+ case_iterator case_begin() const {
+ return case_iterator(Cases.begin(), mapElement);
+ }
+
+ case_iterator case_end() const {
+ return case_iterator(Cases.end(), mapElement);
+ }
+
+ iterator_range<case_iterator> cases() const {
+ return make_range(case_begin(), case_end());
+ }
+};
+
+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; }
+};
+
+class CheckPredicateNode : public DecoderTreeNode {
+ unsigned Index;
+
+public:
+ explicit CheckPredicateNode(unsigned Index)
+ : DecoderTreeNode(CheckPredicate), Index(Index) {}
+
+ unsigned getPredicateIndex() const { return Index; }
+};
+
+class SoftFailNode : public DecoderTreeNode {
+ uint64_t PositiveMask, NegativeMask;
+
+public:
+ explicit 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; }
+};
+
+class DecodeNode : public DecoderTreeNode {
+ unsigned EncodingID;
+ unsigned Index;
+
+public:
+ DecodeNode(unsigned EncodingID, unsigned Index)
+ : DecoderTreeNode(Decode), EncodingID(EncodingID), Index(Index) {}
+
+ unsigned getEncodingID() const { return EncodingID; }
+
+ unsigned getDecoderIndex() const { return Index; }
};
+typedef SmallSetVector<CachedHashString, 16> PredicateSet;
+typedef SmallSetVector<CachedHashString, 16> DecoderSet;
+
struct DecoderTableInfo {
- DecoderTable Table;
PredicateSet Predicates;
DecoderSet Decoders;
};
@@ -244,11 +349,10 @@ class DecoderEmitter {
const CodeGenTarget &getTarget() const { return Target; }
- // Emit the decoder state machine table. Returns a mask of MCD decoder ops
- // that were emitted.
- unsigned emitTable(formatted_raw_ostream &OS, DecoderTable &Table,
- StringRef Namespace, unsigned HwModeID, unsigned BitWidth,
- ArrayRef<unsigned> EncodingIDs) const;
+ // Emit the decoder state machine table.
+ void emitDecoderTable(formatted_raw_ostream &OS, const DecoderTreeNode *Tree,
+ StringRef Namespace, unsigned HwModeID,
+ unsigned BitWidth) const;
void emitInstrLenTable(formatted_raw_ostream &OS,
ArrayRef<unsigned> InstrLen) const;
void emitPredicateFunction(formatted_raw_ostream &OS,
@@ -359,7 +463,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<InstructionEncoding> Encodings;
@@ -464,7 +568,8 @@ class FilterChooser {
// This returns a list of undecoded bits of an instructions, for example,
// Inst{20} = 1 && Inst{3-0} == 0b1111 represents two islands of yet-to-be
// decoded bits in order to verify that the instruction matches the Opcode.
- std::vector<Island> getIslands(const KnownBits &EncodingBits) const;
+ static std::vector<Island> getIslands(const KnownBits &EncodingBits,
+ const KnownBits &FilterBits);
/// Scans the well-known encoding bits of the encodings and, builds up a list
/// of candidate filters, and then returns the best one, if any.
@@ -483,50 +588,129 @@ class FilterChooser {
void dump() const;
};
-class DecoderTableBuilder {
+class DecoderTreeBuilder {
const CodeGenTarget &Target;
ArrayRef<InstructionEncoding> Encodings;
DecoderTableInfo &TableInfo;
public:
- DecoderTableBuilder(const CodeGenTarget &Target,
- ArrayRef<InstructionEncoding> Encodings,
- DecoderTableInfo &TableInfo)
+ DecoderTreeBuilder(const CodeGenTarget &Target,
+ ArrayRef<InstructionEncoding> 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);
+ std::unique_ptr<DecoderTreeNode> buildTree(const FilterChooser &FC) {
+ return buildAnyOfNode(FC);
}
private:
- void emitBinaryParser(raw_ostream &OS, indent Indent,
- const InstructionEncoding &Encoding,
- const OperandInfo &OpInfo) const;
+ static void emitBinaryParser(raw_ostream &OS, indent Indent,
+ const InstructionEncoding &Encoding,
+ const OperandInfo &OpInfo);
+ static void emitDecoder(raw_ostream &OS, indent Indent,
+ const InstructionEncoding &Encoding);
+ unsigned getDecoderIndex(const InstructionEncoding &Encoding);
+
+ static bool emitPredicateMatchAux(StringRef PredicateNamespace,
+ const Init &Val, bool ParenIfBinOp,
+ raw_ostream &OS);
+ static bool emitPredicateMatch(StringRef PredicateNamespace, raw_ostream &OS,
+ const InstructionEncoding &Encoding);
+ unsigned getPredicateIndex(const InstructionEncoding &Encoding) const;
+
+ std::unique_ptr<DecoderTreeNode>
+ buildTerminalNode(unsigned EncodingID, const KnownBits &FilterBits);
+
+ std::unique_ptr<DecoderTreeNode> buildAllOfOrSwitchNode(
+ unsigned StartBit, unsigned NumBits,
+ const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap);
+
+ std::unique_ptr<DecoderTreeNode> buildAnyOfNode(const FilterChooser &FC);
+};
+
+class DecoderTableEmitter {
+ const CodeGenTarget &Target;
+ ArrayRef<InstructionEncoding> Encodings;
+ formatted_raw_ostream OS;
+ unsigned IndexWidth;
+ unsigned CurrentIndex;
+ unsigned CommentIndex;
+ bool HasCheckPredicate = false;
+ bool HasSoftFail = false;
+ bool HasTryDecode = false;
+
+public:
+ DecoderTableEmitter(const CodeGenTarget &Target,
+ ArrayRef<InstructionEncoding> Encodings, raw_ostream &OS)
+ : Target(Target), Encodings(Encodings), OS(OS) {}
+
+ void emitTable(StringRef TableName, unsigned BitWidth,
+ const DecoderTreeNode *Root);
+
+ bool hasCheckPredicate() const { return HasCheckPredicate; }
+
+ bool hasSoftFail() const { return HasSoftFail; }
- void emitDecoder(raw_ostream &OS, indent Indent, unsigned EncodingID) const;
+ bool hasTryDecode() const { return HasTryDecode; }
- unsigned getDecoderIndex(unsigned EncodingID) const;
+private:
+ unsigned computeNodeSize(const DecoderTreeNode *N) const;
- unsigned getPredicateIndex(StringRef P) const;
+ unsigned computeTableSize(unsigned BitWidth,
+ const DecoderTreeNode *Root) const {
+ unsigned Size = 0;
+ if (SpecializeDecodersPerBitwidth)
+ Size = getULEB128Size(BitWidth);
+ Size += computeNodeSize(Root);
+ return Size;
+ }
- bool emitPredicateMatchAux(const Init &Val, bool ParenIfBinOp,
- raw_ostream &OS) const;
+ void emitStartLine() {
+ CommentIndex = CurrentIndex;
+ OS.indent(2);
+ }
- bool emitPredicateMatch(raw_ostream &OS, unsigned EncodingID) const;
+ void emitOpcode(StringRef Name) {
+ emitStartLine();
+ OS << "MCD::" << Name << ", ";
+ ++CurrentIndex;
+ }
- bool doesOpcodeNeedPredicate(unsigned EncodingID) const;
+ void emitByte(uint8_t Val) {
+ OS << static_cast<unsigned>(Val) << ", ";
+ ++CurrentIndex;
+ }
- void emitPredicateTableEntry(unsigned EncodingID) const;
+ void emitUInt8(unsigned Val) {
+ assert(isUInt<8>(Val));
+ emitByte(Val);
+ }
- void emitSoftFailTableEntry(unsigned EncodingID) const;
+ void emitULEB128(uint64_t Val) {
+ while (Val >= 0x80) {
+ emitByte((Val & 0x7F) | 0x80);
+ Val >>= 7;
+ }
+ emitByte(Val);
+ }
- void emitSingletonTableEntry(const FilterChooser &FC) const;
+ formatted_raw_ostream &emitComment(indent Indent) {
+ constexpr unsigned CommentColumn = 45;
+ if (OS.getColumn() > CommentColumn)
+ OS << '\n';
+ OS.PadToColumn(CommentColumn);
+ OS << "// " << format_decimal(CommentIndex, IndexWidth) << ": " << Indent;
+ return OS;
+ }
- void emitTableEntries(const FilterChooser &FC) const;
+ void emitAnyOfNode(const AnyOfNode *N, indent Indent);
+ void emitAllOfNode(const AllOfNode *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);
};
} // end anonymous namespace
@@ -599,204 +783,86 @@ unsigned Filter::usefulness() const {
return FilteredIDs.size() + VariableIDs.empty();
}
-//////////////////////////////////
-// //
-// Filterchooser Implementation //
-// //
-//////////////////////////////////
-
-// Emit the decoder state machine table. Returns a mask of MCD decoder ops
-// that were emitted.
-unsigned DecoderEmitter::emitTable(formatted_raw_ostream &OS,
- DecoderTable &Table, StringRef Namespace,
- unsigned HwModeID, unsigned BitWidth,
- ArrayRef<unsigned> 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<unsigned, unsigned> 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 << "[" << 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;
- };
+static bool doesOpcodeNeedPredicate(const InstructionEncoding &Encoding);
- // FIXME: We may be able to use the NumToSkip values to recover
- // appropriate indentation levels.
- DecoderTable::const_iterator I = Table.begin();
- DecoderTable::const_iterator E = Table.end();
- const uint8_t *const EndPtr = Table.data() + Table.size();
-
- auto emitNumToSkipComment = [&](uint32_t NumToSkip, bool InComment = false) {
- uint32_t Index = ((I - Table.begin()) + NumToSkip);
- OS << (InComment ? ", " : "// ");
- OS << "Skip to: " << Index;
- };
+std::unique_ptr<DecoderTreeNode>
+DecoderTreeBuilder::buildTerminalNode(unsigned EncodingID,
+ const KnownBits &FilterBits) {
+ const InstructionEncoding &Encoding = Encodings[EncodingID];
+ auto N = std::make_unique<AllOfNode>();
- // The first entry when specializing decoders per bitwidth is the bitwidth.
- // This will be used for additional checks in `decodeInstruction`.
- if (SpecializeDecodersPerBitwidth) {
- OS << "/* 0 */";
- OS.PadToColumn(14);
- emitULEB128(I, OS);
- OS << " // Bitwidth " << BitWidth << '\n';
+ if (doesOpcodeNeedPredicate(Encoding)) {
+ unsigned PredicateIndex = getPredicateIndex(Encoding);
+ N->addChild(std::make_unique<CheckPredicateNode>(PredicateIndex));
}
- unsigned OpcodeMask = 0;
+ std::vector<FilterChooser::Island> Islands =
+ FilterChooser::getIslands(Encoding.getMandatoryBits(), FilterBits);
+ for (const FilterChooser::Island &Ilnd : reverse(Islands)) {
+ N->addChild(std::make_unique<CheckFieldNode>(Ilnd.StartBit, Ilnd.NumBits,
+ Ilnd.FieldVal));
+ }
- while (I != E) {
- assert(I < E && "incomplete decode table entry!");
+ 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<SoftFailNode>(PositiveMask.getZExtValue(),
+ NegativeMask.getZExtValue()));
+ }
- uint64_t Pos = I - Table.begin();
- OS << "/* " << Pos << " */";
- OS.PadToColumn(12);
+ unsigned DecoderIndex = getDecoderIndex(Encoding);
+ N->addChild(std::make_unique<DecodeNode>(EncodingID, DecoderIndex));
- const uint8_t DecoderOp = *I++;
- OpcodeMask |= (1 << DecoderOp);
- switch (DecoderOp) {
- default:
- PrintFatalError("Invalid decode table opcode: " + Twine((int)DecoderOp) +
- " at index " + Twine(Pos));
- case MCD::OPC_Scope: {
- OS << " MCD::OPC_Scope, ";
- uint32_t NumToSkip = emitNumToSkip(I, OS);
- emitNumToSkipComment(NumToSkip);
- OS << '\n';
- break;
- }
- case MCD::OPC_ExtractField: {
- OS << " MCD::OPC_ExtractField, ";
-
- // ULEB128 encoded start value.
- const char *ErrMsg = nullptr;
- unsigned Start = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg);
- assert(ErrMsg == nullptr && "ULEB128 value too large!");
- emitULEB128(I, OS);
-
- unsigned Len = *I++;
- OS << Len << ", // Inst{";
- if (Len > 1)
- OS << (Start + Len - 1) << "-";
- OS << Start << "} ...\n";
- break;
- }
- case MCD::OPC_FilterValueOrSkip: {
- OS << " MCD::OPC_FilterValueOrSkip, ";
- // The filter value is ULEB128 encoded.
- emitULEB128(I, OS);
- uint32_t NumToSkip = emitNumToSkip(I, OS);
- emitNumToSkipComment(NumToSkip);
- OS << '\n';
- break;
- }
- case MCD::OPC_FilterValue: {
- OS << " MCD::OPC_FilterValue, ";
- // The filter value is ULEB128 encoded.
- emitULEB128(I, OS);
- OS << '\n';
- break;
- }
- case MCD::OPC_CheckField: {
- OS << " MCD::OPC_CheckField, ";
- // ULEB128 encoded start value.
- emitULEB128(I, OS);
- // 8-bit length.
- unsigned Len = *I++;
- OS << Len << ", ";
- // ULEB128 encoded field value.
- emitULEB128(I, OS);
- OS << '\n';
- break;
- }
- case MCD::OPC_CheckPredicate: {
- OS << " MCD::OPC_CheckPredicate, ";
- emitULEB128(I, OS);
- OS << '\n';
- break;
- }
- case MCD::OPC_Decode:
- case MCD::OPC_TryDecode: {
- bool IsTry = DecoderOp == MCD::OPC_TryDecode;
- // Decode the Opcode value.
- const char *ErrMsg = nullptr;
- unsigned Opc = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg);
- assert(ErrMsg == nullptr && "ULEB128 value too large!");
+ return N;
+}
- OS << " MCD::OPC_" << (IsTry ? "Try" : "") << "Decode, ";
- emitULEB128(I, OS);
+std::unique_ptr<DecoderTreeNode> DecoderTreeBuilder::buildAllOfOrSwitchNode(
+ unsigned int StartBit, unsigned int NumBits,
+ const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap) {
+ if (FCMap.size() == 1) {
+ const auto &[FieldVal, ChildFC] = *FCMap.begin();
+ auto N = std::make_unique<AllOfNode>();
+ N->addChild(std::make_unique<CheckFieldNode>(StartBit, NumBits, FieldVal));
+ N->addChild(buildAnyOfNode(*ChildFC));
+ return N;
+ }
+ auto N = std::make_unique<SwitchFieldNode>(StartBit, NumBits);
+ for (const auto &[FieldVal, ChildFC] : FCMap)
+ N->addCase(FieldVal, buildAnyOfNode(*ChildFC));
+ return N;
+}
- // Decoder index.
- unsigned DecodeIdx = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg);
- assert(ErrMsg == nullptr && "ULEB128 value too large!");
- emitULEB128(I, OS);
+std::unique_ptr<DecoderTreeNode>
+DecoderTreeBuilder::buildAnyOfNode(const FilterChooser &FC) {
+ auto N = std::make_unique<AnyOfNode>();
+ if (FC.SingletonEncodingID) {
+ N->addChild(buildTerminalNode(*FC.SingletonEncodingID, FC.FilterBits));
+ } else {
+ N->addChild(
+ buildAllOfOrSwitchNode(FC.StartBit, FC.NumBits, FC.FilterChooserMap));
+ }
+ if (FC.VariableFC) {
+ N->addChild(buildAnyOfNode(*FC.VariableFC));
+ }
- auto EncI = OpcodeToEncodingID.find(Opc);
- assert(EncI != OpcodeToEncodingID.end() && "no encoding entry");
- auto EncodingID = EncI->second;
+ return N;
+}
- if (!IsTry) {
- OS << "// Opcode: " << Encodings[EncodingID].getName()
- << ", DecodeIdx: " << DecodeIdx << '\n';
- break;
- }
- OS << '\n';
- break;
- }
- case MCD::OPC_SoftFail: {
- OS << " MCD::OPC_SoftFail, ";
- // Decode the positive mask.
- const char *ErrMsg = nullptr;
- uint64_t PositiveMask = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg);
- assert(ErrMsg == nullptr && "ULEB128 value too large!");
- emitULEB128(I, OS);
-
- // Decode the negative mask.
- uint64_t NegativeMask = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg);
- assert(ErrMsg == nullptr && "ULEB128 value too large!");
- emitULEB128(I, OS);
- OS << "// +ve mask: 0x";
- OS.write_hex(PositiveMask);
- OS << ", -ve mask: 0x";
- OS.write_hex(NegativeMask);
- OS << '\n';
- break;
- }
- }
- }
- OS << "};\n\n";
+// Emit the decoder state machine table.
+void DecoderEmitter::emitDecoderTable(formatted_raw_ostream &OS,
+ const DecoderTreeNode *Tree,
+ StringRef Namespace, unsigned HwModeID,
+ unsigned BitWidth) const {
+ SmallString<32> TableName("DecoderTable");
+ TableName.append(Namespace);
+ if (HwModeID != DefaultMode)
+ TableName.append({"_", Target.getHwModes().getModeName(HwModeID)});
+ TableName.append(std::to_string(BitWidth));
- return OpcodeMask;
+ DecoderTableEmitter TableEmitter(Target, Encodings, OS);
+ TableEmitter.emitTable(TableName, BitWidth, Tree);
}
void DecoderEmitter::emitInstrLenTable(formatted_raw_ostream &OS,
@@ -926,7 +992,8 @@ void FilterChooser::dumpStack(raw_ostream &OS, indent Indent,
// Inst{20} = 1 && Inst{3-0} == 0b1111 represents two islands of yet-to-be
// decoded bits in order to verify that the instruction matches the Opcode.
std::vector<FilterChooser::Island>
-FilterChooser::getIslands(const KnownBits &EncodingBits) const {
+FilterChooser::getIslands(const KnownBits &EncodingBits,
+ const KnownBits &FilterBits) {
std::vector<Island> Islands;
uint64_t FieldVal;
unsigned StartBit;
@@ -939,7 +1006,7 @@ FilterChooser::getIslands(const KnownBits &EncodingBits) const {
unsigned FilterWidth = FilterBits.getBitWidth();
for (unsigned i = 0; i != FilterWidth; ++i) {
bool IsKnown = EncodingBits.Zero[i] || EncodingBits.One[i];
- bool Filtered = isPositionFiltered(i);
+ bool Filtered = FilterBits.Zero[i] || FilterBits.One[i];
switch (State) {
default:
llvm_unreachable("Unreachable code!");
@@ -972,9 +1039,9 @@ FilterChooser::getIslands(const KnownBits &EncodingBits) const {
return Islands;
}
-void DecoderTableBuilder::emitBinaryParser(raw_ostream &OS, indent Indent,
- const InstructionEncoding &Encoding,
- const OperandInfo &OpInfo) const {
+void DecoderTreeBuilder::emitBinaryParser(raw_ostream &OS, indent Indent,
+ const InstructionEncoding &Encoding,
+ const OperandInfo &OpInfo) {
if (OpInfo.HasNoEncoding) {
// If an operand has no encoding, the old behavior is to not decode it
// automatically and let the target do it. This is error-prone, so the
@@ -984,7 +1051,6 @@ void DecoderTableBuilder::emitBinaryParser(raw_ostream &OS, indent Indent,
"could not find field for operand '" + OpInfo.Name + "'");
return;
}
-
// Special case for 'bits<0>'.
if (OpInfo.Fields.empty() && !OpInfo.InitValue) {
if (IgnoreNonDecodableOperands)
@@ -1034,10 +1100,8 @@ void DecoderTableBuilder::emitBinaryParser(raw_ostream &OS, indent Indent,
}
}
-void DecoderTableBuilder::emitDecoder(raw_ostream &OS, indent Indent,
- unsigned EncodingID) const {
- const InstructionEncoding &Encoding = Encodings[EncodingID];
-
+void DecoderTreeBuilder::emitDecoder(raw_ostream &OS, indent Indent,
+ const InstructionEncoding &Encoding) {
// If a custom instruction decoder was specified, use that.
StringRef DecoderMethod = Encoding.getDecoderMethod();
if (!DecoderMethod.empty()) {
@@ -1052,14 +1116,15 @@ void DecoderTableBuilder::emitDecoder(raw_ostream &OS, indent Indent,
emitBinaryParser(OS, Indent, Encoding, Op);
}
-unsigned DecoderTableBuilder::getDecoderIndex(unsigned EncodingID) const {
+unsigned
+DecoderTreeBuilder::getDecoderIndex(const InstructionEncoding &Encoding) {
// Build up the predicate string.
SmallString<256> Decoder;
// FIXME: emitDecoder() function can take a buffer directly rather than
// a stream.
raw_svector_ostream S(Decoder);
indent Indent(UseFnTableInDecodeToMCInst ? 2 : 4);
- emitDecoder(S, Indent, EncodingID);
+ emitDecoder(S, Indent, Encoding);
// Using the full decoder string as the key value here is a bit
// heavyweight, but is effective. If the string comparisons become a
@@ -1076,20 +1141,21 @@ unsigned DecoderTableBuilder::getDecoderIndex(unsigned EncodingID) const {
}
// If ParenIfBinOp is true, print a surrounding () if Val uses && or ||.
-bool DecoderTableBuilder::emitPredicateMatchAux(const Init &Val,
- bool ParenIfBinOp,
- raw_ostream &OS) const {
+bool DecoderTreeBuilder::emitPredicateMatchAux(StringRef PredicateNamespace,
+ const Init &Val,
+ bool ParenIfBinOp,
+ raw_ostream &OS) {
if (const auto *D = dyn_cast<DefInit>(&Val)) {
if (!D->getDef()->isSubClassOf("SubtargetFeature"))
return true;
- OS << "Bits[" << Target.getName() << "::" << D->getAsString() << "]";
+ OS << "Bits[" << PredicateNamespace << "::" << D->getAsString() << "]";
return false;
}
if (const auto *D = dyn_cast<DagInit>(&Val)) {
std::string Op = D->getOperator()->getAsString();
if (Op == "not" && D->getNumArgs() == 1) {
OS << '!';
- return emitPredicateMatchAux(*D->getArg(0), true, OS);
+ return emitPredicateMatchAux(PredicateNamespace, *D->getArg(0), true, OS);
}
if ((Op == "any_of" || Op == "all_of") && D->getNumArgs() > 0) {
bool Paren = D->getNumArgs() > 1 && std::exchange(ParenIfBinOp, true);
@@ -1098,7 +1164,7 @@ bool DecoderTableBuilder::emitPredicateMatchAux(const Init &Val,
ListSeparator LS(Op == "any_of" ? " || " : " && ");
for (auto *Arg : D->getArgs()) {
OS << LS;
- if (emitPredicateMatchAux(*Arg, ParenIfBinOp, OS))
+ if (emitPredicateMatchAux(PredicateNamespace, *Arg, ParenIfBinOp, OS))
return true;
}
if (Paren)
@@ -1109,10 +1175,11 @@ bool DecoderTableBuilder::emitPredicateMatchAux(const Init &Val,
return true;
}
-bool DecoderTableBuilder::emitPredicateMatch(raw_ostream &OS,
- unsigned EncodingID) const {
+bool DecoderTreeBuilder::emitPredicateMatch(
+ StringRef PredicateNamespace, raw_ostream &OS,
+ const InstructionEncoding &Encoding) {
const ListInit *Predicates =
- Encodings[EncodingID].getRecord()->getValueAsListInit("Predicates");
+ Encoding.getRecord()->getValueAsListInit("Predicates");
bool IsFirstEmission = true;
for (unsigned i = 0; i < Predicates->size(); ++i) {
const Record *Pred = Predicates->getElementAsRecord(i);
@@ -1124,7 +1191,8 @@ bool DecoderTableBuilder::emitPredicateMatch(raw_ostream &OS,
if (!IsFirstEmission)
OS << " && ";
- if (emitPredicateMatchAux(*Pred->getValueAsDag("AssemblerCondDag"),
+ if (emitPredicateMatchAux(PredicateNamespace,
+ *Pred->getValueAsDag("AssemblerCondDag"),
Predicates->size() > 1, OS))
PrintFatalError(Pred->getLoc(), "Invalid AssemblerCondDag!");
IsFirstEmission = false;
@@ -1132,9 +1200,9 @@ bool DecoderTableBuilder::emitPredicateMatch(raw_ostream &OS,
return !Predicates->empty();
}
-bool DecoderTableBuilder::doesOpcodeNeedPredicate(unsigned EncodingID) const {
+static bool doesOpcodeNeedPredicate(const InstructionEncoding &Encoding) {
const ListInit *Predicates =
- Encodings[EncodingID].getRecord()->getValueAsListInit("Predicates");
+ Encoding.getRecord()->getValueAsListInit("Predicates");
for (unsigned i = 0; i < Predicates->size(); ++i) {
const Record *Pred = Predicates->getElementAsRecord(i);
if (!Pred->getValue("AssemblerMatcherPredicate"))
@@ -1146,7 +1214,15 @@ bool DecoderTableBuilder::doesOpcodeNeedPredicate(unsigned EncodingID) const {
return false;
}
-unsigned DecoderTableBuilder::getPredicateIndex(StringRef Predicate) const {
+unsigned DecoderTreeBuilder::getPredicateIndex(
+ const InstructionEncoding &Encoding) const {
+ // Build up the predicate string.
+ SmallString<256> Predicate;
+ // FIXME: emitPredicateMatch() functions can take a buffer directly rather
+ // than a stream.
+ raw_svector_ostream PS(Predicate);
+ emitPredicateMatch(Target.getName(), PS, Encoding);
+
// 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
@@ -1154,88 +1230,11 @@ unsigned DecoderTableBuilder::getPredicateIndex(StringRef Predicate) const {
// overkill for now, though.
// Make sure the predicate is in the table.
- TableInfo.Predicates.insert(CachedHashString(Predicate));
+ PredicateSet &Predicates = TableInfo.Predicates;
+ Predicates.insert(CachedHashString(Predicate));
// Now figure out the index for when we write out the table.
- PredicateSet::const_iterator P = find(TableInfo.Predicates, Predicate);
- return (unsigned)(P - TableInfo.Predicates.begin());
-}
-
-void DecoderTableBuilder::emitPredicateTableEntry(unsigned EncodingID) const {
- if (!doesOpcodeNeedPredicate(EncodingID))
- return;
-
- // Build up the predicate string.
- SmallString<256> Predicate;
- // FIXME: emitPredicateMatch() functions can take a buffer directly rather
- // than a stream.
- raw_svector_ostream PS(Predicate);
- emitPredicateMatch(PS, EncodingID);
-
- // Figure out the index into the predicate table for the predicate just
- // computed.
- unsigned PIdx = getPredicateIndex(PS.str());
-
- TableInfo.Table.insertOpcode(MCD::OPC_CheckPredicate);
- TableInfo.Table.insertULEB128(PIdx);
-}
-
-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(MCD::OPC_SoftFail);
- TableInfo.Table.insertULEB128(PositiveMask.getZExtValue());
- TableInfo.Table.insertULEB128(NegativeMask.getZExtValue());
-}
-
-// 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<FilterChooser::Island> Islands = FC.getIslands(EncodingBits);
-
- // Emit the predicate table entry if one is needed.
- emitPredicateTableEntry(EncodingID);
-
- // Check any additional encoding fields needed.
- for (const FilterChooser::Island &Ilnd : reverse(Islands)) {
- TableInfo.Table.insertOpcode(MCD::OPC_CheckField);
- TableInfo.Table.insertULEB128(Ilnd.StartBit);
- TableInfo.Table.insertUInt8(Ilnd.NumBits);
- TableInfo.Table.insertULEB128(Ilnd.FieldVal);
- }
-
- // Check for soft failure of the match.
- emitSoftFailTableEntry(EncodingID);
-
- unsigned DIdx = getDecoderIndex(EncodingID);
-
- // Produce OPC_Decode or OPC_TryDecode opcode based on the information
- // whether the instruction decoder is complete or not. If it is complete
- // then it handles all possible values of remaining variable/unfiltered bits
- // and for any value can determine if the bitpattern is a valid instruction
- // or not. This means OPC_Decode will be the final step in the decoding
- // process. If it is not complete, then the Fail return code from the
- // decoder method indicates that additional processing should be done to see
- // if there is any other instruction that also matches the bitpattern and
- // 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));
- TableInfo.Table.insertULEB128(DIdx);
+ PredicateSet::const_iterator P = find(Predicates, Predicate);
+ return std::distance(Predicates.begin(), P);
}
std::unique_ptr<Filter>
@@ -1253,7 +1252,7 @@ FilterChooser::findBestFilter(ArrayRef<bitAttr_t> BitAttrs, bool AllowMixed,
KnownBits EncodingBits = Encoding.getMandatoryBits();
// Look for islands of undecoded bits of any instruction.
- std::vector<Island> Islands = getIslands(EncodingBits);
+ std::vector<Island> Islands = getIslands(EncodingBits, FilterBits);
if (!Islands.empty()) {
// Found an instruction with island(s). Now just assign a filter.
return std::make_unique<Filter>(
@@ -1513,87 +1512,235 @@ 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(MCD::OPC_Scope);
- FixupLoc = Table.insertNumToSkip();
+unsigned DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *N) const {
+ switch (N->getKind()) {
+ case DecoderTreeNode::AnyOf: {
+ const auto *CheckAny = static_cast<const AnyOfNode *>(N);
+ unsigned Size = 0;
+ for (const DecoderTreeNode *Child : drop_end(CheckAny->children())) {
+ unsigned ChildSize = computeNodeSize(Child);
+ Size += 1 + getULEB128Size(ChildSize) + ChildSize;
+ }
+ return Size + computeNodeSize(*std::prev(CheckAny->child_end()));
+ }
+ case DecoderTreeNode::AllOf: {
+ const auto *CheckAll = static_cast<const AllOfNode *>(N);
+ unsigned Size = 0;
+ for (const DecoderTreeNode *Child : CheckAll->children())
+ Size += computeNodeSize(Child);
+ return Size;
+ }
+ case DecoderTreeNode::CheckField: {
+ const auto *CheckField = static_cast<const CheckFieldNode *>(N);
+ return 1 + getULEB128Size(CheckField->getStartBit()) + 1 +
+ getULEB128Size(CheckField->getValue());
}
+ case DecoderTreeNode::SwitchField: {
+ const auto *SwitchN = static_cast<const SwitchFieldNode *>(N);
+ unsigned Size = 1 + getULEB128Size(SwitchN->getStartBit()) + 1;
- 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(MCD::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(MCD::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(MCD::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());
+ for (auto [Val, Child] : drop_end(SwitchN->cases())) {
+ unsigned ChildSize = computeNodeSize(Child);
+ Size += getULEB128Size(Val) + getULEB128Size(ChildSize) + ChildSize;
}
- // 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(MCD::OPC_FilterValue);
- Table.insertULEB128(FilterVal);
+ auto [Val, Child] = *std::prev(SwitchN->case_end());
+ unsigned ChildSize = computeNodeSize(Child);
+ Size += getULEB128Size(Val) + getULEB128Size(0) + ChildSize;
+ return Size;
+ }
+ case DecoderTreeNode::CheckPredicate: {
+ const auto *CheckPredicate = static_cast<const CheckPredicateNode *>(N);
+ return 1 + getULEB128Size(CheckPredicate->getPredicateIndex());
+ }
+ case DecoderTreeNode::SoftFail: {
+ const auto *SoftFail = static_cast<const SoftFailNode *>(N);
+ return 1 + getULEB128Size(SoftFail->getPositiveMask()) +
+ getULEB128Size(SoftFail->getNegativeMask());
+ }
+ case DecoderTreeNode::Decode: {
+ const auto *Decode = static_cast<const DecodeNode *>(N);
+ const InstructionEncoding &Encoding = Encodings[Decode->getEncodingID()];
+ const Record *InstDef = Encoding.getInstruction()->TheDef;
+ unsigned InstOpcode = Target.getInstrIntValue(InstDef);
+ return 1 + getULEB128Size(InstOpcode) +
+ getULEB128Size(Decode->getDecoderIndex());
+ }
+ }
+ llvm_unreachable("Unknown node kind");
+}
- // Emit table entries for the last case.
- emitTableEntries(*Delegate);
+void DecoderTableEmitter::emitAnyOfNode(const AnyOfNode *N, indent Indent) {
+ for (const DecoderTreeNode *Child : drop_end(N->children())) {
+ emitOpcode("OPC_Scope");
+ emitULEB128(computeNodeSize(Child));
+
+ emitComment(Indent) << "{\n";
+ emitNode(Child, Indent + 1);
+ emitComment(Indent) << "}\n";
}
- if (FC.VariableFC) {
- Table.patchNumToSkip(FixupLoc, Table.size());
- emitTableEntries(*FC.VariableFC);
+ const DecoderTreeNode *Child = *std::prev(N->child_end());
+ emitNode(Child, Indent);
+}
+
+void DecoderTableEmitter::emitAllOfNode(const AllOfNode *N, indent Indent) {
+ 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[" << 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";
}
+
+ 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[" << 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);
+ OS << "check Inst[" << MSB << ':' << LSB << "] == " << format_hex(Val, 0)
+ << '\n';
+}
+
+void DecoderTableEmitter::emitCheckPredicateNode(const CheckPredicateNode *N,
+ indent Indent) {
+ unsigned PredicateIndex = N->getPredicateIndex();
+
+ emitOpcode("OPC_CheckPredicate");
+ emitULEB128(PredicateIndex);
+
+ 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);
+
+ emitComment(Indent) << "check softfail";
+ OS << " pos=" << format_hex(PositiveMask, 10);
+ OS << " neg=" << format_hex(NegativeMask, 10) << '\n';
+}
+
+void DecoderTableEmitter::emitDecodeNode(const DecodeNode *N, indent Indent) {
+ const InstructionEncoding &Encoding = Encodings[N->getEncodingID()];
+ const Record *InstDef = Encoding.getInstruction()->TheDef;
+ unsigned InstOpcode = Target.getInstrIntValue(InstDef);
+ unsigned DecoderIndex = N->getDecoderIndex();
+
+ emitOpcode(Encoding.hasCompleteDecoder() ? "OPC_Decode" : "OPC_TryDecode");
+ emitULEB128(InstOpcode);
+ emitULEB128(DecoderIndex);
+
+ emitComment(Indent);
+ if (!Encoding.hasCompleteDecoder())
+ OS << "try ";
+ OS << "decode to " << Encoding.getName() << " using decoder " << DecoderIndex
+ << '\n';
+}
+
+void DecoderTableEmitter::emitNode(const DecoderTreeNode *N, indent Indent) {
+ switch (N->getKind()) {
+ case DecoderTreeNode::AnyOf:
+ return emitAnyOfNode(static_cast<const AnyOfNode *>(N), Indent);
+ case DecoderTreeNode::AllOf:
+ return emitAllOfNode(static_cast<const AllOfNode *>(N), Indent);
+ case DecoderTreeNode::SwitchField:
+ return emitSwitchFieldNode(static_cast<const SwitchFieldNode *>(N), Indent);
+ case DecoderTreeNode::CheckField:
+ return emitCheckFieldNode(static_cast<const CheckFieldNode *>(N), Indent);
+ case DecoderTreeNode::CheckPredicate:
+ HasCheckPredicate = true;
+ return emitCheckPredicateNode(static_cast<const CheckPredicateNode *>(N),
+ Indent);
+ case DecoderTreeNode::SoftFail:
+ HasSoftFail = true;
+ return emitSoftFailNode(static_cast<const SoftFailNode *>(N), Indent);
+ case DecoderTreeNode::Decode:
+ HasTryDecode |=
+ Encodings[static_cast<const DecodeNode *>(N)->getEncodingID()]
+ .hasCompleteDecoder();
+ return emitDecodeNode(static_cast<const DecodeNode *>(N), Indent);
+ }
+ llvm_unreachable("Unknown node kind");
+}
+
+void DecoderTableEmitter::emitTable(StringRef TableName, unsigned BitWidth,
+ const DecoderTreeNode *Root) {
+ unsigned TableSize = computeTableSize(BitWidth, Root);
+ 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 (SpecializeDecodersPerBitwidth) {
+ 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";
}
// emitDecodeInstruction - Emit the templated helper function
// decodeInstruction().
static void emitDecodeInstruction(formatted_raw_ostream &OS, bool IsVarLenInst,
- unsigned OpcodeMask) {
- const bool HasTryDecode = OpcodeMask & (1 << MCD::OPC_TryDecode);
- const bool HasCheckPredicate = OpcodeMask & (1 << MCD::OPC_CheckPredicate);
- const bool HasSoftFail = OpcodeMask & (1 << MCD::OPC_SoftFail);
-
+ bool HasCheckPredicate, bool HasSoftFail,
+ bool HasTryDecode) {
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 <typename InsnType>
static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
InsnType insn, uint64_t Address,
@@ -1621,7 +1768,6 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
OS << R"(
SmallVector<const uint8_t *, 8> ScopeStack;
- uint64_t CurFieldValue = 0;
DecodeStatus S = MCDisassembler::Success;
while (true) {
ptrdiff_t Loc = Ptr - DecodeTable;
@@ -1632,51 +1778,34 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
<< (int)DecoderOp << '\n';
return MCDisassembler::Fail;
case MCD::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");
break;
}
- case MCD::OPC_ExtractField: {
+ case MCD::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");
- break;
- }
- case MCD::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) {
- Ptr = SkipTo;
- LLVM_DEBUG(dbgs() << "continuing at " << Ptr - DecodeTable << '\n');
+ uint64_t FieldVal = fieldFromInstruction(insn, Start, Len);
+ uint64_t CaseVal;
+ unsigned CaseSize;
+ while (true) {
+ CaseVal = decodeULEB128AndIncUnsafe(Ptr);
+ CaseSize = decodeULEB128AndIncUnsafe(Ptr);
+ if (FieldVal == CaseVal || !CaseSize)
+ break;
+ Ptr += CaseSize;
}
- break;
- }
- case MCD::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) {
+ if (FieldVal == CaseVal) {
+ LLVM_DEBUG(dbgs() << Loc << ": OPC_SwitchField(" << Start << ", " << Len
+ << "): " << FieldVal << '\n');
+ } else {
if (ScopeStack.empty()) {
LLVM_DEBUG(dbgs() << "returning Fail\n");
return MCDisassembler::Fail;
@@ -2052,9 +2181,12 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
// 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);
- unsigned OpcodeMask = 0;
+ DecoderTreeBuilder TreeBuilder(Target, Encodings, TableInfo);
+ DecoderTableEmitter TableEmitter(Target, Encodings, OS);
+ // Emit a table for each (namespace, hwmode, width) combination.
+ // If `SpecializeDecodersPerBitwidth` is enabled, emit a decoder function
+ // for each table.
bool HasConflict = false;
for (const auto &[BitWidth, BWMap] : EncMap) {
for (const auto &[Key, EncodingIDs] : BWMap) {
@@ -2067,19 +2199,16 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
if (HasConflict)
continue;
- // 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.
- OpcodeMask |= emitTable(OS, TableInfo.Table, DecoderNamespace, HwModeID,
- BitWidth, EncodingIDs);
+ std::unique_ptr<DecoderTreeNode> Root = TreeBuilder.buildTree(FC);
+
+ SmallString<32> TableName("DecoderTable");
+ TableName.append(DecoderNamespace);
+ if (HwModeID != DefaultMode)
+ TableName.append({"_", Target.getHwModes().getModeName(HwModeID)});
+ TableName.append(std::to_string(BitWidth));
+
+ // Serialize the tree.
+ TableEmitter.emitTable(TableName, BitWidth, Root.get());
}
// Each BitWidth get's its own decoders and decoder function if
@@ -2098,14 +2227,14 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
if (!SpecializeDecodersPerBitwidth)
emitDecoderFunction(OS, TableInfo.Decoders, 0);
- const bool HasCheckPredicate = OpcodeMask & (1 << MCD::OPC_CheckPredicate);
-
// Emit the predicate function.
- if (HasCheckPredicate)
+ if (TableEmitter.hasCheckPredicate())
emitPredicateFunction(OS, TableInfo.Predicates);
// Emit the main entry point for the decoder, decodeInstruction().
- emitDecodeInstruction(OS, IsVarLenInst, OpcodeMask);
+ emitDecodeInstruction(OS, IsVarLenInst, TableEmitter.hasCheckPredicate(),
+ TableEmitter.hasSoftFail(),
+ TableEmitter.hasTryDecode());
OS << "\n} // namespace\n";
}
>From 3202c5887b3b52285f4bad324f65f00b7625f97b Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Mon, 15 Sep 2025 22:52:04 +0300
Subject: [PATCH 2/3] tmp2
---
llvm/utils/TableGen/CMakeLists.txt | 3 +
llvm/utils/TableGen/DecoderEmitter.cpp | 1507 +-------------------
llvm/utils/TableGen/DecoderTree.cpp | 11 +
llvm/utils/TableGen/DecoderTree.h | 224 +++
llvm/utils/TableGen/DecoderTreeBuilder.cpp | 988 +++++++++++++
llvm/utils/TableGen/DecoderTreeEmitter.cpp | 335 +++++
6 files changed, 1580 insertions(+), 1488 deletions(-)
create mode 100644 llvm/utils/TableGen/DecoderTree.cpp
create mode 100644 llvm/utils/TableGen/DecoderTree.h
create mode 100644 llvm/utils/TableGen/DecoderTreeBuilder.cpp
create mode 100644 llvm/utils/TableGen/DecoderTreeEmitter.cpp
diff --git a/llvm/utils/TableGen/CMakeLists.txt b/llvm/utils/TableGen/CMakeLists.txt
index 67291214c14e6..ec071ab823d23 100644
--- a/llvm/utils/TableGen/CMakeLists.txt
+++ b/llvm/utils/TableGen/CMakeLists.txt
@@ -43,6 +43,9 @@ add_tablegen(llvm-tblgen LLVM
DAGISelMatcherGen.cpp
DAGISelMatcherOpt.cpp
DecoderEmitter.cpp
+ DecoderTree.cpp
+ DecoderTreeBuilder.cpp
+ DecoderTreeEmitter.cpp
DFAEmitter.cpp
DFAPacketizerEmitter.cpp
DisassemblerEmitter.cpp
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 9780e29435d84..8a9e094d85cb9 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -17,34 +17,24 @@
#include "Common/InfoByHwMode.h"
#include "Common/InstructionEncoding.h"
#include "Common/VarLenCodeEmitterGen.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/SmallString.h"
#include "llvm/ADT/Statistic.h"
-#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#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"
#include "llvm/TableGen/Record.h"
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <cstdint>
#include <map>
#include <memory>
#include <set>
@@ -80,7 +70,8 @@ static cl::opt<SuppressLevel> DecoderEmitterSuppressDuplicates(
"significantly reducing Table Duplications")),
cl::init(SUPPRESSION_DISABLE), cl::cat(DisassemblerEmitterCat));
-static cl::opt<bool> UseFnTableInDecodeToMCInst(
+namespace llvm {
+cl::opt<bool> UseFnTableInDecodeToMCInst(
"use-fn-table-in-decode-to-mcinst",
cl::desc(
"Use a table of function pointers instead of a switch case in the\n"
@@ -92,23 +83,24 @@ static cl::opt<bool> UseFnTableInDecodeToMCInst(
// bitwidths and defining `InsnBitWidth` template specialization for the
// `InsnType` types used. Some common specializations are already defined in
// MCDecoder.h.
-static cl::opt<bool> SpecializeDecodersPerBitwidth(
+cl::opt<bool> SpecializeDecodersPerBitwidth(
"specialize-decoders-per-bitwidth",
cl::desc("Specialize the generated `decodeToMCInst` function per bitwidth. "
"Helps reduce the code size."),
cl::init(false), cl::cat(DisassemblerEmitterCat));
-static cl::opt<bool> IgnoreNonDecodableOperands(
+cl::opt<bool> IgnoreNonDecodableOperands(
"ignore-non-decodable-operands",
cl::desc(
"Do not issue an error if an operand cannot be decoded automatically."),
cl::init(false), cl::cat(DisassemblerEmitterCat));
-static cl::opt<bool> IgnoreFullyDefinedOperands(
+cl::opt<bool> IgnoreFullyDefinedOperands(
"ignore-fully-defined-operands",
cl::desc(
"Do not automatically decode operands with no '?' in their encoding."),
cl::init(false), cl::cat(DisassemblerEmitterCat));
+} // namespace llvm
STATISTIC(NumEncodings, "Number of encodings considered");
STATISTIC(NumEncodingsLackingDisasm,
@@ -117,220 +109,8 @@ STATISTIC(NumInstructions, "Number of instructions considered");
STATISTIC(NumEncodingsSupported, "Number of encodings supported");
STATISTIC(NumEncodingsOmitted, "Number of encodings omitted");
-/// 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,
- char Unknown) {
- for (unsigned I = Bits.getBitWidth(); I--;) {
- if (Bits.Zero[I] && Bits.One[I])
- OS << '!';
- else if (Bits.Zero[I])
- OS << '0';
- else if (Bits.One[I])
- OS << '1';
- else
- OS << Unknown;
- }
-}
-
namespace {
-/// Sorting predicate to sort encoding IDs by encoding width.
-class LessEncodingIDByWidth {
- ArrayRef<InstructionEncoding> Encodings;
-
-public:
- explicit LessEncodingIDByWidth(ArrayRef<InstructionEncoding> Encodings)
- : Encodings(Encodings) {}
-
- bool operator()(unsigned ID1, unsigned ID2) const {
- return Encodings[ID1].getBitWidth() < Encodings[ID2].getBitWidth();
- }
-};
-
-class DecoderTreeNode {
-public:
- virtual ~DecoderTreeNode() = default;
-
- enum KindTy {
- AnyOf,
- AllOf,
- CheckField,
- SwitchField,
- CheckPredicate,
- SoftFail,
- Decode,
- };
-
- KindTy getKind() const { return Kind; }
-
-protected:
- explicit DecoderTreeNode(KindTy Kind) : Kind(Kind) {}
-
-private:
- KindTy Kind;
-};
-
-class AllOfNode : public DecoderTreeNode {
- SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
-
- static const DecoderTreeNode *
- mapElement(decltype(Children)::const_reference Element) {
- return Element.get();
- }
-
-public:
- AllOfNode() : DecoderTreeNode(AllOf) {}
-
- void addChild(std::unique_ptr<DecoderTreeNode> Child) {
- Children.push_back(std::move(Child));
- }
-
- using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
- decltype(&mapElement)>;
-
- child_iterator child_begin() const {
- return child_iterator(Children.begin(), mapElement);
- }
-
- child_iterator child_end() const {
- return child_iterator(Children.end(), mapElement);
- }
-
- iterator_range<child_iterator> children() const {
- return make_range(child_begin(), child_end());
- }
-};
-
-class AnyOfNode : public DecoderTreeNode {
- SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
-
- static const DecoderTreeNode *
- mapElement(decltype(Children)::const_reference Element) {
- return Element.get();
- }
-
-public:
- AnyOfNode() : DecoderTreeNode(AnyOf) {}
-
- void addChild(std::unique_ptr<DecoderTreeNode> N) {
- Children.push_back(std::move(N));
- }
-
- using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
- decltype(&mapElement)>;
-
- child_iterator child_begin() const {
- return child_iterator(Children.begin(), mapElement);
- }
-
- child_iterator child_end() const {
- return child_iterator(Children.end(), mapElement);
- }
-
- iterator_range<child_iterator> children() const {
- return make_range(child_begin(), child_end());
- }
-};
-
-class SwitchFieldNode : public DecoderTreeNode {
- unsigned StartBit;
- unsigned NumBits;
- std::map<uint64_t, std::unique_ptr<DecoderTreeNode>> Cases;
-
- static std::pair<uint64_t, const DecoderTreeNode *>
- 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<DecoderTreeNode> N) {
- Cases.try_emplace(Value, std::move(N));
- }
-
- unsigned getStartBit() const { return StartBit; }
-
- unsigned getNumBits() const { return NumBits; }
-
- using case_iterator =
- mapped_iterator<decltype(Cases)::const_iterator, decltype(&mapElement)>;
-
- case_iterator case_begin() const {
- return case_iterator(Cases.begin(), mapElement);
- }
-
- case_iterator case_end() const {
- return case_iterator(Cases.end(), mapElement);
- }
-
- iterator_range<case_iterator> cases() const {
- return make_range(case_begin(), case_end());
- }
-};
-
-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; }
-};
-
-class CheckPredicateNode : public DecoderTreeNode {
- unsigned Index;
-
-public:
- explicit CheckPredicateNode(unsigned Index)
- : DecoderTreeNode(CheckPredicate), Index(Index) {}
-
- unsigned getPredicateIndex() const { return Index; }
-};
-
-class SoftFailNode : public DecoderTreeNode {
- uint64_t PositiveMask, NegativeMask;
-
-public:
- explicit 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; }
-};
-
-class DecodeNode : public DecoderTreeNode {
- unsigned EncodingID;
- unsigned Index;
-
-public:
- DecodeNode(unsigned EncodingID, unsigned Index)
- : DecoderTreeNode(Decode), EncodingID(EncodingID), Index(Index) {}
-
- unsigned getEncodingID() const { return EncodingID; }
-
- unsigned getDecoderIndex() const { return Index; }
-};
-
-typedef SmallSetVector<CachedHashString, 16> PredicateSet;
-typedef SmallSetVector<CachedHashString, 16> DecoderSet;
-
-struct DecoderTableInfo {
- PredicateSet Predicates;
- DecoderSet Decoders;
-};
-
using NamespacesHwModesMap = std::map<StringRef, std::set<unsigned>>;
class DecoderEmitter {
@@ -377,493 +157,7 @@ class DecoderEmitter {
void parseInstructionEncodings();
};
-} // end anonymous namespace
-
-namespace {
-
-/// Filter - Filter works with FilterChooser to produce the decoding tree for
-/// the ISA.
-///
-/// It is useful to think of a Filter as governing the switch stmts of the
-/// decoding tree in a certain level. Each case stmt delegates to an inferior
-/// FilterChooser to decide what further decoding logic to employ, or in another
-/// words, what other remaining bits to look at. The FilterChooser eventually
-/// chooses a best Filter to do its job.
-///
-/// This recursive scheme ends when the number of Opcodes assigned to the
-/// FilterChooser becomes 1 or if there is a conflict. A conflict happens when
-/// the Filter/FilterChooser combo does not know how to distinguish among the
-/// Opcodes assigned.
-///
-/// An example of a conflict is
-///
-/// Decoding Conflict:
-/// ................................
-/// 1111............................
-/// 1111010.........................
-/// 1111010...00....................
-/// 1111010...00........0001........
-/// 111101000.00........0001........
-/// 111101000.00........00010000....
-/// 111101000_00________00010000____ VST4q8a
-/// 111101000_00________00010000____ VST4q8b
-///
-/// The Debug output shows the path that the decoding tree follows to reach the
-/// the conclusion that there is a conflict. VST4q8a is a vst4 to double-spaced
-/// even registers, while VST4q8b is a vst4 to double-spaced odd registers.
-///
-/// The encoding info in the .td files does not specify this meta information,
-/// which could have been used by the decoder to resolve the conflict. The
-/// decoder could try to decode the even/odd register numbering and assign to
-/// VST4q8a or VST4q8b, but for the time being, the decoder chooses the "a"
-/// version and return the Opcode since the two have the same Asm format string.
-struct Filter {
- unsigned StartBit; // the starting bit position
- unsigned NumBits; // number of bits to filter
-
- // Map of well-known segment value to the set of uid's with that value.
- std::map<uint64_t, std::vector<unsigned>> FilteredIDs;
-
- // Set of uid's with non-constant segment values.
- std::vector<unsigned> VariableIDs;
-
- Filter(ArrayRef<InstructionEncoding> Encodings,
- ArrayRef<unsigned> EncodingIDs, unsigned StartBit, unsigned NumBits);
-
- // Returns the number of fanout produced by the filter. More fanout implies
- // the filter distinguishes more categories of instructions.
- unsigned usefulness() const;
-}; // end class Filter
-
-// These are states of our finite state machines used in FilterChooser's
-// filterProcessor() which produces the filter candidates to use.
-enum bitAttr_t {
- ATTR_NONE,
- ATTR_FILTERED,
- ATTR_ALL_SET,
- ATTR_ALL_UNSET,
- ATTR_MIXED
-};
-
-/// FilterChooser - FilterChooser chooses the best filter among a set of Filters
-/// in order to perform the decoding of instructions at the current level.
-///
-/// Decoding proceeds from the top down. Based on the well-known encoding bits
-/// of instructions available, FilterChooser builds up the possible Filters that
-/// can further the task of decoding by distinguishing among the remaining
-/// candidate instructions.
-///
-/// Once a filter has been chosen, it is called upon to divide the decoding task
-/// into sub-tasks and delegates them to its inferior FilterChoosers for further
-/// processings.
-///
-/// It is useful to think of a Filter as governing the switch stmts of the
-/// decoding tree. And each case is delegated to an inferior FilterChooser to
-/// decide what further remaining bits to look at.
-
-class FilterChooser {
- // TODO: Unfriend by providing the necessary accessors.
- friend class DecoderTreeBuilder;
-
- // Vector of encodings to choose our filter.
- ArrayRef<InstructionEncoding> Encodings;
-
- /// Encoding IDs for this filter chooser to work on.
- /// Sorted by non-decreasing encoding width.
- SmallVector<unsigned, 0> EncodingIDs;
-
- // Array of bit values passed down from our parent.
- // Set to all unknown for Parent == nullptr.
- KnownBits FilterBits;
-
- // Links to the FilterChooser above us in the decoding tree.
- const FilterChooser *Parent;
-
- /// If the selected filter matches multiple encodings, then this is the
- /// starting position and the width of the filtered range.
- unsigned StartBit;
- unsigned NumBits;
-
- /// If the selected filter matches multiple encodings, and there is
- /// *exactly one* encoding in which all bits are known in the filtered range,
- /// then this is the ID of that encoding.
- /// Also used when there is only one encoding.
- std::optional<unsigned> SingletonEncodingID;
-
- /// 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
- /// contain some unknown bits in the filtered range.
- std::unique_ptr<const FilterChooser> VariableFC;
-
- /// If the selected filter matches multiple encodings, and there is
- /// *more than one* encoding in which all bits are known in the filtered
- /// range, then this is a map of field values to FilterChoosers created for
- /// the subset of encodings sharing that field value.
- /// The "field value" here refers to the encoding bits in the filtered range.
- std::map<uint64_t, std::unique_ptr<const FilterChooser>> FilterChooserMap;
-
- /// Set to true if decoding conflict was encountered.
- bool HasConflict = false;
-
- struct Island {
- unsigned StartBit;
- unsigned NumBits;
- uint64_t FieldVal;
- };
-
-public:
- /// Constructs a top-level filter chooser.
- FilterChooser(ArrayRef<InstructionEncoding> Encodings,
- ArrayRef<unsigned> EncodingIDs)
- : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(nullptr) {
- // Sort encoding IDs once.
- stable_sort(this->EncodingIDs, LessEncodingIDByWidth(Encodings));
- // Filter width is the width of the smallest encoding.
- unsigned FilterWidth = Encodings[this->EncodingIDs.front()].getBitWidth();
- FilterBits = KnownBits(FilterWidth);
- doFilter();
- }
-
- /// Constructs an inferior filter chooser.
- FilterChooser(ArrayRef<InstructionEncoding> Encodings,
- ArrayRef<unsigned> EncodingIDs, const KnownBits &FilterBits,
- const FilterChooser &Parent)
- : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(&Parent) {
- // Inferior filter choosers are created from sorted array of encoding IDs.
- assert(is_sorted(EncodingIDs, LessEncodingIDByWidth(Encodings)));
- assert(!FilterBits.hasConflict() && "Broken filter");
- // Filter width is the width of the smallest encoding.
- unsigned FilterWidth = Encodings[EncodingIDs.front()].getBitWidth();
- this->FilterBits = FilterBits.anyext(FilterWidth);
- doFilter();
- }
-
- FilterChooser(const FilterChooser &) = delete;
- void operator=(const FilterChooser &) = delete;
-
- /// Returns the width of the largest encoding.
- unsigned getMaxEncodingWidth() const {
- // The last encoding ID is the ID of an encoding with the largest width.
- return Encodings[EncodingIDs.back()].getBitWidth();
- }
-
- /// Returns true if any decoding conflicts were encountered.
- bool hasConflict() const { return HasConflict; }
-
-private:
- /// Applies the given filter to the set of encodings this FilterChooser
- /// works with, creating inferior FilterChoosers as necessary.
- void applyFilter(const Filter &F);
-
- /// 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;
-
- bool isPositionFiltered(unsigned Idx) const {
- return FilterBits.Zero[Idx] || FilterBits.One[Idx];
- }
-
- // Calculates the island(s) needed to decode the instruction.
- // This returns a list of undecoded bits of an instructions, for example,
- // Inst{20} = 1 && Inst{3-0} == 0b1111 represents two islands of yet-to-be
- // decoded bits in order to verify that the instruction matches the Opcode.
- static std::vector<Island> getIslands(const KnownBits &EncodingBits,
- const KnownBits &FilterBits);
-
- /// Scans the well-known encoding bits of the encodings and, builds up a list
- /// of candidate filters, and then returns the best one, if any.
- std::unique_ptr<Filter> findBestFilter(ArrayRef<bitAttr_t> BitAttrs,
- bool AllowMixed,
- bool Greedy = true) const;
-
- std::unique_ptr<Filter> findBestFilter() const;
-
- // 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.
- void doFilter();
-
-public:
- void dump() const;
-};
-
-class DecoderTreeBuilder {
- const CodeGenTarget &Target;
- ArrayRef<InstructionEncoding> Encodings;
- DecoderTableInfo &TableInfo;
-
-public:
- DecoderTreeBuilder(const CodeGenTarget &Target,
- ArrayRef<InstructionEncoding> Encodings,
- DecoderTableInfo &TableInfo)
- : Target(Target), Encodings(Encodings), TableInfo(TableInfo) {}
-
- std::unique_ptr<DecoderTreeNode> buildTree(const FilterChooser &FC) {
- return buildAnyOfNode(FC);
- }
-
-private:
- static void emitBinaryParser(raw_ostream &OS, indent Indent,
- const InstructionEncoding &Encoding,
- const OperandInfo &OpInfo);
- static void emitDecoder(raw_ostream &OS, indent Indent,
- const InstructionEncoding &Encoding);
- unsigned getDecoderIndex(const InstructionEncoding &Encoding);
-
- static bool emitPredicateMatchAux(StringRef PredicateNamespace,
- const Init &Val, bool ParenIfBinOp,
- raw_ostream &OS);
- static bool emitPredicateMatch(StringRef PredicateNamespace, raw_ostream &OS,
- const InstructionEncoding &Encoding);
- unsigned getPredicateIndex(const InstructionEncoding &Encoding) const;
-
- std::unique_ptr<DecoderTreeNode>
- buildTerminalNode(unsigned EncodingID, const KnownBits &FilterBits);
-
- std::unique_ptr<DecoderTreeNode> buildAllOfOrSwitchNode(
- unsigned StartBit, unsigned NumBits,
- const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap);
-
- std::unique_ptr<DecoderTreeNode> buildAnyOfNode(const FilterChooser &FC);
-};
-
-class DecoderTableEmitter {
- const CodeGenTarget &Target;
- ArrayRef<InstructionEncoding> Encodings;
- formatted_raw_ostream OS;
- unsigned IndexWidth;
- unsigned CurrentIndex;
- unsigned CommentIndex;
- bool HasCheckPredicate = false;
- bool HasSoftFail = false;
- bool HasTryDecode = false;
-
-public:
- DecoderTableEmitter(const CodeGenTarget &Target,
- ArrayRef<InstructionEncoding> Encodings, raw_ostream &OS)
- : Target(Target), Encodings(Encodings), OS(OS) {}
-
- void emitTable(StringRef TableName, unsigned BitWidth,
- const DecoderTreeNode *Root);
-
- bool hasCheckPredicate() const { return HasCheckPredicate; }
-
- bool hasSoftFail() const { return HasSoftFail; }
-
- bool hasTryDecode() const { return HasTryDecode; }
-
-private:
- unsigned computeNodeSize(const DecoderTreeNode *N) const;
-
- unsigned computeTableSize(unsigned BitWidth,
- const DecoderTreeNode *Root) const {
- unsigned Size = 0;
- if (SpecializeDecodersPerBitwidth)
- Size = getULEB128Size(BitWidth);
- Size += computeNodeSize(Root);
- return Size;
- }
-
- void emitStartLine() {
- CommentIndex = CurrentIndex;
- OS.indent(2);
- }
-
- void emitOpcode(StringRef Name) {
- emitStartLine();
- OS << "MCD::" << Name << ", ";
- ++CurrentIndex;
- }
-
- void emitByte(uint8_t Val) {
- OS << static_cast<unsigned>(Val) << ", ";
- ++CurrentIndex;
- }
-
- void emitUInt8(unsigned Val) {
- assert(isUInt<8>(Val));
- emitByte(Val);
- }
-
- void emitULEB128(uint64_t Val) {
- while (Val >= 0x80) {
- emitByte((Val & 0x7F) | 0x80);
- Val >>= 7;
- }
- emitByte(Val);
- }
-
- formatted_raw_ostream &emitComment(indent Indent) {
- constexpr unsigned CommentColumn = 45;
- if (OS.getColumn() > CommentColumn)
- OS << '\n';
- OS.PadToColumn(CommentColumn);
- OS << "// " << format_decimal(CommentIndex, IndexWidth) << ": " << Indent;
- return OS;
- }
-
- void emitAnyOfNode(const AnyOfNode *N, indent Indent);
- void emitAllOfNode(const AllOfNode *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);
-};
-
-} // end anonymous namespace
-
-///////////////////////////
-// //
-// Filter Implementation //
-// //
-///////////////////////////
-
-Filter::Filter(ArrayRef<InstructionEncoding> Encodings,
- ArrayRef<unsigned> EncodingIDs, unsigned StartBit,
- unsigned NumBits)
- : StartBit(StartBit), NumBits(NumBits) {
- for (unsigned EncodingID : EncodingIDs) {
- const InstructionEncoding &Encoding = Encodings[EncodingID];
- KnownBits EncodingBits = Encoding.getMandatoryBits();
-
- // Scans the segment for possibly well-specified encoding bits.
- KnownBits FieldBits = EncodingBits.extractBits(NumBits, StartBit);
-
- if (FieldBits.isConstant()) {
- // The encoding bits are well-known. Lets add the uid of the
- // instruction into the bucket keyed off the constant field value.
- FilteredIDs[FieldBits.getConstant().getZExtValue()].push_back(EncodingID);
- } else {
- // Some of the encoding bit(s) are unspecified. This contributes to
- // one additional member of "Variable" instructions.
- VariableIDs.push_back(EncodingID);
- }
- }
-
- assert((FilteredIDs.size() + VariableIDs.size() > 0) &&
- "Filter returns no instruction categories");
-}
-
-void FilterChooser::applyFilter(const Filter &F) {
- StartBit = F.StartBit;
- NumBits = F.NumBits;
- assert(FilterBits.extractBits(NumBits, StartBit).isUnknown());
-
- if (!F.VariableIDs.empty()) {
- // Delegates to an inferior filter chooser for further processing on this
- // group of instructions whose segment values are variable.
- VariableFC = std::make_unique<FilterChooser>(Encodings, F.VariableIDs,
- FilterBits, *this);
- HasConflict |= VariableFC->HasConflict;
- }
-
- // Otherwise, create sub choosers.
- for (const auto &[FilterVal, InferiorEncodingIDs] : F.FilteredIDs) {
- // Create a new filter by inserting the field bits into the parent filter.
- APInt FieldBits(NumBits, FilterVal);
- KnownBits InferiorFilterBits = FilterBits;
- InferiorFilterBits.insertBits(KnownBits::makeConstant(FieldBits), StartBit);
-
- // Delegates to an inferior filter chooser for further processing on this
- // category of instructions.
- auto [It, _] = FilterChooserMap.try_emplace(
- FilterVal,
- std::make_unique<FilterChooser>(Encodings, InferiorEncodingIDs,
- InferiorFilterBits, *this));
- HasConflict |= It->second->HasConflict;
- }
-}
-
-// Returns the number of fanout produced by the filter. More fanout implies
-// the filter distinguishes more categories of instructions.
-unsigned Filter::usefulness() const {
- return FilteredIDs.size() + VariableIDs.empty();
-}
-
-static bool doesOpcodeNeedPredicate(const InstructionEncoding &Encoding);
-
-std::unique_ptr<DecoderTreeNode>
-DecoderTreeBuilder::buildTerminalNode(unsigned EncodingID,
- const KnownBits &FilterBits) {
- const InstructionEncoding &Encoding = Encodings[EncodingID];
- auto N = std::make_unique<AllOfNode>();
-
- if (doesOpcodeNeedPredicate(Encoding)) {
- unsigned PredicateIndex = getPredicateIndex(Encoding);
- N->addChild(std::make_unique<CheckPredicateNode>(PredicateIndex));
- }
-
- std::vector<FilterChooser::Island> Islands =
- FilterChooser::getIslands(Encoding.getMandatoryBits(), FilterBits);
- for (const FilterChooser::Island &Ilnd : reverse(Islands)) {
- N->addChild(std::make_unique<CheckFieldNode>(Ilnd.StartBit, Ilnd.NumBits,
- Ilnd.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<SoftFailNode>(PositiveMask.getZExtValue(),
- NegativeMask.getZExtValue()));
- }
-
- unsigned DecoderIndex = getDecoderIndex(Encoding);
- N->addChild(std::make_unique<DecodeNode>(EncodingID, DecoderIndex));
-
- return N;
-}
-
-std::unique_ptr<DecoderTreeNode> DecoderTreeBuilder::buildAllOfOrSwitchNode(
- unsigned int StartBit, unsigned int NumBits,
- const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap) {
- if (FCMap.size() == 1) {
- const auto &[FieldVal, ChildFC] = *FCMap.begin();
- auto N = std::make_unique<AllOfNode>();
- N->addChild(std::make_unique<CheckFieldNode>(StartBit, NumBits, FieldVal));
- N->addChild(buildAnyOfNode(*ChildFC));
- return N;
- }
- auto N = std::make_unique<SwitchFieldNode>(StartBit, NumBits);
- for (const auto &[FieldVal, ChildFC] : FCMap)
- N->addCase(FieldVal, buildAnyOfNode(*ChildFC));
- return N;
-}
-
-std::unique_ptr<DecoderTreeNode>
-DecoderTreeBuilder::buildAnyOfNode(const FilterChooser &FC) {
- auto N = std::make_unique<AnyOfNode>();
- if (FC.SingletonEncodingID) {
- N->addChild(buildTerminalNode(*FC.SingletonEncodingID, FC.FilterBits));
- } else {
- N->addChild(
- buildAllOfOrSwitchNode(FC.StartBit, FC.NumBits, FC.FilterChooserMap));
- }
- if (FC.VariableFC) {
- N->addChild(buildAnyOfNode(*FC.VariableFC));
- }
-
- return N;
-}
-
-// Emit the decoder state machine table.
-void DecoderEmitter::emitDecoderTable(formatted_raw_ostream &OS,
- const DecoderTreeNode *Tree,
- StringRef Namespace, unsigned HwModeID,
- unsigned BitWidth) const {
- SmallString<32> TableName("DecoderTable");
- TableName.append(Namespace);
- if (HwModeID != DefaultMode)
- TableName.append({"_", Target.getHwModes().getModeName(HwModeID)});
- TableName.append(std::to_string(BitWidth));
-
- DecoderTableEmitter TableEmitter(Target, Encodings, OS);
- TableEmitter.emitTable(TableName, BitWidth, Tree);
-}
+} // namespace
void DecoderEmitter::emitInstrLenTable(formatted_raw_ostream &OS,
ArrayRef<unsigned> InstrLen) const {
@@ -975,766 +269,6 @@ void DecoderEmitter::emitDecoderFunction(formatted_raw_ostream &OS,
OS << "}\n";
}
-/// dumpStack - dumpStack traverses the filter chooser chain and calls
-/// dumpFilterArray on each filter chooser up to the top level one.
-void FilterChooser::dumpStack(raw_ostream &OS, indent Indent,
- unsigned PadToWidth) const {
- if (Parent)
- Parent->dumpStack(OS, Indent, PadToWidth);
- assert(PadToWidth >= FilterBits.getBitWidth());
- OS << Indent << indent(PadToWidth - FilterBits.getBitWidth());
- printKnownBits(OS, FilterBits, '.');
- OS << '\n';
-}
-
-// Calculates the island(s) needed to decode the instruction.
-// This returns a list of undecoded bits of an instructions, for example,
-// Inst{20} = 1 && Inst{3-0} == 0b1111 represents two islands of yet-to-be
-// decoded bits in order to verify that the instruction matches the Opcode.
-std::vector<FilterChooser::Island>
-FilterChooser::getIslands(const KnownBits &EncodingBits,
- const KnownBits &FilterBits) {
- std::vector<Island> Islands;
- uint64_t FieldVal;
- unsigned StartBit;
-
- // 0: Init
- // 1: Water (the bit value does not affect decoding)
- // 2: Island (well-known bit value needed for decoding)
- unsigned State = 0;
-
- unsigned FilterWidth = FilterBits.getBitWidth();
- for (unsigned i = 0; i != FilterWidth; ++i) {
- bool IsKnown = EncodingBits.Zero[i] || EncodingBits.One[i];
- bool Filtered = FilterBits.Zero[i] || FilterBits.One[i];
- switch (State) {
- default:
- llvm_unreachable("Unreachable code!");
- case 0:
- case 1:
- if (Filtered || !IsKnown) {
- State = 1; // Still in Water
- } else {
- State = 2; // Into the Island
- StartBit = i;
- FieldVal = static_cast<uint64_t>(EncodingBits.One[i]);
- }
- break;
- case 2:
- if (Filtered || !IsKnown) {
- State = 1; // Into the Water
- Islands.push_back({StartBit, i - StartBit, FieldVal});
- } else {
- State = 2; // Still in Island
- FieldVal |= static_cast<uint64_t>(EncodingBits.One[i])
- << (i - StartBit);
- }
- break;
- }
- }
- // If we are still in Island after the loop, do some housekeeping.
- if (State == 2)
- Islands.push_back({StartBit, FilterWidth - StartBit, FieldVal});
-
- return Islands;
-}
-
-void DecoderTreeBuilder::emitBinaryParser(raw_ostream &OS, indent Indent,
- const InstructionEncoding &Encoding,
- const OperandInfo &OpInfo) {
- if (OpInfo.HasNoEncoding) {
- // If an operand has no encoding, the old behavior is to not decode it
- // automatically and let the target do it. This is error-prone, so the
- // new behavior is to report an error.
- if (!IgnoreNonDecodableOperands)
- PrintError(Encoding.getRecord()->getLoc(),
- "could not find field for operand '" + OpInfo.Name + "'");
- return;
- }
- // Special case for 'bits<0>'.
- if (OpInfo.Fields.empty() && !OpInfo.InitValue) {
- if (IgnoreNonDecodableOperands)
- return;
- assert(!OpInfo.Decoder.empty());
- // The operand has no encoding, so the corresponding argument is omitted.
- // This avoids confusion and allows the function to be overloaded if the
- // operand does have an encoding in other instructions.
- OS << Indent << "if (!Check(S, " << OpInfo.Decoder << "(MI, Decoder)))\n"
- << Indent << " return MCDisassembler::Fail;\n";
- return;
- }
-
- if (OpInfo.fields().empty()) {
- // Only a constant part. The old behavior is to not decode this operand.
- if (IgnoreFullyDefinedOperands)
- return;
- // Initialize `tmp` with the constant part.
- OS << Indent << "tmp = " << format_hex(*OpInfo.InitValue, 0) << ";\n";
- } else if (OpInfo.fields().size() == 1 && !OpInfo.InitValue.value_or(0)) {
- // One variable part and no/zero constant part. Initialize `tmp` with the
- // variable part.
- auto [Base, Width, Offset] = OpInfo.fields().front();
- OS << Indent << "tmp = fieldFromInstruction(insn, " << Base << ", " << Width
- << ')';
- if (Offset)
- OS << " << " << Offset;
- OS << ";\n";
- } else {
- // General case. Initialize `tmp` with the constant part, if any, and
- // insert the variable parts into it.
- OS << Indent << "tmp = " << format_hex(OpInfo.InitValue.value_or(0), 0)
- << ";\n";
- for (auto [Base, Width, Offset] : OpInfo.fields())
- OS << Indent << "insertBits(tmp, fieldFromInstruction(insn, " << Base
- << ", " << Width << "), " << Offset << ", " << Width << ");\n";
- }
-
- StringRef Decoder = OpInfo.Decoder;
- if (!Decoder.empty()) {
- OS << Indent << "if (!Check(S, " << Decoder
- << "(MI, tmp, Address, Decoder))) { "
- << (OpInfo.HasCompleteDecoder ? "" : "DecodeComplete = false; ")
- << "return MCDisassembler::Fail; }\n";
- } else {
- OS << Indent << "MI.addOperand(MCOperand::createImm(tmp));\n";
- }
-}
-
-void DecoderTreeBuilder::emitDecoder(raw_ostream &OS, indent Indent,
- const InstructionEncoding &Encoding) {
- // If a custom instruction decoder was specified, use that.
- StringRef DecoderMethod = Encoding.getDecoderMethod();
- if (!DecoderMethod.empty()) {
- OS << Indent << "if (!Check(S, " << DecoderMethod
- << "(MI, insn, Address, Decoder))) { "
- << (Encoding.hasCompleteDecoder() ? "" : "DecodeComplete = false; ")
- << "return MCDisassembler::Fail; }\n";
- return;
- }
-
- for (const OperandInfo &Op : Encoding.getOperands())
- emitBinaryParser(OS, Indent, Encoding, Op);
-}
-
-unsigned
-DecoderTreeBuilder::getDecoderIndex(const InstructionEncoding &Encoding) {
- // Build up the predicate string.
- SmallString<256> Decoder;
- // FIXME: emitDecoder() function can take a buffer directly rather than
- // a stream.
- raw_svector_ostream S(Decoder);
- indent Indent(UseFnTableInDecodeToMCInst ? 2 : 4);
- emitDecoder(S, Indent, Encoding);
-
- // 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.
-
- // Make sure the predicate is in the table.
- DecoderSet &Decoders = TableInfo.Decoders;
- Decoders.insert(CachedHashString(Decoder));
- // Now figure out the index for when we write out the table.
- DecoderSet::const_iterator P = find(Decoders, Decoder.str());
- return std::distance(Decoders.begin(), P);
-}
-
-// If ParenIfBinOp is true, print a surrounding () if Val uses && or ||.
-bool DecoderTreeBuilder::emitPredicateMatchAux(StringRef PredicateNamespace,
- const Init &Val,
- bool ParenIfBinOp,
- raw_ostream &OS) {
- if (const auto *D = dyn_cast<DefInit>(&Val)) {
- if (!D->getDef()->isSubClassOf("SubtargetFeature"))
- return true;
- OS << "Bits[" << PredicateNamespace << "::" << D->getAsString() << "]";
- return false;
- }
- if (const auto *D = dyn_cast<DagInit>(&Val)) {
- std::string Op = D->getOperator()->getAsString();
- if (Op == "not" && D->getNumArgs() == 1) {
- OS << '!';
- return emitPredicateMatchAux(PredicateNamespace, *D->getArg(0), true, OS);
- }
- if ((Op == "any_of" || Op == "all_of") && D->getNumArgs() > 0) {
- bool Paren = D->getNumArgs() > 1 && std::exchange(ParenIfBinOp, true);
- if (Paren)
- OS << '(';
- ListSeparator LS(Op == "any_of" ? " || " : " && ");
- for (auto *Arg : D->getArgs()) {
- OS << LS;
- if (emitPredicateMatchAux(PredicateNamespace, *Arg, ParenIfBinOp, OS))
- return true;
- }
- if (Paren)
- OS << ')';
- return false;
- }
- }
- return true;
-}
-
-bool DecoderTreeBuilder::emitPredicateMatch(
- StringRef PredicateNamespace, raw_ostream &OS,
- const InstructionEncoding &Encoding) {
- const ListInit *Predicates =
- Encoding.getRecord()->getValueAsListInit("Predicates");
- bool IsFirstEmission = true;
- for (unsigned i = 0; i < Predicates->size(); ++i) {
- const Record *Pred = Predicates->getElementAsRecord(i);
- if (!Pred->getValue("AssemblerMatcherPredicate"))
- continue;
-
- if (!isa<DagInit>(Pred->getValue("AssemblerCondDag")->getValue()))
- continue;
-
- if (!IsFirstEmission)
- OS << " && ";
- if (emitPredicateMatchAux(PredicateNamespace,
- *Pred->getValueAsDag("AssemblerCondDag"),
- Predicates->size() > 1, OS))
- PrintFatalError(Pred->getLoc(), "Invalid AssemblerCondDag!");
- IsFirstEmission = false;
- }
- return !Predicates->empty();
-}
-
-static bool doesOpcodeNeedPredicate(const InstructionEncoding &Encoding) {
- const ListInit *Predicates =
- Encoding.getRecord()->getValueAsListInit("Predicates");
- for (unsigned i = 0; i < Predicates->size(); ++i) {
- const Record *Pred = Predicates->getElementAsRecord(i);
- if (!Pred->getValue("AssemblerMatcherPredicate"))
- continue;
-
- if (isa<DagInit>(Pred->getValue("AssemblerCondDag")->getValue()))
- return true;
- }
- return false;
-}
-
-unsigned DecoderTreeBuilder::getPredicateIndex(
- const InstructionEncoding &Encoding) const {
- // Build up the predicate string.
- SmallString<256> Predicate;
- // FIXME: emitPredicateMatch() functions can take a buffer directly rather
- // than a stream.
- raw_svector_ostream PS(Predicate);
- emitPredicateMatch(Target.getName(), PS, Encoding);
-
- // 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.
-
- // Make sure the predicate is in the table.
- PredicateSet &Predicates = TableInfo.Predicates;
- Predicates.insert(CachedHashString(Predicate));
- // Now figure out the index for when we write out the table.
- PredicateSet::const_iterator P = find(Predicates, Predicate);
- return std::distance(Predicates.begin(), P);
-}
-
-std::unique_ptr<Filter>
-FilterChooser::findBestFilter(ArrayRef<bitAttr_t> BitAttrs, bool AllowMixed,
- bool Greedy) const {
- assert(EncodingIDs.size() >= 2 && "Nothing to filter");
-
- // Heuristics. See also doFilter()'s "Heuristics" comment when num of
- // instructions is 3.
- if (AllowMixed && !Greedy) {
- assert(EncodingIDs.size() == 3);
-
- for (unsigned EncodingID : EncodingIDs) {
- const InstructionEncoding &Encoding = Encodings[EncodingID];
- KnownBits EncodingBits = Encoding.getMandatoryBits();
-
- // Look for islands of undecoded bits of any instruction.
- std::vector<Island> Islands = getIslands(EncodingBits, FilterBits);
- if (!Islands.empty()) {
- // Found an instruction with island(s). Now just assign a filter.
- return std::make_unique<Filter>(
- Encodings, EncodingIDs, Islands[0].StartBit, Islands[0].NumBits);
- }
- }
- }
-
- // The regionAttr automaton consumes the bitAttrs automatons' state,
- // lowest-to-highest.
- //
- // Input symbols: F(iltered), (all_)S(et), (all_)U(nset), M(ixed)
- // States: NONE, ALL_SET, MIXED
- // Initial state: NONE
- //
- // (NONE) ----- F --> (NONE)
- // (NONE) ----- S --> (ALL_SET) ; and set region start
- // (NONE) ----- U --> (NONE)
- // (NONE) ----- M --> (MIXED) ; and set region start
- // (ALL_SET) -- F --> (NONE) ; and report an ALL_SET region
- // (ALL_SET) -- S --> (ALL_SET)
- // (ALL_SET) -- U --> (NONE) ; and report an ALL_SET region
- // (ALL_SET) -- M --> (MIXED) ; and report an ALL_SET region
- // (MIXED) ---- F --> (NONE) ; and report a MIXED region
- // (MIXED) ---- S --> (ALL_SET) ; and report a MIXED region
- // (MIXED) ---- U --> (NONE) ; and report a MIXED region
- // (MIXED) ---- M --> (MIXED)
-
- bitAttr_t RA = ATTR_NONE;
- unsigned StartBit = 0;
-
- std::vector<std::unique_ptr<Filter>> Filters;
-
- auto addCandidateFilter = [&](unsigned StartBit, unsigned EndBit) {
- Filters.push_back(std::make_unique<Filter>(Encodings, EncodingIDs, StartBit,
- EndBit - StartBit));
- };
-
- unsigned FilterWidth = FilterBits.getBitWidth();
- for (unsigned BitIndex = 0; BitIndex != FilterWidth; ++BitIndex) {
- bitAttr_t bitAttr = BitAttrs[BitIndex];
-
- assert(bitAttr != ATTR_NONE && "Bit without attributes");
-
- switch (RA) {
- case ATTR_NONE:
- switch (bitAttr) {
- case ATTR_FILTERED:
- break;
- case ATTR_ALL_SET:
- StartBit = BitIndex;
- RA = ATTR_ALL_SET;
- break;
- case ATTR_ALL_UNSET:
- break;
- case ATTR_MIXED:
- StartBit = BitIndex;
- RA = ATTR_MIXED;
- break;
- default:
- llvm_unreachable("Unexpected bitAttr!");
- }
- break;
- case ATTR_ALL_SET:
- if (!AllowMixed && bitAttr != ATTR_ALL_SET)
- addCandidateFilter(StartBit, BitIndex);
- switch (bitAttr) {
- case ATTR_FILTERED:
- RA = ATTR_NONE;
- break;
- case ATTR_ALL_SET:
- break;
- case ATTR_ALL_UNSET:
- RA = ATTR_NONE;
- break;
- case ATTR_MIXED:
- StartBit = BitIndex;
- RA = ATTR_MIXED;
- break;
- default:
- llvm_unreachable("Unexpected bitAttr!");
- }
- break;
- case ATTR_MIXED:
- if (AllowMixed && bitAttr != ATTR_MIXED)
- addCandidateFilter(StartBit, BitIndex);
- switch (bitAttr) {
- case ATTR_FILTERED:
- StartBit = BitIndex;
- RA = ATTR_NONE;
- break;
- case ATTR_ALL_SET:
- StartBit = BitIndex;
- RA = ATTR_ALL_SET;
- break;
- case ATTR_ALL_UNSET:
- RA = ATTR_NONE;
- break;
- case ATTR_MIXED:
- break;
- default:
- llvm_unreachable("Unexpected bitAttr!");
- }
- break;
- case ATTR_ALL_UNSET:
- llvm_unreachable("regionAttr state machine has no ATTR_UNSET state");
- case ATTR_FILTERED:
- llvm_unreachable("regionAttr state machine has no ATTR_FILTERED state");
- }
- }
-
- // At the end, if we're still in ALL_SET or MIXED states, report a region
- switch (RA) {
- case ATTR_NONE:
- break;
- case ATTR_FILTERED:
- break;
- case ATTR_ALL_SET:
- if (!AllowMixed)
- addCandidateFilter(StartBit, FilterWidth);
- break;
- case ATTR_ALL_UNSET:
- break;
- case ATTR_MIXED:
- if (AllowMixed)
- addCandidateFilter(StartBit, FilterWidth);
- break;
- }
-
- // We have finished with the filter processings. Now it's time to choose
- // the best performing filter.
- auto MaxIt = llvm::max_element(Filters, [](const std::unique_ptr<Filter> &A,
- const std::unique_ptr<Filter> &B) {
- return A->usefulness() < B->usefulness();
- });
- if (MaxIt == Filters.end() || (*MaxIt)->usefulness() == 0)
- return nullptr;
- return std::move(*MaxIt);
-}
-
-std::unique_ptr<Filter> FilterChooser::findBestFilter() const {
- // We maintain BIT_WIDTH copies of the bitAttrs automaton.
- // The automaton consumes the corresponding bit from each
- // instruction.
- //
- // Input symbols: 0, 1, _ (unset), and . (any of the above).
- // States: NONE, FILTERED, ALL_SET, ALL_UNSET, and MIXED.
- // Initial state: NONE.
- //
- // (NONE) ------- [01] -> (ALL_SET)
- // (NONE) ------- _ ----> (ALL_UNSET)
- // (ALL_SET) ---- [01] -> (ALL_SET)
- // (ALL_SET) ---- _ ----> (MIXED)
- // (ALL_UNSET) -- [01] -> (MIXED)
- // (ALL_UNSET) -- _ ----> (ALL_UNSET)
- // (MIXED) ------ . ----> (MIXED)
- // (FILTERED)---- . ----> (FILTERED)
-
- unsigned FilterWidth = FilterBits.getBitWidth();
- SmallVector<bitAttr_t, 128> BitAttrs(FilterWidth, ATTR_NONE);
-
- // FILTERED bit positions provide no entropy and are not worthy of pursuing.
- // Filter::recurse() set either 1 or 0 for each position.
- for (unsigned BitIndex = 0; BitIndex != FilterWidth; ++BitIndex)
- if (isPositionFiltered(BitIndex))
- BitAttrs[BitIndex] = ATTR_FILTERED;
-
- for (unsigned EncodingID : EncodingIDs) {
- const InstructionEncoding &Encoding = Encodings[EncodingID];
- KnownBits EncodingBits = Encoding.getMandatoryBits();
-
- for (unsigned BitIndex = 0; BitIndex != FilterWidth; ++BitIndex) {
- bool IsKnown = EncodingBits.Zero[BitIndex] || EncodingBits.One[BitIndex];
- switch (BitAttrs[BitIndex]) {
- case ATTR_NONE:
- if (IsKnown)
- BitAttrs[BitIndex] = ATTR_ALL_SET;
- else
- BitAttrs[BitIndex] = ATTR_ALL_UNSET;
- break;
- case ATTR_ALL_SET:
- if (!IsKnown)
- BitAttrs[BitIndex] = ATTR_MIXED;
- break;
- case ATTR_ALL_UNSET:
- if (IsKnown)
- BitAttrs[BitIndex] = ATTR_MIXED;
- break;
- case ATTR_MIXED:
- case ATTR_FILTERED:
- break;
- }
- }
- }
-
- // Try regions of consecutive known bit values first.
- if (std::unique_ptr<Filter> F =
- findBestFilter(BitAttrs, /*AllowMixed=*/false))
- return F;
-
- // Then regions of mixed bits (both known and unitialized bit values allowed).
- if (std::unique_ptr<Filter> F = findBestFilter(BitAttrs, /*AllowMixed=*/true))
- return F;
-
- // Heuristics to cope with conflict set {t2CMPrs, t2SUBSrr, t2SUBSrs} where
- // no single instruction for the maximum ATTR_MIXED region Inst{14-4} has a
- // well-known encoding pattern. In such case, we backtrack and scan for the
- // the very first consecutive ATTR_ALL_SET region and assign a filter to it.
- if (EncodingIDs.size() == 3) {
- if (std::unique_ptr<Filter> F =
- findBestFilter(BitAttrs, /*AllowMixed=*/true, /*Greedy=*/false))
- return F;
- }
-
- // There is a conflict we could not resolve.
- return nullptr;
-}
-
-// 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.
-void FilterChooser::doFilter() {
- assert(!EncodingIDs.empty() && "FilterChooser created with no instructions");
-
- // No filter needed.
- if (EncodingIDs.size() == 1) {
- SingletonEncodingID = EncodingIDs.front();
- return;
- }
-
- std::unique_ptr<Filter> BestFilter = findBestFilter();
- if (BestFilter) {
- applyFilter(*BestFilter);
- return;
- }
-
- // Print out useful conflict information for postmortem analysis.
- errs() << "Decoding Conflict:\n";
- dump();
- HasConflict = true;
-}
-
-void FilterChooser::dump() const {
- indent Indent(4);
- // Helps to keep the output right-justified.
- unsigned PadToWidth = getMaxEncodingWidth();
-
- // Dump filter stack.
- dumpStack(errs(), Indent, PadToWidth);
-
- // Dump encodings.
- for (unsigned EncodingID : EncodingIDs) {
- const InstructionEncoding &Encoding = Encodings[EncodingID];
- errs() << Indent << indent(PadToWidth - Encoding.getBitWidth());
- printKnownBits(errs(), Encoding.getMandatoryBits(), '_');
- errs() << " " << Encoding.getName() << '\n';
- }
-}
-
-unsigned DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *N) const {
- switch (N->getKind()) {
- case DecoderTreeNode::AnyOf: {
- const auto *CheckAny = static_cast<const AnyOfNode *>(N);
- unsigned Size = 0;
- for (const DecoderTreeNode *Child : drop_end(CheckAny->children())) {
- unsigned ChildSize = computeNodeSize(Child);
- Size += 1 + getULEB128Size(ChildSize) + ChildSize;
- }
- return Size + computeNodeSize(*std::prev(CheckAny->child_end()));
- }
- case DecoderTreeNode::AllOf: {
- const auto *CheckAll = static_cast<const AllOfNode *>(N);
- unsigned Size = 0;
- for (const DecoderTreeNode *Child : CheckAll->children())
- Size += computeNodeSize(Child);
- return Size;
- }
- case DecoderTreeNode::CheckField: {
- const auto *CheckField = static_cast<const CheckFieldNode *>(N);
- return 1 + getULEB128Size(CheckField->getStartBit()) + 1 +
- getULEB128Size(CheckField->getValue());
- }
- case DecoderTreeNode::SwitchField: {
- const auto *SwitchN = static_cast<const SwitchFieldNode *>(N);
- unsigned Size = 1 + getULEB128Size(SwitchN->getStartBit()) + 1;
-
- for (auto [Val, Child] : drop_end(SwitchN->cases())) {
- unsigned ChildSize = computeNodeSize(Child);
- Size += getULEB128Size(Val) + getULEB128Size(ChildSize) + ChildSize;
- }
-
- auto [Val, Child] = *std::prev(SwitchN->case_end());
- unsigned ChildSize = computeNodeSize(Child);
- Size += getULEB128Size(Val) + getULEB128Size(0) + ChildSize;
- return Size;
- }
- case DecoderTreeNode::CheckPredicate: {
- const auto *CheckPredicate = static_cast<const CheckPredicateNode *>(N);
- return 1 + getULEB128Size(CheckPredicate->getPredicateIndex());
- }
- case DecoderTreeNode::SoftFail: {
- const auto *SoftFail = static_cast<const SoftFailNode *>(N);
- return 1 + getULEB128Size(SoftFail->getPositiveMask()) +
- getULEB128Size(SoftFail->getNegativeMask());
- }
- case DecoderTreeNode::Decode: {
- const auto *Decode = static_cast<const DecodeNode *>(N);
- const InstructionEncoding &Encoding = Encodings[Decode->getEncodingID()];
- const Record *InstDef = Encoding.getInstruction()->TheDef;
- unsigned InstOpcode = Target.getInstrIntValue(InstDef);
- return 1 + getULEB128Size(InstOpcode) +
- getULEB128Size(Decode->getDecoderIndex());
- }
- }
- llvm_unreachable("Unknown node kind");
-}
-
-void DecoderTableEmitter::emitAnyOfNode(const AnyOfNode *N, indent Indent) {
- for (const DecoderTreeNode *Child : drop_end(N->children())) {
- emitOpcode("OPC_Scope");
- emitULEB128(computeNodeSize(Child));
-
- emitComment(Indent) << "{\n";
- emitNode(Child, Indent + 1);
- emitComment(Indent) << "}\n";
- }
-
- const DecoderTreeNode *Child = *std::prev(N->child_end());
- emitNode(Child, Indent);
-}
-
-void DecoderTableEmitter::emitAllOfNode(const AllOfNode *N, indent Indent) {
- 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[" << 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";
- }
-
- 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[" << 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);
- OS << "check Inst[" << MSB << ':' << LSB << "] == " << format_hex(Val, 0)
- << '\n';
-}
-
-void DecoderTableEmitter::emitCheckPredicateNode(const CheckPredicateNode *N,
- indent Indent) {
- unsigned PredicateIndex = N->getPredicateIndex();
-
- emitOpcode("OPC_CheckPredicate");
- emitULEB128(PredicateIndex);
-
- 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);
-
- emitComment(Indent) << "check softfail";
- OS << " pos=" << format_hex(PositiveMask, 10);
- OS << " neg=" << format_hex(NegativeMask, 10) << '\n';
-}
-
-void DecoderTableEmitter::emitDecodeNode(const DecodeNode *N, indent Indent) {
- const InstructionEncoding &Encoding = Encodings[N->getEncodingID()];
- const Record *InstDef = Encoding.getInstruction()->TheDef;
- unsigned InstOpcode = Target.getInstrIntValue(InstDef);
- unsigned DecoderIndex = N->getDecoderIndex();
-
- emitOpcode(Encoding.hasCompleteDecoder() ? "OPC_Decode" : "OPC_TryDecode");
- emitULEB128(InstOpcode);
- emitULEB128(DecoderIndex);
-
- emitComment(Indent);
- if (!Encoding.hasCompleteDecoder())
- OS << "try ";
- OS << "decode to " << Encoding.getName() << " using decoder " << DecoderIndex
- << '\n';
-}
-
-void DecoderTableEmitter::emitNode(const DecoderTreeNode *N, indent Indent) {
- switch (N->getKind()) {
- case DecoderTreeNode::AnyOf:
- return emitAnyOfNode(static_cast<const AnyOfNode *>(N), Indent);
- case DecoderTreeNode::AllOf:
- return emitAllOfNode(static_cast<const AllOfNode *>(N), Indent);
- case DecoderTreeNode::SwitchField:
- return emitSwitchFieldNode(static_cast<const SwitchFieldNode *>(N), Indent);
- case DecoderTreeNode::CheckField:
- return emitCheckFieldNode(static_cast<const CheckFieldNode *>(N), Indent);
- case DecoderTreeNode::CheckPredicate:
- HasCheckPredicate = true;
- return emitCheckPredicateNode(static_cast<const CheckPredicateNode *>(N),
- Indent);
- case DecoderTreeNode::SoftFail:
- HasSoftFail = true;
- return emitSoftFailNode(static_cast<const SoftFailNode *>(N), Indent);
- case DecoderTreeNode::Decode:
- HasTryDecode |=
- Encodings[static_cast<const DecodeNode *>(N)->getEncodingID()]
- .hasCompleteDecoder();
- return emitDecodeNode(static_cast<const DecodeNode *>(N), Indent);
- }
- llvm_unreachable("Unknown node kind");
-}
-
-void DecoderTableEmitter::emitTable(StringRef TableName, unsigned BitWidth,
- const DecoderTreeNode *Root) {
- unsigned TableSize = computeTableSize(BitWidth, Root);
- 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 (SpecializeDecodersPerBitwidth) {
- 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";
-}
-
// emitDecodeInstruction - Emit the templated helper function
// decodeInstruction().
static void emitDecodeInstruction(formatted_raw_ostream &OS, bool IsVarLenInst,
@@ -2180,9 +714,7 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
// 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;
- DecoderTreeBuilder TreeBuilder(Target, Encodings, TableInfo);
- DecoderTableEmitter TableEmitter(Target, Encodings, OS);
+ DecoderTableInfo TableInfo{};
// Emit a table for each (namespace, hwmode, width) combination.
// If `SpecializeDecodersPerBitwidth` is enabled, emit a decoder function
@@ -2193,13 +725,12 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
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<DecoderTreeNode> Tree =
+ ::buildDecoderTree(Target, Encodings, EncodingIDs, TableInfo);
+ if (!Tree) {
+ HasConflict = true;
continue;
-
- std::unique_ptr<DecoderTreeNode> Root = TreeBuilder.buildTree(FC);
+ }
SmallString<32> TableName("DecoderTable");
TableName.append(DecoderNamespace);
@@ -2208,7 +739,8 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
TableName.append(std::to_string(BitWidth));
// Serialize the tree.
- TableEmitter.emitTable(TableName, BitWidth, Root.get());
+ ::emitDecoderTable(OS, Target, TableInfo, Encodings, TableName, BitWidth,
+ Tree.get());
}
// Each BitWidth get's its own decoders and decoder function if
@@ -2228,13 +760,12 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
emitDecoderFunction(OS, TableInfo.Decoders, 0);
// Emit the predicate function.
- if (TableEmitter.hasCheckPredicate())
+ if (TableInfo.HasCheckPredicate)
emitPredicateFunction(OS, TableInfo.Predicates);
// Emit the main entry point for the decoder, decodeInstruction().
- emitDecodeInstruction(OS, IsVarLenInst, TableEmitter.hasCheckPredicate(),
- TableEmitter.hasSoftFail(),
- TableEmitter.hasTryDecode());
+ emitDecodeInstruction(OS, IsVarLenInst, TableInfo.HasCheckPredicate,
+ TableInfo.HasSoftFail, TableInfo.HasTryDecode);
OS << "\n} // namespace\n";
}
diff --git a/llvm/utils/TableGen/DecoderTree.cpp b/llvm/utils/TableGen/DecoderTree.cpp
new file mode 100644
index 0000000000000..47740b5e57c7d
--- /dev/null
+++ b/llvm/utils/TableGen/DecoderTree.cpp
@@ -0,0 +1,11 @@
+//===----------------------------------------------------------------------===//
+//
+// 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;
diff --git a/llvm/utils/TableGen/DecoderTree.h b/llvm/utils/TableGen/DecoderTree.h
new file mode 100644
index 0000000000000..87b59f83d787a
--- /dev/null
+++ b/llvm/utils/TableGen/DecoderTree.h
@@ -0,0 +1,224 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <map>
+#include <memory>
+
+namespace llvm {
+
+class CodeGenTarget;
+class InstructionEncoding;
+class raw_ostream;
+
+class DecoderTreeNode {
+public:
+ virtual ~DecoderTreeNode() = default;
+
+ enum KindTy {
+ AnyOf,
+ AllOf,
+ CheckField,
+ SwitchField,
+ CheckPredicate,
+ SoftFail,
+ Decode,
+ };
+
+ KindTy getKind() const { return Kind; }
+
+protected:
+ explicit DecoderTreeNode(KindTy Kind) : Kind(Kind) {}
+
+private:
+ KindTy Kind;
+};
+
+class AllOfNode : public DecoderTreeNode {
+ SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
+
+ static const DecoderTreeNode *
+ mapElement(decltype(Children)::const_reference Element) {
+ return Element.get();
+ }
+
+public:
+ AllOfNode() : DecoderTreeNode(AllOf) {}
+
+ void addChild(std::unique_ptr<DecoderTreeNode> Child) {
+ Children.push_back(std::move(Child));
+ }
+
+ using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
+ decltype(&mapElement)>;
+
+ child_iterator child_begin() const {
+ return child_iterator(Children.begin(), mapElement);
+ }
+
+ child_iterator child_end() const {
+ return child_iterator(Children.end(), mapElement);
+ }
+
+ iterator_range<child_iterator> children() const {
+ return make_range(child_begin(), child_end());
+ }
+};
+
+class AnyOfNode : public DecoderTreeNode {
+ SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
+
+ static const DecoderTreeNode *
+ mapElement(decltype(Children)::const_reference Element) {
+ return Element.get();
+ }
+
+public:
+ AnyOfNode() : DecoderTreeNode(AnyOf) {}
+
+ void addChild(std::unique_ptr<DecoderTreeNode> N) {
+ Children.push_back(std::move(N));
+ }
+
+ using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
+ decltype(&mapElement)>;
+
+ child_iterator child_begin() const {
+ return child_iterator(Children.begin(), mapElement);
+ }
+
+ child_iterator child_end() const {
+ return child_iterator(Children.end(), mapElement);
+ }
+
+ iterator_range<child_iterator> children() const {
+ return make_range(child_begin(), child_end());
+ }
+};
+
+class SwitchFieldNode : public DecoderTreeNode {
+ unsigned StartBit;
+ unsigned NumBits;
+ std::map<uint64_t, std::unique_ptr<DecoderTreeNode>> Cases;
+
+ static std::pair<uint64_t, const DecoderTreeNode *>
+ 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<DecoderTreeNode> N) {
+ Cases.try_emplace(Value, std::move(N));
+ }
+
+ unsigned getStartBit() const { return StartBit; }
+
+ unsigned getNumBits() const { return NumBits; }
+
+ using case_iterator =
+ mapped_iterator<decltype(Cases)::const_iterator, decltype(&mapElement)>;
+
+ case_iterator case_begin() const {
+ return case_iterator(Cases.begin(), mapElement);
+ }
+
+ case_iterator case_end() const {
+ return case_iterator(Cases.end(), mapElement);
+ }
+
+ iterator_range<case_iterator> cases() const {
+ return make_range(case_begin(), case_end());
+ }
+};
+
+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; }
+};
+
+class CheckPredicateNode : public DecoderTreeNode {
+ unsigned Index;
+
+public:
+ explicit CheckPredicateNode(unsigned Index)
+ : DecoderTreeNode(CheckPredicate), Index(Index) {}
+
+ unsigned getPredicateIndex() const { return Index; }
+};
+
+class SoftFailNode : public DecoderTreeNode {
+ uint64_t PositiveMask, NegativeMask;
+
+public:
+ explicit 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; }
+};
+
+class DecodeNode : public DecoderTreeNode {
+ unsigned EncodingID;
+ unsigned Index;
+
+public:
+ DecodeNode(unsigned EncodingID, unsigned Index)
+ : DecoderTreeNode(Decode), EncodingID(EncodingID), Index(Index) {}
+
+ unsigned getEncodingID() const { return EncodingID; }
+
+ unsigned getDecoderIndex() const { return Index; }
+};
+
+using PredicateSet = SmallSetVector<CachedHashString, 16>;
+using DecoderSet = SmallSetVector<CachedHashString, 16>;
+
+struct DecoderTableInfo {
+ PredicateSet Predicates;
+ DecoderSet Decoders;
+ bool HasCheckPredicate;
+ bool HasSoftFail;
+ bool HasTryDecode;
+};
+
+std::unique_ptr<DecoderTreeNode>
+buildDecoderTree(const CodeGenTarget &Target,
+ ArrayRef<InstructionEncoding> Encodings,
+ ArrayRef<unsigned> EncodingIDs, DecoderTableInfo &TableInfo);
+
+void emitDecoderTable(raw_ostream &OS, const CodeGenTarget &Target,
+ DecoderTableInfo &TableInfo,
+ ArrayRef<InstructionEncoding> Encodings,
+ StringRef TableName, unsigned BitWidth,
+ const DecoderTreeNode *Tree);
+
+} // namespace llvm
+
+#endif // LLVM_UTILS_TABLEGEN_DECODERTREE_H
diff --git a/llvm/utils/TableGen/DecoderTreeBuilder.cpp b/llvm/utils/TableGen/DecoderTreeBuilder.cpp
new file mode 100644
index 0000000000000..5833ec9ad0e0d
--- /dev/null
+++ b/llvm/utils/TableGen/DecoderTreeBuilder.cpp
@@ -0,0 +1,988 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "Common/CodeGenTarget.h"
+#include "Common/InstructionEncoding.h"
+#include "DecoderTree.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TableGen/Error.h"
+
+namespace llvm {
+extern cl::opt<bool> UseFnTableInDecodeToMCInst;
+extern cl::opt<bool> IgnoreNonDecodableOperands;
+extern cl::opt<bool> IgnoreFullyDefinedOperands;
+} // namespace llvm
+
+using namespace llvm;
+
+namespace {
+
+/// Filter - Filter works with FilterChooser to produce the decoding tree for
+/// the ISA.
+///
+/// It is useful to think of a Filter as governing the switch stmts of the
+/// decoding tree in a certain level. Each case stmt delegates to an inferior
+/// FilterChooser to decide what further decoding logic to employ, or in another
+/// words, what other remaining bits to look at. The FilterChooser eventually
+/// chooses a best Filter to do its job.
+///
+/// This recursive scheme ends when the number of Opcodes assigned to the
+/// FilterChooser becomes 1 or if there is a conflict. A conflict happens when
+/// the Filter/FilterChooser combo does not know how to distinguish among the
+/// Opcodes assigned.
+///
+/// An example of a conflict is
+///
+/// Decoding Conflict:
+/// ................................
+/// 1111............................
+/// 1111010.........................
+/// 1111010...00....................
+/// 1111010...00........0001........
+/// 111101000.00........0001........
+/// 111101000.00........00010000....
+/// 111101000_00________00010000____ VST4q8a
+/// 111101000_00________00010000____ VST4q8b
+///
+/// The Debug output shows the path that the decoding tree follows to reach the
+/// the conclusion that there is a conflict. VST4q8a is a vst4 to double-spaced
+/// even registers, while VST4q8b is a vst4 to double-spaced odd registers.
+///
+/// The encoding info in the .td files does not specify this meta information,
+/// which could have been used by the decoder to resolve the conflict. The
+/// decoder could try to decode the even/odd register numbering and assign to
+/// VST4q8a or VST4q8b, but for the time being, the decoder chooses the "a"
+/// version and return the Opcode since the two have the same Asm format string.
+struct Filter {
+ unsigned StartBit; // the starting bit position
+ unsigned NumBits; // number of bits to filter
+
+ // Map of well-known segment value to the set of uid's with that value.
+ std::map<uint64_t, std::vector<unsigned>> FilteredIDs;
+
+ // Set of uid's with non-constant segment values.
+ std::vector<unsigned> VariableIDs;
+
+ Filter(ArrayRef<InstructionEncoding> Encodings,
+ ArrayRef<unsigned> EncodingIDs, unsigned StartBit, unsigned NumBits);
+
+ // Returns the number of fanout produced by the filter. More fanout implies
+ // the filter distinguishes more categories of instructions.
+ unsigned usefulness() const {
+ return FilteredIDs.size() + VariableIDs.empty();
+ }
+};
+
+} // namespace
+
+Filter::Filter(ArrayRef<InstructionEncoding> Encodings,
+ ArrayRef<unsigned> EncodingIDs, unsigned StartBit,
+ unsigned NumBits)
+ : StartBit(StartBit), NumBits(NumBits) {
+ for (unsigned EncodingID : EncodingIDs) {
+ const InstructionEncoding &Encoding = Encodings[EncodingID];
+ KnownBits EncodingBits = Encoding.getMandatoryBits();
+
+ // Scans the segment for possibly well-specified encoding bits.
+ KnownBits FieldBits = EncodingBits.extractBits(NumBits, StartBit);
+
+ if (FieldBits.isConstant()) {
+ // The encoding bits are well-known. Lets add the uid of the
+ // instruction into the bucket keyed off the constant field value.
+ FilteredIDs[FieldBits.getConstant().getZExtValue()].push_back(EncodingID);
+ } else {
+ // Some of the encoding bit(s) are unspecified. This contributes to
+ // one additional member of "Variable" instructions.
+ VariableIDs.push_back(EncodingID);
+ }
+ }
+
+ assert((FilteredIDs.size() + VariableIDs.size() > 0) &&
+ "Filter returns no instruction categories");
+}
+
+namespace {
+
+// These are states of our finite state machines used in FilterChooser's
+// filterProcessor() which produces the filter candidates to use.
+enum bitAttr_t {
+ ATTR_NONE,
+ ATTR_FILTERED,
+ ATTR_ALL_SET,
+ ATTR_ALL_UNSET,
+ ATTR_MIXED
+};
+
+/// FilterChooser - FilterChooser chooses the best filter among a set of Filters
+/// in order to perform the decoding of instructions at the current level.
+///
+/// Decoding proceeds from the top down. Based on the well-known encoding bits
+/// of instructions available, FilterChooser builds up the possible Filters that
+/// can further the task of decoding by distinguishing among the remaining
+/// candidate instructions.
+///
+/// Once a filter has been chosen, it is called upon to divide the decoding task
+/// into sub-tasks and delegates them to its inferior FilterChoosers for further
+/// processings.
+///
+/// It is useful to think of a Filter as governing the switch stmts of the
+/// decoding tree. And each case is delegated to an inferior FilterChooser to
+/// decide what further remaining bits to look at.
+
+class FilterChooser {
+ // TODO: Unfriend by providing the necessary accessors.
+ friend class DecoderTreeBuilder;
+
+ // Vector of encodings to choose our filter.
+ ArrayRef<InstructionEncoding> Encodings;
+
+ /// Encoding IDs for this filter chooser to work on.
+ /// Sorted by non-decreasing encoding width.
+ SmallVector<unsigned, 0> EncodingIDs;
+
+ // Array of bit values passed down from our parent.
+ // Set to all unknown for Parent == nullptr.
+ KnownBits FilterBits;
+
+ // Links to the FilterChooser above us in the decoding tree.
+ const FilterChooser *Parent;
+
+ /// If the selected filter matches multiple encodings, then this is the
+ /// starting position and the width of the filtered range.
+ unsigned StartBit;
+ unsigned NumBits;
+
+ /// If the selected filter matches multiple encodings, and there is
+ /// *exactly one* encoding in which all bits are known in the filtered range,
+ /// then this is the ID of that encoding.
+ /// Also used when there is only one encoding.
+ std::optional<unsigned> SingletonEncodingID;
+
+ /// 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
+ /// contain some unknown bits in the filtered range.
+ std::unique_ptr<const FilterChooser> VariableFC;
+
+ /// If the selected filter matches multiple encodings, and there is
+ /// *more than one* encoding in which all bits are known in the filtered
+ /// range, then this is a map of field values to FilterChoosers created for
+ /// the subset of encodings sharing that field value.
+ /// The "field value" here refers to the encoding bits in the filtered range.
+ std::map<uint64_t, std::unique_ptr<const FilterChooser>> FilterChooserMap;
+
+ /// Set to true if decoding conflict was encountered.
+ bool HasConflict = false;
+
+ struct Island {
+ unsigned StartBit;
+ unsigned NumBits;
+ uint64_t FieldVal;
+ };
+
+public:
+ /// Constructs a top-level filter chooser.
+ FilterChooser(ArrayRef<InstructionEncoding> Encodings,
+ ArrayRef<unsigned> EncodingIDs);
+
+ /// Constructs an inferior filter chooser.
+ FilterChooser(ArrayRef<InstructionEncoding> Encodings,
+ ArrayRef<unsigned> EncodingIDs, const KnownBits &FilterBits,
+ const FilterChooser &Parent);
+
+ /// Returns the width of the largest encoding.
+ unsigned getMaxEncodingWidth() const {
+ // The last encoding ID is the ID of an encoding with the largest width.
+ return Encodings[EncodingIDs.back()].getBitWidth();
+ }
+
+ /// Returns true if any decoding conflicts were encountered.
+ bool hasConflict() const { return HasConflict; }
+
+private:
+ /// Applies the given filter to the set of encodings this FilterChooser
+ /// works with, creating inferior FilterChoosers as necessary.
+ void applyFilter(const Filter &F);
+
+ /// 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;
+
+ bool isPositionFiltered(unsigned Idx) const {
+ return FilterBits.Zero[Idx] || FilterBits.One[Idx];
+ }
+
+ // Calculates the island(s) needed to decode the instruction.
+ // This returns a list of undecoded bits of an instructions, for example,
+ // Inst{20} = 1 && Inst{3-0} == 0b1111 represents two islands of yet-to-be
+ // decoded bits in order to verify that the instruction matches the Opcode.
+ static std::vector<Island> getIslands(const KnownBits &EncodingBits,
+ const KnownBits &FilterBits);
+
+ /// Scans the well-known encoding bits of the encodings and, builds up a list
+ /// of candidate filters, and then returns the best one, if any.
+ std::unique_ptr<Filter> findBestFilter(ArrayRef<bitAttr_t> BitAttrs,
+ bool AllowMixed,
+ bool Greedy = true) const;
+
+ std::unique_ptr<Filter> findBestFilter() const;
+
+ // 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.
+ void doFilter();
+
+public:
+ void dump() const;
+};
+
+/// Sorting predicate to sort encoding IDs by encoding width.
+class LessEncodingIDByWidth {
+ ArrayRef<InstructionEncoding> Encodings;
+
+public:
+ explicit LessEncodingIDByWidth(ArrayRef<InstructionEncoding> Encodings)
+ : Encodings(Encodings) {}
+
+ bool operator()(unsigned ID1, unsigned ID2) const {
+ return Encodings[ID1].getBitWidth() < Encodings[ID2].getBitWidth();
+ }
+};
+
+} // namespace
+
+FilterChooser::FilterChooser(ArrayRef<InstructionEncoding> Encodings,
+ ArrayRef<unsigned int> EncodingIDs)
+ : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(nullptr) {
+ // Sort encoding IDs once.
+ stable_sort(this->EncodingIDs, LessEncodingIDByWidth(Encodings));
+ // Filter width is the width of the smallest encoding.
+ unsigned FilterWidth = Encodings[this->EncodingIDs.front()].getBitWidth();
+ FilterBits = KnownBits(FilterWidth);
+ doFilter();
+}
+
+FilterChooser::FilterChooser(ArrayRef<InstructionEncoding> Encodings,
+ ArrayRef<unsigned int> EncodingIDs,
+ const KnownBits &FilterBits,
+ const FilterChooser &Parent)
+ : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(&Parent) {
+ // Inferior filter choosers are created from sorted array of encoding IDs.
+ assert(is_sorted(EncodingIDs, LessEncodingIDByWidth(Encodings)));
+ assert(!FilterBits.hasConflict() && "Broken filter");
+ // Filter width is the width of the smallest encoding.
+ unsigned FilterWidth = Encodings[EncodingIDs.front()].getBitWidth();
+ this->FilterBits = FilterBits.anyext(FilterWidth);
+ doFilter();
+}
+
+void FilterChooser::applyFilter(const Filter &F) {
+ StartBit = F.StartBit;
+ NumBits = F.NumBits;
+ assert(FilterBits.extractBits(NumBits, StartBit).isUnknown());
+
+ if (!F.VariableIDs.empty()) {
+ // Delegates to an inferior filter chooser for further processing on this
+ // group of instructions whose segment values are variable.
+ VariableFC = std::make_unique<FilterChooser>(Encodings, F.VariableIDs,
+ FilterBits, *this);
+ HasConflict |= VariableFC->HasConflict;
+ }
+
+ // Otherwise, create sub choosers.
+ for (const auto &[FilterVal, InferiorEncodingIDs] : F.FilteredIDs) {
+ // Create a new filter by inserting the field bits into the parent filter.
+ APInt FieldBits(NumBits, FilterVal);
+ KnownBits InferiorFilterBits = FilterBits;
+ InferiorFilterBits.insertBits(KnownBits::makeConstant(FieldBits), StartBit);
+
+ // Delegates to an inferior filter chooser for further processing on this
+ // category of instructions.
+ auto [It, _] = FilterChooserMap.try_emplace(
+ FilterVal,
+ std::make_unique<FilterChooser>(Encodings, InferiorEncodingIDs,
+ InferiorFilterBits, *this));
+ HasConflict |= It->second->HasConflict;
+ }
+}
+
+/// 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,
+ char Unknown) {
+ for (unsigned I = Bits.getBitWidth(); I--;) {
+ if (Bits.Zero[I] && Bits.One[I])
+ OS << '!';
+ else if (Bits.Zero[I])
+ OS << '0';
+ else if (Bits.One[I])
+ OS << '1';
+ else
+ OS << Unknown;
+ }
+}
+
+/// dumpStack - dumpStack traverses the filter chooser chain and calls
+/// dumpFilterArray on each filter chooser up to the top level one.
+void FilterChooser::dumpStack(raw_ostream &OS, indent Indent,
+ unsigned PadToWidth) const {
+ if (Parent)
+ Parent->dumpStack(OS, Indent, PadToWidth);
+ assert(PadToWidth >= FilterBits.getBitWidth());
+ OS << Indent << indent(PadToWidth - FilterBits.getBitWidth());
+ printKnownBits(OS, FilterBits, '.');
+ OS << '\n';
+}
+
+// Calculates the island(s) needed to decode the instruction.
+// This returns a list of undecoded bits of an instructions, for example,
+// Inst{20} = 1 && Inst{3-0} == 0b1111 represents two islands of yet-to-be
+// decoded bits in order to verify that the instruction matches the Opcode.
+std::vector<FilterChooser::Island>
+FilterChooser::getIslands(const KnownBits &EncodingBits,
+ const KnownBits &FilterBits) {
+ std::vector<Island> Islands;
+ uint64_t FieldVal;
+ unsigned StartBit;
+
+ // 0: Init
+ // 1: Water (the bit value does not affect decoding)
+ // 2: Island (well-known bit value needed for decoding)
+ unsigned State = 0;
+
+ unsigned FilterWidth = FilterBits.getBitWidth();
+ for (unsigned i = 0; i != FilterWidth; ++i) {
+ bool IsKnown = EncodingBits.Zero[i] || EncodingBits.One[i];
+ bool Filtered = FilterBits.Zero[i] || FilterBits.One[i];
+ switch (State) {
+ default:
+ llvm_unreachable("Unreachable code!");
+ case 0:
+ case 1:
+ if (Filtered || !IsKnown) {
+ State = 1; // Still in Water
+ } else {
+ State = 2; // Into the Island
+ StartBit = i;
+ FieldVal = static_cast<uint64_t>(EncodingBits.One[i]);
+ }
+ break;
+ case 2:
+ if (Filtered || !IsKnown) {
+ State = 1; // Into the Water
+ Islands.push_back({StartBit, i - StartBit, FieldVal});
+ } else {
+ State = 2; // Still in Island
+ FieldVal |= static_cast<uint64_t>(EncodingBits.One[i])
+ << (i - StartBit);
+ }
+ break;
+ }
+ }
+ // If we are still in Island after the loop, do some housekeeping.
+ if (State == 2)
+ Islands.push_back({StartBit, FilterWidth - StartBit, FieldVal});
+
+ return Islands;
+}
+
+std::unique_ptr<Filter>
+FilterChooser::findBestFilter(ArrayRef<bitAttr_t> BitAttrs, bool AllowMixed,
+ bool Greedy) const {
+ assert(EncodingIDs.size() >= 2 && "Nothing to filter");
+
+ // Heuristics. See also doFilter()'s "Heuristics" comment when num of
+ // instructions is 3.
+ if (AllowMixed && !Greedy) {
+ assert(EncodingIDs.size() == 3);
+
+ for (unsigned EncodingID : EncodingIDs) {
+ const InstructionEncoding &Encoding = Encodings[EncodingID];
+ KnownBits EncodingBits = Encoding.getMandatoryBits();
+
+ // Look for islands of undecoded bits of any instruction.
+ std::vector<Island> Islands = getIslands(EncodingBits, FilterBits);
+ if (!Islands.empty()) {
+ // Found an instruction with island(s). Now just assign a filter.
+ return std::make_unique<Filter>(
+ Encodings, EncodingIDs, Islands[0].StartBit, Islands[0].NumBits);
+ }
+ }
+ }
+
+ // The regionAttr automaton consumes the bitAttrs automatons' state,
+ // lowest-to-highest.
+ //
+ // Input symbols: F(iltered), (all_)S(et), (all_)U(nset), M(ixed)
+ // States: NONE, ALL_SET, MIXED
+ // Initial state: NONE
+ //
+ // (NONE) ----- F --> (NONE)
+ // (NONE) ----- S --> (ALL_SET) ; and set region start
+ // (NONE) ----- U --> (NONE)
+ // (NONE) ----- M --> (MIXED) ; and set region start
+ // (ALL_SET) -- F --> (NONE) ; and report an ALL_SET region
+ // (ALL_SET) -- S --> (ALL_SET)
+ // (ALL_SET) -- U --> (NONE) ; and report an ALL_SET region
+ // (ALL_SET) -- M --> (MIXED) ; and report an ALL_SET region
+ // (MIXED) ---- F --> (NONE) ; and report a MIXED region
+ // (MIXED) ---- S --> (ALL_SET) ; and report a MIXED region
+ // (MIXED) ---- U --> (NONE) ; and report a MIXED region
+ // (MIXED) ---- M --> (MIXED)
+
+ bitAttr_t RA = ATTR_NONE;
+ unsigned StartBit = 0;
+
+ std::vector<std::unique_ptr<Filter>> Filters;
+
+ auto addCandidateFilter = [&](unsigned StartBit, unsigned EndBit) {
+ Filters.push_back(std::make_unique<Filter>(Encodings, EncodingIDs, StartBit,
+ EndBit - StartBit));
+ };
+
+ unsigned FilterWidth = FilterBits.getBitWidth();
+ for (unsigned BitIndex = 0; BitIndex != FilterWidth; ++BitIndex) {
+ bitAttr_t bitAttr = BitAttrs[BitIndex];
+
+ assert(bitAttr != ATTR_NONE && "Bit without attributes");
+
+ switch (RA) {
+ case ATTR_NONE:
+ switch (bitAttr) {
+ case ATTR_FILTERED:
+ break;
+ case ATTR_ALL_SET:
+ StartBit = BitIndex;
+ RA = ATTR_ALL_SET;
+ break;
+ case ATTR_ALL_UNSET:
+ break;
+ case ATTR_MIXED:
+ StartBit = BitIndex;
+ RA = ATTR_MIXED;
+ break;
+ default:
+ llvm_unreachable("Unexpected bitAttr!");
+ }
+ break;
+ case ATTR_ALL_SET:
+ if (!AllowMixed && bitAttr != ATTR_ALL_SET)
+ addCandidateFilter(StartBit, BitIndex);
+ switch (bitAttr) {
+ case ATTR_FILTERED:
+ RA = ATTR_NONE;
+ break;
+ case ATTR_ALL_SET:
+ break;
+ case ATTR_ALL_UNSET:
+ RA = ATTR_NONE;
+ break;
+ case ATTR_MIXED:
+ StartBit = BitIndex;
+ RA = ATTR_MIXED;
+ break;
+ default:
+ llvm_unreachable("Unexpected bitAttr!");
+ }
+ break;
+ case ATTR_MIXED:
+ if (AllowMixed && bitAttr != ATTR_MIXED)
+ addCandidateFilter(StartBit, BitIndex);
+ switch (bitAttr) {
+ case ATTR_FILTERED:
+ StartBit = BitIndex;
+ RA = ATTR_NONE;
+ break;
+ case ATTR_ALL_SET:
+ StartBit = BitIndex;
+ RA = ATTR_ALL_SET;
+ break;
+ case ATTR_ALL_UNSET:
+ RA = ATTR_NONE;
+ break;
+ case ATTR_MIXED:
+ break;
+ default:
+ llvm_unreachable("Unexpected bitAttr!");
+ }
+ break;
+ case ATTR_ALL_UNSET:
+ llvm_unreachable("regionAttr state machine has no ATTR_UNSET state");
+ case ATTR_FILTERED:
+ llvm_unreachable("regionAttr state machine has no ATTR_FILTERED state");
+ }
+ }
+
+ // At the end, if we're still in ALL_SET or MIXED states, report a region
+ switch (RA) {
+ case ATTR_NONE:
+ break;
+ case ATTR_FILTERED:
+ break;
+ case ATTR_ALL_SET:
+ if (!AllowMixed)
+ addCandidateFilter(StartBit, FilterWidth);
+ break;
+ case ATTR_ALL_UNSET:
+ break;
+ case ATTR_MIXED:
+ if (AllowMixed)
+ addCandidateFilter(StartBit, FilterWidth);
+ break;
+ }
+
+ // We have finished with the filter processings. Now it's time to choose
+ // the best performing filter.
+ auto MaxIt = llvm::max_element(Filters, [](const std::unique_ptr<Filter> &A,
+ const std::unique_ptr<Filter> &B) {
+ return A->usefulness() < B->usefulness();
+ });
+ if (MaxIt == Filters.end() || (*MaxIt)->usefulness() == 0)
+ return nullptr;
+ return std::move(*MaxIt);
+}
+
+std::unique_ptr<Filter> FilterChooser::findBestFilter() const {
+ // We maintain BIT_WIDTH copies of the bitAttrs automaton.
+ // The automaton consumes the corresponding bit from each
+ // instruction.
+ //
+ // Input symbols: 0, 1, _ (unset), and . (any of the above).
+ // States: NONE, FILTERED, ALL_SET, ALL_UNSET, and MIXED.
+ // Initial state: NONE.
+ //
+ // (NONE) ------- [01] -> (ALL_SET)
+ // (NONE) ------- _ ----> (ALL_UNSET)
+ // (ALL_SET) ---- [01] -> (ALL_SET)
+ // (ALL_SET) ---- _ ----> (MIXED)
+ // (ALL_UNSET) -- [01] -> (MIXED)
+ // (ALL_UNSET) -- _ ----> (ALL_UNSET)
+ // (MIXED) ------ . ----> (MIXED)
+ // (FILTERED)---- . ----> (FILTERED)
+
+ unsigned FilterWidth = FilterBits.getBitWidth();
+ SmallVector<bitAttr_t, 128> BitAttrs(FilterWidth, ATTR_NONE);
+
+ // FILTERED bit positions provide no entropy and are not worthy of pursuing.
+ // Filter::recurse() set either 1 or 0 for each position.
+ for (unsigned BitIndex = 0; BitIndex != FilterWidth; ++BitIndex)
+ if (isPositionFiltered(BitIndex))
+ BitAttrs[BitIndex] = ATTR_FILTERED;
+
+ for (unsigned EncodingID : EncodingIDs) {
+ const InstructionEncoding &Encoding = Encodings[EncodingID];
+ KnownBits EncodingBits = Encoding.getMandatoryBits();
+
+ for (unsigned BitIndex = 0; BitIndex != FilterWidth; ++BitIndex) {
+ bool IsKnown = EncodingBits.Zero[BitIndex] || EncodingBits.One[BitIndex];
+ switch (BitAttrs[BitIndex]) {
+ case ATTR_NONE:
+ if (IsKnown)
+ BitAttrs[BitIndex] = ATTR_ALL_SET;
+ else
+ BitAttrs[BitIndex] = ATTR_ALL_UNSET;
+ break;
+ case ATTR_ALL_SET:
+ if (!IsKnown)
+ BitAttrs[BitIndex] = ATTR_MIXED;
+ break;
+ case ATTR_ALL_UNSET:
+ if (IsKnown)
+ BitAttrs[BitIndex] = ATTR_MIXED;
+ break;
+ case ATTR_MIXED:
+ case ATTR_FILTERED:
+ break;
+ }
+ }
+ }
+
+ // Try regions of consecutive known bit values first.
+ if (std::unique_ptr<Filter> F =
+ findBestFilter(BitAttrs, /*AllowMixed=*/false))
+ return F;
+
+ // Then regions of mixed bits (both known and unitialized bit values allowed).
+ if (std::unique_ptr<Filter> F = findBestFilter(BitAttrs, /*AllowMixed=*/true))
+ return F;
+
+ // Heuristics to cope with conflict set {t2CMPrs, t2SUBSrr, t2SUBSrs} where
+ // no single instruction for the maximum ATTR_MIXED region Inst{14-4} has a
+ // well-known encoding pattern. In such case, we backtrack and scan for the
+ // the very first consecutive ATTR_ALL_SET region and assign a filter to it.
+ if (EncodingIDs.size() == 3) {
+ if (std::unique_ptr<Filter> F =
+ findBestFilter(BitAttrs, /*AllowMixed=*/true, /*Greedy=*/false))
+ return F;
+ }
+
+ // There is a conflict we could not resolve.
+ return nullptr;
+}
+
+// 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.
+void FilterChooser::doFilter() {
+ assert(!EncodingIDs.empty() && "FilterChooser created with no instructions");
+
+ // No filter needed.
+ if (EncodingIDs.size() == 1) {
+ SingletonEncodingID = EncodingIDs.front();
+ return;
+ }
+
+ std::unique_ptr<Filter> BestFilter = findBestFilter();
+ if (BestFilter) {
+ applyFilter(*BestFilter);
+ return;
+ }
+
+ // Print out useful conflict information for postmortem analysis.
+ errs() << "Decoding Conflict:\n";
+ dump();
+ HasConflict = true;
+}
+
+void FilterChooser::dump() const {
+ indent Indent(4);
+ // Helps to keep the output right-justified.
+ unsigned PadToWidth = getMaxEncodingWidth();
+
+ // Dump filter stack.
+ dumpStack(errs(), Indent, PadToWidth);
+
+ // Dump encodings.
+ for (unsigned EncodingID : EncodingIDs) {
+ const InstructionEncoding &Encoding = Encodings[EncodingID];
+ errs() << Indent << indent(PadToWidth - Encoding.getBitWidth());
+ printKnownBits(errs(), Encoding.getMandatoryBits(), '_');
+ errs() << " " << Encoding.getName() << '\n';
+ }
+}
+
+namespace {
+
+class DecoderTreeBuilder {
+ const CodeGenTarget &Target;
+ ArrayRef<InstructionEncoding> Encodings;
+ DecoderTableInfo &TableInfo;
+
+public:
+ DecoderTreeBuilder(const CodeGenTarget &Target,
+ ArrayRef<InstructionEncoding> Encodings,
+ DecoderTableInfo &TableInfo)
+ : Target(Target), Encodings(Encodings), TableInfo(TableInfo) {}
+
+ std::unique_ptr<DecoderTreeNode> buildTree(const FilterChooser &FC) {
+ return buildAnyOfNode(FC);
+ }
+
+private:
+ static void emitBinaryParser(raw_ostream &OS, indent Indent,
+ const InstructionEncoding &Encoding,
+ const OperandInfo &OpInfo);
+ static void emitDecoder(raw_ostream &OS, indent Indent,
+ const InstructionEncoding &Encoding);
+ unsigned getDecoderIndex(const InstructionEncoding &Encoding);
+
+ static bool emitPredicateMatchAux(StringRef PredicateNamespace,
+ const Init &Val, bool ParenIfBinOp,
+ raw_ostream &OS);
+ static bool emitPredicateMatch(StringRef PredicateNamespace, raw_ostream &OS,
+ const InstructionEncoding &Encoding);
+ unsigned getPredicateIndex(const InstructionEncoding &Encoding) const;
+
+ std::unique_ptr<DecoderTreeNode>
+ buildTerminalNode(unsigned EncodingID, const KnownBits &FilterBits);
+
+ std::unique_ptr<DecoderTreeNode> buildAllOfOrSwitchNode(
+ unsigned StartBit, unsigned NumBits,
+ const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap);
+
+ std::unique_ptr<DecoderTreeNode> buildAnyOfNode(const FilterChooser &FC);
+};
+
+} // namespace
+
+static bool doesOpcodeNeedPredicate(const InstructionEncoding &Encoding);
+
+std::unique_ptr<DecoderTreeNode>
+DecoderTreeBuilder::buildTerminalNode(unsigned EncodingID,
+ const KnownBits &FilterBits) {
+ const InstructionEncoding &Encoding = Encodings[EncodingID];
+ auto N = std::make_unique<AllOfNode>();
+
+ if (doesOpcodeNeedPredicate(Encoding)) {
+ unsigned PredicateIndex = getPredicateIndex(Encoding);
+ N->addChild(std::make_unique<CheckPredicateNode>(PredicateIndex));
+ }
+
+ std::vector<FilterChooser::Island> Islands =
+ FilterChooser::getIslands(Encoding.getMandatoryBits(), FilterBits);
+ for (const FilterChooser::Island &Ilnd : reverse(Islands)) {
+ N->addChild(std::make_unique<CheckFieldNode>(Ilnd.StartBit, Ilnd.NumBits,
+ Ilnd.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<SoftFailNode>(PositiveMask.getZExtValue(),
+ NegativeMask.getZExtValue()));
+ }
+
+ unsigned DecoderIndex = getDecoderIndex(Encoding);
+ N->addChild(std::make_unique<DecodeNode>(EncodingID, DecoderIndex));
+
+ return N;
+}
+
+std::unique_ptr<DecoderTreeNode> DecoderTreeBuilder::buildAllOfOrSwitchNode(
+ unsigned int StartBit, unsigned int NumBits,
+ const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap) {
+ if (FCMap.size() == 1) {
+ const auto &[FieldVal, ChildFC] = *FCMap.begin();
+ auto N = std::make_unique<AllOfNode>();
+ N->addChild(std::make_unique<CheckFieldNode>(StartBit, NumBits, FieldVal));
+ N->addChild(buildAnyOfNode(*ChildFC));
+ return N;
+ }
+ auto N = std::make_unique<SwitchFieldNode>(StartBit, NumBits);
+ for (const auto &[FieldVal, ChildFC] : FCMap)
+ N->addCase(FieldVal, buildAnyOfNode(*ChildFC));
+ return N;
+}
+
+std::unique_ptr<DecoderTreeNode>
+DecoderTreeBuilder::buildAnyOfNode(const FilterChooser &FC) {
+ auto N = std::make_unique<AnyOfNode>();
+ if (FC.SingletonEncodingID) {
+ N->addChild(buildTerminalNode(*FC.SingletonEncodingID, FC.FilterBits));
+ } else {
+ N->addChild(
+ buildAllOfOrSwitchNode(FC.StartBit, FC.NumBits, FC.FilterChooserMap));
+ }
+ if (FC.VariableFC) {
+ N->addChild(buildAnyOfNode(*FC.VariableFC));
+ }
+
+ return N;
+}
+
+void DecoderTreeBuilder::emitBinaryParser(raw_ostream &OS, indent Indent,
+ const InstructionEncoding &Encoding,
+ const OperandInfo &OpInfo) {
+ if (OpInfo.HasNoEncoding) {
+ // If an operand has no encoding, the old behavior is to not decode it
+ // automatically and let the target do it. This is error-prone, so the
+ // new behavior is to report an error.
+ if (!IgnoreNonDecodableOperands)
+ PrintError(Encoding.getRecord()->getLoc(),
+ "could not find field for operand '" + OpInfo.Name + "'");
+ return;
+ }
+ // Special case for 'bits<0>'.
+ if (OpInfo.Fields.empty() && !OpInfo.InitValue) {
+ if (IgnoreNonDecodableOperands)
+ return;
+ assert(!OpInfo.Decoder.empty());
+ // The operand has no encoding, so the corresponding argument is omitted.
+ // This avoids confusion and allows the function to be overloaded if the
+ // operand does have an encoding in other instructions.
+ OS << Indent << "if (!Check(S, " << OpInfo.Decoder << "(MI, Decoder)))\n"
+ << Indent << " return MCDisassembler::Fail;\n";
+ return;
+ }
+
+ if (OpInfo.fields().empty()) {
+ // Only a constant part. The old behavior is to not decode this operand.
+ if (IgnoreFullyDefinedOperands)
+ return;
+ // Initialize `tmp` with the constant part.
+ OS << Indent << "tmp = " << format_hex(*OpInfo.InitValue, 0) << ";\n";
+ } else if (OpInfo.fields().size() == 1 && !OpInfo.InitValue.value_or(0)) {
+ // One variable part and no/zero constant part. Initialize `tmp` with the
+ // variable part.
+ auto [Base, Width, Offset] = OpInfo.fields().front();
+ OS << Indent << "tmp = fieldFromInstruction(insn, " << Base << ", " << Width
+ << ')';
+ if (Offset)
+ OS << " << " << Offset;
+ OS << ";\n";
+ } else {
+ // General case. Initialize `tmp` with the constant part, if any, and
+ // insert the variable parts into it.
+ OS << Indent << "tmp = " << format_hex(OpInfo.InitValue.value_or(0), 0)
+ << ";\n";
+ for (auto [Base, Width, Offset] : OpInfo.fields())
+ OS << Indent << "insertBits(tmp, fieldFromInstruction(insn, " << Base
+ << ", " << Width << "), " << Offset << ", " << Width << ");\n";
+ }
+
+ StringRef Decoder = OpInfo.Decoder;
+ if (!Decoder.empty()) {
+ OS << Indent << "if (!Check(S, " << Decoder
+ << "(MI, tmp, Address, Decoder))) { "
+ << (OpInfo.HasCompleteDecoder ? "" : "DecodeComplete = false; ")
+ << "return MCDisassembler::Fail; }\n";
+ } else {
+ OS << Indent << "MI.addOperand(MCOperand::createImm(tmp));\n";
+ }
+}
+
+void DecoderTreeBuilder::emitDecoder(raw_ostream &OS, indent Indent,
+ const InstructionEncoding &Encoding) {
+ // If a custom instruction decoder was specified, use that.
+ StringRef DecoderMethod = Encoding.getDecoderMethod();
+ if (!DecoderMethod.empty()) {
+ OS << Indent << "if (!Check(S, " << DecoderMethod
+ << "(MI, insn, Address, Decoder))) { "
+ << (Encoding.hasCompleteDecoder() ? "" : "DecodeComplete = false; ")
+ << "return MCDisassembler::Fail; }\n";
+ return;
+ }
+
+ for (const OperandInfo &Op : Encoding.getOperands())
+ emitBinaryParser(OS, Indent, Encoding, Op);
+}
+
+unsigned
+DecoderTreeBuilder::getDecoderIndex(const InstructionEncoding &Encoding) {
+ // Build up the predicate string.
+ SmallString<256> Decoder;
+ // FIXME: emitDecoder() function can take a buffer directly rather than
+ // a stream.
+ raw_svector_ostream S(Decoder);
+ indent Indent(UseFnTableInDecodeToMCInst ? 2 : 4);
+ emitDecoder(S, Indent, Encoding);
+
+ // 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.
+
+ // Make sure the predicate is in the table.
+ DecoderSet &Decoders = TableInfo.Decoders;
+ Decoders.insert(CachedHashString(Decoder));
+ // Now figure out the index for when we write out the table.
+ DecoderSet::const_iterator P = find(Decoders, Decoder.str());
+ return std::distance(Decoders.begin(), P);
+}
+
+// If ParenIfBinOp is true, print a surrounding () if Val uses && or ||.
+bool DecoderTreeBuilder::emitPredicateMatchAux(StringRef PredicateNamespace,
+ const Init &Val,
+ bool ParenIfBinOp,
+ raw_ostream &OS) {
+ if (const auto *D = dyn_cast<DefInit>(&Val)) {
+ if (!D->getDef()->isSubClassOf("SubtargetFeature"))
+ return true;
+ OS << "Bits[" << PredicateNamespace << "::" << D->getAsString() << "]";
+ return false;
+ }
+ if (const auto *D = dyn_cast<DagInit>(&Val)) {
+ std::string Op = D->getOperator()->getAsString();
+ if (Op == "not" && D->getNumArgs() == 1) {
+ OS << '!';
+ return emitPredicateMatchAux(PredicateNamespace, *D->getArg(0), true, OS);
+ }
+ if ((Op == "any_of" || Op == "all_of") && D->getNumArgs() > 0) {
+ bool Paren = D->getNumArgs() > 1 && std::exchange(ParenIfBinOp, true);
+ if (Paren)
+ OS << '(';
+ ListSeparator LS(Op == "any_of" ? " || " : " && ");
+ for (auto *Arg : D->getArgs()) {
+ OS << LS;
+ if (emitPredicateMatchAux(PredicateNamespace, *Arg, ParenIfBinOp, OS))
+ return true;
+ }
+ if (Paren)
+ OS << ')';
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DecoderTreeBuilder::emitPredicateMatch(
+ StringRef PredicateNamespace, raw_ostream &OS,
+ const InstructionEncoding &Encoding) {
+ const ListInit *Predicates =
+ Encoding.getRecord()->getValueAsListInit("Predicates");
+ bool IsFirstEmission = true;
+ for (unsigned i = 0; i < Predicates->size(); ++i) {
+ const Record *Pred = Predicates->getElementAsRecord(i);
+ if (!Pred->getValue("AssemblerMatcherPredicate"))
+ continue;
+
+ if (!isa<DagInit>(Pred->getValue("AssemblerCondDag")->getValue()))
+ continue;
+
+ if (!IsFirstEmission)
+ OS << " && ";
+ if (emitPredicateMatchAux(PredicateNamespace,
+ *Pred->getValueAsDag("AssemblerCondDag"),
+ Predicates->size() > 1, OS))
+ PrintFatalError(Pred->getLoc(), "Invalid AssemblerCondDag!");
+ IsFirstEmission = false;
+ }
+ return !Predicates->empty();
+}
+
+static bool doesOpcodeNeedPredicate(const InstructionEncoding &Encoding) {
+ const ListInit *Predicates =
+ Encoding.getRecord()->getValueAsListInit("Predicates");
+ for (unsigned i = 0; i < Predicates->size(); ++i) {
+ const Record *Pred = Predicates->getElementAsRecord(i);
+ if (!Pred->getValue("AssemblerMatcherPredicate"))
+ continue;
+
+ if (isa<DagInit>(Pred->getValue("AssemblerCondDag")->getValue()))
+ return true;
+ }
+ return false;
+}
+
+unsigned DecoderTreeBuilder::getPredicateIndex(
+ const InstructionEncoding &Encoding) const {
+ // Build up the predicate string.
+ SmallString<256> Predicate;
+ // FIXME: emitPredicateMatch() functions can take a buffer directly rather
+ // than a stream.
+ raw_svector_ostream PS(Predicate);
+ emitPredicateMatch(Target.getName(), PS, Encoding);
+
+ // 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.
+
+ // Make sure the predicate is in the table.
+ PredicateSet &Predicates = TableInfo.Predicates;
+ Predicates.insert(CachedHashString(Predicate));
+ // Now figure out the index for when we write out the table.
+ PredicateSet::const_iterator P = find(Predicates, Predicate);
+ return std::distance(Predicates.begin(), P);
+}
+
+std::unique_ptr<DecoderTreeNode> llvm::buildDecoderTree(
+ const CodeGenTarget &Target, ArrayRef<InstructionEncoding> Encodings,
+ ArrayRef<unsigned> EncodingIDs, DecoderTableInfo &TableInfo) {
+ FilterChooser FC(Encodings, EncodingIDs);
+ if (FC.hasConflict())
+ return nullptr;
+ DecoderTreeBuilder TreeBuilder(Target, Encodings, TableInfo);
+ return TreeBuilder.buildTree(FC);
+}
diff --git a/llvm/utils/TableGen/DecoderTreeEmitter.cpp b/llvm/utils/TableGen/DecoderTreeEmitter.cpp
new file mode 100644
index 0000000000000..0e15cc4712dc9
--- /dev/null
+++ b/llvm/utils/TableGen/DecoderTreeEmitter.cpp
@@ -0,0 +1,335 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "Common/CodeGenTarget.h"
+#include "Common/InstructionEncoding.h"
+#include "DecoderTree.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/LEB128.h"
+
+namespace llvm {
+extern cl::opt<bool> SpecializeDecodersPerBitwidth;
+} // namespace llvm
+
+using namespace llvm;
+
+namespace {
+
+class DecoderTableEmitter {
+ DecoderTableInfo &TableInfo;
+ const CodeGenTarget &Target;
+ ArrayRef<InstructionEncoding> Encodings;
+ formatted_raw_ostream OS;
+ unsigned IndexWidth;
+ unsigned CurrentIndex;
+ unsigned CommentIndex;
+
+public:
+ DecoderTableEmitter(DecoderTableInfo &TableInfo, const CodeGenTarget &Target,
+ ArrayRef<InstructionEncoding> Encodings, raw_ostream &OS)
+ : TableInfo(TableInfo), Target(Target), Encodings(Encodings), OS(OS) {}
+
+ void emitTable(StringRef TableName, unsigned BitWidth,
+ const DecoderTreeNode *Root);
+
+private:
+ unsigned computeNodeSize(const DecoderTreeNode *N) const;
+
+ unsigned computeTableSize(unsigned BitWidth,
+ const DecoderTreeNode *Root) const {
+ unsigned Size = 0;
+ if (SpecializeDecodersPerBitwidth)
+ Size = getULEB128Size(BitWidth);
+ Size += computeNodeSize(Root);
+ return Size;
+ }
+
+ void emitStartLine() {
+ CommentIndex = CurrentIndex;
+ OS.indent(2);
+ }
+
+ void emitOpcode(StringRef Name) {
+ emitStartLine();
+ OS << "MCD::" << Name << ", ";
+ ++CurrentIndex;
+ }
+
+ void emitByte(uint8_t Val) {
+ OS << static_cast<unsigned>(Val) << ", ";
+ ++CurrentIndex;
+ }
+
+ void emitUInt8(unsigned Val) {
+ assert(isUInt<8>(Val));
+ emitByte(Val);
+ }
+
+ void emitULEB128(uint64_t Val) {
+ while (Val >= 0x80) {
+ emitByte((Val & 0x7F) | 0x80);
+ Val >>= 7;
+ }
+ emitByte(Val);
+ }
+
+ formatted_raw_ostream &emitComment(indent Indent) {
+ constexpr unsigned CommentColumn = 45;
+ if (OS.getColumn() > CommentColumn)
+ OS << '\n';
+ OS.PadToColumn(CommentColumn);
+ OS << "// " << format_decimal(CommentIndex, IndexWidth) << ": " << Indent;
+ return OS;
+ }
+
+ void emitAnyOfNode(const AnyOfNode *N, indent Indent);
+ void emitAllOfNode(const AllOfNode *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
+
+unsigned DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *N) const {
+ switch (N->getKind()) {
+ case DecoderTreeNode::AnyOf: {
+ const auto *CheckAny = static_cast<const AnyOfNode *>(N);
+ unsigned Size = 0;
+ for (const DecoderTreeNode *Child : drop_end(CheckAny->children())) {
+ unsigned ChildSize = computeNodeSize(Child);
+ Size += 1 + getULEB128Size(ChildSize) + ChildSize;
+ }
+ return Size + computeNodeSize(*std::prev(CheckAny->child_end()));
+ }
+ case DecoderTreeNode::AllOf: {
+ const auto *CheckAll = static_cast<const AllOfNode *>(N);
+ unsigned Size = 0;
+ for (const DecoderTreeNode *Child : CheckAll->children())
+ Size += computeNodeSize(Child);
+ return Size;
+ }
+ case DecoderTreeNode::CheckField: {
+ const auto *CheckField = static_cast<const CheckFieldNode *>(N);
+ return 1 + getULEB128Size(CheckField->getStartBit()) + 1 +
+ getULEB128Size(CheckField->getValue());
+ }
+ case DecoderTreeNode::SwitchField: {
+ const auto *SwitchN = static_cast<const SwitchFieldNode *>(N);
+ unsigned Size = 1 + getULEB128Size(SwitchN->getStartBit()) + 1;
+
+ for (auto [Val, Child] : drop_end(SwitchN->cases())) {
+ unsigned ChildSize = computeNodeSize(Child);
+ Size += getULEB128Size(Val) + getULEB128Size(ChildSize) + ChildSize;
+ }
+
+ auto [Val, Child] = *std::prev(SwitchN->case_end());
+ unsigned ChildSize = computeNodeSize(Child);
+ Size += getULEB128Size(Val) + getULEB128Size(0) + ChildSize;
+ return Size;
+ }
+ case DecoderTreeNode::CheckPredicate: {
+ const auto *CheckPredicate = static_cast<const CheckPredicateNode *>(N);
+ return 1 + getULEB128Size(CheckPredicate->getPredicateIndex());
+ }
+ case DecoderTreeNode::SoftFail: {
+ const auto *SoftFail = static_cast<const SoftFailNode *>(N);
+ return 1 + getULEB128Size(SoftFail->getPositiveMask()) +
+ getULEB128Size(SoftFail->getNegativeMask());
+ }
+ case DecoderTreeNode::Decode: {
+ const auto *Decode = static_cast<const DecodeNode *>(N);
+ const InstructionEncoding &Encoding = Encodings[Decode->getEncodingID()];
+ const Record *InstDef = Encoding.getInstruction()->TheDef;
+ unsigned InstOpcode = Target.getInstrIntValue(InstDef);
+ return 1 + getULEB128Size(InstOpcode) +
+ getULEB128Size(Decode->getDecoderIndex());
+ }
+ }
+ llvm_unreachable("Unknown node kind");
+}
+
+void DecoderTableEmitter::emitAnyOfNode(const AnyOfNode *N, indent Indent) {
+ for (const DecoderTreeNode *Child : drop_end(N->children())) {
+ emitOpcode("OPC_Scope");
+ emitULEB128(computeNodeSize(Child));
+
+ emitComment(Indent) << "{\n";
+ emitNode(Child, Indent + 1);
+ emitComment(Indent) << "}\n";
+ }
+
+ const DecoderTreeNode *Child = *std::prev(N->child_end());
+ emitNode(Child, Indent);
+}
+
+void DecoderTableEmitter::emitAllOfNode(const AllOfNode *N, indent Indent) {
+ 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[" << 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";
+ }
+
+ 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[" << 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);
+ OS << "check Inst[" << MSB << ':' << LSB << "] == " << format_hex(Val, 0)
+ << '\n';
+}
+
+void DecoderTableEmitter::emitCheckPredicateNode(const CheckPredicateNode *N,
+ indent Indent) {
+ unsigned PredicateIndex = N->getPredicateIndex();
+
+ emitOpcode("OPC_CheckPredicate");
+ emitULEB128(PredicateIndex);
+
+ 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);
+
+ emitComment(Indent) << "check softfail";
+ OS << " pos=" << format_hex(PositiveMask, 10);
+ OS << " neg=" << format_hex(NegativeMask, 10) << '\n';
+}
+
+void DecoderTableEmitter::emitDecodeNode(const DecodeNode *N, indent Indent) {
+ const InstructionEncoding &Encoding = Encodings[N->getEncodingID()];
+ const Record *InstDef = Encoding.getInstruction()->TheDef;
+ unsigned InstOpcode = Target.getInstrIntValue(InstDef);
+ unsigned DecoderIndex = N->getDecoderIndex();
+
+ emitOpcode(Encoding.hasCompleteDecoder() ? "OPC_Decode" : "OPC_TryDecode");
+ emitULEB128(InstOpcode);
+ emitULEB128(DecoderIndex);
+
+ emitComment(Indent);
+ if (!Encoding.hasCompleteDecoder())
+ OS << "try ";
+ OS << "decode to " << Encoding.getName() << " using decoder " << DecoderIndex
+ << '\n';
+}
+
+void DecoderTableEmitter::emitNode(const DecoderTreeNode *N, indent Indent) {
+ switch (N->getKind()) {
+ case DecoderTreeNode::AnyOf:
+ return emitAnyOfNode(static_cast<const AnyOfNode *>(N), Indent);
+ case DecoderTreeNode::AllOf:
+ return emitAllOfNode(static_cast<const AllOfNode *>(N), Indent);
+ case DecoderTreeNode::SwitchField:
+ return emitSwitchFieldNode(static_cast<const SwitchFieldNode *>(N), Indent);
+ case DecoderTreeNode::CheckField:
+ return emitCheckFieldNode(static_cast<const CheckFieldNode *>(N), Indent);
+ case DecoderTreeNode::CheckPredicate:
+ TableInfo.HasCheckPredicate = true;
+ return emitCheckPredicateNode(static_cast<const CheckPredicateNode *>(N),
+ Indent);
+ case DecoderTreeNode::SoftFail:
+ TableInfo.HasSoftFail = true;
+ return emitSoftFailNode(static_cast<const SoftFailNode *>(N), Indent);
+ case DecoderTreeNode::Decode: {
+ const InstructionEncoding &Encoding =
+ Encodings[static_cast<const DecodeNode *>(N)->getEncodingID()];
+ TableInfo.HasTryDecode |= !Encoding.hasCompleteDecoder();
+ return emitDecodeNode(static_cast<const DecodeNode *>(N), Indent);
+ }
+ }
+ llvm_unreachable("Unknown node kind");
+}
+
+void DecoderTableEmitter::emitTable(StringRef TableName, unsigned BitWidth,
+ const DecoderTreeNode *Root) {
+ unsigned TableSize = computeTableSize(BitWidth, Root);
+ 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 (SpecializeDecodersPerBitwidth) {
+ 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";
+}
+
+void llvm::emitDecoderTable(raw_ostream &OS, const CodeGenTarget &Target,
+ DecoderTableInfo &TableInfo,
+ ArrayRef<InstructionEncoding> Encodings,
+ StringRef TableName, unsigned BitWidth,
+ const DecoderTreeNode *Tree) {
+ DecoderTableEmitter TableEmitter(TableInfo, Target, Encodings, OS);
+ TableEmitter.emitTable(TableName, BitWidth, Tree);
+}
>From 2bf19c10fe95c211174f6a7d876594f3c3b7433d Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Tue, 16 Sep 2025 02:39:33 +0300
Subject: [PATCH 3/3] tmp3
---
llvm/utils/TableGen/CMakeLists.txt | 2 +-
llvm/utils/TableGen/DecoderEmitter.cpp | 21 +-
...reeEmitter.cpp => DecoderTableEmitter.cpp} | 251 +++++++++------
llvm/utils/TableGen/DecoderTree.h | 106 ++++---
llvm/utils/TableGen/DecoderTreeBuilder.cpp | 300 ++++++------------
5 files changed, 311 insertions(+), 369 deletions(-)
rename llvm/utils/TableGen/{DecoderTreeEmitter.cpp => DecoderTableEmitter.cpp} (56%)
diff --git a/llvm/utils/TableGen/CMakeLists.txt b/llvm/utils/TableGen/CMakeLists.txt
index ec071ab823d23..1a85553950f69 100644
--- a/llvm/utils/TableGen/CMakeLists.txt
+++ b/llvm/utils/TableGen/CMakeLists.txt
@@ -43,9 +43,9 @@ add_tablegen(llvm-tblgen LLVM
DAGISelMatcherGen.cpp
DAGISelMatcherOpt.cpp
DecoderEmitter.cpp
+ DecoderTableEmitter.cpp
DecoderTree.cpp
DecoderTreeBuilder.cpp
- DecoderTreeEmitter.cpp
DFAEmitter.cpp
DFAPacketizerEmitter.cpp
DisassemblerEmitter.cpp
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 8a9e094d85cb9..1661b8caaef4b 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -129,16 +129,12 @@ class DecoderEmitter {
const CodeGenTarget &getTarget() const { return Target; }
- // Emit the decoder state machine table.
- void emitDecoderTable(formatted_raw_ostream &OS, const DecoderTreeNode *Tree,
- StringRef Namespace, unsigned HwModeID,
- unsigned BitWidth) const;
void emitInstrLenTable(formatted_raw_ostream &OS,
ArrayRef<unsigned> InstrLen) const;
void emitPredicateFunction(formatted_raw_ostream &OS,
- PredicateSet &Predicates) const;
+ const std::set<std::string> &Predicates) const;
void emitDecoderFunction(formatted_raw_ostream &OS,
- const DecoderSet &Decoders,
+ const std::set<std::string> &Decoders,
unsigned BucketBitWidth) const;
// run - Output the code emitter
@@ -167,12 +163,12 @@ void DecoderEmitter::emitInstrLenTable(formatted_raw_ostream &OS,
OS << "};\n\n";
}
-void DecoderEmitter::emitPredicateFunction(formatted_raw_ostream &OS,
- PredicateSet &Predicates) const {
+void DecoderEmitter::emitPredicateFunction(
+ formatted_raw_ostream &OS, const std::set<std::string> &Predicates) const {
// The predicate function is just a big switch statement based on the
// input predicate index.
OS << "static bool checkDecoderPredicate(unsigned Idx, const FeatureBitset "
- "&Bits) {\n";
+ "&FB) {\n";
OS << " switch (Idx) {\n";
OS << " default: llvm_unreachable(\"Invalid index!\");\n";
for (const auto &[Index, Predicate] : enumerate(Predicates)) {
@@ -184,7 +180,7 @@ void DecoderEmitter::emitPredicateFunction(formatted_raw_ostream &OS,
}
void DecoderEmitter::emitDecoderFunction(formatted_raw_ostream &OS,
- const DecoderSet &Decoders,
+ const std::set<std::string> &Decoders,
unsigned BucketBitWidth) const {
// The decoder function is just a big switch statement or a table of function
// pointers based on the input decoder index.
@@ -726,7 +722,7 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
// Emit the decoder for this (namespace, hwmode, width) combination.
std::unique_ptr<DecoderTreeNode> Tree =
- ::buildDecoderTree(Target, Encodings, EncodingIDs, TableInfo);
+ ::buildDecoderTree(Target, Encodings, EncodingIDs);
if (!Tree) {
HasConflict = true;
continue;
@@ -739,8 +735,7 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
TableName.append(std::to_string(BitWidth));
// Serialize the tree.
- ::emitDecoderTable(OS, Target, TableInfo, Encodings, TableName, BitWidth,
- Tree.get());
+ ::emitDecoderTable(OS, TableInfo, TableName, BitWidth, Tree.get());
}
// Each BitWidth get's its own decoders and decoder function if
diff --git a/llvm/utils/TableGen/DecoderTreeEmitter.cpp b/llvm/utils/TableGen/DecoderTableEmitter.cpp
similarity index 56%
rename from llvm/utils/TableGen/DecoderTreeEmitter.cpp
rename to llvm/utils/TableGen/DecoderTableEmitter.cpp
index 0e15cc4712dc9..0a1fca75249ac 100644
--- a/llvm/utils/TableGen/DecoderTreeEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderTableEmitter.cpp
@@ -24,73 +24,35 @@ namespace {
class DecoderTableEmitter {
DecoderTableInfo &TableInfo;
- const CodeGenTarget &Target;
- ArrayRef<InstructionEncoding> Encodings;
formatted_raw_ostream OS;
unsigned IndexWidth;
unsigned CurrentIndex;
unsigned CommentIndex;
public:
- DecoderTableEmitter(DecoderTableInfo &TableInfo, const CodeGenTarget &Target,
- ArrayRef<InstructionEncoding> Encodings, raw_ostream &OS)
- : TableInfo(TableInfo), Target(Target), Encodings(Encodings), OS(OS) {}
+ DecoderTableEmitter(DecoderTableInfo &TableInfo, raw_ostream &OS)
+ : TableInfo(TableInfo), OS(OS) {}
void emitTable(StringRef TableName, unsigned BitWidth,
const DecoderTreeNode *Root);
private:
- unsigned computeNodeSize(const DecoderTreeNode *N) const;
+ void analyzeNode(const DecoderTreeNode *Node) const;
- unsigned computeTableSize(unsigned BitWidth,
- const DecoderTreeNode *Root) const {
- unsigned Size = 0;
- if (SpecializeDecodersPerBitwidth)
- Size = getULEB128Size(BitWidth);
- Size += computeNodeSize(Root);
- return Size;
- }
-
- void emitStartLine() {
- CommentIndex = CurrentIndex;
- OS.indent(2);
- }
-
- void emitOpcode(StringRef Name) {
- emitStartLine();
- OS << "MCD::" << Name << ", ";
- ++CurrentIndex;
- }
-
- void emitByte(uint8_t Val) {
- OS << static_cast<unsigned>(Val) << ", ";
- ++CurrentIndex;
- }
+ unsigned computeNodeSize(const DecoderTreeNode *Node) const;
+ unsigned computeTableSize(const DecoderTreeNode *Root,
+ unsigned BitWidth) const;
- void emitUInt8(unsigned Val) {
- assert(isUInt<8>(Val));
- emitByte(Val);
- }
-
- void emitULEB128(uint64_t Val) {
- while (Val >= 0x80) {
- emitByte((Val & 0x7F) | 0x80);
- Val >>= 7;
- }
- emitByte(Val);
- }
+ void emitStartLine();
+ void emitOpcode(StringRef Name);
+ void emitByte(uint8_t Val);
+ void emitUInt8(unsigned Val);
+ void emitULEB128(uint64_t Val);
- formatted_raw_ostream &emitComment(indent Indent) {
- constexpr unsigned CommentColumn = 45;
- if (OS.getColumn() > CommentColumn)
- OS << '\n';
- OS.PadToColumn(CommentColumn);
- OS << "// " << format_decimal(CommentIndex, IndexWidth) << ": " << Indent;
- return OS;
- }
+ formatted_raw_ostream &emitComment(indent Indent);
- void emitAnyOfNode(const AnyOfNode *N, indent Indent);
- void emitAllOfNode(const AllOfNode *N, 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);
@@ -101,65 +63,155 @@ class DecoderTableEmitter {
} // namespace
-unsigned DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *N) const {
- switch (N->getKind()) {
- case DecoderTreeNode::AnyOf: {
- const auto *CheckAny = static_cast<const AnyOfNode *>(N);
+void DecoderTableEmitter::analyzeNode(const DecoderTreeNode *Node) const {
+ switch (Node->getKind()) {
+ case DecoderTreeNode::CheckAny: {
+ const auto *N = static_cast<const CheckAnyNode *>(Node);
+ for (const DecoderTreeNode *Child : N->children())
+ analyzeNode(Child);
+ break;
+ }
+ case DecoderTreeNode::CheckAll: {
+ const auto *N = static_cast<const CheckAllNode *>(Node);
+ for (const DecoderTreeNode *Child : N->children())
+ analyzeNode(Child);
+ break;
+ }
+ case DecoderTreeNode::CheckField:
+ break;
+ case DecoderTreeNode::SwitchField: {
+ const auto *N = static_cast<const SwitchFieldNode *>(Node);
+ for (const DecoderTreeNode *Child : make_second_range(N->cases()))
+ analyzeNode(Child);
+ break;
+ }
+ case DecoderTreeNode::CheckPredicate: {
+ const auto *N = static_cast<const CheckPredicateNode *>(Node);
+ TableInfo.Predicates.insert(N->getPredicateString().str());
+ TableInfo.HasCheckPredicate = true;
+ break;
+ }
+ case DecoderTreeNode::SoftFail:
+ TableInfo.HasSoftFail = true;
+ break;
+ case DecoderTreeNode::Decode: {
+ const auto *N = static_cast<const DecodeNode *>(Node);
+ const InstructionEncoding &Encoding = N->getEncoding();
+ TableInfo.Decoders.insert(N->getDecoderString().str());
+ TableInfo.HasTryDecode |= !Encoding.hasCompleteDecoder();
+ break;
+ }
+ }
+}
+
+unsigned
+DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
+ switch (Node->getKind()) {
+ case DecoderTreeNode::CheckAny: {
+ const auto *N = static_cast<const CheckAnyNode *>(Node);
unsigned Size = 0;
- for (const DecoderTreeNode *Child : drop_end(CheckAny->children())) {
+ for (const DecoderTreeNode *Child : drop_end(N->children())) {
unsigned ChildSize = computeNodeSize(Child);
Size += 1 + getULEB128Size(ChildSize) + ChildSize;
}
- return Size + computeNodeSize(*std::prev(CheckAny->child_end()));
+ return Size + computeNodeSize(*std::prev(N->child_end()));
}
- case DecoderTreeNode::AllOf: {
- const auto *CheckAll = static_cast<const AllOfNode *>(N);
+ case DecoderTreeNode::CheckAll: {
+ const auto *N = static_cast<const CheckAllNode *>(Node);
unsigned Size = 0;
- for (const DecoderTreeNode *Child : CheckAll->children())
+ for (const DecoderTreeNode *Child : N->children())
Size += computeNodeSize(Child);
return Size;
}
case DecoderTreeNode::CheckField: {
- const auto *CheckField = static_cast<const CheckFieldNode *>(N);
- return 1 + getULEB128Size(CheckField->getStartBit()) + 1 +
- getULEB128Size(CheckField->getValue());
+ const auto *N = static_cast<const CheckFieldNode *>(Node);
+ return 1 + getULEB128Size(N->getStartBit()) + 1 +
+ getULEB128Size(N->getValue());
}
case DecoderTreeNode::SwitchField: {
- const auto *SwitchN = static_cast<const SwitchFieldNode *>(N);
- unsigned Size = 1 + getULEB128Size(SwitchN->getStartBit()) + 1;
+ const auto *N = static_cast<const SwitchFieldNode *>(Node);
+ unsigned Size = 1 + getULEB128Size(N->getStartBit()) + 1;
- for (auto [Val, Child] : drop_end(SwitchN->cases())) {
+ for (auto [Val, Child] : drop_end(N->cases())) {
unsigned ChildSize = computeNodeSize(Child);
Size += getULEB128Size(Val) + getULEB128Size(ChildSize) + ChildSize;
}
- auto [Val, Child] = *std::prev(SwitchN->case_end());
+ 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 *CheckPredicate = static_cast<const CheckPredicateNode *>(N);
- return 1 + getULEB128Size(CheckPredicate->getPredicateIndex());
+ const auto *N = static_cast<const CheckPredicateNode *>(Node);
+ unsigned PredicateIndex =
+ TableInfo.getPredicateIndex(N->getPredicateString());
+ return 1 + getULEB128Size(PredicateIndex);
}
case DecoderTreeNode::SoftFail: {
- const auto *SoftFail = static_cast<const SoftFailNode *>(N);
- return 1 + getULEB128Size(SoftFail->getPositiveMask()) +
- getULEB128Size(SoftFail->getNegativeMask());
+ const auto *N = static_cast<const SoftFailNode *>(Node);
+ return 1 + getULEB128Size(N->getPositiveMask()) +
+ getULEB128Size(N->getNegativeMask());
}
case DecoderTreeNode::Decode: {
- const auto *Decode = static_cast<const DecodeNode *>(N);
- const InstructionEncoding &Encoding = Encodings[Decode->getEncodingID()];
- const Record *InstDef = Encoding.getInstruction()->TheDef;
- unsigned InstOpcode = Target.getInstrIntValue(InstDef);
- return 1 + getULEB128Size(InstOpcode) +
- getULEB128Size(Decode->getDecoderIndex());
+ const auto *N = static_cast<const DecodeNode *>(Node);
+ unsigned InstOpcode = N->getEncoding().getInstruction()->EnumVal;
+ unsigned DecoderIndex = TableInfo.getDecoderIndex(N->getDecoderString());
+ return 1 + getULEB128Size(InstOpcode) + getULEB128Size(DecoderIndex);
}
}
llvm_unreachable("Unknown node kind");
}
-void DecoderTableEmitter::emitAnyOfNode(const AnyOfNode *N, indent Indent) {
+unsigned DecoderTableEmitter::computeTableSize(const DecoderTreeNode *Root,
+ unsigned BitWidth) const {
+ unsigned Size = 0;
+ if (SpecializeDecodersPerBitwidth)
+ Size += getULEB128Size(BitWidth);
+ Size += computeNodeSize(Root);
+ return Size;
+}
+
+void DecoderTableEmitter::emitStartLine() {
+ CommentIndex = CurrentIndex;
+ OS.indent(2);
+}
+
+void DecoderTableEmitter::emitOpcode(StringRef Name) {
+ emitStartLine();
+ OS << "MCD::" << Name << ", ";
+ ++CurrentIndex;
+}
+
+void DecoderTableEmitter::emitByte(uint8_t Val) {
+ OS << static_cast<unsigned>(Val) << ", ";
+ ++CurrentIndex;
+}
+
+void DecoderTableEmitter::emitUInt8(unsigned int Val) {
+ assert(isUInt<8>(Val));
+ emitByte(Val);
+}
+
+void DecoderTableEmitter::emitULEB128(uint64_t Val) {
+ while (Val >= 0x80) {
+ emitByte((Val & 0x7F) | 0x80);
+ Val >>= 7;
+ }
+ emitByte(Val);
+}
+
+formatted_raw_ostream &DecoderTableEmitter::emitComment(indent Indent) {
+ constexpr unsigned CommentColumn = 45;
+ if (OS.getColumn() > CommentColumn)
+ OS << '\n';
+ OS.PadToColumn(CommentColumn);
+ OS << "// " << format_decimal(CommentIndex, IndexWidth) << ": " << Indent;
+ return OS;
+}
+
+void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N,
+ indent Indent) {
for (const DecoderTreeNode *Child : drop_end(N->children())) {
emitOpcode("OPC_Scope");
emitULEB128(computeNodeSize(Child));
@@ -173,7 +225,8 @@ void DecoderTableEmitter::emitAnyOfNode(const AnyOfNode *N, indent Indent) {
emitNode(Child, Indent);
}
-void DecoderTableEmitter::emitAllOfNode(const AllOfNode *N, indent Indent) {
+void DecoderTableEmitter::emitCheckAllNode(const CheckAllNode *N,
+ indent Indent) {
for (const DecoderTreeNode *Child : N->children())
emitNode(Child, Indent);
}
@@ -231,7 +284,8 @@ void DecoderTableEmitter::emitCheckFieldNode(const CheckFieldNode *N,
void DecoderTableEmitter::emitCheckPredicateNode(const CheckPredicateNode *N,
indent Indent) {
- unsigned PredicateIndex = N->getPredicateIndex();
+ unsigned PredicateIndex =
+ TableInfo.getPredicateIndex(N->getPredicateString());
emitOpcode("OPC_CheckPredicate");
emitULEB128(PredicateIndex);
@@ -254,10 +308,9 @@ void DecoderTableEmitter::emitSoftFailNode(const SoftFailNode *N,
}
void DecoderTableEmitter::emitDecodeNode(const DecodeNode *N, indent Indent) {
- const InstructionEncoding &Encoding = Encodings[N->getEncodingID()];
- const Record *InstDef = Encoding.getInstruction()->TheDef;
- unsigned InstOpcode = Target.getInstrIntValue(InstDef);
- unsigned DecoderIndex = N->getDecoderIndex();
+ const InstructionEncoding &Encoding = N->getEncoding();
+ unsigned InstOpcode = Encoding.getInstruction()->EnumVal;
+ unsigned DecoderIndex = TableInfo.getDecoderIndex(N->getDecoderString());
emitOpcode(Encoding.hasCompleteDecoder() ? "OPC_Decode" : "OPC_TryDecode");
emitULEB128(InstOpcode);
@@ -272,34 +325,30 @@ void DecoderTableEmitter::emitDecodeNode(const DecodeNode *N, indent Indent) {
void DecoderTableEmitter::emitNode(const DecoderTreeNode *N, indent Indent) {
switch (N->getKind()) {
- case DecoderTreeNode::AnyOf:
- return emitAnyOfNode(static_cast<const AnyOfNode *>(N), Indent);
- case DecoderTreeNode::AllOf:
- return emitAllOfNode(static_cast<const AllOfNode *>(N), Indent);
+ case DecoderTreeNode::CheckAny:
+ return emitCheckAnyNode(static_cast<const CheckAnyNode *>(N), Indent);
+ case DecoderTreeNode::CheckAll:
+ return emitCheckAllNode(static_cast<const CheckAllNode *>(N), Indent);
case DecoderTreeNode::SwitchField:
return emitSwitchFieldNode(static_cast<const SwitchFieldNode *>(N), Indent);
case DecoderTreeNode::CheckField:
return emitCheckFieldNode(static_cast<const CheckFieldNode *>(N), Indent);
case DecoderTreeNode::CheckPredicate:
- TableInfo.HasCheckPredicate = true;
return emitCheckPredicateNode(static_cast<const CheckPredicateNode *>(N),
Indent);
case DecoderTreeNode::SoftFail:
- TableInfo.HasSoftFail = true;
return emitSoftFailNode(static_cast<const SoftFailNode *>(N), Indent);
- case DecoderTreeNode::Decode: {
- const InstructionEncoding &Encoding =
- Encodings[static_cast<const DecodeNode *>(N)->getEncodingID()];
- TableInfo.HasTryDecode |= !Encoding.hasCompleteDecoder();
+ case DecoderTreeNode::Decode:
return emitDecodeNode(static_cast<const DecodeNode *>(N), Indent);
}
- }
llvm_unreachable("Unknown node kind");
}
void DecoderTableEmitter::emitTable(StringRef TableName, unsigned BitWidth,
const DecoderTreeNode *Root) {
- unsigned TableSize = computeTableSize(BitWidth, Root);
+ analyzeNode(Root);
+
+ unsigned TableSize = computeTableSize(Root, BitWidth);
OS << "static const uint8_t " << TableName << "[" << TableSize << "] = {\n";
// Calculate the number of decimal places for table indices.
@@ -325,11 +374,9 @@ void DecoderTableEmitter::emitTable(StringRef TableName, unsigned BitWidth,
OS << "};\n";
}
-void llvm::emitDecoderTable(raw_ostream &OS, const CodeGenTarget &Target,
- DecoderTableInfo &TableInfo,
- ArrayRef<InstructionEncoding> Encodings,
+void llvm::emitDecoderTable(raw_ostream &OS, DecoderTableInfo &TableInfo,
StringRef TableName, unsigned BitWidth,
const DecoderTreeNode *Tree) {
- DecoderTableEmitter TableEmitter(TableInfo, Target, Encodings, OS);
+ DecoderTableEmitter TableEmitter(TableInfo, OS);
TableEmitter.emitTable(TableName, BitWidth, Tree);
}
diff --git a/llvm/utils/TableGen/DecoderTree.h b/llvm/utils/TableGen/DecoderTree.h
index 87b59f83d787a..b1c3d3a582159 100644
--- a/llvm/utils/TableGen/DecoderTree.h
+++ b/llvm/utils/TableGen/DecoderTree.h
@@ -9,12 +9,13 @@
#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 <map>
#include <memory>
+#include <set>
namespace llvm {
@@ -27,8 +28,8 @@ class DecoderTreeNode {
virtual ~DecoderTreeNode() = default;
enum KindTy {
- AnyOf,
- AllOf,
+ CheckAny,
+ CheckAll,
CheckField,
SwitchField,
CheckPredicate,
@@ -45,7 +46,7 @@ class DecoderTreeNode {
KindTy Kind;
};
-class AllOfNode : public DecoderTreeNode {
+class CheckAnyNode : public DecoderTreeNode {
SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
static const DecoderTreeNode *
@@ -54,10 +55,10 @@ class AllOfNode : public DecoderTreeNode {
}
public:
- AllOfNode() : DecoderTreeNode(AllOf) {}
+ CheckAnyNode() : DecoderTreeNode(CheckAny) {}
- void addChild(std::unique_ptr<DecoderTreeNode> Child) {
- Children.push_back(std::move(Child));
+ void addChild(std::unique_ptr<DecoderTreeNode> N) {
+ Children.push_back(std::move(N));
}
using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
@@ -76,7 +77,7 @@ class AllOfNode : public DecoderTreeNode {
}
};
-class AnyOfNode : public DecoderTreeNode {
+class CheckAllNode : public DecoderTreeNode {
SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
static const DecoderTreeNode *
@@ -85,10 +86,10 @@ class AnyOfNode : public DecoderTreeNode {
}
public:
- AnyOfNode() : DecoderTreeNode(AnyOf) {}
+ CheckAllNode() : DecoderTreeNode(CheckAll) {}
- void addChild(std::unique_ptr<DecoderTreeNode> N) {
- Children.push_back(std::move(N));
+ void addChild(std::unique_ptr<DecoderTreeNode> Child) {
+ Children.push_back(std::move(Child));
}
using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
@@ -107,6 +108,23 @@ class AnyOfNode : public DecoderTreeNode {
}
};
+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; }
+};
+
class SwitchFieldNode : public DecoderTreeNode {
unsigned StartBit;
unsigned NumBits;
@@ -145,38 +163,22 @@ class SwitchFieldNode : public DecoderTreeNode {
}
};
-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; }
-};
-
class CheckPredicateNode : public DecoderTreeNode {
- unsigned Index;
+ std::string PredicateString;
public:
- explicit CheckPredicateNode(unsigned Index)
- : DecoderTreeNode(CheckPredicate), Index(Index) {}
+ explicit CheckPredicateNode(std::string PredicateString)
+ : DecoderTreeNode(CheckPredicate),
+ PredicateString(std::move(PredicateString)) {}
- unsigned getPredicateIndex() const { return Index; }
+ StringRef getPredicateString() const { return PredicateString; }
};
class SoftFailNode : public DecoderTreeNode {
uint64_t PositiveMask, NegativeMask;
public:
- explicit SoftFailNode(uint64_t PositiveMask, uint64_t NegativeMask)
+ SoftFailNode(uint64_t PositiveMask, uint64_t NegativeMask)
: DecoderTreeNode(SoftFail), PositiveMask(PositiveMask),
NegativeMask(NegativeMask) {}
@@ -185,37 +187,45 @@ class SoftFailNode : public DecoderTreeNode {
};
class DecodeNode : public DecoderTreeNode {
- unsigned EncodingID;
- unsigned Index;
+ const InstructionEncoding &Encoding;
+ std::string DecoderString;
public:
- DecodeNode(unsigned EncodingID, unsigned Index)
- : DecoderTreeNode(Decode), EncodingID(EncodingID), Index(Index) {}
+ DecodeNode(const InstructionEncoding &Encoding, std::string DecoderString)
+ : DecoderTreeNode(Decode), Encoding(Encoding),
+ DecoderString(std::move(DecoderString)) {}
- unsigned getEncodingID() const { return EncodingID; }
+ const InstructionEncoding &getEncoding() const { return Encoding; }
- unsigned getDecoderIndex() const { return Index; }
+ StringRef getDecoderString() const { return DecoderString; }
};
-using PredicateSet = SmallSetVector<CachedHashString, 16>;
-using DecoderSet = SmallSetVector<CachedHashString, 16>;
-
struct DecoderTableInfo {
- PredicateSet Predicates;
- DecoderSet Decoders;
+ std::set<std::string> Predicates;
+ std::set<std::string> Decoders;
bool HasCheckPredicate;
bool HasSoftFail;
bool HasTryDecode;
+
+ unsigned getPredicateIndex(StringRef PredicateString) const {
+ auto I = Predicates.find(PredicateString.str());
+ assert(I != Predicates.end());
+ return std::distance(Predicates.begin(), I);
+ }
+
+ unsigned getDecoderIndex(StringRef DecoderString) const {
+ auto I = Decoders.find(DecoderString.str());
+ assert(I != Decoders.end());
+ return std::distance(Decoders.begin(), I);
+ }
};
std::unique_ptr<DecoderTreeNode>
buildDecoderTree(const CodeGenTarget &Target,
ArrayRef<InstructionEncoding> Encodings,
- ArrayRef<unsigned> EncodingIDs, DecoderTableInfo &TableInfo);
+ ArrayRef<unsigned> EncodingIDs);
-void emitDecoderTable(raw_ostream &OS, const CodeGenTarget &Target,
- DecoderTableInfo &TableInfo,
- ArrayRef<InstructionEncoding> Encodings,
+void emitDecoderTable(raw_ostream &OS, DecoderTableInfo &TableInfo,
StringRef TableName, unsigned BitWidth,
const DecoderTreeNode *Tree);
diff --git a/llvm/utils/TableGen/DecoderTreeBuilder.cpp b/llvm/utils/TableGen/DecoderTreeBuilder.cpp
index 5833ec9ad0e0d..89fb12b316f34 100644
--- a/llvm/utils/TableGen/DecoderTreeBuilder.cpp
+++ b/llvm/utils/TableGen/DecoderTreeBuilder.cpp
@@ -8,6 +8,7 @@
#include "Common/CodeGenTarget.h"
#include "Common/InstructionEncoding.h"
+#include "Common/SubtargetFeatureInfo.h"
#include "DecoderTree.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Format.h"
@@ -259,7 +260,7 @@ class LessEncodingIDByWidth {
} // namespace
FilterChooser::FilterChooser(ArrayRef<InstructionEncoding> Encodings,
- ArrayRef<unsigned int> EncodingIDs)
+ ArrayRef<unsigned> EncodingIDs)
: Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(nullptr) {
// Sort encoding IDs once.
stable_sort(this->EncodingIDs, LessEncodingIDByWidth(Encodings));
@@ -442,7 +443,7 @@ FilterChooser::findBestFilter(ArrayRef<bitAttr_t> BitAttrs, bool AllowMixed,
std::vector<std::unique_ptr<Filter>> Filters;
- auto addCandidateFilter = [&](unsigned StartBit, unsigned EndBit) {
+ auto AddCandidateFilter = [&](unsigned StartBit, unsigned EndBit) {
Filters.push_back(std::make_unique<Filter>(Encodings, EncodingIDs, StartBit,
EndBit - StartBit));
};
@@ -474,7 +475,7 @@ FilterChooser::findBestFilter(ArrayRef<bitAttr_t> BitAttrs, bool AllowMixed,
break;
case ATTR_ALL_SET:
if (!AllowMixed && bitAttr != ATTR_ALL_SET)
- addCandidateFilter(StartBit, BitIndex);
+ AddCandidateFilter(StartBit, BitIndex);
switch (bitAttr) {
case ATTR_FILTERED:
RA = ATTR_NONE;
@@ -494,7 +495,7 @@ FilterChooser::findBestFilter(ArrayRef<bitAttr_t> BitAttrs, bool AllowMixed,
break;
case ATTR_MIXED:
if (AllowMixed && bitAttr != ATTR_MIXED)
- addCandidateFilter(StartBit, BitIndex);
+ AddCandidateFilter(StartBit, BitIndex);
switch (bitAttr) {
case ATTR_FILTERED:
StartBit = BitIndex;
@@ -528,13 +529,13 @@ FilterChooser::findBestFilter(ArrayRef<bitAttr_t> BitAttrs, bool AllowMixed,
break;
case ATTR_ALL_SET:
if (!AllowMixed)
- addCandidateFilter(StartBit, FilterWidth);
+ AddCandidateFilter(StartBit, FilterWidth);
break;
case ATTR_ALL_UNSET:
break;
case ATTR_MIXED:
if (AllowMixed)
- addCandidateFilter(StartBit, FilterWidth);
+ AddCandidateFilter(StartBit, FilterWidth);
break;
}
@@ -673,115 +674,51 @@ namespace {
class DecoderTreeBuilder {
const CodeGenTarget &Target;
ArrayRef<InstructionEncoding> Encodings;
- DecoderTableInfo &TableInfo;
public:
DecoderTreeBuilder(const CodeGenTarget &Target,
- ArrayRef<InstructionEncoding> Encodings,
- DecoderTableInfo &TableInfo)
- : Target(Target), Encodings(Encodings), TableInfo(TableInfo) {}
+ ArrayRef<InstructionEncoding> Encodings)
+ : Target(Target), Encodings(Encodings) {}
std::unique_ptr<DecoderTreeNode> buildTree(const FilterChooser &FC) {
- return buildAnyOfNode(FC);
+ return buildCheckAnyNode(FC);
}
private:
- static void emitBinaryParser(raw_ostream &OS, indent Indent,
- const InstructionEncoding &Encoding,
- const OperandInfo &OpInfo);
- static void emitDecoder(raw_ostream &OS, indent Indent,
- const InstructionEncoding &Encoding);
- unsigned getDecoderIndex(const InstructionEncoding &Encoding);
-
- static bool emitPredicateMatchAux(StringRef PredicateNamespace,
- const Init &Val, bool ParenIfBinOp,
- raw_ostream &OS);
- static bool emitPredicateMatch(StringRef PredicateNamespace, raw_ostream &OS,
- const InstructionEncoding &Encoding);
- unsigned getPredicateIndex(const InstructionEncoding &Encoding) const;
-
std::unique_ptr<DecoderTreeNode>
buildTerminalNode(unsigned EncodingID, const KnownBits &FilterBits);
- std::unique_ptr<DecoderTreeNode> buildAllOfOrSwitchNode(
+ std::unique_ptr<DecoderTreeNode> buildCheckAllOrSwitchNode(
unsigned StartBit, unsigned NumBits,
const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap);
- std::unique_ptr<DecoderTreeNode> buildAnyOfNode(const FilterChooser &FC);
+ std::unique_ptr<DecoderTreeNode> buildCheckAnyNode(const FilterChooser &FC);
};
} // namespace
-static bool doesOpcodeNeedPredicate(const InstructionEncoding &Encoding);
-
-std::unique_ptr<DecoderTreeNode>
-DecoderTreeBuilder::buildTerminalNode(unsigned EncodingID,
- const KnownBits &FilterBits) {
- const InstructionEncoding &Encoding = Encodings[EncodingID];
- auto N = std::make_unique<AllOfNode>();
-
- if (doesOpcodeNeedPredicate(Encoding)) {
- unsigned PredicateIndex = getPredicateIndex(Encoding);
- N->addChild(std::make_unique<CheckPredicateNode>(PredicateIndex));
- }
-
- std::vector<FilterChooser::Island> Islands =
- FilterChooser::getIslands(Encoding.getMandatoryBits(), FilterBits);
- for (const FilterChooser::Island &Ilnd : reverse(Islands)) {
- N->addChild(std::make_unique<CheckFieldNode>(Ilnd.StartBit, Ilnd.NumBits,
- Ilnd.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<SoftFailNode>(PositiveMask.getZExtValue(),
- NegativeMask.getZExtValue()));
- }
-
- unsigned DecoderIndex = getDecoderIndex(Encoding);
- N->addChild(std::make_unique<DecodeNode>(EncodingID, DecoderIndex));
-
- return N;
+static bool doesOpcodeNeedPredicate(const InstructionEncoding &Encoding) {
+ std::vector<const Record *> Predicates =
+ Encoding.getRecord()->getValueAsListOfDefs("Predicates");
+ auto MCPredicates = make_filter_range(Predicates, [](const Record *R) {
+ return R->getValue("AssemblerMatcherPredicate");
+ });
+ return !MCPredicates.empty();
}
-std::unique_ptr<DecoderTreeNode> DecoderTreeBuilder::buildAllOfOrSwitchNode(
- unsigned int StartBit, unsigned int NumBits,
- const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap) {
- if (FCMap.size() == 1) {
- const auto &[FieldVal, ChildFC] = *FCMap.begin();
- auto N = std::make_unique<AllOfNode>();
- N->addChild(std::make_unique<CheckFieldNode>(StartBit, NumBits, FieldVal));
- N->addChild(buildAnyOfNode(*ChildFC));
- return N;
- }
- auto N = std::make_unique<SwitchFieldNode>(StartBit, NumBits);
- for (const auto &[FieldVal, ChildFC] : FCMap)
- N->addCase(FieldVal, buildAnyOfNode(*ChildFC));
- return N;
+static std::string getPredicateString(const InstructionEncoding &Encoding,
+ StringRef TargetName) {
+ std::vector<const Record *> Predicates =
+ Encoding.getRecord()->getValueAsListOfDefs("Predicates");
+ std::string Predicate;
+ raw_string_ostream PS(Predicate);
+ SubtargetFeatureInfo::emitMCPredicateCheck(PS, TargetName, Predicates);
+ return Predicate;
}
-std::unique_ptr<DecoderTreeNode>
-DecoderTreeBuilder::buildAnyOfNode(const FilterChooser &FC) {
- auto N = std::make_unique<AnyOfNode>();
- if (FC.SingletonEncodingID) {
- N->addChild(buildTerminalNode(*FC.SingletonEncodingID, FC.FilterBits));
- } else {
- N->addChild(
- buildAllOfOrSwitchNode(FC.StartBit, FC.NumBits, FC.FilterChooserMap));
- }
- if (FC.VariableFC) {
- N->addChild(buildAnyOfNode(*FC.VariableFC));
- }
-
- return N;
-}
-
-void DecoderTreeBuilder::emitBinaryParser(raw_ostream &OS, indent Indent,
- const InstructionEncoding &Encoding,
- const OperandInfo &OpInfo) {
+static void emitBinaryParser(raw_ostream &OS, indent Indent,
+ const InstructionEncoding &Encoding,
+ const OperandInfo &OpInfo) {
if (OpInfo.HasNoEncoding) {
// If an operand has no encoding, the old behavior is to not decode it
// automatically and let the target do it. This is error-prone, so the
@@ -840,8 +777,8 @@ void DecoderTreeBuilder::emitBinaryParser(raw_ostream &OS, indent Indent,
}
}
-void DecoderTreeBuilder::emitDecoder(raw_ostream &OS, indent Indent,
- const InstructionEncoding &Encoding) {
+static void emitDecoder(raw_ostream &OS, indent Indent,
+ const InstructionEncoding &Encoding) {
// If a custom instruction decoder was specified, use that.
StringRef DecoderMethod = Encoding.getDecoderMethod();
if (!DecoderMethod.empty()) {
@@ -856,133 +793,86 @@ void DecoderTreeBuilder::emitDecoder(raw_ostream &OS, indent Indent,
emitBinaryParser(OS, Indent, Encoding, Op);
}
-unsigned
-DecoderTreeBuilder::getDecoderIndex(const InstructionEncoding &Encoding) {
- // Build up the predicate string.
- SmallString<256> Decoder;
- // FIXME: emitDecoder() function can take a buffer directly rather than
- // a stream.
- raw_svector_ostream S(Decoder);
+static std::string getDecoderString(const InstructionEncoding &Encoding) {
+ std::string DecoderString;
+ raw_string_ostream S(DecoderString);
indent Indent(UseFnTableInDecodeToMCInst ? 2 : 4);
emitDecoder(S, Indent, Encoding);
-
- // 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.
-
- // Make sure the predicate is in the table.
- DecoderSet &Decoders = TableInfo.Decoders;
- Decoders.insert(CachedHashString(Decoder));
- // Now figure out the index for when we write out the table.
- DecoderSet::const_iterator P = find(Decoders, Decoder.str());
- return std::distance(Decoders.begin(), P);
+ return DecoderString;
}
-// If ParenIfBinOp is true, print a surrounding () if Val uses && or ||.
-bool DecoderTreeBuilder::emitPredicateMatchAux(StringRef PredicateNamespace,
- const Init &Val,
- bool ParenIfBinOp,
- raw_ostream &OS) {
- if (const auto *D = dyn_cast<DefInit>(&Val)) {
- if (!D->getDef()->isSubClassOf("SubtargetFeature"))
- return true;
- OS << "Bits[" << PredicateNamespace << "::" << D->getAsString() << "]";
- return false;
+std::unique_ptr<DecoderTreeNode>
+DecoderTreeBuilder::buildTerminalNode(unsigned EncodingID,
+ const KnownBits &FilterBits) {
+ const InstructionEncoding &Encoding = Encodings[EncodingID];
+ auto N = std::make_unique<CheckAllNode>();
+
+ if (doesOpcodeNeedPredicate(Encoding)) {
+ std::string Predicate = getPredicateString(Encoding, Target.getName());
+ N->addChild(std::make_unique<CheckPredicateNode>(std::move(Predicate)));
}
- if (const auto *D = dyn_cast<DagInit>(&Val)) {
- std::string Op = D->getOperator()->getAsString();
- if (Op == "not" && D->getNumArgs() == 1) {
- OS << '!';
- return emitPredicateMatchAux(PredicateNamespace, *D->getArg(0), true, OS);
- }
- if ((Op == "any_of" || Op == "all_of") && D->getNumArgs() > 0) {
- bool Paren = D->getNumArgs() > 1 && std::exchange(ParenIfBinOp, true);
- if (Paren)
- OS << '(';
- ListSeparator LS(Op == "any_of" ? " || " : " && ");
- for (auto *Arg : D->getArgs()) {
- OS << LS;
- if (emitPredicateMatchAux(PredicateNamespace, *Arg, ParenIfBinOp, OS))
- return true;
- }
- if (Paren)
- OS << ')';
- return false;
- }
+
+ std::vector<FilterChooser::Island> Islands =
+ FilterChooser::getIslands(Encoding.getMandatoryBits(), FilterBits);
+ for (const FilterChooser::Island &Ilnd : reverse(Islands)) {
+ N->addChild(std::make_unique<CheckFieldNode>(Ilnd.StartBit, Ilnd.NumBits,
+ Ilnd.FieldVal));
}
- return true;
-}
-bool DecoderTreeBuilder::emitPredicateMatch(
- StringRef PredicateNamespace, raw_ostream &OS,
- const InstructionEncoding &Encoding) {
- const ListInit *Predicates =
- Encoding.getRecord()->getValueAsListInit("Predicates");
- bool IsFirstEmission = true;
- for (unsigned i = 0; i < Predicates->size(); ++i) {
- const Record *Pred = Predicates->getElementAsRecord(i);
- if (!Pred->getValue("AssemblerMatcherPredicate"))
- continue;
-
- if (!isa<DagInit>(Pred->getValue("AssemblerCondDag")->getValue()))
- continue;
-
- if (!IsFirstEmission)
- OS << " && ";
- if (emitPredicateMatchAux(PredicateNamespace,
- *Pred->getValueAsDag("AssemblerCondDag"),
- Predicates->size() > 1, OS))
- PrintFatalError(Pred->getLoc(), "Invalid AssemblerCondDag!");
- IsFirstEmission = false;
+ 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<SoftFailNode>(PositiveMask.getZExtValue(),
+ NegativeMask.getZExtValue()));
}
- return !Predicates->empty();
+
+ std::string DecoderIndex = getDecoderString(Encoding);
+ N->addChild(std::make_unique<DecodeNode>(Encoding, DecoderIndex));
+
+ return N;
}
-static bool doesOpcodeNeedPredicate(const InstructionEncoding &Encoding) {
- const ListInit *Predicates =
- Encoding.getRecord()->getValueAsListInit("Predicates");
- for (unsigned i = 0; i < Predicates->size(); ++i) {
- const Record *Pred = Predicates->getElementAsRecord(i);
- if (!Pred->getValue("AssemblerMatcherPredicate"))
- continue;
-
- if (isa<DagInit>(Pred->getValue("AssemblerCondDag")->getValue()))
- return true;
+std::unique_ptr<DecoderTreeNode> DecoderTreeBuilder::buildCheckAllOrSwitchNode(
+ unsigned StartBit, unsigned NumBits,
+ const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap) {
+ if (FCMap.size() == 1) {
+ const auto &[FieldVal, ChildFC] = *FCMap.begin();
+ auto N = std::make_unique<CheckAllNode>();
+ N->addChild(std::make_unique<CheckFieldNode>(StartBit, NumBits, FieldVal));
+ N->addChild(buildCheckAnyNode(*ChildFC));
+ return N;
}
- return false;
+ auto N = std::make_unique<SwitchFieldNode>(StartBit, NumBits);
+ for (const auto &[FieldVal, ChildFC] : FCMap)
+ N->addCase(FieldVal, buildCheckAnyNode(*ChildFC));
+ return N;
}
-unsigned DecoderTreeBuilder::getPredicateIndex(
- const InstructionEncoding &Encoding) const {
- // Build up the predicate string.
- SmallString<256> Predicate;
- // FIXME: emitPredicateMatch() functions can take a buffer directly rather
- // than a stream.
- raw_svector_ostream PS(Predicate);
- emitPredicateMatch(Target.getName(), PS, Encoding);
-
- // 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.
-
- // Make sure the predicate is in the table.
- PredicateSet &Predicates = TableInfo.Predicates;
- Predicates.insert(CachedHashString(Predicate));
- // Now figure out the index for when we write out the table.
- PredicateSet::const_iterator P = find(Predicates, Predicate);
- return std::distance(Predicates.begin(), P);
+std::unique_ptr<DecoderTreeNode>
+DecoderTreeBuilder::buildCheckAnyNode(const FilterChooser &FC) {
+ auto N = std::make_unique<CheckAnyNode>();
+ if (FC.SingletonEncodingID) {
+ N->addChild(buildTerminalNode(*FC.SingletonEncodingID, FC.FilterBits));
+ } else {
+ N->addChild(buildCheckAllOrSwitchNode(FC.StartBit, FC.NumBits,
+ FC.FilterChooserMap));
+ }
+ if (FC.VariableFC) {
+ N->addChild(buildCheckAnyNode(*FC.VariableFC));
+ }
+
+ return N;
}
-std::unique_ptr<DecoderTreeNode> llvm::buildDecoderTree(
- const CodeGenTarget &Target, ArrayRef<InstructionEncoding> Encodings,
- ArrayRef<unsigned> EncodingIDs, DecoderTableInfo &TableInfo) {
+std::unique_ptr<DecoderTreeNode>
+llvm::buildDecoderTree(const CodeGenTarget &Target,
+ ArrayRef<InstructionEncoding> Encodings,
+ ArrayRef<unsigned> EncodingIDs) {
FilterChooser FC(Encodings, EncodingIDs);
if (FC.hasConflict())
return nullptr;
- DecoderTreeBuilder TreeBuilder(Target, Encodings, TableInfo);
+ DecoderTreeBuilder TreeBuilder(Target, Encodings);
return TreeBuilder.buildTree(FC);
}
More information about the llvm-commits
mailing list