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// Each predicator also maintains `Statistic`s that count how often the fusion
23// is matched, split into pre-RA and post-RA counters because both schedulers
24// run MacroFusion.
25//
26// The generated predicator will be like:
27//
28// ```
29// STATISTIC(NumNAMEPreRA, "Times NAME Triggered (pre-ra)");
30// STATISTIC(NumNAMEPostRA, "Times NAME Triggered (post-ra)");
31// bool isNAME(const TargetInstrInfo &TII,
32// const TargetSubtargetInfo &STI,
33// const MachineInstr *FirstMI,
34// const MachineInstr &SecondMI) {
35// auto &MRI = SecondMI.getMF()->getRegInfo();
36// /* Predicates */
37// if (SecondMI.getMF()->getProperties().hasNoVRegs())
38// ++NumNAMEPostRA;
39// else
40// ++NumNAMEPreRA;
41// return true;
42// }
43// ```
44//
45// The `Predicates` part is generated from a list of `FusionPredicate`, which
46// can be predefined predicates, a raw code string or `MCInstPredicate` defined
47// in TargetInstrPredicate.td.
48//
49//===---------------------------------------------------------------------===//
50
51#include "Common/CodeGenTarget.h"
52#include "Common/PredicateExpander.h"
53#include "llvm/Support/Debug.h"
54#include "llvm/TableGen/CodeGenHelpers.h"
55#include "llvm/TableGen/Error.h"
56#include "llvm/TableGen/Record.h"
57#include "llvm/TableGen/TableGenBackend.h"
58#include <vector>
59
60using namespace llvm;
61
62#define DEBUG_TYPE "macro-fusion-predicator"
63
64namespace {
65class MacroFusionPredicatorEmitter {
66 const RecordKeeper &Records;
67 const CodeGenTarget Target;
68
69 void emitMacroFusionDecl(ArrayRef<const Record *> Fusions,
70 PredicateExpander &PE, raw_ostream &OS);
71 void emitMacroFusionImpl(ArrayRef<const Record *> Fusions,
72 PredicateExpander &PE, raw_ostream &OS);
73 void emitPredicates(ArrayRef<const Record *> FirstPredicate,
74 bool IsCommutable, PredicateExpander &PE,
75 raw_ostream &OS);
76 void emitFirstPredicate(const Record *SecondPredicate, bool IsCommutable,
77 PredicateExpander &PE, raw_ostream &OS);
78 void emitSecondPredicate(const Record *SecondPredicate, bool IsCommutable,
79 PredicateExpander &PE, raw_ostream &OS);
80 void emitBothPredicate(const Record *Predicates, bool IsCommutable,
81 PredicateExpander &PE, raw_ostream &OS);
82
83public:
84 MacroFusionPredicatorEmitter(const RecordKeeper &R) : Records(R), Target(R) {}
85
86 void run(raw_ostream &OS);
87};
88} // End anonymous namespace.
89
90void MacroFusionPredicatorEmitter::emitMacroFusionDecl(
91 ArrayRef<const Record *> Fusions, PredicateExpander &PE, raw_ostream &OS) {
92 IfDefEmitter IfDef(
93 OS, ("GET_" + Target.getName() + "_MACRO_FUSION_PRED_DECL").str());
94 NamespaceEmitter LlvmNS(OS, "llvm");
95
96 for (const Record *Fusion : Fusions)
97 OS << "bool is" << Fusion->getName() << "(const TargetInstrInfo &, "
98 << "const TargetSubtargetInfo &, const MachineInstr *, "
99 << "const MachineInstr &);\n";
100}
101
102void MacroFusionPredicatorEmitter::emitMacroFusionImpl(
103 ArrayRef<const Record *> Fusions, PredicateExpander &PE, raw_ostream &OS) {
104 IfDefEmitter IfDef(
105 OS, ("GET_" + Target.getName() + "_MACRO_FUSION_PRED_IMPL").str());
106 NamespaceEmitter LlvmNS(OS, "llvm");
107
108 for (const Record *Fusion : Fusions) {
109 std::vector<const Record *> Predicates =
110 Fusion->getValueAsListOfDefs(FieldName: "Predicates");
111 bool IsCommutable = Fusion->getValueAsBit(FieldName: "IsCommutable");
112
113 // Emit the statistics that count how often this fusion is matched. The
114 // pre-RA and post-RA schedulers both run MacroFusion, so they are split
115 // into separate counters (distinguished below via the `NoVRegs` property)
116 // to avoid conflating the two.
117 OS << "STATISTIC(Num" << Fusion->getName() << "PreRA, \"Times "
118 << Fusion->getName() << " Triggered (pre-ra)\");\n";
119 OS << "STATISTIC(Num" << Fusion->getName() << "PostRA, \"Times "
120 << Fusion->getName() << " Triggered (post-ra)\");\n";
121
122 OS << "bool is" << Fusion->getName() << "(\n";
123 OS.indent(NumSpaces: 4) << "const TargetInstrInfo &TII,\n";
124 OS.indent(NumSpaces: 4) << "const TargetSubtargetInfo &STI,\n";
125 OS.indent(NumSpaces: 4) << "const MachineInstr *FirstMI,\n";
126 OS.indent(NumSpaces: 4) << "const MachineInstr &SecondMI) {\n";
127 OS.indent(NumSpaces: 2)
128 << "[[maybe_unused]] auto &MRI = SecondMI.getMF()->getRegInfo();\n";
129
130 emitPredicates(FirstPredicate: Predicates, IsCommutable, PE, OS);
131
132 OS.indent(NumSpaces: 2) << "if (SecondMI.getMF()->getProperties().hasNoVRegs())\n";
133 OS.indent(NumSpaces: 4) << "++Num" << Fusion->getName() << "PostRA;\n";
134 OS.indent(NumSpaces: 2) << "else\n";
135 OS.indent(NumSpaces: 4) << "++Num" << Fusion->getName() << "PreRA;\n";
136
137 OS.indent(NumSpaces: 2) << "return true;\n";
138 OS << "}\n";
139 }
140}
141
142void MacroFusionPredicatorEmitter::emitPredicates(
143 ArrayRef<const Record *> Predicates, bool IsCommutable,
144 PredicateExpander &PE, raw_ostream &OS) {
145 for (const Record *Predicate : Predicates) {
146 const Record *Target = Predicate->getValueAsDef(FieldName: "Target");
147 if (Target->getName() == "first_fusion_target")
148 emitFirstPredicate(SecondPredicate: Predicate, IsCommutable, PE, OS);
149 else if (Target->getName() == "second_fusion_target")
150 emitSecondPredicate(SecondPredicate: Predicate, IsCommutable, PE, OS);
151 else if (Target->getName() == "both_fusion_target")
152 emitBothPredicate(Predicates: Predicate, IsCommutable, PE, OS);
153 else
154 PrintFatalError(ErrorLoc: Target->getLoc(),
155 Msg: "Unsupported 'FusionTarget': " + Target->getName());
156 }
157}
158
159void MacroFusionPredicatorEmitter::emitFirstPredicate(const Record *Predicate,
160 bool IsCommutable,
161 PredicateExpander &PE,
162 raw_ostream &OS) {
163 if (Predicate->isSubClassOf(Name: "WildcardPred")) {
164 OS.indent(NumSpaces: 2) << "if (!FirstMI)\n";
165 OS.indent(NumSpaces: 2) << " return "
166 << (Predicate->getValueAsBit(FieldName: "ReturnValue") ? "true" : "false")
167 << ";\n";
168 } else if (Predicate->isSubClassOf(Name: "OneUsePred")) {
169 OS.indent(NumSpaces: 2) << "{\n";
170 OS.indent(NumSpaces: 4) << "Register FirstDest = FirstMI->getOperand(0).getReg();\n";
171 OS.indent(NumSpaces: 4)
172 << "if (FirstDest.isVirtual() && !MRI.hasOneNonDBGUse(FirstDest))\n";
173 OS.indent(NumSpaces: 4) << " return false;\n";
174 OS.indent(NumSpaces: 2) << "}\n";
175 } else if (Predicate->isSubClassOf(Name: "FirstInstHasSameReg")) {
176 int FirstOpIdx = Predicate->getValueAsInt(FieldName: "FirstOpIdx");
177 int SecondOpIdx = Predicate->getValueAsInt(FieldName: "SecondOpIdx");
178
179 OS.indent(NumSpaces: 2) << "if (!FirstMI->getOperand(" << FirstOpIdx
180 << ").getReg().isVirtual()) {\n";
181 OS.indent(NumSpaces: 4) << "if (FirstMI->getOperand(" << FirstOpIdx
182 << ").getReg() != FirstMI->getOperand(" << SecondOpIdx
183 << ").getReg())";
184
185 if (IsCommutable) {
186 OS << " {\n";
187 OS.indent(NumSpaces: 6) << "if (!FirstMI->getDesc().isCommutable())\n";
188 OS.indent(NumSpaces: 6) << " return false;\n";
189
190 OS.indent(NumSpaces: 6)
191 << "unsigned SrcOpIdx1 = " << SecondOpIdx
192 << ", SrcOpIdx2 = TargetInstrInfo::CommuteAnyOperandIndex;\n";
193 OS.indent(NumSpaces: 6)
194 << "if (TII.findCommutedOpIndices(FirstMI, SrcOpIdx1, SrcOpIdx2))\n";
195 OS.indent(NumSpaces: 6)
196 << " if (FirstMI->getOperand(" << FirstOpIdx
197 << ").getReg() != FirstMI->getOperand(SrcOpIdx2).getReg())\n";
198 OS.indent(NumSpaces: 6) << " return false;\n";
199 OS.indent(NumSpaces: 4) << "}\n";
200 } else {
201 OS << "\n";
202 OS.indent(NumSpaces: 4) << " return false;\n";
203 }
204 OS.indent(NumSpaces: 2) << "}\n";
205 } else if (Predicate->isSubClassOf(Name: "FusionPredicateWithMCInstPredicate")) {
206 OS.indent(NumSpaces: 2) << "{\n";
207 OS.indent(NumSpaces: 4) << "[[maybe_unused]] const MachineInstr *MI = FirstMI;\n";
208 OS.indent(NumSpaces: 4) << "if (";
209 PE.setNegatePredicate(true);
210 PE.getIndent() = 3;
211 PE.expandPredicate(OS, Rec: Predicate->getValueAsDef(FieldName: "Predicate"));
212 OS << ")\n";
213 OS.indent(NumSpaces: 4) << " return false;\n";
214 OS.indent(NumSpaces: 2) << "}\n";
215 } else {
216 PrintFatalError(ErrorLoc: Predicate->getLoc(),
217 Msg: "Unsupported predicate for first instruction: " +
218 Predicate->getType()->getAsString());
219 }
220}
221
222void MacroFusionPredicatorEmitter::emitSecondPredicate(const Record *Predicate,
223 bool IsCommutable,
224 PredicateExpander &PE,
225 raw_ostream &OS) {
226 if (Predicate->isSubClassOf(Name: "FusionPredicateWithMCInstPredicate")) {
227 OS.indent(NumSpaces: 2) << "{\n";
228 OS.indent(NumSpaces: 4) << "[[maybe_unused]] const MachineInstr *MI = &SecondMI;\n";
229 OS.indent(NumSpaces: 4) << "if (";
230 PE.setNegatePredicate(true);
231 PE.getIndent() = 3;
232 PE.expandPredicate(OS, Rec: Predicate->getValueAsDef(FieldName: "Predicate"));
233 OS << ")\n";
234 OS.indent(NumSpaces: 4) << " return false;\n";
235 OS.indent(NumSpaces: 2) << "}\n";
236 } else if (Predicate->isSubClassOf(Name: "SecondInstHasSameReg")) {
237 int FirstOpIdx = Predicate->getValueAsInt(FieldName: "FirstOpIdx");
238 int SecondOpIdx = Predicate->getValueAsInt(FieldName: "SecondOpIdx");
239
240 OS.indent(NumSpaces: 2) << "if (!SecondMI.getOperand(" << FirstOpIdx
241 << ").getReg().isVirtual()) {\n";
242 OS.indent(NumSpaces: 4) << "if (SecondMI.getOperand(" << FirstOpIdx
243 << ").getReg() != SecondMI.getOperand(" << SecondOpIdx
244 << ").getReg())";
245
246 if (IsCommutable) {
247 OS << " {\n";
248 OS.indent(NumSpaces: 6) << "if (!SecondMI.getDesc().isCommutable())\n";
249 OS.indent(NumSpaces: 6) << " return false;\n";
250
251 OS.indent(NumSpaces: 6)
252 << "unsigned SrcOpIdx1 = " << SecondOpIdx
253 << ", SrcOpIdx2 = TargetInstrInfo::CommuteAnyOperandIndex;\n";
254 OS.indent(NumSpaces: 6)
255 << "if (TII.findCommutedOpIndices(SecondMI, SrcOpIdx1, SrcOpIdx2))\n";
256 OS.indent(NumSpaces: 6)
257 << " if (SecondMI.getOperand(" << FirstOpIdx
258 << ").getReg() != SecondMI.getOperand(SrcOpIdx2).getReg())\n";
259 OS.indent(NumSpaces: 6) << " return false;\n";
260 OS.indent(NumSpaces: 4) << "}\n";
261 } else {
262 OS << "\n";
263 OS.indent(NumSpaces: 4) << " return false;\n";
264 }
265 OS.indent(NumSpaces: 2) << "}\n";
266 } else {
267 PrintFatalError(ErrorLoc: Predicate->getLoc(),
268 Msg: "Unsupported predicate for second instruction: " +
269 Predicate->getType()->getAsString());
270 }
271}
272
273void MacroFusionPredicatorEmitter::emitBothPredicate(const Record *Predicate,
274 bool IsCommutable,
275 PredicateExpander &PE,
276 raw_ostream &OS) {
277 if (Predicate->isSubClassOf(Name: "FusionPredicateWithCode"))
278 OS << Predicate->getValueAsString(FieldName: "Predicate");
279 else if (Predicate->isSubClassOf(Name: "BothFusionPredicateWithMCInstPredicate")) {
280 emitFirstPredicate(Predicate, IsCommutable, PE, OS);
281 emitSecondPredicate(Predicate, IsCommutable, PE, OS);
282 } else if (Predicate->isSubClassOf(Name: "TieReg")) {
283 int FirstOpIdx = Predicate->getValueAsInt(FieldName: "FirstOpIdx");
284 int SecondOpIdx = Predicate->getValueAsInt(FieldName: "SecondOpIdx");
285 OS.indent(NumSpaces: 2) << "if (!(FirstMI->getOperand(" << FirstOpIdx
286 << ").isReg() &&\n";
287 OS.indent(NumSpaces: 2) << " SecondMI.getOperand(" << SecondOpIdx
288 << ").isReg() &&\n";
289 OS.indent(NumSpaces: 2) << " FirstMI->getOperand(" << FirstOpIdx
290 << ").getReg() == SecondMI.getOperand(" << SecondOpIdx
291 << ").getReg()))";
292
293 if (IsCommutable) {
294 OS << " {\n";
295 OS.indent(NumSpaces: 4) << "if (!SecondMI.getDesc().isCommutable())\n";
296 OS.indent(NumSpaces: 4) << " return false;\n";
297
298 OS.indent(NumSpaces: 4)
299 << "unsigned SrcOpIdx1 = " << SecondOpIdx
300 << ", SrcOpIdx2 = TargetInstrInfo::CommuteAnyOperandIndex;\n";
301 OS.indent(NumSpaces: 4)
302 << "if (TII.findCommutedOpIndices(SecondMI, SrcOpIdx1, SrcOpIdx2))\n";
303 OS.indent(NumSpaces: 4)
304 << " if (FirstMI->getOperand(" << FirstOpIdx
305 << ").getReg() != SecondMI.getOperand(SrcOpIdx2).getReg())\n";
306 OS.indent(NumSpaces: 4) << " return false;\n";
307 OS.indent(NumSpaces: 2) << "}";
308 } else {
309 OS << "\n";
310 OS.indent(NumSpaces: 2) << " return false;";
311 }
312 OS << "\n";
313 } else {
314 PrintFatalError(ErrorLoc: Predicate->getLoc(),
315 Msg: "Unsupported predicate for both instruction: " +
316 Predicate->getType()->getAsString());
317 }
318}
319
320void MacroFusionPredicatorEmitter::run(raw_ostream &OS) {
321 // Emit file header.
322 emitSourceFileHeader(Desc: "Macro Fusion Predicators", OS);
323
324 PredicateExpander PE(Target.getName());
325 PE.setByRef(false);
326 PE.setExpandForMC(false);
327
328 ArrayRef<const Record *> Fusions = Records.getAllDerivedDefinitions(ClassName: "Fusion");
329 emitMacroFusionDecl(Fusions, PE, OS);
330 OS << "\n";
331 emitMacroFusionImpl(Fusions, PE, OS);
332}
333
334static TableGen::Emitter::OptClass<MacroFusionPredicatorEmitter>
335 X("gen-macro-fusion-pred", "Generate macro fusion predicators.");
336