Index: include/llvm/TableGen/Record.h =================================================================== --- include/llvm/TableGen/Record.h +++ include/llvm/TableGen/Record.h @@ -1196,6 +1196,9 @@ inline const_arg_iterator arg_begin() const { return Args.begin(); } inline const_arg_iterator arg_end () const { return Args.end(); } + inline iterator_range args() const { + return llvm::make_range(arg_begin(), arg_end()); + } inline size_t arg_size () const { return Args.size(); } inline bool arg_empty() const { return Args.empty(); } Index: test/TableGen/GlobalISelEmitter.td =================================================================== --- test/TableGen/GlobalISelEmitter.td +++ test/TableGen/GlobalISelEmitter.td @@ -25,6 +25,10 @@ GIComplexOperandMatcher, GIComplexPatternEquiv; +def m1 : OperandWithDefaultOps ; +def Z : OperandWithDefaultOps ; +def m1Z : OperandWithDefaultOps ; + //===- Test the function definition boilerplate. --------------------------===// // CHECK: bool MyTargetInstructionSelector::selectImpl(MachineInstr &I) const { @@ -243,6 +247,109 @@ def INSN1 : I<(outs GPR32:$dst), (ins GPR32:$src1, complex:$src2), []>; def : Pat<(sub GPR32:$src1, complex:$src2), (INSN1 GPR32:$src1, complex:$src2)>; +//===- Test a simple pattern with a default operand. ----------------------===// +// + +// 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: ((/* 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: ((/* Operand 2 */ (MRI.getType(MI0.getOperand(2).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: (isOperandImmEqual(MI0.getOperand(2), -2, MRI))))) { +// CHECK-NEXT: // (xor:i32 GPR32:i32:$src1, -2:i32) => (XORI:i32 GPR32:i32:$src1) +// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::XORI)); +// CHECK-NEXT: MIB.add(MI0.getOperand(0)/*dst*/); +// CHECK-NEXT: MIB.addImm(-1); +// CHECK-NEXT: MIB.add(MI0.getOperand(1)/*src1*/); +// CHECK-NEXT: for (const auto *FromMI : {&MI0, }) +// CHECK-NEXT: for (const auto &MMO : FromMI->memoperands()) +// CHECK-NEXT: MIB.addMemOperand(MMO); +// 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; } + +// The -2 is just to distinguish it from the 'not' case below. +def XORI : I<(outs GPR32:$dst), (ins m1:$src2, GPR32:$src1), + [(set GPR32:$dst, (xor GPR32:$src1, -2))]>; + +//===- Test a simple pattern with a default register operand. -------------===// +// + +// 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: ((/* 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: ((/* Operand 2 */ (MRI.getType(MI0.getOperand(2).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: (isOperandImmEqual(MI0.getOperand(2), -3, MRI))))) { +// CHECK-NEXT: // (xor:i32 GPR32:i32:$src1, -3:i32) => (XOR:i32 GPR32:i32:$src1) +// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::XOR)); +// CHECK-NEXT: MIB.add(MI0.getOperand(0)/*dst*/); +// CHECK-NEXT: MIB.addReg(MyTarget::R0); +// CHECK-NEXT: MIB.add(MI0.getOperand(1)/*src1*/); +// CHECK-NEXT: for (const auto *FromMI : {&MI0, }) +// CHECK-NEXT: for (const auto &MMO : FromMI->memoperands()) +// CHECK-NEXT: MIB.addMemOperand(MMO); +// 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; } + +// The -3 is just to distinguish it from the 'not' case below and the other default op case above. +def XOR : I<(outs GPR32:$dst), (ins Z:$src2, GPR32:$src1), + [(set GPR32:$dst, (xor GPR32:$src1, -3))]>; + +//===- Test a simple pattern with a multiple default operands. ------------===// +// + +// 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: ((/* 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: ((/* Operand 2 */ (MRI.getType(MI0.getOperand(2).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: (isOperandImmEqual(MI0.getOperand(2), -4, MRI))))) { +// CHECK-NEXT: // (xor:i32 GPR32:i32:$src1, -4:i32) => (XORlike:i32 GPR32:i32:$src1) +// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::XORlike)); +// CHECK-NEXT: MIB.add(MI0.getOperand(0)/*dst*/); +// CHECK-NEXT: MIB.addImm(-1); +// CHECK-NEXT: MIB.addReg(MyTarget::R0); +// CHECK-NEXT: MIB.add(MI0.getOperand(1)/*src1*/); +// CHECK-NEXT: for (const auto *FromMI : {&MI0, }) +// CHECK-NEXT: for (const auto &MMO : FromMI->memoperands()) +// CHECK-NEXT: MIB.addMemOperand(MMO); +// 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; } + +// The -4 is just to distinguish it from the other 'not' cases. +def XORlike : I<(outs GPR32:$dst), (ins m1Z:$src2, GPR32:$src1), + [(set GPR32:$dst, (xor GPR32:$src1, -4))]>; + //===- Test a simple pattern with constant immediate operands. ------------===// // // This must precede the 3-register variants because constant immediates have Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ utils/TableGen/GlobalISelEmitter.cpp @@ -32,6 +32,7 @@ #include "CodeGenDAGPatterns.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/Statistic.h" #include "llvm/CodeGen/MachineValueType.h" #include "llvm/Support/CommandLine.h" @@ -803,7 +804,7 @@ class OperandRenderer { public: - enum RendererKind { OR_Copy, OR_Register, OR_ComplexPattern }; + enum RendererKind { OR_Copy, OR_Imm, OR_Register, OR_ComplexPattern }; protected: RendererKind Kind; @@ -868,6 +869,24 @@ } }; +/// Adds a specific immediate to the instruction being built. +class ImmRenderer : public OperandRenderer { +protected: + int64_t Imm; + +public: + ImmRenderer(int64_t Imm) + : OperandRenderer(OR_Imm), Imm(Imm) {} + + static bool classof(const OperandRenderer *R) { + return R->getKind() == OR_Imm; + } + + void emitCxxRenderStmts(raw_ostream &OS, RuleMatcher &Rule) const override { + OS << " MIB.addImm(" << Imm << ");\n"; + } +}; + class RenderComplexPatternOperand : public OperandRenderer { private: const Record &TheDef; @@ -1444,11 +1463,66 @@ DstMIBuilder.addRenderer(InsnMatcher, DstIOperand.Name); } + // Figure out which operands need defaults inserted. Operands that subclass + // OperandWithDefaultOps are considered from left to right until we have + // enough operands to render the instruction. + SmallSet DefaultOperands; + unsigned DstINumUses = DstI.Operands.size() - DstI.Operands.NumDefs; + unsigned NumDefaultOperands = 0; + for (unsigned I = 0; I < DstINumUses && + DstINumUses > Dst->getNumChildren() + NumDefaultOperands; + ++I) { + const auto &DstIOperand = DstI.Operands[DstI.Operands.NumDefs + I]; + if (DstIOperand.Rec->isSubClassOf("OperandWithDefaultOps")) { + DefaultOperands.insert(I); + NumDefaultOperands += + DstIOperand.Rec->getValueAsDag("DefaultOps")->getNumArgs(); + } + } + if (DstINumUses > Dst->getNumChildren() + DefaultOperands.size()) + return failedImport("Insufficient operands supplied and default ops " + "couldn't make up the shortfall"); + if (DstINumUses < Dst->getNumChildren() + DefaultOperands.size()) + return failedImport("Too many operands supplied"); + // Render the explicit uses. - for (unsigned i = 0, e = Dst->getNumChildren(); i != e; ++i) { - if (auto Error = importExplicitUseRenderer(DstMIBuilder, Dst->getChild(i), - InsnMatcher)) + unsigned Child = 0; + for (unsigned I = 0; I != DstINumUses; ++I) { + // If we need to insert default ops here, then do so. + if (DefaultOperands.count(I)) { + const auto &DstIOperand = DstI.Operands[DstI.Operands.NumDefs + I]; + + DagInit *DefaultOps = DstIOperand.Rec->getValueAsDag("DefaultOps"); + for (const auto *DefaultOp : DefaultOps->args()) { + // Look through ValueType operators. + if (const DagInit *DefaultDagOp = dyn_cast(DefaultOp)) { + if (const DefInit *DefaultDagOperator = + dyn_cast(DefaultDagOp->getOperator())) { + if (DefaultDagOperator->getDef()->isSubClassOf("ValueType")) + DefaultOp = DefaultDagOp->getArg(0); + } + } + + if (const DefInit *DefaultDefOp = dyn_cast(DefaultOp)) { + DstMIBuilder.addRenderer(DefaultDefOp->getDef()); + continue; + } + + if (const IntInit *DefaultIntOp = dyn_cast(DefaultOp)) { + DstMIBuilder.addRenderer(DefaultIntOp->getValue()); + continue; + } + + return failedImport("Could not add default op"); + } + + continue; + } + + if (auto Error = importExplicitUseRenderer( + DstMIBuilder, Dst->getChild(Child), InsnMatcher)) return std::move(Error); + ++Child; } return DstMIBuilder;