Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 1 | //===- utils/TableGen/X86EVEX2VEXTablesEmitter.cpp - X86 backend-*- C++ -*-===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | /// |
| 10 | /// This tablegen backend is responsible for emitting the X86 backend EVEX2VEX |
| 11 | /// compression tables. |
| 12 | /// |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 15 | #include "CodeGenTarget.h" |
| 16 | #include "llvm/TableGen/Error.h" |
| 17 | #include "llvm/TableGen/TableGenBackend.h" |
| 18 | |
| 19 | using namespace llvm; |
| 20 | |
| 21 | namespace { |
| 22 | |
| 23 | class X86EVEX2VEXTablesEmitter { |
Craig Topper | e5b799b | 2018-06-19 04:24:44 +0000 | [diff] [blame] | 24 | RecordKeeper &Records; |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 25 | CodeGenTarget Target; |
| 26 | |
| 27 | // Hold all non-masked & non-broadcasted EVEX encoded instructions |
| 28 | std::vector<const CodeGenInstruction *> EVEXInsts; |
| 29 | // Hold all VEX encoded instructions. Divided into groups with same opcodes |
| 30 | // to make the search more efficient |
| 31 | std::map<uint64_t, std::vector<const CodeGenInstruction *>> VEXInsts; |
| 32 | |
| 33 | typedef std::pair<const CodeGenInstruction *, const CodeGenInstruction *> Entry; |
| 34 | |
| 35 | // Represent both compress tables |
| 36 | std::vector<Entry> EVEX2VEX128; |
| 37 | std::vector<Entry> EVEX2VEX256; |
| 38 | |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 39 | public: |
Craig Topper | e5b799b | 2018-06-19 04:24:44 +0000 | [diff] [blame] | 40 | X86EVEX2VEXTablesEmitter(RecordKeeper &R) : Records(R), Target(R) {} |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 41 | |
| 42 | // run - Output X86 EVEX2VEX tables. |
| 43 | void run(raw_ostream &OS); |
| 44 | |
| 45 | private: |
| 46 | // Prints the given table as a C++ array of type |
| 47 | // X86EvexToVexCompressTableEntry |
| 48 | void printTable(const std::vector<Entry> &Table, raw_ostream &OS); |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 49 | }; |
| 50 | |
| 51 | void X86EVEX2VEXTablesEmitter::printTable(const std::vector<Entry> &Table, |
| 52 | raw_ostream &OS) { |
Craig Topper | 2bcbecf | 2018-06-18 18:47:07 +0000 | [diff] [blame] | 53 | StringRef Size = (Table == EVEX2VEX128) ? "128" : "256"; |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 54 | |
| 55 | OS << "// X86 EVEX encoded instructions that have a VEX " << Size |
| 56 | << " encoding\n" |
| 57 | << "// (table format: <EVEX opcode, VEX-" << Size << " opcode>).\n" |
| 58 | << "static const X86EvexToVexCompressTableEntry X86EvexToVex" << Size |
| 59 | << "CompressTable[] = {\n" |
| 60 | << " // EVEX scalar with corresponding VEX.\n"; |
| 61 | |
| 62 | // Print all entries added to the table |
| 63 | for (auto Pair : Table) { |
Craig Topper | 026472f | 2017-03-13 00:36:46 +0000 | [diff] [blame] | 64 | OS << " { X86::" << Pair.first->TheDef->getName() |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 65 | << ", X86::" << Pair.second->TheDef->getName() << " },\n"; |
| 66 | } |
| 67 | |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 68 | OS << "};\n\n"; |
| 69 | } |
| 70 | |
| 71 | // Return true if the 2 BitsInits are equal |
| 72 | static inline bool equalBitsInits(const BitsInit *B1, const BitsInit *B2) { |
| 73 | if (B1->getNumBits() != B2->getNumBits()) |
| 74 | PrintFatalError("Comparing two BitsInits with different sizes!"); |
| 75 | |
| 76 | for (unsigned i = 0, e = B1->getNumBits(); i != e; ++i) { |
| 77 | if (BitInit *Bit1 = dyn_cast<BitInit>(B1->getBit(i))) { |
| 78 | if (BitInit *Bit2 = dyn_cast<BitInit>(B2->getBit(i))) { |
| 79 | if (Bit1->getValue() != Bit2->getValue()) |
| 80 | return false; |
| 81 | } else |
| 82 | PrintFatalError("Invalid BitsInit bit"); |
| 83 | } else |
| 84 | PrintFatalError("Invalid BitsInit bit"); |
| 85 | } |
| 86 | return true; |
| 87 | } |
| 88 | |
| 89 | // Calculates the integer value residing BitsInit object |
| 90 | static inline uint64_t getValueFromBitsInit(const BitsInit *B) { |
| 91 | uint64_t Value = 0; |
| 92 | for (unsigned i = 0, e = B->getNumBits(); i != e; ++i) { |
| 93 | if (BitInit *Bit = dyn_cast<BitInit>(B->getBit(i))) |
| 94 | Value |= uint64_t(Bit->getValue()) << i; |
| 95 | else |
| 96 | PrintFatalError("Invalid VectSize bit"); |
| 97 | } |
| 98 | return Value; |
| 99 | } |
| 100 | |
| 101 | // Function object - Operator() returns true if the given VEX instruction |
| 102 | // matches the EVEX instruction of this object. |
| 103 | class IsMatch { |
Craig Topper | 023b407 | 2018-06-19 04:24:42 +0000 | [diff] [blame] | 104 | const CodeGenInstruction *EVEXInst; |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 105 | |
| 106 | public: |
Craig Topper | 023b407 | 2018-06-19 04:24:42 +0000 | [diff] [blame] | 107 | IsMatch(const CodeGenInstruction *EVEXInst) : EVEXInst(EVEXInst) {} |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 108 | |
Craig Topper | 023b407 | 2018-06-19 04:24:42 +0000 | [diff] [blame] | 109 | bool operator()(const CodeGenInstruction *VEXInst) { |
| 110 | Record *RecE = EVEXInst->TheDef; |
| 111 | Record *RecV = VEXInst->TheDef; |
| 112 | uint64_t EVEX_W = |
| 113 | getValueFromBitsInit(RecE->getValueAsBitsInit("VEX_WPrefix")); |
| 114 | uint64_t VEX_W = |
| 115 | getValueFromBitsInit(RecV->getValueAsBitsInit("VEX_WPrefix")); |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 116 | |
Craig Topper | 023b407 | 2018-06-19 04:24:42 +0000 | [diff] [blame] | 117 | if (RecV->getValueAsDef("OpEnc")->getName().str() != "EncVEX" || |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 118 | // VEX/EVEX fields |
Craig Topper | 023b407 | 2018-06-19 04:24:42 +0000 | [diff] [blame] | 119 | RecV->getValueAsDef("OpPrefix") != RecE->getValueAsDef("OpPrefix") || |
| 120 | RecV->getValueAsDef("OpMap") != RecE->getValueAsDef("OpMap") || |
| 121 | RecV->getValueAsBit("hasVEX_4V") != RecE->getValueAsBit("hasVEX_4V") || |
| 122 | !equalBitsInits(RecV->getValueAsBitsInit("EVEX_LL"), |
| 123 | RecE->getValueAsBitsInit("EVEX_LL")) || |
| 124 | // Match is allowed if either is VEX_WIG, or they match, or EVEX |
| 125 | // is VEX_W1X and VEX is VEX_W0. |
| 126 | (!(EVEX_W == 2 || VEX_W == 2 || EVEX_W == VEX_W || |
| 127 | (EVEX_W == 3 && VEX_W == 0))) || |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 128 | // Instruction's format |
Craig Topper | 023b407 | 2018-06-19 04:24:42 +0000 | [diff] [blame] | 129 | RecV->getValueAsDef("Form") != RecE->getValueAsDef("Form") || |
| 130 | RecV->getValueAsBit("isAsmParserOnly") != |
| 131 | RecE->getValueAsBit("isAsmParserOnly")) |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 132 | return false; |
| 133 | |
| 134 | // This is needed for instructions with intrinsic version (_Int). |
| 135 | // Where the only difference is the size of the operands. |
| 136 | // For example: VUCOMISDZrm and Int_VUCOMISDrm |
| 137 | // Also for instructions that their EVEX version was upgraded to work with |
| 138 | // k-registers. For example VPCMPEQBrm (xmm output register) and |
| 139 | // VPCMPEQBZ128rm (k register output register). |
Craig Topper | 023b407 | 2018-06-19 04:24:42 +0000 | [diff] [blame] | 140 | for (unsigned i = 0, e = EVEXInst->Operands.size(); i < e; i++) { |
| 141 | Record *OpRec1 = EVEXInst->Operands[i].Rec; |
| 142 | Record *OpRec2 = VEXInst->Operands[i].Rec; |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 143 | |
| 144 | if (OpRec1 == OpRec2) |
| 145 | continue; |
| 146 | |
| 147 | if (isRegisterOperand(OpRec1) && isRegisterOperand(OpRec2)) { |
| 148 | if (getRegOperandSize(OpRec1) != getRegOperandSize(OpRec2)) |
| 149 | return false; |
| 150 | } else if (isMemoryOperand(OpRec1) && isMemoryOperand(OpRec2)) { |
Craig Topper | 760a318 | 2017-03-13 05:34:03 +0000 | [diff] [blame] | 151 | return false; |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 152 | } else if (isImmediateOperand(OpRec1) && isImmediateOperand(OpRec2)) { |
| 153 | if (OpRec1->getValueAsDef("Type") != OpRec2->getValueAsDef("Type")) |
| 154 | return false; |
| 155 | } else |
| 156 | return false; |
| 157 | } |
| 158 | |
| 159 | return true; |
| 160 | } |
| 161 | |
| 162 | private: |
| 163 | static inline bool isRegisterOperand(const Record *Rec) { |
| 164 | return Rec->isSubClassOf("RegisterClass") || |
| 165 | Rec->isSubClassOf("RegisterOperand"); |
| 166 | } |
| 167 | |
| 168 | static inline bool isMemoryOperand(const Record *Rec) { |
| 169 | return Rec->isSubClassOf("Operand") && |
| 170 | Rec->getValueAsString("OperandType") == "OPERAND_MEMORY"; |
| 171 | } |
| 172 | |
| 173 | static inline bool isImmediateOperand(const Record *Rec) { |
| 174 | return Rec->isSubClassOf("Operand") && |
| 175 | Rec->getValueAsString("OperandType") == "OPERAND_IMMEDIATE"; |
| 176 | } |
| 177 | |
| 178 | static inline unsigned int getRegOperandSize(const Record *RegRec) { |
| 179 | if (RegRec->isSubClassOf("RegisterClass")) |
| 180 | return RegRec->getValueAsInt("Alignment"); |
| 181 | if (RegRec->isSubClassOf("RegisterOperand")) |
| 182 | return RegRec->getValueAsDef("RegClass")->getValueAsInt("Alignment"); |
| 183 | |
| 184 | llvm_unreachable("Register operand's size not known!"); |
| 185 | } |
| 186 | }; |
| 187 | |
| 188 | void X86EVEX2VEXTablesEmitter::run(raw_ostream &OS) { |
| 189 | emitSourceFileHeader("X86 EVEX2VEX tables", OS); |
| 190 | |
| 191 | ArrayRef<const CodeGenInstruction *> NumberedInstructions = |
| 192 | Target.getInstructionsByEnumValue(); |
| 193 | |
| 194 | for (const CodeGenInstruction *Inst : NumberedInstructions) { |
| 195 | // Filter non-X86 instructions. |
| 196 | if (!Inst->TheDef->isSubClassOf("X86Inst")) |
| 197 | continue; |
| 198 | |
| 199 | // Add VEX encoded instructions to one of VEXInsts vectors according to |
| 200 | // it's opcode. |
| 201 | if (Inst->TheDef->getValueAsDef("OpEnc")->getName() == "EncVEX") { |
| 202 | uint64_t Opcode = getValueFromBitsInit(Inst->TheDef-> |
| 203 | getValueAsBitsInit("Opcode")); |
| 204 | VEXInsts[Opcode].push_back(Inst); |
| 205 | } |
| 206 | // Add relevant EVEX encoded instructions to EVEXInsts |
| 207 | else if (Inst->TheDef->getValueAsDef("OpEnc")->getName() == "EncEVEX" && |
| 208 | !Inst->TheDef->getValueAsBit("hasEVEX_K") && |
| 209 | !Inst->TheDef->getValueAsBit("hasEVEX_B") && |
| 210 | getValueFromBitsInit(Inst->TheDef-> |
| 211 | getValueAsBitsInit("EVEX_LL")) != 2 && |
Craig Topper | 2bcbecf | 2018-06-18 18:47:07 +0000 | [diff] [blame] | 212 | !Inst->TheDef->getValueAsBit("notEVEX2VEXConvertible")) |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 213 | EVEXInsts.push_back(Inst); |
| 214 | } |
| 215 | |
| 216 | for (const CodeGenInstruction *EVEXInst : EVEXInsts) { |
| 217 | uint64_t Opcode = getValueFromBitsInit(EVEXInst->TheDef-> |
| 218 | getValueAsBitsInit("Opcode")); |
| 219 | // For each EVEX instruction look for a VEX match in the appropriate vector |
| 220 | // (instructions with the same opcode) using function object IsMatch. |
Craig Topper | e5b799b | 2018-06-19 04:24:44 +0000 | [diff] [blame] | 221 | // Allow EVEX2VEXOverride to explicitly specify a match. |
| 222 | const CodeGenInstruction *VEXInst = nullptr; |
| 223 | if (!EVEXInst->TheDef->isValueUnset("EVEX2VEXOverride")) { |
| 224 | StringRef AltInstStr = |
| 225 | EVEXInst->TheDef->getValueAsString("EVEX2VEXOverride"); |
| 226 | Record *AltInstRec = Records.getDef(AltInstStr); |
| 227 | assert(AltInstRec && "EVEX2VEXOverride instruction not found!"); |
| 228 | VEXInst = &Target.getInstruction(AltInstRec); |
| 229 | } else { |
| 230 | auto Match = llvm::find_if(VEXInsts[Opcode], IsMatch(EVEXInst)); |
| 231 | if (Match != VEXInsts[Opcode].end()) |
| 232 | VEXInst = *Match; |
| 233 | } |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 234 | |
Craig Topper | e5b799b | 2018-06-19 04:24:44 +0000 | [diff] [blame] | 235 | if (!VEXInst) |
| 236 | continue; |
| 237 | |
| 238 | // In case a match is found add new entry to the appropriate table |
| 239 | switch (getValueFromBitsInit( |
| 240 | EVEXInst->TheDef->getValueAsBitsInit("EVEX_LL"))) { |
| 241 | case 0: |
| 242 | EVEX2VEX128.push_back(std::make_pair(EVEXInst, VEXInst)); // {0,0} |
| 243 | break; |
| 244 | case 1: |
| 245 | EVEX2VEX256.push_back(std::make_pair(EVEXInst, VEXInst)); // {0,1} |
| 246 | break; |
| 247 | default: |
| 248 | llvm_unreachable("Instruction's size not fit for the mapping!"); |
Ayman Musa | b59d804 | 2017-03-07 08:11:19 +0000 | [diff] [blame] | 249 | } |
| 250 | } |
| 251 | |
| 252 | // Print both tables |
| 253 | printTable(EVEX2VEX128, OS); |
| 254 | printTable(EVEX2VEX256, OS); |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | namespace llvm { |
| 259 | void EmitX86EVEX2VEXTables(RecordKeeper &RK, raw_ostream &OS) { |
| 260 | X86EVEX2VEXTablesEmitter(RK).run(OS); |
| 261 | } |
| 262 | } |