Skip to content

Commit

Permalink
[globalisel] Re-factor ISel matchers into a hierarchy. NFC
Browse files Browse the repository at this point in the history
Summary:
This should make it possible to easily add everything needed to import all
the existing SelectionDAG rules. It should also serve the likely
kinds of GlobalISel rules (some of which are not currently representable
in SelectionDAG) once we've nailed down the tablegen definition for that.

The hierarchy is as follows:
  MatcherRule - A matching rule. Currently used to emit C++ ISel code but will
  |             also be used to emit test cases and tablegen definitions in the
  |             near future.
  |- Instruction(s) - Represents the instruction to be matched.
     |- Instruction Predicate(s) - Test the opcode, arithmetic flags, etc. of an
     |                             instruction.
     \- Operand(s) - Represents a particular operand of the instruction. In the
        |            future, there may be subclasses to test the same predicates
        |            on multiple operands (including for variadic instructions).
        \ Operand Predicate(s) - Test the type, register bank, etc. of an operand.
                                 This is where the ComplexPattern equivalent
                                 will be represented. It's also
                                 nested-instruction matching will live as a
                                 predicate that follows the DefUse chain to the
                                 Def and tests a MatcherRule from that position.

Support for multiple instruction matchers in a rule has been retained from
the existing code but has been adjusted to assert when it is used.
Previously it would silently drop all but the first instruction matcher.

The tablegen-erated file is not functionally changed but has more
parentheses and no longer attempts to format the if-statements since
keeping track of the indentation is tricky in the presence of the matcher
hierarchy. It would be nice to have CMakes tablegen() run the output
through clang-format (when available) so we don't have to complicate
TableGen with pretty-printing.

It's also worth mentioning that this hierarchy will also be able to emit
TableGen definitions and test cases in the near future. This is the reason
for favouring explicit emit*() calls rather than the << operator.

Reviewers: aditya_nandakumar, rovka, t.p.northover, qcolombet, ab

Reviewed By: ab

Subscribers: igorb, dberris, kristof.beyls, llvm-commits

