10b57cec5SDimitry Andric //===- utils/TableGen/X86FoldTablesEmitter.cpp - X86 backend-*- C++ -*-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This tablegen backend is responsible for emitting the memory fold tables of 100b57cec5SDimitry Andric // the X86 backend instructions. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 14*0fca6ea1SDimitry Andric #include "Common/CodeGenInstruction.h" 15*0fca6ea1SDimitry Andric #include "Common/CodeGenTarget.h" 160b57cec5SDimitry Andric #include "X86RecognizableInstr.h" 17b3edf446SDimitry Andric #include "llvm/ADT/StringSwitch.h" 1806c3fb27SDimitry Andric #include "llvm/Support/X86FoldTablesUtils.h" 1906c3fb27SDimitry Andric #include "llvm/TableGen/Record.h" 200b57cec5SDimitry Andric #include "llvm/TableGen/TableGenBackend.h" 215f757f3fSDimitry Andric #include <set> 220b57cec5SDimitry Andric 230b57cec5SDimitry Andric using namespace llvm; 2481ad6265SDimitry Andric using namespace X86Disassembler; 250b57cec5SDimitry Andric 260b57cec5SDimitry Andric namespace { 270b57cec5SDimitry Andric // Represents an entry in the manual mapped instructions set. 280b57cec5SDimitry Andric struct ManualMapEntry { 290b57cec5SDimitry Andric const char *RegInstStr; 300b57cec5SDimitry Andric const char *MemInstStr; 3106c3fb27SDimitry Andric uint16_t Strategy; 320b57cec5SDimitry Andric }; 330b57cec5SDimitry Andric 340b57cec5SDimitry Andric // List of instructions requiring explicitly aligned memory. 35*0fca6ea1SDimitry Andric const char *ExplicitAlign[] = {"MOVDQA", "MOVAPS", "MOVAPD", "MOVNTPS", 36*0fca6ea1SDimitry Andric "MOVNTPD", "MOVNTDQ", "MOVNTDQA"}; 370b57cec5SDimitry Andric 380b57cec5SDimitry Andric // List of instructions NOT requiring explicit memory alignment. 390b57cec5SDimitry Andric const char *ExplicitUnalign[] = {"MOVDQU", "MOVUPS", "MOVUPD", 405f757f3fSDimitry Andric "PCMPESTRM", "PCMPESTRI", "PCMPISTRM", 415f757f3fSDimitry Andric "PCMPISTRI"}; 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric const ManualMapEntry ManualMapSet[] = { 4406c3fb27SDimitry Andric #define ENTRY(REG, MEM, FLAGS) {#REG, #MEM, FLAGS}, 4506c3fb27SDimitry Andric #include "X86ManualFoldTables.def" 460b57cec5SDimitry Andric }; 470b57cec5SDimitry Andric 4806c3fb27SDimitry Andric const std::set<StringRef> NoFoldSet = { 4906c3fb27SDimitry Andric #define NOFOLD(INSN) #INSN, 5006c3fb27SDimitry Andric #include "X86ManualFoldTables.def" 5106c3fb27SDimitry Andric }; 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric static bool isExplicitAlign(const CodeGenInstruction *Inst) { 540b57cec5SDimitry Andric return any_of(ExplicitAlign, [Inst](const char *InstStr) { 55349cc55cSDimitry Andric return Inst->TheDef->getName().contains(InstStr); 560b57cec5SDimitry Andric }); 570b57cec5SDimitry Andric } 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric static bool isExplicitUnalign(const CodeGenInstruction *Inst) { 600b57cec5SDimitry Andric return any_of(ExplicitUnalign, [Inst](const char *InstStr) { 61349cc55cSDimitry Andric return Inst->TheDef->getName().contains(InstStr); 620b57cec5SDimitry Andric }); 630b57cec5SDimitry Andric } 640b57cec5SDimitry Andric 650b57cec5SDimitry Andric class X86FoldTablesEmitter { 660b57cec5SDimitry Andric RecordKeeper &Records; 670b57cec5SDimitry Andric CodeGenTarget Target; 680b57cec5SDimitry Andric 690b57cec5SDimitry Andric // Represents an entry in the folding table 700b57cec5SDimitry Andric class X86FoldTableEntry { 710b57cec5SDimitry Andric const CodeGenInstruction *RegInst; 720b57cec5SDimitry Andric const CodeGenInstruction *MemInst; 730b57cec5SDimitry Andric 740b57cec5SDimitry Andric public: 7506c3fb27SDimitry Andric bool NoReverse = false; 7606c3fb27SDimitry Andric bool NoForward = false; 7706c3fb27SDimitry Andric bool FoldLoad = false; 7806c3fb27SDimitry Andric bool FoldStore = false; 795f757f3fSDimitry Andric enum BcastType { 805f757f3fSDimitry Andric BCAST_NONE, 81b3edf446SDimitry Andric BCAST_W, 825f757f3fSDimitry Andric BCAST_D, 835f757f3fSDimitry Andric BCAST_Q, 845f757f3fSDimitry Andric BCAST_SS, 855f757f3fSDimitry Andric BCAST_SD, 865f757f3fSDimitry Andric BCAST_SH, 875f757f3fSDimitry Andric }; 885f757f3fSDimitry Andric BcastType BroadcastKind = BCAST_NONE; 895f757f3fSDimitry Andric 9006c3fb27SDimitry Andric Align Alignment; 910b57cec5SDimitry Andric 9206c3fb27SDimitry Andric X86FoldTableEntry() = default; 930b57cec5SDimitry Andric X86FoldTableEntry(const CodeGenInstruction *RegInst, 940b57cec5SDimitry Andric const CodeGenInstruction *MemInst) 950b57cec5SDimitry Andric : RegInst(RegInst), MemInst(MemInst) {} 960b57cec5SDimitry Andric 97*0fca6ea1SDimitry Andric void print(raw_ostream &OS) const { 980b57cec5SDimitry Andric OS.indent(2); 990b57cec5SDimitry Andric OS << "{X86::" << RegInst->TheDef->getName() << ", "; 1000b57cec5SDimitry Andric OS << "X86::" << MemInst->TheDef->getName() << ", "; 1010b57cec5SDimitry Andric 10281ad6265SDimitry Andric std::string Attrs; 10306c3fb27SDimitry Andric if (FoldLoad) 10481ad6265SDimitry Andric Attrs += "TB_FOLDED_LOAD|"; 10506c3fb27SDimitry Andric if (FoldStore) 10681ad6265SDimitry Andric Attrs += "TB_FOLDED_STORE|"; 10706c3fb27SDimitry Andric if (NoReverse) 10881ad6265SDimitry Andric Attrs += "TB_NO_REVERSE|"; 10906c3fb27SDimitry Andric if (NoForward) 11006c3fb27SDimitry Andric Attrs += "TB_NO_FORWARD|"; 11106c3fb27SDimitry Andric if (Alignment != Align(1)) 11206c3fb27SDimitry Andric Attrs += "TB_ALIGN_" + std::to_string(Alignment.value()) + "|"; 1135f757f3fSDimitry Andric switch (BroadcastKind) { 1145f757f3fSDimitry Andric case BCAST_NONE: 1155f757f3fSDimitry Andric break; 116b3edf446SDimitry Andric case BCAST_W: 117b3edf446SDimitry Andric Attrs += "TB_BCAST_W|"; 118b3edf446SDimitry Andric break; 1195f757f3fSDimitry Andric case BCAST_D: 1205f757f3fSDimitry Andric Attrs += "TB_BCAST_D|"; 1215f757f3fSDimitry Andric break; 1225f757f3fSDimitry Andric case BCAST_Q: 1235f757f3fSDimitry Andric Attrs += "TB_BCAST_Q|"; 1245f757f3fSDimitry Andric break; 1255f757f3fSDimitry Andric case BCAST_SS: 1265f757f3fSDimitry Andric Attrs += "TB_BCAST_SS|"; 1275f757f3fSDimitry Andric break; 1285f757f3fSDimitry Andric case BCAST_SD: 1295f757f3fSDimitry Andric Attrs += "TB_BCAST_SD|"; 1305f757f3fSDimitry Andric break; 1315f757f3fSDimitry Andric case BCAST_SH: 1325f757f3fSDimitry Andric Attrs += "TB_BCAST_SH|"; 1335f757f3fSDimitry Andric break; 1345f757f3fSDimitry Andric } 1350b57cec5SDimitry Andric 13681ad6265SDimitry Andric StringRef SimplifiedAttrs = StringRef(Attrs).rtrim("|"); 13781ad6265SDimitry Andric if (SimplifiedAttrs.empty()) 13881ad6265SDimitry Andric SimplifiedAttrs = "0"; 13981ad6265SDimitry Andric 14081ad6265SDimitry Andric OS << SimplifiedAttrs << "},\n"; 1410b57cec5SDimitry Andric } 142e8d8bef9SDimitry Andric 14306c3fb27SDimitry Andric #ifndef NDEBUG 14406c3fb27SDimitry Andric // Check that Uses and Defs are same after memory fold. 14506c3fb27SDimitry Andric void checkCorrectness() const { 14606c3fb27SDimitry Andric auto &RegInstRec = *RegInst->TheDef; 14706c3fb27SDimitry Andric auto &MemInstRec = *MemInst->TheDef; 14806c3fb27SDimitry Andric auto ListOfUsesReg = RegInstRec.getValueAsListOfDefs("Uses"); 14906c3fb27SDimitry Andric auto ListOfUsesMem = MemInstRec.getValueAsListOfDefs("Uses"); 15006c3fb27SDimitry Andric auto ListOfDefsReg = RegInstRec.getValueAsListOfDefs("Defs"); 15106c3fb27SDimitry Andric auto ListOfDefsMem = MemInstRec.getValueAsListOfDefs("Defs"); 15206c3fb27SDimitry Andric if (ListOfUsesReg != ListOfUsesMem || ListOfDefsReg != ListOfDefsMem) 15306c3fb27SDimitry Andric report_fatal_error("Uses/Defs couldn't be changed after folding " + 15406c3fb27SDimitry Andric RegInstRec.getName() + " to " + 15506c3fb27SDimitry Andric MemInstRec.getName()); 15606c3fb27SDimitry Andric } 15706c3fb27SDimitry Andric #endif 15806c3fb27SDimitry Andric }; 159e8d8bef9SDimitry Andric 1605f757f3fSDimitry Andric // NOTE: We check the fold tables are sorted in X86InstrFoldTables.cpp by the 1615f757f3fSDimitry Andric // enum of the instruction, which is computed in 1625f757f3fSDimitry Andric // CodeGenTarget::ComputeInstrsByEnum. So we should use the same comparator 1635f757f3fSDimitry Andric // here. 16406c3fb27SDimitry Andric // FIXME: Could we share the code with CodeGenTarget::ComputeInstrsByEnum? 16506c3fb27SDimitry Andric struct CompareInstrsByEnum { 16606c3fb27SDimitry Andric bool operator()(const CodeGenInstruction *LHS, 16706c3fb27SDimitry Andric const CodeGenInstruction *RHS) const { 16806c3fb27SDimitry Andric assert(LHS && RHS && "LHS and RHS shouldn't be nullptr"); 16906c3fb27SDimitry Andric const auto &D1 = *LHS->TheDef; 17006c3fb27SDimitry Andric const auto &D2 = *RHS->TheDef; 171*0fca6ea1SDimitry Andric return std::tuple(!D1.getValueAsBit("isPseudo"), D1.getName()) < 172*0fca6ea1SDimitry Andric std::tuple(!D2.getValueAsBit("isPseudo"), D2.getName()); 173e8d8bef9SDimitry Andric } 1740b57cec5SDimitry Andric }; 1750b57cec5SDimitry Andric 17606c3fb27SDimitry Andric typedef std::map<const CodeGenInstruction *, X86FoldTableEntry, 17706c3fb27SDimitry Andric CompareInstrsByEnum> 17806c3fb27SDimitry Andric FoldTable; 1795f757f3fSDimitry Andric // Table2Addr - Holds instructions which their memory form performs 1805f757f3fSDimitry Andric // load+store. 1815f757f3fSDimitry Andric // 1825f757f3fSDimitry Andric // Table#i - Holds instructions which the their memory form 1835f757f3fSDimitry Andric // performs a load OR a store, and their #i'th operand is folded. 1845f757f3fSDimitry Andric // 1855f757f3fSDimitry Andric // BroadcastTable#i - Holds instructions which the their memory form performs 1865f757f3fSDimitry Andric // a broadcast load and their #i'th operand is folded. 1870b57cec5SDimitry Andric FoldTable Table2Addr; 1880b57cec5SDimitry Andric FoldTable Table0; 1890b57cec5SDimitry Andric FoldTable Table1; 1900b57cec5SDimitry Andric FoldTable Table2; 1910b57cec5SDimitry Andric FoldTable Table3; 1920b57cec5SDimitry Andric FoldTable Table4; 1935f757f3fSDimitry Andric FoldTable BroadcastTable1; 1945f757f3fSDimitry Andric FoldTable BroadcastTable2; 1955f757f3fSDimitry Andric FoldTable BroadcastTable3; 1965f757f3fSDimitry Andric FoldTable BroadcastTable4; 1970b57cec5SDimitry Andric 1980b57cec5SDimitry Andric public: 1990b57cec5SDimitry Andric X86FoldTablesEmitter(RecordKeeper &R) : Records(R), Target(R) {} 2000b57cec5SDimitry Andric 2010b57cec5SDimitry Andric // run - Generate the 6 X86 memory fold tables. 20206c3fb27SDimitry Andric void run(raw_ostream &OS); 2030b57cec5SDimitry Andric 2040b57cec5SDimitry Andric private: 2050b57cec5SDimitry Andric // Decides to which table to add the entry with the given instructions. 2060b57cec5SDimitry Andric // S sets the strategy of adding the TB_NO_REVERSE flag. 2075f757f3fSDimitry Andric void updateTables(const CodeGenInstruction *RegInst, 2085f757f3fSDimitry Andric const CodeGenInstruction *MemInst, uint16_t S = 0, 2095f757f3fSDimitry Andric bool IsManual = false, bool IsBroadcast = false); 2100b57cec5SDimitry Andric 2110b57cec5SDimitry Andric // Generates X86FoldTableEntry with the given instructions and fill it with 2125f757f3fSDimitry Andric // the appropriate flags, then adds it to a memory fold table. 2135f757f3fSDimitry Andric void addEntryWithFlags(FoldTable &Table, const CodeGenInstruction *RegInst, 2145f757f3fSDimitry Andric const CodeGenInstruction *MemInst, uint16_t S, 2155f757f3fSDimitry Andric unsigned FoldedIdx, bool IsManual); 2165f757f3fSDimitry Andric // Generates X86FoldTableEntry with the given instructions and adds it to a 2175f757f3fSDimitry Andric // broadcast table. 2185f757f3fSDimitry Andric void addBroadcastEntry(FoldTable &Table, const CodeGenInstruction *RegInst, 2195f757f3fSDimitry Andric const CodeGenInstruction *MemInst); 2200b57cec5SDimitry Andric 2210b57cec5SDimitry Andric // Print the given table as a static const C++ array of type 2225f757f3fSDimitry Andric // X86FoldTableEntry. 2230b57cec5SDimitry Andric void printTable(const FoldTable &Table, StringRef TableName, 224*0fca6ea1SDimitry Andric raw_ostream &OS) { 2255f757f3fSDimitry Andric OS << "static const X86FoldTableEntry " << TableName << "[] = {\n"; 2260b57cec5SDimitry Andric 22706c3fb27SDimitry Andric for (auto &E : Table) 22806c3fb27SDimitry Andric E.second.print(OS); 2290b57cec5SDimitry Andric 2300b57cec5SDimitry Andric OS << "};\n\n"; 2310b57cec5SDimitry Andric } 2320b57cec5SDimitry Andric }; 2330b57cec5SDimitry Andric 2340b57cec5SDimitry Andric // Return true if one of the instruction's operands is a RST register class 2350b57cec5SDimitry Andric static bool hasRSTRegClass(const CodeGenInstruction *Inst) { 2360b57cec5SDimitry Andric return any_of(Inst->Operands, [](const CGIOperandList::OperandInfo &OpIn) { 2370b57cec5SDimitry Andric return OpIn.Rec->getName() == "RST" || OpIn.Rec->getName() == "RSTi"; 2380b57cec5SDimitry Andric }); 2390b57cec5SDimitry Andric } 2400b57cec5SDimitry Andric 2410b57cec5SDimitry Andric // Return true if one of the instruction's operands is a ptr_rc_tailcall 2420b57cec5SDimitry Andric static bool hasPtrTailcallRegClass(const CodeGenInstruction *Inst) { 2430b57cec5SDimitry Andric return any_of(Inst->Operands, [](const CGIOperandList::OperandInfo &OpIn) { 2440b57cec5SDimitry Andric return OpIn.Rec->getName() == "ptr_rc_tailcall"; 2450b57cec5SDimitry Andric }); 2460b57cec5SDimitry Andric } 2470b57cec5SDimitry Andric 24806c3fb27SDimitry Andric static uint8_t byteFromBitsInit(const BitsInit *B) { 24906c3fb27SDimitry Andric unsigned N = B->getNumBits(); 25006c3fb27SDimitry Andric assert(N <= 8 && "Field is too large for uint8_t!"); 2510b57cec5SDimitry Andric 25206c3fb27SDimitry Andric uint8_t Value = 0; 25306c3fb27SDimitry Andric for (unsigned I = 0; I != N; ++I) { 25406c3fb27SDimitry Andric BitInit *Bit = cast<BitInit>(B->getBit(I)); 25506c3fb27SDimitry Andric Value |= Bit->getValue() << I; 2560b57cec5SDimitry Andric } 2570b57cec5SDimitry Andric return Value; 2580b57cec5SDimitry Andric } 2590b57cec5SDimitry Andric 26006c3fb27SDimitry Andric static bool mayFoldFromForm(uint8_t Form) { 26106c3fb27SDimitry Andric switch (Form) { 26206c3fb27SDimitry Andric default: 26306c3fb27SDimitry Andric return Form >= X86Local::MRM0r && Form <= X86Local::MRM7r; 26406c3fb27SDimitry Andric case X86Local::MRMXr: 26506c3fb27SDimitry Andric case X86Local::MRMXrCC: 26606c3fb27SDimitry Andric case X86Local::MRMDestReg: 26706c3fb27SDimitry Andric case X86Local::MRMSrcReg: 26806c3fb27SDimitry Andric case X86Local::MRMSrcReg4VOp3: 26906c3fb27SDimitry Andric case X86Local::MRMSrcRegOp4: 27006c3fb27SDimitry Andric case X86Local::MRMSrcRegCC: 27106c3fb27SDimitry Andric return true; 27206c3fb27SDimitry Andric } 2730b57cec5SDimitry Andric } 2740b57cec5SDimitry Andric 27506c3fb27SDimitry Andric static bool mayFoldToForm(uint8_t Form) { 27606c3fb27SDimitry Andric switch (Form) { 27706c3fb27SDimitry Andric default: 27806c3fb27SDimitry Andric return Form >= X86Local::MRM0m && Form <= X86Local::MRM7m; 27906c3fb27SDimitry Andric case X86Local::MRMXm: 28006c3fb27SDimitry Andric case X86Local::MRMXmCC: 28106c3fb27SDimitry Andric case X86Local::MRMDestMem: 28206c3fb27SDimitry Andric case X86Local::MRMSrcMem: 28306c3fb27SDimitry Andric case X86Local::MRMSrcMem4VOp3: 28406c3fb27SDimitry Andric case X86Local::MRMSrcMemOp4: 28506c3fb27SDimitry Andric case X86Local::MRMSrcMemCC: 28606c3fb27SDimitry Andric return true; 28706c3fb27SDimitry Andric } 2880b57cec5SDimitry Andric } 2890b57cec5SDimitry Andric 29006c3fb27SDimitry Andric static bool mayFoldFromLeftToRight(uint8_t LHS, uint8_t RHS) { 29106c3fb27SDimitry Andric switch (LHS) { 29206c3fb27SDimitry Andric default: 29306c3fb27SDimitry Andric llvm_unreachable("Unexpected Form!"); 29406c3fb27SDimitry Andric case X86Local::MRM0r: 29506c3fb27SDimitry Andric return RHS == X86Local::MRM0m; 29606c3fb27SDimitry Andric case X86Local::MRM1r: 29706c3fb27SDimitry Andric return RHS == X86Local::MRM1m; 29806c3fb27SDimitry Andric case X86Local::MRM2r: 29906c3fb27SDimitry Andric return RHS == X86Local::MRM2m; 30006c3fb27SDimitry Andric case X86Local::MRM3r: 30106c3fb27SDimitry Andric return RHS == X86Local::MRM3m; 30206c3fb27SDimitry Andric case X86Local::MRM4r: 30306c3fb27SDimitry Andric return RHS == X86Local::MRM4m; 30406c3fb27SDimitry Andric case X86Local::MRM5r: 30506c3fb27SDimitry Andric return RHS == X86Local::MRM5m; 30606c3fb27SDimitry Andric case X86Local::MRM6r: 30706c3fb27SDimitry Andric return RHS == X86Local::MRM6m; 30806c3fb27SDimitry Andric case X86Local::MRM7r: 30906c3fb27SDimitry Andric return RHS == X86Local::MRM7m; 31006c3fb27SDimitry Andric case X86Local::MRMXr: 31106c3fb27SDimitry Andric return RHS == X86Local::MRMXm; 31206c3fb27SDimitry Andric case X86Local::MRMXrCC: 31306c3fb27SDimitry Andric return RHS == X86Local::MRMXmCC; 31406c3fb27SDimitry Andric case X86Local::MRMDestReg: 31506c3fb27SDimitry Andric return RHS == X86Local::MRMDestMem; 31606c3fb27SDimitry Andric case X86Local::MRMSrcReg: 31706c3fb27SDimitry Andric return RHS == X86Local::MRMSrcMem; 31806c3fb27SDimitry Andric case X86Local::MRMSrcReg4VOp3: 31906c3fb27SDimitry Andric return RHS == X86Local::MRMSrcMem4VOp3; 32006c3fb27SDimitry Andric case X86Local::MRMSrcRegOp4: 32106c3fb27SDimitry Andric return RHS == X86Local::MRMSrcMemOp4; 32206c3fb27SDimitry Andric case X86Local::MRMSrcRegCC: 32306c3fb27SDimitry Andric return RHS == X86Local::MRMSrcMemCC; 32406c3fb27SDimitry Andric } 32506c3fb27SDimitry Andric } 32606c3fb27SDimitry Andric 32706c3fb27SDimitry Andric static bool isNOREXRegClass(const Record *Op) { 328349cc55cSDimitry Andric return Op->getName().contains("_NOREX"); 3290b57cec5SDimitry Andric } 3300b57cec5SDimitry Andric 33106c3fb27SDimitry Andric // Function object - Operator() returns true if the given Reg instruction 33206c3fb27SDimitry Andric // matches the Mem instruction of this object. 3330b57cec5SDimitry Andric class IsMatch { 3340b57cec5SDimitry Andric const CodeGenInstruction *MemInst; 33506c3fb27SDimitry Andric const X86Disassembler::RecognizableInstrBase MemRI; 3365f757f3fSDimitry Andric bool IsBroadcast; 33706c3fb27SDimitry Andric const unsigned Variant; 3380b57cec5SDimitry Andric 3390b57cec5SDimitry Andric public: 3405f757f3fSDimitry Andric IsMatch(const CodeGenInstruction *Inst, bool IsBroadcast, unsigned V) 3415f757f3fSDimitry Andric : MemInst(Inst), MemRI(*MemInst), IsBroadcast(IsBroadcast), Variant(V) {} 3420b57cec5SDimitry Andric 3430b57cec5SDimitry Andric bool operator()(const CodeGenInstruction *RegInst) { 34481ad6265SDimitry Andric X86Disassembler::RecognizableInstrBase RegRI(*RegInst); 34581ad6265SDimitry Andric const Record *RegRec = RegInst->TheDef; 34681ad6265SDimitry Andric const Record *MemRec = MemInst->TheDef; 34781ad6265SDimitry Andric 34881ad6265SDimitry Andric // EVEX_B means different things for memory and register forms. 3495f757f3fSDimitry Andric // register form: rounding control or SAE 3505f757f3fSDimitry Andric // memory form: broadcast 3515f757f3fSDimitry Andric if (IsBroadcast && (RegRI.HasEVEX_B || !MemRI.HasEVEX_B)) 3525f757f3fSDimitry Andric return false; 353647cbc5dSDimitry Andric // EVEX_B indicates NDD for MAP4 instructions 354647cbc5dSDimitry Andric if (!IsBroadcast && (RegRI.HasEVEX_B || MemRI.HasEVEX_B) && 355647cbc5dSDimitry Andric RegRI.OpMap != X86Local::T_MAP4) 35681ad6265SDimitry Andric return false; 35781ad6265SDimitry Andric 35806c3fb27SDimitry Andric if (!mayFoldFromLeftToRight(RegRI.Form, MemRI.Form)) 35981ad6265SDimitry Andric return false; 36081ad6265SDimitry Andric 36181ad6265SDimitry Andric // X86 encoding is crazy, e.g 36281ad6265SDimitry Andric // 36381ad6265SDimitry Andric // f3 0f c7 30 vmxon (%rax) 36481ad6265SDimitry Andric // f3 0f c7 f0 senduipi %rax 36581ad6265SDimitry Andric // 36681ad6265SDimitry Andric // This two instruction have similiar encoding fields but are unrelated 36781ad6265SDimitry Andric if (X86Disassembler::getMnemonic(MemInst, Variant) != 36881ad6265SDimitry Andric X86Disassembler::getMnemonic(RegInst, Variant)) 36981ad6265SDimitry Andric return false; 3700b57cec5SDimitry Andric 37106c3fb27SDimitry Andric // Return false if any of the following fields of does not match. 372*0fca6ea1SDimitry Andric if (std::tuple(RegRI.Encoding, RegRI.Opcode, RegRI.OpPrefix, RegRI.OpMap, 373*0fca6ea1SDimitry Andric RegRI.OpSize, RegRI.AdSize, RegRI.HasREX_W, RegRI.HasVEX_4V, 374*0fca6ea1SDimitry Andric RegRI.HasVEX_L, RegRI.IgnoresVEX_L, RegRI.IgnoresW, 375*0fca6ea1SDimitry Andric RegRI.HasEVEX_K, RegRI.HasEVEX_KZ, RegRI.HasEVEX_L2, 376*0fca6ea1SDimitry Andric RegRI.HasEVEX_NF, RegRec->getValueAsBit("hasEVEX_RC"), 37706c3fb27SDimitry Andric RegRec->getValueAsBit("hasLockPrefix"), 3781db9f3b2SDimitry Andric RegRec->getValueAsBit("hasNoTrackPrefix")) != 379*0fca6ea1SDimitry Andric std::tuple(MemRI.Encoding, MemRI.Opcode, MemRI.OpPrefix, MemRI.OpMap, 380*0fca6ea1SDimitry Andric MemRI.OpSize, MemRI.AdSize, MemRI.HasREX_W, MemRI.HasVEX_4V, 381*0fca6ea1SDimitry Andric MemRI.HasVEX_L, MemRI.IgnoresVEX_L, MemRI.IgnoresW, 382*0fca6ea1SDimitry Andric MemRI.HasEVEX_K, MemRI.HasEVEX_KZ, MemRI.HasEVEX_L2, 383*0fca6ea1SDimitry Andric MemRI.HasEVEX_NF, MemRec->getValueAsBit("hasEVEX_RC"), 38406c3fb27SDimitry Andric MemRec->getValueAsBit("hasLockPrefix"), 3851db9f3b2SDimitry Andric MemRec->getValueAsBit("hasNoTrackPrefix"))) 3860b57cec5SDimitry Andric return false; 3870b57cec5SDimitry Andric 3880b57cec5SDimitry Andric // Make sure the sizes of the operands of both instructions suit each other. 3890b57cec5SDimitry Andric // This is needed for instructions with intrinsic version (_Int). 3900b57cec5SDimitry Andric // Where the only difference is the size of the operands. 39106c3fb27SDimitry Andric // For example: VUCOMISDZrm and VUCOMISDrm_Int 3920b57cec5SDimitry Andric // Also for instructions that their EVEX version was upgraded to work with 3930b57cec5SDimitry Andric // k-registers. For example VPCMPEQBrm (xmm output register) and 3940b57cec5SDimitry Andric // VPCMPEQBZ128rm (k register output register). 3950b57cec5SDimitry Andric unsigned MemOutSize = MemRec->getValueAsDag("OutOperandList")->getNumArgs(); 3960b57cec5SDimitry Andric unsigned RegOutSize = RegRec->getValueAsDag("OutOperandList")->getNumArgs(); 3970b57cec5SDimitry Andric unsigned MemInSize = MemRec->getValueAsDag("InOperandList")->getNumArgs(); 3980b57cec5SDimitry Andric unsigned RegInSize = RegRec->getValueAsDag("InOperandList")->getNumArgs(); 3990b57cec5SDimitry Andric 4000b57cec5SDimitry Andric // Instructions with one output in their memory form use the memory folded 4010b57cec5SDimitry Andric // operand as source and destination (Read-Modify-Write). 4020b57cec5SDimitry Andric unsigned RegStartIdx = 4030b57cec5SDimitry Andric (MemOutSize + 1 == RegOutSize) && (MemInSize == RegInSize) ? 1 : 0; 4040b57cec5SDimitry Andric 40506c3fb27SDimitry Andric bool FoundFoldedOp = false; 40606c3fb27SDimitry Andric for (unsigned I = 0, E = MemInst->Operands.size(); I != E; I++) { 40706c3fb27SDimitry Andric Record *MemOpRec = MemInst->Operands[I].Rec; 40806c3fb27SDimitry Andric Record *RegOpRec = RegInst->Operands[I + RegStartIdx].Rec; 4090b57cec5SDimitry Andric 4100b57cec5SDimitry Andric if (MemOpRec == RegOpRec) 4110b57cec5SDimitry Andric continue; 4120b57cec5SDimitry Andric 41306c3fb27SDimitry Andric if (isRegisterOperand(MemOpRec) && isRegisterOperand(RegOpRec) && 41406c3fb27SDimitry Andric ((getRegOperandSize(MemOpRec) != getRegOperandSize(RegOpRec)) || 41506c3fb27SDimitry Andric (isNOREXRegClass(MemOpRec) != isNOREXRegClass(RegOpRec)))) 4160b57cec5SDimitry Andric return false; 41706c3fb27SDimitry Andric 41806c3fb27SDimitry Andric if (isMemoryOperand(MemOpRec) && isMemoryOperand(RegOpRec) && 41906c3fb27SDimitry Andric (getMemOperandSize(MemOpRec) != getMemOperandSize(RegOpRec))) 4200b57cec5SDimitry Andric return false; 42106c3fb27SDimitry Andric 42206c3fb27SDimitry Andric if (isImmediateOperand(MemOpRec) && isImmediateOperand(RegOpRec) && 42306c3fb27SDimitry Andric (MemOpRec->getValueAsDef("Type") != RegOpRec->getValueAsDef("Type"))) 4240b57cec5SDimitry Andric return false; 42506c3fb27SDimitry Andric 4260b57cec5SDimitry Andric // Only one operand can be folded. 42706c3fb27SDimitry Andric if (FoundFoldedOp) 4280b57cec5SDimitry Andric return false; 4290b57cec5SDimitry Andric 4300b57cec5SDimitry Andric assert(isRegisterOperand(RegOpRec) && isMemoryOperand(MemOpRec)); 43106c3fb27SDimitry Andric FoundFoldedOp = true; 4320b57cec5SDimitry Andric } 4330b57cec5SDimitry Andric 43406c3fb27SDimitry Andric return FoundFoldedOp; 4350b57cec5SDimitry Andric } 4360b57cec5SDimitry Andric }; 4370b57cec5SDimitry Andric 4380b57cec5SDimitry Andric } // end anonymous namespace 4390b57cec5SDimitry Andric 4400b57cec5SDimitry Andric void X86FoldTablesEmitter::addEntryWithFlags(FoldTable &Table, 4415f757f3fSDimitry Andric const CodeGenInstruction *RegInst, 4425f757f3fSDimitry Andric const CodeGenInstruction *MemInst, 44306c3fb27SDimitry Andric uint16_t S, unsigned FoldedIdx, 4445f757f3fSDimitry Andric bool IsManual) { 4450b57cec5SDimitry Andric 4465f757f3fSDimitry Andric assert((IsManual || Table.find(RegInst) == Table.end()) && 4475f757f3fSDimitry Andric "Override entry unexpectedly"); 4485f757f3fSDimitry Andric X86FoldTableEntry Result = X86FoldTableEntry(RegInst, MemInst); 4495f757f3fSDimitry Andric Record *RegRec = RegInst->TheDef; 45006c3fb27SDimitry Andric Result.NoReverse = S & TB_NO_REVERSE; 45106c3fb27SDimitry Andric Result.NoForward = S & TB_NO_FORWARD; 45206c3fb27SDimitry Andric Result.FoldLoad = S & TB_FOLDED_LOAD; 45306c3fb27SDimitry Andric Result.FoldStore = S & TB_FOLDED_STORE; 45406c3fb27SDimitry Andric Result.Alignment = Align(1ULL << ((S & TB_ALIGN_MASK) >> TB_ALIGN_SHIFT)); 4555f757f3fSDimitry Andric if (IsManual) { 4565f757f3fSDimitry Andric Table[RegInst] = Result; 45706c3fb27SDimitry Andric return; 45806c3fb27SDimitry Andric } 45906c3fb27SDimitry Andric 4605f757f3fSDimitry Andric Record *RegOpRec = RegInst->Operands[FoldedIdx].Rec; 4615f757f3fSDimitry Andric Record *MemOpRec = MemInst->Operands[FoldedIdx].Rec; 4620b57cec5SDimitry Andric 4630b57cec5SDimitry Andric // Unfolding code generates a load/store instruction according to the size of 4640b57cec5SDimitry Andric // the register in the register form instruction. 4650b57cec5SDimitry Andric // If the register's size is greater than the memory's operand size, do not 4660b57cec5SDimitry Andric // allow unfolding. 4670b57cec5SDimitry Andric 46806c3fb27SDimitry Andric // the unfolded load size will be based on the register size. If that’s bigger 46906c3fb27SDimitry Andric // than the memory operand size, the unfolded load will load more memory and 47006c3fb27SDimitry Andric // potentially cause a memory fault. 47106c3fb27SDimitry Andric if (getRegOperandSize(RegOpRec) > getMemOperandSize(MemOpRec)) 47206c3fb27SDimitry Andric Result.NoReverse = true; 47306c3fb27SDimitry Andric 47406c3fb27SDimitry Andric // Check no-kz version's isMoveReg 47506c3fb27SDimitry Andric StringRef RegInstName = RegRec->getName(); 47606c3fb27SDimitry Andric unsigned DropLen = 4775f757f3fSDimitry Andric RegInstName.ends_with("rkz") ? 2 : (RegInstName.ends_with("rk") ? 1 : 0); 47806c3fb27SDimitry Andric Record *BaseDef = 47906c3fb27SDimitry Andric DropLen ? Records.getDef(RegInstName.drop_back(DropLen)) : nullptr; 48006c3fb27SDimitry Andric bool IsMoveReg = 4815f757f3fSDimitry Andric BaseDef ? Target.getInstruction(BaseDef).isMoveReg : RegInst->isMoveReg; 48206c3fb27SDimitry Andric // A masked load can not be unfolded to a full load, otherwise it would access 48306c3fb27SDimitry Andric // unexpected memory. A simple store can not be unfolded. 48406c3fb27SDimitry Andric if (IsMoveReg && (BaseDef || Result.FoldStore)) 48506c3fb27SDimitry Andric Result.NoReverse = true; 48606c3fb27SDimitry Andric 48706c3fb27SDimitry Andric uint8_t Enc = byteFromBitsInit(RegRec->getValueAsBitsInit("OpEncBits")); 4885f757f3fSDimitry Andric if (isExplicitAlign(RegInst)) { 4890b57cec5SDimitry Andric // The instruction require explicitly aligned memory. 4900b57cec5SDimitry Andric BitsInit *VectSize = RegRec->getValueAsBitsInit("VectSize"); 49106c3fb27SDimitry Andric Result.Alignment = Align(byteFromBitsInit(VectSize)); 4925f757f3fSDimitry Andric } else if (!Enc && !isExplicitUnalign(RegInst) && 49306c3fb27SDimitry Andric getMemOperandSize(MemOpRec) > 64) { 49406c3fb27SDimitry Andric // Instructions with XOP/VEX/EVEX encoding do not require alignment while 4950b57cec5SDimitry Andric // SSE packed vector instructions require a 16 byte alignment. 49606c3fb27SDimitry Andric Result.Alignment = Align(16); 4970b57cec5SDimitry Andric } 49806c3fb27SDimitry Andric // Expand is only ever created as a masked instruction. It is not safe to 49906c3fb27SDimitry Andric // unfold a masked expand because we don't know if it came from an expand load 50006c3fb27SDimitry Andric // intrinsic or folding a plain load. If it is from a expand load intrinsic, 50106c3fb27SDimitry Andric // Unfolding to plain load would read more elements and could trigger a fault. 50206c3fb27SDimitry Andric if (RegRec->getName().contains("EXPAND")) 50306c3fb27SDimitry Andric Result.NoReverse = true; 5040b57cec5SDimitry Andric 5055f757f3fSDimitry Andric Table[RegInst] = Result; 5060b57cec5SDimitry Andric } 5070b57cec5SDimitry Andric 5085f757f3fSDimitry Andric void X86FoldTablesEmitter::addBroadcastEntry( 5095f757f3fSDimitry Andric FoldTable &Table, const CodeGenInstruction *RegInst, 5105f757f3fSDimitry Andric const CodeGenInstruction *MemInst) { 5110b57cec5SDimitry Andric 5125f757f3fSDimitry Andric assert(Table.find(RegInst) == Table.end() && "Override entry unexpectedly"); 5135f757f3fSDimitry Andric X86FoldTableEntry Result = X86FoldTableEntry(RegInst, MemInst); 5145f757f3fSDimitry Andric 515b3edf446SDimitry Andric DagInit *In = MemInst->TheDef->getValueAsDag("InOperandList"); 516b3edf446SDimitry Andric for (unsigned I = 0, E = In->getNumArgs(); I != E; ++I) { 517b3edf446SDimitry Andric Result.BroadcastKind = 518b3edf446SDimitry Andric StringSwitch<X86FoldTableEntry::BcastType>(In->getArg(I)->getAsString()) 519b3edf446SDimitry Andric .Case("i16mem", X86FoldTableEntry::BCAST_W) 520b3edf446SDimitry Andric .Case("i32mem", X86FoldTableEntry::BCAST_D) 521b3edf446SDimitry Andric .Case("i64mem", X86FoldTableEntry::BCAST_Q) 522b3edf446SDimitry Andric .Case("f16mem", X86FoldTableEntry::BCAST_SH) 523b3edf446SDimitry Andric .Case("f32mem", X86FoldTableEntry::BCAST_SS) 524b3edf446SDimitry Andric .Case("f64mem", X86FoldTableEntry::BCAST_SD) 525b3edf446SDimitry Andric .Default(X86FoldTableEntry::BCAST_NONE); 526b3edf446SDimitry Andric if (Result.BroadcastKind != X86FoldTableEntry::BCAST_NONE) 527b3edf446SDimitry Andric break; 5285f757f3fSDimitry Andric } 529b3edf446SDimitry Andric assert(Result.BroadcastKind != X86FoldTableEntry::BCAST_NONE && 530b3edf446SDimitry Andric "Unknown memory operand for broadcast"); 5315f757f3fSDimitry Andric 5325f757f3fSDimitry Andric Table[RegInst] = Result; 5335f757f3fSDimitry Andric } 5345f757f3fSDimitry Andric 5355f757f3fSDimitry Andric void X86FoldTablesEmitter::updateTables(const CodeGenInstruction *RegInst, 5365f757f3fSDimitry Andric const CodeGenInstruction *MemInst, 5375f757f3fSDimitry Andric uint16_t S, bool IsManual, 5385f757f3fSDimitry Andric bool IsBroadcast) { 5395f757f3fSDimitry Andric 5405f757f3fSDimitry Andric Record *RegRec = RegInst->TheDef; 5415f757f3fSDimitry Andric Record *MemRec = MemInst->TheDef; 5420b57cec5SDimitry Andric unsigned MemOutSize = MemRec->getValueAsDag("OutOperandList")->getNumArgs(); 5430b57cec5SDimitry Andric unsigned RegOutSize = RegRec->getValueAsDag("OutOperandList")->getNumArgs(); 5440b57cec5SDimitry Andric unsigned MemInSize = MemRec->getValueAsDag("InOperandList")->getNumArgs(); 5450b57cec5SDimitry Andric unsigned RegInSize = RegRec->getValueAsDag("InOperandList")->getNumArgs(); 5460b57cec5SDimitry Andric 5470b57cec5SDimitry Andric // Instructions which Read-Modify-Write should be added to Table2Addr. 54806c3fb27SDimitry Andric if (!MemOutSize && RegOutSize == 1 && MemInSize == RegInSize) { 5495f757f3fSDimitry Andric assert(!IsBroadcast && "Read-Modify-Write can not be broadcast"); 5505f757f3fSDimitry Andric // X86 would not unfold Read-Modify-Write instructions so add TB_NO_REVERSE. 5515f757f3fSDimitry Andric addEntryWithFlags(Table2Addr, RegInst, MemInst, S | TB_NO_REVERSE, 0, 5525f757f3fSDimitry Andric IsManual); 5530b57cec5SDimitry Andric return; 5540b57cec5SDimitry Andric } 5550b57cec5SDimitry Andric 556*0fca6ea1SDimitry Andric // Only table0 entries should explicitly specify a load or store flag. 557*0fca6ea1SDimitry Andric // If the instruction writes to the folded operand, it will appear as 558*0fca6ea1SDimitry Andric // an output in the register form instruction and as an input in the 559*0fca6ea1SDimitry Andric // memory form instruction. If the instruction reads from the folded 560*0fca6ea1SDimitry Andric // operand, it will appear as in input in both forms. 5610b57cec5SDimitry Andric if (MemInSize == RegInSize && MemOutSize == RegOutSize) { 5620b57cec5SDimitry Andric // Load-Folding cases. 5630b57cec5SDimitry Andric // If the i'th register form operand is a register and the i'th memory form 5640b57cec5SDimitry Andric // operand is a memory operand, add instructions to Table#i. 5655f757f3fSDimitry Andric for (unsigned I = RegOutSize, E = RegInst->Operands.size(); I < E; I++) { 5665f757f3fSDimitry Andric Record *RegOpRec = RegInst->Operands[I].Rec; 5675f757f3fSDimitry Andric Record *MemOpRec = MemInst->Operands[I].Rec; 5685f757f3fSDimitry Andric // PointerLikeRegClass: For instructions like TAILJMPr, TAILJMPr64, 5695f757f3fSDimitry Andric // TAILJMPr64_REX 57081ad6265SDimitry Andric if ((isRegisterOperand(RegOpRec) || 57181ad6265SDimitry Andric RegOpRec->isSubClassOf("PointerLikeRegClass")) && 57281ad6265SDimitry Andric isMemoryOperand(MemOpRec)) { 5735f757f3fSDimitry Andric switch (I) { 5740b57cec5SDimitry Andric case 0: 5755f757f3fSDimitry Andric assert(!IsBroadcast && "BroadcastTable0 needs to be added"); 576*0fca6ea1SDimitry Andric addEntryWithFlags(Table0, RegInst, MemInst, S | TB_FOLDED_LOAD, 0, 577*0fca6ea1SDimitry Andric IsManual); 5780b57cec5SDimitry Andric return; 5790b57cec5SDimitry Andric case 1: 5805f757f3fSDimitry Andric IsBroadcast 5815f757f3fSDimitry Andric ? addBroadcastEntry(BroadcastTable1, RegInst, MemInst) 5825f757f3fSDimitry Andric : addEntryWithFlags(Table1, RegInst, MemInst, S, 1, IsManual); 5830b57cec5SDimitry Andric return; 5840b57cec5SDimitry Andric case 2: 5855f757f3fSDimitry Andric IsBroadcast 5865f757f3fSDimitry Andric ? addBroadcastEntry(BroadcastTable2, RegInst, MemInst) 5875f757f3fSDimitry Andric : addEntryWithFlags(Table2, RegInst, MemInst, S, 2, IsManual); 5880b57cec5SDimitry Andric return; 5890b57cec5SDimitry Andric case 3: 5905f757f3fSDimitry Andric IsBroadcast 5915f757f3fSDimitry Andric ? addBroadcastEntry(BroadcastTable3, RegInst, MemInst) 5925f757f3fSDimitry Andric : addEntryWithFlags(Table3, RegInst, MemInst, S, 3, IsManual); 5930b57cec5SDimitry Andric return; 5940b57cec5SDimitry Andric case 4: 5955f757f3fSDimitry Andric IsBroadcast 5965f757f3fSDimitry Andric ? addBroadcastEntry(BroadcastTable4, RegInst, MemInst) 5975f757f3fSDimitry Andric : addEntryWithFlags(Table4, RegInst, MemInst, S, 4, IsManual); 5980b57cec5SDimitry Andric return; 5990b57cec5SDimitry Andric } 6000b57cec5SDimitry Andric } 6010b57cec5SDimitry Andric } 6020b57cec5SDimitry Andric } else if (MemInSize == RegInSize + 1 && MemOutSize + 1 == RegOutSize) { 6030b57cec5SDimitry Andric // Store-Folding cases. 6040b57cec5SDimitry Andric // If the memory form instruction performs a store, the *output* 6050b57cec5SDimitry Andric // register of the register form instructions disappear and instead a 6060b57cec5SDimitry Andric // memory *input* operand appears in the memory form instruction. 6070b57cec5SDimitry Andric // For example: 6080b57cec5SDimitry Andric // MOVAPSrr => (outs VR128:$dst), (ins VR128:$src) 6090b57cec5SDimitry Andric // MOVAPSmr => (outs), (ins f128mem:$dst, VR128:$src) 6105f757f3fSDimitry Andric Record *RegOpRec = RegInst->Operands[RegOutSize - 1].Rec; 6115f757f3fSDimitry Andric Record *MemOpRec = MemInst->Operands[RegOutSize - 1].Rec; 6120b57cec5SDimitry Andric if (isRegisterOperand(RegOpRec) && isMemoryOperand(MemOpRec) && 6135f757f3fSDimitry Andric getRegOperandSize(RegOpRec) == getMemOperandSize(MemOpRec)) { 6145f757f3fSDimitry Andric assert(!IsBroadcast && "Store can not be broadcast"); 615*0fca6ea1SDimitry Andric addEntryWithFlags(Table0, RegInst, MemInst, S | TB_FOLDED_STORE, 0, 616*0fca6ea1SDimitry Andric IsManual); 6175f757f3fSDimitry Andric } 6180b57cec5SDimitry Andric } 6190b57cec5SDimitry Andric } 6200b57cec5SDimitry Andric 621*0fca6ea1SDimitry Andric void X86FoldTablesEmitter::run(raw_ostream &OS) { 6220b57cec5SDimitry Andric // Holds all memory instructions 6230b57cec5SDimitry Andric std::vector<const CodeGenInstruction *> MemInsts; 6240b57cec5SDimitry Andric // Holds all register instructions - divided according to opcode. 6250b57cec5SDimitry Andric std::map<uint8_t, std::vector<const CodeGenInstruction *>> RegInsts; 6260b57cec5SDimitry Andric 6270b57cec5SDimitry Andric ArrayRef<const CodeGenInstruction *> NumberedInstructions = 6280b57cec5SDimitry Andric Target.getInstructionsByEnumValue(); 6290b57cec5SDimitry Andric 6300b57cec5SDimitry Andric for (const CodeGenInstruction *Inst : NumberedInstructions) { 6310b57cec5SDimitry Andric const Record *Rec = Inst->TheDef; 63281ad6265SDimitry Andric if (!Rec->isSubClassOf("X86Inst") || Rec->getValueAsBit("isAsmParserOnly")) 63381ad6265SDimitry Andric continue; 6340b57cec5SDimitry Andric 63506c3fb27SDimitry Andric if (NoFoldSet.find(Rec->getName()) != NoFoldSet.end()) 63606c3fb27SDimitry Andric continue; 63706c3fb27SDimitry Andric 638647cbc5dSDimitry Andric // Promoted legacy instruction is in EVEX space, and has REX2-encoding 639647cbc5dSDimitry Andric // alternative. It's added due to HW design and never emitted by compiler. 640647cbc5dSDimitry Andric if (byteFromBitsInit(Rec->getValueAsBitsInit("OpMapBits")) == 641647cbc5dSDimitry Andric X86Local::T_MAP4 && 642647cbc5dSDimitry Andric byteFromBitsInit(Rec->getValueAsBitsInit("explicitOpPrefixBits")) == 643647cbc5dSDimitry Andric X86Local::ExplicitEVEX) 644647cbc5dSDimitry Andric continue; 645647cbc5dSDimitry Andric 6460b57cec5SDimitry Andric // - Instructions including RST register class operands are not relevant 6470b57cec5SDimitry Andric // for memory folding (for further details check the explanation in 6480b57cec5SDimitry Andric // lib/Target/X86/X86InstrFPStack.td file). 6490b57cec5SDimitry Andric // - Some instructions (listed in the manual map above) use the register 6500b57cec5SDimitry Andric // class ptr_rc_tailcall, which can be of a size 32 or 64, to ensure 6510b57cec5SDimitry Andric // safe mapping of these instruction we manually map them and exclude 6520b57cec5SDimitry Andric // them from the automation. 65306c3fb27SDimitry Andric if (hasRSTRegClass(Inst) || hasPtrTailcallRegClass(Inst)) 6540b57cec5SDimitry Andric continue; 6550b57cec5SDimitry Andric 6560b57cec5SDimitry Andric // Add all the memory form instructions to MemInsts, and all the register 65706c3fb27SDimitry Andric // form instructions to RegInsts[Opc], where Opc is the opcode of each 6580b57cec5SDimitry Andric // instructions. this helps reducing the runtime of the backend. 65906c3fb27SDimitry Andric const BitsInit *FormBits = Rec->getValueAsBitsInit("FormBits"); 66006c3fb27SDimitry Andric uint8_t Form = byteFromBitsInit(FormBits); 66106c3fb27SDimitry Andric if (mayFoldToForm(Form)) 6620b57cec5SDimitry Andric MemInsts.push_back(Inst); 66306c3fb27SDimitry Andric else if (mayFoldFromForm(Form)) { 66406c3fb27SDimitry Andric uint8_t Opc = byteFromBitsInit(Rec->getValueAsBitsInit("Opcode")); 6650b57cec5SDimitry Andric RegInsts[Opc].push_back(Inst); 6660b57cec5SDimitry Andric } 6670b57cec5SDimitry Andric } 6680b57cec5SDimitry Andric 6695f757f3fSDimitry Andric // Create a copy b/c the register instruction will removed when a new entry is 6705f757f3fSDimitry Andric // added into memory fold tables. 6715f757f3fSDimitry Andric auto RegInstsForBroadcast = RegInsts; 6725f757f3fSDimitry Andric 67381ad6265SDimitry Andric Record *AsmWriter = Target.getAsmWriter(); 67481ad6265SDimitry Andric unsigned Variant = AsmWriter->getValueAsInt("Variant"); 6755f757f3fSDimitry Andric auto FixUp = [&](const CodeGenInstruction *RegInst) { 6765f757f3fSDimitry Andric StringRef RegInstName = RegInst->TheDef->getName(); 6775f757f3fSDimitry Andric if (RegInstName.ends_with("_REV") || RegInstName.ends_with("_alt")) 6785f757f3fSDimitry Andric if (auto *RegAltRec = Records.getDef(RegInstName.drop_back(4))) 6795f757f3fSDimitry Andric RegInst = &Target.getInstruction(RegAltRec); 6805f757f3fSDimitry Andric return RegInst; 6815f757f3fSDimitry Andric }; 6820b57cec5SDimitry Andric // For each memory form instruction, try to find its register form 6830b57cec5SDimitry Andric // instruction. 6840b57cec5SDimitry Andric for (const CodeGenInstruction *MemInst : MemInsts) { 6850b57cec5SDimitry Andric uint8_t Opc = 68606c3fb27SDimitry Andric byteFromBitsInit(MemInst->TheDef->getValueAsBitsInit("Opcode")); 6870b57cec5SDimitry Andric 688480093f4SDimitry Andric auto RegInstsIt = RegInsts.find(Opc); 689480093f4SDimitry Andric if (RegInstsIt == RegInsts.end()) 6900b57cec5SDimitry Andric continue; 6910b57cec5SDimitry Andric 6920b57cec5SDimitry Andric // Two forms (memory & register) of the same instruction must have the same 6930b57cec5SDimitry Andric // opcode. 694480093f4SDimitry Andric std::vector<const CodeGenInstruction *> &OpcRegInsts = RegInstsIt->second; 6950b57cec5SDimitry Andric 6965f757f3fSDimitry Andric // Memory fold tables 6975f757f3fSDimitry Andric auto Match = 6985f757f3fSDimitry Andric find_if(OpcRegInsts, IsMatch(MemInst, /*IsBroadcast=*/false, Variant)); 6990b57cec5SDimitry Andric if (Match != OpcRegInsts.end()) { 7005f757f3fSDimitry Andric updateTables(FixUp(*Match), MemInst); 7010b57cec5SDimitry Andric OpcRegInsts.erase(Match); 7020b57cec5SDimitry Andric } 7035f757f3fSDimitry Andric 7045f757f3fSDimitry Andric // Broadcast tables 7055f757f3fSDimitry Andric StringRef MemInstName = MemInst->TheDef->getName(); 7065f757f3fSDimitry Andric if (!MemInstName.contains("mb") && !MemInstName.contains("mib")) 7075f757f3fSDimitry Andric continue; 7085f757f3fSDimitry Andric RegInstsIt = RegInstsForBroadcast.find(Opc); 7095f757f3fSDimitry Andric assert(RegInstsIt != RegInstsForBroadcast.end() && 7105f757f3fSDimitry Andric "Unexpected control flow"); 7115f757f3fSDimitry Andric std::vector<const CodeGenInstruction *> &OpcRegInstsForBroadcast = 7125f757f3fSDimitry Andric RegInstsIt->second; 7135f757f3fSDimitry Andric Match = find_if(OpcRegInstsForBroadcast, 7145f757f3fSDimitry Andric IsMatch(MemInst, /*IsBroadcast=*/true, Variant)); 7155f757f3fSDimitry Andric if (Match != OpcRegInstsForBroadcast.end()) { 7165f757f3fSDimitry Andric updateTables(FixUp(*Match), MemInst, 0, /*IsManual=*/false, 7175f757f3fSDimitry Andric /*IsBroadcast=*/true); 7185f757f3fSDimitry Andric OpcRegInstsForBroadcast.erase(Match); 7195f757f3fSDimitry Andric } 7200b57cec5SDimitry Andric } 7210b57cec5SDimitry Andric 7220b57cec5SDimitry Andric // Add the manually mapped instructions listed above. 7230b57cec5SDimitry Andric for (const ManualMapEntry &Entry : ManualMapSet) { 7240b57cec5SDimitry Andric Record *RegInstIter = Records.getDef(Entry.RegInstStr); 7250b57cec5SDimitry Andric Record *MemInstIter = Records.getDef(Entry.MemInstStr); 7260b57cec5SDimitry Andric 7270b57cec5SDimitry Andric updateTables(&(Target.getInstruction(RegInstIter)), 72806c3fb27SDimitry Andric &(Target.getInstruction(MemInstIter)), Entry.Strategy, true); 7290b57cec5SDimitry Andric } 7300b57cec5SDimitry Andric 73106c3fb27SDimitry Andric #ifndef NDEBUG 73206c3fb27SDimitry Andric auto CheckMemFoldTable = [](const FoldTable &Table) -> void { 73306c3fb27SDimitry Andric for (const auto &Record : Table) { 73406c3fb27SDimitry Andric auto &FoldEntry = Record.second; 73506c3fb27SDimitry Andric FoldEntry.checkCorrectness(); 73606c3fb27SDimitry Andric } 73706c3fb27SDimitry Andric }; 73806c3fb27SDimitry Andric CheckMemFoldTable(Table2Addr); 73906c3fb27SDimitry Andric CheckMemFoldTable(Table0); 74006c3fb27SDimitry Andric CheckMemFoldTable(Table1); 74106c3fb27SDimitry Andric CheckMemFoldTable(Table2); 74206c3fb27SDimitry Andric CheckMemFoldTable(Table3); 74306c3fb27SDimitry Andric CheckMemFoldTable(Table4); 7445f757f3fSDimitry Andric CheckMemFoldTable(BroadcastTable1); 7455f757f3fSDimitry Andric CheckMemFoldTable(BroadcastTable2); 7465f757f3fSDimitry Andric CheckMemFoldTable(BroadcastTable3); 7475f757f3fSDimitry Andric CheckMemFoldTable(BroadcastTable4); 74806c3fb27SDimitry Andric #endif 7495f757f3fSDimitry Andric #define PRINT_TABLE(TABLE) printTable(TABLE, #TABLE, OS); 7500b57cec5SDimitry Andric // Print all tables. 7515f757f3fSDimitry Andric PRINT_TABLE(Table2Addr) 7525f757f3fSDimitry Andric PRINT_TABLE(Table0) 7535f757f3fSDimitry Andric PRINT_TABLE(Table1) 7545f757f3fSDimitry Andric PRINT_TABLE(Table2) 7555f757f3fSDimitry Andric PRINT_TABLE(Table3) 7565f757f3fSDimitry Andric PRINT_TABLE(Table4) 7575f757f3fSDimitry Andric PRINT_TABLE(BroadcastTable1) 7585f757f3fSDimitry Andric PRINT_TABLE(BroadcastTable2) 7595f757f3fSDimitry Andric PRINT_TABLE(BroadcastTable3) 7605f757f3fSDimitry Andric PRINT_TABLE(BroadcastTable4) 7610b57cec5SDimitry Andric } 7620b57cec5SDimitry Andric 76306c3fb27SDimitry Andric static TableGen::Emitter::OptClass<X86FoldTablesEmitter> 76406c3fb27SDimitry Andric X("gen-x86-fold-tables", "Generate X86 fold tables"); 765