[tablegen][globalisel] Capture instructions into locals and related infrastructure for multiple instructions matches.

Summary:
Prepare the way for nested instruction matching support by having actions
like CopyRenderer look up operands in the RuleMatcher rather than a
specific InstructionMatcher. This allows actions to reference any operand
from any matched instruction.

It works by checking the 'shape' of the match and capturing
each matched instruction to a local variable. If the shape is wrong
(not enough operands, leaf nodes where non-leafs are expected, etc.), then
the rule exits early without checking the predicates. Once we've captured
the instructions, we then test the predicates as before (except using the
local variables). If the match is successful, then we render the new
instruction as before using the local variables.

It's not noticable in this patch but by the time we support multiple
instruction matching, this patch will also cause a significant improvement
to readability of the emitted code since
MRI.getVRegDef(I->getOperand(0).getReg()) will simply be MI1 after
emitCxxCaptureStmts().

This isn't quite NFC because I've also fixed a bug that I'm surprised we
haven't encountered yet. It now checks there are at least the expected
number of operands before accessing them with getOperand().

Depends on D30531

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

Reviewed By: rovka

Subscribers: dberris, kristof.beyls, llvm-commits

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

llvm-svn: 298257
This commit is contained in:
Daniel Sanders 2017-03-20 15:20:42 +00:00
parent 8fd7a07447
commit 8a3c0788e9
2 changed files with 208 additions and 120 deletions

View File

