aboutsummaryrefslogtreecommitdiff
path: root/llvm/utils
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2023-12-09 13:28:42 +0000
committerDimitry Andric <dim@FreeBSD.org>2023-12-09 13:28:42 +0000
commitb1c73532ee8997fe5dfbeb7d223027bdf99758a0 (patch)
tree7d6e51c294ab6719475d660217aa0c0ad0526292 /llvm/utils
parent7fa27ce4a07f19b07799a767fc29416f3b625afb (diff)
Diffstat (limited to 'llvm/utils')
-rw-r--r--llvm/utils/TableGen/AsmMatcherEmitter.cpp8
-rw-r--r--llvm/utils/TableGen/AsmWriterEmitter.cpp2
-rw-r--r--llvm/utils/TableGen/CallingConvEmitter.cpp13
-rw-r--r--llvm/utils/TableGen/CodeGenRegisters.cpp107
-rw-r--r--llvm/utils/TableGen/CodeGenRegisters.h5
-rw-r--r--llvm/utils/TableGen/CodeGenSchedule.cpp47
-rw-r--r--llvm/utils/TableGen/CodeGenTarget.cpp2
-rw-r--r--llvm/utils/TableGen/CodeGenTarget.h2
-rw-r--r--llvm/utils/TableGen/CompressInstEmitter.cpp249
-rw-r--r--llvm/utils/TableGen/DAGISelMatcherEmitter.cpp156
-rw-r--r--llvm/utils/TableGen/DAGISelMatcherGen.cpp2
-rw-r--r--llvm/utils/TableGen/DXILEmitter.cpp2
-rw-r--r--llvm/utils/TableGen/DirectiveEmitter.cpp1
-rw-r--r--llvm/utils/TableGen/FastISelEmitter.cpp1
-rw-r--r--llvm/utils/TableGen/GICombinerEmitter.cpp1044
-rw-r--r--llvm/utils/TableGen/GlobalISel/CXXPredicates.cpp51
-rw-r--r--llvm/utils/TableGen/GlobalISel/CXXPredicates.h86
-rw-r--r--llvm/utils/TableGen/GlobalISel/CodeExpander.cpp8
-rw-r--r--llvm/utils/TableGen/GlobalISel/CodeExpansions.h4
-rw-r--r--llvm/utils/TableGen/GlobalISel/CombinerUtils.h14
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchDag.cpp138
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchDag.h240
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchDagEdge.cpp39
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchDagEdge.h70
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchDagInstr.cpp48
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchDagInstr.h118
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.cpp153
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.h133
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.cpp69
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.h145
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchDagPredicateDependencyEdge.cpp38
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchDagPredicateDependencyEdge.h61
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchTree.cpp761
-rw-r--r--llvm/utils/TableGen/GlobalISel/GIMatchTree.h626
-rw-r--r--llvm/utils/TableGen/GlobalISel/MatchDataInfo.cpp49
-rw-r--r--llvm/utils/TableGen/GlobalISel/MatchDataInfo.h90
-rw-r--r--llvm/utils/TableGen/GlobalISel/Patterns.cpp842
-rw-r--r--llvm/utils/TableGen/GlobalISel/Patterns.h690
-rw-r--r--llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp3041
-rw-r--r--llvm/utils/TableGen/GlobalISelCombinerMatchTableEmitter.cpp1575
-rw-r--r--llvm/utils/TableGen/GlobalISelEmitter.cpp102
-rw-r--r--llvm/utils/TableGen/GlobalISelMatchTable.cpp182
-rw-r--r--llvm/utils/TableGen/GlobalISelMatchTable.h282
-rw-r--r--llvm/utils/TableGen/GlobalISelMatchTableExecutorEmitter.cpp64
-rw-r--r--llvm/utils/TableGen/GlobalISelMatchTableExecutorEmitter.h8
-rw-r--r--llvm/utils/TableGen/InfoByHwMode.cpp2
-rw-r--r--llvm/utils/TableGen/InfoByHwMode.h1
-rw-r--r--llvm/utils/TableGen/InstrInfoEmitter.cpp30
-rw-r--r--llvm/utils/TableGen/IntrinsicEmitter.cpp3
-rw-r--r--llvm/utils/TableGen/OptParserEmitter.cpp49
-rw-r--r--llvm/utils/TableGen/OptRSTEmitter.cpp2
-rw-r--r--llvm/utils/TableGen/RISCVTargetDefEmitter.cpp9
-rw-r--r--llvm/utils/TableGen/RegisterBankEmitter.cpp6
-rw-r--r--llvm/utils/TableGen/RegisterInfoEmitter.cpp65
-rw-r--r--llvm/utils/TableGen/SearchableTableEmitter.cpp22
-rw-r--r--llvm/utils/TableGen/SubtargetEmitter.cpp94
-rw-r--r--llvm/utils/TableGen/SubtargetFeatureInfo.cpp32
-rw-r--r--llvm/utils/TableGen/SubtargetFeatureInfo.h17
-rw-r--r--llvm/utils/TableGen/VTEmitter.cpp2
-rw-r--r--llvm/utils/TableGen/VarLenCodeEmitterGen.cpp209
-rw-r--r--llvm/utils/TableGen/X86DisassemblerTables.cpp278
-rw-r--r--llvm/utils/TableGen/X86DisassemblerTables.h8
-rw-r--r--llvm/utils/TableGen/X86EVEX2VEXTablesEmitter.cpp44
-rw-r--r--llvm/utils/TableGen/X86FoldTablesEmitter.cpp322
-rw-r--r--llvm/utils/TableGen/X86ManualFoldTables.def2
-rw-r--r--llvm/utils/TableGen/X86RecognizableInstr.cpp890
-rw-r--r--llvm/utils/TableGen/X86RecognizableInstr.h296
67 files changed, 7090 insertions, 6661 deletions
diff --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
index 1c195200a888..f774f0c1018b 100644
--- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp
+++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
@@ -910,7 +910,7 @@ extractSingletonRegisterForAsmOperand(MatchableInfo::AsmOperand &Op,
return;
}
- if (!Tok.startswith(RegisterPrefix))
+ if (!Tok.starts_with(RegisterPrefix))
return;
StringRef RegName = Tok.substr(RegisterPrefix.size());
@@ -1520,7 +1520,7 @@ void AsmMatcherInfo::buildInfo() {
// If the tblgen -match-prefix option is specified (for tblgen hackers),
// filter the set of instructions we consider.
- if (!StringRef(CGI->TheDef->getName()).startswith(MatchPrefix))
+ if (!StringRef(CGI->TheDef->getName()).starts_with(MatchPrefix))
continue;
// Ignore "codegen only" instructions.
@@ -1555,7 +1555,7 @@ void AsmMatcherInfo::buildInfo() {
// filter the set of instruction aliases we consider, based on the target
// instruction.
if (!StringRef(Alias->ResultInst->TheDef->getName())
- .startswith( MatchPrefix))
+ .starts_with(MatchPrefix))
continue;
StringRef V = Alias->TheDef->getValueAsString("AsmVariantName");
@@ -3204,7 +3204,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
Record *AsmParser = Target.getAsmParser();
StringRef ClassName = AsmParser->getValueAsString("AsmParserClassName");
- emitSourceFileHeader("Assembly Matcher Source Fragment", OS);
+ emitSourceFileHeader("Assembly Matcher Source Fragment", OS, Records);
// Compute the information on the instructions to match.
AsmMatcherInfo Info(AsmParser, Target, Records);
diff --git a/llvm/utils/TableGen/AsmWriterEmitter.cpp b/llvm/utils/TableGen/AsmWriterEmitter.cpp
index 92e71910a800..0220927295cf 100644
--- a/llvm/utils/TableGen/AsmWriterEmitter.cpp
+++ b/llvm/utils/TableGen/AsmWriterEmitter.cpp
@@ -1302,7 +1302,7 @@ void AsmWriterEmitter::run(raw_ostream &O) {
std::vector<std::vector<std::string>> TableDrivenOperandPrinters;
unsigned BitsLeft = 0;
unsigned AsmStrBits = 0;
- emitSourceFileHeader("Assembly Writer Source Fragment", O);
+ emitSourceFileHeader("Assembly Writer Source Fragment", O, Records);
EmitGetMnemonic(O, TableDrivenOperandPrinters, BitsLeft, AsmStrBits);
EmitPrintInstruction(O, TableDrivenOperandPrinters, BitsLeft, AsmStrBits);
EmitGetRegisterName(O);
diff --git a/llvm/utils/TableGen/CallingConvEmitter.cpp b/llvm/utils/TableGen/CallingConvEmitter.cpp
index de3810b2e227..02e7000130cd 100644
--- a/llvm/utils/TableGen/CallingConvEmitter.cpp
+++ b/llvm/utils/TableGen/CallingConvEmitter.cpp
@@ -16,6 +16,7 @@
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
#include <deque>
+#include <set>
using namespace llvm;
@@ -106,12 +107,12 @@ void CallingConvEmitter::EmitCallingConv(Record *CC, raw_ostream &O) {
// Emit all of the actions, in order.
for (unsigned i = 0, e = CCActions->size(); i != e; ++i) {
Record *Action = CCActions->getElementAsRecord(i);
- SwiftAction = llvm::any_of(Action->getSuperClasses(),
- [](const std::pair<Record *, SMRange> &Class) {
- std::string Name =
- Class.first->getNameInitAsString();
- return StringRef(Name).startswith("CCIfSwift");
- });
+ SwiftAction =
+ llvm::any_of(Action->getSuperClasses(),
+ [](const std::pair<Record *, SMRange> &Class) {
+ std::string Name = Class.first->getNameInitAsString();
+ return StringRef(Name).starts_with("CCIfSwift");
+ });
O << "\n";
EmitAction(Action, 2, O);
diff --git a/llvm/utils/TableGen/CodeGenRegisters.cpp b/llvm/utils/TableGen/CodeGenRegisters.cpp
index 5c45290a0657..d1abdb74ea4a 100644
--- a/llvm/utils/TableGen/CodeGenRegisters.cpp
+++ b/llvm/utils/TableGen/CodeGenRegisters.cpp
@@ -965,11 +965,20 @@ static bool TopoOrderRC(const CodeGenRegisterClass &PA,
return StringRef(A->getName()) < B->getName();
}
+std::string CodeGenRegisterClass::getNamespaceQualification() const {
+ return Namespace.empty() ? "" : (Namespace + "::").str();
+}
+
std::string CodeGenRegisterClass::getQualifiedName() const {
- if (Namespace.empty())
- return getName();
- else
- return (Namespace + "::" + getName()).str();
+ return getNamespaceQualification() + getName();
+}
+
+std::string CodeGenRegisterClass::getIdName() const {
+ return getName() + "RegClassID";
+}
+
+std::string CodeGenRegisterClass::getQualifiedIdName() const {
+ return getNamespaceQualification() + getIdName();
}
// Compute sub-classes of all register classes.
@@ -1027,8 +1036,8 @@ void CodeGenRegisterClass::computeSubClasses(CodeGenRegBank &RegBank) {
std::optional<std::pair<CodeGenRegisterClass *, CodeGenRegisterClass *>>
CodeGenRegisterClass::getMatchingSubClassWithSubRegs(
CodeGenRegBank &RegBank, const CodeGenSubRegIndex *SubIdx) const {
- auto SizeOrder = [this](const CodeGenRegisterClass *A,
- const CodeGenRegisterClass *B) {
+ auto WeakSizeOrder = [this](const CodeGenRegisterClass *A,
+ const CodeGenRegisterClass *B) {
// If there are multiple, identical register classes, prefer the original
// register class.
if (A == B)
@@ -1050,7 +1059,7 @@ CodeGenRegisterClass::getMatchingSubClassWithSubRegs(
for (auto &RC : RegClasses)
if (SuperRegRCsBV[RC.EnumValue])
SuperRegRCs.emplace_back(&RC);
- llvm::stable_sort(SuperRegRCs, SizeOrder);
+ llvm::stable_sort(SuperRegRCs, WeakSizeOrder);
assert(SuperRegRCs.front() == BiggestSuperRegRC &&
"Biggest class wasn't first");
@@ -1063,11 +1072,11 @@ CodeGenRegisterClass::getMatchingSubClassWithSubRegs(
if (SuperRegClassesBV.any())
SuperRegClasses.push_back(std::make_pair(&RC, SuperRegClassesBV));
}
- llvm::sort(SuperRegClasses,
- [&](const std::pair<CodeGenRegisterClass *, BitVector> &A,
- const std::pair<CodeGenRegisterClass *, BitVector> &B) {
- return SizeOrder(A.first, B.first);
- });
+ llvm::stable_sort(SuperRegClasses,
+ [&](const std::pair<CodeGenRegisterClass *, BitVector> &A,
+ const std::pair<CodeGenRegisterClass *, BitVector> &B) {
+ return WeakSizeOrder(A.first, B.first);
+ });
// Find the biggest subclass and subreg class such that R:subidx is in the
// subreg class for all R in subclass.
@@ -1166,22 +1175,42 @@ CodeGenRegBank::CodeGenRegBank(RecordKeeper &Records,
for (auto &Idx : SubRegIndices)
Idx.updateComponents(*this);
- // Read in the register definitions.
- std::vector<Record*> Regs = Records.getAllDerivedDefinitions("Register");
- llvm::sort(Regs, LessRecordRegister());
- // Assign the enumeration values.
- for (unsigned i = 0, e = Regs.size(); i != e; ++i)
- getReg(Regs[i]);
+ // Read in the register and register tuple definitions.
+ std::vector<Record *> Regs = Records.getAllDerivedDefinitions("Register");
+ if (!Regs.empty() && Regs[0]->isSubClassOf("X86Reg")) {
+ // For X86, we need to sort Registers and RegisterTuples together to list
+ // new registers and register tuples at a later position. So that we can
+ // reduce unnecessary iterations on unsupported registers in LiveVariables.
+ // TODO: Remove this logic when migrate from LiveVariables to LiveIntervals
+ // completely.
+ std::vector<Record *> Tups =
+ Records.getAllDerivedDefinitions("RegisterTuples");
+ for (Record *R : Tups) {
+ // Expand tuples and merge the vectors
+ std::vector<Record *> TupRegs = *Sets.expand(R);
+ Regs.insert(Regs.end(), TupRegs.begin(), TupRegs.end());
+ }
+
+ llvm::sort(Regs, LessRecordRegister());
+ // Assign the enumeration values.
+ for (unsigned i = 0, e = Regs.size(); i != e; ++i)
+ getReg(Regs[i]);
+ } else {
+ llvm::sort(Regs, LessRecordRegister());
+ // Assign the enumeration values.
+ for (unsigned i = 0, e = Regs.size(); i != e; ++i)
+ getReg(Regs[i]);
- // Expand tuples and number the new registers.
- std::vector<Record*> Tups =
- Records.getAllDerivedDefinitions("RegisterTuples");
+ // Expand tuples and number the new registers.
+ std::vector<Record *> Tups =
+ Records.getAllDerivedDefinitions("RegisterTuples");
- for (Record *R : Tups) {
- std::vector<Record *> TupRegs = *Sets.expand(R);
- llvm::sort(TupRegs, LessRecordRegister());
- for (Record *RC : TupRegs)
- getReg(RC);
+ for (Record *R : Tups) {
+ std::vector<Record *> TupRegs = *Sets.expand(R);
+ llvm::sort(TupRegs, LessRecordRegister());
+ for (Record *RC : TupRegs)
+ getReg(RC);
+ }
}
// Now all the registers are known. Build the object graph of explicit
@@ -2114,8 +2143,8 @@ void CodeGenRegBank::computeRegUnitLaneMasks() {
for (auto &Register : Registers) {
// Create an initial lane mask for all register units.
const auto &RegUnits = Register.getRegUnits();
- CodeGenRegister::RegUnitLaneMaskList
- RegUnitLaneMasks(RegUnits.count(), LaneBitmask::getNone());
+ CodeGenRegister::RegUnitLaneMaskList RegUnitLaneMasks(
+ RegUnits.count(), LaneBitmask::getAll());
// Iterate through SubRegisters.
typedef CodeGenRegister::SubRegMap SubRegMap;
const SubRegMap &SubRegs = Register.getSubRegs();
@@ -2134,7 +2163,7 @@ void CodeGenRegBank::computeRegUnitLaneMasks() {
unsigned u = 0;
for (unsigned RU : RegUnits) {
if (SUI == RU) {
- RegUnitLaneMasks[u] |= LaneMask;
+ RegUnitLaneMasks[u] &= LaneMask;
assert(!Found);
Found = true;
}
@@ -2288,8 +2317,8 @@ void CodeGenRegBank::inferSubClassWithSubReg(CodeGenRegisterClass *RC) {
void CodeGenRegBank::inferMatchingSuperRegClass(CodeGenRegisterClass *RC,
std::list<CodeGenRegisterClass>::iterator FirstSubRegRC) {
- SmallVector<std::pair<const CodeGenRegister*,
- const CodeGenRegister*>, 16> SSPairs;
+ DenseMap<const CodeGenRegister *, std::vector<const CodeGenRegister *>>
+ SubToSuperRegs;
BitVector TopoSigs(getNumTopoSigs());
// Iterate in SubRegIndex numerical order to visit synthetic indices last.
@@ -2301,12 +2330,12 @@ void CodeGenRegBank::inferMatchingSuperRegClass(CodeGenRegisterClass *RC,
continue;
// Build list of (Super, Sub) pairs for this SubIdx.
- SSPairs.clear();
+ SubToSuperRegs.clear();
TopoSigs.reset();
for (const auto Super : RC->getMembers()) {
const CodeGenRegister *Sub = Super->getSubRegs().find(&SubIdx)->second;
assert(Sub && "Missing sub-register");
- SSPairs.push_back(std::make_pair(Super, Sub));
+ SubToSuperRegs[Sub].push_back(Super);
TopoSigs.set(Sub->getTopoSig());
}
@@ -2325,16 +2354,20 @@ void CodeGenRegBank::inferMatchingSuperRegClass(CodeGenRegisterClass *RC,
continue;
// Compute the subset of RC that maps into SubRC.
CodeGenRegister::Vec SubSetVec;
- for (unsigned i = 0, e = SSPairs.size(); i != e; ++i)
- if (SubRC.contains(SSPairs[i].second))
- SubSetVec.push_back(SSPairs[i].first);
+ for (const CodeGenRegister *R : SubRC.getMembers()) {
+ auto It = SubToSuperRegs.find(R);
+ if (It != SubToSuperRegs.end()) {
+ const std::vector<const CodeGenRegister *> &SuperRegs = It->second;
+ SubSetVec.insert(SubSetVec.end(), SuperRegs.begin(), SuperRegs.end());
+ }
+ }
if (SubSetVec.empty())
continue;
// RC injects completely into SubRC.
sortAndUniqueRegisters(SubSetVec);
- if (SubSetVec.size() == SSPairs.size()) {
+ if (SubSetVec.size() == RC->getMembers().size()) {
SubRC.addSuperRegClass(&SubIdx, RC);
continue;
}
diff --git a/llvm/utils/TableGen/CodeGenRegisters.h b/llvm/utils/TableGen/CodeGenRegisters.h
index 15f08d1431f9..97f60811a7d8 100644
--- a/llvm/utils/TableGen/CodeGenRegisters.h
+++ b/llvm/utils/TableGen/CodeGenRegisters.h
@@ -353,8 +353,11 @@ namespace llvm {
// created by TableGen.
Record *getDef() const { return TheDef; }
+ std::string getNamespaceQualification() const;
const std::string &getName() const { return Name; }
std::string getQualifiedName() const;
+ std::string getIdName() const;
+ std::string getQualifiedIdName() const;
ArrayRef<ValueTypeByHwMode> getValueTypes() const { return VTs; }
unsigned getNumValueTypes() const { return VTs.size(); }
bool hasType(const ValueTypeByHwMode &VT) const;
@@ -365,7 +368,7 @@ namespace llvm {
llvm_unreachable("VTNum greater than number of ValueTypes in RegClass!");
}
- // Return true if this this class contains the register.
+ // Return true if this class contains the register.
bool contains(const CodeGenRegister*) const;
// Returns true if RC is a subclass.
diff --git a/llvm/utils/TableGen/CodeGenSchedule.cpp b/llvm/utils/TableGen/CodeGenSchedule.cpp
index 04219a6e54d9..54463da19821 100644
--- a/llvm/utils/TableGen/CodeGenSchedule.cpp
+++ b/llvm/utils/TableGen/CodeGenSchedule.cpp
@@ -91,10 +91,25 @@ struct InstRegexOp : public SetTheory::Operator {
PrintFatalError(Loc, "instregex requires pattern string: " +
Expr->getAsString());
StringRef Original = SI->getValue();
+ // Drop an explicit ^ anchor to not interfere with prefix search.
+ bool HadAnchor = Original.consume_front("^");
// Extract a prefix that we can binary search on.
static const char RegexMetachars[] = "()^$|*+?.[]\\{}";
auto FirstMeta = Original.find_first_of(RegexMetachars);
+ if (FirstMeta != StringRef::npos && FirstMeta > 0) {
+ // If we have a regex like ABC* we can only use AB as the prefix, as
+ // the * acts on C.
+ switch (Original[FirstMeta]) {
+ case '+':
+ case '*':
+ case '?':
+ --FirstMeta;
+ break;
+ default:
+ break;
+ }
+ }
// Look for top-level | or ?. We cannot optimize them to binary search.
if (removeParens(Original).find_first_of("|?") != std::string::npos)
@@ -106,7 +121,10 @@ struct InstRegexOp : public SetTheory::Operator {
if (!PatStr.empty()) {
// For the rest use a python-style prefix match.
std::string pat = std::string(PatStr);
- if (pat[0] != '^') {
+ // Add ^ anchor. If we had one originally, don't need the group.
+ if (HadAnchor) {
+ pat.insert(0, "^");
+ } else {
pat.insert(0, "^(");
pat.insert(pat.end(), ')');
}
@@ -118,7 +136,7 @@ struct InstRegexOp : public SetTheory::Operator {
// The generic opcodes are unsorted, handle them manually.
for (auto *Inst : Generics) {
StringRef InstName = Inst->TheDef->getName();
- if (InstName.startswith(Prefix) &&
+ if (InstName.starts_with(Prefix) &&
(!Regexpr || Regexpr->match(InstName.substr(Prefix.size())))) {
Elts.insert(Inst->TheDef);
NumMatches++;
@@ -134,7 +152,7 @@ struct InstRegexOp : public SetTheory::Operator {
}
bool operator()(StringRef LHS, const CodeGenInstruction *RHS) {
return LHS < RHS->TheDef->getName() &&
- !RHS->TheDef->getName().startswith(LHS);
+ !RHS->TheDef->getName().starts_with(LHS);
}
};
auto Range1 =
@@ -369,19 +387,20 @@ processSTIPredicate(STIPredicateFunction &Fn,
const std::pair<APInt, APInt> &LhsMasks = OpcodeMasks[LhsIdx];
const std::pair<APInt, APInt> &RhsMasks = OpcodeMasks[RhsIdx];
- auto LessThan = [](const APInt &Lhs, const APInt &Rhs) {
- unsigned LhsCountPopulation = Lhs.popcount();
- unsigned RhsCountPopulation = Rhs.popcount();
- return ((LhsCountPopulation < RhsCountPopulation) ||
- ((LhsCountPopulation == RhsCountPopulation) &&
- (Lhs.countl_zero() > Rhs.countl_zero())));
+ auto PopulationCountAndLeftBit =
+ [](const APInt &Other) -> std::pair<int, int> {
+ return std::pair<int, int>(Other.popcount(),
+ -Other.countl_zero());
};
+ auto lhsmask_first = PopulationCountAndLeftBit(LhsMasks.first);
+ auto rhsmask_first = PopulationCountAndLeftBit(RhsMasks.first);
+ if (lhsmask_first != rhsmask_first)
+ return lhsmask_first < rhsmask_first;
- if (LhsMasks.first != RhsMasks.first)
- return LessThan(LhsMasks.first, RhsMasks.first);
-
- if (LhsMasks.second != RhsMasks.second)
- return LessThan(LhsMasks.second, RhsMasks.second);
+ auto lhsmask_second = PopulationCountAndLeftBit(LhsMasks.second);
+ auto rhsmask_second = PopulationCountAndLeftBit(RhsMasks.second);
+ if (lhsmask_second != rhsmask_second)
+ return lhsmask_second < rhsmask_second;
return LhsIdx < RhsIdx;
});
diff --git a/llvm/utils/TableGen/CodeGenTarget.cpp b/llvm/utils/TableGen/CodeGenTarget.cpp
index fbdc0499a8cf..53efa66f9dfc 100644
--- a/llvm/utils/TableGen/CodeGenTarget.cpp
+++ b/llvm/utils/TableGen/CodeGenTarget.cpp
@@ -43,7 +43,7 @@ static cl::opt<unsigned>
/// getValueType - Return the MVT::SimpleValueType that the specified TableGen
/// record corresponds to.
-MVT::SimpleValueType llvm::getValueType(Record *Rec) {
+MVT::SimpleValueType llvm::getValueType(const Record *Rec) {
return (MVT::SimpleValueType)Rec->getValueAsInt("Value");
}
diff --git a/llvm/utils/TableGen/CodeGenTarget.h b/llvm/utils/TableGen/CodeGenTarget.h
index 2ba3af724d36..a2b559d53b19 100644
--- a/llvm/utils/TableGen/CodeGenTarget.h
+++ b/llvm/utils/TableGen/CodeGenTarget.h
@@ -43,7 +43,7 @@ class CodeGenSubRegIndex;
/// getValueType - Return the MVT::SimpleValueType that the specified TableGen
/// record corresponds to.
-MVT::SimpleValueType getValueType(Record *Rec);
+MVT::SimpleValueType getValueType(const Record *Rec);
StringRef getName(MVT::SimpleValueType T);
StringRef getEnumName(MVT::SimpleValueType T);
diff --git a/llvm/utils/TableGen/CompressInstEmitter.cpp b/llvm/utils/TableGen/CompressInstEmitter.cpp
index 9d9b69f4cfbd..f703fff0ef3e 100644
--- a/llvm/utils/TableGen/CompressInstEmitter.cpp
+++ b/llvm/utils/TableGen/CompressInstEmitter.cpp
@@ -16,7 +16,7 @@
// This tablegen backend processes CompressPat declarations in a
// td file and generates all the required checks to validate the pattern
// declarations; validate the input and output operands to generate the correct
-// compressed instructions. The checks include validating different types of
+// compressed instructions. The checks include validating different types of
// operands; register operands, immediate operands, fixed register and fixed
// immediate inputs.
//
@@ -26,7 +26,7 @@
// /// CompressInstEmitter backend.
// class CompressPat<dag input, dag output, list<Predicate> predicates = []> {
// /// Uncompressed instruction description.
-// dag Input = input;
+// dag Input = input;
// /// Compressed instruction description.
// dag Output = output;
// /// Predicates that must be true for this to match.
@@ -105,8 +105,8 @@ class CompressInstEmitter {
// Required target features to enable pattern.
std::vector<Record *> PatReqFeatures;
// Maps operands in the Source Instruction to
- IndexedMap<OpData> SourceOperandMap;
// the corresponding Dest instruction operand.
+ IndexedMap<OpData> SourceOperandMap;
// Maps operands in the Dest Instruction
// to the corresponding Source instruction operand.
IndexedMap<OpData> DestOperandMap;
@@ -126,7 +126,7 @@ class CompressInstEmitter {
void addDagOperandMapping(Record *Rec, DagInit *Dag, CodeGenInstruction &Inst,
IndexedMap<OpData> &OperandMap, bool IsSourceInst);
void evaluateCompressPat(Record *Compress);
- void emitCompressInstEmitter(raw_ostream &o, EmitterType EType);
+ void emitCompressInstEmitter(raw_ostream &OS, EmitterType EType);
bool validateTypes(Record *SubType, Record *Type, bool IsSourceInst);
bool validateRegister(Record *Reg, Record *RegClass);
void createDagOperandMapping(Record *Rec, StringMap<unsigned> &SourceOperands,
@@ -144,7 +144,7 @@ class CompressInstEmitter {
public:
CompressInstEmitter(RecordKeeper &R) : Records(R), Target(R) {}
- void run(raw_ostream &o);
+ void run(raw_ostream &OS);
};
} // End anonymous namespace.
@@ -206,53 +206,53 @@ void CompressInstEmitter::addDagOperandMapping(Record *Rec, DagInit *Dag,
// than number of operands in the Dag due to how tied operands
// are represented.
unsigned TiedCount = 0;
- for (unsigned i = 0, e = Inst.Operands.size(); i != e; ++i) {
- int TiedOpIdx = Inst.Operands[i].getTiedRegister();
+ for (unsigned I = 0, E = Inst.Operands.size(); I != E; ++I) {
+ int TiedOpIdx = Inst.Operands[I].getTiedRegister();
if (-1 != TiedOpIdx) {
// Set the entry in OperandMap for the tied operand we're skipping.
- OperandMap[i].Kind = OperandMap[TiedOpIdx].Kind;
- OperandMap[i].Data = OperandMap[TiedOpIdx].Data;
+ OperandMap[I].Kind = OperandMap[TiedOpIdx].Kind;
+ OperandMap[I].Data = OperandMap[TiedOpIdx].Data;
TiedCount++;
continue;
}
- if (DefInit *DI = dyn_cast<DefInit>(Dag->getArg(i - TiedCount))) {
+ if (DefInit *DI = dyn_cast<DefInit>(Dag->getArg(I - TiedCount))) {
if (DI->getDef()->isSubClassOf("Register")) {
// Check if the fixed register belongs to the Register class.
- if (!validateRegister(DI->getDef(), Inst.Operands[i].Rec))
+ if (!validateRegister(DI->getDef(), Inst.Operands[I].Rec))
PrintFatalError(Rec->getLoc(),
"Error in Dag '" + Dag->getAsString() +
"'Register: '" + DI->getDef()->getName() +
"' is not in register class '" +
- Inst.Operands[i].Rec->getName() + "'");
- OperandMap[i].Kind = OpData::Reg;
- OperandMap[i].Data.Reg = DI->getDef();
+ Inst.Operands[I].Rec->getName() + "'");
+ OperandMap[I].Kind = OpData::Reg;
+ OperandMap[I].Data.Reg = DI->getDef();
continue;
}
// Validate that Dag operand type matches the type defined in the
// corresponding instruction. Operands in the input Dag pattern are
// allowed to be a subclass of the type specified in corresponding
// instruction operand instead of being an exact match.
- if (!validateTypes(DI->getDef(), Inst.Operands[i].Rec, IsSourceInst))
+ if (!validateTypes(DI->getDef(), Inst.Operands[I].Rec, IsSourceInst))
PrintFatalError(Rec->getLoc(),
"Error in Dag '" + Dag->getAsString() + "'. Operand '" +
- Dag->getArgNameStr(i - TiedCount) + "' has type '" +
+ Dag->getArgNameStr(I - TiedCount) + "' has type '" +
DI->getDef()->getName() +
"' which does not match the type '" +
- Inst.Operands[i].Rec->getName() +
+ Inst.Operands[I].Rec->getName() +
"' in the corresponding instruction operand!");
- OperandMap[i].Kind = OpData::Operand;
- } else if (IntInit *II = dyn_cast<IntInit>(Dag->getArg(i - TiedCount))) {
+ OperandMap[I].Kind = OpData::Operand;
+ } else if (IntInit *II = dyn_cast<IntInit>(Dag->getArg(I - TiedCount))) {
// Validate that corresponding instruction operand expects an immediate.
- if (Inst.Operands[i].Rec->isSubClassOf("RegisterClass"))
+ if (Inst.Operands[I].Rec->isSubClassOf("RegisterClass"))
PrintFatalError(
Rec->getLoc(),
"Error in Dag '" + Dag->getAsString() + "' Found immediate: '" +
II->getAsString() +
"' but corresponding instruction operand expected a register!");
// No pattern validation check possible for values of fixed immediate.
- OperandMap[i].Kind = OpData::Imm;
- OperandMap[i].Data.Imm = II->getValue();
+ OperandMap[I].Kind = OpData::Imm;
+ OperandMap[I].Data.Imm = II->getValue();
LLVM_DEBUG(
dbgs() << " Found immediate '" << II->getValue() << "' at "
<< (IsSourceInst ? "input " : "output ")
@@ -281,7 +281,7 @@ static bool verifyDagOpCount(CodeGenInstruction &Inst, DagInit *Dag,
"' and Dag operand count mismatch");
// The Instruction might have tied operands so the Dag might have
- // a fewer operand count.
+ // a fewer operand count.
unsigned RealCount = Inst.Operands.size();
for (const auto &Operand : Inst.Operands)
if (Operand.getTiedRegister() != -1)
@@ -307,43 +307,43 @@ void CompressInstEmitter::createDagOperandMapping(
Record *Rec, StringMap<unsigned> &SourceOperands,
StringMap<unsigned> &DestOperands, DagInit *SourceDag, DagInit *DestDag,
IndexedMap<OpData> &SourceOperandMap) {
- for (unsigned i = 0; i < DestDag->getNumArgs(); ++i) {
+ for (unsigned I = 0; I < DestDag->getNumArgs(); ++I) {
// Skip fixed immediates and registers, they were handled in
// addDagOperandMapping.
- if ("" == DestDag->getArgNameStr(i))
+ if ("" == DestDag->getArgNameStr(I))
continue;
- DestOperands[DestDag->getArgNameStr(i)] = i;
+ DestOperands[DestDag->getArgNameStr(I)] = I;
}
- for (unsigned i = 0; i < SourceDag->getNumArgs(); ++i) {
+ for (unsigned I = 0; I < SourceDag->getNumArgs(); ++I) {
// Skip fixed immediates and registers, they were handled in
// addDagOperandMapping.
- if ("" == SourceDag->getArgNameStr(i))
+ if ("" == SourceDag->getArgNameStr(I))
continue;
- StringMap<unsigned>::iterator it =
- SourceOperands.find(SourceDag->getArgNameStr(i));
- if (it != SourceOperands.end()) {
+ StringMap<unsigned>::iterator It =
+ SourceOperands.find(SourceDag->getArgNameStr(I));
+ if (It != SourceOperands.end()) {
// Operand sharing the same name in the Dag should be mapped as tied.
- SourceOperandMap[i].TiedOpIdx = it->getValue();
- if (!validateArgsTypes(SourceDag->getArg(it->getValue()),
- SourceDag->getArg(i)))
+ SourceOperandMap[I].TiedOpIdx = It->getValue();
+ if (!validateArgsTypes(SourceDag->getArg(It->getValue()),
+ SourceDag->getArg(I)))
PrintFatalError(Rec->getLoc(),
- "Input Operand '" + SourceDag->getArgNameStr(i) +
+ "Input Operand '" + SourceDag->getArgNameStr(I) +
"' has a mismatched tied operand!\n");
}
- it = DestOperands.find(SourceDag->getArgNameStr(i));
- if (it == DestOperands.end())
- PrintFatalError(Rec->getLoc(), "Operand " + SourceDag->getArgNameStr(i) +
+ It = DestOperands.find(SourceDag->getArgNameStr(I));
+ if (It == DestOperands.end())
+ PrintFatalError(Rec->getLoc(), "Operand " + SourceDag->getArgNameStr(I) +
" defined in Input Dag but not used in"
" Output Dag!\n");
// Input Dag operand types must match output Dag operand type.
- if (!validateArgsTypes(DestDag->getArg(it->getValue()),
- SourceDag->getArg(i)))
+ if (!validateArgsTypes(DestDag->getArg(It->getValue()),
+ SourceDag->getArg(I)))
PrintFatalError(Rec->getLoc(), "Type mismatch between Input and "
"Output Dag operand '" +
- SourceDag->getArgNameStr(i) + "'!");
- SourceOperands[SourceDag->getArgNameStr(i)] = i;
+ SourceDag->getArgNameStr(I) + "'!");
+ SourceOperands[SourceDag->getArgNameStr(I)] = I;
}
}
@@ -358,27 +358,27 @@ void CompressInstEmitter::createInstOperandMapping(
// operands list to get to the corresponding Dag operand.
unsigned TiedCount = 0;
LLVM_DEBUG(dbgs() << " Operand mapping:\n Source Dest\n");
- for (unsigned i = 0, e = DestInst.Operands.size(); i != e; ++i) {
- int TiedInstOpIdx = DestInst.Operands[i].getTiedRegister();
+ for (unsigned I = 0, E = DestInst.Operands.size(); I != E; ++I) {
+ int TiedInstOpIdx = DestInst.Operands[I].getTiedRegister();
if (TiedInstOpIdx != -1) {
++TiedCount;
- DestOperandMap[i].Data = DestOperandMap[TiedInstOpIdx].Data;
- DestOperandMap[i].Kind = DestOperandMap[TiedInstOpIdx].Kind;
- if (DestOperandMap[i].Kind == OpData::Operand)
+ DestOperandMap[I].Data = DestOperandMap[TiedInstOpIdx].Data;
+ DestOperandMap[I].Kind = DestOperandMap[TiedInstOpIdx].Kind;
+ if (DestOperandMap[I].Kind == OpData::Operand)
// No need to fill the SourceOperandMap here since it was mapped to
// destination operand 'TiedInstOpIdx' in a previous iteration.
- LLVM_DEBUG(dbgs() << " " << DestOperandMap[i].Data.Operand
- << " ====> " << i
+ LLVM_DEBUG(dbgs() << " " << DestOperandMap[I].Data.Operand
+ << " ====> " << I
<< " Dest operand tied with operand '"
<< TiedInstOpIdx << "'\n");
continue;
}
// Skip fixed immediates and registers, they were handled in
// addDagOperandMapping.
- if (DestOperandMap[i].Kind != OpData::Operand)
+ if (DestOperandMap[I].Kind != OpData::Operand)
continue;
- unsigned DagArgIdx = i - TiedCount;
+ unsigned DagArgIdx = I - TiedCount;
StringMap<unsigned>::iterator SourceOp =
SourceOperands.find(DestDag->getArgNameStr(DagArgIdx));
if (SourceOp == SourceOperands.end())
@@ -390,9 +390,9 @@ void CompressInstEmitter::createInstOperandMapping(
assert(DestDag->getArgNameStr(DagArgIdx) ==
SourceDag->getArgNameStr(SourceOp->getValue()) &&
"Incorrect operand mapping detected!\n");
- DestOperandMap[i].Data.Operand = SourceOp->getValue();
- SourceOperandMap[SourceOp->getValue()].Data.Operand = i;
- LLVM_DEBUG(dbgs() << " " << SourceOp->getValue() << " ====> " << i
+ DestOperandMap[I].Data.Operand = SourceOp->getValue();
+ SourceOperandMap[SourceOp->getValue()].Data.Operand = I;
+ LLVM_DEBUG(dbgs() << " " << SourceOp->getValue() << " ====> " << I
<< "\n");
}
}
@@ -403,10 +403,10 @@ void CompressInstEmitter::createInstOperandMapping(
/// - Dag Input opcode is an expanded instruction and Dag Output opcode is a
/// compressed instruction.
/// - Operands in Dag Input must be all used in Dag Output.
-/// Register Operand type in Dag Input Type must be contained in the
+/// Register Operand type in Dag Input Type must be contained in the
/// corresponding Source Instruction type.
-/// - Register Operand type in Dag Input must be the same as in Dag Ouput.
-/// - Register Operand type in Dag Output must be the same as the
+/// - Register Operand type in Dag Input must be the same as in Dag Ouput.
+/// - Register Operand type in Dag Output must be the same as the
/// corresponding Destination Inst type.
/// - Immediate Operand type in Dag Input must be the same as in Dag Ouput.
/// - Immediate Operand type in Dag Ouput must be the same as the corresponding
@@ -414,11 +414,12 @@ void CompressInstEmitter::createInstOperandMapping(
/// - Fixed register must be contained in the corresponding Source Instruction
/// type.
/// - Fixed register must be contained in the corresponding Destination
-/// Instruction type. Warning message printed under these conditions:
+/// Instruction type.
+/// Warning message printed under these conditions:
/// - Fixed immediate in Dag Input or Dag Ouput cannot be checked at this time
/// and generate warning.
/// - Immediate operand type in Dag Input differs from the corresponding Source
-/// Instruction type and generate a warning.
+/// Instruction type and generate a warning.
void CompressInstEmitter::evaluateCompressPat(Record *Rec) {
// Validate input Dag operands.
DagInit *SourceDag = Rec->getValueAsDag("Input");
@@ -426,8 +427,8 @@ void CompressInstEmitter::evaluateCompressPat(Record *Rec) {
LLVM_DEBUG(dbgs() << "Input: " << *SourceDag << "\n");
// Checking we are transforming from compressed to uncompressed instructions.
- Record *Operator = SourceDag->getOperatorAsDef(Rec->getLoc());
- CodeGenInstruction SourceInst(Operator);
+ Record *SourceOperator = SourceDag->getOperatorAsDef(Rec->getLoc());
+ CodeGenInstruction SourceInst(SourceOperator);
verifyDagOpCount(SourceInst, SourceDag, true);
// Validate output Dag operands.
@@ -439,12 +440,13 @@ void CompressInstEmitter::evaluateCompressPat(Record *Rec) {
CodeGenInstruction DestInst(DestOperator);
verifyDagOpCount(DestInst, DestDag, false);
- if (Operator->getValueAsInt("Size") <= DestOperator->getValueAsInt("Size"))
+ if (SourceOperator->getValueAsInt("Size") <=
+ DestOperator->getValueAsInt("Size"))
PrintFatalError(
Rec->getLoc(),
"Compressed instruction '" + DestOperator->getName() +
"'is not strictly smaller than the uncompressed instruction '" +
- Operator->getName() + "' !");
+ SourceOperator->getName() + "' !");
// Fill the mapping from the source to destination instructions.
@@ -538,13 +540,13 @@ static unsigned getPredicates(DenseMap<const Record *, unsigned> &PredicateMap,
}
static void printPredicates(const std::vector<const Record *> &Predicates,
- StringRef Name, raw_ostream &o) {
- for (unsigned i = 0; i < Predicates.size(); ++i) {
- StringRef Pred = Predicates[i]->getValueAsString(Name);
- o << " case " << i + 1 << ": {\n"
- << " // " << Predicates[i]->getName() << "\n"
- << " " << Pred << "\n"
- << " }\n";
+ StringRef Name, raw_ostream &OS) {
+ for (unsigned I = 0; I < Predicates.size(); ++I) {
+ StringRef Pred = Predicates[I]->getValueAsString(Name);
+ OS << " case " << I + 1 << ": {\n"
+ << " // " << Predicates[I]->getName() << "\n"
+ << " " << Pred << "\n"
+ << " }\n";
}
}
@@ -558,7 +560,7 @@ static void mergeCondAndCode(raw_ostream &CombinedStream, StringRef CondStr,
CombinedStream.indent(4) << "} // if\n";
}
-void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &o,
+void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &OS,
EmitterType EType) {
Record *AsmWriter = Target.getAsmWriter();
if (!AsmWriter->getValueAsInt("PassSubtarget"))
@@ -577,8 +579,7 @@ void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &o,
const CompressPat &RHS) {
if (EType == EmitterType::Compress || EType == EmitterType::CheckCompress)
return (LHS.Source.TheDef->getName() < RHS.Source.TheDef->getName());
- else
- return (LHS.Dest.TheDef->getName() < RHS.Dest.TheDef->getName());
+ return (LHS.Dest.TheDef->getName() < RHS.Dest.TheDef->getName());
});
// A list of MCOperandPredicates for all operands in use, and the reverse map.
@@ -594,14 +595,14 @@ void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &o,
raw_string_ostream FuncH(FH);
if (EType == EmitterType::Compress)
- o << "\n#ifdef GEN_COMPRESS_INSTR\n"
- << "#undef GEN_COMPRESS_INSTR\n\n";
+ OS << "\n#ifdef GEN_COMPRESS_INSTR\n"
+ << "#undef GEN_COMPRESS_INSTR\n\n";
else if (EType == EmitterType::Uncompress)
- o << "\n#ifdef GEN_UNCOMPRESS_INSTR\n"
- << "#undef GEN_UNCOMPRESS_INSTR\n\n";
+ OS << "\n#ifdef GEN_UNCOMPRESS_INSTR\n"
+ << "#undef GEN_UNCOMPRESS_INSTR\n\n";
else if (EType == EmitterType::CheckCompress)
- o << "\n#ifdef GEN_CHECK_COMPRESS_INSTR\n"
- << "#undef GEN_CHECK_COMPRESS_INSTR\n\n";
+ OS << "\n#ifdef GEN_CHECK_COMPRESS_INSTR\n"
+ << "#undef GEN_CHECK_COMPRESS_INSTR\n\n";
if (EType == EmitterType::Compress) {
FuncH << "static bool compressInst(MCInst &OutInst,\n";
@@ -617,14 +618,14 @@ void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &o,
}
if (CompressPatterns.empty()) {
- o << FuncH.str();
- o.indent(2) << "return false;\n}\n";
+ OS << FuncH.str();
+ OS.indent(2) << "return false;\n}\n";
if (EType == EmitterType::Compress)
- o << "\n#endif //GEN_COMPRESS_INSTR\n";
+ OS << "\n#endif //GEN_COMPRESS_INSTR\n";
else if (EType == EmitterType::Uncompress)
- o << "\n#endif //GEN_UNCOMPRESS_INSTR\n\n";
+ OS << "\n#endif //GEN_UNCOMPRESS_INSTR\n\n";
else if (EType == EmitterType::CheckCompress)
- o << "\n#endif //GEN_CHECK_COMPRESS_INSTR\n\n";
+ OS << "\n#endif //GEN_CHECK_COMPRESS_INSTR\n\n";
return;
}
@@ -698,11 +699,11 @@ void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &o,
for (auto &Set : AnyOfFeatureSets) {
CondStream.indent(6) << "(";
for (auto &Op : Set) {
- bool isLast = &Op == &*Set.rbegin();
+ bool IsLast = &Op == &*Set.rbegin();
StringRef Not = Op.first ? "!" : "";
CondStream << Not << "STI.getFeatureBits()[" << TargetName
<< "::" << Op.second << "]";
- if (!isLast)
+ if (!IsLast)
CondStream << " || ";
}
CondStream << ") &&\n";
@@ -766,8 +767,8 @@ void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &o,
if (DestOperand.getTiedRegister() == -1)
CondStream.indent(6)
<< "(MI.getOperand(" << OpIdx << ").isReg()) &&\n"
- << " (" << TargetName << "MCRegisterClasses["
- << TargetName << "::" << ClassRec->getName()
+ << " (" << TargetName << "MCRegisterClasses[" << TargetName
+ << "::" << ClassRec->getName()
<< "RegClassID].contains(MI.getOperand(" << OpIdx
<< ").getReg())) &&\n";
@@ -790,8 +791,8 @@ void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &o,
CondStream.indent(6)
<< "MI.getOperand(" << OpIdx << ").isImm() &&\n";
CondStream.indent(6) << TargetName << "ValidateMachineOperand("
- << "MI.getOperand(" << OpIdx
- << "), &STI, " << Entry << ") &&\n";
+ << "MI.getOperand(" << OpIdx << "), &STI, "
+ << Entry << ") &&\n";
}
if (CompressOrUncompress)
CodeStream.indent(6)
@@ -844,63 +845,63 @@ void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &o,
Func.indent(2) << "return false;\n}\n";
if (!MCOpPredicates.empty()) {
- o << "static bool " << ValidatorName << "(const MCOperand &MCOp,\n"
- << " const MCSubtargetInfo &STI,\n"
- << " unsigned PredicateIndex) {\n"
- << " switch (PredicateIndex) {\n"
- << " default:\n"
- << " llvm_unreachable(\"Unknown MCOperandPredicate kind\");\n"
- << " break;\n";
+ OS << "static bool " << ValidatorName << "(const MCOperand &MCOp,\n"
+ << " const MCSubtargetInfo &STI,\n"
+ << " unsigned PredicateIndex) {\n"
+ << " switch (PredicateIndex) {\n"
+ << " default:\n"
+ << " llvm_unreachable(\"Unknown MCOperandPredicate kind\");\n"
+ << " break;\n";
- printPredicates(MCOpPredicates, "MCOperandPredicate", o);
+ printPredicates(MCOpPredicates, "MCOperandPredicate", OS);
- o << " }\n"
- << "}\n\n";
+ OS << " }\n"
+ << "}\n\n";
}
if (!ImmLeafPredicates.empty()) {
- o << "static bool " << TargetName
- << "ValidateMachineOperand(const MachineOperand &MO,\n"
- << " const " << TargetName << "Subtarget *Subtarget,\n"
- << " unsigned PredicateIndex) {\n"
- << " int64_t Imm = MO.getImm();\n"
- << " switch (PredicateIndex) {\n"
- << " default:\n"
- << " llvm_unreachable(\"Unknown ImmLeaf Predicate kind\");\n"
- << " break;\n";
+ OS << "static bool " << TargetName
+ << "ValidateMachineOperand(const MachineOperand &MO,\n"
+ << " const " << TargetName << "Subtarget *Subtarget,\n"
+ << " unsigned PredicateIndex) {\n"
+ << " int64_t Imm = MO.getImm();\n"
+ << " switch (PredicateIndex) {\n"
+ << " default:\n"
+ << " llvm_unreachable(\"Unknown ImmLeaf Predicate kind\");\n"
+ << " break;\n";
- printPredicates(ImmLeafPredicates, "ImmediateCode", o);
+ printPredicates(ImmLeafPredicates, "ImmediateCode", OS);
- o << " }\n"
- << "}\n\n";
+ OS << " }\n"
+ << "}\n\n";
}
- o << FuncH.str();
- o << Func.str();
+ OS << FuncH.str();
+ OS << Func.str();
if (EType == EmitterType::Compress)
- o << "\n#endif //GEN_COMPRESS_INSTR\n";
+ OS << "\n#endif //GEN_COMPRESS_INSTR\n";
else if (EType == EmitterType::Uncompress)
- o << "\n#endif //GEN_UNCOMPRESS_INSTR\n\n";
+ OS << "\n#endif //GEN_UNCOMPRESS_INSTR\n\n";
else if (EType == EmitterType::CheckCompress)
- o << "\n#endif //GEN_CHECK_COMPRESS_INSTR\n\n";
+ OS << "\n#endif //GEN_CHECK_COMPRESS_INSTR\n\n";
}
-void CompressInstEmitter::run(raw_ostream &o) {
+void CompressInstEmitter::run(raw_ostream &OS) {
std::vector<Record *> Insts = Records.getAllDerivedDefinitions("CompressPat");
// Process the CompressPat definitions, validating them as we do so.
- for (unsigned i = 0, e = Insts.size(); i != e; ++i)
- evaluateCompressPat(Insts[i]);
+ for (unsigned I = 0, E = Insts.size(); I != E; ++I)
+ evaluateCompressPat(Insts[I]);
// Emit file header.
- emitSourceFileHeader("Compress instruction Source Fragment", o);
+ emitSourceFileHeader("Compress instruction Source Fragment", OS, Records);
// Generate compressInst() function.
- emitCompressInstEmitter(o, EmitterType::Compress);
+ emitCompressInstEmitter(OS, EmitterType::Compress);
// Generate uncompressInst() function.
- emitCompressInstEmitter(o, EmitterType::Uncompress);
+ emitCompressInstEmitter(OS, EmitterType::Uncompress);
// Generate isCompressibleInst() function.
- emitCompressInstEmitter(o, EmitterType::CheckCompress);
+ emitCompressInstEmitter(OS, EmitterType::CheckCompress);
}
static TableGen::Emitter::OptClass<CompressInstEmitter>
diff --git a/llvm/utils/TableGen/DAGISelMatcherEmitter.cpp b/llvm/utils/TableGen/DAGISelMatcherEmitter.cpp
index 28d4d585f3dd..ed4be74ad0d4 100644
--- a/llvm/utils/TableGen/DAGISelMatcherEmitter.cpp
+++ b/llvm/utils/TableGen/DAGISelMatcherEmitter.cpp
@@ -472,12 +472,16 @@ EmitMatcher(const Matcher *N, const unsigned Indent, unsigned CurrentIdx,
return 2;
case Matcher::CheckPatternPredicate: {
- StringRef Pred =cast<CheckPatternPredicateMatcher>(N)->getPredicate();
- OS << "OPC_CheckPatternPredicate, " << getPatternPredicate(Pred) << ',';
+ StringRef Pred = cast<CheckPatternPredicateMatcher>(N)->getPredicate();
+ unsigned PredNo = getPatternPredicate(Pred);
+ if (PredNo > 255)
+ OS << "OPC_CheckPatternPredicate2, TARGET_VAL(" << PredNo << "),";
+ else
+ OS << "OPC_CheckPatternPredicate, " << PredNo << ',';
if (!OmitComments)
OS << " // " << Pred;
OS << '\n';
- return 2;
+ return 2 + (PredNo > 255);
}
case Matcher::CheckPredicate: {
TreePredicateFn Pred = cast<CheckPredicateMatcher>(N)->getPredicate();
@@ -665,19 +669,42 @@ EmitMatcher(const Matcher *N, const unsigned Indent, unsigned CurrentIdx,
case Matcher::EmitInteger: {
int64_t Val = cast<EmitIntegerMatcher>(N)->getValue();
- OS << "OPC_EmitInteger, "
- << getEnumName(cast<EmitIntegerMatcher>(N)->getVT()) << ", ";
- unsigned Bytes = 2 + EmitSignedVBRValue(Val, OS);
+ MVT::SimpleValueType VT = cast<EmitIntegerMatcher>(N)->getVT();
+ unsigned OpBytes;
+ switch (VT) {
+ case MVT::i8:
+ case MVT::i16:
+ case MVT::i32:
+ case MVT::i64:
+ OpBytes = 1;
+ OS << "OPC_EmitInteger" << MVT(VT).getScalarSizeInBits() << ", ";
+ break;
+ default:
+ OpBytes = 2;
+ OS << "OPC_EmitInteger, " << getEnumName(VT) << ", ";
+ break;
+ }
+ unsigned Bytes = OpBytes + EmitSignedVBRValue(Val, OS);
OS << '\n';
return Bytes;
}
case Matcher::EmitStringInteger: {
const std::string &Val = cast<EmitStringIntegerMatcher>(N)->getValue();
+ MVT::SimpleValueType VT = cast<EmitStringIntegerMatcher>(N)->getVT();
// These should always fit into 7 bits.
- OS << "OPC_EmitStringInteger, "
- << getEnumName(cast<EmitStringIntegerMatcher>(N)->getVT()) << ", " << Val
- << ",\n";
- return 3;
+ unsigned OpBytes;
+ switch (VT) {
+ case MVT::i32:
+ OpBytes = 1;
+ OS << "OPC_EmitStringInteger" << MVT(VT).getScalarSizeInBits() << ", ";
+ break;
+ default:
+ OpBytes = 2;
+ OS << "OPC_EmitStringInteger, " << getEnumName(VT) << ", ";
+ break;
+ }
+ OS << Val << ",\n";
+ return OpBytes + 1;
}
case Matcher::EmitRegister: {
@@ -1033,45 +1060,80 @@ void MatcherTableEmitter::EmitPredicateFunctions(raw_ostream &OS) {
static StringRef getOpcodeString(Matcher::KindTy Kind) {
switch (Kind) {
- case Matcher::Scope: return "OPC_Scope"; break;
- case Matcher::RecordNode: return "OPC_RecordNode"; break;
- case Matcher::RecordChild: return "OPC_RecordChild"; break;
- case Matcher::RecordMemRef: return "OPC_RecordMemRef"; break;
- case Matcher::CaptureGlueInput: return "OPC_CaptureGlueInput"; break;
- case Matcher::MoveChild: return "OPC_MoveChild"; break;
- case Matcher::MoveParent: return "OPC_MoveParent"; break;
- case Matcher::CheckSame: return "OPC_CheckSame"; break;
- case Matcher::CheckChildSame: return "OPC_CheckChildSame"; break;
+ case Matcher::Scope:
+ return "OPC_Scope";
+ case Matcher::RecordNode:
+ return "OPC_RecordNode";
+ case Matcher::RecordChild:
+ return "OPC_RecordChild";
+ case Matcher::RecordMemRef:
+ return "OPC_RecordMemRef";
+ case Matcher::CaptureGlueInput:
+ return "OPC_CaptureGlueInput";
+ case Matcher::MoveChild:
+ return "OPC_MoveChild";
+ case Matcher::MoveParent:
+ return "OPC_MoveParent";
+ case Matcher::CheckSame:
+ return "OPC_CheckSame";
+ case Matcher::CheckChildSame:
+ return "OPC_CheckChildSame";
case Matcher::CheckPatternPredicate:
- return "OPC_CheckPatternPredicate"; break;
- case Matcher::CheckPredicate: return "OPC_CheckPredicate"; break;
- case Matcher::CheckOpcode: return "OPC_CheckOpcode"; break;
- case Matcher::SwitchOpcode: return "OPC_SwitchOpcode"; break;
- case Matcher::CheckType: return "OPC_CheckType"; break;
- case Matcher::SwitchType: return "OPC_SwitchType"; break;
- case Matcher::CheckChildType: return "OPC_CheckChildType"; break;
- case Matcher::CheckInteger: return "OPC_CheckInteger"; break;
- case Matcher::CheckChildInteger: return "OPC_CheckChildInteger"; break;
- case Matcher::CheckCondCode: return "OPC_CheckCondCode"; break;
- case Matcher::CheckChild2CondCode: return "OPC_CheckChild2CondCode"; break;
- case Matcher::CheckValueType: return "OPC_CheckValueType"; break;
- case Matcher::CheckComplexPat: return "OPC_CheckComplexPat"; break;
- case Matcher::CheckAndImm: return "OPC_CheckAndImm"; break;
- case Matcher::CheckOrImm: return "OPC_CheckOrImm"; break;
+ return "OPC_CheckPatternPredicate";
+ case Matcher::CheckPredicate:
+ return "OPC_CheckPredicate";
+ case Matcher::CheckOpcode:
+ return "OPC_CheckOpcode";
+ case Matcher::SwitchOpcode:
+ return "OPC_SwitchOpcode";
+ case Matcher::CheckType:
+ return "OPC_CheckType";
+ case Matcher::SwitchType:
+ return "OPC_SwitchType";
+ case Matcher::CheckChildType:
+ return "OPC_CheckChildType";
+ case Matcher::CheckInteger:
+ return "OPC_CheckInteger";
+ case Matcher::CheckChildInteger:
+ return "OPC_CheckChildInteger";
+ case Matcher::CheckCondCode:
+ return "OPC_CheckCondCode";
+ case Matcher::CheckChild2CondCode:
+ return "OPC_CheckChild2CondCode";
+ case Matcher::CheckValueType:
+ return "OPC_CheckValueType";
+ case Matcher::CheckComplexPat:
+ return "OPC_CheckComplexPat";
+ case Matcher::CheckAndImm:
+ return "OPC_CheckAndImm";
+ case Matcher::CheckOrImm:
+ return "OPC_CheckOrImm";
case Matcher::CheckFoldableChainNode:
- return "OPC_CheckFoldableChainNode"; break;
- case Matcher::CheckImmAllOnesV: return "OPC_CheckImmAllOnesV"; break;
- case Matcher::CheckImmAllZerosV: return "OPC_CheckImmAllZerosV"; break;
- case Matcher::EmitInteger: return "OPC_EmitInteger"; break;
- case Matcher::EmitStringInteger: return "OPC_EmitStringInteger"; break;
- case Matcher::EmitRegister: return "OPC_EmitRegister"; break;
- case Matcher::EmitConvertToTarget: return "OPC_EmitConvertToTarget"; break;
- case Matcher::EmitMergeInputChains: return "OPC_EmitMergeInputChains"; break;
- case Matcher::EmitCopyToReg: return "OPC_EmitCopyToReg"; break;
- case Matcher::EmitNode: return "OPC_EmitNode"; break;
- case Matcher::MorphNodeTo: return "OPC_MorphNodeTo"; break;
- case Matcher::EmitNodeXForm: return "OPC_EmitNodeXForm"; break;
- case Matcher::CompleteMatch: return "OPC_CompleteMatch"; break;
+ return "OPC_CheckFoldableChainNode";
+ case Matcher::CheckImmAllOnesV:
+ return "OPC_CheckImmAllOnesV";
+ case Matcher::CheckImmAllZerosV:
+ return "OPC_CheckImmAllZerosV";
+ case Matcher::EmitInteger:
+ return "OPC_EmitInteger";
+ case Matcher::EmitStringInteger:
+ return "OPC_EmitStringInteger";
+ case Matcher::EmitRegister:
+ return "OPC_EmitRegister";
+ case Matcher::EmitConvertToTarget:
+ return "OPC_EmitConvertToTarget";
+ case Matcher::EmitMergeInputChains:
+ return "OPC_EmitMergeInputChains";
+ case Matcher::EmitCopyToReg:
+ return "OPC_EmitCopyToReg";
+ case Matcher::EmitNode:
+ return "OPC_EmitNode";
+ case Matcher::MorphNodeTo:
+ return "OPC_MorphNodeTo";
+ case Matcher::EmitNodeXForm:
+ return "OPC_EmitNodeXForm";
+ case Matcher::CompleteMatch:
+ return "OPC_CompleteMatch";
}
llvm_unreachable("Unhandled opcode?");
diff --git a/llvm/utils/TableGen/DAGISelMatcherGen.cpp b/llvm/utils/TableGen/DAGISelMatcherGen.cpp
index f773f7c77a77..d08f57b84b95 100644
--- a/llvm/utils/TableGen/DAGISelMatcherGen.cpp
+++ b/llvm/utils/TableGen/DAGISelMatcherGen.cpp
@@ -710,7 +710,7 @@ void MatcherGen::EmitResultLeafAsOperand(const TreePatternNode *N,
const CodeGenRegisterClass &RC =
CGP.getTargetInfo().getRegisterClass(Def);
if (RC.EnumValue <= 127) {
- std::string Value = getQualifiedName(Def) + "RegClassID";
+ std::string Value = RC.getQualifiedIdName();
AddMatcher(new EmitStringIntegerMatcher(Value, MVT::i32));
ResultOps.push_back(NextRecordedOperandNo++);
} else {
diff --git a/llvm/utils/TableGen/DXILEmitter.cpp b/llvm/utils/TableGen/DXILEmitter.cpp
index b294c66007f8..a199463961be 100644
--- a/llvm/utils/TableGen/DXILEmitter.cpp
+++ b/llvm/utils/TableGen/DXILEmitter.cpp
@@ -81,7 +81,7 @@ struct DXILOperationData {
if (R->getValue("llvm_intrinsic")) {
auto *IntrinsicDef = R->getValueAsDef("llvm_intrinsic");
auto DefName = IntrinsicDef->getName();
- assert(DefName.startswith("int_") && "invalid intrinsic name");
+ assert(DefName.starts_with("int_") && "invalid intrinsic name");
// Remove the int_ from intrinsic name.
Intrinsic = DefName.substr(4);
}
diff --git a/llvm/utils/TableGen/DirectiveEmitter.cpp b/llvm/utils/TableGen/DirectiveEmitter.cpp
index 67033c6290ca..b6aee665f8ee 100644
--- a/llvm/utils/TableGen/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/DirectiveEmitter.cpp
@@ -736,6 +736,7 @@ static void GenerateFlangClausesParser(const DirectiveLanguage &DirLang,
.Case("Name", "name")
.Case("ScalarIntConstantExpr", "scalarIntConstantExpr")
.Case("ScalarIntExpr", "scalarIntExpr")
+ .Case("ScalarExpr", "scalarExpr")
.Case("ScalarLogicalExpr", "scalarLogicalExpr")
.Default(("Parser<" + Clause.getFlangClass() + ">{}")
.toStringRef(Scratch));
diff --git a/llvm/utils/TableGen/FastISelEmitter.cpp b/llvm/utils/TableGen/FastISelEmitter.cpp
index 3f3a63de0c0c..b773a6b91ee2 100644
--- a/llvm/utils/TableGen/FastISelEmitter.cpp
+++ b/llvm/utils/TableGen/FastISelEmitter.cpp
@@ -26,6 +26,7 @@
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
+#include <set>
#include <utility>
using namespace llvm;
diff --git a/llvm/utils/TableGen/GICombinerEmitter.cpp b/llvm/utils/TableGen/GICombinerEmitter.cpp
deleted file mode 100644
index ec26024b6518..000000000000
--- a/llvm/utils/TableGen/GICombinerEmitter.cpp
+++ /dev/null
@@ -1,1044 +0,0 @@
-//===- GlobalCombinerEmitter.cpp - Generate a combiner --------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-//
-/// \file Generate a combiner implementation for GlobalISel from a declarative
-/// syntax
-///
-//===----------------------------------------------------------------------===//
-
-#include "CodeGenTarget.h"
-#include "GlobalISel/CodeExpander.h"
-#include "GlobalISel/CodeExpansions.h"
-#include "GlobalISel/CombinerUtils.h"
-#include "GlobalISel/GIMatchDag.h"
-#include "GlobalISel/GIMatchDagEdge.h"
-#include "GlobalISel/GIMatchDagInstr.h"
-#include "GlobalISel/GIMatchDagOperands.h"
-#include "GlobalISel/GIMatchDagPredicate.h"
-#include "GlobalISel/GIMatchTree.h"
-#include "llvm/ADT/SmallSet.h"
-#include "llvm/ADT/Statistic.h"
-#include "llvm/ADT/StringSet.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/ScopedPrinter.h"
-#include "llvm/TableGen/Error.h"
-#include "llvm/TableGen/Record.h"
-#include "llvm/TableGen/StringMatcher.h"
-#include "llvm/TableGen/TableGenBackend.h"
-#include <cstdint>
-
-using namespace llvm;
-
-#define DEBUG_TYPE "gicombiner-emitter"
-
-// FIXME: Use ALWAYS_ENABLED_STATISTIC once it's available.
-unsigned NumPatternTotal = 0;
-STATISTIC(NumPatternTotalStatistic, "Total number of patterns");
-
-cl::OptionCategory
- GICombinerEmitterCat("Options for -gen-global-isel-combiner");
-cl::list<std::string>
- SelectedCombiners("combiners", cl::desc("Emit the specified combiners"),
- cl::cat(GICombinerEmitterCat), cl::CommaSeparated);
-static cl::opt<bool> ShowExpansions(
- "gicombiner-show-expansions",
- cl::desc("Use C++ comments to indicate occurence of code expansion"),
- cl::cat(GICombinerEmitterCat));
-cl::opt<bool> StopAfterParse(
- "gicombiner-stop-after-parse",
- cl::desc("Stop processing after parsing rules and dump state"),
- cl::cat(GICombinerEmitterCat));
-static cl::opt<bool> StopAfterBuild(
- "gicombiner-stop-after-build",
- cl::desc("Stop processing after building the match tree"),
- cl::cat(GICombinerEmitterCat));
-
-namespace {
-typedef uint64_t RuleID;
-
-// We're going to be referencing the same small strings quite a lot for operand
-// names and the like. Make their lifetime management simple with a global
-// string table.
-StringSet<> StrTab;
-
-StringRef insertStrTab(StringRef S) {
- if (S.empty())
- return S;
- return StrTab.insert(S).first->first();
-}
-
-class format_partition_name {
- const GIMatchTree &Tree;
- unsigned Idx;
-
-public:
- format_partition_name(const GIMatchTree &Tree, unsigned Idx)
- : Tree(Tree), Idx(Idx) {}
- void print(raw_ostream &OS) const {
- Tree.getPartitioner()->emitPartitionName(OS, Idx);
- }
-};
-raw_ostream &operator<<(raw_ostream &OS, const format_partition_name &Fmt) {
- Fmt.print(OS);
- return OS;
-}
-
-/// Declares data that is passed from the match stage to the apply stage.
-class MatchDataInfo {
- /// The symbol used in the tablegen patterns
- StringRef PatternSymbol;
- /// The data type for the variable
- StringRef Type;
- /// The name of the variable as declared in the generated matcher.
- std::string VariableName;
-
-public:
- MatchDataInfo(StringRef PatternSymbol, StringRef Type, StringRef VariableName)
- : PatternSymbol(PatternSymbol), Type(Type), VariableName(VariableName) {}
-
- StringRef getPatternSymbol() const { return PatternSymbol; };
- StringRef getType() const { return Type; };
- StringRef getVariableName() const { return VariableName; };
-};
-
-class RootInfo {
- StringRef PatternSymbol;
-
-public:
- RootInfo(StringRef PatternSymbol) : PatternSymbol(PatternSymbol) {}
-
- StringRef getPatternSymbol() const { return PatternSymbol; }
-};
-
-class CombineRule {
-public:
-
- using const_matchdata_iterator = std::vector<MatchDataInfo>::const_iterator;
-
- struct VarInfo {
- const GIMatchDagInstr *N;
- const GIMatchDagOperand *Op;
- const DagInit *Matcher;
-
- public:
- VarInfo(const GIMatchDagInstr *N, const GIMatchDagOperand *Op,
- const DagInit *Matcher)
- : N(N), Op(Op), Matcher(Matcher) {}
- };
-
-protected:
- /// A unique ID for this rule
- /// ID's are used for debugging and run-time disabling of rules among other
- /// things.
- RuleID ID;
-
- /// A unique ID that can be used for anonymous objects belonging to this rule.
- /// Used to create unique names in makeNameForAnon*() without making tests
- /// overly fragile.
- unsigned UID = 0;
-
- /// The record defining this rule.
- const Record &TheDef;
-
- /// The roots of a match. These are the leaves of the DAG that are closest to
- /// the end of the function. I.e. the nodes that are encountered without
- /// following any edges of the DAG described by the pattern as we work our way
- /// from the bottom of the function to the top.
- std::vector<RootInfo> Roots;
-
- GIMatchDag MatchDag;
-
- /// A block of arbitrary C++ to finish testing the match.
- /// FIXME: This is a temporary measure until we have actual pattern matching
- const StringInit *MatchingFixupCode = nullptr;
-
- /// The MatchData defined by the match stage and required by the apply stage.
- /// This allows the plumbing of arbitrary data from C++ predicates between the
- /// stages.
- ///
- /// For example, suppose you have:
- /// %A = <some-constant-expr>
- /// %0 = G_ADD %1, %A
- /// you could define a GIMatchPredicate that walks %A, constant folds as much
- /// as possible and returns an APInt containing the discovered constant. You
- /// could then declare:
- /// def apint : GIDefMatchData<"APInt">;
- /// add it to the rule with:
- /// (defs root:$root, apint:$constant)
- /// evaluate it in the pattern with a C++ function that takes a
- /// MachineOperand& and an APInt& with:
- /// (match [{MIR %root = G_ADD %0, %A }],
- /// (constantfold operand:$A, apint:$constant))
- /// and finally use it in the apply stage with:
- /// (apply (create_operand
- /// [{ MachineOperand::CreateImm(${constant}.getZExtValue());
- /// ]}, apint:$constant),
- /// [{MIR %root = FOO %0, %constant }])
- std::vector<MatchDataInfo> MatchDataDecls;
-
- void declareMatchData(StringRef PatternSymbol, StringRef Type,
- StringRef VarName);
-
- bool parseInstructionMatcher(const CodeGenTarget &Target, StringInit *ArgName,
- const Init &Arg,
- StringMap<std::vector<VarInfo>> &NamedEdgeDefs,
- StringMap<std::vector<VarInfo>> &NamedEdgeUses);
- bool parseWipMatchOpcodeMatcher(const CodeGenTarget &Target,
- StringInit *ArgName, const Init &Arg);
-
-public:
- CombineRule(const CodeGenTarget &Target, GIMatchDagContext &Ctx, RuleID ID,
- const Record &R)
- : ID(ID), TheDef(R), MatchDag(Ctx) {}
- CombineRule(const CombineRule &) = delete;
-
- bool parseDefs();
- bool parseMatcher(const CodeGenTarget &Target);
-
- RuleID getID() const { return ID; }
- unsigned allocUID() { return UID++; }
- StringRef getName() const { return TheDef.getName(); }
- const Record &getDef() const { return TheDef; }
- const StringInit *getMatchingFixupCode() const { return MatchingFixupCode; }
- size_t getNumRoots() const { return Roots.size(); }
-
- GIMatchDag &getMatchDag() { return MatchDag; }
- const GIMatchDag &getMatchDag() const { return MatchDag; }
-
- using const_root_iterator = std::vector<RootInfo>::const_iterator;
- const_root_iterator roots_begin() const { return Roots.begin(); }
- const_root_iterator roots_end() const { return Roots.end(); }
- iterator_range<const_root_iterator> roots() const {
- return llvm::make_range(Roots.begin(), Roots.end());
- }
-
- iterator_range<const_matchdata_iterator> matchdata_decls() const {
- return make_range(MatchDataDecls.begin(), MatchDataDecls.end());
- }
-
- /// Export expansions for this rule
- void declareExpansions(CodeExpansions &Expansions) const {
- for (const auto &I : matchdata_decls())
- Expansions.declare(I.getPatternSymbol(), I.getVariableName());
- }
-
- /// The matcher will begin from the roots and will perform the match by
- /// traversing the edges to cover the whole DAG. This function reverses DAG
- /// edges such that everything is reachable from a root. This is part of the
- /// preparation work for flattening the DAG into a tree.
- void reorientToRoots() {
- SmallSet<const GIMatchDagInstr *, 5> Roots;
- SmallSet<const GIMatchDagInstr *, 5> Visited;
- SmallSet<GIMatchDagEdge *, 20> EdgesRemaining;
-
- for (auto &I : MatchDag.roots()) {
- Roots.insert(I);
- Visited.insert(I);
- }
- for (auto &I : MatchDag.edges())
- EdgesRemaining.insert(I);
-
- bool Progressed = false;
- SmallSet<GIMatchDagEdge *, 20> EdgesToRemove;
- while (!EdgesRemaining.empty()) {
- for (auto *EI : EdgesRemaining) {
- if (Visited.count(EI->getFromMI())) {
- if (Roots.count(EI->getToMI()))
- PrintError(TheDef.getLoc(), "One or more roots are unnecessary");
- Visited.insert(EI->getToMI());
- EdgesToRemove.insert(EI);
- Progressed = true;
- }
- }
- for (GIMatchDagEdge *ToRemove : EdgesToRemove)
- EdgesRemaining.erase(ToRemove);
- EdgesToRemove.clear();
-
- for (auto EI = EdgesRemaining.begin(), EE = EdgesRemaining.end();
- EI != EE; ++EI) {
- if (Visited.count((*EI)->getToMI())) {
- (*EI)->reverse();
- Visited.insert((*EI)->getToMI());
- EdgesToRemove.insert(*EI);
- Progressed = true;
- }
- for (GIMatchDagEdge *ToRemove : EdgesToRemove)
- EdgesRemaining.erase(ToRemove);
- EdgesToRemove.clear();
- }
-
- if (!Progressed) {
- LLVM_DEBUG(dbgs() << "No progress\n");
- return;
- }
- Progressed = false;
- }
- }
-};
-
-StringRef makeNameForAnonInstr(CombineRule &Rule) {
- return insertStrTab(to_string(
- format("__anon%" PRIu64 "_%u", Rule.getID(), Rule.allocUID())));
-}
-
-StringRef makeDebugName(CombineRule &Rule, StringRef Name) {
- return insertStrTab(Name.empty() ? makeNameForAnonInstr(Rule) : StringRef(Name));
-}
-
-StringRef makeNameForAnonPredicate(CombineRule &Rule) {
- return insertStrTab(to_string(
- format("__anonpred%" PRIu64 "_%u", Rule.getID(), Rule.allocUID())));
-}
-
-void CombineRule::declareMatchData(StringRef PatternSymbol, StringRef Type,
- StringRef VarName) {
- MatchDataDecls.emplace_back(PatternSymbol, Type, VarName);
-}
-
-bool CombineRule::parseDefs() {
- DagInit *Defs = TheDef.getValueAsDag("Defs");
-
- if (Defs->getOperatorAsDef(TheDef.getLoc())->getName() != "defs") {
- PrintError(TheDef.getLoc(), "Expected defs operator");
- return false;
- }
-
- for (unsigned I = 0, E = Defs->getNumArgs(); I < E; ++I) {
- // Roots should be collected into Roots
- if (isSpecificDef(*Defs->getArg(I), "root")) {
- Roots.emplace_back(Defs->getArgNameStr(I));
- continue;
- }
-
- // Subclasses of GIDefMatchData should declare that this rule needs to pass
- // data from the match stage to the apply stage, and ensure that the
- // generated matcher has a suitable variable for it to do so.
- if (Record *MatchDataRec =
- getDefOfSubClass(*Defs->getArg(I), "GIDefMatchData")) {
- declareMatchData(Defs->getArgNameStr(I),
- MatchDataRec->getValueAsString("Type"),
- llvm::to_string(llvm::format("MatchData%" PRIu64, ID)));
- continue;
- }
-
- // Otherwise emit an appropriate error message.
- if (getDefOfSubClass(*Defs->getArg(I), "GIDefKind"))
- PrintError(TheDef.getLoc(),
- "This GIDefKind not implemented in tablegen");
- else if (getDefOfSubClass(*Defs->getArg(I), "GIDefKindWithArgs"))
- PrintError(TheDef.getLoc(),
- "This GIDefKindWithArgs not implemented in tablegen");
- else
- PrintError(TheDef.getLoc(),
- "Expected a subclass of GIDefKind or a sub-dag whose "
- "operator is of type GIDefKindWithArgs");
- return false;
- }
-
- if (Roots.empty()) {
- PrintError(TheDef.getLoc(), "Combine rules must have at least one root");
- return false;
- }
- return true;
-}
-
-// Parse an (Instruction $a:Arg1, $b:Arg2, ...) matcher. Edges are formed
-// between matching operand names between different matchers.
-bool CombineRule::parseInstructionMatcher(
- const CodeGenTarget &Target, StringInit *ArgName, const Init &Arg,
- StringMap<std::vector<VarInfo>> &NamedEdgeDefs,
- StringMap<std::vector<VarInfo>> &NamedEdgeUses) {
- if (const DagInit *Matcher =
- getDagWithOperatorOfSubClass(Arg, "Instruction")) {
- auto &Instr =
- Target.getInstruction(Matcher->getOperatorAsDef(TheDef.getLoc()));
-
- StringRef Name = ArgName ? ArgName->getValue() : "";
-
- GIMatchDagInstr *N =
- MatchDag.addInstrNode(makeDebugName(*this, Name), insertStrTab(Name),
- MatchDag.getContext().makeOperandList(Instr));
-
- N->setOpcodeAnnotation(&Instr);
- const auto &P = MatchDag.addPredicateNode<GIMatchDagOpcodePredicate>(
- makeNameForAnonPredicate(*this), Instr);
- MatchDag.addPredicateDependency(N, nullptr, P, &P->getOperandInfo()["mi"]);
- unsigned OpIdx = 0;
- for (const auto &NameInit : Matcher->getArgNames()) {
- StringRef Name = insertStrTab(NameInit->getAsUnquotedString());
- if (Name.empty())
- continue;
- N->assignNameToOperand(OpIdx, Name);
-
- // Record the endpoints of any named edges. We'll add the cartesian
- // product of edges later.
- const auto &InstrOperand = N->getOperandInfo()[OpIdx];
- if (InstrOperand.isDef()) {
- NamedEdgeDefs.try_emplace(Name);
- NamedEdgeDefs[Name].emplace_back(N, &InstrOperand, Matcher);
- } else {
- NamedEdgeUses.try_emplace(Name);
- NamedEdgeUses[Name].emplace_back(N, &InstrOperand, Matcher);
- }
-
- if (InstrOperand.isDef()) {
- if (any_of(Roots, [&](const RootInfo &X) {
- return X.getPatternSymbol() == Name;
- })) {
- N->setMatchRoot();
- }
- }
-
- OpIdx++;
- }
-
- return true;
- }
- return false;
-}
-
-// Parse the wip_match_opcode placeholder that's temporarily present in lieu of
-// implementing macros or choices between two matchers.
-bool CombineRule::parseWipMatchOpcodeMatcher(const CodeGenTarget &Target,
- StringInit *ArgName,
- const Init &Arg) {
- if (const DagInit *Matcher =
- getDagWithSpecificOperator(Arg, "wip_match_opcode")) {
- StringRef Name = ArgName ? ArgName->getValue() : "";
-
- GIMatchDagInstr *N =
- MatchDag.addInstrNode(makeDebugName(*this, Name), insertStrTab(Name),
- MatchDag.getContext().makeEmptyOperandList());
-
- if (any_of(Roots, [&](const RootInfo &X) {
- return ArgName && X.getPatternSymbol() == ArgName->getValue();
- })) {
- N->setMatchRoot();
- }
-
- const auto &P = MatchDag.addPredicateNode<GIMatchDagOneOfOpcodesPredicate>(
- makeNameForAnonPredicate(*this));
- MatchDag.addPredicateDependency(N, nullptr, P, &P->getOperandInfo()["mi"]);
- // Each argument is an opcode that will pass this predicate. Add them all to
- // the predicate implementation
- for (const auto &Arg : Matcher->getArgs()) {
- Record *OpcodeDef = getDefOfSubClass(*Arg, "Instruction");
- if (OpcodeDef) {
- P->addOpcode(&Target.getInstruction(OpcodeDef));
- continue;
- }
- PrintError(TheDef.getLoc(),
- "Arguments to wip_match_opcode must be instructions");
- return false;
- }
- return true;
- }
- return false;
-}
-bool CombineRule::parseMatcher(const CodeGenTarget &Target) {
- StringMap<std::vector<VarInfo>> NamedEdgeDefs;
- StringMap<std::vector<VarInfo>> NamedEdgeUses;
- DagInit *Matchers = TheDef.getValueAsDag("Match");
-
- if (Matchers->getOperatorAsDef(TheDef.getLoc())->getName() != "match") {
- PrintError(TheDef.getLoc(), "Expected match operator");
- return false;
- }
-
- if (Matchers->getNumArgs() == 0) {
- PrintError(TheDef.getLoc(), "Matcher is empty");
- return false;
- }
-
- // The match section consists of a list of matchers and predicates. Parse each
- // one and add the equivalent GIMatchDag nodes, predicates, and edges.
- for (unsigned I = 0; I < Matchers->getNumArgs(); ++I) {
- if (parseInstructionMatcher(Target, Matchers->getArgName(I),
- *Matchers->getArg(I), NamedEdgeDefs,
- NamedEdgeUses))
- continue;
-
- if (parseWipMatchOpcodeMatcher(Target, Matchers->getArgName(I),
- *Matchers->getArg(I)))
- continue;
-
-
- // Parse arbitrary C++ code we have in lieu of supporting MIR matching
- if (const StringInit *StringI = dyn_cast<StringInit>(Matchers->getArg(I))) {
- assert(!MatchingFixupCode &&
- "Only one block of arbitrary code is currently permitted");
- MatchingFixupCode = StringI;
- MatchDag.setHasPostMatchPredicate(true);
- continue;
- }
-
- PrintError(TheDef.getLoc(),
- "Expected a subclass of GIMatchKind or a sub-dag whose "
- "operator is either of a GIMatchKindWithArgs or Instruction");
- PrintNote("Pattern was `" + Matchers->getArg(I)->getAsString() + "'");
- return false;
- }
-
- // Add the cartesian product of use -> def edges.
- bool FailedToAddEdges = false;
- for (const auto &NameAndDefs : NamedEdgeDefs) {
- if (NameAndDefs.getValue().size() > 1) {
- PrintError(TheDef.getLoc(),
- "Two different MachineInstrs cannot def the same vreg");
- for (const auto &NameAndDefOp : NameAndDefs.getValue())
- PrintNote("in " + to_string(*NameAndDefOp.N) + " created from " +
- to_string(*NameAndDefOp.Matcher) + "");
- FailedToAddEdges = true;
- }
- const auto &Uses = NamedEdgeUses[NameAndDefs.getKey()];
- for (const VarInfo &DefVar : NameAndDefs.getValue()) {
- for (const VarInfo &UseVar : Uses) {
- MatchDag.addEdge(insertStrTab(NameAndDefs.getKey()), UseVar.N, UseVar.Op,
- DefVar.N, DefVar.Op);
- }
- }
- }
- if (FailedToAddEdges)
- return false;
-
- // If a variable is referenced in multiple use contexts then we need a
- // predicate to confirm they are the same operand. We can elide this if it's
- // also referenced in a def context and we're traversing the def-use chain
- // from the def to the uses but we can't know which direction we're going
- // until after reorientToRoots().
- for (const auto &NameAndUses : NamedEdgeUses) {
- const auto &Uses = NameAndUses.getValue();
- if (Uses.size() > 1) {
- const auto &LeadingVar = Uses.front();
- for (const auto &Var : ArrayRef<VarInfo>(Uses).drop_front()) {
- // Add a predicate for each pair until we've covered the whole
- // equivalence set. We could test the whole set in a single predicate
- // but that means we can't test any equivalence until all the MO's are
- // available which can lead to wasted work matching the DAG when this
- // predicate can already be seen to have failed.
- //
- // We have a similar problem due to the need to wait for a particular MO
- // before being able to test any of them. However, that is mitigated by
- // the order in which we build the DAG. We build from the roots outwards
- // so by using the first recorded use in all the predicates, we are
- // making the dependency on one of the earliest visited references in
- // the DAG. It's not guaranteed once the generated matcher is optimized
- // (because the factoring the common portions of rules might change the
- // visit order) but this should mean that these predicates depend on the
- // first MO to become available.
- const auto &P = MatchDag.addPredicateNode<GIMatchDagSameMOPredicate>(
- makeNameForAnonPredicate(*this));
- MatchDag.addPredicateDependency(LeadingVar.N, LeadingVar.Op, P,
- &P->getOperandInfo()["mi0"]);
- MatchDag.addPredicateDependency(Var.N, Var.Op, P,
- &P->getOperandInfo()["mi1"]);
- }
- }
- }
- return true;
-}
-
-class GICombinerEmitter {
- RecordKeeper &Records;
- StringRef Name;
- const CodeGenTarget &Target;
- Record *Combiner;
- std::vector<std::unique_ptr<CombineRule>> Rules;
- GIMatchDagContext MatchDagCtx;
-
- std::unique_ptr<CombineRule> makeCombineRule(const Record &R);
-
- void gatherRules(std::vector<std::unique_ptr<CombineRule>> &ActiveRules,
- const std::vector<Record *> &&RulesAndGroups);
-
-public:
- explicit GICombinerEmitter(RecordKeeper &RK, const CodeGenTarget &Target,
- StringRef Name, Record *Combiner);
- ~GICombinerEmitter() {}
-
- StringRef getClassName() const {
- return Combiner->getValueAsString("Classname");
- }
- void run(raw_ostream &OS);
-
- /// Emit the name matcher (guarded by #ifndef NDEBUG) used to disable rules in
- /// response to the generated cl::opt.
- void emitNameMatcher(raw_ostream &OS) const;
-
- void generateCodeForTree(raw_ostream &OS, const GIMatchTree &Tree,
- StringRef Indent) const;
-};
-
-GICombinerEmitter::GICombinerEmitter(RecordKeeper &RK,
- const CodeGenTarget &Target,
- StringRef Name, Record *Combiner)
- : Records(RK), Name(Name), Target(Target), Combiner(Combiner) {}
-
-void GICombinerEmitter::emitNameMatcher(raw_ostream &OS) const {
- std::vector<std::pair<std::string, std::string>> Cases;
- Cases.reserve(Rules.size());
-
- for (const CombineRule &EnumeratedRule : make_pointee_range(Rules)) {
- std::string Code;
- raw_string_ostream SS(Code);
- SS << "return " << EnumeratedRule.getID() << ";\n";
- Cases.push_back(
- std::make_pair(std::string(EnumeratedRule.getName()), Code));
- }
-
- OS << "static std::optional<uint64_t> getRuleIdxForIdentifier(StringRef "
- "RuleIdentifier) {\n"
- << " uint64_t I;\n"
- << " // getAtInteger(...) returns false on success\n"
- << " bool Parsed = !RuleIdentifier.getAsInteger(0, I);\n"
- << " if (Parsed)\n"
- << " return I;\n\n"
- << "#ifndef NDEBUG\n";
- StringMatcher Matcher("RuleIdentifier", Cases, OS);
- Matcher.Emit();
- OS << "#endif // ifndef NDEBUG\n\n"
- << " return std::nullopt;\n"
- << "}\n";
-}
-
-std::unique_ptr<CombineRule>
-GICombinerEmitter::makeCombineRule(const Record &TheDef) {
- std::unique_ptr<CombineRule> Rule =
- std::make_unique<CombineRule>(Target, MatchDagCtx, NumPatternTotal, TheDef);
-
- if (!Rule->parseDefs())
- return nullptr;
- if (!Rule->parseMatcher(Target))
- return nullptr;
-
- Rule->reorientToRoots();
-
- LLVM_DEBUG({
- dbgs() << "Parsed rule defs/match for '" << Rule->getName() << "'\n";
- Rule->getMatchDag().dump();
- Rule->getMatchDag().writeDOTGraph(dbgs(), Rule->getName());
- });
- if (StopAfterParse)
- return Rule;
-
- // For now, don't support traversing from def to use. We'll come back to
- // this later once we have the algorithm changes to support it.
- bool EmittedDefToUseError = false;
- for (const auto &E : Rule->getMatchDag().edges()) {
- if (E->isDefToUse()) {
- if (!EmittedDefToUseError) {
- PrintError(
- TheDef.getLoc(),
- "Generated state machine cannot lookup uses from a def (yet)");
- EmittedDefToUseError = true;
- }
- PrintNote("Node " + to_string(*E->getFromMI()));
- PrintNote("Node " + to_string(*E->getToMI()));
- PrintNote("Edge " + to_string(*E));
- }
- }
- if (EmittedDefToUseError)
- return nullptr;
-
- // For now, don't support multi-root rules. We'll come back to this later
- // once we have the algorithm changes to support it.
- if (Rule->getNumRoots() > 1) {
- PrintError(TheDef.getLoc(), "Multi-root matches are not supported (yet)");
- return nullptr;
- }
- return Rule;
-}
-
-/// Recurse into GICombineGroup's and flatten the ruleset into a simple list.
-void GICombinerEmitter::gatherRules(
- std::vector<std::unique_ptr<CombineRule>> &ActiveRules,
- const std::vector<Record *> &&RulesAndGroups) {
- for (Record *R : RulesAndGroups) {
- if (R->isValueUnset("Rules")) {
- std::unique_ptr<CombineRule> Rule = makeCombineRule(*R);
- if (Rule == nullptr) {
- PrintError(R->getLoc(), "Failed to parse rule");
- continue;
- }
- ActiveRules.emplace_back(std::move(Rule));
- ++NumPatternTotal;
- } else
- gatherRules(ActiveRules, R->getValueAsListOfDefs("Rules"));
- }
-}
-
-void GICombinerEmitter::generateCodeForTree(raw_ostream &OS,
- const GIMatchTree &Tree,
- StringRef Indent) const {
- if (Tree.getPartitioner() != nullptr) {
- Tree.getPartitioner()->generatePartitionSelectorCode(OS, Indent);
- for (const auto &EnumChildren : enumerate(Tree.children())) {
- OS << Indent << "if (Partition == " << EnumChildren.index() << " /* "
- << format_partition_name(Tree, EnumChildren.index()) << " */) {\n";
- generateCodeForTree(OS, EnumChildren.value(), (Indent + " ").str());
- OS << Indent << "}\n";
- }
- return;
- }
-
- bool AnyFullyTested = false;
- for (const auto &Leaf : Tree.possible_leaves()) {
- OS << Indent << "// Leaf name: " << Leaf.getName() << "\n";
-
- const CombineRule *Rule = Leaf.getTargetData<CombineRule>();
- const Record &RuleDef = Rule->getDef();
-
- OS << Indent << "// Rule: " << RuleDef.getName() << "\n"
- << Indent << "if (!RuleConfig->isRuleDisabled(" << Rule->getID()
- << ")) {\n";
-
- CodeExpansions Expansions;
- for (const auto &VarBinding : Leaf.var_bindings()) {
- if (VarBinding.isInstr())
- Expansions.declare(VarBinding.getName(),
- "MIs[" + to_string(VarBinding.getInstrID()) + "]");
- else
- Expansions.declare(VarBinding.getName(),
- "MIs[" + to_string(VarBinding.getInstrID()) +
- "]->getOperand(" +
- to_string(VarBinding.getOpIdx()) + ")");
- }
- Rule->declareExpansions(Expansions);
-
- DagInit *Applyer = RuleDef.getValueAsDag("Apply");
- if (Applyer->getOperatorAsDef(RuleDef.getLoc())->getName() !=
- "apply") {
- PrintError(RuleDef.getLoc(), "Expected 'apply' operator in Apply DAG");
- return;
- }
-
- OS << Indent << " if (1\n";
-
- // Emit code for C++ Predicates.
- if (RuleDef.getValue("Predicates")) {
- ListInit *Preds = RuleDef.getValueAsListInit("Predicates");
- for (Init *I : Preds->getValues()) {
- if (DefInit *Pred = dyn_cast<DefInit>(I)) {
- Record *Def = Pred->getDef();
- if (!Def->isSubClassOf("Predicate")) {
- PrintError(Def->getLoc(), "Unknown 'Predicate' Type");
- return;
- }
-
- StringRef CondString = Def->getValueAsString("CondString");
- if (CondString.empty())
- continue;
-
- OS << Indent << " && (\n"
- << Indent << " // Predicate: " << Def->getName() << "\n"
- << Indent << " " << CondString << "\n"
- << Indent << " )\n";
- }
- }
- }
-
- // Attempt to emit code for any untested predicates left over. Note that
- // isFullyTested() will remain false even if we succeed here and therefore
- // combine rule elision will not be performed. This is because we do not
- // know if there's any connection between the predicates for each leaf and
- // therefore can't tell if one makes another unreachable. Ideally, the
- // partitioner(s) would be sufficiently complete to prevent us from having
- // untested predicates left over.
- for (const GIMatchDagPredicate *Predicate : Leaf.untested_predicates()) {
- if (Predicate->generateCheckCode(OS, (Indent + " ").str(),
- Expansions))
- continue;
- PrintError(RuleDef.getLoc(),
- "Unable to test predicate used in rule");
- PrintNote(SMLoc(),
- "This indicates an incomplete implementation in tablegen");
- Predicate->print(errs());
- errs() << "\n";
- OS << Indent
- << "llvm_unreachable(\"TableGen did not emit complete code for this "
- "path\");\n";
- break;
- }
-
- if (Rule->getMatchingFixupCode() &&
- !Rule->getMatchingFixupCode()->getValue().empty()) {
- // FIXME: Single-use lambda's like this are a serious compile-time
- // performance and memory issue. It's convenient for this early stage to
- // defer some work to successive patches but we need to eliminate this
- // before the ruleset grows to small-moderate size. Last time, it became
- // a big problem for low-mem systems around the 500 rule mark but by the
- // time we grow that large we should have merged the ISel match table
- // mechanism with the Combiner.
- OS << Indent << " && [&]() {\n"
- << Indent << " "
- << CodeExpander(Rule->getMatchingFixupCode()->getValue(), Expansions,
- RuleDef.getLoc(), ShowExpansions)
- << '\n'
- << Indent << " return true;\n"
- << Indent << " }()";
- }
- OS << Indent << " ) {\n" << Indent << " ";
-
- if (const StringInit *Code = dyn_cast<StringInit>(Applyer->getArg(0))) {
- OS << " LLVM_DEBUG(dbgs() << \"Applying rule '"
- << RuleDef.getName()
- << "'\\n\");\n"
- << CodeExpander(Code->getAsUnquotedString(), Expansions,
- RuleDef.getLoc(), ShowExpansions)
- << '\n'
- << Indent << " return true;\n"
- << Indent << " }\n";
- } else {
- PrintError(RuleDef.getLoc(), "Expected apply code block");
- return;
- }
-
- OS << Indent << "}\n";
-
- assert(Leaf.isFullyTraversed());
-
- // If we didn't have any predicates left over and we're not using the
- // trap-door we have to support arbitrary C++ code while we're migrating to
- // the declarative style then we know that subsequent leaves are
- // unreachable.
- if (Leaf.isFullyTested() &&
- (!Rule->getMatchingFixupCode() ||
- Rule->getMatchingFixupCode()->getValue().empty())) {
- AnyFullyTested = true;
- OS << Indent
- << "llvm_unreachable(\"Combine rule elision was incorrect\");\n"
- << Indent << "return false;\n";
- }
- }
- if (!AnyFullyTested)
- OS << Indent << "return false;\n";
-}
-
-static void emitAdditionalHelperMethodArguments(raw_ostream &OS,
- Record *Combiner) {
- for (Record *Arg : Combiner->getValueAsListOfDefs("AdditionalArguments"))
- OS << ",\n " << Arg->getValueAsString("Type")
- << " " << Arg->getValueAsString("Name");
-}
-
-void GICombinerEmitter::run(raw_ostream &OS) {
- Records.startTimer("Gather rules");
- gatherRules(Rules, Combiner->getValueAsListOfDefs("Rules"));
- if (StopAfterParse) {
- MatchDagCtx.print(errs());
- PrintNote(Combiner->getLoc(),
- "Terminating due to -gicombiner-stop-after-parse");
- return;
- }
- if (ErrorsPrinted)
- PrintFatalError(Combiner->getLoc(), "Failed to parse one or more rules");
- LLVM_DEBUG(dbgs() << "Optimizing tree for " << Rules.size() << " rules\n");
- std::unique_ptr<GIMatchTree> Tree;
- Records.startTimer("Optimize combiner");
- {
- GIMatchTreeBuilder TreeBuilder(0);
- for (const auto &Rule : Rules) {
- bool HadARoot = false;
- for (const auto &Root : enumerate(Rule->getMatchDag().roots())) {
- TreeBuilder.addLeaf(Rule->getName(), Root.index(), Rule->getMatchDag(),
- Rule.get());
- HadARoot = true;
- }
- if (!HadARoot)
- PrintFatalError(Rule->getDef().getLoc(), "All rules must have a root");
- }
-
- Tree = TreeBuilder.run();
- }
- if (StopAfterBuild) {
- Tree->writeDOTGraph(outs());
- PrintNote(Combiner->getLoc(),
- "Terminating due to -gicombiner-stop-after-build");
- return;
- }
-
- Records.startTimer("Emit combiner");
- OS << "#ifdef " << Name.upper() << "_GENCOMBINERHELPER_DEPS\n"
- << "#include \"llvm/ADT/SparseBitVector.h\"\n"
- << "namespace llvm {\n"
- << "extern cl::OptionCategory GICombinerOptionCategory;\n"
- << "} // end namespace llvm\n"
- << "#endif // ifdef " << Name.upper() << "_GENCOMBINERHELPER_DEPS\n\n";
-
- OS << "#ifdef " << Name.upper() << "_GENCOMBINERHELPER_H\n"
- << "class " << getClassName() << "RuleConfig {\n"
- << " SparseBitVector<> DisabledRules;\n"
- << "\n"
- << "public:\n"
- << " bool parseCommandLineOption();\n"
- << " bool isRuleDisabled(unsigned ID) const;\n"
- << " bool setRuleEnabled(StringRef RuleIdentifier);\n"
- << " bool setRuleDisabled(StringRef RuleIdentifier);\n"
- << "};\n"
- << "\n"
- << "class " << getClassName();
- StringRef StateClass = Combiner->getValueAsString("StateClass");
- if (!StateClass.empty())
- OS << " : public " << StateClass;
- OS << " {\n"
- << " const " << getClassName() << "RuleConfig *RuleConfig;\n"
- << "\n"
- << "public:\n"
- << " template <typename... Args>" << getClassName() << "(const "
- << getClassName() << "RuleConfig &RuleConfig, Args &&... args) : ";
- if (!StateClass.empty())
- OS << StateClass << "(std::forward<Args>(args)...), ";
- OS << "RuleConfig(&RuleConfig) {}\n"
- << "\n"
- << " bool tryCombineAll(\n"
- << " GISelChangeObserver &Observer,\n"
- << " MachineInstr &MI,\n"
- << " MachineIRBuilder &B";
- emitAdditionalHelperMethodArguments(OS, Combiner);
- OS << ") const;\n";
- OS << "};\n\n";
-
- emitNameMatcher(OS);
-
- OS << "static std::optional<std::pair<uint64_t, uint64_t>> "
- "getRuleRangeForIdentifier(StringRef RuleIdentifier) {\n"
- << " std::pair<StringRef, StringRef> RangePair = "
- "RuleIdentifier.split('-');\n"
- << " if (!RangePair.second.empty()) {\n"
- << " const auto First = "
- "getRuleIdxForIdentifier(RangePair.first);\n"
- << " const auto Last = "
- "getRuleIdxForIdentifier(RangePair.second);\n"
- << " if (!First || !Last)\n"
- << " return std::nullopt;\n"
- << " if (First >= Last)\n"
- << " report_fatal_error(\"Beginning of range should be before "
- "end of range\");\n"
- << " return {{*First, *Last + 1}};\n"
- << " }\n"
- << " if (RangePair.first == \"*\") {\n"
- << " return {{0, " << Rules.size() << "}};\n"
- << " }\n"
- << " const auto I = getRuleIdxForIdentifier(RangePair.first);\n"
- << " if (!I)\n"
- << " return std::nullopt;\n"
- << " return {{*I, *I + 1}};\n"
- << "}\n\n";
-
- for (bool Enabled : {true, false}) {
- OS << "bool " << getClassName() << "RuleConfig::setRule"
- << (Enabled ? "Enabled" : "Disabled") << "(StringRef RuleIdentifier) {\n"
- << " auto MaybeRange = getRuleRangeForIdentifier(RuleIdentifier);\n"
- << " if (!MaybeRange)\n"
- << " return false;\n"
- << " for (auto I = MaybeRange->first; I < MaybeRange->second; ++I)\n"
- << " DisabledRules." << (Enabled ? "reset" : "set") << "(I);\n"
- << " return true;\n"
- << "}\n\n";
- }
-
- OS << "bool " << getClassName()
- << "RuleConfig::isRuleDisabled(unsigned RuleID) const {\n"
- << " return DisabledRules.test(RuleID);\n"
- << "}\n";
- OS << "#endif // ifdef " << Name.upper() << "_GENCOMBINERHELPER_H\n\n";
-
- OS << "#ifdef " << Name.upper() << "_GENCOMBINERHELPER_CPP\n"
- << "\n"
- << "std::vector<std::string> " << Name << "Option;\n"
- << "cl::list<std::string> " << Name << "DisableOption(\n"
- << " \"" << Name.lower() << "-disable-rule\",\n"
- << " cl::desc(\"Disable one or more combiner rules temporarily in "
- << "the " << Name << " pass\"),\n"
- << " cl::CommaSeparated,\n"
- << " cl::Hidden,\n"
- << " cl::cat(GICombinerOptionCategory),\n"
- << " cl::callback([](const std::string &Str) {\n"
- << " " << Name << "Option.push_back(Str);\n"
- << " }));\n"
- << "cl::list<std::string> " << Name << "OnlyEnableOption(\n"
- << " \"" << Name.lower() << "-only-enable-rule\",\n"
- << " cl::desc(\"Disable all rules in the " << Name
- << " pass then re-enable the specified ones\"),\n"
- << " cl::Hidden,\n"
- << " cl::cat(GICombinerOptionCategory),\n"
- << " cl::callback([](const std::string &CommaSeparatedArg) {\n"
- << " StringRef Str = CommaSeparatedArg;\n"
- << " " << Name << "Option.push_back(\"*\");\n"
- << " do {\n"
- << " auto X = Str.split(\",\");\n"
- << " " << Name << "Option.push_back((\"!\" + X.first).str());\n"
- << " Str = X.second;\n"
- << " } while (!Str.empty());\n"
- << " }));\n"
- << "\n"
- << "bool " << getClassName() << "RuleConfig::parseCommandLineOption() {\n"
- << " for (StringRef Identifier : " << Name << "Option) {\n"
- << " bool Enabled = Identifier.consume_front(\"!\");\n"
- << " if (Enabled && !setRuleEnabled(Identifier))\n"
- << " return false;\n"
- << " if (!Enabled && !setRuleDisabled(Identifier))\n"
- << " return false;\n"
- << " }\n"
- << " return true;\n"
- << "}\n\n";
-
- OS << "bool " << getClassName() << "::tryCombineAll(\n"
- << " GISelChangeObserver &Observer,\n"
- << " MachineInstr &MI,\n"
- << " MachineIRBuilder &B";
- emitAdditionalHelperMethodArguments(OS, Combiner);
- OS << ") const {\n"
- << " MachineBasicBlock *MBB = MI.getParent();\n"
- << " MachineFunction *MF = MBB->getParent();\n"
- << " MachineRegisterInfo &MRI = MF->getRegInfo();\n"
- << " SmallVector<MachineInstr *, 8> MIs = {&MI};\n\n"
- << " (void)MBB; (void)MF; (void)MRI; (void)RuleConfig;\n\n";
-
- OS << " // Match data\n";
- for (const auto &Rule : Rules)
- for (const auto &I : Rule->matchdata_decls())
- OS << " " << I.getType() << " " << I.getVariableName() << ";\n";
- OS << "\n";
-
- OS << " int Partition = -1;\n";
- generateCodeForTree(OS, *Tree, " ");
- OS << "\n return false;\n"
- << "}\n"
- << "#endif // ifdef " << Name.upper() << "_GENCOMBINERHELPER_CPP\n";
-}
-
-} // end anonymous namespace
-
-//===----------------------------------------------------------------------===//
-
-static void EmitGICombiner(RecordKeeper &RK, raw_ostream &OS) {
- PrintWarning(
- "'-gen-global-isel-combiner' is deprecated and will be removed soon; "
- "please use '-gen-global-isel-combiner-match-table' instead");
- PrintNote(
- "See "
- "https://discourse.llvm.org/t/rfc-matchtable-based-globalisel-combiners");
-
- CodeGenTarget Target(RK);
- emitSourceFileHeader("Global Combiner", OS);
-
- if (SelectedCombiners.empty())
- PrintFatalError("No combiners selected with -combiners");
- for (const auto &Combiner : SelectedCombiners) {
- Record *CombinerDef = RK.getDef(Combiner);
- if (!CombinerDef)
- PrintFatalError("Could not find " + Combiner);
- GICombinerEmitter(RK, Target, Combiner, CombinerDef).run(OS);
- }
- NumPatternTotalStatistic = NumPatternTotal;
-}
-
-static TableGen::Emitter::Opt X("gen-global-isel-combiner", EmitGICombiner,
- "Generate GlobalISel combiner");
diff --git a/llvm/utils/TableGen/GlobalISel/CXXPredicates.cpp b/llvm/utils/TableGen/GlobalISel/CXXPredicates.cpp
new file mode 100644
index 000000000000..e39293ebfe7a
--- /dev/null
+++ b/llvm/utils/TableGen/GlobalISel/CXXPredicates.cpp
@@ -0,0 +1,51 @@
+//===- CXXPredicates.cpp ----------------------------------------*- C++ -*-===//
+//
+// 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 "CXXPredicates.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace llvm {
+namespace gi {
+
+std::vector<const CXXPredicateCode *>
+CXXPredicateCode::getSorted(const CXXPredicateCodePool &Pool) {
+ std::vector<const CXXPredicateCode *> Out;
+ std::transform(Pool.begin(), Pool.end(), std::back_inserter(Out),
+ [&](auto &Elt) { return Elt.second.get(); });
+ sort(Out, [](const auto *A, const auto *B) { return A->ID < B->ID; });
+ return Out;
+}
+
+const CXXPredicateCode &CXXPredicateCode::get(CXXPredicateCodePool &Pool,
+ std::string Code) {
+ // Check if we already have an identical piece of code, if not, create an
+ // entry in the pool.
+ const auto CodeHash = hash_value(Code);
+ if (auto It = Pool.find(CodeHash); It != Pool.end())
+ return *It->second;
+
+ const auto ID = Pool.size();
+ auto OwnedData = std::unique_ptr<CXXPredicateCode>(
+ new CXXPredicateCode(std::move(Code), ID));
+ const auto &DataRef = *OwnedData;
+ Pool[CodeHash] = std::move(OwnedData);
+ return DataRef;
+}
+
+// TODO: Make BaseEnumName prefix configurable.
+CXXPredicateCode::CXXPredicateCode(std::string Code, unsigned ID)
+ : Code(Code), ID(ID), BaseEnumName("GICombiner" + std::to_string(ID)) {}
+
+CXXPredicateCode::CXXPredicateCodePool CXXPredicateCode::AllCXXMatchCode;
+CXXPredicateCode::CXXPredicateCodePool CXXPredicateCode::AllCXXApplyCode;
+
+} // namespace gi
+} // namespace llvm
diff --git a/llvm/utils/TableGen/GlobalISel/CXXPredicates.h b/llvm/utils/TableGen/GlobalISel/CXXPredicates.h
new file mode 100644
index 000000000000..01610a13110d
--- /dev/null
+++ b/llvm/utils/TableGen/GlobalISel/CXXPredicates.h
@@ -0,0 +1,86 @@
+//===- CXXPredicates.h ------------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file Contains utilities related to handling C++ code in MIR patterns for
+/// GlobalISel. C++ predicates need to be expanded, and then stored in a
+/// static pool until they can be emitted.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_UTILS_MIRPATTERNS_CXXPREDICATES_H
+#define LLVM_UTILS_MIRPATTERNS_CXXPREDICATES_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace llvm {
+namespace gi {
+
+/// Entry into the static pool of all CXX Predicate code. This contains
+/// fully expanded C++ code.
+///
+/// The static pool is hidden inside the object and can be accessed through
+/// getAllMatchCode/getAllApplyCode
+///
+/// Note that CXXPattern trims C++ code, so the Code is already expected to be
+/// free of leading/trailing whitespace.
+class CXXPredicateCode {
+ using CXXPredicateCodePool =
+ DenseMap<hash_code, std::unique_ptr<CXXPredicateCode>>;
+ static CXXPredicateCodePool AllCXXMatchCode;
+ static CXXPredicateCodePool AllCXXApplyCode;
+
+ /// Sorts a `CXXPredicateCodePool` by their IDs and returns it.
+ static std::vector<const CXXPredicateCode *>
+ getSorted(const CXXPredicateCodePool &Pool);
+
+ /// Gets an instance of `CXXPredicateCode` for \p Code, or returns an already
+ /// existing one.
+ static const CXXPredicateCode &get(CXXPredicateCodePool &Pool,
+ std::string Code);
+
+ CXXPredicateCode(std::string Code, unsigned ID);
+
+public:
+ static const CXXPredicateCode &getMatchCode(std::string Code) {
+ return get(AllCXXMatchCode, std::move(Code));
+ }
+
+ static const CXXPredicateCode &getApplyCode(std::string Code) {
+ return get(AllCXXApplyCode, std::move(Code));
+ }
+
+ static std::vector<const CXXPredicateCode *> getAllMatchCode() {
+ return getSorted(AllCXXMatchCode);
+ }
+
+ static std::vector<const CXXPredicateCode *> getAllApplyCode() {
+ return getSorted(AllCXXApplyCode);
+ }
+
+ const std::string Code;
+ const unsigned ID;
+ const std::string BaseEnumName;
+
+ bool needsUnreachable() const {
+ return !StringRef(Code).starts_with("return");
+ }
+
+ std::string getEnumNameWithPrefix(StringRef Prefix) const {
+ return Prefix.str() + BaseEnumName;
+ }
+};
+
+} // namespace gi
+} // end namespace llvm
+
+#endif // ifndef LLVM_UTILS_MIRPATTERNS_CXXPREDICATES_H
diff --git a/llvm/utils/TableGen/GlobalISel/CodeExpander.cpp b/llvm/utils/TableGen/GlobalISel/CodeExpander.cpp
index 42b4aabf2755..20f98bef4887 100644
--- a/llvm/utils/TableGen/GlobalISel/CodeExpander.cpp
+++ b/llvm/utils/TableGen/GlobalISel/CodeExpander.cpp
@@ -31,24 +31,24 @@ void CodeExpander::emit(raw_ostream &OS) const {
OS << Current.substr(0, Pos);
Current = Current.substr(Pos);
- if (Current.startswith("\n")) {
+ if (Current.starts_with("\n")) {
OS << "\n" << Indent;
Current = Current.drop_front(1);
continue;
}
- if (Current.startswith("\\$") || Current.startswith("\\\\")) {
+ if (Current.starts_with("\\$") || Current.starts_with("\\\\")) {
OS << Current[1];
Current = Current.drop_front(2);
continue;
}
- if (Current.startswith("\\")) {
+ if (Current.starts_with("\\")) {
Current = Current.drop_front(1);
continue;
}
- if (Current.startswith("${")) {
+ if (Current.starts_with("${")) {
StringRef StartVar = Current;
Current = Current.drop_front(2);
StringRef Var;
diff --git a/llvm/utils/TableGen/GlobalISel/CodeExpansions.h b/llvm/utils/TableGen/GlobalISel/CodeExpansions.h
index f536e801b27f..b82c3257b321 100644
--- a/llvm/utils/TableGen/GlobalISel/CodeExpansions.h
+++ b/llvm/utils/TableGen/GlobalISel/CodeExpansions.h
@@ -29,6 +29,10 @@ public:
Expansions.try_emplace(Name, Expansion);
}
+ void redeclare(StringRef Name, StringRef Expansion) {
+ Expansions[Name] = Expansion;
+ }
+
std::string lookup(StringRef Variable) const {
return Expansions.lookup(Variable);
}
diff --git a/llvm/utils/TableGen/GlobalISel/CombinerUtils.h b/llvm/utils/TableGen/GlobalISel/CombinerUtils.h
index 394c43e3fa83..8cb2514a10e8 100644
--- a/llvm/utils/TableGen/GlobalISel/CombinerUtils.h
+++ b/llvm/utils/TableGen/GlobalISel/CombinerUtils.h
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
//
/// \file Utility functions used by both Combiner backends.
-/// TODO: Can remove when MatchDAG-based backend is removed.
//
//===----------------------------------------------------------------------===//
@@ -31,7 +30,7 @@ inline bool isSpecificDef(const Init &N, StringRef Def) {
/// A convenience function to check that an Init refers to a def that is a
/// subclass of the given class and coerce it to a def if it is. This is
-/// primarily useful for testing for subclasses of GIMatchKind and similar in
+/// primarily useful for testing for subclasses of GIDefKind and similar in
/// DagInit's since DagInit's support any type inside them.
inline Record *getDefOfSubClass(const Init &N, StringRef Cls) {
if (const DefInit *OpI = dyn_cast<DefInit>(&N))
@@ -42,7 +41,7 @@ inline Record *getDefOfSubClass(const Init &N, StringRef Cls) {
/// A convenience function to check that an Init refers to a dag whose operator
/// is a specific def and coerce it to a dag if it is. This is primarily useful
-/// for testing for subclasses of GIMatchKind and similar in DagInit's since
+/// for testing for subclasses of GIDefKind and similar in DagInit's since
/// DagInit's support any type inside them.
inline const DagInit *getDagWithSpecificOperator(const Init &N,
StringRef Name) {
@@ -56,15 +55,14 @@ inline const DagInit *getDagWithSpecificOperator(const Init &N,
/// A convenience function to check that an Init refers to a dag whose operator
/// is a def that is a subclass of the given class and coerce it to a dag if it
-/// is. This is primarily useful for testing for subclasses of GIMatchKind and
+/// is. This is primarily useful for testing for subclasses of GIDefKind and
/// similar in DagInit's since DagInit's support any type inside them.
inline const DagInit *getDagWithOperatorOfSubClass(const Init &N,
StringRef Cls) {
if (const DagInit *I = dyn_cast<DagInit>(&N))
- if (I->getNumArgs() > 0)
- if (const DefInit *OpI = dyn_cast<DefInit>(I->getOperator()))
- if (OpI->getDef()->isSubClassOf(Cls))
- return I;
+ if (const DefInit *OpI = dyn_cast<DefInit>(I->getOperator()))
+ if (OpI->getDef()->isSubClassOf(Cls))
+ return I;
return nullptr;
}
} // namespace llvm
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDag.cpp b/llvm/utils/TableGen/GlobalISel/GIMatchDag.cpp
deleted file mode 100644
index 8be32d2effa6..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchDag.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-//===- GIMatchDag.cpp - A DAG representation of a pattern to be matched ---===//
-//
-// 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 "GIMatchDag.h"
-
-#include "llvm/Support/Format.h"
-#include "llvm/TableGen/Record.h"
-#include "../CodeGenInstruction.h"
-
-using namespace llvm;
-
-void GIMatchDag::writeDOTGraph(raw_ostream &OS, StringRef ID) const {
- const auto writePorts = [&](StringRef Prefix,
- const GIMatchDagOperandList &Operands) {
- StringRef Separator = "";
- OS << "{";
- for (const auto &Op : enumerate(Operands)) {
- OS << Separator << "<" << Prefix << format("%d", Op.index()) << ">"
- << "#" << Op.index() << " $" << Op.value().getName();
- Separator = "|";
- }
- OS << "}";
- };
-
- OS << "digraph \"" << ID << "\" {\n"
- << " rankdir=\"BT\"\n";
- for (const auto &N : InstrNodes) {
- OS << " " << format("Node%p", &*N) << " [shape=record,label=\"{";
- writePorts("s", N->getOperandInfo());
- OS << "|" << N->getName();
- if (N->getOpcodeAnnotation())
- OS << "|" << N->getOpcodeAnnotation()->TheDef->getName();
- if (N->isMatchRoot())
- OS << "|Match starts here";
- OS << "|";
- SmallVector<std::pair<unsigned, StringRef>, 8> ToPrint;
- for (const auto &Assignment : N->user_assigned_operand_names())
- ToPrint.emplace_back(Assignment.first, Assignment.second);
- llvm::sort(ToPrint);
- StringRef Separator = "";
- for (const auto &Assignment : ToPrint) {
- OS << Separator << "$" << Assignment.second << "=getOperand("
- << Assignment.first << ")";
- Separator = ", ";
- }
- OS << llvm::format("|%p|", &N);
- writePorts("d", N->getOperandInfo());
- OS << "}\"";
- if (N->isMatchRoot())
- OS << ",color=red";
- OS << "]\n";
- }
-
- for (const auto &E : Edges) {
- const char *FromFmt = "Node%p:s%d:n";
- const char *ToFmt = "Node%p:d%d:s";
- if (E->getFromMO()->isDef() && !E->getToMO()->isDef())
- std::swap(FromFmt, ToFmt);
- auto From = format(FromFmt, E->getFromMI(), E->getFromMO()->getIdx());
- auto To = format(ToFmt, E->getToMI(), E->getToMO()->getIdx());
- if (E->getFromMO()->isDef() && !E->getToMO()->isDef())
- std::swap(From, To);
-
- OS << " " << From << " -> " << To << " [label=\"$" << E->getName();
- if (E->getFromMO()->isDef() == E->getToMO()->isDef())
- OS << " INVALID EDGE!";
- OS << "\"";
- if (E->getFromMO()->isDef() == E->getToMO()->isDef())
- OS << ",color=red";
- else if (E->getFromMO()->isDef() && !E->getToMO()->isDef())
- OS << ",dir=back,arrowtail=crow";
- OS << "]\n";
- }
-
- for (const auto &N : PredicateNodes) {
- OS << " " << format("Pred%p", &*N) << " [shape=record,label=\"{";
- writePorts("s", N->getOperandInfo());
- OS << "|" << N->getName() << "|";
- N->printDescription(OS);
- OS << llvm::format("|%p|", &N);
- writePorts("d", N->getOperandInfo());
- OS << "}\",style=dotted]\n";
- }
-
- for (const auto &E : PredicateDependencies) {
- const char *FromMIFmt = "Node%p:e";
- const char *FromMOFmt = "Node%p:s%d:n";
- const char *ToFmt = "Pred%p:d%d:s";
- auto To = format(ToFmt, E->getPredicate(), E->getPredicateOp()->getIdx());
- auto Style = "[style=dotted]";
- if (E->getRequiredMO()) {
- auto From =
- format(FromMOFmt, E->getRequiredMI(), E->getRequiredMO()->getIdx());
- OS << " " << From << " -> " << To << " " << Style << "\n";
- continue;
- }
- auto From = format(FromMIFmt, E->getRequiredMI());
- OS << " " << From << " -> " << To << " " << Style << "\n";
- }
-
- OS << "}\n";
-}
-
-LLVM_DUMP_METHOD void GIMatchDag::print(raw_ostream &OS) const {
- OS << "matchdag {\n";
- for (const auto &N : InstrNodes) {
- OS << " ";
- N->print(OS);
- OS << "\n";
- }
- for (const auto &E : Edges) {
- OS << " ";
- E->print(OS);
- OS << "\n";
- }
-
- for (const auto &P : PredicateNodes) {
- OS << " ";
- P->print(OS);
- OS << "\n";
- }
- for (const auto &D : PredicateDependencies) {
- OS << " ";
- D->print(OS);
- OS << "\n";
- }
- OS << "}\n";
-}
-
-raw_ostream &llvm::operator<<(raw_ostream &OS, const GIMatchDag &G) {
- G.print(OS);
- return OS;
-}
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDag.h b/llvm/utils/TableGen/GlobalISel/GIMatchDag.h
deleted file mode 100644
index c566dd73f709..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchDag.h
+++ /dev/null
@@ -1,240 +0,0 @@
-//===- GIMatchDag.h - Represent a DAG to be matched -------------*- C++ -*-===//
-//
-// 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_GIMATCHDAG_H
-#define LLVM_UTILS_TABLEGEN_GIMATCHDAG_H
-
-#include "GIMatchDagEdge.h"
-#include "GIMatchDagInstr.h"
-#include "GIMatchDagOperands.h"
-#include "GIMatchDagPredicate.h"
-#include "GIMatchDagPredicateDependencyEdge.h"
-
-namespace llvm {
-
-/// This class manages lifetimes for data associated with the GIMatchDag object.
-class GIMatchDagContext {
- GIMatchDagOperandListContext OperandListCtx;
-
-public:
- const GIMatchDagOperandList &makeEmptyOperandList() {
- return OperandListCtx.makeEmptyOperandList();
- }
-
- const GIMatchDagOperandList &makeOperandList(const CodeGenInstruction &I) {
- return OperandListCtx.makeOperandList(I);
- }
-
- const GIMatchDagOperandList &makeMIPredicateOperandList() {
- return OperandListCtx.makeMIPredicateOperandList();
- }
-
-
- const GIMatchDagOperandList &makeTwoMOPredicateOperandList() {
- return OperandListCtx.makeTwoMOPredicateOperandList();
- }
-
- void print(raw_ostream &OS) const {
- OperandListCtx.print(OS);
- }
-
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- LLVM_DUMP_METHOD void dump() const { print(errs()); }
-#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-};
-
-class GIMatchDag {
-public:
- using InstrNodesVec = std::vector<std::unique_ptr<GIMatchDagInstr>>;
- using instr_node_iterator = raw_pointer_iterator<InstrNodesVec::iterator>;
- using const_instr_node_iterator =
- raw_pointer_iterator<InstrNodesVec::const_iterator>;
-
- using EdgesVec = std::vector<std::unique_ptr<GIMatchDagEdge>>;
- using edge_iterator = raw_pointer_iterator<EdgesVec::iterator>;
- using const_edge_iterator = raw_pointer_iterator<EdgesVec::const_iterator>;
-
- using PredicateNodesVec = std::vector<std::unique_ptr<GIMatchDagPredicate>>;
- using predicate_iterator = raw_pointer_iterator<PredicateNodesVec::iterator>;
- using const_predicate_iterator =
- raw_pointer_iterator<PredicateNodesVec::const_iterator>;
-
- using PredicateDependencyEdgesVec =
- std::vector<std::unique_ptr<GIMatchDagPredicateDependencyEdge>>;
- using predicate_edge_iterator =
- raw_pointer_iterator<PredicateDependencyEdgesVec::iterator>;
- using const_predicate_edge_iterator =
- raw_pointer_iterator<PredicateDependencyEdgesVec::const_iterator>;
-
-protected:
- GIMatchDagContext &Ctx;
- InstrNodesVec InstrNodes;
- PredicateNodesVec PredicateNodes;
- EdgesVec Edges;
- PredicateDependencyEdgesVec PredicateDependencies;
- std::vector<GIMatchDagInstr *> MatchRoots;
- // FIXME: This is a temporary measure while we still accept arbitrary code
- // blocks to fix up the matcher while it's being developed.
- bool HasPostMatchPredicate = false;
-
-public:
- GIMatchDag(GIMatchDagContext &Ctx) : Ctx(Ctx) {}
- GIMatchDag(const GIMatchDag &) = delete;
-
- GIMatchDagContext &getContext() const { return Ctx; }
- edge_iterator edges_begin() {
- return raw_pointer_iterator<EdgesVec::iterator>(Edges.begin());
- }
- edge_iterator edges_end() {
- return raw_pointer_iterator<EdgesVec::iterator>(Edges.end());
- }
- const_edge_iterator edges_begin() const {
- return raw_pointer_iterator<EdgesVec::const_iterator>(Edges.begin());
- }
- const_edge_iterator edges_end() const {
- return raw_pointer_iterator<EdgesVec::const_iterator>(Edges.end());
- }
- iterator_range<edge_iterator> edges() {
- return make_range(edges_begin(), edges_end());
- }
- iterator_range<const_edge_iterator> edges() const {
- return make_range(edges_begin(), edges_end());
- }
- iterator_range<std::vector<GIMatchDagInstr *>::iterator> roots() {
- return make_range(MatchRoots.begin(), MatchRoots.end());
- }
- iterator_range<std::vector<GIMatchDagInstr *>::const_iterator> roots() const {
- return make_range(MatchRoots.begin(), MatchRoots.end());
- }
-
- instr_node_iterator instr_nodes_begin() {
- return raw_pointer_iterator<InstrNodesVec::iterator>(InstrNodes.begin());
- }
- instr_node_iterator instr_nodes_end() {
- return raw_pointer_iterator<InstrNodesVec::iterator>(InstrNodes.end());
- }
- const_instr_node_iterator instr_nodes_begin() const {
- return raw_pointer_iterator<InstrNodesVec::const_iterator>(
- InstrNodes.begin());
- }
- const_instr_node_iterator instr_nodes_end() const {
- return raw_pointer_iterator<InstrNodesVec::const_iterator>(
- InstrNodes.end());
- }
- iterator_range<instr_node_iterator> instr_nodes() {
- return make_range(instr_nodes_begin(), instr_nodes_end());
- }
- iterator_range<const_instr_node_iterator> instr_nodes() const {
- return make_range(instr_nodes_begin(), instr_nodes_end());
- }
- predicate_edge_iterator predicate_edges_begin() {
- return raw_pointer_iterator<PredicateDependencyEdgesVec::iterator>(
- PredicateDependencies.begin());
- }
- predicate_edge_iterator predicate_edges_end() {
- return raw_pointer_iterator<PredicateDependencyEdgesVec::iterator>(
- PredicateDependencies.end());
- }
- const_predicate_edge_iterator predicate_edges_begin() const {
- return raw_pointer_iterator<PredicateDependencyEdgesVec::const_iterator>(
- PredicateDependencies.begin());
- }
- const_predicate_edge_iterator predicate_edges_end() const {
- return raw_pointer_iterator<PredicateDependencyEdgesVec::const_iterator>(
- PredicateDependencies.end());
- }
- iterator_range<predicate_edge_iterator> predicate_edges() {
- return make_range(predicate_edges_begin(), predicate_edges_end());
- }
- iterator_range<const_predicate_edge_iterator> predicate_edges() const {
- return make_range(predicate_edges_begin(), predicate_edges_end());
- }
- predicate_iterator predicates_begin() {
- return raw_pointer_iterator<PredicateNodesVec::iterator>(
- PredicateNodes.begin());
- }
- predicate_iterator predicates_end() {
- return raw_pointer_iterator<PredicateNodesVec::iterator>(
- PredicateNodes.end());
- }
- const_predicate_iterator predicates_begin() const {
- return raw_pointer_iterator<PredicateNodesVec::const_iterator>(
- PredicateNodes.begin());
- }
- const_predicate_iterator predicates_end() const {
- return raw_pointer_iterator<PredicateNodesVec::const_iterator>(
- PredicateNodes.end());
- }
- iterator_range<predicate_iterator> predicates() {
- return make_range(predicates_begin(), predicates_end());
- }
- iterator_range<const_predicate_iterator> predicates() const {
- return make_range(predicates_begin(), predicates_end());
- }
-
- template <class... Args> GIMatchDagInstr *addInstrNode(Args &&... args) {
- auto Obj =
- std::make_unique<GIMatchDagInstr>(*this, std::forward<Args>(args)...);
- auto ObjRaw = Obj.get();
- InstrNodes.push_back(std::move(Obj));
- return ObjRaw;
- }
-
- template <class T, class... Args>
- T *addPredicateNode(Args &&... args) {
- auto Obj = std::make_unique<T>(getContext(), std::forward<Args>(args)...);
- auto ObjRaw = Obj.get();
- PredicateNodes.push_back(std::move(Obj));
- return ObjRaw;
- }
-
- template <class... Args> GIMatchDagEdge *addEdge(Args &&... args) {
- auto Obj = std::make_unique<GIMatchDagEdge>(std::forward<Args>(args)...);
- auto ObjRaw = Obj.get();
- Edges.push_back(std::move(Obj));
- return ObjRaw;
- }
-
- template <class... Args>
- GIMatchDagPredicateDependencyEdge *addPredicateDependency(Args &&... args) {
- auto Obj = std::make_unique<GIMatchDagPredicateDependencyEdge>(
- std::forward<Args>(args)...);
- auto ObjRaw = Obj.get();
- PredicateDependencies.push_back(std::move(Obj));
- return ObjRaw;
- }
-
- size_t getInstrNodeIdx(instr_node_iterator I) {
- return std::distance(instr_nodes_begin(), I);
- }
- size_t getInstrNodeIdx(const_instr_node_iterator I) const {
- return std::distance(instr_nodes_begin(), I);
- }
- size_t getNumInstrNodes() const { return InstrNodes.size(); }
- size_t getNumEdges() const { return Edges.size(); }
- size_t getNumPredicates() const { return PredicateNodes.size(); }
-
- void setHasPostMatchPredicate(bool V) { HasPostMatchPredicate = V; }
- bool hasPostMatchPredicate() const { return HasPostMatchPredicate; }
-
- void addMatchRoot(GIMatchDagInstr *N) { MatchRoots.push_back(N); }
-
- LLVM_DUMP_METHOD void print(raw_ostream &OS) const;
-
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- LLVM_DUMP_METHOD void dump() const { print(errs()); }
-#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-
- void writeDOTGraph(raw_ostream &OS, StringRef ID) const;
-};
-
-raw_ostream &operator<<(raw_ostream &OS, const GIMatchDag &G);
-
-} // end namespace llvm
-
-#endif // ifndef LLVM_UTILS_TABLEGEN_GIMATCHDAG_H
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagEdge.cpp b/llvm/utils/TableGen/GlobalISel/GIMatchDagEdge.cpp
deleted file mode 100644
index 796479467df7..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchDagEdge.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===- GIMatchDagEdge.cpp - An edge describing a def/use lookup -----------===//
-//
-// 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 "GIMatchDagEdge.h"
-#include "GIMatchDagInstr.h"
-#include "GIMatchDagOperands.h"
-#include "llvm/Support/raw_ostream.h"
-
-using namespace llvm;
-
-LLVM_DUMP_METHOD void GIMatchDagEdge::print(raw_ostream &OS) const {
- OS << getFromMI()->getName() << "[" << getFromMO()->getName() << "] --["
- << Name << "]--> " << getToMI()->getName() << "[" << getToMO()->getName()
- << "]";
-}
-
-bool GIMatchDagEdge::isDefToUse() const {
- // Def -> Def is invalid so we only need to check FromMO.
- return FromMO->isDef();
-}
-
-void GIMatchDagEdge::reverse() {
- std::swap(FromMI, ToMI);
- std::swap(FromMO, ToMO);
-}
-
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-LLVM_DUMP_METHOD void GIMatchDagEdge::dump() const { print(errs()); }
-#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-
-raw_ostream &llvm::operator<<(raw_ostream &OS, const GIMatchDagEdge &E) {
- E.print(OS);
- return OS;
-}
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagEdge.h b/llvm/utils/TableGen/GlobalISel/GIMatchDagEdge.h
deleted file mode 100644
index e76ef1b4a3aa..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchDagEdge.h
+++ /dev/null
@@ -1,70 +0,0 @@
-//===- GIMatchDagEdge.h - Represent node shared operand lists ---*- C++ -*-===//
-//
-// 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_GIMATCHDAGEDGE_H
-#define LLVM_UTILS_TABLEGEN_GIMATCHDAGEDGE_H
-
-#include "llvm/ADT/StringRef.h"
-
-namespace llvm {
-class raw_ostream;
-class GIMatchDagInstr;
-class GIMatchDagOperand;
-
-/// Represents an edge that connects two instructions together via a pair of
-/// operands. For example:
-/// %a = FOO ...
-/// %0 = BAR %a
-/// %1 = BAZ %a
-/// would have two edges for %a like so:
-/// BAR:Op#1 --[a]----> Op#0:FOO
-/// ^
-/// BAZ:Op#1 --[a]------/
-/// Ideally, all edges in the DAG are from a use to a def as this is a many
-/// to one edge but edges from defs to uses are supported too.
-class GIMatchDagEdge {
- /// The name of the edge. For example,
- /// (FOO $a, $b, $c)
- /// (BAR $d, $e, $a)
- /// will create an edge named 'a' to connect FOO to BAR. Although the name
- /// refers to the edge, the canonical value of 'a' is the operand that defines
- /// it.
- StringRef Name;
- const GIMatchDagInstr *FromMI;
- const GIMatchDagOperand *FromMO;
- const GIMatchDagInstr *ToMI;
- const GIMatchDagOperand *ToMO;
-
-public:
- GIMatchDagEdge(StringRef Name, const GIMatchDagInstr *FromMI, const GIMatchDagOperand *FromMO,
- const GIMatchDagInstr *ToMI, const GIMatchDagOperand *ToMO)
- : Name(Name), FromMI(FromMI), FromMO(FromMO), ToMI(ToMI), ToMO(ToMO) {}
-
- StringRef getName() const { return Name; }
- const GIMatchDagInstr *getFromMI() const { return FromMI; }
- const GIMatchDagOperand *getFromMO() const { return FromMO; }
- const GIMatchDagInstr *getToMI() const { return ToMI; }
- const GIMatchDagOperand *getToMO() const { return ToMO; }
-
- /// Flip the direction of the edge.
- void reverse();
-
- /// Does this edge run from a def to (one of many) uses?
- bool isDefToUse() const;
-
- LLVM_DUMP_METHOD void print(raw_ostream &OS) const;
-
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- LLVM_DUMP_METHOD void dump() const;
-#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-};
-
-raw_ostream &operator<<(raw_ostream &OS, const GIMatchDagEdge &E);
-
-} // end namespace llvm
-#endif // ifndef LLVM_UTILS_TABLEGEN_GIMATCHDAGEDGE_H
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagInstr.cpp b/llvm/utils/TableGen/GlobalISel/GIMatchDagInstr.cpp
deleted file mode 100644
index ad9fbea8f881..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchDagInstr.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-//===- GIMatchDagInstr.cpp - A shared operand list for nodes --------------===//
-//
-// 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 "GIMatchDagInstr.h"
-#include "../CodeGenInstruction.h"
-#include "GIMatchDag.h"
-#include "llvm/TableGen/Record.h"
-
-using namespace llvm;
-
-void GIMatchDagInstr::print(raw_ostream &OS) const {
- OS << "(";
- if (const auto *Annotation = getOpcodeAnnotation())
- OS << Annotation->TheDef->getName();
- else
- OS << "<unknown>";
- OS << " ";
- OperandInfo.print(OS);
- OS << "):$" << Name;
- if (!UserAssignedNamesForOperands.empty()) {
- OS << " // ";
- SmallVector<std::pair<unsigned, StringRef>, 8> ToPrint;
- for (const auto &Assignment : UserAssignedNamesForOperands)
- ToPrint.emplace_back(Assignment.first, Assignment.second);
- llvm::sort(ToPrint);
- StringRef Separator = "";
- for (const auto &Assignment : ToPrint) {
- OS << Separator << "$" << Assignment.second << "=getOperand("
- << Assignment.first << ")";
- Separator = ", ";
- }
- }
-}
-
-void GIMatchDagInstr::setMatchRoot() {
- IsMatchRoot = true;
- Dag.addMatchRoot(this);
-}
-
-raw_ostream &llvm::operator<<(raw_ostream &OS, const GIMatchDagInstr &N) {
- N.print(OS);
- return OS;
-}
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagInstr.h b/llvm/utils/TableGen/GlobalISel/GIMatchDagInstr.h
deleted file mode 100644
index d2c746dda9e9..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchDagInstr.h
+++ /dev/null
@@ -1,118 +0,0 @@
-//===- GIMatchDagInstr.h - Represent instruction to be matched --*- C++ -*-===//
-//
-// 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_GIMATCHDAGINSTR_H
-#define LLVM_UTILS_TABLEGEN_GIMATCHDAGINSTR_H
-
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/raw_ostream.h"
-
-namespace llvm {
-class CodeGenInstruction;
-class GIMatchDag;
-class GIMatchDagOperandList;
-
-/// Represents an instruction in the match DAG. This object knows very little
-/// about the actual instruction to be matched as the bulk of that is in
-/// predicates that are associated with the match DAG. It merely knows the names
-/// and indices of any operands that need to be matched in order to allow edges
-/// to link to them.
-///
-/// Instances of this class objects are owned by the GIMatchDag and are not
-/// shareable between instances of GIMatchDag. This is because the Name,
-/// IsMatchRoot, and OpcodeAnnotation are likely to differ between GIMatchDag
-/// instances.
-class GIMatchDagInstr {
-public:
- using const_user_assigned_operand_names_iterator =
- DenseMap<unsigned, StringRef>::const_iterator;
-
-protected:
- /// The match DAG this instruction belongs to.
- GIMatchDag &Dag;
-
- /// The name of the instruction in the pattern. For example:
- /// (FOO $a, $b, $c):$name
- /// will cause name to be assigned to this member. Anonymous instructions will
- /// have a name assigned for debugging purposes.
- StringRef Name;
-
- /// The name of the instruction in the pattern as assigned by the user. For
- /// example:
- /// (FOO $a, $b, $c):$name
- /// will cause name to be assigned to this member. If a name is not provided,
- /// this will be empty. This name is used to bind variables from rules to the
- /// matched instruction.
- StringRef UserAssignedName;
-
- /// The name of each operand (if any) that was assigned by the user. For
- /// example:
- /// (FOO $a, $b, $c):$name
- /// will cause {0, "a"}, {1, "b"}, {2, "c} to be inserted into this map.
- DenseMap<unsigned, StringRef> UserAssignedNamesForOperands;
-
- /// The operand list for this instruction. This object may be shared with
- /// other instructions of a similar 'shape'.
- const GIMatchDagOperandList &OperandInfo;
-
- /// For debugging purposes, it's helpful to have access to a description of
- /// the Opcode. However, this object shouldn't use it for more than debugging
- /// output since predicates are expected to be handled outside the DAG.
- CodeGenInstruction *OpcodeAnnotation = nullptr;
-
- /// When true, this instruction will be a starting point for a match attempt.
- bool IsMatchRoot = false;
-
-public:
- GIMatchDagInstr(GIMatchDag &Dag, StringRef Name, StringRef UserAssignedName,
- const GIMatchDagOperandList &OperandInfo)
- : Dag(Dag), Name(Name), UserAssignedName(UserAssignedName),
- OperandInfo(OperandInfo) {}
-
- const GIMatchDagOperandList &getOperandInfo() const { return OperandInfo; }
- StringRef getName() const { return Name; }
- StringRef getUserAssignedName() const { return UserAssignedName; }
- void assignNameToOperand(unsigned Idx, StringRef Name) {
- assert(UserAssignedNamesForOperands[Idx].empty() && "Cannot assign twice");
- UserAssignedNamesForOperands[Idx] = Name;
- }
-
- const_user_assigned_operand_names_iterator
- user_assigned_operand_names_begin() const {
- return UserAssignedNamesForOperands.begin();
- }
- const_user_assigned_operand_names_iterator
- user_assigned_operand_names_end() const {
- return UserAssignedNamesForOperands.end();
- }
- iterator_range<const_user_assigned_operand_names_iterator>
- user_assigned_operand_names() const {
- return make_range(user_assigned_operand_names_begin(),
- user_assigned_operand_names_end());
- }
-
- /// Mark this instruction as being a root of the match. This means that the
- /// matcher will start from this node when attempting to match MIR.
- void setMatchRoot();
- bool isMatchRoot() const { return IsMatchRoot; }
-
- void setOpcodeAnnotation(CodeGenInstruction *I) { OpcodeAnnotation = I; }
- CodeGenInstruction *getOpcodeAnnotation() const { return OpcodeAnnotation; }
-
- void print(raw_ostream &OS) const;
-
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- LLVM_DUMP_METHOD void dump() const { print(errs()); }
-#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-};
-
-raw_ostream &operator<<(raw_ostream &OS, const GIMatchDagInstr &N);
-
-} // end namespace llvm
-#endif // ifndef LLVM_UTILS_TABLEGEN_GIMATCHDAGINSTR_H
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.cpp b/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.cpp
deleted file mode 100644
index e79e4686b91e..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-//===- GIMatchDagOperands.cpp - A shared operand list for nodes -----------===//
-//
-// 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 "GIMatchDagOperands.h"
-
-#include "../CodeGenInstruction.h"
-
-using namespace llvm;
-
-void GIMatchDagOperand::Profile(FoldingSetNodeID &ID) const {
- Profile(ID, Idx, Name, IsDef);
-}
-
-void GIMatchDagOperand::Profile(FoldingSetNodeID &ID, size_t Idx,
- StringRef Name, bool IsDef) {
- ID.AddInteger(Idx);
- ID.AddString(Name);
- ID.AddBoolean(IsDef);
-}
-
-void GIMatchDagOperandList::add(StringRef Name, unsigned Idx, bool IsDef) {
- assert(Idx == Operands.size() && "Operands added in wrong order");
- Operands.emplace_back(Operands.size(), Name, IsDef);
- OperandsByName.try_emplace(Operands.back().getName(), Operands.size() - 1);
-}
-
-void GIMatchDagOperandList::Profile(FoldingSetNodeID &ID) const {
- for (const auto &I : enumerate(Operands))
- GIMatchDagOperand::Profile(ID, I.index(), I.value().getName(),
- I.value().isDef());
-}
-
-void GIMatchDagOperandList::print(raw_ostream &OS) const {
- if (Operands.empty()) {
- OS << "<empty>";
- return;
- }
- StringRef Separator = "";
- for (const auto &I : Operands) {
- OS << Separator << I.getIdx() << ":" << I.getName();
- if (I.isDef())
- OS << "<def>";
- Separator = ", ";
- }
-}
-
-const GIMatchDagOperandList::value_type &GIMatchDagOperandList::
-operator[](StringRef K) const {
- const auto &I = OperandsByName.find(K);
- assert(I != OperandsByName.end() && "Operand not found by name");
- return Operands[I->second];
-}
-
-const GIMatchDagOperandList &
-GIMatchDagOperandListContext::makeEmptyOperandList() {
- FoldingSetNodeID ID;
-
- void *InsertPoint;
- GIMatchDagOperandList *Value =
- OperandLists.FindNodeOrInsertPos(ID, InsertPoint);
- if (Value)
- return *Value;
-
- std::unique_ptr<GIMatchDagOperandList> NewValue =
- std::make_unique<GIMatchDagOperandList>();
- OperandLists.InsertNode(NewValue.get(), InsertPoint);
- OperandListsOwner.push_back(std::move(NewValue));
- return *OperandListsOwner.back().get();
-}
-
-const GIMatchDagOperandList &
-GIMatchDagOperandListContext::makeOperandList(const CodeGenInstruction &I) {
- FoldingSetNodeID ID;
- for (unsigned i = 0; i < I.Operands.size(); ++i)
- GIMatchDagOperand::Profile(ID, i, I.Operands[i].Name,
- i < I.Operands.NumDefs);
-
- void *InsertPoint;
- GIMatchDagOperandList *Value =
- OperandLists.FindNodeOrInsertPos(ID, InsertPoint);
- if (Value)
- return *Value;
-
- std::unique_ptr<GIMatchDagOperandList> NewValue =
- std::make_unique<GIMatchDagOperandList>();
- for (unsigned i = 0; i < I.Operands.size(); ++i)
- NewValue->add(I.Operands[i].Name, i, i < I.Operands.NumDefs);
- OperandLists.InsertNode(NewValue.get(), InsertPoint);
- OperandListsOwner.push_back(std::move(NewValue));
- return *OperandListsOwner.back().get();
-}
-
-const GIMatchDagOperandList &
-GIMatchDagOperandListContext::makeMIPredicateOperandList() {
- FoldingSetNodeID ID;
- GIMatchDagOperand::Profile(ID, 0, "$", true);
- GIMatchDagOperand::Profile(ID, 1, "mi", false);
-
- void *InsertPoint;
- GIMatchDagOperandList *Value =
- OperandLists.FindNodeOrInsertPos(ID, InsertPoint);
- if (Value)
- return *Value;
-
- std::unique_ptr<GIMatchDagOperandList> NewValue =
- std::make_unique<GIMatchDagOperandList>();
- NewValue->add("$", 0, true);
- NewValue->add("mi", 1, false);
- OperandLists.InsertNode(NewValue.get(), InsertPoint);
- OperandListsOwner.push_back(std::move(NewValue));
- return *OperandListsOwner.back().get();
-}
-
-
-const GIMatchDagOperandList &
-GIMatchDagOperandListContext::makeTwoMOPredicateOperandList() {
- FoldingSetNodeID ID;
- GIMatchDagOperand::Profile(ID, 0, "$", true);
- GIMatchDagOperand::Profile(ID, 1, "mi0", false);
- GIMatchDagOperand::Profile(ID, 2, "mi1", false);
-
- void *InsertPoint;
- GIMatchDagOperandList *Value =
- OperandLists.FindNodeOrInsertPos(ID, InsertPoint);
- if (Value)
- return *Value;
-
- std::unique_ptr<GIMatchDagOperandList> NewValue =
- std::make_unique<GIMatchDagOperandList>();
- NewValue->add("$", 0, true);
- NewValue->add("mi0", 1, false);
- NewValue->add("mi1", 2, false);
- OperandLists.InsertNode(NewValue.get(), InsertPoint);
- OperandListsOwner.push_back(std::move(NewValue));
- return *OperandListsOwner.back().get();
-}
-
-void GIMatchDagOperandListContext::print(raw_ostream &OS) const {
- OS << "GIMatchDagOperandListContext {\n"
- << " OperandLists {\n";
- for (const auto &I : OperandListsOwner) {
- OS << " ";
- I->print(OS);
- OS << "\n";
- }
- OS << " }\n"
- << "}\n";
-}
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.h b/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.h
deleted file mode 100644
index ae7190cb7296..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchDagOperands.h
+++ /dev/null
@@ -1,133 +0,0 @@
-//===- GIMatchDagOperands.h - Represent operand lists for nodes -*- C++ -*-===//
-//
-// 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_GIMATCHDAGOPERANDS_H
-#define LLVM_UTILS_TABLEGEN_GIMATCHDAGOPERANDS_H
-
-#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/StringMap.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/raw_ostream.h"
-
-#include <vector>
-
-namespace llvm {
-class CodeGenInstruction;
-/// Describes an operand of a MachineInstr w.r.t the DAG Matching. This
-/// information is derived from CodeGenInstruction::Operands but is more
-/// readily available for context-less access as we don't need to know which
-/// instruction it's used with or know how many defs that instruction had.
-///
-/// There may be multiple GIMatchDagOperand's with the same contents. However,
-/// they are uniqued within the set of instructions that have the same overall
-/// operand list. For example, given:
-/// Inst1 operands ($dst:<def>, $src1, $src2)
-/// Inst2 operands ($dst:<def>, $src1, $src2)
-/// Inst3 operands ($dst:<def>, $src)
-/// $src1 will have a single instance of GIMatchDagOperand shared by Inst1 and
-/// Inst2, as will $src2. $dst however, will have two instances one shared
-/// between Inst1 and Inst2 and one unique to Inst3. We could potentially
-/// fully de-dupe the GIMatchDagOperand instances but the saving is not expected
-/// to be worth the overhead.
-///
-/// The result of this is that the address of the object can be relied upon to
-/// trivially identify commonality between two instructions which will be useful
-/// when generating the matcher. When the pointers differ, the contents can be
-/// inspected instead.
-class GIMatchDagOperand {
- unsigned Idx;
- StringRef Name;
- bool IsDef;
-
-public:
- GIMatchDagOperand(unsigned Idx, StringRef Name, bool IsDef)
- : Idx(Idx), Name(Name), IsDef(IsDef) {}
-
- unsigned getIdx() const { return Idx; }
- StringRef getName() const { return Name; }
- bool isDef() const { return IsDef; }
-
- /// This object isn't a FoldingSetNode but it's part of one. See FoldingSet
- /// for details on the Profile function.
- void Profile(FoldingSetNodeID &ID) const;
-
- /// A helper that behaves like Profile() but is also usable without the object.
- /// We use size_t here to match enumerate<...>::index(). If we don't match
- /// that the hashes won't be equal.
- static void Profile(FoldingSetNodeID &ID, size_t Idx, StringRef Name,
- bool IsDef);
-};
-
-/// A list of GIMatchDagOperands for an instruction without any association with
-/// a particular instruction.
-///
-/// An important detail to be aware of with this class is that they are shared
-/// with other instructions of a similar 'shape'. For example, all the binary
-/// instructions are likely to share a single GIMatchDagOperandList. This is
-/// primarily a memory optimization as it's fairly common to have a large number
-/// of instructions but only a few 'shapes'.
-///
-/// See GIMatchDagOperandList::Profile() for the details on how they are folded.
-class GIMatchDagOperandList : public FoldingSetNode {
-public:
- using value_type = GIMatchDagOperand;
-
-protected:
- using vector_type = SmallVector<GIMatchDagOperand, 3>;
-
-public:
- using iterator = vector_type::iterator;
- using const_iterator = vector_type::const_iterator;
-
-protected:
- vector_type Operands;
- StringMap<unsigned> OperandsByName;
-
-public:
- void add(StringRef Name, unsigned Idx, bool IsDef);
-
- /// See FoldingSet for details.
- void Profile(FoldingSetNodeID &ID) const;
-
- iterator begin() { return Operands.begin(); }
- const_iterator begin() const { return Operands.begin(); }
- iterator end() { return Operands.end(); }
- const_iterator end() const { return Operands.end(); }
-
- const value_type &operator[](unsigned I) const { return Operands[I]; }
- const value_type &operator[](StringRef K) const;
-
- void print(raw_ostream &OS) const;
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- LLVM_DUMP_METHOD void dump() const { print(errs()); }
-#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-};
-
-/// This is the portion of GIMatchDagContext that directly relates to
-/// GIMatchDagOperandList and GIMatchDagOperandList.
-class GIMatchDagOperandListContext {
- FoldingSet<GIMatchDagOperandList> OperandLists;
- std::vector<std::unique_ptr<GIMatchDagOperandList>> OperandListsOwner;
-
-public:
- const GIMatchDagOperandList &makeEmptyOperandList();
- const GIMatchDagOperandList &makeOperandList(const CodeGenInstruction &I);
- const GIMatchDagOperandList &makeMIPredicateOperandList();
- const GIMatchDagOperandList &makeTwoMOPredicateOperandList();
-
- void print(raw_ostream &OS) const;
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- LLVM_DUMP_METHOD void dump() const { print(errs()); }
-#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-};
-
-} // end namespace llvm
-#endif // ifndef LLVM_UTILS_TABLEGEN_GIMATCHDAGOPERANDS_H
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.cpp b/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.cpp
deleted file mode 100644
index 6a9e33ac515e..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-//===- GIMatchDagPredicate.cpp - Represent a predicate to check -----------===//
-//
-// 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 "GIMatchDagPredicate.h"
-
-#include "llvm/TableGen/Record.h"
-
-#include "../CodeGenInstruction.h"
-#include "GIMatchDag.h"
-
-using namespace llvm;
-
-void GIMatchDagPredicate::print(raw_ostream &OS) const {
- OS << "<<";
- printDescription(OS);
- OS << ">>:$" << Name;
-}
-
-void GIMatchDagPredicate::printDescription(raw_ostream &OS) const { OS << ""; }
-
-GIMatchDagOpcodePredicate::GIMatchDagOpcodePredicate(
- GIMatchDagContext &Ctx, StringRef Name, const CodeGenInstruction &Instr)
- : GIMatchDagPredicate(GIMatchDagPredicateKind_Opcode, Name,
- Ctx.makeMIPredicateOperandList()),
- Instr(Instr) {}
-
-void GIMatchDagOpcodePredicate::printDescription(raw_ostream &OS) const {
- OS << "$mi.getOpcode() == " << Instr.TheDef->getName();
-}
-
-GIMatchDagOneOfOpcodesPredicate::GIMatchDagOneOfOpcodesPredicate(
- GIMatchDagContext &Ctx, StringRef Name)
- : GIMatchDagPredicate(GIMatchDagPredicateKind_OneOfOpcodes, Name,
- Ctx.makeMIPredicateOperandList()) {}
-
-void GIMatchDagOneOfOpcodesPredicate::printDescription(raw_ostream &OS) const {
- OS << "$mi.getOpcode() == oneof(";
- StringRef Separator = "";
- for (const CodeGenInstruction *Instr : Instrs) {
- OS << Separator << Instr->TheDef->getName();
- Separator = ",";
- }
- OS << ")";
-}
-
-GIMatchDagSameMOPredicate::GIMatchDagSameMOPredicate(GIMatchDagContext &Ctx,
- StringRef Name)
- : GIMatchDagPredicate(GIMatchDagPredicateKind_SameMO, Name,
- Ctx.makeTwoMOPredicateOperandList()) {}
-
-void GIMatchDagSameMOPredicate::printDescription(raw_ostream &OS) const {
- OS << "$mi0 == $mi1";
-}
-
-raw_ostream &llvm::operator<<(raw_ostream &OS, const GIMatchDagPredicate &N) {
- N.print(OS);
- return OS;
-}
-
-raw_ostream &llvm::operator<<(raw_ostream &OS,
- const GIMatchDagOpcodePredicate &N) {
- N.print(OS);
- return OS;
-}
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.h b/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.h
deleted file mode 100644
index 952cbdb24f54..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicate.h
+++ /dev/null
@@ -1,145 +0,0 @@
-//===- GIMatchDagPredicate - Represent a predicate to check -----*- C++ -*-===//
-//
-// 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_GIMATCHDAGPREDICATE_H
-#define LLVM_UTILS_TABLEGEN_GIMATCHDAGPREDICATE_H
-
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringRef.h"
-
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-#include "llvm/Support/raw_ostream.h"
-#endif
-
-namespace llvm {
-class CodeExpansions;
-class CodeGenInstruction;
-class GIMatchDagOperandList;
-class GIMatchDagContext;
-class raw_ostream;
-
-/// Represents a predicate on the match DAG. This records the details of the
-/// predicate. The dependencies are stored in the GIMatchDag as edges.
-///
-/// Instances of this class objects are owned by the GIMatchDag and are not
-/// shareable between instances of GIMatchDag.
-class GIMatchDagPredicate {
-public:
- enum GIMatchDagPredicateKind {
- GIMatchDagPredicateKind_Opcode,
- GIMatchDagPredicateKind_OneOfOpcodes,
- GIMatchDagPredicateKind_SameMO,
- };
-
-protected:
- const GIMatchDagPredicateKind Kind;
-
- /// The name of the predicate. For example:
- /// (FOO $a:s32, $b, $c)
- /// will cause 's32' to be assigned to this member for the $a predicate.
- /// Similarly, the opcode predicate will cause 'FOO' to be assigned to this
- /// member. Anonymous instructions will have a name assigned for debugging
- /// purposes.
- StringRef Name;
-
- /// The operand list for this predicate. This object may be shared with
- /// other predicates of a similar 'shape'.
- const GIMatchDagOperandList &OperandInfo;
-
-public:
- GIMatchDagPredicate(GIMatchDagPredicateKind Kind, StringRef Name,
- const GIMatchDagOperandList &OperandInfo)
- : Kind(Kind), Name(Name), OperandInfo(OperandInfo) {}
- virtual ~GIMatchDagPredicate() {}
-
- GIMatchDagPredicateKind getKind() const { return Kind; }
-
- StringRef getName() const { return Name; }
- const GIMatchDagOperandList &getOperandInfo() const { return OperandInfo; }
-
- // Generate C++ code to check this predicate. If a partitioner has already
- // tested this predicate then this function won't be called. If this function
- // is called, it must emit code and return true to indicate that it did so. If
- // it ever returns false, then the caller will abort due to an untested
- // predicate.
- virtual bool generateCheckCode(raw_ostream &OS, StringRef Indent,
- const CodeExpansions &Expansions) const {
- return false;
- }
-
- virtual void print(raw_ostream &OS) const;
- virtual void printDescription(raw_ostream &OS) const;
-
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- virtual LLVM_DUMP_METHOD void dump() const { print(errs()); }
-#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-};
-
-class GIMatchDagOpcodePredicate : public GIMatchDagPredicate {
- const CodeGenInstruction &Instr;
-
-public:
- GIMatchDagOpcodePredicate(GIMatchDagContext &Ctx, StringRef Name,
- const CodeGenInstruction &Instr);
-
- static bool classof(const GIMatchDagPredicate *P) {
- return P->getKind() == GIMatchDagPredicateKind_Opcode;
- }
-
- const CodeGenInstruction *getInstr() const { return &Instr; }
-
- void printDescription(raw_ostream &OS) const override;
-
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- LLVM_DUMP_METHOD void dump() const override { print(errs()); }
-#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-};
-
-class GIMatchDagOneOfOpcodesPredicate : public GIMatchDagPredicate {
- SmallVector<const CodeGenInstruction *, 4> Instrs;
-
-public:
- GIMatchDagOneOfOpcodesPredicate(GIMatchDagContext &Ctx, StringRef Name);
-
- void addOpcode(const CodeGenInstruction *Instr) { Instrs.push_back(Instr); }
-
- static bool classof(const GIMatchDagPredicate *P) {
- return P->getKind() == GIMatchDagPredicateKind_OneOfOpcodes;
- }
-
- const SmallVectorImpl<const CodeGenInstruction *> &getInstrs() const {
- return Instrs;
- }
-
- void printDescription(raw_ostream &OS) const override;
-
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- LLVM_DUMP_METHOD void dump() const override { print(errs()); }
-#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-};
-
-class GIMatchDagSameMOPredicate : public GIMatchDagPredicate {
-public:
- GIMatchDagSameMOPredicate(GIMatchDagContext &Ctx, StringRef Name);
-
- static bool classof(const GIMatchDagPredicate *P) {
- return P->getKind() == GIMatchDagPredicateKind_SameMO;
- }
-
- void printDescription(raw_ostream &OS) const override;
-
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- LLVM_DUMP_METHOD void dump() const override { print(errs()); }
-#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-};
-
-raw_ostream &operator<<(raw_ostream &OS, const GIMatchDagPredicate &N);
-raw_ostream &operator<<(raw_ostream &OS, const GIMatchDagOpcodePredicate &N);
-
-} // end namespace llvm
-#endif // ifndef LLVM_UTILS_TABLEGEN_GIMATCHDAGPREDICATE_H
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicateDependencyEdge.cpp b/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicateDependencyEdge.cpp
deleted file mode 100644
index 921cbaf9c408..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicateDependencyEdge.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-//===- GIMatchDagPredicateDependencyEdge.cpp - Have inputs before check ---===//
-//
-// 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 "GIMatchDagPredicateDependencyEdge.h"
-
-#include "GIMatchDagInstr.h"
-#include "GIMatchDagOperands.h"
-#include "GIMatchDagPredicate.h"
-
-#include "llvm/Support/raw_ostream.h"
-
-using namespace llvm;
-
-LLVM_DUMP_METHOD void
-GIMatchDagPredicateDependencyEdge::print(raw_ostream &OS) const {
- OS << getRequiredMI()->getName();
- if (getRequiredMO())
- OS << "[" << getRequiredMO()->getName() << "]";
- OS << " ==> " << getPredicate()->getName() << "["
- << getPredicateOp()->getName() << "]";
-}
-
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-LLVM_DUMP_METHOD void GIMatchDagPredicateDependencyEdge::dump() const {
- print(errs());
-}
-#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-
-raw_ostream &llvm::operator<<(raw_ostream &OS,
- const GIMatchDagPredicateDependencyEdge &E) {
- E.print(OS);
- return OS;
-}
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicateDependencyEdge.h b/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicateDependencyEdge.h
deleted file mode 100644
index af91afc6073d..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchDagPredicateDependencyEdge.h
+++ /dev/null
@@ -1,61 +0,0 @@
-//===- GIMatchDagPredicateDependencyEdge - Ensure predicates have inputs --===//
-//
-// 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_GIMATCHDAGPREDICATEEDGE_H
-#define LLVM_UTILS_TABLEGEN_GIMATCHDAGPREDICATEEDGE_H
-
-#include "llvm/Support/Compiler.h"
-
-namespace llvm {
-class GIMatchDagInstr;
-class GIMatchDagPredicate;
-class GIMatchDagOperand;
-
-class raw_ostream;
-
-/// Represents a dependency that must be met to evaluate a predicate.
-///
-/// Instances of this class objects are owned by the GIMatchDag and are not
-/// shareable between instances of GIMatchDag.
-class GIMatchDagPredicateDependencyEdge {
- /// The MI that must be available in order to test the predicate.
- const GIMatchDagInstr *RequiredMI;
- /// The MO that must be available in order to test the predicate. May be
- /// nullptr when only the MI is required.
- const GIMatchDagOperand *RequiredMO;
- /// The Predicate that requires information from RequiredMI/RequiredMO.
- const GIMatchDagPredicate *Predicate;
- /// The Predicate operand that requires information from
- /// RequiredMI/RequiredMO.
- const GIMatchDagOperand *PredicateOp;
-
-public:
- GIMatchDagPredicateDependencyEdge(const GIMatchDagInstr *RequiredMI,
- const GIMatchDagOperand *RequiredMO,
- const GIMatchDagPredicate *Predicate,
- const GIMatchDagOperand *PredicateOp)
- : RequiredMI(RequiredMI), RequiredMO(RequiredMO), Predicate(Predicate),
- PredicateOp(PredicateOp) {}
-
- const GIMatchDagInstr *getRequiredMI() const { return RequiredMI; }
- const GIMatchDagOperand *getRequiredMO() const { return RequiredMO; }
- const GIMatchDagPredicate *getPredicate() const { return Predicate; }
- const GIMatchDagOperand *getPredicateOp() const { return PredicateOp; }
-
- void print(raw_ostream &OS) const;
-
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- LLVM_DUMP_METHOD void dump() const;
-#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-};
-
-raw_ostream &operator<<(raw_ostream &OS,
- const GIMatchDagPredicateDependencyEdge &N);
-
-} // end namespace llvm
-#endif // ifndef LLVM_UTILS_TABLEGEN_GIMATCHDAGPREDICATEEDGE_H
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchTree.cpp b/llvm/utils/TableGen/GlobalISel/GIMatchTree.cpp
deleted file mode 100644
index 23697fd9e2e2..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchTree.cpp
+++ /dev/null
@@ -1,761 +0,0 @@
-//===- GIMatchTree.cpp - A decision tree to match GIMatchDag's ------------===//
-//
-// 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 "GIMatchTree.h"
-#include "GIMatchDagPredicate.h"
-
-#include "../CodeGenInstruction.h"
-
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/Format.h"
-#include "llvm/Support/ScopedPrinter.h"
-#include "llvm/Support/raw_ostream.h"
-#include "llvm/TableGen/Error.h"
-#include "llvm/TableGen/Record.h"
-
-#define DEBUG_TYPE "gimatchtree"
-
-using namespace llvm;
-
-void GIMatchTree::writeDOTGraph(raw_ostream &OS) const {
- OS << "digraph \"matchtree\" {\n";
- writeDOTGraphNode(OS);
- OS << "}\n";
-}
-
-void GIMatchTree::writeDOTGraphNode(raw_ostream &OS) const {
- OS << format(" Node%p", this) << " [shape=record,label=\"{";
- if (Partitioner) {
- Partitioner->emitDescription(OS);
- OS << "|" << Partitioner->getNumPartitions() << " partitions|";
- } else
- OS << "No partitioner|";
- bool IsFullyTraversed = true;
- bool IsFullyTested = true;
- StringRef Separator = "";
- for (const auto &Leaf : PossibleLeaves) {
- OS << Separator << Leaf.getName();
- Separator = ",";
- if (!Leaf.isFullyTraversed())
- IsFullyTraversed = false;
- if (!Leaf.isFullyTested())
- IsFullyTested = false;
- }
- if (!Partitioner && !IsFullyTraversed)
- OS << "|Not fully traversed";
- if (!Partitioner && !IsFullyTested) {
- OS << "|Not fully tested";
- if (IsFullyTraversed) {
- for (const GIMatchTreeLeafInfo &Leaf : PossibleLeaves) {
- if (Leaf.isFullyTested())
- continue;
- OS << "\\n" << Leaf.getName() << ": " << &Leaf;
- for (const GIMatchDagPredicate *P : Leaf.untested_predicates())
- OS << *P;
- }
- }
- }
- OS << "}\"";
- if (!Partitioner &&
- (!IsFullyTraversed || !IsFullyTested || PossibleLeaves.size() > 1))
- OS << ",color=red";
- OS << "]\n";
- for (const auto &C : Children)
- C.writeDOTGraphNode(OS);
- writeDOTGraphEdges(OS);
-}
-
-void GIMatchTree::writeDOTGraphEdges(raw_ostream &OS) const {
- for (const auto &Child : enumerate(Children)) {
- OS << format(" Node%p", this) << " -> " << format("Node%p", &Child.value())
- << " [label=\"#" << Child.index() << " ";
- Partitioner->emitPartitionName(OS, Child.index());
- OS << "\"]\n";
- }
-}
-
-GIMatchTreeBuilderLeafInfo::GIMatchTreeBuilderLeafInfo(
- GIMatchTreeBuilder &Builder, StringRef Name, unsigned RootIdx,
- const GIMatchDag &MatchDag, void *Data)
- : Builder(Builder), Info(Name, RootIdx, Data), MatchDag(MatchDag),
- RemainingInstrNodes(BitVector(MatchDag.getNumInstrNodes(), true)),
- RemainingEdges(BitVector(MatchDag.getNumEdges(), true)),
- RemainingPredicates(BitVector(MatchDag.getNumPredicates(), true)),
- TraversableEdges(MatchDag.getNumEdges()),
- TestablePredicates(MatchDag.getNumPredicates()) {
- // Number all the predicates in this DAG
- for (const auto &[Idx, P] : enumerate(MatchDag.predicates())) {
- PredicateIDs.insert(std::make_pair(P, Idx));
- }
-
- // Number all the predicate dependencies in this DAG and set up a bitvector
- // for each predicate indicating the unsatisfied dependencies.
- for (const auto &[Idx, Dep] : enumerate(MatchDag.predicate_edges())) {
- PredicateDepIDs.insert(std::make_pair(Dep, Idx));
- }
- UnsatisfiedPredDepsForPred.resize(MatchDag.getNumPredicates(),
- BitVector(PredicateDepIDs.size()));
- for (const auto &[Idx, Dep] : enumerate(MatchDag.predicate_edges())) {
- unsigned ID = PredicateIDs.lookup(Dep->getPredicate());
- UnsatisfiedPredDepsForPred[ID].set(Idx);
- }
-}
-
-void GIMatchTreeBuilderLeafInfo::declareInstr(const GIMatchDagInstr *Instr, unsigned ID) {
- // Record the assignment of this instr to the given ID.
- auto InfoI = InstrNodeToInfo.insert(std::make_pair(
- Instr, GIMatchTreeInstrInfo(ID, Instr)));
- InstrIDToInfo.insert(std::make_pair(ID, &InfoI.first->second));
-
- if (Instr == nullptr)
- return;
-
- if (!Instr->getUserAssignedName().empty())
- Info.bindInstrVariable(Instr->getUserAssignedName(), ID);
- for (const auto &VarBinding : Instr->user_assigned_operand_names())
- Info.bindOperandVariable(VarBinding.second, ID, VarBinding.first);
-
- // Clear the bit indicating we haven't visited this instr.
- const auto &NodeI = find(MatchDag.instr_nodes(), Instr);
- assert(NodeI != MatchDag.instr_nodes_end() && "Instr isn't in this DAG");
- unsigned InstrIdx = MatchDag.getInstrNodeIdx(NodeI);
- RemainingInstrNodes.reset(InstrIdx);
-
- // When we declare an instruction, we don't expose any traversable edges just
- // yet. A partitioner has to check they exist and are registers before they
- // are traversable.
-
- // When we declare an instruction, we potentially activate some predicates.
- // Mark the dependencies that are now satisfied as a result of this
- // instruction and mark any predicates whose dependencies are fully
- // satisfied.
- for (const auto &Dep : enumerate(MatchDag.predicate_edges())) {
- if (Dep.value()->getRequiredMI() == Instr &&
- Dep.value()->getRequiredMO() == nullptr) {
- for (const auto &DepsFor : enumerate(UnsatisfiedPredDepsForPred)) {
- DepsFor.value().reset(Dep.index());
- if (DepsFor.value().none())
- TestablePredicates.set(DepsFor.index());
- }
- }
- }
-}
-
-void GIMatchTreeBuilderLeafInfo::declareOperand(unsigned InstrID,
- unsigned OpIdx) {
- const GIMatchDagInstr *Instr = InstrIDToInfo.lookup(InstrID)->getInstrNode();
-
- OperandIDToInfo.insert(std::make_pair(
- std::make_pair(InstrID, OpIdx),
- GIMatchTreeOperandInfo(Instr, OpIdx)));
-
- // When an operand becomes reachable, we potentially activate some traversals.
- // Record the edges that can now be followed as a result of this
- // instruction.
- for (const auto &[Idx, E] : enumerate(MatchDag.edges())) {
- if (E->getFromMI() == Instr && E->getFromMO()->getIdx() == OpIdx) {
- TraversableEdges.set(Idx);
- }
- }
-
- // When an operand becomes reachable, we potentially activate some predicates.
- // Clear the dependencies that are now satisfied as a result of this
- // operand and activate any predicates whose dependencies are fully
- // satisfied.
- for (const auto &Dep : enumerate(MatchDag.predicate_edges())) {
- if (Dep.value()->getRequiredMI() == Instr && Dep.value()->getRequiredMO() &&
- Dep.value()->getRequiredMO()->getIdx() == OpIdx) {
- for (const auto &DepsFor : enumerate(UnsatisfiedPredDepsForPred)) {
- DepsFor.value().reset(Dep.index());
- if (DepsFor.value().none())
- TestablePredicates.set(DepsFor.index());
- }
- }
- }
-}
-
-void GIMatchTreeBuilder::addPartitionersForInstr(unsigned InstrIdx) {
- // Find the partitioners that can be used now that this node is
- // uncovered. Our choices are:
- // - Test the opcode
- addPartitioner(std::make_unique<GIMatchTreeOpcodePartitioner>(InstrIdx));
-}
-
-void GIMatchTreeBuilder::addPartitionersForOperand(unsigned InstrID,
- unsigned OpIdx) {
- LLVM_DEBUG(dbgs() << "Add partitioners for Instrs[" << InstrID
- << "].getOperand(" << OpIdx << ")\n");
- addPartitioner(
- std::make_unique<GIMatchTreeVRegDefPartitioner>(InstrID, OpIdx));
-}
-
-void GIMatchTreeBuilder::filterRedundantPartitioners() {
- // TODO: Filter partitioners for facts that are already known
- // - If we know the opcode, we can elide the num operand check so long as
- // the instruction has a fixed number of operands.
- // - If we know an exact number of operands then we can elide further number
- // of operand checks.
- // - If the current min number of operands exceeds the one we want to check
- // then we can elide it.
-}
-
-void GIMatchTreeBuilder::evaluatePartitioners() {
- // Determine the partitioning the partitioner would produce
- for (auto &Partitioner : Partitioners) {
- LLVM_DEBUG(dbgs() << " Weighing up ";
- Partitioner->emitDescription(dbgs()); dbgs() << "\n");
- Partitioner->repartition(Leaves);
- LLVM_DEBUG(Partitioner->emitPartitionResults(dbgs()));
- }
-}
-
-void GIMatchTreeBuilder::runStep() {
- LLVM_DEBUG(dbgs() << "Building match tree node for " << TreeNode << "\n");
- LLVM_DEBUG(dbgs() << " Rules reachable at this node:\n");
- for (const auto &Leaf : Leaves) {
- LLVM_DEBUG(dbgs() << " " << Leaf.getName() << " (" << &Leaf.getInfo() << "\n");
- TreeNode->addPossibleLeaf(Leaf.getInfo(), Leaf.isFullyTraversed(),
- Leaf.isFullyTested());
- }
-
- LLVM_DEBUG(dbgs() << " Partitioners available at this node:\n");
-#ifndef NDEBUG
- for (const auto &Partitioner : Partitioners)
- LLVM_DEBUG(dbgs() << " "; Partitioner->emitDescription(dbgs());
- dbgs() << "\n");
-#endif // ifndef NDEBUG
-
- LLVM_DEBUG(dbgs() << " Eliminating redundant partitioners:\n");
- filterRedundantPartitioners();
- LLVM_DEBUG(dbgs() << " Partitioners remaining:\n");
-#ifndef NDEBUG
- for (const auto &Partitioner : Partitioners)
- LLVM_DEBUG(dbgs() << " "; Partitioner->emitDescription(dbgs());
- dbgs() << "\n");
-#endif // ifndef NDEBUG
-
- if (Partitioners.empty()) {
- // Nothing left to do but check we really did identify a single rule.
- if (Leaves.size() > 1) {
- LLVM_DEBUG(dbgs() << "Leaf contains multiple rules, drop after the first "
- "fully tested rule\n");
- auto FirstFullyTested =
- llvm::find_if(Leaves, [](const GIMatchTreeBuilderLeafInfo &X) {
- return X.isFullyTraversed() && X.isFullyTested() &&
- !X.getMatchDag().hasPostMatchPredicate();
- });
- if (FirstFullyTested != Leaves.end())
- FirstFullyTested++;
-
-#ifndef NDEBUG
- for (auto &Leaf : make_range(Leaves.begin(), FirstFullyTested))
- LLVM_DEBUG(dbgs() << " Kept " << Leaf.getName() << "\n");
- for (const auto &Leaf : make_range(FirstFullyTested, Leaves.end()))
- LLVM_DEBUG(dbgs() << " Dropped " << Leaf.getName() << "\n");
-#endif // ifndef NDEBUG
- TreeNode->dropLeavesAfter(
- std::distance(Leaves.begin(), FirstFullyTested));
- }
- for (const auto &Leaf : Leaves) {
- if (!Leaf.isFullyTraversed()) {
- PrintError("Leaf " + Leaf.getName() + " is not fully traversed");
- PrintNote("This indicates a missing partitioner within tblgen");
- Leaf.dump(errs());
- for (unsigned InstrIdx : Leaf.untested_instrs())
- PrintNote("Instr " + llvm::to_string(*Leaf.getInstr(InstrIdx)));
- for (unsigned EdgeIdx : Leaf.untested_edges())
- PrintNote("Edge " + llvm::to_string(*Leaf.getEdge(EdgeIdx)));
- }
- }
-
- // Copy out information about untested predicates so the user of the tree
- // can deal with them.
- for (auto LeafPair : zip(Leaves, TreeNode->possible_leaves())) {
- const GIMatchTreeBuilderLeafInfo &BuilderLeaf = std::get<0>(LeafPair);
- GIMatchTreeLeafInfo &TreeLeaf = std::get<1>(LeafPair);
- if (!BuilderLeaf.isFullyTested())
- for (unsigned PredicateIdx : BuilderLeaf.untested_predicates())
- TreeLeaf.addUntestedPredicate(BuilderLeaf.getPredicate(PredicateIdx));
- }
- return;
- }
-
- LLVM_DEBUG(dbgs() << " Weighing up partitioners:\n");
- evaluatePartitioners();
-
- // Select the best partitioner by its ability to partition
- // - Prefer partitioners that don't distinguish between partitions. This
- // is to fail early on decisions that must go a single way.
- auto PartitionerI = std::max_element(
- Partitioners.begin(), Partitioners.end(),
- [](const std::unique_ptr<GIMatchTreePartitioner> &A,
- const std::unique_ptr<GIMatchTreePartitioner> &B) {
- // We generally want partitioners that subdivide the
- // ruleset as much as possible since these take fewer
- // checks to converge on a particular rule. However,
- // it's important to note that one leaf can end up in
- // multiple partitions if the check isn't mutually
- // exclusive (e.g. getVRegDef() vs isReg()).
- // We therefore minimize average leaves per partition.
- return (double)A->getNumLeavesWithDupes() / A->getNumPartitions() >
- (double)B->getNumLeavesWithDupes() / B->getNumPartitions();
- });
-
- // Select a partitioner and partition the ruleset
- // Note that it's possible for a single rule to end up in multiple
- // partitions. For example, an opcode test on a rule without an opcode
- // predicate will result in it being passed to all partitions.
- std::unique_ptr<GIMatchTreePartitioner> Partitioner = std::move(*PartitionerI);
- Partitioners.erase(PartitionerI);
- LLVM_DEBUG(dbgs() << " Selected partitioner: ";
- Partitioner->emitDescription(dbgs()); dbgs() << "\n");
-
- assert(Partitioner->getNumPartitions() > 0 &&
- "Must always partition into at least one partition");
-
- TreeNode->setNumChildren(Partitioner->getNumPartitions());
- for (const auto &[Idx, Child] : enumerate(TreeNode->children())) {
- SubtreeBuilders.emplace_back(&Child, NextInstrID);
- Partitioner->applyForPartition(Idx, *this, SubtreeBuilders.back());
- }
-
- TreeNode->setPartitioner(std::move(Partitioner));
-
- // Recurse into the subtree builders. Each one must get a copy of the
- // remaining partitioners as each path has to check everything.
- for (auto &SubtreeBuilder : SubtreeBuilders) {
- for (const auto &Partitioner : Partitioners)
- SubtreeBuilder.addPartitioner(Partitioner->clone());
- SubtreeBuilder.runStep();
- }
-}
-
-std::unique_ptr<GIMatchTree> GIMatchTreeBuilder::run() {
- unsigned NewInstrID = allocInstrID();
- // Start by recording the root instruction as instr #0 and set up the initial
- // partitioners.
- for (auto &Leaf : Leaves) {
- LLVM_DEBUG(Leaf.getMatchDag().writeDOTGraph(dbgs(), Leaf.getName()));
- GIMatchDagInstr *Root =
- *(Leaf.getMatchDag().roots().begin() + Leaf.getRootIdx());
- Leaf.declareInstr(Root, NewInstrID);
- }
-
- addPartitionersForInstr(NewInstrID);
-
- std::unique_ptr<GIMatchTree> TreeRoot = std::make_unique<GIMatchTree>();
- TreeNode = TreeRoot.get();
- runStep();
-
- return TreeRoot;
-}
-
-void GIMatchTreeOpcodePartitioner::emitPartitionName(raw_ostream &OS, unsigned Idx) const {
- if (PartitionToInstr[Idx] == nullptr) {
- OS << "* or nullptr";
- return;
- }
- OS << PartitionToInstr[Idx]->Namespace
- << "::" << PartitionToInstr[Idx]->TheDef->getName();
-}
-
-void GIMatchTreeOpcodePartitioner::repartition(
- GIMatchTreeBuilder::LeafVec &Leaves) {
- Partitions.clear();
- InstrToPartition.clear();
- PartitionToInstr.clear();
- TestedPredicates.clear();
-
- for (const auto &Leaf : enumerate(Leaves)) {
- bool AllOpcodes = true;
- GIMatchTreeInstrInfo *InstrInfo = Leaf.value().getInstrInfo(InstrID);
- BitVector TestedPredicatesForLeaf(
- Leaf.value().getMatchDag().getNumPredicates());
-
- // If the instruction isn't declared then we don't care about it. Ignore
- // it for now and add it to all partitions later once we know what
- // partitions we have.
- if (!InstrInfo) {
- LLVM_DEBUG(dbgs() << " " << Leaf.value().getName()
- << " doesn't care about Instr[" << InstrID << "]\n");
- assert(TestedPredicatesForLeaf.size() == Leaf.value().getMatchDag().getNumPredicates());
- TestedPredicates.push_back(TestedPredicatesForLeaf);
- continue;
- }
-
- // If the opcode is available to test then any opcode predicates will have
- // been enabled too.
- for (unsigned PIdx : Leaf.value().TestablePredicates.set_bits()) {
- const auto &P = Leaf.value().getPredicate(PIdx);
- SmallVector<const CodeGenInstruction *, 1> OpcodesForThisPredicate;
- if (const auto *OpcodeP = dyn_cast<const GIMatchDagOpcodePredicate>(P)) {
- // We've found _an_ opcode predicate, but we don't know if it's
- // checking this instruction yet.
- bool IsThisPredicate = false;
- for (const auto &PDep : Leaf.value().getMatchDag().predicate_edges()) {
- if (PDep->getRequiredMI() == InstrInfo->getInstrNode() &&
- PDep->getRequiredMO() == nullptr && PDep->getPredicate() == P) {
- IsThisPredicate = true;
- break;
- }
- }
- if (!IsThisPredicate)
- continue;
-
- // If we get here twice then we've somehow ended up with two opcode
- // predicates for one instruction in the same DAG. That should be
- // impossible.
- assert(AllOpcodes && "Conflicting opcode predicates");
- const CodeGenInstruction *Expected = OpcodeP->getInstr();
- OpcodesForThisPredicate.push_back(Expected);
- }
-
- if (const auto *OpcodeP =
- dyn_cast<const GIMatchDagOneOfOpcodesPredicate>(P)) {
- // We've found _an_ oneof(opcodes) predicate, but we don't know if it's
- // checking this instruction yet.
- bool IsThisPredicate = false;
- for (const auto &PDep : Leaf.value().getMatchDag().predicate_edges()) {
- if (PDep->getRequiredMI() == InstrInfo->getInstrNode() &&
- PDep->getRequiredMO() == nullptr && PDep->getPredicate() == P) {
- IsThisPredicate = true;
- break;
- }
- }
- if (!IsThisPredicate)
- continue;
-
- // If we get here twice then we've somehow ended up with two opcode
- // predicates for one instruction in the same DAG. That should be
- // impossible.
- assert(AllOpcodes && "Conflicting opcode predicates");
- append_range(OpcodesForThisPredicate, OpcodeP->getInstrs());
- }
-
- for (const CodeGenInstruction *Expected : OpcodesForThisPredicate) {
- // Mark this predicate as one we're testing.
- TestedPredicatesForLeaf.set(PIdx);
-
- // Partitions must be numbered 0, 1, .., N but instructions don't meet
- // that requirement. Assign a partition number to each opcode if we
- // lack one ...
- auto Partition = InstrToPartition.find(Expected);
- if (Partition == InstrToPartition.end()) {
- BitVector Contents(Leaves.size());
- Partition = InstrToPartition
- .insert(std::make_pair(Expected, Partitions.size()))
- .first;
- PartitionToInstr.push_back(Expected);
- Partitions.insert(std::make_pair(Partitions.size(), Contents));
- }
- // ... and mark this leaf as being in that partition.
- Partitions.find(Partition->second)->second.set(Leaf.index());
- AllOpcodes = false;
- LLVM_DEBUG(dbgs() << " " << Leaf.value().getName()
- << " is in partition " << Partition->second << "\n");
- }
-
- // TODO: This is where we would handle multiple choices of opcode
- // the end result will be that this leaf ends up in multiple
- // partitions similarly to AllOpcodes.
- }
-
- // If we never check the opcode, add it to every partition.
- if (AllOpcodes) {
- // Add a partition for the default case if we don't already have one.
- if (InstrToPartition.insert(std::make_pair(nullptr, 0)).second) {
- PartitionToInstr.push_back(nullptr);
- BitVector Contents(Leaves.size());
- Partitions.insert(std::make_pair(Partitions.size(), Contents));
- }
- LLVM_DEBUG(dbgs() << " " << Leaf.value().getName()
- << " is in all partitions (opcode not checked)\n");
- for (auto &Partition : Partitions)
- Partition.second.set(Leaf.index());
- }
-
- assert(TestedPredicatesForLeaf.size() == Leaf.value().getMatchDag().getNumPredicates());
- TestedPredicates.push_back(TestedPredicatesForLeaf);
- }
-
- if (Partitions.size() == 0) {
- // Add a partition for the default case if we don't already have one.
- if (InstrToPartition.insert(std::make_pair(nullptr, 0)).second) {
- PartitionToInstr.push_back(nullptr);
- BitVector Contents(Leaves.size());
- Partitions.insert(std::make_pair(Partitions.size(), Contents));
- }
- }
-
- // Add any leaves that don't care about this instruction to all partitions.
- for (const auto &Leaf : enumerate(Leaves)) {
- GIMatchTreeInstrInfo *InstrInfo = Leaf.value().getInstrInfo(InstrID);
- if (!InstrInfo) {
- // Add a partition for the default case if we don't already have one.
- if (InstrToPartition.insert(std::make_pair(nullptr, 0)).second) {
- PartitionToInstr.push_back(nullptr);
- BitVector Contents(Leaves.size());
- Partitions.insert(std::make_pair(Partitions.size(), Contents));
- }
- for (auto &Partition : Partitions)
- Partition.second.set(Leaf.index());
- }
- }
-
-}
-
-void GIMatchTreeOpcodePartitioner::applyForPartition(
- unsigned PartitionIdx, GIMatchTreeBuilder &Builder, GIMatchTreeBuilder &SubBuilder) {
- LLVM_DEBUG(dbgs() << " Making partition " << PartitionIdx << "\n");
- const CodeGenInstruction *CGI = PartitionToInstr[PartitionIdx];
-
- BitVector PossibleLeaves = getPossibleLeavesForPartition(PartitionIdx);
- // Consume any predicates we handled.
- for (const auto &[Index, EnumeratedLeaf] :
- enumerate(Builder.getPossibleLeaves())) {
- if (!PossibleLeaves[Index])
- continue;
-
- const auto &TestedPredicatesForLeaf = TestedPredicates[Index];
-
- for (unsigned PredIdx : TestedPredicatesForLeaf.set_bits()) {
- LLVM_DEBUG(dbgs() << " " << EnumeratedLeaf.getName()
- << " tested predicate #" << PredIdx << " of "
- << TestedPredicatesForLeaf.size() << " "
- << *EnumeratedLeaf.getPredicate(PredIdx) << "\n");
- EnumeratedLeaf.RemainingPredicates.reset(PredIdx);
- EnumeratedLeaf.TestablePredicates.reset(PredIdx);
- }
- SubBuilder.addLeaf(EnumeratedLeaf);
- }
-
- // Nothing to do, we don't know anything about this instruction as a result
- // of this partitioner.
- if (CGI == nullptr)
- return;
-
- GIMatchTreeBuilder::LeafVec &NewLeaves = SubBuilder.getPossibleLeaves();
- // Find all the operands we know to exist and are referenced. This will
- // usually be all the referenced operands but there are some cases where
- // instructions are variadic. Such operands must be handled by partitioners
- // that check the number of operands.
- BitVector ReferencedOperands(1);
- for (auto &Leaf : NewLeaves) {
- GIMatchTreeInstrInfo *InstrInfo = Leaf.getInstrInfo(InstrID);
- // Skip any leaves that don't care about this instruction.
- if (!InstrInfo)
- continue;
- const GIMatchDagInstr *Instr = InstrInfo->getInstrNode();
- for (const auto &E : Leaf.getMatchDag().edges()) {
- if (E->getFromMI() == Instr &&
- E->getFromMO()->getIdx() < CGI->Operands.size()) {
- ReferencedOperands.resize(E->getFromMO()->getIdx() + 1);
- ReferencedOperands.set(E->getFromMO()->getIdx());
- }
- }
- }
- for (auto &Leaf : NewLeaves) {
- // Skip any leaves that don't care about this instruction.
- if (!Leaf.getInstrInfo(InstrID))
- continue;
-
- for (unsigned OpIdx : ReferencedOperands.set_bits()) {
- Leaf.declareOperand(InstrID, OpIdx);
- }
- }
- for (unsigned OpIdx : ReferencedOperands.set_bits()) {
- SubBuilder.addPartitionersForOperand(InstrID, OpIdx);
- }
-}
-
-void GIMatchTreeOpcodePartitioner::emitPartitionResults(
- raw_ostream &OS) const {
- OS << "Partitioning by opcode would produce " << Partitions.size()
- << " partitions\n";
- for (const auto &Partition : InstrToPartition) {
- if (Partition.first == nullptr)
- OS << "Default: ";
- else
- OS << Partition.first->TheDef->getName() << ": ";
- StringRef Separator = "";
- for (unsigned I : Partitions.find(Partition.second)->second.set_bits()) {
- OS << Separator << I;
- Separator = ", ";
- }
- OS << "\n";
- }
-}
-
-void GIMatchTreeOpcodePartitioner::generatePartitionSelectorCode(
- raw_ostream &OS, StringRef Indent) const {
- // Make sure not to emit empty switch or switch with just default
- if (PartitionToInstr.size() == 1 && PartitionToInstr[0] == nullptr) {
- OS << Indent << "Partition = 0;\n";
- } else if (PartitionToInstr.size()) {
- OS << Indent << "Partition = -1;\n"
- << Indent << "switch (MIs[" << InstrID << "]->getOpcode()) {\n";
- for (const auto &EnumInstr : enumerate(PartitionToInstr)) {
- if (EnumInstr.value() == nullptr)
- OS << Indent << "default:";
- else
- OS << Indent << "case " << EnumInstr.value()->Namespace
- << "::" << EnumInstr.value()->TheDef->getName() << ":";
- OS << " Partition = " << EnumInstr.index() << "; break;\n";
- }
- OS << Indent << "}\n";
- }
- OS << Indent
- << "// Default case but without conflicting with potential default case "
- "in selection.\n"
- << Indent << "if (Partition == -1) return false;\n";
-}
-
-void GIMatchTreeVRegDefPartitioner::addToPartition(bool Result,
- unsigned LeafIdx) {
- auto I = ResultToPartition.find(Result);
- if (I == ResultToPartition.end()) {
- ResultToPartition.insert(std::make_pair(Result, PartitionToResult.size()));
- PartitionToResult.push_back(Result);
- }
- I = ResultToPartition.find(Result);
- auto P = Partitions.find(I->second);
- if (P == Partitions.end())
- P = Partitions.insert(std::make_pair(I->second, BitVector())).first;
- P->second.resize(LeafIdx + 1);
- P->second.set(LeafIdx);
-}
-
-void GIMatchTreeVRegDefPartitioner::repartition(
- GIMatchTreeBuilder::LeafVec &Leaves) {
- Partitions.clear();
-
- for (const auto &Leaf : enumerate(Leaves)) {
- GIMatchTreeInstrInfo *InstrInfo = Leaf.value().getInstrInfo(InstrID);
- BitVector TraversedEdgesForLeaf(Leaf.value().getMatchDag().getNumEdges());
-
- // If the instruction isn't declared then we don't care about it. Ignore
- // it for now and add it to all partitions later once we know what
- // partitions we have.
- if (!InstrInfo) {
- TraversedEdges.push_back(TraversedEdgesForLeaf);
- continue;
- }
-
- // If this node has an use -> def edge from this operand then this
- // instruction must be in partition 1 (isVRegDef()).
- bool WantsEdge = false;
- for (unsigned EIdx : Leaf.value().TraversableEdges.set_bits()) {
- const auto &E = Leaf.value().getEdge(EIdx);
- if (E->getFromMI() != InstrInfo->getInstrNode() ||
- E->getFromMO()->getIdx() != OpIdx || E->isDefToUse())
- continue;
-
- // We're looking at the right edge. This leaf wants a vreg def so we'll
- // put it in partition 1.
- addToPartition(true, Leaf.index());
- TraversedEdgesForLeaf.set(EIdx);
- WantsEdge = true;
- }
-
- if (!WantsEdge) {
- // If this leaf doesn't have an edge and we don't know what we want,
- // then add it to partition 0 and 1.
- addToPartition(false, Leaf.index());
- addToPartition(true, Leaf.index());
- }
-
- TraversedEdges.push_back(TraversedEdgesForLeaf);
- }
-
- // Add any leaves that don't care about this instruction to all partitions.
- for (const auto &Leaf : enumerate(Leaves)) {
- GIMatchTreeInstrInfo *InstrInfo = Leaf.value().getInstrInfo(InstrID);
- if (!InstrInfo)
- for (auto &Partition : Partitions) {
- Partition.second.resize(Leaf.index() + 1);
- Partition.second.set(Leaf.index());
- }
- }
-}
-
-void GIMatchTreeVRegDefPartitioner::applyForPartition(
- unsigned PartitionIdx, GIMatchTreeBuilder &Builder,
- GIMatchTreeBuilder &SubBuilder) {
- BitVector PossibleLeaves = getPossibleLeavesForPartition(PartitionIdx);
-
- std::vector<BitVector> TraversedEdgesByNewLeaves;
- // Consume any edges we handled.
- for (const auto &[Index, EnumeratedLeaf] :
- enumerate(Builder.getPossibleLeaves())) {
- if (!PossibleLeaves[Index])
- continue;
-
- const auto &TraversedEdgesForLeaf = TraversedEdges[Index];
- TraversedEdgesByNewLeaves.push_back(TraversedEdgesForLeaf);
- EnumeratedLeaf.RemainingEdges.reset(TraversedEdgesForLeaf);
- EnumeratedLeaf.TraversableEdges.reset(TraversedEdgesForLeaf);
- SubBuilder.addLeaf(EnumeratedLeaf);
- }
-
- // Nothing to do. The only thing we know is that it isn't a vreg-def.
- if (PartitionToResult[PartitionIdx] == false)
- return;
-
- NewInstrID = SubBuilder.allocInstrID();
-
- GIMatchTreeBuilder::LeafVec &NewLeaves = SubBuilder.getPossibleLeaves();
- for (const auto &I : zip(NewLeaves, TraversedEdgesByNewLeaves)) {
- auto &Leaf = std::get<0>(I);
- auto &TraversedEdgesForLeaf = std::get<1>(I);
- GIMatchTreeInstrInfo *InstrInfo = Leaf.getInstrInfo(InstrID);
- // Skip any leaves that don't care about this instruction.
- if (!InstrInfo)
- continue;
- for (unsigned EIdx : TraversedEdgesForLeaf.set_bits()) {
- const GIMatchDagEdge *E = Leaf.getEdge(EIdx);
- Leaf.declareInstr(E->getToMI(), NewInstrID);
- }
- }
- SubBuilder.addPartitionersForInstr(NewInstrID);
-}
-
-void GIMatchTreeVRegDefPartitioner::emitPartitionResults(
- raw_ostream &OS) const {
- OS << "Partitioning by vreg-def would produce " << Partitions.size()
- << " partitions\n";
- for (const auto &Partition : Partitions) {
- OS << Partition.first << " (";
- emitPartitionName(OS, Partition.first);
- OS << "): ";
- StringRef Separator = "";
- for (unsigned I : Partition.second.set_bits()) {
- OS << Separator << I;
- Separator = ", ";
- }
- OS << "\n";
- }
-}
-
-void GIMatchTreeVRegDefPartitioner::generatePartitionSelectorCode(
- raw_ostream &OS, StringRef Indent) const {
- OS << Indent << "Partition = -1;\n"
- << Indent << "if (MIs.size() <= " << NewInstrID << ") MIs.resize("
- << (NewInstrID + 1) << ");\n"
- << Indent << "MIs[" << NewInstrID << "] = nullptr;\n"
- << Indent << "if (MIs[" << InstrID << "]->getOperand(" << OpIdx
- << ").isReg())\n"
- << Indent << " MIs[" << NewInstrID << "] = MRI.getVRegDef(MIs[" << InstrID
- << "]->getOperand(" << OpIdx << ").getReg());\n";
-
- for (const auto &Pair : ResultToPartition)
- OS << Indent << "if (MIs[" << NewInstrID << "] "
- << (Pair.first ? "!=" : "==")
- << " nullptr) Partition = " << Pair.second << ";\n";
-
- OS << Indent << "if (Partition == -1) return false;\n";
-}
diff --git a/llvm/utils/TableGen/GlobalISel/GIMatchTree.h b/llvm/utils/TableGen/GlobalISel/GIMatchTree.h
deleted file mode 100644
index c65423ddacdb..000000000000
--- a/llvm/utils/TableGen/GlobalISel/GIMatchTree.h
+++ /dev/null
@@ -1,626 +0,0 @@
-//===- GIMatchTree.h - A decision tree to match GIMatchDag's --------------===//
-//
-// 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_GIMATCHTREE_H
-#define LLVM_UTILS_TABLEGEN_GIMATCHTREE_H
-
-#include "GIMatchDag.h"
-#include "llvm/ADT/BitVector.h"
-
-namespace llvm {
-class raw_ostream;
-
-class GIMatchTreeBuilder;
-class GIMatchTreePartitioner;
-
-/// Describes the binding of a variable to the matched MIR
-class GIMatchTreeVariableBinding {
- /// The name of the variable described by this binding.
- StringRef Name;
- // The matched instruction it is bound to.
- unsigned InstrID;
- // The matched operand (if appropriate) it is bound to.
- std::optional<unsigned> OpIdx;
-
-public:
- GIMatchTreeVariableBinding(StringRef Name, unsigned InstrID,
- std::optional<unsigned> OpIdx = std::nullopt)
- : Name(Name), InstrID(InstrID), OpIdx(OpIdx) {}
-
- bool isInstr() const { return !OpIdx; }
- StringRef getName() const { return Name; }
- unsigned getInstrID() const { return InstrID; }
- unsigned getOpIdx() const {
- assert(OpIdx && "Is not an operand binding");
- return *OpIdx;
- }
-};
-
-/// Associates a matchable with a leaf of the decision tree.
-class GIMatchTreeLeafInfo {
-public:
- using const_var_binding_iterator =
- std::vector<GIMatchTreeVariableBinding>::const_iterator;
- using UntestedPredicatesTy = SmallVector<const GIMatchDagPredicate *, 1>;
- using const_untested_predicates_iterator = UntestedPredicatesTy::const_iterator;
-
-protected:
- /// A name for the matchable. This is primarily for debugging.
- StringRef Name;
- /// Where rules have multiple roots, this is which root we're starting from.
- unsigned RootIdx;
- /// Opaque data the caller of the tree building code understands.
- void *Data;
- /// Has the decision tree covered every edge traversal? If it hasn't then this
- /// is an unrecoverable error indicating there's something wrong with the
- /// partitioners.
- bool IsFullyTraversed;
- /// Has the decision tree covered every predicate test? If it has, then
- /// subsequent matchables on the same leaf are unreachable. If it hasn't, the
- /// code that requested the GIMatchTree is responsible for finishing off any
- /// remaining predicates.
- bool IsFullyTested;
- /// The variable bindings associated with this leaf so far.
- std::vector<GIMatchTreeVariableBinding> VarBindings;
- /// Any predicates left untested by the time we reach this leaf.
- UntestedPredicatesTy UntestedPredicates;
-
-public:
- GIMatchTreeLeafInfo() { llvm_unreachable("Cannot default-construct"); }
- GIMatchTreeLeafInfo(StringRef Name, unsigned RootIdx, void *Data)
- : Name(Name), RootIdx(RootIdx), Data(Data), IsFullyTraversed(false),
- IsFullyTested(false) {}
-
- StringRef getName() const { return Name; }
- unsigned getRootIdx() const { return RootIdx; }
- template <class Ty> Ty *getTargetData() const {
- return static_cast<Ty *>(Data);
- }
- bool isFullyTraversed() const { return IsFullyTraversed; }
- void setIsFullyTraversed(bool V) { IsFullyTraversed = V; }
- bool isFullyTested() const { return IsFullyTested; }
- void setIsFullyTested(bool V) { IsFullyTested = V; }
-
- void bindInstrVariable(StringRef Name, unsigned InstrID) {
- VarBindings.emplace_back(Name, InstrID);
- }
- void bindOperandVariable(StringRef Name, unsigned InstrID, unsigned OpIdx) {
- VarBindings.emplace_back(Name, InstrID, OpIdx);
- }
-
- const_var_binding_iterator var_bindings_begin() const {
- return VarBindings.begin();
- }
- const_var_binding_iterator var_bindings_end() const {
- return VarBindings.end();
- }
- iterator_range<const_var_binding_iterator> var_bindings() const {
- return make_range(VarBindings.begin(), VarBindings.end());
- }
- iterator_range<const_untested_predicates_iterator> untested_predicates() const {
- return make_range(UntestedPredicates.begin(), UntestedPredicates.end());
- }
- void addUntestedPredicate(const GIMatchDagPredicate *P) {
- UntestedPredicates.push_back(P);
- }
-};
-
-/// The nodes of a decision tree used to perform the match.
-/// This will be used to generate the C++ code or state machine equivalent.
-///
-/// It should be noted that some nodes of this tree (most notably nodes handling
-/// def -> use edges) will need to iterate over several possible matches. As
-/// such, code generated from this will sometimes need to support backtracking.
-class GIMatchTree {
- using LeafVector = std::vector<GIMatchTreeLeafInfo>;
-
- /// The partitioner that has been chosen for this node. This may be nullptr if
- /// a partitioner hasn't been chosen yet or if the node is a leaf.
- std::unique_ptr<GIMatchTreePartitioner> Partitioner;
- /// All the leaves that are possible for this node of the tree.
- /// Note: This should be emptied after the tree is built when there are
- /// children but this currently isn't done to aid debuggability of the DOT
- /// graph for the decision tree.
- LeafVector PossibleLeaves;
- /// The children of this node. The index into this array must match the index
- /// chosen by the partitioner.
- std::vector<GIMatchTree> Children;
-
- void writeDOTGraphNode(raw_ostream &OS) const;
- void writeDOTGraphEdges(raw_ostream &OS) const;
-
-public:
- void writeDOTGraph(raw_ostream &OS) const;
-
- void setNumChildren(unsigned Num) { Children.resize(Num); }
- void addPossibleLeaf(const GIMatchTreeLeafInfo &V, bool IsFullyTraversed,
- bool IsFullyTested) {
- PossibleLeaves.push_back(V);
- PossibleLeaves.back().setIsFullyTraversed(IsFullyTraversed);
- PossibleLeaves.back().setIsFullyTested(IsFullyTested);
- }
- void dropLeavesAfter(size_t Length) {
- if (PossibleLeaves.size() > Length)
- PossibleLeaves.resize(Length);
- }
- void setPartitioner(std::unique_ptr<GIMatchTreePartitioner> &&V) {
- Partitioner = std::move(V);
- }
- GIMatchTreePartitioner *getPartitioner() const { return Partitioner.get(); }
-
- std::vector<GIMatchTree>::iterator children_begin() {
- return Children.begin();
- }
- std::vector<GIMatchTree>::iterator children_end() { return Children.end(); }
- iterator_range<std::vector<GIMatchTree>::iterator> children() {
- return make_range(children_begin(), children_end());
- }
- std::vector<GIMatchTree>::const_iterator children_begin() const {
- return Children.begin();
- }
- std::vector<GIMatchTree>::const_iterator children_end() const {
- return Children.end();
- }
- iterator_range<std::vector<GIMatchTree>::const_iterator> children() const {
- return make_range(children_begin(), children_end());
- }
-
- LeafVector::const_iterator possible_leaves_begin() const {
- return PossibleLeaves.begin();
- }
- LeafVector::const_iterator possible_leaves_end() const {
- return PossibleLeaves.end();
- }
- iterator_range<LeafVector::const_iterator>
- possible_leaves() const {
- return make_range(possible_leaves_begin(), possible_leaves_end());
- }
- LeafVector::iterator possible_leaves_begin() {
- return PossibleLeaves.begin();
- }
- LeafVector::iterator possible_leaves_end() {
- return PossibleLeaves.end();
- }
- iterator_range<LeafVector::iterator> possible_leaves() {
- return make_range(possible_leaves_begin(), possible_leaves_end());
- }
-};
-
-/// Record information that is known about the instruction bound to this ID and
-/// GIMatchDagInstrNode. Every rule gets its own set of
-/// GIMatchTreeInstrInfo to bind the shared IDs to an instr node in its
-/// DAG.
-///
-/// For example, if we know that there are 3 operands. We can record it here to
-/// elide duplicate checks.
-class GIMatchTreeInstrInfo {
- /// The instruction ID for the matched instruction.
- unsigned ID;
- /// The corresponding instruction node in the MatchDAG.
- const GIMatchDagInstr *InstrNode;
-
-public:
- GIMatchTreeInstrInfo(unsigned ID, const GIMatchDagInstr *InstrNode)
- : ID(ID), InstrNode(InstrNode) {}
-
- unsigned getID() const { return ID; }
- const GIMatchDagInstr *getInstrNode() const { return InstrNode; }
-};
-
-/// Record information that is known about the operand bound to this ID, OpIdx,
-/// and GIMatchDagInstrNode. Every rule gets its own set of
-/// GIMatchTreeOperandInfo to bind the shared IDs to an operand of an
-/// instr node from its DAG.
-///
-/// For example, if we know that there the operand is a register. We can record
-/// it here to elide duplicate checks.
-class GIMatchTreeOperandInfo {
- /// The corresponding instruction node in the MatchDAG that the operand
- /// belongs to.
- const GIMatchDagInstr *InstrNode;
- unsigned OpIdx;
-
-public:
- GIMatchTreeOperandInfo(const GIMatchDagInstr *InstrNode, unsigned OpIdx)
- : InstrNode(InstrNode), OpIdx(OpIdx) {}
-
- const GIMatchDagInstr *getInstrNode() const { return InstrNode; }
- unsigned getOpIdx() const { return OpIdx; }
-};
-
-/// Represent a leaf of the match tree and any working data we need to build the
-/// tree.
-///
-/// It's important to note that each rule can have multiple
-/// GIMatchTreeBuilderLeafInfo's since the partitioners do not always partition
-/// into mutually-exclusive partitions. For example:
-/// R1: (FOO ..., ...)
-/// R2: (oneof(FOO, BAR) ..., ...)
-/// will partition by opcode into two partitions FOO=>[R1, R2], and BAR=>[R2]
-///
-/// As an optimization, all instructions, edges, and predicates in the DAGs are
-/// numbered and tracked in BitVectors. As such, the GIMatchDAG must not be
-/// modified once construction of the tree has begun.
-class GIMatchTreeBuilderLeafInfo {
-protected:
- GIMatchTreeBuilder &Builder;
- GIMatchTreeLeafInfo Info;
- const GIMatchDag &MatchDag;
- /// The association between GIMatchDagInstr* and GIMatchTreeInstrInfo.
- /// The primary reason for this members existence is to allow the use of
- /// InstrIDToInfo.lookup() since that requires that the value is
- /// default-constructible.
- DenseMap<const GIMatchDagInstr *, GIMatchTreeInstrInfo> InstrNodeToInfo;
- /// The instruction information for a given ID in the context of this
- /// particular leaf.
- DenseMap<unsigned, GIMatchTreeInstrInfo *> InstrIDToInfo;
- /// The operand information for a given ID and OpIdx in the context of this
- /// particular leaf.
- DenseMap<std::pair<unsigned, unsigned>, GIMatchTreeOperandInfo>
- OperandIDToInfo;
-
-public:
- /// The remaining instrs/edges/predicates to visit
- BitVector RemainingInstrNodes;
- BitVector RemainingEdges;
- BitVector RemainingPredicates;
-
- // The remaining predicate dependencies for each predicate
- std::vector<BitVector> UnsatisfiedPredDepsForPred;
-
- /// The edges/predicates we can visit as a result of the declare*() calls we
- /// have already made. We don't need an instrs version since edges imply the
- /// instr.
- BitVector TraversableEdges;
- BitVector TestablePredicates;
-
- /// Map predicates from the DAG to their position in the DAG predicate
- /// iterators.
- DenseMap<GIMatchDagPredicate *, unsigned> PredicateIDs;
- /// Map predicate dependency edges from the DAG to their position in the DAG
- /// predicate dependency iterators.
- DenseMap<GIMatchDagPredicateDependencyEdge *, unsigned> PredicateDepIDs;
-
-public:
- GIMatchTreeBuilderLeafInfo(GIMatchTreeBuilder &Builder, StringRef Name,
- unsigned RootIdx, const GIMatchDag &MatchDag,
- void *Data);
-
- StringRef getName() const { return Info.getName(); }
- GIMatchTreeLeafInfo &getInfo() { return Info; }
- const GIMatchTreeLeafInfo &getInfo() const { return Info; }
- const GIMatchDag &getMatchDag() const { return MatchDag; }
- unsigned getRootIdx() const { return Info.getRootIdx(); }
-
- /// Has this DAG been fully traversed. This must be true by the time the tree
- /// builder finishes.
- bool isFullyTraversed() const {
- // We don't need UnsatisfiedPredDepsForPred because RemainingPredicates
- // can't be all-zero without satisfying all the dependencies. The same is
- // almost true for Edges and Instrs but it's possible to have Instrs without
- // Edges.
- return RemainingInstrNodes.none() && RemainingEdges.none();
- }
-
- /// Has this DAG been fully tested. This hould be true by the time the tree
- /// builder finishes but clients can finish any untested predicates left over
- /// if it's not true.
- bool isFullyTested() const {
- // We don't need UnsatisfiedPredDepsForPred because RemainingPredicates
- // can't be all-zero without satisfying all the dependencies. The same is
- // almost true for Edges and Instrs but it's possible to have Instrs without
- // Edges.
- return RemainingInstrNodes.none() && RemainingEdges.none() &&
- RemainingPredicates.none();
- }
-
- const GIMatchDagInstr *getInstr(unsigned Idx) const {
- return *(MatchDag.instr_nodes_begin() + Idx);
- }
- const GIMatchDagEdge *getEdge(unsigned Idx) const {
- return *(MatchDag.edges_begin() + Idx);
- }
- GIMatchDagEdge *getEdge(unsigned Idx) {
- return *(MatchDag.edges_begin() + Idx);
- }
- const GIMatchDagPredicate *getPredicate(unsigned Idx) const {
- return *(MatchDag.predicates_begin() + Idx);
- }
- iterator_range<llvm::BitVector::const_set_bits_iterator>
- untested_instrs() const {
- return RemainingInstrNodes.set_bits();
- }
- iterator_range<llvm::BitVector::const_set_bits_iterator>
- untested_edges() const {
- return RemainingEdges.set_bits();
- }
- iterator_range<llvm::BitVector::const_set_bits_iterator>
- untested_predicates() const {
- return RemainingPredicates.set_bits();
- }
-
- /// Bind an instr node to the given ID and clear any blocking dependencies
- /// that were waiting for it.
- void declareInstr(const GIMatchDagInstr *Instr, unsigned ID);
-
- /// Bind an operand to the given ID and OpIdx and clear any blocking
- /// dependencies that were waiting for it.
- void declareOperand(unsigned InstrID, unsigned OpIdx);
-
- GIMatchTreeInstrInfo *getInstrInfo(unsigned ID) const {
- return InstrIDToInfo.lookup(ID);
- }
-
- void dump(raw_ostream &OS) const {
- OS << "Leaf " << getName() << " for root #" << getRootIdx() << "\n";
- MatchDag.print(OS);
- for (const auto &I : InstrIDToInfo)
- OS << "Declared Instr #" << I.first << "\n";
- for (const auto &I : OperandIDToInfo)
- OS << "Declared Instr #" << I.first.first << ", Op #" << I.first.second
- << "\n";
- OS << RemainingInstrNodes.count() << " untested instrs of "
- << RemainingInstrNodes.size() << "\n";
- OS << RemainingEdges.count() << " untested edges of "
- << RemainingEdges.size() << "\n";
- OS << RemainingPredicates.count() << " untested predicates of "
- << RemainingPredicates.size() << "\n";
-
- OS << TraversableEdges.count() << " edges could be traversed\n";
- OS << TestablePredicates.count() << " predicates could be tested\n";
- }
-};
-
-/// The tree builder has a fairly tough job. It's purpose is to merge all the
-/// DAGs from the ruleset into a decision tree that walks all of them
-/// simultaneously and identifies the rule that was matched. In addition to
-/// that, it also needs to find the most efficient order to make decisions
-/// without violating any dependencies and ensure that every DAG covers every
-/// instr/edge/predicate.
-class GIMatchTreeBuilder {
-public:
- using LeafVec = std::vector<GIMatchTreeBuilderLeafInfo>;
-
-protected:
- /// The leaves that the resulting decision tree will distinguish.
- LeafVec Leaves;
- /// The tree node being constructed.
- GIMatchTree *TreeNode = nullptr;
- /// The builders for each subtree resulting from the current decision.
- std::vector<GIMatchTreeBuilder> SubtreeBuilders;
- /// The possible partitioners we could apply right now.
- std::vector<std::unique_ptr<GIMatchTreePartitioner>> Partitioners;
- /// The next instruction ID to allocate when requested by the chosen
- /// Partitioner.
- unsigned NextInstrID;
-
- /// Use any context we have stored to cull partitioners that only test things
- /// we already know. At the time of writing, there's no need to do anything
- /// here but it will become important once, for example, there is a
- /// num-operands and an opcode partitioner. This is because applying an opcode
- /// partitioner (usually) makes the number of operands known which makes
- /// additional checking pointless.
- void filterRedundantPartitioners();
-
- /// Evaluate the available partioners and select the best one at the moment.
- void evaluatePartitioners();
-
- /// Construct the current tree node.
- void runStep();
-
-public:
- GIMatchTreeBuilder(unsigned NextInstrID) : NextInstrID(NextInstrID) {}
- GIMatchTreeBuilder(GIMatchTree *TreeNode, unsigned NextInstrID)
- : TreeNode(TreeNode), NextInstrID(NextInstrID) {}
-
- void addLeaf(StringRef Name, unsigned RootIdx, const GIMatchDag &MatchDag,
- void *Data) {
- Leaves.emplace_back(*this, Name, RootIdx, MatchDag, Data);
- }
- void addLeaf(const GIMatchTreeBuilderLeafInfo &L) { Leaves.push_back(L); }
- void addPartitioner(std::unique_ptr<GIMatchTreePartitioner> P) {
- Partitioners.push_back(std::move(P));
- }
- void addPartitionersForInstr(unsigned InstrIdx);
- void addPartitionersForOperand(unsigned InstrID, unsigned OpIdx);
-
- LeafVec &getPossibleLeaves() { return Leaves; }
-
- unsigned allocInstrID() { return NextInstrID++; }
-
- /// Construct the decision tree.
- std::unique_ptr<GIMatchTree> run();
-};
-
-/// Partitioners are the core of the tree builder and are unfortunately rather
-/// tricky to write.
-class GIMatchTreePartitioner {
-protected:
- /// The partitions resulting from applying the partitioner to the possible
- /// leaves. The keys must be consecutive integers starting from 0. This can
- /// lead to some unfortunate situations where partitioners test a predicate
- /// and use 0 for success and 1 for failure if the ruleset encounters a
- /// success case first but is necessary to assign the partition to one of the
- /// tree nodes children. As a result, you usually need some kind of
- /// indirection to map the natural keys (e.g. ptrs/bools) to this linear
- /// sequence. The values are a bitvector indicating which leaves belong to
- /// this partition.
- DenseMap<unsigned, BitVector> Partitions;
-
-public:
- virtual ~GIMatchTreePartitioner() {}
- virtual std::unique_ptr<GIMatchTreePartitioner> clone() const = 0;
-
- /// Determines which partitions the given leaves belong to. A leaf may belong
- /// to multiple partitions in which case it will be duplicated during
- /// applyForPartition().
- ///
- /// This function can be rather complicated. A few particular things to be
- /// aware of include:
- /// * One leaf can be assigned to multiple partitions when there's some
- /// ambiguity.
- /// * Not all DAG's for the leaves may be able to perform the test. For
- /// example, the opcode partitiioner must account for one DAG being a
- /// superset of another such as [(ADD ..., ..., ...)], and [(MUL t, ...,
- /// ...), (ADD ..., t, ...)]
- /// * Attaching meaning to a particular partition index will generally not
- /// work due to the '0, 1, ..., n' requirement. You might encounter cases
- /// where only partition 1 is seen, leaving a missing 0.
- /// * Finding a specific predicate such as the opcode predicate for a specific
- /// instruction is non-trivial. It's often O(NumPredicates), leading to
- /// O(NumPredicates*NumRules) when applied to the whole ruleset. The good
- /// news there is that n is typically small thanks to predicate dependencies
- /// limiting how many are testable at once. Also, with opcode and type
- /// predicates being so frequent the value of m drops very fast too. It
- /// wouldn't be terribly surprising to see a 10k ruleset drop down to an
- /// average of 100 leaves per partition after a single opcode partitioner.
- /// * The same goes for finding specific edges. The need to traverse them in
- /// dependency order dramatically limits the search space at any given
- /// moment.
- /// * If you need to add a leaf to all partitions, make sure you don't forget
- /// them when adding partitions later.
- virtual void repartition(GIMatchTreeBuilder::LeafVec &Leaves) = 0;
-
- /// Delegate the leaves for a given partition to the corresponding subbuilder,
- /// update any recorded context for this partition (e.g. allocate instr id's
- /// for instrs recorder by the current node), and clear any blocking
- /// dependencies this partitioner resolved.
- virtual void applyForPartition(unsigned PartitionIdx,
- GIMatchTreeBuilder &Builder,
- GIMatchTreeBuilder &SubBuilder) = 0;
-
- /// Return a BitVector indicating which leaves should be transferred to the
- /// specified partition. Note that the same leaf can be indicated for multiple
- /// partitions.
- BitVector getPossibleLeavesForPartition(unsigned Idx) {
- const auto &I = Partitions.find(Idx);
- assert(I != Partitions.end() && "Requested non-existant partition");
- return I->second;
- }
-
- size_t getNumPartitions() const { return Partitions.size(); }
- size_t getNumLeavesWithDupes() const {
- size_t S = 0;
- for (const auto &P : Partitions)
- S += P.second.size();
- return S;
- }
-
- /// Emit a brief description of the partitioner suitable for debug printing or
- /// use in a DOT graph.
- virtual void emitDescription(raw_ostream &OS) const = 0;
- /// Emit a label for the given partition suitable for debug printing or use in
- /// a DOT graph.
- virtual void emitPartitionName(raw_ostream &OS, unsigned Idx) const = 0;
-
- /// Emit a long description of how the partitioner partitions the leaves.
- virtual void emitPartitionResults(raw_ostream &OS) const = 0;
-
- /// Generate code to select between partitions based on the MIR being matched.
- /// This is typically a switch statement that picks a partition index.
- virtual void generatePartitionSelectorCode(raw_ostream &OS,
- StringRef Indent) const = 0;
-};
-
-/// Partition according to the opcode of the instruction.
-///
-/// Numbers CodeGenInstr ptrs for use as partition ID's. One special partition,
-/// nullptr, represents the case where the instruction isn't known.
-///
-/// * If the opcode can be tested and is a single opcode, create the partition
-/// for that opcode and assign the leaf to it. This partition no longer needs
-/// to test the opcode, and many details about the instruction will usually
-/// become known (e.g. number of operands for non-variadic instrs) via the
-/// CodeGenInstr ptr.
-/// * (not implemented yet) If the opcode can be tested and is a choice of
-/// opcodes, then the leaf can be treated like the single-opcode case but must
-/// be added to all relevant partitions and not quite as much becomes known as
-/// a result. That said, multiple-choice opcodes are likely similar enough
-/// (because if they aren't then handling them together makes little sense)
-/// that plenty still becomes known. The main implementation issue with this
-/// is having a description to represent the commonality between instructions.
-/// * If the opcode is not tested, the leaf must be added to all partitions
-/// including the wildcard nullptr partition. What becomes known as a result
-/// varies between partitions.
-/// * If the instruction to be tested is not declared then add the leaf to all
-/// partitions. This occurs when we encounter one rule that is a superset of
-/// the other and we are still matching the remainder of the superset. The
-/// result is that the cases that don't match the superset will match the
-/// subset rule, while the ones that do match the superset will match either
-/// (which one is algorithm dependent but will usually be the superset).
-class GIMatchTreeOpcodePartitioner : public GIMatchTreePartitioner {
- unsigned InstrID;
- DenseMap<const CodeGenInstruction *, unsigned> InstrToPartition;
- std::vector<const CodeGenInstruction *> PartitionToInstr;
- std::vector<BitVector> TestedPredicates;
-
-public:
- GIMatchTreeOpcodePartitioner(unsigned InstrID) : InstrID(InstrID) {}
-
- std::unique_ptr<GIMatchTreePartitioner> clone() const override {
- return std::make_unique<GIMatchTreeOpcodePartitioner>(*this);
- }
-
- void emitDescription(raw_ostream &OS) const override {
- OS << "MI[" << InstrID << "].getOpcode()";
- }
-
- void emitPartitionName(raw_ostream &OS, unsigned Idx) const override;
-
- void repartition(GIMatchTreeBuilder::LeafVec &Leaves) override;
- void applyForPartition(unsigned Idx, GIMatchTreeBuilder &SubBuilder,
- GIMatchTreeBuilder &Builder) override;
-
- void emitPartitionResults(raw_ostream &OS) const override;
-
- void generatePartitionSelectorCode(raw_ostream &OS,
- StringRef Indent) const override;
-};
-
-class GIMatchTreeVRegDefPartitioner : public GIMatchTreePartitioner {
- unsigned NewInstrID = -1;
- unsigned InstrID;
- unsigned OpIdx;
- std::vector<BitVector> TraversedEdges;
- DenseMap<unsigned, unsigned> ResultToPartition;
- BitVector PartitionToResult;
-
- void addToPartition(bool Result, unsigned LeafIdx);
-
-public:
- GIMatchTreeVRegDefPartitioner(unsigned InstrID, unsigned OpIdx)
- : InstrID(InstrID), OpIdx(OpIdx) {}
-
- std::unique_ptr<GIMatchTreePartitioner> clone() const override {
- return std::make_unique<GIMatchTreeVRegDefPartitioner>(*this);
- }
-
- void emitDescription(raw_ostream &OS) const override {
- OS << "MI[" << NewInstrID << "] = getVRegDef(MI[" << InstrID
- << "].getOperand(" << OpIdx << "))";
- }
-
- void emitPartitionName(raw_ostream &OS, unsigned Idx) const override {
- bool Result = PartitionToResult[Idx];
- if (Result)
- OS << "true";
- else
- OS << "false";
- }
-
- void repartition(GIMatchTreeBuilder::LeafVec &Leaves) override;
- void applyForPartition(unsigned PartitionIdx, GIMatchTreeBuilder &Builder,
- GIMatchTreeBuilder &SubBuilder) override;
- void emitPartitionResults(raw_ostream &OS) const override;
-
- void generatePartitionSelectorCode(raw_ostream &OS,
- StringRef Indent) const override;
-};
-
-} // end namespace llvm
-#endif // ifndef LLVM_UTILS_TABLEGEN_GIMATCHTREE_H
diff --git a/llvm/utils/TableGen/GlobalISel/MatchDataInfo.cpp b/llvm/utils/TableGen/GlobalISel/MatchDataInfo.cpp
new file mode 100644
index 000000000000..b5c9e4f8c248
--- /dev/null
+++ b/llvm/utils/TableGen/GlobalISel/MatchDataInfo.cpp
@@ -0,0 +1,49 @@
+//===- MatchDataInfo.cpp ----------------------------------------*- C++ -*-===//
+//
+// 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 "MatchDataInfo.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace gi {
+
+StringMap<std::vector<std::string>> AllMatchDataVars;
+
+StringRef MatchDataInfo::getVariableName() const {
+ assert(hasVariableName());
+ return VarName;
+}
+
+void MatchDataInfo::print(raw_ostream &OS) const {
+ OS << "(MatchDataInfo pattern_symbol:" << PatternSymbol << " type:'" << Type
+ << "' var_name:" << (VarName.empty() ? "<unassigned>" : VarName) << ")";
+}
+
+void MatchDataInfo::dump() const { print(dbgs()); }
+
+void AssignMatchDataVariables(MutableArrayRef<MatchDataInfo> Infos) {
+ static unsigned NextVarID = 0;
+
+ StringMap<unsigned> SeenTypes;
+ for (auto &Info : Infos) {
+ unsigned &NumSeen = SeenTypes[Info.getType()];
+ auto &ExistingVars = AllMatchDataVars[Info.getType()];
+
+ if (NumSeen == ExistingVars.size())
+ ExistingVars.push_back("MDInfo" + std::to_string(NextVarID++));
+
+ Info.setVariableName(ExistingVars[NumSeen++]);
+ }
+}
+
+} // namespace gi
+} // namespace llvm
diff --git a/llvm/utils/TableGen/GlobalISel/MatchDataInfo.h b/llvm/utils/TableGen/GlobalISel/MatchDataInfo.h
new file mode 100644
index 000000000000..abe1245bc67d
--- /dev/null
+++ b/llvm/utils/TableGen/GlobalISel/MatchDataInfo.h
@@ -0,0 +1,90 @@
+//===- MatchDataInfo.h ------------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file Contains utilities related to handling "match data" for GlobalISel
+/// Combiners. Match data allows for setting some arbitrary data in the "match"
+/// phase and pass it down to the "apply" phase.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_UTILS_MIRPATTERNS_MATCHDATAINFO_H
+#define LLVM_UTILS_MIRPATTERNS_MATCHDATAINFO_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <vector>
+
+namespace llvm {
+
+class raw_ostream;
+
+namespace gi {
+
+/// Represents MatchData defined by the match stage and required by the apply
+/// stage.
+///
+/// This allows the plumbing of arbitrary data from C++ predicates between the
+/// stages.
+///
+/// When this class is initially created, it only has a pattern symbol and a
+/// type. When all of the MatchDatas declarations of a given pattern have been
+/// parsed, `AssignVariables` must be called to assign storage variable names to
+/// each MatchDataInfo.
+class MatchDataInfo {
+ StringRef PatternSymbol;
+ StringRef Type;
+ std::string VarName;
+
+public:
+ static constexpr StringLiteral StructTypeName = "MatchInfosTy";
+ static constexpr StringLiteral StructName = "MatchInfos";
+
+ MatchDataInfo(StringRef PatternSymbol, StringRef Type)
+ : PatternSymbol(PatternSymbol), Type(Type.trim()) {}
+
+ StringRef getPatternSymbol() const { return PatternSymbol; };
+ StringRef getType() const { return Type; };
+
+ bool hasVariableName() const { return !VarName.empty(); }
+ void setVariableName(StringRef Name) { VarName = Name; }
+ StringRef getVariableName() const;
+
+ std::string getQualifiedVariableName() const {
+ return StructName.str() + "." + getVariableName().str();
+ }
+
+ void print(raw_ostream &OS) const;
+ void dump() const;
+};
+
+/// Pool of type -> variables used to emit MatchData variables declarations.
+///
+/// e.g. if the map contains "int64_t" -> ["MD0", "MD1"], then two variable
+/// declarations must be emitted: `int64_t MD0` and `int64_t MD1`.
+///
+/// This has a static lifetime and will outlive all the `MatchDataInfo` objects
+/// by design. It needs a static lifetime so the backends can emit variable
+/// declarations after processing all the inputs.
+extern StringMap<std::vector<std::string>> AllMatchDataVars;
+
+/// Assign variable names to all MatchDatas used by a pattern. This must be
+/// called after all MatchData decls have been parsed for a given processing
+/// unit (e.g. a combine rule)
+///
+/// Requires an array of MatchDataInfo so we can handle cases where a pattern
+/// uses multiple instances of the same MatchData type.
+///
+/// Writes to \ref AllMatchDataVars.
+void AssignMatchDataVariables(MutableArrayRef<MatchDataInfo> Infos);
+
+} // namespace gi
+} // end namespace llvm
+
+#endif // ifndef LLVM_UTILS_MIRPATTERNS_MATCHDATAINFO_H
diff --git a/llvm/utils/TableGen/GlobalISel/Patterns.cpp b/llvm/utils/TableGen/GlobalISel/Patterns.cpp
new file mode 100644
index 000000000000..0a6d05e06dca
--- /dev/null
+++ b/llvm/utils/TableGen/GlobalISel/Patterns.cpp
@@ -0,0 +1,842 @@
+//===- Patterns.cpp --------------------------------------------*- C++ -*-===//
+//
+// 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 "Patterns.h"
+#include "../CodeGenInstruction.h"
+#include "CXXPredicates.h"
+#include "CodeExpander.h"
+#include "CodeExpansions.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Record.h"
+
+namespace llvm {
+namespace gi {
+
+//===- PatternType --------------------------------------------------------===//
+
+std::optional<PatternType> PatternType::get(ArrayRef<SMLoc> DiagLoc,
+ const Record *R, Twine DiagCtx) {
+ assert(R);
+ if (R->isSubClassOf("ValueType")) {
+ PatternType PT(PT_ValueType);
+ PT.Data.Def = R;
+ return PT;
+ }
+
+ if (R->isSubClassOf(TypeOfClassName)) {
+ auto RawOpName = R->getValueAsString("OpName");
+ if (!RawOpName.starts_with("$")) {
+ PrintError(DiagLoc, DiagCtx + ": invalid operand name format '" +
+ RawOpName + "' in " + TypeOfClassName +
+ ": expected '$' followed by an operand name");
+ return std::nullopt;
+ }
+
+ PatternType PT(PT_TypeOf);
+ PT.Data.Str = RawOpName.drop_front(1);
+ return PT;
+ }
+
+ PrintError(DiagLoc, DiagCtx + ": unknown type '" + R->getName() + "'");
+ return std::nullopt;
+}
+
+PatternType PatternType::getTypeOf(StringRef OpName) {
+ PatternType PT(PT_TypeOf);
+ PT.Data.Str = OpName;
+ return PT;
+}
+
+StringRef PatternType::getTypeOfOpName() const {
+ assert(isTypeOf());
+ return Data.Str;
+}
+
+const Record *PatternType::getLLTRecord() const {
+ assert(isLLT());
+ return Data.Def;
+}
+
+bool PatternType::operator==(const PatternType &Other) const {
+ if (Kind != Other.Kind)
+ return false;
+
+ switch (Kind) {
+ case PT_None:
+ return true;
+ case PT_ValueType:
+ return Data.Def == Other.Data.Def;
+ case PT_TypeOf:
+ return Data.Str == Other.Data.Str;
+ }
+
+ llvm_unreachable("Unknown Type Kind");
+}
+
+std::string PatternType::str() const {
+ switch (Kind) {
+ case PT_None:
+ return "";
+ case PT_ValueType:
+ return Data.Def->getName().str();
+ case PT_TypeOf:
+ return (TypeOfClassName + "<$" + getTypeOfOpName() + ">").str();
+ }
+
+ llvm_unreachable("Unknown type!");
+}
+
+//===- Pattern ------------------------------------------------------------===//
+
+void Pattern::dump() const { return print(dbgs()); }
+
+const char *Pattern::getKindName() const {
+ switch (Kind) {
+ case K_AnyOpcode:
+ return "AnyOpcodePattern";
+ case K_CXX:
+ return "CXXPattern";
+ case K_CodeGenInstruction:
+ return "CodeGenInstructionPattern";
+ case K_PatFrag:
+ return "PatFragPattern";
+ case K_Builtin:
+ return "BuiltinPattern";
+ }
+
+ llvm_unreachable("unknown pattern kind!");
+}
+
+void Pattern::printImpl(raw_ostream &OS, bool PrintName,
+ function_ref<void()> ContentPrinter) const {
+ OS << "(" << getKindName() << " ";
+ if (PrintName)
+ OS << "name:" << getName() << " ";
+ ContentPrinter();
+ OS << ")";
+}
+
+//===- AnyOpcodePattern ---------------------------------------------------===//
+
+void AnyOpcodePattern::print(raw_ostream &OS, bool PrintName) const {
+ printImpl(OS, PrintName, [&OS, this]() {
+ OS << "["
+ << join(map_range(Insts,
+ [](const auto *I) { return I->TheDef->getName(); }),
+ ", ")
+ << "]";
+ });
+}
+
+//===- CXXPattern ---------------------------------------------------------===//
+
+CXXPattern::CXXPattern(const StringInit &Code, StringRef Name)
+ : CXXPattern(Code.getAsUnquotedString(), Name) {}
+
+const CXXPredicateCode &
+CXXPattern::expandCode(const CodeExpansions &CE, ArrayRef<SMLoc> Locs,
+ function_ref<void(raw_ostream &)> AddComment) const {
+ std::string Result;
+ raw_string_ostream OS(Result);
+
+ if (AddComment)
+ AddComment(OS);
+
+ CodeExpander Expander(RawCode, CE, Locs, /*ShowExpansions*/ false);
+ Expander.emit(OS);
+ if (IsApply)
+ return CXXPredicateCode::getApplyCode(std::move(Result));
+ return CXXPredicateCode::getMatchCode(std::move(Result));
+}
+
+void CXXPattern::print(raw_ostream &OS, bool PrintName) const {
+ printImpl(OS, PrintName, [&OS, this] {
+ OS << (IsApply ? "apply" : "match") << " code:\"";
+ printEscapedString(getRawCode(), OS);
+ OS << "\"";
+ });
+}
+
+//===- InstructionOperand -------------------------------------------------===//
+
+std::string InstructionOperand::describe() const {
+ if (!hasImmValue())
+ return "MachineOperand $" + getOperandName().str() + "";
+ std::string Str = "imm " + std::to_string(getImmValue());
+ if (isNamedImmediate())
+ Str += ":$" + getOperandName().str() + "";
+ return Str;
+}
+
+void InstructionOperand::print(raw_ostream &OS) const {
+ if (isDef())
+ OS << "<def>";
+
+ bool NeedsColon = true;
+ if (Type) {
+ if (hasImmValue())
+ OS << "(" << Type.str() << " " << getImmValue() << ")";
+ else
+ OS << Type.str();
+ } else if (hasImmValue())
+ OS << getImmValue();
+ else
+ NeedsColon = false;
+
+ if (isNamedOperand())
+ OS << (NeedsColon ? ":" : "") << "$" << getOperandName();
+}
+
+void InstructionOperand::dump() const { return print(dbgs()); }
+
+//===- InstructionPattern -------------------------------------------------===//
+
+bool InstructionPattern::diagnoseAllSpecialTypes(ArrayRef<SMLoc> Loc,
+ Twine Msg) const {
+ bool HasDiag = false;
+ for (const auto &[Idx, Op] : enumerate(operands())) {
+ if (Op.getType().isSpecial()) {
+ PrintError(Loc, Msg);
+ PrintNote(Loc, "operand " + Twine(Idx) + " of '" + getName() +
+ "' has type '" + Op.getType().str() + "'");
+ HasDiag = true;
+ }
+ }
+ return HasDiag;
+}
+
+void InstructionPattern::reportUnreachable(ArrayRef<SMLoc> Locs) const {
+ PrintError(Locs, "pattern '" + getName() + "' ('" + getInstName() +
+ "') is unreachable from the pattern root!");
+}
+
+bool InstructionPattern::checkSemantics(ArrayRef<SMLoc> Loc) {
+ unsigned NumExpectedOperands = getNumInstOperands();
+
+ if (isVariadic()) {
+ if (Operands.size() < NumExpectedOperands) {
+ PrintError(Loc, +"'" + getInstName() + "' expected at least " +
+ Twine(NumExpectedOperands) + " operands, got " +
+ Twine(Operands.size()));
+ return false;
+ }
+ } else if (NumExpectedOperands != Operands.size()) {
+ PrintError(Loc, +"'" + getInstName() + "' expected " +
+ Twine(NumExpectedOperands) + " operands, got " +
+ Twine(Operands.size()));
+ return false;
+ }
+
+ unsigned OpIdx = 0;
+ unsigned NumDefs = getNumInstDefs();
+ for (auto &Op : Operands)
+ Op.setIsDef(OpIdx++ < NumDefs);
+
+ return true;
+}
+
+void InstructionPattern::print(raw_ostream &OS, bool PrintName) const {
+ printImpl(OS, PrintName, [&OS, this] {
+ OS << getInstName() << " operands:[";
+ StringRef Sep;
+ for (const auto &Op : Operands) {
+ OS << Sep;
+ Op.print(OS);
+ Sep = ", ";
+ }
+ OS << "]";
+
+ printExtras(OS);
+ });
+}
+
+//===- OperandTable -------------------------------------------------------===//
+
+bool OperandTable::addPattern(InstructionPattern *P,
+ function_ref<void(StringRef)> DiagnoseRedef) {
+ for (const auto &Op : P->named_operands()) {
+ StringRef OpName = Op.getOperandName();
+
+ // We always create an entry in the OperandTable, even for uses.
+ // Uses of operands that don't have a def (= live-ins) will remain with a
+ // nullptr as the Def.
+ //
+ // This allows us tell whether an operand exists in a pattern or not. If
+ // there is no entry for it, it doesn't exist, if there is an entry, it's
+ // used/def'd at least once.
+ auto &Def = Table[OpName];
+
+ if (!Op.isDef())
+ continue;
+
+ if (Def) {
+ DiagnoseRedef(OpName);
+ return false;
+ }
+
+ Def = P;
+ }
+
+ return true;
+}
+
+void OperandTable::print(raw_ostream &OS, StringRef Name,
+ StringRef Indent) const {
+ OS << Indent << "(OperandTable ";
+ if (!Name.empty())
+ OS << Name << " ";
+ if (Table.empty()) {
+ OS << "<empty>)\n";
+ return;
+ }
+
+ SmallVector<StringRef, 0> Keys(Table.keys());
+ sort(Keys);
+
+ OS << '\n';
+ for (const auto &Key : Keys) {
+ const auto *Def = Table.at(Key);
+ OS << Indent << " " << Key << " -> "
+ << (Def ? Def->getName() : "<live-in>") << '\n';
+ }
+ OS << Indent << ")\n";
+}
+
+void OperandTable::dump() const { print(dbgs()); }
+
+//===- MIFlagsInfo --------------------------------------------------------===//
+
+void MIFlagsInfo::addSetFlag(const Record *R) {
+ SetF.insert(R->getValueAsString("EnumName"));
+}
+
+void MIFlagsInfo::addUnsetFlag(const Record *R) {
+ UnsetF.insert(R->getValueAsString("EnumName"));
+}
+
+void MIFlagsInfo::addCopyFlag(StringRef InstName) { CopyF.insert(InstName); }
+
+//===- CodeGenInstructionPattern ------------------------------------------===//
+
+bool CodeGenInstructionPattern::is(StringRef OpcodeName) const {
+ return I.TheDef->getName() == OpcodeName;
+}
+
+bool CodeGenInstructionPattern::isVariadic() const {
+ return I.Operands.isVariadic;
+}
+
+bool CodeGenInstructionPattern::hasVariadicDefs() const {
+ // Note: we cannot use variadicOpsAreDefs, it's not set for
+ // GenericInstructions.
+ if (!isVariadic())
+ return false;
+
+ if (I.variadicOpsAreDefs)
+ return true;
+
+ DagInit *OutOps = I.TheDef->getValueAsDag("OutOperandList");
+ if (OutOps->arg_empty())
+ return false;
+
+ auto *LastArgTy = dyn_cast<DefInit>(OutOps->getArg(OutOps->arg_size() - 1));
+ return LastArgTy && LastArgTy->getDef()->getName() == "variable_ops";
+}
+
+unsigned CodeGenInstructionPattern::getNumInstDefs() const {
+ if (!isVariadic() || !hasVariadicDefs())
+ return I.Operands.NumDefs;
+ unsigned NumOuts = I.Operands.size() - I.Operands.NumDefs;
+ assert(Operands.size() > NumOuts);
+ return std::max<unsigned>(I.Operands.NumDefs, Operands.size() - NumOuts);
+}
+
+unsigned CodeGenInstructionPattern::getNumInstOperands() const {
+ unsigned NumCGIOps = I.Operands.size();
+ return isVariadic() ? std::max<unsigned>(NumCGIOps, Operands.size())
+ : NumCGIOps;
+}
+
+MIFlagsInfo &CodeGenInstructionPattern::getOrCreateMIFlagsInfo() {
+ if (!FI)
+ FI = std::make_unique<MIFlagsInfo>();
+ return *FI;
+}
+
+StringRef CodeGenInstructionPattern::getInstName() const {
+ return I.TheDef->getName();
+}
+
+void CodeGenInstructionPattern::printExtras(raw_ostream &OS) const {
+ if (!FI)
+ return;
+
+ OS << " (MIFlags";
+ if (!FI->set_flags().empty())
+ OS << " (set " << join(FI->set_flags(), ", ") << ")";
+ if (!FI->unset_flags().empty())
+ OS << " (unset " << join(FI->unset_flags(), ", ") << ")";
+ if (!FI->copy_flags().empty())
+ OS << " (copy " << join(FI->copy_flags(), ", ") << ")";
+ OS << ')';
+}
+
+//===- OperandTypeChecker -------------------------------------------------===//
+
+bool OperandTypeChecker::check(
+ InstructionPattern &P,
+ std::function<bool(const PatternType &)> VerifyTypeOfOperand) {
+ Pats.push_back(&P);
+
+ for (auto &Op : P.operands()) {
+ const auto Ty = Op.getType();
+ if (!Ty)
+ continue;
+
+ if (Ty.isTypeOf() && !VerifyTypeOfOperand(Ty))
+ return false;
+
+ if (!Op.isNamedOperand())
+ continue;
+
+ StringRef OpName = Op.getOperandName();
+ auto &Info = Types[OpName];
+ if (!Info.Type) {
+ Info.Type = Ty;
+ Info.PrintTypeSrcNote = [this, OpName, Ty, &P]() {
+ PrintSeenWithTypeIn(P, OpName, Ty);
+ };
+ continue;
+ }
+
+ if (Info.Type != Ty) {
+ PrintError(DiagLoc, "conflicting types for operand '" +
+ Op.getOperandName() + "': '" + Info.Type.str() +
+ "' vs '" + Ty.str() + "'");
+ PrintSeenWithTypeIn(P, OpName, Ty);
+ Info.PrintTypeSrcNote();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void OperandTypeChecker::propagateTypes() {
+ for (auto *Pat : Pats) {
+ for (auto &Op : Pat->named_operands()) {
+ if (auto &Info = Types[Op.getOperandName()]; Info.Type)
+ Op.setType(Info.Type);
+ }
+ }
+}
+
+void OperandTypeChecker::PrintSeenWithTypeIn(InstructionPattern &P,
+ StringRef OpName,
+ PatternType Ty) const {
+ PrintNote(DiagLoc, "'" + OpName + "' seen with type '" + Ty.str() + "' in '" +
+ P.getName() + "'");
+}
+
+StringRef PatFrag::getParamKindStr(ParamKind OK) {
+ switch (OK) {
+ case PK_Root:
+ return "root";
+ case PK_MachineOperand:
+ return "machine_operand";
+ case PK_Imm:
+ return "imm";
+ }
+
+ llvm_unreachable("Unknown operand kind!");
+}
+
+//===- PatFrag -----------------------------------------------------------===//
+
+PatFrag::PatFrag(const Record &Def) : Def(Def) {
+ assert(Def.isSubClassOf(ClassName));
+}
+
+StringRef PatFrag::getName() const { return Def.getName(); }
+
+ArrayRef<SMLoc> PatFrag::getLoc() const { return Def.getLoc(); }
+
+void PatFrag::addInParam(StringRef Name, ParamKind Kind) {
+ Params.emplace_back(Param{Name, Kind});
+}
+
+iterator_range<PatFrag::ParamIt> PatFrag::in_params() const {
+ return {Params.begin() + NumOutParams, Params.end()};
+}
+
+void PatFrag::addOutParam(StringRef Name, ParamKind Kind) {
+ assert(NumOutParams == Params.size() &&
+ "Adding out-param after an in-param!");
+ Params.emplace_back(Param{Name, Kind});
+ ++NumOutParams;
+}
+
+iterator_range<PatFrag::ParamIt> PatFrag::out_params() const {
+ return {Params.begin(), Params.begin() + NumOutParams};
+}
+
+unsigned PatFrag::num_roots() const {
+ return count_if(out_params(),
+ [&](const auto &P) { return P.Kind == PK_Root; });
+}
+
+unsigned PatFrag::getParamIdx(StringRef Name) const {
+ for (const auto &[Idx, Op] : enumerate(Params)) {
+ if (Op.Name == Name)
+ return Idx;
+ }
+
+ return -1;
+}
+
+bool PatFrag::checkSemantics() {
+ for (const auto &Alt : Alts) {
+ for (const auto &Pat : Alt.Pats) {
+ switch (Pat->getKind()) {
+ case Pattern::K_AnyOpcode:
+ PrintError("wip_match_opcode cannot be used in " + ClassName);
+ return false;
+ case Pattern::K_Builtin:
+ PrintError("Builtin instructions cannot be used in " + ClassName);
+ return false;
+ case Pattern::K_CXX:
+ continue;
+ case Pattern::K_CodeGenInstruction:
+ if (cast<CodeGenInstructionPattern>(Pat.get())->diagnoseAllSpecialTypes(
+ Def.getLoc(), PatternType::SpecialTyClassName +
+ " is not supported in " + ClassName))
+ return false;
+ continue;
+ case Pattern::K_PatFrag:
+ // TODO: It's just that the emitter doesn't handle it but technically
+ // there is no reason why we can't. We just have to be careful with
+ // operand mappings, it could get complex.
+ PrintError("nested " + ClassName + " are not supported");
+ return false;
+ }
+ }
+ }
+
+ StringSet<> SeenOps;
+ for (const auto &Op : in_params()) {
+ if (SeenOps.count(Op.Name)) {
+ PrintError("duplicate parameter '" + Op.Name + "'");
+ return false;
+ }
+
+ // Check this operand is NOT defined in any alternative's patterns.
+ for (const auto &Alt : Alts) {
+ if (Alt.OpTable.lookup(Op.Name).Def) {
+ PrintError("input parameter '" + Op.Name + "' cannot be redefined!");
+ return false;
+ }
+ }
+
+ if (Op.Kind == PK_Root) {
+ PrintError("input parameterr '" + Op.Name + "' cannot be a root!");
+ return false;
+ }
+
+ SeenOps.insert(Op.Name);
+ }
+
+ for (const auto &Op : out_params()) {
+ if (Op.Kind != PK_Root && Op.Kind != PK_MachineOperand) {
+ PrintError("output parameter '" + Op.Name +
+ "' must be 'root' or 'gi_mo'");
+ return false;
+ }
+
+ if (SeenOps.count(Op.Name)) {
+ PrintError("duplicate parameter '" + Op.Name + "'");
+ return false;
+ }
+
+ // Check this operand is defined in all alternative's patterns.
+ for (const auto &Alt : Alts) {
+ const auto *OpDef = Alt.OpTable.getDef(Op.Name);
+ if (!OpDef) {
+ PrintError("output parameter '" + Op.Name +
+ "' must be defined by all alternative patterns in '" +
+ Def.getName() + "'");
+ return false;
+ }
+
+ if (Op.Kind == PK_Root && OpDef->getNumInstDefs() != 1) {
+ // The instruction that defines the root must have a single def.
+ // Otherwise we'd need to support multiple roots and it gets messy.
+ //
+ // e.g. this is not supported:
+ // (pattern (G_UNMERGE_VALUES $x, $root, $vec))
+ PrintError("all instructions that define root '" + Op.Name + "' in '" +
+ Def.getName() + "' can only have a single output operand");
+ return false;
+ }
+ }
+
+ SeenOps.insert(Op.Name);
+ }
+
+ if (num_out_params() != 0 && num_roots() == 0) {
+ PrintError(ClassName + " must have one root in its 'out' operands");
+ return false;
+ }
+
+ if (num_roots() > 1) {
+ PrintError(ClassName + " can only have one root");
+ return false;
+ }
+
+ // TODO: find unused params
+
+ const auto CheckTypeOf = [&](const PatternType &) -> bool {
+ llvm_unreachable("GITypeOf should have been rejected earlier!");
+ };
+
+ // Now, typecheck all alternatives.
+ for (auto &Alt : Alts) {
+ OperandTypeChecker OTC(Def.getLoc());
+ for (auto &Pat : Alt.Pats) {
+ if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {
+ if (!OTC.check(*IP, CheckTypeOf))
+ return false;
+ }
+ }
+ OTC.propagateTypes();
+ }
+
+ return true;
+}
+
+bool PatFrag::handleUnboundInParam(StringRef ParamName, StringRef ArgName,
+ ArrayRef<SMLoc> DiagLoc) const {
+ // The parameter must be a live-in of all alternatives for this to work.
+ // Otherwise, we risk having unbound parameters being used (= crashes).
+ //
+ // Examples:
+ //
+ // in (ins $y), (patterns (G_FNEG $dst, $y), "return matchFnegOp(${y})")
+ // even if $y is unbound, we'll lazily bind it when emitting the G_FNEG.
+ //
+ // in (ins $y), (patterns "return matchFnegOp(${y})")
+ // if $y is unbound when this fragment is emitted, C++ code expansion will
+ // fail.
+ for (const auto &Alt : Alts) {
+ auto &OT = Alt.OpTable;
+ if (!OT.lookup(ParamName).Found) {
+ llvm::PrintError(DiagLoc, "operand '" + ArgName + "' (for parameter '" +
+ ParamName + "' of '" + getName() +
+ "') cannot be unbound");
+ PrintNote(
+ DiagLoc,
+ "one or more alternatives of '" + getName() + "' do not bind '" +
+ ParamName +
+ "' to an instruction operand; either use a bound operand or "
+ "ensure '" +
+ Def.getName() + "' binds '" + ParamName +
+ "' in all alternatives");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool PatFrag::buildOperandsTables() {
+ // enumerate(...) doesn't seem to allow lvalues so we need to count the old
+ // way.
+ unsigned Idx = 0;
+
+ const auto DiagnoseRedef = [this, &Idx](StringRef OpName) {
+ PrintError("Operand '" + OpName +
+ "' is defined multiple times in patterns of alternative #" +
+ std::to_string(Idx));
+ };
+
+ for (auto &Alt : Alts) {
+ for (auto &Pat : Alt.Pats) {
+ auto *IP = dyn_cast<InstructionPattern>(Pat.get());
+ if (!IP)
+ continue;
+
+ if (!Alt.OpTable.addPattern(IP, DiagnoseRedef))
+ return false;
+ }
+
+ ++Idx;
+ }
+
+ return true;
+}
+
+void PatFrag::print(raw_ostream &OS, StringRef Indent) const {
+ OS << Indent << "(PatFrag name:" << getName() << '\n';
+ if (!in_params().empty()) {
+ OS << Indent << " (ins ";
+ printParamsList(OS, in_params());
+ OS << ")\n";
+ }
+
+ if (!out_params().empty()) {
+ OS << Indent << " (outs ";
+ printParamsList(OS, out_params());
+ OS << ")\n";
+ }
+
+ // TODO: Dump OperandTable as well.
+ OS << Indent << " (alternatives [\n";
+ for (const auto &Alt : Alts) {
+ OS << Indent << " [\n";
+ for (const auto &Pat : Alt.Pats) {
+ OS << Indent << " ";
+ Pat->print(OS, /*PrintName=*/true);
+ OS << ",\n";
+ }
+ OS << Indent << " ],\n";
+ }
+ OS << Indent << " ])\n";
+
+ OS << Indent << ')';
+}
+
+void PatFrag::dump() const { print(dbgs()); }
+
+void PatFrag::printParamsList(raw_ostream &OS, iterator_range<ParamIt> Params) {
+ OS << '['
+ << join(map_range(Params,
+ [](auto &O) {
+ return (O.Name + ":" + getParamKindStr(O.Kind)).str();
+ }),
+ ", ")
+ << ']';
+}
+
+void PatFrag::PrintError(Twine Msg) const { llvm::PrintError(&Def, Msg); }
+
+ArrayRef<InstructionOperand> PatFragPattern::getApplyDefsNeeded() const {
+ assert(PF.num_roots() == 1);
+ // Only roots need to be redef.
+ for (auto [Idx, Param] : enumerate(PF.out_params())) {
+ if (Param.Kind == PatFrag::PK_Root)
+ return getOperand(Idx);
+ }
+ llvm_unreachable("root not found!");
+}
+
+//===- PatFragPattern -----------------------------------------------------===//
+
+bool PatFragPattern::checkSemantics(ArrayRef<SMLoc> DiagLoc) {
+ if (!InstructionPattern::checkSemantics(DiagLoc))
+ return false;
+
+ for (const auto &[Idx, Op] : enumerate(Operands)) {
+ switch (PF.getParam(Idx).Kind) {
+ case PatFrag::PK_Imm:
+ if (!Op.hasImmValue()) {
+ PrintError(DiagLoc, "expected operand " + std::to_string(Idx) +
+ " of '" + getInstName() +
+ "' to be an immediate; got " + Op.describe());
+ return false;
+ }
+ if (Op.isNamedImmediate()) {
+ PrintError(DiagLoc, "operand " + std::to_string(Idx) + " of '" +
+ getInstName() +
+ "' cannot be a named immediate");
+ return false;
+ }
+ break;
+ case PatFrag::PK_Root:
+ case PatFrag::PK_MachineOperand:
+ if (!Op.isNamedOperand() || Op.isNamedImmediate()) {
+ PrintError(DiagLoc, "expected operand " + std::to_string(Idx) +
+ " of '" + getInstName() +
+ "' to be a MachineOperand; got " +
+ Op.describe());
+ return false;
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool PatFragPattern::mapInputCodeExpansions(const CodeExpansions &ParentCEs,
+ CodeExpansions &PatFragCEs,
+ ArrayRef<SMLoc> DiagLoc) const {
+ for (const auto &[Idx, Op] : enumerate(operands())) {
+ StringRef ParamName = PF.getParam(Idx).Name;
+
+ // Operands to a PFP can only be named, or be an immediate, but not a named
+ // immediate.
+ assert(!Op.isNamedImmediate());
+
+ if (Op.isNamedOperand()) {
+ StringRef ArgName = Op.getOperandName();
+ // Map it only if it's been defined.
+ auto It = ParentCEs.find(ArgName);
+ if (It == ParentCEs.end()) {
+ if (!PF.handleUnboundInParam(ParamName, ArgName, DiagLoc))
+ return false;
+ } else
+ PatFragCEs.declare(ParamName, It->second);
+ continue;
+ }
+
+ if (Op.hasImmValue()) {
+ PatFragCEs.declare(ParamName, std::to_string(Op.getImmValue()));
+ continue;
+ }
+
+ llvm_unreachable("Unknown Operand Type!");
+ }
+
+ return true;
+}
+
+//===- BuiltinPattern -----------------------------------------------------===//
+
+BuiltinPattern::BuiltinInfo BuiltinPattern::getBuiltinInfo(const Record &Def) {
+ assert(Def.isSubClassOf(ClassName));
+
+ StringRef Name = Def.getName();
+ for (const auto &KBI : KnownBuiltins) {
+ if (KBI.DefName == Name)
+ return KBI;
+ }
+
+ PrintFatalError(Def.getLoc(),
+ "Unimplemented " + ClassName + " def '" + Name + "'");
+}
+
+bool BuiltinPattern::checkSemantics(ArrayRef<SMLoc> Loc) {
+ if (!InstructionPattern::checkSemantics(Loc))
+ return false;
+
+ // For now all builtins just take names, no immediates.
+ for (const auto &[Idx, Op] : enumerate(operands())) {
+ if (!Op.isNamedOperand() || Op.isNamedImmediate()) {
+ PrintError(Loc, "expected operand " + std::to_string(Idx) + " of '" +
+ getInstName() + "' to be a name");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace gi
+} // namespace llvm
diff --git a/llvm/utils/TableGen/GlobalISel/Patterns.h b/llvm/utils/TableGen/GlobalISel/Patterns.h
new file mode 100644
index 000000000000..b3160552a21f
--- /dev/null
+++ b/llvm/utils/TableGen/GlobalISel/Patterns.h
@@ -0,0 +1,690 @@
+//===- Patterns.h ----------------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file Contains the Pattern hierarchy alongside helper classes such as
+/// PatFrag, MIFlagsInfo, PatternType, etc.
+///
+/// These classes are used by the GlobalISel Combiner backend to help parse,
+/// process and emit MIR patterns.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_UTILS_GLOBALISEL_PATTERNS_H
+#define LLVM_UTILS_GLOBALISEL_PATTERNS_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include <memory>
+#include <optional>
+#include <string>
+
+namespace llvm {
+
+class Record;
+class SMLoc;
+class StringInit;
+class CodeExpansions;
+class CodeGenInstruction;
+
+namespace gi {
+
+class CXXPredicateCode;
+class LLTCodeGen;
+class LLTCodeGenOrTempType;
+class RuleMatcher;
+
+//===- PatternType --------------------------------------------------------===//
+
+/// Represent the type of a Pattern Operand.
+///
+/// Types have two form:
+/// - LLTs, which are straightforward.
+/// - Special types, e.g. GITypeOf
+class PatternType {
+public:
+ static constexpr StringLiteral SpecialTyClassName = "GISpecialType";
+ static constexpr StringLiteral TypeOfClassName = "GITypeOf";
+
+ enum PTKind : uint8_t {
+ PT_None,
+
+ PT_ValueType,
+ PT_TypeOf,
+ };
+
+ PatternType() : Kind(PT_None), Data() {}
+
+ static std::optional<PatternType> get(ArrayRef<SMLoc> DiagLoc,
+ const Record *R, Twine DiagCtx);
+ static PatternType getTypeOf(StringRef OpName);
+
+ bool isNone() const { return Kind == PT_None; }
+ bool isLLT() const { return Kind == PT_ValueType; }
+ bool isSpecial() const { return isTypeOf(); }
+ bool isTypeOf() const { return Kind == PT_TypeOf; }
+
+ StringRef getTypeOfOpName() const;
+ const Record *getLLTRecord() const;
+
+ explicit operator bool() const { return !isNone(); }
+
+ bool operator==(const PatternType &Other) const;
+ bool operator!=(const PatternType &Other) const { return !operator==(Other); }
+
+ std::string str() const;
+
+private:
+ PatternType(PTKind Kind) : Kind(Kind), Data() {}
+
+ PTKind Kind;
+ union DataT {
+ DataT() : Str() {}
+
+ /// PT_ValueType -> ValueType Def.
+ const Record *Def;
+
+ /// PT_TypeOf -> Operand name (without the '$')
+ StringRef Str;
+ } Data;
+};
+
+//===- Pattern Base Class -------------------------------------------------===//
+
+/// Base class for all patterns that can be written in an `apply`, `match` or
+/// `pattern` DAG operator.
+///
+/// For example:
+///
+/// (apply (G_ZEXT $x, $y), (G_ZEXT $y, $z), "return isFoo(${z})")
+///
+/// Creates 3 Pattern objects:
+/// - Two CodeGenInstruction Patterns
+/// - A CXXPattern
+class Pattern {
+public:
+ enum {
+ K_AnyOpcode,
+ K_CXX,
+
+ K_CodeGenInstruction,
+ K_PatFrag,
+ K_Builtin,
+ };
+
+ virtual ~Pattern() = default;
+
+ unsigned getKind() const { return Kind; }
+ const char *getKindName() const;
+
+ bool hasName() const { return !Name.empty(); }
+ StringRef getName() const { return Name; }
+
+ virtual void print(raw_ostream &OS, bool PrintName = true) const = 0;
+ void dump() const;
+
+protected:
+ Pattern(unsigned Kind, StringRef Name) : Kind(Kind), Name(Name) {
+ assert(!Name.empty() && "unnamed pattern!");
+ }
+
+ void printImpl(raw_ostream &OS, bool PrintName,
+ function_ref<void()> ContentPrinter) const;
+
+private:
+ unsigned Kind;
+ StringRef Name;
+};
+
+//===- AnyOpcodePattern ---------------------------------------------------===//
+
+/// `wip_match_opcode` patterns.
+/// This matches one or more opcodes, and does not check any operands
+/// whatsoever.
+///
+/// TODO: Long-term, this needs to be removed. It's a hack around MIR
+/// pattern matching limitations.
+class AnyOpcodePattern : public Pattern {
+public:
+ AnyOpcodePattern(StringRef Name) : Pattern(K_AnyOpcode, Name) {}
+
+ static bool classof(const Pattern *P) { return P->getKind() == K_AnyOpcode; }
+
+ void addOpcode(const CodeGenInstruction *I) { Insts.push_back(I); }
+ const auto &insts() const { return Insts; }
+
+ void print(raw_ostream &OS, bool PrintName = true) const override;
+
+private:
+ SmallVector<const CodeGenInstruction *, 4> Insts;
+};
+
+//===- CXXPattern ---------------------------------------------------------===//
+
+/// Represents raw C++ code which may need some expansions.
+///
+/// e.g. [{ return isFooBux(${src}.getReg()); }]
+///
+/// For the expanded code, \see CXXPredicateCode. CXXPredicateCode objects are
+/// created through `expandCode`.
+///
+/// \see CodeExpander and \see CodeExpansions for more information on code
+/// expansions.
+///
+/// This object has two purposes:
+/// - Represent C++ code as a pattern entry.
+/// - Be a factory for expanded C++ code.
+/// - It's immutable and only holds the raw code so we can expand the same
+/// CXX pattern multiple times if we need to.
+///
+/// Note that the code is always trimmed in the constructor, so leading and
+/// trailing whitespaces are removed. This removes bloat in the output, avoids
+/// formatting issues, but also allows us to check things like
+/// `.startswith("return")` trivially without worrying about spaces.
+class CXXPattern : public Pattern {
+public:
+ CXXPattern(const StringInit &Code, StringRef Name);
+
+ CXXPattern(StringRef Code, StringRef Name)
+ : Pattern(K_CXX, Name), RawCode(Code.trim().str()) {}
+
+ static bool classof(const Pattern *P) { return P->getKind() == K_CXX; }
+
+ void setIsApply(bool Value = true) { IsApply = Value; }
+ StringRef getRawCode() const { return RawCode; }
+
+ /// Expands raw code, replacing things such as `${foo}` with their
+ /// substitution in \p CE.
+ ///
+ /// \param CE Map of Code Expansions
+ /// \param Locs SMLocs for the Code Expander, in case it needs to emit
+ /// diagnostics.
+ /// \param AddComment Optionally called to emit a comment before the expanded
+ /// code.
+ ///
+ /// \return A CXXPredicateCode object that contains the expanded code. Note
+ /// that this may or may not insert a new object. All CXXPredicateCode objects
+ /// are held in a set to avoid emitting duplicate C++ code.
+ const CXXPredicateCode &
+ expandCode(const CodeExpansions &CE, ArrayRef<SMLoc> Locs,
+ function_ref<void(raw_ostream &)> AddComment = {}) const;
+
+ void print(raw_ostream &OS, bool PrintName = true) const override;
+
+private:
+ bool IsApply = false;
+ std::string RawCode;
+};
+
+//===- InstructionPattern ---------------------------------------------===//
+
+/// An operand for an InstructionPattern.
+///
+/// Operands are composed of three elements:
+/// - (Optional) Value
+/// - (Optional) Name
+/// - (Optional) Type
+///
+/// Some examples:
+/// (i32 0):$x -> V=int(0), Name='x', Type=i32
+/// 0:$x -> V=int(0), Name='x'
+/// $x -> Name='x'
+/// i32:$x -> Name='x', Type = i32
+class InstructionOperand {
+public:
+ using IntImmTy = int64_t;
+
+ InstructionOperand(IntImmTy Imm, StringRef Name, PatternType Type)
+ : Value(Imm), Name(Name), Type(Type) {}
+
+ InstructionOperand(StringRef Name, PatternType Type)
+ : Name(Name), Type(Type) {}
+
+ bool isNamedImmediate() const { return hasImmValue() && isNamedOperand(); }
+
+ bool hasImmValue() const { return Value.has_value(); }
+ IntImmTy getImmValue() const { return *Value; }
+
+ bool isNamedOperand() const { return !Name.empty(); }
+ StringRef getOperandName() const {
+ assert(isNamedOperand() && "Operand is unnamed");
+ return Name;
+ }
+
+ InstructionOperand withNewName(StringRef NewName) const {
+ InstructionOperand Result = *this;
+ Result.Name = NewName;
+ return Result;
+ }
+
+ void setIsDef(bool Value = true) { Def = Value; }
+ bool isDef() const { return Def; }
+
+ void setType(PatternType NewType) {
+ assert((!Type || (Type == NewType)) && "Overwriting type!");
+ Type = NewType;
+ }
+ PatternType getType() const { return Type; }
+
+ std::string describe() const;
+ void print(raw_ostream &OS) const;
+
+ void dump() const;
+
+private:
+ std::optional<int64_t> Value;
+ StringRef Name;
+ PatternType Type;
+ bool Def = false;
+};
+
+/// Base class for CodeGenInstructionPattern & PatFragPattern, which handles all
+/// the boilerplate for patterns that have a list of operands for some (pseudo)
+/// instruction.
+class InstructionPattern : public Pattern {
+public:
+ virtual ~InstructionPattern() = default;
+
+ static bool classof(const Pattern *P) {
+ return P->getKind() == K_CodeGenInstruction || P->getKind() == K_PatFrag ||
+ P->getKind() == K_Builtin;
+ }
+
+ template <typename... Ty> void addOperand(Ty &&...Init) {
+ Operands.emplace_back(std::forward<Ty>(Init)...);
+ }
+
+ auto &operands() { return Operands; }
+ const auto &operands() const { return Operands; }
+ unsigned operands_size() const { return Operands.size(); }
+ InstructionOperand &getOperand(unsigned K) { return Operands[K]; }
+ const InstructionOperand &getOperand(unsigned K) const { return Operands[K]; }
+
+ /// When this InstructionPattern is used as the match root, returns the
+ /// operands that must be redefined in the 'apply' pattern for the rule to be
+ /// valid.
+ ///
+ /// For most patterns, this just returns the defs.
+ /// For PatFrag this only returns the root of the PF.
+ ///
+ /// Returns an empty array on error.
+ virtual ArrayRef<InstructionOperand> getApplyDefsNeeded() const {
+ return {operands().begin(), getNumInstDefs()};
+ }
+
+ auto named_operands() {
+ return make_filter_range(Operands,
+ [&](auto &O) { return O.isNamedOperand(); });
+ }
+
+ auto named_operands() const {
+ return make_filter_range(Operands,
+ [&](auto &O) { return O.isNamedOperand(); });
+ }
+
+ virtual bool isVariadic() const { return false; }
+ virtual unsigned getNumInstOperands() const = 0;
+ virtual unsigned getNumInstDefs() const = 0;
+
+ bool hasAllDefs() const { return operands_size() >= getNumInstDefs(); }
+
+ virtual StringRef getInstName() const = 0;
+
+ /// Diagnoses all uses of special types in this Pattern and returns true if at
+ /// least one diagnostic was emitted.
+ bool diagnoseAllSpecialTypes(ArrayRef<SMLoc> Loc, Twine Msg) const;
+
+ void reportUnreachable(ArrayRef<SMLoc> Locs) const;
+ virtual bool checkSemantics(ArrayRef<SMLoc> Loc);
+
+ void print(raw_ostream &OS, bool PrintName = true) const override;
+
+protected:
+ InstructionPattern(unsigned K, StringRef Name) : Pattern(K, Name) {}
+
+ virtual void printExtras(raw_ostream &OS) const {}
+
+ SmallVector<InstructionOperand, 4> Operands;
+};
+
+//===- OperandTable -------------------------------------------------------===//
+
+/// Maps InstructionPattern operands to their definitions. This allows us to tie
+/// different patterns of a (apply), (match) or (patterns) set of patterns
+/// together.
+class OperandTable {
+public:
+ bool addPattern(InstructionPattern *P,
+ function_ref<void(StringRef)> DiagnoseRedef);
+
+ struct LookupResult {
+ LookupResult() = default;
+ LookupResult(InstructionPattern *Def) : Found(true), Def(Def) {}
+
+ bool Found = false;
+ InstructionPattern *Def = nullptr;
+
+ bool isLiveIn() const { return Found && !Def; }
+ };
+
+ LookupResult lookup(StringRef OpName) const {
+ if (auto It = Table.find(OpName); It != Table.end())
+ return LookupResult(It->second);
+ return LookupResult();
+ }
+
+ InstructionPattern *getDef(StringRef OpName) const {
+ return lookup(OpName).Def;
+ }
+
+ void print(raw_ostream &OS, StringRef Name = "", StringRef Indent = "") const;
+
+ auto begin() const { return Table.begin(); }
+ auto end() const { return Table.end(); }
+
+ void dump() const;
+
+private:
+ StringMap<InstructionPattern *> Table;
+};
+
+//===- CodeGenInstructionPattern ------------------------------------------===//
+
+/// Helper class to contain data associated with a MIFlags operand.
+class MIFlagsInfo {
+public:
+ void addSetFlag(const Record *R);
+ void addUnsetFlag(const Record *R);
+ void addCopyFlag(StringRef InstName);
+
+ const auto &set_flags() const { return SetF; }
+ const auto &unset_flags() const { return UnsetF; }
+ const auto &copy_flags() const { return CopyF; }
+
+private:
+ SetVector<StringRef> SetF, UnsetF, CopyF;
+};
+
+/// Matches an instruction, e.g. `G_ADD $x, $y, $z`.
+class CodeGenInstructionPattern : public InstructionPattern {
+public:
+ CodeGenInstructionPattern(const CodeGenInstruction &I, StringRef Name)
+ : InstructionPattern(K_CodeGenInstruction, Name), I(I) {}
+
+ static bool classof(const Pattern *P) {
+ return P->getKind() == K_CodeGenInstruction;
+ }
+
+ bool is(StringRef OpcodeName) const;
+
+ bool hasVariadicDefs() const;
+ bool isVariadic() const override;
+ unsigned getNumInstDefs() const override;
+ unsigned getNumInstOperands() const override;
+
+ MIFlagsInfo &getOrCreateMIFlagsInfo();
+ const MIFlagsInfo *getMIFlagsInfo() const { return FI.get(); }
+
+ const CodeGenInstruction &getInst() const { return I; }
+ StringRef getInstName() const override;
+
+private:
+ void printExtras(raw_ostream &OS) const override;
+
+ const CodeGenInstruction &I;
+ std::unique_ptr<MIFlagsInfo> FI;
+};
+
+//===- OperandTypeChecker -------------------------------------------------===//
+
+/// This is a trivial type checker for all operands in a set of
+/// InstructionPatterns.
+///
+/// It infers the type of each operand, check it's consistent with the known
+/// type of the operand, and then sets all of the types in all operands in
+/// propagateTypes.
+///
+/// It also handles verifying correctness of special types.
+class OperandTypeChecker {
+public:
+ OperandTypeChecker(ArrayRef<SMLoc> DiagLoc) : DiagLoc(DiagLoc) {}
+
+ /// Step 1: Check each pattern one by one. All patterns that pass through here
+ /// are added to a common worklist so propagateTypes can access them.
+ bool check(InstructionPattern &P,
+ std::function<bool(const PatternType &)> VerifyTypeOfOperand);
+
+ /// Step 2: Propagate all types. e.g. if one use of "$a" has type i32, make
+ /// all uses of "$a" have type i32.
+ void propagateTypes();
+
+protected:
+ ArrayRef<SMLoc> DiagLoc;
+
+private:
+ using InconsistentTypeDiagFn = std::function<void()>;
+
+ void PrintSeenWithTypeIn(InstructionPattern &P, StringRef OpName,
+ PatternType Ty) const;
+
+ struct OpTypeInfo {
+ PatternType Type;
+ InconsistentTypeDiagFn PrintTypeSrcNote = []() {};
+ };
+
+ StringMap<OpTypeInfo> Types;
+
+ SmallVector<InstructionPattern *, 16> Pats;
+};
+
+//===- PatFrag ------------------------------------------------------------===//
+
+/// Represents a parsed GICombinePatFrag. This can be thought of as the
+/// equivalent of a CodeGenInstruction, but for PatFragPatterns.
+///
+/// PatFrags are made of 3 things:
+/// - Out parameters (defs)
+/// - In parameters
+/// - A set of pattern lists (alternatives).
+///
+/// If the PatFrag uses instruction patterns, the root must be one of the defs.
+///
+/// Note that this DOES NOT represent the use of the PatFrag, only its
+/// definition. The use of the PatFrag in a Pattern is represented by
+/// PatFragPattern.
+///
+/// PatFrags use the term "parameter" instead of operand because they're
+/// essentially macros, and using that name avoids confusion. Other than that,
+/// they're structured similarly to a MachineInstruction - all parameters
+/// (operands) are in the same list, with defs at the start. This helps mapping
+/// parameters to values, because, param N of a PatFrag is always operand N of a
+/// PatFragPattern.
+class PatFrag {
+public:
+ static constexpr StringLiteral ClassName = "GICombinePatFrag";
+
+ enum ParamKind {
+ PK_Root,
+ PK_MachineOperand,
+ PK_Imm,
+ };
+
+ struct Param {
+ StringRef Name;
+ ParamKind Kind;
+ };
+
+ using ParamVec = SmallVector<Param, 4>;
+ using ParamIt = ParamVec::const_iterator;
+
+ /// Represents an alternative of the PatFrag. When parsing a GICombinePatFrag,
+ /// this is created from its "Alternatives" list. Each alternative is a list
+ /// of patterns written wrapped in a `(pattern ...)` dag init.
+ ///
+ /// Each argument to the `pattern` DAG operator is parsed into a Pattern
+ /// instance.
+ struct Alternative {
+ OperandTable OpTable;
+ SmallVector<std::unique_ptr<Pattern>, 4> Pats;
+ };
+
+ explicit PatFrag(const Record &Def);
+
+ static StringRef getParamKindStr(ParamKind OK);
+
+ StringRef getName() const;
+
+ const Record &getDef() const { return Def; }
+ ArrayRef<SMLoc> getLoc() const;
+
+ Alternative &addAlternative() { return Alts.emplace_back(); }
+ const Alternative &getAlternative(unsigned K) const { return Alts[K]; }
+ unsigned num_alternatives() const { return Alts.size(); }
+
+ void addInParam(StringRef Name, ParamKind Kind);
+ iterator_range<ParamIt> in_params() const;
+ unsigned num_in_params() const { return Params.size() - NumOutParams; }
+
+ void addOutParam(StringRef Name, ParamKind Kind);
+ iterator_range<ParamIt> out_params() const;
+ unsigned num_out_params() const { return NumOutParams; }
+
+ unsigned num_roots() const;
+ unsigned num_params() const { return num_in_params() + num_out_params(); }
+
+ /// Finds the operand \p Name and returns its index or -1 if not found.
+ /// Remember that all params are part of the same list, with out params at the
+ /// start. This means that the index returned can be used to access operands
+ /// of InstructionPatterns.
+ unsigned getParamIdx(StringRef Name) const;
+ const Param &getParam(unsigned K) const { return Params[K]; }
+
+ bool canBeMatchRoot() const { return num_roots() == 1; }
+
+ void print(raw_ostream &OS, StringRef Indent = "") const;
+ void dump() const;
+
+ /// Checks if the in-param \p ParamName can be unbound or not.
+ /// \p ArgName is the name of the argument passed to the PatFrag.
+ ///
+ /// An argument can be unbound only if, for all alternatives:
+ /// - There is no CXX pattern, OR:
+ /// - There is an InstructionPattern that binds the parameter.
+ ///
+ /// e.g. in (MyPatFrag $foo), if $foo has never been seen before (= it's
+ /// unbound), this checks if MyPatFrag supports it or not.
+ bool handleUnboundInParam(StringRef ParamName, StringRef ArgName,
+ ArrayRef<SMLoc> DiagLoc) const;
+
+ bool checkSemantics();
+ bool buildOperandsTables();
+
+private:
+ static void printParamsList(raw_ostream &OS, iterator_range<ParamIt> Params);
+
+ void PrintError(Twine Msg) const;
+
+ const Record &Def;
+ unsigned NumOutParams = 0;
+ ParamVec Params;
+ SmallVector<Alternative, 2> Alts;
+};
+
+//===- PatFragPattern -----------------------------------------------------===//
+
+/// Represents a use of a GICombinePatFrag.
+class PatFragPattern : public InstructionPattern {
+public:
+ PatFragPattern(const PatFrag &PF, StringRef Name)
+ : InstructionPattern(K_PatFrag, Name), PF(PF) {}
+
+ static bool classof(const Pattern *P) { return P->getKind() == K_PatFrag; }
+
+ const PatFrag &getPatFrag() const { return PF; }
+ StringRef getInstName() const override { return PF.getName(); }
+
+ unsigned getNumInstDefs() const override { return PF.num_out_params(); }
+ unsigned getNumInstOperands() const override { return PF.num_params(); }
+
+ ArrayRef<InstructionOperand> getApplyDefsNeeded() const override;
+
+ bool checkSemantics(ArrayRef<SMLoc> DiagLoc) override;
+
+ /// Before emitting the patterns inside the PatFrag, add all necessary code
+ /// expansions to \p PatFragCEs imported from \p ParentCEs.
+ ///
+ /// For a MachineOperand PatFrag parameter, this will fetch the expansion for
+ /// that operand from \p ParentCEs and add it to \p PatFragCEs. Errors can be
+ /// emitted if the MachineOperand reference is unbound.
+ ///
+ /// For an Immediate PatFrag parameter this simply adds the integer value to
+ /// \p PatFragCEs as an expansion.
+ ///
+ /// \param ParentCEs Contains all of the code expansions declared by the other
+ /// patterns emitted so far in the pattern list containing
+ /// this PatFragPattern.
+ /// \param PatFragCEs Output Code Expansions (usually empty)
+ /// \param DiagLoc Diagnostic loc in case an error occurs.
+ /// \return `true` on success, `false` on failure.
+ bool mapInputCodeExpansions(const CodeExpansions &ParentCEs,
+ CodeExpansions &PatFragCEs,
+ ArrayRef<SMLoc> DiagLoc) const;
+
+private:
+ const PatFrag &PF;
+};
+
+//===- BuiltinPattern -----------------------------------------------------===//
+
+/// Represents builtin instructions such as "GIReplaceReg" and "GIEraseRoot".
+enum BuiltinKind {
+ BI_ReplaceReg,
+ BI_EraseRoot,
+};
+
+class BuiltinPattern : public InstructionPattern {
+ struct BuiltinInfo {
+ StringLiteral DefName;
+ BuiltinKind Kind;
+ unsigned NumOps;
+ unsigned NumDefs;
+ };
+
+ static constexpr std::array<BuiltinInfo, 2> KnownBuiltins = {{
+ {"GIReplaceReg", BI_ReplaceReg, 2, 1},
+ {"GIEraseRoot", BI_EraseRoot, 0, 0},
+ }};
+
+public:
+ static constexpr StringLiteral ClassName = "GIBuiltinInst";
+
+ BuiltinPattern(const Record &Def, StringRef Name)
+ : InstructionPattern(K_Builtin, Name), I(getBuiltinInfo(Def)) {}
+
+ static bool classof(const Pattern *P) { return P->getKind() == K_Builtin; }
+
+ unsigned getNumInstOperands() const override { return I.NumOps; }
+ unsigned getNumInstDefs() const override { return I.NumDefs; }
+ StringRef getInstName() const override { return I.DefName; }
+ BuiltinKind getBuiltinKind() const { return I.Kind; }
+
+ bool checkSemantics(ArrayRef<SMLoc> Loc) override;
+
+private:
+ static BuiltinInfo getBuiltinInfo(const Record &Def);
+
+ BuiltinInfo I;
+};
+
+} // namespace gi
+} // end namespace llvm
+
+#endif // ifndef LLVM_UTILS_GLOBALISEL_PATTERNS_H
diff --git a/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp b/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp
new file mode 100644
index 000000000000..89aca87a28ec
--- /dev/null
+++ b/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp
@@ -0,0 +1,3041 @@
+//===- GlobalISelCombinerMatchTableEmitter.cpp - --------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file Generate a combiner implementation for GlobalISel from a declarative
+/// syntax using GlobalISelMatchTable.
+///
+/// Usually, TableGen backends use "assert is an error" as a means to report
+/// invalid input. They try to diagnose common case but don't try very hard and
+/// crashes can be common. This backend aims to behave closer to how a language
+/// compiler frontend would behave: we try extra hard to diagnose invalid inputs
+/// early, and any crash should be considered a bug (= a feature or diagnostic
+/// is missing).
+///
+/// While this can make the backend a bit more complex than it needs to be, it
+/// pays off because MIR patterns can get complicated. Giving useful error
+/// messages to combine writers can help boost their productivity.
+///
+/// As with anything, a good balance has to be found. We also don't want to
+/// write hundreds of lines of code to detect edge cases. In practice, crashing
+/// very occasionally, or giving poor errors in some rare instances, is fine.
+///
+//===----------------------------------------------------------------------===//
+
+#include "CodeGenInstruction.h"
+#include "CodeGenTarget.h"
+#include "GlobalISel/CXXPredicates.h"
+#include "GlobalISel/CodeExpander.h"
+#include "GlobalISel/CodeExpansions.h"
+#include "GlobalISel/CombinerUtils.h"
+#include "GlobalISel/MatchDataInfo.h"
+#include "GlobalISel/Patterns.h"
+#include "GlobalISelMatchTable.h"
+#include "GlobalISelMatchTableExecutorEmitter.h"
+#include "SubtargetFeatureInfo.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/EquivalenceClasses.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/StringMatcher.h"
+#include "llvm/TableGen/TableGenBackend.h"
+#include <cstdint>
+
+using namespace llvm;
+using namespace llvm::gi;
+
+#define DEBUG_TYPE "gicombiner-emitter"
+
+namespace {
+cl::OptionCategory
+ GICombinerEmitterCat("Options for -gen-global-isel-combiner");
+cl::opt<bool> StopAfterParse(
+ "gicombiner-stop-after-parse",
+ cl::desc("Stop processing after parsing rules and dump state"),
+ cl::cat(GICombinerEmitterCat));
+cl::list<std::string>
+ SelectedCombiners("combiners", cl::desc("Emit the specified combiners"),
+ cl::cat(GICombinerEmitterCat), cl::CommaSeparated);
+cl::opt<bool> DebugCXXPreds(
+ "gicombiner-debug-cxxpreds",
+ cl::desc("Add Contextual/Debug comments to all C++ predicates"),
+ cl::cat(GICombinerEmitterCat));
+cl::opt<bool> DebugTypeInfer("gicombiner-debug-typeinfer",
+ cl::desc("Print type inference debug logs"),
+ cl::cat(GICombinerEmitterCat));
+
+constexpr StringLiteral CXXApplyPrefix = "GICXXCustomAction_CombineApply";
+constexpr StringLiteral CXXPredPrefix = "GICXXPred_MI_Predicate_";
+constexpr StringLiteral MIFlagsEnumClassName = "MIFlagEnum";
+
+//===- CodeExpansions Helpers --------------------------------------------===//
+
+void declareInstExpansion(CodeExpansions &CE, const InstructionMatcher &IM,
+ StringRef Name) {
+ CE.declare(Name, "State.MIs[" + to_string(IM.getInsnVarID()) + "]");
+}
+
+void declareInstExpansion(CodeExpansions &CE, const BuildMIAction &A,
+ StringRef Name) {
+ // Note: we use redeclare here because this may overwrite a matcher inst
+ // expansion.
+ CE.redeclare(Name, "OutMIs[" + to_string(A.getInsnID()) + "]");
+}
+
+void declareOperandExpansion(CodeExpansions &CE, const OperandMatcher &OM,
+ StringRef Name) {
+ CE.declare(Name, "State.MIs[" + to_string(OM.getInsnVarID()) +
+ "]->getOperand(" + to_string(OM.getOpIdx()) + ")");
+}
+
+void declareTempRegExpansion(CodeExpansions &CE, unsigned TempRegID,
+ StringRef Name) {
+ CE.declare(Name, "State.TempRegisters[" + to_string(TempRegID) + "]");
+}
+
+//===- Misc. Helpers -----------------------------------------------------===//
+
+/// Copies a StringRef into a static pool to preserve it.
+/// Most Pattern classes use StringRef so we need this.
+StringRef insertStrRef(StringRef S) {
+ if (S.empty())
+ return {};
+
+ static StringSet<> Pool;
+ auto [It, Inserted] = Pool.insert(S);
+ return It->getKey();
+}
+
+template <typename Container> auto keys(Container &&C) {
+ return map_range(C, [](auto &Entry) -> auto & { return Entry.first; });
+}
+
+template <typename Container> auto values(Container &&C) {
+ return map_range(C, [](auto &Entry) -> auto & { return Entry.second; });
+}
+
+std::string getIsEnabledPredicateEnumName(unsigned CombinerRuleID) {
+ return "GICXXPred_Simple_IsRule" + to_string(CombinerRuleID) + "Enabled";
+}
+
+//===- MatchTable Helpers ------------------------------------------------===//
+
+LLTCodeGen getLLTCodeGen(const PatternType &PT) {
+ return *MVTToLLT(getValueType(PT.getLLTRecord()));
+}
+
+LLTCodeGenOrTempType getLLTCodeGenOrTempType(const PatternType &PT,
+ RuleMatcher &RM) {
+ assert(!PT.isNone());
+
+ if (PT.isLLT())
+ return getLLTCodeGen(PT);
+
+ assert(PT.isTypeOf());
+ auto &OM = RM.getOperandMatcher(PT.getTypeOfOpName());
+ return OM.getTempTypeIdx(RM);
+}
+
+//===- PrettyStackTrace Helpers ------------------------------------------===//
+
+class PrettyStackTraceParse : public PrettyStackTraceEntry {
+ const Record &Def;
+
+public:
+ PrettyStackTraceParse(const Record &Def) : Def(Def) {}
+
+ void print(raw_ostream &OS) const override {
+ if (Def.isSubClassOf("GICombineRule"))
+ OS << "Parsing GICombineRule '" << Def.getName() << "'";
+ else if (Def.isSubClassOf(PatFrag::ClassName))
+ OS << "Parsing " << PatFrag::ClassName << " '" << Def.getName() << "'";
+ else
+ OS << "Parsing '" << Def.getName() << "'";
+ OS << '\n';
+ }
+};
+
+class PrettyStackTraceEmit : public PrettyStackTraceEntry {
+ const Record &Def;
+ const Pattern *Pat = nullptr;
+
+public:
+ PrettyStackTraceEmit(const Record &Def, const Pattern *Pat = nullptr)
+ : Def(Def), Pat(Pat) {}
+
+ void print(raw_ostream &OS) const override {
+ if (Def.isSubClassOf("GICombineRule"))
+ OS << "Emitting GICombineRule '" << Def.getName() << "'";
+ else if (Def.isSubClassOf(PatFrag::ClassName))
+ OS << "Emitting " << PatFrag::ClassName << " '" << Def.getName() << "'";
+ else
+ OS << "Emitting '" << Def.getName() << "'";
+
+ if (Pat)
+ OS << " [" << Pat->getKindName() << " '" << Pat->getName() << "']";
+ OS << '\n';
+ }
+};
+
+//===- CombineRuleOperandTypeChecker --------------------------------------===//
+
+/// This is a wrapper around OperandTypeChecker specialized for Combiner Rules.
+/// On top of doing the same things as OperandTypeChecker, this also attempts to
+/// infer as many types as possible for temporary register defs & immediates in
+/// apply patterns.
+///
+/// The inference is trivial and leverages the MCOI OperandTypes encoded in
+/// CodeGenInstructions to infer types across patterns in a CombineRule. It's
+/// thus very limited and only supports CodeGenInstructions (but that's the main
+/// use case so it's fine).
+///
+/// We only try to infer untyped operands in apply patterns when they're temp
+/// reg defs, or immediates. Inference always outputs a `TypeOf<$x>` where $x is
+/// a named operand from a match pattern.
+class CombineRuleOperandTypeChecker : private OperandTypeChecker {
+public:
+ CombineRuleOperandTypeChecker(const Record &RuleDef,
+ const OperandTable &MatchOpTable)
+ : OperandTypeChecker(RuleDef.getLoc()), RuleDef(RuleDef),
+ MatchOpTable(MatchOpTable) {}
+
+ /// Records and checks a 'match' pattern.
+ bool processMatchPattern(InstructionPattern &P);
+
+ /// Records and checks an 'apply' pattern.
+ bool processApplyPattern(InstructionPattern &P);
+
+ /// Propagates types, then perform type inference and do a second round of
+ /// propagation in the apply patterns only if any types were inferred.
+ void propagateAndInferTypes();
+
+private:
+ /// TypeEquivalenceClasses are groups of operands of an instruction that share
+ /// a common type.
+ ///
+ /// e.g. [[a, b], [c, d]] means a and b have the same type, and c and
+ /// d have the same type too. b/c and a/d don't have to have the same type,
+ /// though.
+ using TypeEquivalenceClasses = EquivalenceClasses<StringRef>;
+
+ /// \returns true for `OPERAND_GENERIC_` 0 through 5.
+ /// These are the MCOI types that can be registers. The other MCOI types are
+ /// either immediates, or fancier operands used only post-ISel, so we don't
+ /// care about them for combiners.
+ static bool canMCOIOperandTypeBeARegister(StringRef MCOIType) {
+ // Assume OPERAND_GENERIC_0 through 5 can be registers. The other MCOI
+ // OperandTypes are either never used in gMIR, or not relevant (e.g.
+ // OPERAND_GENERIC_IMM, which is definitely never a register).
+ return MCOIType.drop_back(1).ends_with("OPERAND_GENERIC_");
+ }
+
+ /// Finds the "MCOI::"" operand types for each operand of \p CGP.
+ ///
+ /// This is a bit trickier than it looks because we need to handle variadic
+ /// in/outs.
+ ///
+ /// e.g. for
+ /// (G_BUILD_VECTOR $vec, $x, $y) ->
+ /// [MCOI::OPERAND_GENERIC_0, MCOI::OPERAND_GENERIC_1,
+ /// MCOI::OPERAND_GENERIC_1]
+ ///
+ /// For unknown types (which can happen in variadics where varargs types are
+ /// inconsistent), a unique name is given, e.g. "unknown_type_0".
+ static std::vector<std::string>
+ getMCOIOperandTypes(const CodeGenInstructionPattern &CGP);
+
+ /// Adds the TypeEquivalenceClasses for \p P in \p OutTECs.
+ void getInstEqClasses(const InstructionPattern &P,
+ TypeEquivalenceClasses &OutTECs) const;
+
+ /// Calls `getInstEqClasses` on all patterns of the rule to produce the whole
+ /// rule's TypeEquivalenceClasses.
+ TypeEquivalenceClasses getRuleEqClasses() const;
+
+ /// Tries to infer the type of the \p ImmOpIdx -th operand of \p IP using \p
+ /// TECs.
+ ///
+ /// This is achieved by trying to find a named operand in \p IP that shares
+ /// the same type as \p ImmOpIdx, and using \ref inferNamedOperandType on that
+ /// operand instead.
+ ///
+ /// \returns the inferred type or an empty PatternType if inference didn't
+ /// succeed.
+ PatternType inferImmediateType(const InstructionPattern &IP,
+ unsigned ImmOpIdx,
+ const TypeEquivalenceClasses &TECs) const;
+
+ /// Looks inside \p TECs to infer \p OpName's type.
+ ///
+ /// \returns the inferred type or an empty PatternType if inference didn't
+ /// succeed.
+ PatternType inferNamedOperandType(const InstructionPattern &IP,
+ StringRef OpName,
+ const TypeEquivalenceClasses &TECs) const;
+
+ const Record &RuleDef;
+ SmallVector<InstructionPattern *, 8> MatchPats;
+ SmallVector<InstructionPattern *, 8> ApplyPats;
+
+ const OperandTable &MatchOpTable;
+};
+
+bool CombineRuleOperandTypeChecker::processMatchPattern(InstructionPattern &P) {
+ MatchPats.push_back(&P);
+ return check(P, /*CheckTypeOf*/ [](const auto &) {
+ // GITypeOf in 'match' is currently always rejected by the
+ // CombineRuleBuilder after inference is done.
+ return true;
+ });
+}
+
+bool CombineRuleOperandTypeChecker::processApplyPattern(InstructionPattern &P) {
+ ApplyPats.push_back(&P);
+ return check(P, /*CheckTypeOf*/ [&](const PatternType &Ty) {
+ // GITypeOf<"$x"> can only be used if "$x" is a matched operand.
+ const auto OpName = Ty.getTypeOfOpName();
+ if (MatchOpTable.lookup(OpName).Found)
+ return true;
+
+ PrintError(RuleDef.getLoc(), "'" + OpName + "' ('" + Ty.str() +
+ "') does not refer to a matched operand!");
+ return false;
+ });
+}
+
+void CombineRuleOperandTypeChecker::propagateAndInferTypes() {
+ /// First step here is to propagate types using the OperandTypeChecker. That
+ /// way we ensure all uses of a given register have consistent types.
+ propagateTypes();
+
+ /// Build the TypeEquivalenceClasses for the whole rule.
+ const TypeEquivalenceClasses TECs = getRuleEqClasses();
+
+ /// Look at the apply patterns and find operands that need to be
+ /// inferred. We then try to find an equivalence class that they're a part of
+ /// and select the best operand to use for the `GITypeOf` type. We prioritize
+ /// defs of matched instructions because those are guaranteed to be registers.
+ bool InferredAny = false;
+ for (auto *Pat : ApplyPats) {
+ for (unsigned K = 0; K < Pat->operands_size(); ++K) {
+ auto &Op = Pat->getOperand(K);
+
+ // We only want to take a look at untyped defs or immediates.
+ if ((!Op.isDef() && !Op.hasImmValue()) || Op.getType())
+ continue;
+
+ // Infer defs & named immediates.
+ if (Op.isDef() || Op.isNamedImmediate()) {
+ // Check it's not a redefinition of a matched operand.
+ // In such cases, inference is not necessary because we just copy
+ // operands and don't create temporary registers.
+ if (MatchOpTable.lookup(Op.getOperandName()).Found)
+ continue;
+
+ // Inference is needed here, so try to do it.
+ if (PatternType Ty =
+ inferNamedOperandType(*Pat, Op.getOperandName(), TECs)) {
+ if (DebugTypeInfer)
+ errs() << "INFER: " << Op.describe() << " -> " << Ty.str() << '\n';
+ Op.setType(Ty);
+ InferredAny = true;
+ }
+
+ continue;
+ }
+
+ // Infer immediates
+ if (Op.hasImmValue()) {
+ if (PatternType Ty = inferImmediateType(*Pat, K, TECs)) {
+ if (DebugTypeInfer)
+ errs() << "INFER: " << Op.describe() << " -> " << Ty.str() << '\n';
+ Op.setType(Ty);
+ InferredAny = true;
+ }
+ continue;
+ }
+ }
+ }
+
+ // If we've inferred any types, we want to propagate them across the apply
+ // patterns. Type inference only adds GITypeOf types that point to Matched
+ // operands, so we definitely don't want to propagate types into the match
+ // patterns as well, otherwise bad things happen.
+ if (InferredAny) {
+ OperandTypeChecker OTC(RuleDef.getLoc());
+ for (auto *Pat : ApplyPats) {
+ if (!OTC.check(*Pat, [&](const auto &) { return true; }))
+ PrintFatalError(RuleDef.getLoc(),
+ "OperandTypeChecker unexpectedly failed on '" +
+ Pat->getName() + "' during Type Inference");
+ }
+ OTC.propagateTypes();
+
+ if (DebugTypeInfer) {
+ errs() << "Apply patterns for rule " << RuleDef.getName()
+ << " after inference:\n";
+ for (auto *Pat : ApplyPats) {
+ errs() << " ";
+ Pat->print(errs(), /*PrintName*/ true);
+ errs() << '\n';
+ }
+ errs() << '\n';
+ }
+ }
+}
+
+PatternType CombineRuleOperandTypeChecker::inferImmediateType(
+ const InstructionPattern &IP, unsigned ImmOpIdx,
+ const TypeEquivalenceClasses &TECs) const {
+ // We can only infer CGPs.
+ const auto *CGP = dyn_cast<CodeGenInstructionPattern>(&IP);
+ if (!CGP)
+ return {};
+
+ // For CGPs, we try to infer immediates by trying to infer another named
+ // operand that shares its type.
+ //
+ // e.g.
+ // Pattern: G_BUILD_VECTOR $x, $y, 0
+ // MCOIs: [MCOI::OPERAND_GENERIC_0, MCOI::OPERAND_GENERIC_1,
+ // MCOI::OPERAND_GENERIC_1]
+ // $y has the same type as 0, so we can infer $y and get the type 0 should
+ // have.
+
+ // We infer immediates by looking for a named operand that shares the same
+ // MCOI type.
+ const auto MCOITypes = getMCOIOperandTypes(*CGP);
+ StringRef ImmOpTy = MCOITypes[ImmOpIdx];
+
+ for (const auto &[Idx, Ty] : enumerate(MCOITypes)) {
+ if (Idx != ImmOpIdx && Ty == ImmOpTy) {
+ const auto &Op = IP.getOperand(Idx);
+ if (!Op.isNamedOperand())
+ continue;
+
+ // Named operand with the same name, try to infer that.
+ if (PatternType InferTy =
+ inferNamedOperandType(IP, Op.getOperandName(), TECs))
+ return InferTy;
+ }
+ }
+
+ return {};
+}
+
+PatternType CombineRuleOperandTypeChecker::inferNamedOperandType(
+ const InstructionPattern &IP, StringRef OpName,
+ const TypeEquivalenceClasses &TECs) const {
+ // This is the simplest possible case, we just need to find a TEC that
+ // contains OpName. Look at all other operands in equivalence class and try to
+ // find a suitable one.
+
+ // Check for a def of a matched pattern. This is guaranteed to always
+ // be a register so we can blindly use that.
+ StringRef GoodOpName;
+ for (auto It = TECs.findLeader(OpName); It != TECs.member_end(); ++It) {
+ if (*It == OpName)
+ continue;
+
+ const auto LookupRes = MatchOpTable.lookup(*It);
+ if (LookupRes.Def) // Favor defs
+ return PatternType::getTypeOf(*It);
+
+ // Otherwise just save this in case we don't find any def.
+ if (GoodOpName.empty() && LookupRes.Found)
+ GoodOpName = *It;
+ }
+
+ if (!GoodOpName.empty())
+ return PatternType::getTypeOf(GoodOpName);
+
+ // No good operand found, give up.
+ return {};
+}
+
+std::vector<std::string> CombineRuleOperandTypeChecker::getMCOIOperandTypes(
+ const CodeGenInstructionPattern &CGP) {
+ // FIXME?: Should we cache this? We call it twice when inferring immediates.
+
+ static unsigned UnknownTypeIdx = 0;
+
+ std::vector<std::string> OpTypes;
+ auto &CGI = CGP.getInst();
+ Record *VarArgsTy = CGI.TheDef->isSubClassOf("GenericInstruction")
+ ? CGI.TheDef->getValueAsOptionalDef("variadicOpsType")
+ : nullptr;
+ std::string VarArgsTyName =
+ VarArgsTy ? ("MCOI::" + VarArgsTy->getValueAsString("OperandType")).str()
+ : ("unknown_type_" + Twine(UnknownTypeIdx++)).str();
+
+ // First, handle defs.
+ for (unsigned K = 0; K < CGI.Operands.NumDefs; ++K)
+ OpTypes.push_back(CGI.Operands[K].OperandType);
+
+ // Then, handle variadic defs if there are any.
+ if (CGP.hasVariadicDefs()) {
+ for (unsigned K = CGI.Operands.NumDefs; K < CGP.getNumInstDefs(); ++K)
+ OpTypes.push_back(VarArgsTyName);
+ }
+
+ // If we had variadic defs, the op idx in the pattern won't match the op idx
+ // in the CGI anymore.
+ int CGIOpOffset = int(CGI.Operands.NumDefs) - CGP.getNumInstDefs();
+ assert(CGP.hasVariadicDefs() ? (CGIOpOffset <= 0) : (CGIOpOffset == 0));
+
+ // Handle all remaining use operands, including variadic ones.
+ for (unsigned K = CGP.getNumInstDefs(); K < CGP.getNumInstOperands(); ++K) {
+ unsigned CGIOpIdx = K + CGIOpOffset;
+ if (CGIOpIdx >= CGI.Operands.size()) {
+ assert(CGP.isVariadic());
+ OpTypes.push_back(VarArgsTyName);
+ } else {
+ OpTypes.push_back(CGI.Operands[CGIOpIdx].OperandType);
+ }
+ }
+
+ assert(OpTypes.size() == CGP.operands_size());
+ return OpTypes;
+}
+
+void CombineRuleOperandTypeChecker::getInstEqClasses(
+ const InstructionPattern &P, TypeEquivalenceClasses &OutTECs) const {
+ // Determine the TypeEquivalenceClasses by:
+ // - Getting the MCOI Operand Types.
+ // - Creating a Map of MCOI Type -> [Operand Indexes]
+ // - Iterating over the map, filtering types we don't like, and just adding
+ // the array of Operand Indexes to \p OutTECs.
+
+ // We can only do this on CodeGenInstructions. Other InstructionPatterns have
+ // no type inference information associated with them.
+ // TODO: Could we add some inference information to builtins at least? e.g.
+ // ReplaceReg should always replace with a reg of the same type, for instance.
+ // Though, those patterns are often used alone so it might not be worth the
+ // trouble to infer their types.
+ auto *CGP = dyn_cast<CodeGenInstructionPattern>(&P);
+ if (!CGP)
+ return;
+
+ const auto MCOITypes = getMCOIOperandTypes(*CGP);
+ assert(MCOITypes.size() == P.operands_size());
+
+ DenseMap<StringRef, std::vector<unsigned>> TyToOpIdx;
+ for (const auto &[Idx, Ty] : enumerate(MCOITypes))
+ TyToOpIdx[Ty].push_back(Idx);
+
+ if (DebugTypeInfer)
+ errs() << "\tGroups for " << P.getName() << ":\t";
+
+ for (const auto &[Ty, Idxs] : TyToOpIdx) {
+ if (!canMCOIOperandTypeBeARegister(Ty))
+ continue;
+
+ if (DebugTypeInfer)
+ errs() << '[';
+ StringRef Sep = "";
+
+ // We only collect named operands.
+ StringRef Leader;
+ for (unsigned Idx : Idxs) {
+ const auto &Op = P.getOperand(Idx);
+ if (!Op.isNamedOperand())
+ continue;
+
+ const auto OpName = Op.getOperandName();
+ if (DebugTypeInfer) {
+ errs() << Sep << OpName;
+ Sep = ", ";
+ }
+
+ if (Leader.empty())
+ OutTECs.insert((Leader = OpName));
+ else
+ OutTECs.unionSets(Leader, OpName);
+ }
+
+ if (DebugTypeInfer)
+ errs() << "] ";
+ }
+
+ if (DebugTypeInfer)
+ errs() << '\n';
+}
+
+CombineRuleOperandTypeChecker::TypeEquivalenceClasses
+CombineRuleOperandTypeChecker::getRuleEqClasses() const {
+ StringMap<unsigned> OpNameToEqClassIdx;
+ TypeEquivalenceClasses TECs;
+
+ if (DebugTypeInfer)
+ errs() << "Rule Operand Type Equivalence Classes for " << RuleDef.getName()
+ << ":\n";
+
+ for (const auto *Pat : MatchPats)
+ getInstEqClasses(*Pat, TECs);
+ for (const auto *Pat : ApplyPats)
+ getInstEqClasses(*Pat, TECs);
+
+ if (DebugTypeInfer) {
+ errs() << "Final Type Equivalence Classes: ";
+ for (auto ClassIt = TECs.begin(); ClassIt != TECs.end(); ++ClassIt) {
+ // only print non-empty classes.
+ if (auto MembIt = TECs.member_begin(ClassIt);
+ MembIt != TECs.member_end()) {
+ errs() << '[';
+ StringRef Sep = "";
+ for (; MembIt != TECs.member_end(); ++MembIt) {
+ errs() << Sep << *MembIt;
+ Sep = ", ";
+ }
+ errs() << "] ";
+ }
+ }
+ errs() << '\n';
+ }
+
+ return TECs;
+}
+
+//===- CombineRuleBuilder -------------------------------------------------===//
+
+/// Parses combine rule and builds a small intermediate representation to tie
+/// patterns together and emit RuleMatchers to match them. This may emit more
+/// than one RuleMatcher, e.g. for `wip_match_opcode`.
+///
+/// Memory management for `Pattern` objects is done through `std::unique_ptr`.
+/// In most cases, there are two stages to a pattern's lifetime:
+/// - Creation in a `parse` function
+/// - The unique_ptr is stored in a variable, and may be destroyed if the
+/// pattern is found to be semantically invalid.
+/// - Ownership transfer into a `PatternMap`
+/// - Once a pattern is moved into either the map of Match or Apply
+/// patterns, it is known to be valid and it never moves back.
+class CombineRuleBuilder {
+public:
+ using PatternMap = MapVector<StringRef, std::unique_ptr<Pattern>>;
+ using PatternAlternatives = DenseMap<const Pattern *, unsigned>;
+
+ CombineRuleBuilder(const CodeGenTarget &CGT,
+ SubtargetFeatureInfoMap &SubtargetFeatures,
+ Record &RuleDef, unsigned ID,
+ std::vector<RuleMatcher> &OutRMs)
+ : CGT(CGT), SubtargetFeatures(SubtargetFeatures), RuleDef(RuleDef),
+ RuleID(ID), OutRMs(OutRMs) {}
+
+ /// Parses all fields in the RuleDef record.
+ bool parseAll();
+
+ /// Emits all RuleMatchers into the vector of RuleMatchers passed in the
+ /// constructor.
+ bool emitRuleMatchers();
+
+ void print(raw_ostream &OS) const;
+ void dump() const { print(dbgs()); }
+
+ /// Debug-only verification of invariants.
+#ifndef NDEBUG
+ void verify() const;
+#endif
+
+private:
+ const CodeGenInstruction &getGConstant() const {
+ return CGT.getInstruction(RuleDef.getRecords().getDef("G_CONSTANT"));
+ }
+
+ void PrintError(Twine Msg) const { ::PrintError(&RuleDef, Msg); }
+ void PrintWarning(Twine Msg) const { ::PrintWarning(RuleDef.getLoc(), Msg); }
+ void PrintNote(Twine Msg) const { ::PrintNote(RuleDef.getLoc(), Msg); }
+
+ void print(raw_ostream &OS, const PatternAlternatives &Alts) const;
+
+ bool addApplyPattern(std::unique_ptr<Pattern> Pat);
+ bool addMatchPattern(std::unique_ptr<Pattern> Pat);
+
+ /// Adds the expansions from \see MatchDatas to \p CE.
+ void declareAllMatchDatasExpansions(CodeExpansions &CE) const;
+
+ /// Adds a matcher \p P to \p IM, expanding its code using \p CE.
+ /// Note that the predicate is added on the last InstructionMatcher.
+ ///
+ /// \p Alts is only used if DebugCXXPreds is enabled.
+ void addCXXPredicate(RuleMatcher &M, const CodeExpansions &CE,
+ const CXXPattern &P, const PatternAlternatives &Alts);
+
+ /// Adds an apply \p P to \p IM, expanding its code using \p CE.
+ void addCXXAction(RuleMatcher &M, const CodeExpansions &CE,
+ const CXXPattern &P);
+
+ bool hasOnlyCXXApplyPatterns() const;
+ bool hasEraseRoot() const;
+
+ // Infer machine operand types and check their consistency.
+ bool typecheckPatterns();
+
+ /// For all PatFragPatterns, add a new entry in PatternAlternatives for each
+ /// PatternList it contains. This is multiplicative, so if we have 2
+ /// PatFrags with 3 alternatives each, we get 2*3 permutations added to
+ /// PermutationsToEmit. The "MaxPermutations" field controls how many
+ /// permutations are allowed before an error is emitted and this function
+ /// returns false. This is a simple safeguard to prevent combination of
+ /// PatFrags from generating enormous amounts of rules.
+ bool buildPermutationsToEmit();
+
+ /// Checks additional semantics of the Patterns.
+ bool checkSemantics();
+
+ /// Creates a new RuleMatcher with some boilerplate
+ /// settings/actions/predicates, and and adds it to \p OutRMs.
+ /// \see addFeaturePredicates too.
+ ///
+ /// \param Alts Current set of alternatives, for debug comment.
+ /// \param AdditionalComment Comment string to be added to the
+ /// `DebugCommentAction`.
+ RuleMatcher &addRuleMatcher(const PatternAlternatives &Alts,
+ Twine AdditionalComment = "");
+ bool addFeaturePredicates(RuleMatcher &M);
+
+ bool findRoots();
+ bool buildRuleOperandsTable();
+
+ bool parseDefs(const DagInit &Def);
+ bool
+ parsePatternList(const DagInit &List,
+ function_ref<bool(std::unique_ptr<Pattern>)> ParseAction,
+ StringRef Operator, ArrayRef<SMLoc> DiagLoc,
+ StringRef AnonPatNamePrefix) const;
+
+ std::unique_ptr<Pattern> parseInstructionPattern(const Init &Arg,
+ StringRef PatName) const;
+ std::unique_ptr<Pattern> parseWipMatchOpcodeMatcher(const Init &Arg,
+ StringRef PatName) const;
+ bool parseInstructionPatternOperand(InstructionPattern &IP,
+ const Init *OpInit,
+ const StringInit *OpName) const;
+ bool parseInstructionPatternMIFlags(InstructionPattern &IP,
+ const DagInit *Op) const;
+ std::unique_ptr<PatFrag> parsePatFragImpl(const Record *Def) const;
+ bool parsePatFragParamList(
+ ArrayRef<SMLoc> DiagLoc, const DagInit &OpsList,
+ function_ref<bool(StringRef, PatFrag::ParamKind)> ParseAction) const;
+ const PatFrag *parsePatFrag(const Record *Def) const;
+
+ bool emitMatchPattern(CodeExpansions &CE, const PatternAlternatives &Alts,
+ const InstructionPattern &IP);
+ bool emitMatchPattern(CodeExpansions &CE, const PatternAlternatives &Alts,
+ const AnyOpcodePattern &AOP);
+
+ bool emitPatFragMatchPattern(CodeExpansions &CE,
+ const PatternAlternatives &Alts, RuleMatcher &RM,
+ InstructionMatcher *IM,
+ const PatFragPattern &PFP,
+ DenseSet<const Pattern *> &SeenPats);
+
+ bool emitApplyPatterns(CodeExpansions &CE, RuleMatcher &M);
+
+ // Recursively visits InstructionPatterns from P to build up the
+ // RuleMatcher actions.
+ bool emitInstructionApplyPattern(CodeExpansions &CE, RuleMatcher &M,
+ const InstructionPattern &P,
+ DenseSet<const Pattern *> &SeenPats,
+ StringMap<unsigned> &OperandToTempRegID);
+
+ bool emitCodeGenInstructionApplyImmOperand(RuleMatcher &M,
+ BuildMIAction &DstMI,
+ const CodeGenInstructionPattern &P,
+ const InstructionOperand &O);
+
+ bool emitBuiltinApplyPattern(CodeExpansions &CE, RuleMatcher &M,
+ const BuiltinPattern &P,
+ StringMap<unsigned> &OperandToTempRegID);
+
+ // Recursively visits CodeGenInstructionPattern from P to build up the
+ // RuleMatcher/InstructionMatcher. May create new InstructionMatchers as
+ // needed.
+ using OperandMapperFnRef =
+ function_ref<InstructionOperand(const InstructionOperand &)>;
+ using OperandDefLookupFn =
+ function_ref<const InstructionPattern *(StringRef)>;
+ bool emitCodeGenInstructionMatchPattern(
+ CodeExpansions &CE, const PatternAlternatives &Alts, RuleMatcher &M,
+ InstructionMatcher &IM, const CodeGenInstructionPattern &P,
+ DenseSet<const Pattern *> &SeenPats, OperandDefLookupFn LookupOperandDef,
+ OperandMapperFnRef OperandMapper = [](const auto &O) { return O; });
+
+ const CodeGenTarget &CGT;
+ SubtargetFeatureInfoMap &SubtargetFeatures;
+ Record &RuleDef;
+ const unsigned RuleID;
+ std::vector<RuleMatcher> &OutRMs;
+
+ // For InstructionMatcher::addOperand
+ unsigned AllocatedTemporariesBaseID = 0;
+
+ /// The root of the pattern.
+ StringRef RootName;
+
+ /// These maps have ownership of the actual Pattern objects.
+ /// They both map a Pattern's name to the Pattern instance.
+ PatternMap MatchPats;
+ PatternMap ApplyPats;
+
+ /// Operand tables to tie match/apply patterns together.
+ OperandTable MatchOpTable;
+ OperandTable ApplyOpTable;
+
+ /// Set by findRoots.
+ Pattern *MatchRoot = nullptr;
+ SmallDenseSet<InstructionPattern *, 2> ApplyRoots;
+
+ SmallVector<MatchDataInfo, 2> MatchDatas;
+ SmallVector<PatternAlternatives, 1> PermutationsToEmit;
+
+ // print()/debug-only members.
+ mutable SmallPtrSet<const PatFrag *, 2> SeenPatFrags;
+};
+
+bool CombineRuleBuilder::parseAll() {
+ auto StackTrace = PrettyStackTraceParse(RuleDef);
+
+ if (!parseDefs(*RuleDef.getValueAsDag("Defs")))
+ return false;
+
+ if (!parsePatternList(
+ *RuleDef.getValueAsDag("Match"),
+ [this](auto Pat) { return addMatchPattern(std::move(Pat)); }, "match",
+ RuleDef.getLoc(), (RuleDef.getName() + "_match").str()))
+ return false;
+
+ if (!parsePatternList(
+ *RuleDef.getValueAsDag("Apply"),
+ [this](auto Pat) { return addApplyPattern(std::move(Pat)); }, "apply",
+ RuleDef.getLoc(), (RuleDef.getName() + "_apply").str()))
+ return false;
+
+ if (!buildRuleOperandsTable() || !typecheckPatterns() || !findRoots() ||
+ !checkSemantics() || !buildPermutationsToEmit())
+ return false;
+ LLVM_DEBUG(verify());
+ return true;
+}
+
+bool CombineRuleBuilder::emitRuleMatchers() {
+ auto StackTrace = PrettyStackTraceEmit(RuleDef);
+
+ assert(MatchRoot);
+ CodeExpansions CE;
+ declareAllMatchDatasExpansions(CE);
+
+ assert(!PermutationsToEmit.empty());
+ for (const auto &Alts : PermutationsToEmit) {
+ switch (MatchRoot->getKind()) {
+ case Pattern::K_AnyOpcode: {
+ if (!emitMatchPattern(CE, Alts, *cast<AnyOpcodePattern>(MatchRoot)))
+ return false;
+ break;
+ }
+ case Pattern::K_PatFrag:
+ case Pattern::K_Builtin:
+ case Pattern::K_CodeGenInstruction:
+ if (!emitMatchPattern(CE, Alts, *cast<InstructionPattern>(MatchRoot)))
+ return false;
+ break;
+ case Pattern::K_CXX:
+ PrintError("C++ code cannot be the root of a rule!");
+ return false;
+ default:
+ llvm_unreachable("unknown pattern kind!");
+ }
+ }
+
+ return true;
+}
+
+void CombineRuleBuilder::print(raw_ostream &OS) const {
+ OS << "(CombineRule name:" << RuleDef.getName() << " id:" << RuleID
+ << " root:" << RootName << '\n';
+
+ if (!MatchDatas.empty()) {
+ OS << " (MatchDatas\n";
+ for (const auto &MD : MatchDatas) {
+ OS << " ";
+ MD.print(OS);
+ OS << '\n';
+ }
+ OS << " )\n";
+ }
+
+ if (!SeenPatFrags.empty()) {
+ OS << " (PatFrags\n";
+ for (const auto *PF : SeenPatFrags) {
+ PF->print(OS, /*Indent=*/" ");
+ OS << '\n';
+ }
+ OS << " )\n";
+ }
+
+ const auto DumpPats = [&](StringRef Name, const PatternMap &Pats) {
+ OS << " (" << Name << " ";
+ if (Pats.empty()) {
+ OS << "<empty>)\n";
+ return;
+ }
+
+ OS << '\n';
+ for (const auto &[Name, Pat] : Pats) {
+ OS << " ";
+ if (Pat.get() == MatchRoot)
+ OS << "<match_root>";
+ if (isa<InstructionPattern>(Pat.get()) &&
+ ApplyRoots.contains(cast<InstructionPattern>(Pat.get())))
+ OS << "<apply_root>";
+ OS << Name << ":";
+ Pat->print(OS, /*PrintName=*/false);
+ OS << '\n';
+ }
+ OS << " )\n";
+ };
+
+ DumpPats("MatchPats", MatchPats);
+ DumpPats("ApplyPats", ApplyPats);
+
+ MatchOpTable.print(OS, "MatchPats", /*Indent*/ " ");
+ ApplyOpTable.print(OS, "ApplyPats", /*Indent*/ " ");
+
+ if (PermutationsToEmit.size() > 1) {
+ OS << " (PermutationsToEmit\n";
+ for (const auto &Perm : PermutationsToEmit) {
+ OS << " ";
+ print(OS, Perm);
+ OS << ",\n";
+ }
+ OS << " )\n";
+ }
+
+ OS << ")\n";
+}
+
+#ifndef NDEBUG
+void CombineRuleBuilder::verify() const {
+ const auto VerifyPats = [&](const PatternMap &Pats) {
+ for (const auto &[Name, Pat] : Pats) {
+ if (!Pat)
+ PrintFatalError("null pattern in pattern map!");
+
+ if (Name != Pat->getName()) {
+ Pat->dump();
+ PrintFatalError("Pattern name mismatch! Map name: " + Name +
+ ", Pat name: " + Pat->getName());
+ }
+
+ // Sanity check: the map should point to the same data as the Pattern.
+ // Both strings are allocated in the pool using insertStrRef.
+ if (Name.data() != Pat->getName().data()) {
+ dbgs() << "Map StringRef: '" << Name << "' @ "
+ << (const void *)Name.data() << '\n';
+ dbgs() << "Pat String: '" << Pat->getName() << "' @ "
+ << (const void *)Pat->getName().data() << '\n';
+ PrintFatalError("StringRef stored in the PatternMap is not referencing "
+ "the same string as its Pattern!");
+ }
+ }
+ };
+
+ VerifyPats(MatchPats);
+ VerifyPats(ApplyPats);
+
+ // Check there are no wip_match_opcode patterns in the "apply" patterns.
+ if (any_of(ApplyPats,
+ [&](auto &E) { return isa<AnyOpcodePattern>(E.second.get()); })) {
+ dump();
+ PrintFatalError(
+ "illegal wip_match_opcode pattern in the 'apply' patterns!");
+ }
+
+ // Check there are no nullptrs in ApplyRoots.
+ if (ApplyRoots.contains(nullptr)) {
+ PrintFatalError(
+ "CombineRuleBuilder's ApplyRoots set contains a null pointer!");
+ }
+}
+#endif
+
+void CombineRuleBuilder::print(raw_ostream &OS,
+ const PatternAlternatives &Alts) const {
+ SmallVector<std::string, 1> Strings(
+ map_range(Alts, [](const auto &PatAndPerm) {
+ return PatAndPerm.first->getName().str() + "[" +
+ to_string(PatAndPerm.second) + "]";
+ }));
+ // Sort so output is deterministic for tests. Otherwise it's sorted by pointer
+ // values.
+ sort(Strings);
+ OS << "[" << join(Strings, ", ") << "]";
+}
+
+bool CombineRuleBuilder::addApplyPattern(std::unique_ptr<Pattern> Pat) {
+ StringRef Name = Pat->getName();
+ if (ApplyPats.contains(Name)) {
+ PrintError("'" + Name + "' apply pattern defined more than once!");
+ return false;
+ }
+
+ if (isa<AnyOpcodePattern>(Pat.get())) {
+ PrintError("'" + Name +
+ "': wip_match_opcode is not supported in apply patterns");
+ return false;
+ }
+
+ if (isa<PatFragPattern>(Pat.get())) {
+ PrintError("'" + Name + "': using " + PatFrag::ClassName +
+ " is not supported in apply patterns");
+ return false;
+ }
+
+ if (auto *CXXPat = dyn_cast<CXXPattern>(Pat.get()))
+ CXXPat->setIsApply();
+
+ ApplyPats[Name] = std::move(Pat);
+ return true;
+}
+
+bool CombineRuleBuilder::addMatchPattern(std::unique_ptr<Pattern> Pat) {
+ StringRef Name = Pat->getName();
+ if (MatchPats.contains(Name)) {
+ PrintError("'" + Name + "' match pattern defined more than once!");
+ return false;
+ }
+
+ // For now, none of the builtins can appear in 'match'.
+ if (const auto *BP = dyn_cast<BuiltinPattern>(Pat.get())) {
+ PrintError("'" + BP->getInstName() +
+ "' cannot be used in a 'match' pattern");
+ return false;
+ }
+
+ MatchPats[Name] = std::move(Pat);
+ return true;
+}
+
+void CombineRuleBuilder::declareAllMatchDatasExpansions(
+ CodeExpansions &CE) const {
+ for (const auto &MD : MatchDatas)
+ CE.declare(MD.getPatternSymbol(), MD.getQualifiedVariableName());
+}
+
+void CombineRuleBuilder::addCXXPredicate(RuleMatcher &M,
+ const CodeExpansions &CE,
+ const CXXPattern &P,
+ const PatternAlternatives &Alts) {
+ // FIXME: Hack so C++ code is executed last. May not work for more complex
+ // patterns.
+ auto &IM = *std::prev(M.insnmatchers().end());
+ auto Loc = RuleDef.getLoc();
+ const auto AddComment = [&](raw_ostream &OS) {
+ OS << "// Pattern Alternatives: ";
+ print(OS, Alts);
+ OS << '\n';
+ };
+ const auto &ExpandedCode =
+ DebugCXXPreds ? P.expandCode(CE, Loc, AddComment) : P.expandCode(CE, Loc);
+ IM->addPredicate<GenericInstructionPredicateMatcher>(
+ ExpandedCode.getEnumNameWithPrefix(CXXPredPrefix));
+}
+
+void CombineRuleBuilder::addCXXAction(RuleMatcher &M, const CodeExpansions &CE,
+ const CXXPattern &P) {
+ const auto &ExpandedCode = P.expandCode(CE, RuleDef.getLoc());
+ M.addAction<CustomCXXAction>(
+ ExpandedCode.getEnumNameWithPrefix(CXXApplyPrefix));
+}
+
+bool CombineRuleBuilder::hasOnlyCXXApplyPatterns() const {
+ return all_of(ApplyPats, [&](auto &Entry) {
+ return isa<CXXPattern>(Entry.second.get());
+ });
+}
+
+bool CombineRuleBuilder::hasEraseRoot() const {
+ return any_of(ApplyPats, [&](auto &Entry) {
+ if (const auto *BP = dyn_cast<BuiltinPattern>(Entry.second.get()))
+ return BP->getBuiltinKind() == BI_EraseRoot;
+ return false;
+ });
+}
+
+bool CombineRuleBuilder::typecheckPatterns() {
+ CombineRuleOperandTypeChecker OTC(RuleDef, MatchOpTable);
+
+ for (auto &Pat : values(MatchPats)) {
+ if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {
+ if (!OTC.processMatchPattern(*IP))
+ return false;
+ }
+ }
+
+ for (auto &Pat : values(ApplyPats)) {
+ if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {
+ if (!OTC.processApplyPattern(*IP))
+ return false;
+ }
+ }
+
+ OTC.propagateAndInferTypes();
+
+ // Always check this after in case inference adds some special types to the
+ // match patterns.
+ for (auto &Pat : values(MatchPats)) {
+ if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {
+ if (IP->diagnoseAllSpecialTypes(
+ RuleDef.getLoc(), PatternType::SpecialTyClassName +
+ " is not supported in 'match' patterns")) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool CombineRuleBuilder::buildPermutationsToEmit() {
+ PermutationsToEmit.clear();
+
+ // Start with one empty set of alternatives.
+ PermutationsToEmit.emplace_back();
+ for (const auto &Pat : values(MatchPats)) {
+ unsigned NumAlts = 0;
+ // Note: technically, AnyOpcodePattern also needs permutations, but:
+ // - We only allow a single one of them in the root.
+ // - They cannot be mixed with any other pattern other than C++ code.
+ // So we don't really need to take them into account here. We could, but
+ // that pattern is a hack anyway and the less it's involved, the better.
+ if (const auto *PFP = dyn_cast<PatFragPattern>(Pat.get()))
+ NumAlts = PFP->getPatFrag().num_alternatives();
+ else
+ continue;
+
+ // For each pattern that needs permutations, multiply the current set of
+ // alternatives.
+ auto CurPerms = PermutationsToEmit;
+ PermutationsToEmit.clear();
+
+ for (const auto &Perm : CurPerms) {
+ assert(!Perm.count(Pat.get()) && "Pattern already emitted?");
+ for (unsigned K = 0; K < NumAlts; ++K) {
+ PatternAlternatives NewPerm = Perm;
+ NewPerm[Pat.get()] = K;
+ PermutationsToEmit.emplace_back(std::move(NewPerm));
+ }
+ }
+ }
+
+ if (int64_t MaxPerms = RuleDef.getValueAsInt("MaxPermutations");
+ MaxPerms > 0) {
+ if ((int64_t)PermutationsToEmit.size() > MaxPerms) {
+ PrintError("cannot emit rule '" + RuleDef.getName() + "'; " +
+ Twine(PermutationsToEmit.size()) +
+ " permutations would be emitted, but the max is " +
+ Twine(MaxPerms));
+ return false;
+ }
+ }
+
+ // Ensure we always have a single empty entry, it simplifies the emission
+ // logic so it doesn't need to handle the case where there are no perms.
+ if (PermutationsToEmit.empty()) {
+ PermutationsToEmit.emplace_back();
+ return true;
+ }
+
+ return true;
+}
+
+bool CombineRuleBuilder::checkSemantics() {
+ assert(MatchRoot && "Cannot call this before findRoots()");
+
+ bool UsesWipMatchOpcode = false;
+ for (const auto &Match : MatchPats) {
+ const auto *Pat = Match.second.get();
+
+ if (const auto *CXXPat = dyn_cast<CXXPattern>(Pat)) {
+ if (!CXXPat->getRawCode().contains("return "))
+ PrintWarning("'match' C++ code does not seem to return!");
+ continue;
+ }
+
+ // MIFlags in match cannot use the following syntax: (MIFlags $mi)
+ if (const auto *CGP = dyn_cast<CodeGenInstructionPattern>(Pat)) {
+ if (auto *FI = CGP->getMIFlagsInfo()) {
+ if (!FI->copy_flags().empty()) {
+ PrintError(
+ "'match' patterns cannot refer to flags from other instructions");
+ PrintNote("MIFlags in '" + CGP->getName() +
+ "' refer to: " + join(FI->copy_flags(), ", "));
+ return false;
+ }
+ }
+ }
+
+ const auto *AOP = dyn_cast<AnyOpcodePattern>(Pat);
+ if (!AOP)
+ continue;
+
+ if (UsesWipMatchOpcode) {
+ PrintError("wip_opcode_match can only be present once");
+ return false;
+ }
+
+ UsesWipMatchOpcode = true;
+ }
+
+ for (const auto &Apply : ApplyPats) {
+ assert(Apply.second.get());
+ const auto *IP = dyn_cast<InstructionPattern>(Apply.second.get());
+ if (!IP)
+ continue;
+
+ if (UsesWipMatchOpcode) {
+ PrintError("cannot use wip_match_opcode in combination with apply "
+ "instruction patterns!");
+ return false;
+ }
+
+ // Check that the insts mentioned in copy_flags exist.
+ if (const auto *CGP = dyn_cast<CodeGenInstructionPattern>(IP)) {
+ if (auto *FI = CGP->getMIFlagsInfo()) {
+ for (auto InstName : FI->copy_flags()) {
+ auto It = MatchPats.find(InstName);
+ if (It == MatchPats.end()) {
+ PrintError("unknown instruction '$" + InstName +
+ "' referenced in MIFlags of '" + CGP->getName() + "'");
+ return false;
+ }
+
+ if (!isa<CodeGenInstructionPattern>(It->second.get())) {
+ PrintError(
+ "'$" + InstName +
+ "' does not refer to a CodeGenInstruction in MIFlags of '" +
+ CGP->getName() + "'");
+ return false;
+ }
+ }
+ }
+ }
+
+ const auto *BIP = dyn_cast<BuiltinPattern>(IP);
+ if (!BIP)
+ continue;
+ StringRef Name = BIP->getInstName();
+
+ // (GIEraseInst) has to be the only apply pattern, or it can not be used at
+ // all. The root cannot have any defs either.
+ switch (BIP->getBuiltinKind()) {
+ case BI_EraseRoot: {
+ if (ApplyPats.size() > 1) {
+ PrintError(Name + " must be the only 'apply' pattern");
+ return false;
+ }
+
+ const auto *IRoot = dyn_cast<CodeGenInstructionPattern>(MatchRoot);
+ if (!IRoot) {
+ PrintError(Name +
+ " can only be used if the root is a CodeGenInstruction");
+ return false;
+ }
+
+ if (IRoot->getNumInstDefs() != 0) {
+ PrintError(Name + " can only be used if on roots that do "
+ "not have any output operand");
+ PrintNote("'" + IRoot->getInstName() + "' has " +
+ Twine(IRoot->getNumInstDefs()) + " output operands");
+ return false;
+ }
+ break;
+ }
+ case BI_ReplaceReg: {
+ // (GIReplaceReg can only be used on the root instruction)
+ // TODO: When we allow rewriting non-root instructions, also allow this.
+ StringRef OldRegName = BIP->getOperand(0).getOperandName();
+ auto *Def = MatchOpTable.getDef(OldRegName);
+ if (!Def) {
+ PrintError(Name + " cannot find a matched pattern that defines '" +
+ OldRegName + "'");
+ return false;
+ }
+ if (MatchOpTable.getDef(OldRegName) != MatchRoot) {
+ PrintError(Name + " cannot replace '" + OldRegName +
+ "': this builtin can only replace a register defined by the "
+ "match root");
+ return false;
+ }
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+RuleMatcher &CombineRuleBuilder::addRuleMatcher(const PatternAlternatives &Alts,
+ Twine AdditionalComment) {
+ auto &RM = OutRMs.emplace_back(RuleDef.getLoc());
+ addFeaturePredicates(RM);
+ RM.setPermanentGISelFlags(GISF_IgnoreCopies);
+ RM.addRequiredSimplePredicate(getIsEnabledPredicateEnumName(RuleID));
+
+ std::string Comment;
+ raw_string_ostream CommentOS(Comment);
+ CommentOS << "Combiner Rule #" << RuleID << ": " << RuleDef.getName();
+ if (!Alts.empty()) {
+ CommentOS << " @ ";
+ print(CommentOS, Alts);
+ }
+ if (!AdditionalComment.isTriviallyEmpty())
+ CommentOS << "; " << AdditionalComment;
+ RM.addAction<DebugCommentAction>(Comment);
+ return RM;
+}
+
+bool CombineRuleBuilder::addFeaturePredicates(RuleMatcher &M) {
+ if (!RuleDef.getValue("Predicates"))
+ return true;
+
+ ListInit *Preds = RuleDef.getValueAsListInit("Predicates");
+ for (Init *PI : Preds->getValues()) {
+ DefInit *Pred = dyn_cast<DefInit>(PI);
+ if (!Pred)
+ continue;
+
+ Record *Def = Pred->getDef();
+ if (!Def->isSubClassOf("Predicate")) {
+ ::PrintError(Def, "Unknown 'Predicate' Type");
+ return false;
+ }
+
+ if (Def->getValueAsString("CondString").empty())
+ continue;
+
+ if (SubtargetFeatures.count(Def) == 0) {
+ SubtargetFeatures.emplace(
+ Def, SubtargetFeatureInfo(Def, SubtargetFeatures.size()));
+ }
+
+ M.addRequiredFeature(Def);
+ }
+
+ return true;
+}
+
+bool CombineRuleBuilder::findRoots() {
+ const auto Finish = [&]() {
+ assert(MatchRoot);
+
+ if (hasOnlyCXXApplyPatterns() || hasEraseRoot())
+ return true;
+
+ auto *IPRoot = dyn_cast<InstructionPattern>(MatchRoot);
+ if (!IPRoot)
+ return true;
+
+ if (IPRoot->getNumInstDefs() == 0) {
+ // No defs to work with -> find the root using the pattern name.
+ auto It = ApplyPats.find(RootName);
+ if (It == ApplyPats.end()) {
+ PrintError("Cannot find root '" + RootName + "' in apply patterns!");
+ return false;
+ }
+
+ auto *ApplyRoot = dyn_cast<InstructionPattern>(It->second.get());
+ if (!ApplyRoot) {
+ PrintError("apply pattern root '" + RootName +
+ "' must be an instruction pattern");
+ return false;
+ }
+
+ ApplyRoots.insert(ApplyRoot);
+ return true;
+ }
+
+ // Collect all redefinitions of the MatchRoot's defs and put them in
+ // ApplyRoots.
+ const auto DefsNeeded = IPRoot->getApplyDefsNeeded();
+ for (auto &Op : DefsNeeded) {
+ assert(Op.isDef() && Op.isNamedOperand());
+ StringRef Name = Op.getOperandName();
+
+ auto *ApplyRedef = ApplyOpTable.getDef(Name);
+ if (!ApplyRedef) {
+ PrintError("'" + Name + "' must be redefined in the 'apply' pattern");
+ return false;
+ }
+
+ ApplyRoots.insert((InstructionPattern *)ApplyRedef);
+ }
+
+ if (auto It = ApplyPats.find(RootName); It != ApplyPats.end()) {
+ if (find(ApplyRoots, It->second.get()) == ApplyRoots.end()) {
+ PrintError("apply pattern '" + RootName +
+ "' is supposed to be a root but it does not redefine any of "
+ "the defs of the match root");
+ return false;
+ }
+ }
+
+ return true;
+ };
+
+ // Look by pattern name, e.g.
+ // (G_FNEG $x, $y):$root
+ if (auto MatchPatIt = MatchPats.find(RootName);
+ MatchPatIt != MatchPats.end()) {
+ MatchRoot = MatchPatIt->second.get();
+ return Finish();
+ }
+
+ // Look by def:
+ // (G_FNEG $root, $y)
+ auto LookupRes = MatchOpTable.lookup(RootName);
+ if (!LookupRes.Found) {
+ PrintError("Cannot find root '" + RootName + "' in match patterns!");
+ return false;
+ }
+
+ MatchRoot = LookupRes.Def;
+ if (!MatchRoot) {
+ PrintError("Cannot use live-in operand '" + RootName +
+ "' as match pattern root!");
+ return false;
+ }
+
+ return Finish();
+}
+
+bool CombineRuleBuilder::buildRuleOperandsTable() {
+ const auto DiagnoseRedefMatch = [&](StringRef OpName) {
+ PrintError("Operand '" + OpName +
+ "' is defined multiple times in the 'match' patterns");
+ };
+
+ const auto DiagnoseRedefApply = [&](StringRef OpName) {
+ PrintError("Operand '" + OpName +
+ "' is defined multiple times in the 'apply' patterns");
+ };
+
+ for (auto &Pat : values(MatchPats)) {
+ auto *IP = dyn_cast<InstructionPattern>(Pat.get());
+ if (IP && !MatchOpTable.addPattern(IP, DiagnoseRedefMatch))
+ return false;
+ }
+
+ for (auto &Pat : values(ApplyPats)) {
+ auto *IP = dyn_cast<InstructionPattern>(Pat.get());
+ if (IP && !ApplyOpTable.addPattern(IP, DiagnoseRedefApply))
+ return false;
+ }
+
+ return true;
+}
+
+bool CombineRuleBuilder::parseDefs(const DagInit &Def) {
+ if (Def.getOperatorAsDef(RuleDef.getLoc())->getName() != "defs") {
+ PrintError("Expected defs operator");
+ return false;
+ }
+
+ SmallVector<StringRef> Roots;
+ for (unsigned I = 0, E = Def.getNumArgs(); I < E; ++I) {
+ if (isSpecificDef(*Def.getArg(I), "root")) {
+ Roots.emplace_back(Def.getArgNameStr(I));
+ continue;
+ }
+
+ // Subclasses of GIDefMatchData should declare that this rule needs to pass
+ // data from the match stage to the apply stage, and ensure that the
+ // generated matcher has a suitable variable for it to do so.
+ if (Record *MatchDataRec =
+ getDefOfSubClass(*Def.getArg(I), "GIDefMatchData")) {
+ MatchDatas.emplace_back(Def.getArgNameStr(I),
+ MatchDataRec->getValueAsString("Type"));
+ continue;
+ }
+
+ // Otherwise emit an appropriate error message.
+ if (getDefOfSubClass(*Def.getArg(I), "GIDefKind"))
+ PrintError("This GIDefKind not implemented in tablegen");
+ else if (getDefOfSubClass(*Def.getArg(I), "GIDefKindWithArgs"))
+ PrintError("This GIDefKindWithArgs not implemented in tablegen");
+ else
+ PrintError("Expected a subclass of GIDefKind or a sub-dag whose "
+ "operator is of type GIDefKindWithArgs");
+ return false;
+ }
+
+ if (Roots.size() != 1) {
+ PrintError("Combine rules must have exactly one root");
+ return false;
+ }
+
+ RootName = Roots.front();
+
+ // Assign variables to all MatchDatas.
+ AssignMatchDataVariables(MatchDatas);
+ return true;
+}
+
+bool CombineRuleBuilder::parsePatternList(
+ const DagInit &List,
+ function_ref<bool(std::unique_ptr<Pattern>)> ParseAction,
+ StringRef Operator, ArrayRef<SMLoc> DiagLoc,
+ StringRef AnonPatNamePrefix) const {
+ if (List.getOperatorAsDef(RuleDef.getLoc())->getName() != Operator) {
+ ::PrintError(DiagLoc, "Expected " + Operator + " operator");
+ return false;
+ }
+
+ if (List.getNumArgs() == 0) {
+ ::PrintError(DiagLoc, Operator + " pattern list is empty");
+ return false;
+ }
+
+ // The match section consists of a list of matchers and predicates. Parse each
+ // one and add the equivalent GIMatchDag nodes, predicates, and edges.
+ for (unsigned I = 0; I < List.getNumArgs(); ++I) {
+ Init *Arg = List.getArg(I);
+ std::string Name = List.getArgName(I)
+ ? List.getArgName(I)->getValue().str()
+ : ("__" + AnonPatNamePrefix + "_" + Twine(I)).str();
+
+ if (auto Pat = parseInstructionPattern(*Arg, Name)) {
+ if (!ParseAction(std::move(Pat)))
+ return false;
+ continue;
+ }
+
+ if (auto Pat = parseWipMatchOpcodeMatcher(*Arg, Name)) {
+ if (!ParseAction(std::move(Pat)))
+ return false;
+ continue;
+ }
+
+ // Parse arbitrary C++ code
+ if (const auto *StringI = dyn_cast<StringInit>(Arg)) {
+ auto CXXPat = std::make_unique<CXXPattern>(*StringI, insertStrRef(Name));
+ if (!ParseAction(std::move(CXXPat)))
+ return false;
+ continue;
+ }
+
+ ::PrintError(DiagLoc,
+ "Failed to parse pattern: '" + Arg->getAsString() + "'");
+ return false;
+ }
+
+ return true;
+}
+
+std::unique_ptr<Pattern>
+CombineRuleBuilder::parseInstructionPattern(const Init &Arg,
+ StringRef Name) const {
+ const DagInit *DagPat = dyn_cast<DagInit>(&Arg);
+ if (!DagPat)
+ return nullptr;
+
+ std::unique_ptr<InstructionPattern> Pat;
+ if (const DagInit *IP = getDagWithOperatorOfSubClass(Arg, "Instruction")) {
+ auto &Instr = CGT.getInstruction(IP->getOperatorAsDef(RuleDef.getLoc()));
+ Pat =
+ std::make_unique<CodeGenInstructionPattern>(Instr, insertStrRef(Name));
+ } else if (const DagInit *PFP =
+ getDagWithOperatorOfSubClass(Arg, PatFrag::ClassName)) {
+ const Record *Def = PFP->getOperatorAsDef(RuleDef.getLoc());
+ const PatFrag *PF = parsePatFrag(Def);
+ if (!PF)
+ return nullptr; // Already diagnosed by parsePatFrag
+ Pat = std::make_unique<PatFragPattern>(*PF, insertStrRef(Name));
+ } else if (const DagInit *BP =
+ getDagWithOperatorOfSubClass(Arg, BuiltinPattern::ClassName)) {
+ Pat = std::make_unique<BuiltinPattern>(
+ *BP->getOperatorAsDef(RuleDef.getLoc()), insertStrRef(Name));
+ } else {
+ return nullptr;
+ }
+
+ for (unsigned K = 0; K < DagPat->getNumArgs(); ++K) {
+ Init *Arg = DagPat->getArg(K);
+ if (auto *DagArg = getDagWithSpecificOperator(*Arg, "MIFlags")) {
+ if (!parseInstructionPatternMIFlags(*Pat, DagArg))
+ return nullptr;
+ continue;
+ }
+
+ if (!parseInstructionPatternOperand(*Pat, Arg, DagPat->getArgName(K)))
+ return nullptr;
+ }
+
+ if (!Pat->checkSemantics(RuleDef.getLoc()))
+ return nullptr;
+
+ return std::move(Pat);
+}
+
+std::unique_ptr<Pattern>
+CombineRuleBuilder::parseWipMatchOpcodeMatcher(const Init &Arg,
+ StringRef Name) const {
+ const DagInit *Matcher = getDagWithSpecificOperator(Arg, "wip_match_opcode");
+ if (!Matcher)
+ return nullptr;
+
+ if (Matcher->getNumArgs() == 0) {
+ PrintError("Empty wip_match_opcode");
+ return nullptr;
+ }
+
+ // Each argument is an opcode that can match.
+ auto Result = std::make_unique<AnyOpcodePattern>(insertStrRef(Name));
+ for (const auto &Arg : Matcher->getArgs()) {
+ Record *OpcodeDef = getDefOfSubClass(*Arg, "Instruction");
+ if (OpcodeDef) {
+ Result->addOpcode(&CGT.getInstruction(OpcodeDef));
+ continue;
+ }
+
+ PrintError("Arguments to wip_match_opcode must be instructions");
+ return nullptr;
+ }
+
+ return std::move(Result);
+}
+
+bool CombineRuleBuilder::parseInstructionPatternOperand(
+ InstructionPattern &IP, const Init *OpInit,
+ const StringInit *OpName) const {
+ const auto ParseErr = [&]() {
+ PrintError("cannot parse operand '" + OpInit->getAsUnquotedString() + "' ");
+ if (OpName)
+ PrintNote("operand name is '" + OpName->getAsUnquotedString() + "'");
+ return false;
+ };
+
+ // untyped immediate, e.g. 0
+ if (const auto *IntImm = dyn_cast<IntInit>(OpInit)) {
+ std::string Name = OpName ? OpName->getAsUnquotedString() : "";
+ IP.addOperand(IntImm->getValue(), insertStrRef(Name), PatternType());
+ return true;
+ }
+
+ // typed immediate, e.g. (i32 0)
+ if (const auto *DagOp = dyn_cast<DagInit>(OpInit)) {
+ if (DagOp->getNumArgs() != 1)
+ return ParseErr();
+
+ const Record *TyDef = DagOp->getOperatorAsDef(RuleDef.getLoc());
+ auto ImmTy = PatternType::get(RuleDef.getLoc(), TyDef,
+ "cannot parse immediate '" +
+ DagOp->getAsUnquotedString() + "'");
+ if (!ImmTy)
+ return false;
+
+ if (!IP.hasAllDefs()) {
+ PrintError("out operand of '" + IP.getInstName() +
+ "' cannot be an immediate");
+ return false;
+ }
+
+ const auto *Val = dyn_cast<IntInit>(DagOp->getArg(0));
+ if (!Val)
+ return ParseErr();
+
+ std::string Name = OpName ? OpName->getAsUnquotedString() : "";
+ IP.addOperand(Val->getValue(), insertStrRef(Name), *ImmTy);
+ return true;
+ }
+
+ // Typed operand e.g. $x/$z in (G_FNEG $x, $z)
+ if (auto *DefI = dyn_cast<DefInit>(OpInit)) {
+ if (!OpName) {
+ PrintError("expected an operand name after '" + OpInit->getAsString() +
+ "'");
+ return false;
+ }
+ const Record *Def = DefI->getDef();
+ auto Ty =
+ PatternType::get(RuleDef.getLoc(), Def, "cannot parse operand type");
+ if (!Ty)
+ return false;
+ IP.addOperand(insertStrRef(OpName->getAsUnquotedString()), *Ty);
+ return true;
+ }
+
+ // Untyped operand e.g. $x/$z in (G_FNEG $x, $z)
+ if (isa<UnsetInit>(OpInit)) {
+ assert(OpName && "Unset w/ no OpName?");
+ IP.addOperand(insertStrRef(OpName->getAsUnquotedString()), PatternType());
+ return true;
+ }
+
+ return ParseErr();
+}
+
+bool CombineRuleBuilder::parseInstructionPatternMIFlags(
+ InstructionPattern &IP, const DagInit *Op) const {
+ auto *CGIP = dyn_cast<CodeGenInstructionPattern>(&IP);
+ if (!CGIP) {
+ PrintError("matching/writing MIFlags is only allowed on CodeGenInstruction "
+ "patterns");
+ return false;
+ }
+
+ const auto CheckFlagEnum = [&](const Record *R) {
+ if (!R->isSubClassOf(MIFlagsEnumClassName)) {
+ PrintError("'" + R->getName() + "' is not a subclass of '" +
+ MIFlagsEnumClassName + "'");
+ return false;
+ }
+
+ return true;
+ };
+
+ if (CGIP->getMIFlagsInfo()) {
+ PrintError("MIFlags can only be present once on an instruction");
+ return false;
+ }
+
+ auto &FI = CGIP->getOrCreateMIFlagsInfo();
+ for (unsigned K = 0; K < Op->getNumArgs(); ++K) {
+ const Init *Arg = Op->getArg(K);
+
+ // Match/set a flag: (MIFlags FmNoNans)
+ if (const auto *Def = dyn_cast<DefInit>(Arg)) {
+ const Record *R = Def->getDef();
+ if (!CheckFlagEnum(R))
+ return false;
+
+ FI.addSetFlag(R);
+ continue;
+ }
+
+ // Do not match a flag/unset a flag: (MIFlags (not FmNoNans))
+ if (const DagInit *NotDag = getDagWithSpecificOperator(*Arg, "not")) {
+ for (const Init *NotArg : NotDag->getArgs()) {
+ const DefInit *DefArg = dyn_cast<DefInit>(NotArg);
+ if (!DefArg) {
+ PrintError("cannot parse '" + NotArg->getAsUnquotedString() +
+ "': expected a '" + MIFlagsEnumClassName + "'");
+ return false;
+ }
+
+ const Record *R = DefArg->getDef();
+ if (!CheckFlagEnum(R))
+ return false;
+
+ FI.addUnsetFlag(R);
+ continue;
+ }
+
+ continue;
+ }
+
+ // Copy flags from a matched instruction: (MIFlags $mi)
+ if (isa<UnsetInit>(Arg)) {
+ FI.addCopyFlag(insertStrRef(Op->getArgName(K)->getAsUnquotedString()));
+ continue;
+ }
+ }
+
+ return true;
+}
+
+std::unique_ptr<PatFrag>
+CombineRuleBuilder::parsePatFragImpl(const Record *Def) const {
+ auto StackTrace = PrettyStackTraceParse(*Def);
+ if (!Def->isSubClassOf(PatFrag::ClassName))
+ return nullptr;
+
+ const DagInit *Ins = Def->getValueAsDag("InOperands");
+ if (Ins->getOperatorAsDef(Def->getLoc())->getName() != "ins") {
+ ::PrintError(Def, "expected 'ins' operator for " + PatFrag::ClassName +
+ " in operands list");
+ return nullptr;
+ }
+
+ const DagInit *Outs = Def->getValueAsDag("OutOperands");
+ if (Outs->getOperatorAsDef(Def->getLoc())->getName() != "outs") {
+ ::PrintError(Def, "expected 'outs' operator for " + PatFrag::ClassName +
+ " out operands list");
+ return nullptr;
+ }
+
+ auto Result = std::make_unique<PatFrag>(*Def);
+ if (!parsePatFragParamList(Def->getLoc(), *Outs,
+ [&](StringRef Name, PatFrag::ParamKind Kind) {
+ Result->addOutParam(insertStrRef(Name), Kind);
+ return true;
+ }))
+ return nullptr;
+
+ if (!parsePatFragParamList(Def->getLoc(), *Ins,
+ [&](StringRef Name, PatFrag::ParamKind Kind) {
+ Result->addInParam(insertStrRef(Name), Kind);
+ return true;
+ }))
+ return nullptr;
+
+ const ListInit *Alts = Def->getValueAsListInit("Alternatives");
+ unsigned AltIdx = 0;
+ for (const Init *Alt : *Alts) {
+ const auto *PatDag = dyn_cast<DagInit>(Alt);
+ if (!PatDag) {
+ ::PrintError(Def, "expected dag init for PatFrag pattern alternative");
+ return nullptr;
+ }
+
+ PatFrag::Alternative &A = Result->addAlternative();
+ const auto AddPat = [&](std::unique_ptr<Pattern> Pat) {
+ A.Pats.push_back(std::move(Pat));
+ return true;
+ };
+
+ if (!parsePatternList(
+ *PatDag, AddPat, "pattern", Def->getLoc(),
+ /*AnonPatPrefix*/
+ (Def->getName() + "_alt" + Twine(AltIdx++) + "_pattern").str()))
+ return nullptr;
+ }
+
+ if (!Result->buildOperandsTables() || !Result->checkSemantics())
+ return nullptr;
+
+ return Result;
+}
+
+bool CombineRuleBuilder::parsePatFragParamList(
+ ArrayRef<SMLoc> DiagLoc, const DagInit &OpsList,
+ function_ref<bool(StringRef, PatFrag::ParamKind)> ParseAction) const {
+ for (unsigned K = 0; K < OpsList.getNumArgs(); ++K) {
+ const StringInit *Name = OpsList.getArgName(K);
+ const Init *Ty = OpsList.getArg(K);
+
+ if (!Name) {
+ ::PrintError(DiagLoc, "all operands must be named'");
+ return false;
+ }
+ const std::string NameStr = Name->getAsUnquotedString();
+
+ PatFrag::ParamKind OpKind;
+ if (isSpecificDef(*Ty, "gi_imm"))
+ OpKind = PatFrag::PK_Imm;
+ else if (isSpecificDef(*Ty, "root"))
+ OpKind = PatFrag::PK_Root;
+ else if (isa<UnsetInit>(Ty) ||
+ isSpecificDef(*Ty, "gi_mo")) // no type = gi_mo.
+ OpKind = PatFrag::PK_MachineOperand;
+ else {
+ ::PrintError(
+ DiagLoc,
+ "'" + NameStr +
+ "' operand type was expected to be 'root', 'gi_imm' or 'gi_mo'");
+ return false;
+ }
+
+ if (!ParseAction(NameStr, OpKind))
+ return false;
+ }
+
+ return true;
+}
+
+const PatFrag *CombineRuleBuilder::parsePatFrag(const Record *Def) const {
+ // Cache already parsed PatFrags to avoid doing extra work.
+ static DenseMap<const Record *, std::unique_ptr<PatFrag>> ParsedPatFrags;
+
+ auto It = ParsedPatFrags.find(Def);
+ if (It != ParsedPatFrags.end()) {
+ SeenPatFrags.insert(It->second.get());
+ return It->second.get();
+ }
+
+ std::unique_ptr<PatFrag> NewPatFrag = parsePatFragImpl(Def);
+ if (!NewPatFrag) {
+ ::PrintError(Def, "Could not parse " + PatFrag::ClassName + " '" +
+ Def->getName() + "'");
+ // Put a nullptr in the map so we don't attempt parsing this again.
+ ParsedPatFrags[Def] = nullptr;
+ return nullptr;
+ }
+
+ const auto *Res = NewPatFrag.get();
+ ParsedPatFrags[Def] = std::move(NewPatFrag);
+ SeenPatFrags.insert(Res);
+ return Res;
+}
+
+bool CombineRuleBuilder::emitMatchPattern(CodeExpansions &CE,
+ const PatternAlternatives &Alts,
+ const InstructionPattern &IP) {
+ auto StackTrace = PrettyStackTraceEmit(RuleDef, &IP);
+
+ auto &M = addRuleMatcher(Alts);
+ InstructionMatcher &IM = M.addInstructionMatcher(IP.getName());
+ declareInstExpansion(CE, IM, IP.getName());
+
+ DenseSet<const Pattern *> SeenPats;
+
+ const auto FindOperandDef = [&](StringRef Op) -> InstructionPattern * {
+ return MatchOpTable.getDef(Op);
+ };
+
+ if (const auto *CGP = dyn_cast<CodeGenInstructionPattern>(&IP)) {
+ if (!emitCodeGenInstructionMatchPattern(CE, Alts, M, IM, *CGP, SeenPats,
+ FindOperandDef))
+ return false;
+ } else if (const auto *PFP = dyn_cast<PatFragPattern>(&IP)) {
+ if (!PFP->getPatFrag().canBeMatchRoot()) {
+ PrintError("cannot use '" + PFP->getInstName() + " as match root");
+ return false;
+ }
+
+ if (!emitPatFragMatchPattern(CE, Alts, M, &IM, *PFP, SeenPats))
+ return false;
+ } else if (isa<BuiltinPattern>(&IP)) {
+ llvm_unreachable("No match builtins known!");
+ } else
+ llvm_unreachable("Unknown kind of InstructionPattern!");
+
+ // Emit remaining patterns
+ for (auto &Pat : values(MatchPats)) {
+ if (SeenPats.contains(Pat.get()))
+ continue;
+
+ switch (Pat->getKind()) {
+ case Pattern::K_AnyOpcode:
+ PrintError("wip_match_opcode can not be used with instruction patterns!");
+ return false;
+ case Pattern::K_PatFrag: {
+ if (!emitPatFragMatchPattern(CE, Alts, M, /*IM*/ nullptr,
+ *cast<PatFragPattern>(Pat.get()), SeenPats))
+ return false;
+ continue;
+ }
+ case Pattern::K_Builtin:
+ PrintError("No known match builtins");
+ return false;
+ case Pattern::K_CodeGenInstruction:
+ cast<InstructionPattern>(Pat.get())->reportUnreachable(RuleDef.getLoc());
+ return false;
+ case Pattern::K_CXX: {
+ addCXXPredicate(M, CE, *cast<CXXPattern>(Pat.get()), Alts);
+ continue;
+ }
+ default:
+ llvm_unreachable("unknown pattern kind!");
+ }
+ }
+
+ return emitApplyPatterns(CE, M);
+}
+
+bool CombineRuleBuilder::emitMatchPattern(CodeExpansions &CE,
+ const PatternAlternatives &Alts,
+ const AnyOpcodePattern &AOP) {
+ auto StackTrace = PrettyStackTraceEmit(RuleDef, &AOP);
+
+ for (const CodeGenInstruction *CGI : AOP.insts()) {
+ auto &M = addRuleMatcher(Alts, "wip_match_opcode '" +
+ CGI->TheDef->getName() + "'");
+
+ InstructionMatcher &IM = M.addInstructionMatcher(AOP.getName());
+ declareInstExpansion(CE, IM, AOP.getName());
+ // declareInstExpansion needs to be identical, otherwise we need to create a
+ // CodeExpansions object here instead.
+ assert(IM.getInsnVarID() == 0);
+
+ IM.addPredicate<InstructionOpcodeMatcher>(CGI);
+
+ // Emit remaining patterns.
+ for (auto &Pat : values(MatchPats)) {
+ if (Pat.get() == &AOP)
+ continue;
+
+ switch (Pat->getKind()) {
+ case Pattern::K_AnyOpcode:
+ PrintError("wip_match_opcode can only be present once!");
+ return false;
+ case Pattern::K_PatFrag: {
+ DenseSet<const Pattern *> SeenPats;
+ if (!emitPatFragMatchPattern(CE, Alts, M, /*IM*/ nullptr,
+ *cast<PatFragPattern>(Pat.get()),
+ SeenPats))
+ return false;
+ continue;
+ }
+ case Pattern::K_Builtin:
+ PrintError("No known match builtins");
+ return false;
+ case Pattern::K_CodeGenInstruction:
+ cast<InstructionPattern>(Pat.get())->reportUnreachable(
+ RuleDef.getLoc());
+ return false;
+ case Pattern::K_CXX: {
+ addCXXPredicate(M, CE, *cast<CXXPattern>(Pat.get()), Alts);
+ break;
+ }
+ default:
+ llvm_unreachable("unknown pattern kind!");
+ }
+ }
+
+ if (!emitApplyPatterns(CE, M))
+ return false;
+ }
+
+ return true;
+}
+
+bool CombineRuleBuilder::emitPatFragMatchPattern(
+ CodeExpansions &CE, const PatternAlternatives &Alts, RuleMatcher &RM,
+ InstructionMatcher *IM, const PatFragPattern &PFP,
+ DenseSet<const Pattern *> &SeenPats) {
+ auto StackTrace = PrettyStackTraceEmit(RuleDef, &PFP);
+
+ if (SeenPats.contains(&PFP))
+ return true;
+ SeenPats.insert(&PFP);
+
+ const auto &PF = PFP.getPatFrag();
+
+ if (!IM) {
+ // When we don't have an IM, this means this PatFrag isn't reachable from
+ // the root. This is only acceptable if it doesn't define anything (e.g. a
+ // pure C++ PatFrag).
+ if (PF.num_out_params() != 0) {
+ PFP.reportUnreachable(RuleDef.getLoc());
+ return false;
+ }
+ } else {
+ // When an IM is provided, this is reachable from the root, and we're
+ // expecting to have output operands.
+ // TODO: If we want to allow for multiple roots we'll need a map of IMs
+ // then, and emission becomes a bit more complicated.
+ assert(PF.num_roots() == 1);
+ }
+
+ CodeExpansions PatFragCEs;
+ if (!PFP.mapInputCodeExpansions(CE, PatFragCEs, RuleDef.getLoc()))
+ return false;
+
+ // List of {ParamName, ArgName}.
+ // When all patterns have been emitted, find expansions in PatFragCEs named
+ // ArgName and add their expansion to CE using ParamName as the key.
+ SmallVector<std::pair<std::string, std::string>, 4> CEsToImport;
+
+ // Map parameter names to the actual argument.
+ const auto OperandMapper =
+ [&](const InstructionOperand &O) -> InstructionOperand {
+ if (!O.isNamedOperand())
+ return O;
+
+ StringRef ParamName = O.getOperandName();
+
+ // Not sure what to do with those tbh. They should probably never be here.
+ assert(!O.isNamedImmediate() && "TODO: handle named imms");
+ unsigned PIdx = PF.getParamIdx(ParamName);
+
+ // Map parameters to the argument values.
+ if (PIdx == (unsigned)-1) {
+ // This is a temp of the PatFragPattern, prefix the name to avoid
+ // conflicts.
+ return O.withNewName(
+ insertStrRef((PFP.getName() + "." + ParamName).str()));
+ }
+
+ // The operand will be added to PatFragCEs's code expansions using the
+ // parameter's name. If it's bound to some operand during emission of the
+ // patterns, we'll want to add it to CE.
+ auto ArgOp = PFP.getOperand(PIdx);
+ if (ArgOp.isNamedOperand())
+ CEsToImport.emplace_back(ArgOp.getOperandName().str(), ParamName);
+
+ if (ArgOp.getType() && O.getType() && ArgOp.getType() != O.getType()) {
+ StringRef PFName = PF.getName();
+ PrintWarning("impossible type constraints: operand " + Twine(PIdx) +
+ " of '" + PFP.getName() + "' has type '" +
+ ArgOp.getType().str() + "', but '" + PFName +
+ "' constrains it to '" + O.getType().str() + "'");
+ if (ArgOp.isNamedOperand())
+ PrintNote("operand " + Twine(PIdx) + " of '" + PFP.getName() +
+ "' is '" + ArgOp.getOperandName() + "'");
+ if (O.isNamedOperand())
+ PrintNote("argument " + Twine(PIdx) + " of '" + PFName + "' is '" +
+ ParamName + "'");
+ }
+
+ return ArgOp;
+ };
+
+ // PatFragPatterns are only made of InstructionPatterns or CXXPatterns.
+ // Emit instructions from the root.
+ const auto &FragAlt = PF.getAlternative(Alts.lookup(&PFP));
+ const auto &FragAltOT = FragAlt.OpTable;
+ const auto LookupOperandDef =
+ [&](StringRef Op) -> const InstructionPattern * {
+ return FragAltOT.getDef(Op);
+ };
+
+ DenseSet<const Pattern *> PatFragSeenPats;
+ for (const auto &[Idx, InOp] : enumerate(PF.out_params())) {
+ if (InOp.Kind != PatFrag::PK_Root)
+ continue;
+
+ StringRef ParamName = InOp.Name;
+ const auto *Def = FragAltOT.getDef(ParamName);
+ assert(Def && "PatFrag::checkSemantics should have emitted an error if "
+ "an out operand isn't defined!");
+ assert(isa<CodeGenInstructionPattern>(Def) &&
+ "Nested PatFrags not supported yet");
+
+ if (!emitCodeGenInstructionMatchPattern(
+ PatFragCEs, Alts, RM, *IM, *cast<CodeGenInstructionPattern>(Def),
+ PatFragSeenPats, LookupOperandDef, OperandMapper))
+ return false;
+ }
+
+ // Emit leftovers.
+ for (const auto &Pat : FragAlt.Pats) {
+ if (PatFragSeenPats.contains(Pat.get()))
+ continue;
+
+ if (const auto *CXXPat = dyn_cast<CXXPattern>(Pat.get())) {
+ addCXXPredicate(RM, PatFragCEs, *CXXPat, Alts);
+ continue;
+ }
+
+ if (const auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {
+ IP->reportUnreachable(PF.getLoc());
+ return false;
+ }
+
+ llvm_unreachable("Unexpected pattern kind in PatFrag");
+ }
+
+ for (const auto &[ParamName, ArgName] : CEsToImport) {
+ // Note: we're find if ParamName already exists. It just means it's been
+ // bound before, so we prefer to keep the first binding.
+ CE.declare(ParamName, PatFragCEs.lookup(ArgName));
+ }
+
+ return true;
+}
+
+bool CombineRuleBuilder::emitApplyPatterns(CodeExpansions &CE, RuleMatcher &M) {
+ if (hasOnlyCXXApplyPatterns()) {
+ for (auto &Pat : values(ApplyPats))
+ addCXXAction(M, CE, *cast<CXXPattern>(Pat.get()));
+ return true;
+ }
+
+ DenseSet<const Pattern *> SeenPats;
+ StringMap<unsigned> OperandToTempRegID;
+
+ for (auto *ApplyRoot : ApplyRoots) {
+ assert(isa<InstructionPattern>(ApplyRoot) &&
+ "Root can only be a InstructionPattern!");
+ if (!emitInstructionApplyPattern(CE, M,
+ cast<InstructionPattern>(*ApplyRoot),
+ SeenPats, OperandToTempRegID))
+ return false;
+ }
+
+ for (auto &Pat : values(ApplyPats)) {
+ if (SeenPats.contains(Pat.get()))
+ continue;
+
+ switch (Pat->getKind()) {
+ case Pattern::K_AnyOpcode:
+ llvm_unreachable("Unexpected pattern in apply!");
+ case Pattern::K_PatFrag:
+ // TODO: We could support pure C++ PatFrags as a temporary thing.
+ llvm_unreachable("Unexpected pattern in apply!");
+ case Pattern::K_Builtin:
+ if (!emitInstructionApplyPattern(CE, M, cast<BuiltinPattern>(*Pat),
+ SeenPats, OperandToTempRegID))
+ return false;
+ break;
+ case Pattern::K_CodeGenInstruction:
+ cast<CodeGenInstructionPattern>(*Pat).reportUnreachable(RuleDef.getLoc());
+ return false;
+ case Pattern::K_CXX: {
+ addCXXAction(M, CE, *cast<CXXPattern>(Pat.get()));
+ continue;
+ }
+ default:
+ llvm_unreachable("unknown pattern kind!");
+ }
+ }
+
+ return true;
+}
+
+bool CombineRuleBuilder::emitInstructionApplyPattern(
+ CodeExpansions &CE, RuleMatcher &M, const InstructionPattern &P,
+ DenseSet<const Pattern *> &SeenPats,
+ StringMap<unsigned> &OperandToTempRegID) {
+ auto StackTrace = PrettyStackTraceEmit(RuleDef, &P);
+
+ if (SeenPats.contains(&P))
+ return true;
+
+ SeenPats.insert(&P);
+
+ // First, render the uses.
+ for (auto &Op : P.named_operands()) {
+ if (Op.isDef())
+ continue;
+
+ StringRef OpName = Op.getOperandName();
+ if (const auto *DefPat = ApplyOpTable.getDef(OpName)) {
+ if (!emitInstructionApplyPattern(CE, M, *DefPat, SeenPats,
+ OperandToTempRegID))
+ return false;
+ } else {
+ // If we have no def, check this exists in the MatchRoot.
+ if (!Op.isNamedImmediate() && !MatchOpTable.lookup(OpName).Found) {
+ PrintError("invalid output operand '" + OpName +
+ "': operand is not a live-in of the match pattern, and it "
+ "has no definition");
+ return false;
+ }
+ }
+ }
+
+ if (const auto *BP = dyn_cast<BuiltinPattern>(&P))
+ return emitBuiltinApplyPattern(CE, M, *BP, OperandToTempRegID);
+
+ if (isa<PatFragPattern>(&P))
+ llvm_unreachable("PatFragPatterns is not supported in 'apply'!");
+
+ auto &CGIP = cast<CodeGenInstructionPattern>(P);
+
+ // Now render this inst.
+ auto &DstMI =
+ M.addAction<BuildMIAction>(M.allocateOutputInsnID(), &CGIP.getInst());
+
+ for (auto &Op : P.operands()) {
+ if (Op.isNamedImmediate()) {
+ PrintError("invalid output operand '" + Op.getOperandName() +
+ "': output immediates cannot be named");
+ PrintNote("while emitting pattern '" + P.getName() + "' (" +
+ P.getInstName() + ")");
+ return false;
+ }
+
+ if (Op.hasImmValue()) {
+ if (!emitCodeGenInstructionApplyImmOperand(M, DstMI, CGIP, Op))
+ return false;
+ continue;
+ }
+
+ StringRef OpName = Op.getOperandName();
+
+ // Uses of operand.
+ if (!Op.isDef()) {
+ if (auto It = OperandToTempRegID.find(OpName);
+ It != OperandToTempRegID.end()) {
+ assert(!MatchOpTable.lookup(OpName).Found &&
+ "Temp reg is also from match pattern?");
+ DstMI.addRenderer<TempRegRenderer>(It->second);
+ } else {
+ // This should be a match live in or a redef of a matched instr.
+ // If it's a use of a temporary register, then we messed up somewhere -
+ // the previous condition should have passed.
+ assert(MatchOpTable.lookup(OpName).Found &&
+ !ApplyOpTable.getDef(OpName) && "Temp reg not emitted yet!");
+ DstMI.addRenderer<CopyRenderer>(OpName);
+ }
+ continue;
+ }
+
+ // Determine what we're dealing with. Are we replace a matched instruction?
+ // Creating a new one?
+ auto OpLookupRes = MatchOpTable.lookup(OpName);
+ if (OpLookupRes.Found) {
+ if (OpLookupRes.isLiveIn()) {
+ // live-in of the match pattern.
+ PrintError("Cannot define live-in operand '" + OpName +
+ "' in the 'apply' pattern");
+ return false;
+ }
+ assert(OpLookupRes.Def);
+
+ // TODO: Handle this. We need to mutate the instr, or delete the old
+ // one.
+ // Likewise, we also need to ensure we redef everything, if the
+ // instr has more than one def, we need to redef all or nothing.
+ if (OpLookupRes.Def != MatchRoot) {
+ PrintError("redefining an instruction other than the root is not "
+ "supported (operand '" +
+ OpName + "')");
+ return false;
+ }
+ // redef of a match
+ DstMI.addRenderer<CopyRenderer>(OpName);
+ continue;
+ }
+
+ // Define a new register unique to the apply patterns (AKA a "temp"
+ // register).
+ unsigned TempRegID;
+ if (auto It = OperandToTempRegID.find(OpName);
+ It != OperandToTempRegID.end()) {
+ TempRegID = It->second;
+ } else {
+ // This is a brand new register.
+ TempRegID = M.allocateTempRegID();
+ OperandToTempRegID[OpName] = TempRegID;
+ const auto Ty = Op.getType();
+ if (!Ty) {
+ PrintError("def of a new register '" + OpName +
+ "' in the apply patterns must have a type");
+ return false;
+ }
+
+ declareTempRegExpansion(CE, TempRegID, OpName);
+ // Always insert the action at the beginning, otherwise we may end up
+ // using the temp reg before it's available.
+ M.insertAction<MakeTempRegisterAction>(
+ M.actions_begin(), getLLTCodeGenOrTempType(Ty, M), TempRegID);
+ }
+
+ DstMI.addRenderer<TempRegRenderer>(TempRegID);
+ }
+
+ // Render MIFlags
+ if (const auto *FI = CGIP.getMIFlagsInfo()) {
+ for (StringRef InstName : FI->copy_flags())
+ DstMI.addCopiedMIFlags(M.getInstructionMatcher(InstName));
+ for (StringRef F : FI->set_flags())
+ DstMI.addSetMIFlags(F);
+ for (StringRef F : FI->unset_flags())
+ DstMI.addUnsetMIFlags(F);
+ }
+
+ // Don't allow mutating opcodes for GISel combiners. We want a more precise
+ // handling of MIFlags so we require them to be explicitly preserved.
+ //
+ // TODO: We don't mutate very often, if at all in combiners, but it'd be nice
+ // to re-enable this. We'd then need to always clear MIFlags when mutating
+ // opcodes, and never mutate an inst that we copy flags from.
+ // DstMI.chooseInsnToMutate(M);
+ declareInstExpansion(CE, DstMI, P.getName());
+
+ return true;
+}
+
+bool CombineRuleBuilder::emitCodeGenInstructionApplyImmOperand(
+ RuleMatcher &M, BuildMIAction &DstMI, const CodeGenInstructionPattern &P,
+ const InstructionOperand &O) {
+ // If we have a type, we implicitly emit a G_CONSTANT, except for G_CONSTANT
+ // itself where we emit a CImm.
+ //
+ // No type means we emit a simple imm.
+ // G_CONSTANT is a special case and needs a CImm though so this is likely a
+ // mistake.
+ const bool isGConstant = P.is("G_CONSTANT");
+ const auto Ty = O.getType();
+ if (!Ty) {
+ if (isGConstant) {
+ PrintError("'G_CONSTANT' immediate must be typed!");
+ PrintNote("while emitting pattern '" + P.getName() + "' (" +
+ P.getInstName() + ")");
+ return false;
+ }
+
+ DstMI.addRenderer<ImmRenderer>(O.getImmValue());
+ return true;
+ }
+
+ auto ImmTy = getLLTCodeGenOrTempType(Ty, M);
+
+ if (isGConstant) {
+ DstMI.addRenderer<ImmRenderer>(O.getImmValue(), ImmTy);
+ return true;
+ }
+
+ unsigned TempRegID = M.allocateTempRegID();
+ // Ensure MakeTempReg & the BuildConstantAction occur at the beginning.
+ auto InsertIt = M.insertAction<MakeTempRegisterAction>(M.actions_begin(),
+ ImmTy, TempRegID);
+ M.insertAction<BuildConstantAction>(++InsertIt, TempRegID, O.getImmValue());
+ DstMI.addRenderer<TempRegRenderer>(TempRegID);
+ return true;
+}
+
+bool CombineRuleBuilder::emitBuiltinApplyPattern(
+ CodeExpansions &CE, RuleMatcher &M, const BuiltinPattern &P,
+ StringMap<unsigned> &OperandToTempRegID) {
+ const auto Error = [&](Twine Reason) {
+ PrintError("cannot emit '" + P.getInstName() + "' builtin: " + Reason);
+ return false;
+ };
+
+ switch (P.getBuiltinKind()) {
+ case BI_EraseRoot: {
+ // Root is always inst 0.
+ M.addAction<EraseInstAction>(/*InsnID*/ 0);
+ return true;
+ }
+ case BI_ReplaceReg: {
+ StringRef Old = P.getOperand(0).getOperandName();
+ StringRef New = P.getOperand(1).getOperandName();
+
+ if (!ApplyOpTable.lookup(New).Found && !MatchOpTable.lookup(New).Found)
+ return Error("unknown operand '" + Old + "'");
+
+ auto &OldOM = M.getOperandMatcher(Old);
+ if (auto It = OperandToTempRegID.find(New);
+ It != OperandToTempRegID.end()) {
+ // Replace with temp reg.
+ M.addAction<ReplaceRegAction>(OldOM.getInsnVarID(), OldOM.getOpIdx(),
+ It->second);
+ } else {
+ // Replace with matched reg.
+ auto &NewOM = M.getOperandMatcher(New);
+ M.addAction<ReplaceRegAction>(OldOM.getInsnVarID(), OldOM.getOpIdx(),
+ NewOM.getInsnVarID(), NewOM.getOpIdx());
+ }
+ // checkSemantics should have ensured that we can only rewrite the root.
+ // Ensure we're deleting it.
+ assert(MatchOpTable.getDef(Old) == MatchRoot);
+ // TODO: We could avoid adding the action again if it's already in. The
+ // MatchTable is smart enough to only emit one opcode even if
+ // EraseInstAction is present multiple times. I think searching for a copy
+ // is more expensive than just blindly adding it though.
+ M.addAction<EraseInstAction>(/*InsnID*/ 0);
+
+ return true;
+ }
+ }
+
+ llvm_unreachable("Unknown BuiltinKind!");
+}
+
+bool isLiteralImm(const InstructionPattern &P, unsigned OpIdx) {
+ if (const auto *CGP = dyn_cast<CodeGenInstructionPattern>(&P)) {
+ StringRef InstName = CGP->getInst().TheDef->getName();
+ return (InstName == "G_CONSTANT" || InstName == "G_FCONSTANT") &&
+ OpIdx == 1;
+ }
+
+ llvm_unreachable("TODO");
+}
+
+bool CombineRuleBuilder::emitCodeGenInstructionMatchPattern(
+ CodeExpansions &CE, const PatternAlternatives &Alts, RuleMatcher &M,
+ InstructionMatcher &IM, const CodeGenInstructionPattern &P,
+ DenseSet<const Pattern *> &SeenPats, OperandDefLookupFn LookupOperandDef,
+ OperandMapperFnRef OperandMapper) {
+ auto StackTrace = PrettyStackTraceEmit(RuleDef, &P);
+
+ if (SeenPats.contains(&P))
+ return true;
+
+ SeenPats.insert(&P);
+
+ IM.addPredicate<InstructionOpcodeMatcher>(&P.getInst());
+ declareInstExpansion(CE, IM, P.getName());
+
+ // Check flags if needed.
+ if (const auto *FI = P.getMIFlagsInfo()) {
+ assert(FI->copy_flags().empty());
+
+ if (const auto &SetF = FI->set_flags(); !SetF.empty())
+ IM.addPredicate<MIFlagsInstructionPredicateMatcher>(SetF.getArrayRef());
+ if (const auto &UnsetF = FI->unset_flags(); !UnsetF.empty())
+ IM.addPredicate<MIFlagsInstructionPredicateMatcher>(UnsetF.getArrayRef(),
+ /*CheckNot=*/true);
+ }
+
+ for (const auto &[Idx, OriginalO] : enumerate(P.operands())) {
+ // Remap the operand. This is used when emitting InstructionPatterns inside
+ // PatFrags, so it can remap them to the arguments passed to the pattern.
+ //
+ // We use the remapped operand to emit immediates, and for the symbolic
+ // operand names (in IM.addOperand). CodeExpansions and OperandTable lookups
+ // still use the original name.
+ //
+ // The "def" flag on the remapped operand is always ignored.
+ auto RemappedO = OperandMapper(OriginalO);
+ assert(RemappedO.isNamedOperand() == OriginalO.isNamedOperand() &&
+ "Cannot remap an unnamed operand to a named one!");
+
+ const auto OpName =
+ RemappedO.isNamedOperand() ? RemappedO.getOperandName().str() : "";
+ OperandMatcher &OM =
+ IM.addOperand(Idx, OpName, AllocatedTemporariesBaseID++);
+ if (!OpName.empty())
+ declareOperandExpansion(CE, OM, OriginalO.getOperandName());
+
+ // Handle immediates.
+ if (RemappedO.hasImmValue()) {
+ if (isLiteralImm(P, Idx))
+ OM.addPredicate<LiteralIntOperandMatcher>(RemappedO.getImmValue());
+ else
+ OM.addPredicate<ConstantIntOperandMatcher>(RemappedO.getImmValue());
+ }
+
+ // Handle typed operands, but only bother to check if it hasn't been done
+ // before.
+ //
+ // getOperandMatcher will always return the first OM to have been created
+ // for that Operand. "OM" here is always a new OperandMatcher.
+ //
+ // Always emit a check for unnamed operands.
+ if (OpName.empty() ||
+ !M.getOperandMatcher(OpName).contains<LLTOperandMatcher>()) {
+ if (const auto Ty = RemappedO.getType()) {
+ // TODO: We could support GITypeOf here on the condition that the
+ // OperandMatcher exists already. Though it's clunky to make this work
+ // and isn't all that useful so it's just rejected in typecheckPatterns
+ // at this time.
+ assert(Ty.isLLT() && "Only LLTs are supported in match patterns!");
+ OM.addPredicate<LLTOperandMatcher>(getLLTCodeGen(Ty));
+ }
+ }
+
+ // Stop here if the operand is a def, or if it had no name.
+ if (OriginalO.isDef() || !OriginalO.isNamedOperand())
+ continue;
+
+ const auto *DefPat = LookupOperandDef(OriginalO.getOperandName());
+ if (!DefPat)
+ continue;
+
+ if (OriginalO.hasImmValue()) {
+ assert(!OpName.empty());
+ // This is a named immediate that also has a def, that's not okay.
+ // e.g.
+ // (G_SEXT $y, (i32 0))
+ // (COPY $x, 42:$y)
+ PrintError("'" + OpName +
+ "' is a named immediate, it cannot be defined by another "
+ "instruction");
+ PrintNote("'" + OpName + "' is defined by '" + DefPat->getName() + "'");
+ return false;
+ }
+
+ // From here we know that the operand defines an instruction, and we need to
+ // emit it.
+ auto InstOpM =
+ OM.addPredicate<InstructionOperandMatcher>(M, DefPat->getName());
+ if (!InstOpM) {
+ // TODO: copy-pasted from GlobalISelEmitter.cpp. Is it still relevant
+ // here?
+ PrintError("Nested instruction '" + DefPat->getName() +
+ "' cannot be the same as another operand '" +
+ OriginalO.getOperandName() + "'");
+ return false;
+ }
+
+ auto &IM = (*InstOpM)->getInsnMatcher();
+ if (const auto *CGIDef = dyn_cast<CodeGenInstructionPattern>(DefPat)) {
+ if (!emitCodeGenInstructionMatchPattern(CE, Alts, M, IM, *CGIDef,
+ SeenPats, LookupOperandDef,
+ OperandMapper))
+ return false;
+ continue;
+ }
+
+ if (const auto *PFPDef = dyn_cast<PatFragPattern>(DefPat)) {
+ if (!emitPatFragMatchPattern(CE, Alts, M, &IM, *PFPDef, SeenPats))
+ return false;
+ continue;
+ }
+
+ llvm_unreachable("unknown type of InstructionPattern");
+ }
+
+ return true;
+}
+
+//===- GICombinerEmitter --------------------------------------------------===//
+
+/// Main implementation class. This emits the tablegenerated output.
+///
+/// It collects rules, uses `CombineRuleBuilder` to parse them and accumulate
+/// RuleMatchers, then takes all the necessary state/data from the various
+/// static storage pools and wires them together to emit the match table &
+/// associated function/data structures.
+class GICombinerEmitter final : public GlobalISelMatchTableExecutorEmitter {
+ RecordKeeper &Records;
+ StringRef Name;
+ const CodeGenTarget &Target;
+ Record *Combiner;
+ unsigned NextRuleID = 0;
+
+ // List all combine rules (ID, name) imported.
+ // Note that the combiner rule ID is different from the RuleMatcher ID. The
+ // latter is internal to the MatchTable, the former is the canonical ID of the
+ // combine rule used to disable/enable it.
+ std::vector<std::pair<unsigned, std::string>> AllCombineRules;
+
+ // Keep track of all rules we've seen so far to ensure we don't process
+ // the same rule twice.
+ StringSet<> RulesSeen;
+
+ MatchTable buildMatchTable(MutableArrayRef<RuleMatcher> Rules);
+
+ void emitRuleConfigImpl(raw_ostream &OS);
+
+ void emitAdditionalImpl(raw_ostream &OS) override;
+
+ void emitMIPredicateFns(raw_ostream &OS) override;
+ void emitI64ImmPredicateFns(raw_ostream &OS) override;
+ void emitAPFloatImmPredicateFns(raw_ostream &OS) override;
+ void emitAPIntImmPredicateFns(raw_ostream &OS) override;
+ void emitTestSimplePredicate(raw_ostream &OS) override;
+ void emitRunCustomAction(raw_ostream &OS) override;
+
+ void emitAdditionalTemporariesDecl(raw_ostream &OS,
+ StringRef Indent) override;
+
+ const CodeGenTarget &getTarget() const override { return Target; }
+ StringRef getClassName() const override {
+ return Combiner->getValueAsString("Classname");
+ }
+
+ StringRef getCombineAllMethodName() const {
+ return Combiner->getValueAsString("CombineAllMethodName");
+ }
+
+ std::string getRuleConfigClassName() const {
+ return getClassName().str() + "RuleConfig";
+ }
+
+ void gatherRules(std::vector<RuleMatcher> &Rules,
+ const std::vector<Record *> &&RulesAndGroups);
+
+public:
+ explicit GICombinerEmitter(RecordKeeper &RK, const CodeGenTarget &Target,
+ StringRef Name, Record *Combiner);
+ ~GICombinerEmitter() {}
+
+ void run(raw_ostream &OS);
+};
+
+void GICombinerEmitter::emitRuleConfigImpl(raw_ostream &OS) {
+ OS << "struct " << getRuleConfigClassName() << " {\n"
+ << " SparseBitVector<> DisabledRules;\n\n"
+ << " bool isRuleEnabled(unsigned RuleID) const;\n"
+ << " bool parseCommandLineOption();\n"
+ << " bool setRuleEnabled(StringRef RuleIdentifier);\n"
+ << " bool setRuleDisabled(StringRef RuleIdentifier);\n"
+ << "};\n\n";
+
+ std::vector<std::pair<std::string, std::string>> Cases;
+ Cases.reserve(AllCombineRules.size());
+
+ for (const auto &[ID, Name] : AllCombineRules)
+ Cases.emplace_back(Name, "return " + to_string(ID) + ";\n");
+
+ OS << "static std::optional<uint64_t> getRuleIdxForIdentifier(StringRef "
+ "RuleIdentifier) {\n"
+ << " uint64_t I;\n"
+ << " // getAtInteger(...) returns false on success\n"
+ << " bool Parsed = !RuleIdentifier.getAsInteger(0, I);\n"
+ << " if (Parsed)\n"
+ << " return I;\n\n"
+ << "#ifndef NDEBUG\n";
+ StringMatcher Matcher("RuleIdentifier", Cases, OS);
+ Matcher.Emit();
+ OS << "#endif // ifndef NDEBUG\n\n"
+ << " return std::nullopt;\n"
+ << "}\n";
+
+ OS << "static std::optional<std::pair<uint64_t, uint64_t>> "
+ "getRuleRangeForIdentifier(StringRef RuleIdentifier) {\n"
+ << " std::pair<StringRef, StringRef> RangePair = "
+ "RuleIdentifier.split('-');\n"
+ << " if (!RangePair.second.empty()) {\n"
+ << " const auto First = "
+ "getRuleIdxForIdentifier(RangePair.first);\n"
+ << " const auto Last = "
+ "getRuleIdxForIdentifier(RangePair.second);\n"
+ << " if (!First || !Last)\n"
+ << " return std::nullopt;\n"
+ << " if (First >= Last)\n"
+ << " report_fatal_error(\"Beginning of range should be before "
+ "end of range\");\n"
+ << " return {{*First, *Last + 1}};\n"
+ << " }\n"
+ << " if (RangePair.first == \"*\") {\n"
+ << " return {{0, " << AllCombineRules.size() << "}};\n"
+ << " }\n"
+ << " const auto I = getRuleIdxForIdentifier(RangePair.first);\n"
+ << " if (!I)\n"
+ << " return std::nullopt;\n"
+ << " return {{*I, *I + 1}};\n"
+ << "}\n\n";
+
+ for (bool Enabled : {true, false}) {
+ OS << "bool " << getRuleConfigClassName() << "::setRule"
+ << (Enabled ? "Enabled" : "Disabled") << "(StringRef RuleIdentifier) {\n"
+ << " auto MaybeRange = getRuleRangeForIdentifier(RuleIdentifier);\n"
+ << " if (!MaybeRange)\n"
+ << " return false;\n"
+ << " for (auto I = MaybeRange->first; I < MaybeRange->second; ++I)\n"
+ << " DisabledRules." << (Enabled ? "reset" : "set") << "(I);\n"
+ << " return true;\n"
+ << "}\n\n";
+ }
+
+ OS << "static std::vector<std::string> " << Name << "Option;\n"
+ << "static cl::list<std::string> " << Name << "DisableOption(\n"
+ << " \"" << Name.lower() << "-disable-rule\",\n"
+ << " cl::desc(\"Disable one or more combiner rules temporarily in "
+ << "the " << Name << " pass\"),\n"
+ << " cl::CommaSeparated,\n"
+ << " cl::Hidden,\n"
+ << " cl::cat(GICombinerOptionCategory),\n"
+ << " cl::callback([](const std::string &Str) {\n"
+ << " " << Name << "Option.push_back(Str);\n"
+ << " }));\n"
+ << "static cl::list<std::string> " << Name << "OnlyEnableOption(\n"
+ << " \"" << Name.lower() << "-only-enable-rule\",\n"
+ << " cl::desc(\"Disable all rules in the " << Name
+ << " pass then re-enable the specified ones\"),\n"
+ << " cl::Hidden,\n"
+ << " cl::cat(GICombinerOptionCategory),\n"
+ << " cl::callback([](const std::string &CommaSeparatedArg) {\n"
+ << " StringRef Str = CommaSeparatedArg;\n"
+ << " " << Name << "Option.push_back(\"*\");\n"
+ << " do {\n"
+ << " auto X = Str.split(\",\");\n"
+ << " " << Name << "Option.push_back((\"!\" + X.first).str());\n"
+ << " Str = X.second;\n"
+ << " } while (!Str.empty());\n"
+ << " }));\n"
+ << "\n\n"
+ << "bool " << getRuleConfigClassName()
+ << "::isRuleEnabled(unsigned RuleID) const {\n"
+ << " return !DisabledRules.test(RuleID);\n"
+ << "}\n"
+ << "bool " << getRuleConfigClassName() << "::parseCommandLineOption() {\n"
+ << " for (StringRef Identifier : " << Name << "Option) {\n"
+ << " bool Enabled = Identifier.consume_front(\"!\");\n"
+ << " if (Enabled && !setRuleEnabled(Identifier))\n"
+ << " return false;\n"
+ << " if (!Enabled && !setRuleDisabled(Identifier))\n"
+ << " return false;\n"
+ << " }\n"
+ << " return true;\n"
+ << "}\n\n";
+}
+
+void GICombinerEmitter::emitAdditionalImpl(raw_ostream &OS) {
+ OS << "bool " << getClassName() << "::" << getCombineAllMethodName()
+ << "(MachineInstr &I) const {\n"
+ << " const TargetSubtargetInfo &ST = MF.getSubtarget();\n"
+ << " const PredicateBitset AvailableFeatures = "
+ "getAvailableFeatures();\n"
+ << " B.setInstrAndDebugLoc(I);\n"
+ << " State.MIs.clear();\n"
+ << " State.MIs.push_back(&I);\n"
+ << " " << MatchDataInfo::StructName << " = "
+ << MatchDataInfo::StructTypeName << "();\n\n"
+ << " if (executeMatchTable(*this, State, ExecInfo, B"
+ << ", getMatchTable(), *ST.getInstrInfo(), MRI, "
+ "*MRI.getTargetRegisterInfo(), *ST.getRegBankInfo(), AvailableFeatures"
+ << ", /*CoverageInfo*/ nullptr)) {\n"
+ << " return true;\n"
+ << " }\n\n"
+ << " return false;\n"
+ << "}\n\n";
+}
+
+void GICombinerEmitter::emitMIPredicateFns(raw_ostream &OS) {
+ auto MatchCode = CXXPredicateCode::getAllMatchCode();
+ emitMIPredicateFnsImpl<const CXXPredicateCode *>(
+ OS, "", ArrayRef<const CXXPredicateCode *>(MatchCode),
+ [](const CXXPredicateCode *C) -> StringRef { return C->BaseEnumName; },
+ [](const CXXPredicateCode *C) -> StringRef { return C->Code; });
+}
+
+void GICombinerEmitter::emitI64ImmPredicateFns(raw_ostream &OS) {
+ // Unused, but still needs to be called.
+ emitImmPredicateFnsImpl<unsigned>(
+ OS, "I64", "int64_t", {}, [](unsigned) { return ""; },
+ [](unsigned) { return ""; });
+}
+
+void GICombinerEmitter::emitAPFloatImmPredicateFns(raw_ostream &OS) {
+ // Unused, but still needs to be called.
+ emitImmPredicateFnsImpl<unsigned>(
+ OS, "APFloat", "const APFloat &", {}, [](unsigned) { return ""; },
+ [](unsigned) { return ""; });
+}
+
+void GICombinerEmitter::emitAPIntImmPredicateFns(raw_ostream &OS) {
+ // Unused, but still needs to be called.
+ emitImmPredicateFnsImpl<unsigned>(
+ OS, "APInt", "const APInt &", {}, [](unsigned) { return ""; },
+ [](unsigned) { return ""; });
+}
+
+void GICombinerEmitter::emitTestSimplePredicate(raw_ostream &OS) {
+ if (!AllCombineRules.empty()) {
+ OS << "enum {\n";
+ std::string EnumeratorSeparator = " = GICXXPred_Invalid + 1,\n";
+ // To avoid emitting a switch, we expect that all those rules are in order.
+ // That way we can just get the RuleID from the enum by subtracting
+ // (GICXXPred_Invalid + 1).
+ unsigned ExpectedID = 0;
+ (void)ExpectedID;
+ for (const auto &ID : keys(AllCombineRules)) {
+ assert(ExpectedID++ == ID && "combine rules are not ordered!");
+ OS << " " << getIsEnabledPredicateEnumName(ID) << EnumeratorSeparator;
+ EnumeratorSeparator = ",\n";
+ }
+ OS << "};\n\n";
+ }
+
+ OS << "bool " << getClassName()
+ << "::testSimplePredicate(unsigned Predicate) const {\n"
+ << " return RuleConfig.isRuleEnabled(Predicate - "
+ "GICXXPred_Invalid - "
+ "1);\n"
+ << "}\n";
+}
+
+void GICombinerEmitter::emitRunCustomAction(raw_ostream &OS) {
+ const auto ApplyCode = CXXPredicateCode::getAllApplyCode();
+
+ if (!ApplyCode.empty()) {
+ OS << "enum {\n";
+ std::string EnumeratorSeparator = " = GICXXCustomAction_Invalid + 1,\n";
+ for (const auto &Apply : ApplyCode) {
+ OS << " " << Apply->getEnumNameWithPrefix(CXXApplyPrefix)
+ << EnumeratorSeparator;
+ EnumeratorSeparator = ",\n";
+ }
+ OS << "};\n";
+ }
+
+ OS << "void " << getClassName()
+ << "::runCustomAction(unsigned ApplyID, const MatcherState &State, "
+ "NewMIVector &OutMIs) const "
+ "{\n";
+ if (!ApplyCode.empty()) {
+ OS << " switch(ApplyID) {\n";
+ for (const auto &Apply : ApplyCode) {
+ OS << " case " << Apply->getEnumNameWithPrefix(CXXApplyPrefix) << ":{\n"
+ << " " << join(split(Apply->Code, '\n'), "\n ") << '\n'
+ << " return;\n";
+ OS << " }\n";
+ }
+ OS << "}\n";
+ }
+ OS << " llvm_unreachable(\"Unknown Apply Action\");\n"
+ << "}\n";
+}
+
+void GICombinerEmitter::emitAdditionalTemporariesDecl(raw_ostream &OS,
+ StringRef Indent) {
+ OS << Indent << "struct " << MatchDataInfo::StructTypeName << " {\n";
+ for (const auto &[Type, VarNames] : AllMatchDataVars) {
+ assert(!VarNames.empty() && "Cannot have no vars for this type!");
+ OS << Indent << " " << Type << " " << join(VarNames, ", ") << ";\n";
+ }
+ OS << Indent << "};\n"
+ << Indent << "mutable " << MatchDataInfo::StructTypeName << " "
+ << MatchDataInfo::StructName << ";\n\n";
+}
+
+GICombinerEmitter::GICombinerEmitter(RecordKeeper &RK,
+ const CodeGenTarget &Target,
+ StringRef Name, Record *Combiner)
+ : Records(RK), Name(Name), Target(Target), Combiner(Combiner) {}
+
+MatchTable
+GICombinerEmitter::buildMatchTable(MutableArrayRef<RuleMatcher> Rules) {
+ std::vector<Matcher *> InputRules;
+ for (Matcher &Rule : Rules)
+ InputRules.push_back(&Rule);
+
+ unsigned CurrentOrdering = 0;
+ StringMap<unsigned> OpcodeOrder;
+ for (RuleMatcher &Rule : Rules) {
+ const StringRef Opcode = Rule.getOpcode();
+ assert(!Opcode.empty() && "Didn't expect an undefined opcode");
+ if (OpcodeOrder.count(Opcode) == 0)
+ OpcodeOrder[Opcode] = CurrentOrdering++;
+ }
+
+ llvm::stable_sort(InputRules, [&OpcodeOrder](const Matcher *A,
+ const Matcher *B) {
+ auto *L = static_cast<const RuleMatcher *>(A);
+ auto *R = static_cast<const RuleMatcher *>(B);
+ return std::make_tuple(OpcodeOrder[L->getOpcode()], L->getNumOperands()) <
+ std::make_tuple(OpcodeOrder[R->getOpcode()], R->getNumOperands());
+ });
+
+ for (Matcher *Rule : InputRules)
+ Rule->optimize();
+
+ std::vector<std::unique_ptr<Matcher>> MatcherStorage;
+ std::vector<Matcher *> OptRules =
+ optimizeRules<GroupMatcher>(InputRules, MatcherStorage);
+
+ for (Matcher *Rule : OptRules)
+ Rule->optimize();
+
+ OptRules = optimizeRules<SwitchMatcher>(OptRules, MatcherStorage);
+
+ return MatchTable::buildTable(OptRules, /*WithCoverage*/ false,
+ /*IsCombiner*/ true);
+}
+
+/// Recurse into GICombineGroup's and flatten the ruleset into a simple list.
+void GICombinerEmitter::gatherRules(
+ std::vector<RuleMatcher> &ActiveRules,
+ const std::vector<Record *> &&RulesAndGroups) {
+ for (Record *Rec : RulesAndGroups) {
+ if (!Rec->isValueUnset("Rules")) {
+ gatherRules(ActiveRules, Rec->getValueAsListOfDefs("Rules"));
+ continue;
+ }
+
+ StringRef RuleName = Rec->getName();
+ if (!RulesSeen.insert(RuleName).second) {
+ PrintWarning(Rec->getLoc(),
+ "skipping rule '" + Rec->getName() +
+ "' because it has already been processed");
+ continue;
+ }
+
+ AllCombineRules.emplace_back(NextRuleID, Rec->getName().str());
+ CombineRuleBuilder CRB(Target, SubtargetFeatures, *Rec, NextRuleID++,
+ ActiveRules);
+
+ if (!CRB.parseAll()) {
+ assert(ErrorsPrinted && "Parsing failed without errors!");
+ continue;
+ }
+
+ if (StopAfterParse) {
+ CRB.print(outs());
+ continue;
+ }
+
+ if (!CRB.emitRuleMatchers()) {
+ assert(ErrorsPrinted && "Emission failed without errors!");
+ continue;
+ }
+ }
+}
+
+void GICombinerEmitter::run(raw_ostream &OS) {
+ InstructionOpcodeMatcher::initOpcodeValuesMap(Target);
+ LLTOperandMatcher::initTypeIDValuesMap();
+
+ Records.startTimer("Gather rules");
+ std::vector<RuleMatcher> Rules;
+ gatherRules(Rules, Combiner->getValueAsListOfDefs("Rules"));
+ if (ErrorsPrinted)
+ PrintFatalError(Combiner->getLoc(), "Failed to parse one or more rules");
+
+ if (StopAfterParse)
+ return;
+
+ Records.startTimer("Creating Match Table");
+ unsigned MaxTemporaries = 0;
+ for (const auto &Rule : Rules)
+ MaxTemporaries = std::max(MaxTemporaries, Rule.countRendererFns());
+
+ llvm::stable_sort(Rules, [&](const RuleMatcher &A, const RuleMatcher &B) {
+ if (A.isHigherPriorityThan(B)) {
+ assert(!B.isHigherPriorityThan(A) && "Cannot be more important "
+ "and less important at "
+ "the same time");
+ return true;
+ }
+ return false;
+ });
+
+ const MatchTable Table = buildMatchTable(Rules);
+
+ Records.startTimer("Emit combiner");
+
+ emitSourceFileHeader(getClassName().str() + " Combiner Match Table", OS);
+
+ // Unused
+ std::vector<StringRef> CustomRendererFns;
+ // Unused
+ std::vector<Record *> ComplexPredicates;
+
+ SmallVector<LLTCodeGen, 16> TypeObjects;
+ append_range(TypeObjects, KnownTypes);
+ llvm::sort(TypeObjects);
+
+ // Hack: Avoid empty declarator.
+ if (TypeObjects.empty())
+ TypeObjects.push_back(LLT::scalar(1));
+
+ // GET_GICOMBINER_DEPS, which pulls in extra dependencies.
+ OS << "#ifdef GET_GICOMBINER_DEPS\n"
+ << "#include \"llvm/ADT/SparseBitVector.h\"\n"
+ << "namespace llvm {\n"
+ << "extern cl::OptionCategory GICombinerOptionCategory;\n"
+ << "} // end namespace llvm\n"
+ << "#endif // ifdef GET_GICOMBINER_DEPS\n\n";
+
+ // GET_GICOMBINER_TYPES, which needs to be included before the declaration of
+ // the class.
+ OS << "#ifdef GET_GICOMBINER_TYPES\n";
+ emitRuleConfigImpl(OS);
+ OS << "#endif // ifdef GET_GICOMBINER_TYPES\n\n";
+ emitPredicateBitset(OS, "GET_GICOMBINER_TYPES");
+
+ // GET_GICOMBINER_CLASS_MEMBERS, which need to be included inside the class.
+ emitPredicatesDecl(OS, "GET_GICOMBINER_CLASS_MEMBERS");
+ emitTemporariesDecl(OS, "GET_GICOMBINER_CLASS_MEMBERS");
+
+ // GET_GICOMBINER_IMPL, which needs to be included outside the class.
+ emitExecutorImpl(OS, Table, TypeObjects, Rules, ComplexPredicates,
+ CustomRendererFns, "GET_GICOMBINER_IMPL");
+
+ // GET_GICOMBINER_CONSTRUCTOR_INITS, which are in the constructor's
+ // initializer list.
+ emitPredicatesInit(OS, "GET_GICOMBINER_CONSTRUCTOR_INITS");
+ emitTemporariesInit(OS, MaxTemporaries, "GET_GICOMBINER_CONSTRUCTOR_INITS");
+}
+
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+
+static void EmitGICombiner(RecordKeeper &RK, raw_ostream &OS) {
+ EnablePrettyStackTrace();
+ CodeGenTarget Target(RK);
+
+ if (SelectedCombiners.empty())
+ PrintFatalError("No combiners selected with -combiners");
+ for (const auto &Combiner : SelectedCombiners) {
+ Record *CombinerDef = RK.getDef(Combiner);
+ if (!CombinerDef)
+ PrintFatalError("Could not find " + Combiner);
+ GICombinerEmitter(RK, Target, Combiner, CombinerDef).run(OS);
+ }
+}
+
+static TableGen::Emitter::Opt X("gen-global-isel-combiner", EmitGICombiner,
+ "Generate GlobalISel Combiner");
diff --git a/llvm/utils/TableGen/GlobalISelCombinerMatchTableEmitter.cpp b/llvm/utils/TableGen/GlobalISelCombinerMatchTableEmitter.cpp
deleted file mode 100644
index 3ae66ed01b3a..000000000000
--- a/llvm/utils/TableGen/GlobalISelCombinerMatchTableEmitter.cpp
+++ /dev/null
@@ -1,1575 +0,0 @@
-//===- GlobalISelCombinerMatchTableEmitter.cpp - --------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-//
-/// \file Generate a combiner implementation for GlobalISel from a declarative
-/// syntax using GlobalISelMatchTable.
-///
-//===----------------------------------------------------------------------===//
-
-#include "CodeGenInstruction.h"
-#include "CodeGenTarget.h"
-#include "GlobalISel/CodeExpander.h"
-#include "GlobalISel/CodeExpansions.h"
-#include "GlobalISel/CombinerUtils.h"
-#include "GlobalISelMatchTable.h"
-#include "GlobalISelMatchTableExecutorEmitter.h"
-#include "SubtargetFeatureInfo.h"
-#include "llvm/ADT/Hashing.h"
-#include "llvm/ADT/MapVector.h"
-#include "llvm/ADT/Statistic.h"
-#include "llvm/ADT/StringSet.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/ScopedPrinter.h"
-#include "llvm/TableGen/Error.h"
-#include "llvm/TableGen/Record.h"
-#include "llvm/TableGen/StringMatcher.h"
-#include "llvm/TableGen/TableGenBackend.h"
-#include <cstdint>
-
-using namespace llvm;
-using namespace llvm::gi;
-
-#define DEBUG_TYPE "gicombiner-matchtable-emitter"
-
-extern cl::list<std::string> SelectedCombiners;
-extern cl::opt<bool> StopAfterParse;
-
-namespace {
-constexpr StringLiteral CXXApplyPrefix = "GICXXCustomAction_CombineApply";
-constexpr StringLiteral CXXPredPrefix = "GICXXPred_MI_Predicate_";
-
-std::string getIsEnabledPredicateEnumName(unsigned CombinerRuleID) {
- return "GICXXPred_Simple_IsRule" + to_string(CombinerRuleID) + "Enabled";
-}
-
-void declareInstExpansion(CodeExpansions &CE, const InstructionMatcher &IM,
- StringRef Name) {
- CE.declare(Name, "State.MIs[" + to_string(IM.getInsnVarID()) + "]");
-}
-
-void declareOperandExpansion(CodeExpansions &CE, const OperandMatcher &OM,
- StringRef Name) {
- CE.declare(Name, "State.MIs[" + to_string(OM.getInsnVarID()) +
- "]->getOperand(" + to_string(OM.getOpIdx()) + ")");
-}
-
-//===- MatchData Handling -------------------------------------------------===//
-
-/// Represents MatchData defined by the match stage and required by the apply
-/// stage.
-///
-/// This allows the plumbing of arbitrary data from C++ predicates between the
-/// stages.
-///
-/// When this class is initially created, it only has a pattern symbol and a
-/// type. When all of the MatchDatas declarations of a given pattern have been
-/// parsed, `AssignVariables` must be called to assign storage variable names to
-/// each MatchDataInfo.
-class MatchDataInfo {
- StringRef PatternSymbol;
- StringRef Type;
- std::string VarName;
-
-public:
- static constexpr StringLiteral StructTypeName = "MatchInfosTy";
- static constexpr StringLiteral StructName = "MatchInfos";
-
- MatchDataInfo(StringRef PatternSymbol, StringRef Type)
- : PatternSymbol(PatternSymbol), Type(Type.trim()) {}
-
- StringRef getPatternSymbol() const { return PatternSymbol; };
- StringRef getType() const { return Type; };
-
- bool hasVariableName() const { return !VarName.empty(); }
- void setVariableName(StringRef Name) { VarName = Name; }
- StringRef getVariableName() const;
-
- std::string getQualifiedVariableName() const {
- return StructName.str() + "." + getVariableName().str();
- }
-
- void print(raw_ostream &OS) const;
- void dump() const { print(dbgs()); }
-};
-
-StringRef MatchDataInfo::getVariableName() const {
- assert(hasVariableName());
- return VarName;
-}
-
-void MatchDataInfo::print(raw_ostream &OS) const {
- OS << "(MatchDataInfo pattern_symbol:" << PatternSymbol << " type:'" << Type
- << "' var_name:" << (VarName.empty() ? "<unassigned>" : VarName) << ")";
-}
-
-/// Pool of type -> variables used to emit MatchData variables declarations.
-///
-/// e.g. if the map contains "int64_t" -> ["MD0", "MD1"], then two variable
-/// declarations must be emitted: `int64_t MD0` and `int64_t MD1`.
-///
-/// This has a static lifetime and will outlive all the `MatchDataInfo` objects
-/// by design. It needs to persist after all `CombineRuleBuilder` objects died
-/// so we can emit the variable declarations.
-StringMap<std::vector<std::string>> AllMatchDataVars;
-
-// Assign variable names to all MatchDatas used by a pattern. This must be
-// called after all MatchData decls have been parsed inside a rule.
-//
-// Requires an array of MatchDataInfo so we can handle cases where a pattern
-// uses multiple instances of the same MatchData type.
-void AssignMatchDataVariables(MutableArrayRef<MatchDataInfo> Infos) {
- static unsigned NextVarID = 0;
-
- StringMap<unsigned> SeenTypes;
- for (auto &I : Infos) {
- unsigned &NumSeen = SeenTypes[I.getType()];
- auto &ExistingVars = AllMatchDataVars[I.getType()];
-
- if (NumSeen == ExistingVars.size())
- ExistingVars.push_back("MDInfo" + to_string(NextVarID++));
-
- I.setVariableName(ExistingVars[NumSeen++]);
- }
-}
-
-//===- C++ Predicates Handling --------------------------------------------===//
-
-/// Entry into the static pool of all CXX Predicate code. This contains the
-/// fully expanded C++ code.
-///
-/// Each CXXPattern creates a new entry in the pool to store its data, even
-/// after the pattern is destroyed.
-///
-/// Note that CXXPattern trims C++ code, so the Code is already expected to be
-/// free of leading/trailing whitespace.
-struct CXXPredicateCode {
- CXXPredicateCode(std::string Code, unsigned ID)
- : Code(Code), ID(ID), BaseEnumName("GICombiner" + to_string(ID)) {
- assert(StringRef(Code).trim() == Code &&
- "Code was expected to be trimmed!");
- }
-
- const std::string Code;
- const unsigned ID;
- const std::string BaseEnumName;
-
- bool needsUnreachable() const {
- return !StringRef(Code).starts_with("return");
- }
-
- std::string getEnumNameWithPrefix(StringRef Prefix) const {
- return Prefix.str() + BaseEnumName;
- }
-};
-
-using CXXPredicateCodePool =
- DenseMap<hash_code, std::unique_ptr<CXXPredicateCode>>;
-CXXPredicateCodePool AllCXXMatchCode;
-CXXPredicateCodePool AllCXXApplyCode;
-
-/// Gets an instance of `CXXPredicateCode` for \p Code, or returns an already
-/// existing one.
-const CXXPredicateCode &getOrInsert(CXXPredicateCodePool &Pool,
- std::string Code) {
- // Check if we already have an identical piece of code, if not, create an
- // entry in the pool.
- const auto CodeHash = hash_value(Code);
- if (auto It = Pool.find(CodeHash); It != Pool.end())
- return *It->second;
-
- const auto ID = Pool.size();
- auto OwnedData = std::make_unique<CXXPredicateCode>(std::move(Code), ID);
- const auto &DataRef = *OwnedData;
- Pool[CodeHash] = std::move(OwnedData);
- return DataRef;
-}
-
-/// Sorts a `CXXPredicateCodePool` by their IDs and returns it.
-std::vector<const CXXPredicateCode *>
-getSorted(const CXXPredicateCodePool &Pool) {
- std::vector<const CXXPredicateCode *> Out;
- std::transform(Pool.begin(), Pool.end(), std::back_inserter(Out),
- [&](auto &Elt) { return Elt.second.get(); });
- sort(Out, [](const auto *A, const auto *B) { return A->ID < B->ID; });
- return Out;
-}
-
-//===- Pattern Base Class -------------------------------------------------===//
-
-// An abstract pattern found in a combine rule. This can be an apply or match
-// pattern.
-class Pattern {
-public:
- enum {
- K_AnyOpcode,
- K_Inst,
- K_CXX,
- };
-
- virtual ~Pattern() = default;
-
- unsigned getKind() const { return Kind; }
- const char *getKindName() const;
-
- bool hasName() const { return !Name.empty(); }
- StringRef getName() const { return Name; }
-
- virtual void print(raw_ostream &OS, bool PrintName = true) const = 0;
- void dump() const { return print(dbgs()); }
-
-protected:
- Pattern(unsigned Kind, StringRef Name) : Kind(Kind), Name(Name.str()) {
- assert(!Name.empty() && "unnamed pattern!");
- }
-
- void printImpl(raw_ostream &OS, bool PrintName,
- function_ref<void()> ContentPrinter) const;
-
-private:
- unsigned Kind;
-
- // Note: if this ever changes to a StringRef (e.g. allocated in a pool or
- // something), CombineRuleBuilder::verify() needs to be updated as well.
- // It currently checks that the StringRef in the PatternMap references this.
- std::string Name;
-};
-
-const char *Pattern::getKindName() const {
- switch (Kind) {
- case K_AnyOpcode:
- return "AnyOpcodePattern";
- case K_Inst:
- return "InstructionPattern";
- case K_CXX:
- return "CXXPattern";
- }
-
- llvm_unreachable("unknown pattern kind!");
-}
-
-void Pattern::printImpl(raw_ostream &OS, bool PrintName,
- function_ref<void()> ContentPrinter) const {
- OS << "(" << getKindName() << " ";
- if (PrintName)
- OS << "name:" << getName() << " ";
- ContentPrinter();
- OS << ")";
-}
-
-//===- AnyOpcodePattern ---------------------------------------------------===//
-
-/// `wip_match_opcode` patterns.
-/// This matches one or more opcodes, and does not check any operands
-/// whatsoever.
-class AnyOpcodePattern : public Pattern {
-public:
- AnyOpcodePattern(StringRef Name) : Pattern(K_AnyOpcode, Name) {}
-
- static bool classof(const Pattern *P) { return P->getKind() == K_AnyOpcode; }
-
- void addOpcode(const CodeGenInstruction *I) { Insts.push_back(I); }
- const auto &insts() const { return Insts; }
-
- void print(raw_ostream &OS, bool PrintName = true) const override;
-
-private:
- SmallVector<const CodeGenInstruction *, 4> Insts;
-};
-
-void AnyOpcodePattern::print(raw_ostream &OS, bool PrintName) const {
- printImpl(OS, PrintName, [&OS, this]() {
- OS << "["
- << join(map_range(Insts,
- [](const auto *I) { return I->TheDef->getName(); }),
- ", ")
- << "]";
- });
-}
-
-//===- InstructionPattern -------------------------------------------------===//
-
-/// Matches an instruction, e.g. `G_ADD $x, $y, $z`.
-///
-/// This pattern is simply CodeGenInstruction + a list of operands.
-class InstructionPattern : public Pattern {
-public:
- struct Operand {
- std::string Name;
- bool IsDef = false;
- };
-
- InstructionPattern(const CodeGenInstruction &I, StringRef Name)
- : Pattern(K_Inst, Name), I(I) {}
-
- static bool classof(const Pattern *P) { return P->getKind() == K_Inst; }
-
- const auto &operands() const { return Operands; }
- void addOperand(StringRef Name);
- unsigned getNumDefs() const { return I.Operands.NumDefs; }
-
- const CodeGenInstruction &getInst() const { return I; }
- StringRef getInstName() const { return I.TheDef->getName(); }
-
- void reportUnreachable(ArrayRef<SMLoc> Locs) const;
- bool checkSemantics(ArrayRef<SMLoc> Loc) const;
-
- void print(raw_ostream &OS, bool PrintName = true) const override;
-
-private:
- const CodeGenInstruction &I;
- SmallVector<Operand, 4> Operands;
-};
-
-void InstructionPattern::addOperand(StringRef Name) {
- const bool IsDef = Operands.size() < getNumDefs();
- Operands.emplace_back(Operand{Name.str(), IsDef});
-}
-
-void InstructionPattern::reportUnreachable(ArrayRef<SMLoc> Locs) const {
- PrintError(Locs, "Instruction pattern '" + getName() +
- "' is unreachable from the pattern root!");
-}
-
-bool InstructionPattern::checkSemantics(ArrayRef<SMLoc> Loc) const {
- unsigned NumExpectedOperands = I.Operands.size();
- if (NumExpectedOperands != Operands.size()) {
-
- PrintError(Loc, "'" + getInstName() + "' expected " +
- Twine(NumExpectedOperands) + " operands, got " +
- Twine(Operands.size()));
- return false;
- }
- return true;
-}
-
-void InstructionPattern::print(raw_ostream &OS, bool PrintName) const {
- printImpl(OS, PrintName, [&OS, this]() {
- OS << "inst:" << I.TheDef->getName() << " operands:["
- << join(map_range(Operands,
- [](const auto &O) {
- return (O.IsDef ? "<def>" : "") + O.Name;
- }),
- ", ")
- << "]";
- });
-}
-
-//===- CXXPattern ---------------------------------------------------------===//
-
-/// Raw C++ code which may need some expansions.
-///
-/// e.g. [{ return isFooBux(${src}.getReg()); }]
-///
-/// For the expanded code, \see CXXPredicateCode. CXXPredicateCode objects are
-/// created through `expandCode`.
-///
-/// \see CodeExpander and \see CodeExpansions for more information on code
-/// expansions.
-///
-/// This object has two purposes:
-/// - Represent C++ code as a pattern entry.
-/// - Be a factory for expanded C++ code.
-/// - It's immutable and only holds the raw code so we can expand the same
-/// CXX pattern multiple times if we need to.
-///
-/// Note that the code is always trimmed in the constructor, so leading and
-/// trailing whitespaces are removed. This removes bloat in the output, avoids
-/// formatting issues, but also allows us to check things like
-/// `.startswith("return")` trivially without worrying about spaces.
-class CXXPattern : public Pattern {
-public:
- CXXPattern(const StringInit &Code, StringRef Name, bool IsApply)
- : CXXPattern(Code.getAsUnquotedString(), Name, IsApply) {}
-
- CXXPattern(StringRef Code, StringRef Name, bool IsApply)
- : Pattern(K_CXX, Name), IsApply(IsApply), RawCode(Code.trim().str()) {}
-
- static bool classof(const Pattern *P) { return P->getKind() == K_CXX; }
-
- bool isApply() const { return IsApply; }
- StringRef getRawCode() const { return RawCode; }
-
- /// Expands raw code, replacing things such as `${foo}` with their
- /// substitution in \p CE.
- ///
- /// \param CE Map of Code Expansions
- /// \param Locs SMLocs for the Code Expander, in case it needs to emit
- /// diagnostics.
- /// \return A CXXPredicateCode object that contains the expanded code. Note
- /// that this may or may not insert a new object. All CXXPredicateCode objects
- /// are held in a set to avoid emitting duplicate C++ code.
- const CXXPredicateCode &expandCode(const CodeExpansions &CE,
- ArrayRef<SMLoc> Locs) const;
-
- void print(raw_ostream &OS, bool PrintName = true) const override;
-
-private:
- bool IsApply;
- std::string RawCode;
-};
-
-const CXXPredicateCode &CXXPattern::expandCode(const CodeExpansions &CE,
- ArrayRef<SMLoc> Locs) const {
- std::string Result;
- raw_string_ostream OS(Result);
- CodeExpander Expander(RawCode, CE, Locs, /*ShowExpansions*/ false);
- Expander.emit(OS);
- return getOrInsert(IsApply ? AllCXXApplyCode : AllCXXMatchCode,
- std::move(Result));
-}
-
-void CXXPattern::print(raw_ostream &OS, bool PrintName) const {
- printImpl(OS, PrintName, [&OS, this] {
- OS << (IsApply ? "apply" : "match") << " code:\"";
- printEscapedString(getRawCode(), OS);
- OS << "\"";
- });
-}
-
-//===- CombineRuleBuilder -------------------------------------------------===//
-
-/// Helper for CombineRuleBuilder.
-///
-/// Represents information about an operand.
-/// Operands with no MatchPat are considered live-in to the pattern.
-struct OperandTableEntry {
- // The matcher pattern that defines this operand.
- // null for live-ins.
- InstructionPattern *MatchPat = nullptr;
- // The apply pattern that (re)defines this operand.
- // This can only be non-null if MatchPat is.
- InstructionPattern *ApplyPat = nullptr;
-
- bool isLiveIn() const { return !MatchPat; }
-};
-
-/// Parses combine rule and builds a small intermediate representation to tie
-/// patterns together and emit RuleMatchers to match them. This may emit more
-/// than one RuleMatcher, e.g. for `wip_match_opcode`.
-///
-/// Memory management for `Pattern` objects is done through `std::unique_ptr`.
-/// In most cases, there are two stages to a pattern's lifetime:
-/// - Creation in a `parse` function
-/// - The unique_ptr is stored in a variable, and may be destroyed if the
-/// pattern is found to be semantically invalid.
-/// - Ownership transfer into a `PatternMap`
-/// - Once a pattern is moved into either the map of Match or Apply
-/// patterns, it is known to be valid and it never moves back.
-class CombineRuleBuilder {
-public:
- using PatternMap = MapVector<StringRef, std::unique_ptr<Pattern>>;
-
- CombineRuleBuilder(const CodeGenTarget &CGT,
- SubtargetFeatureInfoMap &SubtargetFeatures,
- Record &RuleDef, unsigned ID,
- std::vector<RuleMatcher> &OutRMs)
- : CGT(CGT), SubtargetFeatures(SubtargetFeatures), RuleDef(RuleDef),
- RuleID(ID), OutRMs(OutRMs) {}
-
- /// Parses all fields in the RuleDef record.
- bool parseAll();
-
- /// Emits all RuleMatchers into the vector of RuleMatchers passed in the
- /// constructor.
- bool emitRuleMatchers();
-
- void print(raw_ostream &OS) const;
- void dump() const { print(dbgs()); }
-
- /// Debug-only verification of invariants.
- void verify() const;
-
-private:
- void PrintError(Twine Msg) const { ::PrintError(RuleDef.getLoc(), Msg); }
-
- /// Adds the expansions from \see MatchDatas to \p CE.
- void declareAllMatchDatasExpansions(CodeExpansions &CE) const;
-
- /// Adds \p P to \p IM, expanding its code using \p CE.
- void addCXXPredicate(InstructionMatcher &IM, const CodeExpansions &CE,
- const CXXPattern &P);
-
- /// Generates a name for anonymous patterns.
- ///
- /// e.g. (G_ADD $x, $y, $z):$foo is a pattern named "foo", but if ":$foo" is
- /// absent, then the pattern is anonymous and this is used to assign it a
- /// name.
- std::string makeAnonPatName(StringRef Prefix) const;
- mutable unsigned AnonIDCnt = 0;
-
- /// Creates a new RuleMatcher with some boilerplate
- /// settings/actions/predicates, and and adds it to \p OutRMs.
- /// \see addFeaturePredicates too.
- ///
- /// \param AdditionalComment Comment string to be added to the
- /// `DebugCommentAction`.
- RuleMatcher &addRuleMatcher(Twine AdditionalComment = "");
- bool addFeaturePredicates(RuleMatcher &M);
-
- bool findRoots();
- bool buildOperandsTable();
-
- bool parseDefs(DagInit &Def);
- bool parseMatch(DagInit &Match);
- bool parseApply(DagInit &Apply);
-
- std::unique_ptr<Pattern> parseInstructionMatcher(const Init &Arg,
- StringRef PatName);
- std::unique_ptr<Pattern> parseWipMatchOpcodeMatcher(const Init &Arg,
- StringRef PatName);
-
- bool emitMatchPattern(CodeExpansions &CE, const InstructionPattern &IP);
- bool emitMatchPattern(CodeExpansions &CE, const AnyOpcodePattern &AOP);
-
- bool emitApplyPatterns(CodeExpansions &CE, RuleMatcher &M);
-
- // Recursively visits InstructionPattern from P to build up the
- // RuleMatcher/InstructionMatcher. May create new InstructionMatchers as
- // needed.
- bool emitInstructionMatchPattern(CodeExpansions &CE, RuleMatcher &M,
- InstructionMatcher &IM,
- const InstructionPattern &P,
- DenseSet<const Pattern *> &SeenPats);
-
- const CodeGenTarget &CGT;
- SubtargetFeatureInfoMap &SubtargetFeatures;
- Record &RuleDef;
- const unsigned RuleID;
- std::vector<RuleMatcher> &OutRMs;
-
- // For InstructionMatcher::addOperand
- unsigned AllocatedTemporariesBaseID = 0;
-
- /// The root of the pattern.
- StringRef RootName;
-
- /// These maps have ownership of the actual Pattern objects.
- /// They both map a Pattern's name to the Pattern instance.
- PatternMap MatchPats;
- PatternMap ApplyPats;
-
- /// Set by findRoots.
- Pattern *MatchRoot = nullptr;
-
- MapVector<StringRef, OperandTableEntry> OperandTable;
- SmallVector<MatchDataInfo, 2> MatchDatas;
-};
-
-bool CombineRuleBuilder::parseAll() {
- if (!parseDefs(*RuleDef.getValueAsDag("Defs")))
- return false;
- if (!parseMatch(*RuleDef.getValueAsDag("Match")))
- return false;
- if (!parseApply(*RuleDef.getValueAsDag("Apply")))
- return false;
- if (!buildOperandsTable())
- return false;
- if (!findRoots())
- return false;
- LLVM_DEBUG(verify());
- return true;
-}
-
-bool CombineRuleBuilder::emitRuleMatchers() {
- assert(MatchRoot);
- CodeExpansions CE;
- declareAllMatchDatasExpansions(CE);
-
- switch (MatchRoot->getKind()) {
- case Pattern::K_AnyOpcode: {
- if (!emitMatchPattern(CE, *cast<AnyOpcodePattern>(MatchRoot)))
- return false;
- break;
- }
- case Pattern::K_Inst:
- if (!emitMatchPattern(CE, *cast<InstructionPattern>(MatchRoot)))
- return false;
- break;
- case Pattern::K_CXX:
- PrintError("C++ code cannot be the root of a pattern!");
- return false;
- default:
- llvm_unreachable("unknown pattern kind!");
- }
-
- return true;
-}
-
-void CombineRuleBuilder::print(raw_ostream &OS) const {
- OS << "(CombineRule name:" << RuleDef.getName() << " id:" << RuleID
- << " root:" << RootName << "\n";
-
- OS << " (MatchDatas ";
- if (MatchDatas.empty())
- OS << "<empty>)\n";
- else {
- OS << "\n";
- for (const auto &MD : MatchDatas) {
- OS << " ";
- MD.print(OS);
- OS << "\n";
- }
- OS << " )\n";
- }
-
- const auto DumpPats = [&](StringRef Name, const PatternMap &Pats) {
- OS << " (" << Name << " ";
- if (Pats.empty()) {
- OS << "<empty>)\n";
- return;
- }
-
- OS << "\n";
- for (const auto &[Name, Pat] : Pats) {
- OS << " ";
- if (Pat.get() == MatchRoot)
- OS << "<root>";
- OS << Name << ":";
- Pat->print(OS, /*PrintName=*/false);
- OS << "\n";
- }
- OS << " )\n";
- };
-
- DumpPats("MatchPats", MatchPats);
- DumpPats("ApplyPats", ApplyPats);
-
- OS << " (OperandTable ";
- if (OperandTable.empty())
- OS << "<empty>)\n";
- else {
- OS << "\n";
- for (const auto &[Key, Val] : OperandTable) {
- OS << " [" << Key;
- if (const auto *P = Val.MatchPat)
- OS << " match_pat:" << P->getName();
- if (const auto *P = Val.ApplyPat)
- OS << " apply_pat:" << P->getName();
- if (Val.isLiveIn())
- OS << " live-in";
- OS << "]\n";
- }
- OS << " )\n";
- }
-
- OS << ")\n";
-}
-
-void CombineRuleBuilder::verify() const {
- const auto VerifyPats = [&](const PatternMap &Pats) {
- for (const auto &[Name, Pat] : Pats) {
- if (!Pat)
- PrintFatalError("null pattern in pattern map!");
-
- if (Name != Pat->getName()) {
- Pat->dump();
- PrintFatalError("Pattern name mismatch! Map name: " + Name +
- ", Pat name: " + Pat->getName());
- }
-
- // As an optimization, the PatternMaps don't re-allocate the PatternName
- // string. They simply reference the std::string inside Pattern. Ensure
- // this is the case to avoid memory issues.
- if (Name.data() != Pat->getName().data()) {
- dbgs() << "Map StringRef: '" << Name << "' @ "
- << (const void *)Name.data() << "\n";
- dbgs() << "Pat String: '" << Pat->getName() << "' @ "
- << (const void *)Pat->getName().data() << "\n";
- PrintFatalError("StringRef stored in the PatternMap is not referencing "
- "the same string as its Pattern!");
- }
- }
- };
-
- VerifyPats(MatchPats);
- VerifyPats(ApplyPats);
-
- for (const auto &[Name, Op] : OperandTable) {
- if (Op.ApplyPat && !Op.MatchPat) {
- dump();
- PrintFatalError("Operand " + Name +
- " has an apply pattern, but no match pattern!");
- }
- }
-}
-
-bool CombineRuleBuilder::addFeaturePredicates(RuleMatcher &M) {
- if (!RuleDef.getValue("Predicates"))
- return true;
-
- ListInit *Preds = RuleDef.getValueAsListInit("Predicates");
- for (Init *I : Preds->getValues()) {
- if (DefInit *Pred = dyn_cast<DefInit>(I)) {
- Record *Def = Pred->getDef();
- if (!Def->isSubClassOf("Predicate")) {
- ::PrintError(Def->getLoc(), "Unknown 'Predicate' Type");
- return false;
- }
-
- if (Def->getValueAsString("CondString").empty())
- continue;
-
- if (SubtargetFeatures.count(Def) == 0) {
- SubtargetFeatures.emplace(
- Def, SubtargetFeatureInfo(Def, SubtargetFeatures.size()));
- }
-
- M.addRequiredFeature(Def);
- }
- }
-
- return true;
-}
-
-void CombineRuleBuilder::declareAllMatchDatasExpansions(
- CodeExpansions &CE) const {
- for (const auto &MD : MatchDatas)
- CE.declare(MD.getPatternSymbol(), MD.getQualifiedVariableName());
-}
-
-void CombineRuleBuilder::addCXXPredicate(InstructionMatcher &IM,
- const CodeExpansions &CE,
- const CXXPattern &P) {
- const auto &ExpandedCode = P.expandCode(CE, RuleDef.getLoc());
- IM.addPredicate<GenericInstructionPredicateMatcher>(
- ExpandedCode.getEnumNameWithPrefix(CXXPredPrefix));
-}
-
-std::string CombineRuleBuilder::makeAnonPatName(StringRef Prefix) const {
- return to_string("__anon_pat_" + Prefix + "_" + to_string(RuleID) + "_" +
- to_string(AnonIDCnt++));
-}
-
-RuleMatcher &CombineRuleBuilder::addRuleMatcher(Twine AdditionalComment) {
- auto &RM = OutRMs.emplace_back(RuleDef.getLoc());
- addFeaturePredicates(RM);
- RM.addRequiredSimplePredicate(getIsEnabledPredicateEnumName(RuleID));
- const std::string AdditionalCommentStr = AdditionalComment.str();
- RM.addAction<DebugCommentAction>(
- "Combiner Rule #" + to_string(RuleID) + ": " + RuleDef.getName().str() +
- (AdditionalCommentStr.empty() ? "" : "; " + AdditionalCommentStr));
- return RM;
-}
-
-bool CombineRuleBuilder::findRoots() {
- // Look by pattern name, e.g.
- // (G_FNEG $x, $y):$root
- if (auto It = MatchPats.find(RootName); It != MatchPats.end()) {
- MatchRoot = It->second.get();
- return true;
- }
-
- // Look by def:
- // (G_FNEG $root, $y)
- auto It = OperandTable.find(RootName);
- if (It == OperandTable.end()) {
- PrintError("Cannot find root '" + RootName + "' in match patterns!");
- return false;
- }
-
- if (!It->second.MatchPat) {
- PrintError("Cannot use live-in operand '" + RootName +
- "' as match pattern root!");
- return false;
- }
-
- MatchRoot = It->second.MatchPat;
- return true;
-}
-
-bool CombineRuleBuilder::buildOperandsTable() {
- // Walk each instruction pattern
- for (auto &[_, P] : MatchPats) {
- auto *IP = dyn_cast<InstructionPattern>(P.get());
- if (!IP)
- continue;
- for (const auto &Operand : IP->operands()) {
- // Create an entry, no matter if it's a use or a def.
- auto &Entry = OperandTable[Operand.Name];
-
- // We only need to do additional checking on defs, though.
- if (!Operand.IsDef)
- continue;
-
- if (Entry.MatchPat) {
- PrintError("Operand '" + Operand.Name +
- "' is defined multiple times in the 'match' patterns");
- return false;
- }
- Entry.MatchPat = IP;
- }
- }
-
- for (auto &[_, P] : ApplyPats) {
- auto *IP = dyn_cast<InstructionPattern>(P.get());
- if (!IP)
- continue;
- for (const auto &Operand : IP->operands()) {
- // Create an entry, no matter if it's a use or a def.
- auto &Entry = OperandTable[Operand.Name];
-
- // We only need to do additional checking on defs, though.
- if (!Operand.IsDef)
- continue;
-
- if (!Entry.MatchPat) {
- PrintError("Cannot define live-in operand '" + Operand.Name +
- "' in the 'apply' pattern");
- return false;
- }
- if (Entry.ApplyPat) {
- PrintError("Operand '" + Operand.Name +
- "' is defined multiple times in the 'apply' patterns");
- return false;
- }
- Entry.ApplyPat = IP;
- }
- }
-
- return true;
-}
-
-bool CombineRuleBuilder::parseDefs(DagInit &Def) {
- if (Def.getOperatorAsDef(RuleDef.getLoc())->getName() != "defs") {
- PrintError("Expected defs operator");
- return false;
- }
-
- SmallVector<StringRef> Roots;
- for (unsigned I = 0, E = Def.getNumArgs(); I < E; ++I) {
- if (isSpecificDef(*Def.getArg(I), "root")) {
- Roots.emplace_back(Def.getArgNameStr(I));
- continue;
- }
-
- // Subclasses of GIDefMatchData should declare that this rule needs to pass
- // data from the match stage to the apply stage, and ensure that the
- // generated matcher has a suitable variable for it to do so.
- if (Record *MatchDataRec =
- getDefOfSubClass(*Def.getArg(I), "GIDefMatchData")) {
- MatchDatas.emplace_back(Def.getArgNameStr(I),
- MatchDataRec->getValueAsString("Type"));
- continue;
- }
-
- // Otherwise emit an appropriate error message.
- if (getDefOfSubClass(*Def.getArg(I), "GIDefKind"))
- PrintError("This GIDefKind not implemented in tablegen");
- else if (getDefOfSubClass(*Def.getArg(I), "GIDefKindWithArgs"))
- PrintError("This GIDefKindWithArgs not implemented in tablegen");
- else
- PrintError("Expected a subclass of GIDefKind or a sub-dag whose "
- "operator is of type GIDefKindWithArgs");
- return false;
- }
-
- if (Roots.size() != 1) {
- PrintError("Combine rules must have exactly one root");
- return false;
- }
-
- RootName = Roots.front();
-
- // Assign variables to all MatchDatas.
- AssignMatchDataVariables(MatchDatas);
- return true;
-}
-
-bool CombineRuleBuilder::parseMatch(DagInit &Match) {
- if (Match.getOperatorAsDef(RuleDef.getLoc())->getName() != "match") {
- PrintError("Expected match operator");
- return false;
- }
-
- if (Match.getNumArgs() == 0) {
- PrintError("Matcher is empty");
- return false;
- }
-
- // The match section consists of a list of matchers and predicates. Parse each
- // one and add the equivalent GIMatchDag nodes, predicates, and edges.
- bool HasOpcodeMatcher = false;
- for (unsigned I = 0; I < Match.getNumArgs(); ++I) {
- Init *Arg = Match.getArg(I);
- std::string Name = Match.getArgName(I)
- ? Match.getArgName(I)->getValue().str()
- : makeAnonPatName("match");
-
- if (MatchPats.contains(Name)) {
- PrintError("'" + Name + "' match pattern defined more than once!");
- return false;
- }
-
- if (auto Pat = parseInstructionMatcher(*Arg, Name)) {
- MatchPats[Pat->getName()] = std::move(Pat);
- continue;
- }
-
- if (auto Pat = parseWipMatchOpcodeMatcher(*Arg, Name)) {
- if (HasOpcodeMatcher) {
- PrintError("wip_opcode_match can only be present once");
- return false;
- }
- HasOpcodeMatcher = true;
- MatchPats[Pat->getName()] = std::move(Pat);
- continue;
- }
-
- // Parse arbitrary C++ code
- if (const auto *StringI = dyn_cast<StringInit>(Arg)) {
- auto CXXPat =
- std::make_unique<CXXPattern>(*StringI, Name, /*IsApply*/ false);
- if (!CXXPat->getRawCode().contains("return ")) {
- PrintWarning(RuleDef.getLoc(),
- "'match' C++ code does not seem to return!");
- }
- MatchPats[CXXPat->getName()] = std::move(CXXPat);
- continue;
- }
-
- // TODO: don't print this on, e.g. bad operand count in inst pat
- PrintError("Expected a subclass of GIMatchKind or a sub-dag whose "
- "operator is either of a GIMatchKindWithArgs or Instruction");
- PrintNote("Pattern was `" + Arg->getAsString() + "'");
- return false;
- }
-
- return true;
-}
-
-bool CombineRuleBuilder::parseApply(DagInit &Apply) {
- // Currently we only support C++ :(
- if (Apply.getOperatorAsDef(RuleDef.getLoc())->getName() != "apply") {
- PrintError("Expected 'apply' operator in Apply DAG");
- return false;
- }
-
- if (Apply.getNumArgs() != 1) {
- PrintError("Expected exactly 1 argument in 'apply'");
- return false;
- }
-
- const StringInit *Code = dyn_cast<StringInit>(Apply.getArg(0));
- auto Pat = std::make_unique<CXXPattern>(*Code, makeAnonPatName("apply"),
- /*IsApply*/ true);
- ApplyPats[Pat->getName()] = std::move(Pat);
- return true;
-}
-
-std::unique_ptr<Pattern>
-CombineRuleBuilder::parseInstructionMatcher(const Init &Arg, StringRef Name) {
- const DagInit *Matcher = getDagWithOperatorOfSubClass(Arg, "Instruction");
- if (!Matcher)
- return nullptr;
-
- auto &Instr = CGT.getInstruction(Matcher->getOperatorAsDef(RuleDef.getLoc()));
- auto Pat = std::make_unique<InstructionPattern>(Instr, Name);
-
- for (const auto &NameInit : Matcher->getArgNames())
- Pat->addOperand(NameInit->getAsUnquotedString());
-
- if (!Pat->checkSemantics(RuleDef.getLoc()))
- return nullptr;
-
- return std::move(Pat);
-}
-
-std::unique_ptr<Pattern>
-CombineRuleBuilder::parseWipMatchOpcodeMatcher(const Init &Arg,
- StringRef Name) {
- const DagInit *Matcher = getDagWithSpecificOperator(Arg, "wip_match_opcode");
- if (!Matcher)
- return nullptr;
-
- if (Matcher->getNumArgs() == 0) {
- PrintError("Empty wip_match_opcode");
- return nullptr;
- }
-
- // Each argument is an opcode that can match.
- auto Result = std::make_unique<AnyOpcodePattern>(Name);
- for (const auto &Arg : Matcher->getArgs()) {
- Record *OpcodeDef = getDefOfSubClass(*Arg, "Instruction");
- if (OpcodeDef) {
- Result->addOpcode(&CGT.getInstruction(OpcodeDef));
- continue;
- }
-
- PrintError("Arguments to wip_match_opcode must be instructions");
- return nullptr;
- }
-
- return std::move(Result);
-}
-
-bool CombineRuleBuilder::emitMatchPattern(CodeExpansions &CE,
- const InstructionPattern &IP) {
- auto &M = addRuleMatcher();
- InstructionMatcher &IM = M.addInstructionMatcher("root");
- declareInstExpansion(CE, IM, IP.getName());
-
- DenseSet<const Pattern *> SeenPats;
- if (!emitInstructionMatchPattern(CE, M, IM, IP, SeenPats))
- return false;
-
- // Emit remaining patterns
- for (auto &[_, Pat] : MatchPats) {
- if (SeenPats.contains(Pat.get()))
- continue;
-
- switch (Pat->getKind()) {
- case Pattern::K_AnyOpcode:
- PrintError("wip_match_opcode can not be used with instruction patterns!");
- return false;
- case Pattern::K_Inst:
- cast<InstructionPattern>(Pat.get())->reportUnreachable(RuleDef.getLoc());
- return false;
- case Pattern::K_CXX: {
- addCXXPredicate(IM, CE, *cast<CXXPattern>(Pat.get()));
- continue;
- }
- default:
- llvm_unreachable("unknown pattern kind!");
- }
- }
-
- return emitApplyPatterns(CE, M);
-}
-
-bool CombineRuleBuilder::emitMatchPattern(CodeExpansions &CE,
- const AnyOpcodePattern &AOP) {
-
- for (const CodeGenInstruction *CGI : AOP.insts()) {
- auto &M = addRuleMatcher("wip_match_opcode alternative '" +
- CGI->TheDef->getName() + "'");
-
- InstructionMatcher &IM = M.addInstructionMatcher(AOP.getName());
- declareInstExpansion(CE, IM, AOP.getName());
- // declareInstExpansion needs to be identical, otherwise we need to create a
- // CodeExpansions object here instead.
- assert(IM.getInsnVarID() == 0);
-
- IM.addPredicate<InstructionOpcodeMatcher>(CGI);
-
- // Emit remaining patterns.
- for (auto &[_, Pat] : MatchPats) {
- if (Pat.get() == &AOP)
- continue;
-
- switch (Pat->getKind()) {
- case Pattern::K_AnyOpcode:
- PrintError("wip_match_opcode can only be present once!");
- return false;
- case Pattern::K_Inst:
- cast<InstructionPattern>(Pat.get())->reportUnreachable(
- RuleDef.getLoc());
- return false;
- case Pattern::K_CXX: {
- addCXXPredicate(IM, CE, *cast<CXXPattern>(Pat.get()));
- break;
- }
- default:
- llvm_unreachable("unknown pattern kind!");
- }
- }
-
- if (!emitApplyPatterns(CE, M))
- return false;
- }
-
- return true;
-}
-
-bool CombineRuleBuilder::emitApplyPatterns(CodeExpansions &CE, RuleMatcher &M) {
- for (auto &[_, Pat] : ApplyPats) {
- switch (Pat->getKind()) {
- case Pattern::K_AnyOpcode:
- case Pattern::K_Inst:
- llvm_unreachable("Unsupported pattern kind in output pattern!");
- case Pattern::K_CXX: {
- CXXPattern *CXXPat = cast<CXXPattern>(Pat.get());
- const auto &ExpandedCode = CXXPat->expandCode(CE, RuleDef.getLoc());
- M.addAction<CustomCXXAction>(
- ExpandedCode.getEnumNameWithPrefix(CXXApplyPrefix));
- continue;
- }
- default:
- llvm_unreachable("Unknown pattern kind!");
- }
- }
-
- return true;
-}
-
-bool CombineRuleBuilder::emitInstructionMatchPattern(
- CodeExpansions &CE, RuleMatcher &M, InstructionMatcher &IM,
- const InstructionPattern &P, DenseSet<const Pattern *> &SeenPats) {
- if (SeenPats.contains(&P))
- return true;
-
- SeenPats.insert(&P);
-
- IM.addPredicate<InstructionOpcodeMatcher>(&P.getInst());
- declareInstExpansion(CE, IM, P.getName());
-
- unsigned OpIdx = 0;
- for (auto &O : P.operands()) {
- auto &OpTableEntry = OperandTable.find(O.Name)->second;
-
- OperandMatcher &OM =
- IM.addOperand(OpIdx++, O.Name, AllocatedTemporariesBaseID++);
- declareOperandExpansion(CE, OM, O.Name);
-
- if (O.IsDef)
- continue;
-
- if (InstructionPattern *DefPat = OpTableEntry.MatchPat) {
- auto InstOpM = OM.addPredicate<InstructionOperandMatcher>(M, O.Name);
- if (!InstOpM) {
- // TODO: copy-pasted from GlobalISelEmitter.cpp. Is it still relevant
- // here?
- PrintError("Nested instruction '" + DefPat->getName() +
- "' cannot be the same as another operand '" + O.Name + "'");
- return false;
- }
-
- if (!emitInstructionMatchPattern(CE, M, (*InstOpM)->getInsnMatcher(),
- *DefPat, SeenPats))
- return false;
- }
- }
-
- return true;
-}
-
-//===- GICombinerEmitter --------------------------------------------------===//
-
-/// This class is essentially the driver. It fetches all TableGen records, calls
-/// CombineRuleBuilder to build the MatchTable's RuleMatchers, then creates the
-/// MatchTable & emits it. It also handles emitting all the supporting code such
-/// as the list of LLTs, the CXXPredicates, etc.
-class GICombinerEmitter final : public GlobalISelMatchTableExecutorEmitter {
- RecordKeeper &Records;
- StringRef Name;
- const CodeGenTarget &Target;
- Record *Combiner;
- unsigned NextRuleID = 0;
-
- // List all combine rules (ID, name) imported.
- // Note that the combiner rule ID is different from the RuleMatcher ID. The
- // latter is internal to the MatchTable, the former is the canonical ID of the
- // combine rule used to disable/enable it.
- std::vector<std::pair<unsigned, std::string>> AllCombineRules;
-
- MatchTable buildMatchTable(MutableArrayRef<RuleMatcher> Rules);
-
- void emitRuleConfigImpl(raw_ostream &OS);
-
- void emitAdditionalImpl(raw_ostream &OS) override;
-
- void emitMIPredicateFns(raw_ostream &OS) override;
- void emitI64ImmPredicateFns(raw_ostream &OS) override;
- void emitAPFloatImmPredicateFns(raw_ostream &OS) override;
- void emitAPIntImmPredicateFns(raw_ostream &OS) override;
- void emitTestSimplePredicate(raw_ostream &OS) override;
- void emitRunCustomAction(raw_ostream &OS) override;
-
- void emitAdditionalTemporariesDecl(raw_ostream &OS,
- StringRef Indent) override;
-
- const CodeGenTarget &getTarget() const override { return Target; }
- StringRef getClassName() const override {
- return Combiner->getValueAsString("Classname");
- }
-
- std::string getRuleConfigClassName() const {
- return getClassName().str() + "RuleConfig";
- }
-
- void gatherRules(std::vector<RuleMatcher> &Rules,
- const std::vector<Record *> &&RulesAndGroups);
-
-public:
- explicit GICombinerEmitter(RecordKeeper &RK, const CodeGenTarget &Target,
- StringRef Name, Record *Combiner);
- ~GICombinerEmitter() {}
-
- void run(raw_ostream &OS);
-};
-
-void GICombinerEmitter::emitRuleConfigImpl(raw_ostream &OS) {
- OS << "struct " << getRuleConfigClassName() << " {\n"
- << " SparseBitVector<> DisabledRules;\n\n"
- << " bool isRuleEnabled(unsigned RuleID) const;\n"
- << " bool parseCommandLineOption();\n"
- << " bool setRuleEnabled(StringRef RuleIdentifier);\n"
- << " bool setRuleDisabled(StringRef RuleIdentifier);\n"
- << "};\n\n";
-
- std::vector<std::pair<std::string, std::string>> Cases;
- Cases.reserve(AllCombineRules.size());
-
- for (const auto &[ID, Name] : AllCombineRules)
- Cases.emplace_back(Name, "return " + to_string(ID) + ";\n");
-
- OS << "static std::optional<uint64_t> getRuleIdxForIdentifier(StringRef "
- "RuleIdentifier) {\n"
- << " uint64_t I;\n"
- << " // getAtInteger(...) returns false on success\n"
- << " bool Parsed = !RuleIdentifier.getAsInteger(0, I);\n"
- << " if (Parsed)\n"
- << " return I;\n\n"
- << "#ifndef NDEBUG\n";
- StringMatcher Matcher("RuleIdentifier", Cases, OS);
- Matcher.Emit();
- OS << "#endif // ifndef NDEBUG\n\n"
- << " return std::nullopt;\n"
- << "}\n";
-
- OS << "static std::optional<std::pair<uint64_t, uint64_t>> "
- "getRuleRangeForIdentifier(StringRef RuleIdentifier) {\n"
- << " std::pair<StringRef, StringRef> RangePair = "
- "RuleIdentifier.split('-');\n"
- << " if (!RangePair.second.empty()) {\n"
- << " const auto First = "
- "getRuleIdxForIdentifier(RangePair.first);\n"
- << " const auto Last = "
- "getRuleIdxForIdentifier(RangePair.second);\n"
- << " if (!First || !Last)\n"
- << " return std::nullopt;\n"
- << " if (First >= Last)\n"
- << " report_fatal_error(\"Beginning of range should be before "
- "end of range\");\n"
- << " return {{*First, *Last + 1}};\n"
- << " }\n"
- << " if (RangePair.first == \"*\") {\n"
- << " return {{0, " << AllCombineRules.size() << "}};\n"
- << " }\n"
- << " const auto I = getRuleIdxForIdentifier(RangePair.first);\n"
- << " if (!I)\n"
- << " return std::nullopt;\n"
- << " return {{*I, *I + 1}};\n"
- << "}\n\n";
-
- for (bool Enabled : {true, false}) {
- OS << "bool " << getRuleConfigClassName() << "::setRule"
- << (Enabled ? "Enabled" : "Disabled") << "(StringRef RuleIdentifier) {\n"
- << " auto MaybeRange = getRuleRangeForIdentifier(RuleIdentifier);\n"
- << " if (!MaybeRange)\n"
- << " return false;\n"
- << " for (auto I = MaybeRange->first; I < MaybeRange->second; ++I)\n"
- << " DisabledRules." << (Enabled ? "reset" : "set") << "(I);\n"
- << " return true;\n"
- << "}\n\n";
- }
-
- OS << "static std::vector<std::string> " << Name << "Option;\n"
- << "static cl::list<std::string> " << Name << "DisableOption(\n"
- << " \"" << Name.lower() << "-disable-rule\",\n"
- << " cl::desc(\"Disable one or more combiner rules temporarily in "
- << "the " << Name << " pass\"),\n"
- << " cl::CommaSeparated,\n"
- << " cl::Hidden,\n"
- << " cl::cat(GICombinerOptionCategory),\n"
- << " cl::callback([](const std::string &Str) {\n"
- << " " << Name << "Option.push_back(Str);\n"
- << " }));\n"
- << "static cl::list<std::string> " << Name << "OnlyEnableOption(\n"
- << " \"" << Name.lower() << "-only-enable-rule\",\n"
- << " cl::desc(\"Disable all rules in the " << Name
- << " pass then re-enable the specified ones\"),\n"
- << " cl::Hidden,\n"
- << " cl::cat(GICombinerOptionCategory),\n"
- << " cl::callback([](const std::string &CommaSeparatedArg) {\n"
- << " StringRef Str = CommaSeparatedArg;\n"
- << " " << Name << "Option.push_back(\"*\");\n"
- << " do {\n"
- << " auto X = Str.split(\",\");\n"
- << " " << Name << "Option.push_back((\"!\" + X.first).str());\n"
- << " Str = X.second;\n"
- << " } while (!Str.empty());\n"
- << " }));\n"
- << "\n\n"
- << "bool " << getRuleConfigClassName()
- << "::isRuleEnabled(unsigned RuleID) const {\n"
- << " return !DisabledRules.test(RuleID);\n"
- << "}\n"
- << "bool " << getRuleConfigClassName() << "::parseCommandLineOption() {\n"
- << " for (StringRef Identifier : " << Name << "Option) {\n"
- << " bool Enabled = Identifier.consume_front(\"!\");\n"
- << " if (Enabled && !setRuleEnabled(Identifier))\n"
- << " return false;\n"
- << " if (!Enabled && !setRuleDisabled(Identifier))\n"
- << " return false;\n"
- << " }\n"
- << " return true;\n"
- << "}\n\n";
-}
-
-void GICombinerEmitter::emitAdditionalImpl(raw_ostream &OS) {
- OS << "bool " << getClassName()
- << "::tryCombineAll(MachineInstr &I) const {\n"
- << " const TargetSubtargetInfo &ST = MF.getSubtarget();\n"
- << " const PredicateBitset AvailableFeatures = "
- "getAvailableFeatures();\n"
- << " NewMIVector OutMIs;\n"
- << " State.MIs.clear();\n"
- << " State.MIs.push_back(&I);\n"
- << " " << MatchDataInfo::StructName << " = "
- << MatchDataInfo::StructTypeName << "();\n\n"
- << " if (executeMatchTable(*this, OutMIs, State, ExecInfo"
- << ", getMatchTable(), *ST.getInstrInfo(), MRI, "
- "*MRI.getTargetRegisterInfo(), *ST.getRegBankInfo(), AvailableFeatures"
- << ", /*CoverageInfo*/ nullptr)) {\n"
- << " return true;\n"
- << " }\n\n"
- << " return false;\n"
- << "}\n\n";
-}
-
-void GICombinerEmitter::emitMIPredicateFns(raw_ostream &OS) {
- auto MatchCode = getSorted(AllCXXMatchCode);
- emitMIPredicateFnsImpl<const CXXPredicateCode *>(
- OS, "", ArrayRef<const CXXPredicateCode *>(MatchCode),
- [](const CXXPredicateCode *C) -> StringRef { return C->BaseEnumName; },
- [](const CXXPredicateCode *C) -> StringRef { return C->Code; });
-}
-
-void GICombinerEmitter::emitI64ImmPredicateFns(raw_ostream &OS) {
- // Unused, but still needs to be called.
- emitImmPredicateFnsImpl<unsigned>(
- OS, "I64", "int64_t", {}, [](unsigned) { return ""; },
- [](unsigned) { return ""; });
-}
-
-void GICombinerEmitter::emitAPFloatImmPredicateFns(raw_ostream &OS) {
- // Unused, but still needs to be called.
- emitImmPredicateFnsImpl<unsigned>(
- OS, "APFloat", "const APFloat &", {}, [](unsigned) { return ""; },
- [](unsigned) { return ""; });
-}
-
-void GICombinerEmitter::emitAPIntImmPredicateFns(raw_ostream &OS) {
- // Unused, but still needs to be called.
- emitImmPredicateFnsImpl<unsigned>(
- OS, "APInt", "const APInt &", {}, [](unsigned) { return ""; },
- [](unsigned) { return ""; });
-}
-
-void GICombinerEmitter::emitTestSimplePredicate(raw_ostream &OS) {
- if (!AllCombineRules.empty()) {
- OS << "enum {\n";
- std::string EnumeratorSeparator = " = GICXXPred_Invalid + 1,\n";
- // To avoid emitting a switch, we expect that all those rules are in order.
- // That way we can just get the RuleID from the enum by subtracting
- // (GICXXPred_Invalid + 1).
- unsigned ExpectedID = 0;
- (void)ExpectedID;
- for (const auto &[ID, _] : AllCombineRules) {
- assert(ExpectedID++ == ID && "combine rules are not ordered!");
- OS << " " << getIsEnabledPredicateEnumName(ID) << EnumeratorSeparator;
- EnumeratorSeparator = ",\n";
- }
- OS << "};\n\n";
- }
-
- OS << "bool " << getClassName()
- << "::testSimplePredicate(unsigned Predicate) const {\n"
- << " return RuleConfig.isRuleEnabled(Predicate - "
- "GICXXPred_Invalid - "
- "1);\n"
- << "}\n";
-}
-
-void GICombinerEmitter::emitRunCustomAction(raw_ostream &OS) {
- const auto ApplyCode = getSorted(AllCXXApplyCode);
-
- if (!ApplyCode.empty()) {
- OS << "enum {\n";
- std::string EnumeratorSeparator = " = GICXXCustomAction_Invalid + 1,\n";
- for (const auto &Apply : ApplyCode) {
- OS << " " << Apply->getEnumNameWithPrefix(CXXApplyPrefix)
- << EnumeratorSeparator;
- EnumeratorSeparator = ",\n";
- }
- OS << "};\n";
- }
-
- OS << "void " << getClassName()
- << "::runCustomAction(unsigned ApplyID, const MatcherState &State) const "
- "{\n";
- if (!ApplyCode.empty()) {
- OS << " switch(ApplyID) {\n";
- for (const auto &Apply : ApplyCode) {
- OS << " case " << Apply->getEnumNameWithPrefix(CXXApplyPrefix) << ":{\n"
- << " " << Apply->Code << "\n"
- << " return;\n";
- OS << " }\n";
- }
- OS << "}\n";
- }
- OS << " llvm_unreachable(\"Unknown Apply Action\");\n"
- << "}\n";
-}
-
-void GICombinerEmitter::emitAdditionalTemporariesDecl(raw_ostream &OS,
- StringRef Indent) {
- OS << Indent << "struct " << MatchDataInfo::StructTypeName << " {\n";
- for (const auto &[Type, VarNames] : AllMatchDataVars) {
- assert(!VarNames.empty() && "Cannot have no vars for this type!");
- OS << Indent << " " << Type << " " << join(VarNames, ", ") << ";\n";
- }
- OS << Indent << "};\n"
- << Indent << "mutable " << MatchDataInfo::StructTypeName << " "
- << MatchDataInfo::StructName << ";\n\n";
-}
-
-GICombinerEmitter::GICombinerEmitter(RecordKeeper &RK,
- const CodeGenTarget &Target,
- StringRef Name, Record *Combiner)
- : Records(RK), Name(Name), Target(Target), Combiner(Combiner) {}
-
-MatchTable
-GICombinerEmitter::buildMatchTable(MutableArrayRef<RuleMatcher> Rules) {
- std::vector<Matcher *> InputRules;
- for (Matcher &Rule : Rules)
- InputRules.push_back(&Rule);
-
- unsigned CurrentOrdering = 0;
- StringMap<unsigned> OpcodeOrder;
- for (RuleMatcher &Rule : Rules) {
- const StringRef Opcode = Rule.getOpcode();
- assert(!Opcode.empty() && "Didn't expect an undefined opcode");
- if (OpcodeOrder.count(Opcode) == 0)
- OpcodeOrder[Opcode] = CurrentOrdering++;
- }
-
- llvm::stable_sort(InputRules, [&OpcodeOrder](const Matcher *A,
- const Matcher *B) {
- auto *L = static_cast<const RuleMatcher *>(A);
- auto *R = static_cast<const RuleMatcher *>(B);
- return std::make_tuple(OpcodeOrder[L->getOpcode()], L->getNumOperands()) <
- std::make_tuple(OpcodeOrder[R->getOpcode()], R->getNumOperands());
- });
-
- for (Matcher *Rule : InputRules)
- Rule->optimize();
-
- std::vector<std::unique_ptr<Matcher>> MatcherStorage;
- std::vector<Matcher *> OptRules =
- optimizeRules<GroupMatcher>(InputRules, MatcherStorage);
-
- for (Matcher *Rule : OptRules)
- Rule->optimize();
-
- OptRules = optimizeRules<SwitchMatcher>(OptRules, MatcherStorage);
-
- return MatchTable::buildTable(OptRules, /*WithCoverage*/ false,
- /*IsCombiner*/ true);
-}
-
-/// Recurse into GICombineGroup's and flatten the ruleset into a simple list.
-void GICombinerEmitter::gatherRules(
- std::vector<RuleMatcher> &ActiveRules,
- const std::vector<Record *> &&RulesAndGroups) {
- for (Record *R : RulesAndGroups) {
- if (R->isValueUnset("Rules")) {
- AllCombineRules.emplace_back(NextRuleID, R->getName().str());
- CombineRuleBuilder CRB(Target, SubtargetFeatures, *R, NextRuleID++,
- ActiveRules);
-
- if (!CRB.parseAll())
- continue;
-
- if (StopAfterParse) {
- CRB.print(outs());
- continue;
- }
-
- if (!CRB.emitRuleMatchers())
- continue;
- } else
- gatherRules(ActiveRules, R->getValueAsListOfDefs("Rules"));
- }
-}
-
-void GICombinerEmitter::run(raw_ostream &OS) {
- Records.startTimer("Gather rules");
- std::vector<RuleMatcher> Rules;
- gatherRules(Rules, Combiner->getValueAsListOfDefs("Rules"));
- if (ErrorsPrinted)
- PrintFatalError(Combiner->getLoc(), "Failed to parse one or more rules");
-
- Records.startTimer("Creating Match Table");
- unsigned MaxTemporaries = 0;
- for (const auto &Rule : Rules)
- MaxTemporaries = std::max(MaxTemporaries, Rule.countRendererFns());
-
- const MatchTable Table = buildMatchTable(Rules);
-
- Records.startTimer("Emit combiner");
-
- emitSourceFileHeader(getClassName().str() + " Combiner Match Table", OS);
-
- // Unused
- std::vector<StringRef> CustomRendererFns;
- // Unused, but hack to avoid empty declarator
- std::vector<LLTCodeGen> TypeObjects = {LLTCodeGen(LLT::scalar(1))};
- // Unused
- std::vector<Record *> ComplexPredicates;
-
- // GET_GICOMBINER_DEPS, which pulls in extra dependencies.
- OS << "#ifdef GET_GICOMBINER_DEPS\n"
- << "#include \"llvm/ADT/SparseBitVector.h\"\n"
- << "namespace llvm {\n"
- << "extern cl::OptionCategory GICombinerOptionCategory;\n"
- << "} // end namespace llvm\n"
- << "#endif // ifdef GET_GICOMBINER_DEPS\n\n";
-
- // GET_GICOMBINER_TYPES, which needs to be included before the declaration of
- // the class.
- OS << "#ifdef GET_GICOMBINER_TYPES\n";
- emitRuleConfigImpl(OS);
- OS << "#endif // ifdef GET_GICOMBINER_TYPES\n\n";
- emitPredicateBitset(OS, "GET_GICOMBINER_TYPES");
-
- // GET_GICOMBINER_CLASS_MEMBERS, which need to be included inside the class.
- emitPredicatesDecl(OS, "GET_GICOMBINER_CLASS_MEMBERS");
- emitTemporariesDecl(OS, "GET_GICOMBINER_CLASS_MEMBERS");
-
- // GET_GICOMBINER_IMPL, which needs to be included outside the class.
- emitExecutorImpl(OS, Table, TypeObjects, Rules, ComplexPredicates,
- CustomRendererFns, "GET_GICOMBINER_IMPL");
-
- // GET_GICOMBINER_CONSTRUCTOR_INITS, which are in the constructor's
- // initializer list.
- emitPredicatesInit(OS, "GET_GICOMBINER_CONSTRUCTOR_INITS");
- emitTemporariesInit(OS, MaxTemporaries, "GET_GICOMBINER_CONSTRUCTOR_INITS");
-}
-
-} // end anonymous namespace
-
-//===----------------------------------------------------------------------===//
-
-static void EmitGICombiner(RecordKeeper &RK, raw_ostream &OS) {
- CodeGenTarget Target(RK);
-
- if (SelectedCombiners.empty())
- PrintFatalError("No combiners selected with -combiners");
- for (const auto &Combiner : SelectedCombiners) {
- Record *CombinerDef = RK.getDef(Combiner);
- if (!CombinerDef)
- PrintFatalError("Could not find " + Combiner);
- GICombinerEmitter(RK, Target, Combiner, CombinerDef).run(OS);
- }
-}
-
-static TableGen::Emitter::Opt X("gen-global-isel-combiner-matchtable",
- EmitGICombiner,
- "Generate GlobalISel combiner Match Table");
diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp
index 3bdcfec06e24..8d9ded1b2ac5 100644
--- a/llvm/utils/TableGen/GlobalISelEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp
@@ -312,6 +312,8 @@ public:
void emitTestSimplePredicate(raw_ostream &OS) override;
void emitRunCustomAction(raw_ostream &OS) override;
+ void postProcessRule(RuleMatcher &M);
+
const CodeGenTarget &getTarget() const override { return Target; }
StringRef getClassName() const override { return ClassName; }
@@ -355,8 +357,8 @@ private:
/// to the number of named operands that predicate expects. Store locations in
/// StoreIdxForName correspond to the order in which operand names appear in
/// predicate's argument list.
- /// When we visit named leaf operand and WaitingForNamedOperands is not zero,
- /// add matcher that will record operand and decrease counter.
+ /// When we visit named operand and WaitingForNamedOperands is not zero, add
+ /// matcher that will record operand and decrease counter.
unsigned WaitingForNamedOperands = 0;
StringMap<unsigned> StoreIdxForName;
@@ -413,6 +415,8 @@ private:
void declareSubtargetFeature(Record *Predicate);
+ unsigned declareHwModeCheck(StringRef HwModeFeatures);
+
MatchTable buildMatchTable(MutableArrayRef<RuleMatcher> Rules, bool Optimize,
bool WithCoverage);
@@ -498,6 +502,10 @@ GlobalISelEmitter::getEquivNode(Record &Equiv, const TreePatternNode *N) const {
return &Target.getInstruction(Equiv.getValueAsDef("IfFloatingPoint"));
}
+ if (!Equiv.isValueUnset("IfConvergent") &&
+ N->getIntrinsicInfo(CGP)->isConvergent)
+ return &Target.getInstruction(Equiv.getValueAsDef("IfConvergent"));
+
for (const TreePredicateCall &Call : N->getPredicateCalls()) {
const TreePredicateFn &Predicate = Call.Fn;
if (!Equiv.isValueUnset("IfSignExtend") &&
@@ -779,13 +787,11 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher(
}
}
- bool IsAtomic = false;
if (SrcGIEquivOrNull &&
SrcGIEquivOrNull->getValueAsBit("CheckMMOIsNonAtomic"))
InsnMatcher.addPredicate<AtomicOrderingMMOPredicateMatcher>("NotAtomic");
else if (SrcGIEquivOrNull &&
SrcGIEquivOrNull->getValueAsBit("CheckMMOIsAtomic")) {
- IsAtomic = true;
InsnMatcher.addPredicate<AtomicOrderingMMOPredicateMatcher>(
"Unordered", AtomicOrderingMMOPredicateMatcher::AO_OrStronger);
}
@@ -839,31 +845,13 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher(
}
}
- // Hack around an unfortunate mistake in how atomic store (and really
- // atomicrmw in general) operands were ordered. A ISD::STORE used the order
- // <stored value>, <pointer> order. ISD::ATOMIC_STORE used the opposite,
- // <pointer>, <stored value>. In GlobalISel there's just the one store
- // opcode, so we need to swap the operands here to get the right type check.
- if (IsAtomic && SrcGIOrNull->TheDef->getName() == "G_STORE") {
- assert(NumChildren == 2 && "wrong operands for atomic store");
-
- const TreePatternNode *PtrChild = Src->getChild(0);
- const TreePatternNode *ValueChild = Src->getChild(1);
-
- if (auto Error = importChildMatcher(Rule, InsnMatcher, PtrChild, true,
- false, 1, TempOpIdx))
- return std::move(Error);
-
- if (auto Error = importChildMatcher(Rule, InsnMatcher, ValueChild, false,
- false, 0, TempOpIdx))
- return std::move(Error);
- return InsnMatcher;
- }
-
// Match the used operands (i.e. the children of the operator).
bool IsIntrinsic =
SrcGIOrNull->TheDef->getName() == "G_INTRINSIC" ||
- SrcGIOrNull->TheDef->getName() == "G_INTRINSIC_W_SIDE_EFFECTS";
+ SrcGIOrNull->TheDef->getName() == "G_INTRINSIC_W_SIDE_EFFECTS" ||
+ SrcGIOrNull->TheDef->getName() == "G_INTRINSIC_CONVERGENT" ||
+ SrcGIOrNull->TheDef->getName() ==
+ "G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS";
const CodeGenIntrinsic *II = Src->getIntrinsicInfo(CGP);
if (IsIntrinsic && !II)
return failedImport("Expected IntInit containing intrinsic ID)");
@@ -1009,6 +997,17 @@ Error GlobalISelEmitter::importChildMatcher(
to_string(*SrcChild) + ")");
}
+ // Try look up SrcChild for a (named) predicate operand if there is any.
+ if (WaitingForNamedOperands) {
+ auto &ScopedNames = SrcChild->getNamesAsPredicateArg();
+ if (!ScopedNames.empty()) {
+ auto PA = ScopedNames.begin();
+ std::string Name = getScopedName(PA->getScope(), PA->getIdentifier());
+ OM.addPredicate<RecordNamedOperandMatcher>(StoreIdxForName[Name], Name);
+ --WaitingForNamedOperands;
+ }
+ }
+
// Check for nested instructions.
if (!SrcChild->isLeaf()) {
if (SrcChild->getOperator()->isSubClassOf("ComplexPattern")) {
@@ -1073,13 +1072,6 @@ Error GlobalISelEmitter::importChildMatcher(
if (auto *ChildDefInit = dyn_cast<DefInit>(SrcChild->getLeafValue())) {
auto *ChildRec = ChildDefInit->getDef();
- if (WaitingForNamedOperands) {
- auto PA = SrcChild->getNamesAsPredicateArg().begin();
- std::string Name = getScopedName(PA->getScope(), PA->getIdentifier());
- OM.addPredicate<RecordNamedOperandMatcher>(StoreIdxForName[Name], Name);
- --WaitingForNamedOperands;
- }
-
// Check for register classes.
if (ChildRec->isSubClassOf("RegisterClass") ||
ChildRec->isSubClassOf("RegisterOperand")) {
@@ -1908,6 +1900,9 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
if (auto Error = importRulePredicates(M, Predicates))
return std::move(Error);
+ if (!P.getHwModeFeatures().empty())
+ M.addHwModeIdx(declareHwModeCheck(P.getHwModeFeatures()));
+
// Next, analyze the pattern operators.
TreePatternNode *Src = P.getSrcPattern();
TreePatternNode *Dst = P.getDstPattern();
@@ -2272,10 +2267,10 @@ void GlobalISelEmitter::emitAdditionalImpl(raw_ostream &OS) {
"&CoverageInfo) const {\n"
<< " const PredicateBitset AvailableFeatures = "
"getAvailableFeatures();\n"
- << " NewMIVector OutMIs;\n"
+ << " MachineIRBuilder B(I);\n"
<< " State.MIs.clear();\n"
<< " State.MIs.push_back(&I);\n\n"
- << " if (executeMatchTable(*this, OutMIs, State, ExecInfo"
+ << " if (executeMatchTable(*this, State, ExecInfo, B"
<< ", getMatchTable(), TII, MF->getRegInfo(), TRI, RBI, AvailableFeatures"
<< ", &CoverageInfo)) {\n"
<< " return true;\n"
@@ -2357,12 +2352,38 @@ void GlobalISelEmitter::emitTestSimplePredicate(raw_ostream &OS) {
void GlobalISelEmitter::emitRunCustomAction(raw_ostream &OS) {
OS << "void " << getClassName()
- << "::runCustomAction(unsigned, const MatcherState&) const {\n"
+ << "::runCustomAction(unsigned, const MatcherState&, NewMIVector &) const "
+ "{\n"
<< " llvm_unreachable(\"" + getClassName() +
" does not support custom C++ actions!\");\n"
<< "}\n";
}
+void GlobalISelEmitter::postProcessRule(RuleMatcher &M) {
+ SmallPtrSet<Record *, 16> UsedRegs;
+
+ // TODO: deal with subregs?
+ for (auto &A : M.actions()) {
+ auto *MI = dyn_cast<BuildMIAction>(A.get());
+ if (!MI)
+ continue;
+
+ for (auto *Use : MI->getCGI()->ImplicitUses)
+ UsedRegs.insert(Use);
+ }
+
+ for (auto &A : M.actions()) {
+ auto *MI = dyn_cast<BuildMIAction>(A.get());
+ if (!MI)
+ continue;
+
+ for (auto *Def : MI->getCGI()->ImplicitDefs) {
+ if (!UsedRegs.contains(Def))
+ MI->setDeadImplicitDef(Def);
+ }
+ }
+}
+
void GlobalISelEmitter::run(raw_ostream &OS) {
if (!UseCoverageFile.empty()) {
RuleCoverage = CodeGenCoverage();
@@ -2420,6 +2441,7 @@ void GlobalISelEmitter::run(raw_ostream &OS) {
"Pattern is not covered by a test");
}
Rules.push_back(std::move(MatcherOrErr.get()));
+ postProcessRule(Rules.back());
}
// Comparison function to order records by name.
@@ -2484,9 +2506,11 @@ void GlobalISelEmitter::run(raw_ostream &OS) {
}
void GlobalISelEmitter::declareSubtargetFeature(Record *Predicate) {
- if (SubtargetFeatures.count(Predicate) == 0)
- SubtargetFeatures.emplace(
- Predicate, SubtargetFeatureInfo(Predicate, SubtargetFeatures.size()));
+ SubtargetFeatures.try_emplace(Predicate, Predicate, SubtargetFeatures.size());
+}
+
+unsigned GlobalISelEmitter::declareHwModeCheck(StringRef HwModeFeatures) {
+ return HwModes.emplace(HwModeFeatures.str(), HwModes.size()).first->second;
}
} // end anonymous namespace
diff --git a/llvm/utils/TableGen/GlobalISelMatchTable.cpp b/llvm/utils/TableGen/GlobalISelMatchTable.cpp
index aab772f020a6..481f3f16e013 100644
--- a/llvm/utils/TableGen/GlobalISelMatchTable.cpp
+++ b/llvm/utils/TableGen/GlobalISelMatchTable.cpp
@@ -43,11 +43,13 @@ std::string getMatchOpcodeForImmPredicate(const TreePredicateFn &Predicate) {
//===- Helpers ------------------------------------------------------------===//
-std::string
-getNameForFeatureBitset(const std::vector<Record *> &FeatureBitset) {
+std::string getNameForFeatureBitset(const std::vector<Record *> &FeatureBitset,
+ int HwModeIdx) {
std::string Name = "GIFBS";
for (const auto &Feature : FeatureBitset)
Name += ("_" + Feature->getName()).str();
+ if (HwModeIdx >= 0)
+ Name += ("_HwMode" + std::to_string(HwModeIdx));
return Name;
}
@@ -241,7 +243,7 @@ void MatchTable::emitDeclaration(raw_ostream &OS) const {
if (I->Flags & MatchTableRecord::MTRF_Outdent)
Indentation -= 2;
}
- OS << "};\n";
+ OS << "}; // Size: " << (CurrentSize * 8) << " bytes\n";
}
MatchTable MatchTable::buildTable(ArrayRef<Matcher *> Rules, bool WithCoverage,
@@ -820,6 +822,15 @@ const OperandMatcher &RuleMatcher::getPhysRegOperandMatcher(Record *Reg) const {
return *I->second;
}
+OperandMatcher &RuleMatcher::getOperandMatcher(StringRef Name) {
+ const auto &I = DefinedOperands.find(Name);
+
+ if (I == DefinedOperands.end())
+ PrintFatalError(SrcLoc, "Operand " + Name + " was not declared in matcher");
+
+ return *I->second;
+}
+
const OperandMatcher &RuleMatcher::getOperandMatcher(StringRef Name) const {
const auto &I = DefinedOperands.find(Name);
@@ -851,9 +862,10 @@ void RuleMatcher::emit(MatchTable &Table) {
<< MatchTable::Comment(("Rule ID " + Twine(RuleID) + " //").str())
<< MatchTable::LineBreak;
- if (!RequiredFeatures.empty()) {
+ if (!RequiredFeatures.empty() || HwModeIdx >= 0) {
Table << MatchTable::Opcode("GIM_CheckFeatures")
- << MatchTable::NamedValue(getNameForFeatureBitset(RequiredFeatures))
+ << MatchTable::NamedValue(
+ getNameForFeatureBitset(RequiredFeatures, HwModeIdx))
<< MatchTable::LineBreak;
}
@@ -866,6 +878,10 @@ void RuleMatcher::emit(MatchTable &Table) {
Matchers.front()->emitPredicateOpcodes(Table, *this);
+ // Check if it's safe to replace registers.
+ for (const auto &MA : Actions)
+ MA->emitAdditionalPredicates(Table, *this);
+
// We must also check if it's safe to fold the matched instructions.
if (InsnVariableIDs.size() >= 2) {
// Invert the map to create stable ordering (by var names)
@@ -1074,6 +1090,17 @@ void RecordNamedOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
<< MatchTable::Comment("Name : " + Name) << MatchTable::LineBreak;
}
+//===- RecordRegisterType ------------------------------------------===//
+
+void RecordRegisterType::emitPredicateOpcodes(MatchTable &Table,
+ RuleMatcher &Rule) const {
+ assert(Idx < 0 && "Temp types always have negative indexes!");
+ Table << MatchTable::Opcode("GIM_RecordRegType") << MatchTable::Comment("MI")
+ << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op")
+ << MatchTable::IntValue(OpIdx) << MatchTable::Comment("TempTypeIdx")
+ << MatchTable::IntValue(Idx) << MatchTable::LineBreak;
+}
+
//===- ComplexPatternOperandMatcher ---------------------------------------===//
void ComplexPatternOperandMatcher::emitPredicateOpcodes(
@@ -1104,7 +1131,7 @@ void RegisterBankOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
<< MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID)
<< MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx)
<< MatchTable::Comment("RC")
- << MatchTable::NamedValue(RC.getQualifiedName() + "RegClassID")
+ << MatchTable::NamedValue(RC.getQualifiedIdName())
<< MatchTable::LineBreak;
}
@@ -1189,6 +1216,18 @@ std::string OperandMatcher::getOperandExpr(unsigned InsnVarID) const {
unsigned OperandMatcher::getInsnVarID() const { return Insn.getInsnVarID(); }
+TempTypeIdx OperandMatcher::getTempTypeIdx(RuleMatcher &Rule) {
+ if (TTIdx >= 0) {
+ // Temp type index not assigned yet, so assign one and add the necessary
+ // predicate.
+ TTIdx = Rule.getNextTempTypeIdx();
+ assert(TTIdx < 0);
+ addPredicate<RecordRegisterType>(TTIdx);
+ return TTIdx;
+ }
+ return TTIdx;
+}
+
void OperandMatcher::emitPredicateOpcodes(MatchTable &Table,
RuleMatcher &Rule) {
if (!Optimized) {
@@ -1502,6 +1541,24 @@ void GenericInstructionPredicateMatcher::emitPredicateOpcodes(
<< MatchTable::LineBreak;
}
+//===- MIFlagsInstructionPredicateMatcher ---------------------------------===//
+
+bool MIFlagsInstructionPredicateMatcher::isIdentical(
+ const PredicateMatcher &B) const {
+ if (!InstructionPredicateMatcher::isIdentical(B))
+ return false;
+ const auto &Other =
+ static_cast<const MIFlagsInstructionPredicateMatcher &>(B);
+ return Flags == Other.Flags && CheckNot == Other.CheckNot;
+}
+
+void MIFlagsInstructionPredicateMatcher::emitPredicateOpcodes(
+ MatchTable &Table, RuleMatcher &Rule) const {
+ Table << MatchTable::Opcode(CheckNot ? "GIM_MIFlagsNot" : "GIM_MIFlags")
+ << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID)
+ << MatchTable::NamedValue(join(Flags, " | ")) << MatchTable::LineBreak;
+}
+
//===- InstructionMatcher -------------------------------------------------===//
OperandMatcher &
@@ -1917,6 +1974,30 @@ void BuildMIAction::chooseInsnToMutate(RuleMatcher &Rule) {
void BuildMIAction::emitActionOpcodes(MatchTable &Table,
RuleMatcher &Rule) const {
+ const auto AddMIFlags = [&]() {
+ for (const InstructionMatcher *IM : CopiedFlags) {
+ Table << MatchTable::Opcode("GIR_CopyMIFlags")
+ << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
+ << MatchTable::Comment("OldInsnID")
+ << MatchTable::IntValue(IM->getInsnVarID())
+ << MatchTable::LineBreak;
+ }
+
+ if (!SetFlags.empty()) {
+ Table << MatchTable::Opcode("GIR_SetMIFlags")
+ << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
+ << MatchTable::NamedValue(join(SetFlags, " | "))
+ << MatchTable::LineBreak;
+ }
+
+ if (!UnsetFlags.empty()) {
+ Table << MatchTable::Opcode("GIR_UnsetMIFlags")
+ << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
+ << MatchTable::NamedValue(join(UnsetFlags, " | "))
+ << MatchTable::LineBreak;
+ }
+ };
+
if (Matched) {
assert(canMutate(Rule, Matched) &&
"Arranged to mutate an insn that isn't mutatable");
@@ -1935,9 +2016,12 @@ void BuildMIAction::emitActionOpcodes(MatchTable &Table,
auto Namespace = Def->getValue("Namespace")
? Def->getValueAsString("Namespace")
: "";
+ const bool IsDead = DeadImplicitDefs.contains(Def);
Table << MatchTable::Opcode("GIR_AddImplicitDef")
<< MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
<< MatchTable::NamedValue(Namespace, Def->getName())
+ << (IsDead ? MatchTable::NamedValue("RegState", "Dead")
+ : MatchTable::IntValue(0))
<< MatchTable::LineBreak;
}
for (auto *Use : I->ImplicitUses) {
@@ -1950,6 +2034,8 @@ void BuildMIAction::emitActionOpcodes(MatchTable &Table,
<< MatchTable::LineBreak;
}
}
+
+ AddMIFlags();
return;
}
@@ -1963,6 +2049,19 @@ void BuildMIAction::emitActionOpcodes(MatchTable &Table,
for (const auto &Renderer : OperandRenderers)
Renderer->emitRenderOpcodes(Table, Rule);
+ for (auto [OpIdx, Def] : enumerate(I->ImplicitDefs)) {
+ auto Namespace =
+ Def->getValue("Namespace") ? Def->getValueAsString("Namespace") : "";
+ if (DeadImplicitDefs.contains(Def)) {
+ Table
+ << MatchTable::Opcode("GIR_SetImplicitDefDead")
+ << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
+ << MatchTable::Comment(
+ ("OpIdx for " + Namespace + "::" + Def->getName() + "").str())
+ << MatchTable::IntValue(OpIdx) << MatchTable::LineBreak;
+ }
+ }
+
if (I->mayLoad || I->mayStore) {
Table << MatchTable::Opcode("GIR_MergeMemOperands")
<< MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
@@ -1984,13 +2083,74 @@ void BuildMIAction::emitActionOpcodes(MatchTable &Table,
<< MatchTable::LineBreak;
}
+ AddMIFlags();
+
// FIXME: This is a hack but it's sufficient for ISel. We'll need to do
// better for combines. Particularly when there are multiple match
// roots.
if (InsnID == 0)
- Table << MatchTable::Opcode("GIR_EraseFromParent")
- << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
+ EraseInstAction::emitActionOpcodes(Table, Rule, /*InsnID*/ 0);
+}
+
+//===- BuildConstantAction ------------------------------------------------===//
+
+void BuildConstantAction::emitActionOpcodes(MatchTable &Table,
+ RuleMatcher &Rule) const {
+ Table << MatchTable::Opcode("GIR_BuildConstant")
+ << MatchTable::Comment("TempRegID") << MatchTable::IntValue(TempRegID)
+ << MatchTable::Comment("Val") << MatchTable::IntValue(Val)
+ << MatchTable::LineBreak;
+}
+
+//===- EraseInstAction ----------------------------------------------------===//
+
+void EraseInstAction::emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule,
+ unsigned InsnID) {
+ // Avoid erasing the same inst twice.
+ if (!Rule.tryEraseInsnID(InsnID))
+ return;
+
+ Table << MatchTable::Opcode("GIR_EraseFromParent")
+ << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
+ << MatchTable::LineBreak;
+}
+
+void EraseInstAction::emitActionOpcodes(MatchTable &Table,
+ RuleMatcher &Rule) const {
+ emitActionOpcodes(Table, Rule, InsnID);
+}
+
+//===- ReplaceRegAction ---------------------------------------------------===//
+
+void ReplaceRegAction::emitAdditionalPredicates(MatchTable &Table,
+ RuleMatcher &Rule) const {
+ if (TempRegID != (unsigned)-1)
+ return;
+
+ Table << MatchTable::Opcode("GIM_CheckCanReplaceReg")
+ << MatchTable::Comment("OldInsnID") << MatchTable::IntValue(OldInsnID)
+ << MatchTable::Comment("OldOpIdx") << MatchTable::IntValue(OldOpIdx)
+ << MatchTable::Comment("NewInsnId") << MatchTable::IntValue(NewInsnId)
+ << MatchTable::Comment("NewOpIdx") << MatchTable::IntValue(NewOpIdx)
+ << MatchTable::LineBreak;
+}
+
+void ReplaceRegAction::emitActionOpcodes(MatchTable &Table,
+ RuleMatcher &Rule) const {
+ if (TempRegID != (unsigned)-1) {
+ Table << MatchTable::Opcode("GIR_ReplaceRegWithTempReg")
+ << MatchTable::Comment("OldInsnID") << MatchTable::IntValue(OldInsnID)
+ << MatchTable::Comment("OldOpIdx") << MatchTable::IntValue(OldOpIdx)
+ << MatchTable::Comment("TempRegID") << MatchTable::IntValue(TempRegID)
<< MatchTable::LineBreak;
+ } else {
+ Table << MatchTable::Opcode("GIR_ReplaceReg")
+ << MatchTable::Comment("OldInsnID") << MatchTable::IntValue(OldInsnID)
+ << MatchTable::Comment("OldOpIdx") << MatchTable::IntValue(OldOpIdx)
+ << MatchTable::Comment("NewInsnId") << MatchTable::IntValue(NewInsnId)
+ << MatchTable::Comment("NewOpIdx") << MatchTable::IntValue(NewOpIdx)
+ << MatchTable::LineBreak;
+ }
}
//===- ConstrainOperandToRegClassAction -----------------------------------===//
@@ -2000,7 +2160,7 @@ void ConstrainOperandToRegClassAction::emitActionOpcodes(
Table << MatchTable::Opcode("GIR_ConstrainOperandRC")
<< MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
<< MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx)
- << MatchTable::NamedValue(RC.getQualifiedName() + "RegClassID")
+ << MatchTable::NamedValue(RC.getQualifiedIdName())
<< MatchTable::LineBreak;
}
@@ -2010,9 +2170,7 @@ void MakeTempRegisterAction::emitActionOpcodes(MatchTable &Table,
RuleMatcher &Rule) const {
Table << MatchTable::Opcode("GIR_MakeTempReg")
<< MatchTable::Comment("TempRegID") << MatchTable::IntValue(TempRegID)
- << MatchTable::Comment("TypeID")
- << MatchTable::NamedValue(Ty.getCxxEnumValue())
- << MatchTable::LineBreak;
+ << MatchTable::Comment("TypeID") << Ty << MatchTable::LineBreak;
}
} // namespace gi
diff --git a/llvm/utils/TableGen/GlobalISelMatchTable.h b/llvm/utils/TableGen/GlobalISelMatchTable.h
index fcb3392226c1..469390d73123 100644
--- a/llvm/utils/TableGen/GlobalISelMatchTable.h
+++ b/llvm/utils/TableGen/GlobalISelMatchTable.h
@@ -59,7 +59,8 @@ using GISelFlags = std::uint16_t;
//===- Helper functions ---------------------------------------------------===//
-std::string getNameForFeatureBitset(const std::vector<Record *> &FeatureBitset);
+std::string getNameForFeatureBitset(const std::vector<Record *> &FeatureBitset,
+ int HwModeIdx);
/// Takes a sequence of \p Rules and group them based on the predicates
/// they share. \p MatcherStorage is used as a memory container
@@ -272,6 +273,40 @@ extern std::set<LLTCodeGen> KnownTypes;
/// MVTs that don't map cleanly to an LLT (e.g., iPTR, *any, ...).
std::optional<LLTCodeGen> MVTToLLT(MVT::SimpleValueType SVT);
+using TempTypeIdx = int64_t;
+class LLTCodeGenOrTempType {
+public:
+ LLTCodeGenOrTempType(const LLTCodeGen &LLT) : Data(LLT) {}
+ LLTCodeGenOrTempType(TempTypeIdx TempTy) : Data(TempTy) {}
+
+ bool isLLTCodeGen() const { return std::holds_alternative<LLTCodeGen>(Data); }
+ bool isTempTypeIdx() const {
+ return std::holds_alternative<TempTypeIdx>(Data);
+ }
+
+ const LLTCodeGen &getLLTCodeGen() const {
+ assert(isLLTCodeGen());
+ return std::get<LLTCodeGen>(Data);
+ }
+
+ TempTypeIdx getTempTypeIdx() const {
+ assert(isTempTypeIdx());
+ return std::get<TempTypeIdx>(Data);
+ }
+
+private:
+ std::variant<LLTCodeGen, TempTypeIdx> Data;
+};
+
+inline MatchTable &operator<<(MatchTable &Table,
+ const LLTCodeGenOrTempType &Ty) {
+ if (Ty.isLLTCodeGen())
+ Table << MatchTable::NamedValue(Ty.getLLTCodeGen().getCxxEnumValue());
+ else
+ Table << MatchTable::IntValue(Ty.getTempTypeIdx());
+ return Table;
+}
+
//===- Matchers -----------------------------------------------------------===//
class Matcher {
public:
@@ -458,6 +493,12 @@ protected:
/// ID for the next temporary register ID allocated with allocateTempRegID()
unsigned NextTempRegID;
+ /// ID for the next recorded type. Starts at -1 and counts down.
+ TempTypeIdx NextTempTypeIdx = -1;
+
+ // HwMode predicate index for this rule. -1 if no HwMode.
+ int HwModeIdx = -1;
+
/// Current GISelFlags
GISelFlags Flags = 0;
@@ -465,6 +506,8 @@ protected:
std::vector<Record *> RequiredFeatures;
std::vector<std::unique_ptr<PredicateMatcher>> EpilogueMatchers;
+ DenseSet<unsigned> ErasedInsnIDs;
+
ArrayRef<SMLoc> SrcLoc;
typedef std::tuple<Record *, unsigned, unsigned>
@@ -492,15 +535,25 @@ public:
RuleMatcher(RuleMatcher &&Other) = default;
RuleMatcher &operator=(RuleMatcher &&Other) = default;
+ TempTypeIdx getNextTempTypeIdx() { return NextTempTypeIdx--; }
+
uint64_t getRuleID() const { return RuleID; }
InstructionMatcher &addInstructionMatcher(StringRef SymbolicName);
void addRequiredFeature(Record *Feature);
const std::vector<Record *> &getRequiredFeatures() const;
+ void addHwModeIdx(unsigned Idx) { HwModeIdx = Idx; }
+ int getHwModeIdx() const { return HwModeIdx; }
+
void addRequiredSimplePredicate(StringRef PredName);
const std::vector<std::string> &getRequiredSimplePredicates();
+ /// Attempts to mark \p ID as erased (GIR_EraseFromParent called on it).
+ /// If \p ID has already been erased, returns false and GIR_EraseFromParent
+ /// should NOT be emitted.
+ bool tryEraseInsnID(unsigned ID) { return ErasedInsnIDs.insert(ID).second; }
+
// Emplaces an action of the specified Kind at the end of the action list.
//
// Returns a reference to the newly created action.
@@ -526,6 +579,8 @@ public:
std::make_unique<Kind>(std::forward<Args>(args)...));
}
+ void setPermanentGISelFlags(GISelFlags V) { Flags = V; }
+
// Update the active GISelFlags based on the GISelFlags Record R.
// A SaveAndRestore object is returned so the old GISelFlags are restored
// at the end of the scope.
@@ -586,6 +641,7 @@ public:
}
InstructionMatcher &getInstructionMatcher(StringRef SymbolicName) const;
+ OperandMatcher &getOperandMatcher(StringRef Name);
const OperandMatcher &getOperandMatcher(StringRef Name) const;
const OperandMatcher &getPhysRegOperandMatcher(Record *) const;
@@ -648,6 +704,10 @@ public:
}
bool predicates_empty() const { return Predicates.empty(); }
+ template <typename Ty> bool contains() const {
+ return any_of(Predicates, [&](auto &P) { return isa<Ty>(P.get()); });
+ }
+
std::unique_ptr<PredicateTy> predicates_pop_front() {
std::unique_ptr<PredicateTy> Front = std::move(Predicates.front());
Predicates.pop_front();
@@ -730,6 +790,7 @@ public:
IPM_VectorSplatImm,
IPM_NoUse,
IPM_GenericPredicate,
+ IPM_MIFlags,
OPM_SameOperand,
OPM_ComplexPattern,
OPM_IntrinsicID,
@@ -742,6 +803,7 @@ public:
OPM_RegBank,
OPM_MBB,
OPM_RecordNamedOperand,
+ OPM_RecordRegType,
};
protected:
@@ -943,6 +1005,30 @@ public:
RuleMatcher &Rule) const override;
};
+/// Generates code to store a register operand's type into the set of temporary
+/// LLTs.
+class RecordRegisterType : public OperandPredicateMatcher {
+protected:
+ TempTypeIdx Idx;
+
+public:
+ RecordRegisterType(unsigned InsnVarID, unsigned OpIdx, TempTypeIdx Idx)
+ : OperandPredicateMatcher(OPM_RecordRegType, InsnVarID, OpIdx), Idx(Idx) {
+ }
+
+ static bool classof(const PredicateMatcher *P) {
+ return P->getKind() == OPM_RecordRegType;
+ }
+
+ bool isIdentical(const PredicateMatcher &B) const override {
+ return OperandPredicateMatcher::isIdentical(B) &&
+ Idx == cast<RecordRegisterType>(&B)->Idx;
+ }
+
+ void emitPredicateOpcodes(MatchTable &Table,
+ RuleMatcher &Rule) const override;
+};
+
/// Generates code to check that an operand is a particular target constant.
class ComplexPatternOperandMatcher : public OperandPredicateMatcher {
protected:
@@ -1149,6 +1235,8 @@ protected:
/// countRendererFns().
unsigned AllocatedTemporariesBaseID;
+ TempTypeIdx TTIdx = 0;
+
public:
OperandMatcher(InstructionMatcher &Insn, unsigned OpIdx,
const std::string &SymbolicName,
@@ -1176,6 +1264,11 @@ public:
unsigned getOpIdx() const { return OpIdx; }
unsigned getInsnVarID() const;
+ /// If this OperandMatcher has not been assigned a TempTypeIdx yet, assigns it
+ /// one and adds a `RecordRegisterType` predicate to this matcher. If one has
+ /// already been assigned, simply returns it.
+ TempTypeIdx getTempTypeIdx(RuleMatcher &Rule);
+
std::string getOperandExpr(unsigned InsnVarID) const;
InstructionMatcher &getInstructionMatcher() const { return Insn; }
@@ -1536,6 +1629,28 @@ public:
RuleMatcher &Rule) const override;
};
+class MIFlagsInstructionPredicateMatcher : public InstructionPredicateMatcher {
+ SmallVector<StringRef, 2> Flags;
+ bool CheckNot; // false = GIM_MIFlags, true = GIM_MIFlagsNot
+
+public:
+ MIFlagsInstructionPredicateMatcher(unsigned InsnVarID,
+ ArrayRef<StringRef> FlagsToCheck,
+ bool CheckNot = false)
+ : InstructionPredicateMatcher(IPM_MIFlags, InsnVarID),
+ Flags(FlagsToCheck), CheckNot(CheckNot) {
+ sort(Flags);
+ }
+
+ static bool classof(const InstructionPredicateMatcher *P) {
+ return P->getKind() == IPM_MIFlags;
+ }
+
+ bool isIdentical(const PredicateMatcher &B) const override;
+ void emitPredicateOpcodes(MatchTable &Table,
+ RuleMatcher &Rule) const override;
+};
+
/// Generates code to check for the absence of use of the result.
// TODO? Generalize this to support checking for one use.
class NoUsePredicateMatcher : public InstructionPredicateMatcher {
@@ -1930,23 +2045,41 @@ public:
};
/// Adds a specific immediate to the instruction being built.
+/// If a LLT is passed, a ConstantInt immediate is created instead.
class ImmRenderer : public OperandRenderer {
protected:
unsigned InsnID;
int64_t Imm;
+ std::optional<LLTCodeGenOrTempType> CImmLLT;
public:
ImmRenderer(unsigned InsnID, int64_t Imm)
: OperandRenderer(OR_Imm), InsnID(InsnID), Imm(Imm) {}
+ ImmRenderer(unsigned InsnID, int64_t Imm, const LLTCodeGenOrTempType &CImmLLT)
+ : OperandRenderer(OR_Imm), InsnID(InsnID), Imm(Imm), CImmLLT(CImmLLT) {
+ if (CImmLLT.isLLTCodeGen())
+ KnownTypes.insert(CImmLLT.getLLTCodeGen());
+ }
+
static bool classof(const OperandRenderer *R) {
return R->getKind() == OR_Imm;
}
void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override {
- Table << MatchTable::Opcode("GIR_AddImm") << MatchTable::Comment("InsnID")
- << MatchTable::IntValue(InsnID) << MatchTable::Comment("Imm")
- << MatchTable::IntValue(Imm) << MatchTable::LineBreak;
+ if (CImmLLT) {
+ assert(Table.isCombiner() &&
+ "ConstantInt immediate are only for combiners!");
+ Table << MatchTable::Opcode("GIR_AddCImm")
+ << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
+ << MatchTable::Comment("Type") << *CImmLLT
+ << MatchTable::Comment("Imm") << MatchTable::IntValue(Imm)
+ << MatchTable::LineBreak;
+ } else {
+ Table << MatchTable::Opcode("GIR_AddImm") << MatchTable::Comment("InsnID")
+ << MatchTable::IntValue(InsnID) << MatchTable::Comment("Imm")
+ << MatchTable::IntValue(Imm) << MatchTable::LineBreak;
+ }
}
};
@@ -2051,11 +2184,34 @@ public:
/// * Adding an operand to an instruction.
class MatchAction {
public:
+ enum ActionKind {
+ AK_DebugComment,
+ AK_CustomCXX,
+ AK_BuildMI,
+ AK_BuildConstantMI,
+ AK_EraseInst,
+ AK_ReplaceReg,
+ AK_ConstraintOpsToDef,
+ AK_ConstraintOpsToRC,
+ AK_MakeTempReg,
+ };
+
+ MatchAction(ActionKind K) : Kind(K) {}
+
+ ActionKind getKind() const { return Kind; }
+
virtual ~MatchAction() {}
+ // Some actions may need to add extra predicates to ensure they can run.
+ virtual void emitAdditionalPredicates(MatchTable &Table,
+ RuleMatcher &Rule) const {}
+
/// Emit the MatchTable opcodes to implement the action.
virtual void emitActionOpcodes(MatchTable &Table,
RuleMatcher &Rule) const = 0;
+
+private:
+ ActionKind Kind;
};
/// Generates a comment describing the matched rule being acted upon.
@@ -2064,7 +2220,12 @@ private:
std::string S;
public:
- DebugCommentAction(StringRef S) : S(std::string(S)) {}
+ DebugCommentAction(StringRef S)
+ : MatchAction(AK_DebugComment), S(std::string(S)) {}
+
+ static bool classof(const MatchAction *A) {
+ return A->getKind() == AK_DebugComment;
+ }
void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override {
Table << MatchTable::Comment(S) << MatchTable::LineBreak;
@@ -2075,7 +2236,12 @@ class CustomCXXAction : public MatchAction {
std::string FnEnumName;
public:
- CustomCXXAction(StringRef FnEnumName) : FnEnumName(FnEnumName.str()) {}
+ CustomCXXAction(StringRef FnEnumName)
+ : MatchAction(AK_CustomCXX), FnEnumName(FnEnumName.str()) {}
+
+ static bool classof(const MatchAction *A) {
+ return A->getKind() == AK_CustomCXX;
+ }
void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override;
};
@@ -2088,19 +2254,36 @@ private:
const CodeGenInstruction *I;
InstructionMatcher *Matched;
std::vector<std::unique_ptr<OperandRenderer>> OperandRenderers;
+ SmallPtrSet<Record *, 4> DeadImplicitDefs;
+
+ std::vector<const InstructionMatcher *> CopiedFlags;
+ std::vector<StringRef> SetFlags;
+ std::vector<StringRef> UnsetFlags;
/// True if the instruction can be built solely by mutating the opcode.
bool canMutate(RuleMatcher &Rule, const InstructionMatcher *Insn) const;
public:
BuildMIAction(unsigned InsnID, const CodeGenInstruction *I)
- : InsnID(InsnID), I(I), Matched(nullptr) {}
+ : MatchAction(AK_BuildMI), InsnID(InsnID), I(I), Matched(nullptr) {}
+
+ static bool classof(const MatchAction *A) {
+ return A->getKind() == AK_BuildMI;
+ }
unsigned getInsnID() const { return InsnID; }
const CodeGenInstruction *getCGI() const { return I; }
+ void addSetMIFlags(StringRef Flag) { SetFlags.push_back(Flag); }
+ void addUnsetMIFlags(StringRef Flag) { UnsetFlags.push_back(Flag); }
+ void addCopiedMIFlags(const InstructionMatcher &IM) {
+ CopiedFlags.push_back(&IM);
+ }
+
void chooseInsnToMutate(RuleMatcher &Rule);
+ void setDeadImplicitDef(Record *R) { DeadImplicitDefs.insert(R); }
+
template <class Kind, class... Args> Kind &addRenderer(Args &&...args) {
OperandRenderers.emplace_back(
std::make_unique<Kind>(InsnID, std::forward<Args>(args)...));
@@ -2110,13 +2293,76 @@ public:
void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override;
};
+/// Generates code to create a constant that defines a TempReg.
+/// The instruction created is usually a G_CONSTANT but it could also be a
+/// G_BUILD_VECTOR for vector types.
+class BuildConstantAction : public MatchAction {
+ unsigned TempRegID;
+ int64_t Val;
+
+public:
+ BuildConstantAction(unsigned TempRegID, int64_t Val)
+ : MatchAction(AK_BuildConstantMI), TempRegID(TempRegID), Val(Val) {}
+
+ static bool classof(const MatchAction *A) {
+ return A->getKind() == AK_BuildConstantMI;
+ }
+
+ void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override;
+};
+
+class EraseInstAction : public MatchAction {
+ unsigned InsnID;
+
+public:
+ EraseInstAction(unsigned InsnID)
+ : MatchAction(AK_EraseInst), InsnID(InsnID) {}
+
+ static bool classof(const MatchAction *A) {
+ return A->getKind() == AK_EraseInst;
+ }
+
+ void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override;
+ static void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule,
+ unsigned InsnID);
+};
+
+class ReplaceRegAction : public MatchAction {
+ unsigned OldInsnID, OldOpIdx;
+ unsigned NewInsnId = -1, NewOpIdx;
+ unsigned TempRegID = -1;
+
+public:
+ ReplaceRegAction(unsigned OldInsnID, unsigned OldOpIdx, unsigned NewInsnId,
+ unsigned NewOpIdx)
+ : MatchAction(AK_EraseInst), OldInsnID(OldInsnID), OldOpIdx(OldOpIdx),
+ NewInsnId(NewInsnId), NewOpIdx(NewOpIdx) {}
+
+ ReplaceRegAction(unsigned OldInsnID, unsigned OldOpIdx, unsigned TempRegID)
+ : MatchAction(AK_EraseInst), OldInsnID(OldInsnID), OldOpIdx(OldOpIdx),
+ TempRegID(TempRegID) {}
+
+ static bool classof(const MatchAction *A) {
+ return A->getKind() == AK_ReplaceReg;
+ }
+
+ void emitAdditionalPredicates(MatchTable &Table,
+ RuleMatcher &Rule) const override;
+ void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override;
+};
+
/// Generates code to constrain the operands of an output instruction to the
/// register classes specified by the definition of that instruction.
class ConstrainOperandsToDefinitionAction : public MatchAction {
unsigned InsnID;
public:
- ConstrainOperandsToDefinitionAction(unsigned InsnID) : InsnID(InsnID) {}
+ ConstrainOperandsToDefinitionAction(unsigned InsnID)
+ : MatchAction(AK_ConstraintOpsToDef), InsnID(InsnID) {}
+
+ static bool classof(const MatchAction *A) {
+ return A->getKind() == AK_ConstraintOpsToDef;
+ }
void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override {
Table << MatchTable::Opcode("GIR_ConstrainSelectedInstOperands")
@@ -2135,7 +2381,12 @@ class ConstrainOperandToRegClassAction : public MatchAction {
public:
ConstrainOperandToRegClassAction(unsigned InsnID, unsigned OpIdx,
const CodeGenRegisterClass &RC)
- : InsnID(InsnID), OpIdx(OpIdx), RC(RC) {}
+ : MatchAction(AK_ConstraintOpsToRC), InsnID(InsnID), OpIdx(OpIdx),
+ RC(RC) {}
+
+ static bool classof(const MatchAction *A) {
+ return A->getKind() == AK_ConstraintOpsToRC;
+ }
void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override;
};
@@ -2144,13 +2395,18 @@ public:
/// instructions together.
class MakeTempRegisterAction : public MatchAction {
private:
- LLTCodeGen Ty;
+ LLTCodeGenOrTempType Ty;
unsigned TempRegID;
public:
- MakeTempRegisterAction(const LLTCodeGen &Ty, unsigned TempRegID)
- : Ty(Ty), TempRegID(TempRegID) {
- KnownTypes.insert(Ty);
+ MakeTempRegisterAction(const LLTCodeGenOrTempType &Ty, unsigned TempRegID)
+ : MatchAction(AK_MakeTempReg), Ty(Ty), TempRegID(TempRegID) {
+ if (Ty.isLLTCodeGen())
+ KnownTypes.insert(Ty.getLLTCodeGen());
+ }
+
+ static bool classof(const MatchAction *A) {
+ return A->getKind() == AK_MakeTempReg;
}
void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override;
diff --git a/llvm/utils/TableGen/GlobalISelMatchTableExecutorEmitter.cpp b/llvm/utils/TableGen/GlobalISelMatchTableExecutorEmitter.cpp
index 8dc422b140a5..c6cd3240a94e 100644
--- a/llvm/utils/TableGen/GlobalISelMatchTableExecutorEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelMatchTableExecutorEmitter.cpp
@@ -15,7 +15,7 @@ using namespace llvm::gi;
void GlobalISelMatchTableExecutorEmitter::emitSubtargetFeatureBitsetImpl(
raw_ostream &OS, ArrayRef<RuleMatcher> Rules) {
SubtargetFeatureInfo::emitSubtargetFeatureBitEnumeration(SubtargetFeatures,
- OS);
+ OS, &HwModes);
// Separate subtarget features by how often they must be recomputed.
SubtargetFeatureInfoMap ModuleFeatures;
@@ -33,7 +33,7 @@ void GlobalISelMatchTableExecutorEmitter::emitSubtargetFeatureBitsetImpl(
SubtargetFeatureInfo::emitComputeAvailableFeatures(
getTarget().getName(), getClassName(), "computeAvailableModuleFeatures",
- ModuleFeatures, OS);
+ ModuleFeatures, OS, "", &HwModes);
OS << "void " << getClassName()
<< "::setupGeneratedPerFunctionState(MachineFunction &MF) {\n"
@@ -49,24 +49,27 @@ void GlobalISelMatchTableExecutorEmitter::emitSubtargetFeatureBitsetImpl(
// Emit a table containing the PredicateBitsets objects needed by the matcher
// and an enum for the matcher to reference them with.
- std::vector<std::vector<Record *>> FeatureBitsets;
+ std::vector<std::pair<std::vector<Record *>, int>> FeatureBitsets;
FeatureBitsets.reserve(Rules.size());
for (auto &Rule : Rules)
- FeatureBitsets.push_back(Rule.getRequiredFeatures());
- llvm::sort(FeatureBitsets, [&](const std::vector<Record *> &A,
- const std::vector<Record *> &B) {
- if (A.size() < B.size())
- return true;
- if (A.size() > B.size())
- return false;
- for (auto [First, Second] : zip(A, B)) {
- if (First->getName() < Second->getName())
- return true;
- if (First->getName() > Second->getName())
- return false;
- }
- return false;
- });
+ FeatureBitsets.emplace_back(Rule.getRequiredFeatures(),
+ Rule.getHwModeIdx());
+ llvm::sort(FeatureBitsets,
+ [&](const std::pair<std::vector<Record *>, int> &A,
+ const std::pair<std::vector<Record *>, int> &B) {
+ if (A.first.size() < B.first.size())
+ return true;
+ if (A.first.size() > B.first.size())
+ return false;
+ for (auto [First, Second] : zip(A.first, B.first)) {
+ if (First->getName() < Second->getName())
+ return true;
+ if (First->getName() > Second->getName())
+ return false;
+ }
+
+ return (A.second < B.second);
+ });
FeatureBitsets.erase(
std::unique(FeatureBitsets.begin(), FeatureBitsets.end()),
FeatureBitsets.end());
@@ -74,22 +77,28 @@ void GlobalISelMatchTableExecutorEmitter::emitSubtargetFeatureBitsetImpl(
<< "enum {\n"
<< " GIFBS_Invalid,\n";
for (const auto &FeatureBitset : FeatureBitsets) {
- if (FeatureBitset.empty())
+ if (FeatureBitset.first.empty() && FeatureBitset.second < 0)
continue;
- OS << " " << getNameForFeatureBitset(FeatureBitset) << ",\n";
+ OS << " "
+ << getNameForFeatureBitset(FeatureBitset.first, FeatureBitset.second)
+ << ",\n";
}
OS << "};\n"
- << "const static PredicateBitset FeatureBitsets[] {\n"
+ << "constexpr static PredicateBitset FeatureBitsets[] {\n"
<< " {}, // GIFBS_Invalid\n";
for (const auto &FeatureBitset : FeatureBitsets) {
- if (FeatureBitset.empty())
+ if (FeatureBitset.first.empty() && FeatureBitset.second < 0)
continue;
OS << " {";
- for (const auto &Feature : FeatureBitset) {
+ for (const auto &Feature : FeatureBitset.first) {
const auto &I = SubtargetFeatures.find(Feature);
assert(I != SubtargetFeatures.end() && "Didn't import predicate?");
OS << I->second.getEnumBitName() << ", ";
}
+ // HwModeIdx
+ if (FeatureBitset.second >= 0) {
+ OS << "Feature_HwMode" << FeatureBitset.second << "Bit, ";
+ }
OS << "},\n";
}
OS << "};\n\n";
@@ -184,11 +193,11 @@ void GlobalISelMatchTableExecutorEmitter::emitExecutorImpl(
void GlobalISelMatchTableExecutorEmitter::emitPredicateBitset(
raw_ostream &OS, StringRef IfDefName) {
+ unsigned Size = SubtargetFeatures.size() + HwModes.size();
OS << "#ifdef " << IfDefName << "\n"
- << "const unsigned MAX_SUBTARGET_PREDICATES = " << SubtargetFeatures.size()
- << ";\n"
+ << "const unsigned MAX_SUBTARGET_PREDICATES = " << Size << ";\n"
<< "using PredicateBitset = "
- "llvm::PredicateBitsetImpl<MAX_SUBTARGET_PREDICATES>;\n"
+ "llvm::Bitset<MAX_SUBTARGET_PREDICATES>;\n"
<< "#endif // ifdef " << IfDefName << "\n\n";
}
@@ -222,7 +231,8 @@ void GlobalISelMatchTableExecutorEmitter::emitTemporariesDecl(
", const MatcherState &State) "
"const override;\n"
<< " bool testSimplePredicate(unsigned PredicateID) const override;\n"
- << " void runCustomAction(unsigned FnID, const MatcherState &State) "
+ << " void runCustomAction(unsigned FnID, const MatcherState &State, "
+ "NewMIVector &OutMIs) "
"const override;\n";
emitAdditionalTemporariesDecl(OS, " ");
OS << "#endif // ifdef " << IfDefName << "\n\n";
diff --git a/llvm/utils/TableGen/GlobalISelMatchTableExecutorEmitter.h b/llvm/utils/TableGen/GlobalISelMatchTableExecutorEmitter.h
index d526e08a96e3..7e952d6df309 100644
--- a/llvm/utils/TableGen/GlobalISelMatchTableExecutorEmitter.h
+++ b/llvm/utils/TableGen/GlobalISelMatchTableExecutorEmitter.h
@@ -20,7 +20,6 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include <functional>
-#include <vector>
namespace llvm {
class CodeGenTarget;
@@ -105,11 +104,12 @@ class GlobalISelMatchTableExecutorEmitter {
if (!Predicates.empty()) {
OS << " switch (PredicateID) {\n";
for (const auto &Pred : Predicates) {
- const auto Code = GetPredCode(Pred);
+ // Ensure all code is indented.
+ const auto Code = join(split(GetPredCode(Pred).str(), "\n"), "\n ");
OS << " case GICXXPred_" << TypeIdentifier << "_Predicate_"
<< GetPredEnumName(Pred) << ": {\n"
<< " " << Code << "\n";
- if (!StringRef(Code).ltrim().startswith("return")) {
+ if (!StringRef(Code).ltrim().starts_with("return")) {
OS << " llvm_unreachable(\"" << GetPredEnumName(Pred)
<< " should have returned\");\n";
}
@@ -222,6 +222,8 @@ public:
// Map of predicates to their subtarget features.
SubtargetFeatureInfoMap SubtargetFeatures;
+
+ std::map<std::string, unsigned> HwModes;
};
} // namespace llvm
diff --git a/llvm/utils/TableGen/InfoByHwMode.cpp b/llvm/utils/TableGen/InfoByHwMode.cpp
index 4e9136e936af..7e4ab5346621 100644
--- a/llvm/utils/TableGen/InfoByHwMode.cpp
+++ b/llvm/utils/TableGen/InfoByHwMode.cpp
@@ -35,6 +35,8 @@ ValueTypeByHwMode::ValueTypeByHwMode(Record *R, const CodeGenHwModes &CGH) {
assert(I.second && "Duplicate entry?");
(void)I;
}
+ if (R->isSubClassOf("PtrValueType"))
+ PtrAddrSpace = R->getValueAsInt("AddrSpace");
}
ValueTypeByHwMode::ValueTypeByHwMode(Record *R, MVT T) : ValueTypeByHwMode(T) {
diff --git a/llvm/utils/TableGen/InfoByHwMode.h b/llvm/utils/TableGen/InfoByHwMode.h
index b8a6645baca5..a9295f6dc342 100644
--- a/llvm/utils/TableGen/InfoByHwMode.h
+++ b/llvm/utils/TableGen/InfoByHwMode.h
@@ -15,7 +15,6 @@
#define LLVM_UTILS_TABLEGEN_INFOBYHWMODE_H
#include "CodeGenHwModes.h"
-#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/CodeGen/MachineValueType.h"
diff --git a/llvm/utils/TableGen/InstrInfoEmitter.cpp b/llvm/utils/TableGen/InstrInfoEmitter.cpp
index cab9ecd4ea97..b2250c0cf989 100644
--- a/llvm/utils/TableGen/InstrInfoEmitter.cpp
+++ b/llvm/utils/TableGen/InstrInfoEmitter.cpp
@@ -22,6 +22,7 @@
#include "Types.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"
@@ -474,7 +475,7 @@ void InstrInfoEmitter::emitOperandTypeMappings(
OS << "LLVM_READONLY\n";
OS << "static int getMemOperandSize(int OpType) {\n";
OS << " switch (OpType) {\n";
- std::map<int, std::vector<StringRef>> SizeToOperandName;
+ std::map<int, SmallVector<StringRef, 0>> SizeToOperandName;
for (const Record *Op : Operands) {
if (!Op->isSubClassOf("X86MemOperand"))
continue;
@@ -482,7 +483,7 @@ void InstrInfoEmitter::emitOperandTypeMappings(
SizeToOperandName[Size].push_back(Op->getName());
}
OS << " default: return 0;\n";
- for (auto KV : SizeToOperandName) {
+ for (const auto &KV : SizeToOperandName) {
for (const StringRef &OperandName : KV.second)
OS << " case OpTypes::" << OperandName << ":\n";
OS << " return " << KV.first << ";\n\n";
@@ -733,7 +734,9 @@ void InstrInfoEmitter::emitFeatureVerifier(raw_ostream &OS,
std::map<Record *, SubtargetFeatureInfo, LessRecordByID> SubtargetFeatures;
SubtargetFeatures.insert(All.begin(), All.end());
- OS << "#if defined(ENABLE_INSTR_PREDICATE_VERIFIER) && !defined(NDEBUG)\n"
+ OS << "#if (defined(ENABLE_INSTR_PREDICATE_VERIFIER) && !defined(NDEBUG)) "
+ << "||\\\n"
+ << " defined(GET_AVAILABLE_OPCODE_CHECKER)\n"
<< "#define GET_COMPUTE_FEATURES\n"
<< "#endif\n";
OS << "#ifdef GET_COMPUTE_FEATURES\n"
@@ -799,7 +802,7 @@ void InstrInfoEmitter::emitFeatureVerifier(raw_ostream &OS,
OS << "},\n";
}
OS << " };\n"
- << " static " << getMinimalTypeForRange(FeatureBitsets.size())
+ << " static constexpr " << getMinimalTypeForRange(FeatureBitsets.size())
<< " RequiredFeaturesRefs[] = {\n";
unsigned InstIdx = 0;
for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) {
@@ -826,6 +829,25 @@ void InstrInfoEmitter::emitFeatureVerifier(raw_ostream &OS,
<< "} // end namespace llvm\n"
<< "#endif // GET_COMPUTE_FEATURES\n\n";
+ OS << "#ifdef GET_AVAILABLE_OPCODE_CHECKER\n"
+ << "#undef GET_AVAILABLE_OPCODE_CHECKER\n"
+ << "namespace llvm {\n"
+ << "namespace " << Target.getName() << "_MC {\n";
+ OS << "bool isOpcodeAvailable("
+ << "unsigned Opcode, const FeatureBitset &Features) {\n"
+ << " FeatureBitset AvailableFeatures = "
+ << "computeAvailableFeatures(Features);\n"
+ << " FeatureBitset RequiredFeatures = "
+ << "computeRequiredFeatures(Opcode);\n"
+ << " FeatureBitset MissingFeatures =\n"
+ << " (AvailableFeatures & RequiredFeatures) ^\n"
+ << " RequiredFeatures;\n"
+ << " return !MissingFeatures.any();\n"
+ << "}\n";
+ OS << "} // end namespace " << Target.getName() << "_MC\n"
+ << "} // end namespace llvm\n"
+ << "#endif // GET_AVAILABLE_OPCODE_CHECKER\n\n";
+
OS << "#ifdef ENABLE_INSTR_PREDICATE_VERIFIER\n"
<< "#undef ENABLE_INSTR_PREDICATE_VERIFIER\n"
<< "#include <sstream>\n\n";
diff --git a/llvm/utils/TableGen/IntrinsicEmitter.cpp b/llvm/utils/TableGen/IntrinsicEmitter.cpp
index 09aad78536fe..28604c5600bf 100644
--- a/llvm/utils/TableGen/IntrinsicEmitter.cpp
+++ b/llvm/utils/TableGen/IntrinsicEmitter.cpp
@@ -12,7 +12,6 @@
#include "CodeGenIntrinsics.h"
#include "SequenceToOffsetTable.h"
-#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
@@ -214,7 +213,7 @@ void IntrinsicEmitter::EmitTargetInfo(const CodeGenIntrinsicTable &Ints,
<< " size_t Count;\n"
<< "};\n";
OS << "static constexpr IntrinsicTargetInfo TargetInfos[] = {\n";
- for (auto Target : Ints.Targets)
+ for (const auto &Target : Ints.Targets)
OS << " {llvm::StringLiteral(\"" << Target.Name << "\"), " << Target.Offset
<< ", " << Target.Count << "},\n";
OS << "};\n";
diff --git a/llvm/utils/TableGen/OptParserEmitter.cpp b/llvm/utils/TableGen/OptParserEmitter.cpp
index a04680b5d91e..257cd44d9e80 100644
--- a/llvm/utils/TableGen/OptParserEmitter.cpp
+++ b/llvm/utils/TableGen/OptParserEmitter.cpp
@@ -34,30 +34,14 @@ static raw_ostream &write_cstring(raw_ostream &OS, llvm::StringRef Str) {
return OS;
}
-static std::string getOptionSpelling(const Record &R, size_t &PrefixLength) {
+static std::string getOptionPrefixedName(const Record &R) {
std::vector<StringRef> Prefixes = R.getValueAsListOfStrings("Prefixes");
StringRef Name = R.getValueAsString("Name");
- if (Prefixes.empty()) {
- PrefixLength = 0;
+ if (Prefixes.empty())
return Name.str();
- }
-
- PrefixLength = Prefixes[0].size();
- return (Twine(Prefixes[0]) + Twine(Name)).str();
-}
-static std::string getOptionSpelling(const Record &R) {
- size_t PrefixLength;
- return getOptionSpelling(R, PrefixLength);
-}
-
-static void emitNameUsingSpelling(raw_ostream &OS, const Record &R) {
- size_t PrefixLength;
- OS << "llvm::StringLiteral(";
- write_cstring(
- OS, StringRef(getOptionSpelling(R, PrefixLength)).substr(PrefixLength));
- OS << ")";
+ return (Prefixes[0] + Twine(Name)).str();
}
class MarshallingInfo {
@@ -105,8 +89,6 @@ struct SimpleEnumValueTable {
}
void emit(raw_ostream &OS) const {
- write_cstring(OS, StringRef(getOptionSpelling(R)));
- OS << ", ";
OS << ShouldParse;
OS << ", ";
OS << ShouldAlwaysEmit;
@@ -320,7 +302,7 @@ static void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
OS << "INVALID";
// The other option arguments (unused for groups).
- OS << ", INVALID, nullptr, 0, 0";
+ OS << ", INVALID, nullptr, 0, 0, 0";
// The option help text.
if (!isa<UnsetInit>(R.getValueInit("HelpText"))) {
@@ -346,8 +328,8 @@ static void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
std::vector<StringRef> RPrefixes = R.getValueAsListOfStrings("Prefixes");
OS << Prefixes[PrefixKeyT(RPrefixes.begin(), RPrefixes.end())] << ", ";
- // The option string.
- emitNameUsingSpelling(OS, R);
+ // The option prefixed name.
+ write_cstring(OS, getOptionPrefixedName(R));
// The option identifier name.
OS << ", " << getOptionName(R);
@@ -358,8 +340,10 @@ static void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
// The containing option group (if any).
OS << ", ";
const ListInit *GroupFlags = nullptr;
+ const ListInit *GroupVis = nullptr;
if (const DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) {
GroupFlags = DI->getDef()->getValueAsListInit("Flags");
+ GroupVis = DI->getDef()->getValueAsListInit("Visibility");
OS << getOptionName(*DI->getDef());
} else
OS << "INVALID";
@@ -386,7 +370,7 @@ static void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
OS << "\"";
}
- // The option flags.
+ // "Flags" for the option, such as HelpHidden and Render*
OS << ", ";
int NumFlags = 0;
const ListInit *LI = R.getValueAsListInit("Flags");
@@ -400,6 +384,21 @@ static void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
if (NumFlags == 0)
OS << '0';
+ // Option visibility, for sharing options between drivers.
+ OS << ", ";
+ int NumVisFlags = 0;
+ LI = R.getValueAsListInit("Visibility");
+ for (Init *I : *LI)
+ OS << (NumVisFlags++ ? " | " : "")
+ << cast<DefInit>(I)->getDef()->getName();
+ if (GroupVis) {
+ for (Init *I : *GroupVis)
+ OS << (NumVisFlags++ ? " | " : "")
+ << cast<DefInit>(I)->getDef()->getName();
+ }
+ if (NumVisFlags == 0)
+ OS << '0';
+
// The option parameter field.
OS << ", " << R.getValueAsInt("NumArgs");
diff --git a/llvm/utils/TableGen/OptRSTEmitter.cpp b/llvm/utils/TableGen/OptRSTEmitter.cpp
index 87e755d943a1..5a7f079dc168 100644
--- a/llvm/utils/TableGen/OptRSTEmitter.cpp
+++ b/llvm/utils/TableGen/OptRSTEmitter.cpp
@@ -91,7 +91,7 @@ static void EmitOptRST(RecordKeeper &Records, raw_ostream &OS) {
HelpText += join(Values.begin(), Values.end() - 1, "', '");
HelpText += "' or '";
}
- HelpText += (Values.front() + "'.").str();
+ HelpText += (Values.back() + "'.").str();
}
if (!HelpText.empty()) {
diff --git a/llvm/utils/TableGen/RISCVTargetDefEmitter.cpp b/llvm/utils/TableGen/RISCVTargetDefEmitter.cpp
index 12174fd83f56..7a6439cb9491 100644
--- a/llvm/utils/TableGen/RISCVTargetDefEmitter.cpp
+++ b/llvm/utils/TableGen/RISCVTargetDefEmitter.cpp
@@ -49,7 +49,7 @@ static std::string getMArch(const Record &Rec) {
static void EmitRISCVTargetDef(RecordKeeper &RK, raw_ostream &OS) {
OS << "#ifndef PROC\n"
- << "#define PROC(ENUM, NAME, DEFAULT_MARCH)\n"
+ << "#define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_UNALIGNED_ACCESS)\n"
<< "#endif\n\n";
// Iterate on all definition records.
@@ -60,9 +60,14 @@ static void EmitRISCVTargetDef(RecordKeeper &RK, raw_ostream &OS) {
if (MArch.empty())
MArch = getMArch(*Rec);
+ const bool FastUnalignedAccess =
+ any_of(Rec->getValueAsListOfDefs("Features"), [&](auto &Feature) {
+ return Feature->getValueAsString("Name") == "fast-unaligned-access";
+ });
+
OS << "PROC(" << Rec->getName() << ", "
<< "{\"" << Rec->getValueAsString("Name") << "\"}, "
- << "{\"" << MArch << "\"})\n";
+ << "{\"" << MArch << "\"}, " << FastUnalignedAccess << ")\n";
}
OS << "\n#undef PROC\n";
OS << "\n";
diff --git a/llvm/utils/TableGen/RegisterBankEmitter.cpp b/llvm/utils/TableGen/RegisterBankEmitter.cpp
index 2d23bf86b6ad..f851d9a79870 100644
--- a/llvm/utils/TableGen/RegisterBankEmitter.cpp
+++ b/llvm/utils/TableGen/RegisterBankEmitter.cpp
@@ -231,9 +231,7 @@ void RegisterBankEmitter::emitBaseClassImplementation(
for (const auto &RCs : RCsGroupedByWord) {
OS << " // " << LowestIdxInWord << "-" << (LowestIdxInWord + 31) << "\n";
for (const auto &RC : RCs) {
- std::string QualifiedRegClassID =
- (Twine(RC->Namespace) + "::" + RC->getName() + "RegClassID").str();
- OS << " (1u << (" << QualifiedRegClassID << " - "
+ OS << " (1u << (" << RC->getQualifiedIdName() << " - "
<< LowestIdxInWord << ")) |\n";
}
OS << " 0,\n";
@@ -246,7 +244,7 @@ void RegisterBankEmitter::emitBaseClassImplementation(
for (const auto &Bank : Banks) {
std::string QualifiedBankID =
(TargetName + "::" + Bank.getEnumeratorName()).str();
- OS << "const RegisterBank " << Bank.getInstanceVarName() << "(/* ID */ "
+ OS << "constexpr RegisterBank " << Bank.getInstanceVarName() << "(/* ID */ "
<< QualifiedBankID << ", /* Name */ \"" << Bank.getName() << "\", "
<< "/* CoveredRegClasses */ " << Bank.getCoverageArrayName()
<< ", /* NumRegClasses */ "
diff --git a/llvm/utils/TableGen/RegisterInfoEmitter.cpp b/llvm/utils/TableGen/RegisterInfoEmitter.cpp
index 3101081114fb..92ef9199cc47 100644
--- a/llvm/utils/TableGen/RegisterInfoEmitter.cpp
+++ b/llvm/utils/TableGen/RegisterInfoEmitter.cpp
@@ -146,8 +146,7 @@ void RegisterInfoEmitter::runEnums(raw_ostream &OS,
OS << "namespace " << Namespace << " {\n";
OS << "enum {\n";
for (const auto &RC : RegisterClasses)
- OS << " " << RC.getName() << "RegClassID"
- << " = " << RC.EnumValue << ",\n";
+ OS << " " << RC.getIdName() << " = " << RC.EnumValue << ",\n";
OS << "\n};\n";
if (!Namespace.empty())
OS << "} // end namespace " << Namespace << "\n\n";
@@ -932,12 +931,6 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target,
MaskVec &LaneMaskVec = RegUnitLaneMasks[i];
assert(LaneMaskVec.empty());
llvm::append_range(LaneMaskVec, RUMasks);
- // Terminator mask should not be used inside of the list.
-#ifndef NDEBUG
- for (LaneBitmask M : LaneMaskVec) {
- assert(!M.all() && "terminator mask should not be part of the list");
- }
-#endif
LaneMaskSeqs.add(LaneMaskVec);
}
@@ -957,6 +950,8 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target,
// Emit the shared table of regunit lane mask sequences.
OS << "extern const LaneBitmask " << TargetName << "LaneMaskLists[] = {\n";
+ // TODO: Omit the terminator since it is never used. The length of this list
+ // is known implicitly from the corresponding reg unit list.
LaneMaskSeqs.emit(OS, printMask, "LaneBitmask::getAll()");
OS << "};\n\n";
@@ -1072,8 +1067,8 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target,
RegSize = RC.RSI.getSimple().RegSize;
OS << " { " << RCName << ", " << RCBitsName << ", "
<< RegClassStrings.get(RC.getName()) << ", " << RC.getOrder().size()
- << ", " << RCBitsSize << ", " << RC.getQualifiedName() + "RegClassID"
- << ", " << RegSize << ", " << RC.CopyCost << ", "
+ << ", " << RCBitsSize << ", " << RC.getQualifiedIdName() << ", "
+ << RegSize << ", " << RC.CopyCost << ", "
<< (RC.Allocatable ? "true" : "false") << " },\n";
}
@@ -1295,7 +1290,7 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target,
for (const ValueTypeByHwMode &VVT : RC.VTs)
if (VVT.hasDefault() || VVT.hasMode(M))
VTs.push_back(VVT.get(M).SimpleTy);
- OS << ", VTLists+" << VTSeqs.get(VTs) << " }, // "
+ OS << ", /*VTLists+*/" << VTSeqs.get(VTs) << " }, // "
<< RC.getName() << '\n';
}
}
@@ -1591,8 +1586,8 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target,
BaseClasses.push_back(&RC);
}
if (!BaseClasses.empty()) {
- // Represent class indexes with uint8_t and allocate one index for nullptr
- assert(BaseClasses.size() <= UINT8_MAX && "Too many base register classes");
+ assert(BaseClasses.size() < UINT16_MAX &&
+ "Too many base register classes");
// Apply order
struct BaseClassOrdering {
@@ -1603,30 +1598,34 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target,
};
llvm::stable_sort(BaseClasses, BaseClassOrdering());
- // Build mapping for Regs (+1 for NoRegister)
- std::vector<uint8_t> Mapping(Regs.size() + 1, 0);
- for (int RCIdx = BaseClasses.size() - 1; RCIdx >= 0; --RCIdx) {
- for (const auto Reg : BaseClasses[RCIdx]->getMembers())
- Mapping[Reg->EnumValue] = RCIdx + 1;
- }
-
OS << "\n// Register to base register class mapping\n\n";
OS << "\n";
OS << "const TargetRegisterClass *" << ClassName
<< "::getPhysRegBaseClass(MCRegister Reg)"
<< " const {\n";
- OS << " static const TargetRegisterClass *BaseClasses[" << (BaseClasses.size() + 1) << "] = {\n";
- OS << " nullptr,\n";
- for (const auto RC : BaseClasses)
- OS << " &" << RC->getQualifiedName() << "RegClass,\n";
- OS << " };\n";
- OS << " static const uint8_t Mapping[" << Mapping.size() << "] = {\n ";
- for (const uint8_t Value : Mapping)
- OS << (unsigned)Value << ",";
- OS << " };\n\n";
- OS << " assert(Reg < sizeof(Mapping));\n";
- OS << " return BaseClasses[Mapping[Reg]];\n";
- OS << "}\n";
+ OS << " static const uint16_t InvalidRegClassID = UINT16_MAX;\n\n";
+ OS << " static const uint16_t Mapping[" << Regs.size() + 1 << "] = {\n";
+ OS << " InvalidRegClassID, // NoRegister\n";
+ for (const CodeGenRegister &Reg : Regs) {
+ const CodeGenRegisterClass *BaseRC = nullptr;
+ for (const CodeGenRegisterClass *RC : BaseClasses) {
+ if (is_contained(RC->getMembers(), &Reg)) {
+ BaseRC = RC;
+ break;
+ }
+ }
+
+ OS << " "
+ << (BaseRC ? BaseRC->getQualifiedIdName() : "InvalidRegClassID")
+ << ", // " << Reg.getName() << "\n";
+ }
+ OS << " };\n\n"
+ " assert(Reg < ArrayRef(Mapping).size());\n"
+ " unsigned RCID = Mapping[Reg];\n"
+ " if (RCID == InvalidRegClassID)\n"
+ " return nullptr;\n"
+ " return RegisterClasses[RCID];\n"
+ "}\n";
}
}
@@ -1653,7 +1652,7 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target,
<< " SubRegIndexNameTable, SubRegIndexLaneMaskTable,\n"
<< " ";
printMask(OS, RegBank.CoveringLanes);
- OS << ", RegClassInfos, HwMode) {\n"
+ OS << ", RegClassInfos, VTLists, HwMode) {\n"
<< " InitMCRegisterInfo(" << TargetName << "RegDesc, " << Regs.size() + 1
<< ", RA, PC,\n " << TargetName
<< "MCRegisterClasses, " << RegisterClasses.size() << ",\n"
diff --git a/llvm/utils/TableGen/SearchableTableEmitter.cpp b/llvm/utils/TableGen/SearchableTableEmitter.cpp
index b6af02c28a80..9987d1ec73d9 100644
--- a/llvm/utils/TableGen/SearchableTableEmitter.cpp
+++ b/llvm/utils/TableGen/SearchableTableEmitter.cpp
@@ -31,12 +31,12 @@ using namespace llvm;
namespace {
-int getAsInt(Init *B) {
+int64_t getAsInt(Init *B) {
return cast<IntInit>(
B->convertInitializerTo(IntRecTy::get(B->getRecordKeeper())))
->getValue();
}
-int getInt(Record *R, StringRef Field) {
+int64_t getInt(Record *R, StringRef Field) {
return getAsInt(R->getValueInit(Field));
}
@@ -720,7 +720,23 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
Twine("Table FilterClass '") +
FilterClass + "' does not exist");
- collectTableEntries(*Table, Records.getAllDerivedDefinitions(FilterClass));
+ RecordVal *FilterClassFieldVal = TableRec->getValue("FilterClassField");
+ std::vector<Record *> Definitions =
+ Records.getAllDerivedDefinitions(FilterClass);
+ if (auto *FilterClassFieldInit =
+ dyn_cast<StringInit>(FilterClassFieldVal->getValue())) {
+ StringRef FilterClassField = FilterClassFieldInit->getValue();
+ llvm::erase_if(Definitions, [&](const Record *R) {
+ const RecordVal *Filter = R->getValue(FilterClassField);
+ if (auto *BitV = dyn_cast<BitInit>(Filter->getValue()))
+ return !BitV->getValue();
+
+ PrintFatalError(Filter, Twine("FilterClassField '") + FilterClass +
+ "' should be a bit value");
+ return true;
+ });
+ }
+ collectTableEntries(*Table, Definitions);
if (!TableRec->isValueUnset("PrimaryKey")) {
Table->PrimaryKey =
diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index e4eb23649e96..f7a7172d61fc 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -119,8 +119,8 @@ class SubtargetEmitter {
const CodeGenProcModel &ProcModel);
Record *FindReadAdvance(const CodeGenSchedRW &SchedRead,
const CodeGenProcModel &ProcModel);
- void ExpandProcResources(RecVec &PRVec, std::vector<int64_t> &Cycles,
- std::vector<int64_t> &StartAtCycles,
+ void ExpandProcResources(RecVec &PRVec, std::vector<int64_t> &ReleaseAtCycles,
+ std::vector<int64_t> &AcquireAtCycles,
const CodeGenProcModel &ProcModel);
void GenSchedClassTables(const CodeGenProcModel &ProcModel,
SchedClassTables &SchedTables);
@@ -976,11 +976,10 @@ Record *SubtargetEmitter::FindReadAdvance(const CodeGenSchedRW &SchedRead,
// Expand an explicit list of processor resources into a full list of implied
// resource groups and super resources that cover them.
-void SubtargetEmitter::ExpandProcResources(RecVec &PRVec,
- std::vector<int64_t> &Cycles,
- std::vector<int64_t> &StartAtCycles,
- const CodeGenProcModel &PM) {
- assert(PRVec.size() == Cycles.size() && "failed precondition");
+void SubtargetEmitter::ExpandProcResources(
+ RecVec &PRVec, std::vector<int64_t> &ReleaseAtCycles,
+ std::vector<int64_t> &AcquireAtCycles, const CodeGenProcModel &PM) {
+ assert(PRVec.size() == ReleaseAtCycles.size() && "failed precondition");
for (unsigned i = 0, e = PRVec.size(); i != e; ++i) {
Record *PRDef = PRVec[i];
RecVec SubResources;
@@ -1000,8 +999,8 @@ void SubtargetEmitter::ExpandProcResources(RecVec &PRVec,
SchedModels.findProcResUnits(SubDef->getValueAsDef("Super"), PM,
SubDef->getLoc());
PRVec.push_back(SuperDef);
- Cycles.push_back(Cycles[i]);
- StartAtCycles.push_back(StartAtCycles[i]);
+ ReleaseAtCycles.push_back(ReleaseAtCycles[i]);
+ AcquireAtCycles.push_back(AcquireAtCycles[i]);
SubDef = SuperDef;
}
}
@@ -1017,8 +1016,8 @@ void SubtargetEmitter::ExpandProcResources(RecVec &PRVec,
}
if (SubI == SubE) {
PRVec.push_back(PR);
- Cycles.push_back(Cycles[i]);
- StartAtCycles.push_back(StartAtCycles[i]);
+ ReleaseAtCycles.push_back(ReleaseAtCycles[i]);
+ AcquireAtCycles.push_back(AcquireAtCycles[i]);
}
}
}
@@ -1150,67 +1149,69 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel,
// Create an entry for each ProcResource listed in WriteRes.
RecVec PRVec = WriteRes->getValueAsListOfDefs("ProcResources");
- std::vector<int64_t> Cycles =
- WriteRes->getValueAsListOfInts("ResourceCycles");
+ std::vector<int64_t> ReleaseAtCycles =
+ WriteRes->getValueAsListOfInts("ReleaseAtCycles");
- std::vector<int64_t> StartAtCycles =
- WriteRes->getValueAsListOfInts("StartAtCycles");
+ std::vector<int64_t> AcquireAtCycles =
+ WriteRes->getValueAsListOfInts("AcquireAtCycles");
// Check consistency of the two vectors carrying the start and
// stop cycles of the resources.
- if (!Cycles.empty() && Cycles.size() != PRVec.size()) {
- // If ResourceCycles is provided, check consistency.
+ if (!ReleaseAtCycles.empty() &&
+ ReleaseAtCycles.size() != PRVec.size()) {
+ // If ReleaseAtCycles is provided, check consistency.
PrintFatalError(
WriteRes->getLoc(),
- Twine("Inconsistent resource cycles: size(ResourceCycles) != "
+ Twine("Inconsistent release at cycles: size(ReleaseAtCycles) != "
"size(ProcResources): ")
.concat(Twine(PRVec.size()))
.concat(" vs ")
- .concat(Twine(Cycles.size())));
+ .concat(Twine(ReleaseAtCycles.size())));
}
- if (!StartAtCycles.empty() && StartAtCycles.size() != PRVec.size()) {
+ if (!AcquireAtCycles.empty() && AcquireAtCycles.size() != PRVec.size()) {
PrintFatalError(
WriteRes->getLoc(),
- Twine("Inconsistent resource cycles: size(StartAtCycles) != "
+ Twine("Inconsistent resource cycles: size(AcquireAtCycles) != "
"size(ProcResources): ")
- .concat(Twine(StartAtCycles.size()))
+ .concat(Twine(AcquireAtCycles.size()))
.concat(" vs ")
.concat(Twine(PRVec.size())));
}
- if (Cycles.empty()) {
- // If ResourceCycles is not provided, default to one cycle
+ if (ReleaseAtCycles.empty()) {
+ // If ReleaseAtCycles is not provided, default to one cycle
// per resource.
- Cycles.resize(PRVec.size(), 1);
+ ReleaseAtCycles.resize(PRVec.size(), 1);
}
- if (StartAtCycles.empty()) {
- // If StartAtCycles is not provided, reserve the resource
+ if (AcquireAtCycles.empty()) {
+ // If AcquireAtCycles is not provided, reserve the resource
// starting from cycle 0.
- StartAtCycles.resize(PRVec.size(), 0);
+ AcquireAtCycles.resize(PRVec.size(), 0);
}
- assert(StartAtCycles.size() == Cycles.size());
+ assert(AcquireAtCycles.size() == ReleaseAtCycles.size());
- ExpandProcResources(PRVec, Cycles, StartAtCycles, ProcModel);
- assert(StartAtCycles.size() == Cycles.size());
+ ExpandProcResources(PRVec, ReleaseAtCycles, AcquireAtCycles, ProcModel);
+ assert(AcquireAtCycles.size() == ReleaseAtCycles.size());
for (unsigned PRIdx = 0, PREnd = PRVec.size();
PRIdx != PREnd; ++PRIdx) {
MCWriteProcResEntry WPREntry;
WPREntry.ProcResourceIdx = ProcModel.getProcResourceIdx(PRVec[PRIdx]);
assert(WPREntry.ProcResourceIdx && "Bad ProcResourceIdx");
- WPREntry.Cycles = Cycles[PRIdx];
- WPREntry.StartAtCycle = StartAtCycles[PRIdx];
- if (StartAtCycles[PRIdx] > Cycles[PRIdx]) {
- PrintFatalError(WriteRes->getLoc(),
- Twine("Inconsistent resource cycles: StartAtCycles "
- "< Cycles must hold."));
+ WPREntry.ReleaseAtCycle = ReleaseAtCycles[PRIdx];
+ WPREntry.AcquireAtCycle = AcquireAtCycles[PRIdx];
+ if (AcquireAtCycles[PRIdx] > ReleaseAtCycles[PRIdx]) {
+ PrintFatalError(
+ WriteRes->getLoc(),
+ Twine("Inconsistent resource cycles: AcquireAtCycles "
+ "< ReleaseAtCycles must hold."));
}
- if (StartAtCycles[PRIdx] < 0) {
+ if (AcquireAtCycles[PRIdx] < 0) {
PrintFatalError(WriteRes->getLoc(),
- Twine("Invalid value: StartAtCycle "
+ Twine("Invalid value: AcquireAtCycle "
"must be a non-negative value."));
}
// If this resource is already used in this sequence, add the current
@@ -1228,9 +1229,10 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel,
// `SubtargetEmitter::EmitSchedClassTables`), or
// 2. thinking how to merge multiple intervals into a
// single interval.
- assert(WPREntry.StartAtCycle == 0 &&
+ assert(WPREntry.AcquireAtCycle == 0 &&
"multiple use ofthe same resource is not yet handled");
- WriteProcResources[WPRIdx].Cycles += WPREntry.Cycles;
+ WriteProcResources[WPRIdx].ReleaseAtCycle +=
+ WPREntry.ReleaseAtCycle;
break;
}
}
@@ -1334,7 +1336,7 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel,
void SubtargetEmitter::EmitSchedClassTables(SchedClassTables &SchedTables,
raw_ostream &OS) {
// Emit global WriteProcResTable.
- OS << "\n// {ProcResourceIdx, Cycles, StartAtCycle}\n"
+ OS << "\n// {ProcResourceIdx, ReleaseAtCycle, AcquireAtCycle}\n"
<< "extern const llvm::MCWriteProcResEntry " << Target
<< "WriteProcResTable[] = {\n"
<< " { 0, 0, 0 }, // Invalid\n";
@@ -1342,8 +1344,8 @@ void SubtargetEmitter::EmitSchedClassTables(SchedClassTables &SchedTables,
WPRIdx != WPREnd; ++WPRIdx) {
MCWriteProcResEntry &WPREntry = SchedTables.WriteProcResources[WPRIdx];
OS << " {" << format("%2d", WPREntry.ProcResourceIdx) << ", "
- << format("%2d", WPREntry.Cycles) << ", "
- << format("%2d", WPREntry.StartAtCycle) << "}";
+ << format("%2d", WPREntry.ReleaseAtCycle) << ", "
+ << format("%2d", WPREntry.AcquireAtCycle) << "}";
if (WPRIdx + 1 < WPREnd)
OS << ',';
OS << " // #" << WPRIdx << '\n';
@@ -1933,7 +1935,7 @@ void SubtargetEmitter::run(raw_ostream &OS) {
if (NumProcs)
OS << Target << "SubTypeKV, ";
else
- OS << "None, ";
+ OS << "std::nullopt, ";
OS << '\n'; OS.indent(22);
OS << Target << "WriteProcResTable, "
<< Target << "WriteLatencyTable, "
@@ -2026,7 +2028,7 @@ void SubtargetEmitter::run(raw_ostream &OS) {
if (NumProcs)
OS << "ArrayRef(" << Target << "SubTypeKV, " << NumProcs << "), ";
else
- OS << "None, ";
+ OS << "std::nullopt, ";
OS << '\n'; OS.indent(24);
OS << Target << "WriteProcResTable, "
<< Target << "WriteLatencyTable, "
diff --git a/llvm/utils/TableGen/SubtargetFeatureInfo.cpp b/llvm/utils/TableGen/SubtargetFeatureInfo.cpp
index 1db8c0bf430a..52afb4d89162 100644
--- a/llvm/utils/TableGen/SubtargetFeatureInfo.cpp
+++ b/llvm/utils/TableGen/SubtargetFeatureInfo.cpp
@@ -47,15 +47,29 @@ SubtargetFeatureInfo::getAll(const RecordKeeper &Records) {
}
void SubtargetFeatureInfo::emitSubtargetFeatureBitEnumeration(
- SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS) {
+ const SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS,
+ const std::map<std::string, unsigned> *HwModes) {
OS << "// Bits for subtarget features that participate in "
<< "instruction matching.\n";
- OS << "enum SubtargetFeatureBits : "
- << getMinimalTypeForRange(SubtargetFeatures.size()) << " {\n";
+ unsigned Size = SubtargetFeatures.size();
+ if (HwModes)
+ Size += HwModes->size();
+
+ OS << "enum SubtargetFeatureBits : " << getMinimalTypeForRange(Size)
+ << " {\n";
for (const auto &SF : SubtargetFeatures) {
const SubtargetFeatureInfo &SFI = SF.second;
OS << " " << SFI.getEnumBitName() << " = " << SFI.Index << ",\n";
}
+
+ if (HwModes) {
+ unsigned Offset = SubtargetFeatures.size();
+ for (const auto &M : *HwModes) {
+ OS << " Feature_HwMode" << M.second << "Bit = " << (M.second + Offset)
+ << ",\n";
+ }
+ }
+
OS << "};\n\n";
}
@@ -87,8 +101,8 @@ void SubtargetFeatureInfo::emitNameTable(
void SubtargetFeatureInfo::emitComputeAvailableFeatures(
StringRef TargetName, StringRef ClassName, StringRef FuncName,
- SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS,
- StringRef ExtraParams) {
+ const SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS,
+ StringRef ExtraParams, const std::map<std::string, unsigned> *HwModes) {
OS << "PredicateBitset " << ClassName << "::\n"
<< FuncName << "(const " << TargetName << "Subtarget *Subtarget";
if (!ExtraParams.empty())
@@ -103,6 +117,14 @@ void SubtargetFeatureInfo::emitComputeAvailableFeatures(
OS << " if (" << CondStr << ")\n";
OS << " Features.set(" << SFI.getEnumBitName() << ");\n";
}
+
+ if (HwModes) {
+ for (const auto &M : *HwModes) {
+ OS << " if (" << M.first << ")\n";
+ OS << " Features.set(Feature_HwMode" << M.second << "Bit);\n";
+ }
+ }
+
OS << " return Features;\n";
OS << "}\n\n";
}
diff --git a/llvm/utils/TableGen/SubtargetFeatureInfo.h b/llvm/utils/TableGen/SubtargetFeatureInfo.h
index 77703e8a87f8..940100448435 100644
--- a/llvm/utils/TableGen/SubtargetFeatureInfo.h
+++ b/llvm/utils/TableGen/SubtargetFeatureInfo.h
@@ -54,9 +54,9 @@ struct SubtargetFeatureInfo {
///
/// This version emits the bit index for the feature and can therefore support
/// more than 64 feature bits.
- static void
- emitSubtargetFeatureBitEnumeration(SubtargetFeatureInfoMap &SubtargetFeatures,
- raw_ostream &OS);
+ static void emitSubtargetFeatureBitEnumeration(
+ const SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS,
+ const std::map<std::string, unsigned> *HwModes = nullptr);
static void emitNameTable(SubtargetFeatureInfoMap &SubtargetFeatures,
raw_ostream &OS);
@@ -75,11 +75,12 @@ struct SubtargetFeatureInfo {
/// \param SubtargetFeatures A map of TableGen records to the
/// SubtargetFeatureInfo equivalent.
/// \param ExtraParams Additional arguments to the generated function.
- static void
- emitComputeAvailableFeatures(StringRef TargetName, StringRef ClassName,
- StringRef FuncName,
- SubtargetFeatureInfoMap &SubtargetFeatures,
- raw_ostream &OS, StringRef ExtraParams = "");
+ /// \param HwModes Map of HwMode conditions to check.
+ static void emitComputeAvailableFeatures(
+ StringRef TargetName, StringRef ClassName, StringRef FuncName,
+ const SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS,
+ StringRef ExtraParams = "",
+ const std::map<std::string, unsigned> *HwModes = nullptr);
/// Emit the function to compute the list of available features given a
/// subtarget.
diff --git a/llvm/utils/TableGen/VTEmitter.cpp b/llvm/utils/TableGen/VTEmitter.cpp
index d398a7e7b58f..5ec1f59318f7 100644
--- a/llvm/utils/TableGen/VTEmitter.cpp
+++ b/llvm/utils/TableGen/VTEmitter.cpp
@@ -30,7 +30,7 @@ public:
} // End anonymous namespace.
void VTEmitter::run(raw_ostream &OS) {
- emitSourceFileHeader("ValueTypes Source Fragment", OS);
+ emitSourceFileHeader("ValueTypes Source Fragment", OS, Records);
std::array<const Record *, 256> VTsByNumber = {};
auto ValueTypes = Records.getAllDerivedDefinitions("ValueType");
diff --git a/llvm/utils/TableGen/VarLenCodeEmitterGen.cpp b/llvm/utils/TableGen/VarLenCodeEmitterGen.cpp
index 85da547d04c1..bfb7e5c33317 100644
--- a/llvm/utils/TableGen/VarLenCodeEmitterGen.cpp
+++ b/llvm/utils/TableGen/VarLenCodeEmitterGen.cpp
@@ -60,6 +60,8 @@
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
+#include <algorithm>
+
using namespace llvm;
namespace {
@@ -67,17 +69,26 @@ namespace {
class VarLenCodeEmitterGen {
RecordKeeper &Records;
- DenseMap<Record *, VarLenInst> VarLenInsts;
+ // Representaton of alternative encodings used for HwModes.
+ using AltEncodingTy = int;
+ // Mode identifier when only one encoding is defined.
+ const AltEncodingTy Universal = -1;
+ // The set of alternative instruction encodings with a descriptive
+ // name suffix to improve readability of the generated code.
+ std::map<AltEncodingTy, std::string> Modes;
+
+ DenseMap<Record *, DenseMap<AltEncodingTy, VarLenInst>> VarLenInsts;
// Emit based values (i.e. fixed bits in the encoded instructions)
void emitInstructionBaseValues(
raw_ostream &OS,
ArrayRef<const CodeGenInstruction *> NumberedInstructions,
- CodeGenTarget &Target, int HwMode = -1);
+ CodeGenTarget &Target, AltEncodingTy Mode);
- std::string getInstructionCase(Record *R, CodeGenTarget &Target);
- std::string getInstructionCaseForEncoding(Record *R, Record *EncodingDef,
- CodeGenTarget &Target);
+ std::string getInstructionCases(Record *R, CodeGenTarget &Target);
+ std::string getInstructionCaseForEncoding(Record *R, AltEncodingTy Mode,
+ const VarLenInst &VLI,
+ CodeGenTarget &Target, int I);
public:
explicit VarLenCodeEmitterGen(RecordKeeper &R) : Records(R) {}
@@ -114,7 +125,7 @@ static std::pair<StringRef, StringRef> getCustomCoders(ArrayRef<Init *> Args) {
}
VarLenInst::VarLenInst(const DagInit *DI, const RecordVal *TheDef)
- : TheDef(TheDef), NumBits(0U) {
+ : TheDef(TheDef), NumBits(0U), HasDynamicSegment(false) {
buildRec(DI);
for (const auto &S : Segments)
NumBits += S.BitWidth;
@@ -214,36 +225,38 @@ void VarLenCodeEmitterGen::run(raw_ostream &OS) {
auto Insts = Records.getAllDerivedDefinitions("Instruction");
auto NumberedInstructions = Target.getInstructionsByEnumValue();
- const CodeGenHwModes &HWM = Target.getHwModes();
- // The set of HwModes used by instruction encodings.
- std::set<unsigned> HwModes;
for (const CodeGenInstruction *CGI : NumberedInstructions) {
Record *R = CGI->TheDef;
-
// Create the corresponding VarLenInst instance.
if (R->getValueAsString("Namespace") == "TargetOpcode" ||
R->getValueAsBit("isPseudo"))
continue;
+ // Setup alternative encodings according to HwModes
if (const RecordVal *RV = R->getValue("EncodingInfos")) {
if (auto *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
+ const CodeGenHwModes &HWM = Target.getHwModes();
EncodingInfoByHwMode EBM(DI->getDef(), HWM);
for (auto &KV : EBM) {
- HwModes.insert(KV.first);
+ AltEncodingTy Mode = KV.first;
+ Modes.insert({Mode, "_" + HWM.getMode(Mode).Name.str()});
Record *EncodingDef = KV.second;
RecordVal *RV = EncodingDef->getValue("Inst");
DagInit *DI = cast<DagInit>(RV->getValue());
- VarLenInsts.insert({EncodingDef, VarLenInst(DI, RV)});
+ VarLenInsts[R].insert({Mode, VarLenInst(DI, RV)});
}
continue;
}
}
RecordVal *RV = R->getValue("Inst");
DagInit *DI = cast<DagInit>(RV->getValue());
- VarLenInsts.insert({R, VarLenInst(DI, RV)});
+ VarLenInsts[R].insert({Universal, VarLenInst(DI, RV)});
}
+ if (Modes.empty())
+ Modes.insert({Universal, ""}); // Base case, skip suffix.
+
// Emit function declaration
OS << "void " << Target.getName()
<< "MCCodeEmitter::getBinaryCodeForInstr(const MCInst &MI,\n"
@@ -253,36 +266,26 @@ void VarLenCodeEmitterGen::run(raw_ostream &OS) {
<< " const MCSubtargetInfo &STI) const {\n";
// Emit instruction base values
- if (HwModes.empty()) {
- emitInstructionBaseValues(OS, NumberedInstructions, Target);
- } else {
- for (unsigned HwMode : HwModes)
- emitInstructionBaseValues(OS, NumberedInstructions, Target, (int)HwMode);
- }
+ for (const auto &Mode : Modes)
+ emitInstructionBaseValues(OS, NumberedInstructions, Target, Mode.first);
- if (!HwModes.empty()) {
- OS << " const unsigned **Index;\n";
- OS << " const uint64_t *InstBits;\n";
- OS << " unsigned HwMode = STI.getHwMode();\n";
- OS << " switch (HwMode) {\n";
- OS << " default: llvm_unreachable(\"Unknown hardware mode!\"); break;\n";
- for (unsigned I : HwModes) {
- OS << " case " << I << ": InstBits = InstBits_" << HWM.getMode(I).Name
- << "; Index = Index_" << HWM.getMode(I).Name << "; break;\n";
- }
- OS << " };\n";
+ if (Modes.size() > 1) {
+ OS << " unsigned Mode = STI.getHwMode();\n";
}
- // Emit helper function to retrieve base values.
- OS << " auto getInstBits = [&](unsigned Opcode) -> APInt {\n"
- << " unsigned NumBits = Index[Opcode][0];\n"
- << " if (!NumBits)\n"
- << " return APInt::getZeroWidth();\n"
- << " unsigned Idx = Index[Opcode][1];\n"
- << " ArrayRef<uint64_t> Data(&InstBits[Idx], "
- << "APInt::getNumWords(NumBits));\n"
- << " return APInt(NumBits, Data);\n"
- << " };\n";
+ for (const auto &Mode : Modes) {
+ // Emit helper function to retrieve base values.
+ OS << " auto getInstBits" << Mode.second
+ << " = [&](unsigned Opcode) -> APInt {\n"
+ << " unsigned NumBits = Index" << Mode.second << "[Opcode][0];\n"
+ << " if (!NumBits)\n"
+ << " return APInt::getZeroWidth();\n"
+ << " unsigned Idx = Index" << Mode.second << "[Opcode][1];\n"
+ << " ArrayRef<uint64_t> Data(&InstBits" << Mode.second << "[Idx], "
+ << "APInt::getNumWords(NumBits));\n"
+ << " return APInt(NumBits, Data);\n"
+ << " };\n";
+ }
// Map to accumulate all the cases.
std::map<std::string, std::vector<std::string>> CaseMap;
@@ -294,7 +297,7 @@ void VarLenCodeEmitterGen::run(raw_ostream &OS) {
continue;
std::string InstName =
(R->getValueAsString("Namespace") + "::" + R->getName()).str();
- std::string Case = getInstructionCase(R, Target);
+ std::string Case = getInstructionCases(R, Target);
CaseMap[Case].push_back(std::move(InstName));
}
@@ -344,19 +347,12 @@ static void emitInstBits(raw_ostream &IS, raw_ostream &SS, const APInt &Bits,
void VarLenCodeEmitterGen::emitInstructionBaseValues(
raw_ostream &OS, ArrayRef<const CodeGenInstruction *> NumberedInstructions,
- CodeGenTarget &Target, int HwMode) {
+ CodeGenTarget &Target, AltEncodingTy Mode) {
std::string IndexArray, StorageArray;
raw_string_ostream IS(IndexArray), SS(StorageArray);
- const CodeGenHwModes &HWM = Target.getHwModes();
- if (HwMode == -1) {
- IS << " static const unsigned Index[][2] = {\n";
- SS << " static const uint64_t InstBits[] = {\n";
- } else {
- StringRef Name = HWM.getMode(HwMode).Name;
- IS << " static const unsigned Index_" << Name << "[][2] = {\n";
- SS << " static const uint64_t InstBits_" << Name << "[] = {\n";
- }
+ IS << " static const unsigned Index" << Modes[Mode] << "[][2] = {\n";
+ SS << " static const uint64_t InstBits" << Modes[Mode] << "[] = {\n";
unsigned NumFixedValueWords = 0U;
for (const CodeGenInstruction *CGI : NumberedInstructions) {
@@ -368,20 +364,18 @@ void VarLenCodeEmitterGen::emitInstructionBaseValues(
continue;
}
- Record *EncodingDef = R;
- if (const RecordVal *RV = R->getValue("EncodingInfos")) {
- if (auto *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
- EncodingInfoByHwMode EBM(DI->getDef(), HWM);
- if (EBM.hasMode(HwMode))
- EncodingDef = EBM.get(HwMode);
- }
+ const auto InstIt = VarLenInsts.find(R);
+ if (InstIt == VarLenInsts.end())
+ PrintFatalError(R, "VarLenInst not found for this record");
+ auto ModeIt = InstIt->second.find(Mode);
+ if (ModeIt == InstIt->second.end())
+ ModeIt = InstIt->second.find(Universal);
+ if (ModeIt == InstIt->second.end()) {
+ IS.indent(4) << "{/*NumBits*/0, /*Index*/0},\t"
+ << "// " << R->getName() << " no encoding\n";
+ continue;
}
-
- auto It = VarLenInsts.find(EncodingDef);
- if (It == VarLenInsts.end())
- PrintFatalError(EncodingDef, "VarLenInst not found for this record");
- const VarLenInst &VLI = It->second;
-
+ const VarLenInst &VLI = ModeIt->second;
unsigned i = 0U, BitWidth = VLI.size();
// Start by filling in fixed values.
@@ -414,48 +408,56 @@ void VarLenCodeEmitterGen::emitInstructionBaseValues(
OS << IS.str() << SS.str();
}
-std::string VarLenCodeEmitterGen::getInstructionCase(Record *R,
- CodeGenTarget &Target) {
+std::string VarLenCodeEmitterGen::getInstructionCases(Record *R,
+ CodeGenTarget &Target) {
+ auto It = VarLenInsts.find(R);
+ if (It == VarLenInsts.end())
+ PrintFatalError(R, "Parsed encoding record not found");
+ const auto &Map = It->second;
+
+ // Is this instructions encoding universal (same for all modes)?
+ // Allways true if there is only one mode.
+ if (Map.size() == 1 && Map.begin()->first == Universal) {
+ // Universal, just pick the first mode.
+ AltEncodingTy Mode = Modes.begin()->first;
+ const auto &Encoding = Map.begin()->second;
+ return getInstructionCaseForEncoding(R, Mode, Encoding, Target, 6);
+ }
+
std::string Case;
- if (const RecordVal *RV = R->getValue("EncodingInfos")) {
- if (auto *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
- const CodeGenHwModes &HWM = Target.getHwModes();
- EncodingInfoByHwMode EBM(DI->getDef(), HWM);
- Case += " switch (HwMode) {\n";
- Case += " default: llvm_unreachable(\"Unhandled HwMode\");\n";
- for (auto &KV : EBM) {
- Case += " case " + itostr(KV.first) + ": {\n";
- Case += getInstructionCaseForEncoding(R, KV.second, Target);
- Case += " break;\n";
- Case += " }\n";
- }
- Case += " }\n";
- return Case;
+ Case += " switch (Mode) {\n";
+ Case += " default: llvm_unreachable(\"Unhandled Mode\");\n";
+ for (const auto &Mode : Modes) {
+ Case += " case " + itostr(Mode.first) + ": {\n";
+ const auto &It = Map.find(Mode.first);
+ if (It == Map.end()) {
+ Case +=
+ " llvm_unreachable(\"Undefined encoding in this mode\");\n";
+ } else {
+ Case +=
+ getInstructionCaseForEncoding(R, It->first, It->second, Target, 8);
}
+ Case += " break;\n";
+ Case += " }\n";
}
- return getInstructionCaseForEncoding(R, R, Target);
+ Case += " }\n";
+ return Case;
}
std::string VarLenCodeEmitterGen::getInstructionCaseForEncoding(
- Record *R, Record *EncodingDef, CodeGenTarget &Target) {
- auto It = VarLenInsts.find(EncodingDef);
- if (It == VarLenInsts.end())
- PrintFatalError(EncodingDef, "Parsed encoding record not found");
- const VarLenInst &VLI = It->second;
- size_t BitWidth = VLI.size();
+ Record *R, AltEncodingTy Mode, const VarLenInst &VLI, CodeGenTarget &Target,
+ int I) {
CodeGenInstruction &CGI = Target.getInstruction(R);
std::string Case;
raw_string_ostream SS(Case);
- // Resize the scratch buffer.
- if (BitWidth && !VLI.isFixedValueOnly())
- SS.indent(6) << "Scratch = Scratch.zext(" << BitWidth << ");\n";
// Populate based value.
- SS.indent(6) << "Inst = getInstBits(opcode);\n";
+ SS.indent(I) << "Inst = getInstBits" << Modes[Mode] << "(opcode);\n";
// Process each segment in VLI.
size_t Offset = 0U;
+ unsigned HighScratchAccess = 0U;
for (const auto &ES : VLI) {
unsigned NumBits = ES.BitWidth;
const Init *Val = ES.Value;
@@ -480,29 +482,40 @@ std::string VarLenCodeEmitterGen::getInstructionCaseForEncoding(
if (ES.CustomEncoder.size())
CustomEncoder = ES.CustomEncoder;
- SS.indent(6) << "Scratch.clearAllBits();\n";
- SS.indent(6) << "// op: " << OperandName.drop_front(1) << "\n";
+ SS.indent(I) << "Scratch.clearAllBits();\n";
+ SS.indent(I) << "// op: " << OperandName.drop_front(1) << "\n";
if (CustomEncoder.empty())
- SS.indent(6) << "getMachineOpValue(MI, MI.getOperand("
+ SS.indent(I) << "getMachineOpValue(MI, MI.getOperand("
<< utostr(FlatOpIdx) << ")";
else
- SS.indent(6) << CustomEncoder << "(MI, /*OpIdx=*/" << utostr(FlatOpIdx);
+ SS.indent(I) << CustomEncoder << "(MI, /*OpIdx=*/" << utostr(FlatOpIdx);
SS << ", /*Pos=*/" << utostr(Offset) << ", Scratch, Fixups, STI);\n";
- SS.indent(6) << "Inst.insertBits("
+ SS.indent(I) << "Inst.insertBits("
<< "Scratch.extractBits(" << utostr(NumBits) << ", "
<< utostr(LoBit) << ")"
<< ", " << Offset << ");\n";
+
+ HighScratchAccess = std::max(HighScratchAccess, NumBits + LoBit);
}
Offset += NumBits;
}
StringRef PostEmitter = R->getValueAsString("PostEncoderMethod");
if (!PostEmitter.empty())
- SS.indent(6) << "Inst = " << PostEmitter << "(MI, Inst, STI);\n";
+ SS.indent(I) << "Inst = " << PostEmitter << "(MI, Inst, STI);\n";
- return Case;
+ // Resize the scratch buffer if it's to small.
+ std::string ScratchResizeStr;
+ if (VLI.size() && !VLI.isFixedValueOnly()) {
+ raw_string_ostream RS(ScratchResizeStr);
+ RS.indent(I) << "if (Scratch.getBitWidth() < " << HighScratchAccess
+ << ") { Scratch = Scratch.zext(" << HighScratchAccess
+ << "); }\n";
+ }
+
+ return ScratchResizeStr + Case;
}
namespace llvm {
diff --git a/llvm/utils/TableGen/X86DisassemblerTables.cpp b/llvm/utils/TableGen/X86DisassemblerTables.cpp
index 708c92aecfc8..959e0fda50b8 100644
--- a/llvm/utils/TableGen/X86DisassemblerTables.cpp
+++ b/llvm/utils/TableGen/X86DisassemblerTables.cpp
@@ -31,39 +31,47 @@ using namespace X86Disassembler;
/// @param insnContext - The instruction class to transform to a string.
/// @return - A statically-allocated string constant that contains the
/// name of the instruction class.
-static inline const char* stringForContext(InstructionContext insnContext) {
+static inline const char *stringForContext(InstructionContext insnContext) {
switch (insnContext) {
default:
llvm_unreachable("Unhandled instruction class");
-#define ENUM_ENTRY(n, r, d) case n: return #n; break;
-#define ENUM_ENTRY_K_B(n, r, d) ENUM_ENTRY(n, r, d) ENUM_ENTRY(n##_K_B, r, d)\
- ENUM_ENTRY(n##_KZ, r, d) ENUM_ENTRY(n##_K, r, d) ENUM_ENTRY(n##_B, r, d)\
- ENUM_ENTRY(n##_KZ_B, r, d)
- INSTRUCTION_CONTEXTS
+#define ENUM_ENTRY(n, r, d) \
+ case n: \
+ return #n; \
+ break;
+#define ENUM_ENTRY_K_B(n, r, d) \
+ ENUM_ENTRY(n, r, d) \
+ ENUM_ENTRY(n##_K_B, r, d) ENUM_ENTRY(n##_KZ, r, d) ENUM_ENTRY(n##_K, r, d) \
+ ENUM_ENTRY(n##_B, r, d) ENUM_ENTRY(n##_KZ_B, r, d)
+ INSTRUCTION_CONTEXTS
#undef ENUM_ENTRY
#undef ENUM_ENTRY_K_B
}
}
/// stringForOperandType - Like stringForContext, but for OperandTypes.
-static inline const char* stringForOperandType(OperandType type) {
+static inline const char *stringForOperandType(OperandType type) {
switch (type) {
default:
llvm_unreachable("Unhandled type");
-#define ENUM_ENTRY(i, d) case i: return #i;
- TYPES
+#define ENUM_ENTRY(i, d) \
+ case i: \
+ return #i;
+ TYPES
#undef ENUM_ENTRY
}
}
/// stringForOperandEncoding - like stringForContext, but for
/// OperandEncodings.
-static inline const char* stringForOperandEncoding(OperandEncoding encoding) {
+static inline const char *stringForOperandEncoding(OperandEncoding encoding) {
switch (encoding) {
default:
llvm_unreachable("Unhandled encoding");
-#define ENUM_ENTRY(i, d) case i: return #i;
- ENCODINGS
+#define ENUM_ENTRY(i, d) \
+ case i: \
+ return #i;
+ ENCODINGS
#undef ENUM_ENTRY
}
}
@@ -83,17 +91,17 @@ static inline bool inheritsFrom(InstructionContext child,
switch (parent) {
case IC:
- return(inheritsFrom(child, IC_64BIT, AdSize64) ||
- (noPrefix && inheritsFrom(child, IC_OPSIZE, noPrefix)) ||
- inheritsFrom(child, IC_ADSIZE) ||
- (noPrefix && inheritsFrom(child, IC_XD, noPrefix)) ||
- (noPrefix && inheritsFrom(child, IC_XS, noPrefix)));
+ return (inheritsFrom(child, IC_64BIT, AdSize64) ||
+ (noPrefix && inheritsFrom(child, IC_OPSIZE, noPrefix)) ||
+ inheritsFrom(child, IC_ADSIZE) ||
+ (noPrefix && inheritsFrom(child, IC_XD, noPrefix)) ||
+ (noPrefix && inheritsFrom(child, IC_XS, noPrefix)));
case IC_64BIT:
- return(inheritsFrom(child, IC_64BIT_REXW) ||
- (noPrefix && inheritsFrom(child, IC_64BIT_OPSIZE, noPrefix)) ||
- (!AdSize64 && inheritsFrom(child, IC_64BIT_ADSIZE)) ||
- (noPrefix && inheritsFrom(child, IC_64BIT_XD, noPrefix)) ||
- (noPrefix && inheritsFrom(child, IC_64BIT_XS, noPrefix)));
+ return (inheritsFrom(child, IC_64BIT_REXW) ||
+ (noPrefix && inheritsFrom(child, IC_64BIT_OPSIZE, noPrefix)) ||
+ (!AdSize64 && inheritsFrom(child, IC_64BIT_ADSIZE)) ||
+ (noPrefix && inheritsFrom(child, IC_64BIT_XD, noPrefix)) ||
+ (noPrefix && inheritsFrom(child, IC_64BIT_XS, noPrefix)));
case IC_OPSIZE:
return inheritsFrom(child, IC_64BIT_OPSIZE) ||
inheritsFrom(child, IC_OPSIZE_ADSIZE);
@@ -118,20 +126,20 @@ static inline bool inheritsFrom(InstructionContext child,
case IC_XS_ADSIZE:
return inheritsFrom(child, IC_64BIT_XS_ADSIZE);
case IC_64BIT_REXW:
- return((noPrefix && inheritsFrom(child, IC_64BIT_REXW_XS, noPrefix)) ||
- (noPrefix && inheritsFrom(child, IC_64BIT_REXW_XD, noPrefix)) ||
- (noPrefix && inheritsFrom(child, IC_64BIT_REXW_OPSIZE, noPrefix)) ||
- (!AdSize64 && inheritsFrom(child, IC_64BIT_REXW_ADSIZE)));
+ return ((noPrefix && inheritsFrom(child, IC_64BIT_REXW_XS, noPrefix)) ||
+ (noPrefix && inheritsFrom(child, IC_64BIT_REXW_XD, noPrefix)) ||
+ (noPrefix && inheritsFrom(child, IC_64BIT_REXW_OPSIZE, noPrefix)) ||
+ (!AdSize64 && inheritsFrom(child, IC_64BIT_REXW_ADSIZE)));
case IC_64BIT_OPSIZE:
return inheritsFrom(child, IC_64BIT_REXW_OPSIZE) ||
(!AdSize64 && inheritsFrom(child, IC_64BIT_OPSIZE_ADSIZE)) ||
(!AdSize64 && inheritsFrom(child, IC_64BIT_REXW_ADSIZE));
case IC_64BIT_XD:
- return(inheritsFrom(child, IC_64BIT_REXW_XD) ||
- (!AdSize64 && inheritsFrom(child, IC_64BIT_XD_ADSIZE)));
+ return (inheritsFrom(child, IC_64BIT_REXW_XD) ||
+ (!AdSize64 && inheritsFrom(child, IC_64BIT_XD_ADSIZE)));
case IC_64BIT_XS:
- return(inheritsFrom(child, IC_64BIT_REXW_XS) ||
- (!AdSize64 && inheritsFrom(child, IC_64BIT_XS_ADSIZE)));
+ return (inheritsFrom(child, IC_64BIT_REXW_XS) ||
+ (!AdSize64 && inheritsFrom(child, IC_64BIT_XS_ADSIZE)));
case IC_64BIT_XD_OPSIZE:
case IC_64BIT_XS_OPSIZE:
return false;
@@ -142,6 +150,7 @@ static inline bool inheritsFrom(InstructionContext child,
case IC_64BIT_REXW_XS:
case IC_64BIT_REXW_OPSIZE:
case IC_64BIT_REXW_ADSIZE:
+ case IC_64BIT_REX2:
return false;
case IC_VEX:
return (VEX_LIG && WIG && inheritsFrom(child, IC_VEX_L_W)) ||
@@ -409,10 +418,8 @@ static inline bool inheritsFrom(InstructionContext child,
(VEX_LIG && inheritsFrom(child, IC_EVEX_L_XD_K_B)) ||
(VEX_LIG && inheritsFrom(child, IC_EVEX_L2_XD_K_B));
case IC_EVEX_OPSIZE_K_B:
- return (VEX_LIG && WIG &&
- inheritsFrom(child, IC_EVEX_L_W_OPSIZE_K_B)) ||
- (VEX_LIG && WIG &&
- inheritsFrom(child, IC_EVEX_L2_W_OPSIZE_K_B)) ||
+ return (VEX_LIG && WIG && inheritsFrom(child, IC_EVEX_L_W_OPSIZE_K_B)) ||
+ (VEX_LIG && WIG && inheritsFrom(child, IC_EVEX_L2_W_OPSIZE_K_B)) ||
(WIG && inheritsFrom(child, IC_EVEX_W_OPSIZE_K_B)) ||
(VEX_LIG && inheritsFrom(child, IC_EVEX_L_OPSIZE_K_B)) ||
(VEX_LIG && inheritsFrom(child, IC_EVEX_L2_OPSIZE_K_B));
@@ -435,10 +442,8 @@ static inline bool inheritsFrom(InstructionContext child,
(VEX_LIG && inheritsFrom(child, IC_EVEX_L_XD_KZ_B)) ||
(VEX_LIG && inheritsFrom(child, IC_EVEX_L2_XD_KZ_B));
case IC_EVEX_OPSIZE_KZ_B:
- return (VEX_LIG && WIG &&
- inheritsFrom(child, IC_EVEX_L_W_OPSIZE_KZ_B)) ||
- (VEX_LIG && WIG &&
- inheritsFrom(child, IC_EVEX_L2_W_OPSIZE_KZ_B)) ||
+ return (VEX_LIG && WIG && inheritsFrom(child, IC_EVEX_L_W_OPSIZE_KZ_B)) ||
+ (VEX_LIG && WIG && inheritsFrom(child, IC_EVEX_L2_W_OPSIZE_KZ_B)) ||
(WIG && inheritsFrom(child, IC_EVEX_W_OPSIZE_KZ_B)) ||
(VEX_LIG && inheritsFrom(child, IC_EVEX_L_OPSIZE_KZ_B)) ||
(VEX_LIG && inheritsFrom(child, IC_EVEX_L2_OPSIZE_KZ_B));
@@ -557,8 +562,8 @@ static inline bool inheritsFrom(InstructionContext child,
case IC_EVEX_L2_W_OPSIZE_KZ_B:
return false;
default:
- errs() << "Unknown instruction class: " <<
- stringForContext((InstructionContext)parent) << "\n";
+ errs() << "Unknown instruction class: "
+ << stringForContext((InstructionContext)parent) << "\n";
llvm_unreachable("Unknown instruction class");
}
}
@@ -576,12 +581,12 @@ static inline bool outranks(InstructionContext upper,
assert(lower < IC_max);
#define ENUM_ENTRY(n, r, d) r,
-#define ENUM_ENTRY_K_B(n, r, d) ENUM_ENTRY(n, r, d) \
- ENUM_ENTRY(n##_K_B, r, d) ENUM_ENTRY(n##_KZ_B, r, d) \
- ENUM_ENTRY(n##_KZ, r, d) ENUM_ENTRY(n##_K, r, d) ENUM_ENTRY(n##_B, r, d)
- static int ranks[IC_max] = {
- INSTRUCTION_CONTEXTS
- };
+#define ENUM_ENTRY_K_B(n, r, d) \
+ ENUM_ENTRY(n, r, d) \
+ ENUM_ENTRY(n##_K_B, r, d) \
+ ENUM_ENTRY(n##_KZ_B, r, d) ENUM_ENTRY(n##_KZ, r, d) ENUM_ENTRY(n##_K, r, d) \
+ ENUM_ENTRY(n##_B, r, d)
+ static int ranks[IC_max] = {INSTRUCTION_CONTEXTS};
#undef ENUM_ENTRY
#undef ENUM_ENTRY_K_B
@@ -604,19 +609,19 @@ static ModRMDecisionType getDecisionType(ModRMDecision &decision) {
satisfiesOneEntry = false;
if (((index & 0xc0) == 0xc0) &&
- (decision.instructionIDs[index] != decision.instructionIDs[0xc0]))
+ (decision.instructionIDs[index] != decision.instructionIDs[0xc0]))
satisfiesSplitRM = false;
if (((index & 0xc0) != 0xc0) &&
- (decision.instructionIDs[index] != decision.instructionIDs[0x00]))
+ (decision.instructionIDs[index] != decision.instructionIDs[0x00]))
satisfiesSplitRM = false;
- if (((index & 0xc0) == 0xc0) &&
- (decision.instructionIDs[index] != decision.instructionIDs[index&0xf8]))
+ if (((index & 0xc0) == 0xc0) && (decision.instructionIDs[index] !=
+ decision.instructionIDs[index & 0xf8]))
satisfiesSplitReg = false;
- if (((index & 0xc0) != 0xc0) &&
- (decision.instructionIDs[index] != decision.instructionIDs[index&0x38]))
+ if (((index & 0xc0) != 0xc0) && (decision.instructionIDs[index] !=
+ decision.instructionIDs[index & 0x38]))
satisfiesSplitMisc = false;
}
@@ -641,11 +646,13 @@ static ModRMDecisionType getDecisionType(ModRMDecision &decision) {
/// @param dt - The decision type.
/// @return - A pointer to the statically-allocated string (e.g.,
/// "MODRM_ONEENTRY" for MODRM_ONEENTRY).
-static const char* stringForDecisionType(ModRMDecisionType dt) {
-#define ENUM_ENTRY(n) case n: return #n;
+static const char *stringForDecisionType(ModRMDecisionType dt) {
+#define ENUM_ENTRY(n) \
+ case n: \
+ return #n;
switch (dt) {
- default:
- llvm_unreachable("Unknown decision type");
+ default:
+ llvm_unreachable("Unknown decision type");
MODRMTYPES
};
#undef ENUM_ENTRY
@@ -658,8 +665,7 @@ DisassemblerTables::DisassemblerTables() {
HasConflicts = false;
}
-DisassemblerTables::~DisassemblerTables() {
-}
+DisassemblerTables::~DisassemblerTables() {}
void DisassemblerTables::emitModRMDecision(raw_ostream &o1, raw_ostream &o2,
unsigned &i1, unsigned &i2,
@@ -677,31 +683,31 @@ void DisassemblerTables::emitModRMDecision(raw_ostream &o1, raw_ostream &o2,
std::vector<unsigned> ModRMDecision;
switch (dt) {
- default:
- llvm_unreachable("Unknown decision type");
- case MODRM_ONEENTRY:
- ModRMDecision.push_back(decision.instructionIDs[0]);
- break;
- case MODRM_SPLITRM:
- ModRMDecision.push_back(decision.instructionIDs[0x00]);
- ModRMDecision.push_back(decision.instructionIDs[0xc0]);
- break;
- case MODRM_SPLITREG:
- for (unsigned index = 0; index < 64; index += 8)
- ModRMDecision.push_back(decision.instructionIDs[index]);
- for (unsigned index = 0xc0; index < 256; index += 8)
- ModRMDecision.push_back(decision.instructionIDs[index]);
- break;
- case MODRM_SPLITMISC:
- for (unsigned index = 0; index < 64; index += 8)
- ModRMDecision.push_back(decision.instructionIDs[index]);
- for (unsigned index = 0xc0; index < 256; ++index)
- ModRMDecision.push_back(decision.instructionIDs[index]);
- break;
- case MODRM_FULL:
- for (unsigned short InstructionID : decision.instructionIDs)
- ModRMDecision.push_back(InstructionID);
- break;
+ default:
+ llvm_unreachable("Unknown decision type");
+ case MODRM_ONEENTRY:
+ ModRMDecision.push_back(decision.instructionIDs[0]);
+ break;
+ case MODRM_SPLITRM:
+ ModRMDecision.push_back(decision.instructionIDs[0x00]);
+ ModRMDecision.push_back(decision.instructionIDs[0xc0]);
+ break;
+ case MODRM_SPLITREG:
+ for (unsigned index = 0; index < 64; index += 8)
+ ModRMDecision.push_back(decision.instructionIDs[index]);
+ for (unsigned index = 0xc0; index < 256; index += 8)
+ ModRMDecision.push_back(decision.instructionIDs[index]);
+ break;
+ case MODRM_SPLITMISC:
+ for (unsigned index = 0; index < 64; index += 8)
+ ModRMDecision.push_back(decision.instructionIDs[index]);
+ for (unsigned index = 0xc0; index < 256; ++index)
+ ModRMDecision.push_back(decision.instructionIDs[index]);
+ break;
+ case MODRM_FULL:
+ for (unsigned short InstructionID : decision.instructionIDs)
+ ModRMDecision.push_back(InstructionID);
+ break;
}
unsigned &EntryNumber = ModRMTable[ModRMDecision];
@@ -721,23 +727,23 @@ void DisassemblerTables::emitModRMDecision(raw_ostream &o1, raw_ostream &o2,
o2 << "{" << stringForDecisionType(dt) << ", " << EntryNumber << "}";
switch (dt) {
- default:
- llvm_unreachable("Unknown decision type");
- case MODRM_ONEENTRY:
- sEntryNumber += 1;
- break;
- case MODRM_SPLITRM:
- sEntryNumber += 2;
- break;
- case MODRM_SPLITREG:
- sEntryNumber += 16;
- break;
- case MODRM_SPLITMISC:
- sEntryNumber += 8 + 64;
- break;
- case MODRM_FULL:
- sEntryNumber += 256;
- break;
+ default:
+ llvm_unreachable("Unknown decision type");
+ case MODRM_ONEENTRY:
+ sEntryNumber += 1;
+ break;
+ case MODRM_SPLITRM:
+ sEntryNumber += 2;
+ break;
+ case MODRM_SPLITREG:
+ sEntryNumber += 16;
+ break;
+ case MODRM_SPLITMISC:
+ sEntryNumber += 8 + 64;
+ break;
+ case MODRM_FULL:
+ sEntryNumber += 256;
+ break;
}
// We assume that the index can fit into uint16_t.
@@ -790,8 +796,9 @@ void DisassemblerTables::emitContextDecision(raw_ostream &o1, raw_ostream &o2,
unsigned &i1, unsigned &i2,
unsigned &ModRMTableNum,
ContextDecision &decision,
- const char* name) const {
- o2.indent(i2) << "static const struct ContextDecision " << name << " = {{/* opcodeDecisions */\n";
+ const char *name) const {
+ o2.indent(i2) << "static const struct ContextDecision " << name
+ << " = {{/* opcodeDecisions */\n";
i2++;
for (unsigned index = 0; index < IC_max; ++index) {
@@ -814,8 +821,8 @@ void DisassemblerTables::emitInstructionInfo(raw_ostream &o,
o << "static const struct OperandSpecifier x86OperandSets[]["
<< X86_MAX_OPERANDS << "] = {\n";
- typedef SmallVector<std::pair<OperandEncoding, OperandType>,
- X86_MAX_OPERANDS> OperandListTy;
+ typedef SmallVector<std::pair<OperandEncoding, OperandType>, X86_MAX_OPERANDS>
+ OperandListTy;
std::map<OperandListTy, unsigned> OperandSets;
unsigned OperandSetNum = 0;
@@ -828,14 +835,15 @@ void DisassemblerTables::emitInstructionInfo(raw_ostream &o,
OperandList.push_back(std::make_pair(Encoding, Type));
}
unsigned &N = OperandSets[OperandList];
- if (N != 0) continue;
+ if (N != 0)
+ continue;
N = ++OperandSetNum;
o << " { /* " << (OperandSetNum - 1) << " */\n";
for (unsigned i = 0, e = OperandList.size(); i != e; ++i) {
const char *Encoding = stringForOperandEncoding(OperandList[i].first);
- const char *Type = stringForOperandType(OperandList[i].second);
+ const char *Type = stringForOperandType(OperandList[i].second);
o << " { " << Encoding << ", " << Type << " },\n";
}
o << " },\n";
@@ -870,8 +878,8 @@ void DisassemblerTables::emitInstructionInfo(raw_ostream &o,
}
void DisassemblerTables::emitContextTable(raw_ostream &o, unsigned &i) const {
- o.indent(i * 2) << "static const uint8_t " CONTEXTS_STR
- "[" << ATTR_max << "] = {\n";
+ o.indent(i * 2) << "static const uint8_t " CONTEXTS_STR "[" << ATTR_max
+ << "] = {\n";
i++;
for (unsigned index = 0; index < ATTR_max; ++index) {
@@ -907,7 +915,8 @@ void DisassemblerTables::emitContextTable(raw_ostream &o, unsigned &i) const {
if (index & ATTR_EVEXB)
o << "_B";
}
- }
+ } else if ((index & ATTR_64BIT) && (index & ATTR_REX2))
+ o << "IC_64BIT_REX2";
else if ((index & ATTR_64BIT) && (index & ATTR_REXW) && (index & ATTR_XS))
o << "IC_64BIT_REXW_XS";
else if ((index & ATTR_64BIT) && (index & ATTR_REXW) && (index & ATTR_XD))
@@ -974,14 +983,19 @@ void DisassemblerTables::emitContextDecisions(raw_ostream &o1, raw_ostream &o2,
unsigned &ModRMTableNum) const {
emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[0], ONEBYTE_STR);
emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[1], TWOBYTE_STR);
- emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[2], THREEBYTE38_STR);
- emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[3], THREEBYTE3A_STR);
+ emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[2],
+ THREEBYTE38_STR);
+ emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[3],
+ THREEBYTE3A_STR);
emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[4], XOP8_MAP_STR);
emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[5], XOP9_MAP_STR);
emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[6], XOPA_MAP_STR);
- emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[7], THREEDNOW_MAP_STR);
- emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[8], MAP5_STR);
- emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[9], MAP6_STR);
+ emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[7],
+ THREEDNOW_MAP_STR);
+ emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[8], MAP4_STR);
+ emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[9], MAP5_STR);
+ emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[10], MAP6_STR);
+ emitContextDecision(o1, o2, i1, i2, ModRMTableNum, *Tables[11], MAP7_STR);
}
void DisassemblerTables::emit(raw_ostream &o) const {
@@ -1021,24 +1035,22 @@ void DisassemblerTables::emit(raw_ostream &o) const {
o << "\n";
}
-void DisassemblerTables::setTableFields(ModRMDecision &decision,
- const ModRMFilter &filter,
- InstrUID uid,
- uint8_t opcode) {
+void DisassemblerTables::setTableFields(ModRMDecision &decision,
+ const ModRMFilter &filter, InstrUID uid,
+ uint8_t opcode) {
for (unsigned index = 0; index < 256; ++index) {
if (filter.accepts(index)) {
if (decision.instructionIDs[index] == uid)
continue;
if (decision.instructionIDs[index] != 0) {
- InstructionSpecifier &newInfo =
- InstructionSpecifiers[uid];
+ InstructionSpecifier &newInfo = InstructionSpecifiers[uid];
InstructionSpecifier &previousInfo =
- InstructionSpecifiers[decision.instructionIDs[index]];
+ InstructionSpecifiers[decision.instructionIDs[index]];
- if(previousInfo.name == "NOOP" && (newInfo.name == "XCHG16ar" ||
- newInfo.name == "XCHG32ar" ||
- newInfo.name == "XCHG64ar"))
+ if (previousInfo.name == "NOOP" &&
+ (newInfo.name == "XCHG16ar" || newInfo.name == "XCHG32ar" ||
+ newInfo.name == "XCHG64ar"))
continue; // special case for XCHG*ar and NOOP
if (outranks(previousInfo.insnContext, newInfo.insnContext))
@@ -1060,16 +1072,10 @@ void DisassemblerTables::setTableFields(ModRMDecision &decision,
}
}
-void DisassemblerTables::setTableFields(OpcodeType type,
- InstructionContext insnContext,
- uint8_t opcode,
- const ModRMFilter &filter,
- InstrUID uid,
- bool is32bit,
- bool noPrefix,
- bool ignoresVEX_L,
- bool ignoresW,
- unsigned addressSize) {
+void DisassemblerTables::setTableFields(
+ OpcodeType type, InstructionContext insnContext, uint8_t opcode,
+ const ModRMFilter &filter, InstrUID uid, bool is32bit, bool noPrefix,
+ bool ignoresVEX_L, bool ignoresW, unsigned addressSize) {
ContextDecision &decision = *Tables[type];
for (unsigned index = 0; index < IC_max; ++index) {
@@ -1082,8 +1088,6 @@ void DisassemblerTables::setTableFields(OpcodeType type,
InstructionSpecifiers[uid].insnContext, noPrefix,
ignoresVEX_L, ignoresW, adSize64))
setTableFields(decision.opcodeDecisions[index].modRMDecisions[opcode],
- filter,
- uid,
- opcode);
+ filter, uid, opcode);
}
}
diff --git a/llvm/utils/TableGen/X86DisassemblerTables.h b/llvm/utils/TableGen/X86DisassemblerTables.h
index 966f7406efec..4fbc58bea338 100644
--- a/llvm/utils/TableGen/X86DisassemblerTables.h
+++ b/llvm/utils/TableGen/X86DisassemblerTables.h
@@ -44,9 +44,11 @@ private:
/// [5] XOP9 map opcode
/// [6] XOPA map opcode
/// [7] 3dnow map opcode
- /// [8] fixed length MAP5 opcode
- /// [9] fixed length MAP6 opcode
- std::unique_ptr<ContextDecision> Tables[10];
+ /// [8] fixed length MAP4 opcode
+ /// [9] fixed length MAP5 opcode
+ /// [10] fixed length MAP6 opcode
+ /// [11] fixed length MAP7 opcode
+ std::unique_ptr<ContextDecision> Tables[12];
// Table of ModRM encodings.
typedef std::map<std::vector<unsigned>, unsigned> ModRMMapTy;
diff --git a/llvm/utils/TableGen/X86EVEX2VEXTablesEmitter.cpp b/llvm/utils/TableGen/X86EVEX2VEXTablesEmitter.cpp
index 35792ab67a4f..c80d9a199fa3 100644
--- a/llvm/utils/TableGen/X86EVEX2VEXTablesEmitter.cpp
+++ b/llvm/utils/TableGen/X86EVEX2VEXTablesEmitter.cpp
@@ -33,14 +33,12 @@ class X86EVEX2VEXTablesEmitter {
// to make the search more efficient
std::map<uint64_t, std::vector<const CodeGenInstruction *>> VEXInsts;
- typedef std::pair<const CodeGenInstruction *, const CodeGenInstruction *> Entry;
- typedef std::pair<StringRef, StringRef> Predicate;
+ typedef std::pair<const CodeGenInstruction *, const CodeGenInstruction *>
+ Entry;
// Represent both compress tables
std::vector<Entry> EVEX2VEX128;
std::vector<Entry> EVEX2VEX256;
- // Represent predicates of VEX instructions.
- std::vector<Predicate> EVEX2VEXPredicates;
public:
X86EVEX2VEXTablesEmitter(RecordKeeper &R) : Records(R), Target(R) {}
@@ -52,9 +50,6 @@ private:
// Prints the given table as a C++ array of type
// X86EvexToVexCompressTableEntry
void printTable(const std::vector<Entry> &Table, raw_ostream &OS);
- // Prints function which checks target feature specific predicate.
- void printCheckPredicate(const std::vector<Predicate> &Predicates,
- raw_ostream &OS);
};
void X86EVEX2VEXTablesEmitter::printTable(const std::vector<Entry> &Table,
@@ -77,19 +72,6 @@ void X86EVEX2VEXTablesEmitter::printTable(const std::vector<Entry> &Table,
OS << "};\n\n";
}
-void X86EVEX2VEXTablesEmitter::printCheckPredicate(
- const std::vector<Predicate> &Predicates, raw_ostream &OS) {
- OS << "static bool CheckVEXInstPredicate"
- << "(MachineInstr &MI, const X86Subtarget *Subtarget) {\n"
- << " unsigned Opc = MI.getOpcode();\n"
- << " switch (Opc) {\n"
- << " default: return true;\n";
- for (const auto &Pair : Predicates)
- OS << " case X86::" << Pair.first << ": return " << Pair.second << ";\n";
- OS << " }\n"
- << "}\n\n";
-}
-
// Return true if the 2 BitsInits are equal
// Calculates the integer value residing BitsInit object
static inline uint64_t getValueFromBitsInit(const BitsInit *B) {
@@ -164,18 +146,6 @@ public:
};
void X86EVEX2VEXTablesEmitter::run(raw_ostream &OS) {
- auto getPredicates = [&](const CodeGenInstruction *Inst) {
- std::vector<Record *> PredicatesRecords =
- Inst->TheDef->getValueAsListOfDefs("Predicates");
- // Currently we only do AVX related checks and assume each instruction
- // has one and only one AVX related predicates.
- for (unsigned i = 0, e = PredicatesRecords.size(); i != e; ++i)
- if (PredicatesRecords[i]->getName().startswith("HasAVX"))
- return PredicatesRecords[i]->getValueAsString("CondString");
- llvm_unreachable(
- "Instruction with checkPredicate set must have one predicate!");
- };
-
emitSourceFileHeader("X86 EVEX2VEX tables", OS);
ArrayRef<const CodeGenInstruction *> NumberedInstructions =
@@ -186,6 +156,9 @@ void X86EVEX2VEXTablesEmitter::run(raw_ostream &OS) {
// Filter non-X86 instructions.
if (!Def->isSubClassOf("X86Inst"))
continue;
+ // _REV instruction should not appear before encoding optimization
+ if (Def->getName().ends_with("_REV"))
+ continue;
RecognizableInstrBase RI(*Inst);
// Add VEX encoded instructions to one of VEXInsts vectors according to
@@ -225,18 +198,11 @@ void X86EVEX2VEXTablesEmitter::run(raw_ostream &OS) {
EVEX2VEX256.push_back(std::make_pair(EVEXInst, VEXInst)); // {0,1}
else
EVEX2VEX128.push_back(std::make_pair(EVEXInst, VEXInst)); // {0,0}
-
- // Adding predicate check to EVEX2VEXPredicates table when needed.
- if (VEXInst->TheDef->getValueAsBit("checkVEXPredicate"))
- EVEX2VEXPredicates.push_back(
- std::make_pair(EVEXInst->TheDef->getName(), getPredicates(VEXInst)));
}
// Print both tables
printTable(EVEX2VEX128, OS);
printTable(EVEX2VEX256, OS);
- // Print CheckVEXInstPredicate function.
- printCheckPredicate(EVEX2VEXPredicates, OS);
}
} // namespace
diff --git a/llvm/utils/TableGen/X86FoldTablesEmitter.cpp b/llvm/utils/TableGen/X86FoldTablesEmitter.cpp
index 89d93e4d3cbc..5fb6b048542b 100644
--- a/llvm/utils/TableGen/X86FoldTablesEmitter.cpp
+++ b/llvm/utils/TableGen/X86FoldTablesEmitter.cpp
@@ -14,11 +14,11 @@
#include "CodeGenInstruction.h"
#include "CodeGenTarget.h"
#include "X86RecognizableInstr.h"
-#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/X86FoldTablesUtils.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
+#include <set>
using namespace llvm;
using namespace X86Disassembler;
@@ -36,16 +36,16 @@ const char *ExplicitAlign[] = {"MOVDQA", "MOVAPS", "MOVAPD", "MOVNTPS",
"MOVNTPD", "MOVNTDQ", "MOVNTDQA"};
// List of instructions NOT requiring explicit memory alignment.
-const char *ExplicitUnalign[] = {"MOVDQU", "MOVUPS", "MOVUPD",
- "PCMPESTRM", "PCMPESTRI",
- "PCMPISTRM", "PCMPISTRI" };
+const char *ExplicitUnalign[] = {"MOVDQU", "MOVUPS", "MOVUPD",
+ "PCMPESTRM", "PCMPESTRI", "PCMPISTRM",
+ "PCMPISTRI"};
const ManualMapEntry ManualMapSet[] = {
#define ENTRY(REG, MEM, FLAGS) {#REG, #MEM, FLAGS},
#include "X86ManualFoldTables.def"
};
-const std::set<StringRef> NoFoldSet= {
+const std::set<StringRef> NoFoldSet = {
#define NOFOLD(INSN) #INSN,
#include "X86ManualFoldTables.def"
};
@@ -76,6 +76,16 @@ class X86FoldTablesEmitter {
bool NoForward = false;
bool FoldLoad = false;
bool FoldStore = false;
+ enum BcastType {
+ BCAST_NONE,
+ BCAST_D,
+ BCAST_Q,
+ BCAST_SS,
+ BCAST_SD,
+ BCAST_SH,
+ };
+ BcastType BroadcastKind = BCAST_NONE;
+
Align Alignment;
X86FoldTableEntry() = default;
@@ -86,7 +96,7 @@ class X86FoldTablesEmitter {
void print(formatted_raw_ostream &OS) const {
OS.indent(2);
OS << "{X86::" << RegInst->TheDef->getName() << ", ";
- OS << "X86::" << MemInst->TheDef->getName() << ", ";
+ OS << "X86::" << MemInst->TheDef->getName() << ", ";
std::string Attrs;
if (FoldLoad)
@@ -99,6 +109,25 @@ class X86FoldTablesEmitter {
Attrs += "TB_NO_FORWARD|";
if (Alignment != Align(1))
Attrs += "TB_ALIGN_" + std::to_string(Alignment.value()) + "|";
+ switch (BroadcastKind) {
+ case BCAST_NONE:
+ break;
+ case BCAST_D:
+ Attrs += "TB_BCAST_D|";
+ break;
+ case BCAST_Q:
+ Attrs += "TB_BCAST_Q|";
+ break;
+ case BCAST_SS:
+ Attrs += "TB_BCAST_SS|";
+ break;
+ case BCAST_SD:
+ Attrs += "TB_BCAST_SD|";
+ break;
+ case BCAST_SH:
+ Attrs += "TB_BCAST_SH|";
+ break;
+ }
StringRef SimplifiedAttrs = StringRef(Attrs).rtrim("|");
if (SimplifiedAttrs.empty())
@@ -124,9 +153,10 @@ class X86FoldTablesEmitter {
#endif
};
- // NOTE: We check the fold tables are sorted in X86InstrFoldTables.cpp by the enum of the
- // instruction, which is computed in CodeGenTarget::ComputeInstrsByEnum. So we should
- // use the same comparator here.
+ // NOTE: We check the fold tables are sorted in X86InstrFoldTables.cpp by the
+ // enum of the instruction, which is computed in
+ // CodeGenTarget::ComputeInstrsByEnum. So we should use the same comparator
+ // here.
// FIXME: Could we share the code with CodeGenTarget::ComputeInstrsByEnum?
struct CompareInstrsByEnum {
bool operator()(const CodeGenInstruction *LHS,
@@ -142,16 +172,24 @@ class X86FoldTablesEmitter {
typedef std::map<const CodeGenInstruction *, X86FoldTableEntry,
CompareInstrsByEnum>
FoldTable;
- // std::vector for each folding table.
- // Table2Addr - Holds instructions which their memory form performs load+store
- // Table#i - Holds instructions which the their memory form perform a load OR
- // a store, and their #i'th operand is folded.
+ // Table2Addr - Holds instructions which their memory form performs
+ // load+store.
+ //
+ // Table#i - Holds instructions which the their memory form
+ // performs a load OR a store, and their #i'th operand is folded.
+ //
+ // BroadcastTable#i - Holds instructions which the their memory form performs
+ // a broadcast load and their #i'th operand is folded.
FoldTable Table2Addr;
FoldTable Table0;
FoldTable Table1;
FoldTable Table2;
FoldTable Table3;
FoldTable Table4;
+ FoldTable BroadcastTable1;
+ FoldTable BroadcastTable2;
+ FoldTable BroadcastTable3;
+ FoldTable BroadcastTable4;
public:
X86FoldTablesEmitter(RecordKeeper &R) : Records(R), Target(R) {}
@@ -162,22 +200,25 @@ public:
private:
// Decides to which table to add the entry with the given instructions.
// S sets the strategy of adding the TB_NO_REVERSE flag.
- void updateTables(const CodeGenInstruction *RegInstr,
- const CodeGenInstruction *MemInstr, uint16_t S = 0,
- bool IsManual = false);
+ void updateTables(const CodeGenInstruction *RegInst,
+ const CodeGenInstruction *MemInst, uint16_t S = 0,
+ bool IsManual = false, bool IsBroadcast = false);
// Generates X86FoldTableEntry with the given instructions and fill it with
- // the appropriate flags - then adds it to Table.
- void addEntryWithFlags(FoldTable &Table, const CodeGenInstruction *RegInstr,
- const CodeGenInstruction *MemInstr, uint16_t S,
- unsigned FoldedIdx, bool isManual);
+ // the appropriate flags, then adds it to a memory fold table.
+ void addEntryWithFlags(FoldTable &Table, const CodeGenInstruction *RegInst,
+ const CodeGenInstruction *MemInst, uint16_t S,
+ unsigned FoldedIdx, bool IsManual);
+ // Generates X86FoldTableEntry with the given instructions and adds it to a
+ // broadcast table.
+ void addBroadcastEntry(FoldTable &Table, const CodeGenInstruction *RegInst,
+ const CodeGenInstruction *MemInst);
// Print the given table as a static const C++ array of type
- // X86MemoryFoldTableEntry.
+ // X86FoldTableEntry.
void printTable(const FoldTable &Table, StringRef TableName,
formatted_raw_ostream &OS) {
- OS << "static const X86MemoryFoldTableEntry MemoryFold" << TableName
- << "[] = {\n";
+ OS << "static const X86FoldTableEntry " << TableName << "[] = {\n";
for (auto &E : Table)
E.second.print(OS);
@@ -288,11 +329,12 @@ static bool isNOREXRegClass(const Record *Op) {
class IsMatch {
const CodeGenInstruction *MemInst;
const X86Disassembler::RecognizableInstrBase MemRI;
+ bool IsBroadcast;
const unsigned Variant;
public:
- IsMatch(const CodeGenInstruction *Inst, unsigned V)
- : MemInst(Inst), MemRI(*MemInst), Variant(V) {}
+ IsMatch(const CodeGenInstruction *Inst, bool IsBroadcast, unsigned V)
+ : MemInst(Inst), MemRI(*MemInst), IsBroadcast(IsBroadcast), Variant(V) {}
bool operator()(const CodeGenInstruction *RegInst) {
X86Disassembler::RecognizableInstrBase RegRI(*RegInst);
@@ -300,7 +342,11 @@ public:
const Record *MemRec = MemInst->TheDef;
// EVEX_B means different things for memory and register forms.
- if (RegRI.HasEVEX_B || MemRI.HasEVEX_B)
+ // register form: rounding control or SAE
+ // memory form: broadcast
+ if (IsBroadcast && (RegRI.HasEVEX_B || !MemRI.HasEVEX_B))
+ return false;
+ if (!IsBroadcast && (RegRI.HasEVEX_B || MemRI.HasEVEX_B))
return false;
if (!mayFoldFromLeftToRight(RegRI.Form, MemRI.Form))
@@ -388,22 +434,24 @@ public:
} // end anonymous namespace
void X86FoldTablesEmitter::addEntryWithFlags(FoldTable &Table,
- const CodeGenInstruction *RegInstr,
- const CodeGenInstruction *MemInstr,
+ const CodeGenInstruction *RegInst,
+ const CodeGenInstruction *MemInst,
uint16_t S, unsigned FoldedIdx,
- bool isManual) {
+ bool IsManual) {
- X86FoldTableEntry Result = X86FoldTableEntry(RegInstr, MemInstr);
- Record *RegRec = RegInstr->TheDef;
- Record *MemRec = MemInstr->TheDef;
+ assert((IsManual || Table.find(RegInst) == Table.end()) &&
+ "Override entry unexpectedly");
+ X86FoldTableEntry Result = X86FoldTableEntry(RegInst, MemInst);
+ Record *RegRec = RegInst->TheDef;
+ Record *MemRec = MemInst->TheDef;
- if (isManual) {
- Result.NoReverse = S & TB_NO_REVERSE;
- Result.NoForward = S & TB_NO_FORWARD;
- Result.FoldLoad = S & TB_FOLDED_LOAD;
- Result.FoldStore = S & TB_FOLDED_STORE;
- Result.Alignment = Align(1ULL << ((S & TB_ALIGN_MASK) >> TB_ALIGN_SHIFT));
- Table[RegInstr] = Result;
+ Result.NoReverse = S & TB_NO_REVERSE;
+ Result.NoForward = S & TB_NO_FORWARD;
+ Result.FoldLoad = S & TB_FOLDED_LOAD;
+ Result.FoldStore = S & TB_FOLDED_STORE;
+ Result.Alignment = Align(1ULL << ((S & TB_ALIGN_MASK) >> TB_ALIGN_SHIFT));
+ if (IsManual) {
+ Table[RegInst] = Result;
return;
}
@@ -422,8 +470,8 @@ void X86FoldTablesEmitter::addEntryWithFlags(FoldTable &Table,
Result.FoldStore = true;
}
- Record *RegOpRec = RegInstr->Operands[FoldedIdx].Rec;
- Record *MemOpRec = MemInstr->Operands[FoldedIdx].Rec;
+ Record *RegOpRec = RegInst->Operands[FoldedIdx].Rec;
+ Record *MemOpRec = MemInst->Operands[FoldedIdx].Rec;
// Unfolding code generates a load/store instruction according to the size of
// the register in the register form instruction.
@@ -439,22 +487,22 @@ void X86FoldTablesEmitter::addEntryWithFlags(FoldTable &Table,
// Check no-kz version's isMoveReg
StringRef RegInstName = RegRec->getName();
unsigned DropLen =
- RegInstName.endswith("rkz") ? 2 : (RegInstName.endswith("rk") ? 1 : 0);
+ RegInstName.ends_with("rkz") ? 2 : (RegInstName.ends_with("rk") ? 1 : 0);
Record *BaseDef =
DropLen ? Records.getDef(RegInstName.drop_back(DropLen)) : nullptr;
bool IsMoveReg =
- BaseDef ? Target.getInstruction(BaseDef).isMoveReg : RegInstr->isMoveReg;
+ BaseDef ? Target.getInstruction(BaseDef).isMoveReg : RegInst->isMoveReg;
// A masked load can not be unfolded to a full load, otherwise it would access
// unexpected memory. A simple store can not be unfolded.
if (IsMoveReg && (BaseDef || Result.FoldStore))
Result.NoReverse = true;
uint8_t Enc = byteFromBitsInit(RegRec->getValueAsBitsInit("OpEncBits"));
- if (isExplicitAlign(RegInstr)) {
+ if (isExplicitAlign(RegInst)) {
// The instruction require explicitly aligned memory.
BitsInit *VectSize = RegRec->getValueAsBitsInit("VectSize");
Result.Alignment = Align(byteFromBitsInit(VectSize));
- } else if (!Enc && !isExplicitUnalign(RegInstr) &&
+ } else if (!Enc && !isExplicitUnalign(RegInst) &&
getMemOperandSize(MemOpRec) > 64) {
// Instructions with XOP/VEX/EVEX encoding do not require alignment while
// SSE packed vector instructions require a 16 byte alignment.
@@ -467,15 +515,75 @@ void X86FoldTablesEmitter::addEntryWithFlags(FoldTable &Table,
if (RegRec->getName().contains("EXPAND"))
Result.NoReverse = true;
- Table[RegInstr] = Result;
+ Table[RegInst] = Result;
+}
+
+void X86FoldTablesEmitter::addBroadcastEntry(
+ FoldTable &Table, const CodeGenInstruction *RegInst,
+ const CodeGenInstruction *MemInst) {
+
+ assert(Table.find(RegInst) == Table.end() && "Override entry unexpectedly");
+ X86FoldTableEntry Result = X86FoldTableEntry(RegInst, MemInst);
+
+ Record *RegRec = RegInst->TheDef;
+ StringRef RegInstName = RegRec->getName();
+ StringRef MemInstName = MemInst->TheDef->getName();
+ Record *Domain = RegRec->getValueAsDef("ExeDomain");
+ bool IsSSEPackedInt = Domain->getName() == "SSEPackedInt";
+ // TODO: Rename AVX512 instructions to simplify conditions, e.g.
+ // D128 -> DZ128
+ // D256 -> DZ256
+ // VPERMI2Drr -> VPERMI2DZrr
+ // VPERMI2Drmb -> VPERMI2DZrmb
+ if ((RegInstName.contains("DZ") || RegInstName.contains("DWZ") ||
+ RegInstName.contains("D128") || RegInstName.contains("D256") ||
+ RegInstName.contains("Dr") || RegInstName.contains("I32")) &&
+ IsSSEPackedInt) {
+ assert((MemInstName.contains("DZ") || RegInstName.contains("DWZ") ||
+ MemInstName.contains("D128") || MemInstName.contains("D256") ||
+ MemInstName.contains("Dr") || MemInstName.contains("I32")) &&
+ "Unmatched names for broadcast");
+ Result.BroadcastKind = X86FoldTableEntry::BCAST_D;
+ } else if ((RegInstName.contains("QZ") || RegInstName.contains("QBZ") ||
+ RegInstName.contains("Q128") || RegInstName.contains("Q256") ||
+ RegInstName.contains("Qr") || RegInstName.contains("I64")) &&
+ IsSSEPackedInt) {
+ assert((MemInstName.contains("QZ") || MemInstName.contains("QBZ") ||
+ MemInstName.contains("Q128") || MemInstName.contains("Q256") ||
+ MemInstName.contains("Qr") || MemInstName.contains("I64")) &&
+ "Unmatched names for broadcast");
+ Result.BroadcastKind = X86FoldTableEntry::BCAST_Q;
+ } else if ((RegInstName.contains("PS") || RegInstName.contains("F32") ||
+ RegInstName.contains("CPH")) &&
+ !RegInstName.contains("PH2PS")) {
+ assert((MemInstName.contains("PS") || MemInstName.contains("F32") ||
+ MemInstName.contains("CPH")) &&
+ "Unmatched names for broadcast");
+ Result.BroadcastKind = X86FoldTableEntry::BCAST_SS;
+ } else if ((RegInstName.contains("PD") || RegInstName.contains("F64")) &&
+ !RegInstName.contains("PH2PD")) {
+ assert((MemInstName.contains("PD") || MemInstName.contains("F64")) &&
+ "Unmatched names for broadcast");
+ Result.BroadcastKind = X86FoldTableEntry::BCAST_SD;
+ } else if (RegInstName.contains("PH")) {
+ assert(MemInstName.contains("PH") && "Unmatched names for broadcast");
+ Result.BroadcastKind = X86FoldTableEntry::BCAST_SH;
+ } else {
+ errs() << RegInstName << ", " << MemInstName << "\n";
+ llvm_unreachable("Name is not canoicalized for broadcast or "
+ "ExeDomain is incorrect");
+ }
+
+ Table[RegInst] = Result;
}
-void X86FoldTablesEmitter::updateTables(const CodeGenInstruction *RegInstr,
- const CodeGenInstruction *MemInstr,
- uint16_t S, bool IsManual) {
+void X86FoldTablesEmitter::updateTables(const CodeGenInstruction *RegInst,
+ const CodeGenInstruction *MemInst,
+ uint16_t S, bool IsManual,
+ bool IsBroadcast) {
- Record *RegRec = RegInstr->TheDef;
- Record *MemRec = MemInstr->TheDef;
+ Record *RegRec = RegInst->TheDef;
+ Record *MemRec = MemInst->TheDef;
unsigned MemOutSize = MemRec->getValueAsDag("OutOperandList")->getNumArgs();
unsigned RegOutSize = RegRec->getValueAsDag("OutOperandList")->getNumArgs();
unsigned MemInSize = MemRec->getValueAsDag("InOperandList")->getNumArgs();
@@ -483,7 +591,10 @@ void X86FoldTablesEmitter::updateTables(const CodeGenInstruction *RegInstr,
// Instructions which Read-Modify-Write should be added to Table2Addr.
if (!MemOutSize && RegOutSize == 1 && MemInSize == RegInSize) {
- addEntryWithFlags(Table2Addr, RegInstr, MemInstr, S, 0, IsManual);
+ assert(!IsBroadcast && "Read-Modify-Write can not be broadcast");
+ // X86 would not unfold Read-Modify-Write instructions so add TB_NO_REVERSE.
+ addEntryWithFlags(Table2Addr, RegInst, MemInst, S | TB_NO_REVERSE, 0,
+ IsManual);
return;
}
@@ -491,28 +602,38 @@ void X86FoldTablesEmitter::updateTables(const CodeGenInstruction *RegInstr,
// Load-Folding cases.
// If the i'th register form operand is a register and the i'th memory form
// operand is a memory operand, add instructions to Table#i.
- for (unsigned i = RegOutSize, e = RegInstr->Operands.size(); i < e; i++) {
- Record *RegOpRec = RegInstr->Operands[i].Rec;
- Record *MemOpRec = MemInstr->Operands[i].Rec;
- // PointerLikeRegClass: For instructions like TAILJMPr, TAILJMPr64, TAILJMPr64_REX
+ for (unsigned I = RegOutSize, E = RegInst->Operands.size(); I < E; I++) {
+ Record *RegOpRec = RegInst->Operands[I].Rec;
+ Record *MemOpRec = MemInst->Operands[I].Rec;
+ // PointerLikeRegClass: For instructions like TAILJMPr, TAILJMPr64,
+ // TAILJMPr64_REX
if ((isRegisterOperand(RegOpRec) ||
RegOpRec->isSubClassOf("PointerLikeRegClass")) &&
isMemoryOperand(MemOpRec)) {
- switch (i) {
+ switch (I) {
case 0:
- addEntryWithFlags(Table0, RegInstr, MemInstr, S, 0, IsManual);
+ assert(!IsBroadcast && "BroadcastTable0 needs to be added");
+ addEntryWithFlags(Table0, RegInst, MemInst, S, 0, IsManual);
return;
case 1:
- addEntryWithFlags(Table1, RegInstr, MemInstr, S, 1, IsManual);
+ IsBroadcast
+ ? addBroadcastEntry(BroadcastTable1, RegInst, MemInst)
+ : addEntryWithFlags(Table1, RegInst, MemInst, S, 1, IsManual);
return;
case 2:
- addEntryWithFlags(Table2, RegInstr, MemInstr, S, 2, IsManual);
+ IsBroadcast
+ ? addBroadcastEntry(BroadcastTable2, RegInst, MemInst)
+ : addEntryWithFlags(Table2, RegInst, MemInst, S, 2, IsManual);
return;
case 3:
- addEntryWithFlags(Table3, RegInstr, MemInstr, S, 3, IsManual);
+ IsBroadcast
+ ? addBroadcastEntry(BroadcastTable3, RegInst, MemInst)
+ : addEntryWithFlags(Table3, RegInst, MemInst, S, 3, IsManual);
return;
case 4:
- addEntryWithFlags(Table4, RegInstr, MemInstr, S, 4, IsManual);
+ IsBroadcast
+ ? addBroadcastEntry(BroadcastTable4, RegInst, MemInst)
+ : addEntryWithFlags(Table4, RegInst, MemInst, S, 4, IsManual);
return;
}
}
@@ -525,16 +646,18 @@ void X86FoldTablesEmitter::updateTables(const CodeGenInstruction *RegInstr,
// For example:
// MOVAPSrr => (outs VR128:$dst), (ins VR128:$src)
// MOVAPSmr => (outs), (ins f128mem:$dst, VR128:$src)
- Record *RegOpRec = RegInstr->Operands[RegOutSize - 1].Rec;
- Record *MemOpRec = MemInstr->Operands[RegOutSize - 1].Rec;
+ Record *RegOpRec = RegInst->Operands[RegOutSize - 1].Rec;
+ Record *MemOpRec = MemInst->Operands[RegOutSize - 1].Rec;
if (isRegisterOperand(RegOpRec) && isMemoryOperand(MemOpRec) &&
- getRegOperandSize(RegOpRec) == getMemOperandSize(MemOpRec))
- addEntryWithFlags(Table0, RegInstr, MemInstr, S, 0, IsManual);
+ getRegOperandSize(RegOpRec) == getMemOperandSize(MemOpRec)) {
+ assert(!IsBroadcast && "Store can not be broadcast");
+ addEntryWithFlags(Table0, RegInst, MemInst, S, 0, IsManual);
+ }
}
}
-void X86FoldTablesEmitter::run(raw_ostream &o) {
- formatted_raw_ostream OS(o);
+void X86FoldTablesEmitter::run(raw_ostream &O) {
+ formatted_raw_ostream OS(O);
// Holds all memory instructions
std::vector<const CodeGenInstruction *> MemInsts;
@@ -575,8 +698,19 @@ void X86FoldTablesEmitter::run(raw_ostream &o) {
}
}
+ // Create a copy b/c the register instruction will removed when a new entry is
+ // added into memory fold tables.
+ auto RegInstsForBroadcast = RegInsts;
+
Record *AsmWriter = Target.getAsmWriter();
unsigned Variant = AsmWriter->getValueAsInt("Variant");
+ auto FixUp = [&](const CodeGenInstruction *RegInst) {
+ StringRef RegInstName = RegInst->TheDef->getName();
+ if (RegInstName.ends_with("_REV") || RegInstName.ends_with("_alt"))
+ if (auto *RegAltRec = Records.getDef(RegInstName.drop_back(4)))
+ RegInst = &Target.getInstruction(RegAltRec);
+ return RegInst;
+ };
// For each memory form instruction, try to find its register form
// instruction.
for (const CodeGenInstruction *MemInst : MemInsts) {
@@ -588,22 +722,33 @@ void X86FoldTablesEmitter::run(raw_ostream &o) {
continue;
// Two forms (memory & register) of the same instruction must have the same
- // opcode. try matching only with register form instructions with the same
// opcode.
std::vector<const CodeGenInstruction *> &OpcRegInsts = RegInstsIt->second;
- auto Match = find_if(OpcRegInsts, IsMatch(MemInst, Variant));
+ // Memory fold tables
+ auto Match =
+ find_if(OpcRegInsts, IsMatch(MemInst, /*IsBroadcast=*/false, Variant));
if (Match != OpcRegInsts.end()) {
- const CodeGenInstruction *RegInst = *Match;
- StringRef RegInstName = RegInst->TheDef->getName();
- if (RegInstName.endswith("_REV") || RegInstName.endswith("_alt")) {
- if (auto *RegAltRec = Records.getDef(RegInstName.drop_back(4))) {
- RegInst = &Target.getInstruction(RegAltRec);
- }
- }
- updateTables(RegInst, MemInst);
+ updateTables(FixUp(*Match), MemInst);
OpcRegInsts.erase(Match);
}
+
+ // Broadcast tables
+ StringRef MemInstName = MemInst->TheDef->getName();
+ if (!MemInstName.contains("mb") && !MemInstName.contains("mib"))
+ continue;
+ RegInstsIt = RegInstsForBroadcast.find(Opc);
+ assert(RegInstsIt != RegInstsForBroadcast.end() &&
+ "Unexpected control flow");
+ std::vector<const CodeGenInstruction *> &OpcRegInstsForBroadcast =
+ RegInstsIt->second;
+ Match = find_if(OpcRegInstsForBroadcast,
+ IsMatch(MemInst, /*IsBroadcast=*/true, Variant));
+ if (Match != OpcRegInstsForBroadcast.end()) {
+ updateTables(FixUp(*Match), MemInst, 0, /*IsManual=*/false,
+ /*IsBroadcast=*/true);
+ OpcRegInstsForBroadcast.erase(Match);
+ }
}
// Add the manually mapped instructions listed above.
@@ -628,14 +773,23 @@ void X86FoldTablesEmitter::run(raw_ostream &o) {
CheckMemFoldTable(Table2);
CheckMemFoldTable(Table3);
CheckMemFoldTable(Table4);
+ CheckMemFoldTable(BroadcastTable1);
+ CheckMemFoldTable(BroadcastTable2);
+ CheckMemFoldTable(BroadcastTable3);
+ CheckMemFoldTable(BroadcastTable4);
#endif
+#define PRINT_TABLE(TABLE) printTable(TABLE, #TABLE, OS);
// Print all tables.
- printTable(Table2Addr, "Table2Addr", OS);
- printTable(Table0, "Table0", OS);
- printTable(Table1, "Table1", OS);
- printTable(Table2, "Table2", OS);
- printTable(Table3, "Table3", OS);
- printTable(Table4, "Table4", OS);
+ PRINT_TABLE(Table2Addr)
+ PRINT_TABLE(Table0)
+ PRINT_TABLE(Table1)
+ PRINT_TABLE(Table2)
+ PRINT_TABLE(Table3)
+ PRINT_TABLE(Table4)
+ PRINT_TABLE(BroadcastTable1)
+ PRINT_TABLE(BroadcastTable2)
+ PRINT_TABLE(BroadcastTable3)
+ PRINT_TABLE(BroadcastTable4)
}
static TableGen::Emitter::OptClass<X86FoldTablesEmitter>
diff --git a/llvm/utils/TableGen/X86ManualFoldTables.def b/llvm/utils/TableGen/X86ManualFoldTables.def
index d949830b0988..8e6cb4a7bd87 100644
--- a/llvm/utils/TableGen/X86ManualFoldTables.def
+++ b/llvm/utils/TableGen/X86ManualFoldTables.def
@@ -225,6 +225,8 @@ NOFOLD(MMX_MOVQ64rr_REV)
// =>
// insertpsrm xmm1, m32, imm
NOFOLD(INSERTPSrr)
+NOFOLD(VINSERTPSZrr)
+NOFOLD(VINSERTPSrr)
#undef NOFOLD
#ifndef ENTRY
diff --git a/llvm/utils/TableGen/X86RecognizableInstr.cpp b/llvm/utils/TableGen/X86RecognizableInstr.cpp
index b2f51ba01689..6e03fc11d6d9 100644
--- a/llvm/utils/TableGen/X86RecognizableInstr.cpp
+++ b/llvm/utils/TableGen/X86RecognizableInstr.cpp
@@ -24,18 +24,19 @@
using namespace llvm;
using namespace X86Disassembler;
-std::string X86Disassembler::getMnemonic(const CodeGenInstruction *I, unsigned Variant) {
- std::string AsmString = I->FlattenAsmStringVariants(I->AsmString, Variant);
- StringRef Mnemonic(AsmString);
- // Extract a mnemonic assuming it's separated by \t
- Mnemonic = Mnemonic.take_until([](char C) { return C == '\t'; });
+std::string X86Disassembler::getMnemonic(const CodeGenInstruction *I,
+ unsigned Variant) {
+ std::string AsmString = I->FlattenAsmStringVariants(I->AsmString, Variant);
+ StringRef Mnemonic(AsmString);
+ // Extract a mnemonic assuming it's separated by \t
+ Mnemonic = Mnemonic.take_until([](char C) { return C == '\t'; });
- // Special case: CMOVCC, JCC, SETCC have "${cond}" in mnemonic.
- // Replace it with "CC" in-place.
- size_t CondPos = Mnemonic.find("${cond}");
- if (CondPos != StringRef::npos)
- Mnemonic = AsmString.replace(CondPos, StringRef::npos, "CC");
- return Mnemonic.upper();
+ // Special case: CMOVCC, JCC, SETCC have "${cond}" in mnemonic.
+ // Replace it with "CC" in-place.
+ size_t CondPos = Mnemonic.find("${cond}");
+ if (CondPos != StringRef::npos)
+ Mnemonic = AsmString.replace(CondPos, StringRef::npos, "CC");
+ return Mnemonic.upper();
}
bool X86Disassembler::isRegisterOperand(const Record *Rec) {
@@ -80,7 +81,7 @@ static uint8_t byteFromBitsInit(BitsInit &init) {
assert(width <= 8 && "Field is too large for uint8_t!");
- int index;
+ int index;
uint8_t mask = 0x01;
uint8_t ret = 0;
@@ -101,8 +102,8 @@ static uint8_t byteFromBitsInit(BitsInit &init) {
/// @param rec - The record from which to extract the value.
/// @param name - The name of the field in the record.
/// @return - The field, as translated by byteFromBitsInit().
-static uint8_t byteFromRec(const Record* rec, StringRef name) {
- BitsInit* bits = rec->getValueAsBitsInit(name);
+static uint8_t byteFromRec(const Record *rec, StringRef name) {
+ BitsInit *bits = rec->getValueAsBitsInit(name);
return byteFromBitsInit(*bits);
}
@@ -129,6 +130,8 @@ RecognizableInstrBase::RecognizableInstrBase(const CodeGenInstruction &insn) {
ForceDisassemble = Rec->getValueAsBit("ForceDisassemble");
CD8_Scale = byteFromRec(Rec, "CD8_Scale");
HasVEX_L = Rec->getValueAsBit("hasVEX_L");
+ ExplicitREX2Prefix =
+ byteFromRec(Rec, "explicitOpPrefixBits") == X86Local::ExplicitREX2;
EncodeRC = HasEVEX_B &&
(Form == X86Local::MRMDestReg || Form == X86Local::MRMSrcReg);
@@ -174,10 +177,13 @@ void RecognizableInstr::processInstr(DisassemblerTables &tables,
recogInstr.emitDecodePath(tables);
}
-#define EVEX_KB(n) (HasEVEX_KZ && HasEVEX_B ? n##_KZ_B : \
- (HasEVEX_K && HasEVEX_B ? n##_K_B : \
- (HasEVEX_KZ ? n##_KZ : \
- (HasEVEX_K? n##_K : (HasEVEX_B ? n##_B : n)))))
+#define EVEX_KB(n) \
+ (HasEVEX_KZ && HasEVEX_B \
+ ? n##_KZ_B \
+ : (HasEVEX_K && HasEVEX_B \
+ ? n##_K_B \
+ : (HasEVEX_KZ ? n##_KZ \
+ : (HasEVEX_K ? n##_K : (HasEVEX_B ? n##_B : n)))))
InstructionContext RecognizableInstr::insnContext() const {
InstructionContext insnContext;
@@ -243,8 +249,7 @@ InstructionContext RecognizableInstr::insnContext() const {
errs() << "Instruction does not use a prefix: " << Name << "\n";
llvm_unreachable("Invalid prefix");
}
- }
- else if (HasREX_W) {
+ } else if (HasREX_W) {
// VEX_W
if (OpPrefix == X86Local::PD)
insnContext = EVEX_KB(IC_EVEX_W_OPSIZE);
@@ -340,6 +345,8 @@ InstructionContext RecognizableInstr::insnContext() const {
insnContext = IC_64BIT_XD;
else if (OpPrefix == X86Local::XS)
insnContext = IC_64BIT_XS;
+ else if (ExplicitREX2Prefix)
+ insnContext = IC_64BIT_REX2;
else if (HasREX_W)
insnContext = IC_64BIT_REXW;
else
@@ -376,9 +383,9 @@ void RecognizableInstr::adjustOperandEncoding(OperandEncoding &encoding) {
// The scaling factor for AVX512 compressed displacement encoding is an
// instruction attribute. Adjust the ModRM encoding type to include the
// scale for compressed displacement.
- if ((encoding != ENCODING_RM &&
- encoding != ENCODING_VSIB &&
- encoding != ENCODING_SIB) ||CD8_Scale == 0)
+ if ((encoding != ENCODING_RM && encoding != ENCODING_VSIB &&
+ encoding != ENCODING_SIB) ||
+ CD8_Scale == 0)
return;
encoding = (OperandEncoding)(encoding + Log2_32(CD8_Scale));
assert(((encoding >= ENCODING_RM && encoding <= ENCODING_RM_CD64) ||
@@ -387,13 +394,11 @@ void RecognizableInstr::adjustOperandEncoding(OperandEncoding &encoding) {
"Invalid CDisp scaling");
}
-void RecognizableInstr::handleOperand(bool optional, unsigned &operandIndex,
- unsigned &physicalOperandIndex,
- unsigned numPhysicalOperands,
- const unsigned *operandMapping,
- OperandEncoding (*encodingFromString)
- (const std::string&,
- uint8_t OpSize)) {
+void RecognizableInstr::handleOperand(
+ bool optional, unsigned &operandIndex, unsigned &physicalOperandIndex,
+ unsigned numPhysicalOperands, const unsigned *operandMapping,
+ OperandEncoding (*encodingFromString)(const std::string &,
+ uint8_t OpSize)) {
if (optional) {
if (physicalOperandIndex >= numPhysicalOperands)
return;
@@ -404,7 +409,7 @@ void RecognizableInstr::handleOperand(bool optional, unsigned &operandIndex,
while (operandMapping[operandIndex] != operandIndex) {
Spec->operands[operandIndex].encoding = ENCODING_DUP;
Spec->operands[operandIndex].type =
- (OperandType)(TYPE_DUP0 + operandMapping[operandIndex]);
+ (OperandType)(TYPE_DUP0 + operandMapping[operandIndex]);
++operandIndex;
}
@@ -422,7 +427,7 @@ void RecognizableInstr::handleOperand(bool optional, unsigned &operandIndex,
}
void RecognizableInstr::emitInstructionSpecifier() {
- Spec->name = Name;
+ Spec->name = Name;
Spec->insnContext = insnContext();
@@ -434,12 +439,13 @@ void RecognizableInstr::emitInstructionSpecifier() {
// operandMapping maps from operands in OperandList to their originals.
// If operandMapping[i] != i, then the entry is a duplicate.
unsigned operandMapping[X86_MAX_OPERANDS];
- assert(numOperands <= X86_MAX_OPERANDS && "X86_MAX_OPERANDS is not large enough");
+ assert(numOperands <= X86_MAX_OPERANDS &&
+ "X86_MAX_OPERANDS is not large enough");
for (unsigned operandIndex = 0; operandIndex < numOperands; ++operandIndex) {
if (!OperandList[operandIndex].Constraints.empty()) {
const CGIOperandList::ConstraintInfo &Constraint =
- OperandList[operandIndex].Constraints[0];
+ OperandList[operandIndex].Constraints[0];
if (Constraint.isTied()) {
operandMapping[operandIndex] = operandIndex;
operandMapping[Constraint.getTiedOperand()] = operandIndex;
@@ -453,21 +459,14 @@ void RecognizableInstr::emitInstructionSpecifier() {
}
}
-#define HANDLE_OPERAND(class) \
- handleOperand(false, \
- operandIndex, \
- physicalOperandIndex, \
- numPhysicalOperands, \
- operandMapping, \
+#define HANDLE_OPERAND(class) \
+ handleOperand(false, operandIndex, physicalOperandIndex, \
+ numPhysicalOperands, operandMapping, \
class##EncodingFromString);
-#define HANDLE_OPTIONAL(class) \
- handleOperand(true, \
- operandIndex, \
- physicalOperandIndex, \
- numPhysicalOperands, \
- operandMapping, \
- class##EncodingFromString);
+#define HANDLE_OPTIONAL(class) \
+ handleOperand(true, operandIndex, physicalOperandIndex, numPhysicalOperands, \
+ operandMapping, class##EncodingFromString);
// operandIndex should always be < numOperands
unsigned operandIndex = 0;
@@ -485,7 +484,8 @@ void RecognizableInstr::emitInstructionSpecifier() {
#endif
switch (Form) {
- default: llvm_unreachable("Unhandled form");
+ default:
+ llvm_unreachable("Unhandled form");
case X86Local::PrefixByte:
return;
case X86Local::RawFrmSrc:
@@ -762,7 +762,7 @@ void RecognizableInstr::emitInstructionSpecifier() {
case X86Local::MRM6X:
case X86Local::MRM7X:
#define MAP(from, to) case X86Local::MRM_##from:
- X86_INSTR_MRM_MAPPING
+ X86_INSTR_MRM_MAPPING
#undef MAP
HANDLE_OPTIONAL(relocation)
break;
@@ -775,28 +775,56 @@ void RecognizableInstr::emitInstructionSpecifier() {
void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const {
// Special cases where the LLVM tables are not complete
-#define MAP(from, to) \
- case X86Local::MRM_##from:
+#define MAP(from, to) case X86Local::MRM_##from:
std::optional<OpcodeType> opcodeType;
switch (OpMap) {
- default: llvm_unreachable("Invalid map!");
- case X86Local::OB: opcodeType = ONEBYTE; break;
- case X86Local::TB: opcodeType = TWOBYTE; break;
- case X86Local::T8: opcodeType = THREEBYTE_38; break;
- case X86Local::TA: opcodeType = THREEBYTE_3A; break;
- case X86Local::XOP8: opcodeType = XOP8_MAP; break;
- case X86Local::XOP9: opcodeType = XOP9_MAP; break;
- case X86Local::XOPA: opcodeType = XOPA_MAP; break;
- case X86Local::ThreeDNow: opcodeType = THREEDNOW_MAP; break;
- case X86Local::T_MAP5: opcodeType = MAP5; break;
- case X86Local::T_MAP6: opcodeType = MAP6; break;
+ default:
+ llvm_unreachable("Invalid map!");
+ case X86Local::OB:
+ opcodeType = ONEBYTE;
+ break;
+ case X86Local::TB:
+ opcodeType = TWOBYTE;
+ break;
+ case X86Local::T8:
+ opcodeType = THREEBYTE_38;
+ break;
+ case X86Local::TA:
+ opcodeType = THREEBYTE_3A;
+ break;
+ case X86Local::XOP8:
+ opcodeType = XOP8_MAP;
+ break;
+ case X86Local::XOP9:
+ opcodeType = XOP9_MAP;
+ break;
+ case X86Local::XOPA:
+ opcodeType = XOPA_MAP;
+ break;
+ case X86Local::ThreeDNow:
+ opcodeType = THREEDNOW_MAP;
+ break;
+ case X86Local::T_MAP4:
+ opcodeType = MAP4;
+ break;
+ case X86Local::T_MAP5:
+ opcodeType = MAP5;
+ break;
+ case X86Local::T_MAP6:
+ opcodeType = MAP6;
+ break;
+ case X86Local::T_MAP7:
+ opcodeType = MAP7;
+ break;
}
std::unique_ptr<ModRMFilter> filter;
switch (Form) {
- default: llvm_unreachable("Invalid form!");
- case X86Local::Pseudo: llvm_unreachable("Pseudo should not be emitted!");
+ default:
+ llvm_unreachable("Invalid form!");
+ case X86Local::Pseudo:
+ llvm_unreachable("Pseudo should not be emitted!");
case X86Local::RawFrm:
case X86Local::AddRegFrm:
case X86Local::RawFrmMemOffs:
@@ -830,28 +858,40 @@ void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const {
case X86Local::MRMXm:
filter = std::make_unique<ModFilter>(false);
break;
- case X86Local::MRM0r: case X86Local::MRM1r:
- case X86Local::MRM2r: case X86Local::MRM3r:
- case X86Local::MRM4r: case X86Local::MRM5r:
- case X86Local::MRM6r: case X86Local::MRM7r:
+ case X86Local::MRM0r:
+ case X86Local::MRM1r:
+ case X86Local::MRM2r:
+ case X86Local::MRM3r:
+ case X86Local::MRM4r:
+ case X86Local::MRM5r:
+ case X86Local::MRM6r:
+ case X86Local::MRM7r:
filter = std::make_unique<ExtendedFilter>(true, Form - X86Local::MRM0r);
break;
- case X86Local::MRM0X: case X86Local::MRM1X:
- case X86Local::MRM2X: case X86Local::MRM3X:
- case X86Local::MRM4X: case X86Local::MRM5X:
- case X86Local::MRM6X: case X86Local::MRM7X:
+ case X86Local::MRM0X:
+ case X86Local::MRM1X:
+ case X86Local::MRM2X:
+ case X86Local::MRM3X:
+ case X86Local::MRM4X:
+ case X86Local::MRM5X:
+ case X86Local::MRM6X:
+ case X86Local::MRM7X:
filter = std::make_unique<ExtendedFilter>(true, Form - X86Local::MRM0X);
break;
case X86Local::MRMr0:
filter = std::make_unique<ExtendedRMFilter>(true, Form - X86Local::MRMr0);
break;
- case X86Local::MRM0m: case X86Local::MRM1m:
- case X86Local::MRM2m: case X86Local::MRM3m:
- case X86Local::MRM4m: case X86Local::MRM5m:
- case X86Local::MRM6m: case X86Local::MRM7m:
+ case X86Local::MRM0m:
+ case X86Local::MRM1m:
+ case X86Local::MRM2m:
+ case X86Local::MRM3m:
+ case X86Local::MRM4m:
+ case X86Local::MRM5m:
+ case X86Local::MRM6m:
+ case X86Local::MRM7m:
filter = std::make_unique<ExtendedFilter>(false, Form - X86Local::MRM0m);
break;
- X86_INSTR_MRM_MAPPING
+ X86_INSTR_MRM_MAPPING
filter = std::make_unique<ExactFilter>(0xC0 + Form - X86Local::MRM_C0);
break;
} // switch (Form)
@@ -860,9 +900,15 @@ void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const {
unsigned AddressSize = 0;
switch (AdSize) {
- case X86Local::AdSize16: AddressSize = 16; break;
- case X86Local::AdSize32: AddressSize = 32; break;
- case X86Local::AdSize64: AddressSize = 64; break;
+ case X86Local::AdSize16:
+ AddressSize = 16;
+ break;
+ case X86Local::AdSize32:
+ AddressSize = 32;
+ break;
+ case X86Local::AdSize64:
+ AddressSize = 64;
+ break;
}
assert(opcodeType && "Opcode type not set");
@@ -881,8 +927,7 @@ void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const {
currentOpcode < (uint8_t)(opcodeToSet + Count); ++currentOpcode)
tables.setTableFields(*opcodeType, insnContext(), currentOpcode, *filter,
UID, Is32Bit, OpPrefix == 0,
- IgnoresVEX_L || EncodeRC,
- IgnoresW, AddressSize);
+ IgnoresVEX_L || EncodeRC, IgnoresW, AddressSize);
} else {
tables.setTableFields(*opcodeType, insnContext(), opcodeToSet, *filter, UID,
Is32Bit, OpPrefix == 0, IgnoresVEX_L || EncodeRC,
@@ -892,187 +937,190 @@ void RecognizableInstr::emitDecodePath(DisassemblerTables &tables) const {
#undef MAP
}
-#define TYPE(str, type) if (s == str) return type;
+#define TYPE(str, type) \
+ if (s == str) \
+ return type;
OperandType RecognizableInstr::typeFromString(const std::string &s,
- bool hasREX_W,
- uint8_t OpSize) {
- if(hasREX_W) {
+ bool hasREX_W, uint8_t OpSize) {
+ if (hasREX_W) {
// For instructions with a REX_W prefix, a declared 32-bit register encoding
// is special.
- TYPE("GR32", TYPE_R32)
+ TYPE("GR32", TYPE_R32)
}
- if(OpSize == X86Local::OpSize16) {
+ if (OpSize == X86Local::OpSize16) {
// For OpSize16 instructions, a declared 16-bit register or
// immediate encoding is special.
- TYPE("GR16", TYPE_Rv)
- } else if(OpSize == X86Local::OpSize32) {
+ TYPE("GR16", TYPE_Rv)
+ } else if (OpSize == X86Local::OpSize32) {
// For OpSize32 instructions, a declared 32-bit register or
// immediate encoding is special.
- TYPE("GR32", TYPE_Rv)
+ TYPE("GR32", TYPE_Rv)
}
- TYPE("i16mem", TYPE_M)
- TYPE("i16imm", TYPE_IMM)
- TYPE("i16i8imm", TYPE_IMM)
- TYPE("GR16", TYPE_R16)
- TYPE("GR16orGR32orGR64", TYPE_R16)
- TYPE("i32mem", TYPE_M)
- TYPE("i32imm", TYPE_IMM)
- TYPE("i32i8imm", TYPE_IMM)
- TYPE("GR32", TYPE_R32)
- TYPE("GR32orGR64", TYPE_R32)
- TYPE("i64mem", TYPE_M)
- TYPE("i64i32imm", TYPE_IMM)
- TYPE("i64i8imm", TYPE_IMM)
- TYPE("GR64", TYPE_R64)
- TYPE("i8mem", TYPE_M)
- TYPE("i8imm", TYPE_IMM)
- TYPE("u4imm", TYPE_UIMM8)
- TYPE("u8imm", TYPE_UIMM8)
- TYPE("i16u8imm", TYPE_UIMM8)
- TYPE("i32u8imm", TYPE_UIMM8)
- TYPE("i64u8imm", TYPE_UIMM8)
- TYPE("GR8", TYPE_R8)
- TYPE("VR128", TYPE_XMM)
- TYPE("VR128X", TYPE_XMM)
- TYPE("f128mem", TYPE_M)
- TYPE("f256mem", TYPE_M)
- TYPE("f512mem", TYPE_M)
- TYPE("FR128", TYPE_XMM)
- TYPE("FR64", TYPE_XMM)
- TYPE("FR64X", TYPE_XMM)
- TYPE("f64mem", TYPE_M)
- TYPE("sdmem", TYPE_M)
- TYPE("FR16X", TYPE_XMM)
- TYPE("FR32", TYPE_XMM)
- TYPE("FR32X", TYPE_XMM)
- TYPE("f32mem", TYPE_M)
- TYPE("f16mem", TYPE_M)
- TYPE("ssmem", TYPE_M)
- TYPE("shmem", TYPE_M)
- TYPE("RST", TYPE_ST)
- TYPE("RSTi", TYPE_ST)
- TYPE("i128mem", TYPE_M)
- TYPE("i256mem", TYPE_M)
- TYPE("i512mem", TYPE_M)
- TYPE("i512mem_GR16", TYPE_M)
- TYPE("i512mem_GR32", TYPE_M)
- TYPE("i512mem_GR64", TYPE_M)
- TYPE("i64i32imm_brtarget", TYPE_REL)
- TYPE("i16imm_brtarget", TYPE_REL)
- TYPE("i32imm_brtarget", TYPE_REL)
- TYPE("ccode", TYPE_IMM)
- TYPE("AVX512RC", TYPE_IMM)
- TYPE("brtarget32", TYPE_REL)
- TYPE("brtarget16", TYPE_REL)
- TYPE("brtarget8", TYPE_REL)
- TYPE("f80mem", TYPE_M)
- TYPE("lea64_32mem", TYPE_M)
- TYPE("lea64mem", TYPE_M)
- TYPE("VR64", TYPE_MM64)
- TYPE("i64imm", TYPE_IMM)
- TYPE("anymem", TYPE_M)
- TYPE("opaquemem", TYPE_M)
- TYPE("sibmem", TYPE_MSIB)
- TYPE("SEGMENT_REG", TYPE_SEGMENTREG)
- TYPE("DEBUG_REG", TYPE_DEBUGREG)
- TYPE("CONTROL_REG", TYPE_CONTROLREG)
- TYPE("srcidx8", TYPE_SRCIDX)
- TYPE("srcidx16", TYPE_SRCIDX)
- TYPE("srcidx32", TYPE_SRCIDX)
- TYPE("srcidx64", TYPE_SRCIDX)
- TYPE("dstidx8", TYPE_DSTIDX)
- TYPE("dstidx16", TYPE_DSTIDX)
- TYPE("dstidx32", TYPE_DSTIDX)
- TYPE("dstidx64", TYPE_DSTIDX)
- TYPE("offset16_8", TYPE_MOFFS)
- TYPE("offset16_16", TYPE_MOFFS)
- TYPE("offset16_32", TYPE_MOFFS)
- TYPE("offset32_8", TYPE_MOFFS)
- TYPE("offset32_16", TYPE_MOFFS)
- TYPE("offset32_32", TYPE_MOFFS)
- TYPE("offset32_64", TYPE_MOFFS)
- TYPE("offset64_8", TYPE_MOFFS)
- TYPE("offset64_16", TYPE_MOFFS)
- TYPE("offset64_32", TYPE_MOFFS)
- TYPE("offset64_64", TYPE_MOFFS)
- TYPE("VR256", TYPE_YMM)
- TYPE("VR256X", TYPE_YMM)
- TYPE("VR512", TYPE_ZMM)
- TYPE("VK1", TYPE_VK)
- TYPE("VK1WM", TYPE_VK)
- TYPE("VK2", TYPE_VK)
- TYPE("VK2WM", TYPE_VK)
- TYPE("VK4", TYPE_VK)
- TYPE("VK4WM", TYPE_VK)
- TYPE("VK8", TYPE_VK)
- TYPE("VK8WM", TYPE_VK)
- TYPE("VK16", TYPE_VK)
- TYPE("VK16WM", TYPE_VK)
- TYPE("VK32", TYPE_VK)
- TYPE("VK32WM", TYPE_VK)
- TYPE("VK64", TYPE_VK)
- TYPE("VK64WM", TYPE_VK)
- TYPE("VK1Pair", TYPE_VK_PAIR)
- TYPE("VK2Pair", TYPE_VK_PAIR)
- TYPE("VK4Pair", TYPE_VK_PAIR)
- TYPE("VK8Pair", TYPE_VK_PAIR)
- TYPE("VK16Pair", TYPE_VK_PAIR)
- TYPE("vx64mem", TYPE_MVSIBX)
- TYPE("vx128mem", TYPE_MVSIBX)
- TYPE("vx256mem", TYPE_MVSIBX)
- TYPE("vy128mem", TYPE_MVSIBY)
- TYPE("vy256mem", TYPE_MVSIBY)
- TYPE("vx64xmem", TYPE_MVSIBX)
- TYPE("vx128xmem", TYPE_MVSIBX)
- TYPE("vx256xmem", TYPE_MVSIBX)
- TYPE("vy128xmem", TYPE_MVSIBY)
- TYPE("vy256xmem", TYPE_MVSIBY)
- TYPE("vy512xmem", TYPE_MVSIBY)
- TYPE("vz256mem", TYPE_MVSIBZ)
- TYPE("vz512mem", TYPE_MVSIBZ)
- TYPE("BNDR", TYPE_BNDR)
- TYPE("TILE", TYPE_TMM)
+ TYPE("i16mem", TYPE_M)
+ TYPE("i16imm", TYPE_IMM)
+ TYPE("i16i8imm", TYPE_IMM)
+ TYPE("GR16", TYPE_R16)
+ TYPE("GR16orGR32orGR64", TYPE_R16)
+ TYPE("i32mem", TYPE_M)
+ TYPE("i32imm", TYPE_IMM)
+ TYPE("i32i8imm", TYPE_IMM)
+ TYPE("GR32", TYPE_R32)
+ TYPE("GR32orGR64", TYPE_R32)
+ TYPE("i64mem", TYPE_M)
+ TYPE("i64i32imm", TYPE_IMM)
+ TYPE("i64i8imm", TYPE_IMM)
+ TYPE("GR64", TYPE_R64)
+ TYPE("i8mem", TYPE_M)
+ TYPE("i8imm", TYPE_IMM)
+ TYPE("u4imm", TYPE_UIMM8)
+ TYPE("u8imm", TYPE_UIMM8)
+ TYPE("i16u8imm", TYPE_UIMM8)
+ TYPE("i32u8imm", TYPE_UIMM8)
+ TYPE("i64u8imm", TYPE_UIMM8)
+ TYPE("GR8", TYPE_R8)
+ TYPE("VR128", TYPE_XMM)
+ TYPE("VR128X", TYPE_XMM)
+ TYPE("f128mem", TYPE_M)
+ TYPE("f256mem", TYPE_M)
+ TYPE("f512mem", TYPE_M)
+ TYPE("FR128", TYPE_XMM)
+ TYPE("FR64", TYPE_XMM)
+ TYPE("FR64X", TYPE_XMM)
+ TYPE("f64mem", TYPE_M)
+ TYPE("sdmem", TYPE_M)
+ TYPE("FR16X", TYPE_XMM)
+ TYPE("FR32", TYPE_XMM)
+ TYPE("FR32X", TYPE_XMM)
+ TYPE("f32mem", TYPE_M)
+ TYPE("f16mem", TYPE_M)
+ TYPE("ssmem", TYPE_M)
+ TYPE("shmem", TYPE_M)
+ TYPE("RST", TYPE_ST)
+ TYPE("RSTi", TYPE_ST)
+ TYPE("i128mem", TYPE_M)
+ TYPE("i256mem", TYPE_M)
+ TYPE("i512mem", TYPE_M)
+ TYPE("i512mem_GR16", TYPE_M)
+ TYPE("i512mem_GR32", TYPE_M)
+ TYPE("i512mem_GR64", TYPE_M)
+ TYPE("i64i32imm_brtarget", TYPE_REL)
+ TYPE("i16imm_brtarget", TYPE_REL)
+ TYPE("i32imm_brtarget", TYPE_REL)
+ TYPE("ccode", TYPE_IMM)
+ TYPE("AVX512RC", TYPE_IMM)
+ TYPE("brtarget32", TYPE_REL)
+ TYPE("brtarget16", TYPE_REL)
+ TYPE("brtarget8", TYPE_REL)
+ TYPE("f80mem", TYPE_M)
+ TYPE("lea64_32mem", TYPE_M)
+ TYPE("lea64mem", TYPE_M)
+ TYPE("VR64", TYPE_MM64)
+ TYPE("i64imm", TYPE_IMM)
+ TYPE("anymem", TYPE_M)
+ TYPE("opaquemem", TYPE_M)
+ TYPE("sibmem", TYPE_MSIB)
+ TYPE("SEGMENT_REG", TYPE_SEGMENTREG)
+ TYPE("DEBUG_REG", TYPE_DEBUGREG)
+ TYPE("CONTROL_REG", TYPE_CONTROLREG)
+ TYPE("srcidx8", TYPE_SRCIDX)
+ TYPE("srcidx16", TYPE_SRCIDX)
+ TYPE("srcidx32", TYPE_SRCIDX)
+ TYPE("srcidx64", TYPE_SRCIDX)
+ TYPE("dstidx8", TYPE_DSTIDX)
+ TYPE("dstidx16", TYPE_DSTIDX)
+ TYPE("dstidx32", TYPE_DSTIDX)
+ TYPE("dstidx64", TYPE_DSTIDX)
+ TYPE("offset16_8", TYPE_MOFFS)
+ TYPE("offset16_16", TYPE_MOFFS)
+ TYPE("offset16_32", TYPE_MOFFS)
+ TYPE("offset32_8", TYPE_MOFFS)
+ TYPE("offset32_16", TYPE_MOFFS)
+ TYPE("offset32_32", TYPE_MOFFS)
+ TYPE("offset32_64", TYPE_MOFFS)
+ TYPE("offset64_8", TYPE_MOFFS)
+ TYPE("offset64_16", TYPE_MOFFS)
+ TYPE("offset64_32", TYPE_MOFFS)
+ TYPE("offset64_64", TYPE_MOFFS)
+ TYPE("VR256", TYPE_YMM)
+ TYPE("VR256X", TYPE_YMM)
+ TYPE("VR512", TYPE_ZMM)
+ TYPE("VK1", TYPE_VK)
+ TYPE("VK1WM", TYPE_VK)
+ TYPE("VK2", TYPE_VK)
+ TYPE("VK2WM", TYPE_VK)
+ TYPE("VK4", TYPE_VK)
+ TYPE("VK4WM", TYPE_VK)
+ TYPE("VK8", TYPE_VK)
+ TYPE("VK8WM", TYPE_VK)
+ TYPE("VK16", TYPE_VK)
+ TYPE("VK16WM", TYPE_VK)
+ TYPE("VK32", TYPE_VK)
+ TYPE("VK32WM", TYPE_VK)
+ TYPE("VK64", TYPE_VK)
+ TYPE("VK64WM", TYPE_VK)
+ TYPE("VK1Pair", TYPE_VK_PAIR)
+ TYPE("VK2Pair", TYPE_VK_PAIR)
+ TYPE("VK4Pair", TYPE_VK_PAIR)
+ TYPE("VK8Pair", TYPE_VK_PAIR)
+ TYPE("VK16Pair", TYPE_VK_PAIR)
+ TYPE("vx64mem", TYPE_MVSIBX)
+ TYPE("vx128mem", TYPE_MVSIBX)
+ TYPE("vx256mem", TYPE_MVSIBX)
+ TYPE("vy128mem", TYPE_MVSIBY)
+ TYPE("vy256mem", TYPE_MVSIBY)
+ TYPE("vx64xmem", TYPE_MVSIBX)
+ TYPE("vx128xmem", TYPE_MVSIBX)
+ TYPE("vx256xmem", TYPE_MVSIBX)
+ TYPE("vy128xmem", TYPE_MVSIBY)
+ TYPE("vy256xmem", TYPE_MVSIBY)
+ TYPE("vy512xmem", TYPE_MVSIBY)
+ TYPE("vz256mem", TYPE_MVSIBZ)
+ TYPE("vz512mem", TYPE_MVSIBZ)
+ TYPE("BNDR", TYPE_BNDR)
+ TYPE("TILE", TYPE_TMM)
errs() << "Unhandled type string " << s << "\n";
llvm_unreachable("Unhandled type string");
}
#undef TYPE
-#define ENCODING(str, encoding) if (s == str) return encoding;
+#define ENCODING(str, encoding) \
+ if (s == str) \
+ return encoding;
OperandEncoding
RecognizableInstr::immediateEncodingFromString(const std::string &s,
uint8_t OpSize) {
- if(OpSize != X86Local::OpSize16) {
+ if (OpSize != X86Local::OpSize16) {
// For instructions without an OpSize prefix, a declared 16-bit register or
// immediate encoding is special.
- ENCODING("i16imm", ENCODING_IW)
+ ENCODING("i16imm", ENCODING_IW)
}
- ENCODING("i32i8imm", ENCODING_IB)
- ENCODING("AVX512RC", ENCODING_IRC)
- ENCODING("i16imm", ENCODING_Iv)
- ENCODING("i16i8imm", ENCODING_IB)
- ENCODING("i32imm", ENCODING_Iv)
- ENCODING("i64i32imm", ENCODING_ID)
- ENCODING("i64i8imm", ENCODING_IB)
- ENCODING("i8imm", ENCODING_IB)
- ENCODING("u4imm", ENCODING_IB)
- ENCODING("u8imm", ENCODING_IB)
- ENCODING("i16u8imm", ENCODING_IB)
- ENCODING("i32u8imm", ENCODING_IB)
- ENCODING("i64u8imm", ENCODING_IB)
+ ENCODING("i32i8imm", ENCODING_IB)
+ ENCODING("AVX512RC", ENCODING_IRC)
+ ENCODING("i16imm", ENCODING_Iv)
+ ENCODING("i16i8imm", ENCODING_IB)
+ ENCODING("i32imm", ENCODING_Iv)
+ ENCODING("i64i32imm", ENCODING_ID)
+ ENCODING("i64i8imm", ENCODING_IB)
+ ENCODING("i8imm", ENCODING_IB)
+ ENCODING("u4imm", ENCODING_IB)
+ ENCODING("u8imm", ENCODING_IB)
+ ENCODING("i16u8imm", ENCODING_IB)
+ ENCODING("i32u8imm", ENCODING_IB)
+ ENCODING("i64u8imm", ENCODING_IB)
// This is not a typo. Instructions like BLENDVPD put
// register IDs in 8-bit immediates nowadays.
- ENCODING("FR32", ENCODING_IB)
- ENCODING("FR64", ENCODING_IB)
- ENCODING("FR128", ENCODING_IB)
- ENCODING("VR128", ENCODING_IB)
- ENCODING("VR256", ENCODING_IB)
- ENCODING("FR16X", ENCODING_IB)
- ENCODING("FR32X", ENCODING_IB)
- ENCODING("FR64X", ENCODING_IB)
- ENCODING("VR128X", ENCODING_IB)
- ENCODING("VR256X", ENCODING_IB)
- ENCODING("VR512", ENCODING_IB)
- ENCODING("TILE", ENCODING_IB)
+ ENCODING("FR32", ENCODING_IB)
+ ENCODING("FR64", ENCODING_IB)
+ ENCODING("FR128", ENCODING_IB)
+ ENCODING("VR128", ENCODING_IB)
+ ENCODING("VR256", ENCODING_IB)
+ ENCODING("FR16X", ENCODING_IB)
+ ENCODING("FR32X", ENCODING_IB)
+ ENCODING("FR64X", ENCODING_IB)
+ ENCODING("VR128X", ENCODING_IB)
+ ENCODING("VR256X", ENCODING_IB)
+ ENCODING("VR512", ENCODING_IB)
+ ENCODING("TILE", ENCODING_IB)
errs() << "Unhandled immediate encoding " << s << "\n";
llvm_unreachable("Unhandled immediate encoding");
}
@@ -1080,35 +1128,35 @@ RecognizableInstr::immediateEncodingFromString(const std::string &s,
OperandEncoding
RecognizableInstr::rmRegisterEncodingFromString(const std::string &s,
uint8_t OpSize) {
- ENCODING("RST", ENCODING_FP)
- ENCODING("RSTi", ENCODING_FP)
- ENCODING("GR16", ENCODING_RM)
- ENCODING("GR16orGR32orGR64",ENCODING_RM)
- ENCODING("GR32", ENCODING_RM)
- ENCODING("GR32orGR64", ENCODING_RM)
- ENCODING("GR64", ENCODING_RM)
- ENCODING("GR8", ENCODING_RM)
- ENCODING("VR128", ENCODING_RM)
- ENCODING("VR128X", ENCODING_RM)
- ENCODING("FR128", ENCODING_RM)
- ENCODING("FR64", ENCODING_RM)
- ENCODING("FR32", ENCODING_RM)
- ENCODING("FR64X", ENCODING_RM)
- ENCODING("FR32X", ENCODING_RM)
- ENCODING("FR16X", ENCODING_RM)
- ENCODING("VR64", ENCODING_RM)
- ENCODING("VR256", ENCODING_RM)
- ENCODING("VR256X", ENCODING_RM)
- ENCODING("VR512", ENCODING_RM)
- ENCODING("VK1", ENCODING_RM)
- ENCODING("VK2", ENCODING_RM)
- ENCODING("VK4", ENCODING_RM)
- ENCODING("VK8", ENCODING_RM)
- ENCODING("VK16", ENCODING_RM)
- ENCODING("VK32", ENCODING_RM)
- ENCODING("VK64", ENCODING_RM)
- ENCODING("BNDR", ENCODING_RM)
- ENCODING("TILE", ENCODING_RM)
+ ENCODING("RST", ENCODING_FP)
+ ENCODING("RSTi", ENCODING_FP)
+ ENCODING("GR16", ENCODING_RM)
+ ENCODING("GR16orGR32orGR64", ENCODING_RM)
+ ENCODING("GR32", ENCODING_RM)
+ ENCODING("GR32orGR64", ENCODING_RM)
+ ENCODING("GR64", ENCODING_RM)
+ ENCODING("GR8", ENCODING_RM)
+ ENCODING("VR128", ENCODING_RM)
+ ENCODING("VR128X", ENCODING_RM)
+ ENCODING("FR128", ENCODING_RM)
+ ENCODING("FR64", ENCODING_RM)
+ ENCODING("FR32", ENCODING_RM)
+ ENCODING("FR64X", ENCODING_RM)
+ ENCODING("FR32X", ENCODING_RM)
+ ENCODING("FR16X", ENCODING_RM)
+ ENCODING("VR64", ENCODING_RM)
+ ENCODING("VR256", ENCODING_RM)
+ ENCODING("VR256X", ENCODING_RM)
+ ENCODING("VR512", ENCODING_RM)
+ ENCODING("VK1", ENCODING_RM)
+ ENCODING("VK2", ENCODING_RM)
+ ENCODING("VK4", ENCODING_RM)
+ ENCODING("VK8", ENCODING_RM)
+ ENCODING("VK16", ENCODING_RM)
+ ENCODING("VK32", ENCODING_RM)
+ ENCODING("VK64", ENCODING_RM)
+ ENCODING("BNDR", ENCODING_RM)
+ ENCODING("TILE", ENCODING_RM)
errs() << "Unhandled R/M register encoding " << s << "\n";
llvm_unreachable("Unhandled R/M register encoding");
}
@@ -1116,48 +1164,48 @@ RecognizableInstr::rmRegisterEncodingFromString(const std::string &s,
OperandEncoding
RecognizableInstr::roRegisterEncodingFromString(const std::string &s,
uint8_t OpSize) {
- ENCODING("GR16", ENCODING_REG)
- ENCODING("GR16orGR32orGR64",ENCODING_REG)
- ENCODING("GR32", ENCODING_REG)
- ENCODING("GR32orGR64", ENCODING_REG)
- ENCODING("GR64", ENCODING_REG)
- ENCODING("GR8", ENCODING_REG)
- ENCODING("VR128", ENCODING_REG)
- ENCODING("FR128", ENCODING_REG)
- ENCODING("FR64", ENCODING_REG)
- ENCODING("FR32", ENCODING_REG)
- ENCODING("VR64", ENCODING_REG)
- ENCODING("SEGMENT_REG", ENCODING_REG)
- ENCODING("DEBUG_REG", ENCODING_REG)
- ENCODING("CONTROL_REG", ENCODING_REG)
- ENCODING("VR256", ENCODING_REG)
- ENCODING("VR256X", ENCODING_REG)
- ENCODING("VR128X", ENCODING_REG)
- ENCODING("FR64X", ENCODING_REG)
- ENCODING("FR32X", ENCODING_REG)
- ENCODING("FR16X", ENCODING_REG)
- ENCODING("VR512", ENCODING_REG)
- ENCODING("VK1", ENCODING_REG)
- ENCODING("VK2", ENCODING_REG)
- ENCODING("VK4", ENCODING_REG)
- ENCODING("VK8", ENCODING_REG)
- ENCODING("VK16", ENCODING_REG)
- ENCODING("VK32", ENCODING_REG)
- ENCODING("VK64", ENCODING_REG)
- ENCODING("VK1Pair", ENCODING_REG)
- ENCODING("VK2Pair", ENCODING_REG)
- ENCODING("VK4Pair", ENCODING_REG)
- ENCODING("VK8Pair", ENCODING_REG)
- ENCODING("VK16Pair", ENCODING_REG)
- ENCODING("VK1WM", ENCODING_REG)
- ENCODING("VK2WM", ENCODING_REG)
- ENCODING("VK4WM", ENCODING_REG)
- ENCODING("VK8WM", ENCODING_REG)
- ENCODING("VK16WM", ENCODING_REG)
- ENCODING("VK32WM", ENCODING_REG)
- ENCODING("VK64WM", ENCODING_REG)
- ENCODING("BNDR", ENCODING_REG)
- ENCODING("TILE", ENCODING_REG)
+ ENCODING("GR16", ENCODING_REG)
+ ENCODING("GR16orGR32orGR64", ENCODING_REG)
+ ENCODING("GR32", ENCODING_REG)
+ ENCODING("GR32orGR64", ENCODING_REG)
+ ENCODING("GR64", ENCODING_REG)
+ ENCODING("GR8", ENCODING_REG)
+ ENCODING("VR128", ENCODING_REG)
+ ENCODING("FR128", ENCODING_REG)
+ ENCODING("FR64", ENCODING_REG)
+ ENCODING("FR32", ENCODING_REG)
+ ENCODING("VR64", ENCODING_REG)
+ ENCODING("SEGMENT_REG", ENCODING_REG)
+ ENCODING("DEBUG_REG", ENCODING_REG)
+ ENCODING("CONTROL_REG", ENCODING_REG)
+ ENCODING("VR256", ENCODING_REG)
+ ENCODING("VR256X", ENCODING_REG)
+ ENCODING("VR128X", ENCODING_REG)
+ ENCODING("FR64X", ENCODING_REG)
+ ENCODING("FR32X", ENCODING_REG)
+ ENCODING("FR16X", ENCODING_REG)
+ ENCODING("VR512", ENCODING_REG)
+ ENCODING("VK1", ENCODING_REG)
+ ENCODING("VK2", ENCODING_REG)
+ ENCODING("VK4", ENCODING_REG)
+ ENCODING("VK8", ENCODING_REG)
+ ENCODING("VK16", ENCODING_REG)
+ ENCODING("VK32", ENCODING_REG)
+ ENCODING("VK64", ENCODING_REG)
+ ENCODING("VK1Pair", ENCODING_REG)
+ ENCODING("VK2Pair", ENCODING_REG)
+ ENCODING("VK4Pair", ENCODING_REG)
+ ENCODING("VK8Pair", ENCODING_REG)
+ ENCODING("VK16Pair", ENCODING_REG)
+ ENCODING("VK1WM", ENCODING_REG)
+ ENCODING("VK2WM", ENCODING_REG)
+ ENCODING("VK4WM", ENCODING_REG)
+ ENCODING("VK8WM", ENCODING_REG)
+ ENCODING("VK16WM", ENCODING_REG)
+ ENCODING("VK32WM", ENCODING_REG)
+ ENCODING("VK64WM", ENCODING_REG)
+ ENCODING("BNDR", ENCODING_REG)
+ ENCODING("TILE", ENCODING_REG)
errs() << "Unhandled reg/opcode register encoding " << s << "\n";
llvm_unreachable("Unhandled reg/opcode register encoding");
}
@@ -1165,27 +1213,27 @@ RecognizableInstr::roRegisterEncodingFromString(const std::string &s,
OperandEncoding
RecognizableInstr::vvvvRegisterEncodingFromString(const std::string &s,
uint8_t OpSize) {
- ENCODING("GR32", ENCODING_VVVV)
- ENCODING("GR64", ENCODING_VVVV)
- ENCODING("FR32", ENCODING_VVVV)
- ENCODING("FR128", ENCODING_VVVV)
- ENCODING("FR64", ENCODING_VVVV)
- ENCODING("VR128", ENCODING_VVVV)
- ENCODING("VR256", ENCODING_VVVV)
- ENCODING("FR16X", ENCODING_VVVV)
- ENCODING("FR32X", ENCODING_VVVV)
- ENCODING("FR64X", ENCODING_VVVV)
- ENCODING("VR128X", ENCODING_VVVV)
- ENCODING("VR256X", ENCODING_VVVV)
- ENCODING("VR512", ENCODING_VVVV)
- ENCODING("VK1", ENCODING_VVVV)
- ENCODING("VK2", ENCODING_VVVV)
- ENCODING("VK4", ENCODING_VVVV)
- ENCODING("VK8", ENCODING_VVVV)
- ENCODING("VK16", ENCODING_VVVV)
- ENCODING("VK32", ENCODING_VVVV)
- ENCODING("VK64", ENCODING_VVVV)
- ENCODING("TILE", ENCODING_VVVV)
+ ENCODING("GR32", ENCODING_VVVV)
+ ENCODING("GR64", ENCODING_VVVV)
+ ENCODING("FR32", ENCODING_VVVV)
+ ENCODING("FR128", ENCODING_VVVV)
+ ENCODING("FR64", ENCODING_VVVV)
+ ENCODING("VR128", ENCODING_VVVV)
+ ENCODING("VR256", ENCODING_VVVV)
+ ENCODING("FR16X", ENCODING_VVVV)
+ ENCODING("FR32X", ENCODING_VVVV)
+ ENCODING("FR64X", ENCODING_VVVV)
+ ENCODING("VR128X", ENCODING_VVVV)
+ ENCODING("VR256X", ENCODING_VVVV)
+ ENCODING("VR512", ENCODING_VVVV)
+ ENCODING("VK1", ENCODING_VVVV)
+ ENCODING("VK2", ENCODING_VVVV)
+ ENCODING("VK4", ENCODING_VVVV)
+ ENCODING("VK8", ENCODING_VVVV)
+ ENCODING("VK16", ENCODING_VVVV)
+ ENCODING("VK32", ENCODING_VVVV)
+ ENCODING("VK64", ENCODING_VVVV)
+ ENCODING("TILE", ENCODING_VVVV)
errs() << "Unhandled VEX.vvvv register encoding " << s << "\n";
llvm_unreachable("Unhandled VEX.vvvv register encoding");
}
@@ -1193,13 +1241,13 @@ RecognizableInstr::vvvvRegisterEncodingFromString(const std::string &s,
OperandEncoding
RecognizableInstr::writemaskRegisterEncodingFromString(const std::string &s,
uint8_t OpSize) {
- ENCODING("VK1WM", ENCODING_WRITEMASK)
- ENCODING("VK2WM", ENCODING_WRITEMASK)
- ENCODING("VK4WM", ENCODING_WRITEMASK)
- ENCODING("VK8WM", ENCODING_WRITEMASK)
- ENCODING("VK16WM", ENCODING_WRITEMASK)
- ENCODING("VK32WM", ENCODING_WRITEMASK)
- ENCODING("VK64WM", ENCODING_WRITEMASK)
+ ENCODING("VK1WM", ENCODING_WRITEMASK)
+ ENCODING("VK2WM", ENCODING_WRITEMASK)
+ ENCODING("VK4WM", ENCODING_WRITEMASK)
+ ENCODING("VK8WM", ENCODING_WRITEMASK)
+ ENCODING("VK16WM", ENCODING_WRITEMASK)
+ ENCODING("VK32WM", ENCODING_WRITEMASK)
+ ENCODING("VK64WM", ENCODING_WRITEMASK)
errs() << "Unhandled mask register encoding " << s << "\n";
llvm_unreachable("Unhandled mask register encoding");
}
@@ -1207,44 +1255,44 @@ RecognizableInstr::writemaskRegisterEncodingFromString(const std::string &s,
OperandEncoding
RecognizableInstr::memoryEncodingFromString(const std::string &s,
uint8_t OpSize) {
- ENCODING("i16mem", ENCODING_RM)
- ENCODING("i32mem", ENCODING_RM)
- ENCODING("i64mem", ENCODING_RM)
- ENCODING("i8mem", ENCODING_RM)
- ENCODING("shmem", ENCODING_RM)
- ENCODING("ssmem", ENCODING_RM)
- ENCODING("sdmem", ENCODING_RM)
- ENCODING("f128mem", ENCODING_RM)
- ENCODING("f256mem", ENCODING_RM)
- ENCODING("f512mem", ENCODING_RM)
- ENCODING("f64mem", ENCODING_RM)
- ENCODING("f32mem", ENCODING_RM)
- ENCODING("f16mem", ENCODING_RM)
- ENCODING("i128mem", ENCODING_RM)
- ENCODING("i256mem", ENCODING_RM)
- ENCODING("i512mem", ENCODING_RM)
- ENCODING("i512mem_GR16", ENCODING_RM)
- ENCODING("i512mem_GR32", ENCODING_RM)
- ENCODING("i512mem_GR64", ENCODING_RM)
- ENCODING("f80mem", ENCODING_RM)
- ENCODING("lea64_32mem", ENCODING_RM)
- ENCODING("lea64mem", ENCODING_RM)
- ENCODING("anymem", ENCODING_RM)
- ENCODING("opaquemem", ENCODING_RM)
- ENCODING("sibmem", ENCODING_SIB)
- ENCODING("vx64mem", ENCODING_VSIB)
- ENCODING("vx128mem", ENCODING_VSIB)
- ENCODING("vx256mem", ENCODING_VSIB)
- ENCODING("vy128mem", ENCODING_VSIB)
- ENCODING("vy256mem", ENCODING_VSIB)
- ENCODING("vx64xmem", ENCODING_VSIB)
- ENCODING("vx128xmem", ENCODING_VSIB)
- ENCODING("vx256xmem", ENCODING_VSIB)
- ENCODING("vy128xmem", ENCODING_VSIB)
- ENCODING("vy256xmem", ENCODING_VSIB)
- ENCODING("vy512xmem", ENCODING_VSIB)
- ENCODING("vz256mem", ENCODING_VSIB)
- ENCODING("vz512mem", ENCODING_VSIB)
+ ENCODING("i16mem", ENCODING_RM)
+ ENCODING("i32mem", ENCODING_RM)
+ ENCODING("i64mem", ENCODING_RM)
+ ENCODING("i8mem", ENCODING_RM)
+ ENCODING("shmem", ENCODING_RM)
+ ENCODING("ssmem", ENCODING_RM)
+ ENCODING("sdmem", ENCODING_RM)
+ ENCODING("f128mem", ENCODING_RM)
+ ENCODING("f256mem", ENCODING_RM)
+ ENCODING("f512mem", ENCODING_RM)
+ ENCODING("f64mem", ENCODING_RM)
+ ENCODING("f32mem", ENCODING_RM)
+ ENCODING("f16mem", ENCODING_RM)
+ ENCODING("i128mem", ENCODING_RM)
+ ENCODING("i256mem", ENCODING_RM)
+ ENCODING("i512mem", ENCODING_RM)
+ ENCODING("i512mem_GR16", ENCODING_RM)
+ ENCODING("i512mem_GR32", ENCODING_RM)
+ ENCODING("i512mem_GR64", ENCODING_RM)
+ ENCODING("f80mem", ENCODING_RM)
+ ENCODING("lea64_32mem", ENCODING_RM)
+ ENCODING("lea64mem", ENCODING_RM)
+ ENCODING("anymem", ENCODING_RM)
+ ENCODING("opaquemem", ENCODING_RM)
+ ENCODING("sibmem", ENCODING_SIB)
+ ENCODING("vx64mem", ENCODING_VSIB)
+ ENCODING("vx128mem", ENCODING_VSIB)
+ ENCODING("vx256mem", ENCODING_VSIB)
+ ENCODING("vy128mem", ENCODING_VSIB)
+ ENCODING("vy256mem", ENCODING_VSIB)
+ ENCODING("vx64xmem", ENCODING_VSIB)
+ ENCODING("vx128xmem", ENCODING_VSIB)
+ ENCODING("vx256xmem", ENCODING_VSIB)
+ ENCODING("vy128xmem", ENCODING_VSIB)
+ ENCODING("vy256xmem", ENCODING_VSIB)
+ ENCODING("vy512xmem", ENCODING_VSIB)
+ ENCODING("vz256mem", ENCODING_VSIB)
+ ENCODING("vz512mem", ENCODING_VSIB)
errs() << "Unhandled memory encoding " << s << "\n";
llvm_unreachable("Unhandled memory encoding");
}
@@ -1252,48 +1300,48 @@ RecognizableInstr::memoryEncodingFromString(const std::string &s,
OperandEncoding
RecognizableInstr::relocationEncodingFromString(const std::string &s,
uint8_t OpSize) {
- if(OpSize != X86Local::OpSize16) {
+ if (OpSize != X86Local::OpSize16) {
// For instructions without an OpSize prefix, a declared 16-bit register or
// immediate encoding is special.
- ENCODING("i16imm", ENCODING_IW)
+ ENCODING("i16imm", ENCODING_IW)
}
- ENCODING("i16imm", ENCODING_Iv)
- ENCODING("i16i8imm", ENCODING_IB)
- ENCODING("i32imm", ENCODING_Iv)
- ENCODING("i32i8imm", ENCODING_IB)
- ENCODING("i64i32imm", ENCODING_ID)
- ENCODING("i64i8imm", ENCODING_IB)
- ENCODING("i8imm", ENCODING_IB)
- ENCODING("u8imm", ENCODING_IB)
- ENCODING("i16u8imm", ENCODING_IB)
- ENCODING("i32u8imm", ENCODING_IB)
- ENCODING("i64u8imm", ENCODING_IB)
+ ENCODING("i16imm", ENCODING_Iv)
+ ENCODING("i16i8imm", ENCODING_IB)
+ ENCODING("i32imm", ENCODING_Iv)
+ ENCODING("i32i8imm", ENCODING_IB)
+ ENCODING("i64i32imm", ENCODING_ID)
+ ENCODING("i64i8imm", ENCODING_IB)
+ ENCODING("i8imm", ENCODING_IB)
+ ENCODING("u8imm", ENCODING_IB)
+ ENCODING("i16u8imm", ENCODING_IB)
+ ENCODING("i32u8imm", ENCODING_IB)
+ ENCODING("i64u8imm", ENCODING_IB)
ENCODING("i64i32imm_brtarget", ENCODING_ID)
- ENCODING("i16imm_brtarget", ENCODING_IW)
- ENCODING("i32imm_brtarget", ENCODING_ID)
- ENCODING("brtarget32", ENCODING_ID)
- ENCODING("brtarget16", ENCODING_IW)
- ENCODING("brtarget8", ENCODING_IB)
- ENCODING("i64imm", ENCODING_IO)
- ENCODING("offset16_8", ENCODING_Ia)
- ENCODING("offset16_16", ENCODING_Ia)
- ENCODING("offset16_32", ENCODING_Ia)
- ENCODING("offset32_8", ENCODING_Ia)
- ENCODING("offset32_16", ENCODING_Ia)
- ENCODING("offset32_32", ENCODING_Ia)
- ENCODING("offset32_64", ENCODING_Ia)
- ENCODING("offset64_8", ENCODING_Ia)
- ENCODING("offset64_16", ENCODING_Ia)
- ENCODING("offset64_32", ENCODING_Ia)
- ENCODING("offset64_64", ENCODING_Ia)
- ENCODING("srcidx8", ENCODING_SI)
- ENCODING("srcidx16", ENCODING_SI)
- ENCODING("srcidx32", ENCODING_SI)
- ENCODING("srcidx64", ENCODING_SI)
- ENCODING("dstidx8", ENCODING_DI)
- ENCODING("dstidx16", ENCODING_DI)
- ENCODING("dstidx32", ENCODING_DI)
- ENCODING("dstidx64", ENCODING_DI)
+ ENCODING("i16imm_brtarget", ENCODING_IW)
+ ENCODING("i32imm_brtarget", ENCODING_ID)
+ ENCODING("brtarget32", ENCODING_ID)
+ ENCODING("brtarget16", ENCODING_IW)
+ ENCODING("brtarget8", ENCODING_IB)
+ ENCODING("i64imm", ENCODING_IO)
+ ENCODING("offset16_8", ENCODING_Ia)
+ ENCODING("offset16_16", ENCODING_Ia)
+ ENCODING("offset16_32", ENCODING_Ia)
+ ENCODING("offset32_8", ENCODING_Ia)
+ ENCODING("offset32_16", ENCODING_Ia)
+ ENCODING("offset32_32", ENCODING_Ia)
+ ENCODING("offset32_64", ENCODING_Ia)
+ ENCODING("offset64_8", ENCODING_Ia)
+ ENCODING("offset64_16", ENCODING_Ia)
+ ENCODING("offset64_32", ENCODING_Ia)
+ ENCODING("offset64_64", ENCODING_Ia)
+ ENCODING("srcidx8", ENCODING_SI)
+ ENCODING("srcidx16", ENCODING_SI)
+ ENCODING("srcidx32", ENCODING_SI)
+ ENCODING("srcidx64", ENCODING_SI)
+ ENCODING("dstidx8", ENCODING_DI)
+ ENCODING("dstidx16", ENCODING_DI)
+ ENCODING("dstidx32", ENCODING_DI)
+ ENCODING("dstidx64", ENCODING_DI)
errs() << "Unhandled relocation encoding " << s << "\n";
llvm_unreachable("Unhandled relocation encoding");
}
@@ -1301,11 +1349,11 @@ RecognizableInstr::relocationEncodingFromString(const std::string &s,
OperandEncoding
RecognizableInstr::opcodeModifierEncodingFromString(const std::string &s,
uint8_t OpSize) {
- ENCODING("GR32", ENCODING_Rv)
- ENCODING("GR64", ENCODING_RO)
- ENCODING("GR16", ENCODING_Rv)
- ENCODING("GR8", ENCODING_RB)
- ENCODING("ccode", ENCODING_CC)
+ ENCODING("GR32", ENCODING_Rv)
+ ENCODING("GR64", ENCODING_RO)
+ ENCODING("GR16", ENCODING_Rv)
+ ENCODING("GR8", ENCODING_RB)
+ ENCODING("ccode", ENCODING_CC)
errs() << "Unhandled opcode modifier encoding " << s << "\n";
llvm_unreachable("Unhandled opcode modifier encoding");
}
diff --git a/llvm/utils/TableGen/X86RecognizableInstr.h b/llvm/utils/TableGen/X86RecognizableInstr.h
index 5efacdb27465..61ad5e32b3fb 100644
--- a/llvm/utils/TableGen/X86RecognizableInstr.h
+++ b/llvm/utils/TableGen/X86RecognizableInstr.h
@@ -25,142 +25,158 @@
struct InstructionSpecifier;
namespace llvm {
-
class Record;
-
-#define X86_INSTR_MRM_MAPPING \
- MAP(C0, 64) \
- MAP(C1, 65) \
- MAP(C2, 66) \
- MAP(C3, 67) \
- MAP(C4, 68) \
- MAP(C5, 69) \
- MAP(C6, 70) \
- MAP(C7, 71) \
- MAP(C8, 72) \
- MAP(C9, 73) \
- MAP(CA, 74) \
- MAP(CB, 75) \
- MAP(CC, 76) \
- MAP(CD, 77) \
- MAP(CE, 78) \
- MAP(CF, 79) \
- MAP(D0, 80) \
- MAP(D1, 81) \
- MAP(D2, 82) \
- MAP(D3, 83) \
- MAP(D4, 84) \
- MAP(D5, 85) \
- MAP(D6, 86) \
- MAP(D7, 87) \
- MAP(D8, 88) \
- MAP(D9, 89) \
- MAP(DA, 90) \
- MAP(DB, 91) \
- MAP(DC, 92) \
- MAP(DD, 93) \
- MAP(DE, 94) \
- MAP(DF, 95) \
- MAP(E0, 96) \
- MAP(E1, 97) \
- MAP(E2, 98) \
- MAP(E3, 99) \
- MAP(E4, 100) \
- MAP(E5, 101) \
- MAP(E6, 102) \
- MAP(E7, 103) \
- MAP(E8, 104) \
- MAP(E9, 105) \
- MAP(EA, 106) \
- MAP(EB, 107) \
- MAP(EC, 108) \
- MAP(ED, 109) \
- MAP(EE, 110) \
- MAP(EF, 111) \
- MAP(F0, 112) \
- MAP(F1, 113) \
- MAP(F2, 114) \
- MAP(F3, 115) \
- MAP(F4, 116) \
- MAP(F5, 117) \
- MAP(F6, 118) \
- MAP(F7, 119) \
- MAP(F8, 120) \
- MAP(F9, 121) \
- MAP(FA, 122) \
- MAP(FB, 123) \
- MAP(FC, 124) \
- MAP(FD, 125) \
- MAP(FE, 126) \
+#define X86_INSTR_MRM_MAPPING \
+ MAP(C0, 64) \
+ MAP(C1, 65) \
+ MAP(C2, 66) \
+ MAP(C3, 67) \
+ MAP(C4, 68) \
+ MAP(C5, 69) \
+ MAP(C6, 70) \
+ MAP(C7, 71) \
+ MAP(C8, 72) \
+ MAP(C9, 73) \
+ MAP(CA, 74) \
+ MAP(CB, 75) \
+ MAP(CC, 76) \
+ MAP(CD, 77) \
+ MAP(CE, 78) \
+ MAP(CF, 79) \
+ MAP(D0, 80) \
+ MAP(D1, 81) \
+ MAP(D2, 82) \
+ MAP(D3, 83) \
+ MAP(D4, 84) \
+ MAP(D5, 85) \
+ MAP(D6, 86) \
+ MAP(D7, 87) \
+ MAP(D8, 88) \
+ MAP(D9, 89) \
+ MAP(DA, 90) \
+ MAP(DB, 91) \
+ MAP(DC, 92) \
+ MAP(DD, 93) \
+ MAP(DE, 94) \
+ MAP(DF, 95) \
+ MAP(E0, 96) \
+ MAP(E1, 97) \
+ MAP(E2, 98) \
+ MAP(E3, 99) \
+ MAP(E4, 100) \
+ MAP(E5, 101) \
+ MAP(E6, 102) \
+ MAP(E7, 103) \
+ MAP(E8, 104) \
+ MAP(E9, 105) \
+ MAP(EA, 106) \
+ MAP(EB, 107) \
+ MAP(EC, 108) \
+ MAP(ED, 109) \
+ MAP(EE, 110) \
+ MAP(EF, 111) \
+ MAP(F0, 112) \
+ MAP(F1, 113) \
+ MAP(F2, 114) \
+ MAP(F3, 115) \
+ MAP(F4, 116) \
+ MAP(F5, 117) \
+ MAP(F6, 118) \
+ MAP(F7, 119) \
+ MAP(F8, 120) \
+ MAP(F9, 121) \
+ MAP(FA, 122) \
+ MAP(FB, 123) \
+ MAP(FC, 124) \
+ MAP(FD, 125) \
+ MAP(FE, 126) \
MAP(FF, 127)
// A clone of X86 since we can't depend on something that is generated.
namespace X86Local {
- enum {
- Pseudo = 0,
- RawFrm = 1,
- AddRegFrm = 2,
- RawFrmMemOffs = 3,
- RawFrmSrc = 4,
- RawFrmDst = 5,
- RawFrmDstSrc = 6,
- RawFrmImm8 = 7,
- RawFrmImm16 = 8,
- AddCCFrm = 9,
- PrefixByte = 10,
- MRMDestMem4VOp3CC = 20,
- MRMr0 = 21,
- MRMSrcMemFSIB = 22,
- MRMDestMemFSIB = 23,
- MRMDestMem = 24,
- MRMSrcMem = 25,
- MRMSrcMem4VOp3 = 26,
- MRMSrcMemOp4 = 27,
- MRMSrcMemCC = 28,
- MRMXmCC = 30, MRMXm = 31,
- MRM0m = 32, MRM1m = 33, MRM2m = 34, MRM3m = 35,
- MRM4m = 36, MRM5m = 37, MRM6m = 38, MRM7m = 39,
- MRMDestReg = 40,
- MRMSrcReg = 41,
- MRMSrcReg4VOp3 = 42,
- MRMSrcRegOp4 = 43,
- MRMSrcRegCC = 44,
- MRMXrCC = 46, MRMXr = 47,
- MRM0r = 48, MRM1r = 49, MRM2r = 50, MRM3r = 51,
- MRM4r = 52, MRM5r = 53, MRM6r = 54, MRM7r = 55,
- MRM0X = 56, MRM1X = 57, MRM2X = 58, MRM3X = 59,
- MRM4X = 60, MRM5X = 61, MRM6X = 62, MRM7X = 63,
+enum {
+ Pseudo = 0,
+ RawFrm = 1,
+ AddRegFrm = 2,
+ RawFrmMemOffs = 3,
+ RawFrmSrc = 4,
+ RawFrmDst = 5,
+ RawFrmDstSrc = 6,
+ RawFrmImm8 = 7,
+ RawFrmImm16 = 8,
+ AddCCFrm = 9,
+ PrefixByte = 10,
+ MRMDestMem4VOp3CC = 20,
+ MRMr0 = 21,
+ MRMSrcMemFSIB = 22,
+ MRMDestMemFSIB = 23,
+ MRMDestMem = 24,
+ MRMSrcMem = 25,
+ MRMSrcMem4VOp3 = 26,
+ MRMSrcMemOp4 = 27,
+ MRMSrcMemCC = 28,
+ MRMXmCC = 30,
+ MRMXm = 31,
+ MRM0m = 32,
+ MRM1m = 33,
+ MRM2m = 34,
+ MRM3m = 35,
+ MRM4m = 36,
+ MRM5m = 37,
+ MRM6m = 38,
+ MRM7m = 39,
+ MRMDestReg = 40,
+ MRMSrcReg = 41,
+ MRMSrcReg4VOp3 = 42,
+ MRMSrcRegOp4 = 43,
+ MRMSrcRegCC = 44,
+ MRMXrCC = 46,
+ MRMXr = 47,
+ MRM0r = 48,
+ MRM1r = 49,
+ MRM2r = 50,
+ MRM3r = 51,
+ MRM4r = 52,
+ MRM5r = 53,
+ MRM6r = 54,
+ MRM7r = 55,
+ MRM0X = 56,
+ MRM1X = 57,
+ MRM2X = 58,
+ MRM3X = 59,
+ MRM4X = 60,
+ MRM5X = 61,
+ MRM6X = 62,
+ MRM7X = 63,
#define MAP(from, to) MRM_##from = to,
- X86_INSTR_MRM_MAPPING
+ X86_INSTR_MRM_MAPPING
#undef MAP
- };
-
- enum {
- OB = 0, TB = 1, T8 = 2, TA = 3, XOP8 = 4, XOP9 = 5, XOPA = 6, ThreeDNow = 7,
- T_MAP5 = 8, T_MAP6 = 9
- };
-
- enum {
- PD = 1, XS = 2, XD = 3, PS = 4
- };
-
- enum {
- VEX = 1, XOP = 2, EVEX = 3
- };
+};
- enum {
- OpSize16 = 1, OpSize32 = 2
- };
+enum {
+ OB = 0,
+ TB = 1,
+ T8 = 2,
+ TA = 3,
+ XOP8 = 4,
+ XOP9 = 5,
+ XOPA = 6,
+ ThreeDNow = 7,
+ T_MAP4 = 8,
+ T_MAP5 = 9,
+ T_MAP6 = 10,
+ T_MAP7 = 11
+};
- enum {
- AdSize16 = 1, AdSize32 = 2, AdSize64 = 3
- };
-}
+enum { PD = 1, XS = 2, XD = 3, PS = 4 };
+enum { VEX = 1, XOP = 2, EVEX = 3 };
+enum { OpSize16 = 1, OpSize32 = 2 };
+enum { AdSize16 = 1, AdSize32 = 2, AdSize64 = 3 };
+enum { ExplicitREX2 = 1 };
+} // namespace X86Local
namespace X86Disassembler {
-
class DisassemblerTables;
-
/// Extract common fields of a single X86 instruction from a CodeGenInstruction
struct RecognizableInstrBase {
/// The OpPrefix field from the record
@@ -206,6 +222,8 @@ struct RecognizableInstrBase {
bool ForceDisassemble;
// The CD8_Scale field from the record
uint8_t CD8_Scale;
+ /// If explicitOpPrefix field from the record equals ExplicitREX2
+ bool ExplicitREX2Prefix;
/// \param insn The CodeGenInstruction to extract information from.
RecognizableInstrBase(const CodeGenInstruction &insn);
/// \returns true if this instruction should be emitted
@@ -219,7 +237,7 @@ struct RecognizableInstrBase {
class RecognizableInstr : public RecognizableInstrBase {
private:
/// The record from the .td files corresponding to this instruction
- const Record* Rec;
+ const Record *Rec;
/// The instruction name as listed in the tables
std::string Name;
// Whether the instruction has the predicate "In32BitMode"
@@ -229,13 +247,13 @@ private:
/// The operands of the instruction, as listed in the CodeGenInstruction.
/// They are not one-to-one with operands listed in the MCInst; for example,
/// memory operands expand to 5 operands in the MCInst
- const std::vector<CGIOperandList::OperandInfo>* Operands;
+ const std::vector<CGIOperandList::OperandInfo> *Operands;
/// The opcode of the instruction, as used in an MCInst
InstrUID UID;
/// The description of the instruction that is emitted into the instruction
/// info table
- InstructionSpecifier* Spec;
+ InstructionSpecifier *Spec;
/// insnContext - Returns the primary context in which the instruction is
/// valid.
@@ -255,8 +273,8 @@ private:
/// If register size does not match OpSize, then
/// register sizes keep their size.
/// @return - The operand's type.
- static OperandType typeFromString(const std::string& s,
- bool hasREX_W, uint8_t OpSize);
+ static OperandType typeFromString(const std::string &s, bool hasREX_W,
+ uint8_t OpSize);
/// immediateEncodingFromString - Translates an immediate encoding from the
/// string provided in the LLVM tables to an OperandEncoding for use in
@@ -286,8 +304,8 @@ private:
uint8_t OpSize);
static OperandEncoding vvvvRegisterEncodingFromString(const std::string &s,
uint8_t OpSize);
- static OperandEncoding writemaskRegisterEncodingFromString(const std::string &s,
- uint8_t OpSize);
+ static OperandEncoding
+ writemaskRegisterEncodingFromString(const std::string &s, uint8_t OpSize);
/// Adjust the encoding type for an operand based on the instruction.
void adjustOperandEncoding(OperandEncoding &encoding);
@@ -310,14 +328,12 @@ private:
/// @param operandMapping - The operand mapping, which has an entry for
/// each operand that indicates whether it is a
/// duplicate, and of what.
- void handleOperand(bool optional,
- unsigned &operandIndex,
+ void handleOperand(bool optional, unsigned &operandIndex,
unsigned &physicalOperandIndex,
unsigned numPhysicalOperands,
const unsigned *operandMapping,
- OperandEncoding (*encodingFromString)
- (const std::string&,
- uint8_t OpSize));
+ OperandEncoding (*encodingFromString)(const std::string &,
+ uint8_t OpSize));
/// emitInstructionSpecifier - Loads the instruction specifier for the current
/// instruction into a DisassemblerTables.
@@ -338,8 +354,7 @@ public:
/// \param tables The DisassemblerTables that the specifier will be added to.
/// \param insn The CodeGenInstruction to extract information from.
/// \param uid The unique ID of the current instruction.
- RecognizableInstr(DisassemblerTables &tables,
- const CodeGenInstruction &insn,
+ RecognizableInstr(DisassemblerTables &tables, const CodeGenInstruction &insn,
InstrUID uid);
/// processInstr - Accepts a CodeGenInstruction and loads decode information
/// for it into a DisassemblerTables if appropriate.
@@ -350,8 +365,7 @@ public:
/// information.
/// \param uid The unique ID of the instruction.
static void processInstr(DisassemblerTables &tables,
- const CodeGenInstruction &insn,
- InstrUID uid);
+ const CodeGenInstruction &insn, InstrUID uid);
};
std::string getMnemonic(const CodeGenInstruction *I, unsigned Variant);
@@ -361,7 +375,5 @@ bool isImmediateOperand(const Record *Rec);
unsigned getRegOperandSize(const Record *RegRec);
unsigned getMemOperandSize(const Record *MemRec);
} // namespace X86Disassembler
-
} // namespace llvm
-
#endif