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>(Val: B->getBit(Bit: 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(Rec: OldOpRec) && isRegisterOperand(Rec: NewOpRec)) { |
148 | if (getRegOperandSize(RegRec: OldOpRec) != getRegOperandSize(RegRec: NewOpRec)) |
149 | return false; |
150 | } else if (isMemoryOperand(Rec: OldOpRec) && isMemoryOperand(Rec: NewOpRec)) { |
151 | if (getMemOperandSize(MemRec: OldOpRec) != getMemOperandSize(MemRec: NewOpRec)) |
152 | return false; |
153 | } else if (isImmediateOperand(Rec: OldOpRec) && isImmediateOperand(Rec: NewOpRec)) { |
154 | if (OldOpRec->getValueAsDef(FieldName: "Type" ) != NewOpRec->getValueAsDef(FieldName: "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(Name: "X86Inst" ) && |
166 | !Rec->getValueAsBit(FieldName: "isAsmParserOnly" ) && |
167 | !Rec->getName().ends_with(Suffix: "_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(B: Rec->getValueAsBitsInit(FieldName: "OpMapBits" )) == |
191 | X86Local::T_MAP4 && |
192 | byteFromBitsInit(B: Rec->getValueAsBitsInit(FieldName: "explicitOpPrefixBits" )) == |
193 | X86Local::ExplicitEVEX) |
194 | continue; |
195 | |
196 | if (NoCompressSet.find(x: 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(x: 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(x: 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(B: Rec->getValueAsBitsInit(FieldName: "Opcode" )); |
216 | StringRef Name = Rec->getName(); |
217 | const CodeGenInstruction *NewInst = nullptr; |
218 | if (ManualMap.find(x: Name) != ManualMap.end()) { |
219 | Record *NewRec = Records.getDef(Name: ManualMap.at(k: Rec->getName())); |
220 | assert(NewRec && "Instruction not found!" ); |
221 | NewInst = &Target.getInstruction(InstRec: NewRec); |
222 | } else if (Name.ends_with(Suffix: "_EVEX" )) { |
223 | if (auto *NewRec = Records.getDef(Name: Name.drop_back(N: 5))) |
224 | NewInst = &Target.getInstruction(InstRec: NewRec); |
225 | } else if (Name.ends_with(Suffix: "_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(Range&: CompressedInsts[Opcode], P: IsMatch(Inst)); |
233 | if (Match != CompressedInsts[Opcode].end()) |
234 | NewInst = *Match; |
235 | } |
236 | |
237 | if (!NewInst) |
238 | continue; |
239 | |
240 | Table.push_back(x: std::pair(Inst, NewInst)); |
241 | auto Predicates = NewInst->TheDef->getValueAsListOfDefs(FieldName: "Predicates" ); |
242 | auto It = llvm::find_if(Range&: Predicates, P: [](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(FieldName: "CondString" )].push_back(x: NewInst); |
249 | } |
250 | |
251 | StringRef Macro = "GET_X86_COMPRESS_EVEX_TABLE" ; |
252 | printTable(Table, Name: "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(s: "_NF" ); |
279 | if (Pos == std::string::npos) |
280 | continue; |
281 | |
282 | if (auto *NewRec = Records.getDef(Name: Name.erase(pos: Pos, n: 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(x: std::pair(&Target.getInstruction(InstRec: NewRec), Inst)); |
297 | } |
298 | } |
299 | printTable(Table, Name: "X86NFTransformTable" , Macro: "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(x: Name) != NoCompressSet.end()) |
319 | continue; |
320 | if (ManualMap.find(x: Name) != ManualMap.end()) { |
321 | auto *NewRec = Records.getDef(Name: ManualMap.at(k: Rec->getName())); |
322 | assert(NewRec && "Instruction not found!" ); |
323 | auto &NewInst = Target.getInstruction(InstRec: NewRec); |
324 | Table.push_back(x: std::pair(Inst, &NewInst)); |
325 | continue; |
326 | } |
327 | |
328 | if (!Name.ends_with(Suffix: "_ND" )) |
329 | continue; |
330 | auto *NewRec = Records.getDef(Name: Name.drop_back(N: 3)); |
331 | if (!NewRec) |
332 | continue; |
333 | auto &NewInst = Target.getInstruction(InstRec: NewRec); |
334 | if (isRegisterOperand(Rec: NewInst.Operands[0].Rec)) |
335 | Table.push_back(x: std::pair(Inst, &NewInst)); |
336 | } |
337 | printTable(Table, Name: "X86ND2NonNDTable" , Macro: "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(x: Name) != ManualMap.end()) { |
355 | auto *NewRec = Records.getDef(Name: ManualMap.at(k: Rec->getName())); |
356 | assert(NewRec && "Instruction not found!" ); |
357 | auto &NewInst = Target.getInstruction(InstRec: NewRec); |
358 | Table.push_back(x: std::pair(Inst, &NewInst)); |
359 | continue; |
360 | } |
361 | |
362 | std::string NewName = ("V" + Name).str(); |
363 | auto *AVXRec = Records.getDef(Name: NewName); |
364 | if (!AVXRec) |
365 | continue; |
366 | auto &AVXInst = Target.getInstruction(InstRec: AVXRec); |
367 | Table.push_back(x: std::pair(Inst, &AVXInst)); |
368 | } |
369 | printTable(Table, Name: "X86SSE2AVXTable" , Macro: "GET_X86_SSE2AVX_TABLE" , OS); |
370 | } |
371 | |
372 | void X86InstrMappingEmitter::run(raw_ostream &OS) { |
373 | emitSourceFileHeader(Desc: "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 | |