Skip to content

Commit

Permalink
[TableGen] Add a backend to generate MacroFusion predicators (#72222)
Browse files Browse the repository at this point in the history
`FusionPredicate` is used to predicate if target instruction matches
 the requirement. The targets can be firstMI, secondMI or both.

The `Fusion` contains a list of `FusionPredicate`. The generated code
will be like:
```
bool isNAME(const TargetInstrInfo &TII,
            const TargetSubtargetInfo &STI,
            const MachineInstr *FirstMI,
            const MachineInstr &SecondMI) {
  auto &MRI = SecondMI.getMF()->getRegInfo();
  /* Predicates */
  return true;
}
```

A boilerplate class called `SimpleFusion` is added. `SimpleFusion` has
a predefined structure of predicates and accepts predicate for
`firstMI`, predicate for `secondMI` and epilog/prolog as arguments.
The generated code for `SimpleFusion` will be like:
```
bool isNAME(const TargetInstrInfo &TII,
            const TargetSubtargetInfo &STI,
            const MachineInstr *FirstMI,
            const MachineInstr &SecondMI) {
  auto &MRI = SecondMI.getMF()->getRegInfo();
  /* Prolog */
  /* Predicate for `SecondMI` */
  /* Wildcard */
  /* Predicate for `FirstMI` */
  /* Check One Use */
  /* Tie registers */
  /* Epilog */
  return true;
}
```
  • Loading branch information
wangpc-pp committed Jan 5, 2024
1 parent 65df696 commit a0e6b7c
Show file tree
Hide file tree
Showing 7 changed files with 462 additions and 0 deletions.
6 changes: 6 additions & 0 deletions llvm/include/llvm/Target/TargetInstrPredicate.td
Expand Up @@ -95,6 +95,12 @@ class MCOperandPredicate<int Index> : MCInstPredicate {
// Return true if machine operand at position `Index` is a register operand.
class CheckIsRegOperand<int Index> : MCOperandPredicate<Index>;

// Return true if machine operand at position `Index` is a virtual register operand.
class CheckIsVRegOperand<int Index> : MCOperandPredicate<Index>;

// Return true if machine operand at position `Index` is not a virtual register operand.
class CheckIsNotVRegOperand<int Index> : CheckNot<CheckIsVRegOperand<Index>>;

// Return true if machine operand at position `Index` is an immediate operand.
class CheckIsImmOperand<int Index> : MCOperandPredicate<Index>;

Expand Down
113 changes: 113 additions & 0 deletions llvm/include/llvm/Target/TargetSchedule.td
Expand Up @@ -584,3 +584,116 @@ class MemoryQueue<ProcResourceKind PR> {

class LoadQueue<ProcResourceKind LDQueue> : MemoryQueue<LDQueue>;
class StoreQueue<ProcResourceKind STQueue> : MemoryQueue<STQueue>;

// The target instruction that FusionPredicate will be evaluated on.
class FusionTarget;
def first_fusion_target : FusionTarget;
def second_fusion_target : FusionTarget;
def both_fusion_target : FusionTarget;

// Base class of FusionPredicate, etc. The avaliable variables are:
// * const TargetInstrInfo &TII
// * const TargetSubtargetInfo &STI
// * const MachineRegisterInfo &MRI
// * const MachineInstr *FirstMI
// * const MachineInstr &SecondMI
class FusionPredicate<FusionTarget target> {
FusionTarget Target = target;
}
class FirstFusionPredicate: FusionPredicate<first_fusion_target>;
class SecondFusionPredicate: FusionPredicate<second_fusion_target>;
class BothFusionPredicate: FusionPredicate<both_fusion_target>;

// FusionPredicate with raw code predicate.
class FusionPredicateWithCode<code pred> : FusionPredicate<both_fusion_target> {
code Predicate = pred;
}

// FusionPredicate with MCInstPredicate.
class FusionPredicateWithMCInstPredicate<FusionTarget target, MCInstPredicate pred>
: FusionPredicate<target> {
MCInstPredicate Predicate = pred;
}
class FirstFusionPredicateWithMCInstPredicate<MCInstPredicate pred>
: FusionPredicateWithMCInstPredicate<first_fusion_target, pred>;
class SecondFusionPredicateWithMCInstPredicate<MCInstPredicate pred>
: FusionPredicateWithMCInstPredicate<second_fusion_target, pred>;
// The pred will be applied on both firstMI and secondMI.
class BothFusionPredicateWithMCInstPredicate<MCInstPredicate pred>
: FusionPredicateWithMCInstPredicate<second_fusion_target, pred>;

// Tie firstOpIdx and secondOpIdx. The operand of `FirstMI` at position
// `firstOpIdx` should be the same as the operand of `SecondMI` at position
// `secondOpIdx`.
class TieReg<int firstOpIdx, int secondOpIdx> : BothFusionPredicate {
int FirstOpIdx = firstOpIdx;
int SecondOpIdx = secondOpIdx;
}

// A predicate for wildcard. The generated code will be like:
// ```
// if (!FirstMI)
// return ReturnValue;
// ```
class WildcardPred<bit ret> : FirstFusionPredicate {
bit ReturnValue = ret;
}
def WildcardFalse : WildcardPred<0>;
def WildcardTrue : WildcardPred<1>;

// Indicates that the destination register of `FirstMI` should have one use if
// it is a virtual register.
class OneUsePred : FirstFusionPredicate;
def OneUse : OneUsePred;

// Handled by MacroFusionPredicatorEmitter backend.
// The generated predicator will be like:
// ```
// bool isNAME(const TargetInstrInfo &TII,
// const TargetSubtargetInfo &STI,
// const MachineInstr *FirstMI,
// const MachineInstr &SecondMI) {
// auto &MRI = SecondMI.getMF()->getRegInfo();
// /* Predicates */
// return true;
// }
// ```
class Fusion<list<FusionPredicate> predicates> {
list<FusionPredicate> Predicates = predicates;
}

// The generated predicator will be like:
// ```
// bool isNAME(const TargetInstrInfo &TII,
// const TargetSubtargetInfo &STI,
// const MachineInstr *FirstMI,
// const MachineInstr &SecondMI) {
// auto &MRI = SecondMI.getMF()->getRegInfo();
// /* Prolog */
// /* Predicate for `SecondMI` */
// /* Wildcard */
// /* Predicate for `FirstMI` */
// /* Check One Use */
// /* Tie registers */
// /* Epilog */
// return true;
// }
// ```
class SimpleFusion<MCInstPredicate firstPred, MCInstPredicate secondPred,
list<FusionPredicate> prolog = [],
list<FusionPredicate> epilog = []>
: Fusion<!listconcat(
prolog,
[
SecondFusionPredicateWithMCInstPredicate<secondPred>,
WildcardTrue,
FirstFusionPredicateWithMCInstPredicate<firstPred>,
SecondFusionPredicateWithMCInstPredicate<
CheckAny<[
CheckIsVRegOperand<0>,
CheckSameRegOperand<0, 1>
]>>,
OneUse,
TieReg<0, 1>,
],
epilog)>;
97 changes: 97 additions & 0 deletions llvm/test/TableGen/MacroFusion.td
@@ -0,0 +1,97 @@
// RUN: llvm-tblgen -gen-macro-fusion-pred -I %p/../../include %s | FileCheck %s --check-prefix=CHECK-PREDICATOR

include "llvm/Target/Target.td"

def TestInstrInfo : InstrInfo { }
def TestAsmWriter : AsmWriter {
int PassSubtarget = 1;
}

def Test : Target {
let InstructionSet = TestInstrInfo;
let AssemblyWriters = [TestAsmWriter];
}

let Namespace = "Test" in {
foreach i = 0-32 in {
def X#i : Register<"x"#i>;
}

def GPR : RegisterClass<"GPR", [i32], 32, (sequence "X%u", 0, 32)>;

class TestInst<int Opc> : Instruction {
field bits<32> Inst;
field bits<32> SoftFail = 0;
let Size = 4;
let Inst = Opc;
let OutOperandList = (outs);
let InOperandList = (ins);
let AsmString = NAME;
}
}

def Inst0 : TestInst<0>;
def Inst1 : TestInst<1>;

def TestFusion: SimpleFusion<CheckOpcode<[Inst0]>,
CheckAll<[
CheckOpcode<[Inst1]>,
CheckRegOperand<0, X0>
]>>;

// CHECK-PREDICATOR: #ifdef GET_Test_MACRO_FUSION_PRED_DECL
// CHECK-PREDICATOR-NEXT: #undef GET_Test_MACRO_FUSION_PRED_DECL
// CHECK-PREDICATOR-EMPTY:
// CHECK-PREDICATOR-NEXT: namespace llvm {
// CHECK-PREDICATOR-NEXT: bool isTestFusion(const TargetInstrInfo &, const TargetSubtargetInfo &, const MachineInstr *, const MachineInstr &);
// CHECK-PREDICATOR-NEXT: } // end namespace llvm
// CHECK-PREDICATOR-EMPTY:
// CHECK-PREDICATOR-NEXT: #endif

// CHECK-PREDICATOR: #ifdef GET_Test_MACRO_FUSION_PRED_IMPL
// CHECK-PREDICATOR-NEXT: #undef GET_Test_MACRO_FUSION_PRED_IMPL
// CHECK-PREDICATOR-EMPTY:
// CHECK-PREDICATOR-NEXT: namespace llvm {
// CHECK-PREDICATOR-NEXT: bool isTestFusion(
// CHECK-PREDICATOR-NEXT: const TargetInstrInfo &TII,
// CHECK-PREDICATOR-NEXT: const TargetSubtargetInfo &STI,
// CHECK-PREDICATOR-NEXT: const MachineInstr *FirstMI,
// CHECK-PREDICATOR-NEXT: const MachineInstr &SecondMI) {
// CHECK-PREDICATOR-NEXT: auto &MRI = SecondMI.getMF()->getRegInfo();
// CHECK-PREDICATOR-NEXT: {
// CHECK-PREDICATOR-NEXT: const MachineInstr *MI = &SecondMI;
// CHECK-PREDICATOR-NEXT: if (!(
// CHECK-PREDICATOR-NEXT: ( MI->getOpcode() == Test::Inst1 )
// CHECK-PREDICATOR-NEXT: && MI->getOperand(0).getReg() == Test::X0
// CHECK-PREDICATOR-NEXT: ))
// CHECK-PREDICATOR-NEXT: return false;
// CHECK-PREDICATOR-NEXT: }
// CHECK-PREDICATOR-NEXT: if (!FirstMI)
// CHECK-PREDICATOR-NEXT: return true;
// CHECK-PREDICATOR-NEXT: {
// CHECK-PREDICATOR-NEXT: const MachineInstr *MI = FirstMI;
// CHECK-PREDICATOR-NEXT: if (( MI->getOpcode() != Test::Inst0 ))
// CHECK-PREDICATOR-NEXT: return false;
// CHECK-PREDICATOR-NEXT: }
// CHECK-PREDICATOR-NEXT: {
// CHECK-PREDICATOR-NEXT: const MachineInstr *MI = &SecondMI;
// CHECK-PREDICATOR-NEXT: if (!(
// CHECK-PREDICATOR-NEXT: MI->getOperand(0).getReg().isVirtual()
// CHECK-PREDICATOR-NEXT: || MI->getOperand(0).getReg() == MI->getOperand(1).getReg()
// CHECK-PREDICATOR-NEXT: ))
// CHECK-PREDICATOR-NEXT: return false;
// CHECK-PREDICATOR-NEXT: }
// CHECK-PREDICATOR-NEXT: {
// CHECK-PREDICATOR-NEXT: Register FirstDest = FirstMI->getOperand(0).getReg();
// CHECK-PREDICATOR-NEXT: if (FirstDest.isVirtual() && !MRI.hasOneNonDBGUse(FirstDest))
// CHECK-PREDICATOR-NEXT: return false;
// CHECK-PREDICATOR-NEXT: }
// CHECK-PREDICATOR-NEXT: if (!(FirstMI->getOperand(0).isReg() &&
// CHECK-PREDICATOR-NEXT: SecondMI.getOperand(1).isReg() &&
// CHECK-PREDICATOR-NEXT: FirstMI->getOperand(0).getReg() == SecondMI.getOperand(1).getReg()))
// CHECK-PREDICATOR-NEXT: return false;
// CHECK-PREDICATOR-NEXT: return true;
// CHECK-PREDICATOR-NEXT: }
// CHECK-PREDICATOR-NEXT: } // end namespace llvm
// CHECK-PREDICATOR-EMPTY:
// CHECK-PREDICATOR-NEXT: #endif
1 change: 1 addition & 0 deletions llvm/utils/TableGen/CMakeLists.txt
Expand Up @@ -72,6 +72,7 @@ add_tablegen(llvm-tblgen LLVM
PredicateExpander.cpp
PseudoLoweringEmitter.cpp
CompressInstEmitter.cpp
MacroFusionPredicatorEmitter.cpp
RegisterBankEmitter.cpp
RegisterInfoEmitter.cpp
SearchableTableEmitter.cpp
Expand Down

0 comments on commit a0e6b7c

Please sign in to comment.