1317017Sdim//===- utils/TableGen/X86EVEX2VEXTablesEmitter.cpp - X86 backend-*- C++ -*-===// 2317017Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6317017Sdim// 7317017Sdim//===----------------------------------------------------------------------===// 8317017Sdim/// 9317017Sdim/// This tablegen backend is responsible for emitting the X86 backend EVEX2VEX 10317017Sdim/// compression tables. 11317017Sdim/// 12317017Sdim//===----------------------------------------------------------------------===// 13317017Sdim 14317017Sdim#include "CodeGenTarget.h" 15317017Sdim#include "llvm/TableGen/Error.h" 16317017Sdim#include "llvm/TableGen/TableGenBackend.h" 17317017Sdim 18317017Sdimusing namespace llvm; 19317017Sdim 20317017Sdimnamespace { 21317017Sdim 22317017Sdimclass X86EVEX2VEXTablesEmitter { 23341825Sdim RecordKeeper &Records; 24317017Sdim CodeGenTarget Target; 25317017Sdim 26317017Sdim // Hold all non-masked & non-broadcasted EVEX encoded instructions 27317017Sdim std::vector<const CodeGenInstruction *> EVEXInsts; 28317017Sdim // Hold all VEX encoded instructions. Divided into groups with same opcodes 29317017Sdim // to make the search more efficient 30317017Sdim std::map<uint64_t, std::vector<const CodeGenInstruction *>> VEXInsts; 31317017Sdim 32317017Sdim typedef std::pair<const CodeGenInstruction *, const CodeGenInstruction *> Entry; 33317017Sdim 34317017Sdim // Represent both compress tables 35317017Sdim std::vector<Entry> EVEX2VEX128; 36317017Sdim std::vector<Entry> EVEX2VEX256; 37317017Sdim 38317017Sdimpublic: 39341825Sdim X86EVEX2VEXTablesEmitter(RecordKeeper &R) : Records(R), Target(R) {} 40317017Sdim 41317017Sdim // run - Output X86 EVEX2VEX tables. 42317017Sdim void run(raw_ostream &OS); 43317017Sdim 44317017Sdimprivate: 45317017Sdim // Prints the given table as a C++ array of type 46317017Sdim // X86EvexToVexCompressTableEntry 47317017Sdim void printTable(const std::vector<Entry> &Table, raw_ostream &OS); 48317017Sdim}; 49317017Sdim 50317017Sdimvoid X86EVEX2VEXTablesEmitter::printTable(const std::vector<Entry> &Table, 51317017Sdim raw_ostream &OS) { 52341825Sdim StringRef Size = (Table == EVEX2VEX128) ? "128" : "256"; 53317017Sdim 54317017Sdim OS << "// X86 EVEX encoded instructions that have a VEX " << Size 55317017Sdim << " encoding\n" 56317017Sdim << "// (table format: <EVEX opcode, VEX-" << Size << " opcode>).\n" 57317017Sdim << "static const X86EvexToVexCompressTableEntry X86EvexToVex" << Size 58317017Sdim << "CompressTable[] = {\n" 59317017Sdim << " // EVEX scalar with corresponding VEX.\n"; 60317017Sdim 61317017Sdim // Print all entries added to the table 62317017Sdim for (auto Pair : Table) { 63317017Sdim OS << " { X86::" << Pair.first->TheDef->getName() 64317017Sdim << ", X86::" << Pair.second->TheDef->getName() << " },\n"; 65317017Sdim } 66317017Sdim 67317017Sdim OS << "};\n\n"; 68317017Sdim} 69317017Sdim 70317017Sdim// Return true if the 2 BitsInits are equal 71317017Sdim// Calculates the integer value residing BitsInit object 72317017Sdimstatic inline uint64_t getValueFromBitsInit(const BitsInit *B) { 73317017Sdim uint64_t Value = 0; 74317017Sdim for (unsigned i = 0, e = B->getNumBits(); i != e; ++i) { 75317017Sdim if (BitInit *Bit = dyn_cast<BitInit>(B->getBit(i))) 76317017Sdim Value |= uint64_t(Bit->getValue()) << i; 77317017Sdim else 78317017Sdim PrintFatalError("Invalid VectSize bit"); 79317017Sdim } 80317017Sdim return Value; 81317017Sdim} 82317017Sdim 83317017Sdim// Function object - Operator() returns true if the given VEX instruction 84317017Sdim// matches the EVEX instruction of this object. 85317017Sdimclass IsMatch { 86341825Sdim const CodeGenInstruction *EVEXInst; 87317017Sdim 88317017Sdimpublic: 89341825Sdim IsMatch(const CodeGenInstruction *EVEXInst) : EVEXInst(EVEXInst) {} 90317017Sdim 91341825Sdim bool operator()(const CodeGenInstruction *VEXInst) { 92341825Sdim Record *RecE = EVEXInst->TheDef; 93341825Sdim Record *RecV = VEXInst->TheDef; 94353358Sdim bool EVEX_W = RecE->getValueAsBit("HasVEX_W"); 95353358Sdim bool VEX_W = RecV->getValueAsBit("HasVEX_W"); 96353358Sdim bool VEX_WIG = RecV->getValueAsBit("IgnoresVEX_W"); 97353358Sdim bool EVEX_WIG = RecE->getValueAsBit("IgnoresVEX_W"); 98353358Sdim bool EVEX_W1_VEX_W0 = RecE->getValueAsBit("EVEX_W1_VEX_W0"); 99317017Sdim 100341825Sdim if (RecV->getValueAsDef("OpEnc")->getName().str() != "EncVEX" || 101360784Sdim RecV->getValueAsBit("isCodeGenOnly") != RecE->getValueAsBit("isCodeGenOnly") || 102317017Sdim // VEX/EVEX fields 103341825Sdim RecV->getValueAsDef("OpPrefix") != RecE->getValueAsDef("OpPrefix") || 104341825Sdim RecV->getValueAsDef("OpMap") != RecE->getValueAsDef("OpMap") || 105341825Sdim RecV->getValueAsBit("hasVEX_4V") != RecE->getValueAsBit("hasVEX_4V") || 106353358Sdim RecV->getValueAsBit("hasEVEX_L2") != RecE->getValueAsBit("hasEVEX_L2") || 107353358Sdim RecV->getValueAsBit("hasVEX_L") != RecE->getValueAsBit("hasVEX_L") || 108341825Sdim // Match is allowed if either is VEX_WIG, or they match, or EVEX 109341825Sdim // is VEX_W1X and VEX is VEX_W0. 110353358Sdim (!(VEX_WIG || (!EVEX_WIG && EVEX_W == VEX_W) || 111353358Sdim (EVEX_W1_VEX_W0 && EVEX_W && !VEX_W))) || 112317017Sdim // Instruction's format 113353358Sdim RecV->getValueAsDef("Form") != RecE->getValueAsDef("Form")) 114317017Sdim return false; 115317017Sdim 116317017Sdim // This is needed for instructions with intrinsic version (_Int). 117317017Sdim // Where the only difference is the size of the operands. 118317017Sdim // For example: VUCOMISDZrm and Int_VUCOMISDrm 119317017Sdim // Also for instructions that their EVEX version was upgraded to work with 120317017Sdim // k-registers. For example VPCMPEQBrm (xmm output register) and 121317017Sdim // VPCMPEQBZ128rm (k register output register). 122341825Sdim for (unsigned i = 0, e = EVEXInst->Operands.size(); i < e; i++) { 123341825Sdim Record *OpRec1 = EVEXInst->Operands[i].Rec; 124341825Sdim Record *OpRec2 = VEXInst->Operands[i].Rec; 125317017Sdim 126317017Sdim if (OpRec1 == OpRec2) 127317017Sdim continue; 128317017Sdim 129317017Sdim if (isRegisterOperand(OpRec1) && isRegisterOperand(OpRec2)) { 130317017Sdim if (getRegOperandSize(OpRec1) != getRegOperandSize(OpRec2)) 131317017Sdim return false; 132317017Sdim } else if (isMemoryOperand(OpRec1) && isMemoryOperand(OpRec2)) { 133317017Sdim return false; 134317017Sdim } else if (isImmediateOperand(OpRec1) && isImmediateOperand(OpRec2)) { 135353358Sdim if (OpRec1->getValueAsDef("Type") != OpRec2->getValueAsDef("Type")) { 136317017Sdim return false; 137353358Sdim } 138317017Sdim } else 139317017Sdim return false; 140317017Sdim } 141317017Sdim 142317017Sdim return true; 143317017Sdim } 144317017Sdim 145317017Sdimprivate: 146317017Sdim static inline bool isRegisterOperand(const Record *Rec) { 147317017Sdim return Rec->isSubClassOf("RegisterClass") || 148317017Sdim Rec->isSubClassOf("RegisterOperand"); 149317017Sdim } 150317017Sdim 151317017Sdim static inline bool isMemoryOperand(const Record *Rec) { 152317017Sdim return Rec->isSubClassOf("Operand") && 153317017Sdim Rec->getValueAsString("OperandType") == "OPERAND_MEMORY"; 154317017Sdim } 155317017Sdim 156317017Sdim static inline bool isImmediateOperand(const Record *Rec) { 157317017Sdim return Rec->isSubClassOf("Operand") && 158317017Sdim Rec->getValueAsString("OperandType") == "OPERAND_IMMEDIATE"; 159317017Sdim } 160317017Sdim 161317017Sdim static inline unsigned int getRegOperandSize(const Record *RegRec) { 162317017Sdim if (RegRec->isSubClassOf("RegisterClass")) 163317017Sdim return RegRec->getValueAsInt("Alignment"); 164317017Sdim if (RegRec->isSubClassOf("RegisterOperand")) 165317017Sdim return RegRec->getValueAsDef("RegClass")->getValueAsInt("Alignment"); 166317017Sdim 167317017Sdim llvm_unreachable("Register operand's size not known!"); 168317017Sdim } 169317017Sdim}; 170317017Sdim 171317017Sdimvoid X86EVEX2VEXTablesEmitter::run(raw_ostream &OS) { 172317017Sdim emitSourceFileHeader("X86 EVEX2VEX tables", OS); 173317017Sdim 174317017Sdim ArrayRef<const CodeGenInstruction *> NumberedInstructions = 175317017Sdim Target.getInstructionsByEnumValue(); 176317017Sdim 177317017Sdim for (const CodeGenInstruction *Inst : NumberedInstructions) { 178317017Sdim // Filter non-X86 instructions. 179317017Sdim if (!Inst->TheDef->isSubClassOf("X86Inst")) 180317017Sdim continue; 181317017Sdim 182317017Sdim // Add VEX encoded instructions to one of VEXInsts vectors according to 183317017Sdim // it's opcode. 184317017Sdim if (Inst->TheDef->getValueAsDef("OpEnc")->getName() == "EncVEX") { 185317017Sdim uint64_t Opcode = getValueFromBitsInit(Inst->TheDef-> 186317017Sdim getValueAsBitsInit("Opcode")); 187317017Sdim VEXInsts[Opcode].push_back(Inst); 188317017Sdim } 189317017Sdim // Add relevant EVEX encoded instructions to EVEXInsts 190317017Sdim else if (Inst->TheDef->getValueAsDef("OpEnc")->getName() == "EncEVEX" && 191317017Sdim !Inst->TheDef->getValueAsBit("hasEVEX_K") && 192317017Sdim !Inst->TheDef->getValueAsBit("hasEVEX_B") && 193353358Sdim !Inst->TheDef->getValueAsBit("hasEVEX_L2") && 194341825Sdim !Inst->TheDef->getValueAsBit("notEVEX2VEXConvertible")) 195317017Sdim EVEXInsts.push_back(Inst); 196317017Sdim } 197317017Sdim 198317017Sdim for (const CodeGenInstruction *EVEXInst : EVEXInsts) { 199317017Sdim uint64_t Opcode = getValueFromBitsInit(EVEXInst->TheDef-> 200317017Sdim getValueAsBitsInit("Opcode")); 201317017Sdim // For each EVEX instruction look for a VEX match in the appropriate vector 202317017Sdim // (instructions with the same opcode) using function object IsMatch. 203341825Sdim // Allow EVEX2VEXOverride to explicitly specify a match. 204341825Sdim const CodeGenInstruction *VEXInst = nullptr; 205341825Sdim if (!EVEXInst->TheDef->isValueUnset("EVEX2VEXOverride")) { 206341825Sdim StringRef AltInstStr = 207341825Sdim EVEXInst->TheDef->getValueAsString("EVEX2VEXOverride"); 208341825Sdim Record *AltInstRec = Records.getDef(AltInstStr); 209341825Sdim assert(AltInstRec && "EVEX2VEXOverride instruction not found!"); 210341825Sdim VEXInst = &Target.getInstruction(AltInstRec); 211341825Sdim } else { 212341825Sdim auto Match = llvm::find_if(VEXInsts[Opcode], IsMatch(EVEXInst)); 213341825Sdim if (Match != VEXInsts[Opcode].end()) 214341825Sdim VEXInst = *Match; 215341825Sdim } 216317017Sdim 217341825Sdim if (!VEXInst) 218341825Sdim continue; 219341825Sdim 220341825Sdim // In case a match is found add new entry to the appropriate table 221353358Sdim if (EVEXInst->TheDef->getValueAsBit("hasVEX_L")) 222353358Sdim EVEX2VEX256.push_back(std::make_pair(EVEXInst, VEXInst)); // {0,1} 223353358Sdim else 224341825Sdim EVEX2VEX128.push_back(std::make_pair(EVEXInst, VEXInst)); // {0,0} 225317017Sdim } 226317017Sdim 227317017Sdim // Print both tables 228317017Sdim printTable(EVEX2VEX128, OS); 229317017Sdim printTable(EVEX2VEX256, OS); 230317017Sdim} 231317017Sdim} 232317017Sdim 233317017Sdimnamespace llvm { 234317017Sdimvoid EmitX86EVEX2VEXTables(RecordKeeper &RK, raw_ostream &OS) { 235317017Sdim X86EVEX2VEXTablesEmitter(RK).run(OS); 236317017Sdim} 237317017Sdim} 238