From 8a3c0788e90fbb1d8551cdd0f9ad8c44f4578dcd Mon Sep 17 00:00:00 2001 From: Daniel Sanders Date: Mon, 20 Mar 2017 15:20:42 +0000 Subject: [PATCH] [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 --- test/TableGen/GlobalISelEmitter.td | 143 ++++++++++++--------- utils/TableGen/GlobalISelEmitter.cpp | 185 ++++++++++++++++++--------- 2 files changed, 208 insertions(+), 120 deletions(-) diff --git a/test/TableGen/GlobalISelEmitter.td b/test/TableGen/GlobalISelEmitter.td index a11e8d34c53..ba3a23753e0 100644 --- a/test/TableGen/GlobalISelEmitter.td +++ b/test/TableGen/GlobalISelEmitter.td @@ -26,43 +26,55 @@ class I 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)]>; diff --git a/utils/TableGen/GlobalISelEmitter.cpp b/utils/TableGen/GlobalISelEmitter.cpp index 35b5ac80ab0..ae386ae98e1 100644 --- a/utils/TableGen/GlobalISelEmitter.cpp +++ b/utils/TableGen/GlobalISelEmitter.cpp @@ -164,14 +164,28 @@ class RuleMatcher { /// have succeeded. std::vector> Actions; + /// A map of instruction matchers to the local variables created by + /// emitCxxCaptureStmts(). + std::map InsnVariableNames; + + /// ID for the next instruction variable defined with defineInsnVar() + unsigned NextInsnVarID; + public: - RuleMatcher() {} + RuleMatcher() + : Matchers(), Actions(), InsnVariableNames(), NextInsnVarID(0) {} InstructionMatcher &addInstructionMatcher(); template 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 { 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 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 { + OptionalOM = 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(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(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 GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { OM.addPredicate(*OpTyOrNone); OM.addPredicate( Target.getRegisterClass(DstIOpRec)); - DstMIBuilder.addRenderer(InsnMatcher, "I", DstIOperand.Name); + DstMIBuilder.addRenderer(InsnMatcher, DstIOperand.Name); ++OpIdx; } @@ -1086,7 +1151,7 @@ Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { if (DstChild->getOperator()->isSubClassOf("SDNode")) { auto &ChildSDNI = CGP.getSDNodeInfo(DstChild->getOperator()); if (ChildSDNI.getSDClassName() == "BasicBlockSDNode") { - DstMIBuilder.addRenderer(InsnMatcher, "I", + DstMIBuilder.addRenderer(InsnMatcher, DstChild->getName()); continue; } @@ -1115,7 +1180,7 @@ Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { } if (ChildRec->isSubClassOf("RegisterClass")) { - DstMIBuilder.addRenderer(InsnMatcher, "I", + DstMIBuilder.addRenderer(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; }