Files
clang-p2996/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp
pvanhout 844c0da777 [TableGen][GlobalISel] Add MIR Pattern Builtins
Adds a new feature to MIR patterns: builtin instructions.
They offer some additional capabilities that currently cannot be expressed without falling back to C++ code.
There are two builtins added with this patch, but more can be added later as new needs arise:
 - GIReplaceReg
 - GIEraseRoot

Depends on D158714, D158713

Reviewed By: arsenm, aemerson

Differential Revision: https://reviews.llvm.org/D158975
2023-09-05 08:19:07 +02:00

3737 lines
123 KiB
C++

//===- GlobalISelCombinerMatchTableEmitter.cpp - --------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file Generate a combiner implementation for GlobalISel from a declarative
/// syntax using GlobalISelMatchTable.
///
/// Usually, TableGen backends use "assert is an error" as a means to report
/// invalid input. They try to diagnose common case but don't try very hard and
/// crashes can be common. This backend aims to behave closer to how a language
/// compiler frontend would behave: we try extra hard to diagnose invalid inputs
/// early, and any crash should be considered a bug (= a feature or diagnostic
/// is missing).
///
/// While this can make the backend a bit more complex than it needs to be, it
/// pays off because MIR patterns can get complicated. Giving useful error
/// messages to combine writers can help boost their productivity.
///
/// As with anything, a good balance has to be found. We also don't want to
/// write hundreds of lines of code to detect edge cases. In practice, crashing
/// very occasionally, or giving poor errors in some rare instances, is fine.
///
//===----------------------------------------------------------------------===//
#include "CodeGenInstruction.h"
#include "CodeGenTarget.h"
#include "GlobalISel/CodeExpander.h"
#include "GlobalISel/CodeExpansions.h"
#include "GlobalISel/CombinerUtils.h"
#include "GlobalISelMatchTable.h"
#include "GlobalISelMatchTableExecutorEmitter.h"
#include "SubtargetFeatureInfo.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/StringMatcher.h"
#include "llvm/TableGen/TableGenBackend.h"
#include <cstdint>
using namespace llvm;
using namespace llvm::gi;
#define DEBUG_TYPE "gicombiner-emitter"
namespace {
cl::OptionCategory
GICombinerEmitterCat("Options for -gen-global-isel-combiner");
cl::opt<bool> StopAfterParse(
"gicombiner-stop-after-parse",
cl::desc("Stop processing after parsing rules and dump state"),
cl::cat(GICombinerEmitterCat));
cl::list<std::string>
SelectedCombiners("combiners", cl::desc("Emit the specified combiners"),
cl::cat(GICombinerEmitterCat), cl::CommaSeparated);
cl::opt<bool> DebugCXXPreds(
"gicombiner-debug-cxxpreds",
cl::desc("Add Contextual/Debug comments to all C++ predicates"),
cl::cat(GICombinerEmitterCat));
constexpr StringLiteral CXXApplyPrefix = "GICXXCustomAction_CombineApply";
constexpr StringLiteral CXXPredPrefix = "GICXXPred_MI_Predicate_";
constexpr StringLiteral PatFragClassName = "GICombinePatFrag";
constexpr StringLiteral BuiltinInstClassName = "GIBuiltinInst";
std::string getIsEnabledPredicateEnumName(unsigned CombinerRuleID) {
return "GICXXPred_Simple_IsRule" + to_string(CombinerRuleID) + "Enabled";
}
/// Copies a StringRef into a static pool to make sure it has a static lifetime.
StringRef insertStrRef(StringRef S) {
if (S.empty())
return {};
static StringSet<> Pool;
auto [It, Inserted] = Pool.insert(S);
return It->getKey();
}
void declareInstExpansion(CodeExpansions &CE, const InstructionMatcher &IM,
StringRef Name) {
CE.declare(Name, "State.MIs[" + to_string(IM.getInsnVarID()) + "]");
}
void declareInstExpansion(CodeExpansions &CE, const BuildMIAction &A,
StringRef Name) {
// Note: we use redeclare here because this may overwrite a matcher inst
// expansion.
CE.redeclare(Name, "OutMIs[" + to_string(A.getInsnID()) + "]");
}
void declareOperandExpansion(CodeExpansions &CE, const OperandMatcher &OM,
StringRef Name) {
CE.declare(Name, "State.MIs[" + to_string(OM.getInsnVarID()) +
"]->getOperand(" + to_string(OM.getOpIdx()) + ")");
}
void declareTempRegExpansion(CodeExpansions &CE, unsigned TempRegID,
StringRef Name) {
CE.declare(Name, "State.TempRegisters[" + to_string(TempRegID) + "]");
}
std::string makeAnonPatName(StringRef Prefix, unsigned Idx) {
return ("__" + Prefix + "_" + Twine(Idx)).str();
}
template <typename Container> auto keys(Container &&C) {
return map_range(C, [](auto &Entry) -> auto & { return Entry.first; });
}
template <typename Container> auto values(Container &&C) {
return map_range(C, [](auto &Entry) -> auto & { return Entry.second; });
}
LLTCodeGen getLLTCodeGenFromRecord(const Record *Ty) {
assert(Ty->isSubClassOf("ValueType"));
return LLTCodeGen(*MVTToLLT(getValueType(Ty)));
}
//===- MatchData Handling -------------------------------------------------===//
/// Represents MatchData defined by the match stage and required by the apply
/// stage.
///
/// This allows the plumbing of arbitrary data from C++ predicates between the
/// stages.
///
/// When this class is initially created, it only has a pattern symbol and a
/// type. When all of the MatchDatas declarations of a given pattern have been
/// parsed, `AssignVariables` must be called to assign storage variable names to
/// each MatchDataInfo.
class MatchDataInfo {
StringRef PatternSymbol;
StringRef Type;
std::string VarName;
public:
static constexpr StringLiteral StructTypeName = "MatchInfosTy";
static constexpr StringLiteral StructName = "MatchInfos";
MatchDataInfo(StringRef PatternSymbol, StringRef Type)
: PatternSymbol(PatternSymbol), Type(Type.trim()) {}
StringRef getPatternSymbol() const { return PatternSymbol; };
StringRef getType() const { return Type; };
bool hasVariableName() const { return !VarName.empty(); }
void setVariableName(StringRef Name) { VarName = Name; }
StringRef getVariableName() const;
std::string getQualifiedVariableName() const {
return StructName.str() + "." + getVariableName().str();
}
void print(raw_ostream &OS) const;
void dump() const { print(dbgs()); }
};
StringRef MatchDataInfo::getVariableName() const {
assert(hasVariableName());
return VarName;
}
void MatchDataInfo::print(raw_ostream &OS) const {
OS << "(MatchDataInfo pattern_symbol:" << PatternSymbol << " type:'" << Type
<< "' var_name:" << (VarName.empty() ? "<unassigned>" : VarName) << ")";
}
/// Pool of type -> variables used to emit MatchData variables declarations.
///
/// e.g. if the map contains "int64_t" -> ["MD0", "MD1"], then two variable
/// declarations must be emitted: `int64_t MD0` and `int64_t MD1`.
///
/// This has a static lifetime and will outlive all the `MatchDataInfo` objects
/// by design. It needs to persist after all `CombineRuleBuilder` objects died
/// so we can emit the variable declarations.
StringMap<std::vector<std::string>> AllMatchDataVars;
// Assign variable names to all MatchDatas used by a pattern. This must be
// called after all MatchData decls have been parsed inside a rule.
//
// Requires an array of MatchDataInfo so we can handle cases where a pattern
// uses multiple instances of the same MatchData type.
void AssignMatchDataVariables(MutableArrayRef<MatchDataInfo> Infos) {
static unsigned NextVarID = 0;
StringMap<unsigned> SeenTypes;
for (auto &Info : Infos) {
unsigned &NumSeen = SeenTypes[Info.getType()];
auto &ExistingVars = AllMatchDataVars[Info.getType()];
if (NumSeen == ExistingVars.size())
ExistingVars.push_back("MDInfo" + to_string(NextVarID++));
Info.setVariableName(ExistingVars[NumSeen++]);
}
}
//===- C++ Predicates Handling --------------------------------------------===//
/// Entry into the static pool of all CXX Predicate code. This contains
/// fully expanded C++ code.
///
/// The static pool is hidden inside the object and can be accessed through
/// getAllMatchCode/getAllApplyCode
///
/// Note that CXXPattern trims C++ code, so the Code is already expected to be
/// free of leading/trailing whitespace.
class CXXPredicateCode {
using CXXPredicateCodePool =
DenseMap<hash_code, std::unique_ptr<CXXPredicateCode>>;
static CXXPredicateCodePool AllCXXMatchCode;
static CXXPredicateCodePool AllCXXApplyCode;
/// Sorts a `CXXPredicateCodePool` by their IDs and returns it.
static std::vector<const CXXPredicateCode *>
getSorted(const CXXPredicateCodePool &Pool) {
std::vector<const CXXPredicateCode *> Out;
std::transform(Pool.begin(), Pool.end(), std::back_inserter(Out),
[&](auto &Elt) { return Elt.second.get(); });
sort(Out, [](const auto *A, const auto *B) { return A->ID < B->ID; });
return Out;
}
/// Gets an instance of `CXXPredicateCode` for \p Code, or returns an already
/// existing one.
static const CXXPredicateCode &get(CXXPredicateCodePool &Pool,
std::string Code) {
// Check if we already have an identical piece of code, if not, create an
// entry in the pool.
const auto CodeHash = hash_value(Code);
if (auto It = Pool.find(CodeHash); It != Pool.end())
return *It->second;
const auto ID = Pool.size();
auto OwnedData = std::unique_ptr<CXXPredicateCode>(
new CXXPredicateCode(std::move(Code), ID));
const auto &DataRef = *OwnedData;
Pool[CodeHash] = std::move(OwnedData);
return DataRef;
}
CXXPredicateCode(std::string Code, unsigned ID)
: Code(Code), ID(ID), BaseEnumName("GICombiner" + to_string(ID)) {
// Don't assert if ErrorsPrinted is set. This may mean CodeExpander failed,
// and it may add spaces in such cases.
assert((ErrorsPrinted || StringRef(Code).trim() == Code) &&
"Code was expected to be trimmed!");
}
public:
static const CXXPredicateCode &getMatchCode(std::string Code) {
return get(AllCXXMatchCode, std::move(Code));
}
static const CXXPredicateCode &getApplyCode(std::string Code) {
return get(AllCXXApplyCode, std::move(Code));
}
static std::vector<const CXXPredicateCode *> getAllMatchCode() {
return getSorted(AllCXXMatchCode);
}
static std::vector<const CXXPredicateCode *> getAllApplyCode() {
return getSorted(AllCXXApplyCode);
}
const std::string Code;
const unsigned ID;
const std::string BaseEnumName;
bool needsUnreachable() const {
return !StringRef(Code).starts_with("return");
}
std::string getEnumNameWithPrefix(StringRef Prefix) const {
return Prefix.str() + BaseEnumName;
}
};
CXXPredicateCode::CXXPredicateCodePool CXXPredicateCode::AllCXXMatchCode;
CXXPredicateCode::CXXPredicateCodePool CXXPredicateCode::AllCXXApplyCode;
//===- Pattern Base Class -------------------------------------------------===//
/// Base class for all patterns that can be written in an `apply`, `match` or
/// `pattern` DAG operator.
///
/// For example:
///
/// (apply (G_ZEXT $x, $y), (G_ZEXT $y, $z), "return isFoo(${z})")
///
/// Creates 3 Pattern objects:
/// - Two CodeGenInstruction Patterns
/// - A CXXPattern
class Pattern {
public:
enum {
K_AnyOpcode,
K_CXX,
K_CodeGenInstruction,
K_PatFrag,
K_Builtin,
};
virtual ~Pattern() = default;
unsigned getKind() const { return Kind; }
const char *getKindName() const;
bool hasName() const { return !Name.empty(); }
StringRef getName() const { return Name; }
virtual void print(raw_ostream &OS, bool PrintName = true) const = 0;
void dump() const { return print(dbgs()); }
protected:
Pattern(unsigned Kind, StringRef Name)
: Kind(Kind), Name(insertStrRef(Name)) {
assert(!Name.empty() && "unnamed pattern!");
}
void printImpl(raw_ostream &OS, bool PrintName,
function_ref<void()> ContentPrinter) const;
private:
unsigned Kind;
StringRef Name;
};
const char *Pattern::getKindName() const {
switch (Kind) {
case K_AnyOpcode:
return "AnyOpcodePattern";
case K_CXX:
return "CXXPattern";
case K_CodeGenInstruction:
return "CodeGenInstructionPattern";
case K_PatFrag:
return "PatFragPattern";
case K_Builtin:
return "BuiltinPattern";
}
llvm_unreachable("unknown pattern kind!");
}
void Pattern::printImpl(raw_ostream &OS, bool PrintName,
function_ref<void()> ContentPrinter) const {
OS << "(" << getKindName() << " ";
if (PrintName)
OS << "name:" << getName() << " ";
ContentPrinter();
OS << ")";
}
//===- AnyOpcodePattern ---------------------------------------------------===//
/// `wip_match_opcode` patterns.
/// This matches one or more opcodes, and does not check any operands
/// whatsoever.
///
/// TODO: Long-term, this needs to be removed. It's a hack around MIR
/// pattern matching limitations.
class AnyOpcodePattern : public Pattern {
public:
AnyOpcodePattern(StringRef Name) : Pattern(K_AnyOpcode, Name) {}
static bool classof(const Pattern *P) { return P->getKind() == K_AnyOpcode; }
void addOpcode(const CodeGenInstruction *I) { Insts.push_back(I); }
const auto &insts() const { return Insts; }
void print(raw_ostream &OS, bool PrintName = true) const override;
private:
SmallVector<const CodeGenInstruction *, 4> Insts;
};
void AnyOpcodePattern::print(raw_ostream &OS, bool PrintName) const {
printImpl(OS, PrintName, [&OS, this]() {
OS << "["
<< join(map_range(Insts,
[](const auto *I) { return I->TheDef->getName(); }),
", ")
<< "]";
});
}
//===- CXXPattern ---------------------------------------------------------===//
/// Represents raw C++ code which may need some expansions.
///
/// e.g. [{ return isFooBux(${src}.getReg()); }]
///
/// For the expanded code, \see CXXPredicateCode. CXXPredicateCode objects are
/// created through `expandCode`.
///
/// \see CodeExpander and \see CodeExpansions for more information on code
/// expansions.
///
/// This object has two purposes:
/// - Represent C++ code as a pattern entry.
/// - Be a factory for expanded C++ code.
/// - It's immutable and only holds the raw code so we can expand the same
/// CXX pattern multiple times if we need to.
///
/// Note that the code is always trimmed in the constructor, so leading and
/// trailing whitespaces are removed. This removes bloat in the output, avoids
/// formatting issues, but also allows us to check things like
/// `.startswith("return")` trivially without worrying about spaces.
class CXXPattern : public Pattern {
public:
CXXPattern(const StringInit &Code, StringRef Name)
: CXXPattern(Code.getAsUnquotedString(), Name) {}
CXXPattern(StringRef Code, StringRef Name)
: Pattern(K_CXX, Name), RawCode(Code.trim().str()) {}
static bool classof(const Pattern *P) { return P->getKind() == K_CXX; }
void setIsApply(bool Value = true) { IsApply = Value; }
StringRef getRawCode() const { return RawCode; }
/// Expands raw code, replacing things such as `${foo}` with their
/// substitution in \p CE.
///
/// \param CE Map of Code Expansions
/// \param Locs SMLocs for the Code Expander, in case it needs to emit
/// diagnostics.
/// \param AddComment If DebugCXXPreds is enabled, this is called to emit a
/// comment before the expanded code.
///
/// \return A CXXPredicateCode object that contains the expanded code. Note
/// that this may or may not insert a new object. All CXXPredicateCode objects
/// are held in a set to avoid emitting duplicate C++ code.
const CXXPredicateCode &
expandCode(const CodeExpansions &CE, ArrayRef<SMLoc> Locs,
function_ref<void(raw_ostream &)> AddComment = {}) const;
void print(raw_ostream &OS, bool PrintName = true) const override;
private:
bool IsApply = false;
std::string RawCode;
};
const CXXPredicateCode &
CXXPattern::expandCode(const CodeExpansions &CE, ArrayRef<SMLoc> Locs,
function_ref<void(raw_ostream &)> AddComment) const {
std::string Result;
raw_string_ostream OS(Result);
if (DebugCXXPreds && AddComment)
AddComment(OS);
CodeExpander Expander(RawCode, CE, Locs, /*ShowExpansions*/ false);
Expander.emit(OS);
if (IsApply)
return CXXPredicateCode::getApplyCode(std::move(Result));
return CXXPredicateCode::getMatchCode(std::move(Result));
}
void CXXPattern::print(raw_ostream &OS, bool PrintName) const {
printImpl(OS, PrintName, [&OS, this] {
OS << (IsApply ? "apply" : "match") << " code:\"";
printEscapedString(getRawCode(), OS);
OS << "\"";
});
}
//===- InstructionPattern ---------------------------------------------===//
/// An operand for an InstructionPattern.
///
/// Operands are composed of three elements:
/// - (Optional) Value
/// - (Optional) Name
/// - (Optional) Type
///
/// Some examples:
/// (i32 0):$x -> V=int(0), Name='x', Type=i32
/// 0:$x -> V=int(0), Name='x'
/// $x -> Name='x'
/// i32:$x -> Name='x', Type = i32
class InstructionOperand {
public:
using IntImmTy = int64_t;
InstructionOperand(IntImmTy Imm, StringRef Name, const Record *Type)
: Value(Imm), Name(insertStrRef(Name)), Type(Type) {
assert(!Type || Type->isSubClassOf("ValueType"));
}
InstructionOperand(StringRef Name, const Record *Type)
: Name(insertStrRef(Name)), Type(Type) {}
bool isNamedImmediate() const { return hasImmValue() && isNamedOperand(); }
bool hasImmValue() const { return Value.has_value(); }
IntImmTy getImmValue() const { return *Value; }
bool isNamedOperand() const { return !Name.empty(); }
StringRef getOperandName() const {
assert(isNamedOperand() && "Operand is unnamed");
return Name;
}
InstructionOperand withNewName(StringRef NewName) const {
InstructionOperand Result = *this;
Result.Name = insertStrRef(NewName);
return Result;
}
void setIsDef(bool Value = true) { Def = Value; }
bool isDef() const { return Def; }
void setType(const Record *R) {
assert((!Type || (Type == R)) && "Overwriting type!");
Type = R;
}
const Record *getType() const { return Type; }
std::string describe() const {
if (!hasImmValue())
return "MachineOperand $" + getOperandName().str() + "";
std::string Str = "imm " + to_string(getImmValue());
if (isNamedImmediate())
Str += ":$" + getOperandName().str() + "";
return Str;
}
void print(raw_ostream &OS) const {
if (isDef())
OS << "<def>";
bool NeedsColon = true;
if (const Record *Ty = getType()) {
if (hasImmValue())
OS << "(" << Ty->getName() << " " << getImmValue() << ")";
else
OS << Ty->getName();
} else if (hasImmValue())
OS << getImmValue();
else
NeedsColon = false;
if (isNamedOperand())
OS << (NeedsColon ? ":" : "") << "$" << getOperandName();
}
void dump() const { return print(dbgs()); }
private:
std::optional<int64_t> Value;
StringRef Name;
const Record *Type = nullptr;
bool Def = false;
};
/// Base class for CodeGenInstructionPattern & PatFragPattern, which handles all
/// the boilerplate for patterns that have a list of operands for some (pseudo)
/// instruction.
class InstructionPattern : public Pattern {
public:
virtual ~InstructionPattern() = default;
static bool classof(const Pattern *P) {
return P->getKind() == K_CodeGenInstruction || P->getKind() == K_PatFrag ||
P->getKind() == K_Builtin;
}
template <typename... Ty> void addOperand(Ty &&...Init) {
Operands.emplace_back(std::forward<Ty>(Init)...);
}
auto &operands() { return Operands; }
const auto &operands() const { return Operands; }
unsigned operands_size() const { return Operands.size(); }
InstructionOperand &getOperand(unsigned K) { return Operands[K]; }
const InstructionOperand &getOperand(unsigned K) const { return Operands[K]; }
/// When this InstructionPattern is used as the match root, returns the
/// operands that must be redefined in the 'apply' pattern for the rule to be
/// valid.
///
/// For most patterns, this just returns the defs.
/// For PatFrag this only returns the root of the PF.
///
/// Returns an empty array on error.
virtual ArrayRef<InstructionOperand> getApplyDefsNeeded() const {
return {operands().begin(), getNumInstDefs()};
}
auto named_operands() {
return make_filter_range(Operands,
[&](auto &O) { return O.isNamedOperand(); });
}
auto named_operands() const {
return make_filter_range(Operands,
[&](auto &O) { return O.isNamedOperand(); });
}
virtual bool isVariadic() const { return false; }
virtual unsigned getNumInstOperands() const = 0;
virtual unsigned getNumInstDefs() const = 0;
bool hasAllDefs() const { return operands_size() >= getNumInstDefs(); }
virtual StringRef getInstName() const = 0;
void reportUnreachable(ArrayRef<SMLoc> Locs) const;
virtual bool checkSemantics(ArrayRef<SMLoc> Loc);
void print(raw_ostream &OS, bool PrintName = true) const override;
protected:
InstructionPattern(unsigned K, StringRef Name) : Pattern(K, Name) {}
SmallVector<InstructionOperand, 4> Operands;
};
void InstructionPattern::reportUnreachable(ArrayRef<SMLoc> Locs) const {
PrintError(Locs, "pattern '" + getName() + "' ('" + getInstName() +
"') is unreachable from the pattern root!");
}
bool InstructionPattern::checkSemantics(ArrayRef<SMLoc> Loc) {
unsigned NumExpectedOperands = getNumInstOperands();
if (isVariadic()) {
if (Operands.size() < NumExpectedOperands) {
PrintError(Loc, +"'" + getInstName() + "' expected at least " +
Twine(NumExpectedOperands) + " operands, got " +
Twine(Operands.size()));
return false;
}
} else if (NumExpectedOperands != Operands.size()) {
PrintError(Loc, +"'" + getInstName() + "' expected " +
Twine(NumExpectedOperands) + " operands, got " +
Twine(Operands.size()));
return false;
}
unsigned OpIdx = 0;
unsigned NumDefs = getNumInstDefs();
for (auto &Op : Operands)
Op.setIsDef(OpIdx++ < NumDefs);
return true;
}
void InstructionPattern::print(raw_ostream &OS, bool PrintName) const {
printImpl(OS, PrintName, [&OS, this] {
OS << getInstName() << " operands:[";
StringRef Sep = "";
for (const auto &Op : Operands) {
OS << Sep;
Op.print(OS);
Sep = ", ";
}
OS << "]";
});
}
//===- OperandTable -------------------------------------------------------===//
/// Maps InstructionPattern operands to their definitions. This allows us to tie
/// different patterns of a (apply), (match) or (patterns) set of patterns
/// together.
template <typename DefTy = InstructionPattern> class OperandTable {
public:
static_assert(std::is_base_of_v<InstructionPattern, DefTy>,
"DefTy should be a derived class from InstructionPattern");
bool addPattern(DefTy *P, function_ref<void(StringRef)> DiagnoseRedef) {
for (const auto &Op : P->named_operands()) {
StringRef OpName = Op.getOperandName();
// We always create an entry in the OperandTable, even for uses.
// Uses of operands that don't have a def (= live-ins) will remain with a
// nullptr as the Def.
//
// This allows us tell whether an operand exists in a pattern or not. If
// there is no entry for it, it doesn't exist, if there is an entry, it's
// used/def'd at least once.
auto &Def = Table[OpName];
if (!Op.isDef())
continue;
if (Def) {
DiagnoseRedef(OpName);
return false;
}
Def = P;
}
return true;
}
struct LookupResult {
LookupResult() = default;
LookupResult(DefTy *Def) : Found(true), Def(Def) {}
bool Found = false;
DefTy *Def = nullptr;
bool isLiveIn() const { return Found && !Def; }
};
LookupResult lookup(StringRef OpName) const {
if (auto It = Table.find(OpName); It != Table.end())
return LookupResult(It->second);
return LookupResult();
}
DefTy *getDef(StringRef OpName) const { return lookup(OpName).Def; }
void print(raw_ostream &OS, StringRef Name = "",
StringRef Indent = "") const {
OS << Indent << "(OperandTable ";
if (!Name.empty())
OS << Name << " ";
if (Table.empty()) {
OS << "<empty>)\n";
return;
}
SmallVector<StringRef, 0> Keys(Table.keys());
sort(Keys);
OS << "\n";
for (const auto &Key : Keys) {
const auto *Def = Table.at(Key);
OS << Indent << " " << Key << " -> "
<< (Def ? Def->getName() : "<live-in>") << "\n";
}
OS << Indent << ")\n";
}
auto begin() const { return Table.begin(); }
auto end() const { return Table.end(); }
void dump() const { print(dbgs()); }
private:
StringMap<DefTy *> Table;
};
//===- CodeGenInstructionPattern ------------------------------------------===//
/// Matches an instruction, e.g. `G_ADD $x, $y, $z`.
class CodeGenInstructionPattern : public InstructionPattern {
public:
CodeGenInstructionPattern(const CodeGenInstruction &I, StringRef Name)
: InstructionPattern(K_CodeGenInstruction, Name), I(I) {}
static bool classof(const Pattern *P) {
return P->getKind() == K_CodeGenInstruction;
}
bool is(StringRef OpcodeName) const {
return I.TheDef->getName() == OpcodeName;
}
bool hasVariadicDefs() const;
bool isVariadic() const override { return I.Operands.isVariadic; }
unsigned getNumInstDefs() const override;
unsigned getNumInstOperands() const override;
const CodeGenInstruction &getInst() const { return I; }
StringRef getInstName() const override { return I.TheDef->getName(); }
private:
const CodeGenInstruction &I;
};
bool CodeGenInstructionPattern::hasVariadicDefs() const {
// Note: we cannot use variadicOpsAreDefs, it's not set for
// GenericInstructions.
if (!isVariadic())
return false;
if (I.variadicOpsAreDefs)
return true;
DagInit *OutOps = I.TheDef->getValueAsDag("OutOperandList");
if (OutOps->arg_empty())
return false;
auto *LastArgTy = dyn_cast<DefInit>(OutOps->getArg(OutOps->arg_size() - 1));
return LastArgTy && LastArgTy->getDef()->getName() == "variable_ops";
}
unsigned CodeGenInstructionPattern::getNumInstDefs() const {
if (!isVariadic() || !hasVariadicDefs())
return I.Operands.NumDefs;
unsigned NumOuts = I.Operands.size() - I.Operands.NumDefs;
assert(Operands.size() > NumOuts);
return std::max<unsigned>(I.Operands.NumDefs, Operands.size() - NumOuts);
}
unsigned CodeGenInstructionPattern::getNumInstOperands() const {
unsigned NumCGIOps = I.Operands.size();
return isVariadic() ? std::max<unsigned>(NumCGIOps, Operands.size())
: NumCGIOps;
}
//===- OperandTypeChecker -------------------------------------------------===//
/// This is a trivial type checker for all operands in a set of
/// InstructionPatterns.
///
/// It infers the type of each operand, check it's consistent with the known
/// type of the operand, and then sets all of the types in all operands in
/// setAllOperandTypes.
class OperandTypeChecker {
public:
OperandTypeChecker(ArrayRef<SMLoc> DiagLoc) : DiagLoc(DiagLoc) {}
bool check(InstructionPattern *P);
void setAllOperandTypes();
private:
struct OpTypeInfo {
const Record *Type = nullptr;
InstructionPattern *TypeSrc = nullptr;
};
ArrayRef<SMLoc> DiagLoc;
StringMap<OpTypeInfo> Types;
SmallVector<InstructionPattern *, 16> Pats;
};
bool OperandTypeChecker::check(InstructionPattern *P) {
Pats.push_back(P);
for (auto &Op : P->named_operands()) {
const Record *Ty = Op.getType();
if (!Ty)
continue;
auto &Info = Types[Op.getOperandName()];
if (!Info.Type) {
Info.Type = Ty;
Info.TypeSrc = P;
continue;
}
if (Info.Type != Ty) {
PrintError(DiagLoc, "conflicting types for operand '" +
Op.getOperandName() + "': first seen with '" +
Info.Type->getName() + "' in '" +
Info.TypeSrc->getName() + ", now seen with '" +
Ty->getName() + "' in '" + P->getName() + "'");
return false;
}
}
return true;
}
void OperandTypeChecker::setAllOperandTypes() {
for (auto *Pat : Pats) {
for (auto &Op : Pat->named_operands()) {
if (auto &Info = Types[Op.getOperandName()]; Info.Type)
Op.setType(Info.Type);
}
}
}
//===- PatFrag ------------------------------------------------------------===//
/// Represents a parsed GICombinePatFrag. This can be thought of as the
/// equivalent of a CodeGenInstruction, but for PatFragPatterns.
///
/// PatFrags are made of 3 things:
/// - Out parameters (defs)
/// - In parameters
/// - A set of pattern lists (alternatives).
///
/// If the PatFrag uses instruction patterns, the root must be one of the defs.
///
/// Note that this DOES NOT represent the use of the PatFrag, only its
/// definition. The use of the PatFrag in a Pattern is represented by
/// PatFragPattern.
///
/// PatFrags use the term "parameter" instead of operand because they're
/// essentially macros, and using that name avoids confusion. Other than that,
/// they're structured similarly to a MachineInstruction - all parameters
/// (operands) are in the same list, with defs at the start. This helps mapping
/// parameters to values, because, param N of a PatFrag is always operand N of a
/// PatFragPattern.
class PatFrag {
public:
enum ParamKind {
PK_Root,
PK_MachineOperand,
PK_Imm,
};
struct Param {
StringRef Name;
ParamKind Kind;
};
using ParamVec = SmallVector<Param, 4>;
using ParamIt = ParamVec::const_iterator;
/// Represents an alternative of the PatFrag. When parsing a GICombinePatFrag,
/// this is created from its "Alternatives" list. Each alternative is a list
/// of patterns written wrapped in a `(pattern ...)` dag init.
///
/// Each argument to the `pattern` DAG operator is parsed into a Pattern
/// instance.
struct Alternative {
OperandTable<> OpTable;
SmallVector<std::unique_ptr<Pattern>, 4> Pats;
};
explicit PatFrag(const Record &Def) : Def(Def) {
assert(Def.isSubClassOf(PatFragClassName));
}
static StringRef getParamKindStr(ParamKind OK);
StringRef getName() const { return Def.getName(); }
const Record &getDef() const { return Def; }
ArrayRef<SMLoc> getLoc() const { return Def.getLoc(); }
Alternative &addAlternative() { return Alts.emplace_back(); }
const Alternative &getAlternative(unsigned K) const { return Alts[K]; }
unsigned num_alternatives() const { return Alts.size(); }
void addInParam(StringRef Name, ParamKind Kind);
iterator_range<ParamIt> in_params() const;
unsigned num_in_params() const { return Params.size() - NumOutParams; }
void addOutParam(StringRef Name, ParamKind Kind);
iterator_range<ParamIt> out_params() const;
unsigned num_out_params() const { return NumOutParams; }
unsigned num_roots() const;
unsigned num_params() const { return num_in_params() + num_out_params(); }
/// Finds the operand \p Name and returns its index or -1 if not found.
/// Remember that all params are part of the same list, with out params at the
/// start. This means that the index returned can be used to access operands
/// of InstructionPatterns.
unsigned getParamIdx(StringRef Name) const;
const Param &getParam(unsigned K) const { return Params[K]; }
bool canBeMatchRoot() const { return num_roots() == 1; }
void print(raw_ostream &OS, StringRef Indent = "") const;
void dump() const { print(dbgs()); }
/// Checks if the in-param \p ParamName can be unbound or not.
/// \p ArgName is the name of the argument passed to the PatFrag.
///
/// An argument can be unbound only if, for all alternatives:
/// - There is no CXX pattern, OR:
/// - There is an InstructionPattern that binds the parameter.
///
/// e.g. in (MyPatFrag $foo), if $foo has never been seen before (= it's
/// unbound), this checks if MyPatFrag supports it or not.
bool handleUnboundInParam(StringRef ParamName, StringRef ArgName,
ArrayRef<SMLoc> DiagLoc) const;
bool checkSemantics();
bool buildOperandsTables();
private:
static void printParamsList(raw_ostream &OS, iterator_range<ParamIt> Params);
void PrintError(Twine Msg) const { ::PrintError(&Def, Msg); }
const Record &Def;
unsigned NumOutParams = 0;
ParamVec Params;
SmallVector<Alternative, 2> Alts;
};
StringRef PatFrag::getParamKindStr(ParamKind OK) {
switch (OK) {
case PK_Root:
return "root";
case PK_MachineOperand:
return "machine_operand";
case PK_Imm:
return "imm";
}
llvm_unreachable("Unknown operand kind!");
}
void PatFrag::addInParam(StringRef Name, ParamKind Kind) {
Params.emplace_back(Param{insertStrRef(Name), Kind});
}
iterator_range<PatFrag::ParamIt> PatFrag::in_params() const {
return {Params.begin() + NumOutParams, Params.end()};
}
void PatFrag::addOutParam(StringRef Name, ParamKind Kind) {
assert(NumOutParams == Params.size() &&
"Adding out-param after an in-param!");
Params.emplace_back(Param{insertStrRef(Name), Kind});
++NumOutParams;
}
iterator_range<PatFrag::ParamIt> PatFrag::out_params() const {
return {Params.begin(), Params.begin() + NumOutParams};
}
unsigned PatFrag::num_roots() const {
return count_if(out_params(),
[&](const auto &P) { return P.Kind == PK_Root; });
}
unsigned PatFrag::getParamIdx(StringRef Name) const {
for (const auto &[Idx, Op] : enumerate(Params)) {
if (Op.Name == Name)
return Idx;
}
return -1;
}
bool PatFrag::checkSemantics() {
for (const auto &Alt : Alts) {
for (const auto &Pat : Alt.Pats) {
switch (Pat->getKind()) {
case Pattern::K_AnyOpcode:
PrintError("wip_match_opcode cannot be used in " + PatFragClassName);
return false;
case Pattern::K_Builtin:
PrintError("Builtin instructions cannot be used in " +
PatFragClassName);
return false;
case Pattern::K_CXX:
case Pattern::K_CodeGenInstruction:
continue;
case Pattern::K_PatFrag:
// TODO: It's just that the emitter doesn't handle it but technically
// there is no reason why we can't. We just have to be careful with
// operand mappings, it could get complex.
PrintError("nested " + PatFragClassName + " are not supported");
return false;
}
}
}
StringSet<> SeenOps;
for (const auto &Op : in_params()) {
if (SeenOps.count(Op.Name)) {
PrintError("duplicate parameter '" + Op.Name + "'");
return false;
}
// Check this operand is NOT defined in any alternative's patterns.
for (const auto &Alt : Alts) {
if (Alt.OpTable.lookup(Op.Name).Def) {
PrintError("input parameter '" + Op.Name + "' cannot be redefined!");
return false;
}
}
if (Op.Kind == PK_Root) {
PrintError("input parameterr '" + Op.Name + "' cannot be a root!");
return false;
}
SeenOps.insert(Op.Name);
}
for (const auto &Op : out_params()) {
if (Op.Kind != PK_Root && Op.Kind != PK_MachineOperand) {
PrintError("output parameter '" + Op.Name +
"' must be 'root' or 'gi_mo'");
return false;
}
if (SeenOps.count(Op.Name)) {
PrintError("duplicate parameter '" + Op.Name + "'");
return false;
}
// Check this operand is defined in all alternative's patterns.
for (const auto &Alt : Alts) {
const auto *OpDef = Alt.OpTable.getDef(Op.Name);
if (!OpDef) {
PrintError("output parameter '" + Op.Name +
"' must be defined by all alternative patterns in '" +
Def.getName() + "'");
return false;
}
if (Op.Kind == PK_Root && OpDef->getNumInstDefs() != 1) {
// The instruction that defines the root must have a single def.
// Otherwise we'd need to support multiple roots and it gets messy.
//
// e.g. this is not supported:
// (pattern (G_UNMERGE_VALUES $x, $root, $vec))
PrintError("all instructions that define root '" + Op.Name + "' in '" +
Def.getName() + "' can only have a single output operand");
return false;
}
}
SeenOps.insert(Op.Name);
}
if (num_out_params() != 0 && num_roots() == 0) {
PrintError(PatFragClassName + " must have one root in its 'out' operands");
return false;
}
if (num_roots() > 1) {
PrintError(PatFragClassName + " can only have one root");
return false;
}
// TODO: find unused params
// Now, typecheck all alternatives.
for (auto &Alt : Alts) {
OperandTypeChecker OTC(Def.getLoc());
for (auto &Pat : Alt.Pats) {
if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {
if (!OTC.check(IP))
return false;
}
}
OTC.setAllOperandTypes();
}
return true;
}
bool PatFrag::handleUnboundInParam(StringRef ParamName, StringRef ArgName,
ArrayRef<SMLoc> DiagLoc) const {
// The parameter must be a live-in of all alternatives for this to work.
// Otherwise, we risk having unbound parameters being used (= crashes).
//
// Examples:
//
// in (ins $y), (patterns (G_FNEG $dst, $y), "return matchFnegOp(${y})")
// even if $y is unbound, we'll lazily bind it when emitting the G_FNEG.
//
// in (ins $y), (patterns "return matchFnegOp(${y})")
// if $y is unbound when this fragment is emitted, C++ code expansion will
// fail.
for (const auto &Alt : Alts) {
auto &OT = Alt.OpTable;
if (!OT.lookup(ParamName).Found) {
::PrintError(DiagLoc, "operand '" + ArgName + "' (for parameter '" +
ParamName + "' of '" + getName() +
"') cannot be unbound");
PrintNote(
DiagLoc,
"one or more alternatives of '" + getName() + "' do not bind '" +
ParamName +
"' to an instruction operand; either use a bound operand or "
"ensure '" +
Def.getName() + "' binds '" + ParamName +
"' in all alternatives");
return false;
}
}
return true;
}
bool PatFrag::buildOperandsTables() {
// enumerate(...) doesn't seem to allow lvalues so we need to count the old
// way.
unsigned Idx = 0;
const auto DiagnoseRedef = [this, &Idx](StringRef OpName) {
PrintError("Operand '" + OpName +
"' is defined multiple times in patterns of alternative #" +
to_string(Idx));
};
for (auto &Alt : Alts) {
for (auto &Pat : Alt.Pats) {
auto *IP = dyn_cast<InstructionPattern>(Pat.get());
if (!IP)
continue;
if (!Alt.OpTable.addPattern(IP, DiagnoseRedef))
return false;
}
++Idx;
}
return true;
}
void PatFrag::print(raw_ostream &OS, StringRef Indent) const {
OS << Indent << "(PatFrag name:" << getName() << "\n";
if (!in_params().empty()) {
OS << Indent << " (ins ";
printParamsList(OS, in_params());
OS << ")\n";
}
if (!out_params().empty()) {
OS << Indent << " (outs ";
printParamsList(OS, out_params());
OS << ")\n";
}
// TODO: Dump OperandTable as well.
OS << Indent << " (alternatives [\n";
for (const auto &Alt : Alts) {
OS << Indent << " [\n";
for (const auto &Pat : Alt.Pats) {
OS << Indent << " ";
Pat->print(OS, /*PrintName=*/true);
OS << ",\n";
}
OS << Indent << " ],\n";
}
OS << Indent << " ])\n";
OS << Indent << ')';
}
void PatFrag::printParamsList(raw_ostream &OS, iterator_range<ParamIt> Params) {
OS << '['
<< join(map_range(Params,
[](auto &O) {
return (O.Name + ":" + getParamKindStr(O.Kind)).str();
}),
", ")
<< ']';
}
//===- PatFragPattern -----------------------------------------------------===//
class PatFragPattern : public InstructionPattern {
public:
PatFragPattern(const PatFrag &PF, StringRef Name)
: InstructionPattern(K_PatFrag, Name), PF(PF) {}
static bool classof(const Pattern *P) { return P->getKind() == K_PatFrag; }
const PatFrag &getPatFrag() const { return PF; }
StringRef getInstName() const override { return PF.getName(); }
unsigned getNumInstDefs() const override { return PF.num_out_params(); }
unsigned getNumInstOperands() const override { return PF.num_params(); }
ArrayRef<InstructionOperand> getApplyDefsNeeded() const override;
bool checkSemantics(ArrayRef<SMLoc> DiagLoc) override;
/// Before emitting the patterns inside the PatFrag, add all necessary code
/// expansions to \p PatFragCEs imported from \p ParentCEs.
///
/// For a MachineOperand PatFrag parameter, this will fetch the expansion for
/// that operand from \p ParentCEs and add it to \p PatFragCEs. Errors can be
/// emitted if the MachineOperand reference is unbound.
///
/// For an Immediate PatFrag parameter this simply adds the integer value to
/// \p PatFragCEs as an expansion.
///
/// \param ParentCEs Contains all of the code expansions declared by the other
/// patterns emitted so far in the pattern list containing
/// this PatFragPattern.
/// \param PatFragCEs Output Code Expansions (usually empty)
/// \param DiagLoc Diagnostic loc in case an error occurs.
/// \return `true` on success, `false` on failure.
bool mapInputCodeExpansions(const CodeExpansions &ParentCEs,
CodeExpansions &PatFragCEs,
ArrayRef<SMLoc> DiagLoc) const;
private:
const PatFrag &PF;
};
ArrayRef<InstructionOperand> PatFragPattern::getApplyDefsNeeded() const {
assert(PF.num_roots() == 1);
// Only roots need to be redef.
for (auto [Idx, Param] : enumerate(PF.out_params())) {
if (Param.Kind == PatFrag::PK_Root)
return getOperand(Idx);
}
llvm_unreachable("root not found!");
}
bool PatFragPattern::checkSemantics(ArrayRef<SMLoc> DiagLoc) {
if (!InstructionPattern::checkSemantics(DiagLoc))
return false;
for (const auto &[Idx, Op] : enumerate(Operands)) {
switch (PF.getParam(Idx).Kind) {
case PatFrag::PK_Imm:
if (!Op.hasImmValue()) {
PrintError(DiagLoc, "expected operand " + to_string(Idx) + " of '" +
getInstName() + "' to be an immediate; got " +
Op.describe());
return false;
}
if (Op.isNamedImmediate()) {
PrintError(DiagLoc, "operand " + to_string(Idx) + " of '" +
getInstName() +
"' cannot be a named immediate");
return false;
}
break;
case PatFrag::PK_Root:
case PatFrag::PK_MachineOperand:
if (!Op.isNamedOperand() || Op.isNamedImmediate()) {
PrintError(DiagLoc, "expected operand " + to_string(Idx) + " of '" +
getInstName() +
"' to be a MachineOperand; got " +
Op.describe());
return false;
}
break;
}
}
return true;
}
bool PatFragPattern::mapInputCodeExpansions(const CodeExpansions &ParentCEs,
CodeExpansions &PatFragCEs,
ArrayRef<SMLoc> DiagLoc) const {
for (const auto &[Idx, Op] : enumerate(operands())) {
StringRef ParamName = PF.getParam(Idx).Name;
// Operands to a PFP can only be named, or be an immediate, but not a named
// immediate.
assert(!Op.isNamedImmediate());
if (Op.isNamedOperand()) {
StringRef ArgName = Op.getOperandName();
// Map it only if it's been defined.
auto It = ParentCEs.find(ArgName);
if (It == ParentCEs.end()) {
if (!PF.handleUnboundInParam(ParamName, ArgName, DiagLoc))
return false;
} else
PatFragCEs.declare(ParamName, It->second);
continue;
}
if (Op.hasImmValue()) {
PatFragCEs.declare(ParamName, to_string(Op.getImmValue()));
continue;
}
llvm_unreachable("Unknown Operand Type!");
}
return true;
}
//===- BuiltinPattern -----------------------------------------------------===//
enum BuiltinKind {
BI_ReplaceReg,
BI_EraseRoot,
};
class BuiltinPattern : public InstructionPattern {
struct BuiltinInfo {
StringLiteral DefName;
BuiltinKind Kind;
unsigned NumOps;
unsigned NumDefs;
};
static constexpr std::array<BuiltinInfo, 2> KnownBuiltins = {{
{"GIReplaceReg", BI_ReplaceReg, 2, 1},
{"GIEraseRoot", BI_EraseRoot, 0, 0},
}};
public:
BuiltinPattern(const Record &Def, StringRef Name)
: InstructionPattern(K_Builtin, Name), I(getBuiltinInfo(Def)) {}
static bool classof(const Pattern *P) { return P->getKind() == K_Builtin; }
unsigned getNumInstOperands() const override { return I.NumOps; }
unsigned getNumInstDefs() const override { return I.NumDefs; }
StringRef getInstName() const override { return I.DefName; }
BuiltinKind getBuiltinKind() const { return I.Kind; }
bool checkSemantics(ArrayRef<SMLoc> Loc) override;
private:
static BuiltinInfo getBuiltinInfo(const Record &Def);
BuiltinInfo I;
};
BuiltinPattern::BuiltinInfo BuiltinPattern::getBuiltinInfo(const Record &Def) {
assert(Def.isSubClassOf(BuiltinInstClassName));
StringRef Name = Def.getName();
for (const auto &KBI : KnownBuiltins) {
if (KBI.DefName == Name)
return KBI;
}
PrintFatalError(Def.getLoc(), "Unimplemented " + BuiltinInstClassName +
" def '" + Name + "'");
}
bool BuiltinPattern::checkSemantics(ArrayRef<SMLoc> Loc) {
if (!InstructionPattern::checkSemantics(Loc))
return false;
// For now all builtins just take names, no immediates.
for (const auto &[Idx, Op] : enumerate(operands())) {
if (!Op.isNamedOperand() || Op.isNamedImmediate()) {
PrintError(Loc, "expected operand " + to_string(Idx) + " of '" +
getInstName() + "' to be a name");
return false;
}
}
return true;
}
//===- PrettyStackTrace Helpers ------------------------------------------===//
class PrettyStackTraceParse : public PrettyStackTraceEntry {
const Record &Def;
public:
PrettyStackTraceParse(const Record &Def) : Def(Def) {}
void print(raw_ostream &OS) const override {
if (Def.isSubClassOf("GICombineRule"))
OS << "Parsing GICombineRule '" << Def.getName() << "'";
else if (Def.isSubClassOf(PatFragClassName))
OS << "Parsing " << PatFragClassName << " '" << Def.getName() << "'";
else
OS << "Parsing '" << Def.getName() << "'";
OS << "\n";
}
};
class PrettyStackTraceEmit : public PrettyStackTraceEntry {
const Record &Def;
const Pattern *Pat = nullptr;
public:
PrettyStackTraceEmit(const Record &Def, const Pattern *Pat = nullptr)
: Def(Def), Pat(Pat) {}
void print(raw_ostream &OS) const override {
if (Def.isSubClassOf("GICombineRule"))
OS << "Emitting GICombineRule '" << Def.getName() << "'";
else if (Def.isSubClassOf(PatFragClassName))
OS << "Emitting " << PatFragClassName << " '" << Def.getName() << "'";
else
OS << "Emitting '" << Def.getName() << "'";
if (Pat)
OS << " [" << Pat->getKindName() << " '" << Pat->getName() << "']";
OS << "\n";
}
};
//===- CombineRuleBuilder -------------------------------------------------===//
/// Parses combine rule and builds a small intermediate representation to tie
/// patterns together and emit RuleMatchers to match them. This may emit more
/// than one RuleMatcher, e.g. for `wip_match_opcode`.
///
/// Memory management for `Pattern` objects is done through `std::unique_ptr`.
/// In most cases, there are two stages to a pattern's lifetime:
/// - Creation in a `parse` function
/// - The unique_ptr is stored in a variable, and may be destroyed if the
/// pattern is found to be semantically invalid.
/// - Ownership transfer into a `PatternMap`
/// - Once a pattern is moved into either the map of Match or Apply
/// patterns, it is known to be valid and it never moves back.
class CombineRuleBuilder {
public:
using PatternMap = MapVector<StringRef, std::unique_ptr<Pattern>>;
using PatternAlternatives = DenseMap<const Pattern *, unsigned>;
CombineRuleBuilder(const CodeGenTarget &CGT,
SubtargetFeatureInfoMap &SubtargetFeatures,
Record &RuleDef, unsigned ID,
std::vector<RuleMatcher> &OutRMs)
: CGT(CGT), SubtargetFeatures(SubtargetFeatures), RuleDef(RuleDef),
RuleID(ID), OutRMs(OutRMs) {}
/// Parses all fields in the RuleDef record.
bool parseAll();
/// Emits all RuleMatchers into the vector of RuleMatchers passed in the
/// constructor.
bool emitRuleMatchers();
void print(raw_ostream &OS) const;
void dump() const { print(dbgs()); }
/// Debug-only verification of invariants.
#ifndef NDEBUG
void verify() const;
#endif
private:
const CodeGenInstruction &getGConstant() const {
return CGT.getInstruction(RuleDef.getRecords().getDef("G_CONSTANT"));
}
void PrintError(Twine Msg) const { ::PrintError(&RuleDef, Msg); }
void PrintWarning(Twine Msg) const { ::PrintWarning(RuleDef.getLoc(), Msg); }
void PrintNote(Twine Msg) const { ::PrintNote(RuleDef.getLoc(), Msg); }
void print(raw_ostream &OS, const PatternAlternatives &Alts) const;
bool addApplyPattern(std::unique_ptr<Pattern> Pat);
bool addMatchPattern(std::unique_ptr<Pattern> Pat);
/// Adds the expansions from \see MatchDatas to \p CE.
void declareAllMatchDatasExpansions(CodeExpansions &CE) const;
/// Adds a matcher \p P to \p IM, expanding its code using \p CE.
/// Note that the predicate is added on the last InstructionMatcher.
///
/// \p Alts is only used if DebugCXXPreds is enabled.
void addCXXPredicate(RuleMatcher &M, const CodeExpansions &CE,
const CXXPattern &P, const PatternAlternatives &Alts);
/// Adds an apply \p P to \p IM, expanding its code using \p CE.
void addCXXAction(RuleMatcher &M, const CodeExpansions &CE,
const CXXPattern &P);
bool hasOnlyCXXApplyPatterns() const;
bool hasEraseRoot() const;
// Infer machine operand types and check their consistency.
bool typecheckPatterns();
/// For all PatFragPatterns, add a new entry in PatternAlternatives for each
/// PatternList it contains. This is multiplicative, so if we have 2
/// PatFrags with 3 alternatives each, we get 2*3 permutations added to
/// PermutationsToEmit. The "MaxPermutations" field controls how many
/// permutations are allowed before an error is emitted and this function
/// returns false. This is a simple safeguard to prevent combination of
/// PatFrags from generating enormous amounts of rules.
bool buildPermutationsToEmit();
/// Checks additional semantics of the Patterns.
bool checkSemantics();
/// Creates a new RuleMatcher with some boilerplate
/// settings/actions/predicates, and and adds it to \p OutRMs.
/// \see addFeaturePredicates too.
///
/// \param Alts Current set of alternatives, for debug comment.
/// \param AdditionalComment Comment string to be added to the
/// `DebugCommentAction`.
RuleMatcher &addRuleMatcher(const PatternAlternatives &Alts,
Twine AdditionalComment = "");
bool addFeaturePredicates(RuleMatcher &M);
bool findRoots();
bool buildRuleOperandsTable();
bool parseDefs(const DagInit &Def);
bool
parsePatternList(const DagInit &List,
function_ref<bool(std::unique_ptr<Pattern>)> ParseAction,
StringRef Operator, ArrayRef<SMLoc> DiagLoc,
StringRef AnonPatNamePrefix) const;
std::unique_ptr<Pattern> parseInstructionPattern(const Init &Arg,
StringRef PatName) const;
std::unique_ptr<Pattern> parseWipMatchOpcodeMatcher(const Init &Arg,
StringRef PatName) const;
bool parseInstructionPatternOperand(InstructionPattern &IP,
const Init *OpInit,
const StringInit *OpName) const;
std::unique_ptr<PatFrag> parsePatFragImpl(const Record *Def) const;
bool parsePatFragParamList(
ArrayRef<SMLoc> DiagLoc, const DagInit &OpsList,
function_ref<bool(StringRef, PatFrag::ParamKind)> ParseAction) const;
const PatFrag *parsePatFrag(const Record *Def) const;
bool emitMatchPattern(CodeExpansions &CE, const PatternAlternatives &Alts,
const InstructionPattern &IP);
bool emitMatchPattern(CodeExpansions &CE, const PatternAlternatives &Alts,
const AnyOpcodePattern &AOP);
bool emitPatFragMatchPattern(CodeExpansions &CE,
const PatternAlternatives &Alts, RuleMatcher &RM,
InstructionMatcher *IM,
const PatFragPattern &PFP,
DenseSet<const Pattern *> &SeenPats);
bool emitApplyPatterns(CodeExpansions &CE, RuleMatcher &M);
// Recursively visits InstructionPatterns from P to build up the
// RuleMatcher actions.
bool emitInstructionApplyPattern(CodeExpansions &CE, RuleMatcher &M,
const InstructionPattern &P,
DenseSet<const Pattern *> &SeenPats,
StringMap<unsigned> &OperandToTempRegID);
bool emitCodeGenInstructionApplyImmOperand(RuleMatcher &M,
BuildMIAction &DstMI,
const CodeGenInstructionPattern &P,
const InstructionOperand &O);
bool emitBuiltinApplyPattern(CodeExpansions &CE, RuleMatcher &M,
const BuiltinPattern &P,
StringMap<unsigned> &OperandToTempRegID);
// Recursively visits CodeGenInstructionPattern from P to build up the
// RuleMatcher/InstructionMatcher. May create new InstructionMatchers as
// needed.
using OperandMapperFnRef =
function_ref<InstructionOperand(const InstructionOperand &)>;
using OperandDefLookupFn =
function_ref<const InstructionPattern *(StringRef)>;
bool emitCodeGenInstructionMatchPattern(
CodeExpansions &CE, const PatternAlternatives &Alts, RuleMatcher &M,
InstructionMatcher &IM, const CodeGenInstructionPattern &P,
DenseSet<const Pattern *> &SeenPats, OperandDefLookupFn LookupOperandDef,
OperandMapperFnRef OperandMapper = [](const auto &O) { return O; });
const CodeGenTarget &CGT;
SubtargetFeatureInfoMap &SubtargetFeatures;
Record &RuleDef;
const unsigned RuleID;
std::vector<RuleMatcher> &OutRMs;
// For InstructionMatcher::addOperand
unsigned AllocatedTemporariesBaseID = 0;
/// The root of the pattern.
StringRef RootName;
/// These maps have ownership of the actual Pattern objects.
/// They both map a Pattern's name to the Pattern instance.
PatternMap MatchPats;
PatternMap ApplyPats;
/// Operand tables to tie match/apply patterns together.
OperandTable<> MatchOpTable;
OperandTable<> ApplyOpTable;
/// Set by findRoots.
Pattern *MatchRoot = nullptr;
SmallDenseSet<InstructionPattern *, 2> ApplyRoots;
SmallVector<MatchDataInfo, 2> MatchDatas;
SmallVector<PatternAlternatives, 1> PermutationsToEmit;
// print()/debug-only members.
mutable SmallPtrSet<const PatFrag *, 2> SeenPatFrags;
};
bool CombineRuleBuilder::parseAll() {
auto StackTrace = PrettyStackTraceParse(RuleDef);
if (!parseDefs(*RuleDef.getValueAsDag("Defs")))
return false;
if (!parsePatternList(
*RuleDef.getValueAsDag("Match"),
[this](auto Pat) { return addMatchPattern(std::move(Pat)); }, "match",
RuleDef.getLoc(), (RuleDef.getName() + "_match").str()))
return false;
if (!parsePatternList(
*RuleDef.getValueAsDag("Apply"),
[this](auto Pat) { return addApplyPattern(std::move(Pat)); }, "apply",
RuleDef.getLoc(), (RuleDef.getName() + "_apply").str()))
return false;
if (!buildRuleOperandsTable() || !typecheckPatterns() || !findRoots() ||
!checkSemantics() || !buildPermutationsToEmit())
return false;
LLVM_DEBUG(verify());
return true;
}
bool CombineRuleBuilder::emitRuleMatchers() {
auto StackTrace = PrettyStackTraceEmit(RuleDef);
assert(MatchRoot);
CodeExpansions CE;
declareAllMatchDatasExpansions(CE);
assert(!PermutationsToEmit.empty());
for (const auto &Alts : PermutationsToEmit) {
switch (MatchRoot->getKind()) {
case Pattern::K_AnyOpcode: {
if (!emitMatchPattern(CE, Alts, *cast<AnyOpcodePattern>(MatchRoot)))
return false;
break;
}
case Pattern::K_PatFrag:
case Pattern::K_Builtin:
case Pattern::K_CodeGenInstruction:
if (!emitMatchPattern(CE, Alts, *cast<InstructionPattern>(MatchRoot)))
return false;
break;
case Pattern::K_CXX:
PrintError("C++ code cannot be the root of a rule!");
return false;
default:
llvm_unreachable("unknown pattern kind!");
}
}
return true;
}
void CombineRuleBuilder::print(raw_ostream &OS) const {
OS << "(CombineRule name:" << RuleDef.getName() << " id:" << RuleID
<< " root:" << RootName << "\n";
if (!MatchDatas.empty()) {
OS << " (MatchDatas\n";
for (const auto &MD : MatchDatas) {
OS << " ";
MD.print(OS);
OS << "\n";
}
OS << " )\n";
}
if (!SeenPatFrags.empty()) {
OS << " (PatFrags\n";
for (const auto *PF : SeenPatFrags) {
PF->print(OS, /*Indent=*/" ");
OS << "\n";
}
OS << " )\n";
}
const auto DumpPats = [&](StringRef Name, const PatternMap &Pats) {
OS << " (" << Name << " ";
if (Pats.empty()) {
OS << "<empty>)\n";
return;
}
OS << "\n";
for (const auto &[Name, Pat] : Pats) {
OS << " ";
if (Pat.get() == MatchRoot)
OS << "<match_root>";
if (isa<InstructionPattern>(Pat.get()) &&
ApplyRoots.contains(cast<InstructionPattern>(Pat.get())))
OS << "<apply_root>";
OS << Name << ":";
Pat->print(OS, /*PrintName=*/false);
OS << "\n";
}
OS << " )\n";
};
DumpPats("MatchPats", MatchPats);
DumpPats("ApplyPats", ApplyPats);
MatchOpTable.print(OS, "MatchPats", /*Indent*/ " ");
ApplyOpTable.print(OS, "ApplyPats", /*Indent*/ " ");
if (PermutationsToEmit.size() > 1) {
OS << " (PermutationsToEmit\n";
for (const auto &Perm : PermutationsToEmit) {
OS << " ";
print(OS, Perm);
OS << ",\n";
}
OS << " )\n";
}
OS << ")\n";
}
#ifndef NDEBUG
void CombineRuleBuilder::verify() const {
const auto VerifyPats = [&](const PatternMap &Pats) {
for (const auto &[Name, Pat] : Pats) {
if (!Pat)
PrintFatalError("null pattern in pattern map!");
if (Name != Pat->getName()) {
Pat->dump();
PrintFatalError("Pattern name mismatch! Map name: " + Name +
", Pat name: " + Pat->getName());
}
// Sanity check: the map should point to the same data as the Pattern.
// Both strings are allocated in the pool using insertStrRef.
if (Name.data() != Pat->getName().data()) {
dbgs() << "Map StringRef: '" << Name << "' @ "
<< (const void *)Name.data() << "\n";
dbgs() << "Pat String: '" << Pat->getName() << "' @ "
<< (const void *)Pat->getName().data() << "\n";
PrintFatalError("StringRef stored in the PatternMap is not referencing "
"the same string as its Pattern!");
}
}
};
VerifyPats(MatchPats);
VerifyPats(ApplyPats);
// Check there are no wip_match_opcode patterns in the "apply" patterns.
if (any_of(ApplyPats,
[&](auto &E) { return isa<AnyOpcodePattern>(E.second.get()); })) {
dump();
PrintFatalError(
"illegal wip_match_opcode pattern in the 'apply' patterns!");
}
// Check there are no nullptrs in ApplyRoots.
if (ApplyRoots.contains(nullptr)) {
PrintFatalError(
"CombineRuleBuilder's ApplyRoots set contains a null pointer!");
}
}
#endif
void CombineRuleBuilder::print(raw_ostream &OS,
const PatternAlternatives &Alts) const {
SmallVector<std::string, 1> Strings(
map_range(Alts, [](const auto &PatAndPerm) {
return PatAndPerm.first->getName().str() + "[" +
to_string(PatAndPerm.second) + "]";
}));
// Sort so output is deterministic for tests. Otherwise it's sorted by pointer
// values.
sort(Strings);
OS << "[" << join(Strings, ", ") << "]";
}
bool CombineRuleBuilder::addApplyPattern(std::unique_ptr<Pattern> Pat) {
StringRef Name = Pat->getName();
if (ApplyPats.contains(Name)) {
PrintError("'" + Name + "' apply pattern defined more than once!");
return false;
}
if (isa<AnyOpcodePattern>(Pat.get())) {
PrintError("'" + Name +
"': wip_match_opcode is not supported in apply patterns");
return false;
}
if (isa<PatFragPattern>(Pat.get())) {
PrintError("'" + Name + "': using " + PatFragClassName +
" is not supported in apply patterns");
return false;
}
if (auto *CXXPat = dyn_cast<CXXPattern>(Pat.get()))
CXXPat->setIsApply();
ApplyPats[Name] = std::move(Pat);
return true;
}
bool CombineRuleBuilder::addMatchPattern(std::unique_ptr<Pattern> Pat) {
StringRef Name = Pat->getName();
if (MatchPats.contains(Name)) {
PrintError("'" + Name + "' match pattern defined more than once!");
return false;
}
// For now, none of the builtins can appear in 'match'.
if (const auto *BP = dyn_cast<BuiltinPattern>(Pat.get())) {
PrintError("'" + BP->getInstName() +
"' cannot be used in a 'match' pattern");
return false;
}
MatchPats[Name] = std::move(Pat);
return true;
}
void CombineRuleBuilder::declareAllMatchDatasExpansions(
CodeExpansions &CE) const {
for (const auto &MD : MatchDatas)
CE.declare(MD.getPatternSymbol(), MD.getQualifiedVariableName());
}
void CombineRuleBuilder::addCXXPredicate(RuleMatcher &M,
const CodeExpansions &CE,
const CXXPattern &P,
const PatternAlternatives &Alts) {
// FIXME: Hack so C++ code is executed last. May not work for more complex
// patterns.
auto &IM = *std::prev(M.insnmatchers().end());
const auto &ExpandedCode =
P.expandCode(CE, RuleDef.getLoc(), [&](raw_ostream &OS) {
OS << "// Pattern Alternatives: ";
print(OS, Alts);
OS << "\n";
});
IM->addPredicate<GenericInstructionPredicateMatcher>(
ExpandedCode.getEnumNameWithPrefix(CXXPredPrefix));
}
void CombineRuleBuilder::addCXXAction(RuleMatcher &M, const CodeExpansions &CE,
const CXXPattern &P) {
const auto &ExpandedCode = P.expandCode(CE, RuleDef.getLoc());
M.addAction<CustomCXXAction>(
ExpandedCode.getEnumNameWithPrefix(CXXApplyPrefix));
}
bool CombineRuleBuilder::hasOnlyCXXApplyPatterns() const {
return all_of(ApplyPats, [&](auto &Entry) {
return isa<CXXPattern>(Entry.second.get());
});
}
bool CombineRuleBuilder::hasEraseRoot() const {
return any_of(ApplyPats, [&](auto &Entry) {
if (const auto *BP = dyn_cast<BuiltinPattern>(Entry.second.get()))
return BP->getBuiltinKind() == BI_EraseRoot;
return false;
});
}
bool CombineRuleBuilder::typecheckPatterns() {
OperandTypeChecker OTC(RuleDef.getLoc());
for (auto &Pat : values(MatchPats)) {
if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {
if (!OTC.check(IP))
return false;
}
}
for (auto &Pat : values(ApplyPats)) {
if (auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {
if (!OTC.check(IP))
return false;
}
}
OTC.setAllOperandTypes();
return true;
}
bool CombineRuleBuilder::buildPermutationsToEmit() {
PermutationsToEmit.clear();
// Start with one empty set of alternatives.
PermutationsToEmit.emplace_back();
for (const auto &Pat : values(MatchPats)) {
unsigned NumAlts = 0;
// Note: technically, AnyOpcodePattern also needs permutations, but:
// - We only allow a single one of them in the root.
// - They cannot be mixed with any other pattern other than C++ code.
// So we don't really need to take them into account here. We could, but
// that pattern is a hack anyway and the less it's involved, the better.
if (const auto *PFP = dyn_cast<PatFragPattern>(Pat.get()))
NumAlts = PFP->getPatFrag().num_alternatives();
else
continue;
// For each pattern that needs permutations, multiply the current set of
// alternatives.
auto CurPerms = PermutationsToEmit;
PermutationsToEmit.clear();
for (const auto &Perm : CurPerms) {
assert(!Perm.count(Pat.get()) && "Pattern already emitted?");
for (unsigned K = 0; K < NumAlts; ++K) {
PatternAlternatives NewPerm = Perm;
NewPerm[Pat.get()] = K;
PermutationsToEmit.emplace_back(std::move(NewPerm));
}
}
}
if (int64_t MaxPerms = RuleDef.getValueAsInt("MaxPermutations");
MaxPerms > 0) {
if ((int64_t)PermutationsToEmit.size() > MaxPerms) {
PrintError("cannot emit rule '" + RuleDef.getName() + "'; " +
Twine(PermutationsToEmit.size()) +
" permutations would be emitted, but the max is " +
Twine(MaxPerms));
return false;
}
}
// Ensure we always have a single empty entry, it simplifies the emission
// logic so it doesn't need to handle the case where there are no perms.
if (PermutationsToEmit.empty()) {
PermutationsToEmit.emplace_back();
return true;
}
return true;
}
bool CombineRuleBuilder::checkSemantics() {
assert(MatchRoot && "Cannot call this before findRoots()");
bool UsesWipMatchOpcode = false;
for (const auto &Match : MatchPats) {
const auto *Pat = Match.second.get();
if (const auto *CXXPat = dyn_cast<CXXPattern>(Pat)) {
if (!CXXPat->getRawCode().contains("return "))
PrintWarning("'match' C++ code does not seem to return!");
continue;
}
const auto *AOP = dyn_cast<AnyOpcodePattern>(Pat);
if (!AOP)
continue;
if (UsesWipMatchOpcode) {
PrintError("wip_opcode_match can only be present once");
return false;
}
UsesWipMatchOpcode = true;
}
for (const auto &Apply : ApplyPats) {
assert(Apply.second.get());
const auto *IP = dyn_cast<InstructionPattern>(Apply.second.get());
if (!IP)
continue;
if (UsesWipMatchOpcode) {
PrintError("cannot use wip_match_opcode in combination with apply "
"instruction patterns!");
return false;
}
const auto *BIP = dyn_cast<BuiltinPattern>(IP);
if (!BIP)
continue;
StringRef Name = BIP->getInstName();
// (GIEraseInst) has to be the only apply pattern, or it can not be used at
// all. The root cannot have any defs either.
switch (BIP->getBuiltinKind()) {
case BI_EraseRoot: {
if (ApplyPats.size() > 1) {
PrintError(Name + " must be the only 'apply' pattern");
return false;
}
const auto *IRoot = dyn_cast<CodeGenInstructionPattern>(MatchRoot);
if (!IRoot) {
PrintError(Name +
" can only be used if the root is a CodeGenInstruction");
return false;
}
if (IRoot->getNumInstDefs() != 0) {
PrintError(Name + " can only be used if on roots that do "
"not have any output operand");
PrintNote("'" + IRoot->getInstName() + "' has " +
Twine(IRoot->getNumInstDefs()) + " output operands");
return false;
}
break;
}
case BI_ReplaceReg: {
// (GIReplaceReg can only be used on the root instruction)
// TODO: When we allow rewriting non-root instructions, also allow this.
StringRef OldRegName = BIP->getOperand(0).getOperandName();
auto *Def = MatchOpTable.getDef(OldRegName);
if (!Def) {
PrintError(Name + " cannot find a matched pattern that defines '" +
OldRegName + "'");
return false;
}
if (MatchOpTable.getDef(OldRegName) != MatchRoot) {
PrintError(Name + " cannot replace '" + OldRegName +
"': this builtin can only replace a register defined by the "
"match root");
return false;
}
break;
}
}
}
return true;
}
RuleMatcher &CombineRuleBuilder::addRuleMatcher(const PatternAlternatives &Alts,
Twine AdditionalComment) {
auto &RM = OutRMs.emplace_back(RuleDef.getLoc());
addFeaturePredicates(RM);
RM.setPermanentGISelFlags(GISF_IgnoreCopies);
RM.addRequiredSimplePredicate(getIsEnabledPredicateEnumName(RuleID));
std::string Comment;
raw_string_ostream CommentOS(Comment);
CommentOS << "Combiner Rule #" << RuleID << ": " << RuleDef.getName();
if (!Alts.empty()) {
CommentOS << " @ ";
print(CommentOS, Alts);
}
if (!AdditionalComment.isTriviallyEmpty())
CommentOS << "; " << AdditionalComment;
RM.addAction<DebugCommentAction>(Comment);
return RM;
}
bool CombineRuleBuilder::addFeaturePredicates(RuleMatcher &M) {
if (!RuleDef.getValue("Predicates"))
return true;
ListInit *Preds = RuleDef.getValueAsListInit("Predicates");
for (Init *PI : Preds->getValues()) {
DefInit *Pred = dyn_cast<DefInit>(PI);
if (!Pred)
continue;
Record *Def = Pred->getDef();
if (!Def->isSubClassOf("Predicate")) {
::PrintError(Def, "Unknown 'Predicate' Type");
return false;
}
if (Def->getValueAsString("CondString").empty())
continue;
if (SubtargetFeatures.count(Def) == 0) {
SubtargetFeatures.emplace(
Def, SubtargetFeatureInfo(Def, SubtargetFeatures.size()));
}
M.addRequiredFeature(Def);
}
return true;
}
bool CombineRuleBuilder::findRoots() {
const auto Finish = [&]() {
assert(MatchRoot);
if (hasOnlyCXXApplyPatterns() || hasEraseRoot())
return true;
auto *IPRoot = dyn_cast<InstructionPattern>(MatchRoot);
if (!IPRoot)
return true;
if (IPRoot->getNumInstDefs() == 0) {
// No defs to work with -> find the root using the pattern name.
auto It = ApplyPats.find(RootName);
if (It == ApplyPats.end()) {
PrintError("Cannot find root '" + RootName + "' in apply patterns!");
return false;
}
auto *ApplyRoot = dyn_cast<InstructionPattern>(It->second.get());
if (!ApplyRoot) {
PrintError("apply pattern root '" + RootName +
"' must be an instruction pattern");
return false;
}
ApplyRoots.insert(ApplyRoot);
return true;
}
// Collect all redefinitions of the MatchRoot's defs and put them in
// ApplyRoots.
const auto DefsNeeded = IPRoot->getApplyDefsNeeded();
for (auto &Op : DefsNeeded) {
assert(Op.isDef() && Op.isNamedOperand());
StringRef Name = Op.getOperandName();
auto *ApplyRedef = ApplyOpTable.getDef(Name);
if (!ApplyRedef) {
PrintError("'" + Name + "' must be redefined in the 'apply' pattern");
return false;
}
ApplyRoots.insert((InstructionPattern *)ApplyRedef);
}
if (auto It = ApplyPats.find(RootName); It != ApplyPats.end()) {
if (find(ApplyRoots, It->second.get()) == ApplyRoots.end()) {
PrintError("apply pattern '" + RootName +
"' is supposed to be a root but it does not redefine any of "
"the defs of the match root");
return false;
}
}
return true;
};
// Look by pattern name, e.g.
// (G_FNEG $x, $y):$root
if (auto MatchPatIt = MatchPats.find(RootName);
MatchPatIt != MatchPats.end()) {
MatchRoot = MatchPatIt->second.get();
return Finish();
}
// Look by def:
// (G_FNEG $root, $y)
auto LookupRes = MatchOpTable.lookup(RootName);
if (!LookupRes.Found) {
PrintError("Cannot find root '" + RootName + "' in match patterns!");
return false;
}
MatchRoot = LookupRes.Def;
if (!MatchRoot) {
PrintError("Cannot use live-in operand '" + RootName +
"' as match pattern root!");
return false;
}
return Finish();
}
bool CombineRuleBuilder::buildRuleOperandsTable() {
const auto DiagnoseRedefMatch = [&](StringRef OpName) {
PrintError("Operand '" + OpName +
"' is defined multiple times in the 'match' patterns");
};
const auto DiagnoseRedefApply = [&](StringRef OpName) {
PrintError("Operand '" + OpName +
"' is defined multiple times in the 'apply' patterns");
};
for (auto &Pat : values(MatchPats)) {
auto *IP = dyn_cast<InstructionPattern>(Pat.get());
if (IP && !MatchOpTable.addPattern(IP, DiagnoseRedefMatch))
return false;
}
for (auto &Pat : values(ApplyPats)) {
auto *IP = dyn_cast<InstructionPattern>(Pat.get());
if (IP && !ApplyOpTable.addPattern(IP, DiagnoseRedefApply))
return false;
}
return true;
}
bool CombineRuleBuilder::parseDefs(const DagInit &Def) {
if (Def.getOperatorAsDef(RuleDef.getLoc())->getName() != "defs") {
PrintError("Expected defs operator");
return false;
}
SmallVector<StringRef> Roots;
for (unsigned I = 0, E = Def.getNumArgs(); I < E; ++I) {
if (isSpecificDef(*Def.getArg(I), "root")) {
Roots.emplace_back(Def.getArgNameStr(I));
continue;
}
// Subclasses of GIDefMatchData should declare that this rule needs to pass
// data from the match stage to the apply stage, and ensure that the
// generated matcher has a suitable variable for it to do so.
if (Record *MatchDataRec =
getDefOfSubClass(*Def.getArg(I), "GIDefMatchData")) {
MatchDatas.emplace_back(Def.getArgNameStr(I),
MatchDataRec->getValueAsString("Type"));
continue;
}
// Otherwise emit an appropriate error message.
if (getDefOfSubClass(*Def.getArg(I), "GIDefKind"))
PrintError("This GIDefKind not implemented in tablegen");
else if (getDefOfSubClass(*Def.getArg(I), "GIDefKindWithArgs"))
PrintError("This GIDefKindWithArgs not implemented in tablegen");
else
PrintError("Expected a subclass of GIDefKind or a sub-dag whose "
"operator is of type GIDefKindWithArgs");
return false;
}
if (Roots.size() != 1) {
PrintError("Combine rules must have exactly one root");
return false;
}
RootName = Roots.front();
// Assign variables to all MatchDatas.
AssignMatchDataVariables(MatchDatas);
return true;
}
bool CombineRuleBuilder::parsePatternList(
const DagInit &List,
function_ref<bool(std::unique_ptr<Pattern>)> ParseAction,
StringRef Operator, ArrayRef<SMLoc> DiagLoc,
StringRef AnonPatNamePrefix) const {
if (List.getOperatorAsDef(RuleDef.getLoc())->getName() != Operator) {
::PrintError(DiagLoc, "Expected " + Operator + " operator");
return false;
}
if (List.getNumArgs() == 0) {
::PrintError(DiagLoc, Operator + " pattern list is empty");
return false;
}
// The match section consists of a list of matchers and predicates. Parse each
// one and add the equivalent GIMatchDag nodes, predicates, and edges.
for (unsigned I = 0; I < List.getNumArgs(); ++I) {
Init *Arg = List.getArg(I);
std::string Name = List.getArgName(I)
? List.getArgName(I)->getValue().str()
: makeAnonPatName(AnonPatNamePrefix, I);
if (auto Pat = parseInstructionPattern(*Arg, Name)) {
if (!ParseAction(std::move(Pat)))
return false;
continue;
}
if (auto Pat = parseWipMatchOpcodeMatcher(*Arg, Name)) {
if (!ParseAction(std::move(Pat)))
return false;
continue;
}
// Parse arbitrary C++ code
if (const auto *StringI = dyn_cast<StringInit>(Arg)) {
auto CXXPat = std::make_unique<CXXPattern>(*StringI, Name);
if (!ParseAction(std::move(CXXPat)))
return false;
continue;
}
::PrintError(DiagLoc,
"Failed to parse pattern: '" + Arg->getAsString() + "'");
return false;
}
return true;
}
std::unique_ptr<Pattern>
CombineRuleBuilder::parseInstructionPattern(const Init &Arg,
StringRef Name) const {
const DagInit *DagPat = dyn_cast<DagInit>(&Arg);
if (!DagPat)
return nullptr;
std::unique_ptr<InstructionPattern> Pat;
if (const DagInit *IP = getDagWithOperatorOfSubClass(Arg, "Instruction")) {
auto &Instr = CGT.getInstruction(IP->getOperatorAsDef(RuleDef.getLoc()));
Pat = std::make_unique<CodeGenInstructionPattern>(Instr, Name);
} else if (const DagInit *PFP =
getDagWithOperatorOfSubClass(Arg, PatFragClassName)) {
const Record *Def = PFP->getOperatorAsDef(RuleDef.getLoc());
const PatFrag *PF = parsePatFrag(Def);
if (!PF)
return nullptr; // Already diagnosed by parsePatFrag
Pat = std::make_unique<PatFragPattern>(*PF, Name);
} else if (const DagInit *BP =
getDagWithOperatorOfSubClass(Arg, BuiltinInstClassName)) {
Pat = std::make_unique<BuiltinPattern>(
*BP->getOperatorAsDef(RuleDef.getLoc()), Name);
} else {
return nullptr;
}
for (unsigned K = 0; K < DagPat->getNumArgs(); ++K) {
if (!parseInstructionPatternOperand(*Pat, DagPat->getArg(K),
DagPat->getArgName(K)))
return nullptr;
}
if (!Pat->checkSemantics(RuleDef.getLoc()))
return nullptr;
return std::move(Pat);
}
std::unique_ptr<Pattern>
CombineRuleBuilder::parseWipMatchOpcodeMatcher(const Init &Arg,
StringRef Name) const {
const DagInit *Matcher = getDagWithSpecificOperator(Arg, "wip_match_opcode");
if (!Matcher)
return nullptr;
if (Matcher->getNumArgs() == 0) {
PrintError("Empty wip_match_opcode");
return nullptr;
}
// Each argument is an opcode that can match.
auto Result = std::make_unique<AnyOpcodePattern>(Name);
for (const auto &Arg : Matcher->getArgs()) {
Record *OpcodeDef = getDefOfSubClass(*Arg, "Instruction");
if (OpcodeDef) {
Result->addOpcode(&CGT.getInstruction(OpcodeDef));
continue;
}
PrintError("Arguments to wip_match_opcode must be instructions");
return nullptr;
}
return std::move(Result);
}
bool CombineRuleBuilder::parseInstructionPatternOperand(
InstructionPattern &IP, const Init *OpInit,
const StringInit *OpName) const {
const auto ParseErr = [&]() {
PrintError("cannot parse operand '" + OpInit->getAsUnquotedString() + "' ");
if (OpName)
PrintNote("operand name is '" + OpName->getAsUnquotedString() + "'");
return false;
};
// untyped immediate, e.g. 0
if (const auto *IntImm = dyn_cast<IntInit>(OpInit)) {
std::string Name = OpName ? OpName->getAsUnquotedString() : "";
IP.addOperand(IntImm->getValue(), Name, /*Type=*/nullptr);
return true;
}
// typed immediate, e.g. (i32 0)
if (const auto *DagOp = dyn_cast<DagInit>(OpInit)) {
if (DagOp->getNumArgs() != 1)
return ParseErr();
Record *ImmTy = DagOp->getOperatorAsDef(RuleDef.getLoc());
if (!ImmTy->isSubClassOf("ValueType")) {
PrintError("cannot parse immediate '" + OpInit->getAsUnquotedString() +
"', '" + ImmTy->getName() + "' is not a ValueType!");
return false;
}
if (!IP.hasAllDefs()) {
PrintError("out operand of '" + IP.getInstName() +
"' cannot be an immediate");
return false;
}
const auto *Val = dyn_cast<IntInit>(DagOp->getArg(0));
if (!Val)
return ParseErr();
std::string Name = OpName ? OpName->getAsUnquotedString() : "";
IP.addOperand(Val->getValue(), Name, ImmTy);
return true;
}
// Typed operand e.g. $x/$z in (G_FNEG $x, $z)
if (auto *DefI = dyn_cast<DefInit>(OpInit)) {
if (!OpName) {
PrintError("expected an operand name after '" + OpInit->getAsString() +
"'");
return false;
}
const Record *Def = DefI->getDef();
if (!Def->isSubClassOf("ValueType")) {
PrintError("invalid operand type: '" + Def->getName() +
"' is not a ValueType");
return false;
}
IP.addOperand(OpName->getAsUnquotedString(), Def);
return true;
}
// Untyped operand e.g. $x/$z in (G_FNEG $x, $z)
if (isa<UnsetInit>(OpInit)) {
assert(OpName && "Unset w/ no OpName?");
IP.addOperand(OpName->getAsUnquotedString(), /*Type=*/nullptr);
return true;
}
return ParseErr();
}
std::unique_ptr<PatFrag>
CombineRuleBuilder::parsePatFragImpl(const Record *Def) const {
auto StackTrace = PrettyStackTraceParse(*Def);
if (!Def->isSubClassOf(PatFragClassName))
return nullptr;
const DagInit *Ins = Def->getValueAsDag("InOperands");
if (Ins->getOperatorAsDef(Def->getLoc())->getName() != "ins") {
::PrintError(Def, "expected 'ins' operator for " + PatFragClassName +
" in operands list");
return nullptr;
}
const DagInit *Outs = Def->getValueAsDag("OutOperands");
if (Outs->getOperatorAsDef(Def->getLoc())->getName() != "outs") {
::PrintError(Def, "expected 'outs' operator for " + PatFragClassName +
" out operands list");
return nullptr;
}
auto Result = std::make_unique<PatFrag>(*Def);
if (!parsePatFragParamList(Def->getLoc(), *Outs,
[&](StringRef Name, PatFrag::ParamKind Kind) {
Result->addOutParam(Name, Kind);
return true;
}))
return nullptr;
if (!parsePatFragParamList(Def->getLoc(), *Ins,
[&](StringRef Name, PatFrag::ParamKind Kind) {
Result->addInParam(Name, Kind);
return true;
}))
return nullptr;
const ListInit *Alts = Def->getValueAsListInit("Alternatives");
unsigned AltIdx = 0;
for (const Init *Alt : *Alts) {
const auto *PatDag = dyn_cast<DagInit>(Alt);
if (!PatDag) {
::PrintError(Def, "expected dag init for PatFrag pattern alternative");
return nullptr;
}
PatFrag::Alternative &A = Result->addAlternative();
const auto AddPat = [&](std::unique_ptr<Pattern> Pat) {
A.Pats.push_back(std::move(Pat));
return true;
};
if (!parsePatternList(
*PatDag, AddPat, "pattern", Def->getLoc(),
/*AnonPatPrefix*/
(Def->getName() + "_alt" + Twine(AltIdx++) + "_pattern").str()))
return nullptr;
}
if (!Result->buildOperandsTables() || !Result->checkSemantics())
return nullptr;
return Result;
}
bool CombineRuleBuilder::parsePatFragParamList(
ArrayRef<SMLoc> DiagLoc, const DagInit &OpsList,
function_ref<bool(StringRef, PatFrag::ParamKind)> ParseAction) const {
for (unsigned K = 0; K < OpsList.getNumArgs(); ++K) {
const StringInit *Name = OpsList.getArgName(K);
const Init *Ty = OpsList.getArg(K);
if (!Name) {
::PrintError(DiagLoc, "all operands must be named'");
return false;
}
const std::string NameStr = Name->getAsUnquotedString();
PatFrag::ParamKind OpKind;
if (isSpecificDef(*Ty, "gi_imm"))
OpKind = PatFrag::PK_Imm;
else if (isSpecificDef(*Ty, "root"))
OpKind = PatFrag::PK_Root;
else if (isa<UnsetInit>(Ty) ||
isSpecificDef(*Ty, "gi_mo")) // no type = gi_mo.
OpKind = PatFrag::PK_MachineOperand;
else {
::PrintError(
DiagLoc,
"'" + NameStr +
"' operand type was expected to be 'root', 'gi_imm' or 'gi_mo'");
return false;
}
if (!ParseAction(NameStr, OpKind))
return false;
}
return true;
}
const PatFrag *CombineRuleBuilder::parsePatFrag(const Record *Def) const {
// Cache already parsed PatFrags to avoid doing extra work.
static DenseMap<const Record *, std::unique_ptr<PatFrag>> ParsedPatFrags;
auto It = ParsedPatFrags.find(Def);
if (It != ParsedPatFrags.end()) {
SeenPatFrags.insert(It->second.get());
return It->second.get();
}
std::unique_ptr<PatFrag> NewPatFrag = parsePatFragImpl(Def);
if (!NewPatFrag) {
::PrintError(Def, "Could not parse " + PatFragClassName + " '" +
Def->getName() + "'");
// Put a nullptr in the map so we don't attempt parsing this again.
ParsedPatFrags[Def] = nullptr;
return nullptr;
}
const auto *Res = NewPatFrag.get();
ParsedPatFrags[Def] = std::move(NewPatFrag);
SeenPatFrags.insert(Res);
return Res;
}
bool CombineRuleBuilder::emitMatchPattern(CodeExpansions &CE,
const PatternAlternatives &Alts,
const InstructionPattern &IP) {
auto StackTrace = PrettyStackTraceEmit(RuleDef, &IP);
auto &M = addRuleMatcher(Alts);
InstructionMatcher &IM = M.addInstructionMatcher("root");
declareInstExpansion(CE, IM, IP.getName());
DenseSet<const Pattern *> SeenPats;
const auto FindOperandDef = [&](StringRef Op) -> InstructionPattern * {
return MatchOpTable.getDef(Op);
};
if (const auto *CGP = dyn_cast<CodeGenInstructionPattern>(&IP)) {
if (!emitCodeGenInstructionMatchPattern(CE, Alts, M, IM, *CGP, SeenPats,
FindOperandDef))
return false;
} else if (const auto *PFP = dyn_cast<PatFragPattern>(&IP)) {
if (!PFP->getPatFrag().canBeMatchRoot()) {
PrintError("cannot use '" + PFP->getInstName() + " as match root");
return false;
}
if (!emitPatFragMatchPattern(CE, Alts, M, &IM, *PFP, SeenPats))
return false;
} else if (isa<BuiltinPattern>(&IP)) {
llvm_unreachable("No match builtins known!");
} else
llvm_unreachable("Unknown kind of InstructionPattern!");
// Emit remaining patterns
for (auto &Pat : values(MatchPats)) {
if (SeenPats.contains(Pat.get()))
continue;
switch (Pat->getKind()) {
case Pattern::K_AnyOpcode:
PrintError("wip_match_opcode can not be used with instruction patterns!");
return false;
case Pattern::K_PatFrag: {
if (!emitPatFragMatchPattern(CE, Alts, M, /*IM*/ nullptr,
*cast<PatFragPattern>(Pat.get()), SeenPats))
return false;
continue;
}
case Pattern::K_Builtin:
PrintError("No known match builtins");
return false;
case Pattern::K_CodeGenInstruction:
cast<InstructionPattern>(Pat.get())->reportUnreachable(RuleDef.getLoc());
return false;
case Pattern::K_CXX: {
addCXXPredicate(M, CE, *cast<CXXPattern>(Pat.get()), Alts);
continue;
}
default:
llvm_unreachable("unknown pattern kind!");
}
}
return emitApplyPatterns(CE, M);
}
bool CombineRuleBuilder::emitMatchPattern(CodeExpansions &CE,
const PatternAlternatives &Alts,
const AnyOpcodePattern &AOP) {
auto StackTrace = PrettyStackTraceEmit(RuleDef, &AOP);
for (const CodeGenInstruction *CGI : AOP.insts()) {
auto &M = addRuleMatcher(Alts, "wip_match_opcode '" +
CGI->TheDef->getName() + "'");
InstructionMatcher &IM = M.addInstructionMatcher(AOP.getName());
declareInstExpansion(CE, IM, AOP.getName());
// declareInstExpansion needs to be identical, otherwise we need to create a
// CodeExpansions object here instead.
assert(IM.getInsnVarID() == 0);
IM.addPredicate<InstructionOpcodeMatcher>(CGI);
// Emit remaining patterns.
for (auto &Pat : values(MatchPats)) {
if (Pat.get() == &AOP)
continue;
switch (Pat->getKind()) {
case Pattern::K_AnyOpcode:
PrintError("wip_match_opcode can only be present once!");
return false;
case Pattern::K_PatFrag: {
DenseSet<const Pattern *> SeenPats;
if (!emitPatFragMatchPattern(CE, Alts, M, /*IM*/ nullptr,
*cast<PatFragPattern>(Pat.get()),
SeenPats))
return false;
continue;
}
case Pattern::K_Builtin:
PrintError("No known match builtins");
return false;
case Pattern::K_CodeGenInstruction:
cast<InstructionPattern>(Pat.get())->reportUnreachable(
RuleDef.getLoc());
return false;
case Pattern::K_CXX: {
addCXXPredicate(M, CE, *cast<CXXPattern>(Pat.get()), Alts);
break;
}
default:
llvm_unreachable("unknown pattern kind!");
}
}
if (!emitApplyPatterns(CE, M))
return false;
}
return true;
}
bool CombineRuleBuilder::emitPatFragMatchPattern(
CodeExpansions &CE, const PatternAlternatives &Alts, RuleMatcher &RM,
InstructionMatcher *IM, const PatFragPattern &PFP,
DenseSet<const Pattern *> &SeenPats) {
auto StackTrace = PrettyStackTraceEmit(RuleDef, &PFP);
if (SeenPats.contains(&PFP))
return true;
SeenPats.insert(&PFP);
const auto &PF = PFP.getPatFrag();
if (!IM) {
// When we don't have an IM, this means this PatFrag isn't reachable from
// the root. This is only acceptable if it doesn't define anything (e.g. a
// pure C++ PatFrag).
if (PF.num_out_params() != 0) {
PFP.reportUnreachable(RuleDef.getLoc());
return false;
}
} else {
// When an IM is provided, this is reachable from the root, and we're
// expecting to have output operands.
// TODO: If we want to allow for multiple roots we'll need a map of IMs
// then, and emission becomes a bit more complicated.
assert(PF.num_roots() == 1);
}
CodeExpansions PatFragCEs;
if (!PFP.mapInputCodeExpansions(CE, PatFragCEs, RuleDef.getLoc()))
return false;
// List of {ParamName, ArgName}.
// When all patterns have been emitted, find expansions in PatFragCEs named
// ArgName and add their expansion to CE using ParamName as the key.
SmallVector<std::pair<std::string, std::string>, 4> CEsToImport;
// Map parameter names to the actual argument.
const auto OperandMapper =
[&](const InstructionOperand &O) -> InstructionOperand {
if (!O.isNamedOperand())
return O;
StringRef ParamName = O.getOperandName();
// Not sure what to do with those tbh. They should probably never be here.
assert(!O.isNamedImmediate() && "TODO: handle named imms");
unsigned PIdx = PF.getParamIdx(ParamName);
// Map parameters to the argument values.
if (PIdx == (unsigned)-1) {
// This is a temp of the PatFragPattern, prefix the name to avoid
// conflicts.
return O.withNewName((PFP.getName() + "." + ParamName).str());
}
// The operand will be added to PatFragCEs's code expansions using the
// parameter's name. If it's bound to some operand during emission of the
// patterns, we'll want to add it to CE.
auto ArgOp = PFP.getOperand(PIdx);
if (ArgOp.isNamedOperand())
CEsToImport.emplace_back(ArgOp.getOperandName().str(), ParamName);
if (ArgOp.getType() && O.getType() && ArgOp.getType() != O.getType()) {
StringRef PFName = PF.getName();
PrintWarning("impossible type constraints: operand " + Twine(PIdx) +
" of '" + PFP.getName() + "' has type '" +
ArgOp.getType()->getName() + "', but '" + PFName +
"' constrains it to '" + O.getType()->getName() + "'");
if (ArgOp.isNamedOperand())
PrintNote("operand " + Twine(PIdx) + " of '" + PFP.getName() +
"' is '" + ArgOp.getOperandName() + "'");
if (O.isNamedOperand())
PrintNote("argument " + Twine(PIdx) + " of '" + PFName + "' is '" +
ParamName + "'");
}
return ArgOp;
};
// PatFragPatterns are only made of InstructionPatterns or CXXPatterns.
// Emit instructions from the root.
const auto &FragAlt = PF.getAlternative(Alts.lookup(&PFP));
const auto &FragAltOT = FragAlt.OpTable;
const auto LookupOperandDef =
[&](StringRef Op) -> const InstructionPattern * {
return FragAltOT.getDef(Op);
};
DenseSet<const Pattern *> PatFragSeenPats;
for (const auto &[Idx, InOp] : enumerate(PF.out_params())) {
if (InOp.Kind != PatFrag::PK_Root)
continue;
StringRef ParamName = InOp.Name;
const auto *Def = FragAltOT.getDef(ParamName);
assert(Def && "PatFrag::checkSemantics should have emitted an error if "
"an out operand isn't defined!");
assert(isa<CodeGenInstructionPattern>(Def) &&
"Nested PatFrags not supported yet");
if (!emitCodeGenInstructionMatchPattern(
PatFragCEs, Alts, RM, *IM, *cast<CodeGenInstructionPattern>(Def),
PatFragSeenPats, LookupOperandDef, OperandMapper))
return false;
}
// Emit leftovers.
for (const auto &Pat : FragAlt.Pats) {
if (PatFragSeenPats.contains(Pat.get()))
continue;
if (const auto *CXXPat = dyn_cast<CXXPattern>(Pat.get())) {
addCXXPredicate(RM, PatFragCEs, *CXXPat, Alts);
continue;
}
if (const auto *IP = dyn_cast<InstructionPattern>(Pat.get())) {
IP->reportUnreachable(PF.getLoc());
return false;
}
llvm_unreachable("Unexpected pattern kind in PatFrag");
}
for (const auto &[ParamName, ArgName] : CEsToImport) {
// Note: we're find if ParamName already exists. It just means it's been
// bound before, so we prefer to keep the first binding.
CE.declare(ParamName, PatFragCEs.lookup(ArgName));
}
return true;
}
bool CombineRuleBuilder::emitApplyPatterns(CodeExpansions &CE, RuleMatcher &M) {
if (hasOnlyCXXApplyPatterns()) {
for (auto &Pat : values(ApplyPats))
addCXXAction(M, CE, *cast<CXXPattern>(Pat.get()));
return true;
}
DenseSet<const Pattern *> SeenPats;
StringMap<unsigned> OperandToTempRegID;
for (auto *ApplyRoot : ApplyRoots) {
assert(isa<InstructionPattern>(ApplyRoot) &&
"Root can only be a InstructionPattern!");
if (!emitInstructionApplyPattern(CE, M,
cast<InstructionPattern>(*ApplyRoot),
SeenPats, OperandToTempRegID))
return false;
}
for (auto &Pat : values(ApplyPats)) {
if (SeenPats.contains(Pat.get()))
continue;
switch (Pat->getKind()) {
case Pattern::K_AnyOpcode:
llvm_unreachable("Unexpected pattern in apply!");
case Pattern::K_PatFrag:
// TODO: We could support pure C++ PatFrags as a temporary thing.
llvm_unreachable("Unexpected pattern in apply!");
case Pattern::K_Builtin:
if (!emitInstructionApplyPattern(CE, M, cast<BuiltinPattern>(*Pat),
SeenPats, OperandToTempRegID))
return false;
break;
case Pattern::K_CodeGenInstruction:
cast<CodeGenInstructionPattern>(*Pat).reportUnreachable(RuleDef.getLoc());
return false;
case Pattern::K_CXX: {
addCXXAction(M, CE, *cast<CXXPattern>(Pat.get()));
continue;
}
default:
llvm_unreachable("unknown pattern kind!");
}
}
return true;
}
bool CombineRuleBuilder::emitInstructionApplyPattern(
CodeExpansions &CE, RuleMatcher &M, const InstructionPattern &P,
DenseSet<const Pattern *> &SeenPats,
StringMap<unsigned> &OperandToTempRegID) {
auto StackTrace = PrettyStackTraceEmit(RuleDef, &P);
if (SeenPats.contains(&P))
return true;
SeenPats.insert(&P);
// First, render the uses.
for (auto &Op : P.named_operands()) {
if (Op.isDef())
continue;
StringRef OpName = Op.getOperandName();
if (const auto *DefPat = ApplyOpTable.getDef(OpName)) {
if (!emitInstructionApplyPattern(CE, M, *DefPat, SeenPats,
OperandToTempRegID))
return false;
} else {
// If we have no def, check this exists in the MatchRoot.
if (!Op.isNamedImmediate() && !MatchOpTable.lookup(OpName).Found) {
PrintError("invalid output operand '" + OpName +
"': operand is not a live-in of the match pattern, and it "
"has no definition");
return false;
}
}
}
if (const auto *BP = dyn_cast<BuiltinPattern>(&P))
return emitBuiltinApplyPattern(CE, M, *BP, OperandToTempRegID);
if (isa<PatFragPattern>(&P))
llvm_unreachable("PatFragPatterns is not supported in 'apply'!");
auto &CGIP = cast<CodeGenInstructionPattern>(P);
// Now render this inst.
auto &DstMI =
M.addAction<BuildMIAction>(M.allocateOutputInsnID(), &CGIP.getInst());
for (auto &Op : P.operands()) {
if (Op.isNamedImmediate()) {
PrintError("invalid output operand '" + Op.getOperandName() +
"': output immediates cannot be named");
PrintNote("while emitting pattern '" + P.getName() + "' (" +
P.getInstName() + ")");
return false;
}
if (Op.hasImmValue()) {
if (!emitCodeGenInstructionApplyImmOperand(M, DstMI, CGIP, Op))
return false;
continue;
}
StringRef OpName = Op.getOperandName();
// Uses of operand.
if (!Op.isDef()) {
if (auto It = OperandToTempRegID.find(OpName);
It != OperandToTempRegID.end()) {
assert(!MatchOpTable.lookup(OpName).Found &&
"Temp reg is also from match pattern?");
DstMI.addRenderer<TempRegRenderer>(It->second);
} else {
// This should be a match live in or a redef of a matched instr.
// If it's a use of a temporary register, then we messed up somewhere -
// the previous condition should have passed.
assert(MatchOpTable.lookup(OpName).Found &&
!ApplyOpTable.getDef(OpName) && "Temp reg not emitted yet!");
DstMI.addRenderer<CopyRenderer>(OpName);
}
continue;
}
// Determine what we're dealing with. Are we replace a matched instruction?
// Creating a new one?
auto OpLookupRes = MatchOpTable.lookup(OpName);
if (OpLookupRes.Found) {
if (OpLookupRes.isLiveIn()) {
// live-in of the match pattern.
PrintError("Cannot define live-in operand '" + OpName +
"' in the 'apply' pattern");
return false;
}
assert(OpLookupRes.Def);
// TODO: Handle this. We need to mutate the instr, or delete the old
// one.
// Likewise, we also need to ensure we redef everything, if the
// instr has more than one def, we need to redef all or nothing.
if (OpLookupRes.Def != MatchRoot) {
PrintError("redefining an instruction other than the root is not "
"supported (operand '" +
OpName + "')");
return false;
}
// redef of a match
DstMI.addRenderer<CopyRenderer>(OpName);
continue;
}
// Define a new register unique to the apply patterns (AKA a "temp"
// register).
unsigned TempRegID;
if (auto It = OperandToTempRegID.find(OpName);
It != OperandToTempRegID.end()) {
TempRegID = It->second;
} else {
// This is a brand new register.
TempRegID = M.allocateTempRegID();
OperandToTempRegID[OpName] = TempRegID;
const Record *Ty = Op.getType();
if (!Ty) {
PrintError("def of a new register '" + OpName +
"' in the apply patterns must have a type");
return false;
}
declareTempRegExpansion(CE, TempRegID, OpName);
// Always insert the action at the beginning, otherwise we may end up
// using the temp reg before it's available.
M.insertAction<MakeTempRegisterAction>(
M.actions_begin(), getLLTCodeGenFromRecord(Ty), TempRegID);
}
DstMI.addRenderer<TempRegRenderer>(TempRegID);
}
// TODO: works?
DstMI.chooseInsnToMutate(M);
declareInstExpansion(CE, DstMI, P.getName());
return true;
}
bool CombineRuleBuilder::emitCodeGenInstructionApplyImmOperand(
RuleMatcher &M, BuildMIAction &DstMI, const CodeGenInstructionPattern &P,
const InstructionOperand &O) {
// If we have a type, we implicitly emit a G_CONSTANT, except for G_CONSTANT
// itself where we emit a CImm.
//
// No type means we emit a simple imm.
// G_CONSTANT is a special case and needs a CImm though so this is likely a
// mistake.
const bool isGConstant = P.is("G_CONSTANT");
const Record *Ty = O.getType();
if (!Ty) {
if (isGConstant) {
PrintError("'G_CONSTANT' immediate must be typed!");
PrintNote("while emitting pattern '" + P.getName() + "' (" +
P.getInstName() + ")");
return false;
}
DstMI.addRenderer<ImmRenderer>(O.getImmValue());
return true;
}
LLTCodeGen LLT = getLLTCodeGenFromRecord(Ty);
if (isGConstant) {
DstMI.addRenderer<ImmRenderer>(O.getImmValue(), LLT);
return true;
}
unsigned TempRegID = M.allocateTempRegID();
auto ActIt = M.insertAction<BuildMIAction>(
M.actions_begin(), M.allocateOutputInsnID(), &getGConstant());
// Ensure MakeTempReg occurs before the BuildMI of th G_CONSTANT.
M.insertAction<MakeTempRegisterAction>(ActIt, LLT, TempRegID);
auto &ConstantMI = *static_cast<BuildMIAction *>(ActIt->get());
ConstantMI.addRenderer<TempRegRenderer>(TempRegID);
ConstantMI.addRenderer<ImmRenderer>(O.getImmValue(), LLT);
DstMI.addRenderer<TempRegRenderer>(TempRegID);
return true;
}
bool CombineRuleBuilder::emitBuiltinApplyPattern(
CodeExpansions &CE, RuleMatcher &M, const BuiltinPattern &P,
StringMap<unsigned> &OperandToTempRegID) {
const auto Error = [&](Twine Reason) {
PrintError("cannot emit '" + P.getInstName() + "' builtin: " + Reason);
return false;
};
switch (P.getBuiltinKind()) {
case BI_EraseRoot: {
// Root is always inst 0.
M.addAction<EraseInstAction>(/*InsnID*/ 0);
return true;
}
case BI_ReplaceReg: {
StringRef Old = P.getOperand(0).getOperandName();
StringRef New = P.getOperand(1).getOperandName();
if (!ApplyOpTable.lookup(New).Found && !MatchOpTable.lookup(New).Found)
return Error("unknown operand '" + Old + "'");
auto &OldOM = M.getOperandMatcher(Old);
if (auto It = OperandToTempRegID.find(New);
It != OperandToTempRegID.end()) {
// Replace with temp reg.
M.addAction<ReplaceRegAction>(OldOM.getInsnVarID(), OldOM.getOpIdx(),
It->second);
} else {
// Replace with matched reg.
auto &NewOM = M.getOperandMatcher(New);
M.addAction<ReplaceRegAction>(OldOM.getInsnVarID(), OldOM.getOpIdx(),
NewOM.getInsnVarID(), NewOM.getOpIdx());
}
// checkSemantics should have ensured that we can only rewrite the root.
// Ensure we're deleting it.
assert(MatchOpTable.getDef(Old) == MatchRoot);
// TODO: We could avoid adding the action again if it's already in. The
// MatchTable is smart enough to only emit one opcode even if
// EraseInstAction is present multiple times. I think searching for a copy
// is more expensive than just blindly adding it though.
M.addAction<EraseInstAction>(/*InsnID*/ 0);
return true;
}
}
llvm_unreachable("Unknown BuiltinKind!");
}
bool isLiteralImm(const InstructionPattern &P, unsigned OpIdx) {
if (const auto *CGP = dyn_cast<CodeGenInstructionPattern>(&P)) {
StringRef InstName = CGP->getInst().TheDef->getName();
return (InstName == "G_CONSTANT" || InstName == "G_FCONSTANT") &&
OpIdx == 1;
}
llvm_unreachable("TODO");
}
bool CombineRuleBuilder::emitCodeGenInstructionMatchPattern(
CodeExpansions &CE, const PatternAlternatives &Alts, RuleMatcher &M,
InstructionMatcher &IM, const CodeGenInstructionPattern &P,
DenseSet<const Pattern *> &SeenPats, OperandDefLookupFn LookupOperandDef,
OperandMapperFnRef OperandMapper) {
auto StackTrace = PrettyStackTraceEmit(RuleDef, &P);
if (SeenPats.contains(&P))
return true;
SeenPats.insert(&P);
IM.addPredicate<InstructionOpcodeMatcher>(&P.getInst());
declareInstExpansion(CE, IM, P.getName());
for (const auto &[Idx, OriginalO] : enumerate(P.operands())) {
// Remap the operand. This is used when emitting InstructionPatterns inside
// PatFrags, so it can remap them to the arguments passed to the pattern.
//
// We use the remapped operand to emit immediates, and for the symbolic
// operand names (in IM.addOperand). CodeExpansions and OperandTable lookups
// still use the original name.
//
// The "def" flag on the remapped operand is always ignored.
auto RemappedO = OperandMapper(OriginalO);
assert(RemappedO.isNamedOperand() == OriginalO.isNamedOperand() &&
"Cannot remap an unnamed operand to a named one!");
const auto OpName =
RemappedO.isNamedOperand() ? RemappedO.getOperandName().str() : "";
OperandMatcher &OM =
IM.addOperand(Idx, OpName, AllocatedTemporariesBaseID++);
if (!OpName.empty())
declareOperandExpansion(CE, OM, OriginalO.getOperandName());
// Handle immediates.
if (RemappedO.hasImmValue()) {
if (isLiteralImm(P, Idx))
OM.addPredicate<LiteralIntOperandMatcher>(RemappedO.getImmValue());
else
OM.addPredicate<ConstantIntOperandMatcher>(RemappedO.getImmValue());
}
// Handle typed operands, but only bother to check if it hasn't been done
// before.
//
// getOperandMatcher will always return the first OM to have been created
// for that Operand. "OM" here is always a new OperandMatcher.
//
// Always emit a check for unnamed operands.
if (OpName.empty() ||
!M.getOperandMatcher(OpName).contains<LLTOperandMatcher>()) {
if (const Record *Ty = RemappedO.getType())
OM.addPredicate<LLTOperandMatcher>(getLLTCodeGenFromRecord(Ty));
}
// Stop here if the operand is a def, or if it had no name.
if (OriginalO.isDef() || !OriginalO.isNamedOperand())
continue;
const auto *DefPat = LookupOperandDef(OriginalO.getOperandName());
if (!DefPat)
continue;
if (OriginalO.hasImmValue()) {
assert(!OpName.empty());
// This is a named immediate that also has a def, that's not okay.
// e.g.
// (G_SEXT $y, (i32 0))
// (COPY $x, 42:$y)
PrintError("'" + OpName +
"' is a named immediate, it cannot be defined by another "
"instruction");
PrintNote("'" + OpName + "' is defined by '" + DefPat->getName() + "'");
return false;
}
// From here we know that the operand defines an instruction, and we need to
// emit it.
auto InstOpM =
OM.addPredicate<InstructionOperandMatcher>(M, DefPat->getName());
if (!InstOpM) {
// TODO: copy-pasted from GlobalISelEmitter.cpp. Is it still relevant
// here?
PrintError("Nested instruction '" + DefPat->getName() +
"' cannot be the same as another operand '" +
OriginalO.getOperandName() + "'");
return false;
}
auto &IM = (*InstOpM)->getInsnMatcher();
if (const auto *CGIDef = dyn_cast<CodeGenInstructionPattern>(DefPat)) {
if (!emitCodeGenInstructionMatchPattern(CE, Alts, M, IM, *CGIDef,
SeenPats, LookupOperandDef,
OperandMapper))
return false;
continue;
}
if (const auto *PFPDef = dyn_cast<PatFragPattern>(DefPat)) {
if (!emitPatFragMatchPattern(CE, Alts, M, &IM, *PFPDef, SeenPats))
return false;
continue;
}
llvm_unreachable("unknown type of InstructionPattern");
}
return true;
}
//===- GICombinerEmitter --------------------------------------------------===//
/// Main implementation class. This emits the tablegenerated output.
///
/// It collects rules, uses `CombineRuleBuilder` to parse them and accumulate
/// RuleMatchers, then takes all the necessary state/data from the various
/// static storage pools and wires them together to emit the match table &
/// associated function/data structures.
class GICombinerEmitter final : public GlobalISelMatchTableExecutorEmitter {
RecordKeeper &Records;
StringRef Name;
const CodeGenTarget &Target;
Record *Combiner;
unsigned NextRuleID = 0;
// List all combine rules (ID, name) imported.
// Note that the combiner rule ID is different from the RuleMatcher ID. The
// latter is internal to the MatchTable, the former is the canonical ID of the
// combine rule used to disable/enable it.
std::vector<std::pair<unsigned, std::string>> AllCombineRules;
MatchTable buildMatchTable(MutableArrayRef<RuleMatcher> Rules);
void emitRuleConfigImpl(raw_ostream &OS);
void emitAdditionalImpl(raw_ostream &OS) override;
void emitMIPredicateFns(raw_ostream &OS) override;
void emitI64ImmPredicateFns(raw_ostream &OS) override;
void emitAPFloatImmPredicateFns(raw_ostream &OS) override;
void emitAPIntImmPredicateFns(raw_ostream &OS) override;
void emitTestSimplePredicate(raw_ostream &OS) override;
void emitRunCustomAction(raw_ostream &OS) override;
void emitAdditionalTemporariesDecl(raw_ostream &OS,
StringRef Indent) override;
const CodeGenTarget &getTarget() const override { return Target; }
StringRef getClassName() const override {
return Combiner->getValueAsString("Classname");
}
StringRef getCombineAllMethodName() const {
return Combiner->getValueAsString("CombineAllMethodName");
}
std::string getRuleConfigClassName() const {
return getClassName().str() + "RuleConfig";
}
void gatherRules(std::vector<RuleMatcher> &Rules,
const std::vector<Record *> &&RulesAndGroups);
public:
explicit GICombinerEmitter(RecordKeeper &RK, const CodeGenTarget &Target,
StringRef Name, Record *Combiner);
~GICombinerEmitter() {}
void run(raw_ostream &OS);
};
void GICombinerEmitter::emitRuleConfigImpl(raw_ostream &OS) {
OS << "struct " << getRuleConfigClassName() << " {\n"
<< " SparseBitVector<> DisabledRules;\n\n"
<< " bool isRuleEnabled(unsigned RuleID) const;\n"
<< " bool parseCommandLineOption();\n"
<< " bool setRuleEnabled(StringRef RuleIdentifier);\n"
<< " bool setRuleDisabled(StringRef RuleIdentifier);\n"
<< "};\n\n";
std::vector<std::pair<std::string, std::string>> Cases;
Cases.reserve(AllCombineRules.size());
for (const auto &[ID, Name] : AllCombineRules)
Cases.emplace_back(Name, "return " + to_string(ID) + ";\n");
OS << "static std::optional<uint64_t> getRuleIdxForIdentifier(StringRef "
"RuleIdentifier) {\n"
<< " uint64_t I;\n"
<< " // getAtInteger(...) returns false on success\n"
<< " bool Parsed = !RuleIdentifier.getAsInteger(0, I);\n"
<< " if (Parsed)\n"
<< " return I;\n\n"
<< "#ifndef NDEBUG\n";
StringMatcher Matcher("RuleIdentifier", Cases, OS);
Matcher.Emit();
OS << "#endif // ifndef NDEBUG\n\n"
<< " return std::nullopt;\n"
<< "}\n";
OS << "static std::optional<std::pair<uint64_t, uint64_t>> "
"getRuleRangeForIdentifier(StringRef RuleIdentifier) {\n"
<< " std::pair<StringRef, StringRef> RangePair = "
"RuleIdentifier.split('-');\n"
<< " if (!RangePair.second.empty()) {\n"
<< " const auto First = "
"getRuleIdxForIdentifier(RangePair.first);\n"
<< " const auto Last = "
"getRuleIdxForIdentifier(RangePair.second);\n"
<< " if (!First || !Last)\n"
<< " return std::nullopt;\n"
<< " if (First >= Last)\n"
<< " report_fatal_error(\"Beginning of range should be before "
"end of range\");\n"
<< " return {{*First, *Last + 1}};\n"
<< " }\n"
<< " if (RangePair.first == \"*\") {\n"
<< " return {{0, " << AllCombineRules.size() << "}};\n"
<< " }\n"
<< " const auto I = getRuleIdxForIdentifier(RangePair.first);\n"
<< " if (!I)\n"
<< " return std::nullopt;\n"
<< " return {{*I, *I + 1}};\n"
<< "}\n\n";
for (bool Enabled : {true, false}) {
OS << "bool " << getRuleConfigClassName() << "::setRule"
<< (Enabled ? "Enabled" : "Disabled") << "(StringRef RuleIdentifier) {\n"
<< " auto MaybeRange = getRuleRangeForIdentifier(RuleIdentifier);\n"
<< " if (!MaybeRange)\n"
<< " return false;\n"
<< " for (auto I = MaybeRange->first; I < MaybeRange->second; ++I)\n"
<< " DisabledRules." << (Enabled ? "reset" : "set") << "(I);\n"
<< " return true;\n"
<< "}\n\n";
}
OS << "static std::vector<std::string> " << Name << "Option;\n"
<< "static cl::list<std::string> " << Name << "DisableOption(\n"
<< " \"" << Name.lower() << "-disable-rule\",\n"
<< " cl::desc(\"Disable one or more combiner rules temporarily in "
<< "the " << Name << " pass\"),\n"
<< " cl::CommaSeparated,\n"
<< " cl::Hidden,\n"
<< " cl::cat(GICombinerOptionCategory),\n"
<< " cl::callback([](const std::string &Str) {\n"
<< " " << Name << "Option.push_back(Str);\n"
<< " }));\n"
<< "static cl::list<std::string> " << Name << "OnlyEnableOption(\n"
<< " \"" << Name.lower() << "-only-enable-rule\",\n"
<< " cl::desc(\"Disable all rules in the " << Name
<< " pass then re-enable the specified ones\"),\n"
<< " cl::Hidden,\n"
<< " cl::cat(GICombinerOptionCategory),\n"
<< " cl::callback([](const std::string &CommaSeparatedArg) {\n"
<< " StringRef Str = CommaSeparatedArg;\n"
<< " " << Name << "Option.push_back(\"*\");\n"
<< " do {\n"
<< " auto X = Str.split(\",\");\n"
<< " " << Name << "Option.push_back((\"!\" + X.first).str());\n"
<< " Str = X.second;\n"
<< " } while (!Str.empty());\n"
<< " }));\n"
<< "\n\n"
<< "bool " << getRuleConfigClassName()
<< "::isRuleEnabled(unsigned RuleID) const {\n"
<< " return !DisabledRules.test(RuleID);\n"
<< "}\n"
<< "bool " << getRuleConfigClassName() << "::parseCommandLineOption() {\n"
<< " for (StringRef Identifier : " << Name << "Option) {\n"
<< " bool Enabled = Identifier.consume_front(\"!\");\n"
<< " if (Enabled && !setRuleEnabled(Identifier))\n"
<< " return false;\n"
<< " if (!Enabled && !setRuleDisabled(Identifier))\n"
<< " return false;\n"
<< " }\n"
<< " return true;\n"
<< "}\n\n";
}
void GICombinerEmitter::emitAdditionalImpl(raw_ostream &OS) {
OS << "bool " << getClassName() << "::" << getCombineAllMethodName()
<< "(MachineInstr &I) const {\n"
<< " const TargetSubtargetInfo &ST = MF.getSubtarget();\n"
<< " const PredicateBitset AvailableFeatures = "
"getAvailableFeatures();\n"
<< " NewMIVector OutMIs;\n"
<< " State.MIs.clear();\n"
<< " State.MIs.push_back(&I);\n"
<< " " << MatchDataInfo::StructName << " = "
<< MatchDataInfo::StructTypeName << "();\n\n"
<< " if (executeMatchTable(*this, OutMIs, State, ExecInfo"
<< ", getMatchTable(), *ST.getInstrInfo(), MRI, "
"*MRI.getTargetRegisterInfo(), *ST.getRegBankInfo(), AvailableFeatures"
<< ", /*CoverageInfo*/ nullptr, &Observer)) {\n"
<< " return true;\n"
<< " }\n\n"
<< " return false;\n"
<< "}\n\n";
}
void GICombinerEmitter::emitMIPredicateFns(raw_ostream &OS) {
auto MatchCode = CXXPredicateCode::getAllMatchCode();
emitMIPredicateFnsImpl<const CXXPredicateCode *>(
OS, "", ArrayRef<const CXXPredicateCode *>(MatchCode),
[](const CXXPredicateCode *C) -> StringRef { return C->BaseEnumName; },
[](const CXXPredicateCode *C) -> StringRef { return C->Code; });
}
void GICombinerEmitter::emitI64ImmPredicateFns(raw_ostream &OS) {
// Unused, but still needs to be called.
emitImmPredicateFnsImpl<unsigned>(
OS, "I64", "int64_t", {}, [](unsigned) { return ""; },
[](unsigned) { return ""; });
}
void GICombinerEmitter::emitAPFloatImmPredicateFns(raw_ostream &OS) {
// Unused, but still needs to be called.
emitImmPredicateFnsImpl<unsigned>(
OS, "APFloat", "const APFloat &", {}, [](unsigned) { return ""; },
[](unsigned) { return ""; });
}
void GICombinerEmitter::emitAPIntImmPredicateFns(raw_ostream &OS) {
// Unused, but still needs to be called.
emitImmPredicateFnsImpl<unsigned>(
OS, "APInt", "const APInt &", {}, [](unsigned) { return ""; },
[](unsigned) { return ""; });
}
void GICombinerEmitter::emitTestSimplePredicate(raw_ostream &OS) {
if (!AllCombineRules.empty()) {
OS << "enum {\n";
std::string EnumeratorSeparator = " = GICXXPred_Invalid + 1,\n";
// To avoid emitting a switch, we expect that all those rules are in order.
// That way we can just get the RuleID from the enum by subtracting
// (GICXXPred_Invalid + 1).
unsigned ExpectedID = 0;
(void)ExpectedID;
for (const auto &ID : keys(AllCombineRules)) {
assert(ExpectedID++ == ID && "combine rules are not ordered!");
OS << " " << getIsEnabledPredicateEnumName(ID) << EnumeratorSeparator;
EnumeratorSeparator = ",\n";
}
OS << "};\n\n";
}
OS << "bool " << getClassName()
<< "::testSimplePredicate(unsigned Predicate) const {\n"
<< " return RuleConfig.isRuleEnabled(Predicate - "
"GICXXPred_Invalid - "
"1);\n"
<< "}\n";
}
void GICombinerEmitter::emitRunCustomAction(raw_ostream &OS) {
const auto ApplyCode = CXXPredicateCode::getAllApplyCode();
if (!ApplyCode.empty()) {
OS << "enum {\n";
std::string EnumeratorSeparator = " = GICXXCustomAction_Invalid + 1,\n";
for (const auto &Apply : ApplyCode) {
OS << " " << Apply->getEnumNameWithPrefix(CXXApplyPrefix)
<< EnumeratorSeparator;
EnumeratorSeparator = ",\n";
}
OS << "};\n";
}
OS << "void " << getClassName()
<< "::runCustomAction(unsigned ApplyID, const MatcherState &State, "
"NewMIVector &OutMIs) const "
"{\n";
if (!ApplyCode.empty()) {
OS << " switch(ApplyID) {\n";
for (const auto &Apply : ApplyCode) {
OS << " case " << Apply->getEnumNameWithPrefix(CXXApplyPrefix) << ":{\n"
<< " " << join(split(Apply->Code, "\n"), "\n ") << "\n"
<< " return;\n";
OS << " }\n";
}
OS << "}\n";
}
OS << " llvm_unreachable(\"Unknown Apply Action\");\n"
<< "}\n";
}
void GICombinerEmitter::emitAdditionalTemporariesDecl(raw_ostream &OS,
StringRef Indent) {
OS << Indent << "struct " << MatchDataInfo::StructTypeName << " {\n";
for (const auto &[Type, VarNames] : AllMatchDataVars) {
assert(!VarNames.empty() && "Cannot have no vars for this type!");
OS << Indent << " " << Type << " " << join(VarNames, ", ") << ";\n";
}
OS << Indent << "};\n"
<< Indent << "mutable " << MatchDataInfo::StructTypeName << " "
<< MatchDataInfo::StructName << ";\n\n";
}
GICombinerEmitter::GICombinerEmitter(RecordKeeper &RK,
const CodeGenTarget &Target,
StringRef Name, Record *Combiner)
: Records(RK), Name(Name), Target(Target), Combiner(Combiner) {}
MatchTable
GICombinerEmitter::buildMatchTable(MutableArrayRef<RuleMatcher> Rules) {
std::vector<Matcher *> InputRules;
for (Matcher &Rule : Rules)
InputRules.push_back(&Rule);
unsigned CurrentOrdering = 0;
StringMap<unsigned> OpcodeOrder;
for (RuleMatcher &Rule : Rules) {
const StringRef Opcode = Rule.getOpcode();
assert(!Opcode.empty() && "Didn't expect an undefined opcode");
if (OpcodeOrder.count(Opcode) == 0)
OpcodeOrder[Opcode] = CurrentOrdering++;
}
llvm::stable_sort(InputRules, [&OpcodeOrder](const Matcher *A,
const Matcher *B) {
auto *L = static_cast<const RuleMatcher *>(A);
auto *R = static_cast<const RuleMatcher *>(B);
return std::make_tuple(OpcodeOrder[L->getOpcode()], L->getNumOperands()) <
std::make_tuple(OpcodeOrder[R->getOpcode()], R->getNumOperands());
});
for (Matcher *Rule : InputRules)
Rule->optimize();
std::vector<std::unique_ptr<Matcher>> MatcherStorage;
std::vector<Matcher *> OptRules =
optimizeRules<GroupMatcher>(InputRules, MatcherStorage);
for (Matcher *Rule : OptRules)
Rule->optimize();
OptRules = optimizeRules<SwitchMatcher>(OptRules, MatcherStorage);
return MatchTable::buildTable(OptRules, /*WithCoverage*/ false,
/*IsCombiner*/ true);
}
/// Recurse into GICombineGroup's and flatten the ruleset into a simple list.
void GICombinerEmitter::gatherRules(
std::vector<RuleMatcher> &ActiveRules,
const std::vector<Record *> &&RulesAndGroups) {
for (Record *Rec : RulesAndGroups) {
if (Rec->isValueUnset("Rules")) {
AllCombineRules.emplace_back(NextRuleID, Rec->getName().str());
CombineRuleBuilder CRB(Target, SubtargetFeatures, *Rec, NextRuleID++,
ActiveRules);
if (!CRB.parseAll()) {
assert(ErrorsPrinted && "Parsing failed without errors!");
continue;
}
if (StopAfterParse) {
CRB.print(outs());
continue;
}
if (!CRB.emitRuleMatchers()) {
assert(ErrorsPrinted && "Emission failed without errors!");
continue;
}
} else
gatherRules(ActiveRules, Rec->getValueAsListOfDefs("Rules"));
}
}
void GICombinerEmitter::run(raw_ostream &OS) {
Records.startTimer("Gather rules");
std::vector<RuleMatcher> Rules;
gatherRules(Rules, Combiner->getValueAsListOfDefs("Rules"));
if (ErrorsPrinted)
PrintFatalError(Combiner->getLoc(), "Failed to parse one or more rules");
if (StopAfterParse)
return;
Records.startTimer("Creating Match Table");
unsigned MaxTemporaries = 0;
for (const auto &Rule : Rules)
MaxTemporaries = std::max(MaxTemporaries, Rule.countRendererFns());
const MatchTable Table = buildMatchTable(Rules);
Records.startTimer("Emit combiner");
emitSourceFileHeader(getClassName().str() + " Combiner Match Table", OS);
// Unused
std::vector<StringRef> CustomRendererFns;
// Unused
std::vector<Record *> ComplexPredicates;
SmallVector<LLTCodeGen, 16> TypeObjects;
append_range(TypeObjects, KnownTypes);
llvm::sort(TypeObjects);
// Hack: Avoid empty declarator.
if (TypeObjects.empty())
TypeObjects.push_back(LLT::scalar(1));
// GET_GICOMBINER_DEPS, which pulls in extra dependencies.
OS << "#ifdef GET_GICOMBINER_DEPS\n"
<< "#include \"llvm/ADT/SparseBitVector.h\"\n"
<< "namespace llvm {\n"
<< "extern cl::OptionCategory GICombinerOptionCategory;\n"
<< "} // end namespace llvm\n"
<< "#endif // ifdef GET_GICOMBINER_DEPS\n\n";
// GET_GICOMBINER_TYPES, which needs to be included before the declaration of
// the class.
OS << "#ifdef GET_GICOMBINER_TYPES\n";
emitRuleConfigImpl(OS);
OS << "#endif // ifdef GET_GICOMBINER_TYPES\n\n";
emitPredicateBitset(OS, "GET_GICOMBINER_TYPES");
// GET_GICOMBINER_CLASS_MEMBERS, which need to be included inside the class.
emitPredicatesDecl(OS, "GET_GICOMBINER_CLASS_MEMBERS");
emitTemporariesDecl(OS, "GET_GICOMBINER_CLASS_MEMBERS");
// GET_GICOMBINER_IMPL, which needs to be included outside the class.
emitExecutorImpl(OS, Table, TypeObjects, Rules, ComplexPredicates,
CustomRendererFns, "GET_GICOMBINER_IMPL");
// GET_GICOMBINER_CONSTRUCTOR_INITS, which are in the constructor's
// initializer list.
emitPredicatesInit(OS, "GET_GICOMBINER_CONSTRUCTOR_INITS");
emitTemporariesInit(OS, MaxTemporaries, "GET_GICOMBINER_CONSTRUCTOR_INITS");
}
} // end anonymous namespace
//===----------------------------------------------------------------------===//
static void EmitGICombiner(RecordKeeper &RK, raw_ostream &OS) {
EnablePrettyStackTrace();
CodeGenTarget Target(RK);
if (SelectedCombiners.empty())
PrintFatalError("No combiners selected with -combiners");
for (const auto &Combiner : SelectedCombiners) {
Record *CombinerDef = RK.getDef(Combiner);
if (!CombinerDef)
PrintFatalError("Could not find " + Combiner);
GICombinerEmitter(RK, Target, Combiner, CombinerDef).run(OS);
}
}
static TableGen::Emitter::Opt X("gen-global-isel-combiner", EmitGICombiner,
"Generate GlobalISel Combiner");