123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056 |
- //===- GlobalCombinerEmitter.cpp - Generate a combiner --------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- /// \file Generate a combiner implementation for GlobalISel from a declarative
- /// syntax
- ///
- //===----------------------------------------------------------------------===//
- #include "CodeGenTarget.h"
- #include "GlobalISel/CodeExpander.h"
- #include "GlobalISel/CodeExpansions.h"
- #include "GlobalISel/GIMatchDag.h"
- #include "GlobalISel/GIMatchDagPredicate.h"
- #include "GlobalISel/GIMatchTree.h"
- #include "llvm/ADT/SmallSet.h"
- #include "llvm/ADT/Statistic.h"
- #include "llvm/ADT/StringSet.h"
- #include "llvm/Support/CommandLine.h"
- #include "llvm/Support/Debug.h"
- #include "llvm/Support/ScopedPrinter.h"
- #include "llvm/TableGen/Error.h"
- #include "llvm/TableGen/StringMatcher.h"
- #include "llvm/TableGen/TableGenBackend.h"
- #include <cstdint>
- using namespace llvm;
- #define DEBUG_TYPE "gicombiner-emitter"
- // FIXME: Use ALWAYS_ENABLED_STATISTIC once it's available.
- unsigned NumPatternTotal = 0;
- STATISTIC(NumPatternTotalStatistic, "Total number of patterns");
- cl::OptionCategory
- GICombinerEmitterCat("Options for -gen-global-isel-combiner");
- static cl::list<std::string>
- SelectedCombiners("combiners", cl::desc("Emit the specified combiners"),
- cl::cat(GICombinerEmitterCat), cl::CommaSeparated);
- static cl::opt<bool> ShowExpansions(
- "gicombiner-show-expansions",
- cl::desc("Use C++ comments to indicate occurence of code expansion"),
- cl::cat(GICombinerEmitterCat));
- static cl::opt<bool> StopAfterParse(
- "gicombiner-stop-after-parse",
- cl::desc("Stop processing after parsing rules and dump state"),
- cl::cat(GICombinerEmitterCat));
- static cl::opt<bool> StopAfterBuild(
- "gicombiner-stop-after-build",
- cl::desc("Stop processing after building the match tree"),
- cl::cat(GICombinerEmitterCat));
- namespace {
- typedef uint64_t RuleID;
- // We're going to be referencing the same small strings quite a lot for operand
- // names and the like. Make their lifetime management simple with a global
- // string table.
- StringSet<> StrTab;
- StringRef insertStrTab(StringRef S) {
- if (S.empty())
- return S;
- return StrTab.insert(S).first->first();
- }
- class format_partition_name {
- const GIMatchTree &Tree;
- unsigned Idx;
- public:
- format_partition_name(const GIMatchTree &Tree, unsigned Idx)
- : Tree(Tree), Idx(Idx) {}
- void print(raw_ostream &OS) const {
- Tree.getPartitioner()->emitPartitionName(OS, Idx);
- }
- };
- raw_ostream &operator<<(raw_ostream &OS, const format_partition_name &Fmt) {
- Fmt.print(OS);
- return OS;
- }
- /// Declares data that is passed from the match stage to the apply stage.
- class MatchDataInfo {
- /// The symbol used in the tablegen patterns
- StringRef PatternSymbol;
- /// The data type for the variable
- StringRef Type;
- /// The name of the variable as declared in the generated matcher.
- std::string VariableName;
- public:
- MatchDataInfo(StringRef PatternSymbol, StringRef Type, StringRef VariableName)
- : PatternSymbol(PatternSymbol), Type(Type), VariableName(VariableName) {}
- StringRef getPatternSymbol() const { return PatternSymbol; };
- StringRef getType() const { return Type; };
- StringRef getVariableName() const { return VariableName; };
- };
- class RootInfo {
- StringRef PatternSymbol;
- public:
- RootInfo(StringRef PatternSymbol) : PatternSymbol(PatternSymbol) {}
- StringRef getPatternSymbol() const { return PatternSymbol; }
- };
- class CombineRule {
- public:
- using const_matchdata_iterator = std::vector<MatchDataInfo>::const_iterator;
- struct VarInfo {
- const GIMatchDagInstr *N;
- const GIMatchDagOperand *Op;
- const DagInit *Matcher;
- public:
- VarInfo(const GIMatchDagInstr *N, const GIMatchDagOperand *Op,
- const DagInit *Matcher)
- : N(N), Op(Op), Matcher(Matcher) {}
- };
- protected:
- /// A unique ID for this rule
- /// ID's are used for debugging and run-time disabling of rules among other
- /// things.
- RuleID ID;
- /// A unique ID that can be used for anonymous objects belonging to this rule.
- /// Used to create unique names in makeNameForAnon*() without making tests
- /// overly fragile.
- unsigned UID = 0;
- /// The record defining this rule.
- const Record &TheDef;
- /// The roots of a match. These are the leaves of the DAG that are closest to
- /// the end of the function. I.e. the nodes that are encountered without
- /// following any edges of the DAG described by the pattern as we work our way
- /// from the bottom of the function to the top.
- std::vector<RootInfo> Roots;
- GIMatchDag MatchDag;
- /// A block of arbitrary C++ to finish testing the match.
- /// FIXME: This is a temporary measure until we have actual pattern matching
- const StringInit *MatchingFixupCode = nullptr;
- /// The MatchData defined by the match stage and required by the apply stage.
- /// This allows the plumbing of arbitrary data from C++ predicates between the
- /// stages.
- ///
- /// For example, suppose you have:
- /// %A = <some-constant-expr>
- /// %0 = G_ADD %1, %A
- /// you could define a GIMatchPredicate that walks %A, constant folds as much
- /// as possible and returns an APInt containing the discovered constant. You
- /// could then declare:
- /// def apint : GIDefMatchData<"APInt">;
- /// add it to the rule with:
- /// (defs root:$root, apint:$constant)
- /// evaluate it in the pattern with a C++ function that takes a
- /// MachineOperand& and an APInt& with:
- /// (match [{MIR %root = G_ADD %0, %A }],
- /// (constantfold operand:$A, apint:$constant))
- /// and finally use it in the apply stage with:
- /// (apply (create_operand
- /// [{ MachineOperand::CreateImm(${constant}.getZExtValue());
- /// ]}, apint:$constant),
- /// [{MIR %root = FOO %0, %constant }])
- std::vector<MatchDataInfo> MatchDataDecls;
- void declareMatchData(StringRef PatternSymbol, StringRef Type,
- StringRef VarName);
- bool parseInstructionMatcher(const CodeGenTarget &Target, StringInit *ArgName,
- const Init &Arg,
- StringMap<std::vector<VarInfo>> &NamedEdgeDefs,
- StringMap<std::vector<VarInfo>> &NamedEdgeUses);
- bool parseWipMatchOpcodeMatcher(const CodeGenTarget &Target,
- StringInit *ArgName, const Init &Arg);
- public:
- CombineRule(const CodeGenTarget &Target, GIMatchDagContext &Ctx, RuleID ID,
- const Record &R)
- : ID(ID), TheDef(R), MatchDag(Ctx) {}
- CombineRule(const CombineRule &) = delete;
- bool parseDefs();
- bool parseMatcher(const CodeGenTarget &Target);
- RuleID getID() const { return ID; }
- unsigned allocUID() { return UID++; }
- StringRef getName() const { return TheDef.getName(); }
- const Record &getDef() const { return TheDef; }
- const StringInit *getMatchingFixupCode() const { return MatchingFixupCode; }
- size_t getNumRoots() const { return Roots.size(); }
- GIMatchDag &getMatchDag() { return MatchDag; }
- const GIMatchDag &getMatchDag() const { return MatchDag; }
- using const_root_iterator = std::vector<RootInfo>::const_iterator;
- const_root_iterator roots_begin() const { return Roots.begin(); }
- const_root_iterator roots_end() const { return Roots.end(); }
- iterator_range<const_root_iterator> roots() const {
- return llvm::make_range(Roots.begin(), Roots.end());
- }
- iterator_range<const_matchdata_iterator> matchdata_decls() const {
- return make_range(MatchDataDecls.begin(), MatchDataDecls.end());
- }
- /// Export expansions for this rule
- void declareExpansions(CodeExpansions &Expansions) const {
- for (const auto &I : matchdata_decls())
- Expansions.declare(I.getPatternSymbol(), I.getVariableName());
- }
- /// The matcher will begin from the roots and will perform the match by
- /// traversing the edges to cover the whole DAG. This function reverses DAG
- /// edges such that everything is reachable from a root. This is part of the
- /// preparation work for flattening the DAG into a tree.
- void reorientToRoots() {
- SmallSet<const GIMatchDagInstr *, 5> Roots;
- SmallSet<const GIMatchDagInstr *, 5> Visited;
- SmallSet<GIMatchDagEdge *, 20> EdgesRemaining;
- for (auto &I : MatchDag.roots()) {
- Roots.insert(I);
- Visited.insert(I);
- }
- for (auto &I : MatchDag.edges())
- EdgesRemaining.insert(I);
- bool Progressed = false;
- SmallSet<GIMatchDagEdge *, 20> EdgesToRemove;
- while (!EdgesRemaining.empty()) {
- for (auto *EI : EdgesRemaining) {
- if (Visited.count(EI->getFromMI())) {
- if (Roots.count(EI->getToMI()))
- PrintError(TheDef.getLoc(), "One or more roots are unnecessary");
- Visited.insert(EI->getToMI());
- EdgesToRemove.insert(EI);
- Progressed = true;
- }
- }
- for (GIMatchDagEdge *ToRemove : EdgesToRemove)
- EdgesRemaining.erase(ToRemove);
- EdgesToRemove.clear();
- for (auto EI = EdgesRemaining.begin(), EE = EdgesRemaining.end();
- EI != EE; ++EI) {
- if (Visited.count((*EI)->getToMI())) {
- (*EI)->reverse();
- Visited.insert((*EI)->getToMI());
- EdgesToRemove.insert(*EI);
- Progressed = true;
- }
- for (GIMatchDagEdge *ToRemove : EdgesToRemove)
- EdgesRemaining.erase(ToRemove);
- EdgesToRemove.clear();
- }
- if (!Progressed) {
- LLVM_DEBUG(dbgs() << "No progress\n");
- return;
- }
- Progressed = false;
- }
- }
- };
- /// A convenience function to check that an Init refers to a specific def. This
- /// is primarily useful for testing for defs and similar in DagInit's since
- /// DagInit's support any type inside them.
- static bool isSpecificDef(const Init &N, StringRef Def) {
- if (const DefInit *OpI = dyn_cast<DefInit>(&N))
- if (OpI->getDef()->getName() == Def)
- return true;
- return false;
- }
- /// A convenience function to check that an Init refers to a def that is a
- /// subclass of the given class and coerce it to a def if it is. This is
- /// primarily useful for testing for subclasses of GIMatchKind and similar in
- /// DagInit's since DagInit's support any type inside them.
- static Record *getDefOfSubClass(const Init &N, StringRef Cls) {
- if (const DefInit *OpI = dyn_cast<DefInit>(&N))
- if (OpI->getDef()->isSubClassOf(Cls))
- return OpI->getDef();
- return nullptr;
- }
- /// A convenience function to check that an Init refers to a dag whose operator
- /// is a specific def and coerce it to a dag if it is. This is primarily useful
- /// for testing for subclasses of GIMatchKind and similar in DagInit's since
- /// DagInit's support any type inside them.
- static const DagInit *getDagWithSpecificOperator(const Init &N,
- StringRef Name) {
- if (const DagInit *I = dyn_cast<DagInit>(&N))
- if (I->getNumArgs() > 0)
- if (const DefInit *OpI = dyn_cast<DefInit>(I->getOperator()))
- if (OpI->getDef()->getName() == Name)
- return I;
- return nullptr;
- }
- /// A convenience function to check that an Init refers to a dag whose operator
- /// is a def that is a subclass of the given class and coerce it to a dag if it
- /// is. This is primarily useful for testing for subclasses of GIMatchKind and
- /// similar in DagInit's since DagInit's support any type inside them.
- static const DagInit *getDagWithOperatorOfSubClass(const Init &N,
- StringRef Cls) {
- if (const DagInit *I = dyn_cast<DagInit>(&N))
- if (I->getNumArgs() > 0)
- if (const DefInit *OpI = dyn_cast<DefInit>(I->getOperator()))
- if (OpI->getDef()->isSubClassOf(Cls))
- return I;
- return nullptr;
- }
- StringRef makeNameForAnonInstr(CombineRule &Rule) {
- return insertStrTab(to_string(
- format("__anon%" PRIu64 "_%u", Rule.getID(), Rule.allocUID())));
- }
- StringRef makeDebugName(CombineRule &Rule, StringRef Name) {
- return insertStrTab(Name.empty() ? makeNameForAnonInstr(Rule) : StringRef(Name));
- }
- StringRef makeNameForAnonPredicate(CombineRule &Rule) {
- return insertStrTab(to_string(
- format("__anonpred%" PRIu64 "_%u", Rule.getID(), Rule.allocUID())));
- }
- void CombineRule::declareMatchData(StringRef PatternSymbol, StringRef Type,
- StringRef VarName) {
- MatchDataDecls.emplace_back(PatternSymbol, Type, VarName);
- }
- bool CombineRule::parseDefs() {
- DagInit *Defs = TheDef.getValueAsDag("Defs");
- if (Defs->getOperatorAsDef(TheDef.getLoc())->getName() != "defs") {
- PrintError(TheDef.getLoc(), "Expected defs operator");
- return false;
- }
- for (unsigned I = 0, E = Defs->getNumArgs(); I < E; ++I) {
- // Roots should be collected into Roots
- if (isSpecificDef(*Defs->getArg(I), "root")) {
- Roots.emplace_back(Defs->getArgNameStr(I));
- continue;
- }
- // Subclasses of GIDefMatchData should declare that this rule needs to pass
- // data from the match stage to the apply stage, and ensure that the
- // generated matcher has a suitable variable for it to do so.
- if (Record *MatchDataRec =
- getDefOfSubClass(*Defs->getArg(I), "GIDefMatchData")) {
- declareMatchData(Defs->getArgNameStr(I),
- MatchDataRec->getValueAsString("Type"),
- llvm::to_string(llvm::format("MatchData%" PRIu64, ID)));
- continue;
- }
- // Otherwise emit an appropriate error message.
- if (getDefOfSubClass(*Defs->getArg(I), "GIDefKind"))
- PrintError(TheDef.getLoc(),
- "This GIDefKind not implemented in tablegen");
- else if (getDefOfSubClass(*Defs->getArg(I), "GIDefKindWithArgs"))
- PrintError(TheDef.getLoc(),
- "This GIDefKindWithArgs not implemented in tablegen");
- else
- PrintError(TheDef.getLoc(),
- "Expected a subclass of GIDefKind or a sub-dag whose "
- "operator is of type GIDefKindWithArgs");
- return false;
- }
- if (Roots.empty()) {
- PrintError(TheDef.getLoc(), "Combine rules must have at least one root");
- return false;
- }
- return true;
- }
- // Parse an (Instruction $a:Arg1, $b:Arg2, ...) matcher. Edges are formed
- // between matching operand names between different matchers.
- bool CombineRule::parseInstructionMatcher(
- const CodeGenTarget &Target, StringInit *ArgName, const Init &Arg,
- StringMap<std::vector<VarInfo>> &NamedEdgeDefs,
- StringMap<std::vector<VarInfo>> &NamedEdgeUses) {
- if (const DagInit *Matcher =
- getDagWithOperatorOfSubClass(Arg, "Instruction")) {
- auto &Instr =
- Target.getInstruction(Matcher->getOperatorAsDef(TheDef.getLoc()));
- StringRef Name = ArgName ? ArgName->getValue() : "";
- GIMatchDagInstr *N =
- MatchDag.addInstrNode(makeDebugName(*this, Name), insertStrTab(Name),
- MatchDag.getContext().makeOperandList(Instr));
- N->setOpcodeAnnotation(&Instr);
- const auto &P = MatchDag.addPredicateNode<GIMatchDagOpcodePredicate>(
- makeNameForAnonPredicate(*this), Instr);
- MatchDag.addPredicateDependency(N, nullptr, P, &P->getOperandInfo()["mi"]);
- unsigned OpIdx = 0;
- for (const auto &NameInit : Matcher->getArgNames()) {
- StringRef Name = insertStrTab(NameInit->getAsUnquotedString());
- if (Name.empty())
- continue;
- N->assignNameToOperand(OpIdx, Name);
- // Record the endpoints of any named edges. We'll add the cartesian
- // product of edges later.
- const auto &InstrOperand = N->getOperandInfo()[OpIdx];
- if (InstrOperand.isDef()) {
- NamedEdgeDefs.try_emplace(Name);
- NamedEdgeDefs[Name].emplace_back(N, &InstrOperand, Matcher);
- } else {
- NamedEdgeUses.try_emplace(Name);
- NamedEdgeUses[Name].emplace_back(N, &InstrOperand, Matcher);
- }
- if (InstrOperand.isDef()) {
- if (any_of(Roots, [&](const RootInfo &X) {
- return X.getPatternSymbol() == Name;
- })) {
- N->setMatchRoot();
- }
- }
- OpIdx++;
- }
- return true;
- }
- return false;
- }
- // Parse the wip_match_opcode placeholder that's temporarily present in lieu of
- // implementing macros or choices between two matchers.
- bool CombineRule::parseWipMatchOpcodeMatcher(const CodeGenTarget &Target,
- StringInit *ArgName,
- const Init &Arg) {
- if (const DagInit *Matcher =
- getDagWithSpecificOperator(Arg, "wip_match_opcode")) {
- StringRef Name = ArgName ? ArgName->getValue() : "";
- GIMatchDagInstr *N =
- MatchDag.addInstrNode(makeDebugName(*this, Name), insertStrTab(Name),
- MatchDag.getContext().makeEmptyOperandList());
- if (any_of(Roots, [&](const RootInfo &X) {
- return ArgName && X.getPatternSymbol() == ArgName->getValue();
- })) {
- N->setMatchRoot();
- }
- const auto &P = MatchDag.addPredicateNode<GIMatchDagOneOfOpcodesPredicate>(
- makeNameForAnonPredicate(*this));
- MatchDag.addPredicateDependency(N, nullptr, P, &P->getOperandInfo()["mi"]);
- // Each argument is an opcode that will pass this predicate. Add them all to
- // the predicate implementation
- for (const auto &Arg : Matcher->getArgs()) {
- Record *OpcodeDef = getDefOfSubClass(*Arg, "Instruction");
- if (OpcodeDef) {
- P->addOpcode(&Target.getInstruction(OpcodeDef));
- continue;
- }
- PrintError(TheDef.getLoc(),
- "Arguments to wip_match_opcode must be instructions");
- return false;
- }
- return true;
- }
- return false;
- }
- bool CombineRule::parseMatcher(const CodeGenTarget &Target) {
- StringMap<std::vector<VarInfo>> NamedEdgeDefs;
- StringMap<std::vector<VarInfo>> NamedEdgeUses;
- DagInit *Matchers = TheDef.getValueAsDag("Match");
- if (Matchers->getOperatorAsDef(TheDef.getLoc())->getName() != "match") {
- PrintError(TheDef.getLoc(), "Expected match operator");
- return false;
- }
- if (Matchers->getNumArgs() == 0) {
- PrintError(TheDef.getLoc(), "Matcher is empty");
- return false;
- }
- // The match section consists of a list of matchers and predicates. Parse each
- // one and add the equivalent GIMatchDag nodes, predicates, and edges.
- for (unsigned I = 0; I < Matchers->getNumArgs(); ++I) {
- if (parseInstructionMatcher(Target, Matchers->getArgName(I),
- *Matchers->getArg(I), NamedEdgeDefs,
- NamedEdgeUses))
- continue;
- if (parseWipMatchOpcodeMatcher(Target, Matchers->getArgName(I),
- *Matchers->getArg(I)))
- continue;
- // Parse arbitrary C++ code we have in lieu of supporting MIR matching
- if (const StringInit *StringI = dyn_cast<StringInit>(Matchers->getArg(I))) {
- assert(!MatchingFixupCode &&
- "Only one block of arbitrary code is currently permitted");
- MatchingFixupCode = StringI;
- MatchDag.setHasPostMatchPredicate(true);
- continue;
- }
- PrintError(TheDef.getLoc(),
- "Expected a subclass of GIMatchKind or a sub-dag whose "
- "operator is either of a GIMatchKindWithArgs or Instruction");
- PrintNote("Pattern was `" + Matchers->getArg(I)->getAsString() + "'");
- return false;
- }
- // Add the cartesian product of use -> def edges.
- bool FailedToAddEdges = false;
- for (const auto &NameAndDefs : NamedEdgeDefs) {
- if (NameAndDefs.getValue().size() > 1) {
- PrintError(TheDef.getLoc(),
- "Two different MachineInstrs cannot def the same vreg");
- for (const auto &NameAndDefOp : NameAndDefs.getValue())
- PrintNote("in " + to_string(*NameAndDefOp.N) + " created from " +
- to_string(*NameAndDefOp.Matcher) + "");
- FailedToAddEdges = true;
- }
- const auto &Uses = NamedEdgeUses[NameAndDefs.getKey()];
- for (const VarInfo &DefVar : NameAndDefs.getValue()) {
- for (const VarInfo &UseVar : Uses) {
- MatchDag.addEdge(insertStrTab(NameAndDefs.getKey()), UseVar.N, UseVar.Op,
- DefVar.N, DefVar.Op);
- }
- }
- }
- if (FailedToAddEdges)
- return false;
- // If a variable is referenced in multiple use contexts then we need a
- // predicate to confirm they are the same operand. We can elide this if it's
- // also referenced in a def context and we're traversing the def-use chain
- // from the def to the uses but we can't know which direction we're going
- // until after reorientToRoots().
- for (const auto &NameAndUses : NamedEdgeUses) {
- const auto &Uses = NameAndUses.getValue();
- if (Uses.size() > 1) {
- const auto &LeadingVar = Uses.front();
- for (const auto &Var : ArrayRef<VarInfo>(Uses).drop_front()) {
- // Add a predicate for each pair until we've covered the whole
- // equivalence set. We could test the whole set in a single predicate
- // but that means we can't test any equivalence until all the MO's are
- // available which can lead to wasted work matching the DAG when this
- // predicate can already be seen to have failed.
- //
- // We have a similar problem due to the need to wait for a particular MO
- // before being able to test any of them. However, that is mitigated by
- // the order in which we build the DAG. We build from the roots outwards
- // so by using the first recorded use in all the predicates, we are
- // making the dependency on one of the earliest visited references in
- // the DAG. It's not guaranteed once the generated matcher is optimized
- // (because the factoring the common portions of rules might change the
- // visit order) but this should mean that these predicates depend on the
- // first MO to become available.
- const auto &P = MatchDag.addPredicateNode<GIMatchDagSameMOPredicate>(
- makeNameForAnonPredicate(*this));
- MatchDag.addPredicateDependency(LeadingVar.N, LeadingVar.Op, P,
- &P->getOperandInfo()["mi0"]);
- MatchDag.addPredicateDependency(Var.N, Var.Op, P,
- &P->getOperandInfo()["mi1"]);
- }
- }
- }
- return true;
- }
- class GICombinerEmitter {
- RecordKeeper &Records;
- StringRef Name;
- const CodeGenTarget &Target;
- Record *Combiner;
- std::vector<std::unique_ptr<CombineRule>> Rules;
- GIMatchDagContext MatchDagCtx;
- std::unique_ptr<CombineRule> makeCombineRule(const Record &R);
- void gatherRules(std::vector<std::unique_ptr<CombineRule>> &ActiveRules,
- const std::vector<Record *> &&RulesAndGroups);
- public:
- explicit GICombinerEmitter(RecordKeeper &RK, const CodeGenTarget &Target,
- StringRef Name, Record *Combiner);
- ~GICombinerEmitter() {}
- StringRef getClassName() const {
- return Combiner->getValueAsString("Classname");
- }
- void run(raw_ostream &OS);
- /// Emit the name matcher (guarded by #ifndef NDEBUG) used to disable rules in
- /// response to the generated cl::opt.
- void emitNameMatcher(raw_ostream &OS) const;
- void generateCodeForTree(raw_ostream &OS, const GIMatchTree &Tree,
- StringRef Indent) const;
- };
- GICombinerEmitter::GICombinerEmitter(RecordKeeper &RK,
- const CodeGenTarget &Target,
- StringRef Name, Record *Combiner)
- : Records(RK), Name(Name), Target(Target), Combiner(Combiner) {}
- void GICombinerEmitter::emitNameMatcher(raw_ostream &OS) const {
- std::vector<std::pair<std::string, std::string>> Cases;
- Cases.reserve(Rules.size());
- for (const CombineRule &EnumeratedRule : make_pointee_range(Rules)) {
- std::string Code;
- raw_string_ostream SS(Code);
- SS << "return " << EnumeratedRule.getID() << ";\n";
- Cases.push_back(
- std::make_pair(std::string(EnumeratedRule.getName()), Code));
- }
- OS << "static 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 None;\n"
- << "}\n";
- }
- std::unique_ptr<CombineRule>
- GICombinerEmitter::makeCombineRule(const Record &TheDef) {
- std::unique_ptr<CombineRule> Rule =
- std::make_unique<CombineRule>(Target, MatchDagCtx, NumPatternTotal, TheDef);
- if (!Rule->parseDefs())
- return nullptr;
- if (!Rule->parseMatcher(Target))
- return nullptr;
- Rule->reorientToRoots();
- LLVM_DEBUG({
- dbgs() << "Parsed rule defs/match for '" << Rule->getName() << "'\n";
- Rule->getMatchDag().dump();
- Rule->getMatchDag().writeDOTGraph(dbgs(), Rule->getName());
- });
- if (StopAfterParse)
- return Rule;
- // For now, don't support traversing from def to use. We'll come back to
- // this later once we have the algorithm changes to support it.
- bool EmittedDefToUseError = false;
- for (const auto &E : Rule->getMatchDag().edges()) {
- if (E->isDefToUse()) {
- if (!EmittedDefToUseError) {
- PrintError(
- TheDef.getLoc(),
- "Generated state machine cannot lookup uses from a def (yet)");
- EmittedDefToUseError = true;
- }
- PrintNote("Node " + to_string(*E->getFromMI()));
- PrintNote("Node " + to_string(*E->getToMI()));
- PrintNote("Edge " + to_string(*E));
- }
- }
- if (EmittedDefToUseError)
- return nullptr;
- // For now, don't support multi-root rules. We'll come back to this later
- // once we have the algorithm changes to support it.
- if (Rule->getNumRoots() > 1) {
- PrintError(TheDef.getLoc(), "Multi-root matches are not supported (yet)");
- return nullptr;
- }
- return Rule;
- }
- /// Recurse into GICombineGroup's and flatten the ruleset into a simple list.
- void GICombinerEmitter::gatherRules(
- std::vector<std::unique_ptr<CombineRule>> &ActiveRules,
- const std::vector<Record *> &&RulesAndGroups) {
- for (Record *R : RulesAndGroups) {
- if (R->isValueUnset("Rules")) {
- std::unique_ptr<CombineRule> Rule = makeCombineRule(*R);
- if (Rule == nullptr) {
- PrintError(R->getLoc(), "Failed to parse rule");
- continue;
- }
- ActiveRules.emplace_back(std::move(Rule));
- ++NumPatternTotal;
- } else
- gatherRules(ActiveRules, R->getValueAsListOfDefs("Rules"));
- }
- }
- void GICombinerEmitter::generateCodeForTree(raw_ostream &OS,
- const GIMatchTree &Tree,
- StringRef Indent) const {
- if (Tree.getPartitioner() != nullptr) {
- Tree.getPartitioner()->generatePartitionSelectorCode(OS, Indent);
- for (const auto &EnumChildren : enumerate(Tree.children())) {
- OS << Indent << "if (Partition == " << EnumChildren.index() << " /* "
- << format_partition_name(Tree, EnumChildren.index()) << " */) {\n";
- generateCodeForTree(OS, EnumChildren.value(), (Indent + " ").str());
- OS << Indent << "}\n";
- }
- return;
- }
- bool AnyFullyTested = false;
- for (const auto &Leaf : Tree.possible_leaves()) {
- OS << Indent << "// Leaf name: " << Leaf.getName() << "\n";
- const CombineRule *Rule = Leaf.getTargetData<CombineRule>();
- const Record &RuleDef = Rule->getDef();
- OS << Indent << "// Rule: " << RuleDef.getName() << "\n"
- << Indent << "if (!RuleConfig->isRuleDisabled(" << Rule->getID()
- << ")) {\n";
- CodeExpansions Expansions;
- for (const auto &VarBinding : Leaf.var_bindings()) {
- if (VarBinding.isInstr())
- Expansions.declare(VarBinding.getName(),
- "MIs[" + to_string(VarBinding.getInstrID()) + "]");
- else
- Expansions.declare(VarBinding.getName(),
- "MIs[" + to_string(VarBinding.getInstrID()) +
- "]->getOperand(" +
- to_string(VarBinding.getOpIdx()) + ")");
- }
- Rule->declareExpansions(Expansions);
- DagInit *Applyer = RuleDef.getValueAsDag("Apply");
- if (Applyer->getOperatorAsDef(RuleDef.getLoc())->getName() !=
- "apply") {
- PrintError(RuleDef.getLoc(), "Expected 'apply' operator in Apply DAG");
- return;
- }
- OS << Indent << " if (1\n";
- // Attempt to emit code for any untested predicates left over. Note that
- // isFullyTested() will remain false even if we succeed here and therefore
- // combine rule elision will not be performed. This is because we do not
- // know if there's any connection between the predicates for each leaf and
- // therefore can't tell if one makes another unreachable. Ideally, the
- // partitioner(s) would be sufficiently complete to prevent us from having
- // untested predicates left over.
- for (const GIMatchDagPredicate *Predicate : Leaf.untested_predicates()) {
- if (Predicate->generateCheckCode(OS, (Indent + " ").str(),
- Expansions))
- continue;
- PrintError(RuleDef.getLoc(),
- "Unable to test predicate used in rule");
- PrintNote(SMLoc(),
- "This indicates an incomplete implementation in tablegen");
- Predicate->print(errs());
- errs() << "\n";
- OS << Indent
- << "llvm_unreachable(\"TableGen did not emit complete code for this "
- "path\");\n";
- break;
- }
- if (Rule->getMatchingFixupCode() &&
- !Rule->getMatchingFixupCode()->getValue().empty()) {
- // FIXME: Single-use lambda's like this are a serious compile-time
- // performance and memory issue. It's convenient for this early stage to
- // defer some work to successive patches but we need to eliminate this
- // before the ruleset grows to small-moderate size. Last time, it became
- // a big problem for low-mem systems around the 500 rule mark but by the
- // time we grow that large we should have merged the ISel match table
- // mechanism with the Combiner.
- OS << Indent << " && [&]() {\n"
- << Indent << " "
- << CodeExpander(Rule->getMatchingFixupCode()->getValue(), Expansions,
- RuleDef.getLoc(), ShowExpansions)
- << "\n"
- << Indent << " return true;\n"
- << Indent << " }()";
- }
- OS << ") {\n" << Indent << " ";
- if (const StringInit *Code = dyn_cast<StringInit>(Applyer->getArg(0))) {
- OS << CodeExpander(Code->getAsUnquotedString(), Expansions,
- RuleDef.getLoc(), ShowExpansions)
- << "\n"
- << Indent << " return true;\n"
- << Indent << " }\n";
- } else {
- PrintError(RuleDef.getLoc(), "Expected apply code block");
- return;
- }
- OS << Indent << "}\n";
- assert(Leaf.isFullyTraversed());
- // If we didn't have any predicates left over and we're not using the
- // trap-door we have to support arbitrary C++ code while we're migrating to
- // the declarative style then we know that subsequent leaves are
- // unreachable.
- if (Leaf.isFullyTested() &&
- (!Rule->getMatchingFixupCode() ||
- Rule->getMatchingFixupCode()->getValue().empty())) {
- AnyFullyTested = true;
- OS << Indent
- << "llvm_unreachable(\"Combine rule elision was incorrect\");\n"
- << Indent << "return false;\n";
- }
- }
- if (!AnyFullyTested)
- OS << Indent << "return false;\n";
- }
- static void emitAdditionalHelperMethodArguments(raw_ostream &OS,
- Record *Combiner) {
- for (Record *Arg : Combiner->getValueAsListOfDefs("AdditionalArguments"))
- OS << ",\n " << Arg->getValueAsString("Type")
- << Arg->getValueAsString("Name");
- }
- void GICombinerEmitter::run(raw_ostream &OS) {
- Records.startTimer("Gather rules");
- gatherRules(Rules, Combiner->getValueAsListOfDefs("Rules"));
- if (StopAfterParse) {
- MatchDagCtx.print(errs());
- PrintNote(Combiner->getLoc(),
- "Terminating due to -gicombiner-stop-after-parse");
- return;
- }
- if (ErrorsPrinted)
- PrintFatalError(Combiner->getLoc(), "Failed to parse one or more rules");
- LLVM_DEBUG(dbgs() << "Optimizing tree for " << Rules.size() << " rules\n");
- std::unique_ptr<GIMatchTree> Tree;
- Records.startTimer("Optimize combiner");
- {
- GIMatchTreeBuilder TreeBuilder(0);
- for (const auto &Rule : Rules) {
- bool HadARoot = false;
- for (const auto &Root : enumerate(Rule->getMatchDag().roots())) {
- TreeBuilder.addLeaf(Rule->getName(), Root.index(), Rule->getMatchDag(),
- Rule.get());
- HadARoot = true;
- }
- if (!HadARoot)
- PrintFatalError(Rule->getDef().getLoc(), "All rules must have a root");
- }
- Tree = TreeBuilder.run();
- }
- if (StopAfterBuild) {
- Tree->writeDOTGraph(outs());
- PrintNote(Combiner->getLoc(),
- "Terminating due to -gicombiner-stop-after-build");
- return;
- }
- Records.startTimer("Emit combiner");
- OS << "#ifdef " << Name.upper() << "_GENCOMBINERHELPER_DEPS\n"
- << "#include \"llvm/ADT/SparseBitVector.h\"\n"
- << "namespace llvm {\n"
- << "extern cl::OptionCategory GICombinerOptionCategory;\n"
- << "} // end namespace llvm\n"
- << "#endif // ifdef " << Name.upper() << "_GENCOMBINERHELPER_DEPS\n\n";
- OS << "#ifdef " << Name.upper() << "_GENCOMBINERHELPER_H\n"
- << "class " << getClassName() << "RuleConfig {\n"
- << " SparseBitVector<> DisabledRules;\n"
- << "\n"
- << "public:\n"
- << " bool parseCommandLineOption();\n"
- << " bool isRuleDisabled(unsigned ID) const;\n"
- << " bool setRuleEnabled(StringRef RuleIdentifier);\n"
- << " bool setRuleDisabled(StringRef RuleIdentifier);\n"
- << "};\n"
- << "\n"
- << "class " << getClassName();
- StringRef StateClass = Combiner->getValueAsString("StateClass");
- if (!StateClass.empty())
- OS << " : public " << StateClass;
- OS << " {\n"
- << " const " << getClassName() << "RuleConfig *RuleConfig;\n"
- << "\n"
- << "public:\n"
- << " template <typename... Args>" << getClassName() << "(const "
- << getClassName() << "RuleConfig &RuleConfig, Args &&... args) : ";
- if (!StateClass.empty())
- OS << StateClass << "(std::forward<Args>(args)...), ";
- OS << "RuleConfig(&RuleConfig) {}\n"
- << "\n"
- << " bool tryCombineAll(\n"
- << " GISelChangeObserver &Observer,\n"
- << " MachineInstr &MI,\n"
- << " MachineIRBuilder &B";
- emitAdditionalHelperMethodArguments(OS, Combiner);
- OS << ") const;\n";
- OS << "};\n\n";
- emitNameMatcher(OS);
- OS << "static 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.hasValue() || !Last.hasValue())\n"
- << " return None;\n"
- << " if (First >= Last)\n"
- << " report_fatal_error(\"Beginning of range should be before "
- "end of range\");\n"
- << " return {{*First, *Last + 1}};\n"
- << " } else if (RangePair.first == \"*\") {\n"
- << " return {{0, " << Rules.size() << "}};\n"
- << " } else {\n"
- << " const auto I = getRuleIdxForIdentifier(RangePair.first);\n"
- << " if (!I.hasValue())\n"
- << " return None;\n"
- << " return {{*I, *I + 1}};\n"
- << " }\n"
- << " return None;\n"
- << "}\n\n";
- for (bool Enabled : {true, false}) {
- OS << "bool " << getClassName() << "RuleConfig::setRule"
- << (Enabled ? "Enabled" : "Disabled") << "(StringRef RuleIdentifier) {\n"
- << " auto MaybeRange = getRuleRangeForIdentifier(RuleIdentifier);\n"
- << " if (!MaybeRange.hasValue())\n"
- << " return false;\n"
- << " for (auto I = MaybeRange->first; I < MaybeRange->second; ++I)\n"
- << " DisabledRules." << (Enabled ? "reset" : "set") << "(I);\n"
- << " return true;\n"
- << "}\n\n";
- }
- OS << "bool " << getClassName()
- << "RuleConfig::isRuleDisabled(unsigned RuleID) const {\n"
- << " return DisabledRules.test(RuleID);\n"
- << "}\n";
- OS << "#endif // ifdef " << Name.upper() << "_GENCOMBINERHELPER_H\n\n";
- OS << "#ifdef " << Name.upper() << "_GENCOMBINERHELPER_CPP\n"
- << "\n"
- << "std::vector<std::string> " << Name << "Option;\n"
- << "cl::list<std::string> " << Name << "DisableOption(\n"
- << " \"" << Name.lower() << "-disable-rule\",\n"
- << " cl::desc(\"Disable one or more combiner rules temporarily in "
- << "the " << Name << " pass\"),\n"
- << " cl::CommaSeparated,\n"
- << " cl::Hidden,\n"
- << " cl::cat(GICombinerOptionCategory),\n"
- << " cl::callback([](const std::string &Str) {\n"
- << " " << Name << "Option.push_back(Str);\n"
- << " }));\n"
- << "cl::list<std::string> " << Name << "OnlyEnableOption(\n"
- << " \"" << Name.lower() << "-only-enable-rule\",\n"
- << " cl::desc(\"Disable all rules in the " << Name
- << " pass then re-enable the specified ones\"),\n"
- << " cl::Hidden,\n"
- << " cl::cat(GICombinerOptionCategory),\n"
- << " cl::callback([](const std::string &CommaSeparatedArg) {\n"
- << " StringRef Str = CommaSeparatedArg;\n"
- << " " << Name << "Option.push_back(\"*\");\n"
- << " do {\n"
- << " auto X = Str.split(\",\");\n"
- << " " << Name << "Option.push_back((\"!\" + X.first).str());\n"
- << " Str = X.second;\n"
- << " } while (!Str.empty());\n"
- << " }));\n"
- << "\n"
- << "bool " << getClassName() << "RuleConfig::parseCommandLineOption() {\n"
- << " for (StringRef Identifier : " << Name << "Option) {\n"
- << " bool Enabled = Identifier.consume_front(\"!\");\n"
- << " if (Enabled && !setRuleEnabled(Identifier))\n"
- << " return false;\n"
- << " if (!Enabled && !setRuleDisabled(Identifier))\n"
- << " return false;\n"
- << " }\n"
- << " return true;\n"
- << "}\n\n";
- OS << "bool " << getClassName() << "::tryCombineAll(\n"
- << " GISelChangeObserver &Observer,\n"
- << " MachineInstr &MI,\n"
- << " MachineIRBuilder &B";
- emitAdditionalHelperMethodArguments(OS, Combiner);
- OS << ") const {\n"
- << " MachineBasicBlock *MBB = MI.getParent();\n"
- << " MachineFunction *MF = MBB->getParent();\n"
- << " MachineRegisterInfo &MRI = MF->getRegInfo();\n"
- << " SmallVector<MachineInstr *, 8> MIs = {&MI};\n\n"
- << " (void)MBB; (void)MF; (void)MRI; (void)RuleConfig;\n\n";
- OS << " // Match data\n";
- for (const auto &Rule : Rules)
- for (const auto &I : Rule->matchdata_decls())
- OS << " " << I.getType() << " " << I.getVariableName() << ";\n";
- OS << "\n";
- OS << " int Partition = -1;\n";
- generateCodeForTree(OS, *Tree, " ");
- OS << "\n return false;\n"
- << "}\n"
- << "#endif // ifdef " << Name.upper() << "_GENCOMBINERHELPER_CPP\n";
- }
- } // end anonymous namespace
- //===----------------------------------------------------------------------===//
- namespace llvm {
- void EmitGICombiner(RecordKeeper &RK, raw_ostream &OS) {
- CodeGenTarget Target(RK);
- emitSourceFileHeader("Global Combiner", OS);
- if (SelectedCombiners.empty())
- PrintFatalError("No combiners selected with -combiners");
- for (const auto &Combiner : SelectedCombiners) {
- Record *CombinerDef = RK.getDef(Combiner);
- if (!CombinerDef)
- PrintFatalError("Could not find " + Combiner);
- GICombinerEmitter(RK, Target, Combiner, CombinerDef).run(OS);
- }
- NumPatternTotalStatistic = NumPatternTotal;
- }
- } // namespace llvm
|