brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.9 KiB · 9abb194 Raw
377 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8///9/// This tablegen backend is responsible for emitting the X86 backend10/// 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 23using namespace llvm;24using namespace X86Disassembler;25 26namespace {27 28class X86InstrMappingEmitter {29  const RecordKeeper &Records;30  const CodeGenTarget Target;31 32  // Hold all pontentially compressible EVEX instructions33  std::vector<const CodeGenInstruction *> PreCompressionInsts;34  // Hold all compressed instructions. Divided into groups with same opcodes35  // to make the search more efficient36  std::map<uint64_t, std::vector<const CodeGenInstruction *>> CompressedInsts;37 38  using Entry =39      std::pair<const CodeGenInstruction *, const CodeGenInstruction *>;40  using PredicateInstMap =41      std::map<StringRef, std::vector<const CodeGenInstruction *>>;42 43  // Hold all compressed instructions that need to check predicate44  PredicateInstMap PredicateInsts;45 46public:47  X86InstrMappingEmitter(const RecordKeeper &R) : Records(R), Target(R) {}48 49  // run - Output X86 EVEX compression tables.50  void run(raw_ostream &OS);51 52private: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 guard65  // \p Macro.66  void printTable(ArrayRef<Entry> Table, StringRef Name, StringRef Macro,67                  raw_ostream &OS);68};69} // namespace70 71void X86InstrMappingEmitter::printClassDef(raw_ostream &OS) {72  OS << "struct X86TableEntry {\n"73        "  uint16_t OldOpc;\n"74        "  uint16_t NewOpc;\n"75        "  bool operator<(const X86TableEntry &RHS) const {\n"76        "    return OldOpc < RHS.OldOpc;\n"77        "  }"78        "  friend bool operator<(const X86TableEntry &TE, unsigned Opc) {\n"79        "    return TE.OldOpc < Opc;\n"80        "  }\n"81        "};";82 83  OS << "\n\n";84}85 86static void printMacroBegin(StringRef Macro, raw_ostream &OS) {87  OS << "\n#ifdef " << Macro << "\n";88}89 90static void printMacroEnd(StringRef Macro, raw_ostream &OS) {91  OS << "#endif // " << Macro << "\n\n";92}93 94void X86InstrMappingEmitter::printTable(ArrayRef<Entry> Table, StringRef Name,95                                        StringRef Macro, raw_ostream &OS) {96  printMacroBegin(Macro, OS);97 98  OS << "static const X86TableEntry " << Name << "[] = {\n";99 100  // Print all entries added to the table101  for (const auto &Pair : Table)102    OS << "  { X86::" << Pair.first->getName()103       << ", X86::" << Pair.second->getName() << " },\n";104 105  OS << "};\n\n";106 107  printMacroEnd(Macro, OS);108}109 110namespace {111class IsMatch {112  const CodeGenInstruction *OldInst;113 114public:115  IsMatch(const CodeGenInstruction *OldInst) : OldInst(OldInst) {}116 117  bool operator()(const CodeGenInstruction *NewInst) {118    RecognizableInstrBase NewRI(*NewInst);119    RecognizableInstrBase OldRI(*OldInst);120 121    // Return false if any of the following fields of does not match.122    if (std::tuple(OldRI.IsCodeGenOnly, OldRI.OpMap, NewRI.OpPrefix,123                   OldRI.HasVEX_4V, OldRI.HasVEX_L, OldRI.HasREX_W,124                   OldRI.Form) !=125        std::tuple(NewRI.IsCodeGenOnly, NewRI.OpMap, OldRI.OpPrefix,126                   NewRI.HasVEX_4V, NewRI.HasVEX_L, NewRI.HasREX_W, NewRI.Form))127      return false;128 129    for (unsigned I = 0, E = OldInst->Operands.size(); I < E; ++I) {130      const Record *OldOpRec = OldInst->Operands[I].Rec;131      const Record *NewOpRec = NewInst->Operands[I].Rec;132 133      if (OldOpRec == NewOpRec)134        continue;135 136      if (isRegisterOperand(OldOpRec) && isRegisterOperand(NewOpRec)) {137        if (getRegOperandSize(OldOpRec) != getRegOperandSize(NewOpRec))138          return false;139      } else if (isMemoryOperand(OldOpRec) && isMemoryOperand(NewOpRec)) {140        if (getMemOperandSize(OldOpRec) != getMemOperandSize(NewOpRec))141          return false;142      } else if (isImmediateOperand(OldOpRec) && isImmediateOperand(NewOpRec)) {143        if (OldOpRec->getValueAsDef("Type") != NewOpRec->getValueAsDef("Type"))144          return false;145      }146    }147 148    return true;149  }150};151} // namespace152 153static bool isInteresting(const Record *Rec) {154  // _REV instruction should not appear before encoding optimization155  return Rec->isSubClassOf("X86Inst") &&156         !Rec->getValueAsBit("isAsmParserOnly") &&157         !Rec->getName().ends_with("_REV");158}159 160void X86InstrMappingEmitter::emitCompressEVEXTable(161    ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {162 163  const std::map<StringRef, StringRef> ManualMap = {164#define ENTRY(OLD, NEW) {#OLD, #NEW},165#include "X86ManualInstrMapping.def"166  };167  const std::set<StringRef> NoCompressSet = {168#define NOCOMP(INSN) #INSN,169#include "X86ManualInstrMapping.def"170  };171 172  for (const CodeGenInstruction *Inst : Insts) {173    const Record *Rec = Inst->TheDef;174    StringRef Name = Rec->getName();175    if (!isInteresting(Rec))176      continue;177 178    // Promoted legacy instruction is in EVEX space, and has REX2-encoding179    // alternative. It's added due to HW design and never emitted by compiler.180    if (byteFromBitsInit(Rec->getValueAsBitsInit("OpMapBits")) ==181            X86Local::T_MAP4 &&182        byteFromBitsInit(Rec->getValueAsBitsInit("explicitOpPrefixBits")) ==183            X86Local::ExplicitEVEX)184      continue;185 186    if (NoCompressSet.find(Name) != NoCompressSet.end())187      continue;188 189    RecognizableInstrBase RI(*Inst);190 191    bool IsND = RI.OpMap == X86Local::T_MAP4 && RI.HasEVEX_B && RI.HasVEX_4V;192    // Add VEX encoded instructions to one of CompressedInsts vectors according193    // to it's opcode.194    if (RI.Encoding == X86Local::VEX)195      CompressedInsts[RI.Opcode].push_back(Inst);196    // Add relevant EVEX encoded instructions to PreCompressionInsts197    else if (RI.Encoding == X86Local::EVEX && !RI.HasEVEX_K && !RI.HasEVEX_L2 &&198             (!RI.HasEVEX_B || IsND))199      PreCompressionInsts.push_back(Inst);200  }201 202  std::vector<Entry> Table;203  for (const CodeGenInstruction *Inst : PreCompressionInsts) {204    const Record *Rec = Inst->TheDef;205    uint8_t Opcode = byteFromBitsInit(Rec->getValueAsBitsInit("Opcode"));206    StringRef Name = Rec->getName();207    const CodeGenInstruction *NewInst = nullptr;208    if (ManualMap.find(Name) != ManualMap.end()) {209      const Record *NewRec = Records.getDef(ManualMap.at(Rec->getName()));210      assert(NewRec && "Instruction not found!");211      NewInst = &Target.getInstruction(NewRec);212    } else if (Name.ends_with("_EVEX")) {213      if (const auto *NewRec = Records.getDef(Name.drop_back(5)))214        NewInst = &Target.getInstruction(NewRec);215    } else if (Name.ends_with("_ND"))216      // Leave it to ND2NONND table.217      continue;218    else {219      // For each pre-compression instruction look for a match in the220      // appropriate vector (instructions with the same opcode) using function221      // object IsMatch.222      const auto &Insts = CompressedInsts[Opcode];223      auto Match = llvm::find_if(Insts, IsMatch(Inst));224      if (Match != Insts.end())225        NewInst = *Match;226    }227 228    if (!NewInst)229      continue;230 231    Table.emplace_back(Inst, NewInst);232    auto Predicates = NewInst->TheDef->getValueAsListOfDefs("Predicates");233    auto It = llvm::find_if(Predicates, [](const Record *R) {234      StringRef Name = R->getName();235      return Name == "HasAVXNECONVERT" || Name == "HasAVXVNNI" ||236             Name == "HasAVXIFMA" || Name == "HasAVXVNNIINT8" ||237             Name == "HasAVXVNNIINT16";238    });239    if (It != Predicates.end())240      PredicateInsts[(*It)->getValueAsString("CondString")].push_back(NewInst);241  }242 243  StringRef Macro = "GET_X86_COMPRESS_EVEX_TABLE";244  printTable(Table, "X86CompressEVEXTable", Macro, OS);245 246  // Prints function which checks target feature for compressed instructions.247  printMacroBegin(Macro, OS);248  OS << "static bool checkPredicate(unsigned Opc, const X86Subtarget "249        "*Subtarget) {\n"250     << "  switch (Opc) {\n"251     << "  default: return true;\n";252  for (const auto &[Key, Val] : PredicateInsts) {253    for (const auto &Inst : Val)254      OS << "  case X86::" << Inst->getName() << ":\n";255    OS << "    return " << Key << ";\n";256  }257  OS << "  }\n";258  OS << "}\n\n";259  printMacroEnd(Macro, OS);260}261 262void X86InstrMappingEmitter::emitNFTransformTable(263    ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {264  std::vector<Entry> Table;265  for (const CodeGenInstruction *Inst : Insts) {266    const Record *Rec = Inst->TheDef;267    if (!isInteresting(Rec))268      continue;269    StringRef Name = Rec->getName();270    if (Name.contains("_NF"))271      continue;272 273    if (auto *NewRec = Name.consume_back("_ND")274                           ? Records.getDef(Name.str() + "_NF_ND")275                           : Records.getDef(Name.str() + "_NF")) {276#ifndef NDEBUG277      auto ClobberEFLAGS = [](const Record *R) {278        return llvm::any_of(279            R->getValueAsListOfDefs("Defs"),280            [](const Record *Def) { return Def->getName() == "EFLAGS"; });281      };282      if (ClobberEFLAGS(NewRec))283        report_fatal_error("EFLAGS should not be clobbered by " +284                           NewRec->getName());285      if (!ClobberEFLAGS(Rec))286        report_fatal_error("EFLAGS should be clobbered by " + Rec->getName());287#endif288      Table.emplace_back(Inst, &Target.getInstruction(NewRec));289    }290  }291  printTable(Table, "X86NFTransformTable", "GET_X86_NF_TRANSFORM_TABLE", OS);292}293 294void X86InstrMappingEmitter::emitND2NonNDTable(295    ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {296 297  const std::map<StringRef, StringRef> ManualMap = {298#define ENTRY_ND(OLD, NEW) {#OLD, #NEW},299#include "X86ManualInstrMapping.def"300  };301  const std::set<StringRef> NoCompressSet = {302#define NOCOMP_ND(INSN) #INSN,303#include "X86ManualInstrMapping.def"304  };305 306  std::vector<Entry> Table;307  for (const CodeGenInstruction *Inst : Insts) {308    const Record *Rec = Inst->TheDef;309    StringRef Name = Rec->getName();310    if (!isInteresting(Rec) || NoCompressSet.find(Name) != NoCompressSet.end())311      continue;312    if (ManualMap.find(Name) != ManualMap.end()) {313      const auto *NewRec = Records.getDef(ManualMap.at(Rec->getName()));314      assert(NewRec && "Instruction not found!");315      auto &NewInst = Target.getInstruction(NewRec);316      Table.emplace_back(Inst, &NewInst);317      continue;318    }319 320    if (!Name.ends_with("_ND"))321      continue;322    const auto *NewRec = Records.getDef(Name.drop_back(3));323    if (!NewRec)324      continue;325    const auto &NewInst = Target.getInstruction(NewRec);326    if (isRegisterOperand(NewInst.Operands[0].Rec))327      Table.emplace_back(Inst, &NewInst);328  }329  printTable(Table, "X86ND2NonNDTable", "GET_X86_ND2NONND_TABLE", OS);330}331 332void X86InstrMappingEmitter::emitSSE2AVXTable(333    ArrayRef<const CodeGenInstruction *> Insts, raw_ostream &OS) {334 335  const std::map<StringRef, StringRef> ManualMap = {336#define ENTRY_SSE2AVX(OLD, NEW) {#OLD, #NEW},337#include "X86ManualInstrMapping.def"338  };339 340  std::vector<Entry> Table;341  for (const CodeGenInstruction *Inst : Insts) {342    const Record *Rec = Inst->TheDef;343    StringRef Name = Rec->getName();344    if (!isInteresting(Rec))345      continue;346    if (ManualMap.find(Name) != ManualMap.end()) {347      const auto *NewRec = Records.getDef(ManualMap.at(Rec->getName()));348      assert(NewRec && "Instruction not found!");349      const auto &NewInst = Target.getInstruction(NewRec);350      Table.emplace_back(Inst, &NewInst);351      continue;352    }353 354    std::string NewName = ("V" + Name).str();355    const auto *AVXRec = Records.getDef(NewName);356    if (!AVXRec)357      continue;358    auto &AVXInst = Target.getInstruction(AVXRec);359    Table.emplace_back(Inst, &AVXInst);360  }361  printTable(Table, "X86SSE2AVXTable", "GET_X86_SSE2AVX_TABLE", OS);362}363 364void X86InstrMappingEmitter::run(raw_ostream &OS) {365  emitSourceFileHeader("X86 instruction mapping", OS);366 367  ArrayRef<const CodeGenInstruction *> Insts = Target.getInstructions();368  printClassDef(OS);369  emitCompressEVEXTable(Insts, OS);370  emitNFTransformTable(Insts, OS);371  emitND2NonNDTable(Insts, OS);372  emitSSE2AVXTable(Insts, OS);373}374 375static TableGen::Emitter::OptClass<X86InstrMappingEmitter>376    X("gen-x86-instr-mapping", "Generate X86 instruction mapping");377