xref: /freebsd/contrib/llvm-project/llvm/utils/TableGen/X86InstrMappingEmitter.cpp (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
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   RecordKeeper &Records;
30   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(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(const std::vector<Entry> &Table, StringRef Name,
67                   StringRef Macro, 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(const std::vector<Entry> &Table,
94                                         StringRef Name, StringRef Macro,
95                                         raw_ostream &OS) {
96   printMacroBegin(Macro, OS);
97 
98   OS << "static const X86TableEntry " << Name << "[] = {\n";
99 
100   // Print all entries added to the table
101   for (const auto &Pair : Table)
102     OS << "  { X86::" << Pair.first->TheDef->getName()
103        << ", X86::" << Pair.second->TheDef->getName() << " },\n";
104 
105   OS << "};\n\n";
106 
107   printMacroEnd(Macro, OS);
108 }
109 
110 static uint8_t byteFromBitsInit(const BitsInit *B) {
111   unsigned N = B->getNumBits();
112   assert(N <= 8 && "Field is too large for uint8_t!");
113 
114   uint8_t Value = 0;
115   for (unsigned I = 0; I != N; ++I) {
116     BitInit *Bit = cast<BitInit>(B->getBit(I));
117     Value |= Bit->getValue() << I;
118   }
119   return Value;
120 }
121 
122 class IsMatch {
123   const CodeGenInstruction *OldInst;
124 
125 public:
126   IsMatch(const CodeGenInstruction *OldInst) : OldInst(OldInst) {}
127 
128   bool operator()(const CodeGenInstruction *NewInst) {
129     RecognizableInstrBase NewRI(*NewInst);
130     RecognizableInstrBase OldRI(*OldInst);
131 
132     // Return false if any of the following fields of does not match.
133     if (std::tuple(OldRI.IsCodeGenOnly, OldRI.OpMap, NewRI.OpPrefix,
134                    OldRI.HasVEX_4V, OldRI.HasVEX_L, OldRI.HasREX_W,
135                    OldRI.Form) !=
136         std::tuple(NewRI.IsCodeGenOnly, NewRI.OpMap, OldRI.OpPrefix,
137                    NewRI.HasVEX_4V, NewRI.HasVEX_L, NewRI.HasREX_W, NewRI.Form))
138       return false;
139 
140     for (unsigned I = 0, E = OldInst->Operands.size(); I < E; ++I) {
141       Record *OldOpRec = OldInst->Operands[I].Rec;
142       Record *NewOpRec = NewInst->Operands[I].Rec;
143 
144       if (OldOpRec == NewOpRec)
145         continue;
146 
147       if (isRegisterOperand(OldOpRec) && isRegisterOperand(NewOpRec)) {
148         if (getRegOperandSize(OldOpRec) != getRegOperandSize(NewOpRec))
149           return false;
150       } else if (isMemoryOperand(OldOpRec) && isMemoryOperand(NewOpRec)) {
151         if (getMemOperandSize(OldOpRec) != getMemOperandSize(NewOpRec))
152           return false;
153       } else if (isImmediateOperand(OldOpRec) && isImmediateOperand(NewOpRec)) {
154         if (OldOpRec->getValueAsDef("Type") != NewOpRec->getValueAsDef("Type"))
155           return false;
156       }
157     }
158 
159     return true;
160   }
161 };
162 
163 static bool isInteresting(const Record *Rec) {
164   // _REV instruction should not appear before encoding optimization
165   return Rec->isSubClassOf("X86Inst") &&
166          !Rec->getValueAsBit("isAsmParserOnly") &&
167          !Rec->getName().ends_with("_REV");
168 }
169 
170 void X86InstrMappingEmitter::emitCompressEVEXTable(
171     ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {
172 
173   const std::map<StringRef, StringRef> ManualMap = {
174 #define ENTRY(OLD, NEW) {#OLD, #NEW},
175 #include "X86ManualInstrMapping.def"
176   };
177   const std::set<StringRef> NoCompressSet = {
178 #define NOCOMP(INSN) #INSN,
179 #include "X86ManualInstrMapping.def"
180   };
181 
182   for (const CodeGenInstruction *Inst : Insts) {
183     const Record *Rec = Inst->TheDef;
184     StringRef Name = Rec->getName();
185     if (!isInteresting(Rec))
186       continue;
187 
188     // Promoted legacy instruction is in EVEX space, and has REX2-encoding
189     // alternative. It's added due to HW design and never emitted by compiler.
190     if (byteFromBitsInit(Rec->getValueAsBitsInit("OpMapBits")) ==
191             X86Local::T_MAP4 &&
192         byteFromBitsInit(Rec->getValueAsBitsInit("explicitOpPrefixBits")) ==
193             X86Local::ExplicitEVEX)
194       continue;
195 
196     if (NoCompressSet.find(Name) != NoCompressSet.end())
197       continue;
198 
199     RecognizableInstrBase RI(*Inst);
200 
201     bool IsND = RI.OpMap == X86Local::T_MAP4 && RI.HasEVEX_B && RI.HasVEX_4V;
202     // Add VEX encoded instructions to one of CompressedInsts vectors according
203     // to it's opcode.
204     if (RI.Encoding == X86Local::VEX)
205       CompressedInsts[RI.Opcode].push_back(Inst);
206     // Add relevant EVEX encoded instructions to PreCompressionInsts
207     else if (RI.Encoding == X86Local::EVEX && !RI.HasEVEX_K && !RI.HasEVEX_L2 &&
208              (!RI.HasEVEX_B || IsND))
209       PreCompressionInsts.push_back(Inst);
210   }
211 
212   std::vector<Entry> Table;
213   for (const CodeGenInstruction *Inst : PreCompressionInsts) {
214     const Record *Rec = Inst->TheDef;
215     uint8_t Opcode = byteFromBitsInit(Rec->getValueAsBitsInit("Opcode"));
216     StringRef Name = Rec->getName();
217     const CodeGenInstruction *NewInst = nullptr;
218     if (ManualMap.find(Name) != ManualMap.end()) {
219       Record *NewRec = Records.getDef(ManualMap.at(Rec->getName()));
220       assert(NewRec && "Instruction not found!");
221       NewInst = &Target.getInstruction(NewRec);
222     } else if (Name.ends_with("_EVEX")) {
223       if (auto *NewRec = Records.getDef(Name.drop_back(5)))
224         NewInst = &Target.getInstruction(NewRec);
225     } else if (Name.ends_with("_ND"))
226       // Leave it to ND2NONND table.
227       continue;
228     else {
229       // For each pre-compression instruction look for a match in the
230       // appropriate vector (instructions with the same opcode) using function
231       // object IsMatch.
232       auto Match = llvm::find_if(CompressedInsts[Opcode], IsMatch(Inst));
233       if (Match != CompressedInsts[Opcode].end())
234         NewInst = *Match;
235     }
236 
237     if (!NewInst)
238       continue;
239 
240     Table.push_back(std::pair(Inst, NewInst));
241     auto Predicates = NewInst->TheDef->getValueAsListOfDefs("Predicates");
242     auto It = llvm::find_if(Predicates, [](const Record *R) {
243       StringRef Name = R->getName();
244       return Name == "HasAVXNECONVERT" || Name == "HasAVXVNNI" ||
245              Name == "HasAVXIFMA";
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.push_back(std::pair(&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       auto *NewRec = Records.getDef(ManualMap.at(Rec->getName()));
322       assert(NewRec && "Instruction not found!");
323       auto &NewInst = Target.getInstruction(NewRec);
324       Table.push_back(std::pair(Inst, &NewInst));
325       continue;
326     }
327 
328     if (!Name.ends_with("_ND"))
329       continue;
330     auto *NewRec = Records.getDef(Name.drop_back(3));
331     if (!NewRec)
332       continue;
333     auto &NewInst = Target.getInstruction(NewRec);
334     if (isRegisterOperand(NewInst.Operands[0].Rec))
335       Table.push_back(std::pair(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       auto *NewRec = Records.getDef(ManualMap.at(Rec->getName()));
356       assert(NewRec && "Instruction not found!");
357       auto &NewInst = Target.getInstruction(NewRec);
358       Table.push_back(std::pair(Inst, &NewInst));
359       continue;
360     }
361 
362     std::string NewName = ("V" + Name).str();
363     auto *AVXRec = Records.getDef(NewName);
364     if (!AVXRec)
365       continue;
366     auto &AVXInst = Target.getInstruction(AVXRec);
367     Table.push_back(std::pair(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