Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/utils/TableGen/X86InstrMappingEmitter.cpp
35258 views
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
388