@ -26,43 +26,55 @@ class I<dag OOps, dag IOps, list<dag> Pat>
//===- Test a simple pattern with regclass operands. ----------------------===//
// CHECK-LABEL: if ((I.getOpcode() == TargetOpcode::G_ADD) &&
// CHECK-NEXT: ((/* dst */ (MRI.getType(I.getOperand(0).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(I.getOperand(0).getReg(), MRI, TRI))))) &&
// CHECK-NEXT: ((/* src1 */ (MRI.getType(I.getOperand(1).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(I.getOperand(1).getReg(), MRI, TRI))))) &&
// CHECK-NEXT: ((/* src2 */ (MRI.getType(I.getOperand(2).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(I.getOperand(2).getReg(), MRI, TRI)))))) {
// CHECK-LABEL: if ([&]() {
// CHECK-NEXT: MachineInstr &MI0 = I;
// CHECK-NEXT: if (MI0.getNumOperands() < 3)
// CHECK-NEXT: return false;
// CHECK-NEXT: if ((MI0.getOpcode() == TargetOpcode::G_ADD) &&
// CHECK-NEXT: ((/* dst */ (MRI.getType(MI0.getOperand(0).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(0).getReg(), MRI, TRI))))) &&
// CHECK-NEXT: ((/* src1 */ (MRI.getType(MI0.getOperand(1).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(1).getReg(), MRI, TRI))))) &&
// CHECK-NEXT: ((/* src2 */ (MRI.getType(MI0.getOperand(2).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(2).getReg(), MRI, TRI)))))) {
// CHECK-NEXT: // (add:i32 GPR32:i32:$src1, GPR32:i32:$src2) => (ADD:i32 GPR32:i32:$src1, GPR32:i32:$src2)
// CHECK-NEXT: I.setDesc(TII.get(MyTarget::ADD));
// CHECK-NEXT: MachineInstr &NewI = I;
// CHECK-NEXT: constrainSelectedInstRegOperands(NewI, TII, TRI, RBI);
// CHECK-NEXT: return true;
// CHECK-NEXT: }
// CHECK-NEXT: return false;
// CHECK-NEXT: }()) { return true; }
// CHECK-NEXT: // (add:i32 GPR32:i32:$src1, GPR32:i32:$src2) => (ADD:i32 GPR32:i32:$src1, GPR32:i32:$src2)
// CHECK-NEXT: I.setDesc(TII.get(MyTarget::ADD));
// CHECK-NEXT: MachineInstr &NewI = I;
// CHECK: constrainSelectedInstRegOperands(NewI, TII, TRI, RBI);
// CHECK-NEXT: return true;
// CHECK-NEXT: }
def ADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2),
[(set GPR32:$dst, (add GPR32:$src1, GPR32:$src2))]>;
// CHECK-LABEL: if ((I.getOpcode() == TargetOpcode::G_MUL) &&
// CHECK-NEXT: ((/* dst */ (MRI.getType(I.getOperand(0).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(I.getOperand(0).getReg(), MRI, TRI))))) &&
// CHECK-NEXT: ((/* src1 */ (MRI.getType(I.getOperand(1).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(I.getOperand(1).getReg(), MRI, TRI))))) &&
// CHECK-NEXT: ((/* src2 */ (MRI.getType(I.getOperand(2).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(I.getOperand(2).getReg(), MRI, TRI)))))) {
// CHECK-NEXT: // (mul:i32 GPR32:i32:$src1, GPR32:i32:$src2) => (MUL:i32 GPR32:i32:$src2, GPR32:i32:$src1)
// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::MUL));
// CHECK-NEXT: MIB.add(I.getOperand(0)/*dst*/);
// CHECK-NEXT: MIB.add(I.getOperand(2)/*src2*/);
// CHECK-NEXT: MIB.add(I.getOperand(1)/*src1*/);
// CHECK-NEXT: MIB.setMemRefs(I.memoperands_begin(), I.memoperands_end());
// CHECK-NEXT: I.eraseFromParent();
// CHECK-NEXT: MachineInstr &NewI = *MIB;
// CHECK: constrainSelectedInstRegOperands(NewI, TII, TRI, RBI);
// CHECK-NEXT: return true;
// CHECK-NEXT: }
// CHECK-LABEL: if ([&]() {
// CHECK-NEXT: MachineInstr &MI0 = I;
// CHECK-NEXT: if (MI0.getNumOperands() < 3)
// CHECK-NEXT: return false;
// CHECK-NEXT: if ((MI0.getOpcode() == TargetOpcode::G_MUL) &&
// CHECK-NEXT: ((/* dst */ (MRI.getType(MI0.getOperand(0).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(0).getReg(), MRI, TRI))))) &&
// CHECK-NEXT: ((/* src1 */ (MRI.getType(MI0.getOperand(1).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(1).getReg(), MRI, TRI))))) &&
// CHECK-NEXT: ((/* src2 */ (MRI.getType(MI0.getOperand(2).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(2).getReg(), MRI, TRI)))))) {
// CHECK-NEXT: // (mul:i32 GPR32:i32:$src1, GPR32:i32:$src2) => (MUL:i32 GPR32:i32:$src2, GPR32:i32:$src1)
// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::MUL));
// CHECK-NEXT: MIB.add(MI0.getOperand(0)/*dst*/);
// CHECK-NEXT: MIB.add(MI0.getOperand(2)/*src2*/);
// CHECK-NEXT: MIB.add(MI0.getOperand(1)/*src1*/);
// CHECK-NEXT: MIB.setMemRefs(I.memoperands_begin(), I.memoperands_end());
// CHECK-NEXT: I.eraseFromParent();
// CHECK-NEXT: MachineInstr &NewI = *MIB;
// CHECK-NEXT: constrainSelectedInstRegOperands(NewI, TII, TRI, RBI);
// CHECK-NEXT: return true;
// CHECK-NEXT: }
// CHECK-NEXT: return false;
// CHECK-NEXT: }()) { return true; }
def MUL : I<(outs GPR32:$dst), (ins GPR32:$src2, GPR32:$src1),
[(set GPR32:$dst, (mul GPR32:$src1, GPR32:$src2))]>;
@ -72,40 +84,51 @@ def MUL : I<(outs GPR32:$dst), (ins GPR32:$src2, GPR32:$src1),
// This must precede the 3-register variants because constant immediates have
// priority over register banks.
// CHECK-LABEL: if ((I.getOpcode() == TargetOpcode::G_XOR) &&
// CHECK-NEXT: ((/* dst */ (MRI.getType(I.getOperand(0).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(I.getOperand(0).getReg(), MRI, TRI))))) &&
// CHECK-NEXT: ((/* Wm */ (MRI.getType(I.getOperand(1).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(I.getOperand(1).getReg(), MRI, TRI))))) &&
// CHECK-NEXT: ((/* Operand 2 */ (MRI.getType(I.getOperand(2).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: (isOperandImmEqual(I.getOperand(2), -1, MRI))))) {
// CHECK-NEXT: // (xor:i32 GPR32:i32:$Wm, -1:i32) => (ORN:i32 R0:i32, GPR32:i32:$Wm)
// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::ORN));
// CHECK-NEXT: MIB.add(I.getOperand(0)/*dst*/);
// CHECK-NEXT: MIB.addReg(MyTarget::R0);
// CHECK-NEXT: MIB.add(I.getOperand(1)/*Wm*/);
// CHECK-NEXT: MIB.setMemRefs(I.memoperands_begin(), I.memoperands_end());
// CHECK-NEXT: I.eraseFromParent();
// CHECK-NEXT: MachineInstr &NewI = *MIB;
// CHECK: constrainSelectedInstRegOperands(NewI, TII, TRI, RBI);
// CHECK-NEXT: return true;
// CHECK-NEXT: }
// CHECK-LABEL: if ([&]() {
// CHECK-NEXT: MachineInstr &MI0 = I;
// CHECK-NEXT: if (MI0.getNumOperands() < 3)
// CHECK-NEXT: return false;
// CHECK-NEXT: if ((MI0.getOpcode() == TargetOpcode::G_XOR) &&
// CHECK-NEXT: ((/* dst */ (MRI.getType(MI0.getOperand(0).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(0).getReg(), MRI, TRI))))) &&
// CHECK-NEXT: ((/* Wm */ (MRI.getType(MI0.getOperand(1).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(1).getReg(), MRI, TRI))))) &&
// CHECK-NEXT: ((/* Operand 2 */ (MRI.getType(MI0.getOperand(2).getReg()) == (LLT::scalar(32))) &&
// CHECK-NEXT: (isOperandImmEqual(MI0.getOperand(2), -1, MRI))))) {
// CHECK-NEXT: // (xor:i32 GPR32:i32:$Wm, -1:i32) => (ORN:i32 R0:i32, GPR32:i32:$Wm)
// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::ORN));
// CHECK-NEXT: MIB.add(MI0.getOperand(0)/*dst*/);
// CHECK-NEXT: MIB.addReg(MyTarget::R0);
// CHECK-NEXT: MIB.add(MI0.getOperand(1)/*Wm*/);
// CHECK-NEXT: MIB.setMemRefs(I.memoperands_begin(), I.memoperands_end());
// CHECK-NEXT: I.eraseFromParent();
// CHECK-NEXT: MachineInstr &NewI = *MIB;
// CHECK-NEXT: constrainSelectedInstRegOperands(NewI, TII, TRI, RBI);
// CHECK-NEXT: return true;
// CHECK-NEXT: }
// CHECK-NEXT: return false;
// CHECK-NEXT: }()) { return true; }
def ORN : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2), []>;
def : Pat<(not GPR32:$Wm), (ORN R0, GPR32:$Wm)>;
//===- Test a pattern with an MBB operand. --------------------------------===//
// CHECK-LABEL: if ((I.getOpcode() == TargetOpcode::G_BR) &&
// CHECK-NEXT: ((/* target */ (I.getOperand(0).isMBB())))) {
// CHECK-NEXT: // (br (bb:Other):$target) => (BR (bb:Other):$target)
// CHECK-NEXT: I.setDesc(TII.get(MyTarget::BR));
// CHECK-NEXT: MachineInstr &NewI = I;
// CHECK: constrainSelectedInstRegOperands(NewI, TII, TRI, RBI);
// CHECK-NEXT: return true;
// CHECK-NEXT: }
// CHECK-LABEL: if ([&]() {
// CHECK-NEXT: MachineInstr &MI0 = I;
// CHECK-NEXT: if (MI0.getNumOperands() < 1)
// CHECK-NEXT: return false;
// CHECK-NEXT: if ((MI0.getOpcode() == TargetOpcode::G_BR) &&
// CHECK-NEXT: ((/* target */ (MI0.getOperand(0).isMBB())))) {
// CHECK-NEXT: // (br (bb:Other):$target) => (BR (bb:Other):$target)
// CHECK-NEXT: I.setDesc(TII.get(MyTarget::BR));
// CHECK-NEXT: MachineInstr &NewI = I;
// CHECK-NEXT: constrainSelectedInstRegOperands(NewI, TII, TRI, RBI);
// CHECK-NEXT: return true;
// CHECK-NEXT: }
// CHECK-NEXT: return false;
// CHECK-NEXT: }()) { return true; }
def BR : I<(outs), (ins unknown:$target),
[(br bb:$target)]>;

