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 const RecordKeeper &Records;
56 const CodeGenTarget Target;
57
58 void emitMacroFusionDecl(ArrayRef<const Record *> Fusions,
59 PredicateExpander &PE, raw_ostream &OS);
60 void emitMacroFusionImpl(ArrayRef<const Record *> Fusions,
61 PredicateExpander &PE, raw_ostream &OS);
62 void emitPredicates(ArrayRef<const Record *> FirstPredicate,
63 bool IsCommutable, PredicateExpander &PE,
64 raw_ostream &OS);
65 void emitFirstPredicate(const Record *SecondPredicate, bool IsCommutable,
66 PredicateExpander &PE, raw_ostream &OS);
67 void emitSecondPredicate(const Record *SecondPredicate, bool IsCommutable,
68 PredicateExpander &PE, raw_ostream &OS);
69 void emitBothPredicate(const Record *Predicates, bool IsCommutable,
70 PredicateExpander &PE, raw_ostream &OS);
71
72public:
73 MacroFusionPredicatorEmitter(const RecordKeeper &R) : Records(R), Target(R) {}
74
75 void run(raw_ostream &OS);
76};
77} // End anonymous namespace.
78
79void MacroFusionPredicatorEmitter::emitMacroFusionDecl(
80 ArrayRef<const Record *> Fusions, PredicateExpander &PE, raw_ostream &OS) {
81 OS << "#ifdef GET_" << Target.getName() << "_MACRO_FUSION_PRED_DECL\n";
82 OS << "#undef GET_" << Target.getName() << "_MACRO_FUSION_PRED_DECL\n\n";
83 OS << "namespace llvm {\n";
84
85 for (const Record *Fusion : Fusions) {
86 OS << "bool is" << Fusion->getName() << "(const TargetInstrInfo &, "
87 << "const TargetSubtargetInfo &, "
88 << "const MachineInstr *, "
89 << "const MachineInstr &);\n";
90 }
91
92 OS << "} // end namespace llvm\n";
93 OS << "\n#endif\n";
94}
95
96void MacroFusionPredicatorEmitter::emitMacroFusionImpl(
97 ArrayRef<const Record *> Fusions, PredicateExpander &PE, raw_ostream &OS) {
98 OS << "#ifdef GET_" << Target.getName() << "_MACRO_FUSION_PRED_IMPL\n";
99 OS << "#undef GET_" << Target.getName() << "_MACRO_FUSION_PRED_IMPL\n\n";
100 OS << "namespace llvm {\n";
101
102 for (const Record *Fusion : Fusions) {
103 std::vector<const Record *> Predicates =
104 Fusion->getValueAsListOfDefs(FieldName: "Predicates");
105 bool IsCommutable = Fusion->getValueAsBit(FieldName: "IsCommutable");
106
107 OS << "bool is" << Fusion->getName() << "(\n";
108 OS.indent(NumSpaces: 4) << "const TargetInstrInfo &TII,\n";
109 OS.indent(NumSpaces: 4) << "const TargetSubtargetInfo &STI,\n";
110 OS.indent(NumSpaces: 4) << "const MachineInstr *FirstMI,\n";
111 OS.indent(NumSpaces: 4) << "const MachineInstr &SecondMI) {\n";
112 OS.indent(NumSpaces: 2)
113 << "[[maybe_unused]] auto &MRI = SecondMI.getMF()->getRegInfo();\n";
114
115 emitPredicates(FirstPredicate: Predicates, IsCommutable, PE, OS);
116
117 OS.indent(NumSpaces: 2) << "return true;\n";
118 OS << "}\n";
119 }
120
121 OS << "} // end namespace llvm\n";
122 OS << "\n#endif\n";
123}
124
125void MacroFusionPredicatorEmitter::emitPredicates(
126 ArrayRef<const Record *> Predicates, bool IsCommutable,
127 PredicateExpander &PE, raw_ostream &OS) {
128 for (const Record *Predicate : Predicates) {
129 const 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(const 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: "FirstInstHasSameReg")) {
159 int FirstOpIdx = Predicate->getValueAsInt(FieldName: "FirstOpIdx");
160 int SecondOpIdx = Predicate->getValueAsInt(FieldName: "SecondOpIdx");
161
162 OS.indent(NumSpaces: 2) << "if (!FirstMI->getOperand(" << FirstOpIdx
163 << ").getReg().isVirtual()) {\n";
164 OS.indent(NumSpaces: 4) << "if (FirstMI->getOperand(" << FirstOpIdx
165 << ").getReg() != FirstMI->getOperand(" << SecondOpIdx
166 << ").getReg())";
167
168 if (IsCommutable) {
169 OS << " {\n";
170 OS.indent(NumSpaces: 6) << "if (!FirstMI->getDesc().isCommutable())\n";
171 OS.indent(NumSpaces: 6) << " return false;\n";
172
173 OS.indent(NumSpaces: 6)
174 << "unsigned SrcOpIdx1 = " << SecondOpIdx
175 << ", SrcOpIdx2 = TargetInstrInfo::CommuteAnyOperandIndex;\n";
176 OS.indent(NumSpaces: 6)
177 << "if (TII.findCommutedOpIndices(FirstMI, SrcOpIdx1, SrcOpIdx2))\n";
178 OS.indent(NumSpaces: 6)
179 << " if (FirstMI->getOperand(" << FirstOpIdx
180 << ").getReg() != FirstMI->getOperand(SrcOpIdx2).getReg())\n";
181 OS.indent(NumSpaces: 6) << " return false;\n";
182 OS.indent(NumSpaces: 4) << "}\n";
183 } else {
184 OS << "\n";
185 OS.indent(NumSpaces: 4) << " return false;\n";
186 }
187 OS.indent(NumSpaces: 2) << "}\n";
188 } else if (Predicate->isSubClassOf(Name: "FusionPredicateWithMCInstPredicate")) {
189 OS.indent(NumSpaces: 2) << "{\n";
190 OS.indent(NumSpaces: 4) << "const MachineInstr *MI = FirstMI;\n";
191 OS.indent(NumSpaces: 4) << "if (";
192 PE.setNegatePredicate(true);
193 PE.getIndent() = 3;
194 PE.expandPredicate(OS, Rec: Predicate->getValueAsDef(FieldName: "Predicate"));
195 OS << ")\n";
196 OS.indent(NumSpaces: 4) << " return false;\n";
197 OS.indent(NumSpaces: 2) << "}\n";
198 } else {
199 PrintFatalError(ErrorLoc: Predicate->getLoc(),
200 Msg: "Unsupported predicate for first instruction: " +
201 Predicate->getType()->getAsString());
202 }
203}
204
205void MacroFusionPredicatorEmitter::emitSecondPredicate(const Record *Predicate,
206 bool IsCommutable,
207 PredicateExpander &PE,
208 raw_ostream &OS) {
209 if (Predicate->isSubClassOf(Name: "FusionPredicateWithMCInstPredicate")) {
210 OS.indent(NumSpaces: 2) << "{\n";
211 OS.indent(NumSpaces: 4) << "const MachineInstr *MI = &SecondMI;\n";
212 OS.indent(NumSpaces: 4) << "if (";
213 PE.setNegatePredicate(true);
214 PE.getIndent() = 3;
215 PE.expandPredicate(OS, Rec: Predicate->getValueAsDef(FieldName: "Predicate"));
216 OS << ")\n";
217 OS.indent(NumSpaces: 4) << " return false;\n";
218 OS.indent(NumSpaces: 2) << "}\n";
219 } else if (Predicate->isSubClassOf(Name: "SecondInstHasSameReg")) {
220 int FirstOpIdx = Predicate->getValueAsInt(FieldName: "FirstOpIdx");
221 int SecondOpIdx = Predicate->getValueAsInt(FieldName: "SecondOpIdx");
222
223 OS.indent(NumSpaces: 2) << "if (!SecondMI.getOperand(" << FirstOpIdx
224 << ").getReg().isVirtual()) {\n";
225 OS.indent(NumSpaces: 4) << "if (SecondMI.getOperand(" << FirstOpIdx
226 << ").getReg() != SecondMI.getOperand(" << SecondOpIdx
227 << ").getReg())";
228
229 if (IsCommutable) {
230 OS << " {\n";
231 OS.indent(NumSpaces: 6) << "if (!SecondMI.getDesc().isCommutable())\n";
232 OS.indent(NumSpaces: 6) << " return false;\n";
233
234 OS.indent(NumSpaces: 6)
235 << "unsigned SrcOpIdx1 = " << SecondOpIdx
236 << ", SrcOpIdx2 = TargetInstrInfo::CommuteAnyOperandIndex;\n";
237 OS.indent(NumSpaces: 6)
238 << "if (TII.findCommutedOpIndices(SecondMI, SrcOpIdx1, SrcOpIdx2))\n";
239 OS.indent(NumSpaces: 6)
240 << " if (SecondMI.getOperand(" << FirstOpIdx
241 << ").getReg() != SecondMI.getOperand(SrcOpIdx2).getReg())\n";
242 OS.indent(NumSpaces: 6) << " return false;\n";
243 OS.indent(NumSpaces: 4) << "}\n";
244 } else {
245 OS << "\n";
246 OS.indent(NumSpaces: 4) << " return false;\n";
247 }
248 OS.indent(NumSpaces: 2) << "}\n";
249 } else {
250 PrintFatalError(ErrorLoc: Predicate->getLoc(),
251 Msg: "Unsupported predicate for second instruction: " +
252 Predicate->getType()->getAsString());
253 }
254}
255
256void MacroFusionPredicatorEmitter::emitBothPredicate(const Record *Predicate,
257 bool IsCommutable,
258 PredicateExpander &PE,
259 raw_ostream &OS) {
260 if (Predicate->isSubClassOf(Name: "FusionPredicateWithCode"))
261 OS << Predicate->getValueAsString(FieldName: "Predicate");
262 else if (Predicate->isSubClassOf(Name: "BothFusionPredicateWithMCInstPredicate")) {
263 emitFirstPredicate(Predicate, IsCommutable, PE, OS);
264 emitSecondPredicate(Predicate, IsCommutable, PE, OS);
265 } else if (Predicate->isSubClassOf(Name: "TieReg")) {
266 int FirstOpIdx = Predicate->getValueAsInt(FieldName: "FirstOpIdx");
267 int SecondOpIdx = Predicate->getValueAsInt(FieldName: "SecondOpIdx");
268 OS.indent(NumSpaces: 2) << "if (!(FirstMI->getOperand(" << FirstOpIdx
269 << ").isReg() &&\n";
270 OS.indent(NumSpaces: 2) << " SecondMI.getOperand(" << SecondOpIdx
271 << ").isReg() &&\n";
272 OS.indent(NumSpaces: 2) << " FirstMI->getOperand(" << FirstOpIdx
273 << ").getReg() == SecondMI.getOperand(" << SecondOpIdx
274 << ").getReg()))";
275
276 if (IsCommutable) {
277 OS << " {\n";
278 OS.indent(NumSpaces: 4) << "if (!SecondMI.getDesc().isCommutable())\n";
279 OS.indent(NumSpaces: 4) << " return false;\n";
280
281 OS.indent(NumSpaces: 4)
282 << "unsigned SrcOpIdx1 = " << SecondOpIdx
283 << ", SrcOpIdx2 = TargetInstrInfo::CommuteAnyOperandIndex;\n";
284 OS.indent(NumSpaces: 4)
285 << "if (TII.findCommutedOpIndices(SecondMI, SrcOpIdx1, SrcOpIdx2))\n";
286 OS.indent(NumSpaces: 4)
287 << " if (FirstMI->getOperand(" << FirstOpIdx
288 << ").getReg() != SecondMI.getOperand(SrcOpIdx2).getReg())\n";
289 OS.indent(NumSpaces: 4) << " return false;\n";
290 OS.indent(NumSpaces: 2) << "}";
291 } else {
292 OS << "\n";
293 OS.indent(NumSpaces: 2) << " return false;";
294 }
295 OS << "\n";
296 } else {
297 PrintFatalError(ErrorLoc: Predicate->getLoc(),
298 Msg: "Unsupported predicate for both instruction: " +
299 Predicate->getType()->getAsString());
300 }
301}
302
303void MacroFusionPredicatorEmitter::run(raw_ostream &OS) {
304 // Emit file header.
305 emitSourceFileHeader(Desc: "Macro Fusion Predicators", OS);
306
307 PredicateExpander PE(Target.getName());
308 PE.setByRef(false);
309 PE.setExpandForMC(false);
310
311 ArrayRef<const Record *> Fusions = Records.getAllDerivedDefinitions(ClassName: "Fusion");
312 emitMacroFusionDecl(Fusions, PE, OS);
313 OS << "\n";
314 emitMacroFusionImpl(Fusions, PE, OS);
315}
316
317static TableGen::Emitter::OptClass<MacroFusionPredicatorEmitter>
318 X("gen-macro-fusion-pred", "Generate macro fusion predicators.");
319