1 //========- utils/TableGen/X86InstrMappingEmitter.cpp - X86 backend-*- C++ -*-//
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
7 //===----------------------------------------------------------------------===//
9 /// This tablegen backend is responsible for emitting the X86 backend
10 /// instruction mapping.
12 //===----------------------------------------------------------------------===//
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"
24 using namespace X86Disassembler
;
28 class X86InstrMappingEmitter
{
29 const RecordKeeper
&Records
;
30 const CodeGenTarget Target
;
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
;
38 typedef std::pair
<const CodeGenInstruction
*, const CodeGenInstruction
*>
40 typedef std::map
<StringRef
, std::vector
<const CodeGenInstruction
*>>
43 // Hold all compressed instructions that need to check predicate
44 PredicateInstMap PredicateInsts
;
47 X86InstrMappingEmitter(const RecordKeeper
&R
) : Records(R
), Target(R
) {}
49 // run - Output X86 EVEX compression tables.
50 void run(raw_ostream
&OS
);
53 void emitCompressEVEXTable(ArrayRef
<const CodeGenInstruction
*> Insts
,
55 void emitNFTransformTable(ArrayRef
<const CodeGenInstruction
*> Insts
,
57 void emitND2NonNDTable(ArrayRef
<const CodeGenInstruction
*> Insts
,
59 void emitSSE2AVXTable(ArrayRef
<const CodeGenInstruction
*> Insts
,
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
66 void printTable(ArrayRef
<Entry
> Table
, StringRef Name
, StringRef Macro
,
70 void X86InstrMappingEmitter::printClassDef(raw_ostream
&OS
) {
71 OS
<< "struct X86TableEntry {\n"
74 " bool operator<(const X86TableEntry &RHS) const {\n"
75 " return OldOpc < RHS.OldOpc;\n"
77 " friend bool operator<(const X86TableEntry &TE, unsigned Opc) {\n"
78 " return TE.OldOpc < Opc;\n"
85 static void printMacroBegin(StringRef Macro
, raw_ostream
&OS
) {
86 OS
<< "\n#ifdef " << Macro
<< "\n";
89 static void printMacroEnd(StringRef Macro
, raw_ostream
&OS
) {
90 OS
<< "#endif // " << Macro
<< "\n\n";
93 void X86InstrMappingEmitter::printTable(ArrayRef
<Entry
> Table
, StringRef Name
,
94 StringRef Macro
, raw_ostream
&OS
) {
95 printMacroBegin(Macro
, OS
);
97 OS
<< "static const X86TableEntry " << Name
<< "[] = {\n";
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";
106 printMacroEnd(Macro
, OS
);
109 static uint8_t byteFromBitsInit(const BitsInit
*B
) {
110 unsigned N
= B
->getNumBits();
111 assert(N
<= 8 && "Field is too large for uint8_t!");
114 for (unsigned I
= 0; I
!= N
; ++I
) {
115 const BitInit
*Bit
= cast
<BitInit
>(B
->getBit(I
));
116 Value
|= Bit
->getValue() << I
;
122 const CodeGenInstruction
*OldInst
;
125 IsMatch(const CodeGenInstruction
*OldInst
) : OldInst(OldInst
) {}
127 bool operator()(const CodeGenInstruction
*NewInst
) {
128 RecognizableInstrBase
NewRI(*NewInst
);
129 RecognizableInstrBase
OldRI(*OldInst
);
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
,
135 std::tuple(NewRI
.IsCodeGenOnly
, NewRI
.OpMap
, OldRI
.OpPrefix
,
136 NewRI
.HasVEX_4V
, NewRI
.HasVEX_L
, NewRI
.HasREX_W
, NewRI
.Form
))
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
;
143 if (OldOpRec
== NewOpRec
)
146 if (isRegisterOperand(OldOpRec
) && isRegisterOperand(NewOpRec
)) {
147 if (getRegOperandSize(OldOpRec
) != getRegOperandSize(NewOpRec
))
149 } else if (isMemoryOperand(OldOpRec
) && isMemoryOperand(NewOpRec
)) {
150 if (getMemOperandSize(OldOpRec
) != getMemOperandSize(NewOpRec
))
152 } else if (isImmediateOperand(OldOpRec
) && isImmediateOperand(NewOpRec
)) {
153 if (OldOpRec
->getValueAsDef("Type") != NewOpRec
->getValueAsDef("Type"))
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");
169 void X86InstrMappingEmitter::emitCompressEVEXTable(
170 ArrayRef
<const CodeGenInstruction
*> Insts
, raw_ostream
&OS
) {
172 const std::map
<StringRef
, StringRef
> ManualMap
= {
173 #define ENTRY(OLD, NEW) {#OLD, #NEW},
174 #include "X86ManualInstrMapping.def"
176 const std::set
<StringRef
> NoCompressSet
= {
177 #define NOCOMP(INSN) #INSN,
178 #include "X86ManualInstrMapping.def"
181 for (const CodeGenInstruction
*Inst
: Insts
) {
182 const Record
*Rec
= Inst
->TheDef
;
183 StringRef Name
= Rec
->getName();
184 if (!isInteresting(Rec
))
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")) ==
191 byteFromBitsInit(Rec
->getValueAsBitsInit("explicitOpPrefixBits")) ==
192 X86Local::ExplicitEVEX
)
195 if (NoCompressSet
.find(Name
) != NoCompressSet
.end())
198 RecognizableInstrBase
RI(*Inst
);
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
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
);
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.
228 // For each pre-compression instruction look for a match in the
229 // appropriate vector (instructions with the same opcode) using function
231 auto Match
= llvm::find_if(CompressedInsts
[Opcode
], IsMatch(Inst
));
232 if (Match
!= CompressedInsts
[Opcode
].end())
239 Table
.push_back(std::pair(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";
247 if (It
!= Predicates
.end())
248 PredicateInsts
[(*It
)->getValueAsString("CondString")].push_back(NewInst
);
251 StringRef Macro
= "GET_X86_COMPRESS_EVEX_TABLE";
252 printTable(Table
, "X86CompressEVEXTable", Macro
, OS
);
254 // Prints function which checks target feature for compressed instructions.
255 printMacroBegin(Macro
, OS
);
256 OS
<< "static bool checkPredicate(unsigned Opc, const X86Subtarget "
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";
267 printMacroEnd(Macro
, OS
);
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
))
277 std::string Name
= Rec
->getName().str();
278 auto Pos
= Name
.find("_NF");
279 if (Pos
== std::string::npos
)
282 if (auto *NewRec
= Records
.getDef(Name
.erase(Pos
, 3))) {
284 auto ClobberEFLAGS
= [](const Record
*R
) {
286 R
->getValueAsListOfDefs("Defs"),
287 [](const Record
*Def
) { return Def
->getName() == "EFLAGS"; });
289 if (ClobberEFLAGS(Rec
))
290 report_fatal_error("EFLAGS should not be clobbered by " +
292 if (!ClobberEFLAGS(NewRec
))
293 report_fatal_error("EFLAGS should be clobbered by " +
296 Table
.push_back(std::pair(&Target
.getInstruction(NewRec
), Inst
));
299 printTable(Table
, "X86NFTransformTable", "GET_X86_NF_TRANSFORM_TABLE", OS
);
302 void X86InstrMappingEmitter::emitND2NonNDTable(
303 ArrayRef
<const CodeGenInstruction
*> Insts
, raw_ostream
&OS
) {
305 const std::map
<StringRef
, StringRef
> ManualMap
= {
306 #define ENTRY_ND(OLD, NEW) {#OLD, #NEW},
307 #include "X86ManualInstrMapping.def"
309 const std::set
<StringRef
> NoCompressSet
= {
310 #define NOCOMP_ND(INSN) #INSN,
311 #include "X86ManualInstrMapping.def"
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())
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
.push_back(std::pair(Inst
, &NewInst
));
328 if (!Name
.ends_with("_ND"))
330 const auto *NewRec
= Records
.getDef(Name
.drop_back(3));
333 const auto &NewInst
= Target
.getInstruction(NewRec
);
334 if (isRegisterOperand(NewInst
.Operands
[0].Rec
))
335 Table
.push_back(std::pair(Inst
, &NewInst
));
337 printTable(Table
, "X86ND2NonNDTable", "GET_X86_ND2NONND_TABLE", OS
);
340 void X86InstrMappingEmitter::emitSSE2AVXTable(
341 ArrayRef
<const CodeGenInstruction
*> Insts
, raw_ostream
&OS
) {
343 const std::map
<StringRef
, StringRef
> ManualMap
= {
344 #define ENTRY_SSE2AVX(OLD, NEW) {#OLD, #NEW},
345 #include "X86ManualInstrMapping.def"
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
))
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
.push_back(std::pair(Inst
, &NewInst
));
362 std::string NewName
= ("V" + Name
).str();
363 const auto *AVXRec
= Records
.getDef(NewName
);
366 auto &AVXInst
= Target
.getInstruction(AVXRec
);
367 Table
.push_back(std::pair(Inst
, &AVXInst
));
369 printTable(Table
, "X86SSE2AVXTable", "GET_X86_SSE2AVX_TABLE", OS
);
372 void X86InstrMappingEmitter::run(raw_ostream
&OS
) {
373 emitSourceFileHeader("X86 instruction mapping", OS
);
375 ArrayRef
<const CodeGenInstruction
*> Insts
=
376 Target
.getInstructionsByEnumValue();
378 emitCompressEVEXTable(Insts
, OS
);
379 emitNFTransformTable(Insts
, OS
);
380 emitND2NonNDTable(Insts
, OS
);
381 emitSSE2AVXTable(Insts
, OS
);
385 static TableGen::Emitter::OptClass
<X86InstrMappingEmitter
>
386 X("gen-x86-instr-mapping", "Generate X86 instruction mapping");