diff options
author | Ayman Musa <ayman.musa@intel.com> | 2017-03-07 08:11:19 +0000 |
---|---|---|
committer | Ayman Musa <ayman.musa@intel.com> | 2017-03-07 08:11:19 +0000 |
commit | 850fc977c807984bcc583e1dede4b9113f1657c3 (patch) | |
tree | 505434c15a722dc53875500f702e18af6c76eeae /llvm/utils/TableGen | |
parent | ac5a2c43af283fba87887de67f05c547c9fd1778 (diff) | |
download | bcm5719-llvm-850fc977c807984bcc583e1dede4b9113f1657c3.tar.gz bcm5719-llvm-850fc977c807984bcc583e1dede4b9113f1657c3.zip |
[X86][AVX512] Adding new LLVM TableGen backend which generates the EVEX2VEX compressing tables.
X86EvexToVex machine instruction pass compresses EVEX encoded instructions by replacing them with their identical VEX encoded instructions when possible.
It uses manually supported 2 large tables that map the EVEX instructions to their VEX ideticals.
This TableGen backend replaces the tables by automatically generating them.
Differential Revision: https://reviews.llvm.org/D30451
llvm-svn: 297127
Diffstat (limited to 'llvm/utils/TableGen')
-rw-r--r-- | llvm/utils/TableGen/CMakeLists.txt | 1 | ||||
-rw-r--r-- | llvm/utils/TableGen/TableGen.cpp | 6 | ||||
-rw-r--r-- | llvm/utils/TableGen/TableGenBackends.h | 1 | ||||
-rw-r--r-- | llvm/utils/TableGen/X86EVEX2VEXTablesEmitter.cpp | 345 |
4 files changed, 353 insertions, 0 deletions
diff --git a/llvm/utils/TableGen/CMakeLists.txt b/llvm/utils/TableGen/CMakeLists.txt index 62fb5dd8fff..b2913afae12 100644 --- a/llvm/utils/TableGen/CMakeLists.txt +++ b/llvm/utils/TableGen/CMakeLists.txt @@ -35,6 +35,7 @@ add_tablegen(llvm-tblgen LLVM TableGen.cpp Types.cpp X86DisassemblerTables.cpp + X86EVEX2VEXTablesEmitter.cpp X86ModRMFilters.cpp X86RecognizableInstr.cpp CTagsEmitter.cpp diff --git a/llvm/utils/TableGen/TableGen.cpp b/llvm/utils/TableGen/TableGen.cpp index 6937f20b441..aa1aefd5e4a 100644 --- a/llvm/utils/TableGen/TableGen.cpp +++ b/llvm/utils/TableGen/TableGen.cpp @@ -46,6 +46,7 @@ enum ActionType { GenAttributes, GenSearchableTables, GenGlobalISel, + GenX86EVEX2VEXTables, GenRegisterBank, }; @@ -96,6 +97,8 @@ namespace { "Generate generic binary-searchable table"), clEnumValN(GenGlobalISel, "gen-global-isel", "Generate GlobalISel selector"), + clEnumValN(GenX86EVEX2VEXTables, "gen-x86-EVEX2VEX-tables", + "Generate X86 EVEX to VEX compress tables"), clEnumValN(GenRegisterBank, "gen-register-bank", "Generate registers bank descriptions"))); @@ -189,6 +192,9 @@ bool LLVMTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenRegisterBank: EmitRegisterBank(Records, OS); break; + case GenX86EVEX2VEXTables: + EmitX86EVEX2VEXTables(Records, OS); + break; } return false; diff --git a/llvm/utils/TableGen/TableGenBackends.h b/llvm/utils/TableGen/TableGenBackends.h index 066d26a08c7..2512997e27f 100644 --- a/llvm/utils/TableGen/TableGenBackends.h +++ b/llvm/utils/TableGen/TableGenBackends.h @@ -81,6 +81,7 @@ void EmitCTags(RecordKeeper &RK, raw_ostream &OS); void EmitAttributes(RecordKeeper &RK, raw_ostream &OS); void EmitSearchableTables(RecordKeeper &RK, raw_ostream &OS); void EmitGlobalISel(RecordKeeper &RK, raw_ostream &OS); +void EmitX86EVEX2VEXTables(RecordKeeper &RK, raw_ostream &OS); void EmitRegisterBank(RecordKeeper &RK, raw_ostream &OS); } // End llvm namespace diff --git a/llvm/utils/TableGen/X86EVEX2VEXTablesEmitter.cpp b/llvm/utils/TableGen/X86EVEX2VEXTablesEmitter.cpp new file mode 100644 index 00000000000..2a2a311a81a --- /dev/null +++ b/llvm/utils/TableGen/X86EVEX2VEXTablesEmitter.cpp @@ -0,0 +1,345 @@ +//===- utils/TableGen/X86EVEX2VEXTablesEmitter.cpp - X86 backend-*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// This tablegen backend is responsible for emitting the X86 backend EVEX2VEX +/// compression tables. +/// +//===----------------------------------------------------------------------===// + +#include "CodeGenDAGPatterns.h" +#include "CodeGenTarget.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/TableGenBackend.h" + +using namespace llvm; + +namespace { + +class X86EVEX2VEXTablesEmitter { + RecordKeeper &Records; + CodeGenTarget Target; + + // Hold all non-masked & non-broadcasted EVEX encoded instructions + std::vector<const CodeGenInstruction *> EVEXInsts; + // Hold all VEX encoded instructions. Divided into groups with same opcodes + // to make the search more efficient + std::map<uint64_t, std::vector<const CodeGenInstruction *>> VEXInsts; + + typedef std::pair<const CodeGenInstruction *, const CodeGenInstruction *> Entry; + + // Represent both compress tables + std::vector<Entry> EVEX2VEX128; + std::vector<Entry> EVEX2VEX256; + + // Represents a manually added entry to the tables + class ManualEntry { + public: + std::string EVEXInstStr; + std::string VEXInstStr; + bool Is128Bit; + + ManualEntry(std::string EVEXInstStr, std::string VEXInstStr, bool Is128Bit) + : EVEXInstStr(EVEXInstStr), VEXInstStr(VEXInstStr), Is128Bit(Is128Bit) { + } + }; + +public: + X86EVEX2VEXTablesEmitter(RecordKeeper &R) : Records(R), Target(R) {} + + // run - Output X86 EVEX2VEX tables. + void run(raw_ostream &OS); + +private: + // Prints the given table as a C++ array of type + // X86EvexToVexCompressTableEntry + void printTable(const std::vector<Entry> &Table, raw_ostream &OS); + + // List of EVEX instructions that match VEX instructions by the encoding + // but do not perform the same operation. + const std::vector<std::string> ExceptionList = { + "VCVTQQ2PD", + "VCVTQQ2PS", + "VPMAXSQ", + "VPMAXUQ", + "VPMINSQ", + "VPMINUQ", + "VPMULLQ", + "VPSRAQ", + "VDBPSADBW", + "VRNDSCALE", + "VSCALEFPS" + }; + + bool inExceptionList(const CodeGenInstruction *Inst) { + // Instruction's name starts with one of the entries in the exception list + for (const std::string& InstStr : ExceptionList) { + if (Inst->TheDef->getName().startswith(InstStr)) + return true; + } + return false; + } + + // Some VEX instructions were duplicated to multiple EVEX versions due the + // introduction of mask variants, and thus some of the EVEX versions have + // different encoding than the VEX instruction. In order to maximize the + // compression we add these entries manually. + const std::vector<ManualEntry> ManuallyAddedEntries = { + // EVEX-Inst VEX-Inst Is128-bit + {"VMOVDQU8Z128mr", "VMOVDQUmr", true}, + {"VMOVDQU8Z128rm", "VMOVDQUrm", true}, + {"VMOVDQU8Z128rr", "VMOVDQUrr", true}, + {"VMOVDQU8Z128rr_REV", "VMOVDQUrr_REV", true}, + {"VMOVDQU16Z128mr", "VMOVDQUmr", true}, + {"VMOVDQU16Z128rm", "VMOVDQUrm", true}, + {"VMOVDQU16Z128rr", "VMOVDQUrr", true}, + {"VMOVDQU16Z128rr_REV", "VMOVDQUrr_REV", true}, + {"VMOVDQU8Z256mr", "VMOVDQUYmr", false}, + {"VMOVDQU8Z256rm", "VMOVDQUYrm", false}, + {"VMOVDQU8Z256rr", "VMOVDQUYrr", false}, + {"VMOVDQU8Z256rr_REV", "VMOVDQUYrr_REV", false}, + {"VMOVDQU16Z256mr", "VMOVDQUYmr", false}, + {"VMOVDQU16Z256rm", "VMOVDQUYrm", false}, + {"VMOVDQU16Z256rr", "VMOVDQUYrr", false}, + {"VMOVDQU16Z256rr_REV", "VMOVDQUYrr_REV", false}, + + {"VPERMILPDZ128mi", "VPERMILPDmi", true}, + {"VPERMILPDZ128ri", "VPERMILPDri", true}, + {"VPERMILPDZ128rm", "VPERMILPDrm", true}, + {"VPERMILPDZ128rr", "VPERMILPDrr", true}, + {"VPERMILPDZ256mi", "VPERMILPDYmi", false}, + {"VPERMILPDZ256ri", "VPERMILPDYri", false}, + {"VPERMILPDZ256rm", "VPERMILPDYrm", false}, + {"VPERMILPDZ256rr", "VPERMILPDYrr", false}, + + {"VPBROADCASTQZ128m", "VPBROADCASTQrm", true}, + {"VPBROADCASTQZ128r", "VPBROADCASTQrr", true}, + {"VPBROADCASTQZ256m", "VPBROADCASTQYrm", false}, + {"VPBROADCASTQZ256r", "VPBROADCASTQYrr", false}, + + {"VBROADCASTSDZ256m", "VBROADCASTSDYrm", false}, + {"VBROADCASTSDZ256r", "VBROADCASTSDYrr", false}, + + {"VEXTRACTF64x2Z256mr", "VEXTRACTF128mr", false}, + {"VEXTRACTF64x2Z256rr", "VEXTRACTF128rr", false}, + {"VEXTRACTI64x2Z256mr", "VEXTRACTI128mr", false}, + {"VEXTRACTI64x2Z256rr", "VEXTRACTI128rr", false}, + + {"VINSERTF64x2Z256rm", "VINSERTF128rm", false}, + {"VINSERTF64x2Z256rr", "VINSERTF128rr", false}, + {"VINSERTI64x2Z256rm", "VINSERTI128rm", false}, + {"VINSERTI64x2Z256rr", "VINSERTI128rr", false} + }; +}; + +void X86EVEX2VEXTablesEmitter::printTable(const std::vector<Entry> &Table, + raw_ostream &OS) { + std::string Size = (Table == EVEX2VEX128) ? "128" : "256"; + + OS << "// X86 EVEX encoded instructions that have a VEX " << Size + << " encoding\n" + << "// (table format: <EVEX opcode, VEX-" << Size << " opcode>).\n" + << "static const X86EvexToVexCompressTableEntry X86EvexToVex" << Size + << "CompressTable[] = {\n" + << " // EVEX scalar with corresponding VEX.\n"; + + // Print all entries added to the table + for (auto Pair : Table) { + OS << "{ X86::" << Pair.first->TheDef->getName() + << ", X86::" << Pair.second->TheDef->getName() << " },\n"; + } + + // Print the manually added entries + for (const ManualEntry &Entry : ManuallyAddedEntries) { + if ((Table == EVEX2VEX128 && Entry.Is128Bit) || + (Table == EVEX2VEX256 && !Entry.Is128Bit)) { + OS << "{ X86::" << Entry.EVEXInstStr << ", X86::" << Entry.VEXInstStr + << " },\n"; + } + } + + OS << "};\n\n"; +} + +// Return true if the 2 BitsInits are equal +static inline bool equalBitsInits(const BitsInit *B1, const BitsInit *B2) { + if (B1->getNumBits() != B2->getNumBits()) + PrintFatalError("Comparing two BitsInits with different sizes!"); + + for (unsigned i = 0, e = B1->getNumBits(); i != e; ++i) { + if (BitInit *Bit1 = dyn_cast<BitInit>(B1->getBit(i))) { + if (BitInit *Bit2 = dyn_cast<BitInit>(B2->getBit(i))) { + if (Bit1->getValue() != Bit2->getValue()) + return false; + } else + PrintFatalError("Invalid BitsInit bit"); + } else + PrintFatalError("Invalid BitsInit bit"); + } + return true; +} + +// Calculates the integer value residing BitsInit object +static inline uint64_t getValueFromBitsInit(const BitsInit *B) { + uint64_t Value = 0; + for (unsigned i = 0, e = B->getNumBits(); i != e; ++i) { + if (BitInit *Bit = dyn_cast<BitInit>(B->getBit(i))) + Value |= uint64_t(Bit->getValue()) << i; + else + PrintFatalError("Invalid VectSize bit"); + } + return Value; +} + +// Function object - Operator() returns true if the given VEX instruction +// matches the EVEX instruction of this object. +class IsMatch { + const CodeGenInstruction *Inst; + +public: + IsMatch(const CodeGenInstruction *Inst) : Inst(Inst) {} + + bool operator()(const CodeGenInstruction *Inst2) { + Record *Rec1 = Inst->TheDef; + Record *Rec2 = Inst2->TheDef; + uint64_t Rec1WVEX = + getValueFromBitsInit(Rec1->getValueAsBitsInit("VEX_WPrefix")); + uint64_t Rec2WVEX = + getValueFromBitsInit(Rec2->getValueAsBitsInit("VEX_WPrefix")); + + if (Rec2->getValueAsDef("OpEnc")->getName().str() != "EncVEX" || + // VEX/EVEX fields + Rec2->getValueAsDef("OpPrefix") != Rec1->getValueAsDef("OpPrefix") || + Rec2->getValueAsDef("OpMap") != Rec1->getValueAsDef("OpMap") || + Rec2->getValueAsBit("hasVEX_4V") != Rec1->getValueAsBit("hasVEX_4V") || + !equalBitsInits(Rec2->getValueAsBitsInit("EVEX_LL"), + Rec1->getValueAsBitsInit("EVEX_LL")) || + (Rec1WVEX != 2 && Rec2WVEX != 2 && Rec1WVEX != Rec2WVEX) || + // Instruction's format + Rec2->getValueAsDef("Form") != Rec1->getValueAsDef("Form") || + Rec2->getValueAsBit("isAsmParserOnly") != + Rec1->getValueAsBit("isAsmParserOnly")) + return false; + + // This is needed for instructions with intrinsic version (_Int). + // Where the only difference is the size of the operands. + // For example: VUCOMISDZrm and Int_VUCOMISDrm + // Also for instructions that their EVEX version was upgraded to work with + // k-registers. For example VPCMPEQBrm (xmm output register) and + // VPCMPEQBZ128rm (k register output register). + for (unsigned i = 0; i < Inst->Operands.size(); i++) { + Record *OpRec1 = Inst->Operands[i].Rec; + Record *OpRec2 = Inst2->Operands[i].Rec; + + if (OpRec1 == OpRec2) + continue; + + if (isRegisterOperand(OpRec1) && isRegisterOperand(OpRec2)) { + if (getRegOperandSize(OpRec1) != getRegOperandSize(OpRec2)) + return false; + } else if (isMemoryOperand(OpRec1) && isMemoryOperand(OpRec2)) { + return false; + } else if (isImmediateOperand(OpRec1) && isImmediateOperand(OpRec2)) { + if (OpRec1->getValueAsDef("Type") != OpRec2->getValueAsDef("Type")) + return false; + } else + return false; + } + + return true; + } + +private: + static inline bool isRegisterOperand(const Record *Rec) { + return Rec->isSubClassOf("RegisterClass") || + Rec->isSubClassOf("RegisterOperand"); + } + + static inline bool isMemoryOperand(const Record *Rec) { + return Rec->isSubClassOf("Operand") && + Rec->getValueAsString("OperandType") == "OPERAND_MEMORY"; + } + + static inline bool isImmediateOperand(const Record *Rec) { + return Rec->isSubClassOf("Operand") && + Rec->getValueAsString("OperandType") == "OPERAND_IMMEDIATE"; + } + + static inline unsigned int getRegOperandSize(const Record *RegRec) { + if (RegRec->isSubClassOf("RegisterClass")) + return RegRec->getValueAsInt("Alignment"); + if (RegRec->isSubClassOf("RegisterOperand")) + return RegRec->getValueAsDef("RegClass")->getValueAsInt("Alignment"); + + llvm_unreachable("Register operand's size not known!"); + } +}; + +void X86EVEX2VEXTablesEmitter::run(raw_ostream &OS) { + emitSourceFileHeader("X86 EVEX2VEX tables", OS); + + ArrayRef<const CodeGenInstruction *> NumberedInstructions = + Target.getInstructionsByEnumValue(); + + for (const CodeGenInstruction *Inst : NumberedInstructions) { + // Filter non-X86 instructions. + if (!Inst->TheDef->isSubClassOf("X86Inst")) + continue; + + // Add VEX encoded instructions to one of VEXInsts vectors according to + // it's opcode. + if (Inst->TheDef->getValueAsDef("OpEnc")->getName() == "EncVEX") { + uint64_t Opcode = getValueFromBitsInit(Inst->TheDef-> + getValueAsBitsInit("Opcode")); + VEXInsts[Opcode].push_back(Inst); + } + // Add relevant EVEX encoded instructions to EVEXInsts + else if (Inst->TheDef->getValueAsDef("OpEnc")->getName() == "EncEVEX" && + !Inst->TheDef->getValueAsBit("hasEVEX_K") && + !Inst->TheDef->getValueAsBit("hasEVEX_B") && + getValueFromBitsInit(Inst->TheDef-> + getValueAsBitsInit("EVEX_LL")) != 2 && + !inExceptionList(Inst)) + EVEXInsts.push_back(Inst); + } + + for (const CodeGenInstruction *EVEXInst : EVEXInsts) { + uint64_t Opcode = getValueFromBitsInit(EVEXInst->TheDef-> + getValueAsBitsInit("Opcode")); + // For each EVEX instruction look for a VEX match in the appropriate vector + // (instructions with the same opcode) using function object IsMatch. + auto Match = llvm::find_if(VEXInsts[Opcode], IsMatch(EVEXInst)); + if (Match != VEXInsts[Opcode].end()) { + const CodeGenInstruction *VEXInst = *Match; + + // In case a match is found add new entry to the appropriate table + switch (getValueFromBitsInit( + EVEXInst->TheDef->getValueAsBitsInit("EVEX_LL"))) { + case 0: + EVEX2VEX128.push_back(std::make_pair(EVEXInst, VEXInst)); // {0,0} + break; + case 1: + EVEX2VEX256.push_back(std::make_pair(EVEXInst, VEXInst)); // {0,1} + break; + default: + llvm_unreachable("Instruction's size not fit for the mapping!"); + } + } + } + + // Print both tables + printTable(EVEX2VEX128, OS); + printTable(EVEX2VEX256, OS); +} +} + +namespace llvm { +void EmitX86EVEX2VEXTables(RecordKeeper &RK, raw_ostream &OS) { + X86EVEX2VEXTablesEmitter(RK).run(OS); +} +} |