Index: include/llvm/Target/GlobalISel/Target.td =================================================================== --- include/llvm/Target/GlobalISel/Target.td +++ include/llvm/Target/GlobalISel/Target.td @@ -20,9 +20,10 @@ // Definitions that inherit from LLT define types that will be used in the // GlobalISel matcher. class LLT; +class LLTScalar : LLT { int SizeInBits = sizeinbits; } -def s32 : LLT; -def s64 : LLT; +def s32 : LLTScalar<32>; +def s64 : LLTScalar<64>; // Defines a matcher for complex operands. This is analogous to ComplexPattern // from SelectionDAG. @@ -54,3 +55,109 @@ // overwritten. string MatcherFn = matcherfn; } + +// === Operand predicates === +class GIMatchOperandPredicate; + +// Operand must be of the given type. +class GIMatchLLT : GIMatchOperandPredicate { + LLT Type = type; +} + +// Operand must match the given complex pattern. +// FIXME: Merge with GIComplexOperandMatcher +class GIMatchComplexPattern : GIMatchOperandPredicate { + GIComplexOperandMatcher Matcher = matcher; +} + +// Operand must be in the register bank for the given register class. +class GIMatchRegBankForClass : GIMatchOperandPredicate { + RegisterClass RC = rc; +} + +// Operand must be an MBB. +// This one is a def since classes cannot be anonymously defined without arguments. +class GIMatchMBBImpl : GIMatchOperandPredicate; +def GIMatchMBB : GIMatchMBBImpl; + +// Operand must match a specific integer. +class GIMatchInt : GIMatchOperandPredicate { + int Value = value; +} + +// TODO: Operand must match arbitrary code (for ImmLeaf). +class GIMatchCode : GIMatchOperandPredicate { + code Code = c; +} + +// Operand must be present and must match all the predicates. If name is +// non-empty it can be used by the renderers. +class GIMatchOperand predicates> { + string Name = name; + list Predicates = predicates; +} + +// === Instruction Predicates === +class GIMatchInsnPredicate; + +// Instruction must have the given opcode. +class GIMatchOpcode : GIMatchInsnPredicate { + Instruction Insn = insn; +} + +// Instruction must be present and must match all the predicates and operands. +class GIMatchInsn predicates, list operands> { + list Predicates = predicates; + list Operands = operands; +} + +// Operand must use a register that's defined by an instruction matching the +// given instruction matcher. +class GIMatchSubInsn : GIMatchOperandPredicate { + GIMatchInsn Insn = insn; +} + +// === Operand Renderers === +class GIOperandRenderer; + +// Copy operand from match. +class GICopyOperand : GIOperandRenderer { + string Name = name; +} + +// Add a specific register. +class GIAddRegister : GIOperandRenderer { + Register Reg = reg; +} + +// Add a specific integer. +class GIAddImm : GIOperandRenderer { + int Value = value; +} + +// Render ComplexPattern results. +class GIAddComplexOperand : GIOperandRenderer { + GIComplexOperandMatcher Matcher = matcher; + string Name = name; +} + +// === Actions === +class GIAction; + +// Build an instruction. +// Will automatically convert to an in-place mutation where possible. +class GIBuildMI renderers> : GIAction { + Instruction I = i; + list Renderers = renderers; +} + +// === Rules === + +// Defines a GlobalISel matcher rule. +class GIRule insnmatchers, list actions> { + // A list of instruction matchers. These form the roots of the match. + list InsnMatchers = insnmatchers; + + // A list of actions to take in response to the rule. + list Actions = actions; +} Index: test/TableGen/GlobalISelEmitter-GIRule.td =================================================================== --- /dev/null +++ test/TableGen/GlobalISelEmitter-GIRule.td @@ -0,0 +1,438 @@ +// RUN: llvm-tblgen -gen-global-isel -I %p/../../include %s | FileCheck %s + +include "llvm/Target/Target.td" + +//===- Define the necessary boilerplate for our test target. --------------===// + +def MyTargetISA : InstrInfo; +def MyTarget : Target { let InstructionSet = MyTargetISA; } + +def R0 : Register<"r0"> { let Namespace = "MyTarget"; } +def GPR32 : RegisterClass<"MyTarget", [i32], 32, (add R0)>; + +class I Pat> + : Instruction { + let Namespace = "MyTarget"; + let OutOperandList = OOps; + let InOperandList = IOps; + let Pattern = Pat; +} + +def complex : Operand; +def gi_complex : + GIComplexOperandMatcher; + +def m1 : OperandWithDefaultOps ; +def Z : OperandWithDefaultOps ; + +//===- Test the function definition boilerplate. --------------------------===// + +// CHECK: bool MyTargetInstructionSelector::selectImpl(MachineInstr &I) const { +// CHECK: MachineFunction &MF = *I.getParent()->getParent(); +// CHECK: const MachineRegisterInfo &MRI = MF.getRegInfo(); + +//===- Test a pattern with multiple ComplexPattern operands. --------------===// +// + +// CHECK-LABEL: if ([&]() { +// CHECK-NEXT: MachineInstr &MI0 = I; +// CHECK-NEXT: if (MI0.getNumOperands() < 4) +// CHECK-NEXT: return false; +// CHECK-NEXT: if ((MI0.getOpcode() == TargetOpcode::G_SELECT) && +// 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: (selectComplexPattern(MI0.getOperand(2), TempOp0, TempOp1)))) && +// CHECK-NEXT: ((/* src3 */ (MRI.getType(MI0.getOperand(3).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: (selectComplexPattern(MI0.getOperand(3), TempOp2, TempOp3))))) { +// CHECK-NEXT: // Rule1 +// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::INSN2)); +// CHECK-NEXT: MIB.add(MI0.getOperand(0)/*dst*/); +// CHECK-NEXT: MIB.add(MI0.getOperand(1)/*src1*/); +// CHECK-NEXT: MIB.add(TempOp2); +// CHECK-NEXT: MIB.add(TempOp3); +// CHECK-NEXT: MIB.add(TempOp0); +// CHECK-NEXT: MIB.add(TempOp1); +// 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: } + +def INSN2 : I<(outs GPR32:$dst), (ins GPR32:$src1, complex:$src2, complex:$src3), []>; +def Rule1 : GIRule< + [GIMatchInsn<[GIMatchOpcode], + [GIMatchOperand<"dst", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"src1", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"src2", [GIMatchLLT, GIMatchComplexPattern]>, + GIMatchOperand<"src3", [GIMatchLLT, GIMatchComplexPattern]> + ]>], + [GIBuildMI, + GICopyOperand<"src1">, + GIAddComplexOperand, + GIAddComplexOperand + ]> + ]>; + +//===- Test a simple pattern with regclass 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_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: // Rule2 +// 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; } + + +def ADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2), []>; +def Rule2 : GIRule< + [GIMatchInsn<[GIMatchOpcode], [ + GIMatchOperand<"dst", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"src1", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"src2", [GIMatchLLT, GIMatchRegBankForClass]>]>], + [GIBuildMI, GICopyOperand<"src1">, GICopyOperand<"src2">]>]>; + +//===- Test a nested instruction match. -----------------------------------===// + +// CHECK-LABEL: if ([&]() { +// CHECK-NEXT: MachineInstr &MI0 = I; +// CHECK-NEXT: if (MI0.getNumOperands() < 3) +// CHECK-NEXT: return false; +// CHECK-NEXT: if (!MI0.getOperand(1).isReg()) +// CHECK-NEXT: return false; +// CHECK-NEXT: MachineInstr &MI1 = *MRI.getVRegDef(MI0.getOperand(1).getReg()); +// CHECK-NEXT: if (MI1.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: ((/* Operand 1 */ (MRI.getType(MI0.getOperand(1).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: (((MI1.getOpcode() == TargetOpcode::G_ADD) && +// CHECK-NEXT: ((/* Operand 0 */ (MRI.getType(MI1.getOperand(0).getReg()) == (LLT::scalar(32))))) && +// CHECK-NEXT: ((/* src1 */ (MRI.getType(MI1.getOperand(1).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI1.getOperand(1).getReg(), MRI, TRI))))) && +// CHECK-NEXT: ((/* src2 */ (MRI.getType(MI1.getOperand(2).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI1.getOperand(2).getReg(), MRI, TRI)))))) +// CHECK-NEXT: ))) && +// CHECK-NEXT: ((/* src3 */ (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: if (!isObviouslySafeToFold(MI1)) return false; +// CHECK-NEXT: // Rule3 +// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::MULADD)); +// CHECK-NEXT: MIB.add(MI0.getOperand(0)/*dst*/); +// CHECK-NEXT: MIB.add(MI1.getOperand(1)/*src1*/); +// CHECK-NEXT: MIB.add(MI1.getOperand(2)/*src2*/); +// CHECK-NEXT: MIB.add(MI0.getOperand(2)/*src3*/); +// 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: } + +// We also get a second rule by commutativity. +// CHECK-LABEL: if ([&]() { +// CHECK-NEXT: MachineInstr &MI0 = I; +// CHECK-NEXT: if (MI0.getNumOperands() < 3) +// CHECK-NEXT: return false; +// CHECK-NEXT: if (!MI0.getOperand(2).isReg()) +// CHECK-NEXT: return false; +// CHECK-NEXT: MachineInstr &MI1 = *MRI.getVRegDef(MI0.getOperand(2).getReg()); +// CHECK-NEXT: if (MI1.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: ((/* src3 */ (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: (((MI1.getOpcode() == TargetOpcode::G_ADD) && +// CHECK-NEXT: ((/* Operand 0 */ (MRI.getType(MI1.getOperand(0).getReg()) == (LLT::scalar(32))))) && +// CHECK-NEXT: ((/* src1 */ (MRI.getType(MI1.getOperand(1).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI1.getOperand(1).getReg(), MRI, TRI))))) && +// CHECK-NEXT: ((/* src2 */ (MRI.getType(MI1.getOperand(2).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI1.getOperand(2).getReg(), MRI, TRI)))))) +// CHECK-NEXT: )))) { +// CHECK-NEXT: if (!isObviouslySafeToFold(MI1)) return false; +// CHECK-NEXT: // Rule3X +// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::MULADD)); +// CHECK-NEXT: MIB.add(MI0.getOperand(0)/*dst*/); +// CHECK-NEXT: MIB.add(MI1.getOperand(1)/*src1*/); +// CHECK-NEXT: MIB.add(MI1.getOperand(2)/*src2*/); +// CHECK-NEXT: MIB.add(MI0.getOperand(1)/*src3*/); +// 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: } + +def MULADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3), []>; +def Rule3: GIRule< + [GIMatchInsn<[GIMatchOpcode], [ + GIMatchOperand<"dst", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"", + [GIMatchLLT, + GIMatchSubInsn], [ + GIMatchOperand<"", [GIMatchLLT]>, + GIMatchOperand<"src1", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"src2", [GIMatchLLT, GIMatchRegBankForClass]>]>>]>, + GIMatchOperand<"src3", [GIMatchLLT, GIMatchRegBankForClass]>]> + ], + [GIBuildMI, + GICopyOperand<"src1">, + GICopyOperand<"src2">, + GICopyOperand<"src3">]> + ]>; + +// Commutativity is manual at the moment. +def Rule3X: GIRule< + [GIMatchInsn<[GIMatchOpcode], [ + GIMatchOperand<"dst", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"src3", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"", + [GIMatchLLT, + GIMatchSubInsn], [ + GIMatchOperand<"", [GIMatchLLT]>, + GIMatchOperand<"src1", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"src2", [GIMatchLLT, GIMatchRegBankForClass]>]>>]>]>], + [GIBuildMI, + GICopyOperand<"src1">, + GICopyOperand<"src2">, + GICopyOperand<"src3">]> + ]>; + +//===- Test another simple pattern with regclass 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_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: // Rule4 +// 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), + []>; +def Rule4: GIRule< + [GIMatchInsn<[GIMatchOpcode], [ + GIMatchOperand<"dst", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"src1", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"src2", [GIMatchLLT, GIMatchRegBankForClass]>]> + ], + [GIBuildMI, GICopyOperand<"src2">, GICopyOperand<"src1">]>]>; + +//===- Test a pattern with ComplexPattern 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_SUB) && +// 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: (selectComplexPattern(MI0.getOperand(2), TempOp0, TempOp1))))) { +// CHECK-NEXT: // Rule5 +// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::INSN1)); +// CHECK-NEXT: MIB.add(MI0.getOperand(0)/*dst*/); +// CHECK-NEXT: MIB.add(MI0.getOperand(1)/*src1*/); +// CHECK-NEXT: MIB.add(TempOp0); +// CHECK-NEXT: MIB.add(TempOp1); +// 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: } + +def INSN1 : I<(outs GPR32:$dst), (ins GPR32:$src1, complex:$src2), []>; +def Rule5 : GIRule< + [GIMatchInsn<[GIMatchOpcode], [ + GIMatchOperand<"dst", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"src1", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"src2", [GIMatchLLT, GIMatchComplexPattern]>]> + ], + [GIBuildMI, + GICopyOperand<"src1">, + GIAddComplexOperand]> + ]>; + +//===- 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: // Rule6 +// 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: 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; } + +// The -2 is just to distinguish it from the 'not' case below. +def XORI : I<(outs GPR32:$dst), (ins m1:$src2, GPR32:$src1), []>; +def Rule6 : GIRule< + [GIMatchInsn<[GIMatchOpcode], [ + GIMatchOperand<"dst", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"src1", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"", [GIMatchLLT, GIMatchInt<-2>]>]> + ], + [GIBuildMI, GIAddImm<-1>, GICopyOperand<"src1">]>]>; + +//===- 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: // Rule7 +// 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: 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; } + +// 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), []>; +def Rule7 : GIRule< + [GIMatchInsn<[GIMatchOpcode], [ + GIMatchOperand<"dst", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"src1", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"", [GIMatchLLT, GIMatchInt<-3>]>]> + ], + [GIBuildMI, GIAddRegister, GICopyOperand<"src1">]>]>; + +//===- Test a simple pattern with constant immediate operands. ------------===// +// +// This must precede the 3-register variants because constant immediates have +// priority over register banks. + +// 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: // Rule8 +// 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 Rule8 : GIRule< + [GIMatchInsn<[GIMatchOpcode], [ + GIMatchOperand<"dst", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"Wm", [GIMatchLLT, GIMatchRegBankForClass]>, + GIMatchOperand<"", [GIMatchLLT, GIMatchInt<-1>]>]> + ], + [GIBuildMI, GIAddRegister, GICopyOperand<"Wm">]>]>; + +//===- Test a pattern with an MBB operand. --------------------------------===// + +// 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)]>; +def : GIRule< + [GIMatchInsn<[GIMatchOpcode], [GIMatchOperand<"target", [GIMatchMBB]>]>], + [GIBuildMI]>]>; Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ utils/TableGen/GlobalISelEmitter.cpp @@ -62,6 +62,25 @@ namespace { //===- Helper functions ---------------------------------------------------===// +class IndentStr { +private: + unsigned Indent; + +public: + IndentStr(unsigned Indent) : Indent(Indent) {} + + unsigned get() const { return Indent; } +}; + +IndentStr operator+(IndentStr Indent, unsigned N) { + return IndentStr(Indent.get() + N); +} + +raw_ostream &operator<<(raw_ostream &OS, IndentStr Indent) { + OS << std::string(Indent.get(), ' '); + return OS; +} + /// This class stands in for LLT wherever we want to tablegen-erate an /// equivalent at compiler run-time. class LLTCodeGen { @@ -91,22 +110,14 @@ class OperandPlaceholder { private: enum PlaceholderKind { - OP_MatchReference, OP_Temporary, } Kind; - struct MatchReferenceData { - InstructionMatcher *InsnMatcher; - StringRef InsnVarName; - StringRef SymbolicName; - }; - struct TemporaryData { unsigned OpIdx; }; union { - struct MatchReferenceData MatchReference; struct TemporaryData Temporary; }; @@ -115,16 +126,6 @@ public: ~OperandPlaceholder() {} - static OperandPlaceholder - CreateMatchReference(InstructionMatcher *InsnMatcher, - const StringRef InsnVarName, const StringRef SymbolicName) { - OperandPlaceholder Result(OP_MatchReference); - Result.MatchReference.InsnMatcher = InsnMatcher; - Result.MatchReference.InsnVarName = InsnVarName; - Result.MatchReference.SymbolicName = SymbolicName; - return Result; - } - static OperandPlaceholder CreateTemporary(unsigned OpIdx) { OperandPlaceholder Result(OP_Temporary); Result.Temporary.OpIdx = OpIdx; @@ -132,6 +133,7 @@ } void emitCxxValueExpr(raw_ostream &OS) const; + void emitTblgen(raw_ostream &OS, IndentStr Indent) const; }; /// Convert an MVT to an equivalent LLT if possible, or the invalid LLT() for @@ -234,6 +236,7 @@ void emitCxxCaptureStmts(raw_ostream &OS, StringRef Expr); + void emitTblgen(raw_ostream &OS, IndentStr Indent); void emit(raw_ostream &OS); /// Compare the priority of this object and B. @@ -244,6 +247,9 @@ /// Report the maximum number of temporary operands needed by the rule /// matcher. unsigned countTemporaryOperands() const; + + // FIXME: Remove this as soon as possible + InstructionMatcher &insnmatcher_front() const { return *Matchers.front(); } }; template class PredicateListMatcher { @@ -301,6 +307,7 @@ /// but OPM_Int must have priority over OPM_RegBank since constant integers /// are represented by a virtual register defined by a G_CONSTANT instruction. enum PredicateKind { + OPM_Arbitrary, OPM_ComplexPattern, OPM_Instruction, OPM_Int, @@ -339,6 +346,8 @@ virtual void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule, StringRef OperandExpr) const = 0; + virtual void emitTblgen(raw_ostream &OS, IndentStr Indent) const = 0; + /// Compare the priority of this object and B. /// /// Returns true if this object is more important than B. @@ -370,6 +379,10 @@ Ty.emitCxxConstructorCall(OS); OS << ")"; } + + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << Indent << "GIMatchLLT<" << Ty.get() << ">"; + } }; /// Generates code to check that an operand is a particular target constant. @@ -405,6 +418,10 @@ OS << ")"; } + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << Indent << "GIMatchComplexPattern<" << TheDef.getName() << ">"; + } + unsigned countTemporaryOperands() const override { return getNumOperands(); } @@ -429,6 +446,10 @@ << "RegClass) == RBI.getRegBank(" << OperandExpr << ".getReg(), MRI, TRI))"; } + + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << Indent << "GIMatchRegBankForClass<" << RC.getName() << ">"; + } }; /// Generates code to check that an operand is a basic block. @@ -444,6 +465,10 @@ StringRef OperandExpr) const override { OS << OperandExpr << ".isMBB()"; } + + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << "GIMatchMBB"; + } }; /// Generates code to check that an operand is a particular int. @@ -463,6 +488,33 @@ StringRef OperandExpr) const override { OS << "isOperandImmEqual(" << OperandExpr << ", " << Value << ", MRI)"; } + + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << "GIMatchInt<" << Value << ">"; + } +}; + +/// Generates code to check that an an arbitrary predicate. +class ArbitraryOperandPredicateMatcher : public OperandPredicateMatcher { +protected: + TreePredicateFn Predicate; + +public: + ArbitraryOperandPredicateMatcher(const TreePredicateFn &Predicate) + : OperandPredicateMatcher(OPM_Arbitrary), Predicate(Predicate) {} + + static bool classof(const OperandPredicateMatcher *P) { + return P->getKind() == OPM_Arbitrary; + } + + void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule, + StringRef OperandExpr) const override { + OS << Predicate.getFnName() << "(" << OperandExpr << ", MRI)"; + } + + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << "GIMatchCode<...>"; + } }; /// Generates code to check that a set of predicates match for a particular @@ -533,6 +585,18 @@ OS << ")"; } + void emitTblgen(raw_ostream &OS, IndentStr Indent) const { + OS << Indent << "GIMatchOperand<\"" << SymbolicName << "\", [\n"; + Indent = Indent + 1; + StringRef Separator = ""; + for (const auto &Predicate : predicates()) { + OS << Separator; + Predicate->emitTblgen(OS, Indent+1); + Separator = ",\n"; + } + OS << "]>"; + } + /// Compare the priority of this object and B. /// /// Returns true if this object is more important than B. @@ -601,6 +665,8 @@ virtual void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule, StringRef InsnVarName) const = 0; + virtual void emitTblgen(raw_ostream &OS, IndentStr Indent) const = 0; + /// Compare the priority of this object and B. /// /// Returns true if this object is more important than B. @@ -632,6 +698,10 @@ << "::" << I->TheDef->getName(); } + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << Indent << "GIMatchOpcode<" << I->TheDef->getName() << ">"; + } + /// Compare the priority of this object and B. /// /// Returns true if this object is more important than B. @@ -701,7 +771,7 @@ OptionalOM = getOptionalOperand(SymbolicName); if (OM.hasValue()) return *OM.getValue(); - llvm_unreachable("Failed to lookup operand"); + llvm_unreachable(("Failed to lookup operand " + SymbolicName).str().c_str()); } unsigned getNumOperands() const { return Operands.size(); } @@ -742,6 +812,25 @@ } } + void emitTblgen(raw_ostream &OS, IndentStr Indent) { + OS << Indent << "GIMatchInsn<[\n"; + Indent = Indent + 1; + StringRef Separator = ""; + for (const auto &Predicate : predicates()) { + OS << Separator; + Predicate->emitTblgen(OS, Indent+1); + Separator = ",\n"; + } + OS << "\n" << Indent << "], [\n"; + Separator = ""; + for (const auto &Operand : Operands) { + OS << Separator; + Operand->emitTblgen(OS, Indent+1); + Separator = ",\n"; + } + OS << "]>\n"; + } + /// Compare the priority of this object and B. /// /// Returns true if this object is more important than B. @@ -833,15 +922,25 @@ InsnMatcher->emitCxxPredicateExpr(OS, Rule, OperandExpr); OS << ")\n"; } + + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << Indent << "GIMatchSubInsn<\n"; + InsnMatcher->emitTblgen(OS, Indent + 1); + OS << ">"; + } }; //===- Actions ------------------------------------------------------------===// void OperandPlaceholder::emitCxxValueExpr(raw_ostream &OS) const { switch (Kind) { - case OP_MatchReference: - OS << MatchReference.InsnMatcher->getOperand(MatchReference.SymbolicName) - .getOperandExpr(MatchReference.InsnVarName); + case OP_Temporary: + OS << "TempOp" << Temporary.OpIdx; break; + } +} + +void OperandPlaceholder::emitTblgen(raw_ostream &OS, IndentStr Indent) const { + switch (Kind) { case OP_Temporary: OS << "TempOp" << Temporary.OpIdx; break; @@ -862,6 +961,7 @@ RendererKind getKind() const { return Kind; } virtual void emitCxxRenderStmts(raw_ostream &OS, RuleMatcher &Rule) const = 0; + virtual void emitTblgen(raw_ostream &OS, IndentStr Indent) const = 0; }; /// A CopyRenderer emits code to copy a single operand from an existing @@ -873,7 +973,7 @@ /// that it can be used as a source for the instruction being built. const InstructionMatcher &Matched; /// The name of the operand. - const StringRef SymbolicName; + std::string SymbolicName; public: CopyRenderer(const InstructionMatcher &Matched, StringRef SymbolicName) @@ -893,6 +993,10 @@ std::string OperandExpr = Operand.getOperandExpr(InsnVarName); OS << " MIB.add(" << OperandExpr << "/*" << SymbolicName << "*/);\n"; } + + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << Indent << "GICopyOperand<\"" << SymbolicName << "\">"; + } }; /// Adds a specific physical register to the instruction being built. @@ -913,6 +1017,10 @@ OS << " MIB.addReg(" << RegisterDef->getValueAsString("Namespace") << "::" << RegisterDef->getName() << ");\n"; } + + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << Indent << "GIAddRegister<" << RegisterDef->getName() << ">"; + } }; /// Adds a specific immediate to the instruction being built. @@ -931,11 +1039,17 @@ void emitCxxRenderStmts(raw_ostream &OS, RuleMatcher &Rule) const override { OS << " MIB.addImm(" << Imm << ");\n"; } + + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << Indent << "GIAddImm<" << Imm << ">"; + } }; class RenderComplexPatternOperand : public OperandRenderer { private: const Record &TheDef; + /// The name of the operand. + const StringRef SymbolicName; std::vector Sources; unsigned getNumOperands() const { @@ -943,9 +1057,10 @@ } public: - RenderComplexPatternOperand(const Record &TheDef, + RenderComplexPatternOperand(const Record &TheDef, StringRef SymbolicName, const ArrayRef Sources) - : OperandRenderer(OR_ComplexPattern), TheDef(TheDef), Sources(Sources) {} + : OperandRenderer(OR_ComplexPattern), TheDef(TheDef), + SymbolicName(SymbolicName), Sources(Sources) {} static bool classof(const OperandRenderer *R) { return R->getKind() == OR_ComplexPattern; @@ -959,6 +1074,18 @@ OS << ");\n"; } } + + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << Indent << "GIAddComplexOperand<" << TheDef.getName() << ", \"" + << SymbolicName << "\" /* "; + StringRef Separator = ""; + for (const OperandPlaceholder &Placeholder : Sources) { + OS << Separator; + Placeholder.emitTblgen(OS, Indent); + Separator = ", "; + } + OS << " */>"; + } }; /// An action taken when all Matcher predicates succeeded for a parent rule. @@ -977,19 +1104,25 @@ /// action. virtual void emitCxxActionStmts(raw_ostream &OS, RuleMatcher &Rule, StringRef RecycleVarName) const = 0; + + virtual void emitTblgen(raw_ostream &OS, IndentStr Indent) const = 0; }; /// Generates a comment describing the matched rule being acted upon. class DebugCommentAction : public MatchAction { private: - const PatternToMatch &P; + std::string Comment; public: - DebugCommentAction(const PatternToMatch &P) : P(P) {} + DebugCommentAction(StringRef Comment) : Comment(Comment) {} void emitCxxActionStmts(raw_ostream &OS, RuleMatcher &Rule, StringRef RecycleVarName) const override { - OS << "// " << *P.getSrcPattern() << " => " << *P.getDstPattern() << "\n"; + OS << "// " << Comment << "\n"; + } + + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << Indent << "// " << Comment << "\n"; } }; @@ -1064,6 +1197,18 @@ OS << " " << RecycleVarName << ".eraseFromParent();\n"; OS << " MachineInstr &NewI = *MIB;\n"; } + + void emitTblgen(raw_ostream &OS, IndentStr Indent) const override { + OS << Indent << "GIBuildMI<" << I->TheDef->getName() << ", [\n"; + Indent = Indent + 1; + StringRef Separator = ""; + for (const auto &Renderer : OperandRenderers) { + OS << Separator; + Renderer->emitTblgen(OS, Indent + 1); + Separator = ",\n"; + } + OS << Indent << "]>\n"; + } }; InstructionMatcher &RuleMatcher::addInstructionMatcher() { @@ -1101,6 +1246,19 @@ Matchers.front()->emitCxxCaptureStmts(OS, *this, InsnVarName); } +void RuleMatcher::emitTblgen(raw_ostream &OS, IndentStr Indent) { + OS << "#if 0\n"; + OS << "GIRule<[\n"; + Indent = Indent + 1; + for (const auto &Matcher : Matchers) + Matcher->emitTblgen(OS, Indent + 1); + OS << Indent << "], [\n"; + for (const auto &Action : Actions) + Action->emitTblgen(OS, Indent + 1); + OS << Indent << "]>\n"; + OS << "#endif // 0\n"; +} + void RuleMatcher::emit(raw_ostream &OS) { if (Matchers.empty()) llvm_unreachable("Unexpected empty matcher!"); @@ -1252,6 +1410,17 @@ importImplicitDefRenderers(BuildMIAction &DstMIBuilder, const std::vector &ImplicitDefs) const; + void parseGIMatchInsnPredicates(const std::vector &PredicateDefs, + InstructionMatcher &InsnMatcher) const; + void parseGIMatchOperands(const std::vector &OperandDefs, + InstructionMatcher &InsnMatcher, + unsigned &TempOpIdx) const; + void parseGIMatchOperandPredicates(const std::vector &PredicateDefs, + OperandMatcher &OpndMatcher, + unsigned &TempOpIdx) const; + void parseGIMatchInsn(Record *InsnMatchDef, InstructionMatcher &InsnMatcher, + unsigned &TempOpIdx) const; + /// Analyze pattern \p P, returning a matcher for it if possible. /// Otherwise, return an Error explaining why we don't support it. Expected runOnPattern(const PatternToMatch &P); @@ -1357,9 +1526,11 @@ unsigned &TempOpIdx) const { OperandMatcher &OM = InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx); - if (SrcChild->hasAnyPredicate()) - return failedImport("Src pattern child has predicate (" + - explainPredicates(SrcChild) + ")"); + for (const auto &P : SrcChild->getPredicateFns()) { + if (P.isAlwaysTrue()) + continue; + OM.addPredicate(P); + } ArrayRef ChildTypes = SrcChild->getExtTypes(); if (ChildTypes.size() != 1) @@ -1450,7 +1621,13 @@ return true; } } - return failedImport("Dst pattern child isn't a leaf node or an MBB"); + + if (DstChild->getOperator()->isSubClassOf("SDNodeXForm")) + return failedImport("Dst pattern child isn't a leaf node or an MBB (it's " + "an SDNodeXForm)"); + + return failedImport("Dst pattern child isn't a leaf node or an MBB (" + + DstChild->getOperator()->getName() + ")"); } // Otherwise, we're looking for a bog-standard RegisterClass operand. @@ -1491,7 +1668,7 @@ RenderedOperands.push_back(OperandPlaceholder::CreateTemporary( OM.getAllocatedTemporariesBaseID() + I)); DstMIBuilder.addRenderer( - *ComplexPattern->second, RenderedOperands); + *ComplexPattern->second, DstChild->getName(), RenderedOperands); return true; } @@ -1601,7 +1778,9 @@ Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { // Keep track of the matchers and actions to emit. RuleMatcher M; - M.addAction(P); + M.addAction(llvm::to_string(*P.getSrcPattern()) + + " => " + + llvm::to_string(*P.getDstPattern())); auto TrueOrError = importRulePredicates(M, P.getPredicates()->getValues()); if (auto Error = TrueOrError.takeError()) @@ -1671,6 +1850,106 @@ return std::move(M); } +void GlobalISelEmitter::parseGIMatchInsnPredicates( + const std::vector &PredicateDefs, + InstructionMatcher &InsnMatcher) const { + for (Record *PredicateDef : PredicateDefs) { + if (PredicateDef->isSubClassOf("GIMatchOpcode")) { + InsnMatcher.addPredicate( + &Target.getInstruction(PredicateDef->getValueAsDef("Insn"))); + + continue; + } + + PrintFatalError(PredicateDef->getLoc(), "Matcher was not a GIMatchInsn"); + } +} + +void GlobalISelEmitter::parseGIMatchOperandPredicates( + const std::vector &PredicateDefs, OperandMatcher &OpndMatcher, + unsigned &TempOpIdx) const { + for (Record *PredicateMatchDef : PredicateDefs) { + if (PredicateMatchDef->isSubClassOf("GIMatchLLT")) { + Record *LLTDef = PredicateMatchDef->getValueAsDef("Type"); + if (LLTDef->isSubClassOf("LLTScalar")) { + OpndMatcher.addPredicate( + LLTCodeGen(LLT::scalar(LLTDef->getValueAsInt("SizeInBits")))); + + continue; + } + PrintFatalError(LLTDef->getLoc(), "Unknown LLT"); + } + + if (PredicateMatchDef->isSubClassOf("GIMatchRegBankForClass")) { + Record *RCDef = PredicateMatchDef->getValueAsDef("RC"); + OpndMatcher.addPredicate( + Target.getRegisterClass(RCDef)); + + continue; + } + + if (PredicateMatchDef->isSubClassOf("GIMatchComplexPattern")) { + Record *CPDef = PredicateMatchDef->getValueAsDef("Matcher"); + auto &Predicate = OpndMatcher.addPredicate( + OpndMatcher, *CPDef); + TempOpIdx += Predicate.countTemporaryOperands(); + + continue; + } + + if (PredicateMatchDef->isSubClassOf("GIMatchInt")) { + OpndMatcher.addPredicate( + PredicateMatchDef->getValueAsInt("Value")); + continue; + } + + if (PredicateMatchDef->isSubClassOf("GIMatchSubInsn")) { + auto &Predicate = OpndMatcher.addPredicate(); + parseGIMatchInsn(PredicateMatchDef->getValueAsDef("Insn"), + Predicate.getInsnMatcher(), TempOpIdx); + continue; + } + + if (PredicateMatchDef->isSubClassOf("GIMatchMBBImpl")) { + OpndMatcher.addPredicate(); + continue; + } + + PrintFatalError(PredicateMatchDef->getLoc(), + "Matcher was not a GIMatchOperandPredicate"); + } +} + +void GlobalISelEmitter::parseGIMatchOperands( + const std::vector &OperandDefs, InstructionMatcher &InsnMatcher, + unsigned &TempOpIdx) const { + unsigned OpIdx = 0; + for (Record *OperandMatchDef : OperandDefs) { + if (OperandMatchDef->isSubClassOf("GIMatchOperand")) { + OperandMatcher &OpndMatcher = InsnMatcher.addOperand( + OpIdx++, OperandMatchDef->getValueAsString("Name"), TempOpIdx); + + parseGIMatchOperandPredicates( + OperandMatchDef->getValueAsListOfDefs("Predicates"), OpndMatcher, + TempOpIdx); + continue; + } + + PrintFatalError(OperandMatchDef->getLoc(), "Matcher was not a GIMatchInsn"); + } +} + +void GlobalISelEmitter::parseGIMatchInsn(Record *InsnMatchDef, + InstructionMatcher &InsnMatcher, + unsigned &TempOpIdx) const { + assert(InsnMatchDef->isSubClassOf("GIMatchInsn")); + parseGIMatchInsnPredicates(InsnMatchDef->getValueAsListOfDefs("Predicates"), + InsnMatcher); + + parseGIMatchOperands(InsnMatchDef->getValueAsListOfDefs("Operands"), + InsnMatcher, TempOpIdx); +} + void GlobalISelEmitter::run(raw_ostream &OS) { // Track the GINodeEquiv definitions. gatherNodeEquivs(); @@ -1699,6 +1978,68 @@ Rules.push_back(std::move(MatcherOrErr.get())); } + for (Record *RuleDef : RK.getAllDerivedDefinitions("GIRule")) { + RuleMatcher Matcher; + + unsigned TempOpIdx = 0; + for (Record *InsnMatchDef : RuleDef->getValueAsListOfDefs("InsnMatchers")) { + InstructionMatcher &InsnMatcher = Matcher.addInstructionMatcher(); + parseGIMatchInsn(InsnMatchDef, InsnMatcher, TempOpIdx); + } + + Matcher.addAction(RuleDef->getName()); + for (Record *ActionDef : RuleDef->getValueAsListOfDefs("Actions")) { + if (ActionDef->isSubClassOf("GIBuildMI")) { + BuildMIAction &Action = Matcher.addAction( + &Target.getInstruction(ActionDef->getValueAsDef("I")), + Matcher.insnmatcher_front()); + + for (Record *RendererDef : ActionDef->getValueAsListOfDefs("Renderers")) { + if (RendererDef->isSubClassOf("GICopyOperand")) { + Action.addRenderer( + Matcher.insnmatcher_front(), + RendererDef->getValueAsString("Name")); + continue; + } + + if (RendererDef->isSubClassOf("GIAddRegister")) { + Action.addRenderer( + RendererDef->getValueAsDef("Reg")); + continue; + } + + if (RendererDef->isSubClassOf("GIAddImm")) { + Action.addRenderer( + RendererDef->getValueAsInt("Value")); + continue; + } + + if (RendererDef->isSubClassOf("GIAddComplexOperand")) { + std::string Name = RendererDef->getValueAsString("Name"); + + SmallVector RenderedOperands; + const OperandMatcher &OM = Matcher.insnmatcher_front().getOperand(Name); + for (unsigned I = 0; I < OM.countTemporaryOperands(); ++I) + RenderedOperands.push_back(OperandPlaceholder::CreateTemporary( + OM.getAllocatedTemporariesBaseID() + I)); + + Action.addRenderer( + *RendererDef->getValueAsDef("Matcher"), Name, RenderedOperands); + continue; + } + + PrintFatalError(RendererDef->getLoc(), "Renderer was not a GIOperandRenderer"); + } + + continue; + } + + PrintFatalError(ActionDef->getLoc(), "Action was not a GIAction"); + } + + Rules.push_back(std::move(Matcher)); + } + std::stable_sort(Rules.begin(), Rules.end(), [&](const RuleMatcher &A, const RuleMatcher &B) { if (A.isHigherPriorityThan(B)) { @@ -1731,6 +2072,7 @@ << " const MachineRegisterInfo &MRI = MF.getRegInfo();\n"; for (auto &Rule : Rules) { + Rule.emitTblgen(OS, 0); Rule.emit(OS); ++NumPatternEmitted; }