Differential Revision: https://reviews.llvm.org/D28942

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@293172 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
dsandersllvm committed Jan 26, 2017
1 parent 9016ce4 commit 2b464c4
Showing 1 changed file with 226 additions and 48 deletions.
274 changes: 226 additions & 48 deletions utils/TableGen/GlobalISelEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,6 @@ static bool isTrivialOperatorNode(const TreePatternNode *N) {

//===- Matchers -----------------------------------------------------------===//

struct Matcher {
virtual ~Matcher() {}
virtual void emit(raw_ostream &OS) const = 0;
};

raw_ostream &operator<<(raw_ostream &S, const Matcher &M) {
M.emit(S);
return S;
}

struct MatchAction {
virtual ~MatchAction() {}
virtual void emit(raw_ostream &OS) const = 0;
Expand All @@ -130,46 +120,211 @@ raw_ostream &operator<<(raw_ostream &S, const MatchAction &A) {
return S;
}

struct MatchOpcode : public Matcher {
MatchOpcode(const CodeGenInstruction *I) : I(I) {}
const CodeGenInstruction *I;
template <class PredicateTy> class PredicateListMatcher {
private:
typedef std::vector<std::unique_ptr<PredicateTy>> PredicateVec;
PredicateVec Predicates;

virtual void emit(raw_ostream &OS) const {
OS << "I.getOpcode() == " << I->Namespace << "::" << I->TheDef->getName();
public:
/// Construct a new operand predicate and add it to the matcher.
template <class Kind, class... Args>
Kind &addPredicate(Args&&... args) {
Predicates.emplace_back(make_unique<Kind>(std::forward<Args...>(args)...));
return *static_cast<Kind *>(Predicates.back().get());
}

typename PredicateVec::const_iterator predicates_begin() const { return Predicates.begin(); }
typename PredicateVec::const_iterator predicates_end() const { return Predicates.end(); }
iterator_range<typename PredicateVec::const_iterator> predicates() const {
return make_range(predicates_begin(), predicates_end());
}

#if 0
/// Emit a C++ expression that tests whether all the predicates are met.
template <class... Args>
void emitCxxPredicatesExpr(raw_ostream &OS, Args&&... args) const {
if (Predicates.empty()) {
OS << "true";
return;
}

StringRef Separator = "";
for (const auto &Predicate : predicates()) {
OS << Separator << "(";
Predicate->emitCxxPredicateExpr(OS, std::forward<Args...>(args)...);
OS << ")";
Separator = " && ";
}
}
#else
/// Emit a C++ expression that tests whether all the predicates are met.
template <class Arg1>
void emitCxxPredicatesExpr(raw_ostream &OS, Arg1&& arg1) const {
if (Predicates.empty()) {
OS << "true";
return;
}

StringRef Separator = "";
for (const auto &Predicate : predicates()) {
OS << Separator << "(";
Predicate->emitCxxPredicateExpr(OS, std::forward<Arg1>(arg1));
OS << ")";
Separator = " && ";
}
}

template <class Arg1, class Arg2>
void emitCxxPredicatesExpr(raw_ostream &OS, Arg1&& arg1, Arg2&& arg2) const {
if (Predicates.empty()) {
OS << "true";
return;
}

StringRef Separator = "";
for (const auto &Predicate : predicates()) {
OS << Separator << "(";
Predicate->emitCxxPredicateExpr(OS, std::forward<Arg1>(arg1),
std::forward<Arg2>(arg2));
OS << ")";
Separator = " && ";
}
}
#endif
};

struct MatchRegOpType : public Matcher {
MatchRegOpType(unsigned OpIdx, std::string Ty)
: OpIdx(OpIdx), Ty(Ty) {}
unsigned OpIdx;
/// Generates code to check a predicate of an operand.
///
/// Typical predicates include:
/// * Operand is a particular register.
/// * Operand is assigned a particular register bank.
/// * Operand is an MBB.
class OperandPredicateMatcher {
public:
virtual ~OperandPredicateMatcher() {}

/// Emit a C++ expression that checks the predicate for the OpIdx operand of
/// the instruction given in InsnVarName.
virtual void emitCxxPredicateExpr(raw_ostream &OS,
const StringRef InsnVarName,
unsigned OpIdx) const = 0;
};

/// Generates code to check that an operand is a particular LLT.
class LLTOperandMatcher : public OperandPredicateMatcher {
protected:
std::string Ty;

virtual void emit(raw_ostream &OS) const {
OS << "MRI.getType(I.getOperand(" << OpIdx << ").getReg()) == (" << Ty
<< ")";
public:
LLTOperandMatcher(std::string Ty) : Ty(Ty) {}

void emitCxxPredicateExpr(raw_ostream &OS, const StringRef InsnVarName,
unsigned OpIdx) const override {
OS << "MRI.getType(" << InsnVarName << ".getOperand(" << OpIdx
<< ").getReg()) == (" << Ty << ")";
}
};

struct MatchRegOpBank : public Matcher {
MatchRegOpBank(unsigned OpIdx, const CodeGenRegisterClass &RC)
: OpIdx(OpIdx), RC(RC) {}
unsigned OpIdx;
/// Generates code to check that an operand is in a particular register bank.
class RegisterBankOperandMatcher : public OperandPredicateMatcher {
protected:
const CodeGenRegisterClass &RC;

virtual void emit(raw_ostream &OS) const {
public:
RegisterBankOperandMatcher(const CodeGenRegisterClass &RC) : RC(RC) {}

void emitCxxPredicateExpr(raw_ostream &OS, const StringRef InsnVarName,
unsigned OpIdx) const override {
OS << "(&RBI.getRegBankFromRegClass(" << RC.getQualifiedName()
<< "RegClass) == RBI.getRegBank(I.getOperand(" << OpIdx
<< ").getReg(), MRI, TRI))";
<< "RegClass) == RBI.getRegBank(" << InsnVarName << ".getOperand("
<< OpIdx << ").getReg(), MRI, TRI))";
}
};

struct MatchMBBOp : public Matcher {
MatchMBBOp(unsigned OpIdx) : OpIdx(OpIdx) {}
/// Generates code to check that an operand is a basic block.
class MBBOperandMatcher : public OperandPredicateMatcher {
public:
void emitCxxPredicateExpr(raw_ostream &OS, const StringRef InsnVarName,
unsigned OpIdx) const override {
OS << InsnVarName << ".getOperand(" << OpIdx << ").isMBB()";
}
};

/// Generates code to check that a set of predicates match for a particular
/// operand.
class OperandMatcher : public PredicateListMatcher<OperandPredicateMatcher> {
protected:
unsigned OpIdx;

virtual void emit(raw_ostream &OS) const {
OS << "I.getOperand(" << OpIdx << ").isMBB()";
public:
OperandMatcher(unsigned OpIdx) : OpIdx(OpIdx) {}

/// Emit a C++ expression that tests whether the instruction named in
/// InsnVarName matches all the predicate and all the operands.
void emitCxxPredicateExpr(raw_ostream &OS, const StringRef InsnVarName) const {
OS << "(";
emitCxxPredicatesExpr(OS, InsnVarName, OpIdx);
OS << ")";
}
};

/// Generates code to check a predicate on an instruction.
///
/// Typical predicates include:
/// * The opcode of the instruction is a particular value.
/// * The nsw/nuw flag is/isn't set.
class InstructionPredicateMatcher {
public:
virtual ~InstructionPredicateMatcher() {}

/// Emit a C++ expression that tests whether the instruction named in
/// InsnVarName matches the predicate.
virtual void emitCxxPredicateExpr(raw_ostream &OS,
const StringRef InsnVarName) const = 0;
};

/// Generates code to check the opcode of an instruction.
class InstructionOpcodeMatcher : public InstructionPredicateMatcher {
protected:
const CodeGenInstruction *I;

public:
InstructionOpcodeMatcher(const CodeGenInstruction *I) : I(I) {}

void emitCxxPredicateExpr(raw_ostream &OS,
const StringRef InsnVarName) const override {
OS << InsnVarName << ".getOpcode() == " << I->Namespace
<< "::" << I->TheDef->getName();
}
};

/// Generates code to check that a set of predicates and operands match for a
/// particular instruction.
///
/// Typical predicates include:
/// * Has a specific opcode.
/// * Has an nsw/nuw flag or doesn't.
class InstructionMatcher
: public PredicateListMatcher<InstructionPredicateMatcher> {
protected:
std::vector<OperandMatcher> Operands;

public:
/// Add an operand to the matcher.
OperandMatcher &addOperand(unsigned OpIdx) {
Operands.emplace_back(OpIdx);
return Operands.back();
}

/// Emit a C++ expression that tests whether the instruction named in
/// InsnVarName matches all the predicates and all the operands.
void emitCxxPredicateExpr(raw_ostream &OS, const StringRef InsnVarName) const {
emitCxxPredicatesExpr(OS, InsnVarName);
for (const auto &Operand : Operands) {
OS << " && (";
Operand.emitCxxPredicateExpr(OS, InsnVarName);
OS << ")";
}
}
};

Expand All @@ -183,14 +338,25 @@ struct MutateOpcode : public MatchAction {
}
};

class MatcherEmitter {
/// Generates code to check that a match rule matches.
///
/// This currently supports a single match position but could be extended to
/// support multiple positions to support div/rem fusion or load-multiple
/// instructions.
class RuleMatcher {
const PatternToMatch &P;

std::vector<std::unique_ptr<InstructionMatcher>> Matchers;

public:
std::vector<std::unique_ptr<Matcher>> Matchers;
std::vector<std::unique_ptr<MatchAction>> Actions;

MatcherEmitter(const PatternToMatch &P) : P(P) {}
RuleMatcher(const PatternToMatch &P) : P(P) {}

InstructionMatcher &addInstructionMatcher() {
Matchers.emplace_back(new InstructionMatcher());
return *Matchers.back();
}

void emit(raw_ostream &OS) {
if (Matchers.empty())
Expand All @@ -199,9 +365,18 @@ class MatcherEmitter {
OS << " // Src: " << *P.getSrcPattern() << "\n"
<< " // Dst: " << *P.getDstPattern() << "\n";

OS << " if ((" << *Matchers.front() << ")";
for (auto &MA : makeArrayRef(Matchers).drop_front())
OS << " &&\n (" << *MA << ")";
// The representation supports rules that require multiple roots such as:
// %ptr(p0) = ...
// %elt0(s32) = G_LOAD %ptr
// %1(p0) = G_ADD %ptr, 4
// %elt1(s32) = G_LOAD p0 %1
// which could be usefully folded into:
// %ptr(p0) = ...
// %elt0(s32), %elt1(s32) = TGT_LOAD_PAIR %ptr
// on some targets but we don't need to make use of that yet.
assert(Matchers.size() == 1 && "Cannot handle multi-root matchers yet");
OS << " if (";
Matchers.front()->emitCxxPredicateExpr(OS, "I");
OS << ") {\n";

for (auto &MA : Actions)
Expand Down Expand Up @@ -235,7 +410,7 @@ Optional<GlobalISelEmitter::SkipReason>
GlobalISelEmitter::runOnPattern(const PatternToMatch &P, raw_ostream &OS) {

// Keep track of the matchers and actions to emit.
MatcherEmitter M(P);
RuleMatcher M(P);

// First, analyze the whole pattern.
// If the entire pattern has a predicate (e.g., target features), ignore it.
Expand Down Expand Up @@ -268,7 +443,8 @@ GlobalISelEmitter::runOnPattern(const PatternToMatch &P, raw_ostream &OS) {
auto &SrcGI = *SrcGIOrNull;

// The operators look good: match the opcode and mutate it to the new one.
M.Matchers.emplace_back(new MatchOpcode(&SrcGI));
InstructionMatcher &InsnMatcher = M.addInstructionMatcher();
InsnMatcher.addPredicate<InstructionOpcodeMatcher>(&SrcGI);
M.Actions.emplace_back(new MutateOpcode(&DstI));

// Next, analyze the children, only accepting patterns that don't require
Expand All @@ -291,9 +467,10 @@ GlobalISelEmitter::runOnPattern(const PatternToMatch &P, raw_ostream &OS) {
if (!OpTyOrNone)
return SkipReason{"Dst operand has an unsupported type"};

M.Matchers.emplace_back(new MatchRegOpType(OpIdx, *OpTyOrNone));
M.Matchers.emplace_back(
new MatchRegOpBank(OpIdx, Target.getRegisterClass(DstIOpRec)));
OperandMatcher &OM = InsnMatcher.addOperand(OpIdx);
OM.addPredicate<LLTOperandMatcher>(*OpTyOrNone);
OM.addPredicate<RegisterBankOperandMatcher>(
Target.getRegisterClass(DstIOpRec));
++OpIdx;
}

Expand All @@ -316,7 +493,7 @@ GlobalISelEmitter::runOnPattern(const PatternToMatch &P, raw_ostream &OS) {
if (SrcChild->getOperator()->isSubClassOf("SDNode")) {
auto &ChildSDNI = CGP.getSDNodeInfo(SrcChild->getOperator());
if (ChildSDNI.getSDClassName() == "BasicBlockSDNode") {
M.Matchers.emplace_back(new MatchMBBOp(OpIdx++));
InsnMatcher.addOperand(OpIdx++).addPredicate<MBBOperandMatcher>();
continue;
}
}
Expand All @@ -341,9 +518,10 @@ GlobalISelEmitter::runOnPattern(const PatternToMatch &P, raw_ostream &OS) {
if (!OpTyOrNone)
return SkipReason{"Src operand has an unsupported type"};

M.Matchers.emplace_back(new MatchRegOpType(OpIdx, *OpTyOrNone));
M.Matchers.emplace_back(
new MatchRegOpBank(OpIdx, Target.getRegisterClass(ChildRec)));
OperandMatcher &OM = InsnMatcher.addOperand(OpIdx);
OM.addPredicate<LLTOperandMatcher>(*OpTyOrNone);
OM.addPredicate<RegisterBankOperandMatcher>(
Target.getRegisterClass(ChildRec));
++OpIdx;
}

Expand Down

0 comments on commit 2b464c4

Please sign in to comment.