1 | //==- utils/TableGen/X86CompressEVEXTablesEmitter.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 EVEX |
10 | /// compression tables. |
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 | const std::map<StringRef, StringRef> ManualMap = { |
29 | #define ENTRY(OLD, NEW) {#OLD, #NEW}, |
30 | #include "X86ManualCompressEVEXTables.def" |
31 | }; |
32 | const std::set<StringRef> NoCompressSet = { |
33 | #define NOCOMP(INSN) #INSN, |
34 | #include "X86ManualCompressEVEXTables.def" |
35 | }; |
36 | |
37 | class X86CompressEVEXTablesEmitter { |
38 | RecordKeeper &Records; |
39 | CodeGenTarget Target; |
40 | |
41 | // Hold all pontentially compressible EVEX instructions |
42 | std::vector<const CodeGenInstruction *> PreCompressionInsts; |
43 | // Hold all compressed instructions. Divided into groups with same opcodes |
44 | // to make the search more efficient |
45 | std::map<uint64_t, std::vector<const CodeGenInstruction *>> CompressedInsts; |
46 | |
47 | typedef std::pair<const CodeGenInstruction *, const CodeGenInstruction *> |
48 | Entry; |
49 | typedef std::map<StringRef, std::vector<const CodeGenInstruction *>> |
50 | PredicateInstMap; |
51 | |
52 | std::vector<Entry> Table; |
53 | // Hold all compressed instructions that need to check predicate |
54 | PredicateInstMap PredicateInsts; |
55 | |
56 | public: |
57 | X86CompressEVEXTablesEmitter(RecordKeeper &R) : Records(R), Target(R) {} |
58 | |
59 | // run - Output X86 EVEX compression tables. |
60 | void run(raw_ostream &OS); |
61 | |
62 | private: |
63 | // Prints the given table as a C++ array of type X86CompressEVEXTableEntry |
64 | void printTable(const std::vector<Entry> &Table, raw_ostream &OS); |
65 | // Prints function which checks target feature for compressed instructions. |
66 | void printCheckPredicate(const PredicateInstMap &PredicateInsts, |
67 | raw_ostream &OS); |
68 | }; |
69 | |
70 | void X86CompressEVEXTablesEmitter::printTable(const std::vector<Entry> &Table, |
71 | raw_ostream &OS) { |
72 | |
73 | OS << "static const X86CompressEVEXTableEntry X86CompressEVEXTable[] = {\n" ; |
74 | |
75 | // Print all entries added to the table |
76 | for (const auto &Pair : Table) |
77 | OS << " { X86::" << Pair.first->TheDef->getName() |
78 | << ", X86::" << Pair.second->TheDef->getName() << " },\n" ; |
79 | |
80 | OS << "};\n\n" ; |
81 | } |
82 | |
83 | void X86CompressEVEXTablesEmitter::printCheckPredicate( |
84 | const PredicateInstMap &PredicateInsts, raw_ostream &OS) { |
85 | |
86 | OS << "static bool checkPredicate(unsigned Opc, const X86Subtarget " |
87 | "*Subtarget) {\n" |
88 | << " switch (Opc) {\n" |
89 | << " default: return true;\n" ; |
90 | for (const auto &[Key, Val] : PredicateInsts) { |
91 | for (const auto &Inst : Val) |
92 | OS << " case X86::" << Inst->TheDef->getName() << ":\n" ; |
93 | OS << " return " << Key << ";\n" ; |
94 | } |
95 | |
96 | OS << " }\n" ; |
97 | OS << "}\n\n" ; |
98 | } |
99 | |
100 | static uint8_t byteFromBitsInit(const BitsInit *B) { |
101 | unsigned N = B->getNumBits(); |
102 | assert(N <= 8 && "Field is too large for uint8_t!" ); |
103 | |
104 | uint8_t Value = 0; |
105 | for (unsigned I = 0; I != N; ++I) { |
106 | BitInit *Bit = cast<BitInit>(Val: B->getBit(Bit: I)); |
107 | Value |= Bit->getValue() << I; |
108 | } |
109 | return Value; |
110 | } |
111 | |
112 | class IsMatch { |
113 | const CodeGenInstruction *OldInst; |
114 | |
115 | public: |
116 | IsMatch(const CodeGenInstruction *OldInst) : OldInst(OldInst) {} |
117 | |
118 | bool operator()(const CodeGenInstruction *NewInst) { |
119 | RecognizableInstrBase NewRI(*NewInst); |
120 | RecognizableInstrBase OldRI(*OldInst); |
121 | |
122 | // Return false if any of the following fields of does not match. |
123 | if (std::tuple(OldRI.IsCodeGenOnly, OldRI.OpMap, NewRI.OpPrefix, |
124 | OldRI.HasVEX_4V, OldRI.HasVEX_L, OldRI.HasREX_W, |
125 | OldRI.Form) != |
126 | std::tuple(NewRI.IsCodeGenOnly, NewRI.OpMap, OldRI.OpPrefix, |
127 | NewRI.HasVEX_4V, NewRI.HasVEX_L, NewRI.HasREX_W, NewRI.Form)) |
128 | return false; |
129 | |
130 | for (unsigned I = 0, E = OldInst->Operands.size(); I < E; ++I) { |
131 | Record *OldOpRec = OldInst->Operands[I].Rec; |
132 | Record *NewOpRec = NewInst->Operands[I].Rec; |
133 | |
134 | if (OldOpRec == NewOpRec) |
135 | continue; |
136 | |
137 | if (isRegisterOperand(Rec: OldOpRec) && isRegisterOperand(Rec: NewOpRec)) { |
138 | if (getRegOperandSize(RegRec: OldOpRec) != getRegOperandSize(RegRec: NewOpRec)) |
139 | return false; |
140 | } else if (isMemoryOperand(Rec: OldOpRec) && isMemoryOperand(Rec: NewOpRec)) { |
141 | if (getMemOperandSize(MemRec: OldOpRec) != getMemOperandSize(MemRec: NewOpRec)) |
142 | return false; |
143 | } else if (isImmediateOperand(Rec: OldOpRec) && isImmediateOperand(Rec: NewOpRec)) { |
144 | if (OldOpRec->getValueAsDef(FieldName: "Type" ) != NewOpRec->getValueAsDef(FieldName: "Type" )) |
145 | return false; |
146 | } |
147 | } |
148 | |
149 | return true; |
150 | } |
151 | }; |
152 | |
153 | void X86CompressEVEXTablesEmitter::run(raw_ostream &OS) { |
154 | emitSourceFileHeader(Desc: "X86 EVEX compression tables" , OS); |
155 | |
156 | ArrayRef<const CodeGenInstruction *> NumberedInstructions = |
157 | Target.getInstructionsByEnumValue(); |
158 | |
159 | for (const CodeGenInstruction *Inst : NumberedInstructions) { |
160 | const Record *Rec = Inst->TheDef; |
161 | StringRef Name = Rec->getName(); |
162 | // _REV instruction should not appear before encoding optimization |
163 | if (!Rec->isSubClassOf(Name: "X86Inst" ) || |
164 | Rec->getValueAsBit(FieldName: "isAsmParserOnly" ) || Name.ends_with(Suffix: "_REV" )) |
165 | continue; |
166 | |
167 | // Promoted legacy instruction is in EVEX space, and has REX2-encoding |
168 | // alternative. It's added due to HW design and never emitted by compiler. |
169 | if (byteFromBitsInit(B: Rec->getValueAsBitsInit(FieldName: "OpMapBits" )) == |
170 | X86Local::T_MAP4 && |
171 | byteFromBitsInit(B: Rec->getValueAsBitsInit(FieldName: "explicitOpPrefixBits" )) == |
172 | X86Local::ExplicitEVEX) |
173 | continue; |
174 | |
175 | if (NoCompressSet.find(x: Name) != NoCompressSet.end()) |
176 | continue; |
177 | |
178 | RecognizableInstrBase RI(*Inst); |
179 | |
180 | bool IsND = RI.OpMap == X86Local::T_MAP4 && RI.HasEVEX_B && RI.HasVEX_4V; |
181 | // Add VEX encoded instructions to one of CompressedInsts vectors according |
182 | // to it's opcode. |
183 | if (RI.Encoding == X86Local::VEX) |
184 | CompressedInsts[RI.Opcode].push_back(x: Inst); |
185 | // Add relevant EVEX encoded instructions to PreCompressionInsts |
186 | else if (RI.Encoding == X86Local::EVEX && !RI.HasEVEX_K && !RI.HasEVEX_L2 && |
187 | (!RI.HasEVEX_B || IsND)) |
188 | PreCompressionInsts.push_back(x: Inst); |
189 | } |
190 | |
191 | for (const CodeGenInstruction *Inst : PreCompressionInsts) { |
192 | const Record *Rec = Inst->TheDef; |
193 | uint8_t Opcode = byteFromBitsInit(B: Rec->getValueAsBitsInit(FieldName: "Opcode" )); |
194 | StringRef Name = Rec->getName(); |
195 | const CodeGenInstruction *NewInst = nullptr; |
196 | if (ManualMap.find(x: Name) != ManualMap.end()) { |
197 | Record *NewRec = Records.getDef(Name: ManualMap.at(k: Rec->getName())); |
198 | assert(NewRec && "Instruction not found!" ); |
199 | NewInst = &Target.getInstruction(InstRec: NewRec); |
200 | } else if (Name.ends_with(Suffix: "_EVEX" )) { |
201 | if (auto *NewRec = Records.getDef(Name: Name.drop_back(N: 5))) |
202 | NewInst = &Target.getInstruction(InstRec: NewRec); |
203 | } else if (Name.ends_with(Suffix: "_ND" )) { |
204 | if (auto *NewRec = Records.getDef(Name: Name.drop_back(N: 3))) { |
205 | auto &TempInst = Target.getInstruction(InstRec: NewRec); |
206 | if (isRegisterOperand(Rec: TempInst.Operands[0].Rec)) |
207 | NewInst = &TempInst; |
208 | } |
209 | } else { |
210 | // For each pre-compression instruction look for a match in the |
211 | // appropriate vector (instructions with the same opcode) using function |
212 | // object IsMatch. |
213 | auto Match = llvm::find_if(Range&: CompressedInsts[Opcode], P: IsMatch(Inst)); |
214 | if (Match != CompressedInsts[Opcode].end()) |
215 | NewInst = *Match; |
216 | } |
217 | |
218 | if (!NewInst) |
219 | continue; |
220 | |
221 | Table.push_back(x: std::pair(Inst, NewInst)); |
222 | auto Predicates = NewInst->TheDef->getValueAsListOfDefs(FieldName: "Predicates" ); |
223 | auto It = llvm::find_if(Range&: Predicates, P: [](const Record *R) { |
224 | StringRef Name = R->getName(); |
225 | return Name == "HasAVXNECONVERT" || Name == "HasAVXVNNI" || |
226 | Name == "HasAVXIFMA" ; |
227 | }); |
228 | if (It != Predicates.end()) |
229 | PredicateInsts[(*It)->getValueAsString(FieldName: "CondString" )].push_back(x: NewInst); |
230 | } |
231 | |
232 | printTable(Table, OS); |
233 | printCheckPredicate(PredicateInsts, OS); |
234 | } |
235 | } // namespace |
236 | |
237 | static TableGen::Emitter::OptClass<X86CompressEVEXTablesEmitter> |
238 | X("gen-x86-compress-evex-tables" , "Generate X86 EVEX compression tables" ); |
239 | |