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