1//===------ MacroFusionPredicatorEmitter.cpp - Generator for Fusion ------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===---------------------------------------------------------------------===//
8//
9// MacroFusionPredicatorEmitter implements a TableGen-driven predicators
10// generator for macro-op fusions.
11//
12// This TableGen backend processes `Fusion` definitions and generates
13// predicators for checking if input instructions can be fused. These
14// predicators can used in `MacroFusion` DAG mutation.
15//
16// The generated header file contains two parts: one for predicator
17// declarations and one for predicator implementations. The user can get them
18// by defining macro `GET_<TargetName>_MACRO_FUSION_PRED_DECL` or
19// `GET_<TargetName>_MACRO_FUSION_PRED_IMPL` and then including the generated
20// header file.
21//
22// The generated predicator will be like:
23//
24// ```
25// bool isNAME(const TargetInstrInfo &TII,
26// const TargetSubtargetInfo &STI,
27// const MachineInstr *FirstMI,
28// const MachineInstr &SecondMI) {
29// auto &MRI = SecondMI.getMF()->getRegInfo();
30// /* Predicates */
31// return true;
32// }
33// ```
34//
35// The `Predicates` part is generated from a list of `FusionPredicate`, which
36// can be predefined predicates, a raw code string or `MCInstPredicate` defined
37// in TargetInstrPredicate.td.
38//
39//===---------------------------------------------------------------------===//
40
41#include "Common/CodeGenTarget.h"
42#include "Common/PredicateExpander.h"
43#include "llvm/Support/Debug.h"
44#include "llvm/TableGen/Error.h"
45#include "llvm/TableGen/Record.h"
46#include "llvm/TableGen/TableGenBackend.h"
47#include <vector>
48
49using namespace llvm;
50
51#define DEBUG_TYPE "macro-fusion-predicator"
52
53namespace {
54class MacroFusionPredicatorEmitter {
55 RecordKeeper &Records;
56 CodeGenTarget Target;
57
58 void emitMacroFusionDecl(ArrayRef<Record *> Fusions, PredicateExpander &PE,
59 raw_ostream &OS);
60 void emitMacroFusionImpl(ArrayRef<Record *> Fusions, PredicateExpander &PE,
61 raw_ostream &OS);
62 void emitPredicates(ArrayRef<Record *> FirstPredicate, bool IsCommutable,
63 PredicateExpander &PE, raw_ostream &OS);
64 void emitFirstPredicate(Record *SecondPredicate, bool IsCommutable,
65 PredicateExpander &PE, raw_ostream &OS);
66 void emitSecondPredicate(Record *SecondPredicate, bool IsCommutable,
67 PredicateExpander &PE, raw_ostream &OS);
68 void emitBothPredicate(Record *Predicates, bool IsCommutable,
69 PredicateExpander &PE, raw_ostream &OS);
70
71public:
72 MacroFusionPredicatorEmitter(RecordKeeper &R) : Records(R), Target(R) {}
73
74 void run(raw_ostream &OS);
75};
76} // End anonymous namespace.
77
78void MacroFusionPredicatorEmitter::emitMacroFusionDecl(
79 ArrayRef<Record *> Fusions, PredicateExpander &PE, raw_ostream &OS) {
80 OS << "#ifdef GET_" << Target.getName() << "_MACRO_FUSION_PRED_DECL\n";
81 OS << "#undef GET_" << Target.getName() << "_MACRO_FUSION_PRED_DECL\n\n";
82 OS << "namespace llvm {\n";
83
84 for (Record *Fusion : Fusions) {
85 OS << "bool is" << Fusion->getName() << "(const TargetInstrInfo &, "
86 << "const TargetSubtargetInfo &, "
87 << "const MachineInstr *, "
88 << "const MachineInstr &);\n";
89 }
90
91 OS << "} // end namespace llvm\n";
92 OS << "\n#endif\n";
93}
94
95void MacroFusionPredicatorEmitter::emitMacroFusionImpl(
96 ArrayRef<Record *> Fusions, PredicateExpander &PE, raw_ostream &OS) {
97 OS << "#ifdef GET_" << Target.getName() << "_MACRO_FUSION_PRED_IMPL\n";
98 OS << "#undef GET_" << Target.getName() << "_MACRO_FUSION_PRED_IMPL\n\n";
99 OS << "namespace llvm {\n";
100
101 for (Record *Fusion : Fusions) {
102 std::vector<Record *> Predicates =
103 Fusion->getValueAsListOfDefs(FieldName: "Predicates");
104 bool IsCommutable = Fusion->getValueAsBit(FieldName: "IsCommutable");
105
106 OS << "bool is" << Fusion->getName() << "(\n";
107 OS.indent(NumSpaces: 4) << "const TargetInstrInfo &TII,\n";
108 OS.indent(NumSpaces: 4) << "const TargetSubtargetInfo &STI,\n";
109 OS.indent(NumSpaces: 4) << "const MachineInstr *FirstMI,\n";
110 OS.indent(NumSpaces: 4) << "const MachineInstr &SecondMI) {\n";
111 OS.indent(NumSpaces: 2)
112 << "[[maybe_unused]] auto &MRI = SecondMI.getMF()->getRegInfo();\n";
113
114 emitPredicates(FirstPredicate: Predicates, IsCommutable, PE, OS);
115
116 OS.indent(NumSpaces: 2) << "return true;\n";
117 OS << "}\n";
118 }
119
120 OS << "} // end namespace llvm\n";
121 OS << "\n#endif\n";
122}
123
124void MacroFusionPredicatorEmitter::emitPredicates(ArrayRef<Record *> Predicates,
125 bool IsCommutable,
126 PredicateExpander &PE,
127 raw_ostream &OS) {
128 for (Record *Predicate : Predicates) {
129 Record *Target = Predicate->getValueAsDef(FieldName: "Target");
130 if (Target->getName() == "first_fusion_target")
131 emitFirstPredicate(SecondPredicate: Predicate, IsCommutable, PE, OS);
132 else if (Target->getName() == "second_fusion_target")
133 emitSecondPredicate(SecondPredicate: Predicate, IsCommutable, PE, OS);
134 else if (Target->getName() == "both_fusion_target")
135 emitBothPredicate(Predicates: Predicate, IsCommutable, PE, OS);
136 else
137 PrintFatalError(ErrorLoc: Target->getLoc(),
138 Msg: "Unsupported 'FusionTarget': " + Target->getName());
139 }
140}
141
142void MacroFusionPredicatorEmitter::emitFirstPredicate(Record *Predicate,
143 bool IsCommutable,
144 PredicateExpander &PE,
145 raw_ostream &OS) {
146 if (Predicate->isSubClassOf(Name: "WildcardPred")) {
147 OS.indent(NumSpaces: 2) << "if (!FirstMI)\n";
148 OS.indent(NumSpaces: 2) << " return "
149 << (Predicate->getValueAsBit(FieldName: "ReturnValue") ? "true" : "false")
150 << ";\n";
151 } else if (Predicate->isSubClassOf(Name: "OneUsePred")) {
152 OS.indent(NumSpaces: 2) << "{\n";
153 OS.indent(NumSpaces: 4) << "Register FirstDest = FirstMI->getOperand(0).getReg();\n";
154 OS.indent(NumSpaces: 4)
155 << "if (FirstDest.isVirtual() && !MRI.hasOneNonDBGUse(FirstDest))\n";
156 OS.indent(NumSpaces: 4) << " return false;\n";
157 OS.indent(NumSpaces: 2) << "}\n";
158 } else if (Predicate->isSubClassOf(Name: "FusionPredicateWithMCInstPredicate")) {
159 OS.indent(NumSpaces: 2) << "{\n";
160 OS.indent(NumSpaces: 4) << "const MachineInstr *MI = FirstMI;\n";
161 OS.indent(NumSpaces: 4) << "if (";
162 PE.setNegatePredicate(true);
163 PE.setIndentLevel(3);
164 PE.expandPredicate(OS, Rec: Predicate->getValueAsDef(FieldName: "Predicate"));
165 OS << ")\n";
166 OS.indent(NumSpaces: 4) << " return false;\n";
167 OS.indent(NumSpaces: 2) << "}\n";
168 } else {
169 PrintFatalError(ErrorLoc: Predicate->getLoc(),
170 Msg: "Unsupported predicate for first instruction: " +
171 Predicate->getType()->getAsString());
172 }
173}
174
175void MacroFusionPredicatorEmitter::emitSecondPredicate(Record *Predicate,
176 bool IsCommutable,
177 PredicateExpander &PE,
178 raw_ostream &OS) {
179 if (Predicate->isSubClassOf(Name: "FusionPredicateWithMCInstPredicate")) {
180 OS.indent(NumSpaces: 2) << "{\n";
181 OS.indent(NumSpaces: 4) << "const MachineInstr *MI = &SecondMI;\n";
182 OS.indent(NumSpaces: 4) << "if (";
183 PE.setNegatePredicate(true);
184 PE.setIndentLevel(3);
185 PE.expandPredicate(OS, Rec: Predicate->getValueAsDef(FieldName: "Predicate"));
186 OS << ")\n";
187 OS.indent(NumSpaces: 4) << " return false;\n";
188 OS.indent(NumSpaces: 2) << "}\n";
189 } else if (Predicate->isSubClassOf(Name: "SameReg")) {
190 int FirstOpIdx = Predicate->getValueAsInt(FieldName: "FirstOpIdx");
191 int SecondOpIdx = Predicate->getValueAsInt(FieldName: "SecondOpIdx");
192
193 OS.indent(NumSpaces: 2) << "if (!SecondMI.getOperand(" << FirstOpIdx
194 << ").getReg().isVirtual()) {\n";
195 OS.indent(NumSpaces: 4) << "if (SecondMI.getOperand(" << FirstOpIdx
196 << ").getReg() != SecondMI.getOperand(" << SecondOpIdx
197 << ").getReg())";
198
199 if (IsCommutable) {
200 OS << " {\n";
201 OS.indent(NumSpaces: 6) << "if (!SecondMI.getDesc().isCommutable())\n";
202 OS.indent(NumSpaces: 6) << " return false;\n";
203
204 OS.indent(NumSpaces: 6)
205 << "unsigned SrcOpIdx1 = " << SecondOpIdx
206 << ", SrcOpIdx2 = TargetInstrInfo::CommuteAnyOperandIndex;\n";
207 OS.indent(NumSpaces: 6)
208 << "if (TII.findCommutedOpIndices(SecondMI, SrcOpIdx1, SrcOpIdx2))\n";
209 OS.indent(NumSpaces: 6)
210 << " if (SecondMI.getOperand(" << FirstOpIdx
211 << ").getReg() != SecondMI.getOperand(SrcOpIdx2).getReg())\n";
212 OS.indent(NumSpaces: 6) << " return false;\n";
213 OS.indent(NumSpaces: 4) << "}\n";
214 } else {
215 OS << "\n";
216 OS.indent(NumSpaces: 4) << " return false;\n";
217 }
218 OS.indent(NumSpaces: 2) << "}\n";
219 } else {
220 PrintFatalError(ErrorLoc: Predicate->getLoc(),
221 Msg: "Unsupported predicate for second instruction: " +
222 Predicate->getType()->getAsString());
223 }
224}
225
226void MacroFusionPredicatorEmitter::emitBothPredicate(Record *Predicate,
227 bool IsCommutable,
228 PredicateExpander &PE,
229 raw_ostream &OS) {
230 if (Predicate->isSubClassOf(Name: "FusionPredicateWithCode"))
231 OS << Predicate->getValueAsString(FieldName: "Predicate");
232 else if (Predicate->isSubClassOf(Name: "BothFusionPredicateWithMCInstPredicate")) {
233 emitFirstPredicate(Predicate, IsCommutable, PE, OS);
234 emitSecondPredicate(Predicate, IsCommutable, PE, OS);
235 } else if (Predicate->isSubClassOf(Name: "TieReg")) {
236 int FirstOpIdx = Predicate->getValueAsInt(FieldName: "FirstOpIdx");
237 int SecondOpIdx = Predicate->getValueAsInt(FieldName: "SecondOpIdx");
238 OS.indent(NumSpaces: 2) << "if (!(FirstMI->getOperand(" << FirstOpIdx
239 << ").isReg() &&\n";
240 OS.indent(NumSpaces: 2) << " SecondMI.getOperand(" << SecondOpIdx
241 << ").isReg() &&\n";
242 OS.indent(NumSpaces: 2) << " FirstMI->getOperand(" << FirstOpIdx
243 << ").getReg() == SecondMI.getOperand(" << SecondOpIdx
244 << ").getReg()))";
245
246 if (IsCommutable) {
247 OS << " {\n";
248 OS.indent(NumSpaces: 4) << "if (!SecondMI.getDesc().isCommutable())\n";
249 OS.indent(NumSpaces: 4) << " return false;\n";
250
251 OS.indent(NumSpaces: 4)
252 << "unsigned SrcOpIdx1 = " << SecondOpIdx
253 << ", SrcOpIdx2 = TargetInstrInfo::CommuteAnyOperandIndex;\n";
254 OS.indent(NumSpaces: 4)
255 << "if (TII.findCommutedOpIndices(SecondMI, SrcOpIdx1, SrcOpIdx2))\n";
256 OS.indent(NumSpaces: 4)
257 << " if (FirstMI->getOperand(" << FirstOpIdx
258 << ").getReg() != SecondMI.getOperand(SrcOpIdx2).getReg())\n";
259 OS.indent(NumSpaces: 4) << " return false;\n";
260 OS.indent(NumSpaces: 2) << "}";
261 } else {
262 OS << "\n";
263 OS.indent(NumSpaces: 2) << " return false;";
264 }
265 OS << "\n";
266 } else
267 PrintFatalError(ErrorLoc: Predicate->getLoc(),
268 Msg: "Unsupported predicate for both instruction: " +
269 Predicate->getType()->getAsString());
270}
271
272void MacroFusionPredicatorEmitter::run(raw_ostream &OS) {
273 // Emit file header.
274 emitSourceFileHeader(Desc: "Macro Fusion Predicators", OS);
275
276 PredicateExpander PE(Target.getName());
277 PE.setByRef(false);
278 PE.setExpandForMC(false);
279
280 std::vector<Record *> Fusions = Records.getAllDerivedDefinitions(ClassName: "Fusion");
281 // Sort macro fusions by name.
282 sort(C&: Fusions, Comp: LessRecord());
283 emitMacroFusionDecl(Fusions, PE, OS);
284 OS << "\n";
285 emitMacroFusionImpl(Fusions, PE, OS);
286}
287
288static TableGen::Emitter::OptClass<MacroFusionPredicatorEmitter>
289 X("gen-macro-fusion-pred", "Generate macro fusion predicators.");
290