View File

@ -164,14 +164,28 @@ class RuleMatcher {
/// have succeeded.
std::vector<std::unique_ptr<MatchAction>> Actions;
/// A map of instruction matchers to the local variables created by
/// emitCxxCaptureStmts().
std::map<const InstructionMatcher *, std::string> InsnVariableNames;
/// ID for the next instruction variable defined with defineInsnVar()
unsigned NextInsnVarID;
public:
RuleMatcher() {}
RuleMatcher()
: Matchers(), Actions(), InsnVariableNames(), NextInsnVarID(0) {}
InstructionMatcher &addInstructionMatcher();
template <class Kind, class... Args> Kind &addAction(Args &&... args);
void emit(raw_ostream &OS) const;
std::string defineInsnVar(raw_ostream &OS, const InstructionMatcher &Matcher,
StringRef Value);
StringRef getInsnVarName(const InstructionMatcher &InsnMatcher) const;
void emitCxxCaptureStmts(raw_ostream &OS, StringRef Expr);
void emit(raw_ostream &OS);
/// Compare the priority of this object and B.
///
@ -255,7 +269,7 @@ public:
PredicateKind getKind() const { return Kind; }
/// Emit a C++ expression that checks the predicate for the given operand.
virtual void emitCxxPredicateExpr(raw_ostream &OS,
virtual void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule,
StringRef OperandExpr) const = 0;
/// Compare the priority of this object and B.
@ -283,7 +297,7 @@ public:
return P->getKind() == OPM_LLT;
}
void emitCxxPredicateExpr(raw_ostream &OS,
void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule,
StringRef OperandExpr) const override {
OS << "MRI.getType(" << OperandExpr << ".getReg()) == (";
Ty.emitCxxConstructorCall(OS);
@ -308,7 +322,7 @@ public:
: OperandPredicateMatcher(OPM_ComplexPattern), TheDef(TheDef),
BaseTemporaryID(BaseTemporaryID) {}
void emitCxxPredicateExpr(raw_ostream &OS,
void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule,
StringRef OperandExpr) const override {
OS << TheDef.getValueAsString("MatcherFn") << "(" << OperandExpr;
for (unsigned I = 0; I < getNumOperands(); ++I) {
@ -337,7 +351,7 @@ public:
return P->getKind() == OPM_RegBank;
}
void emitCxxPredicateExpr(raw_ostream &OS,
void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule,
StringRef OperandExpr) const override {
OS << "(&RBI.getRegBankFromRegClass(" << RC.getQualifiedName()
<< "RegClass) == RBI.getRegBank(" << OperandExpr
@ -354,7 +368,7 @@ public:
return P->getKind() == OPM_MBB;
}
void emitCxxPredicateExpr(raw_ostream &OS,
void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule,
StringRef OperandExpr) const override {
OS << OperandExpr << ".isMBB()";
}
@ -373,7 +387,7 @@ public:
return P->getKind() == OPM_Int;
}
void emitCxxPredicateExpr(raw_ostream &OS,
void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule,
StringRef OperandExpr) const override {
OS << "isOperandImmEqual(" << OperandExpr << ", " << Value << ", MRI)";
}
@ -383,12 +397,14 @@ public:
/// operand.
class OperandMatcher : public PredicateListMatcher<OperandPredicateMatcher> {
protected:
InstructionMatcher &Insn;
unsigned OpIdx;
std::string SymbolicName;
public:
OperandMatcher(unsigned OpIdx, const std::string &SymbolicName)
: OpIdx(OpIdx), SymbolicName(SymbolicName) {}
OperandMatcher(InstructionMatcher &Insn, unsigned OpIdx,
const std::string &SymbolicName)
: Insn(Insn), OpIdx(OpIdx), SymbolicName(SymbolicName) {}
bool hasSymbolicName() const { return !SymbolicName.empty(); }
const StringRef getSymbolicName() const { return SymbolicName; }
@ -398,16 +414,19 @@ public:
return (InsnVarName + ".getOperand(" + llvm::to_string(OpIdx) + ")").str();
}
InstructionMatcher &getInstructionMatcher() const { return Insn; }
/// 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 {
void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule,
const StringRef InsnVarName) const {
OS << "(/* ";
if (SymbolicName.empty())
OS << "Operand " << OpIdx;
else
OS << SymbolicName;
OS << " */ ";
emitCxxPredicateListExpr(OS, getOperandExpr(InsnVarName));
emitCxxPredicateListExpr(OS, Rule, getOperandExpr(InsnVarName));
OS << ")";
}
@ -468,7 +487,7 @@ public:
/// Emit a C++ expression that tests whether the instruction named in
/// InsnVarName matches the predicate.
virtual void emitCxxPredicateExpr(raw_ostream &OS,
virtual void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule,
StringRef InsnVarName) const = 0;
/// Compare the priority of this object and B.
@ -496,7 +515,7 @@ public:
return P->getKind() == IPM_Opcode;
}
void emitCxxPredicateExpr(raw_ostream &OS,
void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule,
StringRef InsnVarName) const override {
OS << InsnVarName << ".getOpcode() == " << I->Namespace
<< "::" << I->TheDef->getName();
@ -540,18 +559,25 @@ protected:
public:
/// Add an operand to the matcher.
OperandMatcher &addOperand(unsigned OpIdx, const std::string &SymbolicName) {
Operands.emplace_back(OpIdx, SymbolicName);
Operands.emplace_back(*this, OpIdx, SymbolicName);
return Operands.back();
}
const OperandMatcher &getOperand(const StringRef SymbolicName) const {
Optional<const OperandMatcher *> getOptionalOperand(StringRef SymbolicName) const {
assert(!SymbolicName.empty() && "Cannot lookup unnamed operand");
const auto &I = std::find_if(Operands.begin(), Operands.end(),
[&SymbolicName](const OperandMatcher &X) {
return X.getSymbolicName() == SymbolicName;
});
if (I != Operands.end())
return *I;
return &*I;
return None;
}
const OperandMatcher &getOperand(const StringRef SymbolicName) const {
Optional<const OperandMatcher *>OM = getOptionalOperand(SymbolicName);
if (OM.hasValue())
return *OM.getValue();
llvm_unreachable("Failed to lookup operand");
}
@ -566,13 +592,24 @@ public:
return make_range(operands_begin(), operands_end());
}
/// Emit C++ statements to check the shape of the match and capture
/// instructions into local variables.
///
/// TODO: When nested instruction matching is implemented, this function will
/// descend into the operands and capture variables.
void emitCxxCaptureStmts(raw_ostream &OS, RuleMatcher &Rule, StringRef Expr) {
OS << "if (" << Expr << ".getNumOperands() < " << getNumOperands() << ")\n"
<< " return false;\n";
}
/// 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, StringRef InsnVarName) const {
emitCxxPredicateListExpr(OS, InsnVarName);
void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule,
StringRef InsnVarName) const {
emitCxxPredicateListExpr(OS, Rule, InsnVarName);
for (const auto &Operand : Operands) {
OS << " &&\n(";
Operand.emitCxxPredicateExpr(OS, InsnVarName);
Operand.emitCxxPredicateExpr(OS, Rule, InsnVarName);
OS << ")";
}
}
@ -646,7 +683,7 @@ public:
RendererKind getKind() const { return Kind; }
virtual void emitCxxRenderStmts(raw_ostream &OS) const = 0;
virtual void emitCxxRenderStmts(raw_ostream &OS, RuleMatcher &Rule) const = 0;
};
/// A CopyRenderer emits code to copy a single operand from an existing
@ -657,16 +694,13 @@ protected:
/// This provides the facility for looking up an a operand by it's name so
/// that it can be used as a source for the instruction being built.
const InstructionMatcher &Matched;
/// The name of the instruction to copy from.
const StringRef InsnVarName;
/// The name of the operand.
const StringRef SymbolicName;
public:
CopyRenderer(const InstructionMatcher &Matched, const StringRef InsnVarName,
const StringRef SymbolicName)
: OperandRenderer(OR_Copy), Matched(Matched), InsnVarName(InsnVarName),
SymbolicName(SymbolicName) {}
CopyRenderer(const InstructionMatcher &Matched, StringRef SymbolicName)
: OperandRenderer(OR_Copy), Matched(Matched), SymbolicName(SymbolicName) {
}
static bool classof(const OperandRenderer *R) {
return R->getKind() == OR_Copy;
@ -674,9 +708,11 @@ public:
const StringRef getSymbolicName() const { return SymbolicName; }
void emitCxxRenderStmts(raw_ostream &OS) const override {
std::string OperandExpr =
Matched.getOperand(SymbolicName).getOperandExpr(InsnVarName);
void emitCxxRenderStmts(raw_ostream &OS, RuleMatcher &Rule) const override {
const OperandMatcher &Operand = Matched.getOperand(SymbolicName);
StringRef InsnVarName =
Rule.getInsnVarName(Operand.getInstructionMatcher());
std::string OperandExpr = Operand.getOperandExpr(InsnVarName);
OS << " MIB.add(" << OperandExpr << "/*" << SymbolicName << "*/);\n";
}
};
@ -695,7 +731,7 @@ public:
return R->getKind() == OR_Register;
}
void emitCxxRenderStmts(raw_ostream &OS) const override {
void emitCxxRenderStmts(raw_ostream &OS, RuleMatcher &Rule) const override {
OS << " MIB.addReg(" << RegisterDef->getValueAsString("Namespace")
<< "::" << RegisterDef->getName() << ");\n";
}
@ -719,7 +755,7 @@ public:
return R->getKind() == OR_ComplexPattern;
}
void emitCxxRenderStmts(raw_ostream &OS) const override {
void emitCxxRenderStmts(raw_ostream &OS, RuleMatcher &Rule) const override {
assert(Sources.size() == getNumOperands() && "Inconsistent number of operands");
for (const auto &Source : Sources) {
OS << "MIB.add(";
@ -740,11 +776,11 @@ public:
/// Emit the C++ statements to implement the action.
///
/// \param InsnVarName If given, it's an instruction to recycle. The
/// requirements on the instruction vary from action to
/// action.
virtual void emitCxxActionStmts(raw_ostream &OS,
const StringRef InsnVarName) const = 0;
/// \param RecycleVarName If given, it's an instruction to recycle. The
/// requirements on the instruction vary from action to
/// action.
virtual void emitCxxActionStmts(raw_ostream &OS, RuleMatcher &Rule,
StringRef RecycleVarName) const = 0;
};
/// Generates a comment describing the matched rule being acted upon.
@ -755,9 +791,9 @@ private:
public:
DebugCommentAction(const PatternToMatch &P) : P(P) {}
void emitCxxActionStmts(raw_ostream &OS,
const StringRef InsnVarName) const override {
OS << "// " << *P.getSrcPattern() << " => " << *P.getDstPattern();
void emitCxxActionStmts(raw_ostream &OS, RuleMatcher &Rule,
StringRef RecycleVarName) const override {
OS << "// " << *P.getSrcPattern() << " => " << *P.getDstPattern() << "\n";
}
};
@ -794,12 +830,12 @@ public:
return *static_cast<Kind *>(OperandRenderers.back().get());
}
virtual void emitCxxActionStmts(raw_ostream &OS,
const StringRef InsnVarName) const {
void emitCxxActionStmts(raw_ostream &OS, RuleMatcher &Rule,
StringRef RecycleVarName) const override {
if (canMutate()) {
OS << "I.setDesc(TII.get(" << I->Namespace << "::" << I->TheDef->getName()
<< "));\n";
OS << " MachineInstr &NewI = I;\n";
OS << RecycleVarName << ".setDesc(TII.get(" << I->Namespace
<< "::" << I->TheDef->getName() << "));\n";
OS << " MachineInstr &NewI = " << RecycleVarName << ";\n";
return;
}
@ -810,9 +846,9 @@ public:
"I.getDebugLoc(), TII.get("
<< I->Namespace << "::" << I->TheDef->getName() << "));\n";
for (const auto &Renderer : OperandRenderers)
Renderer->emitCxxRenderStmts(OS);
Renderer->emitCxxRenderStmts(OS, Rule);
OS << " MIB.setMemRefs(I.memoperands_begin(), I.memoperands_end());\n";
OS << " " << InsnVarName << ".eraseFromParent();\n";
OS << " " << RecycleVarName << ".eraseFromParent();\n";
OS << " MachineInstr &NewI = *MIB;\n";
}
};
@ -828,7 +864,31 @@ Kind &RuleMatcher::addAction(Args &&... args) {
return *static_cast<Kind *>(Actions.back().get());
}
void RuleMatcher::emit(raw_ostream &OS) const {
std::string RuleMatcher::defineInsnVar(raw_ostream &OS,
const InstructionMatcher &Matcher,
StringRef Value) {
std::string InsnVarName = "MI" + llvm::to_string(NextInsnVarID++);
OS << "MachineInstr &" << InsnVarName << " = " << Value << ";\n";
InsnVariableNames[&Matcher] = InsnVarName;
return InsnVarName;
}
StringRef RuleMatcher::getInsnVarName(const InstructionMatcher &InsnMatcher) const {
const auto &I = InsnVariableNames.find(&InsnMatcher);
if (I != InsnVariableNames.end())
return I->second;
llvm_unreachable("Matched Insn was not captured in a local variable");
}
/// Emit C++ statements to check the shape of the match and capture
/// instructions into local variables.
void RuleMatcher::emitCxxCaptureStmts(raw_ostream &OS, StringRef Expr) {
assert(Matchers.size() == 1 && "Cannot handle multi-root matchers yet");
std::string InsnVarName = defineInsnVar(OS, *Matchers.front(), Expr);
Matchers.front()->emitCxxCaptureStmts(OS, *this, InsnVarName);
}
void RuleMatcher::emit(raw_ostream &OS) {
if (Matchers.empty())
llvm_unreachable("Unexpected empty matcher!");
@ -842,19 +902,24 @@ void RuleMatcher::emit(raw_ostream &OS) const {
// %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 << "if ([&]() {\n";
emitCxxCaptureStmts(OS, "I");
OS << " if (";
Matchers.front()->emitCxxPredicateExpr(OS, *this,
getInsnVarName(*Matchers.front()));
OS << ") {\n";
for (const auto &MA : Actions) {
OS << " ";
MA->emitCxxActionStmts(OS, "I");
OS << "\n";
MA->emitCxxActionStmts(OS, *this, "I");
}
OS << " constrainSelectedInstRegOperands(NewI, TII, TRI, RBI);\n";
OS << " return true;\n";
OS << " }\n\n";
OS << " constrainSelectedInstRegOperands(NewI, TII, TRI, RBI);\n";
OS << " return true;\n";
OS << " }\n";
OS << " return false;\n";
OS << " }()) { return true; }\n\n";
}
bool RuleMatcher::isHigherPriorityThan(const RuleMatcher &B) const {
@ -1007,7 +1072,7 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
OM.addPredicate<LLTOperandMatcher>(*OpTyOrNone);
OM.addPredicate<RegisterBankOperandMatcher>(
Target.getRegisterClass(DstIOpRec));
DstMIBuilder.addRenderer<CopyRenderer>(InsnMatcher, "I", DstIOperand.Name);
DstMIBuilder.addRenderer<CopyRenderer>(InsnMatcher, DstIOperand.Name);
++OpIdx;
}
@ -1086,7 +1151,7 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
if (DstChild->getOperator()->isSubClassOf("SDNode")) {
auto &ChildSDNI = CGP.getSDNodeInfo(DstChild->getOperator());
if (ChildSDNI.getSDClassName() == "BasicBlockSDNode") {
DstMIBuilder.addRenderer<CopyRenderer>(InsnMatcher, "I",
DstMIBuilder.addRenderer<CopyRenderer>(InsnMatcher,
DstChild->getName());
continue;
}
@ -1115,7 +1180,7 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
}
if (ChildRec->isSubClassOf("RegisterClass")) {
DstMIBuilder.addRenderer<CopyRenderer>(InsnMatcher, "I",
DstMIBuilder.addRenderer<CopyRenderer>(InsnMatcher,
DstChild->getName());
continue;
}
@ -1210,7 +1275,7 @@ void GlobalISelEmitter::run(raw_ostream &OS) {
<< " MachineFunction &MF = *I.getParent()->getParent();\n"
<< " const MachineRegisterInfo &MRI = MF.getRegInfo();\n";
for (const auto &Rule : Rules) {
for (auto &Rule : Rules) {
Rule.emit(OS);
++NumPatternEmitted;
}