xref: /llvm-project/llvm/utils/TableGen/X86InstrMappingEmitter.cpp (revision 4e8c9d28132039a98feb97cec2759cddeb37d934)
1 //========- utils/TableGen/X86InstrMappingEmitter.cpp - X86 backend-*- C++ -*-//
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 /// This tablegen backend is responsible for emitting the X86 backend
10 /// instruction mapping.
11 ///
12 //===----------------------------------------------------------------------===//
13 
14 #include "Common/CodeGenInstruction.h"
15 #include "Common/CodeGenTarget.h"
16 #include "X86RecognizableInstr.h"
17 #include "llvm/TableGen/Error.h"
18 #include "llvm/TableGen/Record.h"
19 #include "llvm/TableGen/TableGenBackend.h"
20 #include <map>
21 #include <set>
22 
23 using namespace llvm;
24 using namespace X86Disassembler;
25 
26 namespace {
27 
28 class X86InstrMappingEmitter {
29   const RecordKeeper &Records;
30   const CodeGenTarget Target;
31 
32   // Hold all pontentially compressible EVEX instructions
33   std::vector<const CodeGenInstruction *> PreCompressionInsts;
34   // Hold all compressed instructions. Divided into groups with same opcodes
35   // to make the search more efficient
36   std::map<uint64_t, std::vector<const CodeGenInstruction *>> CompressedInsts;
37 
38   typedef std::pair<const CodeGenInstruction *, const CodeGenInstruction *>
39       Entry;
40   typedef std::map<StringRef, std::vector<const CodeGenInstruction *>>
41       PredicateInstMap;
42 
43   // Hold all compressed instructions that need to check predicate
44   PredicateInstMap PredicateInsts;
45 
46 public:
47   X86InstrMappingEmitter(const RecordKeeper &R) : Records(R), Target(R) {}
48 
49   // run - Output X86 EVEX compression tables.
50   void run(raw_ostream &OS);
51 
52 private:
53   void emitCompressEVEXTable(ArrayRef<const CodeGenInstruction *> Insts,
54                              raw_ostream &OS);
55   void emitNFTransformTable(ArrayRef<const CodeGenInstruction *> Insts,
56                             raw_ostream &OS);
57   void emitND2NonNDTable(ArrayRef<const CodeGenInstruction *> Insts,
58                          raw_ostream &OS);
59   void emitSSE2AVXTable(ArrayRef<const CodeGenInstruction *> Insts,
60                         raw_ostream &OS);
61 
62   // Prints the definition of class X86TableEntry.
63   void printClassDef(raw_ostream &OS);
64   // Prints the given table as a C++ array of type X86TableEntry under the guard
65   // \p Macro.
66   void printTable(ArrayRef<Entry> Table, StringRef Name, StringRef Macro,
67                   raw_ostream &OS);
68 };
69 
70 void X86InstrMappingEmitter::printClassDef(raw_ostream &OS) {
71   OS << "struct X86TableEntry {\n"
72         "  uint16_t OldOpc;\n"
73         "  uint16_t NewOpc;\n"
74         "  bool operator<(const X86TableEntry &RHS) const {\n"
75         "    return OldOpc < RHS.OldOpc;\n"
76         "  }"
77         "  friend bool operator<(const X86TableEntry &TE, unsigned Opc) {\n"
78         "    return TE.OldOpc < Opc;\n"
79         "  }\n"
80         "};";
81 
82   OS << "\n\n";
83 }
84 
85 static void printMacroBegin(StringRef Macro, raw_ostream &OS) {
86   OS << "\n#ifdef " << Macro << "\n";
87 }
88 
89 static void printMacroEnd(StringRef Macro, raw_ostream &OS) {
90   OS << "#endif // " << Macro << "\n\n";
91 }
92 
93 void X86InstrMappingEmitter::printTable(ArrayRef<Entry> Table, StringRef Name,
94                                         StringRef Macro, raw_ostream &OS) {
95   printMacroBegin(Macro, OS);
96 
97   OS << "static const X86TableEntry " << Name << "[] = {\n";
98 
99   // Print all entries added to the table
100   for (const auto &Pair : Table)
101     OS << "  { X86::" << Pair.first->TheDef->getName()
102        << ", X86::" << Pair.second->TheDef->getName() << " },\n";
103 
104   OS << "};\n\n";
105 
106   printMacroEnd(Macro, OS);
107 }
108 
109 static uint8_t byteFromBitsInit(const BitsInit *B) {
110   unsigned N = B->getNumBits();
111   assert(N <= 8 && "Field is too large for uint8_t!");
112 
113   uint8_t Value = 0;
114   for (unsigned I = 0; I != N; ++I) {
115     const BitInit *Bit = cast<BitInit>(B->getBit(I));
116     Value |= Bit->getValue() << I;
117   }
118   return Value;
119 }
120 
121 class IsMatch {
122   const CodeGenInstruction *OldInst;
123 
124 public:
125   IsMatch(const CodeGenInstruction *OldInst) : OldInst(OldInst) {}
126 
127   bool operator()(const CodeGenInstruction *NewInst) {
128     RecognizableInstrBase NewRI(*NewInst);
129     RecognizableInstrBase OldRI(*OldInst);
130 
131     // Return false if any of the following fields of does not match.
132     if (std::tuple(OldRI.IsCodeGenOnly, OldRI.OpMap, NewRI.OpPrefix,
133                    OldRI.HasVEX_4V, OldRI.HasVEX_L, OldRI.HasREX_W,
134                    OldRI.Form) !=
135         std::tuple(NewRI.IsCodeGenOnly, NewRI.OpMap, OldRI.OpPrefix,
136                    NewRI.HasVEX_4V, NewRI.HasVEX_L, NewRI.HasREX_W, NewRI.Form))
137       return false;
138 
139     for (unsigned I = 0, E = OldInst->Operands.size(); I < E; ++I) {
140       const Record *OldOpRec = OldInst->Operands[I].Rec;
141       const Record *NewOpRec = NewInst->Operands[I].Rec;
142 
143       if (OldOpRec == NewOpRec)
144         continue;
145 
146       if (isRegisterOperand(OldOpRec) && isRegisterOperand(NewOpRec)) {
147         if (getRegOperandSize(OldOpRec) != getRegOperandSize(NewOpRec))
148           return false;
149       } else if (isMemoryOperand(OldOpRec) && isMemoryOperand(NewOpRec)) {
150         if (getMemOperandSize(OldOpRec) != getMemOperandSize(NewOpRec))
151           return false;
152       } else if (isImmediateOperand(OldOpRec) && isImmediateOperand(NewOpRec)) {
153         if (OldOpRec->getValueAsDef("Type") != NewOpRec->getValueAsDef("Type"))
154           return false;
155       }
156     }
157 
158     return true;
159   }
160 };
161 
162 static bool isInteresting(const Record *Rec) {
163   // _REV instruction should not appear before encoding optimization
164   return Rec->isSubClassOf("X86Inst") &&
165          !Rec->getValueAsBit("isAsmParserOnly") &&
166          !Rec->getName().ends_with("_REV");
167 }
168 
169 void X86InstrMappingEmitter::emitCompressEVEXTable(
170     ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {
171 
172   const std::map<StringRef, StringRef> ManualMap = {
173 #define ENTRY(OLD, NEW) {#OLD, #NEW},
174 #include "X86ManualInstrMapping.def"
175   };
176   const std::set<StringRef> NoCompressSet = {
177 #define NOCOMP(INSN) #INSN,
178 #include "X86ManualInstrMapping.def"
179   };
180 
181   for (const CodeGenInstruction *Inst : Insts) {
182     const Record *Rec = Inst->TheDef;
183     StringRef Name = Rec->getName();
184     if (!isInteresting(Rec))
185       continue;
186 
187     // Promoted legacy instruction is in EVEX space, and has REX2-encoding
188     // alternative. It's added due to HW design and never emitted by compiler.
189     if (byteFromBitsInit(Rec->getValueAsBitsInit("OpMapBits")) ==
190             X86Local::T_MAP4 &&
191         byteFromBitsInit(Rec->getValueAsBitsInit("explicitOpPrefixBits")) ==
192             X86Local::ExplicitEVEX)
193       continue;
194 
195     if (NoCompressSet.find(Name) != NoCompressSet.end())
196       continue;
197 
198     RecognizableInstrBase RI(*Inst);
199 
200     bool IsND = RI.OpMap == X86Local::T_MAP4 && RI.HasEVEX_B && RI.HasVEX_4V;
201     // Add VEX encoded instructions to one of CompressedInsts vectors according
202     // to it's opcode.
203     if (RI.Encoding == X86Local::VEX)
204       CompressedInsts[RI.Opcode].push_back(Inst);
205     // Add relevant EVEX encoded instructions to PreCompressionInsts
206     else if (RI.Encoding == X86Local::EVEX && !RI.HasEVEX_K && !RI.HasEVEX_L2 &&
207              (!RI.HasEVEX_B || IsND))
208       PreCompressionInsts.push_back(Inst);
209   }
210 
211   std::vector<Entry> Table;
212   for (const CodeGenInstruction *Inst : PreCompressionInsts) {
213     const Record *Rec = Inst->TheDef;
214     uint8_t Opcode = byteFromBitsInit(Rec->getValueAsBitsInit("Opcode"));
215     StringRef Name = Rec->getName();
216     const CodeGenInstruction *NewInst = nullptr;
217     if (ManualMap.find(Name) != ManualMap.end()) {
218       const Record *NewRec = Records.getDef(ManualMap.at(Rec->getName()));
219       assert(NewRec && "Instruction not found!");
220       NewInst = &Target.getInstruction(NewRec);
221     } else if (Name.ends_with("_EVEX")) {
222       if (const auto *NewRec = Records.getDef(Name.drop_back(5)))
223         NewInst = &Target.getInstruction(NewRec);
224     } else if (Name.ends_with("_ND"))
225       // Leave it to ND2NONND table.
226       continue;
227     else {
228       // For each pre-compression instruction look for a match in the
229       // appropriate vector (instructions with the same opcode) using function
230       // object IsMatch.
231       auto Match = llvm::find_if(CompressedInsts[Opcode], IsMatch(Inst));
232       if (Match != CompressedInsts[Opcode].end())
233         NewInst = *Match;
234     }
235 
236     if (!NewInst)
237       continue;
238 
239     Table.emplace_back(Inst, NewInst);
240     auto Predicates = NewInst->TheDef->getValueAsListOfDefs("Predicates");
241     auto It = llvm::find_if(Predicates, [](const Record *R) {
242       StringRef Name = R->getName();
243       return Name == "HasAVXNECONVERT" || Name == "HasAVXVNNI" ||
244              Name == "HasAVXIFMA" || Name == "HasAVXVNNIINT8" ||
245              Name == "HasAVXVNNIINT16";
246     });
247     if (It != Predicates.end())
248       PredicateInsts[(*It)->getValueAsString("CondString")].push_back(NewInst);
249   }
250 
251   StringRef Macro = "GET_X86_COMPRESS_EVEX_TABLE";
252   printTable(Table, "X86CompressEVEXTable", Macro, OS);
253 
254   // Prints function which checks target feature for compressed instructions.
255   printMacroBegin(Macro, OS);
256   OS << "static bool checkPredicate(unsigned Opc, const X86Subtarget "
257         "*Subtarget) {\n"
258      << "  switch (Opc) {\n"
259      << "  default: return true;\n";
260   for (const auto &[Key, Val] : PredicateInsts) {
261     for (const auto &Inst : Val)
262       OS << "  case X86::" << Inst->TheDef->getName() << ":\n";
263     OS << "    return " << Key << ";\n";
264   }
265   OS << "  }\n";
266   OS << "}\n\n";
267   printMacroEnd(Macro, OS);
268 }
269 
270 void X86InstrMappingEmitter::emitNFTransformTable(
271     ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {
272   std::vector<Entry> Table;
273   for (const CodeGenInstruction *Inst : Insts) {
274     const Record *Rec = Inst->TheDef;
275     if (!isInteresting(Rec))
276       continue;
277     std::string Name = Rec->getName().str();
278     auto Pos = Name.find("_NF");
279     if (Pos == std::string::npos)
280       continue;
281 
282     if (auto *NewRec = Records.getDef(Name.erase(Pos, 3))) {
283 #ifndef NDEBUG
284       auto ClobberEFLAGS = [](const Record *R) {
285         return llvm::any_of(
286             R->getValueAsListOfDefs("Defs"),
287             [](const Record *Def) { return Def->getName() == "EFLAGS"; });
288       };
289       if (ClobberEFLAGS(Rec))
290         report_fatal_error("EFLAGS should not be clobbered by " +
291                            Rec->getName());
292       if (!ClobberEFLAGS(NewRec))
293         report_fatal_error("EFLAGS should be clobbered by " +
294                            NewRec->getName());
295 #endif
296       Table.emplace_back(&Target.getInstruction(NewRec), Inst);
297     }
298   }
299   printTable(Table, "X86NFTransformTable", "GET_X86_NF_TRANSFORM_TABLE", OS);
300 }
301 
302 void X86InstrMappingEmitter::emitND2NonNDTable(
303     ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {
304 
305   const std::map<StringRef, StringRef> ManualMap = {
306 #define ENTRY_ND(OLD, NEW) {#OLD, #NEW},
307 #include "X86ManualInstrMapping.def"
308   };
309   const std::set<StringRef> NoCompressSet = {
310 #define NOCOMP_ND(INSN) #INSN,
311 #include "X86ManualInstrMapping.def"
312   };
313 
314   std::vector<Entry> Table;
315   for (const CodeGenInstruction *Inst : Insts) {
316     const Record *Rec = Inst->TheDef;
317     StringRef Name = Rec->getName();
318     if (!isInteresting(Rec) || NoCompressSet.find(Name) != NoCompressSet.end())
319       continue;
320     if (ManualMap.find(Name) != ManualMap.end()) {
321       const auto *NewRec = Records.getDef(ManualMap.at(Rec->getName()));
322       assert(NewRec && "Instruction not found!");
323       auto &NewInst = Target.getInstruction(NewRec);
324       Table.emplace_back(Inst, &NewInst);
325       continue;
326     }
327 
328     if (!Name.ends_with("_ND"))
329       continue;
330     const auto *NewRec = Records.getDef(Name.drop_back(3));
331     if (!NewRec)
332       continue;
333     const auto &NewInst = Target.getInstruction(NewRec);
334     if (isRegisterOperand(NewInst.Operands[0].Rec))
335       Table.emplace_back(Inst, &NewInst);
336   }
337   printTable(Table, "X86ND2NonNDTable", "GET_X86_ND2NONND_TABLE", OS);
338 }
339 
340 void X86InstrMappingEmitter::emitSSE2AVXTable(
341     ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {
342 
343   const std::map<StringRef, StringRef> ManualMap = {
344 #define ENTRY_SSE2AVX(OLD, NEW) {#OLD, #NEW},
345 #include "X86ManualInstrMapping.def"
346   };
347 
348   std::vector<Entry> Table;
349   for (const CodeGenInstruction *Inst : Insts) {
350     const Record *Rec = Inst->TheDef;
351     StringRef Name = Rec->getName();
352     if (!isInteresting(Rec))
353       continue;
354     if (ManualMap.find(Name) != ManualMap.end()) {
355       const auto *NewRec = Records.getDef(ManualMap.at(Rec->getName()));
356       assert(NewRec && "Instruction not found!");
357       const auto &NewInst = Target.getInstruction(NewRec);
358       Table.emplace_back(Inst, &NewInst);
359       continue;
360     }
361 
362     std::string NewName = ("V" + Name).str();
363     const auto *AVXRec = Records.getDef(NewName);
364     if (!AVXRec)
365       continue;
366     auto &AVXInst = Target.getInstruction(AVXRec);
367     Table.emplace_back(Inst, &AVXInst);
368   }
369   printTable(Table, "X86SSE2AVXTable", "GET_X86_SSE2AVX_TABLE", OS);
370 }
371 
372 void X86InstrMappingEmitter::run(raw_ostream &OS) {
373   emitSourceFileHeader("X86 instruction mapping", OS);
374 
375   ArrayRef<const CodeGenInstruction *> Insts =
376       Target.getInstructionsByEnumValue();
377   printClassDef(OS);
378   emitCompressEVEXTable(Insts, OS);
379   emitNFTransformTable(Insts, OS);
380   emitND2NonNDTable(Insts, OS);
381   emitSSE2AVXTable(Insts, OS);
382 }
383 } // namespace
384 
385 static TableGen::Emitter::OptClass<X86InstrMappingEmitter>
386     X("gen-x86-instr-mapping", "Generate X86 instruction mapping");
387