xref: /freebsd/contrib/llvm-project/llvm/utils/TableGen/X86InstrMappingEmitter.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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:
X86InstrMappingEmitter(const RecordKeeper & R)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 
printClassDef(raw_ostream & OS)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 
printMacroBegin(StringRef Macro,raw_ostream & OS)85 static void printMacroBegin(StringRef Macro, raw_ostream &OS) {
86   OS << "\n#ifdef " << Macro << "\n";
87 }
88 
printMacroEnd(StringRef Macro,raw_ostream & OS)89 static void printMacroEnd(StringRef Macro, raw_ostream &OS) {
90   OS << "#endif // " << Macro << "\n\n";
91 }
92 
printTable(ArrayRef<Entry> Table,StringRef Name,StringRef Macro,raw_ostream & OS)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 
byteFromBitsInit(const BitsInit * B)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:
IsMatch(const CodeGenInstruction * OldInst)125   IsMatch(const CodeGenInstruction *OldInst) : OldInst(OldInst) {}
126 
operator ()(const CodeGenInstruction * NewInst)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 
isInteresting(const Record * Rec)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 
emitCompressEVEXTable(ArrayRef<const CodeGenInstruction * > Insts,raw_ostream & OS)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       const auto &Insts = CompressedInsts[Opcode];
232       auto Match = llvm::find_if(Insts, IsMatch(Inst));
233       if (Match != Insts.end())
234         NewInst = *Match;
235     }
236 
237     if (!NewInst)
238       continue;
239 
240     Table.emplace_back(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" || Name == "HasAVXVNNIINT8" ||
246              Name == "HasAVXVNNIINT16";
247     });
248     if (It != Predicates.end())
249       PredicateInsts[(*It)->getValueAsString("CondString")].push_back(NewInst);
250   }
251 
252   StringRef Macro = "GET_X86_COMPRESS_EVEX_TABLE";
253   printTable(Table, "X86CompressEVEXTable", Macro, OS);
254 
255   // Prints function which checks target feature for compressed instructions.
256   printMacroBegin(Macro, OS);
257   OS << "static bool checkPredicate(unsigned Opc, const X86Subtarget "
258         "*Subtarget) {\n"
259      << "  switch (Opc) {\n"
260      << "  default: return true;\n";
261   for (const auto &[Key, Val] : PredicateInsts) {
262     for (const auto &Inst : Val)
263       OS << "  case X86::" << Inst->TheDef->getName() << ":\n";
264     OS << "    return " << Key << ";\n";
265   }
266   OS << "  }\n";
267   OS << "}\n\n";
268   printMacroEnd(Macro, OS);
269 }
270 
emitNFTransformTable(ArrayRef<const CodeGenInstruction * > Insts,raw_ostream & OS)271 void X86InstrMappingEmitter::emitNFTransformTable(
272     ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {
273   std::vector<Entry> Table;
274   for (const CodeGenInstruction *Inst : Insts) {
275     const Record *Rec = Inst->TheDef;
276     if (!isInteresting(Rec))
277       continue;
278     StringRef Name = Rec->getName();
279     if (Name.contains("_NF"))
280       continue;
281 
282     if (auto *NewRec = Name.consume_back("_ND")
283                            ? Records.getDef(Name.str() + "_NF_ND")
284                            : Records.getDef(Name.str() + "_NF")) {
285 #ifndef NDEBUG
286       auto ClobberEFLAGS = [](const Record *R) {
287         return llvm::any_of(
288             R->getValueAsListOfDefs("Defs"),
289             [](const Record *Def) { return Def->getName() == "EFLAGS"; });
290       };
291       if (ClobberEFLAGS(NewRec))
292         report_fatal_error("EFLAGS should not be clobbered by " +
293                            NewRec->getName());
294       if (!ClobberEFLAGS(Rec))
295         report_fatal_error("EFLAGS should be clobbered by " + Rec->getName());
296 #endif
297       Table.emplace_back(Inst, &Target.getInstruction(NewRec));
298     }
299   }
300   printTable(Table, "X86NFTransformTable", "GET_X86_NF_TRANSFORM_TABLE", OS);
301 }
302 
emitND2NonNDTable(ArrayRef<const CodeGenInstruction * > Insts,raw_ostream & OS)303 void X86InstrMappingEmitter::emitND2NonNDTable(
304     ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {
305 
306   const std::map<StringRef, StringRef> ManualMap = {
307 #define ENTRY_ND(OLD, NEW) {#OLD, #NEW},
308 #include "X86ManualInstrMapping.def"
309   };
310   const std::set<StringRef> NoCompressSet = {
311 #define NOCOMP_ND(INSN) #INSN,
312 #include "X86ManualInstrMapping.def"
313   };
314 
315   std::vector<Entry> Table;
316   for (const CodeGenInstruction *Inst : Insts) {
317     const Record *Rec = Inst->TheDef;
318     StringRef Name = Rec->getName();
319     if (!isInteresting(Rec) || NoCompressSet.find(Name) != NoCompressSet.end())
320       continue;
321     if (ManualMap.find(Name) != ManualMap.end()) {
322       const auto *NewRec = Records.getDef(ManualMap.at(Rec->getName()));
323       assert(NewRec && "Instruction not found!");
324       auto &NewInst = Target.getInstruction(NewRec);
325       Table.emplace_back(Inst, &NewInst);
326       continue;
327     }
328 
329     if (!Name.ends_with("_ND"))
330       continue;
331     const auto *NewRec = Records.getDef(Name.drop_back(3));
332     if (!NewRec)
333       continue;
334     const auto &NewInst = Target.getInstruction(NewRec);
335     if (isRegisterOperand(NewInst.Operands[0].Rec))
336       Table.emplace_back(Inst, &NewInst);
337   }
338   printTable(Table, "X86ND2NonNDTable", "GET_X86_ND2NONND_TABLE", OS);
339 }
340 
emitSSE2AVXTable(ArrayRef<const CodeGenInstruction * > Insts,raw_ostream & OS)341 void X86InstrMappingEmitter::emitSSE2AVXTable(
342     ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {
343 
344   const std::map<StringRef, StringRef> ManualMap = {
345 #define ENTRY_SSE2AVX(OLD, NEW) {#OLD, #NEW},
346 #include "X86ManualInstrMapping.def"
347   };
348 
349   std::vector<Entry> Table;
350   for (const CodeGenInstruction *Inst : Insts) {
351     const Record *Rec = Inst->TheDef;
352     StringRef Name = Rec->getName();
353     if (!isInteresting(Rec))
354       continue;
355     if (ManualMap.find(Name) != ManualMap.end()) {
356       const auto *NewRec = Records.getDef(ManualMap.at(Rec->getName()));
357       assert(NewRec && "Instruction not found!");
358       const auto &NewInst = Target.getInstruction(NewRec);
359       Table.emplace_back(Inst, &NewInst);
360       continue;
361     }
362 
363     std::string NewName = ("V" + Name).str();
364     const auto *AVXRec = Records.getDef(NewName);
365     if (!AVXRec)
366       continue;
367     auto &AVXInst = Target.getInstruction(AVXRec);
368     Table.emplace_back(Inst, &AVXInst);
369   }
370   printTable(Table, "X86SSE2AVXTable", "GET_X86_SSE2AVX_TABLE", OS);
371 }
372 
run(raw_ostream & OS)373 void X86InstrMappingEmitter::run(raw_ostream &OS) {
374   emitSourceFileHeader("X86 instruction mapping", OS);
375 
376   ArrayRef<const CodeGenInstruction *> Insts = Target.getInstructions();
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