Path: blob/main/contrib/llvm-project/llvm/utils/TableGen/X86InstrMappingEmitter.cpp
35258 views
//========- utils/TableGen/X86InstrMappingEmitter.cpp - X86 backend-*- C++ -*-//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// This tablegen backend is responsible for emitting the X86 backend9/// instruction mapping.10///11//===----------------------------------------------------------------------===//1213#include "Common/CodeGenInstruction.h"14#include "Common/CodeGenTarget.h"15#include "X86RecognizableInstr.h"16#include "llvm/TableGen/Error.h"17#include "llvm/TableGen/Record.h"18#include "llvm/TableGen/TableGenBackend.h"19#include <map>20#include <set>2122using namespace llvm;23using namespace X86Disassembler;2425namespace {2627class X86InstrMappingEmitter {28RecordKeeper &Records;29CodeGenTarget Target;3031// Hold all pontentially compressible EVEX instructions32std::vector<const CodeGenInstruction *> PreCompressionInsts;33// Hold all compressed instructions. Divided into groups with same opcodes34// to make the search more efficient35std::map<uint64_t, std::vector<const CodeGenInstruction *>> CompressedInsts;3637typedef std::pair<const CodeGenInstruction *, const CodeGenInstruction *>38Entry;39typedef std::map<StringRef, std::vector<const CodeGenInstruction *>>40PredicateInstMap;4142// Hold all compressed instructions that need to check predicate43PredicateInstMap PredicateInsts;4445public:46X86InstrMappingEmitter(RecordKeeper &R) : Records(R), Target(R) {}4748// run - Output X86 EVEX compression tables.49void run(raw_ostream &OS);5051private:52void emitCompressEVEXTable(ArrayRef<const CodeGenInstruction *> Insts,53raw_ostream &OS);54void emitNFTransformTable(ArrayRef<const CodeGenInstruction *> Insts,55raw_ostream &OS);56void emitND2NonNDTable(ArrayRef<const CodeGenInstruction *> Insts,57raw_ostream &OS);58void emitSSE2AVXTable(ArrayRef<const CodeGenInstruction *> Insts,59raw_ostream &OS);6061// Prints the definition of class X86TableEntry.62void printClassDef(raw_ostream &OS);63// Prints the given table as a C++ array of type X86TableEntry under the guard64// \p Macro.65void printTable(const std::vector<Entry> &Table, StringRef Name,66StringRef Macro, raw_ostream &OS);67};6869void X86InstrMappingEmitter::printClassDef(raw_ostream &OS) {70OS << "struct X86TableEntry {\n"71" uint16_t OldOpc;\n"72" uint16_t NewOpc;\n"73" bool operator<(const X86TableEntry &RHS) const {\n"74" return OldOpc < RHS.OldOpc;\n"75" }"76" friend bool operator<(const X86TableEntry &TE, unsigned Opc) {\n"77" return TE.OldOpc < Opc;\n"78" }\n"79"};";8081OS << "\n\n";82}8384static void printMacroBegin(StringRef Macro, raw_ostream &OS) {85OS << "\n#ifdef " << Macro << "\n";86}8788static void printMacroEnd(StringRef Macro, raw_ostream &OS) {89OS << "#endif // " << Macro << "\n\n";90}9192void X86InstrMappingEmitter::printTable(const std::vector<Entry> &Table,93StringRef Name, StringRef Macro,94raw_ostream &OS) {95printMacroBegin(Macro, OS);9697OS << "static const X86TableEntry " << Name << "[] = {\n";9899// Print all entries added to the table100for (const auto &Pair : Table)101OS << " { X86::" << Pair.first->TheDef->getName()102<< ", X86::" << Pair.second->TheDef->getName() << " },\n";103104OS << "};\n\n";105106printMacroEnd(Macro, OS);107}108109static uint8_t byteFromBitsInit(const BitsInit *B) {110unsigned N = B->getNumBits();111assert(N <= 8 && "Field is too large for uint8_t!");112113uint8_t Value = 0;114for (unsigned I = 0; I != N; ++I) {115BitInit *Bit = cast<BitInit>(B->getBit(I));116Value |= Bit->getValue() << I;117}118return Value;119}120121class IsMatch {122const CodeGenInstruction *OldInst;123124public:125IsMatch(const CodeGenInstruction *OldInst) : OldInst(OldInst) {}126127bool operator()(const CodeGenInstruction *NewInst) {128RecognizableInstrBase NewRI(*NewInst);129RecognizableInstrBase OldRI(*OldInst);130131// Return false if any of the following fields of does not match.132if (std::tuple(OldRI.IsCodeGenOnly, OldRI.OpMap, NewRI.OpPrefix,133OldRI.HasVEX_4V, OldRI.HasVEX_L, OldRI.HasREX_W,134OldRI.Form) !=135std::tuple(NewRI.IsCodeGenOnly, NewRI.OpMap, OldRI.OpPrefix,136NewRI.HasVEX_4V, NewRI.HasVEX_L, NewRI.HasREX_W, NewRI.Form))137return false;138139for (unsigned I = 0, E = OldInst->Operands.size(); I < E; ++I) {140Record *OldOpRec = OldInst->Operands[I].Rec;141Record *NewOpRec = NewInst->Operands[I].Rec;142143if (OldOpRec == NewOpRec)144continue;145146if (isRegisterOperand(OldOpRec) && isRegisterOperand(NewOpRec)) {147if (getRegOperandSize(OldOpRec) != getRegOperandSize(NewOpRec))148return false;149} else if (isMemoryOperand(OldOpRec) && isMemoryOperand(NewOpRec)) {150if (getMemOperandSize(OldOpRec) != getMemOperandSize(NewOpRec))151return false;152} else if (isImmediateOperand(OldOpRec) && isImmediateOperand(NewOpRec)) {153if (OldOpRec->getValueAsDef("Type") != NewOpRec->getValueAsDef("Type"))154return false;155}156}157158return true;159}160};161162static bool isInteresting(const Record *Rec) {163// _REV instruction should not appear before encoding optimization164return Rec->isSubClassOf("X86Inst") &&165!Rec->getValueAsBit("isAsmParserOnly") &&166!Rec->getName().ends_with("_REV");167}168169void X86InstrMappingEmitter::emitCompressEVEXTable(170ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {171172const std::map<StringRef, StringRef> ManualMap = {173#define ENTRY(OLD, NEW) {#OLD, #NEW},174#include "X86ManualInstrMapping.def"175};176const std::set<StringRef> NoCompressSet = {177#define NOCOMP(INSN) #INSN,178#include "X86ManualInstrMapping.def"179};180181for (const CodeGenInstruction *Inst : Insts) {182const Record *Rec = Inst->TheDef;183StringRef Name = Rec->getName();184if (!isInteresting(Rec))185continue;186187// Promoted legacy instruction is in EVEX space, and has REX2-encoding188// alternative. It's added due to HW design and never emitted by compiler.189if (byteFromBitsInit(Rec->getValueAsBitsInit("OpMapBits")) ==190X86Local::T_MAP4 &&191byteFromBitsInit(Rec->getValueAsBitsInit("explicitOpPrefixBits")) ==192X86Local::ExplicitEVEX)193continue;194195if (NoCompressSet.find(Name) != NoCompressSet.end())196continue;197198RecognizableInstrBase RI(*Inst);199200bool IsND = RI.OpMap == X86Local::T_MAP4 && RI.HasEVEX_B && RI.HasVEX_4V;201// Add VEX encoded instructions to one of CompressedInsts vectors according202// to it's opcode.203if (RI.Encoding == X86Local::VEX)204CompressedInsts[RI.Opcode].push_back(Inst);205// Add relevant EVEX encoded instructions to PreCompressionInsts206else if (RI.Encoding == X86Local::EVEX && !RI.HasEVEX_K && !RI.HasEVEX_L2 &&207(!RI.HasEVEX_B || IsND))208PreCompressionInsts.push_back(Inst);209}210211std::vector<Entry> Table;212for (const CodeGenInstruction *Inst : PreCompressionInsts) {213const Record *Rec = Inst->TheDef;214uint8_t Opcode = byteFromBitsInit(Rec->getValueAsBitsInit("Opcode"));215StringRef Name = Rec->getName();216const CodeGenInstruction *NewInst = nullptr;217if (ManualMap.find(Name) != ManualMap.end()) {218Record *NewRec = Records.getDef(ManualMap.at(Rec->getName()));219assert(NewRec && "Instruction not found!");220NewInst = &Target.getInstruction(NewRec);221} else if (Name.ends_with("_EVEX")) {222if (auto *NewRec = Records.getDef(Name.drop_back(5)))223NewInst = &Target.getInstruction(NewRec);224} else if (Name.ends_with("_ND"))225// Leave it to ND2NONND table.226continue;227else {228// For each pre-compression instruction look for a match in the229// appropriate vector (instructions with the same opcode) using function230// object IsMatch.231auto Match = llvm::find_if(CompressedInsts[Opcode], IsMatch(Inst));232if (Match != CompressedInsts[Opcode].end())233NewInst = *Match;234}235236if (!NewInst)237continue;238239Table.push_back(std::pair(Inst, NewInst));240auto Predicates = NewInst->TheDef->getValueAsListOfDefs("Predicates");241auto It = llvm::find_if(Predicates, [](const Record *R) {242StringRef Name = R->getName();243return Name == "HasAVXNECONVERT" || Name == "HasAVXVNNI" ||244Name == "HasAVXIFMA";245});246if (It != Predicates.end())247PredicateInsts[(*It)->getValueAsString("CondString")].push_back(NewInst);248}249250StringRef Macro = "GET_X86_COMPRESS_EVEX_TABLE";251printTable(Table, "X86CompressEVEXTable", Macro, OS);252253// Prints function which checks target feature for compressed instructions.254printMacroBegin(Macro, OS);255OS << "static bool checkPredicate(unsigned Opc, const X86Subtarget "256"*Subtarget) {\n"257<< " switch (Opc) {\n"258<< " default: return true;\n";259for (const auto &[Key, Val] : PredicateInsts) {260for (const auto &Inst : Val)261OS << " case X86::" << Inst->TheDef->getName() << ":\n";262OS << " return " << Key << ";\n";263}264OS << " }\n";265OS << "}\n\n";266printMacroEnd(Macro, OS);267}268269void X86InstrMappingEmitter::emitNFTransformTable(270ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {271std::vector<Entry> Table;272for (const CodeGenInstruction *Inst : Insts) {273const Record *Rec = Inst->TheDef;274if (!isInteresting(Rec))275continue;276std::string Name = Rec->getName().str();277auto Pos = Name.find("_NF");278if (Pos == std::string::npos)279continue;280281if (auto *NewRec = Records.getDef(Name.erase(Pos, 3))) {282#ifndef NDEBUG283auto ClobberEFLAGS = [](const Record *R) {284return llvm::any_of(285R->getValueAsListOfDefs("Defs"),286[](const Record *Def) { return Def->getName() == "EFLAGS"; });287};288if (ClobberEFLAGS(Rec))289report_fatal_error("EFLAGS should not be clobbered by " +290Rec->getName());291if (!ClobberEFLAGS(NewRec))292report_fatal_error("EFLAGS should be clobbered by " +293NewRec->getName());294#endif295Table.push_back(std::pair(&Target.getInstruction(NewRec), Inst));296}297}298printTable(Table, "X86NFTransformTable", "GET_X86_NF_TRANSFORM_TABLE", OS);299}300301void X86InstrMappingEmitter::emitND2NonNDTable(302ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {303304const std::map<StringRef, StringRef> ManualMap = {305#define ENTRY_ND(OLD, NEW) {#OLD, #NEW},306#include "X86ManualInstrMapping.def"307};308const std::set<StringRef> NoCompressSet = {309#define NOCOMP_ND(INSN) #INSN,310#include "X86ManualInstrMapping.def"311};312313std::vector<Entry> Table;314for (const CodeGenInstruction *Inst : Insts) {315const Record *Rec = Inst->TheDef;316StringRef Name = Rec->getName();317if (!isInteresting(Rec) || NoCompressSet.find(Name) != NoCompressSet.end())318continue;319if (ManualMap.find(Name) != ManualMap.end()) {320auto *NewRec = Records.getDef(ManualMap.at(Rec->getName()));321assert(NewRec && "Instruction not found!");322auto &NewInst = Target.getInstruction(NewRec);323Table.push_back(std::pair(Inst, &NewInst));324continue;325}326327if (!Name.ends_with("_ND"))328continue;329auto *NewRec = Records.getDef(Name.drop_back(3));330if (!NewRec)331continue;332auto &NewInst = Target.getInstruction(NewRec);333if (isRegisterOperand(NewInst.Operands[0].Rec))334Table.push_back(std::pair(Inst, &NewInst));335}336printTable(Table, "X86ND2NonNDTable", "GET_X86_ND2NONND_TABLE", OS);337}338339void X86InstrMappingEmitter::emitSSE2AVXTable(340ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {341342const std::map<StringRef, StringRef> ManualMap = {343#define ENTRY_SSE2AVX(OLD, NEW) {#OLD, #NEW},344#include "X86ManualInstrMapping.def"345};346347std::vector<Entry> Table;348for (const CodeGenInstruction *Inst : Insts) {349const Record *Rec = Inst->TheDef;350StringRef Name = Rec->getName();351if (!isInteresting(Rec))352continue;353if (ManualMap.find(Name) != ManualMap.end()) {354auto *NewRec = Records.getDef(ManualMap.at(Rec->getName()));355assert(NewRec && "Instruction not found!");356auto &NewInst = Target.getInstruction(NewRec);357Table.push_back(std::pair(Inst, &NewInst));358continue;359}360361std::string NewName = ("V" + Name).str();362auto *AVXRec = Records.getDef(NewName);363if (!AVXRec)364continue;365auto &AVXInst = Target.getInstruction(AVXRec);366Table.push_back(std::pair(Inst, &AVXInst));367}368printTable(Table, "X86SSE2AVXTable", "GET_X86_SSE2AVX_TABLE", OS);369}370371void X86InstrMappingEmitter::run(raw_ostream &OS) {372emitSourceFileHeader("X86 instruction mapping", OS);373374ArrayRef<const CodeGenInstruction *> Insts =375Target.getInstructionsByEnumValue();376printClassDef(OS);377emitCompressEVEXTable(Insts, OS);378emitNFTransformTable(Insts, OS);379emitND2NonNDTable(Insts, OS);380emitSSE2AVXTable(Insts, OS);381}382} // namespace383384static TableGen::Emitter::OptClass<X86InstrMappingEmitter>385X("gen-x86-instr-mapping", "Generate X86 instruction mapping");386387388