2322 lines
88 KiB
C++
2322 lines
88 KiB
C++
|
//===- GlobalISelMatchTable.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
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "GlobalISelMatchTable.h"
|
||
|
#include "CodeGenInstruction.h"
|
||
|
#include "CodeGenRegisters.h"
|
||
|
#include "llvm/ADT/Statistic.h"
|
||
|
#include "llvm/Support/Debug.h"
|
||
|
#include "llvm/Support/LEB128.h"
|
||
|
#include "llvm/Support/ScopedPrinter.h"
|
||
|
#include "llvm/Support/raw_ostream.h"
|
||
|
#include "llvm/TableGen/Error.h"
|
||
|
|
||
|
#define DEBUG_TYPE "gi-match-table"
|
||
|
|
||
|
STATISTIC(NumPatternEmitted, "Number of patterns emitted");
|
||
|
|
||
|
namespace llvm {
|
||
|
namespace gi {
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
Error failUnsupported(const Twine &Reason) {
|
||
|
return make_error<StringError>(Reason, inconvertibleErrorCode());
|
||
|
}
|
||
|
|
||
|
/// Get the name of the enum value used to number the predicate function.
|
||
|
std::string getEnumNameForPredicate(const TreePredicateFn &Predicate) {
|
||
|
if (Predicate.hasGISelPredicateCode())
|
||
|
return "GICXXPred_MI_" + Predicate.getFnName();
|
||
|
return "GICXXPred_" + Predicate.getImmTypeIdentifier().str() + "_" +
|
||
|
Predicate.getFnName();
|
||
|
}
|
||
|
|
||
|
std::string getMatchOpcodeForImmPredicate(const TreePredicateFn &Predicate) {
|
||
|
return "GIM_Check" + Predicate.getImmTypeIdentifier().str() + "ImmPredicate";
|
||
|
}
|
||
|
|
||
|
// GIMT_Encode2/4/8
|
||
|
constexpr StringLiteral EncodeMacroName = "GIMT_Encode";
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
//===- Helpers ------------------------------------------------------------===//
|
||
|
|
||
|
void emitEncodingMacrosDef(raw_ostream &OS) {
|
||
|
OS << "#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n"
|
||
|
<< "#define " << EncodeMacroName << "2(Val)"
|
||
|
<< " uint8_t(Val), uint8_t((uint16_t)Val >> 8)\n"
|
||
|
<< "#define " << EncodeMacroName << "4(Val)"
|
||
|
<< " uint8_t(Val), uint8_t((uint32_t)Val >> 8), "
|
||
|
"uint8_t((uint32_t)Val >> 16), uint8_t((uint32_t)Val >> 24)\n"
|
||
|
<< "#define " << EncodeMacroName << "8(Val)"
|
||
|
<< " uint8_t(Val), uint8_t((uint64_t)Val >> 8), "
|
||
|
"uint8_t((uint64_t)Val >> 16), uint8_t((uint64_t)Val >> 24), "
|
||
|
"uint8_t((uint64_t)Val >> 32), uint8_t((uint64_t)Val >> 40), "
|
||
|
"uint8_t((uint64_t)Val >> 48), uint8_t((uint64_t)Val >> 56)\n"
|
||
|
<< "#else\n"
|
||
|
<< "#define " << EncodeMacroName << "2(Val)"
|
||
|
<< " uint8_t((uint16_t)Val >> 8), uint8_t(Val)\n"
|
||
|
<< "#define " << EncodeMacroName << "4(Val)"
|
||
|
<< " uint8_t((uint32_t)Val >> 24), uint8_t((uint32_t)Val >> 16), "
|
||
|
"uint8_t((uint32_t)Val >> 8), uint8_t(Val)\n"
|
||
|
<< "#define " << EncodeMacroName << "8(Val)"
|
||
|
<< " uint8_t((uint64_t)Val >> 56), uint8_t((uint64_t)Val >> 48), "
|
||
|
"uint8_t((uint64_t)Val >> 40), uint8_t((uint64_t)Val >> 32), "
|
||
|
"uint8_t((uint64_t)Val >> 24), uint8_t((uint64_t)Val >> 16), "
|
||
|
"uint8_t((uint64_t)Val >> 8), uint8_t(Val)\n"
|
||
|
<< "#endif\n";
|
||
|
}
|
||
|
|
||
|
void emitEncodingMacrosUndef(raw_ostream &OS) {
|
||
|
OS << "#undef " << EncodeMacroName << "2\n"
|
||
|
<< "#undef " << EncodeMacroName << "4\n"
|
||
|
<< "#undef " << EncodeMacroName << "8\n";
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
template <class GroupT>
|
||
|
std::vector<Matcher *>
|
||
|
optimizeRules(ArrayRef<Matcher *> Rules,
|
||
|
std::vector<std::unique_ptr<Matcher>> &MatcherStorage) {
|
||
|
|
||
|
std::vector<Matcher *> OptRules;
|
||
|
std::unique_ptr<GroupT> CurrentGroup = std::make_unique<GroupT>();
|
||
|
assert(CurrentGroup->empty() && "Newly created group isn't empty!");
|
||
|
unsigned NumGroups = 0;
|
||
|
|
||
|
auto ProcessCurrentGroup = [&]() {
|
||
|
if (CurrentGroup->empty())
|
||
|
// An empty group is good to be reused:
|
||
|
return;
|
||
|
|
||
|
// If the group isn't large enough to provide any benefit, move all the
|
||
|
// added rules out of it and make sure to re-create the group to properly
|
||
|
// re-initialize it:
|
||
|
if (CurrentGroup->size() < 2)
|
||
|
append_range(OptRules, CurrentGroup->matchers());
|
||
|
else {
|
||
|
CurrentGroup->finalize();
|
||
|
OptRules.push_back(CurrentGroup.get());
|
||
|
MatcherStorage.emplace_back(std::move(CurrentGroup));
|
||
|
++NumGroups;
|
||
|
}
|
||
|
CurrentGroup = std::make_unique<GroupT>();
|
||
|
};
|
||
|
for (Matcher *Rule : Rules) {
|
||
|
// Greedily add as many matchers as possible to the current group:
|
||
|
if (CurrentGroup->addMatcher(*Rule))
|
||
|
continue;
|
||
|
|
||
|
ProcessCurrentGroup();
|
||
|
assert(CurrentGroup->empty() && "A group wasn't properly re-initialized");
|
||
|
|
||
|
// Try to add the pending matcher to a newly created empty group:
|
||
|
if (!CurrentGroup->addMatcher(*Rule))
|
||
|
// If we couldn't add the matcher to an empty group, that group type
|
||
|
// doesn't support that kind of matchers at all, so just skip it:
|
||
|
OptRules.push_back(Rule);
|
||
|
}
|
||
|
ProcessCurrentGroup();
|
||
|
|
||
|
LLVM_DEBUG(dbgs() << "NumGroups: " << NumGroups << "\n");
|
||
|
(void)NumGroups;
|
||
|
assert(CurrentGroup->empty() && "The last group wasn't properly processed");
|
||
|
return OptRules;
|
||
|
}
|
||
|
|
||
|
template std::vector<Matcher *> optimizeRules<GroupMatcher>(
|
||
|
ArrayRef<Matcher *> Rules,
|
||
|
std::vector<std::unique_ptr<Matcher>> &MatcherStorage);
|
||
|
|
||
|
template std::vector<Matcher *> optimizeRules<SwitchMatcher>(
|
||
|
ArrayRef<Matcher *> Rules,
|
||
|
std::vector<std::unique_ptr<Matcher>> &MatcherStorage);
|
||
|
|
||
|
static std::string getEncodedEmitStr(StringRef NamedValue, unsigned NumBytes) {
|
||
|
if (NumBytes == 2 || NumBytes == 4 || NumBytes == 8)
|
||
|
return (EncodeMacroName + Twine(NumBytes) + "(" + NamedValue + ")").str();
|
||
|
llvm_unreachable("Unsupported number of bytes!");
|
||
|
}
|
||
|
|
||
|
//===- Global Data --------------------------------------------------------===//
|
||
|
|
||
|
std::set<LLTCodeGen> KnownTypes;
|
||
|
|
||
|
//===- MatchTableRecord ---------------------------------------------------===//
|
||
|
|
||
|
void MatchTableRecord::emit(raw_ostream &OS, bool LineBreakIsNextAfterThis,
|
||
|
const MatchTable &Table) const {
|
||
|
bool UseLineComment =
|
||
|
LineBreakIsNextAfterThis || (Flags & MTRF_LineBreakFollows);
|
||
|
if (Flags & (MTRF_JumpTarget | MTRF_CommaFollows))
|
||
|
UseLineComment = false;
|
||
|
|
||
|
if (Flags & MTRF_Comment)
|
||
|
OS << (UseLineComment ? "// " : "/*");
|
||
|
|
||
|
if (NumElements > 1 && !(Flags & (MTRF_PreEncoded | MTRF_Comment)))
|
||
|
OS << getEncodedEmitStr(EmitStr, NumElements);
|
||
|
else
|
||
|
OS << EmitStr;
|
||
|
|
||
|
if (Flags & MTRF_Label)
|
||
|
OS << ": @" << Table.getLabelIndex(LabelID);
|
||
|
|
||
|
if ((Flags & MTRF_Comment) && !UseLineComment)
|
||
|
OS << "*/";
|
||
|
|
||
|
if (Flags & MTRF_JumpTarget) {
|
||
|
if (Flags & MTRF_Comment)
|
||
|
OS << " ";
|
||
|
// TODO: Could encode this AOT to speed up build of generated file
|
||
|
OS << getEncodedEmitStr(llvm::to_string(Table.getLabelIndex(LabelID)),
|
||
|
NumElements);
|
||
|
}
|
||
|
|
||
|
if (Flags & MTRF_CommaFollows) {
|
||
|
OS << ",";
|
||
|
if (!LineBreakIsNextAfterThis && !(Flags & MTRF_LineBreakFollows))
|
||
|
OS << " ";
|
||
|
}
|
||
|
|
||
|
if (Flags & MTRF_LineBreakFollows)
|
||
|
OS << "\n";
|
||
|
}
|
||
|
|
||
|
//===- MatchTable ---------------------------------------------------------===//
|
||
|
|
||
|
MatchTableRecord MatchTable::LineBreak = {
|
||
|
std::nullopt, "" /* Emit String */, 0 /* Elements */,
|
||
|
MatchTableRecord::MTRF_LineBreakFollows};
|
||
|
|
||
|
MatchTableRecord MatchTable::Comment(StringRef Comment) {
|
||
|
return MatchTableRecord(std::nullopt, Comment, 0,
|
||
|
MatchTableRecord::MTRF_Comment);
|
||
|
}
|
||
|
|
||
|
MatchTableRecord MatchTable::Opcode(StringRef Opcode, int IndentAdjust) {
|
||
|
unsigned ExtraFlags = 0;
|
||
|
if (IndentAdjust > 0)
|
||
|
ExtraFlags |= MatchTableRecord::MTRF_Indent;
|
||
|
if (IndentAdjust < 0)
|
||
|
ExtraFlags |= MatchTableRecord::MTRF_Outdent;
|
||
|
|
||
|
return MatchTableRecord(std::nullopt, Opcode, 1,
|
||
|
MatchTableRecord::MTRF_CommaFollows | ExtraFlags);
|
||
|
}
|
||
|
|
||
|
MatchTableRecord MatchTable::NamedValue(unsigned NumBytes,
|
||
|
StringRef NamedValue) {
|
||
|
return MatchTableRecord(std::nullopt, NamedValue, NumBytes,
|
||
|
MatchTableRecord::MTRF_CommaFollows);
|
||
|
}
|
||
|
|
||
|
MatchTableRecord MatchTable::NamedValue(unsigned NumBytes, StringRef NamedValue,
|
||
|
int64_t RawValue) {
|
||
|
return MatchTableRecord(std::nullopt, NamedValue, NumBytes,
|
||
|
MatchTableRecord::MTRF_CommaFollows, RawValue);
|
||
|
}
|
||
|
|
||
|
MatchTableRecord MatchTable::NamedValue(unsigned NumBytes, StringRef Namespace,
|
||
|
StringRef NamedValue) {
|
||
|
return MatchTableRecord(std::nullopt, (Namespace + "::" + NamedValue).str(),
|
||
|
NumBytes, MatchTableRecord::MTRF_CommaFollows);
|
||
|
}
|
||
|
|
||
|
MatchTableRecord MatchTable::NamedValue(unsigned NumBytes, StringRef Namespace,
|
||
|
StringRef NamedValue,
|
||
|
int64_t RawValue) {
|
||
|
return MatchTableRecord(std::nullopt, (Namespace + "::" + NamedValue).str(),
|
||
|
NumBytes, MatchTableRecord::MTRF_CommaFollows,
|
||
|
RawValue);
|
||
|
}
|
||
|
|
||
|
MatchTableRecord MatchTable::IntValue(unsigned NumBytes, int64_t IntValue) {
|
||
|
assert(isUIntN(NumBytes * 8, IntValue) || isIntN(NumBytes * 8, IntValue));
|
||
|
auto Str = llvm::to_string(IntValue);
|
||
|
if (NumBytes == 1 && IntValue < 0)
|
||
|
Str = "uint8_t(" + Str + ")";
|
||
|
// TODO: Could optimize this directly to save the compiler some work when
|
||
|
// building the file
|
||
|
return MatchTableRecord(std::nullopt, Str, NumBytes,
|
||
|
MatchTableRecord::MTRF_CommaFollows);
|
||
|
}
|
||
|
|
||
|
MatchTableRecord MatchTable::ULEB128Value(uint64_t IntValue) {
|
||
|
uint8_t Buffer[10];
|
||
|
unsigned Len = encodeULEB128(IntValue, Buffer);
|
||
|
|
||
|
// Simple case (most common)
|
||
|
if (Len == 1) {
|
||
|
return MatchTableRecord(std::nullopt, llvm::to_string((unsigned)Buffer[0]),
|
||
|
1, MatchTableRecord::MTRF_CommaFollows);
|
||
|
}
|
||
|
|
||
|
// Print it as, e.g. /* -123456 (*/, 0xC0, 0xBB, 0x78 /*)*/
|
||
|
std::string Str;
|
||
|
raw_string_ostream OS(Str);
|
||
|
OS << "/* " << llvm::to_string(IntValue) << "(*/";
|
||
|
for (unsigned K = 0; K < Len; ++K) {
|
||
|
if (K)
|
||
|
OS << ", ";
|
||
|
OS << "0x" << llvm::toHex({Buffer[K]});
|
||
|
}
|
||
|
OS << "/*)*/";
|
||
|
return MatchTableRecord(std::nullopt, Str, Len,
|
||
|
MatchTableRecord::MTRF_CommaFollows |
|
||
|
MatchTableRecord::MTRF_PreEncoded);
|
||
|
}
|
||
|
|
||
|
MatchTableRecord MatchTable::Label(unsigned LabelID) {
|
||
|
return MatchTableRecord(LabelID, "Label " + llvm::to_string(LabelID), 0,
|
||
|
MatchTableRecord::MTRF_Label |
|
||
|
MatchTableRecord::MTRF_Comment |
|
||
|
MatchTableRecord::MTRF_LineBreakFollows);
|
||
|
}
|
||
|
|
||
|
MatchTableRecord MatchTable::JumpTarget(unsigned LabelID) {
|
||
|
return MatchTableRecord(LabelID, "Label " + llvm::to_string(LabelID), 4,
|
||
|
MatchTableRecord::MTRF_JumpTarget |
|
||
|
MatchTableRecord::MTRF_Comment |
|
||
|
MatchTableRecord::MTRF_CommaFollows);
|
||
|
}
|
||
|
|
||
|
void MatchTable::emitUse(raw_ostream &OS) const { OS << "MatchTable" << ID; }
|
||
|
|
||
|
void MatchTable::emitDeclaration(raw_ostream &OS) const {
|
||
|
unsigned Indentation = 4;
|
||
|
OS << " constexpr static uint8_t MatchTable" << ID << "[] = {";
|
||
|
LineBreak.emit(OS, true, *this);
|
||
|
OS << std::string(Indentation, ' ');
|
||
|
|
||
|
for (auto I = Contents.begin(), E = Contents.end(); I != E; ++I) {
|
||
|
bool LineBreakIsNext = false;
|
||
|
const auto &NextI = std::next(I);
|
||
|
|
||
|
if (NextI != E) {
|
||
|
if (NextI->EmitStr == "" &&
|
||
|
NextI->Flags == MatchTableRecord::MTRF_LineBreakFollows)
|
||
|
LineBreakIsNext = true;
|
||
|
}
|
||
|
|
||
|
if (I->Flags & MatchTableRecord::MTRF_Indent)
|
||
|
Indentation += 2;
|
||
|
|
||
|
I->emit(OS, LineBreakIsNext, *this);
|
||
|
if (I->Flags & MatchTableRecord::MTRF_LineBreakFollows)
|
||
|
OS << std::string(Indentation, ' ');
|
||
|
|
||
|
if (I->Flags & MatchTableRecord::MTRF_Outdent)
|
||
|
Indentation -= 2;
|
||
|
}
|
||
|
OS << "}; // Size: " << CurrentSize << " bytes\n";
|
||
|
}
|
||
|
|
||
|
MatchTable MatchTable::buildTable(ArrayRef<Matcher *> Rules, bool WithCoverage,
|
||
|
bool IsCombiner) {
|
||
|
MatchTable Table(WithCoverage, IsCombiner);
|
||
|
for (Matcher *Rule : Rules)
|
||
|
Rule->emit(Table);
|
||
|
|
||
|
return Table << MatchTable::Opcode("GIM_Reject") << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- LLTCodeGen ---------------------------------------------------------===//
|
||
|
|
||
|
std::string LLTCodeGen::getCxxEnumValue() const {
|
||
|
std::string Str;
|
||
|
raw_string_ostream OS(Str);
|
||
|
|
||
|
emitCxxEnumValue(OS);
|
||
|
return Str;
|
||
|
}
|
||
|
|
||
|
void LLTCodeGen::emitCxxEnumValue(raw_ostream &OS) const {
|
||
|
if (Ty.isScalar()) {
|
||
|
OS << "GILLT_s" << Ty.getSizeInBits();
|
||
|
return;
|
||
|
}
|
||
|
if (Ty.isVector()) {
|
||
|
OS << (Ty.isScalable() ? "GILLT_nxv" : "GILLT_v")
|
||
|
<< Ty.getElementCount().getKnownMinValue() << "s"
|
||
|
<< Ty.getScalarSizeInBits();
|
||
|
return;
|
||
|
}
|
||
|
if (Ty.isPointer()) {
|
||
|
OS << "GILLT_p" << Ty.getAddressSpace();
|
||
|
if (Ty.getSizeInBits() > 0)
|
||
|
OS << "s" << Ty.getSizeInBits();
|
||
|
return;
|
||
|
}
|
||
|
llvm_unreachable("Unhandled LLT");
|
||
|
}
|
||
|
|
||
|
void LLTCodeGen::emitCxxConstructorCall(raw_ostream &OS) const {
|
||
|
if (Ty.isScalar()) {
|
||
|
OS << "LLT::scalar(" << Ty.getSizeInBits() << ")";
|
||
|
return;
|
||
|
}
|
||
|
if (Ty.isVector()) {
|
||
|
OS << "LLT::vector("
|
||
|
<< (Ty.isScalable() ? "ElementCount::getScalable("
|
||
|
: "ElementCount::getFixed(")
|
||
|
<< Ty.getElementCount().getKnownMinValue() << "), "
|
||
|
<< Ty.getScalarSizeInBits() << ")";
|
||
|
return;
|
||
|
}
|
||
|
if (Ty.isPointer() && Ty.getSizeInBits() > 0) {
|
||
|
OS << "LLT::pointer(" << Ty.getAddressSpace() << ", " << Ty.getSizeInBits()
|
||
|
<< ")";
|
||
|
return;
|
||
|
}
|
||
|
llvm_unreachable("Unhandled LLT");
|
||
|
}
|
||
|
|
||
|
/// This ordering is used for std::unique() and llvm::sort(). There's no
|
||
|
/// particular logic behind the order but either A < B or B < A must be
|
||
|
/// true if A != B.
|
||
|
bool LLTCodeGen::operator<(const LLTCodeGen &Other) const {
|
||
|
if (Ty.isValid() != Other.Ty.isValid())
|
||
|
return Ty.isValid() < Other.Ty.isValid();
|
||
|
if (!Ty.isValid())
|
||
|
return false;
|
||
|
|
||
|
if (Ty.isVector() != Other.Ty.isVector())
|
||
|
return Ty.isVector() < Other.Ty.isVector();
|
||
|
if (Ty.isScalar() != Other.Ty.isScalar())
|
||
|
return Ty.isScalar() < Other.Ty.isScalar();
|
||
|
if (Ty.isPointer() != Other.Ty.isPointer())
|
||
|
return Ty.isPointer() < Other.Ty.isPointer();
|
||
|
|
||
|
if (Ty.isPointer() && Ty.getAddressSpace() != Other.Ty.getAddressSpace())
|
||
|
return Ty.getAddressSpace() < Other.Ty.getAddressSpace();
|
||
|
|
||
|
if (Ty.isVector() && Ty.getElementCount() != Other.Ty.getElementCount())
|
||
|
return std::make_tuple(Ty.isScalable(),
|
||
|
Ty.getElementCount().getKnownMinValue()) <
|
||
|
std::make_tuple(Other.Ty.isScalable(),
|
||
|
Other.Ty.getElementCount().getKnownMinValue());
|
||
|
|
||
|
assert((!Ty.isVector() || Ty.isScalable() == Other.Ty.isScalable()) &&
|
||
|
"Unexpected mismatch of scalable property");
|
||
|
return Ty.isVector()
|
||
|
? std::make_tuple(Ty.isScalable(),
|
||
|
Ty.getSizeInBits().getKnownMinValue()) <
|
||
|
std::make_tuple(Other.Ty.isScalable(),
|
||
|
Other.Ty.getSizeInBits().getKnownMinValue())
|
||
|
: Ty.getSizeInBits().getFixedValue() <
|
||
|
Other.Ty.getSizeInBits().getFixedValue();
|
||
|
}
|
||
|
|
||
|
//===- LLTCodeGen Helpers -------------------------------------------------===//
|
||
|
|
||
|
std::optional<LLTCodeGen> MVTToLLT(MVT::SimpleValueType SVT) {
|
||
|
MVT VT(SVT);
|
||
|
|
||
|
if (VT.isVector() && !VT.getVectorElementCount().isScalar())
|
||
|
return LLTCodeGen(
|
||
|
LLT::vector(VT.getVectorElementCount(), VT.getScalarSizeInBits()));
|
||
|
|
||
|
if (VT.isInteger() || VT.isFloatingPoint())
|
||
|
return LLTCodeGen(LLT::scalar(VT.getSizeInBits()));
|
||
|
|
||
|
return std::nullopt;
|
||
|
}
|
||
|
|
||
|
//===- Matcher ------------------------------------------------------------===//
|
||
|
|
||
|
void Matcher::optimize() {}
|
||
|
|
||
|
Matcher::~Matcher() {}
|
||
|
|
||
|
//===- GroupMatcher -------------------------------------------------------===//
|
||
|
|
||
|
bool GroupMatcher::candidateConditionMatches(
|
||
|
const PredicateMatcher &Predicate) const {
|
||
|
|
||
|
if (empty()) {
|
||
|
// Sharing predicates for nested instructions is not supported yet as we
|
||
|
// currently don't hoist the GIM_RecordInsn's properly, therefore we can
|
||
|
// only work on the original root instruction (InsnVarID == 0):
|
||
|
if (Predicate.getInsnVarID() != 0)
|
||
|
return false;
|
||
|
// ... otherwise an empty group can handle any predicate with no specific
|
||
|
// requirements:
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
const Matcher &Representative = **Matchers.begin();
|
||
|
const auto &RepresentativeCondition = Representative.getFirstCondition();
|
||
|
// ... if not empty, the group can only accomodate matchers with the exact
|
||
|
// same first condition:
|
||
|
return Predicate.isIdentical(RepresentativeCondition);
|
||
|
}
|
||
|
|
||
|
bool GroupMatcher::addMatcher(Matcher &Candidate) {
|
||
|
if (!Candidate.hasFirstCondition())
|
||
|
return false;
|
||
|
|
||
|
const PredicateMatcher &Predicate = Candidate.getFirstCondition();
|
||
|
if (!candidateConditionMatches(Predicate))
|
||
|
return false;
|
||
|
|
||
|
Matchers.push_back(&Candidate);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void GroupMatcher::finalize() {
|
||
|
assert(Conditions.empty() && "Already finalized?");
|
||
|
if (empty())
|
||
|
return;
|
||
|
|
||
|
Matcher &FirstRule = **Matchers.begin();
|
||
|
for (;;) {
|
||
|
// All the checks are expected to succeed during the first iteration:
|
||
|
for (const auto &Rule : Matchers)
|
||
|
if (!Rule->hasFirstCondition())
|
||
|
return;
|
||
|
const auto &FirstCondition = FirstRule.getFirstCondition();
|
||
|
for (unsigned I = 1, E = Matchers.size(); I < E; ++I)
|
||
|
if (!Matchers[I]->getFirstCondition().isIdentical(FirstCondition))
|
||
|
return;
|
||
|
|
||
|
Conditions.push_back(FirstRule.popFirstCondition());
|
||
|
for (unsigned I = 1, E = Matchers.size(); I < E; ++I)
|
||
|
Matchers[I]->popFirstCondition();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GroupMatcher::emit(MatchTable &Table) {
|
||
|
unsigned LabelID = ~0U;
|
||
|
if (!Conditions.empty()) {
|
||
|
LabelID = Table.allocateLabelID();
|
||
|
Table << MatchTable::Opcode("GIM_Try", +1)
|
||
|
<< MatchTable::Comment("On fail goto")
|
||
|
<< MatchTable::JumpTarget(LabelID) << MatchTable::LineBreak;
|
||
|
}
|
||
|
for (auto &Condition : Conditions)
|
||
|
Condition->emitPredicateOpcodes(
|
||
|
Table, *static_cast<RuleMatcher *>(*Matchers.begin()));
|
||
|
|
||
|
for (const auto &M : Matchers)
|
||
|
M->emit(Table);
|
||
|
|
||
|
// Exit the group
|
||
|
if (!Conditions.empty())
|
||
|
Table << MatchTable::Opcode("GIM_Reject", -1) << MatchTable::LineBreak
|
||
|
<< MatchTable::Label(LabelID);
|
||
|
}
|
||
|
|
||
|
void GroupMatcher::optimize() {
|
||
|
// Make sure we only sort by a specific predicate within a range of rules that
|
||
|
// all have that predicate checked against a specific value (not a wildcard):
|
||
|
auto F = Matchers.begin();
|
||
|
auto T = F;
|
||
|
auto E = Matchers.end();
|
||
|
while (T != E) {
|
||
|
while (T != E) {
|
||
|
auto *R = static_cast<RuleMatcher *>(*T);
|
||
|
if (!R->getFirstConditionAsRootType().get().isValid())
|
||
|
break;
|
||
|
++T;
|
||
|
}
|
||
|
std::stable_sort(F, T, [](Matcher *A, Matcher *B) {
|
||
|
auto *L = static_cast<RuleMatcher *>(A);
|
||
|
auto *R = static_cast<RuleMatcher *>(B);
|
||
|
return L->getFirstConditionAsRootType() <
|
||
|
R->getFirstConditionAsRootType();
|
||
|
});
|
||
|
if (T != E)
|
||
|
F = ++T;
|
||
|
}
|
||
|
optimizeRules<GroupMatcher>(Matchers, MatcherStorage).swap(Matchers);
|
||
|
optimizeRules<SwitchMatcher>(Matchers, MatcherStorage).swap(Matchers);
|
||
|
}
|
||
|
|
||
|
//===- SwitchMatcher ------------------------------------------------------===//
|
||
|
|
||
|
bool SwitchMatcher::isSupportedPredicateType(const PredicateMatcher &P) {
|
||
|
return isa<InstructionOpcodeMatcher>(P) || isa<LLTOperandMatcher>(P);
|
||
|
}
|
||
|
|
||
|
bool SwitchMatcher::candidateConditionMatches(
|
||
|
const PredicateMatcher &Predicate) const {
|
||
|
|
||
|
if (empty()) {
|
||
|
// Sharing predicates for nested instructions is not supported yet as we
|
||
|
// currently don't hoist the GIM_RecordInsn's properly, therefore we can
|
||
|
// only work on the original root instruction (InsnVarID == 0):
|
||
|
if (Predicate.getInsnVarID() != 0)
|
||
|
return false;
|
||
|
// ... while an attempt to add even a root matcher to an empty SwitchMatcher
|
||
|
// could fail as not all the types of conditions are supported:
|
||
|
if (!isSupportedPredicateType(Predicate))
|
||
|
return false;
|
||
|
// ... or the condition might not have a proper implementation of
|
||
|
// getValue() / isIdenticalDownToValue() yet:
|
||
|
if (!Predicate.hasValue())
|
||
|
return false;
|
||
|
// ... otherwise an empty Switch can accomodate the condition with no
|
||
|
// further requirements:
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
const Matcher &CaseRepresentative = **Matchers.begin();
|
||
|
const auto &RepresentativeCondition = CaseRepresentative.getFirstCondition();
|
||
|
// Switch-cases must share the same kind of condition and path to the value it
|
||
|
// checks:
|
||
|
if (!Predicate.isIdenticalDownToValue(RepresentativeCondition))
|
||
|
return false;
|
||
|
|
||
|
const auto Value = Predicate.getValue();
|
||
|
// ... but be unique with respect to the actual value they check:
|
||
|
return Values.count(Value) == 0;
|
||
|
}
|
||
|
|
||
|
bool SwitchMatcher::addMatcher(Matcher &Candidate) {
|
||
|
if (!Candidate.hasFirstCondition())
|
||
|
return false;
|
||
|
|
||
|
const PredicateMatcher &Predicate = Candidate.getFirstCondition();
|
||
|
if (!candidateConditionMatches(Predicate))
|
||
|
return false;
|
||
|
const auto Value = Predicate.getValue();
|
||
|
Values.insert(Value);
|
||
|
|
||
|
Matchers.push_back(&Candidate);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void SwitchMatcher::finalize() {
|
||
|
assert(Condition == nullptr && "Already finalized");
|
||
|
assert(Values.size() == Matchers.size() && "Broken SwitchMatcher");
|
||
|
if (empty())
|
||
|
return;
|
||
|
|
||
|
llvm::stable_sort(Matchers, [](const Matcher *L, const Matcher *R) {
|
||
|
return L->getFirstCondition().getValue() <
|
||
|
R->getFirstCondition().getValue();
|
||
|
});
|
||
|
Condition = Matchers[0]->popFirstCondition();
|
||
|
for (unsigned I = 1, E = Values.size(); I < E; ++I)
|
||
|
Matchers[I]->popFirstCondition();
|
||
|
}
|
||
|
|
||
|
void SwitchMatcher::emitPredicateSpecificOpcodes(const PredicateMatcher &P,
|
||
|
MatchTable &Table) {
|
||
|
assert(isSupportedPredicateType(P) && "Predicate type is not supported");
|
||
|
|
||
|
if (const auto *Condition = dyn_cast<InstructionOpcodeMatcher>(&P)) {
|
||
|
Table << MatchTable::Opcode("GIM_SwitchOpcode") << MatchTable::Comment("MI")
|
||
|
<< MatchTable::ULEB128Value(Condition->getInsnVarID());
|
||
|
return;
|
||
|
}
|
||
|
if (const auto *Condition = dyn_cast<LLTOperandMatcher>(&P)) {
|
||
|
Table << MatchTable::Opcode("GIM_SwitchType") << MatchTable::Comment("MI")
|
||
|
<< MatchTable::ULEB128Value(Condition->getInsnVarID())
|
||
|
<< MatchTable::Comment("Op")
|
||
|
<< MatchTable::ULEB128Value(Condition->getOpIdx());
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
llvm_unreachable("emitPredicateSpecificOpcodes is broken: can not handle a "
|
||
|
"predicate type that is claimed to be supported");
|
||
|
}
|
||
|
|
||
|
void SwitchMatcher::emit(MatchTable &Table) {
|
||
|
assert(Values.size() == Matchers.size() && "Broken SwitchMatcher");
|
||
|
if (empty())
|
||
|
return;
|
||
|
assert(Condition != nullptr &&
|
||
|
"Broken SwitchMatcher, hasn't been finalized?");
|
||
|
|
||
|
std::vector<unsigned> LabelIDs(Values.size());
|
||
|
std::generate(LabelIDs.begin(), LabelIDs.end(),
|
||
|
[&Table]() { return Table.allocateLabelID(); });
|
||
|
const unsigned Default = Table.allocateLabelID();
|
||
|
|
||
|
const int64_t LowerBound = Values.begin()->getRawValue();
|
||
|
const int64_t UpperBound = Values.rbegin()->getRawValue() + 1;
|
||
|
|
||
|
emitPredicateSpecificOpcodes(*Condition, Table);
|
||
|
|
||
|
Table << MatchTable::Comment("[") << MatchTable::IntValue(2, LowerBound)
|
||
|
<< MatchTable::IntValue(2, UpperBound) << MatchTable::Comment(")")
|
||
|
<< MatchTable::Comment("default:") << MatchTable::JumpTarget(Default);
|
||
|
|
||
|
int64_t J = LowerBound;
|
||
|
auto VI = Values.begin();
|
||
|
for (unsigned I = 0, E = Values.size(); I < E; ++I) {
|
||
|
auto V = *VI++;
|
||
|
while (J++ < V.getRawValue())
|
||
|
Table << MatchTable::IntValue(4, 0);
|
||
|
V.turnIntoComment();
|
||
|
Table << MatchTable::LineBreak << V << MatchTable::JumpTarget(LabelIDs[I]);
|
||
|
}
|
||
|
Table << MatchTable::LineBreak;
|
||
|
|
||
|
for (unsigned I = 0, E = Values.size(); I < E; ++I) {
|
||
|
Table << MatchTable::Label(LabelIDs[I]);
|
||
|
Matchers[I]->emit(Table);
|
||
|
Table << MatchTable::Opcode("GIM_Reject") << MatchTable::LineBreak;
|
||
|
}
|
||
|
Table << MatchTable::Label(Default);
|
||
|
}
|
||
|
|
||
|
//===- RuleMatcher --------------------------------------------------------===//
|
||
|
|
||
|
uint64_t RuleMatcher::NextRuleID = 0;
|
||
|
|
||
|
StringRef RuleMatcher::getOpcode() const {
|
||
|
return Matchers.front()->getOpcode();
|
||
|
}
|
||
|
|
||
|
unsigned RuleMatcher::getNumOperands() const {
|
||
|
return Matchers.front()->getNumOperands();
|
||
|
}
|
||
|
|
||
|
LLTCodeGen RuleMatcher::getFirstConditionAsRootType() {
|
||
|
InstructionMatcher &InsnMatcher = *Matchers.front();
|
||
|
if (!InsnMatcher.predicates_empty())
|
||
|
if (const auto *TM =
|
||
|
dyn_cast<LLTOperandMatcher>(&**InsnMatcher.predicates_begin()))
|
||
|
if (TM->getInsnVarID() == 0 && TM->getOpIdx() == 0)
|
||
|
return TM->getTy();
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
void RuleMatcher::optimize() {
|
||
|
for (auto &Item : InsnVariableIDs) {
|
||
|
InstructionMatcher &InsnMatcher = *Item.first;
|
||
|
for (auto &OM : InsnMatcher.operands()) {
|
||
|
// Complex Patterns are usually expensive and they relatively rarely fail
|
||
|
// on their own: more often we end up throwing away all the work done by a
|
||
|
// matching part of a complex pattern because some other part of the
|
||
|
// enclosing pattern didn't match. All of this makes it beneficial to
|
||
|
// delay complex patterns until the very end of the rule matching,
|
||
|
// especially for targets having lots of complex patterns.
|
||
|
for (auto &OP : OM->predicates())
|
||
|
if (isa<ComplexPatternOperandMatcher>(OP))
|
||
|
EpilogueMatchers.emplace_back(std::move(OP));
|
||
|
OM->eraseNullPredicates();
|
||
|
}
|
||
|
InsnMatcher.optimize();
|
||
|
}
|
||
|
llvm::sort(EpilogueMatchers, [](const std::unique_ptr<PredicateMatcher> &L,
|
||
|
const std::unique_ptr<PredicateMatcher> &R) {
|
||
|
return std::make_tuple(L->getKind(), L->getInsnVarID(), L->getOpIdx()) <
|
||
|
std::make_tuple(R->getKind(), R->getInsnVarID(), R->getOpIdx());
|
||
|
});
|
||
|
}
|
||
|
|
||
|
bool RuleMatcher::hasFirstCondition() const {
|
||
|
if (insnmatchers_empty())
|
||
|
return false;
|
||
|
InstructionMatcher &Matcher = insnmatchers_front();
|
||
|
if (!Matcher.predicates_empty())
|
||
|
return true;
|
||
|
for (auto &OM : Matcher.operands())
|
||
|
for (auto &OP : OM->predicates())
|
||
|
if (!isa<InstructionOperandMatcher>(OP))
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const PredicateMatcher &RuleMatcher::getFirstCondition() const {
|
||
|
assert(!insnmatchers_empty() &&
|
||
|
"Trying to get a condition from an empty RuleMatcher");
|
||
|
|
||
|
InstructionMatcher &Matcher = insnmatchers_front();
|
||
|
if (!Matcher.predicates_empty())
|
||
|
return **Matcher.predicates_begin();
|
||
|
// If there is no more predicate on the instruction itself, look at its
|
||
|
// operands.
|
||
|
for (auto &OM : Matcher.operands())
|
||
|
for (auto &OP : OM->predicates())
|
||
|
if (!isa<InstructionOperandMatcher>(OP))
|
||
|
return *OP;
|
||
|
|
||
|
llvm_unreachable("Trying to get a condition from an InstructionMatcher with "
|
||
|
"no conditions");
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<PredicateMatcher> RuleMatcher::popFirstCondition() {
|
||
|
assert(!insnmatchers_empty() &&
|
||
|
"Trying to pop a condition from an empty RuleMatcher");
|
||
|
|
||
|
InstructionMatcher &Matcher = insnmatchers_front();
|
||
|
if (!Matcher.predicates_empty())
|
||
|
return Matcher.predicates_pop_front();
|
||
|
// If there is no more predicate on the instruction itself, look at its
|
||
|
// operands.
|
||
|
for (auto &OM : Matcher.operands())
|
||
|
for (auto &OP : OM->predicates())
|
||
|
if (!isa<InstructionOperandMatcher>(OP)) {
|
||
|
std::unique_ptr<PredicateMatcher> Result = std::move(OP);
|
||
|
OM->eraseNullPredicates();
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
llvm_unreachable("Trying to pop a condition from an InstructionMatcher with "
|
||
|
"no conditions");
|
||
|
}
|
||
|
|
||
|
GISelFlags RuleMatcher::updateGISelFlag(GISelFlags CurFlags, const Record *R,
|
||
|
StringRef FlagName,
|
||
|
GISelFlags FlagBit) {
|
||
|
// If the value of a flag is unset, ignore it.
|
||
|
// If it's set, it always takes precedence over the existing value so
|
||
|
// clear/set the corresponding bit.
|
||
|
bool Unset = false;
|
||
|
bool Value = R->getValueAsBitOrUnset("GIIgnoreCopies", Unset);
|
||
|
if (!Unset)
|
||
|
return Value ? (CurFlags | FlagBit) : (CurFlags & ~FlagBit);
|
||
|
return CurFlags;
|
||
|
}
|
||
|
|
||
|
SaveAndRestore<GISelFlags> RuleMatcher::setGISelFlags(const Record *R) {
|
||
|
if (!R || !R->isSubClassOf("GISelFlags"))
|
||
|
return {Flags, Flags};
|
||
|
|
||
|
assert((R->isSubClassOf("PatFrags") || R->isSubClassOf("Pattern")) &&
|
||
|
"GISelFlags is only expected on Pattern/PatFrags!");
|
||
|
|
||
|
GISelFlags NewFlags =
|
||
|
updateGISelFlag(Flags, R, "GIIgnoreCopies", GISF_IgnoreCopies);
|
||
|
return {Flags, NewFlags};
|
||
|
}
|
||
|
|
||
|
Error RuleMatcher::defineComplexSubOperand(StringRef SymbolicName,
|
||
|
Record *ComplexPattern,
|
||
|
unsigned RendererID,
|
||
|
unsigned SubOperandID,
|
||
|
StringRef ParentSymbolicName) {
|
||
|
std::string ParentName(ParentSymbolicName);
|
||
|
if (ComplexSubOperands.count(SymbolicName)) {
|
||
|
const std::string &RecordedParentName =
|
||
|
ComplexSubOperandsParentName[SymbolicName];
|
||
|
if (RecordedParentName != ParentName)
|
||
|
return failUnsupported("Error: Complex suboperand " + SymbolicName +
|
||
|
" referenced by different operands: " +
|
||
|
RecordedParentName + " and " + ParentName + ".");
|
||
|
// Complex suboperand referenced more than once from same the operand is
|
||
|
// used to generate 'same operand check'. Emitting of
|
||
|
// GIR_ComplexSubOperandRenderer for them is already handled.
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
ComplexSubOperands[SymbolicName] =
|
||
|
std::make_tuple(ComplexPattern, RendererID, SubOperandID);
|
||
|
ComplexSubOperandsParentName[SymbolicName] = ParentName;
|
||
|
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
InstructionMatcher &RuleMatcher::addInstructionMatcher(StringRef SymbolicName) {
|
||
|
Matchers.emplace_back(new InstructionMatcher(*this, SymbolicName));
|
||
|
MutatableInsns.insert(Matchers.back().get());
|
||
|
return *Matchers.back();
|
||
|
}
|
||
|
|
||
|
void RuleMatcher::addRequiredSimplePredicate(StringRef PredName) {
|
||
|
RequiredSimplePredicates.push_back(PredName.str());
|
||
|
}
|
||
|
|
||
|
const std::vector<std::string> &RuleMatcher::getRequiredSimplePredicates() {
|
||
|
return RequiredSimplePredicates;
|
||
|
}
|
||
|
|
||
|
void RuleMatcher::addRequiredFeature(Record *Feature) {
|
||
|
RequiredFeatures.push_back(Feature);
|
||
|
}
|
||
|
|
||
|
const std::vector<Record *> &RuleMatcher::getRequiredFeatures() const {
|
||
|
return RequiredFeatures;
|
||
|
}
|
||
|
|
||
|
unsigned RuleMatcher::implicitlyDefineInsnVar(InstructionMatcher &Matcher) {
|
||
|
unsigned NewInsnVarID = NextInsnVarID++;
|
||
|
InsnVariableIDs[&Matcher] = NewInsnVarID;
|
||
|
return NewInsnVarID;
|
||
|
}
|
||
|
|
||
|
unsigned RuleMatcher::getInsnVarID(InstructionMatcher &InsnMatcher) const {
|
||
|
const auto &I = InsnVariableIDs.find(&InsnMatcher);
|
||
|
if (I != InsnVariableIDs.end())
|
||
|
return I->second;
|
||
|
llvm_unreachable("Matched Insn was not captured in a local variable");
|
||
|
}
|
||
|
|
||
|
void RuleMatcher::defineOperand(StringRef SymbolicName, OperandMatcher &OM) {
|
||
|
if (!DefinedOperands.contains(SymbolicName)) {
|
||
|
DefinedOperands[SymbolicName] = &OM;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If the operand is already defined, then we must ensure both references in
|
||
|
// the matcher have the exact same node.
|
||
|
RuleMatcher &RM = OM.getInstructionMatcher().getRuleMatcher();
|
||
|
OM.addPredicate<SameOperandMatcher>(
|
||
|
OM.getSymbolicName(), getOperandMatcher(OM.getSymbolicName()).getOpIdx(),
|
||
|
RM.getGISelFlags());
|
||
|
}
|
||
|
|
||
|
void RuleMatcher::definePhysRegOperand(Record *Reg, OperandMatcher &OM) {
|
||
|
if (!PhysRegOperands.contains(Reg)) {
|
||
|
PhysRegOperands[Reg] = &OM;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
InstructionMatcher &
|
||
|
RuleMatcher::getInstructionMatcher(StringRef SymbolicName) const {
|
||
|
for (const auto &I : InsnVariableIDs)
|
||
|
if (I.first->getSymbolicName() == SymbolicName)
|
||
|
return *I.first;
|
||
|
llvm_unreachable(
|
||
|
("Failed to lookup instruction " + SymbolicName).str().c_str());
|
||
|
}
|
||
|
|
||
|
const OperandMatcher &RuleMatcher::getPhysRegOperandMatcher(Record *Reg) const {
|
||
|
const auto &I = PhysRegOperands.find(Reg);
|
||
|
|
||
|
if (I == PhysRegOperands.end()) {
|
||
|
PrintFatalError(SrcLoc, "Register " + Reg->getName() +
|
||
|
" was not declared in matcher");
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
|
||
|
if (I == DefinedOperands.end())
|
||
|
PrintFatalError(SrcLoc, "Operand " + Name + " was not declared in matcher");
|
||
|
|
||
|
return *I->second;
|
||
|
}
|
||
|
|
||
|
void RuleMatcher::emit(MatchTable &Table) {
|
||
|
if (Matchers.empty())
|
||
|
llvm_unreachable("Unexpected empty matcher!");
|
||
|
|
||
|
// The representation supports rules that require multiple roots such as:
|
||
|
// %ptr(p0) = ...
|
||
|
// %elt0(s32) = G_LOAD %ptr
|
||
|
// %1(p0) = G_ADD %ptr, 4
|
||
|
// %elt1(s32) = G_LOAD p0 %1
|
||
|
// which could be usefully folded into:
|
||
|
// %ptr(p0) = ...
|
||
|
// %elt0(s32), %elt1(s32) = TGT_LOAD_PAIR %ptr
|
||
|
// on some targets but we don't need to make use of that yet.
|
||
|
assert(Matchers.size() == 1 && "Cannot handle multi-root matchers yet");
|
||
|
|
||
|
unsigned LabelID = Table.allocateLabelID();
|
||
|
Table << MatchTable::Opcode("GIM_Try", +1)
|
||
|
<< MatchTable::Comment("On fail goto")
|
||
|
<< MatchTable::JumpTarget(LabelID)
|
||
|
<< MatchTable::Comment(("Rule ID " + Twine(RuleID) + " //").str())
|
||
|
<< MatchTable::LineBreak;
|
||
|
|
||
|
if (!RequiredFeatures.empty() || HwModeIdx >= 0) {
|
||
|
Table << MatchTable::Opcode("GIM_CheckFeatures")
|
||
|
<< MatchTable::NamedValue(
|
||
|
2, getNameForFeatureBitset(RequiredFeatures, HwModeIdx))
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
if (!RequiredSimplePredicates.empty()) {
|
||
|
for (const auto &Pred : RequiredSimplePredicates) {
|
||
|
Table << MatchTable::Opcode("GIM_CheckSimplePredicate")
|
||
|
<< MatchTable::NamedValue(2, Pred) << MatchTable::LineBreak;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
SmallVector<unsigned, 2> InsnIDs;
|
||
|
for (const auto &Pair : InsnVariableIDs) {
|
||
|
// Skip the root node since it isn't moving anywhere. Everything else is
|
||
|
// sinking to meet it.
|
||
|
if (Pair.first == Matchers.front().get())
|
||
|
continue;
|
||
|
|
||
|
InsnIDs.push_back(Pair.second);
|
||
|
}
|
||
|
llvm::sort(InsnIDs);
|
||
|
|
||
|
for (const auto &InsnID : InsnIDs) {
|
||
|
// Reject the difficult cases until we have a more accurate check.
|
||
|
Table << MatchTable::Opcode("GIM_CheckIsSafeToFold")
|
||
|
<< MatchTable::Comment("InsnID") << MatchTable::ULEB128Value(InsnID)
|
||
|
<< MatchTable::LineBreak;
|
||
|
|
||
|
// FIXME: Emit checks to determine it's _actually_ safe to fold and/or
|
||
|
// account for unsafe cases.
|
||
|
//
|
||
|
// Example:
|
||
|
// MI1--> %0 = ...
|
||
|
// %1 = ... %0
|
||
|
// MI0--> %2 = ... %0
|
||
|
// It's not safe to erase MI1. We currently handle this by not
|
||
|
// erasing %0 (even when it's dead).
|
||
|
//
|
||
|
// Example:
|
||
|
// MI1--> %0 = load volatile @a
|
||
|
// %1 = load volatile @a
|
||
|
// MI0--> %2 = ... %0
|
||
|
// It's not safe to sink %0's def past %1. We currently handle
|
||
|
// this by rejecting all loads.
|
||
|
//
|
||
|
// Example:
|
||
|
// MI1--> %0 = load @a
|
||
|
// %1 = store @a
|
||
|
// MI0--> %2 = ... %0
|
||
|
// It's not safe to sink %0's def past %1. We currently handle
|
||
|
// this by rejecting all loads.
|
||
|
//
|
||
|
// Example:
|
||
|
// G_CONDBR %cond, @BB1
|
||
|
// BB0:
|
||
|
// MI1--> %0 = load @a
|
||
|
// G_BR @BB1
|
||
|
// BB1:
|
||
|
// MI0--> %2 = ... %0
|
||
|
// It's not always safe to sink %0 across control flow. In this
|
||
|
// case it may introduce a memory fault. We currentl handle
|
||
|
// this by rejecting all loads.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (const auto &PM : EpilogueMatchers)
|
||
|
PM->emitPredicateOpcodes(Table, *this);
|
||
|
|
||
|
for (const auto &MA : Actions)
|
||
|
MA->emitActionOpcodes(Table, *this);
|
||
|
|
||
|
assert((Table.isWithCoverage() ? !Table.isCombiner() : true) &&
|
||
|
"Combiner tables don't support coverage!");
|
||
|
if (Table.isWithCoverage())
|
||
|
Table << MatchTable::Opcode("GIR_Coverage")
|
||
|
<< MatchTable::IntValue(4, RuleID) << MatchTable::LineBreak;
|
||
|
else if (!Table.isCombiner())
|
||
|
Table << MatchTable::Comment(("GIR_Coverage, " + Twine(RuleID) + ",").str())
|
||
|
<< MatchTable::LineBreak;
|
||
|
|
||
|
Table << MatchTable::Opcode("GIR_Done", -1) << MatchTable::LineBreak
|
||
|
<< MatchTable::Label(LabelID);
|
||
|
++NumPatternEmitted;
|
||
|
}
|
||
|
|
||
|
bool RuleMatcher::isHigherPriorityThan(const RuleMatcher &B) const {
|
||
|
// Rules involving more match roots have higher priority.
|
||
|
if (Matchers.size() > B.Matchers.size())
|
||
|
return true;
|
||
|
if (Matchers.size() < B.Matchers.size())
|
||
|
return false;
|
||
|
|
||
|
for (auto Matcher : zip(Matchers, B.Matchers)) {
|
||
|
if (std::get<0>(Matcher)->isHigherPriorityThan(*std::get<1>(Matcher)))
|
||
|
return true;
|
||
|
if (std::get<1>(Matcher)->isHigherPriorityThan(*std::get<0>(Matcher)))
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
unsigned RuleMatcher::countRendererFns() const {
|
||
|
return std::accumulate(
|
||
|
Matchers.begin(), Matchers.end(), 0,
|
||
|
[](unsigned A, const std::unique_ptr<InstructionMatcher> &Matcher) {
|
||
|
return A + Matcher->countRendererFns();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
//===- PredicateMatcher ---------------------------------------------------===//
|
||
|
|
||
|
PredicateMatcher::~PredicateMatcher() {}
|
||
|
|
||
|
//===- OperandPredicateMatcher --------------------------------------------===//
|
||
|
|
||
|
OperandPredicateMatcher::~OperandPredicateMatcher() {}
|
||
|
|
||
|
bool OperandPredicateMatcher::isHigherPriorityThan(
|
||
|
const OperandPredicateMatcher &B) const {
|
||
|
// Generally speaking, an instruction is more important than an Int or a
|
||
|
// LiteralInt because it can cover more nodes but theres an exception to
|
||
|
// this. G_CONSTANT's are less important than either of those two because they
|
||
|
// are more permissive.
|
||
|
|
||
|
const InstructionOperandMatcher *AOM =
|
||
|
dyn_cast<InstructionOperandMatcher>(this);
|
||
|
const InstructionOperandMatcher *BOM =
|
||
|
dyn_cast<InstructionOperandMatcher>(&B);
|
||
|
bool AIsConstantInsn = AOM && AOM->getInsnMatcher().isConstantInstruction();
|
||
|
bool BIsConstantInsn = BOM && BOM->getInsnMatcher().isConstantInstruction();
|
||
|
|
||
|
if (AOM && BOM) {
|
||
|
// The relative priorities between a G_CONSTANT and any other instruction
|
||
|
// don't actually matter but this code is needed to ensure a strict weak
|
||
|
// ordering. This is particularly important on Windows where the rules will
|
||
|
// be incorrectly sorted without it.
|
||
|
if (AIsConstantInsn != BIsConstantInsn)
|
||
|
return AIsConstantInsn < BIsConstantInsn;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (AOM && AIsConstantInsn && (B.Kind == OPM_Int || B.Kind == OPM_LiteralInt))
|
||
|
return false;
|
||
|
if (BOM && BIsConstantInsn && (Kind == OPM_Int || Kind == OPM_LiteralInt))
|
||
|
return true;
|
||
|
|
||
|
return Kind < B.Kind;
|
||
|
}
|
||
|
|
||
|
//===- SameOperandMatcher -------------------------------------------------===//
|
||
|
|
||
|
void SameOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
const OperandMatcher &OtherOM = Rule.getOperandMatcher(MatchingName);
|
||
|
unsigned OtherInsnVarID = Rule.getInsnVarID(OtherOM.getInstructionMatcher());
|
||
|
assert(OtherInsnVarID == OtherOM.getInstructionMatcher().getInsnVarID());
|
||
|
const bool IgnoreCopies = Flags & GISF_IgnoreCopies;
|
||
|
Table << MatchTable::Opcode(IgnoreCopies
|
||
|
? "GIM_CheckIsSameOperandIgnoreCopies"
|
||
|
: "GIM_CheckIsSameOperand")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("OpIdx") << MatchTable::ULEB128Value(OpIdx)
|
||
|
<< MatchTable::Comment("OtherMI")
|
||
|
<< MatchTable::ULEB128Value(OtherInsnVarID)
|
||
|
<< MatchTable::Comment("OtherOpIdx")
|
||
|
<< MatchTable::ULEB128Value(OtherOM.getOpIdx())
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- LLTOperandMatcher --------------------------------------------------===//
|
||
|
|
||
|
std::map<LLTCodeGen, unsigned> LLTOperandMatcher::TypeIDValues;
|
||
|
|
||
|
MatchTableRecord LLTOperandMatcher::getValue() const {
|
||
|
const auto VI = TypeIDValues.find(Ty);
|
||
|
if (VI == TypeIDValues.end())
|
||
|
return MatchTable::NamedValue(1, getTy().getCxxEnumValue());
|
||
|
return MatchTable::NamedValue(1, getTy().getCxxEnumValue(), VI->second);
|
||
|
}
|
||
|
|
||
|
bool LLTOperandMatcher::hasValue() const {
|
||
|
if (TypeIDValues.size() != KnownTypes.size())
|
||
|
initTypeIDValuesMap();
|
||
|
return TypeIDValues.count(Ty);
|
||
|
}
|
||
|
|
||
|
void LLTOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckType") << MatchTable::Comment("MI")
|
||
|
<< MatchTable::ULEB128Value(InsnVarID) << MatchTable::Comment("Op")
|
||
|
<< MatchTable::ULEB128Value(OpIdx) << MatchTable::Comment("Type")
|
||
|
<< getValue() << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- PointerToAnyOperandMatcher -----------------------------------------===//
|
||
|
|
||
|
void PointerToAnyOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckPointerToAny")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("Op") << MatchTable::ULEB128Value(OpIdx)
|
||
|
<< MatchTable::Comment("SizeInBits")
|
||
|
<< MatchTable::ULEB128Value(SizeInBits) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- RecordNamedOperandMatcher ------------------------------------------===//
|
||
|
|
||
|
void RecordNamedOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_RecordNamedOperand")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("Op") << MatchTable::ULEB128Value(OpIdx)
|
||
|
<< MatchTable::Comment("StoreIdx") << MatchTable::ULEB128Value(StoreIdx)
|
||
|
<< 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::ULEB128Value(InsnVarID) << MatchTable::Comment("Op")
|
||
|
<< MatchTable::ULEB128Value(OpIdx) << MatchTable::Comment("TempTypeIdx")
|
||
|
<< MatchTable::IntValue(1, Idx) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- ComplexPatternOperandMatcher ---------------------------------------===//
|
||
|
|
||
|
void ComplexPatternOperandMatcher::emitPredicateOpcodes(
|
||
|
MatchTable &Table, RuleMatcher &Rule) const {
|
||
|
unsigned ID = getAllocatedTemporariesBaseID();
|
||
|
Table << MatchTable::Opcode("GIM_CheckComplexPattern")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("Op") << MatchTable::ULEB128Value(OpIdx)
|
||
|
<< MatchTable::Comment("Renderer") << MatchTable::IntValue(2, ID)
|
||
|
<< MatchTable::NamedValue(2, ("GICP_" + TheDef.getName()).str())
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
unsigned ComplexPatternOperandMatcher::getAllocatedTemporariesBaseID() const {
|
||
|
return Operand.getAllocatedTemporariesBaseID();
|
||
|
}
|
||
|
|
||
|
//===- RegisterBankOperandMatcher -----------------------------------------===//
|
||
|
|
||
|
bool RegisterBankOperandMatcher::isIdentical(const PredicateMatcher &B) const {
|
||
|
return OperandPredicateMatcher::isIdentical(B) &&
|
||
|
RC.getDef() == cast<RegisterBankOperandMatcher>(&B)->RC.getDef();
|
||
|
}
|
||
|
|
||
|
void RegisterBankOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckRegBankForClass")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("Op") << MatchTable::ULEB128Value(OpIdx)
|
||
|
<< MatchTable::Comment("RC")
|
||
|
<< MatchTable::NamedValue(2, RC.getQualifiedIdName())
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- MBBOperandMatcher --------------------------------------------------===//
|
||
|
|
||
|
void MBBOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckIsMBB") << MatchTable::Comment("MI")
|
||
|
<< MatchTable::ULEB128Value(InsnVarID) << MatchTable::Comment("Op")
|
||
|
<< MatchTable::ULEB128Value(OpIdx) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- ImmOperandMatcher --------------------------------------------------===//
|
||
|
|
||
|
void ImmOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckIsImm") << MatchTable::Comment("MI")
|
||
|
<< MatchTable::ULEB128Value(InsnVarID) << MatchTable::Comment("Op")
|
||
|
<< MatchTable::ULEB128Value(OpIdx) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- ConstantIntOperandMatcher ------------------------------------------===//
|
||
|
|
||
|
void ConstantIntOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
const bool IsInt8 = isInt<8>(Value);
|
||
|
Table << MatchTable::Opcode(IsInt8 ? "GIM_CheckConstantInt8"
|
||
|
: "GIM_CheckConstantInt")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("Op") << MatchTable::ULEB128Value(OpIdx)
|
||
|
<< MatchTable::IntValue(IsInt8 ? 1 : 8, Value) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- LiteralIntOperandMatcher -------------------------------------------===//
|
||
|
|
||
|
void LiteralIntOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckLiteralInt")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("Op") << MatchTable::ULEB128Value(OpIdx)
|
||
|
<< MatchTable::IntValue(8, Value) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- CmpPredicateOperandMatcher -----------------------------------------===//
|
||
|
|
||
|
void CmpPredicateOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckCmpPredicate")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("Op") << MatchTable::ULEB128Value(OpIdx)
|
||
|
<< MatchTable::Comment("Predicate")
|
||
|
<< MatchTable::NamedValue(2, "CmpInst", PredName)
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- IntrinsicIDOperandMatcher ------------------------------------------===//
|
||
|
|
||
|
void IntrinsicIDOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckIntrinsicID")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("Op") << MatchTable::ULEB128Value(OpIdx)
|
||
|
<< MatchTable::NamedValue(2, "Intrinsic::" + II->EnumName)
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- OperandImmPredicateMatcher -----------------------------------------===//
|
||
|
|
||
|
void OperandImmPredicateMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckImmOperandPredicate")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("MO") << MatchTable::ULEB128Value(OpIdx)
|
||
|
<< MatchTable::Comment("Predicate")
|
||
|
<< MatchTable::NamedValue(2, getEnumNameForPredicate(Predicate))
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- OperandMatcher -----------------------------------------------------===//
|
||
|
|
||
|
std::string OperandMatcher::getOperandExpr(unsigned InsnVarID) const {
|
||
|
return "State.MIs[" + llvm::to_string(InsnVarID) + "]->getOperand(" +
|
||
|
llvm::to_string(OpIdx) + ")";
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
std::string Comment;
|
||
|
raw_string_ostream CommentOS(Comment);
|
||
|
CommentOS << "MIs[" << getInsnVarID() << "] ";
|
||
|
if (SymbolicName.empty())
|
||
|
CommentOS << "Operand " << OpIdx;
|
||
|
else
|
||
|
CommentOS << SymbolicName;
|
||
|
Table << MatchTable::Comment(Comment) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
emitPredicateListOpcodes(Table, Rule);
|
||
|
}
|
||
|
|
||
|
bool OperandMatcher::isHigherPriorityThan(OperandMatcher &B) {
|
||
|
// Operand matchers involving more predicates have higher priority.
|
||
|
if (predicates_size() > B.predicates_size())
|
||
|
return true;
|
||
|
if (predicates_size() < B.predicates_size())
|
||
|
return false;
|
||
|
|
||
|
// This assumes that predicates are added in a consistent order.
|
||
|
for (auto &&Predicate : zip(predicates(), B.predicates())) {
|
||
|
if (std::get<0>(Predicate)->isHigherPriorityThan(*std::get<1>(Predicate)))
|
||
|
return true;
|
||
|
if (std::get<1>(Predicate)->isHigherPriorityThan(*std::get<0>(Predicate)))
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
unsigned OperandMatcher::countRendererFns() {
|
||
|
return std::accumulate(
|
||
|
predicates().begin(), predicates().end(), 0,
|
||
|
[](unsigned A,
|
||
|
const std::unique_ptr<OperandPredicateMatcher> &Predicate) {
|
||
|
return A + Predicate->countRendererFns();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
Error OperandMatcher::addTypeCheckPredicate(const TypeSetByHwMode &VTy,
|
||
|
bool OperandIsAPointer) {
|
||
|
if (!VTy.isMachineValueType())
|
||
|
return failUnsupported("unsupported typeset");
|
||
|
|
||
|
if (VTy.getMachineValueType() == MVT::iPTR && OperandIsAPointer) {
|
||
|
addPredicate<PointerToAnyOperandMatcher>(0);
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
auto OpTyOrNone = MVTToLLT(VTy.getMachineValueType().SimpleTy);
|
||
|
if (!OpTyOrNone)
|
||
|
return failUnsupported("unsupported type");
|
||
|
|
||
|
if (OperandIsAPointer)
|
||
|
addPredicate<PointerToAnyOperandMatcher>(OpTyOrNone->get().getSizeInBits());
|
||
|
else if (VTy.isPointer())
|
||
|
addPredicate<LLTOperandMatcher>(
|
||
|
LLT::pointer(VTy.getPtrAddrSpace(), OpTyOrNone->get().getSizeInBits()));
|
||
|
else
|
||
|
addPredicate<LLTOperandMatcher>(*OpTyOrNone);
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
//===- InstructionOpcodeMatcher -------------------------------------------===//
|
||
|
|
||
|
DenseMap<const CodeGenInstruction *, unsigned>
|
||
|
InstructionOpcodeMatcher::OpcodeValues;
|
||
|
|
||
|
MatchTableRecord
|
||
|
InstructionOpcodeMatcher::getInstValue(const CodeGenInstruction *I) const {
|
||
|
const auto VI = OpcodeValues.find(I);
|
||
|
if (VI != OpcodeValues.end())
|
||
|
return MatchTable::NamedValue(2, I->Namespace, I->TheDef->getName(),
|
||
|
VI->second);
|
||
|
return MatchTable::NamedValue(2, I->Namespace, I->TheDef->getName());
|
||
|
}
|
||
|
|
||
|
void InstructionOpcodeMatcher::initOpcodeValuesMap(
|
||
|
const CodeGenTarget &Target) {
|
||
|
OpcodeValues.clear();
|
||
|
|
||
|
unsigned OpcodeValue = 0;
|
||
|
for (const CodeGenInstruction *I : Target.getInstructionsByEnumValue())
|
||
|
OpcodeValues[I] = OpcodeValue++;
|
||
|
}
|
||
|
|
||
|
MatchTableRecord InstructionOpcodeMatcher::getValue() const {
|
||
|
assert(Insts.size() == 1);
|
||
|
|
||
|
const CodeGenInstruction *I = Insts[0];
|
||
|
const auto VI = OpcodeValues.find(I);
|
||
|
if (VI != OpcodeValues.end())
|
||
|
return MatchTable::NamedValue(2, I->Namespace, I->TheDef->getName(),
|
||
|
VI->second);
|
||
|
return MatchTable::NamedValue(2, I->Namespace, I->TheDef->getName());
|
||
|
}
|
||
|
|
||
|
void InstructionOpcodeMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
StringRef CheckType =
|
||
|
Insts.size() == 1 ? "GIM_CheckOpcode" : "GIM_CheckOpcodeIsEither";
|
||
|
Table << MatchTable::Opcode(CheckType) << MatchTable::Comment("MI")
|
||
|
<< MatchTable::ULEB128Value(InsnVarID);
|
||
|
|
||
|
for (const CodeGenInstruction *I : Insts)
|
||
|
Table << getInstValue(I);
|
||
|
Table << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
bool InstructionOpcodeMatcher::isHigherPriorityThan(
|
||
|
const InstructionPredicateMatcher &B) const {
|
||
|
if (InstructionPredicateMatcher::isHigherPriorityThan(B))
|
||
|
return true;
|
||
|
if (B.InstructionPredicateMatcher::isHigherPriorityThan(*this))
|
||
|
return false;
|
||
|
|
||
|
// Prioritize opcodes for cosmetic reasons in the generated source. Although
|
||
|
// this is cosmetic at the moment, we may want to drive a similar ordering
|
||
|
// using instruction frequency information to improve compile time.
|
||
|
if (const InstructionOpcodeMatcher *BO =
|
||
|
dyn_cast<InstructionOpcodeMatcher>(&B))
|
||
|
return Insts[0]->TheDef->getName() < BO->Insts[0]->TheDef->getName();
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool InstructionOpcodeMatcher::isConstantInstruction() const {
|
||
|
return Insts.size() == 1 && Insts[0]->TheDef->getName() == "G_CONSTANT";
|
||
|
}
|
||
|
|
||
|
StringRef InstructionOpcodeMatcher::getOpcode() const {
|
||
|
return Insts[0]->TheDef->getName();
|
||
|
}
|
||
|
|
||
|
bool InstructionOpcodeMatcher::isVariadicNumOperands() const {
|
||
|
// If one is variadic, they all should be.
|
||
|
return Insts[0]->Operands.isVariadic;
|
||
|
}
|
||
|
|
||
|
StringRef InstructionOpcodeMatcher::getOperandType(unsigned OpIdx) const {
|
||
|
// Types expected to be uniform for all alternatives.
|
||
|
return Insts[0]->Operands[OpIdx].OperandType;
|
||
|
}
|
||
|
|
||
|
//===- InstructionNumOperandsMatcher --------------------------------------===//
|
||
|
|
||
|
void InstructionNumOperandsMatcher::emitPredicateOpcodes(
|
||
|
MatchTable &Table, RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckNumOperands")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("Expected")
|
||
|
<< MatchTable::ULEB128Value(NumOperands) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- InstructionImmPredicateMatcher -------------------------------------===//
|
||
|
|
||
|
bool InstructionImmPredicateMatcher::isIdentical(
|
||
|
const PredicateMatcher &B) const {
|
||
|
return InstructionPredicateMatcher::isIdentical(B) &&
|
||
|
Predicate.getOrigPatFragRecord() ==
|
||
|
cast<InstructionImmPredicateMatcher>(&B)
|
||
|
->Predicate.getOrigPatFragRecord();
|
||
|
}
|
||
|
|
||
|
void InstructionImmPredicateMatcher::emitPredicateOpcodes(
|
||
|
MatchTable &Table, RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode(getMatchOpcodeForImmPredicate(Predicate))
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("Predicate")
|
||
|
<< MatchTable::NamedValue(2, getEnumNameForPredicate(Predicate))
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- AtomicOrderingMMOPredicateMatcher ----------------------------------===//
|
||
|
|
||
|
bool AtomicOrderingMMOPredicateMatcher::isIdentical(
|
||
|
const PredicateMatcher &B) const {
|
||
|
if (!InstructionPredicateMatcher::isIdentical(B))
|
||
|
return false;
|
||
|
const auto &R = *cast<AtomicOrderingMMOPredicateMatcher>(&B);
|
||
|
return Order == R.Order && Comparator == R.Comparator;
|
||
|
}
|
||
|
|
||
|
void AtomicOrderingMMOPredicateMatcher::emitPredicateOpcodes(
|
||
|
MatchTable &Table, RuleMatcher &Rule) const {
|
||
|
StringRef Opcode = "GIM_CheckAtomicOrdering";
|
||
|
|
||
|
if (Comparator == AO_OrStronger)
|
||
|
Opcode = "GIM_CheckAtomicOrderingOrStrongerThan";
|
||
|
if (Comparator == AO_WeakerThan)
|
||
|
Opcode = "GIM_CheckAtomicOrderingWeakerThan";
|
||
|
|
||
|
Table << MatchTable::Opcode(Opcode) << MatchTable::Comment("MI")
|
||
|
<< MatchTable::ULEB128Value(InsnVarID) << MatchTable::Comment("Order")
|
||
|
<< MatchTable::NamedValue(1,
|
||
|
("(uint8_t)AtomicOrdering::" + Order).str())
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- MemorySizePredicateMatcher -----------------------------------------===//
|
||
|
|
||
|
void MemorySizePredicateMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckMemorySizeEqualTo")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("MMO") << MatchTable::ULEB128Value(MMOIdx)
|
||
|
<< MatchTable::Comment("Size") << MatchTable::IntValue(4, Size)
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- MemoryAddressSpacePredicateMatcher ---------------------------------===//
|
||
|
|
||
|
bool MemoryAddressSpacePredicateMatcher::isIdentical(
|
||
|
const PredicateMatcher &B) const {
|
||
|
if (!InstructionPredicateMatcher::isIdentical(B))
|
||
|
return false;
|
||
|
auto *Other = cast<MemoryAddressSpacePredicateMatcher>(&B);
|
||
|
return MMOIdx == Other->MMOIdx && AddrSpaces == Other->AddrSpaces;
|
||
|
}
|
||
|
|
||
|
void MemoryAddressSpacePredicateMatcher::emitPredicateOpcodes(
|
||
|
MatchTable &Table, RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckMemoryAddressSpace")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("MMO")
|
||
|
<< MatchTable::ULEB128Value(MMOIdx)
|
||
|
// Encode number of address spaces to expect.
|
||
|
<< MatchTable::Comment("NumAddrSpace")
|
||
|
<< MatchTable::ULEB128Value(AddrSpaces.size());
|
||
|
for (unsigned AS : AddrSpaces)
|
||
|
Table << MatchTable::Comment("AddrSpace") << MatchTable::ULEB128Value(AS);
|
||
|
|
||
|
Table << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- MemoryAlignmentPredicateMatcher ------------------------------------===//
|
||
|
|
||
|
bool MemoryAlignmentPredicateMatcher::isIdentical(
|
||
|
const PredicateMatcher &B) const {
|
||
|
if (!InstructionPredicateMatcher::isIdentical(B))
|
||
|
return false;
|
||
|
auto *Other = cast<MemoryAlignmentPredicateMatcher>(&B);
|
||
|
return MMOIdx == Other->MMOIdx && MinAlign == Other->MinAlign;
|
||
|
}
|
||
|
|
||
|
void MemoryAlignmentPredicateMatcher::emitPredicateOpcodes(
|
||
|
MatchTable &Table, RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckMemoryAlignment")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("MMO") << MatchTable::ULEB128Value(MMOIdx)
|
||
|
<< MatchTable::Comment("MinAlign") << MatchTable::ULEB128Value(MinAlign)
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- MemoryVsLLTSizePredicateMatcher ------------------------------------===//
|
||
|
|
||
|
bool MemoryVsLLTSizePredicateMatcher::isIdentical(
|
||
|
const PredicateMatcher &B) const {
|
||
|
return InstructionPredicateMatcher::isIdentical(B) &&
|
||
|
MMOIdx == cast<MemoryVsLLTSizePredicateMatcher>(&B)->MMOIdx &&
|
||
|
Relation == cast<MemoryVsLLTSizePredicateMatcher>(&B)->Relation &&
|
||
|
OpIdx == cast<MemoryVsLLTSizePredicateMatcher>(&B)->OpIdx;
|
||
|
}
|
||
|
|
||
|
void MemoryVsLLTSizePredicateMatcher::emitPredicateOpcodes(
|
||
|
MatchTable &Table, RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode(
|
||
|
Relation == EqualTo ? "GIM_CheckMemorySizeEqualToLLT"
|
||
|
: Relation == GreaterThan ? "GIM_CheckMemorySizeGreaterThanLLT"
|
||
|
: "GIM_CheckMemorySizeLessThanLLT")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("MMO") << MatchTable::ULEB128Value(MMOIdx)
|
||
|
<< MatchTable::Comment("OpIdx") << MatchTable::ULEB128Value(OpIdx)
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- VectorSplatImmPredicateMatcher -------------------------------------===//
|
||
|
|
||
|
void VectorSplatImmPredicateMatcher::emitPredicateOpcodes(
|
||
|
MatchTable &Table, RuleMatcher &Rule) const {
|
||
|
if (Kind == AllOnes)
|
||
|
Table << MatchTable::Opcode("GIM_CheckIsBuildVectorAllOnes");
|
||
|
else
|
||
|
Table << MatchTable::Opcode("GIM_CheckIsBuildVectorAllZeros");
|
||
|
|
||
|
Table << MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID);
|
||
|
Table << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- GenericInstructionPredicateMatcher ---------------------------------===//
|
||
|
|
||
|
GenericInstructionPredicateMatcher::GenericInstructionPredicateMatcher(
|
||
|
unsigned InsnVarID, TreePredicateFn Predicate)
|
||
|
: GenericInstructionPredicateMatcher(InsnVarID,
|
||
|
getEnumNameForPredicate(Predicate)) {}
|
||
|
|
||
|
bool GenericInstructionPredicateMatcher::isIdentical(
|
||
|
const PredicateMatcher &B) const {
|
||
|
return InstructionPredicateMatcher::isIdentical(B) &&
|
||
|
EnumVal ==
|
||
|
static_cast<const GenericInstructionPredicateMatcher &>(B).EnumVal;
|
||
|
}
|
||
|
void GenericInstructionPredicateMatcher::emitPredicateOpcodes(
|
||
|
MatchTable &Table, RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIM_CheckCxxInsnPredicate")
|
||
|
<< MatchTable::Comment("MI") << MatchTable::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::Comment("FnId") << MatchTable::NamedValue(2, EnumVal)
|
||
|
<< 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::ULEB128Value(InsnVarID)
|
||
|
<< MatchTable::NamedValue(4, join(Flags, " | "))
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- InstructionMatcher -------------------------------------------------===//
|
||
|
|
||
|
OperandMatcher &
|
||
|
InstructionMatcher::addOperand(unsigned OpIdx, const std::string &SymbolicName,
|
||
|
unsigned AllocatedTemporariesBaseID) {
|
||
|
Operands.emplace_back(new OperandMatcher(*this, OpIdx, SymbolicName,
|
||
|
AllocatedTemporariesBaseID));
|
||
|
if (!SymbolicName.empty())
|
||
|
Rule.defineOperand(SymbolicName, *Operands.back());
|
||
|
|
||
|
return *Operands.back();
|
||
|
}
|
||
|
|
||
|
OperandMatcher &InstructionMatcher::getOperand(unsigned OpIdx) {
|
||
|
auto I = llvm::find_if(Operands,
|
||
|
[&OpIdx](const std::unique_ptr<OperandMatcher> &X) {
|
||
|
return X->getOpIdx() == OpIdx;
|
||
|
});
|
||
|
if (I != Operands.end())
|
||
|
return **I;
|
||
|
llvm_unreachable("Failed to lookup operand");
|
||
|
}
|
||
|
|
||
|
OperandMatcher &InstructionMatcher::addPhysRegInput(Record *Reg, unsigned OpIdx,
|
||
|
unsigned TempOpIdx) {
|
||
|
assert(SymbolicName.empty());
|
||
|
OperandMatcher *OM = new OperandMatcher(*this, OpIdx, "", TempOpIdx);
|
||
|
Operands.emplace_back(OM);
|
||
|
Rule.definePhysRegOperand(Reg, *OM);
|
||
|
PhysRegInputs.emplace_back(Reg, OpIdx);
|
||
|
return *OM;
|
||
|
}
|
||
|
|
||
|
void InstructionMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) {
|
||
|
if (NumOperandsCheck)
|
||
|
InstructionNumOperandsMatcher(InsnVarID, getNumOperands())
|
||
|
.emitPredicateOpcodes(Table, Rule);
|
||
|
|
||
|
// First emit all instruction level predicates need to be verified before we
|
||
|
// can verify operands.
|
||
|
emitFilteredPredicateListOpcodes(
|
||
|
[](const PredicateMatcher &P) { return !P.dependsOnOperands(); }, Table,
|
||
|
Rule);
|
||
|
|
||
|
// Emit all operand constraints.
|
||
|
for (const auto &Operand : Operands)
|
||
|
Operand->emitPredicateOpcodes(Table, Rule);
|
||
|
|
||
|
// All of the tablegen defined predicates should now be matched. Now emit
|
||
|
// any custom predicates that rely on all generated checks.
|
||
|
emitFilteredPredicateListOpcodes(
|
||
|
[](const PredicateMatcher &P) { return P.dependsOnOperands(); }, Table,
|
||
|
Rule);
|
||
|
}
|
||
|
|
||
|
bool InstructionMatcher::isHigherPriorityThan(InstructionMatcher &B) {
|
||
|
// Instruction matchers involving more operands have higher priority.
|
||
|
if (Operands.size() > B.Operands.size())
|
||
|
return true;
|
||
|
if (Operands.size() < B.Operands.size())
|
||
|
return false;
|
||
|
|
||
|
for (auto &&P : zip(predicates(), B.predicates())) {
|
||
|
auto L = static_cast<InstructionPredicateMatcher *>(std::get<0>(P).get());
|
||
|
auto R = static_cast<InstructionPredicateMatcher *>(std::get<1>(P).get());
|
||
|
if (L->isHigherPriorityThan(*R))
|
||
|
return true;
|
||
|
if (R->isHigherPriorityThan(*L))
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for (auto Operand : zip(Operands, B.Operands)) {
|
||
|
if (std::get<0>(Operand)->isHigherPriorityThan(*std::get<1>(Operand)))
|
||
|
return true;
|
||
|
if (std::get<1>(Operand)->isHigherPriorityThan(*std::get<0>(Operand)))
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
unsigned InstructionMatcher::countRendererFns() {
|
||
|
return std::accumulate(
|
||
|
predicates().begin(), predicates().end(), 0,
|
||
|
[](unsigned A,
|
||
|
const std::unique_ptr<PredicateMatcher> &Predicate) {
|
||
|
return A + Predicate->countRendererFns();
|
||
|
}) +
|
||
|
std::accumulate(
|
||
|
Operands.begin(), Operands.end(), 0,
|
||
|
[](unsigned A, const std::unique_ptr<OperandMatcher> &Operand) {
|
||
|
return A + Operand->countRendererFns();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void InstructionMatcher::optimize() {
|
||
|
SmallVector<std::unique_ptr<PredicateMatcher>, 8> Stash;
|
||
|
const auto &OpcMatcher = getOpcodeMatcher();
|
||
|
|
||
|
Stash.push_back(predicates_pop_front());
|
||
|
if (Stash.back().get() == &OpcMatcher) {
|
||
|
if (NumOperandsCheck && OpcMatcher.isVariadicNumOperands() &&
|
||
|
getNumOperands() != 0)
|
||
|
Stash.emplace_back(
|
||
|
new InstructionNumOperandsMatcher(InsnVarID, getNumOperands()));
|
||
|
NumOperandsCheck = false;
|
||
|
|
||
|
for (auto &OM : Operands)
|
||
|
for (auto &OP : OM->predicates())
|
||
|
if (isa<IntrinsicIDOperandMatcher>(OP)) {
|
||
|
Stash.push_back(std::move(OP));
|
||
|
OM->eraseNullPredicates();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (InsnVarID > 0) {
|
||
|
assert(!Operands.empty() && "Nested instruction is expected to def a vreg");
|
||
|
for (auto &OP : Operands[0]->predicates())
|
||
|
OP.reset();
|
||
|
Operands[0]->eraseNullPredicates();
|
||
|
}
|
||
|
for (auto &OM : Operands) {
|
||
|
for (auto &OP : OM->predicates())
|
||
|
if (isa<LLTOperandMatcher>(OP))
|
||
|
Stash.push_back(std::move(OP));
|
||
|
OM->eraseNullPredicates();
|
||
|
}
|
||
|
while (!Stash.empty())
|
||
|
prependPredicate(Stash.pop_back_val());
|
||
|
}
|
||
|
|
||
|
//===- InstructionOperandMatcher ------------------------------------------===//
|
||
|
|
||
|
void InstructionOperandMatcher::emitCaptureOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
const unsigned NewInsnVarID = InsnMatcher->getInsnVarID();
|
||
|
const bool IgnoreCopies = Flags & GISF_IgnoreCopies;
|
||
|
Table << MatchTable::Opcode(IgnoreCopies ? "GIM_RecordInsnIgnoreCopies"
|
||
|
: "GIM_RecordInsn")
|
||
|
<< MatchTable::Comment("DefineMI")
|
||
|
<< MatchTable::ULEB128Value(NewInsnVarID) << MatchTable::Comment("MI")
|
||
|
<< MatchTable::ULEB128Value(getInsnVarID())
|
||
|
<< MatchTable::Comment("OpIdx") << MatchTable::ULEB128Value(getOpIdx())
|
||
|
<< MatchTable::Comment("MIs[" + llvm::to_string(NewInsnVarID) + "]")
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
bool InstructionOperandMatcher::isHigherPriorityThan(
|
||
|
const OperandPredicateMatcher &B) const {
|
||
|
if (OperandPredicateMatcher::isHigherPriorityThan(B))
|
||
|
return true;
|
||
|
if (B.OperandPredicateMatcher::isHigherPriorityThan(*this))
|
||
|
return false;
|
||
|
|
||
|
if (const InstructionOperandMatcher *BP =
|
||
|
dyn_cast<InstructionOperandMatcher>(&B))
|
||
|
if (InsnMatcher->isHigherPriorityThan(*BP->InsnMatcher))
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//===- OperandRenderer ----------------------------------------------------===//
|
||
|
|
||
|
OperandRenderer::~OperandRenderer() {}
|
||
|
|
||
|
//===- CopyRenderer -------------------------------------------------------===//
|
||
|
|
||
|
void CopyRenderer::emitRenderOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
const OperandMatcher &Operand = Rule.getOperandMatcher(SymbolicName);
|
||
|
unsigned OldInsnVarID = Rule.getInsnVarID(Operand.getInstructionMatcher());
|
||
|
Table << MatchTable::Opcode("GIR_Copy") << MatchTable::Comment("NewInsnID")
|
||
|
<< MatchTable::ULEB128Value(NewInsnID)
|
||
|
<< MatchTable::Comment("OldInsnID")
|
||
|
<< MatchTable::ULEB128Value(OldInsnVarID)
|
||
|
<< MatchTable::Comment("OpIdx")
|
||
|
<< MatchTable::ULEB128Value(Operand.getOpIdx())
|
||
|
<< MatchTable::Comment(SymbolicName) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- CopyPhysRegRenderer ------------------------------------------------===//
|
||
|
|
||
|
void CopyPhysRegRenderer::emitRenderOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
const OperandMatcher &Operand = Rule.getPhysRegOperandMatcher(PhysReg);
|
||
|
unsigned OldInsnVarID = Rule.getInsnVarID(Operand.getInstructionMatcher());
|
||
|
Table << MatchTable::Opcode("GIR_Copy") << MatchTable::Comment("NewInsnID")
|
||
|
<< MatchTable::ULEB128Value(NewInsnID)
|
||
|
<< MatchTable::Comment("OldInsnID")
|
||
|
<< MatchTable::ULEB128Value(OldInsnVarID)
|
||
|
<< MatchTable::Comment("OpIdx")
|
||
|
<< MatchTable::ULEB128Value(Operand.getOpIdx())
|
||
|
<< MatchTable::Comment(PhysReg->getName()) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- CopyOrAddZeroRegRenderer -------------------------------------------===//
|
||
|
|
||
|
void CopyOrAddZeroRegRenderer::emitRenderOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
const OperandMatcher &Operand = Rule.getOperandMatcher(SymbolicName);
|
||
|
unsigned OldInsnVarID = Rule.getInsnVarID(Operand.getInstructionMatcher());
|
||
|
Table << MatchTable::Opcode("GIR_CopyOrAddZeroReg")
|
||
|
<< MatchTable::Comment("NewInsnID")
|
||
|
<< MatchTable::ULEB128Value(NewInsnID)
|
||
|
<< MatchTable::Comment("OldInsnID")
|
||
|
<< MatchTable::ULEB128Value(OldInsnVarID)
|
||
|
<< MatchTable::Comment("OpIdx")
|
||
|
<< MatchTable::ULEB128Value(Operand.getOpIdx())
|
||
|
<< MatchTable::NamedValue(
|
||
|
2,
|
||
|
(ZeroRegisterDef->getValue("Namespace")
|
||
|
? ZeroRegisterDef->getValueAsString("Namespace")
|
||
|
: ""),
|
||
|
ZeroRegisterDef->getName())
|
||
|
<< MatchTable::Comment(SymbolicName) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- CopyConstantAsImmRenderer ------------------------------------------===//
|
||
|
|
||
|
void CopyConstantAsImmRenderer::emitRenderOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
InstructionMatcher &InsnMatcher = Rule.getInstructionMatcher(SymbolicName);
|
||
|
unsigned OldInsnVarID = Rule.getInsnVarID(InsnMatcher);
|
||
|
Table << MatchTable::Opcode(Signed ? "GIR_CopyConstantAsSImm"
|
||
|
: "GIR_CopyConstantAsUImm")
|
||
|
<< MatchTable::Comment("NewInsnID")
|
||
|
<< MatchTable::ULEB128Value(NewInsnID)
|
||
|
<< MatchTable::Comment("OldInsnID")
|
||
|
<< MatchTable::ULEB128Value(OldInsnVarID)
|
||
|
<< MatchTable::Comment(SymbolicName) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- CopyFConstantAsFPImmRenderer ---------------------------------------===//
|
||
|
|
||
|
void CopyFConstantAsFPImmRenderer::emitRenderOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
InstructionMatcher &InsnMatcher = Rule.getInstructionMatcher(SymbolicName);
|
||
|
unsigned OldInsnVarID = Rule.getInsnVarID(InsnMatcher);
|
||
|
Table << MatchTable::Opcode("GIR_CopyFConstantAsFPImm")
|
||
|
<< MatchTable::Comment("NewInsnID")
|
||
|
<< MatchTable::ULEB128Value(NewInsnID)
|
||
|
<< MatchTable::Comment("OldInsnID")
|
||
|
<< MatchTable::ULEB128Value(OldInsnVarID)
|
||
|
<< MatchTable::Comment(SymbolicName) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- CopySubRegRenderer -------------------------------------------------===//
|
||
|
|
||
|
void CopySubRegRenderer::emitRenderOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
const OperandMatcher &Operand = Rule.getOperandMatcher(SymbolicName);
|
||
|
unsigned OldInsnVarID = Rule.getInsnVarID(Operand.getInstructionMatcher());
|
||
|
Table << MatchTable::Opcode("GIR_CopySubReg")
|
||
|
<< MatchTable::Comment("NewInsnID")
|
||
|
<< MatchTable::ULEB128Value(NewInsnID)
|
||
|
<< MatchTable::Comment("OldInsnID")
|
||
|
<< MatchTable::ULEB128Value(OldInsnVarID)
|
||
|
<< MatchTable::Comment("OpIdx")
|
||
|
<< MatchTable::ULEB128Value(Operand.getOpIdx())
|
||
|
<< MatchTable::Comment("SubRegIdx")
|
||
|
<< MatchTable::IntValue(2, SubReg->EnumValue)
|
||
|
<< MatchTable::Comment(SymbolicName) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- AddRegisterRenderer ------------------------------------------------===//
|
||
|
|
||
|
void AddRegisterRenderer::emitRenderOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIR_AddRegister")
|
||
|
<< MatchTable::Comment("InsnID") << MatchTable::ULEB128Value(InsnID);
|
||
|
if (RegisterDef->getName() != "zero_reg") {
|
||
|
Table << MatchTable::NamedValue(
|
||
|
2,
|
||
|
(RegisterDef->getValue("Namespace")
|
||
|
? RegisterDef->getValueAsString("Namespace")
|
||
|
: ""),
|
||
|
RegisterDef->getName());
|
||
|
} else {
|
||
|
Table << MatchTable::NamedValue(2, Target.getRegNamespace(), "NoRegister");
|
||
|
}
|
||
|
Table << MatchTable::Comment("AddRegisterRegFlags");
|
||
|
|
||
|
// TODO: This is encoded as a 64-bit element, but only 16 or 32-bits are
|
||
|
// really needed for a physical register reference. We can pack the
|
||
|
// register and flags in a single field.
|
||
|
if (IsDef)
|
||
|
Table << MatchTable::NamedValue(2, "RegState::Define");
|
||
|
else
|
||
|
Table << MatchTable::IntValue(2, 0);
|
||
|
Table << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- TempRegRenderer ----------------------------------------------------===//
|
||
|
|
||
|
void TempRegRenderer::emitRenderOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
const bool NeedsFlags = (SubRegIdx || IsDef);
|
||
|
if (SubRegIdx) {
|
||
|
assert(!IsDef);
|
||
|
Table << MatchTable::Opcode("GIR_AddTempSubRegister");
|
||
|
} else
|
||
|
Table << MatchTable::Opcode(NeedsFlags ? "GIR_AddTempRegister"
|
||
|
: "GIR_AddSimpleTempRegister");
|
||
|
|
||
|
Table << MatchTable::Comment("InsnID") << MatchTable::ULEB128Value(InsnID)
|
||
|
<< MatchTable::Comment("TempRegID")
|
||
|
<< MatchTable::ULEB128Value(TempRegID);
|
||
|
|
||
|
if (!NeedsFlags) {
|
||
|
Table << MatchTable::LineBreak;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Table << MatchTable::Comment("TempRegFlags");
|
||
|
if (IsDef) {
|
||
|
SmallString<32> RegFlags;
|
||
|
RegFlags += "RegState::Define";
|
||
|
if (IsDead)
|
||
|
RegFlags += "|RegState::Dead";
|
||
|
Table << MatchTable::NamedValue(2, RegFlags);
|
||
|
} else
|
||
|
Table << MatchTable::IntValue(2, 0);
|
||
|
|
||
|
if (SubRegIdx)
|
||
|
Table << MatchTable::NamedValue(2, SubRegIdx->getQualifiedName());
|
||
|
Table << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- ImmRenderer --------------------------------------------------------===//
|
||
|
|
||
|
void ImmRenderer::emitAddImm(MatchTable &Table, RuleMatcher &RM,
|
||
|
unsigned InsnID, int64_t Imm, StringRef ImmName) {
|
||
|
const bool IsInt8 = isInt<8>(Imm);
|
||
|
|
||
|
Table << MatchTable::Opcode(IsInt8 ? "GIR_AddImm8" : "GIR_AddImm")
|
||
|
<< MatchTable::Comment("InsnID") << MatchTable::ULEB128Value(InsnID)
|
||
|
<< MatchTable::Comment(ImmName)
|
||
|
<< MatchTable::IntValue(IsInt8 ? 1 : 8, Imm) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
void ImmRenderer::emitRenderOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
if (CImmLLT) {
|
||
|
assert(Table.isCombiner() &&
|
||
|
"ConstantInt immediate are only for combiners!");
|
||
|
Table << MatchTable::Opcode("GIR_AddCImm") << MatchTable::Comment("InsnID")
|
||
|
<< MatchTable::ULEB128Value(InsnID) << MatchTable::Comment("Type")
|
||
|
<< *CImmLLT << MatchTable::Comment("Imm")
|
||
|
<< MatchTable::IntValue(8, Imm) << MatchTable::LineBreak;
|
||
|
} else
|
||
|
emitAddImm(Table, Rule, InsnID, Imm);
|
||
|
}
|
||
|
|
||
|
//===- SubRegIndexRenderer ------------------------------------------------===//
|
||
|
|
||
|
void SubRegIndexRenderer::emitRenderOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
ImmRenderer::emitAddImm(Table, Rule, InsnID, SubRegIdx->EnumValue,
|
||
|
"SubRegIndex");
|
||
|
}
|
||
|
|
||
|
//===- RenderComplexPatternOperand ----------------------------------------===//
|
||
|
|
||
|
void RenderComplexPatternOperand::emitRenderOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode(
|
||
|
SubOperand ? (SubReg ? "GIR_ComplexSubOperandSubRegRenderer"
|
||
|
: "GIR_ComplexSubOperandRenderer")
|
||
|
: "GIR_ComplexRenderer")
|
||
|
<< MatchTable::Comment("InsnID") << MatchTable::ULEB128Value(InsnID)
|
||
|
<< MatchTable::Comment("RendererID")
|
||
|
<< MatchTable::IntValue(2, RendererID);
|
||
|
if (SubOperand)
|
||
|
Table << MatchTable::Comment("SubOperand")
|
||
|
<< MatchTable::ULEB128Value(*SubOperand);
|
||
|
if (SubReg)
|
||
|
Table << MatchTable::Comment("SubRegIdx")
|
||
|
<< MatchTable::IntValue(2, SubReg->EnumValue);
|
||
|
Table << MatchTable::Comment(SymbolicName) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- CustomRenderer -----------------------------------------------------===//
|
||
|
|
||
|
void CustomRenderer::emitRenderOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
InstructionMatcher &InsnMatcher = Rule.getInstructionMatcher(SymbolicName);
|
||
|
unsigned OldInsnVarID = Rule.getInsnVarID(InsnMatcher);
|
||
|
Table << MatchTable::Opcode("GIR_CustomRenderer")
|
||
|
<< MatchTable::Comment("InsnID") << MatchTable::ULEB128Value(InsnID)
|
||
|
<< MatchTable::Comment("OldInsnID")
|
||
|
<< MatchTable::ULEB128Value(OldInsnVarID)
|
||
|
<< MatchTable::Comment("Renderer")
|
||
|
<< MatchTable::NamedValue(
|
||
|
2, "GICR_" + Renderer.getValueAsString("RendererFn").str())
|
||
|
<< MatchTable::Comment(SymbolicName) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- CustomOperandRenderer ----------------------------------------------===//
|
||
|
|
||
|
void CustomOperandRenderer::emitRenderOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
const OperandMatcher &OpdMatcher = Rule.getOperandMatcher(SymbolicName);
|
||
|
Table << MatchTable::Opcode("GIR_CustomOperandRenderer")
|
||
|
<< MatchTable::Comment("InsnID") << MatchTable::ULEB128Value(InsnID)
|
||
|
<< MatchTable::Comment("OldInsnID")
|
||
|
<< MatchTable::ULEB128Value(OpdMatcher.getInsnVarID())
|
||
|
<< MatchTable::Comment("OpIdx")
|
||
|
<< MatchTable::ULEB128Value(OpdMatcher.getOpIdx())
|
||
|
<< MatchTable::Comment("OperandRenderer")
|
||
|
<< MatchTable::NamedValue(
|
||
|
2, "GICR_" + Renderer.getValueAsString("RendererFn").str())
|
||
|
<< MatchTable::Comment(SymbolicName) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- CustomCXXAction ----------------------------------------------------===//
|
||
|
|
||
|
void CustomCXXAction::emitActionOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIR_CustomAction")
|
||
|
<< MatchTable::NamedValue(2, FnEnumName) << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- BuildMIAction ------------------------------------------------------===//
|
||
|
|
||
|
bool BuildMIAction::canMutate(RuleMatcher &Rule,
|
||
|
const InstructionMatcher *Insn) const {
|
||
|
if (!Insn)
|
||
|
return false;
|
||
|
|
||
|
if (OperandRenderers.size() != Insn->getNumOperands())
|
||
|
return false;
|
||
|
|
||
|
for (const auto &Renderer : enumerate(OperandRenderers)) {
|
||
|
if (const auto *Copy = dyn_cast<CopyRenderer>(&*Renderer.value())) {
|
||
|
const OperandMatcher &OM =
|
||
|
Rule.getOperandMatcher(Copy->getSymbolicName());
|
||
|
if (Insn != &OM.getInstructionMatcher() ||
|
||
|
OM.getOpIdx() != Renderer.index())
|
||
|
return false;
|
||
|
} else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void BuildMIAction::chooseInsnToMutate(RuleMatcher &Rule) {
|
||
|
for (auto *MutateCandidate : Rule.mutatable_insns()) {
|
||
|
if (canMutate(Rule, MutateCandidate)) {
|
||
|
// Take the first one we're offered that we're able to mutate.
|
||
|
Rule.reserveInsnMatcherForMutation(MutateCandidate);
|
||
|
Matched = MutateCandidate;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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::ULEB128Value(InsnID)
|
||
|
<< MatchTable::Comment("OldInsnID")
|
||
|
<< MatchTable::ULEB128Value(IM->getInsnVarID())
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
if (!SetFlags.empty()) {
|
||
|
Table << MatchTable::Opcode("GIR_SetMIFlags")
|
||
|
<< MatchTable::Comment("InsnID") << MatchTable::ULEB128Value(InsnID)
|
||
|
<< MatchTable::NamedValue(4, join(SetFlags, " | "))
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
if (!UnsetFlags.empty()) {
|
||
|
Table << MatchTable::Opcode("GIR_UnsetMIFlags")
|
||
|
<< MatchTable::Comment("InsnID") << MatchTable::ULEB128Value(InsnID)
|
||
|
<< MatchTable::NamedValue(4, join(UnsetFlags, " | "))
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
if (Matched) {
|
||
|
assert(canMutate(Rule, Matched) &&
|
||
|
"Arranged to mutate an insn that isn't mutatable");
|
||
|
|
||
|
unsigned RecycleInsnID = Rule.getInsnVarID(*Matched);
|
||
|
Table << MatchTable::Opcode("GIR_MutateOpcode")
|
||
|
<< MatchTable::Comment("InsnID") << MatchTable::ULEB128Value(InsnID)
|
||
|
<< MatchTable::Comment("RecycleInsnID")
|
||
|
<< MatchTable::ULEB128Value(RecycleInsnID)
|
||
|
<< MatchTable::Comment("Opcode")
|
||
|
<< MatchTable::NamedValue(2, I->Namespace, I->TheDef->getName())
|
||
|
<< MatchTable::LineBreak;
|
||
|
|
||
|
if (!I->ImplicitDefs.empty() || !I->ImplicitUses.empty()) {
|
||
|
for (auto *Def : I->ImplicitDefs) {
|
||
|
auto Namespace = Def->getValue("Namespace")
|
||
|
? Def->getValueAsString("Namespace")
|
||
|
: "";
|
||
|
const bool IsDead = DeadImplicitDefs.contains(Def);
|
||
|
Table << MatchTable::Opcode("GIR_AddImplicitDef")
|
||
|
<< MatchTable::Comment("InsnID")
|
||
|
<< MatchTable::ULEB128Value(InsnID)
|
||
|
<< MatchTable::NamedValue(2, Namespace, Def->getName())
|
||
|
<< (IsDead ? MatchTable::NamedValue(2, "RegState", "Dead")
|
||
|
: MatchTable::IntValue(2, 0))
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
for (auto *Use : I->ImplicitUses) {
|
||
|
auto Namespace = Use->getValue("Namespace")
|
||
|
? Use->getValueAsString("Namespace")
|
||
|
: "";
|
||
|
Table << MatchTable::Opcode("GIR_AddImplicitUse")
|
||
|
<< MatchTable::Comment("InsnID")
|
||
|
<< MatchTable::ULEB128Value(InsnID)
|
||
|
<< MatchTable::NamedValue(2, Namespace, Use->getName())
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
AddMIFlags();
|
||
|
|
||
|
// Mark the mutated instruction as erased.
|
||
|
Rule.tryEraseInsnID(RecycleInsnID);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// TODO: Simple permutation looks like it could be almost as common as
|
||
|
// mutation due to commutative operations.
|
||
|
|
||
|
Table << MatchTable::Opcode("GIR_BuildMI") << MatchTable::Comment("InsnID")
|
||
|
<< MatchTable::ULEB128Value(InsnID) << MatchTable::Comment("Opcode")
|
||
|
<< MatchTable::NamedValue(2, I->Namespace, I->TheDef->getName())
|
||
|
<< MatchTable::LineBreak;
|
||
|
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::ULEB128Value(InsnID)
|
||
|
<< MatchTable::Comment(
|
||
|
("OpIdx for " + Namespace + "::" + Def->getName() + "").str())
|
||
|
<< MatchTable::ULEB128Value(OpIdx) << MatchTable::LineBreak;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (I->mayLoad || I->mayStore) {
|
||
|
// Emit the ID's for all the instructions that are matched by this rule.
|
||
|
// TODO: Limit this to matched instructions that mayLoad/mayStore or have
|
||
|
// some other means of having a memoperand. Also limit this to
|
||
|
// emitted instructions that expect to have a memoperand too. For
|
||
|
// example, (G_SEXT (G_LOAD x)) that results in separate load and
|
||
|
// sign-extend instructions shouldn't put the memoperand on the
|
||
|
// sign-extend since it has no effect there.
|
||
|
|
||
|
std::vector<unsigned> MergeInsnIDs;
|
||
|
for (const auto &IDMatcherPair : Rule.defined_insn_vars())
|
||
|
MergeInsnIDs.push_back(IDMatcherPair.second);
|
||
|
llvm::sort(MergeInsnIDs);
|
||
|
|
||
|
Table << MatchTable::Opcode("GIR_MergeMemOperands")
|
||
|
<< MatchTable::Comment("InsnID") << MatchTable::ULEB128Value(InsnID)
|
||
|
<< MatchTable::Comment("NumInsns")
|
||
|
<< MatchTable::IntValue(1, MergeInsnIDs.size())
|
||
|
<< MatchTable::Comment("MergeInsnID's");
|
||
|
for (const auto &MergeInsnID : MergeInsnIDs)
|
||
|
Table << MatchTable::ULEB128Value(MergeInsnID);
|
||
|
Table << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
AddMIFlags();
|
||
|
}
|
||
|
|
||
|
//===- BuildConstantAction ------------------------------------------------===//
|
||
|
|
||
|
void BuildConstantAction::emitActionOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIR_BuildConstant")
|
||
|
<< MatchTable::Comment("TempRegID")
|
||
|
<< MatchTable::ULEB128Value(TempRegID) << MatchTable::Comment("Val")
|
||
|
<< MatchTable::IntValue(8, 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::ULEB128Value(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::ULEB128Value(OldInsnID)
|
||
|
<< MatchTable::Comment("OldOpIdx") << MatchTable::ULEB128Value(OldOpIdx)
|
||
|
<< MatchTable::Comment("NewInsnId")
|
||
|
<< MatchTable::ULEB128Value(NewInsnId)
|
||
|
<< MatchTable::Comment("NewOpIdx") << MatchTable::ULEB128Value(NewOpIdx)
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
void ReplaceRegAction::emitActionOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
if (TempRegID != (unsigned)-1) {
|
||
|
Table << MatchTable::Opcode("GIR_ReplaceRegWithTempReg")
|
||
|
<< MatchTable::Comment("OldInsnID")
|
||
|
<< MatchTable::ULEB128Value(OldInsnID)
|
||
|
<< MatchTable::Comment("OldOpIdx")
|
||
|
<< MatchTable::ULEB128Value(OldOpIdx)
|
||
|
<< MatchTable::Comment("TempRegID")
|
||
|
<< MatchTable::ULEB128Value(TempRegID) << MatchTable::LineBreak;
|
||
|
} else {
|
||
|
Table << MatchTable::Opcode("GIR_ReplaceReg")
|
||
|
<< MatchTable::Comment("OldInsnID")
|
||
|
<< MatchTable::ULEB128Value(OldInsnID)
|
||
|
<< MatchTable::Comment("OldOpIdx")
|
||
|
<< MatchTable::ULEB128Value(OldOpIdx)
|
||
|
<< MatchTable::Comment("NewInsnId")
|
||
|
<< MatchTable::ULEB128Value(NewInsnId)
|
||
|
<< MatchTable::Comment("NewOpIdx")
|
||
|
<< MatchTable::ULEB128Value(NewOpIdx) << MatchTable::LineBreak;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//===- ConstrainOperandToRegClassAction -----------------------------------===//
|
||
|
|
||
|
void ConstrainOperandToRegClassAction::emitActionOpcodes(
|
||
|
MatchTable &Table, RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIR_ConstrainOperandRC")
|
||
|
<< MatchTable::Comment("InsnID") << MatchTable::ULEB128Value(InsnID)
|
||
|
<< MatchTable::Comment("Op") << MatchTable::ULEB128Value(OpIdx)
|
||
|
<< MatchTable::NamedValue(2, RC.getQualifiedIdName())
|
||
|
<< MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
//===- MakeTempRegisterAction ---------------------------------------------===//
|
||
|
|
||
|
void MakeTempRegisterAction::emitActionOpcodes(MatchTable &Table,
|
||
|
RuleMatcher &Rule) const {
|
||
|
Table << MatchTable::Opcode("GIR_MakeTempReg")
|
||
|
<< MatchTable::Comment("TempRegID")
|
||
|
<< MatchTable::ULEB128Value(TempRegID) << MatchTable::Comment("TypeID")
|
||
|
<< Ty << MatchTable::LineBreak;
|
||
|
}
|
||
|
|
||
|
} // namespace gi
|
||
|
} // namespace llvm
|