| 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: |
| 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 | |
| 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(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 | |
| 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>(Val: B->getBit(Bit: I)); |
| 116 | Value |= Bit->getValue() << I; |
| 117 | } |
| 118 | return Value; |
| 119 | } |
| 120 | |
| 121 | class IsMatch { |
| 122 | const CodeGenInstruction *OldInst; |
| 123 | |
| 124 | public: |
| 125 | IsMatch(const CodeGenInstruction *OldInst) : OldInst(OldInst) {} |
| 126 | |
| 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(Rec: OldOpRec) && isRegisterOperand(Rec: NewOpRec)) { |
| 147 | if (getRegOperandSize(RegRec: OldOpRec) != getRegOperandSize(RegRec: NewOpRec)) |
| 148 | return false; |
| 149 | } else if (isMemoryOperand(Rec: OldOpRec) && isMemoryOperand(Rec: NewOpRec)) { |
| 150 | if (getMemOperandSize(MemRec: OldOpRec) != getMemOperandSize(MemRec: NewOpRec)) |
| 151 | return false; |
| 152 | } else if (isImmediateOperand(Rec: OldOpRec) && isImmediateOperand(Rec: NewOpRec)) { |
| 153 | if (OldOpRec->getValueAsDef(FieldName: "Type" ) != NewOpRec->getValueAsDef(FieldName: "Type" )) |
| 154 | return false; |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | return true; |
| 159 | } |
| 160 | }; |
| 161 | |
| 162 | static bool isInteresting(const Record *Rec) { |
| 163 | // _REV instruction should not appear before encoding optimization |
| 164 | return Rec->isSubClassOf(Name: "X86Inst" ) && |
| 165 | !Rec->getValueAsBit(FieldName: "isAsmParserOnly" ) && |
| 166 | !Rec->getName().ends_with(Suffix: "_REV" ); |
| 167 | } |
| 168 | |
| 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(B: Rec->getValueAsBitsInit(FieldName: "OpMapBits" )) == |
| 190 | X86Local::T_MAP4 && |
| 191 | byteFromBitsInit(B: Rec->getValueAsBitsInit(FieldName: "explicitOpPrefixBits" )) == |
| 192 | X86Local::ExplicitEVEX) |
| 193 | continue; |
| 194 | |
| 195 | if (NoCompressSet.find(x: 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(x: 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(x: 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(B: Rec->getValueAsBitsInit(FieldName: "Opcode" )); |
| 215 | StringRef Name = Rec->getName(); |
| 216 | const CodeGenInstruction *NewInst = nullptr; |
| 217 | if (ManualMap.find(x: Name) != ManualMap.end()) { |
| 218 | const Record *NewRec = Records.getDef(Name: ManualMap.at(k: Rec->getName())); |
| 219 | assert(NewRec && "Instruction not found!" ); |
| 220 | NewInst = &Target.getInstruction(InstRec: NewRec); |
| 221 | } else if (Name.ends_with(Suffix: "_EVEX" )) { |
| 222 | if (const auto *NewRec = Records.getDef(Name: Name.drop_back(N: 5))) |
| 223 | NewInst = &Target.getInstruction(InstRec: NewRec); |
| 224 | } else if (Name.ends_with(Suffix: "_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(Range: Insts, P: IsMatch(Inst)); |
| 233 | if (Match != Insts.end()) |
| 234 | NewInst = *Match; |
| 235 | } |
| 236 | |
| 237 | if (!NewInst) |
| 238 | continue; |
| 239 | |
| 240 | Table.emplace_back(args&: Inst, args&: 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" || Name == "HasAVXVNNIINT8" || |
| 246 | Name == "HasAVXVNNIINT16" ; |
| 247 | }); |
| 248 | if (It != Predicates.end()) |
| 249 | PredicateInsts[(*It)->getValueAsString(FieldName: "CondString" )].push_back(x: NewInst); |
| 250 | } |
| 251 | |
| 252 | StringRef Macro = "GET_X86_COMPRESS_EVEX_TABLE" ; |
| 253 | printTable(Table, Name: "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 | |
| 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(Other: "_NF" )) |
| 280 | continue; |
| 281 | |
| 282 | if (auto *NewRec = Name.consume_back(Suffix: "_ND" ) |
| 283 | ? Records.getDef(Name: Name.str() + "_NF_ND" ) |
| 284 | : Records.getDef(Name: 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(args&: Inst, args: &Target.getInstruction(InstRec: NewRec)); |
| 298 | } |
| 299 | } |
| 300 | printTable(Table, Name: "X86NFTransformTable" , Macro: "GET_X86_NF_TRANSFORM_TABLE" , OS); |
| 301 | } |
| 302 | |
| 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(x: Name) != NoCompressSet.end()) |
| 320 | continue; |
| 321 | if (ManualMap.find(x: Name) != ManualMap.end()) { |
| 322 | const auto *NewRec = Records.getDef(Name: ManualMap.at(k: Rec->getName())); |
| 323 | assert(NewRec && "Instruction not found!" ); |
| 324 | auto &NewInst = Target.getInstruction(InstRec: NewRec); |
| 325 | Table.emplace_back(args&: Inst, args: &NewInst); |
| 326 | continue; |
| 327 | } |
| 328 | |
| 329 | if (!Name.ends_with(Suffix: "_ND" )) |
| 330 | continue; |
| 331 | const auto *NewRec = Records.getDef(Name: Name.drop_back(N: 3)); |
| 332 | if (!NewRec) |
| 333 | continue; |
| 334 | const auto &NewInst = Target.getInstruction(InstRec: NewRec); |
| 335 | if (isRegisterOperand(Rec: NewInst.Operands[0].Rec)) |
| 336 | Table.emplace_back(args&: Inst, args: &NewInst); |
| 337 | } |
| 338 | printTable(Table, Name: "X86ND2NonNDTable" , Macro: "GET_X86_ND2NONND_TABLE" , OS); |
| 339 | } |
| 340 | |
| 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(x: Name) != ManualMap.end()) { |
| 356 | const auto *NewRec = Records.getDef(Name: ManualMap.at(k: Rec->getName())); |
| 357 | assert(NewRec && "Instruction not found!" ); |
| 358 | const auto &NewInst = Target.getInstruction(InstRec: NewRec); |
| 359 | Table.emplace_back(args&: Inst, args: &NewInst); |
| 360 | continue; |
| 361 | } |
| 362 | |
| 363 | std::string NewName = ("V" + Name).str(); |
| 364 | const auto *AVXRec = Records.getDef(Name: NewName); |
| 365 | if (!AVXRec) |
| 366 | continue; |
| 367 | auto &AVXInst = Target.getInstruction(InstRec: AVXRec); |
| 368 | Table.emplace_back(args&: Inst, args: &AVXInst); |
| 369 | } |
| 370 | printTable(Table, Name: "X86SSE2AVXTable" , Macro: "GET_X86_SSE2AVX_TABLE" , OS); |
| 371 | } |
| 372 | |
| 373 | void X86InstrMappingEmitter::run(raw_ostream &OS) { |
| 374 | emitSourceFileHeader(Desc: "X86 instruction mapping" , OS); |
| 375 | |
| 376 | ArrayRef<const CodeGenInstruction *> Insts = |
| 377 | Target.getInstructionsByEnumValue(); |
| 378 | printClassDef(OS); |
| 379 | emitCompressEVEXTable(Insts, OS); |
| 380 | emitNFTransformTable(Insts, OS); |
| 381 | emitND2NonNDTable(Insts, OS); |
| 382 | emitSSE2AVXTable(Insts, OS); |
| 383 | } |
| 384 | } // namespace |
| 385 | |
| 386 | static TableGen::Emitter::OptClass<X86InstrMappingEmitter> |
| 387 | X("gen-x86-instr-mapping" , "Generate X86 instruction mapping" ); |
| 388 | |