1584 lines
53 KiB
C++
1584 lines
53 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.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CodeGenInstruction.h"
|
|
#include "CodeGenTarget.h"
|
|
#include "GlobalISel/CodeExpander.h"
|
|
#include "GlobalISel/CodeExpansions.h"
|
|
#include "GlobalISel/CombinerUtils.h"
|
|
#include "GlobalISelMatchTable.h"
|
|
#include "GlobalISelMatchTableExecutorEmitter.h"
|
|
#include "SubtargetFeatureInfo.h"
|
|
#include "llvm/ADT/Hashing.h"
|
|
#include "llvm/ADT/MapVector.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
#include "llvm/ADT/StringSet.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/ScopedPrinter.h"
|
|
#include "llvm/TableGen/Error.h"
|
|
#include "llvm/TableGen/Record.h"
|
|
#include "llvm/TableGen/StringMatcher.h"
|
|
#include "llvm/TableGen/TableGenBackend.h"
|
|
#include <cstdint>
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::gi;
|
|
|
|
#define DEBUG_TYPE "gicombiner-matchtable-emitter"
|
|
|
|
extern cl::list<std::string> SelectedCombiners;
|
|
extern cl::opt<bool> StopAfterParse;
|
|
|
|
namespace {
|
|
constexpr StringLiteral CXXApplyPrefix = "GICXXCustomAction_CombineApply";
|
|
constexpr StringLiteral CXXPredPrefix = "GICXXPred_MI_Predicate_";
|
|
|
|
std::string getIsEnabledPredicateEnumName(unsigned CombinerRuleID) {
|
|
return "GICXXPred_Simple_IsRule" + to_string(CombinerRuleID) + "Enabled";
|
|
}
|
|
|
|
void declareInstExpansion(CodeExpansions &CE, const InstructionMatcher &IM,
|
|
StringRef Name) {
|
|
CE.declare(Name, "State.MIs[" + to_string(IM.getInsnVarID()) + "]");
|
|
}
|
|
|
|
void declareOperandExpansion(CodeExpansions &CE, const OperandMatcher &OM,
|
|
StringRef Name) {
|
|
CE.declare(Name, "State.MIs[" + to_string(OM.getInsnVarID()) +
|
|
"]->getOperand(" + to_string(OM.getOpIdx()) + ")");
|
|
}
|
|
|
|
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; });
|
|
}
|
|
|
|
//===- MatchData Handling -------------------------------------------------===//
|
|
|
|
/// Represents MatchData defined by the match stage and required by the apply
|
|
/// stage.
|
|
///
|
|
/// This allows the plumbing of arbitrary data from C++ predicates between the
|
|
/// stages.
|
|
///
|
|
/// When this class is initially created, it only has a pattern symbol and a
|
|
/// type. When all of the MatchDatas declarations of a given pattern have been
|
|
/// parsed, `AssignVariables` must be called to assign storage variable names to
|
|
/// each MatchDataInfo.
|
|
class MatchDataInfo {
|
|
StringRef PatternSymbol;
|
|
StringRef Type;
|
|
std::string VarName;
|
|
|
|
public:
|
|
static constexpr StringLiteral StructTypeName = "MatchInfosTy";
|
|
static constexpr StringLiteral StructName = "MatchInfos";
|
|
|
|
MatchDataInfo(StringRef PatternSymbol, StringRef Type)
|
|
: PatternSymbol(PatternSymbol), Type(Type.trim()) {}
|
|
|
|
StringRef getPatternSymbol() const { return PatternSymbol; };
|
|
StringRef getType() const { return Type; };
|
|
|
|
bool hasVariableName() const { return !VarName.empty(); }
|
|
void setVariableName(StringRef Name) { VarName = Name; }
|
|
StringRef getVariableName() const;
|
|
|
|
std::string getQualifiedVariableName() const {
|
|
return StructName.str() + "." + getVariableName().str();
|
|
}
|
|
|
|
void print(raw_ostream &OS) const;
|
|
void dump() const { print(dbgs()); }
|
|
};
|
|
|
|
StringRef MatchDataInfo::getVariableName() const {
|
|
assert(hasVariableName());
|
|
return VarName;
|
|
}
|
|
|
|
void MatchDataInfo::print(raw_ostream &OS) const {
|
|
OS << "(MatchDataInfo pattern_symbol:" << PatternSymbol << " type:'" << Type
|
|
<< "' var_name:" << (VarName.empty() ? "<unassigned>" : VarName) << ")";
|
|
}
|
|
|
|
/// Pool of type -> variables used to emit MatchData variables declarations.
|
|
///
|
|
/// e.g. if the map contains "int64_t" -> ["MD0", "MD1"], then two variable
|
|
/// declarations must be emitted: `int64_t MD0` and `int64_t MD1`.
|
|
///
|
|
/// This has a static lifetime and will outlive all the `MatchDataInfo` objects
|
|
/// by design. It needs to persist after all `CombineRuleBuilder` objects died
|
|
/// so we can emit the variable declarations.
|
|
StringMap<std::vector<std::string>> AllMatchDataVars;
|
|
|
|
// Assign variable names to all MatchDatas used by a pattern. This must be
|
|
// called after all MatchData decls have been parsed inside a rule.
|
|
//
|
|
// Requires an array of MatchDataInfo so we can handle cases where a pattern
|
|
// uses multiple instances of the same MatchData type.
|
|
void AssignMatchDataVariables(MutableArrayRef<MatchDataInfo> Infos) {
|
|
static unsigned NextVarID = 0;
|
|
|
|
StringMap<unsigned> SeenTypes;
|
|
for (auto &I : Infos) {
|
|
unsigned &NumSeen = SeenTypes[I.getType()];
|
|
auto &ExistingVars = AllMatchDataVars[I.getType()];
|
|
|
|
if (NumSeen == ExistingVars.size())
|
|
ExistingVars.push_back("MDInfo" + to_string(NextVarID++));
|
|
|
|
I.setVariableName(ExistingVars[NumSeen++]);
|
|
}
|
|
}
|
|
|
|
//===- C++ Predicates Handling --------------------------------------------===//
|
|
|
|
/// Entry into the static pool of all CXX Predicate code. This contains the
|
|
/// fully expanded C++ code.
|
|
///
|
|
/// Each CXXPattern creates a new entry in the pool to store its data, even
|
|
/// after the pattern is destroyed.
|
|
///
|
|
/// Note that CXXPattern trims C++ code, so the Code is already expected to be
|
|
/// free of leading/trailing whitespace.
|
|
struct CXXPredicateCode {
|
|
CXXPredicateCode(std::string Code, unsigned ID)
|
|
: Code(Code), ID(ID), BaseEnumName("GICombiner" + to_string(ID)) {
|
|
assert(StringRef(Code).trim() == Code &&
|
|
"Code was expected to be trimmed!");
|
|
}
|
|
|
|
const std::string Code;
|
|
const unsigned ID;
|
|
const std::string BaseEnumName;
|
|
|
|
bool needsUnreachable() const {
|
|
return !StringRef(Code).starts_with("return");
|
|
}
|
|
|
|
std::string getEnumNameWithPrefix(StringRef Prefix) const {
|
|
return Prefix.str() + BaseEnumName;
|
|
}
|
|
};
|
|
|
|
using CXXPredicateCodePool =
|
|
DenseMap<hash_code, std::unique_ptr<CXXPredicateCode>>;
|
|
CXXPredicateCodePool AllCXXMatchCode;
|
|
CXXPredicateCodePool AllCXXApplyCode;
|
|
|
|
/// Gets an instance of `CXXPredicateCode` for \p Code, or returns an already
|
|
/// existing one.
|
|
const CXXPredicateCode &getOrInsert(CXXPredicateCodePool &Pool,
|
|
std::string Code) {
|
|
// Check if we already have an identical piece of code, if not, create an
|
|
// entry in the pool.
|
|
const auto CodeHash = hash_value(Code);
|
|
if (auto It = Pool.find(CodeHash); It != Pool.end())
|
|
return *It->second;
|
|
|
|
const auto ID = Pool.size();
|
|
auto OwnedData = std::make_unique<CXXPredicateCode>(std::move(Code), ID);
|
|
const auto &DataRef = *OwnedData;
|
|
Pool[CodeHash] = std::move(OwnedData);
|
|
return DataRef;
|
|
}
|
|
|
|
/// Sorts a `CXXPredicateCodePool` by their IDs and returns it.
|
|
std::vector<const CXXPredicateCode *>
|
|
getSorted(const CXXPredicateCodePool &Pool) {
|
|
std::vector<const CXXPredicateCode *> Out;
|
|
std::transform(Pool.begin(), Pool.end(), std::back_inserter(Out),
|
|
[&](auto &Elt) { return Elt.second.get(); });
|
|
sort(Out, [](const auto *A, const auto *B) { return A->ID < B->ID; });
|
|
return Out;
|
|
}
|
|
|
|
//===- Pattern Base Class -------------------------------------------------===//
|
|
|
|
// An abstract pattern found in a combine rule. This can be an apply or match
|
|
// pattern.
|
|
class Pattern {
|
|
public:
|
|
enum {
|
|
K_AnyOpcode,
|
|
K_Inst,
|
|
K_CXX,
|
|
};
|
|
|
|
virtual ~Pattern() = default;
|
|
|
|
unsigned getKind() const { return Kind; }
|
|
const char *getKindName() const;
|
|
|
|
bool hasName() const { return !Name.empty(); }
|
|
StringRef getName() const { return Name; }
|
|
|
|
virtual void print(raw_ostream &OS, bool PrintName = true) const = 0;
|
|
void dump() const { return print(dbgs()); }
|
|
|
|
protected:
|
|
Pattern(unsigned Kind, StringRef Name) : Kind(Kind), Name(Name.str()) {
|
|
assert(!Name.empty() && "unnamed pattern!");
|
|
}
|
|
|
|
void printImpl(raw_ostream &OS, bool PrintName,
|
|
function_ref<void()> ContentPrinter) const;
|
|
|
|
private:
|
|
unsigned Kind;
|
|
|
|
// Note: if this ever changes to a StringRef (e.g. allocated in a pool or
|
|
// something), CombineRuleBuilder::verify() needs to be updated as well.
|
|
// It currently checks that the StringRef in the PatternMap references this.
|
|
std::string Name;
|
|
};
|
|
|
|
const char *Pattern::getKindName() const {
|
|
switch (Kind) {
|
|
case K_AnyOpcode:
|
|
return "AnyOpcodePattern";
|
|
case K_Inst:
|
|
return "InstructionPattern";
|
|
case K_CXX:
|
|
return "CXXPattern";
|
|
}
|
|
|
|
llvm_unreachable("unknown pattern kind!");
|
|
}
|
|
|
|
void Pattern::printImpl(raw_ostream &OS, bool PrintName,
|
|
function_ref<void()> ContentPrinter) const {
|
|
OS << "(" << getKindName() << " ";
|
|
if (PrintName)
|
|
OS << "name:" << getName() << " ";
|
|
ContentPrinter();
|
|
OS << ")";
|
|
}
|
|
|
|
//===- AnyOpcodePattern ---------------------------------------------------===//
|
|
|
|
/// `wip_match_opcode` patterns.
|
|
/// This matches one or more opcodes, and does not check any operands
|
|
/// whatsoever.
|
|
class AnyOpcodePattern : public Pattern {
|
|
public:
|
|
AnyOpcodePattern(StringRef Name) : Pattern(K_AnyOpcode, Name) {}
|
|
|
|
static bool classof(const Pattern *P) { return P->getKind() == K_AnyOpcode; }
|
|
|
|
void addOpcode(const CodeGenInstruction *I) { Insts.push_back(I); }
|
|
const auto &insts() const { return Insts; }
|
|
|
|
void print(raw_ostream &OS, bool PrintName = true) const override;
|
|
|
|
private:
|
|
SmallVector<const CodeGenInstruction *, 4> Insts;
|
|
};
|
|
|
|
void AnyOpcodePattern::print(raw_ostream &OS, bool PrintName) const {
|
|
printImpl(OS, PrintName, [&OS, this]() {
|
|
OS << "["
|
|
<< join(map_range(Insts,
|
|
[](const auto *I) { return I->TheDef->getName(); }),
|
|
", ")
|
|
<< "]";
|
|
});
|
|
}
|
|
|
|
//===- InstructionPattern -------------------------------------------------===//
|
|
|
|
/// Matches an instruction, e.g. `G_ADD $x, $y, $z`.
|
|
///
|
|
/// This pattern is simply CodeGenInstruction + a list of operands.
|
|
class InstructionPattern : public Pattern {
|
|
public:
|
|
struct Operand {
|
|
std::string Name;
|
|
bool IsDef = false;
|
|
};
|
|
|
|
InstructionPattern(const CodeGenInstruction &I, StringRef Name)
|
|
: Pattern(K_Inst, Name), I(I) {}
|
|
|
|
static bool classof(const Pattern *P) { return P->getKind() == K_Inst; }
|
|
|
|
const auto &operands() const { return Operands; }
|
|
void addOperand(StringRef Name);
|
|
unsigned getNumDefs() const { return I.Operands.NumDefs; }
|
|
|
|
const CodeGenInstruction &getInst() const { return I; }
|
|
StringRef getInstName() const { return I.TheDef->getName(); }
|
|
|
|
void reportUnreachable(ArrayRef<SMLoc> Locs) const;
|
|
bool checkSemantics(ArrayRef<SMLoc> Loc) const;
|
|
|
|
void print(raw_ostream &OS, bool PrintName = true) const override;
|
|
|
|
private:
|
|
const CodeGenInstruction &I;
|
|
SmallVector<Operand, 4> Operands;
|
|
};
|
|
|
|
void InstructionPattern::addOperand(StringRef Name) {
|
|
const bool IsDef = Operands.size() < getNumDefs();
|
|
Operands.emplace_back(Operand{Name.str(), IsDef});
|
|
}
|
|
|
|
void InstructionPattern::reportUnreachable(ArrayRef<SMLoc> Locs) const {
|
|
PrintError(Locs, "Instruction pattern '" + getName() +
|
|
"' is unreachable from the pattern root!");
|
|
}
|
|
|
|
bool InstructionPattern::checkSemantics(ArrayRef<SMLoc> Loc) const {
|
|
unsigned NumExpectedOperands = I.Operands.size();
|
|
if (NumExpectedOperands != Operands.size()) {
|
|
|
|
PrintError(Loc, "'" + getInstName() + "' expected " +
|
|
Twine(NumExpectedOperands) + " operands, got " +
|
|
Twine(Operands.size()));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void InstructionPattern::print(raw_ostream &OS, bool PrintName) const {
|
|
printImpl(OS, PrintName, [&OS, this]() {
|
|
OS << "inst:" << I.TheDef->getName() << " operands:["
|
|
<< join(map_range(Operands,
|
|
[](const auto &O) {
|
|
return (O.IsDef ? "<def>" : "") + O.Name;
|
|
}),
|
|
", ")
|
|
<< "]";
|
|
});
|
|
}
|
|
|
|
//===- CXXPattern ---------------------------------------------------------===//
|
|
|
|
/// Raw C++ code which may need some expansions.
|
|
///
|
|
/// e.g. [{ return isFooBux(${src}.getReg()); }]
|
|
///
|
|
/// For the expanded code, \see CXXPredicateCode. CXXPredicateCode objects are
|
|
/// created through `expandCode`.
|
|
///
|
|
/// \see CodeExpander and \see CodeExpansions for more information on code
|
|
/// expansions.
|
|
///
|
|
/// This object has two purposes:
|
|
/// - Represent C++ code as a pattern entry.
|
|
/// - Be a factory for expanded C++ code.
|
|
/// - It's immutable and only holds the raw code so we can expand the same
|
|
/// CXX pattern multiple times if we need to.
|
|
///
|
|
/// Note that the code is always trimmed in the constructor, so leading and
|
|
/// trailing whitespaces are removed. This removes bloat in the output, avoids
|
|
/// formatting issues, but also allows us to check things like
|
|
/// `.startswith("return")` trivially without worrying about spaces.
|
|
class CXXPattern : public Pattern {
|
|
public:
|
|
CXXPattern(const StringInit &Code, StringRef Name, bool IsApply)
|
|
: CXXPattern(Code.getAsUnquotedString(), Name, IsApply) {}
|
|
|
|
CXXPattern(StringRef Code, StringRef Name, bool IsApply)
|
|
: Pattern(K_CXX, Name), IsApply(IsApply), RawCode(Code.trim().str()) {}
|
|
|
|
static bool classof(const Pattern *P) { return P->getKind() == K_CXX; }
|
|
|
|
bool isApply() const { return IsApply; }
|
|
StringRef getRawCode() const { return RawCode; }
|
|
|
|
/// Expands raw code, replacing things such as `${foo}` with their
|
|
/// substitution in \p CE.
|
|
///
|
|
/// \param CE Map of Code Expansions
|
|
/// \param Locs SMLocs for the Code Expander, in case it needs to emit
|
|
/// diagnostics.
|
|
/// \return A CXXPredicateCode object that contains the expanded code. Note
|
|
/// that this may or may not insert a new object. All CXXPredicateCode objects
|
|
/// are held in a set to avoid emitting duplicate C++ code.
|
|
const CXXPredicateCode &expandCode(const CodeExpansions &CE,
|
|
ArrayRef<SMLoc> Locs) const;
|
|
|
|
void print(raw_ostream &OS, bool PrintName = true) const override;
|
|
|
|
private:
|
|
bool IsApply;
|
|
std::string RawCode;
|
|
};
|
|
|
|
const CXXPredicateCode &CXXPattern::expandCode(const CodeExpansions &CE,
|
|
ArrayRef<SMLoc> Locs) const {
|
|
std::string Result;
|
|
raw_string_ostream OS(Result);
|
|
CodeExpander Expander(RawCode, CE, Locs, /*ShowExpansions*/ false);
|
|
Expander.emit(OS);
|
|
return getOrInsert(IsApply ? AllCXXApplyCode : AllCXXMatchCode,
|
|
std::move(Result));
|
|
}
|
|
|
|
void CXXPattern::print(raw_ostream &OS, bool PrintName) const {
|
|
printImpl(OS, PrintName, [&OS, this] {
|
|
OS << (IsApply ? "apply" : "match") << " code:\"";
|
|
printEscapedString(getRawCode(), OS);
|
|
OS << "\"";
|
|
});
|
|
}
|
|
|
|
//===- CombineRuleBuilder -------------------------------------------------===//
|
|
|
|
/// Helper for CombineRuleBuilder.
|
|
///
|
|
/// Represents information about an operand.
|
|
/// Operands with no MatchPat are considered live-in to the pattern.
|
|
struct OperandTableEntry {
|
|
// The matcher pattern that defines this operand.
|
|
// null for live-ins.
|
|
InstructionPattern *MatchPat = nullptr;
|
|
// The apply pattern that (re)defines this operand.
|
|
// This can only be non-null if MatchPat is.
|
|
InstructionPattern *ApplyPat = nullptr;
|
|
|
|
bool isLiveIn() const { return !MatchPat; }
|
|
};
|
|
|
|
/// Parses combine rule and builds a small intermediate representation to tie
|
|
/// patterns together and emit RuleMatchers to match them. This may emit more
|
|
/// than one RuleMatcher, e.g. for `wip_match_opcode`.
|
|
///
|
|
/// Memory management for `Pattern` objects is done through `std::unique_ptr`.
|
|
/// In most cases, there are two stages to a pattern's lifetime:
|
|
/// - Creation in a `parse` function
|
|
/// - The unique_ptr is stored in a variable, and may be destroyed if the
|
|
/// pattern is found to be semantically invalid.
|
|
/// - Ownership transfer into a `PatternMap`
|
|
/// - Once a pattern is moved into either the map of Match or Apply
|
|
/// patterns, it is known to be valid and it never moves back.
|
|
class CombineRuleBuilder {
|
|
public:
|
|
using PatternMap = MapVector<StringRef, std::unique_ptr<Pattern>>;
|
|
|
|
CombineRuleBuilder(const CodeGenTarget &CGT,
|
|
SubtargetFeatureInfoMap &SubtargetFeatures,
|
|
Record &RuleDef, unsigned ID,
|
|
std::vector<RuleMatcher> &OutRMs)
|
|
: CGT(CGT), SubtargetFeatures(SubtargetFeatures), RuleDef(RuleDef),
|
|
RuleID(ID), OutRMs(OutRMs) {}
|
|
|
|
/// Parses all fields in the RuleDef record.
|
|
bool parseAll();
|
|
|
|
/// Emits all RuleMatchers into the vector of RuleMatchers passed in the
|
|
/// constructor.
|
|
bool emitRuleMatchers();
|
|
|
|
void print(raw_ostream &OS) const;
|
|
void dump() const { print(dbgs()); }
|
|
|
|
/// Debug-only verification of invariants.
|
|
void verify() const;
|
|
|
|
private:
|
|
void PrintError(Twine Msg) const { ::PrintError(RuleDef.getLoc(), Msg); }
|
|
|
|
/// Adds the expansions from \see MatchDatas to \p CE.
|
|
void declareAllMatchDatasExpansions(CodeExpansions &CE) const;
|
|
|
|
/// Adds \p P to \p IM, expanding its code using \p CE.
|
|
void addCXXPredicate(InstructionMatcher &IM, const CodeExpansions &CE,
|
|
const CXXPattern &P);
|
|
|
|
/// Generates a name for anonymous patterns.
|
|
///
|
|
/// e.g. (G_ADD $x, $y, $z):$foo is a pattern named "foo", but if ":$foo" is
|
|
/// absent, then the pattern is anonymous and this is used to assign it a
|
|
/// name.
|
|
std::string makeAnonPatName(StringRef Prefix) const;
|
|
mutable unsigned AnonIDCnt = 0;
|
|
|
|
/// Creates a new RuleMatcher with some boilerplate
|
|
/// settings/actions/predicates, and and adds it to \p OutRMs.
|
|
/// \see addFeaturePredicates too.
|
|
///
|
|
/// \param AdditionalComment Comment string to be added to the
|
|
/// `DebugCommentAction`.
|
|
RuleMatcher &addRuleMatcher(Twine AdditionalComment = "");
|
|
bool addFeaturePredicates(RuleMatcher &M);
|
|
|
|
bool findRoots();
|
|
bool buildOperandsTable();
|
|
|
|
bool parseDefs(DagInit &Def);
|
|
bool parseMatch(DagInit &Match);
|
|
bool parseApply(DagInit &Apply);
|
|
|
|
std::unique_ptr<Pattern> parseInstructionMatcher(const Init &Arg,
|
|
StringRef PatName);
|
|
std::unique_ptr<Pattern> parseWipMatchOpcodeMatcher(const Init &Arg,
|
|
StringRef PatName);
|
|
|
|
bool emitMatchPattern(CodeExpansions &CE, const InstructionPattern &IP);
|
|
bool emitMatchPattern(CodeExpansions &CE, const AnyOpcodePattern &AOP);
|
|
|
|
bool emitApplyPatterns(CodeExpansions &CE, RuleMatcher &M);
|
|
|
|
// Recursively visits InstructionPattern from P to build up the
|
|
// RuleMatcher/InstructionMatcher. May create new InstructionMatchers as
|
|
// needed.
|
|
bool emitInstructionMatchPattern(CodeExpansions &CE, RuleMatcher &M,
|
|
InstructionMatcher &IM,
|
|
const InstructionPattern &P,
|
|
DenseSet<const Pattern *> &SeenPats);
|
|
|
|
const CodeGenTarget &CGT;
|
|
SubtargetFeatureInfoMap &SubtargetFeatures;
|
|
Record &RuleDef;
|
|
const unsigned RuleID;
|
|
std::vector<RuleMatcher> &OutRMs;
|
|
|
|
// For InstructionMatcher::addOperand
|
|
unsigned AllocatedTemporariesBaseID = 0;
|
|
|
|
/// The root of the pattern.
|
|
StringRef RootName;
|
|
|
|
/// These maps have ownership of the actual Pattern objects.
|
|
/// They both map a Pattern's name to the Pattern instance.
|
|
PatternMap MatchPats;
|
|
PatternMap ApplyPats;
|
|
|
|
/// Set by findRoots.
|
|
Pattern *MatchRoot = nullptr;
|
|
|
|
MapVector<StringRef, OperandTableEntry> OperandTable;
|
|
SmallVector<MatchDataInfo, 2> MatchDatas;
|
|
};
|
|
|
|
bool CombineRuleBuilder::parseAll() {
|
|
if (!parseDefs(*RuleDef.getValueAsDag("Defs")))
|
|
return false;
|
|
if (!parseMatch(*RuleDef.getValueAsDag("Match")))
|
|
return false;
|
|
if (!parseApply(*RuleDef.getValueAsDag("Apply")))
|
|
return false;
|
|
if (!buildOperandsTable())
|
|
return false;
|
|
if (!findRoots())
|
|
return false;
|
|
LLVM_DEBUG(verify());
|
|
return true;
|
|
}
|
|
|
|
bool CombineRuleBuilder::emitRuleMatchers() {
|
|
assert(MatchRoot);
|
|
CodeExpansions CE;
|
|
declareAllMatchDatasExpansions(CE);
|
|
|
|
switch (MatchRoot->getKind()) {
|
|
case Pattern::K_AnyOpcode: {
|
|
if (!emitMatchPattern(CE, *cast<AnyOpcodePattern>(MatchRoot)))
|
|
return false;
|
|
break;
|
|
}
|
|
case Pattern::K_Inst:
|
|
if (!emitMatchPattern(CE, *cast<InstructionPattern>(MatchRoot)))
|
|
return false;
|
|
break;
|
|
case Pattern::K_CXX:
|
|
PrintError("C++ code cannot be the root of a pattern!");
|
|
return false;
|
|
default:
|
|
llvm_unreachable("unknown pattern kind!");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CombineRuleBuilder::print(raw_ostream &OS) const {
|
|
OS << "(CombineRule name:" << RuleDef.getName() << " id:" << RuleID
|
|
<< " root:" << RootName << "\n";
|
|
|
|
OS << " (MatchDatas ";
|
|
if (MatchDatas.empty())
|
|
OS << "<empty>)\n";
|
|
else {
|
|
OS << "\n";
|
|
for (const auto &MD : MatchDatas) {
|
|
OS << " ";
|
|
MD.print(OS);
|
|
OS << "\n";
|
|
}
|
|
OS << " )\n";
|
|
}
|
|
|
|
const auto DumpPats = [&](StringRef Name, const PatternMap &Pats) {
|
|
OS << " (" << Name << " ";
|
|
if (Pats.empty()) {
|
|
OS << "<empty>)\n";
|
|
return;
|
|
}
|
|
|
|
OS << "\n";
|
|
for (const auto &[Name, Pat] : Pats) {
|
|
OS << " ";
|
|
if (Pat.get() == MatchRoot)
|
|
OS << "<root>";
|
|
OS << Name << ":";
|
|
Pat->print(OS, /*PrintName=*/false);
|
|
OS << "\n";
|
|
}
|
|
OS << " )\n";
|
|
};
|
|
|
|
DumpPats("MatchPats", MatchPats);
|
|
DumpPats("ApplyPats", ApplyPats);
|
|
|
|
OS << " (OperandTable ";
|
|
if (OperandTable.empty())
|
|
OS << "<empty>)\n";
|
|
else {
|
|
OS << "\n";
|
|
for (const auto &[Key, Val] : OperandTable) {
|
|
OS << " [" << Key;
|
|
if (const auto *P = Val.MatchPat)
|
|
OS << " match_pat:" << P->getName();
|
|
if (const auto *P = Val.ApplyPat)
|
|
OS << " apply_pat:" << P->getName();
|
|
if (Val.isLiveIn())
|
|
OS << " live-in";
|
|
OS << "]\n";
|
|
}
|
|
OS << " )\n";
|
|
}
|
|
|
|
OS << ")\n";
|
|
}
|
|
|
|
void CombineRuleBuilder::verify() const {
|
|
const auto VerifyPats = [&](const PatternMap &Pats) {
|
|
for (const auto &[Name, Pat] : Pats) {
|
|
if (!Pat)
|
|
PrintFatalError("null pattern in pattern map!");
|
|
|
|
if (Name != Pat->getName()) {
|
|
Pat->dump();
|
|
PrintFatalError("Pattern name mismatch! Map name: " + Name +
|
|
", Pat name: " + Pat->getName());
|
|
}
|
|
|
|
// As an optimization, the PatternMaps don't re-allocate the PatternName
|
|
// string. They simply reference the std::string inside Pattern. Ensure
|
|
// this is the case to avoid memory issues.
|
|
if (Name.data() != Pat->getName().data()) {
|
|
dbgs() << "Map StringRef: '" << Name << "' @ "
|
|
<< (const void *)Name.data() << "\n";
|
|
dbgs() << "Pat String: '" << Pat->getName() << "' @ "
|
|
<< (const void *)Pat->getName().data() << "\n";
|
|
PrintFatalError("StringRef stored in the PatternMap is not referencing "
|
|
"the same string as its Pattern!");
|
|
}
|
|
}
|
|
};
|
|
|
|
VerifyPats(MatchPats);
|
|
VerifyPats(ApplyPats);
|
|
|
|
for (const auto &[Name, Op] : OperandTable) {
|
|
if (Op.ApplyPat && !Op.MatchPat) {
|
|
dump();
|
|
PrintFatalError("Operand " + Name +
|
|
" has an apply pattern, but no match pattern!");
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CombineRuleBuilder::addFeaturePredicates(RuleMatcher &M) {
|
|
if (!RuleDef.getValue("Predicates"))
|
|
return true;
|
|
|
|
ListInit *Preds = RuleDef.getValueAsListInit("Predicates");
|
|
for (Init *I : Preds->getValues()) {
|
|
if (DefInit *Pred = dyn_cast<DefInit>(I)) {
|
|
Record *Def = Pred->getDef();
|
|
if (!Def->isSubClassOf("Predicate")) {
|
|
::PrintError(Def->getLoc(), "Unknown 'Predicate' Type");
|
|
return false;
|
|
}
|
|
|
|
if (Def->getValueAsString("CondString").empty())
|
|
continue;
|
|
|
|
if (SubtargetFeatures.count(Def) == 0) {
|
|
SubtargetFeatures.emplace(
|
|
Def, SubtargetFeatureInfo(Def, SubtargetFeatures.size()));
|
|
}
|
|
|
|
M.addRequiredFeature(Def);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CombineRuleBuilder::declareAllMatchDatasExpansions(
|
|
CodeExpansions &CE) const {
|
|
for (const auto &MD : MatchDatas)
|
|
CE.declare(MD.getPatternSymbol(), MD.getQualifiedVariableName());
|
|
}
|
|
|
|
void CombineRuleBuilder::addCXXPredicate(InstructionMatcher &IM,
|
|
const CodeExpansions &CE,
|
|
const CXXPattern &P) {
|
|
const auto &ExpandedCode = P.expandCode(CE, RuleDef.getLoc());
|
|
IM.addPredicate<GenericInstructionPredicateMatcher>(
|
|
ExpandedCode.getEnumNameWithPrefix(CXXPredPrefix));
|
|
}
|
|
|
|
std::string CombineRuleBuilder::makeAnonPatName(StringRef Prefix) const {
|
|
return to_string("__anon_pat_" + Prefix + "_" + to_string(RuleID) + "_" +
|
|
to_string(AnonIDCnt++));
|
|
}
|
|
|
|
RuleMatcher &CombineRuleBuilder::addRuleMatcher(Twine AdditionalComment) {
|
|
auto &RM = OutRMs.emplace_back(RuleDef.getLoc());
|
|
addFeaturePredicates(RM);
|
|
RM.addRequiredSimplePredicate(getIsEnabledPredicateEnumName(RuleID));
|
|
const std::string AdditionalCommentStr = AdditionalComment.str();
|
|
RM.addAction<DebugCommentAction>(
|
|
"Combiner Rule #" + to_string(RuleID) + ": " + RuleDef.getName().str() +
|
|
(AdditionalCommentStr.empty() ? "" : "; " + AdditionalCommentStr));
|
|
return RM;
|
|
}
|
|
|
|
bool CombineRuleBuilder::findRoots() {
|
|
// Look by pattern name, e.g.
|
|
// (G_FNEG $x, $y):$root
|
|
if (auto It = MatchPats.find(RootName); It != MatchPats.end()) {
|
|
MatchRoot = It->second.get();
|
|
return true;
|
|
}
|
|
|
|
// Look by def:
|
|
// (G_FNEG $root, $y)
|
|
auto It = OperandTable.find(RootName);
|
|
if (It == OperandTable.end()) {
|
|
PrintError("Cannot find root '" + RootName + "' in match patterns!");
|
|
return false;
|
|
}
|
|
|
|
if (!It->second.MatchPat) {
|
|
PrintError("Cannot use live-in operand '" + RootName +
|
|
"' as match pattern root!");
|
|
return false;
|
|
}
|
|
|
|
MatchRoot = It->second.MatchPat;
|
|
return true;
|
|
}
|
|
|
|
bool CombineRuleBuilder::buildOperandsTable() {
|
|
// Walk each instruction pattern
|
|
for (auto &P : values(MatchPats)) {
|
|
auto *IP = dyn_cast<InstructionPattern>(P.get());
|
|
if (!IP)
|
|
continue;
|
|
for (const auto &Operand : IP->operands()) {
|
|
// Create an entry, no matter if it's a use or a def.
|
|
auto &Entry = OperandTable[Operand.Name];
|
|
|
|
// We only need to do additional checking on defs, though.
|
|
if (!Operand.IsDef)
|
|
continue;
|
|
|
|
if (Entry.MatchPat) {
|
|
PrintError("Operand '" + Operand.Name +
|
|
"' is defined multiple times in the 'match' patterns");
|
|
return false;
|
|
}
|
|
Entry.MatchPat = IP;
|
|
}
|
|
}
|
|
|
|
for (auto &P : values(ApplyPats)) {
|
|
auto *IP = dyn_cast<InstructionPattern>(P.get());
|
|
if (!IP)
|
|
continue;
|
|
for (const auto &Operand : IP->operands()) {
|
|
// Create an entry, no matter if it's a use or a def.
|
|
auto &Entry = OperandTable[Operand.Name];
|
|
|
|
// We only need to do additional checking on defs, though.
|
|
if (!Operand.IsDef)
|
|
continue;
|
|
|
|
if (!Entry.MatchPat) {
|
|
PrintError("Cannot define live-in operand '" + Operand.Name +
|
|
"' in the 'apply' pattern");
|
|
return false;
|
|
}
|
|
if (Entry.ApplyPat) {
|
|
PrintError("Operand '" + Operand.Name +
|
|
"' is defined multiple times in the 'apply' patterns");
|
|
return false;
|
|
}
|
|
Entry.ApplyPat = IP;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CombineRuleBuilder::parseDefs(DagInit &Def) {
|
|
if (Def.getOperatorAsDef(RuleDef.getLoc())->getName() != "defs") {
|
|
PrintError("Expected defs operator");
|
|
return false;
|
|
}
|
|
|
|
SmallVector<StringRef> Roots;
|
|
for (unsigned I = 0, E = Def.getNumArgs(); I < E; ++I) {
|
|
if (isSpecificDef(*Def.getArg(I), "root")) {
|
|
Roots.emplace_back(Def.getArgNameStr(I));
|
|
continue;
|
|
}
|
|
|
|
// Subclasses of GIDefMatchData should declare that this rule needs to pass
|
|
// data from the match stage to the apply stage, and ensure that the
|
|
// generated matcher has a suitable variable for it to do so.
|
|
if (Record *MatchDataRec =
|
|
getDefOfSubClass(*Def.getArg(I), "GIDefMatchData")) {
|
|
MatchDatas.emplace_back(Def.getArgNameStr(I),
|
|
MatchDataRec->getValueAsString("Type"));
|
|
continue;
|
|
}
|
|
|
|
// Otherwise emit an appropriate error message.
|
|
if (getDefOfSubClass(*Def.getArg(I), "GIDefKind"))
|
|
PrintError("This GIDefKind not implemented in tablegen");
|
|
else if (getDefOfSubClass(*Def.getArg(I), "GIDefKindWithArgs"))
|
|
PrintError("This GIDefKindWithArgs not implemented in tablegen");
|
|
else
|
|
PrintError("Expected a subclass of GIDefKind or a sub-dag whose "
|
|
"operator is of type GIDefKindWithArgs");
|
|
return false;
|
|
}
|
|
|
|
if (Roots.size() != 1) {
|
|
PrintError("Combine rules must have exactly one root");
|
|
return false;
|
|
}
|
|
|
|
RootName = Roots.front();
|
|
|
|
// Assign variables to all MatchDatas.
|
|
AssignMatchDataVariables(MatchDatas);
|
|
return true;
|
|
}
|
|
|
|
bool CombineRuleBuilder::parseMatch(DagInit &Match) {
|
|
if (Match.getOperatorAsDef(RuleDef.getLoc())->getName() != "match") {
|
|
PrintError("Expected match operator");
|
|
return false;
|
|
}
|
|
|
|
if (Match.getNumArgs() == 0) {
|
|
PrintError("Matcher is empty");
|
|
return false;
|
|
}
|
|
|
|
// The match section consists of a list of matchers and predicates. Parse each
|
|
// one and add the equivalent GIMatchDag nodes, predicates, and edges.
|
|
bool HasOpcodeMatcher = false;
|
|
for (unsigned I = 0; I < Match.getNumArgs(); ++I) {
|
|
Init *Arg = Match.getArg(I);
|
|
std::string Name = Match.getArgName(I)
|
|
? Match.getArgName(I)->getValue().str()
|
|
: makeAnonPatName("match");
|
|
|
|
if (MatchPats.contains(Name)) {
|
|
PrintError("'" + Name + "' match pattern defined more than once!");
|
|
return false;
|
|
}
|
|
|
|
if (auto Pat = parseInstructionMatcher(*Arg, Name)) {
|
|
MatchPats[Pat->getName()] = std::move(Pat);
|
|
continue;
|
|
}
|
|
|
|
if (auto Pat = parseWipMatchOpcodeMatcher(*Arg, Name)) {
|
|
if (HasOpcodeMatcher) {
|
|
PrintError("wip_opcode_match can only be present once");
|
|
return false;
|
|
}
|
|
HasOpcodeMatcher = true;
|
|
MatchPats[Pat->getName()] = std::move(Pat);
|
|
continue;
|
|
}
|
|
|
|
// Parse arbitrary C++ code
|
|
if (const auto *StringI = dyn_cast<StringInit>(Arg)) {
|
|
auto CXXPat =
|
|
std::make_unique<CXXPattern>(*StringI, Name, /*IsApply*/ false);
|
|
if (!CXXPat->getRawCode().contains("return ")) {
|
|
PrintWarning(RuleDef.getLoc(),
|
|
"'match' C++ code does not seem to return!");
|
|
}
|
|
MatchPats[CXXPat->getName()] = std::move(CXXPat);
|
|
continue;
|
|
}
|
|
|
|
// TODO: don't print this on, e.g. bad operand count in inst pat
|
|
PrintError("Expected a subclass of GIMatchKind or a sub-dag whose "
|
|
"operator is either of a GIMatchKindWithArgs or Instruction");
|
|
PrintNote("Pattern was `" + Arg->getAsString() + "'");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CombineRuleBuilder::parseApply(DagInit &Apply) {
|
|
// Currently we only support C++ :(
|
|
if (Apply.getOperatorAsDef(RuleDef.getLoc())->getName() != "apply") {
|
|
PrintError("Expected 'apply' operator in Apply DAG");
|
|
return false;
|
|
}
|
|
|
|
if (Apply.getNumArgs() != 1) {
|
|
PrintError("Expected exactly 1 argument in 'apply'");
|
|
return false;
|
|
}
|
|
|
|
const StringInit *Code = dyn_cast<StringInit>(Apply.getArg(0));
|
|
auto Pat = std::make_unique<CXXPattern>(*Code, makeAnonPatName("apply"),
|
|
/*IsApply*/ true);
|
|
ApplyPats[Pat->getName()] = std::move(Pat);
|
|
return true;
|
|
}
|
|
|
|
std::unique_ptr<Pattern>
|
|
CombineRuleBuilder::parseInstructionMatcher(const Init &Arg, StringRef Name) {
|
|
const DagInit *Matcher = getDagWithOperatorOfSubClass(Arg, "Instruction");
|
|
if (!Matcher)
|
|
return nullptr;
|
|
|
|
auto &Instr = CGT.getInstruction(Matcher->getOperatorAsDef(RuleDef.getLoc()));
|
|
auto Pat = std::make_unique<InstructionPattern>(Instr, Name);
|
|
|
|
for (const auto &NameInit : Matcher->getArgNames())
|
|
Pat->addOperand(NameInit->getAsUnquotedString());
|
|
|
|
if (!Pat->checkSemantics(RuleDef.getLoc()))
|
|
return nullptr;
|
|
|
|
return std::move(Pat);
|
|
}
|
|
|
|
std::unique_ptr<Pattern>
|
|
CombineRuleBuilder::parseWipMatchOpcodeMatcher(const Init &Arg,
|
|
StringRef Name) {
|
|
const DagInit *Matcher = getDagWithSpecificOperator(Arg, "wip_match_opcode");
|
|
if (!Matcher)
|
|
return nullptr;
|
|
|
|
if (Matcher->getNumArgs() == 0) {
|
|
PrintError("Empty wip_match_opcode");
|
|
return nullptr;
|
|
}
|
|
|
|
// Each argument is an opcode that can match.
|
|
auto Result = std::make_unique<AnyOpcodePattern>(Name);
|
|
for (const auto &Arg : Matcher->getArgs()) {
|
|
Record *OpcodeDef = getDefOfSubClass(*Arg, "Instruction");
|
|
if (OpcodeDef) {
|
|
Result->addOpcode(&CGT.getInstruction(OpcodeDef));
|
|
continue;
|
|
}
|
|
|
|
PrintError("Arguments to wip_match_opcode must be instructions");
|
|
return nullptr;
|
|
}
|
|
|
|
return std::move(Result);
|
|
}
|
|
|
|
bool CombineRuleBuilder::emitMatchPattern(CodeExpansions &CE,
|
|
const InstructionPattern &IP) {
|
|
auto &M = addRuleMatcher();
|
|
InstructionMatcher &IM = M.addInstructionMatcher("root");
|
|
declareInstExpansion(CE, IM, IP.getName());
|
|
|
|
DenseSet<const Pattern *> SeenPats;
|
|
if (!emitInstructionMatchPattern(CE, M, IM, IP, SeenPats))
|
|
return false;
|
|
|
|
// Emit remaining patterns
|
|
for (auto &Pat : 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_Inst:
|
|
cast<InstructionPattern>(Pat.get())->reportUnreachable(RuleDef.getLoc());
|
|
return false;
|
|
case Pattern::K_CXX: {
|
|
addCXXPredicate(IM, CE, *cast<CXXPattern>(Pat.get()));
|
|
continue;
|
|
}
|
|
default:
|
|
llvm_unreachable("unknown pattern kind!");
|
|
}
|
|
}
|
|
|
|
return emitApplyPatterns(CE, M);
|
|
}
|
|
|
|
bool CombineRuleBuilder::emitMatchPattern(CodeExpansions &CE,
|
|
const AnyOpcodePattern &AOP) {
|
|
|
|
for (const CodeGenInstruction *CGI : AOP.insts()) {
|
|
auto &M = addRuleMatcher("wip_match_opcode alternative '" +
|
|
CGI->TheDef->getName() + "'");
|
|
|
|
InstructionMatcher &IM = M.addInstructionMatcher(AOP.getName());
|
|
declareInstExpansion(CE, IM, AOP.getName());
|
|
// declareInstExpansion needs to be identical, otherwise we need to create a
|
|
// CodeExpansions object here instead.
|
|
assert(IM.getInsnVarID() == 0);
|
|
|
|
IM.addPredicate<InstructionOpcodeMatcher>(CGI);
|
|
|
|
// Emit remaining patterns.
|
|
for (auto &Pat : 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_Inst:
|
|
cast<InstructionPattern>(Pat.get())->reportUnreachable(
|
|
RuleDef.getLoc());
|
|
return false;
|
|
case Pattern::K_CXX: {
|
|
addCXXPredicate(IM, CE, *cast<CXXPattern>(Pat.get()));
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("unknown pattern kind!");
|
|
}
|
|
}
|
|
|
|
if (!emitApplyPatterns(CE, M))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CombineRuleBuilder::emitApplyPatterns(CodeExpansions &CE, RuleMatcher &M) {
|
|
for (auto &Pat : values(ApplyPats)) {
|
|
switch (Pat->getKind()) {
|
|
case Pattern::K_AnyOpcode:
|
|
case Pattern::K_Inst:
|
|
llvm_unreachable("Unsupported pattern kind in output pattern!");
|
|
case Pattern::K_CXX: {
|
|
CXXPattern *CXXPat = cast<CXXPattern>(Pat.get());
|
|
const auto &ExpandedCode = CXXPat->expandCode(CE, RuleDef.getLoc());
|
|
M.addAction<CustomCXXAction>(
|
|
ExpandedCode.getEnumNameWithPrefix(CXXApplyPrefix));
|
|
continue;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unknown pattern kind!");
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CombineRuleBuilder::emitInstructionMatchPattern(
|
|
CodeExpansions &CE, RuleMatcher &M, InstructionMatcher &IM,
|
|
const InstructionPattern &P, DenseSet<const Pattern *> &SeenPats) {
|
|
if (SeenPats.contains(&P))
|
|
return true;
|
|
|
|
SeenPats.insert(&P);
|
|
|
|
IM.addPredicate<InstructionOpcodeMatcher>(&P.getInst());
|
|
declareInstExpansion(CE, IM, P.getName());
|
|
|
|
unsigned OpIdx = 0;
|
|
for (auto &O : P.operands()) {
|
|
auto &OpTableEntry = OperandTable.find(O.Name)->second;
|
|
|
|
OperandMatcher &OM =
|
|
IM.addOperand(OpIdx++, O.Name, AllocatedTemporariesBaseID++);
|
|
declareOperandExpansion(CE, OM, O.Name);
|
|
|
|
if (O.IsDef)
|
|
continue;
|
|
|
|
if (InstructionPattern *DefPat = OpTableEntry.MatchPat) {
|
|
auto InstOpM = OM.addPredicate<InstructionOperandMatcher>(M, O.Name);
|
|
if (!InstOpM) {
|
|
// TODO: copy-pasted from GlobalISelEmitter.cpp. Is it still relevant
|
|
// here?
|
|
PrintError("Nested instruction '" + DefPat->getName() +
|
|
"' cannot be the same as another operand '" + O.Name + "'");
|
|
return false;
|
|
}
|
|
|
|
if (!emitInstructionMatchPattern(CE, M, (*InstOpM)->getInsnMatcher(),
|
|
*DefPat, SeenPats))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//===- GICombinerEmitter --------------------------------------------------===//
|
|
|
|
/// This class is essentially the driver. It fetches all TableGen records, calls
|
|
/// CombineRuleBuilder to build the MatchTable's RuleMatchers, then creates the
|
|
/// MatchTable & emits it. It also handles emitting all the supporting code such
|
|
/// as the list of LLTs, the CXXPredicates, etc.
|
|
class GICombinerEmitter final : public GlobalISelMatchTableExecutorEmitter {
|
|
RecordKeeper &Records;
|
|
StringRef Name;
|
|
const CodeGenTarget &Target;
|
|
Record *Combiner;
|
|
unsigned NextRuleID = 0;
|
|
|
|
// List all combine rules (ID, name) imported.
|
|
// Note that the combiner rule ID is different from the RuleMatcher ID. The
|
|
// latter is internal to the MatchTable, the former is the canonical ID of the
|
|
// combine rule used to disable/enable it.
|
|
std::vector<std::pair<unsigned, std::string>> AllCombineRules;
|
|
|
|
MatchTable buildMatchTable(MutableArrayRef<RuleMatcher> Rules);
|
|
|
|
void emitRuleConfigImpl(raw_ostream &OS);
|
|
|
|
void emitAdditionalImpl(raw_ostream &OS) override;
|
|
|
|
void emitMIPredicateFns(raw_ostream &OS) override;
|
|
void emitI64ImmPredicateFns(raw_ostream &OS) override;
|
|
void emitAPFloatImmPredicateFns(raw_ostream &OS) override;
|
|
void emitAPIntImmPredicateFns(raw_ostream &OS) override;
|
|
void emitTestSimplePredicate(raw_ostream &OS) override;
|
|
void emitRunCustomAction(raw_ostream &OS) override;
|
|
|
|
void emitAdditionalTemporariesDecl(raw_ostream &OS,
|
|
StringRef Indent) override;
|
|
|
|
const CodeGenTarget &getTarget() const override { return Target; }
|
|
StringRef getClassName() const override {
|
|
return Combiner->getValueAsString("Classname");
|
|
}
|
|
|
|
std::string getRuleConfigClassName() const {
|
|
return getClassName().str() + "RuleConfig";
|
|
}
|
|
|
|
void gatherRules(std::vector<RuleMatcher> &Rules,
|
|
const std::vector<Record *> &&RulesAndGroups);
|
|
|
|
public:
|
|
explicit GICombinerEmitter(RecordKeeper &RK, const CodeGenTarget &Target,
|
|
StringRef Name, Record *Combiner);
|
|
~GICombinerEmitter() {}
|
|
|
|
void run(raw_ostream &OS);
|
|
};
|
|
|
|
void GICombinerEmitter::emitRuleConfigImpl(raw_ostream &OS) {
|
|
OS << "struct " << getRuleConfigClassName() << " {\n"
|
|
<< " SparseBitVector<> DisabledRules;\n\n"
|
|
<< " bool isRuleEnabled(unsigned RuleID) const;\n"
|
|
<< " bool parseCommandLineOption();\n"
|
|
<< " bool setRuleEnabled(StringRef RuleIdentifier);\n"
|
|
<< " bool setRuleDisabled(StringRef RuleIdentifier);\n"
|
|
<< "};\n\n";
|
|
|
|
std::vector<std::pair<std::string, std::string>> Cases;
|
|
Cases.reserve(AllCombineRules.size());
|
|
|
|
for (const auto &[ID, Name] : AllCombineRules)
|
|
Cases.emplace_back(Name, "return " + to_string(ID) + ";\n");
|
|
|
|
OS << "static std::optional<uint64_t> getRuleIdxForIdentifier(StringRef "
|
|
"RuleIdentifier) {\n"
|
|
<< " uint64_t I;\n"
|
|
<< " // getAtInteger(...) returns false on success\n"
|
|
<< " bool Parsed = !RuleIdentifier.getAsInteger(0, I);\n"
|
|
<< " if (Parsed)\n"
|
|
<< " return I;\n\n"
|
|
<< "#ifndef NDEBUG\n";
|
|
StringMatcher Matcher("RuleIdentifier", Cases, OS);
|
|
Matcher.Emit();
|
|
OS << "#endif // ifndef NDEBUG\n\n"
|
|
<< " return std::nullopt;\n"
|
|
<< "}\n";
|
|
|
|
OS << "static std::optional<std::pair<uint64_t, uint64_t>> "
|
|
"getRuleRangeForIdentifier(StringRef RuleIdentifier) {\n"
|
|
<< " std::pair<StringRef, StringRef> RangePair = "
|
|
"RuleIdentifier.split('-');\n"
|
|
<< " if (!RangePair.second.empty()) {\n"
|
|
<< " const auto First = "
|
|
"getRuleIdxForIdentifier(RangePair.first);\n"
|
|
<< " const auto Last = "
|
|
"getRuleIdxForIdentifier(RangePair.second);\n"
|
|
<< " if (!First || !Last)\n"
|
|
<< " return std::nullopt;\n"
|
|
<< " if (First >= Last)\n"
|
|
<< " report_fatal_error(\"Beginning of range should be before "
|
|
"end of range\");\n"
|
|
<< " return {{*First, *Last + 1}};\n"
|
|
<< " }\n"
|
|
<< " if (RangePair.first == \"*\") {\n"
|
|
<< " return {{0, " << AllCombineRules.size() << "}};\n"
|
|
<< " }\n"
|
|
<< " const auto I = getRuleIdxForIdentifier(RangePair.first);\n"
|
|
<< " if (!I)\n"
|
|
<< " return std::nullopt;\n"
|
|
<< " return {{*I, *I + 1}};\n"
|
|
<< "}\n\n";
|
|
|
|
for (bool Enabled : {true, false}) {
|
|
OS << "bool " << getRuleConfigClassName() << "::setRule"
|
|
<< (Enabled ? "Enabled" : "Disabled") << "(StringRef RuleIdentifier) {\n"
|
|
<< " auto MaybeRange = getRuleRangeForIdentifier(RuleIdentifier);\n"
|
|
<< " if (!MaybeRange)\n"
|
|
<< " return false;\n"
|
|
<< " for (auto I = MaybeRange->first; I < MaybeRange->second; ++I)\n"
|
|
<< " DisabledRules." << (Enabled ? "reset" : "set") << "(I);\n"
|
|
<< " return true;\n"
|
|
<< "}\n\n";
|
|
}
|
|
|
|
OS << "static std::vector<std::string> " << Name << "Option;\n"
|
|
<< "static cl::list<std::string> " << Name << "DisableOption(\n"
|
|
<< " \"" << Name.lower() << "-disable-rule\",\n"
|
|
<< " cl::desc(\"Disable one or more combiner rules temporarily in "
|
|
<< "the " << Name << " pass\"),\n"
|
|
<< " cl::CommaSeparated,\n"
|
|
<< " cl::Hidden,\n"
|
|
<< " cl::cat(GICombinerOptionCategory),\n"
|
|
<< " cl::callback([](const std::string &Str) {\n"
|
|
<< " " << Name << "Option.push_back(Str);\n"
|
|
<< " }));\n"
|
|
<< "static cl::list<std::string> " << Name << "OnlyEnableOption(\n"
|
|
<< " \"" << Name.lower() << "-only-enable-rule\",\n"
|
|
<< " cl::desc(\"Disable all rules in the " << Name
|
|
<< " pass then re-enable the specified ones\"),\n"
|
|
<< " cl::Hidden,\n"
|
|
<< " cl::cat(GICombinerOptionCategory),\n"
|
|
<< " cl::callback([](const std::string &CommaSeparatedArg) {\n"
|
|
<< " StringRef Str = CommaSeparatedArg;\n"
|
|
<< " " << Name << "Option.push_back(\"*\");\n"
|
|
<< " do {\n"
|
|
<< " auto X = Str.split(\",\");\n"
|
|
<< " " << Name << "Option.push_back((\"!\" + X.first).str());\n"
|
|
<< " Str = X.second;\n"
|
|
<< " } while (!Str.empty());\n"
|
|
<< " }));\n"
|
|
<< "\n\n"
|
|
<< "bool " << getRuleConfigClassName()
|
|
<< "::isRuleEnabled(unsigned RuleID) const {\n"
|
|
<< " return !DisabledRules.test(RuleID);\n"
|
|
<< "}\n"
|
|
<< "bool " << getRuleConfigClassName() << "::parseCommandLineOption() {\n"
|
|
<< " for (StringRef Identifier : " << Name << "Option) {\n"
|
|
<< " bool Enabled = Identifier.consume_front(\"!\");\n"
|
|
<< " if (Enabled && !setRuleEnabled(Identifier))\n"
|
|
<< " return false;\n"
|
|
<< " if (!Enabled && !setRuleDisabled(Identifier))\n"
|
|
<< " return false;\n"
|
|
<< " }\n"
|
|
<< " return true;\n"
|
|
<< "}\n\n";
|
|
}
|
|
|
|
void GICombinerEmitter::emitAdditionalImpl(raw_ostream &OS) {
|
|
OS << "bool " << getClassName()
|
|
<< "::tryCombineAll(MachineInstr &I) const {\n"
|
|
<< " const TargetSubtargetInfo &ST = MF.getSubtarget();\n"
|
|
<< " const PredicateBitset AvailableFeatures = "
|
|
"getAvailableFeatures();\n"
|
|
<< " NewMIVector OutMIs;\n"
|
|
<< " State.MIs.clear();\n"
|
|
<< " State.MIs.push_back(&I);\n"
|
|
<< " " << MatchDataInfo::StructName << " = "
|
|
<< MatchDataInfo::StructTypeName << "();\n\n"
|
|
<< " if (executeMatchTable(*this, OutMIs, State, ExecInfo"
|
|
<< ", getMatchTable(), *ST.getInstrInfo(), MRI, "
|
|
"*MRI.getTargetRegisterInfo(), *ST.getRegBankInfo(), AvailableFeatures"
|
|
<< ", /*CoverageInfo*/ nullptr)) {\n"
|
|
<< " return true;\n"
|
|
<< " }\n\n"
|
|
<< " return false;\n"
|
|
<< "}\n\n";
|
|
}
|
|
|
|
void GICombinerEmitter::emitMIPredicateFns(raw_ostream &OS) {
|
|
auto MatchCode = getSorted(AllCXXMatchCode);
|
|
emitMIPredicateFnsImpl<const CXXPredicateCode *>(
|
|
OS, "", ArrayRef<const CXXPredicateCode *>(MatchCode),
|
|
[](const CXXPredicateCode *C) -> StringRef { return C->BaseEnumName; },
|
|
[](const CXXPredicateCode *C) -> StringRef { return C->Code; });
|
|
}
|
|
|
|
void GICombinerEmitter::emitI64ImmPredicateFns(raw_ostream &OS) {
|
|
// Unused, but still needs to be called.
|
|
emitImmPredicateFnsImpl<unsigned>(
|
|
OS, "I64", "int64_t", {}, [](unsigned) { return ""; },
|
|
[](unsigned) { return ""; });
|
|
}
|
|
|
|
void GICombinerEmitter::emitAPFloatImmPredicateFns(raw_ostream &OS) {
|
|
// Unused, but still needs to be called.
|
|
emitImmPredicateFnsImpl<unsigned>(
|
|
OS, "APFloat", "const APFloat &", {}, [](unsigned) { return ""; },
|
|
[](unsigned) { return ""; });
|
|
}
|
|
|
|
void GICombinerEmitter::emitAPIntImmPredicateFns(raw_ostream &OS) {
|
|
// Unused, but still needs to be called.
|
|
emitImmPredicateFnsImpl<unsigned>(
|
|
OS, "APInt", "const APInt &", {}, [](unsigned) { return ""; },
|
|
[](unsigned) { return ""; });
|
|
}
|
|
|
|
void GICombinerEmitter::emitTestSimplePredicate(raw_ostream &OS) {
|
|
if (!AllCombineRules.empty()) {
|
|
OS << "enum {\n";
|
|
std::string EnumeratorSeparator = " = GICXXPred_Invalid + 1,\n";
|
|
// To avoid emitting a switch, we expect that all those rules are in order.
|
|
// That way we can just get the RuleID from the enum by subtracting
|
|
// (GICXXPred_Invalid + 1).
|
|
unsigned ExpectedID = 0;
|
|
(void)ExpectedID;
|
|
for (const auto &ID : 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 = getSorted(AllCXXApplyCode);
|
|
|
|
if (!ApplyCode.empty()) {
|
|
OS << "enum {\n";
|
|
std::string EnumeratorSeparator = " = GICXXCustomAction_Invalid + 1,\n";
|
|
for (const auto &Apply : ApplyCode) {
|
|
OS << " " << Apply->getEnumNameWithPrefix(CXXApplyPrefix)
|
|
<< EnumeratorSeparator;
|
|
EnumeratorSeparator = ",\n";
|
|
}
|
|
OS << "};\n";
|
|
}
|
|
|
|
OS << "void " << getClassName()
|
|
<< "::runCustomAction(unsigned ApplyID, const MatcherState &State) const "
|
|
"{\n";
|
|
if (!ApplyCode.empty()) {
|
|
OS << " switch(ApplyID) {\n";
|
|
for (const auto &Apply : ApplyCode) {
|
|
OS << " case " << Apply->getEnumNameWithPrefix(CXXApplyPrefix) << ":{\n"
|
|
<< " " << Apply->Code << "\n"
|
|
<< " return;\n";
|
|
OS << " }\n";
|
|
}
|
|
OS << "}\n";
|
|
}
|
|
OS << " llvm_unreachable(\"Unknown Apply Action\");\n"
|
|
<< "}\n";
|
|
}
|
|
|
|
void GICombinerEmitter::emitAdditionalTemporariesDecl(raw_ostream &OS,
|
|
StringRef Indent) {
|
|
OS << Indent << "struct " << MatchDataInfo::StructTypeName << " {\n";
|
|
for (const auto &[Type, VarNames] : AllMatchDataVars) {
|
|
assert(!VarNames.empty() && "Cannot have no vars for this type!");
|
|
OS << Indent << " " << Type << " " << join(VarNames, ", ") << ";\n";
|
|
}
|
|
OS << Indent << "};\n"
|
|
<< Indent << "mutable " << MatchDataInfo::StructTypeName << " "
|
|
<< MatchDataInfo::StructName << ";\n\n";
|
|
}
|
|
|
|
GICombinerEmitter::GICombinerEmitter(RecordKeeper &RK,
|
|
const CodeGenTarget &Target,
|
|
StringRef Name, Record *Combiner)
|
|
: Records(RK), Name(Name), Target(Target), Combiner(Combiner) {}
|
|
|
|
MatchTable
|
|
GICombinerEmitter::buildMatchTable(MutableArrayRef<RuleMatcher> Rules) {
|
|
std::vector<Matcher *> InputRules;
|
|
for (Matcher &Rule : Rules)
|
|
InputRules.push_back(&Rule);
|
|
|
|
unsigned CurrentOrdering = 0;
|
|
StringMap<unsigned> OpcodeOrder;
|
|
for (RuleMatcher &Rule : Rules) {
|
|
const StringRef Opcode = Rule.getOpcode();
|
|
assert(!Opcode.empty() && "Didn't expect an undefined opcode");
|
|
if (OpcodeOrder.count(Opcode) == 0)
|
|
OpcodeOrder[Opcode] = CurrentOrdering++;
|
|
}
|
|
|
|
llvm::stable_sort(InputRules, [&OpcodeOrder](const Matcher *A,
|
|
const Matcher *B) {
|
|
auto *L = static_cast<const RuleMatcher *>(A);
|
|
auto *R = static_cast<const RuleMatcher *>(B);
|
|
return std::make_tuple(OpcodeOrder[L->getOpcode()], L->getNumOperands()) <
|
|
std::make_tuple(OpcodeOrder[R->getOpcode()], R->getNumOperands());
|
|
});
|
|
|
|
for (Matcher *Rule : InputRules)
|
|
Rule->optimize();
|
|
|
|
std::vector<std::unique_ptr<Matcher>> MatcherStorage;
|
|
std::vector<Matcher *> OptRules =
|
|
optimizeRules<GroupMatcher>(InputRules, MatcherStorage);
|
|
|
|
for (Matcher *Rule : OptRules)
|
|
Rule->optimize();
|
|
|
|
OptRules = optimizeRules<SwitchMatcher>(OptRules, MatcherStorage);
|
|
|
|
return MatchTable::buildTable(OptRules, /*WithCoverage*/ false,
|
|
/*IsCombiner*/ true);
|
|
}
|
|
|
|
/// Recurse into GICombineGroup's and flatten the ruleset into a simple list.
|
|
void GICombinerEmitter::gatherRules(
|
|
std::vector<RuleMatcher> &ActiveRules,
|
|
const std::vector<Record *> &&RulesAndGroups) {
|
|
for (Record *R : RulesAndGroups) {
|
|
if (R->isValueUnset("Rules")) {
|
|
AllCombineRules.emplace_back(NextRuleID, R->getName().str());
|
|
CombineRuleBuilder CRB(Target, SubtargetFeatures, *R, NextRuleID++,
|
|
ActiveRules);
|
|
|
|
if (!CRB.parseAll())
|
|
continue;
|
|
|
|
if (StopAfterParse) {
|
|
CRB.print(outs());
|
|
continue;
|
|
}
|
|
|
|
if (!CRB.emitRuleMatchers())
|
|
continue;
|
|
} else
|
|
gatherRules(ActiveRules, R->getValueAsListOfDefs("Rules"));
|
|
}
|
|
}
|
|
|
|
void GICombinerEmitter::run(raw_ostream &OS) {
|
|
Records.startTimer("Gather rules");
|
|
std::vector<RuleMatcher> Rules;
|
|
gatherRules(Rules, Combiner->getValueAsListOfDefs("Rules"));
|
|
if (ErrorsPrinted)
|
|
PrintFatalError(Combiner->getLoc(), "Failed to parse one or more rules");
|
|
|
|
Records.startTimer("Creating Match Table");
|
|
unsigned MaxTemporaries = 0;
|
|
for (const auto &Rule : Rules)
|
|
MaxTemporaries = std::max(MaxTemporaries, Rule.countRendererFns());
|
|
|
|
const MatchTable Table = buildMatchTable(Rules);
|
|
|
|
Records.startTimer("Emit combiner");
|
|
|
|
emitSourceFileHeader(getClassName().str() + " Combiner Match Table", OS);
|
|
|
|
// Unused
|
|
std::vector<StringRef> CustomRendererFns;
|
|
// Unused, but hack to avoid empty declarator
|
|
std::vector<LLTCodeGen> TypeObjects = {LLTCodeGen(LLT::scalar(1))};
|
|
// Unused
|
|
std::vector<Record *> ComplexPredicates;
|
|
|
|
// GET_GICOMBINER_DEPS, which pulls in extra dependencies.
|
|
OS << "#ifdef GET_GICOMBINER_DEPS\n"
|
|
<< "#include \"llvm/ADT/SparseBitVector.h\"\n"
|
|
<< "namespace llvm {\n"
|
|
<< "extern cl::OptionCategory GICombinerOptionCategory;\n"
|
|
<< "} // end namespace llvm\n"
|
|
<< "#endif // ifdef GET_GICOMBINER_DEPS\n\n";
|
|
|
|
// GET_GICOMBINER_TYPES, which needs to be included before the declaration of
|
|
// the class.
|
|
OS << "#ifdef GET_GICOMBINER_TYPES\n";
|
|
emitRuleConfigImpl(OS);
|
|
OS << "#endif // ifdef GET_GICOMBINER_TYPES\n\n";
|
|
emitPredicateBitset(OS, "GET_GICOMBINER_TYPES");
|
|
|
|
// GET_GICOMBINER_CLASS_MEMBERS, which need to be included inside the class.
|
|
emitPredicatesDecl(OS, "GET_GICOMBINER_CLASS_MEMBERS");
|
|
emitTemporariesDecl(OS, "GET_GICOMBINER_CLASS_MEMBERS");
|
|
|
|
// GET_GICOMBINER_IMPL, which needs to be included outside the class.
|
|
emitExecutorImpl(OS, Table, TypeObjects, Rules, ComplexPredicates,
|
|
CustomRendererFns, "GET_GICOMBINER_IMPL");
|
|
|
|
// GET_GICOMBINER_CONSTRUCTOR_INITS, which are in the constructor's
|
|
// initializer list.
|
|
emitPredicatesInit(OS, "GET_GICOMBINER_CONSTRUCTOR_INITS");
|
|
emitTemporariesInit(OS, MaxTemporaries, "GET_GICOMBINER_CONSTRUCTOR_INITS");
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static void EmitGICombiner(RecordKeeper &RK, raw_ostream &OS) {
|
|
CodeGenTarget Target(RK);
|
|
|
|
if (SelectedCombiners.empty())
|
|
PrintFatalError("No combiners selected with -combiners");
|
|
for (const auto &Combiner : SelectedCombiners) {
|
|
Record *CombinerDef = RK.getDef(Combiner);
|
|
if (!CombinerDef)
|
|
PrintFatalError("Could not find " + Combiner);
|
|
GICombinerEmitter(RK, Target, Combiner, CombinerDef).run(OS);
|
|
}
|
|
}
|
|
|
|
static TableGen::Emitter::Opt X("gen-global-isel-combiner-matchtable",
|
|
EmitGICombiner,
|
|
"Generate GlobalISel combiner Match Table");
|