The way HwMode is currently implemented, tablegen duplicates each
pattern that is dependent on hardware mode. The HwMode predicate is
added as a pattern predicate on the duplicated pattern.
RISC-V uses HwMode on the GPR register class which means almost every
isel pattern is affected by HwMode. This results in the isel table
being nearly twice the size it would be if we only had a single GPR
size.
This patch proposes to do the expansion at instruction selection time
instead. To accomplish this new opcodes like OPC_CheckTypeByHwMode
are added to the isel table. The unique combinations of types and HwMode
are converted to an index that is the payload for the new opcodes.
TableGen emits a new virtual function getValueTypeByHwMode that uses
this index and the current HwMode to look up the type.
This reduces the size of the isel table on RISC-V from ~2.38 million
bytes to ~1.38 million bytes.
I did not add an OPC_SwitchTypeByHwMode opcode yet. If the VT requires a
hardware mode, we emit an OPC_Scope+OPC_CheckTypeByHwMode instead. I
expect adding an OPC_SwitchTypeByHwMode could further reduce the table
size. I will investigate this as a follow up.
Many of the matcher classes in tablegen now use ValueTypeByHwMode
insteadof MVT. This may have an impact on the memory usage and runtime of
tablegen. We can mitigate some of this by splitting the matchers into MVT and
ValueTypeByHwMode versions. We can also explore alternate data
structures for ValueTypeByHwMode instead of a std::map. Maybe a sorted vector.
A similar change can be made to GlobalISel as a follow up.
1573 lines
51 KiB
C++
1573 lines
51 KiB
C++
//===- DAGISelMatcherEmitter.cpp - Matcher Emitter ------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains code to generate C++ code for a matcher.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Basic/SDNodeProperties.h"
|
|
#include "Common/CodeGenDAGPatterns.h"
|
|
#include "Common/CodeGenInstruction.h"
|
|
#include "Common/CodeGenRegisters.h"
|
|
#include "Common/CodeGenTarget.h"
|
|
#include "DAGISelMatcher.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/MapVector.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/TinyPtrVector.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Format.h"
|
|
#include "llvm/Support/LEB128.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "llvm/TableGen/Error.h"
|
|
#include "llvm/TableGen/Record.h"
|
|
|
|
using namespace llvm;
|
|
|
|
enum {
|
|
IndexWidth = 7,
|
|
FullIndexWidth = IndexWidth + 4,
|
|
HistOpcWidth = 40,
|
|
};
|
|
|
|
static cl::OptionCategory DAGISelCat("Options for -gen-dag-isel");
|
|
|
|
// To reduce generated source code size.
|
|
static cl::opt<bool> OmitComments("omit-comments",
|
|
cl::desc("Do not generate comments"),
|
|
cl::init(false), cl::cat(DAGISelCat));
|
|
|
|
static cl::opt<bool> InstrumentCoverage(
|
|
"instrument-coverage",
|
|
cl::desc("Generates tables to help identify patterns matched"),
|
|
cl::init(false), cl::cat(DAGISelCat));
|
|
|
|
namespace {
|
|
class MatcherTableEmitter {
|
|
const CodeGenDAGPatterns &CGP;
|
|
|
|
SmallVector<unsigned, Matcher::HighestKind + 1> OpcodeCounts;
|
|
|
|
std::vector<TreePattern *> NodePredicates;
|
|
std::vector<TreePattern *> NodePredicatesWithOperands;
|
|
|
|
// We de-duplicate the predicates by code string, and use this map to track
|
|
// all the patterns with "identical" predicates.
|
|
MapVector<std::string, TinyPtrVector<TreePattern *>, StringMap<unsigned>>
|
|
NodePredicatesByCodeToRun;
|
|
|
|
std::vector<std::string> PatternPredicates;
|
|
|
|
std::vector<const ComplexPattern *> ComplexPatterns;
|
|
|
|
DenseMap<const Record *, unsigned> NodeXFormMap;
|
|
std::vector<const Record *> NodeXForms;
|
|
|
|
std::vector<std::string> VecIncludeStrings;
|
|
MapVector<std::string, unsigned, StringMap<unsigned>> VecPatterns;
|
|
|
|
std::map<ValueTypeByHwMode, unsigned> ValueTypeMap;
|
|
|
|
unsigned getPatternIdxFromTable(std::string &&P, std::string &&include_loc) {
|
|
const auto [It, Inserted] =
|
|
VecPatterns.try_emplace(std::move(P), VecPatterns.size());
|
|
if (Inserted) {
|
|
VecIncludeStrings.push_back(std::move(include_loc));
|
|
return VecIncludeStrings.size() - 1;
|
|
}
|
|
return It->second;
|
|
}
|
|
|
|
public:
|
|
MatcherTableEmitter(const Matcher *TheMatcher, const CodeGenDAGPatterns &cgp)
|
|
: CGP(cgp), OpcodeCounts(Matcher::HighestKind + 1, 0) {
|
|
// Record the usage of ComplexPattern.
|
|
MapVector<const ComplexPattern *, unsigned> ComplexPatternUsage;
|
|
// Record the usage of PatternPredicate.
|
|
MapVector<StringRef, unsigned> PatternPredicateUsage;
|
|
// Record the usage of Predicate.
|
|
MapVector<TreePattern *, unsigned> PredicateUsage;
|
|
|
|
// Iterate the whole MatcherTable once and do some statistics.
|
|
std::function<void(const Matcher *)> Statistic = [&](const Matcher *N) {
|
|
while (N) {
|
|
if (auto *SM = dyn_cast<ScopeMatcher>(N))
|
|
for (unsigned I = 0; I < SM->getNumChildren(); I++)
|
|
Statistic(SM->getChild(I));
|
|
else if (auto *SOM = dyn_cast<SwitchOpcodeMatcher>(N))
|
|
for (unsigned I = 0; I < SOM->getNumCases(); I++)
|
|
Statistic(SOM->getCaseMatcher(I));
|
|
else if (auto *STM = dyn_cast<SwitchTypeMatcher>(N))
|
|
for (unsigned I = 0; I < STM->getNumCases(); I++)
|
|
Statistic(STM->getCaseMatcher(I));
|
|
else if (auto *CPM = dyn_cast<CheckComplexPatMatcher>(N))
|
|
++ComplexPatternUsage[&CPM->getPattern()];
|
|
else if (auto *CPPM = dyn_cast<CheckPatternPredicateMatcher>(N))
|
|
++PatternPredicateUsage[CPPM->getPredicate()];
|
|
else if (auto *PM = dyn_cast<CheckPredicateMatcher>(N))
|
|
++PredicateUsage[PM->getPredicate().getOrigPatFragRecord()];
|
|
N = N->getNext();
|
|
}
|
|
};
|
|
Statistic(TheMatcher);
|
|
|
|
// Sort ComplexPatterns by usage.
|
|
std::vector<std::pair<const ComplexPattern *, unsigned>> ComplexPatternList(
|
|
ComplexPatternUsage.begin(), ComplexPatternUsage.end());
|
|
stable_sort(ComplexPatternList, [](const auto &A, const auto &B) {
|
|
return A.second > B.second;
|
|
});
|
|
for (const auto &ComplexPattern : ComplexPatternList)
|
|
ComplexPatterns.push_back(ComplexPattern.first);
|
|
|
|
// Sort PatternPredicates by usage.
|
|
std::vector<std::pair<std::string, unsigned>> PatternPredicateList(
|
|
PatternPredicateUsage.begin(), PatternPredicateUsage.end());
|
|
stable_sort(PatternPredicateList, [](const auto &A, const auto &B) {
|
|
return A.second > B.second;
|
|
});
|
|
for (const auto &PatternPredicate : PatternPredicateList)
|
|
PatternPredicates.push_back(PatternPredicate.first);
|
|
|
|
// Sort Predicates by usage.
|
|
// Merge predicates with same code.
|
|
for (const auto &Usage : PredicateUsage) {
|
|
TreePattern *TP = Usage.first;
|
|
TreePredicateFn Pred(TP);
|
|
NodePredicatesByCodeToRun[Pred.getCodeToRunOnSDNode()].push_back(TP);
|
|
}
|
|
|
|
std::vector<std::pair<TreePattern *, unsigned>> PredicateList;
|
|
// Sum the usage.
|
|
for (auto &Predicate : NodePredicatesByCodeToRun) {
|
|
TinyPtrVector<TreePattern *> &TPs = Predicate.second;
|
|
stable_sort(TPs, [](const auto *A, const auto *B) {
|
|
return A->getRecord()->getName() < B->getRecord()->getName();
|
|
});
|
|
unsigned Uses = 0;
|
|
for (TreePattern *TP : TPs)
|
|
Uses += PredicateUsage[TP];
|
|
|
|
// We only add the first predicate here since they are with the same code.
|
|
PredicateList.emplace_back(TPs[0], Uses);
|
|
}
|
|
|
|
stable_sort(PredicateList, [](const auto &A, const auto &B) {
|
|
return A.second > B.second;
|
|
});
|
|
for (const auto &Predicate : PredicateList) {
|
|
TreePattern *TP = Predicate.first;
|
|
if (TreePredicateFn(TP).usesOperands())
|
|
NodePredicatesWithOperands.push_back(TP);
|
|
else
|
|
NodePredicates.push_back(TP);
|
|
}
|
|
}
|
|
|
|
unsigned EmitMatcherList(const Matcher *N, const unsigned Indent,
|
|
unsigned StartIdx, raw_ostream &OS);
|
|
|
|
unsigned SizeMatcherList(Matcher *N, raw_ostream &OS);
|
|
|
|
void EmitPredicateFunctions(raw_ostream &OS);
|
|
|
|
void EmitValueTypeFunction(raw_ostream &OS);
|
|
|
|
void EmitHistogram(const Matcher *N, raw_ostream &OS);
|
|
|
|
void EmitPatternMatchTable(raw_ostream &OS);
|
|
|
|
private:
|
|
void EmitNodePredicatesFunction(const std::vector<TreePattern *> &Preds,
|
|
StringRef Decl, raw_ostream &OS);
|
|
|
|
unsigned SizeMatcher(Matcher *N, raw_ostream &OS);
|
|
|
|
unsigned EmitMatcher(const Matcher *N, const unsigned Indent,
|
|
unsigned CurrentIdx, raw_ostream &OS);
|
|
|
|
unsigned getNodePredicate(TreePredicateFn Pred) {
|
|
// We use the first predicate.
|
|
TreePattern *PredPat =
|
|
NodePredicatesByCodeToRun[Pred.getCodeToRunOnSDNode()][0];
|
|
return Pred.usesOperands()
|
|
? llvm::find(NodePredicatesWithOperands, PredPat) -
|
|
NodePredicatesWithOperands.begin()
|
|
: llvm::find(NodePredicates, PredPat) - NodePredicates.begin();
|
|
}
|
|
|
|
unsigned getPatternPredicate(StringRef PredName) {
|
|
return llvm::find(PatternPredicates, PredName) - PatternPredicates.begin();
|
|
}
|
|
unsigned getComplexPat(const ComplexPattern &P) {
|
|
return llvm::find(ComplexPatterns, &P) - ComplexPatterns.begin();
|
|
}
|
|
|
|
unsigned getNodeXFormID(const Record *Rec) {
|
|
unsigned &Entry = NodeXFormMap[Rec];
|
|
if (Entry == 0) {
|
|
NodeXForms.push_back(Rec);
|
|
Entry = NodeXForms.size();
|
|
}
|
|
return Entry - 1;
|
|
}
|
|
|
|
unsigned getValueTypeID(const ValueTypeByHwMode &VT) {
|
|
unsigned &Entry = ValueTypeMap[VT];
|
|
if (Entry == 0) {
|
|
Entry = ValueTypeMap.size();
|
|
if (Entry > 256)
|
|
report_fatal_error(
|
|
"More ValueType by HwMode than fit in a 8-bit index");
|
|
}
|
|
|
|
return Entry - 1;
|
|
}
|
|
|
|
unsigned emitValueTypeByHwMode(const ValueTypeByHwMode &VTBH,
|
|
raw_ostream &OS);
|
|
};
|
|
} // end anonymous namespace.
|
|
|
|
static std::string GetPatFromTreePatternNode(const TreePatternNode &N) {
|
|
std::string str;
|
|
raw_string_ostream Stream(str);
|
|
Stream << N;
|
|
return str;
|
|
}
|
|
|
|
static unsigned GetVBRSize(unsigned Val) {
|
|
if (Val <= 127)
|
|
return 1;
|
|
|
|
unsigned NumBytes = 0;
|
|
while (Val >= 128) {
|
|
Val >>= 7;
|
|
++NumBytes;
|
|
}
|
|
return NumBytes + 1;
|
|
}
|
|
|
|
/// EmitVBRValue - Emit the specified value as a VBR, returning the number of
|
|
/// bytes emitted.
|
|
static unsigned EmitVBRValue(uint64_t Val, raw_ostream &OS) {
|
|
if (Val <= 127) {
|
|
OS << Val << ',';
|
|
return 1;
|
|
}
|
|
|
|
uint64_t InVal = Val;
|
|
unsigned NumBytes = 0;
|
|
while (Val >= 128) {
|
|
OS << (Val & 127) << "|128,";
|
|
Val >>= 7;
|
|
++NumBytes;
|
|
}
|
|
OS << Val;
|
|
if (!OmitComments)
|
|
OS << "/*" << InVal << "*/";
|
|
OS << ',';
|
|
return NumBytes + 1;
|
|
}
|
|
|
|
/// Emit the specified signed value as a VBR. To improve compression we encode
|
|
/// positive numbers shifted left by 1 and negative numbers negated and shifted
|
|
/// left by 1 with bit 0 set.
|
|
static unsigned EmitSignedVBRValue(int64_t Val, raw_ostream &OS) {
|
|
uint8_t Buffer[10];
|
|
unsigned Len = encodeSLEB128(Val, Buffer);
|
|
|
|
for (unsigned i = 0; i != Len - 1; ++i)
|
|
OS << static_cast<unsigned>(Buffer[i] & 127) << "|128,";
|
|
|
|
OS << static_cast<unsigned>(Buffer[Len - 1]);
|
|
if ((Len > 1 || Val < 0) && !OmitComments)
|
|
OS << "/*" << Val << "*/";
|
|
OS << ',';
|
|
return Len;
|
|
}
|
|
|
|
// This is expensive and slow.
|
|
static std::string getIncludePath(const Record *R) {
|
|
std::string str;
|
|
raw_string_ostream Stream(str);
|
|
auto Locs = R->getLoc();
|
|
SMLoc L;
|
|
if (Locs.size() > 1) {
|
|
// Get where the pattern prototype was instantiated
|
|
L = Locs[1];
|
|
} else if (Locs.size() == 1) {
|
|
L = Locs[0];
|
|
}
|
|
unsigned CurBuf = SrcMgr.FindBufferContainingLoc(L);
|
|
assert(CurBuf && "Invalid or unspecified location!");
|
|
|
|
Stream << SrcMgr.getBufferInfo(CurBuf).Buffer->getBufferIdentifier() << ":"
|
|
<< SrcMgr.FindLineNumber(L, CurBuf);
|
|
return str;
|
|
}
|
|
|
|
/// This function traverses the matcher tree and sizes all the nodes
|
|
/// that are children of the three kinds of nodes that have them.
|
|
unsigned MatcherTableEmitter::SizeMatcherList(Matcher *N, raw_ostream &OS) {
|
|
unsigned Size = 0;
|
|
while (N) {
|
|
Size += SizeMatcher(N, OS);
|
|
N = N->getNext();
|
|
}
|
|
return Size;
|
|
}
|
|
|
|
/// This function sizes the children of the three kinds of nodes that
|
|
/// have them. It does so by using special cases for those three
|
|
/// nodes, but sharing the code in EmitMatcher() for the other kinds.
|
|
unsigned MatcherTableEmitter::SizeMatcher(Matcher *N, raw_ostream &OS) {
|
|
unsigned Idx = 0;
|
|
|
|
++OpcodeCounts[N->getKind()];
|
|
switch (N->getKind()) {
|
|
// The Scope matcher has its kind, a series of child size + child,
|
|
// and a trailing zero.
|
|
case Matcher::Scope: {
|
|
ScopeMatcher *SM = cast<ScopeMatcher>(N);
|
|
assert(SM->getNext() == nullptr && "Scope matcher should not have next");
|
|
unsigned Size = 1; // Count the kind.
|
|
for (unsigned i = 0, e = SM->getNumChildren(); i != e; ++i) {
|
|
const unsigned ChildSize = SizeMatcherList(SM->getChild(i), OS);
|
|
assert(ChildSize != 0 && "Matcher cannot have child of size 0");
|
|
SM->getChild(i)->setSize(ChildSize);
|
|
Size += GetVBRSize(ChildSize) + ChildSize; // Count VBR and child size.
|
|
}
|
|
++Size; // Count the zero sentinel.
|
|
return Size;
|
|
}
|
|
|
|
// SwitchOpcode and SwitchType have their kind, a series of child size +
|
|
// opcode/type + child, and a trailing zero.
|
|
case Matcher::SwitchOpcode:
|
|
case Matcher::SwitchType: {
|
|
unsigned Size = 1; // Count the kind.
|
|
unsigned NumCases;
|
|
if (const SwitchOpcodeMatcher *SOM = dyn_cast<SwitchOpcodeMatcher>(N))
|
|
NumCases = SOM->getNumCases();
|
|
else
|
|
NumCases = cast<SwitchTypeMatcher>(N)->getNumCases();
|
|
for (unsigned i = 0, e = NumCases; i != e; ++i) {
|
|
Matcher *Child;
|
|
if (SwitchOpcodeMatcher *SOM = dyn_cast<SwitchOpcodeMatcher>(N)) {
|
|
Child = SOM->getCaseMatcher(i);
|
|
Size += 2; // Count the child's opcode.
|
|
} else {
|
|
Child = cast<SwitchTypeMatcher>(N)->getCaseMatcher(i);
|
|
Size += GetVBRSize(cast<SwitchTypeMatcher>(N)
|
|
->getCaseType(i)
|
|
.SimpleTy); // Count the child's type.
|
|
}
|
|
const unsigned ChildSize = SizeMatcherList(Child, OS);
|
|
assert(ChildSize != 0 && "Matcher cannot have child of size 0");
|
|
Child->setSize(ChildSize);
|
|
Size += GetVBRSize(ChildSize) + ChildSize; // Count VBR and child size.
|
|
}
|
|
++Size; // Count the zero sentinel.
|
|
return Size;
|
|
}
|
|
|
|
default:
|
|
// Employ the matcher emitter to size other matchers.
|
|
return EmitMatcher(N, 0, Idx, OS);
|
|
}
|
|
llvm_unreachable("Unreachable");
|
|
}
|
|
|
|
static void BeginEmitFunction(raw_ostream &OS, StringRef RetType,
|
|
StringRef Decl, bool AddOverride) {
|
|
OS << "#ifdef GET_DAGISEL_DECL\n";
|
|
OS << RetType << ' ' << Decl;
|
|
if (AddOverride)
|
|
OS << " override";
|
|
OS << ";\n"
|
|
"#endif\n"
|
|
"#if defined(GET_DAGISEL_BODY) || DAGISEL_INLINE\n";
|
|
OS << RetType << " DAGISEL_CLASS_COLONCOLON " << Decl << "\n";
|
|
if (AddOverride) {
|
|
OS << "#if DAGISEL_INLINE\n"
|
|
" override\n"
|
|
"#endif\n";
|
|
}
|
|
}
|
|
|
|
static void EndEmitFunction(raw_ostream &OS) {
|
|
OS << "#endif // GET_DAGISEL_BODY\n\n";
|
|
}
|
|
|
|
void MatcherTableEmitter::EmitPatternMatchTable(raw_ostream &OS) {
|
|
|
|
if (!isUInt<32>(VecPatterns.size()))
|
|
report_fatal_error("More patterns defined that can fit into 32-bit Pattern "
|
|
"Table index encoding");
|
|
|
|
assert(VecPatterns.size() == VecIncludeStrings.size() &&
|
|
"The sizes of Pattern and include vectors should be the same");
|
|
|
|
BeginEmitFunction(OS, "StringRef", "getPatternForIndex(unsigned Index)",
|
|
true /*AddOverride*/);
|
|
OS << "{\n";
|
|
OS << "static const char *PATTERN_MATCH_TABLE[] = {\n";
|
|
|
|
for (const auto &It : VecPatterns) {
|
|
OS << "\"" << It.first << "\",\n";
|
|
}
|
|
|
|
OS << "\n};";
|
|
OS << "\nreturn StringRef(PATTERN_MATCH_TABLE[Index]);";
|
|
OS << "\n}\n";
|
|
EndEmitFunction(OS);
|
|
|
|
BeginEmitFunction(OS, "StringRef", "getIncludePathForIndex(unsigned Index)",
|
|
true /*AddOverride*/);
|
|
OS << "{\n";
|
|
OS << "static const char *INCLUDE_PATH_TABLE[] = {\n";
|
|
|
|
for (const auto &It : VecIncludeStrings) {
|
|
OS << "\"" << It << "\",\n";
|
|
}
|
|
|
|
OS << "\n};";
|
|
OS << "\nreturn StringRef(INCLUDE_PATH_TABLE[Index]);";
|
|
OS << "\n}\n";
|
|
EndEmitFunction(OS);
|
|
}
|
|
|
|
static unsigned emitMVT(MVT VT, raw_ostream &OS) {
|
|
// Print the MVT directly if it doesn't require a VBR.
|
|
if (VT.SimpleTy <= 127) {
|
|
OS << getEnumName(VT) << ',';
|
|
return 1;
|
|
}
|
|
|
|
if (!OmitComments)
|
|
OS << "/*" << getEnumName(VT) << "*/";
|
|
return EmitVBRValue(VT.SimpleTy, OS);
|
|
}
|
|
|
|
unsigned
|
|
MatcherTableEmitter::emitValueTypeByHwMode(const ValueTypeByHwMode &VTBH,
|
|
raw_ostream &OS) {
|
|
if (!OmitComments)
|
|
OS << "/*" << VTBH << "*/";
|
|
OS << getValueTypeID(VTBH) << ',';
|
|
return 1;
|
|
}
|
|
/// EmitMatcher - Emit bytes for the specified matcher and return
|
|
/// the number of bytes emitted.
|
|
unsigned MatcherTableEmitter::EmitMatcher(const Matcher *N,
|
|
const unsigned Indent,
|
|
unsigned CurrentIdx,
|
|
raw_ostream &OS) {
|
|
OS.indent(Indent);
|
|
|
|
switch (N->getKind()) {
|
|
case Matcher::Scope: {
|
|
const ScopeMatcher *SM = cast<ScopeMatcher>(N);
|
|
unsigned StartIdx = CurrentIdx;
|
|
|
|
OS << "OPC_Scope";
|
|
if (!OmitComments)
|
|
OS << " /*" << SM->getNumChildren() << " children */";
|
|
OS << ", ";
|
|
++CurrentIdx;
|
|
|
|
// Emit all of the children.
|
|
for (unsigned i = 0, e = SM->getNumChildren(); i != e; ++i) {
|
|
if (i != 0) {
|
|
if (!OmitComments) {
|
|
OS << "/*" << format_decimal(CurrentIdx, IndexWidth) << "*/";
|
|
OS.indent(Indent) << "/*Scope*/ ";
|
|
} else {
|
|
OS.indent(Indent);
|
|
}
|
|
}
|
|
|
|
const Matcher *Child = SM->getChild(i);
|
|
unsigned ChildSize = Child->getSize();
|
|
CurrentIdx += EmitVBRValue(ChildSize, OS);
|
|
if (!OmitComments)
|
|
OS << " // ->" << CurrentIdx + ChildSize;
|
|
OS << '\n';
|
|
|
|
ChildSize = EmitMatcherList(Child, Indent + 1, CurrentIdx, OS);
|
|
assert(ChildSize == Child->getSize() &&
|
|
"Emitted child size does not match calculated size");
|
|
CurrentIdx += ChildSize;
|
|
}
|
|
|
|
// Emit a zero as a sentinel indicating end of 'Scope'.
|
|
if (!OmitComments)
|
|
OS << "/*" << format_decimal(CurrentIdx, IndexWidth) << "*/";
|
|
OS.indent(Indent) << "0,";
|
|
if (!OmitComments)
|
|
OS << " // End of Scope";
|
|
OS << '\n';
|
|
return CurrentIdx - StartIdx + 1;
|
|
}
|
|
|
|
case Matcher::RecordNode:
|
|
OS << "OPC_RecordNode,";
|
|
if (!OmitComments)
|
|
OS << " // #" << cast<RecordMatcher>(N)->getResultNo() << " = "
|
|
<< cast<RecordMatcher>(N)->getWhatFor();
|
|
OS << '\n';
|
|
return 1;
|
|
|
|
case Matcher::RecordChild:
|
|
OS << "OPC_RecordChild" << cast<RecordChildMatcher>(N)->getChildNo() << ',';
|
|
if (!OmitComments)
|
|
OS << " // #" << cast<RecordChildMatcher>(N)->getResultNo() << " = "
|
|
<< cast<RecordChildMatcher>(N)->getWhatFor();
|
|
OS << '\n';
|
|
return 1;
|
|
|
|
case Matcher::RecordMemRef:
|
|
OS << "OPC_RecordMemRef,\n";
|
|
return 1;
|
|
|
|
case Matcher::CaptureGlueInput:
|
|
OS << "OPC_CaptureGlueInput,\n";
|
|
return 1;
|
|
|
|
case Matcher::MoveChild: {
|
|
const auto *MCM = cast<MoveChildMatcher>(N);
|
|
|
|
OS << "OPC_MoveChild";
|
|
// Handle the specialized forms.
|
|
if (MCM->getChildNo() >= 8)
|
|
OS << ", ";
|
|
OS << MCM->getChildNo() << ",\n";
|
|
return (MCM->getChildNo() >= 8) ? 2 : 1;
|
|
}
|
|
|
|
case Matcher::MoveSibling: {
|
|
const auto *MSM = cast<MoveSiblingMatcher>(N);
|
|
|
|
OS << "OPC_MoveSibling";
|
|
// Handle the specialized forms.
|
|
if (MSM->getSiblingNo() >= 8)
|
|
OS << ", ";
|
|
OS << MSM->getSiblingNo() << ",\n";
|
|
return (MSM->getSiblingNo() >= 8) ? 2 : 1;
|
|
}
|
|
|
|
case Matcher::MoveParent:
|
|
OS << "OPC_MoveParent,\n";
|
|
return 1;
|
|
|
|
case Matcher::CheckSame:
|
|
OS << "OPC_CheckSame, " << cast<CheckSameMatcher>(N)->getMatchNumber()
|
|
<< ",\n";
|
|
return 2;
|
|
|
|
case Matcher::CheckChildSame:
|
|
OS << "OPC_CheckChild" << cast<CheckChildSameMatcher>(N)->getChildNo()
|
|
<< "Same, " << cast<CheckChildSameMatcher>(N)->getMatchNumber() << ",\n";
|
|
return 2;
|
|
|
|
case Matcher::CheckPatternPredicate: {
|
|
StringRef Pred = cast<CheckPatternPredicateMatcher>(N)->getPredicate();
|
|
unsigned PredNo = getPatternPredicate(Pred);
|
|
if (PredNo > 255)
|
|
OS << "OPC_CheckPatternPredicateTwoByte, TARGET_VAL(" << PredNo << "),";
|
|
else if (PredNo < 8)
|
|
OS << "OPC_CheckPatternPredicate" << PredNo << ',';
|
|
else
|
|
OS << "OPC_CheckPatternPredicate, " << PredNo << ',';
|
|
if (!OmitComments)
|
|
OS << " // " << Pred;
|
|
OS << '\n';
|
|
return 2 + (PredNo > 255) - (PredNo < 8);
|
|
}
|
|
case Matcher::CheckPredicate: {
|
|
TreePredicateFn Pred = cast<CheckPredicateMatcher>(N)->getPredicate();
|
|
unsigned OperandBytes = 0;
|
|
unsigned PredNo = getNodePredicate(Pred);
|
|
|
|
if (Pred.usesOperands()) {
|
|
unsigned NumOps = cast<CheckPredicateMatcher>(N)->getNumOperands();
|
|
OS << "OPC_CheckPredicateWithOperands, " << NumOps << "/*#Ops*/, ";
|
|
for (unsigned i = 0; i < NumOps; ++i)
|
|
OS << cast<CheckPredicateMatcher>(N)->getOperandNo(i) << ", ";
|
|
OperandBytes = 1 + NumOps;
|
|
} else {
|
|
if (PredNo < 8) {
|
|
OperandBytes = -1;
|
|
OS << "OPC_CheckPredicate" << PredNo << ',';
|
|
} else {
|
|
OS << "OPC_CheckPredicate, ";
|
|
}
|
|
}
|
|
|
|
if (PredNo >= 8 || Pred.usesOperands())
|
|
OS << PredNo << ',';
|
|
if (!OmitComments)
|
|
OS << " // " << Pred.getFnName();
|
|
OS << '\n';
|
|
return 2 + OperandBytes;
|
|
}
|
|
|
|
case Matcher::CheckOpcode:
|
|
OS << "OPC_CheckOpcode, TARGET_VAL("
|
|
<< cast<CheckOpcodeMatcher>(N)->getOpcode().getEnumName() << "),\n";
|
|
return 3;
|
|
|
|
case Matcher::SwitchOpcode:
|
|
case Matcher::SwitchType: {
|
|
unsigned StartIdx = CurrentIdx;
|
|
|
|
unsigned NumCases;
|
|
if (const SwitchOpcodeMatcher *SOM = dyn_cast<SwitchOpcodeMatcher>(N)) {
|
|
OS << "OPC_SwitchOpcode ";
|
|
NumCases = SOM->getNumCases();
|
|
} else {
|
|
OS << "OPC_SwitchType ";
|
|
NumCases = cast<SwitchTypeMatcher>(N)->getNumCases();
|
|
}
|
|
|
|
if (!OmitComments)
|
|
OS << "/*" << NumCases << " cases */";
|
|
OS << ", ";
|
|
++CurrentIdx;
|
|
|
|
// For each case we emit the size, then the opcode, then the matcher.
|
|
for (unsigned i = 0, e = NumCases; i != e; ++i) {
|
|
const Matcher *Child;
|
|
unsigned IdxSize;
|
|
if (const SwitchOpcodeMatcher *SOM = dyn_cast<SwitchOpcodeMatcher>(N)) {
|
|
Child = SOM->getCaseMatcher(i);
|
|
IdxSize = 2; // size of opcode in table is 2 bytes.
|
|
} else {
|
|
Child = cast<SwitchTypeMatcher>(N)->getCaseMatcher(i);
|
|
IdxSize = GetVBRSize(
|
|
cast<SwitchTypeMatcher>(N)
|
|
->getCaseType(i)
|
|
.SimpleTy); // size of type in table is sizeof(VBR(MVT)) byte.
|
|
}
|
|
|
|
if (i != 0) {
|
|
if (!OmitComments)
|
|
OS << "/*" << format_decimal(CurrentIdx, IndexWidth) << "*/";
|
|
OS.indent(Indent);
|
|
if (!OmitComments)
|
|
OS << (isa<SwitchOpcodeMatcher>(N) ? "/*SwitchOpcode*/ "
|
|
: "/*SwitchType*/ ");
|
|
}
|
|
|
|
unsigned ChildSize = Child->getSize();
|
|
CurrentIdx += EmitVBRValue(ChildSize, OS) + IdxSize;
|
|
OS << ' ';
|
|
if (const SwitchOpcodeMatcher *SOM = dyn_cast<SwitchOpcodeMatcher>(N))
|
|
OS << "TARGET_VAL(" << SOM->getCaseOpcode(i).getEnumName() << "),";
|
|
else
|
|
emitMVT(cast<SwitchTypeMatcher>(N)->getCaseType(i), OS);
|
|
if (!OmitComments)
|
|
OS << " // ->" << CurrentIdx + ChildSize;
|
|
OS << '\n';
|
|
|
|
ChildSize = EmitMatcherList(Child, Indent + 1, CurrentIdx, OS);
|
|
assert(ChildSize == Child->getSize() &&
|
|
"Emitted child size does not match calculated size");
|
|
CurrentIdx += ChildSize;
|
|
}
|
|
|
|
// Emit the final zero to terminate the switch.
|
|
if (!OmitComments)
|
|
OS << "/*" << format_decimal(CurrentIdx, IndexWidth) << "*/";
|
|
OS.indent(Indent) << "0,";
|
|
if (!OmitComments)
|
|
OS << (isa<SwitchOpcodeMatcher>(N) ? " // EndSwitchOpcode"
|
|
: " // EndSwitchType");
|
|
|
|
OS << '\n';
|
|
return CurrentIdx - StartIdx + 1;
|
|
}
|
|
|
|
case Matcher::CheckType: {
|
|
const ValueTypeByHwMode &VTBH = cast<CheckTypeMatcher>(N)->getType();
|
|
if (VTBH.isSimple()) {
|
|
MVT VT = VTBH.getSimple();
|
|
if (cast<CheckTypeMatcher>(N)->getResNo() == 0) {
|
|
switch (VT.SimpleTy) {
|
|
case MVT::i32:
|
|
case MVT::i64:
|
|
OS << "OPC_CheckTypeI" << MVT(VT).getSizeInBits() << ",\n";
|
|
return 1;
|
|
default:
|
|
OS << "OPC_CheckType, ";
|
|
unsigned NumBytes = emitMVT(VT, OS);
|
|
OS << '\n';
|
|
return NumBytes + 1;
|
|
}
|
|
}
|
|
|
|
OS << "OPC_CheckTypeRes, " << cast<CheckTypeMatcher>(N)->getResNo()
|
|
<< ", ";
|
|
unsigned NumBytes =
|
|
emitMVT(cast<CheckTypeMatcher>(N)->getType().getSimple(), OS);
|
|
OS << '\n';
|
|
return NumBytes + 2;
|
|
}
|
|
|
|
unsigned OpSize = 1;
|
|
if (cast<CheckTypeMatcher>(N)->getResNo() == 0) {
|
|
OS << "OPC_CheckTypeByHwMode, ";
|
|
} else {
|
|
OS << "OPC_CheckTypeResByHwMode, "
|
|
<< cast<CheckTypeMatcher>(N)->getResNo() << ", ";
|
|
OpSize += 1;
|
|
}
|
|
OpSize += emitValueTypeByHwMode(VTBH, OS);
|
|
OS << '\n';
|
|
return OpSize;
|
|
}
|
|
|
|
case Matcher::CheckChildType: {
|
|
const ValueTypeByHwMode &VTBH = cast<CheckChildTypeMatcher>(N)->getType();
|
|
if (VTBH.isSimple()) {
|
|
MVT VT = VTBH.getSimple();
|
|
switch (VT.SimpleTy) {
|
|
case MVT::i32:
|
|
case MVT::i64:
|
|
OS << "OPC_CheckChild" << cast<CheckChildTypeMatcher>(N)->getChildNo()
|
|
<< "TypeI" << MVT(VT).getSizeInBits() << ",\n";
|
|
return 1;
|
|
default:
|
|
OS << "OPC_CheckChild" << cast<CheckChildTypeMatcher>(N)->getChildNo()
|
|
<< "Type, ";
|
|
unsigned NumBytes = emitMVT(VT, OS);
|
|
OS << '\n';
|
|
return NumBytes + 1;
|
|
}
|
|
} else {
|
|
OS << "OPC_CheckChild" << cast<CheckChildTypeMatcher>(N)->getChildNo()
|
|
<< "TypeByHwMode, ";
|
|
unsigned NumBytes = emitValueTypeByHwMode(VTBH, OS);
|
|
OS << '\n';
|
|
return NumBytes + 1;
|
|
}
|
|
}
|
|
|
|
case Matcher::CheckInteger: {
|
|
OS << "OPC_CheckInteger, ";
|
|
unsigned Bytes =
|
|
1 + EmitSignedVBRValue(cast<CheckIntegerMatcher>(N)->getValue(), OS);
|
|
OS << '\n';
|
|
return Bytes;
|
|
}
|
|
case Matcher::CheckChildInteger: {
|
|
OS << "OPC_CheckChild" << cast<CheckChildIntegerMatcher>(N)->getChildNo()
|
|
<< "Integer, ";
|
|
unsigned Bytes = 1 + EmitSignedVBRValue(
|
|
cast<CheckChildIntegerMatcher>(N)->getValue(), OS);
|
|
OS << '\n';
|
|
return Bytes;
|
|
}
|
|
case Matcher::CheckCondCode:
|
|
OS << "OPC_CheckCondCode, ISD::"
|
|
<< cast<CheckCondCodeMatcher>(N)->getCondCodeName() << ",\n";
|
|
return 2;
|
|
|
|
case Matcher::CheckChild2CondCode:
|
|
OS << "OPC_CheckChild2CondCode, ISD::"
|
|
<< cast<CheckChild2CondCodeMatcher>(N)->getCondCodeName() << ",\n";
|
|
return 2;
|
|
|
|
case Matcher::CheckValueType: {
|
|
OS << "OPC_CheckValueType, ";
|
|
unsigned NumBytes = emitMVT(cast<CheckValueTypeMatcher>(N)->getVT(), OS);
|
|
OS << "\n";
|
|
return NumBytes + 1;
|
|
}
|
|
|
|
case Matcher::CheckComplexPat: {
|
|
const CheckComplexPatMatcher *CCPM = cast<CheckComplexPatMatcher>(N);
|
|
const ComplexPattern &Pattern = CCPM->getPattern();
|
|
unsigned PatternNo = getComplexPat(Pattern);
|
|
if (PatternNo < 8)
|
|
OS << "OPC_CheckComplexPat" << PatternNo << ", /*#*/"
|
|
<< CCPM->getMatchNumber() << ',';
|
|
else
|
|
OS << "OPC_CheckComplexPat, /*CP*/" << PatternNo << ", /*#*/"
|
|
<< CCPM->getMatchNumber() << ',';
|
|
|
|
if (!OmitComments) {
|
|
OS << " // " << Pattern.getSelectFunc();
|
|
OS << ":$" << CCPM->getName();
|
|
for (unsigned i = 0, e = Pattern.getNumOperands(); i != e; ++i)
|
|
OS << " #" << CCPM->getFirstResult() + i;
|
|
|
|
if (Pattern.hasProperty(SDNPHasChain))
|
|
OS << " + chain result";
|
|
}
|
|
OS << '\n';
|
|
return PatternNo < 8 ? 2 : 3;
|
|
}
|
|
|
|
case Matcher::CheckAndImm: {
|
|
OS << "OPC_CheckAndImm, ";
|
|
unsigned Bytes =
|
|
1 + EmitVBRValue(cast<CheckAndImmMatcher>(N)->getValue(), OS);
|
|
OS << '\n';
|
|
return Bytes;
|
|
}
|
|
|
|
case Matcher::CheckOrImm: {
|
|
OS << "OPC_CheckOrImm, ";
|
|
unsigned Bytes =
|
|
1 + EmitVBRValue(cast<CheckOrImmMatcher>(N)->getValue(), OS);
|
|
OS << '\n';
|
|
return Bytes;
|
|
}
|
|
|
|
case Matcher::CheckFoldableChainNode:
|
|
OS << "OPC_CheckFoldableChainNode,\n";
|
|
return 1;
|
|
|
|
case Matcher::CheckImmAllOnesV:
|
|
OS << "OPC_CheckImmAllOnesV,\n";
|
|
return 1;
|
|
|
|
case Matcher::CheckImmAllZerosV:
|
|
OS << "OPC_CheckImmAllZerosV,\n";
|
|
return 1;
|
|
|
|
case Matcher::EmitInteger: {
|
|
const auto *IM = cast<EmitIntegerMatcher>(N);
|
|
int64_t Val = IM->getValue();
|
|
const std::string &Str = IM->getString();
|
|
const ValueTypeByHwMode &VTBH = IM->getVT();
|
|
unsigned TypeBytes = 0;
|
|
if (VTBH.isSimple()) {
|
|
MVT VT = VTBH.getSimple();
|
|
switch (VT.SimpleTy) {
|
|
case MVT::i8:
|
|
case MVT::i16:
|
|
case MVT::i32:
|
|
case MVT::i64:
|
|
OS << "OPC_EmitIntegerI" << VT.getSizeInBits() << ", ";
|
|
break;
|
|
default:
|
|
OS << "OPC_EmitInteger, ";
|
|
TypeBytes = emitMVT(VT, OS);
|
|
OS << ' ';
|
|
break;
|
|
}
|
|
} else {
|
|
OS << "OPC_EmitIntegerByHwMode, ";
|
|
TypeBytes = emitValueTypeByHwMode(VTBH, OS);
|
|
OS << ' ';
|
|
}
|
|
// If the value is 63 or smaller, use the string directly. Otherwise, use
|
|
// a VBR.
|
|
unsigned ValBytes = 1;
|
|
if (!Str.empty() && Val <= 63)
|
|
OS << Str << ',';
|
|
else
|
|
ValBytes = EmitSignedVBRValue(Val, OS);
|
|
if (!OmitComments) {
|
|
OS << " // #" << IM->getResultNo() << " = ";
|
|
if (!Str.empty())
|
|
OS << Str;
|
|
else
|
|
OS << Val;
|
|
}
|
|
OS << '\n';
|
|
return 1 + TypeBytes + ValBytes;
|
|
}
|
|
|
|
case Matcher::EmitRegister: {
|
|
const EmitRegisterMatcher *Matcher = cast<EmitRegisterMatcher>(N);
|
|
const CodeGenRegister *Reg = Matcher->getReg();
|
|
const ValueTypeByHwMode &VTBH = Matcher->getVT();
|
|
unsigned OpBytes;
|
|
if (VTBH.isSimple()) {
|
|
MVT VT = VTBH.getSimple();
|
|
// If the enum value of the register is larger than one byte can handle,
|
|
// use EmitRegister2.
|
|
if (Reg && Reg->EnumValue > 255) {
|
|
OS << "OPC_EmitRegister2, ";
|
|
OpBytes = emitMVT(VT, OS);
|
|
OS << " TARGET_VAL(" << getQualifiedName(Reg->TheDef) << "),\n";
|
|
return OpBytes + 3;
|
|
}
|
|
switch (VT.SimpleTy) {
|
|
case MVT::i32:
|
|
case MVT::i64:
|
|
OpBytes = 1;
|
|
OS << "OPC_EmitRegisterI" << VT.getSizeInBits() << ", ";
|
|
break;
|
|
default:
|
|
OS << "OPC_EmitRegister, ";
|
|
OpBytes = emitMVT(VT, OS) + 1;
|
|
OS << ' ';
|
|
break;
|
|
}
|
|
} else {
|
|
if (Reg && Reg->EnumValue > 255) {
|
|
OS << "OPC_EmitRegisterByHwMode2, ";
|
|
OpBytes = emitValueTypeByHwMode(VTBH, OS);
|
|
OS << " TARGET_VAL(" << getQualifiedName(Reg->TheDef) << "),\n";
|
|
return OpBytes + 3;
|
|
}
|
|
|
|
OS << "OPC_EmitRegisterByHwMode, ";
|
|
OpBytes = emitValueTypeByHwMode(VTBH, OS) + 1;
|
|
OS << ' ';
|
|
}
|
|
if (Reg) {
|
|
OS << getQualifiedName(Reg->TheDef);
|
|
} else {
|
|
OS << "0 ";
|
|
if (!OmitComments)
|
|
OS << "/*zero_reg*/";
|
|
}
|
|
|
|
OS << ',';
|
|
if (!OmitComments)
|
|
OS << " // #" << Matcher->getResultNo();
|
|
OS << '\n';
|
|
return OpBytes + 1;
|
|
}
|
|
|
|
case Matcher::EmitConvertToTarget: {
|
|
const auto *CTTM = cast<EmitConvertToTargetMatcher>(N);
|
|
unsigned Slot = CTTM->getSlot();
|
|
OS << "OPC_EmitConvertToTarget";
|
|
if (Slot >= 8)
|
|
OS << ", ";
|
|
OS << Slot << ',';
|
|
if (!OmitComments)
|
|
OS << " // #" << CTTM->getResultNo();
|
|
OS << '\n';
|
|
return 1 + (Slot >= 8);
|
|
}
|
|
|
|
case Matcher::EmitMergeInputChains: {
|
|
const EmitMergeInputChainsMatcher *MN =
|
|
cast<EmitMergeInputChainsMatcher>(N);
|
|
|
|
// Handle the specialized forms OPC_EmitMergeInputChains1_0, 1_1, and 1_2.
|
|
if (MN->getNumNodes() == 1 && MN->getNode(0) < 3) {
|
|
OS << "OPC_EmitMergeInputChains1_" << MN->getNode(0) << ",\n";
|
|
return 1;
|
|
}
|
|
|
|
OS << "OPC_EmitMergeInputChains, " << MN->getNumNodes() << ',';
|
|
for (unsigned i = 0, e = MN->getNumNodes(); i != e; ++i)
|
|
OS << ' ' << MN->getNode(i) << ",";
|
|
OS << '\n';
|
|
return 2 + MN->getNumNodes();
|
|
}
|
|
case Matcher::EmitCopyToReg: {
|
|
const auto *C2RMatcher = cast<EmitCopyToRegMatcher>(N);
|
|
int Bytes = 3;
|
|
const CodeGenRegister *Reg = C2RMatcher->getDestPhysReg();
|
|
unsigned Slot = C2RMatcher->getSrcSlot();
|
|
if (Reg->EnumValue > 255) {
|
|
assert(isUInt<16>(Reg->EnumValue) && "not handled");
|
|
OS << "OPC_EmitCopyToRegTwoByte, " << Slot << ", "
|
|
<< "TARGET_VAL(" << getQualifiedName(Reg->TheDef) << "),\n";
|
|
++Bytes;
|
|
} else {
|
|
if (Slot < 8) {
|
|
OS << "OPC_EmitCopyToReg" << Slot << ", "
|
|
<< getQualifiedName(Reg->TheDef) << ",\n";
|
|
--Bytes;
|
|
} else {
|
|
OS << "OPC_EmitCopyToReg, " << Slot << ", "
|
|
<< getQualifiedName(Reg->TheDef) << ",\n";
|
|
}
|
|
}
|
|
|
|
return Bytes;
|
|
}
|
|
case Matcher::EmitNodeXForm: {
|
|
const EmitNodeXFormMatcher *XF = cast<EmitNodeXFormMatcher>(N);
|
|
OS << "OPC_EmitNodeXForm, " << getNodeXFormID(XF->getNodeXForm()) << ", "
|
|
<< XF->getSlot() << ',';
|
|
if (!OmitComments)
|
|
OS << " // " << XF->getNodeXForm()->getName() << " #"
|
|
<< XF->getResultNo();
|
|
OS << '\n';
|
|
return 3;
|
|
}
|
|
|
|
case Matcher::EmitNode:
|
|
case Matcher::MorphNodeTo: {
|
|
auto NumCoveredBytes = 0;
|
|
if (InstrumentCoverage) {
|
|
if (const MorphNodeToMatcher *SNT = dyn_cast<MorphNodeToMatcher>(N)) {
|
|
NumCoveredBytes = 3;
|
|
OS << "OPC_Coverage, ";
|
|
std::string src =
|
|
GetPatFromTreePatternNode(SNT->getPattern().getSrcPattern());
|
|
std::string dst =
|
|
GetPatFromTreePatternNode(SNT->getPattern().getDstPattern());
|
|
const Record *PatRecord = SNT->getPattern().getSrcRecord();
|
|
std::string include_src = getIncludePath(PatRecord);
|
|
unsigned Offset =
|
|
getPatternIdxFromTable(src + " -> " + dst, std::move(include_src));
|
|
OS << "COVERAGE_IDX_VAL(" << Offset << "),\n";
|
|
OS.indent(FullIndexWidth + Indent);
|
|
}
|
|
}
|
|
const EmitNodeMatcherCommon *EN = cast<EmitNodeMatcherCommon>(N);
|
|
bool SupportsDeactivationSymbol =
|
|
EN->getInstruction().TheDef->getValueAsBit(
|
|
"supportsDeactivationSymbol");
|
|
if (SupportsDeactivationSymbol) {
|
|
OS << "OPC_CaptureDeactivationSymbol,\n";
|
|
OS.indent(FullIndexWidth + Indent);
|
|
}
|
|
|
|
bool ByHwMode =
|
|
llvm::any_of(EN->getVTList(), [](const ValueTypeByHwMode &VT) {
|
|
return !VT.isSimple();
|
|
});
|
|
|
|
bool IsEmitNode = isa<EmitNodeMatcher>(EN);
|
|
OS << (IsEmitNode ? "OPC_EmitNode" : "OPC_MorphNodeTo");
|
|
unsigned NumVTs = EN->getNumVTs();
|
|
bool CompressVTs = !ByHwMode && EN->getNumVTs() < 3;
|
|
bool CompressNodeInfo = false;
|
|
if (CompressVTs) {
|
|
OS << NumVTs;
|
|
// When NumVTs is zero, only consider compressing the chain flag. Any
|
|
// zero result node without chain would be deleted and not eligible for
|
|
// isel.
|
|
if (NumVTs > 0 && !EN->hasChain() && !EN->hasInGlue() &&
|
|
!EN->hasOutGlue() && !EN->hasMemRefs() &&
|
|
EN->getNumFixedArityOperands() == -1) {
|
|
CompressNodeInfo = true;
|
|
OS << "None";
|
|
} else if (EN->hasChain() && !EN->hasInGlue() && !EN->hasOutGlue() &&
|
|
!EN->hasMemRefs() && EN->getNumFixedArityOperands() == -1) {
|
|
CompressNodeInfo = true;
|
|
OS << "Chain";
|
|
} else if (NumVTs > 0 && !IsEmitNode && !EN->hasChain() &&
|
|
EN->hasInGlue() && !EN->hasOutGlue() && !EN->hasMemRefs() &&
|
|
EN->getNumFixedArityOperands() == -1) {
|
|
CompressNodeInfo = true;
|
|
OS << "GlueInput";
|
|
} else if (NumVTs > 0 && !IsEmitNode && !EN->hasChain() &&
|
|
!EN->hasInGlue() && EN->hasOutGlue() && !EN->hasMemRefs() &&
|
|
EN->getNumFixedArityOperands() == -1) {
|
|
CompressNodeInfo = true;
|
|
OS << "GlueOutput";
|
|
}
|
|
}
|
|
|
|
if (ByHwMode)
|
|
OS << "ByHwMode";
|
|
|
|
const CodeGenInstruction &CGI = EN->getInstruction();
|
|
OS << ", TARGET_VAL(" << CGI.Namespace << "::" << CGI.TheDef->getName()
|
|
<< ")";
|
|
|
|
if (!CompressNodeInfo) {
|
|
OS << ", 0";
|
|
if (EN->hasChain())
|
|
OS << "|OPFL_Chain";
|
|
if (EN->hasInGlue())
|
|
OS << "|OPFL_GlueInput";
|
|
if (EN->hasOutGlue())
|
|
OS << "|OPFL_GlueOutput";
|
|
if (EN->hasMemRefs())
|
|
OS << "|OPFL_MemRefs";
|
|
if (EN->getNumFixedArityOperands() != -1)
|
|
OS << "|OPFL_Variadic" << EN->getNumFixedArityOperands();
|
|
}
|
|
OS << ",\n";
|
|
|
|
OS.indent(FullIndexWidth + Indent + 4);
|
|
if (!CompressVTs) {
|
|
OS << EN->getNumVTs();
|
|
if (!OmitComments)
|
|
OS << "/*#VTs*/";
|
|
OS << ",";
|
|
}
|
|
unsigned NumTypeBytes = 0;
|
|
if (ByHwMode) {
|
|
for (unsigned i = 0, e = EN->getNumVTs(); i != e; ++i) {
|
|
OS << ' ';
|
|
const ValueTypeByHwMode &VTBH = EN->getVT(i);
|
|
NumTypeBytes += emitValueTypeByHwMode(VTBH, OS);
|
|
}
|
|
} else {
|
|
for (unsigned i = 0, e = EN->getNumVTs(); i != e; ++i) {
|
|
OS << ' ';
|
|
NumTypeBytes += emitMVT(EN->getVT(i).getSimple(), OS);
|
|
}
|
|
}
|
|
|
|
OS << ' ' << EN->getNumOperands();
|
|
if (!OmitComments)
|
|
OS << "/*#Ops*/";
|
|
OS << ',';
|
|
unsigned NumOperandBytes = 0;
|
|
for (unsigned i = 0, e = EN->getNumOperands(); i != e; ++i) {
|
|
OS << ' ';
|
|
NumOperandBytes += EmitVBRValue(EN->getOperand(i), OS);
|
|
}
|
|
|
|
if (!OmitComments) {
|
|
// Print the result #'s for EmitNode.
|
|
if (const EmitNodeMatcher *E = dyn_cast<EmitNodeMatcher>(EN)) {
|
|
if (unsigned NumResults = EN->getNumVTs()) {
|
|
OS << " // Results =";
|
|
unsigned First = E->getFirstResultSlot();
|
|
for (unsigned i = 0; i != NumResults; ++i)
|
|
OS << " #" << First + i;
|
|
}
|
|
}
|
|
OS << '\n';
|
|
|
|
if (const MorphNodeToMatcher *SNT = dyn_cast<MorphNodeToMatcher>(N)) {
|
|
OS.indent(FullIndexWidth + Indent)
|
|
<< "// Src: " << SNT->getPattern().getSrcPattern()
|
|
<< " - Complexity = " << SNT->getPattern().getPatternComplexity(CGP)
|
|
<< '\n';
|
|
OS.indent(FullIndexWidth + Indent)
|
|
<< "// Dst: " << SNT->getPattern().getDstPattern() << '\n';
|
|
}
|
|
} else {
|
|
OS << '\n';
|
|
}
|
|
|
|
return 4 + SupportsDeactivationSymbol + !CompressVTs + !CompressNodeInfo +
|
|
NumTypeBytes + NumOperandBytes + NumCoveredBytes;
|
|
}
|
|
case Matcher::CompleteMatch: {
|
|
const CompleteMatchMatcher *CM = cast<CompleteMatchMatcher>(N);
|
|
auto NumCoveredBytes = 0;
|
|
if (InstrumentCoverage) {
|
|
NumCoveredBytes = 3;
|
|
OS << "OPC_Coverage, ";
|
|
std::string src =
|
|
GetPatFromTreePatternNode(CM->getPattern().getSrcPattern());
|
|
std::string dst =
|
|
GetPatFromTreePatternNode(CM->getPattern().getDstPattern());
|
|
const Record *PatRecord = CM->getPattern().getSrcRecord();
|
|
std::string include_src = getIncludePath(PatRecord);
|
|
unsigned Offset =
|
|
getPatternIdxFromTable(src + " -> " + dst, std::move(include_src));
|
|
OS << "COVERAGE_IDX_VAL(" << Offset << "),\n";
|
|
OS.indent(FullIndexWidth + Indent);
|
|
}
|
|
OS << "OPC_CompleteMatch, " << CM->getNumResults() << ",";
|
|
unsigned NumResultBytes = 0;
|
|
for (unsigned i = 0, e = CM->getNumResults(); i != e; ++i) {
|
|
OS << ' ';
|
|
NumResultBytes += EmitVBRValue(CM->getResult(i), OS);
|
|
}
|
|
OS << '\n';
|
|
if (!OmitComments) {
|
|
OS.indent(FullIndexWidth + Indent)
|
|
<< " // Src: " << CM->getPattern().getSrcPattern()
|
|
<< " - Complexity = " << CM->getPattern().getPatternComplexity(CGP)
|
|
<< '\n';
|
|
OS.indent(FullIndexWidth + Indent)
|
|
<< " // Dst: " << CM->getPattern().getDstPattern();
|
|
}
|
|
OS << '\n';
|
|
return 2 + NumResultBytes + NumCoveredBytes;
|
|
}
|
|
}
|
|
llvm_unreachable("Unreachable");
|
|
}
|
|
|
|
/// This function traverses the matcher tree and emits all the nodes.
|
|
/// The nodes have already been sized.
|
|
unsigned MatcherTableEmitter::EmitMatcherList(const Matcher *N,
|
|
const unsigned Indent,
|
|
unsigned CurrentIdx,
|
|
raw_ostream &OS) {
|
|
unsigned Size = 0;
|
|
while (N) {
|
|
if (!OmitComments)
|
|
OS << "/*" << format_decimal(CurrentIdx, IndexWidth) << "*/";
|
|
unsigned MatcherSize = EmitMatcher(N, Indent, CurrentIdx, OS);
|
|
Size += MatcherSize;
|
|
CurrentIdx += MatcherSize;
|
|
|
|
// If there are other nodes in this list, iterate to them, otherwise we're
|
|
// done.
|
|
N = N->getNext();
|
|
}
|
|
return Size;
|
|
}
|
|
|
|
void MatcherTableEmitter::EmitNodePredicatesFunction(
|
|
const std::vector<TreePattern *> &Preds, StringRef Decl, raw_ostream &OS) {
|
|
if (Preds.empty())
|
|
return;
|
|
|
|
BeginEmitFunction(OS, "bool", Decl, true /*AddOverride*/);
|
|
OS << "{\n";
|
|
OS << " switch (PredNo) {\n";
|
|
OS << " default: llvm_unreachable(\"Invalid predicate in table?\");\n";
|
|
for (unsigned i = 0, e = Preds.size(); i != e; ++i) {
|
|
// Emit the predicate code corresponding to this pattern.
|
|
TreePredicateFn PredFn(Preds[i]);
|
|
assert(!PredFn.isAlwaysTrue() && "No code in this predicate");
|
|
std::string PredFnCodeStr = PredFn.getCodeToRunOnSDNode();
|
|
|
|
OS << " case " << i << ": {\n";
|
|
for (auto *SimilarPred : NodePredicatesByCodeToRun[PredFnCodeStr])
|
|
OS << " // " << TreePredicateFn(SimilarPred).getFnName() << '\n';
|
|
OS << PredFnCodeStr << "\n }\n";
|
|
}
|
|
OS << " }\n";
|
|
OS << "}\n";
|
|
EndEmitFunction(OS);
|
|
}
|
|
|
|
void MatcherTableEmitter::EmitPredicateFunctions(raw_ostream &OS) {
|
|
// Emit pattern predicates.
|
|
if (!PatternPredicates.empty()) {
|
|
BeginEmitFunction(OS, "bool",
|
|
"CheckPatternPredicate(unsigned PredNo) const",
|
|
true /*AddOverride*/);
|
|
OS << "{\n";
|
|
OS << " switch (PredNo) {\n";
|
|
OS << " default: llvm_unreachable(\"Invalid predicate in table?\");\n";
|
|
for (unsigned i = 0, e = PatternPredicates.size(); i != e; ++i)
|
|
OS << " case " << i << ": return " << PatternPredicates[i] << ";\n";
|
|
OS << " }\n";
|
|
OS << "}\n";
|
|
EndEmitFunction(OS);
|
|
}
|
|
|
|
// Emit Node predicates.
|
|
EmitNodePredicatesFunction(
|
|
NodePredicates, "CheckNodePredicate(SDValue Op, unsigned PredNo) const",
|
|
OS);
|
|
EmitNodePredicatesFunction(
|
|
NodePredicatesWithOperands,
|
|
"CheckNodePredicateWithOperands(SDValue Op, unsigned PredNo, "
|
|
"ArrayRef<SDValue> Operands) const",
|
|
OS);
|
|
|
|
// Emit CompletePattern matchers.
|
|
// FIXME: This should be const.
|
|
if (!ComplexPatterns.empty()) {
|
|
BeginEmitFunction(
|
|
OS, "bool",
|
|
"CheckComplexPattern(SDNode *Root, SDNode *Parent,\n"
|
|
" SDValue N, unsigned PatternNo,\n"
|
|
" SmallVectorImpl<std::pair<SDValue, SDNode *>> &Result)",
|
|
true /*AddOverride*/);
|
|
OS << "{\n";
|
|
OS << " unsigned NextRes = Result.size();\n";
|
|
OS << " switch (PatternNo) {\n";
|
|
OS << " default: llvm_unreachable(\"Invalid pattern # in table?\");\n";
|
|
for (unsigned i = 0, e = ComplexPatterns.size(); i != e; ++i) {
|
|
const ComplexPattern &P = *ComplexPatterns[i];
|
|
unsigned NumOps = P.getNumOperands();
|
|
|
|
if (P.hasProperty(SDNPHasChain))
|
|
++NumOps; // Get the chained node too.
|
|
|
|
OS << " case " << i << ":\n";
|
|
if (InstrumentCoverage)
|
|
OS << " {\n";
|
|
OS << " Result.resize(NextRes+" << NumOps << ");\n";
|
|
if (InstrumentCoverage)
|
|
OS << " bool Succeeded = " << P.getSelectFunc();
|
|
else
|
|
OS << " return " << P.getSelectFunc();
|
|
|
|
OS << "(";
|
|
// If the complex pattern wants the root of the match, pass it in as the
|
|
// first argument.
|
|
if (P.wantsRoot())
|
|
OS << "Root, ";
|
|
|
|
// If the complex pattern wants the parent of the operand being matched,
|
|
// pass it in as the next argument.
|
|
if (P.wantsParent())
|
|
OS << "Parent, ";
|
|
|
|
OS << "N";
|
|
for (unsigned i = 0; i != NumOps; ++i)
|
|
OS << ", Result[NextRes+" << i << "].first";
|
|
OS << ");\n";
|
|
if (InstrumentCoverage) {
|
|
OS << " if (Succeeded)\n";
|
|
OS << " dbgs() << \"\\nCOMPLEX_PATTERN: " << P.getSelectFunc()
|
|
<< "\\n\" ;\n";
|
|
OS << " return Succeeded;\n";
|
|
OS << " }\n";
|
|
}
|
|
}
|
|
OS << " }\n";
|
|
OS << "}\n";
|
|
EndEmitFunction(OS);
|
|
}
|
|
|
|
// Emit SDNodeXForm handlers.
|
|
// FIXME: This should be const.
|
|
if (!NodeXForms.empty()) {
|
|
BeginEmitFunction(OS, "SDValue",
|
|
"RunSDNodeXForm(SDValue V, unsigned XFormNo)",
|
|
true /*AddOverride*/);
|
|
OS << "{\n";
|
|
OS << " switch (XFormNo) {\n";
|
|
OS << " default: llvm_unreachable(\"Invalid xform # in table?\");\n";
|
|
|
|
// FIXME: The node xform could take SDValue's instead of SDNode*'s.
|
|
for (unsigned i = 0, e = NodeXForms.size(); i != e; ++i) {
|
|
const CodeGenDAGPatterns::NodeXForm &Entry =
|
|
CGP.getSDNodeTransform(NodeXForms[i]);
|
|
|
|
const Record *SDNode = Entry.first;
|
|
const std::string &Code = Entry.second;
|
|
|
|
OS << " case " << i << ": { ";
|
|
if (!OmitComments)
|
|
OS << "// " << NodeXForms[i]->getName();
|
|
OS << '\n';
|
|
|
|
std::string ClassName = CGP.getSDNodeInfo(SDNode).getSDClassName().str();
|
|
if (ClassName == "SDNode")
|
|
OS << " SDNode *N = V.getNode();\n";
|
|
else
|
|
OS << " " << ClassName << " *N = cast<" << ClassName
|
|
<< ">(V.getNode());\n";
|
|
OS << Code << "\n }\n";
|
|
}
|
|
OS << " }\n";
|
|
OS << "}\n";
|
|
EndEmitFunction(OS);
|
|
}
|
|
}
|
|
|
|
void MatcherTableEmitter::EmitValueTypeFunction(raw_ostream &OS) {
|
|
if (ValueTypeMap.empty())
|
|
return;
|
|
|
|
BeginEmitFunction(OS, "MVT", "getValueTypeForHwMode(unsigned Index) const",
|
|
/*AddOverride=*/true);
|
|
OS << "{\n";
|
|
|
|
OS << " switch (Index) {\n";
|
|
OS << " default: llvm_unreachable(\"Unexpected index\");\n";
|
|
|
|
for (const auto &[VTs, Idx] : ValueTypeMap) {
|
|
OS << " case " << (Idx - 1) << ":\n";
|
|
OS << " switch (HwMode) {\n";
|
|
if (!VTs.hasDefault())
|
|
OS << " default:\n return MVT();\n";
|
|
for (const auto [Mode, VT] : VTs) {
|
|
if (Mode == DefaultMode)
|
|
OS << " default:\n";
|
|
else
|
|
OS << " case " << Mode << ":\n";
|
|
OS << " return " << getEnumName(VT) << ";\n";
|
|
}
|
|
|
|
OS << " }\n";
|
|
OS << " break;\n";
|
|
}
|
|
|
|
OS << " }\n";
|
|
|
|
OS << "}\n";
|
|
EndEmitFunction(OS);
|
|
}
|
|
|
|
static StringRef getOpcodeString(Matcher::KindTy Kind) {
|
|
switch (Kind) {
|
|
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::MoveSibling:
|
|
return "OPC_MoveSibling";
|
|
case Matcher::MoveParent:
|
|
return "OPC_MoveParent";
|
|
case Matcher::CheckSame:
|
|
return "OPC_CheckSame";
|
|
case Matcher::CheckChildSame:
|
|
return "OPC_CheckChildSame";
|
|
case Matcher::CheckPatternPredicate:
|
|
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";
|
|
case Matcher::CheckImmAllOnesV:
|
|
return "OPC_CheckImmAllOnesV";
|
|
case Matcher::CheckImmAllZerosV:
|
|
return "OPC_CheckImmAllZerosV";
|
|
case Matcher::EmitInteger:
|
|
return "OPC_EmitInteger";
|
|
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?");
|
|
}
|
|
|
|
void MatcherTableEmitter::EmitHistogram(const Matcher *M, raw_ostream &OS) {
|
|
if (OmitComments)
|
|
return;
|
|
|
|
OS << " // Opcode Histogram:\n";
|
|
for (unsigned i = 0, e = OpcodeCounts.size(); i != e; ++i) {
|
|
OS << " // #"
|
|
<< left_justify(getOpcodeString((Matcher::KindTy)i), HistOpcWidth)
|
|
<< " = " << OpcodeCounts[i] << '\n';
|
|
}
|
|
OS << '\n';
|
|
}
|
|
|
|
void llvm::EmitMatcherTable(Matcher *TheMatcher, const CodeGenDAGPatterns &CGP,
|
|
raw_ostream &OS) {
|
|
OS << "#if defined(GET_DAGISEL_DECL) && defined(GET_DAGISEL_BODY)\n";
|
|
OS << "#error GET_DAGISEL_DECL and GET_DAGISEL_BODY cannot be both defined, ";
|
|
OS << "undef both for inline definitions\n";
|
|
OS << "#endif\n\n";
|
|
|
|
// Emit a check for omitted class name.
|
|
OS << "#ifdef GET_DAGISEL_BODY\n";
|
|
OS << "#define LOCAL_DAGISEL_STRINGIZE(X) LOCAL_DAGISEL_STRINGIZE_(X)\n";
|
|
OS << "#define LOCAL_DAGISEL_STRINGIZE_(X) #X\n";
|
|
OS << "static_assert(sizeof(LOCAL_DAGISEL_STRINGIZE(GET_DAGISEL_BODY)) > 1,"
|
|
"\n";
|
|
OS << " \"GET_DAGISEL_BODY is empty: it should be defined with the class "
|
|
"name\");\n";
|
|
OS << "#undef LOCAL_DAGISEL_STRINGIZE_\n";
|
|
OS << "#undef LOCAL_DAGISEL_STRINGIZE\n";
|
|
OS << "#endif\n\n";
|
|
|
|
OS << "#if !defined(GET_DAGISEL_DECL) && !defined(GET_DAGISEL_BODY)\n";
|
|
OS << "#define DAGISEL_INLINE 1\n";
|
|
OS << "#else\n";
|
|
OS << "#define DAGISEL_INLINE 0\n";
|
|
OS << "#endif\n\n";
|
|
|
|
OS << "#if !DAGISEL_INLINE\n";
|
|
OS << "#define DAGISEL_CLASS_COLONCOLON GET_DAGISEL_BODY ::\n";
|
|
OS << "#else\n";
|
|
OS << "#define DAGISEL_CLASS_COLONCOLON\n";
|
|
OS << "#endif\n\n";
|
|
|
|
BeginEmitFunction(OS, "void", "SelectCode(SDNode *N)", false /*AddOverride*/);
|
|
MatcherTableEmitter MatcherEmitter(TheMatcher, CGP);
|
|
|
|
// First we size all the children of the three kinds of matchers that have
|
|
// them. This is done by sharing the code in EmitMatcher(). but we don't
|
|
// want to emit anything, so we turn off comments and use a null stream.
|
|
bool SaveOmitComments = OmitComments;
|
|
OmitComments = true;
|
|
raw_null_ostream NullOS;
|
|
unsigned TotalSize = MatcherEmitter.SizeMatcherList(TheMatcher, NullOS);
|
|
OmitComments = SaveOmitComments;
|
|
|
|
// Now that the matchers are sized, we can emit the code for them to the
|
|
// final stream.
|
|
OS << "{\n";
|
|
OS << " // Some target values are emitted as 2 bytes, TARGET_VAL handles\n";
|
|
OS << " // this. Coverage indexes are emitted as 4 bytes,\n";
|
|
OS << " // COVERAGE_IDX_VAL handles this.\n";
|
|
OS << " #define TARGET_VAL(X) X & 255, unsigned(X) >> 8\n";
|
|
OS << " #define COVERAGE_IDX_VAL(X) X & 255, (unsigned(X) >> 8) & 255, ";
|
|
OS << "(unsigned(X) >> 16) & 255, (unsigned(X) >> 24) & 255\n";
|
|
OS << " static const uint8_t MatcherTable[] = {\n";
|
|
TotalSize = MatcherEmitter.EmitMatcherList(TheMatcher, 1, 0, OS);
|
|
OS << " 0\n }; // Total Array size is " << (TotalSize + 1)
|
|
<< " bytes\n\n";
|
|
|
|
MatcherEmitter.EmitHistogram(TheMatcher, OS);
|
|
|
|
OS << " #undef COVERAGE_IDX_VAL\n";
|
|
OS << " #undef TARGET_VAL\n";
|
|
OS << " SelectCodeCommon(N, MatcherTable, sizeof(MatcherTable));\n";
|
|
OS << "}\n";
|
|
EndEmitFunction(OS);
|
|
|
|
// Next up, emit the function for node and pattern predicates:
|
|
MatcherEmitter.EmitPredicateFunctions(OS);
|
|
|
|
MatcherEmitter.EmitValueTypeFunction(OS);
|
|
|
|
if (InstrumentCoverage)
|
|
MatcherEmitter.EmitPatternMatchTable(OS);
|
|
|
|
// Clean up the preprocessor macros.
|
|
OS << "\n";
|
|
OS << "#ifdef DAGISEL_INLINE\n";
|
|
OS << "#undef DAGISEL_INLINE\n";
|
|
OS << "#endif\n";
|
|
OS << "#ifdef DAGISEL_CLASS_COLONCOLON\n";
|
|
OS << "#undef DAGISEL_CLASS_COLONCOLON\n";
|
|
OS << "#endif\n";
|
|
OS << "#ifdef GET_DAGISEL_DECL\n";
|
|
OS << "#undef GET_DAGISEL_DECL\n";
|
|
OS << "#endif\n";
|
|
OS << "#ifdef GET_DAGISEL_BODY\n";
|
|
OS << "#undef GET_DAGISEL_BODY\n";
|
|
OS << "#endif\n";
|
|
}
|